[gmime] Added unit tests for decrypt_session()



commit c0d9e42f1a8a35cd80c354c0c6831517bbcd72ce
Author: Jeffrey Stedfast <jestedfa microsoft com>
Date:   Mon Dec 5 10:16:48 2016 -0500

    Added unit tests for decrypt_session()
    
    Thanks to Daniel Kahn Gillmor for this patch

 tests/test-pgpmime.c |   88 +++++++++++++++++++++++++++++++++++++++-----------
 tests/testsuite.c    |   63 +++++++++++++++++++++++++++++-------
 tests/testsuite.h    |    1 +
 3 files changed, 121 insertions(+), 31 deletions(-)
---
diff --git a/tests/test-pgpmime.c b/tests/test-pgpmime.c
index 27e6b6b..90f3e5b 100644
--- a/tests/test-pgpmime.c
+++ b/tests/test-pgpmime.c
@@ -259,18 +259,15 @@ test_multipart_signed (GMimeCryptoContext *ctx)
 #define MULTIPART_ENCRYPTED_CONTENT "This is a test of multipart/encrypted.\n"
 
 static void
-test_multipart_encrypted (GMimeCryptoContext *ctx, gboolean sign)
+create_encrypted_message (GMimeCryptoContext *ctx, gboolean sign,
+                         GMimeStream **cleartext_out, GMimeStream **stream_out)
 {
        GMimeStream *cleartext, *stream;
        GMimeMultipartEncrypted *mpe;
-       GMimeDecryptResult *result;
        GMimeDataWrapper *content;
-       GMimeObject *decrypted;
        GPtrArray *recipients;
        GMimeMessage *message;
        Exception *ex = NULL;
-       GMimeParser *parser;
-       GByteArray *buf[2];
        GError *err = NULL;
        GMimePart *part;
        
@@ -324,12 +321,34 @@ test_multipart_encrypted (GMimeCryptoContext *ctx, gboolean sign)
        
        stream = g_mime_stream_mem_new ();
        g_mime_object_write_to_stream ((GMimeObject *) message, stream);
-       g_mime_stream_reset (stream);
        g_object_unref (message);
        
+       *stream_out = stream;
+       *cleartext_out = cleartext;
+}      
+
+static char *
+test_multipart_encrypted (GMimeCryptoContext *ctx, gboolean sign,
+                         GMimeStream *cleartext, GMimeStream *stream,
+                         const char *session_key)
+{
+       GMimeStream *test_stream;
+       GMimeMultipartEncrypted *mpe;
+       GMimeDecryptResult *result;
+       GMimeDataWrapper *content;
+       GMimeObject *decrypted;
+       GMimeMessage *message;
+       Exception *ex = NULL;
+       GMimeParser *parser;
+       GByteArray *buf[2];
+       GError *err = NULL;
+       GMimePart *part;
+       char *ret = NULL;
+       
+       g_mime_stream_reset (stream);
+       g_mime_stream_reset (cleartext);
        parser = g_mime_parser_new ();
        g_mime_parser_init_with_stream (parser, stream);
-       g_object_unref (stream);
        
        message = g_mime_parser_construct_message (parser);
        g_object_unref (parser);
@@ -343,19 +362,18 @@ test_multipart_encrypted (GMimeCryptoContext *ctx, gboolean sign)
        mpe = (GMimeMultipartEncrypted *) message->mime_part;
        
        /* okay, now to test our decrypt function... */
-       decrypted = g_mime_multipart_encrypted_decrypt (mpe, ctx, &result, &err);
+       decrypted = g_mime_multipart_encrypted_decrypt_session (mpe, ctx, session_key, &result, &err);
        if (!decrypted || err != NULL) {
                ex = exception_new ("decryption failed: %s", err->message);
-               g_object_unref (cleartext);
                g_error_free (err);
                throw (ex);
        }
        
        if (!result->session_key) {
                ex = exception_new ("No session key returned!");
-               g_object_unref (cleartext);
                throw (ex);
        }
+       ret = g_strdup (result->session_key);
        
        if (result->signatures)
                v(print_verify_results (result->signatures));
@@ -371,24 +389,28 @@ test_multipart_encrypted (GMimeCryptoContext *ctx, gboolean sign)
        g_object_unref (result);
        
        if (ex != NULL) {
-               g_object_unref (cleartext);
+               g_free (ret);
+               ret = 0;
                throw (ex);
        }
        
-       stream = g_mime_stream_mem_new ();
-       g_mime_object_write_to_stream (decrypted, stream);
+       test_stream = g_mime_stream_mem_new ();
+       g_mime_object_write_to_stream (decrypted, test_stream);
        
        buf[0] = GMIME_STREAM_MEM (cleartext)->buffer;
-       buf[1] = GMIME_STREAM_MEM (stream)->buffer;
+       buf[1] = GMIME_STREAM_MEM (test_stream)->buffer;
        
        if (buf[0]->len != buf[1]->len || memcmp (buf[0]->data, buf[1]->data, buf[0]->len) != 0)
                ex = exception_new ("decrypted data does not match original cleartext");
        
-       g_object_unref (cleartext);
-       g_object_unref (stream);
+       g_object_unref (test_stream);
        
-       if (ex != NULL)
+       if (ex != NULL) {
+               g_free (ret);
+               ret = 0;
                throw (ex);
+       }
+       return ret;
 }
 
 static void
@@ -420,6 +442,8 @@ int main (int argc, char *argv[])
        struct stat st;
        char *key;
        int i;
