OpenVPN
test_auth_token.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2016-2026 Sentyron B.V. <openvpn@sentyron.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <stdarg.h>
32#include <string.h>
33#include <setjmp.h>
34#include <cmocka.h>
35
36#include "auth_token.c"
37#include "test_common.h"
38
40{
42 struct key_type kt;
43 struct user_pass up;
45};
46
47/* Dummy functions that do nothing to mock the functionality */
48void
52
53void
54auth_set_client_reason(struct tls_multi *multi, const char *reason)
55{
56}
57
58static const char *now0key0 =
59 "SESS_ID_AT_0123456789abcdefAAAAAAAAAAAAAAAAAAAAAE5JsQJOVfo8jnI3RL3tBaR5NkE4yPfcylFUHmHSc5Bu";
60
61static const char *zeroinline = "-----BEGIN OpenVPN auth-token server key-----\n"
62 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
63 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
64 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n"
65 "-----END OpenVPN auth-token server key-----";
66
67static const char *allx01inline =
68 "-----BEGIN OpenVPN auth-token server key-----\n"
69 "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB\n"
70 "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB\n"
71 "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=\n"
72 "-----END OpenVPN auth-token server key-----";
73
74static const char *random_key =
75 "-----BEGIN OpenVPN auth-token server key-----\n"
76 "+mmmf7IQ5cymtMVjKYTWk8IOcYanRlpQmV9Tb3EjkHYxueBVDg3yqRgzeBlVGzNLD//rAPiOVhau\n"
77 "3NDBjNOQB8951bfs7Cc2mYfay92Bh2gRJ5XEM/DMfzCWN+7uU6NWoTTHr4FuojnIQtjtqVAj/JS9\n"
78 "w+dTSp/vYHl+c7uHd19uVRu/qLqV85+rm4tUGIjO7FfYuwyPqwmhuIsi3hs9QkSimh888FmBpoKY\n"
79 "/tbKVTJZmSERKti9KEwtV2eVAR0znN5KW7lCB3mHVAhN7bUpcoDjfCzYIFARxwswTFu9gFkwqUMY\n"
80 "I1KUOgIsVNs4llACioeXplYekWETR+YkJwDc/A==\n"
81 "-----END OpenVPN auth-token server key-----";
82
83static const char *random_token =
84 "SESS_ID_AT_ThhRItzOKNKrh3dfAAAAAFwzHpwAAAAAXDMenDdrq0RoH3dkA1f7O3wO+7kZcx2DusVZrRmFlWQM9HOb";
85
86static const char *last_session_state_value;
87static char last_session_id_value[64];
88
89static int
90setup(void **state)
91{
92 struct test_context *ctx = calloc(1, sizeof(*ctx));
93 *state = ctx;
94
95 struct key_parameters key = { 0 };
96 key.hmac_size = MAX_HMAC_KEY_LENGTH; /* 64 byte of 0 */
97
98 ctx->kt = auth_token_kt();
99 if (!ctx->kt.digest)
100 {
101 return 0;
102 }
103 ctx->multi.opt.auth_token_generate = true;
104 ctx->multi.opt.auth_token_lifetime = 3000;
105 ctx->session = &ctx->multi.session[TM_ACTIVE];
106
107 ctx->session->opt = calloc(1, sizeof(struct tls_options));
108 ctx->session->opt->renegotiate_seconds = 240;
109 ctx->session->opt->auth_token_renewal = 120;
110 ctx->session->opt->auth_token_lifetime = 3000;
111
112 strcpy(ctx->up.username, "test user name");
113 strcpy(ctx->up.password, "ignored");
114
115 init_key_ctx(&ctx->multi.opt.auth_token_key, &key, &ctx->kt, false, "TEST");
116
117 now = 0;
118
121
122 return 0;
123}
124
125static int
126teardown(void **state)
127{
128 struct test_context *ctx = (struct test_context *)*state;
129
131 wipe_auth_token(&ctx->multi);
132
133 free(ctx->session->opt);
134 free(ctx);
135
136 return 0;
137}
138
139static void
141{
142 struct test_context *ctx = (struct test_context *)*state;
143
144 generate_auth_token(&ctx->up, &ctx->multi);
145 strcpy(ctx->up.password, ctx->multi.auth_token);
146 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), AUTH_TOKEN_HMAC_OK);
147}
148
149static void
151{
152 struct test_context *ctx = (struct test_context *)*state;
153
154 generate_auth_token(&ctx->up, &ctx->multi);
155 strcpy(ctx->up.password, ctx->multi.auth_token);
156 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), AUTH_TOKEN_HMAC_OK);
157
158 /* Change auth-token key */
159 struct key_parameters key;
160 memset(key.hmac, '1', sizeof(key.hmac));
161 key.hmac_size = MAX_HMAC_KEY_LENGTH;
162
164 init_key_ctx(&ctx->multi.opt.auth_token_key, &key, &ctx->kt, false, "TEST");
165
166 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), 0);
167
168 /* Load original test key again */
169 memset(&key.hmac, 0, sizeof(key.hmac));
171 init_key_ctx(&ctx->multi.opt.auth_token_key, &key, &ctx->kt, false, "TEST");
172 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), AUTH_TOKEN_HMAC_OK);
173}
174
175static void
177{
178 struct test_context *ctx = (struct test_context *)*state;
179
180 const time_t initial_time = 100000;
181 now = initial_time;
182 generate_auth_token(&ctx->up, &ctx->multi);
183
184 const time_t token_renew_window =
185 initial_time + 2 * ctx->session->opt->auth_token_renewal;
186 const time_t token_eol = initial_time + ctx->session->opt->auth_token_lifetime + 1;
187
188 strcpy(ctx->up.password, ctx->multi.auth_token);
189 free(ctx->multi.auth_token_initial);
190 ctx->multi.auth_token_initial = NULL;
191
192 /* No time has passed */
193 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), AUTH_TOKEN_HMAC_OK);
194
195 /* Token before validity, should be rejected */
196 now = initial_time - 100;
197 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
199
200 /* Token still valid for renewal_time */
201 now = token_renew_window - 20;
202 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), AUTH_TOKEN_HMAC_OK);
203
204 /* Token past validity, should be rejected */
205 now = token_renew_window + 20;
206 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
208
209 /* Token past lifetime, should be rejected */
210 now = token_eol;
211 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
213
214 free(ctx->multi.auth_token_initial);
215 ctx->multi.auth_token_initial = NULL;
216
217 /* regenerate the token util it hits the expiry */
218 now = initial_time;
219 while (now < token_eol)
220 {
221 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
223 generate_auth_token(&ctx->up, &ctx->multi);
224 strcpy(ctx->up.password, ctx->multi.auth_token);
226 }
227
228
229 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
232
233 /* Non expiring token should be fine */
234 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), AUTH_TOKEN_HMAC_OK);
235}
236
237static void
238zerohmac(char *token)
239{
240 char *hmacstart =
241 token + AUTH_TOKEN_SESSION_ID_LEN + strlen(SESSION_ID_PREFIX) + 2 * sizeof(uint64_t);
242 memset(hmacstart, 0x8d, strlen(hmacstart));
243}
244
245static void
247{
248 struct test_context *ctx = (struct test_context *)*state;
249
250 now = 0;
251 /* Preload the session id so the same session id is used here */
252 ctx->multi.auth_token_initial = strdup(now0key0);
253 assert_non_null(ctx->multi.auth_token_initial);
254
255 /* Zero the hmac part to ensure we have a newly generated token */
257
258 generate_auth_token(&ctx->up, &ctx->multi);
259
260 assert_string_equal(now0key0, ctx->multi.auth_token);
261
262 strcpy(ctx->up.password, ctx->multi.auth_token);
263 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), AUTH_TOKEN_HMAC_OK);
264}
265
266
267void
268setenv_str(struct env_set *es, const char *name, const char *value)
269{
270 if (streq(name, "session_state"))
271 {
273 }
274 else if (streq(name, "session_id"))
275 {
276 strncpy(last_session_id_value, value, sizeof(last_session_id_value));
277 }
278}
279
280void
282{
283 struct test_context *ctx = (struct test_context *)*state;
284
285 /* Generate first auth token and check it is correct */
286 generate_auth_token(&ctx->up, &ctx->multi);
287 strcpy(ctx->up.password, ctx->multi.auth_token);
288 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), AUTH_TOKEN_HMAC_OK);
289
290 char *token_sessiona = strdup(ctx->multi.auth_token);
291
292 /* Generate second token */
293 wipe_auth_token(&ctx->multi);
294
295 generate_auth_token(&ctx->up, &ctx->multi);
296 strcpy(ctx->up.password, ctx->multi.auth_token);
297 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), AUTH_TOKEN_HMAC_OK);
298
299 assert_memory_not_equal(ctx->multi.auth_token_initial + strlen(SESSION_ID_PREFIX),
300 token_sessiona + strlen(SESSION_ID_PREFIX),
302
303 /* The first token is valid but should trigger the invalid response since
304 * the session id is not the same */
305 strcpy(ctx->up.password, token_sessiona);
306 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), 0);
307 free(token_sessiona);
308}
309
310static void
312{
313 struct test_context *ctx = (struct test_context *)*state;
314
315 CLEAR(ctx->up.username);
316 now = 0;
317
318 generate_auth_token(&ctx->up, &ctx->multi);
319 strcpy(ctx->up.password, ctx->multi.auth_token);
320 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), AUTH_TOKEN_HMAC_OK);
321
322 now = 100000;
323 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
325 strcpy(ctx->up.username, "test user name");
326
327 now = 0;
328 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
330
331 strcpy(ctx->up.username, "test user name");
332 now = 100000;
333 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
335
336 zerohmac(ctx->up.password);
337 assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), 0);
338}
339
340static void
342{
343 struct test_context *ctx = (struct test_context *)*state;
344
345 struct key_state *ks = &ctx->multi.session[TM_ACTIVE].key[KS_PRIMARY];
346
348 ctx->multi.auth_token = NULL;
349 add_session_token_env(ctx->session, &ctx->multi, &ctx->up);
350 assert_string_equal(last_session_state_value, "Initial");
351
352 /* This generated a fresh token with a new session ID,
353 * check that this session id is part of the multi initial token and
354 * found at the right index */
355 char *idx = strstr(ctx->multi.auth_token_initial, last_session_id_value);
356 assert_non_null(idx);
357
358 ptrdiff_t offset = idx - ctx->multi.auth_token_initial;
359 assert_int_equal(offset, 11);
360
363 strcpy(ctx->up.password, "somepassword");
364
365 free(ctx->multi.auth_token_initial);
366 ctx->multi.auth_token_initial = strdup(now0key0);
367
368 add_session_token_env(ctx->session, &ctx->multi, &ctx->up);
369 assert_string_equal(last_session_state_value, "Initial");
370 assert_string_equal(last_session_id_value, "0123456789abcdef");
371
373 free(ctx->multi.auth_token_initial);
374 ctx->multi.auth_token_initial = NULL;
375
377 strcpy(ctx->up.password, now0key0);
378 add_session_token_env(ctx->session, &ctx->multi, &ctx->up);
379 assert_string_equal(last_session_state_value, "Authenticated");
380 assert_string_equal(last_session_id_value, "0123456789abcdef");
381
383 add_session_token_env(ctx->session, &ctx->multi, &ctx->up);
384 assert_string_equal(last_session_state_value, "Expired");
385
387 add_session_token_env(ctx->session, &ctx->multi, &ctx->up);
388 assert_string_equal(last_session_state_value, "AuthenticatedEmptyUser");
389
392 add_session_token_env(ctx->session, &ctx->multi, &ctx->up);
393 assert_string_equal(last_session_state_value, "ExpiredEmptyUser");
394}
395
396static void
398{
399 struct test_context *ctx = (struct test_context *)*state;
400
401 now = 0x5c331e9c;
402 /* Preload the session id so the same session id is used here */
404 assert_non_null(ctx->multi.auth_token_initial);
405
408
409 /* Zero the hmac part to ensure we have a newly generated token */
411
412 generate_auth_token(&ctx->up, &ctx->multi);
413
414 assert_string_equal(random_token, ctx->multi.auth_token);
415
416 strcpy(ctx->up.password, ctx->multi.auth_token);
417 assert_true(verify_auth_token(&ctx->up, &ctx->multi, ctx->session));
418}
419
420
421static void
423{
424 /* Known-good tokens are recognised */
425 assert_true(is_auth_token(now0key0));
426 assert_true(is_auth_token(random_token));
427
428 /* Empty and clearly too-short strings are rejected */
429 assert_false(is_auth_token(""));
430 assert_false(is_auth_token("foobar"));
431 assert_false(is_auth_token(SESSION_ID_PREFIX));
432 assert_false(is_auth_token("SESS_ID_AT_Something_wild"));
433
434 /* Since all auth token have the same length, get the length from
435 * now0key0 */
436 const size_t auth_token_len = strlen(now0key0);
437
438 /* Right length, incorrect prefix */
439 char buf[256] = { 0 };
440 assert_true(auth_token_len < sizeof(buf));
441 memset(buf, 'A', auth_token_len);
442 buf[auth_token_len] = '\0';
443 assert_false(is_auth_token(buf));
444
445 /* Correct prefix to ensure the previous test is valid */
446 memcpy(buf, "SESS_ID_AT_", strlen("SESS_ID_AT_"));
447 assert_true(is_auth_token(buf));
448
449 /* Correct prefix, one byte too short */
450 buf[auth_token_len - 1] = '\0';
451 assert_false(is_auth_token(buf));
452
453 /* Correct prefix, one byte too long */
454 strcpy(buf, now0key0);
455 buf[auth_token_len] = 'A';
456 buf[auth_token_len + 1] = '\0';
457 assert_false(is_auth_token(buf));
458}
459
460static void
462{
463 struct test_context *ctx = (struct test_context *)*state;
464
467 strcpy(ctx->up.password, now0key0);
468 assert_true(verify_auth_token(&ctx->up, &ctx->multi, ctx->session));
469
472 assert_false(verify_auth_token(&ctx->up, &ctx->multi, ctx->session));
473}
474
475int
476main(void)
477{
479 const struct CMUnitTest tests[] = {
480 cmocka_unit_test_setup_teardown(auth_token_basic_test, setup, teardown),
481 cmocka_unit_test_setup_teardown(auth_token_fail_invalid_key, setup, teardown),
482 cmocka_unit_test_setup_teardown(auth_token_test_known_keys, setup, teardown),
483 cmocka_unit_test_setup_teardown(auth_token_test_empty_user, setup, teardown),
484 cmocka_unit_test_setup_teardown(auth_token_test_env, setup, teardown),
485 cmocka_unit_test_setup_teardown(auth_token_test_random_keys, setup, teardown),
486 cmocka_unit_test_setup_teardown(auth_token_test_key_load, setup, teardown),
487 cmocka_unit_test_setup_teardown(auth_token_test_timeout, setup, teardown),
488 cmocka_unit_test_setup_teardown(auth_token_test_session_mismatch, setup, teardown),
489 cmocka_unit_test(auth_token_test_is_auth_token),
490 };
491
492 return cmocka_run_group_tests_name("auth-token tests", tests, NULL, NULL);
493}
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: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:179
#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:309
static struct key_type auth_token_kt(void)
Definition auth_token.c:51
#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:57
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:416
bool is_auth_token(const char *password)
Return if the password string has the format of a password.
Definition auth_token.c:40
#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:109
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:996
void free_key_ctx(struct key_ctx *ctx)
Definition crypto.c:1081
#define MAX_HMAC_KEY_LENGTH
#define KS_PRIMARY
Primary key state index.
Definition ssl_common.h:464
#define TM_ACTIVE
Active tls_session.
Definition ssl_common.h:544
#define CLEAR(x)
Definition basic.h:32
#define streq(x, y)
Definition options.h:721
time_t now
Definition otime.c:33
#define AUTH_TOKEN_HMAC_OK
Auth-token sent from client has valid hmac.
Definition ssl_common.h:686
#define AUTH_TOKEN_EXPIRED
Auth-token sent from client has expired.
Definition ssl_common.h:688
#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:696
internal structure similar to struct key that holds key information but is not represented on wire an...
Definition crypto.h:163
Security parameter state of one TLS and data channel key session.
Definition ssl_common.h:208
unsigned int auth_token_state_flags
The state of the auth-token sent from the client.
Definition ssl_common.h:211
const char * digest
Message digest static parameters.
Definition crypto.h:143
Container for unidirectional cipher and HMAC key material.
Definition crypto.h:152
uint8_t hmac[MAX_HMAC_KEY_LENGTH]
Key material for HMAC operations.
Definition crypto.h:155
struct user_pass up
struct tls_multi multi
struct tls_session * session
struct key_type kt
Security parameter state for a single VPN tunnel.
Definition ssl_common.h:611
char * auth_token_initial
The first auth-token we sent to a client.
Definition ssl_common.h:683
struct tls_options opt
Definition ssl_common.h:616
struct tls_session session[TM_SIZE]
Array of tls_session objects representing control channel sessions with the remote peer.
Definition ssl_common.h:711
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:678
struct key_ctx auth_token_key
Definition ssl_common.h:407
unsigned int auth_token_renewal
Definition ssl_common.h:405
unsigned int auth_token_lifetime
Definition ssl_common.h:404
interval_t renegotiate_seconds
Definition ssl_common.h:349
bool auth_token_generate
Generate auth-tokens on successful user/pass auth,seet via options->auth_token_generate.
Definition ssl_common.h:400
Security parameter state of a single session within a VPN tunnel.
Definition ssl_common.h:489
struct key_state key[KS_SIZE]
Definition ssl_common.h:524
struct tls_options * opt
Definition ssl_common.h:491
char password[USER_PASS_LEN]
Definition misc.h:71
char username[USER_PASS_LEN]
Definition misc.h:70
static void auth_token_test_is_auth_token(void **state)
static void auth_token_test_env(void **state)
static int teardown(void **state)
void auth_set_client_reason(struct tls_multi *multi, const char *reason)
Sets the reason why authentication of a client failed.
static void auth_token_test_known_keys(void **state)
static const char * zeroinline
static void auth_token_test_key_load(void **state)
static const char * random_token
static const char * now0key0
static void auth_token_test_timeout(void **state)
static void auth_token_test_random_keys(void **state)
void auth_token_test_session_mismatch(void **state)
static void auth_token_test_empty_user(void **state)
int main(void)
void setenv_str(struct env_set *es, const char *name, const char *value)
static void auth_token_fail_invalid_key(void **state)
static const char * last_session_state_value
static const char * random_key
static int setup(void **state)
static void auth_token_basic_test(void **state)
static const char * allx01inline
static void zerohmac(char *token)
static char last_session_id_value[64]
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.
static void openvpn_unit_test_setup(void)
Sets up the environment for unit tests like making both stderr and stdout non-buffered to avoid messa...
Definition test_common.h:61
struct env_set * es