blob: f11d28996c98001f29e26994063a5328266c150e [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
199nc_server_get_pubkey(const struct lyd_node *node, const struct nc_client_auth *auth_client, struct nc_client_auth_pubkey **pubkey)
200{
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{
299 free(hostkey->pub_base64);
300 hostkey->pub_base64 = NULL;
301}
302
303static void
304nc_server_del_truststore_reference(struct nc_client_auth *client_auth)
305{
306 free(client_auth->ts_reference);
307 client_auth->ts_reference = NULL;
308}
309
310static void
311nc_server_del_private_key(struct nc_hostkey *hostkey)
312{
313 free(hostkey->priv_base64);
314 hostkey->priv_base64 = NULL;
315}
316
317static void
romanc1d2b092023-02-02 08:58:27 +0100318nc_server_del_auth_client_username(struct nc_client_auth *auth_client)
319{
320 free(auth_client->username);
321 auth_client->username = NULL;
322}
323
324static void
325nc_server_del_auth_client_pubkey_name(struct nc_client_auth_pubkey *pubkey)
326{
327 free(pubkey->name);
328 pubkey->name = NULL;
329}
330
331static void
332nc_server_del_auth_client_pubkey_pub_base64(struct nc_client_auth_pubkey *pubkey)
333{
334 free(pubkey->pub_base64);
335 pubkey->pub_base64 = NULL;
336}
337
338static void
339nc_server_del_auth_client_ts_reference(struct nc_client_auth *auth_client)
340{
341 free(auth_client->ts_reference);
342 auth_client->ts_reference = NULL;
343}
344
345static void
346nc_server_del_auth_client_password(struct nc_client_auth *auth_client)
347{
348 free(auth_client->password);
349 auth_client->password = NULL;
350}
351
352static void
353nc_server_del_hostkey_algs(struct nc_server_ssh_opts *opts)
354{
355 free(opts->hostkey_algs);
356 opts->hostkey_algs = NULL;
357}
358
359static void
360nc_server_del_kex_algs(struct nc_server_ssh_opts *opts)
361{
362 free(opts->kex_algs);
363 opts->kex_algs = NULL;
364}
365
366static void
367nc_server_del_encryption_algs(struct nc_server_ssh_opts *opts)
368{
369 free(opts->encryption_algs);
370 opts->encryption_algs = NULL;
371}
372
373static void
374nc_server_del_mac_algs(struct nc_server_ssh_opts *opts)
375{
376 free(opts->mac_algs);
377 opts->mac_algs = NULL;
378}
379
380static void
381nc_server_del_hostkey(struct nc_server_ssh_opts *opts, struct nc_hostkey *hostkey)
382{
383 assert(hostkey->ks_type == NC_STORE_LOCAL || hostkey->ks_type == NC_STORE_KEYSTORE);
384
385 if (hostkey->ks_type == NC_STORE_LOCAL) {
386 nc_server_del_public_key(hostkey);
387 nc_server_del_private_key(hostkey);
romanc1d2b092023-02-02 08:58:27 +0100388 }
389
390 nc_server_del_hostkey_name(hostkey);
391 opts->hostkey_count--;
392 if (!opts->hostkey_count) {
393 free(opts->hostkeys);
394 opts->hostkeys = NULL;
395 }
396}
397
398static void
399nc_server_del_auth_client_pubkey(struct nc_client_auth *auth_client, struct nc_client_auth_pubkey *pubkey)
400{
401 nc_server_del_auth_client_pubkey_name(pubkey);
402 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
403
404 auth_client->pubkey_count--;
405 if (!auth_client->pubkey_count) {
406 free(auth_client->pubkeys);
407 auth_client->pubkeys = NULL;
408 }
409}
410
411static void
412nc_server_del_auth_client(struct nc_server_ssh_opts *opts, struct nc_client_auth *auth_client)
413{
414 uint16_t i, pubkey_count;
415
416 if (auth_client->ks_type == NC_STORE_LOCAL) {
417 pubkey_count = auth_client->pubkey_count;
418 for (i = 0; i < pubkey_count; i++) {
419 nc_server_del_auth_client_pubkey(auth_client, &auth_client->pubkeys[i]);
420 }
421 } else if (auth_client->ks_type == NC_STORE_TRUSTSTORE) {
422 nc_server_del_auth_client_ts_reference(auth_client);
423 } else {
424 return;
425 }
426
427 nc_server_del_auth_client_password(auth_client);
428 nc_server_del_auth_client_pam_name(auth_client);
429 nc_server_del_auth_client_pam_dir(auth_client);
430 nc_server_del_auth_client_username(auth_client);
431
432 opts->client_count--;
433 if (!opts->client_count) {
434 free(opts->auth_clients);
435 opts->auth_clients = NULL;
436 }
437}
438
439static void
440nc_server_del_ssh(struct nc_bind *bind, struct nc_server_ssh_opts *opts)
441{
442 uint16_t i, hostkey_count, client_count;
443
444 nc_server_del_local_address(bind);
445 if (bind->sock > -1) {
446 close(bind->sock);
447 }
448
449 /* store in variable because it gets decremented in the function call */
450 hostkey_count = opts->hostkey_count;
451 for (i = 0; i < hostkey_count; i++) {
452 nc_server_del_hostkey(opts, &opts->hostkeys[i]);
453 }
454
455 client_count = opts->client_count;
456 for (i = 0; i < client_count; i++) {
457 nc_server_del_auth_client(opts, &opts->auth_clients[i]);
458 }
459
460 nc_server_del_hostkey_algs(opts);
461 nc_server_del_kex_algs(opts);
462 nc_server_del_encryption_algs(opts);
463 nc_server_del_mac_algs(opts);
464
465 free(opts);
466 opts = NULL;
467}
468
469void
470nc_server_del_endpt_ssh(struct nc_endpt *endpt, struct nc_bind *bind)
471{
472 nc_server_del_endpt_name(endpt);
473 nc_server_del_ssh(bind, endpt->opts.ssh);
474
475 server_opts.endpt_count--;
476 if (!server_opts.endpt_count) {
477 free(server_opts.endpts);
478 free(server_opts.binds);
479 server_opts.endpts = NULL;
480 server_opts.binds = NULL;
481 }
482}
483
roman45cec4e2023-02-17 10:21:39 +0100484void
roman83683fb2023-02-24 09:15:23 +0100485nc_server_del_unix_socket(struct nc_bind *bind, struct nc_server_unix_opts *opts)
486{
487 if (bind->sock > -1) {
488 close(bind->sock);
489 }
490
491 free(bind->address);
492 free(opts->address);
493
494 free(opts);
495 opts = NULL;
496}
497
498void
499nc_server_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *bind)
500{
501 nc_server_del_endpt_name(endpt);
502 nc_server_del_unix_socket(bind, endpt->opts.unixsock);
503
504 server_opts.endpt_count--;
505 if (!server_opts.endpt_count) {
506 free(server_opts.endpts);
507 free(server_opts.binds);
508 server_opts.endpts = NULL;
509 server_opts.binds = NULL;
510 }
511}
512
513void
roman45cec4e2023-02-17 10:21:39 +0100514nc_server_config_del_keystore(void)
515{
516 int i, j;
517 struct nc_keystore *ks = &server_opts.keystore;
518
519 /* delete all asymmetric keys */
520 for (i = 0; i < ks->asym_key_count; i++) {
521 free(ks->asym_keys[i].name);
522 free(ks->asym_keys[i].pub_base64);
523 free(ks->asym_keys[i].priv_base64);
524
525 for (j = 0; j < ks->asym_keys[i].cert_count; j++) {
526 /* free associated certificates */
527 free(ks->asym_keys[i].certs[j].name);
528 free(ks->asym_keys[i].certs[j].cert_base64);
529 }
530 free(ks->asym_keys[i].certs);
531 ks->asym_keys[i].cert_count = 0;
532 }
533 free(ks->asym_keys);
534 ks->asym_key_count = 0;
535
536 /* delete all symmetric keys */
537 for (i = 0; i < ks->sym_key_count; i++) {
538 free(ks->sym_keys[i].name);
539 free(ks->sym_keys[i].base64);
540 }
541 free(ks->sym_keys);
542 ks->sym_key_count = 0;
543}
544
romanc1d2b092023-02-02 08:58:27 +0100545/* presence container */
546int
romane028ef92023-02-24 16:33:08 +0100547nc_server_config_listen(NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100548{
549 uint16_t i;
550
551 assert(op == NC_OP_CREATE || op == NC_OP_DELETE);
552
553 if (op == NC_OP_DELETE) {
554 for (i = 0; i < server_opts.endpt_count; i++) {
roman83683fb2023-02-24 09:15:23 +0100555 if (server_opts.endpts[i].ti == NC_TI_LIBSSH) {
556 nc_server_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]);
557 } else if (server_opts.endpts[i].ti == NC_TI_OPENSSL) {
558 /* todo */
559 } else {
560 nc_server_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]);
561 }
romanc1d2b092023-02-02 08:58:27 +0100562 }
563 }
564
565 return 0;
566}
567
568/* default leaf */
569static int
romane028ef92023-02-24 16:33:08 +0100570nc_server_config_idle_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100571{
572 assert(!strcmp(LYD_NAME(node), "idle-timeout"));
573
574 if (equal_parent_name(node, 1, "listen")) {
575 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
576 server_opts.idle_timeout = strtoul(lyd_get_value(node), NULL, 10);
577 } else {
578 /* default value */
579 server_opts.idle_timeout = 3600;
580 }
581 }
582
583 return 0;
584}
585
586static int
587nc_server_create_bind(void)
588{
589 int ret = 0;
590 void *tmp;
591
592 tmp = realloc(server_opts.binds, (server_opts.endpt_count + 1) * sizeof *server_opts.binds);
593 if (!tmp) {
594 ERRMEM;
595 ret = 1;
596 goto cleanup;
597 }
598 server_opts.binds = tmp;
599 memset(&server_opts.binds[server_opts.endpt_count], 0, sizeof *server_opts.binds);
600
601 server_opts.binds[server_opts.endpt_count].sock = -1;
602
603cleanup:
604 return ret;
605}
606
607static int
608nc_server_create_endpoint(const struct lyd_node *node)
609{
610 int ret = 0;
611 void *tmp;
612
613 tmp = realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts);
614 if (!tmp) {
615 ERRMEM;
616 ret = 1;
617 goto cleanup;
618 }
619 server_opts.endpts = tmp;
620 memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts);
621
622 node = lyd_child(node);
623 assert(!strcmp(LYD_NAME(node), "name"));
624
625 server_opts.endpts[server_opts.endpt_count].name = strdup(lyd_get_value(node));
626 if (!server_opts.endpts[server_opts.endpt_count].name) {
627 ERRMEM;
628 ret = 1;
629 goto cleanup;
630 }
631
632 if (nc_server_create_bind()) {
633 ret = 1;
634 goto cleanup;
635 }
636
637 server_opts.endpt_count++;
638
639cleanup:
640 return ret;
641}
642
643/* list */
644static int
romane028ef92023-02-24 16:33:08 +0100645nc_server_config_endpoint(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100646{
647 int ret = 0;
648 struct nc_endpt *endpt;
649 struct nc_bind *bind;
650
651 assert(!strcmp(LYD_NAME(node), "endpoint"));
652
653 if (op == NC_OP_CREATE) {
654 ret = nc_server_create_endpoint(node);
655 if (ret) {
656 goto cleanup;
657 }
658 } else if (op == NC_OP_DELETE) {
659 /* free all children */
660 if (nc_server_get_endpt(node, &endpt, &bind)) {
661 ret = 1;
662 goto cleanup;
663 }
664 nc_server_del_endpt_ssh(endpt, bind);
665 }
666
667cleanup:
668 return ret;
669}
670
671static int
672nc_server_create_ssh(struct nc_endpt *endpt)
673{
674 endpt->ti = NC_TI_LIBSSH;
675 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
676 if (!endpt->opts.ssh) {
677 ERRMEM;
678 return 1;
679 }
680
681 return 0;
682}
683
684/* NP container */
685static int
romane028ef92023-02-24 16:33:08 +0100686nc_server_config_ssh(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100687{
688 struct nc_endpt *endpt;
689 struct nc_bind *bind;
690 int ret = 0;
691
692 assert(!strcmp(LYD_NAME(node), "ssh"));
693
694 if (nc_server_get_endpt(node, &endpt, &bind)) {
695 ret = 1;
696 goto cleanup;
697 }
698
699 if (op == NC_OP_CREATE) {
700 ret = nc_server_create_ssh(endpt);
701 if (ret) {
702 goto cleanup;
703 }
704 } else if (op == NC_OP_DELETE) {
705 nc_server_del_ssh(bind, endpt->opts.ssh);
706 }
707
708cleanup:
709 return ret;
710}
711
712static int
713nc_server_config_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port)
714{
715 int sock = -1, set_addr, ret = 0;
716
roman83683fb2023-02-24 09:15:23 +0100717 assert((address && !port) || (!address && port) || (endpt->ti == NC_TI_UNIX));
romanc1d2b092023-02-02 08:58:27 +0100718
719 if (address) {
720 set_addr = 1;
721 } else {
722 set_addr = 0;
723 }
724
725 if (set_addr) {
726 port = bind->port;
727 } else {
728 address = bind->address;
729 }
730
romanc1d2b092023-02-02 08:58:27 +0100731 /* we have all the information we need to create a listening socket */
roman83683fb2023-02-24 09:15:23 +0100732 if ((address && port) || (endpt->ti == NC_TI_UNIX)) {
romanc1d2b092023-02-02 08:58:27 +0100733 /* create new socket, close the old one */
roman83683fb2023-02-24 09:15:23 +0100734 if (endpt->ti == NC_TI_UNIX) {
735 sock = nc_sock_listen_unix(endpt->opts.unixsock);
736 } else {
737 sock = nc_sock_listen_inet(address, port, &endpt->ka);
738 }
739
romanc1d2b092023-02-02 08:58:27 +0100740 if (sock == -1) {
741 ret = 1;
742 goto cleanup;
743 }
744
745 if (bind->sock > -1) {
746 close(bind->sock);
747 }
748 bind->sock = sock;
749 }
750
751 if (sock > -1) {
752 switch (endpt->ti) {
roman83683fb2023-02-24 09:15:23 +0100753 case NC_TI_UNIX:
754 VRB(NULL, "Listening on %s for UNIX connections.", endpt->opts.unixsock->address);
755 break;
romanc1d2b092023-02-02 08:58:27 +0100756#ifdef NC_ENABLED_SSH
757 case NC_TI_LIBSSH:
758 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
759 break;
760#endif
761#ifdef NC_ENABLED_TLS
762 case NC_TI_OPENSSL:
763 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
764 break;
765#endif
766 default:
767 ERRINT;
768 ret = 1;
769 break;
770 }
771 }
772
773cleanup:
774 return ret;
775}
776
777/* mandatory leaf */
778static int
romane028ef92023-02-24 16:33:08 +0100779nc_server_config_local_address(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100780{
781 struct nc_endpt *endpt;
782 struct nc_bind *bind;
783 int ret = 0;
784
785 (void) op;
786
787 assert(!strcmp(LYD_NAME(node), "local-address"));
788
789 if (equal_parent_name(node, 4, "listen")) {
790 if (nc_server_get_endpt(node, &endpt, &bind)) {
791 ret = 1;
792 goto cleanup;
793 }
794
795 nc_server_del_local_address(bind);
796 bind->address = strdup(lyd_get_value(node));
797 if (!bind->address) {
798 ERRMEM;
799 ret = 1;
800 goto cleanup;
801 }
802
803 ret = nc_server_config_set_address_port(endpt, bind, lyd_get_value(node), 0);
804 if (ret) {
805 goto cleanup;
806 }
807 }
808
809cleanup:
810 return ret;
811}
812
813/* leaf with default value */
814static int
romane028ef92023-02-24 16:33:08 +0100815nc_server_config_local_port(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 assert(!strcmp(LYD_NAME(node), "local-port"));
822
823 if (equal_parent_name(node, 4, "listen")) {
824 if (nc_server_get_endpt(node, &endpt, &bind)) {
825 ret = 1;
826 goto cleanup;
827 }
828
829 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
830 bind->port = strtoul(lyd_get_value(node), NULL, 10);
831 } else {
832 /* delete -> set to default */
833 bind->port = 0;
834 }
835
836 ret = nc_server_config_set_address_port(endpt, bind, NULL, bind->port);
837 if (ret) {
838 goto cleanup;
839 }
840 }
841
842cleanup:
843 return ret;
844}
845
846/* P container */
847static int
romane028ef92023-02-24 16:33:08 +0100848nc_server_config_keepalives(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100849{
850 struct nc_endpt *endpt;
851 struct nc_bind *bind;
852 int ret = 0;
853
854 assert(!strcmp(LYD_NAME(node), "keepalives"));
855
856 if (equal_parent_name(node, 4, "listen")) {
857 if (nc_server_get_endpt(node, &endpt, &bind)) {
858 ret = 1;
859 goto cleanup;
860 }
861
862 if (op == NC_OP_CREATE) {
863 endpt->ka.enabled = 1;
864 } else {
865 endpt->ka.enabled = 0;
866 }
867 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
868 if (ret) {
869 goto cleanup;
870 }
871 }
872
873cleanup:
874 return ret;
875}
876
877/* mandatory leaf */
878static int
romane028ef92023-02-24 16:33:08 +0100879nc_server_config_idle_time(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100880{
881 struct nc_endpt *endpt;
882 struct nc_bind *bind;
883 int ret = 0;
884
885 assert(!strcmp(LYD_NAME(node), "idle-time"));
886
887 if (equal_parent_name(node, 4, "listen")) {
888 if (nc_server_get_endpt(node, &endpt, &bind)) {
889 ret = 1;
890 goto cleanup;
891 }
892
893 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
894 endpt->ka.idle_time = strtoul(lyd_get_value(node), NULL, 10);
895 } else {
896 endpt->ka.idle_time = 0;
897 }
898 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
899 if (ret) {
900 goto cleanup;
901 }
902 }
903
904cleanup:
905 return ret;
906}
907
908/* mandatory leaf */
909static int
romane028ef92023-02-24 16:33:08 +0100910nc_server_config_max_probes(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100911{
912 struct nc_endpt *endpt;
913 struct nc_bind *bind;
914 int ret = 0;
915
916 assert(!strcmp(LYD_NAME(node), "max-probes"));
917
918 if (equal_parent_name(node, 4, "listen")) {
919 if (nc_server_get_endpt(node, &endpt, &bind)) {
920 ret = 1;
921 goto cleanup;
922 }
923
924 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
925 endpt->ka.max_probes = strtoul(lyd_get_value(node), NULL, 10);
926 } else {
927 endpt->ka.max_probes = 0;
928 }
929 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
930 if (ret) {
931 goto cleanup;
932 }
933 }
934
935cleanup:
936 return ret;
937}
938
939/* mandatory leaf */
940static int
romane028ef92023-02-24 16:33:08 +0100941nc_server_config_probe_interval(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100942{
943 struct nc_endpt *endpt;
944 struct nc_bind *bind;
945 int ret = 0;
946
947 assert(!strcmp(LYD_NAME(node), "probe-interval"));
948
949 if (equal_parent_name(node, 4, "listen")) {
950 if (nc_server_get_endpt(node, &endpt, &bind)) {
951 ret = 1;
952 goto cleanup;
953 }
954
955 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
956 endpt->ka.probe_interval = strtoul(lyd_get_value(node), NULL, 10);
957 } else {
958 endpt->ka.probe_interval = 0;
959 }
960 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
961 if (ret) {
962 goto cleanup;
963 }
964 }
965
966cleanup:
967 return ret;
968}
969
970static int
971nc_server_create_host_key(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
972{
973 int ret = 0;
974 void *tmp;
975
976 tmp = realloc(opts->hostkeys,
977 (opts->hostkey_count + 1) * sizeof *opts->hostkeys);
978 if (!tmp) {
979 ERRMEM;
980 ret = 1;
981 goto cleanup;
982 }
983 opts->hostkeys = tmp;
984
985 memset(&opts->hostkeys[opts->hostkey_count], 0, sizeof *opts->hostkeys);
986
987 opts->hostkeys[opts->hostkey_count].name = strdup(lyd_get_value(lyd_child(node)));
988 if (!opts->hostkeys[opts->hostkey_count].name) {
989 ERRMEM;
990 ret = 1;
991 goto cleanup;
992 }
993
994 /* set union selector */
995 lyd_find_path(node, "public-key", 0, (struct lyd_node **)&node);
996 assert(node);
997
998 if (!lyd_find_path(node, "local-definition", 0, NULL)) {
999 opts->hostkeys[opts->hostkey_count].ks_type = NC_STORE_LOCAL;
1000 } else {
1001 opts->hostkeys[opts->hostkey_count].ks_type = NC_STORE_KEYSTORE;
1002 }
1003
1004 opts->hostkey_count++;
1005
1006cleanup:
1007 return ret;
1008}
1009
1010/* list */
1011static int
romane028ef92023-02-24 16:33:08 +01001012nc_server_config_host_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001013{
1014 struct nc_endpt *endpt;
1015 struct nc_hostkey *hostkey;
1016 int ret = 0;
1017
1018 assert(!strcmp(LYD_NAME(node), "host-key"));
1019
1020 if ((equal_parent_name(node, 1, "server-identity")) && (equal_parent_name(node, 5, "listen"))) {
1021 if (nc_server_get_endpt(node, &endpt, NULL)) {
1022 ret = 1;
1023 goto cleanup;
1024 }
1025
1026 if (op == NC_OP_CREATE) {
1027 ret = nc_server_create_host_key(node, endpt->opts.ssh);
1028 if (ret) {
1029 goto cleanup;
1030 }
1031 } else if (op == NC_OP_DELETE) {
1032 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1033 ret = 1;
1034 goto cleanup;
1035 }
1036
1037 nc_server_del_hostkey(endpt->opts.ssh, hostkey);
1038 }
1039 } else if (equal_parent_name(node, 1, "transport-params")) {
1040 /* just a container with the name host-key, nothing to be done */
1041 goto cleanup;
1042 } else {
1043 ERRINT;
1044 ret = 1;
1045 goto cleanup;
1046 }
1047
1048cleanup:
1049 return ret;
1050}
1051
1052/* mandatory leaf */
romane028ef92023-02-24 16:33:08 +01001053static int
1054nc_server_config_public_key_format(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001055{
1056 const char *format;
1057 struct nc_endpt *endpt;
1058 struct nc_client_auth *auth_client;
1059 struct nc_client_auth_pubkey *pubkey;
1060 struct nc_hostkey *hostkey;
1061 int ret = 0;
1062
1063 assert(!strcmp(LYD_NAME(node), "public-key-format"));
1064
1065 format = ((struct lyd_node_term *)node)->value.ident->name;
1066
1067 if ((equal_parent_name(node, 6, "client-authentication")) && (equal_parent_name(node, 10, "listen"))) {
1068 if (nc_server_get_endpt(node, &endpt, NULL)) {
1069 ret = 1;
1070 goto cleanup;
1071 }
1072
1073 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1074 ret = 1;
1075 goto cleanup;
1076 }
1077
1078 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1079 ret = 1;
1080 goto cleanup;
1081 }
1082
1083 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1084 if (!strcmp(format, "ssh-public-key-format")) {
1085 pubkey->pubkey_type = NC_SSH_PUBKEY_X509;
1086 } else if (!strcmp(format, "subject-public-key-info-format")) {
1087 pubkey->pubkey_type = NC_SSH_PUBKEY_SSH2;
1088 } else {
1089 ERR(NULL, "Public key format (%s) not supported.", format);
1090 }
1091 }
1092 } else if ((equal_parent_name(node, 5, "server-identity")) && (equal_parent_name(node, 11, "listen"))) {
1093 if (nc_server_get_endpt(node, &endpt, NULL)) {
1094 ret = 1;
1095 goto cleanup;
1096 }
1097
1098 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1099 ret = 1;
1100 goto cleanup;
1101 }
1102
1103 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1104 if (!strcmp(format, "ssh-public-key-format")) {
romanc1d2b092023-02-02 08:58:27 +01001105 hostkey->pubkey_type = NC_SSH_PUBKEY_SSH2;
romane028ef92023-02-24 16:33:08 +01001106 } else if (!strcmp(format, "subject-public-key-info-format")) {
1107 hostkey->pubkey_type = NC_SSH_PUBKEY_X509;
romanc1d2b092023-02-02 08:58:27 +01001108 } else {
1109 ERR(NULL, "Public key format (%s) not supported.", format);
1110 }
1111 }
1112 }
1113
1114cleanup:
1115 return ret;
1116}
1117
1118/* leaf */
romane028ef92023-02-24 16:33:08 +01001119static int
1120nc_server_config_private_key_format(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001121{
1122 const char *format;
1123 struct nc_endpt *endpt;
1124 struct nc_hostkey *hostkey;
1125 int ret = 0;
1126
1127 assert(!strcmp(LYD_NAME(node), "private-key-format"));
1128
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 format = ((struct lyd_node_term *)node)->value.ident->name;
1140 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1141 if (!strcmp(format, "rsa-private-key-format")) {
1142 hostkey->privkey_type = NC_SSH_KEY_RSA;
1143 } else if (!strcmp(format, "ec-private-key-format")) {
1144 hostkey->privkey_type = NC_SSH_KEY_ECDSA;
1145 } else {
1146 ERR(NULL, "Private key format (%s) not supported.", format);
1147 }
1148 }
1149
1150cleanup:
1151 return ret;
1152}
1153
1154static int
1155nc_server_replace_cleartext_private_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
1156{
1157 nc_server_del_private_key(hostkey);
1158 hostkey->priv_base64 = strdup(lyd_get_value(node));
1159 if (!hostkey->priv_base64) {
1160 ERRMEM;
1161 return 1;
1162 }
1163
1164 return 0;
1165}
1166
1167static int
romane028ef92023-02-24 16:33:08 +01001168nc_server_config_cleartext_private_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001169{
1170 struct nc_endpt *endpt;
1171 struct nc_hostkey *hostkey;
1172 int ret = 0;
1173
1174 assert(!strcmp(LYD_NAME(node), "cleartext-private-key"));
1175
1176 if ((equal_parent_name(node, 6, "ssh")) && (equal_parent_name(node, 8, "listen"))) {
1177 if (nc_server_get_endpt(node, &endpt, NULL)) {
1178 ret = 1;
1179 goto cleanup;
1180 }
1181 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1182 ret = 1;
1183 goto cleanup;
1184 }
1185
1186 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1187 ret = nc_server_replace_cleartext_private_key(node, hostkey);
1188 if (ret) {
1189 goto cleanup;
1190 }
1191 } else {
1192 nc_server_del_private_key(hostkey);
1193 }
1194 }
1195
1196cleanup:
1197 return ret;
1198}
1199
1200static int
1201nc_server_create_keystore_reference(const struct lyd_node *node, struct nc_hostkey *hostkey)
1202{
1203 uint16_t i;
roman45cec4e2023-02-17 10:21:39 +01001204 struct nc_keystore *ks = &server_opts.keystore;
romanc1d2b092023-02-02 08:58:27 +01001205
1206 /* lookup name */
roman45cec4e2023-02-17 10:21:39 +01001207 for (i = 0; i < ks->asym_key_count; i++) {
1208 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
romanc1d2b092023-02-02 08:58:27 +01001209 break;
1210 }
1211 }
1212
roman45cec4e2023-02-17 10:21:39 +01001213 if (i == ks->asym_key_count) {
1214 ERR(NULL, "Keystore \"%s\" not found.", lyd_get_value(node));
romanc1d2b092023-02-02 08:58:27 +01001215 return 1;
1216 }
1217
roman45cec4e2023-02-17 10:21:39 +01001218 hostkey->ks_ref = &ks->asym_keys[i];
romanc1d2b092023-02-02 08:58:27 +01001219
1220 return 0;
1221}
1222
1223/* leaf */
1224static int
romane028ef92023-02-24 16:33:08 +01001225nc_server_config_keystore_reference(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001226{
1227 struct nc_endpt *endpt;
1228 struct nc_hostkey *hostkey;
1229 int ret = 0;
1230
1231 assert(!strcmp(LYD_NAME(node), "keystore-reference"));
1232
roman45cec4e2023-02-17 10:21:39 +01001233 if ((equal_parent_name(node, 3, "server-identity")) && (equal_parent_name(node, 7, "listen"))) {
romanc1d2b092023-02-02 08:58:27 +01001234 if (nc_server_get_endpt(node, &endpt, NULL)) {
1235 ret = 1;
1236 goto cleanup;
1237 }
1238 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1239 ret = 1;
1240 goto cleanup;
1241 }
1242
1243 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1244 ret = nc_server_create_keystore_reference(node, hostkey);
1245 if (ret) {
1246 goto cleanup;
1247 }
1248 } else {
roman45cec4e2023-02-17 10:21:39 +01001249 hostkey->ks_ref = NULL;
romanc1d2b092023-02-02 08:58:27 +01001250 }
1251 }
1252
1253cleanup:
1254 return ret;
1255}
1256
1257static int
1258nc_server_create_auth_key_public_key_list(const struct lyd_node *node, struct nc_client_auth *auth_client)
1259{
1260 int ret = 0;
1261 void *tmp;
1262
1263 assert(!strcmp(LYD_NAME(node), "public-key"));
1264
1265 tmp = realloc(auth_client->pubkeys, (auth_client->pubkey_count + 1) * sizeof *auth_client->pubkeys);
1266 if (!tmp) {
1267 ERRMEM;
1268 ret = 1;
1269 goto cleanup;
1270 }
1271 auth_client->pubkeys = tmp;
1272
1273 memset(&auth_client->pubkeys[auth_client->pubkey_count], 0, sizeof *auth_client->pubkeys);
1274
1275 node = lyd_child(node);
1276 assert(!strcmp(LYD_NAME(node), "name"));
1277
1278 auth_client->pubkeys[auth_client->pubkey_count].name = strdup(lyd_get_value(node));
1279 if (!auth_client->pubkeys[auth_client->pubkey_count].name) {
1280 ERRMEM;
1281 ret = 1;
1282 goto cleanup;
1283 }
1284
1285 ++auth_client->pubkey_count;
1286
1287cleanup:
1288 return ret;
1289}
1290
1291static int
1292nc_server_replace_auth_key_public_key_leaf(const struct lyd_node *node, struct nc_client_auth_pubkey *pubkey)
1293{
1294 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
1295
1296 pubkey->pub_base64 = strdup(lyd_get_value(node));
1297 if (!pubkey->pub_base64) {
1298 ERRMEM;
1299 return 1;
1300 }
1301
1302 return 0;
1303}
1304
1305static int
1306nc_server_replace_host_key_public_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
1307{
1308 nc_server_del_public_key(hostkey);
1309
1310 hostkey->pub_base64 = strdup(lyd_get_value(node));
1311 if (!hostkey->pub_base64) {
1312 ERRMEM;
1313 return 1;
1314 }
1315
1316 return 0;
1317}
1318
1319static int
romane028ef92023-02-24 16:33:08 +01001320nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001321{
1322 struct nc_endpt *endpt;
1323 struct nc_hostkey *hostkey;
1324 struct nc_client_auth *auth_client;
1325 struct nc_client_auth_pubkey *pubkey;
1326 int ret = 0;
1327
1328 assert(!strcmp(LYD_NAME(node), "public-key"));
1329
1330 if ((equal_parent_name(node, 3, "host-key")) && (equal_parent_name(node, 8, "listen"))) {
1331 /* server's public-key, mandatory leaf */
1332 if (nc_server_get_endpt(node, &endpt, NULL)) {
1333 ret = 1;
1334 goto cleanup;
1335 }
1336
1337 if (nc_server_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1338 ret = 1;
1339 goto cleanup;
1340 }
1341
1342 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1343 ret = nc_server_replace_host_key_public_key(node, hostkey);
1344 if (ret) {
1345 goto cleanup;
1346 }
1347 }
1348 } else if ((equal_parent_name(node, 5, "client-authentication")) && (equal_parent_name(node, 9, "listen"))) {
1349 /* client auth pubkeys, list */
1350 if (nc_server_get_endpt(node, &endpt, NULL)) {
1351 ret = 1;
1352 goto cleanup;
1353 }
1354
1355 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1356 ret = 1;
1357 goto cleanup;
1358 }
1359
1360 if (op == NC_OP_CREATE) {
1361 ret = nc_server_create_auth_key_public_key_list(node, auth_client);
1362 if (ret) {
1363 goto cleanup;
1364 }
1365 } else if (op == NC_OP_DELETE) {
1366 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1367 ret = 1;
1368 goto cleanup;
1369 }
1370
1371 nc_server_del_auth_client_pubkey(auth_client, pubkey);
1372 }
1373 } else if ((equal_parent_name(node, 6, "client-authentication")) && (equal_parent_name(node, 10, "listen"))) {
1374 /* client auth pubkey, leaf */
1375 if (nc_server_get_endpt(node, &endpt, NULL)) {
1376 ret = 1;
1377 goto cleanup;
1378 }
1379
1380 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1381 ret = 1;
1382 goto cleanup;
1383 }
1384
1385 if (nc_server_get_pubkey(node, auth_client, &pubkey)) {
1386 ret = 1;
1387 goto cleanup;
1388 }
1389
1390 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1391 ret = nc_server_replace_auth_key_public_key_leaf(node, pubkey);
1392 if (ret) {
1393 goto cleanup;
1394 }
1395 } else {
1396 nc_server_del_auth_client_pubkey_pub_base64(pubkey);
1397 }
1398 }
1399
1400cleanup:
1401 return ret;
1402}
1403
1404static int
1405nc_server_create_user(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
1406{
1407 int ret = 0;
1408 void *tmp;
1409
1410 tmp = realloc(opts->auth_clients, (opts->client_count + 1) * sizeof *opts->auth_clients);
1411 if (!tmp) {
1412 ERRMEM;
1413 ret = 1;
1414 goto cleanup;
1415 }
1416 opts->auth_clients = tmp;
1417
1418 memset(&opts->auth_clients[opts->client_count], 0, sizeof *opts->auth_clients);
1419
1420 opts->auth_clients[opts->client_count].username = strdup(lyd_get_value(lyd_child(node)));
1421 if (!opts->auth_clients[opts->client_count].username) {
1422 ERRMEM;
1423 ret = 1;
1424 goto cleanup;
1425 }
1426
1427 lyd_find_path(node, "public-keys", 0, (struct lyd_node **)&node);
1428
1429 if (node) {
1430 /* set union selector */
1431 if (!lyd_find_path(node, "local-definition", 0, NULL)) {
1432 opts->auth_clients[opts->client_count].ks_type = NC_STORE_LOCAL;
1433 } else {
1434 opts->auth_clients[opts->client_count].ks_type = NC_STORE_TRUSTSTORE;
1435 }
1436 }
1437
1438 ++opts->client_count;
1439
1440cleanup:
1441 return ret;
1442}
1443
1444/* list */
1445static int
romane028ef92023-02-24 16:33:08 +01001446nc_server_config_user(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001447{
1448 struct nc_endpt *endpt;
1449 struct nc_client_auth *auth_client;
1450 int ret = 0;
1451
1452 assert(!strcmp(LYD_NAME(node), "user"));
1453
1454 if (equal_parent_name(node, 6, "listen")) {
1455 if (nc_server_get_endpt(node, &endpt, NULL)) {
1456 ret = 1;
1457 goto cleanup;
1458 }
1459
1460 if (op == NC_OP_CREATE) {
1461 ret = nc_server_create_user(node, endpt->opts.ssh);
1462 if (ret) {
1463 goto cleanup;
1464 }
1465 } else if (op == NC_OP_DELETE) {
1466 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1467 ret = 1;
1468 goto cleanup;
1469 }
1470
1471 nc_server_del_auth_client(endpt->opts.ssh, auth_client);
1472 }
1473 }
1474
1475cleanup:
1476 return ret;
1477}
1478
1479static int
romane028ef92023-02-24 16:33:08 +01001480nc_server_config_auth_attempts(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001481{
1482 struct nc_endpt *endpt;
1483 int ret = 0;
1484
1485 assert(!strcmp(LYD_NAME(node), "auth-attempts"));
1486
1487 if (equal_parent_name(node, 5, "listen")) {
1488 if (nc_server_get_endpt(node, &endpt, NULL)) {
1489 ret = 1;
1490 goto cleanup;
1491 }
1492
1493 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1494 endpt->opts.ssh->auth_attempts = strtoul(lyd_get_value(node), NULL, 10);
1495 }
1496 }
1497
1498cleanup:
1499 return ret;
1500}
1501
1502static int
romane028ef92023-02-24 16:33:08 +01001503nc_server_config_auth_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001504{
1505 struct nc_endpt *endpt;
1506 int ret = 0;
1507
1508 assert(!strcmp(LYD_NAME(node), "auth-timeout"));
1509
1510 if (equal_parent_name(node, 5, "listen")) {
1511 if (nc_server_get_endpt(node, &endpt, NULL)) {
1512 ret = 1;
1513 goto cleanup;
1514 }
1515
1516 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1517 endpt->opts.ssh->auth_timeout = strtoul(lyd_get_value(node), NULL, 10);
1518 }
1519 }
1520
1521cleanup:
1522 return ret;
1523}
1524
1525static int
1526nc_server_replace_truststore_reference(const struct lyd_node *node, struct nc_client_auth *client_auth)
1527{
1528 /*todo*/
1529 nc_server_del_truststore_reference(client_auth);
1530
1531 client_auth->ts_reference = strdup(lyd_get_value(node));
1532 if (!client_auth->ts_reference) {
1533 ERRMEM;
1534 return 1;
1535 }
1536
1537 return 0;
1538}
1539
1540/* leaf */
1541static int
romane028ef92023-02-24 16:33:08 +01001542nc_server_config_truststore_reference(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001543{
1544 struct nc_endpt *endpt;
1545 struct nc_client_auth *auth_client;
1546 int ret = 0;
1547
1548 assert(!strcmp(LYD_NAME(node), "truststore-reference"));
1549
1550 if ((equal_parent_name(node, 1, "public-keys")) && (equal_parent_name(node, 8, "listen"))) {
1551 if (nc_server_get_endpt(node, &endpt, NULL)) {
1552 ret = 1;
1553 goto cleanup;
1554 }
1555
1556 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1557 ret = 1;
1558 goto cleanup;
1559 }
1560
1561 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1562 ret = nc_server_replace_truststore_reference(node, auth_client);
1563 if (ret) {
1564 goto cleanup;
1565 }
1566 } else {
1567 nc_server_del_truststore_reference(auth_client);
1568 }
1569 }
1570
1571cleanup:
1572 return ret;
1573}
1574
1575static int
1576nc_server_replace_password(const struct lyd_node *node, struct nc_client_auth *auth_client)
1577{
1578 nc_server_del_auth_client_password(auth_client);
1579
1580 auth_client->password = strdup(lyd_get_value(node));
1581 if (!auth_client->password) {
1582 ERRMEM;
1583 return 1;
1584 }
1585
1586 return 0;
1587}
1588
1589/* leaf */
1590static int
romane028ef92023-02-24 16:33:08 +01001591nc_server_config_password(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001592{
1593 struct nc_endpt *endpt;
1594 struct nc_client_auth *auth_client;
1595 int ret = 0;
1596
1597 assert(!strcmp(LYD_NAME(node), "password"));
1598
1599 if (equal_parent_name(node, 7, "listen")) {
1600 if (nc_server_get_endpt(node, &endpt, NULL)) {
1601 ret = 1;
1602 goto cleanup;
1603 }
1604
1605 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1606 ret = 1;
1607 goto cleanup;
1608 }
1609
1610 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1611 ret = nc_server_replace_password(node, auth_client);
1612 if (ret) {
1613 goto cleanup;
1614 }
1615 } else {
1616 nc_server_del_auth_client_password(auth_client);
1617 }
1618 }
1619
1620cleanup:
1621 return ret;
1622}
1623
1624static int
romane028ef92023-02-24 16:33:08 +01001625nc_server_config_pam_name(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001626{
1627 struct nc_endpt *endpt;
1628 struct nc_client_auth *auth_client;
1629 int ret = 0;
1630
1631 assert(!strcmp(LYD_NAME(node), "pam-config-file-name"));
1632
1633 if (equal_parent_name(node, 8, "listen")) {
1634 if (nc_server_get_endpt(node, &endpt, NULL)) {
1635 ret = 1;
1636 goto cleanup;
1637 }
1638
1639 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1640 ret = 1;
1641 goto cleanup;
1642 }
1643
1644 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1645 nc_server_del_auth_client_pam_name(auth_client);
1646
1647 auth_client->pam_config_name = strdup(lyd_get_value(node));
1648 if (!auth_client->pam_config_name) {
1649 ERRMEM;
1650 ret = 1;
1651 goto cleanup;
1652 }
1653 }
1654 }
1655
1656cleanup:
1657 return ret;
1658}
1659
1660static int
romane028ef92023-02-24 16:33:08 +01001661nc_server_config_pam_dir(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001662{
1663 struct nc_endpt *endpt;
1664 struct nc_client_auth *auth_client;
1665 int ret = 0;
1666
1667 assert(!strcmp(LYD_NAME(node), "pam-config-file-dir"));
1668
1669 if (equal_parent_name(node, 8, "listen")) {
1670 if (nc_server_get_endpt(node, &endpt, NULL)) {
1671 ret = 1;
1672 goto cleanup;
1673 }
1674
1675 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1676 ret = 1;
1677 goto cleanup;
1678 }
1679
1680 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1681 nc_server_del_auth_client_pam_dir(auth_client);
1682 auth_client->pam_config_dir = strdup(lyd_get_value(node));
1683 if (!auth_client->pam_config_dir) {
1684 ERRMEM;
1685 ret = 1;
1686 goto cleanup;
1687 }
1688 }
1689 }
1690
1691cleanup:
1692 return ret;
1693}
1694
1695/* leaf */
1696static int
romane028ef92023-02-24 16:33:08 +01001697nc_server_config_none(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001698{
1699 struct nc_endpt *endpt;
1700 struct nc_client_auth *auth_client;
1701 int ret = 0;
1702
1703 assert(!strcmp(LYD_NAME(node), "none"));
1704
1705 if (equal_parent_name(node, 7, "listen")) {
1706 if (nc_server_get_endpt(node, &endpt, NULL)) {
1707 ret = 1;
1708 goto cleanup;
1709 }
1710
1711 if (nc_server_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
1712 ret = 1;
1713 goto cleanup;
1714 }
1715
1716 if (op == NC_OP_CREATE) {
1717 auth_client->supports_none = 1;
1718 } else {
1719 auth_client->supports_none = 0;
1720 }
1721 }
1722
1723cleanup:
1724 return ret;
1725}
1726
1727static int
romane028ef92023-02-24 16:33:08 +01001728nc_server_config_transport_params(const char *alg, char **alg_store, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001729{
1730 int ret = 0, alg_found = 0;
1731 char *substr, *haystack;
1732 size_t alg_len = strlen(alg);
1733
1734 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1735 if (!*alg_store) {
1736 /* first call */
1737 *alg_store = strdup(alg);
1738 if (!*alg_store) {
1739 ERRMEM;
1740 ret = 1;
1741 goto cleanup;
1742 }
1743 } else {
1744 /* +1 because of ',' between algorithms */
1745 *alg_store = nc_realloc(*alg_store, strlen(*alg_store) + alg_len + 1 + 1);
1746 if (!*alg_store) {
1747 ERRMEM;
1748 ret = 1;
1749 goto cleanup;
1750 }
1751 sprintf(*alg_store, "%s,%s", *alg_store, alg);
1752 }
1753 } else {
1754 /* delete */
1755 haystack = *alg_store;
1756 while ((substr = strstr(haystack, alg))) {
1757 /* iterate over all the substrings */
1758 if (((substr == haystack) && (*(substr + alg_len) == ',')) ||
1759 ((substr != haystack) && (*(substr - 1) == ',') && (*(substr + alg_len) == ','))) {
1760 /* either the first element of the string or somewhere in the middle */
1761 memmove(substr, substr + alg_len + 1, strlen(substr + alg_len + 1));
1762 alg_found = 1;
1763 break;
1764 } else if ((*(substr - 1) == ',') && (*(substr + alg_len) == '\0')) {
1765 /* the last element of the string */
1766 *(substr - 1) = '\0';
1767 alg_found = 1;
1768 break;
1769 }
1770 haystack++;
1771 }
1772 if (!alg_found) {
1773 ERR(NULL, "Unable to delete an algorithm (%s), which was not previously added.", alg);
1774 ret = 1;
1775 }
1776 }
1777
1778cleanup:
1779 return ret;
1780}
1781
1782/* leaf-list */
1783static int
romane028ef92023-02-24 16:33:08 +01001784nc_server_config_host_key_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001785{
1786 struct nc_endpt *endpt;
1787 int ret = 0, listen = 0;
1788 const char *alg;
1789 uint8_t i;
1790
1791 /* get the algorithm name and compare it with algs supported by libssh */
1792 alg = ((struct lyd_node_term *)node)->value.ident->name;
1793
1794 if (equal_parent_name(node, 6, "listen")) {
1795 listen = 1;
1796 if (nc_server_get_endpt(node, &endpt, NULL)) {
1797 ret = 1;
1798 goto cleanup;
1799 }
1800 }
1801
1802 i = 0;
1803 while (supported_hostkey_algs[i]) {
1804 if (!strcmp(supported_hostkey_algs[i], alg)) {
1805 if (listen) {
romane028ef92023-02-24 16:33:08 +01001806 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->hostkey_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001807 ret = 1;
1808 goto cleanup;
1809 }
1810 }
1811 break;
1812 }
1813 i++;
1814 }
1815 if (!supported_hostkey_algs[i]) {
1816 /* algorithm not supported */
1817 ERR(NULL, "Public key algorithm (%s) not supported by libssh.", alg);
1818 ret = 1;
1819 }
1820
1821cleanup:
1822 return ret;
1823}
1824
1825/* leaf-list */
1826static int
romane028ef92023-02-24 16:33:08 +01001827nc_server_config_kex_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001828{
1829 struct nc_endpt *endpt;
1830 int ret = 0, listen = 0;
1831 const char *alg;
1832 uint8_t i;
1833
1834 /* get the algorithm name and compare it with algs supported by libssh */
1835 alg = ((struct lyd_node_term *)node)->value.ident->name;
1836
1837 if (equal_parent_name(node, 6, "listen")) {
1838 listen = 1;
1839 if (nc_server_get_endpt(node, &endpt, NULL)) {
1840 ret = 1;
1841 goto cleanup;
1842 }
1843 }
1844
1845 i = 0;
1846 while (supported_kex_algs[i]) {
1847 if (!strcmp(supported_kex_algs[i], alg)) {
1848 if (listen) {
romane028ef92023-02-24 16:33:08 +01001849 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->kex_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001850 ret = 1;
1851 goto cleanup;
1852 }
1853 }
1854 break;
1855 }
1856 i++;
1857 }
1858 if (!supported_kex_algs[i]) {
1859 /* algorithm not supported */
1860 ERR(NULL, "Key exchange algorithm (%s) not supported by libssh.", alg);
1861 ret = 1;
1862 }
1863
1864cleanup:
1865 return ret;
1866}
1867
1868/* leaf-list */
1869static int
romane028ef92023-02-24 16:33:08 +01001870nc_server_config_encryption_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001871{
1872 struct nc_endpt *endpt;
1873 int ret = 0, listen = 0;
1874 const char *alg;
1875 uint8_t i;
1876
1877 /* get the algorithm name and compare it with algs supported by libssh */
1878 alg = ((struct lyd_node_term *)node)->value.ident->name;
1879
1880 if (equal_parent_name(node, 6, "listen")) {
1881 listen = 1;
1882 if (nc_server_get_endpt(node, &endpt, NULL)) {
1883 ret = 1;
1884 goto cleanup;
1885 }
1886 }
1887
1888 i = 0;
1889 while (supported_encryption_algs[i]) {
1890 if (!strcmp(supported_encryption_algs[i], alg)) {
1891 if (listen) {
romane028ef92023-02-24 16:33:08 +01001892 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->encryption_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001893 ret = 1;
1894 goto cleanup;
1895 }
1896 }
1897 break;
1898 }
1899 i++;
1900 }
1901 if (!supported_encryption_algs[i]) {
1902 /* algorithm not supported */
1903 ERR(NULL, "Encryption algorithm (%s) not supported by libssh.", alg);
1904 ret = 1;
1905 }
1906
1907cleanup:
1908 return ret;
1909}
1910
1911/* leaf-list */
1912static int
romane028ef92023-02-24 16:33:08 +01001913nc_server_config_mac_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001914{
1915 struct nc_endpt *endpt;
1916 int ret = 0, listen = 0;
1917 const char *alg;
1918 uint8_t i;
1919
1920 /* get the algorithm name and compare it with algs supported by libssh */
1921 alg = ((struct lyd_node_term *)node)->value.ident->name;
1922
1923 if (equal_parent_name(node, 6, "listen")) {
1924 listen = 1;
1925 if (nc_server_get_endpt(node, &endpt, NULL)) {
1926 ret = 1;
1927 goto cleanup;
1928 }
1929 }
1930
1931 i = 0;
1932 while (supported_mac_algs[i]) {
1933 if (!strcmp(supported_mac_algs[i], alg)) {
1934 if (listen) {
romane028ef92023-02-24 16:33:08 +01001935 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->mac_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001936 ret = 1;
1937 goto cleanup;
1938 }
1939 }
1940 break;
1941 }
1942 i++;
1943 }
1944 if (!supported_mac_algs[i]) {
1945 /* algorithm not supported */
1946 ERR(NULL, "MAC algorithm (%s) not supported by libssh.", alg);
1947 ret = 1;
1948 }
1949
1950cleanup:
1951 return ret;
1952}
1953
1954static int
roman83683fb2023-02-24 09:15:23 +01001955nc_server_create_unix_socket(struct nc_endpt *endpt)
1956{
1957 endpt->ti = NC_TI_UNIX;
1958 endpt->opts.unixsock = calloc(1, sizeof *endpt->opts.unixsock);
1959 if (!endpt->opts.unixsock) {
1960 ERRMEM;
1961 return 1;
1962 }
1963
1964 /* set default values */
1965 endpt->opts.unixsock->mode = -1;
1966 endpt->opts.unixsock->uid = -1;
1967 endpt->opts.unixsock->gid = -1;
1968
1969 return 0;
1970}
1971
1972static int
romane028ef92023-02-24 16:33:08 +01001973nc_server_config_unix_socket(const struct lyd_node *node, NC_OPERATION op)
roman83683fb2023-02-24 09:15:23 +01001974{
1975 int ret = 0;
1976 uint32_t prev_lo;
1977 struct nc_endpt *endpt;
1978 struct nc_bind *bind;
1979 struct nc_server_unix_opts *opts;
1980 struct lyd_node *data = NULL;
1981
1982 assert(!strcmp(LYD_NAME(node), "unix-socket"));
1983
1984 if (nc_server_get_endpt(node, &endpt, &bind)) {
1985 ret = 1;
1986 goto cleanup;
1987 }
1988
1989 if (op == NC_OP_CREATE) {
1990 if (nc_server_create_unix_socket(endpt)) {
1991 ret = 1;
1992 goto cleanup;
1993 }
1994
1995 opts = endpt->opts.unixsock;
1996
1997 lyd_find_path(node, "path", 0, &data);
1998 assert(data);
1999
2000 opts->address = strdup(lyd_get_value(data));
2001 bind->address = strdup(lyd_get_value(data));
2002 if (!opts->address || !bind->address) {
2003 ERRMEM;
2004 ret = 1;
2005 goto cleanup;
2006 }
2007
2008 /* silently search for non-mandatory parameters */
2009 prev_lo = ly_log_options(0);
2010 ret = lyd_find_path(node, "mode", 0, &data);
2011 if (!ret) {
2012 opts->mode = strtol(lyd_get_value(data), NULL, 8);
2013 }
2014
2015 ret = lyd_find_path(node, "uid", 0, &data);
2016 if (!ret) {
2017 opts->uid = strtol(lyd_get_value(data), NULL, 10);
2018 }
2019
2020 ret = lyd_find_path(node, "gid", 0, &data);
2021 if (!ret) {
2022 opts->gid = strtol(lyd_get_value(data), NULL, 10);
2023 }
2024
2025 /* reset the logging options */
2026 ly_log_options(prev_lo);
2027
2028 ret = nc_server_config_set_address_port(endpt, bind, NULL, 0);
2029 if (ret) {
2030 goto cleanup;
2031 }
2032 } else if (op == NC_OP_DELETE) {
2033 nc_server_del_unix_socket(bind, endpt->opts.unixsock);
2034 }
2035
2036cleanup:
2037 return ret;
2038}
2039
2040static int
romanc1d2b092023-02-02 08:58:27 +01002041nc_server_configure(const struct lyd_node *node, NC_OPERATION op)
2042{
2043 const char *name = LYD_NAME(node);
2044
2045 if (!strcmp(name, "listen")) {
romane028ef92023-02-24 16:33:08 +01002046 if (nc_server_config_listen(op)) {
romanc1d2b092023-02-02 08:58:27 +01002047 goto error;
2048 }
2049 } else if (!strcmp(name, "idle-timeout")) {
romane028ef92023-02-24 16:33:08 +01002050 if (nc_server_config_idle_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002051 goto error;
2052 }
2053 } else if (!strcmp(name, "endpoint")) {
romane028ef92023-02-24 16:33:08 +01002054 if (nc_server_config_endpoint(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002055 goto error;
2056 }
2057 } else if (!strcmp(name, "ssh")) {
romane028ef92023-02-24 16:33:08 +01002058 if (nc_server_config_ssh(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002059 goto error;
2060 }
2061 } else if (!strcmp(name, "local-address")) {
romane028ef92023-02-24 16:33:08 +01002062 if (nc_server_config_local_address(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002063 goto error;
2064 }
2065 } else if (!strcmp(name, "local-port")) {
romane028ef92023-02-24 16:33:08 +01002066 if (nc_server_config_local_port(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002067 goto error;
2068 }
2069 } else if (!strcmp(name, "keepalives")) {
romane028ef92023-02-24 16:33:08 +01002070 if (nc_server_config_keepalives(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002071 goto error;
2072 }
2073 } else if (!strcmp(name, "idle-time")) {
romane028ef92023-02-24 16:33:08 +01002074 if (nc_server_config_idle_time(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002075 goto error;
2076 }
2077 } else if (!strcmp(name, "max-probes")) {
romane028ef92023-02-24 16:33:08 +01002078 if (nc_server_config_max_probes(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002079 goto error;
2080 }
2081 } else if (!strcmp(name, "probe-interval")) {
romane028ef92023-02-24 16:33:08 +01002082 if (nc_server_config_probe_interval(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002083 goto error;
2084 }
2085 } else if (!strcmp(name, "host-key")) {
romane028ef92023-02-24 16:33:08 +01002086 if (nc_server_config_host_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002087 goto error;
2088 }
2089 } else if (!strcmp(name, "public-key-format")) {
romane028ef92023-02-24 16:33:08 +01002090 if (nc_server_config_public_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002091 goto error;
2092 }
2093 } else if (!strcmp(name, "public-key")) {
romane028ef92023-02-24 16:33:08 +01002094 if (nc_server_config_public_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002095 goto error;
2096 }
2097 } else if (!strcmp(name, "private-key-format")) {
romane028ef92023-02-24 16:33:08 +01002098 if (nc_server_config_private_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002099 goto error;
2100 }
2101 } else if (!strcmp(name, "cleartext-private-key")) {
romane028ef92023-02-24 16:33:08 +01002102 if (nc_server_config_cleartext_private_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002103 goto error;
2104 }
2105 } else if (!strcmp(name, "keystore-reference")) {
romane028ef92023-02-24 16:33:08 +01002106 if (nc_server_config_keystore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002107 goto error;
2108 }
2109 } else if (!strcmp(name, "user")) {
romane028ef92023-02-24 16:33:08 +01002110 if (nc_server_config_user(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002111 goto error;
2112 }
2113 } else if (!strcmp(name, "auth-attempts")) {
romane028ef92023-02-24 16:33:08 +01002114 if (nc_server_config_auth_attempts(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002115 goto error;
2116 }
2117 } else if (!strcmp(name, "auth-timeout")) {
romane028ef92023-02-24 16:33:08 +01002118 if (nc_server_config_auth_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002119 goto error;
2120 }
2121 } else if (!strcmp(name, "truststore-reference")) {
romane028ef92023-02-24 16:33:08 +01002122 if (nc_server_config_truststore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002123 goto error;
2124 }
2125 } else if (!strcmp(name, "password")) {
romane028ef92023-02-24 16:33:08 +01002126 if (nc_server_config_password(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002127 goto error;
2128 }
2129 } else if (!strcmp(name, "pam-config-file-name")) {
romane028ef92023-02-24 16:33:08 +01002130 if (nc_server_config_pam_name(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002131 goto error;
2132 }
2133 } else if (!strcmp(name, "pam-config-file-dir")) {
romane028ef92023-02-24 16:33:08 +01002134 if (nc_server_config_pam_dir(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002135 goto error;
2136 }
2137 } else if (!strcmp(name, "none")) {
romane028ef92023-02-24 16:33:08 +01002138 if (nc_server_config_none(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002139 goto error;
2140 }
2141 } else if (!strcmp(name, "host-key-alg")) {
romane028ef92023-02-24 16:33:08 +01002142 if (nc_server_config_host_key_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002143 goto error;
2144 }
2145 } else if (!strcmp(name, "key-exchange-alg")) {
romane028ef92023-02-24 16:33:08 +01002146 if (nc_server_config_kex_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002147 goto error;
2148 }
2149 } else if (!strcmp(name, "encryption-alg")) {
romane028ef92023-02-24 16:33:08 +01002150 if (nc_server_config_encryption_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002151 goto error;
2152 }
2153 } else if (!strcmp(name, "mac-alg")) {
romane028ef92023-02-24 16:33:08 +01002154 if (nc_server_config_mac_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002155 goto error;
2156 }
roman83683fb2023-02-24 09:15:23 +01002157 } else if (!strcmp(name, "unix-socket")) {
romane028ef92023-02-24 16:33:08 +01002158 if (nc_server_config_unix_socket(node, op)) {
roman83683fb2023-02-24 09:15:23 +01002159 goto error;
2160 }
romanc1d2b092023-02-02 08:58:27 +01002161 } 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,
2162 "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,
2163 "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,
2164 "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,
2165 "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,
2166 "id")) {} else if (!strcmp(name, "fingerprint")) {} else if (!strcmp(name, "map-type")) {}
2167
2168 return 0;
2169
2170error:
2171 ERR(NULL, "Configuring (%s) failed.", LYD_NAME(node));
2172 return 1;
2173}
2174
2175int
2176nc_session_server_parse_tree(const struct lyd_node *node, NC_OPERATION parent_op)
2177{
2178 struct lyd_node *child;
2179 struct lyd_meta *m;
2180 NC_OPERATION current_op;
2181
2182 assert(node);
2183
2184 /* get current op */
2185 LY_LIST_FOR(node->meta, m) {
2186 if (!strcmp(m->name, "operation")) {
2187 if (!strcmp(lyd_get_meta_value(m), "create")) {
2188 current_op = NC_OP_CREATE;
2189 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
2190 current_op = NC_OP_DELETE;
2191 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
2192 current_op = NC_OP_REPLACE;
2193 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
2194 current_op = NC_OP_NONE;
2195 }
2196 break;
2197 }
2198 }
2199
2200 /* node has no op, inherit from the parent */
2201 if (!m) {
2202 current_op = parent_op;
2203 }
2204
2205 switch (current_op) {
2206 case NC_OP_NONE:
2207 break;
2208 case NC_OP_CREATE:
2209 case NC_OP_DELETE:
2210 case NC_OP_REPLACE:
2211 if (nc_server_configure(node, current_op)) {
2212 return 1;
2213 }
2214 break;
2215 default:
2216 break;
2217 }
2218
2219 if (current_op != NC_OP_DELETE) {
2220 LY_LIST_FOR(lyd_child(node), child) {
2221 if (nc_session_server_parse_tree(child, current_op)) {
2222 return 1;
2223 }
2224 }
2225 }
2226 return 0;
2227}
2228
2229static int
romane028ef92023-02-24 16:33:08 +01002230nc_server_config_asymmetric_key_certificate(const struct lyd_node *tree, struct nc_ks_asym_key *key)
romanc1d2b092023-02-02 08:58:27 +01002231{
2232 int ret = 0;
roman45cec4e2023-02-17 10:21:39 +01002233 struct lyd_node *node;
romanc1d2b092023-02-02 08:58:27 +01002234 void *tmp;
2235
roman45cec4e2023-02-17 10:21:39 +01002236 /* create new certificate */
2237 tmp = realloc(key->certs, (key->cert_count + 1) * sizeof *key->certs);
2238 if (!tmp) {
2239 ERRMEM;
2240 ret = 1;
2241 goto cleanup;
2242 }
2243 key->certs = tmp;
2244 key->cert_count++;
2245
2246 /* set name */
2247 lyd_find_path(tree, "name", 0, &node);
2248 assert(node);
2249
2250 key->certs[key->cert_count - 1].name = strdup(lyd_get_value(node));
2251 if (!key->certs[key->cert_count - 1].name) {
2252 ERRMEM;
2253 ret = 1;
romanc1d2b092023-02-02 08:58:27 +01002254 goto cleanup;
2255 }
2256
roman45cec4e2023-02-17 10:21:39 +01002257 /* set certificate data */
2258 lyd_find_path(tree, "cert-data", 0, &node);
2259 assert(node);
romanc1d2b092023-02-02 08:58:27 +01002260
roman45cec4e2023-02-17 10:21:39 +01002261 key->certs[key->cert_count - 1].cert_base64 = strdup(lyd_get_value(node));
2262 if (!key->certs[key->cert_count - 1].cert_base64) {
2263 ERRMEM;
2264 ret = 1;
2265 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002266 }
2267
2268cleanup:
roman45cec4e2023-02-17 10:21:39 +01002269 return ret;
2270}
2271
2272static int
romane028ef92023-02-24 16:33:08 +01002273nc_server_config_asymmetric_key(const struct lyd_node *tree)
roman45cec4e2023-02-17 10:21:39 +01002274{
2275 int ret = 0;
2276 struct lyd_node *node = NULL, *iter;
2277 void *tmp;
2278 struct nc_keystore *ks = &server_opts.keystore;
2279 struct nc_ks_asym_key *key;
2280 const char *format;
2281
2282 /* create new asymmetric key */
2283 tmp = realloc(ks->asym_keys, (ks->asym_key_count + 1) * sizeof *ks->asym_keys);
2284 if (!tmp) {
2285 ERRMEM;
2286 ret = 1;
2287 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002288 }
roman45cec4e2023-02-17 10:21:39 +01002289 ks->asym_keys = tmp;
2290 memset(&ks->asym_keys[ks->asym_key_count], 0, sizeof *ks->asym_keys);
2291 key = &ks->asym_keys[ks->asym_key_count];
2292 ks->asym_key_count++;
2293
2294 /* set name */
2295 lyd_find_path(tree, "name", 0, &node);
2296 assert(node);
2297
2298 key->name = strdup(lyd_get_value(node));
2299 if (!key->name) {
2300 ERRMEM;
2301 ret = 1;
2302 goto cleanup;
2303 }
2304
2305 /* set public-key-format, mandatory */
2306 lyd_find_path(tree, "public-key-format", 0, &node);
2307 assert(node);
2308
2309 format = ((struct lyd_node_term *)node)->value.ident->name;
2310 if (!strcmp(format, "ssh-public-key-format")) {
2311 key->pubkey_type = NC_SSH_PUBKEY_X509;
2312 } else if (!strcmp(format, "subject-public-key-info-format")) {
2313 key->pubkey_type = NC_SSH_PUBKEY_SSH2;
2314 } else {
2315 ERR(NULL, "Public key format \"%s\" not supported.", format);
2316 ret = 1;
2317 goto cleanup;
2318 }
2319
2320 /* set public-key, mandatory */
2321 lyd_find_path(tree, "public-key", 0, &node);
2322 assert(node);
2323
2324 key->pub_base64 = strdup(lyd_get_value(node));
2325 if (!key->pub_base64) {
2326 ERRMEM;
2327 ret = 1;
2328 goto cleanup;
2329 }
2330
2331 /* set private-key-format */
2332 ret = lyd_find_path(tree, "private-key-format", 0, &node);
2333 if (!ret) {
2334 format = ((struct lyd_node_term *)node)->value.ident->name;
2335 if (!strcmp(format, "rsa-private-key-format")) {
2336 key->privkey_type = NC_SSH_KEY_RSA;
2337 } else if (!strcmp(format, "ec-private-key-format")) {
2338 key->privkey_type = NC_SSH_KEY_ECDSA;
2339 } else {
2340 ERR(NULL, "Private key format (%s) not supported.", format);
2341 ret = 1;
2342 goto cleanup;
2343 }
2344 }
2345
2346 /* set private key, mandatory */
2347 lyd_find_path(tree, "cleartext-private-key", 0, &node);
2348 assert(node);
2349
2350 key->priv_base64 = strdup(lyd_get_value(node));
2351 if (!key->priv_base64) {
2352 ERRMEM;
2353 ret = 1;
2354 goto cleanup;
2355 }
2356
2357 /* set certificates associated with the key pair */
2358 ret = lyd_find_path(tree, "certificates", 0, &node);
2359 if (!ret) {
2360 node = lyd_child(node);
2361 if (node) {
2362 /* certificate list instance */
2363 LY_LIST_FOR(node, iter) {
romane028ef92023-02-24 16:33:08 +01002364 if (nc_server_config_asymmetric_key_certificate(iter, key)) {
roman45cec4e2023-02-17 10:21:39 +01002365 ret = 1;
2366 goto cleanup;
2367 }
2368 }
2369 }
2370 } else if (ret == LY_ENOTFOUND) {
2371 /* certificates container not present, but it's ok */
2372 ret = 0;
2373 }
2374
2375cleanup:
2376 return ret;
2377}
2378
2379static int
romane028ef92023-02-24 16:33:08 +01002380nc_server_config_symmetric_key(const struct lyd_node *tree)
roman45cec4e2023-02-17 10:21:39 +01002381{
2382 int ret = 0;
2383 const char *format;
2384 struct lyd_node *node;
2385 struct nc_keystore *ks = &server_opts.keystore;
2386 struct nc_ks_sym_key *key;
2387 void *tmp;
2388
2389 /* create new symmetric key */
2390 tmp = realloc(ks->sym_keys, (ks->sym_key_count + 1) * sizeof *ks->sym_keys);
2391 if (tmp) {
2392 ERRMEM;
2393 ret = 1;
2394 goto cleanup;
2395 }
2396 memset(&ks->sym_keys[ks->sym_key_count], 0, sizeof *ks->sym_keys);
2397 ks->sym_keys = tmp;
2398 key = &ks->sym_keys[ks->sym_key_count];
2399 ks->sym_key_count++;
2400
2401 /* set name */
2402 lyd_find_path(tree, "name", 0, &node);
2403 assert(node);
2404
2405 key->name = strdup(lyd_get_value(node));
2406 if (!key->name) {
2407 ERRMEM;
2408 ret = 1;
2409 goto cleanup;
2410 }
2411
2412 /* check if the identity matches with the supported one */
2413 lyd_find_path(tree, "key-format", 0, &node);
2414 assert(node);
2415
2416 format = ((struct lyd_node_term *)node)->value.ident->name;
2417 if (strcmp(format, "symmetric-key-format")) {
2418 ret = 1;
2419 goto cleanup;
2420 }
2421
2422 /* set key data */
2423 lyd_find_path(tree, "cleartext-key", 0, &node);
2424 assert(node);
2425
2426 key->base64 = strdup(lyd_get_value(node));
2427 if (!key->base64) {
2428 ERRMEM;
2429 ret = 1;
2430 goto cleanup;
2431 }
2432
2433cleanup:
romanc1d2b092023-02-02 08:58:27 +01002434 return ret;
2435}
2436
2437static int
2438nc_fill_keystore(const struct lyd_node *data)
2439{
2440 int ret = 0;
2441 uint32_t prev_lo;
roman45cec4e2023-02-17 10:21:39 +01002442 struct lyd_node *tree, *as_keys, *s_keys, *iter;
romanc1d2b092023-02-02 08:58:27 +01002443
roman45cec4e2023-02-17 10:21:39 +01002444 /* silently search for nodes, some of them may not be present */
romanc1d2b092023-02-02 08:58:27 +01002445 prev_lo = ly_log_options(0);
roman45cec4e2023-02-17 10:21:39 +01002446
2447 ret = lyd_find_path(data, "/ietf-keystore:keystore", 0, &tree);
romanc1d2b092023-02-02 08:58:27 +01002448 if (ret) {
2449 WRN(NULL, "Keystore container not found in the YANG data.");
roman27215242023-03-10 14:55:00 +01002450 ret = 0;
roman45cec4e2023-02-17 10:21:39 +01002451 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002452 }
2453
roman45cec4e2023-02-17 10:21:39 +01002454 ret = lyd_find_path(tree, "asymmetric-keys", 0, &as_keys);
2455 if (!ret) {
2456 /* asymmetric keys container is present */
2457 as_keys = lyd_child(as_keys);
2458 if (as_keys && !strcmp(LYD_NAME(as_keys), "asymmetric-key")) {
2459 /* asymmetric key list */
2460 LY_LIST_FOR(as_keys, iter) {
romane028ef92023-02-24 16:33:08 +01002461 if (nc_server_config_asymmetric_key(iter)) {
roman45cec4e2023-02-17 10:21:39 +01002462 ret = 1;
2463 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002464 }
2465 }
romanc1d2b092023-02-02 08:58:27 +01002466 }
romanc1d2b092023-02-02 08:58:27 +01002467 }
2468
roman45cec4e2023-02-17 10:21:39 +01002469 ret = lyd_find_path(tree, "symmetric-keys", 0, &s_keys);
2470 if (!ret) {
2471 /* symmetric keys container is present */
2472 s_keys = lyd_child(s_keys);
2473 if (s_keys && !strcmp(LYD_NAME(s_keys), "symmetric-key")) {
2474 /* symmetric key list */
2475 LY_LIST_FOR(s_keys, iter) {
romane028ef92023-02-24 16:33:08 +01002476 if (nc_server_config_symmetric_key(iter)) {
roman45cec4e2023-02-17 10:21:39 +01002477 ret = 1;
2478 goto cleanup;
2479 }
2480 }
2481 }
2482 }
romanc1d2b092023-02-02 08:58:27 +01002483
roman45cec4e2023-02-17 10:21:39 +01002484cleanup:
2485 /* reset the logging options back to what they were */
2486 ly_log_options(prev_lo);
2487 return ret;
romanc1d2b092023-02-02 08:58:27 +01002488}
2489
2490API int
2491nc_server_config_load_modules(struct ly_ctx **ctx)
2492{
2493 int i, new_ctx = 0;
2494
2495 if (!*ctx) {
2496 if (ly_ctx_new(NC_SERVER_SEARCH_DIR, 0, ctx)) {
2497 ERR(NULL, "Couldn't create new libyang context.\n");
2498 goto error;
2499 }
2500 new_ctx = 1;
2501 }
2502
2503 /* all features */
2504 const char *ietf_nectonf_server[] = {"ssh-listen", "tls-listen", "ssh-call-home", "tls-call-home", "central-netconf-server-supported", NULL};
2505 /* all features */
2506 const char *ietf_x509_cert_to_name[] = {NULL};
2507 /* no private-key-encryption and csr-generation */
2508 const char *ietf_crypto_types[] = {
2509 "one-symmetric-key-format", "one-asymmetric-key-format", "symmetrically-encrypted-value-format",
2510 "asymmetrically-encrypted-value-format", "cms-enveloped-data-format", "cms-encrypted-data-format",
2511 "p10-based-csrs", "certificate-expiration-notification", "hidden-keys", "password-encryption",
2512 "symmetric-key-encryption", NULL
2513 };
2514 /* all features */
2515 const char *ietf_tcp_common[] = {"keepalives-supported", NULL};
2516 /* no ssh-x509-certs */
2517 const char *ietf_ssh_common[] = {"transport-params", "public-key-generation", NULL};
2518 /* all features */
2519 const char *iana_ssh_encryption_algs[] = {NULL};
2520 /* all features */
2521 const char *iana_ssh_key_exchange_algs[] = {NULL};
2522 /* all features */
2523 const char *iana_ssh_mac_algs[] = {NULL};
2524 /* all features */
2525 const char *iana_ssh_public_key_algs[] = {NULL};
2526 /* all features */
2527 const char *ietf_keystore[] = {"central-keystore-supported", "local-definitions-supported", "asymmetric-keys", "symmetric-keys", NULL};
2528 /* no ssh-server-keepalives and local-user-auth-hostbased */
2529 const char *ietf_ssh_server[] = {"local-users-supported", "local-user-auth-publickey", "local-user-auth-password", "local-user-auth-none", NULL};
2530 /* all features */
2531 const char *ietf_truststore[] = {"central-truststore-supported", "local-definitions-supported", "certificates", "public-keys", NULL};
2532 /* all features */
2533 const char *ietf_tls_server[] = {
2534 "tls-server-keepalives", "server-ident-x509-cert", "server-ident-raw-public-key", "server-ident-tls12-psk",
2535 "server-ident-tls13-epsk", "client-auth-supported", "client-auth-x509-cert", "client-auth-raw-public-key",
2536 "client-auth-tls12-psk", "client-auth-tls13-epsk", NULL
2537 };
2538 /* all features */
2539 const char *libnetconf2_netconf_server[] = {NULL};
2540
2541 const char *module_names[] = {
2542 "ietf-netconf-server", "ietf-x509-cert-to-name", "ietf-crypto-types",
2543 "ietf-tcp-common", "ietf-ssh-common", "iana-ssh-encryption-algs",
2544 "iana-ssh-key-exchange-algs", "iana-ssh-mac-algs", "iana-ssh-public-key-algs",
2545 "ietf-keystore", "ietf-ssh-server", "ietf-truststore",
2546 "ietf-tls-server", "libnetconf2-netconf-server", NULL
2547 };
2548
2549 const char **module_features[] = {
2550 ietf_nectonf_server, ietf_x509_cert_to_name, ietf_crypto_types,
2551 ietf_tcp_common, ietf_ssh_common, iana_ssh_encryption_algs,
2552 iana_ssh_key_exchange_algs, iana_ssh_mac_algs, iana_ssh_public_key_algs,
2553 ietf_keystore, ietf_ssh_server, ietf_truststore,
2554 ietf_tls_server, libnetconf2_netconf_server, NULL
2555 };
2556
2557 for (i = 0; module_names[i] != NULL; i++) {
2558 if (!ly_ctx_load_module(*ctx, module_names[i], NULL, module_features[i])) {
2559 ERR(NULL, "Loading module \"%s\" failed.\n", module_names[i]);
2560 goto error;
2561 }
2562 }
2563
2564 return 0;
2565
2566error:
2567 if (new_ctx) {
2568 ly_ctx_destroy(*ctx);
2569 *ctx = NULL;
2570 }
2571 return 1;
2572}
2573
2574API int
2575nc_server_config_setup_path(const struct ly_ctx *ctx, const char *path)
2576{
2577 struct lyd_node *tree = NULL;
2578 int ret = 0;
2579
2580 if (!path) {
2581 ERRARG("Missing path parameter.");
2582 ret = 1;
2583 goto cleanup;
2584 }
2585
2586 ret = lyd_parse_data_path(ctx, path, LYD_XML, LYD_PARSE_NO_STATE | LYD_PARSE_STRICT, LYD_VALIDATE_NO_STATE, &tree);
2587 if (ret) {
2588 goto cleanup;
2589 }
2590
2591 ret = nc_server_config_setup(tree);
2592 if (ret) {
2593 goto cleanup;
2594 }
2595
2596cleanup:
2597 lyd_free_all(tree);
2598 return ret;
2599}
2600
2601API int
2602nc_server_config_setup(const struct lyd_node *data)
2603{
2604 int ret = 0;
2605 struct lyd_node *tree;
2606 struct lyd_meta *m;
roman27215242023-03-10 14:55:00 +01002607 NC_OPERATION op = NC_OP_NONE;
romanc1d2b092023-02-02 08:58:27 +01002608
2609 /* LOCK */
2610 pthread_rwlock_wrlock(&server_opts.config_lock);
2611
2612 ret = nc_fill_keystore(data);
2613 if (ret) {
2614 ERR(NULL, "Filling keystore failed.");
2615 goto cleanup;
2616 }
2617
2618 ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &tree);
2619 if (ret) {
2620 ERR(NULL, "Unable to find the netconf-server container in the YANG data.");
2621 goto cleanup;
2622 }
2623
2624 LY_LIST_FOR(tree->meta, m) {
2625 if (!strcmp(m->name, "operation")) {
2626 if (!strcmp(lyd_get_meta_value(m), "create")) {
2627 op = NC_OP_CREATE;
2628 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
2629 op = NC_OP_DELETE;
2630 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
2631 op = NC_OP_REPLACE;
2632 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
2633 op = NC_OP_NONE;
2634 } else {
2635 ERR(NULL, "Unexpected operation (%s).", lyd_get_meta_value(m));
2636 ret = 1;
2637 goto cleanup;
2638 }
2639 }
2640 }
2641
2642 if (nc_session_server_parse_tree(tree, op)) {
2643 ret = 1;
2644 goto cleanup;
2645 }
2646
2647cleanup:
2648 /* UNLOCK */
2649 pthread_rwlock_unlock(&server_opts.config_lock);
2650 return ret;
2651}