summaryrefslogtreecommitdiffstats
path: root/mailnews/mime/src/nsCMSSecureMessage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/mime/src/nsCMSSecureMessage.cpp')
-rw-r--r--mailnews/mime/src/nsCMSSecureMessage.cpp363
1 files changed, 363 insertions, 0 deletions
diff --git a/mailnews/mime/src/nsCMSSecureMessage.cpp b/mailnews/mime/src/nsCMSSecureMessage.cpp
new file mode 100644
index 000000000..0b7a99d7d
--- /dev/null
+++ b/mailnews/mime/src/nsCMSSecureMessage.cpp
@@ -0,0 +1,363 @@
+/* -*- 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 "nsXPIDLString.h"
+#include "nsCOMPtr.h"
+#include "nsISupports.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsCRT.h"
+#include "nsIX509CertDB.h"
+
+#include "nsICMSSecureMessage.h"
+
+#include "nsCMSSecureMessage.h"
+#include "nsIX509Cert.h"
+#include "nsNSSHelper.h"
+#include "nsNSSCertificate.h"
+#include "nsNSSShutDown.h"
+
+#include <string.h>
+#include "plbase64.h"
+#include "cert.h"
+#include "cms.h"
+
+#include "nsIServiceManager.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+
+#include "mozilla/Logging.h"
+#ifdef PR_LOGGING
+extern mozilla::LazyLogModule gPIPNSSLog;
+#endif
+
+using namespace mozilla;
+
+// Standard ISupports implementation
+// NOTE: Should these be the thread-safe versions?
+
+/*****
+ * nsCMSSecureMessage
+ *****/
+
+// Standard ISupports implementation
+NS_IMPL_ISUPPORTS(nsCMSSecureMessage, nsICMSSecureMessage)
+
+// nsCMSSecureMessage constructor
+nsCMSSecureMessage::nsCMSSecureMessage()
+{
+ // initialize superclass
+}
+
+// nsCMSMessage destructor
+nsCMSSecureMessage::~nsCMSSecureMessage()
+{
+}
+
+nsresult nsCMSSecureMessage::Init()
+{
+ nsresult rv;
+ nsCOMPtr<nsISupports> nssInitialized = do_GetService("@mozilla.org/psm;1", &rv);
+ return rv;
+}
+
+/* string getCertByPrefID (in string certID); */
+NS_IMETHODIMP nsCMSSecureMessage::
+GetCertByPrefID(const char *certID, char **_retval)
+{
+ nsNSSShutDownPreventionLock locker;
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::GetCertByPrefID\n"));
+ nsresult rv = NS_OK;
+ CERTCertificate *cert = 0;
+ nsXPIDLCString nickname;
+ nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
+
+ *_retval = 0;
+
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ goto done;
+ }
+
+ rv = prefs->GetCharPref(certID,
+ getter_Copies(nickname));
+ if (NS_FAILED(rv)) goto done;
+
+ /* Find a good cert in the user's database */
+ cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), const_cast<char*>(nickname.get()),
+ certUsageEmailRecipient, true, ctx);
+
+ if (!cert) {
+ /* Success, but no value */
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::GetCertByPrefID - can't find user cert\n"));
+ goto done;
+ }
+
+ /* Convert the DER to a BASE64 String */
+ encode(cert->derCert.data, cert->derCert.len, _retval);
+
+done:
+ if (cert) CERT_DestroyCertificate(cert);
+ return rv;
+}
+
+
+// nsCMSSecureMessage::DecodeCert
+nsresult nsCMSSecureMessage::
+DecodeCert(const char *value, nsIX509Cert ** _retval)
+{
+ nsNSSShutDownPreventionLock locker;
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::DecodeCert\n"));
+ nsresult rv = NS_OK;
+ int32_t length;
+ unsigned char *data = 0;
+
+ *_retval = 0;
+
+ if (!value) { return NS_ERROR_FAILURE; }
+
+ rv = decode(value, &data, &length);
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::DecodeCert - can't decode cert\n"));
+ return rv;
+ }
+
+ nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
+ if (!certdb) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIX509Cert> cert;
+ certdb->ConstructX509(reinterpret_cast<char *>(data), length, getter_AddRefs(cert));
+
+ if (cert) {
+ *_retval = cert;
+ NS_ADDREF(*_retval);
+ }
+ else {
+ rv = NS_ERROR_FAILURE;
+ }
+
+ free((char*)data);
+ return rv;
+}
+
+// nsCMSSecureMessage::SendMessage
+nsresult nsCMSSecureMessage::
+SendMessage(const char *msg, const char *base64Cert, char ** _retval)
+{
+ nsNSSShutDownPreventionLock locker;
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage\n"));
+ nsresult rv = NS_OK;
+ CERTCertificate *cert = 0;
+ NSSCMSMessage *cmsMsg = 0;
+ unsigned char *certDER = 0;
+ int32_t derLen;
+ NSSCMSEnvelopedData *env;
+ NSSCMSContentInfo *cinfo;
+ NSSCMSRecipientInfo *rcpt;
+ SECItem output;
+ PLArenaPool *arena = PORT_NewArena(1024);
+ SECStatus s;
+ nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
+
+ /* Step 0. Create a CMS Message */
+ cmsMsg = NSS_CMSMessage_Create(nullptr);
+ if (!cmsMsg) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't create NSSCMSMessage\n"));
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ /* Step 1. Import the certificate into NSS */
+ rv = decode(base64Cert, &certDER, &derLen);
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't decode / import cert into NSS\n"));
+ goto done;
+ }
+
+ cert = CERT_DecodeCertFromPackage((char *)certDER, derLen);
+ if (!cert) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't decode cert from package\n"));
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ /* Step 2. Get a signature cert */
+
+ /* Step 3. Build inner (signature) content */
+
+ /* Step 4. Build outer (enveloped) content */
+ env = NSS_CMSEnvelopedData_Create(cmsMsg, SEC_OID_DES_EDE3_CBC, 0);
+ if (!env) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't create envelope data\n"));
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ cinfo = NSS_CMSEnvelopedData_GetContentInfo(env);
+ s = NSS_CMSContentInfo_SetContent_Data(cmsMsg, cinfo, 0, false);
+ if (s != SECSuccess) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't set content data\n"));
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ rcpt = NSS_CMSRecipientInfo_Create(cmsMsg, cert);
+ if (!rcpt) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't create recipient info\n"));
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ s = NSS_CMSEnvelopedData_AddRecipient(env, rcpt);
+ if (s != SECSuccess) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't add recipient\n"));
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ /* Step 5. Add content to message */
+ cinfo = NSS_CMSMessage_GetContentInfo(cmsMsg);
+ s = NSS_CMSContentInfo_SetContent_EnvelopedData(cmsMsg, cinfo, env);
+ if (s != SECSuccess) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't set content enveloped data\n"));
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ /* Step 6. Encode */
+ NSSCMSEncoderContext *ecx;
+
+ output.data = 0; output.len = 0;
+ ecx = NSS_CMSEncoder_Start(cmsMsg, 0, 0, &output, arena,
+ 0, ctx, 0, 0, 0, 0);
+ if (!ecx) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't start cms encoder\n"));
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ s = NSS_CMSEncoder_Update(ecx, msg, strlen(msg));
+ if (s != SECSuccess) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't update encoder\n"));
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ s = NSS_CMSEncoder_Finish(ecx);
+ if (s != SECSuccess) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::SendMessage - can't finish encoder\n"));
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ /* Step 7. Base64 encode and return the result */
+ rv = encode(output.data, output.len, _retval);
+
+done:
+ if (certDER) free((char *)certDER);
+ if (cert) CERT_DestroyCertificate(cert);
+ if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg);
+ if (arena) PORT_FreeArena(arena, false); /* false? */
+
+ return rv;
+}
+
+/*
+ * nsCMSSecureMessage::ReceiveMessage
+ */
+nsresult nsCMSSecureMessage::
+ReceiveMessage(const char *msg, char **_retval)
+{
+ nsNSSShutDownPreventionLock locker;
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::ReceiveMessage\n"));
+ nsresult rv = NS_OK;
+ NSSCMSDecoderContext *dcx;
+ unsigned char *der = 0;
+ int32_t derLen;
+ NSSCMSMessage *cmsMsg = 0;
+ SECItem *content;
+ nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
+
+ /* Step 1. Decode the base64 wrapper */
+ rv = decode(msg, &der, &derLen);
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::ReceiveMessage - can't base64 decode\n"));
+ goto done;
+ }
+
+ dcx = NSS_CMSDecoder_Start(0, 0, 0, /* pw */ 0, ctx, /* key */ 0, 0);
+ if (!dcx) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::ReceiveMessage - can't start decoder\n"));
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ (void)NSS_CMSDecoder_Update(dcx, (char *)der, derLen);
+ cmsMsg = NSS_CMSDecoder_Finish(dcx);
+ if (!cmsMsg) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::ReceiveMessage - can't finish decoder\n"));
+ rv = NS_ERROR_FAILURE;
+ /* Memory leak on dcx?? */
+ goto done;
+ }
+
+ content = NSS_CMSMessage_GetContent(cmsMsg);
+ if (!content) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::ReceiveMessage - can't get content\n"));
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ /* Copy the data */
+ *_retval = (char*)malloc(content->len+1);
+ memcpy(*_retval, content->data, content->len);
+ (*_retval)[content->len] = 0;
+
+done:
+ if (der) free(der);
+ if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg);
+
+ return rv;
+}
+
+nsresult nsCMSSecureMessage::
+encode(const unsigned char *data, int32_t dataLen, char **_retval)
+{
+ nsresult rv = NS_OK;
+
+ *_retval = PL_Base64Encode((const char *)data, dataLen, nullptr);
+ if (!*_retval) { rv = NS_ERROR_OUT_OF_MEMORY; goto loser; }
+
+loser:
+ return rv;
+}
+
+nsresult nsCMSSecureMessage::
+decode(const char *data, unsigned char **result, int32_t * _retval)
+{
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::decode\n"));
+ nsresult rv = NS_OK;
+ uint32_t len = strlen(data);
+ int adjust = 0;
+
+ /* Compute length adjustment */
+ if (data[len-1] == '=') {
+ adjust++;
+ if (data[len-2] == '=') adjust++;
+ }
+
+ *result = (unsigned char *)PL_Base64Decode(data, len, nullptr);
+ if (!*result) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsCMSSecureMessage::decode - error decoding base64\n"));
+ rv = NS_ERROR_ILLEGAL_VALUE;
+ goto loser;
+ }
+
+ *_retval = (len*3)/4 - adjust;
+
+loser:
+ return rv;
+}