summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/mime/src/mimemcms.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/mime/src/mimemcms.cpp')
-rw-r--r--comm/mailnews/mime/src/mimemcms.cpp501
1 files changed, 501 insertions, 0 deletions
diff --git a/comm/mailnews/mime/src/mimemcms.cpp b/comm/mailnews/mime/src/mimemcms.cpp
new file mode 100644
index 0000000000..d2e5b18f19
--- /dev/null
+++ b/comm/mailnews/mime/src/mimemcms.cpp
@@ -0,0 +1,501 @@
+/* -*- 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 "nsICMSMessage.h"
+#include "nsICMSMessageErrors.h"
+#include "nsICMSDecoder.h"
+#include "nsICryptoHash.h"
+#include "mimemcms.h"
+#include "mimecryp.h"
+#include "nsMimeTypes.h"
+#include "nspr.h"
+#include "nsMimeStringResources.h"
+#include "mimemsg.h"
+#include "mimemoz2.h"
+#include "nsIURI.h"
+#include "nsIMsgWindow.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsIMsgSMIMEHeaderSink.h"
+#include "nsCOMPtr.h"
+#include "nsIX509Cert.h"
+#include "plstr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIMailChannel.h"
+
+#define MIME_SUPERCLASS mimeMultipartSignedClass
+MimeDefClass(MimeMultipartSignedCMS, MimeMultipartSignedCMSClass,
+ mimeMultipartSignedCMSClass, &MIME_SUPERCLASS);
+
+static int MimeMultipartSignedCMS_initialize(MimeObject*);
+
+static void* MimeMultCMS_init(MimeObject*);
+static int MimeMultCMS_data_hash(const char*, int32_t, void*);
+static int MimeMultCMS_sig_hash(const char*, int32_t, void*);
+static int MimeMultCMS_data_eof(void*, bool);
+static int MimeMultCMS_sig_eof(void*, bool);
+static int MimeMultCMS_sig_init(void*, MimeObject*, MimeHeaders*);
+static char* MimeMultCMS_generate(void*);
+static void MimeMultCMS_free(void*);
+static void MimeMultCMS_suppressed_child(void* crypto_closure);
+
+extern int SEC_ERROR_CERT_ADDR_MISMATCH;
+
+static int MimeMultipartSignedCMSClassInitialize(
+ MimeMultipartSignedCMSClass* clazz) {
+ MimeObjectClass* oclass = (MimeObjectClass*)clazz;
+ MimeMultipartSignedClass* sclass = (MimeMultipartSignedClass*)clazz;
+
+ oclass->initialize = MimeMultipartSignedCMS_initialize;
+
+ sclass->crypto_init = MimeMultCMS_init;
+ sclass->crypto_data_hash = MimeMultCMS_data_hash;
+ sclass->crypto_data_eof = MimeMultCMS_data_eof;
+ sclass->crypto_signature_init = MimeMultCMS_sig_init;
+ sclass->crypto_signature_hash = MimeMultCMS_sig_hash;
+ sclass->crypto_signature_eof = MimeMultCMS_sig_eof;
+ sclass->crypto_generate_html = MimeMultCMS_generate;
+ sclass->crypto_notify_suppressed_child = MimeMultCMS_suppressed_child;
+ sclass->crypto_free = MimeMultCMS_free;
+
+ PR_ASSERT(!oclass->class_initialized);
+ return 0;
+}
+
+static int MimeMultipartSignedCMS_initialize(MimeObject* object) {
+ return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
+}
+
+typedef struct MimeMultCMSdata {
+ int16_t hash_type;
+ nsCOMPtr<nsICryptoHash> data_hash_context;
+ nsCOMPtr<nsICMSDecoder> sig_decoder_context;
+ nsCOMPtr<nsICMSMessage> content_info;
+ char* sender_addr;
+ bool decoding_failed;
+ bool reject_signature;
+ unsigned char* item_data;
+ uint32_t item_len;
+ MimeObject* self;
+ nsCOMPtr<nsIMsgSMIMEHeaderSink> smimeHeaderSink;
+ nsCString url;
+
+ MimeMultCMSdata()
+ : hash_type(0),
+ sender_addr(nullptr),
+ decoding_failed(false),
+ reject_signature(false),
+ item_data(nullptr),
+ self(nullptr) {}
+
+ ~MimeMultCMSdata() {
+ PR_FREEIF(sender_addr);
+
+ // Do a graceful shutdown of the nsICMSDecoder and release the nsICMSMessage
+ if (sig_decoder_context) {
+ nsCOMPtr<nsICMSMessage> cinfo;
+ sig_decoder_context->Finish(getter_AddRefs(cinfo));
+ }
+
+ delete[] item_data;
+ }
+} MimeMultCMSdata;
+
+/* #### MimeEncryptedCMS and MimeMultipartSignedCMS have a sleazy,
+ incestuous, dysfunctional relationship. */
+extern bool MimeAnyParentCMSSigned(MimeObject* obj);
+extern void MimeCMSGetFromSender(MimeObject* obj, nsCString& from_addr,
+ nsCString& from_name, nsCString& sender_addr,
+ nsCString& sender_name, nsCString& msg_date);
+extern void MimeCMSRequestAsyncSignatureVerification(
+ nsICMSMessage* aCMSMsg, const char* aFromAddr, const char* aFromName,
+ const char* aSenderAddr, const char* aSenderName, const char* aMsgDate,
+ nsIMsgSMIMEHeaderSink* aHeaderSink, int32_t aMimeNestingLevel,
+ const nsCString& aMsgNeckoURL, const nsCString& aOriginMimePartNumber,
+ const nsTArray<uint8_t>& aDigestData, int16_t aDigestType);
+extern char* MimeCMS_MakeSAURL(MimeObject* obj);
+extern char* IMAP_CreateReloadAllPartsUrl(const char* url);
+extern int MIMEGetRelativeCryptoNestLevel(MimeObject* obj);
+
+static void* MimeMultCMS_init(MimeObject* obj) {
+ MimeHeaders* hdrs = obj->headers;
+ MimeMultCMSdata* data = 0;
+ char *ct, *micalg;
+ int16_t hash_type;
+ nsresult rv;
+
+ data = new MimeMultCMSdata;
+ if (!data) return 0;
+
+ data->self = obj;
+
+ mime_stream_data* msd =
+ (mime_stream_data*)(data->self->options->stream_closure);
+ if (msd) {
+ nsIChannel* channel = msd->channel; // note the lack of ref counting...
+ if (channel) {
+ nsCOMPtr<nsIURI> uri;
+ channel->GetURI(getter_AddRefs(uri));
+ if (uri) {
+ rv = uri->GetSpec(data->url);
+
+ // We only want to update the UI if the current mime transaction
+ // is intended for display.
+ // If the current transaction is intended for background processing,
+ // we can learn that by looking at the additional header=filter
+ // string contained in the URI.
+ //
+ // If we find something, we do not set smimeHeaderSink,
+ // which will prevent us from giving UI feedback.
+ //
+ // If we do not find header=filter, we assume the result of the
+ // processing will be shown in the UI.
+
+ if (!strstr(data->url.get(), "?header=filter") &&
+ !strstr(data->url.get(), "&header=filter") &&
+ !strstr(data->url.get(), "?header=attach") &&
+ !strstr(data->url.get(), "&header=attach")) {
+ nsCOMPtr<nsIMailChannel> mailChannel = do_QueryInterface(channel);
+ if (mailChannel) {
+ mailChannel->GetSmimeHeaderSink(
+ getter_AddRefs(data->smimeHeaderSink));
+ }
+ }
+ }
+ } // if channel
+ } // if msd
+
+ if (obj->parent && MimeAnyParentCMSSigned(obj)) {
+ // Parent is signed. We know this part is a signature, too, because
+ // multipart doesn't allow encryption.
+ // We don't support "inner sign" with outer sign, because the
+ // inner encrypted part could have been produced by an attacker who
+ // stripped away a part containing the signature (S/MIME doesn't
+ // have integrity protection).
+ // Also we don't want to support sign-then-sign, that's misleading,
+ // which part would be shown as having a signature?
+ // We skip signature verfication, and return bad signature status.
+
+ // Note: We must return a valid pointer to ourselve's data,
+ // otherwise the parent will attempt to re-init us.
+
+ data->reject_signature = true;
+ if (data->smimeHeaderSink) {
+ int aRelativeNestLevel = MIMEGetRelativeCryptoNestLevel(data->self);
+ nsAutoCString partnum;
+ partnum.Adopt(mime_part_address(data->self));
+ data->smimeHeaderSink->SignedStatus(aRelativeNestLevel,
+ nsICMSMessageErrors::GENERAL_ERROR,
+ nullptr, data->url, partnum);
+ }
+ return data;
+ }
+
+ ct = MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE, false, false);
+ if (!ct) {
+ delete data;
+ return 0; /* #### bogus message? out of memory? */
+ }
+ micalg = MimeHeaders_get_parameter(ct, PARAM_MICALG, NULL, NULL);
+ PR_Free(ct);
+ ct = 0;
+ if (!micalg) {
+ delete data;
+ return 0; /* #### bogus message? out of memory? */
+ }
+
+ bool allowSha1 = mozilla::Preferences::GetBool(
+ "mail.smime.accept_insecure_sha1_message_signatures", false);
+
+ if (allowSha1 && (!PL_strcasecmp(micalg, PARAM_MICALG_SHA1) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA1_2) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA1_3) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA1_4) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA1_5)))
+ hash_type = nsICryptoHash::SHA1;
+ else if (!PL_strcasecmp(micalg, PARAM_MICALG_SHA256) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA256_2) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA256_3))
+ hash_type = nsICryptoHash::SHA256;
+ else if (!PL_strcasecmp(micalg, PARAM_MICALG_SHA384) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA384_2) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA384_3))
+ hash_type = nsICryptoHash::SHA384;
+ else if (!PL_strcasecmp(micalg, PARAM_MICALG_SHA512) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA512_2) ||
+ !PL_strcasecmp(micalg, PARAM_MICALG_SHA512_3))
+ hash_type = nsICryptoHash::SHA512;
+ else {
+ data->reject_signature = true;
+ if (data->smimeHeaderSink) {
+ int aRelativeNestLevel = MIMEGetRelativeCryptoNestLevel(data->self);
+ nsAutoCString partnum;
+ partnum.Adopt(mime_part_address(data->self));
+ data->smimeHeaderSink->SignedStatus(aRelativeNestLevel,
+ nsICMSMessageErrors::GENERAL_ERROR,
+ nullptr, data->url, partnum);
+ }
+ PR_Free(micalg);
+ return data;
+ }
+
+ PR_Free(micalg);
+ micalg = 0;
+
+ data->hash_type = hash_type;
+
+ data->data_hash_context =
+ do_CreateInstance("@mozilla.org/security/hash;1", &rv);
+ if (NS_FAILED(rv)) {
+ delete data;
+ return 0;
+ }
+
+ rv = data->data_hash_context->Init(data->hash_type);
+ if (NS_FAILED(rv)) {
+ delete data;
+ return 0;
+ }
+
+ PR_SetError(0, 0);
+
+ return data;
+}
+
+static int MimeMultCMS_data_hash(const char* buf, int32_t size,
+ void* crypto_closure) {
+ MimeMultCMSdata* data = (MimeMultCMSdata*)crypto_closure;
+ if (!data) {
+ return -1;
+ }
+
+ if (data->reject_signature) {
+ return 0;
+ }
+
+ if (!data->data_hash_context) {
+ return -1;
+ }
+
+ PR_SetError(0, 0);
+ nsresult rv = data->data_hash_context->Update((unsigned char*)buf, size);
+ data->decoding_failed = NS_FAILED(rv);
+
+ return 0;
+}
+
+static int MimeMultCMS_data_eof(void* crypto_closure, bool abort_p) {
+ MimeMultCMSdata* data = (MimeMultCMSdata*)crypto_closure;
+ if (!data) {
+ return -1;
+ }
+
+ if (data->reject_signature) {
+ return 0;
+ }
+
+ if (!data->data_hash_context) {
+ return -1;
+ }
+
+ nsAutoCString hashString;
+ data->data_hash_context->Finish(false, hashString);
+ PR_SetError(0, 0);
+
+ data->item_len = hashString.Length();
+ data->item_data = new unsigned char[data->item_len];
+ if (!data->item_data) return MIME_OUT_OF_MEMORY;
+
+ memcpy(data->item_data, hashString.get(), data->item_len);
+
+ // Release our reference to nsICryptoHash //
+ data->data_hash_context = nullptr;
+
+ /* At this point, data->item.data contains a digest for the first part.
+ When we process the signature, the security library will compare this
+ digest to what's in the signature object. */
+
+ return 0;
+}
+
+static int MimeMultCMS_sig_init(void* crypto_closure,
+ MimeObject* multipart_object,
+ MimeHeaders* signature_hdrs) {
+ MimeMultCMSdata* data = (MimeMultCMSdata*)crypto_closure;
+ char* ct;
+ int status = 0;
+ nsresult rv;
+
+ if (data->reject_signature) {
+ return 0;
+ }
+
+ if (!signature_hdrs) {
+ return -1;
+ }
+
+ ct = MimeHeaders_get(signature_hdrs, HEADER_CONTENT_TYPE, true, false);
+
+ /* Verify that the signature object is of the right type. */
+ if (!ct || /* is not a signature type */
+ (PL_strcasecmp(ct, APPLICATION_XPKCS7_SIGNATURE) != 0 &&
+ PL_strcasecmp(ct, APPLICATION_PKCS7_SIGNATURE) != 0)) {
+ status = -1; /* #### error msg about bogus message */
+ }
+ PR_FREEIF(ct);
+ if (status < 0) return status;
+
+ data->sig_decoder_context = do_CreateInstance(NS_CMSDECODER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return 0;
+
+ rv = data->sig_decoder_context->Start(nullptr, nullptr);
+ if (NS_FAILED(rv)) {
+ status = PR_GetError();
+ if (status >= 0) status = -1;
+ }
+ return status;
+}
+
+static int MimeMultCMS_sig_hash(const char* buf, int32_t size,
+ void* crypto_closure) {
+ MimeMultCMSdata* data = (MimeMultCMSdata*)crypto_closure;
+ nsresult rv;
+
+ if (!data) {
+ return -1;
+ }
+
+ if (data->reject_signature) {
+ return 0;
+ }
+
+ if (!data->sig_decoder_context) {
+ return -1;
+ }
+
+ rv = data->sig_decoder_context->Update(buf, size);
+ data->decoding_failed = NS_FAILED(rv);
+
+ return 0;
+}
+
+static int MimeMultCMS_sig_eof(void* crypto_closure, bool abort_p) {
+ MimeMultCMSdata* data = (MimeMultCMSdata*)crypto_closure;
+
+ if (!data) {
+ return -1;
+ }
+
+ if (data->reject_signature) {
+ return 0;
+ }
+
+ /* Hand an EOF to the crypto library.
+
+ We save away the value returned and will use it later to emit a
+ blurb about whether the signature validation was cool.
+ */
+
+ if (data->sig_decoder_context) {
+ data->sig_decoder_context->Finish(getter_AddRefs(data->content_info));
+
+ // Release our reference to nsICMSDecoder //
+ data->sig_decoder_context = nullptr;
+ }
+
+ return 0;
+}
+
+static void MimeMultCMS_free(void* crypto_closure) {
+ MimeMultCMSdata* data = (MimeMultCMSdata*)crypto_closure;
+ if (!data) return;
+
+ delete data;
+}
+
+static void MimeMultCMS_suppressed_child(void* crypto_closure) {
+ // I'm a multipart/signed. If one of my cryptographic child elements
+ // was suppressed, then I want my signature to be shown as invalid.
+ MimeMultCMSdata* data = (MimeMultCMSdata*)crypto_closure;
+ if (data && data->smimeHeaderSink) {
+ if (data->reject_signature) {
+ return;
+ }
+
+ nsAutoCString partnum;
+ partnum.Adopt(mime_part_address(data->self));
+ data->smimeHeaderSink->SignedStatus(
+ MIMEGetRelativeCryptoNestLevel(data->self),
+ nsICMSMessageErrors::GENERAL_ERROR, nullptr, data->url, partnum);
+ }
+}
+
+static char* MimeMultCMS_generate(void* crypto_closure) {
+ MimeMultCMSdata* data = (MimeMultCMSdata*)crypto_closure;
+ if (!data) return 0;
+ nsCOMPtr<nsIX509Cert> signerCert;
+
+ int aRelativeNestLevel = MIMEGetRelativeCryptoNestLevel(data->self);
+
+ if (aRelativeNestLevel < 0) return nullptr;
+
+ if (aRelativeNestLevel >= 0) {
+ // maxWantedNesting 1: only want outermost nesting level
+ if (aRelativeNestLevel > 1) return nullptr;
+ }
+
+ nsAutoCString partnum;
+ partnum.Adopt(mime_part_address(data->self));
+
+ if (data->self->options->missing_parts) {
+ // We were not given all parts of the message.
+ // We are therefore unable to verify correctness of the signature.
+
+ if (data->smimeHeaderSink) {
+ data->smimeHeaderSink->SignedStatus(
+ aRelativeNestLevel, nsICMSMessageErrors::VERIFY_NOT_YET_ATTEMPTED,
+ nullptr, data->url, partnum);
+ }
+ return nullptr;
+ }
+
+ if (!data->content_info) {
+ /* No content_info at all -- since we're inside a multipart/signed,
+ that means that we've either gotten a message that was truncated
+ before the signature part, or we ran out of memory, or something
+ awful has happened.
+ */
+ return nullptr;
+ }
+
+ nsCString from_addr;
+ nsCString from_name;
+ nsCString sender_addr;
+ nsCString sender_name;
+ nsCString msg_date;
+
+ MimeCMSGetFromSender(data->self, from_addr, from_name, sender_addr,
+ sender_name, msg_date);
+
+ nsTArray<uint8_t> digest;
+ digest.AppendElements(data->item_data, data->item_len);
+
+ if (!data->reject_signature && data->smimeHeaderSink) {
+ MimeCMSRequestAsyncSignatureVerification(
+ data->content_info, from_addr.get(), from_name.get(), sender_addr.get(),
+ sender_name.get(), msg_date.get(), data->smimeHeaderSink,
+ aRelativeNestLevel, data->url, partnum, digest, data->hash_type);
+ }
+
+ if (data->content_info) {
+#if 0 // XXX Fix this. What do we do here? //
+ if (SEC_CMSContainsCertsOrCrls(data->content_info))
+ {
+ /* #### call libsec telling it to import the certs */
+ }
+#endif
+ }
+
+ return nullptr;
+}