aboutsummaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorDaniel Kahn Gillmor <dkg@fifthhorseman.net>2018-02-04 15:33:34 -0500
committerDaniel Kahn Gillmor <dkg@fifthhorseman.net>2018-02-04 15:33:34 -0500
commitd9be1028d47cb7e98b474df420858a690798810b (patch)
treefb37f83ca098129a5301ef141dc6a5007a0972a9 /util
parenta8fb877ad7e960d69ec10887ff79e24bb99c587c (diff)
parent3c4e64d976eb561ac5157df1bbe5882e3e65b583 (diff)
Merge tag 'debian/0.26-1' into debian/stretch-backports
notmuch Debian 0.26-1 upload (same as 0.26)
Diffstat (limited to 'util')
-rw-r--r--util/Makefile.local2
-rw-r--r--util/crypto.c221
-rw-r--r--util/crypto.h37
-rw-r--r--util/error_util.c1
-rw-r--r--util/gmime-extra.c28
-rw-r--r--util/gmime-extra.h19
-rw-r--r--util/string-util.c13
-rw-r--r--util/string-util.h2
8 files changed, 309 insertions, 14 deletions
diff --git a/util/Makefile.local b/util/Makefile.local
index 3027880b..ba03230e 100644
--- a/util/Makefile.local
+++ b/util/Makefile.local
@@ -5,7 +5,7 @@ extra_cflags += -I$(srcdir)/$(dir)
libnotmuch_util_c_srcs := $(dir)/xutil.c $(dir)/error_util.c $(dir)/hex-escape.c \
$(dir)/string-util.c $(dir)/talloc-extra.c $(dir)/zlib-extra.c \
- $(dir)/util.c $(dir)/gmime-extra.c
+ $(dir)/util.c $(dir)/gmime-extra.c $(dir)/crypto.c
libnotmuch_util_modules := $(libnotmuch_util_c_srcs:.c=.o)
diff --git a/util/crypto.c b/util/crypto.c
new file mode 100644
index 00000000..9d3b6dad
--- /dev/null
+++ b/util/crypto.c
@@ -0,0 +1,221 @@
+/* notmuch - Not much of an email program, (just index and search)
+ *
+ * Copyright © 2012 Jameson Rollins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see https://www.gnu.org/licenses/ .
+ *
+ * Authors: Jameson Rollins <jrollins@finestructure.net>
+ */
+
+#include "crypto.h"
+#include <strings.h>
+#define unused(x) x __attribute__ ((unused))
+
+#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
+
+#if (GMIME_MAJOR_VERSION < 3)
+/* Create or pass on a GPG context (GMime 2.6) */
+static notmuch_status_t
+get_gpg_context (_notmuch_crypto_t *crypto, GMimeCryptoContext **ctx)
+{
+ if (ctx == NULL || crypto == NULL)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ if (crypto->gpgctx) {
+ *ctx = crypto->gpgctx;
+ return NOTMUCH_STATUS_SUCCESS;
+ }
+
+ /* TODO: GMimePasswordRequestFunc */
+ crypto->gpgctx = g_mime_gpg_context_new (NULL, crypto->gpgpath ? crypto->gpgpath : "gpg");
+ if (! crypto->gpgctx) {
+ return NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION;
+ }
+
+ g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) crypto->gpgctx, true);
+ g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) crypto->gpgctx, false);
+
+ *ctx = crypto->gpgctx;
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
+/* Create or pass on a PKCS7 context (GMime 2.6) */
+static notmuch_status_t
+get_pkcs7_context (_notmuch_crypto_t *crypto, GMimeCryptoContext **ctx)
+{
+ if (ctx == NULL || crypto == NULL)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ if (crypto->pkcs7ctx) {
+ *ctx = crypto->pkcs7ctx;
+ return NOTMUCH_STATUS_SUCCESS;
+ }
+
+ /* TODO: GMimePasswordRequestFunc */
+ crypto->pkcs7ctx = g_mime_pkcs7_context_new (NULL);
+ if (! crypto->pkcs7ctx) {
+ return NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION;
+ }
+
+ g_mime_pkcs7_context_set_always_trust ((GMimePkcs7Context *) crypto->pkcs7ctx,
+ false);
+
+ *ctx = crypto->pkcs7ctx;
+ return NOTMUCH_STATUS_SUCCESS;
+}
+static const struct {
+ const char *protocol;
+ notmuch_status_t (*get_context) (_notmuch_crypto_t *crypto, GMimeCryptoContext **ctx);
+} protocols[] = {
+ {
+ .protocol = "application/pgp-signature",
+ .get_context = get_gpg_context,
+ },
+ {
+ .protocol = "application/pgp-encrypted",
+ .get_context = get_gpg_context,
+ },
+ {
+ .protocol = "application/pkcs7-signature",
+ .get_context = get_pkcs7_context,
+ },
+ {
+ .protocol = "application/x-pkcs7-signature",
+ .get_context = get_pkcs7_context,
+ },
+};
+
+/* for the specified protocol return the context pointer (initializing
+ * if needed) */
+notmuch_status_t
+_notmuch_crypto_get_gmime_ctx_for_protocol (_notmuch_crypto_t *crypto,
+ const char *protocol,
+ GMimeCryptoContext **ctx)
+{
+ if (! protocol)
+ return NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL;
+
+ /* As per RFC 1847 section 2.1: "the [protocol] value token is
+ * comprised of the type and sub-type tokens of the Content-Type".
+ * As per RFC 1521 section 2: "Content-Type values, subtypes, and
+ * parameter names as defined in this document are
+ * case-insensitive." Thus, we use strcasecmp for the protocol.
+ */
+ for (size_t i = 0; i < ARRAY_SIZE (protocols); i++) {
+ if (strcasecmp (protocol, protocols[i].protocol) == 0)
+ return protocols[i].get_context (crypto, ctx);
+ }
+
+ return NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL;
+}
+
+void
+_notmuch_crypto_cleanup (_notmuch_crypto_t *crypto)
+{
+ if (crypto->gpgctx) {
+ g_object_unref (crypto->gpgctx);
+ crypto->gpgctx = NULL;
+ }
+
+ if (crypto->pkcs7ctx) {
+ g_object_unref (crypto->pkcs7ctx);
+ crypto->pkcs7ctx = NULL;
+ }
+}
+#else
+void _notmuch_crypto_cleanup (unused(_notmuch_crypto_t *crypto))
+{
+}
+#endif
+
+GMimeObject *
+_notmuch_crypto_decrypt (bool *attempted,
+ notmuch_decryption_policy_t decrypt,
+ notmuch_message_t *message,
+ g_mime_3_unused(GMimeCryptoContext* crypto_ctx),
+ GMimeMultipartEncrypted *part,
+ GMimeDecryptResult **decrypt_result,
+ GError **err)
+{
+ GMimeObject *ret = NULL;
+ if (decrypt == NOTMUCH_DECRYPT_FALSE)
+ return NULL;
+
+ /* the versions of notmuch that can support session key decryption */
+#if HAVE_GMIME_SESSION_KEYS
+ if (message) {
+ notmuch_message_properties_t *list = NULL;
+
+ for (list = notmuch_message_get_properties (message, "session-key", TRUE);
+ notmuch_message_properties_valid (list); notmuch_message_properties_move_to_next (list)) {
+ if (err && *err) {
+ g_error_free (*err);
+ *err = NULL;
+ }
+ if (attempted)
+ *attempted = true;
+#if (GMIME_MAJOR_VERSION < 3)
+ ret = g_mime_multipart_encrypted_decrypt_session (part,
+ crypto_ctx,
+ notmuch_message_properties_value (list),
+ decrypt_result, err);
+#else
+ ret = g_mime_multipart_encrypted_decrypt (part,
+ GMIME_DECRYPT_NONE,
+ notmuch_message_properties_value (list),
+ decrypt_result, err);
+#endif
+ if (ret)
+ break;
+ }
+ if (list)
+ notmuch_message_properties_destroy (list);
+ if (ret)
+ return ret;
+ }
+#endif
+
+ if (err && *err) {
+ g_error_free (*err);
+ *err = NULL;
+ }
+
+ if (decrypt == NOTMUCH_DECRYPT_AUTO)
+ return ret;
+
+ if (attempted)
+ *attempted = true;
+#if (GMIME_MAJOR_VERSION < 3)
+#if HAVE_GMIME_SESSION_KEYS
+ gboolean oldgetsk = g_mime_crypto_context_get_retrieve_session_key (crypto_ctx);
+ gboolean newgetsk = (decrypt == NOTMUCH_DECRYPT_TRUE && decrypt_result);
+ if (newgetsk != oldgetsk)
+ /* This could return an error, but we can't do anything about it, so ignore it */
+ g_mime_crypto_context_set_retrieve_session_key (crypto_ctx, newgetsk, NULL);
+#endif
+ ret = g_mime_multipart_encrypted_decrypt(part, crypto_ctx,
+ decrypt_result, err);
+#if HAVE_GMIME_SESSION_KEYS
+ if (newgetsk != oldgetsk)
+ g_mime_crypto_context_set_retrieve_session_key (crypto_ctx, oldgetsk, NULL);
+#endif
+#else
+ GMimeDecryptFlags flags = GMIME_DECRYPT_NONE;
+ if (decrypt == NOTMUCH_DECRYPT_TRUE && decrypt_result)
+ flags |= GMIME_DECRYPT_EXPORT_SESSION_KEY;
+ ret = g_mime_multipart_encrypted_decrypt(part, flags, NULL,
+ decrypt_result, err);
+#endif
+ return ret;
+}
diff --git a/util/crypto.h b/util/crypto.h
new file mode 100644
index 00000000..c384601c
--- /dev/null
+++ b/util/crypto.h
@@ -0,0 +1,37 @@
+#ifndef _CRYPTO_H
+#define _CRYPTO_H
+
+#include <stdbool.h>
+#include "gmime-extra.h"
+#include "notmuch.h"
+
+typedef struct _notmuch_crypto {
+ bool verify;
+ notmuch_decryption_policy_t decrypt;
+#if (GMIME_MAJOR_VERSION < 3)
+ GMimeCryptoContext* gpgctx;
+ GMimeCryptoContext* pkcs7ctx;
+ const char *gpgpath;
+#endif
+} _notmuch_crypto_t;
+
+GMimeObject *
+_notmuch_crypto_decrypt (bool *attempted,
+ notmuch_decryption_policy_t decrypt,
+ notmuch_message_t *message,
+ GMimeCryptoContext* crypto_ctx,
+ GMimeMultipartEncrypted *part,
+ GMimeDecryptResult **decrypt_result,
+ GError **err);
+
+#if (GMIME_MAJOR_VERSION < 3)
+notmuch_status_t
+_notmuch_crypto_get_gmime_ctx_for_protocol (_notmuch_crypto_t *crypto,
+ const char *protocol,
+ GMimeCryptoContext **ctx);
+#endif
+
+void
+_notmuch_crypto_cleanup (_notmuch_crypto_t *crypto);
+
+#endif
diff --git a/util/error_util.c b/util/error_util.c
index 778bbd52..e64162c7 100644
--- a/util/error_util.c
+++ b/util/error_util.c
@@ -34,6 +34,7 @@ _internal_error (const char *format, ...)
fprintf (stderr, "Internal error: ");
vfprintf (stderr, format, va_args);
+ va_end (va_args);
exit (1);
}
diff --git a/util/gmime-extra.c b/util/gmime-extra.c
index 901d4d56..bc1e3c4d 100644
--- a/util/gmime-extra.c
+++ b/util/gmime-extra.c
@@ -33,6 +33,21 @@ g_string_talloc_strdup (void *ctx, char *g_string)
#if (GMIME_MAJOR_VERSION < 3)
+const char *
+g_mime_certificate_get_valid_userid (GMimeCertificate *cert)
+{
+ /* output user id only if validity is FULL or ULTIMATE. */
+ /* note that gmime 2.6 is using the term "trust" here, which
+ * is WRONG. It's actually user id "validity". */
+ const char *name = g_mime_certificate_get_name (cert);
+ if (name == NULL)
+ return name;
+ GMimeCertificateTrust trust = g_mime_certificate_get_trust (cert);
+ if (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE)
+ return name;
+ return NULL;
+}
+
char *
g_mime_message_get_address_string (GMimeMessage *message, GMimeRecipientType type)
{
@@ -107,6 +122,19 @@ g_mime_utils_header_decode_date_unix (const char *date) {
#else /* GMime >= 3.0 */
+const char *
+g_mime_certificate_get_valid_userid (GMimeCertificate *cert)
+{
+ /* output user id only if validity is FULL or ULTIMATE. */
+ const char *uid = g_mime_certificate_get_user_id (cert);
+ if (uid == NULL)
+ return uid;
+ GMimeValidity validity = g_mime_certificate_get_id_validity (cert);
+ if (validity == GMIME_VALIDITY_FULL || validity == GMIME_VALIDITY_ULTIMATE)
+ return uid;
+ return NULL;
+}
+
const char*
g_mime_certificate_get_fpr16 (GMimeCertificate *cert) {
const char *fpr = g_mime_certificate_get_fingerprint (cert);
diff --git a/util/gmime-extra.h b/util/gmime-extra.h
index de275bc1..ca822b8c 100644
--- a/util/gmime-extra.h
+++ b/util/gmime-extra.h
@@ -16,12 +16,9 @@ GMimeStream *g_mime_stream_stdout_new(void);
#define g_mime_2_6_unref(obj) g_object_unref (obj)
#define g_mime_3_unused(arg) arg
#define g_mime_certificate_get_fpr16(cert) g_mime_certificate_get_key_id (cert)
-#define g_mime_certificate_get_uid(cert) g_mime_certificate_get_name (cert);
#else /* GMime >= 3.0 */
-typedef GMimeAddressType GMimeRecipientType;
#define GMIME_ENABLE_RFC_2047_WORKAROUNDS 0xdeadbeef
-#define g_mime_certificate_get_uid(cert) g_mime_certificate_get_key_id (cert);
#define g_mime_content_type_to_string(c) g_mime_content_type_get_mime_type (c)
#define g_mime_filter_crlf_new(encode,dots) g_mime_filter_dos2unix_new (FALSE)
#define g_mime_gpg_context_new(func,path) g_mime_gpg_context_new ()
@@ -30,7 +27,6 @@ typedef GMimeAddressType GMimeRecipientType;
#define g_mime_init(flags) g_mime_init()
#define g_mime_message_add_recipient(m,t,n,a) g_mime_message_add_mailbox (m,t,n,a)
#define g_mime_message_set_subject(m,s) g_mime_message_set_subject(m,s,NULL)
-#define g_mime_multipart_encrypted_decrypt(mpe,ctx,out,err) g_mime_multipart_encrypted_decrypt(mpe, GMIME_DECRYPT_NONE, NULL, out, err)
#define g_mime_multipart_signed_verify(mps,ctx,err) g_mime_multipart_signed_verify(mps, GMIME_ENCRYPT_NONE, err)
#define g_mime_object_write_to_stream(o,s) g_mime_object_write_to_stream (o,NULL,s)
#define g_mime_object_set_header(o,h,v) g_mime_object_set_header (o,h,v,NULL)
@@ -47,15 +43,6 @@ typedef GMimeAddressType GMimeRecipientType;
typedef GMimeSignatureStatus GMimeSignatureError;
-typedef GMimeTrust GMimeCertificateTrust;
-
-#define GMIME_CERTIFICATE_TRUST_UNKNOWN GMIME_TRUST_UNKNOWN
-#define GMIME_CERTIFICATE_TRUST_UNDEFINED GMIME_TRUST_UNDEFINED
-#define GMIME_CERTIFICATE_TRUST_NEVER GMIME_TRUST_NEVER
-#define GMIME_CERTIFICATE_TRUST_MARGINAL GMIME_TRUST_MARGINAL
-#define GMIME_CERTIFICATE_TRUST_FULLY GMIME_TRUST_FULL
-#define GMIME_CERTIFICATE_TRUST_ULTIMATE GMIME_TRUST_ULTIMATE
-
#define g_mime_2_6_unref(obj) /*ignore*/
#define g_mime_3_unused(arg) unused(arg)
#endif
@@ -107,4 +94,10 @@ gboolean g_mime_signature_status_bad (GMimeSignatureStatus status);
gboolean g_mime_signature_status_error (GMimeSignatureError status);
gint64 g_mime_utils_header_decode_date_unix (const char *date);
+
+/**
+ * Return string for valid User ID (or NULL if no valid User ID exists)
+ */
+const char * g_mime_certificate_get_valid_userid (GMimeCertificate *cert);
+
#endif
diff --git a/util/string-util.c b/util/string-util.c
index 18125309..b0108811 100644
--- a/util/string-util.c
+++ b/util/string-util.c
@@ -255,3 +255,16 @@ strcase_hash (const void *ptr)
return hash;
}
+
+void
+strip_trailing (char *str, char ch)
+{
+ int i;
+
+ for (i = strlen (str) - 1; i >= 0; i--) {
+ if (str[i] == ch)
+ str[i] = '\0';
+ else
+ break;
+ }
+}
diff --git a/util/string-util.h b/util/string-util.h
index 87917b8f..97770614 100644
--- a/util/string-util.h
+++ b/util/string-util.h
@@ -75,6 +75,8 @@ int strcase_equal (const void *a, const void *b);
/* GLib GHashFunc compatible case insensitive hash function */
unsigned int strcase_hash (const void *ptr);
+void strip_trailing (char *str, char ch);
+
#ifdef __cplusplus
}
#endif