]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
login-proxy: Support retrying reconnection on temporary authentication failures
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Wed, 6 May 2020 17:05:02 +0000 (20:05 +0300)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Mon, 25 May 2020 08:38:55 +0000 (08:38 +0000)
src/imap-login/imap-proxy.c
src/login-common/client-common-auth.c
src/login-common/login-proxy.c
src/login-common/login-proxy.h
src/pop3-login/pop3-proxy.c
src/submission-login/submission-proxy.c

index 09c10b80b2e2914ec509243ba04b02435532b4d1..c7f3daa19aaee8668cc521586dfea2e0411f78b8 100644 (file)
@@ -247,6 +247,13 @@ client_send_login_reply(struct imap_client *client, string_t *str,
        str_append(str, "\r\n");
 }
 
+static bool auth_resp_code_is_tempfail(const char *resp_code)
+{
+       /* Dovecot uses [UNAVAILABLE] for failures that can be retried.
+          Non-retriable failures are [SERVERBUG]. */
+       return strcasecmp(resp_code, IMAP_RESP_CODE_UNAVAILABLE) == 0;
+}
+
 int imap_proxy_parse_line(struct client *client, const char *line)
 {
        struct imap_client *imap_client = (struct imap_client *)client;
@@ -351,6 +358,8 @@ int imap_proxy_parse_line(struct client *client, const char *line)
                const char *log_line = line;
                if (strncasecmp(log_line, "NO ", 3) == 0)
                        log_line += 3;
+               enum login_proxy_failure_type failure_type =
+                       LOGIN_PROXY_FAILURE_TYPE_AUTH;
 #define STR_NO_IMAP_RESP_CODE_AUTHFAILED "NO ["IMAP_RESP_CODE_AUTHFAILED"]"
                if (str_begins(line, STR_NO_IMAP_RESP_CODE_AUTHFAILED)) {
                        /* the remote sent a generic "authentication failed"
@@ -363,8 +372,13 @@ int imap_proxy_parse_line(struct client *client, const char *line)
                                               AUTH_FAILED_MSG);
                } else if (str_begins(line, "NO [")) {
                        /* remote sent some other resp-code. forward it. */
-                       client_send_raw(client, t_strconcat(
-                               imap_client->cmd_tag, " ", line, "\r\n", NULL));
+                       const char *resp_code = t_strcut(line + 4, ']');
+                       if (auth_resp_code_is_tempfail(resp_code))
+                               failure_type = LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL;
+                       else {
+                               client_send_raw(client, t_strconcat(
+                                       imap_client->cmd_tag, " ", line, "\r\n", NULL));
+                       }
                } else {
                        /* there was no [resp-code], so remote isn't Dovecot
                           v1.2+. we could either forward the line as-is and
@@ -380,7 +394,7 @@ int imap_proxy_parse_line(struct client *client, const char *line)
 
                login_proxy_failed(client->login_proxy,
                                   login_proxy_get_event(client->login_proxy),
-                                  LOGIN_PROXY_FAILURE_TYPE_AUTH, log_line);
+                                  failure_type, log_line);
                return -1;
        } else if (strncasecmp(line, "* CAPABILITY ", 13) == 0) {
                i_free(imap_client->proxy_backend_capability);
@@ -452,7 +466,7 @@ void imap_proxy_reset(struct client *client)
 static void
 imap_proxy_send_failure_reply(struct imap_client *imap_client,
                              enum login_proxy_failure_type type,
-                             const char *reason ATTR_UNUSED)
+                             const char *reason)
 {
        switch (type) {
        case LOGIN_PROXY_FAILURE_TYPE_CONNECT:
@@ -469,6 +483,10 @@ imap_proxy_send_failure_reply(struct imap_client *imap_client,
                                       IMAP_RESP_CODE_SERVERBUG,
                                       LOGIN_PROXY_FAILURE_MSG);
                break;
+       case LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL:
+               client_send_raw(&imap_client->common, t_strconcat(
+                       imap_client->cmd_tag, " NO ", reason, "\r\n", NULL));
+               break;
        case LOGIN_PROXY_FAILURE_TYPE_AUTH:
                /* reply was already sent */
                break;
index 1cc9cce7f27baf2169bde5a6806bc23a7a79dc11..b66e63a2e79e6e750ee4462de2d1cda2292e0f79 100644 (file)
@@ -375,6 +375,7 @@ void client_common_proxy_failed(struct client *client,
        case LOGIN_PROXY_FAILURE_TYPE_PROTOCOL:
                break;
        case LOGIN_PROXY_FAILURE_TYPE_AUTH:
+       case LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL:
                client->proxy_auth_failed = TRUE;
                break;
        }
index 185a331fa604c95c35de7592e8e149b308233903..cf317b5b551715dd8c833d702a29054da3446071 100644 (file)
@@ -575,6 +575,9 @@ bool login_proxy_failed(struct login_proxy *proxy, struct event *event,
                log_prefix = "";
                try_reconnect = FALSE;
                break;
+       case LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL:
+               log_prefix = "";
+               break;
        default:
                i_unreached();
        }
@@ -586,7 +589,8 @@ bool login_proxy_failed(struct login_proxy *proxy, struct event *event,
                return TRUE;
        }
 
-       if (type != LOGIN_PROXY_FAILURE_TYPE_AUTH)
+       if (type != LOGIN_PROXY_FAILURE_TYPE_AUTH &&
+           type != LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL)
                e_error(event, "%s%s", log_prefix, reason);
        else if (proxy->client->set->auth_verbose)
                client_proxy_log_failure(proxy->client, reason);
index 4729c70a84f4f489a2fb10f5f01483de5bd5e321..84db55630179d18975932bf14da6d0b21eb9f25e 100644 (file)
@@ -41,6 +41,9 @@ enum login_proxy_failure_type {
        /* Authentication failed to backend. The LOGIN/AUTH command reply was
           already sent to the client. */
        LOGIN_PROXY_FAILURE_TYPE_AUTH,
+       /* Authentication failed with a temporary failure code. Attempting it
+          again might work. */
+       LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL,
 };
 
 struct login_proxy_settings {
index 30ae5a8a4f671a9a19e6cd94f6f300d0e969114d..e165b33c43d67031f3a25c0c33607d45965e5920 100644 (file)
@@ -248,18 +248,23 @@ int pop3_proxy_parse_line(struct client *client, const char *line)
           So for now we'll just forward the error message. This
           shouldn't be a real problem since of course everyone will
           be using only Dovecot as their backend :) */
+       enum login_proxy_failure_type failure_type =
+               LOGIN_PROXY_FAILURE_TYPE_AUTH;
        if (!str_begins(line, "-ERR ")) {
                client_send_reply(client, POP3_CMD_REPLY_ERROR,
                                  AUTH_FAILED_MSG);
+       } else if (str_begins(line, "-ERR [SYS/TEMP]")) {
+               /* delay sending the reply until we know if we reconnect */
+               failure_type = LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL;
+               line += 5;
        } else {
                client_send_raw(client, t_strconcat(line, "\r\n", NULL));
+               line += 5;
        }
 
-       if (str_begins(line, "-ERR "))
-               line += 5;
        login_proxy_failed(client->login_proxy,
                           login_proxy_get_event(client->login_proxy),
-                          LOGIN_PROXY_FAILURE_TYPE_AUTH, line);
+                          failure_type, line);
        return -1;
 }
 
