#include "login-interface.h"
+#define AUTH_CLIENT_MINOR_VERSION_CHANNEL_BINDING 3
+
struct auth_client_connection {
struct connection conn;
struct auth *auth;
#include "str.h"
#include "strescape.h"
#include "str-sanitize.h"
+#include "base64.h"
#include "auth-request.h"
#include "userdb-template.h"
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)
{
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)
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);
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;
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;
}
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
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);
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. */