Bug 1409259 - Implement the Symantec distrust algorithm WIP draft
authorJ.C. Jones <jjones@mozilla.com>
Thu, 19 Oct 2017 18:45:24 -0700
changeset 683911 92733b0f43cf1d565f8b13fd5516c70fbca7fb12
parent 683910 55b14e321e3fdb3e1df6fc7589c74a6f611ae72a
child 736765 88559fb52504f7c11c67be8b595aabda0d6d5936
push id85504
push userbmo:jjones@mozilla.com
push dateFri, 20 Oct 2017 14:33:49 +0000
bugs1409259
milestone58.0a1
Bug 1409259 - Implement the Symantec distrust algorithm WIP MozReview-Commit-ID: 5icKHm0xNCG
security/manager/ssl/nsNSSCallbacks.cpp
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -1259,33 +1259,136 @@ DetermineEVAndCTStatusAndSetNewCert(RefP
   }
 
   if (rv == Success) {
     sslStatus->SetCertificateTransparencyInfo(certificateTransparencyInfo);
   }
 }
 
 static nsresult
+SplitTripleCertificateChain(nsIX509CertList* aCertList,
+                            /* out */ nsCOMPtr<nsIX509Cert>& aRoot,
+                            /* out */ nsCOMPtr<nsIX509Cert>& aIntermediate,
+                            /* out */ nsCOMPtr<nsIX509Cert>& aEndEntity) {
+  MOZ_ASSERT(aCertList);
+  if (!aCertList) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsISimpleEnumerator> chainElt;
+  nsresult rv = aCertList->GetEnumerator(getter_AddRefs(chainElt));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Each chain may have multiple certificates.
+  bool hasMoreCerts = false;
+  rv = chainElt->HasMoreElements(&hasMoreCerts);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // The first certificate is the root
+  if (!hasMoreCerts) {
+    return NS_ERROR_FAILURE; // Not a chain
+  }
+
+  {
+    nsCOMPtr<nsISupports> certSupports;
+    rv = chainElt->GetNext(getter_AddRefs(certSupports));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aRoot = do_QueryInterface(certSupports, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = chainElt->HasMoreElements(&hasMoreCerts);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // The next certificate is an intermediate
+  if (!hasMoreCerts) {
+    return NS_ERROR_FAILURE; // Insufficient chain length
+  }
+
+  {
+    nsCOMPtr<nsISupports> certSupports;
+    rv = chainElt->GetNext(getter_AddRefs(certSupports));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aIntermediate = do_QueryInterface(certSupports, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = chainElt->HasMoreElements(&hasMoreCerts);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // The next certificate is an end-entity
+  if (!hasMoreCerts) {
+    return NS_ERROR_FAILURE; // Insufficient chain length
+  }
+
+  {
+    nsCOMPtr<nsISupports> certSupports;
+    rv = chainElt->GetNext(getter_AddRefs(certSupports));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aEndEntity = do_QueryInterface(certSupports, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = chainElt->HasMoreElements(&hasMoreCerts);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!hasMoreCerts) {
+    return NS_OK; // standard length 3 chain
+  }
+
+  return NS_ERROR_FAILURE; // Unexpectedly long chain, not relevant
+}
+
+static nsresult
 IsCertificateDistrustImminent(nsIX509CertList* aCertList,
                               /* out */ bool* aResult) {
   MOZ_ASSERT(aCertList);
   MOZ_ASSERT(aResult);
   if (!aCertList || !aResult) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  // TODO: Implement algorithm below (and add tests)
+  nsCOMPtr<nsIX509Cert> rootCert, intCert, eeCert;
+  nsresult rv = SplitTripleCertificateChain(aCertList, rootCert, intCert,
+                                            eeCert);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // If the root is not one of the Symantec roots, exit false
+  if (!CertDNIsInList(rootCert->GetCert(), RootSymantecDNs)) {
+    *aResult = false;
+    return NS_OK;
+  }
+
+  // If the intermediate is one of the whitelisted intermediates, exit false
+  if (CertDNIsInList(intCert->GetCert(), RootAppleAndGoogleDNs)) {
+    *aResult = false;
+    return NS_OK;
+  }
 
   // If the end entity's notBefore date is not before 2016-06-01, exit false
+  nsCOMPtr<nsIX509CertValidity> validity;
+  eeCert->GetValidity(getter_AddRefs(validity));
 
-  // If the intermediate is one of the whitelisted intermediates, exit false
+  PRTime notBefore;
+  validity->GetNotBefore(&notBefore);
+
+  // PRTime is microseconds since the epoch, whereas JS time is milliseconds.
+  // (new Date("2016-06-01T00:00:00Z")).getTime() * 1000
+  static const PRTime JUNE_1_2016 = 1464739200000000;
+
+  if (notBefore > JUNE_1_2016) {
+    *aResult = false;
+    return NS_OK;
+  }
 
   // Otherwise, this certificate will imminently be distrusted, exit true
-  *aResult = false;
+  *aResult = true;
   return NS_OK;
 }
 
 void HandshakeCallback(PRFileDesc* fd, void* client_data) {
   nsNSSShutDownPreventionLock locker;
   SECStatus rv;
 
   nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;