summaryrefslogtreecommitdiffstats
path: root/autocrypt
diff options
context:
space:
mode:
authorKevin McCarthy <kevin@8t8.us>2019-07-11 18:45:45 -0700
committerKevin McCarthy <kevin@8t8.us>2019-08-03 14:08:09 -0700
commitc7d4b01957745a333912ab415570858064843000 (patch)
tree71a3b739a0c807e795e5a8f20047e267d53b6501 /autocrypt
parent7fd50ddd8f4899232567573e26faf1863ae5b390 (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.c146
-rw-r--r--autocrypt/autocrypt.h22
-rw-r--r--autocrypt/autocrypt_db.c342
-rw-r--r--autocrypt/autocrypt_gpgme.c36
-rw-r--r--autocrypt/autocrypt_private.h15
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