Skip to content

Commit

Permalink
box: make auth subsystem pluggable
Browse files Browse the repository at this point in the history
This commit introduces an abstraction for the authentication code so
that one can easily add new methods. To add a new method, one just needs
to define a set of authentication callbacks in a struct auth_method and
register it with auth_method_register.

The IPROTO_AUTH and _user.auth formats were initially designed with
extensibility in mind: both take the authentication method name
(currently, only 'chap-sha1' is supported) so no changes to the schema
are required.

Note that although 'chap-sha1' is now implemented in its own file
src/box/auth_chap_sha1.c, we don't merge src/scramble.c into it.
This will be done later, in the scope of tarantool#7987.

Since we call authentication plug-ins "methods" (not "mechanisms"),
let's rename BOX_USER_FIELD_AUTH_MECH_LIST to BOX_USER_FIELD_AUTH while
we are at it. Anyway, the corresponding field of the _user system space
is called 'auth' (not 'auth_mech_list').

Closes tarantool#7986

NO_DOC=refactoring
NO_TEST=refactoring
NO_CHANGELOG=refactoring
  • Loading branch information
locker committed Dec 8, 2022
1 parent df7af0c commit 1c9e1a3
Show file tree
Hide file tree
Showing 17 changed files with 627 additions and 97 deletions.
1 change: 0 additions & 1 deletion extra/exports
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,6 @@ lua_upvaluejoin
lua_xmove
lua_yield
memtx_tx_story_gc_step
password_prepare
PMurHash32
PMurHash32_Process
PMurHash32_Result
Expand Down
1 change: 1 addition & 0 deletions src/box/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ set(box_sources
user_def.c
user.cc
authentication.c
auth_chap_sha1.c
replication.cc
recovery.cc
xstream.cc
Expand Down
53 changes: 26 additions & 27 deletions src/box/alter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#include "constraint_id.h"
#include "space_upgrade.h"
#include "box.h"
#include "authentication.h"

/* {{{ Auxiliary functions and methods. */

Expand Down Expand Up @@ -2883,11 +2884,9 @@ user_has_data(struct user *user, bool *has_data)
}

