]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
mail-crypt: fs-crypt - Add unit tests for fs-crypt
authorAki Tuomi <aki.tuomi@open-xchange.com>
Fri, 18 Mar 2022 13:39:57 +0000 (15:39 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Mon, 28 Mar 2022 08:47:53 +0000 (08:47 +0000)
src/plugins/mail-crypt/Makefile.am
src/plugins/mail-crypt/test-fs-crypt.c [new file with mode: 0644]

index f240a2ad2cd1ada8e5be8554235b48ed419367f7..e70298488bcbe34ea9776789dd14a78c8bc7d907 100644 (file)
@@ -77,9 +77,24 @@ libdoveadm_mail_crypt_plugin_la_DEPENDENCIES = $(LIBDOVECOT_DEPS)
 libdoveadm_mail_crypt_plugin_la_LDFLAGS = -module -avoid-version
 
 test_programs = \
+       test-fs-crypt \
        test-mail-global-key \
        test-mail-key
 
+test_fs_crypt_SOURCES = \
+       test-fs-crypt.c \
+       fs-crypt-settings.c
+test_fs_crypt_LDADD = $(LIBDOVECOT) \
+       fs-crypt.lo \
+       mail-crypt-global-key.lo \
+       mail-crypt-pluginenv.lo
+test_fs_crypt_DEPENDENCIES = $(LIBDOVECOT_DEPS) \
+       fs-crypt.lo \
+       mail-crypt-global-key.lo \
+       mail-crypt-pluginenv.lo
+test_fs_crypt_LDFLAGS = $(DOVECOT_BINARY_LDFLAGS)
+test_fs_crypt_CFLAGS = $(AM_CPPFLAGS) $(DOVECOT_BINARY_CFLAGS) -Dtop_builddir=\"$(top_builddir)\"
+
 test_mail_global_key_SOURCES = \
        test-mail-global-key.c \
        fs-crypt-settings.c \
diff --git a/src/plugins/mail-crypt/test-fs-crypt.c b/src/plugins/mail-crypt/test-fs-crypt.c
new file mode 100644 (file)
index 0000000..5cff40d
--- /dev/null
@@ -0,0 +1,279 @@
+/* Copyright (c) 2022 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "istream.h"
+#include "ostream.h"
+#include "path-util.h"
+#include "randgen.h"
+#include "test-common.h"
+#include "hex-binary.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-----";
+
+extern const struct fs fs_class_crypt;
+
+static struct fs_settings test_fs_set;
+
+static void test_setup(void)
+{
+       struct fs *fs;
+       struct fs_file *file;
+       const char *error;
+
+       test_fs_set.base_dir = ".";
+       test_fs_set.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_init_from_string("posix", &test_fs_set, &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)
+{
+       test_begin("fs-crypt: read write");
+       const char *error;
+       struct fs *fs;
+
+       if (fs_init_from_string("crypt:public_key_path=test_public_key.pem:"
+                               "private_key_path=test_private_key.pem:posix",
+                               &test_fs_set, &fs, &error) < 0)
+               i_fatal("fs_init(crypt:posix) failed: %s", error);
+
+       i_unlink_if_exists("test_file");
+       /* write some data to disk */
+       unsigned char databuf[128];
+       random_fill(databuf, sizeof(databuf));
+
+       struct fs_file *file = fs_file_init(fs, "test_file", FS_OPEN_MODE_CREATE);
+       struct ostream *os = fs_write_stream(file);
+       test_assert(o_stream_send(os, databuf, sizeof(databuf)) == sizeof(databuf));
+       test_assert(o_stream_finish(os) == 1);
+       fs_write_stream_finish(file, &os);
+       fs_file_deinit(&file);
+
+       file = fs_file_init(fs, "test_file", FS_OPEN_MODE_READONLY);
+
+       struct istream *is = fs_read_stream(file, 8192);
+       buffer_t *readbuf = t_buffer_create(sizeof(databuf));
+       int ret;
+
+       while ((ret = i_stream_read(is)) > 0) {
+               size_t size;
+               const unsigned char *data = i_stream_get_data(is, &size);
+               buffer_append(readbuf, data, size);
+               i_stream_skip(is, size);
+       }
+
+       test_assert(ret == -1);
+       test_assert(is->stream_errno == 0);
+       test_assert(is->eof);
+       test_assert(readbuf->used == sizeof(databuf) &&
+                   memcmp(readbuf->data, databuf, sizeof(databuf)) == 0);
+       i_stream_unref(&is);
+
+       fs_file_deinit(&file);
+       fs_deinit(&fs);
+
+       test_end();
+}
+
+static void test_fs_crypt_read_write_0(void)
+{
+       test_begin("fs-crypt: read write (size=0)");
+       const char *error;
+       struct fs *fs;
+
+       if (fs_init_from_string("crypt:public_key_path=test_public_key.pem:"
+                               "private_key_path=test_private_key.pem:posix",
+                               &test_fs_set, &fs, &error) < 0)
+               i_fatal("fs_init(crypt:posix) failed: %s", error);
+
+       i_unlink_if_exists("test_file");
+       /* write nothing to disk */
+
+       struct fs_file *file = fs_file_init(fs, "test_file", FS_OPEN_MODE_CREATE);
+       struct ostream *os = fs_write_stream(file);
+       test_assert(o_stream_finish(os) == 1);
+       fs_write_stream_finish(file, &os);
+       fs_file_deinit(&file);
+
+       /* check that the result file is empty */
+       struct stat st;
+       if (stat("test_file", &st) < 0)
+               i_fatal("stat(test_file) failed: %m");
+       test_assert_ucmp(st.st_size, ==, 0);
+
+       file = fs_file_init(fs, "test_file", FS_OPEN_MODE_READONLY);
+
+       struct istream *is = fs_read_stream(file, 8192);
+       test_assert(i_stream_read(is) == -1);
+       test_assert(is->v_offset == 0);
+       test_assert(is->stream_errno == 0);
+       test_assert(is->eof);
+       i_stream_unref(&is);
+
+       fs_file_deinit(&file);
+       fs_deinit(&fs);
+
+       test_end();
+}
+
+static void test_fs_crypt_read_write_unencrypted(void)
+{
+       test_begin("fs-crypt: read write (maybe encrypted)");
+       const char *error;
+       struct fs *fs;
+
+       if (fs_init_from_string("crypt:public_key_path=:"
+                               "private_key_path=test_private_key.pem:"
+                               "maybe:posix",
+                               &test_fs_set, &fs, &error) < 0)
+               i_fatal("fs_init(crypt:posix) failed: %s", error);
+
+       i_unlink_if_exists("test_file");
+       /* write some data to disk */
+       unsigned char databuf[128];
+       /* avoid being detected as crypted */
+       memset(databuf, '\1', 8);
+       random_fill(databuf+8, sizeof(databuf)-8);
+
+       struct fs_file *file = fs_file_init(fs, "test_file", FS_OPEN_MODE_CREATE);
+       struct ostream *os = fs_write_stream(file);
+       test_assert(o_stream_send(os, databuf, sizeof(databuf)) == sizeof(databuf));
+       test_assert(o_stream_finish(os) == 1);
+       fs_write_stream_finish(file, &os);
+       fs_file_deinit(&file);
+
+       file = fs_file_init(fs, "test_file", FS_OPEN_MODE_READONLY);
+
+       struct istream *is = fs_read_stream(file, 8192);
+       buffer_t *readbuf = t_buffer_create(sizeof(databuf));
+       int ret;
+
+       while ((ret = i_stream_read(is)) > 0) {
+               size_t size;
+               const unsigned char *data = i_stream_get_data(is, &size);
+               buffer_append(readbuf, data, size);
+               i_stream_skip(is, size);
+       }
+
+       test_assert(ret == -1);
+       test_assert(is->eof);
+       test_assert(readbuf->used == sizeof(databuf) &&
+                   memcmp(readbuf->data, databuf, sizeof(databuf)) == 0);
+       i_stream_unref(&is);
+
+       fs_file_deinit(&file);
+       fs_deinit(&fs);
+
+       if (fs_init_from_string("crypt:public_key_path=test_public_key.pem:"
+                               "private_key_path=test_private_key.pem:"
+                               "maybe:posix",
+                               &test_fs_set, &fs, &error) < 0)
+               i_fatal("fs_init(crypt:posix) failed: %s", error);
+
+       i_unlink_if_exists("test_file");
+       /* write some data to disk */
+       random_fill(databuf, sizeof(databuf));
+
+       file = fs_file_init(fs, "test_file", FS_OPEN_MODE_CREATE);
+       os = fs_write_stream(file);
+       test_assert(o_stream_send(os, databuf, sizeof(databuf)) == sizeof(databuf));
+       test_assert(o_stream_finish(os) == 1);
+       fs_write_stream_finish(file, &os);
+       fs_file_deinit(&file);
+
+       file = fs_file_init(fs, "test_file", FS_OPEN_MODE_READONLY);
+
+       is = fs_read_stream(file, 8192);
+       readbuf = t_buffer_create(sizeof(databuf));
+
+       while ((ret = i_stream_read(is)) > 0) {
+               size_t size;
+               const unsigned char *data = i_stream_get_data(is, &size);
+               buffer_append(readbuf, data, size);
+               i_stream_skip(is, size);
+       }
+
+       test_assert(ret == -1);
+       test_assert(is->eof);
+       test_assert(readbuf->used == sizeof(databuf) &&
+                   memcmp(readbuf->data, databuf, sizeof(databuf)) == 0);
+       i_stream_unref(&is);
+
+       fs_file_deinit(&file);
+       fs_deinit(&fs);
+
+       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");
+}
+
+static bool test_init_dcrypt(void)
+{
+       const char *error;
+       struct dcrypt_settings set = {
+               .module_dir = top_builddir"/src/lib-dcrypt/.libs"
+       };
+       if (!dcrypt_initialize(NULL, &set, &error)) {
+               i_error("No functional dcrypt backend found - "
+                       "skipping tests: %s", error);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+int main(void)
+{
+       if (!test_init_dcrypt())
+               return 0;
+       void (*const tests[])(void)  = {
+               test_setup,
+               test_fs_crypt_read_write,
+               test_fs_crypt_read_write_0,
+               test_fs_crypt_read_write_unencrypted,
+               test_teardown,
+               NULL
+       };
+       int ret = test_run(tests);
+       dcrypt_deinitialize();
+       return ret;
+}