Bug 1240290

Tag #1273
This commit is contained in:
Matt A. Tobin 2019-11-11 01:56:34 -05:00 • committed by Roy Tam
commit 800fce0b81
14 changed files with 282 additions and 215 deletions

View file

@ -116,7 +116,6 @@ nsMsgAttachmentHandler::nsMsgAttachmentHandler() :
m_bogus_attachment(false), m_bogus_attachment(false),
m_done(false), m_done(false),
m_already_encoded_p(false), m_already_encoded_p(false),
m_decrypted_p(false),
mDeleteFile(false), mDeleteFile(false),
mMHTMLPart(false), mMHTMLPart(false),
mPartUserOmissionOverride(false), mPartUserOmissionOverride(false),

View file

@ -140,16 +140,6 @@ public:
bool m_already_encoded_p; // If we attach a document that is already bool m_already_encoded_p; // If we attach a document that is already
// encoded, we just pass it through. // encoded, we just pass it through.
bool m_decrypted_p; /* S/MIME -- when attaching a message that was
encrypted, it's necessary to decrypt it first
(since nobody but the original recipient can
read it -- if you forward it to someone in the
raw, it will be useless to them.) This flag
indicates whether decryption occurred, so that
libmsg can issue appropriate warnings about
doing a cleartext forward of a message that was
originally encrypted. */
bool mDeleteFile; // If this is true, Delete the file...its bool mDeleteFile; // If this is true, Delete the file...its
// NOT the original file! // NOT the original file!

View file

