summaryrefslogtreecommitdiffstats
path: root/mailnews/extensions/smime/src
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/extensions/smime/src')
-rw-r--r--mailnews/extensions/smime/src/moz.build23
-rw-r--r--mailnews/extensions/smime/src/nsCertPicker.cpp471
-rw-r--r--mailnews/extensions/smime/src/nsCertPicker.h36
-rw-r--r--mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.cpp36
-rw-r--r--mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.h25
-rw-r--r--mailnews/extensions/smime/src/nsMsgComposeSecure.cpp1203
-rw-r--r--mailnews/extensions/smime/src/nsMsgComposeSecure.h106
-rw-r--r--mailnews/extensions/smime/src/nsMsgSMIMECID.h42
-rw-r--r--mailnews/extensions/smime/src/nsSMimeJSHelper.cpp335
-rw-r--r--mailnews/extensions/smime/src/nsSMimeJSHelper.h26
-rw-r--r--mailnews/extensions/smime/src/smime-service.js24
-rw-r--r--mailnews/extensions/smime/src/smime-service.manifest3
12 files changed, 2330 insertions, 0 deletions
diff --git a/mailnews/extensions/smime/src/moz.build b/mailnews/extensions/smime/src/moz.build
new file mode 100644
index 000000000..f3e888dd4
--- /dev/null
+++ b/mailnews/extensions/smime/src/moz.build
@@ -0,0 +1,23 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SOURCES += [
+ 'nsCertPicker.cpp',
+ 'nsEncryptedSMIMEURIsService.cpp',
+ 'nsMsgComposeSecure.cpp',
+ 'nsSMimeJSHelper.cpp',
+]
+
+EXTRA_COMPONENTS += [
+ 'smime-service.js',
+ 'smime-service.manifest',
+]
+
+FINAL_LIBRARY = 'mail'
+
+LOCAL_INCLUDES += [
+ '/mozilla/security/manager/pki',
+ '/mozilla/security/pkix/include'
+]
diff --git a/mailnews/extensions/smime/src/nsCertPicker.cpp b/mailnews/extensions/smime/src/nsCertPicker.cpp
new file mode 100644
index 000000000..183f9605c
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsCertPicker.cpp
@@ -0,0 +1,471 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCertPicker.h"
+
+#include "MainThreadUtils.h"
+#include "ScopedNSSTypes.h"
+#include "cert.h"
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsICertPickDialogs.h"
+#include "nsIDOMWindow.h"
+#include "nsIDialogParamBlock.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIServiceManager.h"
+#include "nsIX509CertValidity.h"
+#include "nsMemory.h"
+#include "nsMsgComposeSecure.h"
+#include "nsNSSCertificate.h"
+#include "nsNSSComponent.h"
+#include "nsNSSDialogHelper.h"
+#include "nsNSSHelper.h"
+#include "nsNSSShutDown.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+#include "pkix/pkixtypes.h"
+
+using namespace mozilla;
+
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertNicknames,
+ CERTCertNicknames,
+ CERT_FreeNicknames)
+
+CERTCertNicknames*
+getNSSCertNicknamesFromCertList(const UniqueCERTCertList& certList)
+{
+ static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
+
+ nsresult rv;
+
+ nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ nsAutoString expiredString, notYetValidString;
+ nsAutoString expiredStringLeadingSpace, notYetValidStringLeadingSpace;
+
+ nssComponent->GetPIPNSSBundleString("NicknameExpired", expiredString);
+ nssComponent->GetPIPNSSBundleString("NicknameNotYetValid", notYetValidString);
+
+ expiredStringLeadingSpace.Append(' ');
+ expiredStringLeadingSpace.Append(expiredString);
+
+ notYetValidStringLeadingSpace.Append(' ');
+ notYetValidStringLeadingSpace.Append(notYetValidString);
+
+ NS_ConvertUTF16toUTF8 aUtf8ExpiredString(expiredStringLeadingSpace);
+ NS_ConvertUTF16toUTF8 aUtf8NotYetValidString(notYetValidStringLeadingSpace);
+
+ return CERT_NicknameStringsFromCertList(certList.get(),
+ const_cast<char*>(aUtf8ExpiredString.get()),
+ const_cast<char*>(aUtf8NotYetValidString.get()));
+}
+
+nsresult
+FormatUIStrings(nsIX509Cert* cert, const nsAutoString& nickname,
+ nsAutoString& nickWithSerial, nsAutoString& details)
+{
+ if (!NS_IsMainThread()) {
+ NS_ERROR("nsNSSCertificate::FormatUIStrings called off the main thread");
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ RefPtr<nsMsgComposeSecure> mcs = new nsMsgComposeSecure;
+ if (!mcs) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoString info;
+ nsAutoString temp1;
+
+ nickWithSerial.Append(nickname);
+
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoIssuedFor", info))) {
+ details.Append(info);
+ details.Append(char16_t(' '));
+ if (NS_SUCCEEDED(cert->GetSubjectName(temp1)) && !temp1.IsEmpty()) {
+ details.Append(temp1);
+ }
+ details.Append(char16_t('\n'));
+ }
+
+ if (NS_SUCCEEDED(cert->GetSerialNumber(temp1)) && !temp1.IsEmpty()) {
+ details.AppendLiteral(" ");
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertDumpSerialNo", info))) {
+ details.Append(info);
+ details.AppendLiteral(": ");
+ }
+ details.Append(temp1);
+
+ nickWithSerial.AppendLiteral(" [");
+ nickWithSerial.Append(temp1);
+ nickWithSerial.Append(char16_t(']'));
+
+ details.Append(char16_t('\n'));
+ }
+
+ nsCOMPtr<nsIX509CertValidity> validity;
+ nsresult rv = cert->GetValidity(getter_AddRefs(validity));
+ if (NS_SUCCEEDED(rv) && validity) {
+ details.AppendLiteral(" ");
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoValid", info))) {
+ details.Append(info);
+ }
+
+ if (NS_SUCCEEDED(validity->GetNotBeforeLocalTime(temp1)) && !temp1.IsEmpty()) {
+ details.Append(char16_t(' '));
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoFrom", info))) {
+ details.Append(info);
+ details.Append(char16_t(' '));
+ }
+ details.Append(temp1);
+ }
+
+ if (NS_SUCCEEDED(validity->GetNotAfterLocalTime(temp1)) && !temp1.IsEmpty()) {
+ details.Append(char16_t(' '));
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoTo", info))) {
+ details.Append(info);
+ details.Append(char16_t(' '));
+ }
+ details.Append(temp1);
+ }
+
+ details.Append(char16_t('\n'));
+ }
+
+ if (NS_SUCCEEDED(cert->GetKeyUsages(temp1)) && !temp1.IsEmpty()) {
+ details.AppendLiteral(" ");
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertDumpKeyUsage", info))) {
+ details.Append(info);
+ details.AppendLiteral(": ");
+ }
+ details.Append(temp1);
+ details.Append(char16_t('\n'));
+ }
+
+ UniqueCERTCertificate nssCert(cert->GetCert());
+ if (!nssCert) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoString firstEmail;
+ const char* aWalkAddr;
+ for (aWalkAddr = CERT_GetFirstEmailAddress(nssCert.get())
+ ;
+ aWalkAddr
+ ;
+ aWalkAddr = CERT_GetNextEmailAddress(nssCert.get(), aWalkAddr))
+ {
+ NS_ConvertUTF8toUTF16 email(aWalkAddr);
+ if (email.IsEmpty())
+ continue;
+
+ if (firstEmail.IsEmpty()) {
+ // If the first email address from the subject DN is also present
+ // in the subjectAltName extension, GetEmailAddresses() will return
+ // it twice (as received from NSS). Remember the first address so that
+ // we can filter out duplicates later on.
+ firstEmail = email;
+
+ details.AppendLiteral(" ");
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoEmail", info))) {
+ details.Append(info);
+ details.AppendLiteral(": ");
+ }
+ details.Append(email);
+ }
+ else {
+ // Append current address if it's different from the first one.
+ if (!firstEmail.Equals(email)) {
+ details.AppendLiteral(", ");
+ details.Append(email);
+ }
+ }
+ }
+
+ if (!firstEmail.IsEmpty()) {
+ // We got at least one email address, so we want a newline
+ details.Append(char16_t('\n'));
+ }
+
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoIssuedBy", info))) {
+ details.Append(info);
+ details.Append(char16_t(' '));
+
+ if (NS_SUCCEEDED(cert->GetIssuerName(temp1)) && !temp1.IsEmpty()) {
+ details.Append(temp1);
+ }
+
+ details.Append(char16_t('\n'));
+ }
+
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoStoredIn", info))) {
+ details.Append(info);
+ details.Append(char16_t(' '));
+
+ if (NS_SUCCEEDED(cert->GetTokenName(temp1)) && !temp1.IsEmpty()) {
+ details.Append(temp1);
+ }
+ }
+
+ // the above produces the following output:
+ //
+ // Issued to: $subjectName
+ // Serial number: $serialNumber
+ // Valid from: $starting_date to $expiration_date
+ // Certificate Key usage: $usages
+ // Email: $address(es)
+ // Issued by: $issuerName
+ // Stored in: $token
+
+ return rv;
+}
+
+NS_IMPL_ISUPPORTS(nsCertPicker, nsICertPickDialogs, nsIUserCertPicker)
+
+nsCertPicker::nsCertPicker()
+{
+}
+
+nsCertPicker::~nsCertPicker()
+{
+ nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+ return;
+ }
+
+ shutdown(ShutdownCalledFrom::Object);
+}
+
+nsresult
+nsCertPicker::Init()
+{
+ nsresult rv;
+ nsCOMPtr<nsISupports> psm = do_GetService("@mozilla.org/psm;1", &rv);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsCertPicker::PickCertificate(nsIInterfaceRequestor *ctx,
+ const char16_t **certNickList,
+ const char16_t **certDetailsList,
+ uint32_t count,
+ int32_t *selectedIndex,
+ bool *canceled)
+{
+ nsresult rv;
+ uint32_t i;
+
+ *canceled = false;
+
+ nsCOMPtr<nsIDialogParamBlock> block =
+ do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID);
+ if (!block) return NS_ERROR_FAILURE;
+
+ block->SetNumberStrings(1+count*2);
+
+ for (i = 0; i < count; i++) {
+ rv = block->SetString(i, certNickList[i]);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ for (i = 0; i < count; i++) {
+ rv = block->SetString(i+count, certDetailsList[i]);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ rv = block->SetInt(0, count);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = block->SetInt(1, *selectedIndex);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = nsNSSDialogHelper::openDialog(nullptr,
+ "chrome://messenger/content/certpicker.xul",
+ block);
+ if (NS_FAILED(rv)) return rv;
+
+ int32_t status;
+
+ rv = block->GetInt(0, &status);
+ if (NS_FAILED(rv)) return rv;
+
+ *canceled = (status == 0)?true:false;
+ if (!*canceled) {
+ rv = block->GetInt(1, selectedIndex);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsCertPicker::PickByUsage(nsIInterfaceRequestor *ctx,
+ const char16_t *selectedNickname,
+ int32_t certUsage,
+ bool allowInvalid,
+ bool allowDuplicateNicknames,
+ const nsAString &emailAddress,
+ bool *canceled,
+ nsIX509Cert **_retval)
+{
+ nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ int32_t selectedIndex = -1;
+ bool selectionFound = false;
+ char16_t **certNicknameList = nullptr;
+ char16_t **certDetailsList = nullptr;
+ CERTCertListNode* node = nullptr;
+ nsresult rv = NS_OK;
+
+ {
+ // Iterate over all certs. This assures that user is logged in to all hardware tokens.
+ nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
+ UniqueCERTCertList allcerts(PK11_ListCerts(PK11CertListUnique, ctx));
+ }
+
+ /* find all user certs that are valid for the specified usage */
+ /* note that we are allowing expired certs in this list */
+ UniqueCERTCertList certList(
+ CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(),
+ (SECCertUsage)certUsage,
+ !allowDuplicateNicknames,
+ !allowInvalid,
+ ctx));
+ if (!certList) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ /* if a (non-empty) emailAddress argument is supplied to PickByUsage, */
+ /* remove non-matching certificates from the candidate list */
+
+ if (!emailAddress.IsEmpty()) {
+ node = CERT_LIST_HEAD(certList);
+ while (!CERT_LIST_END(node, certList)) {
+ /* if the cert has at least one e-mail address, check if suitable */
+ if (CERT_GetFirstEmailAddress(node->cert)) {
+ RefPtr<nsNSSCertificate> tempCert(nsNSSCertificate::Create(node->cert));
+ bool match = false;
+ rv = tempCert->ContainsEmailAddress(emailAddress, &match);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!match) {
+ /* doesn't contain the specified address, so remove from the list */
+ CERTCertListNode* freenode = node;
+ node = CERT_LIST_NEXT(node);
+ CERT_RemoveCertListNode(freenode);
+ continue;
+ }
+ }
+ node = CERT_LIST_NEXT(node);
+ }
+ }
+
+ UniqueCERTCertNicknames nicknames(getNSSCertNicknamesFromCertList(certList));
+ if (!nicknames) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ certNicknameList = (char16_t **)moz_xmalloc(sizeof(char16_t *) * nicknames->numnicknames);
+ certDetailsList = (char16_t **)moz_xmalloc(sizeof(char16_t *) * nicknames->numnicknames);
+
+ if (!certNicknameList || !certDetailsList) {
+ free(certNicknameList);
+ free(certDetailsList);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ int32_t CertsToUse;
+
+ for (CertsToUse = 0, node = CERT_LIST_HEAD(certList.get());
+ !CERT_LIST_END(node, certList.get()) &&
+ CertsToUse < nicknames->numnicknames;
+ node = CERT_LIST_NEXT(node)
+ )
+ {
+ RefPtr<nsNSSCertificate> tempCert(nsNSSCertificate::Create(node->cert));
+
+ if (tempCert) {
+
+ nsAutoString i_nickname(NS_ConvertUTF8toUTF16(nicknames->nicknames[CertsToUse]));
+ nsAutoString nickWithSerial;
+ nsAutoString details;
+
+ if (!selectionFound) {
+ /* for the case when selectedNickname refers to a bare nickname */
+ if (i_nickname == nsDependentString(selectedNickname)) {
+ selectedIndex = CertsToUse;
+ selectionFound = true;
+ }
+ }
+
+ if (NS_SUCCEEDED(FormatUIStrings(tempCert, i_nickname, nickWithSerial,
+ details))) {
+ certNicknameList[CertsToUse] = ToNewUnicode(nickWithSerial);
+ certDetailsList[CertsToUse] = ToNewUnicode(details);
+ if (!selectionFound) {
+ /* for the case when selectedNickname refers to nickname + serial */
+ if (nickWithSerial == nsDependentString(selectedNickname)) {
+ selectedIndex = CertsToUse;
+ selectionFound = true;
+ }
+ }
+ }
+ else {
+ certNicknameList[CertsToUse] = nullptr;
+ certDetailsList[CertsToUse] = nullptr;
+ }
+
+ ++CertsToUse;
+ }
+ }
+
+ if (CertsToUse) {
+ nsCOMPtr<nsICertPickDialogs> dialogs;
+ rv = getNSSDialogs(getter_AddRefs(dialogs), NS_GET_IID(nsICertPickDialogs),
+ NS_CERTPICKDIALOGS_CONTRACTID);
+
+ if (NS_SUCCEEDED(rv)) {
+ // Show the cert picker dialog and get the index of the selected cert.
+ rv = dialogs->PickCertificate(ctx, (const char16_t**)certNicknameList,
+ (const char16_t**)certDetailsList,
+ CertsToUse, &selectedIndex, canceled);
+ }
+ }
+
+ int32_t i;
+ for (i = 0; i < CertsToUse; ++i) {
+ free(certNicknameList[i]);
+ free(certDetailsList[i]);
+ }
+ free(certNicknameList);
+ free(certDetailsList);
+
+ if (!CertsToUse) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (NS_SUCCEEDED(rv) && !*canceled) {
+ for (i = 0, node = CERT_LIST_HEAD(certList);
+ !CERT_LIST_END(node, certList);
+ ++i, node = CERT_LIST_NEXT(node)) {
+
+ if (i == selectedIndex) {
+ RefPtr<nsNSSCertificate> cert = nsNSSCertificate::Create(node->cert);
+ if (!cert) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ }
+
+ cert.forget(_retval);
+ break;
+ }
+ }
+ }
+
+ return rv;
+}
diff --git a/mailnews/extensions/smime/src/nsCertPicker.h b/mailnews/extensions/smime/src/nsCertPicker.h
new file mode 100644
index 000000000..e5881f14f
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsCertPicker.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCertPicker_h
+#define nsCertPicker_h
+
+#include "nsICertPickDialogs.h"
+#include "nsIUserCertPicker.h"
+#include "nsNSSShutDown.h"
+
+#define NS_CERT_PICKER_CID \
+ { 0x735959a1, 0xaf01, 0x447e, { 0xb0, 0x2d, 0x56, 0xe9, 0x68, 0xfa, 0x52, 0xb4 } }
+
+class nsCertPicker : public nsICertPickDialogs
+ , public nsIUserCertPicker
+ , public nsNSSShutDownObject
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICERTPICKDIALOGS
+ NS_DECL_NSIUSERCERTPICKER
+
+ nsCertPicker();
+
+ // Nothing to actually release.
+ virtual void virtualDestroyNSSReference() override {}
+
+ nsresult Init();
+
+protected:
+ virtual ~nsCertPicker();
+};
+
+#endif // nsCertPicker_h
diff --git a/mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.cpp b/mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.cpp
new file mode 100644
index 000000000..9276a07a6
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.cpp
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsEncryptedSMIMEURIsService.h"
+
+NS_IMPL_ISUPPORTS(nsEncryptedSMIMEURIsService, nsIEncryptedSMIMEURIsService)
+
+nsEncryptedSMIMEURIsService::nsEncryptedSMIMEURIsService()
+{
+}
+
+nsEncryptedSMIMEURIsService::~nsEncryptedSMIMEURIsService()
+{
+}
+
+NS_IMETHODIMP nsEncryptedSMIMEURIsService::RememberEncrypted(const nsACString & uri)
+{
+ // Assuming duplicates are allowed.
+ mEncryptedURIs.AppendElement(uri);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsEncryptedSMIMEURIsService::ForgetEncrypted(const nsACString & uri)
+{
+ // Assuming, this will only remove one copy of the string, if the array
+ // contains multiple copies of the same string.
+ mEncryptedURIs.RemoveElement(uri);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsEncryptedSMIMEURIsService::IsEncrypted(const nsACString & uri, bool *_retval)
+{
+ *_retval = mEncryptedURIs.Contains(uri);
+ return NS_OK;
+}
diff --git a/mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.h b/mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.h
new file mode 100644
index 000000000..429acbf0a
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.h
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsEncryptedSMIMEURIsService_H_
+#define _nsEncryptedSMIMEURIsService_H_
+
+#include "nsIEncryptedSMIMEURIsSrvc.h"
+#include "nsTArray.h"
+#include "nsStringGlue.h"
+
+class nsEncryptedSMIMEURIsService : public nsIEncryptedSMIMEURIsService
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIENCRYPTEDSMIMEURISSERVICE
+
+ nsEncryptedSMIMEURIsService();
+
+protected:
+ virtual ~nsEncryptedSMIMEURIsService();
+ nsTArray<nsCString> mEncryptedURIs;
+};
+
+#endif
diff --git a/mailnews/extensions/smime/src/nsMsgComposeSecure.cpp b/mailnews/extensions/smime/src/nsMsgComposeSecure.cpp
new file mode 100644
index 000000000..55383c828
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsMsgComposeSecure.cpp
@@ -0,0 +1,1203 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMsgComposeSecure.h"
+
+#include <algorithm>
+
+#include "ScopedNSSTypes.h"
+#include "cert.h"
+#include "keyhi.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Services.h"
+#include "mozilla/mailnews/MimeEncoder.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
+#include "msgCore.h"
+#include "nsAlgorithm.h"
+#include "nsComponentManagerUtils.h"
+#include "nsICryptoHash.h"
+#include "nsIMimeConverter.h"
+#include "nsIMsgCompFields.h"
+#include "nsIMsgIdentity.h"
+#include "nsIX509CertDB.h"
+#include "nsMemory.h"
+#include "nsMimeTypes.h"
+#include "nsMsgMimeCID.h"
+#include "nsNSSComponent.h"
+#include "nsServiceManagerUtils.h"
+#include "nspr.h"
+#include "pkix/Result.h"
+
+using namespace mozilla::mailnews;
+using namespace mozilla;
+using namespace mozilla::psm;
+
+#define MK_MIME_ERROR_WRITING_FILE -1
+
+#define SMIME_STRBUNDLE_URL "chrome://messenger/locale/am-smime.properties"
+
+// It doesn't make sense to encode the message because the message will be
+// displayed only if the MUA doesn't support MIME.
+// We need to consider what to do in case the server doesn't support 8BITMIME.
+// In short, we can't use non-ASCII characters here.
+static const char crypto_multipart_blurb[] = "This is a cryptographically signed message in MIME format.";
+
+static void mime_crypto_write_base64 (void *closure, const char *buf,
+ unsigned long size);
+static nsresult mime_encoder_output_fn(const char *buf, int32_t size,
+ void *closure);
+static nsresult mime_nested_encoder_output_fn(const char *buf, int32_t size,
+ void *closure);
+static nsresult make_multipart_signed_header_string(bool outer_p,
+ char **header_return,
+ char **boundary_return,
+ int16_t hash_type);
+static char *mime_make_separator(const char *prefix);
+
+
+static void
+GenerateGlobalRandomBytes(unsigned char *buf, int32_t len)
+{
+ static bool firstTime = true;
+
+ if (firstTime)
+ {
+ // Seed the random-number generator with current time so that
+ // the numbers will be different every time we run.
+ srand( (unsigned)PR_Now() );
+ firstTime = false;
+ }
+
+ for( int32_t i = 0; i < len; i++ )
+ buf[i] = rand() % 10;
+}
+
+char
+*mime_make_separator(const char *prefix)
+{
+ unsigned char rand_buf[13];
+ GenerateGlobalRandomBytes(rand_buf, 12);
+
+ return PR_smprintf("------------%s"
+ "%02X%02X%02X%02X"
+ "%02X%02X%02X%02X"
+ "%02X%02X%02X%02X",
+ prefix,
+ rand_buf[0], rand_buf[1], rand_buf[2], rand_buf[3],
+ rand_buf[4], rand_buf[5], rand_buf[6], rand_buf[7],
+ rand_buf[8], rand_buf[9], rand_buf[10], rand_buf[11]);
+}
+
+// end of copied code which needs fixed....
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Implementation of nsMsgSMIMEComposeFields
+/////////////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS(nsMsgSMIMEComposeFields, nsIMsgSMIMECompFields)
+
+nsMsgSMIMEComposeFields::nsMsgSMIMEComposeFields()
+:mSignMessage(false), mAlwaysEncryptMessage(false)
+{
+}
+
+nsMsgSMIMEComposeFields::~nsMsgSMIMEComposeFields()
+{
+}
+
+NS_IMETHODIMP nsMsgSMIMEComposeFields::SetSignMessage(bool value)
+{
+ mSignMessage = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgSMIMEComposeFields::GetSignMessage(bool *_retval)
+{
+ *_retval = mSignMessage;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgSMIMEComposeFields::SetRequireEncryptMessage(bool value)
+{
+ mAlwaysEncryptMessage = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgSMIMEComposeFields::GetRequireEncryptMessage(bool *_retval)
+{
+ *_retval = mAlwaysEncryptMessage;
+ return NS_OK;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Implementation of nsMsgComposeSecure
+/////////////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS(nsMsgComposeSecure, nsIMsgComposeSecure)
+
+nsMsgComposeSecure::nsMsgComposeSecure()
+{
+ /* member initializers and constructor code */
+ mMultipartSignedBoundary = 0;
+ mBuffer = 0;
+ mBufferedBytes = 0;
+ mHashType = 0;
+}
+
+nsMsgComposeSecure::~nsMsgComposeSecure()
+{
+ /* destructor code */
+ if (mEncryptionContext) {
+ if (mBufferedBytes) {
+ mEncryptionContext->Update(mBuffer, mBufferedBytes);
+ mBufferedBytes = 0;
+ }
+ mEncryptionContext->Finish();
+ }
+
+ delete [] mBuffer;
+
+ PR_FREEIF(mMultipartSignedBoundary);
+}
+
+NS_IMETHODIMP nsMsgComposeSecure::RequiresCryptoEncapsulation(nsIMsgIdentity * aIdentity, nsIMsgCompFields * aCompFields, bool * aRequiresEncryptionWork)
+{
+ NS_ENSURE_ARG_POINTER(aRequiresEncryptionWork);
+
+ *aRequiresEncryptionWork = false;
+
+ bool alwaysEncryptMessages = false;
+ bool signMessage = false;
+ nsresult rv = ExtractEncryptionState(aIdentity, aCompFields, &signMessage, &alwaysEncryptMessages);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (alwaysEncryptMessages || signMessage)
+ *aRequiresEncryptionWork = true;
+
+ return NS_OK;
+}
+
+
+nsresult nsMsgComposeSecure::GetSMIMEBundleString(const char16_t *name,
+ nsString &outString)
+{
+ outString.Truncate();
+
+ NS_ENSURE_ARG_POINTER(name);
+
+ NS_ENSURE_TRUE(InitializeSMIMEBundle(), NS_ERROR_FAILURE);
+
+ return mSMIMEBundle->GetStringFromName(name, getter_Copies(outString));
+}
+
+nsresult
+nsMsgComposeSecure::
+SMIMEBundleFormatStringFromName(const char16_t *name,
+ const char16_t **params,
+ uint32_t numParams,
+ char16_t **outString)
+{
+ NS_ENSURE_ARG_POINTER(name);
+
+ if (!InitializeSMIMEBundle())
+ return NS_ERROR_FAILURE;
+
+ return mSMIMEBundle->FormatStringFromName(name, params,
+ numParams, outString);
+}
+
+bool nsMsgComposeSecure::InitializeSMIMEBundle()
+{
+ if (mSMIMEBundle)
+ return true;
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ nsresult rv = bundleService->CreateBundle(SMIME_STRBUNDLE_URL,
+ getter_AddRefs(mSMIMEBundle));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ return true;
+}
+
+void nsMsgComposeSecure::SetError(nsIMsgSendReport *sendReport, const char16_t *bundle_string)
+{
+ if (!sendReport || !bundle_string)
+ return;
+
+ if (mErrorAlreadyReported)
+ return;
+
+ mErrorAlreadyReported = true;
+
+ nsString errorString;
+ nsresult res = GetSMIMEBundleString(bundle_string, errorString);
+ if (NS_SUCCEEDED(res) && !errorString.IsEmpty())
+ {
+ sendReport->SetMessage(nsIMsgSendReport::process_Current,
+ errorString.get(),
+ true);
+ }
+}
+
+void nsMsgComposeSecure::SetErrorWithParam(nsIMsgSendReport *sendReport, const char16_t *bundle_string, const char *param)
+{
+ if (!sendReport || !bundle_string || !param)
+ return;
+
+ if (mErrorAlreadyReported)
+ return;
+
+ mErrorAlreadyReported = true;
+
+ nsString errorString;
+ nsresult res;
+ const char16_t *params[1];
+
+ NS_ConvertASCIItoUTF16 ucs2(param);
+ params[0]= ucs2.get();
+
+ res = SMIMEBundleFormatStringFromName(bundle_string,
+ params,
+ 1,
+ getter_Copies(errorString));
+
+ if (NS_SUCCEEDED(res) && !errorString.IsEmpty())
+ {
+ sendReport->SetMessage(nsIMsgSendReport::process_Current,
+ errorString.get(),
+ true);
+ }
+}
+
+nsresult nsMsgComposeSecure::ExtractEncryptionState(nsIMsgIdentity * aIdentity, nsIMsgCompFields * aComposeFields, bool * aSignMessage, bool * aEncrypt)
+{
+ if (!aComposeFields && !aIdentity)
+ return NS_ERROR_FAILURE; // kick out...invalid args....
+
+ NS_ENSURE_ARG_POINTER(aSignMessage);
+ NS_ENSURE_ARG_POINTER(aEncrypt);
+
+ nsCOMPtr<nsISupports> securityInfo;
+ if (aComposeFields)
+ aComposeFields->GetSecurityInfo(getter_AddRefs(securityInfo));
+
+ if (securityInfo) // if we were given security comp fields, use them.....
+ {
+ nsCOMPtr<nsIMsgSMIMECompFields> smimeCompFields = do_QueryInterface(securityInfo);
+ if (smimeCompFields)
+ {
+ smimeCompFields->GetSignMessage(aSignMessage);
+ smimeCompFields->GetRequireEncryptMessage(aEncrypt);
+ return NS_OK;
+ }
+ }
+
+ // get the default info from the identity....
+ int32_t ep = 0;
+ nsresult testrv = aIdentity->GetIntAttribute("encryptionpolicy", &ep);
+ if (NS_FAILED(testrv)) {
+ *aEncrypt = false;
+ }
+ else {
+ *aEncrypt = (ep > 0);
+ }
+
+ testrv = aIdentity->GetBoolAttribute("sign_mail", aSignMessage);
+ if (NS_FAILED(testrv))
+ {
+ *aSignMessage = false;
+ }
+ return NS_OK;
+}
+
+// Select a hash algorithm to sign message
+// based on subject public key type and size.
+static nsresult
+GetSigningHashFunction(nsIX509Cert *aSigningCert, int16_t *hashType)
+{
+ // Get the signing certificate
+ CERTCertificate *scert = nullptr;
+ if (aSigningCert) {
+ scert = aSigningCert->GetCert();
+ }
+ if (!scert) {
+ return NS_ERROR_FAILURE;
+ }
+
+ UniqueSECKEYPublicKey scertPublicKey(CERT_ExtractPublicKey(scert));
+ if (!scertPublicKey) {
+ return mozilla::MapSECStatus(SECFailure);
+ }
+ KeyType subjectPublicKeyType = SECKEY_GetPublicKeyType(scertPublicKey.get());
+
+ // Get the length of the signature in bits.
+ unsigned siglen = SECKEY_SignatureLen(scertPublicKey.get()) * 8;
+ if (!siglen) {
+ return mozilla::MapSECStatus(SECFailure);
+ }
+
+ // Select a hash function for signature generation whose security strength
+ // meets or exceeds the security strength of the public key, using NIST
+ // Special Publication 800-57, Recommendation for Key Management - Part 1:
+ // General (Revision 3), where Table 2 specifies the security strength of
+ // the public key and Table 3 lists acceptable hash functions. (The security
+ // strength of the hash (for digital signatures) is half the length of the
+ // output.)
+ // [SP 800-57 is available at http://csrc.nist.gov/publications/PubsSPs.html.]
+ if (subjectPublicKeyType == rsaKey) {
+ // For RSA, siglen is the same as the length of the modulus.
+
+ // SHA-1 provides equivalent security strength for up to 1024 bits
+ // SHA-256 provides equivalent security strength for up to 3072 bits
+
+ if (siglen > 3072) {
+ *hashType = nsICryptoHash::SHA512;
+ } else if (siglen > 1024) {
+ *hashType = nsICryptoHash::SHA256;
+ } else {
+ *hashType = nsICryptoHash::SHA1;
+ }
+ } else if (subjectPublicKeyType == dsaKey) {
+ // For DSA, siglen is twice the length of the q parameter of the key.
+ // The security strength of the key is half the length (in bits) of
+ // the q parameter of the key.
+
+ // NSS only supports SHA-1, SHA-224, and SHA-256 for DSA signatures.
+ // The S/MIME code does not support SHA-224.
+
+ if (siglen >= 512) { // 512-bit signature = 256-bit q parameter
+ *hashType = nsICryptoHash::SHA256;
+ } else {
+ *hashType = nsICryptoHash::SHA1;
+ }
+ } else if (subjectPublicKeyType == ecKey) {
+ // For ECDSA, siglen is twice the length of the field size. The security
+ // strength of the key is half the length (in bits) of the field size.
+
+ if (siglen >= 1024) { // 1024-bit signature = 512-bit field size
+ *hashType = nsICryptoHash::SHA512;
+ } else if (siglen >= 768) { // 768-bit signature = 384-bit field size
+ *hashType = nsICryptoHash::SHA384;
+ } else if (siglen >= 512) { // 512-bit signature = 256-bit field size
+ *hashType = nsICryptoHash::SHA256;
+ } else {
+ *hashType = nsICryptoHash::SHA1;
+ }
+ } else {
+ // Unknown key type
+ *hashType = nsICryptoHash::SHA256;
+ NS_WARNING("GetSigningHashFunction: Subject public key type unknown.");
+ }
+ return NS_OK;
+}
+
+/* void beginCryptoEncapsulation (in nsOutputFileStream aStream, in boolean aEncrypt, in boolean aSign, in string aRecipeints, in boolean aIsDraft); */
+NS_IMETHODIMP nsMsgComposeSecure::BeginCryptoEncapsulation(nsIOutputStream * aStream,
+ const char * aRecipients,
+ nsIMsgCompFields * aCompFields,
+ nsIMsgIdentity * aIdentity,
+ nsIMsgSendReport *sendReport,
+ bool aIsDraft)
+{
+ mErrorAlreadyReported = false;
+ nsresult rv = NS_OK;
+
+ bool encryptMessages = false;
+ bool signMessage = false;
+ ExtractEncryptionState(aIdentity, aCompFields, &signMessage, &encryptMessages);
+
+ if (!signMessage && !encryptMessages) return NS_ERROR_FAILURE;
+
+ mStream = aStream;
+ mIsDraft = aIsDraft;
+
+ if (encryptMessages && signMessage)
+ mCryptoState = mime_crypto_signed_encrypted;
+ else if (encryptMessages)
+ mCryptoState = mime_crypto_encrypted;
+ else if (signMessage)
+ mCryptoState = mime_crypto_clear_signed;
+ else
+ PR_ASSERT(0);
+
+ aIdentity->GetUnicharAttribute("signing_cert_name", mSigningCertName);
+ aIdentity->GetCharAttribute("signing_cert_dbkey", mSigningCertDBKey);
+ aIdentity->GetUnicharAttribute("encryption_cert_name", mEncryptionCertName);
+ aIdentity->GetCharAttribute("encryption_cert_dbkey", mEncryptionCertDBKey);
+
+ rv = MimeCryptoHackCerts(aRecipients, sendReport, encryptMessages, signMessage, aIdentity);
+ if (NS_FAILED(rv)) {
+ goto FAIL;
+ }
+
+ if (signMessage && mSelfSigningCert) {
+ rv = GetSigningHashFunction(mSelfSigningCert, &mHashType);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ switch (mCryptoState)
+ {
+ case mime_crypto_clear_signed:
+ rv = MimeInitMultipartSigned(true, sendReport);
+ break;
+ case mime_crypto_opaque_signed:
+ PR_ASSERT(0); /* #### no api for this yet */
+ rv = NS_ERROR_NOT_IMPLEMENTED;
+ break;
+ case mime_crypto_signed_encrypted:
+ rv = MimeInitEncryption(true, sendReport);
+ break;
+ case mime_crypto_encrypted:
+ rv = MimeInitEncryption(false, sendReport);
+ break;
+ case mime_crypto_none:
+ /* This can happen if mime_crypto_hack_certs() decided to turn off
+ encryption (by asking the user.) */
+ // XXX 1 is not a valid nsresult
+ rv = static_cast<nsresult>(1);
+ break;
+ default:
+ PR_ASSERT(0);
+ break;
+ }
+
+FAIL:
+ return rv;
+}
+
+/* void finishCryptoEncapsulation (in boolean aAbort); */
+NS_IMETHODIMP nsMsgComposeSecure::FinishCryptoEncapsulation(bool aAbort, nsIMsgSendReport *sendReport)
+{
+ nsresult rv = NS_OK;
+
+ if (!aAbort) {
+ switch (mCryptoState) {
+ case mime_crypto_clear_signed:
+ rv = MimeFinishMultipartSigned (true, sendReport);
+ break;
+ case mime_crypto_opaque_signed:
+ PR_ASSERT(0); /* #### no api for this yet */
+ rv = NS_ERROR_FAILURE;
+ break;
+ case mime_crypto_signed_encrypted:
+ rv = MimeFinishEncryption (true, sendReport);
+ break;
+ case mime_crypto_encrypted:
+ rv = MimeFinishEncryption (false, sendReport);
+ break;
+ default:
+ PR_ASSERT(0);
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ }
+ return rv;
+}
+
+nsresult nsMsgComposeSecure::MimeInitMultipartSigned(bool aOuter, nsIMsgSendReport *sendReport)
+{
+ /* First, construct and write out the multipart/signed MIME header data.
+ */
+ nsresult rv = NS_OK;
+ char *header = 0;
+ uint32_t L;
+
+ rv = make_multipart_signed_header_string(aOuter, &header,
+ &mMultipartSignedBoundary, mHashType);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ L = strlen(header);
+
+ if (aOuter){
+ /* If this is the outer block, write it to the file. */
+ uint32_t n;
+ rv = mStream->Write(header, L, &n);
+ if (NS_FAILED(rv) || n < L) {
+ // XXX This is -1, not an nsresult
+ rv = static_cast<nsresult>(MK_MIME_ERROR_WRITING_FILE);
+ }
+ } else {
+ /* If this is an inner block, feed it through the crypto stream. */
+ rv = MimeCryptoWriteBlock (header, L);
+ }
+
+ PR_Free(header);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ /* Now initialize the crypto library, so that we can compute a hash
+ on the object which we are signing.
+ */
+
+ PR_SetError(0,0);
+ mDataHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mDataHash->Init(mHashType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PR_SetError(0,0);
+ return rv;
+}
+
+nsresult nsMsgComposeSecure::MimeInitEncryption(bool aSign, nsIMsgSendReport *sendReport)
+{
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleSvc =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleSvc, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> sMIMEBundle;
+ nsString mime_smime_enc_content_desc;
+
+ bundleSvc->CreateBundle(SMIME_STRBUNDLE_URL, getter_AddRefs(sMIMEBundle));
+
+ if (!sMIMEBundle)
+ return NS_ERROR_FAILURE;
+
+ sMIMEBundle->GetStringFromName(u"mime_smimeEncryptedContentDesc",
+ getter_Copies(mime_smime_enc_content_desc));
+ NS_ConvertUTF16toUTF8 enc_content_desc_utf8(mime_smime_enc_content_desc);
+
+ nsCOMPtr<nsIMimeConverter> mimeConverter =
+ do_GetService(NS_MIME_CONVERTER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCString encodedContentDescription;
+ mimeConverter->EncodeMimePartIIStr_UTF8(enc_content_desc_utf8, false, "UTF-8",
+ sizeof("Content-Description: "),
+ nsIMimeConverter::MIME_ENCODED_WORD_SIZE,
+ encodedContentDescription);
+
+ /* First, construct and write out the opaque-crypto-blob MIME header data.
+ */
+
+ char *s =
+ PR_smprintf("Content-Type: " APPLICATION_PKCS7_MIME
+ "; name=\"smime.p7m\"; smime-type=enveloped-data" CRLF
+ "Content-Transfer-Encoding: " ENCODING_BASE64 CRLF
+ "Content-Disposition: attachment"
+ "; filename=\"smime.p7m\"" CRLF
+ "Content-Description: %s" CRLF
+ CRLF,
+ encodedContentDescription.get());
+
+ uint32_t L;
+ if (!s) return NS_ERROR_OUT_OF_MEMORY;
+ L = strlen(s);
+ uint32_t n;
+ rv = mStream->Write(s, L, &n);
+ if (NS_FAILED(rv) || n < L) {
+ return NS_ERROR_FAILURE;
+ }
+ PR_Free(s);
+ s = 0;
+
+ /* Now initialize the crypto library, so that we can filter the object
+ to be encrypted through it.
+ */
+
+ if (!mIsDraft) {
+ uint32_t numCerts;
+ mCerts->GetLength(&numCerts);
+ PR_ASSERT(numCerts > 0);
+ if (numCerts == 0) return NS_ERROR_FAILURE;
+ }
+
+ // Initialize the base64 encoder
+ MOZ_ASSERT(!mCryptoEncoder, "Shouldn't have an encoder already");
+ mCryptoEncoder = MimeEncoder::GetBase64Encoder(mime_encoder_output_fn,
+ this);
+
+ /* Initialize the encrypter (and add the sender's cert.) */
+ PR_ASSERT(mSelfEncryptionCert);
+ PR_SetError(0,0);
+ mEncryptionCinfo = do_CreateInstance(NS_CMSMESSAGE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+ rv = mEncryptionCinfo->CreateEncrypted(mCerts);
+ if (NS_FAILED(rv)) {
+ SetError(sendReport, u"ErrorEncryptMail");
+ goto FAIL;
+ }
+
+ mEncryptionContext = do_CreateInstance(NS_CMSENCODER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ if (!mBuffer) {
+ mBuffer = new char[eBufferSize];
+ if (!mBuffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ mBufferedBytes = 0;
+
+ rv = mEncryptionContext->Start(mEncryptionCinfo, mime_crypto_write_base64, mCryptoEncoder);
+ if (NS_FAILED(rv)) {
+ SetError(sendReport, u"ErrorEncryptMail");
+ goto FAIL;
+ }
+
+ /* If we're signing, tack a multipart/signed header onto the front of
+ the data to be encrypted, and initialize the sign-hashing code too.
+ */
+ if (aSign) {
+ rv = MimeInitMultipartSigned(false, sendReport);
+ if (NS_FAILED(rv)) goto FAIL;
+ }
+
+ FAIL:
+ return rv;
+}
+
+nsresult nsMsgComposeSecure::MimeFinishMultipartSigned (bool aOuter, nsIMsgSendReport *sendReport)
+{
+ int status;
+ nsresult rv;
+ nsCOMPtr<nsICMSMessage> cinfo = do_CreateInstance(NS_CMSMESSAGE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsICMSEncoder> encoder = do_CreateInstance(NS_CMSENCODER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ char * header = nullptr;
+ nsCOMPtr<nsIStringBundleService> bundleSvc =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleSvc, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> sMIMEBundle;
+ nsString mime_smime_sig_content_desc;
+
+ bundleSvc->CreateBundle(SMIME_STRBUNDLE_URL, getter_AddRefs(sMIMEBundle));
+
+ if (!sMIMEBundle)
+ return NS_ERROR_FAILURE;
+
+ sMIMEBundle->GetStringFromName(u"mime_smimeSignatureContentDesc",
+ getter_Copies(mime_smime_sig_content_desc));
+
+ NS_ConvertUTF16toUTF8 sig_content_desc_utf8(mime_smime_sig_content_desc);
+
+ /* Compute the hash...
+ */
+
+ nsAutoCString hashString;
+ mDataHash->Finish(false, hashString);
+
+ mDataHash = nullptr;
+
+ status = PR_GetError();
+ if (status < 0) goto FAIL;
+
+ /* Write out the headers for the signature.
+ */
+ uint32_t L;
+ header =
+ PR_smprintf(CRLF
+ "--%s" CRLF
+ "Content-Type: " APPLICATION_PKCS7_SIGNATURE
+ "; name=\"smime.p7s\"" CRLF
+ "Content-Transfer-Encoding: " ENCODING_BASE64 CRLF
+ "Content-Disposition: attachment; "
+ "filename=\"smime.p7s\"" CRLF
+ "Content-Description: %s" CRLF
+ CRLF,
+ mMultipartSignedBoundary,
+ sig_content_desc_utf8.get());
+
+ if (!header) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto FAIL;
+ }
+
+ L = strlen(header);
+ if (aOuter) {
+ /* If this is the outer block, write it to the file. */
+ uint32_t n;
+ rv = mStream->Write(header, L, &n);
+ if (NS_FAILED(rv) || n < L) {
+ // XXX This is -1, not an nsresult
+ rv = static_cast<nsresult>(MK_MIME_ERROR_WRITING_FILE);
+ }
+ } else {
+ /* If this is an inner block, feed it through the crypto stream. */
+ rv = MimeCryptoWriteBlock (header, L);
+ }
+
+ PR_Free(header);
+
+ /* Create the signature...
+ */
+
+ NS_ASSERTION(mHashType, "Hash function for signature has not been set.");
+
+ PR_ASSERT (mSelfSigningCert);
+ PR_SetError(0,0);
+
+ rv = cinfo->CreateSigned(mSelfSigningCert, mSelfEncryptionCert,
+ (unsigned char*)hashString.get(), hashString.Length(), mHashType);
+ if (NS_FAILED(rv)) {
+ SetError(sendReport, u"ErrorCanNotSignMail");
+ goto FAIL;
+ }
+
+ // Initialize the base64 encoder for the signature data.
+ MOZ_ASSERT(!mSigEncoder, "Shouldn't already have a mSigEncoder");
+ mSigEncoder = MimeEncoder::GetBase64Encoder(
+ (aOuter ? mime_encoder_output_fn : mime_nested_encoder_output_fn), this);
+
+ /* Write out the signature.
+ */
+ PR_SetError(0,0);
+ rv = encoder->Start(cinfo, mime_crypto_write_base64, mSigEncoder);
+ if (NS_FAILED(rv)) {
+ SetError(sendReport, u"ErrorCanNotSignMail");
+ goto FAIL;
+ }
+
+ // We're not passing in any data, so no update needed.
+ rv = encoder->Finish();
+ if (NS_FAILED(rv)) {
+ SetError(sendReport, u"ErrorCanNotSignMail");
+ goto FAIL;
+ }
+
+ // Shut down the sig's base64 encoder.
+ rv = mSigEncoder->Flush();
+ mSigEncoder = nullptr;
+ if (NS_FAILED(rv)) {
+ goto FAIL;
+ }
+
+ /* Now write out the terminating boundary.
+ */
+ {
+ uint32_t L;
+ char *header = PR_smprintf(CRLF "--%s--" CRLF,
+ mMultipartSignedBoundary);
+ PR_Free(mMultipartSignedBoundary);
+ mMultipartSignedBoundary = 0;
+
+ if (!header) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto FAIL;
+ }
+ L = strlen(header);
+ if (aOuter) {
+ /* If this is the outer block, write it to the file. */
+ uint32_t n;
+ rv = mStream->Write(header, L, &n);
+ if (NS_FAILED(rv) || n < L)
+ // XXX This is -1, not an nsresult
+ rv = static_cast<nsresult>(MK_MIME_ERROR_WRITING_FILE);
+ } else {
+ /* If this is an inner block, feed it through the crypto stream. */
+ rv = MimeCryptoWriteBlock (header, L);
+ }
+ }
+
+FAIL:
+ return rv;
+}
+
+
+/* Helper function for mime_finish_crypto_encapsulation() to close off
+ an opaque crypto object (for encrypted or signed-and-encrypted messages.)
+ */
+nsresult nsMsgComposeSecure::MimeFinishEncryption (bool aSign, nsIMsgSendReport *sendReport)
+{
+ nsresult rv;
+
+ /* If this object is both encrypted and signed, close off the
+ signature first (since it's inside.) */
+ if (aSign) {
+ rv = MimeFinishMultipartSigned (false, sendReport);
+ if (NS_FAILED(rv)) {
+ goto FAIL;
+ }
+ }
+
+ /* Close off the opaque encrypted blob.
+ */
+ PR_ASSERT(mEncryptionContext);
+
+ if (mBufferedBytes) {
+ rv = mEncryptionContext->Update(mBuffer, mBufferedBytes);
+ mBufferedBytes = 0;
+ if (NS_FAILED(rv)) {
+ PR_ASSERT(PR_GetError() < 0);
+ goto FAIL;
+ }
+ }
+
+ rv = mEncryptionContext->Finish();
+ if (NS_FAILED(rv)) {
+ SetError(sendReport, u"ErrorEncryptMail");
+ goto FAIL;
+ }
+
+ mEncryptionContext = nullptr;
+
+ PR_ASSERT(mEncryptionCinfo);
+ if (!mEncryptionCinfo) {
+ rv = NS_ERROR_FAILURE;
+ }
+ if (mEncryptionCinfo) {
+ mEncryptionCinfo = nullptr;
+ }
+
+ // Shut down the base64 encoder.
+ mCryptoEncoder->Flush();
+ mCryptoEncoder = nullptr;
+
+ uint32_t n;
+ rv = mStream->Write(CRLF, 2, &n);
+ if (NS_FAILED(rv) || n < 2)
+ rv = NS_ERROR_FAILURE;
+
+ FAIL:
+ return rv;
+}
+
+/* Used to figure out what certs should be used when encrypting this message.
+ */
+nsresult nsMsgComposeSecure::MimeCryptoHackCerts(const char *aRecipients,
+ nsIMsgSendReport *sendReport,
+ bool aEncrypt,
+ bool aSign,
+ nsIMsgIdentity *aIdentity)
+{
+ nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
+ nsresult res;
+
+ mCerts = do_CreateInstance(NS_ARRAY_CONTRACTID, &res);
+ if (NS_FAILED(res)) {
+ return res;
+ }
+
+ PR_ASSERT(aEncrypt || aSign);
+
+ /*
+ Signing and encryption certs use the following (per-identity) preferences:
+ - "signing_cert_name"/"encryption_cert_name": a string specifying the
+ nickname of the certificate
+ - "signing_cert_dbkey"/"encryption_cert_dbkey": a Base64 encoded blob
+ specifying an nsIX509Cert dbKey (represents serial number
+ and issuer DN, which is considered to be unique for X.509 certificates)
+
+ When retrieving the prefs, we try (in this order):
+ 1) *_cert_dbkey, if available
+ 2) *_cert_name (for maintaining backwards compatibility with preference
+ attributes written by earlier versions)
+ */
+
+ RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
+ NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
+
+ UniqueCERTCertList builtChain;
+ if (!mEncryptionCertDBKey.IsEmpty()) {
+ certdb->FindCertByDBKey(mEncryptionCertDBKey.get(),
+ getter_AddRefs(mSelfEncryptionCert));
+ if (mSelfEncryptionCert &&
+ (certVerifier->VerifyCert(mSelfEncryptionCert->GetCert(),
+ certificateUsageEmailRecipient,
+ mozilla::pkix::Now(),
+ nullptr, nullptr,
+ builtChain) != mozilla::pkix::Success)) {
+ // not suitable for encryption, so unset cert and clear pref
+ mSelfEncryptionCert = nullptr;
+ mEncryptionCertDBKey.Truncate();
+ aIdentity->SetCharAttribute("encryption_cert_dbkey",
+ mEncryptionCertDBKey);
+ }
+ }
+ if (!mSelfEncryptionCert) {
+ certdb->FindEmailEncryptionCert(mEncryptionCertName,
+ getter_AddRefs(mSelfEncryptionCert));
+ }
+
+ // same procedure for the signing cert
+ if (!mSigningCertDBKey.IsEmpty()) {
+ certdb->FindCertByDBKey(mSigningCertDBKey.get(),
+ getter_AddRefs(mSelfSigningCert));
+ if (mSelfSigningCert &&
+ (certVerifier->VerifyCert(mSelfSigningCert->GetCert(),
+ certificateUsageEmailSigner,
+ mozilla::pkix::Now(),
+ nullptr, nullptr,
+ builtChain) != mozilla::pkix::Success)) {
+ // not suitable for signing, so unset cert and clear pref
+ mSelfSigningCert = nullptr;
+ mSigningCertDBKey.Truncate();
+ aIdentity->SetCharAttribute("signing_cert_dbkey", mSigningCertDBKey);
+ }
+ }
+ if (!mSelfSigningCert) {
+ certdb->FindEmailSigningCert(mSigningCertName,
+ getter_AddRefs(mSelfSigningCert));
+ }
+
+ // must have both the signing and encryption certs to sign
+ if (!mSelfSigningCert && aSign) {
+ SetError(sendReport, u"NoSenderSigningCert");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mSelfEncryptionCert && aEncrypt) {
+ SetError(sendReport, u"NoSenderEncryptionCert");
+ return NS_ERROR_FAILURE;
+ }
+
+
+ if (aEncrypt && mSelfEncryptionCert) {
+ // Make sure self's configured cert is prepared for being used
+ // as an email recipient cert.
+ UniqueCERTCertificate nsscert(mSelfEncryptionCert->GetCert());
+ if (!nsscert) {
+ return NS_ERROR_FAILURE;
+ }
+ // XXX: This does not respect the nsNSSShutDownObject protocol.
+ if (CERT_SaveSMimeProfile(nsscert.get(), nullptr, nullptr) != SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ /* If the message is to be encrypted, then get the recipient certs */
+ if (aEncrypt) {
+ nsTArray<nsCString> mailboxes;
+ ExtractEmails(EncodedHeader(nsDependentCString(aRecipients)),
+ UTF16ArrayAdapter<>(mailboxes));
+ uint32_t count = mailboxes.Length();
+
+ bool already_added_self_cert = false;
+
+ for (uint32_t i = 0; i < count; i++) {
+ nsCString mailbox_lowercase;
+ ToLowerCase(mailboxes[i], mailbox_lowercase);
+ nsCOMPtr<nsIX509Cert> cert;
+ res = certdb->FindCertByEmailAddress(mailbox_lowercase.get(),
+ getter_AddRefs(cert));
+ if (NS_FAILED(res)) {
+ // Failure to find a valid encryption cert is fatal.
+ // Here I assume that mailbox is ascii rather than utf8.
+ SetErrorWithParam(sendReport,
+ u"MissingRecipientEncryptionCert",
+ mailboxes[i].get());
+
+ return res;
+ }
+
+ /* #### see if recipient requests `signedData'.
+ if (...) no_clearsigning_p = true;
+ (This is the only reason we even bother looking up the certs
+ of the recipients if we're sending a signed-but-not-encrypted
+ message.)
+ */
+
+ bool isSame;
+ if (NS_SUCCEEDED(cert->Equals(mSelfEncryptionCert, &isSame))
+ && isSame) {
+ already_added_self_cert = true;
+ }
+
+ mCerts->AppendElement(cert, false);
+ }
+
+ if (!already_added_self_cert) {
+ mCerts->AppendElement(mSelfEncryptionCert, false);
+ }
+ }
+ return res;
+}
+
+NS_IMETHODIMP nsMsgComposeSecure::MimeCryptoWriteBlock (const char *buf, int32_t size)
+{
+ int status = 0;
+ nsresult rv;
+
+ /* If this is a From line, mangle it before signing it. You just know
+ that something somewhere is going to mangle it later, and that's
+ going to cause the signature check to fail.
+
+ (This assumes that, in the cases where From-mangling must happen,
+ this function is called a line at a time. That happens to be the
+ case.)
+ */
+ if (size >= 5 && buf[0] == 'F' && !strncmp(buf, "From ", 5)) {
+ char mangle[] = ">";
+ nsresult res = MimeCryptoWriteBlock (mangle, 1);
+ if (NS_FAILED(res))
+ return res;
+ // This value will actually be cast back to an nsresult before use, so this
+ // cast is reasonable under the circumstances.
+ status = static_cast<int>(res);
+ }
+
+ /* If we're signing, or signing-and-encrypting, feed this data into
+ the computation of the hash. */
+ if (mDataHash) {
+ PR_SetError(0,0);
+ mDataHash->Update((const uint8_t*) buf, size);
+ status = PR_GetError();
+ if (status < 0) goto FAIL;
+ }
+
+ PR_SetError(0,0);
+ if (mEncryptionContext) {
+ /* If we're encrypting, or signing-and-encrypting, write this data
+ by filtering it through the crypto library. */
+
+ /* We want to create equally sized encryption strings */
+ const char *inputBytesIterator = buf;
+ uint32_t inputBytesLeft = size;
+
+ while (inputBytesLeft) {
+ const uint32_t spaceLeftInBuffer = eBufferSize - mBufferedBytes;
+ const uint32_t bytesToAppend = std::min(inputBytesLeft, spaceLeftInBuffer);
+
+ memcpy(mBuffer+mBufferedBytes, inputBytesIterator, bytesToAppend);
+ mBufferedBytes += bytesToAppend;
+
+ inputBytesIterator += bytesToAppend;
+ inputBytesLeft -= bytesToAppend;
+
+ if (eBufferSize == mBufferedBytes) {
+ rv = mEncryptionContext->Update(mBuffer, mBufferedBytes);
+ mBufferedBytes = 0;
+ if (NS_FAILED(rv)) {
+ status = PR_GetError();
+ PR_ASSERT(status < 0);
+ if (status >= 0) status = -1;
+ goto FAIL;
+ }
+ }
+ }
+ } else {
+ /* If we're not encrypting (presumably just signing) then write this
+ data directly to the file. */
+
+ uint32_t n;
+ rv = mStream->Write(buf, size, &n);
+ if (NS_FAILED(rv) || n < (uint32_t)size) {
+ // XXX MK_MIME_ERROR_WRITING_FILE is -1, which is not a valid nsresult
+ return static_cast<nsresult>(MK_MIME_ERROR_WRITING_FILE);
+ }
+ }
+ FAIL:
+ // XXX status sometimes has invalid nsresults like -1 or PR_GetError()
+ // assigned to it
+ return static_cast<nsresult>(status);
+}
+
+/* Returns a string consisting of a Content-Type header, and a boundary
+ string, suitable for moving from the header block, down into the body
+ of a multipart object. The boundary itself is also returned (so that
+ the caller knows what to write to close it off.)
+ */
+static nsresult
+make_multipart_signed_header_string(bool outer_p,
+ char **header_return,
+ char **boundary_return,
+ int16_t hash_type)
+{
+ const char *hashStr;
+ *header_return = 0;
+ *boundary_return = mime_make_separator("ms");
+
+ if (!*boundary_return)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ switch (hash_type) {
+ case nsICryptoHash::SHA1:
+ hashStr = PARAM_MICALG_SHA1;
+ break;
+ case nsICryptoHash::SHA256:
+ hashStr = PARAM_MICALG_SHA256;
+ break;
+ case nsICryptoHash::SHA384:
+ hashStr = PARAM_MICALG_SHA384;
+ break;
+ case nsICryptoHash::SHA512:
+ hashStr = PARAM_MICALG_SHA512;
+ break;
+ default:
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *header_return = PR_smprintf(
+ "Content-Type: " MULTIPART_SIGNED "; "
+ "protocol=\"" APPLICATION_PKCS7_SIGNATURE "\"; "
+ "micalg=%s; "
+ "boundary=\"%s\"" CRLF
+ CRLF
+ "%s%s"
+ "--%s" CRLF,
+ hashStr,
+ *boundary_return,
+ (outer_p ? crypto_multipart_blurb : ""),
+ (outer_p ? CRLF CRLF : ""),
+ *boundary_return);
+
+ if (!*header_return) {
+ PR_Free(*boundary_return);
+ *boundary_return = 0;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+/* Used as the output function of a SEC_PKCS7EncoderContext -- we feed
+ plaintext into the crypto engine, and it calls this function with encrypted
+ data; then this function writes a base64-encoded representation of that
+ data to the file (by filtering it through the given MimeEncoder object.)
+
+ Also used as the output function of SEC_PKCS7Encode() -- but in that case,
+ it's used to write the encoded representation of the signature. The only
+ difference is which MimeEncoder object is used.
+ */
+static void
+mime_crypto_write_base64 (void *closure, const char *buf, unsigned long size)
+{
+ MimeEncoder *encoder = (MimeEncoder *) closure;
+ nsresult rv = encoder->Write(buf, size);
+ PR_SetError(NS_FAILED(rv) ? static_cast<uint32_t>(rv) : 0, 0);
+}
+
+
+/* Used as the output function of MimeEncoder -- when we have generated
+ the signature for a multipart/signed object, this is used to write the
+ base64-encoded representation of the signature to the file.
+ */
+// TODO: size should probably be converted to uint32_t
+nsresult mime_encoder_output_fn(const char *buf, int32_t size, void *closure)
+{
+ nsMsgComposeSecure *state = (nsMsgComposeSecure *) closure;
+ nsCOMPtr<nsIOutputStream> stream;
+ state->GetOutputStream(getter_AddRefs(stream));
+ uint32_t n;
+ nsresult rv = stream->Write((char *) buf, size, &n);
+ if (NS_FAILED(rv) || n < (uint32_t)size)
+ return NS_ERROR_FAILURE;
+ else
+ return NS_OK;
+}
+
+/* Like mime_encoder_output_fn, except this is used for the case where we
+ are both signing and encrypting -- the base64-encoded output of the
+ signature should be fed into the crypto engine, rather than being written
+ directly to the file.
+ */
+static nsresult
+mime_nested_encoder_output_fn (const char *buf, int32_t size, void *closure)
+{
+ nsMsgComposeSecure *state = (nsMsgComposeSecure *) closure;
+
+ // Copy to new null-terminated string so JS glue doesn't crash when
+ // MimeCryptoWriteBlock() is implemented in JS.
+ nsCString bufWithNull;
+ bufWithNull.Assign(buf, size);
+ return state->MimeCryptoWriteBlock(bufWithNull.get(), size);
+}
diff --git a/mailnews/extensions/smime/src/nsMsgComposeSecure.h b/mailnews/extensions/smime/src/nsMsgComposeSecure.h
new file mode 100644
index 000000000..0f3b9ac60
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsMsgComposeSecure.h
@@ -0,0 +1,106 @@
+/* -*- Mode: idl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _nsMsgComposeSecure_H_
+#define _nsMsgComposeSecure_H_
+
+#include "nsIMsgComposeSecure.h"
+#include "nsIMsgSMIMECompFields.h"
+#include "nsCOMPtr.h"
+#include "nsICMSEncoder.h"
+#include "nsIX509Cert.h"
+#include "nsIStringBundle.h"
+#include "nsICryptoHash.h"
+#include "nsICMSMessage.h"
+#include "nsIMutableArray.h"
+#include "nsStringGlue.h"
+#include "nsIOutputStream.h"
+#include "nsAutoPtr.h"
+
+class nsIMsgCompFields;
+namespace mozilla {
+namespace mailnews {
+class MimeEncoder;
+}
+}
+
+class nsMsgSMIMEComposeFields : public nsIMsgSMIMECompFields
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGSMIMECOMPFIELDS
+
+ nsMsgSMIMEComposeFields();
+
+private:
+ virtual ~nsMsgSMIMEComposeFields();
+ bool mSignMessage;
+ bool mAlwaysEncryptMessage;
+};
+
+typedef enum {
+ mime_crypto_none, /* normal unencapsulated MIME message */
+ mime_crypto_clear_signed, /* multipart/signed encapsulation */
+ mime_crypto_opaque_signed, /* application/x-pkcs7-mime (signedData) */
+ mime_crypto_encrypted, /* application/x-pkcs7-mime */
+ mime_crypto_signed_encrypted /* application/x-pkcs7-mime */
+} mimeDeliveryCryptoState;
+
+class nsMsgComposeSecure : public nsIMsgComposeSecure
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGCOMPOSESECURE
+
+ nsMsgComposeSecure();
+
+ void GetOutputStream(nsIOutputStream **stream) { NS_IF_ADDREF(*stream = mStream);}
+ nsresult GetSMIMEBundleString(const char16_t *name, nsString &outString);
+
+private:
+ virtual ~nsMsgComposeSecure();
+ typedef mozilla::mailnews::MimeEncoder MimeEncoder;
+ nsresult MimeInitMultipartSigned(bool aOuter, nsIMsgSendReport *sendReport);
+ nsresult MimeInitEncryption(bool aSign, nsIMsgSendReport *sendReport);
+ nsresult MimeFinishMultipartSigned (bool aOuter, nsIMsgSendReport *sendReport);
+ nsresult MimeFinishEncryption (bool aSign, nsIMsgSendReport *sendReport);
+ nsresult MimeCryptoHackCerts(const char *aRecipients, nsIMsgSendReport *sendReport, bool aEncrypt, bool aSign, nsIMsgIdentity *aIdentity);
+ bool InitializeSMIMEBundle();
+ nsresult SMIMEBundleFormatStringFromName(const char16_t *name,
+ const char16_t **params,
+ uint32_t numParams,
+ char16_t **outString);
+ nsresult ExtractEncryptionState(nsIMsgIdentity * aIdentity, nsIMsgCompFields * aComposeFields, bool * aSignMessage, bool * aEncrypt);
+
+ mimeDeliveryCryptoState mCryptoState;
+ nsCOMPtr<nsIOutputStream> mStream;
+ int16_t mHashType;
+ nsCOMPtr<nsICryptoHash> mDataHash;
+ nsAutoPtr<MimeEncoder> mSigEncoder;
+ char *mMultipartSignedBoundary;
+ nsString mSigningCertName;
+ nsAutoCString mSigningCertDBKey;
+ nsCOMPtr<nsIX509Cert> mSelfSigningCert;
+ nsString mEncryptionCertName;
+ nsAutoCString mEncryptionCertDBKey;
+ nsCOMPtr<nsIX509Cert> mSelfEncryptionCert;
+ nsCOMPtr<nsIMutableArray> mCerts;
+ nsCOMPtr<nsICMSMessage> mEncryptionCinfo;
+ nsCOMPtr<nsICMSEncoder> mEncryptionContext;
+ nsCOMPtr<nsIStringBundle> mSMIMEBundle;
+
+ nsAutoPtr<MimeEncoder> mCryptoEncoder;
+ bool mIsDraft;
+
+ enum {eBufferSize = 8192};
+ char *mBuffer;
+ uint32_t mBufferedBytes;
+
+ bool mErrorAlreadyReported;
+ void SetError(nsIMsgSendReport *sendReport, const char16_t *bundle_string);
+ void SetErrorWithParam(nsIMsgSendReport *sendReport, const char16_t *bundle_string, const char *param);
+};
+
+#endif
diff --git a/mailnews/extensions/smime/src/nsMsgSMIMECID.h b/mailnews/extensions/smime/src/nsMsgSMIMECID.h
new file mode 100644
index 000000000..0fbf2d1bf
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsMsgSMIMECID.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMsgSMIMECID_h__
+#define nsMsgSMIMECID_h__
+
+#include "nsISupports.h"
+#include "nsIFactory.h"
+#include "nsIComponentManager.h"
+
+#define NS_MSGSMIMECOMPFIELDS_CONTRACTID \
+ "@mozilla.org/messenger-smime/composefields;1"
+
+#define NS_MSGSMIMECOMPFIELDS_CID \
+{ /* 122C919C-96B7-49a0-BBC8-0ABC67EEFFE0 */ \
+ 0x122c919c, 0x96b7, 0x49a0, \
+ { 0xbb, 0xc8, 0xa, 0xbc, 0x67, 0xee, 0xff, 0xe0 }}
+
+#define NS_MSGCOMPOSESECURE_CID \
+{ /* dd753201-9a23-4e08-957f-b3616bf7e012 */ \
+ 0xdd753201, 0x9a23, 0x4e08, \
+ {0x95, 0x7f, 0xb3, 0x61, 0x6b, 0xf7, 0xe0, 0x12 }}
+
+#define NS_SMIMEJSHELPER_CONTRACTID \
+ "@mozilla.org/messenger-smime/smimejshelper;1"
+
+#define NS_SMIMEJSJELPER_CID \
+{ /* d57d928c-60e4-4f81-999d-5c762e611205 */ \
+ 0xd57d928c, 0x60e4, 0x4f81, \
+ {0x99, 0x9d, 0x5c, 0x76, 0x2e, 0x61, 0x12, 0x05 }}
+
+#define NS_SMIMEENCRYPTURISERVICE_CONTRACTID \
+ "@mozilla.org/messenger-smime/smime-encrypted-uris-service;1"
+
+#define NS_SMIMEENCRYPTURISERVICE_CID \
+{ /* a0134d58-018f-4d40-a099-fa079e5024a6 */ \
+ 0xa0134d58, 0x018f, 0x4d40, \
+ {0xa0, 0x99, 0xfa, 0x07, 0x9e, 0x50, 0x24, 0xa6 }}
+
+#endif // nsMsgSMIMECID_h__
diff --git a/mailnews/extensions/smime/src/nsSMimeJSHelper.cpp b/mailnews/extensions/smime/src/nsSMimeJSHelper.cpp
new file mode 100644
index 000000000..c392980b6
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsSMimeJSHelper.cpp
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/mailnews/MimeHeaderParser.h"
+#include "nspr.h"
+#include "nsSMimeJSHelper.h"
+#include "nsCOMPtr.h"
+#include "nsMemory.h"
+#include "nsStringGlue.h"
+#include "nsIX509CertDB.h"
+#include "nsIX509CertValidity.h"
+#include "nsIServiceManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsCRTGlue.h"
+
+using namespace mozilla::mailnews;
+
+NS_IMPL_ISUPPORTS(nsSMimeJSHelper, nsISMimeJSHelper)
+
+nsSMimeJSHelper::nsSMimeJSHelper()
+{
+}
+
+nsSMimeJSHelper::~nsSMimeJSHelper()
+{
+}
+
+NS_IMETHODIMP nsSMimeJSHelper::GetRecipientCertsInfo(
+ nsIMsgCompFields *compFields,
+ uint32_t *count,
+ char16_t ***emailAddresses,
+ int32_t **certVerification,
+ char16_t ***certIssuedInfos,
+ char16_t ***certExpiresInfos,
+ nsIX509Cert ***certs,
+ bool *canEncrypt)
+{
+ NS_ENSURE_ARG_POINTER(count);
+ *count = 0;
+
+ NS_ENSURE_ARG_POINTER(emailAddresses);
+ NS_ENSURE_ARG_POINTER(certVerification);
+ NS_ENSURE_ARG_POINTER(certIssuedInfos);
+ NS_ENSURE_ARG_POINTER(certExpiresInfos);
+ NS_ENSURE_ARG_POINTER(certs);
+ NS_ENSURE_ARG_POINTER(canEncrypt);
+
+ NS_ENSURE_ARG_POINTER(compFields);
+
+ nsTArray<nsCString> mailboxes;
+ nsresult rv = getMailboxList(compFields, mailboxes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t mailbox_count = mailboxes.Length();
+
+ nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
+
+ *count = mailbox_count;
+ *canEncrypt = false;
+ rv = NS_OK;
+
+ if (mailbox_count)
+ {
+ char16_t **outEA = static_cast<char16_t **>(moz_xmalloc(mailbox_count * sizeof(char16_t *)));
+ int32_t *outCV = static_cast<int32_t *>(moz_xmalloc(mailbox_count * sizeof(int32_t)));
+ char16_t **outCII = static_cast<char16_t **>(moz_xmalloc(mailbox_count * sizeof(char16_t *)));
+ char16_t **outCEI = static_cast<char16_t **>(moz_xmalloc(mailbox_count * sizeof(char16_t *)));
+ nsIX509Cert **outCerts = static_cast<nsIX509Cert **>(moz_xmalloc(mailbox_count * sizeof(nsIX509Cert *)));
+
+ if (!outEA || !outCV || !outCII || !outCEI || !outCerts)
+ {
+ free(outEA);
+ free(outCV);
+ free(outCII);
+ free(outCEI);
+ free(outCerts);
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ else
+ {
+ char16_t **iEA = outEA;
+ int32_t *iCV = outCV;
+ char16_t **iCII = outCII;
+ char16_t **iCEI = outCEI;
+ nsIX509Cert **iCert = outCerts;
+
+ bool found_blocker = false;
+ bool memory_failure = false;
+
+ for (uint32_t i = 0;
+ i < mailbox_count;
+ ++i, ++iEA, ++iCV, ++iCII, ++iCEI, ++iCert)
+ {
+ *iCert = nullptr;
+ *iCV = 0;
+ *iCII = nullptr;
+ *iCEI = nullptr;
+
+ if (memory_failure) {
+ *iEA = nullptr;
+ continue;
+ }
+
+ nsCString &email = mailboxes[i];
+ *iEA = ToNewUnicode(NS_ConvertUTF8toUTF16(email));
+ if (!*iEA) {
+ memory_failure = true;
+ continue;
+ }
+
+ nsCString email_lowercase;
+ ToLowerCase(email, email_lowercase);
+
+ nsCOMPtr<nsIX509Cert> cert;
+ if (NS_SUCCEEDED(certdb->FindCertByEmailAddress(
+ email_lowercase.get(), getter_AddRefs(cert))))
+ {
+ *iCert = cert;
+ NS_ADDREF(*iCert);
+
+ nsCOMPtr<nsIX509CertValidity> validity;
+ rv = cert->GetValidity(getter_AddRefs(validity));
+
+ if (NS_SUCCEEDED(rv)) {
+ nsString id, ed;
+
+ if (NS_SUCCEEDED(validity->GetNotBeforeLocalDay(id)))
+ {
+ *iCII = ToNewUnicode(id);
+ if (!*iCII) {
+ memory_failure = true;
+ continue;
+ }
+ }
+
+ if (NS_SUCCEEDED(validity->GetNotAfterLocalDay(ed)))
+ {
+ *iCEI = ToNewUnicode(ed);
+ if (!*iCEI) {
+ memory_failure = true;
+ continue;
+ }
+ }
+ }
+ }
+ else
+ {
+ found_blocker = true;
+ }
+ }
+
+ if (memory_failure) {
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mailbox_count, outEA);
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mailbox_count, outCII);
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mailbox_count, outCEI);
+ NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(mailbox_count, outCerts);
+ free(outCV);
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ else {
+ if (mailbox_count > 0 && !found_blocker)
+ {
+ *canEncrypt = true;
+ }
+
+ *emailAddresses = outEA;
+ *certVerification = outCV;
+ *certIssuedInfos = outCII;
+ *certExpiresInfos = outCEI;
+ *certs = outCerts;
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsSMimeJSHelper::GetNoCertAddresses(
+ nsIMsgCompFields *compFields,
+ uint32_t *count,
+ char16_t ***emailAddresses)
+{
+ NS_ENSURE_ARG_POINTER(count);
+ *count = 0;
+
+ NS_ENSURE_ARG_POINTER(emailAddresses);
+
+ NS_ENSURE_ARG_POINTER(compFields);
+
+ nsTArray<nsCString> mailboxes;
+ nsresult rv = getMailboxList(compFields, mailboxes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t mailbox_count = mailboxes.Length();
+
+ if (!mailbox_count)
+ {
+ *count = 0;
+ *emailAddresses = nullptr;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
+
+ uint32_t missing_count = 0;
+ bool *haveCert = new bool[mailbox_count];
+ if (!haveCert)
+ {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = NS_OK;
+
+ if (mailbox_count)
+ {
+ for (uint32_t i = 0; i < mailbox_count; ++i)
+ {
+ haveCert[i] = false;
+
+ nsCString email_lowercase;
+ ToLowerCase(mailboxes[i], email_lowercase);
+
+ nsCOMPtr<nsIX509Cert> cert;
+ if (NS_SUCCEEDED(certdb->FindCertByEmailAddress(
+ email_lowercase.get(), getter_AddRefs(cert))))
+ haveCert[i] = true;
+
+ if (!haveCert[i])
+ ++missing_count;
+ }
+ }
+
+ *count = missing_count;
+
+ if (missing_count)
+ {
+ char16_t **outEA = static_cast<char16_t **>(moz_xmalloc(missing_count * sizeof(char16_t *)));
+ if (!outEA )
+ {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ else
+ {
+ char16_t **iEA = outEA;
+
+ bool memory_failure = false;
+
+ for (uint32_t i = 0; i < mailbox_count; ++i)
+ {
+ if (!haveCert[i])
+ {
+ if (memory_failure) {
+ *iEA = nullptr;
+ }
+ else {
+ *iEA = ToNewUnicode(NS_ConvertUTF8toUTF16(mailboxes[i]));
+ if (!*iEA) {
+ memory_failure = true;
+ }
+ }
+ ++iEA;
+ }
+ }
+
+ if (memory_failure) {
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(missing_count, outEA);
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ else {
+ *emailAddresses = outEA;
+ }
+ }
+ }
+ else
+ {
+ *emailAddresses = nullptr;
+ }
+
+ delete [] haveCert;
+ return rv;
+}
+
+nsresult nsSMimeJSHelper::getMailboxList(nsIMsgCompFields *compFields,
+ nsTArray<nsCString> &mailboxes)
+{
+ if (!compFields)
+ return NS_ERROR_INVALID_ARG;
+
+ nsresult res;
+ nsString to, cc, bcc, ng;
+
+ res = compFields->GetTo(to);
+ if (NS_FAILED(res))
+ return res;
+
+ res = compFields->GetCc(cc);
+ if (NS_FAILED(res))
+ return res;
+
+ res = compFields->GetBcc(bcc);
+ if (NS_FAILED(res))
+ return res;
+
+ res = compFields->GetNewsgroups(ng);
+ if (NS_FAILED(res))
+ return res;
+
+ {
+ nsCString all_recipients;
+
+ if (!to.IsEmpty()) {
+ all_recipients.Append(NS_ConvertUTF16toUTF8(to));
+ all_recipients.Append(',');
+ }
+
+ if (!cc.IsEmpty()) {
+ all_recipients.Append(NS_ConvertUTF16toUTF8(cc));
+ all_recipients.Append(',');
+ }
+
+ if (!bcc.IsEmpty()) {
+ all_recipients.Append(NS_ConvertUTF16toUTF8(bcc));
+ all_recipients.Append(',');
+ }
+
+ if (!ng.IsEmpty())
+ all_recipients.Append(NS_ConvertUTF16toUTF8(ng));
+
+ ExtractEmails(EncodedHeader(all_recipients),
+ UTF16ArrayAdapter<>(mailboxes));
+ }
+
+ return NS_OK;
+}
diff --git a/mailnews/extensions/smime/src/nsSMimeJSHelper.h b/mailnews/extensions/smime/src/nsSMimeJSHelper.h
new file mode 100644
index 000000000..403ab2098
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsSMimeJSHelper.h
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsSMimeJSHelper_H_
+#define _nsSMimeJSHelper_H_
+
+#include "nsISMimeJSHelper.h"
+#include "nsIX509Cert.h"
+#include "nsIMsgCompFields.h"
+
+class nsSMimeJSHelper : public nsISMimeJSHelper
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISMIMEJSHELPER
+
+ nsSMimeJSHelper();
+
+private:
+ virtual ~nsSMimeJSHelper();
+ nsresult getMailboxList(nsIMsgCompFields *compFields,
+ nsTArray<nsCString> &mailboxes);
+};
+
+#endif
diff --git a/mailnews/extensions/smime/src/smime-service.js b/mailnews/extensions/smime/src/smime-service.js
new file mode 100644
index 000000000..9fa618fd6
--- /dev/null
+++ b/mailnews/extensions/smime/src/smime-service.js
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function SMIMEService() {}
+
+SMIMEService.prototype = {
+ name: "smime",
+ chromePackageName: "messenger",
+ showPanel: function(server) {
+ // don't show the panel for news, rss, or local accounts
+ return (server.type != "nntp" && server.type != "rss" &&
+ server.type != "im" && server.type != "none");
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIMsgAccountManagerExtension]),
+ classID: Components.ID("{f2809796-1dd1-11b2-8c1b-8f15f007c699}"),
+};
+
+var components = [SMIMEService];
+var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
diff --git a/mailnews/extensions/smime/src/smime-service.manifest b/mailnews/extensions/smime/src/smime-service.manifest
new file mode 100644
index 000000000..1b799ed7b
--- /dev/null
+++ b/mailnews/extensions/smime/src/smime-service.manifest
@@ -0,0 +1,3 @@
+component {f2809796-1dd1-11b2-8c1b-8f15f007c699} smime-service.js
+contract @mozilla.org/accountmanager/extension;1?name=smime {f2809796-1dd1-11b2-8c1b-8f15f007c699}
+category mailnews-accountmanager-extensions smime-account-manager-extension @mozilla.org/accountmanager/extension;1?name=smime