]> git.sur5r.net Git - openldap/blobdiff - libraries/libldap/tls_m.c
ITS#7359 MozNSS: prefer unlocked slot when getting private key
[openldap] / libraries / libldap / tls_m.c
index 767cea29e31d057bc31c09586b9cca2d38368a62..5022efb89cc99dfdf18fac57b2dbcb81fa4eb101 100644 (file)
@@ -901,8 +901,10 @@ tlsm_get_pin(PK11SlotInfo *slot, PRBool retry, tlsm_ctx *ctx)
         * capability the server would have to be started in foreground mode
         * if using an encrypted key.
         */
-       if ( ctx->tc_pin_file ) {
+       if ( ctx && ctx->tc_pin_file ) {
                pwdstr = tlsm_get_pin_from_file( token_name, ctx );
+               if (retry && pwdstr != NULL)
+                       return NULL;
        }
 #endif /* RETRIEVE_PASSWORD_FROM_FILE */
 #ifdef READ_PASSWORD_FROM_STDIN
@@ -988,16 +990,49 @@ tlsm_cert_is_self_issued( CERTCertificate *cert )
        return is_self_issued;
 }
 
+/*
+ * The private key for used certificate can be already unlocked by other
+ * thread or library. Find the unlocked key if possible.
+ */
+static SECKEYPrivateKey *
+tlsm_find_unlocked_key(tlsm_ctx *ctx, void *pin_arg)
+{
+       SECKEYPrivateKey *result = NULL;
+
+       PK11SlotList *slots = PK11_GetAllSlotsForCert(ctx->tc_certificate, NULL);
+       if (!slots) {
+               PRErrorCode errcode = PR_GetError();
+               Debug(LDAP_DEBUG_ANY,
+                               "TLS: cannot get all slots for certificate '%s' (error %d: %s)",
+                               tlsm_ctx_subject_name(ctx), errcode,
+                               PR_ErrorToString(errcode, PR_LANGUAGE_I_DEFAULT));
+               return result;
+       }
+
+       PK11SlotListElement *le;
+       for (le = slots->head; le && !result; le = le->next) {
+               PK11SlotInfo *slot = le->slot;
+               if (!PK11_IsLoggedIn(slot, NULL))
+                       continue;
+
+               result = PK11_FindKeyByDERCert(slot, ctx->tc_certificate, pin_arg);
+       }
+
+       PK11_FreeSlotList(slots);
+       return result;
+}
+
 static SECStatus
 tlsm_verify_cert(CERTCertDBHandle *handle, CERTCertificate *cert, void *pinarg,
-                                PRBool checksig, SECCertificateUsage certUsage, int errorToIgnore )
+                                PRBool checksig, SECCertificateUsage certUsage, PRBool warn_only,
+                                PRBool ignore_issuer )
 {
        CERTVerifyLog verifylog;
        SECStatus ret = SECSuccess;
        const char *name;
        int debug_level = LDAP_DEBUG_ANY;
 
-       if ( errorToIgnore == -1 ) {
+       if ( warn_only ) {
                debug_level = LDAP_DEBUG_TRACE;
        }
 
@@ -1061,7 +1096,11 @@ tlsm_verify_cert(CERTCertDBHandle *handle, CERTCertificate *cert, void *pinarg,
 
                                        PR_SetError(orig_error, orig_oserror);
 
-                               } else if ( errorToIgnore && ( node->error == errorToIgnore ) ) {
+                               } else if ( warn_only || ( ignore_issuer && (
+                                       node->error == SEC_ERROR_UNKNOWN_ISSUER ||
+                                       node->error == SEC_ERROR_UNTRUSTED_ISSUER )
+                               ) ) {
+                                       ret = SECSuccess;
                                        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 ) );
@@ -1082,8 +1121,6 @@ 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;
@@ -1096,39 +1133,16 @@ 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( ctx->tc_certdb, peercert,
                                                        SSL_RevealPinArg( fd ),
-                                                       checksig, certUsage, errorToIgnore );
+                                                       checksig, certUsage, ctx->tc_warn_only, PR_FALSE );
        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 )
 {
@@ -1321,7 +1335,19 @@ tlsm_ctx_load_private_key(tlsm_ctx *ctx)
 
        void *pin_arg = SSL_RevealPinArg(ctx->tc_model);
 
-       ctx->tc_private_key = PK11_FindKeyByAnyCert(ctx->tc_certificate, pin_arg);
+       SECKEYPrivateKey *unlocked_key = tlsm_find_unlocked_key(ctx, pin_arg);
+       Debug(LDAP_DEBUG_ANY,
+                       "TLS: %s unlocked certificate for certificate '%s'.\n",
+                       unlocked_key ? "found" : "no", tlsm_ctx_subject_name(ctx), 0);
+
+       /* prefer unlocked key, then key from opened certdb, then any other */
+       if (unlocked_key)
+               ctx->tc_private_key = unlocked_key;
+       else if (ctx->tc_certdb_slot)
+               ctx->tc_private_key = PK11_FindKeyByDERCert(ctx->tc_certdb_slot, ctx->tc_certificate, pin_arg);
+       else
+               ctx->tc_private_key = PK11_FindKeyByAnyCert(ctx->tc_certificate, pin_arg);
+
        if (!ctx->tc_private_key) {
                PRErrorCode errcode = PR_GetError();
                Debug(LDAP_DEBUG_ANY,
@@ -1672,7 +1698,6 @@ tlsm_deferred_init( void *arg )
                        }
 
                        tlsm_get_certdb_prefix( securitydir, &realcertdir, &prefix );
-                       LDAP_MUTEX_LOCK( &tlsm_init_mutex );
 
                        /* initialize only moddb; certdb will be initialized explicitly */
 #ifdef HAVE_NSS_INITCONTEXT
@@ -1704,8 +1729,6 @@ 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) {
@@ -1729,7 +1752,6 @@ 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
@@ -1753,7 +1775,6 @@ 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,
@@ -1765,9 +1786,7 @@ tlsm_deferred_init( void *arg )
 
                if ( errcode || lt->lt_cacertfile ) {
                        /* initialize the PEM module */
-                       LDAP_MUTEX_LOCK( &tlsm_init_mutex );
                        if ( tlsm_init_pem_module() ) {
-                               LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
                                int pem_errcode = PORT_GetError();
                                Debug( LDAP_DEBUG_ANY,
                                           "TLS: could not initialize moznss PEM module - error %d:%s.\n",
@@ -1779,7 +1798,6 @@ tlsm_deferred_init( void *arg )
                        } else if ( !errcode ) {
                                tlsm_init_ca_certs( ctx, lt->lt_cacertfile, NULL );
                        }
-                       LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
                }
 
                if ( errcode ) {
@@ -1819,10 +1837,8 @@ tlsm_deferred_init( void *arg )
                }
 
                if  ( ctx->tc_is_server ) {
-                       LDAP_MUTEX_LOCK( &tlsm_init_mutex );
                        /* 0 means use the defaults here */
                        SSL_ConfigServerSessionIDCache( 0, 0, 0, NULL );
-                       LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
                }
 
 #ifndef HAVE_NSS_INITCONTEXT
@@ -1842,7 +1858,6 @@ tlsm_find_and_verify_cert_key(tlsm_ctx *ctx)
        SECCertificateUsage certUsage;
        PRBool checkSig;
        SECStatus status;
-       int errorToIgnore;
        void *pin_arg;
 
        if (tlsm_ctx_load_private_key(ctx))
@@ -1851,13 +1866,9 @@ tlsm_find_and_verify_cert_key(tlsm_ctx *ctx)
        pin_arg = SSL_RevealPinArg(ctx->tc_model);
        certUsage = ctx->tc_is_server ? certificateUsageSSLServer : certificateUsageSSLClient;
        checkSig = ctx->tc_verify_cert ? PR_TRUE : PR_FALSE;
-       if ( ctx->tc_warn_only )
-               errorToIgnore = -1;
-       else
-               errorToIgnore = SEC_ERROR_UNKNOWN_ISSUER; /* may not have a CA cert */
 
        status = tlsm_verify_cert( ctx->tc_certdb, ctx->tc_certificate, pin_arg,
-                                                          checkSig, certUsage, errorToIgnore );
+                                                          checkSig, certUsage, ctx->tc_warn_only, PR_TRUE );
 
        return status == SECSuccess ? 0 : -1;
 }
@@ -2036,6 +2047,8 @@ tlsm_ctx_free ( tls_ctx *ctx )
        LDAP_MUTEX_UNLOCK( &c->tc_refmutex );
        if ( refcount )
                return;
+
+       LDAP_MUTEX_LOCK( &tlsm_init_mutex );
        if ( c->tc_model )
                PR_Close( c->tc_model );
        if (c->tc_certificate)
@@ -2056,17 +2069,16 @@ tlsm_ctx_free ( tls_ctx *ctx )
        tlsm_free_pem_objs( c );
 #ifdef HAVE_NSS_INITCONTEXT
        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 */
+       LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
 #ifdef LDAP_R_COMPILE
        ldap_pvt_thread_mutex_destroy( &c->tc_refmutex );
 #endif
@@ -2128,6 +2140,12 @@ tlsm_deferred_ctx_init( void *arg )
                return -1;
        }
 
