|
@@ -0,0 +1,317 @@
|
|
|
+# HG changeset patch
|
|
|
+# User Dana Keeler <dkeeler@mozilla.com>
|
|
|
+# Date 1567549154 0
|
|
|
+# Node ID d55c56bc0e576d753ce724593d439e79646d7e48
|
|
|
+# Parent 3edd753ff6a964443d86c9bd11f09490641838bc
|
|
|
+bug 1576755 - split "unknown" bucket in CERT_VALIDATION_SUCCESS_BY_CA (and other _BY_CA probes) r=jcj,kjacobs
|
|
|
+
|
|
|
+The "unknown" bucket is inconsistent and often much higher than we expect. This
|
|
|
+patch splits that bucket by adding the categories "from softoken (cert9.db)",
|
|
|
+"from an external PKCS#11 token", and "imported from the OS via the 'Enterprise
|
|
|
+Roots' feature". Hopefully this will give us more insight into this data.
|
|
|
+
|
|
|
+Differential Revision: https://phabricator.services.mozilla.com/D44065
|
|
|
+
|
|
|
+diff --git a/security/manager/ssl/PublicKeyPinningService.cpp b/security/manager/ssl/PublicKeyPinningService.cpp
|
|
|
+--- a/security/manager/ssl/PublicKeyPinningService.cpp
|
|
|
++++ b/security/manager/ssl/PublicKeyPinningService.cpp
|
|
|
+@@ -309,17 +309,18 @@ static nsresult CheckPinsForHostname(
|
|
|
+ return rv;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Only log telemetry if the certificate list is non-empty.
|
|
|
+ if (rootCert && !enforceTestModeResult && pinningTelemetryInfo) {
|
|
|
+ UniqueCERTCertificate rootCertObj =
|
|
|
+ UniqueCERTCertificate(rootCert.get()->GetCert());
|
|
|
+ if (rootCertObj) {
|
|
|
+- int32_t binNumber = RootCABinNumber(&rootCertObj->derCert);
|
|
|
++ int32_t binNumber =
|
|
|
++ RootCABinNumber(&rootCertObj->derCert, rootCertObj->slot);
|
|
|
+ if (binNumber != ROOT_CERTIFICATE_UNKNOWN) {
|
|
|
+ pinningTelemetryInfo->accumulateForRoot = true;
|
|
|
+ pinningTelemetryInfo->rootBucket = binNumber;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
|
|
|
+diff --git a/security/manager/ssl/RootCertificateTelemetryUtils.cpp b/security/manager/ssl/RootCertificateTelemetryUtils.cpp
|
|
|
+--- a/security/manager/ssl/RootCertificateTelemetryUtils.cpp
|
|
|
++++ b/security/manager/ssl/RootCertificateTelemetryUtils.cpp
|
|
|
+@@ -34,17 +34,21 @@ class BinaryHashSearchArrayComparator {
|
|
|
+ }
|
|
|
+
|
|
|
+ private:
|
|
|
+ const uint8_t* mTarget;
|
|
|
+ };
|
|
|
+
|
|
|
+ // Perform a hash of the provided cert, then search in the RootHashes.inc data
|
|
|
+ // structure for a matching bin number.
|
|
|
+-int32_t RootCABinNumber(const SECItem* cert) {
|
|
|
++// If no matching root is found, this may be a CA from the softoken (cert9.db),
|
|
|
++// it may be a CA from an external PKCS#11 token, or it may be a CA from OS
|
|
|
++// storage (Enterprise Root). The slot argument is used to attempt to determine
|
|
|
++// this. See also the constants in RootCertificateTelemetryUtils.h.
|
|
|
++int32_t RootCABinNumber(const SECItem* cert, PK11SlotInfo* slot) {
|
|
|
+ Digest digest;
|
|
|
+
|
|
|
+ // Compute SHA256 hash of the certificate
|
|
|
+ nsresult rv = digest.DigestBuf(SEC_OID_SHA256, cert->data, cert->len);
|
|
|
+ if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
+ return ROOT_CERTIFICATE_HASH_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -62,25 +66,59 @@ int32_t RootCABinNumber(const SECItem* c
|
|
|
+ static_cast<uint8_t*>(digest.get().data), digest.get().len),
|
|
|
+ &idx)) {
|
|
|
+ MOZ_LOG(gPublicKeyPinningTelemetryLog, LogLevel::Debug,
|
|
|
+ ("pkpinTelem: Telemetry index was %zu, bin is %d\n", idx,
|
|
|
+ ROOT_TABLE[idx].binNumber));
|
|
|
+ return (int32_t)ROOT_TABLE[idx].binNumber;
|
|
|
+ }
|
|
|
+
|
|
|
+- // Didn't match.
|
|
|
++ // Didn't match. It may be from the softoken, an external PKCS#11 token, or
|
|
|
++ // imported from the OS as an "Enterprise Root".
|
|
|
++ UniquePK11SlotInfo softokenSlot(PK11_GetInternalKeySlot());
|
|
|
++ if (!softokenSlot) {
|
|
|
++ return ROOT_CERTIFICATE_UNKNOWN;
|
|
|
++ }
|
|
|
++ if (slot == softokenSlot.get()) {
|
|
|
++ return ROOT_CERTIFICATE_SOFTOKEN;
|
|
|
++ }
|
|
|
++ UniquePK11SlotInfo nssInternalSlot(PK11_GetInternalSlot());
|
|
|
++ UniqueSECMODModule rootsModule(SECMOD_FindModule(kRootModuleName));
|
|
|
++ if (!rootsModule || rootsModule->slotCount != 1) {
|
|
|
++ return ROOT_CERTIFICATE_UNKNOWN;
|
|
|
++ }
|
|
|
++ if (slot && slot != nssInternalSlot.get() && slot != rootsModule->slots[0]) {
|
|
|
++ return ROOT_CERTIFICATE_EXTERNAL_TOKEN;
|
|
|
++ }
|
|
|
++ nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
|
|
|
++ if (!component) {
|
|
|
++ return ROOT_CERTIFICATE_UNKNOWN;
|
|
|
++ }
|
|
|
++ nsTArray<nsTArray<uint8_t>> enterpriseRoots;
|
|
|
++ rv = component->GetEnterpriseRoots(enterpriseRoots);
|
|
|
++ if (NS_FAILED(rv)) {
|
|
|
++ return ROOT_CERTIFICATE_UNKNOWN;
|
|
|
++ }
|
|
|
++ for (const auto& enterpriseRoot : enterpriseRoots) {
|
|
|
++ if (enterpriseRoot.Length() == cert->len &&
|
|
|
++ memcmp(enterpriseRoot.Elements(), cert->data,
|
|
|
++ enterpriseRoot.Length()) == 0) {
|
|
|
++ return ROOT_CERTIFICATE_ENTERPRISE_ROOT;
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ // We have no idea what this is.
|
|
|
+ return ROOT_CERTIFICATE_UNKNOWN;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Attempt to increment the appropriate bin in the provided Telemetry probe ID.
|
|
|
+ // If there was a hash failure, we do nothing.
|
|
|
+ void AccumulateTelemetryForRootCA(mozilla::Telemetry::HistogramID probe,
|
|
|
+ const CERTCertificate* cert) {
|
|
|
+- int32_t binId = RootCABinNumber(&cert->derCert);
|
|
|
++ int32_t binId = RootCABinNumber(&cert->derCert, cert->slot);
|
|
|
+
|
|
|
+ if (binId != ROOT_CERTIFICATE_HASH_FAILURE) {
|
|
|
+ Accumulate(probe, binId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } // namespace psm
|
|
|
+ } // namespace mozilla
|
|
|
+diff --git a/security/manager/ssl/RootCertificateTelemetryUtils.h b/security/manager/ssl/RootCertificateTelemetryUtils.h
|
|
|
+--- a/security/manager/ssl/RootCertificateTelemetryUtils.h
|
|
|
++++ b/security/manager/ssl/RootCertificateTelemetryUtils.h
|
|
|
+@@ -12,19 +12,26 @@
|
|
|
+
|
|
|
+ namespace mozilla {
|
|
|
+ namespace psm {
|
|
|
+
|
|
|
+ // Note: New CAs will show up as UNKNOWN_ROOT until
|
|
|
+ // RootHashes.inc is updated to include them. 0 is reserved by
|
|
|
+ // genRootCAHashes.js for the unknowns.
|
|
|
+ #define ROOT_CERTIFICATE_UNKNOWN 0
|
|
|
++// 1 indicates the CA is not a built-in and comes from the softoken (cert9.db)
|
|
|
++#define ROOT_CERTIFICATE_SOFTOKEN 1
|
|
|
++// 2 indicates the CA is not a bilt-in and comes from an external PKCS#11 token
|
|
|
++#define ROOT_CERTIFICATE_EXTERNAL_TOKEN 2
|
|
|
++// 3 indicates the CA is not a built-in and comes from the OS via the
|
|
|
++// "Enterprise Roots" feature
|
|
|
++#define ROOT_CERTIFICATE_ENTERPRISE_ROOT 3
|
|
|
+ #define ROOT_CERTIFICATE_HASH_FAILURE -1
|
|
|
+
|
|
|
+-int32_t RootCABinNumber(const SECItem* cert);
|
|
|
++int32_t RootCABinNumber(const SECItem* cert, PK11SlotInfo* slot);
|
|
|
+
|
|
|
+ void AccumulateTelemetryForRootCA(mozilla::Telemetry::HistogramID probe,
|
|
|
+ const CERTCertificate* cert);
|
|
|
+
|
|
|
+ } // namespace psm
|
|
|
+ } // namespace mozilla
|
|
|
+
|
|
|
+ #endif // RootCertificateTelemetryUtils_h
|
|
|
+diff --git a/security/manager/ssl/RootHashes.inc b/security/manager/ssl/RootHashes.inc
|
|
|
+--- a/security/manager/ssl/RootHashes.inc
|
|
|
++++ b/security/manager/ssl/RootHashes.inc
|
|
|
+@@ -839,52 +839,34 @@ static const struct CertAuthorityHash RO
|
|
|
+ },
|
|
|
+ {
|
|
|
+ /* QuoVadis_Root_Certification_Authority */
|
|
|
+ { 0xA4, 0x5E, 0xDE, 0x3B, 0xBB, 0xF0, 0x9C, 0x8A, 0xE1, 0x5C, 0x72, 0xEF, 0xC0, 0x72, 0x68, 0xD6,
|
|
|
+ 0x93, 0xA2, 0x1C, 0x99, 0x6F, 0xD5, 0x1E, 0x67, 0xCA, 0x07, 0x94, 0x60, 0xFD, 0x6D, 0x88, 0x73 },
|
|
|
+ 31 /* Bin Number */
|
|
|
+ },
|
|
|
+ {
|
|
|
+- /* GTE_CyberTrust_Global_Root */
|
|
|
+- { 0xA5, 0x31, 0x25, 0x18, 0x8D, 0x21, 0x10, 0xAA, 0x96, 0x4B, 0x02, 0xC7, 0xB7, 0xC6, 0xDA, 0x32,
|
|
|
+- 0x03, 0x17, 0x08, 0x94, 0xE5, 0xFB, 0x71, 0xFF, 0xFB, 0x66, 0x67, 0xD5, 0xE6, 0x81, 0x0A, 0x36 },
|
|
|
+- 1 /* Bin Number */
|
|
|
+- },
|
|
|
+- {
|
|
|
+ /* WellsSecure_Public_Root_Certificate_Authority */
|
|
|
+ { 0xA7, 0x12, 0x72, 0xAE, 0xAA, 0xA3, 0xCF, 0xE8, 0x72, 0x7F, 0x7F, 0xB3, 0x9F, 0x0F, 0xB3, 0xD1,
|
|
|
+ 0xE5, 0x42, 0x6E, 0x90, 0x60, 0xB0, 0x6E, 0xE6, 0xF1, 0x3E, 0x9A, 0x3C, 0x58, 0x33, 0xCD, 0x43 },
|
|
|
+ 65 /* Bin Number */
|
|
|
+ },
|
|
|
+ {
|
|
|
+- /* Thawte_Premium_Server_CA */
|
|
|
+- { 0xAB, 0x70, 0x36, 0x36, 0x5C, 0x71, 0x54, 0xAA, 0x29, 0xC2, 0xC2, 0x9F, 0x5D, 0x41, 0x91, 0x16,
|
|
|
+- 0x3B, 0x16, 0x2A, 0x22, 0x25, 0x01, 0x13, 0x57, 0xD5, 0x6D, 0x07, 0xFF, 0xA7, 0xBC, 0x1F, 0x72 },
|
|
|
+- 3 /* Bin Number */
|
|
|
+- },
|
|
|
+- {
|
|
|
+ /* OU_RSA_Security_2048_V3_O_RSA_Security_Inc */
|
|
|
+ { 0xAF, 0x8B, 0x67, 0x62, 0xA1, 0xE5, 0x28, 0x22, 0x81, 0x61, 0xA9, 0x5D, 0x5C, 0x55, 0x9E, 0xE2,
|
|
|
+ 0x66, 0x27, 0x8F, 0x75, 0xD7, 0x9E, 0x83, 0x01, 0x89, 0xA5, 0x03, 0x50, 0x6A, 0xBD, 0x6B, 0x4C },
|
|
|
+ 19 /* Bin Number */
|
|
|
+ },
|
|
|
+ {
|
|
|
+ /* E_Tugra_Certification_Authority */
|
|
|
+ { 0xB0, 0xBF, 0xD5, 0x2B, 0xB0, 0xD7, 0xD9, 0xBD, 0x92, 0xBF, 0x5D, 0x4D, 0xC1, 0x3D, 0xA2, 0x55,
|
|
|
+ 0xC0, 0x2C, 0x54, 0x2F, 0x37, 0x83, 0x65, 0xEA, 0x89, 0x39, 0x11, 0xF5, 0x5E, 0x55, 0xF2, 0x3C },
|
|
|
+ 141 /* Bin Number */
|
|
|
+ },
|
|
|
+ {
|
|
|
+- /* Thawte_Server_CA */
|
|
|
+- { 0xB4, 0x41, 0x0B, 0x73, 0xE2, 0xE6, 0xEA, 0xCA, 0x47, 0xFB, 0xC4, 0x2F, 0x8F, 0xA4, 0x01, 0x8A,
|
|
|
+- 0xF4, 0x38, 0x1D, 0xC5, 0x4C, 0xFA, 0xA8, 0x44, 0x50, 0x46, 0x1E, 0xED, 0x09, 0x45, 0x4D, 0xE9 },
|
|
|
+- 2 /* Bin Number */
|
|
|
+- },
|
|
|
+- {
|
|
|
+ /* GeoTrust_Primary_Certification_Authority___G3 */
|
|
|
+ { 0xB4, 0x78, 0xB8, 0x12, 0x25, 0x0D, 0xF8, 0x78, 0x63, 0x5C, 0x2A, 0xA7, 0xEC, 0x7D, 0x15, 0x5E,
|
|
|
+ 0xAA, 0x62, 0x5E, 0xE8, 0x29, 0x16, 0xE2, 0xCD, 0x29, 0x43, 0x61, 0x88, 0x6C, 0xD1, 0xFB, 0xD4 },
|
|
|
+ 86 /* Bin Number */
|
|
|
+ },
|
|
|
+ {
|
|
|
+ /* Deutsche_Telekom_Root_CA_2 */
|
|
|
+ { 0xB6, 0x19, 0x1A, 0x50, 0xD0, 0xC3, 0x97, 0x7F, 0x7D, 0xA9, 0x9B, 0xCD, 0xAA, 0xC8, 0x6A, 0x22,
|
|
|
+diff --git a/security/manager/tools/KnownRootHashes.json b/security/manager/tools/KnownRootHashes.json
|
|
|
+--- a/security/manager/tools/KnownRootHashes.json
|
|
|
++++ b/security/manager/tools/KnownRootHashes.json
|
|
|
+@@ -2,34 +2,33 @@
|
|
|
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
+ // file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
+ //
|
|
|
+ //***************************************************************************
|
|
|
+ // This is an automatically generated file. It's used to maintain state for
|
|
|
+ // runs of genRootCAHashes.js; you should never need to manually edit it
|
|
|
+ //***************************************************************************
|
|
|
+
|
|
|
++// Notes:
|
|
|
++// binNumber 1 used to be for "GTE_CyberTrust_Global_Root", but that root was
|
|
|
++// removed from the built-in roots module, so now it is used to indicate that
|
|
|
++// the certificate is not a built-in and was found in the softoken (cert9.db).
|
|
|
++
|
|
|
++// binNumber 2 used to be for "Thawte_Server_CA", but that root was removed from
|
|
|
++// the built-in roots module, so now it is used to indicate that the certificate
|
|
|
++// is not a built-in and was found on an external PKCS#11 token.
|
|
|
++
|
|
|
++// binNumber 3 used to be for "Thawte_Premium_Server_CA", but that root was
|
|
|
++// removed from the built-in roots module, so now it is used to indicate that
|
|
|
++// the certificate is not a built-in and was temporarily imported from the OS as
|
|
|
++// part of the "Enterprise Roots" feature.
|
|
|
++
|
|
|
+ {
|
|
|
+ "roots": [
|
|
|
+ {
|
|
|
+- "label": "GTE_CyberTrust_Global_Root",
|
|
|
+- "binNumber": 1,
|
|
|
+- "sha256Fingerprint": "pTElGI0hEKqWSwLHt8baMgMXCJTl+3H/+2Zn1eaBCjY="
|
|
|
+- },
|
|
|
+- {
|
|
|
+- "label": "Thawte_Server_CA",
|
|
|
+- "binNumber": 2,
|
|
|
+- "sha256Fingerprint": "tEELc+Lm6spH+8Qvj6QBivQ4HcVM+qhEUEYe7QlFTek="
|
|
|
+- },
|
|
|
+- {
|
|
|
+- "label": "Thawte_Premium_Server_CA",
|
|
|
+- "binNumber": 3,
|
|
|
+- "sha256Fingerprint": "q3A2NlxxVKopwsKfXUGRFjsWKiIlARNX1W0H/6e8H3I="
|
|
|
+- },
|
|
|
+- {
|
|
|
+ "label": "OU_Equifax_Secure_Certificate_Authority_O_Equifax_C_US",
|
|
|
+ "binNumber": 4,
|
|
|
+ "sha256Fingerprint": "CCl6QEfbojaAxzHbbjF2U8p4SOG+vToLAXmnB/ks8Xg="
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "label": "OU_VeriSign_Trust_Network_OU___c__1998_VeriSign__Inc____For_authorized_use_only__OU_Class_3_Public_Primary_Certification_Authority___G2_O__VeriSign__Inc___C_US",
|
|
|
+ "binNumber": 5,
|
|
|
+ "sha256Fingerprint": "g848Eiloilk9SF+BlzwPkZVDHto3zF42Qw55x6iIY4s="
|
|
|
+diff --git a/security/manager/tools/genRootCAHashes.js b/security/manager/tools/genRootCAHashes.js
|
|
|
+--- a/security/manager/tools/genRootCAHashes.js
|
|
|
++++ b/security/manager/tools/genRootCAHashes.js
|
|
|
+@@ -16,25 +16,40 @@ ChromeUtils.import("resource://gre/modul
|
|
|
+ ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
|
|
|
+ ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
|
|
|
+ const { CommonUtils } = ChromeUtils.import("resource://services-common/utils.js", {});
|
|
|
+
|
|
|
+ const FILENAME_OUTPUT = "RootHashes.inc";
|
|
|
+ const FILENAME_TRUST_ANCHORS = "KnownRootHashes.json";
|
|
|
+ const ROOT_NOT_ASSIGNED = -1;
|
|
|
+
|
|
|
+-const JSON_HEADER = "// This Source Code Form is subject to the terms of the Mozilla Public\n" +
|
|
|
+-"// License, v. 2.0. If a copy of the MPL was not distributed with this\n" +
|
|
|
+-"// file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n" +
|
|
|
+-"//\n" +
|
|
|
+-"//***************************************************************************\n" +
|
|
|
+-"// This is an automatically generated file. It's used to maintain state for\n" +
|
|
|
+-"// runs of genRootCAHashes.js; you should never need to manually edit it\n" +
|
|
|
+-"//***************************************************************************\n" +
|
|
|
+-"\n";
|
|
|
++const JSON_HEADER = `// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
++// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
++// file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
++//
|
|
|
++//***************************************************************************
|
|
|
++// This is an automatically generated file. It's used to maintain state for
|
|
|
++// runs of genRootCAHashes.js; you should never need to manually edit it
|
|
|
++//***************************************************************************
|
|
|
++
|
|
|
++// Notes:
|
|
|
++// binNumber 1 used to be for "GTE_CyberTrust_Global_Root", but that root was
|
|
|
++// removed from the built-in roots module, so now it is used to indicate that
|
|
|
++// the certificate is not a built-in and was found in the softoken (cert9.db).
|
|
|
++
|
|
|
++// binNumber 2 used to be for "Thawte_Server_CA", but that root was removed from
|
|
|
++// the built-in roots module, so now it is used to indicate that the certificate
|
|
|
++// is not a built-in and was found on an external PKCS#11 token.
|
|
|
++
|
|
|
++// binNumber 3 used to be for "Thawte_Premium_Server_CA", but that root was
|
|
|
++// removed from the built-in roots module, so now it is used to indicate that
|
|
|
++// the certificate is not a built-in and was temporarily imported from the OS as
|
|
|
++// part of the "Enterprise Roots" feature.
|
|
|
++
|
|
|
++`;
|
|
|
+
|
|
|
+ const FILE_HEADER = "/* This Source Code Form is subject to the terms of the Mozilla Public\n" +
|
|
|
+ " * License, v. 2.0. If a copy of the MPL was not distributed with this\n" +
|
|
|
+ " * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n" +
|
|
|
+ "\n" +
|
|
|
+ "/*****************************************************************************/\n" +
|
|
|
+ "/* This is an automatically generated file. If you're not */\n" +
|
|
|
+ "/* RootCertificateTelemetryUtils.cpp, you shouldn't be #including it. */\n" +
|