[gmime] Added support for decypting multipart/encrypted messages using a session key
- From: Jeffrey Stedfast <fejj src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gmime] Added support for decypting multipart/encrypted messages using a session key
- Date: Mon, 5 Dec 2016 15:05:44 +0000 (UTC)
commit e80dd2594f546d0eeda0b53082d6ca34b738f9f3
Author: Jeffrey Stedfast <jestedfa microsoft com>
Date: Mon Dec 5 10:05:05 2016 -0500
Added support for decypting multipart/encrypted messages using a session key
2016-12-05 Jeffrey Stedfast <fejj gnome org>
* gmime/gmime-multipart-encrypted.c
(g_mime_multipart_encrypted_decrypt_session): New function to
decrypt a multipart/encrypted using a session_key.
* gmime/gmime-crypto-context.c (g_mime_crypto_context_decrypt_session): New
function to decrypt a MIME part using a session_key.
* gmime/gmime-gpg-context.c (gpg_ctx_get_argv): Added support for
--override-session-key-id for the new decrypt_session() method.
(gpg_ctx_op_start): Updated to create the secret_fd when passing a session_key
as well.
(gpg_ctx_write_session_key): New function to write the session_key to gpg.
(gpg_decrypt_session): New function to decrypt a MIME part using a session_key.
Thanks to Daniel Kahn Gillmor for this patch.
ChangeLog | 18 ++++++++++
gmime/gmime-crypto-context.c | 67 ++++++++++++++++++++++++++++++++++++-
gmime/gmime-crypto-context.h | 8 ++++
gmime/gmime-gpg-context.c | 66 ++++++++++++++++++++++++++++++++++--
gmime/gmime-multipart-encrypted.c | 39 +++++++++++++++++++++-
gmime/gmime-multipart-encrypted.h | 6 +++
6 files changed, 198 insertions(+), 6 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index d9ad9cc..a219032 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,23 @@
2016-12-05 Jeffrey Stedfast <fejj gnome org>
+ * gmime/gmime-multipart-encrypted.c
+ (g_mime_multipart_encrypted_decrypt_session): New function to
+ decrypt a multipart/encrypted using a session_key.
+
+ * gmime/gmime-crypto-context.c (g_mime_crypto_context_decrypt_session): New
+ function to decrypt a MIME part using a session_key.
+
+ * gmime/gmime-gpg-context.c (gpg_ctx_get_argv): Added support for
+ --override-session-key-id for the new decrypt_session() method.
+ (gpg_ctx_op_start): Updated to create the secret_fd when passing a session_key
+ as well.
+ (gpg_ctx_write_session_key): New function to write the session_key to gpg.
+ (gpg_decrypt_session): New function to decrypt a MIME part using a session_key.
+
+ Thanks to Daniel Kahn Gillmor for this patch.
+
+2016-12-05 Jeffrey Stedfast <fejj gnome org>
+
* gmime/gmime-gpg-context.c (gpg_ctx_parse_status): Advance over the SESSION_KEY
identifier before calling next_token() so that next_token() actually gets the
sesstion key token that we want. Also fixed to free any existing session_key
diff --git a/gmime/gmime-crypto-context.c b/gmime/gmime-crypto-context.c
index 3b4171c..e610b7d 100644
--- a/gmime/gmime-crypto-context.c
+++ b/gmime/gmime-crypto-context.c
@@ -70,6 +70,10 @@ static int crypto_encrypt (GMimeCryptoContext *ctx, gboolean sign,
static GMimeDecryptResult *crypto_decrypt (GMimeCryptoContext *ctx, GMimeStream *istream,
GMimeStream *ostream, GError **err);
+static GMimeDecryptResult *crypto_decrypt_session (GMimeCryptoContext *ctx, const char *session_key,
+ GMimeStream *istream, GMimeStream *ostream,
+ GError **err);
+
static int crypto_import_keys (GMimeCryptoContext *ctx, GMimeStream *istream,
GError **err);
@@ -120,6 +124,7 @@ g_mime_crypto_context_class_init (GMimeCryptoContextClass *klass)
klass->verify = crypto_verify;
klass->encrypt = crypto_encrypt;
klass->decrypt = crypto_decrypt;
+ klass->decrypt_session = crypto_decrypt_session;
klass->import_keys = crypto_import_keys;
klass->export_keys = crypto_export_keys;
klass->get_signature_protocol = crypto_get_signature_protocol;
@@ -423,6 +428,17 @@ crypto_decrypt (GMimeCryptoContext *ctx, GMimeStream *istream,
return NULL;
}
+static GMimeDecryptResult *
+crypto_decrypt_session (GMimeCryptoContext *ctx, const char *session_key,
+ GMimeStream *istream, GMimeStream *ostream,
+ GError **err)
+{
+ g_set_error (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED,
+ "Decryption with a session key is not supported by this crypto context");
+
+ return NULL;
+}
+
/**
* g_mime_crypto_context_decrypt:
@@ -443,7 +459,7 @@ crypto_decrypt (GMimeCryptoContext *ctx, GMimeStream *istream,
* was encrypted to.
*
* Note: It *may* be possible to maliciously design an encrypted stream such
- * that recursively decrypting it will result in ane endless loop, causing
+ * that recursively decrypting it will result in an endless loop, causing
* a denial of service attack on your application.
*
* Returns: (transfer full): a #GMimeDecryptResult on success or %NULL
@@ -460,6 +476,55 @@ g_mime_crypto_context_decrypt (GMimeCryptoContext *ctx, GMimeStream *istream,
return GMIME_CRYPTO_CONTEXT_GET_CLASS (ctx)->decrypt (ctx, istream, ostream, err);
}
+/**
+ * g_mime_crypto_context_decrypt_session:
+ * @ctx: a #GMimeCryptoContext
+ * @session_key: session key to use
+ * @istream: input/ciphertext stream
+ * @ostream: output/cleartext stream
+ * @err: a #GError
+ *
+ * Decrypts the ciphertext input stream using a specific session key
+ * and writes the resulting cleartext to the output stream. If
+ * @session_key is non-%NULL, but is not valid for the ciphertext, the
+ * decryption will fail even if other available secret key material
+ * may have been able to decrypt it. If @session_key is %NULL, this
+ * does the same thing as g_mime_crypto_context_decrypt().
+ *
+ * When non-%NULL, @session_key should be a %NULL-terminated string,
+ * such as the one returned by g_mime_decrypt_result_get_session_key()
+ * from a previous decryption.
+ *
+ * If the encrypted input stream was also signed, the returned
+ * #GMimeDecryptResult will have a non-%NULL list of signatures, each with a
+ * #GMimeSignatureStatus (among other details about each signature).
+ *
+ * On success, the returned #GMimeDecryptResult will contain a list of
+ * certificates, one for each recipient, that the original encrypted stream
+ * was encrypted to.
+ *
+ * Note: It *may* be possible to maliciously design an encrypted stream such
+ * that recursively decrypting it will result in an endless loop, causing
+ * a denial of service attack on your application.
+ *
+ * Returns: (transfer full): a #GMimeDecryptResult on success or %NULL
+ * on error.
+ **/
+GMimeDecryptResult *
+g_mime_crypto_context_decrypt_session (GMimeCryptoContext *ctx, const char *session_key,
+ GMimeStream *istream, GMimeStream *ostream,
+ GError **err)
+{
+ g_return_val_if_fail (GMIME_IS_CRYPTO_CONTEXT (ctx), NULL);
+ g_return_val_if_fail (GMIME_IS_STREAM (istream), NULL);
+ g_return_val_if_fail (GMIME_IS_STREAM (ostream), NULL);
+
+ if (session_key == NULL)
+ return GMIME_CRYPTO_CONTEXT_GET_CLASS (ctx)->decrypt (ctx, istream, ostream, err);
+ else
+ return GMIME_CRYPTO_CONTEXT_GET_CLASS (ctx)->decrypt_session (ctx, session_key, istream,
ostream, err);
+}
+
static int
crypto_import_keys (GMimeCryptoContext *ctx, GMimeStream *istream, GError **err)
diff --git a/gmime/gmime-crypto-context.h b/gmime/gmime-crypto-context.h
index 72573ed..6f337c4 100644
--- a/gmime/gmime-crypto-context.h
+++ b/gmime/gmime-crypto-context.h
@@ -113,6 +113,10 @@ struct _GMimeCryptoContextClass {
int (* export_keys) (GMimeCryptoContext *ctx, GPtrArray *keys,
GMimeStream *ostream, GError **err);
+
+ GMimeDecryptResult * (* decrypt_session) (GMimeCryptoContext *ctx, const char *session_key,
+ GMimeStream *istream, GMimeStream *ostream,
+ GError **err);
};
@@ -149,6 +153,10 @@ int g_mime_crypto_context_encrypt (GMimeCryptoContext *ctx, gboolean sign,
GMimeDecryptResult *g_mime_crypto_context_decrypt (GMimeCryptoContext *ctx, GMimeStream *istream,
GMimeStream *ostream, GError **err);
+GMimeDecryptResult *g_mime_crypto_context_decrypt_session (GMimeCryptoContext *ctx, const char *session_key,
+ GMimeStream *istream, GMimeStream *ostream,
+ GError **err);
+
/* key/certificate routines */
int g_mime_crypto_context_import_keys (GMimeCryptoContext *ctx, GMimeStream *istream, GError **err);
diff --git a/gmime/gmime-gpg-context.c b/gmime/gmime-gpg-context.c
index d8a4b2c..e39465c 100644
--- a/gmime/gmime-gpg-context.c
+++ b/gmime/gmime-gpg-context.c
@@ -107,6 +107,10 @@ static int gpg_encrypt (GMimeCryptoContext *ctx, gboolean sign, const char *user
static GMimeDecryptResult *gpg_decrypt (GMimeCryptoContext *ctx, GMimeStream *istream,
GMimeStream *ostream, GError **err);
+static GMimeDecryptResult *gpg_decrypt_session (GMimeCryptoContext *ctx, const char *session_key,
+ GMimeStream *istream, GMimeStream *ostream,
+ GError **err);
+
static int gpg_import_keys (GMimeCryptoContext *ctx, GMimeStream *istream,
GError **err);
@@ -158,6 +162,7 @@ g_mime_gpg_context_class_init (GMimeGpgContextClass *klass)
crypto_class->verify = gpg_verify;
crypto_class->encrypt = gpg_encrypt;
crypto_class->decrypt = gpg_decrypt;
+ crypto_class->decrypt_session = gpg_decrypt_session;
crypto_class->import_keys = gpg_import_keys;
crypto_class->export_keys = gpg_export_keys;
crypto_class->get_signature_protocol = gpg_get_signature_protocol;
@@ -333,8 +338,9 @@ struct _GpgCtx {
unsigned int need_passwd:1;
unsigned int bad_passwds:2;
unsigned int decrypt_okay:1;
+ unsigned int override_session_key:1;
- unsigned int padding:19;
+ unsigned int padding:18;
};
static struct _GpgCtx *
@@ -364,6 +370,7 @@ gpg_ctx_new (GMimeGpgContext *ctx)
gpg->always_trust = FALSE;
gpg->use_agent = FALSE;
gpg->armor = FALSE;
+ gpg->override_session_key = FALSE;
gpg->stdin_fd = -1;
gpg->stdout_fd = -1;
@@ -625,7 +632,7 @@ gpg_ctx_get_argv (struct _GpgCtx *gpg, int status_fd, int secret_fd, char ***str
(*strv)[v++] = buf = g_strdup_printf ("--status-fd=%d", status_fd);
g_ptr_array_add (args, buf);
- if (gpg->need_passwd) {
+ if (gpg->need_passwd && !gpg->override_session_key) {
(*strv)[v++] = buf = g_strdup_printf ("--command-fd=%d", secret_fd);
g_ptr_array_add (args, buf);
}
@@ -705,6 +712,11 @@ gpg_ctx_get_argv (struct _GpgCtx *gpg, int status_fd, int secret_fd, char ***str
if (gpg->ctx->retrieve_session_key)
g_ptr_array_add (args, "--show-session-key");
+ if (gpg->override_session_key) {
+ (*strv)[v++] = buf = g_strdup_printf ("--override-session-key-fd=%d", secret_fd);
+ g_ptr_array_add (args, buf);
+ }
+
g_ptr_array_add (args, "--decrypt");
g_ptr_array_add (args, "--output");
g_ptr_array_add (args, "-");
@@ -744,10 +756,14 @@ gpg_ctx_op_start (struct _GpgCtx *gpg)
char **argv, **strv = NULL;
int flags;
- for (i = 0; i < 10; i++)
+ maxfd = G_N_ELEMENTS (fds);
+ for (i = 0; i < maxfd; i++)
fds[i] = -1;
- maxfd = (gpg->need_passwd || gpg->sigstream) ? 10 : 8;
+ /* don't create the command-fd if we don't need it */
+ if (!(gpg->need_passwd || gpg->sigstream || gpg->override_session_key))
+ maxfd -=2;
+
for (i = 0; i < maxfd; i += 2) {
if (pipe (fds + i) == -1)
goto exception;
@@ -1057,6 +1073,29 @@ gpg_ctx_parse_signer_info (struct _GpgCtx *gpg, char *status)
}
}
+/* write the session_key to the secret file descriptor and close
+ it. Returns 0 on success. */
+static int
+gpg_ctx_write_session_key (struct _GpgCtx *gpg, const char *session_key)
+{
+ size_t len = strlen (session_key);
+ ssize_t w, nwritten = 0;
+
+ do {
+ do {
+ w = write (gpg->secret_fd, session_key + nwritten, len - nwritten);
+ } while (w == -1 && (errno == EINTR || errno == EAGAIN));
+
+ if (w > 0)
+ nwritten += w;
+ } while (nwritten < len && w != -1);
+
+ close (gpg->secret_fd);
+ gpg->secret_fd = -1;
+
+ return (w == -1);
+}
+
static int
gpg_ctx_parse_status (struct _GpgCtx *gpg, GError **err)
{
@@ -2040,6 +2079,14 @@ static GMimeDecryptResult *
gpg_decrypt (GMimeCryptoContext *context, GMimeStream *istream,
GMimeStream *ostream, GError **err)
{
+ return gpg_decrypt_session (context, NULL, istream, ostream, err);
+}
+
+static GMimeDecryptResult *
+gpg_decrypt_session (GMimeCryptoContext *context, const char *session_key,
+ GMimeStream *istream, GMimeStream *ostream,
+ GError **err)
+{
#ifdef ENABLE_CRYPTOGRAPHY
GMimeGpgContext *ctx = (GMimeGpgContext *) context;
GMimeDecryptResult *result;
@@ -2052,6 +2099,8 @@ gpg_decrypt (GMimeCryptoContext *context, GMimeStream *istream,
gpg_ctx_set_use_agent (gpg, ctx->use_agent);
gpg_ctx_set_istream (gpg, istream);
gpg_ctx_set_ostream (gpg, ostream);
+ if (session_key)
+ gpg->override_session_key = TRUE;
if (gpg_ctx_op_start (gpg) == -1) {
g_set_error (err, GMIME_ERROR, errno,
@@ -2062,6 +2111,15 @@ gpg_decrypt (GMimeCryptoContext *context, GMimeStream *istream,
return NULL;
}
+ if (session_key && gpg_ctx_write_session_key (gpg, session_key)) {
+ g_set_error (err, GMIME_ERROR, errno,
+ _("Failed to pass session key to gpg: %s"),
+ errno ? g_strerror (errno) : _("Unknown"));
+ gpg_ctx_free (gpg);
+
+ return NULL;
+ }
+
while (!gpg_ctx_op_complete (gpg)) {
if (gpg_ctx_op_step (gpg, err) == -1) {
gpg_ctx_op_cancel (gpg);
diff --git a/gmime/gmime-multipart-encrypted.c b/gmime/gmime-multipart-encrypted.c
index 35e7e1c..1cd07c3 100644
--- a/gmime/gmime-multipart-encrypted.c
+++ b/gmime/gmime-multipart-encrypted.c
@@ -292,6 +292,43 @@ GMimeObject *
g_mime_multipart_encrypted_decrypt (GMimeMultipartEncrypted *mpe, GMimeCryptoContext *ctx,
GMimeDecryptResult **result, GError **err)
{
+ return g_mime_multipart_encrypted_decrypt_session (mpe, ctx, NULL, result, err);
+}
+
+/**
+ * g_mime_multipart_encrypted_decrypt_session:
+ * @mpe: multipart/encrypted object
+ * @ctx: decryption context
+ * @session_key: session key to use
+ * @result: a #GMimeDecryptionResult
+ * @err: a #GError
+ *
+ * Attempts to decrypt the encrypted MIME part contained within the
+ * multipart/encrypted object @mpe using the @ctx decryption context
+ * trying only the supplied session key. If @session_key is
+ * non-%NULL, but is not valid for the ciphertext, the decryption will
+ * fail even if other available secret key material may have been able
+ * to decrypt it. If @session_key is %NULL, this does the same thing
+ * as g_mime_multipart_encrypted_decrypt().
+ *
+ * When non-%NULL, @session_key should be a %NULL-terminated string,
+ * such as the one returned by g_mime_decrypt_result_get_session_key()
+ * from a previous decryption.
+ *
+ * If @result is non-%NULL, then on a successful decrypt operation, it will be
+ * updated to point to a newly-allocated #GMimeDecryptResult with signature
+ * status information as well as a list of recipients that the part was
+ * encrypted to.
+ *
+ * Returns: (transfer full): the decrypted MIME part on success or
+ * %NULL on fail. If the decryption fails, an exception will be set on
+ * @err to provide information as to why the failure occured.
+ **/
+GMimeObject *
+g_mime_multipart_encrypted_decrypt_session (GMimeMultipartEncrypted *mpe, GMimeCryptoContext *ctx,
+ const char *session_key, GMimeDecryptResult **result,
+ GError **err)
+{
GMimeObject *decrypted, *version, *encrypted;
GMimeStream *stream, *ciphertext;
const char *protocol, *supported;
@@ -367,7 +404,7 @@ g_mime_multipart_encrypted_decrypt (GMimeMultipartEncrypted *mpe, GMimeCryptoCon
g_object_unref (crlf_filter);
/* get the cleartext */
- if (!(res = g_mime_crypto_context_decrypt (ctx, ciphertext, filtered_stream, err))) {
+ if (!(res = g_mime_crypto_context_decrypt_session (ctx, session_key, ciphertext, filtered_stream,
err))) {
g_object_unref (filtered_stream);
g_object_unref (ciphertext);
g_object_unref (stream);
diff --git a/gmime/gmime-multipart-encrypted.h b/gmime/gmime-multipart-encrypted.h
index 7f629e0..da154a2 100644
--- a/gmime/gmime-multipart-encrypted.h
+++ b/gmime/gmime-multipart-encrypted.h
@@ -76,6 +76,12 @@ GMimeObject *g_mime_multipart_encrypted_decrypt (GMimeMultipartEncrypted *mpe,
GMimeDecryptResult **result,
GError **err);
+GMimeObject *g_mime_multipart_encrypted_decrypt_session (GMimeMultipartEncrypted *mpe,
+ GMimeCryptoContext *ctx,
+ const char *session_key,
+ GMimeDecryptResult **result,
+ GError **err);
+
G_END_DECLS
#endif /* __GMIME_MULTIPART_ENCRYPTED_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]