+       if ( SSL_SetPKCS11PinArg(ctx->tc_model, ctx) ) {
+               Debug( LDAP_DEBUG_ANY,
+                               "TLS: could not set pin prompt argument\n", 0, 0, 0);
+               return -1;
+       }
+
        if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_SECURITY, PR_TRUE ) ) {
                Debug( LDAP_DEBUG_ANY,
                       "TLS: could not set secure mode on.\n",
@@ -2305,12 +2323,6 @@ tlsm_deferred_ctx_init( void *arg )
                   since a cert has been specified, assume the client wants to do cert auth
                */
                if ( ctx->tc_certificate ) {
-                       if ( tlsm_authenticate_to_slot( ctx, ctx->tc_certificate->slot ) ) {
-                               Debug( LDAP_DEBUG_ANY, 
-                                      "TLS: error: unable to authenticate to the security device for certificate '%s'\n",
-                                      tlsm_ctx_subject_name(ctx), 0, 0 );
-                               return -1;
-                       }
                        if ( tlsm_clientauth_init( ctx ) ) {
                                Debug( LDAP_DEBUG_ANY, 
                                       "TLS: error: unable to set up client certificate authentication using '%s'\n",
@@ -2330,15 +2342,6 @@ tlsm_deferred_ctx_init( void *arg )
                        return -1;
                }
 
-               /* authenticate to the server's token - this will do nothing
-                  if the key/cert db is not password protected */
-               if ( tlsm_authenticate_to_slot( ctx, ctx->tc_certificate->slot ) ) {
-                       Debug( LDAP_DEBUG_ANY, 
-                                  "TLS: error: unable to authenticate to the security device for certificate '%s'\n",
-                                  tlsm_ctx_subject_name(ctx), 0, 0 );
-                       return -1;
-               }
-
                if (tlsm_find_and_verify_cert_key(ctx)) {
                        Debug( LDAP_DEBUG_ANY, 
                               "TLS: error: unable to find and verify server's cert and key for certificate %s\n",
@@ -2476,7 +2479,9 @@ tlsm_session_new ( tls_ctx * ctx, int is_server )
        int rc;
 
        c->tc_is_server = is_server;
+       LDAP_MUTEX_LOCK( &tlsm_init_mutex );
        status = PR_CallOnceWithArg( &c->tc_callonce, tlsm_deferred_ctx_init, c );
+       LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
        if ( PR_SUCCESS != status ) {
                PRErrorCode err = PR_GetError();
                Debug( LDAP_DEBUG_ANY,