/**
* Supposedly a user may have many authentication mechanisms
* defined, but for now we only support chap-sha1. Get
* password of chap-sha1 from the _user space.
* Initialize the user authenticator from the _user space data.
*/
int
static int
user_def_fill_auth_data(struct user_def *user, const char *auth_data)
{
uint8_t type = mp_typeof(*auth_data);
Expand All @@ -2908,35 +2907,36 @@ user_def_fill_auth_data(struct user_def *user, const char *auth_data)
"use box.schema.user.passwd() to reset password");
return -1;
}
uint32_t mech_count = mp_decode_map(&auth_data);
for (uint32_t i = 0; i < mech_count; i++) {
uint32_t method_count = mp_decode_map(&auth_data);
for (uint32_t i = 0; i < method_count; i++) {
if (mp_typeof(*auth_data) != MP_STR) {
mp_next(&auth_data);
mp_next(&auth_data);
continue;
}
uint32_t len;
const char *mech_name = mp_decode_str(&auth_data, &len);
if (strncasecmp(mech_name, "chap-sha1", 9) != 0) {
mp_next(&auth_data);
uint32_t method_name_len;
const char *method_name = mp_decode_str(&auth_data,
&method_name_len);
const char *auth_data_end = auth_data;
mp_next(&auth_data_end);
const struct auth_method *method = auth_method_by_name(
method_name, method_name_len);
if (method == NULL) {
auth_data = auth_data_end;
continue;
}
const char *hash2_base64 = mp_decode_str(&auth_data, &len);
if (len != 0 && len != SCRAMBLE_BASE64_SIZE) {
diag_set(ClientError, ER_CREATE_USER,
user->name, "invalid user password");
struct authenticator *auth = authenticator_new(
method, auth_data, auth_data_end);
if (auth == NULL)
return -1;
/* The guest user may only have an empty password. */
if (user->uid == GUEST &&
!authenticate_password(auth, "", 0)) {
authenticator_delete(auth);
diag_set(ClientError, ER_GUEST_USER_PASSWORD);
return -1;
}
if (user->uid == GUEST) {
/** Guest user is permitted to have empty password */
if (strncmp(hash2_base64, CHAP_SHA1_EMPTY_PASSWORD, len)) {
diag_set(ClientError, ER_GUEST_USER_PASSWORD);
return -1;
}
}

base64_decode(hash2_base64, len, user->hash2,
sizeof(user->hash2));
user->auth = auth;
break;
}
return 0;
Expand Down Expand Up @@ -2981,9 +2981,8 @@ user_def_new_from_tuple(struct tuple *tuple)
* Check for trivial errors when a plain text
* password is saved in this field instead.
*/
if (tuple_field_count(tuple) > BOX_USER_FIELD_AUTH_MECH_LIST) {
const char *auth_data =
tuple_field(tuple, BOX_USER_FIELD_AUTH_MECH_LIST);
if (tuple_field_count(tuple) > BOX_USER_FIELD_AUTH) {
const char *auth_data = tuple_field(tuple, BOX_USER_FIELD_AUTH);
const char *tmp = auth_data;
bool is_auth_empty;
if (mp_typeof(*auth_data) == MP_ARRAY &&
Expand Down
186 changes: 186 additions & 0 deletions src/box/auth_chap_sha1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright 2010-2022, Tarantool AUTHORS, please see AUTHORS file.
*/
#include "auth_chap_sha1.h"

#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>

#include "authentication.h"
#include "base64.h"
#include "diag.h"
#include "errcode.h"
#include "error.h"
#include "fiber.h"
#include "msgpuck.h"
#include "scramble.h"
#include "small/region.h"
#include "trivia/util.h"

#define AUTH_CHAP_SHA1_NAME "chap-sha1"

/** chap-sha1 authenticator implementation. */
struct auth_chap_sha1_authenticator {
/** Base class. */
struct authenticator base;
/** sha1(sha1(password)). */
char hash2[SCRAMBLE_SIZE];
};

/** auth_method::auth_method_delete */
static void
auth_chap_sha1_delete(struct auth_method *method)
{
TRASH(method);
free(method);
}

/** auth_method::auth_data_prepare */
static void
auth_chap_sha1_data_prepare(const struct auth_method *method,
const char *password, int password_len,
const char **auth_data,
const char **auth_data_end)
{
(void)method;
struct region *region = &fiber()->gc;
size_t size = mp_sizeof_str(SCRAMBLE_BASE64_SIZE);
char *p = xregion_alloc(region, size);
*auth_data = p;
*auth_data_end = p + size;
p = mp_encode_strl(p, SCRAMBLE_BASE64_SIZE);
password_prepare(password, password_len, p, SCRAMBLE_BASE64_SIZE);
}

/** auth_method::auth_request_prepare */
static void
auth_chap_sha1_request_prepare(const struct auth_method *method,
const char *password, int password_len,
const char *salt,
const char **auth_request,
const char **auth_request_end)
{
(void)method;
struct region *region = &fiber()->gc;
size_t size = mp_sizeof_str(SCRAMBLE_SIZE);
char *p = xregion_alloc(region, size);
*auth_request = p;
*auth_request_end = p + size;
p = mp_encode_strl(p, SCRAMBLE_SIZE);
scramble_prepare(p, salt, password, password_len);
}

/** auth_method::auth_request_check */
static int
auth_chap_sha1_request_check(const struct auth_method *method,
const char *auth_request,
const char *auth_request_end)
{
(void)method;
uint32_t scramble_len;
if (mp_typeof(*auth_request) == MP_STR) {
scramble_len = mp_decode_strl(&auth_request);
} else if (mp_typeof(*auth_request) == MP_BIN) {
/*
* Scramble is not a character stream, so some codecs
* automatically pack it as MP_BIN.
*/
scramble_len = mp_decode_binl(&auth_request);
} else {
diag_set(ClientError, ER_INVALID_AUTH_REQUEST,
AUTH_CHAP_SHA1_NAME, "scramble must be string");
return -1;
}
assert(auth_request + scramble_len == auth_request_end);
(void)auth_request_end;
if (scramble_len != SCRAMBLE_SIZE) {
diag_set(ClientError, ER_INVALID_AUTH_REQUEST,
AUTH_CHAP_SHA1_NAME, "invalid scramble size");
return -1;
}
return 0;
}

/** auth_method::authenticator_new */
static struct authenticator *
auth_chap_sha1_authenticator_new(const struct auth_method *method,
const char *auth_data,
const char *auth_data_end)
{
if (mp_typeof(*auth_data) != MP_STR) {
diag_set(ClientError, ER_INVALID_AUTH_DATA,
AUTH_CHAP_SHA1_NAME, "scramble must be string");
return NULL;
}
uint32_t hash2_base64_len;
const char *hash2_base64 = mp_decode_str(&auth_data,
&hash2_base64_len);
assert(auth_data == auth_data_end);
(void)auth_data_end;
if (hash2_base64_len != SCRAMBLE_BASE64_SIZE) {
diag_set(ClientError, ER_INVALID_AUTH_DATA,
AUTH_CHAP_SHA1_NAME, "invalid scramble size");
return NULL;
}
struct auth_chap_sha1_authenticator *auth = xmalloc(sizeof(*auth));
auth->base.method = method;
int hash2_len = base64_decode(hash2_base64, hash2_base64_len,
auth->hash2, sizeof(auth->hash2));
assert(hash2_len == sizeof(auth->hash2));
(void)hash2_len;
return (struct authenticator *)auth;
}

/** auth_method::authenticator_delete */
static void
auth_chap_sha1_authenticator_delete(struct authenticator *auth_)
{
struct auth_chap_sha1_authenticator *auth =
(struct auth_chap_sha1_authenticator *)auth_;
TRASH(auth);
free(auth);
}

/** auth_method::authenticator_check_request */
static bool
auth_chap_sha1_authenticate_request(const struct authenticator *auth_,
const char *salt,
const char *auth_request,
const char *auth_request_end)
{
const struct auth_chap_sha1_authenticator *auth =
(const struct auth_chap_sha1_authenticator *)auth_;
uint32_t scramble_len;
const char *scramble;
if (mp_typeof(*auth_request) == MP_STR) {
scramble = mp_decode_str(&auth_request, &scramble_len);
} else if (mp_typeof(*auth_request) == MP_BIN) {
scramble = mp_decode_bin(&auth_request, &scramble_len);
} else {
unreachable();
}
assert(auth_request == auth_request_end);
(void)auth_request_end;
assert(scramble_len == SCRAMBLE_SIZE);
(void)scramble_len;
return scramble_check(scramble, salt, auth->hash2) == 0;
}

struct auth_method *
auth_chap_sha1_new(void)
{
struct auth_method *method = xmalloc(sizeof(*method));
method->name = AUTH_CHAP_SHA1_NAME;
method->auth_method_delete = auth_chap_sha1_delete;
method->auth_data_prepare = auth_chap_sha1_data_prepare;
method->auth_request_prepare = auth_chap_sha1_request_prepare;
method->auth_request_check = auth_chap_sha1_request_check;
method->authenticator_new = auth_chap_sha1_authenticator_new;
method->authenticator_delete = auth_chap_sha1_authenticator_delete;
method->authenticate_request = auth_chap_sha1_authenticate_request;
return method;
}
23 changes: 23 additions & 0 deletions src/box/auth_chap_sha1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright 2010-2022, Tarantool AUTHORS, please see AUTHORS file.
*/
#pragma once

#if defined(__cplusplus)
extern "C" {
#endif /* defined(__cplusplus) */

struct auth_method;

/**
* Allocates and initializes 'chap-sha1' authentication method.
* This function never fails.
*/
struct auth_method *
auth_chap_sha1_new(void);

#if defined(__cplusplus)
} /* extern "C" */
#endif /* defined(__cplusplus) */

0 comments on commit 1c9e1a3

Please sign in to comment.