netscape-revival
lib/libmime2/mimempkc.c
/* -*- Mode: C; tab-width: 4 -*-
mimempkc.c --- definition of the MimeMultipartSignedPKCS7 class (see mimei.h)
Copyright © 1997 Netscape Communications Corporation, all rights reserved.
Created: Jamie Zawinski <jwz@netscape.com>, 23-Sep-96.
*/
#include "secpkcs7.h" /* Interface to libsec.a */
#include "mimempkc.h"
#define MIME_SUPERCLASS mimeMultipartSignedClass
MimeDefClass(MimeMultipartSignedPKCS7, MimeMultipartSignedPKCS7Class,
mimeMultipartSignedPKCS7Class, &MIME_SUPERCLASS);
static int MimeMultipartSignedPKCS7_initialize (MimeObject *);
static void *MimeMultPKCS7_init (MimeObject *);
static int MimeMultPKCS7_data_hash (char *, int32, void *);
static int MimeMultPKCS7_sig_hash (char *, int32, void *);
static int MimeMultPKCS7_data_eof (void *, XP_Bool);
static int MimeMultPKCS7_sig_eof (void *, XP_Bool);
static int MimeMultPKCS7_sig_init (void *, MimeObject *, MimeHeaders *);
#ifdef MIME_TO_HTML
static char * MimeMultPKCS7_generate (void *);
#endif /* MIME_TO_HTML */
static void MimeMultPKCS7_free (void *);
static void MimeMultPKCS7_get_content_info (MimeObject *,
SEC_PKCS7ContentInfo **,
char **, int32 *, int32 *);
extern int SEC_ERROR_CERT_ADDR_MISMATCH;
static int
MimeMultipartSignedPKCS7ClassInitialize(MimeMultipartSignedPKCS7Class *class)
{
MimeObjectClass *oclass = (MimeObjectClass *) class;
MimeMultipartSignedClass *sclass = (MimeMultipartSignedClass *) class;
oclass->initialize = MimeMultipartSignedPKCS7_initialize;
sclass->crypto_init = MimeMultPKCS7_init;
sclass->crypto_data_hash = MimeMultPKCS7_data_hash;
sclass->crypto_data_eof = MimeMultPKCS7_data_eof;
sclass->crypto_signature_init = MimeMultPKCS7_sig_init;
sclass->crypto_signature_hash = MimeMultPKCS7_sig_hash;
sclass->crypto_signature_eof = MimeMultPKCS7_sig_eof;
#ifdef MIME_TO_HTML
sclass->crypto_generate_html = MimeMultPKCS7_generate;
#endif /* MIME_TO_HTML */
sclass->crypto_free = MimeMultPKCS7_free;
class->get_content_info = MimeMultPKCS7_get_content_info;
XP_ASSERT(!oclass->class_initialized);
return 0;
}
static int
MimeMultipartSignedPKCS7_initialize (MimeObject *object)
{
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
}
typedef struct MimeMultPKCS7data {
HASH_HashType hash_type;
void *data_hash_context;
SEC_PKCS7DecoderContext *sig_decoder_context;
SEC_PKCS7ContentInfo *content_info;
char *sender_addr;
int32 decode_error;
int32 verify_error;
SECItem item;
MimeObject *self;
XP_Bool parent_is_encrypted_p;
XP_Bool parent_holds_stamp_p;
} MimeMultPKCS7data;
static void
MimeMultPKCS7_get_content_info(MimeObject *obj,
SEC_PKCS7ContentInfo **content_info_ret,
char **sender_email_addr_return,
int32 *decode_error_ret,
int32 *verify_error_ret)
{
MimeMultipartSigned *msig = (MimeMultipartSigned *) obj;
if (msig && msig->crypto_closure)
{
MimeMultPKCS7data *data = (MimeMultPKCS7data *) msig->crypto_closure;
*decode_error_ret = data->decode_error;
*verify_error_ret = data->verify_error;
*content_info_ret = SEC_PKCS7CopyContentInfo(data->content_info);
if (sender_email_addr_return)
*sender_email_addr_return = (data->sender_addr
? XP_STRDUP(data->sender_addr)
: 0);
}
}
/* #### MimeEncryptedPKCS7 and MimeMultipartSignedPKCS7 have a sleazy,
incestuous, dysfunctional relationship. */
extern XP_Bool MimeEncryptedPKCS7_encrypted_p (MimeObject *obj);
extern XP_Bool MimePKCS7HeadersAndCertsMatch(MimeObject *obj,
SEC_PKCS7ContentInfo *,
char **);
extern char *MimePKCS7_MakeSAURL(MimeObject *obj);
static void *
MimeMultPKCS7_init (MimeObject *obj)
{
MimeHeaders *hdrs = obj->headers;
MimeMultPKCS7data *data = 0;
char *ct, *micalg;
HASH_HashType hash_type;
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);
XP_FREE(ct);
ct = 0;
if (!micalg) return 0; /* #### bogus message? out of memory? */
if (!strcasecomp(micalg, PARAM_MICALG_MD5))
hash_type = HASH_AlgMD5;
else if (!strcasecomp(micalg, PARAM_MICALG_SHA1) ||
!strcasecomp(micalg, PARAM_MICALG_SHA1_2) ||
!strcasecomp(micalg, PARAM_MICALG_SHA1_3) ||
!strcasecomp(micalg, PARAM_MICALG_SHA1_4) ||
!strcasecomp(micalg, PARAM_MICALG_SHA1_5))
hash_type = HASH_AlgSHA1;
else if (!strcasecomp(micalg, PARAM_MICALG_MD2))
hash_type = HASH_AlgMD2;
else
hash_type = HASH_AlgNULL;
XP_FREE(micalg);
micalg = 0;
if (hash_type == HASH_AlgNULL) return 0; /* #### bogus message? */
data = (MimeMultPKCS7data *) XP_ALLOC(sizeof(*data));
if (!data) return 0;
XP_MEMSET(data, 0, sizeof(*data));
data->self = obj;
data->hash_type = hash_type;
XP_ASSERT(SECHashObjects[data->hash_type].create);
if (!SECHashObjects[data->hash_type].create) return 0;
XP_ASSERT(!data->data_hash_context);
XP_ASSERT(!data->sig_decoder_context);
data->data_hash_context = SECHashObjects[data->hash_type].create();
XP_ASSERT(data->data_hash_context);
if (!data->data_hash_context)
{
XP_FREE(data);
return 0;
}
XP_ASSERT(SECHashObjects[data->hash_type].begin);
if (!SECHashObjects[data->hash_type].begin) return 0;
XP_SetError(0);
SECHashObjects[data->hash_type].begin(data->data_hash_context);
if (!data->decode_error)
{
data->decode_error = XP_GetError();
if (data->decode_error)
{
XP_FREE(data);
return 0;
}
}
data->parent_holds_stamp_p =
(obj->parent && mime_crypto_stamped_p(obj->parent));
data->parent_is_encrypted_p =
(obj->parent && MimeEncryptedPKCS7_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);
return data;
}
static int
MimeMultPKCS7_data_hash (char *buf, int32 size, void *crypto_closure)
{
MimeMultPKCS7data *data = (MimeMultPKCS7data *) crypto_closure;
XP_ASSERT(data && data->data_hash_context);
if (!data || !data->data_hash_context) return -1;
XP_ASSERT(SECHashObjects[data->hash_type].update);
if (!SECHashObjects[data->hash_type].update) return 0;
XP_ASSERT(!data->sig_decoder_context);
XP_SetError(0);
SECHashObjects[data->hash_type].update(data->data_hash_context,
(unsigned char *) buf, size);
if (!data->verify_error)
data->verify_error = XP_GetError();
return 0;
}
static int
MimeMultPKCS7_data_eof (void *crypto_closure, XP_Bool abort_p)
{
MimeMultPKCS7data *data = (MimeMultPKCS7data *) crypto_closure;
XP_ASSERT(data && data->data_hash_context);
if (!data || !data->data_hash_context) return -1;
XP_ASSERT(SECHashObjects[data->hash_type].end);
if (!SECHashObjects[data->hash_type].end) return 0;
XP_ASSERT(!data->sig_decoder_context);
data->item.len = SECHashObjects[data->hash_type].length;
data->item.data = (unsigned char *) XP_ALLOC(data->item.len);
if (!data->item.data) return MK_OUT_OF_MEMORY;
XP_SetError(0);
SECHashObjects[data->hash_type].end(data->data_hash_context,
data->item.data,
&data->item.len,
data->item.len);
if (!data->verify_error)
data->verify_error = XP_GetError();
SECHashObjects[data->hash_type].destroy(data->data_hash_context, TRUE);
data->data_hash_context = 0;
/* 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
MimeMultPKCS7_sig_init (void *crypto_closure,
MimeObject *multipart_object,
MimeHeaders *signature_hdrs)
{
MimeMultPKCS7data *data = (MimeMultPKCS7data *) crypto_closure;
MimeDisplayOptions *opts = multipart_object->options;
char *ct;
int status = 0;
XP_ASSERT(!data->data_hash_context);
XP_ASSERT(!data->sig_decoder_context);
XP_ASSERT(signature_hdrs);
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 || strcasecomp(ct, APPLICATION_XPKCS7_SIGNATURE))
status = -1; /* #### error msg about bogus message */
FREEIF(ct);
if (status < 0) return status;
XP_SetError(0);
data->sig_decoder_context =
SEC_PKCS7DecoderStart(0, 0, /* no content cb */
((SECKEYGetPasswordKey) opts->passwd_prompt_fn),
opts->passwd_prompt_fn_arg);
if (!data->sig_decoder_context)
{
status = XP_GetError();
XP_ASSERT(status < 0);
if (status >= 0) status = -1;
}
return status;
}
static int
MimeMultPKCS7_sig_hash (char *buf, int32 size, void *crypto_closure)
{
MimeMultPKCS7data *data = (MimeMultPKCS7data *) crypto_closure;
XP_ASSERT(data && data->sig_decoder_context);
if (!data || !data->sig_decoder_context) return -1;
XP_ASSERT(!data->data_hash_context);
XP_SetError(0);
if (SEC_PKCS7DecoderUpdate(data->sig_decoder_context, buf, size)
!= SECSuccess)
{
if (!data->verify_error)
data->verify_error = XP_GetError();
XP_ASSERT(data->verify_error < 0);
if (data->verify_error >= 0)
data->verify_error = -1;
}
return 0;
}
static int
MimeMultPKCS7_sig_eof (void *crypto_closure, XP_Bool abort_p)
{
MimeMultPKCS7data *data = (MimeMultPKCS7data *) crypto_closure;
if (!data) return -1;
XP_ASSERT(!data->data_hash_context);
/* 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.
*/
XP_ASSERT(!data->content_info);
if (data->sig_decoder_context)
{
XP_SetError(0);
data->content_info = SEC_PKCS7DecoderFinish (data->sig_decoder_context);
data->sig_decoder_context = 0;
if (!data->content_info && !data->verify_error)
data->verify_error = XP_GetError();
XP_ASSERT(data->content_info ||
data->verify_error || data->decode_error);
}
return 0;
}
static void
MimeMultPKCS7_free (void *crypto_closure)
{
MimeMultPKCS7data *data = (MimeMultPKCS7data *) crypto_closure;
XP_ASSERT(data);
if (!data) return;
FREEIF(data->sender_addr);
if (data->data_hash_context)
{
SECHashObjects[data->hash_type].destroy(data->data_hash_context, TRUE);
data->data_hash_context = 0;
}
if (data->sig_decoder_context)
{
SEC_PKCS7ContentInfo *cinfo =
SEC_PKCS7DecoderFinish (data->sig_decoder_context);
if (cinfo)
SEC_PKCS7DestroyContentInfo(cinfo);
}
if (data->content_info)
{
SEC_PKCS7DestroyContentInfo(data->content_info);
data->content_info = 0;
}
FREEIF(data->item.data);
XP_FREE(data);
}
#ifdef MIME_TO_HTML
static char *
MimeMultPKCS7_generate (void *crypto_closure)
{
MimeMultPKCS7data *data = (MimeMultPKCS7data *) crypto_closure;
XP_Bool signed_p = TRUE;
XP_Bool good_p = TRUE;
XP_Bool encrypted_p;
XP_ASSERT(data);
if (!data) return 0;
encrypted_p = data->parent_is_encrypted_p;
if (data->content_info)
{
XP_SetError(0);
good_p =
SEC_PKCS7VerifyDetachedSignature(data->content_info,
certUsageEmailSigner,
&data->item,
data->hash_type,
TRUE); /* #### keepcerts */
if (!good_p)
{
if (!data->verify_error)
data->verify_error = XP_GetError();
XP_ASSERT(data->verify_error < 0);
if (data->verify_error >= 0)
data->verify_error = -1;
}
else
{
good_p = MimePKCS7HeadersAndCertsMatch(data->self,
data->content_info,
&data->sender_addr);
if (!good_p && !data->verify_error)
data->verify_error = SEC_ERROR_CERT_ADDR_MISMATCH;
}
if (SEC_PKCS7ContainsCertsOrCrls(data->content_info))
{
/* #### call libsec telling it to import the certs */
}
/* Don't free these yet -- keep them around for the lifetime of the
MIME object, so that we can get at the security info of sub-parts
of the currently-displayed message. */
#if 0
SEC_PKCS7DestroyContentInfo(data->content_info);
data->content_info = 0;
#endif /* 0 */
}
else
{
/* 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. Anyway, it sure ain't good_p.
*/
good_p = FALSE;
}
XP_ASSERT(data->self);
if (data->self && data->self->parent)
mime_set_crypto_stamp(data->self->parent, signed_p, encrypted_p);
{
char *stamp_url = (data->self
? MimePKCS7_MakeSAURL(data->self)
: 0);
char *result =
MimeHeaders_make_crypto_stamp (encrypted_p, signed_p, good_p,
data->parent_holds_stamp_p,
stamp_url);
FREEIF(stamp_url);
return result;
}
}
#endif /* MIME_TO_HTML */