X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=libraries%2Flibldap%2Ftls_m.c;h=c85d322014fa838341f3fefdea9a5f693fadc079;hb=7164c4c66215da39f03e353a9c72eba408abddbf;hp=6d1c0a4ca114195ec7e990e685cab91bbb8e56de;hpb=bbecfa740de0cb7f93ef568d1e681f88009eedcc;p=openldap diff --git a/libraries/libldap/tls_m.c b/libraries/libldap/tls_m.c index 6d1c0a4ca1..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; - ret = tlsm_verify_cert( (CERTCertDBHandle *)arg, SSL_PeerCertificate( fd ), + if (ctx && ctx->tc_warn_only ) + errorToIgnore = -1; + + 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 ) { @@ -1320,7 +1373,7 @@ static int 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 ) { @@ -1336,14 +1389,24 @@ tlsm_init_ca_certs( tlsm_ctx *ctx, const char *cacertfile, const char *cacertdir "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; @@ -1397,7 +1460,6 @@ tlsm_init_ca_certs( tlsm_ctx *ctx, const char *cacertfile, const char *cacertdir 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, @@ -1412,14 +1474,6 @@ tlsm_init_ca_certs( tlsm_ctx *ctx, const char *cacertfile, const char *cacertdir } 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; } @@ -1512,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 */ @@ -1539,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 ) { @@ -1557,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) { @@ -1580,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 @@ -1598,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, @@ -1611,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 @@ -1652,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 */ @@ -1754,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 */ @@ -1770,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; } @@ -1799,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; } @@ -1825,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 " @@ -1847,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 * @@ -1875,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; } @@ -1911,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 @@ -2036,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 || @@ -2045,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 ) ) { @@ -2158,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; } @@ -2179,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", @@ -2272,7 +2394,7 @@ tlsm_is_non_ssl_message( PRFileDesc *fd, ber_tag_t *thebyte ) } if ( p->firsttag == LBER_SEQUENCE ) { - if ( *thebyte ) { + if ( thebyte ) { *thebyte = p->firsttag; } return 1; @@ -2769,7 +2891,7 @@ tlsm_PR_GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data) struct tls_data *p; p = tlsm_get_pvt_tls_data( fd ); - if ( !data ) { + if ( p == NULL || data == NULL ) { return PR_FAILURE; } @@ -2890,7 +3012,9 @@ tlsm_init( void ) * context in the child. */ if ( !nofork ) { - PR_SetEnv( "NSS_STRICT_NOFORK=DISABLED" ); + /* will leak one time */ + char *noforkenvvar = PL_strdup( "NSS_STRICT_NOFORK=DISABLED" ); + PR_SetEnv( noforkenvvar ); } return 0;