]> 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 540d531f30104c2d69c40a302952ac98bd006a71..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,
@@ -1832,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))
@@ -1841,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;
 }
@@ -2119,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",
@@ -2296,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",
@@ -2321,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",