bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "lib.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "array.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "str.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "hex-binary.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "base64.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "mail-user.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "mail-crypt-common.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "mail-crypt-key.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "mail-crypt-plugin.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiint mail_crypt_load_global_public_key(const char *set_key, const char *key_data,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_crypt_global_keys *global_keys,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char **error_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *error;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi enum dcrypt_key_format format;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi enum dcrypt_key_kind kind;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!dcrypt_key_string_get_info(key_data, &format, NULL,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi &kind, NULL, NULL, NULL, &error)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi key_data = str_c(t_base64_decode_str(key_data));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!dcrypt_key_string_get_info(key_data, &format, NULL,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi &kind, NULL, NULL, NULL, &error)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = t_strdup_printf("%s: Couldn't parse public key: %s",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi set_key, error);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (kind != DCRYPT_KEY_KIND_PUBLIC) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = t_strdup_printf("%s: key is not public", set_key);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!dcrypt_key_load_public(&global_keys->public_key, key_data, &error)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = t_strdup_printf("%s: Couldn't load public key: %s",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi set_key, error);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return 0;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimail_crypt_key_get_ids(struct dcrypt_private_key *key,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char **key_id_r, const char **key_id_old_r,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char **error_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *error;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi buffer_t *key_id;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *key_id_r = NULL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *key_id_old_r = NULL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* new key ID */
c147bff818798a979d93537f72f5c1f68f5d5ba8Aki Tuomi key_id = t_buffer_create(MAIL_CRYPT_HASH_BUF_SIZE);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!dcrypt_key_id_private(key, MAIL_CRYPT_KEY_ID_ALGORITHM, key_id, &error)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = t_strdup_printf("Failed to get private key ID: %s", error);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *key_id_r = binary_to_hex(key_id->data, key_id->used);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi buffer_set_used_size(key_id, 0);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* old key ID */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (dcrypt_key_type_private(key) != DCRYPT_KEY_EC)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return 0;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!dcrypt_key_id_private_old(key, key_id, &error)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = t_strdup_printf("Failed to get private key old ID: %s",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi error);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *key_id_old_r = binary_to_hex(key_id->data, key_id->used);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return 0;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiint mail_crypt_load_global_private_key(const char *set_key, const char *key_data,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *set_pw, const char *key_password,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_crypt_global_keys *global_keys,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char **error_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi enum dcrypt_key_format format;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi enum dcrypt_key_kind kind;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi enum dcrypt_key_encryption_type enc_type;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *error;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!dcrypt_key_string_get_info(key_data, &format, NULL, &kind,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi &enc_type, NULL, NULL, &error)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi key_data = str_c(t_base64_decode_str(key_data));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!dcrypt_key_string_get_info(key_data, &format, NULL, &kind,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi &enc_type, NULL, NULL, &error)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = t_strdup_printf("%s: Couldn't parse private"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi " key: %s", set_key, error);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (kind != DCRYPT_KEY_KIND_PRIVATE) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = t_strdup_printf("%s: key is not private", set_key);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* Fail here if password is not set since openssl will prompt
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi * for it otherwise */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (key_password == NULL) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (error_r != NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = t_strdup_printf("%s: %s unset, no "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "password to decrypt the key",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi set_key, set_pw);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct dcrypt_private_key *key = NULL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!dcrypt_key_load_private(&key, key_data, key_password, NULL, &error)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = t_strdup_printf("%s: Couldn't load private key: %s",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi set_key, error);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *key_id, *key_id_old;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (mail_crypt_key_get_ids(key, &key_id, &key_id_old, error_r) < 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dcrypt_key_unref_private(&key);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_crypt_global_private_key *priv_key =
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi array_append_space(&global_keys->private_keys);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi priv_key->key = key;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi priv_key->key_id = i_strdup(key_id);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi priv_key->key_id_old = i_strdup(key_id_old);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return 0;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomivoid mail_crypt_global_keys_init(struct mail_crypt_global_keys *global_keys_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(global_keys_r);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_array_init(&global_keys_r->private_keys, 4);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomivoid mail_crypt_global_keys_free(struct mail_crypt_global_keys *global_keys)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_crypt_global_private_key *priv_key;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (global_keys->public_key != NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dcrypt_key_unref_public(&global_keys->public_key);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!array_is_created(&global_keys->private_keys))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi array_foreach_modifiable(&global_keys->private_keys, priv_key) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dcrypt_key_unref_private(&priv_key->key);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_free(priv_key->key_id);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_free(priv_key->key_id_old);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi array_free(&global_keys->private_keys);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistruct dcrypt_private_key *
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimail_crypt_global_key_find(struct mail_crypt_global_keys *global_keys,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *pubkey_digest)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const struct mail_crypt_global_private_key *priv_key;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
e41e5d5456fb6c78693a385239ef5b0cbbd57408Aki Tuomi if (!array_is_created(&global_keys->private_keys))
e41e5d5456fb6c78693a385239ef5b0cbbd57408Aki Tuomi return NULL;
e41e5d5456fb6c78693a385239ef5b0cbbd57408Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi array_foreach(&global_keys->private_keys, priv_key) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strcmp(priv_key->key_id, pubkey_digest) == 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return priv_key->key;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (priv_key->key_id_old != NULL &&
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi strcmp(priv_key->key_id_old, pubkey_digest) == 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return priv_key->key;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return NULL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}