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