Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
5 #include "nsUsageArrayHelper.h"
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"
16 #include "nspr.h"
17 #include "secerr.h"
19 using namespace mozilla;
20 using namespace mozilla::psm;
22 #ifdef PR_LOGGING
23 extern PRLogModuleInfo* gPIPNSSLog;
24 #endif
26 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); // XXX? needed?::
28 nsUsageArrayHelper::nsUsageArrayHelper(CERTCertificate *aCert)
29 :mCert(aCert)
30 {
31 nsNSSShutDownPreventionLock locker;
32 defaultcertdb = CERT_GetDefaultCertDB();
33 nssComponent = do_GetService(kNSSComponentCID, &m_rv);
34 }
36 namespace {
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 }
53 } // unnamed namespace
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 }
74 if (isFatalError(previousCheckResult)) {
75 return previousCheckResult;
76 }
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 }
108 SECStatus rv = certVerifier->VerifyCert(mCert, aCertUsage, time,
109 nullptr /*XXX:wincx*/,
110 nullptr /*hostname*/, flags);
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 }
122 PRErrorCode error = PR_GetError();
124 uint32_t result = nsIX509Cert::NOT_VERIFIED_UNKNOWN;
125 verifyFailed(&result, error);
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 }
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));
138 return result;
139 }
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 }
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;
186 NS_ENSURE_TRUE(nssComponent, NS_ERROR_NOT_AVAILABLE);
188 if (outArraySize < max_returned_out_array_size)
189 return NS_ERROR_FAILURE;
191 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
192 NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
194 uint32_t &count = *_count;
195 count = 0;
197 PRTime now = PR_Now();
198 CertVerifier::Flags flags = localOnly ? CertVerifier::FLAG_LOCAL_ONLY : 0;
200 // The following list of checks must be < max_returned_out_array_size
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);
218 if (isFatalError(result) || count == 0) {
219 MOZ_ASSERT(result != nsIX509Cert::VERIFIED_OK);
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 }