OpenVPN
auth_token.c
Go to the documentation of this file.
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5#include "syshead.h"
6
7#include "base64.h"
8#include "buffer.h"
9#include "crypto.h"
10#include "openvpn.h"
11#include "ssl_common.h"
12#include "auth_token.h"
13#include "push.h"
14#include "integer.h"
15#include "ssl.h"
16#include "ssl_verify.h"
17#include <inttypes.h>
18
19const char *auth_token_pem_name = "OpenVPN auth-token server key";
20
21#define AUTH_TOKEN_SESSION_ID_LEN 12
22#define AUTH_TOKEN_SESSION_ID_BASE64_LEN (AUTH_TOKEN_SESSION_ID_LEN * 8 / 6)
23
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
31static struct key_type
33{
34 return create_kt("none", "SHA256", "auth-gen-token");
35}
36
37void
39 const struct user_pass *up)
40{
41 if (!multi->opt.auth_token_generate)
42 {
43 return;
44 }
45
46 int auth_token_state_flags = session->key[KS_PRIMARY].auth_token_state_flags;
47
48 const char *state;
49
50 if (!is_auth_token(up->password))
51 {
52 state = "Initial";
53 }
54 else if (auth_token_state_flags & AUTH_TOKEN_HMAC_OK)
55 {
56 switch (auth_token_state_flags & (AUTH_TOKEN_VALID_EMPTYUSER|AUTH_TOKEN_EXPIRED))
57 {
58 case 0:
59 state = "Authenticated";
60 break;
61
63 state = "Expired";
64 break;
65
67 state = "AuthenticatedEmptyUser";
68 break;
69
71 state = "ExpiredEmptyUser";
72 break;
73
74 default:
75 /* Silence compiler warning, all four possible combinations are covered */
76 ASSERT(0);
77 }
78 }
79 else
80 {
81 state = "Invalid";
82 }
83
84 setenv_str(session->opt->es, "session_state", state);
85
86 /* We had a valid session id before */
87 const char *session_id_source;
88 if (auth_token_state_flags & AUTH_TOKEN_HMAC_OK
89 && !(auth_token_state_flags & AUTH_TOKEN_EXPIRED))
90 {
91 session_id_source = up->password;
92 }
93 else
94 {
95 /*
96 * No session before, generate a new session token for the new session
97 */
98 if (!multi->auth_token_initial)
99 {
100 generate_auth_token(up, multi);
101 }
102 session_id_source = multi->auth_token_initial;
103 }
104 /*
105 * In the auth-token the auth token is already base64 encoded
106 * and being a multiple of 4 ensure that it a multiple of bytes
107 * in the encoding
108 */
109
111 memcpy(session_id, session_id_source + strlen(SESSION_ID_PREFIX),
113
114 setenv_str(session->opt->es, "session_id", session_id);
115}
116
117void
119{
121}
122
123void
124auth_token_init_secret(struct key_ctx *key_ctx, const char *key_file,
125 bool key_inline)
126{
127 struct key_type kt = auth_token_kt();
128
129 struct buffer server_secret_key = alloc_buf(2048);
130
131 bool key_loaded = false;
132 if (key_file)
133 {
137 }
138 else
139 {
142 }
143
144 if (!key_loaded)
145 {
146 msg(M_FATAL, "ERROR: Cannot load auth-token secret");
147 }
148
149 struct key key;
150
151 if (!buf_read(&server_secret_key, &key, sizeof(key)))
152 {
153 msg(M_FATAL, "ERROR: not enough data in auth-token secret");
154 }
155
156 struct key_parameters key_params;
157 key_parameters_from_key(&key_params, &key);
158 init_key_ctx(key_ctx, &key_params, &kt, false, "auth-token secret");
159
160 free_buf(&server_secret_key);
161}
162
163void
164generate_auth_token(const struct user_pass *up, struct tls_multi *multi)
165{
166 struct gc_arena gc = gc_new();
167
168 int64_t timestamp = htonll((uint64_t)now);
169 int64_t initial_timestamp = timestamp;
170
171 hmac_ctx_t *ctx = multi->opt.auth_token_key.hmac;
172 ASSERT(hmac_ctx_size(ctx) == 256/8);
173
174 uint8_t sessid[AUTH_TOKEN_SESSION_ID_LEN];
175
176 if (multi->auth_token_initial)
177 {
178 /* Just enough space to fit 8 bytes+ 1 extra to decode a non-padded
179 * base64 string (multiple of 3 bytes). 9 bytes => 12 bytes base64
180 * bytes
181 */
182 char old_tstamp_decode[9];
183
184 /* Make a copy of the string to not modify multi->auth_token_initial */
185 char *initial_token_copy = string_alloc(multi->auth_token_initial, &gc);
186
187 char *old_sessid = initial_token_copy + strlen(SESSION_ID_PREFIX);
188 char *old_tstamp_initial = old_sessid + AUTH_TOKEN_SESSION_ID_LEN*8/6;
189
190 /*
191 * We null terminate the old token just after the session ID to let
192 * our base64 decode function only decode the session ID
193 */
194 old_tstamp_initial[12] = '\0';
195 ASSERT(openvpn_base64_decode(old_tstamp_initial, old_tstamp_decode, 9) == 9);
196
197 memcpy(&initial_timestamp, &old_tstamp_decode, sizeof(initial_timestamp));
198
199 old_tstamp_initial[0] = '\0';
201 }
202 else if (!rand_bytes(sessid, AUTH_TOKEN_SESSION_ID_LEN))
203 {
204 msg( M_FATAL, "Failed to get enough randomness for "
205 "authentication token");
206 }
207
208 /* Calculate the HMAC */
209 /* We enforce up->username to be \0 terminated in ssl.c.. Allowing username
210 * with \0 in them is asking for troubles in so many ways anyway that we
211 * ignore that corner case here
212 */
213 uint8_t hmac_output[256/8];
214
215 hmac_ctx_reset(ctx);
216
217 /*
218 * If the token was only valid for the empty user, also generate
219 * a new token with the empty username since we do not want to loose
220 * the information that the username cannot be trusted
221 */
222 struct key_state *ks = &multi->session[TM_ACTIVE].key[KS_PRIMARY];
224 {
225 hmac_ctx_update(ctx, (const uint8_t *) "", 0);
226 }
227 else
228 {
229 hmac_ctx_update(ctx, (uint8_t *) up->username, (int) strlen(up->username));
230 }
232 hmac_ctx_update(ctx, (uint8_t *) &initial_timestamp, sizeof(initial_timestamp));
233 hmac_ctx_update(ctx, (uint8_t *) &timestamp, sizeof(timestamp));
234 hmac_ctx_final(ctx, hmac_output);
235
236 /* Construct the unencoded session token */
237 struct buffer token = alloc_buf_gc(
238 2*sizeof(uint64_t) + AUTH_TOKEN_SESSION_ID_LEN + 256/8, &gc);
239
240 ASSERT(buf_write(&token, sessid, sizeof(sessid)));
242 ASSERT(buf_write(&token, &timestamp, sizeof(timestamp)));
244
245 char *b64output = NULL;
247
250
254
255 free(b64output);
256
257 /* free the auth-token if defined, we will replace it with a new one */
258 free(multi->auth_token);
259 multi->auth_token = strdup((char *)BPTR(&session_token));
260
261 dmsg(D_SHOW_KEYS, "Generated token for client: %s (%s)",
262 multi->auth_token, up->username);
263
264 if (!multi->auth_token_initial)
265 {
266 /*
267 * Save the initial auth token to continue using the same session ID
268 * and timestamp in updates
269 */
270 multi->auth_token_initial = strdup(multi->auth_token);
271 }
272
273 gc_free(&gc);
274}
275
276
277static bool
278check_hmac_token(hmac_ctx_t *ctx, const uint8_t *b64decoded, const char *username)
279{
280 ASSERT(hmac_ctx_size(ctx) == 256/8);
281
282 uint8_t hmac_output[256/8];
283
284 hmac_ctx_reset(ctx);
285 hmac_ctx_update(ctx, (uint8_t *) username, (int)strlen(username));
288
289 const uint8_t *hmac = b64decoded + TOKEN_DATA_LEN - 256/8;
290 return memcmp_constant_time(&hmac_output, hmac, 32) == 0;
291}
292
293unsigned int
294verify_auth_token(struct user_pass *up, struct tls_multi *multi,
295 struct tls_session *session)
296{
297 /*
298 * Base64 is <= input and input is < USER_PASS_LEN, so using USER_PASS_LEN
299 * is safe here but a bit overkill
300 */
301 ASSERT(up && !up->protected);
305
306 /*
307 * Ensure that the decoded data is the size of the
308 * timestamp + hmac + session id
309 */
311 {
312 msg(M_WARN, "ERROR: --auth-token wrong size (%d!=%d)",
314 return 0;
315 }
316
317 unsigned int ret = 0;
318
319 const uint8_t *sessid = b64decoded;
321 const uint8_t *tstamp = tstamp_initial + sizeof(int64_t);
322
323 /* tstamp, tstamp_initial might not be aligned to an uint64, use memcpy
324 * to avoid unaligned access */
325 uint64_t timestamp = 0, timestamp_initial = 0;
326 memcpy(&timestamp, tstamp, sizeof(uint64_t));
327 timestamp = ntohll(timestamp);
328
331
332 hmac_ctx_t *ctx = multi->opt.auth_token_key.hmac;
333 if (check_hmac_token(ctx, b64decoded, up->username))
334 {
335 ret |= AUTH_TOKEN_HMAC_OK;
336 }
337 else if (check_hmac_token(ctx, b64decoded, ""))
338 {
339 ret |= AUTH_TOKEN_HMAC_OK;
341 /* overwrite the username of the client with the empty one */
342 strcpy(up->username, "");
343 }
344 else
345 {
346 msg(M_WARN, "--auth-gen-token: HMAC on token from client failed (%s)",
347 up->username);
348 return 0;
349 }
350
351 /* Accept session tokens only if their timestamp is in the acceptable range
352 * for renegotiations */
353 bool in_renegotiation_time = now >= timestamp
354 && now < timestamp + 2 * session->opt->auth_token_renewal;
355
357 {
358 msg(M_WARN, "Timestamp (%" PRIu64 ") of auth-token is out of the renewal window",
359 timestamp);
360 ret |= AUTH_TOKEN_EXPIRED;
361 }
362
363 /* Sanity check the initial timestamp */
364 if (timestamp < timestamp_initial)
365 {
366 msg(M_WARN, "Initial timestamp (%" PRIu64 ") in token from client earlier than "
367 "current timestamp %" PRIu64 ". Broken/unsynchronised clock?",
368 timestamp_initial, timestamp);
369 ret |= AUTH_TOKEN_EXPIRED;
370 }
371
372 if (multi->opt.auth_token_lifetime
374 {
375 ret |= AUTH_TOKEN_EXPIRED;
376 }
377
378 if (ret & AUTH_TOKEN_EXPIRED)
379 {
380 /* Tell client that the session token is expired */
381 auth_set_client_reason(multi, "SESSION: token expired");
382 msg(M_INFO, "--auth-gen-token: auth-token from client expired");
383 }
384
385 /* Check that we do have the same session ID in the token as in our stored
386 * auth-token to ensure that it did not change.
387 * This also compares the prefix and session part of the
388 * tokens, which should be identical if the session ID stayed the same */
389 if (multi->auth_token_initial
392 {
393 msg(M_WARN, "--auth-gen-token: session id in token changed (Rejecting "
394 "token.");
395 ret = 0;
396 }
397 return ret;
398}
399
400void
402{
403 if (multi)
404 {
405 if (multi->auth_token)
406 {
408 free(multi->auth_token);
409 }
410 if (multi->auth_token_initial)
411 {
413 strlen(multi->auth_token_initial));
414 free(multi->auth_token_initial);
415 }
416 multi->auth_token = NULL;
417 multi->auth_token_initial = NULL;
418 }
419}
420
421void
423{
424 struct tls_multi *multi = c->c2.tls_multi;
425 struct tls_session *session = &multi->session[TM_ACTIVE];
426
427 if (get_primary_key(multi)->state < S_GENERATED_KEYS
428 || get_primary_key(multi)->authenticated != KS_AUTH_TRUE)
429 {
430 /* the currently active session is still in renegotiation or another
431 * not fully authorized state. We are either very close to a
432 * renegotiation or have deauthorized the client. In both cases
433 * we just ignore the request to send another token
434 */
435 return;
436 }
437
438 if (!multi->auth_token_initial)
439 {
440 msg(D_SHOW_KEYS, "initial auth-token not generated yet, skipping "
441 "auth-token renewal.");
442 return;
443 }
444
445 if (!multi->locked_username)
446 {
447 msg(D_SHOW_KEYS, "username not locked, skipping auth-token renewal.");
448 return;
449 }
450
451 struct user_pass up;
452 CLEAR(up);
453 strncpynt(up.username, multi->locked_username, sizeof(up.username));
454
455 generate_auth_token(&up, multi);
456
458}
459
460void
462{
463 /*
464 * Auth token already sent to client, update auth-token on client.
465 * The initial auth-token is sent as part of the push message, for this
466 * update we need to schedule an extra push message.
467 *
468 * Otherwise, the auth-token get pushed out as part of the "normal"
469 * push-reply
470 */
471 if (multi->auth_token_initial)
472 {
473 /*
474 * We do not explicitly reschedule the sending of the
475 * control message here. This might delay this reply
476 * a few seconds but this message is not time critical
477 */
479 }
480}
void auth_token_write_server_key_file(const char *filename)
Generate a auth-token server secret key, and write to file.
Definition auth_token.c:118
static bool check_hmac_token(hmac_ctx_t *ctx, const uint8_t *b64decoded, const char *username)
Definition auth_token.c:278
void auth_token_init_secret(struct key_ctx *key_ctx, const char *key_file, bool key_inline)
Loads an HMAC secret from a file or if no file is present generates a epheremal secret for the run ti...
Definition auth_token.c:124
void generate_auth_token(const struct user_pass *up, struct tls_multi *multi)
Generate an auth token based on username and timestamp.
Definition auth_token.c:164
#define AUTH_TOKEN_SESSION_ID_LEN
Definition auth_token.c:21
unsigned int verify_auth_token(struct user_pass *up, struct tls_multi *multi, struct tls_session *session)
Verifies the auth token to be in the format that generate_auth_token create and checks if the token i...
Definition auth_token.c:294
static struct key_type auth_token_kt(void)
Definition auth_token.c:32
#define AUTH_TOKEN_SESSION_ID_BASE64_LEN
Definition auth_token.c:22
void add_session_token_env(struct tls_session *session, struct tls_multi *multi, const struct user_pass *up)
Put the session id, and auth token status into the environment if auth-token is enabled.
Definition auth_token.c:38
const char * auth_token_pem_name
Definition auth_token.c:19
#define TOKEN_DATA_LEN
Definition auth_token.c:29
void check_send_auth_token(struct context *c)
Checks if the timer to resend the auth-token has expired and if a new auth-token should be send to th...
Definition auth_token.c:422
void wipe_auth_token(struct tls_multi *multi)
Wipes the authentication token out of the memory, frees and cleans up related buffers and flags.
Definition auth_token.c:401
void resend_auth_token_renegotiation(struct tls_multi *multi, struct tls_session *session)
Checks if a client should be sent a new auth token to update its current auth-token.
Definition auth_token.c:461
static bool is_auth_token(const char *password)
Return if the password string has the format of a password.
Definition auth_token.h:127
#define SESSION_ID_PREFIX
The prefix given to auth tokens start with, this prefix is special cased to not show up in log files ...
Definition auth_token.h:115
void free_buf(struct buffer *buf)
Definition buffer.c:183
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition buffer.c:88
struct buffer alloc_buf(size_t size)
Definition buffer.c:62
char * string_alloc(const char *str, struct gc_arena *gc)
Definition buffer.c:649
#define BPTR(buf)
Definition buffer.h:124
static bool buf_read(struct buffer *src, void *dest, int size)
Definition buffer.h:778
static void secure_memzero(void *data, size_t len)
Securely zeroise memory.
Definition buffer.h:414
static bool buf_write(struct buffer *dest, const void *src, size_t size)
Definition buffer.h:668
static bool buf_write_u8(struct buffer *dest, uint8_t data)
Definition buffer.h:692
#define BLEN(buf)
Definition buffer.h:127
static void strncpynt(char *dest, const char *src, size_t maxlen)
Definition buffer.h:361
static void gc_free(struct gc_arena *a)
Definition buffer.h:1033
static struct gc_arena gc_new(void)
Definition buffer.h:1025
void init_key_ctx(struct key_ctx *ctx, const struct key_parameters *key, const struct key_type *kt, int enc, const char *prefix)
Definition crypto.c:1015
bool read_pem_key_file(struct buffer *key, const char *pem_name, const char *key_file, bool key_inline)
Read key material from a PEM encoded files into the key structure.
Definition crypto.c:1902
bool generate_ephemeral_key(struct buffer *key, const char *key_name)
Generate ephermal key material into the key structure.
Definition crypto.c:1884
void write_pem_key_file(const char *filename, const char *pem_name)
Generate a server key with enough randomness to fill a key struct and write to file.
Definition crypto.c:1846
void key_parameters_from_key(struct key_parameters *key_params, const struct key *key)
Converts a struct key representation into a struct key_parameters representation.
Definition crypto.c:1219
Data Channel Cryptography Module.
int memcmp_constant_time(const void *a, const void *b, size_t size)
As memcmp(), but constant-time.
static struct key_type create_kt(const char *cipher, const char *md, const char *optname)
Creates and validates an instance of struct key_type with the provided algs.
Definition crypto.h:685
void hmac_ctx_update(hmac_ctx_t *ctx, const uint8_t *src, int src_len)
void hmac_ctx_reset(hmac_ctx_t *ctx)
void hmac_ctx_final(hmac_ctx_t *ctx, uint8_t *dst)
int hmac_ctx_size(hmac_ctx_t *ctx)
int rand_bytes(uint8_t *output, int len)
Wrapper for secure random number generator.
mbedtls_md_context_t hmac_ctx_t
Generic HMAC context.
void setenv_str(struct env_set *es, const char *name, const char *value)
Definition env_set.c:283
#define D_SHOW_KEYS
Definition errlevel.h:121
#define M_INFO
Definition errlevel.h:55
#define KS_PRIMARY
Primary key state index.
Definition ssl_common.h:456
#define TM_ACTIVE
Active tls_session.
Definition ssl_common.h:535
#define S_GENERATED_KEYS
The data channel keys have been generated The TLS session is fully authenticated when reaching this s...
Definition ssl_common.h:102
#define ntohll(x)
Definition integer.h:35
#define htonll(x)
Definition integer.h:30
#define USER_PASS_LEN
Definition misc.h:69
#define CLEAR(x)
Definition basic.h:33
#define M_FATAL
Definition error.h:89
#define dmsg(flags,...)
Definition error.h:148
#define msg(flags,...)
Definition error.h:144
#define ASSERT(x)
Definition error.h:195
#define M_WARN
Definition error.h:91
time_t now
Definition otime.c:34
void send_push_reply_auth_token(struct tls_multi *multi)
Sends a push reply message only containin the auth-token to update the auth-token on the client.
Definition push.c:761
int openvpn_base64_decode(const char *str, void *data, int size)
Definition base64.c:158
int openvpn_base64_encode(const void *data, int size, char **str)
Definition base64.c:52
Control Channel SSL/Data channel negotiation module.
Control Channel Common Data Structures.
#define AUTH_TOKEN_HMAC_OK
Auth-token sent from client has valid hmac.
Definition ssl_common.h:658
#define AUTH_TOKEN_EXPIRED
Auth-token sent from client has expired.
Definition ssl_common.h:660
@ KS_AUTH_TRUE
Key state is authenticated.
Definition ssl_common.h:151
static const struct key_state * get_primary_key(const struct tls_multi *multi)
gets an item of key_state objects in the order they should be scanned by data channel modules.
Definition ssl_common.h:728
#define AUTH_TOKEN_VALID_EMPTYUSER
Auth-token is only valid for an empty username and not the username actually supplied from the client...
Definition ssl_common.h:662
void auth_set_client_reason(struct tls_multi *multi, const char *client_reason)
Sets the reason why authentication of a client failed.
Definition ssl_verify.c:808
Control Channel Verification Module.
Wrapper structure for dynamically allocated memory.
Definition buffer.h:61
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:66
struct tls_multi * tls_multi
TLS state structure for this VPN tunnel.
Definition openvpn.h:323
Contains all state information for one tunnel.
Definition openvpn.h:474
struct context_2 c2
Level 2 context.
Definition openvpn.h:514
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:117
Container for one set of cipher and/or HMAC contexts.
Definition crypto.h:201
hmac_ctx_t * hmac
Generic HMAC context.
Definition crypto.h:203
internal structure similar to struct key that holds key information but is not represented on wire an...
Definition crypto.h:162
Security parameter state of one TLS and data channel key session.
Definition ssl_common.h:200
unsigned int auth_token_state_flags
The state of the auth-token sent from the client.
Definition ssl_common.h:203
Container for unidirectional cipher and HMAC key material.
Definition crypto.h:152
Security parameter state for a single VPN tunnel.
Definition ssl_common.h:597
char * auth_token_initial
The first auth-token we sent to a client.
Definition ssl_common.h:654
char * locked_username
Definition ssl_common.h:630
struct tls_options opt
Definition ssl_common.h:602
struct tls_session session[TM_SIZE]
Array of tls_session objects representing control channel sessions with the remote peer.
Definition ssl_common.h:681
char * auth_token
If server sends a generated auth-token, this is the token to use for future user/pass authentications...
Definition ssl_common.h:650
struct key_ctx auth_token_key
Definition ssl_common.h:399
unsigned int auth_token_lifetime
Definition ssl_common.h:396
bool auth_token_generate
Generate auth-tokens on successful user/pass auth,seet via options->auth_token_generate.
Definition ssl_common.h:392
Security parameter state of a single session within a VPN tunnel.
Definition ssl_common.h:480
struct key_state key[KS_SIZE]
Definition ssl_common.h:515
bool protected
Definition misc.h:63
char password[USER_PASS_LEN]
Definition misc.h:73
char username[USER_PASS_LEN]
Definition misc.h:72
struct gc_arena gc
Definition test_ssl.c:155