From: Kevin McCarthy Date: Sun, 21 Jul 2019 22:15:06 +0000 (-0700) Subject: Autocrypt outgoing emails X-Git-Tag: 2019-10-25~97^2~38 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b79635768f2689ceef68e4b99b557bde484df2ba;p=neomutt Autocrypt outgoing emails Change crypt_get_keys() to query autocrypt. When oppenc_mode is set, we still query the original keyring regardless, because the compose menu can still run oppenc even if autocrypt is on. Since mutt_autocrypt_ui_recommendation() checks each key as part of making the recommendation, add a keylist parameter and use that function. Add gpgme changes to use the autocrypt context for encryption. Postpone work: * Change mutt_protect() to have a postpone parameter. Remove the manual toggling of the SEC_SIGN bit outside the call when postponing. * Since autocrypt doesn't set the SEC_SIGN bit, this allows us to turn off signing inside mutt_protect() for both normal and autocrypt mode. * Set the autocrypt postpone key in AutocryptDefaultKey. Write autocrypt and gossip headers in outgoing emails. Co-authored-by: Richard Russon --- diff --git a/autocrypt/autocrypt.c b/autocrypt/autocrypt.c index cf5d7c193..9aab282bb 100644 --- a/autocrypt/autocrypt.c +++ b/autocrypt/autocrypt.c @@ -450,13 +450,19 @@ cleanup: return rv; } -enum AutocryptRec mutt_autocrypt_ui_recommendation(struct Email *hdr) +/* Returns the recommendation. If the recommendataion is > NO and + * keylist is not NULL, keylist will be populated with the autocrypt + * keyids + */ +enum AutocryptRec mutt_autocrypt_ui_recommendation(struct Email *hdr, char **keylist) { enum AutocryptRec rv = AUTOCRYPT_REC_OFF; struct AutocryptAccount *account = NULL; struct AutocryptPeer *peer = NULL; struct Address *recip = NULL; int all_encrypt = 1, has_discourage = 0; + struct Buffer *keylist_buf = NULL; + const char *matching_key; if (!C_Autocrypt || mutt_autocrypt_init(0) || !hdr) return AUTOCRYPT_REC_OFF; @@ -471,6 +477,9 @@ enum AutocryptRec mutt_autocrypt_ui_recommendation(struct Email *hdr) if (mutt_autocrypt_db_account_get(from, &account) <= 0) goto cleanup; + keylist_buf = mutt_buffer_pool_get(); + mutt_buffer_addstr(keylist_buf, account->keyid); + struct AddressList recips = TAILQ_HEAD_INITIALIZER(recips); mutt_addrlist_copy(&recips, &hdr->env->to, false); @@ -488,6 +497,8 @@ enum AutocryptRec mutt_autocrypt_ui_recommendation(struct Email *hdr) if (mutt_autocrypt_gpgme_is_valid_key(peer->keyid)) { + matching_key = peer->keyid; + if (!(peer->last_seen && peer->autocrypt_timestamp) || (peer->last_seen - peer->autocrypt_timestamp > 35 * 24 * 60 * 60)) { @@ -500,12 +511,18 @@ enum AutocryptRec mutt_autocrypt_ui_recommendation(struct Email *hdr) } else if (mutt_autocrypt_gpgme_is_valid_key(peer->gossip_keyid)) { + matching_key = peer->gossip_keyid; + has_discourage = 1; all_encrypt = 0; } else goto cleanup; + if (mutt_buffer_len(keylist_buf)) + mutt_buffer_addch(keylist_buf, ' '); + mutt_buffer_addstr(keylist_buf, matching_key); + mutt_autocrypt_db_peer_free(&peer); } @@ -516,9 +533,191 @@ enum AutocryptRec mutt_autocrypt_ui_recommendation(struct Email *hdr) else rv = AUTOCRYPT_REC_AVAILABLE; + if (keylist) + mutt_str_replace(keylist, mutt_b2s(keylist_buf)); + +cleanup: + mutt_autocrypt_db_account_free(&account); + mutt_addrlist_clear(&recips); + mutt_autocrypt_db_peer_free(&peer); + mutt_buffer_pool_release(&keylist_buf); + return rv; +} + +int mutt_autocrypt_set_sign_as_default_key(struct Email *hdr) +{ + int rv = -1; + struct AutocryptAccount *account = NULL; + + if (!C_Autocrypt || mutt_autocrypt_init(0) || !hdr) + return -1; + + struct Address *from = TAILQ_FIRST(&hdr->env->from); + if (!from || TAILQ_NEXT(from, entries)) + return -1; + + if (mutt_autocrypt_db_account_get(from, &account) <= 0) + goto cleanup; + if (!account->keyid) + goto cleanup; + + mutt_str_replace(&AutocryptSignAs, account->keyid); + mutt_str_replace(&AutocryptDefaultKey, account->keyid); + + rv = 0; + +cleanup: + mutt_autocrypt_db_account_free(&account); + return rv; +} + +static void write_autocrypt_header_line(FILE *fp, const char *addr, + int prefer_encrypt, const char *keydata) +{ + int count = 0; + + fprintf(fp, "addr=%s; ", addr); + if (prefer_encrypt) + fputs("prefer-encrypt=mutual; ", fp); + fputs("keydata=\n", fp); + + while (*keydata) + { + count = 0; + fputs("\t", fp); + while (*keydata && count < 75) + { + fputc(*keydata, fp); + count++; + keydata++; + } + fputs("\n", fp); + } +} + +int mutt_autocrypt_write_autocrypt_header(struct Envelope *env, FILE *fp) +{ + int rv = -1; + struct AutocryptAccount *account = NULL; + + if (!C_Autocrypt || mutt_autocrypt_init(0) || !env) + return -1; + + struct Address *from = TAILQ_FIRST(&env->from); + if (!from || TAILQ_NEXT(from, entries)) + return -1; + + if (mutt_autocrypt_db_account_get(from, &account) <= 0) + goto cleanup; + if (!account->keydata) + goto cleanup; + + fputs("Autocrypt: ", fp); + write_autocrypt_header_line(fp, account->email_addr, account->prefer_encrypt, + account->keydata); + + rv = 0; + cleanup: mutt_autocrypt_db_account_free(&account); + return rv; +} + +int mutt_autocrypt_write_gossip_headers(struct Envelope *env, FILE *fp) +{ + struct AutocryptHeader *gossip; + + if (!C_Autocrypt || mutt_autocrypt_init(0) || !env) + return -1; + + for (gossip = env->autocrypt_gossip; gossip; gossip = gossip->next) + { + fputs("Autocrypt-Gossip: ", fp); + write_autocrypt_header_line(fp, gossip->addr, 0, gossip->keydata); + } + + return 0; +} + +int mutt_autocrypt_generate_gossip_list(struct Email *hdr) +{ + int rv = -1; + struct AutocryptPeer *peer = NULL; + struct AutocryptAccount *account = NULL; + struct Address *recip = NULL; + struct AutocryptHeader *gossip; + const char *keydata, *addr; + struct Envelope *mime_headers; + + if (!C_Autocrypt || mutt_autocrypt_init(0) || !hdr) + return -1; + + mime_headers = hdr->content->mime_headers; + if (!mime_headers) + mime_headers = hdr->content->mime_headers = mutt_env_new(); + mutt_free_autocrypthdr(&mime_headers->autocrypt_gossip); + + struct AddressList recips = TAILQ_HEAD_INITIALIZER(recips); + + mutt_addrlist_copy(&recips, &hdr->env->to, false); + mutt_addrlist_copy(&recips, &hdr->env->cc, false); + + TAILQ_FOREACH(recip, &recips, entries) + { + /* At this point, we just accept missing keys and include what + * we can. */ + if (mutt_autocrypt_db_peer_get(recip, &peer) <= 0) + continue; + + keydata = NULL; + if (mutt_autocrypt_gpgme_is_valid_key(peer->keyid)) + keydata = peer->keydata; + else if (mutt_autocrypt_gpgme_is_valid_key(peer->gossip_keyid)) + keydata = peer->gossip_keydata; + + if (keydata) + { + gossip = mutt_new_autocrypthdr(); + gossip->addr = mutt_str_strdup(peer->email_addr); + gossip->keydata = mutt_str_strdup(keydata); + gossip->next = mime_headers->autocrypt_gossip; + mime_headers->autocrypt_gossip = gossip; + } + + mutt_autocrypt_db_peer_free(&peer); + } + + TAILQ_FOREACH(recip, &hdr->env->reply_to, entries) + { + addr = keydata = NULL; + if (mutt_autocrypt_db_account_get(recip, &account) > 0) + { + addr = account->email_addr; + keydata = account->keydata; + } + else if (mutt_autocrypt_db_peer_get(recip, &peer) > 0) + { + addr = peer->email_addr; + if (mutt_autocrypt_gpgme_is_valid_key(peer->keyid)) + keydata = peer->keydata; + else if (mutt_autocrypt_gpgme_is_valid_key(peer->gossip_keyid)) + keydata = peer->gossip_keydata; + } + + if (keydata) + { + gossip = mutt_new_autocrypthdr(); + gossip->addr = mutt_str_strdup(addr); + gossip->keydata = mutt_str_strdup(keydata); + gossip->next = mime_headers->autocrypt_gossip; + mime_headers->autocrypt_gossip = gossip; + } + mutt_autocrypt_db_account_free(&account); + mutt_autocrypt_db_peer_free(&peer); + } + mutt_addrlist_clear(&recips); + mutt_autocrypt_db_account_free(&account); mutt_autocrypt_db_peer_free(&peer); return rv; } diff --git a/autocrypt/autocrypt.h b/autocrypt/autocrypt.h index d3f10fa4f..95a77305a 100644 --- a/autocrypt/autocrypt.h +++ b/autocrypt/autocrypt.h @@ -80,6 +80,10 @@ int mutt_autocrypt_init (int); void mutt_autocrypt_cleanup (void); int mutt_autocrypt_process_autocrypt_header (struct Email *hdr, struct Envelope *env); int mutt_autocrypt_process_gossip_header (struct Email *hdr, struct Envelope *env); -enum AutocryptRec mutt_autocrypt_ui_recommendation (struct Email *hdr); +enum AutocryptRec mutt_autocrypt_ui_recommendation (struct Email *hdr, char **keylist); +int mutt_autocrypt_set_sign_as_default_key (struct Email *hdr); +int mutt_autocrypt_write_autocrypt_header (struct Envelope *env, FILE *fp); +int mutt_autocrypt_write_gossip_headers (struct Envelope *env, FILE *fp); +int mutt_autocrypt_generate_gossip_list (struct Email *hdr); #endif /* MUTT_AUTOCRYPT_AUTOCRYPT_H */ diff --git a/compose.c b/compose.c index fc4d5dc4d..79caa1056 100644 --- a/compose.c +++ b/compose.c @@ -337,7 +337,7 @@ static void autocrypt_compose_menu(struct Email *e) { case 1: e->security |= (SEC_AUTOCRYPT | SEC_AUTOCRYPT_OVERRIDE); - e->security &= ~(SEC_ENCRYPT | SEC_SIGN | SEC_OPPENCRYPT); + e->security &= ~(SEC_ENCRYPT | SEC_SIGN | SEC_OPPENCRYPT | SEC_INLINE); break; case 2: e->security &= ~SEC_AUTOCRYPT; @@ -444,24 +444,27 @@ static void redraw_crypt_lines(struct ComposeRedrawData *rd) #ifdef USE_AUTOCRYPT mutt_window_move(MuttIndexWindow, HDR_AUTOCRYPT, 0); mutt_window_clrtoeol(MuttIndexWindow); - SET_COLOR(MT_COLOR_COMPOSE_HEADER); - printw("%*s", HeaderPadding[HDR_AUTOCRYPT], _(Prompts[HDR_AUTOCRYPT])); - NORMAL_COLOR; - if (C_Autocrypt && (e->security & SEC_AUTOCRYPT)) - { - SET_COLOR(MT_COLOR_COMPOSE_SECURITY_ENCRYPT); - addstr(_("Encrypt")); - } - else + if (C_Autocrypt) { - SET_COLOR(MT_COLOR_COMPOSE_SECURITY_NONE); - addstr(_("Off")); - } + SET_COLOR(MT_COLOR_COMPOSE_HEADER); + printw("%*s", HeaderPadding[HDR_AUTOCRYPT], _(Prompts[HDR_AUTOCRYPT])); + NORMAL_COLOR; + if (e->security & SEC_AUTOCRYPT) + { + SET_COLOR(MT_COLOR_COMPOSE_SECURITY_ENCRYPT); + addstr(_("Encrypt")); + } + else + { + SET_COLOR(MT_COLOR_COMPOSE_SECURITY_NONE); + addstr(_("Off")); + } - SET_COLOR(MT_COLOR_COMPOSE_HEADER); - mutt_window_mvprintw(MuttIndexWindow, HDR_AUTOCRYPT, 40, "%s", _("Recommendation: ")); - NORMAL_COLOR; - printw("%s", _(AutocryptRecUiFlags[rd->autocrypt_rec])); + SET_COLOR(MT_COLOR_COMPOSE_HEADER); + mutt_window_mvprintw(MuttIndexWindow, HDR_AUTOCRYPT, 40, "%s", _("Recommendation: ")); + NORMAL_COLOR; + printw("%s", _(AutocryptRecUiFlags[rd->autocrypt_rec])); + } #endif } @@ -473,28 +476,29 @@ static void update_crypt_info(struct ComposeRedrawData *rd) crypt_opportunistic_encrypt(e); #ifdef USE_AUTOCRYPT - rd->autocrypt_rec = mutt_autocrypt_ui_recommendation(e); - - /* Anything that enables SEC_ENCRYPT or SEC_SIGN, or turns on SMIME - * overrides autocrypt, be it oppenc or the user having turned on - * those flags manually. */ - if (e->security & (SEC_ENCRYPT | SEC_SIGN | APPLICATION_SMIME)) - e->security &= ~(SEC_AUTOCRYPT | SEC_AUTOCRYPT_OVERRIDE); - else + if (C_Autocrypt) { - if (!(e->security & SEC_AUTOCRYPT_OVERRIDE)) + rd->autocrypt_rec = mutt_autocrypt_ui_recommendation(e, NULL); + + /* Anything that enables SEC_ENCRYPT or SEC_SIGN, or turns on SMIME + * overrides autocrypt, be it oppenc or the user having turned on + * those flags manually. */ + if (e->security & (SEC_ENCRYPT | SEC_SIGN | APPLICATION_SMIME)) + e->security &= ~(SEC_AUTOCRYPT | SEC_AUTOCRYPT_OVERRIDE); + else { - if (rd->autocrypt_rec == AUTOCRYPT_REC_YES) - e->security |= SEC_AUTOCRYPT; - else - e->security &= ~SEC_AUTOCRYPT; + if (!(e->security & SEC_AUTOCRYPT_OVERRIDE)) + { + if (rd->autocrypt_rec == AUTOCRYPT_REC_YES) + { + e->security |= SEC_AUTOCRYPT; + e->security &= ~SEC_INLINE; + } + else + e->security &= ~SEC_AUTOCRYPT; + } } } - /* TODO: - * - autocrypt menu for manually enabling/disabling (turns on override) - * - deal with pgp and smime menu and their effects on security->SEC_AUTOCRYPT - * when encryption or signing is enabled or if switch to smime mode - */ #endif redraw_crypt_lines(rd); @@ -2134,6 +2138,9 @@ int mutt_compose_menu(struct Email *e, char *fcc, size_t fcclen, struct Email *e #endif #ifdef USE_AUTOCRYPT case OP_COMPOSE_AUTOCRYPT_MENU: + if (!C_Autocrypt) + break; + if ((WithCrypto & APPLICATION_SMIME) && (e->security & APPLICATION_SMIME)) { if (e->security & (SEC_ENCRYPT | SEC_SIGN)) @@ -2157,6 +2164,15 @@ int mutt_compose_menu(struct Email *e, char *fcc, size_t fcclen, struct Email *e } } +#ifdef USE_AUTOCRYPT + /* This is a fail-safe to make sure the bit isn't somehow turned + * on. The user could have disabled the option after setting SEC_AUTOCRYPT, + * or perhaps resuming or replying to an autocrypt message. + */ + if (!C_Autocrypt) + e->security &= ~SEC_AUTOCRYPT; +#endif + mutt_menu_pop_current(menu); mutt_menu_destroy(&menu); diff --git a/globals.h b/globals.h index e31bafa4b..13d3aa5e7 100644 --- a/globals.h +++ b/globals.h @@ -98,6 +98,8 @@ WHERE char *C_AttributionLocale; ///< Config: Locale for dates in th WHERE char *C_AttachFormat; ///< Config: printf-like format string for the attachment menu #ifdef USE_AUTOCRYPT WHERE char *C_AutocryptDir; +WHERE char *AutocryptSignAs; /* This is used in ncrypt/crypt_gpgme.c */ +WHERE char *AutocryptDefaultKey; /* Used for postponing messages */ #endif WHERE char *C_ConfigCharset; ///< Config: Character set that the config files are in WHERE char *C_CryptProtectedHeadersSubject; ///< Config: Use this as the subject for encrypted emails diff --git a/ncrypt/crypt.c b/ncrypt/crypt.c index 7fc8e1752..9e367fc3d 100644 --- a/ncrypt/crypt.c +++ b/ncrypt/crypt.c @@ -58,6 +58,9 @@ #include "send.h" #include "sendlib.h" #include "state.h" +#ifdef USE_AUTOCRYPT +#include "autocrypt/autocrypt.h" +#endif struct Mailbox; @@ -164,24 +167,35 @@ bool crypt_valid_passphrase(SecurityFlags flags) * @param keylist List of keys to encrypt to (space-separated) * @retval 0 Success * @retval -1 Error + * + * In postpone mode, signing is automatically disabled. */ -int mutt_protect(struct Email *e, char *keylist) +int mutt_protect(struct Email *e, char *keylist, int postpone) { struct Body *pbody = NULL, *tmp_pbody = NULL; struct Body *tmp_smime_pbody = NULL; struct Body *tmp_pgp_pbody = NULL; - int flags = (WithCrypto & APPLICATION_PGP) ? e->security : 0; + int security, sign, has_retainable_sig = 0; if (!WithCrypto) return -1; - if (!(e->security & (SEC_ENCRYPT | SEC_SIGN))) + security = e->security; + sign = security & (SEC_AUTOCRYPT | SEC_SIGN); + if (postpone) + { + sign = 0; + security &= ~SEC_SIGN; + } + + if (!(security & (SEC_ENCRYPT | SEC_AUTOCRYPT)) && !sign) return 0; - if ((e->security & SEC_SIGN) && !crypt_valid_passphrase(e->security)) + if (sign && !(security & SEC_AUTOCRYPT) && !crypt_valid_passphrase(security)) return -1; - if (((WithCrypto & APPLICATION_PGP) != 0) && ((e->security & PGP_INLINE) == PGP_INLINE)) + if ((WithCrypto & APPLICATION_PGP) && !(security & SEC_AUTOCRYPT) && + ((security & PGP_INLINE) == PGP_INLINE)) { if ((e->content->type != TYPE_TEXT) || (mutt_str_strcasecmp(e->content->subtype, "plain") != 0)) @@ -214,7 +228,7 @@ int mutt_protect(struct Email *e, char *keylist) mutt_endwin(); puts(_("Invoking PGP...")); } - pbody = crypt_pgp_traditional_encryptsign(e->content, flags, keylist); + pbody = crypt_pgp_traditional_encryptsign(e->content, security, keylist); if (pbody) { e->content = pbody; @@ -242,7 +256,7 @@ int mutt_protect(struct Email *e, char *keylist) if (WithCrypto & APPLICATION_PGP) tmp_pgp_pbody = e->content; - if (C_CryptUsePka && (e->security & SEC_SIGN)) + if (C_CryptUsePka && sign) { /* Set sender (necessary for e.g. PKA). */ const char *mailbox = NULL; @@ -259,9 +273,9 @@ int mutt_protect(struct Email *e, char *keylist) if (!mailbox && C_EnvelopeFromAddress) mailbox = C_EnvelopeFromAddress->mailbox; - if (((WithCrypto & APPLICATION_SMIME) != 0) && (e->security & APPLICATION_SMIME)) + if (((WithCrypto & APPLICATION_SMIME) != 0) && (security & APPLICATION_SMIME)) crypt_smime_set_sender(mailbox); - else if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & APPLICATION_PGP)) + else if (((WithCrypto & APPLICATION_PGP) != 0) && (security & APPLICATION_PGP)) crypt_pgp_set_sender(mailbox); if (free_from) @@ -280,9 +294,27 @@ int mutt_protect(struct Email *e, char *keylist) e->content->mime_headers = protected_headers; } - if (e->security & SEC_SIGN) + /* A note about e->content->mime_headers. If postpone or send + * fails, the mime_headers is cleared out before returning to the + * compose menu. So despite the "robustness" code above and in the + * gen_gossip_list function below, mime_headers will not be set when + * entering mutt_protect(). + * + * This is important to note because the user could toggle + * $crypt_protected_headers_write or $autocrypt off back in the + * compose menu. We don't want mutt_write_rfc822_header() to write + * stale data from one option if the other is set. + */ +#ifdef USE_AUTOCRYPT + if (C_Autocrypt && !postpone && (security & SEC_AUTOCRYPT)) { - if (((WithCrypto & APPLICATION_SMIME) != 0) && (e->security & APPLICATION_SMIME)) + mutt_autocrypt_generate_gossip_list(e); + } +#endif + + if (sign) + { + if (((WithCrypto & APPLICATION_SMIME) != 0) && (security & APPLICATION_SMIME)) { tmp_pbody = crypt_smime_sign_message(e->content); if (!tmp_pbody) @@ -291,27 +323,28 @@ int mutt_protect(struct Email *e, char *keylist) tmp_smime_pbody = tmp_pbody; } - if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & APPLICATION_PGP) && - (!(flags & SEC_ENCRYPT) || C_PgpRetainableSigs)) + if (((WithCrypto & APPLICATION_PGP) != 0) && (security & APPLICATION_PGP) && + (!(security & (SEC_ENCRYPT | SEC_AUTOCRYPT)) || C_PgpRetainableSigs)) { tmp_pbody = crypt_pgp_sign_message(e->content); if (!tmp_pbody) goto bail; - flags &= ~SEC_SIGN; + has_retainable_sig = 1; + sign = 0; pbody = tmp_pbody; tmp_pgp_pbody = tmp_pbody; } - if ((WithCrypto != 0) && (e->security & APPLICATION_SMIME) && (e->security & APPLICATION_PGP)) + if ((WithCrypto != 0) && (security & APPLICATION_SMIME) && (security & APPLICATION_PGP)) { /* here comes the draft ;-) */ } } - if (e->security & SEC_ENCRYPT) + if (security & (SEC_ENCRYPT | SEC_AUTOCRYPT)) { - if (((WithCrypto & APPLICATION_SMIME) != 0) && (e->security & APPLICATION_SMIME)) + if (((WithCrypto & APPLICATION_SMIME) != 0) && (security & APPLICATION_SMIME)) { tmp_pbody = crypt_smime_build_smime_entity(tmp_smime_pbody, keylist); if (!tmp_pbody) @@ -331,13 +364,13 @@ int mutt_protect(struct Email *e, char *keylist) pbody = tmp_pbody; } - if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & APPLICATION_PGP)) + if (((WithCrypto & APPLICATION_PGP) != 0) && (security & APPLICATION_PGP)) { - pbody = crypt_pgp_encrypt_message(tmp_pgp_pbody, keylist, (flags & SEC_SIGN)); + pbody = crypt_pgp_encrypt_message(e, tmp_pgp_pbody, keylist, sign); if (!pbody) { /* did we perform a retainable signature? */ - if (flags != e->security) + if (has_retainable_sig) { /* remove the outer multipart layer */ tmp_pgp_pbody = mutt_remove_multipart(tmp_pgp_pbody); @@ -352,7 +385,7 @@ int mutt_protect(struct Email *e, char *keylist) * signatures. */ - if (flags != e->security) + if (has_retainable_sig) { tmp_pgp_pbody = mutt_remove_multipart(tmp_pgp_pbody); mutt_body_free(&tmp_pgp_pbody->next); @@ -925,6 +958,17 @@ int crypt_get_keys(struct Email *e, char **keylist, bool oppenc_mode) /* Do a quick check to make sure that we can find all of the encryption * keys if the user has requested this service. */ + *keylist = NULL; + +#ifdef USE_AUTOCRYPT + if (!oppenc_mode && (e->security & SEC_AUTOCRYPT)) + { + if (mutt_autocrypt_ui_recommendation(e, keylist) <= AUTOCRYPT_REC_NO) + return (-1); + return (0); + } +#endif + if (WithCrypto & APPLICATION_PGP) OptPgpCheckTrust = true; @@ -934,8 +978,6 @@ int crypt_get_keys(struct Email *e, char **keylist, bool oppenc_mode) mutt_addrlist_qualify(&addrlist, fqdn); mutt_addrlist_dedupe(&addrlist); - *keylist = NULL; - if (oppenc_mode || (e->security & SEC_ENCRYPT)) { if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & APPLICATION_PGP)) diff --git a/ncrypt/crypt_gpgme.c b/ncrypt/crypt_gpgme.c index af7e9f163..aab9ea3f7 100644 --- a/ncrypt/crypt_gpgme.c +++ b/ncrypt/crypt_gpgme.c @@ -719,6 +719,12 @@ static gpgme_ctx_t create_gpgme_context(bool for_smime) gpgme_ctx_t ctx; err = gpgme_new(&ctx); + +#ifdef USE_AUTOCRYPT + if (!err && OptAutocryptGpgme) + err = gpgme_ctx_set_engine_info(ctx, GPGME_PROTOCOL_OpenPGP, NULL, C_AutocryptDir); +#endif + if (err != 0) { mutt_error(_("error creating gpgme context: %s"), gpgme_strerror(err)); @@ -1116,13 +1122,22 @@ static gpgme_key_t *create_recipient_set(const char *keylist, bool use_smime) */ static int set_signer(gpgme_ctx_t ctx, bool for_smime) { - char *signid = for_smime ? C_SmimeDefaultKey : C_PgpSignAs; + char *signid = NULL; gpgme_error_t err; gpgme_ctx_t listctx; gpgme_key_t key, key2; char *fpr = NULL, *fpr2 = NULL; - if (!signid || !*signid) + if (for_smime) + signid = C_SmimeSignAs ? C_SmimeSignAs : C_SmimeDefaultKey; +#ifdef USE_AUTOCRYPT + else if (OptAutocryptGpgme) + signid = AutocryptSignAs; +#endif + else + signid = C_PgpSignAs ? C_PgpSignAs : C_PgpDefaultKey; + + if (!signid) return 0; listctx = create_gpgme_context(for_smime); diff --git a/ncrypt/cryptglue.c b/ncrypt/cryptglue.c index 09d0da2e6..c7e63eb25 100644 --- a/ncrypt/cryptglue.c +++ b/ncrypt/cryptglue.c @@ -42,6 +42,10 @@ #include "crypt_mod.h" #include "curs_lib.h" #include "ncrypt.h" +#include "options.h" +#ifdef USE_AUTOCRYPT +#include "autocrypt/autocrypt.h" +#endif struct Address; struct AddressList; @@ -62,6 +66,7 @@ extern struct CryptModuleSpecs CryptModSmimeClassic; #endif #ifdef CRYPT_BACKEND_GPGME +#include "ncrypt/crypt_gpgme.h" extern struct CryptModuleSpecs CryptModPgpGpgme; extern struct CryptModuleSpecs CryptModSmimeGpgme; #endif @@ -291,8 +296,25 @@ struct Body *crypt_pgp_sign_message(struct Body *a) /** * crypt_pgp_encrypt_message - Wrapper for CryptModuleSpecs::pgp_encrypt_message() */ -struct Body *crypt_pgp_encrypt_message(struct Body *a, char *keylist, bool sign) +struct Body *crypt_pgp_encrypt_message(struct Email *msg, struct Body *a, + char *keylist, int sign) { +#ifdef USE_AUTOCRYPT + struct Body *result; + + if (msg->security & SEC_AUTOCRYPT) + { + if (mutt_autocrypt_set_sign_as_default_key(msg)) + return NULL; + + OptAutocryptGpgme = true; + result = pgp_gpgme_encrypt_message(a, keylist, sign); + OptAutocryptGpgme = false; + + return result; + } +#endif + if (CRYPT_MOD_CALL_CHECK(PGP, pgp_encrypt_message)) return CRYPT_MOD_CALL(PGP, pgp_encrypt_message)(a, keylist, sign); diff --git a/ncrypt/cryptglue.h b/ncrypt/cryptglue.h index 193fea9c9..70257c417 100644 --- a/ncrypt/cryptglue.h +++ b/ncrypt/cryptglue.h @@ -30,7 +30,7 @@ struct AddressList; struct Body; struct State; -struct Body *crypt_pgp_encrypt_message(struct Body *a, char *keylist, bool sign); +struct Body *crypt_pgp_encrypt_message (struct Email *msg, struct Body *a, char *keylist, int sign); char * crypt_pgp_find_keys(struct AddressList *al, bool oppenc_mode); void crypt_pgp_invoke_import(const char *fname); void crypt_pgp_set_sender(const char *sender); diff --git a/ncrypt/ncrypt.h b/ncrypt/ncrypt.h index a69e2ca6e..2f25628d0 100644 --- a/ncrypt/ncrypt.h +++ b/ncrypt/ncrypt.h @@ -198,7 +198,7 @@ SecurityFlags mutt_is_malformed_multipart_pgp_encrypted(struct Body *b); SecurityFlags mutt_is_multipart_encrypted(struct Body *b); SecurityFlags mutt_is_multipart_signed(struct Body *b); int mutt_is_valid_multipart_pgp_encrypted(struct Body *b); -int mutt_protect(struct Email *e, char *keylist); +int mutt_protect(struct Email *e, char *keylist, int); int mutt_protected_headers_handler(struct Body *m, struct State *s); bool mutt_should_hide_protected_subject(struct Email *e); int mutt_signed_handler(struct Body *a, struct State *s); diff --git a/options.h b/options.h index 2d26f12c6..a15033238 100644 --- a/options.h +++ b/options.h @@ -29,6 +29,7 @@ /* pseudo options */ WHERE bool OptAttachMsg; /**< (pseudo) used by attach-message */ +WHERE bool OptAutocryptGpgme; /**< (pseudo) use Autocrypt context inside ncrypt/crypt_gpgme.c */ WHERE bool OptAuxSort; /**< (pseudo) using auxiliary sort function */ WHERE bool OptDontHandlePgpKeys; /**< (pseudo) used to extract PGP keys */ WHERE bool OptForceRefresh; /**< (pseudo) refresh even during macros */ diff --git a/postpone.c b/postpone.c index c1fd96b0d..0bc703479 100644 --- a/postpone.c +++ b/postpone.c @@ -517,6 +517,20 @@ SecurityFlags mutt_parse_crypt_hdr(const char *p, bool set_empty_signas, Securit flags |= SEC_OPPENCRYPT; break; + case 'a': + case 'A': +#ifdef USE_AUTOCRYPT + flags |= SEC_AUTOCRYPT; +#endif + break; + + case 'z': + case 'Z': +#ifdef USE_AUTOCRYPT + flags |= SEC_AUTOCRYPT_OVERRIDE; +#endif + break; + case 's': case 'S': flags |= SEC_SIGN; diff --git a/send.c b/send.c index d778ed60a..4c9efc475 100644 --- a/send.c +++ b/send.c @@ -84,6 +84,9 @@ #ifdef USE_IMAP #include "imap/imap.h" #endif +#ifdef USE_AUTOCRYPT +#include "autocrypt/autocrypt.h" +#endif /* These Config Variables are only used in send.c */ unsigned char C_AbortNoattach; ///< Config: Abort sending the email if attachments are missing @@ -1603,17 +1606,17 @@ static int save_fcc(struct Email *e, char *fcc, size_t fcc_len, struct Body *cle * Protected Headers. */ if (!C_FccBeforeSend) { - if ((WithCrypto != 0) && (e->security & (SEC_ENCRYPT | SEC_SIGN)) && C_FccClear) + if (WithCrypto && (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) && C_FccClear) { e->content = clear_content; - e->security &= ~(SEC_ENCRYPT | SEC_SIGN); + e->security &= ~(SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT); mutt_env_free(&e->content->mime_headers); } /* check to see if the user wants copies of all attachments */ if (e->content->type == TYPE_MULTIPART) { - if ((WithCrypto != 0) && (e->security & (SEC_ENCRYPT | SEC_SIGN)) && + if ((WithCrypto != 0) && (e->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) && ((mutt_str_strcmp(e->content->subtype, "encrypted") == 0) || (mutt_str_strcmp(e->content->subtype, "signed") == 0))) { @@ -1630,7 +1633,7 @@ static int save_fcc(struct Email *e, char *fcc, size_t fcc_len, struct Body *cle /* this means writing only the main part */ e->content = clear_content->parts; - if (mutt_protect(e, pgpkeylist) == -1) + if (mutt_protect(e, pgpkeylist, 0) == -1) { /* we can't do much about it at this point, so * fallback to saving the whole thing to fcc */ @@ -1750,7 +1753,7 @@ static int postpone_message(struct Email *e_post, struct Email *e_cur, char *fcc mutt_encode_descriptions(e_post->content, true); - if ((WithCrypto != 0) && C_PostponeEncrypt && (e_post->security & SEC_ENCRYPT)) + if (WithCrypto && C_PostponeEncrypt && (e_post->security & (SEC_ENCRYPT | SEC_AUTOCRYPT))) { if (((WithCrypto & APPLICATION_PGP) != 0) && (e_post->security & APPLICATION_PGP)) encrypt_as = C_PgpDefaultKey; @@ -1759,26 +1762,27 @@ static int postpone_message(struct Email *e_post, struct Email *e_cur, char *fcc if (!encrypt_as) encrypt_as = C_PostponeEncryptAs; - if (encrypt_as) +#ifdef USE_AUTOCRYPT + if (e_post->security & SEC_AUTOCRYPT) { - bool is_signed = (e_post->security & SEC_SIGN); - if (is_signed) - e_post->security &= ~SEC_SIGN; + if (mutt_autocrypt_set_sign_as_default_key(e_post)) + return -1; + encrypt_as = AutocryptDefaultKey; + } +#endif + if (encrypt_as) + { pgpkeylist = mutt_str_strdup(encrypt_as); clear_content = e_post->content; - if (mutt_protect(e_post, pgpkeylist) == -1) + if (mutt_protect(e_post, pgpkeylist, 1) == -1) { - if (is_signed) - e_post->security |= SEC_SIGN; FREE(&pgpkeylist); e_post->content = mutt_remove_multipart(e_post->content); decode_descriptions(e_post->content); return -1; } - if (is_signed) - e_post->security |= SEC_SIGN; FREE(&pgpkeylist); mutt_encode_descriptions(e_post->content, false); @@ -2241,23 +2245,36 @@ int ci_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile if ((WithCrypto != 0) && (e_templ->security == 0) && !(flags & (SEND_BATCH | SEND_MAILX | SEND_POSTPONED | SEND_RESEND))) { - if (C_CryptAutosign) - e_templ->security |= SEC_SIGN; - if (C_CryptAutoencrypt) - e_templ->security |= SEC_ENCRYPT; - if (C_CryptReplyencrypt && e_cur && (e_cur->security & SEC_ENCRYPT)) - e_templ->security |= SEC_ENCRYPT; - if (C_CryptReplysign && e_cur && (e_cur->security & SEC_SIGN)) - e_templ->security |= SEC_SIGN; - if (C_CryptReplysignencrypted && e_cur && (e_cur->security & SEC_ENCRYPT)) - e_templ->security |= SEC_SIGN; - if (((WithCrypto & APPLICATION_PGP) != 0) && - ((e_templ->security & (SEC_ENCRYPT | SEC_SIGN)) || C_CryptOpportunisticEncrypt)) - { - if (C_PgpAutoinline) - e_templ->security |= SEC_INLINE; - if (C_PgpReplyinline && e_cur && (e_cur->security & SEC_INLINE)) - e_templ->security |= SEC_INLINE; + if ( +#ifdef USE_AUTOCRYPT + C_Autocrypt +#else + 0 +#endif + && e_cur && (e_cur->security & SEC_AUTOCRYPT)) + { + e_templ->security |= (SEC_AUTOCRYPT | SEC_AUTOCRYPT_OVERRIDE); + } + else + { + if (C_CryptAutosign) + e_templ->security |= SEC_SIGN; + if (C_CryptAutoencrypt) + e_templ->security |= SEC_ENCRYPT; + if (C_CryptReplyencrypt && e_cur && (e_cur->security & SEC_ENCRYPT)) + e_templ->security |= SEC_ENCRYPT; + if (C_CryptReplysign && e_cur && (e_cur->security & SEC_SIGN)) + e_templ->security |= SEC_SIGN; + if (C_CryptReplysignencrypted && e_cur && (e_cur->security & SEC_ENCRYPT)) + e_templ->security |= SEC_SIGN; + if ((WithCrypto & APPLICATION_PGP) && + ((e_templ->security & (SEC_ENCRYPT | SEC_SIGN)) || C_CryptOpportunisticEncrypt)) + { + if (C_PgpAutoinline) + e_templ->security |= SEC_INLINE; + if (C_PgpReplyinline && e_cur && (e_cur->security & SEC_INLINE)) + e_templ->security |= SEC_INLINE; + } } if (e_templ->security || C_CryptOpportunisticEncrypt) @@ -2308,7 +2325,7 @@ int ci_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile /* If something has already enabled encryption, e.g. C_CryptAutoencrypt * or C_CryptReplyencrypt, then don't enable opportunistic encrypt for * the message. */ - if (!(e_templ->security & SEC_ENCRYPT)) + if (!(e_templ->security & (SEC_ENCRYPT | SEC_AUTOCRYPT))) { e_templ->security |= SEC_OPPENCRYPT; crypt_opportunistic_encrypt(e_templ); @@ -2464,13 +2481,13 @@ int ci_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile if (WithCrypto) { - if (e_templ->security & (SEC_ENCRYPT | SEC_SIGN)) + if (e_templ->security & (SEC_ENCRYPT | SEC_SIGN | SEC_AUTOCRYPT)) { /* save the decrypted attachments */ clear_content = e_templ->content; if ((crypt_get_keys(e_templ, &pgpkeylist, 0) == -1) || - (mutt_protect(e_templ, pgpkeylist) == -1)) + (mutt_protect(e_templ, pgpkeylist, 0) == -1)) { e_templ->content = mutt_remove_multipart(e_templ->content); @@ -2511,7 +2528,7 @@ int ci_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile { if (!WithCrypto) ; - else if ((e_templ->security & SEC_ENCRYPT) || + else if ((e_templ->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)) || ((e_templ->security & SEC_SIGN) && (e_templ->content->type == TYPE_APPLICATION))) { mutt_body_free(&e_templ->content); /* destroy PGP data */ diff --git a/sendlib.c b/sendlib.c index 33ef9b974..ac281e7f4 100644 --- a/sendlib.c +++ b/sendlib.c @@ -74,6 +74,9 @@ #else #define EX_OK 0 #endif +#ifdef USE_AUTOCRYPT +#include "autocrypt/autocrypt.h" +#endif /* These Config Variables are only used in sendlib.c */ bool C_Allow8bit; ///< Config: Allow 8-bit messages, don't use quoted-printable or base64 @@ -483,8 +486,15 @@ int mutt_write_mime_header(struct Body *a, FILE *fp) if (a->encoding != ENC_7BIT) fprintf(fp, "Content-Transfer-Encoding: %s\n", ENCODING(a->encoding)); - if (C_CryptProtectedHeadersWrite && a->mime_headers) + if ((C_CryptProtectedHeadersWrite +#ifdef USE_AUTOCRYPT + || C_Autocrypt +#endif + ) && + a->mime_headers) + { mutt_rfc822_write_header(fp, a->mime_headers, NULL, MUTT_WRITE_HEADER_MIME, false, false); + } /* Do NOT add the terminator here!!! */ return ferror(fp) ? -1 : 0; @@ -2359,6 +2369,16 @@ int mutt_rfc822_write_header(FILE *fp, struct Envelope *env, fputc('\n', fp); } +#ifdef USE_AUTOCRYPT + if (C_Autocrypt) + { + if (mode == MUTT_WRITE_HEADER_NORMAL) + mutt_autocrypt_write_autocrypt_header(env, fp); + if (mode == MUTT_WRITE_HEADER_MIME) + mutt_autocrypt_write_gossip_headers(env, fp); + } +#endif + /* Add any user defined headers */ struct ListNode *tmp = NULL; STAILQ_FOREACH(tmp, &env->userhdrs, entries) @@ -3264,6 +3284,12 @@ int mutt_write_fcc(const char *path, struct Email *e, const char *msgid, } if (e->security & SEC_INLINE) fputc('I', msg->fp); +#ifdef USE_AUTOCRYPT + if (e->security & SEC_AUTOCRYPT) + fputc('A', msg->fp); + if (e->security & SEC_AUTOCRYPT_OVERRIDE) + fputc('Z', msg->fp); +#endif fputc('\n', msg->fp); }