test_mail_key_LDFLAGS = $(DOVECOT_BINARY_LDFLAGS)
test_mail_key_CFLAGS = $(AM_CPPFLAGS) $(DOVECOT_BINARY_CFLAGS) -Dtop_builddir=\"$(top_builddir)\"
-EXTRA_DIST = fs-crypt-common.c
-
noinst_HEADERS = \
mail-crypt-plugin.h \
mail-crypt-common.h \
+++ /dev/null
-/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "randgen.h"
-#include "istream.h"
-#include "istream-try.h"
-#include "ostream.h"
-#include "dcrypt-iostream.h"
-#include "istream-decrypt.h"
-#include "ostream-encrypt.h"
-#include "iostream-temp.h"
-#include "mailbox-list.h"
-#include "mail-namespace.h"
-#include "mail-crypt-common.h"
-#include "mail-crypt-key.h"
-#include "dcrypt-iostream.h"
-#include "fs-api-private.h"
-
-#define FS_CRYPT_ISTREAM_MIN_BUFFER_SIZE 1024
-
-struct crypt_fs {
- 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;
-};
-
-struct crypt_fs_file {
- struct fs_file file;
- struct crypt_fs *fs;
- struct fs_file *super_read;
- enum fs_open_mode open_mode;
- struct istream *input;
-
- struct ostream *super_output;
- struct ostream *temp_output;
-};
-
-#define CRYPT_FS(ptr) container_of((ptr), struct crypt_fs, fs)
-#define CRYPT_FILE(ptr) container_of((ptr), struct crypt_fs_file, file)
-
-/* defined outside this file */
-extern const struct fs FS_CLASS_CRYPT;
-
-static
-int fs_crypt_load_keys(struct crypt_fs *fs, const char **error_r);
-
-static struct fs *fs_crypt_alloc(void)
-{
- struct crypt_fs *fs;
-
- fs = i_new(struct crypt_fs, 1);
- fs->fs = FS_CLASS_CRYPT;
-
- return &fs->fs;
-}
-
-static int
-fs_crypt_init(struct fs *_fs, const char *args,
- 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 = "";
-
- 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)
- 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;
-}
-
-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);
- i_free(fs);
-}
-
-static struct fs_file *fs_crypt_file_alloc(void)
-{
- struct crypt_fs_file *file = i_new(struct crypt_fs_file, 1);
- return &file->file;
-}
-
-static void
-fs_crypt_file_init(struct fs_file *_file, const char *path,
- enum fs_open_mode mode, enum fs_open_flags flags)
-{
- struct crypt_fs *fs = CRYPT_FS(_file->fs);
- struct crypt_fs_file *file = CRYPT_FILE(_file);
-
- file->file.path = i_strdup(path);
- file->fs = fs;
- file->open_mode = mode;
-
- /* avoid unnecessarily creating two seekable streams */
- flags &= ENUM_NEGATE(FS_OPEN_FLAG_SEEKABLE);
-
- file->file.parent = fs_file_init_parent(_file, path, mode, flags);
- if (mode == FS_OPEN_MODE_READONLY &&
- (flags & FS_OPEN_FLAG_ASYNC) == 0) {
- /* use async stream for super, so fs_read_stream() won't create
- another seekable stream needlessly */
- file->super_read = fs_file_init_parent(_file, path,
- mode, flags | FS_OPEN_FLAG_ASYNC |
- FS_OPEN_FLAG_ASYNC_NOQUEUE);
- } else {
- file->super_read = file->file.parent;
- }
-}
-
-static void fs_crypt_file_deinit(struct fs_file *_file)
-{
- struct crypt_fs_file *file = CRYPT_FILE(_file);
-
- if (file->super_read != _file->parent)
- fs_file_deinit(&file->super_read);
- fs_file_free(_file);
- i_free(file->file.path);
- i_free(file);
-}
-
-static void fs_crypt_file_close(struct fs_file *_file)
-{
- struct crypt_fs_file *file = CRYPT_FILE(_file);
-
- i_stream_unref(&file->input);
- fs_file_close(file->super_read);
- fs_file_close(_file->parent);
-}
-
-static void fs_crypt_set_metadata(struct fs_file *_file,
- const char *key, const char *value)
-{
- struct crypt_fs_file *file = CRYPT_FILE(_file);
-
- fs_set_metadata(_file->parent, key, value);
- if (file->super_read != NULL)
- 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_istream_get_key(const char *pubkey_digest,
- struct dcrypt_private_key **priv_key_r,
- const char **error_r, void *context)
-{
- struct crypt_fs_file *file = context;
-
- if (fs_crypt_load_keys(file->fs, error_r) < 0)
- return -1;
-
- *priv_key_r = mail_crypt_global_key_find(&file->fs->keys, pubkey_digest);
- if (*priv_key_r == NULL)
- return 0;
- dcrypt_key_ref_private(*priv_key_r);
- return 1;
-}
-
-static struct istream *
-fs_crypt_read_stream(struct fs_file *_file, size_t max_buffer_size)
-{
- struct crypt_fs_file *file = CRYPT_FILE(_file);
- struct istream *input;
-
- if (file->input != NULL) {
- i_stream_ref(file->input);
- i_stream_seek(file->input, 0);
- return file->input;
- }
-
- input = fs_read_stream(file->super_read,
- I_MAX(FS_CRYPT_ISTREAM_MIN_BUFFER_SIZE, max_buffer_size));
- if (input->stream_errno != 0) {
- file->input = input;
- i_stream_ref(file->input);
- return file->input;
- }
-
- if (file->fs->allow_missing_keys) {
- struct istream *decrypted_input =
- i_stream_create_decrypt_callback(input,
- fs_crypt_istream_get_key, file);
- struct istream *plaintext_input =
- i_stream_create_noop(input);
- /* If the file is not encrypted, fall back to reading
- * it as plaintext. */
- struct istream *inputs[] = {
- decrypted_input,
- plaintext_input,
- NULL
- };
- file->input = istream_try_create(inputs, max_buffer_size);
- i_stream_unref(&decrypted_input);
- i_stream_unref(&plaintext_input);
- } else {
- file->input = i_stream_create_decrypt_callback(input,
- fs_crypt_istream_get_key, file);
- }
- i_stream_unref(&input);
- i_stream_ref(file->input);
- return file->input;
-}
-
-static void fs_crypt_write_stream(struct fs_file *_file)
-{
- struct crypt_fs_file *file = CRYPT_FILE(_file);
- struct event *event = _file->event;
- const char *error;
-
- i_assert(_file->output == NULL);
-
- if (fs_crypt_load_keys(file->fs, &error) < 0) {
- _file->output = o_stream_create_error_str(EIO,
- "Couldn't read settings: %s", error);
- return;
- }
-
- if (file->fs->keys.public_key == NULL) {
- if (!file->fs->allow_missing_keys) {
- _file->output = o_stream_create_error_str(EINVAL,
- "Encryption required, but no public key available");
- return;
- } else {
- e_debug(event,
- "No public key provided, NOT encrypting stream %s",
- fs_file_path(_file));
- }
- file->super_output = fs_write_stream(_file->parent);
- _file->output = file->super_output;
- 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")) {
- flags = IO_STREAM_ENC_INTEGRITY_AEAD;
- } else {
- flags = IO_STREAM_ENC_INTEGRITY_HMAC;
- }
-
- file->temp_output =
- iostream_temp_create_named(_file->fs->temp_path_prefix,
- 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,
- flags);
-}
-
-static int fs_crypt_write_stream_finish(struct fs_file *_file, bool success)
-{
- struct crypt_fs_file *file = CRYPT_FILE(_file);
- struct istream *input;
- int ret;
-
- if (_file->output != NULL) {
- if (_file->output == file->super_output)
- _file->output = NULL;
- else
- o_stream_unref(&_file->output);
- }
- if (!success) {
- if (file->super_output != NULL) {
- /* no encryption */
- i_assert(file->temp_output == NULL);
- fs_write_stream_abort_error(_file->parent, &file->super_output,
- "write(%s) failed: %s",
- o_stream_get_name(file->super_output),
- o_stream_get_error(file->super_output));
- } else {
- o_stream_destroy(&file->temp_output);
- }
- return -1;
- }
-
- if (file->super_output != NULL) {
- /* no encrypt */
- i_assert(file->temp_output == NULL);
- return fs_write_stream_finish(_file->parent, &file->super_output);
- }
- if (file->temp_output == NULL) {
- /* finishing up */
- i_assert(file->super_output == NULL);
- return fs_write_stream_finish_async(_file->parent);
- }
-
- /* finish writing the temporary file */
- input = iostream_temp_finish(&file->temp_output, IO_BLOCK_SIZE);
- file->super_output = fs_write_stream(_file->parent);
- o_stream_nsend_istream(file->super_output, input);
- ret = fs_write_stream_finish(_file->parent, &file->super_output);
- i_stream_unref(&input);
- return ret;
-}
/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
-#define FS_CLASS_CRYPT fs_class_crypt
-#include "fs-crypt-common.c"
+
+#include "lib.h"
+#include "randgen.h"
+#include "istream.h"
+#include "istream-try.h"
+#include "ostream.h"
+#include "dcrypt-iostream.h"
+#include "istream-decrypt.h"
+#include "ostream-encrypt.h"
+#include "iostream-temp.h"
+#include "mailbox-list.h"
+#include "mail-namespace.h"
+#include "mail-crypt-common.h"
+#include "mail-crypt-key.h"
+#include "dcrypt-iostream.h"
+#include "fs-api-private.h"
+
+#define FS_CRYPT_ISTREAM_MIN_BUFFER_SIZE 1024
+
+struct crypt_fs {
+ 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;
+};
+
+struct crypt_fs_file {
+ struct fs_file file;
+ struct crypt_fs *fs;
+ struct fs_file *super_read;
+ enum fs_open_mode open_mode;
+ struct istream *input;
+
+ struct ostream *super_output;
+ struct ostream *temp_output;
+};
+
+#define CRYPT_FS(ptr) container_of((ptr), struct crypt_fs, fs)
+#define CRYPT_FILE(ptr) container_of((ptr), struct crypt_fs_file, file)
+
+/* defined outside this file */
+extern const struct fs fs_class_crypt;
+
+static struct fs *fs_crypt_alloc(void)
+{
+ struct crypt_fs *fs;
+
+ fs = i_new(struct crypt_fs, 1);
+ fs->fs = fs_class_crypt;
+
+ return &fs->fs;
+}
+
+static int
+fs_crypt_init(struct fs *_fs, const char *args,
+ 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 = "";
+
+ 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)
+ 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;
+}
+
+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);
+ i_free(fs);
+}
+
+static struct fs_file *fs_crypt_file_alloc(void)
+{
+ struct crypt_fs_file *file = i_new(struct crypt_fs_file, 1);
+ return &file->file;
+}
+
+static void
+fs_crypt_file_init(struct fs_file *_file, const char *path,
+ enum fs_open_mode mode, enum fs_open_flags flags)
+{
+ struct crypt_fs *fs = CRYPT_FS(_file->fs);
+ struct crypt_fs_file *file = CRYPT_FILE(_file);
+
+ file->file.path = i_strdup(path);
+ file->fs = fs;
+ file->open_mode = mode;
+
+ /* avoid unnecessarily creating two seekable streams */
+ flags &= ENUM_NEGATE(FS_OPEN_FLAG_SEEKABLE);
+
+ file->file.parent = fs_file_init_parent(_file, path, mode, flags);
+ if (mode == FS_OPEN_MODE_READONLY &&
+ (flags & FS_OPEN_FLAG_ASYNC) == 0) {
+ /* use async stream for super, so fs_read_stream() won't create
+ another seekable stream needlessly */
+ file->super_read = fs_file_init_parent(_file, path,
+ mode, flags | FS_OPEN_FLAG_ASYNC |
+ FS_OPEN_FLAG_ASYNC_NOQUEUE);
+ } else {
+ file->super_read = file->file.parent;
+ }
+}
+
+static void fs_crypt_file_deinit(struct fs_file *_file)
+{
+ struct crypt_fs_file *file = CRYPT_FILE(_file);
+
+ if (file->super_read != _file->parent)
+ fs_file_deinit(&file->super_read);
+ fs_file_free(_file);
+ i_free(file->file.path);
+ i_free(file);
+}
+
+static void fs_crypt_file_close(struct fs_file *_file)
+{
+ struct crypt_fs_file *file = CRYPT_FILE(_file);
+
+ i_stream_unref(&file->input);
+ fs_file_close(file->super_read);
+ fs_file_close(_file->parent);
+}
+
+static void fs_crypt_set_metadata(struct fs_file *_file,
+ const char *key, const char *value)
+{
+ struct crypt_fs_file *file = CRYPT_FILE(_file);
+
+ fs_set_metadata(_file->parent, key, value);
+ if (file->super_read != NULL)
+ 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)
return 0;
}
+static int
+fs_crypt_istream_get_key(const char *pubkey_digest,
+ struct dcrypt_private_key **priv_key_r,
+ const char **error_r, void *context)
+{
+ struct crypt_fs_file *file = context;
+
+ if (fs_crypt_load_keys(file->fs, error_r) < 0)
+ return -1;
+
+ *priv_key_r = mail_crypt_global_key_find(&file->fs->keys, pubkey_digest);
+ if (*priv_key_r == NULL)
+ return 0;
+ dcrypt_key_ref_private(*priv_key_r);
+ return 1;
+}
+
+static struct istream *
+fs_crypt_read_stream(struct fs_file *_file, size_t max_buffer_size)
+{
+ struct crypt_fs_file *file = CRYPT_FILE(_file);
+ struct istream *input;
+
+ if (file->input != NULL) {
+ i_stream_ref(file->input);
+ i_stream_seek(file->input, 0);
+ return file->input;
+ }
+
+ input = fs_read_stream(file->super_read,
+ I_MAX(FS_CRYPT_ISTREAM_MIN_BUFFER_SIZE, max_buffer_size));
+
+ if (file->fs->allow_missing_keys) {
+ struct istream *decrypted_input =
+ i_stream_create_decrypt_callback(input,
+ fs_crypt_istream_get_key, file);
+ struct istream *plaintext_input =
+ i_stream_create_noop(input);
+ /* If the file is not encrypted, fall back to reading
+ * it as plaintext. */
+ struct istream *inputs[] = {
+ decrypted_input,
+ plaintext_input,
+ NULL
+ };
+ file->input = istream_try_create(inputs, max_buffer_size);
+ i_stream_unref(&decrypted_input);
+ i_stream_unref(&plaintext_input);
+ } else {
+ file->input = i_stream_create_decrypt_callback(input,
+ fs_crypt_istream_get_key, file);
+ }
+ i_stream_unref(&input);
+ i_stream_ref(file->input);
+ return file->input;
+}
+
+static void fs_crypt_write_stream(struct fs_file *_file)
+{
+ struct crypt_fs_file *file = CRYPT_FILE(_file);
+ struct event *event = _file->event;
+ const char *error;
+
+ i_assert(_file->output == NULL);
+
+ if (fs_crypt_load_keys(file->fs, &error) < 0) {
+ _file->output = o_stream_create_error_str(EIO,
+ "Couldn't read settings: %s", error);
+ return;
+ }
+
+ if (file->fs->keys.public_key == NULL) {
+ if (!file->fs->allow_missing_keys) {
+ _file->output = o_stream_create_error_str(EINVAL,
+ "Encryption required, but no public key available");
+ return;
+ } else {
+ e_debug(event,
+ "No public key provided, NOT encrypting stream %s",
+ fs_file_path(_file));
+ }
+ file->super_output = fs_write_stream(_file->parent);
+ _file->output = file->super_output;
+ 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")) {
+ flags = IO_STREAM_ENC_INTEGRITY_AEAD;
+ } else {
+ flags = IO_STREAM_ENC_INTEGRITY_HMAC;
+ }
+
+ file->temp_output =
+ iostream_temp_create_named(_file->fs->temp_path_prefix,
+ 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,
+ flags);
+}
+
+static int fs_crypt_write_stream_finish(struct fs_file *_file, bool success)
+{
+ struct crypt_fs_file *file = CRYPT_FILE(_file);
+ struct istream *input;
+ int ret;
+
+ if (_file->output != NULL) {
+ if (_file->output == file->super_output)
+ _file->output = NULL;
+ else
+ o_stream_unref(&_file->output);
+ }
+ if (!success) {
+ if (file->super_output != NULL) {
+ /* no encryption */
+ i_assert(file->temp_output == NULL);
+ fs_write_stream_abort_error(_file->parent, &file->super_output,
+ "write(%s) failed: %s",
+ o_stream_get_name(file->super_output),
+ o_stream_get_error(file->super_output));
+ } else {
+ o_stream_destroy(&file->temp_output);
+ }
+ return -1;
+ }
+
+ if (file->super_output != NULL) {
+ /* no encrypt */
+ i_assert(file->temp_output == NULL);
+ return fs_write_stream_finish(_file->parent, &file->super_output);
+ }
+ if (file->temp_output == NULL) {
+ /* finishing up */
+ i_assert(file->super_output == NULL);
+ return fs_write_stream_finish_async(_file->parent);
+ }
+
+ /* finish writing the temporary file */
+ input = iostream_temp_finish(&file->temp_output, IO_BLOCK_SIZE);
+ file->super_output = fs_write_stream(_file->parent);
+ o_stream_nsend_istream(file->super_output, input);
+ ret = fs_write_stream_finish(_file->parent, &file->super_output);
+ i_stream_unref(&input);
+ return ret;
+}
+
const struct fs fs_class_crypt = {
.name = "crypt",
.v = {