From 9e549686b23b6dffdcbd09c9b10dc2cb795fbcdf Mon Sep 17 00:00:00 2001 From: zautrix Date: Fri, 18 Mar 2005 20:17:03 +0000 Subject: Initial revision --- (limited to 'libetpan/src/engine/mailprivacy_gnupg.c') diff --git a/libetpan/src/engine/mailprivacy_gnupg.c b/libetpan/src/engine/mailprivacy_gnupg.c new file mode 100644 index 0000000..01dcc80 --- a/dev/null +++ b/libetpan/src/engine/mailprivacy_gnupg.c @@ -0,0 +1,2614 @@ +/* + * libEtPan! -- a mail library + * + * Copyright (C) 2001, 2005 - DINH Viet Hoa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the libEtPan! project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +#include "mailprivacy_gnupg.h" + +#include "mailprivacy.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "mailprivacy_tools.h" +#include +#include + +static int pgp_is_encrypted(struct mailmime * mime) +{ + if (mime->mm_content_type != NULL) { + clistiter * cur; + + if (strcasecmp(mime->mm_content_type->ct_subtype, "encrypted") != 0) + return 0; + + for(cur = clist_begin(mime->mm_content_type->ct_parameters) ; cur != NULL ; + cur = clist_next(cur)) { + struct mailmime_parameter * param; + + param = clist_content(cur); + + if ((strcasecmp(param->pa_name, "protocol") == 0) && + (strcasecmp(param->pa_value, "application/pgp-encrypted") == 0)) + return 1; + } + } + + return 0; +} + +static int pgp_is_signed(struct mailmime * mime) +{ + if (mime->mm_content_type != NULL) { + clistiter * cur; + + if (strcasecmp(mime->mm_content_type->ct_subtype, "signed") != 0) + return 0; + + for(cur = clist_begin(mime->mm_content_type->ct_parameters) ; + cur != NULL ; cur = clist_next(cur)) { + struct mailmime_parameter * param; + + param = clist_content(cur); + + if ((strcasecmp(param->pa_name, "protocol") == 0) && + (strcasecmp(param->pa_value, "application/pgp-signature") == 0)) + return 1; + } + } + + return 0; +} + +#define PGP_SIGNED "-----BEGIN PGP SIGNED MESSAGE-----" + +int pgp_is_clearsigned(char * data, size_t len) +{ + if (len >= strlen(PGP_SIGNED)) + if (strncmp(data, PGP_SIGNED, sizeof(PGP_SIGNED) - 1) == 0) + return 1; + + return 0; +} + +#define PGP_CRYPTED "-----BEGIN PGP MESSAGE-----" + +int pgp_is_crypted_armor(char * data, size_t len) +{ + if (len >= strlen(PGP_CRYPTED)) + if (strncmp(data, PGP_CRYPTED, sizeof(PGP_CRYPTED) - 1) == 0) + return 1; + + return 0; +} + + +#define BUF_SIZE 1024 + +enum { + NO_ERROR_PGP = 0, + ERROR_PGP_CHECK, + ERROR_PGP_COMMAND, + ERROR_PGP_FILE, +}; + +/* write output to a file */ + +static int get_pgp_output(FILE * dest_f, char * command) +{ + FILE * p; + char buf[BUF_SIZE]; + size_t size; + int res; + int status; + char command_redirected[PATH_MAX]; + + snprintf(command_redirected, sizeof(command_redirected), "%s 2>&1", command); + + /* + flush buffer so that it is not flushed more than once when forking + */ + fflush(dest_f); + + p = popen(command_redirected, "r"); + if (p == NULL) { + res = ERROR_PGP_COMMAND; + goto err; + } + + while ((size = fread(buf, 1, sizeof(buf), p)) != 0) { + size_t written; + + written = fwrite(buf, 1, size, dest_f); + if (written != size) { + res = ERROR_PGP_FILE; + goto close; + } + } + status = pclose(p); + + if (WEXITSTATUS(status) != 0) + return ERROR_PGP_CHECK; + else + return NO_ERROR_PGP; + + close: + pclose(p); + err: + return res; +} + +#define PGP_DECRYPT_DESCRIPTION "PGP encrypted part\r\n" +#define PGP_DECRYPT_FAILED "PGP decryption FAILED\r\n" +#define PGP_DECRYPT_SUCCESS "PGP decryption success\r\n" + +/* extracted from mailprivacy_smime.c -- begin */ + +static char * get_first_from_addr(struct mailmime * mime) +{ + clistiter * cur; + struct mailimf_single_fields single_fields; + struct mailimf_fields * fields; + struct mailimf_mailbox * mb; + + if (mime->mm_type != MAILMIME_MESSAGE) + return NULL; + + fields = mime->mm_data.mm_message.mm_fields; + if (fields == NULL) + return NULL; + + mailimf_single_fields_init(&single_fields, fields); + + if (single_fields.fld_from == NULL) + return NULL; + + cur = clist_begin(single_fields.fld_from->frm_mb_list->mb_list); + if (cur == NULL) + return NULL; + + mb = clist_content(cur); + + return mb->mb_addr_spec; +} + +/* extracted from mailprivacy_smime.c -- end */ + +static int pgp_decrypt(struct mailprivacy * privacy, + mailmessage * msg, + struct mailmime * mime, struct mailmime ** result) +{ + char default_key[PATH_MAX]; + struct mailmime * version_mime; + struct mailmime * encrypted_mime; + clistiter * cur; + char encrypted_filename[PATH_MAX]; + char description_filename[PATH_MAX]; + FILE * description_f; + char decrypted_filename[PATH_MAX]; + FILE * decrypted_f; + char command[PATH_MAX]; + struct mailmime * description_mime; + struct mailmime * decrypted_mime; + int r; + int res; + int decrypt_ok; + char quoted_decrypted_filename[PATH_MAX]; + char quoted_encrypted_filename[PATH_MAX]; + struct mailmime * multipart; + char * email; + + /* get the two parts of the PGP message */ + + cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list); + if (cur == NULL) { + res = MAIL_ERROR_INVAL; + goto err; + } + + version_mime = clist_content(cur); + cur = clist_next(cur); + if (cur == NULL) { + res = MAIL_ERROR_INVAL; + goto err; + } + + encrypted_mime = clist_content(cur); + + /* fetch the second section, that's the useful one */ + + r = mailprivacy_fetch_decoded_to_file(privacy, + encrypted_filename, sizeof(encrypted_filename), + msg, encrypted_mime); + if (r != MAIL_NO_ERROR) { + res = r; + goto err; + } + + /* we are in a safe directory */ + + decrypted_f = mailprivacy_get_tmp_file(privacy, + decrypted_filename, + sizeof(decrypted_filename)); + if (decrypted_f == NULL) { + res = MAIL_ERROR_FILE; + goto unlink_encrypted; + } + fclose(decrypted_f); + + /* description */ + + description_f = mailprivacy_get_tmp_file(privacy, + description_filename, + sizeof(description_filename)); + if (description_f == NULL) { + res = MAIL_ERROR_FILE; + goto unlink_decrypted; + } + + fprintf(description_f, PGP_DECRYPT_DESCRIPTION); + + /* get encryption key */ + + * default_key = '\0'; + email = get_first_from_addr(mime); + if (email != NULL) + snprintf(default_key, sizeof(default_key), + "--default-key %s", email); + + /* run the command */ + + r = mail_quote_filename(quoted_encrypted_filename, + sizeof(quoted_encrypted_filename), encrypted_filename); + if (r < 0) { + fclose(description_f); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + r = mail_quote_filename(quoted_decrypted_filename, + sizeof(quoted_decrypted_filename), decrypted_filename); + if (r < 0) { + fclose(description_f); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + decrypt_ok = 0; + snprintf(command, sizeof(command), + "gpg -q --batch --yes --out %s %s --decrypt %s", + quoted_decrypted_filename, default_key, quoted_encrypted_filename); + + r = get_pgp_output(description_f, command); + switch (r) { + case NO_ERROR_PGP: + decrypt_ok = 1; + break; + case ERROR_PGP_CHECK: + decrypt_ok = 0; + break; + case ERROR_PGP_COMMAND: + fclose(description_f); + res = MAIL_ERROR_COMMAND; + goto unlink_description; + case ERROR_PGP_FILE: + fclose(description_f); + res = MAIL_ERROR_FILE; + goto unlink_description; + } + if (decrypt_ok) + fprintf(description_f, PGP_DECRYPT_SUCCESS); + else + fprintf(description_f, PGP_DECRYPT_FAILED); + fclose(description_f); + + /* building multipart */ + + r = mailmime_new_with_content("multipart/x-decrypted", NULL, &multipart); + if (r != MAILIMF_NO_ERROR) { + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + /* building the description part */ + + description_mime = mailprivacy_new_file_part(privacy, + description_filename, + "text/plain", MAILMIME_MECHANISM_8BIT); + if (description_mime == NULL) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + /* adds the description part */ + + r = mailmime_smart_add_part(multipart, description_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(description_mime); + mailmime_free(description_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + /* building the decrypted part */ + + r = mailprivacy_get_part_from_file(privacy, 1, + decrypted_filename, &decrypted_mime); + if (r == MAIL_NO_ERROR) { + /* adds the decrypted part */ + + r = mailmime_smart_add_part(multipart, decrypted_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(decrypted_mime); + mailmime_free(decrypted_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + } + + unlink(description_filename); + unlink(decrypted_filename); + unlink(encrypted_filename); + + * result = multipart; + + return MAIL_NO_ERROR; + + unlink_description: + unlink(description_filename); + unlink_decrypted: + unlink(decrypted_filename); + unlink_encrypted: + unlink(encrypted_filename); + err: + return res; +} + +#define PGP_VERIFY_DESCRIPTION "PGP verify signed message\r\n" +#define PGP_VERIFY_FAILED "PGP verification FAILED\r\n" +#define PGP_VERIFY_SUCCESS "PGP verification success\r\n" + +static int +pgp_verify(struct mailprivacy * privacy, + mailmessage * msg, + struct mailmime * mime, struct mailmime ** result) +{ + struct mailmime * signed_mime; + struct mailmime * signature_mime; + char signed_filename[PATH_MAX]; + char signature_filename[PATH_MAX]; + int res; + int r; + clistiter * cur; + char command[PATH_MAX]; + int sign_ok; + struct mailmime * description_mime; + FILE * description_f; + char description_filename[PATH_MAX]; + char quoted_signed_filename[PATH_MAX]; + char quoted_signature_filename[PATH_MAX]; + struct mailmime * multipart; + struct mailmime * signed_msg_mime; + + /* get the two parts of the PGP message */ + + cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list); + if (cur == NULL) { + res = MAIL_ERROR_INVAL; + goto err; + } + + signed_mime = clist_content(cur); + cur = clist_next(cur); + if (cur == NULL) { + res = MAIL_ERROR_INVAL; + goto err; + } + + signature_mime = clist_content(cur); + + /* fetch signed part and write it to a file */ + + r = mailprivacy_fetch_mime_body_to_file(privacy, + signed_filename, sizeof(signed_filename), + msg, signed_mime); + if (r != MAIL_NO_ERROR) { + res = r; + goto err; + } + + /* fetch signed part and write it to a file */ + + r = mailprivacy_fetch_decoded_to_file(privacy, + signature_filename, sizeof(signature_filename), + msg, signature_mime); + if (r != MAIL_NO_ERROR) { + res = r; + goto unlink_signed; + } + + /* description */ + + description_f = mailprivacy_get_tmp_file(privacy, + description_filename, + sizeof(description_filename)); + if (description_f == NULL) { + res = MAIL_ERROR_FILE; + goto unlink_signature; + } + + fprintf(description_f, PGP_VERIFY_DESCRIPTION); + + /* run the command */ + + r = mail_quote_filename(quoted_signature_filename, + sizeof(quoted_signature_filename), signature_filename); + if (r < 0) { + fclose(description_f); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + r = mail_quote_filename(quoted_signed_filename, + sizeof(quoted_signed_filename), signed_filename); + if (r < 0) { + fclose(description_f); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + sign_ok = 0; + snprintf(command, sizeof(command), "gpg -q --batch --yes --verify %s %s", + quoted_signature_filename, quoted_signed_filename); + + r = get_pgp_output(description_f, command); + switch (r) { + case NO_ERROR_PGP: + sign_ok = 1; + break; + case ERROR_PGP_CHECK: + sign_ok = 0; + break; + case ERROR_PGP_COMMAND: + fclose(description_f); + res = MAIL_ERROR_COMMAND; + goto unlink_description; + case ERROR_PGP_FILE: + fclose(description_f); + res = MAIL_ERROR_FILE; + goto unlink_description; + } + + if (sign_ok) + fprintf(description_f, PGP_VERIFY_SUCCESS); + else + fprintf(description_f, PGP_VERIFY_FAILED); + fclose(description_f); + + /* building multipart */ + + r = mailmime_new_with_content("multipart/x-verified", NULL, &multipart); + if (r != MAILIMF_NO_ERROR) { + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + /* building the description part */ + + description_mime = mailprivacy_new_file_part(privacy, + description_filename, + "text/plain", MAILMIME_MECHANISM_8BIT); + if (description_mime == NULL) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + /* adds the description part */ + + r = mailmime_smart_add_part(multipart, description_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(description_mime); + mailmime_free(description_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + r = mailprivacy_get_part_from_file(privacy, 1, + signed_filename, &signed_msg_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + r = mailmime_smart_add_part(multipart, signed_msg_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(signed_msg_mime); + mailmime_free(signed_msg_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + unlink(description_filename); + unlink(signature_filename); + unlink(signed_filename); + + * result = multipart; + + return MAIL_NO_ERROR; + + unlink_description: + unlink(description_filename); + unlink_signature: + unlink(signature_filename); + unlink_signed: + unlink(signed_filename); + err: + return res; +} + + +#define PGP_CLEAR_VERIFY_DESCRIPTION "PGP verify clear signed message\r\n" +#define PGP_CLEAR_VERIFY_FAILED "PGP verification FAILED\r\n" +#define PGP_CLEAR_VERIFY_SUCCESS "PGP verification success\r\n" + +static int pgp_verify_clearsigned(struct mailprivacy * privacy, + mailmessage * msg, + struct mailmime * mime, + char * content, size_t content_len, struct mailmime ** result) +{ + int r; + char command[PATH_MAX]; + int res; + int sign_ok; + size_t written; + char signed_filename[PATH_MAX]; + FILE * signed_f; + char stripped_filename[PATH_MAX]; + FILE * stripped_f; + char description_filename[PATH_MAX]; + FILE * description_f; + char quoted_signed_filename[PATH_MAX]; + char quoted_stripped_filename[PATH_MAX]; + struct mailmime * stripped_mime; + struct mailmime * description_mime; + struct mailmime * multipart; + struct mailmime_content * content_type; + + if (mime->mm_parent == NULL) { + res = MAIL_ERROR_INVAL; + goto err; + } + + if (mime->mm_parent->mm_type == MAILMIME_SINGLE) { + res = MAIL_ERROR_INVAL; + goto err; + } + + signed_f = mailprivacy_get_tmp_file(privacy, + signed_filename, sizeof(signed_filename)); + if (signed_f == NULL) { + res = MAIL_ERROR_FILE; + goto err; + } + + written = fwrite(content, 1, content_len, signed_f); + if (written != content_len) { + fclose(signed_f); + unlink(signed_filename); + res = MAIL_ERROR_FILE; + goto err; + } + fclose(signed_f); + + /* XXX - prepare file for PGP, remove trailing WS */ + + stripped_f = mailprivacy_get_tmp_file(privacy, + stripped_filename, sizeof(stripped_filename)); + if (stripped_f == NULL) { + res = MAIL_ERROR_FILE; + goto unlink_signed; + } + fclose(stripped_f); + + /* description */ + + description_f = mailprivacy_get_tmp_file(privacy, + description_filename, + sizeof(description_filename)); + if (description_f == NULL) { + res = MAIL_ERROR_FILE; + goto unlink_stripped; + } + + fprintf(description_f, PGP_CLEAR_VERIFY_DESCRIPTION); + + r = mail_quote_filename(quoted_stripped_filename, + sizeof(quoted_stripped_filename), stripped_filename); + if (r < 0) { + fclose(description_f); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + r = mail_quote_filename(quoted_signed_filename, + sizeof(quoted_signed_filename), signed_filename); + if (r < 0) { + fclose(description_f); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + snprintf(command, sizeof(command), + "gpg -q --batch --yes --out %s --decrypt %s", + quoted_stripped_filename, quoted_signed_filename); + + sign_ok = 0; + r = get_pgp_output(description_f, command); + switch (r) { + case NO_ERROR_PGP: + sign_ok = 1; + break; + case ERROR_PGP_CHECK: + sign_ok = 0; + break; + case ERROR_PGP_COMMAND: + res = MAIL_ERROR_COMMAND; + fclose(description_f); + goto unlink_description; + case ERROR_PGP_FILE: + res = MAIL_ERROR_FILE; + fclose(description_f); + goto unlink_description; + } + if (sign_ok) + fprintf(description_f, PGP_CLEAR_VERIFY_SUCCESS); + else + fprintf(description_f, PGP_CLEAR_VERIFY_FAILED); + fclose(description_f); + + /* building multipart */ + + r = mailmime_new_with_content("multipart/x-verified", NULL, &multipart); + if (r != MAILIMF_NO_ERROR) { + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + /* building the description part */ + + description_mime = mailprivacy_new_file_part(privacy, + description_filename, + "text/plain", MAILMIME_MECHANISM_8BIT); + if (description_mime == NULL) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + /* adds the description part */ + + r = mailmime_smart_add_part(multipart, description_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(description_mime); + mailmime_free(description_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + /* building the signature stripped part */ + + stripped_mime = mailprivacy_new_file_part(privacy, + stripped_filename, + "application/octet-stream", + MAILMIME_MECHANISM_8BIT); + if (stripped_mime == NULL) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + /* place original content type */ + + content_type = mailmime_content_dup(mime->mm_content_type); + if (content_type == NULL) { + mailprivacy_mime_clear(stripped_mime); + mailmime_free(stripped_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + mailmime_content_free(stripped_mime->mm_content_type); + stripped_mime->mm_content_type = content_type; + + /* place original MIME fields */ + + if (mime->mm_mime_fields != NULL) { + struct mailmime_fields * mime_fields; + clistiter * cur; + + mime_fields = mailprivacy_mime_fields_dup(privacy, mime->mm_mime_fields); + if (mime_fields == NULL) { + mailprivacy_mime_clear(stripped_mime); + mailmime_free(stripped_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + for(cur = clist_begin(mime_fields->fld_list) ; + cur != NULL ; cur = clist_next(cur)) { + struct mailmime_field * field; + + field = clist_content(cur); + if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) { + mailmime_field_free(field); + clist_delete(mime_fields->fld_list, cur); + break; + } + } + clist_concat(stripped_mime->mm_mime_fields->fld_list, + mime_fields->fld_list); + mailmime_fields_free(mime_fields); + } + + /* adds the stripped part */ + + r = mailmime_smart_add_part(multipart, stripped_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(stripped_mime); + mailmime_free(stripped_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + unlink(description_filename); + unlink(stripped_filename); + unlink(signed_filename); + + * result = multipart; + + return MAIL_NO_ERROR; + + unlink_description: + unlink(description_filename); + unlink_stripped: + unlink(stripped_filename); + unlink_signed: + unlink(signed_filename); + err: + return res; +} + + +#define PGP_DECRYPT_ARMOR_DESCRIPTION "PGP ASCII armor encrypted part\r\n" +#define PGP_DECRYPT_ARMOR_FAILED "PGP ASCII armor decryption FAILED\r\n" +#define PGP_DECRYPT_ARMOR_SUCCESS "PGP ASCII armor decryption success\r\n" + +static int pgp_decrypt_armor(struct mailprivacy * privacy, + mailmessage * msg, + struct mailmime * mime, + char * content, size_t content_len, struct mailmime ** result) +{ + char default_key[PATH_MAX]; + FILE * encrypted_f; + char encrypted_filename[PATH_MAX]; + char description_filename[PATH_MAX]; + FILE * description_f; + char decrypted_filename[PATH_MAX]; + FILE * decrypted_f; + size_t written; + char command[PATH_MAX]; + struct mailmime * description_mime; + struct mailmime * decrypted_mime; + struct mailmime * multipart; + int r; + int res; + int sign_ok; + char quoted_decrypted_filename[PATH_MAX]; + char quoted_encrypted_filename[PATH_MAX]; + char * email; + + if (mime->mm_parent == NULL) { + res = MAIL_ERROR_INVAL; + goto err; + } + + if (mime->mm_parent->mm_type == MAILMIME_SINGLE) { + res = MAIL_ERROR_INVAL; + goto err; + } + + encrypted_f = mailprivacy_get_tmp_file(privacy, + encrypted_filename, + sizeof(encrypted_filename)); + if (encrypted_f == NULL) { + res = MAIL_ERROR_FILE; + goto err; + } + + written = fwrite(content, 1, content_len, encrypted_f); + if (written != content_len) { + fclose(encrypted_f); + unlink(encrypted_filename); + res = MAIL_ERROR_FILE; + goto err; + } + + fclose(encrypted_f); + + /* we are in a safe directory */ + + decrypted_f = mailprivacy_get_tmp_file(privacy, + decrypted_filename, + sizeof(decrypted_filename)); + if (decrypted_f == NULL) { + res = MAIL_ERROR_FILE; + goto unlink_encrypted; + } + fclose(decrypted_f); + + /* description */ + + description_f = mailprivacy_get_tmp_file(privacy, + description_filename, + sizeof(description_filename)); + if (description_f == NULL) { + res = MAIL_ERROR_FILE; + goto unlink_decrypted; + } + + fprintf(description_f, PGP_DECRYPT_ARMOR_DESCRIPTION); + + /* get encryption key */ + + * default_key = '\0'; + email = get_first_from_addr(mime); + if (email != NULL) + snprintf(default_key, sizeof(default_key), + "--default-key %s", email); + + /* run the command */ + + r = mail_quote_filename(quoted_encrypted_filename, + sizeof(quoted_encrypted_filename), encrypted_filename); + if (r < 0) { + fclose(description_f); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + r = mail_quote_filename(quoted_decrypted_filename, + sizeof(quoted_decrypted_filename), decrypted_filename); + if (r < 0) { + fclose(description_f); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + sign_ok = 0; + snprintf(command, sizeof(command), + "gpg -q --batch --yes --out %s %s --decrypt %s", + quoted_decrypted_filename, default_key, quoted_encrypted_filename); + + r = get_pgp_output(description_f, command); + switch (r) { + case NO_ERROR_PGP: + sign_ok = 1; + break; + case ERROR_PGP_CHECK: + sign_ok = 0; + break; + case ERROR_PGP_COMMAND: + fclose(description_f); + res = MAIL_ERROR_COMMAND; + goto unlink_description; + case ERROR_PGP_FILE: + fclose(description_f); + res = MAIL_ERROR_FILE; + goto unlink_description; + } + if (sign_ok) + fprintf(description_f, PGP_DECRYPT_ARMOR_SUCCESS); + else + fprintf(description_f, PGP_DECRYPT_ARMOR_FAILED); + fclose(description_f); + + /* building multipart */ + + r = mailmime_new_with_content("multipart/x-decrypted", NULL, &multipart); + if (r != MAILIMF_NO_ERROR) { + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + /* building the description part */ + + description_mime = mailprivacy_new_file_part(privacy, + description_filename, + "text/plain", MAILMIME_MECHANISM_8BIT); + if (description_mime == NULL) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + /* adds the description part */ + + r = mailmime_smart_add_part(multipart, description_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(description_mime); + mailmime_free(description_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + /* building the decrypted part */ + + r = mailprivacy_get_part_from_file(privacy, 1, + decrypted_filename, &decrypted_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = r; + goto unlink_description; + } + + /* adds the decrypted part */ + + r = mailmime_smart_add_part(multipart, decrypted_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(decrypted_mime); + mailmime_free(decrypted_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_description; + } + + unlink(description_filename); + unlink(decrypted_filename); + unlink(encrypted_filename); + + * result = multipart; + + return MAIL_NO_ERROR; + + unlink_description: + unlink(description_filename); + unlink_decrypted: + unlink(decrypted_filename); + unlink_encrypted: + unlink(encrypted_filename); + err: + return res; +} + + +static int mime_is_text(struct mailmime * build_info) +{ + if (build_info->mm_type == MAILMIME_SINGLE) { + if (build_info->mm_content_type != NULL) { + if (build_info->mm_content_type->ct_type->tp_type == + MAILMIME_TYPE_DISCRETE_TYPE) { + if (build_info->mm_content_type->ct_type->tp_data.tp_discrete_type->dt_type == + MAILMIME_DISCRETE_TYPE_TEXT) + return 1; + } + } + else + return 1; + } + + return 0; +} + + +static int pgp_test_encrypted(struct mailprivacy * privacy, + mailmessage * msg, struct mailmime * mime) +{ + int r; + int res; + + switch (mime->mm_type) { + case MAILMIME_MULTIPLE: + return (pgp_is_encrypted(mime) || pgp_is_signed(mime)); + + case MAILMIME_SINGLE: + /* clear sign or ASCII armor encryption */ + if (mime_is_text(mime)) { + char * content; + size_t content_len; + char * parsed_content; + size_t parsed_content_len; + size_t cur_token; + int encoding; + struct mailmime_single_fields single_fields; + + r = mailprivacy_msg_fetch_section(privacy, msg, mime, + &content, &content_len); + if (r != MAIL_NO_ERROR) + return 0; + + mailmime_single_fields_init(&single_fields, mime->mm_mime_fields, + mime->mm_content_type); + if (single_fields.fld_encoding != NULL) + encoding = single_fields.fld_encoding->enc_type; + else + encoding = MAILMIME_MECHANISM_8BIT; + + cur_token = 0; + r = mailmime_part_parse(content, content_len, &cur_token, + encoding, &parsed_content, &parsed_content_len); + mailprivacy_msg_fetch_result_free(privacy, msg, content); + + if (r != MAILIMF_NO_ERROR) + return 0; + + res = 0; + + if (pgp_is_clearsigned(parsed_content, parsed_content_len)) + res = 1; + else if (pgp_is_crypted_armor(parsed_content, parsed_content_len)) + res = 1; + + mmap_string_unref(parsed_content); + + return res; + } + break; + } + + return 0; +} + +static int pgp_handler(struct mailprivacy * privacy, + mailmessage * msg, + struct mailmime * mime, struct mailmime ** result) +{ + int r; + struct mailmime * alternative_mime; + + alternative_mime = NULL; + switch (mime->mm_type) { + case MAILMIME_MULTIPLE: + r = MAIL_ERROR_INVAL; + if (pgp_is_encrypted(mime)) { + r = pgp_decrypt(privacy, msg, mime, &alternative_mime); + } + else if (pgp_is_signed(mime)) { + r = pgp_verify(privacy, msg, mime, &alternative_mime); + } + + if (r != MAIL_NO_ERROR) + return r; + + * result = alternative_mime; + + return MAIL_NO_ERROR; + + case MAILMIME_SINGLE: + /* clear sign or ASCII armor encryption */ + if (mime_is_text(mime)) { + char * content; + size_t content_len; + char * parsed_content; + size_t parsed_content_len; + size_t cur_token; + int encoding; + struct mailmime_single_fields single_fields; + + r = mailprivacy_msg_fetch_section(privacy, msg, mime, + &content, &content_len); + if (r != MAIL_NO_ERROR) + return MAIL_ERROR_FETCH; + + mailmime_single_fields_init(&single_fields, mime->mm_mime_fields, + mime->mm_content_type); + if (single_fields.fld_encoding != NULL) + encoding = single_fields.fld_encoding->enc_type; + else + encoding = MAILMIME_MECHANISM_8BIT; + + cur_token = 0; + r = mailmime_part_parse(content, content_len, &cur_token, + encoding, &parsed_content, &parsed_content_len); + mailprivacy_msg_fetch_result_free(privacy, msg, content); + + if (r != MAILIMF_NO_ERROR) + return MAIL_ERROR_PARSE; + + r = MAIL_ERROR_INVAL; + if (pgp_is_clearsigned(parsed_content, + parsed_content_len)) { + r = pgp_verify_clearsigned(privacy, + msg, mime, parsed_content, parsed_content_len, &alternative_mime); + } + else if (pgp_is_crypted_armor(parsed_content, + parsed_content_len)) { + r = pgp_decrypt_armor(privacy, + msg, mime, parsed_content, parsed_content_len, &alternative_mime); + } + + mmap_string_unref(parsed_content); + + if (r != MAIL_NO_ERROR) + return r; + + * result = alternative_mime; + + return MAIL_NO_ERROR; + } + break; + } + + return MAIL_ERROR_INVAL; +} + + +#if 0 +static void prepare_mime_single(struct mailmime * mime) +{ + struct mailmime_single_fields single_fields; + int encoding; + int r; + + if (mime->mime_fields != NULL) { + mailmime_single_fields_init(&single_fields, mime->mime_fields, + mime->content_type); + if (single_fields.encoding != NULL) { + encoding = single_fields.encoding->type; + switch (encoding) { + case MAILMIME_MECHANISM_8BIT: + case MAILMIME_MECHANISM_7BIT: + case MAILMIME_MECHANISM_BINARY: + single_fields.encoding->type = MAILMIME_MECHANISM_QUOTED_PRINTABLE; + break; + } + } + else { + struct mailmime_mechanism * mechanism; + struct mailmime_field * field; + + mechanism = + mailmime_mechanism_new(MAILMIME_MECHANISM_QUOTED_PRINTABLE, NULL); + if (mechanism == NULL) + return; + + field = mailmime_field_new(MAILMIME_FIELD_TRANSFER_ENCODING, + NULL, mechanism, NULL, NULL, 0, NULL, NULL); + if (field == NULL) { + mailmime_mechanism_free(mechanism); + return; + } + + r = clist_append(mime->mime_fields->list, field); + if (r < 0) { + mailmime_field_free(field); + return; + } + } + } + + switch (mime->body->encoding) { + case MAILMIME_MECHANISM_8BIT: + case MAILMIME_MECHANISM_7BIT: + case MAILMIME_MECHANISM_BINARY: + mime->body->encoding = MAILMIME_MECHANISM_QUOTED_PRINTABLE; + mime->body->encoded = 0; + break; + } +} + +/* + prepare_mime() + + we assume we built ourself the message. +*/ + +static void prepare_mime(struct mailmime * mime) +{ + clistiter * cur; + + switch (mime->type) { + case MAILMIME_SINGLE: + if (mime->body != NULL) { + prepare_mime_single(mime); + } + break; + + case MAILMIME_MULTIPLE: + for(cur = clist_begin(mime->list) ; cur != NULL ; cur = clist_next(cur)) { + struct mailmime * child; + + child = cur->data; + + prepare_mime(child); + } + break; + + case MAILMIME_MESSAGE: + if (mime->msg_mime) { + prepare_mime(mime->msg_mime); + } + break; + } +} +#endif + +static int pgp_sign_mime(struct mailprivacy * privacy, + struct mailmime * mime, struct mailmime ** result) +{ + char signed_filename[PATH_MAX]; + FILE * signed_f; + int res; + int r; + int col; + char signature_filename[PATH_MAX]; + FILE * signature_f; + char command[PATH_MAX]; + char quoted_signature_filename[PATH_MAX]; + char quoted_signed_filename[PATH_MAX]; + char default_key[PATH_MAX]; + struct mailmime * signature_mime; + struct mailmime * multipart; + struct mailmime_content * content; + struct mailmime_parameter * param; + struct mailmime * signed_msg_mime; + char * dup_signature_filename; + char * email; + + /* get signing key */ + + * default_key = '\0'; + email = get_first_from_addr(mime); + if (email != NULL) + snprintf(default_key, sizeof(default_key), + "--default-key %s", email); + + /* part to sign */ + + /* encode quoted printable all text parts */ + + mailprivacy_prepare_mime(mime); + + signed_f = mailprivacy_get_tmp_file(privacy, + signed_filename, sizeof(signed_filename)); + if (signed_f == NULL) { + res = MAIL_ERROR_FILE; + goto err; + } + + col = 0; + r = mailmime_write(signed_f, &col, mime); + if (r != MAILIMF_NO_ERROR) { + fclose(signed_f); + res = MAIL_ERROR_FILE; + goto unlink_signed; + } + + fclose(signed_f); + + /* prepare destination file for signature */ + + signature_f = mailprivacy_get_tmp_file(privacy, + signature_filename, + sizeof(signature_filename)); + if (signature_f == NULL) { + res = MAIL_ERROR_FILE; + goto unlink_signed; + } + fclose(signature_f); + + r = mail_quote_filename(quoted_signed_filename, + sizeof(quoted_signed_filename), signed_filename); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto unlink_signature; + } + + r = mail_quote_filename(quoted_signature_filename, + sizeof(quoted_signature_filename), signature_filename); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto unlink_signature; + } + + snprintf(command, sizeof(command), + "gpg -q -a --batch --yes --digest-algo sha1 --out %s %s -b %s 2>/dev/null", + quoted_signature_filename, default_key, quoted_signed_filename); + + r = system(command); + if (WEXITSTATUS(r) != 0) { + res = MAIL_ERROR_COMMAND; + goto unlink_signature; + } + + /* multipart */ + + multipart = mailprivacy_new_file_part(privacy, NULL, + "multipart/signed", -1); + + content = multipart->mm_content_type; + + param = mailmime_param_new_with_data("micalg", "pgp-sha1"); + if (param == NULL) { + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_signature; + } + + r = clist_append(content->ct_parameters, param); + if (r < 0) { + mailmime_parameter_free(param); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_signature; + } + + param = mailmime_param_new_with_data("protocol", + "application/pgp-signature"); + if (param == NULL) { + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_signature; + } + + r = clist_append(content->ct_parameters, param); + if (r < 0) { + mailmime_parameter_free(param); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_signature; + } + + /* signed part */ + + r = mailprivacy_get_part_from_file(privacy, 1, + signed_filename, &signed_msg_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = r; + goto unlink_signature; + } + + mailprivacy_prepare_mime(signed_msg_mime); + + r = mailmime_smart_add_part(multipart, signed_msg_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(signed_msg_mime); + mailmime_free(signed_msg_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_signature; + } + + /* signature part */ + + /* reencode the signature file with CRLF */ + dup_signature_filename = mailprivacy_dup_imf_file(privacy, + signature_filename); + if (dup_signature_filename == NULL) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_FILE; + goto unlink_signature; + } + + /* replace the signature file */ + unlink(signature_filename); + strncpy(signature_filename, + dup_signature_filename, sizeof(signature_filename)); + signature_filename[sizeof(signature_filename) - 1] = '\0'; + + signature_mime = mailprivacy_new_file_part(privacy, + signature_filename, + "application/pgp-signature", + MAILMIME_MECHANISM_8BIT); + if (signature_mime == NULL) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_signature; + } + + r = mailmime_smart_add_part(multipart, signature_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(signature_mime); + mailmime_free(signature_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_signature; + } + + unlink(signature_filename); + unlink(signed_filename); + + * result = multipart; + + return MAIL_NO_ERROR; + + unlink_signature: + unlink(signature_filename); + unlink_signed: + unlink(signed_filename); + err: + return res; +} + + +/* ********************************************************************* */ +/* find GPG recipient */ + +static int recipient_add_mb(char * recipient, size_t * len, + struct mailimf_mailbox * mb) +{ + char buffer[PATH_MAX]; + size_t buflen; + + if (mb->mb_addr_spec != NULL) { + snprintf(buffer, sizeof(buffer), "-r %s ", mb->mb_addr_spec); + buflen = strlen(buffer); + if (buflen >= * len) + return MAIL_ERROR_MEMORY; + + strncat(recipient, buffer, * len); + (* len) -= buflen; + } + + return MAIL_NO_ERROR; +} + +static int recipient_add_mb_list(char * recipient, size_t * len, + struct mailimf_mailbox_list * mb_list) +{ + clistiter * cur; + int r; + + for(cur = clist_begin(mb_list->mb_list) ; cur != NULL ; + cur = clist_next(cur)) { + struct mailimf_mailbox * mb; + + mb = clist_content(cur); + + r = recipient_add_mb(recipient, len, mb); + if (r != MAIL_NO_ERROR) + return r; + } + + return MAIL_NO_ERROR; +} + +static int recipient_add_group(char * recipient, size_t * len, + struct mailimf_group * group) +{ + return recipient_add_mb_list(recipient, len, group->grp_mb_list); +} + +static int recipient_add_addr(char * recipient, size_t * len, + struct mailimf_address * addr) +{ + int r; + + switch (addr->ad_type) { + case MAILIMF_ADDRESS_MAILBOX: + r = recipient_add_mb(recipient, len, addr->ad_data.ad_mailbox); + break; + case MAILIMF_ADDRESS_GROUP: + r = recipient_add_group(recipient, len, addr->ad_data.ad_group); + break; + default: + r = MAIL_ERROR_INVAL; + } + + return r; +} + +static int recipient_add_addr_list(char * recipient, size_t * len, + struct mailimf_address_list * addr_list) +{ + clistiter * cur; + int r; + + for(cur = clist_begin(addr_list->ad_list) ; cur != NULL ; + cur = clist_next(cur)) { + struct mailimf_address * addr; + + addr = clist_content(cur); + + r = recipient_add_addr(recipient, len, addr); + if (r != MAIL_NO_ERROR) + return r; + } + + return MAIL_NO_ERROR; +} + + +static int collect_recipient(char * recipient, size_t size, + struct mailimf_fields * fields) +{ + struct mailimf_single_fields single_fields; + int r; + size_t remaining; + int res; + + * recipient = '\0'; + remaining = size; + + if (fields != NULL) + mailimf_single_fields_init(&single_fields, fields); + + if (single_fields.fld_to != NULL) { + r = recipient_add_addr_list(recipient, &remaining, + single_fields.fld_to->to_addr_list); + if (r != MAIL_NO_ERROR) { + res = r; + goto err; + } + } + + if (single_fields.fld_cc != NULL) { + r = recipient_add_addr_list(recipient, &remaining, + single_fields.fld_cc->cc_addr_list); + if (r != MAIL_NO_ERROR) { + res = r; + goto err; + } + } + + if (single_fields.fld_bcc != NULL) { + if (single_fields.fld_bcc->bcc_addr_list != NULL) { + r = recipient_add_addr_list(recipient, &remaining, + single_fields.fld_bcc->bcc_addr_list); + if (r != MAIL_NO_ERROR) { + res = r; + goto err; + } + } + } + + return MAIL_NO_ERROR; + + err: + return res; +} + + +#define PGP_VERSION "Version: 1\r\n" + +static int pgp_sign_encrypt_mime(struct mailprivacy * privacy, + struct mailmime * mime, struct mailmime ** result) +{ + char original_filename[PATH_MAX]; + FILE * original_f; + int res; + int r; + int col; + char encrypted_filename[PATH_MAX]; + FILE * encrypted_f; + char version_filename[PATH_MAX]; + FILE * version_f; + char command[PATH_MAX]; + char quoted_original_filename[PATH_MAX]; + char quoted_encrypted_filename[PATH_MAX]; + char default_key[PATH_MAX]; + struct mailmime * version_mime; + struct mailmime * multipart; + struct mailmime_content * content; + struct mailmime_parameter * param; + struct mailmime * encrypted_mime; + char recipient[PATH_MAX]; + struct mailimf_fields * fields; + struct mailmime * root; + size_t written; + char * email; + + root = mime; + while (root->mm_parent != NULL) + root = root->mm_parent; + + fields = NULL; + if (root->mm_type == MAILMIME_MESSAGE) + fields = root->mm_data.mm_message.mm_fields; + + /* recipient */ + + collect_recipient(recipient, sizeof(recipient), fields); + + /* get signing key */ + + * default_key = '\0'; + email = get_first_from_addr(mime); + if (email != NULL) + snprintf(default_key, sizeof(default_key), + "--default-key %s", email); + + /* part to encrypt */ + + /* encode quoted printable all text parts */ + + mailprivacy_prepare_mime(mime); + + original_f = mailprivacy_get_tmp_file(privacy, original_filename, + sizeof(original_filename)); + if (original_f == NULL) { + res = MAIL_ERROR_FILE; + goto err; + } + + col = 0; + r = mailmime_write(original_f, &col, mime); + if (r != MAILIMF_NO_ERROR) { + fclose(original_f); + res = MAIL_ERROR_FILE; + goto unlink_original; + } + + fclose(original_f); + + /* prepare destination file for encryption */ + + encrypted_f = mailprivacy_get_tmp_file(privacy, + encrypted_filename, + sizeof(encrypted_filename)); + if (encrypted_f == NULL) { + res = MAIL_ERROR_FILE; + goto unlink_original; + } + fclose(encrypted_f); + + r = mail_quote_filename(quoted_original_filename, + sizeof(quoted_original_filename), original_filename); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + r = mail_quote_filename(quoted_encrypted_filename, + sizeof(quoted_encrypted_filename), encrypted_filename); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + snprintf(command, sizeof(command), + "gpg -q %s -a --batch --yes --digest-algo sha1 --out %s -s %s -e %s 2>/dev/null", + recipient, quoted_encrypted_filename, default_key, + quoted_original_filename); + + r = system(command); + if (WEXITSTATUS(r) != 0) { + res = MAIL_ERROR_COMMAND; + goto unlink_encrypted; + } + + /* multipart */ + + multipart = mailprivacy_new_file_part(privacy, NULL, + "multipart/encrypted", -1); + + content = multipart->mm_content_type; + + param = mailmime_param_new_with_data("protocol", + "application/pgp-encrypted"); + if (param == NULL) { + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + r = clist_append(content->ct_parameters, param); + if (r < 0) { + mailmime_parameter_free(param); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + /* version part */ + + version_f = mailprivacy_get_tmp_file(privacy, + version_filename, + sizeof(version_filename)); + if (version_f == NULL) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_FILE; + goto unlink_encrypted; + } + written = fwrite(PGP_VERSION, 1, sizeof(PGP_VERSION) - 1, version_f); + if (written != sizeof(PGP_VERSION) - 1) { + fclose(version_f); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_FILE; + goto unlink_encrypted; + } + fclose(version_f); + + version_mime = mailprivacy_new_file_part(privacy, + version_filename, + "application/pgp-encrypted", + MAILMIME_MECHANISM_8BIT); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = r; + goto unlink_version; + } + + r = mailmime_smart_add_part(multipart, version_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(version_mime); + mailmime_free(version_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_version; + } + + /* encrypted part */ + + encrypted_mime = mailprivacy_new_file_part(privacy, + encrypted_filename, + "application/octet-stream", + MAILMIME_MECHANISM_8BIT); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = r; + goto unlink_version; + } + + r = mailmime_smart_add_part(multipart, encrypted_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(encrypted_mime); + mailmime_free(encrypted_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_version; + } + + unlink(version_filename); + unlink(encrypted_filename); + unlink(original_filename); + + * result = multipart; + + return MAIL_NO_ERROR; + + unlink_version: + unlink(version_filename); + unlink_encrypted: + unlink(encrypted_filename); + unlink_original: + unlink(original_filename); + err: + return res; +} + + +static int pgp_encrypt_mime(struct mailprivacy * privacy, + struct mailmime * mime, struct mailmime ** result) +{ + char original_filename[PATH_MAX]; + FILE * original_f; + int res; + int r; + int col; + char encrypted_filename[PATH_MAX]; + FILE * encrypted_f; + char version_filename[PATH_MAX]; + FILE * version_f; + char command[PATH_MAX]; + char quoted_original_filename[PATH_MAX]; + char quoted_encrypted_filename[PATH_MAX]; + struct mailmime * version_mime; + struct mailmime * multipart; + struct mailmime_content * content; + struct mailmime_parameter * param; + struct mailmime * encrypted_mime; + char recipient[PATH_MAX]; + struct mailimf_fields * fields; + struct mailmime * root; + size_t written; + + root = mime; + while (root->mm_parent != NULL) + root = root->mm_parent; + + fields = NULL; + if (root->mm_type == MAILMIME_MESSAGE) + fields = root->mm_data.mm_message.mm_fields; + + /* recipient */ + + collect_recipient(recipient, sizeof(recipient), fields); + + /* part to encrypt */ + + /* encode quoted printable all text parts */ + + mailprivacy_prepare_mime(mime); + + original_f = mailprivacy_get_tmp_file(privacy, + original_filename, sizeof(original_filename)); + if (original_f == NULL) { + res = MAIL_ERROR_FILE; + goto err; + } + + col = 0; + r = mailmime_write(original_f, &col, mime); + if (r != MAILIMF_NO_ERROR) { + fclose(original_f); + res = MAIL_ERROR_FILE; + goto unlink_original; + } + + fclose(original_f); + + /* prepare destination file for encryption */ + + encrypted_f = mailprivacy_get_tmp_file(privacy, + encrypted_filename, + sizeof(encrypted_filename)); + if (encrypted_f == NULL) { + res = MAIL_ERROR_FILE; + goto unlink_original; + } + fclose(encrypted_f); + + r = mail_quote_filename(quoted_original_filename, + sizeof(quoted_original_filename), original_filename); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + r = mail_quote_filename(quoted_encrypted_filename, + sizeof(quoted_encrypted_filename), encrypted_filename); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + snprintf(command, sizeof(command), + "gpg -q %s -a --batch --yes --out %s -e %s 2>/dev/null", + recipient, quoted_encrypted_filename, quoted_original_filename); + + r = system(command); + if (WEXITSTATUS(r) != 0) { + res = MAIL_ERROR_COMMAND; + goto unlink_encrypted; + } + + /* multipart */ + + multipart = mailprivacy_new_file_part(privacy, NULL, + "multipart/encrypted", -1); + + content = multipart->mm_content_type; + + param = mailmime_param_new_with_data("protocol", + "application/pgp-encrypted"); + if (param == NULL) { + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + r = clist_append(content->ct_parameters, param); + if (r < 0) { + mailmime_parameter_free(param); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + /* version part */ + + version_f = mailprivacy_get_tmp_file(privacy, + version_filename, sizeof(version_filename)); + if (version_f == NULL) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_FILE; + goto unlink_encrypted; + } + written = fwrite(PGP_VERSION, 1, sizeof(PGP_VERSION) - 1, version_f); + if (written != sizeof(PGP_VERSION) - 1) { + fclose(version_f); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_FILE; + goto unlink_encrypted; + } + fclose(version_f); + + version_mime = mailprivacy_new_file_part(privacy, + version_filename, + "application/pgp-encrypted", + MAILMIME_MECHANISM_8BIT); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = r; + goto unlink_version; + } + + r = mailmime_smart_add_part(multipart, version_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(version_mime); + mailmime_free(version_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_version; + } + + /* encrypted part */ + + encrypted_mime = mailprivacy_new_file_part(privacy, + encrypted_filename, + "application/octet-stream", + MAILMIME_MECHANISM_8BIT); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = r; + goto unlink_version; + } + + r = mailmime_smart_add_part(multipart, encrypted_mime); + if (r != MAIL_NO_ERROR) { + mailprivacy_mime_clear(encrypted_mime); + mailmime_free(encrypted_mime); + mailprivacy_mime_clear(multipart); + mailmime_free(multipart); + res = MAIL_ERROR_MEMORY; + goto unlink_version; + } + + unlink(version_filename); + unlink(encrypted_filename); + unlink(original_filename); + + * result = multipart; + + return MAIL_NO_ERROR; + + unlink_version: + unlink(version_filename); + unlink_encrypted: + unlink(encrypted_filename); + unlink_original: + unlink(original_filename); + err: + return res; +} + +static int pgp_clear_sign(struct mailprivacy * privacy, + struct mailmime * mime, struct mailmime ** result) +{ + char default_key[PATH_MAX]; + char original_filename[PATH_MAX]; + FILE * original_f; + char signed_filename[PATH_MAX]; + FILE * signed_f; + char quoted_original_filename[PATH_MAX]; + char quoted_signed_filename[PATH_MAX]; + int col; + struct mailmime * signed_mime; + int res; + int r; + char command[PATH_MAX]; + struct mailmime_content * content_type; + char * email; + + if (mime->mm_type != MAILMIME_SINGLE) { + res = MAIL_ERROR_INVAL; + goto err; + } + + if (mime->mm_data.mm_single == NULL) { + res = MAIL_ERROR_INVAL; + goto err; + } + + /* get signing key */ + + * default_key = '\0'; + email = get_first_from_addr(mime); + if (email != NULL) + snprintf(default_key, sizeof(default_key), + "--default-key %s", email); + + /* get part to sign */ + + original_f = mailprivacy_get_tmp_file(privacy, + original_filename, + sizeof(original_filename)); + if (original_f == NULL) { + res = MAIL_ERROR_FILE; + goto err; + } + + col = 0; + r = mailmime_data_write(original_f, &col, mime->mm_data.mm_single, 1); + if (r != MAILIMF_NO_ERROR) { + fclose(original_f); + res = MAIL_ERROR_FILE; + goto unlink_original; + } + fclose(original_f); + + signed_f = mailprivacy_get_tmp_file(privacy, + signed_filename, + sizeof(signed_filename)); + if (signed_f == NULL) { + res = MAIL_ERROR_FILE; + goto unlink_original; + } + fclose(signed_f); + + r = mail_quote_filename(quoted_original_filename, + sizeof(quoted_original_filename), original_filename); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto unlink_signed; + } + + r = mail_quote_filename(quoted_signed_filename, + sizeof(quoted_signed_filename), signed_filename); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto unlink_signed; + } + + snprintf(command, sizeof(command), + "gpg -q --batch --yes --digest-algo sha1 --out %s %s --clearsign %s 2>/dev/null", + quoted_signed_filename, default_key, quoted_original_filename); + + r = system(command); + if (WEXITSTATUS(r) != 0) { + res = MAIL_ERROR_COMMAND; + goto unlink_signed; + } + + /* building the signed part */ + + signed_mime = mailprivacy_new_file_part(privacy, signed_filename, + NULL, MAILMIME_MECHANISM_8BIT); + if (signed_mime == NULL) { + res = MAIL_ERROR_MEMORY; + goto unlink_signed; + } + + /* place original content type */ + + content_type = mailmime_content_dup(mime->mm_content_type); + if (content_type == NULL) { + mailprivacy_mime_clear(signed_mime); + mailmime_free(signed_mime); + res = MAIL_ERROR_MEMORY; + goto unlink_signed; + } + + mailmime_content_free(signed_mime->mm_content_type); + signed_mime->mm_content_type = content_type; + + /* place original MIME fields */ + + if (mime->mm_mime_fields != NULL) { + struct mailmime_fields * mime_fields; + clistiter * cur; + + mime_fields = mailprivacy_mime_fields_dup(privacy, + mime->mm_mime_fields); + if (mime_fields == NULL) { + mailprivacy_mime_clear(signed_mime); + mailmime_free(signed_mime); + res = MAIL_ERROR_MEMORY; + goto unlink_signed; + } + for(cur = clist_begin(mime_fields->fld_list) ; + cur != NULL ; cur = clist_next(cur)) { + struct mailmime_field * field; + + field = clist_content(cur); + if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) { + mailmime_field_free(field); + clist_delete(mime_fields->fld_list, cur); + break; + } + } + clist_concat(signed_mime->mm_mime_fields->fld_list, + mime_fields->fld_list); + mailmime_fields_free(mime_fields); + } + + unlink(signed_filename); + unlink(original_filename); + + * result = signed_mime; + + return MAIL_NO_ERROR; + + unlink_signed: + unlink(signed_filename); + unlink_original: + unlink(original_filename); + err: + return res; +} + + +static int pgp_armor_encrypt(struct mailprivacy * privacy, + struct mailmime * mime, struct mailmime ** result) +{ + char original_filename[PATH_MAX]; + FILE * original_f; + char encrypted_filename[PATH_MAX]; + FILE * encrypted_f; + char quoted_original_filename[PATH_MAX]; + char quoted_encrypted_filename[PATH_MAX]; + int col; + struct mailmime * encrypted_mime; + int res; + int r; + char command[PATH_MAX]; + struct mailmime_content * content_type; + struct mailmime * root; + struct mailimf_fields * fields; + char recipient[PATH_MAX]; + + if (mime->mm_type != MAILMIME_SINGLE) { + res = MAIL_ERROR_INVAL; + goto err; + } + + if (mime->mm_data.mm_single == NULL) { + res = MAIL_ERROR_INVAL; + goto err; + } + + root = mime; + while (root->mm_parent != NULL) + root = root->mm_parent; + + fields = NULL; + if (root->mm_type == MAILMIME_MESSAGE) + fields = root->mm_data.mm_message.mm_fields; + + /* recipient */ + + collect_recipient(recipient, sizeof(recipient), fields); + + /* get part to encrypt */ + + original_f = mailprivacy_get_tmp_file(privacy, original_filename, + sizeof(original_filename)); + if (original_f == NULL) { + res = MAIL_ERROR_FILE; + goto err; + } + + col = 0; + r = mailmime_data_write(original_f, &col, mime->mm_data.mm_single, 1); + if (r != MAILIMF_NO_ERROR) { + fclose(original_f); + res = MAIL_ERROR_FILE; + goto unlink_original; + } + fclose(original_f); + + encrypted_f = mailprivacy_get_tmp_file(privacy, + encrypted_filename, + sizeof(encrypted_filename)); + if (encrypted_f == NULL) { + res = MAIL_ERROR_FILE; + goto unlink_original; + } + fclose(encrypted_f); + + r = mail_quote_filename(quoted_original_filename, + sizeof(quoted_original_filename), original_filename); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + r = mail_quote_filename(quoted_encrypted_filename, + sizeof(quoted_encrypted_filename), encrypted_filename); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + snprintf(command, sizeof(command), + "gpg -q %s --batch --yes --out %s -e --armor %s 2>/dev/null", + recipient, quoted_encrypted_filename, quoted_original_filename); + + r = system(command); + if (WEXITSTATUS(r) != 0) { + res = MAIL_ERROR_COMMAND; + goto unlink_encrypted; + } + + /* building the encrypted part */ + + encrypted_mime = mailprivacy_new_file_part(privacy, + encrypted_filename, + "application/octet-stream", + MAILMIME_MECHANISM_8BIT); + if (encrypted_mime == NULL) { + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + /* place original content type */ + + content_type = mailmime_content_dup(mime->mm_content_type); + if (content_type == NULL) { + mailprivacy_mime_clear(encrypted_mime); + mailmime_free(encrypted_mime); + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + mailmime_content_free(encrypted_mime->mm_content_type); + encrypted_mime->mm_content_type = content_type; + + /* place original MIME fields */ + + if (mime->mm_mime_fields != NULL) { + struct mailmime_fields * mime_fields; + clistiter * cur; + + mime_fields = mailprivacy_mime_fields_dup(privacy, mime->mm_mime_fields); + if (mime_fields == NULL) { + mailprivacy_mime_clear(encrypted_mime); + mailmime_free(encrypted_mime); + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + for(cur = clist_begin(mime_fields->fld_list) ; + cur != NULL ; cur = clist_next(cur)) { + struct mailmime_field * field; + + field = clist_content(cur); + if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) { + mailmime_field_free(field); + clist_delete(mime_fields->fld_list, cur); + break; + } + } + clist_concat(encrypted_mime->mm_mime_fields->fld_list, + mime_fields->fld_list); + mailmime_fields_free(mime_fields); + } + + unlink(encrypted_filename); + unlink(original_filename); + + * result = encrypted_mime; + + return MAIL_NO_ERROR; + + unlink_encrypted: + unlink(encrypted_filename); + unlink_original: + unlink(original_filename); + err: + return res; +} + + +static int pgp_armor_sign_encrypt(struct mailprivacy * privacy, + struct mailmime * mime, struct mailmime ** result) +{ + char default_key[PATH_MAX]; + char original_filename[PATH_MAX]; + FILE * original_f; + char encrypted_filename[PATH_MAX]; + FILE * encrypted_f; + char quoted_original_filename[PATH_MAX]; + char quoted_encrypted_filename[PATH_MAX]; + int col; + struct mailmime * encrypted_mime; + int res; + int r; + char command[PATH_MAX]; + struct mailmime_content * content_type; + struct mailmime * root; + struct mailimf_fields * fields; + char recipient[PATH_MAX]; + char * email; + + if (mime->mm_type != MAILMIME_SINGLE) { + res = MAIL_ERROR_INVAL; + goto err; + } + + if (mime->mm_data.mm_single == NULL) { + res = MAIL_ERROR_INVAL; + goto err; + } + + /* get signing key */ + + * default_key = '\0'; + email = get_first_from_addr(mime); + if (email != NULL) + snprintf(default_key, sizeof(default_key), + "--default-key %s", email); + + root = mime; + while (root->mm_parent != NULL) + root = root->mm_parent; + + fields = NULL; + if (root->mm_type == MAILMIME_MESSAGE) + fields = root->mm_data.mm_message.mm_fields; + + /* recipient */ + + collect_recipient(recipient, sizeof(recipient), fields); + + /* get part to encrypt */ + + original_f = mailprivacy_get_tmp_file(privacy, + original_filename, + sizeof(original_filename)); + if (original_f == NULL) { + res = MAIL_ERROR_FILE; + goto err; + } + + col = 0; + r = mailmime_data_write(original_f, &col, mime->mm_data.mm_single, 1); + if (r != MAILIMF_NO_ERROR) { + fclose(original_f); + res = MAIL_ERROR_FILE; + goto unlink_original; + } + fclose(original_f); + + encrypted_f = mailprivacy_get_tmp_file(privacy, + encrypted_filename, + sizeof(encrypted_filename)); + if (encrypted_f == NULL) { + res = MAIL_ERROR_FILE; + goto unlink_original; + } + fclose(encrypted_f); + + r = mail_quote_filename(quoted_original_filename, + sizeof(quoted_original_filename), original_filename); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + r = mail_quote_filename(quoted_encrypted_filename, + sizeof(quoted_encrypted_filename), encrypted_filename); + if (r < 0) { + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + snprintf(command, sizeof(command), + "gpg -q %s --batch --yes --digest-algo sha1 " + "--out %s %s -e -s -a %s 2>/dev/null", + recipient, encrypted_filename, default_key, original_filename); + + r = system(command); + if (WEXITSTATUS(r) != 0) { + res = MAIL_ERROR_COMMAND; + goto unlink_encrypted; + } + + /* building the encrypted part */ + + encrypted_mime = mailprivacy_new_file_part(privacy, + encrypted_filename, + "application/octet-stream", + MAILMIME_MECHANISM_8BIT); + if (encrypted_mime == NULL) { + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + /* place original content type */ + + content_type = mailmime_content_dup(mime->mm_content_type); + if (content_type == NULL) { + mailprivacy_mime_clear(encrypted_mime); + mailmime_free(encrypted_mime); + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + + mailmime_content_free(encrypted_mime->mm_content_type); + encrypted_mime->mm_content_type = content_type; + + /* place original MIME fields */ + + if (mime->mm_mime_fields != NULL) { + struct mailmime_fields * mime_fields; + clistiter * cur; + + mime_fields = mailprivacy_mime_fields_dup(privacy, mime->mm_mime_fields); + if (mime_fields == NULL) { + mailprivacy_mime_clear(encrypted_mime); + mailmime_free(encrypted_mime); + res = MAIL_ERROR_MEMORY; + goto unlink_encrypted; + } + for(cur = clist_begin(mime_fields->fld_list) ; + cur != NULL ; cur = clist_next(cur)) { + struct mailmime_field * field; + + field = clist_content(cur); + if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) { + mailmime_field_free(field); + clist_delete(mime_fields->fld_list, cur); + break; + } + } + clist_concat(encrypted_mime->mm_mime_fields->fld_list, + mime_fields->fld_list); + mailmime_fields_free(mime_fields); + } + + unlink(encrypted_filename); + unlink(original_filename); + + * result = encrypted_mime; + + return MAIL_NO_ERROR; + + unlink_encrypted: + unlink(encrypted_filename); + unlink_original: + unlink(original_filename); + err: + return res; +} + + +static struct mailprivacy_encryption pgp_encryption_tab[] = { + /* PGP signed part */ + { + .name = "signed", + .description = "PGP signed part", + .encrypt = pgp_sign_mime, + }, + + /* PGP encrypted part */ + + { + .name = "encrypted", + .description = "PGP encrypted part", + .encrypt = pgp_encrypt_mime, + }, + + /* PGP signed & encrypted part */ + + { + .name = "signed-encrypted", + .description = "PGP signed & encrypted part", + .encrypt = pgp_sign_encrypt_mime, + }, + + /* PGP clear signed part */ + + { + .name = "clear-signed", + .description = "PGP clear signed part", + .encrypt = pgp_clear_sign, + }, + + /* PGP armor encrypted part */ + + { + .name = "encrypted-armor", + .description = "PGP ASCII armor encrypted part", + .encrypt = pgp_armor_encrypt, + }, + + /* PGP armor signed & encrypted part */ + + { + .name = "signed-encrypted-armor", + .description = "PGP ASCII armor signed & encrypted part", + .encrypt = pgp_armor_sign_encrypt, + }, +}; + +static struct mailprivacy_protocol pgp_protocol = { + .name = "pgp", + .description = "OpenPGP", + + .is_encrypted = pgp_test_encrypted, + .decrypt = pgp_handler, + + .encryption_count = + (sizeof(pgp_encryption_tab) / sizeof(pgp_encryption_tab[0])), + + .encryption_tab = pgp_encryption_tab, +}; + +int mailprivacy_gnupg_init(struct mailprivacy * privacy) +{ + return mailprivacy_register(privacy, &pgp_protocol); +} + +void mailprivacy_gnupg_done(struct mailprivacy * privacy) +{ + mailprivacy_unregister(privacy, &pgp_protocol); +} -- cgit v0.9.0.2