]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
var-expand-crypt: Convert to new var_expand
authorAki Tuomi <aki.tuomi@open-xchange.com>
Thu, 29 Aug 2024 13:04:04 +0000 (16:04 +0300)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 12 Feb 2025 10:34:14 +0000 (12:34 +0200)
src/plugins/var-expand-crypt/Makefile.am
src/plugins/var-expand-crypt/test-var-expand-crypt.c
src/plugins/var-expand-crypt/var-expand-crypt-plugin.c

index d220979f1d9485d815af0e9fe973eef3777b9288..8b198f243ed027d5a71de68e0da94f7e55319c43 100644 (file)
@@ -1,4 +1,5 @@
 AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/lib-var-expand \
        -I$(top_srcdir)/src/lib \
        -I$(top_srcdir)/src/lib-test \
        -I$(top_srcdir)/src/lib-dcrypt
@@ -24,6 +25,7 @@ lib20_var_expand_crypt_la_SOURCES = \
 test_programs = test-var-expand-crypt
 
 test_var_expand_crypt_CFLAGS = \
+       $(AM_CPPFLAGS) \
        -DDCRYPT_BUILD_DIR=\"$(top_builddir)/src/lib-dcrypt\"
 test_var_expand_crypt_SOURCES = \
        test-var-expand-crypt.c
index 72c564aece5751e01d97374819746873a6a84e45..5564cfacdec2f374dbbc9fc7d161bbc7a95e7086 100644 (file)
@@ -3,7 +3,7 @@
 #include "lib.h"
 #include "test-common.h"
 #include "str.h"
-#include "var-expand.h"
+#include "var-expand-new.h"
 #include "randgen.h"
 #include "dcrypt.h"
 
