mail-crypt-key.c revision bcb4e51a409d94ae670de96afb8483a4f7855294
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* this is lazily initialized */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mail_crypt_get_key_cache(struct mail_crypt_key_cache_entry *cache,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for(struct mail_crypt_key_cache_entry *ent = cache;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (privkey_r != NULL && ent->pair.priv != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (pubkey_r != NULL && ent->pair.pub != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if ((privkey_r == NULL && pubkey_r == NULL) ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid mail_crypt_put_key_cache(struct mail_crypt_key_cache_entry **cache,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for(struct mail_crypt_key_cache_entry *ent = *cache;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* not found */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid mail_crypt_key_cache_destroy(struct mail_crypt_key_cache_entry **cache)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_crypt_key_cache_entry *next, *cur = *cache;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mail_crypt_private_key_id_match(struct dcrypt_private_key *key,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!dcrypt_key_id_private(key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *hash = binary_to_hex(key_id->data, key_id->used);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!dcrypt_key_id_private_old(key, key_id, error_r)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen hash = binary_to_hex(key_id->data, key_id->used);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("Key %s does not match given ID %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mail_crypt_public_key_id_match(struct dcrypt_public_key *key,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen buffer_t *key_id = t_str_new(MAIL_CRYPT_HASH_BUF_SIZE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!dcrypt_key_id_public(key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *hash = binary_to_hex(key_id->data, key_id->used);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!dcrypt_key_id_public_old(key, key_id, error_r)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen hash = binary_to_hex(key_id->data, key_id->used);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("Key %s does not match given ID %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mail_crypt_env_get_private_key(struct mail_user *user, const char *pubid,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char **error_r)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mail_crypt_global_keys_load(user, "mail_crypt", &global_keys,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* see if we got a key */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_crypt_global_key_find(&global_keys, pubid);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenconst char *mail_crypt_get_key_path(bool user_key, bool public, const char *pubid)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mail_crypt_decrypt_private_key(struct mailbox *box, const char *pubid,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char **error_r)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *enc_hash = NULL, *key_hash = NULL, *pw = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct dcrypt_private_key *key = NULL, *dec_key = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* see what the key needs for decrypting */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!dcrypt_key_string_get_info(data, NULL, NULL, &key_kind,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("Cannot use key %s: "
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Expected private key, got public key",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (key_hash != NULL && strcmp(key_hash, pubid) != 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("Cannot use key %s: "
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Incorrect key hash %s stored",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* see if it needs decrypting */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* no key or password */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen pw = mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_PASSWORD);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("Cannot decrypt key %s: "
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Password not available",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_KEY) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((ret = mail_crypt_user_get_private_key(user, enc_hash,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* last resort, look at environment */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret == 0 && (ret = mail_crypt_env_get_private_key(user, enc_hash,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("Cannot decrypt key %s: "
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Private key %s not available:",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (ret < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("Cannot decrypt key %s: %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bool res = dcrypt_key_load_private(&key, data, pw, dec_key, error_r);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mail_crypt_private_key_id_match(key, pubid, error_r) <= 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mail_crypt_get_private_key(struct mailbox *box, const char *pubid,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char **error_r)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_crypt_user *muser = mail_crypt_get_mail_crypt_user(user);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* check cache */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mail_crypt_get_key_cache(muser->key_cache, pubid, key_r, NULL) > 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *attr_name = mail_crypt_get_key_path(user_key, FALSE, pubid);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("mailbox_attribute_get(%s, %s%s) failed: %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((ret = mail_crypt_decrypt_private_key(box, pubid, value.value,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_crypt_put_key_cache(&muser->key_cache, pubid, key, NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mail_crypt_user_get_private_key(struct mail_user *user, const char *pubid,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char **error_r)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mailbox *box = mailbox_alloc(ns->list, "INBOX",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* try retrieve currently active user key */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("mailbox_open(%s) failed: %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((ret = mailbox_attribute_get(box, MAIL_ATTRIBUTE_TYPE_SHARED,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("mailbox_attribute_get(%s, /shared/%s) failed: %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* try to open key */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = mail_crypt_get_private_key(box, pubid, TRUE, FALSE,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mail_crypt_box_get_private_key(struct mailbox *box,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char **error_r)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* get active key */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((ret = mailbox_attribute_get(box, MAIL_ATTRIBUTE_TYPE_SHARED,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen BOX_CRYPT_PREFIX ACTIVE_KEY_NAME, &value)) <= 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("mailbox_attribute_get(%s, /shared/%s) failed: %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return mail_crypt_get_private_key(box, value.value,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mail_crypt_set_private_key(struct mailbox_transaction_context *t,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bool user_key, bool shared, const char *pubid,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char **error_r)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* folder keys must be encrypted with some other key,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unless they are shared keys */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(user_key || shared || enc_key != NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen buffer_t *data = t_str_new(MAIL_CRYPT_KEY_BUF_SIZE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_user *user = mail_storage_get_user(
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *attr_name = mail_crypt_get_key_path(user_key, FALSE, pubid);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (pw = mail_user_plugin_getenv(user,MAIL_CRYPT_USERENV_PASSWORD))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* export key */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!dcrypt_key_store_private(key, DCRYPT_FORMAT_DOVECOT, algo, data,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* store it */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("mailbox_attribute_set(%s, %s/%s) failed: %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mailbox_get_vname(mailbox_transaction_get_mailbox(t)),
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen safe_memset(buffer_get_modifiable_data(data, NULL), 0, data->used);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mail_crypt_user_set_private_key(struct mail_user *user, const char *pubid,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char **error_r)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_namespace *ns = mail_namespace_find_inbox(user->namespaces);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mailbox *box = mailbox_alloc(ns->list, "INBOX",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((ret = mail_crypt_env_get_private_key(user, NULL, &env_key,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (ret > 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen dcrypt_key_convert_private_to_public(env_key, &enc_key);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mail_user_plugin_getenv(user, MAIL_CRYPT_REQUIRE_ENCRYPTED_USER_KEY) != NULL &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_PASSWORD) == NULL &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_user_plugin_getenv(user, MAIL_CRYPT_USERENV_KEY) == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = MAIL_CRYPT_REQUIRE_ENCRYPTED_USER_KEY " set, cannot "
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "generate user keypair without password or key";
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("mailbox_open(%s) failed: %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen t = mailbox_transaction_begin(box, 0, __func__);
error_r)) < 0) {
return ret;
const char **error_r)
int ret;
struct mailbox_transaction_context *t;
return ret;
const char **error_r)
int ret;
if (ret < 0) {
return ret;
pubid);
const char **error_r)
int ret;
&value)) <= 0) {
if (ret < 0) {
return ret;
const char **error_r)
int ret;
&value)) <= 0) {
if (ret < 0) {
return ret;
const char **error_r)
error_r)) {
if (mailbox_attribute_set(t,
&value) < 0) {
const char **error_r)
struct mailbox_transaction_context *t;
int ret;
error_r)) == 0) {
&value)) < 0) {
if (ret < 0) {
} else if (mailbox_transaction_commit(&t) < 0) {
return ret;
const char **error_r)
int ret;
struct mailbox_transaction_context *t;
error_r)) == 0) {
&value)) < 0) {
if (ret < 0) {
} else if (mailbox_transaction_commit(&t) < 0) {
return ret;
const char *pubid,
const char **error_r)
const char *pubid,
const char **error_r)
error_r) < 0)
const char *pubid,
const char **error_r)
int ret;
const char *hexname =
pubid);
if (ret < 0) {
return ret;
error_r);
return ret;
const char *pubid,
const char *target_uid,
const char **error_r)
int ret;
const char *attr_name;
const char *hexname =
pubid);
pubid);
NULL));
return ret;
const char *pubid,
const char *target_uid,
const char **error_r)
int ret;
const char *hexname =
pubid);
attr_name)) <= 0) {
if (ret < 0) {
NULL));
return ret;
const char **pubid_r,
const char **error_r)
error_r)) {
const char **pubid_r,
const char **error_r)
const char **pubid_r,
const char **error_r)
int ret;
&user_key,
error_r)) <= 0) {
if (ret < 0)
return ret;
const char *user_pubid;
error_r) < 0) {
error_r)) < 0) {
return ret;
const char **error_r)
const char *key;
int ret;
if (ret < 0)
return ret;
const char **error_r)
const char *id;
int ret;
} else if (ret > 0)
if (ret < 0)
return ret;
const char *dest_user,
const char **error_r)
int ret = 0;
return ret;
const char **error_r)
int ret;
return ret;
const char **error_r)
int ret;
error_r) < 0) {
return ret;
const char *env =
switch (env[0]) {
return ret;