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