/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
- * Copyright 2009-2017 The OpenLDAP Foundation.
- * Copyright 2009-2017 by Howard Chu.
+ * Copyright 2009-2018 The OpenLDAP Foundation.
+ * Copyright 2009-2018 by Howard Chu.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
#include "portable.h"
-#define SLAPD_OVER_AUTOCA SLAPD_MOD_DYNAMIC
-
#ifdef SLAPD_OVER_AUTOCA
#include <stdio.h>
#include "slap.h"
#include "config.h"
+#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/evp.h>
+#include <openssl/bn.h>
+
+/* Starting with OpenSSL 1.1.0, rsa.h is no longer included in
+ * x509.h, so we need to explicitly include it for the
+ * call to EVP_PKEY_CTX_set_rsa_keygen_bits
+ */
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+#include <openssl/rsa.h>
+#define X509_get_notBefore(x) X509_getm_notBefore(x)
+#define X509_get_notAfter(x) X509_getm_notAfter(x)
+#endif
/* This overlay implements a certificate authority that can generate
* certificates automatically for any entry in the directory.
X509_NAME *subj_name, *issuer_name;
X509 *subj_cert;
struct berval derdn;
- const unsigned char *p;
+ unsigned char *pp;
EVP_PKEY *evpk = NULL;
int rc;
- unsigned char *pp;
if ((subj_cert = X509_new()) == NULL)
return -1;
autoca_dnbv2der( op, args->subjectDN, &derdn );
- p = (const unsigned char *)derdn.bv_val;
- subj_name = d2i_X509_NAME( NULL, &p, derdn.bv_len );
+ pp = (unsigned char *)derdn.bv_val;
+ subj_name = d2i_X509_NAME( NULL, (const unsigned char **)&pp, derdn.bv_len );
op->o_tmpfree( derdn.bv_val, op->o_tmpmemctx );
if ( subj_name == NULL )
{
if ( subj_name ) X509_NAME_free( subj_name );
goto fail1;
}
- args->derpkey.bv_len = i2d_PrivateKey( evpk, NULL );
- args->derpkey.bv_val = op->o_tmpalloc( args->derpkey.bv_len, op->o_tmpmemctx );
- pp = args->derpkey.bv_val;
- i2d_PrivateKey( evpk, &pp );
+ /* encode DER in PKCS#8 */
+ {
+ PKCS8_PRIV_KEY_INFO *p8inf;
+ if (( p8inf = EVP_PKEY2PKCS8( evpk )) == NULL )
+ goto fail2;
+ args->derpkey.bv_len = i2d_PKCS8_PRIV_KEY_INFO( p8inf, NULL );
+ args->derpkey.bv_val = op->o_tmpalloc( args->derpkey.bv_len, op->o_tmpmemctx );
+ pp = (unsigned char *)args->derpkey.bv_val;
+ i2d_PKCS8_PRIV_KEY_INFO( p8inf, &pp );
+ PKCS8_PRIV_KEY_INFO_free( p8inf );
+ }
args->newpkey = evpk;
/* set random serial */
goto fail3;
args->dercert.bv_len = i2d_X509( subj_cert, NULL );
args->dercert.bv_val = op->o_tmpalloc( args->dercert.bv_len, op->o_tmpmemctx );
- pp = args->dercert.bv_val;
+ pp = (unsigned char *)args->dercert.bv_val;
i2d_X509( subj_cert, &pp );
args->newcert = subj_cert;
return 0;
static const struct berval configDN = BER_BVC("cn=config");
-static int
-autoca_setca( Operation *op, struct berval *cacert )
+/* must run as a pool thread to avoid cn=config deadlock */
+static void *
+autoca_setca_task( void *ctx, void *arg )
{
- Operation op2;
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ struct berval *cacert = arg;
Modifications mod;
struct berval bvs[2];
- BackendInfo *bi;
slap_callback cb = {0};
SlapReply rs = {REP_RESULT};
const char *text;
- op2 = *op;
+ connection_fake_init( &conn, &opbuf, ctx );
+ op = &opbuf.ob_op;
+
mod.sml_numvals = 1;
mod.sml_values = bvs;
mod.sml_nvalues = NULL;
mod.sml_desc = NULL;
if ( slap_str2ad( "olcTLSCACertificate;binary", &mod.sml_desc, &text ))
- return -1;
+ goto leave;
mod.sml_op = LDAP_MOD_REPLACE;
mod.sml_flags = SLAP_MOD_INTERNAL;
bvs[0] = *cacert;
mod.sml_next = NULL;
cb.sc_response = slap_null_cb;
- op2.o_bd = select_backend( (struct berval *)&configDN, 0 );
- if ( !op2.o_bd )
- return -1;
+ op->o_bd = select_backend( (struct berval *)&configDN, 0 );
+ if ( !op->o_bd )
+ goto leave;
- op2.o_tag = LDAP_REQ_MODIFY;
- op2.o_callback = &cb;
- op2.orm_modlist = &mod;
- op2.orm_no_opattrs = 1;
- op2.o_req_dn = configDN;
- op2.o_req_ndn = configDN;
- op2.o_dn = op2.o_bd->be_rootdn;
- op2.o_ndn = op2.o_bd->be_rootndn;
- op2.o_bd->be_modify( &op2, &rs );
- return rs.sr_err;
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->o_callback = &cb;
+ op->orm_modlist = &mod;
+ op->orm_no_opattrs = 1;
+ op->o_req_dn = configDN;
+ op->o_req_ndn = configDN;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->o_bd->be_modify( op, &rs );
+leave:
+ ch_free( arg );
+ return NULL;
+}
+
+static int
+autoca_setca( struct berval *cacert )
+{
+ struct berval *bv = ch_malloc( sizeof(struct berval) + cacert->bv_len );
+ bv->bv_len = cacert->bv_len;
+ bv->bv_val = (char *)(bv+1);
+ AC_MEMCPY( bv->bv_val, cacert->bv_val, bv->bv_len );
+ return ldap_pvt_thread_pool_submit( &connection_pool, autoca_setca_task, bv );
}
static int
{
Modifications mod[2];
struct berval bvs[4];
- BackendInfo *bi;
slap_callback cb = {0};
SlapReply rs = {REP_RESULT};
const char *text;
- static const struct berval config = BER_BVC("cn=config");
mod[0].sml_numvals = 1;
mod[0].sml_values = bvs;
arg2.oc = NULL;
else
arg2.oc = oc_usrObj;
+ if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ))
+ {
+ Entry *e = entry_dup( rs->sr_entry );
+ rs_replace_entry( op, rs, on, e );
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
+ }
arg2.dercert = &args.dercert;
arg2.derpkey = &args.derpkey;
arg2.on = on;
/* If this is our cert DN, configure it */
if ( dn_match( &rs->sr_entry->e_nname, &ai->ai_localndn ))
autoca_setlocal( &op2, &args.dercert, &args.derpkey );
- if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ))
- {
- Entry *e = entry_dup( rs->sr_entry );
- rs_replace_entry( op, rs, on, e );
- rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
- }
attr_merge_one( rs->sr_entry, ad_usrCert, &args.dercert, NULL );
attr_merge_one( rs->sr_entry, ad_usrPkey, &args.derpkey, NULL );
}
)
{
/* we only act on a search that returns just our cert/key attrs */
- if ( op->ors_attrs[0].an_desc == ad_usrCert &&
+ if ( op->ors_attrs && op->ors_attrs[0].an_desc == ad_usrCert &&
op->ors_attrs[1].an_desc == ad_usrPkey &&
op->ors_attrs[2].an_name.bv_val == NULL )
{
if (slapMode & SLAP_TOOL_MODE)
return 0;
+ if ( ! *aca_attr2[0].ad ) {
+ int i, code;
+ const char *text;
+
+ for ( i=0; aca_attr2[i].at; i++ ) {
+ code = slap_str2ad( aca_attr2[i].at, aca_attr2[i].ad, &text );
+ if ( code ) return code;
+ }
+
+ /* Schema may not be loaded, ignore if missing */
+ slap_str2ad( "ipHostNumber", &ad_ipaddr, &text );
+
+ for ( i=0; aca_ocs[i].ot; i++ ) {
+ code = register_oc( aca_ocs[i].ot, aca_ocs[i].oc, 0 );
+ if ( code ) return code;
+ }
+ }
+
thrctx = ldap_pvt_thread_pool_context();
connection_fake_init2( &conn, &opbuf, thrctx, 0 );
op = &opbuf.ob_op;
a = attr_find( e->e_attrs, ad_caPkey );
if ( a ) {
const unsigned char *pp;
- pp = a->a_vals[0].bv_val;
+ pp = (unsigned char *)a->a_vals[0].bv_val;
ai->ai_pkey = d2i_AutoPrivateKey( NULL, &pp, a->a_vals[0].bv_len );
if ( ai->ai_pkey )
{
a = attr_find( e->e_attrs, ad_caCert );
if ( a )
{
- pp = a->a_vals[0].bv_val;
+ pp = (unsigned char *)a->a_vals[0].bv_val;
ai->ai_cert = d2i_X509( NULL, &pp, a->a_vals[0].bv_len );
/* If TLS wasn't configured yet, set this as our CA */
if ( !slap_tls_ctx )
- autoca_setca( op, a->a_vals );
+ autoca_setca( a->a_vals );
}
}
gotat = 1;
/* If TLS wasn't configured yet, set this as our CA */
if ( !slap_tls_ctx )
- autoca_setca( op, &args.dercert );
+ autoca_setca( &args.dercert );
op->o_tmpfree( args.dercert.bv_val, op->o_tmpmemctx );
op->o_tmpfree( args.derpkey.bv_val, op->o_tmpmemctx );
int autoca_initialize() {
int i, code;
- const char *text;
autoca.on_bi.bi_type = "autoca";
autoca.on_bi.bi_db_init = autoca_db_init;
if ( code ) return code;
}
- for ( i=0; aca_attr2[i].at; i++ ) {
- code = slap_str2ad( aca_attr2[i].at, aca_attr2[i].ad, &text );
- if ( code ) return code;
- }
-
- /* Schema may not be loaded, ignore if missing */
- slap_str2ad( "ipHostNumber", &ad_ipaddr, &text );
-
- for ( i=0; aca_ocs[i].ot; i++ ) {
- code = register_oc( aca_ocs[i].ot, aca_ocs[i].oc, 0 );
- if ( code ) return code;
- }
-
return overlay_register( &autoca );
}