Bug 682927 - Dis-trust DigiNotar root certificate, part 3; r=kaie,dveditz
authorBrian Smith <bsmith@mozilla.com>
Tue, 30 Aug 2011 10:33:04 -0400
changeset 76203 fcca99426576
parent 76202 365bd397fa9b
child 76204 005bce677a00
child 105175 2be20e5f492d
push id21085
push usereakhgari@mozilla.com
push date2011-08-30 14:33 +0000
treeherdermozilla-central@fcca99426576 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskaie, dveditz
bugs682927
milestone9.0a1
Bug 682927 - Dis-trust DigiNotar root certificate, part 3; r=kaie,dveditz
security/manager/ssl/src/nsNSSCallbacks.cpp
security/manager/ssl/src/nsNSSCallbacks.h
security/manager/ssl/src/nsNSSIOLayer.cpp
security/manager/ssl/src/nsNSSIOLayer.h
--- a/security/manager/ssl/src/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/src/nsNSSCallbacks.cpp
@@ -1033,16 +1033,63 @@ static struct nsSerialBinaryBlacklistEnt
   { 16, "\x3e\x75\xce\xd4\x6b\x69\x30\x21\x21\x88\x30\xae\x86\xa8\x2a\x71" },
   { 17, "\x00\xe9\x02\x8b\x95\x78\xe4\x15\xdc\x1a\x71\x0a\x2b\x88\x15\x44\x47" },
   { 17, "\x00\xd7\x55\x8f\xda\xf5\xf1\x10\x5b\xb2\x13\x28\x2b\x70\x77\x29\xa3" },
   { 16, "\x04\x7e\xcb\xe9\xfc\xa5\x5f\x7b\xd0\x9e\xae\x36\xe1\x0c\xae\x1e" },
   { 17, "\x00\xf5\xc8\x6a\xf3\x61\x62\xf1\x3a\x64\xf5\x4f\x6d\xc9\x58\x7c\x06" },
   { 0, 0 } // end marker
 };
 
+// Bug 682927: Do not trust any DigiNotar-issued certificates.
+// We do this check after normal certificate validation because we do not
+// want to override a "revoked" OCSP response.
+PRErrorCode
+PSM_SSL_BlacklistDigiNotar(CERTCertificate * serverCert,
+                           CERTCertList * serverCertChain)
+{
+  PRBool isDigiNotarIssuedCert = PR_FALSE;
+
+  for (CERTCertListNode *node = CERT_LIST_HEAD(serverCertChain);
+       !CERT_LIST_END(node, serverCertChain);
+       node = CERT_LIST_NEXT(node)) {
+    if (!node->cert->issuerName)
+      continue;
+
+    if (strstr(node->cert->issuerName, "CN=DigiNotar")) {
+      isDigiNotarIssuedCert = PR_TRUE;
+      // Do not let the user override the error if the cert was
+      // chained from the "DigiNotar Root CA" cert and the cert was issued
+      // within the time window in which we think the mis-issuance(s) occurred.
+      if (strstr(node->cert->issuerName, "CN=DigiNotar Root CA")) {
+        PRTime cutoff = 0, notBefore = 0, notAfter = 0;
+        PRStatus status = PR_ParseTimeString("01-JUL-2011 00:00", PR_TRUE, &cutoff);
+        NS_ASSERTION(status == PR_SUCCESS, "PR_ParseTimeString failed");
+        if (status != PR_SUCCESS ||
+           CERT_GetCertTimes(serverCert, &notBefore, &notAfter) != SECSuccess ||
+           notBefore >= cutoff) {
+          return SEC_ERROR_REVOKED_CERTIFICATE;
+        }
+      }
+    }
+
+    // By request of the Dutch government
+    if (!strcmp(node->cert->issuerName,
+                "CN=Staat der Nederlanden Root CA,O=Staat der Nederlanden,C=NL") &&
+        CERT_LIST_END(CERT_LIST_NEXT(node), serverCertChain)) {
+      return 0;
+    }
+  }
+
+  if (isDigiNotarIssuedCert)
+    return SEC_ERROR_UNTRUSTED_ISSUER; // user can override this
+  else
+    return 0; // No DigiNotor cert => carry on as normal
+}
+
+
 SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd,
                                               PRBool checksig, PRBool isServer) {
   nsNSSShutDownPreventionLock locker;
 
   CERTCertificate *serverCert = SSL_PeerCertificate(fd);
   CERTCertificateCleaner serverCertCleaner(serverCert);
 
   if (serverCert && 
@@ -1078,40 +1125,54 @@ SECStatus PR_CALLBACK AuthCertificateCal
 
       if (server_cert_comparison_len == locked_cert_comparison_len &&
           !memcmp(server_cert_comparison_start, locked_cert_comparison_start, locked_cert_comparison_len)) {
         PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
         return SECFailure;
       }
     }
   }
