From: Martin Mares Date: Sun, 23 Jul 2017 13:19:17 +0000 (+0200) Subject: Finished implementation of temporary tokens X-Git-Tag: v0.9~36 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=90a0f10d4f80f42a233ba674e51156eb1a8ae95b;p=subauth.git Finished implementation of temporary tokens Also replaced stack-based allocation by mempools. --- diff --git a/client/subauth.c b/client/subauth.c index 723ee4f..9c65297 100644 --- a/client/subauth.c +++ b/client/subauth.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,7 @@ static char *arg_zone; static char *arg_ident; static char *arg_comment; static int debug; +static char *arg_expire; /*** Communication with the daemon ***/ @@ -41,6 +43,12 @@ static void set_string(struct json_node *n, const char *key, const char *val) json_object_set(n, key, json_new_string(js, val)); } +static void set_uint(struct json_node *n, const char *key, uint val) +{ + if (val) + json_object_set(n, key, json_new_number(js, val)); +} + static struct json_node *get_child(struct json_node *obj, const char *key, uint type) { struct json_node *n = json_object_get(obj, key); @@ -307,6 +315,44 @@ static void cmd_login(void) printf("OK\n"); } +static uint parse_expire(void) +{ + if (!arg_expire) + arg_expire = "5m"; + + uint expire; + const char *next; + const char *err = str_to_uint(&expire, arg_expire, &next, 10); + if (err || !next[0] || next[1]) + opt_failure("Invalid syntax of --expire"); + + switch (next[0]) + { + case 'h': + return expire * 3600; + case 'm': + return expire * 60; + case 's': + return expire; + default: + opt_failure("--expire uses an unknown unit"); + } +} + +static void cmd_temp_token(void) +{ + if (!arg_zone) + opt_failure("--zone must be given"); + + op_new("create-temp"); + set_string(rq, "zone", arg_zone); + set_uint(rq, "validity", parse_expire()); + + op_run(); + struct json_node *jt = need_child(rp, "token", JSON_STRING); + printf("%s\n", jt->string); +} + static void cmd_raw(void) { op_new(""); @@ -329,6 +375,7 @@ enum command { CMD_LIST, CMD_ZONES, CMD_LOGIN, + CMD_TEMP_TOKEN, CMD_RAW, CMD_MAX }; @@ -344,6 +391,7 @@ void (* const command_handlers[CMD_MAX])(void) = { [CMD_LIST] = cmd_list, [CMD_ZONES] = cmd_zones, [CMD_LOGIN] = cmd_login, + [CMD_TEMP_TOKEN] = cmd_temp_token, [CMD_RAW] = cmd_raw, }; @@ -369,6 +417,7 @@ static const struct opt_section options = { OPT_SWITCH(0, "create-token", command, CMD_CREATE_TOKEN, OPT_SINGLE, "\tCreate a new token"), OPT_SWITCH(0, "delete-token", command, CMD_DELETE_TOKEN, OPT_SINGLE, "\tRemove an existing token"), OPT_SWITCH(0, "login", command, CMD_LOGIN, OPT_SINGLE, "\tTry to log in"), + OPT_SWITCH(0, "temp-token", command, CMD_TEMP_TOKEN, OPT_SINGLE, "\tCreate a temporary token"), OPT_HELP(""), OPT_HELP("Administrative commands:"), OPT_SWITCH(0, "create-acct", command, CMD_CREATE_ACCT, OPT_SINGLE, "\tCreate an account"), @@ -381,6 +430,7 @@ static const struct opt_section options = { OPT_STRING('z', "zone", arg_zone, OPT_REQUIRED_VALUE, "zone\tAuthentication zone"), OPT_STRING('i', "ident", arg_ident, OPT_REQUIRED_VALUE, "id\tToken ID ('*' for all tokens in zone)"), OPT_STRING('c', "comment", arg_comment, OPT_REQUIRED_VALUE, "string\tHuman-readable description of token"), + OPT_STRING('x', "expire", arg_expire, OPT_REQUIRED_VALUE, "n(h|m|s)\tSet expiration time of a temporary token (default: 5m)"), OPT_END } }; diff --git a/etc/subauthd b/etc/subauthd index 1209a1a..86676fa 100644 --- a/etc/subauthd +++ b/etc/subauthd @@ -10,7 +10,7 @@ SubauthD { AutoCreateAcct 1 AllowPasswd 1 AllowTokens 16 - MaxTempValidity 20 + MaxTempValidity 3600 } Zone { diff --git a/server/auth.c b/server/auth.c index 833cc51..de94eca 100644 --- a/server/auth.c +++ b/server/auth.c @@ -176,7 +176,7 @@ void auth_set_token_passwd(struct auth_token *at, const char *passwd) at->hash = xstrdup(hash); } -char *auth_set_token_generated(struct auth_token *at, const char *comment) +char *auth_set_token_generated(struct auth_token *at, const char *comment, struct mempool *pool) { char ident_text[2*DEFAULT_IDENT_BYTES + 1]; do @@ -196,7 +196,7 @@ char *auth_set_token_generated(struct auth_token *at, const char *comment) gcry_randomize(token_bytes, DEFAULT_GENERATED_BYTES, GCRY_STRONG_RANDOM); uint token_len = 2*DEFAULT_IDENT_BYTES + 1 + 2*DEFAULT_GENERATED_BYTES + 1; - char *token_text = xmalloc(token_len); + char *token_text = mp_alloc(pool, token_len); memcpy(token_text, ident_text, 2*DEFAULT_IDENT_BYTES); char *token_rhs = token_text + 2*DEFAULT_IDENT_BYTES; *token_rhs++ = '-'; diff --git a/server/cmd.c b/server/cmd.c index 207c313..28ec103 100644 --- a/server/cmd.c +++ b/server/cmd.c @@ -261,9 +261,8 @@ static void cmd_create_token(struct client *c) cmd_error(c, "Maximum number of tokens was reached"); struct auth_token *at = auth_create_token(aa); - char *tok = auth_set_token_generated(at, get_string(c->request, "comment")); + char *tok = auth_set_token_generated(at, get_string(c->request, "comment"), c->pool); set_string(c, c->reply, "token", tok); - xfree(tok); msg(L_INFO, "Created token: login=<%s> zone=<%s> id=<%s>", aa->user->login, aa->zone->name, at->ident); @@ -299,7 +298,7 @@ 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)) + if (!get_uint(c->request, "validity", &validity)) cmd_error(c, "Validity must be given"); if (!aa->zone->max_temp_validity) @@ -308,11 +307,12 @@ static void cmd_create_temp(struct client *c) 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); + char *tok = temp_generate(aa->zone->name, aa->user->login, validity, c->pool); 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); + const char *shortened = temp_shorten(tok, c->pool); + + msg(L_INFO, "Created temp token: login=<%s> zone=<%s> temp=<%s> validity=%u", aa->user->login, aa->zone->name, shortened, validity); cmd_ok(c); } @@ -326,11 +326,12 @@ static void cmd_login_fake(struct client *c, const char *passwd) 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 *shortened = temp_shorten(given_passwd, c->pool); - const char *reason = temp_check(az->name, login, given_passwd); + const char *reason = temp_check(az->name, login, given_passwd, c->pool); if (reason) { - msg(L_INFO, "Login failed: %s user=<%s> zone=<%s> type=", reason, login, az->name); + msg(L_INFO, "Login failed: %s user=<%s> zone=<%s> temp=<%s>", reason, login, az->name, shortened); goto reject; } @@ -348,19 +349,20 @@ static void cmd_login_by_temp(struct client *c, struct auth_zone *az, const char struct auth_user *au = auth_find_user(login, 0); if (!au) { - msg(L_INFO, "Login failed: No user=<%s> type=", login); + msg(L_INFO, "Login failed: No user=<%s> temp=<%s>", login, shortened); 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); + msg(L_INFO, "Login failed: No account user=<%s> zone=<%s> temp=<%s>", login, az->name, shortened); goto reject; } - msg(L_INFO, "Login successful: user=<%s> zone=<%s> type=", login, az->name); + msg(L_INFO, "Login successful: user=<%s> zone=<%s> temp=<%s>", login, az->name, shortened); cmd_ok(c); + return; reject: cmd_error(c, "Temporary token refused"); diff --git a/server/subauthd.c b/server/subauthd.c index 9cf7c71..b0026ec 100644 --- a/server/subauthd.c +++ b/server/subauthd.c @@ -44,7 +44,7 @@ static void client_close(struct client *c) timer_del(&c->timer); close(c->socket.fd); json_delete(c->json); - xfree(c); + mp_delete(c->pool); // This includes the connection structure num_connections--; } @@ -228,7 +228,9 @@ static int listen_read_handler(struct main_file *fi) if (fcntl(new_sk, F_SETFL, fcntl(new_sk, F_GETFL) | O_NONBLOCK) < 0) die("Cannot set O_NONBLOCK: %m"); - struct client *c = xmalloc_zero(sizeof(*c)); + struct mempool *mp = mp_new(4096); + struct client *c = mp_alloc_zero(mp, sizeof(*c)); + c->pool = mp; c->json = json_new(); c->socket.fd = new_sk; diff --git a/server/subauthd.h b/server/subauthd.h index f676b5e..facaef6 100644 --- a/server/subauthd.h +++ b/server/subauthd.h @@ -10,6 +10,7 @@ #include #include +#include #include #define SOCKET_TIMEOUT 60000 // in ms @@ -20,6 +21,7 @@ struct client { struct main_file socket; struct main_timer timer; int uid; + struct mempool *pool; struct json_context *json; struct json_node *request; struct json_node *reply; @@ -100,7 +102,7 @@ void auth_delete_acct(struct auth_acct *aa); void auth_delete_token(struct auth_token *at); struct auth_token *auth_create_token(struct auth_acct *aa); void auth_set_token_passwd(struct auth_token *at, const char *passwd); -char *auth_set_token_generated(struct auth_token *at, const char *comment); +char *auth_set_token_generated(struct auth_token *at, const char *comment, struct mempool *pool); bool auth_check_token(struct auth_token *at, const char *passwd); extern struct auth_token *auth_fake_token; @@ -108,5 +110,6 @@ 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); +char *temp_generate(const char *zone, const char *login, uint validity, struct mempool *pool); +const char *temp_check(const char *zone, const char *login, const char *token, struct mempool *pool); +const char *temp_shorten(const char *token, struct mempool *pool); diff --git a/server/temp.c b/server/temp.c index bd8184e..4742ee5 100644 --- a/server/temp.c +++ b/server/temp.c @@ -4,13 +4,9 @@ * (c) 2017 Martin Mares */ -// FIXME #include #include -#include -#include #include -#include #include #include @@ -20,10 +16,6 @@ #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 @@ -42,18 +34,26 @@ static void temp_sign(byte *sig_text, char *raw_token) gcry_md_write(md, raw_token, strlen(raw_token)); uint sig_len = base64_encode(sig_text, gcry_md_read(md, 0), TEMP_SIGN_SIZE); + // Since the length of the input is fixed, the number of padding characters + // at the end is also fixed. Hence we remove them for better readability + // of tokens. This is harmless as we never attempt to decode these strings. + while (sig_len > 0 && sig_text[sig_len-1] == '=') + sig_len--; sig_text[sig_len] = 0; gcry_md_close(md); } -char *temp_generate(const char *zone, const char *login, uint validity) +char *temp_generate(const char *zone, const char *login, uint validity, struct mempool *pool) { 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); + // See the note on base64 in temp_sign() + while (nonce_len > 0 && nonce_text[nonce_len-1] == '=') + nonce_len--; nonce_text[nonce_len] = 0; unsigned long long expire = time(NULL) + validity; @@ -61,42 +61,49 @@ char *temp_generate(const char *zone, const char *login, uint validity) 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 *raw_token = mp_multicat(pool, 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)); + return mp_multicat(pool, "t-", nonce_text, "-", expire_text, "-", sig_text, NULL); } -const char *temp_check(const char *zone, const char *login, const char *token) +const char *temp_check(const char *zone, const char *login, const char *token, struct mempool *pool) { // 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"; + if (str_sepsplit(mp_strdup(pool, token), '-', fields, 4) != 4) + return "Wrong token format"; + if (strcmp(fields[0], "t")) + return "Wrong token format"; // Check signature - char *raw_token = stk_strmulticat(zone, "-", login, "-", fields[1], "-", fields[2], NULL); + char *raw_token = mp_multicat(pool, 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"; + return "Wrong token signature"; // Check expiration unsigned long long expire; if (sscanf(fields[2], "%lld", &expire) != 1) - return "Wrong format"; + return "Wrong token format"; if (expire < (unsigned long long) time(NULL)) - return "Expired"; + return "Token expired"; // OK return NULL; } +const char *temp_shorten(const char *token, struct mempool *pool) +{ + char *fields[4]; + if (str_sepsplit(mp_strdup(pool, token), '-', fields, 4) != 4) + return "???"; + return mp_printf(pool, "%.8s", fields[1]); +} + void temp_init(void) { if (temp_key_file)