]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
doveadm: Support dumping encrypted dcrypt file content
authorAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 29 Nov 2023 07:08:46 +0000 (09:08 +0200)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 17 Jan 2024 08:23:46 +0000 (10:23 +0200)
src/doveadm/doveadm-dump-dcrypt-file.c

index b86e1878b4ed74fa11356390a2b4cb34967f888c..433181931ef6b4070f37e50b13184ceee5284e54 100644 (file)
@@ -1,21 +1,48 @@
 /* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "buffer.h"
+#include "hex-binary.h"
+#include "str.h"
 #include "dcrypt.h"
+#include "write-full.h"
 #include "istream.h"
 #include "istream-decrypt.h"
 #include "dcrypt-iostream.h"
 #include "doveadm-dump.h"
 #include <stdio.h>
 
+struct dcrypt_dump_context {
+       const char *key;
+       struct dcrypt_private_key *dec_key;
+       const char *password;
+       char *digest;
+};
+
 static int get_digest(const char *digest,
-               struct dcrypt_private_key **priv_key_r ATTR_UNUSED,
-               const char **error_r ATTR_UNUSED,
-               void *context)
+                     struct dcrypt_private_key **priv_key_r,
+                     const char **error_r, void *context)
 {
-       const char **digest_r = (const char**)context;
-       *digest_r = t_strdup(digest);
-       return 0;
+       struct dcrypt_dump_context *ctx = context;
+       ctx->digest = i_strdup(digest);
+       string_t *keydata = t_str_new(4096);
+       struct dcrypt_private_key *priv;
+       if (buffer_append_full_file(keydata, ctx->key, SIZE_MAX, error_r) !=
+           BUFFER_APPEND_OK)
+               return -1;
+       if (!dcrypt_key_load_private(&priv, str_c(keydata), ctx->password,
+                                    ctx->dec_key, error_r))
+               return -1;
+       /* check digest */
+       buffer_t *id = t_buffer_create(32);
+       if (!dcrypt_key_id_private(priv, "sha256", id, error_r))
+               return -1;
+       if (strcmp(binary_to_hex(id->data, id->used), digest) != 0) {
+               dcrypt_key_unref_private(&priv);
+               return 0;
+       }
+       *priv_key_r = priv;
+       return 1;
 }
 
 static void dcrypt_istream_dump_metadata(const struct istream *stream)
@@ -42,30 +69,46 @@ static void dcrypt_istream_dump_metadata(const struct istream *stream)
 }
 
 static bool dcrypt_file_dump_metadata(struct doveadm_cmd_context *cctx,
-                                     const char *filename, bool print)
+                                     const char *filename, bool print,
+                                     struct dcrypt_dump_context *ctx)
 {
        bool ret = FALSE;
        struct istream *is = i_stream_create_file(filename, IO_BLOCK_SIZE);
-       const char *key_digest = NULL;
-       struct istream *ds = i_stream_create_decrypt_callback(is,
-                       get_digest, &key_digest);
+       struct istream *ds =
+               i_stream_create_decrypt_callback(is, get_digest, ctx);
 
-       ssize_t size = i_stream_read(ds);
-       i_assert(size < 0);
+       size_t size;
+       const unsigned char *data;
+       ssize_t ret2 = i_stream_read(ds);
 
-       if (key_digest != NULL) {
+       i_assert(ctx->key != NULL || ret2 < 2);
+
+       if (ctx->digest != NULL) {
                ret = TRUE;
                if (print) {
                        dcrypt_istream_dump_metadata(ds);
-                       printf("decrypt key digest: %s\n", key_digest);
+                       printf("decrypt key digest: %s\n", ctx->digest);
                }
        } else if (print && ds->stream_errno != 0) {
                e_error(cctx->event, "read(%s) failed: %s",
                        i_stream_get_name(ds), i_stream_get_error(ds));
        }
 
+       if (print && ctx->key != NULL) {
+               while (i_stream_read_more(ds, &data, &size) > 0) {
+                       if (write_full(STDOUT_FILENO, data, size) < 0)
+                               i_fatal("write(STDOUT) failed: %m");
+                       i_stream_skip(ds, size);
+               }
+               if (ds->stream_errno != 0) {
+                       i_error("read(%s) failed: %s", i_stream_get_name(ds),
+                               i_stream_get_error(ds));
+               }
+       }
+
        i_stream_unref(&ds);
        i_stream_unref(&is);
+       i_free(ctx->digest);
        return ret;
 }
 
@@ -73,22 +116,36 @@ static bool test_dump_dcrypt_file(struct doveadm_cmd_context *cctx,
                                  const char *path)
 {
        const char *error;
+       struct dcrypt_dump_context ctx;
+       i_zero(&ctx);
        if (!dcrypt_initialize("openssl", NULL, &error)) {
                e_error(cctx->event, "%s", error);
                return FALSE;
        }
-       bool ret = dcrypt_file_dump_metadata(cctx, path, FALSE);
+       bool ret = dcrypt_file_dump_metadata(cctx, path, FALSE, &ctx);
        return ret;
 }
 
-static void
-cmd_dump_dcrypt_file(struct doveadm_cmd_context *cctx,
-                    const char *path, const char *const *args ATTR_UNUSED)
+static void cmd_dump_dcrypt_file(struct doveadm_cmd_context *cctx,
+                                const char *path, const char *const *args)
 {
        const char *error = NULL;
+       struct dcrypt_dump_context ctx;
+       i_zero(&ctx);
        if (!dcrypt_initialize("openssl", NULL, &error))
                i_fatal("dcrypt_initialize failed: %s", error);
-       (void)dcrypt_file_dump_metadata(cctx, path, TRUE);
+       for (; *args != NULL; args++) {
+               const char *key, *value;
+               if (!t_split_key_value_eq(*args, &key, &value))
+                       i_fatal("Invalid argument '%s': Missing '='", *args);
+               if (strcmp(key, "private_key") == 0)
+                       ctx.key = value;
+               else if (strcmp(key, "password") == 0)
+                       ctx.password = value;
+               else
+                       i_fatal("Unsupported argument '%s'", key);
+       }
+       (void)dcrypt_file_dump_metadata(cctx, path, TRUE, &ctx);
 }
 
 struct doveadm_cmd_dump doveadm_cmd_dump_dcrypt_file = {