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