@@ -15,28 +15,29 @@ extern void var_expand_crypt_deinit(void);
 static void test_var_expand_crypt(void)
 {
        struct var_expand_table table[] = {
-               { '\0', "98b3b40a48ca40f998b3b40a48ca40f9", "iv" },
-               { '\0', "cc2981c8f38aea59cc2981c8f38aea59", "key" },
-               { '\0', "46b58741763fe22598014be26331a082", "encrypted_noiv" },
-               { '\0', "98b3b40a48ca40f998b3b40a48ca40f9$46b58741763fe22598014be26331a082$", "encrypted" },
-               { '\0', "hello, world", "decrypted" },
-               { '\0', NULL, "encrypted2" },
-               { '\0', NULL, NULL }
+               { .key = "iv", .value = "98b3b40a48ca40f998b3b40a48ca40f9" },
+               { .key = "key", .value = "cc2981c8f38aea59cc2981c8f38aea59" },
+               { .key = "encrypted_raw", .value = "46b58741763fe22598014be26331a082" },
+               { .key = "encrypted", .value = "98b3b40a48ca40f998b3b40a48ca40f9$46b58741763fe22598014be26331a082$" },
+               { .key = "decrypted", .value = "hello, world" },
+               { .key = "encrypted2", .value = NULL },
+               { NULL, NULL, NULL }
+       };
+       const struct var_expand_params params = {
+               .table = table,
        };
 
-       static struct {
+       const struct {
                const char *input;
                const char *output;
                int expect_ret;
        } test_cases[] = {
-               { "%{encrypt;algo=null:decrypted}", "", -1 },
-               { "%{encrypt;algo=aes-128-cbc,iv=98b3b40a48ca40f998b3b40a48ca40f9,key=cc2981c8f38aea59cc2981c8f38aea59:decrypted}", "98b3b40a48ca40f998b3b40a48ca40f9$46b58741763fe22598014be26331a082$", 1 },
-               { "%{encrypt;noiv=yes,algo=aes-128-cbc,iv=98b3b40a48ca40f998b3b40a48ca40f9,key=cc2981c8f38aea59cc2981c8f38aea59:decrypted}", "46b58741763fe22598014be26331a082", 1 },
-               { "%{encrypt;algo=aes-128-cbc,iv=%{iv},key=%{key}:decrypted}", "98b3b40a48ca40f998b3b40a48ca40f9$46b58741763fe22598014be26331a082$", 1 },
-               { "%{decrypt;algo=null:encrypted}", "", -1 },
-               { "%{decrypt;algo=aes-128-cbc,key=%{key}:encrypted}", "hello, world", 1 },
-               { "%{decrypt;algo=aes-128-cbc,iv=%{iv},key=%{key}:encrypted_noiv}", "hello, world", 1 },
-               { "%{decrypt;algo=aes-128-cbc,iv=98b3b40a48ca40f998b3b40a48ca40f9,key=cc2981c8f38aea59cc2981c8f38aea59:encrypted_noiv}", "hello, world", 1 },
+               { "%{decrypted|encrypt(algorithm='null')}", "", -1 },
+               { "%{decrypted|encrypt(algorithm='aes-128-cbc',iv=iv,key=key)}", "98b3b40a48ca40f998b3b40a48ca40f9$46b58741763fe22598014be26331a082$", 0 },
+               { "%{decrypted|encrypt(algorithm='aes-128-cbc',iv=iv,key=key,raw=1)|hexlify}", "46b58741763fe22598014be26331a082", 0 },
+               { "%{encrypted|decrypt(algorithm='null')}", "", -1 },
+               { "%{encrypted|decrypt(algorithm='aes-128-cbc',key=key)}", "hello, world", 0 },
+               { "%{encrypted_raw|unhexlify|decrypt(algorithm='aes-128-cbc',iv=iv,key=key,raw=1)}", "hello, world", 0 },
        };
 
        unsigned int i;
@@ -47,15 +48,14 @@ static void test_var_expand_crypt(void)
        for(i=0; i < N_ELEMENTS(test_cases); i++) T_BEGIN {
                const char *error;
                string_t *dest = t_str_new(32);
-               int ret = var_expand_with_table(dest, test_cases[i].input,
-                                               table, &error);
+               int ret = var_expand_new(dest, test_cases[i].input, &params, &error);
                if (ret < 0) {
                        if (test_cases[i].expect_ret == -1)
                                i_info("Expected: var_expand(%s): %s", test_cases[i].input, error);
                        else
                                i_error("var_expand(%s): %s", test_cases[i].input, error);
                }
-               test_assert_idx(strcmp(str_c(dest), test_cases[i].output)==0, i);
+               test_assert_strcmp_idx(str_c(dest), test_cases[i].output, i);
                test_assert_idx(ret == test_cases[i].expect_ret, i);
        } T_END;
 
@@ -71,10 +71,10 @@ static void test_var_expand_crypt(void)
                str_truncate(input, 0);
                str_truncate(output, 0);
 
-               test_assert_idx(var_expand_with_table(input, "%{encrypt;algo=aes-128-cbc,key=%{key}:decrypted}", table, &error) == 1, i);
+               test_assert_idx(var_expand_new(input, "%{decrypted|encrypt(algorithm='aes-128-cbc',key=key)}", &params, &error) == 0, i);
                table[5].value = str_c(input);
-               test_assert_idx(var_expand_with_table(output, "%{decrypt;algo=aes-128-cbc,key=%{key}:encrypted2}", table, &error) == 1, i);
-               test_assert_idx(strcmp(str_c(output), table[4].value)==0, i);
+               test_assert_idx(var_expand_new(output, "%{encrypted2|decrypt(algorithm='aes-128-cbc',key=key)}", &params, &error) == 0, i);
+               test_assert_strcmp_idx(str_c(output), table[4].value, i);
        };
 
        var_expand_crypt_deinit();
index 0de0f7d5fdbfa774617ece2098992d3b5ffeb795..3172102d7802b6aabcb164a7570a0a6d2a365df8 100644 (file)
@@ -1,12 +1,8 @@
 /* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
-#include "array.h"
 #include "hex-binary.h"
-#include "base64.h"
 #include "str.h"
-#include "strescape.h"
-#include "var-expand.h"
 #include "var-expand-private.h"
 #include "dcrypt.h"
 
 
 struct module;
 
-enum crypt_field_format {
-       FORMAT_HEX,
-       FORMAT_BASE64
-};
-
 struct var_expand_crypt_context {
-       struct var_expand_context *ctx;
        const char *algo;
        string_t *iv;
        string_t *enckey;
-       enum crypt_field_format format;
-       bool enc_result_only:1;
+       buffer_t *input;
+       bool raw;
 };
 
 static bool var_expand_crypt_initialize(const char **error_r);
@@ -35,71 +25,6 @@ void var_expand_crypt_deinit(void);
 void auth_var_expand_crypt_init(struct module *module);
 void auth_var_expand_crypt_deinit(void);
 
-static int
-var_expand_crypt_settings(struct var_expand_crypt_context *ctx,
-                         const char *const *args, const char **error_r)
-{
-       while(args != NULL && *args != NULL) {
-               int ret;
-               const char *k = t_strcut(*args, '=');
-               const char *value = strchr(*args, '=');
-               if (value == NULL) {
-                       args++;
-                       continue;
-               } else {
-                       value++;
-               }
-
-               if (strcmp(k, "iv") == 0) {
-                       str_truncate(ctx->iv, 0);
-                       if ((ret = var_expand_with_arrays(ctx->iv, value,
-                                                         ctx->ctx->tables,
-                                                         ctx->ctx->funcs,
-                                                         error_r)) <= 0) {
-                               return ret;
-                       }
-                       const char *hexiv = t_strdup(str_c(ctx->iv));
-                       /* try to decode IV */
-                       str_truncate(ctx->iv, 0);
-                       hex_to_binary(hexiv, ctx->iv);
-               } if (strcmp(k, "noiv") == 0) {
-                       ctx->enc_result_only = strcasecmp(value, "yes")==0;
-               } if (strcmp(k, "algo") == 0) {
-                       ctx->algo = value;
-               } else if (strcmp(k, "key") == 0) {
-                       str_truncate(ctx->enckey, 0);
-                       if ((ret = var_expand_with_arrays(ctx->enckey, value,
-                                                         ctx->ctx->tables,
-                                                         ctx->ctx->funcs,
-                                                         error_r)) <= 0) {
-                               return ret;
-                       }
-                       const char *hexkey = t_strdup(str_c(ctx->enckey));
-                       str_truncate(ctx->enckey, 0);
-                       hex_to_binary(hexkey, ctx->enckey);
-               } else if (strcmp(k, "format") == 0) {
-                       if (strcmp(value, "hex") == 0) {
-                               ctx->format = FORMAT_HEX;
-                       } else if (strcmp(value, "base64") == 0) {
-                               ctx->format = FORMAT_BASE64;
-                       } else {
-                               *error_r = t_strdup_printf(
-                                       "Cannot parse hash arguments:"
-                                       "'%s' is not supported format",
-                                       value);
-                               return -1;
-                       }
-               }
-               args++;
-       }
-
-       if (ctx->algo == NULL) {
-               ctx->algo = VAR_EXPAND_CRYPT_DEFAULT_ALGO;
-       }
-
-       return 1;
-}
-
 static int
 var_expand_crypt(struct dcrypt_context_symmetric *dctx, buffer_t *key, buffer_t *iv,
                 const buffer_t *input, buffer_t *output, const char **error_r)
