blob: 929209e84fc8155d4302a07f72da29ce0f1a0af7 [file] [log] [blame]
romanc1d2b092023-02-02 08:58:27 +01001/**
romane028ef92023-02-24 16:33:08 +01002 * @file server_config.c
romanc1d2b092023-02-02 08:58:27 +01003 * @author Roman Janota <janota@cesnet.cz>
4 * @brief libnetconf2 server configuration functions
5 *
6 * @copyright
7 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
8 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
roman27215242023-03-10 14:55:00 +010015
16#define _GNU_SOURCE
17
romanc1d2b092023-02-02 08:58:27 +010018#include <assert.h>
19#include <stdlib.h>
20#include <string.h>
21
22#include "compat.h"
romanc1d2b092023-02-02 08:58:27 +010023#include "libnetconf.h"
romane028ef92023-02-24 16:33:08 +010024#include "server_config.h"
romanc1d2b092023-02-02 08:58:27 +010025#include "session_server.h"
26#include "session_server_ch.h"
27
28/* All libssh supported host-key, key-exchange, encryption and mac algorithms as of version 0.10.90 */
29
30static const char *supported_hostkey_algs[] = {
31 "ssh-ed25519-cert-v01@openssh.com", "ecdsa-sha2-nistp521-cert-v01@openssh.com",
32 "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ecdsa-sha2-nistp256-cert-v01@openssh.com",
33 "rsa-sha2-512-cert-v01@openssh.com", "rsa-sha2-256-cert-v01@openssh.com",
34 "ssh-rsa-cert-v01@openssh.com", "ssh-dss-cert-v01@openssh.com",
35 "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256",
36 "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa", "ssh-dss", NULL
37};
38
39static const char *supported_kex_algs[] = {
40 "diffie-hellman-group-exchange-sha1", "curve25519-sha256", "curve25519-sha256@libssh.org",
41 "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group18-sha512",
42 "diffie-hellman-group16-sha512", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group14-sha256", NULL
43};
44
45static const char *supported_encryption_algs[] = {
46 "chacha20-poly1305@openssh.com", "aes256-gcm@openssh.com", "aes128-gcm@openssh.com",
47 "aes256-ctr", "aes192-ctr", "aes128-ctr", "aes256-cbc", "aes192-cbc", "aes128-cbc",
roman27215242023-03-10 14:55:00 +010048 "blowfish-cbc", "triple-des-cbc", "none", NULL
romanc1d2b092023-02-02 08:58:27 +010049};
50
51static const char *supported_mac_algs[] = {
52 "hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "hmac-sha1-etm@openssh.com",
53 "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", NULL
54};
55
56extern struct nc_server_opts server_opts;
57
58/**
59 * @brief Get the pointer to an endpoint structure based on node's location in the YANG data.
60 *
61 * @param[in] node Node from which the endpoint containing this node is derived.
62 * @param[out] endpt Endpoint containing the node.
63 * @param[out] bind Bind corresponding to the endpoint. Optional.
64 * @return 0 on success, 1 on error.
65 */
66static int
67nc_server_get_endpt(const struct lyd_node *node, struct nc_endpt **endpt, struct nc_bind **bind)
68{
69 uint16_t i;
70 const char *endpt_name;
71
72 assert(node);
73
74 while (node) {
75 if (!strcmp(LYD_NAME(node), "endpoint")) {
76 break;
77 }
78 node = lyd_parent(node);
79 }
80
81 if (!node) {
82 ERR(NULL, "Node \"%s\" is not contained in an endpoint subtree.", LYD_NAME(node));
83 return 1;
84 }
85
86 node = lyd_child(node);
87 assert(!strcmp(LYD_NAME(node), "name"));
88 endpt_name = lyd_get_value(node);
89
90 for (i = 0; i < server_opts.endpt_count; i++) {
91 if (!strcmp(server_opts.endpts[i].name, endpt_name)) {
92 *endpt = &server_opts.endpts[i];
93 if (bind) {
94 *bind = &server_opts.binds[i];
95 }
96 return 0;
97 }
98 }
99
100 ERR(NULL, "Endpoint \"%s\" was not found.", endpt_name);
101 return 1;
102}
103
104/**
105 * @brief Get the pointer to a hostkey structure based on node's location in the YANG data.
106 *
107 * @param[in] node Node from which the hotkey containing this node is derived.
108 * @param[in] opts Server SSH opts storing the array of the hostkey structures.
109 * @param[out] hostkey Hostkey containing the node.
110 * @return 0 on success, 1 on error.
111 */
112static int
113nc_server_get_hostkey(const struct lyd_node *node, const struct nc_server_ssh_opts *opts, struct nc_hostkey **hostkey)
114{
115 uint16_t i;
116 const char *hostkey_name;
117
118 assert(node && opts);
119
120 while (node) {
121 if (!strcmp(LYD_NAME(node), "host-key")) {
122 break;
123 }
124 node = lyd_parent(node);
125 }
126
127 if (!node) {
128 ERR(NULL, "Node \"%s\" is not contained in a host-key subtree.", LYD_NAME(node));
129 return 1;
130 }
131
132 node = lyd_child(node);
133 assert(!strcmp(LYD_NAME(node), "name"));
134 hostkey_name = lyd_get_value(node);
135
136 for (i = 0; i < opts->hostkey_count; i++) {
137 if (!strcmp(opts->hostkeys[i].name, hostkey_name)) {
138 *hostkey = &opts->hostkeys[i];
139 return 0;
140 }
141 }
142
143 ERR(NULL, "Host-key \"%s\" was not found.", hostkey_name);
144 return 1;
145}
146
147/**
148 * @brief Get the pointer to a client authentication structure based on node's location in the YANG data.
149 *
150 * @param[in] node Node from which the client-authentication structure containing this node is derived.
151 * @param[in] opts Server SSH opts storing the array of the client authentication structures.
152 * @param[out] auth_client Client authentication structure containing the node.
153 * @return 0 on success, 1 on error.
154 */
155static int
156nc_server_get_auth_client(const struct lyd_node *node, const struct nc_server_ssh_opts *opts, struct nc_client_auth **auth_client)
157{
158 uint16_t i;
159 const char *authkey_name;
160
161 assert(node && opts);
162
163 while (node) {
164 if (!strcmp(LYD_NAME(node), "user")) {
165 break;
166 }
167 node = lyd_parent(node);
168 }
169
170 if (!node) {
171 ERR(NULL, "Node \"%s\" is not contained in a client-authentication subtree.", LYD_NAME(node));
172 return 1;
173 }
174
175 node = lyd_child(node);
176 assert(!strcmp(LYD_NAME(node), "name"));
177 authkey_name = lyd_get_value(node);
178
179 for (i = 0; i < opts->client_count; i++) {
180 if (!strcmp(opts->auth_clients[i].username, authkey_name)) {
181 *auth_client = &opts->auth_clients[i];
182 return 0;
183 }
184 }
185
186 ERR(NULL, "Authorized key \"%s\" was not found.", authkey_name);
187 return 1;
188}
189
190/**
191 * @brief Get the pointer to a client authentication public key structure based on node's location in the YANG data.
192 *
193 * @param[in] node Node from which the ca-public key structure containing this node is derived.
194 * @param[in] auth_client Client authentication structure storing the array of the public key structures.
195 * @param[out] pubkey Public key structure containing the node.
196 * @return 0 on success, 1 on error.
197 */
198static int
roman8edee342023-03-31 13:25:48 +0200199nc_server_get_pubkey(const struct lyd_node *node, const struct nc_client_auth *auth_client, struct nc_public_key **pubkey)
romanc1d2b092023-02-02 08:58:27 +0100200{
201 uint16_t i;
202 const char *pubkey_name;
203
204 assert(node && auth_client);
205
206 node = lyd_parent(node);
207 while (node) {
208 if (!strcmp(LYD_NAME(node), "public-key")) {
209 break;
210 }
211 node = lyd_parent(node);
212 }
213
214 if (!node) {
215 ERR(NULL, "Node \"%s\" is not contained in a public-key subtree.", LYD_NAME(node));
216 return 1;
217 }
218
219 node = lyd_child(node);
220 assert(!strcmp(LYD_NAME(node), "name"));
221 pubkey_name = lyd_get_value(node);
222
223 for (i = 0; i < auth_client->pubkey_count; i++) {
224 if (!strcmp(auth_client->pubkeys[i].name, pubkey_name)) {
225 *pubkey = &auth_client->pubkeys[i];
226 return 0;
227 }
228 }
229
230 ERR(NULL, "Public key \"%s\" was not found.", pubkey_name);
231 return 1;
232}
233
234/**
235 * @brief Compares the nth-parent name.
236 *
237 * @param[in] node Node of which nth-parent to compare.
238 * @param[in] parent_count Count of parents.
239 * @param[in] parent_name Expected name of the parent.
240 * @return 1 if the name matches, 0 otherwise.
241 */
242static int
243equal_parent_name(const struct lyd_node *node, uint16_t parent_count, const char *parent_name)
244{
245 uint16_t i;
246
247 assert(node && parent_count > 0 && parent_name);
248
249 node = lyd_parent(node);
250 for (i = 1; i < parent_count; i++) {
251 node = lyd_parent(node);
252 }
253
254 if (!strcmp(LYD_NAME(node), parent_name)) {
255 return 1;
256 }
257
258 return 0;
259}
260
261static void
262nc_server_del_auth_client_pam_name(struct nc_client_auth *auth_client)
263{
264 free(auth_client->pam_config_name);
265 auth_client->pam_config_name = NULL;
266}
267
268static void
269nc_server_del_auth_client_pam_dir(struct nc_client_auth *auth_client)
270{
271 free(auth_client->pam_config_dir);
272 auth_client->pam_config_dir = NULL;
273}
274
275static void
276nc_server_del_endpt_name(struct nc_endpt *endpt)
277{
278 free(endpt->name);
279 endpt->name = NULL;
280}
281
282static void
283nc_server_del_local_address(struct nc_bind *bind)
284{
285 free(bind->address);
286 bind->address = NULL;
287}
288
289static void
290nc_server_del_hostkey_name(struct nc_hostkey *hostkey)
291{
292 free(hostkey->name);
293 hostkey->name = NULL;
294}
295
296static void
297nc_server_del_public_key(struct nc_hostkey *hostkey)
298{
roman8edee342023-03-31 13:25:48 +0200299 free(hostkey->key.pub_base64);
300 hostkey->key.pub_base64 = NULL;
romanc1d2b092023-02-02 08:58:27 +0100301}
302
303static void
romanc1d2b092023-02-02 08:58:27 +0100304nc_server_del_private_key(struct nc_hostkey *hostkey)
305{
roman8edee342023-03-31 13:25:48 +0200306 free(hostkey->key.priv_base64);
307 hostkey->key.priv_base64 = NULL;
romanc1d2b092023-02-02 08:58:27 +0100308}
309
310static void
romanc1d2b092023-02-02 08:58:27 +0100311nc_server_del_auth_client_username(struct nc_client_auth *auth_client)
312{
313 free(auth_client->username);
314 auth_client->username = NULL;
315}
316
317static void
roman8edee342023-03-31 13:25:48 +0200318nc_server_del_auth_client_pubkey_name(struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100319{
320 free(pubkey->name);
321 pubkey->name = NULL;
322}
323
324static void
roman8edee342023-03-31 13:25:48 +0200325nc_server_del_auth_client_pubkey_pub_base64(struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100326{
327 free(pubkey->pub_base64);
328 pubkey->pub_base64 = NULL;
329}
330
331static void
romanc1d2b092023-02-02 08:58:27 +0100332nc_server_del_auth_client_password(struct nc_client_auth *auth_client)
333{
334 free(auth_client->password);
335 auth_client->password = NULL;
336}
337
338static void
339nc_server_del_hostkey_algs(struct nc_server_ssh_opts *opts)
340{
341 free(opts->hostkey_algs);
342 opts->hostkey_algs = NULL;
343}
344
345static void
346nc_server_del_kex_algs(struct nc_server_ssh_opts *opts)
347{
348 free(opts->kex_algs);
349 opts->kex_algs = NULL;
350}
351
352static void
353nc_server_del_encryption_algs(struct nc_server_ssh_opts *opts)
354{
355 free(opts->encryption_algs);
356 opts->encryption_algs = NULL;
357}
358
359static void
360nc_server_del_mac_algs(struct nc_server_ssh_opts *opts)
361{
362 free(opts->mac_algs);
363 opts->mac_algs = NULL;
364}
365
366static void
367nc_server_del_hostkey(struct nc_server_ssh_opts *opts, struct nc_hostkey *hostkey)
368{
369 assert(hostkey->ks_type == NC_STORE_LOCAL || hostkey->ks_type == NC_STORE_KEYSTORE);
370
371 if (hostkey->ks_type == NC_STORE_LOCAL) {
372 nc_server_del_public_key(hostkey);
373 nc_server_del_private_key(hostkey);
romanc1d2b092023-02-02 08:58:27 +0100374 }
375
376 nc_server_del_hostkey_name(hostkey);
377 opts->hostkey_count--;
378 if (!opts->hostkey_count) {
379 free(opts->hostkeys);
380 opts->hostkeys = NULL;
381 }
382}
383
384static void
roman8edee342023-03-31 13:25:48 +0200385nc_server_del_auth_client_pubkey(struct nc_client_auth *auth_client, struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100386{
387 nc_server_del_auth_client_pubkey_name(pubkey);
388 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
389
390 auth_client->pubkey_count--;
391 if (!auth_client->pubkey_count) {
392 free(auth_client->pubkeys);
393 auth_client->pubkeys = NULL;
394 }
395}
396
397static void
398nc_server_del_auth_client(struct nc_server_ssh_opts *opts, struct nc_client_auth *auth_client)
399{
400 uint16_t i, pubkey_count;
401
402 if (auth_client->ks_type == NC_STORE_LOCAL) {
403 pubkey_count = auth_client->pubkey_count;
404 for (i = 0; i < pubkey_count; i++) {
405 nc_server_del_auth_client_pubkey(auth_client, &auth_client->pubkeys[i]);
406 }
romanc1d2b092023-02-02 08:58:27 +0100407 }
408
409 nc_server_del_auth_client_password(auth_client);
410 nc_server_del_auth_client_pam_name(auth_client);
411 nc_server_del_auth_client_pam_dir(auth_client);
412 nc_server_del_auth_client_username(auth_client);
413
414 opts->client_count--;
415 if (!opts->client_count) {
416 free(opts->auth_clients);
417 opts->auth_clients = NULL;
418 }
419}
420
421static void
422nc_server_del_ssh(struct nc_bind *bind, struct nc_server_ssh_opts *opts)
423{
424 uint16_t i, hostkey_count, client_count;
425
426 nc_server_del_local_address(bind);
427 if (bind->sock > -1) {
428 close(bind->sock);
429 }
430
431 /* store in variable because it gets decremented in the function call */
432 hostkey_count = opts->hostkey_count;
433 for (i = 0; i < hostkey_count; i++) {
434 nc_server_del_hostkey(opts, &opts->hostkeys[i]);
435 }
436
437 client_count = opts->client_count;
438 for (i = 0; i < client_count; i++) {
439 nc_server_del_auth_client(opts, &opts->auth_clients[i]);
440 }
441
442 nc_server_del_hostkey_algs(opts);
443 nc_server_del_kex_algs(opts);
444 nc_server_del_encryption_algs(opts);
445 nc_server_del_mac_algs(opts);
446
447 free(opts);
448 opts = NULL;
449}
450
451void
452nc_server_del_endpt_ssh(struct nc_endpt *endpt, struct nc_bind *bind)
453{
454 nc_server_del_endpt_name(endpt);
455 nc_server_del_ssh(bind, endpt->opts.ssh);
456
457 server_opts.endpt_count--;
458 if (!server_opts.endpt_count) {
459 free(server_opts.endpts);
460 free(server_opts.binds);
461 server_opts.endpts = NULL;
462 server_opts.binds = NULL;
463 }
464}
465
roman45cec4e2023-02-17 10:21:39 +0100466void
roman83683fb2023-02-24 09:15:23 +0100467nc_server_del_unix_socket(struct nc_bind *bind, struct nc_server_unix_opts *opts)
468{
469 if (bind->sock > -1) {
470 close(bind->sock);
471 }
472
473 free(bind->address);
474 free(opts->address);
475
476 free(opts);
477 opts = NULL;
478}
479
480void
481nc_server_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *bind)
482{
483 nc_server_del_endpt_name(endpt);
484 nc_server_del_unix_socket(bind, endpt->opts.unixsock);
485
486 server_opts.endpt_count--;
487 if (!server_opts.endpt_count) {
488 free(server_opts.endpts);
489 free(server_opts.binds);
490 server_opts.endpts = NULL;
491 server_opts.binds = NULL;
492 }
493}
494
495void
roman45cec4e2023-02-17 10:21:39 +0100496nc_server_config_del_keystore(void)
497{
498 int i, j;
499 struct nc_keystore *ks = &server_opts.keystore;
500
501 /* delete all asymmetric keys */
502 for (i = 0; i < ks->asym_key_count; i++) {
503 free(ks->asym_keys[i].name);
504 free(ks->asym_keys[i].pub_base64);
505 free(ks->asym_keys[i].priv_base64);
506
507 for (j = 0; j < ks->asym_keys[i].cert_count; j++) {
508 /* free associated certificates */
509 free(ks->asym_keys[i].certs[j].name);
510 free(ks->asym_keys[i].certs[j].cert_base64);
511 }
512 free(ks->asym_keys[i].certs);
romand57b3722023-04-05 11:26:25 +0200513 ks->asym_keys[i].certs = NULL;
roman45cec4e2023-02-17 10:21:39 +0100514 ks->asym_keys[i].cert_count = 0;
515 }
516 free(ks->asym_keys);
romand57b3722023-04-05 11:26:25 +0200517 ks->asym_keys = NULL;
roman45cec4e2023-02-17 10:21:39 +0100518 ks->asym_key_count = 0;
519
520 /* delete all symmetric keys */
521 for (i = 0; i < ks->sym_key_count; i++) {
522 free(ks->sym_keys[i].name);
523 free(ks->sym_keys[i].base64);
524 }
525 free(ks->sym_keys);
romand57b3722023-04-05 11:26:25 +0200526 ks->sym_keys = NULL;
roman45cec4e2023-02-17 10:21:39 +0100527 ks->sym_key_count = 0;
528}
529
romand57b3722023-04-05 11:26:25 +0200530void
531nc_server_config_del_trustore(void)
532{
533 int i, j;
534 struct nc_truststore *ts = &server_opts.truststore;
535
536 /* delete all cert bags */
537 for (i = 0; i < ts->cert_bag_count; i++) {
538 free(ts->cert_bags[i].name);
539 for (j = 0; j < ts->cert_bags[i].cert_count; j++) {
540 /* free associated certificates */
541 free(ts->cert_bags[i].certs[j].name);
542 free(ts->cert_bags[i].certs[j].cert_base64);
543 }
544 free(ts->cert_bags[i].certs);
545 ts->cert_bags[i].certs = NULL;
546 ts->cert_bags[i].cert_count = 0;
547 }
548 free(ts->cert_bags);
549 ts->cert_bags = NULL;
550 ts->cert_bag_count = 0;
551
552 /* delete all pubkey bags */
553 for (i = 0; i < ts->pub_bag_count; i++) {
554 free(ts->pub_bags[i].name);
555 for (j = 0; j < ts->pub_bags[i].pubkey_count; j++) {
556 /* free associated pubkeys */
557 free(ts->pub_bags[i].pubkeys[j].name);
558 free(ts->pub_bags[i].pubkeys[j].pub_base64);
559 }
560 free(ts->pub_bags[i].pubkeys);
561 ts->pub_bags[i].pubkeys = NULL;
562 ts->pub_bags[i].pubkey_count = 0;
563 }
564 free(ts->pub_bags);
565 ts->pub_bags = NULL;
566 ts->pub_bag_count = 0;
567}
568
romanc1d2b092023-02-02 08:58:27 +0100569/* presence container */
570int
romane028ef92023-02-24 16:33:08 +0100571nc_server_config_listen(NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100572{
573 uint16_t i;
574
575 assert(op == NC_OP_CREATE || op == NC_OP_DELETE);
576
577 if (op == NC_OP_DELETE) {
578 for (i = 0; i < server_opts.endpt_count; i++) {
roman83683fb2023-02-24 09:15:23 +0100579 if (server_opts.endpts[i].ti == NC_TI_LIBSSH) {
580 nc_server_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]);
581 } else if (server_opts.endpts[i].ti == NC_TI_OPENSSL) {
582 /* todo */
583 } else {
584 nc_server_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]);
585 }
romanc1d2b092023-02-02 08:58:27 +0100586 }
587 }
588
589 return 0;
590}
591
592/* default leaf */
593static int
romane028ef92023-02-24 16:33:08 +0100594nc_server_config_idle_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100595{
596 assert(!strcmp(LYD_NAME(node), "idle-timeout"));
597
598 if (equal_parent_name(node, 1, "listen")) {
599 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
600 server_opts.idle_timeout = strtoul(lyd_get_value(node), NULL, 10);
601 } else {
602 /* default value */
603 server_opts.idle_timeout = 3600;
604 }
605 }
606
607 return 0;
608}
609
610static int
611nc_server_create_bind(void)
612{
613 int ret = 0;
614 void *tmp;
615
616 tmp = realloc(server_opts.binds, (server_opts.endpt_count + 1) * sizeof *server_opts.binds);
617 if (!tmp) {
618 ERRMEM;
619 ret = 1;
620 goto cleanup;
621 }
622 server_opts.binds = tmp;
623 memset(&server_opts.binds[server_opts.endpt_count], 0, sizeof *server_opts.binds);
624
625 server_opts.binds[server_opts.endpt_count].sock = -1;
626
627cleanup:
628 return ret;
629}
630
631static int
632nc_server_create_endpoint(const struct lyd_node *node)
633{
634 int ret = 0;
635 void *tmp;
636
637 tmp = realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts);
638 if (!tmp) {
639 ERRMEM;
640 ret = 1;
641 goto cleanup;
642 }
643 server_opts.endpts = tmp;
644 memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts);
645
646 node = lyd_child(node);
647 assert(!strcmp(LYD_NAME(node), "name"));
648
649 server_opts.endpts[server_opts.endpt_count].name = strdup(lyd_get_value(node));
650 if (!server_opts.endpts[server_opts.endpt_count].name) {
651 ERRMEM;
652 ret = 1;
653 goto cleanup;
654 }
655
656 if (nc_server_create_bind()) {
657 ret = 1;
658 goto cleanup;
659 }
660
661 server_opts.endpt_count++;
662
663cleanup:
664 return ret;
665}
666
667/* list */
668static int
romane028ef92023-02-24 16:33:08 +0100669nc_server_config_endpoint(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100670{
671 int ret = 0;
672 struct nc_endpt *endpt;
673 struct nc_bind *bind;
674
675 assert(!strcmp(LYD_NAME(node), "endpoint"));
676
677 if (op == NC_OP_CREATE) {
678 ret = nc_server_create_endpoint(node);
679 if (ret) {
680 goto cleanup;
681 }
682 } else if (op == NC_OP_DELETE) {
683 /* free all children */
684 if (nc_server_get_endpt(node, &endpt, &bind)) {
685 ret = 1;
686 goto cleanup;
687 }
688 nc_server_del_endpt_ssh(endpt, bind);
689 }
690
691cleanup:
692 return ret;
693}
694
695static int
696nc_server_create_ssh(struct nc_endpt *endpt)
697{
698 endpt->ti = NC_TI_LIBSSH;
699 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
700 if (!endpt->opts.ssh) {
701 ERRMEM;
702 return 1;
703 }
704
705 return 0;
706}
707
708/* NP container */
709static int
romane028ef92023-02-24 16:33:08 +0100710nc_server_config_ssh(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100711{
712 struct nc_endpt *endpt;
713 struct nc_bind *bind;
714 int ret = 0;
715
716 assert(!strcmp(LYD_NAME(node), "ssh"));
717
718 if (nc_server_get_endpt(node, &endpt, &bind)) {
719 ret = 1;
720 goto cleanup;
721 }
722
723 if (op == NC_OP_CREATE) {
724 ret = nc_server_create_ssh(endpt);
725 if (ret) {
726 goto cleanup;
727 }
728 } else if (op == NC_OP_DELETE) {
729 nc_server_del_ssh(bind, endpt->opts.ssh);
730 }
731
732cleanup:
733 return ret;
734}
735
736static int
737nc_server_config_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port)
738{
739 int sock = -1, set_addr, ret = 0;
740
roman83683fb2023-02-24 09:15:23 +0100741 assert((address && !port) || (!address && port) || (endpt->ti == NC_TI_UNIX));
romanc1d2b092023-02-02 08:58:27 +0100742
743 if (address) {
744 set_addr = 1;
745 } else {
746 set_addr = 0;
747 }
748
749 if (set_addr) {
750 port = bind->port;
751 } else {
752 address = bind->address;
753 }
754
romanc1d2b092023-02-02 08:58:27 +0100755 /* we have all the information we need to create a listening socket */
roman83683fb2023-02-24 09:15:23 +0100756 if ((address && port) || (endpt->ti == NC_TI_UNIX)) {
romanc1d2b092023-02-02 08:58:27 +0100757 /* create new socket, close the old one */
roman83683fb2023-02-24 09:15:23 +0100758 if (endpt->ti == NC_TI_UNIX) {
759 sock = nc_sock_listen_unix(endpt->opts.unixsock);
760 } else {
761 sock = nc_sock_listen_inet(address, port, &endpt->ka);
762 }
763
romanc1d2b092023-02-02 08:58:27 +0100764 if (sock == -1) {
765 ret = 1;
766 goto cleanup;
767 }
768
769 if (bind->sock > -1) {
770 close(bind->sock);
771 }
772 bind->sock = sock;
773 }
774
775 if (sock > -1) {
776 switch (endpt->ti) {
roman83683fb2023-02-24 09:15:23 +0100777 case NC_TI_UNIX:
778 VRB(NULL, "Listening on %s for UNIX connections.", endpt->opts.unixsock->address);
779 break;
romanc1d2b092023-02-02 08:58:27 +0100780#ifdef NC_ENABLED_SSH
781 case NC_TI_LIBSSH:
782 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
783 break;
784#endif
785#ifdef NC_ENABLED_TLS
786 case NC_TI_OPENSSL:
787 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
788 break;
789#endif
790 default:
791 ERRINT;
792 ret = 1;
793 break;
794 }
795 }
796
797cleanup:
798 return ret;
799}
800
801/* mandatory leaf */
802static int
romane028ef92023-02-24 16:33:08 +0100803nc_server_config_local_address(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100804{
805 struct nc_endpt *endpt;
806 struct nc_bind *bind;
807 int ret = 0;
808
809 (void) op;
810
811 assert(!strcmp(LYD_NAME(node), "local-address"));
812
813 if (equal_parent_name(node, 4, "listen")) {
814 if (nc_server_get_endpt(node, &endpt, &bind)) {
815 ret = 1;
816 goto cleanup;
817 }
818
819 nc_server_del_local_address(bind);
820 bind->address = strdup(lyd_get_value(node));
821 if (!bind->address) {
822 ERRMEM;
823 ret = 1;
824 goto cleanup;
825 }
826
827 ret = nc_server_config_set_address_port(endpt, bind, lyd_get_value(node), 0);
828 if (ret) {
829 goto cleanup;
830 }
831 }
832
833cleanup:
834 return ret;
835}
836
837/* leaf with default value */
838static int
romane028ef92023-02-24 16:33:08 +0100839nc_server_config_local_port(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100840{
841 struct nc_endpt *endpt;
842 struct nc_bind *bind;
843 int ret = 0;
844
845 assert(!strcmp(LYD_NAME(node), "local-port"));
846
847 if (equal_parent_name(node, 4, "listen")) {
848 if (nc_server_get_endpt(node, &endpt, &bind)) {
849 ret = 1;
850 goto cleanup;
851 }
852
853 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
854 bind->port = strtoul(lyd_get_value(node), NULL, 10);
855 } else {
856 /* delete -> set to default */
857 bind->port = 0;
858 }
859
860 ret = nc_server_config_set_address_port(endpt, bind, NULL, bind->port);
861 if (ret) {
862 goto cleanup;
863 }
864 }
865
866cleanup:
867 return ret;
868}
869
870/* P container */
871static int
romane028ef92023-02-24 16:33:08 +0100872nc_server_config_keepalives(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100873{
874 struct nc_endpt *endpt;
875 struct nc_bind *bind;
876 int ret = 0;
877
878 assert(!strcmp(LYD_NAME(node), "keepalives"));
879
880 if (equal_parent_name(node, 4, "listen")) {
881 if (nc_server_get_endpt(node, &endpt, &bind)) {
882 ret = 1;
883 goto cleanup;
884 }
885
886 if (op == NC_OP_CREATE) {
887 endpt->ka.enabled = 1;
888 } else {
889 endpt->ka.enabled = 0;
890 }
891 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
892 if (ret) {
893 goto cleanup;
894 }
895 }
896
897cleanup:
898 return ret;
899}
900
901/* mandatory leaf */
902static int
romane028ef92023-02-24 16:33:08 +0100903nc_server_config_idle_time(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100904{
905 struct nc_endpt *endpt;
906 struct nc_bind *bind;
907 int ret = 0;
908
909 assert(!strcmp(LYD_NAME(node), "idle-time"));
910
911 if (equal_parent_name(node, 4, "listen")) {
912 if (nc_server_get_endpt(node, &endpt, &bind)) {
913 ret = 1;
914 goto cleanup;
915 }
916
917 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
918 endpt->ka.idle_time = strtoul(lyd_get_value(node), NULL, 10);
919 } else {
920 endpt->ka.idle_time = 0;
921 }
922 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
923 if (ret) {
924 goto cleanup;
925 }
926 }
927
928cleanup:
929 return ret;
930}
931
932/* mandatory leaf */
933static int
romane028ef92023-02-24 16:33:08 +0100934nc_server_config_max_probes(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100935{
936 struct nc_endpt *endpt;
937 struct nc_bind *bind;
938 int ret = 0;
939
940 assert(!strcmp(LYD_NAME(node), "max-probes"));
941
942 if (equal_parent_name(node, 4, "listen")) {
943 if (nc_server_get_endpt(node, &endpt, &bind)) {
944 ret = 1;
945 goto cleanup;
946 }
947
948 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
949 endpt->ka.max_probes = strtoul(lyd_get_value(node), NULL, 10);
950 } else {
951 endpt->ka.max_probes = 0;
952 }
953 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
954 if (ret) {
955 goto cleanup;
956 }
957 }
958
959cleanup:
960 return ret;
961}
962
963/* mandatory leaf */
964static int
romane028ef92023-02-24 16:33:08 +0100965nc_server_config_probe_interval(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100966{
967 struct nc_endpt *endpt;
968 struct nc_bind *bind;
969 int ret = 0;
970
971 assert(!strcmp(LYD_NAME(node), "probe-interval"));
972
973 if (equal_parent_name(node, 4, "listen")) {
974 if (nc_server_get_endpt(node, &endpt, &bind)) {
975 ret = 1;
976 goto cleanup;
977 }
978
979 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
980 endpt->ka.probe_interval = strtoul(lyd_get_value(node), NULL, 10);
981 } else {
982 endpt->ka.probe_interval = 0;
983 }
984 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
985 if (ret) {
986 goto cleanup;
987 }
988 }
989
990cleanup:
991 return ret;
992}
993
994static int
995nc_server_create_host_key(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
996{
997 int ret = 0;
998 void *tmp;
999
1000 tmp = realloc(opts->hostkeys,
1001 (opts->hostkey_count + 1) * sizeof *opts->hostkeys);
1002 if (!tmp) {
1003 ERRMEM;
1004 ret = 1;
1005 goto cleanup;
1006 }
1007 opts->hostkeys = tmp;
1008
1009 memset(&opts->hostkeys[opts->hostkey_count], 0, sizeof *opts->hostkeys);
1010
1011 opts->hostkeys[opts->hostkey_count].name = strdup(lyd_get_value(lyd_child(node)));
1012 if (!opts->hostkeys[opts->hostkey_count].name) {
1013 ERRMEM;
1014 ret = 1;
1015 goto cleanup;
1016 }
1017
1018 /* set union selector */
1019 lyd_find_path(node, "public-key", 0, (struct lyd_node **)&node);
1020 assert(node);
1021
1022 if (!lyd_find_path(node, "local-definition", 0, NULL)) {
1023 opts->hostkeys[opts->hostkey_count].ks_type = NC_STORE_LOCAL;
1024 } else {
1025 opts->hostkeys[opts->hostkey_count].ks_type = NC_STORE_KEYSTORE;
1026 }
1027
1028 opts->hostkey_count++;
1029
1030cleanup:
1031 return ret;
1032}
1033
1034/* list */
1035static int
romane028ef92023-02-24 16:33:08 +01001036nc_server_config_host_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001037{
1038 struct nc_endpt *endpt;
1039 struct nc_hostkey *hostkey;
1040 int ret = 0;
1041
1042 assert(!strcmp(LYD_NAME(node), "host-key"));
1043
1044 if ((equal_parent_name(node, 1, "server-identity")) && (equal_parent_name(node, 5, "listen"))) {
1045 if (nc_server_get_endpt(node, &endpt, NULL)) {
1046 ret = 1;
1047 goto cleanup;
1048 }
1049
1050 if (op == NC_OP_CREATE) {
1051 ret = nc_server_create_host_key(node, endpt->opts.ssh);
1052 if (ret) {
1053 goto cleanup;
1054 }
1055 } else if (op == NC_OP_DELETE) {
1056 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1057 ret = 1;
1058 goto cleanup;
1059 }
1060
1061 nc_server_del_hostkey(endpt->opts.ssh, hostkey);
1062 }
1063 } else if (equal_parent_name(node, 1, "transport-params")) {
1064 /* just a container with the name host-key, nothing to be done */
1065 goto cleanup;
1066 } else {
1067 ERRINT;
1068 ret = 1;
1069 goto cleanup;
1070 }
1071
1072cleanup:
1073 return ret;
1074}
1075
1076/* mandatory leaf */
romane028ef92023-02-24 16:33:08 +01001077static int
1078nc_server_config_public_key_format(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001079{
1080 const char *format;
1081 struct nc_endpt *endpt;
1082 struct nc_client_auth *auth_client;
roman8edee342023-03-31 13:25:48 +02001083 struct nc_public_key *pubkey;
romanc1d2b092023-02-02 08:58:27 +01001084 struct nc_hostkey *hostkey;
1085 int ret = 0;
1086
1087 assert(!strcmp(LYD_NAME(node), "public-key-format"));
1088
1089 format = ((struct lyd_node_term *)node)->value.ident->name;
1090
1091 if ((equal_parent_name(node, 6, "client-authentication")) && (equal_parent_name(node, 10, "listen"))) {
1092 if (nc_server_get_endpt(node, &endpt, NULL)) {
1093 ret = 1;
1094 goto cleanup;
1095 }
1096
1097 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1098 ret = 1;
1099 goto cleanup;
1100 }
1101
1102 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1103 ret = 1;
1104 goto cleanup;
1105 }
1106
1107 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1108 if (!strcmp(format, "ssh-public-key-format")) {
1109 pubkey->pubkey_type = NC_SSH_PUBKEY_X509;
1110 } else if (!strcmp(format, "subject-public-key-info-format")) {
1111 pubkey->pubkey_type = NC_SSH_PUBKEY_SSH2;
1112 } else {
1113 ERR(NULL, "Public key format (%s) not supported.", format);
1114 }
1115 }
1116 } else if ((equal_parent_name(node, 5, "server-identity")) && (equal_parent_name(node, 11, "listen"))) {
1117 if (nc_server_get_endpt(node, &endpt, NULL)) {
1118 ret = 1;
1119 goto cleanup;
1120 }
1121
1122 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1123 ret = 1;
1124 goto cleanup;
1125 }
1126
1127 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1128 if (!strcmp(format, "ssh-public-key-format")) {
roman8edee342023-03-31 13:25:48 +02001129 hostkey->key.pubkey_type = NC_SSH_PUBKEY_SSH2;
romane028ef92023-02-24 16:33:08 +01001130 } else if (!strcmp(format, "subject-public-key-info-format")) {
roman8edee342023-03-31 13:25:48 +02001131 hostkey->key.pubkey_type = NC_SSH_PUBKEY_X509;
romanc1d2b092023-02-02 08:58:27 +01001132 } else {
1133 ERR(NULL, "Public key format (%s) not supported.", format);
1134 }
1135 }
1136 }
1137
1138cleanup:
1139 return ret;
1140}
1141
1142/* leaf */
romane028ef92023-02-24 16:33:08 +01001143static int
1144nc_server_config_private_key_format(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001145{
1146 const char *format;
1147 struct nc_endpt *endpt;
1148 struct nc_hostkey *hostkey;
1149 int ret = 0;
1150
1151 assert(!strcmp(LYD_NAME(node), "private-key-format"));
1152
1153 if (nc_server_get_endpt(node, &endpt, NULL)) {
1154 ret = 1;
1155 goto cleanup;
1156 }
1157
1158 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1159 ret = 1;
1160 goto cleanup;
1161 }
1162
1163 format = ((struct lyd_node_term *)node)->value.ident->name;
1164 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1165 if (!strcmp(format, "rsa-private-key-format")) {
roman8edee342023-03-31 13:25:48 +02001166 hostkey->key.privkey_type = NC_SSH_KEY_RSA;
romanc1d2b092023-02-02 08:58:27 +01001167 } else if (!strcmp(format, "ec-private-key-format")) {
roman8edee342023-03-31 13:25:48 +02001168 hostkey->key.privkey_type = NC_SSH_KEY_ECDSA;
romanc1d2b092023-02-02 08:58:27 +01001169 } else {
1170 ERR(NULL, "Private key format (%s) not supported.", format);
1171 }
1172 }
1173
1174cleanup:
1175 return ret;
1176}
1177
1178static int
1179nc_server_replace_cleartext_private_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
1180{
1181 nc_server_del_private_key(hostkey);
roman8edee342023-03-31 13:25:48 +02001182 hostkey->key.priv_base64 = strdup(lyd_get_value(node));
1183 if (!hostkey->key.priv_base64) {
romanc1d2b092023-02-02 08:58:27 +01001184 ERRMEM;
1185 return 1;
1186 }
1187
1188 return 0;
1189}
1190
1191static int
romane028ef92023-02-24 16:33:08 +01001192nc_server_config_cleartext_private_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001193{
1194 struct nc_endpt *endpt;
1195 struct nc_hostkey *hostkey;
1196 int ret = 0;
1197
1198 assert(!strcmp(LYD_NAME(node), "cleartext-private-key"));
1199
1200 if ((equal_parent_name(node, 6, "ssh")) && (equal_parent_name(node, 8, "listen"))) {
1201 if (nc_server_get_endpt(node, &endpt, NULL)) {
1202 ret = 1;
1203 goto cleanup;
1204 }
1205 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1206 ret = 1;
1207 goto cleanup;
1208 }
1209
1210 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1211 ret = nc_server_replace_cleartext_private_key(node, hostkey);
1212 if (ret) {
1213 goto cleanup;
1214 }
1215 } else {
1216 nc_server_del_private_key(hostkey);
1217 }
1218 }
1219
1220cleanup:
1221 return ret;
1222}
1223
1224static int
1225nc_server_create_keystore_reference(const struct lyd_node *node, struct nc_hostkey *hostkey)
1226{
1227 uint16_t i;
roman45cec4e2023-02-17 10:21:39 +01001228 struct nc_keystore *ks = &server_opts.keystore;
romanc1d2b092023-02-02 08:58:27 +01001229
1230 /* lookup name */
roman45cec4e2023-02-17 10:21:39 +01001231 for (i = 0; i < ks->asym_key_count; i++) {
1232 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
romanc1d2b092023-02-02 08:58:27 +01001233 break;
1234 }
1235 }
1236
roman45cec4e2023-02-17 10:21:39 +01001237 if (i == ks->asym_key_count) {
1238 ERR(NULL, "Keystore \"%s\" not found.", lyd_get_value(node));
romanc1d2b092023-02-02 08:58:27 +01001239 return 1;
1240 }
1241
roman45cec4e2023-02-17 10:21:39 +01001242 hostkey->ks_ref = &ks->asym_keys[i];
romanc1d2b092023-02-02 08:58:27 +01001243
1244 return 0;
1245}
1246
1247/* leaf */
1248static int
romane028ef92023-02-24 16:33:08 +01001249nc_server_config_keystore_reference(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001250{
1251 struct nc_endpt *endpt;
1252 struct nc_hostkey *hostkey;
1253 int ret = 0;
1254
1255 assert(!strcmp(LYD_NAME(node), "keystore-reference"));
1256
roman45cec4e2023-02-17 10:21:39 +01001257 if ((equal_parent_name(node, 3, "server-identity")) && (equal_parent_name(node, 7, "listen"))) {
romanc1d2b092023-02-02 08:58:27 +01001258 if (nc_server_get_endpt(node, &endpt, NULL)) {
1259 ret = 1;
1260 goto cleanup;
1261 }
1262 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1263 ret = 1;
1264 goto cleanup;
1265 }
1266
1267 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1268 ret = nc_server_create_keystore_reference(node, hostkey);
1269 if (ret) {
1270 goto cleanup;
1271 }
1272 } else {
roman45cec4e2023-02-17 10:21:39 +01001273 hostkey->ks_ref = NULL;
romanc1d2b092023-02-02 08:58:27 +01001274 }
1275 }
1276
1277cleanup:
1278 return ret;
1279}
1280
1281static int
1282nc_server_create_auth_key_public_key_list(const struct lyd_node *node, struct nc_client_auth *auth_client)
1283{
1284 int ret = 0;
1285 void *tmp;
1286
1287 assert(!strcmp(LYD_NAME(node), "public-key"));
1288
1289 tmp = realloc(auth_client->pubkeys, (auth_client->pubkey_count + 1) * sizeof *auth_client->pubkeys);
1290 if (!tmp) {
1291 ERRMEM;
1292 ret = 1;
1293 goto cleanup;
1294 }
1295 auth_client->pubkeys = tmp;
1296
1297 memset(&auth_client->pubkeys[auth_client->pubkey_count], 0, sizeof *auth_client->pubkeys);
1298
1299 node = lyd_child(node);
1300 assert(!strcmp(LYD_NAME(node), "name"));
1301
1302 auth_client->pubkeys[auth_client->pubkey_count].name = strdup(lyd_get_value(node));
1303 if (!auth_client->pubkeys[auth_client->pubkey_count].name) {
1304 ERRMEM;
1305 ret = 1;
1306 goto cleanup;
1307 }
1308
1309 ++auth_client->pubkey_count;
1310
1311cleanup:
1312 return ret;
1313}
1314
1315static int
roman8edee342023-03-31 13:25:48 +02001316nc_server_replace_auth_key_public_key_leaf(const struct lyd_node *node, struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +01001317{
1318 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
1319
1320 pubkey->pub_base64 = strdup(lyd_get_value(node));
1321 if (!pubkey->pub_base64) {
1322 ERRMEM;
1323 return 1;
1324 }
1325
1326 return 0;
1327}
1328
1329static int
1330nc_server_replace_host_key_public_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
1331{
1332 nc_server_del_public_key(hostkey);
1333
roman8edee342023-03-31 13:25:48 +02001334 hostkey->key.pub_base64 = strdup(lyd_get_value(node));
1335 if (!hostkey->key.pub_base64) {
romanc1d2b092023-02-02 08:58:27 +01001336 ERRMEM;
1337 return 1;
1338 }
1339
1340 return 0;
1341}
1342
1343static int
romane028ef92023-02-24 16:33:08 +01001344nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001345{
1346 struct nc_endpt *endpt;
1347 struct nc_hostkey *hostkey;
1348 struct nc_client_auth *auth_client;
roman8edee342023-03-31 13:25:48 +02001349 struct nc_public_key *pubkey;
romanc1d2b092023-02-02 08:58:27 +01001350 int ret = 0;
1351
1352 assert(!strcmp(LYD_NAME(node), "public-key"));
1353
1354 if ((equal_parent_name(node, 3, "host-key")) && (equal_parent_name(node, 8, "listen"))) {
1355 /* server's public-key, mandatory leaf */
1356 if (nc_server_get_endpt(node, &endpt, NULL)) {
1357 ret = 1;
1358 goto cleanup;
1359 }
1360
1361 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1362 ret = 1;
1363 goto cleanup;
1364 }
1365
1366 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1367 ret = nc_server_replace_host_key_public_key(node, hostkey);
1368 if (ret) {
1369 goto cleanup;
1370 }
1371 }
1372 } else if ((equal_parent_name(node, 5, "client-authentication")) && (equal_parent_name(node, 9, "listen"))) {
1373 /* client auth pubkeys, list */
1374 if (nc_server_get_endpt(node, &endpt, NULL)) {
1375 ret = 1;
1376 goto cleanup;
1377 }
1378
1379 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1380 ret = 1;
1381 goto cleanup;
1382 }
1383
1384 if (op == NC_OP_CREATE) {
1385 ret = nc_server_create_auth_key_public_key_list(node, auth_client);
1386 if (ret) {
1387 goto cleanup;
1388 }
1389 } else if (op == NC_OP_DELETE) {
1390 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1391 ret = 1;
1392 goto cleanup;
1393 }
1394
1395 nc_server_del_auth_client_pubkey(auth_client, pubkey);
1396 }
1397 } else if ((equal_parent_name(node, 6, "client-authentication")) && (equal_parent_name(node, 10, "listen"))) {
1398 /* client auth pubkey, leaf */
1399 if (nc_server_get_endpt(node, &endpt, NULL)) {
1400 ret = 1;
1401 goto cleanup;
1402 }
1403
1404 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1405 ret = 1;
1406 goto cleanup;
1407 }
1408
1409 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1410 ret = 1;
1411 goto cleanup;
1412 }
1413
1414 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1415 ret = nc_server_replace_auth_key_public_key_leaf(node, pubkey);
1416 if (ret) {
1417 goto cleanup;
1418 }
1419 } else {
1420 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
1421 }
1422 }
1423
1424cleanup:
1425 return ret;
1426}
1427
1428static int
1429nc_server_create_user(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
1430{
1431 int ret = 0;
1432 void *tmp;
1433
1434 tmp = realloc(opts->auth_clients, (opts->client_count + 1) * sizeof *opts->auth_clients);
1435 if (!tmp) {
1436 ERRMEM;
1437 ret = 1;
1438 goto cleanup;
1439 }
1440 opts->auth_clients = tmp;
1441
1442 memset(&opts->auth_clients[opts->client_count], 0, sizeof *opts->auth_clients);
1443
1444 opts->auth_clients[opts->client_count].username = strdup(lyd_get_value(lyd_child(node)));
1445 if (!opts->auth_clients[opts->client_count].username) {
1446 ERRMEM;
1447 ret = 1;
1448 goto cleanup;
1449 }
1450
1451 lyd_find_path(node, "public-keys", 0, (struct lyd_node **)&node);
1452
1453 if (node) {
1454 /* set union selector */
1455 if (!lyd_find_path(node, "local-definition", 0, NULL)) {
1456 opts->auth_clients[opts->client_count].ks_type = NC_STORE_LOCAL;
1457 } else {
1458 opts->auth_clients[opts->client_count].ks_type = NC_STORE_TRUSTSTORE;
1459 }
1460 }
1461
1462 ++opts->client_count;
1463
1464cleanup:
1465 return ret;
1466}
1467
1468/* list */
1469static int
romane028ef92023-02-24 16:33:08 +01001470nc_server_config_user(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001471{
1472 struct nc_endpt *endpt;
1473 struct nc_client_auth *auth_client;
1474 int ret = 0;
1475
1476 assert(!strcmp(LYD_NAME(node), "user"));
1477
1478 if (equal_parent_name(node, 6, "listen")) {
1479 if (nc_server_get_endpt(node, &endpt, NULL)) {
1480 ret = 1;
1481 goto cleanup;
1482 }
1483
1484 if (op == NC_OP_CREATE) {
1485 ret = nc_server_create_user(node, endpt->opts.ssh);
1486 if (ret) {
1487 goto cleanup;
1488 }
1489 } else if (op == NC_OP_DELETE) {
1490 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1491 ret = 1;
1492 goto cleanup;
1493 }
1494
1495 nc_server_del_auth_client(endpt->opts.ssh, auth_client);
1496 }
1497 }
1498
1499cleanup:
1500 return ret;
1501}
1502
1503static int
romane028ef92023-02-24 16:33:08 +01001504nc_server_config_auth_attempts(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001505{
1506 struct nc_endpt *endpt;
1507 int ret = 0;
1508
1509 assert(!strcmp(LYD_NAME(node), "auth-attempts"));
1510
1511 if (equal_parent_name(node, 5, "listen")) {
1512 if (nc_server_get_endpt(node, &endpt, NULL)) {
1513 ret = 1;
1514 goto cleanup;
1515 }
1516
1517 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1518 endpt->opts.ssh->auth_attempts = strtoul(lyd_get_value(node), NULL, 10);
1519 }
1520 }
1521
1522cleanup:
1523 return ret;
1524}
1525
1526static int
romane028ef92023-02-24 16:33:08 +01001527nc_server_config_auth_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001528{
1529 struct nc_endpt *endpt;
1530 int ret = 0;
1531
1532 assert(!strcmp(LYD_NAME(node), "auth-timeout"));
1533
1534 if (equal_parent_name(node, 5, "listen")) {
1535 if (nc_server_get_endpt(node, &endpt, NULL)) {
1536 ret = 1;
1537 goto cleanup;
1538 }
1539
1540 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1541 endpt->opts.ssh->auth_timeout = strtoul(lyd_get_value(node), NULL, 10);
1542 }
1543 }
1544
1545cleanup:
1546 return ret;
1547}
1548
1549static int
1550nc_server_replace_truststore_reference(const struct lyd_node *node, struct nc_client_auth *client_auth)
1551{
romand57b3722023-04-05 11:26:25 +02001552 uint16_t i;
1553 struct nc_truststore *ts = &server_opts.truststore;
romanc1d2b092023-02-02 08:58:27 +01001554
romand57b3722023-04-05 11:26:25 +02001555 /* lookup name */
1556 for (i = 0; i < ts->pub_bag_count; i++) {
1557 if (!strcmp(lyd_get_value(node), ts->pub_bags[i].name)) {
1558 break;
1559 }
1560 }
1561
1562 if (i == ts->pub_bag_count) {
1563 ERR(NULL, "Truststore \"%s\" not found.", lyd_get_value(node));
romanc1d2b092023-02-02 08:58:27 +01001564 return 1;
1565 }
1566
romand57b3722023-04-05 11:26:25 +02001567 client_auth->ts_ref = &ts->pub_bags[i];
1568
romanc1d2b092023-02-02 08:58:27 +01001569 return 0;
1570}
1571
1572/* leaf */
1573static int
romane028ef92023-02-24 16:33:08 +01001574nc_server_config_truststore_reference(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001575{
1576 struct nc_endpt *endpt;
1577 struct nc_client_auth *auth_client;
1578 int ret = 0;
1579
1580 assert(!strcmp(LYD_NAME(node), "truststore-reference"));
1581
1582 if ((equal_parent_name(node, 1, "public-keys")) && (equal_parent_name(node, 8, "listen"))) {
1583 if (nc_server_get_endpt(node, &endpt, NULL)) {
1584 ret = 1;
1585 goto cleanup;
1586 }
1587
1588 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1589 ret = 1;
1590 goto cleanup;
1591 }
1592
1593 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1594 ret = nc_server_replace_truststore_reference(node, auth_client);
1595 if (ret) {
1596 goto cleanup;
1597 }
1598 } else {
romand57b3722023-04-05 11:26:25 +02001599 auth_client->ts_ref = NULL;
romanc1d2b092023-02-02 08:58:27 +01001600 }
1601 }
1602
1603cleanup:
1604 return ret;
1605}
1606
1607static int
1608nc_server_replace_password(const struct lyd_node *node, struct nc_client_auth *auth_client)
1609{
1610 nc_server_del_auth_client_password(auth_client);
1611
1612 auth_client->password = strdup(lyd_get_value(node));
1613 if (!auth_client->password) {
1614 ERRMEM;
1615 return 1;
1616 }
1617
1618 return 0;
1619}
1620
1621/* leaf */
1622static int
romane028ef92023-02-24 16:33:08 +01001623nc_server_config_password(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001624{
1625 struct nc_endpt *endpt;
1626 struct nc_client_auth *auth_client;
1627 int ret = 0;
1628
1629 assert(!strcmp(LYD_NAME(node), "password"));
1630
1631 if (equal_parent_name(node, 7, "listen")) {
1632 if (nc_server_get_endpt(node, &endpt, NULL)) {
1633 ret = 1;
1634 goto cleanup;
1635 }
1636
1637 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1638 ret = 1;
1639 goto cleanup;
1640 }
1641
1642 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1643 ret = nc_server_replace_password(node, auth_client);
1644 if (ret) {
1645 goto cleanup;
1646 }
1647 } else {
1648 nc_server_del_auth_client_password(auth_client);
1649 }
1650 }
1651
1652cleanup:
1653 return ret;
1654}
1655
1656static int
romane028ef92023-02-24 16:33:08 +01001657nc_server_config_pam_name(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001658{
1659 struct nc_endpt *endpt;
1660 struct nc_client_auth *auth_client;
1661 int ret = 0;
1662
1663 assert(!strcmp(LYD_NAME(node), "pam-config-file-name"));
1664
1665 if (equal_parent_name(node, 8, "listen")) {
1666 if (nc_server_get_endpt(node, &endpt, NULL)) {
1667 ret = 1;
1668 goto cleanup;
1669 }
1670
1671 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1672 ret = 1;
1673 goto cleanup;
1674 }
1675
1676 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1677 nc_server_del_auth_client_pam_name(auth_client);
1678
1679 auth_client->pam_config_name = strdup(lyd_get_value(node));
1680 if (!auth_client->pam_config_name) {
1681 ERRMEM;
1682 ret = 1;
1683 goto cleanup;
1684 }
1685 }
1686 }
1687
1688cleanup:
1689 return ret;
1690}
1691
1692static int
romane028ef92023-02-24 16:33:08 +01001693nc_server_config_pam_dir(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001694{
1695 struct nc_endpt *endpt;
1696 struct nc_client_auth *auth_client;
1697 int ret = 0;
1698
1699 assert(!strcmp(LYD_NAME(node), "pam-config-file-dir"));
1700
1701 if (equal_parent_name(node, 8, "listen")) {
1702 if (nc_server_get_endpt(node, &endpt, NULL)) {
1703 ret = 1;
1704 goto cleanup;
1705 }
1706
1707 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1708 ret = 1;
1709 goto cleanup;
1710 }
1711
1712 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1713 nc_server_del_auth_client_pam_dir(auth_client);
1714 auth_client->pam_config_dir = strdup(lyd_get_value(node));
1715 if (!auth_client->pam_config_dir) {
1716 ERRMEM;
1717 ret = 1;
1718 goto cleanup;
1719 }
1720 }
1721 }
1722
1723cleanup:
1724 return ret;
1725}
1726
1727/* leaf */
1728static int
romane028ef92023-02-24 16:33:08 +01001729nc_server_config_none(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001730{
1731 struct nc_endpt *endpt;
1732 struct nc_client_auth *auth_client;
1733 int ret = 0;
1734
1735 assert(!strcmp(LYD_NAME(node), "none"));
1736
1737 if (equal_parent_name(node, 7, "listen")) {
1738 if (nc_server_get_endpt(node, &endpt, NULL)) {
1739 ret = 1;
1740 goto cleanup;
1741 }
1742
1743 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1744 ret = 1;
1745 goto cleanup;
1746 }
1747
1748 if (op == NC_OP_CREATE) {
1749 auth_client->supports_none = 1;
1750 } else {
1751 auth_client->supports_none = 0;
1752 }
1753 }
1754
1755cleanup:
1756 return ret;
1757}
1758
1759static int
romane028ef92023-02-24 16:33:08 +01001760nc_server_config_transport_params(const char *alg, char **alg_store, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001761{
1762 int ret = 0, alg_found = 0;
1763 char *substr, *haystack;
1764 size_t alg_len = strlen(alg);
1765
1766 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1767 if (!*alg_store) {
1768 /* first call */
1769 *alg_store = strdup(alg);
1770 if (!*alg_store) {
1771 ERRMEM;
1772 ret = 1;
1773 goto cleanup;
1774 }
1775 } else {
1776 /* +1 because of ',' between algorithms */
1777 *alg_store = nc_realloc(*alg_store, strlen(*alg_store) + alg_len + 1 + 1);
1778 if (!*alg_store) {
1779 ERRMEM;
1780 ret = 1;
1781 goto cleanup;
1782 }
1783 sprintf(*alg_store, "%s,%s", *alg_store, alg);
1784 }
1785 } else {
1786 /* delete */
1787 haystack = *alg_store;
1788 while ((substr = strstr(haystack, alg))) {
1789 /* iterate over all the substrings */
1790 if (((substr == haystack) && (*(substr + alg_len) == ',')) ||
1791 ((substr != haystack) && (*(substr - 1) == ',') && (*(substr + alg_len) == ','))) {
1792 /* either the first element of the string or somewhere in the middle */
1793 memmove(substr, substr + alg_len + 1, strlen(substr + alg_len + 1));
1794 alg_found = 1;
1795 break;
1796 } else if ((*(substr - 1) == ',') && (*(substr + alg_len) == '\0')) {
1797 /* the last element of the string */
1798 *(substr - 1) = '\0';
1799 alg_found = 1;
1800 break;
1801 }
1802 haystack++;
1803 }
1804 if (!alg_found) {
1805 ERR(NULL, "Unable to delete an algorithm (%s), which was not previously added.", alg);
1806 ret = 1;
1807 }
1808 }
1809
1810cleanup:
1811 return ret;
1812}
1813
1814/* leaf-list */
1815static int
romane028ef92023-02-24 16:33:08 +01001816nc_server_config_host_key_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001817{
1818 struct nc_endpt *endpt;
1819 int ret = 0, listen = 0;
1820 const char *alg;
1821 uint8_t i;
1822
1823 /* get the algorithm name and compare it with algs supported by libssh */
1824 alg = ((struct lyd_node_term *)node)->value.ident->name;
1825
1826 if (equal_parent_name(node, 6, "listen")) {
1827 listen = 1;
1828 if (nc_server_get_endpt(node, &endpt, NULL)) {
1829 ret = 1;
1830 goto cleanup;
1831 }
1832 }
1833
1834 i = 0;
1835 while (supported_hostkey_algs[i]) {
1836 if (!strcmp(supported_hostkey_algs[i], alg)) {
1837 if (listen) {
romane028ef92023-02-24 16:33:08 +01001838 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->hostkey_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001839 ret = 1;
1840 goto cleanup;
1841 }
1842 }
1843 break;
1844 }
1845 i++;
1846 }
1847 if (!supported_hostkey_algs[i]) {
1848 /* algorithm not supported */
1849 ERR(NULL, "Public key algorithm (%s) not supported by libssh.", alg);
1850 ret = 1;
1851 }
1852
1853cleanup:
1854 return ret;
1855}
1856
1857/* leaf-list */
1858static int
romane028ef92023-02-24 16:33:08 +01001859nc_server_config_kex_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001860{
1861 struct nc_endpt *endpt;
1862 int ret = 0, listen = 0;
1863 const char *alg;
1864 uint8_t i;
1865
1866 /* get the algorithm name and compare it with algs supported by libssh */
1867 alg = ((struct lyd_node_term *)node)->value.ident->name;
1868
1869 if (equal_parent_name(node, 6, "listen")) {
1870 listen = 1;
1871 if (nc_server_get_endpt(node, &endpt, NULL)) {
1872 ret = 1;
1873 goto cleanup;
1874 }
1875 }
1876
1877 i = 0;
1878 while (supported_kex_algs[i]) {
1879 if (!strcmp(supported_kex_algs[i], alg)) {
1880 if (listen) {
romane028ef92023-02-24 16:33:08 +01001881 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->kex_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001882 ret = 1;
1883 goto cleanup;
1884 }
1885 }
1886 break;
1887 }
1888 i++;
1889 }
1890 if (!supported_kex_algs[i]) {
1891 /* algorithm not supported */
1892 ERR(NULL, "Key exchange algorithm (%s) not supported by libssh.", alg);
1893 ret = 1;
1894 }
1895
1896cleanup:
1897 return ret;
1898}
1899
1900/* leaf-list */
1901static int
romane028ef92023-02-24 16:33:08 +01001902nc_server_config_encryption_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001903{
1904 struct nc_endpt *endpt;
1905 int ret = 0, listen = 0;
1906 const char *alg;
1907 uint8_t i;
1908
1909 /* get the algorithm name and compare it with algs supported by libssh */
1910 alg = ((struct lyd_node_term *)node)->value.ident->name;
1911
1912 if (equal_parent_name(node, 6, "listen")) {
1913 listen = 1;
1914 if (nc_server_get_endpt(node, &endpt, NULL)) {
1915 ret = 1;
1916 goto cleanup;
1917 }
1918 }
1919
1920 i = 0;
1921 while (supported_encryption_algs[i]) {
1922 if (!strcmp(supported_encryption_algs[i], alg)) {
1923 if (listen) {
romane028ef92023-02-24 16:33:08 +01001924 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->encryption_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001925 ret = 1;
1926 goto cleanup;
1927 }
1928 }
1929 break;
1930 }
1931 i++;
1932 }
1933 if (!supported_encryption_algs[i]) {
1934 /* algorithm not supported */
1935 ERR(NULL, "Encryption algorithm (%s) not supported by libssh.", alg);
1936 ret = 1;
1937 }
1938
1939cleanup:
1940 return ret;
1941}
1942
1943/* leaf-list */
1944static int
romane028ef92023-02-24 16:33:08 +01001945nc_server_config_mac_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001946{
1947 struct nc_endpt *endpt;
1948 int ret = 0, listen = 0;
1949 const char *alg;
1950 uint8_t i;
1951
1952 /* get the algorithm name and compare it with algs supported by libssh */
1953 alg = ((struct lyd_node_term *)node)->value.ident->name;
1954
1955 if (equal_parent_name(node, 6, "listen")) {
1956 listen = 1;
1957 if (nc_server_get_endpt(node, &endpt, NULL)) {
1958 ret = 1;
1959 goto cleanup;
1960 }
1961 }
1962
1963 i = 0;
1964 while (supported_mac_algs[i]) {
1965 if (!strcmp(supported_mac_algs[i], alg)) {
1966 if (listen) {
romane028ef92023-02-24 16:33:08 +01001967 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->mac_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001968 ret = 1;
1969 goto cleanup;
1970 }
1971 }
1972 break;
1973 }
1974 i++;
1975 }
1976 if (!supported_mac_algs[i]) {
1977 /* algorithm not supported */
1978 ERR(NULL, "MAC algorithm (%s) not supported by libssh.", alg);
1979 ret = 1;
1980 }
1981
1982cleanup:
1983 return ret;
1984}
1985
1986static int
roman83683fb2023-02-24 09:15:23 +01001987nc_server_create_unix_socket(struct nc_endpt *endpt)
1988{
1989 endpt->ti = NC_TI_UNIX;
1990 endpt->opts.unixsock = calloc(1, sizeof *endpt->opts.unixsock);
1991 if (!endpt->opts.unixsock) {
1992 ERRMEM;
1993 return 1;
1994 }
1995
1996 /* set default values */
1997 endpt->opts.unixsock->mode = -1;
1998 endpt->opts.unixsock->uid = -1;
1999 endpt->opts.unixsock->gid = -1;
2000
2001 return 0;
2002}
2003
2004static int
romane028ef92023-02-24 16:33:08 +01002005nc_server_config_unix_socket(const struct lyd_node *node, NC_OPERATION op)
roman83683fb2023-02-24 09:15:23 +01002006{
2007 int ret = 0;
2008 uint32_t prev_lo;
2009 struct nc_endpt *endpt;
2010 struct nc_bind *bind;
2011 struct nc_server_unix_opts *opts;
2012 struct lyd_node *data = NULL;
2013
2014 assert(!strcmp(LYD_NAME(node), "unix-socket"));
2015
2016 if (nc_server_get_endpt(node, &endpt, &bind)) {
2017 ret = 1;
2018 goto cleanup;
2019 }
2020
2021 if (op == NC_OP_CREATE) {
2022 if (nc_server_create_unix_socket(endpt)) {
2023 ret = 1;
2024 goto cleanup;
2025 }
2026
2027 opts = endpt->opts.unixsock;
2028
2029 lyd_find_path(node, "path", 0, &data);
2030 assert(data);
2031
2032 opts->address = strdup(lyd_get_value(data));
2033 bind->address = strdup(lyd_get_value(data));
2034 if (!opts->address || !bind->address) {
2035 ERRMEM;
2036 ret = 1;
2037 goto cleanup;
2038 }
2039
2040 /* silently search for non-mandatory parameters */
2041 prev_lo = ly_log_options(0);
2042 ret = lyd_find_path(node, "mode", 0, &data);
2043 if (!ret) {
2044 opts->mode = strtol(lyd_get_value(data), NULL, 8);
2045 }
2046
2047 ret = lyd_find_path(node, "uid", 0, &data);
2048 if (!ret) {
2049 opts->uid = strtol(lyd_get_value(data), NULL, 10);
2050 }
2051
2052 ret = lyd_find_path(node, "gid", 0, &data);
2053 if (!ret) {
2054 opts->gid = strtol(lyd_get_value(data), NULL, 10);
2055 }
2056
2057 /* reset the logging options */
2058 ly_log_options(prev_lo);
2059
2060 ret = nc_server_config_set_address_port(endpt, bind, NULL, 0);
2061 if (ret) {
2062 goto cleanup;
2063 }
2064 } else if (op == NC_OP_DELETE) {
2065 nc_server_del_unix_socket(bind, endpt->opts.unixsock);
2066 }
2067
2068cleanup:
2069 return ret;
2070}
2071
2072static int
romanc1d2b092023-02-02 08:58:27 +01002073nc_server_configure(const struct lyd_node *node, NC_OPERATION op)
2074{
2075 const char *name = LYD_NAME(node);
2076
2077 if (!strcmp(name, "listen")) {
romane028ef92023-02-24 16:33:08 +01002078 if (nc_server_config_listen(op)) {
romanc1d2b092023-02-02 08:58:27 +01002079 goto error;
2080 }
2081 } else if (!strcmp(name, "idle-timeout")) {
romane028ef92023-02-24 16:33:08 +01002082 if (nc_server_config_idle_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002083 goto error;
2084 }
2085 } else if (!strcmp(name, "endpoint")) {
romane028ef92023-02-24 16:33:08 +01002086 if (nc_server_config_endpoint(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002087 goto error;
2088 }
2089 } else if (!strcmp(name, "ssh")) {
romane028ef92023-02-24 16:33:08 +01002090 if (nc_server_config_ssh(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002091 goto error;
2092 }
2093 } else if (!strcmp(name, "local-address")) {
romane028ef92023-02-24 16:33:08 +01002094 if (nc_server_config_local_address(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002095 goto error;
2096 }
2097 } else if (!strcmp(name, "local-port")) {
romane028ef92023-02-24 16:33:08 +01002098 if (nc_server_config_local_port(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002099 goto error;
2100 }
2101 } else if (!strcmp(name, "keepalives")) {
romane028ef92023-02-24 16:33:08 +01002102 if (nc_server_config_keepalives(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002103 goto error;
2104 }
2105 } else if (!strcmp(name, "idle-time")) {
romane028ef92023-02-24 16:33:08 +01002106 if (nc_server_config_idle_time(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002107 goto error;
2108 }
2109 } else if (!strcmp(name, "max-probes")) {
romane028ef92023-02-24 16:33:08 +01002110 if (nc_server_config_max_probes(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002111 goto error;
2112 }
2113 } else if (!strcmp(name, "probe-interval")) {
romane028ef92023-02-24 16:33:08 +01002114 if (nc_server_config_probe_interval(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002115 goto error;
2116 }
2117 } else if (!strcmp(name, "host-key")) {
romane028ef92023-02-24 16:33:08 +01002118 if (nc_server_config_host_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002119 goto error;
2120 }
2121 } else if (!strcmp(name, "public-key-format")) {
romane028ef92023-02-24 16:33:08 +01002122 if (nc_server_config_public_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002123 goto error;
2124 }
2125 } else if (!strcmp(name, "public-key")) {
romane028ef92023-02-24 16:33:08 +01002126 if (nc_server_config_public_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002127 goto error;
2128 }
2129 } else if (!strcmp(name, "private-key-format")) {
romane028ef92023-02-24 16:33:08 +01002130 if (nc_server_config_private_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002131 goto error;
2132 }
2133 } else if (!strcmp(name, "cleartext-private-key")) {
romane028ef92023-02-24 16:33:08 +01002134 if (nc_server_config_cleartext_private_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002135 goto error;
2136 }
2137 } else if (!strcmp(name, "keystore-reference")) {
romane028ef92023-02-24 16:33:08 +01002138 if (nc_server_config_keystore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002139 goto error;
2140 }
2141 } else if (!strcmp(name, "user")) {
romane028ef92023-02-24 16:33:08 +01002142 if (nc_server_config_user(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002143 goto error;
2144 }
2145 } else if (!strcmp(name, "auth-attempts")) {
romane028ef92023-02-24 16:33:08 +01002146 if (nc_server_config_auth_attempts(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002147 goto error;
2148 }
2149 } else if (!strcmp(name, "auth-timeout")) {
romane028ef92023-02-24 16:33:08 +01002150 if (nc_server_config_auth_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002151 goto error;
2152 }
2153 } else if (!strcmp(name, "truststore-reference")) {
romane028ef92023-02-24 16:33:08 +01002154 if (nc_server_config_truststore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002155 goto error;
2156 }
2157 } else if (!strcmp(name, "password")) {
romane028ef92023-02-24 16:33:08 +01002158 if (nc_server_config_password(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002159 goto error;
2160 }
2161 } else if (!strcmp(name, "pam-config-file-name")) {
romane028ef92023-02-24 16:33:08 +01002162 if (nc_server_config_pam_name(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002163 goto error;
2164 }
2165 } else if (!strcmp(name, "pam-config-file-dir")) {
romane028ef92023-02-24 16:33:08 +01002166 if (nc_server_config_pam_dir(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002167 goto error;
2168 }
2169 } else if (!strcmp(name, "none")) {
romane028ef92023-02-24 16:33:08 +01002170 if (nc_server_config_none(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002171 goto error;
2172 }
2173 } else if (!strcmp(name, "host-key-alg")) {
romane028ef92023-02-24 16:33:08 +01002174 if (nc_server_config_host_key_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002175 goto error;
2176 }
2177 } else if (!strcmp(name, "key-exchange-alg")) {
romane028ef92023-02-24 16:33:08 +01002178 if (nc_server_config_kex_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002179 goto error;
2180 }
2181 } else if (!strcmp(name, "encryption-alg")) {
romane028ef92023-02-24 16:33:08 +01002182 if (nc_server_config_encryption_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002183 goto error;
2184 }
2185 } else if (!strcmp(name, "mac-alg")) {
romane028ef92023-02-24 16:33:08 +01002186 if (nc_server_config_mac_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002187 goto error;
2188 }
roman83683fb2023-02-24 09:15:23 +01002189 } else if (!strcmp(name, "unix-socket")) {
romane028ef92023-02-24 16:33:08 +01002190 if (nc_server_config_unix_socket(node, op)) {
roman83683fb2023-02-24 09:15:23 +01002191 goto error;
2192 }
romanc1d2b092023-02-02 08:58:27 +01002193 } 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,
2194 "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,
2195 "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,
2196 "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,
2197 "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,
2198 "id")) {} else if (!strcmp(name, "fingerprint")) {} else if (!strcmp(name, "map-type")) {}
2199
2200 return 0;
2201
2202error:
2203 ERR(NULL, "Configuring (%s) failed.", LYD_NAME(node));
2204 return 1;
2205}
2206
2207int
2208nc_session_server_parse_tree(const struct lyd_node *node, NC_OPERATION parent_op)
2209{
2210 struct lyd_node *child;
2211 struct lyd_meta *m;
2212 NC_OPERATION current_op;
2213
2214 assert(node);
2215
2216 /* get current op */
2217 LY_LIST_FOR(node->meta, m) {
2218 if (!strcmp(m->name, "operation")) {
2219 if (!strcmp(lyd_get_meta_value(m), "create")) {
2220 current_op = NC_OP_CREATE;
2221 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
2222 current_op = NC_OP_DELETE;
2223 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
2224 current_op = NC_OP_REPLACE;
2225 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
2226 current_op = NC_OP_NONE;
2227 }
2228 break;
2229 }
2230 }
2231
2232 /* node has no op, inherit from the parent */
2233 if (!m) {
2234 current_op = parent_op;
2235 }
2236
2237 switch (current_op) {
2238 case NC_OP_NONE:
2239 break;
2240 case NC_OP_CREATE:
2241 case NC_OP_DELETE:
2242 case NC_OP_REPLACE:
2243 if (nc_server_configure(node, current_op)) {
2244 return 1;
2245 }
2246 break;
2247 default:
2248 break;
2249 }
2250
2251 if (current_op != NC_OP_DELETE) {
2252 LY_LIST_FOR(lyd_child(node), child) {
2253 if (nc_session_server_parse_tree(child, current_op)) {
2254 return 1;
2255 }
2256 }
2257 }
2258 return 0;
2259}
2260
2261static int
roman8edee342023-03-31 13:25:48 +02002262nc_server_config_asymmetric_key_certificate(const struct lyd_node *tree, struct nc_asymmetric_key *key)
romanc1d2b092023-02-02 08:58:27 +01002263{
2264 int ret = 0;
roman45cec4e2023-02-17 10:21:39 +01002265 struct lyd_node *node;
romanc1d2b092023-02-02 08:58:27 +01002266 void *tmp;
2267
roman45cec4e2023-02-17 10:21:39 +01002268 /* create new certificate */
2269 tmp = realloc(key->certs, (key->cert_count + 1) * sizeof *key->certs);
2270 if (!tmp) {
2271 ERRMEM;
2272 ret = 1;
2273 goto cleanup;
2274 }
2275 key->certs = tmp;
2276 key->cert_count++;
2277
2278 /* set name */
2279 lyd_find_path(tree, "name", 0, &node);
2280 assert(node);
2281
2282 key->certs[key->cert_count - 1].name = strdup(lyd_get_value(node));
2283 if (!key->certs[key->cert_count - 1].name) {
2284 ERRMEM;
2285 ret = 1;
romanc1d2b092023-02-02 08:58:27 +01002286 goto cleanup;
2287 }
2288
roman45cec4e2023-02-17 10:21:39 +01002289 /* set certificate data */
2290 lyd_find_path(tree, "cert-data", 0, &node);
2291 assert(node);
romanc1d2b092023-02-02 08:58:27 +01002292
roman45cec4e2023-02-17 10:21:39 +01002293 key->certs[key->cert_count - 1].cert_base64 = strdup(lyd_get_value(node));
2294 if (!key->certs[key->cert_count - 1].cert_base64) {
2295 ERRMEM;
2296 ret = 1;
2297 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002298 }
2299
2300cleanup:
roman45cec4e2023-02-17 10:21:39 +01002301 return ret;
2302}
2303
2304static int
romane028ef92023-02-24 16:33:08 +01002305nc_server_config_asymmetric_key(const struct lyd_node *tree)
roman45cec4e2023-02-17 10:21:39 +01002306{
2307 int ret = 0;
2308 struct lyd_node *node = NULL, *iter;
2309 void *tmp;
2310 struct nc_keystore *ks = &server_opts.keystore;
roman8edee342023-03-31 13:25:48 +02002311 struct nc_asymmetric_key *key;
roman45cec4e2023-02-17 10:21:39 +01002312 const char *format;
2313
2314 /* create new asymmetric key */
2315 tmp = realloc(ks->asym_keys, (ks->asym_key_count + 1) * sizeof *ks->asym_keys);
2316 if (!tmp) {
2317 ERRMEM;
2318 ret = 1;
2319 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002320 }
roman45cec4e2023-02-17 10:21:39 +01002321 ks->asym_keys = tmp;
2322 memset(&ks->asym_keys[ks->asym_key_count], 0, sizeof *ks->asym_keys);
2323 key = &ks->asym_keys[ks->asym_key_count];
2324 ks->asym_key_count++;
2325
2326 /* set name */
2327 lyd_find_path(tree, "name", 0, &node);
2328 assert(node);
2329
2330 key->name = strdup(lyd_get_value(node));
2331 if (!key->name) {
2332 ERRMEM;
2333 ret = 1;
2334 goto cleanup;
2335 }
2336
2337 /* set public-key-format, mandatory */
2338 lyd_find_path(tree, "public-key-format", 0, &node);
2339 assert(node);
2340
2341 format = ((struct lyd_node_term *)node)->value.ident->name;
2342 if (!strcmp(format, "ssh-public-key-format")) {
roman45cec4e2023-02-17 10:21:39 +01002343 key->pubkey_type = NC_SSH_PUBKEY_SSH2;
romand57b3722023-04-05 11:26:25 +02002344 } else if (!strcmp(format, "subject-public-key-info-format")) {
2345 key->pubkey_type = NC_SSH_PUBKEY_X509;
roman45cec4e2023-02-17 10:21:39 +01002346 } else {
2347 ERR(NULL, "Public key format \"%s\" not supported.", format);
2348 ret = 1;
2349 goto cleanup;
2350 }
2351
2352 /* set public-key, mandatory */
2353 lyd_find_path(tree, "public-key", 0, &node);
2354 assert(node);
2355
2356 key->pub_base64 = strdup(lyd_get_value(node));
2357 if (!key->pub_base64) {
2358 ERRMEM;
2359 ret = 1;
2360 goto cleanup;
2361 }
2362
2363 /* set private-key-format */
2364 ret = lyd_find_path(tree, "private-key-format", 0, &node);
2365 if (!ret) {
2366 format = ((struct lyd_node_term *)node)->value.ident->name;
2367 if (!strcmp(format, "rsa-private-key-format")) {
2368 key->privkey_type = NC_SSH_KEY_RSA;
2369 } else if (!strcmp(format, "ec-private-key-format")) {
2370 key->privkey_type = NC_SSH_KEY_ECDSA;
2371 } else {
2372 ERR(NULL, "Private key format (%s) not supported.", format);
2373 ret = 1;
2374 goto cleanup;
2375 }
2376 }
2377
2378 /* set private key, mandatory */
2379 lyd_find_path(tree, "cleartext-private-key", 0, &node);
2380 assert(node);
2381
2382 key->priv_base64 = strdup(lyd_get_value(node));
2383 if (!key->priv_base64) {
2384 ERRMEM;
2385 ret = 1;
2386 goto cleanup;
2387 }
2388
2389 /* set certificates associated with the key pair */
2390 ret = lyd_find_path(tree, "certificates", 0, &node);
2391 if (!ret) {
2392 node = lyd_child(node);
2393 if (node) {
2394 /* certificate list instance */
2395 LY_LIST_FOR(node, iter) {
romane028ef92023-02-24 16:33:08 +01002396 if (nc_server_config_asymmetric_key_certificate(iter, key)) {
roman45cec4e2023-02-17 10:21:39 +01002397 ret = 1;
2398 goto cleanup;
2399 }
2400 }
2401 }
2402 } else if (ret == LY_ENOTFOUND) {
2403 /* certificates container not present, but it's ok */
2404 ret = 0;
2405 }
2406
2407cleanup:
2408 return ret;
2409}
2410
2411static int
romane028ef92023-02-24 16:33:08 +01002412nc_server_config_symmetric_key(const struct lyd_node *tree)
roman45cec4e2023-02-17 10:21:39 +01002413{
2414 int ret = 0;
2415 const char *format;
2416 struct lyd_node *node;
2417 struct nc_keystore *ks = &server_opts.keystore;
roman8edee342023-03-31 13:25:48 +02002418 struct nc_symmetric_key *key;
roman45cec4e2023-02-17 10:21:39 +01002419 void *tmp;
2420
2421 /* create new symmetric key */
2422 tmp = realloc(ks->sym_keys, (ks->sym_key_count + 1) * sizeof *ks->sym_keys);
romand57b3722023-04-05 11:26:25 +02002423 if (!tmp) {
roman45cec4e2023-02-17 10:21:39 +01002424 ERRMEM;
2425 ret = 1;
2426 goto cleanup;
2427 }
2428 memset(&ks->sym_keys[ks->sym_key_count], 0, sizeof *ks->sym_keys);
2429 ks->sym_keys = tmp;
2430 key = &ks->sym_keys[ks->sym_key_count];
2431 ks->sym_key_count++;
2432
2433 /* set name */
2434 lyd_find_path(tree, "name", 0, &node);
2435 assert(node);
2436
2437 key->name = strdup(lyd_get_value(node));
2438 if (!key->name) {
2439 ERRMEM;
2440 ret = 1;
2441 goto cleanup;
2442 }
2443
2444 /* check if the identity matches with the supported one */
2445 lyd_find_path(tree, "key-format", 0, &node);
2446 assert(node);
2447
2448 format = ((struct lyd_node_term *)node)->value.ident->name;
2449 if (strcmp(format, "symmetric-key-format")) {
2450 ret = 1;
2451 goto cleanup;
2452 }
2453
2454 /* set key data */
2455 lyd_find_path(tree, "cleartext-key", 0, &node);
2456 assert(node);
2457
2458 key->base64 = strdup(lyd_get_value(node));
2459 if (!key->base64) {
2460 ERRMEM;
2461 ret = 1;
2462 goto cleanup;
2463 }
2464
2465cleanup:
romanc1d2b092023-02-02 08:58:27 +01002466 return ret;
2467}
2468
2469static int
2470nc_fill_keystore(const struct lyd_node *data)
2471{
2472 int ret = 0;
2473 uint32_t prev_lo;
roman45cec4e2023-02-17 10:21:39 +01002474 struct lyd_node *tree, *as_keys, *s_keys, *iter;
romanc1d2b092023-02-02 08:58:27 +01002475
roman45cec4e2023-02-17 10:21:39 +01002476 /* silently search for nodes, some of them may not be present */
romanc1d2b092023-02-02 08:58:27 +01002477 prev_lo = ly_log_options(0);
roman45cec4e2023-02-17 10:21:39 +01002478
2479 ret = lyd_find_path(data, "/ietf-keystore:keystore", 0, &tree);
romanc1d2b092023-02-02 08:58:27 +01002480 if (ret) {
romand57b3722023-04-05 11:26:25 +02002481 VRB(NULL, "Keystore container not found in the YANG data.");
roman27215242023-03-10 14:55:00 +01002482 ret = 0;
roman45cec4e2023-02-17 10:21:39 +01002483 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002484 }
2485
roman45cec4e2023-02-17 10:21:39 +01002486 ret = lyd_find_path(tree, "asymmetric-keys", 0, &as_keys);
2487 if (!ret) {
2488 /* asymmetric keys container is present */
2489 as_keys = lyd_child(as_keys);
2490 if (as_keys && !strcmp(LYD_NAME(as_keys), "asymmetric-key")) {
2491 /* asymmetric key list */
2492 LY_LIST_FOR(as_keys, iter) {
romane028ef92023-02-24 16:33:08 +01002493 if (nc_server_config_asymmetric_key(iter)) {
roman45cec4e2023-02-17 10:21:39 +01002494 ret = 1;
2495 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002496 }
2497 }
romanc1d2b092023-02-02 08:58:27 +01002498 }
romanc1d2b092023-02-02 08:58:27 +01002499 }
2500
roman45cec4e2023-02-17 10:21:39 +01002501 ret = lyd_find_path(tree, "symmetric-keys", 0, &s_keys);
2502 if (!ret) {
2503 /* symmetric keys container is present */
2504 s_keys = lyd_child(s_keys);
2505 if (s_keys && !strcmp(LYD_NAME(s_keys), "symmetric-key")) {
2506 /* symmetric key list */
2507 LY_LIST_FOR(s_keys, iter) {
romane028ef92023-02-24 16:33:08 +01002508 if (nc_server_config_symmetric_key(iter)) {
roman45cec4e2023-02-17 10:21:39 +01002509 ret = 1;
2510 goto cleanup;
2511 }
2512 }
2513 }
2514 }
romanc1d2b092023-02-02 08:58:27 +01002515
roman45cec4e2023-02-17 10:21:39 +01002516cleanup:
2517 /* reset the logging options back to what they were */
2518 ly_log_options(prev_lo);
2519 return ret;
romanc1d2b092023-02-02 08:58:27 +01002520}
2521
romand57b3722023-04-05 11:26:25 +02002522static int
2523nc_server_config_create_truststore_certificate(const struct lyd_node *tree, struct nc_certificate_bag *bag)
2524{
2525 int ret = 0;
2526 struct lyd_node *node;
2527 void *tmp;
2528 struct nc_certificate *cert;
2529
2530 /* create new certificate */
2531 tmp = realloc(bag->certs, (bag->cert_count + 1) * sizeof *bag->certs);
2532 if (!tmp) {
2533 ERRMEM;
2534 ret = 1;
2535 goto cleanup;
2536 }
2537 memset(&bag->certs[bag->cert_count], 0, sizeof *bag->certs);
2538 bag->certs = tmp;
2539 cert = &bag->certs[bag->cert_count];
2540 bag->cert_count++;
2541
2542 /* set name */
2543 lyd_find_path(tree, "name", 0, &node);
2544 assert(node);
2545
2546 cert->name = strdup(lyd_get_value(node));
2547 if (!cert->name) {
2548 ERRMEM;
2549 ret = 1;
2550 goto cleanup;
2551 }
2552
2553 /* set cert data */
2554 lyd_find_path(tree, "cert-data", 0, &node);
2555 assert(node);
2556
2557 cert->cert_base64 = strdup(lyd_get_value(node));
2558 if (!cert->cert_base64) {
2559 ERRMEM;
2560 ret = 1;
2561 goto cleanup;
2562 }
2563
2564cleanup:
2565 return ret;
2566}
2567
2568static int
2569nc_server_config_certificate_bag(const struct lyd_node *tree)
2570{
2571 int ret = 0;
2572 struct lyd_node *node = NULL, *iter;
2573 void *tmp;
2574 struct nc_truststore *ts = &server_opts.truststore;
2575 struct nc_certificate_bag *bag;
2576
2577 /* create new certificate bag */
2578 tmp = realloc(ts->cert_bags, (ts->cert_bag_count + 1) * sizeof *ts->cert_bags);
2579 if (!tmp) {
2580 ERRMEM;
2581 ret = 1;
2582 goto cleanup;
2583 }
2584 ts->cert_bags = tmp;
2585 memset(&ts->cert_bags[ts->cert_bag_count], 0, sizeof *ts->cert_bags);
2586 bag = &ts->cert_bags[ts->cert_bag_count];
2587 ts->cert_bag_count++;
2588
2589 /* set name */
2590 lyd_find_path(tree, "name", 0, &node);
2591 assert(node);
2592
2593 bag->name = strdup(lyd_get_value(node));
2594 if (!bag->name) {
2595 ERRMEM;
2596 ret = 1;
2597 goto cleanup;
2598 }
2599
2600 /* set certificates associated with this bag */
2601 ret = lyd_find_path(tree, "certificate", 0, &node);
2602 if (!ret) {
2603 LY_LIST_FOR(node, iter) {
2604 if (nc_server_config_create_truststore_certificate(iter, bag)) {
2605 ret = 1;
2606 goto cleanup;
2607 }
2608 }
2609 } else if (ret == LY_ENOTFOUND) {
2610 /* certificate list not present, but it's ok */
2611 ret = 0;
2612 }
2613
2614cleanup:
2615 return ret;
2616}
2617
2618static int
2619nc_server_config_create_truststore_public_key(const struct lyd_node *tree, struct nc_public_key_bag *bag)
2620{
2621 int ret = 0;
2622 struct lyd_node *node;
2623 void *tmp;
2624 struct nc_public_key *key;
2625 const char *format;
2626
2627 /* create new public key */
2628 tmp = realloc(bag->pubkeys, (bag->pubkey_count + 1) * sizeof *bag->pubkeys);
2629 if (!tmp) {
2630 ERRMEM;
2631 ret = 1;
2632 goto cleanup;
2633 }
2634 bag->pubkeys = tmp;
2635 memset(&bag->pubkeys[bag->pubkey_count], 0, sizeof *bag->pubkeys);
2636 key = &bag->pubkeys[bag->pubkey_count];
2637 bag->pubkey_count++;
2638
2639 /* set name */
2640 lyd_find_path(tree, "name", 0, &node);
2641 assert(node);
2642
2643 key->name = strdup(lyd_get_value(node));
2644 if (!key->name) {
2645 ERRMEM;
2646 ret = 1;
2647 goto cleanup;
2648 }
2649
2650 /* set public-key-format, mandatory */
2651 lyd_find_path(tree, "public-key-format", 0, &node);
2652 assert(node);
2653
2654 format = ((struct lyd_node_term *)node)->value.ident->name;
2655 if (!strcmp(format, "ssh-public-key-format")) {
2656 key->pubkey_type = NC_SSH_PUBKEY_SSH2;
2657 } else if (!strcmp(format, "subject-public-key-info-format")) {
2658 key->pubkey_type = NC_SSH_PUBKEY_X509;
2659 } else {
2660 ERR(NULL, "Public key format \"%s\" not supported.", format);
2661 ret = 1;
2662 goto cleanup;
2663 }
2664
2665 /* set public key data */
2666 lyd_find_path(tree, "public-key", 0, &node);
2667 assert(node);
2668
2669 key->pub_base64 = strdup(lyd_get_value(node));
2670 if (!key->pub_base64) {
2671 ERRMEM;
2672 ret = 1;
2673 goto cleanup;
2674 }
2675
2676cleanup:
2677 return ret;
2678}
2679
2680static int
2681nc_server_config_public_key_bag(const struct lyd_node *tree)
2682{
2683 int ret = 0;
2684 struct lyd_node *node = NULL, *iter;
2685 void *tmp;
2686 struct nc_truststore *ts = &server_opts.truststore;
2687 struct nc_public_key_bag *bag;
2688 const struct lysc_node *schema;
2689
2690 /* create new public key bag */
2691 tmp = realloc(ts->pub_bags, (ts->pub_bag_count + 1) * sizeof *ts->pub_bags);
2692 if (!tmp) {
2693 ERRMEM;
2694 ret = 1;
2695 goto cleanup;
2696 }
2697 ts->pub_bags = tmp;
2698 memset(&ts->pub_bags[ts->pub_bag_count], 0, sizeof *ts->pub_bags);
2699 bag = &ts->pub_bags[ts->pub_bag_count];
2700 ts->pub_bag_count++;
2701
2702 /* set name */
2703 lyd_find_path(tree, "name", 0, &node);
2704 assert(node);
2705
2706 bag->name = strdup(lyd_get_value(node));
2707 if (!bag->name) {
2708 ERRMEM;
2709 ret = 1;
2710 goto cleanup;
2711 }
2712
2713 /* get the schema node of public key so we can iterate over it's list */
2714 schema = lys_find_path(NULL, tree->schema, "public-key", 0);
2715 LYD_LIST_FOR_INST(node, schema, iter) {
2716 /* set public keys associated with this bag */
2717 if (nc_server_config_create_truststore_public_key(iter, bag)) {
2718 ret = 1;
2719 goto cleanup;
2720 }
2721 }
2722
2723cleanup:
2724 return ret;
2725}
2726
2727static int
2728nc_fill_truststore(const struct lyd_node *data)
2729{
2730 int ret = 0;
2731 struct lyd_node *tree, *cert_bags, *pub_bags, *iter;
2732 uint32_t prev_lo;
2733
2734 /* silently search for nodes, some of them may not be present */
2735 prev_lo = ly_log_options(0);
2736
2737 ret = lyd_find_path(data, "/ietf-truststore:truststore", 0, &tree);
2738 if (ret) {
2739 VRB(NULL, "Truststore container not found in the YANG data.");
2740 ret = 0;
2741 goto cleanup;
2742 }
2743
2744 ret = lyd_find_path(tree, "certificate-bags", 0, &cert_bags);
2745 if (!ret) {
2746 /* certificate bags container is present */
2747 cert_bags = lyd_child(cert_bags);
2748 if (cert_bags && !strcmp(LYD_NAME(cert_bags), "certificate-bag")) {
2749 /* certificate bag list */
2750 LY_LIST_FOR(cert_bags, iter) {
2751 if (nc_server_config_certificate_bag(iter)) {
2752 ret = 1;
2753 goto cleanup;
2754 }
2755 }
2756 }
2757 }
2758
2759 ret = lyd_find_path(tree, "public-key-bags", 0, &pub_bags);
2760 if (!ret) {
2761 /* public key bags container is present */
2762 pub_bags = lyd_child(pub_bags);
2763 if (pub_bags && !strcmp(LYD_NAME(pub_bags), "public-key-bag")) {
2764 /* public key bag list */
2765 LY_LIST_FOR(pub_bags, iter) {
2766 if (nc_server_config_public_key_bag(iter)) {
2767 ret = 1;
2768 goto cleanup;
2769 }
2770 }
2771 }
2772 } else if (ret == LY_ENOTFOUND) {
2773 /* it's not mandatory so it's ok */
2774 ret = 0;
2775 }
2776
2777cleanup:
2778 /* reset the logging options back to what they were */
2779 ly_log_options(prev_lo);
2780 return ret;
2781}
2782
romanc1d2b092023-02-02 08:58:27 +01002783API int
2784nc_server_config_load_modules(struct ly_ctx **ctx)
2785{
2786 int i, new_ctx = 0;
2787
2788 if (!*ctx) {
2789 if (ly_ctx_new(NC_SERVER_SEARCH_DIR, 0, ctx)) {
2790 ERR(NULL, "Couldn't create new libyang context.\n");
2791 goto error;
2792 }
2793 new_ctx = 1;
2794 }
2795
2796 /* all features */
2797 const char *ietf_nectonf_server[] = {"ssh-listen", "tls-listen", "ssh-call-home", "tls-call-home", "central-netconf-server-supported", NULL};
2798 /* all features */
2799 const char *ietf_x509_cert_to_name[] = {NULL};
2800 /* no private-key-encryption and csr-generation */
2801 const char *ietf_crypto_types[] = {
2802 "one-symmetric-key-format", "one-asymmetric-key-format", "symmetrically-encrypted-value-format",
2803 "asymmetrically-encrypted-value-format", "cms-enveloped-data-format", "cms-encrypted-data-format",
2804 "p10-based-csrs", "certificate-expiration-notification", "hidden-keys", "password-encryption",
2805 "symmetric-key-encryption", NULL
2806 };
2807 /* all features */
2808 const char *ietf_tcp_common[] = {"keepalives-supported", NULL};
2809 /* no ssh-x509-certs */
2810 const char *ietf_ssh_common[] = {"transport-params", "public-key-generation", NULL};
2811 /* all features */
2812 const char *iana_ssh_encryption_algs[] = {NULL};
2813 /* all features */
2814 const char *iana_ssh_key_exchange_algs[] = {NULL};
2815 /* all features */
2816 const char *iana_ssh_mac_algs[] = {NULL};
2817 /* all features */
2818 const char *iana_ssh_public_key_algs[] = {NULL};
2819 /* all features */
2820 const char *ietf_keystore[] = {"central-keystore-supported", "local-definitions-supported", "asymmetric-keys", "symmetric-keys", NULL};
2821 /* no ssh-server-keepalives and local-user-auth-hostbased */
2822 const char *ietf_ssh_server[] = {"local-users-supported", "local-user-auth-publickey", "local-user-auth-password", "local-user-auth-none", NULL};
2823 /* all features */
2824 const char *ietf_truststore[] = {"central-truststore-supported", "local-definitions-supported", "certificates", "public-keys", NULL};
2825 /* all features */
2826 const char *ietf_tls_server[] = {
2827 "tls-server-keepalives", "server-ident-x509-cert", "server-ident-raw-public-key", "server-ident-tls12-psk",
2828 "server-ident-tls13-epsk", "client-auth-supported", "client-auth-x509-cert", "client-auth-raw-public-key",
2829 "client-auth-tls12-psk", "client-auth-tls13-epsk", NULL
2830 };
2831 /* all features */
2832 const char *libnetconf2_netconf_server[] = {NULL};
2833
2834 const char *module_names[] = {
2835 "ietf-netconf-server", "ietf-x509-cert-to-name", "ietf-crypto-types",
2836 "ietf-tcp-common", "ietf-ssh-common", "iana-ssh-encryption-algs",
2837 "iana-ssh-key-exchange-algs", "iana-ssh-mac-algs", "iana-ssh-public-key-algs",
2838 "ietf-keystore", "ietf-ssh-server", "ietf-truststore",
2839 "ietf-tls-server", "libnetconf2-netconf-server", NULL
2840 };
2841
2842 const char **module_features[] = {
2843 ietf_nectonf_server, ietf_x509_cert_to_name, ietf_crypto_types,
2844 ietf_tcp_common, ietf_ssh_common, iana_ssh_encryption_algs,
2845 iana_ssh_key_exchange_algs, iana_ssh_mac_algs, iana_ssh_public_key_algs,
2846 ietf_keystore, ietf_ssh_server, ietf_truststore,
2847 ietf_tls_server, libnetconf2_netconf_server, NULL
2848 };
2849
2850 for (i = 0; module_names[i] != NULL; i++) {
2851 if (!ly_ctx_load_module(*ctx, module_names[i], NULL, module_features[i])) {
2852 ERR(NULL, "Loading module \"%s\" failed.\n", module_names[i]);
2853 goto error;
2854 }
2855 }
2856
2857 return 0;
2858
2859error:
2860 if (new_ctx) {
2861 ly_ctx_destroy(*ctx);
2862 *ctx = NULL;
2863 }
2864 return 1;
2865}
2866
2867API int
2868nc_server_config_setup_path(const struct ly_ctx *ctx, const char *path)
2869{
2870 struct lyd_node *tree = NULL;
2871 int ret = 0;
2872
2873 if (!path) {
2874 ERRARG("Missing path parameter.");
2875 ret = 1;
2876 goto cleanup;
2877 }
2878
2879 ret = lyd_parse_data_path(ctx, path, LYD_XML, LYD_PARSE_NO_STATE | LYD_PARSE_STRICT, LYD_VALIDATE_NO_STATE, &tree);
2880 if (ret) {
2881 goto cleanup;
2882 }
2883
2884 ret = nc_server_config_setup(tree);
2885 if (ret) {
2886 goto cleanup;
2887 }
2888
2889cleanup:
2890 lyd_free_all(tree);
2891 return ret;
2892}
2893
2894API int
2895nc_server_config_setup(const struct lyd_node *data)
2896{
2897 int ret = 0;
2898 struct lyd_node *tree;
2899 struct lyd_meta *m;
roman27215242023-03-10 14:55:00 +01002900 NC_OPERATION op = NC_OP_NONE;
romanc1d2b092023-02-02 08:58:27 +01002901
2902 /* LOCK */
2903 pthread_rwlock_wrlock(&server_opts.config_lock);
2904
2905 ret = nc_fill_keystore(data);
2906 if (ret) {
2907 ERR(NULL, "Filling keystore failed.");
2908 goto cleanup;
2909 }
2910
romand57b3722023-04-05 11:26:25 +02002911 ret = nc_fill_truststore(data);
2912 if (ret) {
2913 ERR(NULL, "Filling truststore failed.");
2914 goto cleanup;
2915 }
2916
romanc1d2b092023-02-02 08:58:27 +01002917 ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &tree);
2918 if (ret) {
2919 ERR(NULL, "Unable to find the netconf-server container in the YANG data.");
2920 goto cleanup;
2921 }
2922
2923 LY_LIST_FOR(tree->meta, m) {
2924 if (!strcmp(m->name, "operation")) {
2925 if (!strcmp(lyd_get_meta_value(m), "create")) {
2926 op = NC_OP_CREATE;
2927 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
2928 op = NC_OP_DELETE;
2929 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
2930 op = NC_OP_REPLACE;
2931 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
2932 op = NC_OP_NONE;
2933 } else {
2934 ERR(NULL, "Unexpected operation (%s).", lyd_get_meta_value(m));
2935 ret = 1;
2936 goto cleanup;
2937 }
2938 }
2939 }
2940
2941 if (nc_session_server_parse_tree(tree, op)) {
2942 ret = 1;
2943 goto cleanup;
2944 }
2945
2946cleanup:
2947 /* UNLOCK */
2948 pthread_rwlock_unlock(&server_opts.config_lock);
2949 return ret;
2950}