+ fgets( (char*)phrase, sizeof(phrase), stdin );
+ if ( isTTY ) {
+ fprintf( stdout, "\n" );
+ echoOn( infd );
+ }
+ /* stomp on newline */
+ phrase[strlen((char*)phrase)-1] = 0;
+
+ pwdstr = PL_strdup( (char*)phrase );
+ }
+
+#endif /* READ_PASSWORD_FROM_STDIN */
+ return pwdstr;
+}
+
+/*
+ * PKCS11 devices (including the internal softokn cert/key database)
+ * may be protected by a pin or password or even pass phrase
+ * MozNSS needs a way for the user to provide that
+ */
+static char *
+tlsm_pin_prompt(PK11SlotInfo *slot, PRBool retry, void *arg)
+{
+ tlsm_ctx *ctx = (tlsm_ctx *)arg;
+
+ return tlsm_get_pin( slot, retry, ctx );
+}
+
+static SECStatus
+tlsm_get_basic_constraint_extension( CERTCertificate *cert,
+ CERTBasicConstraints *cbcval )
+{
+ SECItem encodedVal = { 0, NULL };
+ SECStatus rc;
+
+ rc = CERT_FindCertExtension( cert, SEC_OID_X509_BASIC_CONSTRAINTS,
+ &encodedVal);
+ if ( rc != SECSuccess ) {
+ return rc;
+ }
+
+ rc = CERT_DecodeBasicConstraintValue( cbcval, &encodedVal );
+
+ /* free the raw extension data */
+ PORT_Free( encodedVal.data );
+
+ return rc;
+}
+
+static PRBool
+tlsm_cert_is_self_issued( CERTCertificate *cert )
+{
+ /* A cert is self-issued if its subject and issuer are equal and
+ * both are of non-zero length.
+ */
+ PRBool is_self_issued = cert &&
+ (PRBool)SECITEM_ItemsAreEqual( &cert->derIssuer,
+ &cert->derSubject ) &&
+ cert->derSubject.len > 0;
+ return is_self_issued;
+}
+
+static SECStatus
+tlsm_verify_cert(CERTCertDBHandle *handle, CERTCertificate *cert, void *pinarg,
+ PRBool checksig, SECCertificateUsage certUsage, int errorToIgnore )
+{
+ CERTVerifyLog verifylog;
+ SECStatus ret = SECSuccess;
+ const char *name;
+ int debug_level = LDAP_DEBUG_ANY;
+
+ if ( errorToIgnore == -1 ) {
+ debug_level = LDAP_DEBUG_TRACE;
+ }
+
+ /* the log captures information about every cert in the chain, so we can tell
+ which cert caused the problem and what the problem was */
+ memset( &verifylog, 0, sizeof( verifylog ) );
+ verifylog.arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
+ if ( verifylog.arena == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS certificate verification: Out of memory for certificate verification logger\n",
+ 0, 0, 0 );
+ return SECFailure;
+ }
+ ret = CERT_VerifyCertificate( handle, cert, checksig, certUsage, PR_Now(), pinarg, &verifylog,
+ NULL );
+ if ( ( name = cert->subjectName ) == NULL ) {
+ name = cert->nickname;
+ }
+ if ( verifylog.head == NULL ) {
+ /* it is possible for CERT_VerifyCertificate return with an error with no logging */
+ if ( ret != SECSuccess ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( debug_level,
+ "TLS: certificate [%s] is not valid - error %d:%s.\n",
+ name ? name : "(unknown)",
+ errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ }
+ } else {
+ const char *name;
+ CERTVerifyLogNode *node;
+
+ ret = SECSuccess; /* reset */
+ node = verifylog.head;
+ while ( node ) {
+ if ( ( name = node->cert->subjectName ) == NULL ) {
+ name = node->cert->nickname;
+ }
+ if ( node->error ) {
+ /* NSS does not like CA certs that have the basic constraints extension
+ with the CA flag set to FALSE - openssl doesn't check if the cert
+ is self issued */
+ if ( ( node->error == SEC_ERROR_CA_CERT_INVALID ) &&
+ tlsm_cert_is_self_issued( node->cert ) ) {
+ CERTBasicConstraints basicConstraint;
+ SECStatus rv = tlsm_get_basic_constraint_extension( node->cert, &basicConstraint );
+ if ( ( rv == SECSuccess ) && ( basicConstraint.isCA == PR_FALSE ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: certificate [%s] is not correct because it is a CA cert and the "
+ "BasicConstraint CA flag is set to FALSE - allowing for now, but "
+ "please fix your certs if possible\n", name, 0, 0 );
+ } else { /* does not have basicconstraint, or some other error */
+ ret = SECFailure;
+ Debug( debug_level,
+ "TLS: certificate [%s] is not valid - CA cert is not valid\n",
+ name, 0, 0 );
+ }
+ } else if ( errorToIgnore && ( node->error == errorToIgnore ) ) {
+ Debug( debug_level,
+ "TLS: Warning: ignoring error for certificate [%s] - error %ld:%s.\n",
+ name, node->error, PR_ErrorToString( node->error, PR_LANGUAGE_I_DEFAULT ) );
+ } else {
+ ret = SECFailure;
+ Debug( debug_level,
+ "TLS: certificate [%s] is not valid - error %ld:%s.\n",
+ name, node->error, PR_ErrorToString( node->error, PR_LANGUAGE_I_DEFAULT ) );
+ }
+ }
+ CERT_DestroyCertificate( node->cert );
+ node = node->next;
+ }
+ }
+
+ PORT_FreeArena( verifylog.arena, PR_FALSE );
+
+ if ( ret == SECSuccess ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: certificate [%s] is valid\n", name, 0, 0 );
+ } else if ( errorToIgnore == -1 ) {
+ ret = SECSuccess;
+ }
+
+ return ret;
+}
+
+static SECStatus
+tlsm_auth_cert_handler(void *arg, PRFileDesc *fd,
+ PRBool checksig, PRBool isServer)
+{
+ SECCertificateUsage certUsage = isServer ? certificateUsageSSLClient : certificateUsageSSLServer;
+ SECStatus ret = SECSuccess;
+ CERTCertificate *peercert = SSL_PeerCertificate( fd );
+ int errorToIgnore = 0;
+ tlsm_ctx *ctx = (tlsm_ctx *)arg;
+
+ if (ctx && ctx->tc_warn_only )
+ errorToIgnore = -1;
+
+ ret = tlsm_verify_cert( ctx->tc_certdb, peercert,
+ SSL_RevealPinArg( fd ),
+ checksig, certUsage, errorToIgnore );
+ CERT_DestroyCertificate( peercert );
+
+ return ret;
+}
+
+static int
+tlsm_authenticate_to_slot( tlsm_ctx *ctx, PK11SlotInfo *slot )
+{
+ int rc = -1;
+
+ if ( SECSuccess != PK11_Authenticate( slot, PR_FALSE, ctx ) ) {
+ char *token_name = PK11_GetTokenName( slot );
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not authenticate to the security token %s - error %d:%s.\n",
+ token_name ? token_name : DEFAULT_TOKEN_NAME, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ } else {
+ rc = 0; /* success */
+ }
+
+ return rc;
+}
+
+static SECStatus
+tlsm_nss_shutdown_cb( void *appData, void *nssData )
+{
+ SECStatus rc = SECSuccess;
+
+ SSL_ShutdownServerSessionIDCache();
+
+ if ( pem_module ) {
+ SECMOD_UnloadUserModule( pem_module );
+ SECMOD_DestroyModule( pem_module );
+ pem_module = NULL;
+ }
+ return rc;
+}
+
+static PRCallOnceType tlsm_register_shutdown_callonce = {0,0};
+static PRStatus PR_CALLBACK
+tlsm_register_nss_shutdown_cb( void )
+{
+ if ( SECSuccess == NSS_RegisterShutdown( tlsm_nss_shutdown_cb,
+ NULL ) ) {
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+}
+
+static PRStatus
+tlsm_register_nss_shutdown( void )
+{
+ return PR_CallOnce( &tlsm_register_shutdown_callonce,
+ tlsm_register_nss_shutdown_cb );
+}
+
+static int
+tlsm_init_pem_module( void )
+{
+ int rc = 0;
+ char *fullname = NULL;
+ char *configstring = NULL;
+
+ if ( pem_module ) {
+ return rc;
+ }
+
+ /* not loaded - load it */
+ /* get the system dependent library name */
+ fullname = PR_GetLibraryName( NULL, PEM_LIBRARY );
+ /* Load our PKCS#11 module */
+ configstring = PR_smprintf( "library=%s name=" PEM_MODULE " parameters=\"\"", fullname );
+ PL_strfree( fullname );
+
+ pem_module = SECMOD_LoadUserModule( configstring, NULL, PR_FALSE );
+ PR_smprintf_free( configstring );
+
+ if ( !pem_module || !pem_module->loaded ) {
+ if ( pem_module ) {
+ SECMOD_DestroyModule( pem_module );
+ pem_module = NULL;
+ }
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static void
+tlsm_add_pem_obj( tlsm_ctx *ctx, PK11GenericObject *obj )
+{
+ int idx = ctx->tc_n_pem_objs;
+ ctx->tc_n_pem_objs++;
+ ctx->tc_pem_objs = (PK11GenericObject **)
+ PORT_Realloc( ctx->tc_pem_objs, ctx->tc_n_pem_objs * sizeof( PK11GenericObject * ) );
+ ctx->tc_pem_objs[idx] = obj;
+}
+
+static void
+tlsm_free_pem_objs( tlsm_ctx *ctx )
+{
+ /* free in reverse order of allocation */
+ while ( ctx->tc_n_pem_objs-- ) {
+ PK11_DestroyGenericObject( ctx->tc_pem_objs[ctx->tc_n_pem_objs] );
+ ctx->tc_pem_objs[ctx->tc_n_pem_objs] = NULL;
+ }
+ PORT_Free(ctx->tc_pem_objs);
+ ctx->tc_pem_objs = NULL;
+ ctx->tc_n_pem_objs = 0;
+}
+
+static int
+tlsm_add_cert_from_file( tlsm_ctx *ctx, const char *filename, PRBool isca, PRBool istrusted )
+{
+ CK_SLOT_ID slotID;
+ PK11SlotInfo *slot = NULL;
+ PK11GenericObject *rv;
+ CK_ATTRIBUTE *attrs;
+ CK_ATTRIBUTE theTemplate[20];
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_OBJECT_CLASS objClass = CKO_CERTIFICATE;
+ char tmpslotname[64];
+ char *slotname = NULL;
+ const char *ptr = NULL;
+ char sep = PR_GetDirectorySeparator();
+ PRFileInfo fi;
+ PRStatus status;
+
+ memset( &fi, 0, sizeof(fi) );
+ status = PR_GetFileInfo( filename, &fi );
+ if ( PR_SUCCESS != status) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not read certificate file %s - error %d:%s.\n",
+ filename, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ return -1;
+ }
+
+ if ( fi.type != PR_FILE_FILE ) {
+ PR_SetError(PR_IS_DIRECTORY_ERROR, 0);
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: the certificate file %s is not a file.\n",
+ filename, 0 ,0 );
+ return -1;
+ }
+
+ attrs = theTemplate;
+
+ if ( isca ) {
+ slotID = 0; /* CA and trust objects use slot 0 */
+ PR_snprintf( tmpslotname, sizeof(tmpslotname), TLSM_PEM_TOKEN_FMT, slotID );
+ slotname = tmpslotname;
+ istrusted = PR_TRUE;
+ } else {
+ if ( ctx->tc_slotname == NULL ) { /* need new slot */
+ if ( istrusted ) {
+ slotID = 0;
+ } else {
+ slotID = ++tlsm_slot_count;
+ }
+ ctx->tc_slotname = PR_smprintf( TLSM_PEM_TOKEN_FMT, slotID );
+ }
+ slotname = ctx->tc_slotname;
+
+ if ( ( ptr = PL_strrchr( filename, sep ) ) ) {
+ PL_strfree( ctx->tc_certname );
+ ++ptr;
+ if ( istrusted ) {
+ /* pemnss conflates trusted certs with CA certs - since there can
+ be more than one CA cert in a file (e.g. ca-bundle.crt) pemnss
+ numbers each trusted cert - in the case of a server cert, there will be
+ only one, so it will be number 0 */
+ ctx->tc_certname = PR_smprintf( "%s:%s - 0", slotname, ptr );
+ } else {
+ ctx->tc_certname = PR_smprintf( "%s:%s", slotname, ptr );
+ }
+ }
+ }
+
+ slot = PK11_FindSlotByName( slotname );
+
+ if ( !slot ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not find the slot for certificate %s - error %d:%s.\n",
+ ctx->tc_certname, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ return -1;
+ }
+
+ PK11_SETATTRS( attrs, CKA_CLASS, &objClass, sizeof(objClass) ); attrs++;
+ PK11_SETATTRS( attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) ); attrs++;
+ PK11_SETATTRS( attrs, CKA_LABEL, (unsigned char *)filename, strlen(filename)+1 ); attrs++;
+ if ( istrusted ) {
+ PK11_SETATTRS( attrs, CKA_TRUST, &cktrue, sizeof(CK_BBOOL) ); attrs++;
+ } else {
+ PK11_SETATTRS( attrs, CKA_TRUST, &ckfalse, sizeof(CK_BBOOL) ); attrs++;
+ }
+ /* This loads the certificate in our PEM module into the appropriate
+ * slot.
+ */
+ rv = PK11_CreateGenericObject( slot, theTemplate, 4, PR_FALSE /* isPerm */ );
+
+ PK11_FreeSlot( slot );
+
+ if ( !rv ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not add the certificate %s - error %d:%s.\n",
+ ctx->tc_certname, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ return -1;
+ }
+
+ tlsm_add_pem_obj( ctx, rv );
+
+ return 0;
+}
+
+static int
+tlsm_add_key_from_file( tlsm_ctx *ctx, const char *filename )
+{
+ CK_SLOT_ID slotID;
+ PK11SlotInfo * slot = NULL;
+ PK11GenericObject *rv;
+ CK_ATTRIBUTE *attrs;
+ CK_ATTRIBUTE theTemplate[20];
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_OBJECT_CLASS objClass = CKO_PRIVATE_KEY;
+ int retcode = 0;
+ PRFileInfo fi;
+ PRStatus status;
+
+ memset( &fi, 0, sizeof(fi) );
+ status = PR_GetFileInfo( filename, &fi );
+ if ( PR_SUCCESS != status) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not read key file %s - error %d:%s.\n",
+ filename, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ return -1;
+ }
+
+ if ( fi.type != PR_FILE_FILE ) {
+ PR_SetError(PR_IS_DIRECTORY_ERROR, 0);
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: the key file %s is not a file.\n",
+ filename, 0 ,0 );
+ return -1;
+ }
+
+ attrs = theTemplate;
+
+ if ( ctx->tc_slotname == NULL ) { /* need new slot */
+ slotID = ++tlsm_slot_count;
+ ctx->tc_slotname = PR_smprintf( TLSM_PEM_TOKEN_FMT, slotID );
+ }
+ slot = PK11_FindSlotByName( ctx->tc_slotname );
+
+ if ( !slot ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not find the slot %s for the private key - error %d:%s.\n",
+ ctx->tc_slotname, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ return -1;
+ }
+
+ PK11_SETATTRS( attrs, CKA_CLASS, &objClass, sizeof(objClass) ); attrs++;
+ PK11_SETATTRS( attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) ); attrs++;
+ PK11_SETATTRS( attrs, CKA_LABEL, (unsigned char *)filename, strlen(filename)+1 ); attrs++;
+ rv = PK11_CreateGenericObject( slot, theTemplate, 3, PR_FALSE /* isPerm */ );
+
+ if ( !rv ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not add the certificate %s - error %d:%s.\n",
+ ctx->tc_certname, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ retcode = -1;
+ } else {
+ /* When adding an encrypted key the PKCS#11 will be set as removed */
+ /* This will force the token to be seen as re-inserted */
+ SECMOD_WaitForAnyTokenEvent( pem_module, 0, 0 );
+ PK11_IsPresent( slot );
+ retcode = 0;
+ }
+
+ PK11_FreeSlot( slot );
+
+ if ( !retcode ) {
+ tlsm_add_pem_obj( ctx, rv );
+ }
+ return retcode;
+}
+
+static int
+tlsm_init_ca_certs( tlsm_ctx *ctx, const char *cacertfile, const char *cacertdir )
+{
+ PRBool isca = PR_TRUE;
+ PRStatus status = PR_SUCCESS;
+ PRErrorCode errcode = PR_SUCCESS;
+
+ if ( !cacertfile && !cacertdir ) {
+ /* no checking - not good, but allowed */
+ return 0;
+ }
+
+ if ( cacertfile ) {
+ int rc = tlsm_add_cert_from_file( ctx, cacertfile, isca, PR_TRUE );
+ if ( rc ) {
+ errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: %s is not a valid CA certificate file - error %d:%s.\n",
+ cacertfile, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ /* failure with cacertfile is a hard failure even if cacertdir is
+ also specified and contains valid CA cert files */
+ status = PR_FAILURE;
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: loaded CA certificate file %s.\n",
+ cacertfile, 0, 0 );
+ }
+ }
+
+ /* if cacertfile above failed, we will return failure, even
+ if there is a valid CA cert in cacertdir - but we still
+ process cacertdir in case the user has enabled trace level
+ debugging so they can see the processing for cacertdir too */
+ /* any cacertdir failures are "soft" failures - if the user specifies
+ no cert checking, then we allow the tls/ssl to continue, no matter
+ what was specified for cacertdir, or the contents of the directory
+ - this is different behavior than that of cacertfile */
+ if ( cacertdir ) {
+ PRFileInfo fi;
+ PRDir *dir;
+ PRDirEntry *entry;
+ PRStatus fistatus = PR_FAILURE;
+
+ memset( &fi, 0, sizeof(fi) );
+ fistatus = PR_GetFileInfo( cacertdir, &fi );
+ if ( PR_SUCCESS != fistatus) {
+ errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not get info about the CA certificate directory %s - error %d:%s.\n",
+ cacertdir, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ goto done;
+ }
+
+ if ( fi.type != PR_FILE_DIRECTORY ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: the CA certificate directory %s is not a directory.\n",
+ cacertdir, 0 ,0 );
+ goto done;
+ }
+
+ dir = PR_OpenDir( cacertdir );
+ if ( NULL == dir ) {
+ errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not open the CA certificate directory %s - error %d:%s.\n",
+ cacertdir, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ goto done;
+ }
+
+ do {
+ entry = PR_ReadDir( dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN );
+ if ( ( NULL != entry ) && ( NULL != entry->name ) ) {
+ char *fullpath = NULL;
+ char *ptr;
+
+ ptr = PL_strrstr( entry->name, PEM_CA_HASH_FILE_SUFFIX );
+ if ( ( ptr == NULL ) || ( *(ptr + PEM_CA_HASH_FILE_SUFFIX_LEN) != '\0' ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: file %s does not end in [%s] - does not appear to be a CA certificate "
+ "directory file with a properly hashed file name - skipping.\n",
+ entry->name, PEM_CA_HASH_FILE_SUFFIX, 0 );
+ continue;
+ }
+ fullpath = PR_smprintf( "%s/%s", cacertdir, entry->name );
+ if ( !tlsm_add_cert_from_file( ctx, fullpath, isca, PR_TRUE ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: loaded CA certificate file %s from CA certificate directory %s.\n",
+ fullpath, cacertdir, 0 );
+ } else {
+ errcode = PR_GetError();
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: %s is not a valid CA certificate file - error %d:%s.\n",
+ fullpath, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ }
+ PR_smprintf_free( fullpath );
+ }
+ } while ( NULL != entry );
+ PR_CloseDir( dir );
+ }
+done:
+ if ( status != PR_SUCCESS ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * NSS supports having multiple cert/key databases in the same
+ * directory, each one having a unique string prefix e.g.
+ * slapd-01-cert8.db - the prefix here is "slapd-01-"
+ * this function examines the given certdir - if it looks like
+ * /path/to/directory/prefix it will return the
+ * /path/to/directory part in realcertdir, and the prefix in prefix
+ */
+static void
+tlsm_get_certdb_prefix( const char *certdir, char **realcertdir, char **prefix )
+{
+ char sep = PR_GetDirectorySeparator();
+ char *ptr = NULL;
+ struct PRFileInfo prfi;
+ PRStatus prc;
+
+ *realcertdir = (char *)certdir; /* default is the one passed in */
+
+ /* if certdir is not given, just return */
+ if ( !certdir ) {
+ return;
+ }
+
+ prc = PR_GetFileInfo( certdir, &prfi );
+ /* if certdir exists (file or directory) then it cannot specify a prefix */
+ if ( prc == PR_SUCCESS ) {
+ return;
+ }
+
+ /* if certdir was given, and there is a '/' in certdir, see if there
+ is anything after the last '/' - if so, assume it is the prefix */
+ if ( ( ( ptr = strrchr( certdir, sep ) ) ) && *(ptr+1) ) {
+ *realcertdir = PL_strndup( certdir, ptr-certdir );
+ *prefix = PL_strdup( ptr+1 );
+ }
+
+ return;
+}
+
+/*
+ * This is the part of the init we defer until we get the
+ * actual security configuration information. This is
+ * only called once, protected by a PRCallOnce
+ * NOTE: This must be done before the first call to SSL_ImportFD,
+ * especially the setting of the policy
+ * NOTE: This must be called after fork()
+ */
+static int
+tlsm_deferred_init( void *arg )
+{
+ tlsm_ctx *ctx = (tlsm_ctx *)arg;
+ struct ldaptls *lt = ctx->tc_config;
+ const char *securitydirs[3];
+ int ii;
+ int nn;
+ PRErrorCode errcode = 1;
+#ifdef HAVE_NSS_INITCONTEXT
+ NSSInitParameters initParams;
+ NSSInitContext *initctx = NULL;
+#endif
+ SECStatus rc;
+ int done = 0;
+
+#ifdef HAVE_SECMOD_RESTARTMODULES
+ /* NSS enforces the pkcs11 requirement that modules should be unloaded after
+ a fork() - since there is no portable way to determine if NSS has been
+ already initialized in a parent process, we just call SECMOD_RestartModules
+ with force == FALSE - if the module has been unloaded due to a fork, it will
+ be reloaded, otherwise, it is a no-op */
+ if ( SECFailure == ( rc = SECMOD_RestartModules(PR_FALSE /* do not force */) ) ) {
+ errcode = PORT_GetError();
+ if ( errcode != SEC_ERROR_NOT_INITIALIZED ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: could not restart the security modules: %d:%s\n",
+ errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
+ } else {
+ errcode = 1;
+ }
+ }
+#endif
+
+#ifdef HAVE_NSS_INITCONTEXT
+ memset( &initParams, 0, sizeof( initParams ) );
+ initParams.length = sizeof( initParams );
+#endif /* HAVE_NSS_INITCONTEXT */
+
+#ifdef LDAP_R_COMPILE
+ if ( PR_CallOnce( &tlsm_init_mutex_callonce, tlsm_thr_init_callonce ) ) {
+ return -1;
+ }
+#endif /* LDAP_R_COMPILE */
+
+#ifndef HAVE_NSS_INITCONTEXT
+ if ( !NSS_IsInitialized() ) {
+#endif /* HAVE_NSS_INITCONTEXT */
+ /*
+ MOZNSS_DIR will override everything else - you can
+ always set MOZNSS_DIR to force the use of this
+ directory
+ If using MOZNSS, specify the location of the moznss db dir
+ in the cacertdir directive of the OpenLDAP configuration.
+ DEFAULT_MOZNSS_DIR will only be used if the code cannot
+ find a security dir to use based on the current
+ settings
+ */
+ nn = 0;
+ securitydirs[nn++] = PR_GetEnv( "MOZNSS_DIR" );
+ securitydirs[nn++] = lt->lt_cacertdir;
+ securitydirs[nn++] = PR_GetEnv( "DEFAULT_MOZNSS_DIR" );
+ for ( ii = 0; !done && ( ii < nn ); ++ii ) {
+ char *realcertdir = NULL;
+ const char *defprefix = "";
+ char *prefix = (char *)defprefix;
+ const char *securitydir = securitydirs[ii];
+ if ( NULL == securitydir ) {
+ continue;
+ }
+
+ tlsm_get_certdb_prefix( securitydir, &realcertdir, &prefix );
+ LDAP_MUTEX_LOCK( &tlsm_init_mutex );
+
+#ifdef HAVE_NSS_INITCONTEXT
+#ifdef INITCONTEXT_HACK
+ if ( !NSS_IsInitialized() && ctx->tc_is_server ) {
+ rc = NSS_Initialize( realcertdir, prefix, prefix, SECMOD_DB, NSS_INIT_READONLY );
+ } else {
+ initctx = NSS_InitContext( realcertdir, prefix, prefix, SECMOD_DB,
+ &initParams, NSS_INIT_READONLY );
+ rc = (initctx == NULL) ? SECFailure : SECSuccess;
+ }
+#else
+ initctx = NSS_InitContext( realcertdir, prefix, prefix, SECMOD_DB,
+ &initParams, NSS_INIT_READONLY );
+ rc = (initctx == NULL) ? SECFailure : SECSuccess;
+#endif
+#else
+ rc = NSS_Initialize( realcertdir, prefix, prefix, SECMOD_DB, NSS_INIT_READONLY );
+#endif
+
+ LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
+
+ if ( rc != SECSuccess ) {
+ errcode = PORT_GetError();
+ if ( securitydirs[ii] != lt->lt_cacertdir) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: could not initialize moznss using security dir %s prefix %s - error %d.\n",
+ realcertdir, prefix, errcode );
+ }
+ } else {
+ /* success */
+ Debug( LDAP_DEBUG_TRACE, "TLS: using moznss security dir %s prefix %s.\n",
+ realcertdir, prefix, 0 );
+ errcode = 0;
+ done = 1;
+ }
+ if ( realcertdir != securitydir ) {
+ PL_strfree( realcertdir );
+ }
+ if ( prefix != defprefix ) {
+ PL_strfree( prefix );
+ }
+ }
+
+ if ( errcode ) { /* no moznss db found, or not using moznss db */
+ LDAP_MUTEX_LOCK( &tlsm_init_mutex );
+#ifdef HAVE_NSS_INITCONTEXT
+ int flags = NSS_INIT_READONLY|NSS_INIT_NOCERTDB|NSS_INIT_NOMODDB;
+#ifdef INITCONTEXT_HACK
+ if ( !NSS_IsInitialized() && ctx->tc_is_server ) {
+ rc = NSS_NoDB_Init( NULL );
+ } else {
+ initctx = NSS_InitContext( "", "", "", SECMOD_DB,
+ &initParams, flags );
+ rc = (initctx == NULL) ? SECFailure : SECSuccess;
+ }
+#else
+ initctx = NSS_InitContext( "", "", "", SECMOD_DB,
+ &initParams, flags );
+ rc = (initctx == NULL) ? SECFailure : SECSuccess;
+#endif
+#else
+ rc = NSS_NoDB_Init( NULL );
+#endif
+ LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
+ if ( rc != SECSuccess ) {
+ errcode = PORT_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not initialize moznss - error %d:%s.\n",
+ errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
+ return -1;
+ }
+
+#ifdef HAVE_NSS_INITCONTEXT
+ ctx->tc_initctx = initctx;
+#endif
+
+ /* initialize the PEM module */
+ LDAP_MUTEX_LOCK( &tlsm_init_mutex );
+ if ( tlsm_init_pem_module() ) {
+ LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
+ errcode = PORT_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not initialize moznss PEM module - error %d:%s.\n",
+ errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
+ return -1;
+ }
+ LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
+
+ if ( tlsm_init_ca_certs( ctx, lt->lt_cacertfile, lt->lt_cacertdir ) ) {
+ /* if we tried to use lt->lt_cacertdir as an NSS key/cert db, errcode
+ will be a value other than 1 - print an error message so that the
+ user will know that failed too */
+ if ( ( errcode != 1 ) && ( lt->lt_cacertdir ) ) {
+ char *realcertdir = NULL;
+ char *prefix = NULL;
+ tlsm_get_certdb_prefix( lt->lt_cacertdir, &realcertdir, &prefix );
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: could not initialize moznss using security dir %s prefix %s - error %d.\n",
+ realcertdir, prefix ? prefix : "", errcode );
+ if ( realcertdir != lt->lt_cacertdir ) {
+ PL_strfree( realcertdir );
+ }
+ PL_strfree( prefix );
+ }
+ return -1;
+ }
+
+ ctx->tc_using_pem = PR_TRUE;
+ }
+
+#ifdef HAVE_NSS_INITCONTEXT
+ if ( !ctx->tc_initctx ) {
+ ctx->tc_initctx = initctx;