Also replaced stack-based allocation by mempools.
#include <ucw/lib.h>
#include <ucw/log.h>
#include <ucw/opt.h>
+#include <ucw/strtonum.h>
#include <ucw-json/json.h>
#include <errno.h>
static char *arg_ident;
static char *arg_comment;
static int debug;
+static char *arg_expire;
/*** Communication with the daemon ***/
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);
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("");
CMD_LIST,
CMD_ZONES,
CMD_LOGIN,
+ CMD_TEMP_TOKEN,
CMD_RAW,
CMD_MAX
};
[CMD_LIST] = cmd_list,
[CMD_ZONES] = cmd_zones,
[CMD_LOGIN] = cmd_login,
+ [CMD_TEMP_TOKEN] = cmd_temp_token,
[CMD_RAW] = cmd_raw,
};
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"),
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
}
};
AutoCreateAcct 1
AllowPasswd 1
AllowTokens 16
- MaxTempValidity 20
+ MaxTempValidity 3600
}
Zone {
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
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++ = '-';
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);
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)
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);
}
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=<temp>", reason, login, az->name);
+ msg(L_INFO, "Login failed: %s user=<%s> zone=<%s> temp=<%s>", reason, login, az->name, shortened);
goto reject;
}
struct auth_user *au = auth_find_user(login, 0);
if (!au)
{
- msg(L_INFO, "Login failed: No user=<%s> type=<temp>", 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=<temp>", 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=<temp>", 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");
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--;
}
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;
#include <ucw/clists.h>
#include <ucw/mainloop.h>
+#include <ucw/mempool.h>
#include <ucw-json/json.h>
#define SOCKET_TIMEOUT 60000 // in ms
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;
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;
/* 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);
* (c) 2017 Martin Mares <mj@ucw.cz>
*/
-// FIXME
#include <ucw/lib.h>
#include <ucw/base64.h>
-#include <ucw/gary.h>
-#include <ucw/stkstring.h>
#include <ucw/string.h>
-#include <ucw/unaligned.h>
#include <errno.h>
#include <fcntl.h>
#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
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;
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)