|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include "nsUsageArrayHelper.h" |
|
6 |
|
7 #include "mozilla/Assertions.h" |
|
8 #include "nsCOMPtr.h" |
|
9 #include "nsIDateTimeFormat.h" |
|
10 #include "nsDateTimeFormatCID.h" |
|
11 #include "nsComponentManagerUtils.h" |
|
12 #include "nsReadableUtils.h" |
|
13 #include "nsNSSCertificate.h" |
|
14 #include "nsServiceManagerUtils.h" |
|
15 |
|
16 #include "nspr.h" |
|
17 #include "secerr.h" |
|
18 |
|
19 using namespace mozilla; |
|
20 using namespace mozilla::psm; |
|
21 |
|
22 #ifdef PR_LOGGING |
|
23 extern PRLogModuleInfo* gPIPNSSLog; |
|
24 #endif |
|
25 |
|
26 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); // XXX? needed?:: |
|
27 |
|
28 nsUsageArrayHelper::nsUsageArrayHelper(CERTCertificate *aCert) |
|
29 :mCert(aCert) |
|
30 { |
|
31 nsNSSShutDownPreventionLock locker; |
|
32 defaultcertdb = CERT_GetDefaultCertDB(); |
|
33 nssComponent = do_GetService(kNSSComponentCID, &m_rv); |
|
34 } |
|
35 |
|
36 namespace { |
|
37 |
|
38 // Some validation errors are non-fatal in that, we should keep checking the |
|
39 // cert for other usages after receiving them; i.e. they are errors that NSS |
|
40 // returns when a certificate isn't valid for a particular usage, but which |
|
41 // don't indicate that the certificate is invalid for ANY usage. Others errors |
|
42 // (e.g. revocation) are fatal, and we should immediately stop validation of |
|
43 // the cert when we encounter them. |
|
44 bool |
|
45 isFatalError(uint32_t checkResult) |
|
46 { |
|
47 return checkResult != nsIX509Cert::VERIFIED_OK && |
|
48 checkResult != nsIX509Cert::USAGE_NOT_ALLOWED && |
|
49 checkResult != nsIX509Cert::ISSUER_NOT_TRUSTED && |
|
50 checkResult != nsIX509Cert::ISSUER_UNKNOWN; |
|
51 } |
|
52 |
|
53 } // unnamed namespace |
|
54 |
|
55 // Validates the certificate for the given usage. If the certificate is valid |
|
56 // for the given usage, aCounter is incremented, a string description of the |
|
57 // usage is appended to outUsages, and nsNSSCertificate::VERIFIED_OK is |
|
58 // returned. Otherwise, if validation failed, one of the other "Constants for |
|
59 // certificate verification results" in nsIX509Cert is returned. |
|
60 uint32_t |
|
61 nsUsageArrayHelper::check(uint32_t previousCheckResult, |
|
62 const char *suffix, |
|
63 CertVerifier * certVerifier, |
|
64 SECCertificateUsage aCertUsage, |
|
65 PRTime time, |
|
66 CertVerifier::Flags flags, |
|
67 uint32_t &aCounter, |
|
68 char16_t **outUsages) |
|
69 { |
|
70 if (!aCertUsage) { |
|
71 MOZ_CRASH("caller should have supplied non-zero aCertUsage"); |
|
72 } |
|
73 |
|
74 if (isFatalError(previousCheckResult)) { |
|
75 return previousCheckResult; |
|
76 } |
|
77 |
|
78 nsAutoCString typestr; |
|
79 switch (aCertUsage) { |
|
80 case certificateUsageSSLClient: |
|
81 typestr = "VerifySSLClient"; |
|
82 break; |
|
83 case certificateUsageSSLServer: |
|
84 typestr = "VerifySSLServer"; |
|
85 break; |
|
86 case certificateUsageEmailSigner: |
|
87 typestr = "VerifyEmailSigner"; |
|
88 break; |
|
89 case certificateUsageEmailRecipient: |
|
90 typestr = "VerifyEmailRecip"; |
|
91 break; |
|
92 case certificateUsageObjectSigner: |
|
93 typestr = "VerifyObjSign"; |
|
94 break; |
|
95 case certificateUsageSSLCA: |
|
96 typestr = "VerifySSLCA"; |
|
97 break; |
|
98 case certificateUsageVerifyCA: |
|
99 typestr = "VerifyCAVerifier"; |
|
100 break; |
|
101 case certificateUsageStatusResponder: |
|
102 typestr = "VerifyStatusResponder"; |
|
103 break; |
|
104 default: |
|
105 MOZ_CRASH("unknown cert usage passed to check()"); |
|
106 } |
|
107 |
|
108 SECStatus rv = certVerifier->VerifyCert(mCert, aCertUsage, time, |
|
109 nullptr /*XXX:wincx*/, |
|
110 nullptr /*hostname*/, flags); |
|
111 |
|
112 if (rv == SECSuccess) { |
|
113 typestr.Append(suffix); |
|
114 nsAutoString verifyDesc; |
|
115 m_rv = nssComponent->GetPIPNSSBundleString(typestr.get(), verifyDesc); |
|
116 if (NS_SUCCEEDED(m_rv)) { |
|
117 outUsages[aCounter++] = ToNewUnicode(verifyDesc); |
|
118 } |
|
119 return nsIX509Cert::VERIFIED_OK; |
|
120 } |
|
121 |
|
122 PRErrorCode error = PR_GetError(); |
|
123 |
|
124 uint32_t result = nsIX509Cert::NOT_VERIFIED_UNKNOWN; |
|
125 verifyFailed(&result, error); |
|
126 |
|
127 // USAGE_NOT_ALLOWED is the weakest non-fatal error; let all other errors |
|
128 // override it. |
|
129 if (result == nsIX509Cert::USAGE_NOT_ALLOWED && |
|
130 previousCheckResult != nsIX509Cert::VERIFIED_OK) { |
|
131 result = previousCheckResult; |
|
132 } |
|
133 |
|
134 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
135 ("error validating certificate for usage %s: %s (%d) -> %ud \n", |
|
136 typestr.get(), PR_ErrorToName(error), (int) error, (int) result)); |
|
137 |
|
138 return result; |
|
139 } |
|
140 |
|
141 |
|
142 // Maps the error code to one of the Constants for certificate verification |
|
143 // results" in nsIX509Cert. |
|
144 void |
|
145 nsUsageArrayHelper::verifyFailed(uint32_t *_verified, int err) |
|
146 { |
|
147 switch (err) { |
|
148 /* For these cases, verify only failed for the particular usage */ |
|
149 case SEC_ERROR_INADEQUATE_KEY_USAGE: |
|
150 case SEC_ERROR_INADEQUATE_CERT_TYPE: |
|
151 case SEC_ERROR_CA_CERT_INVALID: |
|
152 *_verified = nsNSSCertificate::USAGE_NOT_ALLOWED; break; |
|
153 /* These are the cases that have individual error messages */ |
|
154 case SEC_ERROR_REVOKED_CERTIFICATE: |
|
155 *_verified = nsNSSCertificate::CERT_REVOKED; break; |
|
156 case SEC_ERROR_EXPIRED_CERTIFICATE: |
|
157 *_verified = nsNSSCertificate::CERT_EXPIRED; break; |
|
158 case SEC_ERROR_UNTRUSTED_CERT: |
|
159 *_verified = nsNSSCertificate::CERT_NOT_TRUSTED; break; |
|
160 case SEC_ERROR_UNTRUSTED_ISSUER: |
|
161 *_verified = nsNSSCertificate::ISSUER_NOT_TRUSTED; break; |
|
162 case SEC_ERROR_UNKNOWN_ISSUER: |
|
163 *_verified = nsNSSCertificate::ISSUER_UNKNOWN; break; |
|
164 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: |
|
165 // XXX are there other error for this? |
|
166 *_verified = nsNSSCertificate::INVALID_CA; break; |
|
167 case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: |
|
168 *_verified = nsNSSCertificate::SIGNATURE_ALGORITHM_DISABLED; break; |
|
169 default: |
|
170 *_verified = nsNSSCertificate::NOT_VERIFIED_UNKNOWN; break; |
|
171 } |
|
172 } |
|
173 |
|
174 nsresult |
|
175 nsUsageArrayHelper::GetUsagesArray(const char *suffix, |
|
176 bool localOnly, |
|
177 uint32_t outArraySize, |
|
178 uint32_t *_verified, |
|
179 uint32_t *_count, |
|
180 char16_t **outUsages) |
|
181 { |
|
182 nsNSSShutDownPreventionLock locker; |
|
183 if (NS_FAILED(m_rv)) |
|
184 return m_rv; |
|
185 |
|
186 NS_ENSURE_TRUE(nssComponent, NS_ERROR_NOT_AVAILABLE); |
|
187 |
|
188 if (outArraySize < max_returned_out_array_size) |
|
189 return NS_ERROR_FAILURE; |
|
190 |
|
191 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier()); |
|
192 NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED); |
|
193 |
|
194 uint32_t &count = *_count; |
|
195 count = 0; |
|
196 |
|
197 PRTime now = PR_Now(); |
|
198 CertVerifier::Flags flags = localOnly ? CertVerifier::FLAG_LOCAL_ONLY : 0; |
|
199 |
|
200 // The following list of checks must be < max_returned_out_array_size |
|
201 |
|
202 uint32_t result; |
|
203 result = check(nsIX509Cert::VERIFIED_OK, suffix, certVerifier, |
|
204 certificateUsageSSLClient, now, flags, count, outUsages); |
|
205 result = check(result, suffix, certVerifier, |
|
206 certificateUsageSSLServer, now, flags, count, outUsages); |
|
207 result = check(result, suffix, certVerifier, |
|
208 certificateUsageEmailSigner, now, flags, count, outUsages); |
|
209 result = check(result, suffix, certVerifier, |
|
210 certificateUsageEmailRecipient, now, flags, count, outUsages); |
|
211 result = check(result, suffix, certVerifier, |
|
212 certificateUsageObjectSigner, now, flags, count, outUsages); |
|
213 result = check(result, suffix, certVerifier, |
|
214 certificateUsageSSLCA, now, flags, count, outUsages); |
|
215 result = check(result, suffix, certVerifier, |
|
216 certificateUsageStatusResponder, now, flags, count, outUsages); |
|
217 |
|
218 if (isFatalError(result) || count == 0) { |
|
219 MOZ_ASSERT(result != nsIX509Cert::VERIFIED_OK); |
|
220 |
|
221 // Clear the output usage strings in the case where we encountered a fatal |
|
222 // error after we already successfully validated the cert for some usages. |
|
223 for (uint32_t i = 0; i < count; ++i) { |
|
224 delete outUsages[i]; |
|
225 outUsages[i] = nullptr; |
|
226 } |
|
227 count = 0; |
|
228 *_verified = result; |
|
229 } else { |
|
230 *_verified = nsNSSCertificate::VERIFIED_OK; |
|
231 } |
|
232 return NS_OK; |
|
233 } |