diff options
-rw-r--r-- | src/compose.c | 126 | ||||
-rw-r--r-- | src/rfc2015.c | 336 | ||||
-rw-r--r-- | src/rfc2015.h | 8 |
3 files changed, 418 insertions, 52 deletions
diff --git a/src/compose.c b/src/compose.c index b8e4b853..2831e8c5 100644 --- a/src/compose.c +++ b/src/compose.c @@ -3061,6 +3061,66 @@ static gint compose_clearsign_text(Compose *compose, gchar **text) return 0; } + +static gint compose_encrypt_armored(Compose *compose, gchar **text) +{ + gchar *tmp_file; + + tmp_file = get_tmp_file(); + if (str_write_to_file(*text, tmp_file) < 0) { + g_free(tmp_file); + return -1; + } + + if (rfc2015_encrypt_armored(tmp_file, compose->to_list) < 0) { + g_unlink(tmp_file); + g_free(tmp_file); + return -1; + } + + g_free(*text); + *text = file_read_to_str(tmp_file); + g_unlink(tmp_file); + g_free(tmp_file); + if (*text == NULL) + return -1; + + return 0; +} + +static gint compose_encrypt_sign_armored(Compose *compose, gchar **text) +{ + GSList *key_list; + gchar *tmp_file; + + tmp_file = get_tmp_file(); + if (str_write_to_file(*text, tmp_file) < 0) { + g_free(tmp_file); + return -1; + } + + if (compose_create_signers_list(compose, &key_list) < 0) { + g_unlink(tmp_file); + g_free(tmp_file); + return -1; + } + + if (rfc2015_encrypt_sign_armored + (tmp_file, compose->to_list, key_list) < 0) { + g_unlink(tmp_file); + g_free(tmp_file); + return -1; + } + + g_free(*text); + *text = file_read_to_str(tmp_file); + g_unlink(tmp_file); + g_free(tmp_file); + if (*text == NULL) + return -1; + + return 0; +} #endif /* USE_GPGME */ static gint compose_write_to_file(Compose *compose, const gchar *file, @@ -3079,6 +3139,10 @@ static gint compose_write_to_file(Compose *compose, const gchar *file, const gchar *src_charset = CS_INTERNAL; EncodingType encoding; gint line; +#if USE_GPGME + gboolean use_pgpmime_encryption = FALSE; + gboolean use_pgpmime_signing = FALSE; +#endif if ((fp = g_fopen(file, "wb")) == NULL) { FILE_OP_ERROR(file, "fopen"); @@ -3162,9 +3226,17 @@ static gint compose_write_to_file(Compose *compose, const gchar *file, buf = canon_buf; #if USE_GPGME + if (compose->use_signing && !compose->account->clearsign) + use_pgpmime_signing = TRUE; + if (compose->use_encryption && compose->account->ascii_armored) { + use_pgpmime_encryption = FALSE; + use_pgpmime_signing = FALSE; + } + if (compose->use_encryption && !compose->account->ascii_armored) + use_pgpmime_encryption = TRUE; + /* protect trailing spaces */ - if (rfc2015_is_available() && !is_draft && - compose->use_signing && !compose->account->clearsign) { + if (rfc2015_is_available() && !is_draft && use_pgpmime_signing) { if (encoding == ENC_7BIT) { if (!g_ascii_strcasecmp(body_charset, CS_ISO_2022_JP)) { gchar *tmp; @@ -3180,18 +3252,33 @@ static gint compose_write_to_file(Compose *compose, const gchar *file, } } - if (rfc2015_is_available() && !is_draft && - compose->use_signing && compose->account->clearsign) { - /* MIME encoding doesn't fit with cleartext signature */ - if (encoding == ENC_QUOTED_PRINTABLE || encoding == ENC_BASE64) - encoding = ENC_8BIT; + if (rfc2015_is_available() && !is_draft) { + gint ret; - if (compose_clearsign_text(compose, &buf) < 0) { - g_warning("clearsign failed\n"); - fclose(fp); - g_unlink(file); - g_free(buf); - return -1; + if (compose->use_encryption && compose->account->ascii_armored) { + if (compose->use_signing) + ret = compose_encrypt_sign_armored(compose, &buf); + else + ret = compose_encrypt_armored(compose, &buf); + if (ret < 0) { + g_warning("ascii-armored encryption failed\n"); + fclose(fp); + g_unlink(file); + g_free(buf); + return -1; + } + } else if (compose->use_signing && compose->account->clearsign) { + /* MIME encoding doesn't fit with cleartext signature */ + if (encoding == ENC_QUOTED_PRINTABLE || encoding == ENC_BASE64) + encoding = ENC_8BIT; + + if (compose_clearsign_text(compose, &buf) < 0) { + g_warning("clearsign failed\n"); + fclose(fp); + g_unlink(file); + g_free(buf); + return -1; + } } } #endif @@ -3251,8 +3338,7 @@ static gint compose_write_to_file(Compose *compose, const gchar *file, fprintf(fp, "Content-Type: text/plain; charset=%s\n", body_charset); #if USE_GPGME - if (rfc2015_is_available() && - compose->use_signing && !compose->account->clearsign) + if (rfc2015_is_available() && use_pgpmime_signing) fprintf(fp, "Content-Disposition: inline\n"); #endif fprintf(fp, "Content-Transfer-Encoding: %s\n", @@ -3313,15 +3399,14 @@ static gint compose_write_to_file(Compose *compose, const gchar *file, return 0; } - if ((compose->use_signing && !compose->account->clearsign) || - compose->use_encryption) { + if (use_pgpmime_signing || use_pgpmime_encryption) { if (canonicalize_file_replace(file) < 0) { g_unlink(file); return -1; } } - if (compose->use_signing && !compose->account->clearsign) { + if (use_pgpmime_signing) { GSList *key_list; if (compose_create_signers_list(compose, &key_list) < 0 || @@ -3330,7 +3415,7 @@ static gint compose_write_to_file(Compose *compose, const gchar *file, return -1; } } - if (compose->use_encryption) { + if (use_pgpmime_encryption) { if (compose->use_bcc) { const gchar *text; gchar *bcc; @@ -3354,8 +3439,7 @@ static gint compose_write_to_file(Compose *compose, const gchar *file, } } } - if (rfc2015_encrypt(file, compose->to_list, - compose->account->ascii_armored) < 0) { + if (rfc2015_encrypt(file, compose->to_list) < 0) { g_unlink(file); return -1; } diff --git a/src/rfc2015.c b/src/rfc2015.c index 48d178e3..d380e0a8 100644 --- a/src/rfc2015.c +++ b/src/rfc2015.c @@ -864,8 +864,7 @@ leave: * Encrypt the file by extracting all recipients and finding the * encryption keys for all of them. The file content is then replaced * by the encrypted one. */ -gint rfc2015_encrypt(const gchar *file, GSList *recp_list, - gboolean ascii_armored) +gint rfc2015_encrypt(const gchar *file, GSList *recp_list) { FILE *fp = NULL; gchar buf[BUFFSIZE]; @@ -1022,27 +1021,20 @@ gint rfc2015_encrypt(const gchar *file, GSList *recp_list, if (!mime_version_seen) fputs("MIME-Version: 1\r\n", fp); - if (ascii_armored) { - fprintf(fp, - "Content-Type: text/plain; charset=US-ASCII\r\n" - "Content-Transfer-Encoding: 7bit\r\n" - "\r\n"); - } else { - fprintf(fp, - "Content-Type: multipart/encrypted;" - " protocol=\"application/pgp-encrypted\";\r\n" - " boundary=\"%s\"\r\n" - "\r\n" - "--%s\r\n" - "Content-Type: application/pgp-encrypted\r\n" - "\r\n" - "Version: 1\r\n" - "\r\n" - "--%s\r\n" - "Content-Type: application/octet-stream\r\n" - "\r\n", - boundary, boundary, boundary); - } + fprintf(fp, + "Content-Type: multipart/encrypted;" + " protocol=\"application/pgp-encrypted\";\r\n" + " boundary=\"%s\"\r\n" + "\r\n" + "--%s\r\n" + "Content-Type: application/pgp-encrypted\r\n" + "\r\n" + "Version: 1\r\n" + "\r\n" + "--%s\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n", + boundary, boundary, boundary); /* append the encrypted stuff */ err = (gpgme_data_seek(cipher, 0 , SEEK_SET) == -1) ? @@ -1068,12 +1060,10 @@ gint rfc2015_encrypt(const gchar *file, GSList *recp_list, } /* and the final boundary */ - if (!ascii_armored) { - fprintf(fp, - "\r\n" - "--%s--\r\n", - boundary); - } + fprintf(fp, + "\r\n" + "--%s--\r\n", + boundary); fflush(fp); if (ferror(fp)) { FILE_OP_ERROR(file, "fwrite"); @@ -1102,6 +1092,118 @@ failure: return -1; /* error */ } +gint rfc2015_encrypt_armored(const gchar *file, GSList *recp_list) +{ + FILE *fp = NULL; + gchar buf[BUFFSIZE]; + gint i; + gpgme_error_t err; + gpgme_data_t plain = NULL; + gpgme_data_t cipher = NULL; + gpgme_key_t *kset = NULL; + ssize_t bytesRW = 0; + + kset = gpgmegtk_recipient_selection(recp_list); + if (!kset) { + debug_print("error creating recipient list\n"); + goto failure; + } + + /* Open the source file */ + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + err = gpgme_data_new(&plain); + if (err) { + g_warning("gpgme_data_new failed: %s\n", gpgme_strerror(err)); + goto failure; + } + + while (bytesRW != -1 && fgets(buf, sizeof(buf), fp)) { + bytesRW = gpgme_data_write(plain, buf, strlen(buf)); + } + if (ferror(fp)) { + FILE_OP_ERROR(file, "fgets"); + goto failure; + } + if (bytesRW == -1) { + debug_print("gpgme_data_write failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + cipher = pgp_encrypt(plain, kset); + gpgme_data_release(plain); + plain = NULL; + i = 0; + while (kset[i] != NULL) { + gpgme_key_unref(kset[i]); + i++; + } + g_free(kset); + kset = NULL; + if (!cipher) + goto failure; + + if (fclose(fp)) { + FILE_OP_ERROR(file, "fclose"); + goto failure; + } + if ((fp = g_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + err = (gpgme_data_seek(cipher, 0 , SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (err) { + debug_print("** gpgme_data_seek on cipher failed: %s\n", + gpgme_strerror(err)); + debug_print("gpgme_data_seek failed: %s\n", + gpgme_strerror(err)); + goto failure; + } + + bytesRW = gpgme_data_read(cipher, buf, sizeof(buf)); + while (bytesRW > 0) { + fwrite(buf, bytesRW, 1, fp); + bytesRW = gpgme_data_read(cipher, buf, sizeof(buf)); + } + + if (bytesRW != 0) { + debug_print("** gpgme_data_read failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + fflush(fp); + if (ferror(fp)) { + FILE_OP_ERROR(file, "fwrite"); + goto failure; + } + fclose(fp); + gpgme_data_release(cipher); + return 0; + +failure: + if (fp) + fclose(fp); + gpgme_data_release(plain); + gpgme_data_release(cipher); + if (kset != NULL) { + i = 0; + while (kset[i] != NULL) { + gpgme_key_unref(kset[i]); + i++; + } + g_free(kset); + } + + return -1; +} + /* * plain contains an entire mime object. Sign it and return an * GpgmeData object with the signature of it or NULL in case of error. @@ -1178,6 +1280,81 @@ leave: return sig; } +/* + * plain contains an entire mime object. Encrypt and sign it and return an + * GpgmeData object with the encrypted and signed version of it or NULL in + * case of error. + * micalg returns the micalg information about the signature. + */ +static gpgme_data_t pgp_encrypt_sign(gpgme_data_t plain, gpgme_key_t kset[], + GSList *key_list, gchar **micalg) +{ + GSList *p; + gpgme_ctx_t ctx = NULL; + gpgme_error_t err; + gpgme_data_t cipher = NULL; + gpgme_sign_result_t result = NULL; + struct passphrase_cb_info_s info; + + *micalg = NULL; + memset(&info, 0, sizeof info); + + err = gpgme_new(&ctx); + if (err) + goto leave; + err = gpgme_data_new(&cipher); + if (err) + goto leave; + + if (!g_getenv("GPG_AGENT_INFO")) { + info.c = ctx; + gpgme_set_passphrase_cb(ctx, gpgmegtk_passphrase_cb, &info); + } + gpgme_set_textmode(ctx, 1); + gpgme_set_armor(ctx, 1); + gpgme_signers_clear(ctx); + for (p = key_list; p != NULL; p = p->next) { + err = gpgme_signers_add(ctx, (gpgme_key_t) p->data); + if (err) + goto leave; + } + for (p = key_list; p != NULL; p = p->next) + gpgme_key_unref((gpgme_key_t) p->data); + g_slist_free(key_list); + + err = (gpgme_data_seek(plain, 0, SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (!err) { + err = gpgme_op_encrypt_sign(ctx, kset, GPGME_ENCRYPT_ALWAYS_TRUST, plain, cipher); + } + if (!err) { + result = gpgme_op_sign_result(ctx); + if (result && result->signatures) { + if (gpgme_get_protocol(ctx) == GPGME_PROTOCOL_OpenPGP) { + *micalg = g_strdup_printf + ("PGP-%s", gpgme_hash_algo_name(result->signatures->hash_algo)); + } else { + *micalg = g_strdup(gpgme_hash_algo_name(result->signatures->hash_algo)); + } + } else { + /* can't get result (maybe no signing key?) */ + err = GPG_ERR_USER_1; + } + } + +leave: + if (err) { + gpgmegtk_free_passphrase(); + g_warning("pgp_sign(): encryption and signing failed: %s\n", gpgme_strerror(err)); + gpgme_data_release(cipher); + cipher = NULL; + } else { + debug_print("encryption and signing succeeded\n"); + } + + gpgme_release(ctx); + return cipher; +} /* * Sign the file and replace its content with the signed one. @@ -1483,4 +1660,105 @@ failure: return -1; } +gint rfc2015_encrypt_sign_armored(const gchar *file, GSList *recp_list, + GSList *key_list) +{ + FILE *fp; + gchar buf[BUFFSIZE]; + gint i; + gpgme_error_t err; + gpgme_data_t plain = NULL; + gpgme_data_t cipher = NULL; + gpgme_key_t *kset = NULL; + ssize_t bytesRW = 0; + gchar *micalg = NULL; + + kset = gpgmegtk_recipient_selection(recp_list); + if (!kset) { + debug_print("error creating recipient list\n"); + goto failure; + } + + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + err = gpgme_data_new(&plain); + if (err) { + debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err)); + goto failure; + } + + while ((bytesRW != -1) && fgets(buf, sizeof(buf), fp)) { + bytesRW = gpgme_data_write(plain, buf, strlen(buf)); + } + if (ferror(fp)) { + FILE_OP_ERROR(file, "fgets"); + goto failure; + } + if (bytesRW == -1) { + debug_print("gpgme_data_write failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + cipher = pgp_encrypt_sign(plain, kset, key_list, &micalg); + if (micalg) + g_free(micalg); + if (!cipher) + goto failure; + + if (fclose(fp) == EOF) { + FILE_OP_ERROR(file, "fclose"); + fp = NULL; + goto failure; + } + if ((fp = g_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + goto failure; + } + + err = (gpgme_data_seek(cipher, 0, SEEK_SET) == -1) ? + gpgme_error_from_errno(errno) : 0; + if (err) { + debug_print("gpgme_data_seek on cipher failed: %s\n", + gpgme_strerror(err)); + goto failure; + } + + bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE); + while (bytesRW > 0) { + fwrite(buf, bytesRW, 1, fp); + bytesRW = gpgme_data_read(cipher, buf, BUFFSIZE); + } + if (bytesRW != 0) { + debug_print("gpgme_data_read failed: %s\n", + gpgme_strerror(gpgme_error_from_errno(errno))); + goto failure; + } + + if (fclose(fp) == EOF) { + FILE_OP_ERROR(file, "fclose"); + fp = NULL; + goto failure; + } + gpgme_data_release(plain); + gpgme_data_release(cipher); + for (i = 0; kset[i] != NULL; i++) + gpgme_key_unref(kset[i]); + return 0; + +failure: + if (fp) + fclose(fp); + gpgme_data_release(plain); + gpgme_data_release(cipher); + if (kset != NULL) { + for (i = 0; kset[i] != NULL; i++) + gpgme_key_unref(kset[i]); + } + return -1; +} + #endif /* USE_GPGME */ diff --git a/src/rfc2015.h b/src/rfc2015.h index e7c9f123..8e0baee0 100644 --- a/src/rfc2015.h +++ b/src/rfc2015.h @@ -49,11 +49,15 @@ FILE *rfc2015_open_message_decrypted (MsgInfo *msginfo, GSList *rfc2015_create_signers_list (const gchar *keyid); gint rfc2015_encrypt (const gchar *file, - GSList *recp_list, - gboolean ascii_armored); + GSList *recp_list); +gint rfc2015_encrypt_armored (const gchar *file, + GSList *recp_list); gint rfc2015_sign (const gchar *file, GSList *key_list); gint rfc2015_clearsign (const gchar *file, GSList *key_list); +gint rfc2015_encrypt_sign_armored (const gchar *file, + GSList *recp_list, + GSList *key_list); #endif /* __RFC2015_H__ */ |