diff options
-rw-r--r-- | camel/ChangeLog | 23 | ||||
-rw-r--r-- | camel/camel-cipher-context.c | 127 | ||||
-rw-r--r-- | camel/camel-cipher-context.h | 42 | ||||
-rw-r--r-- | camel/camel-gpg-context.c | 90 | ||||
-rw-r--r-- | camel/camel-multipart-encrypted.c | 14 | ||||
-rw-r--r-- | camel/camel-multipart-signed.c | 5 | ||||
-rw-r--r-- | camel/camel-smime-context.c | 245 | ||||
-rw-r--r-- | camel/camel-smime-context.h | 7 |
8 files changed, 380 insertions, 173 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index faff76a429..a61c63cbcb 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,26 @@ +2003-11-10 Not Zed <NotZed@Ximian.com> + + * camel-smime-context.c (sm_verify_cmsg): split out the CMSMessage + verification code so it can be used from enveloped or externally + signed data. + + * camel-cipher-context.c (camel_cipher_verify): only take a + mimepart, internally handle multiparts and the hash. + +2003-11-07 Not Zed <NotZed@Ximian.com> + + * camel-cipher-context.c: make ciphervalidity a public structure, + added encrypt status. + (camel_cipher_decrypt): changed to return a ciphervalidity. fixed + implementations. + (camel_cipher_validity_*): Fixed implementations to match new + structure, some of this is now redundant. + +2003-11-06 Not Zed <NotZed@Ximian.com> + + * camel-smime-context.c (camel_smime_context_describe_part): + implement. + 2003-11-05 Jeffrey Stedfast <fejj@ximian.com> * providers/smtp/camel-smtp-transport.c (connect_to_server): Don't diff --git a/camel/camel-cipher-context.c b/camel/camel-cipher-context.c index dff60c25aa..9e741e4531 100644 --- a/camel/camel-cipher-context.c +++ b/camel/camel-cipher-context.c @@ -24,8 +24,10 @@ #include <config.h> #endif -#include <glib.h> #include <pthread.h> +#include <string.h> + +#include <glib.h> #include "camel-cipher-context.h" #include "camel-stream.h" @@ -132,8 +134,7 @@ camel_cipher_sign (CamelCipherContext *context, const char *userid, CamelCipherH } static CamelCipherValidity * -cipher_verify (CamelCipherContext *context, CamelCipherHash hash, struct _CamelStream *istream, - struct _CamelMimePart *sigpart, CamelException *ex) +cipher_verify (CamelCipherContext *context, struct _CamelMimePart *sigpart, CamelException *ex) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Verifying is not supported by this cipher")); @@ -143,8 +144,7 @@ cipher_verify (CamelCipherContext *context, CamelCipherHash hash, struct _CamelS /** * camel_cipher_verify: * @context: Cipher Context - * @istream: input stream - * @sigstream: optional detached-signature stream + * @ipart: part to verify * @ex: exception * * Verifies the signature. If @istream is a clearsigned stream, @@ -157,8 +157,7 @@ cipher_verify (CamelCipherContext *context, CamelCipherHash hash, struct _CamelS * execute at all. **/ CamelCipherValidity * -camel_cipher_verify (CamelCipherContext *context, CamelCipherHash hash, struct _CamelStream *istream, - struct _CamelMimePart *sigpart, CamelException *ex) +camel_cipher_verify (CamelCipherContext *context, struct _CamelMimePart *ipart, CamelException *ex) { CamelCipherValidity *valid; @@ -166,7 +165,7 @@ camel_cipher_verify (CamelCipherContext *context, CamelCipherHash hash, struct _ CIPHER_LOCK(context); - valid = CCC_CLASS (context)->verify (context, hash, istream, sigpart, ex); + valid = CCC_CLASS (context)->verify (context, ipart, ex); CIPHER_UNLOCK(context); @@ -213,8 +212,8 @@ camel_cipher_encrypt (CamelCipherContext *context, const char *userid, GPtrArray return retval; } -static struct _CamelMimePart * -cipher_decrypt(CamelCipherContext *context, struct _CamelMimePart *ipart, CamelException *ex) +static CamelCipherValidity * +cipher_decrypt(CamelCipherContext *context, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Decryption is not supported by this cipher")); @@ -223,30 +222,29 @@ cipher_decrypt(CamelCipherContext *context, struct _CamelMimePart *ipart, CamelE /** * camel_cipher_decrypt: - * @context: Cipher Context - * @ciphertext: ciphertext stream (ie input stream) - * @cleartext: cleartext stream (ie output stream) - * @ex: exception - * - * Decrypts the ciphertext input stream and writes the resulting - * cleartext to the output stream. - * - * Return value: 0 for success or -1 for failure. + * @context: + * @ipart: + * @opart: + * @ex: + * + * Decrypts @ipart into @opart. + * + * Return value: A validity/encryption status. **/ -struct _CamelMimePart * -camel_cipher_decrypt (CamelCipherContext *context, struct _CamelMimePart *ipart, CamelException *ex) +CamelCipherValidity * +camel_cipher_decrypt(CamelCipherContext *context, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex) { - struct _CamelMimePart *opart; + CamelCipherValidity *valid; g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL); CIPHER_LOCK(context); - opart = CCC_CLASS (context)->decrypt (context, ipart, ex); + valid = CCC_CLASS (context)->decrypt (context, ipart, opart, ex); CIPHER_UNLOCK(context); - return opart; + return valid; } static int @@ -341,20 +339,15 @@ camel_cipher_hash_to_id(CamelCipherContext *context, CamelCipherHash hash) } /* Cipher Validity stuff */ -struct _CamelCipherValidity { - gboolean valid; - gchar *description; -}; CamelCipherValidity * camel_cipher_validity_new (void) { CamelCipherValidity *validity; - validity = g_new (CamelCipherValidity, 1); - validity->valid = FALSE; - validity->description = NULL; - + validity = g_malloc(sizeof(*validity)); + camel_cipher_validity_init(validity); + return validity; } @@ -362,18 +355,15 @@ void camel_cipher_validity_init (CamelCipherValidity *validity) { g_assert (validity != NULL); - - validity->valid = FALSE; - validity->description = NULL; + + memset(validity, 0, sizeof(*validity)); } gboolean camel_cipher_validity_get_valid (CamelCipherValidity *validity) { - if (validity == NULL) - return FALSE; - - return validity->valid; + return validity != NULL + && validity->sign.status == CAMEL_CIPHER_VALIDITY_SIGN_GOOD; } void @@ -381,7 +371,7 @@ camel_cipher_validity_set_valid (CamelCipherValidity *validity, gboolean valid) { g_assert (validity != NULL); - validity->valid = valid; + validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_GOOD; } gchar * @@ -390,7 +380,7 @@ camel_cipher_validity_get_description (CamelCipherValidity *validity) if (validity == NULL) return NULL; - return validity->description; + return validity->sign.description; } void @@ -398,18 +388,47 @@ camel_cipher_validity_set_description (CamelCipherValidity *validity, const gcha { g_assert (validity != NULL); - g_free (validity->description); - validity->description = g_strdup (description); + g_free(validity->sign.description); + validity->sign.description = g_strdup(description); } void camel_cipher_validity_clear (CamelCipherValidity *validity) { g_assert (validity != NULL); - - validity->valid = FALSE; - g_free (validity->description); - validity->description = NULL; + + g_free(validity->sign.description); + g_free(validity->encrypt.description); + camel_cipher_validity_init(validity); +} + +/** + * camel_cipher_validity_envelope: + * @validity: + * @outer: + * + * Calculate a conglomerate validity based on wrapping one secure part inside + * another one. + **/ +void +camel_cipher_validity_envelope(CamelCipherValidity *valid, CamelCipherValidity *outer) +{ + if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE + && valid->encrypt.status == CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE + && outer->sign.status == CAMEL_CIPHER_VALIDITY_SIGN_NONE + && outer->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) { + /* case 1: only signed inside only encrypted -> merge both */ + valid->encrypt.status = outer->encrypt.status; + valid->encrypt.description = g_strdup(outer->encrypt.description); + } else if (valid->sign.status == CAMEL_CIPHER_VALIDITY_SIGN_NONE + && valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE + && outer->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE + && outer->encrypt.status == CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) { + /* case 2: only encrypted inside only signed */ + valid->sign.status = outer->sign.status; + valid->sign.description = g_strdup(outer->sign.description); + } + /* Otherwise, I dunno - what do you do? */ } void @@ -417,9 +436,9 @@ camel_cipher_validity_free (CamelCipherValidity *validity) { if (validity == NULL) return; - - g_free (validity->description); - g_free (validity); + + camel_cipher_validity_clear(validity); + g_free(validity); } /* ********************************************************************** */ @@ -511,6 +530,7 @@ cc_prepare_sign(CamelMimePart *part) /** * camel_cipher_canonical_to_stream: * @part: Part to write. + * @flags: flags for the canonicalisation filter (CamelMimeFilterCanon) * @ostream: stream to write canonicalised output to. * * Writes a part to a stream in a canonicalised format, suitable for signing/encrypting. @@ -520,16 +540,17 @@ cc_prepare_sign(CamelMimePart *part) * Return value: -1 on error; **/ int -camel_cipher_canonical_to_stream(CamelMimePart *part, CamelStream *ostream) +camel_cipher_canonical_to_stream(CamelMimePart *part, guint32 flags, CamelStream *ostream) { CamelStreamFilter *filter; CamelMimeFilter *canon; int res = -1; - cc_prepare_sign(part); + if (flags & (CAMEL_MIME_FILTER_CANON_FROM|CAMEL_MIME_FILTER_CANON_STRIP)) + cc_prepare_sign(part); filter = camel_stream_filter_new_with_stream(ostream); - canon = camel_mime_filter_canon_new(CAMEL_MIME_FILTER_CANON_STRIP|CAMEL_MIME_FILTER_CANON_CRLF|CAMEL_MIME_FILTER_CANON_FROM); + canon = camel_mime_filter_canon_new(flags); camel_stream_filter_add(filter, canon); camel_object_unref(canon); diff --git a/camel/camel-cipher-context.h b/camel/camel-cipher-context.h index db83e92438..d3aea4c6b0 100644 --- a/camel/camel-cipher-context.h +++ b/camel/camel-cipher-context.h @@ -51,6 +51,31 @@ typedef enum { CAMEL_CIPHER_HASH_HAVAL5160 } CamelCipherHash; +enum _camel_cipher_validity_sign_t { + CAMEL_CIPHER_VALIDITY_SIGN_NONE, + CAMEL_CIPHER_VALIDITY_SIGN_GOOD, + CAMEL_CIPHER_VALIDITY_SIGN_BAD, + CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN, +}; + +enum _camel_cipher_validity_encrypt_t { + CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE, + CAMEL_CIPHER_VALIDITY_ENCRYPT_WEAK, + CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED, /* encrypted, unknown strenght */ + CAMEL_CIPHER_VALIDITY_ENCRYPT_STRONG, +}; + +struct _CamelCipherValidity { + struct { + enum _camel_cipher_validity_sign_t status; + char *description; + } sign; + struct { + enum _camel_cipher_validity_encrypt_t status; + char *description; + } encrypt; +}; + typedef struct _CamelCipherContext { CamelObject parent_object; @@ -73,15 +98,14 @@ typedef struct _CamelCipherContextClass { int (*sign) (CamelCipherContext *context, const char *userid, CamelCipherHash hash, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex); - CamelCipherValidity * (*verify) (CamelCipherContext *context, CamelCipherHash hash, - struct _CamelStream *istream, struct _CamelMimePart *sigpart, - CamelException *ex); + CamelCipherValidity * (*verify) (CamelCipherContext *context, struct _CamelMimePart *ipart, CamelException *ex); int (*encrypt) (CamelCipherContext *context, const char *userid, GPtrArray *recipients, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex); - struct _CamelMimePart *(*decrypt) (CamelCipherContext *context, struct _CamelMimePart *ipart, CamelException *ex); + CamelCipherValidity *(*decrypt) (CamelCipherContext *context, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, + CamelException *ex); int (*import_keys) (CamelCipherContext *context, struct _CamelStream *istream, CamelException *ex); @@ -108,13 +132,12 @@ const char * camel_cipher_hash_to_id (CamelCipherContext *context, CamelCip /* cipher routines */ int camel_cipher_sign (CamelCipherContext *context, const char *userid, CamelCipherHash hash, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex); -CamelCipherValidity *camel_cipher_verify (CamelCipherContext *context, CamelCipherHash hash, - struct _CamelStream *istream, struct _CamelMimePart *sigpart, - CamelException *ex); +CamelCipherValidity *camel_cipher_verify (CamelCipherContext *context, struct _CamelMimePart *ipart, CamelException *ex); int camel_cipher_encrypt (CamelCipherContext *context, const char *userid, GPtrArray *recipients, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex); -struct _CamelMimePart *camel_cipher_decrypt (CamelCipherContext *context, struct _CamelMimePart *ipart, CamelException *ex); +CamelCipherValidity *camel_cipher_decrypt (CamelCipherContext *context, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, + CamelException *ex); /* key/certificate routines */ int camel_cipher_import_keys (CamelCipherContext *context, struct _CamelStream *istream, @@ -130,10 +153,11 @@ void camel_cipher_validity_set_valid (CamelCipherValidity *valid char *camel_cipher_validity_get_description (CamelCipherValidity *validity); void camel_cipher_validity_set_description (CamelCipherValidity *validity, const char *description); void camel_cipher_validity_clear (CamelCipherValidity *validity); +void camel_cipher_validity_envelope(CamelCipherValidity *valid, CamelCipherValidity *outer); void camel_cipher_validity_free (CamelCipherValidity *validity); /* utility functions */ -int camel_cipher_canonical_to_stream(CamelMimePart *part, CamelStream *ostream); +int camel_cipher_canonical_to_stream(CamelMimePart *part, guint32 flags, CamelStream *ostream); #ifdef __cplusplus } diff --git a/camel/camel-gpg-context.c b/camel/camel-gpg-context.c index a3b158d943..71b9106e10 100644 --- a/camel/camel-gpg-context.c +++ b/camel/camel-gpg-context.c @@ -1225,9 +1225,9 @@ gpg_sign (CamelCipherContext *context, const char *userid, CamelCipherHash hash, /* Note: see rfc2015 or rfc3156, section 5 */ /* FIXME: stream this, we stream output at least */ - /*prepare_sign(content);*/ istream = camel_stream_mem_new(); - if (camel_cipher_canonical_to_stream(ipart, istream) == -1) { + if (camel_cipher_canonical_to_stream(ipart, CAMEL_MIME_FILTER_CANON_STRIP|CAMEL_MIME_FILTER_CANON_CRLF|CAMEL_MIME_FILTER_CANON_FROM, + istream) == -1) { camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not generate signing data: %s"), g_strerror(errno)); goto fail; @@ -1346,32 +1346,54 @@ swrite (CamelMimePart *sigpart) } static CamelCipherValidity * -gpg_verify (CamelCipherContext *context, CamelCipherHash hash, - CamelStream *istream, CamelMimePart *sigpart, - CamelException *ex) +gpg_verify (CamelCipherContext *context, CamelMimePart *ipart, CamelException *ex) { CamelCipherValidity *validity; - const char *diagnostics = NULL; - struct _GpgCtx *gpg; + const char *diagnostics = NULL, *tmp; + struct _GpgCtx *gpg = NULL; char *sigfile = NULL; gboolean valid; - - if (sigpart != NULL) { - /* We are going to verify a detached signature so save - the signature to a temp file. */ - sigfile = swrite (sigpart); - if (sigfile == NULL) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot verify message signature: " - "could not create temp file: %s"), - g_strerror (errno)); - return NULL; - } + CamelContentType *ct; + CamelMimePart *sigpart, *datapart; + CamelStream *istream = NULL; + CamelMultipart *mps; + + ct = camel_mime_part_get_content_type(ipart); + tmp = camel_content_type_param(ct, "protocol"); + mps = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)ipart); + if (!camel_content_type_is(ct, "multipart", "signed") + || !CAMEL_IS_MULTIPART_SIGNED(mps) + || tmp == NULL + || g_ascii_strcasecmp(tmp, context->sign_protocol) != 0) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot verify message signature: Incorrect message format")); + return NULL; + } + + datapart = camel_multipart_get_part(mps, CAMEL_MULTIPART_SIGNED_CONTENT); + sigpart = camel_multipart_get_part(mps, CAMEL_MULTIPART_SIGNED_SIGNATURE); + + if (sigpart == NULL || datapart == NULL) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot verify message signature: Incorrect message format")); + goto exception; + } + + sigfile = swrite (sigpart); + if (sigfile == NULL) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot verify message signature: could not create temp file: %s"), + g_strerror (errno)); + goto exception; } + + istream = camel_stream_mem_new(); + camel_cipher_canonical_to_stream(datapart, CAMEL_MIME_FILTER_CANON_CRLF, istream); + camel_stream_reset(istream); gpg = gpg_ctx_new (context->session); gpg_ctx_set_mode (gpg, GPG_CTX_MODE_VERIFY); - gpg_ctx_set_hash (gpg, hash); + gpg_ctx_set_hash (gpg, camel_cipher_id_to_hash(context, camel_content_type_param(ct, "micalg"))); gpg_ctx_set_sigfile (gpg, sigfile); gpg_ctx_set_istream (gpg, istream); @@ -1413,7 +1435,10 @@ gpg_verify (CamelCipherContext *context, CamelCipherHash hash, exception: gpg_ctx_free (gpg); - + + if (istream) + camel_object_unref(istream); + if (sigfile) { unlink (sigfile); g_free (sigfile); @@ -1436,7 +1461,7 @@ gpg_encrypt (CamelCipherContext *context, const char *userid, GPtrArray *recipie ostream = camel_stream_mem_new(); istream = camel_stream_mem_new(); - if (camel_cipher_canonical_to_stream(ipart, istream) == -1) { + if (camel_cipher_canonical_to_stream(ipart, CAMEL_MIME_FILTER_CANON_CRLF, istream) == -1) { camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not generate encrypting data: %s"), g_strerror(errno)); goto fail1; @@ -1512,7 +1537,7 @@ gpg_encrypt (CamelCipherContext *context, const char *userid, GPtrArray *recipie mpe = camel_multipart_encrypted_new(); ct = camel_content_type_new("multipart", "encrypted"); - camel_content_type_set_param(ct, "protocol", context->sign_protocol); + camel_content_type_set_param(ct, "protocol", context->encrypt_protocol); camel_data_wrapper_set_mime_type_field((CamelDataWrapper *)mpe, ct); camel_content_type_unref(ct); camel_multipart_set_boundary((CamelMultipart *)mpe, NULL); @@ -1535,11 +1560,11 @@ fail1: return res; } -static CamelMimePart * -gpg_decrypt (CamelCipherContext *context, CamelMimePart *ipart, CamelException *ex) +static CamelCipherValidity * +gpg_decrypt(CamelCipherContext *context, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex) { struct _GpgCtx *gpg; - CamelMimePart *opart = NULL; + CamelCipherValidity *valid = NULL; CamelStream *ostream, *istream; istream = camel_stream_mem_new(); @@ -1588,20 +1613,21 @@ gpg_decrypt (CamelCipherContext *context, CamelMimePart *ipart, CamelException * goto fail; } - opart = camel_mime_part_new(); camel_stream_reset(ostream); - if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)opart, ostream) == -1) { + if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)opart, ostream) != -1) { + valid = camel_cipher_validity_new(); + valid->encrypt.description = g_strdup(_("Encrypted content")); + valid->encrypt.status = CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED; + } else { camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Unable to parse message content")); - camel_object_unref(opart); - opart = NULL; } fail: camel_object_unref(ostream); camel_object_unref(istream); gpg_ctx_free (gpg); - - return opart; + + return valid; } static int diff --git a/camel/camel-multipart-encrypted.c b/camel/camel-multipart-encrypted.c index 2dfe3b4914..aa036bec23 100644 --- a/camel/camel-multipart-encrypted.c +++ b/camel/camel-multipart-encrypted.c @@ -205,6 +205,7 @@ camel_multipart_encrypted_decrypt (CamelMultipartEncrypted *mpe, { CamelMimePart *version_part, *encrypted_part, *decrypted_part; CamelContentType *mime_type; + CamelCipherValidity *valid; CamelDataWrapper *wrapper; const char *protocol; char *content_type; @@ -256,11 +257,16 @@ camel_multipart_encrypted_decrypt (CamelMultipartEncrypted *mpe, _("Failed to decrypt MIME part: invalid structure")); return NULL; } - - decrypted_part = camel_cipher_decrypt(cipher, encrypted_part, ex); - if (decrypted_part) { - camel_object_ref (decrypted_part); + + decrypted_part = camel_mime_part_new(); + valid = camel_cipher_decrypt(cipher, encrypted_part, decrypted_part, ex); + if (valid) { + camel_object_ref(decrypted_part); mpe->decrypted = decrypted_part; + camel_cipher_validity_free(valid); + } else { + camel_object_ref(decrypted_part); + decrypted_part = NULL; } return decrypted_part; diff --git a/camel/camel-multipart-signed.c b/camel/camel-multipart-signed.c index 2647ba6591..47d749ab73 100644 --- a/camel/camel-multipart-signed.c +++ b/camel/camel-multipart-signed.c @@ -670,6 +670,10 @@ camel_multipart_signed_sign(CamelMultipartSigned *mps, CamelCipherContext *conte CamelCipherValidity * camel_multipart_signed_verify(CamelMultipartSigned *mps, CamelCipherContext *context, CamelException *ex) { + abort(); + + return NULL; +#if 0 CamelCipherValidity *valid; CamelMimePart *sigpart; CamelStream *constream; @@ -720,6 +724,7 @@ camel_multipart_signed_verify(CamelMultipartSigned *mps, CamelCipherContext *con camel_object_unref(constream); return valid; +#endif } diff --git a/camel/camel-smime-context.c b/camel/camel-smime-context.c index 75cbba9e2b..e2af550db8 100644 --- a/camel/camel-smime-context.c +++ b/camel/camel-smime-context.c @@ -68,6 +68,41 @@ struct _CamelSMIMEContextPrivate { static CamelCipherContextClass *parent_class = NULL; +/* used for decode content callback, for streaming decode */ +static void +sm_write_stream(void *arg, const char *buf, unsigned long len) +{ + camel_stream_write((CamelStream *)arg, buf, len); +} + +static PK11SymKey * +sm_decrypt_key(void *arg, SECAlgorithmID *algid) +{ + printf("Decrypt key called\n"); + return (PK11SymKey *)arg; +} + +static char * +sm_get_passwd(PK11SlotInfo *info, PRBool retry, void *arg) +{ + CamelSMIMEContext *context = arg; + char *pass, *nsspass = NULL; + char *prompt; + CamelException *ex; + + ex = camel_exception_new(); + prompt = g_strdup_printf(_("Enter security pass-phrase for `%s'"), PK11_GetTokenName(info)); + pass = camel_session_get_password(((CamelCipherContext *)context)->session, prompt, FALSE, TRUE, NULL, PK11_GetTokenName(info), ex); + camel_exception_free(ex); + g_free(prompt); + if (pass) { + nsspass = PORT_Strdup(pass); + g_free(pass); + } + + return nsspass; +} + /** * camel_smime_context_new: * @session: session @@ -108,10 +143,62 @@ camel_smime_context_set_sign_mode(CamelSMIMEContext *context, camel_smime_sign_t context->priv->sign_mode = type; } +/* TODO: This is suboptimal, but the only other solution is to pass around NSSCMSMessages */ guint32 camel_smime_context_describe_part(CamelSMIMEContext *context, CamelMimePart *part) { - return 0; + guint32 flags = 0; + CamelContentType *ct; + const char *tmp; + + ct = camel_mime_part_get_content_type(part); + + if (camel_content_type_is(ct, "multipart", "signed")) { + tmp = camel_content_type_param(ct, "protocol"); + if (tmp && g_ascii_strcasecmp(tmp, ((CamelCipherContext *)context)->sign_protocol)) + flags = CAMEL_SMIME_SIGNED; + } else if (camel_content_type_is(ct, "application", "x-pkcs7-mime")) { + CamelStreamMem *istream; + NSSCMSMessage *cmsg; + NSSCMSDecoderContext *dec; + + /* FIXME: stream this to the decoder incrementally */ + istream = (CamelStreamMem *)camel_stream_mem_new(); + camel_data_wrapper_decode_to_stream(camel_medium_get_content_object((CamelMedium *)part), (CamelStream *)istream); + camel_stream_reset((CamelStream *)istream); + + dec = NSS_CMSDecoder_Start(NULL, + NULL, NULL, + sm_get_passwd, context, /* password callback */ + NULL, NULL); /* decrypt key callback */ + + NSS_CMSDecoder_Update(dec, istream->buffer->data, istream->buffer->len); + camel_object_unref(istream); + + cmsg = NSS_CMSDecoder_Finish(dec); + if (cmsg) { + if (NSS_CMSMessage_IsSigned(cmsg)) { + printf("message is signed\n"); + flags |= CAMEL_SMIME_SIGNED; + } + + if (NSS_CMSMessage_IsEncrypted(cmsg)) { + printf("message is encrypted\n"); + flags |= CAMEL_SMIME_ENCRYPTED; + } +#if 0 + if (NSS_CMSMessage_ContainsCertsOrCrls(cmsg)) { + printf("message contains certs or crls\n"); + flags |= CAMEL_SMIME_CERTS; + } +#endif + NSS_CMSMessage_Destroy(cmsg); + } else { + printf("Message could not be parsed\n"); + } + } + + return flags; } static const char * @@ -141,41 +228,6 @@ sm_id_to_hash(CamelCipherContext *context, const char *id) return CAMEL_CIPHER_HASH_DEFAULT; } -/* used for decode content callback, for streaming decode */ -static void -sm_write_stream(void *arg, const char *buf, unsigned long len) -{ - camel_stream_write((CamelStream *)arg, buf, len); -} - -static PK11SymKey * -sm_decrypt_key(void *arg, SECAlgorithmID *algid) -{ - printf("Decrypt key called\n"); - return (PK11SymKey *)arg; -} - -static char * -sm_get_passwd(PK11SlotInfo *info, PRBool retry, void *arg) -{ - CamelSMIMEContext *context = arg; - char *pass, *nsspass = NULL; - char *prompt; - CamelException *ex; - - ex = camel_exception_new(); - prompt = g_strdup_printf(_("Enter security pass-phrase for `%s'"), PK11_GetTokenName(info)); - pass = camel_session_get_password(((CamelCipherContext *)context)->session, prompt, FALSE, TRUE, NULL, PK11_GetTokenName(info), ex); - camel_exception_free(ex); - g_free(prompt); - if (pass) { - nsspass = PORT_Strdup(pass); - g_free(pass); - } - - return nsspass; -} - static NSSCMSMessage * sm_signing_cmsmessage(CamelSMIMEContext *context, const char *nick, SECOidTag hash, int detached, CamelException *ex) { @@ -346,7 +398,10 @@ sm_sign(CamelCipherContext *context, const char *userid, CamelCipherHash hash, C /* FIXME: stream this, we stream output at least */ istream = camel_stream_mem_new(); - if (camel_cipher_canonical_to_stream(ipart, istream) == -1) { + if (camel_cipher_canonical_to_stream(ipart, + CAMEL_MIME_FILTER_CANON_STRIP + |CAMEL_MIME_FILTER_CANON_CRLF + |CAMEL_MIME_FILTER_CANON_FROM, istream) == -1) { camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Could not generate signing data: %s"), g_strerror(errno)); goto fail; @@ -466,11 +521,9 @@ sm_status_description(NSSCMSVerificationStatus status) } static CamelCipherValidity * -sm_verify(CamelCipherContext *context, CamelCipherHash hash, CamelStream *istream, CamelMimePart *sigpart, CamelException *ex) +sm_verify_cmsg(CamelCipherContext *context, NSSCMSMessage *cmsg, CamelMimePart *extpart, CamelException *ex) { struct _CamelSMIMEContextPrivate *p = ((CamelSMIMEContext *)context)->priv; - NSSCMSDecoderContext *dec; - NSSCMSMessage *cmsg; NSSCMSSignedData *sigd = NULL; NSSCMSEnvelopedData *envd; NSSCMSEncryptedData *encd; @@ -484,22 +537,6 @@ sm_verify(CamelCipherContext *context, CamelCipherHash hash, CamelStream *istrea CamelCipherValidity *valid; GString *description; - dec = NSS_CMSDecoder_Start(NULL, - NULL, NULL, /* content callback */ - sm_get_passwd, context, /* password callback */ - NULL, NULL); /* decrypt key callback */ - - /* FIXME: Stream? not worth it? sigs are small */ - mem = (CamelStreamMem *)camel_stream_mem_new(); - camel_data_wrapper_decode_to_stream(camel_medium_get_content_object((CamelMedium *)sigpart), (CamelStream *)mem); - (void)NSS_CMSDecoder_Update(dec, mem->buffer->data, mem->buffer->len); - camel_object_unref(mem); - cmsg = NSS_CMSDecoder_Finish(dec); - if (cmsg == NULL) { - camel_exception_setv(ex, 1, "Decoder failed"); - return NULL; - } - description = g_string_new(""); valid = camel_cipher_validity_new(); camel_cipher_validity_set_valid(valid, TRUE); @@ -522,6 +559,11 @@ sm_verify(CamelCipherContext *context, CamelCipherHash hash, CamelStream *istrea /* need to build digests of the content */ if (!NSS_CMSSignedData_HasDigests(sigd)) { + if (extpart == NULL) { + camel_exception_setv(ex, 1, "Digests missing from enveloped data"); + goto fail; + } + if ((poolp = PORT_NewArena(1024)) == NULL) { camel_exception_setv(ex, 1, "out of memory"); goto fail; @@ -536,12 +578,12 @@ sm_verify(CamelCipherContext *context, CamelCipherHash hash, CamelStream *istrea } mem = (CamelStreamMem *)camel_stream_mem_new(); - camel_stream_write_to_stream(istream, (CamelStream *)mem); + camel_cipher_canonical_to_stream(extpart, CAMEL_MIME_FILTER_CANON_CRLF, (CamelStream *)mem); NSS_CMSDigestContext_Update(digcx, mem->buffer->data, mem->buffer->len); camel_object_unref(mem); if (NSS_CMSDigestContext_FinishMultiple(digcx, poolp, &digests) != SECSuccess) { - camel_exception_setv(ex, 1, "Can not calculate digests"); + camel_exception_setv(ex, 1, "Cannot calculate digests"); goto fail; } @@ -572,7 +614,6 @@ sm_verify(CamelCipherContext *context, CamelCipherHash hash, CamelStream *istrea g_string_printf(description, "Certficate only message, certificates imported and verified"); } } else { - if (!NSS_CMSSignedData_HasDigests(sigd)) { camel_exception_setv(ex, 1, "Can't find signature digests"); goto fail; @@ -606,7 +647,6 @@ sm_verify(CamelCipherContext *context, CamelCipherHash hash, CamelStream *istrea break; case SEC_OID_PKCS7_ENVELOPED_DATA: envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo); - /* do we need to look into the enveloped data for signatures too?? */ break; case SEC_OID_PKCS7_ENCRYPTED_DATA: encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent(cinfo); @@ -622,20 +662,74 @@ sm_verify(CamelCipherContext *context, CamelCipherHash hash, CamelStream *istrea camel_cipher_validity_set_description(valid, description->str); g_string_free(description, TRUE); - NSS_CMSMessage_Destroy(cmsg); return valid; fail: - NSS_CMSMessage_Destroy(cmsg); camel_cipher_validity_free(valid); g_string_free(description, TRUE); - if (poolp) - PORT_FreeArena(poolp, PR_FALSE); - return NULL; } +static CamelCipherValidity * +sm_verify(CamelCipherContext *context, CamelMimePart *ipart, CamelException *ex) +{ + NSSCMSDecoderContext *dec; + NSSCMSMessage *cmsg; + CamelStreamMem *mem; + CamelCipherValidity *valid; + CamelContentType *ct; + const char *tmp; + CamelMimePart *extpart, *sigpart; + + ct = camel_mime_part_get_content_type(ipart); + if (camel_content_type_is(ct, "multipart", "signed")) { + CamelMultipart *mps = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)ipart); + + tmp = camel_content_type_param(ct, "protocol"); + extpart = camel_multipart_get_part(mps, CAMEL_MULTIPART_SIGNED_CONTENT); + sigpart = camel_multipart_get_part(mps, CAMEL_MULTIPART_SIGNED_SIGNATURE); + if (!CAMEL_IS_MULTIPART_SIGNED(mps) + || tmp == NULL + || g_ascii_strcasecmp(tmp, context->sign_protocol) != 0 + || extpart == NULL + || sigpart == NULL) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot verify message signature: Incorrect message format")); + return NULL; + } + } else if (camel_content_type_is(ct, "application", "x-pkcs7-mime")) { + extpart = NULL; + sigpart = ipart; + } else { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot verify message signature: Incorrect message format")); + return NULL; + } + + dec = NSS_CMSDecoder_Start(NULL, + NULL, NULL, /* content callback */ + sm_get_passwd, context, /* password callback */ + NULL, NULL); /* decrypt key callback */ + + /* FIXME: we should stream this to the decoder */ + mem = (CamelStreamMem *)camel_stream_mem_new(); + camel_data_wrapper_decode_to_stream(camel_medium_get_content_object((CamelMedium *)sigpart), (CamelStream *)mem); + (void)NSS_CMSDecoder_Update(dec, mem->buffer->data, mem->buffer->len); + camel_object_unref(mem); + cmsg = NSS_CMSDecoder_Finish(dec); + if (cmsg == NULL) { + camel_exception_setv(ex, 1, "Decoder failed"); + return NULL; + } + + valid = sm_verify_cmsg(context, cmsg, extpart, ex); + + NSS_CMSMessage_Destroy(cmsg); + + return valid; +} + static int sm_encrypt(CamelCipherContext *context, const char *userid, GPtrArray *recipients, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex) { @@ -812,14 +906,14 @@ fail: return -1; } -static CamelMimePart * -sm_decrypt(CamelCipherContext *context, CamelMimePart *ipart, CamelException *ex) +static CamelCipherValidity * +sm_decrypt(CamelCipherContext *context, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex) { NSSCMSDecoderContext *dec; NSSCMSMessage *cmsg; CamelStreamMem *istream; CamelStream *ostream; - CamelMimePart *opart = NULL; + CamelCipherValidity *valid = NULL; /* FIXME: This assumes the content is only encrypted. Perhaps its ok for this api to do this ... */ @@ -856,15 +950,22 @@ sm_decrypt(CamelCipherContext *context, CamelMimePart *ipart, CamelException *ex } #endif - NSS_CMSMessage_Destroy(cmsg); - - opart = camel_mime_part_new(); camel_stream_reset(ostream); camel_data_wrapper_construct_from_stream((CamelDataWrapper *)opart, ostream); + + if (NSS_CMSMessage_IsSigned(cmsg)) { + valid = sm_verify_cmsg(context, cmsg, NULL, ex); + } else { + valid = camel_cipher_validity_new(); + valid->encrypt.description = g_strdup(_("Encrypted content")); + valid->encrypt.status = CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED; + } + + NSS_CMSMessage_Destroy(cmsg); fail: camel_object_unref(ostream); - return opart; + return valid; } static int diff --git a/camel/camel-smime-context.h b/camel/camel-smime-context.h index f0e8d45c3b..19f0e8464b 100644 --- a/camel/camel-smime-context.h +++ b/camel/camel-smime-context.h @@ -42,9 +42,10 @@ typedef enum _camel_smime_sign_t { } camel_smime_sign_t; typedef enum _camel_smime_describe_t { - CAMEL_SMIME_SIGNED = 1, - CAMEL_SMIME_ENCRYPTED = 2, - CAMEL_SMIME_CERTS = 4, + CAMEL_SMIME_SIGNED = 1<<0, + CAMEL_SMIME_ENCRYPTED = 1<<1, + CAMEL_SMIME_CERTS = 1<<2, + CAMEL_SMIME_CRLS = 1<<3, } camel_smime_describe_t; typedef struct _CamelSMIMEContext CamelSMIMEContext; |