OpenVPN
auth_token.c
Go to the documentation of this file.
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 #define AUTH_TOKEN_SESSION_ID_BASE64_LEN (AUTH_TOKEN_SESSION_ID_LEN * 8 / 6)
25 
26 #if AUTH_TOKEN_SESSION_ID_LEN % 3
27 #error AUTH_TOKEN_SESSION_ID_LEN needs to be multiple a 3
28 #endif
29 
30 /* Size of the data of the token (not b64 encoded and without prefix) */
31 #define TOKEN_DATA_LEN (2 * sizeof(int64_t) + AUTH_TOKEN_SESSION_ID_LEN + 32)
32 
33 static struct key_type
35 {
36  struct key_type kt = { 0 };
37  /* We do not encrypt our session tokens */
38  kt.cipher = NULL;
39  kt.digest = md_kt_get("SHA256");
40 
41  if (!kt.digest)
42  {
43  msg(M_WARN, "ERROR: --tls-crypt requires HMAC-SHA-256 support.");
44  return (struct key_type) { 0 };
45  }
46 
47  kt.hmac_length = md_kt_size(kt.digest);
48 
49  return kt;
50 }
51 
52 
53 void
55  const struct user_pass *up)
56 {
57  if (!multi->opt.auth_token_generate)
58  {
59  return;
60  }
61 
62  int auth_token_state_flags = session->key[KS_PRIMARY].auth_token_state_flags;
63 
64  const char *state;
65 
66  if (!is_auth_token(up->password))
67  {
68  state = "Initial";
69  }
70  else if (auth_token_state_flags & AUTH_TOKEN_HMAC_OK)
71  {
72  switch (auth_token_state_flags & (AUTH_TOKEN_VALID_EMPTYUSER|AUTH_TOKEN_EXPIRED))
73  {
74  case 0:
75  state = "Authenticated";
76  break;
77 
78  case AUTH_TOKEN_EXPIRED:
79  state = "Expired";
80  break;
81 
83  state = "AuthenticatedEmptyUser";
84  break;
85 
87  state = "ExpiredEmptyUser";
88  break;
89 
90  default:
91  /* Silence compiler warning, all four possible combinations are covered */
92  ASSERT(0);
93  }
94  }
95  else
96  {
97  state = "Invalid";
98  }
99 
100  setenv_str(session->opt->es, "session_state", state);
101 
102  /* We had a valid session id before */
103  const char *session_id_source;
104  if (auth_token_state_flags & AUTH_TOKEN_HMAC_OK
105  && !(auth_token_state_flags & AUTH_TOKEN_EXPIRED))
106  {
107  session_id_source = up->password;
108  }
109  else
110  {
111  /*
112  * No session before, generate a new session token for the new session
113  */
114  if (!multi->auth_token_initial)
115  {
116  generate_auth_token(up, multi);
117  }
118  session_id_source = multi->auth_token_initial;
119  }
120  /*
121  * In the auth-token the auth token is already base64 encoded
122  * and being a multiple of 4 ensure that it a multiple of bytes
123  * in the encoding
124  */
125 
127  memcpy(session_id, session_id_source + strlen(SESSION_ID_PREFIX),
129 
130  setenv_str(session->opt->es, "session_id", session_id);
131 }
132 
133 void
134 auth_token_write_server_key_file(const char *filename)
135 {
137 }
138 
139 void
140 auth_token_init_secret(struct key_ctx *key_ctx, const char *key_file,
141  bool key_inline)
142 {
143  struct key_type kt = auth_token_kt();
144 
145  struct buffer server_secret_key = alloc_buf(2048);
146 
147  bool key_loaded = false;
148  if (key_file)
149  {
150  key_loaded = read_pem_key_file(&server_secret_key,
152  key_file, key_inline);
153  }
154  else
155  {
156  key_loaded = generate_ephemeral_key(&server_secret_key,
158  }
159 
160  if (!key_loaded)
161  {
162  msg(M_FATAL, "ERROR: Cannot load auth-token secret");
163  }
164 
165  struct key key;
166 
167  if (!buf_read(&server_secret_key, &key, sizeof(key)))
168  {
169  msg(M_FATAL, "ERROR: not enough data in auth-token secret");
170  }
171  init_key_ctx(key_ctx, &key, &kt, false, "auth-token secret");
172 
173  free_buf(&server_secret_key);
174 }
175 
176 void
177 generate_auth_token(const struct user_pass *up, struct tls_multi *multi)
178 {
179  struct gc_arena gc = gc_new();
180 
181  int64_t timestamp = htonll((uint64_t)now);
182  int64_t initial_timestamp = timestamp;
183 
184  hmac_ctx_t *ctx = multi->opt.auth_token_key.hmac;
185  ASSERT(hmac_ctx_size(ctx) == 256/8);
186 
187  uint8_t sessid[AUTH_TOKEN_SESSION_ID_LEN];
188 
189  if (multi->auth_token_initial)
190  {
191  /* Just enough space to fit 8 bytes+ 1 extra to decode a non padded
192  * base64 string (multiple of 3 bytes). 9 bytes => 12 bytes base64
193  * bytes
194  */
195  char old_tstamp_decode[9];
196 
197  /* Make a copy of the string to not modify multi->auth_token_initial */
198  char *initial_token_copy = string_alloc(multi->auth_token_initial, &gc);
199 
200  char *old_sessid = initial_token_copy + strlen(SESSION_ID_PREFIX);
201  char *old_tsamp_initial = old_sessid + AUTH_TOKEN_SESSION_ID_LEN*8/6;
202 
203  /*
204  * We null terminate the old token just after the session ID to let
205  * our base64 decode function only decode the session ID
206  */
207  old_tsamp_initial[12] = '\0';
208  ASSERT(openvpn_base64_decode(old_tsamp_initial, old_tstamp_decode, 9) == 9);
209 
210  /*
211  * Avoid old gcc (4.8.x) complaining about strict aliasing
212  * by using a temporary variable instead of doing it in one
213  * line
214  */
215  uint64_t *tstamp_ptr = (uint64_t *) old_tstamp_decode;
216  initial_timestamp = *tstamp_ptr;
217 
218  old_tsamp_initial[0] = '\0';
220  }
221  else if (!rand_bytes(sessid, AUTH_TOKEN_SESSION_ID_LEN))
222  {
223  msg( M_FATAL, "Failed to get enough randomness for "
224  "authentication token");
225  }
226 
227  /* Calculate the HMAC */
228  /* We enforce up->username to be \0 terminated in ssl.c.. Allowing username
229  * with \0 in them is asking for troubles in so many ways anyway that we
230  * ignore that corner case here
231  */
232  uint8_t hmac_output[256/8];
233 
234  hmac_ctx_reset(ctx);
235 
236  /*
237  * If the token was only valid for the empty user, also generate
238  * a new token with the empty username since we do not want to loose
239  * the information that the username cannot be trusted
240  */
241  struct key_state *ks = &multi->session[TM_ACTIVE].key[KS_PRIMARY];
243  {
244  hmac_ctx_update(ctx, (const uint8_t *) "", 0);
245  }
246  else
247  {
248  hmac_ctx_update(ctx, (uint8_t *) up->username, (int) strlen(up->username));
249  }
251  hmac_ctx_update(ctx, (uint8_t *) &initial_timestamp, sizeof(initial_timestamp));
252  hmac_ctx_update(ctx, (uint8_t *) &timestamp, sizeof(timestamp));
253  hmac_ctx_final(ctx, hmac_output);
254 
255  /* Construct the unencoded session token */
256  struct buffer token = alloc_buf_gc(
257  2*sizeof(uint64_t) + AUTH_TOKEN_SESSION_ID_LEN + 256/8, &gc);
258 
259  ASSERT(buf_write(&token, sessid, sizeof(sessid)));
260  ASSERT(buf_write(&token, &initial_timestamp, sizeof(initial_timestamp)));
261  ASSERT(buf_write(&token, &timestamp, sizeof(timestamp)));
262  ASSERT(buf_write(&token, hmac_output, sizeof(hmac_output)));
263 
264  char *b64output;
265  openvpn_base64_encode(BPTR(&token), BLEN(&token), &b64output);
266 
267  struct buffer session_token = alloc_buf_gc(
268  strlen(SESSION_ID_PREFIX) + strlen(b64output) + 1, &gc);
269 
270  ASSERT(buf_write(&session_token, SESSION_ID_PREFIX, strlen(SESSION_ID_PREFIX)));
271  ASSERT(buf_write(&session_token, b64output, (int)strlen(b64output)));
272  ASSERT(buf_write_u8(&session_token, 0));
273 
274  free(b64output);
275 
276  /* free the auth-token if defined, we will replace it with a new one */
277  free(multi->auth_token);
278  multi->auth_token = strdup((char *)BPTR(&session_token));
279 
280  dmsg(D_SHOW_KEYS, "Generated token for client: %s (%s)",
281  multi->auth_token, up->username);
282 
283  if (!multi->auth_token_initial)
284  {
285  /*
286  * Save the initial auth token to continue using the same session ID
287  * and timestamp in updates
288  */
289  multi->auth_token_initial = strdup(multi->auth_token);
290  }
291 
292  gc_free(&gc);
293 }
294 
295 
296 static bool
297 check_hmac_token(hmac_ctx_t *ctx, const uint8_t *b64decoded, const char *username)
298 {
299  ASSERT(hmac_ctx_size(ctx) == 256/8);
300 
301  uint8_t hmac_output[256/8];
302 
303  hmac_ctx_reset(ctx);
304  hmac_ctx_update(ctx, (uint8_t *) username, (int)strlen(username));
305  hmac_ctx_update(ctx, b64decoded, TOKEN_DATA_LEN - 256/8);
306  hmac_ctx_final(ctx, hmac_output);
307 
308  const uint8_t *hmac = b64decoded + TOKEN_DATA_LEN - 256/8;
309  return memcmp_constant_time(&hmac_output, hmac, 32) == 0;
310 }
311 
312 unsigned int
313 verify_auth_token(struct user_pass *up, struct tls_multi *multi,
314  struct tls_session *session)
315 {
316  /*
317  * Base64 is <= input and input is < USER_PASS_LEN, so using USER_PASS_LEN
318  * is safe here but a bit overkill
319  */
320  uint8_t b64decoded[USER_PASS_LEN];
321  int decoded_len = openvpn_base64_decode(up->password + strlen(SESSION_ID_PREFIX),
322  b64decoded, USER_PASS_LEN);
323 
324  /*
325  * Ensure that the decoded data is the size of the
326  * timestamp + hmac + session id
327  */
328  if (decoded_len != TOKEN_DATA_LEN)
329  {
330  msg(M_WARN, "ERROR: --auth-token wrong size (%d!=%d)",
331  decoded_len, (int) TOKEN_DATA_LEN);
332  return 0;
333  }
334 
335  unsigned int ret = 0;
336 
337  const uint8_t *sessid = b64decoded;
338  const uint8_t *tstamp_initial = sessid + AUTH_TOKEN_SESSION_ID_LEN;
339  const uint8_t *tstamp = tstamp_initial + sizeof(int64_t);
340 
341  uint64_t timestamp = ntohll(*((uint64_t *) (tstamp)));
342  uint64_t timestamp_initial = ntohll(*((uint64_t *) (tstamp_initial)));
343 
344  hmac_ctx_t *ctx = multi->opt.auth_token_key.hmac;
345  if (check_hmac_token(ctx, b64decoded, up->username))
346  {
347  ret |= AUTH_TOKEN_HMAC_OK;
348  }
349  else if (check_hmac_token(ctx, b64decoded, ""))
350  {
351  ret |= AUTH_TOKEN_HMAC_OK;
353  /* overwrite the username of the client with the empty one */
354  strcpy(up->username, "");
355  }
356  else
357  {
358  msg(M_WARN, "--auth-gen-token: HMAC on token from client failed (%s)",
359  up->username);
360  return 0;
361  }
362 
363  /* Accept session tokens that not expired are in the acceptable range
364  * for renogiations */
365  bool in_renegotiation_time = now >= timestamp
366  && now < timestamp + 2 * session->opt->renegotiate_seconds;
367 
368  if (!in_renegotiation_time)
369  {
370  ret |= AUTH_TOKEN_EXPIRED;
371  }
372 
373  /* Sanity check the initial timestamp */
374  if (timestamp < timestamp_initial)
375  {
376  msg(M_WARN, "Initial timestamp (%" PRIu64 " in token from client earlier than "
377  "current timestamp %" PRIu64 ". Broken/unsynchronised clock?",
378  timestamp_initial, timestamp);
379  ret |= AUTH_TOKEN_EXPIRED;
380  }
381 
382  if (multi->opt.auth_token_lifetime
383  && now > timestamp_initial + multi->opt.auth_token_lifetime)
384  {
385  ret |= AUTH_TOKEN_EXPIRED;
386  }
387 
388  if (ret & AUTH_TOKEN_EXPIRED)
389  {
390  /* Tell client that the session token is expired */
391  auth_set_client_reason(multi, "SESSION: token expired");
392  msg(M_INFO, "--auth-gen-token: auth-token from client expired");
393  }
394 
395  /* Check that we do have the same session ID in the token as in our stored
396  * auth-token to ensure that it did not change.
397  * This also compares the prefix and session part of the
398  * tokens, which should be identical if the session ID stayed the same */
399  if (multi->auth_token_initial
402  {
403  msg(M_WARN, "--auth-gen-token: session id in token changed (Rejecting "
404  "token.");
405  ret = 0;
406  }
407  return ret;
408 }
409 
410 void
412 {
413  if (multi)
414  {
415  if (multi->auth_token)
416  {
417  secure_memzero(multi->auth_token, strlen(multi->auth_token));
418  free(multi->auth_token);
419  }
420  if (multi->auth_token_initial)
421  {
423  strlen(multi->auth_token_initial));
424  free(multi->auth_token_initial);
425  }
426  multi->auth_token = NULL;
427  multi->auth_token_initial = NULL;
428  }
429 }
430 
431 void
433 {
434  /*
435  * Auth token already sent to client, update auth-token on client.
436  * The initial auth-token is sent as part of the push message, for this
437  * update we need to schedule an extra push message.
438  *
439  * Otherwise the auth-token get pushed out as part of the "normal"
440  * push-reply
441  */
442  bool is_renegotiation = session->key[KS_PRIMARY].key_id != 0;
443 
444  if (multi->auth_token_initial && is_renegotiation)
445  {
446  /*
447  * We do not explicitly reschedule the sending of the
448  * control message here. This might delay this reply
449  * a few seconds but this message is not time critical
450  */
452  }
453 }
static bool buf_write_u8(struct buffer *dest, int data)
Definition: buffer.h:713
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:54
#define TM_ACTIVE
Active tls_session.
Definition: ssl_common.h:508
Security parameter state of one TLS and data channel key session.
Definition: ssl_common.h:203
struct key_state key[KS_SIZE]
Definition: ssl_common.h:488
uint8_t hmac_length
HMAC length, in bytes.
Definition: crypto.h:142
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:177
void free_buf(struct buffer *buf)
Definition: buffer.c:185
char * string_alloc(const char *str, struct gc_arena *gc)
Definition: buffer.c:685
#define USER_PASS_LEN
Definition: misc.h:68
void init_key_ctx(struct key_ctx *ctx, const struct key *key, const struct key_type *kt, int enc, const char *prefix)
Definition: crypto.c:819
unsigned int auth_token_lifetime
Definition: ssl_common.h:373
static void secure_memzero(void *data, size_t len)
Securely zeroise memory.
Definition: buffer.h:401
#define M_INFO
Definition: errlevel.h:55
#define AUTH_TOKEN_EXPIRED
Auth-token sent from client has expired.
Definition: ssl_common.h:630
struct tls_options * opt
Definition: ssl_common.h:457
#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
char * auth_token_initial
The first auth-token we sent to a client.
Definition: ssl_common.h:624
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1023
Security parameter state for a single VPN tunnel.
Definition: ssl_common.h:566
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:645
struct buffer alloc_buf(size_t size)
Definition: buffer.c:64
void setenv_str(struct env_set *es, const char *name, const char *value)
Definition: env_set.c:285
#define AUTH_TOKEN_SESSION_ID_LEN
Definition: auth_token.c:23
#define dmsg(flags,...)
Definition: error.h:157
bool auth_token_generate
Generate auth-tokens on successful user/pass auth,seet via options->auth_token_generate.
Definition: ssl_common.h:369
#define ASSERT(x)
Definition: error.h:204
bool generate_ephemeral_key(struct buffer *key, const char *key_name)
Generate ephermal key material into the key structure.
Definition: crypto.c:1905
int auth_token_state_flags
The state of the auth-token sent from the client.
Definition: ssl_common.h:207
struct tls_session session[TM_SIZE]
Array of tls_session objects representing control channel sessions with the remote peer...
Definition: ssl_common.h:651
mbedtls_md_context_t hmac_ctx_t
Generic HMAC context.
char username[USER_PASS_LEN]
Definition: misc.h:70
static bool buf_read(struct buffer *src, void *dest, int size)
Definition: buffer.h:800
static struct key_type auth_token_kt(void)
Definition: auth_token.c:34
#define TOKEN_DATA_LEN
Definition: auth_token.c:31
#define BPTR(buf)
Definition: buffer.h:124
#define AUTH_TOKEN_HMAC_OK
Auth-token sent from client has valid hmac.
Definition: ssl_common.h:628
#define ntohll(x)
Definition: integer.h:35
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:432
interval_t renegotiate_seconds
Definition: ssl_common.h:323
#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:632
int rand_bytes(uint8_t *output, int len)
Wrapper for secure random number generator.
static struct gc_arena gc_new(void)
Definition: buffer.h:1015
#define KS_PRIMARY
Primary key state index.
Definition: ssl_common.h:430
static bool check_hmac_token(hmac_ctx_t *ctx, const uint8_t *b64decoded, const char *username)
Definition: auth_token.c:297
time_t now
Definition: otime.c:36
int memcmp_constant_time(const void *a, const void *b, size_t size)
As memcmp(), but constant-time.
void hmac_ctx_update(hmac_ctx_t *ctx, const uint8_t *src, int src_len)
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:833
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:1923
int openvpn_base64_decode(const char *str, void *data, int size)
Definition: base64.c:160
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:1867
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:411
Container for one set of cipher and/or HMAC contexts.
Definition: crypto.h:164
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:313
int hmac_ctx_size(const hmac_ctx_t *ctx)
struct tls_options opt
Definition: ssl_common.h:572
const md_kt_t * md_kt_get(const char *digest)
Return message digest parameters, based on the given digest name.
hmac_ctx_t * hmac
Generic HMAC context.
Definition: crypto.h:167
int openvpn_base64_encode(const void *data, int size, char **str)
Definition: base64.c:54
#define htonll(x)
Definition: integer.h:30
#define BLEN(buf)
Definition: buffer.h:127
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:140
unsigned char md_kt_size(const md_kt_t *kt)
Returns the size of the message digest, in bytes.
#define msg(flags,...)
Definition: error.h:153
int key_id
Key id for this key_state, inherited from struct tls_session.
Definition: ssl_common.h:213
struct key_ctx auth_token_key
Definition: ssl_common.h:375
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:620
const md_kt_t * digest
Message digest static parameters.
Definition: crypto.h:144
Security parameter state of a single session within a VPN tunnel.
Definition: ssl_common.h:454
Definition: misc.h:56
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
#define M_FATAL
Definition: error.h:98
#define D_SHOW_KEYS
Definition: errlevel.h:117
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition: buffer.c:90
#define M_WARN
Definition: error.h:100
#define free
Definition: cmocka.c:1850
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
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:134
static bool buf_write(struct buffer *dest, const void *src, size_t size)
Definition: buffer.h:689
#define PRIu64
char password[USER_PASS_LEN]
Definition: misc.h:71
void hmac_ctx_final(hmac_ctx_t *ctx, uint8_t *dst)
const cipher_kt_t * cipher
Cipher static parameters.
Definition: crypto.h:143
const char * auth_token_pem_name
Definition: auth_token.c:21
#define AUTH_TOKEN_SESSION_ID_BASE64_LEN
Definition: auth_token.c:24
void hmac_ctx_reset(hmac_ctx_t *ctx)
struct env_set * es
Definition: ssl_common.h:381
Container for unidirectional cipher and HMAC key material.
Definition: crypto.h:151
static bool is_auth_token(const char *password)
Return if the password string has the format of a password.
Definition: auth_token.h:127