From: Timo Sirainen Date: Thu, 14 May 2020 21:39:31 +0000 (+0300) Subject: login-common: Add proxy_redirect_reauth auth extra field support X-Git-Tag: 2.4.0~4825 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3f52259fa938b1745fd1d0d8d439357d109803ca;p=thirdparty%2Fdovecot%2Fcore.git login-common: Add proxy_redirect_reauth auth extra field support Without this a proxy redirect request is immediately trusted and will be reconnected to. With this enabled, a new EXTERNAL authentication request is performed, which contains the redirect destination as well as the current redirection path. The auth server can catch this request and figure out whether the redirect is allowed or not, or if the proxy should be reconnecting to a different IP entirely. --- diff --git a/src/login-common/client-common-auth.c b/src/login-common/client-common-auth.c index 369d713856..3a4f574c79 100644 --- a/src/login-common/client-common-auth.c +++ b/src/login-common/client-common-auth.c @@ -7,6 +7,7 @@ #include "istream.h" #include "ostream.h" #include "str.h" +#include "strescape.h" #include "safe-memset.h" #include "time-util.h" #include "settings-parser.h" @@ -222,6 +223,8 @@ static bool client_auth_parse_args(const struct client *client, bool success, reply_r->proxy_nopipelining = TRUE; else if (strcmp(key, "proxy_not_trusted") == 0) reply_r->proxy_not_trusted = TRUE; + else if (strcmp(key, "proxy_redirect_reauth") == 0) + reply_r->proxy_redirect_reauth = TRUE; else if (strcmp(key, "master") == 0) { /* ignore empty master field */ if (*value != '\0') @@ -420,14 +423,108 @@ static void proxy_reset(struct client *client) client->v.proxy_reset(client); } +static void +proxy_redirect_reauth_callback(struct auth_client_request *request, + enum auth_request_status status, + const char *data_base64 ATTR_UNUSED, + const char *const *args, void *context) +{ + struct client *client = context; + struct client_auth_reply reply; + const char *error = NULL; + + i_assert(client->reauth_request == request); + + client->reauth_request = NULL; + switch (status) { + case AUTH_REQUEST_STATUS_CONTINUE: + error = "Unexpected SASL continuation request received"; + break; + case AUTH_REQUEST_STATUS_OK: + if (!client_auth_parse_args(client, FALSE, args, &reply)) { + error = "Redirect authentication returned invalid input"; + break; + } + + if (!reply.proxy) { + error = "Redirect authentication is missing proxy field"; + break; + } + login_proxy_redirect_finish(client->login_proxy, + &reply.host_ip, reply.port); + return; + case AUTH_REQUEST_STATUS_INTERNAL_FAIL: + error = "Internal authentication failure"; + break; + case AUTH_REQUEST_STATUS_FAIL: + if (!client_auth_parse_args(client, FALSE, args, &reply)) + error = "Failed to parse auth reply"; + else if (reply.reason == NULL || reply.reason[0] == '\0') + error = "Redirect authentication unexpectedly failed"; + else + error = t_strdup_printf( + "Redirect authentication unexpectedly failed: %s", + reply.reason); + break; + case AUTH_REQUEST_STATUS_ABORT: + error = "Redirect authentication aborted"; + break; + } + i_assert(error != NULL); + login_proxy_failed(client->login_proxy, + login_proxy_get_event(client->login_proxy), + LOGIN_PROXY_FAILURE_TYPE_INTERNAL, error); +} + +static void +proxy_redirect_reauth(struct client *client, const char *destuser, + const struct ip_addr *ip, in_port_t port) +{ + struct auth_request_info info; + const char *client_error; + + if (sasl_server_auth_request_info_fill(client, &info, &client_error) < 0) { + const char *error = t_strdup_printf( + "Unexpected failure on reauth: %s", client_error); + login_proxy_failed(client->login_proxy, + login_proxy_get_event(client->login_proxy), + LOGIN_PROXY_FAILURE_TYPE_INTERNAL, error); + return; + } + string_t *hosts_attempted = t_str_new(64); + str_append(hosts_attempted, "proxy_redirect_host_attempts="); + login_proxy_get_redirect_path(client->login_proxy, hosts_attempted); + unsigned int connect_timeout_msecs = + login_proxy_get_connect_timeout_msecs(client->login_proxy); + const char *const extra_fields[] = { + t_strdup_printf("proxy_redirect_host_next=%s:%u", + net_ip2addr(ip), port), + str_c(hosts_attempted), + t_strdup_printf("destuser=%s", str_tabescape(destuser)), + t_strdup_printf("proxy_timeout=%u", connect_timeout_msecs), + }; + info.mech = "EXTERNAL"; + t_array_init(&info.extra_fields, N_ELEMENTS(extra_fields)); + array_append(&info.extra_fields, extra_fields, + N_ELEMENTS(extra_fields)); + client->reauth_request = + auth_client_request_new(auth_client, &info, + proxy_redirect_reauth_callback, client); +} + static bool proxy_try_redirect(struct client *client, const char *destination, const char **error_r) { - const char *host; + const char *host, *p, *destuser = client->proxy_user; struct ip_addr ip; in_port_t port; + p = strrchr(destination, '@'); + if (p != NULL) { + destuser = t_strdup_until(destination, p); + destination = p+1; + } if (net_str2hostport(destination, login_proxy_get_port(client->login_proxy), &host, &port) < 0) { @@ -441,7 +538,12 @@ proxy_try_redirect(struct client *client, const char *destination, host); return FALSE; } - login_proxy_redirect_finish(client->login_proxy, &ip, port); + /* At least for now we support sending the destuser only for reauth + requests. */ + if (client->proxy_redirect_reauth) + proxy_redirect_reauth(client, destuser, &ip, port); + else + login_proxy_redirect_finish(client->login_proxy, &ip, port); return TRUE; } @@ -454,7 +556,8 @@ proxy_redirect(struct client *client, struct event *event, proxy_reset(client); if (!proxy_try_redirect(client, destination, &error)) { login_proxy_failed(client->login_proxy, event, - LOGIN_PROXY_FAILURE_TYPE_INTERNAL_CONFIG, error); + LOGIN_PROXY_FAILURE_TYPE_INTERNAL_CONFIG, + t_strdup_printf("Redirect to %s: %s", destination, error)); } } @@ -567,6 +670,7 @@ static int proxy_start(struct client *client, client->proxy_noauth = reply->proxy_noauth; client->proxy_nopipelining = reply->proxy_nopipelining; client->proxy_not_trusted = reply->proxy_not_trusted; + client->proxy_redirect_reauth = reply->proxy_redirect_reauth; if (login_proxy_new(client, event, &proxy_set, proxy_input, client->v.proxy_failed, proxy_redirect) < 0) { diff --git a/src/login-common/client-common.c b/src/login-common/client-common.c index 41a111b1ed..d3df9bb57b 100644 --- a/src/login-common/client-common.c +++ b/src/login-common/client-common.c @@ -353,6 +353,9 @@ void client_destroy(struct client *client, const char *reason) i_assert(client->auth_request == NULL); i_assert(client->anvil_query == NULL); + if (client->reauth_request != NULL) + auth_client_request_abort(&client->reauth_request, "Aborted"); + timeout_remove(&client->to_disconnect); timeout_remove(&client->to_auth_waiting); str_free(&client->auth_response); diff --git a/src/login-common/client-common.h b/src/login-common/client-common.h index 428a1f2141..3d6dfad1c1 100644 --- a/src/login-common/client-common.h +++ b/src/login-common/client-common.h @@ -115,6 +115,7 @@ struct client_auth_reply { bool proxy_noauth:1; bool proxy_nopipelining:1; bool proxy_not_trusted:1; + bool proxy_redirect_reauth:1; bool nologin:1; }; @@ -204,6 +205,7 @@ struct client { char *auth_mech_name; enum sasl_server_auth_flags auth_flags; struct auth_client_request *auth_request; + struct auth_client_request *reauth_request; string_t *auth_response; time_t auth_first_started, auth_finished; const char *sasl_final_resp; @@ -255,6 +257,7 @@ struct client { bool proxy_noauth:1; bool proxy_nopipelining:1; bool proxy_not_trusted:1; + bool proxy_redirect_reauth:1; bool auth_waiting:1; bool notified_auth_ready:1; bool notified_disconnect:1; diff --git a/src/login-common/login-proxy.c b/src/login-common/login-proxy.c index 01f53ba192..643512e45e 100644 --- a/src/login-common/login-proxy.c +++ b/src/login-common/login-proxy.c @@ -811,6 +811,12 @@ login_proxy_get_ssl_flags(const struct login_proxy *proxy) return proxy->ssl_flags; } +unsigned int +login_proxy_get_connect_timeout_msecs(const struct login_proxy *proxy) +{ + return proxy->connect_timeout_msecs; +} + static void login_proxy_finished(enum iostream_proxy_side side, enum iostream_proxy_status status, diff --git a/src/login-common/login-proxy.h b/src/login-common/login-proxy.h index a96fc86962..48cf0bf629 100644 --- a/src/login-common/login-proxy.h +++ b/src/login-common/login-proxy.h @@ -123,6 +123,8 @@ const char *login_proxy_get_ip_str(const struct login_proxy *proxy) ATTR_PURE; in_port_t login_proxy_get_port(const struct login_proxy *proxy) ATTR_PURE; enum login_proxy_ssl_flags login_proxy_get_ssl_flags(const struct login_proxy *proxy) ATTR_PURE; +unsigned int +login_proxy_get_connect_timeout_msecs(const struct login_proxy *proxy) ATTR_PURE; void login_proxy_kill_idle(void);