diff options
author | Kevin McCarthy <kevin@8t8.us> | 2019-07-11 18:45:45 -0700 |
---|---|---|
committer | Kevin McCarthy <kevin@8t8.us> | 2019-08-03 14:08:09 -0700 |
commit | c7d4b01957745a333912ab415570858064843000 (patch) | |
tree | 71a3b739a0c807e795e5a8f20047e267d53b6501 /autocrypt | |
parent | 7fd50ddd8f4899232567573e26faf1863ae5b390 (diff) |
Process autocrypt headers.
Create/update peer database accounts and gpg keys based on the headers.
Diffstat (limited to 'autocrypt')
-rw-r--r-- | autocrypt/autocrypt.c | 146 | ||||
-rw-r--r-- | autocrypt/autocrypt.h | 22 | ||||
-rw-r--r-- | autocrypt/autocrypt_db.c | 342 | ||||
-rw-r--r-- | autocrypt/autocrypt_gpgme.c | 36 | ||||
-rw-r--r-- | autocrypt/autocrypt_private.h | 15 |
5 files changed, 557 insertions, 4 deletions
diff --git a/autocrypt/autocrypt.c b/autocrypt/autocrypt.c index c9269bcf..5207af4b 100644 --- a/autocrypt/autocrypt.c +++ b/autocrypt/autocrypt.c @@ -22,9 +22,13 @@ #include "mutt.h" #include "mutt_curses.h" +#include "mime.h" #include "autocrypt.h" #include "autocrypt_private.h" +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif #include <errno.h> static int autocrypt_dir_init (int can_create) @@ -156,3 +160,145 @@ cleanup: mutt_buffer_pool_release (&keydata); return rv; } + +int mutt_autocrypt_process_autocrypt_header (HEADER *hdr, ENVELOPE *env) +{ + AUTOCRYPTHDR *ac_hdr, *valid_ac_hdr = NULL; + struct timeval now; + AUTOCRYPT_PEER *peer = NULL; + AUTOCRYPT_PEER_HISTORY *peerhist = NULL; + BUFFER *keyid = NULL; + int update_db = 0, insert_db = 0, insert_db_history = 0, import_gpg = 0; + int rv = -1; + + if (!option (OPTAUTOCRYPT)) + return 0; + + if (mutt_autocrypt_init (0)) + return -1; + + if (!hdr || !hdr->content || !env) + return 0; + + /* 1.1 spec says to skip emails with more than one From header */ + if (!env->from || env->from->next) + return 0; + + /* 1.1 spec also says to skip multipart/report emails */ + if (hdr->content->type == TYPEMULTIPART && + !(ascii_strcasecmp (hdr->content->subtype, "report"))) + 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; + + for (ac_hdr = env->autocrypt; ac_hdr; ac_hdr = ac_hdr->next) + { + if (ac_hdr->invalid) + continue; + + /* NOTE: this assumes the processing is occurring right after + * mutt_parse_rfc822_line() and the from ADDR is still in the same + * form (intl) as the autocrypt header addr field */ + if (ascii_strcasecmp (env->from->mailbox, ac_hdr->addr)) + continue; + + /* 1.1 spec says ignore all, if more than one valid header is found. */ + if (valid_ac_hdr) + { + valid_ac_hdr = NULL; + break; + } + valid_ac_hdr = ac_hdr; + } + + if (mutt_autocrypt_db_peer_get (env->from, &peer) < 0) + goto cleanup; + + if (peer) + { + if (hdr->date_sent <= peer->autocrypt_timestamp) + { + rv = 0; + goto cleanup; + } + + if (hdr->date_sent > peer->last_seen) + { + update_db = 1; + peer->last_seen = hdr->date_sent; + } + + if (valid_ac_hdr) + { + update_db = 1; + peer->autocrypt_timestamp = hdr->date_sent; + peer->prefer_encrypt = valid_ac_hdr->prefer_encrypt; + if (mutt_strcmp (peer->keydata, valid_ac_hdr->keydata)) + { + import_gpg = 1; + insert_db_history = 1; + mutt_str_replace (&peer->keydata, valid_ac_hdr->keydata); + } + } + } + else if (valid_ac_hdr) + { + import_gpg = 1; + insert_db = 1; + insert_db_history = 1; + } + + if (!(import_gpg || insert_db || update_db)) + { + rv = 0; + goto cleanup; + } + + if (!peer) + { + peer = mutt_autocrypt_db_peer_new (); + peer->last_seen = hdr->date_sent; + peer->autocrypt_timestamp = hdr->date_sent; + peer->keydata = safe_strdup (valid_ac_hdr->keydata); + peer->prefer_encrypt = valid_ac_hdr->prefer_encrypt; + } + + if (import_gpg) + { + keyid = mutt_buffer_pool_get (); + if (mutt_autocrypt_gpgme_import_key (peer->keydata, keyid)) + goto cleanup; + mutt_str_replace (&peer->keyid, mutt_b2s (keyid)); + } + + if (insert_db && + mutt_autocrypt_db_peer_insert (env->from, peer)) + goto cleanup; + + if (update_db && + mutt_autocrypt_db_peer_update (env->from, peer)) + goto cleanup; + + if (insert_db_history) + { + peerhist = mutt_autocrypt_db_peer_history_new (); + peerhist->email_msgid = safe_strdup (env->message_id); + peerhist->timestamp = hdr->date_sent; + peerhist->keydata = safe_strdup (peer->keydata); + if (mutt_autocrypt_db_peer_history_insert (env->from, peerhist)) + goto cleanup; + } + + rv = 0; + +cleanup: + mutt_autocrypt_db_peer_free (&peer); + mutt_autocrypt_db_peer_history_free (&peerhist); + mutt_buffer_pool_release (&keyid); + + return rv; +} diff --git a/autocrypt/autocrypt.h b/autocrypt/autocrypt.h index c690fae2..859a708a 100644 --- a/autocrypt/autocrypt.h +++ b/autocrypt/autocrypt.h @@ -32,7 +32,29 @@ typedef struct int enabled; } AUTOCRYPT_ACCOUNT; +typedef struct +{ + char *email_addr; + sqlite3_int64 last_seen; + sqlite3_int64 autocrypt_timestamp; + char *keyid; + char *keydata; + int prefer_encrypt; /* 0 = nopref, 1 = mutual */ + sqlite3_int64 gossip_timestamp; + char *gossip_keyid; + char *gossip_keydata; +} AUTOCRYPT_PEER; + +typedef struct +{ + char *peer_email_addr; + char *email_msgid; + sqlite3_int64 timestamp; + char *keydata; +} AUTOCRYPT_PEER_HISTORY; + int mutt_autocrypt_init (int); void mutt_autocrypt_cleanup (void); +int mutt_autocrypt_process_autocrypt_header (HEADER *hdr, ENVELOPE *env); #endif diff --git a/autocrypt/autocrypt_db.c b/autocrypt/autocrypt_db.c index a053c433..604c1f34 100644 --- a/autocrypt/autocrypt_db.c +++ b/autocrypt/autocrypt_db.c @@ -25,6 +25,14 @@ #include "autocrypt.h" #include "autocrypt_private.h" +/* Prepared statements */ +static sqlite3_stmt *AccountGetStmt; +static sqlite3_stmt *AccountInsertStmt; +static sqlite3_stmt *PeerGetStmt; +static sqlite3_stmt *PeerInsertStmt; +static sqlite3_stmt *PeerUpdateStmt; +static sqlite3_stmt *PeerHistoryInsertStmt; + static int autocrypt_db_create (const char *db_path) { if (sqlite3_open_v2 (db_path, @@ -96,6 +104,16 @@ void mutt_autocrypt_db_close (void) sqlite3_finalize (AccountInsertStmt); AccountInsertStmt = NULL; + sqlite3_finalize (PeerGetStmt); + PeerGetStmt = NULL; + sqlite3_finalize (PeerInsertStmt); + PeerInsertStmt = NULL; + sqlite3_finalize (PeerUpdateStmt); + PeerUpdateStmt = NULL; + + sqlite3_finalize (PeerHistoryInsertStmt); + PeerHistoryInsertStmt = NULL; + sqlite3_close_v2 (AutocryptDB); AutocryptDB = NULL; } @@ -263,3 +281,327 @@ cleanup: sqlite3_reset (AccountInsertStmt); return rv; } + +AUTOCRYPT_PEER *mutt_autocrypt_db_peer_new (void) +{ + return safe_calloc (1, sizeof(AUTOCRYPT_PEER)); +} + +void mutt_autocrypt_db_peer_free (AUTOCRYPT_PEER **peer) +{ + if (!peer || !*peer) + return; + FREE (&(*peer)->email_addr); + FREE (&(*peer)->keyid); + FREE (&(*peer)->keydata); + FREE (&(*peer)->gossip_keyid); + FREE (&(*peer)->gossip_keydata); + FREE (peer); /* __FREE_CHECKED__ */ +} + +int mutt_autocrypt_db_peer_get (ADDRESS *addr, AUTOCRYPT_PEER **peer) +{ + int rv = -1, result; + char *email = NULL; + + email = normalize_email_addr (addr); + *peer = NULL; + + if (!PeerGetStmt) + { + if (sqlite3_prepare_v2 ( + AutocryptDB, + "SELECT " + "email_addr, " + "last_seen, " + "autocrypt_timestamp, " + "keyid, " + "keydata, " + "prefer_encrypt, " + "gossip_timestamp, " + "gossip_keyid, " + "gossip_keydata " + "FROM peer " + "WHERE email_addr = ?", + -1, + &PeerGetStmt, + NULL) != SQLITE_OK) + goto cleanup; + } + + if (sqlite3_bind_text (PeerGetStmt, + 1, + email, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + + result = sqlite3_step (PeerGetStmt); + if (result != SQLITE_ROW) + { + if (result == SQLITE_DONE) + rv = 0; + goto cleanup; + } + + *peer = mutt_autocrypt_db_peer_new (); + (*peer)->email_addr = strdup_column_text (PeerGetStmt, 0); + (*peer)->last_seen = sqlite3_column_int64 (PeerGetStmt, 1); + (*peer)->autocrypt_timestamp = sqlite3_column_int64 (PeerGetStmt, 2); + (*peer)->keyid = strdup_column_text (PeerGetStmt, 3); + (*peer)->keydata = strdup_column_text (PeerGetStmt, 4); + (*peer)->prefer_encrypt = sqlite3_column_int (PeerGetStmt, 5); + (*peer)->gossip_timestamp = sqlite3_column_int64 (PeerGetStmt, 6); + (*peer)->gossip_keyid = strdup_column_text (PeerGetStmt, 7); + (*peer)->gossip_keydata = strdup_column_text (PeerGetStmt, 8); + + rv = 1; + +cleanup: + FREE (&email); + sqlite3_reset (PeerGetStmt); + return rv; +} + +int mutt_autocrypt_db_peer_insert (ADDRESS *addr, AUTOCRYPT_PEER *peer) +{ + int rv = -1; + char *email = NULL; + + email = normalize_email_addr (addr); + + if (!PeerInsertStmt) + { + if (sqlite3_prepare_v2 ( + AutocryptDB, + "INSERT INTO peer " + "(email_addr, " + "last_seen, " + "autocrypt_timestamp, " + "keyid, " + "keydata, " + "prefer_encrypt, " + "gossip_timestamp, " + "gossip_keyid, " + "gossip_keydata) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);", + -1, + &PeerInsertStmt, + NULL) != SQLITE_OK) + goto cleanup; + } + + if (sqlite3_bind_text (PeerInsertStmt, + 1, + email, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_int64 (PeerInsertStmt, + 2, + peer->last_seen) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_int64 (PeerInsertStmt, + 3, + peer->autocrypt_timestamp) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text (PeerInsertStmt, + 4, + peer->keyid, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text (PeerInsertStmt, + 5, + peer->keydata, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_int (PeerInsertStmt, + 6, + peer->prefer_encrypt) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_int64 (PeerInsertStmt, + 7, + peer->gossip_timestamp) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text (PeerInsertStmt, + 8, + peer->gossip_keyid, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text (PeerInsertStmt, + 9, + peer->gossip_keydata, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + + if (sqlite3_step (PeerInsertStmt) != SQLITE_DONE) + goto cleanup; + + rv = 0; + +cleanup: + FREE (&email); + sqlite3_reset (PeerInsertStmt); + return rv; +} + +int mutt_autocrypt_db_peer_update (ADDRESS *addr, AUTOCRYPT_PEER *peer) +{ + int rv = -1; + char *email = NULL; + + email = normalize_email_addr (addr); + + if (!PeerUpdateStmt) + { + if (sqlite3_prepare_v2 ( + AutocryptDB, + "UPDATE peer SET " + "last_seen = ?, " + "autocrypt_timestamp = ?, " + "keyid = ?, " + "keydata = ?, " + "prefer_encrypt = ?, " + "gossip_timestamp = ?, " + "gossip_keyid = ?, " + "gossip_keydata = ? " + "WHERE email_addr = ?;", + -1, + &PeerUpdateStmt, + NULL) != SQLITE_OK) + goto cleanup; + } + + if (sqlite3_bind_int64 (PeerUpdateStmt, + 1, + peer->last_seen) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_int64 (PeerUpdateStmt, + 2, + peer->autocrypt_timestamp) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text (PeerUpdateStmt, + 3, + peer->keyid, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text (PeerUpdateStmt, + 4, + peer->keydata, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_int (PeerUpdateStmt, + 5, + peer->prefer_encrypt) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_int64 (PeerUpdateStmt, + 6, + peer->gossip_timestamp) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text (PeerUpdateStmt, + 7, + peer->gossip_keyid, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text (PeerUpdateStmt, + 8, + peer->gossip_keydata, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text (PeerUpdateStmt, + 9, + email, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + + if (sqlite3_step (PeerUpdateStmt) != SQLITE_DONE) + goto cleanup; + + rv = 0; + +cleanup: + FREE (&email); + sqlite3_reset (PeerUpdateStmt); + return rv; +} + +AUTOCRYPT_PEER_HISTORY *mutt_autocrypt_db_peer_history_new (void) +{ + return safe_calloc (1, sizeof(AUTOCRYPT_PEER_HISTORY)); +} + +void mutt_autocrypt_db_peer_history_free (AUTOCRYPT_PEER_HISTORY **peerhist) +{ + if (!peerhist || !*peerhist) + return; + FREE (&(*peerhist)->peer_email_addr); + FREE (&(*peerhist)->email_msgid); + FREE (&(*peerhist)->keydata); + FREE (peerhist); /* __FREE_CHECKED__ */ +} + +int mutt_autocrypt_db_peer_history_insert (ADDRESS *addr, AUTOCRYPT_PEER_HISTORY *peerhist) +{ + int rv = -1; + char *email = NULL; + + email = normalize_email_addr (addr); + + if (!PeerHistoryInsertStmt) + { + if (sqlite3_prepare_v2 ( + AutocryptDB, + "INSERT INTO peer_history " + "(peer_email_addr, " + "email_msgid, " + "timestamp, " + "keydata) " + "VALUES (?, ?, ?, ?);", + -1, + &PeerHistoryInsertStmt, + NULL) != SQLITE_OK) + goto cleanup; + } + + if (sqlite3_bind_text (PeerHistoryInsertStmt, + 1, + email, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text (PeerHistoryInsertStmt, + 2, + peerhist->email_msgid, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_int64 (PeerHistoryInsertStmt, + 3, + peerhist->timestamp) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text (PeerHistoryInsertStmt, + 4, + peerhist->keydata, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + + if (sqlite3_step (PeerHistoryInsertStmt) != SQLITE_DONE) + goto cleanup; + + rv = 0; + +cleanup: + FREE (&email); + sqlite3_reset (PeerHistoryInsertStmt); + return rv; +} diff --git a/autocrypt/autocrypt_gpgme.c b/autocrypt/autocrypt_gpgme.c index f37fca7f..952f648b 100644 --- a/autocrypt/autocrypt_gpgme.c +++ b/autocrypt/autocrypt_gpgme.c @@ -176,3 +176,39 @@ cleanup: gpgme_release (ctx); return rv; } + +int mutt_autocrypt_gpgme_import_key (const char *keydata, BUFFER *keyid) +{ + int rv = -1; + gpgme_ctx_t ctx = NULL; + BUFFER *raw_keydata = NULL; + gpgme_data_t dh = NULL; + gpgme_import_result_t result; + + if (create_gpgme_context (&ctx)) + goto cleanup; + + raw_keydata = mutt_buffer_pool_get (); + if (!mutt_buffer_from_base64 (raw_keydata, keydata)) + goto cleanup; + + if (gpgme_data_new_from_mem (&dh, mutt_b2s (raw_keydata), + mutt_buffer_len (raw_keydata), 0)) + goto cleanup; + + if (gpgme_op_import (ctx, dh)) + goto cleanup; + + result = gpgme_op_import_result (ctx); + if (!result->imports || !result->imports->fpr) + goto cleanup; + mutt_buffer_strcpy (keyid, result->imports->fpr); + + rv = 0; + +cleanup: + gpgme_data_release (dh); + gpgme_release (ctx); + mutt_buffer_pool_release (&raw_keydata); + return rv; +} diff --git a/autocrypt/autocrypt_private.h b/autocrypt/autocrypt_private.h index 8bb757a3..d43c9e83 100644 --- a/autocrypt/autocrypt_private.h +++ b/autocrypt/autocrypt_private.h @@ -32,14 +32,21 @@ int mutt_autocrypt_db_account_get (ADDRESS *addr, AUTOCRYPT_ACCOUNT **account); int mutt_autocrypt_db_account_insert (ADDRESS *addr, const char *keyid, const char *keydata, int prefer_encrypt); +AUTOCRYPT_PEER *mutt_autocrypt_db_peer_new (void); +void mutt_autocrypt_db_peer_free (AUTOCRYPT_PEER **peer); +int mutt_autocrypt_db_peer_get (ADDRESS *addr, AUTOCRYPT_PEER **peer); +int mutt_autocrypt_db_peer_insert (ADDRESS *addr, AUTOCRYPT_PEER *peer); +int mutt_autocrypt_db_peer_update (ADDRESS *addr, AUTOCRYPT_PEER *peer); + +AUTOCRYPT_PEER_HISTORY *mutt_autocrypt_db_peer_history_new (void); +void mutt_autocrypt_db_peer_history_free (AUTOCRYPT_PEER_HISTORY **peerhist); +int mutt_autocrypt_db_peer_history_insert (ADDRESS *addr, AUTOCRYPT_PEER_HISTORY *peerhist); + int mutt_autocrypt_schema_init (void); int mutt_autocrypt_schema_update (void); int mutt_autocrypt_gpgme_init (void); int mutt_autocrypt_gpgme_create_key (ADDRESS *addr, BUFFER *keyid, BUFFER *keydata); - -/* Prepared statements */ -sqlite3_stmt *AccountGetStmt; -sqlite3_stmt *AccountInsertStmt; +int mutt_autocrypt_gpgme_import_key (const char *keydata, BUFFER *keyid); #endif |