bug 1267463 - add a more nuanced subject common name fallback option for prerelease channels r=Cykesiopka,jcj a=lizzard
authorDavid Keeler <dkeeler@mozilla.com>
Mon, 25 Apr 2016 15:55:18 -0700
changeset 332691 9fef9b8ac1983b982fbf016fbccf5a81fed013a4
parent 332690 ca5c6ee18745648f96255a68654757998caeca8d
child 332692 28d54417440fbd929538bb85bedd281f21c05108
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersCykesiopka, jcj, lizzard
bugs1267463
milestone48.0a2
bug 1267463 - add a more nuanced subject common name fallback option for prerelease channels r=Cykesiopka,jcj a=lizzard MozReview-Commit-ID: 1vHXrPAHTRm
netwerk/base/security-prefs.js
security/certverifier/BRNameMatchingPolicy.cpp
security/certverifier/BRNameMatchingPolicy.h
security/manager/ssl/nsNSSComponent.cpp
security/manager/ssl/tests/unit/test_baseline_requirements/moz.build
security/manager/ssl/tests/unit/test_baseline_requirements/no-san-older.pem
security/manager/ssl/tests/unit/test_baseline_requirements/no-san-older.pem.certspec
security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-older.pem
security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-older.pem.certspec
security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js
--- a/netwerk/base/security-prefs.js
+++ b/netwerk/base/security-prefs.js
@@ -54,17 +54,19 @@ pref("security.pki.sha1_enforcement_leve
 
 // security.pki.name_matching_mode controls how the platform matches hostnames
 // to name information in TLS certificates. The possible values are:
 // 0: always fall back to the subject common name if necessary (as in, if the
 //    subject alternative name extension is either not present or does not
 //    contain any DNS names or IP addresses)
 // 1: fall back to the subject common name for certificates valid before 23
 //    August 2016 if necessary
-// 2: only use name information from the subject alternative name extension
+// 2: fall back to the subject common name for certificates valid before 23
+//    August 2015 if necessary
+// 3: only use name information from the subject alternative name extension
 #ifdef RELEASE_BUILD
 pref("security.pki.name_matching_mode", 1);
 #else
 pref("security.pki.name_matching_mode", 2);
 #endif
 
 pref("security.webauth.u2f", false);
 pref("security.webauth.u2f_enable_softtoken", false);
--- a/security/certverifier/BRNameMatchingPolicy.cpp
+++ b/security/certverifier/BRNameMatchingPolicy.cpp
@@ -11,23 +11,30 @@
 using namespace mozilla::psm;
 using namespace mozilla::pkix;
 
 Result
 BRNameMatchingPolicy::FallBackToCommonName(
   Time notBefore,
   /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName)
 {
+  // (new Date("2015-08-23T00:00:00Z")).getTime() / 1000
+  static const Time AUGUST_23_2015 = TimeFromEpochInSeconds(1440288000);
   // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000
   static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400);
   switch (mMode)
   {
     case Mode::Enforce:
       fallBackToCommonName = FallBackToSearchWithinSubject::No;
       break;
+    case Mode::EnforceAfter23August2015:
+      fallBackToCommonName = notBefore > AUGUST_23_2015
+                           ? FallBackToSearchWithinSubject::No
+                           : FallBackToSearchWithinSubject::Yes;
+      break;
     case Mode::EnforceAfter23August2016:
       fallBackToCommonName = notBefore > AUGUST_23_2016
                            ? FallBackToSearchWithinSubject::No
                            : FallBackToSearchWithinSubject::Yes;
       break;
     case Mode::DoNotEnforce:
       fallBackToCommonName = FallBackToSearchWithinSubject::Yes;
       break;
--- a/security/certverifier/BRNameMatchingPolicy.h
+++ b/security/certverifier/BRNameMatchingPolicy.h
@@ -18,28 +18,31 @@ namespace mozilla { namespace psm {
 // subjectAltName extension". Consequently, since any name information present
 // in the common name must be present in the subject alternative name extension,
 // when performing name matching, it should not be necessary to fall back to the
 // common name. Because this consequence has not commonly been enforced, this
 // implementation provides a mechanism to start enforcing it gradually while
 // maintaining some backwards compatibility. If configured with the mode
 // "EnforceAfter23August2016", name matching will only fall back to using the
 // subject common name for certificates where the notBefore field is before 23
-// August 2016.
+// August 2016. Similarly, the mode "EnforceAfter23August2015" is also
+// available. This is to provide a balance between allowing preexisting
+// long-lived certificates and detecting newly-issued problematic certificates.
 // Note that this implementation does not actually directly enforce that if the
 // subject common name is present, its value corresponds to a dNSName or
 // iPAddress entry in the subject alternative name extension.
 
 class BRNameMatchingPolicy : public mozilla::pkix::NameMatchingPolicy
 {
 public:
   enum class Mode {
     DoNotEnforce = 0,
     EnforceAfter23August2016 = 1,
-    Enforce = 2,
+    EnforceAfter23August2015 = 2,
+    Enforce = 3,
   };
 
   explicit BRNameMatchingPolicy(Mode mode)
     : mMode(mode)
   {
   }
 
   virtual mozilla::pkix::Result FallBackToCommonName(
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -1335,16 +1335,17 @@ void nsNSSComponent::setValidationOption
   }
 
   BRNameMatchingPolicy::Mode nameMatchingMode =
     static_cast<BRNameMatchingPolicy::Mode>
       (Preferences::GetInt("security.pki.name_matching_mode",
                            static_cast<int32_t>(BRNameMatchingPolicy::Mode::DoNotEnforce)));
   switch (nameMatchingMode) {
     case BRNameMatchingPolicy::Mode::Enforce:
+    case BRNameMatchingPolicy::Mode::EnforceAfter23August2015:
     case BRNameMatchingPolicy::Mode::EnforceAfter23August2016:
     case BRNameMatchingPolicy::Mode::DoNotEnforce:
       break;
     default:
       nameMatchingMode = BRNameMatchingPolicy::Mode::DoNotEnforce;
       break;
   }
 
--- a/security/manager/ssl/tests/unit/test_baseline_requirements/moz.build
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/moz.build
@@ -3,15 +3,17 @@
 # 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/.
 
 # Temporarily disabled. See bug 1256495.
 #test_certificates = (
 #    'ca.pem',
 #    'no-san-old.pem',
+#    'no-san-older.pem',
 #    'no-san-recent.pem',
 #    'san-contains-no-hostnames-old.pem',
+#    'san-contains-no-hostnames-older.pem',
 #    'san-contains-no-hostnames-recent.pem',
 #)
 #
 #for test_certificate in test_certificates:
 #    GeneratedTestCertificate(test_certificate)
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-older.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICrzCCAZmgAwIBAgIUZ3gdKZRvWFYArMRStT2zAGE6JDQwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTUwNzI0MDAwMDAwWhgPMjAxNjA5MjQwMDAw
+MDBaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo
+4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD
+SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX
+kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx
+owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/
+Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMAsGCSqGSIb3DQEBCwOC
+AQEAn81HiHxHv7Mhq16XWoD6ttEu9sllOHssw452wRdIieM2XcMIi5DPI81XDhqk
+5Zw/pUDfWxNFLq92JM2UArF7RtWICBYqi4UJcdnrMb84rwOVdgt9sDQYdamqj5qF
+90XpTOIA+ZyfAMXxJpJTEC9WN1rc3cK6Epv0+7KqE6FBa5aH8uAYXAb/+eKmtwY/
+ELQWT2jsoplihEw8aJsXTR9M9y2UudxPhqgA7lPpstbOMNuw2xg8oWBnqgZGRGvH
+7Afu2CoxEmYbxXnO6cJ30/pAuVLnVpmJg1n7DLT8is+2fUiOMyuve7KAjxuAo3KT
+XEiTdgDBOOO+gIY532x/jErbRA==
+-----END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-older.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:example.com
+validity:20150724-20160924
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-older.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4TCCAcugAwIBAgIUTePOoF7EmrE46xg5zgyv8RLxEhYwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTUwNzI0MDAwMDAwWhgPMjAxNjA5MjQwMDAw
+MDBaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo
+4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD
+SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX
+kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx
+owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/
+Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABozAwLjAsBgNVHREEJTAj
+pCEwHzEdMBsGA1UECgwURXhhbXBsZSBPcmdhbml6YXRpb24wCwYJKoZIhvcNAQEL
+A4IBAQCdicLQK5tTHkNs58IySxDedV+BqQYxvNZN3cfrqzFdehS4OzVe8hQkwKhJ
+5PpOfkh86KxoBActVL8AtP+mIBZ765qEFPhGEFMfEhqattsO6BluMNTB7ZP8HeDm
+/oCI7Ae5u+Yr5IvjBNSv5fpY7sfDiM4oqXBolJYkR+YTu+GSKYhLZTtiHhG1/Jgw
+yzBzWkfYxIW7EOe2t99krNWpPaf1MyUXO3Pj/7qfURMDeocaAvPGw7tqCZ0nsbOe
+rJvNVZDMaPbOVQKsSSyIrEDfBlQViG10eQIUaURuAY4hJs6AoLvS5b3EaxAQAWtJ
+okbLuSVbj7giOF84hkcFM3hE1FTs
+-----END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-older.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:example.com
+validity:20150724-20160924
+extension:subjectAlternativeName:/O=Example Organization
--- a/security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js
@@ -51,81 +51,138 @@ function run_test() {
   // not in general treated as built-ins, these should all successfully verify
   // regardless of the value of the pref.
   Services.prefs.setIntPref("security.pki.name_matching_mode", 0);
   do_print("current mode: always fall back, root not built-in");
   checkCertOn25August2016(certFromFile("no-san-recent"),
                           PRErrorCodeSuccess);
   checkCertOn25August2016(certFromFile("no-san-old"),
                           PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("no-san-older"),
+                          PRErrorCodeSuccess);
   checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"),
                           PRErrorCodeSuccess);
   checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"),
                           PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("san-contains-no-hostnames-older"),
+                          PRErrorCodeSuccess);
 
   Services.prefs.setIntPref("security.pki.name_matching_mode", 1);
   do_print("current mode: fall back for notBefore < August 23, 2016, root " +
            "not built-in");
   checkCertOn25August2016(certFromFile("no-san-recent"),
                           PRErrorCodeSuccess);
   checkCertOn25August2016(certFromFile("no-san-old"),
                           PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("no-san-older"),
+                          PRErrorCodeSuccess);
   checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"),
                           PRErrorCodeSuccess);
   checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"),
                           PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("san-contains-no-hostnames-older"),
+                          PRErrorCodeSuccess);
 
   Services.prefs.setIntPref("security.pki.name_matching_mode", 2);
+  do_print("current mode: fall back for notBefore < August 23, 2015, root " +
+           "not built-in");
+  checkCertOn25August2016(certFromFile("no-san-recent"),
+                          PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("no-san-old"),
+                          PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("no-san-older"),
+                          PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"),
+                          PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"),
+                          PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("san-contains-no-hostnames-older"),
+                          PRErrorCodeSuccess);
+
+  Services.prefs.setIntPref("security.pki.name_matching_mode", 3);
   do_print("current mode: never fall back, root not built-in");
   checkCertOn25August2016(certFromFile("no-san-recent"),
                           PRErrorCodeSuccess);
   checkCertOn25August2016(certFromFile("no-san-old"),
                           PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("no-san-older"),
+                          PRErrorCodeSuccess);
   checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"),
                           PRErrorCodeSuccess);
   checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"),
                           PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("san-contains-no-hostnames-older"),
+                          PRErrorCodeSuccess);
 
   // In debug builds, we can treat an imported root as a built-in, and thus we
   // can actually test the different values of the pref.
   if (isDebugBuild) {
     let root = certFromFile("ca");
     Services.prefs.setCharPref("security.test.built_in_root_hash",
                                root.sha256Fingerprint);
 
     // Always fall back if necessary.
     Services.prefs.setIntPref("security.pki.name_matching_mode", 0);
     do_print("current mode: always fall back, root built-in");
     checkCertOn25August2016(certFromFile("no-san-recent"),
                             PRErrorCodeSuccess);
     checkCertOn25August2016(certFromFile("no-san-old"),
                             PRErrorCodeSuccess);
+    checkCertOn25August2016(certFromFile("no-san-older"),
+                            PRErrorCodeSuccess);
     checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"),
                             PRErrorCodeSuccess);
     checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"),
                             PRErrorCodeSuccess);
