]> mj.ucw.cz Git - subauth.git/blob - server/temp.c
Debian packaging: init
[subauth.git] / server / temp.c
1 /*
2  *      Sub-authentication Daemon: Temporary Tokens
3  *
4  *      (c) 2017 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <ucw/lib.h>
8 #include <ucw/base64.h>
9 #include <ucw/string.h>
10
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <gcrypt.h>
14 #include <time.h>
15 #include <unistd.h>
16
17 #include "subauthd.h"
18
19 #define TEMP_KEY_SIZE 32
20 #define TEMP_NONCE_SIZE 16
21 #define TEMP_SIGN_SIZE 16               // SHA2-256 HMAC
22
23 static byte temp_key[TEMP_KEY_SIZE];
24
25 static void temp_sign(byte *sig_text, char *raw_token)
26 {
27   // Sign the nonce with the temp key using SHA2-256 HMAC
28   gcry_md_hd_t md;
29   if (gcry_md_open(&md, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
30     die("gcry_md_open(CGRY_MD_SHA256) failed");
31   if (gcry_md_setkey(md, temp_key, TEMP_KEY_SIZE) != GPG_ERR_NO_ERROR)
32     die("gcry_md_setkey() failed");
33
34   gcry_md_write(md, raw_token, strlen(raw_token));
35
36   uint sig_len = base64_encode(sig_text, gcry_md_read(md, 0), TEMP_SIGN_SIZE);
37   // Since the length of the input is fixed, the number of padding characters
38   // at the end is also fixed. Hence we remove them for better readability
39   // of tokens. This is harmless as we never attempt to decode these strings.
40   while (sig_len > 0 && sig_text[sig_len-1] == '=')
41     sig_len--;
42   sig_text[sig_len] = 0;
43
44   gcry_md_close(md);
45 }
46
47 char *temp_generate(const char *zone, const char *login, uint validity, struct mempool *pool)
48 {
49   byte nonce[TEMP_NONCE_SIZE];
50   gcry_randomize(nonce, TEMP_NONCE_SIZE, GCRY_STRONG_RANDOM);
51
52   char nonce_text[2*TEMP_NONCE_SIZE + 1];
53   uint nonce_len = base64_encode(nonce_text, nonce, TEMP_NONCE_SIZE);
54   // See the note on base64 in temp_sign()
55   while (nonce_len > 0 && nonce_text[nonce_len-1] == '=')
56     nonce_len--;
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 = mp_multicat(pool, 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 mp_multicat(pool, "t-", nonce_text, "-", expire_text, "-", sig_text, NULL);
70 }
71
72 const char *temp_check(const char *zone, const char *login, const char *token, struct mempool *pool)
73 {
74   // Split and check format
75   char *fields[4];
76   if (str_sepsplit(mp_strdup(pool, token), '-', fields, 4) != 4)
77     return "Wrong token format";
78   if (strcmp(fields[0], "t"))
79     return "Wrong token format";
80
81   // Check signature
82   char *raw_token = mp_multicat(pool, zone, "-", login, "-", fields[1], "-", fields[2], NULL);
83   char sig_text[2*TEMP_SIGN_SIZE + 1];
84   temp_sign(sig_text, raw_token);
85   if (strcmp(sig_text, fields[3]))
86     return "Wrong token signature";
87
88   // Check expiration
89   unsigned long long expire;
90   if (sscanf(fields[2], "%lld", &expire) != 1)
91     return "Wrong token format";
92   if (expire < (unsigned long long) time(NULL))
93     return "Token expired";
94
95   // OK
96   return NULL;
97 }
98
99 const char *temp_shorten(const char *token, struct mempool *pool)
100 {
101   char *fields[4];
102   if (str_sepsplit(mp_strdup(pool, token), '-', fields, 4) != 4)
103     return "???";
104   return mp_printf(pool, "%.8s", fields[1]);
105 }
106
107 void temp_init(void)
108 {
109   if (temp_key_file)
110     {
111       int fd = open(temp_key_file, O_RDONLY);
112       if (fd >= 0)
113         {
114           int n = read(fd, temp_key, TEMP_KEY_SIZE);
115           if (n != TEMP_KEY_SIZE)
116             die("Cannot read temporary token key from %s", temp_key_file);
117           close(fd);
118           msg(L_INFO, "Loaded temporary token key from %s", temp_key_file);
119           return;
120         }
121     }
122
123   gcry_randomize(temp_key, TEMP_KEY_SIZE, GCRY_STRONG_RANDOM);
124   msg(L_INFO, "Generated new temporary token key");
125
126   if (temp_key_file)
127     {
128       int fd = open(temp_key_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
129       if (fd < 0)
130         die("Cannot create %s: %m", temp_key_file);
131       int n = write(fd, temp_key, TEMP_KEY_SIZE);
132       if (n != TEMP_KEY_SIZE)
133         die("Cannot write temporary token key");
134       close(fd);
135     }
136 }