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 "nsCMS.h"
7
8 #include "CertVerifier.h"
9 #include "CryptoTask.h"
10 #include "ScopedNSSTypes.h"
11 #include "cms.h"
12 #include "mozilla/Logging.h"
13 #include "mozilla/RefPtr.h"
14 #include "nsDependentSubstring.h"
15 #include "nsICryptoHash.h"
16 #include "nsISupports.h"
17 #include "nsIX509CertDB.h"
18 #include "nsNSSCertificate.h"
19 #include "nsNSSComponent.h"
20 #include "nsNSSHelper.h"
21 #include "nsServiceManagerUtils.h"
22 #include "mozpkix/Result.h"
23 #include "mozpkix/pkixtypes.h"
24 #include "sechash.h"
25 #include "secerr.h"
26 #include "smime.h"
27 #include "mozilla/StaticMutex.h"
28
29 using namespace mozilla;
30 using namespace mozilla::psm;
31 using namespace mozilla::pkix;
32
33 static mozilla::LazyLogModule gCMSLog("CMS");
34
NS_IMPL_ISUPPORTS(nsCMSMessage,nsICMSMessage)35 NS_IMPL_ISUPPORTS(nsCMSMessage, nsICMSMessage)
36
37 nsCMSMessage::nsCMSMessage() { m_cmsMsg = nullptr; }
nsCMSMessage(NSSCMSMessage * aCMSMsg)38 nsCMSMessage::nsCMSMessage(NSSCMSMessage* aCMSMsg) { m_cmsMsg = aCMSMsg; }
39
~nsCMSMessage()40 nsCMSMessage::~nsCMSMessage() { destructorSafeDestroyNSSReference(); }
41
Init()42 nsresult nsCMSMessage::Init() {
43 nsresult rv;
44 nsCOMPtr<nsISupports> nssInitialized =
45 do_GetService("@mozilla.org/psm;1", &rv);
46 return rv;
47 }
48
destructorSafeDestroyNSSReference()49 void nsCMSMessage::destructorSafeDestroyNSSReference() {
50 if (m_cmsMsg) {
51 NSS_CMSMessage_Destroy(m_cmsMsg);
52 }
53 }
54
VerifySignature()55 NS_IMETHODIMP nsCMSMessage::VerifySignature() {
56 return CommonVerifySignature({}, 0);
57 }
58
GetTopLevelSignerInfo()59 NSSCMSSignerInfo* nsCMSMessage::GetTopLevelSignerInfo() {
60 if (!m_cmsMsg) return nullptr;
61
62 if (!NSS_CMSMessage_IsSigned(m_cmsMsg)) return nullptr;
63
64 NSSCMSContentInfo* cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0);
65 if (!cinfo) return nullptr;
66
67 NSSCMSSignedData* sigd =
68 (NSSCMSSignedData*)NSS_CMSContentInfo_GetContent(cinfo);
69 if (!sigd) return nullptr;
70
71 PR_ASSERT(NSS_CMSSignedData_SignerInfoCount(sigd) > 0);
72 return NSS_CMSSignedData_GetSignerInfo(sigd, 0);
73 }
74
GetSignerEmailAddress(char ** aEmail)75 NS_IMETHODIMP nsCMSMessage::GetSignerEmailAddress(char** aEmail) {
76 MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSMessage::GetSignerEmailAddress"));
77 NS_ENSURE_ARG(aEmail);
78
79 NSSCMSSignerInfo* si = GetTopLevelSignerInfo();
80 if (!si) return NS_ERROR_FAILURE;
81
82 *aEmail = NSS_CMSSignerInfo_GetSignerEmailAddress(si);
83 return NS_OK;
84 }
85
GetSignerCommonName(char ** aName)86 NS_IMETHODIMP nsCMSMessage::GetSignerCommonName(char** aName) {
87 MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSMessage::GetSignerCommonName"));
88 NS_ENSURE_ARG(aName);
89
90 NSSCMSSignerInfo* si = GetTopLevelSignerInfo();
91 if (!si) return NS_ERROR_FAILURE;
92
93 *aName = NSS_CMSSignerInfo_GetSignerCommonName(si);
94 return NS_OK;
95 }
96
ContentIsEncrypted(bool * isEncrypted)97 NS_IMETHODIMP nsCMSMessage::ContentIsEncrypted(bool* isEncrypted) {
98 MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSMessage::ContentIsEncrypted"));
99 NS_ENSURE_ARG(isEncrypted);
100
101 if (!m_cmsMsg) return NS_ERROR_FAILURE;
102
103 *isEncrypted = NSS_CMSMessage_IsEncrypted(m_cmsMsg);
104
105 return NS_OK;
106 }
107
ContentIsSigned(bool * isSigned)108 NS_IMETHODIMP nsCMSMessage::ContentIsSigned(bool* isSigned) {
109 MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSMessage::ContentIsSigned"));
110 NS_ENSURE_ARG(isSigned);
111
112 if (!m_cmsMsg) return NS_ERROR_FAILURE;
113
114 *isSigned = NSS_CMSMessage_IsSigned(m_cmsMsg);
115
116 return NS_OK;
117 }
118
GetSignerCert(nsIX509Cert ** scert)119 NS_IMETHODIMP nsCMSMessage::GetSignerCert(nsIX509Cert** scert) {
120 NSSCMSSignerInfo* si = GetTopLevelSignerInfo();
121 if (!si) return NS_ERROR_FAILURE;
122
123 nsCOMPtr<nsIX509Cert> cert;
124 if (si->cert) {
125 MOZ_LOG(gCMSLog, LogLevel::Debug,
126 ("nsCMSMessage::GetSignerCert got signer cert"));
127
128 nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
129 nsTArray<uint8_t> certBytes;
130 certBytes.AppendElements(si->cert->derCert.data, si->cert->derCert.len);
131 nsresult rv = certdb->ConstructX509(certBytes, getter_AddRefs(cert));
132 NS_ENSURE_SUCCESS(rv, rv);
133 } else {
134 MOZ_LOG(gCMSLog, LogLevel::Debug,
135 ("nsCMSMessage::GetSignerCert no signer cert, do we have a cert "
136 "list? %s",
137 (si->certList ? "yes" : "no")));
138
139 *scert = nullptr;
140 }
141
142 cert.forget(scert);
143
144 return NS_OK;
145 }
146
GetEncryptionCert(nsIX509Cert **)147 NS_IMETHODIMP nsCMSMessage::GetEncryptionCert(nsIX509Cert**) {
148 return NS_ERROR_NOT_IMPLEMENTED;
149 }
150
151 NS_IMETHODIMP
VerifyDetachedSignature(const nsTArray<uint8_t> & aDigestData,int16_t aDigestType)152 nsCMSMessage::VerifyDetachedSignature(const nsTArray<uint8_t>& aDigestData,
153 int16_t aDigestType) {
154 if (aDigestData.IsEmpty()) return NS_ERROR_FAILURE;
155
156 return CommonVerifySignature(aDigestData, aDigestType);
157 }
158
159 // This is an exact copy of NSS_CMSArray_Count from NSS' cmsarray.c,
160 // temporarily necessary, see below for for justification.
myNSS_CMSArray_Count(void ** array)161 static int myNSS_CMSArray_Count(void** array) {
162 int n = 0;
163
164 if (array == NULL) return 0;
165
166 while (*array++ != NULL) n++;
167
168 return n;
169 }
170
171 // This is an exact copy of NSS_CMSArray_Add from NSS' cmsarray.c,
172 // temporarily necessary, see below for for justification.
myNSS_CMSArray_Add(PLArenaPool * poolp,void *** array,void * obj)173 static SECStatus myNSS_CMSArray_Add(PLArenaPool* poolp, void*** array,
174 void* obj) {
175 void** p;
176 int n;
177 void** dest;
178
179 PORT_Assert(array != NULL);
180 if (array == NULL) return SECFailure;
181
182 if (*array == NULL) {
183 dest = (void**)PORT_ArenaAlloc(poolp, 2 * sizeof(void*));
184 n = 0;
185 } else {
186 n = 0;
187 p = *array;
188 while (*p++) n++;
189 dest = (void**)PORT_ArenaGrow(poolp, *array, (n + 1) * sizeof(void*),
190 (n + 2) * sizeof(void*));
191 }
192
193 if (dest == NULL) return SECFailure;
194
195 dest[n] = obj;
196 dest[n + 1] = NULL;
197 *array = dest;
198 return SECSuccess;
199 }
200
201 // This is an exact copy of NSS_CMSArray_Add from NSS' cmsarray.c,
202 // temporarily necessary, see below for for justification.
myNSS_CMSSignedData_AddTempCertificate(NSSCMSSignedData * sigd,CERTCertificate * cert)203 static SECStatus myNSS_CMSSignedData_AddTempCertificate(NSSCMSSignedData* sigd,
204 CERTCertificate* cert) {
205 CERTCertificate* c;
206 SECStatus rv;
207
208 if (!sigd || !cert) {
209 PORT_SetError(SEC_ERROR_INVALID_ARGS);
210 return SECFailure;
211 }
212
213 c = CERT_DupCertificate(cert);
214 rv = myNSS_CMSArray_Add(sigd->cmsg->poolp, (void***)&(sigd->tempCerts),
215 (void*)c);
216 return rv;
217 }
218
219 typedef SECStatus (*extraVerificationOnCertFn)(CERTCertificate* cert,
220 SECCertUsage certusage);
221
myExtraVerificationOnCert(CERTCertificate * cert,SECCertUsage certusage)222 static SECStatus myExtraVerificationOnCert(CERTCertificate* cert,
223 SECCertUsage certusage) {
224 RefPtr<SharedCertVerifier> certVerifier;
225 certVerifier = GetDefaultCertVerifier();
226 if (!certVerifier) {
227 return SECFailure;
228 }
229
230 SECCertificateUsage usageForPkix;
231
232 switch (certusage) {
233 case certUsageEmailSigner:
234 usageForPkix = certificateUsageEmailSigner;
235 break;
236 case certUsageEmailRecipient:
237 usageForPkix = certificateUsageEmailRecipient;
238 break;
239 default:
240 return SECFailure;
241 }
242
243 UniqueCERTCertList builtChain;
244 mozilla::pkix::Result result = certVerifier->VerifyCert(
245 cert, usageForPkix, Now(), nullptr /*XXX pinarg*/, nullptr /*hostname*/,
246 builtChain,
247 // Only local checks can run on the main thread.
248 CertVerifier::FLAG_LOCAL_ONLY);
249 if (result != mozilla::pkix::Success) {
250 return SECFailure;
251 }
252
253 return SECSuccess;
254 }
255
256 // This is a temporary copy of NSS_CMSSignedData_ImportCerts, which
257 // performs additional verifications prior to import.
258 // The copy is almost identical to the original.
259 //
260 // The ONLY DIFFERENCE is the addition of parameter extraVerifyFn,
261 // and the call to it - plus a non-null check.
262 //
263 // NSS should add this or a similar API in the future,
264 // and then these temporary functions should be removed, including
265 // the ones above. Request is tracked in bugzilla 1738592.
myNSS_CMSSignedData_ImportCerts(NSSCMSSignedData * sigd,CERTCertDBHandle * certdb,SECCertUsage certusage,PRBool keepcerts,extraVerificationOnCertFn extraVerifyFn)266 static SECStatus myNSS_CMSSignedData_ImportCerts(
267 NSSCMSSignedData* sigd, CERTCertDBHandle* certdb, SECCertUsage certusage,
268 PRBool keepcerts, extraVerificationOnCertFn extraVerifyFn) {
269 int certcount;
270 CERTCertificate** certArray = NULL;
271 CERTCertList* certList = NULL;
272 CERTCertListNode* node;
273 SECStatus rv;
274 SECItem** rawArray;
275 int i;
276 PRTime now;
277
278 if (!sigd) {
279 PORT_SetError(SEC_ERROR_INVALID_ARGS);
280 return SECFailure;
281 }
282
283 certcount = myNSS_CMSArray_Count((void**)sigd->rawCerts);
284
285 /* get the certs in the temp DB */
286 rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts,
287 &certArray, PR_FALSE, PR_FALSE, NULL);
288 if (rv != SECSuccess) {
289 goto loser;
290 }
291
292 /* save the certs so they don't get destroyed */
293 for (i = 0; i < certcount; i++) {
294 CERTCertificate* cert = certArray[i];
295 if (cert) myNSS_CMSSignedData_AddTempCertificate(sigd, cert);
296 }
297
298 if (!keepcerts) {
299 goto done;
300 }
301
302 /* build a CertList for filtering */
303 certList = CERT_NewCertList();
304 if (certList == NULL) {
305 rv = SECFailure;
306 goto loser;
307 }
308 for (i = 0; i < certcount; i++) {
309 CERTCertificate* cert = certArray[i];
310 if (cert) cert = CERT_DupCertificate(cert);
311 if (cert) CERT_AddCertToListTail(certList, cert);
312 }
313
314 /* filter out the certs we don't want */
315 rv = CERT_FilterCertListByUsage(certList, certusage, PR_FALSE);
316 if (rv != SECSuccess) {
317 goto loser;
318 }
319
320 /* go down the remaining list of certs and verify that they have
321 * valid chains, then import them.
322 */
323 now = PR_Now();
324 for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
325 node = CERT_LIST_NEXT(node)) {
326 CERTCertificateList* certChain;
327
328 if (!node->cert) {
329 continue;
330 }
331
332 if (extraVerifyFn) {
333 if ((*extraVerifyFn)(node->cert, certusage) != SECSuccess) {
334 continue;
335 }
336 }
337
338 if (CERT_VerifyCert(certdb, node->cert, PR_TRUE, certusage, now, NULL,
339 NULL) != SECSuccess) {
340 continue;
341 }
342
343 certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE);
344 if (!certChain) {
345 continue;
346 }
347
348 /*
349 * CertChain returns an array of SECItems, import expects an array of
350 * SECItem pointers. Create the SECItem Pointers from the array of
351 * SECItems.
352 */
353 rawArray = (SECItem**)PORT_Alloc(certChain->len * sizeof(SECItem*));
354 if (!rawArray) {
355 CERT_DestroyCertificateList(certChain);
356 continue;
357 }
358 for (i = 0; i < certChain->len; i++) {
359 rawArray[i] = &certChain->certs[i];
360 }
361 (void)CERT_ImportCerts(certdb, certusage, certChain->len, rawArray, NULL,
362 keepcerts, PR_FALSE, NULL);
363 PORT_Free(rawArray);
364 CERT_DestroyCertificateList(certChain);
365 }
366
367 rv = SECSuccess;
368
369 /* XXX CRL handling */
370
371 done:
372 if (sigd->signerInfos != NULL) {
373 /* fill in all signerinfo's certs */
374 for (i = 0; sigd->signerInfos[i] != NULL; i++)
375 (void)NSS_CMSSignerInfo_GetSigningCertificate(sigd->signerInfos[i],
376 certdb);
377 }
378
379 loser:
380 /* now free everything */
381 if (certArray) {
382 CERT_DestroyCertArray(certArray, certcount);
383 }
384 if (certList) {
385 CERT_DestroyCertList(certList);
386 }
387
388 return rv;
389 }
390
CommonVerifySignature(const nsTArray<uint8_t> & aDigestData,int16_t aDigestType)391 nsresult nsCMSMessage::CommonVerifySignature(
392 const nsTArray<uint8_t>& aDigestData, int16_t aDigestType) {
393 MOZ_LOG(gCMSLog, LogLevel::Debug,
394 ("nsCMSMessage::CommonVerifySignature, content level count %d",
395 NSS_CMSMessage_ContentLevelCount(m_cmsMsg)));
396 NSSCMSContentInfo* cinfo = nullptr;
397 NSSCMSSignedData* sigd = nullptr;
398 NSSCMSSignerInfo* si;
399 int32_t nsigners;
400 nsresult rv = NS_ERROR_FAILURE;
401
402 if (!NSS_CMSMessage_IsSigned(m_cmsMsg)) {
403 MOZ_LOG(gCMSLog, LogLevel::Debug,
404 ("nsCMSMessage::CommonVerifySignature - not signed"));
405 return NS_ERROR_CMS_VERIFY_NOT_SIGNED;
406 }
407
408 cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0);
409 if (cinfo) {
410 switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
411 case SEC_OID_PKCS7_SIGNED_DATA:
412 sigd = reinterpret_cast<NSSCMSSignedData*>(
413 NSS_CMSContentInfo_GetContent(cinfo));
414 break;
415
416 case SEC_OID_PKCS7_ENVELOPED_DATA:
417 case SEC_OID_PKCS7_ENCRYPTED_DATA:
418 case SEC_OID_PKCS7_DIGESTED_DATA:
419 default: {
420 MOZ_LOG(gCMSLog, LogLevel::Debug,
421 ("nsCMSMessage::CommonVerifySignature - unexpected "
422 "ContentTypeTag"));
423 rv = NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
424 goto loser;
425 }
426 }
427 }
428
429 if (!sigd) {
430 MOZ_LOG(gCMSLog, LogLevel::Debug,
431 ("nsCMSMessage::CommonVerifySignature - no content info"));
432 rv = NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
433 goto loser;
434 }
435
436 if (!aDigestData.IsEmpty()) {
437 SECOidTag oidTag;
438 SECItem digest;
439 // NSS_CMSSignedData_SetDigestValue() takes a copy and won't mutate our
440 // data, so we're OK to cast away the const here.
441 digest.data = const_cast<uint8_t*>(aDigestData.Elements());
442 digest.len = aDigestData.Length();
443
444 if (NSS_CMSSignedData_HasDigests(sigd)) {
445 SECAlgorithmID** existingAlgs = NSS_CMSSignedData_GetDigestAlgs(sigd);
446 if (existingAlgs) {
447 while (*existingAlgs) {
448 SECAlgorithmID* alg = *existingAlgs;
449 SECOidTag algOIDTag = SECOID_FindOIDTag(&alg->algorithm);
450 NSS_CMSSignedData_SetDigestValue(sigd, algOIDTag, NULL);
451 ++existingAlgs;
452 }
453 }
454 }
455
456 oidTag =
457 HASH_GetHashOidTagByHashType(static_cast<HASH_HashType>(aDigestType));
458 if (oidTag == SEC_OID_UNKNOWN) {
459 rv = NS_ERROR_CMS_VERIFY_BAD_DIGEST;
460 goto loser;
461 }
462
463 if (NSS_CMSSignedData_SetDigestValue(sigd, oidTag, &digest)) {
464 MOZ_LOG(gCMSLog, LogLevel::Debug,
465 ("nsCMSMessage::CommonVerifySignature - bad digest"));
466 rv = NS_ERROR_CMS_VERIFY_BAD_DIGEST;
467 goto loser;
468 }
469 }
470
471 // Import certs. Note that import failure is not a signature verification
472 // failure. //
473 if (myNSS_CMSSignedData_ImportCerts(
474 sigd, CERT_GetDefaultCertDB(), certUsageEmailRecipient, true,
475 myExtraVerificationOnCert) != SECSuccess) {
476 MOZ_LOG(gCMSLog, LogLevel::Debug,
477 ("nsCMSMessage::CommonVerifySignature - can not import certs"));
478 }
479
480 nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
481 PR_ASSERT(nsigners > 0);
482 NS_ENSURE_TRUE(nsigners > 0, NS_ERROR_UNEXPECTED);
483 si = NSS_CMSSignedData_GetSignerInfo(sigd, 0);
484
485 NS_ENSURE_TRUE(si, NS_ERROR_UNEXPECTED);
486 NS_ENSURE_TRUE(si->cert, NS_ERROR_UNEXPECTED);
487
488 // See bug 324474. We want to make sure the signing cert is
489 // still valid at the current time.
490
491 if (myExtraVerificationOnCert(si->cert, certUsageEmailSigner) != SECSuccess) {
492 MOZ_LOG(gCMSLog, LogLevel::Debug,
493 ("nsCMSMessage::CommonVerifySignature - signing cert not trusted "
494 "now"));
495 rv = NS_ERROR_CMS_VERIFY_UNTRUSTED;
496 goto loser;
497 }
498
499 // We verify the first signer info, only //
500 // XXX: NSS_CMSSignedData_VerifySignerInfo calls CERT_VerifyCert, which
501 // requires NSS's certificate verification configuration to be done in
502 // order to work well (e.g. honoring OCSP preferences and proxy settings
503 // for OCSP requests), but Gecko stopped doing that configuration. Something
504 // similar to what was done for Gecko bug 1028643 needs to be done here too.
505 if (NSS_CMSSignedData_VerifySignerInfo(sigd, 0, CERT_GetDefaultCertDB(),
506 certUsageEmailSigner) != SECSuccess) {
507 MOZ_LOG(
508 gCMSLog, LogLevel::Debug,
509 ("nsCMSMessage::CommonVerifySignature - unable to verify signature"));
510
511 if (NSSCMSVS_SigningCertNotFound == si->verificationStatus) {
512 MOZ_LOG(gCMSLog, LogLevel::Debug,
513 ("nsCMSMessage::CommonVerifySignature - signing cert not found"));
514 rv = NS_ERROR_CMS_VERIFY_NOCERT;
515 } else if (NSSCMSVS_SigningCertNotTrusted == si->verificationStatus) {
516 MOZ_LOG(gCMSLog, LogLevel::Debug,
517 ("nsCMSMessage::CommonVerifySignature - signing cert not trusted "
518 "at signing time"));
519 rv = NS_ERROR_CMS_VERIFY_UNTRUSTED;
520 } else if (NSSCMSVS_Unverified == si->verificationStatus) {
521 MOZ_LOG(gCMSLog, LogLevel::Debug,
522 ("nsCMSMessage::CommonVerifySignature - can not verify"));
523 rv = NS_ERROR_CMS_VERIFY_ERROR_UNVERIFIED;
524 } else if (NSSCMSVS_ProcessingError == si->verificationStatus) {
525 MOZ_LOG(gCMSLog, LogLevel::Debug,
526 ("nsCMSMessage::CommonVerifySignature - processing error"));
527 rv = NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
528 } else if (NSSCMSVS_BadSignature == si->verificationStatus) {
529 MOZ_LOG(gCMSLog, LogLevel::Debug,
530 ("nsCMSMessage::CommonVerifySignature - bad signature"));
531 rv = NS_ERROR_CMS_VERIFY_BAD_SIGNATURE;
532 } else if (NSSCMSVS_DigestMismatch == si->verificationStatus) {
533 MOZ_LOG(gCMSLog, LogLevel::Debug,
534 ("nsCMSMessage::CommonVerifySignature - digest mismatch"));
535 rv = NS_ERROR_CMS_VERIFY_DIGEST_MISMATCH;
536 } else if (NSSCMSVS_SignatureAlgorithmUnknown == si->verificationStatus) {
537 MOZ_LOG(gCMSLog, LogLevel::Debug,
538 ("nsCMSMessage::CommonVerifySignature - algo unknown"));
539 rv = NS_ERROR_CMS_VERIFY_UNKNOWN_ALGO;
540 } else if (NSSCMSVS_SignatureAlgorithmUnsupported ==
541 si->verificationStatus) {
542 MOZ_LOG(gCMSLog, LogLevel::Debug,
543 ("nsCMSMessage::CommonVerifySignature - algo not supported"));
544 rv = NS_ERROR_CMS_VERIFY_UNSUPPORTED_ALGO;
545 } else if (NSSCMSVS_MalformedSignature == si->verificationStatus) {
546 MOZ_LOG(gCMSLog, LogLevel::Debug,
547 ("nsCMSMessage::CommonVerifySignature - malformed signature"));
548 rv = NS_ERROR_CMS_VERIFY_MALFORMED_SIGNATURE;
549 }
550
551 goto loser;
552 }
553
554 // Save the profile. Note that save import failure is not a signature
555 // verification failure. //
556 if (NSS_SMIMESignerInfo_SaveSMIMEProfile(si) != SECSuccess) {
557 MOZ_LOG(gCMSLog, LogLevel::Debug,
558 ("nsCMSMessage::CommonVerifySignature - unable to save smime "
559 "profile"));
560 }
561
562 rv = NS_OK;
563 loser:
564 return rv;
565 }
566
AsyncVerifySignature(nsISMimeVerificationListener * aListener)567 NS_IMETHODIMP nsCMSMessage::AsyncVerifySignature(
568 nsISMimeVerificationListener* aListener) {
569 return CommonAsyncVerifySignature(aListener, {}, 0);
570 }
571
AsyncVerifyDetachedSignature(nsISMimeVerificationListener * aListener,const nsTArray<uint8_t> & aDigestData,int16_t aDigestType)572 NS_IMETHODIMP nsCMSMessage::AsyncVerifyDetachedSignature(
573 nsISMimeVerificationListener* aListener,
574 const nsTArray<uint8_t>& aDigestData, int16_t aDigestType) {
575 if (aDigestData.IsEmpty()) return NS_ERROR_FAILURE;
576
577 return CommonAsyncVerifySignature(aListener, aDigestData, aDigestType);
578 }
579
580 class SMimeVerificationTask final : public CryptoTask {
581 public:
SMimeVerificationTask(nsICMSMessage * aMessage,nsISMimeVerificationListener * aListener,const nsTArray<uint8_t> & aDigestData,int16_t aDigestType)582 SMimeVerificationTask(nsICMSMessage* aMessage,
583 nsISMimeVerificationListener* aListener,
584 const nsTArray<uint8_t>& aDigestData,
585 int16_t aDigestType)
586 : mMessage(aMessage),
587 mListener(aListener),
588 mDigestData(aDigestData.Clone()),
589 mDigestType(aDigestType) {
590 MOZ_ASSERT(NS_IsMainThread());
591 }
592
593 private:
CalculateResult()594 virtual nsresult CalculateResult() override {
595 MOZ_ASSERT(!NS_IsMainThread());
596
597 mozilla::StaticMutexAutoLock lock(sMutex);
598 nsresult rv;
599 if (mDigestData.IsEmpty()) {
600 rv = mMessage->VerifySignature();
601 } else {
602 rv = mMessage->VerifyDetachedSignature(mDigestData, mDigestType);
603 }
604
605 return rv;
606 }
CallCallback(nsresult rv)607 virtual void CallCallback(nsresult rv) override {
608 MOZ_ASSERT(NS_IsMainThread());
609 mListener->Notify(mMessage, rv);
610 }
611
612 nsCOMPtr<nsICMSMessage> mMessage;
613 nsCOMPtr<nsISMimeVerificationListener> mListener;
614 nsTArray<uint8_t> mDigestData;
615 int16_t mDigestType;
616
617 static mozilla::StaticMutex sMutex;
618 };
619
620 mozilla::StaticMutex SMimeVerificationTask::sMutex;
621
CommonAsyncVerifySignature(nsISMimeVerificationListener * aListener,const nsTArray<uint8_t> & aDigestData,int16_t aDigestType)622 nsresult nsCMSMessage::CommonAsyncVerifySignature(
623 nsISMimeVerificationListener* aListener,
624 const nsTArray<uint8_t>& aDigestData, int16_t aDigestType) {
625 RefPtr<CryptoTask> task =
626 new SMimeVerificationTask(this, aListener, aDigestData, aDigestType);
627 return task->Dispatch();
628 }
629
630 class nsZeroTerminatedCertArray {
631 public:
nsZeroTerminatedCertArray()632 nsZeroTerminatedCertArray() : mCerts(nullptr), mPoolp(nullptr), mSize(0) {}
633
~nsZeroTerminatedCertArray()634 ~nsZeroTerminatedCertArray() { destructorSafeDestroyNSSReference(); }
635
destructorSafeDestroyNSSReference()636 void destructorSafeDestroyNSSReference() {
637 if (mCerts) {
638 for (uint32_t i = 0; i < mSize; i++) {
639 if (mCerts[i]) {
640 CERT_DestroyCertificate(mCerts[i]);
641 }
642 }
643 }
644
645 if (mPoolp) PORT_FreeArena(mPoolp, false);
646 }
647
allocate(uint32_t count)648 bool allocate(uint32_t count) {
649 // only allow allocation once
650 if (mPoolp) return false;
651
652 mSize = count;
653
654 if (!mSize) return false;
655
656 mPoolp = PORT_NewArena(1024);
657 if (!mPoolp) return false;
658
659 mCerts = (CERTCertificate**)PORT_ArenaZAlloc(
660 mPoolp, (count + 1) * sizeof(CERTCertificate*));
661
662 if (!mCerts) return false;
663
664 // null array, including zero termination
665 for (uint32_t i = 0; i < count + 1; i++) {
666 mCerts[i] = nullptr;
667 }
668
669 return true;
670 }
671
set(uint32_t i,CERTCertificate * c)672 void set(uint32_t i, CERTCertificate* c) {
673 if (i >= mSize) return;
674
675 if (mCerts[i]) {
676 CERT_DestroyCertificate(mCerts[i]);
677 }
678
679 mCerts[i] = CERT_DupCertificate(c);
680 }
681
get(uint32_t i)682 CERTCertificate* get(uint32_t i) {
683 if (i >= mSize) return nullptr;
684
685 return CERT_DupCertificate(mCerts[i]);
686 }
687
getRawArray()688 CERTCertificate** getRawArray() { return mCerts; }
689
690 private:
691 CERTCertificate** mCerts;
692 PLArenaPool* mPoolp;
693 uint32_t mSize;
694 };
695
CreateEncrypted(const nsTArray<RefPtr<nsIX509Cert>> & aRecipientCerts)696 NS_IMETHODIMP nsCMSMessage::CreateEncrypted(
697 const nsTArray<RefPtr<nsIX509Cert>>& aRecipientCerts) {
698 MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSMessage::CreateEncrypted"));
699 NSSCMSContentInfo* cinfo;
700 NSSCMSEnvelopedData* envd;
701 NSSCMSRecipientInfo* recipientInfo;
702 nsZeroTerminatedCertArray recipientCerts;
703 SECOidTag bulkAlgTag;
704 int keySize;
705 uint32_t i;
706 nsresult rv = NS_ERROR_FAILURE;
707
708 // Check the recipient certificates //
709 uint32_t recipientCertCount = aRecipientCerts.Length();
710 PR_ASSERT(recipientCertCount > 0);
711
712 if (!recipientCerts.allocate(recipientCertCount)) {
713 goto loser;
714 }
715
716 for (i = 0; i < recipientCertCount; i++) {
717 nsIX509Cert* x509cert = aRecipientCerts[i];
718
719 if (!x509cert) return NS_ERROR_FAILURE;
720
721 UniqueCERTCertificate c(x509cert->GetCert());
722 recipientCerts.set(i, c.get());
723 }
724
725 // Find a bulk key algorithm //
726 if (NSS_SMIMEUtil_FindBulkAlgForRecipients(
727 recipientCerts.getRawArray(), &bulkAlgTag, &keySize) != SECSuccess) {
728 MOZ_LOG(gCMSLog, LogLevel::Debug,
729 ("nsCMSMessage::CreateEncrypted - can't find bulk alg for "
730 "recipients"));
731 rv = NS_ERROR_CMS_ENCRYPT_NO_BULK_ALG;
732 goto loser;
733 }
734
735 m_cmsMsg = NSS_CMSMessage_Create(nullptr);
736 if (!m_cmsMsg) {
737 MOZ_LOG(gCMSLog, LogLevel::Debug,
738 ("nsCMSMessage::CreateEncrypted - can't create new cms message"));
739 rv = NS_ERROR_OUT_OF_MEMORY;
740 goto loser;
741 }
742
743 if ((envd = NSS_CMSEnvelopedData_Create(m_cmsMsg, bulkAlgTag, keySize)) ==
744 nullptr) {
745 MOZ_LOG(gCMSLog, LogLevel::Debug,
746 ("nsCMSMessage::CreateEncrypted - can't create enveloped data"));
747 goto loser;
748 }
749
750 cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg);
751 if (NSS_CMSContentInfo_SetContent_EnvelopedData(m_cmsMsg, cinfo, envd) !=
752 SECSuccess) {
753 MOZ_LOG(gCMSLog, LogLevel::Debug,
754 ("nsCMSMessage::CreateEncrypted - can't create content enveloped "
755 "data"));
756 goto loser;
757 }
758
759 cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd);
760 if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nullptr, false) !=
761 SECSuccess) {
762 MOZ_LOG(gCMSLog, LogLevel::Debug,
763 ("nsCMSMessage::CreateEncrypted - can't set content data"));
764 goto loser;
765 }
766
767 // Create and attach recipient information //
768 for (i = 0; i < recipientCertCount; i++) {
769 UniqueCERTCertificate rc(recipientCerts.get(i));
770 if ((recipientInfo = NSS_CMSRecipientInfo_Create(m_cmsMsg, rc.get())) ==
771 nullptr) {
772 MOZ_LOG(gCMSLog, LogLevel::Debug,
773 ("nsCMSMessage::CreateEncrypted - can't create recipient info"));
774 goto loser;
775 }
776 if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientInfo) != SECSuccess) {
777 MOZ_LOG(gCMSLog, LogLevel::Debug,
778 ("nsCMSMessage::CreateEncrypted - can't add recipient info"));
779 goto loser;
780 }
781 }
782
783 return NS_OK;
784 loser:
785 if (m_cmsMsg) {
786 NSS_CMSMessage_Destroy(m_cmsMsg);
787 m_cmsMsg = nullptr;
788 }
789
790 return rv;
791 }
792
IsAllowedHash(const int16_t aCryptoHashInt)793 bool nsCMSMessage::IsAllowedHash(const int16_t aCryptoHashInt) {
794 switch (aCryptoHashInt) {
795 case nsICryptoHash::SHA1:
796 case nsICryptoHash::SHA256:
797 case nsICryptoHash::SHA384:
798 case nsICryptoHash::SHA512:
799 return true;
800 default:
801 return false;
802 }
803 }
804
805 NS_IMETHODIMP
CreateSigned(nsIX509Cert * aSigningCert,nsIX509Cert * aEncryptCert,const nsTArray<uint8_t> & aDigestData,int16_t aDigestType)806 nsCMSMessage::CreateSigned(nsIX509Cert* aSigningCert, nsIX509Cert* aEncryptCert,
807 const nsTArray<uint8_t>& aDigestData,
808 int16_t aDigestType) {
809 NS_ENSURE_ARG(aSigningCert);
810 MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned"));
811 NSSCMSContentInfo* cinfo;
812 NSSCMSSignedData* sigd;
813 NSSCMSSignerInfo* signerinfo;
814 UniqueCERTCertificate scert(aSigningCert->GetCert());
815 UniqueCERTCertificate ecert;
816 nsresult rv = NS_ERROR_FAILURE;
817
818 if (!scert) {
819 return NS_ERROR_FAILURE;
820 }
821
822 if (aEncryptCert) {
823 ecert = UniqueCERTCertificate(aEncryptCert->GetCert());
824 }
825
826 if (!IsAllowedHash(aDigestType)) {
827 return NS_ERROR_INVALID_ARG;
828 }
829
830 SECOidTag digestType =
831 HASH_GetHashOidTagByHashType(static_cast<HASH_HashType>(aDigestType));
832 if (digestType == SEC_OID_UNKNOWN) {
833 return NS_ERROR_INVALID_ARG;
834 }
835
836 /*
837 * create the message object
838 */
839 m_cmsMsg =
840 NSS_CMSMessage_Create(nullptr); /* create a message on its own pool */
841 if (!m_cmsMsg) {
842 MOZ_LOG(gCMSLog, LogLevel::Debug,
843 ("nsCMSMessage::CreateSigned - can't create new message"));
844 rv = NS_ERROR_OUT_OF_MEMORY;
845 goto loser;
846 }
847
848 /*
849 * build chain of objects: message->signedData->data
850 */
851 if ((sigd = NSS_CMSSignedData_Create(m_cmsMsg)) == nullptr) {
852 MOZ_LOG(gCMSLog, LogLevel::Debug,
853 ("nsCMSMessage::CreateSigned - can't create signed data"));
854 goto loser;
855 }
856 cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg);
857 if (NSS_CMSContentInfo_SetContent_SignedData(m_cmsMsg, cinfo, sigd) !=
858 SECSuccess) {
859 MOZ_LOG(gCMSLog, LogLevel::Debug,
860 ("nsCMSMessage::CreateSigned - can't set content signed data"));
861 goto loser;
862 }
863
864 cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
865
866 /* we're always passing data in and detaching optionally */
867 if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nullptr, true) !=
868 SECSuccess) {
869 MOZ_LOG(gCMSLog, LogLevel::Debug,
870 ("nsCMSMessage::CreateSigned - can't set content data"));
871 goto loser;
872 }
873
874 /*
875 * create & attach signer information
876 */
877 signerinfo = NSS_CMSSignerInfo_Create(m_cmsMsg, scert.get(), digestType);
878 if (!signerinfo) {
879 MOZ_LOG(gCMSLog, LogLevel::Debug,
880 ("nsCMSMessage::CreateSigned - can't create signer info"));
881 goto loser;
882 }
883
884 /* we want the cert chain included for this one */
885 if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain,
886 certUsageEmailSigner) != SECSuccess) {
887 MOZ_LOG(gCMSLog, LogLevel::Debug,
888 ("nsCMSMessage::CreateSigned - can't include signer cert chain"));
889 goto loser;
890 }
891
892 if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess) {
893 MOZ_LOG(gCMSLog, LogLevel::Debug,
894 ("nsCMSMessage::CreateSigned - can't add signing time"));
895 goto loser;
896 }
897
898 if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) {
899 MOZ_LOG(gCMSLog, LogLevel::Debug,
900 ("nsCMSMessage::CreateSigned - can't add smime caps"));
901 goto loser;
902 }
903
904 if (ecert) {
905 if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(
906 signerinfo, ecert.get(), CERT_GetDefaultCertDB()) != SECSuccess) {
907 MOZ_LOG(gCMSLog, LogLevel::Debug,
908 ("nsCMSMessage::CreateSigned - can't add smime enc key prefs"));
909 goto loser;
910 }
911
912 if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(
913 signerinfo, ecert.get(), CERT_GetDefaultCertDB()) != SECSuccess) {
914 MOZ_LOG(
915 gCMSLog, LogLevel::Debug,
916 ("nsCMSMessage::CreateSigned - can't add MS smime enc key prefs"));
917 goto loser;
918 }
919
920 // If signing and encryption cert are identical, don't add it twice.
921 bool addEncryptionCert =
922 (ecert && (!scert || !CERT_CompareCerts(ecert.get(), scert.get())));
923
924 if (addEncryptionCert &&
925 NSS_CMSSignedData_AddCertificate(sigd, ecert.get()) != SECSuccess) {
926 MOZ_LOG(gCMSLog, LogLevel::Debug,
927 ("nsCMSMessage::CreateSigned - can't add own encryption "
928 "certificate"));
929 goto loser;
930 }
931 }
932
933 if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
934 MOZ_LOG(gCMSLog, LogLevel::Debug,
935 ("nsCMSMessage::CreateSigned - can't add signer info"));
936 goto loser;
937 }
938
939 // Finally, add the pre-computed digest if passed in
940 if (!aDigestData.IsEmpty()) {
941 SECItem digest;
942
943 // NSS_CMSSignedData_SetDigestValue() takes a copy and won't mutate our
944 // data, so we're OK to cast away the const here.
945 digest.data = const_cast<uint8_t*>(aDigestData.Elements());
946 digest.len = aDigestData.Length();
947 if (NSS_CMSSignedData_SetDigestValue(sigd, digestType, &digest) !=
948 SECSuccess) {
949 MOZ_LOG(gCMSLog, LogLevel::Debug,
950 ("nsCMSMessage::CreateSigned - can't set digest value"));
951 goto loser;
952 }
953 }
954
955 return NS_OK;
956 loser:
957 if (m_cmsMsg) {
958 NSS_CMSMessage_Destroy(m_cmsMsg);
959 m_cmsMsg = nullptr;
960 }
961 return rv;
962 }
963
NS_IMPL_ISUPPORTS(nsCMSDecoder,nsICMSDecoder)964 NS_IMPL_ISUPPORTS(nsCMSDecoder, nsICMSDecoder)
965
966 nsCMSDecoder::nsCMSDecoder() : m_dcx(nullptr) {}
967
~nsCMSDecoder()968 nsCMSDecoder::~nsCMSDecoder() { destructorSafeDestroyNSSReference(); }
969
Init()970 nsresult nsCMSDecoder::Init() {
971 nsresult rv;
972 nsCOMPtr<nsISupports> nssInitialized =
973 do_GetService("@mozilla.org/psm;1", &rv);
974 return rv;
975 }
976
destructorSafeDestroyNSSReference()977 void nsCMSDecoder::destructorSafeDestroyNSSReference() {
978 if (m_dcx) {
979 NSS_CMSDecoder_Cancel(m_dcx);
980 m_dcx = nullptr;
981 }
982 }
983
984 /* void start (in NSSCMSContentCallback cb, in voidPtr arg); */
Start(NSSCMSContentCallback cb,void * arg)985 NS_IMETHODIMP nsCMSDecoder::Start(NSSCMSContentCallback cb, void* arg) {
986 MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSDecoder::Start"));
987 m_ctx = new PipUIContext();
988
989 m_dcx = NSS_CMSDecoder_Start(0, cb, arg, 0, m_ctx, 0, 0);
990 if (!m_dcx) {
991 MOZ_LOG(gCMSLog, LogLevel::Debug,
992 ("nsCMSDecoder::Start - can't start decoder"));
993 return NS_ERROR_FAILURE;
994 }
995 return NS_OK;
996 }
997
998 /* void update (in string bug, in long len); */
Update(const char * buf,int32_t len)999 NS_IMETHODIMP nsCMSDecoder::Update(const char* buf, int32_t len) {
1000 NSS_CMSDecoder_Update(m_dcx, (char*)buf, len);
1001 return NS_OK;
1002 }
1003
1004 /* void finish (); */
Finish(nsICMSMessage ** aCMSMsg)1005 NS_IMETHODIMP nsCMSDecoder::Finish(nsICMSMessage** aCMSMsg) {
1006 MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSDecoder::Finish"));
1007 NSSCMSMessage* cmsMsg;
1008 cmsMsg = NSS_CMSDecoder_Finish(m_dcx);
1009 m_dcx = nullptr;
1010 if (cmsMsg) {
1011 nsCMSMessage* obj = new nsCMSMessage(cmsMsg);
1012 // The NSS object cmsMsg still carries a reference to the context
1013 // we gave it on construction.
1014 // Make sure the context will live long enough.
1015 obj->referenceContext(m_ctx);
1016 NS_ADDREF(*aCMSMsg = obj);
1017 }
1018 return NS_OK;
1019 }
1020
NS_IMPL_ISUPPORTS(nsCMSEncoder,nsICMSEncoder)1021 NS_IMPL_ISUPPORTS(nsCMSEncoder, nsICMSEncoder)
1022
1023 nsCMSEncoder::nsCMSEncoder() : m_ecx(nullptr) {}
1024
~nsCMSEncoder()1025 nsCMSEncoder::~nsCMSEncoder() { destructorSafeDestroyNSSReference(); }
1026
Init()1027 nsresult nsCMSEncoder::Init() {
1028 nsresult rv;
1029 nsCOMPtr<nsISupports> nssInitialized =
1030 do_GetService("@mozilla.org/psm;1", &rv);
1031 return rv;
1032 }
1033
destructorSafeDestroyNSSReference()1034 void nsCMSEncoder::destructorSafeDestroyNSSReference() {
1035 if (m_ecx) NSS_CMSEncoder_Cancel(m_ecx);
1036 }
1037
1038 /* void start (); */
Start(nsICMSMessage * aMsg,NSSCMSContentCallback cb,void * arg)1039 NS_IMETHODIMP nsCMSEncoder::Start(nsICMSMessage* aMsg, NSSCMSContentCallback cb,
1040 void* arg) {
1041 MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSEncoder::Start"));
1042 nsCMSMessage* cmsMsg = static_cast<nsCMSMessage*>(aMsg);
1043 m_ctx = new PipUIContext();
1044
1045 m_ecx = NSS_CMSEncoder_Start(cmsMsg->getCMS(), cb, arg, 0, 0, 0, m_ctx, 0, 0,
1046 0, 0);
1047 if (!m_ecx) {
1048 MOZ_LOG(gCMSLog, LogLevel::Debug,
1049 ("nsCMSEncoder::Start - can't start encoder"));
1050 return NS_ERROR_FAILURE;
1051 }
1052 return NS_OK;
1053 }
1054
1055 /* void update (in string aBuf, in long aLen); */
Update(const char * aBuf,int32_t aLen)1056 NS_IMETHODIMP nsCMSEncoder::Update(const char* aBuf, int32_t aLen) {
1057 MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSEncoder::Update"));
1058 if (!m_ecx || NSS_CMSEncoder_Update(m_ecx, aBuf, aLen) != SECSuccess) {
1059 MOZ_LOG(gCMSLog, LogLevel::Debug,
1060 ("nsCMSEncoder::Update - can't update encoder"));
1061 return NS_ERROR_FAILURE;
1062 }
1063 return NS_OK;
1064 }
1065
1066 /* void finish (); */
Finish()1067 NS_IMETHODIMP nsCMSEncoder::Finish() {
1068 nsresult rv = NS_OK;
1069 MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSEncoder::Finish"));
1070 if (!m_ecx || NSS_CMSEncoder_Finish(m_ecx) != SECSuccess) {
1071 MOZ_LOG(gCMSLog, LogLevel::Debug,
1072 ("nsCMSEncoder::Finish - can't finish encoder"));
1073 rv = NS_ERROR_FAILURE;
1074 }
1075 m_ecx = nullptr;
1076 return rv;
1077 }
1078
1079 /* void encode (in nsICMSMessage aMsg); */
Encode(nsICMSMessage * aMsg)1080 NS_IMETHODIMP nsCMSEncoder::Encode(nsICMSMessage* aMsg) {
1081 MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSEncoder::Encode"));
1082 return NS_ERROR_NOT_IMPLEMENTED;
1083 }
1084