]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
auth: Add support for channel binding
authorStephan Bosch <stephan.bosch@open-xchange.com>
Thu, 5 Nov 2020 02:00:25 +0000 (03:00 +0100)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 12 Feb 2025 10:34:16 +0000 (12:34 +0200)
Also support channel binding for mechanisms such as GS2-KRB5, which only
involve one round trip. None of those is supported yet though. This is
implemented using out-of-band round trips that can exchange data between
auth service and login service beyond the normal SASL exchange.

src/auth/auth-client-connection.h
src/auth/auth-request-fields.c
src/auth/auth-request-handler.c
src/auth/auth-request.h

index 8967833f521ffac32363e4f60fedf397127c6f6c..f0d5c5c6c5713227f5de5e070ae5f18f6327bf5a 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "login-interface.h"
 
+#define AUTH_CLIENT_MINOR_VERSION_CHANNEL_BINDING 3
+
 struct auth_client_connection {
        struct connection conn;
        struct auth *auth;
index b076efcf3400080a71e34525816659f918b3dde1..bd545462fba3501950687714b18a78e4eb3126d3 100644 (file)
@@ -6,6 +6,7 @@
 #include "str.h"
 #include "strescape.h"
 #include "str-sanitize.h"
+#include "base64.h"
 #include "auth-request.h"
 #include "userdb-template.h"
 
@@ -246,6 +247,32 @@ bool auth_request_import_auth(struct auth_request *request,
        return TRUE;
 }
 
+static void
+auth_request_import_channel_binding(struct auth_request *request,
+                                   const char *value)
+{
+       struct auth_request_fields *fields = &request->fields;
+
+       if (fields->channel_binding.type == NULL ||
+           fields->channel_binding.data != NULL)
+               return;
+
+       size_t value_len = strlen(value);
+       fields->channel_binding.data = buffer_create_dynamic(
+               request->pool, MAX_BASE64_DECODED_SIZE(value_len));
+       (void)base64_decode(value, value_len,
+                           fields->channel_binding.data);
+}
+
+void auth_request_import_continue(struct auth_request *request,
+                                 const char *key, const char *value)
+{
+       i_assert(value != NULL);
+
+       if (strcmp(key, "channel_binding") == 0)
+               auth_request_import_channel_binding(request, value);
+}
+
 bool auth_request_import(struct auth_request *request,
                         const char *key, const char *value)
 {
@@ -292,6 +319,26 @@ bool auth_request_import(struct auth_request *request,
        return TRUE;
 }
 
+void auth_request_start_channel_binding(struct auth_request *request,
+                                       const char *type)
+{
+       i_assert(type != NULL);
+       request->fields.channel_binding.type = p_strdup(request->pool, type);
+       event_add_str(request->event, "channel_binding", type);
+}
+
+int auth_request_accept_channel_binding(struct auth_request *request,
+                                       buffer_t **data_r)
+{
+       if (request->fields.channel_binding.type == NULL ||
+           request->fields.channel_binding.data == NULL)
+               return -1;
+       *data_r = request->fields.channel_binding.data;
+       request->fields.channel_binding.type = NULL;
+       request->fields.channel_binding.data = NULL;
+       return 0;
+}
+
 static int
 auth_request_fix_username(struct auth_request *request, const char **username,
                          const char **error_r)
index e470a284ef6ca0f2b3e096abcfb1624980590c28..8a595c1045ca2a44112582e4b6e5c89c27016ee5 100644 (file)
@@ -271,7 +271,19 @@ auth_request_handler_reply_continue_finish(struct auth_request *request,
 
        str = t_str_new(64 + MAX_BASE64_ENCODED_SIZE(reply_size));
        str_printfa(str, "CONT\t%u\t", request->id);
-       base64_encode(auth_reply, reply_size, str);
+       if (auth_reply == NULL) {
+               /* Send out-of-band challenge */
+               str_append_c(str, '#');
+       } else {
+               /* Send normal challenge */
+               base64_encode(auth_reply, reply_size, str);
+       }
+       if (request->fields.channel_binding.type != NULL &&
+           handler->conn->conn.minor_version >=
+               AUTH_CLIENT_MINOR_VERSION_CHANNEL_BINDING) {
+               auth_str_add_keyvalue(str, "channel_binding",
+                                     request->fields.channel_binding.type);
+       }
 
        request->accept_cont_input = TRUE;
        handler->callback(str_c(str), handler->conn);
@@ -702,6 +714,8 @@ int auth_request_handler_auth_continue(struct auth_request_handler *handler,
                                       const char *const *args)
 {
        struct auth_request *request;
+       const char *name, *arg;
+       const char *data;
        size_t data_len;
        buffer_t *buf;
        unsigned int id;
@@ -734,19 +748,37 @@ int auth_request_handler_auth_continue(struct auth_request_handler *handler,
 
        request->accept_cont_input = FALSE;
 
-       data_len = strlen(args[1]);
-       buf = t_buffer_create(MAX_BASE64_DECODED_SIZE(data_len));
-       if (base64_decode(args[1], data_len, buf) < 0) {
-               auth_request_handler_auth_fail_code(handler, request,
-                       AUTH_CLIENT_FAIL_CODE_INVALID_BASE64,
-                       "Invalid base64 data in continued response");
-               return 1;
+       data = args[1];
+       data_len = strlen(data);
+       if (data_len == 1 && *data == '#') {
+               /* Out-of-band response */
+               buf = NULL;
+       } else {
+               /* Normal SASL response */
+               buf = t_buffer_create(MAX_BASE64_DECODED_SIZE(data_len));
+               if ((handler->conn->conn.minor_version <
+                       AUTH_CLIENT_MINOR_VERSION_CHANNEL_BINDING &&
+                    args[2] != NULL) ||
+                   base64_decode(data, data_len, buf) < 0) {
+                       auth_request_handler_auth_fail_code(handler, request,
+                               AUTH_CLIENT_FAIL_CODE_INVALID_BASE64,
+                               "Invalid base64 data in continued response");
+                       return -1;
+               }
+       }
+
+       for (args += 2; *args != NULL; args++) {
+               t_split_key_value_eq(*args, &name, &arg);
+               auth_request_import_continue(request, name, arg);
        }
 
        /* handler is referenced until auth_request_handler_reply()
           is called. */
        handler->refcount++;
-       auth_request_continue(request, buf->data, buf->used);
+       if (buf == NULL)
+               auth_request_continue(request, NULL, 0);
+       else
+               auth_request_continue(request, buf->data, buf->used);
        return 1;
 }
 
index e02682008ff5fef2da88442c17ff253a80be6575..ba2070cf78f8de0438d12883bbe3ad1368ebbaa8 100644 (file)
@@ -79,6 +79,10 @@ struct auth_request_fields {
        size_t delayed_credentials_size;
 
        enum auth_request_conn_secured conn_secured;
+       struct {
+               const char *type;
+               buffer_t *data;
+       } channel_binding;
 
        /* Authentication was successfully finished, including policy checks
           and such. There may still be some final delay or final SASL
@@ -272,6 +276,8 @@ bool auth_request_import_info(struct auth_request *request,
                              const char *key, const char *value);
 bool auth_request_import_auth(struct auth_request *request,
                              const char *key, const char *value);
+void auth_request_import_continue(struct auth_request *request,
+                                 const char *key, const char *value);
 bool auth_request_import_master(struct auth_request *request,
                                const char *key, const char *value);
 
@@ -288,6 +294,11 @@ void auth_request_lookup_credentials(struct auth_request *request,
 void auth_request_lookup_user(struct auth_request *request,
                              userdb_callback_t *callback);
 
+void auth_request_start_channel_binding(struct auth_request *request,
+                                       const char *type);
+int auth_request_accept_channel_binding(struct auth_request *request,
+                                       buffer_t **data_r);
+
 bool auth_request_set_username(struct auth_request *request,
                               const char *username, const char **error_r);
 /* Change the username without any translations or checks. */