From 90898d458284fca082b6dbbb95a77fb5c59bb983 Mon Sep 17 00:00:00 2001 From: Kevin McCarthy Date: Mon, 15 Jul 2019 18:36:57 -0700 Subject: [PATCH] Add gossip header processing Co-authored-by: Richard Russon --- autocrypt/autocrypt.c | 143 ++++++++++++++++++++++++++++++++++ autocrypt/autocrypt.h | 10 +++ autocrypt/autocrypt_db.c | 67 ++++++++++++++++ autocrypt/autocrypt_private.h | 4 + commands.c | 28 +++++-- email/envelope.c | 1 + email/envelope.h | 1 + email/parse.c | 8 ++ 8 files changed, 256 insertions(+), 6 deletions(-) diff --git a/autocrypt/autocrypt.c b/autocrypt/autocrypt.c index 370a86387..3ecebcf20 100644 --- a/autocrypt/autocrypt.c +++ b/autocrypt/autocrypt.c @@ -309,3 +309,146 @@ cleanup: return rv; } + +static struct Address *matching_gossip_address(struct Envelope *env, const char *addr) +{ + struct Address *np = NULL; + + TAILQ_FOREACH(np, &env->to, entries) + { + if (!mutt_str_strcasecmp(np->mailbox, addr)) + return np; + } + + TAILQ_FOREACH(np, &env->cc, entries) + { + if (!mutt_str_strcasecmp(np->mailbox, addr)) + return np; + } + + TAILQ_FOREACH(np, &env->reply_to, entries) + { + if (!mutt_str_strcasecmp(np->mailbox, addr)) + return np; + } + + return NULL; +} + +int mutt_autocrypt_process_gossip_header(struct Email *hdr, struct Envelope *env) +{ + struct AutocryptHeader *ac_hdr; + struct timeval now; + struct AutocryptPeer *peer = NULL; + struct AutocryptGossipHistory *gossip_hist = NULL; + struct Address *peer_addr; + struct Buffer *keyid = NULL; + int update_db = 0, insert_db = 0, insert_db_history = 0, import_gpg = 0; + int rv = -1; + + if (!C_Autocrypt) + return 0; + + if (mutt_autocrypt_init(0)) + return -1; + + if (!hdr || !hdr->content || !env) + return 0; + + struct Address *from = TAILQ_FIRST(&env->from); + if (!from) + return 0; + + /* Ignore emails that appear to be more than a week in the future, + * since they can block all future updates during that time. */ + gettimeofday(&now, NULL); + if (hdr->date_sent > (now.tv_sec + 7 * 24 * 60 * 60)) + return 0; + + keyid = mutt_buffer_pool_get(); + + /* To ensure the address headers match the gossip header format */ + mutt_env_to_intl(env, NULL, NULL); + + for (ac_hdr = env->autocrypt_gossip; ac_hdr; ac_hdr = ac_hdr->next) + { + if (ac_hdr->invalid) + continue; + + peer_addr = matching_gossip_address(env, ac_hdr->addr); + if (!peer_addr) + continue; + + if (mutt_autocrypt_db_peer_get(from, &peer) < 0) + goto cleanup; + + if (peer) + { + if (hdr->date_sent <= peer->gossip_timestamp) + { + mutt_autocrypt_db_peer_free(&peer); + continue; + } + + update_db = 1; + peer->gossip_timestamp = hdr->date_sent; + if (mutt_str_strcmp(peer->gossip_keydata, ac_hdr->keydata)) + { + import_gpg = 1; + insert_db_history = 1; + mutt_str_replace(&peer->gossip_keydata, ac_hdr->keydata); + } + } + else + { + import_gpg = 1; + insert_db = 1; + insert_db_history = 1; + } + + if (!peer) + { + peer = mutt_autocrypt_db_peer_new(); + peer->gossip_timestamp = hdr->date_sent; + peer->gossip_keydata = mutt_str_strdup(ac_hdr->keydata); + } + + if (import_gpg) + { + if (mutt_autocrypt_gpgme_import_key(peer->gossip_keydata, keyid)) + goto cleanup; + mutt_str_replace(&peer->gossip_keyid, mutt_b2s(keyid)); + } + + if (insert_db && mutt_autocrypt_db_peer_insert(peer_addr, peer)) + goto cleanup; + + if (update_db && mutt_autocrypt_db_peer_update(peer_addr, peer)) + goto cleanup; + + if (insert_db_history) + { + gossip_hist = mutt_autocrypt_db_gossip_history_new(); + gossip_hist->sender_email_addr = mutt_str_strdup(from->mailbox); + gossip_hist->email_msgid = mutt_str_strdup(env->message_id); + gossip_hist->timestamp = hdr->date_sent; + gossip_hist->gossip_keydata = mutt_str_strdup(peer->gossip_keydata); + if (mutt_autocrypt_db_gossip_history_insert(peer_addr, gossip_hist)) + goto cleanup; + } + + mutt_autocrypt_db_peer_free(&peer); + mutt_autocrypt_db_gossip_history_free(&gossip_hist); + mutt_buffer_reset(keyid); + update_db = insert_db = insert_db_history = import_gpg = 0; + } + + rv = 0; + +cleanup: + mutt_autocrypt_db_peer_free(&peer); + mutt_autocrypt_db_gossip_history_free(&gossip_hist); + mutt_buffer_pool_release(&keyid); + + return rv; +} diff --git a/autocrypt/autocrypt.h b/autocrypt/autocrypt.h index 72f35a804..82dc6a9b1 100644 --- a/autocrypt/autocrypt.h +++ b/autocrypt/autocrypt.h @@ -58,8 +58,18 @@ struct AutocryptPeerHistory char *keydata; }; +struct AutocryptGossipHistory +{ + char *peer_email_addr; + char *sender_email_addr; + char *email_msgid; + sqlite3_int64 timestamp; + char *gossip_keydata; +}; + 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); #endif /* MUTT_AUTOCRYPT_AUTOCRYPT_H */ diff --git a/autocrypt/autocrypt_db.c b/autocrypt/autocrypt_db.c index 1c2e1d5c3..e9c964176 100644 --- a/autocrypt/autocrypt_db.c +++ b/autocrypt/autocrypt_db.c @@ -37,6 +37,7 @@ static sqlite3_stmt *PeerGetStmt; static sqlite3_stmt *PeerInsertStmt; static sqlite3_stmt *PeerUpdateStmt; static sqlite3_stmt *PeerHistoryInsertStmt; +static sqlite3_stmt *GossipHistoryInsertStmt; static int autocrypt_db_create(const char *db_path) { @@ -112,6 +113,9 @@ void mutt_autocrypt_db_close(void) sqlite3_finalize(PeerHistoryInsertStmt); PeerHistoryInsertStmt = NULL; + sqlite3_finalize(GossipHistoryInsertStmt); + GossipHistoryInsertStmt = NULL; + sqlite3_close_v2(AutocryptDB); AutocryptDB = NULL; } @@ -495,3 +499,66 @@ cleanup: sqlite3_reset(PeerHistoryInsertStmt); return rv; } + +struct AutocryptGossipHistory *mutt_autocrypt_db_gossip_history_new(void) +{ + return mutt_mem_calloc(1, sizeof(struct AutocryptGossipHistory)); +} + +void mutt_autocrypt_db_gossip_history_free(struct AutocryptGossipHistory **gossip_hist) +{ + if (!gossip_hist || !*gossip_hist) + return; + FREE(&(*gossip_hist)->peer_email_addr); + FREE(&(*gossip_hist)->sender_email_addr); + FREE(&(*gossip_hist)->email_msgid); + FREE(&(*gossip_hist)->gossip_keydata); + FREE(gossip_hist); +} + +int mutt_autocrypt_db_gossip_history_insert(struct Address *addr, + struct AutocryptGossipHistory *gossip_hist) +{ + int rv = -1; + char *email = NULL; + + email = normalize_email_addr(addr); + + if (!GossipHistoryInsertStmt) + { + if (sqlite3_prepare_v3(AutocryptDB, + "INSERT INTO gossip_history " + "(peer_email_addr, " + "sender_email_addr, " + "email_msgid, " + "timestamp, " + "gossip_keydata) " + "VALUES (?, ?, ?, ?, ?);", + -1, SQLITE_PREPARE_PERSISTENT, + &GossipHistoryInsertStmt, NULL) != SQLITE_OK) + goto cleanup; + } + + if (sqlite3_bind_text(GossipHistoryInsertStmt, 1, email, -1, SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text(GossipHistoryInsertStmt, 2, gossip_hist->sender_email_addr, + -1, SQLITE_STATIC) != SQLITE_OK) + if (sqlite3_bind_text(GossipHistoryInsertStmt, 3, gossip_hist->email_msgid, + -1, SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_int64(GossipHistoryInsertStmt, 4, gossip_hist->timestamp) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text(GossipHistoryInsertStmt, 5, gossip_hist->gossip_keydata, + -1, SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + + if (sqlite3_step(GossipHistoryInsertStmt) != SQLITE_DONE) + goto cleanup; + + rv = 0; + +cleanup: + FREE(&email); + sqlite3_reset(GossipHistoryInsertStmt); + return rv; +} diff --git a/autocrypt/autocrypt_private.h b/autocrypt/autocrypt_private.h index e63fbf352..76a302926 100644 --- a/autocrypt/autocrypt_private.h +++ b/autocrypt/autocrypt_private.h @@ -49,6 +49,10 @@ struct AutocryptPeerHistory *mutt_autocrypt_db_peer_history_new (void); void mutt_autocrypt_db_peer_history_free (struct AutocryptPeerHistory **peerhist); int mutt_autocrypt_db_peer_history_insert (struct Address *addr, struct AutocryptPeerHistory *peerhist); +struct AutocryptGossipHistory *mutt_autocrypt_db_gossip_history_new (void); +void mutt_autocrypt_db_gossip_history_free (struct AutocryptGossipHistory **gossip_hist); +int mutt_autocrypt_db_gossip_history_insert (struct Address *addr, struct AutocryptGossipHistory *gossip_hist); + int mutt_autocrypt_schema_init (void); int mutt_autocrypt_schema_update (void); diff --git a/commands.c b/commands.c index c7160eb73..2d369c866 100644 --- a/commands.c +++ b/commands.c @@ -78,6 +78,9 @@ #ifdef ENABLE_NLS #include #endif +#ifdef USE_AUTOCRYPT +#include "autocrypt/autocrypt.h" +#endif /* These Config Variables are only used in commands.c */ unsigned char C_CryptVerifySig; ///< Config: Verify PGP or SMIME signatures @@ -95,16 +98,22 @@ static const char *ExtPagerProgress = "all"; static char LastSaveFolder[PATH_MAX] = ""; /** - * update_protected_headers - Get the protected header and update the index + * process_protected_headers - Get the protected header and update the index * @param e Email to update */ -static void update_protected_headers(struct Email *e) +static void process_protected_headers(struct Email *e) { struct Envelope *prot_headers = NULL; regmatch_t pmatch[1]; - if (!C_CryptProtectedHeadersRead) + if (!C_CryptProtectedHeadersRead +#ifdef USE_AUTOCRYPT + && !C_Autocrypt +#endif + ) + { return; + } /* Grab protected headers to update in the index */ if (e->security & SEC_SIGN) @@ -142,7 +151,7 @@ static void update_protected_headers(struct Email *e) } /* Update protected headers in the index and header cache. */ - if (prot_headers && prot_headers->subject && + if (C_CryptProtectedHeadersRead && prot_headers && prot_headers->subject && mutt_str_strcmp(e->env->subject, prot_headers->subject)) { if (Context->mailbox->subj_hash && e->env->real_subj) @@ -168,6 +177,13 @@ static void update_protected_headers(struct Email *e) Context->mailbox->changed = 1; } } + +#ifdef USE_AUTOCRYPT + if (C_Autocrypt && (e->security & SEC_ENCRYPT) && prot_headers && prot_headers->autocrypt_gossip) + { + mutt_autocrypt_process_gossip_header(e, e->env); + } +#endif } /** @@ -302,8 +318,8 @@ int mutt_display_message(struct Mailbox *m, struct Email *e) * are color patterns for both ~g and ~V */ e->pair = 0; - /* Grab protected headers and update the header and index */ - update_protected_headers(e); + /* Process protected headers and autocrypt gossip headers */ + process_protected_headers(e); } if (builtin) diff --git a/email/envelope.c b/email/envelope.c index f81859cd2..fa8732e8e 100644 --- a/email/envelope.c +++ b/email/envelope.c @@ -117,6 +117,7 @@ void mutt_env_free(struct Envelope **p) #ifdef USE_AUTOCRYPT mutt_free_autocrypthdr(&(*p)->autocrypt); + mutt_free_autocrypthdr(&(*p)->autocrypt_gossip); #endif FREE(p); diff --git a/email/envelope.h b/email/envelope.h index 7cf51dcdb..73ec268a7 100644 --- a/email/envelope.h +++ b/email/envelope.h @@ -80,6 +80,7 @@ struct Envelope struct ListHead userhdrs; ///< user defined headers #ifdef USE_AUTOCRYPT struct AutocryptHeader *autocrypt; + struct AutocryptHeader *autocrypt_gossip; #endif unsigned char changed; ///< Changed fields, e.g. #MUTT_ENV_CHANGED_SUBJECT }; diff --git a/email/parse.c b/email/parse.c index e1a4969a0..469437e20 100644 --- a/email/parse.c +++ b/email/parse.c @@ -953,6 +953,14 @@ int mutt_rfc822_parse_line(struct Envelope *env, struct Email *e, char *line, matched = 1; } } + else if (mutt_str_strcasecmp(line + 1, "utocrypt-gossip") == 0) + { + if (C_Autocrypt) + { + env->autocrypt_gossip = parse_autocrypt(env->autocrypt_gossip, p); + matched = 1; + } + } #endif break; -- 2.40.0