blob: 735267b96ad063da00c1d7dfd4a52e3f26a314ee [file] [log] [blame]
roman5cbb6532023-06-22 12:53:17 +02001/**
2 * @file test_ch.c
3 * @author Roman Janota <xjanot04@fit.vutbr.cz>
4 * @brief libnetconf2 Call-home 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 <errno.h>
19#include <pthread.h>
20#include <setjmp.h>
21#include <stdarg.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include <cmocka.h>
27
28#include "tests/config.h"
29
30#define NC_PS_POLL_TIMEOUT 2000
31
32#define NC_ACCEPT_TIMEOUT 2000
33
34struct ly_ctx *ctx;
35
36struct test_state {
37 pthread_barrier_t barrier;
romanb6f44032023-06-30 15:07:56 +020038 struct lyd_node *ssh_tree;
39 struct lyd_node *tls_tree;
roman5cbb6532023-06-22 12:53:17 +020040};
41
romanba93eac2023-07-18 14:36:48 +020042char buffer[512];
43char expected[512];
44
45static void
46test_msg_callback(const struct nc_session *session, NC_VERB_LEVEL level, const char *msg)
47{
48 (void) level;
49 (void) session;
50
51 if (strstr(msg, expected)) {
52 strcpy(buffer, msg);
53 }
54
55 printf("%s\n", msg);
56}
57
roman5cbb6532023-06-22 12:53:17 +020058/* acquire ctx cb for dispatch */
59const struct ly_ctx *
60ch_session_acquire_ctx_cb(void *cb_data)
61{
62 (void) cb_data;
63 return ctx;
64}
65
66/* release ctx cb for dispatch */
67void
68ch_session_release_ctx_cb(void *cb_data)
69{
70 (void) cb_data;
71 return;
72}
73
74/* new session cb for dispatch */
75int
76ch_new_session_cb(const char *client_name, struct nc_session *new_session, void *user_data)
77{
78 int ret = 0;
79 struct nc_pollsession *ps = (struct nc_pollsession *)user_data;
80
81 (void) client_name;
82
83 ret = nc_ps_add_session(ps, new_session);
84 assert_int_equal(ret, 0);
85 return 0;
86}
87
88static void *
romanb6f44032023-06-30 15:07:56 +020089server_thread_ssh(void *arg)
roman5cbb6532023-06-22 12:53:17 +020090{
91 int ret;
92 struct test_state *state = arg;
93 struct nc_pollsession *ps;
roman5cbb6532023-06-22 12:53:17 +020094
romanba93eac2023-07-18 14:36:48 +020095 /* set print clb so we get access to messages */
96 nc_set_print_clb_session(test_msg_callback);
97 buffer[0] = '\0';
98 strcpy(expected, "reconnecting in");
99
roman5cbb6532023-06-22 12:53:17 +0200100 /* prepare data for deleting the call-home client */
roman8ba6efa2023-07-12 15:27:52 +0200101 ret = nc_server_config_new_del_ch_client("ch_ssh", &state->ssh_tree);
roman5cbb6532023-06-22 12:53:17 +0200102 assert_int_equal(ret, 0);
103
104 /* new poll session */
105 ps = nc_ps_new();
106 assert_non_null(ps);
107
108 pthread_barrier_wait(&state->barrier);
109 /* create the call-home client thread */
romanb6f44032023-06-30 15:07:56 +0200110 ret = nc_connect_ch_client_dispatch("ch_ssh", ch_session_acquire_ctx_cb,
roman5cbb6532023-06-22 12:53:17 +0200111 ch_session_release_ctx_cb, NULL, ch_new_session_cb, ps);
112 assert_int_equal(ret, 0);
113
114 /* poll */
115 do {
116 ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
117 if (ret & (NC_PSPOLL_TIMEOUT | NC_PSPOLL_NOSESSIONS)) {
118 usleep(500);
119 }
romanba93eac2023-07-18 14:36:48 +0200120 } while (!strlen(buffer));
roman5cbb6532023-06-22 12:53:17 +0200121
122 /* delete the call-home client, the thread should end */
romanb6f44032023-06-30 15:07:56 +0200123 ret = nc_server_config_setup_data(state->ssh_tree);
roman5cbb6532023-06-22 12:53:17 +0200124 assert_int_equal(ret, 0);
125
roman5cbb6532023-06-22 12:53:17 +0200126 nc_ps_clear(ps, 1, NULL);
127 nc_ps_free(ps);
128 nc_server_destroy();
129 return NULL;
130}
131
132static void *
romanb6f44032023-06-30 15:07:56 +0200133client_thread_ssh(void *arg)
roman5cbb6532023-06-22 12:53:17 +0200134{
135 int ret;
136 struct nc_session *session = NULL;
137 struct test_state *state = arg;
138
139 /* skip all hostkey and known_hosts checks */
140 nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);
141
142 /* set directory where to search for modules */
143 ret = nc_client_set_schema_searchpath(MODULES_DIR);
144 assert_int_equal(ret, 0);
145
146 /* set ssh username */
romanb6f44032023-06-30 15:07:56 +0200147 ret = nc_client_ssh_ch_set_username("test_ch_ssh");
roman5cbb6532023-06-22 12:53:17 +0200148 assert_int_equal(ret, 0);
149
150 /* add client's key pair */
151 ret = nc_client_ssh_ch_add_keypair(TESTS_DIR "/data/id_ed25519.pub", TESTS_DIR "/data/id_ed25519");
152 assert_int_equal(ret, 0);
153
154 /* add call-home bind */
155 ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", 10009);
156 assert_int_equal(ret, 0);
157
158 pthread_barrier_wait(&state->barrier);
159 /* connect */
160 ret = nc_accept_callhome(NC_ACCEPT_TIMEOUT, NULL, &session);
161 assert_int_equal(ret, 1);
162
163 ret = nc_client_ssh_ch_del_bind("127.0.0.1", 10009);
164 assert_int_equal(ret, 0);
165
166 nc_session_free(session, NULL);
167 return NULL;
168}
169
roman5cbb6532023-06-22 12:53:17 +0200170static int
romanb6f44032023-06-30 15:07:56 +0200171setup_ssh(void **state)
roman5cbb6532023-06-22 12:53:17 +0200172{
173 int ret;
roman5cbb6532023-06-22 12:53:17 +0200174 struct test_state *test_state;
175
176 nc_verbosity(NC_VERB_VERBOSE);
177
178 /* init barrier */
179 test_state = malloc(sizeof *test_state);
180 assert_non_null(test_state);
181
182 ret = pthread_barrier_init(&test_state->barrier, NULL, 2);
183 assert_int_equal(ret, 0);
184
romanb6f44032023-06-30 15:07:56 +0200185 test_state->ssh_tree = NULL;
roman5cbb6532023-06-22 12:53:17 +0200186 *state = test_state;
187
188 /* create new context */
189 ret = ly_ctx_new(MODULES_DIR, 0, &ctx);
190 assert_int_equal(ret, 0);
191
192 /* load default modules into context */
193 ret = nc_server_init_ctx(&ctx);
194 assert_int_equal(ret, 0);
195
196 /* load ietf-netconf-server module and it's imports into context */
197 ret = nc_server_config_load_modules(&ctx);
198 assert_int_equal(ret, 0);
199
romanb6f44032023-06-30 15:07:56 +0200200 /* set call-home address and port */
201 ret = nc_server_config_new_ch_address_port(ctx, "ch_ssh", "endpt", NC_TI_LIBSSH, "127.0.0.1", "10009", &test_state->ssh_tree);
roman5cbb6532023-06-22 12:53:17 +0200202 assert_int_equal(ret, 0);
203
romanba93eac2023-07-18 14:36:48 +0200204 /* set connection type to persistent */
romanb6f44032023-06-30 15:07:56 +0200205 ret = nc_server_config_new_ch_persistent(ctx, "ch_ssh", &test_state->ssh_tree);
roman5cbb6532023-06-22 12:53:17 +0200206 assert_int_equal(ret, 0);
207
romanba93eac2023-07-18 14:36:48 +0200208 /* set the period of the periodic connection type, this should remove the persistent connection type */
romanb6f44032023-06-30 15:07:56 +0200209 ret = nc_server_config_new_ch_period(ctx, "ch_ssh", 3, &test_state->ssh_tree);
210 assert_int_equal(ret, 0);
211
212 /* set call-home server hostkey */
roman8ba6efa2023-07-12 15:27:52 +0200213 ret = nc_server_config_new_ch_ssh_hostkey(ctx, "ch_ssh", "endpt", "hostkey", TESTS_DIR "/data/key_ecdsa", NULL, &test_state->ssh_tree);
romanb6f44032023-06-30 15:07:56 +0200214 assert_int_equal(ret, 0);
215
216 /* set call-home client's pubkey */
roman8ba6efa2023-07-12 15:27:52 +0200217 ret = nc_server_config_new_ch_ssh_user_pubkey(ctx, "ch_ssh", "endpt", "test_ch_ssh", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &test_state->ssh_tree);
roman5cbb6532023-06-22 12:53:17 +0200218 assert_int_equal(ret, 0);
219
220 /* configure the server based on the data */
romanb6f44032023-06-30 15:07:56 +0200221 ret = nc_server_config_setup_data(test_state->ssh_tree);
roman5cbb6532023-06-22 12:53:17 +0200222 assert_int_equal(ret, 0);
223
224 /* initialize server */
225 ret = nc_server_init();
226 assert_int_equal(ret, 0);
227
roman5cbb6532023-06-22 12:53:17 +0200228 return 0;
229}
230
231static int
romanb6f44032023-06-30 15:07:56 +0200232teardown_ssh(void **state)
roman5cbb6532023-06-22 12:53:17 +0200233{
234 int ret = 0;
235 struct test_state *test_state;
236
237 assert_non_null(state);
238 test_state = *state;
239
240 ret = pthread_barrier_destroy(&test_state->barrier);
241 assert_int_equal(ret, 0);
242
romanb6f44032023-06-30 15:07:56 +0200243 lyd_free_tree(test_state->ssh_tree);
244
245 free(*state);
246 nc_client_destroy();
247 ly_ctx_destroy(ctx);
248
249 return 0;
250}
251
252static void
253test_nc_ch_ssh(void **state)
254{
255 int ret, i;
256 pthread_t tids[2];
257
258 assert_non_null(state);
259
260 /* client */
261 ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
262 assert_int_equal(ret, 0);
263
264 /* server */
265 ret = pthread_create(&tids[1], NULL, server_thread_ssh, *state);
266 assert_int_equal(ret, 0);
267
268 for (i = 0; i < 2; i++) {
269 pthread_join(tids[i], NULL);
270 }
271}
272
273static void *
274server_thread_tls(void *arg)
275{
276 int ret;
277 struct test_state *state = arg;
278 struct nc_pollsession *ps;
279
280 /* prepare data for deleting the call-home client */
roman8ba6efa2023-07-12 15:27:52 +0200281 ret = nc_server_config_new_del_ch_client("ch_tls", &state->tls_tree);
romanb6f44032023-06-30 15:07:56 +0200282 assert_int_equal(ret, 0);
283
284 /* new poll session */
285 ps = nc_ps_new();
286 assert_non_null(ps);
287
288 pthread_barrier_wait(&state->barrier);
289 /* create the call-home client thread */
290 ret = nc_connect_ch_client_dispatch("ch_tls", ch_session_acquire_ctx_cb,
291 ch_session_release_ctx_cb, NULL, ch_new_session_cb, ps);
292 assert_int_equal(ret, 0);
293
294 /* poll */
295 do {
296 ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
297 if (ret & (NC_PSPOLL_TIMEOUT | NC_PSPOLL_NOSESSIONS)) {
298 usleep(500);
299 }
300 } while (!(ret & NC_PSPOLL_SESSION_TERM));
301
302 /* delete the call-home client, the thread should end */
303 ret = nc_server_config_setup_data(state->tls_tree);
304 assert_int_equal(ret, 0);
305
306 nc_ps_clear(ps, 1, NULL);
307 nc_ps_free(ps);
308 nc_server_destroy();
309 return NULL;
310}
311
312static void *
313client_thread_tls(void *arg)
314{
315 int ret;
316 struct nc_session *session = NULL;
317 struct test_state *state = arg;
318
319 /* set directory where to search for modules */
320 ret = nc_client_set_schema_searchpath(MODULES_DIR);
321 assert_int_equal(ret, 0);
322
323 /* add client's cert */
324 ret = nc_client_tls_ch_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key");
325 assert_int_equal(ret, 0);
326
327 /* set client ca */
328 ret = nc_client_tls_ch_set_trusted_ca_paths(TESTS_DIR "/data/serverca.pem", NULL);
329 assert_int_equal(ret, 0);
330
331 /* add call-home bind */
332 ret = nc_client_tls_ch_add_bind_listen("127.0.0.1", 10010);
333 assert_int_equal(ret, 0);
334
335 pthread_barrier_wait(&state->barrier);
336 /* connect */
337 ret = nc_accept_callhome(NC_ACCEPT_TIMEOUT, NULL, &session);
338 assert_int_equal(ret, 1);
339
340 ret = nc_client_tls_ch_del_bind("127.0.0.1", 10010);
341 assert_int_equal(ret, 0);
342
343 nc_session_free(session, NULL);
344 return NULL;
345}
346
347static void
348test_nc_ch_tls(void **state)
349{
350 int ret, i;
351 pthread_t tids[2];
352
353 assert_non_null(state);
354
355 /* client */
356 ret = pthread_create(&tids[0], NULL, client_thread_tls, *state);
357 assert_int_equal(ret, 0);
358
359 /* server */
360 ret = pthread_create(&tids[1], NULL, server_thread_tls, *state);
361 assert_int_equal(ret, 0);
362
363 for (i = 0; i < 2; i++) {
364 pthread_join(tids[i], NULL);
365 }
366}
367
368static int
369setup_tls(void **state)
370{
371 int ret;
372 struct test_state *test_state;
373
374 nc_verbosity(NC_VERB_VERBOSE);
375
376 /* init barrier */
377 test_state = malloc(sizeof *test_state);
378 assert_non_null(test_state);
379
380 ret = pthread_barrier_init(&test_state->barrier, NULL, 2);
381 assert_int_equal(ret, 0);
382
383 test_state->tls_tree = NULL;
384 *state = test_state;
385
386 /* create new context */
387 ret = ly_ctx_new(MODULES_DIR, 0, &ctx);
388 assert_int_equal(ret, 0);
389
390 /* load default modules into context */
391 ret = nc_server_init_ctx(&ctx);
392 assert_int_equal(ret, 0);
393
394 /* load ietf-netconf-server module and it's imports into context */
395 ret = nc_server_config_load_modules(&ctx);
396 assert_int_equal(ret, 0);
397
398 /* set call-home address and port */
399 ret = nc_server_config_new_ch_address_port(ctx, "ch_tls", "endpt", NC_TI_OPENSSL, "127.0.0.1", "10010", &test_state->tls_tree);
400 assert_int_equal(ret, 0);
401
402 /* set call-home server certificate */
403 ret = nc_server_config_new_ch_tls_server_certificate(ctx, "ch_tls", "endpt", NULL, TESTS_DIR "/data/server.key", TESTS_DIR "/data/server.crt", &test_state->tls_tree);
404 assert_int_equal(ret, 0);
405
406 /* set call-home client end entity certificate */
407 ret = nc_server_config_new_ch_tls_client_certificate(ctx, "ch_tls", "endpt", "ee-cert", TESTS_DIR "/data/client.crt", &test_state->tls_tree);
408 assert_int_equal(ret, 0);
409
410 /* set call-home client certificate authority certificate */
411 ret = nc_server_config_new_ch_tls_client_ca(ctx, "ch_tls", "endpt", "ca-cert", TESTS_DIR "/data/serverca.pem", &test_state->tls_tree);
412 assert_int_equal(ret, 0);
413
414 /* set call-home CTN */
415 ret = nc_server_config_new_ch_tls_ctn(ctx, "ch_tls", "endpt", 1,
416 "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",
417 NC_TLS_CTN_SPECIFIED, "ch_client_tls", &test_state->tls_tree);
418 assert_int_equal(ret, 0);
419
420 /* configure the server based on the data */
421 ret = nc_server_config_setup_data(test_state->tls_tree);
422 assert_int_equal(ret, 0);
423
424 /* initialize server */
425 ret = nc_server_init();
426 assert_int_equal(ret, 0);
427
428 return 0;
429}
430
431static int
432teardown_tls(void **state)
433{
434 int ret = 0;
435 struct test_state *test_state;
436
437 assert_non_null(state);
438 test_state = *state;
439
440 ret = pthread_barrier_destroy(&test_state->barrier);
441 assert_int_equal(ret, 0);
442
443 lyd_free_tree(test_state->tls_tree);
roman142718b2023-06-29 09:15:29 +0200444
roman5cbb6532023-06-22 12:53:17 +0200445 free(*state);
446 nc_client_destroy();
447 ly_ctx_destroy(ctx);
448
449 return 0;
450}
451
452int
453main(void)
454{
455 const struct CMUnitTest tests[] = {
romanb6f44032023-06-30 15:07:56 +0200456 cmocka_unit_test_setup_teardown(test_nc_ch_ssh, setup_ssh, teardown_ssh),
457 cmocka_unit_test_setup_teardown(test_nc_ch_tls, setup_tls, teardown_tls),
roman5cbb6532023-06-22 12:53:17 +0200458 };
459
460 setenv("CMOCKA_TEST_ABORT", "1", 1);
461 return cmocka_run_group_tests(tests, NULL, NULL);
462}