Add crypt_* settings, which will be shared with mail_crypt plugin.
endif
lib10_mail_crypt_plugin_la_SOURCES = \
+ crypt-settings.c \
mail-crypt-global-key.c \
mail-crypt-userenv.c \
mail-crypt-key.c \
mail-crypt-acl-plugin.c
libfs_crypt_la_SOURCES = fs-crypt.c \
+ crypt-settings.c \
mail-crypt-global-key.c \
mail-crypt-pluginenv.c \
fs-crypt-settings.c
test-fs-crypt.c \
fs-crypt-settings.c
test_fs_crypt_LDADD = $(LIBDOVECOT) \
+ crypt-settings.lo \
fs-crypt.lo \
mail-crypt-global-key.lo \
mail-crypt-pluginenv.lo
test_fs_crypt_DEPENDENCIES = $(LIBDOVECOT_DEPS) \
+ crypt-settings.lo \
fs-crypt.lo \
mail-crypt-global-key.lo \
mail-crypt-pluginenv.lo
test_mail_global_key_SOURCES = \
test-mail-global-key.c \
+ crypt-settings.c \
fs-crypt-settings.c \
mail-crypt-global-key.c
test_mail_global_key_LDADD = $(LIBDOVECOT)
test_mail_key_SOURCES = \
test-mail-key.c \
+ crypt-settings.c \
mail-crypt-key.c \
mail-crypt-global-key.c \
mail-crypt-userenv.c
test_mail_key_CFLAGS = $(AM_CPPFLAGS) $(DOVECOT_BINARY_CFLAGS) -Dtop_builddir=\"$(top_builddir)\"
noinst_HEADERS = \
+ crypt-settings.h \
mail-crypt-plugin.h \
mail-crypt-common.h \
mail-crypt-global-key.h \
--- /dev/null
+/* Copyright (c) 2023 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "settings-parser.h"
+#include "crypt-settings.h"
+
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct crypt_private_key_settings)
+static const struct setting_define crypt_private_key_setting_defines[] = {
+ DEF(STR, crypt_private_key_name),
+ DEF(STR, crypt_private_key),
+ DEF(STR, crypt_private_key_password),
+
+ SETTING_DEFINE_LIST_END
+};
+
+static const struct crypt_private_key_settings crypt_private_key_default_settings = {
+ .crypt_private_key_name = "",
+ .crypt_private_key = "",
+ .crypt_private_key_password = "",
+};
+
+const struct setting_parser_info crypt_private_key_setting_parser_info = {
+ .name = "crypt_private_key",
+
+ .defines = crypt_private_key_setting_defines,
+ .defaults = &crypt_private_key_default_settings,
+
+ .struct_size = sizeof(struct crypt_private_key_settings),
+ .pool_offset1 = 1 + offsetof(struct crypt_private_key_settings, pool),
+};
+
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct crypt_settings)
+static const struct setting_define crypt_setting_defines[] = {
+ DEF(STR, crypt_global_public_key),
+ { .type = SET_FILTER_ARRAY, .key = "crypt_global_private_key",
+ .offset = offsetof(struct crypt_settings, crypt_global_private_keys),
+ .filter_array_field_name = "crypt_private_key_name" },
+
+ DEF(STR, crypt_write_algorithm),
+ DEF(UINT, crypt_write_version),
+ DEF(BOOL, crypt_plain_fallback),
+
+ { .type = SET_FILTER_ARRAY, .key = "crypt_user_key_encryption_key",
+ .offset = offsetof(struct crypt_settings, crypt_user_key_encryption_keys),
+ .filter_array_field_name = "crypt_private_key_name" },
+ DEF(STR, crypt_user_key_password),
+ DEF(STR, crypt_user_key_curve),
+ DEF(BOOL, crypt_user_private_key_require_encrypted),
+
+ SETTING_DEFINE_LIST_END
+};
+
+static const struct crypt_settings crypt_default_settings = {
+ .crypt_global_public_key = "",
+ .crypt_global_private_keys = ARRAY_INIT,
+
+ .crypt_write_algorithm = "aes-256-gcm-sha256",
+ .crypt_write_version = UINT_MAX,
+ .crypt_plain_fallback = FALSE,
+
+ .crypt_user_key_encryption_keys = ARRAY_INIT,
+ .crypt_user_key_password = "",
+ .crypt_user_key_curve = "",
+ .crypt_user_private_key_require_encrypted = FALSE,
+};
+
+const struct setting_parser_info crypt_setting_parser_info = {
+ .name = "crypt",
+
+ .defines = crypt_setting_defines,
+ .defaults = &crypt_default_settings,
+
+ .struct_size = sizeof(struct crypt_settings),
+ .pool_offset1 = 1 + offsetof(struct crypt_settings, pool),
+};
--- /dev/null
+#ifndef CRYPT_SETTINGS_H
+#define CRYPT_SETTINGS_H
+
+struct crypt_private_key_settings {
+ pool_t pool;
+
+ const char *crypt_private_key_name;
+ const char *crypt_private_key;
+ const char *crypt_private_key_password;
+};
+
+struct crypt_settings {
+ pool_t pool;
+
+ const char *crypt_global_public_key;
+ ARRAY_TYPE(const_string) crypt_global_private_keys;
+
+ const char *crypt_write_algorithm;
+ unsigned int crypt_write_version;
+ bool crypt_plain_fallback;
+
+ /* for user-specific keys: */
+ ARRAY_TYPE(const_string) crypt_user_key_encryption_keys;
+ const char *crypt_user_key_password;
+ const char *crypt_user_key_curve; /* for generating new user keys */
+ bool crypt_user_private_key_require_encrypted;
+};
+
+extern const struct setting_parser_info crypt_setting_parser_info;
+extern const struct setting_parser_info crypt_private_key_setting_parser_info;
+
+#endif
#include "istream.h"
#include "istream-try.h"
#include "ostream.h"
+#include "settings.h"
#include "dcrypt-iostream.h"
#include "istream-decrypt.h"
#include "ostream-encrypt.h"
struct fs fs;
struct mail_crypt_global_keys keys;
bool keys_loaded;
- bool allow_missing_keys;
- char *enc_algo;
- char *set_prefix;
- char *public_key_path;
- char *private_key_path;
- char *password;
+ const struct crypt_settings *set;
};
struct crypt_fs_file {
}
static int
-fs_crypt_init(struct fs *_fs, const char *args,
- const struct fs_parameters *params, const char **error_r)
+fs_crypt_init(struct fs *_fs, const struct fs_parameters *params,
+ const char **error_r)
{
struct crypt_fs *fs = CRYPT_FS(_fs);
- const char *enc_algo, *set_prefix;
- const char *p, *arg, *value, *error, *parent_name, *parent_args;
- const char *public_key_path = "", *private_key_path = "", *password = "";
+ const char *error;
if (!dcrypt_initialize("openssl", NULL, &error))
i_fatal("dcrypt_initialize(): %s", error);
- /* [algo=<s>:][set_prefix=<n>:][public_key_path=<s>:]
- [private_key_path=<s>:[password=<s>:]]<parent fs> */
- set_prefix = "mail_crypt_global";
- enc_algo = "aes-256-gcm-sha256";
- for (;;) {
- p = strchr(args, ':');
- if (p == NULL)
- break;
- arg = t_strdup_until(args, p);
- if (strcmp(arg, "maybe") == 0) {
- fs->allow_missing_keys = TRUE;
- args = p + 1;
- continue;
- } else if ((value = strchr(arg, '=')) == NULL)
- break;
- arg = t_strdup_until(arg, value++);
- args = p+1;
-
- if (strcmp(arg, "algo") == 0)
- enc_algo = value;
- else if (strcmp(arg, "set_prefix") == 0)
- set_prefix = value;
- else if (strcmp(arg, "public_key_path") == 0)
- public_key_path = value;
- else if (strcmp(arg, "private_key_path") == 0)
- private_key_path = value;
- else if (strcmp(arg, "password") == 0)
- password = value;
- else {
- *error_r = t_strdup_printf(
- "Invalid parameter '%s'", arg);
- return -1;
- }
- }
-
- parent_args = strchr(args, ':');
- if (parent_args == NULL) {
- parent_name = args;
- parent_args = "";
- } else {
- parent_name = t_strdup_until(args, parent_args);
- parent_args++;
- }
- if (fs_legacy_init(parent_name, parent_args, _fs->event, params,
- &_fs->parent, error_r) < 0)
+ if (settings_get(_fs->event, &crypt_setting_parser_info, 0,
+ &fs->set, error_r) < 0)
return -1;
- fs->enc_algo = i_strdup(enc_algo);
- fs->set_prefix = i_strdup(set_prefix);
- fs->public_key_path = i_strdup_empty(public_key_path);
- fs->private_key_path = i_strdup_empty(private_key_path);
- fs->password = i_strdup_empty(password);
- return 0;
+
+ return fs_init_parent(_fs, params, error_r);
}
static void fs_crypt_free(struct fs *_fs)
struct crypt_fs *fs = CRYPT_FS(_fs);
mail_crypt_global_keys_free(&fs->keys);
- i_free(fs->enc_algo);
- i_free(fs->set_prefix);
- i_free(fs->public_key_path);
- i_free(fs->private_key_path);
- i_free(fs->password);
+ settings_free(fs->set);
i_free(fs);
}
fs_set_metadata(file->super_read, key, value);
}
-static int fs_crypt_read_file(const char *set_name, const char *path,
- char **key_data_r, const char **error_r)
-{
- struct istream *input;
- int ret;
-
- input = i_stream_create_file(path, SIZE_MAX);
- while (i_stream_read(input) > 0) ;
- if (input->stream_errno != 0) {
- *error_r = t_strdup_printf("%s: read(%s) failed: %s",
- set_name, path, i_stream_get_error(input));
- ret = -1;
- } else {
- size_t size;
- const unsigned char *data = i_stream_get_data(input, &size);
- *key_data_r = i_strndup(data, size);
- ret = 0;
- }
- i_stream_unref(&input);
- return ret;
-}
-
-static int
-fs_crypt_load_keys_from_path(struct crypt_fs *fs, const char **error_r)
-{
- char *key_data;
-
- mail_crypt_global_keys_init(&fs->keys);
- if (fs->public_key_path != NULL) {
- if (fs_crypt_read_file("crypt:public_key_path",
- fs->public_key_path,
- &key_data, error_r) < 0)
- return -1;
- if (mail_crypt_load_global_public_key("crypt:public_key_path",
- key_data, &fs->keys,
- error_r) < 0) {
- i_free(key_data);
- return -1;
- }
- i_free(key_data);
- }
- if (fs->private_key_path != NULL) {
- if (fs_crypt_read_file("crypt:private_key_path",
- fs->private_key_path,
- &key_data, error_r) < 0)
- return -1;
- if (mail_crypt_load_global_private_key("crypt:private_key_path",
- key_data, "crypt:password",
- fs->password, &fs->keys,
- error_r) < 0) {
- i_free(key_data);
- return -1;
- }
- i_free(key_data);
- }
- return 0;
-}
-
static
int fs_crypt_load_keys(struct crypt_fs *fs, const char **error_r)
{
- const char *error;
-
if (fs->keys_loaded)
return 0;
- if (fs->public_key_path != NULL || fs->private_key_path != NULL) {
- /* overrides using settings */
- if (fs_crypt_load_keys_from_path(fs, error_r) < 0)
- return -1;
- fs->keys_loaded = TRUE;
- return 0;
- }
- if (mail_crypt_global_keys_load_pluginenv(fs->set_prefix, &fs->keys,
- &error) < 0) {
- *error_r = t_strdup_printf("%s: %s", fs->set_prefix, error);
+ if (mail_crypt_global_keys_load(fs->fs.event, fs->set,
+ &fs->keys, error_r) < 0)
return -1;
- }
fs->keys_loaded = TRUE;
return 0;
}
input = fs_read_stream(file->super_read,
I_MAX(FS_CRYPT_ISTREAM_MIN_BUFFER_SIZE, max_buffer_size));
- if (file->fs->allow_missing_keys) {
+ if (file->fs->set->crypt_plain_fallback) {
struct istream *decrypted_input =
i_stream_create_decrypt_callback(input,
fs_crypt_istream_get_key, file);
}
if (file->fs->keys.public_key == NULL) {
- if (!file->fs->allow_missing_keys) {
+ if (!file->fs->set->crypt_plain_fallback) {
_file->output = o_stream_create_error_str(EINVAL,
"Encryption required, but no public key available");
return;
}
enum io_stream_encrypt_flags flags;
- if (strstr(file->fs->enc_algo, "gcm") != NULL ||
- strstr(file->fs->enc_algo, "ccm") != NULL ||
- str_begins_with(file->fs->enc_algo, "chacha20-poly1305")) {
+ if (strstr(file->fs->set->crypt_write_algorithm, "gcm") != NULL ||
+ strstr(file->fs->set->crypt_write_algorithm, "ccm") != NULL ||
+ str_begins_with(file->fs->set->crypt_write_algorithm,
+ "chacha20-poly1305")) {
flags = IO_STREAM_ENC_INTEGRITY_AEAD;
} else {
flags = IO_STREAM_ENC_INTEGRITY_HMAC;
IOSTREAM_TEMP_FLAG_TRY_FD_DUP,
fs_file_path(_file));
_file->output = o_stream_create_encrypt(file->temp_output,
- file->fs->enc_algo, file->fs->keys.public_key,
+ file->fs->set->crypt_write_algorithm, file->fs->keys.public_key,
flags);
}
.name = "crypt",
.v = {
.alloc = fs_crypt_alloc,
- .legacy_init = fs_crypt_init,
+ .init = fs_crypt_init,
.deinit = NULL,
.free = fs_crypt_free,
.get_properties = fs_wrapper_get_properties,
#define MAIL_CRYPT_COMMON_H
#include "dcrypt.h"
+#include "crypt-settings.h"
#define MAIL_CRYPT_PW_CIPHER "aes-256-ctr"
#define MAIL_CRYPT_KEY_CIPHER "ecdh-aes-256-ctr"
#include "str.h"
#include "hex-binary.h"
#include "base64.h"
+#include "settings.h"
#include "mail-user.h"
#include "mail-crypt-common.h"
#include "mail-crypt-key.h"
if (enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD) {
/* Fail here if password is not set since openssl will prompt
* for it otherwise */
- if (key_password == NULL) {
+ if (key_password == NULL || key_password[0] == '\0') {
*error_r = t_strdup_printf("%s: %s unset, no password to decrypt the key",
set_key, set_pw);
return -1;
return 0;
}
+int mail_crypt_global_keys_load(struct event *event,
+ const struct crypt_settings *set,
+ struct mail_crypt_global_keys *global_keys_r,
+ const char **error_r)
+{
+ const struct crypt_private_key_settings *key_set;
+ const char *key_name, *error;
+
+ mail_crypt_global_keys_init(global_keys_r);
+ if (set->crypt_global_public_key[0] != '\0') {
+ if (mail_crypt_load_global_public_key(
+ "crypt_global_public_key",
+ set->crypt_global_public_key, global_keys_r,
+ error_r) < 0)
+ return -1;
+ }
+
+ if (!array_is_created(&set->crypt_global_private_keys))
+ return 0;
+ array_foreach_elem(&set->crypt_global_private_keys, key_name) {
+ if (settings_get_filter(event, "crypt_global_private_key",
+ key_name,
+ &crypt_private_key_setting_parser_info,
+ 0, &key_set, &error) < 0) {
+ *error_r = t_strdup_printf(
+ "Failed to get crypt_private_key %s: %s",
+ key_name, error);
+ return -1;
+ }
+ if (mail_crypt_load_global_private_key(
+ key_name, key_set->crypt_private_key,
+ key_name, key_set->crypt_private_key_password,
+ global_keys_r, error_r) < 0) {
+ settings_free(key_set);
+ return -1;
+ }
+ settings_free(key_set);
+ }
+ return 0;
+}
+
void mail_crypt_global_keys_init(struct mail_crypt_global_keys *global_keys_r)
{
i_zero(global_keys_r);
#ifndef MAIL_CRYPT_GLOBAL_KEY_H
#define MAIL_CRYPT_GLOBAL_KEY_H
+struct crypt_settings;
+
struct mail_crypt_global_private_key {
struct dcrypt_private_key *key;
char *key_id, *key_id_old;
struct mail_user;
+int mail_crypt_global_keys_load(struct event *event,
+ const struct crypt_settings *set,
+ struct mail_crypt_global_keys *global_keys_r,
+ const char **error_r);
+
int mail_crypt_global_keys_load_from_user(struct mail_user *user,
const char *set_prefix,
struct mail_crypt_global_keys *global_keys_r,
#include "randgen.h"
#include "test-common.h"
#include "hex-binary.h"
+#include "settings.h"
#include "fs-api.h"
#include "fs-api-private.h"
#include "dcrypt.h"
#include <unistd.h>
-const char *private_key_pem = "-----BEGIN PRIVATE KEY-----\n"
-"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgYIufJZZe2Y6iFz5x\n"
-"koIoysb3dZLZWsyekjOc/GjsLd2hRANCAASnIWgQuhE8jqALcmfiunRyEk7vkq/y\n"
-"a9vYK50b3cFhCsLU4tfVTLkB1Y/6VlZj63QKMzXNvk5G5OD1ofElcpyj\n"
-"-----END PRIVATE KEY-----";
-const char *public_key_pem = "-----BEGIN PUBLIC KEY-----\n"
-"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpyFoELoRPI6gC3Jn4rp0chJO75Kv\n"
-"8mvb2CudG93BYQrC1OLX1Uy5AdWP+lZWY+t0CjM1zb5ORuTg9aHxJXKcow==\n"
-"-----END PUBLIC KEY-----";
+#define PRIVATE_KEY_PEM \
+"-----BEGIN PRIVATE KEY-----\n" \
+"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgYIufJZZe2Y6iFz5x\n" \
+"koIoysb3dZLZWsyekjOc/GjsLd2hRANCAASnIWgQuhE8jqALcmfiunRyEk7vkq/y\n" \
+"a9vYK50b3cFhCsLU4tfVTLkB1Y/6VlZj63QKMzXNvk5G5OD1ofElcpyj\n" \
+"-----END PRIVATE KEY-----"
+#define PUBLIC_KEY_PEM \
+ "-----BEGIN PUBLIC KEY-----\n" \
+"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpyFoELoRPI6gC3Jn4rp0chJO75Kv\n" \
+"8mvb2CudG93BYQrC1OLX1Uy5AdWP+lZWY+t0CjM1zb5ORuTg9aHxJXKcow==\n" \
+"-----END PUBLIC KEY-----"
extern const struct fs fs_class_crypt;
static void test_setup(void)
{
- struct fs *fs;
- struct fs_file *file;
- const char *error;
-
test_fs_params.base_dir = ".";
test_fs_params.temp_dir = ".";
- i_unlink_if_exists("test_public_key.pem");
- i_unlink_if_exists("test_private_key.pem");
-
fs_class_register(&fs_class_posix);
fs_class_register(&fs_class_crypt);
-
- if (fs_legacy_init("posix", "", NULL, &test_fs_params, &fs, &error) < 0)
- i_fatal("fs_init(posix) failed: %s", error);
- /* write keys to disk */
- file = fs_file_init(fs, "test_public_key.pem", FS_OPEN_MODE_CREATE);
- if (fs_write(file, public_key_pem, strlen(public_key_pem)) < 0) {
- i_fatal("fs_write(test_public_key.pem) failed: %s",
- fs_file_last_error(file));
- }
- fs_file_deinit(&file);
- file = fs_file_init(fs, "test_private_key.pem", FS_OPEN_MODE_CREATE);
- if (fs_write(file, private_key_pem, strlen(private_key_pem)) < 0) {
- i_fatal("fs_write(test_private_key.pem) failed: %s",
- fs_file_last_error(file));
- }
- fs_file_deinit(&file);
- fs_deinit(&fs);
}
static void test_fs_crypt_read_write(void)
const char *error;
struct fs *fs;
- if (fs_legacy_init("crypt", "public_key_path=test_public_key.pem:"
- "private_key_path=test_private_key.pem:posix",
- NULL, &test_fs_params, &fs, &error) < 0)
+ const char *const test_settings[] = {
+ "fs_parent/fs_driver", "posix",
+ "fs_driver", "crypt",
+ "crypt_global_public_key", PUBLIC_KEY_PEM,
+ "crypt_global_private_key", "main",
+ "crypt_global_private_key/main/crypt_private_key", PRIVATE_KEY_PEM,
+ NULL
+ };
+ struct settings_simple test_set;
+ settings_simple_init(&test_set, test_settings);
+ if (fs_init_auto(test_set.event, &test_fs_params, &fs, &error) <= 0)
i_fatal("fs_init(crypt:posix) failed: %s", error);
i_unlink_if_exists("test_file");
fs_file_deinit(&file);
fs_deinit(&fs);
+ settings_simple_deinit(&test_set);
test_end();
}
const char *error;
struct fs *fs;
- if (fs_legacy_init("crypt", "public_key_path=test_public_key.pem:"
- "private_key_path=test_private_key.pem:posix",
- NULL, &test_fs_params, &fs, &error) < 0)
+ const char *const test_settings[] = {
+ "fs_parent/fs_driver", "posix",
+ "fs_driver", "crypt",
+ "crypt_global_public_key", PUBLIC_KEY_PEM,
+ "crypt_global_private_key", "main",
+ "crypt_global_private_key/main/crypt_private_key", PRIVATE_KEY_PEM,
+ NULL
+ };
+ struct settings_simple test_set;
+ settings_simple_init(&test_set, test_settings);
+ if (fs_init_auto(test_set.event, &test_fs_params, &fs, &error) <= 0)
i_fatal("fs_init(crypt:posix) failed: %s", error);
i_unlink_if_exists("test_file");
fs_file_deinit(&file);
fs_deinit(&fs);
+ settings_simple_deinit(&test_set);
test_end();
}
const char *error;
struct fs *fs;
- if (fs_legacy_init("crypt", "public_key_path=:"
- "private_key_path=test_private_key.pem:"
- "maybe:posix",
- NULL, &test_fs_params, &fs, &error) < 0)
+ const char *const test_settings[] = {
+ "fs_parent/fs_driver", "posix",
+ "fs_driver", "crypt",
+ "crypt_plain_fallback", "yes",
+ "crypt_global_private_key", "main",
+ "crypt_global_private_key/main/crypt_private_key", PRIVATE_KEY_PEM,
+ NULL
+ };
+ struct settings_simple test_set;
+ settings_simple_init(&test_set, test_settings);
+ if (fs_init_auto(test_set.event, &test_fs_params, &fs, &error) <= 0)
i_fatal("fs_init(crypt:posix) failed: %s", error);
i_unlink_if_exists("test_file");
fs_file_deinit(&file);
fs_deinit(&fs);
-
- if (fs_legacy_init("crypt", "public_key_path=test_public_key.pem:"
- "private_key_path=test_private_key.pem:"
- "maybe:posix",
- NULL, &test_fs_params, &fs, &error) < 0)
+ settings_simple_deinit(&test_set);
+
+ const char *const test_settings2[] = {
+ "fs_parent/fs_driver", "posix",
+ "fs_driver", "crypt",
+ "crypt_plain_fallback", "yes",
+ "crypt_global_public_key", PUBLIC_KEY_PEM,
+ "crypt_global_private_key", "main",
+ "crypt_global_private_key/main/crypt_private_key", PRIVATE_KEY_PEM,
+ NULL
+ };
+ settings_simple_init(&test_set, test_settings2);
+ if (fs_init_auto(test_set.event, &test_fs_params, &fs, &error) <= 0)
i_fatal("fs_init(crypt:posix) failed: %s", error);
i_unlink_if_exists("test_file");
fs_file_deinit(&file);
fs_deinit(&fs);
+ settings_simple_deinit(&test_set);
test_end();
}
static void test_teardown(void)
{
- i_unlink_if_exists("test_public_key.pem");
- i_unlink_if_exists("test_private_key.pem");
i_unlink_if_exists("test_file");
}
#include "mail-crypt-common.h"
#include "mail-crypt-key.h"
-#include "fs-crypt-settings.h"
#include "mail-crypt-pluginenv.c"