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 #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
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
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  }
68  {
70  {
71  case 0:
72  state = "Authenticated";
73  break;
74 
75  case AUTH_TOKEN_EXPIRED:
76  state = "Expired";
77  break;
78 
80  state = "AuthenticatedEmptyUser";
81  break;
82 
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;
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 
124  memcpy(session_id, session_id_source + strlen(SESSION_ID_PREFIX),
126 
127  setenv_str(session->opt->es, "session_id", session_id);
128 }
129 
130 void
131 auth_token_write_server_key_file(const char *filename)
132 {
134 }
135 
136 void
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,
149  key_file, key_inline);
150  }
151  else
152  {
153  key_loaded = generate_ephemeral_key(&server_secret_key,
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
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 
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';
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  */
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  }
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
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
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;
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
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
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  {
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 }
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:52
int auth_token_state_flags
The state of the auth-token sent from the client last time.
Definition: ssl_common.h:577
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:174
void free_buf(struct buffer *buf)
Definition: buffer.c:185
#define USER_PASS_LEN
Definition: misc.h:73
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:837
unsigned int auth_token_lifetime
Definition: ssl_common.h:326
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:567
struct tls_options * opt
Definition: ssl_common.h:409
#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, for clients that do not update their auth-token (older Open...
Definition: ssl_common.h:561
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1023
#define dmsg
Definition: error.h:174
Security parameter state for a single VPN tunnel.
Definition: ssl_common.h:501
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
bool auth_token_generate
Generate auth-tokens on successful user/pass auth,seet via options->auth_token_generate.
Definition: ssl_common.h:322
#define ASSERT(x)
Definition: error.h:221
bool generate_ephemeral_key(struct buffer *key, const char *key_name)
Generate ephermal key material into the key structure.
Definition: crypto.c:1919
mbedtls_md_context_t hmac_ctx_t
Generic HMAC context.
char username[USER_PASS_LEN]
Definition: misc.h:75
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:32
static bool buf_write(struct buffer *dest, const void *src, int size)
Definition: buffer.h:689
#define TOKEN_DATA_LEN
Definition: auth_token.c:29
#define BPTR(buf)
Definition: buffer.h:124
#define AUTH_TOKEN_HMAC_OK
Auth-token sent from client has valid hmac.
Definition: ssl_common.h:565
#define ntohll(x)
Definition: integer.h:35
interval_t renegotiate_seconds
Definition: ssl_common.h:277
#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:569
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
static bool check_hmac_token(hmac_ctx_t *ctx, const uint8_t *b64decoded, const char *username)
Definition: auth_token.c:283
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:807
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:1937
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:1881
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:390
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:299
int hmac_ctx_size(const hmac_ctx_t *ctx)
struct tls_options opt
Definition: ssl_common.h:507
const md_kt_t * md_kt_get(const char *digest)
Return message digest parameters, based on the given digest name.
#define msg
Definition: error.h:173
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
unsigned __int64 uint64_t
Definition: config-msvc.h:157
#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:137
unsigned char md_kt_size(const md_kt_t *kt)
Returns the size of the message digest, in bytes.
struct key_ctx auth_token_key
Definition: ssl_common.h:328
__int64 int64_t
Definition: config-msvc.h:161
unsigned __int8 uint8_t
Definition: config-msvc.h:160
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:557
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:406
Definition: misc.h:63
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
#define M_FATAL
Definition: error.h:94
#define D_SHOW_KEYS
Definition: errlevel.h:118
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition: buffer.c:90
#define M_WARN
Definition: error.h:96
#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:131
#define PRIu64
char password[USER_PASS_LEN]
Definition: misc.h:76
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
void hmac_ctx_reset(hmac_ctx_t *ctx)
struct env_set * es
Definition: ssl_common.h:334
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