blob: 50e3c1bf7765d297412b6670d6c3fafcb5afdc8f [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
roman83683fb2023-02-24 09:15:23 +0100482nc_server_del_unix_socket(struct nc_bind *bind, struct nc_server_unix_opts *opts)
483{
484 if (bind->sock > -1) {
485 close(bind->sock);
486 }
487
488 free(bind->address);
489 free(opts->address);
490
491 free(opts);
492 opts = NULL;
493}
494
495void
496nc_server_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *bind)
497{
498 nc_server_del_endpt_name(endpt);
499 nc_server_del_unix_socket(bind, endpt->opts.unixsock);
500
501 server_opts.endpt_count--;
502 if (!server_opts.endpt_count) {
503 free(server_opts.endpts);
504 free(server_opts.binds);
505 server_opts.endpts = NULL;
506 server_opts.binds = NULL;
507 }
508}
509
510void
roman45cec4e2023-02-17 10:21:39 +0100511nc_server_config_del_keystore(void)
512{
513 int i, j;
514 struct nc_keystore *ks = &server_opts.keystore;
515
516 /* delete all asymmetric keys */
517 for (i = 0; i < ks->asym_key_count; i++) {
518 free(ks->asym_keys[i].name);
519 free(ks->asym_keys[i].pub_base64);
520 free(ks->asym_keys[i].priv_base64);
521
522 for (j = 0; j < ks->asym_keys[i].cert_count; j++) {
523 /* free associated certificates */
524 free(ks->asym_keys[i].certs[j].name);
525 free(ks->asym_keys[i].certs[j].cert_base64);
526 }
527 free(ks->asym_keys[i].certs);
528 ks->asym_keys[i].cert_count = 0;
529 }
530 free(ks->asym_keys);
531 ks->asym_key_count = 0;
532
533 /* delete all symmetric keys */
534 for (i = 0; i < ks->sym_key_count; i++) {
535 free(ks->sym_keys[i].name);
536 free(ks->sym_keys[i].base64);
537 }
538 free(ks->sym_keys);
539 ks->sym_key_count = 0;
540}
541
romanc1d2b092023-02-02 08:58:27 +0100542/* presence container */
543int
544nc_server_configure_listen(NC_OPERATION op)
545{
546 uint16_t i;
547
548 assert(op == NC_OP_CREATE || op == NC_OP_DELETE);
549
550 if (op == NC_OP_DELETE) {
551 for (i = 0; i < server_opts.endpt_count; i++) {
roman83683fb2023-02-24 09:15:23 +0100552 if (server_opts.endpts[i].ti == NC_TI_LIBSSH) {
553 nc_server_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]);
554 } else if (server_opts.endpts[i].ti == NC_TI_OPENSSL) {
555 /* todo */
556 } else {
557 nc_server_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]);
558 }
romanc1d2b092023-02-02 08:58:27 +0100559 }
560 }
561
562 return 0;
563}
564
565/* default leaf */
566static int
567nc_server_configure_idle_timeout(const struct lyd_node *node, NC_OPERATION op)
568{
569 assert(!strcmp(LYD_NAME(node), "idle-timeout"));
570
571 if (equal_parent_name(node, 1, "listen")) {
572 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
573 server_opts.idle_timeout = strtoul(lyd_get_value(node), NULL, 10);
574 } else {
575 /* default value */
576 server_opts.idle_timeout = 3600;
577 }
578 }
579
580 return 0;
581}
582
583static int
584nc_server_create_bind(void)
585{
586 int ret = 0;
587 void *tmp;
588
589 tmp = realloc(server_opts.binds, (server_opts.endpt_count + 1) * sizeof *server_opts.binds);
590 if (!tmp) {
591 ERRMEM;
592 ret = 1;
593 goto cleanup;
594 }
595 server_opts.binds = tmp;
596 memset(&server_opts.binds[server_opts.endpt_count], 0, sizeof *server_opts.binds);
597
598 server_opts.binds[server_opts.endpt_count].sock = -1;
599
600cleanup:
601 return ret;
602}
603
604static int
605nc_server_create_endpoint(const struct lyd_node *node)
606{
607 int ret = 0;
608 void *tmp;
609
610 tmp = realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts);
611 if (!tmp) {
612 ERRMEM;
613 ret = 1;
614 goto cleanup;
615 }
616 server_opts.endpts = tmp;
617 memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts);
618
619 node = lyd_child(node);
620 assert(!strcmp(LYD_NAME(node), "name"));
621
622 server_opts.endpts[server_opts.endpt_count].name = strdup(lyd_get_value(node));
623 if (!server_opts.endpts[server_opts.endpt_count].name) {
624 ERRMEM;
625 ret = 1;
626 goto cleanup;
627 }
628
629 if (nc_server_create_bind()) {
630 ret = 1;
631 goto cleanup;
632 }
633
634 server_opts.endpt_count++;
635
636cleanup:
637 return ret;
638}
639
640/* list */
641static int
642nc_server_configure_endpoint(const struct lyd_node *node, NC_OPERATION op)
643{
644 int ret = 0;
645 struct nc_endpt *endpt;
646 struct nc_bind *bind;
647
648 assert(!strcmp(LYD_NAME(node), "endpoint"));
649
650 if (op == NC_OP_CREATE) {
651 ret = nc_server_create_endpoint(node);
652 if (ret) {
653 goto cleanup;
654 }
655 } else if (op == NC_OP_DELETE) {
656 /* free all children */
657 if (nc_server_get_endpt(node, &endpt, &bind)) {
658 ret = 1;
659 goto cleanup;
660 }
661 nc_server_del_endpt_ssh(endpt, bind);
662 }
663
664cleanup:
665 return ret;
666}
667
668static int
669nc_server_create_ssh(struct nc_endpt *endpt)
670{
671 endpt->ti = NC_TI_LIBSSH;
672 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
673 if (!endpt->opts.ssh) {
674 ERRMEM;
675 return 1;
676 }
677
678 return 0;
679}
680
681/* NP container */
682static int
683nc_server_configure_ssh(const struct lyd_node *node, NC_OPERATION op)
684{
685 struct nc_endpt *endpt;
686 struct nc_bind *bind;
687 int ret = 0;
688
689 assert(!strcmp(LYD_NAME(node), "ssh"));
690
691 if (nc_server_get_endpt(node, &endpt, &bind)) {
692 ret = 1;
693 goto cleanup;
694 }
695
696 if (op == NC_OP_CREATE) {
697 ret = nc_server_create_ssh(endpt);
698 if (ret) {
699 goto cleanup;
700 }
701 } else if (op == NC_OP_DELETE) {
702 nc_server_del_ssh(bind, endpt->opts.ssh);
703 }
704
705cleanup:
706 return ret;
707}
708
709static int
710nc_server_config_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port)
711{
712 int sock = -1, set_addr, ret = 0;
713
roman83683fb2023-02-24 09:15:23 +0100714 assert((address && !port) || (!address && port) || (endpt->ti == NC_TI_UNIX));
romanc1d2b092023-02-02 08:58:27 +0100715
716 if (address) {
717 set_addr = 1;
718 } else {
719 set_addr = 0;
720 }
721
722 if (set_addr) {
723 port = bind->port;
724 } else {
725 address = bind->address;
726 }
727
romanc1d2b092023-02-02 08:58:27 +0100728 /* we have all the information we need to create a listening socket */
roman83683fb2023-02-24 09:15:23 +0100729 if ((address && port) || (endpt->ti == NC_TI_UNIX)) {
romanc1d2b092023-02-02 08:58:27 +0100730 /* create new socket, close the old one */
roman83683fb2023-02-24 09:15:23 +0100731 if (endpt->ti == NC_TI_UNIX) {
732 sock = nc_sock_listen_unix(endpt->opts.unixsock);
733 } else {
734 sock = nc_sock_listen_inet(address, port, &endpt->ka);
735 }
736
romanc1d2b092023-02-02 08:58:27 +0100737 if (sock == -1) {
738 ret = 1;
739 goto cleanup;
740 }
741
742 if (bind->sock > -1) {
743 close(bind->sock);
744 }
745 bind->sock = sock;
746 }
747
748 if (sock > -1) {
749 switch (endpt->ti) {
roman83683fb2023-02-24 09:15:23 +0100750 case NC_TI_UNIX:
751 VRB(NULL, "Listening on %s for UNIX connections.", endpt->opts.unixsock->address);
752 break;
romanc1d2b092023-02-02 08:58:27 +0100753#ifdef NC_ENABLED_SSH
754 case NC_TI_LIBSSH:
755 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
756 break;
757#endif
758#ifdef NC_ENABLED_TLS
759 case NC_TI_OPENSSL:
760 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
761 break;
762#endif
763 default:
764 ERRINT;
765 ret = 1;
766 break;
767 }
768 }
769
770cleanup:
771 return ret;
772}
773
774/* mandatory leaf */
775static int
776nc_server_configure_local_address(const struct lyd_node *node, NC_OPERATION op)
777{
778 struct nc_endpt *endpt;
779 struct nc_bind *bind;
780 int ret = 0;
781
782 (void) op;
783
784 assert(!strcmp(LYD_NAME(node), "local-address"));
785
786 if (equal_parent_name(node, 4, "listen")) {
787 if (nc_server_get_endpt(node, &endpt, &bind)) {
788 ret = 1;
789 goto cleanup;
790 }
791
792 nc_server_del_local_address(bind);
793 bind->address = strdup(lyd_get_value(node));
794 if (!bind->address) {
795 ERRMEM;
796 ret = 1;
797 goto cleanup;
798 }
799
800 ret = nc_server_config_set_address_port(endpt, bind, lyd_get_value(node), 0);
801 if (ret) {
802 goto cleanup;
803 }
804 }
805
806cleanup:
807 return ret;
808}
809
810/* leaf with default value */
811static int
812nc_server_configure_local_port(const struct lyd_node *node, NC_OPERATION op)
813{
814 struct nc_endpt *endpt;
815 struct nc_bind *bind;
816 int ret = 0;
817
818 assert(!strcmp(LYD_NAME(node), "local-port"));
819
820 if (equal_parent_name(node, 4, "listen")) {
821 if (nc_server_get_endpt(node, &endpt, &bind)) {
822 ret = 1;
823 goto cleanup;
824 }
825
826 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
827 bind->port = strtoul(lyd_get_value(node), NULL, 10);
828 } else {
829 /* delete -> set to default */
830 bind->port = 0;
831 }
832
833 ret = nc_server_config_set_address_port(endpt, bind, NULL, bind->port);
834 if (ret) {
835 goto cleanup;
836 }
837 }
838
839cleanup:
840 return ret;
841}
842
843/* P container */
844static int
845nc_server_configure_keepalives(const struct lyd_node *node, NC_OPERATION op)
846{
847 struct nc_endpt *endpt;
848 struct nc_bind *bind;
849 int ret = 0;
850
851 assert(!strcmp(LYD_NAME(node), "keepalives"));
852
853 if (equal_parent_name(node, 4, "listen")) {
854 if (nc_server_get_endpt(node, &endpt, &bind)) {
855 ret = 1;
856 goto cleanup;
857 }
858
859 if (op == NC_OP_CREATE) {
860 endpt->ka.enabled = 1;
861 } else {
862 endpt->ka.enabled = 0;
863 }
864 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
865 if (ret) {
866 goto cleanup;
867 }
868 }
869
870cleanup:
871 return ret;
872}
873
874/* mandatory leaf */
875static int
876nc_server_configure_idle_time(const struct lyd_node *node, NC_OPERATION op)
877{
878 struct nc_endpt *endpt;
879 struct nc_bind *bind;
880 int ret = 0;
881
882 assert(!strcmp(LYD_NAME(node), "idle-time"));
883
884 if (equal_parent_name(node, 4, "listen")) {
885 if (nc_server_get_endpt(node, &endpt, &bind)) {
886 ret = 1;
887 goto cleanup;
888 }
889
890 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
891 endpt->ka.idle_time = strtoul(lyd_get_value(node), NULL, 10);
892 } else {
893 endpt->ka.idle_time = 0;
894 }
895 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
896 if (ret) {
897 goto cleanup;
898 }
899 }
900
901cleanup:
902 return ret;
903}
904
905/* mandatory leaf */
906static int
907nc_server_configure_max_probes(const struct lyd_node *node, NC_OPERATION op)
908{
909 struct nc_endpt *endpt;
910 struct nc_bind *bind;
911 int ret = 0;
912
913 assert(!strcmp(LYD_NAME(node), "max-probes"));
914
915 if (equal_parent_name(node, 4, "listen")) {
916 if (nc_server_get_endpt(node, &endpt, &bind)) {
917 ret = 1;
918 goto cleanup;
919 }
920
921 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
922 endpt->ka.max_probes = strtoul(lyd_get_value(node), NULL, 10);
923 } else {
924 endpt->ka.max_probes = 0;
925 }
926 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
927 if (ret) {
928 goto cleanup;
929 }
930 }
931
932cleanup:
933 return ret;
934}
935
936/* mandatory leaf */
937static int
938nc_server_configure_probe_interval(const struct lyd_node *node, NC_OPERATION op)
939{
940 struct nc_endpt *endpt;
941 struct nc_bind *bind;
942 int ret = 0;
943
944 assert(!strcmp(LYD_NAME(node), "probe-interval"));
945
946 if (equal_parent_name(node, 4, "listen")) {
947 if (nc_server_get_endpt(node, &endpt, &bind)) {
948 ret = 1;
949 goto cleanup;
950 }
951
952 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
953 endpt->ka.probe_interval = strtoul(lyd_get_value(node), NULL, 10);
954 } else {
955 endpt->ka.probe_interval = 0;
956 }
957 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
958 if (ret) {
959 goto cleanup;
960 }
961 }
962
963cleanup:
964 return ret;
965}
966
967static int
968nc_server_create_host_key(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
969{
970 int ret = 0;
971 void *tmp;
972
973 tmp = realloc(opts->hostkeys,
974 (opts->hostkey_count + 1) * sizeof *opts->hostkeys);
975 if (!tmp) {
976 ERRMEM;
977 ret = 1;
978 goto cleanup;
979 }
980 opts->hostkeys = tmp;
981
982 memset(&opts->hostkeys[opts->hostkey_count], 0, sizeof *opts->hostkeys);
983
984 opts->hostkeys[opts->hostkey_count].name = strdup(lyd_get_value(lyd_child(node)));
985 if (!opts->hostkeys[opts->hostkey_count].name) {
986 ERRMEM;
987 ret = 1;
988 goto cleanup;
989 }
990
991 /* set union selector */
992 lyd_find_path(node, "public-key", 0, (struct lyd_node **)&node);
993 assert(node);
994
995 if (!lyd_find_path(node, "local-definition", 0, NULL)) {
996 opts->hostkeys[opts->hostkey_count].ks_type = NC_STORE_LOCAL;
997 } else {
998 opts->hostkeys[opts->hostkey_count].ks_type = NC_STORE_KEYSTORE;
999 }
1000
1001 opts->hostkey_count++;
1002
1003cleanup:
1004 return ret;
1005}
1006
1007/* list */
1008static int
1009nc_server_configure_host_key(const struct lyd_node *node, NC_OPERATION op)
1010{
1011 struct nc_endpt *endpt;
1012 struct nc_hostkey *hostkey;
1013 int ret = 0;
1014
1015 assert(!strcmp(LYD_NAME(node), "host-key"));
1016
1017 if ((equal_parent_name(node, 1, "server-identity")) && (equal_parent_name(node, 5, "listen"))) {
1018 if (nc_server_get_endpt(node, &endpt, NULL)) {
1019 ret = 1;
1020 goto cleanup;
1021 }
1022
1023 if (op == NC_OP_CREATE) {
1024 ret = nc_server_create_host_key(node, endpt->opts.ssh);
1025 if (ret) {
1026 goto cleanup;
1027 }
1028 } else if (op == NC_OP_DELETE) {
1029 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1030 ret = 1;
1031 goto cleanup;
1032 }
1033
1034 nc_server_del_hostkey(endpt->opts.ssh, hostkey);
1035 }
1036 } else if (equal_parent_name(node, 1, "transport-params")) {
1037 /* just a container with the name host-key, nothing to be done */
1038 goto cleanup;
1039 } else {
1040 ERRINT;
1041 ret = 1;
1042 goto cleanup;
1043 }
1044
1045cleanup:
1046 return ret;
1047}
1048
1049/* mandatory leaf */
1050int
1051nc_server_configure_public_key_format(const struct lyd_node *node, NC_OPERATION op)
1052{
1053 const char *format;
1054 struct nc_endpt *endpt;
1055 struct nc_client_auth *auth_client;
1056 struct nc_client_auth_pubkey *pubkey;
1057 struct nc_hostkey *hostkey;
1058 int ret = 0;
1059
1060 assert(!strcmp(LYD_NAME(node), "public-key-format"));
1061
1062 format = ((struct lyd_node_term *)node)->value.ident->name;
1063
1064 if ((equal_parent_name(node, 6, "client-authentication")) && (equal_parent_name(node, 10, "listen"))) {
1065 if (nc_server_get_endpt(node, &endpt, NULL)) {
1066 ret = 1;
1067 goto cleanup;
1068 }
1069
1070 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1071 ret = 1;
1072 goto cleanup;
1073 }
1074
1075 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1076 ret = 1;
1077 goto cleanup;
1078 }
1079
1080 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1081 if (!strcmp(format, "ssh-public-key-format")) {
1082 pubkey->pubkey_type = NC_SSH_PUBKEY_X509;
1083 } else if (!strcmp(format, "subject-public-key-info-format")) {
1084 pubkey->pubkey_type = NC_SSH_PUBKEY_SSH2;
1085 } else {
1086 ERR(NULL, "Public key format (%s) not supported.", format);
1087 }
1088 }
1089 } else if ((equal_parent_name(node, 5, "server-identity")) && (equal_parent_name(node, 11, "listen"))) {
1090 if (nc_server_get_endpt(node, &endpt, NULL)) {
1091 ret = 1;
1092 goto cleanup;
1093 }
1094
1095 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1096 ret = 1;
1097 goto cleanup;
1098 }
1099
1100 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1101 if (!strcmp(format, "ssh-public-key-format")) {
1102 hostkey->pubkey_type = NC_SSH_PUBKEY_X509;
1103 } else if (!strcmp(format, "subject-public-key-info-format")) {
1104 hostkey->pubkey_type = NC_SSH_PUBKEY_SSH2;
1105 } else {
1106 ERR(NULL, "Public key format (%s) not supported.", format);
1107 }
1108 }
1109 }
1110
1111cleanup:
1112 return ret;
1113}
1114
1115/* leaf */
1116int
1117nc_server_configure_private_key_format(const struct lyd_node *node, NC_OPERATION op)
1118{
1119 const char *format;
1120 struct nc_endpt *endpt;
1121 struct nc_hostkey *hostkey;
1122 int ret = 0;
1123
1124 assert(!strcmp(LYD_NAME(node), "private-key-format"));
1125
1126 if (nc_server_get_endpt(node, &endpt, NULL)) {
1127 ret = 1;
1128 goto cleanup;
1129 }
1130
1131 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1132 ret = 1;
1133 goto cleanup;
1134 }
1135
1136 format = ((struct lyd_node_term *)node)->value.ident->name;
1137 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1138 if (!strcmp(format, "rsa-private-key-format")) {
1139 hostkey->privkey_type = NC_SSH_KEY_RSA;
1140 } else if (!strcmp(format, "ec-private-key-format")) {
1141 hostkey->privkey_type = NC_SSH_KEY_ECDSA;
1142 } else {
1143 ERR(NULL, "Private key format (%s) not supported.", format);
1144 }
1145 }
1146
1147cleanup:
1148 return ret;
1149}
1150
1151static int
1152nc_server_replace_cleartext_private_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
1153{
1154 nc_server_del_private_key(hostkey);
1155 hostkey->priv_base64 = strdup(lyd_get_value(node));
1156 if (!hostkey->priv_base64) {
1157 ERRMEM;
1158 return 1;
1159 }
1160
1161 return 0;
1162}
1163
1164static int
1165nc_server_configure_cleartext_private_key(const struct lyd_node *node, NC_OPERATION op)
1166{
1167 struct nc_endpt *endpt;
1168 struct nc_hostkey *hostkey;
1169 int ret = 0;
1170
1171 assert(!strcmp(LYD_NAME(node), "cleartext-private-key"));
1172
1173 if ((equal_parent_name(node, 6, "ssh")) && (equal_parent_name(node, 8, "listen"))) {
1174 if (nc_server_get_endpt(node, &endpt, NULL)) {
1175 ret = 1;
1176 goto cleanup;
1177 }
1178 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1179 ret = 1;
1180 goto cleanup;
1181 }
1182
1183 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1184 ret = nc_server_replace_cleartext_private_key(node, hostkey);
1185 if (ret) {
1186 goto cleanup;
1187 }
1188 } else {
1189 nc_server_del_private_key(hostkey);
1190 }
1191 }
1192
1193cleanup:
1194 return ret;
1195}
1196
1197static int
1198nc_server_create_keystore_reference(const struct lyd_node *node, struct nc_hostkey *hostkey)
1199{
1200 uint16_t i;
roman45cec4e2023-02-17 10:21:39 +01001201 struct nc_keystore *ks = &server_opts.keystore;
romanc1d2b092023-02-02 08:58:27 +01001202
1203 /* lookup name */
roman45cec4e2023-02-17 10:21:39 +01001204 for (i = 0; i < ks->asym_key_count; i++) {
1205 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
romanc1d2b092023-02-02 08:58:27 +01001206 break;
1207 }
1208 }
1209
roman45cec4e2023-02-17 10:21:39 +01001210 if (i == ks->asym_key_count) {
1211 ERR(NULL, "Keystore \"%s\" not found.", lyd_get_value(node));
romanc1d2b092023-02-02 08:58:27 +01001212 return 1;
1213 }
1214
roman45cec4e2023-02-17 10:21:39 +01001215 hostkey->ks_ref = &ks->asym_keys[i];
romanc1d2b092023-02-02 08:58:27 +01001216
1217 return 0;
1218}
1219
1220/* leaf */
1221static int
1222nc_server_configure_keystore_reference(const struct lyd_node *node, NC_OPERATION op)
1223{
1224 struct nc_endpt *endpt;
1225 struct nc_hostkey *hostkey;
1226 int ret = 0;
1227
1228 assert(!strcmp(LYD_NAME(node), "keystore-reference"));
1229
roman45cec4e2023-02-17 10:21:39 +01001230 if ((equal_parent_name(node, 3, "server-identity")) && (equal_parent_name(node, 7, "listen"))) {
romanc1d2b092023-02-02 08:58:27 +01001231 if (nc_server_get_endpt(node, &endpt, NULL)) {
1232 ret = 1;
1233 goto cleanup;
1234 }
1235 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1236 ret = 1;
1237 goto cleanup;
1238 }
1239
1240 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1241 ret = nc_server_create_keystore_reference(node, hostkey);
1242 if (ret) {
1243 goto cleanup;
1244 }
1245 } else {
roman45cec4e2023-02-17 10:21:39 +01001246 hostkey->ks_ref = NULL;
romanc1d2b092023-02-02 08:58:27 +01001247 }
1248 }
1249
1250cleanup:
1251 return ret;
1252}
1253
1254static int
1255nc_server_create_auth_key_public_key_list(const struct lyd_node *node, struct nc_client_auth *auth_client)
1256{
1257 int ret = 0;
1258 void *tmp;
1259
1260 assert(!strcmp(LYD_NAME(node), "public-key"));
1261
1262 tmp = realloc(auth_client->pubkeys, (auth_client->pubkey_count + 1) * sizeof *auth_client->pubkeys);
1263 if (!tmp) {
1264 ERRMEM;
1265 ret = 1;
1266 goto cleanup;
1267 }
1268 auth_client->pubkeys = tmp;
1269
1270 memset(&auth_client->pubkeys[auth_client->pubkey_count], 0, sizeof *auth_client->pubkeys);
1271
1272 node = lyd_child(node);
1273 assert(!strcmp(LYD_NAME(node), "name"));
1274
1275 auth_client->pubkeys[auth_client->pubkey_count].name = strdup(lyd_get_value(node));
1276 if (!auth_client->pubkeys[auth_client->pubkey_count].name) {
1277 ERRMEM;
1278 ret = 1;
1279 goto cleanup;
1280 }
1281
1282 ++auth_client->pubkey_count;
1283
1284cleanup:
1285 return ret;
1286}
1287
1288static int
1289nc_server_replace_auth_key_public_key_leaf(const struct lyd_node *node, struct nc_client_auth_pubkey *pubkey)
1290{
1291 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
1292
1293 pubkey->pub_base64 = strdup(lyd_get_value(node));
1294 if (!pubkey->pub_base64) {
1295 ERRMEM;
1296 return 1;
1297 }
1298
1299 return 0;
1300}
1301
1302static int
1303nc_server_replace_host_key_public_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
1304{
1305 nc_server_del_public_key(hostkey);
1306
1307 hostkey->pub_base64 = strdup(lyd_get_value(node));
1308 if (!hostkey->pub_base64) {
1309 ERRMEM;
1310 return 1;
1311 }
1312
1313 return 0;
1314}
1315
1316static int
1317nc_server_configure_public_key(const struct lyd_node *node, NC_OPERATION op)
1318{
1319 struct nc_endpt *endpt;
1320 struct nc_hostkey *hostkey;
1321 struct nc_client_auth *auth_client;
1322 struct nc_client_auth_pubkey *pubkey;
1323 int ret = 0;
1324
1325 assert(!strcmp(LYD_NAME(node), "public-key"));
1326
1327 if ((equal_parent_name(node, 3, "host-key")) && (equal_parent_name(node, 8, "listen"))) {
1328 /* server's public-key, mandatory leaf */
1329 if (nc_server_get_endpt(node, &endpt, NULL)) {
1330 ret = 1;
1331 goto cleanup;
1332 }
1333
1334 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1335 ret = 1;
1336 goto cleanup;
1337 }
1338
1339 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1340 ret = nc_server_replace_host_key_public_key(node, hostkey);
1341 if (ret) {
1342 goto cleanup;
1343 }
1344 }
1345 } else if ((equal_parent_name(node, 5, "client-authentication")) && (equal_parent_name(node, 9, "listen"))) {
1346 /* client auth pubkeys, list */
1347 if (nc_server_get_endpt(node, &endpt, NULL)) {
1348 ret = 1;
1349 goto cleanup;
1350 }
1351
1352 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1353 ret = 1;
1354 goto cleanup;
1355 }
1356
1357 if (op == NC_OP_CREATE) {
1358 ret = nc_server_create_auth_key_public_key_list(node, auth_client);
1359 if (ret) {
1360 goto cleanup;
1361 }
1362 } else if (op == NC_OP_DELETE) {
1363 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1364 ret = 1;
1365 goto cleanup;
1366 }
1367
1368 nc_server_del_auth_client_pubkey(auth_client, pubkey);
1369 }
1370 } else if ((equal_parent_name(node, 6, "client-authentication")) && (equal_parent_name(node, 10, "listen"))) {
1371 /* client auth pubkey, leaf */
1372 if (nc_server_get_endpt(node, &endpt, NULL)) {
1373 ret = 1;
1374 goto cleanup;
1375 }
1376
1377 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1378 ret = 1;
1379 goto cleanup;
1380 }
1381
1382 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1383 ret = 1;
1384 goto cleanup;
1385 }
1386
1387 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1388 ret = nc_server_replace_auth_key_public_key_leaf(node, pubkey);
1389 if (ret) {
1390 goto cleanup;
1391 }
1392 } else {
1393 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
1394 }
1395 }
1396
1397cleanup:
1398 return ret;
1399}
1400
1401static int
1402nc_server_create_user(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
1403{
1404 int ret = 0;
1405 void *tmp;
1406
1407 tmp = realloc(opts->auth_clients, (opts->client_count + 1) * sizeof *opts->auth_clients);
1408 if (!tmp) {
1409 ERRMEM;
1410 ret = 1;
1411 goto cleanup;
1412 }
1413 opts->auth_clients = tmp;
1414
1415 memset(&opts->auth_clients[opts->client_count], 0, sizeof *opts->auth_clients);
1416
1417 opts->auth_clients[opts->client_count].username = strdup(lyd_get_value(lyd_child(node)));
1418 if (!opts->auth_clients[opts->client_count].username) {
1419 ERRMEM;
1420 ret = 1;
1421 goto cleanup;
1422 }
1423
1424 lyd_find_path(node, "public-keys", 0, (struct lyd_node **)&node);
1425
1426 if (node) {
1427 /* set union selector */
1428 if (!lyd_find_path(node, "local-definition", 0, NULL)) {
1429 opts->auth_clients[opts->client_count].ks_type = NC_STORE_LOCAL;
1430 } else {
1431 opts->auth_clients[opts->client_count].ks_type = NC_STORE_TRUSTSTORE;
1432 }
1433 }
1434
1435 ++opts->client_count;
1436
1437cleanup:
1438 return ret;
1439}
1440
1441/* list */
1442static int
1443nc_server_configure_user(const struct lyd_node *node, NC_OPERATION op)
1444{
1445 struct nc_endpt *endpt;
1446 struct nc_client_auth *auth_client;
1447 int ret = 0;
1448
1449 assert(!strcmp(LYD_NAME(node), "user"));
1450
1451 if (equal_parent_name(node, 6, "listen")) {
1452 if (nc_server_get_endpt(node, &endpt, NULL)) {
1453 ret = 1;
1454 goto cleanup;
1455 }
1456
1457 if (op == NC_OP_CREATE) {
1458 ret = nc_server_create_user(node, endpt->opts.ssh);
1459 if (ret) {
1460 goto cleanup;
1461 }
1462 } else if (op == NC_OP_DELETE) {
1463 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1464 ret = 1;
1465 goto cleanup;
1466 }
1467
1468 nc_server_del_auth_client(endpt->opts.ssh, auth_client);
1469 }
1470 }
1471
1472cleanup:
1473 return ret;
1474}
1475
1476static int
1477nc_server_configure_auth_attempts(const struct lyd_node *node, NC_OPERATION op)
1478{
1479 struct nc_endpt *endpt;
1480 int ret = 0;
1481
1482 assert(!strcmp(LYD_NAME(node), "auth-attempts"));
1483
1484 if (equal_parent_name(node, 5, "listen")) {
1485 if (nc_server_get_endpt(node, &endpt, NULL)) {
1486 ret = 1;
1487 goto cleanup;
1488 }
1489
1490 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1491 endpt->opts.ssh->auth_attempts = strtoul(lyd_get_value(node), NULL, 10);
1492 }
1493 }
1494
1495cleanup:
1496 return ret;
1497}
1498
1499static int
1500nc_server_configure_auth_timeout(const struct lyd_node *node, NC_OPERATION op)
1501{
1502 struct nc_endpt *endpt;
1503 int ret = 0;
1504
1505 assert(!strcmp(LYD_NAME(node), "auth-timeout"));
1506
1507 if (equal_parent_name(node, 5, "listen")) {
1508 if (nc_server_get_endpt(node, &endpt, NULL)) {
1509 ret = 1;
1510 goto cleanup;
1511 }
1512
1513 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1514 endpt->opts.ssh->auth_timeout = strtoul(lyd_get_value(node), NULL, 10);
1515 }
1516 }
1517
1518cleanup:
1519 return ret;
1520}
1521
1522static int
1523nc_server_replace_truststore_reference(const struct lyd_node *node, struct nc_client_auth *client_auth)
1524{
1525 /*todo*/
1526 nc_server_del_truststore_reference(client_auth);
1527
1528 client_auth->ts_reference = strdup(lyd_get_value(node));
1529 if (!client_auth->ts_reference) {
1530 ERRMEM;
1531 return 1;
1532 }
1533
1534 return 0;
1535}
1536
1537/* leaf */
1538static int
1539nc_server_configure_truststore_reference(const struct lyd_node *node, NC_OPERATION op)
1540{
1541 struct nc_endpt *endpt;
1542 struct nc_client_auth *auth_client;
1543 int ret = 0;
1544
1545 assert(!strcmp(LYD_NAME(node), "truststore-reference"));
1546
1547 if ((equal_parent_name(node, 1, "public-keys")) && (equal_parent_name(node, 8, "listen"))) {
1548 if (nc_server_get_endpt(node, &endpt, NULL)) {
1549 ret = 1;
1550 goto cleanup;
1551 }
1552
1553 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1554 ret = 1;
1555 goto cleanup;
1556 }
1557
1558 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1559 ret = nc_server_replace_truststore_reference(node, auth_client);
1560 if (ret) {
1561 goto cleanup;
1562 }
1563 } else {
1564 nc_server_del_truststore_reference(auth_client);
1565 }
1566 }
1567
1568cleanup:
1569 return ret;
1570}
1571
1572static int
1573nc_server_replace_password(const struct lyd_node *node, struct nc_client_auth *auth_client)
1574{
1575 nc_server_del_auth_client_password(auth_client);
1576
1577 auth_client->password = strdup(lyd_get_value(node));
1578 if (!auth_client->password) {
1579 ERRMEM;
1580 return 1;
1581 }
1582
1583 return 0;
1584}
1585
1586/* leaf */
1587static int
1588nc_server_configure_password(const struct lyd_node *node, NC_OPERATION op)
1589{
1590 struct nc_endpt *endpt;
1591 struct nc_client_auth *auth_client;
1592 int ret = 0;
1593
1594 assert(!strcmp(LYD_NAME(node), "password"));
1595
1596 if (equal_parent_name(node, 7, "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_password(node, auth_client);
1609 if (ret) {
1610 goto cleanup;
1611 }
1612 } else {
1613 nc_server_del_auth_client_password(auth_client);
1614 }
1615 }
1616
1617cleanup:
1618 return ret;
1619}
1620
1621static int
1622nc_server_configure_pam_name(const struct lyd_node *node, NC_OPERATION op)
1623{
1624 struct nc_endpt *endpt;
1625 struct nc_client_auth *auth_client;
1626 int ret = 0;
1627
1628 assert(!strcmp(LYD_NAME(node), "pam-config-file-name"));
1629
1630 if (equal_parent_name(node, 8, "listen")) {
1631 if (nc_server_get_endpt(node, &endpt, NULL)) {
1632 ret = 1;
1633 goto cleanup;
1634 }
1635
1636 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1637 ret = 1;
1638 goto cleanup;
1639 }
1640
1641 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1642 nc_server_del_auth_client_pam_name(auth_client);
1643
1644 auth_client->pam_config_name = strdup(lyd_get_value(node));
1645 if (!auth_client->pam_config_name) {
1646 ERRMEM;
1647 ret = 1;
1648 goto cleanup;
1649 }
1650 }
1651 }
1652
1653cleanup:
1654 return ret;
1655}
1656
1657static int
1658nc_server_configure_pam_dir(const struct lyd_node *node, NC_OPERATION op)
1659{
1660 struct nc_endpt *endpt;
1661 struct nc_client_auth *auth_client;
1662 int ret = 0;
1663
1664 assert(!strcmp(LYD_NAME(node), "pam-config-file-dir"));
1665
1666 if (equal_parent_name(node, 8, "listen")) {
1667 if (nc_server_get_endpt(node, &endpt, NULL)) {
1668 ret = 1;
1669 goto cleanup;
1670 }
1671
1672 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1673 ret = 1;
1674 goto cleanup;
1675 }
1676
1677 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1678 nc_server_del_auth_client_pam_dir(auth_client);
1679 auth_client->pam_config_dir = strdup(lyd_get_value(node));
1680 if (!auth_client->pam_config_dir) {
1681 ERRMEM;
1682 ret = 1;
1683 goto cleanup;
1684 }
1685 }
1686 }
1687
1688cleanup:
1689 return ret;
1690}
1691
1692/* leaf */
1693static int
1694nc_server_configure_none(const struct lyd_node *node, NC_OPERATION op)
1695{
1696 struct nc_endpt *endpt;
1697 struct nc_client_auth *auth_client;
1698 int ret = 0;
1699
1700 assert(!strcmp(LYD_NAME(node), "none"));
1701
1702 if (equal_parent_name(node, 7, "listen")) {
1703 if (nc_server_get_endpt(node, &endpt, NULL)) {
1704 ret = 1;
1705 goto cleanup;
1706 }
1707
1708 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1709 ret = 1;
1710 goto cleanup;
1711 }
1712
1713 if (op == NC_OP_CREATE) {
1714 auth_client->supports_none = 1;
1715 } else {
1716 auth_client->supports_none = 0;
1717 }
1718 }
1719
1720cleanup:
1721 return ret;
1722}
1723
1724static int
1725nc_server_configure_transport_params(const char *alg, char **alg_store, NC_OPERATION op)
1726{
1727 int ret = 0, alg_found = 0;
1728 char *substr, *haystack;
1729 size_t alg_len = strlen(alg);
1730
1731 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1732 if (!*alg_store) {
1733 /* first call */
1734 *alg_store = strdup(alg);
1735 if (!*alg_store) {
1736 ERRMEM;
1737 ret = 1;
1738 goto cleanup;
1739 }
1740 } else {
1741 /* +1 because of ',' between algorithms */
1742 *alg_store = nc_realloc(*alg_store, strlen(*alg_store) + alg_len + 1 + 1);
1743 if (!*alg_store) {
1744 ERRMEM;
1745 ret = 1;
1746 goto cleanup;
1747 }
1748 sprintf(*alg_store, "%s,%s", *alg_store, alg);
1749 }
1750 } else {
1751 /* delete */
1752 haystack = *alg_store;
1753 while ((substr = strstr(haystack, alg))) {
1754 /* iterate over all the substrings */
1755 if (((substr == haystack) && (*(substr + alg_len) == ',')) ||
1756 ((substr != haystack) && (*(substr - 1) == ',') && (*(substr + alg_len) == ','))) {
1757 /* either the first element of the string or somewhere in the middle */
1758 memmove(substr, substr + alg_len + 1, strlen(substr + alg_len + 1));
1759 alg_found = 1;
1760 break;
1761 } else if ((*(substr - 1) == ',') && (*(substr + alg_len) == '\0')) {
1762 /* the last element of the string */
1763 *(substr - 1) = '\0';
1764 alg_found = 1;
1765 break;
1766 }
1767 haystack++;
1768 }
1769 if (!alg_found) {
1770 ERR(NULL, "Unable to delete an algorithm (%s), which was not previously added.", alg);
1771 ret = 1;
1772 }
1773 }
1774
1775cleanup:
1776 return ret;
1777}
1778
1779/* leaf-list */
1780static int
1781nc_server_configure_host_key_alg(const struct lyd_node *node, NC_OPERATION op)
1782{
1783 struct nc_endpt *endpt;
1784 int ret = 0, listen = 0;
1785 const char *alg;
1786 uint8_t i;
1787
1788 /* get the algorithm name and compare it with algs supported by libssh */
1789 alg = ((struct lyd_node_term *)node)->value.ident->name;
1790
1791 if (equal_parent_name(node, 6, "listen")) {
1792 listen = 1;
1793 if (nc_server_get_endpt(node, &endpt, NULL)) {
1794 ret = 1;
1795 goto cleanup;
1796 }
1797 }
1798
1799 i = 0;
1800 while (supported_hostkey_algs[i]) {
1801 if (!strcmp(supported_hostkey_algs[i], alg)) {
1802 if (listen) {
1803 if (nc_server_configure_transport_params(alg, &endpt->opts.ssh->hostkey_algs, op)) {
1804 ret = 1;
1805 goto cleanup;
1806 }
1807 }
1808 break;
1809 }
1810 i++;
1811 }
1812 if (!supported_hostkey_algs[i]) {
1813 /* algorithm not supported */
1814 ERR(NULL, "Public key algorithm (%s) not supported by libssh.", alg);
1815 ret = 1;
1816 }
1817
1818cleanup:
1819 return ret;
1820}
1821
1822/* leaf-list */
1823static int
1824nc_server_configure_kex_alg(const struct lyd_node *node, NC_OPERATION op)
1825{
1826 struct nc_endpt *endpt;
1827 int ret = 0, listen = 0;
1828 const char *alg;
1829 uint8_t i;
1830
1831 /* get the algorithm name and compare it with algs supported by libssh */
1832 alg = ((struct lyd_node_term *)node)->value.ident->name;
1833
1834 if (equal_parent_name(node, 6, "listen")) {
1835 listen = 1;
1836 if (nc_server_get_endpt(node, &endpt, NULL)) {
1837 ret = 1;
1838 goto cleanup;
1839 }
1840 }
1841
1842 i = 0;
1843 while (supported_kex_algs[i]) {
1844 if (!strcmp(supported_kex_algs[i], alg)) {
1845 if (listen) {
1846 if (nc_server_configure_transport_params(alg, &endpt->opts.ssh->kex_algs, op)) {
1847 ret = 1;
1848 goto cleanup;
1849 }
1850 }
1851 break;
1852 }
1853 i++;
1854 }
1855 if (!supported_kex_algs[i]) {
1856 /* algorithm not supported */
1857 ERR(NULL, "Key exchange algorithm (%s) not supported by libssh.", alg);
1858 ret = 1;
1859 }
1860
1861cleanup:
1862 return ret;
1863}
1864
1865/* leaf-list */
1866static int
1867nc_server_configure_encryption_alg(const struct lyd_node *node, NC_OPERATION op)
1868{
1869 struct nc_endpt *endpt;
1870 int ret = 0, listen = 0;
1871 const char *alg;
1872 uint8_t i;
1873
1874 /* get the algorithm name and compare it with algs supported by libssh */
1875 alg = ((struct lyd_node_term *)node)->value.ident->name;
1876
1877 if (equal_parent_name(node, 6, "listen")) {
1878 listen = 1;
1879 if (nc_server_get_endpt(node, &endpt, NULL)) {
1880 ret = 1;
1881 goto cleanup;
1882 }
1883 }
1884
1885 i = 0;
1886 while (supported_encryption_algs[i]) {
1887 if (!strcmp(supported_encryption_algs[i], alg)) {
1888 if (listen) {
1889 if (nc_server_configure_transport_params(alg, &endpt->opts.ssh->encryption_algs, op)) {
1890 ret = 1;
1891 goto cleanup;
1892 }
1893 }
1894 break;
1895 }
1896 i++;
1897 }
1898 if (!supported_encryption_algs[i]) {
1899 /* algorithm not supported */
1900 ERR(NULL, "Encryption algorithm (%s) not supported by libssh.", alg);
1901 ret = 1;
1902 }
1903
1904cleanup:
1905 return ret;
1906}
1907
1908/* leaf-list */
1909static int
1910nc_server_configure_mac_alg(const struct lyd_node *node, NC_OPERATION op)
1911{
1912 struct nc_endpt *endpt;
1913 int ret = 0, listen = 0;
1914 const char *alg;
1915 uint8_t i;
1916
1917 /* get the algorithm name and compare it with algs supported by libssh */
1918 alg = ((struct lyd_node_term *)node)->value.ident->name;
1919
1920 if (equal_parent_name(node, 6, "listen")) {
1921 listen = 1;
1922 if (nc_server_get_endpt(node, &endpt, NULL)) {
1923 ret = 1;
1924 goto cleanup;
1925 }
1926 }
1927
1928 i = 0;
1929 while (supported_mac_algs[i]) {
1930 if (!strcmp(supported_mac_algs[i], alg)) {
1931 if (listen) {
1932 if (nc_server_configure_transport_params(alg, &endpt->opts.ssh->mac_algs, op)) {
1933 ret = 1;
1934 goto cleanup;
1935 }
1936 }
1937 break;
1938 }
1939 i++;
1940 }
1941 if (!supported_mac_algs[i]) {
1942 /* algorithm not supported */
1943 ERR(NULL, "MAC algorithm (%s) not supported by libssh.", alg);
1944 ret = 1;
1945 }
1946
1947cleanup:
1948 return ret;
1949}
1950
1951static int
roman83683fb2023-02-24 09:15:23 +01001952nc_server_create_unix_socket(struct nc_endpt *endpt)
1953{
1954 endpt->ti = NC_TI_UNIX;
1955 endpt->opts.unixsock = calloc(1, sizeof *endpt->opts.unixsock);
1956 if (!endpt->opts.unixsock) {
1957 ERRMEM;
1958 return 1;
1959 }
1960
1961 /* set default values */
1962 endpt->opts.unixsock->mode = -1;
1963 endpt->opts.unixsock->uid = -1;
1964 endpt->opts.unixsock->gid = -1;
1965
1966 return 0;
1967}
1968
1969static int
1970nc_server_configure_unix_socket(const struct lyd_node *node, NC_OPERATION op)
1971{
1972 int ret = 0;
1973 uint32_t prev_lo;
1974 struct nc_endpt *endpt;
1975 struct nc_bind *bind;
1976 struct nc_server_unix_opts *opts;
1977 struct lyd_node *data = NULL;
1978
1979 assert(!strcmp(LYD_NAME(node), "unix-socket"));
1980
1981 if (nc_server_get_endpt(node, &endpt, &bind)) {
1982 ret = 1;
1983 goto cleanup;
1984 }
1985
1986 if (op == NC_OP_CREATE) {
1987 if (nc_server_create_unix_socket(endpt)) {
1988 ret = 1;
1989 goto cleanup;
1990 }
1991
1992 opts = endpt->opts.unixsock;
1993
1994 lyd_find_path(node, "path", 0, &data);
1995 assert(data);
1996
1997 opts->address = strdup(lyd_get_value(data));
1998 bind->address = strdup(lyd_get_value(data));
1999 if (!opts->address || !bind->address) {
2000 ERRMEM;
2001 ret = 1;
2002 goto cleanup;
2003 }
2004
2005 /* silently search for non-mandatory parameters */
2006 prev_lo = ly_log_options(0);
2007 ret = lyd_find_path(node, "mode", 0, &data);
2008 if (!ret) {
2009 opts->mode = strtol(lyd_get_value(data), NULL, 8);
2010 }
2011
2012 ret = lyd_find_path(node, "uid", 0, &data);
2013 if (!ret) {
2014 opts->uid = strtol(lyd_get_value(data), NULL, 10);
2015 }
2016
2017 ret = lyd_find_path(node, "gid", 0, &data);
2018 if (!ret) {
2019 opts->gid = strtol(lyd_get_value(data), NULL, 10);
2020 }
2021
2022 /* reset the logging options */
2023 ly_log_options(prev_lo);
2024
2025 ret = nc_server_config_set_address_port(endpt, bind, NULL, 0);
2026 if (ret) {
2027 goto cleanup;
2028 }
2029 } else if (op == NC_OP_DELETE) {
2030 nc_server_del_unix_socket(bind, endpt->opts.unixsock);
2031 }
2032
2033cleanup:
2034 return ret;
2035}
2036
2037static int
romanc1d2b092023-02-02 08:58:27 +01002038nc_server_configure(const struct lyd_node *node, NC_OPERATION op)
2039{
2040 const char *name = LYD_NAME(node);
2041
2042 if (!strcmp(name, "listen")) {
2043 if (nc_server_configure_listen(op)) {
2044 goto error;
2045 }
2046 } else if (!strcmp(name, "idle-timeout")) {
2047 if (nc_server_configure_idle_timeout(node, op)) {
2048 goto error;
2049 }
2050 } else if (!strcmp(name, "endpoint")) {
2051 if (nc_server_configure_endpoint(node, op)) {
2052 goto error;
2053 }
2054 } else if (!strcmp(name, "ssh")) {
2055 if (nc_server_configure_ssh(node, op)) {
2056 goto error;
2057 }
2058 } else if (!strcmp(name, "local-address")) {
2059 if (nc_server_configure_local_address(node, op)) {
2060 goto error;
2061 }
2062 } else if (!strcmp(name, "local-port")) {
2063 if (nc_server_configure_local_port(node, op)) {
2064 goto error;
2065 }
2066 } else if (!strcmp(name, "keepalives")) {
2067 if (nc_server_configure_keepalives(node, op)) {
2068 goto error;
2069 }
2070 } else if (!strcmp(name, "idle-time")) {
2071 if (nc_server_configure_idle_time(node, op)) {
2072 goto error;
2073 }
2074 } else if (!strcmp(name, "max-probes")) {
2075 if (nc_server_configure_max_probes(node, op)) {
2076 goto error;
2077 }
2078 } else if (!strcmp(name, "probe-interval")) {
2079 if (nc_server_configure_probe_interval(node, op)) {
2080 goto error;
2081 }
2082 } else if (!strcmp(name, "host-key")) {
2083 if (nc_server_configure_host_key(node, op)) {
2084 goto error;
2085 }
2086 } else if (!strcmp(name, "public-key-format")) {
2087 if (nc_server_configure_public_key_format(node, op)) {
2088 goto error;
2089 }
2090 } else if (!strcmp(name, "public-key")) {
2091 if (nc_server_configure_public_key(node, op)) {
2092 goto error;
2093 }
2094 } else if (!strcmp(name, "private-key-format")) {
2095 if (nc_server_configure_private_key_format(node, op)) {
2096 goto error;
2097 }
2098 } else if (!strcmp(name, "cleartext-private-key")) {
2099 if (nc_server_configure_cleartext_private_key(node, op)) {
2100 goto error;
2101 }
2102 } else if (!strcmp(name, "keystore-reference")) {
2103 if (nc_server_configure_keystore_reference(node, op)) {
2104 goto error;
2105 }
2106 } else if (!strcmp(name, "user")) {
2107 if (nc_server_configure_user(node, op)) {
2108 goto error;
2109 }
2110 } else if (!strcmp(name, "auth-attempts")) {
2111 if (nc_server_configure_auth_attempts(node, op)) {
2112 goto error;
2113 }
2114 } else if (!strcmp(name, "auth-timeout")) {
2115 if (nc_server_configure_auth_timeout(node, op)) {
2116 goto error;
2117 }
2118 } else if (!strcmp(name, "truststore-reference")) {
2119 if (nc_server_configure_truststore_reference(node, op)) {
2120 goto error;
2121 }
2122 } else if (!strcmp(name, "password")) {
2123 if (nc_server_configure_password(node, op)) {
2124 goto error;
2125 }
2126 } else if (!strcmp(name, "pam-config-file-name")) {
2127 if (nc_server_configure_pam_name(node, op)) {
2128 goto error;
2129 }
2130 } else if (!strcmp(name, "pam-config-file-dir")) {
2131 if (nc_server_configure_pam_dir(node, op)) {
2132 goto error;
2133 }
2134 } else if (!strcmp(name, "none")) {
2135 if (nc_server_configure_none(node, op)) {
2136 goto error;
2137 }
2138 } else if (!strcmp(name, "host-key-alg")) {
2139 if (nc_server_configure_host_key_alg(node, op)) {
2140 goto error;
2141 }
2142 } else if (!strcmp(name, "key-exchange-alg")) {
2143 if (nc_server_configure_kex_alg(node, op)) {
2144 goto error;
2145 }
2146 } else if (!strcmp(name, "encryption-alg")) {
2147 if (nc_server_configure_encryption_alg(node, op)) {
2148 goto error;
2149 }
2150 } else if (!strcmp(name, "mac-alg")) {
2151 if (nc_server_configure_mac_alg(node, op)) {
2152 goto error;
2153 }
roman83683fb2023-02-24 09:15:23 +01002154 } else if (!strcmp(name, "unix-socket")) {
2155 if (nc_server_configure_unix_socket(node, op)) {
2156 goto error;
2157 }
romanc1d2b092023-02-02 08:58:27 +01002158 } 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,
2159 "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,
2160 "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,
2161 "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,
2162 "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,
2163 "id")) {} else if (!strcmp(name, "fingerprint")) {} else if (!strcmp(name, "map-type")) {}
2164
2165 return 0;
2166
2167error:
2168 ERR(NULL, "Configuring (%s) failed.", LYD_NAME(node));
2169 return 1;
2170}
2171
2172int
2173nc_session_server_parse_tree(const struct lyd_node *node, NC_OPERATION parent_op)
2174{
2175 struct lyd_node *child;
2176 struct lyd_meta *m;
2177 NC_OPERATION current_op;
2178
2179 assert(node);
2180
2181 /* get current op */
2182 LY_LIST_FOR(node->meta, m) {
2183 if (!strcmp(m->name, "operation")) {
2184 if (!strcmp(lyd_get_meta_value(m), "create")) {
2185 current_op = NC_OP_CREATE;
2186 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
2187 current_op = NC_OP_DELETE;
2188 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
2189 current_op = NC_OP_REPLACE;
2190 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
2191 current_op = NC_OP_NONE;
2192 }
2193 break;
2194 }
2195 }
2196
2197 /* node has no op, inherit from the parent */
2198 if (!m) {
2199 current_op = parent_op;
2200 }
2201
2202 switch (current_op) {
2203 case NC_OP_NONE:
2204 break;
2205 case NC_OP_CREATE:
2206 case NC_OP_DELETE:
2207 case NC_OP_REPLACE:
2208 if (nc_server_configure(node, current_op)) {
2209 return 1;
2210 }
2211 break;
2212 default:
2213 break;
2214 }
2215
2216 if (current_op != NC_OP_DELETE) {
2217 LY_LIST_FOR(lyd_child(node), child) {
2218 if (nc_session_server_parse_tree(child, current_op)) {
2219 return 1;
2220 }
2221 }
2222 }
2223 return 0;
2224}
2225
2226static int
roman45cec4e2023-02-17 10:21:39 +01002227nc_server_configure_asymmetric_key_certificate(const struct lyd_node *tree, struct nc_ks_asym_key *key)
romanc1d2b092023-02-02 08:58:27 +01002228{
2229 int ret = 0;
roman45cec4e2023-02-17 10:21:39 +01002230 struct lyd_node *node;
romanc1d2b092023-02-02 08:58:27 +01002231 void *tmp;
2232
roman45cec4e2023-02-17 10:21:39 +01002233 /* create new certificate */
2234 tmp = realloc(key->certs, (key->cert_count + 1) * sizeof *key->certs);
2235 if (!tmp) {
2236 ERRMEM;
2237 ret = 1;
2238 goto cleanup;
2239 }
2240 key->certs = tmp;
2241 key->cert_count++;
2242
2243 /* set name */
2244 lyd_find_path(tree, "name", 0, &node);
2245 assert(node);
2246
2247 key->certs[key->cert_count - 1].name = strdup(lyd_get_value(node));
2248 if (!key->certs[key->cert_count - 1].name) {
2249 ERRMEM;
2250 ret = 1;
romanc1d2b092023-02-02 08:58:27 +01002251 goto cleanup;
2252 }
2253
roman45cec4e2023-02-17 10:21:39 +01002254 /* set certificate data */
2255 lyd_find_path(tree, "cert-data", 0, &node);
2256 assert(node);
romanc1d2b092023-02-02 08:58:27 +01002257
roman45cec4e2023-02-17 10:21:39 +01002258 key->certs[key->cert_count - 1].cert_base64 = strdup(lyd_get_value(node));
2259 if (!key->certs[key->cert_count - 1].cert_base64) {
2260 ERRMEM;
2261 ret = 1;
2262 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002263 }
2264
2265cleanup:
roman45cec4e2023-02-17 10:21:39 +01002266 return ret;
2267}
2268
2269static int
2270nc_server_configure_asymmetric_key(const struct lyd_node *tree)
2271{
2272 int ret = 0;
2273 struct lyd_node *node = NULL, *iter;
2274 void *tmp;
2275 struct nc_keystore *ks = &server_opts.keystore;
2276 struct nc_ks_asym_key *key;
2277 const char *format;
2278
2279 /* create new asymmetric key */
2280 tmp = realloc(ks->asym_keys, (ks->asym_key_count + 1) * sizeof *ks->asym_keys);
2281 if (!tmp) {
2282 ERRMEM;
2283 ret = 1;
2284 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002285 }
roman45cec4e2023-02-17 10:21:39 +01002286 ks->asym_keys = tmp;
2287 memset(&ks->asym_keys[ks->asym_key_count], 0, sizeof *ks->asym_keys);
2288 key = &ks->asym_keys[ks->asym_key_count];
2289 ks->asym_key_count++;
2290
2291 /* set name */
2292 lyd_find_path(tree, "name", 0, &node);
2293 assert(node);
2294
2295 key->name = strdup(lyd_get_value(node));
2296 if (!key->name) {
2297 ERRMEM;
2298 ret = 1;
2299 goto cleanup;
2300 }
2301
2302 /* set public-key-format, mandatory */
2303 lyd_find_path(tree, "public-key-format", 0, &node);
2304 assert(node);
2305
2306 format = ((struct lyd_node_term *)node)->value.ident->name;
2307 if (!strcmp(format, "ssh-public-key-format")) {
2308 key->pubkey_type = NC_SSH_PUBKEY_X509;
2309 } else if (!strcmp(format, "subject-public-key-info-format")) {
2310 key->pubkey_type = NC_SSH_PUBKEY_SSH2;
2311 } else {
2312 ERR(NULL, "Public key format \"%s\" not supported.", format);
2313 ret = 1;
2314 goto cleanup;
2315 }
2316
2317 /* set public-key, mandatory */
2318 lyd_find_path(tree, "public-key", 0, &node);
2319 assert(node);
2320
2321 key->pub_base64 = strdup(lyd_get_value(node));
2322 if (!key->pub_base64) {
2323 ERRMEM;
2324 ret = 1;
2325 goto cleanup;
2326 }
2327
2328 /* set private-key-format */
2329 ret = lyd_find_path(tree, "private-key-format", 0, &node);
2330 if (!ret) {
2331 format = ((struct lyd_node_term *)node)->value.ident->name;
2332 if (!strcmp(format, "rsa-private-key-format")) {
2333 key->privkey_type = NC_SSH_KEY_RSA;
2334 } else if (!strcmp(format, "ec-private-key-format")) {
2335 key->privkey_type = NC_SSH_KEY_ECDSA;
2336 } else {
2337 ERR(NULL, "Private key format (%s) not supported.", format);
2338 ret = 1;
2339 goto cleanup;
2340 }
2341 }
2342
2343 /* set private key, mandatory */
2344 lyd_find_path(tree, "cleartext-private-key", 0, &node);
2345 assert(node);
2346
2347 key->priv_base64 = strdup(lyd_get_value(node));
2348 if (!key->priv_base64) {
2349 ERRMEM;
2350 ret = 1;
2351 goto cleanup;
2352 }
2353
2354 /* set certificates associated with the key pair */
2355 ret = lyd_find_path(tree, "certificates", 0, &node);
2356 if (!ret) {
2357 node = lyd_child(node);
2358 if (node) {
2359 /* certificate list instance */
2360 LY_LIST_FOR(node, iter) {
2361 if (nc_server_configure_asymmetric_key_certificate(iter, key)) {
2362 ret = 1;
2363 goto cleanup;
2364 }
2365 }
2366 }
2367 } else if (ret == LY_ENOTFOUND) {
2368 /* certificates container not present, but it's ok */
2369 ret = 0;
2370 }
2371
2372cleanup:
2373 return ret;
2374}
2375
2376static int
2377nc_server_configure_symmetric_key(const struct lyd_node *tree)
2378{
2379 int ret = 0;
2380 const char *format;
2381 struct lyd_node *node;
2382 struct nc_keystore *ks = &server_opts.keystore;
2383 struct nc_ks_sym_key *key;
2384 void *tmp;
2385
2386 /* create new symmetric key */
2387 tmp = realloc(ks->sym_keys, (ks->sym_key_count + 1) * sizeof *ks->sym_keys);
2388 if (tmp) {
2389 ERRMEM;
2390 ret = 1;
2391 goto cleanup;
2392 }
2393 memset(&ks->sym_keys[ks->sym_key_count], 0, sizeof *ks->sym_keys);
2394 ks->sym_keys = tmp;
2395 key = &ks->sym_keys[ks->sym_key_count];
2396 ks->sym_key_count++;
2397
2398 /* set name */
2399 lyd_find_path(tree, "name", 0, &node);
2400 assert(node);
2401
2402 key->name = strdup(lyd_get_value(node));
2403 if (!key->name) {
2404 ERRMEM;
2405 ret = 1;
2406 goto cleanup;
2407 }
2408
2409 /* check if the identity matches with the supported one */
2410 lyd_find_path(tree, "key-format", 0, &node);
2411 assert(node);
2412
2413 format = ((struct lyd_node_term *)node)->value.ident->name;
2414 if (strcmp(format, "symmetric-key-format")) {
2415 ret = 1;
2416 goto cleanup;
2417 }
2418
2419 /* set key data */
2420 lyd_find_path(tree, "cleartext-key", 0, &node);
2421 assert(node);
2422
2423 key->base64 = strdup(lyd_get_value(node));
2424 if (!key->base64) {
2425 ERRMEM;
2426 ret = 1;
2427 goto cleanup;
2428 }
2429
2430cleanup:
romanc1d2b092023-02-02 08:58:27 +01002431 return ret;
2432}
2433
2434static int
2435nc_fill_keystore(const struct lyd_node *data)
2436{
2437 int ret = 0;
2438 uint32_t prev_lo;
roman45cec4e2023-02-17 10:21:39 +01002439 struct lyd_node *tree, *as_keys, *s_keys, *iter;
romanc1d2b092023-02-02 08:58:27 +01002440
roman45cec4e2023-02-17 10:21:39 +01002441 /* silently search for nodes, some of them may not be present */
romanc1d2b092023-02-02 08:58:27 +01002442 prev_lo = ly_log_options(0);
roman45cec4e2023-02-17 10:21:39 +01002443
2444 ret = lyd_find_path(data, "/ietf-keystore:keystore", 0, &tree);
romanc1d2b092023-02-02 08:58:27 +01002445 if (ret) {
2446 WRN(NULL, "Keystore container not found in the YANG data.");
roman45cec4e2023-02-17 10:21:39 +01002447 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002448 }
2449
roman45cec4e2023-02-17 10:21:39 +01002450 ret = lyd_find_path(tree, "asymmetric-keys", 0, &as_keys);
2451 if (!ret) {
2452 /* asymmetric keys container is present */
2453 as_keys = lyd_child(as_keys);
2454 if (as_keys && !strcmp(LYD_NAME(as_keys), "asymmetric-key")) {
2455 /* asymmetric key list */
2456 LY_LIST_FOR(as_keys, iter) {
2457 if (nc_server_configure_asymmetric_key(iter)) {
2458 ret = 1;
2459 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002460 }
2461 }
romanc1d2b092023-02-02 08:58:27 +01002462 }
romanc1d2b092023-02-02 08:58:27 +01002463 }
2464
roman45cec4e2023-02-17 10:21:39 +01002465 ret = lyd_find_path(tree, "symmetric-keys", 0, &s_keys);
2466 if (!ret) {
2467 /* symmetric keys container is present */
2468 s_keys = lyd_child(s_keys);
2469 if (s_keys && !strcmp(LYD_NAME(s_keys), "symmetric-key")) {
2470 /* symmetric key list */
2471 LY_LIST_FOR(s_keys, iter) {
2472 if (nc_server_configure_symmetric_key(iter)) {
2473 ret = 1;
2474 goto cleanup;
2475 }
2476 }
2477 }
2478 }
romanc1d2b092023-02-02 08:58:27 +01002479
roman45cec4e2023-02-17 10:21:39 +01002480cleanup:
2481 /* reset the logging options back to what they were */
2482 ly_log_options(prev_lo);
2483 return ret;
romanc1d2b092023-02-02 08:58:27 +01002484}
2485
2486API int
2487nc_server_config_load_modules(struct ly_ctx **ctx)
2488{
2489 int i, new_ctx = 0;
2490
2491 if (!*ctx) {
2492 if (ly_ctx_new(NC_SERVER_SEARCH_DIR, 0, ctx)) {
2493 ERR(NULL, "Couldn't create new libyang context.\n");
2494 goto error;
2495 }
2496 new_ctx = 1;
2497 }
2498
2499 /* all features */
2500 const char *ietf_nectonf_server[] = {"ssh-listen", "tls-listen", "ssh-call-home", "tls-call-home", "central-netconf-server-supported", NULL};
2501 /* all features */
2502 const char *ietf_x509_cert_to_name[] = {NULL};
2503 /* no private-key-encryption and csr-generation */
2504 const char *ietf_crypto_types[] = {
2505 "one-symmetric-key-format", "one-asymmetric-key-format", "symmetrically-encrypted-value-format",
2506 "asymmetrically-encrypted-value-format", "cms-enveloped-data-format", "cms-encrypted-data-format",
2507 "p10-based-csrs", "certificate-expiration-notification", "hidden-keys", "password-encryption",
2508 "symmetric-key-encryption", NULL
2509 };
2510 /* all features */
2511 const char *ietf_tcp_common[] = {"keepalives-supported", NULL};
2512 /* no ssh-x509-certs */
2513 const char *ietf_ssh_common[] = {"transport-params", "public-key-generation", NULL};
2514 /* all features */
2515 const char *iana_ssh_encryption_algs[] = {NULL};
2516 /* all features */
2517 const char *iana_ssh_key_exchange_algs[] = {NULL};
2518 /* all features */
2519 const char *iana_ssh_mac_algs[] = {NULL};
2520 /* all features */
2521 const char *iana_ssh_public_key_algs[] = {NULL};
2522 /* all features */
2523 const char *ietf_keystore[] = {"central-keystore-supported", "local-definitions-supported", "asymmetric-keys", "symmetric-keys", NULL};
2524 /* no ssh-server-keepalives and local-user-auth-hostbased */
2525 const char *ietf_ssh_server[] = {"local-users-supported", "local-user-auth-publickey", "local-user-auth-password", "local-user-auth-none", NULL};
2526 /* all features */
2527 const char *ietf_truststore[] = {"central-truststore-supported", "local-definitions-supported", "certificates", "public-keys", NULL};
2528 /* all features */
2529 const char *ietf_tls_server[] = {
2530 "tls-server-keepalives", "server-ident-x509-cert", "server-ident-raw-public-key", "server-ident-tls12-psk",
2531 "server-ident-tls13-epsk", "client-auth-supported", "client-auth-x509-cert", "client-auth-raw-public-key",
2532 "client-auth-tls12-psk", "client-auth-tls13-epsk", NULL
2533 };
2534 /* all features */
2535 const char *libnetconf2_netconf_server[] = {NULL};
2536
2537 const char *module_names[] = {
2538 "ietf-netconf-server", "ietf-x509-cert-to-name", "ietf-crypto-types",
2539 "ietf-tcp-common", "ietf-ssh-common", "iana-ssh-encryption-algs",
2540 "iana-ssh-key-exchange-algs", "iana-ssh-mac-algs", "iana-ssh-public-key-algs",
2541 "ietf-keystore", "ietf-ssh-server", "ietf-truststore",
2542 "ietf-tls-server", "libnetconf2-netconf-server", NULL
2543 };
2544
2545 const char **module_features[] = {
2546 ietf_nectonf_server, ietf_x509_cert_to_name, ietf_crypto_types,
2547 ietf_tcp_common, ietf_ssh_common, iana_ssh_encryption_algs,
2548 iana_ssh_key_exchange_algs, iana_ssh_mac_algs, iana_ssh_public_key_algs,
2549 ietf_keystore, ietf_ssh_server, ietf_truststore,
2550 ietf_tls_server, libnetconf2_netconf_server, NULL
2551 };
2552
2553 for (i = 0; module_names[i] != NULL; i++) {
2554 if (!ly_ctx_load_module(*ctx, module_names[i], NULL, module_features[i])) {
2555 ERR(NULL, "Loading module \"%s\" failed.\n", module_names[i]);
2556 goto error;
2557 }
2558 }
2559
2560 return 0;
2561
2562error:
2563 if (new_ctx) {
2564 ly_ctx_destroy(*ctx);
2565 *ctx = NULL;
2566 }
2567 return 1;
2568}
2569
2570API int
2571nc_server_config_setup_path(const struct ly_ctx *ctx, const char *path)
2572{
2573 struct lyd_node *tree = NULL;
2574 int ret = 0;
2575
2576 if (!path) {
2577 ERRARG("Missing path parameter.");
2578 ret = 1;
2579 goto cleanup;
2580 }
2581
2582 ret = lyd_parse_data_path(ctx, path, LYD_XML, LYD_PARSE_NO_STATE | LYD_PARSE_STRICT, LYD_VALIDATE_NO_STATE, &tree);
2583 if (ret) {
2584 goto cleanup;
2585 }
2586
2587 ret = nc_server_config_setup(tree);
2588 if (ret) {
2589 goto cleanup;
2590 }
2591
2592cleanup:
2593 lyd_free_all(tree);
2594 return ret;
2595}
2596
2597API int
2598nc_server_config_setup(const struct lyd_node *data)
2599{
2600 int ret = 0;
2601 struct lyd_node *tree;
2602 struct lyd_meta *m;
2603 NC_OPERATION op;
2604
2605 /* LOCK */
2606 pthread_rwlock_wrlock(&server_opts.config_lock);
2607
2608 ret = nc_fill_keystore(data);
2609 if (ret) {
2610 ERR(NULL, "Filling keystore failed.");
2611 goto cleanup;
2612 }
2613
2614 ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &tree);
2615 if (ret) {
2616 ERR(NULL, "Unable to find the netconf-server container in the YANG data.");
2617 goto cleanup;
2618 }
2619
2620 LY_LIST_FOR(tree->meta, m) {
2621 if (!strcmp(m->name, "operation")) {
2622 if (!strcmp(lyd_get_meta_value(m), "create")) {
2623 op = NC_OP_CREATE;
2624 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
2625 op = NC_OP_DELETE;
2626 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
2627 op = NC_OP_REPLACE;
2628 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
2629 op = NC_OP_NONE;
2630 } else {
2631 ERR(NULL, "Unexpected operation (%s).", lyd_get_meta_value(m));
2632 ret = 1;
2633 goto cleanup;
2634 }
2635 }
2636 }
2637
2638 if (nc_session_server_parse_tree(tree, op)) {
2639 ret = 1;
2640 goto cleanup;
2641 }
2642
2643cleanup:
2644 /* UNLOCK */
2645 pthread_rwlock_unlock(&server_opts.config_lock);
2646 return ret;
2647}