From: Stephan Bosch Date: Thu, 5 Nov 2020 02:00:25 +0000 (+0100) Subject: auth: Add support for channel binding X-Git-Tag: 2.4.1~297 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1486c30e191ff079bfa78e7950173bb33d8073d9;p=thirdparty%2Fdovecot%2Fcore.git auth: Add support for channel binding 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. --- diff --git a/src/auth/auth-client-connection.h b/src/auth/auth-client-connection.h index 8967833f52..f0d5c5c6c5 100644 --- a/src/auth/auth-client-connection.h +++ b/src/auth/auth-client-connection.h @@ -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; diff --git a/src/auth/auth-request-fields.c b/src/auth/auth-request-fields.c index b076efcf34..bd545462fb 100644 --- a/src/auth/auth-request-fields.c +++ b/src/auth/auth-request-fields.c @@ -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) diff --git a/src/auth/auth-request-handler.c b/src/auth/auth-request-handler.c index e470a284ef..8a595c1045 100644 --- a/src/auth/auth-request-handler.c +++ b/src/auth/auth-request-handler.c @@ -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; } diff --git a/src/auth/auth-request.h b/src/auth/auth-request.h index e02682008f..ba2070cf78 100644 --- a/src/auth/auth-request.h +++ b/src/auth/auth-request.h @@ -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. */