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