You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
818 lines
27 KiB
818 lines
27 KiB
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 |
|
+ * | <data> | | <data> | |
|
+ * . . |
|
+ * . . |
|
+ * . | <data> | |
|
+ * . +------------+ |
|
+ * | <data> | |
|
+ * +------------+ |
|
+ * |
|
+ * 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. */
|
|
|