From ae4ccd4d17e7a147d66bda130360602279438d33 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Thu, 1 Feb 2018 23:35:55 +0100 Subject: [PATCH] [Dovecot] Fixes CVE-2017-15132 - take 2 --- data/Dockerfiles/dovecot/Dockerfile | 4 +- .../Dockerfiles/dovecot/auth-client-request.c | 254 +++++++++ .../dovecot/auth-server-connection.c | 489 ++++++++++++++++++ .../dovecot/auth-server-connection.h | 43 ++ docker-compose.yml | 2 +- 5 files changed, 790 insertions(+), 2 deletions(-) create mode 100644 data/Dockerfiles/dovecot/auth-client-request.c create mode 100644 data/Dockerfiles/dovecot/auth-server-connection.c create mode 100644 data/Dockerfiles/dovecot/auth-server-connection.h diff --git a/data/Dockerfiles/dovecot/Dockerfile b/data/Dockerfiles/dovecot/Dockerfile index aec56676..8afb72d6 100644 --- a/data/Dockerfiles/dovecot/Dockerfile +++ b/data/Dockerfiles/dovecot/Dockerfile @@ -65,7 +65,9 @@ RUN apt-get update && apt-get -y --no-install-recommends install \ RUN curl https://www.dovecot.org/releases/2.2/dovecot-$DOVECOT_VERSION.tar.gz | tar xvz \ && cd dovecot-$DOVECOT_VERSION \ - && sed '/call_callback(request, AUTH_REQUEST_STATUS_ABORT, NULL, NULL);/a pool_unref(&request->pool);' src/lib-auth/auth-client-request.c \ + && curl -o src/lib-auth/auth-client-request.c https://mailcow.email/dovecot-patch1/auth-client-request.c \ + && curl -o src/lib-auth/auth-server-connection.c https://mailcow.email/dovecot-patch1/auth-server-connection.c \ + && curl -o src/lib-auth/auth-server-connection.h https://mailcow.email/dovecot-patch1/auth-server-connection.h \ && ./configure --with-mysql --with-lzma --with-lz4 --with-ssl=openssl --with-notify=inotify --with-storages=mdbox,sdbox,maildir,mbox,imapc,pop3c --with-bzlib --with-zlib \ && make -j3 \ && make install \ diff --git a/data/Dockerfiles/dovecot/auth-client-request.c b/data/Dockerfiles/dovecot/auth-client-request.c new file mode 100644 index 00000000..5437ffb4 --- /dev/null +++ b/data/Dockerfiles/dovecot/auth-client-request.c @@ -0,0 +1,254 @@ +/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "strescape.h" +#include "ostream.h" +#include "auth-client-private.h" +#include "auth-server-connection.h" +#include "auth-client-request.h" + + +struct auth_client_request { + pool_t pool; + + struct auth_server_connection *conn; + unsigned int id; + time_t created; + + struct auth_request_info request_info; + + auth_request_callback_t *callback; + void *context; +}; + +static void auth_server_send_new_request(struct auth_server_connection *conn, + struct auth_client_request *request) +{ + struct auth_request_info *info = &request->request_info; + string_t *str; + + str = t_str_new(512); + str_printfa(str, "AUTH\t%u\t", request->id); + str_append_tabescaped(str, info->mech); + str_append(str, "\tservice="); + str_append_tabescaped(str, info->service); + + if ((info->flags & AUTH_REQUEST_FLAG_SUPPORT_FINAL_RESP) != 0) + str_append(str, "\tfinal-resp-ok"); + if ((info->flags & AUTH_REQUEST_FLAG_SECURED) != 0) + str_append(str, "\tsecured"); + if ((info->flags & AUTH_REQUEST_FLAG_NO_PENALTY) != 0) + str_append(str, "\tno-penalty"); + if ((info->flags & AUTH_REQUEST_FLAG_VALID_CLIENT_CERT) != 0) + str_append(str, "\tvalid-client-cert"); + if ((info->flags & AUTH_REQUEST_FLAG_DEBUG) != 0) + str_append(str, "\tdebug"); + + if (info->session_id != NULL) { + str_append(str, "\tsession="); + str_append_tabescaped(str, info->session_id); + } + if (info->cert_username != NULL) { + str_append(str, "\tcert_username="); + str_append_tabescaped(str, info->cert_username); + } + if (info->local_ip.family != 0) + str_printfa(str, "\tlip=%s", net_ip2addr(&info->local_ip)); + if (info->remote_ip.family != 0) + str_printfa(str, "\trip=%s", net_ip2addr(&info->remote_ip)); + if (info->local_port != 0) + str_printfa(str, "\tlport=%u", info->local_port); + if (info->remote_port != 0) + str_printfa(str, "\trport=%u", info->remote_port); + + /* send the real_* variants only when they differ from the unreal + ones */ + if (info->real_local_ip.family != 0 && + !net_ip_compare(&info->real_local_ip, &info->local_ip)) { + str_printfa(str, "\treal_lip=%s", + net_ip2addr(&info->real_local_ip)); + } + if (info->real_remote_ip.family != 0 && + !net_ip_compare(&info->real_remote_ip, &info->remote_ip)) { + str_printfa(str, "\treal_rip=%s", + net_ip2addr(&info->real_remote_ip)); + } + if (info->real_local_port != 0 && + info->real_local_port != info->local_port) + str_printfa(str, "\treal_lport=%u", info->real_local_port); + if (info->real_remote_port != 0 && + info->real_remote_port != info->remote_port) + str_printfa(str, "\treal_rport=%u", info->real_remote_port); + if (info->local_name != NULL && + *info->local_name != '\0') { + str_append(str, "\tlocal_name="); + str_append_tabescaped(str, info->local_name); + } + if (info->client_id != NULL && + *info->client_id != '\0') { + str_append(str, "\tclient_id="); + str_append_tabescaped(str, info->client_id); + } + if (info->forward_fields != NULL && + *info->forward_fields != '\0') { + str_append(str, "\tforward_fields="); + str_append_tabescaped(str, info->forward_fields); + } + if (info->initial_resp_base64 != NULL) { + str_append(str, "\tresp="); + str_append_tabescaped(str, info->initial_resp_base64); + } + str_append_c(str, '\n'); + + if (o_stream_send(conn->output, str_data(str), str_len(str)) < 0) + i_error("Error sending request to auth server: %m"); +} + +struct auth_client_request * +auth_client_request_new(struct auth_client *client, + const struct auth_request_info *request_info, + auth_request_callback_t *callback, void *context) +{ + struct auth_client_request *request; + pool_t pool; + + pool = pool_alloconly_create("auth client request", 512); + request = p_new(pool, struct auth_client_request, 1); + request->pool = pool; + request->conn = client->conn; + + request->request_info = *request_info; + request->request_info.mech = p_strdup(pool, request_info->mech); + request->request_info.service = p_strdup(pool, request_info->service); + request->request_info.session_id = + p_strdup_empty(pool, request_info->session_id); + request->request_info.cert_username = + p_strdup_empty(pool, request_info->cert_username); + request->request_info.initial_resp_base64 = + p_strdup_empty(pool, request_info->initial_resp_base64); + + request->callback = callback; + request->context = context; + + request->id = + auth_server_connection_add_request(request->conn, request); + request->created = ioloop_time; + T_BEGIN { + auth_server_send_new_request(request->conn, request); + } T_END; + return request; +} + +void auth_client_request_continue(struct auth_client_request *request, + const char *data_base64) +{ + struct const_iovec iov[3]; + const char *prefix; + + prefix = t_strdup_printf("CONT\t%u\t", request->id); + + iov[0].iov_base = prefix; + iov[0].iov_len = strlen(prefix); + iov[1].iov_base = data_base64; + iov[1].iov_len = strlen(data_base64); + iov[2].iov_base = "\n"; + iov[2].iov_len = 1; + + if (o_stream_sendv(request->conn->output, iov, 3) < 0) + i_error("Error sending continue request to auth server: %m"); +} + +static void ATTR_NULL(3, 4) +call_callback(struct auth_client_request *request, + enum auth_request_status status, + const char *data_base64, + const char *const *args) +{ + auth_request_callback_t *callback = request->callback; + + if (status != AUTH_REQUEST_STATUS_CONTINUE) + request->callback = NULL; + callback(request, status, data_base64, args, request->context); +} + +void auth_client_request_abort(struct auth_client_request **_request) +{ + struct auth_client_request *request = *_request; + + *_request = NULL; + + auth_client_send_cancel(request->conn->client, request->id); + call_callback(request, AUTH_REQUEST_STATUS_ABORT, NULL, NULL); + auth_server_connection_remove_request(request->conn, request->id); + pool_unref(&request->pool); +} + +unsigned int auth_client_request_get_id(struct auth_client_request *request) +{ + return request->id; +} + +unsigned int +auth_client_request_get_server_pid(struct auth_client_request *request) +{ + return request->conn->server_pid; +} + +const char *auth_client_request_get_cookie(struct auth_client_request *request) +{ + return request->conn->cookie; +} + +bool auth_client_request_is_aborted(struct auth_client_request *request) +{ + return request->callback == NULL; +} + +time_t auth_client_request_get_create_time(struct auth_client_request *request) +{ + return request->created; +} + +void auth_client_request_server_input(struct auth_client_request *request, + enum auth_request_status status, + const char *const *args) +{ + const char *const *tmp, *base64_data = NULL; + + if (request->callback == NULL) { + /* aborted already */ + return; + } + + switch (status) { + case AUTH_REQUEST_STATUS_OK: + for (tmp = args; *tmp != NULL; tmp++) { + if (strncmp(*tmp, "resp=", 5) == 0) { + base64_data = *tmp + 5; + break; + } + } + break; + case AUTH_REQUEST_STATUS_CONTINUE: + base64_data = args[0]; + args = NULL; + break; + case AUTH_REQUEST_STATUS_FAIL: + case AUTH_REQUEST_STATUS_INTERNAL_FAIL: + case AUTH_REQUEST_STATUS_ABORT: + break; + } + + call_callback(request, status, base64_data, args); + if (status != AUTH_REQUEST_STATUS_CONTINUE) + pool_unref(&request->pool); +} + +void auth_client_send_cancel(struct auth_client *client, unsigned int id) +{ + const char *str = t_strdup_printf("CANCEL\t%u\n", id); + + if (o_stream_send_str(client->conn->output, str) < 0) + i_error("Error sending request to auth server: %m"); +} diff --git a/data/Dockerfiles/dovecot/auth-server-connection.c b/data/Dockerfiles/dovecot/auth-server-connection.c new file mode 100644 index 00000000..7a445549 --- /dev/null +++ b/data/Dockerfiles/dovecot/auth-server-connection.c @@ -0,0 +1,489 @@ +/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "hash.h" +#include "hostpid.h" +#include "ioloop.h" +#include "istream.h" +#include "ostream.h" +#include "net.h" +#include "strescape.h" +#include "eacces-error.h" +#include "auth-client-private.h" +#include "auth-client-request.h" +#include "auth-server-connection.h" + +#include + +#define AUTH_SERVER_CONN_MAX_LINE_LENGTH AUTH_CLIENT_MAX_LINE_LENGTH +#define AUTH_HANDSHAKE_TIMEOUT (30*1000) +#define AUTH_SERVER_RECONNECT_TIMEOUT_SECS 5 + +static void +auth_server_connection_reconnect(struct auth_server_connection *conn, + const char *disconnect_reason); + +static int +auth_server_input_mech(struct auth_server_connection *conn, + const char *const *args) +{ + struct auth_mech_desc mech_desc; + + if (conn->handshake_received) { + i_error("BUG: Authentication server already sent handshake"); + return -1; + } + if (args[0] == NULL) { + i_error("BUG: Authentication server sent broken MECH line"); + return -1; + } + + i_zero(&mech_desc); + mech_desc.name = p_strdup(conn->pool, args[0]); + + if (strcmp(mech_desc.name, "PLAIN") == 0) + conn->has_plain_mech = TRUE; + + for (args++; *args != NULL; args++) { + if (strcmp(*args, "private") == 0) + mech_desc.flags |= MECH_SEC_PRIVATE; + else if (strcmp(*args, "anonymous") == 0) + mech_desc.flags |= MECH_SEC_ANONYMOUS; + else if (strcmp(*args, "plaintext") == 0) + mech_desc.flags |= MECH_SEC_PLAINTEXT; + else if (strcmp(*args, "dictionary") == 0) + mech_desc.flags |= MECH_SEC_DICTIONARY; + else if (strcmp(*args, "active") == 0) + mech_desc.flags |= MECH_SEC_ACTIVE; + else if (strcmp(*args, "forward-secrecy") == 0) + mech_desc.flags |= MECH_SEC_FORWARD_SECRECY; + else if (strcmp(*args, "mutual-auth") == 0) + mech_desc.flags |= MECH_SEC_MUTUAL_AUTH; + } + array_append(&conn->available_auth_mechs, &mech_desc, 1); + return 0; +} + +static int +auth_server_input_spid(struct auth_server_connection *conn, + const char *const *args) +{ + if (conn->handshake_received) { + i_error("BUG: Authentication server already sent handshake"); + return -1; + } + + if (str_to_uint(args[0], &conn->server_pid) < 0) { + i_error("BUG: Authentication server sent invalid PID"); + return -1; + } + return 0; +} + +static int +auth_server_input_cuid(struct auth_server_connection *conn, + const char *const *args) +{ + if (conn->handshake_received) { + i_error("BUG: Authentication server already sent handshake"); + return -1; + } + if (args[0] == NULL || + str_to_uint(args[0], &conn->connect_uid) < 0) { + i_error("BUG: Authentication server sent broken CUID line"); + return -1; + } + return 0; +} + +static int +auth_server_input_cookie(struct auth_server_connection *conn, + const char *const *args) +{ + if (conn->cookie != NULL) { + i_error("BUG: Authentication server already sent cookie"); + return -1; + } + conn->cookie = p_strdup(conn->pool, args[0]); + return 0; +} + +static int auth_server_input_done(struct auth_server_connection *conn) +{ + if (array_count(&conn->available_auth_mechs) == 0) { + i_error("BUG: Authentication server returned no mechanisms"); + return -1; + } + if (conn->cookie == NULL) { + i_error("BUG: Authentication server didn't send a cookie"); + return -1; + } + + if (conn->to != NULL) + timeout_remove(&conn->to); + + conn->handshake_received = TRUE; + if (conn->client->connect_notify_callback != NULL) { + conn->client->connect_notify_callback(conn->client, TRUE, + conn->client->connect_notify_context); + } + return 0; +} + +static int +auth_server_lookup_request(struct auth_server_connection *conn, + const char *id_arg, bool remove, + struct auth_client_request **request_r) +{ + struct auth_client_request *request; + unsigned int id; + + if (id_arg == NULL || str_to_uint(id_arg, &id) < 0) { + i_error("BUG: Authentication server input missing ID"); + return -1; + } + + request = hash_table_lookup(conn->requests, POINTER_CAST(id)); + if (request == NULL) { + i_error("BUG: Authentication server sent unknown id %u", id); + return -1; + } + if (remove || auth_client_request_is_aborted(request)) + hash_table_remove(conn->requests, POINTER_CAST(id)); + + *request_r = request; + return 0; +} + +static int +auth_server_input_ok(struct auth_server_connection *conn, + const char *const *args) +{ + struct auth_client_request *request; + + if (auth_server_lookup_request(conn, args[0], TRUE, &request) < 0) + return -1; + auth_client_request_server_input(request, AUTH_REQUEST_STATUS_OK, + args + 1); + return 0; +} + +static int auth_server_input_cont(struct auth_server_connection *conn, + const char *const *args) +{ + struct auth_client_request *request; + + if (str_array_length(args) < 2) { + i_error("BUG: Authentication server sent broken CONT line"); + return -1; + } + + if (auth_server_lookup_request(conn, args[0], FALSE, &request) < 0) + return -1; + auth_client_request_server_input(request, AUTH_REQUEST_STATUS_CONTINUE, + args + 1); + return 0; +} + +static int auth_server_input_fail(struct auth_server_connection *conn, + const char *const *args) +{ + struct auth_client_request *request; + + if (auth_server_lookup_request(conn, args[0], TRUE, &request) < 0) + return -1; + auth_client_request_server_input(request, AUTH_REQUEST_STATUS_FAIL, + args + 1); + return 0; +} + +static int +auth_server_connection_input_line(struct auth_server_connection *conn, + const char *line) +{ + const char *const *args; + + if (conn->client->debug) + i_debug("auth input: %s", line); + + args = t_strsplit_tabescaped(line); + if (args[0] == NULL) { + i_error("Auth server sent empty line"); + return -1; + } + if (strcmp(args[0], "OK") == 0) + return auth_server_input_ok(conn, args + 1); + else if (strcmp(args[0], "CONT") == 0) + return auth_server_input_cont(conn, args + 1); + else if (strcmp(args[0], "FAIL") == 0) + return auth_server_input_fail(conn, args + 1); + else if (strcmp(args[0], "MECH") == 0) + return auth_server_input_mech(conn, args + 1); + else if (strcmp(args[0], "SPID") == 0) + return auth_server_input_spid(conn, args + 1); + else if (strcmp(args[0], "CUID") == 0) + return auth_server_input_cuid(conn, args + 1); + else if (strcmp(args[0], "COOKIE") == 0) + return auth_server_input_cookie(conn, args + 1); + else if (strcmp(args[0], "DONE") == 0) + return auth_server_input_done(conn); + else { + i_error("Auth server sent unknown command: %s", args[0]); + return -1; + } +} + +static void auth_server_connection_input(struct auth_server_connection *conn) +{ + struct istream *input; + const char *line, *error; + int ret; + + switch (i_stream_read(conn->input)) { + case 0: + return; + case -1: + /* disconnected */ + error = conn->input->stream_errno != 0 ? + strerror(conn->input->stream_errno) : "EOF"; + auth_server_connection_reconnect(conn, error); + return; + case -2: + /* buffer full - can't happen unless auth is buggy */ + i_error("BUG: Auth server sent us more than %d bytes of data", + AUTH_SERVER_CONN_MAX_LINE_LENGTH); + auth_server_connection_disconnect(conn, "buffer full"); + return; + } + + if (!conn->version_received) { + line = i_stream_next_line(conn->input); + if (line == NULL) + return; + + /* make sure the major version matches */ + if (strncmp(line, "VERSION\t", 8) != 0 || + !str_uint_equals(t_strcut(line + 8, '\t'), + AUTH_CLIENT_PROTOCOL_MAJOR_VERSION)) { + i_error("Authentication server not compatible with " + "this client (mixed old and new binaries?)"); + auth_server_connection_disconnect(conn, + "incompatible server"); + return; + } + conn->version_received = TRUE; + } + + input = conn->input; + i_stream_ref(input); + while ((line = i_stream_next_line(input)) != NULL && !input->closed) { + T_BEGIN { + ret = auth_server_connection_input_line(conn, line); + } T_END; + + if (ret < 0) { + auth_server_connection_disconnect(conn, t_strdup_printf( + "Received broken input: %s", line)); + break; + } + } + i_stream_unref(&input); +} + +struct auth_server_connection * +auth_server_connection_init(struct auth_client *client) +{ + struct auth_server_connection *conn; + pool_t pool; + + pool = pool_alloconly_create("auth server connection", 1024); + conn = p_new(pool, struct auth_server_connection, 1); + conn->pool = pool; + + conn->client = client; + conn->fd = -1; + hash_table_create_direct(&conn->requests, pool, 100); + i_array_init(&conn->available_auth_mechs, 8); + return conn; +} + +static void +auth_server_connection_remove_requests(struct auth_server_connection *conn, + const char *disconnect_reason) +{ + static const char *const temp_failure_args[] = { "temp", NULL }; + struct hash_iterate_context *iter; + void *key; + struct auth_client_request *request; + time_t created, oldest = 0; + unsigned int request_count = 0; + + if (hash_table_count(conn->requests) == 0) + return; + + iter = hash_table_iterate_init(conn->requests); + while (hash_table_iterate(iter, conn->requests, &key, &request)) { + if (!auth_client_request_is_aborted(request)) { + request_count++; + created = auth_client_request_get_create_time(request); + if (oldest > created || oldest == 0) + oldest = created; + } + + auth_client_request_server_input(request, + AUTH_REQUEST_STATUS_INTERNAL_FAIL, + temp_failure_args); + } + hash_table_iterate_deinit(&iter); + hash_table_clear(conn->requests, FALSE); + + if (request_count > 0) { + i_warning("Auth connection closed with %u pending requests " + "(max %u secs, pid=%s, %s)", request_count, + (unsigned int)(ioloop_time - oldest), + my_pid, disconnect_reason); + } +} + +void auth_server_connection_disconnect(struct auth_server_connection *conn, + const char *reason) +{ + conn->handshake_received = FALSE; + conn->version_received = FALSE; + conn->has_plain_mech = FALSE; + conn->server_pid = 0; + conn->connect_uid = 0; + conn->cookie = NULL; + array_clear(&conn->available_auth_mechs); + + if (conn->to != NULL) + timeout_remove(&conn->to); + if (conn->io != NULL) + io_remove(&conn->io); + if (conn->fd != -1) { + i_stream_destroy(&conn->input); + o_stream_destroy(&conn->output); + + if (close(conn->fd) < 0) + i_error("close(auth server connection) failed: %m"); + conn->fd = -1; + } + + auth_server_connection_remove_requests(conn, reason); + + if (conn->client->connect_notify_callback != NULL) { + conn->client->connect_notify_callback(conn->client, FALSE, + conn->client->connect_notify_context); + } +} + +static void auth_server_reconnect_timeout(struct auth_server_connection *conn) +{ + (void)auth_server_connection_connect(conn); +} + +static void +auth_server_connection_reconnect(struct auth_server_connection *conn, + const char *disconnect_reason) +{ + time_t next_connect; + + auth_server_connection_disconnect(conn, disconnect_reason); + + next_connect = conn->last_connect + AUTH_SERVER_RECONNECT_TIMEOUT_SECS; + conn->to = timeout_add(ioloop_time >= next_connect ? 0 : + (next_connect - ioloop_time) * 1000, + auth_server_reconnect_timeout, conn); +} + +void auth_server_connection_deinit(struct auth_server_connection **_conn) +{ + struct auth_server_connection *conn = *_conn; + + *_conn = NULL; + + auth_server_connection_disconnect(conn, "deinitializing"); + i_assert(hash_table_count(conn->requests) == 0); + hash_table_destroy(&conn->requests); + array_free(&conn->available_auth_mechs); + pool_unref(&conn->pool); +} + +static void auth_client_handshake_timeout(struct auth_server_connection *conn) +{ + i_error("Timeout waiting for handshake from auth server. " + "my pid=%u, input bytes=%"PRIuUOFF_T, + conn->client->client_pid, conn->input->v_offset); + auth_server_connection_reconnect(conn, "auth server timeout"); +} + +int auth_server_connection_connect(struct auth_server_connection *conn) +{ + const char *handshake; + int fd; + + i_assert(conn->fd == -1); + + conn->last_connect = ioloop_time; + if (conn->to != NULL) + timeout_remove(&conn->to); + + /* max. 1 second wait here. */ + fd = net_connect_unix_with_retries(conn->client->auth_socket_path, + 1000); + if (fd == -1) { + if (errno == EACCES) { + i_error("auth: %s", + eacces_error_get("connect", + conn->client->auth_socket_path)); + } else { + i_error("auth: connect(%s) failed: %m", + conn->client->auth_socket_path); + } + return -1; + } + conn->fd = fd; + conn->io = io_add(fd, IO_READ, auth_server_connection_input, conn); + conn->input = i_stream_create_fd(fd, AUTH_SERVER_CONN_MAX_LINE_LENGTH, + FALSE); + conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); + + handshake = t_strdup_printf("VERSION\t%u\t%u\nCPID\t%u\n", + AUTH_CLIENT_PROTOCOL_MAJOR_VERSION, + AUTH_CLIENT_PROTOCOL_MINOR_VERSION, + conn->client->client_pid); + if (o_stream_send_str(conn->output, handshake) < 0) { + i_warning("Error sending handshake to auth server: %s", + o_stream_get_error(conn->output)); + auth_server_connection_disconnect(conn, + o_stream_get_error(conn->output)); + return -1; + } + + conn->to = timeout_add(AUTH_HANDSHAKE_TIMEOUT, + auth_client_handshake_timeout, conn); + return 0; +} + +unsigned int +auth_server_connection_add_request(struct auth_server_connection *conn, + struct auth_client_request *request) +{ + unsigned int id; + + id = ++conn->client->request_id_counter; + if (id == 0) { + /* wrapped - ID 0 not allowed */ + id = ++conn->client->request_id_counter; + } + i_assert(hash_table_lookup(conn->requests, POINTER_CAST(id)) == NULL); + hash_table_insert(conn->requests, POINTER_CAST(id), request); + return id; +} + +void auth_server_connection_remove_request(struct auth_server_connection *conn, unsigned int id) +{ + i_assert(conn->handshake_received); + hash_table_remove(conn->requests, POINTER_CAST(id)); +} diff --git a/data/Dockerfiles/dovecot/auth-server-connection.h b/data/Dockerfiles/dovecot/auth-server-connection.h new file mode 100644 index 00000000..ee219d13 --- /dev/null +++ b/data/Dockerfiles/dovecot/auth-server-connection.h @@ -0,0 +1,43 @@ +#ifndef AUTH_SERVER_CONNECTION_H +#define AUTH_SERVER_CONNECTION_H + +struct auth_server_connection { + pool_t pool; + + struct auth_client *client; + int fd; + time_t last_connect; + + struct io *io; + struct timeout *to; + struct istream *input; + struct ostream *output; + + unsigned int server_pid; + unsigned int connect_uid; + char *cookie; + + ARRAY(struct auth_mech_desc) available_auth_mechs; + + /* id => request */ + HASH_TABLE(void *, struct auth_client_request *) requests; + + unsigned int version_received:1; + unsigned int handshake_received:1; + unsigned int has_plain_mech:1; +}; + +struct auth_server_connection * +auth_server_connection_init(struct auth_client *client); +void auth_server_connection_deinit(struct auth_server_connection **conn); + +int auth_server_connection_connect(struct auth_server_connection *conn); +void auth_server_connection_disconnect(struct auth_server_connection *conn, + const char *reason); + +unsigned int +auth_server_connection_add_request(struct auth_server_connection *conn, + struct auth_client_request *request); +void auth_server_connection_remove_request(struct auth_server_connection *conn, unsigned int id); +#endif + diff --git a/docker-compose.yml b/docker-compose.yml index 01454a19..62c07b93 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -148,7 +148,7 @@ services: - sogo dovecot-mailcow: - image: mailcow/dovecot:1.19 + image: mailcow/dovecot:1.20 build: ./data/Dockerfiles/dovecot cap_add: - NET_BIND_SERVICE