Generate gpg key and add account record to the database.
Co-authored-by: Richard Russon <rich@flatcap.org>
#include <sys/types.h>
#include <unistd.h>
#include "autocrypt_private.h"
+#include "address/lib.h"
#include "mutt.h"
#include "autocrypt.h"
#include "curs_lib.h"
#include "globals.h"
+#include "mutt_curses.h"
+#include "send.h"
static int autocrypt_dir_init(int can_create)
{
{
mutt_autocrypt_db_close();
}
+
+/* Creates a brand new account the first time autocrypt is initialized */
+int mutt_autocrypt_account_init(void)
+{
+ struct Address *addr = NULL;
+ struct AutocryptAccount *account = NULL;
+ bool done = false;
+ int rc = -1;
+
+ mutt_debug(LL_DEBUG1, "In mutt_autocrypt_account_init\n");
+ if (mutt_yesorno(_("Create an initial autocrypt account?"), MUTT_YES) != MUTT_YES)
+ return 0;
+
+ struct Buffer *keyid = mutt_buffer_pool_get();
+ struct Buffer *keydata = mutt_buffer_pool_get();
+
+ if (C_From)
+ {
+ addr = mutt_addr_copy(C_From);
+ if (!addr->personal && C_Realname)
+ addr->personal = mutt_str_strdup(C_Realname);
+ }
+
+ struct AddressList al = TAILQ_HEAD_INITIALIZER(al);
+ mutt_addrlist_append(&al, addr);
+
+ do
+ {
+ if (mutt_edit_address(&al, _("Autocrypt account address: "), 0) != 0)
+ goto cleanup;
+
+ addr = TAILQ_FIRST(&al);
+ if (!addr || !addr->mailbox || TAILQ_NEXT(addr, entries))
+ {
+ /* L10N:
+ Autocrypt prompts for an account email address, and requires
+ a single address.
+ */
+ mutt_error(_("Please enter a single email address"));
+ done = false;
+ }
+ else
+ done = true;
+ } while (!done);
+
+ addr = TAILQ_FIRST(&al);
+ if (mutt_autocrypt_db_account_get(addr, &account) < 0)
+ goto cleanup;
+ if (account)
+ {
+ mutt_error(_("That email address already has an autocrypt account"));
+ goto cleanup;
+ }
+
+ if (mutt_autocrypt_gpgme_create_key(addr, keyid, keydata))
+ goto cleanup;
+
+ /* TODO: prompt for prefer_encrypt value? */
+ if (mutt_autocrypt_db_account_insert(addr, mutt_b2s(keyid), mutt_b2s(keydata), 0))
+ goto cleanup;
+
+ rc = 0;
+
+cleanup:
+ if (rc == 0)
+ mutt_message(_("Autocrypt account creation succeeded"));
+ else
+ mutt_error(_("Autocrypt account creation aborted"));
+
+ mutt_autocrypt_db_account_free(&account);
+ mutt_addrlist_clear(&al);
+ mutt_buffer_pool_release(&keyid);
+ mutt_buffer_pool_release(&keydata);
+ return rc;
+}
WHERE sqlite3 *AutocryptDB;
+struct AutocryptAccount
+{
+ char *email_addr;
+ char *keyid;
+ char *keydata;
+ int prefer_encrypt; /* 0 = nopref, 1 = mutual */
+ int enabled;
+};
+
int mutt_autocrypt_init (int);
void mutt_autocrypt_cleanup (void);
#include <sys/types.h>
#include <unistd.h>
#include "autocrypt_private.h"
+#include "address/lib.h"
#include "mutt.h"
#include "autocrypt.h"
#include "globals.h"
db_path = mutt_buffer_pool_get();
mutt_buffer_concat_path(db_path, C_AutocryptDir, "autocrypt.db");
+
if (stat(mutt_b2s(db_path), &sb))
{
- if (!can_create || autocrypt_db_create(mutt_b2s(db_path)))
+ 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();
}
else
{
mutt_error(_("Unable to open autocrypt database %s"), mutt_b2s(db_path));
goto cleanup;
}
- }
- if (mutt_autocrypt_schema_update())
- goto cleanup;
+ if (mutt_autocrypt_schema_update())
+ goto cleanup;
+ }
rv = 0;
if (!AutocryptDB)
return;
- /* TODO:
- * call sqlite3_finalize () for each prepared statement
- */
+ sqlite3_finalize(AccountGetStmt);
+ AccountGetStmt = NULL;
+ sqlite3_finalize(AccountInsertStmt);
+ AccountInsertStmt = NULL;
sqlite3_close_v2(AutocryptDB);
AutocryptDB = 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 char *normalize_email_addr(struct Address *addr)
+{
+ struct AddressList al = TAILQ_HEAD_INITIALIZER(al);
+ struct Address *norm_addr = mutt_addr_copy(addr);
+ mutt_addrlist_append(&al, norm_addr);
+
+ mutt_addrlist_to_local(&al);
+ mutt_str_strlower(norm_addr->mailbox);
+ mutt_addrlist_to_intl(&al, NULL);
+
+ char *email = mutt_str_strdup(TAILQ_FIRST(&al)->mailbox);
+ mutt_addrlist_clear(&al);
+
+ return email;
+}
+
+/* Helper that converts to char * and mutt_str_strdups the result */
+static char *strdup_column_text(sqlite3_stmt *stmt, int index)
+{
+ const char *val = (const char *) sqlite3_column_text(stmt, index);
+ return mutt_str_strdup(val);
+}
+
+struct AutocryptAccount *mutt_autocrypt_db_account_new(void)
+{
+ return mutt_mem_calloc(1, sizeof(struct AutocryptAccount));
+}
+
+void mutt_autocrypt_db_account_free(struct AutocryptAccount **account)
+{
+ if (!account || !*account)
+ return;
+ FREE(&(*account)->email_addr);
+ FREE(&(*account)->keyid);
+ FREE(&(*account)->keydata);
+ FREE(account);
+}
+
+int mutt_autocrypt_db_account_get(struct Address *addr, struct AutocryptAccount **account)
+{
+ int rv = -1, result;
+ char *email = NULL;
+
+ email = normalize_email_addr(addr);
+ *account = NULL;
+
+ if (!AccountGetStmt)
+ {
+ if (sqlite3_prepare_v2(AutocryptDB,
+ "SELECT "
+ "email_addr, "
+ "keyid, "
+ "keydata, "
+ "prefer_encrypt, "
+ "enabled "
+ "FROM account "
+ "WHERE email_addr = ?",
+ -1, &AccountGetStmt, NULL) != SQLITE_OK)
+ goto cleanup;
+ }
+
+ if (sqlite3_bind_text(AccountGetStmt, 1, email, -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:
+ FREE(&email);
+ sqlite3_reset(AccountGetStmt);
+ return rv;
+}
+
+int mutt_autocrypt_db_account_insert(struct Address *addr, const char *keyid,
+ const char *keydata, int prefer_encrypt)
+{
+ int rv = -1;
+ char *email = NULL;
+
+ email = normalize_email_addr(addr);
+
+ if (!AccountInsertStmt)
+ {
+ if (sqlite3_prepare_v2(AutocryptDB,
+ "INSERT INTO account "
+ "(email_addr, "
+ "keyid, "
+ "keydata, "
+ "prefer_encrypt, "
+ "enabled) "
+ "VALUES (?, ?, ?, ?, ?);",
+ -1, &AccountInsertStmt, NULL) != SQLITE_OK)
+ goto cleanup;
+ }
+
+ if (sqlite3_bind_text(AccountInsertStmt, 1, email, -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:
+ FREE(&email);
+ sqlite3_reset(AccountInsertStmt);
+ return rv;
+}
*/
#include "config.h"
+#include <gpgme.h>
#include "autocrypt_private.h"
+#include "address/lib.h"
#include "mutt.h"
#include "autocrypt.h"
+#include "globals.h"
#include "ncrypt/crypt_gpgme.h"
+static int create_gpgme_context(gpgme_ctx_t *ctx)
+{
+ gpgme_error_t err;
+
+ err = gpgme_new(ctx);
+ if (!err)
+ err = gpgme_ctx_set_engine_info(*ctx, GPGME_PROTOCOL_OpenPGP, NULL, C_AutocryptDir);
+ if (err)
+ {
+ mutt_error(_("error creating gpgme context: %s\n"), gpgme_strerror(err));
+ return -1;
+ }
+
+ return 0;
+}
+
int mutt_autocrypt_gpgme_init(void)
{
pgp_gpgme_init();
return 0;
}
+
+static int export_keydata(gpgme_ctx_t ctx, gpgme_key_t key, struct Buffer *keydata)
+{
+ int rv = -1;
+ gpgme_data_t dh = NULL;
+ gpgme_key_t export_keys[2];
+ size_t export_data_len;
+
+ if (gpgme_data_new(&dh))
+ goto cleanup;
+
+ /* This doesn't seem to work */
+#if 0
+ if (gpgme_data_set_encoding (dh, GPGME_DATA_ENCODING_BASE64))
+ goto cleanup;
+#endif
+
+ export_keys[0] = key;
+ export_keys[1] = NULL;
+ if (gpgme_op_export_keys(ctx, export_keys, GPGME_EXPORT_MODE_MINIMAL, dh))
+ goto cleanup;
+
+ char *export_data = gpgme_data_release_and_get_mem(dh, &export_data_len);
+ dh = NULL;
+
+ mutt_b64_buffer_encode(keydata, export_data, export_data_len);
+ gpgme_free(export_data);
+ export_data = NULL;
+
+ rv = 0;
+
+cleanup:
+ gpgme_data_release(dh);
+ return rv;
+}
+
+/* TODO: not sure if this function will be useful in the future. */
+int mutt_autocrypt_gpgme_export_key(const char *keyid, struct Buffer *keydata)
+{
+ int rv = -1;
+ gpgme_ctx_t ctx = NULL;
+ gpgme_key_t key = NULL;
+
+ if (create_gpgme_context(&ctx))
+ goto cleanup;
+
+ if (gpgme_get_key(ctx, keyid, &key, 0))
+ goto cleanup;
+
+ if (export_keydata(ctx, key, keydata))
+ goto cleanup;
+
+ rv = 0;
+cleanup:
+ gpgme_key_unref(key);
+ gpgme_release(ctx);
+ return rv;
+}
+
+int mutt_autocrypt_gpgme_create_key(struct Address *addr, struct Buffer *keyid,
+ struct Buffer *keydata)
+{
+ int rv = -1;
+ gpgme_ctx_t ctx = NULL;
+ gpgme_error_t err;
+ gpgme_genkey_result_t keyresult;
+ gpgme_key_t primary_key = NULL;
+ char buf[1024] = { 0 };
+
+ /* gpgme says addresses should not be in idna form */
+ struct Address *copy = mutt_addr_copy(addr);
+ mutt_addr_to_local(copy);
+ mutt_addr_write(buf, sizeof(buf), copy, false);
+ mutt_addr_free(©);
+
+ if (create_gpgme_context(&ctx))
+ goto cleanup;
+
+ mutt_message(_("Generating autocrypt key..."));
+
+ /* Primary key */
+ err = gpgme_op_createkey(ctx, buf, "ed25519", 0, 0, NULL,
+ GPGME_CREATE_NOPASSWD | GPGME_CREATE_FORCE | GPGME_CREATE_NOEXPIRE);
+ if (err)
+ {
+ mutt_error(_("Error creating autocrypt key: %s\n"), gpgme_strerror(err));
+ goto cleanup;
+ }
+ keyresult = gpgme_op_genkey_result(ctx);
+ if (!keyresult->fpr)
+ goto cleanup;
+ mutt_buffer_strcpy(keyid, keyresult->fpr);
+ mutt_debug(LL_DEBUG1, "Generated key with id %s\n", mutt_b2s(keyid));
+
+ /* Get gpgme_key_t to create the secondary key and export keydata */
+ err = gpgme_get_key(ctx, mutt_b2s(keyid), &primary_key, 0);
+ if (err)
+ goto cleanup;
+
+ /* Secondary key */
+ err = gpgme_op_createsubkey(ctx, primary_key, "cv25519", 0, 0,
+ GPGME_CREATE_NOPASSWD | GPGME_CREATE_NOEXPIRE);
+ if (err)
+ {
+ mutt_error(_("Error creating autocrypt key: %s\n"), gpgme_strerror(err));
+ goto cleanup;
+ }
+
+ /* get keydata */
+ if (export_keydata(ctx, primary_key, keydata))
+ goto cleanup;
+ mutt_debug(LL_DEBUG1, "key has keydata *%s*\n", mutt_b2s(keydata));
+
+ rv = 0;
+
+cleanup:
+ gpgme_key_unref(primary_key);
+ gpgme_release(ctx);
+ return rv;
+}
#ifndef MUTT_AUTOCRYPT_AUTOCRYPT_PRIVATE_H
#define MUTT_AUTOCRYPT_AUTOCRYPT_PRIVATE_H
-int mutt_autocrypt_db_init(int can_create);
-void mutt_autocrypt_db_close(void);
+#include <sqlite3.h>
-int mutt_autocrypt_schema_init(void);
-int mutt_autocrypt_schema_update(void);
+struct Address;
+struct Buffer;
-int mutt_autocrypt_gpgme_init(void);
+int mutt_autocrypt_account_init (void);
+
+int mutt_autocrypt_db_init (int can_create);
+void mutt_autocrypt_db_close (void);
+
+struct AutocryptAccount *mutt_autocrypt_db_account_new (void);
+void mutt_autocrypt_db_account_free (struct AutocryptAccount **account);
+int mutt_autocrypt_db_account_get (struct Address *addr, struct AutocryptAccount **account);
+int mutt_autocrypt_db_account_insert (struct Address *addr, const char *keyid,
+ const char *keydata, int prefer_encrypt);
+
+int mutt_autocrypt_schema_init (void);
+int mutt_autocrypt_schema_update (void);
+
+int mutt_autocrypt_gpgme_init (void);
+int mutt_autocrypt_gpgme_create_key (struct Address *addr, struct Buffer *keyid, struct Buffer *keydata);
+
+/* Prepared statements */
+sqlite3_stmt *AccountGetStmt;
+sqlite3_stmt *AccountInsertStmt;
#endif /* MUTT_AUTOCRYPT_AUTOCRYPT_PRIVATE_H */
/* Initialize crypto backends. */
crypt_init();
-#ifdef USE_AUTOCRYPT
- if (C_Autocrypt)
- mutt_autocrypt_init(!(sendflags & SEND_BATCH));
-#endif
if (new_magic)
{
log_queue_set_max_size(100);
}
+ /* Initialize autocrypt after curses messages are working,
+ * because of the initial account setup screens. */
+#ifdef USE_AUTOCRYPT
+ if (C_Autocrypt)
+ mutt_autocrypt_init(!(sendflags & SEND_BATCH));
+#endif
+
/* Create the C_Folder directory if it doesn't exist. */
if (!OptNoCurses && C_Folder)
{