]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-auth-client: auth-client - Implement implicit handling of final SASL reply
authorStephan Bosch <stephan.bosch@open-xchange.com>
Fri, 27 Oct 2023 02:42:09 +0000 (04:42 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Fri, 17 Nov 2023 10:49:10 +0000 (10:49 +0000)
Turn it into a additional SASL interaction cycle if the client does not support
handing a final reply/challenge.

src/lib-auth-client/auth-client-private.h
src/lib-auth-client/auth-client-request.c
src/lib-auth-client/auth-client.h
src/login-common/sasl-server.c

index d5c99e2fc364f6d3aa0d3ee47b537cead375041d..b87bf9339d789c68dd064ba54c357903ff7acd7b 100644 (file)
@@ -9,13 +9,17 @@
 
 struct auth_client_request {
        pool_t pool;
+       enum auth_request_flags flags;
        struct event *event;
 
        struct auth_client_connection *conn;
-       struct timeout *to_fail;
+       struct timeout *to_fail, *to_final;
        unsigned int id;
        time_t created;
 
+       enum auth_request_status final_status;
+       const char *const *final_args;
+
        auth_request_callback_t *callback;
        void *context;
 
index 472e3c05ea9c98a9717b2811fd45ec072354cc9c..bda7e2bcd6a2b2eb07ace25ded6f695a3e05bae9 100644 (file)
 
 static void
 auth_client_request_fail_conn_lost(struct auth_client_request *request);
+static void
+auth_client_request_handle_input(struct auth_client_request **_request,
+                                enum auth_request_status status,
+                                const char *base64_data,
+                                const char *const *args, bool final);
 
 static void
 auth_server_send_new_request(struct auth_client_connection *conn,
@@ -190,6 +195,8 @@ auth_client_request_new(struct auth_client *client,
        request = p_new(pool, struct auth_client_request, 1);
        request->pool = pool;
        request->conn = client->conn;
+       request->flags = request_info->flags;
+       request->final_status = AUTH_REQUEST_STATUS_CONTINUE;
 
        request->callback = callback;
        request->context = context;
@@ -250,6 +257,7 @@ static void auth_client_request_free(struct auth_client_request **_request)
        auth_client_connection_remove_request(request->conn, request);
 
        timeout_remove(&request->to_fail);
+       timeout_remove(&request->to_final);
        event_unref(&request->event);
        pool_unref(&request->pool);
 }
@@ -310,12 +318,26 @@ args_parse_user(struct auth_client_request *request, const char *key,
                event_add_str(request->event, "auth_user", value);
 }
 
+static void auth_client_request_final(struct auth_client_request *request)
+{
+       timeout_remove(&request->to_final);
+       i_assert(request->final_status != AUTH_REQUEST_STATUS_CONTINUE);
+       auth_client_request_handle_input(&request, request->final_status, NULL,
+                                        request->final_args, FALSE);
+}
+
 void auth_client_request_continue(struct auth_client_request *request,
                                   const char *data_base64)
 {
        struct const_iovec iov[3];
        const char *prefix;
 
+       if (request->final_status != AUTH_REQUEST_STATUS_CONTINUE) {
+               request->to_final = timeout_add_short(
+                       0, auth_client_request_final, request);
+               return;
+       }
+
        if (!request->conn->connected) {
                e_error(request->event,
                        "Error sending continue request to auth server: "
@@ -343,18 +365,20 @@ void auth_client_request_continue(struct auth_client_request *request,
        }
 }
 
-void auth_client_request_server_input(struct auth_client_request **_request,
-                                     enum auth_request_status status,
-                                     const char *const *args)
+static void
+auth_client_request_handle_input(struct auth_client_request **_request,
+                                enum auth_request_status status,
+                                const char *base64_data,
+                                const char *const *args, bool final)
 {
        struct auth_client_request *request = *_request;
-       const char *const *tmp, *base64_data = NULL;
+       const char *const *tmp;
        struct event_passthrough *e;
 
        if (auth_client_request_is_aborted(request)) {
                /* aborted already */
                auth_client_request_free(_request);
-               return TRUE;
+               return;
        }
 
        switch (status) {
@@ -368,17 +392,14 @@ void auth_client_request_server_input(struct auth_client_request **_request,
                break;
        }
 
-       for (tmp = args; *tmp != NULL; tmp++) {
+       for (tmp = args; tmp != NULL && *tmp != NULL; tmp++) {
                const char *key;
                const char *value;
                t_split_key_value_eq(*tmp, &key, &value);
-               if (str_begins(key, "event_", &key)) {
+               if (str_begins(key, "event_", &key))
                        event_add_str(request->event, key, value);
-               } else if (strcmp(key, "resp") == 0) {
-                       base64_data = value;
-               } else {
+               else
                        args_parse_user(request, key, value);
-               }
        }
 
        switch (status) {
@@ -386,9 +407,10 @@ void auth_client_request_server_input(struct auth_client_request **_request,
                e_debug(e->event(), "Finished");
                break;
        case AUTH_REQUEST_STATUS_CONTINUE:
-               base64_data = args[0];
-               args = NULL;
-               e_debug(e->event(), "Got challenge");
+               if (!final)
+                       e_debug(e->event(), "Got challenge");
+               else
+                       e_debug(e->event(), "Created final challenge");
                break;
        case AUTH_REQUEST_STATUS_FAIL:
                e->add_str("error", "Authentication failed");
@@ -407,6 +429,54 @@ void auth_client_request_server_input(struct auth_client_request **_request,
                auth_client_request_free(_request);
 }
 
+void auth_client_request_server_input(struct auth_client_request **_request,
+                                     enum auth_request_status status,
+                                     const char *const *args)
+{
+       struct auth_client_request *request = *_request;
+       const char *const *tmp, *base64_data = NULL;
+       bool final = FALSE;
+
+       if (auth_client_request_is_aborted(request)) {
+               /* aborted already */
+               auth_client_request_free(_request);
+               return;
+       }
+
+       switch (status) {
+       case AUTH_REQUEST_STATUS_FAIL:
+       case AUTH_REQUEST_STATUS_OK:
+       case AUTH_REQUEST_STATUS_INTERNAL_FAIL:
+               for (tmp = args; *tmp != NULL; tmp++) {
+                       const char *key;
+                       const char *value;
+                       t_split_key_value_eq(*tmp, &key, &value);
+                       if (strcmp(key, "resp") == 0)
+                               base64_data = value;
+               }
+               if (base64_data == NULL ||
+                   (status == AUTH_REQUEST_STATUS_OK &&
+                    HAS_ALL_BITS(request->flags,
+                                 AUTH_REQUEST_FLAG_SUPPORT_FINAL_RESP)))
+                       break;
+               request->final_status = status;
+               request->final_args = p_strarray_dup(request->pool, args);
+               status = AUTH_REQUEST_STATUS_CONTINUE;
+               args = NULL;
+               final = TRUE;
+               break;
+       case AUTH_REQUEST_STATUS_CONTINUE:
+               base64_data = args[0];
+               args = NULL;
+               break;
+       case AUTH_REQUEST_STATUS_ABORT:
+               i_unreached();
+       }
+
+       auth_client_request_handle_input(_request, status,
+                                        base64_data, args, final);
+}
+
 void auth_client_send_cancel(struct auth_client *client, unsigned int id)
 {
        if (!client->conn->connected) {
index 79b23bd848bd6b070bfb174f2b745f22ee800888..236e97648d9d86e8b8bcae2b6ec9171e3736d2bc 100644 (file)
@@ -17,6 +17,8 @@ enum auth_request_flags {
        AUTH_REQUEST_FLAG_VALID_CLIENT_CERT     = 0x02,
        /* Skip penalty checks for this request */
        AUTH_REQUEST_FLAG_NO_PENALTY            = 0x04,
+       /* Support final SASL response (handled locally) */
+       AUTH_REQUEST_FLAG_SUPPORT_FINAL_RESP    = 0x08,
        /* Enable auth_debug=yes logging for this request */
        AUTH_REQUEST_FLAG_DEBUG                 = 0x10,
        /* Connection from the previous hop is secured by TLS. */
index 99c4c8ab51d9eea5d616d322755c331f34d51bba..6f88a6aea28e0dd8b1fe015eea31cad4d02e9e27 100644 (file)
@@ -113,6 +113,8 @@ client_get_auth_flags(struct client *client)
                auth_flags |= AUTH_REQUEST_FLAG_CONN_SECURED_TLS;
        if (client->connection_secured)
                auth_flags |= AUTH_REQUEST_FLAG_CONN_SECURED;
+       if (login_binary->sasl_support_final_reply)
+               auth_flags |= AUTH_REQUEST_FLAG_SUPPORT_FINAL_RESP;
        return auth_flags;
 }