blob: 53360c6ddfe84b15f4903233606d324361b4540e [file] [log] [blame]
roman581b2502023-08-18 15:57:02 +02001/**
2 * @file test_runtime_changes.c
3 * @author Roman Janota <janota@cesnet.cz>
4 * @brief libnetconf2 Runtime changes test.
5 *
6 * @copyright
7 * Copyright (c) 2023 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
16#define _GNU_SOURCE
17
18#include <pthread.h>
19#include <setjmp.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include <cmocka.h>
25
26#include "tests/config.h"
27
28#define NC_ACCEPT_TIMEOUT 2000
29#define NC_PS_POLL_TIMEOUT 2000
30
31struct test_state {
32 pthread_barrier_t start_barrier;
33 pthread_barrier_t end_barrier;
34 struct lyd_node *tree;
35};
36
37typedef enum {
38 NC_TEST_EXPECT_FAIL,
39 NC_TEST_EXPECT_OK
40} NC_TEST_EXPECT;
41
42typedef enum {
43 NC_TEST_STATE_END,
44 NC_TEST_STATE_RUN
45} NC_TEST_STATE;
46
47struct ly_ctx *ctx;
48int test_running;
49int expect_ok;
50
51static void *
52server_thread(void *arg)
53{
54 int ret;
55 NC_MSG_TYPE msgtype;
56 struct nc_session *session;
57 struct nc_pollsession *ps;
58 struct test_state *state = arg;
59
60 ps = nc_ps_new();
61 assert_non_null(ps);
62
63 /* just to wait for when new data is configured */
64 pthread_barrier_wait(&state->end_barrier);
65
66 while (1) {
67 /* config ready, wait for client/server to be ready */
68 pthread_barrier_wait(&state->start_barrier);
69 msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session);
70
71 if (expect_ok) {
72 assert_int_equal(msgtype, NC_MSG_HELLO);
73 ret = nc_ps_add_session(ps, session);
74 assert_int_equal(ret, 0);
75
76 do {
77 ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
78 assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC);
79 } while (!(ret & NC_PSPOLL_SESSION_TERM));
80 nc_ps_clear(ps, 1, NULL);
81 } else {
82 assert_int_equal(msgtype, NC_MSG_ERROR);
83 }
84
85 if (!test_running) {
86 break;
87 }
88
89 /* wait for next config */
90 pthread_barrier_wait(&state->end_barrier);
91 }
92
93 nc_ps_free(ps);
94 return NULL;
95}
96
97static void *
98client_thread_tls(void *arg)
99{
100 int ret;
101 struct nc_session *session = NULL;
102 struct test_state *state = arg;
103
104 ret = nc_client_set_schema_searchpath(MODULES_DIR);
105 assert_int_equal(ret, 0);
106
107 /* set client cert */
108 ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key");
109 assert_int_equal(ret, 0);
110
111 /* set client ca */
112 ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data");
113 assert_int_equal(ret, 0);
114
115 /* just to wait for when new data is configured */
116 pthread_barrier_wait(&state->end_barrier);
117
118 while (1) {
119 /* config ready, wait for client/server to be ready */
120 pthread_barrier_wait(&state->start_barrier);
121 session = nc_connect_tls("127.0.0.1", 10005, NULL);
122 if (expect_ok) {
123 assert_non_null(session);
124 nc_session_free(session, NULL);
125 } else {
126 assert_null(session);
127 }
128
129 if (!test_running) {
130 break;
131 }
132
133 /* wait for next config */
134 pthread_barrier_wait(&state->end_barrier);
135 }
136
137 return NULL;
138}
139
140static void *
141client_thread_ssh(void *arg)
142{
143 int ret;
144 struct nc_session *session = NULL;
145 struct test_state *state = arg;
146
147 ret = nc_client_set_schema_searchpath(MODULES_DIR);
148 assert_int_equal(ret, 0);
149
150 /* skip all hostkey and known_hosts checks */
151 nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);
152
153 /* set ssh username */
154 ret = nc_client_ssh_set_username("client");
155 assert_int_equal(ret, 0);
156
157 /* add client's key pair */
158 ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/id_ed25519.pub", TESTS_DIR "/data/id_ed25519");
159 assert_int_equal(ret, 0);
160
161 /* just to wait for when new data is configured */
162 pthread_barrier_wait(&state->end_barrier);
163
164 while (1) {
165 /* config ready, wait for client/server to be ready */
166 pthread_barrier_wait(&state->start_barrier);
167 session = nc_connect_ssh("127.0.0.1", 10006, NULL);
168 if (expect_ok) {
169 assert_non_null(session);
170 nc_session_free(session, NULL);
171 } else {
172 assert_null(session);
173 }
174
175 if (!test_running) {
176 break;
177 }
178
179 /* wait for next config */
180 pthread_barrier_wait(&state->end_barrier);
181 }
182
183 return NULL;
184}
185
186static inline void
187configure(struct test_state *state, NC_TEST_EXPECT ok_or_fail, NC_TEST_STATE run_or_end)
188{
189 int ret = 0;
190
191 /* lidl synchronization */
192 pthread_barrier_wait(&state->end_barrier);
193
194 /* apply new config */
195 ret = nc_server_config_setup_data(state->tree);
196 assert_int_equal(ret, 0);
197
198 /* set test params */
199 expect_ok = ok_or_fail;
200 test_running = run_or_end;
201
202 /* it just works */
203 pthread_barrier_wait(&state->start_barrier);
204}
205
206static void
207init_test_create_threads_tls(pthread_t tids[2], void **state)
208{
209 int ret;
210
211 /* so threads dont quit immediately */
212 test_running = NC_TEST_STATE_RUN;
213 ret = pthread_create(&tids[0], NULL, client_thread_tls, *state);
214 assert_int_equal(ret, 0);
215 ret = pthread_create(&tids[1], NULL, server_thread, *state);
216 assert_int_equal(ret, 0);
217}
218
219static void
220init_test_create_threads_ssh(pthread_t tids[2], void **state)
221{
222 int ret;
223
224 /* so threads dont quit immediately */
225 test_running = NC_TEST_STATE_RUN;
226 ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
227 assert_int_equal(ret, 0);
228 ret = pthread_create(&tids[1], NULL, server_thread, *state);
229 assert_int_equal(ret, 0);
230}
231
232static void
233test_nc_change_tls_srv_crt(void **state)
234{
235 int ret, i;
236 pthread_t tids[2];
237 struct test_state *test_state;
238
239 assert_non_null(state);
240 test_state = *state;
241 init_test_create_threads_tls(tids, state);
242
romane6ec60e2023-10-19 15:21:52 +0200243 ret = nc_server_config_add_tls_server_cert(ctx, "endpt_tls", TESTS_DIR "/data/client.key", NULL, TESTS_DIR "/data/client.crt", &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200244 assert_int_equal(ret, 0);
245 configure(test_state, NC_TEST_EXPECT_FAIL, NC_TEST_STATE_RUN);
246
romane6ec60e2023-10-19 15:21:52 +0200247 ret = nc_server_config_add_tls_server_cert(ctx, "endpt_tls", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200248 assert_int_equal(ret, 0);
249 configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END);
250
251 for (i = 0; i < 2; i++) {
252 pthread_join(tids[i], NULL);
253 }
254}
255
256static void
257test_nc_change_tls_client_crt(void **state)
258{
259 int ret, i;
260 pthread_t tids[2];
261 struct test_state *test_state;
262
263 assert_non_null(state);
264 test_state = *state;
265 init_test_create_threads_tls(tids, state);
266
romane6ec60e2023-10-19 15:21:52 +0200267 ret = nc_server_config_add_tls_client_cert(ctx, "endpt_tls", "client_cert", TESTS_DIR "/data/server.crt", &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200268 assert_int_equal(ret, 0);
269 configure(test_state, NC_TEST_EXPECT_FAIL, NC_TEST_STATE_RUN);
270
romane6ec60e2023-10-19 15:21:52 +0200271 ret = nc_server_config_add_tls_client_cert(ctx, "endpt_tls", "client_cert", TESTS_DIR "/data/client.crt", &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200272 assert_int_equal(ret, 0);
273 configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END);
274
275 for (i = 0; i < 2; i++) {
276 pthread_join(tids[i], NULL);
277 }
278}
279
280static void
281test_nc_change_tls_ctn(void **state)
282{
283 int ret, i;
284 pthread_t tids[2];
285 struct test_state *test_state;
286
287 assert_non_null(state);
288 test_state = *state;
289 init_test_create_threads_tls(tids, state);
290
Roytakb2794852023-10-18 14:30:22 +0200291 ret = nc_server_config_add_tls_ctn(ctx, "endpt_tls", 1,
roman581b2502023-08-18 15:57:02 +0200292 "FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF",
293 NC_TLS_CTN_SPECIFIED, "invalid-fingerprint", &test_state->tree);
294 assert_int_equal(ret, 0);
295 configure(test_state, NC_TEST_EXPECT_FAIL, NC_TEST_STATE_RUN);
296
Roytakb2794852023-10-18 14:30:22 +0200297 ret = nc_server_config_add_tls_ctn(ctx, "endpt_tls", 1,
roman581b2502023-08-18 15:57:02 +0200298 "04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D",
299 NC_TLS_CTN_SPECIFIED, "client", &test_state->tree);
300 assert_int_equal(ret, 0);
301 configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END);
302
303 for (i = 0; i < 2; i++) {
304 pthread_join(tids[i], NULL);
305 }
306}
307
308static void
roman581b2502023-08-18 15:57:02 +0200309test_nc_change_ssh_hostkey(void **state)
310{
311 int ret, i;
312 pthread_t tids[2];
313 struct test_state *test_state;
314
315 assert_non_null(state);
316 test_state = *state;
317 init_test_create_threads_ssh(tids, state);
318
Roytakb2794852023-10-18 14:30:22 +0200319 ret = nc_server_config_add_ssh_hostkey(ctx, "endpt_ssh", "hostkey", TESTS_DIR "/data/server.key", NULL, &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200320 assert_int_equal(ret, 0);
321 configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_RUN);
322
Roytakb2794852023-10-18 14:30:22 +0200323 ret = nc_server_config_add_keystore_asym_key(ctx, NC_TI_LIBSSH, "keystore_hostkey", TESTS_DIR "/data/key_rsa", TESTS_DIR "/data/key_rsa.pub", &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200324 assert_int_equal(ret, 0);
romand348b942023-10-13 14:32:19 +0200325 ret = nc_server_config_add_ssh_keystore_ref(ctx, "endpt_ssh", "hostkey", "keystore_hostkey", &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200326 assert_int_equal(ret, 0);
327 configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END);
328
329 for (i = 0; i < 2; i++) {
330 pthread_join(tids[i], NULL);
331 }
332}
333
334static void
335test_nc_change_ssh_usr_pubkey(void **state)
336{
337 int ret, i;
338 pthread_t tids[2];
339 struct test_state *test_state;
340
341 assert_non_null(state);
342 test_state = *state;
343 init_test_create_threads_ssh(tids, state);
344
Roytakb2794852023-10-18 14:30:22 +0200345 ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt_ssh", "client", "pubkey", TESTS_DIR "/data/id_ecdsa521.pub", &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200346 assert_int_equal(ret, 0);
347 configure(test_state, NC_TEST_EXPECT_FAIL, NC_TEST_STATE_RUN);
348
Roytakb2794852023-10-18 14:30:22 +0200349 ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt_ssh", "client", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200350 assert_int_equal(ret, 0);
351 configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END);
352
353 for (i = 0; i < 2; i++) {
354 pthread_join(tids[i], NULL);
355 }
356}
357
358static int
359setup_f(void **state)
360{
361 int ret;
362 struct test_state *test_state;
363
364 nc_verbosity(NC_VERB_VERBOSE);
365
366 test_state = malloc(sizeof *test_state);
367 assert_non_null(test_state);
368
369 /* init barriers */
370 ret = pthread_barrier_init(&test_state->start_barrier, NULL, 3);
371 assert_int_equal(ret, 0);
372 ret = pthread_barrier_init(&test_state->end_barrier, NULL, 3);
373 assert_int_equal(ret, 0);
374
375 test_state->tree = NULL;
376 *state = test_state;
377
378 ret = ly_ctx_new(MODULES_DIR, 0, &ctx);
379 assert_int_equal(ret, 0);
380
381 ret = nc_server_init_ctx(&ctx);
382 assert_int_equal(ret, 0);
383
384 ret = nc_server_config_load_modules(&ctx);
385 assert_int_equal(ret, 0);
386
387 /* create new address and port data */
Roytakb2794852023-10-18 14:30:22 +0200388 ret = nc_server_config_add_address_port(ctx, "endpt_tls", NC_TI_OPENSSL, "127.0.0.1", 10005, &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200389 assert_int_equal(ret, 0);
390
391 /* create new server certificate data */
romane6ec60e2023-10-19 15:21:52 +0200392 ret = nc_server_config_add_tls_server_cert(ctx, "endpt_tls", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200393 assert_int_equal(ret, 0);
394
395 /* create new end entity client cert data */
romane6ec60e2023-10-19 15:21:52 +0200396 ret = nc_server_config_add_tls_client_cert(ctx, "endpt_tls", "client_cert", TESTS_DIR "/data/client.crt", &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200397 assert_int_equal(ret, 0);
398
399 /* create new cert-to-name */
Roytakb2794852023-10-18 14:30:22 +0200400 ret = nc_server_config_add_tls_ctn(ctx, "endpt_tls", 1,
roman581b2502023-08-18 15:57:02 +0200401 "04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D",
402 NC_TLS_CTN_SPECIFIED, "client", &test_state->tree);
403 assert_int_equal(ret, 0);
404
405 /* create new address and port data */
Roytakb2794852023-10-18 14:30:22 +0200406 ret = nc_server_config_add_address_port(ctx, "endpt_ssh", NC_TI_LIBSSH, "127.0.0.1", 10006, &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200407 assert_int_equal(ret, 0);
408
409 /* create new hostkey data */
Roytakb2794852023-10-18 14:30:22 +0200410 ret = nc_server_config_add_ssh_hostkey(ctx, "endpt_ssh", "hostkey", TESTS_DIR "/data/server.key", NULL, &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200411 assert_int_equal(ret, 0);
412
413 /* create new ssh user pubkey data */
Roytakb2794852023-10-18 14:30:22 +0200414 ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt_ssh", "client", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &test_state->tree);
roman581b2502023-08-18 15:57:02 +0200415 assert_int_equal(ret, 0);
416
417 ret = nc_server_init();
418 assert_int_equal(ret, 0);
419
romaneadc4782023-09-14 10:10:08 +0200420 /* initialize client */
421 ret = nc_client_init();
422 assert_int_equal(ret, 0);
423
roman581b2502023-08-18 15:57:02 +0200424 return 0;
425}
426
427static int
428teardown_f(void **state)
429{
430 int ret = 0;
431 struct test_state *test_state;
432
433 assert_non_null(state);
434 test_state = *state;
435
436 ret = pthread_barrier_destroy(&test_state->start_barrier);
437 assert_int_equal(ret, 0);
438 ret = pthread_barrier_destroy(&test_state->end_barrier);
439 assert_int_equal(ret, 0);
440
441 lyd_free_all(test_state->tree);
442 free(*state);
443 nc_client_destroy();
444 nc_server_destroy();
445 ly_ctx_destroy(ctx);
446
447 return 0;
448}
449
450int
451main(void)
452{
453 const struct CMUnitTest tests[] = {
454 cmocka_unit_test_setup_teardown(test_nc_change_tls_srv_crt, setup_f, teardown_f),
455 cmocka_unit_test_setup_teardown(test_nc_change_tls_client_crt, setup_f, teardown_f),
456 cmocka_unit_test_setup_teardown(test_nc_change_tls_ctn, setup_f, teardown_f),
roman581b2502023-08-18 15:57:02 +0200457 cmocka_unit_test_setup_teardown(test_nc_change_ssh_hostkey, setup_f, teardown_f),
romand348b942023-10-13 14:32:19 +0200458 cmocka_unit_test_setup_teardown(test_nc_change_ssh_usr_pubkey, setup_f, teardown_f),
roman581b2502023-08-18 15:57:02 +0200459 };
460
461 setenv("CMOCKA_TEST_ABORT", "1", 1);
462 return cmocka_run_group_tests(tests, NULL, NULL);
463}