/src/mozilla-central/security/manager/ssl/nsNSSCertificate.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include "nsNSSCertificate.h" |
7 | | |
8 | | #include "CertVerifier.h" |
9 | | #include "ExtendedValidation.h" |
10 | | #include "NSSCertDBTrustDomain.h" |
11 | | #include "certdb.h" |
12 | | #include "mozilla/Assertions.h" |
13 | | #include "mozilla/Base64.h" |
14 | | #include "mozilla/Casting.h" |
15 | | #include "mozilla/NotNull.h" |
16 | | #include "mozilla/Unused.h" |
17 | | #include "nsArray.h" |
18 | | #include "nsCOMPtr.h" |
19 | | #include "nsICertificateDialogs.h" |
20 | | #include "nsIClassInfoImpl.h" |
21 | | #include "nsIObjectInputStream.h" |
22 | | #include "nsIObjectOutputStream.h" |
23 | | #include "nsISupportsPrimitives.h" |
24 | | #include "nsIURI.h" |
25 | | #include "nsIX509Cert.h" |
26 | | #include "nsNSSASN1Object.h" |
27 | | #include "nsNSSCertHelper.h" |
28 | | #include "nsNSSCertTrust.h" |
29 | | #include "nsNSSCertValidity.h" |
30 | | #include "nsPK11TokenDB.h" |
31 | | #include "nsPKCS12Blob.h" |
32 | | #include "nsProxyRelease.h" |
33 | | #include "nsReadableUtils.h" |
34 | | #include "nsString.h" |
35 | | #include "nsThreadUtils.h" |
36 | | #include "nsUnicharUtils.h" |
37 | | #include "nspr.h" |
38 | | #include "pkix/pkixnss.h" |
39 | | #include "pkix/pkixtypes.h" |
40 | | #include "pkix/Result.h" |
41 | | #include "prerror.h" |
42 | | #include "secasn1.h" |
43 | | #include "secder.h" |
44 | | #include "secerr.h" |
45 | | #include "ssl.h" |
46 | | |
47 | | #ifdef XP_WIN |
48 | | #include <winsock.h> // for htonl |
49 | | #endif |
50 | | |
51 | | using namespace mozilla; |
52 | | using namespace mozilla::psm; |
53 | | |
54 | | extern LazyLogModule gPIPNSSLog; |
55 | | |
56 | | class nsNSSCertListEnumerator : public nsSimpleEnumerator |
57 | | { |
58 | | public: |
59 | | NS_DECL_NSISIMPLEENUMERATOR |
60 | | |
61 | 0 | const nsID& DefaultInterface() override { return NS_GET_IID(nsIX509Cert); } |
62 | | |
63 | | explicit nsNSSCertListEnumerator( |
64 | | const std::vector<UniqueCERTCertificate>& certs); |
65 | | |
66 | | nsNSSCertListEnumerator(const nsNSSCertListEnumerator&) = delete; |
67 | | void operator=(const nsNSSCertListEnumerator&) = delete; |
68 | | |
69 | | private: |
70 | 0 | virtual ~nsNSSCertListEnumerator() = default; |
71 | | |
72 | | std::vector<UniqueCERTCertificate> mCerts; |
73 | | std::vector<UniqueCERTCertificate>::const_iterator mPosition; |
74 | | }; |
75 | | |
76 | | // This is being stored in an uint32_t that can otherwise |
77 | | // only take values from nsIX509Cert's list of cert types. |
78 | | // As nsIX509Cert is frozen, we choose a value not contained |
79 | | // in the list to mean not yet initialized. |
80 | 0 | #define CERT_TYPE_NOT_YET_INITIALIZED (1 << 30) |
81 | | |
82 | | NS_IMPL_ISUPPORTS(nsNSSCertificate, |
83 | | nsIX509Cert, |
84 | | nsISerializable, |
85 | | nsIClassInfo) |
86 | | |
87 | | /*static*/ nsNSSCertificate* |
88 | | nsNSSCertificate::Create(CERTCertificate* cert) |
89 | 0 | { |
90 | 0 | if (cert) |
91 | 0 | return new nsNSSCertificate(cert); |
92 | 0 | else |
93 | 0 | return new nsNSSCertificate(); |
94 | 0 | } |
95 | | |
96 | | nsNSSCertificate* |
97 | | nsNSSCertificate::ConstructFromDER(char* certDER, int derLen) |
98 | 0 | { |
99 | 0 | nsNSSCertificate* newObject = nsNSSCertificate::Create(); |
100 | 0 | if (newObject && !newObject->InitFromDER(certDER, derLen)) { |
101 | 0 | delete newObject; |
102 | 0 | newObject = nullptr; |
103 | 0 | } |
104 | 0 |
|
105 | 0 | return newObject; |
106 | 0 | } |
107 | | |
108 | | bool |
109 | | nsNSSCertificate::InitFromDER(char* certDER, int derLen) |
110 | 0 | { |
111 | 0 | if (!certDER || !derLen) |
112 | 0 | return false; |
113 | 0 | |
114 | 0 | CERTCertificate* aCert = CERT_DecodeCertFromPackage(certDER, derLen); |
115 | 0 |
|
116 | 0 | if (!aCert) |
117 | 0 | return false; |
118 | 0 | |
119 | 0 | if (!aCert->dbhandle) |
120 | 0 | { |
121 | 0 | aCert->dbhandle = CERT_GetDefaultCertDB(); |
122 | 0 | } |
123 | 0 |
|
124 | 0 | mCert.reset(aCert); |
125 | 0 | GetSubjectAltNames(); |
126 | 0 | return true; |
127 | 0 | } |
128 | | |
129 | | nsNSSCertificate::nsNSSCertificate(CERTCertificate* cert) |
130 | | : mCert(nullptr) |
131 | | , mPermDelete(false) |
132 | | , mCertType(CERT_TYPE_NOT_YET_INITIALIZED) |
133 | | , mSubjectAltNames() |
134 | 0 | { |
135 | 0 | if (cert) { |
136 | 0 | mCert.reset(CERT_DupCertificate(cert)); |
137 | 0 | GetSubjectAltNames(); |
138 | 0 | } |
139 | 0 | } |
140 | | |
141 | | nsNSSCertificate::nsNSSCertificate() |
142 | | : mCert(nullptr) |
143 | | , mPermDelete(false) |
144 | | , mCertType(CERT_TYPE_NOT_YET_INITIALIZED) |
145 | | , mSubjectAltNames() |
146 | 0 | { |
147 | 0 | } |
148 | | |
149 | | nsNSSCertificate::~nsNSSCertificate() |
150 | 0 | { |
151 | 0 | if (mPermDelete) { |
152 | 0 | if (mCertType == nsNSSCertificate::USER_CERT) { |
153 | 0 | nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext(); |
154 | 0 | PK11_DeleteTokenCertAndKey(mCert.get(), cxt); |
155 | 0 | } else if (mCert->slot && !PK11_IsReadOnly(mCert->slot)) { |
156 | 0 | // If the list of built-ins does contain a non-removable |
157 | 0 | // copy of this certificate, our call will not remove |
158 | 0 | // the certificate permanently, but rather remove all trust. |
159 | 0 | SEC_DeletePermCertificate(mCert.get()); |
160 | 0 | } |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | | static uint32_t |
165 | | getCertType(CERTCertificate* cert) |
166 | 0 | { |
167 | 0 | nsNSSCertTrust trust(cert->trust); |
168 | 0 | if (cert->nickname && trust.HasAnyUser()) { |
169 | 0 | return nsIX509Cert::USER_CERT; |
170 | 0 | } |
171 | 0 | if (trust.HasAnyCA()) { |
172 | 0 | return nsIX509Cert::CA_CERT; |
173 | 0 | } |
174 | 0 | if (trust.HasPeer(true, false)) { |
175 | 0 | return nsIX509Cert::SERVER_CERT; |
176 | 0 | } |
177 | 0 | if (trust.HasPeer(false, true) && cert->emailAddr) { |
178 | 0 | return nsIX509Cert::EMAIL_CERT; |
179 | 0 | } |
180 | 0 | if (CERT_IsCACert(cert, nullptr)) { |
181 | 0 | return nsIX509Cert::CA_CERT; |
182 | 0 | } |
183 | 0 | if (cert->emailAddr) { |
184 | 0 | return nsIX509Cert::EMAIL_CERT; |
185 | 0 | } |
186 | 0 | return nsIX509Cert::UNKNOWN_CERT; |
187 | 0 | } |
188 | | |
189 | | nsresult |
190 | | nsNSSCertificate::GetCertType(uint32_t* aCertType) |
191 | 0 | { |
192 | 0 | if (mCertType == CERT_TYPE_NOT_YET_INITIALIZED) { |
193 | 0 | // only determine cert type once and cache it |
194 | 0 | mCertType = getCertType(mCert.get()); |
195 | 0 | } |
196 | 0 | *aCertType = mCertType; |
197 | 0 | return NS_OK; |
198 | 0 | } |
199 | | |
200 | | NS_IMETHODIMP |
201 | | nsNSSCertificate::GetIsSelfSigned(bool* aIsSelfSigned) |
202 | 0 | { |
203 | 0 | NS_ENSURE_ARG(aIsSelfSigned); |
204 | 0 |
|
205 | 0 | *aIsSelfSigned = mCert->isRoot; |
206 | 0 | return NS_OK; |
207 | 0 | } |
208 | | |
209 | | NS_IMETHODIMP |
210 | | nsNSSCertificate::GetIsBuiltInRoot(bool* aIsBuiltInRoot) |
211 | 0 | { |
212 | 0 | NS_ENSURE_ARG(aIsBuiltInRoot); |
213 | 0 |
|
214 | 0 | pkix::Result rv = IsCertBuiltInRoot(mCert.get(), *aIsBuiltInRoot); |
215 | 0 | if (rv != pkix::Result::Success) { |
216 | 0 | return NS_ERROR_FAILURE; |
217 | 0 | } |
218 | 0 | return NS_OK; |
219 | 0 | } |
220 | | |
221 | | nsresult |
222 | | nsNSSCertificate::MarkForPermDeletion() |
223 | 0 | { |
224 | 0 | // make sure user is logged in to the token |
225 | 0 | nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext(); |
226 | 0 |
|
227 | 0 | if (mCert->slot && PK11_NeedLogin(mCert->slot) && |
228 | 0 | !PK11_NeedUserInit(mCert->slot) && !PK11_IsInternal(mCert->slot)) { |
229 | 0 | if (SECSuccess != PK11_Authenticate(mCert->slot, true, ctx)) { |
230 | 0 | return NS_ERROR_FAILURE; |
231 | 0 | } |
232 | 0 | } |
233 | 0 | |
234 | 0 | mPermDelete = true; |
235 | 0 | return NS_OK; |
236 | 0 | } |
237 | | |
238 | | /** |
239 | | * Appends a pipnss bundle string to the given string. |
240 | | * |
241 | | * @param bundleKey Key for the string to append. |
242 | | * @param currentText The text to append to, using commas as separators. |
243 | | */ |
244 | | template<size_t N> |
245 | | void |
246 | | AppendBundleStringCommaSeparated(const char (&bundleKey)[N], |
247 | | /*in/out*/ nsAString& currentText) |
248 | 0 | { |
249 | 0 | nsAutoString bundleString; |
250 | 0 | nsresult rv = GetPIPNSSBundleString(bundleKey, bundleString); |
251 | 0 | if (NS_FAILED(rv)) { |
252 | 0 | return; |
253 | 0 | } |
254 | 0 | |
255 | 0 | if (!currentText.IsEmpty()) { |
256 | 0 | currentText.Append(','); |
257 | 0 | } |
258 | 0 | currentText.Append(bundleString); |
259 | 0 | } Unexecuted instantiation: void AppendBundleStringCommaSeparated<15ul>(char const (&) [15ul], nsTSubstring<char16_t>&) Unexecuted instantiation: void AppendBundleStringCommaSeparated<17ul>(char const (&) [17ul], nsTSubstring<char16_t>&) Unexecuted instantiation: void AppendBundleStringCommaSeparated<14ul>(char const (&) [14ul], nsTSubstring<char16_t>&) Unexecuted instantiation: void AppendBundleStringCommaSeparated<13ul>(char const (&) [13ul], nsTSubstring<char16_t>&) Unexecuted instantiation: void AppendBundleStringCommaSeparated<19ul>(char const (&) [19ul], nsTSubstring<char16_t>&) Unexecuted instantiation: void AppendBundleStringCommaSeparated<18ul>(char const (&) [18ul], nsTSubstring<char16_t>&) |
260 | | |
261 | | NS_IMETHODIMP |
262 | | nsNSSCertificate::GetKeyUsages(nsAString& text) |
263 | 0 | { |
264 | 0 | text.Truncate(); |
265 | 0 |
|
266 | 0 | if (!mCert) { |
267 | 0 | return NS_ERROR_FAILURE; |
268 | 0 | } |
269 | 0 | |
270 | 0 | if (!mCert->extensions) { |
271 | 0 | return NS_OK; |
272 | 0 | } |
273 | 0 | |
274 | 0 | ScopedAutoSECItem keyUsageItem; |
275 | 0 | if (CERT_FindKeyUsageExtension(mCert.get(), &keyUsageItem) != SECSuccess) { |
276 | 0 | return PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND ? NS_OK |
277 | 0 | : NS_ERROR_FAILURE; |
278 | 0 | } |
279 | 0 |
|
280 | 0 | unsigned char keyUsage = 0; |
281 | 0 | if (keyUsageItem.len) { |
282 | 0 | keyUsage = keyUsageItem.data[0]; |
283 | 0 | } |
284 | 0 |
|
285 | 0 | if (keyUsage & KU_DIGITAL_SIGNATURE) { |
286 | 0 | AppendBundleStringCommaSeparated("CertDumpKUSign", text); |
287 | 0 | } |
288 | 0 | if (keyUsage & KU_NON_REPUDIATION) { |
289 | 0 | AppendBundleStringCommaSeparated("CertDumpKUNonRep", text); |
290 | 0 | } |
291 | 0 | if (keyUsage & KU_KEY_ENCIPHERMENT) { |
292 | 0 | AppendBundleStringCommaSeparated("CertDumpKUEnc", text); |
293 | 0 | } |
294 | 0 | if (keyUsage & KU_DATA_ENCIPHERMENT) { |
295 | 0 | AppendBundleStringCommaSeparated("CertDumpKUDEnc", text); |
296 | 0 | } |
297 | 0 | if (keyUsage & KU_KEY_AGREEMENT) { |
298 | 0 | AppendBundleStringCommaSeparated("CertDumpKUKA", text); |
299 | 0 | } |
300 | 0 | if (keyUsage & KU_KEY_CERT_SIGN) { |
301 | 0 | AppendBundleStringCommaSeparated("CertDumpKUCertSign", text); |
302 | 0 | } |
303 | 0 | if (keyUsage & KU_CRL_SIGN) { |
304 | 0 | AppendBundleStringCommaSeparated("CertDumpKUCRLSign", text); |
305 | 0 | } |
306 | 0 |
|
307 | 0 | return NS_OK; |
308 | 0 | } |
309 | | |
310 | | NS_IMETHODIMP |
311 | | nsNSSCertificate::GetDbKey(nsACString& aDbKey) |
312 | 0 | { |
313 | 0 | return GetDbKey(mCert, aDbKey); |
314 | 0 | } |
315 | | |
316 | | nsresult |
317 | | nsNSSCertificate::GetDbKey(const UniqueCERTCertificate& cert, nsACString& aDbKey) |
318 | 0 | { |
319 | 0 | static_assert(sizeof(uint64_t) == 8, "type size sanity check"); |
320 | 0 | static_assert(sizeof(uint32_t) == 4, "type size sanity check"); |
321 | 0 | // The format of the key is the base64 encoding of the following: |
322 | 0 | // 4 bytes: {0, 0, 0, 0} (this was intended to be the module ID, but it was |
323 | 0 | // never implemented) |
324 | 0 | // 4 bytes: {0, 0, 0, 0} (this was intended to be the slot ID, but it was |
325 | 0 | // never implemented) |
326 | 0 | // 4 bytes: <serial number length in big-endian order> |
327 | 0 | // 4 bytes: <DER-encoded issuer distinguished name length in big-endian order> |
328 | 0 | // n bytes: <bytes of serial number> |
329 | 0 | // m bytes: <DER-encoded issuer distinguished name> |
330 | 0 | nsAutoCString buf; |
331 | 0 | const char leadingZeroes[] = {0, 0, 0, 0, 0, 0, 0, 0}; |
332 | 0 | buf.Append(leadingZeroes, sizeof(leadingZeroes)); |
333 | 0 | uint32_t serialNumberLen = htonl(cert->serialNumber.len); |
334 | 0 | buf.Append(BitwiseCast<const char*, const uint32_t*>(&serialNumberLen), |
335 | 0 | sizeof(uint32_t)); |
336 | 0 | uint32_t issuerLen = htonl(cert->derIssuer.len); |
337 | 0 | buf.Append(BitwiseCast<const char*, const uint32_t*>(&issuerLen), |
338 | 0 | sizeof(uint32_t)); |
339 | 0 | buf.Append(BitwiseCast<char*, unsigned char*>(cert->serialNumber.data), |
340 | 0 | cert->serialNumber.len); |
341 | 0 | buf.Append(BitwiseCast<char*, unsigned char*>(cert->derIssuer.data), |
342 | 0 | cert->derIssuer.len); |
343 | 0 |
|
344 | 0 | return Base64Encode(buf, aDbKey); |
345 | 0 | } |
346 | | |
347 | | NS_IMETHODIMP |
348 | | nsNSSCertificate::GetDisplayName(nsAString& aDisplayName) |
349 | 0 | { |
350 | 0 | aDisplayName.Truncate(); |
351 | 0 |
|
352 | 0 | MOZ_ASSERT(mCert, "mCert should not be null in GetDisplayName"); |
353 | 0 | if (!mCert) { |
354 | 0 | return NS_ERROR_FAILURE; |
355 | 0 | } |
356 | 0 | |
357 | 0 | UniquePORTString commonName(CERT_GetCommonName(&mCert->subject)); |
358 | 0 | UniquePORTString organizationalUnitName(CERT_GetOrgUnitName(&mCert->subject)); |
359 | 0 | UniquePORTString organizationName(CERT_GetOrgName(&mCert->subject)); |
360 | 0 |
|
361 | 0 | bool isBuiltInRoot; |
362 | 0 | nsresult rv = GetIsBuiltInRoot(&isBuiltInRoot); |
363 | 0 | if (NS_FAILED(rv)) { |
364 | 0 | return rv; |
365 | 0 | } |
366 | 0 | |
367 | 0 | // Only use the nickname for built-in roots where we already have a hard-coded |
368 | 0 | // reasonable display name (unfortunately we have to strip off the leading |
369 | 0 | // slot identifier followed by a ':'). Otherwise, attempt to use the following |
370 | 0 | // in order: |
371 | 0 | // - the common name, if present |
372 | 0 | // - an organizational unit name, if present |
373 | 0 | // - an organization name, if present |
374 | 0 | // - the entire subject distinguished name, if non-empty |
375 | 0 | // - an email address, if one can be found |
376 | 0 | // In the unlikely event that none of these fields are present and non-empty |
377 | 0 | // (the subject really shouldn't be empty), an empty string is returned. |
378 | 0 | nsAutoCString builtInRootNickname; |
379 | 0 | if (isBuiltInRoot) { |
380 | 0 | nsAutoCString fullNickname(mCert->nickname); |
381 | 0 | int32_t index = fullNickname.Find(":"); |
382 | 0 | if (index != kNotFound) { |
383 | 0 | // Substring will gracefully handle the case where index is the last |
384 | 0 | // character in the string (that is, if the nickname is just |
385 | 0 | // "Builtin Object Token:"). In that case, we'll get an empty string. |
386 | 0 | builtInRootNickname = Substring(fullNickname, |
387 | 0 | AssertedCast<uint32_t>(index + 1)); |
388 | 0 | } |
389 | 0 | } |
390 | 0 | const char* nameOptions[] = { |
391 | 0 | builtInRootNickname.get(), |
392 | 0 | commonName.get(), |
393 | 0 | organizationalUnitName.get(), |
394 | 0 | organizationName.get(), |
395 | 0 | mCert->subjectName, |
396 | 0 | mCert->emailAddr |
397 | 0 | }; |
398 | 0 |
|
399 | 0 | for (auto nameOption : nameOptions) { |
400 | 0 | if (nameOption) { |
401 | 0 | size_t len = strlen(nameOption); |
402 | 0 | if (len > 0) { |
403 | 0 | LossyUTF8ToUTF16(nameOption, len, aDisplayName); |
404 | 0 | return NS_OK; |
405 | 0 | } |
406 | 0 | } |
407 | 0 | } |
408 | 0 |
|
409 | 0 | return NS_OK; |
410 | 0 | } |
411 | | |
412 | | NS_IMETHODIMP |
413 | | nsNSSCertificate::GetEmailAddress(nsAString& aEmailAddress) |
414 | 0 | { |
415 | 0 | if (mCert->emailAddr) { |
416 | 0 | CopyUTF8toUTF16(MakeStringSpan(mCert->emailAddr), aEmailAddress); |
417 | 0 | } else { |
418 | 0 | GetPIPNSSBundleString("CertNoEmailAddress", aEmailAddress); |
419 | 0 | } |
420 | 0 | return NS_OK; |
421 | 0 | } |
422 | | |
423 | | NS_IMETHODIMP |
424 | | nsNSSCertificate::GetEmailAddresses(uint32_t* aLength, char16_t*** aAddresses) |
425 | 0 | { |
426 | 0 | NS_ENSURE_ARG(aLength); |
427 | 0 | NS_ENSURE_ARG(aAddresses); |
428 | 0 |
|
429 | 0 | *aLength = 0; |
430 | 0 |
|
431 | 0 | for (const char* aAddr = CERT_GetFirstEmailAddress(mCert.get()); |
432 | 0 | aAddr; |
433 | 0 | aAddr = CERT_GetNextEmailAddress(mCert.get(), aAddr)) { |
434 | 0 | ++(*aLength); |
435 | 0 | } |
436 | 0 |
|
437 | 0 | *aAddresses = (char16_t**) moz_xmalloc(sizeof(char16_t*) * (*aLength)); |
438 | 0 |
|
439 | 0 | uint32_t iAddr = 0; |
440 | 0 | for (const char* aAddr = CERT_GetFirstEmailAddress(mCert.get()); |
441 | 0 | aAddr; |
442 | 0 | aAddr = CERT_GetNextEmailAddress(mCert.get(), aAddr)) { |
443 | 0 | (*aAddresses)[iAddr] = ToNewUnicode(nsDependentCString(aAddr)); |
444 | 0 | iAddr++; |
445 | 0 | } |
446 | 0 |
|
447 | 0 | return NS_OK; |
448 | 0 | } |
449 | | |
450 | | NS_IMETHODIMP |
451 | | nsNSSCertificate::ContainsEmailAddress(const nsAString& aEmailAddress, |
452 | | bool* result) |
453 | 0 | { |
454 | 0 | NS_ENSURE_ARG(result); |
455 | 0 | *result = false; |
456 | 0 |
|
457 | 0 | for (const char* aAddr = CERT_GetFirstEmailAddress(mCert.get()); |
458 | 0 | aAddr; |
459 | 0 | aAddr = CERT_GetNextEmailAddress(mCert.get(), aAddr)) { |
460 | 0 | nsAutoString certAddr; |
461 | 0 | LossyUTF8ToUTF16(aAddr, strlen(aAddr), certAddr); |
462 | 0 | ToLowerCase(certAddr); |
463 | 0 |
|
464 | 0 | nsAutoString testAddr(aEmailAddress); |
465 | 0 | ToLowerCase(testAddr); |
466 | 0 |
|
467 | 0 | if (certAddr == testAddr) { |
468 | 0 | *result = true; |
469 | 0 | break; |
470 | 0 | } |
471 | 0 | } |
472 | 0 |
|
473 | 0 | return NS_OK; |
474 | 0 | } |
475 | | |
476 | | NS_IMETHODIMP |
477 | | nsNSSCertificate::GetCommonName(nsAString& aCommonName) |
478 | 0 | { |
479 | 0 | aCommonName.Truncate(); |
480 | 0 | if (mCert) { |
481 | 0 | UniquePORTString commonName(CERT_GetCommonName(&mCert->subject)); |
482 | 0 | if (commonName) { |
483 | 0 | LossyUTF8ToUTF16(commonName.get(), strlen(commonName.get()), aCommonName); |
484 | 0 | } |
485 | 0 | } |
486 | 0 | return NS_OK; |
487 | 0 | } |
488 | | |
489 | | NS_IMETHODIMP |
490 | | nsNSSCertificate::GetOrganization(nsAString& aOrganization) |
491 | 0 | { |
492 | 0 | aOrganization.Truncate(); |
493 | 0 | if (mCert) { |
494 | 0 | UniquePORTString organization(CERT_GetOrgName(&mCert->subject)); |
495 | 0 | if (organization) { |
496 | 0 | LossyUTF8ToUTF16(organization.get(), strlen(organization.get()), |
497 | 0 | aOrganization); |
498 | 0 | } |
499 | 0 | } |
500 | 0 | return NS_OK; |
501 | 0 | } |
502 | | |
503 | | NS_IMETHODIMP |
504 | | nsNSSCertificate::GetIssuerCommonName(nsAString& aCommonName) |
505 | 0 | { |
506 | 0 | aCommonName.Truncate(); |
507 | 0 | if (mCert) { |
508 | 0 | UniquePORTString commonName(CERT_GetCommonName(&mCert->issuer)); |
509 | 0 | if (commonName) { |
510 | 0 | LossyUTF8ToUTF16(commonName.get(), strlen(commonName.get()), aCommonName); |
511 | 0 | } |
512 | 0 | } |
513 | 0 | return NS_OK; |
514 | 0 | } |
515 | | |
516 | | NS_IMETHODIMP |
517 | | nsNSSCertificate::GetIssuerOrganization(nsAString& aOrganization) |
518 | 0 | { |
519 | 0 | aOrganization.Truncate(); |
520 | 0 | if (mCert) { |
521 | 0 | UniquePORTString organization(CERT_GetOrgName(&mCert->issuer)); |
522 | 0 | if (organization) { |
523 | 0 | LossyUTF8ToUTF16(organization.get(), strlen(organization.get()), |
524 | 0 | aOrganization); |
525 | 0 | } |
526 | 0 | } |
527 | 0 | return NS_OK; |
528 | 0 | } |
529 | | |
530 | | NS_IMETHODIMP |
531 | | nsNSSCertificate::GetIssuerOrganizationUnit(nsAString& aOrganizationUnit) |
532 | 0 | { |
533 | 0 | aOrganizationUnit.Truncate(); |
534 | 0 | if (mCert) { |
535 | 0 | UniquePORTString organizationUnit(CERT_GetOrgUnitName(&mCert->issuer)); |
536 | 0 | if (organizationUnit) { |
537 | 0 | LossyUTF8ToUTF16(organizationUnit.get(), strlen(organizationUnit.get()), |
538 | 0 | aOrganizationUnit); |
539 | 0 | } |
540 | 0 | } |
541 | 0 | return NS_OK; |
542 | 0 | } |
543 | | |
544 | | NS_IMETHODIMP |
545 | | nsNSSCertificate::GetOrganizationalUnit(nsAString& aOrganizationalUnit) |
546 | 0 | { |
547 | 0 | aOrganizationalUnit.Truncate(); |
548 | 0 | if (mCert) { |
549 | 0 | UniquePORTString orgunit(CERT_GetOrgUnitName(&mCert->subject)); |
550 | 0 | if (orgunit) { |
551 | 0 | LossyUTF8ToUTF16(orgunit.get(), strlen(orgunit.get()), |
552 | 0 | aOrganizationalUnit); |
553 | 0 | } |
554 | 0 | } |
555 | 0 | return NS_OK; |
556 | 0 | } |
557 | | |
558 | | NS_IMETHODIMP |
559 | | nsNSSCertificate::GetSubjectName(nsAString& _subjectName) |
560 | 0 | { |
561 | 0 | _subjectName.Truncate(); |
562 | 0 | if (mCert->subjectName) { |
563 | 0 | LossyUTF8ToUTF16(mCert->subjectName, strlen(mCert->subjectName), |
564 | 0 | _subjectName); |
565 | 0 | } |
566 | 0 | return NS_OK; |
567 | 0 | } |
568 | | |
569 | | // Reads dNSName and iPAddress entries encountered in the subject alternative |
570 | | // name extension of the certificate and stores them in mSubjectAltNames. |
571 | | void |
572 | | nsNSSCertificate::GetSubjectAltNames() |
573 | 0 | { |
574 | 0 | mSubjectAltNames.clear(); |
575 | 0 |
|
576 | 0 | ScopedAutoSECItem altNameExtension; |
577 | 0 | SECStatus rv = CERT_FindCertExtension(mCert.get(), |
578 | 0 | SEC_OID_X509_SUBJECT_ALT_NAME, |
579 | 0 | &altNameExtension); |
580 | 0 | if (rv != SECSuccess) { |
581 | 0 | return; |
582 | 0 | } |
583 | 0 | UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); |
584 | 0 | if (!arena) { |
585 | 0 | return; |
586 | 0 | } |
587 | 0 | CERTGeneralName* sanNameList(CERT_DecodeAltNameExtension(arena.get(), |
588 | 0 | &altNameExtension)); |
589 | 0 | if (!sanNameList) { |
590 | 0 | return; |
591 | 0 | } |
592 | 0 | |
593 | 0 | CERTGeneralName* current = sanNameList; |
594 | 0 | do { |
595 | 0 | nsAutoString name; |
596 | 0 | switch (current->type) { |
597 | 0 | case certDNSName: |
598 | 0 | { |
599 | 0 | nsDependentCSubstring nameFromCert(BitwiseCast<char*, unsigned char*>( |
600 | 0 | current->name.other.data), |
601 | 0 | current->name.other.len); |
602 | 0 | // dNSName fields are defined as type IA5String and thus should |
603 | 0 | // be limited to ASCII characters. |
604 | 0 | if (IsASCII(nameFromCert)) { |
605 | 0 | name.Assign(NS_ConvertASCIItoUTF16(nameFromCert)); |
606 | 0 | mSubjectAltNames.push_back(name); |
607 | 0 | } |
608 | 0 | } |
609 | 0 | break; |
610 | 0 |
|
611 | 0 | case certIPAddress: |
612 | 0 | { |
613 | 0 | char buf[INET6_ADDRSTRLEN]; |
614 | 0 | PRNetAddr addr; |
615 | 0 | if (current->name.other.len == 4) { |
616 | 0 | addr.inet.family = PR_AF_INET; |
617 | 0 | memcpy(&addr.inet.ip, current->name.other.data, |
618 | 0 | current->name.other.len); |
619 | 0 | PR_NetAddrToString(&addr, buf, sizeof(buf)); |
620 | 0 | name.AssignASCII(buf); |
621 | 0 | } else if (current->name.other.len == 16) { |
622 | 0 | addr.ipv6.family = PR_AF_INET6; |
623 | 0 | memcpy(&addr.ipv6.ip, current->name.other.data, |
624 | 0 | current->name.other.len); |
625 | 0 | PR_NetAddrToString(&addr, buf, sizeof(buf)); |
626 | 0 | name.AssignASCII(buf); |
627 | 0 | } else { |
628 | 0 | /* invalid IP address */ |
629 | 0 | } |
630 | 0 | if (!name.IsEmpty()) { |
631 | 0 | mSubjectAltNames.push_back(name); |
632 | 0 | } |
633 | 0 | break; |
634 | 0 | } |
635 | 0 |
|
636 | 0 | default: // all other types of names are ignored |
637 | 0 | break; |
638 | 0 | } |
639 | 0 | current = CERT_GetNextGeneralName(current); |
640 | 0 | } while (current != sanNameList); // double linked |
641 | 0 |
|
642 | 0 | return; |
643 | 0 | } |
644 | | |
645 | | NS_IMETHODIMP |
646 | | nsNSSCertificate::GetSubjectAltNames(nsAString& _subjectAltNames) |
647 | 0 | { |
648 | 0 | _subjectAltNames.Truncate(); |
649 | 0 |
|
650 | 0 | for (auto altName : mSubjectAltNames) { |
651 | 0 | if (!_subjectAltNames.IsEmpty()) { |
652 | 0 | _subjectAltNames.Append(','); |
653 | 0 | } |
654 | 0 | _subjectAltNames.Append(altName); |
655 | 0 | } |
656 | 0 | return NS_OK; |
657 | 0 | } |
658 | | |
659 | | NS_IMETHODIMP |
660 | | nsNSSCertificate::GetIssuerName(nsAString& _issuerName) |
661 | 0 | { |
662 | 0 | _issuerName.Truncate(); |
663 | 0 | if (mCert->issuerName) { |
664 | 0 | LossyUTF8ToUTF16(mCert->issuerName, strlen(mCert->issuerName), _issuerName); |
665 | 0 | } |
666 | 0 | return NS_OK; |
667 | 0 | } |
668 | | |
669 | | NS_IMETHODIMP |
670 | | nsNSSCertificate::GetSerialNumber(nsAString& _serialNumber) |
671 | 0 | { |
672 | 0 | _serialNumber.Truncate(); |
673 | 0 | UniquePORTString tmpstr(CERT_Hexify(&mCert->serialNumber, 1)); |
674 | 0 | if (tmpstr) { |
675 | 0 | _serialNumber = NS_ConvertASCIItoUTF16(tmpstr.get()); |
676 | 0 | return NS_OK; |
677 | 0 | } |
678 | 0 | return NS_ERROR_FAILURE; |
679 | 0 | } |
680 | | |
681 | | nsresult |
682 | | nsNSSCertificate::GetCertificateHash(nsAString& aFingerprint, SECOidTag aHashAlg) |
683 | 0 | { |
684 | 0 | aFingerprint.Truncate(); |
685 | 0 | Digest digest; |
686 | 0 | nsresult rv = digest.DigestBuf(aHashAlg, mCert->derCert.data, |
687 | 0 | mCert->derCert.len); |
688 | 0 | if (NS_FAILED(rv)) { |
689 | 0 | return rv; |
690 | 0 | } |
691 | 0 | |
692 | 0 | // CERT_Hexify's second argument is an int that is interpreted as a boolean |
693 | 0 | UniquePORTString fpStr(CERT_Hexify(const_cast<SECItem*>(&digest.get()), 1)); |
694 | 0 | if (!fpStr) { |
695 | 0 | return NS_ERROR_FAILURE; |
696 | 0 | } |
697 | 0 | |
698 | 0 | aFingerprint.AssignASCII(fpStr.get()); |
699 | 0 | return NS_OK; |
700 | 0 | } |
701 | | |
702 | | NS_IMETHODIMP |
703 | | nsNSSCertificate::GetSha256Fingerprint(nsAString& aSha256Fingerprint) |
704 | 0 | { |
705 | 0 | return GetCertificateHash(aSha256Fingerprint, SEC_OID_SHA256); |
706 | 0 | } |
707 | | |
708 | | NS_IMETHODIMP |
709 | | nsNSSCertificate::GetSha1Fingerprint(nsAString& _sha1Fingerprint) |
710 | 0 | { |
711 | 0 | return GetCertificateHash(_sha1Fingerprint, SEC_OID_SHA1); |
712 | 0 | } |
713 | | |
714 | | NS_IMETHODIMP |
715 | | nsNSSCertificate::GetTokenName(nsAString& aTokenName) |
716 | 0 | { |
717 | 0 | MOZ_ASSERT(mCert); |
718 | 0 | if (!mCert) { |
719 | 0 | return NS_ERROR_FAILURE; |
720 | 0 | } |
721 | 0 | UniquePK11SlotInfo internalSlot(PK11_GetInternalSlot()); |
722 | 0 | if (!internalSlot) { |
723 | 0 | return NS_ERROR_FAILURE; |
724 | 0 | } |
725 | 0 | nsCOMPtr<nsIPK11Token> token( |
726 | 0 | new nsPK11Token(mCert->slot ? mCert->slot : internalSlot.get())); |
727 | 0 | nsAutoCString tmp; |
728 | 0 | nsresult rv = token->GetTokenName(tmp); |
729 | 0 | if (NS_FAILED(rv)) { |
730 | 0 | return rv; |
731 | 0 | } |
732 | 0 | aTokenName.Assign(NS_ConvertUTF8toUTF16(tmp)); |
733 | 0 | return NS_OK; |
734 | 0 | } |
735 | | |
736 | | NS_IMETHODIMP |
737 | | nsNSSCertificate::GetSha256SubjectPublicKeyInfoDigest(nsACString& aSha256SPKIDigest) |
738 | 0 | { |
739 | 0 | aSha256SPKIDigest.Truncate(); |
740 | 0 | Digest digest; |
741 | 0 | nsresult rv = digest.DigestBuf(SEC_OID_SHA256, mCert->derPublicKey.data, |
742 | 0 | mCert->derPublicKey.len); |
743 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
744 | 0 | return rv; |
745 | 0 | } |
746 | 0 | rv = Base64Encode(nsDependentCSubstring( |
747 | 0 | BitwiseCast<char*, unsigned char*>(digest.get().data), |
748 | 0 | digest.get().len), |
749 | 0 | aSha256SPKIDigest); |
750 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
751 | 0 | return rv; |
752 | 0 | } |
753 | 0 | return NS_OK; |
754 | 0 | } |
755 | | |
756 | | NS_IMETHODIMP |
757 | | nsNSSCertificate::GetRawDER(uint32_t* aLength, uint8_t** aArray) |
758 | 0 | { |
759 | 0 | if (mCert) { |
760 | 0 | *aArray = (uint8_t*)moz_xmalloc(mCert->derCert.len); |
761 | 0 | memcpy(*aArray, mCert->derCert.data, mCert->derCert.len); |
762 | 0 | *aLength = mCert->derCert.len; |
763 | 0 | return NS_OK; |
764 | 0 | } |
765 | 0 | *aLength = 0; |
766 | 0 | return NS_ERROR_FAILURE; |
767 | 0 | } |
768 | | |
769 | | CERTCertificate* |
770 | | nsNSSCertificate::GetCert() |
771 | 0 | { |
772 | 0 | return (mCert) ? CERT_DupCertificate(mCert.get()) : nullptr; |
773 | 0 | } |
774 | | |
775 | | NS_IMETHODIMP |
776 | | nsNSSCertificate::GetValidity(nsIX509CertValidity** aValidity) |
777 | 0 | { |
778 | 0 | NS_ENSURE_ARG(aValidity); |
779 | 0 |
|
780 | 0 | if (!mCert) { |
781 | 0 | return NS_ERROR_FAILURE; |
782 | 0 | } |
783 | 0 | |
784 | 0 | nsCOMPtr<nsIX509CertValidity> validity = new nsX509CertValidity(mCert); |
785 | 0 | validity.forget(aValidity); |
786 | 0 | return NS_OK; |
787 | 0 | } |
788 | | |
789 | | NS_IMETHODIMP |
790 | | nsNSSCertificate::GetASN1Structure(nsIASN1Object** aASN1Structure) |
791 | 0 | { |
792 | 0 | NS_ENSURE_ARG_POINTER(aASN1Structure); |
793 | 0 | if (!NS_IsMainThread()) { |
794 | 0 | return NS_ERROR_NOT_SAME_THREAD; |
795 | 0 | } |
796 | 0 | return CreateASN1Struct(aASN1Structure); |
797 | 0 | } |
798 | | |
799 | | NS_IMETHODIMP |
800 | | nsNSSCertificate::Equals(nsIX509Cert* other, bool* result) |
801 | 0 | { |
802 | 0 | NS_ENSURE_ARG(other); |
803 | 0 | NS_ENSURE_ARG(result); |
804 | 0 |
|
805 | 0 | UniqueCERTCertificate cert(other->GetCert()); |
806 | 0 | *result = (mCert.get() == cert.get()); |
807 | 0 | return NS_OK; |
808 | 0 | } |
809 | | |
810 | | namespace mozilla { |
811 | | |
812 | | // TODO(bug 1036065): It seems like we only construct CERTCertLists for the |
813 | | // purpose of constructing nsNSSCertLists, so maybe we should change this |
814 | | // function to output an nsNSSCertList instead. |
815 | | SECStatus |
816 | | ConstructCERTCertListFromReversedDERArray( |
817 | | const mozilla::pkix::DERArray& certArray, |
818 | | /*out*/ UniqueCERTCertList& certList) |
819 | 0 | { |
820 | 0 | certList = UniqueCERTCertList(CERT_NewCertList()); |
821 | 0 | if (!certList) { |
822 | 0 | return SECFailure; |
823 | 0 | } |
824 | 0 | |
825 | 0 | CERTCertDBHandle* certDB(CERT_GetDefaultCertDB()); // non-owning |
826 | 0 |
|
827 | 0 | size_t numCerts = certArray.GetLength(); |
828 | 0 | for (size_t i = 0; i < numCerts; ++i) { |
829 | 0 | SECItem certDER(UnsafeMapInputToSECItem(*certArray.GetDER(i))); |
830 | 0 | UniqueCERTCertificate cert(CERT_NewTempCertificate(certDB, &certDER, |
831 | 0 | nullptr, false, true)); |
832 | 0 | if (!cert) { |
833 | 0 | return SECFailure; |
834 | 0 | } |
835 | 0 | // certArray is ordered with the root first, but we want the resulting |
836 | 0 | // certList to have the root last. |
837 | 0 | if (CERT_AddCertToListHead(certList.get(), cert.get()) != SECSuccess) { |
838 | 0 | return SECFailure; |
839 | 0 | } |
840 | 0 | Unused << cert.release(); // cert is now owned by certList. |
841 | 0 | } |
842 | 0 |
|
843 | 0 | return SECSuccess; |
844 | 0 | } |
845 | | |
846 | | } // namespace mozilla |
847 | | |
848 | | NS_IMPL_CLASSINFO(nsNSSCertList, |
849 | | nullptr, |
850 | | // inferred from nsIX509Cert |
851 | | nsIClassInfo::THREADSAFE, |
852 | | NS_X509CERTLIST_CID) |
853 | | |
854 | | NS_IMPL_ISUPPORTS_CI(nsNSSCertList, |
855 | | nsIX509CertList, |
856 | | nsISerializable) |
857 | | |
858 | | nsNSSCertList::nsNSSCertList(UniqueCERTCertList certList) |
859 | 0 | { |
860 | 0 | // Commonly we'll store only 3 certificates. If this is a verified certificate |
861 | 0 | // chain, it may be as many as 8 certificates. If this is a list of all known |
862 | 0 | // certificates, it may be a few hundred. We'll optimize for the common case |
863 | 0 | // (i.e. a verified certificate chain). |
864 | 0 | mCerts.reserve(8); |
865 | 0 | if (certList.get()) { |
866 | 0 | for (CERTCertListNode* node = CERT_LIST_HEAD(certList.get()); |
867 | 0 | !CERT_LIST_END(node, certList.get()); |
868 | 0 | node = CERT_LIST_NEXT(node)) { |
869 | 0 | UniqueCERTCertificate cert(CERT_DupCertificate(node->cert)); |
870 | 0 | mCerts.push_back(std::move(cert)); |
871 | 0 | } |
872 | 0 | } |
873 | 0 | } |
874 | | |
875 | | nsNSSCertList::nsNSSCertList() |
876 | 0 | { |
877 | 0 | // Commonly we'll store only 3 certificates. If this is a verified certificate |
878 | 0 | // chain, it may be as many as 8 certificates. If this is a list of all known |
879 | 0 | // certificates, it may be a few hundred. We'll optimize for the common case |
880 | 0 | // (i.e. a verified certificate chain). |
881 | 0 | mCerts.reserve(8); |
882 | 0 | } |
883 | | |
884 | | // This is the implementation of nsIX509CertList.getCertList(). |
885 | | nsNSSCertList* |
886 | | nsNSSCertList::GetCertList() |
887 | 0 | { |
888 | 0 | return this; |
889 | 0 | } |
890 | | |
891 | | NS_IMETHODIMP |
892 | | nsNSSCertList::AddCert(nsIX509Cert* aCert) |
893 | 0 | { |
894 | 0 | // We need an owning handle when calling nsIX509Cert::GetCert(). |
895 | 0 | UniqueCERTCertificate cert(aCert->GetCert()); |
896 | 0 | if (!cert) { |
897 | 0 | NS_ERROR("Somehow got nullptr for mCertificate in nsNSSCertificate."); |
898 | 0 | return NS_ERROR_FAILURE; |
899 | 0 | } |
900 | 0 | mCerts.push_back(std::move(cert)); |
901 | 0 | return NS_OK; |
902 | 0 | } |
903 | | |
904 | | UniqueCERTCertList |
905 | | nsNSSCertList::DupCertList(const UniqueCERTCertList& certList) |
906 | 0 | { |
907 | 0 | if (!certList) { |
908 | 0 | return nullptr; |
909 | 0 | } |
910 | 0 | |
911 | 0 | UniqueCERTCertList newList(CERT_NewCertList()); |
912 | 0 | if (!newList) { |
913 | 0 | return nullptr; |
914 | 0 | } |
915 | 0 | |
916 | 0 | for (CERTCertListNode* node = CERT_LIST_HEAD(certList); |
917 | 0 | !CERT_LIST_END(node, certList); |
918 | 0 | node = CERT_LIST_NEXT(node)) { |
919 | 0 | UniqueCERTCertificate cert(CERT_DupCertificate(node->cert)); |
920 | 0 | if (!cert) { |
921 | 0 | return nullptr; |
922 | 0 | } |
923 | 0 | |
924 | 0 | if (CERT_AddCertToListTail(newList.get(), cert.get()) != SECSuccess) { |
925 | 0 | return nullptr; |
926 | 0 | } |
927 | 0 | |
928 | 0 | Unused << cert.release(); // Ownership transferred to the cert list. |
929 | 0 | } |
930 | 0 | return newList; |
931 | 0 | } |
932 | | |
933 | | NS_IMETHODIMP |
934 | | nsNSSCertList::AsPKCS7Blob(/*out*/ nsACString& result) |
935 | 0 | { |
936 | 0 | UniqueNSSCMSMessage cmsg(NSS_CMSMessage_Create(nullptr)); |
937 | 0 | if (!cmsg) { |
938 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
939 | 0 | ("nsNSSCertList::AsPKCS7Blob - can't create CMS message")); |
940 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
941 | 0 | } |
942 | 0 |
|
943 | 0 | UniqueNSSCMSSignedData sigd(nullptr); |
944 | 0 | nsresult rv = ForEachCertificateInChain( |
945 | 0 | [&cmsg, &sigd] (nsCOMPtr<nsIX509Cert> aCert, bool /*unused*/, |
946 | 0 | /*out*/ bool& /*unused*/) { |
947 | 0 | // We need an owning handle when calling nsIX509Cert::GetCert(). |
948 | 0 | UniqueCERTCertificate nssCert(aCert->GetCert()); |
949 | 0 | if (!sigd) { |
950 | 0 | sigd.reset(NSS_CMSSignedData_CreateCertsOnly(cmsg.get(), nssCert.get(), |
951 | 0 | false)); |
952 | 0 | if (!sigd) { |
953 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
954 | 0 | ("nsNSSCertList::AsPKCS7Blob - can't create SignedData")); |
955 | 0 | return NS_ERROR_FAILURE; |
956 | 0 | } |
957 | 0 | } else if (NSS_CMSSignedData_AddCertificate(sigd.get(), nssCert.get()) |
958 | 0 | != SECSuccess) { |
959 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
960 | 0 | ("nsNSSCertList::AsPKCS7Blob - can't add cert")); |
961 | 0 | return NS_ERROR_FAILURE; |
962 | 0 | } |
963 | 0 | return NS_OK; |
964 | 0 | }); |
965 | 0 | if (NS_FAILED(rv)) { |
966 | 0 | return rv; |
967 | 0 | } |
968 | 0 | |
969 | 0 | NSSCMSContentInfo* cinfo = NSS_CMSMessage_GetContentInfo(cmsg.get()); |
970 | 0 | if (NSS_CMSContentInfo_SetContent_SignedData(cmsg.get(), cinfo, sigd.get()) |
971 | 0 | != SECSuccess) { |
972 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
973 | 0 | ("nsNSSCertList::AsPKCS7Blob - can't attach SignedData")); |
974 | 0 | return NS_ERROR_FAILURE; |
975 | 0 | } |
976 | 0 | // cmsg owns sigd now. |
977 | 0 | Unused << sigd.release(); |
978 | 0 |
|
979 | 0 | UniquePLArenaPool arena(PORT_NewArena(1024)); |
980 | 0 | if (!arena) { |
981 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
982 | 0 | ("nsNSSCertList::AsPKCS7Blob - out of memory")); |
983 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
984 | 0 | } |
985 | 0 |
|
986 | 0 | SECItem certP7 = { siBuffer, nullptr, 0 }; |
987 | 0 | NSSCMSEncoderContext* ecx = NSS_CMSEncoder_Start(cmsg.get(), nullptr, nullptr, |
988 | 0 | &certP7, arena.get(), nullptr, |
989 | 0 | nullptr, nullptr, nullptr, |
990 | 0 | nullptr, nullptr); |
991 | 0 | if (!ecx) { |
992 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
993 | 0 | ("nsNSSCertList::AsPKCS7Blob - can't create encoder")); |
994 | 0 | return NS_ERROR_FAILURE; |
995 | 0 | } |
996 | 0 |
|
997 | 0 | if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) { |
998 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
999 | 0 | ("nsNSSCertList::AsPKCS7Blob - failed to add encoded data")); |
1000 | 0 | return NS_ERROR_FAILURE; |
1001 | 0 | } |
1002 | 0 |
|
1003 | 0 | result.Assign(nsDependentCSubstring(reinterpret_cast<const char*>(certP7.data), |
1004 | 0 | certP7.len)); |
1005 | 0 | return NS_OK; |
1006 | 0 | } |
1007 | | |
1008 | | NS_IMETHODIMP |
1009 | | nsNSSCertList::Write(nsIObjectOutputStream* aStream) |
1010 | 0 | { |
1011 | 0 | // Write the length of the list |
1012 | 0 | nsresult rv = aStream->Write32(mCerts.size()); |
1013 | 0 |
|
1014 | 0 | // Serialize each certificate |
1015 | 0 | for (const auto& certRef : mCerts) { |
1016 | 0 | nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::Create(certRef.get()); |
1017 | 0 | if (!cert) { |
1018 | 0 | rv = NS_ERROR_OUT_OF_MEMORY; |
1019 | 0 | break; |
1020 | 0 | } |
1021 | 0 | |
1022 | 0 | nsCOMPtr<nsISerializable> serializableCert = do_QueryInterface(cert); |
1023 | 0 | rv = aStream->WriteCompoundObject(serializableCert, NS_GET_IID(nsIX509Cert), true); |
1024 | 0 | if (NS_FAILED(rv)) { |
1025 | 0 | break; |
1026 | 0 | } |
1027 | 0 | } |
1028 | 0 |
|
1029 | 0 | return rv; |
1030 | 0 | } |
1031 | | |
1032 | | NS_IMETHODIMP |
1033 | | nsNSSCertList::Read(nsIObjectInputStream* aStream) |
1034 | 0 | { |
1035 | 0 | uint32_t certListLen; |
1036 | 0 | nsresult rv = aStream->Read32(&certListLen); |
1037 | 0 | if (NS_FAILED(rv)) { |
1038 | 0 | return rv; |
1039 | 0 | } |
1040 | 0 | |
1041 | 0 | for (uint32_t i = 0; i < certListLen; ++i) { |
1042 | 0 | nsCOMPtr<nsISupports> certSupports; |
1043 | 0 | rv = aStream->ReadObject(true, getter_AddRefs(certSupports)); |
1044 | 0 | if (NS_FAILED(rv)) { |
1045 | 0 | break; |
1046 | 0 | } |
1047 | 0 | |
1048 | 0 | nsCOMPtr<nsIX509Cert> cert = do_QueryInterface(certSupports); |
1049 | 0 | rv = AddCert(cert); |
1050 | 0 | if (NS_FAILED(rv)) { |
1051 | 0 | break; |
1052 | 0 | } |
1053 | 0 | } |
1054 | 0 |
|
1055 | 0 | return rv; |
1056 | 0 | } |
1057 | | |
1058 | | NS_IMETHODIMP |
1059 | | nsNSSCertList::GetEnumerator(nsISimpleEnumerator** _retval) |
1060 | 0 | { |
1061 | 0 | nsCOMPtr<nsISimpleEnumerator> enumerator(new nsNSSCertListEnumerator(mCerts)); |
1062 | 0 | enumerator.forget(_retval); |
1063 | 0 | return NS_OK; |
1064 | 0 | } |
1065 | | |
1066 | | NS_IMETHODIMP |
1067 | | nsNSSCertList::Equals(nsIX509CertList* other, bool* result) |
1068 | 0 | { |
1069 | 0 | NS_ENSURE_ARG(result); |
1070 | 0 | *result = true; |
1071 | 0 |
|
1072 | 0 | nsresult rv; |
1073 | 0 |
|
1074 | 0 | nsCOMPtr<nsISimpleEnumerator> selfEnumerator; |
1075 | 0 | rv = GetEnumerator(getter_AddRefs(selfEnumerator)); |
1076 | 0 | if (NS_FAILED(rv)) { |
1077 | 0 | return rv; |
1078 | 0 | } |
1079 | 0 | |
1080 | 0 | nsCOMPtr<nsISimpleEnumerator> otherEnumerator; |
1081 | 0 | rv = other->GetEnumerator(getter_AddRefs(otherEnumerator)); |
1082 | 0 | if (NS_FAILED(rv)) { |
1083 | 0 | return rv; |
1084 | 0 | } |
1085 | 0 | |
1086 | 0 | nsCOMPtr<nsISupports> selfSupports; |
1087 | 0 | nsCOMPtr<nsISupports> otherSupports; |
1088 | 0 | while (NS_SUCCEEDED(selfEnumerator->GetNext(getter_AddRefs(selfSupports)))) { |
1089 | 0 | if (NS_SUCCEEDED(otherEnumerator->GetNext(getter_AddRefs(otherSupports)))) { |
1090 | 0 | nsCOMPtr<nsIX509Cert> selfCert = do_QueryInterface(selfSupports); |
1091 | 0 | nsCOMPtr<nsIX509Cert> otherCert = do_QueryInterface(otherSupports); |
1092 | 0 |
|
1093 | 0 | bool certsEqual = false; |
1094 | 0 | rv = selfCert->Equals(otherCert, &certsEqual); |
1095 | 0 | if (NS_FAILED(rv)) { |
1096 | 0 | return rv; |
1097 | 0 | } |
1098 | 0 | if (!certsEqual) { |
1099 | 0 | *result = false; |
1100 | 0 | break; |
1101 | 0 | } |
1102 | 0 | } else { |
1103 | 0 | // other is shorter than self |
1104 | 0 | *result = false; |
1105 | 0 | break; |
1106 | 0 | } |
1107 | 0 | } |
1108 | 0 |
|
1109 | 0 | // Make sure self is the same length as other |
1110 | 0 | bool otherHasMore = false; |
1111 | 0 | rv = otherEnumerator->HasMoreElements(&otherHasMore); |
1112 | 0 | if (NS_FAILED(rv)) { |
1113 | 0 | return rv; |
1114 | 0 | } |
1115 | 0 | if (otherHasMore) { |
1116 | 0 | *result = false; |
1117 | 0 | } |
1118 | 0 |
|
1119 | 0 | return NS_OK; |
1120 | 0 | } |
1121 | | |
1122 | | nsresult |
1123 | | nsNSSCertList::ForEachCertificateInChain(ForEachCertOperation& aOperation) |
1124 | 0 | { |
1125 | 0 | nsCOMPtr<nsISimpleEnumerator> chainElt; |
1126 | 0 | nsresult rv = GetEnumerator(getter_AddRefs(chainElt)); |
1127 | 0 | if (NS_FAILED(rv)) { |
1128 | 0 | return rv; |
1129 | 0 | } |
1130 | 0 | |
1131 | 0 | // Each chain may have multiple certificates. |
1132 | 0 | bool hasMore = false; |
1133 | 0 | rv = chainElt->HasMoreElements(&hasMore); |
1134 | 0 | if (NS_FAILED(rv)) { |
1135 | 0 | return rv; |
1136 | 0 | } |
1137 | 0 | |
1138 | 0 | if (!hasMore) { |
1139 | 0 | return NS_OK; // Empty lists are fine |
1140 | 0 | } |
1141 | 0 | |
1142 | 0 | do { |
1143 | 0 | nsCOMPtr<nsISupports> certSupports; |
1144 | 0 | rv = chainElt->GetNext(getter_AddRefs(certSupports)); |
1145 | 0 | if (NS_FAILED(rv)) { |
1146 | 0 | return rv; |
1147 | 0 | } |
1148 | 0 | |
1149 | 0 | nsCOMPtr<nsIX509Cert> cert = do_QueryInterface(certSupports, &rv); |
1150 | 0 | if (NS_FAILED(rv)) { |
1151 | 0 | return rv; |
1152 | 0 | } |
1153 | 0 | |
1154 | 0 | rv = chainElt->HasMoreElements(&hasMore); |
1155 | 0 | if (NS_FAILED(rv)) { |
1156 | 0 | return rv; |
1157 | 0 | } |
1158 | 0 | |
1159 | 0 | bool continueLoop = true; |
1160 | 0 | rv = aOperation(cert, hasMore, continueLoop); |
1161 | 0 | if (NS_FAILED(rv) || !continueLoop) { |
1162 | 0 | return rv; |
1163 | 0 | } |
1164 | 0 | } while (hasMore); |
1165 | 0 |
|
1166 | 0 | return NS_OK; |
1167 | 0 | } |
1168 | | |
1169 | | nsresult |
1170 | | nsNSSCertList::SegmentCertificateChain(/* out */ nsCOMPtr<nsIX509Cert>& aRoot, |
1171 | | /* out */ nsCOMPtr<nsIX509CertList>& aIntermediates, |
1172 | | /* out */ nsCOMPtr<nsIX509Cert>& aEndEntity) |
1173 | 0 | { |
1174 | 0 | if (aRoot || aIntermediates || aEndEntity) { |
1175 | 0 | // All passed-in nsCOMPtrs should be empty for the state machine to work |
1176 | 0 | return NS_ERROR_UNEXPECTED; |
1177 | 0 | } |
1178 | 0 | |
1179 | 0 | aIntermediates = new nsNSSCertList(); |
1180 | 0 |
|
1181 | 0 | nsresult rv = ForEachCertificateInChain( |
1182 | 0 | [&aRoot, &aIntermediates, &aEndEntity] (nsCOMPtr<nsIX509Cert> aCert, |
1183 | 0 | bool hasMore, bool& aContinue) { |
1184 | 0 | if (!aEndEntity) { |
1185 | 0 | // This is the end entity |
1186 | 0 | aEndEntity = aCert; |
1187 | 0 | } else if (!hasMore) { |
1188 | 0 | // This is the root |
1189 | 0 | aRoot = aCert; |
1190 | 0 | } else { |
1191 | 0 | // One of (potentially many) intermediates |
1192 | 0 | if (NS_FAILED(aIntermediates->AddCert(aCert))) { |
1193 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1194 | 0 | } |
1195 | 0 | } |
1196 | 0 | |
1197 | 0 | return NS_OK; |
1198 | 0 | }); |
1199 | 0 |
|
1200 | 0 | if (NS_FAILED(rv)) { |
1201 | 0 | return rv; |
1202 | 0 | } |
1203 | 0 | |
1204 | 0 | if (!aRoot || !aEndEntity) { |
1205 | 0 | // No self-signed (or empty) chains allowed |
1206 | 0 | return NS_ERROR_INVALID_ARG; |
1207 | 0 | } |
1208 | 0 | |
1209 | 0 | return NS_OK; |
1210 | 0 | } |
1211 | | |
1212 | | nsresult |
1213 | | nsNSSCertList::GetRootCertificate(/* out */ nsCOMPtr<nsIX509Cert>& aRoot) |
1214 | 0 | { |
1215 | 0 | if (aRoot) { |
1216 | 0 | return NS_ERROR_UNEXPECTED; |
1217 | 0 | } |
1218 | 0 | // If the list is empty, leave aRoot empty. |
1219 | 0 | if (mCerts.size() < 1) { |
1220 | 0 | return NS_OK; |
1221 | 0 | } |
1222 | 0 | const UniqueCERTCertificate& cert = mCerts.back(); |
1223 | 0 | // This increases the refcount on the underlying CERTCertificate, which aRoot |
1224 | 0 | // will own. |
1225 | 0 | aRoot = nsNSSCertificate::Create(cert.get()); |
1226 | 0 | if (!aRoot) { |
1227 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1228 | 0 | } |
1229 | 0 | return NS_OK; |
1230 | 0 | } |
1231 | | |
1232 | | nsNSSCertListEnumerator::nsNSSCertListEnumerator( |
1233 | | const std::vector<UniqueCERTCertificate>& certs) |
1234 | 0 | { |
1235 | 0 | mCerts.reserve(certs.size()); |
1236 | 0 | // Unfortunately we can't just clone the vector because we have to ensure the |
1237 | 0 | // reference counts on the CERTCertificates are accurate. |
1238 | 0 | for (const auto& certRef : certs) { |
1239 | 0 | UniqueCERTCertificate cert(CERT_DupCertificate(certRef.get())); |
1240 | 0 | mCerts.push_back(std::move(cert)); |
1241 | 0 | } |
1242 | 0 | mPosition = mCerts.cbegin(); |
1243 | 0 | } |
1244 | | |
1245 | | NS_IMETHODIMP |
1246 | | nsNSSCertListEnumerator::HasMoreElements(bool* _retval) |
1247 | 0 | { |
1248 | 0 | *_retval = mPosition != mCerts.cend(); |
1249 | 0 | return NS_OK; |
1250 | 0 | } |
1251 | | |
1252 | | NS_IMETHODIMP |
1253 | | nsNSSCertListEnumerator::GetNext(nsISupports** _retval) |
1254 | 0 | { |
1255 | 0 | *_retval = nullptr; |
1256 | 0 | if (mPosition == mCerts.cend()) { |
1257 | 0 | return NS_ERROR_UNEXPECTED; |
1258 | 0 | } |
1259 | 0 | const UniqueCERTCertificate& certRef = *mPosition; |
1260 | 0 | // nsNSSCertificate::Create calls nsNSSCertificate::nsNSSCertificate, which |
1261 | 0 | // increases the reference count on the underlying CERTCertificate itself. |
1262 | 0 | nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(certRef.get()); |
1263 | 0 | if (!nssCert) { |
1264 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1265 | 0 | } |
1266 | 0 | nssCert.forget(_retval); |
1267 | 0 | mPosition++; |
1268 | 0 | return NS_OK; |
1269 | 0 | } |
1270 | | |
1271 | | NS_IMETHODIMP |
1272 | | nsNSSCertificate::Write(nsIObjectOutputStream* aStream) |
1273 | 0 | { |
1274 | 0 | NS_ENSURE_STATE(mCert); |
1275 | 0 | // This field used to be the cached EV status, but it is no longer necessary. |
1276 | 0 | nsresult rv = aStream->Write32(0); |
1277 | 0 | if (NS_FAILED(rv)) { |
1278 | 0 | return rv; |
1279 | 0 | } |
1280 | 0 | rv = aStream->Write32(mCert->derCert.len); |
1281 | 0 | if (NS_FAILED(rv)) { |
1282 | 0 | return rv; |
1283 | 0 | } |
1284 | 0 | return aStream->WriteByteArray(mCert->derCert.data, mCert->derCert.len); |
1285 | 0 | } |
1286 | | |
1287 | | NS_IMETHODIMP |
1288 | | nsNSSCertificate::Read(nsIObjectInputStream* aStream) |
1289 | 0 | { |
1290 | 0 | NS_ENSURE_STATE(!mCert); |
1291 | 0 |
|
1292 | 0 | // This field is no longer used. |
1293 | 0 | uint32_t unusedCachedEVStatus; |
1294 | 0 | nsresult rv = aStream->Read32(&unusedCachedEVStatus); |
1295 | 0 | if (NS_FAILED(rv)) { |
1296 | 0 | return rv; |
1297 | 0 | } |
1298 | 0 | |
1299 | 0 | uint32_t len; |
1300 | 0 | rv = aStream->Read32(&len); |
1301 | 0 | if (NS_FAILED(rv)) { |
1302 | 0 | return rv; |
1303 | 0 | } |
1304 | 0 | |
1305 | 0 | nsCString str; |
1306 | 0 | rv = aStream->ReadBytes(len, getter_Copies(str)); |
1307 | 0 | if (NS_FAILED(rv)) { |
1308 | 0 | return rv; |
1309 | 0 | } |
1310 | 0 | |
1311 | 0 | if (!InitFromDER(const_cast<char*>(str.get()), len)) { |
1312 | 0 | return NS_ERROR_UNEXPECTED; |
1313 | 0 | } |
1314 | 0 | |
1315 | 0 | return NS_OK; |
1316 | 0 | } |
1317 | | |
1318 | | NS_IMETHODIMP |
1319 | | nsNSSCertificate::GetInterfaces(uint32_t* count, nsIID*** array) |
1320 | 0 | { |
1321 | 0 | *count = 0; |
1322 | 0 | *array = nullptr; |
1323 | 0 | return NS_OK; |
1324 | 0 | } |
1325 | | |
1326 | | NS_IMETHODIMP |
1327 | | nsNSSCertificate::GetScriptableHelper(nsIXPCScriptable** _retval) |
1328 | 0 | { |
1329 | 0 | *_retval = nullptr; |
1330 | 0 | return NS_OK; |
1331 | 0 | } |
1332 | | |
1333 | | NS_IMETHODIMP |
1334 | | nsNSSCertificate::GetContractID(nsACString& aContractID) |
1335 | 0 | { |
1336 | 0 | aContractID.SetIsVoid(true); |
1337 | 0 | return NS_OK; |
1338 | 0 | } |
1339 | | |
1340 | | NS_IMETHODIMP |
1341 | | nsNSSCertificate::GetClassDescription(nsACString& aClassDescription) |
1342 | 0 | { |
1343 | 0 | aClassDescription.SetIsVoid(true); |
1344 | 0 | return NS_OK; |
1345 | 0 | } |
1346 | | |
1347 | | NS_IMETHODIMP |
1348 | | nsNSSCertificate::GetClassID(nsCID** aClassID) |
1349 | 0 | { |
1350 | 0 | *aClassID = (nsCID*) moz_xmalloc(sizeof(nsCID)); |
1351 | 0 | return GetClassIDNoAlloc(*aClassID); |
1352 | 0 | } |
1353 | | |
1354 | | NS_IMETHODIMP |
1355 | | nsNSSCertificate::GetFlags(uint32_t* aFlags) |
1356 | 0 | { |
1357 | 0 | *aFlags = nsIClassInfo::THREADSAFE; |
1358 | 0 | return NS_OK; |
1359 | 0 | } |
1360 | | |
1361 | | NS_IMETHODIMP |
1362 | | nsNSSCertificate::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) |
1363 | 0 | { |
1364 | 0 | static NS_DEFINE_CID(kNSSCertificateCID, NS_X509CERT_CID); |
1365 | 0 |
|
1366 | 0 | *aClassIDNoAlloc = kNSSCertificateCID; |
1367 | 0 | return NS_OK; |
1368 | 0 | } |