diff options
Diffstat (limited to '')
-rw-r--r-- | comm/mailnews/mime/src/mimecms.cpp | 772 |
1 files changed, 772 insertions, 0 deletions
diff --git a/comm/mailnews/mime/src/mimecms.cpp b/comm/mailnews/mime/src/mimecms.cpp new file mode 100644 index 0000000000..e9d2d33ae5 --- /dev/null +++ b/comm/mailnews/mime/src/mimecms.cpp @@ -0,0 +1,772 @@ +/* -*- 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 "mimecms.h" +#include "mimemcms.h" +#include "mimemsig.h" +#include "nspr.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 "nsServiceManagerUtils.h" +#include "nsComponentManagerUtils.h" +#include "nsThreadUtils.h" +#include "nsProxyRelease.h" +#include "mozilla/mailnews/MimeHeaderParser.h" +#include "nsIMailChannel.h" + +using namespace mozilla::mailnews; + +// The name "mime encrypted" is misleading, because this code is used +// both for CMS messages that are encrypted, and also for messages that +// aren't encrypted, but only contain a signature. + +#define MIME_SUPERCLASS mimeEncryptedClass +MimeDefClass(MimeEncryptedCMS, MimeEncryptedCMSClass, mimeEncryptedCMSClass, + &MIME_SUPERCLASS); + +static void* MimeCMS_init(MimeObject*, + int (*output_fn)(const char*, int32_t, void*), void*); +static int MimeCMS_write(const char*, int32_t, void*); +static int MimeCMS_eof(void*, bool); +static char* MimeCMS_generate(void*); +static void MimeCMS_free(void*); + +extern int SEC_ERROR_CERT_ADDR_MISMATCH; + +static int MimeEncryptedCMSClassInitialize(MimeEncryptedCMSClass* clazz) { +#ifdef DEBUG + MimeObjectClass* oclass = (MimeObjectClass*)clazz; + NS_ASSERTION(!oclass->class_initialized, + "1.2 <mscott@netscape.com> 01 Nov 2001 17:59"); +#endif + + MimeEncryptedClass* eclass = (MimeEncryptedClass*)clazz; + eclass->crypto_init = MimeCMS_init; + eclass->crypto_write = MimeCMS_write; + eclass->crypto_eof = MimeCMS_eof; + eclass->crypto_generate_html = MimeCMS_generate; + eclass->crypto_free = MimeCMS_free; + + return 0; +} + +typedef struct MimeCMSdata { + int (*output_fn)(const char* buf, int32_t buf_size, void* output_closure); + void* output_closure; + nsCOMPtr<nsICMSDecoder> decoder_context; + nsCOMPtr<nsICMSMessage> content_info; + bool ci_is_encrypted; + char* sender_addr; + bool decoding_failed; + bool skip_content; + uint32_t decoded_bytes; + MimeObject* self; + bool any_parent_is_encrypted_p; + bool any_parent_is_signed_p; + nsCOMPtr<nsIMsgSMIMEHeaderSink> smimeHeaderSink; + nsCString url; + + MimeCMSdata() + : output_fn(nullptr), + output_closure(nullptr), + ci_is_encrypted(false), + sender_addr(nullptr), + decoding_failed(false), + skip_content(false), + decoded_bytes(0), + self(nullptr), + any_parent_is_encrypted_p(false), + any_parent_is_signed_p(false) {} + + ~MimeCMSdata() { + if (sender_addr) PR_Free(sender_addr); + + // Do an orderly release of nsICMSDecoder and nsICMSMessage // + if (decoder_context) { + nsCOMPtr<nsICMSMessage> cinfo; + decoder_context->Finish(getter_AddRefs(cinfo)); + } + } +} MimeCMSdata; + +/* SEC_PKCS7DecoderContentCallback for SEC_PKCS7DecoderStart() */ +static void MimeCMS_content_callback(void* arg, const char* buf, + unsigned long length) { + int status; + MimeCMSdata* data = (MimeCMSdata*)arg; + if (!data) return; + + if (!data->output_fn) return; + + PR_SetError(0, 0); + status = data->output_fn(buf, length, data->output_closure); + if (status < 0) { + PR_SetError(status, 0); + data->output_fn = 0; + return; + } + + data->decoded_bytes += length; +} + +bool MimeEncryptedCMS_encrypted_p(MimeObject* obj) { + bool encrypted; + + if (!obj) return false; + if (mime_typep(obj, (MimeObjectClass*)&mimeEncryptedCMSClass)) { + MimeEncrypted* enc = (MimeEncrypted*)obj; + MimeCMSdata* data = (MimeCMSdata*)enc->crypto_closure; + if (!data || !data->content_info) return false; + data->content_info->ContentIsEncrypted(&encrypted); + return encrypted; + } + return false; +} + +bool MimeEncOrMP_CMS_signed_p(MimeObject* obj) { + bool is_signed; + + if (!obj) return false; + if (mime_typep(obj, (MimeObjectClass*)&mimeMultipartSignedCMSClass)) { + return true; + } + if (mime_typep(obj, (MimeObjectClass*)&mimeEncryptedCMSClass)) { + MimeEncrypted* enc = (MimeEncrypted*)obj; + MimeCMSdata* data = (MimeCMSdata*)enc->crypto_closure; + if (!data || !data->content_info) return false; + data->content_info->ContentIsSigned(&is_signed); + return is_signed; + } + return false; +} + +bool MimeAnyParentCMSEncrypted(MimeObject* obj) { + MimeObject* o2 = obj; + while (o2 && o2->parent) { + if (MimeEncryptedCMS_encrypted_p(o2->parent)) { + return true; + } + o2 = o2->parent; + } + return false; +} + +bool MimeAnyParentCMSSigned(MimeObject* obj) { + MimeObject* o2 = obj; + while (o2 && o2->parent) { + if (MimeEncOrMP_CMS_signed_p(o2->parent)) { + return true; + } + o2 = o2->parent; + } + return false; +} + +bool MimeCMSHeadersAndCertsMatch(nsICMSMessage* content_info, + nsIX509Cert* signerCert, const char* from_addr, + const char* from_name, const char* sender_addr, + const char* sender_name, + bool* signing_cert_without_email_address) { + nsCString cert_addr; + bool match = true; + bool foundFrom = false; + bool foundSender = false; + + /* Find the name and address in the cert. + */ + if (content_info) { + // Extract any address contained in the cert. + // This will be used for testing, whether the cert contains no addresses at + // all. + content_info->GetSignerEmailAddress(getter_Copies(cert_addr)); + } + + if (signing_cert_without_email_address) + *signing_cert_without_email_address = cert_addr.IsEmpty(); + + /* Now compare them -- + consider it a match if the address in the cert matches the + address in the From field (or as a fallback, the Sender field) + */ + + /* If there is no addr in the cert at all, it can not match and we fail. */ + if (cert_addr.IsEmpty()) { + match = false; + } else { + if (signerCert) { + if (from_addr && *from_addr) { + NS_ConvertASCIItoUTF16 ucs2From(from_addr); + if (NS_FAILED(signerCert->ContainsEmailAddress(ucs2From, &foundFrom))) { + foundFrom = false; + } + } else if (sender_addr && *sender_addr) { + NS_ConvertASCIItoUTF16 ucs2Sender(sender_addr); + if (NS_FAILED( + signerCert->ContainsEmailAddress(ucs2Sender, &foundSender))) { + foundSender = false; + } + } + } + + if (!foundSender && !foundFrom) { + match = false; + } + } + + return match; +} + +class nsSMimeVerificationListener : public nsISMimeVerificationListener { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISMIMEVERIFICATIONLISTENER + + nsSMimeVerificationListener(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); + + protected: + virtual ~nsSMimeVerificationListener() {} + + /** + * It is safe to declare this implementation as thread safe, + * despite not using a lock to protect the members. + * Because of the way the object will be used, we don't expect a race. + * After construction, the object is passed to another thread, + * but will no longer be accessed on the original thread. + * The other thread is unable to access/modify self's data members. + * When the other thread is finished, it will call into the "Notify" + * callback. Self's members will be accessed on the other thread, + * but this is fine, because there is no race with the original thread. + * Race-protection for XPCOM reference counting is sufficient. + */ + bool mSinkIsNull; + nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink> mHeaderSink; + int32_t mMimeNestingLevel; + nsCString mMsgNeckoURL; + nsCString mOriginMimePartNumber; + + nsCString mFromAddr; + nsCString mFromName; + nsCString mSenderAddr; + nsCString mSenderName; + nsCString mMsgDate; +}; + +class SignedStatusRunnable : public mozilla::Runnable { + public: + SignedStatusRunnable( + const nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink>& aSink, + int32_t aNestingLevel, int32_t aSignatureStatus, nsIX509Cert* aSignerCert, + const nsCString& aMsgNeckoURL, const nsCString& aOriginMimePartNumber); + NS_DECL_NSIRUNNABLE + nsresult mResult; + + protected: + nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink> m_sink; + int32_t m_nestingLevel; + int32_t m_signatureStatus; + nsCOMPtr<nsIX509Cert> m_signerCert; + nsCString m_msgNeckoURL; + nsCString m_originMimePartNumber; +}; + +SignedStatusRunnable::SignedStatusRunnable( + const nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink>& aSink, + int32_t aNestingLevel, int32_t aSignatureStatus, nsIX509Cert* aSignerCert, + const nsCString& aMsgNeckoURL, const nsCString& aOriginMimePartNumber) + : mozilla::Runnable("SignedStatusRunnable"), + mResult(NS_ERROR_UNEXPECTED), + m_sink(aSink), + m_nestingLevel(aNestingLevel), + m_signatureStatus(aSignatureStatus), + m_signerCert(aSignerCert), + m_msgNeckoURL(aMsgNeckoURL), + m_originMimePartNumber(aOriginMimePartNumber) {} + +NS_IMETHODIMP SignedStatusRunnable::Run() { + mResult = + m_sink->SignedStatus(m_nestingLevel, m_signatureStatus, m_signerCert, + m_msgNeckoURL, m_originMimePartNumber); + return NS_OK; +} + +nsresult ProxySignedStatus( + const nsMainThreadPtrHandle<nsIMsgSMIMEHeaderSink>& aSink, + int32_t aNestingLevel, int32_t aSignatureStatus, nsIX509Cert* aSignerCert, + const nsCString& aMsgNeckoURL, const nsCString& aOriginMimePartNumber) { + RefPtr<SignedStatusRunnable> signedStatus = new SignedStatusRunnable( + aSink, aNestingLevel, aSignatureStatus, aSignerCert, aMsgNeckoURL, + aOriginMimePartNumber); + nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete( + "ProxySignedStatus"_ns, mozilla::GetMainThreadSerialEventTarget(), + do_AddRef(signedStatus)); + NS_ENSURE_SUCCESS(rv, rv); + return signedStatus->mResult; +} + +NS_IMPL_ISUPPORTS(nsSMimeVerificationListener, nsISMimeVerificationListener) + +nsSMimeVerificationListener::nsSMimeVerificationListener( + 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) + : mMsgNeckoURL(aMsgNeckoURL), mOriginMimePartNumber(aOriginMimePartNumber) { + mHeaderSink = new nsMainThreadPtrHolder<nsIMsgSMIMEHeaderSink>( + "nsSMimeVerificationListener::mHeaderSink", aHeaderSink); + mSinkIsNull = !aHeaderSink; + mMimeNestingLevel = aMimeNestingLevel; + + mFromAddr = aFromAddr; + mFromName = aFromName; + mSenderAddr = aSenderAddr; + mSenderName = aSenderName; + mMsgDate = aMsgDate; +} + +NS_IMETHODIMP nsSMimeVerificationListener::Notify( + nsICMSMessage* aVerifiedMessage, nsresult aVerificationResultCode) { + // Only continue if we have a valid pointer to the UI + NS_ENSURE_FALSE(mSinkIsNull, NS_OK); + + NS_ENSURE_TRUE(aVerifiedMessage, NS_ERROR_FAILURE); + + nsCOMPtr<nsIX509Cert> signerCert; + aVerifiedMessage->GetSignerCert(getter_AddRefs(signerCert)); + + int32_t signature_status = nsICMSMessageErrors::GENERAL_ERROR; + + if (NS_FAILED(aVerificationResultCode)) { + if (NS_ERROR_MODULE_SECURITY == + NS_ERROR_GET_MODULE(aVerificationResultCode)) + signature_status = NS_ERROR_GET_CODE(aVerificationResultCode); + else if (NS_ERROR_NOT_IMPLEMENTED == aVerificationResultCode) + signature_status = nsICMSMessageErrors::VERIFY_ERROR_PROCESSING; + } else { + bool signing_cert_without_email_address; + + bool good_p = MimeCMSHeadersAndCertsMatch( + aVerifiedMessage, signerCert, mFromAddr.get(), mFromName.get(), + mSenderAddr.get(), mSenderName.get(), + &signing_cert_without_email_address); + if (!good_p) { + if (signing_cert_without_email_address) + signature_status = nsICMSMessageErrors::VERIFY_CERT_WITHOUT_ADDRESS; + else + signature_status = nsICMSMessageErrors::VERIFY_HEADER_MISMATCH; + } else { + PRTime sigTime; + if (NS_FAILED(aVerifiedMessage->GetSigningTime(&sigTime))) { + // Signing time attribute is optional in CMS messages. + signature_status = nsICMSMessageErrors::SUCCESS; + } else { + // If it's present, check for a rough match with the message date. + PRTime msgTime; + if (PR_ParseTimeString(mMsgDate.get(), false, &msgTime) != PR_SUCCESS) { + signature_status = nsICMSMessageErrors::VERIFY_TIME_MISMATCH; + } else { + PRTime delta; + + if (sigTime > msgTime) { + delta = sigTime - msgTime; + } else { + delta = msgTime - sigTime; + } + + if (delta / PR_USEC_PER_SEC > 60 * 60 * 1) { + signature_status = nsICMSMessageErrors::VERIFY_TIME_MISMATCH; + } else { + signature_status = nsICMSMessageErrors::SUCCESS; + } + } + } + } + } + + if (NS_IsMainThread()) { + mHeaderSink->SignedStatus(mMimeNestingLevel, signature_status, signerCert, + mMsgNeckoURL, mOriginMimePartNumber); + } else { + ProxySignedStatus(mHeaderSink, mMimeNestingLevel, signature_status, + signerCert, mMsgNeckoURL, mOriginMimePartNumber); + } + + return NS_OK; +} + +int MIMEGetRelativeCryptoNestLevel(MimeObject* obj) { + /* + the part id of any mimeobj is mime_part_address(obj) + our currently displayed crypto part is obj + the part shown as the toplevel object in the current window is + obj->options->part_to_load + possibly stored in the toplevel object only ??? + but hopefully all nested mimeobject point to the same displayooptions + + we need to find out the nesting level of our currently displayed crypto + object wrt the shown part in the toplevel window + */ + + // if we are showing the toplevel message, aTopMessageNestLevel == 0 + int aTopMessageNestLevel = 0; + MimeObject* aTopShownObject = nullptr; + if (obj && obj->options->part_to_load) { + bool aAlreadyFoundTop = false; + for (MimeObject* walker = obj; walker; walker = walker->parent) { + if (aAlreadyFoundTop) { + if (!mime_typep(walker, (MimeObjectClass*)&mimeEncryptedClass) && + !mime_typep(walker, (MimeObjectClass*)&mimeMultipartSignedClass)) { + ++aTopMessageNestLevel; + } + } + if (!aAlreadyFoundTop) { + char* addr = mime_part_address(walker); + if (!strcmp(addr, walker->options->part_to_load)) { + aAlreadyFoundTop = true; + aTopShownObject = walker; + } + PR_FREEIF(addr); + } + if (!aAlreadyFoundTop && !walker->parent) { + // The mime part part_to_load is not a parent of the + // the crypto mime part passed in to this function as parameter obj. + // That means the crypto part belongs to another branch of the mime + // tree. + return -1; + } + } + } + + bool CryptoObjectIsChildOfTopShownObject = false; + if (!aTopShownObject) { + // no sub part specified, top message is displayed, and + // our crypto object is definitively a child of it + CryptoObjectIsChildOfTopShownObject = true; + } + + // if we are the child of the topmost message, aCryptoPartNestLevel == 1 + int aCryptoPartNestLevel = 0; + if (obj) { + for (MimeObject* walker = obj; walker; walker = walker->parent) { + // Crypto mime objects are transparent wrt nesting. + if (!mime_typep(walker, (MimeObjectClass*)&mimeEncryptedClass) && + !mime_typep(walker, (MimeObjectClass*)&mimeMultipartSignedClass)) { + ++aCryptoPartNestLevel; + } + if (aTopShownObject && walker->parent == aTopShownObject) { + CryptoObjectIsChildOfTopShownObject = true; + } + } + } + + if (!CryptoObjectIsChildOfTopShownObject) { + return -1; + } + + return aCryptoPartNestLevel - aTopMessageNestLevel; +} + +static void* MimeCMS_init(MimeObject* obj, + int (*output_fn)(const char* buf, int32_t buf_size, + void* output_closure), + void* output_closure) { + MimeCMSdata* data; + nsresult rv; + + if (!(obj && obj->options && output_fn)) return 0; + + data = new MimeCMSdata; + if (!data) return 0; + + data->self = obj; + data->output_fn = output_fn; + data->output_closure = output_closure; + PR_SetError(0, 0); + + data->any_parent_is_signed_p = MimeAnyParentCMSSigned(obj); + + if (data->any_parent_is_signed_p) { + // Parent is signed. + // We don't know yet if this child is signed or encrypted. + // (We'll know after decoding has completed and EOF is called.) + // We don't support "inner encrypt" 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). + // A sign-then-sign encoding is confusing, too, because it could be + // an attempt to influence which signature is shown. + data->skip_content = true; + } + + if (!data->skip_content) { + data->decoder_context = do_CreateInstance(NS_CMSDECODER_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + delete data; + return 0; + } + + rv = data->decoder_context->Start(MimeCMS_content_callback, data); + if (NS_FAILED(rv)) { + delete data; + return 0; + } + } + + data->any_parent_is_encrypted_p = MimeAnyParentCMSEncrypted(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 + + return data; +} + +static int MimeCMS_write(const char* buf, int32_t buf_size, void* closure) { + MimeCMSdata* data = (MimeCMSdata*)closure; + nsresult rv; + + if (!data || !data->output_fn || !data->decoder_context) return -1; + + if (!data->decoding_failed && !data->skip_content) { + PR_SetError(0, 0); + rv = data->decoder_context->Update(buf, buf_size); + data->decoding_failed = NS_FAILED(rv); + } + + return 0; +} + +void MimeCMSGetFromSender(MimeObject* obj, nsCString& from_addr, + nsCString& from_name, nsCString& sender_addr, + nsCString& sender_name, nsCString& msg_date) { + MimeHeaders* msg_headers = 0; + + /* Find the headers of the MimeMessage which is the parent (or grandparent) + of this object (remember, crypto objects nest.) */ + MimeObject* o2 = obj; + msg_headers = o2->headers; + while (o2 && o2->parent && + !mime_typep(o2->parent, (MimeObjectClass*)&mimeMessageClass)) { + o2 = o2->parent; + msg_headers = o2->headers; + } + + if (!msg_headers) return; + + /* Find the names and addresses in the From and/or Sender fields. + */ + nsCString s; + + /* Extract the name and address of the "From:" field. */ + s.Adopt(MimeHeaders_get(msg_headers, HEADER_FROM, false, false)); + if (!s.IsEmpty()) ExtractFirstAddress(EncodedHeader(s), from_name, from_addr); + + /* Extract the name and address of the "Sender:" field. */ + s.Adopt(MimeHeaders_get(msg_headers, HEADER_SENDER, false, false)); + if (!s.IsEmpty()) + ExtractFirstAddress(EncodedHeader(s), sender_name, sender_addr); + + msg_date.Adopt(MimeHeaders_get(msg_headers, HEADER_DATE, false, true)); +} + +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) { + RefPtr<nsSMimeVerificationListener> listener = + new nsSMimeVerificationListener( + aFromAddr, aFromName, aSenderAddr, aSenderName, aMsgDate, aHeaderSink, + aMimeNestingLevel, aMsgNeckoURL, aOriginMimePartNumber); + + long verifyFlags = 0; + if (mozilla::Preferences::GetBool( + "mail.smime.accept_insecure_sha1_message_signatures", false)) { + verifyFlags |= nsICMSVerifyFlags::VERIFY_ALLOW_WEAK_SHA1; + } + + if (aDigestData.IsEmpty()) + aCMSMsg->AsyncVerifySignature(verifyFlags, listener); + else + aCMSMsg->AsyncVerifyDetachedSignature(verifyFlags, listener, aDigestData, + aDigestType); +} + +static int MimeCMS_eof(void* crypto_closure, bool abort_p) { + MimeCMSdata* data = (MimeCMSdata*)crypto_closure; + nsresult rv; + int32_t status = nsICMSMessageErrors::SUCCESS; + + if (!data || !data->output_fn) { + return -1; + } + + if (!data->skip_content && !data->decoder_context) { + // If we don't skip, we should have a context. + return -1; + } + + int aRelativeNestLevel = MIMEGetRelativeCryptoNestLevel(data->self); + + /* Hand an EOF to the crypto library. It may call data->output_fn. + (Today, the crypto library has no flushing to do, but maybe there + will be someday.) + + We save away the value returned and will use it later to emit a + blurb about whether the signature validation was cool. + */ + + PR_SetError(0, 0); + if (!data->skip_content) { + rv = data->decoder_context->Finish(getter_AddRefs(data->content_info)); + if (NS_FAILED(rv)) status = nsICMSMessageErrors::GENERAL_ERROR; + + data->decoder_context = nullptr; + } + + nsCOMPtr<nsIX509Cert> certOfInterest; + + if (!data->smimeHeaderSink) return 0; + + if (aRelativeNestLevel < 0) return 0; + + // maxWantedNesting 1: only want outermost nesting level + if (aRelativeNestLevel > 1) return 0; + + if (data->decoding_failed) status = nsICMSMessageErrors::GENERAL_ERROR; + + nsAutoCString partnum; + partnum.Adopt(mime_part_address(data->self)); + + if (data->skip_content) { + // Skipping content means, we detected a forbidden combination + // of CMS objects, so let's make sure we replace the parent status + // with a bad status. + if (data->any_parent_is_signed_p) { + data->smimeHeaderSink->SignedStatus(aRelativeNestLevel, + nsICMSMessageErrors::GENERAL_ERROR, + nullptr, data->url, partnum); + } + if (data->any_parent_is_encrypted_p) { + data->smimeHeaderSink->EncryptionStatus( + aRelativeNestLevel, nsICMSMessageErrors::GENERAL_ERROR, nullptr, + data->url, partnum); + } + return 0; + } + + if (!data->content_info) { + if (!data->decoded_bytes) { + // We were unable to decode any data. + status = nsICMSMessageErrors::GENERAL_ERROR; + } else { + // Some content got decoded, but we failed to decode + // the final summary, probably we got truncated data. + status = nsICMSMessageErrors::ENCRYPT_INCOMPLETE; + } + + // Although a CMS message could be either encrypted or opaquely signed, + // what we see is most likely encrypted, because if it were + // signed only, we probably would have been able to decode it. + + data->ci_is_encrypted = true; + } else { + rv = data->content_info->ContentIsEncrypted(&data->ci_is_encrypted); + + if (NS_SUCCEEDED(rv) && data->ci_is_encrypted) { + data->content_info->GetEncryptionCert(getter_AddRefs(certOfInterest)); + } else { + // Existing logic in mimei assumes, if !ci_is_encrypted, then it is + // signed. Make sure it indeed is signed. + + bool testIsSigned; + rv = data->content_info->ContentIsSigned(&testIsSigned); + + if (NS_FAILED(rv) || !testIsSigned) { + // Neither signed nor encrypted? + // We are unable to understand what we got, do not try to indicate + // S/Mime status. + return 0; + } + + 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); + + 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, {}, 0); + } + } + + if (data->ci_is_encrypted) { + data->smimeHeaderSink->EncryptionStatus(aRelativeNestLevel, status, + certOfInterest, data->url, partnum); + } + + return 0; +} + +static void MimeCMS_free(void* crypto_closure) { + MimeCMSdata* data = (MimeCMSdata*)crypto_closure; + if (!data) return; + + delete data; +} + +static char* MimeCMS_generate(void* crypto_closure) { return nullptr; } |