diff -up dovecot-2.2.36/src/auth/auth-request.c.CVE_2020_12674prereq dovecot-2.2.36/src/auth/auth-request.c --- dovecot-2.2.36/src/auth/auth-request.c.CVE_2020_12674prereq 2020-08-09 14:37:53.468591087 +0200 +++ dovecot-2.2.36/src/auth/auth-request.c 2020-08-09 14:37:53.482591165 +0200 @@ -16,6 +16,7 @@ #include "auth-cache.h" #include "auth-request.h" #include "auth-request-handler.h" +#include "auth-request-handler-private.h" #include "auth-request-stats.h" #include "auth-client-connection.h" #include "auth-master-connection.h" @@ -68,9 +69,6 @@ static void auth_request_userdb_import(struct auth_request *request, const char *args); static -void auth_request_verify_plain_continue(struct auth_request *request, - verify_plain_callback_t *callback); -static void auth_request_lookup_credentials_policy_continue(struct auth_request *request, lookup_credentials_callback_t *callback); static @@ -211,10 +209,12 @@ void auth_request_success_continue(struc return; } - stats = auth_request_stats_get(request); - stats->auth_success_count++; - if (request->master_user != NULL) - stats->auth_master_success_count++; + if (request->set->stats) { + stats = auth_request_stats_get(request); + stats->auth_success_count++; + if (request->master_user != NULL) + stats->auth_master_success_count++; + } auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED); auth_request_refresh_last_access(request); @@ -228,8 +228,10 @@ void auth_request_fail(struct auth_reque i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); - stats = auth_request_stats_get(request); - stats->auth_failure_count++; + if (request->set->stats) { + stats = auth_request_stats_get(request); + stats->auth_failure_count++; + } auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED); auth_request_refresh_last_access(request); @@ -989,7 +991,7 @@ void auth_request_policy_penalty_finish( switch(ctx->type) { case AUTH_POLICY_CHECK_TYPE_PLAIN: - auth_request_verify_plain_continue(ctx->request, ctx->callback_plain); + ctx->request->handler->verify_plain_continue_callback(ctx->request, ctx->callback_plain); return; case AUTH_POLICY_CHECK_TYPE_LOOKUP: auth_request_lookup_credentials_policy_continue(ctx->request, ctx->callback_lookup); @@ -1036,7 +1038,8 @@ void auth_request_verify_plain(struct au request->user_changed_by_lookup = FALSE; if (request->policy_processed || !request->set->policy_check_before_auth) { - auth_request_verify_plain_continue(request, callback); + request->handler->verify_plain_continue_callback(request, + callback); } else { ctx = p_new(request->pool, struct auth_policy_check_ctx, 1); ctx->request = request; @@ -1046,9 +1049,9 @@ void auth_request_verify_plain(struct au } } -static -void auth_request_verify_plain_continue(struct auth_request *request, - verify_plain_callback_t *callback) { +void auth_request_default_verify_plain_continue(struct auth_request *request, + verify_plain_callback_t *callback) +{ struct auth_passdb *passdb; enum passdb_result result; diff -up dovecot-2.2.36/src/auth/auth-request-handler.c.CVE_2020_12674prereq dovecot-2.2.36/src/auth/auth-request-handler.c --- dovecot-2.2.36/src/auth/auth-request-handler.c.CVE_2020_12674prereq 2020-08-09 14:37:53.467591082 +0200 +++ dovecot-2.2.36/src/auth/auth-request-handler.c 2020-08-09 14:37:53.482591165 +0200 @@ -16,32 +16,29 @@ #include "auth-token.h" #include "auth-master-connection.h" #include "auth-request-handler.h" +#include "auth-request-handler-private.h" #include "auth-policy.h" #define AUTH_FAILURE_DELAY_CHECK_MSECS 500 -struct auth_request_handler { - int refcount; - pool_t pool; - HASH_TABLE(void *, struct auth_request *) requests; - - unsigned int connect_uid, client_pid; - - auth_client_request_callback_t *callback; - struct auth_client_connection *conn; - - auth_master_request_callback_t *master_callback; - - unsigned int destroyed:1; - unsigned int token_auth:1; -}; - static ARRAY(struct auth_request *) auth_failures_arr; static struct aqueue *auth_failures; static struct timeout *to_auth_failures; static void auth_failure_timeout(void *context) ATTR_NULL(1); +static void +auth_request_handler_default_reply_callback(struct auth_request *request, + enum auth_client_result result, + const void *auth_reply, + size_t reply_size); + +static void +auth_request_handler_default_reply_continue(struct auth_request *request, + const void *reply, + size_t reply_size); + + struct auth_request_handler * auth_request_handler_create(bool token_auth, auth_client_request_callback_t *callback, struct auth_client_connection *conn, @@ -60,6 +57,12 @@ auth_request_handler_create(bool token_a handler->conn = conn; handler->master_callback = master_callback; handler->token_auth = token_auth; + handler->reply_callback = + auth_request_handler_default_reply_callback; + handler->reply_continue_callback = + auth_request_handler_default_reply_continue; + handler->verify_plain_continue_callback = + auth_request_default_verify_plain_continue; return handler; } @@ -342,6 +345,16 @@ void auth_request_handler_reply(struct a enum auth_client_result result, const void *auth_reply, size_t reply_size) { + struct auth_request_handler *handler = request->handler; + handler->reply_callback(request, result, auth_reply, reply_size); +} + +static void +auth_request_handler_default_reply_callback(struct auth_request *request, + enum auth_client_result result, + const void *auth_reply, + size_t reply_size) +{ struct auth_request_handler *handler = request->handler; string_t *str; int ret; @@ -394,6 +407,14 @@ void auth_request_handler_reply(struct a void auth_request_handler_reply_continue(struct auth_request *request, const void *reply, size_t reply_size) { + request->handler->reply_continue_callback(request, reply, reply_size); +} + +static void +auth_request_handler_default_reply_continue(struct auth_request *request, + const void *reply, + size_t reply_size) +{ auth_request_handler_reply(request, AUTH_CLIENT_RESULT_CONTINUE, reply, reply_size); } @@ -657,6 +678,7 @@ static void auth_str_append_userdb_extra auth_str_add_keyvalue(dest, "master_user", request->master_user); } + auth_str_add_keyvalue(dest, "auth_mech", request->mech->mech_name); if (*request->set->anonymous_username != '\0' && strcmp(request->user, request->set->anonymous_username) == 0) { /* this is an anonymous login, either via ANONYMOUS diff -up dovecot-2.2.36/src/auth/auth-request-handler.h.CVE_2020_12674prereq dovecot-2.2.36/src/auth/auth-request-handler.h --- dovecot-2.2.36/src/auth/auth-request-handler.h.CVE_2020_12674prereq 2017-06-23 13:18:28.000000000 +0200 +++ dovecot-2.2.36/src/auth/auth-request-handler.h 2020-08-09 14:37:53.482591165 +0200 @@ -17,6 +17,17 @@ auth_client_request_callback_t(const cha typedef void auth_master_request_callback_t(const char *reply, struct auth_master_connection *conn); +typedef void +auth_request_handler_reply_callback_t(struct auth_request *request, + enum auth_client_result result, + const void *auth_reply, + size_t reply_size); +typedef void +auth_request_handler_reply_continue_callback_t(struct auth_request *request, + const void *reply, + size_t reply_size); + + struct auth_request_handler * auth_request_handler_create(bool token_auth, auth_client_request_callback_t *callback, struct auth_client_connection *conn, diff -up dovecot-2.2.36/src/auth/auth-request-handler-private.h.CVE_2020_12674prereq dovecot-2.2.36/src/auth/auth-request-handler-private.h --- dovecot-2.2.36/src/auth/auth-request-handler-private.h.CVE_2020_12674prereq 2020-08-09 14:37:53.482591165 +0200 +++ dovecot-2.2.36/src/auth/auth-request-handler-private.h 2020-08-09 14:37:53.482591165 +0200 @@ -0,0 +1,27 @@ +#ifndef AUTH_REQUEST_HANDLER_PRIVATE_H +#define AUTH_REQUEST_HANDLER_PRIVATE_H + +struct auth_request; +struct auth_client_connection; + +struct auth_request_handler { + int refcount; + pool_t pool; + HASH_TABLE(void *, struct auth_request *) requests; + + unsigned int connect_uid, client_pid; + + auth_client_request_callback_t *callback; + struct auth_client_connection *conn; + + auth_master_request_callback_t *master_callback; + auth_request_handler_reply_callback_t *reply_callback; + auth_request_handler_reply_continue_callback_t *reply_continue_callback; + verify_plain_continue_callback_t *verify_plain_continue_callback; + + bool destroyed:1; + bool token_auth:1; +}; + + +#endif diff -up dovecot-2.2.36/src/auth/auth-request.h.CVE_2020_12674prereq dovecot-2.2.36/src/auth/auth-request.h --- dovecot-2.2.36/src/auth/auth-request.h.CVE_2020_12674prereq 2018-04-30 15:52:05.000000000 +0200 +++ dovecot-2.2.36/src/auth/auth-request.h 2020-08-09 14:37:53.482591165 +0200 @@ -269,6 +269,8 @@ void auth_request_set_credentials(struct set_credentials_callback_t *callback); void auth_request_userdb_callback(enum userdb_result result, struct auth_request *request); +void auth_request_default_verify_plain_continue(struct auth_request *request, + verify_plain_callback_t *callback); void auth_request_refresh_last_access(struct auth_request *request); void auth_str_append(string_t *dest, const char *key, const char *value); diff -up dovecot-2.2.36/src/auth/Makefile.am.CVE_2020_12674prereq dovecot-2.2.36/src/auth/Makefile.am --- dovecot-2.2.36/src/auth/Makefile.am.CVE_2020_12674prereq 2018-04-30 15:52:05.000000000 +0200 +++ dovecot-2.2.36/src/auth/Makefile.am 2020-08-09 14:37:53.482591165 +0200 @@ -227,7 +227,8 @@ test_programs = \ test-auth-cache \ test-auth-request-var-expand \ test-username-filter \ - test-db-dict + test-db-dict \ + test-mech noinst_PROGRAMS = $(test_programs) @@ -253,6 +254,13 @@ test_db_dict_SOURCES = test-db-dict.c test_db_dict_LDADD = $(test_libs) libauth.la test_db_dict_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) +test_mech_SOURCES = \ + test-mock.c \ + test-mech.c + +test_mech_LDADD = $(test_libs) $(auth_libs) $(AUTH_LIBS) +test_mech_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs) + check: check-am check-test check-test: all-am for bin in $(test_programs); do \ diff -up dovecot-2.2.36/src/auth/passdb.h.CVE_2020_12674prereq dovecot-2.2.36/src/auth/passdb.h --- dovecot-2.2.36/src/auth/passdb.h.CVE_2020_12674prereq 2018-04-30 15:52:05.000000000 +0200 +++ dovecot-2.2.36/src/auth/passdb.h 2020-08-09 14:37:53.482591165 +0200 @@ -24,6 +24,8 @@ enum passdb_result { typedef void verify_plain_callback_t(enum passdb_result result, struct auth_request *request); +typedef void verify_plain_continue_callback_t(struct auth_request *request, + verify_plain_callback_t *callback); typedef void lookup_credentials_callback_t(enum passdb_result result, const unsigned char *credentials, size_t size, diff -up dovecot-2.2.36/src/auth/test-auth.h.CVE_2020_12674prereq dovecot-2.2.36/src/auth/test-auth.h --- dovecot-2.2.36/src/auth/test-auth.h.CVE_2020_12674prereq 2020-08-09 14:38:24.950765769 +0200 +++ dovecot-2.2.36/src/auth/test-auth.h 2020-08-09 14:38:06.304662311 +0200 @@ -0,0 +1,21 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#ifndef TEST_AUTH_H +#define TEST_AUTH_H 1 + +#include "lib.h" +#include "test-common.h" + +struct auth_passdb; +struct auth_passdb_settings mock_passdb_set; + +void test_auth_request_var_expand(void); +void test_db_dict_parse_cache_key(void); +void test_username_filter(void); +void test_db_lua(void); +struct auth_passdb *passdb_mock(void); +void passdb_mock_mod_init(void); +void passdb_mock_mod_deinit(void); + +#endif + diff -up dovecot-2.2.36/src/auth/test-mock.c.CVE_2020_12674prereq dovecot-2.2.36/src/auth/test-mock.c --- dovecot-2.2.36/src/auth/test-mock.c.CVE_2020_12674prereq 2020-08-09 14:37:53.483591170 +0200 +++ dovecot-2.2.36/src/auth/test-mock.c 2020-08-09 14:37:53.483591170 +0200 @@ -0,0 +1,109 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "test-auth.h" +#include "auth-common.h" +#include "passdb.h" + +struct auth_penalty *auth_penalty; +time_t process_start_time; +bool worker, worker_restart_request; +static struct passdb_module *mock_passdb_mod = NULL; +static pool_t mock_pool; + +void auth_module_load(const char *names ATTR_UNUSED) +{ +} +void auth_refresh_proctitle(void) { +} + +static void passdb_mock_init(struct passdb_module *module ATTR_UNUSED) +{ +} +static void passdb_mock_deinit(struct passdb_module *module ATTR_UNUSED) +{ +} +static void passdb_mock_verify_plain(struct auth_request *request, const char *password ATTR_UNUSED, + verify_plain_callback_t *callback) +{ + callback(PASSDB_RESULT_OK, request); +} + +static void passdb_mock_lookup_credentials(struct auth_request *request, + lookup_credentials_callback_t *callback) +{ + passdb_handle_credentials(PASSDB_RESULT_OK, "password", "PLAIN", + callback, request); +} + +static struct passdb_module_interface mock_interface = { + .name = "mock", + .init = passdb_mock_init, + .deinit = passdb_mock_deinit, + .verify_plain = passdb_mock_verify_plain, + .lookup_credentials = passdb_mock_lookup_credentials, +}; + +struct auth_passdb_settings mock_passdb_set = { + .name = "mock", + .driver = "mock", + .args = "", + .default_fields = "", + .override_fields = "", + .mechanisms = "", + .username_filter = "", + .skip = "never", + .result_success = "return-ok", + .result_failure = "continue", + .result_internalfail = "continue", + .deny = FALSE, + .pass = FALSE, + .master = FALSE, + .auth_verbose = "default" +}; + +void passdb_mock_mod_init(void) +{ + if (mock_passdb_mod != NULL) + return; + + mock_pool = pool_allocfree_create("auth mock"); + + passdb_register_module(&mock_interface); + + struct auth_passdb_settings set = { + .name = "mock", + .driver = "mock", + .args = "", + .default_fields = "", + .override_fields = "", + .mechanisms = "", + .username_filter = "", + + .skip = "never", + .result_success = "return-ok", + .result_failure = "continue", + .result_internalfail = "continue", + + .deny = FALSE, + .pass = FALSE, + .master = FALSE, + .auth_verbose = "default" + }; + mock_passdb_mod = passdb_preinit(mock_pool, &set); + passdb_init(mock_passdb_mod); +} + +void passdb_mock_mod_deinit(void) +{ + passdb_deinit(mock_passdb_mod); + passdb_unregister_module(&mock_interface); + pool_unref(&mock_pool); +} + +struct auth_passdb *passdb_mock(void) +{ + struct auth_passdb *ret = i_new(struct auth_passdb, 1); + ret->set = &mock_passdb_set; + ret->passdb = mock_passdb_mod; + return ret; +} diff -up dovecot-2.2.36/src/lib/lib.c.CVE_2020_12674prereq dovecot-2.2.36/src/lib/lib.c --- dovecot-2.2.36/src/lib/lib.c.CVE_2020_12674prereq 2018-04-30 15:52:05.000000000 +0200 +++ dovecot-2.2.36/src/lib/lib.c 2020-08-09 14:37:53.483591170 +0200 @@ -175,7 +175,7 @@ void lib_init(void) hostpid_init(); lib_open_non_stdio_dev_null(); var_expand_extensions_init(); - + random_init(); lib_initialized = TRUE; } diff -up dovecot-2.2.36/src/lib/lib.h.CVE_2020_12674prereq dovecot-2.2.36/src/lib/lib.h --- dovecot-2.2.36/src/lib/lib.h.CVE_2020_12674prereq 2020-08-24 14:42:44.084069002 +0200 +++ dovecot-2.2.36/src/lib/lib.h 2020-08-24 14:42:44.108068770 +0200 @@ -33,6 +33,7 @@ #include "imem.h" #include "byteorder.h" #include "rand.h" +#include "randgen.h" typedef struct buffer buffer_t; typedef struct buffer string_t; diff -up dovecot-2.2.36/src/lib/Makefile.am.CVE_2020_12674prereq dovecot-2.2.36/src/lib/Makefile.am --- dovecot-2.2.36/src/lib/Makefile.am.CVE_2020_12674prereq 2020-08-09 14:37:53.464591065 +0200 +++ dovecot-2.2.36/src/lib/Makefile.am 2020-08-09 14:37:53.483591170 +0200 @@ -98,6 +98,7 @@ liblib_la_SOURCES = \ md4.c \ md5.c \ mempool.c \ + mempool-allocfree.c \ mempool-alloconly.c \ mempool-datastack.c \ mempool-system.c \ diff -up dovecot-2.2.36/src/lib/mempool-allocfree.c.CVE_2020_12674prereq dovecot-2.2.36/src/lib/mempool-allocfree.c --- dovecot-2.2.36/src/lib/mempool-allocfree.c.CVE_2020_12674prereq 2020-08-09 14:37:53.483591170 +0200 +++ dovecot-2.2.36/src/lib/mempool-allocfree.c 2020-08-09 14:37:53.483591170 +0200 @@ -0,0 +1,328 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +/* @UNSAFE: whole file */ +#include "lib.h" +#include "safe-memset.h" +#include "mempool.h" +#include "llist.h" + +/* + * As the name implies, allocfree pools support both allocating and freeing + * memory. + * + * Implementation + * ============== + * + * Each allocfree pool contains a pool structure (struct allocfree_pool) to + * keep track of allocfree-specific pool information and zero or more blocks + * (struct pool_block) that keep track of ranges of memory used to back the + * allocations. The blocks are kept in a doubly-linked list used to keep + * track of all allocations that belong to the pool. + * + * +-----------+ + * | allocfree | + * | pool | + * +-----+-----+ + * | + * | blocks +------------+ next +------------+ next + * \------->| pool block |<=====>| pool block |<=====>...<====> NULL + * +------------+ prev +------------+ prev + * | | | | + * . . + * . . + * . | | + * . +------------+ + * | | + * +------------+ + * + * Creation + * -------- + * + * When an allocfree pool is created the linked list of allocated blocks is + * initialized to be empty. + * + * Allocation & Freeing + * -------------------- + * + * Since each allocation (via p_malloc()) corresponds to one block, + * allocations are simply a matter of: + * + * - allocating enough memory from the system heap (via calloc()) to hold + * the block header and the requested number of bytes, + * - making a note of the user-requested size in the block header, + * - adding the new block to the pool's linked list of blocks, and + * - returning a pointer to the payload area of the block to the caller. + * + * Freeing memory is simpler. The passed in pointer is converted to a + * struct pool_block pointer. Then the block is removed from the pool's + * linked list and free()d. + * + * If the pool was created via pool_allocfree_create_clean(), all blocks are + * safe_memset() to zero just before being free()d. + * + * Reallocation + * ------------ + * + * Reallocation is done by calling realloc() with a new size that is large + * enough to cover the requested number of bytes plus the block header + * overhead. + * + * Clearing + * -------- + * + * Clearing the pool is supposed to return the pool to the same state it was + * in when it was first created. To that end, the allocfree pool frees all + * the blocks allocated since the pool's creation. In other words, clearing + * is equivalent to (but faster than) calling p_free() for each allocation + * in the pool. + * + * Finally, if the pool was created via pool_allocfree_create_clean(), all + * blocks are safe_memset() to zero before being free()d. + * + * Destruction + * ----------- + * + * Destroying a pool first clears it (see above) and then the pool structure + * itself is safe_memset() to zero (if pool_allocfree_create_clean() was + * used) and free()d. (The clearing leaves the pool in a minimal state + * with no blocks allocated.) + */ + +struct allocfree_pool { + struct pool pool; + int refcount; + size_t total_alloc_count; + size_t total_alloc_used; + + struct pool_block *blocks; +#ifdef DEBUG + char *name; +#endif + bool clean_frees; +}; + +struct pool_block { + struct pool_block *prev,*next; + + size_t size; + unsigned char *block; +}; + +#define SIZEOF_ALLOCFREE_POOL MEM_ALIGN(sizeof(struct allocfree_pool)) +#define SIZEOF_POOLBLOCK (MEM_ALIGN(sizeof(struct pool_block))) + +static const char *pool_allocfree_get_name(pool_t pool); +static void pool_allocfree_ref(pool_t pool); +static void pool_allocfree_unref(pool_t *pool); +static void *pool_allocfree_malloc(pool_t pool, size_t size); +static void pool_allocfree_free(pool_t pool, void *mem); +static void *pool_allocfree_realloc(pool_t pool, void *mem, + size_t old_size, size_t new_size); +static void pool_allocfree_clear(pool_t pool); +static size_t pool_allocfree_get_max_easy_alloc_size(pool_t pool); + +static const struct pool_vfuncs static_allocfree_pool_vfuncs = { + pool_allocfree_get_name, + + pool_allocfree_ref, + pool_allocfree_unref, + + pool_allocfree_malloc, + pool_allocfree_free, + + pool_allocfree_realloc, + + pool_allocfree_clear, + pool_allocfree_get_max_easy_alloc_size +}; + +static const struct pool static_allocfree_pool = { + .v = &static_allocfree_pool_vfuncs, + + .alloconly_pool = FALSE, + .datastack_pool = FALSE +}; + +pool_t pool_allocfree_create(const char *name ATTR_UNUSED) +{ + struct allocfree_pool *pool; + + if (SIZEOF_POOLBLOCK > (SSIZE_T_MAX - POOL_MAX_ALLOC_SIZE)) + i_panic("POOL_MAX_ALLOC_SIZE is too large"); + + pool = calloc(1, SIZEOF_ALLOCFREE_POOL); + if (pool == NULL) + i_fatal_status(FATAL_OUTOFMEM, "calloc(1, %"PRIuSIZE_T"): Out of memory", + SIZEOF_ALLOCFREE_POOL); +#ifdef DEBUG + pool->name = strdup(name); +#endif + pool->pool = static_allocfree_pool; + pool->refcount = 1; + return &pool->pool; +} + +pool_t pool_allocfree_create_clean(const char *name) +{ + struct allocfree_pool *apool; + pool_t pool; + + pool = pool_allocfree_create(name); + apool = (struct allocfree_pool *)pool; + apool->clean_frees = TRUE; + return pool; +} + +static void pool_allocfree_destroy(struct allocfree_pool *apool) +{ + pool_allocfree_clear(&apool->pool); + if (apool->clean_frees) + safe_memset(apool, 0, SIZEOF_ALLOCFREE_POOL); +#ifdef DEBUG + free(apool->name); +#endif + free(apool); +} + +static const char *pool_allocfree_get_name(pool_t pool ATTR_UNUSED) +{ +#ifdef DEBUG + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + return apool->name; +#else + return "alloc"; +#endif +} + +static void pool_allocfree_ref(pool_t pool) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + i_assert(apool->refcount > 0); + + apool->refcount++; +} + +static void pool_allocfree_unref(pool_t *_pool) +{ + pool_t pool = *_pool; + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + i_assert(apool->refcount > 0); + + /* erase the pointer before freeing anything, as the pointer may + exist inside the pool's memory area */ + *_pool = NULL; + + if (--apool->refcount > 0) + return; + + pool_allocfree_destroy(apool); +} + +static void *pool_block_attach(struct allocfree_pool *apool, struct pool_block *block) +{ + i_assert(block->size > 0); + DLLIST_PREPEND(&apool->blocks, block); + block->block = PTR_OFFSET(block,SIZEOF_POOLBLOCK); + apool->total_alloc_used += block->size; + apool->total_alloc_count++; + return block->block; +} + +static struct pool_block * +pool_block_detach(struct allocfree_pool *apool, unsigned char *mem) +{ + struct pool_block *block = PTR_OFFSET(mem, -SIZEOF_POOLBLOCK); + + /* make sure the block we are dealing with is correct */ + i_assert(block->block == mem); + i_assert((block->prev == NULL || block->prev->next == block) && + (block->next == NULL || block->next->prev == block)); + + i_assert(apool->total_alloc_used >= block->size); + i_assert(apool->total_alloc_count > 0); + DLLIST_REMOVE(&apool->blocks, block); + apool->total_alloc_used -= block->size; + apool->total_alloc_count--; + + return block; +} + +static void *pool_allocfree_malloc(pool_t pool, size_t size) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + + struct pool_block *block = calloc(1, SIZEOF_POOLBLOCK + size); + if (block == NULL) + i_fatal_status(FATAL_OUTOFMEM, "calloc(1, %"PRIuSIZE_T"): Out of memory", + SIZEOF_POOLBLOCK + size); + block->size = size; + return pool_block_attach(apool, block); +} + +static void pool_allocfree_free(pool_t pool, void *mem) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + struct pool_block *block = pool_block_detach(apool, mem); + if (apool->clean_frees) + safe_memset(block, 0, SIZEOF_POOLBLOCK+block->size); + free(block); +} + +static void *pool_allocfree_realloc(pool_t pool, void *mem, + size_t old_size, size_t new_size) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + unsigned char *new_mem; + + struct pool_block *block = pool_block_detach(apool, mem); + if ((new_mem = realloc(block, SIZEOF_POOLBLOCK+new_size)) == NULL) + i_fatal_status(FATAL_OUTOFMEM, "realloc(block, %"PRIuSIZE_T")", + SIZEOF_POOLBLOCK+new_size); + + /* zero out new memory */ + if (new_size > old_size) + memset(new_mem + SIZEOF_POOLBLOCK + old_size, 0, + new_size - old_size); + block = (struct pool_block*)new_mem; + block->size = new_size; + return pool_block_attach(apool, block); +} + +static void pool_allocfree_clear(pool_t pool) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + struct pool_block *block, *next; + + for (block = apool->blocks; block != NULL; block = next) { + next = block->next; + pool_allocfree_free(pool, block->block); + } + i_assert(apool->total_alloc_used == 0 && apool->total_alloc_count == 0); +} + +static size_t pool_allocfree_get_max_easy_alloc_size(pool_t pool ATTR_UNUSED) +{ + return 0; +} + +size_t pool_allocfree_get_total_used_size(pool_t pool) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + return apool->total_alloc_used; +} + +size_t pool_allocfree_get_total_alloc_size(pool_t pool) +{ + struct allocfree_pool *apool = + container_of(pool, struct allocfree_pool, pool); + return apool->total_alloc_used + + SIZEOF_POOLBLOCK*apool->total_alloc_count + sizeof(*apool); +} diff -up dovecot-2.2.36/src/lib/mempool.h.CVE_2020_12674prereq dovecot-2.2.36/src/lib/mempool.h --- dovecot-2.2.36/src/lib/mempool.h.CVE_2020_12674prereq 2018-04-30 15:52:05.000000000 +0200 +++ dovecot-2.2.36/src/lib/mempool.h 2020-08-09 14:37:53.483591170 +0200 @@ -10,6 +10,11 @@ pools to disable the warning. */ #define MEMPOOL_GROWING "GROWING-" +/* The maximum allocation size that's allowed. Anything larger than that + will panic. No pool ever should need more than 4kB of overhead per + allocation. */ +#define POOL_MAX_ALLOC_SIZE (SSIZE_T_MAX - 4096) + /* Memory allocated and reallocated (the new data in it) in pools is always zeroed, it will cost only a few CPU cycles and may well save some debug time. */ @@ -63,6 +68,10 @@ pool_t pool_alloconly_create(const char needed. */ pool_t pool_alloconly_create_clean(const char *name, size_t size); +/* Create new alloc pool. This is very similar to system pool, but it + will deallocate all memory on deinit. */ +pool_t pool_allocfree_create(const char *name); + /* When allocating memory from returned pool, the data stack frame must be the same as it was when calling this function. pool_unref() also checks that the stack frame is the same. This should make it quite safe to use. */