+       GMimeStream *stream = NULL, *cleartext = NULL;
+       char *session_key = NULL;
        
        g_mime_init (0);
        
@@ -470,19 +494,45 @@ int main (int argc, char *argv[])
        
        testsuite_check ("multipart/encrypted");
        try {
-               test_multipart_encrypted (ctx, FALSE);
+               create_encrypted_message (ctx, FALSE, &cleartext, &stream);
+               session_key = test_multipart_encrypted (ctx, FALSE, cleartext, stream, NULL);
+               if (testsuite_can_safely_override_session_key ())
+                       g_free (test_multipart_encrypted (ctx, FALSE, cleartext, stream, session_key));
                testsuite_check_passed ();
        } catch (ex) {
                testsuite_check_failed ("multipart/encrypted failed: %s", ex->message);
        } finally;
+       if (cleartext)
+               g_object_unref (cleartext);
+       if (stream)
+               g_object_unref (stream);
+       cleartext = stream = NULL;
+       if (session_key) {
+               memset (session_key, 0, strlen (session_key));
+               g_free (session_key);
+               session_key = NULL;
+       }
        
        testsuite_check ("multipart/encrypted+sign");
        try {
-               test_multipart_encrypted (ctx, TRUE);
+               create_encrypted_message (ctx, TRUE, &cleartext, &stream);
+               session_key = test_multipart_encrypted (ctx, TRUE, cleartext, stream, NULL);
+               if (testsuite_can_safely_override_session_key ())
+                       g_free (test_multipart_encrypted (ctx, TRUE, cleartext, stream, session_key));
                testsuite_check_passed ();
        } catch (ex) {
                testsuite_check_failed ("multipart/encrypted+sign failed: %s", ex->message);
        } finally;
+       if (cleartext)
+               g_object_unref (cleartext);
+       if (stream)
+               g_object_unref (stream);
+       cleartext = stream = NULL;
+       if (session_key) {
+               memset (session_key, 0, strlen (session_key));
+               g_free (session_key);
+               session_key = NULL;
+       }
        
        g_object_unref (ctx);
        
diff --git a/tests/testsuite.c b/tests/testsuite.c
index 68f9522..4e6cdc2 100644
--- a/tests/testsuite.c
+++ b/tests/testsuite.c
@@ -350,39 +350,79 @@ g_throw (Exception *ex)
        longjmp (env->env, 1);
 }
 
-static int
-is_gpg_modern (void)
+static const char *
+get_gpg_version (void)
 {
+       static gboolean found = FALSE;
+       static char version[1024];
        const char vheader[] = "gpg (GnuPG) ";
        char *vstring = NULL;
        size_t vlen = 0;
        FILE *vpipe;
        int ret;
        
+       if (found)
+               return version;
+       
        if ((vpipe = popen ("gpg --version", "r")) == NULL)
-               return 0;
+               return NULL;
        
        if (getline (&vstring, &vlen, vpipe) == -1) {
                free (vstring);
                pclose (vpipe);
-               return 0;
+               return NULL;
        }
        
        pclose (vpipe);
        
        if (strncmp (vstring, vheader, sizeof (vheader) - 1)) {
                free (vstring);
-               return 0;
+               return NULL;
        }
        
-       ret = (vstring[sizeof (vheader) - 1] > '2') ||
-               (vstring[sizeof (vheader) - 1] == '2' &&
-                vstring[sizeof (vheader)] == '.' &&
-                vstring[sizeof (vheader) + 1] >= '1');
-       
+       strncpy (version, vstring + sizeof (vheader) - 1, sizeof (version) - 1);
+       version[sizeof (version) - 1] = '\0';
        free (vstring);
+       found = TRUE;
        
-       return ret;
+       return version;
+}
+
+static int
+is_gpg_modern (void)
+{
+       const char *ver = get_gpg_version ();
+
+       if (!ver)
+               return 0;
+       
+       return (ver[0] > '2') ||
+               (ver[0] == '2' &&
+                ver[1] == '.' &&
+                ver[2] >= '1');
+}
+
+/* in versions of gpg before 2.1.16, the only mechanism to override
+   the session key was --override-session-key, which leaks its
+   argument to the process table.
+
+   in 2.1.16, gpg introduced --override-session-key-fd, which is what
+   gmime uses to be safe.
+*/
+
+int
+testsuite_can_safely_override_session_key (void)
+{
+       const char *ver = get_gpg_version ();
+       
+       if (!ver)
+               return 0;
+       
+       return (ver[0] > '2') ||
+               ((ver[0] == '2' && ver[1] == '.' ) &&
+                ((ver[2] > '1') ||
+                 ((ver[2] == '1' && ver[3] == '.') &&
+                  (atoi(ver+4) >= 16))));
 }
 
 int
@@ -421,7 +461,6 @@ testsuite_setup_gpghome (void)
                        return EXIT_FAILURE;
        }
        
-       
        return EXIT_SUCCESS;
 }
 
diff --git a/tests/testsuite.h b/tests/testsuite.h
index a5f9392..87d3126 100644
--- a/tests/testsuite.h
+++ b/tests/testsuite.h
@@ -48,6 +48,7 @@ int testsuite_total_errors (void);
 /* GnuPG test suite utility functions */
 int testsuite_setup_gpghome (void);
 int testsuite_destroy_gpghome (void);
+int testsuite_can_safely_override_session_key (void);
 
 /*#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
 #define G_GNUC_NORETURN __attribute__((noreturn))


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]