diff options
Diffstat (limited to 'mail')
-rw-r--r-- | mail/ChangeLog | 28 | ||||
-rw-r--r-- | mail/mail-crypto.c | 674 | ||||
-rw-r--r-- | mail/mail-crypto.h | 33 | ||||
-rw-r--r-- | mail/mail-format.c | 124 |
4 files changed, 766 insertions, 93 deletions
diff --git a/mail/ChangeLog b/mail/ChangeLog index 0108799e21..1e8079c66d 100644 --- a/mail/ChangeLog +++ b/mail/ChangeLog @@ -1,3 +1,31 @@ +2000-12-15 Jeffrey Stedfast <fejj@helixcode.com> + + * mail-format.c (handle_multipart_signed): New callback to handle + multipart/signed parts. + (decode_pgp): Update to account for the cipherlen argument needed + for openpgp_decrypt. + (is_rfc2015): Removed as we now have a better version in + mail-crypto. + (handle_multipart_encrypted): Updated to use the PGP/MIME utility + functions. + + * mail-crypto.c (mail_crypto_openpgp_decrypt): Don't check + (!*plaintext) as it could be a binary stream. Now also takes a + cipherlen argument. + (mail_crypto_openpgp_sign): New function. + (pgp_mime_part_sign): New function to replace a mime part with the + pgp signed equivalent. + (pgp_mime_part_encrypt): New function to replace a mime part with + the pgp encrypted equivalent. + (pgp_mime_part_decrypt): New function to decrypt a pgp encrypted + mime part (like from pgp_mime_part_encrypt) and replace it. + (is_rfc2015_signed): New function to determine if a mime part is + an rfc2015 signed part. + (is_rfc2015_encrypted): New function to determine if a mime part + is an rfc2015 encrypted part. + (mail_crypto_openpgp_verify): New openpgp function to verify a + signature. + 2000-12-14 Christopher James Lahey <clahey@helixcode.com> * mail-threads.c (update_active_views): Unref the iterator when diff --git a/mail/mail-crypto.c b/mail/mail-crypto.c index de866bcda3..a013b72878 100644 --- a/mail/mail-crypto.c +++ b/mail/mail-crypto.c @@ -57,6 +57,8 @@ #include <unistd.h> #include <signal.h> +#include <camel/camel-mime-filter-from.h> + static int cleanup_child (pid_t child) { @@ -338,7 +340,8 @@ crypto_exec_with_passwd (const char *path, char *argv[], const char *input, int * the case that the cleartext is a binary stream. **/ char * -mail_crypto_openpgp_decrypt (const char *ciphertext, int *outlen, CamelException *ex) +mail_crypto_openpgp_decrypt (const char *ciphertext, int cipherlen, + int *outlen, CamelException *ex) { int retval, i; char *path, *argv[12]; @@ -398,14 +401,17 @@ mail_crypto_openpgp_decrypt (const char *ciphertext, int *outlen, CamelException putenv (passwd_fd); #endif argv[i++] = NULL; - + + /* initialize outlen */ + *outlen = 0; + retval = crypto_exec_with_passwd (path, argv, - ciphertext, strlen (ciphertext), + ciphertext, cipherlen, passwd_fds, passphrase, &plaintext, outlen, &diagnostics); - if (retval != 0 || !*plaintext) { + if (retval != 0 || *outlen == 0) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, "%s", diagnostics); g_free (plaintext); @@ -599,15 +605,15 @@ mail_crypto_openpgp_encrypt (const char *in, int inlen, * @plaintext: text to sign * @userid: user id to sign with * @hash: Preferred hash function (md5 or sha1) - * @detach: Specify whether or not to generate a detached clearsign. * @ex: a CamelException * * Clearsigns the plaintext using the user id **/ char * -mail_crypto_openpgp_clearsign (const char *plaintext, const char *userid, - PgpHashType hash, gboolean detach, +mail_crypto_openpgp_clearsign (const char *plaintext, + const char *userid, + PgpHashType hash, CamelException *ex) { int retval; @@ -661,9 +667,6 @@ mail_crypto_openpgp_clearsign (const char *plaintext, const char *userid, argv[i++] = "--clearsign"; - if (detach) - argv[i++] = "-b"; - if (hash_str) { argv[i++] = "--digest-algo"; argv[i++] = hash_str; @@ -692,9 +695,6 @@ mail_crypto_openpgp_clearsign (const char *plaintext, const char *userid, argv[i++] = "pgps"; - if (detach) - argv[i++] = "-b"; - if (userid) { argv[i++] = "-u"; argv[i++] = (char *) userid; @@ -710,7 +710,7 @@ mail_crypto_openpgp_clearsign (const char *plaintext, const char *userid, sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); putenv (passwd_fd); #else - /* FIXME: modify to respect hash and detach */ + /* FIXME: modify to respect hash */ path = PGP_PATH; argv[i++] = "pgp"; @@ -741,7 +741,653 @@ mail_crypto_openpgp_clearsign (const char *plaintext, const char *userid, } g_free (diagnostics); + return ciphertext; } + +/** + * mail_crypto_openpgp_sign: + * @in: input data to sign + * @inlen: length of input data + * @userid: userid to sign with + * @hash: preferred hash type (md5 or sha1) + * @ex: exception + * + * Returns an allocated string containing the detached signature using + * the preferred hash. + **/ +char * +mail_crypto_openpgp_sign (const char *in, int inlen, const char *userid, + PgpHashType hash, CamelException *ex) +{ + char *argv[20]; + char *cyphertext = NULL; + char *diagnostics = NULL; + char *passphrase = NULL; + char *hash_str = NULL; + char *path; + int passwd_fds[2]; + char passwd_fd[32]; + int retval, i; + + +#ifndef PGP_PROGRAM + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("No GPG/PGP program available.")); + return NULL; +#endif + + passphrase = mail_session_request_dialog (_("Please enter your PGP/GPG passphrase."), + TRUE, "pgp", FALSE); + if (!passphrase) { + camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, + _("No password provided.")); + return NULL; + } + + if (pipe (passwd_fds) < 0) { + g_free (passphrase); + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Couldn't create pipe to GPG/PGP: %s"), + g_strerror (errno)); + return NULL; + } + + switch (hash) { + case PGP_HASH_TYPE_MD5: + hash_str = "MD5"; + break; + case PGP_HASH_TYPE_SHA1: + hash_str = "SHA1"; + break; + default: + hash_str = NULL; + } + + i = 0; +#if defined(GPG_PATH) + path = GPG_PATH; + argv[i++] = "gpg"; + + argv[i++] = "--sign"; + argv[i++] = "-b"; + if (hash_str) { + argv[i++] = "--digest-algo"; + argv[i++] = hash_str; + } + + if (userid) { + argv[i++] = "-u"; + argv[i++] = (char *) userid; + } + + argv[i++] = "--verbose"; + argv[i++] = "--yes"; + argv[i++] = "--batch"; + + argv[i++] = "--armor"; + + argv[i++] = "--output"; + argv[i++] = "-"; /* output to stdout */ + + argv[i++] = "--passphrase-fd"; + sprintf (passwd_fd, "%d", passwd_fds[0]); + argv[i++] = passwd_fd; +#elif defined(PGP5_PATH) + path = PGP5_PATH; + + /* FIXME: respect hash */ + argv[i++] = "pgps"; + + if (userid) { + argv[i++] = "-u"; + argv[i++] = (char *) userid; + } + + argv[i++] = "-b"; + argv[i++] = "-f"; + argv[i++] = "-z"; + argv[i++] = "-a"; + argv[i++] = "-o"; + argv[i++] = "-"; /* output to stdout */ + + argv[i++] = "-s"; + sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); + putenv (passwd_fd); +#else + path = PGP_PATH; + + /* FIXME: needs to respect hash and also return only the detached sig */ + argv[i++] = "pgp"; + + if (userid) { + argv[i++] = "-u"; + argv[i++] = (char *) userid; + } + + argv[i++] = "-f"; + argv[i++] = "-a"; + argv[i++] = "-o"; + argv[i++] = "-"; + + argv[i++] = "-s"; + sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); + putenv (passwd_fd); +#endif + + argv[i++] = NULL; + + retval = crypto_exec_with_passwd (path, argv, + in, inlen, + passwd_fds, passphrase, + &cyphertext, NULL, + &diagnostics); + + g_free (passphrase); + + if (retval != 0 || !*cyphertext) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + "%s", diagnostics); + g_free (cyphertext); + cyphertext = NULL; + } + + g_free (diagnostics); + + return cyphertext; +} + +gboolean +mail_crypto_openpgp_verify (const char *in, int inlen, const char *sigin, int siglen, CamelException *ex) +{ + char *argv[20]; + char *cleartext = NULL; + char *diagnostics = NULL; + char *path; + int passwd_fds[2]; + char passwd_fd[32]; + char *tmp = "/tmp/mail-crypto-XXXXXX"; + int retval, i, clearlen; + gboolean valid = TRUE; + + +#ifndef PGP_PROGRAM + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("No GPG/PGP program available.")); + return FALSE; +#endif + + if (pipe (passwd_fds) < 0) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Couldn't create pipe to GPG/PGP: %s"), + g_strerror (errno)); + return FALSE; + } + + i = 0; +#if defined(GPG_PATH) + path = GPG_PATH; + + argv[i++] = "gpg"; + + argv[i++] = "--verify"; + + if (sigin != NULL && siglen) { + /* We are going to verify a detached signature so save + the signature to a temp file and write the data to + verify to stdin */ + int fd; + + fd = mkstemp (tmp); + if (fd == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Couldn't create temp file: %s"), + g_strerror (errno)); + return FALSE; + } + + write (fd, sigin, siglen); + close (fd); + + argv[i++] = tmp; + argv[i++] = "-"; + } else { + /* We are going to verify using stdin */ + argv[i++] = "-"; + } + + argv[i++] = "--verbose"; + argv[i++] = "--yes"; + argv[i++] = "--batch"; + + argv[i++] = "--output"; + argv[i++] = "-"; /* output to stdout */ +#elif defined (PGP5_PATH) + path = PGP5_PATH; + + argv[i++] = "pgpv"; +#else + path = PGP_PATH; + + argv[i++] = "pgp"; +#endif + + argv[i++] = NULL; + + clearlen = 0; + retval = crypto_exec_with_passwd (path, argv, + in, inlen, + passwd_fds, NULL, + &cleartext, &clearlen, + &diagnostics); + + /* FIXME: maybe we should always set an exception? */ + if (retval != 0 || clearlen == 0) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + "%s", diagnostics); + valid = FALSE; + } + + g_free (diagnostics); + g_free (cleartext); + + return valid; +} + +/** rfc2015 stuff *******************************/ + +gboolean +is_rfc2015_signed (CamelMimePart *mime_part) +{ + CamelDataWrapper *wrapper; + CamelMultipart *mp; + CamelMimePart *part; + GMimeContentField *type; + const gchar *param; + int nparts; + + /* check that we have a multipart/signed */ + type = camel_mime_part_get_content_type (mime_part); + if (!gmime_content_field_is_type (type, "multipart", "signed")) + return FALSE; + + /* check that we have a protocol param with the value: "application/pgp-signed" */ + param = gmime_content_field_get_parameter (type, "protocol"); + if (!param || g_strcasecmp (param, "\"application/pgp-signed\"")) + return FALSE; + + /* check that we have exactly 2 subparts */ + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + mp = CAMEL_MULTIPART (wrapper); + nparts = camel_multipart_get_number (mp); + if (nparts != 2) + return FALSE; + + /* The first part may be of any type except for + * application/pgp-signature - check it. */ + part = camel_multipart_get_part (mp, 0); + type = camel_mime_part_get_content_type (part); + if (gmime_content_field_is_type (type, "application","pgp-signature")) + return FALSE; + + /* The second part should be application/pgp-signature. */ + part = camel_multipart_get_part (mp, 1); + type = camel_mime_part_get_content_type (part); + if (!gmime_content_field_is_type (type, "application","pgp-siganture")) + return FALSE; + + /* FIXME: Implement multisig stuff */ + + return TRUE; +} + +gboolean +is_rfc2015_encrypted (CamelMimePart *mime_part) +{ + CamelDataWrapper *wrapper; + CamelMultipart *mp; + CamelMimePart *part; + GMimeContentField *type; + const gchar *param; + int nparts; + + /* check that we have a multipart/encrypted */ + type = camel_mime_part_get_content_type (mime_part); + if (!gmime_content_field_is_type (type, "multipart", "encrypted")) + return FALSE; + + /* check that we have a protocol param with the value: "application/pgp-encrypted" */ + param = gmime_content_field_get_parameter (type, "protocol"); + if (!param || g_strcasecmp (param, "\"application/pgp-encrypted\"")) + return FALSE; + + /* check that we have at least 2 subparts */ + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + mp = CAMEL_MULTIPART (wrapper); + nparts = camel_multipart_get_number (mp); + if (nparts < 2) + return FALSE; + + /* The first part should be application/pgp-encrypted */ + part = camel_multipart_get_part (mp, 0); + type = camel_mime_part_get_content_type (part); + if (!gmime_content_field_is_type (type, "application","pgp-encrypted")) + return FALSE; + + /* The second part should be application/octet-stream - this + is the one we care most about */ + part = camel_multipart_get_part (mp, 1); + type = camel_mime_part_get_content_type (part); + if (!gmime_content_field_is_type (type, "application","octet-stream")) + return FALSE; + + return TRUE; +} + +/** + * pgp_mime_part_sign: + * @mime_part: a MIME part that will be replaced by a pgp signed part + * @userid: userid to sign with + * @hash: one of PGP_HASH_TYPE_MD5 or PGP_HASH_TYPE_SHA1 + * @ex: exception which will be set if there are any errors. + * + * Constructs a PGP/MIME multipart in compliance with rfc2015 and + * replaces #part with the generated multipart/signed. On failure, + * #ex will be set and #part will remain untouched. + **/ +void +pgp_mime_part_sign (CamelMimePart **mime_part, const gchar *userid, PgpHashType hash, CamelException *ex) +{ + CamelMimePart *part, *signed_part; + CamelMultipart *multipart; + CamelMimePartEncodingType encoding; + GMimeContentField *mime_type; + CamelStreamFilter *filtered_stream; + CamelMimeFilter *crlf_filter, *from_filter; + CamelStream *stream; + GByteArray *array; + gchar *cleartext, *signature; + gchar *hash_type; + gint clearlen; + + g_return_if_fail (*mime_part != NULL); + g_return_if_fail (userid != NULL); + g_return_if_fail (hash != PGP_HASH_TYPE_NONE); + + part = *mime_part; + encoding = camel_mime_part_get_encoding (part); + + /* the encoding should really be QP or Base64 */ + if (encoding != CAMEL_MIME_PART_ENCODING_BASE64) + camel_mime_part_set_encoding (part, CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE); + + /* get the cleartext */ + array = g_byte_array_new (); + stream = camel_stream_mem_new_with_byte_array (array); + crlf_filter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, + CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); + from_filter = CAMEL_MIME_FILTER (camel_mime_filter_from_new ()); + filtered_stream = camel_stream_filter_new_with_stream (stream); + camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER (crlf_filter)); + camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER (from_filter)); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (part), CAMEL_STREAM (filtered_stream)); + camel_object_unref (CAMEL_OBJECT (filtered_stream)); + camel_object_unref (CAMEL_OBJECT (stream)); + cleartext = array->data; + clearlen = array->len; + + /* get the signature */ + signature = mail_crypto_openpgp_sign (cleartext, clearlen, userid, hash, ex); + g_byte_array_free (array, TRUE); + if (camel_exception_is_set (ex)) { + /* restore the original encoding */ + camel_mime_part_set_encoding (part, encoding); + return; + } + + /* construct the pgp-signature mime part */ + fprintf (stderr, "signature:\n%s\n", signature); + signed_part = camel_mime_part_new (); + camel_mime_part_set_content (signed_part, signature, strlen (signature), + "application/pgp-signature"); + g_free (signature); + camel_mime_part_set_encoding (signed_part, CAMEL_MIME_PART_ENCODING_DEFAULT); + + /* construct the container multipart/signed */ + multipart = camel_multipart_new (); + camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart), + "multipart/signed"); + switch (hash) { + case PGP_HASH_TYPE_MD5: + hash_type = "pgp-md5"; + break; + case PGP_HASH_TYPE_SHA1: + hash_type = "pgp-sha1"; + break; + default: + hash_type = NULL; + } + mime_type = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (multipart)); + gmime_content_field_set_parameter (mime_type, "micalg", hash_type); + gmime_content_field_set_parameter (mime_type, "protocol", "application/pgp-signature"); + camel_multipart_set_boundary (multipart, NULL); + + /* add the parts to the multipart */ + camel_multipart_add_part (multipart, part); + camel_object_unref (CAMEL_OBJECT (part)); + camel_multipart_add_part (multipart, signed_part); + camel_object_unref (CAMEL_OBJECT (signed_part)); + + /* replace the input part with the output part */ + *mime_part = camel_mime_part_new (); + camel_medium_set_content_object (CAMEL_MEDIUM (*mime_part), + CAMEL_DATA_WRAPPER (multipart)); + camel_object_unref (CAMEL_OBJECT (multipart)); +} + + +/** + * pgp_mime_part_verify: + * @mime_part: a multipart/signed MIME Part + * @ex: exception + * + * Returns TRUE if the signature is valid otherwise returns + * FALSE. Note: you may want to check the exception if it fails as + * there may be useful information to give to the user; example: + * verification may have failed merely because the user doesn't have + * the sender's key on her system. + **/ +gboolean +pgp_mime_part_verify (CamelMimePart *mime_part, CamelException *ex) +{ + CamelDataWrapper *wrapper; + CamelMultipart *multipart; + CamelMimePart *part, *sigpart; + GByteArray *content, *sig; + CamelStreamFilter *filtered_stream; + CamelMimeFilter *crlf_filter; + CamelStream *stream; + gboolean valid = FALSE; + + g_return_val_if_fail (mime_part != NULL, FALSE); + + if (!is_rfc2015_signed (mime_part)) + return FALSE; + + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + multipart = CAMEL_MULTIPART (wrapper); + + /* get the plain part */ + part = camel_multipart_get_part (multipart, 0); + content = g_byte_array_new (); + stream = camel_stream_mem_new_with_byte_array (content); + crlf_filter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, + CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); + filtered_stream = camel_stream_filter_new_with_stream (stream); + camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER (crlf_filter)); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (part), CAMEL_STREAM (filtered_stream)); + camel_object_unref (CAMEL_OBJECT (filtered_stream)); + camel_object_unref (CAMEL_OBJECT (stream)); + + /* get the signed part */ + sigpart = camel_multipart_get_part (multipart, 1); + sig = g_byte_array_new (); + stream = camel_stream_mem_new_with_byte_array (sig); + camel_data_wrapper_write_to_stream (camel_medium_get_content_object (CAMEL_MEDIUM (sigpart)), stream); + camel_object_unref (CAMEL_OBJECT (stream)); + + /* verify */ + valid = mail_crypto_openpgp_verify (content->data, content->len, + sig->data, sig->len, ex); + + g_byte_array_free (content, TRUE); + g_byte_array_free (sig, TRUE); + + return valid; +} + + +/** + * pgp_mime_part_encrypt: + * @mime_part: a MIME part that will be replaced by a pgp encrypted part + * @recipients: list of recipient PGP Key IDs + * @ex: exception which will be set if there are any errors. + * + * Constructs a PGP/MIME multipart in compliance with rfc2015 and + * replaces #mime_part with the generated multipart/signed. On failure, + * #ex will be set and #part will remain untouched. + **/ +void +pgp_mime_part_encrypt (CamelMimePart **mime_part, const GPtrArray *recipients, CamelException *ex) +{ + CamelMultipart *multipart; + CamelMimePart *part, *version_part, *encrypted_part; + GMimeContentField *mime_type; + CamelStream *stream; + GByteArray *contents; + gchar *ciphertext; + + g_return_if_fail (*mime_part != NULL); + g_return_if_fail (recipients != NULL); + + part = *mime_part; + + /* get the contents */ + contents = g_byte_array_new (); + stream = camel_stream_mem_new_with_byte_array (contents); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (part), stream); + camel_object_unref (CAMEL_OBJECT (stream)); + + /* pgp encrypt */ + ciphertext = mail_crypto_openpgp_encrypt (contents->data, + contents->len, + recipients, FALSE, NULL, ex); + if (camel_exception_is_set (ex)) + return; + + /* construct the version part */ + version_part = camel_mime_part_new (); + camel_mime_part_set_encoding (version_part, CAMEL_MIME_PART_ENCODING_7BIT); + camel_mime_part_set_content (version_part, "Version: 1", strlen ("Version: 1"), + "application/pgp-encrypted"); + + /* construct the pgp-encrypted mime part */ + encrypted_part = camel_mime_part_new (); + camel_mime_part_set_encoding (encrypted_part, CAMEL_MIME_PART_ENCODING_7BIT); + camel_mime_part_set_content (encrypted_part, ciphertext, strlen (ciphertext), + "application/octet-stream"); + g_free (ciphertext); + + /* construct the container multipart/signed */ + multipart = camel_multipart_new (); + camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart), + "multipart/encrypted"); + camel_multipart_set_boundary (multipart, NULL); + mime_type = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (multipart)); + gmime_content_field_set_parameter (mime_type, "protocol", "application/pgp-encrypted"); + + /* add the parts to the multipart */ + camel_multipart_add_part (multipart, version_part); + camel_object_unref (CAMEL_OBJECT (version_part)); + camel_multipart_add_part (multipart, encrypted_part); + camel_object_unref (CAMEL_OBJECT (encrypted_part)); + + /* replace the input part with the output part */ + *mime_part = camel_mime_part_new (); + camel_medium_set_content_object (CAMEL_MEDIUM (*mime_part), + CAMEL_DATA_WRAPPER (multipart)); + camel_object_unref (CAMEL_OBJECT (multipart)); + + /* destroy the original part */ + camel_object_unref (CAMEL_OBJECT (part)); +} + + +/** + * pgp_mime_part_decrypt: + * @mime_part: a multipart/encrypted MIME Part + * @ex: exception + * + * Returns the decrypted MIME Part on success or NULL on fail. + **/ +CamelMimePart * +pgp_mime_part_decrypt (CamelMimePart *mime_part, CamelException *ex) +{ + CamelDataWrapper *wrapper; + CamelMultipart *multipart; + CamelMimeParser *parser; + CamelMimePart *encrypted_part, *part; + GMimeContentField *mime_type; + CamelStream *stream; + GByteArray *content; + gchar *cleartext, *ciphertext = NULL; + int cipherlen, clearlen; + + g_return_val_if_fail (mime_part != NULL, NULL); + + /* make sure the mime part is a multipart/encrypted */ + if (!is_rfc2015_encrypted (mime_part)) + return NULL; + + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + multipart = CAMEL_MULTIPART (wrapper); + + /* get the encrypted part (second part) */ + encrypted_part = camel_multipart_get_part (multipart, 1 /* second part starting at 0 */); + mime_type = camel_mime_part_get_content_type (encrypted_part); + if (!gmime_content_field_is_type (mime_type, "application", "octet-stream")) + return NULL; + + /* get the ciphertext */ + content = g_byte_array_new (); + stream = camel_stream_mem_new_with_byte_array (content); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (encrypted_part), stream); + camel_object_unref (CAMEL_OBJECT (stream)); + ciphertext = content->data; + cipherlen = content->len; + + /* get the cleartext */ + cleartext = mail_crypto_openpgp_decrypt (ciphertext, cipherlen, &clearlen, ex); + g_byte_array_free (content, TRUE); + if (camel_exception_is_set (ex)) + return NULL; + + /* create a stream based on the returned cleartext */ + stream = camel_stream_mem_new (); + camel_stream_write (stream, cleartext, clearlen); + camel_stream_reset (stream); + g_free (cleartext); + + /* construct the new decrypted mime part from the stream */ + part = camel_mime_part_new (); + parser = camel_mime_parser_new (); + camel_mime_parser_init_with_stream (parser, stream); + camel_mime_part_construct_from_parser (part, parser); + camel_object_unref (CAMEL_OBJECT (parser)); + camel_object_unref (CAMEL_OBJECT (stream)); + + return part; +} + #endif /* PGP_PROGRAM */ diff --git a/mail/mail-crypto.h b/mail/mail-crypto.h index 44d8a00400..250f46c022 100644 --- a/mail/mail-crypto.h +++ b/mail/mail-crypto.h @@ -38,11 +38,11 @@ typedef enum { } PgpHashType; char *mail_crypto_openpgp_decrypt (const char *ciphertext, + int cipherlen, int *outlen, CamelException *ex); -char *mail_crypto_openpgp_encrypt (const char *plaintext, - int inlen, +char *mail_crypto_openpgp_encrypt (const char *in, int inlen, const GPtrArray *recipients, gboolean sign, const char *userid, @@ -51,9 +51,36 @@ char *mail_crypto_openpgp_encrypt (const char *plaintext, char *mail_crypto_openpgp_clearsign (const char *plaintext, const char *userid, PgpHashType hash, - gboolean detached, CamelException *ex); +char *mail_crypto_openpgp_sign (const char *in, int inlen, + const char *userid, + PgpHashType hash, + CamelException *ex); + +gboolean mail_crypto_openpgp_verify (const char *in, int inlen, + const char *sigin, int siglen, + CamelException *ex); + +gboolean is_rfc2015_signed (CamelMimePart *part); + +gboolean is_rfc2015_encrypted (CamelMimePart *part); + +void pgp_mime_part_sign (CamelMimePart **mime_part, + const gchar *userid, + PgpHashType hash, + CamelException *ex); + +gboolean pgp_mime_part_verify (CamelMimePart *mime_part, + CamelException *ex); + +void pgp_mime_part_encrypt (CamelMimePart **mime_part, + const GPtrArray *recipients, + CamelException *ex); + +CamelMimePart *pgp_mime_part_decrypt (CamelMimePart *mime_part, + CamelException *ex); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/mail/mail-format.c b/mail/mail-format.c index b7eb7dbea0..b63b9a8d3d 100644 --- a/mail/mail-format.c +++ b/mail/mail-format.c @@ -75,6 +75,9 @@ static gboolean handle_multipart_appledouble (CamelMimePart *part, static gboolean handle_multipart_encrypted (CamelMimePart *part, const char *mime_type, MailDisplay *md); +static gboolean handle_multipart_signed (CamelMimePart *part, + const char *mime_type, + MailDisplay *md); static gboolean handle_message_rfc822 (CamelMimePart *part, const char *mime_type, MailDisplay *md); @@ -305,6 +308,8 @@ setup_mime_tables (void) handle_multipart_appledouble); g_hash_table_insert (mime_function_table, "multipart/encrypted", handle_multipart_encrypted); + g_hash_table_insert (mime_function_table, "multipart/signed", + handle_multipart_signed); /* RFC 2046 says unrecognized text subtypes can be treated * as text/plain (as long as you recognize the character set), @@ -835,8 +840,9 @@ decode_pgp (const char *ciphertext, int *outlen, MailDisplay *md) camel_exception_init (&ex); #ifdef PGP_PROGRAM /* FIXME: multipart parts */ + /* another FIXME: this doesn't have to return plaintext you realize... */ if (g_datalist_get_data (md->data, "show_pgp")) { - plaintext = mail_crypto_openpgp_decrypt (ciphertext, outlen, &ex); + plaintext = mail_crypto_openpgp_decrypt (ciphertext, strlen (ciphertext), outlen, &ex); if (plaintext) return plaintext; } @@ -880,11 +886,12 @@ try_inline_pgp (char *start, MailDisplay *md) if (!end) return start; - end += sizeof ("-----END PGP MESSAGE-----") - 1; + end += strlen ("-----END PGP MESSAGE-----") - 1; mail_html_write (md->html, md->stream, "<hr>"); - /* FIXME: uhm, pgp decrypted data doesn't have to be plaintext */ + /* FIXME: uhm, pgp decrypted data doesn't have to be plaintext + * however, I suppose that since it was 'inline', it probably is */ ciphertext = g_strndup (start, end - start); plaintext = decode_pgp (ciphertext, &outlen, md); g_free (ciphertext); @@ -1189,90 +1196,55 @@ handle_multipart_mixed (CamelMimePart *part, const char *mime_type, } static gboolean -is_rfc2015 (CamelMimePart *part) +handle_multipart_encrypted (CamelMimePart *part, const char *mime_type, + MailDisplay *md) { - int nparts; - char *text; CamelDataWrapper *wrapper; - CamelMultipart *mp; - GMimeContentField *type; - - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part)); - mp = CAMEL_MULTIPART (wrapper); - nparts = camel_multipart_get_number (mp); - if (nparts != 2) - return FALSE; - - /* Check for application/pgp-encrypted in the first part. */ - part = camel_multipart_get_part (mp, 0); - type = camel_mime_part_get_content_type (part); - if (!gmime_content_field_is_type (type, "application", "pgp-encrypted")) - return FALSE; - - /* Check version. */ + CamelMimePart *mime_part; + CamelException ex; + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part)); - text = get_data_wrapper_text (wrapper); - if (!text || !strstr(text, "Version: 1")) { - g_free(text); - return FALSE; + g_return_val_if_fail (CAMEL_IS_MULTIPART (wrapper), FALSE); + + /* Currently we only handle RFC2015-style PGP encryption. */ + if (!is_rfc2015_encrypted (part)) + return handle_multipart_mixed (part, mime_type, md); + + camel_exception_init (&ex); + mime_part = pgp_mime_part_decrypt (part, &ex); + if (camel_exception_is_set (&ex)) { + /* I guess we just treat this as a multipart/mixed */ + return handle_multipart_mixed (part, mime_type, md); + } else { + gboolean retcode; + + retcode = call_handler_function (mime_part, md); + camel_object_unref (CAMEL_OBJECT (mime_part)); + + return retcode; } - g_free(text); - - /* Check for application/octet-stream in the second part. */ - part = camel_multipart_get_part(mp, 1); - type = camel_mime_part_get_content_type (part); - if (!gmime_content_field_is_type (type, "application", "octet-stream")) - return FALSE; - - return TRUE; } +/* FIXME: So this function is mostly just a place-holder for now */ static gboolean -handle_multipart_encrypted (CamelMimePart *part, const char *mime_type, - MailDisplay *md) +handle_multipart_signed (CamelMimePart *part, const char *mime_type, + MailDisplay *md) { - CamelDataWrapper *wrapper = - camel_medium_get_content_object (CAMEL_MEDIUM (part)); - CamelMultipart *mp; - char *ciphertext, *plaintext; - int outlen; - + CamelDataWrapper *wrapper; + CamelException ex; + gboolean valid; + + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part)); g_return_val_if_fail (CAMEL_IS_MULTIPART (wrapper), FALSE); - mp = CAMEL_MULTIPART (wrapper); - - /* Currently we only handle RFC2015-style PGP encryption. */ - if (!is_rfc2015 (part)) + + /* Currently we only handle RFC2015-style PGP signatures. */ + if (!is_rfc2015_signed (part)) return handle_multipart_mixed (part, mime_type, md); - - part = camel_multipart_get_part (mp, 1); - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part)); - ciphertext = get_data_wrapper_text (wrapper); - if (!ciphertext) - return FALSE; - /* FIXME: please note that decrypted data does NOT have to be plaintext */ - plaintext = decode_pgp (ciphertext, &outlen, md); - if (plaintext) { - CamelStream *memstream; - - memstream = camel_stream_mem_new_with_buffer (plaintext, - strlen (plaintext)); - part = camel_mime_part_new (); - camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (part), - memstream); - camel_object_unref (CAMEL_OBJECT (memstream)); - - mail_html_write (md->html, md->stream, - "<table width=\"100%%\" border=2 " - "cellpadding=4><tr><td>"); - call_handler_function (part, md); - mail_html_write (md->html, md->stream, "</td></tr></table>"); - camel_object_hook_event (CAMEL_OBJECT (md->current_message), - "finalize", destroy_part, part); - g_free (plaintext); - } - - return TRUE; + camel_exception_init (&ex); + valid = pgp_mime_part_verify (part, &ex); + + return valid; } /* As seen in RFC 2387! */ |