diff options
| author | Daniel Kahn Gillmor <dkg@fifthhorseman.net> | 2018-02-04 15:33:34 -0500 |
|---|---|---|
| committer | Daniel Kahn Gillmor <dkg@fifthhorseman.net> | 2018-02-04 15:33:34 -0500 |
| commit | d9be1028d47cb7e98b474df420858a690798810b (patch) | |
| tree | fb37f83ca098129a5301ef141dc6a5007a0972a9 /util | |
| parent | a8fb877ad7e960d69ec10887ff79e24bb99c587c (diff) | |
| parent | 3c4e64d976eb561ac5157df1bbe5882e3e65b583 (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.local | 2 | ||||
| -rw-r--r-- | util/crypto.c | 221 | ||||
| -rw-r--r-- | util/crypto.h | 37 | ||||
| -rw-r--r-- | util/error_util.c | 1 | ||||
| -rw-r--r-- | util/gmime-extra.c | 28 | ||||
| -rw-r--r-- | util/gmime-extra.h | 19 | ||||
| -rw-r--r-- | util/string-util.c | 13 | ||||
| -rw-r--r-- | util/string-util.h | 2 |
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 |
