blob: 2c41ee079aaa584a73f54937917f428244343a1c [file] [log] [blame]
romanc1d2b092023-02-02 08:58:27 +01001/**
2 * @file config_server.c
3 * @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 */
15#include <assert.h>
16#include <stdlib.h>
17#include <string.h>
18
19#include "compat.h"
20#include "config_server.h"
21#include "libnetconf.h"
22#include "session_server.h"
23#include "session_server_ch.h"
24
25/* All libssh supported host-key, key-exchange, encryption and mac algorithms as of version 0.10.90 */
26
27static const char *supported_hostkey_algs[] = {
28 "ssh-ed25519-cert-v01@openssh.com", "ecdsa-sha2-nistp521-cert-v01@openssh.com",
29 "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ecdsa-sha2-nistp256-cert-v01@openssh.com",
30 "rsa-sha2-512-cert-v01@openssh.com", "rsa-sha2-256-cert-v01@openssh.com",
31 "ssh-rsa-cert-v01@openssh.com", "ssh-dss-cert-v01@openssh.com",
32 "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256",
33 "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa", "ssh-dss", NULL
34};
35
36static const char *supported_kex_algs[] = {
37 "diffie-hellman-group-exchange-sha1", "curve25519-sha256", "curve25519-sha256@libssh.org",
38 "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group18-sha512",
39 "diffie-hellman-group16-sha512", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group14-sha256", NULL
40};
41
42static const char *supported_encryption_algs[] = {
43 "chacha20-poly1305@openssh.com", "aes256-gcm@openssh.com", "aes128-gcm@openssh.com",
44 "aes256-ctr", "aes192-ctr", "aes128-ctr", "aes256-cbc", "aes192-cbc", "aes128-cbc",
45 "blowfish-cbc", "3des-cbc", "none", NULL
46};
47
48static const char *supported_mac_algs[] = {
49 "hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "hmac-sha1-etm@openssh.com",
50 "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", NULL
51};
52
53extern struct nc_server_opts server_opts;
54
55/**
56 * @brief Get the pointer to an endpoint structure based on node's location in the YANG data.
57 *
58 * @param[in] node Node from which the endpoint containing this node is derived.
59 * @param[out] endpt Endpoint containing the node.
60 * @param[out] bind Bind corresponding to the endpoint. Optional.
61 * @return 0 on success, 1 on error.
62 */
63static int
64nc_server_get_endpt(const struct lyd_node *node, struct nc_endpt **endpt, struct nc_bind **bind)
65{
66 uint16_t i;
67 const char *endpt_name;
68
69 assert(node);
70
71 while (node) {
72 if (!strcmp(LYD_NAME(node), "endpoint")) {
73 break;
74 }
75 node = lyd_parent(node);
76 }
77
78 if (!node) {
79 ERR(NULL, "Node \"%s\" is not contained in an endpoint subtree.", LYD_NAME(node));
80 return 1;
81 }
82
83 node = lyd_child(node);
84 assert(!strcmp(LYD_NAME(node), "name"));
85 endpt_name = lyd_get_value(node);
86
87 for (i = 0; i < server_opts.endpt_count; i++) {
88 if (!strcmp(server_opts.endpts[i].name, endpt_name)) {
89 *endpt = &server_opts.endpts[i];
90 if (bind) {
91 *bind = &server_opts.binds[i];
92 }
93 return 0;
94 }
95 }
96
97 ERR(NULL, "Endpoint \"%s\" was not found.", endpt_name);
98 return 1;
99}
100
101/**
102 * @brief Get the pointer to a hostkey structure based on node's location in the YANG data.
103 *
104 * @param[in] node Node from which the hotkey containing this node is derived.
105 * @param[in] opts Server SSH opts storing the array of the hostkey structures.
106 * @param[out] hostkey Hostkey containing the node.
107 * @return 0 on success, 1 on error.
108 */
109static int
110nc_server_get_hostkey(const struct lyd_node *node, const struct nc_server_ssh_opts *opts, struct nc_hostkey **hostkey)
111{
112 uint16_t i;
113 const char *hostkey_name;
114
115 assert(node && opts);
116
117 while (node) {
118 if (!strcmp(LYD_NAME(node), "host-key")) {
119 break;
120 }
121 node = lyd_parent(node);
122 }
123
124 if (!node) {
125 ERR(NULL, "Node \"%s\" is not contained in a host-key subtree.", LYD_NAME(node));
126 return 1;
127 }
128
129 node = lyd_child(node);
130 assert(!strcmp(LYD_NAME(node), "name"));
131 hostkey_name = lyd_get_value(node);
132
133 for (i = 0; i < opts->hostkey_count; i++) {
134 if (!strcmp(opts->hostkeys[i].name, hostkey_name)) {
135 *hostkey = &opts->hostkeys[i];
136 return 0;
137 }
138 }
139
140 ERR(NULL, "Host-key \"%s\" was not found.", hostkey_name);
141 return 1;
142}
143
144/**
145 * @brief Get the pointer to a client authentication structure based on node's location in the YANG data.
146 *
147 * @param[in] node Node from which the client-authentication structure containing this node is derived.
148 * @param[in] opts Server SSH opts storing the array of the client authentication structures.
149 * @param[out] auth_client Client authentication structure containing the node.
150 * @return 0 on success, 1 on error.
151 */
152static int
153nc_server_get_auth_client(const struct lyd_node *node, const struct nc_server_ssh_opts *opts, struct nc_client_auth **auth_client)
154{
155 uint16_t i;
156 const char *authkey_name;
157
158 assert(node && opts);
159
160 while (node) {
161 if (!strcmp(LYD_NAME(node), "user")) {
162 break;
163 }
164 node = lyd_parent(node);
165 }
166
167 if (!node) {
168 ERR(NULL, "Node \"%s\" is not contained in a client-authentication subtree.", LYD_NAME(node));
169 return 1;
170 }
171
172 node = lyd_child(node);
173 assert(!strcmp(LYD_NAME(node), "name"));
174 authkey_name = lyd_get_value(node);
175
176 for (i = 0; i < opts->client_count; i++) {
177 if (!strcmp(opts->auth_clients[i].username, authkey_name)) {
178 *auth_client = &opts->auth_clients[i];
179 return 0;
180 }
181 }
182
183 ERR(NULL, "Authorized key \"%s\" was not found.", authkey_name);
184 return 1;
185}
186
187/**
188 * @brief Get the pointer to a client authentication public key structure based on node's location in the YANG data.
189 *
190 * @param[in] node Node from which the ca-public key structure containing this node is derived.
191 * @param[in] auth_client Client authentication structure storing the array of the public key structures.
192 * @param[out] pubkey Public key structure containing the node.
193 * @return 0 on success, 1 on error.
194 */
195static int
196nc_server_get_pubkey(const struct lyd_node *node, const struct nc_client_auth *auth_client, struct nc_client_auth_pubkey **pubkey)
197{
198 uint16_t i;
199 const char *pubkey_name;
200
201 assert(node && auth_client);
202
203 node = lyd_parent(node);
204 while (node) {
205 if (!strcmp(LYD_NAME(node), "public-key")) {
206 break;
207 }
208 node = lyd_parent(node);
209 }
210
211 if (!node) {
212 ERR(NULL, "Node \"%s\" is not contained in a public-key subtree.", LYD_NAME(node));
213 return 1;
214 }
215
216 node = lyd_child(node);
217 assert(!strcmp(LYD_NAME(node), "name"));
218 pubkey_name = lyd_get_value(node);
219
220 for (i = 0; i < auth_client->pubkey_count; i++) {
221 if (!strcmp(auth_client->pubkeys[i].name, pubkey_name)) {
222 *pubkey = &auth_client->pubkeys[i];
223 return 0;
224 }
225 }
226
227 ERR(NULL, "Public key \"%s\" was not found.", pubkey_name);
228 return 1;
229}
230
231/**
232 * @brief Compares the nth-parent name.
233 *
234 * @param[in] node Node of which nth-parent to compare.
235 * @param[in] parent_count Count of parents.
236 * @param[in] parent_name Expected name of the parent.
237 * @return 1 if the name matches, 0 otherwise.
238 */
239static int
240equal_parent_name(const struct lyd_node *node, uint16_t parent_count, const char *parent_name)
241{
242 uint16_t i;
243
244 assert(node && parent_count > 0 && parent_name);
245
246 node = lyd_parent(node);
247 for (i = 1; i < parent_count; i++) {
248 node = lyd_parent(node);
249 }
250
251 if (!strcmp(LYD_NAME(node), parent_name)) {
252 return 1;
253 }
254
255 return 0;
256}
257
258static void
259nc_server_del_auth_client_pam_name(struct nc_client_auth *auth_client)
260{
261 free(auth_client->pam_config_name);
262 auth_client->pam_config_name = NULL;
263}
264
265static void
266nc_server_del_auth_client_pam_dir(struct nc_client_auth *auth_client)
267{
268 free(auth_client->pam_config_dir);
269 auth_client->pam_config_dir = NULL;
270}
271
272static void
273nc_server_del_endpt_name(struct nc_endpt *endpt)
274{
275 free(endpt->name);
276 endpt->name = NULL;
277}
278
279static void
280nc_server_del_local_address(struct nc_bind *bind)
281{
282 free(bind->address);
283 bind->address = NULL;
284}
285
286static void
287nc_server_del_hostkey_name(struct nc_hostkey *hostkey)
288{
289 free(hostkey->name);
290 hostkey->name = NULL;
291}
292
293static void
294nc_server_del_public_key(struct nc_hostkey *hostkey)
295{
296 free(hostkey->pub_base64);
297 hostkey->pub_base64 = NULL;
298}
299
300static void
301nc_server_del_truststore_reference(struct nc_client_auth *client_auth)
302{
303 free(client_auth->ts_reference);
304 client_auth->ts_reference = NULL;
305}
306
307static void
308nc_server_del_private_key(struct nc_hostkey *hostkey)
309{
310 free(hostkey->priv_base64);
311 hostkey->priv_base64 = NULL;
312}
313
314static void
romanc1d2b092023-02-02 08:58:27 +0100315nc_server_del_auth_client_username(struct nc_client_auth *auth_client)
316{
317 free(auth_client->username);
318 auth_client->username = NULL;
319}
320
321static void
322nc_server_del_auth_client_pubkey_name(struct nc_client_auth_pubkey *pubkey)
323{
324 free(pubkey->name);
325 pubkey->name = NULL;
326}
327
328static void
329nc_server_del_auth_client_pubkey_pub_base64(struct nc_client_auth_pubkey *pubkey)
330{
331 free(pubkey->pub_base64);
332 pubkey->pub_base64 = NULL;
333}
334
335static void
336nc_server_del_auth_client_ts_reference(struct nc_client_auth *auth_client)
337{
338 free(auth_client->ts_reference);
339 auth_client->ts_reference = NULL;
340}
341
342static void
343nc_server_del_auth_client_password(struct nc_client_auth *auth_client)
344{
345 free(auth_client->password);
346 auth_client->password = NULL;
347}
348
349static void
350nc_server_del_hostkey_algs(struct nc_server_ssh_opts *opts)
351{
352 free(opts->hostkey_algs);
353 opts->hostkey_algs = NULL;
354}
355
356static void
357nc_server_del_kex_algs(struct nc_server_ssh_opts *opts)
358{
359 free(opts->kex_algs);
360 opts->kex_algs = NULL;
361}
362
363static void
364nc_server_del_encryption_algs(struct nc_server_ssh_opts *opts)
365{
366 free(opts->encryption_algs);
367 opts->encryption_algs = NULL;
368}
369
370static void
371nc_server_del_mac_algs(struct nc_server_ssh_opts *opts)
372{
373 free(opts->mac_algs);
374 opts->mac_algs = NULL;
375}
376
377static void
378nc_server_del_hostkey(struct nc_server_ssh_opts *opts, struct nc_hostkey *hostkey)
379{
380 assert(hostkey->ks_type == NC_STORE_LOCAL || hostkey->ks_type == NC_STORE_KEYSTORE);
381
382 if (hostkey->ks_type == NC_STORE_LOCAL) {
383 nc_server_del_public_key(hostkey);
384 nc_server_del_private_key(hostkey);
romanc1d2b092023-02-02 08:58:27 +0100385 }
386
387 nc_server_del_hostkey_name(hostkey);
388 opts->hostkey_count--;
389 if (!opts->hostkey_count) {
390 free(opts->hostkeys);
391 opts->hostkeys = NULL;
392 }
393}
394
395static void
396nc_server_del_auth_client_pubkey(struct nc_client_auth *auth_client, struct nc_client_auth_pubkey *pubkey)
397{
398 nc_server_del_auth_client_pubkey_name(pubkey);
399 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
400
401 auth_client->pubkey_count--;
402 if (!auth_client->pubkey_count) {
403 free(auth_client->pubkeys);
404 auth_client->pubkeys = NULL;
405 }
406}
407
408static void
409nc_server_del_auth_client(struct nc_server_ssh_opts *opts, struct nc_client_auth *auth_client)
410{
411 uint16_t i, pubkey_count;
412
413 if (auth_client->ks_type == NC_STORE_LOCAL) {
414 pubkey_count = auth_client->pubkey_count;
415 for (i = 0; i < pubkey_count; i++) {
416 nc_server_del_auth_client_pubkey(auth_client, &auth_client->pubkeys[i]);
417 }
418 } else if (auth_client->ks_type == NC_STORE_TRUSTSTORE) {
419 nc_server_del_auth_client_ts_reference(auth_client);
420 } else {
421 return;
422 }
423
424 nc_server_del_auth_client_password(auth_client);
425 nc_server_del_auth_client_pam_name(auth_client);
426 nc_server_del_auth_client_pam_dir(auth_client);
427 nc_server_del_auth_client_username(auth_client);
428
429 opts->client_count--;
430 if (!opts->client_count) {
431 free(opts->auth_clients);
432 opts->auth_clients = NULL;
433 }
434}
435
436static void
437nc_server_del_ssh(struct nc_bind *bind, struct nc_server_ssh_opts *opts)
438{
439 uint16_t i, hostkey_count, client_count;
440
441 nc_server_del_local_address(bind);
442 if (bind->sock > -1) {
443 close(bind->sock);
444 }
445
446 /* store in variable because it gets decremented in the function call */
447 hostkey_count = opts->hostkey_count;
448 for (i = 0; i < hostkey_count; i++) {
449 nc_server_del_hostkey(opts, &opts->hostkeys[i]);
450 }
451
452 client_count = opts->client_count;
453 for (i = 0; i < client_count; i++) {
454 nc_server_del_auth_client(opts, &opts->auth_clients[i]);
455 }
456
457 nc_server_del_hostkey_algs(opts);
458 nc_server_del_kex_algs(opts);
459 nc_server_del_encryption_algs(opts);
460 nc_server_del_mac_algs(opts);
461
462 free(opts);
463 opts = NULL;
464}
465
466void
467nc_server_del_endpt_ssh(struct nc_endpt *endpt, struct nc_bind *bind)
468{
469 nc_server_del_endpt_name(endpt);
470 nc_server_del_ssh(bind, endpt->opts.ssh);
471
472 server_opts.endpt_count--;
473 if (!server_opts.endpt_count) {
474 free(server_opts.endpts);
475 free(server_opts.binds);
476 server_opts.endpts = NULL;
477 server_opts.binds = NULL;
478 }
479}
480
roman45cec4e2023-02-17 10:21:39 +0100481void
482nc_server_config_del_keystore(void)
483{
484 int i, j;
485 struct nc_keystore *ks = &server_opts.keystore;
486
487 /* delete all asymmetric keys */
488 for (i = 0; i < ks->asym_key_count; i++) {
489 free(ks->asym_keys[i].name);
490 free(ks->asym_keys[i].pub_base64);
491 free(ks->asym_keys[i].priv_base64);
492
493 for (j = 0; j < ks->asym_keys[i].cert_count; j++) {
494 /* free associated certificates */
495 free(ks->asym_keys[i].certs[j].name);
496 free(ks->asym_keys[i].certs[j].cert_base64);
497 }
498 free(ks->asym_keys[i].certs);
499 ks->asym_keys[i].cert_count = 0;
500 }
501 free(ks->asym_keys);
502 ks->asym_key_count = 0;
503
504 /* delete all symmetric keys */
505 for (i = 0; i < ks->sym_key_count; i++) {
506 free(ks->sym_keys[i].name);
507 free(ks->sym_keys[i].base64);
508 }
509 free(ks->sym_keys);
510 ks->sym_key_count = 0;
511}
512
romanc1d2b092023-02-02 08:58:27 +0100513/* presence container */
514int
515nc_server_configure_listen(NC_OPERATION op)
516{
517 uint16_t i;
518
519 assert(op == NC_OP_CREATE || op == NC_OP_DELETE);
520
521 if (op == NC_OP_DELETE) {
522 for (i = 0; i < server_opts.endpt_count; i++) {
523 nc_server_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]);
524 }
525 }
526
527 return 0;
528}
529
530/* default leaf */
531static int
532nc_server_configure_idle_timeout(const struct lyd_node *node, NC_OPERATION op)
533{
534 assert(!strcmp(LYD_NAME(node), "idle-timeout"));
535
536 if (equal_parent_name(node, 1, "listen")) {
537 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
538 server_opts.idle_timeout = strtoul(lyd_get_value(node), NULL, 10);
539 } else {
540 /* default value */
541 server_opts.idle_timeout = 3600;
542 }
543 }
544
545 return 0;
546}
547
548static int
549nc_server_create_bind(void)
550{
551 int ret = 0;
552 void *tmp;
553
554 tmp = realloc(server_opts.binds, (server_opts.endpt_count + 1) * sizeof *server_opts.binds);
555 if (!tmp) {
556 ERRMEM;
557 ret = 1;
558 goto cleanup;
559 }
560 server_opts.binds = tmp;
561 memset(&server_opts.binds[server_opts.endpt_count], 0, sizeof *server_opts.binds);
562
563 server_opts.binds[server_opts.endpt_count].sock = -1;
564
565cleanup:
566 return ret;
567}
568
569static int
570nc_server_create_endpoint(const struct lyd_node *node)
571{
572 int ret = 0;
573 void *tmp;
574
575 tmp = realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts);
576 if (!tmp) {
577 ERRMEM;
578 ret = 1;
579 goto cleanup;
580 }
581 server_opts.endpts = tmp;
582 memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts);
583
584 node = lyd_child(node);
585 assert(!strcmp(LYD_NAME(node), "name"));
586
587 server_opts.endpts[server_opts.endpt_count].name = strdup(lyd_get_value(node));
588 if (!server_opts.endpts[server_opts.endpt_count].name) {
589 ERRMEM;
590 ret = 1;
591 goto cleanup;
592 }
593
594 if (nc_server_create_bind()) {
595 ret = 1;
596 goto cleanup;
597 }
598
599 server_opts.endpt_count++;
600
601cleanup:
602 return ret;
603}
604
605/* list */
606static int
607nc_server_configure_endpoint(const struct lyd_node *node, NC_OPERATION op)
608{
609 int ret = 0;
610 struct nc_endpt *endpt;
611 struct nc_bind *bind;
612
613 assert(!strcmp(LYD_NAME(node), "endpoint"));
614
615 if (op == NC_OP_CREATE) {
616 ret = nc_server_create_endpoint(node);
617 if (ret) {
618 goto cleanup;
619 }
620 } else if (op == NC_OP_DELETE) {
621 /* free all children */
622 if (nc_server_get_endpt(node, &endpt, &bind)) {
623 ret = 1;
624 goto cleanup;
625 }
626 nc_server_del_endpt_ssh(endpt, bind);
627 }
628
629cleanup:
630 return ret;
631}
632
633static int
634nc_server_create_ssh(struct nc_endpt *endpt)
635{
636 endpt->ti = NC_TI_LIBSSH;
637 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
638 if (!endpt->opts.ssh) {
639 ERRMEM;
640 return 1;
641 }
642
643 return 0;
644}
645
646/* NP container */
647static int
648nc_server_configure_ssh(const struct lyd_node *node, NC_OPERATION op)
649{
650 struct nc_endpt *endpt;
651 struct nc_bind *bind;
652 int ret = 0;
653
654 assert(!strcmp(LYD_NAME(node), "ssh"));
655
656 if (nc_server_get_endpt(node, &endpt, &bind)) {
657 ret = 1;
658 goto cleanup;
659 }
660
661 if (op == NC_OP_CREATE) {
662 ret = nc_server_create_ssh(endpt);
663 if (ret) {
664 goto cleanup;
665 }
666 } else if (op == NC_OP_DELETE) {
667 nc_server_del_ssh(bind, endpt->opts.ssh);
668 }
669
670cleanup:
671 return ret;
672}
673
674static int
675nc_server_config_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port)
676{
677 int sock = -1, set_addr, ret = 0;
678
679 assert((address && !port) || (!address && port));
680
681 if (address) {
682 set_addr = 1;
683 } else {
684 set_addr = 0;
685 }
686
687 if (set_addr) {
688 port = bind->port;
689 } else {
690 address = bind->address;
691 }
692
693 if (!set_addr && (endpt->ti == NC_TI_UNIX)) {
694 ret = 1;
695 goto cleanup;
696 }
697
698 /* we have all the information we need to create a listening socket */
699 if (address && port) {
700 /* create new socket, close the old one */
701 sock = nc_sock_listen_inet(address, port, &endpt->ka);
702 if (sock == -1) {
703 ret = 1;
704 goto cleanup;
705 }
706
707 if (bind->sock > -1) {
708 close(bind->sock);
709 }
710 bind->sock = sock;
711 }
712
713 if (sock > -1) {
714 switch (endpt->ti) {
715#ifdef NC_ENABLED_SSH
716 case NC_TI_LIBSSH:
717 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
718 break;
719#endif
720#ifdef NC_ENABLED_TLS
721 case NC_TI_OPENSSL:
722 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
723 break;
724#endif
725 default:
726 ERRINT;
727 ret = 1;
728 break;
729 }
730 }
731
732cleanup:
733 return ret;
734}
735
736/* mandatory leaf */
737static int
738nc_server_configure_local_address(const struct lyd_node *node, NC_OPERATION op)
739{
740 struct nc_endpt *endpt;
741 struct nc_bind *bind;
742 int ret = 0;
743
744 (void) op;
745
746 assert(!strcmp(LYD_NAME(node), "local-address"));
747
748 if (equal_parent_name(node, 4, "listen")) {
749 if (nc_server_get_endpt(node, &endpt, &bind)) {
750 ret = 1;
751 goto cleanup;
752 }
753
754 nc_server_del_local_address(bind);
755 bind->address = strdup(lyd_get_value(node));
756 if (!bind->address) {
757 ERRMEM;
758 ret = 1;
759 goto cleanup;
760 }
761
762 ret = nc_server_config_set_address_port(endpt, bind, lyd_get_value(node), 0);
763 if (ret) {
764 goto cleanup;
765 }
766 }
767
768cleanup:
769 return ret;
770}
771
772/* leaf with default value */
773static int
774nc_server_configure_local_port(const struct lyd_node *node, NC_OPERATION op)
775{
776 struct nc_endpt *endpt;
777 struct nc_bind *bind;
778 int ret = 0;
779
780 assert(!strcmp(LYD_NAME(node), "local-port"));
781
782 if (equal_parent_name(node, 4, "listen")) {
783 if (nc_server_get_endpt(node, &endpt, &bind)) {
784 ret = 1;
785 goto cleanup;
786 }
787
788 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
789 bind->port = strtoul(lyd_get_value(node), NULL, 10);
790 } else {
791 /* delete -> set to default */
792 bind->port = 0;
793 }
794
795 ret = nc_server_config_set_address_port(endpt, bind, NULL, bind->port);
796 if (ret) {
797 goto cleanup;
798 }
799 }
800
801cleanup:
802 return ret;
803}
804
805/* P container */
806static int
807nc_server_configure_keepalives(const struct lyd_node *node, NC_OPERATION op)
808{
809 struct nc_endpt *endpt;
810 struct nc_bind *bind;
811 int ret = 0;
812
813 assert(!strcmp(LYD_NAME(node), "keepalives"));
814
815 if (equal_parent_name(node, 4, "listen")) {
816 if (nc_server_get_endpt(node, &endpt, &bind)) {
817 ret = 1;
818 goto cleanup;
819 }
820
821 if (op == NC_OP_CREATE) {
822 endpt->ka.enabled = 1;
823 } else {
824 endpt->ka.enabled = 0;
825 }
826 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
827 if (ret) {
828 goto cleanup;
829 }
830 }
831
832cleanup:
833 return ret;
834}
835
836/* mandatory leaf */
837static int
838nc_server_configure_idle_time(const struct lyd_node *node, NC_OPERATION op)
839{
840 struct nc_endpt *endpt;
841 struct nc_bind *bind;
842 int ret = 0;
843
844 assert(!strcmp(LYD_NAME(node), "idle-time"));
845
846 if (equal_parent_name(node, 4, "listen")) {
847 if (nc_server_get_endpt(node, &endpt, &bind)) {
848 ret = 1;
849 goto cleanup;
850 }
851
852 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
853 endpt->ka.idle_time = strtoul(lyd_get_value(node), NULL, 10);
854 } else {
855 endpt->ka.idle_time = 0;
856 }
857 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
858 if (ret) {
859 goto cleanup;
860 }
861 }
862
863cleanup:
864 return ret;
865}
866
867/* mandatory leaf */
868static int
869nc_server_configure_max_probes(const struct lyd_node *node, NC_OPERATION op)
870{
871 struct nc_endpt *endpt;
872 struct nc_bind *bind;
873 int ret = 0;
874
875 assert(!strcmp(LYD_NAME(node), "max-probes"));
876
877 if (equal_parent_name(node, 4, "listen")) {
878 if (nc_server_get_endpt(node, &endpt, &bind)) {
879 ret = 1;
880 goto cleanup;
881 }
882
883 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
884 endpt->ka.max_probes = strtoul(lyd_get_value(node), NULL, 10);
885 } else {
886 endpt->ka.max_probes = 0;
887 }
888 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
889 if (ret) {
890 goto cleanup;
891 }
892 }
893
894cleanup:
895 return ret;
896}
897
898/* mandatory leaf */
899static int
900nc_server_configure_probe_interval(const struct lyd_node *node, NC_OPERATION op)
901{
902 struct nc_endpt *endpt;
903 struct nc_bind *bind;
904 int ret = 0;
905
906 assert(!strcmp(LYD_NAME(node), "probe-interval"));
907
908 if (equal_parent_name(node, 4, "listen")) {
909 if (nc_server_get_endpt(node, &endpt, &bind)) {
910 ret = 1;
911 goto cleanup;
912 }
913
914 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
915 endpt->ka.probe_interval = strtoul(lyd_get_value(node), NULL, 10);
916 } else {
917 endpt->ka.probe_interval = 0;
918 }
919 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
920 if (ret) {
921 goto cleanup;
922 }
923 }
924
925cleanup:
926 return ret;
927}
928
929static int
930nc_server_create_host_key(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
931{
932 int ret = 0;
933 void *tmp;
934
935 tmp = realloc(opts->hostkeys,
936 (opts->hostkey_count + 1) * sizeof *opts->hostkeys);
937 if (!tmp) {
938 ERRMEM;
939 ret = 1;
940 goto cleanup;
941 }
942 opts->hostkeys = tmp;
943
944 memset(&opts->hostkeys[opts->hostkey_count], 0, sizeof *opts->hostkeys);
945
946 opts->hostkeys[opts->hostkey_count].name = strdup(lyd_get_value(lyd_child(node)));
947 if (!opts->hostkeys[opts->hostkey_count].name) {
948 ERRMEM;
949 ret = 1;
950 goto cleanup;
951 }
952
953 /* set union selector */
954 lyd_find_path(node, "public-key", 0, (struct lyd_node **)&node);
955 assert(node);
956
957 if (!lyd_find_path(node, "local-definition", 0, NULL)) {
958 opts->hostkeys[opts->hostkey_count].ks_type = NC_STORE_LOCAL;
959 } else {
960 opts->hostkeys[opts->hostkey_count].ks_type = NC_STORE_KEYSTORE;
961 }
962
963 opts->hostkey_count++;
964
965cleanup:
966 return ret;
967}
968
969/* list */
970static int
971nc_server_configure_host_key(const struct lyd_node *node, NC_OPERATION op)
972{
973 struct nc_endpt *endpt;
974 struct nc_hostkey *hostkey;
975 int ret = 0;
976
977 assert(!strcmp(LYD_NAME(node), "host-key"));
978
979 if ((equal_parent_name(node, 1, "server-identity")) && (equal_parent_name(node, 5, "listen"))) {
980 if (nc_server_get_endpt(node, &endpt, NULL)) {
981 ret = 1;
982 goto cleanup;
983 }
984
985 if (op == NC_OP_CREATE) {
986 ret = nc_server_create_host_key(node, endpt->opts.ssh);
987 if (ret) {
988 goto cleanup;
989 }
990 } else if (op == NC_OP_DELETE) {
991 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
992 ret = 1;
993 goto cleanup;
994 }
995
996 nc_server_del_hostkey(endpt->opts.ssh, hostkey);
997 }
998 } else if (equal_parent_name(node, 1, "transport-params")) {
999 /* just a container with the name host-key, nothing to be done */
1000 goto cleanup;
1001 } else {
1002 ERRINT;
1003 ret = 1;
1004 goto cleanup;
1005 }
1006
1007cleanup:
1008 return ret;
1009}
1010
1011/* mandatory leaf */
1012int
1013nc_server_configure_public_key_format(const struct lyd_node *node, NC_OPERATION op)
1014{
1015 const char *format;
1016 struct nc_endpt *endpt;
1017 struct nc_client_auth *auth_client;
1018 struct nc_client_auth_pubkey *pubkey;
1019 struct nc_hostkey *hostkey;
1020 int ret = 0;
1021
1022 assert(!strcmp(LYD_NAME(node), "public-key-format"));
1023
1024 format = ((struct lyd_node_term *)node)->value.ident->name;
1025
1026 if ((equal_parent_name(node, 6, "client-authentication")) && (equal_parent_name(node, 10, "listen"))) {
1027 if (nc_server_get_endpt(node, &endpt, NULL)) {
1028 ret = 1;
1029 goto cleanup;
1030 }
1031
1032 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1033 ret = 1;
1034 goto cleanup;
1035 }
1036
1037 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1038 ret = 1;
1039 goto cleanup;
1040 }
1041
1042 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1043 if (!strcmp(format, "ssh-public-key-format")) {
1044 pubkey->pubkey_type = NC_SSH_PUBKEY_X509;
1045 } else if (!strcmp(format, "subject-public-key-info-format")) {
1046 pubkey->pubkey_type = NC_SSH_PUBKEY_SSH2;
1047 } else {
1048 ERR(NULL, "Public key format (%s) not supported.", format);
1049 }
1050 }
1051 } else if ((equal_parent_name(node, 5, "server-identity")) && (equal_parent_name(node, 11, "listen"))) {
1052 if (nc_server_get_endpt(node, &endpt, NULL)) {
1053 ret = 1;
1054 goto cleanup;
1055 }
1056
1057 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1058 ret = 1;
1059 goto cleanup;
1060 }
1061
1062 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1063 if (!strcmp(format, "ssh-public-key-format")) {
1064 hostkey->pubkey_type = NC_SSH_PUBKEY_X509;
1065 } else if (!strcmp(format, "subject-public-key-info-format")) {
1066 hostkey->pubkey_type = NC_SSH_PUBKEY_SSH2;
1067 } else {
1068 ERR(NULL, "Public key format (%s) not supported.", format);
1069 }
1070 }
1071 }
1072
1073cleanup:
1074 return ret;
1075}
1076
1077/* leaf */
1078int
1079nc_server_configure_private_key_format(const struct lyd_node *node, NC_OPERATION op)
1080{
1081 const char *format;
1082 struct nc_endpt *endpt;
1083 struct nc_hostkey *hostkey;
1084 int ret = 0;
1085
1086 assert(!strcmp(LYD_NAME(node), "private-key-format"));
1087
1088 if (nc_server_get_endpt(node, &endpt, NULL)) {
1089 ret = 1;
1090 goto cleanup;
1091 }
1092
1093 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1094 ret = 1;
1095 goto cleanup;
1096 }
1097
1098 format = ((struct lyd_node_term *)node)->value.ident->name;
1099 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1100 if (!strcmp(format, "rsa-private-key-format")) {
1101 hostkey->privkey_type = NC_SSH_KEY_RSA;
1102 } else if (!strcmp(format, "ec-private-key-format")) {
1103 hostkey->privkey_type = NC_SSH_KEY_ECDSA;
1104 } else {
1105 ERR(NULL, "Private key format (%s) not supported.", format);
1106 }
1107 }
1108
1109cleanup:
1110 return ret;
1111}
1112
1113static int
1114nc_server_replace_cleartext_private_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
1115{
1116 nc_server_del_private_key(hostkey);
1117 hostkey->priv_base64 = strdup(lyd_get_value(node));
1118 if (!hostkey->priv_base64) {
1119 ERRMEM;
1120 return 1;
1121 }
1122
1123 return 0;
1124}
1125
1126static int
1127nc_server_configure_cleartext_private_key(const struct lyd_node *node, NC_OPERATION op)
1128{
1129 struct nc_endpt *endpt;
1130 struct nc_hostkey *hostkey;
1131 int ret = 0;
1132
1133 assert(!strcmp(LYD_NAME(node), "cleartext-private-key"));
1134
1135 if ((equal_parent_name(node, 6, "ssh")) && (equal_parent_name(node, 8, "listen"))) {
1136 if (nc_server_get_endpt(node, &endpt, NULL)) {
1137 ret = 1;
1138 goto cleanup;
1139 }
1140 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1141 ret = 1;
1142 goto cleanup;
1143 }
1144
1145 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1146 ret = nc_server_replace_cleartext_private_key(node, hostkey);
1147 if (ret) {
1148 goto cleanup;
1149 }
1150 } else {
1151 nc_server_del_private_key(hostkey);
1152 }
1153 }
1154
1155cleanup:
1156 return ret;
1157}
1158
1159static int
1160nc_server_create_keystore_reference(const struct lyd_node *node, struct nc_hostkey *hostkey)
1161{
1162 uint16_t i;
roman45cec4e2023-02-17 10:21:39 +01001163 struct nc_keystore *ks = &server_opts.keystore;
romanc1d2b092023-02-02 08:58:27 +01001164
1165 /* lookup name */
roman45cec4e2023-02-17 10:21:39 +01001166 for (i = 0; i < ks->asym_key_count; i++) {
1167 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
romanc1d2b092023-02-02 08:58:27 +01001168 break;
1169 }
1170 }
1171
roman45cec4e2023-02-17 10:21:39 +01001172 if (i == ks->asym_key_count) {
1173 ERR(NULL, "Keystore \"%s\" not found.", lyd_get_value(node));
romanc1d2b092023-02-02 08:58:27 +01001174 return 1;
1175 }
1176
roman45cec4e2023-02-17 10:21:39 +01001177 hostkey->ks_ref = &ks->asym_keys[i];
romanc1d2b092023-02-02 08:58:27 +01001178
1179 return 0;
1180}
1181
1182/* leaf */
1183static int
1184nc_server_configure_keystore_reference(const struct lyd_node *node, NC_OPERATION op)
1185{
1186 struct nc_endpt *endpt;
1187 struct nc_hostkey *hostkey;
1188 int ret = 0;
1189
1190 assert(!strcmp(LYD_NAME(node), "keystore-reference"));
1191
roman45cec4e2023-02-17 10:21:39 +01001192 if ((equal_parent_name(node, 3, "server-identity")) && (equal_parent_name(node, 7, "listen"))) {
romanc1d2b092023-02-02 08:58:27 +01001193 if (nc_server_get_endpt(node, &endpt, NULL)) {
1194 ret = 1;
1195 goto cleanup;
1196 }
1197 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1198 ret = 1;
1199 goto cleanup;
1200 }
1201
1202 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1203 ret = nc_server_create_keystore_reference(node, hostkey);
1204 if (ret) {
1205 goto cleanup;
1206 }
1207 } else {
roman45cec4e2023-02-17 10:21:39 +01001208 hostkey->ks_ref = NULL;
romanc1d2b092023-02-02 08:58:27 +01001209 }
1210 }
1211
1212cleanup:
1213 return ret;
1214}
1215
1216static int
1217nc_server_create_auth_key_public_key_list(const struct lyd_node *node, struct nc_client_auth *auth_client)
1218{
1219 int ret = 0;
1220 void *tmp;
1221
1222 assert(!strcmp(LYD_NAME(node), "public-key"));
1223
1224 tmp = realloc(auth_client->pubkeys, (auth_client->pubkey_count + 1) * sizeof *auth_client->pubkeys);
1225 if (!tmp) {
1226 ERRMEM;
1227 ret = 1;
1228 goto cleanup;
1229 }
1230 auth_client->pubkeys = tmp;
1231
1232 memset(&auth_client->pubkeys[auth_client->pubkey_count], 0, sizeof *auth_client->pubkeys);
1233
1234 node = lyd_child(node);
1235 assert(!strcmp(LYD_NAME(node), "name"));
1236
1237 auth_client->pubkeys[auth_client->pubkey_count].name = strdup(lyd_get_value(node));
1238 if (!auth_client->pubkeys[auth_client->pubkey_count].name) {
1239 ERRMEM;
1240 ret = 1;
1241 goto cleanup;
1242 }
1243
1244 ++auth_client->pubkey_count;
1245
1246cleanup:
1247 return ret;
1248}
1249
1250static int
1251nc_server_replace_auth_key_public_key_leaf(const struct lyd_node *node, struct nc_client_auth_pubkey *pubkey)
1252{
1253 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
1254
1255 pubkey->pub_base64 = strdup(lyd_get_value(node));
1256 if (!pubkey->pub_base64) {
1257 ERRMEM;
1258 return 1;
1259 }
1260
1261 return 0;
1262}
1263
1264static int
1265nc_server_replace_host_key_public_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
1266{
1267 nc_server_del_public_key(hostkey);
1268
1269 hostkey->pub_base64 = strdup(lyd_get_value(node));
1270 if (!hostkey->pub_base64) {
1271 ERRMEM;
1272 return 1;
1273 }
1274
1275 return 0;
1276}
1277
1278static int
1279nc_server_configure_public_key(const struct lyd_node *node, NC_OPERATION op)
1280{
1281 struct nc_endpt *endpt;
1282 struct nc_hostkey *hostkey;
1283 struct nc_client_auth *auth_client;
1284 struct nc_client_auth_pubkey *pubkey;
1285 int ret = 0;
1286
1287 assert(!strcmp(LYD_NAME(node), "public-key"));
1288
1289 if ((equal_parent_name(node, 3, "host-key")) && (equal_parent_name(node, 8, "listen"))) {
1290 /* server's public-key, mandatory leaf */
1291 if (nc_server_get_endpt(node, &endpt, NULL)) {
1292 ret = 1;
1293 goto cleanup;
1294 }
1295
1296 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1297 ret = 1;
1298 goto cleanup;
1299 }
1300
1301 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1302 ret = nc_server_replace_host_key_public_key(node, hostkey);
1303 if (ret) {
1304 goto cleanup;
1305 }
1306 }
1307 } else if ((equal_parent_name(node, 5, "client-authentication")) && (equal_parent_name(node, 9, "listen"))) {
1308 /* client auth pubkeys, list */
1309 if (nc_server_get_endpt(node, &endpt, NULL)) {
1310 ret = 1;
1311 goto cleanup;
1312 }
1313
1314 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1315 ret = 1;
1316 goto cleanup;
1317 }
1318
1319 if (op == NC_OP_CREATE) {
1320 ret = nc_server_create_auth_key_public_key_list(node, auth_client);
1321 if (ret) {
1322 goto cleanup;
1323 }
1324 } else if (op == NC_OP_DELETE) {
1325 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1326 ret = 1;
1327 goto cleanup;
1328 }
1329
1330 nc_server_del_auth_client_pubkey(auth_client, pubkey);
1331 }
1332 } else if ((equal_parent_name(node, 6, "client-authentication")) && (equal_parent_name(node, 10, "listen"))) {
1333 /* client auth pubkey, leaf */
1334 if (nc_server_get_endpt(node, &endpt, NULL)) {
1335 ret = 1;
1336 goto cleanup;
1337 }
1338
1339 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1340 ret = 1;
1341 goto cleanup;
1342 }
1343
1344 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1345 ret = 1;
1346 goto cleanup;
1347 }
1348
1349 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1350 ret = nc_server_replace_auth_key_public_key_leaf(node, pubkey);
1351 if (ret) {
1352 goto cleanup;
1353 }
1354 } else {
1355 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
1356 }
1357 }
1358
1359cleanup:
1360 return ret;
1361}
1362
1363static int
1364nc_server_create_user(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
1365{
1366 int ret = 0;
1367 void *tmp;
1368
1369 tmp = realloc(opts->auth_clients, (opts->client_count + 1) * sizeof *opts->auth_clients);
1370 if (!tmp) {
1371 ERRMEM;
1372 ret = 1;
1373 goto cleanup;
1374 }
1375 opts->auth_clients = tmp;
1376
1377 memset(&opts->auth_clients[opts->client_count], 0, sizeof *opts->auth_clients);
1378
1379 opts->auth_clients[opts->client_count].username = strdup(lyd_get_value(lyd_child(node)));
1380 if (!opts->auth_clients[opts->client_count].username) {
1381 ERRMEM;
1382 ret = 1;
1383 goto cleanup;
1384 }
1385
1386 lyd_find_path(node, "public-keys", 0, (struct lyd_node **)&node);
1387
1388 if (node) {
1389 /* set union selector */
1390 if (!lyd_find_path(node, "local-definition", 0, NULL)) {
1391 opts->auth_clients[opts->client_count].ks_type = NC_STORE_LOCAL;
1392 } else {
1393 opts->auth_clients[opts->client_count].ks_type = NC_STORE_TRUSTSTORE;
1394 }
1395 }
1396
1397 ++opts->client_count;
1398
1399cleanup:
1400 return ret;
1401}
1402
1403/* list */
1404static int
1405nc_server_configure_user(const struct lyd_node *node, NC_OPERATION op)
1406{
1407 struct nc_endpt *endpt;
1408 struct nc_client_auth *auth_client;
1409 int ret = 0;
1410
1411 assert(!strcmp(LYD_NAME(node), "user"));
1412
1413 if (equal_parent_name(node, 6, "listen")) {
1414 if (nc_server_get_endpt(node, &endpt, NULL)) {
1415 ret = 1;
1416 goto cleanup;
1417 }
1418
1419 if (op == NC_OP_CREATE) {
1420 ret = nc_server_create_user(node, endpt->opts.ssh);
1421 if (ret) {
1422 goto cleanup;
1423 }
1424 } else if (op == NC_OP_DELETE) {
1425 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1426 ret = 1;
1427 goto cleanup;
1428 }
1429
1430 nc_server_del_auth_client(endpt->opts.ssh, auth_client);
1431 }
1432 }
1433
1434cleanup:
1435 return ret;
1436}
1437
1438static int
1439nc_server_configure_auth_attempts(const struct lyd_node *node, NC_OPERATION op)
1440{
1441 struct nc_endpt *endpt;
1442 int ret = 0;
1443
1444 assert(!strcmp(LYD_NAME(node), "auth-attempts"));
1445
1446 if (equal_parent_name(node, 5, "listen")) {
1447 if (nc_server_get_endpt(node, &endpt, NULL)) {
1448 ret = 1;
1449 goto cleanup;
1450 }
1451
1452 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1453 endpt->opts.ssh->auth_attempts = strtoul(lyd_get_value(node), NULL, 10);
1454 }
1455 }
1456
1457cleanup:
1458 return ret;
1459}
1460
1461static int
1462nc_server_configure_auth_timeout(const struct lyd_node *node, NC_OPERATION op)
1463{
1464 struct nc_endpt *endpt;
1465 int ret = 0;
1466
1467 assert(!strcmp(LYD_NAME(node), "auth-timeout"));
1468
1469 if (equal_parent_name(node, 5, "listen")) {
1470 if (nc_server_get_endpt(node, &endpt, NULL)) {
1471 ret = 1;
1472 goto cleanup;
1473 }
1474
1475 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1476 endpt->opts.ssh->auth_timeout = strtoul(lyd_get_value(node), NULL, 10);
1477 }
1478 }
1479
1480cleanup:
1481 return ret;
1482}
1483
1484static int
1485nc_server_replace_truststore_reference(const struct lyd_node *node, struct nc_client_auth *client_auth)
1486{
1487 /*todo*/
1488 nc_server_del_truststore_reference(client_auth);
1489
1490 client_auth->ts_reference = strdup(lyd_get_value(node));
1491 if (!client_auth->ts_reference) {
1492 ERRMEM;
1493 return 1;
1494 }
1495
1496 return 0;
1497}
1498
1499/* leaf */
1500static int
1501nc_server_configure_truststore_reference(const struct lyd_node *node, NC_OPERATION op)
1502{
1503 struct nc_endpt *endpt;
1504 struct nc_client_auth *auth_client;
1505 int ret = 0;
1506
1507 assert(!strcmp(LYD_NAME(node), "truststore-reference"));
1508
1509 if ((equal_parent_name(node, 1, "public-keys")) && (equal_parent_name(node, 8, "listen"))) {
1510 if (nc_server_get_endpt(node, &endpt, NULL)) {
1511 ret = 1;
1512 goto cleanup;
1513 }
1514
1515 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1516 ret = 1;
1517 goto cleanup;
1518 }
1519
1520 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1521 ret = nc_server_replace_truststore_reference(node, auth_client);
1522 if (ret) {
1523 goto cleanup;
1524 }
1525 } else {
1526 nc_server_del_truststore_reference(auth_client);
1527 }
1528 }
1529
1530cleanup:
1531 return ret;
1532}
1533
1534static int
1535nc_server_replace_password(const struct lyd_node *node, struct nc_client_auth *auth_client)
1536{
1537 nc_server_del_auth_client_password(auth_client);
1538
1539 auth_client->password = strdup(lyd_get_value(node));
1540 if (!auth_client->password) {
1541 ERRMEM;
1542 return 1;
1543 }
1544
1545 return 0;
1546}
1547
1548/* leaf */
1549static int
1550nc_server_configure_password(const struct lyd_node *node, NC_OPERATION op)
1551{
1552 struct nc_endpt *endpt;
1553 struct nc_client_auth *auth_client;
1554 int ret = 0;
1555
1556 assert(!strcmp(LYD_NAME(node), "password"));
1557
1558 if (equal_parent_name(node, 7, "listen")) {
1559 if (nc_server_get_endpt(node, &endpt, NULL)) {
1560 ret = 1;
1561 goto cleanup;
1562 }
1563
1564 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1565 ret = 1;
1566 goto cleanup;
1567 }
1568
1569 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1570 ret = nc_server_replace_password(node, auth_client);
1571 if (ret) {
1572 goto cleanup;
1573 }
1574 } else {
1575 nc_server_del_auth_client_password(auth_client);
1576 }
1577 }
1578
1579cleanup:
1580 return ret;
1581}
1582
1583static int
1584nc_server_configure_pam_name(const struct lyd_node *node, NC_OPERATION op)
1585{
1586 struct nc_endpt *endpt;
1587 struct nc_client_auth *auth_client;
1588 int ret = 0;
1589
1590 assert(!strcmp(LYD_NAME(node), "pam-config-file-name"));
1591
1592 if (equal_parent_name(node, 8, "listen")) {
1593 if (nc_server_get_endpt(node, &endpt, NULL)) {
1594 ret = 1;
1595 goto cleanup;
1596 }
1597
1598 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1599 ret = 1;
1600 goto cleanup;
1601 }
1602
1603 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1604 nc_server_del_auth_client_pam_name(auth_client);
1605
1606 auth_client->pam_config_name = strdup(lyd_get_value(node));
1607 if (!auth_client->pam_config_name) {
1608 ERRMEM;
1609 ret = 1;
1610 goto cleanup;
1611 }
1612 }
1613 }
1614
1615cleanup:
1616 return ret;
1617}
1618
1619static int
1620nc_server_configure_pam_dir(const struct lyd_node *node, NC_OPERATION op)
1621{
1622 struct nc_endpt *endpt;
1623 struct nc_client_auth *auth_client;
1624 int ret = 0;
1625
1626 assert(!strcmp(LYD_NAME(node), "pam-config-file-dir"));
1627
1628 if (equal_parent_name(node, 8, "listen")) {
1629 if (nc_server_get_endpt(node, &endpt, NULL)) {
1630 ret = 1;
1631 goto cleanup;
1632 }
1633
1634 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1635 ret = 1;
1636 goto cleanup;
1637 }
1638
1639 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1640 nc_server_del_auth_client_pam_dir(auth_client);
1641 auth_client->pam_config_dir = strdup(lyd_get_value(node));
1642 if (!auth_client->pam_config_dir) {
1643 ERRMEM;
1644 ret = 1;
1645 goto cleanup;
1646 }
1647 }
1648 }
1649
1650cleanup:
1651 return ret;
1652}
1653
1654/* leaf */
1655static int
1656nc_server_configure_none(const struct lyd_node *node, NC_OPERATION op)
1657{
1658 struct nc_endpt *endpt;
1659 struct nc_client_auth *auth_client;
1660 int ret = 0;
1661
1662 assert(!strcmp(LYD_NAME(node), "none"));
1663
1664 if (equal_parent_name(node, 7, "listen")) {
1665 if (nc_server_get_endpt(node, &endpt, NULL)) {
1666 ret = 1;
1667 goto cleanup;
1668 }
1669
1670 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1671 ret = 1;
1672 goto cleanup;
1673 }
1674
1675 if (op == NC_OP_CREATE) {
1676 auth_client->supports_none = 1;
1677 } else {
1678 auth_client->supports_none = 0;
1679 }
1680 }
1681
1682cleanup:
1683 return ret;
1684}
1685
1686static int
1687nc_server_configure_transport_params(const char *alg, char **alg_store, NC_OPERATION op)
1688{
1689 int ret = 0, alg_found = 0;
1690 char *substr, *haystack;
1691 size_t alg_len = strlen(alg);
1692
1693 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1694 if (!*alg_store) {
1695 /* first call */
1696 *alg_store = strdup(alg);
1697 if (!*alg_store) {
1698 ERRMEM;
1699 ret = 1;
1700 goto cleanup;
1701 }
1702 } else {
1703 /* +1 because of ',' between algorithms */
1704 *alg_store = nc_realloc(*alg_store, strlen(*alg_store) + alg_len + 1 + 1);
1705 if (!*alg_store) {
1706 ERRMEM;
1707 ret = 1;
1708 goto cleanup;
1709 }
1710 sprintf(*alg_store, "%s,%s", *alg_store, alg);
1711 }
1712 } else {
1713 /* delete */
1714 haystack = *alg_store;
1715 while ((substr = strstr(haystack, alg))) {
1716 /* iterate over all the substrings */
1717 if (((substr == haystack) && (*(substr + alg_len) == ',')) ||
1718 ((substr != haystack) && (*(substr - 1) == ',') && (*(substr + alg_len) == ','))) {
1719 /* either the first element of the string or somewhere in the middle */
1720 memmove(substr, substr + alg_len + 1, strlen(substr + alg_len + 1));
1721 alg_found = 1;
1722 break;
1723 } else if ((*(substr - 1) == ',') && (*(substr + alg_len) == '\0')) {
1724 /* the last element of the string */
1725 *(substr - 1) = '\0';
1726 alg_found = 1;
1727 break;
1728 }
1729 haystack++;
1730 }
1731 if (!alg_found) {
1732 ERR(NULL, "Unable to delete an algorithm (%s), which was not previously added.", alg);
1733 ret = 1;
1734 }
1735 }
1736
1737cleanup:
1738 return ret;
1739}
1740
1741/* leaf-list */
1742static int
1743nc_server_configure_host_key_alg(const struct lyd_node *node, NC_OPERATION op)
1744{
1745 struct nc_endpt *endpt;
1746 int ret = 0, listen = 0;
1747 const char *alg;
1748 uint8_t i;
1749
1750 /* get the algorithm name and compare it with algs supported by libssh */
1751 alg = ((struct lyd_node_term *)node)->value.ident->name;
1752
1753 if (equal_parent_name(node, 6, "listen")) {
1754 listen = 1;
1755 if (nc_server_get_endpt(node, &endpt, NULL)) {
1756 ret = 1;
1757 goto cleanup;
1758 }
1759 }
1760
1761 i = 0;
1762 while (supported_hostkey_algs[i]) {
1763 if (!strcmp(supported_hostkey_algs[i], alg)) {
1764 if (listen) {
1765 if (nc_server_configure_transport_params(alg, &endpt->opts.ssh->hostkey_algs, op)) {
1766 ret = 1;
1767 goto cleanup;
1768 }
1769 }
1770 break;
1771 }
1772 i++;
1773 }
1774 if (!supported_hostkey_algs[i]) {
1775 /* algorithm not supported */
1776 ERR(NULL, "Public key algorithm (%s) not supported by libssh.", alg);
1777 ret = 1;
1778 }
1779
1780cleanup:
1781 return ret;
1782}
1783
1784/* leaf-list */
1785static int
1786nc_server_configure_kex_alg(const struct lyd_node *node, NC_OPERATION op)
1787{
1788 struct nc_endpt *endpt;
1789 int ret = 0, listen = 0;
1790 const char *alg;
1791 uint8_t i;
1792
1793 /* get the algorithm name and compare it with algs supported by libssh */
1794 alg = ((struct lyd_node_term *)node)->value.ident->name;
1795
1796 if (equal_parent_name(node, 6, "listen")) {
1797 listen = 1;
1798 if (nc_server_get_endpt(node, &endpt, NULL)) {
1799 ret = 1;
1800 goto cleanup;
1801 }
1802 }
1803
1804 i = 0;
1805 while (supported_kex_algs[i]) {
1806 if (!strcmp(supported_kex_algs[i], alg)) {
1807 if (listen) {
1808 if (nc_server_configure_transport_params(alg, &endpt->opts.ssh->kex_algs, op)) {
1809 ret = 1;
1810 goto cleanup;
1811 }
1812 }
1813 break;
1814 }
1815 i++;
1816 }
1817 if (!supported_kex_algs[i]) {
1818 /* algorithm not supported */
1819 ERR(NULL, "Key exchange algorithm (%s) not supported by libssh.", alg);
1820 ret = 1;
1821 }
1822
1823cleanup:
1824 return ret;
1825}
1826
1827/* leaf-list */
1828static int
1829nc_server_configure_encryption_alg(const struct lyd_node *node, NC_OPERATION op)
1830{
1831 struct nc_endpt *endpt;
1832 int ret = 0, listen = 0;
1833 const char *alg;
1834 uint8_t i;
1835
1836 /* get the algorithm name and compare it with algs supported by libssh */
1837 alg = ((struct lyd_node_term *)node)->value.ident->name;
1838
1839 if (equal_parent_name(node, 6, "listen")) {
1840 listen = 1;
1841 if (nc_server_get_endpt(node, &endpt, NULL)) {
1842 ret = 1;
1843 goto cleanup;
1844 }
1845 }
1846
1847 i = 0;
1848 while (supported_encryption_algs[i]) {
1849 if (!strcmp(supported_encryption_algs[i], alg)) {
1850 if (listen) {
1851 if (nc_server_configure_transport_params(alg, &endpt->opts.ssh->encryption_algs, op)) {
1852 ret = 1;
1853 goto cleanup;
1854 }
1855 }
1856 break;
1857 }
1858 i++;
1859 }
1860 if (!supported_encryption_algs[i]) {
1861 /* algorithm not supported */
1862 ERR(NULL, "Encryption algorithm (%s) not supported by libssh.", alg);
1863 ret = 1;
1864 }
1865
1866cleanup:
1867 return ret;
1868}
1869
1870/* leaf-list */
1871static int
1872nc_server_configure_mac_alg(const struct lyd_node *node, NC_OPERATION op)
1873{
1874 struct nc_endpt *endpt;
1875 int ret = 0, listen = 0;
1876 const char *alg;
1877 uint8_t i;
1878
1879 /* get the algorithm name and compare it with algs supported by libssh */
1880 alg = ((struct lyd_node_term *)node)->value.ident->name;
1881
1882 if (equal_parent_name(node, 6, "listen")) {
1883 listen = 1;
1884 if (nc_server_get_endpt(node, &endpt, NULL)) {
1885 ret = 1;
1886 goto cleanup;
1887 }
1888 }
1889
1890 i = 0;
1891 while (supported_mac_algs[i]) {
1892 if (!strcmp(supported_mac_algs[i], alg)) {
1893 if (listen) {
1894 if (nc_server_configure_transport_params(alg, &endpt->opts.ssh->mac_algs, op)) {
1895 ret = 1;
1896 goto cleanup;
1897 }
1898 }
1899 break;
1900 }
1901 i++;
1902 }
1903 if (!supported_mac_algs[i]) {
1904 /* algorithm not supported */
1905 ERR(NULL, "MAC algorithm (%s) not supported by libssh.", alg);
1906 ret = 1;
1907 }
1908
1909cleanup:
1910 return ret;
1911}
1912
1913static int
1914nc_server_configure(const struct lyd_node *node, NC_OPERATION op)
1915{
1916 const char *name = LYD_NAME(node);
1917
1918 if (!strcmp(name, "listen")) {
1919 if (nc_server_configure_listen(op)) {
1920 goto error;
1921 }
1922 } else if (!strcmp(name, "idle-timeout")) {
1923 if (nc_server_configure_idle_timeout(node, op)) {
1924 goto error;
1925 }
1926 } else if (!strcmp(name, "endpoint")) {
1927 if (nc_server_configure_endpoint(node, op)) {
1928 goto error;
1929 }
1930 } else if (!strcmp(name, "ssh")) {
1931 if (nc_server_configure_ssh(node, op)) {
1932 goto error;
1933 }
1934 } else if (!strcmp(name, "local-address")) {
1935 if (nc_server_configure_local_address(node, op)) {
1936 goto error;
1937 }
1938 } else if (!strcmp(name, "local-port")) {
1939 if (nc_server_configure_local_port(node, op)) {
1940 goto error;
1941 }
1942 } else if (!strcmp(name, "keepalives")) {
1943 if (nc_server_configure_keepalives(node, op)) {
1944 goto error;
1945 }
1946 } else if (!strcmp(name, "idle-time")) {
1947 if (nc_server_configure_idle_time(node, op)) {
1948 goto error;
1949 }
1950 } else if (!strcmp(name, "max-probes")) {
1951 if (nc_server_configure_max_probes(node, op)) {
1952 goto error;
1953 }
1954 } else if (!strcmp(name, "probe-interval")) {
1955 if (nc_server_configure_probe_interval(node, op)) {
1956 goto error;
1957 }
1958 } else if (!strcmp(name, "host-key")) {
1959 if (nc_server_configure_host_key(node, op)) {
1960 goto error;
1961 }
1962 } else if (!strcmp(name, "public-key-format")) {
1963 if (nc_server_configure_public_key_format(node, op)) {
1964 goto error;
1965 }
1966 } else if (!strcmp(name, "public-key")) {
1967 if (nc_server_configure_public_key(node, op)) {
1968 goto error;
1969 }
1970 } else if (!strcmp(name, "private-key-format")) {
1971 if (nc_server_configure_private_key_format(node, op)) {
1972 goto error;
1973 }
1974 } else if (!strcmp(name, "cleartext-private-key")) {
1975 if (nc_server_configure_cleartext_private_key(node, op)) {
1976 goto error;
1977 }
1978 } else if (!strcmp(name, "keystore-reference")) {
1979 if (nc_server_configure_keystore_reference(node, op)) {
1980 goto error;
1981 }
1982 } else if (!strcmp(name, "user")) {
1983 if (nc_server_configure_user(node, op)) {
1984 goto error;
1985 }
1986 } else if (!strcmp(name, "auth-attempts")) {
1987 if (nc_server_configure_auth_attempts(node, op)) {
1988 goto error;
1989 }
1990 } else if (!strcmp(name, "auth-timeout")) {
1991 if (nc_server_configure_auth_timeout(node, op)) {
1992 goto error;
1993 }
1994 } else if (!strcmp(name, "truststore-reference")) {
1995 if (nc_server_configure_truststore_reference(node, op)) {
1996 goto error;
1997 }
1998 } else if (!strcmp(name, "password")) {
1999 if (nc_server_configure_password(node, op)) {
2000 goto error;
2001 }
2002 } else if (!strcmp(name, "pam-config-file-name")) {
2003 if (nc_server_configure_pam_name(node, op)) {
2004 goto error;
2005 }
2006 } else if (!strcmp(name, "pam-config-file-dir")) {
2007 if (nc_server_configure_pam_dir(node, op)) {
2008 goto error;
2009 }
2010 } else if (!strcmp(name, "none")) {
2011 if (nc_server_configure_none(node, op)) {
2012 goto error;
2013 }
2014 } else if (!strcmp(name, "host-key-alg")) {
2015 if (nc_server_configure_host_key_alg(node, op)) {
2016 goto error;
2017 }
2018 } else if (!strcmp(name, "key-exchange-alg")) {
2019 if (nc_server_configure_kex_alg(node, op)) {
2020 goto error;
2021 }
2022 } else if (!strcmp(name, "encryption-alg")) {
2023 if (nc_server_configure_encryption_alg(node, op)) {
2024 goto error;
2025 }
2026 } else if (!strcmp(name, "mac-alg")) {
2027 if (nc_server_configure_mac_alg(node, op)) {
2028 goto error;
2029 }
2030 } 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,
2031 "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,
2032 "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,
2033 "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,
2034 "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,
2035 "id")) {} else if (!strcmp(name, "fingerprint")) {} else if (!strcmp(name, "map-type")) {}
2036
2037 return 0;
2038
2039error:
2040 ERR(NULL, "Configuring (%s) failed.", LYD_NAME(node));
2041 return 1;
2042}
2043
2044int
2045nc_session_server_parse_tree(const struct lyd_node *node, NC_OPERATION parent_op)
2046{
2047 struct lyd_node *child;
2048 struct lyd_meta *m;
2049 NC_OPERATION current_op;
2050
2051 assert(node);
2052
2053 /* get current op */
2054 LY_LIST_FOR(node->meta, m) {
2055 if (!strcmp(m->name, "operation")) {
2056 if (!strcmp(lyd_get_meta_value(m), "create")) {
2057 current_op = NC_OP_CREATE;
2058 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
2059 current_op = NC_OP_DELETE;
2060 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
2061 current_op = NC_OP_REPLACE;
2062 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
2063 current_op = NC_OP_NONE;
2064 }
2065 break;
2066 }
2067 }
2068
2069 /* node has no op, inherit from the parent */
2070 if (!m) {
2071 current_op = parent_op;
2072 }
2073
2074 switch (current_op) {
2075 case NC_OP_NONE:
2076 break;
2077 case NC_OP_CREATE:
2078 case NC_OP_DELETE:
2079 case NC_OP_REPLACE:
2080 if (nc_server_configure(node, current_op)) {
2081 return 1;
2082 }
2083 break;
2084 default:
2085 break;
2086 }
2087
2088 if (current_op != NC_OP_DELETE) {
2089 LY_LIST_FOR(lyd_child(node), child) {
2090 if (nc_session_server_parse_tree(child, current_op)) {
2091 return 1;
2092 }
2093 }
2094 }
2095 return 0;
2096}
2097
2098static int
roman45cec4e2023-02-17 10:21:39 +01002099nc_server_configure_asymmetric_key_certificate(const struct lyd_node *tree, struct nc_ks_asym_key *key)
romanc1d2b092023-02-02 08:58:27 +01002100{
2101 int ret = 0;
roman45cec4e2023-02-17 10:21:39 +01002102 struct lyd_node *node;
romanc1d2b092023-02-02 08:58:27 +01002103 void *tmp;
2104
roman45cec4e2023-02-17 10:21:39 +01002105 /* create new certificate */
2106 tmp = realloc(key->certs, (key->cert_count + 1) * sizeof *key->certs);
2107 if (!tmp) {
2108 ERRMEM;
2109 ret = 1;
2110 goto cleanup;
2111 }
2112 key->certs = tmp;
2113 key->cert_count++;
2114
2115 /* set name */
2116 lyd_find_path(tree, "name", 0, &node);
2117 assert(node);
2118
2119 key->certs[key->cert_count - 1].name = strdup(lyd_get_value(node));
2120 if (!key->certs[key->cert_count - 1].name) {
2121 ERRMEM;
2122 ret = 1;
romanc1d2b092023-02-02 08:58:27 +01002123 goto cleanup;
2124 }
2125
roman45cec4e2023-02-17 10:21:39 +01002126 /* set certificate data */
2127 lyd_find_path(tree, "cert-data", 0, &node);
2128 assert(node);
romanc1d2b092023-02-02 08:58:27 +01002129
roman45cec4e2023-02-17 10:21:39 +01002130 key->certs[key->cert_count - 1].cert_base64 = strdup(lyd_get_value(node));
2131 if (!key->certs[key->cert_count - 1].cert_base64) {
2132 ERRMEM;
2133 ret = 1;
2134 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002135 }
2136
2137cleanup:
roman45cec4e2023-02-17 10:21:39 +01002138 return ret;
2139}
2140
2141static int
2142nc_server_configure_asymmetric_key(const struct lyd_node *tree)
2143{
2144 int ret = 0;
2145 struct lyd_node *node = NULL, *iter;
2146 void *tmp;
2147 struct nc_keystore *ks = &server_opts.keystore;
2148 struct nc_ks_asym_key *key;
2149 const char *format;
2150
2151 /* create new asymmetric key */
2152 tmp = realloc(ks->asym_keys, (ks->asym_key_count + 1) * sizeof *ks->asym_keys);
2153 if (!tmp) {
2154 ERRMEM;
2155 ret = 1;
2156 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002157 }
roman45cec4e2023-02-17 10:21:39 +01002158 ks->asym_keys = tmp;
2159 memset(&ks->asym_keys[ks->asym_key_count], 0, sizeof *ks->asym_keys);
2160 key = &ks->asym_keys[ks->asym_key_count];
2161 ks->asym_key_count++;
2162
2163 /* set name */
2164 lyd_find_path(tree, "name", 0, &node);
2165 assert(node);
2166
2167 key->name = strdup(lyd_get_value(node));
2168 if (!key->name) {
2169 ERRMEM;
2170 ret = 1;
2171 goto cleanup;
2172 }
2173
2174 /* set public-key-format, mandatory */
2175 lyd_find_path(tree, "public-key-format", 0, &node);
2176 assert(node);
2177
2178 format = ((struct lyd_node_term *)node)->value.ident->name;
2179 if (!strcmp(format, "ssh-public-key-format")) {
2180 key->pubkey_type = NC_SSH_PUBKEY_X509;
2181 } else if (!strcmp(format, "subject-public-key-info-format")) {
2182 key->pubkey_type = NC_SSH_PUBKEY_SSH2;
2183 } else {
2184 ERR(NULL, "Public key format \"%s\" not supported.", format);
2185 ret = 1;
2186 goto cleanup;
2187 }
2188
2189 /* set public-key, mandatory */
2190 lyd_find_path(tree, "public-key", 0, &node);
2191 assert(node);
2192
2193 key->pub_base64 = strdup(lyd_get_value(node));
2194 if (!key->pub_base64) {
2195 ERRMEM;
2196 ret = 1;
2197 goto cleanup;
2198 }
2199
2200 /* set private-key-format */
2201 ret = lyd_find_path(tree, "private-key-format", 0, &node);
2202 if (!ret) {
2203 format = ((struct lyd_node_term *)node)->value.ident->name;
2204 if (!strcmp(format, "rsa-private-key-format")) {
2205 key->privkey_type = NC_SSH_KEY_RSA;
2206 } else if (!strcmp(format, "ec-private-key-format")) {
2207 key->privkey_type = NC_SSH_KEY_ECDSA;
2208 } else {
2209 ERR(NULL, "Private key format (%s) not supported.", format);
2210 ret = 1;
2211 goto cleanup;
2212 }
2213 }
2214
2215 /* set private key, mandatory */
2216 lyd_find_path(tree, "cleartext-private-key", 0, &node);
2217 assert(node);
2218
2219 key->priv_base64 = strdup(lyd_get_value(node));
2220 if (!key->priv_base64) {
2221 ERRMEM;
2222 ret = 1;
2223 goto cleanup;
2224 }
2225
2226 /* set certificates associated with the key pair */
2227 ret = lyd_find_path(tree, "certificates", 0, &node);
2228 if (!ret) {
2229 node = lyd_child(node);
2230 if (node) {
2231 /* certificate list instance */
2232 LY_LIST_FOR(node, iter) {
2233 if (nc_server_configure_asymmetric_key_certificate(iter, key)) {
2234 ret = 1;
2235 goto cleanup;
2236 }
2237 }
2238 }
2239 } else if (ret == LY_ENOTFOUND) {
2240 /* certificates container not present, but it's ok */
2241 ret = 0;
2242 }
2243
2244cleanup:
2245 return ret;
2246}
2247
2248static int
2249nc_server_configure_symmetric_key(const struct lyd_node *tree)
2250{
2251 int ret = 0;
2252 const char *format;
2253 struct lyd_node *node;
2254 struct nc_keystore *ks = &server_opts.keystore;
2255 struct nc_ks_sym_key *key;
2256 void *tmp;
2257
2258 /* create new symmetric key */
2259 tmp = realloc(ks->sym_keys, (ks->sym_key_count + 1) * sizeof *ks->sym_keys);
2260 if (tmp) {
2261 ERRMEM;
2262 ret = 1;
2263 goto cleanup;
2264 }
2265 memset(&ks->sym_keys[ks->sym_key_count], 0, sizeof *ks->sym_keys);
2266 ks->sym_keys = tmp;
2267 key = &ks->sym_keys[ks->sym_key_count];
2268 ks->sym_key_count++;
2269
2270 /* set name */
2271 lyd_find_path(tree, "name", 0, &node);
2272 assert(node);
2273
2274 key->name = strdup(lyd_get_value(node));
2275 if (!key->name) {
2276 ERRMEM;
2277 ret = 1;
2278 goto cleanup;
2279 }
2280
2281 /* check if the identity matches with the supported one */
2282 lyd_find_path(tree, "key-format", 0, &node);
2283 assert(node);
2284
2285 format = ((struct lyd_node_term *)node)->value.ident->name;
2286 if (strcmp(format, "symmetric-key-format")) {
2287 ret = 1;
2288 goto cleanup;
2289 }
2290
2291 /* set key data */
2292 lyd_find_path(tree, "cleartext-key", 0, &node);
2293 assert(node);
2294
2295 key->base64 = strdup(lyd_get_value(node));
2296 if (!key->base64) {
2297 ERRMEM;
2298 ret = 1;
2299 goto cleanup;
2300 }
2301
2302cleanup:
romanc1d2b092023-02-02 08:58:27 +01002303 return ret;
2304}
2305
2306static int
2307nc_fill_keystore(const struct lyd_node *data)
2308{
2309 int ret = 0;
2310 uint32_t prev_lo;
roman45cec4e2023-02-17 10:21:39 +01002311 struct lyd_node *tree, *as_keys, *s_keys, *iter;
romanc1d2b092023-02-02 08:58:27 +01002312
roman45cec4e2023-02-17 10:21:39 +01002313 /* silently search for nodes, some of them may not be present */
romanc1d2b092023-02-02 08:58:27 +01002314 prev_lo = ly_log_options(0);
roman45cec4e2023-02-17 10:21:39 +01002315
2316 ret = lyd_find_path(data, "/ietf-keystore:keystore", 0, &tree);
romanc1d2b092023-02-02 08:58:27 +01002317 if (ret) {
2318 WRN(NULL, "Keystore container not found in the YANG data.");
roman45cec4e2023-02-17 10:21:39 +01002319 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002320 }
2321
roman45cec4e2023-02-17 10:21:39 +01002322 ret = lyd_find_path(tree, "asymmetric-keys", 0, &as_keys);
2323 if (!ret) {
2324 /* asymmetric keys container is present */
2325 as_keys = lyd_child(as_keys);
2326 if (as_keys && !strcmp(LYD_NAME(as_keys), "asymmetric-key")) {
2327 /* asymmetric key list */
2328 LY_LIST_FOR(as_keys, iter) {
2329 if (nc_server_configure_asymmetric_key(iter)) {
2330 ret = 1;
2331 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002332 }
2333 }
romanc1d2b092023-02-02 08:58:27 +01002334 }
romanc1d2b092023-02-02 08:58:27 +01002335 }
2336
roman45cec4e2023-02-17 10:21:39 +01002337 ret = lyd_find_path(tree, "symmetric-keys", 0, &s_keys);
2338 if (!ret) {
2339 /* symmetric keys container is present */
2340 s_keys = lyd_child(s_keys);
2341 if (s_keys && !strcmp(LYD_NAME(s_keys), "symmetric-key")) {
2342 /* symmetric key list */
2343 LY_LIST_FOR(s_keys, iter) {
2344 if (nc_server_configure_symmetric_key(iter)) {
2345 ret = 1;
2346 goto cleanup;
2347 }
2348 }
2349 }
2350 }
romanc1d2b092023-02-02 08:58:27 +01002351
roman45cec4e2023-02-17 10:21:39 +01002352cleanup:
2353 /* reset the logging options back to what they were */
2354 ly_log_options(prev_lo);
2355 return ret;
romanc1d2b092023-02-02 08:58:27 +01002356}
2357
2358API int
2359nc_server_config_load_modules(struct ly_ctx **ctx)
2360{
2361 int i, new_ctx = 0;
2362
2363 if (!*ctx) {
2364 if (ly_ctx_new(NC_SERVER_SEARCH_DIR, 0, ctx)) {
2365 ERR(NULL, "Couldn't create new libyang context.\n");
2366 goto error;
2367 }
2368 new_ctx = 1;
2369 }
2370
2371 /* all features */
2372 const char *ietf_nectonf_server[] = {"ssh-listen", "tls-listen", "ssh-call-home", "tls-call-home", "central-netconf-server-supported", NULL};
2373 /* all features */
2374 const char *ietf_x509_cert_to_name[] = {NULL};
2375 /* no private-key-encryption and csr-generation */
2376 const char *ietf_crypto_types[] = {
2377 "one-symmetric-key-format", "one-asymmetric-key-format", "symmetrically-encrypted-value-format",
2378 "asymmetrically-encrypted-value-format", "cms-enveloped-data-format", "cms-encrypted-data-format",
2379 "p10-based-csrs", "certificate-expiration-notification", "hidden-keys", "password-encryption",
2380 "symmetric-key-encryption", NULL
2381 };
2382 /* all features */
2383 const char *ietf_tcp_common[] = {"keepalives-supported", NULL};
2384 /* no ssh-x509-certs */
2385 const char *ietf_ssh_common[] = {"transport-params", "public-key-generation", NULL};
2386 /* all features */
2387 const char *iana_ssh_encryption_algs[] = {NULL};
2388 /* all features */
2389 const char *iana_ssh_key_exchange_algs[] = {NULL};
2390 /* all features */
2391 const char *iana_ssh_mac_algs[] = {NULL};
2392 /* all features */
2393 const char *iana_ssh_public_key_algs[] = {NULL};
2394 /* all features */
2395 const char *ietf_keystore[] = {"central-keystore-supported", "local-definitions-supported", "asymmetric-keys", "symmetric-keys", NULL};
2396 /* no ssh-server-keepalives and local-user-auth-hostbased */
2397 const char *ietf_ssh_server[] = {"local-users-supported", "local-user-auth-publickey", "local-user-auth-password", "local-user-auth-none", NULL};
2398 /* all features */
2399 const char *ietf_truststore[] = {"central-truststore-supported", "local-definitions-supported", "certificates", "public-keys", NULL};
2400 /* all features */
2401 const char *ietf_tls_server[] = {
2402 "tls-server-keepalives", "server-ident-x509-cert", "server-ident-raw-public-key", "server-ident-tls12-psk",
2403 "server-ident-tls13-epsk", "client-auth-supported", "client-auth-x509-cert", "client-auth-raw-public-key",
2404 "client-auth-tls12-psk", "client-auth-tls13-epsk", NULL
2405 };
2406 /* all features */
2407 const char *libnetconf2_netconf_server[] = {NULL};
2408
2409 const char *module_names[] = {
2410 "ietf-netconf-server", "ietf-x509-cert-to-name", "ietf-crypto-types",
2411 "ietf-tcp-common", "ietf-ssh-common", "iana-ssh-encryption-algs",
2412 "iana-ssh-key-exchange-algs", "iana-ssh-mac-algs", "iana-ssh-public-key-algs",
2413 "ietf-keystore", "ietf-ssh-server", "ietf-truststore",
2414 "ietf-tls-server", "libnetconf2-netconf-server", NULL
2415 };
2416
2417 const char **module_features[] = {
2418 ietf_nectonf_server, ietf_x509_cert_to_name, ietf_crypto_types,
2419 ietf_tcp_common, ietf_ssh_common, iana_ssh_encryption_algs,
2420 iana_ssh_key_exchange_algs, iana_ssh_mac_algs, iana_ssh_public_key_algs,
2421 ietf_keystore, ietf_ssh_server, ietf_truststore,
2422 ietf_tls_server, libnetconf2_netconf_server, NULL
2423 };
2424
2425 for (i = 0; module_names[i] != NULL; i++) {
2426 if (!ly_ctx_load_module(*ctx, module_names[i], NULL, module_features[i])) {
2427 ERR(NULL, "Loading module \"%s\" failed.\n", module_names[i]);
2428 goto error;
2429 }
2430 }
2431
2432 return 0;
2433
2434error:
2435 if (new_ctx) {
2436 ly_ctx_destroy(*ctx);
2437 *ctx = NULL;
2438 }
2439 return 1;
2440}
2441
2442API int
2443nc_server_config_setup_path(const struct ly_ctx *ctx, const char *path)
2444{
2445 struct lyd_node *tree = NULL;
2446 int ret = 0;
2447
2448 if (!path) {
2449 ERRARG("Missing path parameter.");
2450 ret = 1;
2451 goto cleanup;
2452 }
2453
2454 ret = lyd_parse_data_path(ctx, path, LYD_XML, LYD_PARSE_NO_STATE | LYD_PARSE_STRICT, LYD_VALIDATE_NO_STATE, &tree);
2455 if (ret) {
2456 goto cleanup;
2457 }
2458
2459 ret = nc_server_config_setup(tree);
2460 if (ret) {
2461 goto cleanup;
2462 }
2463
2464cleanup:
2465 lyd_free_all(tree);
2466 return ret;
2467}
2468
2469API int
2470nc_server_config_setup(const struct lyd_node *data)
2471{
2472 int ret = 0;
2473 struct lyd_node *tree;
2474 struct lyd_meta *m;
2475 NC_OPERATION op;
2476
2477 /* LOCK */
2478 pthread_rwlock_wrlock(&server_opts.config_lock);
2479
2480 ret = nc_fill_keystore(data);
2481 if (ret) {
2482 ERR(NULL, "Filling keystore failed.");
2483 goto cleanup;
2484 }
2485
2486 ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &tree);
2487 if (ret) {
2488 ERR(NULL, "Unable to find the netconf-server container in the YANG data.");
2489 goto cleanup;
2490 }
2491
2492 LY_LIST_FOR(tree->meta, m) {
2493 if (!strcmp(m->name, "operation")) {
2494 if (!strcmp(lyd_get_meta_value(m), "create")) {
2495 op = NC_OP_CREATE;
2496 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
2497 op = NC_OP_DELETE;
2498 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
2499 op = NC_OP_REPLACE;
2500 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
2501 op = NC_OP_NONE;
2502 } else {
2503 ERR(NULL, "Unexpected operation (%s).", lyd_get_meta_value(m));
2504 ret = 1;
2505 goto cleanup;
2506 }
2507 }
2508 }
2509
2510 if (nc_session_server_parse_tree(tree, op)) {
2511 ret = 1;
2512 goto cleanup;
2513 }
2514
2515cleanup:
2516 /* UNLOCK */
2517 pthread_rwlock_unlock(&server_opts.config_lock);
2518 return ret;
2519}