1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef TrustOverrides_h
8 #define TrustOverrides_h
9
10 #include "nsNSSCertificate.h"
11 #include "nsNSSCertValidity.h"
12 #include "mozilla/PodOperations.h"
13
14 using namespace mozilla;
15
16 struct DataAndLength {
17 const uint8_t* data;
18 uint32_t len;
19 };
20
21 template <size_t T>
CertDNIsInList(const CERTCertificate * aCert,const DataAndLength (& aDnList)[T])22 static bool CertDNIsInList(const CERTCertificate* aCert,
23 const DataAndLength (&aDnList)[T]) {
24 MOZ_ASSERT(aCert);
25 if (!aCert) {
26 return false;
27 }
28
29 for (auto& dn : aDnList) {
30 if (aCert->derSubject.len == dn.len &&
31 mozilla::PodEqual(aCert->derSubject.data, dn.data, dn.len)) {
32 return true;
33 }
34 }
35 return false;
36 }
37
38 template <size_t T>
CertSPKIIsInList(const CERTCertificate * aCert,const DataAndLength (& aSpkiList)[T])39 static bool CertSPKIIsInList(const CERTCertificate* aCert,
40 const DataAndLength (&aSpkiList)[T]) {
41 MOZ_ASSERT(aCert);
42 if (!aCert) {
43 return false;
44 }
45
46 for (auto& spki : aSpkiList) {
47 if (aCert->derPublicKey.len == spki.len &&
48 mozilla::PodEqual(aCert->derPublicKey.data, spki.data, spki.len)) {
49 return true;
50 }
51 }
52 return false;
53 }
54
55 template <size_t T, size_t R>
CertMatchesStaticData(const CERTCertificate * cert,const unsigned char (& subject)[T],const unsigned char (& spki)[R])56 static bool CertMatchesStaticData(const CERTCertificate* cert,
57 const unsigned char (&subject)[T],
58 const unsigned char (&spki)[R]) {
59 MOZ_ASSERT(cert);
60 if (!cert) {
61 return false;
62 }
63 return cert->derSubject.len == T &&
64 mozilla::PodEqual(cert->derSubject.data, subject, T) &&
65 cert->derPublicKey.len == R &&
66 mozilla::PodEqual(cert->derPublicKey.data, spki, R);
67 }
68
69 // Implements the graduated Symantec distrust algorithm from Bug 1409257.
70 // This accepts a pre-segmented certificate chain (e.g. SegmentCertificateChain)
71 // as |intCerts| and |eeCert|, and pre-assumes that the root has been identified
72 // as being affected (this is to avoid duplicate Segment operations in the
73 // NSSCertDBTrustDomain). If |permitAfterDate| is non-zero, this algorithm
74 // returns "not distrusted" if the NotBefore date of |eeCert| is after
75 // the |permitAfterDate|. Then each of the |intCerts| is evaluated against a
76 // |whitelist| of SPKI entries, and if a match is found, then this returns
77 // "not distrusted." Otherwise, due to the precondition holding, the chain is
78 // "distrusted."
79 template <size_t T>
CheckForSymantecDistrust(const nsCOMPtr<nsIX509CertList> & intCerts,const nsCOMPtr<nsIX509Cert> & eeCert,const PRTime & permitAfterDate,const DataAndLength (& whitelist)[T],bool & isDistrusted)80 static nsresult CheckForSymantecDistrust(
81 const nsCOMPtr<nsIX509CertList>& intCerts,
82 const nsCOMPtr<nsIX509Cert>& eeCert, const PRTime& permitAfterDate,
83 const DataAndLength (&whitelist)[T],
84 /* out */ bool& isDistrusted) {
85 // PRECONDITION: The rootCert is already verified as being one of the
86 // affected Symantec roots
87
88 // Check the preference to see if this is enabled before proceeding.
89 // TODO in Bug 1437754
90
91 isDistrusted = true;
92
93 // Only check the validity period if we're asked
94 if (permitAfterDate > 0) {
95 // We need to verify the age of the end entity
96 nsCOMPtr<nsIX509CertValidity> validity;
97 nsresult rv = eeCert->GetValidity(getter_AddRefs(validity));
98 if (NS_FAILED(rv)) {
99 return rv;
100 }
101
102 PRTime notBefore;
103 rv = validity->GetNotBefore(¬Before);
104 if (NS_FAILED(rv)) {
105 return rv;
106 }
107
108 // If the end entity's notBefore date is after the permitAfter date, this
109 // algorithm doesn't apply, so exit false before we do any iterating.
110 if (notBefore >= permitAfterDate) {
111 isDistrusted = false;
112 return NS_OK;
113 }
114 }
115
116 // Look for one of the intermediates to be in the whitelist
117 RefPtr<nsNSSCertList> intCertList = intCerts->GetCertList();
118
119 return intCertList->ForEachCertificateInChain(
120 [&isDistrusted, &whitelist](nsCOMPtr<nsIX509Cert> aCert, bool aHasMore,
121 /* out */ bool& aContinue) {
122 // We need an owning handle when calling nsIX509Cert::GetCert().
123 UniqueCERTCertificate nssCert(aCert->GetCert());
124 if (CertSPKIIsInList(nssCert.get(), whitelist)) {
125 // In the whitelist
126 isDistrusted = false;
127 aContinue = false;
128 }
129 return NS_OK;
130 });
131 }
132
133 #endif // TrustOverrides_h
134