blob: 70f5ba30eed7091c4f798616c6a5fd00d2ccac91 [file] [log] [blame]
romanc1d2b092023-02-02 08:58:27 +01001/**
2 * @file test_auth.c
3 * @author Roman Janota <xjanot04@fit.vutbr.cz>
romand348b942023-10-13 14:32:19 +02004 * @brief libnetconf2 Linux PAM keyboard-interactive authentication test
romanc1d2b092023-02-02 08:58:27 +01005 *
6 * @copyright
7 * Copyright (c) 2022 CESNET, z.s.p.o.
8 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
roman9b1379c2023-03-31 10:11:10 +020016#define _GNU_SOURCE
17
romanc1d2b092023-02-02 08:58:27 +010018#include <errno.h>
19#include <pthread.h>
20#include <setjmp.h>
roman9b1379c2023-03-31 10:11:10 +020021#include <stdarg.h>
romanc1d2b092023-02-02 08:58:27 +010022#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include <cmocka.h>
27
romanc1d2b092023-02-02 08:58:27 +010028#include "tests/config.h"
29
roman83683fb2023-02-24 09:15:23 +010030#define NC_ACCEPT_TIMEOUT 2000
31#define NC_PS_POLL_TIMEOUT 2000
romanc1d2b092023-02-02 08:58:27 +010032
33struct ly_ctx *ctx;
34
35struct test_state {
romanc1d2b092023-02-02 08:58:27 +010036 pthread_barrier_t barrier;
37};
38
romanc1d2b092023-02-02 08:58:27 +010039static void *
40server_thread(void *arg)
41{
42 int ret;
43 NC_MSG_TYPE msgtype;
44 struct nc_session *session;
45 struct nc_pollsession *ps;
46 struct test_state *state = arg;
47
48 (void) arg;
49
50 ps = nc_ps_new();
51 assert_non_null(ps);
52
53 /* accept a session and add it to the poll session structure */
54 pthread_barrier_wait(&state->barrier);
55 msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session);
56 assert_int_equal(msgtype, NC_MSG_HELLO);
57
58 ret = nc_ps_add_session(ps, session);
59 assert_int_equal(ret, 0);
60
61 do {
62 ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
63 assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC);
64 } while (!(ret & NC_PSPOLL_SESSION_TERM));
65
66 nc_ps_clear(ps, 1, NULL);
67 nc_ps_free(ps);
romanc1d2b092023-02-02 08:58:27 +010068 return NULL;
69}
70
romanc1d2b092023-02-02 08:58:27 +010071static char *
72auth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv)
73{
74 (void) instruction;
75 (void) echo;
76 (void) auth_name;
77 (void) priv;
78
79 /* send the replies to keyboard-interactive authentication */
80 if (strstr(prompt, "backwards")) {
81 return strdup("tni_tset");
82 } else if (strstr(prompt, "1+1")) {
83 return strdup("2");
84 } else {
85 return NULL;
86 }
87}
88
89static void *
90client_thread_interactive(void *arg)
91{
92 int ret;
93 struct nc_session *session = NULL;
94 struct test_state *state = arg;
95
roman472420c2023-04-24 16:28:09 +020096 /* skip all hostkey and known_hosts checks */
97 nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);
98
romanc1d2b092023-02-02 08:58:27 +010099 ret = nc_client_set_schema_searchpath(MODULES_DIR);
100 assert_int_equal(ret, 0);
101
102 ret = nc_client_ssh_set_username("test_int");
103 assert_int_equal(ret, 0);
104
105 /* set keyboard-interactive authentication callback */
106 nc_client_ssh_set_auth_interactive_clb(auth_interactive, NULL);
107
108 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1);
109 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -1);
110 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, 1);
111
112 pthread_barrier_wait(&state->barrier);
113 session = nc_connect_ssh("127.0.0.1", 10005, NULL);
114 assert_non_null(session);
115
116 nc_session_free(session, NULL);
romanc1d2b092023-02-02 08:58:27 +0100117 return NULL;
118}
119
120static void
121test_nc_auth_interactive(void **state)
122{
123 int ret, i;
124 pthread_t tids[2];
125
126 assert_non_null(state);
127
128 ret = pthread_create(&tids[0], NULL, client_thread_interactive, *state);
129 assert_int_equal(ret, 0);
130 ret = pthread_create(&tids[1], NULL, server_thread, *state);
131 assert_int_equal(ret, 0);
132
133 for (i = 0; i < 2; i++) {
134 pthread_join(tids[i], NULL);
135 }
136}
137
138static char *
139auth_password(const char *username, const char *hostname, void *priv)
140{
141 (void) hostname;
142 (void) priv;
143
roman9b1379c2023-03-31 10:11:10 +0200144 /* set the reply to password authentication */
romanc1d2b092023-02-02 08:58:27 +0100145 if (!strcmp(username, "test_pw")) {
146 return strdup("testpw");
147 } else {
148 return NULL;
149 }
150}
151
152static void *
153client_thread_password(void *arg)
154{
155 int ret;
156 struct nc_session *session = NULL;
157 struct test_state *state = arg;
158
roman472420c2023-04-24 16:28:09 +0200159 /* skip all hostkey and known_hosts checks */
160 nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);
161
romanc1d2b092023-02-02 08:58:27 +0100162 ret = nc_client_set_schema_searchpath(MODULES_DIR);
163 assert_int_equal(ret, 0);
164
165 ret = nc_client_ssh_set_username("test_pw");
166 assert_int_equal(ret, 0);
167
168 nc_client_ssh_set_auth_password_clb(auth_password, NULL);
169
170 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1);
171 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, 1);
172 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, -1);
173
174 pthread_barrier_wait(&state->barrier);
175 session = nc_connect_ssh("127.0.0.1", 10005, NULL);
176 assert_non_null(session);
177
178 nc_session_free(session, NULL);
romanc1d2b092023-02-02 08:58:27 +0100179 return NULL;
180}
181
182static void
183test_nc_auth_password(void **state)
184{
185 int ret, i;
186 pthread_t tids[2];
187
188 assert_non_null(state);
189
190 ret = pthread_create(&tids[0], NULL, client_thread_password, *state);
191 assert_int_equal(ret, 0);
192 ret = pthread_create(&tids[1], NULL, server_thread, *state);
193 assert_int_equal(ret, 0);
194
195 for (i = 0; i < 2; i++) {
196 pthread_join(tids[i], NULL);
197 }
198}
199
200static void *
201client_thread_pubkey(void *arg)
202{
203 int ret;
204 struct nc_session *session = NULL;
205 struct test_state *state = arg;
206
roman472420c2023-04-24 16:28:09 +0200207 /* skip all hostkey and known_hosts checks */
208 nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);
209
romanc1d2b092023-02-02 08:58:27 +0100210 ret = nc_client_set_schema_searchpath(MODULES_DIR);
211 assert_int_equal(ret, 0);
212
213 ret = nc_client_ssh_set_username("test_pk");
214 assert_int_equal(ret, 0);
215
216 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 1);
217 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -1);
218 nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, -1);
219
220 ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/key_rsa.pub", TESTS_DIR "/data/key_rsa");
221 assert_int_equal(ret, 0);
222
223 pthread_barrier_wait(&state->barrier);
224 session = nc_connect_ssh("127.0.0.1", 10005, NULL);
225 assert_non_null(session);
226
227 nc_session_free(session, NULL);
romanc1d2b092023-02-02 08:58:27 +0100228 return NULL;
229}
230
231static void
232test_nc_auth_pubkey(void **state)
233{
234 int ret, i;
235 pthread_t tids[2];
236
237 assert_non_null(state);
238
239 ret = pthread_create(&tids[0], NULL, client_thread_pubkey, *state);
240 assert_int_equal(ret, 0);
241 ret = pthread_create(&tids[1], NULL, server_thread, *state);
242 assert_int_equal(ret, 0);
243
244 for (i = 0; i < 2; i++) {
245 pthread_join(tids[i], NULL);
246 }
247}
248
249static void *
250client_thread_none(void *arg)
251{
252 int ret;
253 struct nc_session *session = NULL;
254 struct test_state *state = arg;
255
roman472420c2023-04-24 16:28:09 +0200256 /* skip all hostkey and known_hosts checks */
257 nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);
258
romanc1d2b092023-02-02 08:58:27 +0100259 ret = nc_client_set_schema_searchpath(MODULES_DIR);
260 assert_int_equal(ret, 0);
261
262 ret = nc_client_ssh_set_username("test_none");
263 assert_int_equal(ret, 0);
264
265 pthread_barrier_wait(&state->barrier);
266 session = nc_connect_ssh("127.0.0.1", 10005, NULL);
267 assert_non_null(session);
268
269 nc_session_free(session, NULL);
romanc1d2b092023-02-02 08:58:27 +0100270 return NULL;
271}
272
273static void
274test_nc_auth_none(void **state)
275{
276 int ret, i;
277 pthread_t tids[2];
278
279 assert_non_null(state);
280
281 ret = pthread_create(&tids[0], NULL, client_thread_none, *state);
282 assert_int_equal(ret, 0);
283 ret = pthread_create(&tids[1], NULL, server_thread, *state);
284 assert_int_equal(ret, 0);
285
286 for (i = 0; i < 2; i++) {
287 pthread_join(tids[i], NULL);
288 }
289}
290
291static int
292setup_f(void **state)
293{
294 int ret;
roman142718b2023-06-29 09:15:29 +0200295 struct lyd_node *tree = NULL;
romanc1d2b092023-02-02 08:58:27 +0100296 struct test_state *test_state;
297
298 nc_verbosity(NC_VERB_VERBOSE);
299
300 /* init barrier */
301 test_state = malloc(sizeof *test_state);
302 assert_non_null(test_state);
303
304 ret = pthread_barrier_init(&test_state->barrier, NULL, 2);
305 assert_int_equal(ret, 0);
306
307 *state = test_state;
308
309 ret = ly_ctx_new(MODULES_DIR, 0, &ctx);
310 assert_int_equal(ret, 0);
311
312 ret = nc_server_init_ctx(&ctx);
313 assert_int_equal(ret, 0);
314
315 ret = nc_server_config_load_modules(&ctx);
316 assert_int_equal(ret, 0);
317
Roytakb2794852023-10-18 14:30:22 +0200318 ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_LIBSSH, "127.0.0.1", 10005, &tree);
roman142718b2023-06-29 09:15:29 +0200319 assert_int_equal(ret, 0);
320
Roytakb2794852023-10-18 14:30:22 +0200321 ret = nc_server_config_add_ssh_hostkey(ctx, "endpt", "hostkey", TESTS_DIR "/data/key_ecdsa", NULL, &tree);
roman142718b2023-06-29 09:15:29 +0200322 assert_int_equal(ret, 0);
323
Roytakb2794852023-10-18 14:30:22 +0200324 ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt", "test_pk", "pubkey", TESTS_DIR "/data/key_rsa.pub", &tree);
roman142718b2023-06-29 09:15:29 +0200325 assert_int_equal(ret, 0);
326
Roytakb2794852023-10-18 14:30:22 +0200327 ret = nc_server_config_add_ssh_user_interactive(ctx, "endpt", "test_int", "netconf.conf", BUILD_DIR "/tests", &tree);
roman142718b2023-06-29 09:15:29 +0200328 assert_int_equal(ret, 0);
329
Roytakb2794852023-10-18 14:30:22 +0200330 ret = nc_server_config_add_ssh_user_password(ctx, "endpt", "test_pw", "testpw", &tree);
roman142718b2023-06-29 09:15:29 +0200331 assert_int_equal(ret, 0);
332
romane6ec60e2023-10-19 15:21:52 +0200333 ret = lyd_new_path(tree, ctx, "/ietf-netconf-server:netconf-server/listen/endpoint[name='endpt']/ssh/ssh-server-parameters/"
334 "client-authentication/users/user[name='test_none']/none", NULL, 0, NULL);
romanc1d2b092023-02-02 08:58:27 +0100335 assert_int_equal(ret, 0);
336
337 /* configure the server based on the data */
roman142718b2023-06-29 09:15:29 +0200338 ret = nc_server_config_setup_data(tree);
romanc1d2b092023-02-02 08:58:27 +0100339 assert_int_equal(ret, 0);
340
romanc1d2b092023-02-02 08:58:27 +0100341 ret = nc_server_init();
342 assert_int_equal(ret, 0);
343
romaneadc4782023-09-14 10:10:08 +0200344 /* initialize client */
345 ret = nc_client_init();
346 assert_int_equal(ret, 0);
347
romanc1d2b092023-02-02 08:58:27 +0100348 lyd_free_all(tree);
349
350 return 0;
351}
352
353static int
354teardown_f(void **state)
355{
356 int ret = 0;
357 struct test_state *test_state;
358
359 assert_non_null(state);
360 test_state = *state;
361
362 ret = pthread_barrier_destroy(&test_state->barrier);
363 assert_int_equal(ret, 0);
364
365 free(*state);
366 nc_client_destroy();
367 nc_server_destroy();
368 ly_ctx_destroy(ctx);
369
370 return 0;
371}
372
373int
374main(void)
375{
376 const struct CMUnitTest tests[] = {
377 cmocka_unit_test_setup_teardown(test_nc_auth_interactive, setup_f, teardown_f),
378 cmocka_unit_test_setup_teardown(test_nc_auth_pubkey, setup_f, teardown_f),
379 cmocka_unit_test_setup_teardown(test_nc_auth_password, setup_f, teardown_f),
380 cmocka_unit_test_setup_teardown(test_nc_auth_none, setup_f, teardown_f)
381 };
382
383 setenv("CMOCKA_TEST_ABORT", "1", 1);
384 return cmocka_run_group_tests(tests, NULL, NULL);
385}