Skip to content

Commit 1486c30

Browse files
stephanboschcmouse
authored andcommitted
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.
1 parent a8848be commit 1486c30

File tree

4 files changed

+101
-9
lines changed

4 files changed

+101
-9
lines changed

src/auth/auth-client-connection.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#include "login-interface.h"
55

6+
#define AUTH_CLIENT_MINOR_VERSION_CHANNEL_BINDING 3
7+
68
struct auth_client_connection {
79
struct connection conn;
810
struct auth *auth;

src/auth/auth-request-fields.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "str.h"
77
#include "strescape.h"
88
#include "str-sanitize.h"
9+
#include "base64.h"
910
#include "auth-request.h"
1011
#include "userdb-template.h"
1112

@@ -246,6 +247,32 @@ bool auth_request_import_auth(struct auth_request *request,
246247
return TRUE;
247248
}
248249

250+
static void
251+
auth_request_import_channel_binding(struct auth_request *request,
252+
const char *value)
253+
{
254+
struct auth_request_fields *fields = &request->fields;
255+
256+
if (fields->channel_binding.type == NULL ||
257+
fields->channel_binding.data != NULL)
258+
return;
259+
260+
size_t value_len = strlen(value);
261+
fields->channel_binding.data = buffer_create_dynamic(
262+
request->pool, MAX_BASE64_DECODED_SIZE(value_len));
263+
(void)base64_decode(value, value_len,
264+
fields->channel_binding.data);
265+
}
266+
267+
void auth_request_import_continue(struct auth_request *request,
268+
const char *key, const char *value)
269+
{
270+
i_assert(value != NULL);
271+
272+
if (strcmp(key, "channel_binding") == 0)
273+
auth_request_import_channel_binding(request, value);
274+
}
275+
249276
bool auth_request_import(struct auth_request *request,
250277
const char *key, const char *value)
251278
{
@@ -292,6 +319,26 @@ bool auth_request_import(struct auth_request *request,
292319
return TRUE;
293320
}
294321

322+
void auth_request_start_channel_binding(struct auth_request *request,
323+
const char *type)
324+
{
325+
i_assert(type != NULL);
326+
request->fields.channel_binding.type = p_strdup(request->pool, type);
327+
event_add_str(request->event, "channel_binding", type);
328+
}
329+
330+
int auth_request_accept_channel_binding(struct auth_request *request,
331+
buffer_t **data_r)
332+
{
333+
if (request->fields.channel_binding.type == NULL ||
334+
request->fields.channel_binding.data == NULL)
335+
return -1;
336+
*data_r = request->fields.channel_binding.data;
337+
request->fields.channel_binding.type = NULL;
338+
request->fields.channel_binding.data = NULL;
339+
return 0;
340+
}
341+
295342
static int
296343
auth_request_fix_username(struct auth_request *request, const char **username,
297344
const char **error_r)

src/auth/auth-request-handler.c

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,19 @@ auth_request_handler_reply_continue_finish(struct auth_request *request,
271271

272272
str = t_str_new(64 + MAX_BASE64_ENCODED_SIZE(reply_size));
273273
str_printfa(str, "CONT\t%u\t", request->id);
274-
base64_encode(auth_reply, reply_size, str);
274+
if (auth_reply == NULL) {
275+
/* Send out-of-band challenge */
276+
str_append_c(str, '#');
277+
} else {
278+
/* Send normal challenge */
279+
base64_encode(auth_reply, reply_size, str);
280+
}
281+
if (request->fields.channel_binding.type != NULL &&
282+
handler->conn->conn.minor_version >=
283+
AUTH_CLIENT_MINOR_VERSION_CHANNEL_BINDING) {
284+
auth_str_add_keyvalue(str, "channel_binding",
285+
request->fields.channel_binding.type);
286+
}
275287

276288
request->accept_cont_input = TRUE;
277289
handler->callback(str_c(str), handler->conn);
@@ -702,6 +714,8 @@ int auth_request_handler_auth_continue(struct auth_request_handler *handler,
702714
const char *const *args)
703715
{
704716
struct auth_request *request;
717+
const char *name, *arg;
718+
const char *data;
705719
size_t data_len;
706720
buffer_t *buf;
707721
unsigned int id;
@@ -734,19 +748,37 @@ int auth_request_handler_auth_continue(struct auth_request_handler *handler,
734748

735749
request->accept_cont_input = FALSE;
736750

737-
data_len = strlen(args[1]);
738-
buf = t_buffer_create(MAX_BASE64_DECODED_SIZE(data_len));
739-
if (base64_decode(args[1], data_len, buf) < 0) {
740-
auth_request_handler_auth_fail_code(handler, request,
741-
AUTH_CLIENT_FAIL_CODE_INVALID_BASE64,
742-
"Invalid base64 data in continued response");
743-
return 1;
751+
data = args[1];
752+
data_len = strlen(data);
753+
if (data_len == 1 && *data == '#') {
754+
/* Out-of-band response */
755+
buf = NULL;
756+
} else {
757+
/* Normal SASL response */
758+
buf = t_buffer_create(MAX_BASE64_DECODED_SIZE(data_len));
759+
if ((handler->conn->conn.minor_version <
760+
AUTH_CLIENT_MINOR_VERSION_CHANNEL_BINDING &&
761+
args[2] != NULL) ||
762+
base64_decode(data, data_len, buf) < 0) {
763+
auth_request_handler_auth_fail_code(handler, request,
764+
AUTH_CLIENT_FAIL_CODE_INVALID_BASE64,
765+
"Invalid base64 data in continued response");
766+
return -1;
767+
}
768+
}
769+
770+
for (args += 2; *args != NULL; args++) {
771+
t_split_key_value_eq(*args, &name, &arg);
772+
auth_request_import_continue(request, name, arg);
744773
}
745774

746775
/* handler is referenced until auth_request_handler_reply()
747776
is called. */
748777
handler->refcount++;
749-
auth_request_continue(request, buf->data, buf->used);
778+
if (buf == NULL)
779+
auth_request_continue(request, NULL, 0);
780+
else
781+
auth_request_continue(request, buf->data, buf->used);
750782
return 1;
751783
}
752784

src/auth/auth-request.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ struct auth_request_fields {
7979
size_t delayed_credentials_size;
8080

8181
enum auth_request_conn_secured conn_secured;
82+
struct {
83+
const char *type;
84+
buffer_t *data;
85+
} channel_binding;
8286

8387
/* Authentication was successfully finished, including policy checks
8488
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,
272276
const char *key, const char *value);
273277
bool auth_request_import_auth(struct auth_request *request,
274278
const char *key, const char *value);
279+
void auth_request_import_continue(struct auth_request *request,
280+
const char *key, const char *value);
275281
bool auth_request_import_master(struct auth_request *request,
276282
const char *key, const char *value);
277283

@@ -288,6 +294,11 @@ void auth_request_lookup_credentials(struct auth_request *request,
288294
void auth_request_lookup_user(struct auth_request *request,
289295
userdb_callback_t *callback);
290296

297+
void auth_request_start_channel_binding(struct auth_request *request,
298+
const char *type);
299+
int auth_request_accept_channel_binding(struct auth_request *request,
300+
buffer_t **data_r);
301+
291302
bool auth_request_set_username(struct auth_request *request,
292303
const char *username, const char **error_r);
293304
/* Change the username without any translations or checks. */

0 commit comments

Comments
 (0)