-  
+
   SECStatus rv = PSM_SSL_PKIX_AuthCertificate(fd, serverCert, checksig, isServer);
 
   // We want to remember the CA certs in the temp db, so that the application can find the
   // complete chain at any time it might need it.
   // But we keep only those CA certs in the temp db, that we didn't already know.
 
   if (serverCert) {
     nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
     nsRefPtr<nsSSLStatus> status = infoObject->SSLStatus();
     nsRefPtr<nsNSSCertificate> nsc;
 
     if (!status || !status->mServerCert) {
       nsc = nsNSSCertificate::Create(serverCert);
     }
 
-    if (SECSuccess == rv) {
+    CERTCertList *certList = nsnull;
+    if (rv == SECSuccess) {
+      certList = CERT_GetCertChainFromCert(serverCert, PR_Now(), certUsageSSLCA);
+      if (!certList) {
+        rv = SECFailure;
+      } else {
+        PRErrorCode blacklistErrorCode = PSM_SSL_BlacklistDigiNotar(serverCert,
+                                                                    certList);
+        if (blacklistErrorCode != 0) {
+          infoObject->SetCertIssuerBlacklisted();
+          PORT_SetError(blacklistErrorCode);
+          rv = SECFailure;
+        }
+      }
+    }
+
+    if (rv == SECSuccess) {
       if (nsc) {
         PRBool dummyIsEV;
         nsc->GetIsExtendedValidation(&dummyIsEV); // the nsc object will cache the status
       }
     
-      CERTCertList *certList = CERT_GetCertChainFromCert(serverCert, PR_Now(), certUsageSSLCA);
-
       nsCOMPtr<nsINSSComponent> nssComponent;
       
       for (CERTCertListNode *node = CERT_LIST_HEAD(certList);
            !CERT_LIST_END(node, certList);
            node = CERT_LIST_NEXT(node)) {
 
         if (node->cert->slot) {
           // This cert was found on a token, no need to remember it in the temp db.
@@ -1137,16 +1198,19 @@ SECStatus PR_CALLBACK AuthCertificateCal
             PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE, 
                             nickname, PR_FALSE);
             PK11_FreeSlot(slot);
           }
         }
         PR_FREEIF(nickname);
       }
 
+    }
+
+    if (certList) {
       CERT_DestroyCertList(certList);
     }
 
     // The connection may get terminated, for example, if the server requires
     // a client cert. Let's provide a minimal SSLStatus
     // to the caller that contains at least the cert and its status.
     if (!status) {
       status = new nsSSLStatus();
--- a/security/manager/ssl/src/nsNSSCallbacks.h
+++ b/security/manager/ssl/src/nsNSSCallbacks.h
@@ -50,16 +50,19 @@
 
 char* PR_CALLBACK
 PK11PasswordPrompt(PK11SlotInfo *slot, PRBool retry, void* arg);
 
 void PR_CALLBACK HandshakeCallback(PRFileDesc *fd, void *client_data);
 SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd,
                                               PRBool checksig, PRBool isServer);
 
