]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
doveadm: Add support for proxy loop detection
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 14 Jun 2021 17:59:09 +0000 (20:59 +0300)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Mon, 17 Jan 2022 11:52:09 +0000 (13:52 +0200)
src/doveadm/doveadm-dsync.c
src/doveadm/doveadm-mail-server.c
src/doveadm/doveadm-mail.c
src/doveadm/doveadm-mail.h
src/doveadm/doveadm-util.h
src/doveadm/server-connection.c
src/doveadm/server-connection.h

index 3614a383b8abae4227c8c85a9283efc7e8abbc21..7131b3c78aa388d6920e224573bb447c903a2d4a 100644 (file)
@@ -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;
index 7aa8c11d722df4c0941bd8809cd8d752319cf26b..4cec31d7d1f5ca9e28b3e9f6b7b9593d35a46456 100644 (file)
 #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;
index 574c3b7f3453b14c34b5ddc9991771d861ac40dd..84d798a3bf7606f4fc30740266461f04101d8cf0 100644 (file)
@@ -30,6 +30,8 @@
 
 #include <stdio.h>
 
+/* 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 */
 
index af5d0fae3e3ccb5f894c7a7d56a8033342057bda..1af075d70e104af7a8be64d035dae1c0f9c7510d 100644 (file)
@@ -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 <user>|-A] [-S <socket_path>] "
index 8aefd92f34a67927e90e667fb41543bde1caa364..5cda0149f855a151f4261520848090fd9e5f3a47 100644 (file)
@@ -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;
 
index c22d10fd30d420c8b29a0b45cc4d057b2d899c28..7de8c3113bb68c1e830ca9c3c61f9fb3bc6851a2 100644 (file)
@@ -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;
+       }
+
+       /* <flags> <username> <command> [<args>] -
+          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;
index c37a9297321dc585fafa92c8905f0690732c0116..f2dff41960216eb5ed055b5eee8327f57b7da2d8 100644 (file)
@@ -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);