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