+PRErrorCode PSM_SSL_BlacklistDigiNotar(CERTCertificate * serverCert,
+                                       CERTCertList * serverCertChain);
+
 SECStatus RegisterMyOCSPAIAInfoCallback();
 SECStatus UnregisterMyOCSPAIAInfoCallback();
 
 class nsHTTPListener : public nsIStreamLoaderObserver
 {
 private:
   // For XPCOM implementations that are not a base class for some other
   // class, it is good practice to make the destructor non-virtual and
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -221,17 +221,18 @@ nsNSSSocketInfo::nsNSSSocketInfo()
     mForSTARTTLS(PR_FALSE),
     mHandshakePending(PR_TRUE),
     mCanceled(PR_FALSE),
     mHasCleartextPhase(PR_FALSE),
     mHandshakeInProgress(PR_FALSE),
     mAllowTLSIntoleranceTimeout(PR_TRUE),
     mRememberClientAuthCertificate(PR_FALSE),
     mHandshakeStartTime(0),
-    mPort(0)
+    mPort(0),
+    mIsCertIssuerBlacklisted(PR_FALSE)
 {
   mThreadData = new nsSSLSocketThreadData;
 }
 
 nsNSSSocketInfo::~nsNSSSocketInfo()
 {
   delete mThreadData;
 
@@ -3435,16 +3436,20 @@ nsNSSBadCertHandler(void *arg, PRFileDes
       cvout[0].value.pointer.log = verify_log;
       cvout[1].type = cert_po_end;
 
       srv = CERT_PKIXVerifyCert(peerCert, certificateUsageSSLServer,
                                 survivingParams->GetRawPointerForNSS(),
                                 cvout, (void*)infoObject);
     }
 
+    if (infoObject->IsCertIssuerBlacklisted()) {
+      collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
+    }
+
     // We ignore the result code of the cert verification.
     // Either it is a failure, which is expected, and we'll process the
     //                         verify log below.
     // Or it is a success, then a domain mismatch is the only 
     //                     possible failure. 
 
     CERTVerifyLogNode *i_node;
     for (i_node = verify_log->head; i_node; i_node = i_node->next)
--- a/security/manager/ssl/src/nsNSSIOLayer.h
+++ b/security/manager/ssl/src/nsNSSIOLayer.h
@@ -197,16 +197,22 @@ public:
 
   /* Set SSL Status values */
   nsresult SetSSLStatus(nsSSLStatus *aSSLStatus);
   nsSSLStatus* SSLStatus() { return mSSLStatus; }
   PRBool hasCertErrors();
   
   PRStatus CloseSocketAndDestroy();
   
+  PRBool IsCertIssuerBlacklisted() const {
+    return mIsCertIssuerBlacklisted;
+  }
+  void SetCertIssuerBlacklisted() {
+    mIsCertIssuerBlacklisted = PR_TRUE;
+  }
 protected:
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
   PRFileDesc* mFd;
   nsCOMPtr<nsIX509Cert> mCert;
   nsCOMPtr<nsIX509Cert> mPreviousCert; // DocShellDependent
   enum { 
     blocking_state_unknown, is_nonblocking_socket, is_blocking_socket 
   } mBlockingState;
@@ -224,16 +230,17 @@ protected:
   PRPackedBool mCanceled;
   PRPackedBool mHasCleartextPhase;
   PRPackedBool mHandshakeInProgress;
   PRPackedBool mAllowTLSIntoleranceTimeout;
   PRPackedBool mRememberClientAuthCertificate;
   PRIntervalTime mHandshakeStartTime;
   PRInt32 mPort;
   nsXPIDLCString mHostName;
+  PRErrorCode mIsCertIssuerBlacklisted;
 
   /* SSL Status */
   nsRefPtr<nsSSLStatus> mSSLStatus;
 
   nsresult ActivateSSL();
 
   nsSSLSocketThreadData *mThreadData;