diff --git a/NEWS b/NEWS index aa87914cd2d..88e2a26ed00 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,65 @@ +v2.4.1 2025-03-28 Aki Tuomi + + * auth: Change unix_listener/auth-userdb/group = $SET:default_internal_group + This change needs dovecot_config_version=2.4.1. + * auth: lua - Remove support for single string result. + * imap: Unconditionally advertise SPECIAL-USE capability. + * lib-dcrypt: Install dcrypt_openssl.so into dovecot modules directory. + * lib-master: For glibc, default MALLOC_MMAP_THRESHOLD_=131072. + * lib-storage: Change default mail_cache_fields to: + hdr.date hdr.subject hdr.from hdr.sender hdr.reply-to hdr.to + hdr.cc hdr.bcc hdr.in-reply-to hdr.message-id + date.received size.virtual imap.bodystructure mime.parts hdr.references + hdr.importance hdr.x-priority hdr.x-open-xchange-share-url + pop3.uidl pop3.order. This change needs dovecot_config_version=2.4.1. + * lib-var-expand: Use moduledir instead of pkglibdir for crypt. + * lmtp: Change the default lmtp_user_concurrency_limit to 10. + This change needs dovecot_config_version=2.4.1. + * lmtp: Change the default service_restart_request_count to 1. + This change needs dovecot_config_version=2.4.1. + + auth: Allow configuring passdb/userdb sql to use auth-workers. + + config: Add default group @mailbox_defaults = english. + + config: Improve "Unknown setting" error with more details and + suggestions. + + doveconf: Add -U parameter to ignore unknown settings in config file. + + fts-flatcurve: Support lock files in VOLATILEDIR. + + imap-acl: Add support for the IMAP LIST-MYRIGHTS capability (RFC 8440). + + imap-client: Support ANONYMOUS authentication. + + imap: Implement support for the REPLACE capability. + - auth: ldap - Passdb fields were ignored with + passdb_ldap_bind_userdn=yes. + - auth: lua - Fix error result handling in lua passdb/userdb. + - auth: oauth2 - When building oauth2 failure reply, memory would leak. + - config: local_name handling would work wrong with multiple names and + wildcards. + - fts-flatcurve: A potential crash could occur when searching virtual + mailboxes. + Fixes: Panic: file fts-search.c: line 87 (level_scores_add_vuids): + assertion failed: (array_count(&vuids_arr) == array_count(&br->scores)) + - fts-flatcurve: Maybe queries were done wrong. + - fts-flatcurve: Non-selectable mailboxes were not ignored when doing + optimize/rescan. + - fts-flatcurve: Signal 11 crash could happen with fts rescan. + - fts: Fix crash caused by event object lifecycle mishandling. + - imap-hibernate: Client counters would get reset on unhibernation, + affecting imap_logout_format variables. + - imap: Crash would occur with Maildir when trying to send INPROGRESS + during mailbox syncing. + - ldap: Dovecot could not be compiled without LDAP. + - lib-dcrypt: Output stream encryption can cause assert crash if + attempting to encrypt over 64 GiB of data with GCM. This is still not + supported with GCM, but it fails better. + - lib-http: HTTP client context memory usage was increasing. + - lib-http: Pipeline corruption could happen after 100 Continue response. + - lib-settings: Variable expansion initialization could crash with + Panic: file settings.c: line 1560 (settings_var_expand_init_add): + assertion failed: (I_MAX(num_tables, num_provs) == num_ctx) + - lib-smtp: Pipelining initial SASL response after AUTH was broken. + - lib-var-expand: If filter failed, memory leak would occur. + - lib-var-expand: Older bison versions did not have error symbol for + handling causing unexpected behaviour on the parser on error conditions. + - quota: Quota calculations had minor bugs causing small errors. + v2.4.0 2025-01-24 Aki Tuomi * config: dovecot_config_version must be the first non-comment diff --git a/src/auth/db-oauth2.c b/src/auth/db-oauth2.c index fb61272754f..bd807e30b55 100644 --- a/src/auth/db-oauth2.c +++ b/src/auth/db-oauth2.c @@ -381,13 +381,9 @@ db_oauth2_add_extra_fields(struct db_oauth2_request *req, const char **error_r) { "oauth2", db_oauth2_var_expand_func_oauth2 }, { NULL, NULL } }; - const struct var_expand_provider *provider_arr[] = { - func_table, - NULL - }; struct var_expand_params params = { .table = auth_request_get_var_expand_table(req->auth_request), - .providers_arr = provider_arr, + .providers = func_table, .context = req, }; struct auth_request *request = req->auth_request; diff --git a/src/lib-auth/Makefile.am b/src/lib-auth/Makefile.am index 8769563f2d2..380457baf6d 100644 --- a/src/lib-auth/Makefile.am +++ b/src/lib-auth/Makefile.am @@ -19,7 +19,6 @@ libauth_la_SOURCES = \ libauth_crypt_la_SOURCES = \ mycrypt.c \ - password-scheme.c \ password-scheme-crypt.c \ password-scheme-sodium.c diff --git a/src/lib-settings/settings.c b/src/lib-settings/settings.c index ec60f7ad086..8e4ac6aef94 100644 --- a/src/lib-settings/settings.c +++ b/src/lib-settings/settings.c @@ -1407,33 +1407,67 @@ settings_var_expand_init_add(struct settings_var_expand_init_ctx *init_ctx, const struct var_expand_params *params) { unsigned int need_contexts = 1; + static const struct var_expand_table empty_table[] = { + VAR_EXPAND_TABLE_END + }; + static const struct var_expand_table *empty_table_ptr = empty_table; + static const struct var_expand_provider empty_provider[] = { + VAR_EXPAND_TABLE_END + }; + static const struct var_expand_provider *empty_provider_ptr = empty_provider; + if (params->table != NULL) { i_assert(params->tables_arr == NULL); array_push_back(&init_ctx->tables, ¶ms->table); + } else if (params->tables_arr == NULL && params->providers_arr == NULL) { + array_push_back(&init_ctx->tables, &empty_table_ptr); } if (params->providers != NULL) { i_assert(params->providers_arr == NULL); array_push_back(&init_ctx->providers, ¶ms->providers); + } else if (params->tables_arr == NULL && params->providers_arr == NULL) { + array_push_back(&init_ctx->providers, &empty_provider_ptr); } if (params->table != NULL || params->providers != NULL) array_push_back(&init_ctx->contexts, ¶ms->context); - if (params->tables_arr != NULL) { - for (unsigned int i = 0; params->tables_arr[i] != NULL; i++) { - if (i > need_contexts) - need_contexts = i; - array_push_back(&init_ctx->tables, - ¶ms->tables_arr[i]); - } - } - if (params->providers_arr != NULL) { - for (unsigned int i = 0; params->providers_arr[i] != NULL; i++) { - if (i > need_contexts) - need_contexts = i; - array_push_back(&init_ctx->providers, - ¶ms->providers_arr[i]); - } + if (params->tables_arr != NULL || params->providers_arr != NULL) { + unsigned int table_count = 0, prov_count = 0; + const struct var_expand_table *tbl; + const struct var_expand_provider *prov; + + /* Ensure that we get a table and provider so that contexts line up */ + do { + tbl = params->tables_arr != NULL ? + params->tables_arr[table_count] : NULL; + prov = params->providers_arr != NULL ? + params->providers_arr[prov_count] : NULL; + + /* for each provider and table, there needs to be + a correspoding entry in both arrays to make sure + that when we go through them, we get the correct + context too. + */ + if (tbl != NULL) { + array_push_back(&init_ctx->tables, &tbl); + table_count++; + } else if (prov != NULL) { + array_push_back(&init_ctx->tables, &empty_table_ptr); + } + if (prov != NULL) { + array_push_back(&init_ctx->providers, &prov); + prov_count++; + } else if (tbl != NULL) { + array_push_back(&init_ctx->providers, &empty_provider_ptr); + } + if (prov == NULL && tbl == NULL) + break; + } while (tbl != NULL || prov != NULL); + + if (I_MAX(table_count, prov_count) > need_contexts) + need_contexts = I_MAX(table_count, prov_count); } + if (params->escape_func != NULL) init_ctx->escape_func = params->escape_func; if (params->escape_context != NULL) @@ -1447,6 +1481,11 @@ settings_var_expand_init_add(struct settings_var_expand_init_ctx *init_ctx, array_push_back(&init_ctx->contexts, ctx); } i_assert(count == need_contexts); + } else { + /* Make sure we push some context enough to pad the context stack + to match the number of tables and providers. */ + for (unsigned int i = 1; i < need_contexts; i++) + array_push_back(&init_ctx->contexts, ¶ms->context); } /* ensure everything is still good */ diff --git a/src/lib-settings/test-settings.c b/src/lib-settings/test-settings.c index 1d56dce3a89..5236570b30f 100644 --- a/src/lib-settings/test-settings.c +++ b/src/lib-settings/test-settings.c @@ -250,10 +250,101 @@ static void test_settings_get(void) result_sorted_name_reverse); } +static int +test_var_expand_hierarchy_key1(const char *field_name ATTR_UNUSED, + const char **value_r, void *context, + const char **error_r ATTR_UNUSED) +{ + test_assert_strcmp(context, "context1"); + *value_r = "key1_value"; + return 0; +} + +static int +test_var_expand_hierarchy_key2(const char *key ATTR_UNUSED, + const char **value_r, void *context, + const char **error_r ATTR_UNUSED) +{ + test_assert_strcmp(context, "context2"); + *value_r = "key2_value"; + return 0; +} + +static void test_var_expand_hierarchy(void) +{ + test_begin("settings_get - hierarchical event"); + char *context1 = "context1"; + char *context2 = "context2"; + struct var_expand_table tab1[] = { + { .key = "key1", .func = test_var_expand_hierarchy_key1 }, + { .key = NULL } + }; + struct var_expand_params params1 = { + .table = tab1, + .context = context1, + }; + + struct var_expand_provider prov2[] = { + { "key2", test_var_expand_hierarchy_key2 }, + { NULL, NULL } + }; + struct var_expand_params params2 = { + .providers = prov2, + .context = context2, + }; + + struct var_expand_params params3; + i_zero(¶ms3); + params3.tables_arr = (const struct var_expand_table *const[]) { + tab1, + tab1, + NULL + }; + params3.contexts = (void *const[]) { + context1, + context1, + VAR_EXPAND_CONTEXTS_END, + }; + + struct settings_root *set_root = settings_root_init(); + settings_root_override(set_root, "test2_title", "%{key1} and %{key2:foo}", + SETTINGS_OVERRIDE_TYPE_DEFAULT); + + struct event *root = event_create(NULL); + event_set_ptr(root, SETTINGS_EVENT_ROOT, set_root); + event_set_ptr(root, SETTINGS_EVENT_VAR_EXPAND_PARAMS, ¶ms1); + + struct event *child = event_create(root); + event_set_ptr(child, SETTINGS_EVENT_VAR_EXPAND_PARAMS, ¶ms2); + + struct test2_settings *set; + const char *error; + test_assert(settings_get(child, &test2_setting_parser_info, 0, + &set, &error) == 0); + test_assert_strcmp(set->title, "key1_value and key2_value"); + settings_free(set); + + struct event *child2 = event_create(child); + event_set_ptr(child2, SETTINGS_EVENT_VAR_EXPAND_PARAMS, ¶ms3); + + test_assert(settings_get(child2, &test2_setting_parser_info, 0, + &set, &error) == 0); + test_assert_strcmp(set->title, "key1_value and key2_value"); + settings_free(set); + + + event_unref(&child2); + event_unref(&child); + event_unref(&root); + settings_root_deinit(&set_root); + test_end(); +} + int main(void) { static void (*const test_functions[])(void) = { test_settings_get, + test_var_expand_hierarchy, NULL }; return test_run(test_functions); diff --git a/src/lib/test-file-cache.c b/src/lib/test-file-cache.c index 4316dfdc9fa..b22eeddd00b 100644 --- a/src/lib/test-file-cache.c +++ b/src/lib/test-file-cache.c @@ -265,17 +265,23 @@ static void test_file_cache_errors(void) page_size, strerror(ENOMEM)); test_assert(setrlimit(RLIMIT_AS, &rl_new) == 0); test_expect_error_string(errstr); - test_assert(file_cache_set_size(cache, 1024) == -1); - test_assert(setrlimit(RLIMIT_AS, &rl_cur) == 0); - - /* same for mremap */ - errstr = t_strdup_printf("mremap_anon(.test_file_cache, %zu) failed: %s", - page_size*2, strerror(ENOMEM)); - test_assert(file_cache_set_size(cache, 1) == 0); - test_assert(setrlimit(RLIMIT_AS, &rl_new) == 0); - test_expect_error_string(errstr); - test_assert(file_cache_set_size(cache, page_size*2) == -1); - test_assert(setrlimit(RLIMIT_AS, &rl_cur) == 0); + int ret = file_cache_set_size(cache, 1024); + if (ret == 0) { + /* RLIMIT_AS isn't working in this OS - skip this test */ + test_expect_no_more_errors(); + } else { + test_assert(ret == -1); + test_assert(setrlimit(RLIMIT_AS, &rl_cur) == 0); + + /* same for mremap */ + errstr = t_strdup_printf("mremap_anon(.test_file_cache, %zu) failed: %s", + page_size*2, strerror(ENOMEM)); + test_assert(file_cache_set_size(cache, 1) == 0); + test_assert(setrlimit(RLIMIT_AS, &rl_new) == 0); + test_expect_error_string(errstr); + test_assert(file_cache_set_size(cache, page_size*2) == -1); + test_assert(setrlimit(RLIMIT_AS, &rl_cur) == 0); + } #endif file_cache_free(&cache); diff --git a/version b/version new file mode 100644 index 00000000000..005119baaa0 --- /dev/null +++ b/version @@ -0,0 +1 @@ +2.4.1