From: Timo Sirainen Date: Mon, 14 Jun 2021 17:59:09 +0000 (+0300) Subject: doveadm: Add support for proxy loop detection X-Git-Tag: 2.4.0~4790 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3044989cf9417aa669a39bfbd71964d64efc7f4a;p=thirdparty%2Fdovecot%2Fcore.git doveadm: Add support for proxy loop detection --- diff --git a/src/doveadm/doveadm-dsync.c b/src/doveadm/doveadm-dsync.c index 3614a383b8..7131b3c78a 100644 --- a/src/doveadm/doveadm-dsync.c +++ b/src/doveadm/doveadm-dsync.c @@ -883,7 +883,8 @@ static void dsync_server_run_command(struct dsync_cmd_context *ctx, str_append_c(cmd, '\n'); ctx->tcp_conn = conn; - server_connection_cmd(conn, str_c(cmd), NULL, + /* dsync command can't be proxied currently, so use TTL 1 */ + server_connection_cmd(conn, 1, str_c(cmd), NULL, dsync_connected_callback, ctx); io_loop_run(current_ioloop); ctx->tcp_conn = NULL; diff --git a/src/doveadm/doveadm-mail-server.c b/src/doveadm/doveadm-mail-server.c index 7aa8c11d72..4cec31d7d1 100644 --- a/src/doveadm/doveadm-mail-server.c +++ b/src/doveadm/doveadm-mail-server.c @@ -25,9 +25,18 @@ #define DOVEADM_MAIL_SERVER_FAILED() \ (internal_failure || master_service_is_killed(master_service)) +struct doveadm_proxy_redirect { + struct ip_addr ip; + in_port_t port; +}; + struct doveadm_mail_server_cmd { struct server_connection *conn; char *username; + + int proxy_ttl; + ARRAY(struct doveadm_proxy_redirect) redirect_path; + char *cmdline; struct istream *input; }; @@ -101,16 +110,41 @@ static void doveadm_mail_server_cmd_free(struct doveadm_mail_server_cmd **_cmd) return; i_stream_unref(&cmd->input); + array_free(&cmd->redirect_path); i_free(cmd->cmdline); i_free(cmd->username); i_free(cmd); } +static bool +doveadm_proxy_cmd_have_connected(struct doveadm_mail_server_cmd *servercmd, + const struct ip_addr *ip, in_port_t port) +{ + const struct doveadm_proxy_redirect *redirect; + struct ip_addr conn_ip; + in_port_t conn_port; + + server_connection_get_dest(servercmd->conn, &conn_ip, &conn_port); + i_assert(conn_ip.family != 0); + + if (net_ip_compare(&conn_ip, ip) && conn_port == port) + return TRUE; + if (!array_is_created(&servercmd->redirect_path)) + return FALSE; + + array_foreach(&servercmd->redirect_path, redirect) { + if (net_ip_compare(&redirect->ip, ip) && redirect->port == port) + return TRUE; + } + return FALSE; +} + static int doveadm_cmd_redirect(struct doveadm_mail_server_cmd *servercmd, const char *destination) { struct doveadm_server *orig_server, *new_server; struct server_connection *conn; + struct doveadm_proxy_redirect *redirect; struct ip_addr ip; in_port_t port; const char *destuser, *host, *error; @@ -123,6 +157,23 @@ static int doveadm_cmd_redirect(struct doveadm_mail_server_cmd *servercmd, return -1; } + if (doveadm_proxy_cmd_have_connected(servercmd, &ip, + orig_server->port)) { + i_error("%s: Proxying loops - already connected to %s:%u", + orig_server->name, net_ip2addr(&ip), orig_server->port); + return -1; + } + + i_assert(servercmd->proxy_ttl > 0); + servercmd->proxy_ttl--; + + /* Add current ip/port to redirect path */ + if (!array_is_created(&servercmd->redirect_path)) + i_array_init(&servercmd->redirect_path, 2); + redirect = array_append_space(&servercmd->redirect_path); + redirect->ip = ip; + redirect->port = orig_server->port; + new_server = doveadm_server_get(destination); new_server->ip = ip; new_server->ssl_flags = orig_server->ssl_flags; @@ -140,7 +191,8 @@ static int doveadm_cmd_redirect(struct doveadm_mail_server_cmd *servercmd, servercmd->conn = conn; if (servercmd->input != NULL) i_stream_seek(servercmd->input, 0); - server_connection_cmd(conn, servercmd->cmdline, servercmd->input, + server_connection_cmd(conn, servercmd->proxy_ttl, + servercmd->cmdline, servercmd->input, doveadm_cmd_callback, servercmd); return 0; } @@ -226,11 +278,13 @@ static void doveadm_mail_server_handle(struct server_connection *conn, servercmd = i_new(struct doveadm_mail_server_cmd, 1); servercmd->conn = conn; servercmd->username = i_strdup(username); + servercmd->proxy_ttl = cmd_ctx->proxy_ttl; servercmd->cmdline = i_strdup(str_c(cmd)); servercmd->input = cmd_ctx->cmd_input; if (servercmd->input != NULL) i_stream_ref(servercmd->input); - server_connection_cmd(conn, str_c(cmd), cmd_ctx->cmd_input, + server_connection_cmd(conn, cmd_ctx->proxy_ttl, + str_c(cmd), cmd_ctx->cmd_input, doveadm_cmd_callback, servercmd); } @@ -403,6 +457,10 @@ int doveadm_mail_server_user(struct doveadm_mail_cmd_context *ctx, /* run it ourself */ return 0; } + if (ctx->proxy_ttl <= 1) { + *error_r = "TTL reached zero - proxies appear to be looping?"; + return -1; + } if (referral != NULL) { ctx->cctx->referral = referral; return 1; diff --git a/src/doveadm/doveadm-mail.c b/src/doveadm/doveadm-mail.c index 574c3b7f34..84d798a3bf 100644 --- a/src/doveadm/doveadm-mail.c +++ b/src/doveadm/doveadm-mail.c @@ -30,6 +30,8 @@ #include +/* See LOGIN_PROXY_TTL */ +#define DOVEADM_PROXY_TTL 5 #define DOVEADM_MAIL_CMD_INPUT_TIMEOUT_MSECS (5*60*1000) struct force_resync_cmd_context { @@ -602,6 +604,7 @@ doveadm_mail_cmd_init(const struct doveadm_mail_cmd *cmd, ctx = cmd->alloc(); ctx->set = set; ctx->cmd = cmd; + ctx->proxy_ttl = DOVEADM_PROXY_TTL; if (ctx->v.init == NULL) ctx->v.init = doveadm_mail_cmd_init_noop; if (ctx->v.get_next_user == NULL) @@ -950,6 +953,9 @@ doveadm_cmd_ver2_to_mail_cmd_wrapper(struct doveadm_cmd_context *cctx) /* This parameter allows to set additional * mailbox transaction flags. */ mctx->transaction_flags = arg->value.v_int64; + } else if (strcmp(arg->name, "proxy-ttl") == 0) { + /* if this becomes <= 1, stop attempting to proxy */ + mctx->proxy_ttl = arg->value.v_int64; /* Keep all named special parameters above this line */ diff --git a/src/doveadm/doveadm-mail.h b/src/doveadm/doveadm-mail.h index af5d0fae3e..1af075d70e 100644 --- a/src/doveadm/doveadm-mail.h +++ b/src/doveadm/doveadm-mail.h @@ -77,6 +77,7 @@ struct doveadm_mail_cmd_context { /* search args aren't set for all mail commands */ struct mail_search_args *search_args; struct istream *users_list_input; + int proxy_ttl; struct mail_storage_service_user *cur_service_user; struct mail_user *cur_mail_user; @@ -198,7 +199,8 @@ DOVEADM_CMD_PARAM('A', "all-users", CMD_PARAM_BOOL, 0) \ DOVEADM_CMD_PARAM('S', "socket-path", CMD_PARAM_STR, 0) \ DOVEADM_CMD_PARAM('u', "user", CMD_PARAM_STR, 0) \ DOVEADM_CMD_PARAM('\0', "trans-flags", CMD_PARAM_INT64, 0) \ -DOVEADM_CMD_PARAM('F', "user-file", CMD_PARAM_ISTREAM, 0) +DOVEADM_CMD_PARAM('F', "user-file", CMD_PARAM_ISTREAM, 0) \ +DOVEADM_CMD_PARAM('\0', "proxy-ttl", CMD_PARAM_INT64, 0) #define DOVEADM_CMD_MAIL_USAGE_PREFIX \ "[-u |-A] [-S ] " diff --git a/src/doveadm/doveadm-util.h b/src/doveadm/doveadm-util.h index 8aefd92f34..5cda0149f8 100644 --- a/src/doveadm/doveadm-util.h +++ b/src/doveadm/doveadm-util.h @@ -4,9 +4,9 @@ #include "net.h" #define DOVEADM_SERVER_PROTOCOL_VERSION_MAJOR 1 -#define DOVEADM_SERVER_PROTOCOL_VERSION_MINOR 2 -#define DOVEADM_SERVER_PROTOCOL_VERSION_LINE "VERSION\tdoveadm-server\t1\t2" -#define DOVEADM_CLIENT_PROTOCOL_VERSION_LINE "VERSION\tdoveadm-client\t1\t2" +#define DOVEADM_SERVER_PROTOCOL_VERSION_MINOR 3 +#define DOVEADM_SERVER_PROTOCOL_VERSION_LINE "VERSION\tdoveadm-server\t1\t3" +#define DOVEADM_CLIENT_PROTOCOL_VERSION_LINE "VERSION\tdoveadm-client\t1\t3" extern bool doveadm_verbose, doveadm_debug, doveadm_server; diff --git a/src/doveadm/server-connection.c b/src/doveadm/server-connection.c index c22d10fd30..7de8c3113b 100644 --- a/src/doveadm/server-connection.c +++ b/src/doveadm/server-connection.c @@ -31,6 +31,7 @@ #define DOVEADM_PROTO_MINOR_MIN_MULTIPLEX 1 #define DOVEADM_PROTO_MINOR_MIN_STARTTLS 2 +#define DOVEADM_PROTO_MINOR_MIN_PROXY_TTL 3 enum server_reply_state { SERVER_REPLY_STATE_DONE = 0, @@ -57,6 +58,7 @@ struct server_connection { struct istream *cmd_input; struct ostream *cmd_output; const char *delayed_cmd; + int delayed_cmd_proxy_ttl; server_cmd_callback_t *callback; void *context; @@ -267,11 +269,44 @@ server_handle_input(struct server_connection *conn, i_stream_skip(conn->input, size); } +static void +server_connection_send_cmd(struct server_connection *conn, + const char *cmdline, int proxy_ttl) +{ + i_assert(conn->authenticated); + i_assert(proxy_ttl >= 1); + + if (conn->minor < DOVEADM_PROTO_MINOR_MIN_PROXY_TTL) { + o_stream_nsend_str(conn->output, cmdline); + return; + } + + /* [] - + Insert --proxy-ttl as the first arg. */ + const char *p = strchr(cmdline, '\t'); + i_assert(p != NULL); + p = strchr(p+1, '\t'); + i_assert(p != NULL); + p = strchr(p+1, '\t'); + i_assert(p != NULL); + size_t prefix_len = p - cmdline; + + const char *proxy_ttl_str = t_strdup_printf( + "\t--proxy-ttl\t%d", proxy_ttl); + struct const_iovec iov[] = { + { cmdline, prefix_len }, + { proxy_ttl_str, strlen(proxy_ttl_str) }, + { cmdline + prefix_len, strlen(cmdline + prefix_len) }, + }; + o_stream_nsendv(conn->output, iov, N_ELEMENTS(iov)); +} + static void server_connection_authenticated(struct server_connection *conn) { conn->authenticated = TRUE; if (conn->delayed_cmd != NULL) { - o_stream_nsend_str(conn->output, conn->delayed_cmd); + server_connection_send_cmd(conn, conn->delayed_cmd, + conn->delayed_cmd_proxy_ttl); conn->delayed_cmd = NULL; server_connection_send_cmd_input(conn); } @@ -680,11 +715,21 @@ server_connection_get_server(struct server_connection *conn) return conn->server; } -void server_connection_cmd(struct server_connection *conn, const char *line, - struct istream *cmd_input, +void server_connection_get_dest(struct server_connection *conn, + struct ip_addr *ip_r, in_port_t *port_r) +{ + if (net_getpeername(conn->fd, ip_r, port_r) < 0) { + i_zero(ip_r); + *port_r = 0; + } +} + +void server_connection_cmd(struct server_connection *conn, int proxy_ttl, + const char *line, struct istream *cmd_input, server_cmd_callback_t *callback, void *context) { i_assert(conn->delayed_cmd == NULL); + i_assert(proxy_ttl >= 1); conn->state = SERVER_REPLY_STATE_PRINT; if (cmd_input != NULL) { @@ -692,10 +737,11 @@ void server_connection_cmd(struct server_connection *conn, const char *line, i_stream_ref(cmd_input); conn->cmd_input = cmd_input; } - if (!conn->authenticated) + if (!conn->authenticated) { + conn->delayed_cmd_proxy_ttl = proxy_ttl; conn->delayed_cmd = p_strdup(conn->pool, line); - else { - o_stream_nsend_str(conn->output, line); + } else { + server_connection_send_cmd(conn, line, proxy_ttl); server_connection_send_cmd_input(conn); } conn->callback = callback; diff --git a/src/doveadm/server-connection.h b/src/doveadm/server-connection.h index c37a929732..f2dff41960 100644 --- a/src/doveadm/server-connection.h +++ b/src/doveadm/server-connection.h @@ -24,8 +24,11 @@ void server_connection_destroy(struct server_connection **conn); struct doveadm_server * server_connection_get_server(struct server_connection *conn); -void server_connection_cmd(struct server_connection *conn, const char *line, - struct istream *cmd_input, +void server_connection_get_dest(struct server_connection *conn, + struct ip_addr *ip_r, in_port_t *port_r); + +void server_connection_cmd(struct server_connection *conn, int proxy_ttl, + const char *line, struct istream *cmd_input, server_cmd_callback_t *callback, void *context); /* Returns TRUE if no command is being processed */ bool server_connection_is_idle(struct server_connection *conn);