]> mj.ucw.cz Git - subauth.git/commitdiff
Bits of support of temporary tokens
authorMartin Mares <mj@ucw.cz>
Sat, 22 Jul 2017 23:03:02 +0000 (01:03 +0200)
committerMartin Mares <mj@ucw.cz>
Sat, 22 Jul 2017 23:03:02 +0000 (01:03 +0200)
etc/subauthd
server/Makefile
server/auth.c
server/cmd.c
server/subauthd.c
server/subauthd.h
server/temp.c [new file with mode: 0644]

index d19eaea3920fa7883966927d648a634c5cad8f11..1209a1ab73a18a0033f4d83dcb5e11ae36c5ae81 100644 (file)
@@ -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 {
index fef0b415f2d6d6f4195b1406079903c076494333..b4b6446337825d42b1bbe57268c8c224487c99eb 100644 (file)
@@ -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)
index e92d5ad887c6ebdb961caadc2bb9365951fb40c5..833cc517c689e57da21138b78413bdda7a8e7a3f 100644 (file)
@@ -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();
index 5533ddc92e677bad4572d39c2e18a19644d88afb..207c31356f14a97007f0af22734b0717622941b0 100644 (file)
@@ -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=<temp>", 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=<temp>", 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=<temp>", login, az->name);
+      goto reject;
+    }
+
+  msg(L_INFO, "Login successful: user=<%s> zone=<%s> type=<temp>", 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 },
index 0654685e78a93aff2429e7edeff7d77e281edba4..9cf7c714141dca322e07b12298f8660ca09cdf0d 100644 (file)
@@ -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();
 
index 4fb665039c5beea8e195a057fa0fba3677a7acbe..f676b5edcc8ec400bd54de21fbd3700479867c7b 100644 (file)
@@ -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 (file)
index 0000000..bd8184e
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ *     Sub-authentication Daemon: Temporary Tokens
+ *
+ *     (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 <gcrypt.h>
+#include <time.h>
+#include <unistd.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
+
+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);
+    }
+}