+    checkCertOn25August2016(certFromFile("san-contains-no-hostnames-older"),
+                            PRErrorCodeSuccess);
 
     // Only fall back if notBefore < 23 August 2016
     Services.prefs.setIntPref("security.pki.name_matching_mode", 1);
     do_print("current mode: fall back for notBefore < August 23, 2016, root " +
              "built-in");
     checkCertOn25August2016(certFromFile("no-san-recent"),
                             SSL_ERROR_BAD_CERT_DOMAIN);
     checkCertOn25August2016(certFromFile("no-san-old"),
                             PRErrorCodeSuccess);
+    checkCertOn25August2016(certFromFile("no-san-older"),
+                            PRErrorCodeSuccess);
     checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"),
                             SSL_ERROR_BAD_CERT_DOMAIN);
     checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"),
                             PRErrorCodeSuccess);
+    checkCertOn25August2016(certFromFile("san-contains-no-hostnames-older"),
+                            PRErrorCodeSuccess);
+
+    // Only fall back if notBefore < 23 August 2015
+    Services.prefs.setIntPref("security.pki.name_matching_mode", 2);
+    do_print("current mode: fall back for notBefore < August 23, 2015, root " +
+             "built-in");
+    checkCertOn25August2016(certFromFile("no-san-recent"),
+                            SSL_ERROR_BAD_CERT_DOMAIN);
+    checkCertOn25August2016(certFromFile("no-san-old"),
+                            SSL_ERROR_BAD_CERT_DOMAIN);
+    checkCertOn25August2016(certFromFile("no-san-older"),
+                            PRErrorCodeSuccess);
+    checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"),
+                            SSL_ERROR_BAD_CERT_DOMAIN);
+    checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"),
+                            SSL_ERROR_BAD_CERT_DOMAIN);
+    checkCertOn25August2016(certFromFile("san-contains-no-hostnames-older"),
+                            PRErrorCodeSuccess);
 
     // Never fall back.
-    Services.prefs.setIntPref("security.pki.name_matching_mode", 2);
+    Services.prefs.setIntPref("security.pki.name_matching_mode", 3);
     do_print("current mode: never fall back, root built-in");
     checkCertOn25August2016(certFromFile("no-san-recent"),
                             SSL_ERROR_BAD_CERT_DOMAIN);
     checkCertOn25August2016(certFromFile("no-san-old"),
                             SSL_ERROR_BAD_CERT_DOMAIN);
+    checkCertOn25August2016(certFromFile("no-san-older"),
+                            SSL_ERROR_BAD_CERT_DOMAIN);
     checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"),
                             SSL_ERROR_BAD_CERT_DOMAIN);
     checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"),
                             SSL_ERROR_BAD_CERT_DOMAIN);
+    checkCertOn25August2016(certFromFile("san-contains-no-hostnames-older"),
+                            SSL_ERROR_BAD_CERT_DOMAIN);
   }
 }