]> mj.ucw.cz Git - subauth.git/blob - server/temp.c
Bits of support of temporary tokens
[subauth.git] / server / temp.c
1 /*
2  *      Sub-authentication Daemon: Temporary Tokens
3  *
4  *      (c) 2017 Martin Mares <mj@ucw.cz>
5  */
6
7 // FIXME
8 #include <ucw/lib.h>
9 #include <ucw/base64.h>
10 #include <ucw/gary.h>
11 #include <ucw/stkstring.h>
12 #include <ucw/string.h>
13 #include <ucw/unaligned.h>
14
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <gcrypt.h>
18 #include <time.h>
19 #include <unistd.h>
20
21 #include "subauthd.h"
22
23 // FIXME: Re-defined to handle const strings properly
24 #undef stk_strmulticat
25 #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; })
26
27 #define TEMP_KEY_SIZE 32
28 #define TEMP_NONCE_SIZE 16
29 #define TEMP_SIGN_SIZE 16               // SHA2-256 HMAC
30
31 static byte temp_key[TEMP_KEY_SIZE];
32
33 static void temp_sign(byte *sig_text, char *raw_token)
34 {
35   // Sign the nonce with the temp key using SHA2-256 HMAC
36   gcry_md_hd_t md;
37   if (gcry_md_open(&md, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
38     die("gcry_md_open(CGRY_MD_SHA256) failed");
39   if (gcry_md_setkey(md, temp_key, TEMP_KEY_SIZE) != GPG_ERR_NO_ERROR)
40     die("gcry_md_setkey() failed");
41
42   gcry_md_write(md, raw_token, strlen(raw_token));
43
44   uint sig_len = base64_encode(sig_text, gcry_md_read(md, 0), TEMP_SIGN_SIZE);
45   sig_text[sig_len] = 0;
46
47   gcry_md_close(md);
48 }
49
50 char *temp_generate(const char *zone, const char *login, uint validity)
51 {
52   byte nonce[TEMP_NONCE_SIZE];
53   gcry_randomize(nonce, TEMP_NONCE_SIZE, GCRY_STRONG_RANDOM);
54
55   char nonce_text[2*TEMP_NONCE_SIZE + 1];
56   uint nonce_len = base64_encode(nonce_text, nonce, TEMP_NONCE_SIZE);
57   nonce_text[nonce_len] = 0;
58
59   unsigned long long expire = time(NULL) + validity;
60   char expire_text[32];
61   int expire_len = snprintf(expire_text, sizeof(expire_text), "%lld", expire);
62   ASSERT(expire_len < (int) sizeof(expire_text));
63
64   char *raw_token = stk_strmulticat((char *) zone, "-", login, "-", nonce_text, "-", expire_text, NULL);
65
66   char sig_text[2*TEMP_SIGN_SIZE + 1];
67   temp_sign(sig_text, raw_token);
68
69   return xstrdup(stk_strmulticat("t-", nonce_text, "-", expire_text, "-", sig_text, NULL));
70 }
71
72 const char *temp_check(const char *zone, const char *login, const char *token)
73 {
74   // Split and check format
75   // FIXME: Refuse tokens which are too long
76   char *fields[4];
77   if (str_sepsplit(stk_strdup(token), '-', fields, 4) != 4)
78     return "Wrong format";
79   if (!strcmp(fields[0], "t"))
80     return "Wrong format";
81
82   // Check signature
83   char *raw_token = stk_strmulticat(zone, "-", login, "-", fields[1], "-", fields[2], NULL);
84   char sig_text[2*TEMP_SIGN_SIZE + 1];
85   temp_sign(sig_text, raw_token);
86   if (strcmp(sig_text, fields[3]))
87     return "Wrong signature";
88
89   // Check expiration
90   unsigned long long expire;
91   if (sscanf(fields[2], "%lld", &expire) != 1)
92     return "Wrong format";
93   if (expire < (unsigned long long) time(NULL))
94     return "Expired";
95
96   // OK
97   return NULL;
98 }
99
100 void temp_init(void)
101 {
102   if (temp_key_file)
103     {
104       int fd = open(temp_key_file, O_RDONLY);
105       if (fd >= 0)
106         {
107           int n = read(fd, temp_key, TEMP_KEY_SIZE);
108           if (n != TEMP_KEY_SIZE)
109             die("Cannot read temporary token key from %s", temp_key_file);
110           close(fd);
111           msg(L_INFO, "Loaded temporary token key from %s", temp_key_file);
112           return;
113         }
114     }
115
116   gcry_randomize(temp_key_file, TEMP_KEY_SIZE, GCRY_STRONG_RANDOM);
117   msg(L_INFO, "Generated new temporary token key");
118
119   if (temp_key_file)
120     {
121       int fd = open(temp_key_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
122       if (fd < 0)
123         die("Cannot create %s: %m", temp_key_file);
124       int n = write(fd, temp_key, TEMP_KEY_SIZE);
125       if (n != TEMP_KEY_SIZE)
126         die("Cannot write temporary token key");
127       close(fd);
128     }
129 }