@@ -110,7 +35,7 @@ var_expand_crypt(struct dcrypt_context_symmetric *dctx, buffer_t *key, buffer_t
                /* acquire IV */
                dcrypt_ctx_sym_get_iv(dctx, iv);
        } else if (dcrypt_ctx_sym_get_iv_length(dctx) != iv->used) {
-               *error_r = t_strdup_printf("crypt: IV length invalid (%zu != %u)",
+               *error_r = t_strdup_printf("IV length invalid (%zu != %u)",
                                           iv->used,
                                           dcrypt_ctx_sym_get_iv_length(dctx));
                return -1;
@@ -119,7 +44,7 @@ var_expand_crypt(struct dcrypt_context_symmetric *dctx, buffer_t *key, buffer_t
        }
 
        if (dcrypt_ctx_sym_get_key_length(dctx) != key->used) {
-               *error_r = t_strdup_printf("crypt: Key length invalid (%zu != %u)",
+               *error_r = t_strdup_printf("Key length invalid (%zu != %u)",
                                           key->used,
                                           dcrypt_ctx_sym_get_key_length(dctx));
                return -1;
@@ -135,178 +60,143 @@ var_expand_crypt(struct dcrypt_context_symmetric *dctx, buffer_t *key, buffer_t
        return 0;
 }
 
-static int
-var_expand_encrypt(struct var_expand_context *_ctx,
-                  const char *key, const char *field,
-                  const char **result_r, const char **error_r)
+static int var_expand_crypt_settings(struct var_expand_state *state,
+                                    const struct var_expand_statement *stmt,
+                                    struct var_expand_crypt_context *ctx,
+                                    const char **error_r)
 {
-       if (!var_expand_crypt_initialize(error_r))
-               return -1;
+       const char *iv;
+
+       ctx->iv = t_buffer_create(32);
+       ctx->enckey = t_buffer_create(32);
+       ctx->algo = VAR_EXPAND_CRYPT_DEFAULT_ALGO;
+
+       struct var_expand_parameter_iter_context *iter =
+               var_expand_parameter_iter_init(stmt);
+       while (var_expand_parameter_iter_more(iter)) {
+               const struct var_expand_parameter *par =
+                       var_expand_parameter_iter_next(iter);
+
+               const char *key = var_expand_parameter_key(par);
+               if (key == NULL)
+                       ERROR_TOO_MANY_UNNAMED_PARAMETERS;
+               else if (strcmp(key, "algorithm") == 0) {
+                       if (var_expand_parameter_string_or_var(state, par,
+                                                              &ctx->algo, error_r) < 0)
+                               return -1;
+               } else if (strcmp(key, "iv") == 0) {
+                       if (var_expand_parameter_string_or_var(state, par, &iv,
+                                                              error_r) < 0) {
+                               return -1;
+                       }
+                       hex_to_binary(iv, ctx->iv);
+               } else if (strcmp(key, "key") == 0) {
+                       const char *enckey;
+                       if (var_expand_parameter_string_or_var(state, par, &enckey,
+                                                              error_r) < 0) {
+                               return -1;
+                       }
+                       hex_to_binary(enckey, ctx->enckey);
+               } else if (strcmp(key, "raw") == 0) {
+                       if (var_expand_parameter_bool_or_var(state, par, &ctx->raw,
+                                                            error_r) < 0)
+                               return -1;
+               } else
+                       ERROR_UNSUPPORTED_KEY(key);
+       }
 
-       const char *p = strchr(key, ';');
-       const char *const *args = NULL;
-       const char *value;
-       struct var_expand_crypt_context ctx;
-       string_t *dest;
-       int ret = 0;
+       ERROR_IF_NO_TRANSFER_TO(stmt->function);
 
-       memset(&ctx, 0, sizeof(ctx));
-       ctx.ctx = _ctx;
-       ctx.format = FORMAT_HEX;
+       ctx->input = state->transfer;
+       if (ctx->raw || strcmp(stmt->function, "encrypt") == 0)
+               return 0;
 
-       if (p != NULL) {
-               args = t_strsplit(p+1, ",");
+       /* handle $ separated input, only support hex */
+       const char *const *parts = t_strsplit(str_c(state->transfer), "$");
+       if (str_array_length(parts) == 3 && *parts[2] == '\0') {
+               if (ctx->iv->used > 0) {
+                       *error_r = "Cannot have iv in parameter and input";
+                       return -1;
+               }
+               hex_to_binary(parts[0], ctx->iv);
+               ctx->input = t_buffer_create(strlen(parts[1]) / 2);
+               hex_to_binary(parts[1], ctx->input);
+       } else {
+               *error_r = "Invalid input format";
+               return -1;
        }
 
-       string_t *field_value = t_str_new(64);
-       ctx.iv = t_str_new(64);
-       ctx.enckey = t_str_new(64);
-       string_t *tmp = t_str_new(128);
-
-       if ((ret = var_expand_long(_ctx, field, strlen(field),
-                                  &value, error_r)) <= 0) {
-               return ret;
-       }
+       return 0;
 
-       if (*value == '\0') {
-               *result_r = value;
-               return ret;
-       }
+}
 
-       if ((ret = var_expand_crypt_settings(&ctx, args, error_r)) <= 0)
-               return ret;
+static int
+var_expand_encrypt(const struct var_expand_statement *stmt,
+                  struct var_expand_state *state, const char **error_r)
+{
+       if (!var_expand_crypt_initialize(error_r))
+               return -1;
 
-       str_append(field_value, value);
+       struct var_expand_crypt_context ctx;
+       i_zero(&ctx);
+       if (var_expand_crypt_settings(state, stmt, &ctx, error_r) < 0)
+               return -1;
 
        struct dcrypt_context_symmetric *dctx;
        if (!dcrypt_ctx_sym_create(ctx.algo, DCRYPT_MODE_ENCRYPT, &dctx, error_r))
                return -1;
+       buffer_t *dest = t_buffer_create(state->transfer->used*2);
 
-       ret = var_expand_crypt(dctx, ctx.enckey, ctx.iv, field_value, tmp, error_r);
+       int ret = var_expand_crypt(dctx, ctx.enckey, ctx.iv, ctx.input,
+                                  dest, error_r);
        dcrypt_ctx_sym_destroy(&dctx);
 
        if (ret == 0) {
-               /* makes compiler happy */
-               const char *enciv = NULL;
-               const char *res = NULL;
-
-               switch(ctx.format) {
-               case FORMAT_HEX:
-                       enciv = binary_to_hex(ctx.iv->data, ctx.iv->used);
-                       res = binary_to_hex(tmp->data, tmp->used);
-                       break;
-               case FORMAT_BASE64:
-                       dest = t_str_new(32);
-                       base64_encode(ctx.iv->data, ctx.iv->used, dest);
-                       enciv = str_c(dest);
-                       dest = t_str_new(32);
-                       base64_encode(tmp->data, tmp->used, dest);
-                       res = str_c(dest);
-                       break;
-               default:
-                       i_unreached();
+               if (ctx.raw)
+                       var_expand_state_set_transfer_binary(state, dest->data, dest->used);
+               else {
+                       state->transfer_set = TRUE;
+                       str_truncate(state->transfer, 0);
+                       binary_to_hex_append(state->transfer, ctx.iv->data, ctx.iv->used);
+                       str_append_c(state->transfer, '$');
+                       binary_to_hex_append(state->transfer, dest->data, dest->used);
+                       str_append_c(state->transfer, '$');
                }
-               if (ctx.enc_result_only)
-                       *result_r = t_strdup(res);
-               else
-                       *result_r = t_strdup_printf("%s$%s$", enciv, res);
-               ret = 1;
        }
 
        return ret;
 }
 
 static int
-var_expand_decrypt(struct var_expand_context *_ctx,
-                  const char *key, const char *field,
-                  const char **result_r, const char **error_r)
+var_expand_decrypt(const struct var_expand_statement *stmt,
+                  struct var_expand_state *state, const char **error_r)
+
 {
        if (!var_expand_crypt_initialize(error_r))
                return -1;
 
-       const char *p = strchr(key, ';');
-       const char *const *args = NULL;
-       const char *value;
        struct var_expand_crypt_context ctx;
-       int ret = 0;
-
-       memset(&ctx, 0, sizeof(ctx));
-       ctx.ctx = _ctx;
-       ctx.format = FORMAT_HEX;
-
-       if (p != NULL) {
-               args = t_strsplit(p+1, ",");
-       }
-
-       string_t *field_value = t_str_new(64);
-       ctx.iv = t_str_new(64);
-       ctx.enckey = t_str_new(64);
-       string_t *tmp = t_str_new(128);
-
-       if ((ret = var_expand_long(_ctx, field, strlen(field),
-                                  &value, error_r)) <= 0) {
-               return ret;
-       }
-
-       if (*value == '\0') {
-               *result_r = value;
-               return ret;
-       }
-
-       if ((ret = var_expand_crypt_settings(&ctx, args, error_r)) <= 0)
-               return ret;
-
-       const char *encdata = value;
-       const char *enciv = "";
-
-       /* make sure IV is correct */
-       if (ctx.iv->used == 0 && (p = strchr(encdata, '$')) != NULL) {
-               /* see if IV can be taken from data */
-               enciv = t_strcut(encdata, '$');
-               encdata = t_strcut(p+1,'$');
-       }
-
-       str_truncate(field_value, 0);
-
-       /* try to decode iv and encdata */
-       switch(ctx.format) {
-       case FORMAT_HEX:
-               if (ctx.iv->used == 0)
-                       hex_to_binary(enciv, ctx.iv);
-               hex_to_binary(encdata, field_value);
-               break;
-       case FORMAT_BASE64:
-               if (ctx.iv->used == 0)
-                       str_append_str(ctx.iv, t_base64_decode_str(enciv));
-               str_append_str(field_value, t_base64_decode_str(encdata));
-               break;
-       }
-
-       if (ctx.iv->used == 0) {
-               *error_r = t_strdup_printf("decrypt: IV missing");
+       i_zero(&ctx);
+       if (var_expand_crypt_settings(state, stmt, &ctx, error_r) < 0)
                return -1;
-       }
 
        struct dcrypt_context_symmetric *dctx;
        if (!dcrypt_ctx_sym_create(ctx.algo, DCRYPT_MODE_DECRYPT, &dctx, error_r))
                return -1;
-       ret = var_expand_crypt(dctx, ctx.enckey, ctx.iv, field_value, tmp, error_r);
+       string_t *dest = t_buffer_create(state->transfer->used / 2);
+       int ret = var_expand_crypt(dctx, ctx.enckey, ctx.iv, ctx.input,
+                                  dest, error_r);
        dcrypt_ctx_sym_destroy(&dctx);
 
        if (ret == 0) {
-               *result_r = str_c(tmp);
-               ret = 1;
+               if (memchr(dest->data, '\0', dest->used) != NULL)
+                       var_expand_state_set_transfer_binary(state, dest->data, dest->used);
+               else
+                       var_expand_state_set_transfer(state, str_c(dest));
        }
-
        return ret;
 }
 
-static const struct var_expand_extension_func_table funcs[] = {
-       { "encrypt", var_expand_encrypt },
-       { "decrypt", var_expand_decrypt },
-       { NULL, NULL, }
-};
-
 static bool var_expand_crypt_initialize(const char **error_r)
 {
        return dcrypt_initialize(NULL, NULL, error_r);
@@ -314,15 +204,17 @@ static bool var_expand_crypt_initialize(const char **error_r)
 
 void var_expand_crypt_init(struct module *module ATTR_UNUSED)
 {
-       var_expand_register_func_array(funcs);
        /* do not initialize dcrypt here - saves alot of memory
           to not load openssl every time. Only load it if
           needed */
+       var_expand_register_filter("encrypt", var_expand_encrypt);
+       var_expand_register_filter("decrypt", var_expand_decrypt);
 }
 
 void var_expand_crypt_deinit(void)
 {
-       var_expand_unregister_func_array(funcs);
+       var_expand_unregister_filter("encrypt");
+       var_expand_unregister_filter("decrypt");
 }
 
 void auth_var_expand_crypt_init(struct module *module)