@ -8,6 +8,7 @@
#include "nsICMSMessageErrors.h" #include "nsICMSMessageErrors.h"
#include "nsICMSDecoder.h" #include "nsICMSDecoder.h"
#include "mimecms.h" #include "mimecms.h"
#include "mimemcms.h"
#include "mimemsig.h" #include "mimemsig.h"
#include "nspr.h" #include "nspr.h"
#include "mimemsg.h" #include "mimemsg.h"
@ -28,6 +29,9 @@
using namespace mozilla::mailnews; 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 #define MIME_SUPERCLASS mimeEncryptedClass
MimeDefClass(MimeEncryptedCMS, MimeEncryptedCMSClass, MimeDefClass(MimeEncryptedCMS, MimeEncryptedCMSClass,
@ -68,10 +72,11 @@ typedef struct MimeCMSdata
bool ci_is_encrypted; bool ci_is_encrypted;
char *sender_addr; char *sender_addr;
bool decoding_failed; bool decoding_failed;
bool skip_content;
uint32_t decoded_bytes; uint32_t decoded_bytes;
MimeObject *self; MimeObject *self;
bool parent_is_encrypted_p; bool any_parent_is_encrypted_p;
bool parent_holds_stamp_p; bool any_parent_is_signed_p;
nsCOMPtr<nsIMsgSMIMEHeaderSink> smimeHeaderSink; nsCOMPtr<nsIMsgSMIMEHeaderSink> smimeHeaderSink;
nsCString url; nsCString url;
@ -81,12 +86,11 @@ typedef struct MimeCMSdata
ci_is_encrypted(false), ci_is_encrypted(false),
sender_addr(nullptr), sender_addr(nullptr),
decoding_failed(false), decoding_failed(false),
skip_content(false),
decoded_bytes(0), decoded_bytes(0),
self(nullptr), self(nullptr),
parent_is_encrypted_p(false), any_parent_is_encrypted_p(false),
parent_holds_stamp_p(false) any_parent_is_signed_p(false) {}
{
}
~MimeCMSdata() ~MimeCMSdata()
{ {
@ -140,6 +144,46 @@ bool MimeEncryptedCMS_encrypted_p (MimeObject *obj)
return false; 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, bool MimeCMSHeadersAndCertsMatch(nsICMSMessage *content_info,
nsIX509Cert *signerCert, nsIX509Cert *signerCert,
@ -439,38 +483,37 @@ static void *MimeCMS_init(MimeObject *obj,
data->output_fn = output_fn; data->output_fn = output_fn;
data->output_closure = output_closure; data->output_closure = output_closure;
PR_SetError(0, 0); 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); data->decoder_context = do_CreateInstance(NS_CMSDECODER_CONTRACTID, &rv);
if (NS_FAILED(rv)) if (NS_FAILED(rv)) {
{
delete data; delete data;
return 0; return 0;
} }
rv = data->decoder_context->Start(MimeCMS_content_callback, data); rv = data->decoder_context->Start(MimeCMS_content_callback, data);
if (NS_FAILED(rv)) if (NS_FAILED(rv)) {
{
delete data; delete data;
return 0; return 0;
} }
}
// XXX Fix later XXX // data->any_parent_is_encrypted_p = MimeAnyParentCMSEncrypted(obj);
data->parent_holds_stamp_p =
(obj->parent &&
(mime_crypto_stamped_p(obj->parent) ||
mime_typep(obj->parent, (MimeObjectClass *) &mimeEncryptedClass)));
data->parent_is_encrypted_p =
(obj->parent && MimeEncryptedCMS_encrypted_p (obj->parent));
/* If the parent of this object is a crypto-blob, then it's the grandparent
who would have written out the headers and prepared for a stamp...
(This shit sucks.)
*/
if (data->parent_is_encrypted_p &&
!data->parent_holds_stamp_p &&
obj->parent && obj->parent->parent)
data->parent_holds_stamp_p =
mime_crypto_stamped_p (obj->parent->parent);
mime_stream_data *msd = (mime_stream_data *) (data->self->options->stream_closure); mime_stream_data *msd = (mime_stream_data *) (data->self->options->stream_closure);
if (msd) if (msd)
@ -530,9 +573,11 @@ MimeCMS_write (const char *buf, int32_t buf_size, void *closure)
if (!data || !data->output_fn || !data->decoder_context) return -1; if (!data || !data->output_fn || !data->decoder_context) return -1;
if (!data->decoding_failed && !data->skip_content) {
PR_SetError(0, 0); PR_SetError(0, 0);
rv = data->decoder_context->Update(buf, buf_size); rv = data->decoder_context->Update(buf, buf_size);
data->decoding_failed = NS_FAILED(rv); data->decoding_failed = NS_FAILED(rv);
}
return 0; return 0;
} }
@ -606,7 +651,12 @@ MimeCMS_eof (void *crypto_closure, bool abort_p)
nsresult rv; nsresult rv;
int32_t status = nsICMSMessageErrors::SUCCESS; int32_t status = nsICMSMessageErrors::SUCCESS;
if (!data || !data->output_fn || !data->decoder_context) { 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; return -1;
} }
@ -621,11 +671,12 @@ MimeCMS_eof (void *crypto_closure, bool abort_p)
*/ */
PR_SetError(0, 0); PR_SetError(0, 0);
if (!data->skip_content) {
rv = data->decoder_context->Finish(getter_AddRefs(data->content_info)); rv = data->decoder_context->Finish(getter_AddRefs(data->content_info));
if (NS_FAILED(rv)) if (NS_FAILED(rv)) status = nsICMSMessageErrors::GENERAL_ERROR;
status = nsICMSMessageErrors::GENERAL_ERROR;
data->decoder_context = nullptr; data->decoder_context = nullptr;
}
nsCOMPtr<nsIX509Cert> certOfInterest; nsCOMPtr<nsIX509Cert> certOfInterest;
@ -644,6 +695,21 @@ MimeCMS_eof (void *crypto_closure, bool abort_p)
if (data->decoding_failed) if (data->decoding_failed)
status = nsICMSMessageErrors::GENERAL_ERROR; status = nsICMSMessageErrors::GENERAL_ERROR;
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);
}
if (data->any_parent_is_encrypted_p) {
data->smimeHeaderSink->EncryptionStatus(aRelativeNestLevel,
nsICMSMessageErrors::GENERAL_ERROR, nullptr, data->url);
}
return 0;
}
if (!data->content_info) if (!data->content_info)
{ {
if (!data->decoded_bytes) if (!data->decoded_bytes)

View file

@ -418,18 +418,8 @@ MimeEncrypted_emit_buffered_child(MimeObject *obj)
obj->options->headers != MimeHeadersCitation && obj->options->headers != MimeHeadersCitation &&
obj->options->write_html_p && obj->options->write_html_p &&
obj->options->output_fn) obj->options->output_fn)
// && !mime_crypto_object_p(enc->hdrs, true, obj->options)) // XXX fix later XXX //
{ {
char *html; char *html;
#if 0 // XXX Fix this later XXX //
char *html = (((MimeEncryptedClass *) obj->clazz)->crypto_generate_html
(enc->crypto_closure));
if (!html) return -1; /* MK_OUT_OF_MEMORY? */
status = MimeObject_write(obj, html, strlen(html), false);
PR_FREEIF(html);
if (status < 0) return status;
#endif
/* Now that we have written out the crypto stamp, the outermost header /* Now that we have written out the crypto stamp, the outermost header
block is well and truly closed. If this is in fact the outermost block is well and truly closed. If this is in fact the outermost
@ -445,7 +435,8 @@ MimeEncrypted_emit_buffered_child(MimeObject *obj)
for (p = obj; p->parent; p = p->parent) for (p = obj; p->parent; p = p->parent)
outer_headers = p->headers; outer_headers = p->headers;
NS_ASSERTION(obj->options->state->first_data_written_p, "1.2 <mscott@netscape.com> 01 Nov 2001 17:59"); NS_ASSERTION(obj->options->state->first_data_written_p, "1.2 <mscott@netscape.com> 01 Nov 2001 17:59");
html = obj->options->generate_post_header_html_fn(NULL, char *html = obj->options->generate_post_header_html_fn(
NULL,
obj->options->html_closure, obj->options->html_closure,
outer_headers); outer_headers);
obj->options->state->post_header_html_run_p = true; obj->options->state->post_header_html_run_p = true;

View file

@ -234,3 +234,13 @@ MimeExternalObject_displayable_inline_p (MimeObjectClass *clazz,
{ {
return false; return false;
} }
#undef MIME_SUPERCLASS
#define MIME_SUPERCLASS mimeExternalObjectClass
MimeDefClass(MimeSuppressedCrypto, MimeSuppressedCryptoClass,
mimeSuppressedCryptoClass, &MIME_SUPERCLASS);
static int MimeSuppressedCryptoClassInitialize(MimeSuppressedCryptoClass *clazz) {
MimeExternalObjectClass *lclass = (MimeExternalObjectClass *)clazz;
return MimeExternalObjectClassInitialize(lclass);
}

View file

@ -31,4 +31,20 @@ struct MimeExternalObject {
#define MimeExternalObjectClassInitializer(ITYPE,CSUPER) \ #define MimeExternalObjectClassInitializer(ITYPE,CSUPER) \
{ MimeLeafClassInitializer(ITYPE,CSUPER) } { MimeLeafClassInitializer(ITYPE,CSUPER) }
typedef struct MimeSuppressedCryptoClass MimeSuppressedCryptoClass;
typedef struct MimeSuppressedCrypto MimeSuppressedCrypto;
struct MimeSuppressedCryptoClass {
MimeExternalObjectClass eobj;
};
extern "C" MimeSuppressedCryptoClass mimeSuppressedCryptoClass;
struct MimeSuppressedCrypto {
MimeExternalObject eobj;
};
#define MimeSuppressedCryptoClassInitializer(ITYPE, CSUPER) \
{ MimeExternalObjectClassInitializer(ITYPE, CSUPER) }
#endif /* _MIMEEOBJ_H_ */ #endif /* _MIMEEOBJ_H_ */

View file

@ -731,7 +731,7 @@ mime_find_class (const char *content_type, MimeHeaders *hdrs,
// Allowing them would leak the plain text in case the part is // Allowing them would leak the plain text in case the part is
// cleverly hidden and the decrypted content gets included in // cleverly hidden and the decrypted content gets included in
// replies and forwards. // replies and forwards.
clazz = (MimeObjectClass *)&mimeExternalObjectClass; clazz = (MimeObjectClass *)&mimeSuppressedCryptoClass;
return clazz; return clazz;
} }
@ -951,10 +951,15 @@ mime_create (const char *content_type, MimeHeaders *hdrs,
(clazz != (MimeObjectClass *)&mimeInlineTextRichtextClass) && (clazz != (MimeObjectClass *)&mimeInlineTextRichtextClass) &&
(clazz != (MimeObjectClass *)&mimeInlineTextEnrichedClass) && (clazz != (MimeObjectClass *)&mimeInlineTextEnrichedClass) &&
(clazz != (MimeObjectClass *)&mimeMessageClass) && (clazz != (MimeObjectClass *)&mimeMessageClass) &&
(clazz != (MimeObjectClass *)&mimeInlineImageClass) ) (clazz != (MimeObjectClass *)&mimeInlineImageClass) ) {
// not a special inline type, so show as attachment // not a special inline type, so show as attachment
// However, mimeSuppressedCryptoClass is treated identically as
// mimeExternalObjectClass, let's not lose that type information.
if (clazz != (MimeObjectClass *)&mimeSuppressedCryptoClass) {
clazz = (MimeObjectClass *)&mimeExternalObjectClass; clazz = (MimeObjectClass *)&mimeExternalObjectClass;
} }
}
}
/* If the option `Show Attachments Inline' is off, now would be the time to change our mind... */ /* If the option `Show Attachments Inline' is off, now would be the time to change our mind... */
/* Also, if we're doing a reply (i.e. quoting the body), then treat that according to preference. */ /* Also, if we're doing a reply (i.e. quoting the body), then treat that according to preference. */
@ -1221,21 +1226,6 @@ mime_crypto_object_p(MimeHeaders *hdrs, bool clearsigned_counts, MimeDisplayOpti
return false; return false;
} }
/* Whether the given object has written out the HTML version of its headers
in such a way that it will have a "crypto stamp" next to the headers. If
this is true, then the child must write out its HTML slightly differently
to take this into account...
*/
bool
mime_crypto_stamped_p(MimeObject *obj)
{
if (!obj) return false;
if (mime_typep (obj, (MimeObjectClass *) &mimeMessageClass))
return ((MimeMessage *) obj)->crypto_stamped_p;
else
return false;
}
#endif // ENABLE_SMIME #endif // ENABLE_SMIME
/* Puts a part-number into a URL. If append_p is true, then the part number /* Puts a part-number into a URL. If append_p is true, then the part number

View file

@ -332,14 +332,6 @@ extern void mime_get_crypto_state (MimeObject *obj,
bool *signed_p, bool *encrypted_p, bool *signed_p, bool *encrypted_p,
bool *signed_ok, bool *encrypted_ok); bool *signed_ok, bool *encrypted_ok);
/* Whether the given object has written out the HTML version of its headers
in such a way that it will have a "crypto stamp" next to the headers. If
this is true, then the child must write out its HTML slightly differently
to take this into account...
*/
extern bool mime_crypto_stamped_p(MimeObject *obj);
/* How the crypto code tells the MimeMessage object what the crypto stamp /* How the crypto code tells the MimeMessage object what the crypto stamp
on it says. */ on it says. */
extern void mime_set_crypto_stamp(MimeObject *obj, extern void mime_set_crypto_stamp(MimeObject *obj,
@ -352,7 +344,7 @@ public:
MimeParseStateObject() MimeParseStateObject()
{root = 0; separator_queued_p = false; separator_suppressed_p = false; {root = 0; separator_queued_p = false; separator_suppressed_p = false;
first_part_written_p = false; post_header_html_run_p = false; first_data_written_p = false; first_part_written_p = false; post_header_html_run_p = false; first_data_written_p = false;
decrypted_p = false; strippingPart = false; strippingPart = false;
} }
MimeObject *root; /* The outermost parser object. */ MimeObject *root; /* The outermost parser object. */
@ -376,10 +368,6 @@ public:
bool first_data_written_p; /* State used for Mozilla lazy-stream- bool first_data_written_p; /* State used for Mozilla lazy-stream-
creation evilness. */ creation evilness. */
bool decrypted_p; /* If options->dexlate_p is true, then this
will be set to indicate whether any
dexlateion did in fact occur.
*/
nsTArray<nsCString> partsToStrip; /* if we're stripping parts, what parts to strip */ nsTArray<nsCString> partsToStrip; /* if we're stripping parts, what parts to strip */
nsTArray<nsCString> detachToFiles; /* if we're detaching parts, where each part was detached to */ nsTArray<nsCString> detachToFiles; /* if we're detaching parts, where each part was detached to */
bool strippingPart; bool strippingPart;

View file

@ -38,6 +38,7 @@ static int MimeMultCMS_sig_eof (void *, bool);
static int MimeMultCMS_sig_init (void *, MimeObject *, MimeHeaders *); static int MimeMultCMS_sig_init (void *, MimeObject *, MimeHeaders *);
static char * MimeMultCMS_generate (void *); static char * MimeMultCMS_generate (void *);
static void MimeMultCMS_free (void *); static void MimeMultCMS_free (void *);
static void MimeMultCMS_suppressed_child(void *crypto_closure);
extern int SEC_ERROR_CERT_ADDR_MISMATCH; extern int SEC_ERROR_CERT_ADDR_MISMATCH;
@ -56,6 +57,7 @@ MimeMultipartSignedCMSClassInitialize(MimeMultipartSignedCMSClass *clazz)
sclass->crypto_signature_hash = MimeMultCMS_sig_hash; sclass->crypto_signature_hash = MimeMultCMS_sig_hash;
sclass->crypto_signature_eof = MimeMultCMS_sig_eof; sclass->crypto_signature_eof = MimeMultCMS_sig_eof;
sclass->crypto_generate_html = MimeMultCMS_generate; sclass->crypto_generate_html = MimeMultCMS_generate;
sclass->crypto_notify_suppressed_child = MimeMultCMS_suppressed_child;
sclass->crypto_free = MimeMultCMS_free; sclass->crypto_free = MimeMultCMS_free;
PR_ASSERT(!oclass->class_initialized); PR_ASSERT(!oclass->class_initialized);
@ -80,8 +82,6 @@ typedef struct MimeMultCMSdata
unsigned char* item_data; unsigned char* item_data;
uint32_t item_len; uint32_t item_len;
MimeObject *self; MimeObject *self;
bool parent_is_encrypted_p;
bool parent_holds_stamp_p;
nsCOMPtr<nsIMsgSMIMEHeaderSink> smimeHeaderSink; nsCOMPtr<nsIMsgSMIMEHeaderSink> smimeHeaderSink;
nsCString url; nsCString url;
@ -90,11 +90,7 @@ typedef struct MimeMultCMSdata
sender_addr(nullptr), sender_addr(nullptr),
decoding_failed(false), decoding_failed(false),
item_data(nullptr), item_data(nullptr),
self(nullptr), self(nullptr) {}
parent_is_encrypted_p(false),
parent_holds_stamp_p(false)
{
}
~MimeMultCMSdata() ~MimeMultCMSdata()
{ {
@ -113,7 +109,7 @@ typedef struct MimeMultCMSdata
/* #### MimeEncryptedCMS and MimeMultipartSignedCMS have a sleazy, /* #### MimeEncryptedCMS and MimeMultipartSignedCMS have a sleazy,
incestuous, dysfunctional relationship. */ incestuous, dysfunctional relationship. */
extern bool MimeEncryptedCMS_encrypted_p (MimeObject *obj); extern bool MimeAnyParentCMSSigned(MimeObject *obj);
extern void MimeCMSGetFromSender(MimeObject *obj, extern void MimeCMSGetFromSender(MimeObject *obj,
nsCString &from_addr, nsCString &from_addr,
nsCString &from_name, nsCString &from_name,
@ -142,82 +138,11 @@ MimeMultCMS_init (MimeObject *obj)
int16_t hash_type; int16_t hash_type;
nsresult rv; nsresult rv;
ct = MimeHeaders_get (hdrs, HEADER_CONTENT_TYPE, false, false);
if (!ct) return 0; /* #### bogus message? out of memory? */
micalg = MimeHeaders_get_parameter (ct, PARAM_MICALG, NULL, NULL);
PR_Free(ct);
ct = 0;
if (!micalg) return 0; /* #### bogus message? out of memory? */
if (!PL_strcasecmp(micalg, PARAM_MICALG_MD5) ||
!PL_strcasecmp(micalg, PARAM_MICALG_MD5_2))
hash_type = nsICryptoHash::MD5;
else if (!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 if (!PL_strcasecmp(micalg, PARAM_MICALG_MD2))
hash_type = nsICryptoHash::MD2;
else
hash_type = -1;
PR_Free(micalg);
micalg = 0;
if (hash_type == -1) return 0; /* #### bogus message? */
data = new MimeMultCMSdata; data = new MimeMultCMSdata;
if (!data) if (!data)
return 0; return 0;
data->self = obj; data->self = obj;
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);
data->parent_holds_stamp_p =
(obj->parent && mime_crypto_stamped_p(obj->parent));
data->parent_is_encrypted_p =
(obj->parent && MimeEncryptedCMS_encrypted_p (obj->parent));
/* If the parent of this object is a crypto-blob, then it's the grandparent
who would have written out the headers and prepared for a stamp...
(This s##t s$%#s.)
*/
if (data->parent_is_encrypted_p &&
!data->parent_holds_stamp_p &&
obj->parent && obj->parent->parent)
data->parent_holds_stamp_p =
mime_crypto_stamped_p (obj->parent->parent);
mime_stream_data *msd = (mime_stream_data *) (data->self->options->stream_closure); mime_stream_data *msd = (mime_stream_data *) (data->self->options->stream_closure);
if (msd) if (msd)
@ -266,6 +191,89 @@ MimeMultCMS_init (MimeObject *obj)
} // if channel } // if channel
} // if msd } // 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?
// TODO: should we show all contents, without any signature info?
if (data->smimeHeaderSink) {
data->smimeHeaderSink->SignedStatus(
MIMEGetRelativeCryptoNestLevel(data->self),
nsICMSMessageErrors::GENERAL_ERROR, nullptr, data->url);
}
delete data;
PR_SetError(-1, 0);
return 0;
}
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? */
}
if (!PL_strcasecmp(micalg, PARAM_MICALG_MD5) ||
!PL_strcasecmp(micalg, PARAM_MICALG_MD5_2))
hash_type = nsICryptoHash::MD5;
else if (!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
hash_type = -1;
PR_Free(micalg);
micalg = 0;
if (hash_type == -1) {
delete data;
return 0; /* #### bogus message? */
}
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; return data;
} }
@ -400,6 +408,17 @@ MimeMultCMS_free (void *crypto_closure)
delete data; 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) {
data->smimeHeaderSink->SignedStatus(
MIMEGetRelativeCryptoNestLevel(data->self),
nsICMSMessageErrors::GENERAL_ERROR, nullptr, data->url);
}
}
static char * static char *
MimeMultCMS_generate (void *crypto_closure) MimeMultCMS_generate (void *crypto_closure)
{ {

View file

@ -786,37 +786,25 @@ MimeMessage_write_headers_html (MimeObject *obj)
return status; return status;
} }
if (!msg->crypto_stamped_p) // If this is the outermost message, then now is the time to run the
{ // post_header_html_fn.
/* If we're not writing a xlation stamp, and this is the outermost if (obj->options && obj->options->state &&
message, then now is the time to run the post_header_html_fn.
(Otherwise, it will be run when the xlation-stamp is finally
closed off, in MimeXlateed_emit_buffered_child() or
MimeMultipartSigned_emit_child().)
*/
if (obj->options &&
obj->options->state &&
obj->options->generate_post_header_html_fn && obj->options->generate_post_header_html_fn &&
!obj->options->state->post_header_html_run_p) !obj->options->state->post_header_html_run_p) {
{
char *html = 0; char *html = 0;
PR_ASSERT(obj->options->state->first_data_written_p); PR_ASSERT(obj->options->state->first_data_written_p);
html = obj->options->generate_post_header_html_fn(NULL, html = obj->options->generate_post_header_html_fn(
obj->options->html_closure, NULL, obj->options->html_closure, msg->hdrs);
msg->hdrs);
obj->options->state->post_header_html_run_p = true; obj->options->state->post_header_html_run_p = true;
if (html) if (html) {
{
status = MimeObject_write(obj, html, strlen(html), false); status = MimeObject_write(obj, html, strlen(html), false);
PR_Free(html); PR_Free(html);
if (status < 0) if (status < 0) {
{
mimeEmitterEndHeader(obj->options, obj); mimeEmitterEndHeader(obj->options, obj);
return status; return status;
} }
} }
} }
}
mimeEmitterEndHeader(obj->options, obj); mimeEmitterEndHeader(obj->options, obj);

