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
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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;