/* * Copyright (C) 2019 Kevin J. McCarthy * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if HAVE_CONFIG_H # include "config.h" #endif #include "mutt.h" #include "mutt_idna.h" #include "autocrypt.h" #include "autocrypt_private.h" /* Prepared statements */ static sqlite3_stmt *AccountGetStmt; static sqlite3_stmt *AccountInsertStmt; static sqlite3_stmt *AccountUpdateStmt; static sqlite3_stmt *AccountDeleteStmt; 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) { if (sqlite3_open_v2 (db_path, &AutocryptDB, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) { /* L10N: %s is the path to the database. For some reason sqlite3 failed to open that database file. */ mutt_error (_("Unable to open autocrypt database %s"), db_path); mutt_sleep (0); return -1; } return mutt_autocrypt_schema_init (); } int mutt_autocrypt_db_init (int can_create) { int rv = -1; BUFFER *db_path = NULL; struct stat sb; if (AutocryptDB) return 0; if (!option (OPTAUTOCRYPT) || !AutocryptDir) return -1; db_path = mutt_buffer_pool_get (); mutt_buffer_concat_path (db_path, AutocryptDir, "autocrypt.db"); if (stat (mutt_b2s (db_path), &sb)) { if (!can_create) goto cleanup; if (autocrypt_db_create (mutt_b2s (db_path))) goto cleanup; /* Don't abort the whole init process because account creation failed */ mutt_autocrypt_account_init (1); mutt_autocrypt_scan_mailboxes (); } else { if (sqlite3_open_v2 (mutt_b2s (db_path), &AutocryptDB, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) { /* L10N: Error message if autocrypt couldn't open the sqlite database for some reason. The %s is the full path of the database file. */ mutt_error (_("Unable to open autocrypt database %s"), mutt_b2s (db_path)); mutt_sleep (0); goto cleanup; } if (mutt_autocrypt_schema_update ()) goto cleanup; } rv = 0; cleanup: mutt_buffer_pool_release (&db_path); return rv; } void mutt_autocrypt_db_close (void) { if (!AutocryptDB) return; sqlite3_finalize (AccountGetStmt); AccountGetStmt = NULL; sqlite3_finalize (AccountInsertStmt); AccountInsertStmt = NULL; sqlite3_finalize (AccountUpdateStmt); AccountUpdateStmt = NULL; sqlite3_finalize (AccountDeleteStmt); AccountDeleteStmt = NULL; sqlite3_finalize (PeerGetStmt); PeerGetStmt = NULL; sqlite3_finalize (PeerInsertStmt); PeerInsertStmt = NULL; sqlite3_finalize (PeerUpdateStmt); PeerUpdateStmt = NULL; sqlite3_finalize (PeerHistoryInsertStmt); PeerHistoryInsertStmt = NULL; sqlite3_finalize (GossipHistoryInsertStmt); GossipHistoryInsertStmt = NULL; sqlite3_close_v2 (AutocryptDB); AutocryptDB = NULL; } void mutt_autocrypt_db_normalize_addrlist (ADDRESS *addrlist) { ADDRESS *addr; mutt_addrlist_to_local (addrlist); for (addr = addrlist; addr; addr = addr->next) ascii_strlower (addr->mailbox); mutt_addrlist_to_intl (addrlist, NULL); } /* The autocrypt spec says email addresses should be * normalized to lower case and stored in idna form. * * In order to avoid visible changes to addresses in the index, * we make a copy of the address before lowercasing it. * * The return value must be freed. */ static ADDRESS *copy_normalize_addr (ADDRESS *addr) { ADDRESS *norm_addr; /* NOTE: the db functions expect a single address, so in * this function we copy only the address passed in. * * The normalize_addrlist above is extended to work on a list * because of requirements in autocrypt.c */ norm_addr = rfc822_new_address (); norm_addr->mailbox = safe_strdup (addr->mailbox); norm_addr->is_intl = addr->is_intl; norm_addr->intl_checked = addr->intl_checked; mutt_autocrypt_db_normalize_addrlist (norm_addr); return norm_addr; } /* Helper that converts to char * and safe_strdups the result */ static char *strdup_column_text (sqlite3_stmt *stmt, int index) { const char *val = (const char *)sqlite3_column_text (stmt, index); return safe_strdup (val); } AUTOCRYPT_ACCOUNT *mutt_autocrypt_db_account_new (void) { return safe_calloc (1, sizeof(AUTOCRYPT_ACCOUNT)); } void mutt_autocrypt_db_account_free (AUTOCRYPT_ACCOUNT **account) { if (!account || !*account) return; FREE (&(*account)->email_addr); FREE (&(*account)->keyid); FREE (&(*account)->keydata); FREE (account); /* __FREE_CHECKED__ */ } int mutt_autocrypt_db_account_get (ADDRESS *addr, AUTOCRYPT_ACCOUNT **account) { int rv = -1, result; ADDRESS *norm_addr = NULL; norm_addr = copy_normalize_addr (addr); *account = NULL; if (!AccountGetStmt) { if (sqlite3_prepare_v3 ( AutocryptDB, "SELECT " "email_addr, " "keyid, " "keydata, " "prefer_encrypt, " "enabled " "FROM account " "WHERE email_addr = ?", -1, SQLITE_PREPARE_PERSISTENT, &AccountGetStmt, NULL) != SQLITE_OK) goto cleanup; } if (sqlite3_bind_text (AccountGetStmt, 1, norm_addr->mailbox, -1, SQLITE_STATIC) != SQLITE_OK) goto cleanup; result = sqlite3_step (AccountGetStmt); if (result != SQLITE_ROW) { if (result == SQLITE_DONE) rv = 0; goto cleanup; } *account = mutt_autocrypt_db_account_new (); (*account)->email_addr = strdup_column_text (AccountGetStmt, 0); (*account)->keyid = strdup_column_text (AccountGetStmt, 1); (*account)->keydata = strdup_column_text (AccountGetStmt, 2); (*account)->prefer_encrypt = sqlite3_column_int (AccountGetStmt, 3); (*account)->enabled = sqlite3_column_int (AccountGetStmt, 4); rv = 1; cleanup: rfc822_free_address (&norm_addr); sqlite3_reset (AccountGetStmt); return rv; } int mutt_autocrypt_db_account_insert (ADDRESS *addr, const char *keyid, const char *keydata, int prefer_encrypt) { int rv = -1; ADDRESS *norm_addr = NULL; norm_addr = copy_normalize_addr (addr); if (!AccountInsertStmt) { if (sqlite3_prepare_v3 ( AutocryptDB, "INSERT INTO account " "(email_addr, " "keyid, " "keydata, " "prefer_encrypt, " "enabled) " "VALUES (?, ?, ?, ?, ?);", -1, SQLITE_PREPARE_PERSISTENT, &AccountInsertStmt, NULL) != SQLITE_OK) goto cleanup; } if (sqlite3_bind_text (AccountInsertStmt, 1, norm_addr->mailbox, -1, SQLITE_STATIC) != SQLITE_OK) goto cleanup; if (sqlite3_bind_text (AccountInsertStmt, 2, keyid, -1, SQLITE_STATIC) != SQLITE_OK) goto cleanup; if (sqlite3_bind_text (AccountInsertStmt, 3, keydata, -1, SQLITE_STATIC) != SQLITE_OK) goto cleanup; if (sqlite3_bind_int (AccountInsertStmt, 4, prefer_encrypt) != SQLITE_OK) goto cleanup; if (sqlite3_bind_int (AccountInsertStmt, 5, 1) != SQLITE_OK) goto cleanup; if (sqlite3_step (AccountInsertStmt) != SQLITE_DONE) goto cleanup; rv = 0; cleanup: rfc822_free_address (&norm_addr); sqlite3_reset (AccountInsertStmt); return rv; } int mutt_autocrypt_db_account_update (AUTOCRYPT_ACCOUNT *acct) { int rv = -1; if (!AccountUpdateStmt) { if (sqlite3_prepare_v3 ( AutocryptDB, "UPDATE account SET " "keyid = ?, " "keydata = ?, " "prefer_encrypt = ?, " "enabled = ? " "WHERE email_addr = ?;", -1, SQLITE_PREPARE_PERSISTENT, &AccountUpdateStmt, NULL) != SQLITE_OK) goto cleanup; } if (sqlite3_bind_text (AccountUpdateStmt, 1, acct->keyid, -1, SQLITE_STATIC) != SQLITE_OK) goto cleanup; if (sqlite3_bind_text (AccountUpdateStmt, 2, acct->keydata, -1, SQLITE_STATIC) != SQLITE_OK) goto cleanup; if (sqlite3_bind_int (AccountUpdateStmt, 3, acct->prefer_encrypt) != SQLITE_OK) goto cleanup; if (sqlite3_bind_int (AccountUpdateStmt, 4, acct->enabled) != SQLITE_OK) goto cleanup; if (sqlite3_bind_text (AccountUpdateStmt, 5, acct->email_addr, -1, SQLITE_STATIC) != SQLITE_OK) goto cleanup; if (sqlite3_step (AccountUpdateStmt) != SQLITE_DONE) goto cleanup; rv = 0; cleanup: sqlite3_reset (AccountUpdateStmt); return rv; } int mutt_autocrypt_db_account_delete (AUTOCRYPT_ACCOUNT *acct) { int rv = -1; if (!AccountDeleteStmt) { if (sqlite3_prepare_v3 ( AutocryptDB, "DELETE from account " "WHERE email_addr = ?;", -1, SQLITE_PREPARE_PERSISTENT, &AccountDeleteStmt, NULL) != SQLITE_OK) goto cleanup; } if (sqlite3_bind_text (AccountDeleteStmt, 1, acct->email_addr, -1, SQLITE_STATIC) != SQLITE_OK) goto cleanup; if (sqlite3_step (AccountDeleteStmt) != SQLITE_DONE) goto cleanup; rv = 0; cleanup: sqlite3_reset (AccountDeleteStmt); return rv; } int mutt_autocrypt_db_account_get_all (AUTOCRYPT_ACCOUNT ***accounts, int *num_accounts) { int rv = -1, result; sqlite3_stmt *stmt = NULL; AUTOCRYPT_ACCOUNT **results = NULL, *account; int results_len = 0, results_count = 0; *accounts = NULL; *num_accounts = 0; /* Note, speed is not of the essence for the account management screen, * so we don't bother with a persistent prepared statement */ if (sqlite3_prepare_v2 ( AutocryptDB, "SELECT " "email_addr, " "keyid, " "keydata, " "prefer_encrypt, " "enabled " "FROM account " "ORDER BY email_addr", -1, &stmt, NULL) != SQLITE_OK) goto cleanup; while ((result = sqlite3_step (stmt)) == SQLITE_ROW) { if (results_count == results_len) { results_len += 5; safe_realloc (&results, results_len * sizeof(AUTOCRYPT_ACCOUNT *)); } results[results_count++] = account = mutt_autocrypt_db_account_new (); account->email_addr = strdup_column_text (stmt, 0); account->keyid = strdup_column_text (stmt, 1); account->keydata = strdup_column_text (stmt, 2); account->prefer_encrypt = sqlite3_column_int (stmt, 3); account->enabled = sqlite3_column_int (stmt, 4); } if (result == SQLITE_DONE) { *accounts = results; rv = *num_accounts = results_count; } else { while (results_count > 0) mutt_autocrypt_db_account_free (&results[--results_count]); FREE (&results); } cleanup: sqlite3_finalize (stmt); 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; ADDRESS *norm_addr = NULL; norm_addr = copy_normalize_addr (addr); *peer = NULL; if (!PeerGetStmt) { if (sqlite3_prepare_v3 ( AutocryptDB, "SELECT " "email_addr, " "last_seen, " "autocrypt_timestamp, " "keyid, " "keydata, " "prefer_encrypt, " "gossip_timestamp, " "gossip_keyid, " "gossip_keydata " "FROM peer " "WHERE email_addr = ?", -1, SQLITE_PREPARE_PERSISTENT, &PeerGetStmt, NULL) != SQLITE_OK) goto cleanup; } if (sqlite3_bind_text (PeerGetStmt, 1, norm_addr->mailbox, -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: rfc822_free_address (&norm_addr); sqlite3_reset (PeerGetStmt); return rv; } int mutt_autocrypt_db_peer_insert (ADDRESS *addr, AUTOCRYPT_PEER *peer) { int rv = -1; ADDRESS *norm_addr = NULL; norm_addr = copy_normalize_addr (addr); if (!PeerInsertStmt) { if (sqlite3_prepare_v3 ( AutocryptDB, "INSERT INTO peer " "(email_addr, " "last_seen, " "autocrypt_timestamp, " "keyid, " "keydata, " "prefer_encrypt, " "gossip_timestamp, " "gossip_keyid, " "gossip_keydata) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);", -1, SQLITE_PREPARE_PERSISTENT, &PeerInsertStmt, NULL) != SQLITE_OK) goto cleanup; } if (sqlite3_bind_text (PeerInsertStmt, 1, norm_addr->mailbox, -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: rfc822_free_address (&norm_addr); sqlite3_reset (PeerInsertStmt); return rv; } int mutt_autocrypt_db_peer_update (AUTOCRYPT_PEER *peer) { int rv = -1; if (!PeerUpdateStmt) { if (sqlite3_prepare_v3 ( AutocryptDB, "UPDATE peer SET " "last_seen = ?, " "autocrypt_timestamp = ?, " "keyid = ?, " "keydata = ?, " "prefer_encrypt = ?, " "gossip_timestamp = ?, " "gossip_keyid = ?, " "gossip_keydata = ? " "WHERE email_addr = ?;", -1, SQLITE_PREPARE_PERSISTENT, &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, peer->email_addr, -1, SQLITE_STATIC) != SQLITE_OK) goto cleanup; if (sqlite3_step (PeerUpdateStmt) != SQLITE_DONE) goto cleanup; rv = 0; cleanup: 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; ADDRESS *norm_addr = NULL; norm_addr = copy_normalize_addr (addr); if (!PeerHistoryInsertStmt) { if (sqlite3_prepare_v3 ( AutocryptDB, "INSERT INTO peer_history " "(peer_email_addr, " "email_msgid, " "timestamp, " "keydata) " "VALUES (?, ?, ?, ?);", -1, SQLITE_PREPARE_PERSISTENT, &PeerHistoryInsertStmt, NULL) != SQLITE_OK) goto cleanup; } if (sqlite3_bind_text (PeerHistoryInsertStmt, 1, norm_addr->mailbox, -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: rfc822_free_address (&norm_addr); sqlite3_reset (PeerHistoryInsertStmt); return rv; } AUTOCRYPT_GOSSIP_HISTORY *mutt_autocrypt_db_gossip_history_new (void) { return safe_calloc (1, sizeof(AUTOCRYPT_GOSSIP_HISTORY)); } void mutt_autocrypt_db_gossip_history_free (AUTOCRYPT_GOSSIP_HISTORY **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); /* __FREE_CHECKED__ */ } int mutt_autocrypt_db_gossip_history_insert (ADDRESS *addr, AUTOCRYPT_GOSSIP_HISTORY *gossip_hist) { int rv = -1; ADDRESS *norm_addr = NULL; norm_addr = copy_normalize_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, norm_addr->mailbox, -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: rfc822_free_address (&norm_addr); sqlite3_reset (GossipHistoryInsertStmt); return rv; }