blob: 16a635e0b8576d9f729f685e39de9255329ea47d [file] [log] [blame]
romanc1d2b092023-02-02 08:58:27 +01001/**
romane028ef92023-02-24 16:33:08 +01002 * @file server_config.c
romanc1d2b092023-02-02 08:58:27 +01003 * @author Roman Janota <janota@cesnet.cz>
4 * @brief libnetconf2 server configuration functions
5 *
6 * @copyright
7 * Copyright (c) 2015 - 2021 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 */
roman27215242023-03-10 14:55:00 +010015
16#define _GNU_SOURCE
17
romanc1d2b092023-02-02 08:58:27 +010018#include <assert.h>
19#include <stdlib.h>
20#include <string.h>
21
22#include "compat.h"
romanc1d2b092023-02-02 08:58:27 +010023#include "libnetconf.h"
romane028ef92023-02-24 16:33:08 +010024#include "server_config.h"
romanc1d2b092023-02-02 08:58:27 +010025#include "session_server.h"
26#include "session_server_ch.h"
27
28/* All libssh supported host-key, key-exchange, encryption and mac algorithms as of version 0.10.90 */
29
30static const char *supported_hostkey_algs[] = {
31 "ssh-ed25519-cert-v01@openssh.com", "ecdsa-sha2-nistp521-cert-v01@openssh.com",
32 "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ecdsa-sha2-nistp256-cert-v01@openssh.com",
33 "rsa-sha2-512-cert-v01@openssh.com", "rsa-sha2-256-cert-v01@openssh.com",
34 "ssh-rsa-cert-v01@openssh.com", "ssh-dss-cert-v01@openssh.com",
35 "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256",
36 "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa", "ssh-dss", NULL
37};
38
39static const char *supported_kex_algs[] = {
40 "diffie-hellman-group-exchange-sha1", "curve25519-sha256", "curve25519-sha256@libssh.org",
41 "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group18-sha512",
42 "diffie-hellman-group16-sha512", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group14-sha256", NULL
43};
44
45static const char *supported_encryption_algs[] = {
46 "chacha20-poly1305@openssh.com", "aes256-gcm@openssh.com", "aes128-gcm@openssh.com",
47 "aes256-ctr", "aes192-ctr", "aes128-ctr", "aes256-cbc", "aes192-cbc", "aes128-cbc",
roman27215242023-03-10 14:55:00 +010048 "blowfish-cbc", "triple-des-cbc", "none", NULL
romanc1d2b092023-02-02 08:58:27 +010049};
50
51static const char *supported_mac_algs[] = {
52 "hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "hmac-sha1-etm@openssh.com",
53 "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", NULL
54};
55
56extern struct nc_server_opts server_opts;
57
58/**
59 * @brief Get the pointer to an endpoint structure based on node's location in the YANG data.
60 *
61 * @param[in] node Node from which the endpoint containing this node is derived.
62 * @param[out] endpt Endpoint containing the node.
63 * @param[out] bind Bind corresponding to the endpoint. Optional.
64 * @return 0 on success, 1 on error.
65 */
66static int
67nc_server_get_endpt(const struct lyd_node *node, struct nc_endpt **endpt, struct nc_bind **bind)
68{
69 uint16_t i;
70 const char *endpt_name;
71
72 assert(node);
73
74 while (node) {
75 if (!strcmp(LYD_NAME(node), "endpoint")) {
76 break;
77 }
78 node = lyd_parent(node);
79 }
80
81 if (!node) {
82 ERR(NULL, "Node \"%s\" is not contained in an endpoint subtree.", LYD_NAME(node));
83 return 1;
84 }
85
86 node = lyd_child(node);
87 assert(!strcmp(LYD_NAME(node), "name"));
88 endpt_name = lyd_get_value(node);
89
90 for (i = 0; i < server_opts.endpt_count; i++) {
91 if (!strcmp(server_opts.endpts[i].name, endpt_name)) {
92 *endpt = &server_opts.endpts[i];
93 if (bind) {
94 *bind = &server_opts.binds[i];
95 }
96 return 0;
97 }
98 }
99
100 ERR(NULL, "Endpoint \"%s\" was not found.", endpt_name);
101 return 1;
102}
103
104/**
105 * @brief Get the pointer to a hostkey structure based on node's location in the YANG data.
106 *
107 * @param[in] node Node from which the hotkey containing this node is derived.
108 * @param[in] opts Server SSH opts storing the array of the hostkey structures.
109 * @param[out] hostkey Hostkey containing the node.
110 * @return 0 on success, 1 on error.
111 */
112static int
113nc_server_get_hostkey(const struct lyd_node *node, const struct nc_server_ssh_opts *opts, struct nc_hostkey **hostkey)
114{
115 uint16_t i;
116 const char *hostkey_name;
117
118 assert(node && opts);
119
120 while (node) {
121 if (!strcmp(LYD_NAME(node), "host-key")) {
122 break;
123 }
124 node = lyd_parent(node);
125 }
126
127 if (!node) {
128 ERR(NULL, "Node \"%s\" is not contained in a host-key subtree.", LYD_NAME(node));
129 return 1;
130 }
131
132 node = lyd_child(node);
133 assert(!strcmp(LYD_NAME(node), "name"));
134 hostkey_name = lyd_get_value(node);
135
136 for (i = 0; i < opts->hostkey_count; i++) {
137 if (!strcmp(opts->hostkeys[i].name, hostkey_name)) {
138 *hostkey = &opts->hostkeys[i];
139 return 0;
140 }
141 }
142
143 ERR(NULL, "Host-key \"%s\" was not found.", hostkey_name);
144 return 1;
145}
146
147/**
148 * @brief Get the pointer to a client authentication structure based on node's location in the YANG data.
149 *
150 * @param[in] node Node from which the client-authentication structure containing this node is derived.
151 * @param[in] opts Server SSH opts storing the array of the client authentication structures.
152 * @param[out] auth_client Client authentication structure containing the node.
153 * @return 0 on success, 1 on error.
154 */
155static int
156nc_server_get_auth_client(const struct lyd_node *node, const struct nc_server_ssh_opts *opts, struct nc_client_auth **auth_client)
157{
158 uint16_t i;
159 const char *authkey_name;
160
161 assert(node && opts);
162
163 while (node) {
164 if (!strcmp(LYD_NAME(node), "user")) {
165 break;
166 }
167 node = lyd_parent(node);
168 }
169
170 if (!node) {
171 ERR(NULL, "Node \"%s\" is not contained in a client-authentication subtree.", LYD_NAME(node));
172 return 1;
173 }
174
175 node = lyd_child(node);
176 assert(!strcmp(LYD_NAME(node), "name"));
177 authkey_name = lyd_get_value(node);
178
179 for (i = 0; i < opts->client_count; i++) {
180 if (!strcmp(opts->auth_clients[i].username, authkey_name)) {
181 *auth_client = &opts->auth_clients[i];
182 return 0;
183 }
184 }
185
186 ERR(NULL, "Authorized key \"%s\" was not found.", authkey_name);
187 return 1;
188}
189
190/**
191 * @brief Get the pointer to a client authentication public key structure based on node's location in the YANG data.
192 *
193 * @param[in] node Node from which the ca-public key structure containing this node is derived.
194 * @param[in] auth_client Client authentication structure storing the array of the public key structures.
195 * @param[out] pubkey Public key structure containing the node.
196 * @return 0 on success, 1 on error.
197 */
198static int
roman8edee342023-03-31 13:25:48 +0200199nc_server_get_pubkey(const struct lyd_node *node, const struct nc_client_auth *auth_client, struct nc_public_key **pubkey)
romanc1d2b092023-02-02 08:58:27 +0100200{
201 uint16_t i;
202 const char *pubkey_name;
203
204 assert(node && auth_client);
205
206 node = lyd_parent(node);
207 while (node) {
208 if (!strcmp(LYD_NAME(node), "public-key")) {
209 break;
210 }
211 node = lyd_parent(node);
212 }
213
214 if (!node) {
215 ERR(NULL, "Node \"%s\" is not contained in a public-key subtree.", LYD_NAME(node));
216 return 1;
217 }
218
219 node = lyd_child(node);
220 assert(!strcmp(LYD_NAME(node), "name"));
221 pubkey_name = lyd_get_value(node);
222
223 for (i = 0; i < auth_client->pubkey_count; i++) {
224 if (!strcmp(auth_client->pubkeys[i].name, pubkey_name)) {
225 *pubkey = &auth_client->pubkeys[i];
226 return 0;
227 }
228 }
229
230 ERR(NULL, "Public key \"%s\" was not found.", pubkey_name);
231 return 1;
232}
233
234/**
235 * @brief Compares the nth-parent name.
236 *
237 * @param[in] node Node of which nth-parent to compare.
238 * @param[in] parent_count Count of parents.
239 * @param[in] parent_name Expected name of the parent.
240 * @return 1 if the name matches, 0 otherwise.
241 */
242static int
243equal_parent_name(const struct lyd_node *node, uint16_t parent_count, const char *parent_name)
244{
245 uint16_t i;
246
247 assert(node && parent_count > 0 && parent_name);
248
249 node = lyd_parent(node);
250 for (i = 1; i < parent_count; i++) {
251 node = lyd_parent(node);
252 }
253
254 if (!strcmp(LYD_NAME(node), parent_name)) {
255 return 1;
256 }
257
258 return 0;
259}
260
261static void
262nc_server_del_auth_client_pam_name(struct nc_client_auth *auth_client)
263{
264 free(auth_client->pam_config_name);
265 auth_client->pam_config_name = NULL;
266}
267
268static void
269nc_server_del_auth_client_pam_dir(struct nc_client_auth *auth_client)
270{
271 free(auth_client->pam_config_dir);
272 auth_client->pam_config_dir = NULL;
273}
274
275static void
276nc_server_del_endpt_name(struct nc_endpt *endpt)
277{
278 free(endpt->name);
279 endpt->name = NULL;
280}
281
282static void
283nc_server_del_local_address(struct nc_bind *bind)
284{
285 free(bind->address);
286 bind->address = NULL;
287}
288
289static void
290nc_server_del_hostkey_name(struct nc_hostkey *hostkey)
291{
292 free(hostkey->name);
293 hostkey->name = NULL;
294}
295
296static void
297nc_server_del_public_key(struct nc_hostkey *hostkey)
298{
roman8edee342023-03-31 13:25:48 +0200299 free(hostkey->key.pub_base64);
300 hostkey->key.pub_base64 = NULL;
romanc1d2b092023-02-02 08:58:27 +0100301}
302
303static void
romanc1d2b092023-02-02 08:58:27 +0100304nc_server_del_private_key(struct nc_hostkey *hostkey)
305{
roman8edee342023-03-31 13:25:48 +0200306 free(hostkey->key.priv_base64);
307 hostkey->key.priv_base64 = NULL;
romanc1d2b092023-02-02 08:58:27 +0100308}
309
310static void
romanc1d2b092023-02-02 08:58:27 +0100311nc_server_del_auth_client_username(struct nc_client_auth *auth_client)
312{
313 free(auth_client->username);
314 auth_client->username = NULL;
315}
316
317static void
roman8edee342023-03-31 13:25:48 +0200318nc_server_del_auth_client_pubkey_name(struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100319{
320 free(pubkey->name);
321 pubkey->name = NULL;
322}
323
324static void
roman8edee342023-03-31 13:25:48 +0200325nc_server_del_auth_client_pubkey_pub_base64(struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100326{
327 free(pubkey->pub_base64);
328 pubkey->pub_base64 = NULL;
329}
330
331static void
romanc1d2b092023-02-02 08:58:27 +0100332nc_server_del_auth_client_password(struct nc_client_auth *auth_client)
333{
334 free(auth_client->password);
335 auth_client->password = NULL;
336}
337
338static void
339nc_server_del_hostkey_algs(struct nc_server_ssh_opts *opts)
340{
341 free(opts->hostkey_algs);
342 opts->hostkey_algs = NULL;
343}
344
345static void
346nc_server_del_kex_algs(struct nc_server_ssh_opts *opts)
347{
348 free(opts->kex_algs);
349 opts->kex_algs = NULL;
350}
351
352static void
353nc_server_del_encryption_algs(struct nc_server_ssh_opts *opts)
354{
355 free(opts->encryption_algs);
356 opts->encryption_algs = NULL;
357}
358
359static void
360nc_server_del_mac_algs(struct nc_server_ssh_opts *opts)
361{
362 free(opts->mac_algs);
363 opts->mac_algs = NULL;
364}
365
366static void
367nc_server_del_hostkey(struct nc_server_ssh_opts *opts, struct nc_hostkey *hostkey)
368{
369 assert(hostkey->ks_type == NC_STORE_LOCAL || hostkey->ks_type == NC_STORE_KEYSTORE);
370
371 if (hostkey->ks_type == NC_STORE_LOCAL) {
372 nc_server_del_public_key(hostkey);
373 nc_server_del_private_key(hostkey);
romanc1d2b092023-02-02 08:58:27 +0100374 }
375
376 nc_server_del_hostkey_name(hostkey);
377 opts->hostkey_count--;
378 if (!opts->hostkey_count) {
379 free(opts->hostkeys);
380 opts->hostkeys = NULL;
381 }
382}
383
384static void
roman8edee342023-03-31 13:25:48 +0200385nc_server_del_auth_client_pubkey(struct nc_client_auth *auth_client, struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100386{
387 nc_server_del_auth_client_pubkey_name(pubkey);
388 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
389
390 auth_client->pubkey_count--;
391 if (!auth_client->pubkey_count) {
392 free(auth_client->pubkeys);
393 auth_client->pubkeys = NULL;
394 }
395}
396
397static void
398nc_server_del_auth_client(struct nc_server_ssh_opts *opts, struct nc_client_auth *auth_client)
399{
400 uint16_t i, pubkey_count;
401
402 if (auth_client->ks_type == NC_STORE_LOCAL) {
403 pubkey_count = auth_client->pubkey_count;
404 for (i = 0; i < pubkey_count; i++) {
405 nc_server_del_auth_client_pubkey(auth_client, &auth_client->pubkeys[i]);
406 }
romanc1d2b092023-02-02 08:58:27 +0100407 }
408
409 nc_server_del_auth_client_password(auth_client);
410 nc_server_del_auth_client_pam_name(auth_client);
411 nc_server_del_auth_client_pam_dir(auth_client);
412 nc_server_del_auth_client_username(auth_client);
413
414 opts->client_count--;
415 if (!opts->client_count) {
416 free(opts->auth_clients);
417 opts->auth_clients = NULL;
418 }
419}
420
421static void
422nc_server_del_ssh(struct nc_bind *bind, struct nc_server_ssh_opts *opts)
423{
424 uint16_t i, hostkey_count, client_count;
425
426 nc_server_del_local_address(bind);
427 if (bind->sock > -1) {
428 close(bind->sock);
429 }
430
431 /* store in variable because it gets decremented in the function call */
432 hostkey_count = opts->hostkey_count;
433 for (i = 0; i < hostkey_count; i++) {
434 nc_server_del_hostkey(opts, &opts->hostkeys[i]);
435 }
436
437 client_count = opts->client_count;
438 for (i = 0; i < client_count; i++) {
439 nc_server_del_auth_client(opts, &opts->auth_clients[i]);
440 }
441
442 nc_server_del_hostkey_algs(opts);
443 nc_server_del_kex_algs(opts);
444 nc_server_del_encryption_algs(opts);
445 nc_server_del_mac_algs(opts);
446
447 free(opts);
448 opts = NULL;
449}
450
451void
452nc_server_del_endpt_ssh(struct nc_endpt *endpt, struct nc_bind *bind)
453{
454 nc_server_del_endpt_name(endpt);
455 nc_server_del_ssh(bind, endpt->opts.ssh);
456
457 server_opts.endpt_count--;
458 if (!server_opts.endpt_count) {
459 free(server_opts.endpts);
460 free(server_opts.binds);
461 server_opts.endpts = NULL;
462 server_opts.binds = NULL;
463 }
464}
465
roman45cec4e2023-02-17 10:21:39 +0100466void
roman83683fb2023-02-24 09:15:23 +0100467nc_server_del_unix_socket(struct nc_bind *bind, struct nc_server_unix_opts *opts)
468{
469 if (bind->sock > -1) {
470 close(bind->sock);
471 }
472
473 free(bind->address);
474 free(opts->address);
475
476 free(opts);
477 opts = NULL;
478}
479
480void
481nc_server_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *bind)
482{
483 nc_server_del_endpt_name(endpt);
484 nc_server_del_unix_socket(bind, endpt->opts.unixsock);
485
486 server_opts.endpt_count--;
487 if (!server_opts.endpt_count) {
488 free(server_opts.endpts);
489 free(server_opts.binds);
490 server_opts.endpts = NULL;
491 server_opts.binds = NULL;
492 }
493}
494
495void
roman45cec4e2023-02-17 10:21:39 +0100496nc_server_config_del_keystore(void)
497{
498 int i, j;
499 struct nc_keystore *ks = &server_opts.keystore;
500
501 /* delete all asymmetric keys */
502 for (i = 0; i < ks->asym_key_count; i++) {
503 free(ks->asym_keys[i].name);
504 free(ks->asym_keys[i].pub_base64);
505 free(ks->asym_keys[i].priv_base64);
506
507 for (j = 0; j < ks->asym_keys[i].cert_count; j++) {
508 /* free associated certificates */
509 free(ks->asym_keys[i].certs[j].name);
510 free(ks->asym_keys[i].certs[j].cert_base64);
511 }
512 free(ks->asym_keys[i].certs);
romand57b3722023-04-05 11:26:25 +0200513 ks->asym_keys[i].certs = NULL;
roman45cec4e2023-02-17 10:21:39 +0100514 ks->asym_keys[i].cert_count = 0;
515 }
516 free(ks->asym_keys);
romand57b3722023-04-05 11:26:25 +0200517 ks->asym_keys = NULL;
roman45cec4e2023-02-17 10:21:39 +0100518 ks->asym_key_count = 0;
519
520 /* delete all symmetric keys */
521 for (i = 0; i < ks->sym_key_count; i++) {
522 free(ks->sym_keys[i].name);
523 free(ks->sym_keys[i].base64);
524 }
525 free(ks->sym_keys);
romand57b3722023-04-05 11:26:25 +0200526 ks->sym_keys = NULL;
roman45cec4e2023-02-17 10:21:39 +0100527 ks->sym_key_count = 0;
528}
529
romand57b3722023-04-05 11:26:25 +0200530void
531nc_server_config_del_trustore(void)
532{
533 int i, j;
534 struct nc_truststore *ts = &server_opts.truststore;
535
536 /* delete all cert bags */
537 for (i = 0; i < ts->cert_bag_count; i++) {
538 free(ts->cert_bags[i].name);
539 for (j = 0; j < ts->cert_bags[i].cert_count; j++) {
540 /* free associated certificates */
541 free(ts->cert_bags[i].certs[j].name);
542 free(ts->cert_bags[i].certs[j].cert_base64);
543 }
544 free(ts->cert_bags[i].certs);
545 ts->cert_bags[i].certs = NULL;
546 ts->cert_bags[i].cert_count = 0;
547 }
548 free(ts->cert_bags);
549 ts->cert_bags = NULL;
550 ts->cert_bag_count = 0;
551
552 /* delete all pubkey bags */
553 for (i = 0; i < ts->pub_bag_count; i++) {
554 free(ts->pub_bags[i].name);
555 for (j = 0; j < ts->pub_bags[i].pubkey_count; j++) {
556 /* free associated pubkeys */
557 free(ts->pub_bags[i].pubkeys[j].name);
558 free(ts->pub_bags[i].pubkeys[j].pub_base64);
559 }
560 free(ts->pub_bags[i].pubkeys);
561 ts->pub_bags[i].pubkeys = NULL;
562 ts->pub_bags[i].pubkey_count = 0;
563 }
564 free(ts->pub_bags);
565 ts->pub_bags = NULL;
566 ts->pub_bag_count = 0;
567}
568
romanc1d2b092023-02-02 08:58:27 +0100569/* presence container */
570int
romane028ef92023-02-24 16:33:08 +0100571nc_server_config_listen(NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100572{
573 uint16_t i;
574
575 assert(op == NC_OP_CREATE || op == NC_OP_DELETE);
576
577 if (op == NC_OP_DELETE) {
578 for (i = 0; i < server_opts.endpt_count; i++) {
roman456f92d2023-04-28 10:28:12 +0200579 switch (server_opts.endpts[i].ti) {
580#ifdef NC_ENABLED_SSH
581 case NC_TI_LIBSSH:
roman83683fb2023-02-24 09:15:23 +0100582 nc_server_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]);
roman456f92d2023-04-28 10:28:12 +0200583 break;
584#endif
585#ifdef NC_ENABLED_TLS
586 case NC_TI_OPENSSL:
roman83683fb2023-02-24 09:15:23 +0100587 /* todo */
roman456f92d2023-04-28 10:28:12 +0200588 break;
589#endif
590 case NC_TI_UNIX:
roman83683fb2023-02-24 09:15:23 +0100591 nc_server_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]);
roman456f92d2023-04-28 10:28:12 +0200592 break;
593 case NC_TI_NONE:
594 case NC_TI_FD:
595 ERRINT;
596 return 1;
roman83683fb2023-02-24 09:15:23 +0100597 }
romanc1d2b092023-02-02 08:58:27 +0100598 }
599 }
600
601 return 0;
602}
603
604/* default leaf */
605static int
romane028ef92023-02-24 16:33:08 +0100606nc_server_config_idle_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100607{
608 assert(!strcmp(LYD_NAME(node), "idle-timeout"));
609
610 if (equal_parent_name(node, 1, "listen")) {
611 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
612 server_opts.idle_timeout = strtoul(lyd_get_value(node), NULL, 10);
613 } else {
614 /* default value */
615 server_opts.idle_timeout = 3600;
616 }
617 }
618
619 return 0;
620}
621
622static int
623nc_server_create_bind(void)
624{
625 int ret = 0;
626 void *tmp;
627
628 tmp = realloc(server_opts.binds, (server_opts.endpt_count + 1) * sizeof *server_opts.binds);
629 if (!tmp) {
630 ERRMEM;
631 ret = 1;
632 goto cleanup;
633 }
634 server_opts.binds = tmp;
635 memset(&server_opts.binds[server_opts.endpt_count], 0, sizeof *server_opts.binds);
636
637 server_opts.binds[server_opts.endpt_count].sock = -1;
638
639cleanup:
640 return ret;
641}
642
643static int
644nc_server_create_endpoint(const struct lyd_node *node)
645{
646 int ret = 0;
647 void *tmp;
648
649 tmp = realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts);
650 if (!tmp) {
651 ERRMEM;
652 ret = 1;
653 goto cleanup;
654 }
655 server_opts.endpts = tmp;
656 memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts);
657
658 node = lyd_child(node);
659 assert(!strcmp(LYD_NAME(node), "name"));
660
661 server_opts.endpts[server_opts.endpt_count].name = strdup(lyd_get_value(node));
662 if (!server_opts.endpts[server_opts.endpt_count].name) {
663 ERRMEM;
664 ret = 1;
665 goto cleanup;
666 }
667
668 if (nc_server_create_bind()) {
669 ret = 1;
670 goto cleanup;
671 }
672
673 server_opts.endpt_count++;
674
675cleanup:
676 return ret;
677}
678
679/* list */
680static int
romane028ef92023-02-24 16:33:08 +0100681nc_server_config_endpoint(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100682{
683 int ret = 0;
684 struct nc_endpt *endpt;
685 struct nc_bind *bind;
686
687 assert(!strcmp(LYD_NAME(node), "endpoint"));
688
689 if (op == NC_OP_CREATE) {
690 ret = nc_server_create_endpoint(node);
691 if (ret) {
692 goto cleanup;
693 }
694 } else if (op == NC_OP_DELETE) {
695 /* free all children */
696 if (nc_server_get_endpt(node, &endpt, &bind)) {
697 ret = 1;
698 goto cleanup;
699 }
700 nc_server_del_endpt_ssh(endpt, bind);
701 }
702
703cleanup:
704 return ret;
705}
706
707static int
708nc_server_create_ssh(struct nc_endpt *endpt)
709{
710 endpt->ti = NC_TI_LIBSSH;
711 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
712 if (!endpt->opts.ssh) {
713 ERRMEM;
714 return 1;
715 }
716
717 return 0;
718}
719
720/* NP container */
721static int
romane028ef92023-02-24 16:33:08 +0100722nc_server_config_ssh(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100723{
724 struct nc_endpt *endpt;
725 struct nc_bind *bind;
726 int ret = 0;
727
728 assert(!strcmp(LYD_NAME(node), "ssh"));
729
730 if (nc_server_get_endpt(node, &endpt, &bind)) {
731 ret = 1;
732 goto cleanup;
733 }
734
735 if (op == NC_OP_CREATE) {
736 ret = nc_server_create_ssh(endpt);
737 if (ret) {
738 goto cleanup;
739 }
740 } else if (op == NC_OP_DELETE) {
741 nc_server_del_ssh(bind, endpt->opts.ssh);
742 }
743
744cleanup:
745 return ret;
746}
747
748static int
749nc_server_config_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port)
750{
751 int sock = -1, set_addr, ret = 0;
752
roman83683fb2023-02-24 09:15:23 +0100753 assert((address && !port) || (!address && port) || (endpt->ti == NC_TI_UNIX));
romanc1d2b092023-02-02 08:58:27 +0100754
755 if (address) {
756 set_addr = 1;
757 } else {
758 set_addr = 0;
759 }
760
761 if (set_addr) {
762 port = bind->port;
763 } else {
764 address = bind->address;
765 }
766
romanc1d2b092023-02-02 08:58:27 +0100767 /* we have all the information we need to create a listening socket */
roman83683fb2023-02-24 09:15:23 +0100768 if ((address && port) || (endpt->ti == NC_TI_UNIX)) {
romanc1d2b092023-02-02 08:58:27 +0100769 /* create new socket, close the old one */
roman83683fb2023-02-24 09:15:23 +0100770 if (endpt->ti == NC_TI_UNIX) {
771 sock = nc_sock_listen_unix(endpt->opts.unixsock);
772 } else {
773 sock = nc_sock_listen_inet(address, port, &endpt->ka);
774 }
775
romanc1d2b092023-02-02 08:58:27 +0100776 if (sock == -1) {
777 ret = 1;
778 goto cleanup;
779 }
780
781 if (bind->sock > -1) {
782 close(bind->sock);
783 }
784 bind->sock = sock;
785 }
786
787 if (sock > -1) {
788 switch (endpt->ti) {
roman83683fb2023-02-24 09:15:23 +0100789 case NC_TI_UNIX:
790 VRB(NULL, "Listening on %s for UNIX connections.", endpt->opts.unixsock->address);
791 break;
romanc1d2b092023-02-02 08:58:27 +0100792#ifdef NC_ENABLED_SSH
793 case NC_TI_LIBSSH:
794 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
795 break;
796#endif
797#ifdef NC_ENABLED_TLS
798 case NC_TI_OPENSSL:
799 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
800 break;
801#endif
802 default:
803 ERRINT;
804 ret = 1;
805 break;
806 }
807 }
808
809cleanup:
810 return ret;
811}
812
813/* mandatory leaf */
814static int
romane028ef92023-02-24 16:33:08 +0100815nc_server_config_local_address(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100816{
817 struct nc_endpt *endpt;
818 struct nc_bind *bind;
819 int ret = 0;
820
821 (void) op;
822
823 assert(!strcmp(LYD_NAME(node), "local-address"));
824
825 if (equal_parent_name(node, 4, "listen")) {
826 if (nc_server_get_endpt(node, &endpt, &bind)) {
827 ret = 1;
828 goto cleanup;
829 }
830
831 nc_server_del_local_address(bind);
832 bind->address = strdup(lyd_get_value(node));
833 if (!bind->address) {
834 ERRMEM;
835 ret = 1;
836 goto cleanup;
837 }
838
839 ret = nc_server_config_set_address_port(endpt, bind, lyd_get_value(node), 0);
840 if (ret) {
841 goto cleanup;
842 }
843 }
844
845cleanup:
846 return ret;
847}
848
849/* leaf with default value */
850static int
romane028ef92023-02-24 16:33:08 +0100851nc_server_config_local_port(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100852{
853 struct nc_endpt *endpt;
854 struct nc_bind *bind;
855 int ret = 0;
856
857 assert(!strcmp(LYD_NAME(node), "local-port"));
858
859 if (equal_parent_name(node, 4, "listen")) {
860 if (nc_server_get_endpt(node, &endpt, &bind)) {
861 ret = 1;
862 goto cleanup;
863 }
864
865 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
866 bind->port = strtoul(lyd_get_value(node), NULL, 10);
867 } else {
868 /* delete -> set to default */
869 bind->port = 0;
870 }
871
872 ret = nc_server_config_set_address_port(endpt, bind, NULL, bind->port);
873 if (ret) {
874 goto cleanup;
875 }
876 }
877
878cleanup:
879 return ret;
880}
881
882/* P container */
883static int
romane028ef92023-02-24 16:33:08 +0100884nc_server_config_keepalives(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100885{
886 struct nc_endpt *endpt;
887 struct nc_bind *bind;
888 int ret = 0;
889
890 assert(!strcmp(LYD_NAME(node), "keepalives"));
891
892 if (equal_parent_name(node, 4, "listen")) {
893 if (nc_server_get_endpt(node, &endpt, &bind)) {
894 ret = 1;
895 goto cleanup;
896 }
897
898 if (op == NC_OP_CREATE) {
899 endpt->ka.enabled = 1;
900 } else {
901 endpt->ka.enabled = 0;
902 }
903 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
904 if (ret) {
905 goto cleanup;
906 }
907 }
908
909cleanup:
910 return ret;
911}
912
913/* mandatory leaf */
914static int
romane028ef92023-02-24 16:33:08 +0100915nc_server_config_idle_time(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100916{
917 struct nc_endpt *endpt;
918 struct nc_bind *bind;
919 int ret = 0;
920
921 assert(!strcmp(LYD_NAME(node), "idle-time"));
922
923 if (equal_parent_name(node, 4, "listen")) {
924 if (nc_server_get_endpt(node, &endpt, &bind)) {
925 ret = 1;
926 goto cleanup;
927 }
928
929 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
930 endpt->ka.idle_time = strtoul(lyd_get_value(node), NULL, 10);
931 } else {
932 endpt->ka.idle_time = 0;
933 }
934 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
935 if (ret) {
936 goto cleanup;
937 }
938 }
939
940cleanup:
941 return ret;
942}
943
944/* mandatory leaf */
945static int
romane028ef92023-02-24 16:33:08 +0100946nc_server_config_max_probes(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100947{
948 struct nc_endpt *endpt;
949 struct nc_bind *bind;
950 int ret = 0;
951
952 assert(!strcmp(LYD_NAME(node), "max-probes"));
953
954 if (equal_parent_name(node, 4, "listen")) {
955 if (nc_server_get_endpt(node, &endpt, &bind)) {
956 ret = 1;
957 goto cleanup;
958 }
959
960 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
961 endpt->ka.max_probes = strtoul(lyd_get_value(node), NULL, 10);
962 } else {
963 endpt->ka.max_probes = 0;
964 }
965 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
966 if (ret) {
967 goto cleanup;
968 }
969 }
970
971cleanup:
972 return ret;
973}
974
975/* mandatory leaf */
976static int
romane028ef92023-02-24 16:33:08 +0100977nc_server_config_probe_interval(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100978{
979 struct nc_endpt *endpt;
980 struct nc_bind *bind;
981 int ret = 0;
982
983 assert(!strcmp(LYD_NAME(node), "probe-interval"));
984
985 if (equal_parent_name(node, 4, "listen")) {
986 if (nc_server_get_endpt(node, &endpt, &bind)) {
987 ret = 1;
988 goto cleanup;
989 }
990
991 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
992 endpt->ka.probe_interval = strtoul(lyd_get_value(node), NULL, 10);
993 } else {
994 endpt->ka.probe_interval = 0;
995 }
996 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
997 if (ret) {
998 goto cleanup;
999 }
1000 }
1001
1002cleanup:
1003 return ret;
1004}
1005
1006static int
1007nc_server_create_host_key(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
1008{
1009 int ret = 0;
1010 void *tmp;
1011
1012 tmp = realloc(opts->hostkeys,
1013 (opts->hostkey_count + 1) * sizeof *opts->hostkeys);
1014 if (!tmp) {
1015 ERRMEM;
1016 ret = 1;
1017 goto cleanup;
1018 }
1019 opts->hostkeys = tmp;
1020
1021 memset(&opts->hostkeys[opts->hostkey_count], 0, sizeof *opts->hostkeys);
1022
1023 opts->hostkeys[opts->hostkey_count].name = strdup(lyd_get_value(lyd_child(node)));
1024 if (!opts->hostkeys[opts->hostkey_count].name) {
1025 ERRMEM;
1026 ret = 1;
1027 goto cleanup;
1028 }
1029
1030 /* set union selector */
1031 lyd_find_path(node, "public-key", 0, (struct lyd_node **)&node);
1032 assert(node);
1033
1034 if (!lyd_find_path(node, "local-definition", 0, NULL)) {
1035 opts->hostkeys[opts->hostkey_count].ks_type = NC_STORE_LOCAL;
1036 } else {
1037 opts->hostkeys[opts->hostkey_count].ks_type = NC_STORE_KEYSTORE;
1038 }
1039
1040 opts->hostkey_count++;
1041
1042cleanup:
1043 return ret;
1044}
1045
1046/* list */
1047static int
romane028ef92023-02-24 16:33:08 +01001048nc_server_config_host_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001049{
1050 struct nc_endpt *endpt;
1051 struct nc_hostkey *hostkey;
1052 int ret = 0;
1053
1054 assert(!strcmp(LYD_NAME(node), "host-key"));
1055
1056 if ((equal_parent_name(node, 1, "server-identity")) && (equal_parent_name(node, 5, "listen"))) {
1057 if (nc_server_get_endpt(node, &endpt, NULL)) {
1058 ret = 1;
1059 goto cleanup;
1060 }
1061
1062 if (op == NC_OP_CREATE) {
1063 ret = nc_server_create_host_key(node, endpt->opts.ssh);
1064 if (ret) {
1065 goto cleanup;
1066 }
1067 } else if (op == NC_OP_DELETE) {
1068 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1069 ret = 1;
1070 goto cleanup;
1071 }
1072
1073 nc_server_del_hostkey(endpt->opts.ssh, hostkey);
1074 }
1075 } else if (equal_parent_name(node, 1, "transport-params")) {
1076 /* just a container with the name host-key, nothing to be done */
1077 goto cleanup;
1078 } else {
1079 ERRINT;
1080 ret = 1;
1081 goto cleanup;
1082 }
1083
1084cleanup:
1085 return ret;
1086}
1087
1088/* mandatory leaf */
romane028ef92023-02-24 16:33:08 +01001089static int
1090nc_server_config_public_key_format(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001091{
1092 const char *format;
1093 struct nc_endpt *endpt;
1094 struct nc_client_auth *auth_client;
roman8edee342023-03-31 13:25:48 +02001095 struct nc_public_key *pubkey;
romanc1d2b092023-02-02 08:58:27 +01001096 struct nc_hostkey *hostkey;
1097 int ret = 0;
1098
1099 assert(!strcmp(LYD_NAME(node), "public-key-format"));
1100
1101 format = ((struct lyd_node_term *)node)->value.ident->name;
1102
1103 if ((equal_parent_name(node, 6, "client-authentication")) && (equal_parent_name(node, 10, "listen"))) {
1104 if (nc_server_get_endpt(node, &endpt, NULL)) {
1105 ret = 1;
1106 goto cleanup;
1107 }
1108
1109 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1110 ret = 1;
1111 goto cleanup;
1112 }
1113
1114 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1115 ret = 1;
1116 goto cleanup;
1117 }
1118
1119 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1120 if (!strcmp(format, "ssh-public-key-format")) {
1121 pubkey->pubkey_type = NC_SSH_PUBKEY_X509;
1122 } else if (!strcmp(format, "subject-public-key-info-format")) {
1123 pubkey->pubkey_type = NC_SSH_PUBKEY_SSH2;
1124 } else {
1125 ERR(NULL, "Public key format (%s) not supported.", format);
1126 }
1127 }
1128 } else if ((equal_parent_name(node, 5, "server-identity")) && (equal_parent_name(node, 11, "listen"))) {
1129 if (nc_server_get_endpt(node, &endpt, NULL)) {
1130 ret = 1;
1131 goto cleanup;
1132 }
1133
1134 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1135 ret = 1;
1136 goto cleanup;
1137 }
1138
1139 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1140 if (!strcmp(format, "ssh-public-key-format")) {
roman8edee342023-03-31 13:25:48 +02001141 hostkey->key.pubkey_type = NC_SSH_PUBKEY_SSH2;
romane028ef92023-02-24 16:33:08 +01001142 } else if (!strcmp(format, "subject-public-key-info-format")) {
roman8edee342023-03-31 13:25:48 +02001143 hostkey->key.pubkey_type = NC_SSH_PUBKEY_X509;
romanc1d2b092023-02-02 08:58:27 +01001144 } else {
1145 ERR(NULL, "Public key format (%s) not supported.", format);
1146 }
1147 }
1148 }
1149
1150cleanup:
1151 return ret;
1152}
1153
1154/* leaf */
romane028ef92023-02-24 16:33:08 +01001155static int
1156nc_server_config_private_key_format(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001157{
1158 const char *format;
1159 struct nc_endpt *endpt;
1160 struct nc_hostkey *hostkey;
1161 int ret = 0;
1162
1163 assert(!strcmp(LYD_NAME(node), "private-key-format"));
1164
1165 if (nc_server_get_endpt(node, &endpt, NULL)) {
1166 ret = 1;
1167 goto cleanup;
1168 }
1169
1170 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1171 ret = 1;
1172 goto cleanup;
1173 }
1174
1175 format = ((struct lyd_node_term *)node)->value.ident->name;
1176 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1177 if (!strcmp(format, "rsa-private-key-format")) {
roman8edee342023-03-31 13:25:48 +02001178 hostkey->key.privkey_type = NC_SSH_KEY_RSA;
romanc1d2b092023-02-02 08:58:27 +01001179 } else if (!strcmp(format, "ec-private-key-format")) {
roman8edee342023-03-31 13:25:48 +02001180 hostkey->key.privkey_type = NC_SSH_KEY_ECDSA;
roman44600f42023-04-28 15:54:27 +02001181 } else if (!strcmp(format, "ed25519-private-key-format")) {
1182 hostkey->key.privkey_type = NC_SSH_KEY_ED25519;
romanc1d2b092023-02-02 08:58:27 +01001183 } else {
1184 ERR(NULL, "Private key format (%s) not supported.", format);
1185 }
1186 }
1187
1188cleanup:
1189 return ret;
1190}
1191
1192static int
1193nc_server_replace_cleartext_private_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
1194{
1195 nc_server_del_private_key(hostkey);
roman8edee342023-03-31 13:25:48 +02001196 hostkey->key.priv_base64 = strdup(lyd_get_value(node));
1197 if (!hostkey->key.priv_base64) {
romanc1d2b092023-02-02 08:58:27 +01001198 ERRMEM;
1199 return 1;
1200 }
1201
1202 return 0;
1203}
1204
1205static int
romane028ef92023-02-24 16:33:08 +01001206nc_server_config_cleartext_private_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001207{
1208 struct nc_endpt *endpt;
1209 struct nc_hostkey *hostkey;
1210 int ret = 0;
1211
1212 assert(!strcmp(LYD_NAME(node), "cleartext-private-key"));
1213
1214 if ((equal_parent_name(node, 6, "ssh")) && (equal_parent_name(node, 8, "listen"))) {
1215 if (nc_server_get_endpt(node, &endpt, NULL)) {
1216 ret = 1;
1217 goto cleanup;
1218 }
1219 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1220 ret = 1;
1221 goto cleanup;
1222 }
1223
1224 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1225 ret = nc_server_replace_cleartext_private_key(node, hostkey);
1226 if (ret) {
1227 goto cleanup;
1228 }
1229 } else {
1230 nc_server_del_private_key(hostkey);
1231 }
1232 }
1233
1234cleanup:
1235 return ret;
1236}
1237
1238static int
1239nc_server_create_keystore_reference(const struct lyd_node *node, struct nc_hostkey *hostkey)
1240{
1241 uint16_t i;
roman45cec4e2023-02-17 10:21:39 +01001242 struct nc_keystore *ks = &server_opts.keystore;
romanc1d2b092023-02-02 08:58:27 +01001243
1244 /* lookup name */
roman45cec4e2023-02-17 10:21:39 +01001245 for (i = 0; i < ks->asym_key_count; i++) {
1246 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
romanc1d2b092023-02-02 08:58:27 +01001247 break;
1248 }
1249 }
1250
roman45cec4e2023-02-17 10:21:39 +01001251 if (i == ks->asym_key_count) {
1252 ERR(NULL, "Keystore \"%s\" not found.", lyd_get_value(node));
romanc1d2b092023-02-02 08:58:27 +01001253 return 1;
1254 }
1255
roman45cec4e2023-02-17 10:21:39 +01001256 hostkey->ks_ref = &ks->asym_keys[i];
romanc1d2b092023-02-02 08:58:27 +01001257
1258 return 0;
1259}
1260
1261/* leaf */
1262static int
romane028ef92023-02-24 16:33:08 +01001263nc_server_config_keystore_reference(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001264{
1265 struct nc_endpt *endpt;
1266 struct nc_hostkey *hostkey;
1267 int ret = 0;
1268
1269 assert(!strcmp(LYD_NAME(node), "keystore-reference"));
1270
roman45cec4e2023-02-17 10:21:39 +01001271 if ((equal_parent_name(node, 3, "server-identity")) && (equal_parent_name(node, 7, "listen"))) {
romanc1d2b092023-02-02 08:58:27 +01001272 if (nc_server_get_endpt(node, &endpt, NULL)) {
1273 ret = 1;
1274 goto cleanup;
1275 }
1276 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1277 ret = 1;
1278 goto cleanup;
1279 }
1280
1281 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1282 ret = nc_server_create_keystore_reference(node, hostkey);
1283 if (ret) {
1284 goto cleanup;
1285 }
1286 } else {
roman45cec4e2023-02-17 10:21:39 +01001287 hostkey->ks_ref = NULL;
romanc1d2b092023-02-02 08:58:27 +01001288 }
1289 }
1290
1291cleanup:
1292 return ret;
1293}
1294
1295static int
1296nc_server_create_auth_key_public_key_list(const struct lyd_node *node, struct nc_client_auth *auth_client)
1297{
1298 int ret = 0;
1299 void *tmp;
1300
1301 assert(!strcmp(LYD_NAME(node), "public-key"));
1302
1303 tmp = realloc(auth_client->pubkeys, (auth_client->pubkey_count + 1) * sizeof *auth_client->pubkeys);
1304 if (!tmp) {
1305 ERRMEM;
1306 ret = 1;
1307 goto cleanup;
1308 }
1309 auth_client->pubkeys = tmp;
1310
1311 memset(&auth_client->pubkeys[auth_client->pubkey_count], 0, sizeof *auth_client->pubkeys);
1312
1313 node = lyd_child(node);
1314 assert(!strcmp(LYD_NAME(node), "name"));
1315
1316 auth_client->pubkeys[auth_client->pubkey_count].name = strdup(lyd_get_value(node));
1317 if (!auth_client->pubkeys[auth_client->pubkey_count].name) {
1318 ERRMEM;
1319 ret = 1;
1320 goto cleanup;
1321 }
1322
1323 ++auth_client->pubkey_count;
1324
1325cleanup:
1326 return ret;
1327}
1328
1329static int
roman8edee342023-03-31 13:25:48 +02001330nc_server_replace_auth_key_public_key_leaf(const struct lyd_node *node, struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +01001331{
1332 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
1333
1334 pubkey->pub_base64 = strdup(lyd_get_value(node));
1335 if (!pubkey->pub_base64) {
1336 ERRMEM;
1337 return 1;
1338 }
1339
1340 return 0;
1341}
1342
1343static int
1344nc_server_replace_host_key_public_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
1345{
1346 nc_server_del_public_key(hostkey);
1347
roman8edee342023-03-31 13:25:48 +02001348 hostkey->key.pub_base64 = strdup(lyd_get_value(node));
1349 if (!hostkey->key.pub_base64) {
romanc1d2b092023-02-02 08:58:27 +01001350 ERRMEM;
1351 return 1;
1352 }
1353
1354 return 0;
1355}
1356
1357static int
romane028ef92023-02-24 16:33:08 +01001358nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001359{
1360 struct nc_endpt *endpt;
1361 struct nc_hostkey *hostkey;
1362 struct nc_client_auth *auth_client;
roman8edee342023-03-31 13:25:48 +02001363 struct nc_public_key *pubkey;
romanc1d2b092023-02-02 08:58:27 +01001364 int ret = 0;
1365
1366 assert(!strcmp(LYD_NAME(node), "public-key"));
1367
1368 if ((equal_parent_name(node, 3, "host-key")) && (equal_parent_name(node, 8, "listen"))) {
1369 /* server's public-key, mandatory leaf */
1370 if (nc_server_get_endpt(node, &endpt, NULL)) {
1371 ret = 1;
1372 goto cleanup;
1373 }
1374
1375 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1376 ret = 1;
1377 goto cleanup;
1378 }
1379
1380 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1381 ret = nc_server_replace_host_key_public_key(node, hostkey);
1382 if (ret) {
1383 goto cleanup;
1384 }
1385 }
1386 } else if ((equal_parent_name(node, 5, "client-authentication")) && (equal_parent_name(node, 9, "listen"))) {
1387 /* client auth pubkeys, list */
1388 if (nc_server_get_endpt(node, &endpt, NULL)) {
1389 ret = 1;
1390 goto cleanup;
1391 }
1392
1393 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1394 ret = 1;
1395 goto cleanup;
1396 }
1397
1398 if (op == NC_OP_CREATE) {
1399 ret = nc_server_create_auth_key_public_key_list(node, auth_client);
1400 if (ret) {
1401 goto cleanup;
1402 }
1403 } else if (op == NC_OP_DELETE) {
1404 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1405 ret = 1;
1406 goto cleanup;
1407 }
1408
1409 nc_server_del_auth_client_pubkey(auth_client, pubkey);
1410 }
1411 } else if ((equal_parent_name(node, 6, "client-authentication")) && (equal_parent_name(node, 10, "listen"))) {
1412 /* client auth pubkey, leaf */
1413 if (nc_server_get_endpt(node, &endpt, NULL)) {
1414 ret = 1;
1415 goto cleanup;
1416 }
1417
1418 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1419 ret = 1;
1420 goto cleanup;
1421 }
1422
1423 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1424 ret = 1;
1425 goto cleanup;
1426 }
1427
1428 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1429 ret = nc_server_replace_auth_key_public_key_leaf(node, pubkey);
1430 if (ret) {
1431 goto cleanup;
1432 }
1433 } else {
1434 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
1435 }
1436 }
1437
1438cleanup:
1439 return ret;
1440}
1441
1442static int
1443nc_server_create_user(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
1444{
1445 int ret = 0;
1446 void *tmp;
1447
1448 tmp = realloc(opts->auth_clients, (opts->client_count + 1) * sizeof *opts->auth_clients);
1449 if (!tmp) {
1450 ERRMEM;
1451 ret = 1;
1452 goto cleanup;
1453 }
1454 opts->auth_clients = tmp;
1455
1456 memset(&opts->auth_clients[opts->client_count], 0, sizeof *opts->auth_clients);
1457
1458 opts->auth_clients[opts->client_count].username = strdup(lyd_get_value(lyd_child(node)));
1459 if (!opts->auth_clients[opts->client_count].username) {
1460 ERRMEM;
1461 ret = 1;
1462 goto cleanup;
1463 }
1464
1465 lyd_find_path(node, "public-keys", 0, (struct lyd_node **)&node);
1466
1467 if (node) {
1468 /* set union selector */
1469 if (!lyd_find_path(node, "local-definition", 0, NULL)) {
1470 opts->auth_clients[opts->client_count].ks_type = NC_STORE_LOCAL;
1471 } else {
1472 opts->auth_clients[opts->client_count].ks_type = NC_STORE_TRUSTSTORE;
1473 }
1474 }
1475
1476 ++opts->client_count;
1477
1478cleanup:
1479 return ret;
1480}
1481
1482/* list */
1483static int
romane028ef92023-02-24 16:33:08 +01001484nc_server_config_user(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001485{
1486 struct nc_endpt *endpt;
1487 struct nc_client_auth *auth_client;
1488 int ret = 0;
1489
1490 assert(!strcmp(LYD_NAME(node), "user"));
1491
1492 if (equal_parent_name(node, 6, "listen")) {
1493 if (nc_server_get_endpt(node, &endpt, NULL)) {
1494 ret = 1;
1495 goto cleanup;
1496 }
1497
1498 if (op == NC_OP_CREATE) {
1499 ret = nc_server_create_user(node, endpt->opts.ssh);
1500 if (ret) {
1501 goto cleanup;
1502 }
1503 } else if (op == NC_OP_DELETE) {
1504 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1505 ret = 1;
1506 goto cleanup;
1507 }
1508
1509 nc_server_del_auth_client(endpt->opts.ssh, auth_client);
1510 }
1511 }
1512
1513cleanup:
1514 return ret;
1515}
1516
1517static int
romane028ef92023-02-24 16:33:08 +01001518nc_server_config_auth_attempts(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001519{
1520 struct nc_endpt *endpt;
1521 int ret = 0;
1522
1523 assert(!strcmp(LYD_NAME(node), "auth-attempts"));
1524
1525 if (equal_parent_name(node, 5, "listen")) {
1526 if (nc_server_get_endpt(node, &endpt, NULL)) {
1527 ret = 1;
1528 goto cleanup;
1529 }
1530
1531 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1532 endpt->opts.ssh->auth_attempts = strtoul(lyd_get_value(node), NULL, 10);
1533 }
1534 }
1535
1536cleanup:
1537 return ret;
1538}
1539
1540static int
romane028ef92023-02-24 16:33:08 +01001541nc_server_config_auth_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001542{
1543 struct nc_endpt *endpt;
1544 int ret = 0;
1545
1546 assert(!strcmp(LYD_NAME(node), "auth-timeout"));
1547
1548 if (equal_parent_name(node, 5, "listen")) {
1549 if (nc_server_get_endpt(node, &endpt, NULL)) {
1550 ret = 1;
1551 goto cleanup;
1552 }
1553
1554 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1555 endpt->opts.ssh->auth_timeout = strtoul(lyd_get_value(node), NULL, 10);
1556 }
1557 }
1558
1559cleanup:
1560 return ret;
1561}
1562
1563static int
1564nc_server_replace_truststore_reference(const struct lyd_node *node, struct nc_client_auth *client_auth)
1565{
romand57b3722023-04-05 11:26:25 +02001566 uint16_t i;
1567 struct nc_truststore *ts = &server_opts.truststore;
romanc1d2b092023-02-02 08:58:27 +01001568
romand57b3722023-04-05 11:26:25 +02001569 /* lookup name */
1570 for (i = 0; i < ts->pub_bag_count; i++) {
1571 if (!strcmp(lyd_get_value(node), ts->pub_bags[i].name)) {
1572 break;
1573 }
1574 }
1575
1576 if (i == ts->pub_bag_count) {
1577 ERR(NULL, "Truststore \"%s\" not found.", lyd_get_value(node));
romanc1d2b092023-02-02 08:58:27 +01001578 return 1;
1579 }
1580
romand57b3722023-04-05 11:26:25 +02001581 client_auth->ts_ref = &ts->pub_bags[i];
1582
romanc1d2b092023-02-02 08:58:27 +01001583 return 0;
1584}
1585
1586/* leaf */
1587static int
romane028ef92023-02-24 16:33:08 +01001588nc_server_config_truststore_reference(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001589{
1590 struct nc_endpt *endpt;
1591 struct nc_client_auth *auth_client;
1592 int ret = 0;
1593
1594 assert(!strcmp(LYD_NAME(node), "truststore-reference"));
1595
1596 if ((equal_parent_name(node, 1, "public-keys")) && (equal_parent_name(node, 8, "listen"))) {
1597 if (nc_server_get_endpt(node, &endpt, NULL)) {
1598 ret = 1;
1599 goto cleanup;
1600 }
1601
1602 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1603 ret = 1;
1604 goto cleanup;
1605 }
1606
1607 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1608 ret = nc_server_replace_truststore_reference(node, auth_client);
1609 if (ret) {
1610 goto cleanup;
1611 }
1612 } else {
romand57b3722023-04-05 11:26:25 +02001613 auth_client->ts_ref = NULL;
romanc1d2b092023-02-02 08:58:27 +01001614 }
1615 }
1616
1617cleanup:
1618 return ret;
1619}
1620
1621static int
1622nc_server_replace_password(const struct lyd_node *node, struct nc_client_auth *auth_client)
1623{
1624 nc_server_del_auth_client_password(auth_client);
1625
1626 auth_client->password = strdup(lyd_get_value(node));
1627 if (!auth_client->password) {
1628 ERRMEM;
1629 return 1;
1630 }
1631
1632 return 0;
1633}
1634
1635/* leaf */
1636static int
romane028ef92023-02-24 16:33:08 +01001637nc_server_config_password(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001638{
1639 struct nc_endpt *endpt;
1640 struct nc_client_auth *auth_client;
1641 int ret = 0;
1642
1643 assert(!strcmp(LYD_NAME(node), "password"));
1644
1645 if (equal_parent_name(node, 7, "listen")) {
1646 if (nc_server_get_endpt(node, &endpt, NULL)) {
1647 ret = 1;
1648 goto cleanup;
1649 }
1650
1651 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1652 ret = 1;
1653 goto cleanup;
1654 }
1655
1656 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1657 ret = nc_server_replace_password(node, auth_client);
1658 if (ret) {
1659 goto cleanup;
1660 }
1661 } else {
1662 nc_server_del_auth_client_password(auth_client);
1663 }
1664 }
1665
1666cleanup:
1667 return ret;
1668}
1669
1670static int
romane028ef92023-02-24 16:33:08 +01001671nc_server_config_pam_name(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001672{
1673 struct nc_endpt *endpt;
1674 struct nc_client_auth *auth_client;
1675 int ret = 0;
1676
1677 assert(!strcmp(LYD_NAME(node), "pam-config-file-name"));
1678
1679 if (equal_parent_name(node, 8, "listen")) {
1680 if (nc_server_get_endpt(node, &endpt, NULL)) {
1681 ret = 1;
1682 goto cleanup;
1683 }
1684
1685 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1686 ret = 1;
1687 goto cleanup;
1688 }
1689
1690 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1691 nc_server_del_auth_client_pam_name(auth_client);
1692
1693 auth_client->pam_config_name = strdup(lyd_get_value(node));
1694 if (!auth_client->pam_config_name) {
1695 ERRMEM;
1696 ret = 1;
1697 goto cleanup;
1698 }
1699 }
1700 }
1701
1702cleanup:
1703 return ret;
1704}
1705
1706static int
romane028ef92023-02-24 16:33:08 +01001707nc_server_config_pam_dir(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001708{
1709 struct nc_endpt *endpt;
1710 struct nc_client_auth *auth_client;
1711 int ret = 0;
1712
1713 assert(!strcmp(LYD_NAME(node), "pam-config-file-dir"));
1714
1715 if (equal_parent_name(node, 8, "listen")) {
1716 if (nc_server_get_endpt(node, &endpt, NULL)) {
1717 ret = 1;
1718 goto cleanup;
1719 }
1720
1721 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1722 ret = 1;
1723 goto cleanup;
1724 }
1725
1726 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1727 nc_server_del_auth_client_pam_dir(auth_client);
1728 auth_client->pam_config_dir = strdup(lyd_get_value(node));
1729 if (!auth_client->pam_config_dir) {
1730 ERRMEM;
1731 ret = 1;
1732 goto cleanup;
1733 }
1734 }
1735 }
1736
1737cleanup:
1738 return ret;
1739}
1740
1741/* leaf */
1742static int
romane028ef92023-02-24 16:33:08 +01001743nc_server_config_none(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001744{
1745 struct nc_endpt *endpt;
1746 struct nc_client_auth *auth_client;
1747 int ret = 0;
1748
1749 assert(!strcmp(LYD_NAME(node), "none"));
1750
1751 if (equal_parent_name(node, 7, "listen")) {
1752 if (nc_server_get_endpt(node, &endpt, NULL)) {
1753 ret = 1;
1754 goto cleanup;
1755 }
1756
1757 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1758 ret = 1;
1759 goto cleanup;
1760 }
1761
1762 if (op == NC_OP_CREATE) {
1763 auth_client->supports_none = 1;
1764 } else {
1765 auth_client->supports_none = 0;
1766 }
1767 }
1768
1769cleanup:
1770 return ret;
1771}
1772
1773static int
romane028ef92023-02-24 16:33:08 +01001774nc_server_config_transport_params(const char *alg, char **alg_store, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001775{
1776 int ret = 0, alg_found = 0;
1777 char *substr, *haystack;
1778 size_t alg_len = strlen(alg);
1779
1780 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1781 if (!*alg_store) {
1782 /* first call */
1783 *alg_store = strdup(alg);
1784 if (!*alg_store) {
1785 ERRMEM;
1786 ret = 1;
1787 goto cleanup;
1788 }
1789 } else {
1790 /* +1 because of ',' between algorithms */
1791 *alg_store = nc_realloc(*alg_store, strlen(*alg_store) + alg_len + 1 + 1);
1792 if (!*alg_store) {
1793 ERRMEM;
1794 ret = 1;
1795 goto cleanup;
1796 }
1797 sprintf(*alg_store, "%s,%s", *alg_store, alg);
1798 }
1799 } else {
1800 /* delete */
1801 haystack = *alg_store;
1802 while ((substr = strstr(haystack, alg))) {
1803 /* iterate over all the substrings */
1804 if (((substr == haystack) && (*(substr + alg_len) == ',')) ||
1805 ((substr != haystack) && (*(substr - 1) == ',') && (*(substr + alg_len) == ','))) {
1806 /* either the first element of the string or somewhere in the middle */
1807 memmove(substr, substr + alg_len + 1, strlen(substr + alg_len + 1));
1808 alg_found = 1;
1809 break;
1810 } else if ((*(substr - 1) == ',') && (*(substr + alg_len) == '\0')) {
1811 /* the last element of the string */
1812 *(substr - 1) = '\0';
1813 alg_found = 1;
1814 break;
1815 }
1816 haystack++;
1817 }
1818 if (!alg_found) {
1819 ERR(NULL, "Unable to delete an algorithm (%s), which was not previously added.", alg);
1820 ret = 1;
1821 }
1822 }
1823
1824cleanup:
1825 return ret;
1826}
1827
1828/* leaf-list */
1829static int
romane028ef92023-02-24 16:33:08 +01001830nc_server_config_host_key_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001831{
1832 struct nc_endpt *endpt;
1833 int ret = 0, listen = 0;
1834 const char *alg;
1835 uint8_t i;
1836
1837 /* get the algorithm name and compare it with algs supported by libssh */
1838 alg = ((struct lyd_node_term *)node)->value.ident->name;
1839
1840 if (equal_parent_name(node, 6, "listen")) {
1841 listen = 1;
1842 if (nc_server_get_endpt(node, &endpt, NULL)) {
1843 ret = 1;
1844 goto cleanup;
1845 }
1846 }
1847
1848 i = 0;
1849 while (supported_hostkey_algs[i]) {
1850 if (!strcmp(supported_hostkey_algs[i], alg)) {
1851 if (listen) {
romane028ef92023-02-24 16:33:08 +01001852 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->hostkey_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001853 ret = 1;
1854 goto cleanup;
1855 }
1856 }
1857 break;
1858 }
1859 i++;
1860 }
1861 if (!supported_hostkey_algs[i]) {
1862 /* algorithm not supported */
1863 ERR(NULL, "Public key algorithm (%s) not supported by libssh.", alg);
1864 ret = 1;
1865 }
1866
1867cleanup:
1868 return ret;
1869}
1870
1871/* leaf-list */
1872static int
romane028ef92023-02-24 16:33:08 +01001873nc_server_config_kex_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001874{
1875 struct nc_endpt *endpt;
1876 int ret = 0, listen = 0;
1877 const char *alg;
1878 uint8_t i;
1879
1880 /* get the algorithm name and compare it with algs supported by libssh */
1881 alg = ((struct lyd_node_term *)node)->value.ident->name;
1882
1883 if (equal_parent_name(node, 6, "listen")) {
1884 listen = 1;
1885 if (nc_server_get_endpt(node, &endpt, NULL)) {
1886 ret = 1;
1887 goto cleanup;
1888 }
1889 }
1890
1891 i = 0;
1892 while (supported_kex_algs[i]) {
1893 if (!strcmp(supported_kex_algs[i], alg)) {
1894 if (listen) {
romane028ef92023-02-24 16:33:08 +01001895 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->kex_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001896 ret = 1;
1897 goto cleanup;
1898 }
1899 }
1900 break;
1901 }
1902 i++;
1903 }
1904 if (!supported_kex_algs[i]) {
1905 /* algorithm not supported */
1906 ERR(NULL, "Key exchange algorithm (%s) not supported by libssh.", alg);
1907 ret = 1;
1908 }
1909
1910cleanup:
1911 return ret;
1912}
1913
1914/* leaf-list */
1915static int
romane028ef92023-02-24 16:33:08 +01001916nc_server_config_encryption_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001917{
1918 struct nc_endpt *endpt;
1919 int ret = 0, listen = 0;
1920 const char *alg;
1921 uint8_t i;
1922
1923 /* get the algorithm name and compare it with algs supported by libssh */
1924 alg = ((struct lyd_node_term *)node)->value.ident->name;
1925
1926 if (equal_parent_name(node, 6, "listen")) {
1927 listen = 1;
1928 if (nc_server_get_endpt(node, &endpt, NULL)) {
1929 ret = 1;
1930 goto cleanup;
1931 }
1932 }
1933
1934 i = 0;
1935 while (supported_encryption_algs[i]) {
1936 if (!strcmp(supported_encryption_algs[i], alg)) {
1937 if (listen) {
romane028ef92023-02-24 16:33:08 +01001938 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->encryption_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001939 ret = 1;
1940 goto cleanup;
1941 }
1942 }
1943 break;
1944 }
1945 i++;
1946 }
1947 if (!supported_encryption_algs[i]) {
1948 /* algorithm not supported */
1949 ERR(NULL, "Encryption algorithm (%s) not supported by libssh.", alg);
1950 ret = 1;
1951 }
1952
1953cleanup:
1954 return ret;
1955}
1956
1957/* leaf-list */
1958static int
romane028ef92023-02-24 16:33:08 +01001959nc_server_config_mac_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001960{
1961 struct nc_endpt *endpt;
1962 int ret = 0, listen = 0;
1963 const char *alg;
1964 uint8_t i;
1965
1966 /* get the algorithm name and compare it with algs supported by libssh */
1967 alg = ((struct lyd_node_term *)node)->value.ident->name;
1968
1969 if (equal_parent_name(node, 6, "listen")) {
1970 listen = 1;
1971 if (nc_server_get_endpt(node, &endpt, NULL)) {
1972 ret = 1;
1973 goto cleanup;
1974 }
1975 }
1976
1977 i = 0;
1978 while (supported_mac_algs[i]) {
1979 if (!strcmp(supported_mac_algs[i], alg)) {
1980 if (listen) {
romane028ef92023-02-24 16:33:08 +01001981 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->mac_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001982 ret = 1;
1983 goto cleanup;
1984 }
1985 }
1986 break;
1987 }
1988 i++;
1989 }
1990 if (!supported_mac_algs[i]) {
1991 /* algorithm not supported */
1992 ERR(NULL, "MAC algorithm (%s) not supported by libssh.", alg);
1993 ret = 1;
1994 }
1995
1996cleanup:
1997 return ret;
1998}
1999
2000static int
roman83683fb2023-02-24 09:15:23 +01002001nc_server_create_unix_socket(struct nc_endpt *endpt)
2002{
2003 endpt->ti = NC_TI_UNIX;
2004 endpt->opts.unixsock = calloc(1, sizeof *endpt->opts.unixsock);
2005 if (!endpt->opts.unixsock) {
2006 ERRMEM;
2007 return 1;
2008 }
2009
2010 /* set default values */
2011 endpt->opts.unixsock->mode = -1;
2012 endpt->opts.unixsock->uid = -1;
2013 endpt->opts.unixsock->gid = -1;
2014
2015 return 0;
2016}
2017
2018static int
romane028ef92023-02-24 16:33:08 +01002019nc_server_config_unix_socket(const struct lyd_node *node, NC_OPERATION op)
roman83683fb2023-02-24 09:15:23 +01002020{
2021 int ret = 0;
2022 uint32_t prev_lo;
2023 struct nc_endpt *endpt;
2024 struct nc_bind *bind;
2025 struct nc_server_unix_opts *opts;
2026 struct lyd_node *data = NULL;
2027
2028 assert(!strcmp(LYD_NAME(node), "unix-socket"));
2029
2030 if (nc_server_get_endpt(node, &endpt, &bind)) {
2031 ret = 1;
2032 goto cleanup;
2033 }
2034
2035 if (op == NC_OP_CREATE) {
2036 if (nc_server_create_unix_socket(endpt)) {
2037 ret = 1;
2038 goto cleanup;
2039 }
2040
2041 opts = endpt->opts.unixsock;
2042
2043 lyd_find_path(node, "path", 0, &data);
2044 assert(data);
2045
2046 opts->address = strdup(lyd_get_value(data));
2047 bind->address = strdup(lyd_get_value(data));
2048 if (!opts->address || !bind->address) {
2049 ERRMEM;
2050 ret = 1;
2051 goto cleanup;
2052 }
2053
2054 /* silently search for non-mandatory parameters */
2055 prev_lo = ly_log_options(0);
2056 ret = lyd_find_path(node, "mode", 0, &data);
2057 if (!ret) {
2058 opts->mode = strtol(lyd_get_value(data), NULL, 8);
2059 }
2060
2061 ret = lyd_find_path(node, "uid", 0, &data);
2062 if (!ret) {
2063 opts->uid = strtol(lyd_get_value(data), NULL, 10);
2064 }
2065
2066 ret = lyd_find_path(node, "gid", 0, &data);
2067 if (!ret) {
2068 opts->gid = strtol(lyd_get_value(data), NULL, 10);
2069 }
2070
2071 /* reset the logging options */
2072 ly_log_options(prev_lo);
2073
2074 ret = nc_server_config_set_address_port(endpt, bind, NULL, 0);
2075 if (ret) {
2076 goto cleanup;
2077 }
2078 } else if (op == NC_OP_DELETE) {
2079 nc_server_del_unix_socket(bind, endpt->opts.unixsock);
2080 }
2081
2082cleanup:
2083 return ret;
2084}
2085
2086static int
romanc1d2b092023-02-02 08:58:27 +01002087nc_server_configure(const struct lyd_node *node, NC_OPERATION op)
2088{
2089 const char *name = LYD_NAME(node);
2090
2091 if (!strcmp(name, "listen")) {
romane028ef92023-02-24 16:33:08 +01002092 if (nc_server_config_listen(op)) {
romanc1d2b092023-02-02 08:58:27 +01002093 goto error;
2094 }
2095 } else if (!strcmp(name, "idle-timeout")) {
romane028ef92023-02-24 16:33:08 +01002096 if (nc_server_config_idle_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002097 goto error;
2098 }
2099 } else if (!strcmp(name, "endpoint")) {
romane028ef92023-02-24 16:33:08 +01002100 if (nc_server_config_endpoint(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002101 goto error;
2102 }
2103 } else if (!strcmp(name, "ssh")) {
romane028ef92023-02-24 16:33:08 +01002104 if (nc_server_config_ssh(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002105 goto error;
2106 }
2107 } else if (!strcmp(name, "local-address")) {
romane028ef92023-02-24 16:33:08 +01002108 if (nc_server_config_local_address(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002109 goto error;
2110 }
2111 } else if (!strcmp(name, "local-port")) {
romane028ef92023-02-24 16:33:08 +01002112 if (nc_server_config_local_port(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002113 goto error;
2114 }
2115 } else if (!strcmp(name, "keepalives")) {
romane028ef92023-02-24 16:33:08 +01002116 if (nc_server_config_keepalives(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002117 goto error;
2118 }
2119 } else if (!strcmp(name, "idle-time")) {
romane028ef92023-02-24 16:33:08 +01002120 if (nc_server_config_idle_time(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002121 goto error;
2122 }
2123 } else if (!strcmp(name, "max-probes")) {
romane028ef92023-02-24 16:33:08 +01002124 if (nc_server_config_max_probes(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002125 goto error;
2126 }
2127 } else if (!strcmp(name, "probe-interval")) {
romane028ef92023-02-24 16:33:08 +01002128 if (nc_server_config_probe_interval(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002129 goto error;
2130 }
2131 } else if (!strcmp(name, "host-key")) {
romane028ef92023-02-24 16:33:08 +01002132 if (nc_server_config_host_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002133 goto error;
2134 }
2135 } else if (!strcmp(name, "public-key-format")) {
romane028ef92023-02-24 16:33:08 +01002136 if (nc_server_config_public_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002137 goto error;
2138 }
2139 } else if (!strcmp(name, "public-key")) {
romane028ef92023-02-24 16:33:08 +01002140 if (nc_server_config_public_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002141 goto error;
2142 }
2143 } else if (!strcmp(name, "private-key-format")) {
romane028ef92023-02-24 16:33:08 +01002144 if (nc_server_config_private_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002145 goto error;
2146 }
2147 } else if (!strcmp(name, "cleartext-private-key")) {
romane028ef92023-02-24 16:33:08 +01002148 if (nc_server_config_cleartext_private_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002149 goto error;
2150 }
2151 } else if (!strcmp(name, "keystore-reference")) {
romane028ef92023-02-24 16:33:08 +01002152 if (nc_server_config_keystore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002153 goto error;
2154 }
2155 } else if (!strcmp(name, "user")) {
romane028ef92023-02-24 16:33:08 +01002156 if (nc_server_config_user(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002157 goto error;
2158 }
2159 } else if (!strcmp(name, "auth-attempts")) {
romane028ef92023-02-24 16:33:08 +01002160 if (nc_server_config_auth_attempts(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002161 goto error;
2162 }
2163 } else if (!strcmp(name, "auth-timeout")) {
romane028ef92023-02-24 16:33:08 +01002164 if (nc_server_config_auth_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002165 goto error;
2166 }
2167 } else if (!strcmp(name, "truststore-reference")) {
romane028ef92023-02-24 16:33:08 +01002168 if (nc_server_config_truststore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002169 goto error;
2170 }
2171 } else if (!strcmp(name, "password")) {
romane028ef92023-02-24 16:33:08 +01002172 if (nc_server_config_password(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002173 goto error;
2174 }
2175 } else if (!strcmp(name, "pam-config-file-name")) {
romane028ef92023-02-24 16:33:08 +01002176 if (nc_server_config_pam_name(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002177 goto error;
2178 }
2179 } else if (!strcmp(name, "pam-config-file-dir")) {
romane028ef92023-02-24 16:33:08 +01002180 if (nc_server_config_pam_dir(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002181 goto error;
2182 }
2183 } else if (!strcmp(name, "none")) {
romane028ef92023-02-24 16:33:08 +01002184 if (nc_server_config_none(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002185 goto error;
2186 }
2187 } else if (!strcmp(name, "host-key-alg")) {
romane028ef92023-02-24 16:33:08 +01002188 if (nc_server_config_host_key_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002189 goto error;
2190 }
2191 } else if (!strcmp(name, "key-exchange-alg")) {
romane028ef92023-02-24 16:33:08 +01002192 if (nc_server_config_kex_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002193 goto error;
2194 }
2195 } else if (!strcmp(name, "encryption-alg")) {
romane028ef92023-02-24 16:33:08 +01002196 if (nc_server_config_encryption_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002197 goto error;
2198 }
2199 } else if (!strcmp(name, "mac-alg")) {
romane028ef92023-02-24 16:33:08 +01002200 if (nc_server_config_mac_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002201 goto error;
2202 }
roman83683fb2023-02-24 09:15:23 +01002203 } else if (!strcmp(name, "unix-socket")) {
romane028ef92023-02-24 16:33:08 +01002204 if (nc_server_config_unix_socket(node, op)) {
roman83683fb2023-02-24 09:15:23 +01002205 goto error;
2206 }
romanc1d2b092023-02-02 08:58:27 +01002207 } else if (!strcmp(name, "cert-data")) {} else if (!strcmp(name, "expiration-date")) {} else if (!strcmp(name, "asymmetric-key")) {} else if (!strcmp(name, "certificate")) {} else if (!strcmp(name, "key-format")) {} else if (!strcmp(name,
2208 "cleartext-key")) {} else if (!strcmp(name, "hidden-key")) {} else if (!strcmp(name, "id_hint")) {} else if (!strcmp(name, "external-identity")) {} else if (!strcmp(name, "hash")) {} else if (!strcmp(name, "context")) {} else if (!strcmp(name,
2209 "target-protocol")) {} else if (!strcmp(name, "target-kdf")) {} else if (!strcmp(name, "client-authentication")) {} else if (!strcmp(name, "ca-certs")) {} else if (!strcmp(name, "ee-certs")) {} else if (!strcmp(name,
2210 "raw-public-keys")) {} else if (!strcmp(name, "tls12-psks")) {} else if (!strcmp(name, "tls13-epsks")) {} else if (!strcmp(name, "tls-version")) {} else if (!strcmp(name, "cipher-suite")) {} else if (!strcmp(name,
2211 "peer-allowed-to-send")) {} else if (!strcmp(name, "test-peer-aliveness")) {} else if (!strcmp(name, "max-wait")) {} else if (!strcmp(name, "max-attempts")) {} else if (!strcmp(name, "cert-to-name")) {} else if (!strcmp(name,
2212 "id")) {} else if (!strcmp(name, "fingerprint")) {} else if (!strcmp(name, "map-type")) {}
2213
2214 return 0;
2215
2216error:
2217 ERR(NULL, "Configuring (%s) failed.", LYD_NAME(node));
2218 return 1;
2219}
2220
2221int
2222nc_session_server_parse_tree(const struct lyd_node *node, NC_OPERATION parent_op)
2223{
2224 struct lyd_node *child;
2225 struct lyd_meta *m;
2226 NC_OPERATION current_op;
2227
2228 assert(node);
2229
2230 /* get current op */
2231 LY_LIST_FOR(node->meta, m) {
2232 if (!strcmp(m->name, "operation")) {
2233 if (!strcmp(lyd_get_meta_value(m), "create")) {
2234 current_op = NC_OP_CREATE;
2235 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
2236 current_op = NC_OP_DELETE;
2237 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
2238 current_op = NC_OP_REPLACE;
2239 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
2240 current_op = NC_OP_NONE;
2241 }
2242 break;
2243 }
2244 }
2245
2246 /* node has no op, inherit from the parent */
2247 if (!m) {
2248 current_op = parent_op;
2249 }
2250
2251 switch (current_op) {
2252 case NC_OP_NONE:
2253 break;
2254 case NC_OP_CREATE:
2255 case NC_OP_DELETE:
2256 case NC_OP_REPLACE:
2257 if (nc_server_configure(node, current_op)) {
2258 return 1;
2259 }
2260 break;
2261 default:
2262 break;
2263 }
2264
2265 if (current_op != NC_OP_DELETE) {
2266 LY_LIST_FOR(lyd_child(node), child) {
2267 if (nc_session_server_parse_tree(child, current_op)) {
2268 return 1;
2269 }
2270 }
2271 }
2272 return 0;
2273}
2274
2275static int
roman8edee342023-03-31 13:25:48 +02002276nc_server_config_asymmetric_key_certificate(const struct lyd_node *tree, struct nc_asymmetric_key *key)
romanc1d2b092023-02-02 08:58:27 +01002277{
2278 int ret = 0;
roman45cec4e2023-02-17 10:21:39 +01002279 struct lyd_node *node;
romanc1d2b092023-02-02 08:58:27 +01002280 void *tmp;
2281
roman45cec4e2023-02-17 10:21:39 +01002282 /* create new certificate */
2283 tmp = realloc(key->certs, (key->cert_count + 1) * sizeof *key->certs);
2284 if (!tmp) {
2285 ERRMEM;
2286 ret = 1;
2287 goto cleanup;
2288 }
2289 key->certs = tmp;
2290 key->cert_count++;
2291
2292 /* set name */
2293 lyd_find_path(tree, "name", 0, &node);
2294 assert(node);
2295
2296 key->certs[key->cert_count - 1].name = strdup(lyd_get_value(node));
2297 if (!key->certs[key->cert_count - 1].name) {
2298 ERRMEM;
2299 ret = 1;
romanc1d2b092023-02-02 08:58:27 +01002300 goto cleanup;
2301 }
2302
roman45cec4e2023-02-17 10:21:39 +01002303 /* set certificate data */
2304 lyd_find_path(tree, "cert-data", 0, &node);
2305 assert(node);
romanc1d2b092023-02-02 08:58:27 +01002306
roman45cec4e2023-02-17 10:21:39 +01002307 key->certs[key->cert_count - 1].cert_base64 = strdup(lyd_get_value(node));
2308 if (!key->certs[key->cert_count - 1].cert_base64) {
2309 ERRMEM;
2310 ret = 1;
2311 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002312 }
2313
2314cleanup:
roman45cec4e2023-02-17 10:21:39 +01002315 return ret;
2316}
2317
2318static int
romane028ef92023-02-24 16:33:08 +01002319nc_server_config_asymmetric_key(const struct lyd_node *tree)
roman45cec4e2023-02-17 10:21:39 +01002320{
2321 int ret = 0;
2322 struct lyd_node *node = NULL, *iter;
2323 void *tmp;
2324 struct nc_keystore *ks = &server_opts.keystore;
roman8edee342023-03-31 13:25:48 +02002325 struct nc_asymmetric_key *key;
roman45cec4e2023-02-17 10:21:39 +01002326 const char *format;
2327
2328 /* create new asymmetric key */
2329 tmp = realloc(ks->asym_keys, (ks->asym_key_count + 1) * sizeof *ks->asym_keys);
2330 if (!tmp) {
2331 ERRMEM;
2332 ret = 1;
2333 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002334 }
roman45cec4e2023-02-17 10:21:39 +01002335 ks->asym_keys = tmp;
2336 memset(&ks->asym_keys[ks->asym_key_count], 0, sizeof *ks->asym_keys);
2337 key = &ks->asym_keys[ks->asym_key_count];
2338 ks->asym_key_count++;
2339
2340 /* set name */
2341 lyd_find_path(tree, "name", 0, &node);
2342 assert(node);
2343
2344 key->name = strdup(lyd_get_value(node));
2345 if (!key->name) {
2346 ERRMEM;
2347 ret = 1;
2348 goto cleanup;
2349 }
2350
2351 /* set public-key-format, mandatory */
2352 lyd_find_path(tree, "public-key-format", 0, &node);
2353 assert(node);
2354
2355 format = ((struct lyd_node_term *)node)->value.ident->name;
2356 if (!strcmp(format, "ssh-public-key-format")) {
roman45cec4e2023-02-17 10:21:39 +01002357 key->pubkey_type = NC_SSH_PUBKEY_SSH2;
romand57b3722023-04-05 11:26:25 +02002358 } else if (!strcmp(format, "subject-public-key-info-format")) {
2359 key->pubkey_type = NC_SSH_PUBKEY_X509;
roman45cec4e2023-02-17 10:21:39 +01002360 } else {
2361 ERR(NULL, "Public key format \"%s\" not supported.", format);
2362 ret = 1;
2363 goto cleanup;
2364 }
2365
2366 /* set public-key, mandatory */
2367 lyd_find_path(tree, "public-key", 0, &node);
2368 assert(node);
2369
2370 key->pub_base64 = strdup(lyd_get_value(node));
2371 if (!key->pub_base64) {
2372 ERRMEM;
2373 ret = 1;
2374 goto cleanup;
2375 }
2376
2377 /* set private-key-format */
2378 ret = lyd_find_path(tree, "private-key-format", 0, &node);
2379 if (!ret) {
2380 format = ((struct lyd_node_term *)node)->value.ident->name;
2381 if (!strcmp(format, "rsa-private-key-format")) {
2382 key->privkey_type = NC_SSH_KEY_RSA;
2383 } else if (!strcmp(format, "ec-private-key-format")) {
2384 key->privkey_type = NC_SSH_KEY_ECDSA;
2385 } else {
2386 ERR(NULL, "Private key format (%s) not supported.", format);
2387 ret = 1;
2388 goto cleanup;
2389 }
2390 }
2391
2392 /* set private key, mandatory */
2393 lyd_find_path(tree, "cleartext-private-key", 0, &node);
2394 assert(node);
2395
2396 key->priv_base64 = strdup(lyd_get_value(node));
2397 if (!key->priv_base64) {
2398 ERRMEM;
2399 ret = 1;
2400 goto cleanup;
2401 }
2402
2403 /* set certificates associated with the key pair */
2404 ret = lyd_find_path(tree, "certificates", 0, &node);
2405 if (!ret) {
2406 node = lyd_child(node);
2407 if (node) {
2408 /* certificate list instance */
2409 LY_LIST_FOR(node, iter) {
romane028ef92023-02-24 16:33:08 +01002410 if (nc_server_config_asymmetric_key_certificate(iter, key)) {
roman45cec4e2023-02-17 10:21:39 +01002411 ret = 1;
2412 goto cleanup;
2413 }
2414 }
2415 }
2416 } else if (ret == LY_ENOTFOUND) {
2417 /* certificates container not present, but it's ok */
2418 ret = 0;
2419 }
2420
2421cleanup:
2422 return ret;
2423}
2424
2425static int
romane028ef92023-02-24 16:33:08 +01002426nc_server_config_symmetric_key(const struct lyd_node *tree)
roman45cec4e2023-02-17 10:21:39 +01002427{
2428 int ret = 0;
2429 const char *format;
2430 struct lyd_node *node;
2431 struct nc_keystore *ks = &server_opts.keystore;
roman8edee342023-03-31 13:25:48 +02002432 struct nc_symmetric_key *key;
roman45cec4e2023-02-17 10:21:39 +01002433 void *tmp;
2434
2435 /* create new symmetric key */
2436 tmp = realloc(ks->sym_keys, (ks->sym_key_count + 1) * sizeof *ks->sym_keys);
romand57b3722023-04-05 11:26:25 +02002437 if (!tmp) {
roman45cec4e2023-02-17 10:21:39 +01002438 ERRMEM;
2439 ret = 1;
2440 goto cleanup;
2441 }
2442 memset(&ks->sym_keys[ks->sym_key_count], 0, sizeof *ks->sym_keys);
2443 ks->sym_keys = tmp;
2444 key = &ks->sym_keys[ks->sym_key_count];
2445 ks->sym_key_count++;
2446
2447 /* set name */
2448 lyd_find_path(tree, "name", 0, &node);
2449 assert(node);
2450
2451 key->name = strdup(lyd_get_value(node));
2452 if (!key->name) {
2453 ERRMEM;
2454 ret = 1;
2455 goto cleanup;
2456 }
2457
2458 /* check if the identity matches with the supported one */
2459 lyd_find_path(tree, "key-format", 0, &node);
2460 assert(node);
2461
2462 format = ((struct lyd_node_term *)node)->value.ident->name;
2463 if (strcmp(format, "symmetric-key-format")) {
2464 ret = 1;
2465 goto cleanup;
2466 }
2467
2468 /* set key data */
2469 lyd_find_path(tree, "cleartext-key", 0, &node);
2470 assert(node);
2471
2472 key->base64 = strdup(lyd_get_value(node));
2473 if (!key->base64) {
2474 ERRMEM;
2475 ret = 1;
2476 goto cleanup;
2477 }
2478
2479cleanup:
romanc1d2b092023-02-02 08:58:27 +01002480 return ret;
2481}
2482
2483static int
2484nc_fill_keystore(const struct lyd_node *data)
2485{
2486 int ret = 0;
2487 uint32_t prev_lo;
roman45cec4e2023-02-17 10:21:39 +01002488 struct lyd_node *tree, *as_keys, *s_keys, *iter;
romanc1d2b092023-02-02 08:58:27 +01002489
roman45cec4e2023-02-17 10:21:39 +01002490 /* silently search for nodes, some of them may not be present */
romanc1d2b092023-02-02 08:58:27 +01002491 prev_lo = ly_log_options(0);
roman45cec4e2023-02-17 10:21:39 +01002492
2493 ret = lyd_find_path(data, "/ietf-keystore:keystore", 0, &tree);
romanc1d2b092023-02-02 08:58:27 +01002494 if (ret) {
romand57b3722023-04-05 11:26:25 +02002495 VRB(NULL, "Keystore container not found in the YANG data.");
roman27215242023-03-10 14:55:00 +01002496 ret = 0;
roman45cec4e2023-02-17 10:21:39 +01002497 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002498 }
2499
roman45cec4e2023-02-17 10:21:39 +01002500 ret = lyd_find_path(tree, "asymmetric-keys", 0, &as_keys);
2501 if (!ret) {
2502 /* asymmetric keys container is present */
2503 as_keys = lyd_child(as_keys);
2504 if (as_keys && !strcmp(LYD_NAME(as_keys), "asymmetric-key")) {
2505 /* asymmetric key list */
2506 LY_LIST_FOR(as_keys, iter) {
romane028ef92023-02-24 16:33:08 +01002507 if (nc_server_config_asymmetric_key(iter)) {
roman45cec4e2023-02-17 10:21:39 +01002508 ret = 1;
2509 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002510 }
2511 }
romanc1d2b092023-02-02 08:58:27 +01002512 }
romanc1d2b092023-02-02 08:58:27 +01002513 }
2514
roman45cec4e2023-02-17 10:21:39 +01002515 ret = lyd_find_path(tree, "symmetric-keys", 0, &s_keys);
2516 if (!ret) {
2517 /* symmetric keys container is present */
2518 s_keys = lyd_child(s_keys);
2519 if (s_keys && !strcmp(LYD_NAME(s_keys), "symmetric-key")) {
2520 /* symmetric key list */
2521 LY_LIST_FOR(s_keys, iter) {
romane028ef92023-02-24 16:33:08 +01002522 if (nc_server_config_symmetric_key(iter)) {
roman45cec4e2023-02-17 10:21:39 +01002523 ret = 1;
2524 goto cleanup;
2525 }
2526 }
2527 }
2528 }
romanc1d2b092023-02-02 08:58:27 +01002529
roman45cec4e2023-02-17 10:21:39 +01002530cleanup:
2531 /* reset the logging options back to what they were */
2532 ly_log_options(prev_lo);
2533 return ret;
romanc1d2b092023-02-02 08:58:27 +01002534}
2535
romand57b3722023-04-05 11:26:25 +02002536static int
2537nc_server_config_create_truststore_certificate(const struct lyd_node *tree, struct nc_certificate_bag *bag)
2538{
2539 int ret = 0;
2540 struct lyd_node *node;
2541 void *tmp;
2542 struct nc_certificate *cert;
2543
2544 /* create new certificate */
2545 tmp = realloc(bag->certs, (bag->cert_count + 1) * sizeof *bag->certs);
2546 if (!tmp) {
2547 ERRMEM;
2548 ret = 1;
2549 goto cleanup;
2550 }
2551 memset(&bag->certs[bag->cert_count], 0, sizeof *bag->certs);
2552 bag->certs = tmp;
2553 cert = &bag->certs[bag->cert_count];
2554 bag->cert_count++;
2555
2556 /* set name */
2557 lyd_find_path(tree, "name", 0, &node);
2558 assert(node);
2559
2560 cert->name = strdup(lyd_get_value(node));
2561 if (!cert->name) {
2562 ERRMEM;
2563 ret = 1;
2564 goto cleanup;
2565 }
2566
2567 /* set cert data */
2568 lyd_find_path(tree, "cert-data", 0, &node);
2569 assert(node);
2570
2571 cert->cert_base64 = strdup(lyd_get_value(node));
2572 if (!cert->cert_base64) {
2573 ERRMEM;
2574 ret = 1;
2575 goto cleanup;
2576 }
2577
2578cleanup:
2579 return ret;
2580}
2581
2582static int
2583nc_server_config_certificate_bag(const struct lyd_node *tree)
2584{
2585 int ret = 0;
2586 struct lyd_node *node = NULL, *iter;
2587 void *tmp;
2588 struct nc_truststore *ts = &server_opts.truststore;
2589 struct nc_certificate_bag *bag;
2590
2591 /* create new certificate bag */
2592 tmp = realloc(ts->cert_bags, (ts->cert_bag_count + 1) * sizeof *ts->cert_bags);
2593 if (!tmp) {
2594 ERRMEM;
2595 ret = 1;
2596 goto cleanup;
2597 }
2598 ts->cert_bags = tmp;
2599 memset(&ts->cert_bags[ts->cert_bag_count], 0, sizeof *ts->cert_bags);
2600 bag = &ts->cert_bags[ts->cert_bag_count];
2601 ts->cert_bag_count++;
2602
2603 /* set name */
2604 lyd_find_path(tree, "name", 0, &node);
2605 assert(node);
2606
2607 bag->name = strdup(lyd_get_value(node));
2608 if (!bag->name) {
2609 ERRMEM;
2610 ret = 1;
2611 goto cleanup;
2612 }
2613
2614 /* set certificates associated with this bag */
2615 ret = lyd_find_path(tree, "certificate", 0, &node);
2616 if (!ret) {
2617 LY_LIST_FOR(node, iter) {
2618 if (nc_server_config_create_truststore_certificate(iter, bag)) {
2619 ret = 1;
2620 goto cleanup;
2621 }
2622 }
2623 } else if (ret == LY_ENOTFOUND) {
2624 /* certificate list not present, but it's ok */
2625 ret = 0;
2626 }
2627
2628cleanup:
2629 return ret;
2630}
2631
2632static int
2633nc_server_config_create_truststore_public_key(const struct lyd_node *tree, struct nc_public_key_bag *bag)
2634{
2635 int ret = 0;
2636 struct lyd_node *node;
2637 void *tmp;
2638 struct nc_public_key *key;
2639 const char *format;
2640
2641 /* create new public key */
2642 tmp = realloc(bag->pubkeys, (bag->pubkey_count + 1) * sizeof *bag->pubkeys);
2643 if (!tmp) {
2644 ERRMEM;
2645 ret = 1;
2646 goto cleanup;
2647 }
2648 bag->pubkeys = tmp;
2649 memset(&bag->pubkeys[bag->pubkey_count], 0, sizeof *bag->pubkeys);
2650 key = &bag->pubkeys[bag->pubkey_count];
2651 bag->pubkey_count++;
2652
2653 /* set name */
2654 lyd_find_path(tree, "name", 0, &node);
2655 assert(node);
2656
2657 key->name = strdup(lyd_get_value(node));
2658 if (!key->name) {
2659 ERRMEM;
2660 ret = 1;
2661 goto cleanup;
2662 }
2663
2664 /* set public-key-format, mandatory */
2665 lyd_find_path(tree, "public-key-format", 0, &node);
2666 assert(node);
2667
2668 format = ((struct lyd_node_term *)node)->value.ident->name;
2669 if (!strcmp(format, "ssh-public-key-format")) {
2670 key->pubkey_type = NC_SSH_PUBKEY_SSH2;
2671 } else if (!strcmp(format, "subject-public-key-info-format")) {
2672 key->pubkey_type = NC_SSH_PUBKEY_X509;
2673 } else {
2674 ERR(NULL, "Public key format \"%s\" not supported.", format);
2675 ret = 1;
2676 goto cleanup;
2677 }
2678
2679 /* set public key data */
2680 lyd_find_path(tree, "public-key", 0, &node);
2681 assert(node);
2682
2683 key->pub_base64 = strdup(lyd_get_value(node));
2684 if (!key->pub_base64) {
2685 ERRMEM;
2686 ret = 1;
2687 goto cleanup;
2688 }
2689
2690cleanup:
2691 return ret;
2692}
2693
2694static int
2695nc_server_config_public_key_bag(const struct lyd_node *tree)
2696{
2697 int ret = 0;
2698 struct lyd_node *node = NULL, *iter;
2699 void *tmp;
2700 struct nc_truststore *ts = &server_opts.truststore;
2701 struct nc_public_key_bag *bag;
2702 const struct lysc_node *schema;
2703
2704 /* create new public key bag */
2705 tmp = realloc(ts->pub_bags, (ts->pub_bag_count + 1) * sizeof *ts->pub_bags);
2706 if (!tmp) {
2707 ERRMEM;
2708 ret = 1;
2709 goto cleanup;
2710 }
2711 ts->pub_bags = tmp;
2712 memset(&ts->pub_bags[ts->pub_bag_count], 0, sizeof *ts->pub_bags);
2713 bag = &ts->pub_bags[ts->pub_bag_count];
2714 ts->pub_bag_count++;
2715
2716 /* set name */
2717 lyd_find_path(tree, "name", 0, &node);
2718 assert(node);
2719
2720 bag->name = strdup(lyd_get_value(node));
2721 if (!bag->name) {
2722 ERRMEM;
2723 ret = 1;
2724 goto cleanup;
2725 }
2726
2727 /* get the schema node of public key so we can iterate over it's list */
2728 schema = lys_find_path(NULL, tree->schema, "public-key", 0);
2729 LYD_LIST_FOR_INST(node, schema, iter) {
2730 /* set public keys associated with this bag */
2731 if (nc_server_config_create_truststore_public_key(iter, bag)) {
2732 ret = 1;
2733 goto cleanup;
2734 }
2735 }
2736
2737cleanup:
2738 return ret;
2739}
2740
2741static int
2742nc_fill_truststore(const struct lyd_node *data)
2743{
2744 int ret = 0;
2745 struct lyd_node *tree, *cert_bags, *pub_bags, *iter;
2746 uint32_t prev_lo;
2747
2748 /* silently search for nodes, some of them may not be present */
2749 prev_lo = ly_log_options(0);
2750
2751 ret = lyd_find_path(data, "/ietf-truststore:truststore", 0, &tree);
2752 if (ret) {
2753 VRB(NULL, "Truststore container not found in the YANG data.");
2754 ret = 0;
2755 goto cleanup;
2756 }
2757
2758 ret = lyd_find_path(tree, "certificate-bags", 0, &cert_bags);
2759 if (!ret) {
2760 /* certificate bags container is present */
2761 cert_bags = lyd_child(cert_bags);
2762 if (cert_bags && !strcmp(LYD_NAME(cert_bags), "certificate-bag")) {
2763 /* certificate bag list */
2764 LY_LIST_FOR(cert_bags, iter) {
2765 if (nc_server_config_certificate_bag(iter)) {
2766 ret = 1;
2767 goto cleanup;
2768 }
2769 }
2770 }
2771 }
2772
2773 ret = lyd_find_path(tree, "public-key-bags", 0, &pub_bags);
2774 if (!ret) {
2775 /* public key bags container is present */
2776 pub_bags = lyd_child(pub_bags);
2777 if (pub_bags && !strcmp(LYD_NAME(pub_bags), "public-key-bag")) {
2778 /* public key bag list */
2779 LY_LIST_FOR(pub_bags, iter) {
2780 if (nc_server_config_public_key_bag(iter)) {
2781 ret = 1;
2782 goto cleanup;
2783 }
2784 }
2785 }
2786 } else if (ret == LY_ENOTFOUND) {
2787 /* it's not mandatory so it's ok */
2788 ret = 0;
2789 }
2790
2791cleanup:
2792 /* reset the logging options back to what they were */
2793 ly_log_options(prev_lo);
2794 return ret;
2795}
2796
romanc1d2b092023-02-02 08:58:27 +01002797API int
2798nc_server_config_load_modules(struct ly_ctx **ctx)
2799{
2800 int i, new_ctx = 0;
2801
2802 if (!*ctx) {
2803 if (ly_ctx_new(NC_SERVER_SEARCH_DIR, 0, ctx)) {
2804 ERR(NULL, "Couldn't create new libyang context.\n");
2805 goto error;
2806 }
2807 new_ctx = 1;
2808 }
2809
2810 /* all features */
2811 const char *ietf_nectonf_server[] = {"ssh-listen", "tls-listen", "ssh-call-home", "tls-call-home", "central-netconf-server-supported", NULL};
2812 /* all features */
2813 const char *ietf_x509_cert_to_name[] = {NULL};
2814 /* no private-key-encryption and csr-generation */
2815 const char *ietf_crypto_types[] = {
2816 "one-symmetric-key-format", "one-asymmetric-key-format", "symmetrically-encrypted-value-format",
2817 "asymmetrically-encrypted-value-format", "cms-enveloped-data-format", "cms-encrypted-data-format",
2818 "p10-based-csrs", "certificate-expiration-notification", "hidden-keys", "password-encryption",
2819 "symmetric-key-encryption", NULL
2820 };
2821 /* all features */
2822 const char *ietf_tcp_common[] = {"keepalives-supported", NULL};
2823 /* no ssh-x509-certs */
2824 const char *ietf_ssh_common[] = {"transport-params", "public-key-generation", NULL};
2825 /* all features */
2826 const char *iana_ssh_encryption_algs[] = {NULL};
2827 /* all features */
2828 const char *iana_ssh_key_exchange_algs[] = {NULL};
2829 /* all features */
2830 const char *iana_ssh_mac_algs[] = {NULL};
2831 /* all features */
2832 const char *iana_ssh_public_key_algs[] = {NULL};
2833 /* all features */
2834 const char *ietf_keystore[] = {"central-keystore-supported", "local-definitions-supported", "asymmetric-keys", "symmetric-keys", NULL};
2835 /* no ssh-server-keepalives and local-user-auth-hostbased */
2836 const char *ietf_ssh_server[] = {"local-users-supported", "local-user-auth-publickey", "local-user-auth-password", "local-user-auth-none", NULL};
2837 /* all features */
2838 const char *ietf_truststore[] = {"central-truststore-supported", "local-definitions-supported", "certificates", "public-keys", NULL};
2839 /* all features */
2840 const char *ietf_tls_server[] = {
2841 "tls-server-keepalives", "server-ident-x509-cert", "server-ident-raw-public-key", "server-ident-tls12-psk",
2842 "server-ident-tls13-epsk", "client-auth-supported", "client-auth-x509-cert", "client-auth-raw-public-key",
2843 "client-auth-tls12-psk", "client-auth-tls13-epsk", NULL
2844 };
2845 /* all features */
2846 const char *libnetconf2_netconf_server[] = {NULL};
2847
2848 const char *module_names[] = {
2849 "ietf-netconf-server", "ietf-x509-cert-to-name", "ietf-crypto-types",
2850 "ietf-tcp-common", "ietf-ssh-common", "iana-ssh-encryption-algs",
2851 "iana-ssh-key-exchange-algs", "iana-ssh-mac-algs", "iana-ssh-public-key-algs",
2852 "ietf-keystore", "ietf-ssh-server", "ietf-truststore",
2853 "ietf-tls-server", "libnetconf2-netconf-server", NULL
2854 };
2855
2856 const char **module_features[] = {
2857 ietf_nectonf_server, ietf_x509_cert_to_name, ietf_crypto_types,
2858 ietf_tcp_common, ietf_ssh_common, iana_ssh_encryption_algs,
2859 iana_ssh_key_exchange_algs, iana_ssh_mac_algs, iana_ssh_public_key_algs,
2860 ietf_keystore, ietf_ssh_server, ietf_truststore,
2861 ietf_tls_server, libnetconf2_netconf_server, NULL
2862 };
2863
2864 for (i = 0; module_names[i] != NULL; i++) {
2865 if (!ly_ctx_load_module(*ctx, module_names[i], NULL, module_features[i])) {
2866 ERR(NULL, "Loading module \"%s\" failed.\n", module_names[i]);
2867 goto error;
2868 }
2869 }
2870
2871 return 0;
2872
2873error:
2874 if (new_ctx) {
2875 ly_ctx_destroy(*ctx);
2876 *ctx = NULL;
2877 }
2878 return 1;
2879}
2880
2881API int
2882nc_server_config_setup_path(const struct ly_ctx *ctx, const char *path)
2883{
2884 struct lyd_node *tree = NULL;
2885 int ret = 0;
2886
2887 if (!path) {
2888 ERRARG("Missing path parameter.");
2889 ret = 1;
2890 goto cleanup;
2891 }
2892
2893 ret = lyd_parse_data_path(ctx, path, LYD_XML, LYD_PARSE_NO_STATE | LYD_PARSE_STRICT, LYD_VALIDATE_NO_STATE, &tree);
2894 if (ret) {
2895 goto cleanup;
2896 }
2897
2898 ret = nc_server_config_setup(tree);
2899 if (ret) {
2900 goto cleanup;
2901 }
2902
2903cleanup:
2904 lyd_free_all(tree);
2905 return ret;
2906}
2907
2908API int
2909nc_server_config_setup(const struct lyd_node *data)
2910{
2911 int ret = 0;
2912 struct lyd_node *tree;
2913 struct lyd_meta *m;
roman27215242023-03-10 14:55:00 +01002914 NC_OPERATION op = NC_OP_NONE;
romanc1d2b092023-02-02 08:58:27 +01002915
2916 /* LOCK */
2917 pthread_rwlock_wrlock(&server_opts.config_lock);
2918
2919 ret = nc_fill_keystore(data);
2920 if (ret) {
2921 ERR(NULL, "Filling keystore failed.");
2922 goto cleanup;
2923 }
2924
romand57b3722023-04-05 11:26:25 +02002925 ret = nc_fill_truststore(data);
2926 if (ret) {
2927 ERR(NULL, "Filling truststore failed.");
2928 goto cleanup;
2929 }
2930
romanc1d2b092023-02-02 08:58:27 +01002931 ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &tree);
2932 if (ret) {
2933 ERR(NULL, "Unable to find the netconf-server container in the YANG data.");
2934 goto cleanup;
2935 }
2936
2937 LY_LIST_FOR(tree->meta, m) {
2938 if (!strcmp(m->name, "operation")) {
2939 if (!strcmp(lyd_get_meta_value(m), "create")) {
2940 op = NC_OP_CREATE;
2941 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
2942 op = NC_OP_DELETE;
2943 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
2944 op = NC_OP_REPLACE;
2945 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
2946 op = NC_OP_NONE;
2947 } else {
2948 ERR(NULL, "Unexpected operation (%s).", lyd_get_meta_value(m));
2949 ret = 1;
2950 goto cleanup;
2951 }
2952 }
2953 }
2954
2955 if (nc_session_server_parse_tree(tree, op)) {
2956 ret = 1;
2957 goto cleanup;
2958 }
2959
2960cleanup:
2961 /* UNLOCK */
2962 pthread_rwlock_unlock(&server_opts.config_lock);
2963 return ret;
2964}