OpenVPN
test_user_pass.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) 2023-2025 OpenVPN Inc <sales@openvpn.net>
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 as published by the
12 * Free Software Foundation, either version 2 of the License,
13 * or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, see <https://www.gnu.org/licenses/>.
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include "syshead.h"
29#include "manage.h"
30
31#include <stdlib.h>
32#include <string.h>
33#include <setjmp.h>
34#include <cmocka.h>
35#include "test_common.h"
36
37#include "misc.c"
38
39struct management *management; /* global */
40
41/* mocking */
42#if defined(ENABLE_SYSTEMD)
43bool
44query_user_exec_systemd(void)
45{
47}
48#endif
49bool
51{
52 /* Loop through configured query_user slots */
53 for (int i = 0; i < QUERY_USER_NUMSLOTS && query_user[i].response != NULL; i++)
54 {
55 check_expected(query_user[i].prompt);
56 strncpy(query_user[i].response, mock_ptr_type(char *), query_user[i].response_len);
57 }
58
59 return mock();
60}
61void
62management_auth_failure(struct management *man, const char *type, const char *reason)
63{
64 assert_true(0);
65}
66bool
67management_query_user_pass(struct management *man, struct user_pass *up, const char *type,
68 const unsigned int flags, const char *static_challenge)
69{
70 assert_true(0);
71 return false;
72}
73/* stubs for some unused functions instead of pulling in too many dependencies */
74int
75parse_line(const char *line, char **p, const int n, const char *file, const int line_num,
76 msglvl_t msglevel, struct gc_arena *gc)
77{
78 assert_true(0);
79 return 0;
80}
81
82#ifdef _WIN32
83bool
84protect_buffer_win32(char *buf, DWORD len)
85{
86 return true;
87}
88
89bool
90unprotect_buffer_win32(char *buf, DWORD len)
91{
92 return true;
93}
94#endif
95
96/* tooling */
97static void
99{
100 up->defined = false;
101 up->token_defined = false;
102 up->nocache = false;
103 strcpy(up->username, "user");
104 strcpy(up->password, "password");
105}
106
107static void
109{
110 struct user_pass up = { 0 };
111 reset_user_pass(&up);
112 up.defined = true;
113 assert_true(get_user_pass_cr(&up, NULL, "UT", 0, NULL));
114}
115
116static void
118{
119 struct user_pass up = { 0 };
120 reset_user_pass(&up);
121 unsigned int flags = GET_USER_PASS_NEED_OK;
122
123 expect_string(query_user_exec_builtin, query_user[i].prompt, "NEED-OK|UT|user:");
124 will_return(query_user_exec_builtin, "");
125 will_return(query_user_exec_builtin, true);
126 /*FIXME: query_user_exec() called even though nothing queued */
127 will_return(query_user_exec_builtin, true);
128 assert_true(get_user_pass_cr(&up, NULL, "UT", flags, NULL));
129 assert_true(up.defined);
130 assert_string_equal(up.password, "ok");
131
132 reset_user_pass(&up);
133
134 expect_string(query_user_exec_builtin, query_user[i].prompt, "NEED-OK|UT|user:");
135 will_return(query_user_exec_builtin, "cancel");
136 will_return(query_user_exec_builtin, true);
137 /*FIXME: query_user_exec() called even though nothing queued */
138 will_return(query_user_exec_builtin, true);
139 assert_true(get_user_pass_cr(&up, NULL, "UT", flags, NULL));
140 assert_true(up.defined);
141 assert_string_equal(up.password, "cancel");
142}
143
144static void
146{
147 struct user_pass up = { 0 };
148 reset_user_pass(&up);
149 unsigned int flags = GET_USER_PASS_INLINE_CREDS;
150
151 /*FIXME: query_user_exec() called even though nothing queued */
152 will_return(query_user_exec_builtin, true);
153 assert_true(get_user_pass_cr(&up, "iuser\nipassword", "UT", flags, NULL));
154 assert_true(up.defined);
155 assert_string_equal(up.username, "iuser");
156 assert_string_equal(up.password, "ipassword");
157
158 reset_user_pass(&up);
159
160 /* Test various valid characters */
161 /*FIXME: query_user_exec() called even though nothing queued */
162 will_return(query_user_exec_builtin, true);
163 /* FIXME? content after first two lines just ignored */
164 assert_true(
165 get_user_pass_cr(&up, "#iuser and 커뮤니티\n//ipasswörd!\nsome other content\nnot relevant",
166 "UT", flags, NULL));
167 assert_true(up.defined);
168 assert_string_equal(up.username, "#iuser and 커뮤니티");
169 assert_string_equal(up.password, "//ipasswörd!");
170
171 reset_user_pass(&up);
172
173 /* Test various invalid characters */
174 /*FIXME: query_user_exec() called even though nothing queued */
175 will_return(query_user_exec_builtin, true);
176 /*FIXME? allows arbitrary crap if c > 127 */
177 /*FIXME? silently removes control characters */
178 assert_true(get_user_pass_cr(&up, "\tiuser\r\nipass\xffwo\x1erd", "UT", flags, NULL));
179 assert_true(up.defined);
180 assert_string_equal(up.username, "iuser");
181 assert_string_equal(up.password, "ipass\xffword");
182
183 reset_user_pass(&up);
184
185 /*FIXME: query_user_exec() called even though nothing queued */
186 will_return(query_user_exec_builtin, true);
187 /*FIXME: silently removes control characters but does not error out */
188 assert_true(get_user_pass_cr(&up, "\t\n\t", "UT", flags, NULL));
189 assert_true(up.defined);
190 assert_string_equal(up.username, "");
191 assert_string_equal(up.password, "");
192
193 reset_user_pass(&up);
194
195 expect_string(query_user_exec_builtin, query_user[i].prompt, "Enter UT Password:");
196 will_return(query_user_exec_builtin, "cpassword");
197 will_return(query_user_exec_builtin, true);
198 /* will try to retrieve missing password from stdin */
199 assert_true(get_user_pass_cr(&up, "iuser", "UT", flags, NULL));
200 assert_true(up.defined);
201 assert_string_equal(up.username, "iuser");
202 assert_string_equal(up.password, "cpassword");
203
204 reset_user_pass(&up);
205
207 /*FIXME: query_user_exec() called even though nothing queued */
208 will_return(query_user_exec_builtin, true);
209 assert_true(get_user_pass_cr(&up, "ipassword\n", "UT", flags, NULL));
210 assert_true(up.defined);
211 assert_string_equal(up.username, "user");
212 assert_string_equal(up.password, "ipassword");
213
214 reset_user_pass(&up);
215
217 expect_string(query_user_exec_builtin, query_user[i].prompt, "Enter UT Password:");
218 will_return(query_user_exec_builtin, "cpassword");
219 will_return(query_user_exec_builtin, true);
220 /* will try to retrieve missing password from stdin */
221 assert_true(get_user_pass_cr(&up, "", "UT", flags, NULL));
222 assert_true(up.defined);
223 assert_string_equal(up.username, "user");
224 assert_string_equal(up.password, "cpassword");
225}
226
227/* NOTE: expect_assert_failure does not seem to work with MSVC */
228#ifndef _MSC_VER
229/* NOTE: leaks gc memory */
230static void
232{
233 struct user_pass up = { 0 };
234 reset_user_pass(&up);
235 unsigned int flags = GET_USER_PASS_INLINE_CREDS;
236
237 reset_user_pass(&up);
238
239 /*FIXME: query_user_exec() called even though nothing queued */
240 /*FIXME? username error thrown very late in stdin handling */
241 will_return(query_user_exec_builtin, true);
242 expect_assert_failure(get_user_pass_cr(&up, "\nipassword\n", "UT", flags, NULL));
243}
244#endif
245
246static void
248{
249 struct user_pass up = { 0 };
250 reset_user_pass(&up);
251 unsigned int flags = 0;
252
253 expect_string(query_user_exec_builtin, query_user[i].prompt, "Enter UT Username:");
254 expect_string(query_user_exec_builtin, query_user[i].prompt, "Enter UT Password:");
255 will_return(query_user_exec_builtin, "cuser");
256 will_return(query_user_exec_builtin, "cpassword");
257 will_return(query_user_exec_builtin, true);
258 assert_true(get_user_pass_cr(&up, "stdin", "UT", flags, NULL));
259 assert_true(up.defined);
260 assert_string_equal(up.username, "cuser");
261 assert_string_equal(up.password, "cpassword");
262
263 reset_user_pass(&up);
264
266 expect_string(query_user_exec_builtin, query_user[i].prompt, "Enter UT Password:");
267 will_return(query_user_exec_builtin, "cpassword");
268 will_return(query_user_exec_builtin, true);
269 assert_true(get_user_pass_cr(&up, "stdin", "UT", flags, NULL));
270 assert_true(up.defined);
271 assert_string_equal(up.username, "user");
272 assert_string_equal(up.password, "cpassword");
273
274 reset_user_pass(&up);
275
277 expect_string(query_user_exec_builtin, query_user[i].prompt, "Enter UT Password:");
278 will_return(query_user_exec_builtin, "");
279 will_return(query_user_exec_builtin, true);
280 /*FIXME? does not error out on empty password */
281 assert_true(get_user_pass_cr(&up, "stdin", "UT", flags, NULL));
282 assert_true(up.defined);
283 assert_string_equal(up.username, "user");
284 assert_string_equal(up.password, "");
285}
286
287/* NOTE: expect_assert_failure does not seem to work with MSVC */
288#ifndef _MSC_VER
289/* NOTE: leaks gc memory */
290static void
292{
293 struct user_pass up = { 0 };
294 reset_user_pass(&up);
295 unsigned int flags = 0;
296
297 expect_string(query_user_exec_builtin, query_user[i].prompt, "Enter UT Username:");
298 expect_string(query_user_exec_builtin, query_user[i].prompt, "Enter UT Password:");
299 will_return(query_user_exec_builtin, "");
300 will_return(query_user_exec_builtin, "cpassword");
301 will_return(query_user_exec_builtin, true);
302 expect_assert_failure(get_user_pass_cr(&up, "stdin", "UT", flags, NULL));
303}
304#endif
305
306static void
308{
309 struct user_pass up = { 0 };
310 reset_user_pass(&up);
311 unsigned int flags = 0;
312
313 char authfile[PATH_MAX] = { 0 };
314 openvpn_test_get_srcdir_dir(authfile, PATH_MAX, "input/user_pass.txt");
315
316 /*FIXME: query_user_exec() called even though nothing queued */
317 will_return(query_user_exec_builtin, true);
318 assert_true(get_user_pass_cr(&up, authfile, "UT", flags, NULL));
319 assert_true(up.defined);
320 assert_string_equal(up.username, "fuser");
321 assert_string_equal(up.password, "fpassword");
322
323 reset_user_pass(&up);
324
325 openvpn_test_get_srcdir_dir(authfile, PATH_MAX, "input/appears_empty.txt");
326 /*FIXME: query_user_exec() called even though nothing queued */
327 will_return(query_user_exec_builtin, true);
328 /*FIXME? does not error out */
329 assert_true(get_user_pass_cr(&up, authfile, "UT", flags, NULL));
330 assert_true(up.defined);
331 assert_string_equal(up.username, "");
332 assert_string_equal(up.password, "");
333
334 reset_user_pass(&up);
335
336 openvpn_test_get_srcdir_dir(authfile, PATH_MAX, "input/user_only.txt");
337 expect_string(query_user_exec_builtin, query_user[i].prompt, "Enter UT Password:");
338 will_return(query_user_exec_builtin, "cpassword");
339 will_return(query_user_exec_builtin, true);
340 /* will try to retrieve missing password from stdin */
341 assert_true(get_user_pass_cr(&up, authfile, "UT", flags, NULL));
342 assert_true(up.defined);
343 assert_string_equal(up.username, "fuser");
344 assert_string_equal(up.password, "cpassword");
345
346 reset_user_pass(&up);
347
349 openvpn_test_get_srcdir_dir(authfile, PATH_MAX, "input/user_only.txt");
350 /*FIXME: query_user_exec() called even though nothing queued */
351 will_return(query_user_exec_builtin, true);
352 assert_true(get_user_pass_cr(&up, authfile, "UT", flags, NULL));
353 assert_true(up.defined);
354 assert_string_equal(up.username, "user");
355 assert_string_equal(up.password, "fuser");
356}
357
358#ifdef ENABLE_MANAGEMENT
359static void
361{
362 struct user_pass up = { 0 };
363 reset_user_pass(&up);
364 const char *challenge = "CRV1:R,E:Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l:Y3Ix:Please enter token PIN";
365 unsigned int flags = GET_USER_PASS_DYNAMIC_CHALLENGE;
366
367 expect_string(query_user_exec_builtin, query_user[i].prompt,
368 "CHALLENGE: Please enter token PIN");
369 will_return(query_user_exec_builtin, "challenge_response");
370 will_return(query_user_exec_builtin, true);
371 assert_true(get_user_pass_cr(&up, NULL, "UT", flags, challenge));
372 assert_true(up.defined);
373 assert_string_equal(up.username, "cr1");
374 assert_string_equal(up.password, "CRV1::Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l::challenge_response");
375}
376
377static void
379{
380 struct user_pass up = { 0 };
381 reset_user_pass(&up);
382 const char *challenge = "Please enter token PIN";
383 unsigned int flags = GET_USER_PASS_STATIC_CHALLENGE;
384
385 expect_string(query_user_exec_builtin, query_user[i].prompt, "Enter UT Username:");
386 will_return(query_user_exec_builtin, "cuser");
387 expect_string(query_user_exec_builtin, query_user[i].prompt, "Enter UT Password:");
388 will_return(query_user_exec_builtin, "cpassword");
389 will_return(query_user_exec_builtin, true);
390 expect_string(query_user_exec_builtin, query_user[i].prompt,
391 "CHALLENGE: Please enter token PIN");
392 will_return(query_user_exec_builtin, "challenge_response");
393 will_return(query_user_exec_builtin, true);
394 assert_true(get_user_pass_cr(&up, NULL, "UT", flags, challenge));
395 assert_true(up.defined);
396 assert_string_equal(up.username, "cuser");
397 /* SCRV1:cpassword:challenge_response but base64-encoded */
398 assert_string_equal(up.password, "SCRV1:Y3Bhc3N3b3Jk:Y2hhbGxlbmdlX3Jlc3BvbnNl");
399
400 reset_user_pass(&up);
401
403
404 expect_string(query_user_exec_builtin, query_user[i].prompt, "Enter UT Username:");
405 will_return(query_user_exec_builtin, "c1user");
406 expect_string(query_user_exec_builtin, query_user[i].prompt, "Enter UT Password:");
407 will_return(query_user_exec_builtin, "c1password");
408 will_return(query_user_exec_builtin, true);
409 expect_string(query_user_exec_builtin, query_user[i].prompt,
410 "CHALLENGE: Please enter token PIN");
411 will_return(query_user_exec_builtin, "0123456");
412 will_return(query_user_exec_builtin, true);
413 assert_true(get_user_pass_cr(&up, NULL, "UT", flags, challenge));
414 assert_true(up.defined);
415 assert_string_equal(up.username, "c1user");
416 /* password and response concatenated */
417 assert_string_equal(up.password, "c1password0123456");
418
419 reset_user_pass(&up);
420
422
423 /*FIXME: query_user_exec() called even though nothing queued */
424 will_return(query_user_exec_builtin, true);
425 expect_string(query_user_exec_builtin, query_user[i].prompt,
426 "CHALLENGE: Please enter token PIN");
427 will_return(query_user_exec_builtin, "challenge_response");
428 will_return(query_user_exec_builtin, true);
429 assert_true(get_user_pass_cr(&up, "iuser\nipassword", "UT", flags, challenge));
430 assert_true(up.defined);
431 assert_string_equal(up.username, "iuser");
432 /* SCRV1:ipassword:challenge_response but base64-encoded */
433 assert_string_equal(up.password, "SCRV1:aXBhc3N3b3Jk:Y2hhbGxlbmdlX3Jlc3BvbnNl");
434}
435#endif /* ENABLE_MANAGEMENT */
436
437/* NOTE: expect_assert_failure does not seem to work with MSVC */
438#ifndef _MSC_VER
439/* NOTE: leaks gc memory */
440static void
442{
443 struct user_pass up = { 0 };
444 reset_user_pass(&up);
445 unsigned int flags = 0;
446
447 char authfile[PATH_MAX] = { 0 };
448
449 openvpn_test_get_srcdir_dir(authfile, PATH_MAX, "input/empty.txt");
450 expect_assert_failure(get_user_pass_cr(&up, authfile, "UT", flags, NULL));
451
452 reset_user_pass(&up);
453
455 openvpn_test_get_srcdir_dir(authfile, PATH_MAX, "input/empty.txt");
456 expect_assert_failure(get_user_pass_cr(&up, authfile, "UT", flags, NULL));
457}
458#endif /* ifndef _MSC_VER */
459
460const struct CMUnitTest user_pass_tests[] = {
461 cmocka_unit_test(test_get_user_pass_defined),
462 cmocka_unit_test(test_get_user_pass_needok),
463 cmocka_unit_test(test_get_user_pass_inline_creds),
464 cmocka_unit_test(test_get_user_pass_authfile_stdin),
465 cmocka_unit_test(test_get_user_pass_authfile_file),
466#ifdef ENABLE_MANAGEMENT
467 cmocka_unit_test(test_get_user_pass_dynamic_challenge),
468 cmocka_unit_test(test_get_user_pass_static_challenge),
469#endif /* ENABLE_MANAGEMENT */
470#ifndef _MSC_VER
474#endif
475};
476
477int
478main(void)
479{
481 return cmocka_run_group_tests(user_pass_tests, NULL, NULL);
482}
struct _query_user query_user[QUERY_USER_NUMSLOTS]
Global variable, declared in console.c.
Definition console.c:40
#define QUERY_USER_NUMSLOTS
Definition console.h:41
bool get_user_pass_cr(struct user_pass *up, const char *auth_file, const char *prefix, const unsigned int flags, const char *auth_challenge)
Retrieves the user credentials from various sources depending on the flags.
Definition misc.c:201
#define GET_USER_PASS_STATIC_CHALLENGE_CONCAT
indicates password and response should be concatenated
Definition misc.h:125
#define GET_USER_PASS_PASSWORD_ONLY
Definition misc.h:112
#define GET_USER_PASS_INLINE_CREDS
indicates that auth_file is actually inline creds
Definition misc.h:123
#define GET_USER_PASS_STATIC_CHALLENGE
SCRV1 protocol – static challenge.
Definition misc.h:119
#define GET_USER_PASS_NEED_OK
Definition misc.h:113
#define GET_USER_PASS_DYNAMIC_CHALLENGE
CRV1 protocol – dynamic challenge.
Definition misc.h:118
unsigned int msglvl_t
Definition error.h:77
char * response
The user's response.
Definition console.h:36
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:116
bool token_defined
Definition misc.h:56
bool defined
Definition misc.h:53
char password[USER_PASS_LEN]
Definition misc.h:68
bool nocache
Definition misc.h:57
char username[USER_PASS_LEN]
Definition misc.h:67
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:35
void openvpn_test_get_srcdir_dir(char *buf, size_t bufsize, const char *filename)
Helper function to get a file path from the unit test directory to open it or pass its path to anothe...
Definition test_common.h:53
struct gc_arena gc
Definition test_ssl.c:131
void management_auth_failure(struct management *man, const char *type, const char *reason)
struct management * management
int parse_line(const char *line, char **p, const int n, const char *file, const int line_num, msglvl_t msglevel, struct gc_arena *gc)
static void reset_user_pass(struct user_pass *up)
const struct CMUnitTest user_pass_tests[]
bool query_user_exec_builtin(void)
Loop through configured query_user slots, using the built-in method for querying the user.
bool protect_buffer_win32(char *buf, DWORD len)
Encrypt a region of memory using CryptProtectMemory() with access restricted to the current process.
static void test_get_user_pass_authfile_file_assertions(void **state)
bool unprotect_buffer_win32(char *buf, DWORD len)
Decrypt a previously encrypted region of memory using CryptUnProtectMemory() with access restricted t...
static void test_get_user_pass_defined(void **state)
static void test_get_user_pass_static_challenge(void **state)
bool management_query_user_pass(struct management *man, struct user_pass *up, const char *type, const unsigned int flags, const char *static_challenge)
int main(void)
static void test_get_user_pass_authfile_stdin_assertions(void **state)
static void test_get_user_pass_authfile_stdin(void **state)
static void test_get_user_pass_needok(void **state)
static void test_get_user_pass_authfile_file(void **state)
static void test_get_user_pass_dynamic_challenge(void **state)
static void test_get_user_pass_inline_creds(void **state)
static void test_get_user_pass_inline_creds_assertions(void **state)