]> mj.ucw.cz Git - subauth.git/commitdiff
Finished implementation of temporary tokens
authorMartin Mares <mj@ucw.cz>
Sun, 23 Jul 2017 13:19:17 +0000 (15:19 +0200)
committerMartin Mares <mj@ucw.cz>
Sun, 23 Jul 2017 13:19:17 +0000 (15:19 +0200)
Also replaced stack-based allocation by mempools.

client/subauth.c
etc/subauthd
server/auth.c
server/cmd.c
server/subauthd.c
server/subauthd.h
server/temp.c

index 723ee4f3a055bc45bc93e6b0b3ae4733145fc103..9c65297da84303f4e9dc1da7b97e6f9b97523e98 100644 (file)
@@ -7,6 +7,7 @@
 #include <ucw/lib.h>
 #include <ucw/log.h>
 #include <ucw/opt.h>
+#include <ucw/strtonum.h>
 #include <ucw-json/json.h>
 
 #include <errno.h>
@@ -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
   }
 };
index 1209a1ab73a18a0033f4d83dcb5e11ae36c5ae81..86676fa649aaa88b40c1fc8e6bc5b1c71c8e2bde 100644 (file)
@@ -10,7 +10,7 @@ SubauthD {
                AutoCreateAcct  1
                AllowPasswd     1
                AllowTokens     16
-               MaxTempValidity 20
+               MaxTempValidity 3600
        }
 
        Zone {
index 833cc517c689e57da21138b78413bdda7a8e7a3f..de94eca669467a2f81250059a0b9111f910ccb06 100644 (file)
@@ -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++ = '-';
index 207c31356f14a97007f0af22734b0717622941b0..28ec103baf920f7bd3a089c526c40aec4167ff44 100644 (file)
@@ -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=<temp>", 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=<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");
index 9cf7c714141dca322e07b12298f8660ca09cdf0d..b0026ecabf0022fd8a2fe47878bcc9be384cb3f5 100644 (file)
@@ -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;
index f676b5edcc8ec400bd54de21fbd3700479867c7b..facaef65f2af5f247c05a44b8cda8d499c99bca4 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <ucw/clists.h>
 #include <ucw/mainloop.h>
+#include <ucw/mempool.h>
 #include <ucw-json/json.h>
 
 #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);
index bd8184e3bf8abdf2d265fe9eeae088df2ee04642..4742ee55f4ac9093461c14ae2b9f8cc37c7f5075 100644 (file)
@@ -4,13 +4,9 @@
  *     (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
@@ -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)