aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhiro <hiro@ee746299-78ed-0310-b773-934348b2243d>2007-09-07 06:49:21 +0000
committerhiro <hiro@ee746299-78ed-0310-b773-934348b2243d>2007-09-07 06:49:21 +0000
commitba3dbbff7d2e41e611cea1c89a915cfa1273f418 (patch)
treee0917462b9963644d63f1a58c77f149cee34ab6a /src
parente9c15465d2b069798f8ac398073721fa43810f8e (diff)
implemented proper ascii-armored PGP encryption.
git-svn-id: svn://sylpheed.sraoss.jp/sylpheed/trunk@1891 ee746299-78ed-0310-b773-934348b2243d
Diffstat (limited to 'src')
-rw-r--r--src/compose.c126
-rw-r--r--src/rfc2015.c336
-rw-r--r--src/rfc2015.h8
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__ */