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