[Dovecot] Fixes CVE-2017-15132 - take 2
This commit is contained in:
		| @@ -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 \ | ||||
|   | ||||
							
								
								
									
										254
									
								
								data/Dockerfiles/dovecot/auth-client-request.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								data/Dockerfiles/dovecot/auth-client-request.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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"); | ||||
| } | ||||
							
								
								
									
										489
									
								
								data/Dockerfiles/dovecot/auth-server-connection.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										489
									
								
								data/Dockerfiles/dovecot/auth-server-connection.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <unistd.h> | ||||
|  | ||||
| #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)); | ||||
| } | ||||
							
								
								
									
										43
									
								
								data/Dockerfiles/dovecot/auth-server-connection.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								data/Dockerfiles/dovecot/auth-server-connection.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user