Home | History | Annotate | Line # | Download | only in overlays
      1 /*	$NetBSD: autoca.c,v 1.3 2025/09/05 21:16:32 christos Exp $	*/
      2 
      3 /* autoca.c - Automatic Certificate Authority */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 2009-2024 The OpenLDAP Foundation.
      8  * Copyright 2009-2018 by Howard Chu.
      9  * All rights reserved.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted only as authorized by the OpenLDAP
     13  * Public License.
     14  *
     15  * A copy of this license is available in the file LICENSE in the
     16  * top-level directory of the distribution or, alternatively, at
     17  * <http://www.OpenLDAP.org/license.html>.
     18  */
     19 /* ACKNOWLEDGEMENTS:
     20  * This work was initially developed by Howard Chu for inclusion in
     21  * OpenLDAP Software.
     22  */
     23 
     24 #include <sys/cdefs.h>
     25 __RCSID("$NetBSD: autoca.c,v 1.3 2025/09/05 21:16:32 christos Exp $");
     26 
     27 #include "portable.h"
     28 
     29 #ifdef SLAPD_OVER_AUTOCA
     30 
     31 #include <stdio.h>
     32 
     33 #include <ac/string.h>
     34 #include <ac/socket.h>
     35 
     36 #include "lutil.h"
     37 #include "slap.h"
     38 #include "slap-config.h"
     39 
     40 #include <openssl/x509.h>
     41 #include <openssl/x509v3.h>
     42 #include <openssl/evp.h>
     43 #include <openssl/bn.h>
     44 
     45 /* Starting with OpenSSL 1.1.0, rsa.h is no longer included in
     46  * x509.h, so we need to explicitly include it for the
     47  * call to EVP_PKEY_CTX_set_rsa_keygen_bits
     48  */
     49 
     50 #if OPENSSL_VERSION_NUMBER >= 0x10100000
     51 #include <openssl/rsa.h>
     52 #define X509_get_notBefore(x)	X509_getm_notBefore(x)
     53 #define X509_get_notAfter(x)	X509_getm_notAfter(x)
     54 #endif
     55 
     56 #if OPENSSL_VERSION_MAJOR >= 3
     57 #define BN_pseudo_rand(bn, bits, top, bottom)	BN_rand(bn, bits, top, bottom)
     58 #endif
     59 
     60 /* This overlay implements a certificate authority that can generate
     61  * certificates automatically for any entry in the directory.
     62  * On startup it generates a self-signed CA cert for the directory's
     63  * suffix entry and uses this to sign all other certs that it generates.
     64  * User and server certs are generated on demand, using a Search request.
     65  */
     66 
     67 #define LBER_TAG_OID        ((ber_tag_t) 0x06UL)
     68 #define LBER_TAG_UTF8       ((ber_tag_t) 0x0cUL)
     69 
     70 #define KEYBITS	2048
     71 #define MIN_KEYBITS	512
     72 
     73 #define ACA_SCHEMA_ROOT	"1.3.6.1.4.1.4203.666.11.11"
     74 
     75 #define ACA_SCHEMA_AT ACA_SCHEMA_ROOT ".1"
     76 #define ACA_SCHEMA_OC ACA_SCHEMA_ROOT ".2"
     77 
     78 static AttributeDescription *ad_caCert, *ad_caPkey, *ad_usrCert, *ad_usrPkey;
     79 static AttributeDescription *ad_mail, *ad_ipaddr;
     80 static ObjectClass *oc_caObj, *oc_usrObj;
     81 
     82 static char *aca_attrs[] = {
     83 	"( " ACA_SCHEMA_AT ".1 NAME 'cAPrivateKey' "
     84 		"DESC 'X.509 CA private key, use ;binary' "
     85 		"SUP pKCS8PrivateKey )",
     86 	"( " ACA_SCHEMA_AT ".2 NAME 'userPrivateKey' "
     87 		"DESC 'X.509 user private key, use ;binary' "
     88 		"SUP pKCS8PrivateKey )",
     89 	NULL
     90 };
     91 
     92 static struct {
     93 	char *at;
     94 	AttributeDescription **ad;
     95 } aca_attr2[] = {
     96 	{ "cACertificate;binary", &ad_caCert },
     97 	{ "cAPrivateKey;binary", &ad_caPkey },
     98 	{ "userCertificate;binary", &ad_usrCert },
     99 	{ "userPrivateKey;binary", &ad_usrPkey },
    100 	{ "mail", &ad_mail },
    101 	{ NULL }
    102 };
    103 
    104 static struct {
    105 	char *ot;
    106 	ObjectClass **oc;
    107 } aca_ocs[] = {
    108 	{ "( " ACA_SCHEMA_OC ".1 NAME 'autoCA' "
    109 		"DESC 'Automated PKI certificate authority' "
    110 		"SUP pkiCA AUXILIARY "
    111 		"MAY cAPrivateKey )", &oc_caObj },
    112 	{ "( " ACA_SCHEMA_OC ".2 NAME 'autoCAuser' "
    113 		"DESC 'Automated PKI CA user' "
    114 		"SUP pkiUser AUXILIARY "
    115 		"MAY userPrivateKey )", &oc_usrObj },
    116 	{ NULL }
    117 };
    118 
    119 typedef struct autoca_info {
    120 	X509 *ai_cert;
    121 	EVP_PKEY *ai_pkey;
    122 	ObjectClass *ai_usrclass;
    123 	ObjectClass *ai_srvclass;
    124 	struct berval ai_localdn;
    125 	struct berval ai_localndn;
    126 	int ai_usrkeybits;
    127 	int ai_srvkeybits;
    128 	int ai_cakeybits;
    129 	int ai_usrdays;
    130 	int ai_srvdays;
    131 	int ai_cadays;
    132 } autoca_info;
    133 
    134 /* Rewrite an LDAP DN in DER form
    135  * Input must be valid DN, therefore no error checking is done here.
    136  */
    137 static int autoca_dnbv2der( Operation *op, struct berval *bv, struct berval *der )
    138 {
    139 	BerElementBuffer berbuf;
    140 	BerElement *ber = (BerElement *)&berbuf;
    141 	LDAPDN dn;
    142 	LDAPRDN rdn;
    143 	LDAPAVA *ava;
    144 	AttributeDescription *ad;
    145 	int irdn, iava;
    146 
    147 	ldap_bv2dn_x( bv, &dn, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx );
    148 
    149 	ber_init2( ber, NULL, LBER_USE_DER );
    150 	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
    151 
    152 	/* count RDNs, we need them in reverse order */
    153 	for (irdn = 0; dn[irdn]; irdn++);
    154 	irdn--;
    155 
    156 	/* DN is a SEQuence of RDNs */
    157 	ber_start_seq( ber, LBER_SEQUENCE );
    158 	for (; irdn >=0; irdn--)
    159 	{
    160 		/* RDN is a SET of AVAs */
    161 		ber_start_set( ber, LBER_SET );
    162 		rdn = dn[irdn];
    163 		for (iava = 0; rdn[iava]; iava++)
    164 		{
    165 			const char *text;
    166 			char oid[1024];
    167 			struct berval bvo = { sizeof(oid), oid };
    168 			struct berval bva;
    169 
    170 			/* AVA is a SEQuence of attr and value */
    171 			ber_start_seq( ber, LBER_SEQUENCE );
    172 			ava = rdn[iava];
    173 			ad = NULL;
    174 			slap_bv2ad( &ava->la_attr, &ad, &text );
    175 			ber_str2bv( ad->ad_type->sat_oid, 0, 0, &bva );
    176 			ber_encode_oid( &bva, &bvo );
    177 			ber_put_berval( ber, &bvo, LBER_TAG_OID );
    178 			ber_put_berval( ber, &ava->la_value, LBER_TAG_UTF8 );
    179 			ber_put_seq( ber );
    180 		}
    181 		ber_put_set( ber );
    182 	}
    183 	ber_put_seq( ber );
    184 	ber_flatten2( ber, der, 0 );
    185 	ldap_dnfree_x( dn, op->o_tmpmemctx );
    186 	return 0;
    187 }
    188 
    189 static int autoca_genpkey(int bits, EVP_PKEY **pkey)
    190 {
    191 	EVP_PKEY_CTX *kctx;
    192 	int rc;
    193 
    194 	kctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
    195 	if (kctx == NULL)
    196 		return -1;
    197 	if (EVP_PKEY_keygen_init(kctx) <= 0)
    198 	{
    199 		EVP_PKEY_CTX_free(kctx);
    200 		return -1;
    201 	}
    202 	if (EVP_PKEY_CTX_set_rsa_keygen_bits(kctx, bits) <= 0)
    203 	{
    204 		EVP_PKEY_CTX_free(kctx);
    205 		return -1;
    206 	}
    207 	rc = EVP_PKEY_keygen(kctx, pkey);
    208 	EVP_PKEY_CTX_free(kctx);
    209 	return rc;
    210 }
    211 
    212 static int autoca_signcert(X509 *cert, EVP_PKEY *pkey)
    213 {
    214 	EVP_MD_CTX *ctx = EVP_MD_CTX_create();
    215 	EVP_PKEY_CTX *pkctx = NULL;
    216 	int rc = -1;
    217 
    218 	if ( ctx == NULL )
    219 		return -1;
    220 	if (EVP_DigestSignInit(ctx, &pkctx, NULL, NULL, pkey))
    221 	{
    222 		rc = X509_sign_ctx(cert, ctx);
    223 	}
    224 	EVP_MD_CTX_destroy(ctx);
    225 	return rc;
    226 }
    227 
    228 #define SERIAL_BITS	64	/* should be less than 160 */
    229 
    230 typedef struct myext {
    231 	char *name;
    232 	char *value;
    233 } myext;
    234 
    235 static myext CAexts[] = {
    236 	{ "subjectKeyIdentifier", "hash" },
    237 	{ "authorityKeyIdentifier", "keyid:always,issuer" },
    238 	{ "basicConstraints", "critical,CA:true" },
    239 	{ "keyUsage", "digitalSignature,cRLSign,keyCertSign" },
    240 	{ "nsComment", "OpenLDAP automatic certificate" },
    241 	{ NULL }
    242 };
    243 
    244 static myext usrExts[] = {
    245 	{ "subjectKeyIdentifier", "hash" },
    246 	{ "authorityKeyIdentifier", "keyid:always,issuer" },
    247 	{ "basicConstraints", "CA:false" },
    248 	{ "keyUsage", "digitalSignature,nonRepudiation,keyEncipherment" },
    249 	{ "extendedKeyUsage", "clientAuth,emailProtection,codeSigning" },
    250 	{ "nsComment", "OpenLDAP automatic certificate" },
    251 	{ NULL }
    252 };
    253 
    254 static myext srvExts[] = {
    255 	{ "subjectKeyIdentifier", "hash" },
    256 	{ "authorityKeyIdentifier", "keyid:always,issuer" },
    257 	{ "basicConstraints", "CA:false" },
    258 	{ "keyUsage", "digitalSignature,keyEncipherment" },
    259 	{ "extendedKeyUsage", "serverAuth,clientAuth" },
    260 	{ "nsComment", "OpenLDAP automatic certificate" },
    261 	{ NULL }
    262 };
    263 
    264 typedef struct genargs {
    265 	X509 *issuer_cert;
    266 	EVP_PKEY *issuer_pkey;
    267 	struct berval *subjectDN;
    268 	myext *cert_exts;
    269 	myext *more_exts;
    270 	X509 *newcert;
    271 	EVP_PKEY *newpkey;
    272 	struct berval dercert;
    273 	struct berval derpkey;
    274 	int keybits;
    275 	int days;
    276 } genargs;
    277 
    278 static int autoca_gencert( Operation *op, genargs *args )
    279 {
    280 	X509_NAME *subj_name, *issuer_name;
    281 	X509 *subj_cert;
    282 	struct berval derdn;
    283 	unsigned char *pp;
    284 	EVP_PKEY *evpk = NULL;
    285 	int rc;
    286 
    287 	if ((subj_cert = X509_new()) == NULL)
    288 		return -1;
    289 
    290 	autoca_dnbv2der( op, args->subjectDN, &derdn );
    291 	pp = (unsigned char *)derdn.bv_val;
    292 	subj_name = d2i_X509_NAME( NULL, (const unsigned char **)&pp, derdn.bv_len );
    293 	op->o_tmpfree( derdn.bv_val, op->o_tmpmemctx );
    294 	if ( subj_name == NULL )
    295 	{
    296 fail1:
    297 		X509_free( subj_cert );
    298 		return -1;
    299 	}
    300 
    301 	rc = autoca_genpkey( args->keybits, &evpk );
    302 	if ( rc <= 0 )
    303 	{
    304 fail2:
    305 		if ( subj_name ) X509_NAME_free( subj_name );
    306 		goto fail1;
    307 	}
    308 	/* encode DER in PKCS#8 */
    309 	{
    310 		PKCS8_PRIV_KEY_INFO *p8inf;
    311 		if (( p8inf = EVP_PKEY2PKCS8( evpk )) == NULL )
    312 			goto fail2;
    313 		args->derpkey.bv_len = i2d_PKCS8_PRIV_KEY_INFO( p8inf, NULL );
    314 		args->derpkey.bv_val = op->o_tmpalloc( args->derpkey.bv_len, op->o_tmpmemctx );
    315 		pp = (unsigned char *)args->derpkey.bv_val;
    316 		i2d_PKCS8_PRIV_KEY_INFO( p8inf, &pp );
    317 		PKCS8_PRIV_KEY_INFO_free( p8inf );
    318 	}
    319 	args->newpkey = evpk;
    320 
    321 	/* set random serial */
    322 	{
    323 		BIGNUM *bn = BN_new();
    324 		if ( bn == NULL )
    325 		{
    326 fail3:
    327 			EVP_PKEY_free( evpk );
    328 			goto fail2;
    329 		}
    330 		if (!BN_pseudo_rand(bn, SERIAL_BITS, 0, 0))
    331 		{
    332 			BN_free( bn );
    333 			goto fail3;
    334 		}
    335 		if (!BN_to_ASN1_INTEGER(bn, X509_get_serialNumber(subj_cert)))
    336 		{
    337 			BN_free( bn );
    338 			goto fail3;
    339 		}
    340 		BN_free(bn);
    341 	}
    342 	if (args->issuer_cert) {
    343 		issuer_name = X509_get_subject_name(args->issuer_cert);
    344 	} else {
    345 		issuer_name = subj_name;
    346 		args->issuer_cert = subj_cert;
    347 		args->issuer_pkey = evpk;
    348 	}
    349 	if (!X509_set_version(subj_cert, 2) ||	/* set version to V3 */
    350 		!X509_set_issuer_name(subj_cert, issuer_name) ||
    351 		!X509_set_subject_name(subj_cert, subj_name) ||
    352 		!X509_gmtime_adj(X509_get_notBefore(subj_cert), 0) ||
    353 		!X509_time_adj_ex(X509_get_notAfter(subj_cert), args->days, 0, NULL) ||
    354 		!X509_set_pubkey(subj_cert, evpk))
    355 	{
    356 		goto fail3;
    357 	}
    358 	X509_NAME_free(subj_name);
    359 	subj_name = NULL;
    360 
    361 	/* set cert extensions */
    362 	{
    363 		X509V3_CTX ctx;
    364 		X509_EXTENSION *ext;
    365 		int i;
    366 
    367 		X509V3_set_ctx(&ctx, args->issuer_cert, subj_cert, NULL, NULL, 0);
    368 		for (i=0; args->cert_exts[i].name; i++) {
    369 			ext = X509V3_EXT_nconf(NULL, &ctx, args->cert_exts[i].name, args->cert_exts[i].value);
    370 			if ( ext == NULL )
    371 				goto fail3;
    372 			rc = X509_add_ext(subj_cert, ext, -1);
    373 			X509_EXTENSION_free(ext);
    374 			if ( !rc )
    375 				goto fail3;
    376 		}
    377 		if (args->more_exts) {
    378 			for (i=0; args->more_exts[i].name; i++) {
    379 				ext = X509V3_EXT_nconf(NULL, &ctx, args->more_exts[i].name, args->more_exts[i].value);
    380 				if ( ext == NULL )
    381 					goto fail3;
    382 				rc = X509_add_ext(subj_cert, ext, -1);
    383 				X509_EXTENSION_free(ext);
    384 				if ( !rc )
    385 					goto fail3;
    386 			}
    387 		}
    388 	}
    389 	rc = autoca_signcert( subj_cert, args->issuer_pkey );
    390 	if ( rc < 0 )
    391 		goto fail3;
    392 	args->dercert.bv_len = i2d_X509( subj_cert, NULL );
    393 	args->dercert.bv_val = op->o_tmpalloc( args->dercert.bv_len, op->o_tmpmemctx );
    394 	pp = (unsigned char *)args->dercert.bv_val;
    395 	i2d_X509( subj_cert, &pp );
    396 	args->newcert = subj_cert;
    397 	return 0;
    398 }
    399 
    400 typedef struct saveargs {
    401 	ObjectClass *oc;
    402 	struct berval *dercert;
    403 	struct berval *derpkey;
    404 	slap_overinst *on;
    405 	struct berval *dn;
    406 	struct berval *ndn;
    407 	int isca;
    408 } saveargs;
    409 
    410 static int autoca_savecert( Operation *op, saveargs *args )
    411 {
    412 	Modifications mod[3], *mp = mod;
    413 	struct berval bvs[6], *bp = bvs;
    414 	BackendInfo *bi;
    415 	slap_callback cb = {0};
    416 	SlapReply rs = {REP_RESULT};
    417 
    418 	if ( args->oc ) {
    419 		mp->sml_numvals = 1;
    420 		mp->sml_values = bp;
    421 		mp->sml_nvalues = NULL;
    422 		mp->sml_desc = slap_schema.si_ad_objectClass;
    423 		mp->sml_op = LDAP_MOD_ADD;
    424 		mp->sml_flags = SLAP_MOD_INTERNAL;
    425 		*bp++ = args->oc->soc_cname;
    426 		BER_BVZERO( bp );
    427 		bp++;
    428 		mp->sml_next = mp+1;
    429 		mp++;
    430 	}
    431 	mp->sml_numvals = 1;
    432 	mp->sml_values = bp;
    433 	mp->sml_nvalues = NULL;
    434 	mp->sml_desc = args->isca ? ad_caCert : ad_usrCert;
    435 	mp->sml_op = LDAP_MOD_REPLACE;
    436 	mp->sml_flags = SLAP_MOD_INTERNAL;
    437 	*bp++ = *args->dercert;
    438 	BER_BVZERO( bp );
    439 	bp++;
    440 	mp->sml_next = mp+1;
    441 	mp++;
    442 
    443 	mp->sml_numvals = 1;
    444 	mp->sml_values = bp;
    445 	mp->sml_nvalues = NULL;
    446 	mp->sml_desc = args->isca ? ad_caPkey : ad_usrPkey;
    447 	mp->sml_op = LDAP_MOD_ADD;
    448 	mp->sml_flags = SLAP_MOD_INTERNAL;
    449 	*bp++ = *args->derpkey;
    450 	BER_BVZERO( bp );
    451 	mp->sml_next = NULL;
    452 
    453 	cb.sc_response = slap_null_cb;
    454 	bi = op->o_bd->bd_info;
    455 	op->o_bd->bd_info = args->on->on_info->oi_orig;
    456 	op->o_tag = LDAP_REQ_MODIFY;
    457 	op->o_callback = &cb;
    458 	op->orm_modlist = mod;
    459 	op->orm_no_opattrs = 1;
    460 	op->o_req_dn = *args->dn;
    461 	op->o_req_ndn = *args->ndn;
    462 	op->o_bd->be_modify( op, &rs );
    463 	op->o_bd->bd_info = bi;
    464 	return rs.sr_err;
    465 }
    466 
    467 static const struct berval configDN = BER_BVC("cn=config");
    468 
    469 /* must run as a pool thread to avoid cn=config deadlock */
    470 static void *
    471 autoca_setca_task( void *ctx, void *arg )
    472 {
    473 	Connection conn = { 0 };
    474 	OperationBuffer opbuf;
    475 	Operation *op;
    476 	struct berval *cacert = arg;
    477 	Modifications mod;
    478 	struct berval bvs[2];
    479 	slap_callback cb = {0};
    480 	SlapReply rs = {REP_RESULT};
    481 	const char *text;
    482 
    483 	connection_fake_init( &conn, &opbuf, ctx );
    484 	op = &opbuf.ob_op;
    485 
    486 	mod.sml_numvals = 1;
    487 	mod.sml_values = bvs;
    488 	mod.sml_nvalues = NULL;
    489 	mod.sml_desc = NULL;
    490 	if ( slap_str2ad( "olcTLSCACertificate;binary", &mod.sml_desc, &text ))
    491 		goto leave;
    492 	mod.sml_op = LDAP_MOD_REPLACE;
    493 	mod.sml_flags = SLAP_MOD_INTERNAL;
    494 	bvs[0] = *cacert;
    495 	BER_BVZERO( &bvs[1] );
    496 	mod.sml_next = NULL;
    497 
    498 	cb.sc_response = slap_null_cb;
    499 	op->o_bd = select_backend( (struct berval *)&configDN, 0 );
    500 	if ( !op->o_bd )
    501 		goto leave;
    502 
    503 	op->o_tag = LDAP_REQ_MODIFY;
    504 	op->o_callback = &cb;
    505 	op->orm_modlist = &mod;
    506 	op->orm_no_opattrs = 1;
    507 	op->o_req_dn = configDN;
    508 	op->o_req_ndn = configDN;
    509 	op->o_dn = op->o_bd->be_rootdn;
    510 	op->o_ndn = op->o_bd->be_rootndn;
    511 	op->o_bd->be_modify( op, &rs );
    512 leave:
    513 	ch_free( arg );
    514 	return NULL;
    515 }
    516 
    517 static int
    518 autoca_setca( struct berval *cacert )
    519 {
    520 	struct berval *bv = ch_malloc( sizeof(struct berval) + cacert->bv_len );
    521 	bv->bv_len = cacert->bv_len;
    522 	bv->bv_val = (char *)(bv+1);
    523 	AC_MEMCPY( bv->bv_val, cacert->bv_val, bv->bv_len );
    524 	return ldap_pvt_thread_pool_submit( &connection_pool, autoca_setca_task, bv );
    525 }
    526 
    527 static int
    528 autoca_setlocal( Operation *op, struct berval *cert, struct berval *pkey )
    529 {
    530 	Modifications mod[2];
    531 	struct berval bvs[4];
    532 	slap_callback cb = {0};
    533 	SlapReply rs = {REP_RESULT};
    534 	const char *text;
    535 
    536 	mod[0].sml_numvals = 1;
    537 	mod[0].sml_values = bvs;
    538 	mod[0].sml_nvalues = NULL;
    539 	mod[0].sml_desc = NULL;
    540 	if ( slap_str2ad( "olcTLSCertificate;binary", &mod[0].sml_desc, &text ))
    541 		return -1;
    542 	mod[0].sml_op = LDAP_MOD_REPLACE;
    543 	mod[0].sml_flags = SLAP_MOD_INTERNAL;
    544 	bvs[0] = *cert;
    545 	BER_BVZERO( &bvs[1] );
    546 	mod[0].sml_next = &mod[1];
    547 
    548 	mod[1].sml_numvals = 1;
    549 	mod[1].sml_values = &bvs[2];
    550 	mod[1].sml_nvalues = NULL;
    551 	mod[1].sml_desc = NULL;
    552 	if ( slap_str2ad( "olcTLSCertificateKey;binary", &mod[1].sml_desc, &text ))
    553 		return -1;
    554 	mod[1].sml_op = LDAP_MOD_REPLACE;
    555 	mod[1].sml_flags = SLAP_MOD_INTERNAL;
    556 	bvs[2] = *pkey;
    557 	BER_BVZERO( &bvs[3] );
    558 	mod[1].sml_next = NULL;
    559 
    560 	cb.sc_response = slap_null_cb;
    561 	op->o_bd = select_backend( (struct berval *)&configDN, 0 );
    562 	if ( !op->o_bd )
    563 		return -1;
    564 
    565 	op->o_tag = LDAP_REQ_MODIFY;
    566 	op->o_callback = &cb;
    567 	op->orm_modlist = mod;
    568 	op->orm_no_opattrs = 1;
    569 	op->o_req_dn = configDN;
    570 	op->o_req_ndn = configDN;
    571 	op->o_dn = op->o_bd->be_rootdn;
    572 	op->o_ndn = op->o_bd->be_rootndn;
    573 	op->o_bd->be_modify( op, &rs );
    574 	return rs.sr_err;
    575 }
    576 
    577 enum {
    578 	ACA_USRCLASS = 1,
    579 	ACA_SRVCLASS,
    580 	ACA_USRKEYBITS,
    581 	ACA_SRVKEYBITS,
    582 	ACA_CAKEYBITS,
    583 	ACA_USRDAYS,
    584 	ACA_SRVDAYS,
    585 	ACA_CADAYS,
    586 	ACA_LOCALDN
    587 };
    588 
    589 static int autoca_cf( ConfigArgs *c )
    590 {
    591 	slap_overinst *on = (slap_overinst *)c->bi;
    592 	autoca_info *ai = on->on_bi.bi_private;
    593 	int rc = 0;
    594 
    595 	switch( c->op ) {
    596 	case SLAP_CONFIG_EMIT:
    597 		switch( c->type ) {
    598 		case ACA_USRCLASS:
    599 			if ( ai->ai_usrclass ) {
    600 				c->value_string = ch_strdup( ai->ai_usrclass->soc_cname.bv_val );
    601 			} else {
    602 				rc = 1;
    603 			}
    604 			break;
    605 		case ACA_SRVCLASS:
    606 			if ( ai->ai_srvclass ) {
    607 				c->value_string = ch_strdup( ai->ai_srvclass->soc_cname.bv_val );
    608 			} else {
    609 				rc = 1;
    610 			}
    611 			break;
    612 		case ACA_USRKEYBITS:
    613 			c->value_int = ai->ai_usrkeybits;
    614 			break;
    615 		case ACA_SRVKEYBITS:
    616 			c->value_int = ai->ai_srvkeybits;
    617 			break;
    618 		case ACA_CAKEYBITS:
    619 			c->value_int = ai->ai_cakeybits;
    620 			break;
    621 		case ACA_USRDAYS:
    622 			c->value_int = ai->ai_usrdays;
    623 			break;
    624 		case ACA_SRVDAYS:
    625 			c->value_int = ai->ai_srvdays;
    626 			break;
    627 		case ACA_CADAYS:
    628 			c->value_int = ai->ai_cadays;
    629 			break;
    630 		case ACA_LOCALDN:
    631 			if ( !BER_BVISNULL( &ai->ai_localdn )) {
    632 				rc = value_add_one( &c->rvalue_vals, &ai->ai_localdn );
    633 			} else {
    634 				rc = 1;
    635 			}
    636 			break;
    637 		}
    638 		break;
    639 	case LDAP_MOD_DELETE:
    640 		switch( c->type ) {
    641 		case ACA_USRCLASS:
    642 			ai->ai_usrclass = NULL;
    643 			break;
    644 		case ACA_SRVCLASS:
    645 			ai->ai_srvclass = NULL;
    646 			break;
    647 		case ACA_LOCALDN:
    648 			if ( ai->ai_localdn.bv_val ) {
    649 				ch_free( ai->ai_localdn.bv_val );
    650 				ch_free( ai->ai_localndn.bv_val );
    651 				BER_BVZERO( &ai->ai_localdn );
    652 				BER_BVZERO( &ai->ai_localndn );
    653 			}
    654 			break;
    655 		/* single-valued attrs, all no-ops */
    656 		}
    657 		break;
    658 	case SLAP_CONFIG_ADD:
    659 	case LDAP_MOD_ADD:
    660 		switch( c->type ) {
    661 		case ACA_USRCLASS:
    662 			{
    663 				ObjectClass *oc = oc_find( c->value_string );
    664 				if ( oc )
    665 					ai->ai_usrclass = oc;
    666 				else
    667 					rc = 1;
    668 			}
    669 			break;
    670 		case ACA_SRVCLASS:
    671 			{
    672 				ObjectClass *oc = oc_find( c->value_string );
    673 				if ( oc )
    674 					ai->ai_srvclass = oc;
    675 				else
    676 					rc = 1;
    677 			}
    678 			break;
    679 		case ACA_USRKEYBITS:
    680 			if ( c->value_int < MIN_KEYBITS )
    681 				rc = 1;
    682 			else
    683 				ai->ai_usrkeybits = c->value_int;
    684 			break;
    685 		case ACA_SRVKEYBITS:
    686 			if ( c->value_int < MIN_KEYBITS )
    687 				rc = 1;
    688 			else
    689 				ai->ai_srvkeybits = c->value_int;
    690 			break;
    691 		case ACA_CAKEYBITS:
    692 			if ( c->value_int < MIN_KEYBITS )
    693 				rc = 1;
    694 			else
    695 				ai->ai_cakeybits = c->value_int;
    696 			break;
    697 		case ACA_USRDAYS:
    698 			ai->ai_usrdays = c->value_int;
    699 			break;
    700 		case ACA_SRVDAYS:
    701 			ai->ai_srvdays = c->value_int;
    702 			break;
    703 		case ACA_CADAYS:
    704 			ai->ai_cadays = c->value_int;
    705 			break;
    706 		case ACA_LOCALDN:
    707 			if ( c->be->be_nsuffix == NULL ) {
    708 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
    709 					"suffix must be set" );
    710 				Debug( LDAP_DEBUG_CONFIG, "autoca_config: %s\n",
    711 					c->cr_msg );
    712 				rc = ARG_BAD_CONF;
    713 				break;
    714 			}
    715 			if ( !dnIsSuffix( &c->value_ndn, c->be->be_nsuffix )) {
    716 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
    717 					"DN is not a subordinate of backend" );
    718 				Debug( LDAP_DEBUG_CONFIG, "autoca_config: %s\n",
    719 					c->cr_msg );
    720 				rc = ARG_BAD_CONF;
    721 				break;
    722 			}
    723 			if ( ai->ai_localdn.bv_val ) {
    724 				ch_free( ai->ai_localdn.bv_val );
    725 				ch_free( ai->ai_localndn.bv_val );
    726 			}
    727 			ai->ai_localdn = c->value_dn;
    728 			ai->ai_localndn = c->value_ndn;
    729 		}
    730 	}
    731 	return rc;
    732 }
    733 
    734 static ConfigTable autoca_cfg[] = {
    735 	{ "userClass", "objectclass", 2, 2, 0,
    736 	  ARG_STRING|ARG_MAGIC|ACA_USRCLASS, autoca_cf,
    737 	  "( OLcfgOvAt:22.1 NAME 'olcAutoCAuserClass' "
    738 	  "DESC 'ObjectClass of user entries' "
    739 	  "EQUALITY caseIgnoreMatch "
    740 	  "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
    741 	{ "serverClass", "objectclass", 2, 2, 0,
    742 	  ARG_STRING|ARG_MAGIC|ACA_SRVCLASS, autoca_cf,
    743 	  "( OLcfgOvAt:22.2 NAME 'olcAutoCAserverClass' "
    744 	  "DESC 'ObjectClass of server entries' "
    745 	  "EQUALITY caseIgnoreMatch "
    746 	  "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
    747 	{ "userKeybits", "integer", 2, 2, 0,
    748 	  ARG_INT|ARG_MAGIC|ACA_USRKEYBITS, autoca_cf,
    749 	  "( OLcfgOvAt:22.3 NAME 'olcAutoCAuserKeybits' "
    750 	  "DESC 'Size of PrivateKey for user entries' "
    751 	  "EQUALITY integerMatch "
    752 	  "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
    753 	{ "serverKeybits", "integer", 2, 2, 0,
    754 	  ARG_INT|ARG_MAGIC|ACA_SRVKEYBITS, autoca_cf,
    755 	  "( OLcfgOvAt:22.4 NAME 'olcAutoCAserverKeybits' "
    756 	  "DESC 'Size of PrivateKey for server entries' "
    757 	  "EQUALITY integerMatch "
    758 	  "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
    759 	{ "caKeybits", "integer", 2, 2, 0,
    760 	  ARG_INT|ARG_MAGIC|ACA_CAKEYBITS, autoca_cf,
    761 	  "( OLcfgOvAt:22.5 NAME 'olcAutoCAKeybits' "
    762 	  "DESC 'Size of PrivateKey for CA certificate' "
    763 	  "EQUALITY integerMatch "
    764 	  "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
    765 	{ "userDays", "integer", 2, 2, 0,
    766 	  ARG_INT|ARG_MAGIC|ACA_USRDAYS, autoca_cf,
    767 	  "( OLcfgOvAt:22.6 NAME 'olcAutoCAuserDays' "
    768 	  "DESC 'Lifetime of user certificates in days' "
    769 	  "EQUALITY integerMatch "
    770 	  "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
    771 	{ "serverDays", "integer", 2, 2, 0,
    772 	  ARG_INT|ARG_MAGIC|ACA_SRVDAYS, autoca_cf,
    773 	  "( OLcfgOvAt:22.7 NAME 'olcAutoCAserverDays' "
    774 	  "DESC 'Lifetime of server certificates in days' "
    775 	  "EQUALITY integerMatch "
    776 	  "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
    777 	{ "caDays", "integer", 2, 2, 0,
    778 	  ARG_INT|ARG_MAGIC|ACA_CADAYS, autoca_cf,
    779 	  "( OLcfgOvAt:22.8 NAME 'olcAutoCADays' "
    780 	  "DESC 'Lifetime of CA certificate in days' "
    781 	  "EQUALITY integerMatch "
    782 	  "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
    783 	{ "localdn", "dn", 2, 2, 0,
    784 	  ARG_DN|ARG_QUOTE|ARG_MAGIC|ACA_LOCALDN, autoca_cf,
    785 	  "( OLcfgOvAt:22.9 NAME 'olcAutoCAlocalDN' "
    786 	  "DESC 'DN of local server cert' "
    787 	  "EQUALITY distinguishedNameMatch "
    788 	  "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
    789 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
    790 };
    791 
    792 static ConfigOCs autoca_ocs[] = {
    793 	{ "( OLcfgOvOc:22.1 "
    794 	  "NAME 'olcAutoCAConfig' "
    795 	  "DESC 'AutoCA configuration' "
    796 	  "SUP olcOverlayConfig "
    797 	  "MAY ( olcAutoCAuserClass $ olcAutoCAserverClass $ "
    798 	   "olcAutoCAuserKeybits $ olcAutoCAserverKeybits $ olcAutoCAKeyBits $ "
    799 	   "olcAutoCAuserDays $ olcAutoCAserverDays $ olcAutoCADays $ "
    800 	   "olcAutoCAlocalDN ) )",
    801 	  Cft_Overlay, autoca_cfg },
    802 	{ NULL, 0, NULL }
    803 };
    804 
    805 static int
    806 autoca_op_response(
    807 	Operation *op,
    808 	SlapReply *rs
    809 )
    810 {
    811 	slap_overinst *on = op->o_callback->sc_private;
    812 	autoca_info *ai = on->on_bi.bi_private;
    813 	Attribute *a;
    814 	int isusr = 0;
    815 
    816 	if (rs->sr_type != REP_SEARCH)
    817 		return SLAP_CB_CONTINUE;
    818 
    819 	/* If root or self */
    820 	if ( !be_isroot( op ) &&
    821 		!dn_match( &rs->sr_entry->e_nname, &op->o_ndn ))
    822 		return SLAP_CB_CONTINUE;
    823 
    824 	isusr = is_entry_objectclass( rs->sr_entry, ai->ai_usrclass, SLAP_OCF_CHECK_SUP );
    825 	if ( !isusr )
    826 	{
    827 		if (!is_entry_objectclass( rs->sr_entry, ai->ai_srvclass, SLAP_OCF_CHECK_SUP ))
    828 			return SLAP_CB_CONTINUE;
    829 	}
    830 	a = attr_find( rs->sr_entry->e_attrs, ad_usrPkey );
    831 	if ( !a )
    832 	{
    833 		Operation op2;
    834 		genargs args;
    835 		saveargs arg2;
    836 		myext extras[2];
    837 		int rc;
    838 
    839 		args.issuer_cert = ai->ai_cert;
    840 		args.issuer_pkey = ai->ai_pkey;
    841 		args.subjectDN = &rs->sr_entry->e_name;
    842 		args.more_exts = NULL;
    843 		if ( isusr )
    844 		{
    845 			args.cert_exts = usrExts;
    846 			args.keybits = ai->ai_usrkeybits;
    847 			args.days = ai->ai_usrdays;
    848 			a = attr_find( rs->sr_entry->e_attrs, ad_mail );
    849 			if ( a )
    850 			{
    851 				extras[0].name = "subjectAltName";
    852 				extras[1].name = NULL;
    853 				extras[0].value = op->o_tmpalloc( sizeof("email:") + a->a_vals[0].bv_len, op->o_tmpmemctx );
    854 				sprintf(extras[0].value, "email:%s", a->a_vals[0].bv_val);
    855 				args.more_exts = extras;
    856 			}
    857 		} else
    858 		{
    859 			args.cert_exts = srvExts;
    860 			args.keybits = ai->ai_srvkeybits;
    861 			args.days = ai->ai_srvdays;
    862 			if ( ad_ipaddr && (a = attr_find( rs->sr_entry->e_attrs, ad_ipaddr )))
    863 			{
    864 				extras[0].name = "subjectAltName";
    865 				extras[1].name = NULL;
    866 				extras[0].value = op->o_tmpalloc( sizeof("IP:") + a->a_vals[0].bv_len, op->o_tmpmemctx );
    867 				sprintf(extras[0].value, "IP:%s", a->a_vals[0].bv_val);
    868 				args.more_exts = extras;
    869 			}
    870 		}
    871 		rc = autoca_gencert( op, &args );
    872 		if ( rc )
    873 			return SLAP_CB_CONTINUE;
    874 		X509_free( args.newcert );
    875 		EVP_PKEY_free( args.newpkey );
    876 
    877 		if ( is_entry_objectclass( rs->sr_entry, oc_usrObj, 0 ))
    878 			arg2.oc = NULL;
    879 		else
    880 			arg2.oc = oc_usrObj;
    881 		if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ))
    882 		{
    883 			Entry *e = entry_dup( rs->sr_entry );
    884 			rs_replace_entry( op, rs, on, e );
    885 			rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
    886 		}
    887 		arg2.dercert = &args.dercert;
    888 		arg2.derpkey = &args.derpkey;
    889 		arg2.on = on;
    890 		arg2.dn = &rs->sr_entry->e_name;
    891 		arg2.ndn = &rs->sr_entry->e_nname;
    892 		arg2.isca = 0;
    893 		op2 = *op;
    894 		rc = autoca_savecert( &op2, &arg2 );
    895 		if ( !rc )
    896 		{
    897 			/* If this is our cert DN, configure it */
    898 			if ( dn_match( &rs->sr_entry->e_nname, &ai->ai_localndn ))
    899 				autoca_setlocal( &op2, &args.dercert, &args.derpkey );
    900 			attr_merge_one( rs->sr_entry, ad_usrCert, &args.dercert, NULL );
    901 			attr_merge_one( rs->sr_entry, ad_usrPkey, &args.derpkey, NULL );
    902 		}
    903 		op->o_tmpfree( args.dercert.bv_val, op->o_tmpmemctx );
    904 		op->o_tmpfree( args.derpkey.bv_val, op->o_tmpmemctx );
    905 	}
    906 
    907 	return SLAP_CB_CONTINUE;
    908 }
    909 
    910 static int
    911 autoca_op_search(
    912 	Operation *op,
    913 	SlapReply *rs
    914 )
    915 {
    916 	/* we only act on a search that returns just our cert/key attrs */
    917 	if ( op->ors_attrs && op->ors_attrs[0].an_desc == ad_usrCert &&
    918 		op->ors_attrs[1].an_desc == ad_usrPkey &&
    919 		op->ors_attrs[2].an_name.bv_val == NULL )
    920 	{
    921 		slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
    922 		slap_callback *sc = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
    923 		sc->sc_response = autoca_op_response;
    924 		sc->sc_private = on;
    925 		sc->sc_next = op->o_callback;
    926 		op->o_callback = sc;
    927 	}
    928 	return SLAP_CB_CONTINUE;
    929 }
    930 
    931 static int
    932 autoca_db_init(
    933 	BackendDB *be,
    934 	ConfigReply *cr
    935 )
    936 {
    937 	slap_overinst *on = (slap_overinst *) be->bd_info;
    938 	autoca_info *ai;
    939 
    940 	ai = ch_calloc(1, sizeof(autoca_info));
    941 	on->on_bi.bi_private = ai;
    942 
    943 	/* set defaults */
    944 	ai->ai_usrclass = oc_find( "person" );
    945 	ai->ai_srvclass = oc_find( "ipHost" );
    946 	ai->ai_usrkeybits = KEYBITS;
    947 	ai->ai_srvkeybits = KEYBITS;
    948 	ai->ai_cakeybits = KEYBITS;
    949 	ai->ai_usrdays = 365;	/* 1 year */
    950 	ai->ai_srvdays = 1826;	/* 5 years */
    951 	ai->ai_cadays = 3652;	/* 10 years */
    952 	return 0;
    953 }
    954 
    955 static int
    956 autoca_db_destroy(
    957 	BackendDB *be,
    958 	ConfigReply *cr
    959 )
    960 {
    961 	slap_overinst *on = (slap_overinst *) be->bd_info;
    962 	autoca_info *ai = on->on_bi.bi_private;
    963 
    964 	if ( ai->ai_cert )
    965 		X509_free( ai->ai_cert );
    966 	if ( ai->ai_pkey )
    967 		EVP_PKEY_free( ai->ai_pkey );
    968 	ch_free( ai );
    969 
    970 	return 0;
    971 }
    972 
    973 static int
    974 autoca_db_open(
    975 	BackendDB *be,
    976 	ConfigReply *cr
    977 )
    978 {
    979 	slap_overinst *on = (slap_overinst *)be->bd_info;
    980 	autoca_info *ai = on->on_bi.bi_private;
    981 
    982 	Connection conn = { 0 };
    983 	OperationBuffer opbuf;
    984 	Operation *op;
    985 	void *thrctx;
    986 	Entry *e = NULL;
    987 	Attribute *a;
    988 	int rc;
    989 
    990 	if (slapMode & SLAP_TOOL_MODE)
    991 		return 0;
    992 
    993 	if ( ! *aca_attr2[0].ad ) {
    994 		int i, code;
    995 		const char *text;
    996 
    997 		for ( i=0; aca_attr2[i].at; i++ ) {
    998 			code = slap_str2ad( aca_attr2[i].at, aca_attr2[i].ad, &text );
    999 			if ( code ) return code;
   1000 		}
   1001 
   1002 		/* Schema may not be loaded, ignore if missing */
   1003 		slap_str2ad( "ipHostNumber", &ad_ipaddr, &text );
   1004 
   1005 		for ( i=0; aca_ocs[i].ot; i++ ) {
   1006 			code = register_oc( aca_ocs[i].ot, aca_ocs[i].oc, 0 );
   1007 			if ( code ) return code;
   1008 		}
   1009 	}
   1010 
   1011 	thrctx = ldap_pvt_thread_pool_context();
   1012 	connection_fake_init2( &conn, &opbuf, thrctx, 0 );
   1013 	op = &opbuf.ob_op;
   1014 	op->o_bd = be;
   1015 	op->o_dn = be->be_rootdn;
   1016 	op->o_ndn = be->be_rootndn;
   1017 	rc = overlay_entry_get_ov( op, be->be_nsuffix, NULL,
   1018 		NULL, 0, &e, on );
   1019 
   1020 	if ( e ) {
   1021 		int gotoc = 0, gotat = 0;
   1022 		if ( is_entry_objectclass( e, oc_caObj, 0 )) {
   1023 			gotoc = 1;
   1024 			a = attr_find( e->e_attrs, ad_caPkey );
   1025 			if ( a ) {
   1026 				const unsigned char *pp;
   1027 				pp = (unsigned char *)a->a_vals[0].bv_val;
   1028 				ai->ai_pkey = d2i_AutoPrivateKey( NULL, &pp, a->a_vals[0].bv_len );
   1029 				if ( ai->ai_pkey )
   1030 				{
   1031 					a = attr_find( e->e_attrs, ad_caCert );
   1032 					if ( a )
   1033 					{
   1034 						pp = (unsigned char *)a->a_vals[0].bv_val;
   1035 						ai->ai_cert = d2i_X509( NULL, &pp, a->a_vals[0].bv_len );
   1036 						/* If TLS wasn't configured yet, set this as our CA */
   1037 						if ( !slap_tls_ctx )
   1038 							autoca_setca( a->a_vals );
   1039 					}
   1040 				}
   1041 				gotat = 1;
   1042 			}
   1043 		}
   1044 		overlay_entry_release_ov( op, e, 0, on );
   1045 		/* generate attrs, store... */
   1046 		if ( !gotat ) {
   1047 			genargs args;
   1048 			saveargs arg2;
   1049 
   1050 			args.issuer_cert = NULL;
   1051 			args.issuer_pkey = NULL;
   1052 			args.subjectDN = &be->be_suffix[0];
   1053 			args.cert_exts = CAexts;
   1054 			args.more_exts = NULL;
   1055 			args.keybits = ai->ai_cakeybits;
   1056 			args.days = ai->ai_cadays;
   1057 
   1058 			rc = autoca_gencert( op, &args );
   1059 			if ( rc )
   1060 				return -1;
   1061 
   1062 			ai->ai_cert = args.newcert;
   1063 			ai->ai_pkey = args.newpkey;
   1064 
   1065 			arg2.dn = be->be_suffix;
   1066 			arg2.ndn = be->be_nsuffix;
   1067 			arg2.isca = 1;
   1068 			if ( !gotoc )
   1069 				arg2.oc = oc_caObj;
   1070 			else
   1071 				arg2.oc = NULL;
   1072 			arg2.on = on;
   1073 			arg2.dercert = &args.dercert;
   1074 			arg2.derpkey = &args.derpkey;
   1075 
   1076 			autoca_savecert( op, &arg2 );
   1077 
   1078 			/* If TLS wasn't configured yet, set this as our CA */
   1079 			if ( !slap_tls_ctx )
   1080 				autoca_setca( &args.dercert );
   1081 
   1082 			op->o_tmpfree( args.dercert.bv_val, op->o_tmpmemctx );
   1083 			op->o_tmpfree( args.derpkey.bv_val, op->o_tmpmemctx );
   1084 		}
   1085 	}
   1086 
   1087 	return 0;
   1088 }
   1089 
   1090 static slap_overinst autoca;
   1091 
   1092 /* This overlay is set up for dynamic loading via moduleload. For static
   1093  * configuration, you'll need to arrange for the slap_overinst to be
   1094  * initialized and registered by some other function inside slapd.
   1095  */
   1096 
   1097 int autoca_initialize() {
   1098 	int i, code;
   1099 
   1100 	autoca.on_bi.bi_type = "autoca";
   1101 	autoca.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
   1102 	autoca.on_bi.bi_db_init = autoca_db_init;
   1103 	autoca.on_bi.bi_db_destroy = autoca_db_destroy;
   1104 	autoca.on_bi.bi_db_open = autoca_db_open;
   1105 	autoca.on_bi.bi_op_search = autoca_op_search;
   1106 
   1107 	autoca.on_bi.bi_cf_ocs = autoca_ocs;
   1108 	code = config_register_schema( autoca_cfg, autoca_ocs );
   1109 	if ( code ) return code;
   1110 
   1111 	for ( i=0; aca_attrs[i]; i++ ) {
   1112 		code = register_at( aca_attrs[i], NULL, 0 );
   1113 		if ( code ) return code;
   1114 	}
   1115 
   1116 	return overlay_register( &autoca );
   1117 }
   1118 
   1119 #if SLAPD_OVER_AUTOCA == SLAPD_MOD_DYNAMIC
   1120 int
   1121 init_module( int argc, char *argv[] )
   1122 {
   1123 	return autoca_initialize();
   1124 }
   1125 #endif
   1126 
   1127 #endif /* defined(SLAPD_OVER_AUTOCA) */
   1128