@@ -273,7 +278,7 @@ void pop3_proxy_reset(struct client *client)
 static void
 pop3_proxy_send_failure_reply(struct client *client,
                              enum login_proxy_failure_type type,
-                             const char *reason ATTR_UNUSED)
+                             const char *reason)
 {
        switch (type) {
        case LOGIN_PROXY_FAILURE_TYPE_CONNECT:
@@ -285,6 +290,9 @@ pop3_proxy_send_failure_reply(struct client *client,
                client_send_reply(client, POP3_CMD_REPLY_ERROR,
                                  LOGIN_PROXY_FAILURE_MSG);
                break;
+       case LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL:
+               client_send_reply(client, POP3_CMD_REPLY_ERROR, reason);
+               break;
        case LOGIN_PROXY_FAILURE_TYPE_AUTH:
                /* reply was already sent */
                break;
index 3fa99577f7653e54ad2da7a624ea9c52407d2f0c..a148318ee1e8fd9b5b3c9f7b63941f09cf4a89a6 100644 (file)
@@ -432,14 +432,20 @@ int submission_proxy_parse_line(struct client *client, const char *line)
           So for now we'll just forward the error message. This
           shouldn't be a real problem since of course everyone will
           be using only Dovecot as their backend :) */
-       i_assert((status / 100) != 2);
-       i_assert(subm_client->proxy_reply != NULL);
-       smtp_server_reply_submit(subm_client->proxy_reply);
-       subm_client->pending_auth = NULL;
+       enum login_proxy_failure_type failure_type =
+               LOGIN_PROXY_FAILURE_TYPE_AUTH;
+       if ((status / 100) == 4)
+               failure_type = LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL;
+       else {
+               i_assert((status / 100) != 2);
+               i_assert(subm_client->proxy_reply != NULL);
+               smtp_server_reply_submit(subm_client->proxy_reply);
+               subm_client->pending_auth = NULL;
+       }
 
        login_proxy_failed(client->login_proxy,
                           login_proxy_get_event(client->login_proxy),
-                          LOGIN_PROXY_FAILURE_TYPE_AUTH, text);
+                          failure_type, text);
        return -1;
 }
 
@@ -473,6 +479,13 @@ submission_proxy_send_failure_reply(struct submission_client *subm_client,
                subm_client->pending_auth = NULL;
                smtp_server_reply(cmd, 454, "4.7.0", LOGIN_PROXY_FAILURE_MSG);
                break;
+       case LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL:
+               i_assert(cmd != NULL);
+               subm_client->pending_auth = NULL;
+
+               i_assert(subm_client->proxy_reply != NULL);
+               smtp_server_reply_submit(subm_client->proxy_reply);
+               break;
        case LOGIN_PROXY_FAILURE_TYPE_AUTH:
                /* reply was already sent */
                i_assert(cmd == NULL);