1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #elif defined(_MSC_VER)
4 #include "config-msvc.h"
5 #endif
6 
7 #include "syshead.h"
8 
9 #include "base64.h"
10 #include "buffer.h"
11 #include "crypto.h"
12 #include "openvpn.h"
13 #include "ssl_common.h"
14 #include "auth_token.h"
15 #include "push.h"
16 #include "integer.h"
17 #include "ssl.h"
18 #include "ssl_verify.h"
19 #include <inttypes.h>
20 
21 const char *auth_token_pem_name = "OpenVPN auth-token server key";
22 
23 #define AUTH_TOKEN_SESSION_ID_LEN 12
24 #if AUTH_TOKEN_SESSION_ID_LEN % 3
25 #error AUTH_TOKEN_SESSION_ID_LEN needs to be multiple a 3
26 #endif
27 
28 /* Size of the data of the token (not b64 encoded and without prefix) */
29 #define TOKEN_DATA_LEN (2 * sizeof(int64_t) + AUTH_TOKEN_SESSION_ID_LEN + 32)
30 
31 static struct key_type
auth_token_kt(void)32 auth_token_kt(void)
33 {
34     struct key_type kt = { 0 };
35     /* We do not encrypt our session tokens */
36     kt.cipher = NULL;
37     kt.digest = md_kt_get("SHA256");
38 
39     if (!kt.digest)
40     {
41         msg(M_WARN, "ERROR: --tls-crypt requires HMAC-SHA-256 support.");
42         return (struct key_type) { 0 };
43     }
44 
45     kt.hmac_length = md_kt_size(kt.digest);
46 
47     return kt;
48 }
49 
50 
51 void
add_session_token_env(struct tls_session * session,struct tls_multi * multi,const struct user_pass * up)52 add_session_token_env(struct tls_session *session, struct tls_multi *multi,
53                       const struct user_pass *up)
54 {
55     if (!multi->opt.auth_token_generate)
56     {
57         return;
58     }
59 
60 
61     const char *state;
62 
63     if (!is_auth_token(up->password))
64     {
65         state = "Initial";
66     }
67     else if (multi->auth_token_state_flags & AUTH_TOKEN_HMAC_OK)
68     {
69         switch (multi->auth_token_state_flags & (AUTH_TOKEN_VALID_EMPTYUSER|AUTH_TOKEN_EXPIRED))
70         {
71             case 0:
72                 state = "Authenticated";
73                 break;
74 
75             case AUTH_TOKEN_EXPIRED:
76                 state = "Expired";
77                 break;
78 
79             case AUTH_TOKEN_VALID_EMPTYUSER:
80                 state = "AuthenticatedEmptyUser";
81                 break;
82 
83             case AUTH_TOKEN_VALID_EMPTYUSER | AUTH_TOKEN_EXPIRED:
84                 state = "ExpiredEmptyUser";
85                 break;
86 
87             default:
88                 /* Silence compiler warning, all four possible combinations are covered */
89                 ASSERT(0);
90         }
91     }
92     else
93     {
94         state = "Invalid";
95     }
96 
97     setenv_str(session->opt->es, "session_state", state);
98 
99     /* We had a valid session id before */
100     const char *session_id_source;
101     if (multi->auth_token_state_flags & AUTH_TOKEN_HMAC_OK
102         && !(multi->auth_token_state_flags & AUTH_TOKEN_EXPIRED))
103     {
104         session_id_source = up->password;
105     }
106     else
107     {
108         /*
109          * No session before, generate a new session token for the new session
110          */
111         if (!multi->auth_token)
112         {
113             generate_auth_token(up, multi);
114         }
115         session_id_source = multi->auth_token;
116     }
117     /*
118      * In the auth-token the auth token is already base64 encoded
119      * and being a multiple of 4 ensure that it a multiple of bytes
120      * in the encoding
121      */
122 
123     char session_id[AUTH_TOKEN_SESSION_ID_LEN*2] = {0};
124     memcpy(session_id, session_id_source + strlen(SESSION_ID_PREFIX),
125            AUTH_TOKEN_SESSION_ID_LEN*8/6);
126 
127     setenv_str(session->opt->es, "session_id", session_id);
128 }
129 
130 void
auth_token_write_server_key_file(const char * filename)131 auth_token_write_server_key_file(const char *filename)
132 {
133     write_pem_key_file(filename, auth_token_pem_name);
134 }
135 
136 void
auth_token_init_secret(struct key_ctx * key_ctx,const char * key_file,bool key_inline)137 auth_token_init_secret(struct key_ctx *key_ctx, const char *key_file,
138                        bool key_inline)
139 {
140     struct key_type kt = auth_token_kt();
141 
142     struct buffer server_secret_key = alloc_buf(2048);
143 
144     bool key_loaded = false;
145     if (key_file)
146     {
147         key_loaded = read_pem_key_file(&server_secret_key,
148                                        auth_token_pem_name,
149                                        key_file, key_inline);
150     }
151     else
152     {
153         key_loaded = generate_ephemeral_key(&server_secret_key,
154                                             auth_token_pem_name);
155     }
156 
157     if (!key_loaded)
158     {
159         msg(M_FATAL, "ERROR: Cannot load auth-token secret");
160     }
161 
162     struct key key;
163 
164     if (!buf_read(&server_secret_key, &key, sizeof(key)))
165     {
166         msg(M_FATAL, "ERROR: not enough data in auth-token secret");
167     }
168     init_key_ctx(key_ctx, &key, &kt, false, "auth-token secret");
169 
170     free_buf(&server_secret_key);
171 }
172 
173 void
generate_auth_token(const struct user_pass * up,struct tls_multi * multi)174 generate_auth_token(const struct user_pass *up, struct tls_multi *multi)
175 {
176     struct gc_arena gc = gc_new();
177 
178     int64_t timestamp = htonll((uint64_t)now);
179     int64_t initial_timestamp = timestamp;
180 
181     hmac_ctx_t *ctx = multi->opt.auth_token_key.hmac;
182     ASSERT(hmac_ctx_size(ctx) == 256/8);
183 
184     uint8_t sessid[AUTH_TOKEN_SESSION_ID_LEN];
185 
186     if (multi->auth_token)
187     {
188         /* Just enough space to fit 8 bytes+ 1 extra to decode a non padded
189          * base64 string (multiple of 3 bytes). 9 bytes => 12 bytes base64
190          * bytes
191          */
192         char old_tstamp_decode[9];
193 
194         /*
195          * reuse the same session id and timestamp and null terminate it at
196          * for base64 decode it only decodes the session id part of it
197          */
198         char *old_sessid = multi->auth_token + strlen(SESSION_ID_PREFIX);
199         char *old_tsamp_initial = old_sessid + AUTH_TOKEN_SESSION_ID_LEN*8/6;
200 
201         old_tsamp_initial[12] = '\0';
202         ASSERT(openvpn_base64_decode(old_tsamp_initial, old_tstamp_decode, 9) == 9);
203 
204         /*
205          * Avoid old gcc (4.8.x) complaining about strict aliasing
206          * by using a temporary variable instead of doing it in one
207          * line
208          */
209         uint64_t *tstamp_ptr = (uint64_t *) old_tstamp_decode;
210         initial_timestamp = *tstamp_ptr;
211 
212         old_tsamp_initial[0] = '\0';
213         ASSERT(openvpn_base64_decode(old_sessid, sessid, AUTH_TOKEN_SESSION_ID_LEN)==AUTH_TOKEN_SESSION_ID_LEN);
214 
215 
216         /* free the auth-token, we will replace it with a new one */
217         free(multi->auth_token);
218     }
219     else if (!rand_bytes(sessid, AUTH_TOKEN_SESSION_ID_LEN))
220     {
221         msg( M_FATAL, "Failed to get enough randomness for "
222              "authentication token");
223     }
224 
225     /* Calculate the HMAC */
226     /* We enforce up->username to be \0 terminated in ssl.c.. Allowing username
227      * with \0 in them is asking for troubles in so many ways anyway that we
228      * ignore that corner case here
229      */
230     uint8_t hmac_output[256/8];
231 
232     hmac_ctx_reset(ctx);
233 
234     /*
235      * If the token was only valid for the empty user, also generate
236      * a new token with the empty username since we do not want to loose
237      * the information that the username cannot be trusted
238      */
239     if (multi->auth_token_state_flags & AUTH_TOKEN_VALID_EMPTYUSER)
240     {
241         hmac_ctx_update(ctx, (const uint8_t *) "", 0);
242     }
243     else
244     {
245         hmac_ctx_update(ctx, (uint8_t *) up->username, (int) strlen(up->username));
246     }
247     hmac_ctx_update(ctx, sessid, AUTH_TOKEN_SESSION_ID_LEN);
248     hmac_ctx_update(ctx, (uint8_t *) &initial_timestamp, sizeof(initial_timestamp));
249     hmac_ctx_update(ctx, (uint8_t *) &timestamp, sizeof(timestamp));
250     hmac_ctx_final(ctx, hmac_output);
251 
252     /* Construct the unencoded session token */
253     struct buffer token = alloc_buf_gc(
254         2*sizeof(uint64_t) + AUTH_TOKEN_SESSION_ID_LEN + 256/8, &gc);
255 
256     ASSERT(buf_write(&token, sessid, sizeof(sessid)));
257     ASSERT(buf_write(&token, &initial_timestamp, sizeof(initial_timestamp)));
258     ASSERT(buf_write(&token, &timestamp, sizeof(timestamp)));
259     ASSERT(buf_write(&token, hmac_output, sizeof(hmac_output)));
260 
261     char *b64output;
262     openvpn_base64_encode(BPTR(&token), BLEN(&token), &b64output);
263 
264     struct buffer session_token = alloc_buf_gc(
265         strlen(SESSION_ID_PREFIX) + strlen(b64output) + 1, &gc);
266 
267     ASSERT(buf_write(&session_token, SESSION_ID_PREFIX, strlen(SESSION_ID_PREFIX)));
268     ASSERT(buf_write(&session_token, b64output, (int)strlen(b64output)));
269     ASSERT(buf_write_u8(&session_token, 0));
270 
271     free(b64output);
272 
273     multi->auth_token = strdup((char *)BPTR(&session_token));
274 
275     dmsg(D_SHOW_KEYS, "Generated token for client: %s (%s)",
276          multi->auth_token, up->username);
277 
278     gc_free(&gc);
279 }
280 
281 
282 static bool
check_hmac_token(hmac_ctx_t * ctx,const uint8_t * b64decoded,const char * username)283 check_hmac_token(hmac_ctx_t *ctx, const uint8_t *b64decoded, const char *username)
284 {
285     ASSERT(hmac_ctx_size(ctx) == 256/8);
286 
287     uint8_t hmac_output[256/8];
288 
289     hmac_ctx_reset(ctx);
290     hmac_ctx_update(ctx, (uint8_t *) username, (int)strlen(username));
291     hmac_ctx_update(ctx, b64decoded, TOKEN_DATA_LEN - 256/8);
292     hmac_ctx_final(ctx, hmac_output);
293 
294     const uint8_t *hmac = b64decoded + TOKEN_DATA_LEN - 256/8;
295     return memcmp_constant_time(&hmac_output, hmac, 32) == 0;
296 }
297 
298 unsigned int
verify_auth_token(struct user_pass * up,struct tls_multi * multi,struct tls_session * session)299 verify_auth_token(struct user_pass *up, struct tls_multi *multi,
300                   struct tls_session *session)
301 {
302     /*
303      * Base64 is <= input and input is < USER_PASS_LEN, so using USER_PASS_LEN
304      * is safe here but a bit overkill
305      */
306     uint8_t b64decoded[USER_PASS_LEN];
307     int decoded_len = openvpn_base64_decode(up->password + strlen(SESSION_ID_PREFIX),
308                                             b64decoded, USER_PASS_LEN);
309 
310     /*
311      * Ensure that the decoded data is the size of the
312      * timestamp + hmac + session id
313      */
314     if (decoded_len != TOKEN_DATA_LEN)
315     {
316         msg(M_WARN, "ERROR: --auth-token wrong size (%d!=%d)",
317             decoded_len, (int) TOKEN_DATA_LEN);
318         return 0;
319     }
320 
321     unsigned int ret = 0;
322 
323     const uint8_t *sessid = b64decoded;
324     const uint8_t *tstamp_initial = sessid + AUTH_TOKEN_SESSION_ID_LEN;
325     const uint8_t *tstamp = tstamp_initial + sizeof(int64_t);
326 
327     uint64_t timestamp = ntohll(*((uint64_t *) (tstamp)));
328     uint64_t timestamp_initial = ntohll(*((uint64_t *) (tstamp_initial)));
329 
330     hmac_ctx_t *ctx = multi->opt.auth_token_key.hmac;
331     if (check_hmac_token(ctx, b64decoded, up->username))
332     {
333         ret |= AUTH_TOKEN_HMAC_OK;
334     }
335     else if (check_hmac_token(ctx, b64decoded, ""))
336     {
337         ret |= AUTH_TOKEN_HMAC_OK;
338         ret |= AUTH_TOKEN_VALID_EMPTYUSER;
339         /* overwrite the username of the client with the empty one */
340         strcpy(up->username, "");
341     }
342     else
343     {
344         msg(M_WARN, "--auth-token-gen: HMAC on token from client failed (%s)",
345             up->username);
346         return 0;
347     }
348 
349     /* Accept session tokens that not expired are in the acceptable range
350      * for renogiations */
351     bool in_renog_time = now >= timestamp
352                          && now < timestamp + 2 * session->opt->renegotiate_seconds;
353 
354     /* We could still have a client that does not update
355      * its auth-token, so also allow the initial auth-token */
356     bool initialtoken = multi->auth_token_initial
357                         && memcmp_constant_time(up->password, multi->auth_token_initial,
358                                                 strlen(multi->auth_token_initial)) == 0;
359 
360     if (!in_renog_time && !initialtoken)
361     {
362         ret |= AUTH_TOKEN_EXPIRED;
363     }
364 
365     /* Sanity check the initial timestamp */
366     if (timestamp < timestamp_initial)
367     {
368         msg(M_WARN, "Initial timestamp (%" PRIu64 " in token from client earlier than "
369             "current timestamp %" PRIu64 ". Broken/unsynchronised clock?",
370             timestamp_initial, timestamp);
371         ret |= AUTH_TOKEN_EXPIRED;
372     }
373 
374     if (multi->opt.auth_token_lifetime
375         && now > timestamp_initial + multi->opt.auth_token_lifetime)
376     {
377         ret |= AUTH_TOKEN_EXPIRED;
378     }
379 
380     if (ret & AUTH_TOKEN_EXPIRED)
381     {
382         /* Tell client that the session token is expired */
383         auth_set_client_reason(multi, "SESSION: token expired");
384         msg(M_INFO, "--auth-token-gen: auth-token from client expired");
385     }
386     return ret;
387 }
388 
389 void
wipe_auth_token(struct tls_multi * multi)390 wipe_auth_token(struct tls_multi *multi)
391 {
392     if (multi)
393     {
394         if (multi->auth_token)
395         {
396             secure_memzero(multi->auth_token, strlen(multi->auth_token));
397             free(multi->auth_token);
398         }
399         if (multi->auth_token_initial)
400         {
401             secure_memzero(multi->auth_token_initial,
402                            strlen(multi->auth_token_initial));
403             free(multi->auth_token_initial);
404         }
405         multi->auth_token = NULL;
406         multi->auth_token_initial = NULL;
407     }
408 }
409