From 447e53eca94b874eef747f8cb7affc4ee64ec108 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Sun, 23 Jul 2017 01:03:02 +0200 Subject: [PATCH] Bits of support of temporary tokens --- etc/subauthd | 2 + server/Makefile | 2 +- server/auth.c | 4 +- server/cmd.c | 70 +++++++++++++++++++++++++ server/subauthd.c | 4 ++ server/subauthd.h | 8 +++ server/temp.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 server/temp.c diff --git a/etc/subauthd b/etc/subauthd index d19eaea..1209a1a 100644 --- a/etc/subauthd +++ b/etc/subauthd @@ -2,6 +2,7 @@ SubauthD { SocketPath @INSTALL_RUN_DIR@/subauthd.socket MaxConnections 1000 Database @INSTALL_STATE_DIR@/subauthd.db + TempKeyFile @INSTALL_STATE_DIR@/subauthd-seed Zone { Name mail @@ -9,6 +10,7 @@ SubauthD { AutoCreateAcct 1 AllowPasswd 1 AllowTokens 16 + MaxTempValidity 20 } Zone { diff --git a/server/Makefile b/server/Makefile index fef0b41..b4b6446 100644 --- a/server/Makefile +++ b/server/Makefile @@ -3,6 +3,6 @@ DIRS+=server PROGS+=$(o)/server/subauthd CONFIGS+=subauthd -$(o)/server/subauthd: $(addprefix $(o)/server/, subauthd.o cmd.o auth.o) +$(o)/server/subauthd: $(addprefix $(o)/server/, subauthd.o cmd.o auth.o temp.o) $(o)/server/subauthd: LIBS+=$(LIBGCRYPT_LIBS) $(o)/server/auth.o: CFLAGS+=$(LIBGCRYPT_CFLAGS) diff --git a/server/auth.c b/server/auth.c index e92d5ad..833cc51 100644 --- a/server/auth.c +++ b/server/auth.c @@ -411,8 +411,8 @@ void auth_init(void) { if (!gcry_check_version(GCRYPT_VERSION)) die("libgcrypt version mismatch"); - gcry_control (GCRYCTL_DISABLE_SECMEM, 0); - gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); + gcry_control(GCRYCTL_DISABLE_SECMEM, 0); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); user_hash_init(); db_read(); diff --git a/server/cmd.c b/server/cmd.c index 5533ddc..207c313 100644 --- a/server/cmd.c +++ b/server/cmd.c @@ -294,12 +294,78 @@ static void cmd_delete_token(struct client *c) cmd_ok(c); } +static void cmd_create_temp(struct client *c) +{ + struct auth_acct *aa = cmd_need_target_acct(c); + + uint validity; + if (get_uint(c->request, "validity", &validity)) + cmd_error(c, "Validity must be given"); + + if (!aa->zone->max_temp_validity) + cmd_error(c, "This zone does not allow temporary tokens"); + + if (validity > aa->zone->max_temp_validity) + cmd_error(c, "This zone limits temporary token validity to %d seconds", aa->zone->max_temp_validity); + + char *tok = temp_generate(aa->zone->name, aa->user->login, validity); + set_string(c, c->reply, "token", tok); + xfree(tok); + + msg(L_INFO, "Created temp token: login=<%s> zone=<%s> validity=%u", aa->user->login, aa->zone->name, validity); + + cmd_ok(c); +} + static void cmd_login_fake(struct client *c, const char *passwd) { auth_check_token(auth_fake_token, passwd); cmd_error(c, "Invalid password"); } +static void cmd_login_by_temp(struct client *c, struct auth_zone *az, const char *given_passwd) +{ + const char *login = cmd_need_string(c, "login"); + + const char *reason = temp_check(az->name, login, given_passwd); + if (reason) + { + msg(L_INFO, "Login failed: %s user=<%s> zone=<%s> type=", reason, login, az->name); + goto reject; + } + + /* + * The following checks test for improbable things like user + * disappearing since the token has been issued. + */ + + if (!az->max_temp_validity) + { + msg(L_INFO, "Login failed: Temporary tokens no longer accepted for zone=<%s>", az->name); + goto reject; + } + + struct auth_user *au = auth_find_user(login, 0); + if (!au) + { + msg(L_INFO, "Login failed: No user=<%s> type=", login); + goto reject; + } + + struct auth_acct *aa = auth_find_acct(au, az, 0); + if (!aa) + { + msg(L_INFO, "Login failed: No account user=<%s> zone=<%s> type=", login, az->name); + goto reject; + } + + msg(L_INFO, "Login successful: user=<%s> zone=<%s> type=", login, az->name); + cmd_ok(c); + +reject: + cmd_error(c, "Temporary token refused"); +} + static void cmd_login(struct client *c) { struct auth_zone *az = cmd_need_zone(c); @@ -322,6 +388,9 @@ static void cmd_login(struct client *c) passwd = passbuf; } + if (ident && !strcmp(ident, "t")) + return cmd_login_by_temp(c, az, given_passwd); + const char *login = cmd_need_string(c, "login"); struct auth_user *au = auth_find_user(login, 0); if (!au) @@ -439,6 +508,7 @@ static const struct command command_table[] = { { "delete-token", cmd_delete_token }, { "set-passwd", cmd_set_passwd }, { "delete-passwd", cmd_delete_passwd }, + { "create-temp", cmd_create_temp }, { "login", cmd_login }, { "list-accts", cmd_list_accts }, { "list-zones", cmd_list_zones }, diff --git a/server/subauthd.c b/server/subauthd.c index 0654685..9cf7c71 100644 --- a/server/subauthd.c +++ b/server/subauthd.c @@ -26,6 +26,7 @@ static char *socket_path = "subauthd.socket"; static uint max_connections = ~0U; clist zone_list; char *database_name = "subauthd.db"; +char *temp_key_file; static struct main_file listen_socket; static uint num_connections; @@ -294,6 +295,7 @@ static struct cf_section zone_config = { CF_UINT("AutoCreateAcct", PTR_TO(struct auth_zone, auto_create_acct)), CF_UINT("AllowPasswd", PTR_TO(struct auth_zone, allow_passwd)), CF_UINT("AllowTokens", PTR_TO(struct auth_zone, allow_tokens)), + CF_UINT("MaxTempValidity", PTR_TO(struct auth_zone, max_temp_validity)), CF_END } }; @@ -304,6 +306,7 @@ static struct cf_section daemon_config = { CF_UINT("MaxConnections", &max_connections), CF_LIST("Zone", &zone_list, &zone_config), CF_STRING("Database", &database_name), + CF_STRING("TempKeyFile", &temp_key_file), CF_END } }; @@ -329,6 +332,7 @@ int main(int argc UNUSED, char **argv) opt_parse(&options, argv+1); auth_init(); + temp_init(); main_init(); init_socket(); diff --git a/server/subauthd.h b/server/subauthd.h index 4fb6650..f676b5e 100644 --- a/server/subauthd.h +++ b/server/subauthd.h @@ -27,6 +27,7 @@ struct client { extern clist zone_list; // of struct auth_zone extern char *database_name; +extern char *temp_key_file; /* cmd.c */ @@ -53,6 +54,7 @@ struct auth_zone { uint auto_create_acct; uint allow_passwd; uint allow_tokens; + uint max_temp_validity; }; struct auth_user { @@ -102,3 +104,9 @@ char *auth_set_token_generated(struct auth_token *at, const char *comment); bool auth_check_token(struct auth_token *at, const char *passwd); extern struct auth_token *auth_fake_token; + +/* temp.c */ + +void temp_init(void); +char *temp_generate(const char *zone, const char *login, uint validity); +const char *temp_check(const char *zone, const char *login, const char *token); diff --git a/server/temp.c b/server/temp.c new file mode 100644 index 0000000..bd8184e --- /dev/null +++ b/server/temp.c @@ -0,0 +1,129 @@ +/* + * Sub-authentication Daemon: Temporary Tokens + * + * (c) 2017 Martin Mares + */ + +// FIXME +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "subauthd.h" + +// FIXME: Re-defined to handle const strings properly +#undef stk_strmulticat +#define stk_strmulticat(s...) ({ const char *_s[]={s}; char *_x=alloca(stk_array_len((char **)_s, ARRAY_SIZE(_s)-1)); stk_array_join(_x, (char **)_s, ARRAY_SIZE(_s)-1, 0); _x; }) + +#define TEMP_KEY_SIZE 32 +#define TEMP_NONCE_SIZE 16 +#define TEMP_SIGN_SIZE 16 // SHA2-256 HMAC + +static byte temp_key[TEMP_KEY_SIZE]; + +static void temp_sign(byte *sig_text, char *raw_token) +{ + // Sign the nonce with the temp key using SHA2-256 HMAC + gcry_md_hd_t md; + if (gcry_md_open(&md, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR) + die("gcry_md_open(CGRY_MD_SHA256) failed"); + if (gcry_md_setkey(md, temp_key, TEMP_KEY_SIZE) != GPG_ERR_NO_ERROR) + die("gcry_md_setkey() failed"); + + gcry_md_write(md, raw_token, strlen(raw_token)); + + uint sig_len = base64_encode(sig_text, gcry_md_read(md, 0), TEMP_SIGN_SIZE); + sig_text[sig_len] = 0; + + gcry_md_close(md); +} + +char *temp_generate(const char *zone, const char *login, uint validity) +{ + byte nonce[TEMP_NONCE_SIZE]; + gcry_randomize(nonce, TEMP_NONCE_SIZE, GCRY_STRONG_RANDOM); + + char nonce_text[2*TEMP_NONCE_SIZE + 1]; + uint nonce_len = base64_encode(nonce_text, nonce, TEMP_NONCE_SIZE); + nonce_text[nonce_len] = 0; + + unsigned long long expire = time(NULL) + validity; + char expire_text[32]; + int expire_len = snprintf(expire_text, sizeof(expire_text), "%lld", expire); + ASSERT(expire_len < (int) sizeof(expire_text)); + + char *raw_token = stk_strmulticat((char *) zone, "-", login, "-", nonce_text, "-", expire_text, NULL); + + char sig_text[2*TEMP_SIGN_SIZE + 1]; + temp_sign(sig_text, raw_token); + + return xstrdup(stk_strmulticat("t-", nonce_text, "-", expire_text, "-", sig_text, NULL)); +} + +const char *temp_check(const char *zone, const char *login, const char *token) +{ + // Split and check format + // FIXME: Refuse tokens which are too long + char *fields[4]; + if (str_sepsplit(stk_strdup(token), '-', fields, 4) != 4) + return "Wrong format"; + if (!strcmp(fields[0], "t")) + return "Wrong format"; + + // Check signature + char *raw_token = stk_strmulticat(zone, "-", login, "-", fields[1], "-", fields[2], NULL); + char sig_text[2*TEMP_SIGN_SIZE + 1]; + temp_sign(sig_text, raw_token); + if (strcmp(sig_text, fields[3])) + return "Wrong signature"; + + // Check expiration + unsigned long long expire; + if (sscanf(fields[2], "%lld", &expire) != 1) + return "Wrong format"; + if (expire < (unsigned long long) time(NULL)) + return "Expired"; + + // OK + return NULL; +} + +void temp_init(void) +{ + if (temp_key_file) + { + int fd = open(temp_key_file, O_RDONLY); + if (fd >= 0) + { + int n = read(fd, temp_key, TEMP_KEY_SIZE); + if (n != TEMP_KEY_SIZE) + die("Cannot read temporary token key from %s", temp_key_file); + close(fd); + msg(L_INFO, "Loaded temporary token key from %s", temp_key_file); + return; + } + } + + gcry_randomize(temp_key_file, TEMP_KEY_SIZE, GCRY_STRONG_RANDOM); + msg(L_INFO, "Generated new temporary token key"); + + if (temp_key_file) + { + int fd = open(temp_key_file, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) + die("Cannot create %s: %m", temp_key_file); + int n = write(fd, temp_key, TEMP_KEY_SIZE); + if (n != TEMP_KEY_SIZE) + die("Cannot write temporary token key"); + close(fd); + } +} -- 2.39.2