#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"
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;
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, ¶ms, &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;
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)}", ¶ms, &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)}", ¶ms, &error) == 0, i);
+ test_assert_strcmp_idx(str_c(output), table[4].value, i);
};
var_expand_crypt_deinit();
/* 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);
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)
/* 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;
}
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;
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);
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)