/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
- * Copyright 2008-2010 The OpenLDAP Foundation.
+ * Copyright 2008-2011 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
#include <nss/secerr.h>
#include <nss/keyhi.h>
#include <nss/secmod.h>
+#include <nss/cert.h>
+
+#undef NSS_VERSION_INT
+#define NSS_VERSION_INT ((NSS_VMAJOR << 24) | (NSS_VMINOR << 16) | \
+ (NSS_VPATCH << 8) | NSS_VBUILD)
/* NSS 3.12.5 and later have NSS_InitContext */
-#if NSS_VMAJOR <= 3 && NSS_VMINOR <= 12 && NSS_VPATCH < 5
-/* do nothing */
-#else
+#if NSS_VERSION_INT >= 0x030c0500
#define HAVE_NSS_INITCONTEXT 1
#endif
+/* NSS 3.12.9 and later have SECMOD_RestartModules */
+#if NSS_VERSION_INT >= 0x030c0900
+#define HAVE_SECMOD_RESTARTMODULES 1
+#endif
+
/* InitContext does not currently work in server mode */
/* #define INITCONTEXT_HACK 1 */
#endif
PK11GenericObject **tc_pem_objs; /* array of objects to free */
int tc_n_pem_objs; /* number of objects */
+ PRBool tc_warn_only; /* only warn of errors in validation */
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_t tc_refmutex;
#endif
#ifdef LDAP_R_COMPILE
+/* it doesn't seem guaranteed that a client will call
+ tlsm_thr_init in a non-threaded context - so we have
+ to wrap the mutex creation in a prcallonce
+*/
+static ldap_pvt_thread_mutex_t tlsm_init_mutex;
+static PRCallOnceType tlsm_init_mutex_callonce = {0,0};
+
+static PRStatus PR_CALLBACK
+tlsm_thr_init_callonce( void )
+{
+ if ( ldap_pvt_thread_mutex_init( &tlsm_init_mutex ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not create mutex for moznss initialization: %d\n", errno, 0, 0 );
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
static void
tlsm_thr_init( void )
{
+ ( void )PR_CallOnce( &tlsm_init_mutex_callonce, tlsm_thr_init_callonce );
}
#endif /* LDAP_R_COMPILE */
/* SSL3 ciphers */
{"RC4-MD5", SSL_RSA_WITH_RC4_128_MD5, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_MD5, SSL3, 128, 128, SSL_MEDIUM, SSL_ALLOWED},
- {"RC4-SHA", SSL_RSA_WITH_RC4_128_SHA, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_SHA1, SSL3, 128, 128, SSL_MEDIUM, SSL_NOT_ALLOWED},
+ {"RC4-SHA", SSL_RSA_WITH_RC4_128_SHA, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_SHA1, SSL3, 128, 128, SSL_MEDIUM, SSL_ALLOWED},
{"DES-CBC3-SHA", SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_3DES|SSL_SHA1, SSL3, 168, 168, SSL_HIGH, SSL_ALLOWED},
{"DES-CBC-SHA", SSL_RSA_WITH_DES_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_DES|SSL_SHA1, SSL3, 56, 56, SSL_LOW, SSL_ALLOWED},
{"EXP-RC4-MD5", SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_MD5, SSL3, 40, 128, SSL_EXPORT40, SSL_ALLOWED},
/* TLSv1 ciphers */
{"EXP1024-DES-CBC-SHA", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_DES|SSL_SHA, TLS1, 56, 56, SSL_EXPORT56, SSL_ALLOWED},
{"EXP1024-RC4-SHA", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_SHA, TLS1, 56, 56, SSL_EXPORT56, SSL_ALLOWED},
- {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_AES|SSL_SHA, TLS1, 128, 128, SSL_HIGH, SSL_NOT_ALLOWED},
- {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_AES|SSL_SHA, TLS1, 256, 256, SSL_HIGH, SSL_NOT_ALLOWED},
+ {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_AES|SSL_SHA, TLS1, 128, 128, SSL_HIGH, SSL_ALLOWED},
+ {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_AES|SSL_SHA, TLS1, 256, 256, SSL_HIGH, SSL_ALLOWED},
};
#define ciphernum (sizeof(ciphers_def)/sizeof(cipher_properties))
case SEC_ERROR_UNTRUSTED_ISSUER:
case SEC_ERROR_UNKNOWN_ISSUER:
case SEC_ERROR_EXPIRED_CERTIFICATE:
+ case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
if (ctx->tc_verify_cert) {
success = SECFailure;
}
}
static SECStatus
-tlsm_auth_cert_handler(void *arg, PRFileDesc *fd,
- PRBool checksig, PRBool isServer)
+tlsm_get_basic_constraint_extension( CERTCertificate *cert,
+ CERTBasicConstraints *cbcval )
{
- SECStatus ret = SSL_AuthCertificate(arg, fd, checksig, isServer);
+ SECItem encodedVal = { 0, NULL };
+ SECStatus rc;
- if ( ret != SECSuccess ) {
- PRErrorCode errcode = PORT_GetError();
- /* we bypass NSS's hostname checks and do our own - tlsm_session_chkhost will handle it */
- if ( errcode == SSL_ERROR_BAD_CERT_DOMAIN ) {
- Debug( LDAP_DEBUG_TRACE,
- "TLS certificate verification: defer\n",
- 0, 0, 0 );
- } else {
- Debug( LDAP_DEBUG_ANY,
- "TLS certificate verification: Error, %d: %s\n",
- errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 ) ;
+ 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 verification: ok\n",
- 0, 0, 0 );
+ "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 )
{
SECStatus rc = SECSuccess;
SSL_ShutdownServerSessionIDCache();
- SSL_ClearSessionCache();
if ( pem_module ) {
SECMOD_UnloadUserModule( pem_module );
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 )
{
}
static int
-tlsm_add_cert_from_file( tlsm_ctx *ctx, const char *filename, PRBool isca )
+tlsm_add_cert_from_file( tlsm_ctx *ctx, const char *filename, PRBool isca, PRBool istrusted )
{
CK_SLOT_ID slotID;
PK11SlotInfo *slot = NULL;
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 */
- slotID = ++tlsm_slot_count;
+ 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;
- ctx->tc_certname = PR_smprintf( "%s:%s", slotname, 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 );
+ }
}
}
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 ( isca ) {
+ if ( istrusted ) {
PK11_SETATTRS( attrs, CKA_TRUST, &cktrue, sizeof(CK_BBOOL) ); attrs++;
} else {
PK11_SETATTRS( attrs, CKA_TRUST, &ckfalse, sizeof(CK_BBOOL) ); attrs++;
tlsm_init_ca_certs( tlsm_ctx *ctx, const char *cacertfile, const char *cacertdir )
{
PRBool isca = PR_TRUE;
- PRStatus status = PR_FAILURE;
+ PRStatus status = PR_SUCCESS;
PRErrorCode errcode = PR_SUCCESS;
if ( !cacertfile && !cacertdir ) {
}
if ( cacertfile ) {
- int rc = tlsm_add_cert_from_file( ctx, cacertfile, isca );
+ 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 );
- status = PR_SUCCESS; /* have at least one good CA - we can proceed */
}
}
+ /* 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;
continue;
}
fullpath = PR_smprintf( "%s/%s", cacertdir, entry->name );
- if ( !tlsm_add_cert_from_file( ctx, fullpath, isca ) ) {
+ 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 );
- status = PR_SUCCESS; /* found at least 1 valid CA file in the dir */
} else {
errcode = PR_GetError();
Debug( LDAP_DEBUG_TRACE,
}
done:
if ( status != PR_SUCCESS ) {
- const char *fmtstr = NULL;
- if ( cacertfile && cacertdir ) {
- fmtstr = "TLS: did not find any valid CA certificates in %s or %s\n";
- } else {
- fmtstr = "TLS: did not find any valid CA certificates in %s%s\n";
- }
- Debug( LDAP_DEBUG_ANY, fmtstr, cacertdir ? cacertdir : "",
- cacertfile ? cacertfile : "", 0 );
return -1;
}
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 */
}
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 );
#endif
+ LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
+
if ( rc != SECSuccess ) {
errcode = PORT_GetError();
if ( securitydirs[ii] != lt->lt_cacertdir) {
}
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
#else
rc = NSS_NoDB_Init( NULL );
#endif
+ LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
if ( rc != SECSuccess ) {
errcode = PORT_GetError();
Debug( LDAP_DEBUG_ANY,
#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
PK11_SetPasswordFunc( tlsm_pin_prompt );
/* register cleanup function */
- /* delete the old one, if any */
- NSS_UnregisterShutdown( tlsm_nss_shutdown_cb, NULL );
- NSS_RegisterShutdown( tlsm_nss_shutdown_cb, NULL );
-
+ if ( tlsm_register_nss_shutdown() ) {
+ errcode = PORT_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not register NSS shutdown function: %d:%s\n",
+ errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
+ return -1;
+ }
#ifndef HAVE_NSS_INITCONTEXT
}
#endif /* HAVE_NSS_INITCONTEXT */
SECCertificateUsage certUsage;
PRBool checkSig = PR_TRUE;
SECStatus status;
+ /* may not have a CA cert - ok - ignore SEC_ERROR_UNKNOWN_ISSUER */
+ int errorToIgnore = SEC_ERROR_UNKNOWN_ISSUER;
if ( pRetKey ) {
*pRetKey = key; /* caller will deal with this */
} else {
checkSig = PR_FALSE;
}
- status = CERT_VerifyCertificateNow( ctx->tc_certdb, cert,
- checkSig, certUsage,
- pin_arg, NULL );
- if ( status != SECSuccess ) {
- /* NSS doesn't like self-signed CA certs that are also used for
- TLS/SSL server certs (such as generated by openssl req -x509)
- CERT_VerifyCertificateNow returns SEC_ERROR_UNTRUSTED_ISSUER in that case
- so, see if the cert and issuer are the same cert
- */
- PRErrorCode errcode = PR_GetError();
-
- if ( errcode == SEC_ERROR_UNTRUSTED_ISSUER ) {
- CERTCertificate *issuer = CERT_FindCertIssuer( cert, PR_Now(), certUsageSSLServer );
- if ( NULL == issuer ) {
- /* no issuer - fail */
- Debug( LDAP_DEBUG_ANY,
- "TLS: error: the server certificate %s has no issuer - "
- "please check this certificate for validity\n",
- certname, 0, 0 );
- } else if ( CERT_CompareCerts( cert, issuer ) ) {
- /* self signed - warn and allow */
- status = SECSuccess;
- rc = 0;
- Debug( LDAP_DEBUG_ANY,
- "TLS: warning: using self-signed server certificate %s\n",
- certname, 0, 0 );
- }
- CERT_DestroyCertificate( issuer );
- }
-
- if ( status != SECSuccess ) {
- Debug( LDAP_DEBUG_ANY,
- "TLS: error: the certificate %s is not valid - error %d:%s\n",
- certname, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
- }
- } else {
- rc = 0; /* success */
+ if ( ctx->tc_warn_only ) {
+ errorToIgnore = -1;
+ }
+ status = tlsm_verify_cert( ctx->tc_certdb, cert, pin_arg,
+ checkSig, certUsage, errorToIgnore );
+ if ( status == SECSuccess ) {
+ rc = 0;
}
} else {
PRErrorCode errcode = PR_GetError();
{
tlsm_ctx *ctx = (tlsm_ctx *)arg;
int rc;
+ PRBool saveval;
/* don't need caNames - this function will call CERT_VerifyCertificateNow
which will verify the cert against the known CAs */
+ saveval = ctx->tc_warn_only;
+ ctx->tc_warn_only = PR_TRUE;
rc = tlsm_find_and_verify_cert_key( ctx, fd, ctx->tc_certname, 0, pRetCert, pRetKey );
+ ctx->tc_warn_only = saveval;
if ( rc ) {
Debug( LDAP_DEBUG_ANY,
"TLS: error: unable to perform client certificate authentication for "
"certificate named %s\n", ctx->tc_certname, 0, 0 );
+ if ( pRetKey && *pRetKey ) {
+ SECKEY_DestroyPrivateKey( *pRetKey );
+ *pRetKey = NULL;
+ }
+ if ( pRetCert && *pRetCert ) {
+ CERT_DestroyCertificate( *pRetCert );
+ *pRetCert = NULL;
+ }
return SECFailure;
}
{
SECStatus status = SECFailure;
int rc;
+ PRBool saveval;
+ saveval = ctx->tc_warn_only;
+ ctx->tc_warn_only = PR_TRUE;
rc = tlsm_find_and_verify_cert_key( ctx, ctx->tc_model, ctx->tc_certname, 0, NULL, NULL );
+ ctx->tc_warn_only = saveval;
if ( rc ) {
Debug( LDAP_DEBUG_ANY,
"TLS: error: unable to set up client certificate authentication for "
static void
tlsm_destroy( void )
{
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_destroy( &tlsm_init_mutex );
+#endif
}
static tls_ctx *
#endif /* HAVE_NSS_INITCONTEXT */
ctx->tc_pem_objs = NULL;
ctx->tc_n_pem_objs = 0;
+ ctx->tc_warn_only = PR_FALSE;
}
return (tls_ctx *)ctx;
}
PL_strfree( c->tc_slotname );
tlsm_free_pem_objs( c );
#ifdef HAVE_NSS_INITCONTEXT
- if (c->tc_initctx)
- NSS_ShutdownContext( c->tc_initctx );
+ if ( c->tc_initctx ) {
+ LDAP_MUTEX_LOCK( &tlsm_init_mutex );
+ if ( NSS_ShutdownContext( c->tc_initctx ) ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not shutdown NSS - error %d:%s.\n",
+ errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
+ }
+ LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
+ }
c->tc_initctx = NULL;
#endif /* HAVE_NSS_INITCONTEXT */
#ifdef LDAP_R_COMPILE
"TLS: could not set cipher list %s.\n",
lt->lt_ciphersuite, 0, 0 );
return -1;
- }
+ } else if ( tlsm_parse_ciphers( ctx, "DEFAULT" ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set cipher list DEFAULT.\n",
+ 0, 0, 0 );
+ return -1;
+ }
- if ( ctx->tc_require_cert ) {
+ if ( !ctx->tc_require_cert ) {
+ ctx->tc_verify_cert = PR_FALSE;
+ } else if ( !ctx->tc_is_server ) {
request_cert = PR_TRUE;
require_cert = SSL_REQUIRE_NO_ERROR;
if ( ctx->tc_require_cert == LDAP_OPT_X_TLS_DEMAND ||
}
if ( ctx->tc_require_cert != LDAP_OPT_X_TLS_ALLOW )
ctx->tc_verify_cert = PR_TRUE;
- } else {
- ctx->tc_verify_cert = PR_FALSE;
+ } else { /* server */
+ /* server does not request certs by default */
+ /* if allow - client may send cert, server will ignore if errors */
+ /* if try - client may send cert, server will error if bad cert */
+ /* if hard or demand - client must send cert, server will error if bad cert */
+ request_cert = PR_TRUE;
+ require_cert = SSL_REQUIRE_NO_ERROR;
+ if ( ctx->tc_require_cert == LDAP_OPT_X_TLS_DEMAND ||
+ ctx->tc_require_cert == LDAP_OPT_X_TLS_HARD ) {
+ require_cert = SSL_REQUIRE_ALWAYS;
+ }
+ if ( ctx->tc_require_cert != LDAP_OPT_X_TLS_ALLOW ) {
+ ctx->tc_verify_cert = PR_TRUE;
+ } else {
+ ctx->tc_warn_only = PR_TRUE;
+ }
}
if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_REQUEST_CERTIFICATE, request_cert ) ) {
/* otherwise, assume this is the name of a cert already in the db */
if ( ctx->tc_using_pem ) {
/* this sets ctx->tc_certname to the correct value */
- int rc = tlsm_add_cert_from_file( ctx, lt->lt_certfile, PR_FALSE /* not a ca */ );
+ int rc = tlsm_add_cert_from_file( ctx, lt->lt_certfile, PR_FALSE, PR_TRUE );
if ( rc ) {
return rc;
}
Debug( LDAP_DEBUG_ANY,
"TLS: error: unable to find and verify server's cert and key for certificate %s\n",
ctx->tc_certname, 0, 0 );
+ CERT_DestroyCertificate( serverCert );
+ SECKEY_DestroyPrivateKey( serverKey );
return -1;
}
/* Callback for authenticating certificate */
if ( SSL_AuthCertificateHook( ctx->tc_model, tlsm_auth_cert_handler,
- ctx->tc_certdb ) != SECSuccess ) {
+ ctx ) != SECSuccess ) {
PRErrorCode err = PR_GetError();
Debug( LDAP_DEBUG_ANY,
"TLS: error: could not set auth cert handler for moznss - error %d:%s\n",
}
if ( p->firsttag == LBER_SEQUENCE ) {
- if ( *thebyte ) {
+ if ( thebyte ) {
*thebyte = p->firsttag;
}
return 1;
tlsm_session_errmsg( tls_session *sess, int rc, char *buf, size_t len )
{
int i;
+ int prerror = PR_GetError();
- rc = PR_GetError();
i = PR_GetErrorTextLength();
if ( i > len ) {
char *msg = LDAP_MALLOC( i+1 );
LDAP_FREE( msg );
} else if ( i ) {
PR_GetErrorText( buf );
+ } else if ( prerror ) {
+ i = PR_snprintf( buf, len, "TLS error %d:%s",
+ prerror, PR_ErrorToString( prerror, PR_LANGUAGE_I_DEFAULT ) );
}
- return i ? buf : NULL;
+ return ( i > 0 ) ? buf : NULL;
}
static int
struct tls_data *p;
p = tlsm_get_pvt_tls_data( fd );
- if ( !data ) {
+ if ( p == NULL || data == NULL ) {
return PR_FAILURE;
}
static int
tlsm_init( void )
{
+ char *nofork = PR_GetEnv( "NSS_STRICT_NOFORK" );
+
PR_Init(0, 0, 0);
tlsm_layer_id = PR_GetUniqueIdentity( "OpenLDAP" );
+ /*
+ * There are some applications that acquire a crypto context in the parent process
+ * and expect that crypto context to work after a fork(). This does not work
+ * with NSS using strict PKCS11 compliance mode. We set this environment
+ * variable here to tell the software encryption module/token to allow crypto
+ * contexts to persist across a fork(). However, if you are using some other
+ * module or encryption device that supports and expects full PKCS11 semantics,
+ * the only recourse is to rewrite the application with atfork() handlers to save
+ * the crypto context in the parent and restore (and SECMOD_RestartModules) the
+ * context in the child.
+ */
+ if ( !nofork ) {
+ /* will leak one time */
+ char *noforkenvvar = PL_strdup( "NSS_STRICT_NOFORK=DISABLED" );
+ PR_SetEnv( noforkenvvar );
+ }
+
return 0;
}