From: Timo Sirainen Date: Wed, 9 Jun 2021 19:31:56 +0000 (+0300) Subject: doveadm-server: Support referrals returned by passdb X-Git-Tag: 2.4.0~4793 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2e5ea3307d5597ee21b3cf867707bbe856e90504;p=thirdparty%2Fdovecot%2Fcore.git doveadm-server: Support referrals returned by passdb --- diff --git a/src/doveadm/client-connection-tcp.c b/src/doveadm/client-connection-tcp.c index afe7e1d090..f63d3c32ac 100644 --- a/src/doveadm/client-connection-tcp.c +++ b/src/doveadm/client-connection-tcp.c @@ -157,10 +157,17 @@ static void doveadm_server_restore_logs(void) } static void -doveadm_cmd_server_post(struct client_connection_tcp *conn, const char *cmd_name) +doveadm_cmd_server_post(struct client_connection_tcp *conn, + struct doveadm_cmd_context *cctx) { const char *str = NULL; + if (cctx->referral != NULL) { + o_stream_nsend_str(conn->output, t_strdup_printf( + "\n-REFERRAL %s\n", cctx->referral)); + return; + } + if (doveadm_exit_code == 0) { o_stream_nsend(conn->output, "\n+\n", 3); return; @@ -174,7 +181,7 @@ doveadm_cmd_server_post(struct client_connection_tcp *conn, const char *cmd_name } else { o_stream_nsend_str(conn->output, "\n-\n"); i_error("BUG: Command '%s' returned unknown error code %d", - cmd_name, doveadm_exit_code); + cctx->cmd->name, doveadm_exit_code); } } @@ -186,7 +193,7 @@ doveadm_cmd_server_run_ver2(struct client_connection_tcp *conn, i_getopt_reset(); if (doveadm_cmdline_run(argc, argv, cctx) < 0) doveadm_exit_code = EX_USAGE; - doveadm_cmd_server_post(conn, cctx->cmd->name); + doveadm_cmd_server_post(conn, cctx); } static int doveadm_cmd_handle(struct client_connection_tcp *conn, diff --git a/src/doveadm/doveadm-cmd.h b/src/doveadm/doveadm-cmd.h index 2f44f76c90..f203a8ae2f 100644 --- a/src/doveadm/doveadm-cmd.h +++ b/src/doveadm/doveadm-cmd.h @@ -81,6 +81,10 @@ struct doveadm_cmd_context { enum doveadm_client_type conn_type; struct istream *input; struct ostream *output; + + /* non-NULL if doveadm-server should return referral to another + server instead. */ + const char *referral; }; ARRAY_DEFINE_TYPE(doveadm_cmd_ver2, struct doveadm_cmd_ver2); diff --git a/src/doveadm/doveadm-mail-server.c b/src/doveadm/doveadm-mail-server.c index 89a3540f13..e65a25e6e6 100644 --- a/src/doveadm/doveadm-mail-server.c +++ b/src/doveadm/doveadm-mail-server.c @@ -251,6 +251,7 @@ doveadm_mail_server_user_get_host(struct doveadm_mail_cmd_context *ctx, const char **user_r, const char **host_r, struct ip_addr *hostip_r, in_port_t *port_r, enum doveadm_proxy_ssl_flags *ssl_flags_r, + const char **referral_r, const char **error_r) { struct auth_master_connection *auth_conn; @@ -259,12 +260,13 @@ doveadm_mail_server_user_get_host(struct doveadm_mail_cmd_context *ctx, const char *auth_socket_path, *proxy_host, *proxy_hostip, *const *fields; unsigned int i; in_port_t proxy_port; - bool proxying; + bool proxying, nologin; int ret; *user_r = input->username; *host_r = ctx->set->doveadm_socket_path; *port_r = ctx->set->doveadm_port; + *referral_r = NULL; if (ctx->set->doveadm_port == 0) return 0; @@ -300,7 +302,7 @@ doveadm_mail_server_user_get_host(struct doveadm_mail_cmd_context *ctx, so just continue with the default host */ } else { proxy_host = NULL; proxy_hostip = NULL; proxying = FALSE; - proxy_port = ctx->set->doveadm_port; + proxy_port = ctx->set->doveadm_port; nologin = FALSE; for (i = 0; fields[i] != NULL; i++) { const char *p, *key, *value; @@ -315,6 +317,8 @@ doveadm_mail_server_user_get_host(struct doveadm_mail_cmd_context *ctx, if (strcmp(key, "proxy") == 0) proxying = TRUE; + else if (strcmp(key, "nologin") == 0) + nologin = TRUE; else if (strcmp(key, "host") == 0) proxy_host = value; else if (strcmp(key, "hostip") == 0) @@ -343,9 +347,21 @@ doveadm_mail_server_user_get_host(struct doveadm_mail_cmd_context *ctx, auth_socket_path, proxy_hostip); ret = -1; } - if (!proxying) - ret = 0; - else if (proxy_host == NULL) { + if (!proxying) { + if (!nologin) + ret = 0; + else if (proxy_host == NULL) { + /* Allow accessing nologin users via doveadm + protocol, since it's only admins that access + them. */ + ret = 0; + } else { + /* Referral */ + *referral_r = t_strdup_printf("%s@%s", + *user_r, proxy_host); + ret = 1; + } + } else if (proxy_host == NULL) { *error_r = t_strdup_printf("%s: Proxy is missing destination host", auth_socket_path); if (strstr(auth_socket_path, "/auth-userdb") != NULL) { @@ -369,7 +385,7 @@ int doveadm_mail_server_user(struct doveadm_mail_cmd_context *ctx, { struct doveadm_server *server; struct server_connection *conn; - const char *user, *host; + const char *user, *host, *referral; struct ip_addr hostip; enum doveadm_proxy_ssl_flags ssl_flags = 0; char *username_dup; @@ -381,7 +397,7 @@ int doveadm_mail_server_user(struct doveadm_mail_cmd_context *ctx, i_zero(&hostip); ret = doveadm_mail_server_user_get_host(ctx, input, &user, &host, &hostip, - &port, &ssl_flags, error_r); + &port, &ssl_flags, &referral, error_r); if (ret < 0) return ret; if (ret == 0 && @@ -389,6 +405,10 @@ int doveadm_mail_server_user(struct doveadm_mail_cmd_context *ctx, /* run it ourself */ return 0; } + if (referral != NULL) { + ctx->cctx->referral = referral; + return 1; + } /* server sends the sticky headers for each row as well, so undo any sticks we might have added already */ diff --git a/src/doveadm/server-connection.c b/src/doveadm/server-connection.c index fc277c2425..4887acd241 100644 --- a/src/doveadm/server-connection.c +++ b/src/doveadm/server-connection.c @@ -426,14 +426,27 @@ static void server_connection_input_cmd_error(struct server_connection *conn, const char *line) { + const char *code, *args = strchr(line, ' '); + if (args != NULL) + code = t_strdup_until(line, args++); + else { + code = line; + args = ""; + } struct doveadm_server_reply reply = { - .exit_code = doveadm_str_to_exit_code(line), + .exit_code = doveadm_str_to_exit_code(code), .error = line, }; - if (reply.exit_code == DOVEADM_EX_UNKNOWN && - str_to_int(line, &reply.exit_code) < 0) { - /* old doveadm-server */ - reply.exit_code = EX_TEMPFAIL; + switch (reply.exit_code) { + case DOVEADM_EX_REFERRAL: + reply.error = args; + break; + case DOVEADM_EX_UNKNOWN: + if (str_to_int(line, &reply.exit_code) < 0) { + /* old doveadm-server */ + reply.exit_code = EX_TEMPFAIL; + } + break; } server_connection_callback(conn, &reply); }