View file

@ -25,12 +25,6 @@ struct MimeMessage {
MimeContainer container; /* superclass variables */ MimeContainer container; /* superclass variables */
MimeHeaders *hdrs; /* headers of this message */ MimeHeaders *hdrs; /* headers of this message */
bool newline_p; /* whether the last line ended in a newline */ bool newline_p; /* whether the last line ended in a newline */
bool crypto_stamped_p; /* whether the header of this message has been
emitted expecting its child to emit HTML
which says that it is xlated. */
bool crypto_msg_signed_p; /* What the emitted xlation-stamp *says*. */
bool crypto_msg_encrypted_p;
bool grabSubject; /* Should we try to grab the subject of this message */ bool grabSubject; /* Should we try to grab the subject of this message */
int32_t bodyLength; /* Used for determining if the body has been truncated */ int32_t bodyLength; /* Used for determining if the body has been truncated */
int32_t sizeSoFar; /* The total size of the MIME message, once parsing is int32_t sizeSoFar; /* The total size of the MIME message, once parsing is

View file

@ -14,6 +14,7 @@
#include "msgCore.h" #include "msgCore.h"
#include "nsMimeStringResources.h" #include "nsMimeStringResources.h"
#include "mimemoz2.h" #include "mimemoz2.h"
#include "mimeeobj.h"
#include "nsIMimeConverter.h" // for MimeConverterOutputCallback #include "nsIMimeConverter.h" // for MimeConverterOutputCallback
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
@ -32,6 +33,8 @@ static void MimeMultipartSigned_finalize (MimeObject *);
static int MimeMultipartSigned_emit_child (MimeObject *obj); static int MimeMultipartSigned_emit_child (MimeObject *obj);
extern "C" MimeSuppressedCryptoClass mimeSuppressedCryptoClass;
static int static int
MimeMultipartSignedClassInitialize(MimeMultipartSignedClass *clazz) MimeMultipartSignedClassInitialize(MimeMultipartSignedClass *clazz)
{ {
@ -582,6 +585,11 @@ MimeMultipartSigned_emit_child (MimeObject *obj)
int status = 0; int status = 0;
MimeObject *body; MimeObject *body;
if (!sig->crypto_closure) {
// We might have decided to skip processing this part.
return 0;
}
NS_ASSERTION(sig->crypto_closure, "no crypto closure"); NS_ASSERTION(sig->crypto_closure, "no crypto closure");
/* Emit some HTML saying whether the signature was cool. /* Emit some HTML saying whether the signature was cool.
@ -594,15 +602,11 @@ MimeMultipartSigned_emit_child (MimeObject *obj)
obj->options->headers != MimeHeadersCitation && obj->options->headers != MimeHeadersCitation &&
sig->crypto_closure) sig->crypto_closure)
{ {
// Calling crypto_generate_html may trigger wanted side effects,
// but we're no longer using its results.
char *html = (((MimeMultipartSignedClass *) obj->clazz) char *html = (((MimeMultipartSignedClass *) obj->clazz)
->crypto_generate_html (sig->crypto_closure)); ->crypto_generate_html (sig->crypto_closure));
#if 0 // XXX For the moment, no HTML output. Fix this XXX // PR_FREEIF(html);
if (!html) return -1; /* MIME_OUT_OF_MEMORY? */
status = MimeObject_write(obj, html, strlen(html), false);
PR_Free(html);
if (status < 0) return status;
#endif
/* Now that we have written out the crypto stamp, the outermost header /* Now that we have written out the crypto stamp, the outermost header
block is well and truly closed. If this is in fact the outermost block is well and truly closed. If this is in fact the outermost
@ -719,6 +723,11 @@ MimeMultipartSigned_emit_child (MimeObject *obj)
if (!body) if (!body)
return -1; return -1;
if (mime_typep(body, (MimeObjectClass *)&mimeSuppressedCryptoClass)) {
((MimeMultipartSignedClass *)obj->clazz)
->crypto_notify_suppressed_child(sig->crypto_closure);
}
#ifdef MIME_DRAFTS #ifdef MIME_DRAFTS
if (body->options->decompose_file_p) { if (body->options->decompose_file_p) {
body->options->signed_p = true; body->options->signed_p = true;

View file

@ -105,6 +105,8 @@ struct MimeMultipartSignedClass {
char * (*crypto_generate_html) (void *crypto_closure); char * (*crypto_generate_html) (void *crypto_closure);
void (*crypto_notify_suppressed_child)(void *crypto_closure);
void (*crypto_free) (void *crypto_closure); void (*crypto_free) (void *crypto_closure);
}; };

View file

@ -43,6 +43,7 @@ extern "C" MimeObjectClass mimeMultipartRelatedClass;
extern "C" MimeObjectClass mimeMultipartSignedClass; extern "C" MimeObjectClass mimeMultipartSignedClass;
extern "C" MimeObjectClass mimeInlineTextVCardClass; extern "C" MimeObjectClass mimeInlineTextVCardClass;
extern "C" MimeExternalObjectClass mimeExternalObjectClass; extern "C" MimeExternalObjectClass mimeExternalObjectClass;
extern "C" MimeSuppressedCryptoClass mimeSuppressedCryptoClass;
#if defined(DEBUG) && defined(XP_UNIX) #if defined(DEBUG) && defined(XP_UNIX)
static int MimeMultipart_debug_print (MimeObject *, PRFileDesc *, int32_t); static int MimeMultipart_debug_print (MimeObject *, PRFileDesc *, int32_t);
@ -471,7 +472,9 @@ MimeMultipart_create_child(MimeObject *obj)
part functions set correctly */ part functions set correctly */
!mime_typep(body, (MimeObjectClass*) &mimeMultipartClass) !mime_typep(body, (MimeObjectClass*) &mimeMultipartClass)
#endif #endif
&& ! (mime_typep(body, (MimeObjectClass*)&mimeExternalObjectClass) && !strcmp(body->content_type, "text/x-vcard")) && !((mime_typep(body, (MimeObjectClass *)&mimeExternalObjectClass) ||
mime_typep(body, (MimeObjectClass *)&mimeSuppressedCryptoClass)) &&
!strcmp(body->content_type, "text/x-vcard"))
) )
{ {
status = obj->options->decompose_file_init_fn ( obj->options->stream_closure, mult->hdrs ); status = obj->options->decompose_file_init_fn ( obj->options->stream_closure, mult->hdrs );
@ -575,7 +578,9 @@ MimeMultipart_close_child(MimeObject *object)
part functions set correctly */ part functions set correctly */
!mime_typep(kid,(MimeObjectClass*) &mimeMultipartClass) !mime_typep(kid,(MimeObjectClass*) &mimeMultipartClass)
#endif #endif
&& !(mime_typep(kid, (MimeObjectClass*)&mimeExternalObjectClass) && !strcmp(kid->content_type, "text/x-vcard")) && !((mime_typep(kid, (MimeObjectClass *)&mimeExternalObjectClass) ||
mime_typep(kid, (MimeObjectClass *)&mimeSuppressedCryptoClass)) &&
!strcmp(kid->content_type, "text/x-vcard"))
) )
{ {
status = object->options->decompose_file_close_fn ( object->options->stream_closure ); status = object->options->decompose_file_close_fn ( object->options->stream_closure );