X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=libraries%2Flibldap%2Ftls_m.c;h=c85d322014fa838341f3fefdea9a5f693fadc079;hb=7164c4c66215da39f03e353a9c72eba408abddbf;hp=224b571842848ffff49f10dc60de43ebc823de21;hpb=5e467e489949c4eb9e22953b3e0450a6a00a7399;p=openldap diff --git a/libraries/libldap/tls_m.c b/libraries/libldap/tls_m.c index 224b571842..c85d322014 100644 --- a/libraries/libldap/tls_m.c +++ b/libraries/libldap/tls_m.c @@ -96,6 +96,7 @@ typedef struct tlsm_ctx { #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 @@ -129,9 +130,29 @@ static int tlsm_init( void ); #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 */ @@ -671,6 +692,7 @@ tlsm_bad_cert_handler(void *arg, PRFileDesc *ssl) 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; } @@ -944,6 +966,11 @@ tlsm_verify_cert(CERTCertDBHandle *handle, CERTCertificate *cert, void *pinarg, 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 */ @@ -964,7 +991,7 @@ tlsm_verify_cert(CERTCertDBHandle *handle, CERTCertificate *cert, void *pinarg, /* it is possible for CERT_VerifyCertificate return with an error with no logging */ if ( ret != SECSuccess ) { PRErrorCode errcode = PR_GetError(); - Debug( LDAP_DEBUG_ANY, + Debug( debug_level, "TLS: certificate [%s] is not valid - error %d:%s.\n", name ? name : "(unknown)", errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); @@ -994,17 +1021,17 @@ tlsm_verify_cert(CERTCertDBHandle *handle, CERTCertificate *cert, void *pinarg, "please fix your certs if possible\n", name, 0, 0 ); } else { /* does not have basicconstraint, or some other error */ ret = SECFailure; - Debug( LDAP_DEBUG_ANY, + 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( LDAP_DEBUG_ANY, + 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( LDAP_DEBUG_ANY, + Debug( debug_level, "TLS: certificate [%s] is not valid - error %ld:%s.\n", name, node->error, PR_ErrorToString( node->error, PR_LANGUAGE_I_DEFAULT ) ); } @@ -1019,7 +1046,9 @@ tlsm_verify_cert(CERTCertDBHandle *handle, CERTCertificate *cert, void *pinarg, if ( ret == SECSuccess ) { Debug( LDAP_DEBUG_TRACE, "TLS: certificate [%s] is valid\n", name, 0, 0 ); - } + } else if ( errorToIgnore == -1 ) { + ret = SECSuccess; + } return ret; } @@ -1030,10 +1059,17 @@ tlsm_auth_cert_handler(void *arg, PRFileDesc *fd, { 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( (CERTCertDBHandle *)arg, SSL_PeerCertificate( fd ), + ret = tlsm_verify_cert( ctx->tc_certdb, peercert, SSL_RevealPinArg( fd ), - checksig, certUsage, 0 ); + checksig, certUsage, errorToIgnore ); + CERT_DestroyCertificate( peercert ); return ret; } @@ -1063,7 +1099,6 @@ tlsm_nss_shutdown_cb( void *appData, void *nssData ) SECStatus rc = SECSuccess; SSL_ShutdownServerSessionIDCache(); - SSL_ClearSessionCache(); if ( pem_module ) { SECMOD_UnloadUserModule( pem_module ); @@ -1073,6 +1108,24 @@ tlsm_nss_shutdown_cb( void *appData, void *nssData ) 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 ) { @@ -1513,6 +1566,12 @@ tlsm_deferred_init( void *arg ) 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 */ @@ -1540,6 +1599,8 @@ tlsm_deferred_init( void *arg ) } 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 ) { @@ -1558,6 +1619,8 @@ tlsm_deferred_init( void *arg ) 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) { @@ -1581,6 +1644,7 @@ tlsm_deferred_init( void *arg ) } 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 @@ -1599,6 +1663,7 @@ tlsm_deferred_init( void *arg ) #else rc = NSS_NoDB_Init( NULL ); #endif + LDAP_MUTEX_UNLOCK( &tlsm_init_mutex ); if ( rc != SECSuccess ) { errcode = PORT_GetError(); Debug( LDAP_DEBUG_ANY, @@ -1612,13 +1677,16 @@ tlsm_deferred_init( void *arg ) #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 @@ -1653,10 +1721,13 @@ tlsm_deferred_init( void *arg ) 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 */ @@ -1755,6 +1826,8 @@ tlsm_find_and_verify_cert_key(tlsm_ctx *ctx, PRFileDesc *fd, const char *certnam 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 */ @@ -1771,9 +1844,11 @@ tlsm_find_and_verify_cert_key(tlsm_ctx *ctx, PRFileDesc *fd, const char *certnam } else { checkSig = PR_FALSE; } - /* may not have a CA cert - ok - ignore SEC_ERROR_UNKNOWN_ISSUER */ + if ( ctx->tc_warn_only ) { + errorToIgnore = -1; + } status = tlsm_verify_cert( ctx->tc_certdb, cert, pin_arg, - checkSig, certUsage, SEC_ERROR_UNKNOWN_ISSUER ); + checkSig, certUsage, errorToIgnore ); if ( status == SECSuccess ) { rc = 0; } @@ -1800,14 +1875,26 @@ tlsm_get_client_auth_data( void *arg, PRFileDesc *fd, { 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; } @@ -1826,8 +1913,12 @@ tlsm_clientauth_init( tlsm_ctx *ctx ) { 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 " @@ -1848,6 +1939,9 @@ tlsm_clientauth_init( tlsm_ctx *ctx ) static void tlsm_destroy( void ) { +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_destroy( &tlsm_init_mutex ); +#endif } static tls_ctx * @@ -1876,6 +1970,7 @@ tlsm_ctx_new ( struct ldapoptions *lo ) #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; } @@ -1912,8 +2007,16 @@ tlsm_ctx_free ( 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 @@ -2037,7 +2140,9 @@ tlsm_deferred_ctx_init( void *arg ) 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 || @@ -2046,8 +2151,22 @@ tlsm_deferred_ctx_init( void *arg ) } 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 ) ) { @@ -2159,6 +2278,8 @@ tlsm_deferred_ctx_init( void *arg ) 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; } @@ -2180,7 +2301,7 @@ tlsm_deferred_ctx_init( void *arg ) /* 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",