blob: f616584633e2a7e476240d6c8e923c5f6dbd28f3 [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
romanf02273a2023-05-25 09:44:11 +02007 * Copyright (c) 2022-2023 CESNET, z.s.p.o.
romanc1d2b092023-02-02 08:58:27 +01008 *
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>
roman12644fe2023-06-08 11:06:42 +020019#include <ctype.h>
roman3f9b65c2023-06-05 14:26:58 +020020#include <pthread.h>
21#include <stdint.h>
romanc1d2b092023-02-02 08:58:27 +010022#include <stdlib.h>
23#include <string.h>
roman2eab4742023-06-06 10:00:26 +020024#include <unistd.h>
romanc1d2b092023-02-02 08:58:27 +010025
roman3f9b65c2023-06-05 14:26:58 +020026#include <libyang/libyang.h>
27
romanfaecc582023-06-15 16:13:31 +020028#ifdef NC_ENABLED_SSH_TLS
roman13145912023-08-17 15:36:54 +020029#include <openssl/err.h>
30#include <openssl/evp.h> // EVP_PKEY_free
31#include <openssl/x509.h> // d2i_PUBKEY
romanfaecc582023-06-15 16:13:31 +020032#include <openssl/x509_vfy.h> // X509_STORE_free
33#endif
34
romanc1d2b092023-02-02 08:58:27 +010035#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020036#include "config.h"
37#include "log_p.h"
romane028ef92023-02-24 16:33:08 +010038#include "server_config.h"
romanf02273a2023-05-25 09:44:11 +020039#include "server_config_p.h"
roman3f9b65c2023-06-05 14:26:58 +020040#include "session_p.h"
41
roman2eab4742023-06-06 10:00:26 +020042#ifdef NC_ENABLED_SSH_TLS
romanc1d2b092023-02-02 08:58:27 +010043
44/* All libssh supported host-key, key-exchange, encryption and mac algorithms as of version 0.10.90 */
45
46static const char *supported_hostkey_algs[] = {
romana6bf6ab2023-05-26 13:26:02 +020047 "openssh-ssh-ed25519-cert-v01", "openssh-ecdsa-sha2-nistp521-cert-v01",
48 "openssh-ecdsa-sha2-nistp384-cert-v01", "openssh-ecdsa-sha2-nistp256-cert-v01",
49 "openssh-rsa-sha2-512-cert-v01", "openssh-rsa-sha2-256-cert-v01",
50 "openssh-ssh-rsa-cert-v01", "openssh-ssh-dss-cert-v01",
romanc1d2b092023-02-02 08:58:27 +010051 "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256",
52 "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa", "ssh-dss", NULL
53};
54
55static const char *supported_kex_algs[] = {
romana6bf6ab2023-05-26 13:26:02 +020056 "diffie-hellman-group-exchange-sha1", "curve25519-sha256", "libssh-curve25519-sha256",
romanc1d2b092023-02-02 08:58:27 +010057 "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group18-sha512",
58 "diffie-hellman-group16-sha512", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group14-sha256", NULL
59};
60
61static const char *supported_encryption_algs[] = {
romana6bf6ab2023-05-26 13:26:02 +020062 "openssh-chacha20-poly1305", "openssh-aes256-gcm", "openssh-aes128-gcm",
romanc1d2b092023-02-02 08:58:27 +010063 "aes256-ctr", "aes192-ctr", "aes128-ctr", "aes256-cbc", "aes192-cbc", "aes128-cbc",
roman27215242023-03-10 14:55:00 +010064 "blowfish-cbc", "triple-des-cbc", "none", NULL
romanc1d2b092023-02-02 08:58:27 +010065};
66
67static const char *supported_mac_algs[] = {
romana6bf6ab2023-05-26 13:26:02 +020068 "openssh-hmac-sha2-256-etm", "openssh-hmac-sha2-512-etm", "openssh-hmac-sha1-etm",
romanc1d2b092023-02-02 08:58:27 +010069 "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", NULL
70};
71
roman2eab4742023-06-06 10:00:26 +020072#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +020073
romanc1d2b092023-02-02 08:58:27 +010074extern struct nc_server_opts server_opts;
75
roman4cb8bb12023-06-29 09:16:46 +020076static int
77is_listen(const struct lyd_node *node)
78{
79 assert(node);
80
81 while (node) {
82 if (!strcmp(LYD_NAME(node), "listen")) {
83 break;
84 }
85 node = lyd_parent(node);
86 }
87
88 return node != NULL;
89}
90
91static int
92is_ch(const struct lyd_node *node)
93{
94 assert(node);
95
96 while (node) {
97 if (!strcmp(LYD_NAME(node), "call-home")) {
98 break;
99 }
100 node = lyd_parent(node);
101 }
102
103 return node != NULL;
104}
105
106#ifdef NC_ENABLED_SSH_TLS
107
108static int
109is_ssh(const struct lyd_node *node)
110{
111 assert(node);
112
113 while (node) {
114 if (!strcmp(LYD_NAME(node), "ssh")) {
115 break;
116 }
117 node = lyd_parent(node);
118 }
119
120 return node != NULL;
121}
122
123static int
124is_tls(const struct lyd_node *node)
125{
126 assert(node);
127
128 while (node) {
129 if (!strcmp(LYD_NAME(node), "tls")) {
130 break;
131 }
132 node = lyd_parent(node);
133 }
134
135 return node != NULL;
136}
137
138#endif /* NC_ENABLED_SSH_TLS */
139
140static int
romanf02273a2023-05-25 09:44:11 +0200141nc_server_config_get_endpt(const struct lyd_node *node, struct nc_endpt **endpt, struct nc_bind **bind)
romanc1d2b092023-02-02 08:58:27 +0100142{
143 uint16_t i;
romanb9beb112023-07-18 09:06:58 +0200144 const char *name;
romanc1d2b092023-02-02 08:58:27 +0100145
roman4cb8bb12023-06-29 09:16:46 +0200146 assert(node && endpt);
romanb9beb112023-07-18 09:06:58 +0200147 name = LYD_NAME(node);
romanc1d2b092023-02-02 08:58:27 +0100148
149 while (node) {
150 if (!strcmp(LYD_NAME(node), "endpoint")) {
151 break;
152 }
153 node = lyd_parent(node);
154 }
155
156 if (!node) {
romanb9beb112023-07-18 09:06:58 +0200157 ERR(NULL, "Node \"%s\" is not contained in an endpoint subtree.", name);
romanc1d2b092023-02-02 08:58:27 +0100158 return 1;
159 }
160
161 node = lyd_child(node);
162 assert(!strcmp(LYD_NAME(node), "name"));
romanb9beb112023-07-18 09:06:58 +0200163 name = lyd_get_value(node);
romanc1d2b092023-02-02 08:58:27 +0100164
165 for (i = 0; i < server_opts.endpt_count; i++) {
romanb9beb112023-07-18 09:06:58 +0200166 if (!strcmp(server_opts.endpts[i].name, name)) {
romanc1d2b092023-02-02 08:58:27 +0100167 *endpt = &server_opts.endpts[i];
168 if (bind) {
169 *bind = &server_opts.binds[i];
170 }
171 return 0;
172 }
173 }
174
romanb9beb112023-07-18 09:06:58 +0200175 ERR(NULL, "Endpoint \"%s\" was not found.", name);
romanc1d2b092023-02-02 08:58:27 +0100176 return 1;
177}
178
roman4cb8bb12023-06-29 09:16:46 +0200179static int
roman5cbb6532023-06-22 12:53:17 +0200180nc_server_config_get_ch_client(const struct lyd_node *node, struct nc_ch_client **ch_client)
181{
182 uint16_t i;
romanb9beb112023-07-18 09:06:58 +0200183 const char *name;
roman5cbb6532023-06-22 12:53:17 +0200184
roman4cb8bb12023-06-29 09:16:46 +0200185 assert(node && ch_client);
romanb9beb112023-07-18 09:06:58 +0200186 name = LYD_NAME(node);
roman4cb8bb12023-06-29 09:16:46 +0200187
roman5cbb6532023-06-22 12:53:17 +0200188 while (node) {
189 if (!strcmp(LYD_NAME(node), "netconf-client")) {
190 break;
191 }
192 node = lyd_parent(node);
193 }
194
195 if (!node) {
romanb9beb112023-07-18 09:06:58 +0200196 ERR(NULL, "Node \"%s\" is not contained in a netconf-client subtree.", name);
roman5cbb6532023-06-22 12:53:17 +0200197 return 1;
198 }
199
200 node = lyd_child(node);
201 assert(!strcmp(LYD_NAME(node), "name"));
romanb9beb112023-07-18 09:06:58 +0200202 name = lyd_get_value(node);
roman5cbb6532023-06-22 12:53:17 +0200203
romanb9beb112023-07-18 09:06:58 +0200204 /* LOCK */
205 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
roman5cbb6532023-06-22 12:53:17 +0200206 for (i = 0; i < server_opts.ch_client_count; i++) {
romanb9beb112023-07-18 09:06:58 +0200207 if (!strcmp(server_opts.ch_clients[i].name, name)) {
roman5cbb6532023-06-22 12:53:17 +0200208 *ch_client = &server_opts.ch_clients[i];
romanb9beb112023-07-18 09:06:58 +0200209 /* UNLOCK */
210 pthread_rwlock_unlock(&server_opts.ch_client_lock);
roman5cbb6532023-06-22 12:53:17 +0200211 return 0;
212 }
213 }
214
romanb9beb112023-07-18 09:06:58 +0200215 /* UNLOCK */
216 pthread_rwlock_unlock(&server_opts.ch_client_lock);
217 ERR(NULL, "Call-home client \"%s\" was not found.", name);
roman5cbb6532023-06-22 12:53:17 +0200218 return 1;
219}
220
roman4cb8bb12023-06-29 09:16:46 +0200221#ifdef NC_ENABLED_SSH_TLS
222
223static int
224nc_server_config_get_ch_endpt(const struct lyd_node *node, struct nc_ch_endpt **ch_endpt)
roman5cbb6532023-06-22 12:53:17 +0200225{
226 uint16_t i;
romanb9beb112023-07-18 09:06:58 +0200227 const char *name;
roman4cb8bb12023-06-29 09:16:46 +0200228 struct nc_ch_client *ch_client;
229
230 assert(node && ch_endpt);
romanb9beb112023-07-18 09:06:58 +0200231 name = LYD_NAME(node);
roman4cb8bb12023-06-29 09:16:46 +0200232
233 if (nc_server_config_get_ch_client(node, &ch_client)) {
234 return 1;
235 }
roman5cbb6532023-06-22 12:53:17 +0200236
237 while (node) {
238 if (!strcmp(LYD_NAME(node), "endpoint")) {
239 break;
240 }
241 node = lyd_parent(node);
242 }
243
244 if (!node) {
romanb9beb112023-07-18 09:06:58 +0200245 ERR(NULL, "Node \"%s\" is not contained in a call-home endpoint subtree.", name);
roman5cbb6532023-06-22 12:53:17 +0200246 return 1;
247 }
248
249 node = lyd_child(node);
250 assert(!strcmp(LYD_NAME(node), "name"));
romanb9beb112023-07-18 09:06:58 +0200251 name = lyd_get_value(node);
roman5cbb6532023-06-22 12:53:17 +0200252
253 for (i = 0; i < ch_client->ch_endpt_count; i++) {
romanb9beb112023-07-18 09:06:58 +0200254 if (!strcmp(ch_client->ch_endpts[i].name, name)) {
roman5cbb6532023-06-22 12:53:17 +0200255 *ch_endpt = &ch_client->ch_endpts[i];
256 return 0;
257 }
258 }
259
romanb9beb112023-07-18 09:06:58 +0200260 ERR(NULL, "Call-home client's \"%s\" endpoint \"%s\" was not found.", ch_client->name, name);
roman5cbb6532023-06-22 12:53:17 +0200261 return 1;
262}
263
roman4cb8bb12023-06-29 09:16:46 +0200264static int
265nc_server_config_get_ssh_opts(const struct lyd_node *node, struct nc_server_ssh_opts **opts)
266{
267 struct nc_endpt *endpt;
268 struct nc_ch_endpt *ch_endpt;
roman3f9b65c2023-06-05 14:26:58 +0200269
roman4cb8bb12023-06-29 09:16:46 +0200270 assert(node && opts);
271
272 if (is_listen(node)) {
273 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
274 return 1;
275 }
276 *opts = endpt->opts.ssh;
277 } else {
278 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
279 return 1;
280 }
281 *opts = ch_endpt->opts.ssh;
282 }
283
284 return 0;
285}
286
287static int
288nc_server_config_get_hostkey(const struct lyd_node *node, struct nc_hostkey **hostkey)
romanc1d2b092023-02-02 08:58:27 +0100289{
290 uint16_t i;
romanb9beb112023-07-18 09:06:58 +0200291 const char *name;
roman4cb8bb12023-06-29 09:16:46 +0200292 struct nc_server_ssh_opts *opts;
romanc1d2b092023-02-02 08:58:27 +0100293
roman4cb8bb12023-06-29 09:16:46 +0200294 assert(node && hostkey);
romanb9beb112023-07-18 09:06:58 +0200295 name = LYD_NAME(node);
romanc1d2b092023-02-02 08:58:27 +0100296
297 while (node) {
298 if (!strcmp(LYD_NAME(node), "host-key")) {
299 break;
300 }
301 node = lyd_parent(node);
302 }
303
304 if (!node) {
romanb9beb112023-07-18 09:06:58 +0200305 ERR(NULL, "Node \"%s\" is not contained in a host-key subtree.", name);
romanc1d2b092023-02-02 08:58:27 +0100306 return 1;
307 }
308
309 node = lyd_child(node);
310 assert(!strcmp(LYD_NAME(node), "name"));
romanb9beb112023-07-18 09:06:58 +0200311 name = lyd_get_value(node);
romanc1d2b092023-02-02 08:58:27 +0100312
romanb9beb112023-07-18 09:06:58 +0200313 if (nc_server_config_get_ssh_opts(node, &opts)) {
314 return 1;
315 }
romanc1d2b092023-02-02 08:58:27 +0100316 for (i = 0; i < opts->hostkey_count; i++) {
romanb9beb112023-07-18 09:06:58 +0200317 if (!strcmp(opts->hostkeys[i].name, name)) {
romanc1d2b092023-02-02 08:58:27 +0100318 *hostkey = &opts->hostkeys[i];
319 return 0;
320 }
321 }
322
romanb9beb112023-07-18 09:06:58 +0200323 ERR(NULL, "Host-key \"%s\" was not found.", name);
romanc1d2b092023-02-02 08:58:27 +0100324 return 1;
325}
326
roman4cb8bb12023-06-29 09:16:46 +0200327static int
328nc_server_config_get_auth_client(const struct lyd_node *node, struct nc_client_auth **auth_client)
romanc1d2b092023-02-02 08:58:27 +0100329{
330 uint16_t i;
romanb9beb112023-07-18 09:06:58 +0200331 const char *name;
roman4cb8bb12023-06-29 09:16:46 +0200332 struct nc_server_ssh_opts *opts;
romanc1d2b092023-02-02 08:58:27 +0100333
roman4cb8bb12023-06-29 09:16:46 +0200334 assert(node && auth_client);
romanb9beb112023-07-18 09:06:58 +0200335 name = LYD_NAME(node);
romanc1d2b092023-02-02 08:58:27 +0100336
337 while (node) {
338 if (!strcmp(LYD_NAME(node), "user")) {
339 break;
340 }
341 node = lyd_parent(node);
342 }
343
344 if (!node) {
romanb9beb112023-07-18 09:06:58 +0200345 ERR(NULL, "Node \"%s\" is not contained in a client-authentication subtree.", name);
romanc1d2b092023-02-02 08:58:27 +0100346 return 1;
347 }
348
349 node = lyd_child(node);
350 assert(!strcmp(LYD_NAME(node), "name"));
romanb9beb112023-07-18 09:06:58 +0200351 name = lyd_get_value(node);
romanc1d2b092023-02-02 08:58:27 +0100352
romanb9beb112023-07-18 09:06:58 +0200353 if (nc_server_config_get_ssh_opts(node, &opts)) {
354 return 1;
355 }
romanc1d2b092023-02-02 08:58:27 +0100356 for (i = 0; i < opts->client_count; i++) {
romanb9beb112023-07-18 09:06:58 +0200357 if (!strcmp(opts->auth_clients[i].username, name)) {
romanc1d2b092023-02-02 08:58:27 +0100358 *auth_client = &opts->auth_clients[i];
359 return 0;
360 }
361 }
362
romanb9beb112023-07-18 09:06:58 +0200363 ERR(NULL, "Authorized key \"%s\" was not found.", name);
romanc1d2b092023-02-02 08:58:27 +0100364 return 1;
365}
366
roman4cb8bb12023-06-29 09:16:46 +0200367static int
368nc_server_config_get_pubkey(const struct lyd_node *node, struct nc_public_key **pubkey)
romanc1d2b092023-02-02 08:58:27 +0100369{
370 uint16_t i;
romanb9beb112023-07-18 09:06:58 +0200371 const char *name;
roman4cb8bb12023-06-29 09:16:46 +0200372 struct nc_client_auth *auth_client;
romanc1d2b092023-02-02 08:58:27 +0100373
roman4cb8bb12023-06-29 09:16:46 +0200374 assert(node && pubkey);
romanb9beb112023-07-18 09:06:58 +0200375 name = LYD_NAME(node);
romanc1d2b092023-02-02 08:58:27 +0100376
377 node = lyd_parent(node);
378 while (node) {
379 if (!strcmp(LYD_NAME(node), "public-key")) {
380 break;
381 }
382 node = lyd_parent(node);
383 }
384
385 if (!node) {
romanb9beb112023-07-18 09:06:58 +0200386 ERR(NULL, "Node \"%s\" is not contained in a public-key subtree.", name);
romanc1d2b092023-02-02 08:58:27 +0100387 return 1;
388 }
389
390 node = lyd_child(node);
391 assert(!strcmp(LYD_NAME(node), "name"));
romanb9beb112023-07-18 09:06:58 +0200392 name = lyd_get_value(node);
romanc1d2b092023-02-02 08:58:27 +0100393
romanb9beb112023-07-18 09:06:58 +0200394 if (nc_server_config_get_auth_client(node, &auth_client)) {
395 return 1;
396 }
romanc1d2b092023-02-02 08:58:27 +0100397 for (i = 0; i < auth_client->pubkey_count; i++) {
romanb9beb112023-07-18 09:06:58 +0200398 if (!strcmp(auth_client->pubkeys[i].name, name)) {
romanc1d2b092023-02-02 08:58:27 +0100399 *pubkey = &auth_client->pubkeys[i];
400 return 0;
401 }
402 }
403
romanb9beb112023-07-18 09:06:58 +0200404 ERR(NULL, "Public key \"%s\" was not found.", name);
romanc1d2b092023-02-02 08:58:27 +0100405 return 1;
406}
407
roman4cb8bb12023-06-29 09:16:46 +0200408static int
romanb6f44032023-06-30 15:07:56 +0200409nc_server_config_get_tls_opts(const struct lyd_node *node, struct nc_server_tls_opts **opts)
410{
411 struct nc_endpt *endpt;
412 struct nc_ch_endpt *ch_endpt;
413
414 assert(node && opts);
415
416 if (is_listen(node)) {
417 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
418 return 1;
419 }
420 *opts = endpt->opts.tls;
421 } else {
422 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
423 return 1;
424 }
425 *opts = ch_endpt->opts.tls;
426 }
427
428 return 0;
429}
430
431static int
roman4cb8bb12023-06-29 09:16:46 +0200432nc_server_config_get_cert(const struct lyd_node *node, int is_ee, struct nc_certificate **cert)
roman3f9b65c2023-06-05 14:26:58 +0200433{
434 uint16_t i;
romanb9beb112023-07-18 09:06:58 +0200435 const char *name;
roman4cb8bb12023-06-29 09:16:46 +0200436 struct nc_cert_grouping *auth_client;
romanb6f44032023-06-30 15:07:56 +0200437 struct nc_server_tls_opts *opts;
roman3f9b65c2023-06-05 14:26:58 +0200438
roman4cb8bb12023-06-29 09:16:46 +0200439 assert(node && cert);
romanb9beb112023-07-18 09:06:58 +0200440 name = LYD_NAME(node);
roman3f9b65c2023-06-05 14:26:58 +0200441
442 while (node) {
443 if (!strcmp(LYD_NAME(node), "certificate")) {
444 break;
445 }
446 node = lyd_parent(node);
447 }
448
449 if (!node) {
romanb9beb112023-07-18 09:06:58 +0200450 ERR(NULL, "Node \"%s\" is not contained in a certificate subtree.", name);
roman3f9b65c2023-06-05 14:26:58 +0200451 return 1;
452 }
453
454 node = lyd_child(node);
455 assert(!strcmp(LYD_NAME(node), "name"));
romanb9beb112023-07-18 09:06:58 +0200456 name = lyd_get_value(node);
457
458 if (nc_server_config_get_tls_opts(node, &opts)) {
459 return 1;
460 }
461 if (is_ee) {
462 auth_client = &opts->ee_certs;
463 } else {
464 auth_client = &opts->ca_certs;
465 }
roman3f9b65c2023-06-05 14:26:58 +0200466
467 for (i = 0; i < auth_client->cert_count; i++) {
romanb9beb112023-07-18 09:06:58 +0200468 if (!strcmp(auth_client->certs[i].name, name)) {
roman3f9b65c2023-06-05 14:26:58 +0200469 *cert = &auth_client->certs[i];
470 return 0;
471 }
472 }
473
romanb9beb112023-07-18 09:06:58 +0200474 ERR(NULL, "Certificate \"%s\" was not found.", name);
roman3f9b65c2023-06-05 14:26:58 +0200475 return 1;
476}
477
478static int
roman4cb8bb12023-06-29 09:16:46 +0200479nc_server_config_get_ctn(const struct lyd_node *node, struct nc_ctn **ctn)
roman3f9b65c2023-06-05 14:26:58 +0200480{
481 uint32_t id;
482 struct nc_ctn *iter;
romanb6f44032023-06-30 15:07:56 +0200483 struct nc_server_tls_opts *opts;
romanb9beb112023-07-18 09:06:58 +0200484 const char *name;
roman3f9b65c2023-06-05 14:26:58 +0200485
roman4cb8bb12023-06-29 09:16:46 +0200486 assert(node && ctn);
romanb9beb112023-07-18 09:06:58 +0200487 name = LYD_NAME(node);
roman3f9b65c2023-06-05 14:26:58 +0200488
489 node = lyd_parent(node);
490 while (node) {
491 if (!strcmp(LYD_NAME(node), "cert-to-name")) {
492 break;
493 }
494 node = lyd_parent(node);
495 }
496
497 if (!node) {
romanb9beb112023-07-18 09:06:58 +0200498 ERR(NULL, "Node \"%s\" is not contained in a cert-to-name subtree.", name);
roman3f9b65c2023-06-05 14:26:58 +0200499 return 1;
500 }
501
502 node = lyd_child(node);
503 assert(!strcmp(LYD_NAME(node), "id"));
504 id = strtoul(lyd_get_value(node), NULL, 10);
505
romanb9beb112023-07-18 09:06:58 +0200506 if (nc_server_config_get_tls_opts(node, &opts)) {
507 return 1;
508 }
509
romanb6f44032023-06-30 15:07:56 +0200510 iter = opts->ctn;
roman3f9b65c2023-06-05 14:26:58 +0200511 while (iter) {
512 if (iter->id == id) {
513 *ctn = iter;
514 return 0;
515 }
516
517 iter = iter->next;
518 }
519
520 ERR(NULL, "Cert-to-name entry with id \"%d\" was not found.", id);
521 return 1;
522}
523
roman2eab4742023-06-06 10:00:26 +0200524NC_PRIVKEY_FORMAT
525nc_server_config_get_private_key_type(const char *format)
526{
527 if (!strcmp(format, "rsa-private-key-format")) {
528 return NC_PRIVKEY_FORMAT_RSA;
529 } else if (!strcmp(format, "ec-private-key-format")) {
530 return NC_PRIVKEY_FORMAT_EC;
roman13145912023-08-17 15:36:54 +0200531 } else if (!strcmp(format, "private-key-info-format")) {
roman2eab4742023-06-06 10:00:26 +0200532 return NC_PRIVKEY_FORMAT_X509;
533 } else if (!strcmp(format, "openssh-private-key-format")) {
534 return NC_PRIVKEY_FORMAT_OPENSSH;
535 } else {
536 ERR(NULL, "Private key format (%s) not supported.", format);
537 return NC_PRIVKEY_FORMAT_UNKNOWN;
538 }
539}
540
541#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +0200542
romanba93eac2023-07-18 14:36:48 +0200543static int
544nc_server_config_get_ch_client_with_lock(const struct lyd_node *node, struct nc_ch_client **ch_client)
545{
546 uint16_t i;
547 const char *name;
548
549 assert(node && ch_client);
550 name = LYD_NAME(node);
551
552 while (node) {
553 if (!strcmp(LYD_NAME(node), "netconf-client")) {
554 break;
555 }
556 node = lyd_parent(node);
557 }
558
559 if (!node) {
560 ERR(NULL, "Node \"%s\" is not contained in a netconf-client subtree.", name);
561 return 1;
562 }
563
564 node = lyd_child(node);
565 assert(!strcmp(LYD_NAME(node), "name"));
566 name = lyd_get_value(node);
567
568 /* LOCK */
569 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
570 for (i = 0; i < server_opts.ch_client_count; i++) {
571 if (!strcmp(server_opts.ch_clients[i].name, name)) {
572 /* LOCK */
573 pthread_mutex_lock(&server_opts.ch_clients[i].lock);
574 *ch_client = &server_opts.ch_clients[i];
575 return 0;
576 }
577 }
578
579 ERR(NULL, "Call-home client \"%s\" was not found.", name);
580 return 1;
581}
582
583static void
584nc_ch_client_unlock(struct nc_ch_client *client)
585{
586 assert(client);
587
588 pthread_mutex_unlock(&client->lock);
589 pthread_rwlock_unlock(&server_opts.ch_client_lock);
590}
591
romanf02273a2023-05-25 09:44:11 +0200592int
romanc1d2b092023-02-02 08:58:27 +0100593equal_parent_name(const struct lyd_node *node, uint16_t parent_count, const char *parent_name)
594{
595 uint16_t i;
596
597 assert(node && parent_count > 0 && parent_name);
598
599 node = lyd_parent(node);
600 for (i = 1; i < parent_count; i++) {
601 node = lyd_parent(node);
602 }
603
604 if (!strcmp(LYD_NAME(node), parent_name)) {
605 return 1;
606 }
607
608 return 0;
609}
610
romanf02273a2023-05-25 09:44:11 +0200611int
612nc_server_config_realloc(const char *key_value, void **ptr, size_t size, uint16_t *count)
613{
614 int ret = 0;
615 void *tmp;
616 char **name;
617
618 tmp = realloc(*ptr, (*count + 1) * size);
619 if (!tmp) {
620 ERRMEM;
621 ret = 1;
622 goto cleanup;
623 }
624 *ptr = tmp;
625
626 /* set the newly allocated memory to 0 */
627 memset((char *)(*ptr) + (*count * size), 0, size);
628 (*count)++;
629
630 /* access the first member of the supposed structure */
631 name = (char **)((*ptr) + ((*count - 1) * size));
632
633 /* and set it's value */
634 *name = strdup(key_value);
635 if (!*name) {
636 ERRMEM;
637 ret = 1;
638 goto cleanup;
639 }
640
641cleanup:
642 return ret;
643}
644
roman3f9b65c2023-06-05 14:26:58 +0200645static void
646nc_server_config_del_endpt_name(struct nc_endpt *endpt)
647{
648 free(endpt->name);
649 endpt->name = NULL;
650}
651
roman2eab4742023-06-06 10:00:26 +0200652#ifdef NC_ENABLED_SSH_TLS
653
roman3f9b65c2023-06-05 14:26:58 +0200654static void
655nc_server_config_del_local_address(struct nc_bind *bind)
656{
657 free(bind->address);
658 bind->address = NULL;
659}
660
romanc1d2b092023-02-02 08:58:27 +0100661static void
roman874fed12023-05-25 10:20:01 +0200662nc_server_config_del_auth_client_pam_name(struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100663{
664 free(auth_client->pam_config_name);
665 auth_client->pam_config_name = NULL;
666}
667
668static void
roman874fed12023-05-25 10:20:01 +0200669nc_server_config_del_auth_client_pam_dir(struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100670{
671 free(auth_client->pam_config_dir);
672 auth_client->pam_config_dir = NULL;
673}
674
675static void
roman0bbc19c2023-05-26 09:59:09 +0200676nc_server_config_del_endpt_reference(struct nc_endpt *endpt)
677{
678 free(endpt->referenced_endpt_name);
679 endpt->referenced_endpt_name = NULL;
680}
681
682static void
roman874fed12023-05-25 10:20:01 +0200683nc_server_config_del_hostkey_name(struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +0100684{
685 free(hostkey->name);
686 hostkey->name = NULL;
687}
688
689static void
roman874fed12023-05-25 10:20:01 +0200690nc_server_config_del_public_key(struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +0100691{
roman3f9b65c2023-06-05 14:26:58 +0200692 free(hostkey->key.pubkey_data);
693 hostkey->key.pubkey_data = NULL;
romanc1d2b092023-02-02 08:58:27 +0100694}
695
696static void
roman874fed12023-05-25 10:20:01 +0200697nc_server_config_del_private_key(struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +0100698{
roman3f9b65c2023-06-05 14:26:58 +0200699 free(hostkey->key.privkey_data);
700 hostkey->key.privkey_data = NULL;
romanc1d2b092023-02-02 08:58:27 +0100701}
702
703static void
roman874fed12023-05-25 10:20:01 +0200704nc_server_config_del_auth_client_pubkey_name(struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100705{
706 free(pubkey->name);
707 pubkey->name = NULL;
708}
709
710static void
roman874fed12023-05-25 10:20:01 +0200711nc_server_config_del_auth_client_pubkey_pub_base64(struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100712{
roman3f9b65c2023-06-05 14:26:58 +0200713 free(pubkey->data);
714 pubkey->data = NULL;
romanc1d2b092023-02-02 08:58:27 +0100715}
716
717static void
roman874fed12023-05-25 10:20:01 +0200718nc_server_config_del_auth_client_password(struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100719{
720 free(auth_client->password);
721 auth_client->password = NULL;
722}
723
724static void
roman874fed12023-05-25 10:20:01 +0200725nc_server_config_del_hostkey_algs(struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100726{
727 free(opts->hostkey_algs);
728 opts->hostkey_algs = NULL;
729}
730
731static void
roman874fed12023-05-25 10:20:01 +0200732nc_server_config_del_kex_algs(struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100733{
734 free(opts->kex_algs);
735 opts->kex_algs = NULL;
736}
737
738static void
roman874fed12023-05-25 10:20:01 +0200739nc_server_config_del_encryption_algs(struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100740{
741 free(opts->encryption_algs);
742 opts->encryption_algs = NULL;
743}
744
745static void
roman874fed12023-05-25 10:20:01 +0200746nc_server_config_del_mac_algs(struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100747{
748 free(opts->mac_algs);
749 opts->mac_algs = NULL;
750}
751
752static void
romandd019a92023-09-14 10:17:07 +0200753nc_server_config_del_keystore_reference(struct nc_hostkey *hostkey)
754{
755 free(hostkey->ks_ref);
756 hostkey->ks_ref = NULL;
757}
758
759static void
roman874fed12023-05-25 10:20:01 +0200760nc_server_config_del_hostkey(struct nc_server_ssh_opts *opts, struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +0100761{
roman874fed12023-05-25 10:20:01 +0200762 assert(hostkey->store == NC_STORE_LOCAL || hostkey->store == NC_STORE_KEYSTORE);
romanc1d2b092023-02-02 08:58:27 +0100763
roman874fed12023-05-25 10:20:01 +0200764 if (hostkey->store == NC_STORE_LOCAL) {
765 nc_server_config_del_public_key(hostkey);
766 nc_server_config_del_private_key(hostkey);
romandd019a92023-09-14 10:17:07 +0200767 } else {
768 nc_server_config_del_keystore_reference(hostkey);
romanc1d2b092023-02-02 08:58:27 +0100769 }
770
roman874fed12023-05-25 10:20:01 +0200771 nc_server_config_del_hostkey_name(hostkey);
romanc1d2b092023-02-02 08:58:27 +0100772 opts->hostkey_count--;
773 if (!opts->hostkey_count) {
774 free(opts->hostkeys);
775 opts->hostkeys = NULL;
roman33981232023-07-08 11:55:13 +0200776 } else if (hostkey == &opts->hostkeys[opts->hostkey_count + 1]) {
777 memcpy(hostkey, &opts->hostkeys[opts->hostkey_count], sizeof *opts->hostkeys);
romanc1d2b092023-02-02 08:58:27 +0100778 }
779}
780
781static void
roman874fed12023-05-25 10:20:01 +0200782nc_server_config_del_auth_client_pubkey(struct nc_client_auth *auth_client, struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100783{
roman874fed12023-05-25 10:20:01 +0200784 nc_server_config_del_auth_client_pubkey_name(pubkey);
785 nc_server_config_del_auth_client_pubkey_pub_base64(pubkey);
romanc1d2b092023-02-02 08:58:27 +0100786
787 auth_client->pubkey_count--;
788 if (!auth_client->pubkey_count) {
789 free(auth_client->pubkeys);
790 auth_client->pubkeys = NULL;
roman33981232023-07-08 11:55:13 +0200791 } else if (pubkey == &auth_client->pubkeys[auth_client->pubkey_count + 1]) {
792 memcpy(pubkey, &auth_client->pubkeys[auth_client->pubkey_count], sizeof *auth_client->pubkeys);
romanc1d2b092023-02-02 08:58:27 +0100793 }
794}
795
796static void
roman874fed12023-05-25 10:20:01 +0200797nc_server_config_del_auth_client(struct nc_server_ssh_opts *opts, struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100798{
799 uint16_t i, pubkey_count;
800
roman4cb8bb12023-06-29 09:16:46 +0200801 free(auth_client->username);
802 auth_client->username = NULL;
803
roman874fed12023-05-25 10:20:01 +0200804 if (auth_client->store == NC_STORE_LOCAL) {
romanc1d2b092023-02-02 08:58:27 +0100805 pubkey_count = auth_client->pubkey_count;
806 for (i = 0; i < pubkey_count; i++) {
roman874fed12023-05-25 10:20:01 +0200807 nc_server_config_del_auth_client_pubkey(auth_client, &auth_client->pubkeys[i]);
romanc1d2b092023-02-02 08:58:27 +0100808 }
romanc1d2b092023-02-02 08:58:27 +0100809 }
810
roman874fed12023-05-25 10:20:01 +0200811 nc_server_config_del_auth_client_password(auth_client);
812 nc_server_config_del_auth_client_pam_name(auth_client);
813 nc_server_config_del_auth_client_pam_dir(auth_client);
romanc1d2b092023-02-02 08:58:27 +0100814
815 opts->client_count--;
816 if (!opts->client_count) {
817 free(opts->auth_clients);
818 opts->auth_clients = NULL;
roman33981232023-07-08 11:55:13 +0200819 } else if (auth_client == &opts->auth_clients[opts->client_count + 1]) {
820 memcpy(auth_client, &opts->auth_clients[opts->client_count], sizeof *opts->auth_clients);
romanc1d2b092023-02-02 08:58:27 +0100821 }
822}
823
824static void
roman874fed12023-05-25 10:20:01 +0200825nc_server_config_del_ssh(struct nc_bind *bind, struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100826{
827 uint16_t i, hostkey_count, client_count;
828
roman874fed12023-05-25 10:20:01 +0200829 nc_server_config_del_local_address(bind);
romanc1d2b092023-02-02 08:58:27 +0100830 if (bind->sock > -1) {
831 close(bind->sock);
832 }
833
834 /* store in variable because it gets decremented in the function call */
835 hostkey_count = opts->hostkey_count;
836 for (i = 0; i < hostkey_count; i++) {
roman874fed12023-05-25 10:20:01 +0200837 nc_server_config_del_hostkey(opts, &opts->hostkeys[i]);
romanc1d2b092023-02-02 08:58:27 +0100838 }
839
840 client_count = opts->client_count;
841 for (i = 0; i < client_count; i++) {
roman874fed12023-05-25 10:20:01 +0200842 nc_server_config_del_auth_client(opts, &opts->auth_clients[i]);
romanc1d2b092023-02-02 08:58:27 +0100843 }
844
roman874fed12023-05-25 10:20:01 +0200845 nc_server_config_del_hostkey_algs(opts);
846 nc_server_config_del_kex_algs(opts);
847 nc_server_config_del_encryption_algs(opts);
848 nc_server_config_del_mac_algs(opts);
romanc1d2b092023-02-02 08:58:27 +0100849
850 free(opts);
851 opts = NULL;
852}
853
854void
roman874fed12023-05-25 10:20:01 +0200855nc_server_config_del_endpt_ssh(struct nc_endpt *endpt, struct nc_bind *bind)
romanc1d2b092023-02-02 08:58:27 +0100856{
roman874fed12023-05-25 10:20:01 +0200857 nc_server_config_del_endpt_name(endpt);
roman0bbc19c2023-05-26 09:59:09 +0200858 nc_server_config_del_endpt_reference(endpt);
roman874fed12023-05-25 10:20:01 +0200859 nc_server_config_del_ssh(bind, endpt->opts.ssh);
romanc1d2b092023-02-02 08:58:27 +0100860
861 server_opts.endpt_count--;
862 if (!server_opts.endpt_count) {
863 free(server_opts.endpts);
864 free(server_opts.binds);
865 server_opts.endpts = NULL;
866 server_opts.binds = NULL;
roman33981232023-07-08 11:55:13 +0200867 } else if (endpt == &server_opts.endpts[server_opts.endpt_count + 1]) {
868 memcpy(endpt, &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
869 memcpy(bind, &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
romanc1d2b092023-02-02 08:58:27 +0100870 }
871}
872
roman2eab4742023-06-06 10:00:26 +0200873#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +0200874
roman45cec4e2023-02-17 10:21:39 +0100875void
roman874fed12023-05-25 10:20:01 +0200876nc_server_config_del_unix_socket(struct nc_bind *bind, struct nc_server_unix_opts *opts)
roman83683fb2023-02-24 09:15:23 +0100877{
878 if (bind->sock > -1) {
879 close(bind->sock);
880 }
881
romand0b78372023-09-14 10:06:03 +0200882 unlink(bind->address);
roman83683fb2023-02-24 09:15:23 +0100883 free(bind->address);
884 free(opts->address);
885
886 free(opts);
roman83683fb2023-02-24 09:15:23 +0100887}
888
889void
roman874fed12023-05-25 10:20:01 +0200890nc_server_config_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *bind)
roman83683fb2023-02-24 09:15:23 +0100891{
roman874fed12023-05-25 10:20:01 +0200892 nc_server_config_del_endpt_name(endpt);
893 nc_server_config_del_unix_socket(bind, endpt->opts.unixsock);
roman83683fb2023-02-24 09:15:23 +0100894
895 server_opts.endpt_count--;
896 if (!server_opts.endpt_count) {
897 free(server_opts.endpts);
898 free(server_opts.binds);
899 server_opts.endpts = NULL;
900 server_opts.binds = NULL;
roman33981232023-07-08 11:55:13 +0200901 } else if (endpt == &server_opts.endpts[server_opts.endpt_count + 1]) {
902 memcpy(endpt, &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
903 memcpy(bind, &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
roman83683fb2023-02-24 09:15:23 +0100904 }
905}
906
roman2eab4742023-06-06 10:00:26 +0200907#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +0200908
909static void
romanfaecc582023-06-15 16:13:31 +0200910nc_server_config_del_url(struct nc_server_tls_opts *opts)
911{
912 free(opts->crl_url);
913 opts->crl_url = NULL;
914}
915
916static void
917nc_server_config_del_path(struct nc_server_tls_opts *opts)
918{
919 free(opts->crl_path);
920 opts->crl_path = NULL;
921}
922
923static void
roman12644fe2023-06-08 11:06:42 +0200924nc_server_config_tls_del_ciphers(struct nc_server_tls_opts *opts)
925{
926 free(opts->ciphers);
927 opts->ciphers = NULL;
928}
929
930static void
roman3f9b65c2023-06-05 14:26:58 +0200931nc_server_config_tls_del_public_key(struct nc_server_tls_opts *opts)
932{
933 free(opts->pubkey_data);
934 opts->pubkey_data = NULL;
935}
936
937static void
938nc_server_config_tls_del_cleartext_private_key(struct nc_server_tls_opts *opts)
939{
940 free(opts->privkey_data);
941 opts->privkey_data = NULL;
942}
943
944static void
945nc_server_config_tls_del_cert_data(struct nc_server_tls_opts *opts)
946{
947 free(opts->cert_data);
948 opts->cert_data = NULL;
949}
950
951static void
952nc_server_config_tls_del_cert_data_certificate(struct nc_certificate *cert)
953{
954 free(cert->data);
955 cert->data = NULL;
956}
957
958static void
959nc_server_config_del_fingerprint(struct nc_ctn *ctn)
960{
961 free(ctn->fingerprint);
962 ctn->fingerprint = NULL;
963}
964
965static void
966nc_server_config_del_cert(struct nc_cert_grouping *certs, struct nc_certificate *cert)
967{
968 free(cert->name);
969 cert->name = NULL;
970
971 free(cert->data);
972 cert->data = NULL;
973
974 certs->cert_count--;
975 if (!certs->cert_count) {
976 free(certs->certs);
977 certs->certs = NULL;
roman33981232023-07-08 11:55:13 +0200978 } else if (cert == &certs->certs[certs->cert_count + 1]) {
979 memcpy(cert, &certs->certs[certs->cert_count], sizeof *certs->certs);
roman3f9b65c2023-06-05 14:26:58 +0200980 }
981}
982
983static void
984nc_server_config_tls_del_certs(struct nc_cert_grouping *ca)
985{
986 uint16_t i, cert_count;
987
988 if (ca->store == NC_STORE_LOCAL) {
989 cert_count = ca->cert_count;
990 for (i = 0; i < cert_count; i++) {
991 nc_server_config_del_cert(ca, &ca->certs[i]);
992 }
993 }
994}
995
996static void
997nc_server_config_del_ctn(struct nc_server_tls_opts *opts, struct nc_ctn *ctn)
998{
999 struct nc_ctn *iter;
1000
1001 free(ctn->fingerprint);
1002 ctn->fingerprint = NULL;
1003
1004 free(ctn->name);
1005 ctn->name = NULL;
1006
1007 if (opts->ctn == ctn) {
1008 /* it's the first in the list */
1009 opts->ctn = ctn->next;
1010 free(ctn);
1011 return;
1012 }
1013
1014 iter = opts->ctn;
1015 while (iter) {
1016 if (iter->next == ctn) {
1017 /* found the ctn */
1018 break;
1019 }
1020 iter = iter->next;
1021 }
1022
1023 iter->next = ctn->next;
1024 free(ctn);
1025}
1026
1027static void
roman12644fe2023-06-08 11:06:42 +02001028nc_server_config_tls_del_ctns(struct nc_server_tls_opts *opts)
roman3f9b65c2023-06-05 14:26:58 +02001029{
1030 struct nc_ctn *cur, *next;
1031
1032 cur = opts->ctn;
1033 while (cur) {
1034 next = cur->next;
1035 free(cur->fingerprint);
1036 free(cur->name);
1037 free(cur);
1038 cur = next;
1039 }
1040 opts->ctn = NULL;
1041}
1042
1043static void
1044nc_server_config_del_tls(struct nc_bind *bind, struct nc_server_tls_opts *opts)
1045{
1046 nc_server_config_del_local_address(bind);
1047 if (bind->sock > -1) {
1048 close(bind->sock);
1049 }
1050
1051 if (opts->store == NC_STORE_LOCAL) {
1052 nc_server_config_tls_del_public_key(opts);
1053 nc_server_config_tls_del_cleartext_private_key(opts);
1054 nc_server_config_tls_del_cert_data(opts);
1055 }
1056
1057 nc_server_config_tls_del_certs(&opts->ca_certs);
1058 nc_server_config_tls_del_certs(&opts->ee_certs);
1059
romanfaecc582023-06-15 16:13:31 +02001060 nc_server_config_del_path(opts);
1061 nc_server_config_del_url(opts);
1062 X509_STORE_free(opts->crl_store);
1063 opts->crl_store = NULL;
1064
roman12644fe2023-06-08 11:06:42 +02001065 nc_server_config_tls_del_ctns(opts);
1066 nc_server_config_tls_del_ciphers(opts);
roman3f9b65c2023-06-05 14:26:58 +02001067
1068 free(opts);
1069}
1070
1071static void
1072nc_server_config_del_endpt_tls(struct nc_endpt *endpt, struct nc_bind *bind)
1073{
1074 nc_server_config_del_endpt_name(endpt);
roman2e797ef2023-06-19 10:47:49 +02001075 nc_server_config_del_endpt_reference(endpt);
roman3f9b65c2023-06-05 14:26:58 +02001076 nc_server_config_del_tls(bind, endpt->opts.tls);
1077
1078 server_opts.endpt_count--;
1079 if (!server_opts.endpt_count) {
1080 free(server_opts.endpts);
1081 free(server_opts.binds);
1082 server_opts.endpts = NULL;
1083 server_opts.binds = NULL;
roman33981232023-07-08 11:55:13 +02001084 } else if (endpt == &server_opts.endpts[server_opts.endpt_count + 1]) {
1085 memcpy(endpt, &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
1086 memcpy(bind, &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
roman3f9b65c2023-06-05 14:26:58 +02001087 }
1088}
1089
roman5cbb6532023-06-22 12:53:17 +02001090static void
1091nc_server_config_del_remote_address(struct nc_ch_endpt *ch_endpt)
1092{
1093 free(ch_endpt->address);
1094 ch_endpt->address = NULL;
1095}
1096
roman2eab4742023-06-06 10:00:26 +02001097#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +02001098
romanc1d2b092023-02-02 08:58:27 +01001099/* presence container */
1100int
romanf02273a2023-05-25 09:44:11 +02001101nc_server_config_listen(struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001102{
roman0bbc19c2023-05-26 09:59:09 +02001103 uint16_t i, endpt_count;
romanc1d2b092023-02-02 08:58:27 +01001104
romanf02273a2023-05-25 09:44:11 +02001105 (void) node;
1106
romanc1d2b092023-02-02 08:58:27 +01001107 assert(op == NC_OP_CREATE || op == NC_OP_DELETE);
1108
1109 if (op == NC_OP_DELETE) {
roman0bbc19c2023-05-26 09:59:09 +02001110 endpt_count = server_opts.endpt_count;
1111 for (i = 0; i < endpt_count; i++) {
roman456f92d2023-04-28 10:28:12 +02001112 switch (server_opts.endpts[i].ti) {
roman2eab4742023-06-06 10:00:26 +02001113#ifdef NC_ENABLED_SSH_TLS
roman456f92d2023-04-28 10:28:12 +02001114 case NC_TI_LIBSSH:
roman874fed12023-05-25 10:20:01 +02001115 nc_server_config_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]);
roman456f92d2023-04-28 10:28:12 +02001116 break;
roman456f92d2023-04-28 10:28:12 +02001117 case NC_TI_OPENSSL:
roman3f9b65c2023-06-05 14:26:58 +02001118 nc_server_config_del_endpt_tls(&server_opts.endpts[i], &server_opts.binds[i]);
roman456f92d2023-04-28 10:28:12 +02001119 break;
roman2eab4742023-06-06 10:00:26 +02001120#endif /* NC_ENABLED_SSH_TLS */
roman456f92d2023-04-28 10:28:12 +02001121 case NC_TI_UNIX:
roman874fed12023-05-25 10:20:01 +02001122 nc_server_config_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]);
roman456f92d2023-04-28 10:28:12 +02001123 break;
1124 case NC_TI_NONE:
1125 case NC_TI_FD:
1126 ERRINT;
1127 return 1;
roman83683fb2023-02-24 09:15:23 +01001128 }
romanc1d2b092023-02-02 08:58:27 +01001129 }
1130 }
1131
1132 return 0;
1133}
1134
roman5cbb6532023-06-22 12:53:17 +02001135#ifdef NC_ENABLED_SSH_TLS
1136
1137static void
1138nc_server_config_ch_del_ssh(struct nc_server_ssh_opts *opts)
1139{
1140 uint16_t i, hostkey_count, client_count;
1141
1142 /* store in variable because it gets decremented in the function call */
1143 hostkey_count = opts->hostkey_count;
1144 for (i = 0; i < hostkey_count; i++) {
1145 nc_server_config_del_hostkey(opts, &opts->hostkeys[i]);
1146 }
1147
1148 client_count = opts->client_count;
1149 for (i = 0; i < client_count; i++) {
1150 nc_server_config_del_auth_client(opts, &opts->auth_clients[i]);
1151 }
1152
1153 nc_server_config_del_hostkey_algs(opts);
1154 nc_server_config_del_kex_algs(opts);
1155 nc_server_config_del_encryption_algs(opts);
1156 nc_server_config_del_mac_algs(opts);
1157
1158 free(opts);
1159 opts = NULL;
1160}
1161
1162static void
romanb6f44032023-06-30 15:07:56 +02001163nc_server_config_ch_del_tls(struct nc_server_tls_opts *opts)
1164{
1165 if (opts->store == NC_STORE_LOCAL) {
1166 nc_server_config_tls_del_public_key(opts);
1167 nc_server_config_tls_del_cleartext_private_key(opts);
1168 nc_server_config_tls_del_cert_data(opts);
1169 }
1170
1171 nc_server_config_tls_del_certs(&opts->ca_certs);
1172 nc_server_config_tls_del_certs(&opts->ee_certs);
1173
1174 nc_server_config_del_path(opts);
1175 nc_server_config_del_url(opts);
1176 X509_STORE_free(opts->crl_store);
1177 opts->crl_store = NULL;
1178
1179 nc_server_config_tls_del_ctns(opts);
1180 nc_server_config_tls_del_ciphers(opts);
1181
1182 free(opts);
1183}
1184
1185static void
roman5cbb6532023-06-22 12:53:17 +02001186nc_server_config_ch_del_endpt_address(struct nc_ch_endpt *ch_endpt)
1187{
1188 free(ch_endpt->address);
1189 ch_endpt->address = NULL;
1190}
1191
1192#endif /* NC_ENABLED_SSH_TLS */
1193
1194static void
1195nc_server_config_ch_del_endpt(struct nc_ch_client *ch_client, struct nc_ch_endpt *ch_endpt)
1196{
1197 free(ch_endpt->name);
1198 ch_endpt->name = NULL;
1199
1200#ifdef NC_ENABLED_SSH_TLS
1201 nc_server_config_ch_del_endpt_address(ch_endpt);
1202 if (ch_endpt->sock_pending > -1) {
1203 close(ch_endpt->sock_pending);
1204 ch_endpt->sock_pending = -1;
1205 }
1206#endif /* NC_ENABLED_SSH_TLS */
1207
1208 switch (ch_endpt->ti) {
1209#ifdef NC_ENABLED_SSH_TLS
1210 case NC_TI_LIBSSH:
1211 nc_server_config_ch_del_ssh(ch_endpt->opts.ssh);
1212 break;
romanb6f44032023-06-30 15:07:56 +02001213 case NC_TI_OPENSSL:
1214 nc_server_config_ch_del_tls(ch_endpt->opts.tls);
1215 break;
roman5cbb6532023-06-22 12:53:17 +02001216#endif /* NC_ENABLED_SSH_TLS */
1217 default:
1218 ERRINT;
1219 break;
1220 }
1221
1222 ch_client->ch_endpt_count--;
1223 if (!ch_client->ch_endpt_count) {
1224 free(ch_client->ch_endpts);
1225 ch_client->ch_endpts = NULL;
1226 }
1227}
1228
1229static void
1230nc_server_config_ch_del_client(struct nc_ch_client *ch_client)
1231{
1232 uint16_t i, ch_endpt_count;
romanba93eac2023-07-18 14:36:48 +02001233 struct nc_ch_client client;
1234 pthread_t tid;
roman5cbb6532023-06-22 12:53:17 +02001235
romanba93eac2023-07-18 14:36:48 +02001236 /* WR LOCK */
roman5cbb6532023-06-22 12:53:17 +02001237 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1238
romanba93eac2023-07-18 14:36:48 +02001239 /* copy the client we want to delete into a local variable */
1240 memcpy(&client, ch_client, sizeof *ch_client);
1241 /* get his tid */
1242 tid = client.tid;
roman5cbb6532023-06-22 12:53:17 +02001243
romanba93eac2023-07-18 14:36:48 +02001244 /* delete the client */
roman5cbb6532023-06-22 12:53:17 +02001245 server_opts.ch_client_count--;
1246 if (!server_opts.ch_client_count) {
1247 free(server_opts.ch_clients);
1248 server_opts.ch_clients = NULL;
romanba93eac2023-07-18 14:36:48 +02001249 } else {
1250 memcpy(ch_client, &server_opts.ch_clients[server_opts.ch_client_count], sizeof *server_opts.ch_clients);
roman5cbb6532023-06-22 12:53:17 +02001251 }
1252
romanba93eac2023-07-18 14:36:48 +02001253 /* WR UNLOCK */
roman5cbb6532023-06-22 12:53:17 +02001254 pthread_rwlock_unlock(&server_opts.ch_client_lock);
romanba93eac2023-07-18 14:36:48 +02001255
1256 /* RD LOCK */
1257 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
1258
1259 if (client.thread_data->thread_running) {
1260 /* CH COND LOCK */
1261 pthread_mutex_lock(&client.thread_data->cond_lock);
1262 client.thread_data->thread_running = 0;
1263 pthread_cond_signal(&client.thread_data->cond);
1264 /* CH COND UNLOCK */
1265 pthread_mutex_unlock(&client.thread_data->cond_lock);
1266
1267 /* RD UNLOCK */
1268 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1269
1270 /* wait for the thread to terminate */
1271 pthread_join(tid, NULL);
1272 }
1273
1274 /* free its members */
1275 free(client.name);
1276
1277 ch_endpt_count = client.ch_endpt_count;
1278 for (i = 0; i < ch_endpt_count; i++) {
1279 nc_server_config_ch_del_endpt(&client, &client.ch_endpts[i]);
1280 }
roman5cbb6532023-06-22 12:53:17 +02001281}
1282
1283void
1284nc_server_config_ch(const struct lyd_node *node, NC_OPERATION op)
1285{
1286 uint16_t i, ch_client_count;
1287
1288 (void) node;
1289
1290 if (op == NC_OP_DELETE) {
1291 ch_client_count = server_opts.ch_client_count;
1292 for (i = 0; i < ch_client_count; i++) {
1293 nc_server_config_ch_del_client(&server_opts.ch_clients[i]);
1294 }
1295 }
1296}
1297
romanc1d2b092023-02-02 08:58:27 +01001298/* default leaf */
1299static int
romane028ef92023-02-24 16:33:08 +01001300nc_server_config_idle_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001301{
romanb6f44032023-06-30 15:07:56 +02001302 struct nc_ch_client *ch_client;
1303
romanc1d2b092023-02-02 08:58:27 +01001304 assert(!strcmp(LYD_NAME(node), "idle-timeout"));
1305
romanb6f44032023-06-30 15:07:56 +02001306 if (is_listen(node)) {
romanc1d2b092023-02-02 08:58:27 +01001307 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1308 server_opts.idle_timeout = strtoul(lyd_get_value(node), NULL, 10);
1309 } else {
1310 /* default value */
1311 server_opts.idle_timeout = 3600;
1312 }
romanb6f44032023-06-30 15:07:56 +02001313 } else {
1314 /* call-home idle timeout */
1315 if (nc_server_config_get_ch_client(node, &ch_client)) {
1316 return 1;
1317 }
1318
1319 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1320 ch_client->idle_timeout = strtoul(lyd_get_value(node), NULL, 10);
1321 } else if (op == NC_OP_DELETE) {
1322 ch_client->idle_timeout = 180;
1323 }
romanc1d2b092023-02-02 08:58:27 +01001324 }
1325
1326 return 0;
1327}
1328
1329static int
roman874fed12023-05-25 10:20:01 +02001330nc_server_config_create_bind(void)
romanc1d2b092023-02-02 08:58:27 +01001331{
1332 int ret = 0;
1333 void *tmp;
1334
1335 tmp = realloc(server_opts.binds, (server_opts.endpt_count + 1) * sizeof *server_opts.binds);
1336 if (!tmp) {
1337 ERRMEM;
1338 ret = 1;
1339 goto cleanup;
1340 }
1341 server_opts.binds = tmp;
1342 memset(&server_opts.binds[server_opts.endpt_count], 0, sizeof *server_opts.binds);
1343
1344 server_opts.binds[server_opts.endpt_count].sock = -1;
1345
1346cleanup:
1347 return ret;
1348}
1349
1350static int
roman874fed12023-05-25 10:20:01 +02001351nc_server_config_create_endpoint(const struct lyd_node *node)
romanc1d2b092023-02-02 08:58:27 +01001352{
roman874fed12023-05-25 10:20:01 +02001353 if (nc_server_config_create_bind()) {
romanf02273a2023-05-25 09:44:11 +02001354 return 1;
romanc1d2b092023-02-02 08:58:27 +01001355 }
romanc1d2b092023-02-02 08:58:27 +01001356
1357 node = lyd_child(node);
1358 assert(!strcmp(LYD_NAME(node), "name"));
1359
romanf02273a2023-05-25 09:44:11 +02001360 return nc_server_config_realloc(lyd_get_value(node), (void **)&server_opts.endpts, sizeof *server_opts.endpts, &server_opts.endpt_count);
romanc1d2b092023-02-02 08:58:27 +01001361}
1362
roman5cbb6532023-06-22 12:53:17 +02001363static int
1364nc_server_config_ch_create_endpoint(const struct lyd_node *node, struct nc_ch_client *ch_client)
1365{
1366 node = lyd_child(node);
1367 assert(!strcmp(LYD_NAME(node), "name"));
1368
1369 return nc_server_config_realloc(lyd_get_value(node), (void **)&ch_client->ch_endpts, sizeof *ch_client->ch_endpts, &ch_client->ch_endpt_count);
1370}
1371
romanc1d2b092023-02-02 08:58:27 +01001372/* list */
1373static int
romane028ef92023-02-24 16:33:08 +01001374nc_server_config_endpoint(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001375{
1376 int ret = 0;
1377 struct nc_endpt *endpt;
1378 struct nc_bind *bind;
roman5cbb6532023-06-22 12:53:17 +02001379 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001380
1381 assert(!strcmp(LYD_NAME(node), "endpoint"));
1382
roman5cbb6532023-06-22 12:53:17 +02001383 if (is_listen(node)) {
1384 /* listen */
1385 if (op == NC_OP_CREATE) {
1386 ret = nc_server_config_create_endpoint(node);
1387 if (ret) {
1388 goto cleanup;
1389 }
1390 } else if (op == NC_OP_DELETE) {
1391 /* free all children */
1392 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
1393 ret = 1;
1394 goto cleanup;
1395 }
1396
1397 switch (endpt->ti) {
1398#ifdef NC_ENABLED_SSH_TLS
1399 case NC_TI_LIBSSH:
1400 nc_server_config_del_endpt_ssh(endpt, bind);
1401 break;
1402 case NC_TI_OPENSSL:
1403 nc_server_config_del_endpt_tls(endpt, bind);
1404 break;
1405#endif /* NC_ENABLED_SSH_TLS */
1406 case NC_TI_UNIX:
1407 nc_server_config_del_endpt_unix_socket(endpt, bind);
1408 break;
1409 case NC_TI_NONE:
1410 case NC_TI_FD:
1411 ERRINT;
1412 ret = 1;
1413 goto cleanup;
1414 }
romanc1d2b092023-02-02 08:58:27 +01001415 }
roman5cbb6532023-06-22 12:53:17 +02001416 } else if (is_ch(node)) {
romanba93eac2023-07-18 14:36:48 +02001417 /* LOCK */
1418 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanc1d2b092023-02-02 08:58:27 +01001419 ret = 1;
1420 goto cleanup;
1421 }
roman3f9b65c2023-06-05 14:26:58 +02001422
roman5cbb6532023-06-22 12:53:17 +02001423 if (op == NC_OP_CREATE) {
1424 ret = nc_server_config_ch_create_endpoint(node, ch_client);
1425 if (ret) {
1426 goto cleanup;
1427 }
1428
1429 /* init ch sock */
1430 ch_client->ch_endpts[ch_client->ch_endpt_count - 1].sock_pending = -1;
roman3f9b65c2023-06-05 14:26:58 +02001431 }
romanc1d2b092023-02-02 08:58:27 +01001432 }
1433
1434cleanup:
romanba93eac2023-07-18 14:36:48 +02001435 if (is_ch(node)) {
1436 /* UNLOCK */
1437 nc_ch_client_unlock(ch_client);
1438 }
romanc1d2b092023-02-02 08:58:27 +01001439 return ret;
1440}
1441
roman2eab4742023-06-06 10:00:26 +02001442#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +02001443
romanc1d2b092023-02-02 08:58:27 +01001444static int
roman874fed12023-05-25 10:20:01 +02001445nc_server_config_create_ssh(struct nc_endpt *endpt)
romanc1d2b092023-02-02 08:58:27 +01001446{
1447 endpt->ti = NC_TI_LIBSSH;
1448 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1449 if (!endpt->opts.ssh) {
1450 ERRMEM;
1451 return 1;
1452 }
1453
1454 return 0;
1455}
1456
roman5cbb6532023-06-22 12:53:17 +02001457static int
1458nc_server_config_ch_create_ssh(struct nc_ch_endpt *ch_endpt)
1459{
1460 ch_endpt->ti = NC_TI_LIBSSH;
1461 ch_endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1462 if (!ch_endpt->opts.ssh) {
1463 ERRMEM;
1464 return 1;
1465 }
1466
1467 return 0;
1468}
1469
romanc1d2b092023-02-02 08:58:27 +01001470/* NP container */
1471static int
romane028ef92023-02-24 16:33:08 +01001472nc_server_config_ssh(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001473{
1474 struct nc_endpt *endpt;
1475 struct nc_bind *bind;
roman5cbb6532023-06-22 12:53:17 +02001476 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02001477 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001478 int ret = 0;
1479
1480 assert(!strcmp(LYD_NAME(node), "ssh"));
1481
roman5cbb6532023-06-22 12:53:17 +02001482 if (is_listen(node)) {
1483 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
1484 ret = 1;
romanc1d2b092023-02-02 08:58:27 +01001485 goto cleanup;
1486 }
roman5cbb6532023-06-22 12:53:17 +02001487
1488 if (op == NC_OP_CREATE) {
1489 ret = nc_server_config_create_ssh(endpt);
1490 if (ret) {
1491 goto cleanup;
1492 }
1493 } else if (op == NC_OP_DELETE) {
1494 nc_server_config_del_ssh(bind, endpt->opts.ssh);
1495 }
1496 } else {
romanba93eac2023-07-18 14:36:48 +02001497 /* LOCK */
1498 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1499 ret = 1;
1500 goto cleanup;
1501 }
1502
roman4cb8bb12023-06-29 09:16:46 +02001503 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02001504 ret = 1;
1505 goto cleanup;
1506 }
1507
1508 if (op == NC_OP_CREATE) {
1509 ret = nc_server_config_ch_create_ssh(ch_endpt);
1510 if (ret) {
1511 goto cleanup;
1512 }
romanb6f44032023-06-30 15:07:56 +02001513 } else if (op == NC_OP_DELETE) {
1514 nc_server_config_ch_del_ssh(ch_endpt->opts.ssh);
roman5cbb6532023-06-22 12:53:17 +02001515 }
romanc1d2b092023-02-02 08:58:27 +01001516 }
1517
1518cleanup:
romanba93eac2023-07-18 14:36:48 +02001519 if (is_ch(node)) {
1520 /* UNLOCK */
1521 nc_ch_client_unlock(ch_client);
1522 }
romanc1d2b092023-02-02 08:58:27 +01001523 return ret;
1524}
1525
roman3f9b65c2023-06-05 14:26:58 +02001526static int
1527nc_server_config_create_tls(struct nc_endpt *endpt)
1528{
1529 endpt->ti = NC_TI_OPENSSL;
1530 endpt->opts.tls = calloc(1, sizeof *endpt->opts.tls);
1531 if (!endpt->opts.tls) {
1532 ERRMEM;
1533 return 1;
1534 }
1535
1536 return 0;
1537}
1538
1539static int
romanb6f44032023-06-30 15:07:56 +02001540nc_server_config_ch_create_tls(struct nc_ch_endpt *ch_endpt)
1541{
1542 ch_endpt->ti = NC_TI_OPENSSL;
1543 ch_endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1544 if (!ch_endpt->opts.tls) {
1545 ERRMEM;
1546 return 1;
1547 }
1548
1549 return 0;
1550}
1551
1552static int
roman3f9b65c2023-06-05 14:26:58 +02001553nc_server_config_tls(const struct lyd_node *node, NC_OPERATION op)
1554{
1555 struct nc_endpt *endpt;
1556 struct nc_bind *bind;
romanb6f44032023-06-30 15:07:56 +02001557 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02001558 struct nc_ch_client *ch_client;
roman3f9b65c2023-06-05 14:26:58 +02001559 int ret = 0;
1560
1561 assert(!strcmp(LYD_NAME(node), "tls"));
1562
romanb6f44032023-06-30 15:07:56 +02001563 if (is_listen(node)) {
1564 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
1565 ret = 1;
roman3f9b65c2023-06-05 14:26:58 +02001566 goto cleanup;
1567 }
romanb6f44032023-06-30 15:07:56 +02001568
1569 if (op == NC_OP_CREATE) {
1570 ret = nc_server_config_create_tls(endpt);
1571 if (ret) {
1572 goto cleanup;
1573 }
1574 } else if (op == NC_OP_DELETE) {
1575 nc_server_config_del_tls(bind, endpt->opts.tls);
1576 }
1577 } else {
romanba93eac2023-07-18 14:36:48 +02001578 /* LOCK */
1579 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1580 ret = 1;
1581 goto cleanup;
1582 }
1583
romanb6f44032023-06-30 15:07:56 +02001584 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
1585 ret = 1;
1586 goto cleanup;
1587 }
1588
1589 if (op == NC_OP_CREATE) {
1590 ret = nc_server_config_ch_create_tls(ch_endpt);
1591 if (ret) {
1592 goto cleanup;
1593 }
1594 }
roman3f9b65c2023-06-05 14:26:58 +02001595 }
1596
1597cleanup:
romanba93eac2023-07-18 14:36:48 +02001598 if (is_ch(node)) {
1599 /* UNLOCK */
1600 nc_ch_client_unlock(ch_client);
1601 }
roman3f9b65c2023-06-05 14:26:58 +02001602 return ret;
1603}
1604
roman2eab4742023-06-06 10:00:26 +02001605#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +02001606
romanc1d2b092023-02-02 08:58:27 +01001607static int
1608nc_server_config_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port)
1609{
1610 int sock = -1, set_addr, ret = 0;
1611
roman83683fb2023-02-24 09:15:23 +01001612 assert((address && !port) || (!address && port) || (endpt->ti == NC_TI_UNIX));
romanc1d2b092023-02-02 08:58:27 +01001613
1614 if (address) {
1615 set_addr = 1;
1616 } else {
1617 set_addr = 0;
1618 }
1619
1620 if (set_addr) {
1621 port = bind->port;
1622 } else {
1623 address = bind->address;
1624 }
1625
romanc1d2b092023-02-02 08:58:27 +01001626 /* we have all the information we need to create a listening socket */
roman83683fb2023-02-24 09:15:23 +01001627 if ((address && port) || (endpt->ti == NC_TI_UNIX)) {
romanc1d2b092023-02-02 08:58:27 +01001628 /* create new socket, close the old one */
roman83683fb2023-02-24 09:15:23 +01001629 if (endpt->ti == NC_TI_UNIX) {
1630 sock = nc_sock_listen_unix(endpt->opts.unixsock);
1631 } else {
1632 sock = nc_sock_listen_inet(address, port, &endpt->ka);
1633 }
1634
romanc1d2b092023-02-02 08:58:27 +01001635 if (sock == -1) {
1636 ret = 1;
1637 goto cleanup;
1638 }
1639
1640 if (bind->sock > -1) {
1641 close(bind->sock);
1642 }
1643 bind->sock = sock;
1644 }
1645
1646 if (sock > -1) {
1647 switch (endpt->ti) {
roman83683fb2023-02-24 09:15:23 +01001648 case NC_TI_UNIX:
1649 VRB(NULL, "Listening on %s for UNIX connections.", endpt->opts.unixsock->address);
1650 break;
roman2eab4742023-06-06 10:00:26 +02001651#ifdef NC_ENABLED_SSH_TLS
romanc1d2b092023-02-02 08:58:27 +01001652 case NC_TI_LIBSSH:
1653 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
1654 break;
romanc1d2b092023-02-02 08:58:27 +01001655 case NC_TI_OPENSSL:
1656 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
1657 break;
roman2eab4742023-06-06 10:00:26 +02001658#endif /* NC_ENABLED_SSH_TLS */
romanc1d2b092023-02-02 08:58:27 +01001659 default:
1660 ERRINT;
1661 ret = 1;
1662 break;
1663 }
1664 }
1665
1666cleanup:
1667 return ret;
1668}
1669
roman2eab4742023-06-06 10:00:26 +02001670#ifdef NC_ENABLED_SSH_TLS
1671
romanc1d2b092023-02-02 08:58:27 +01001672/* mandatory leaf */
1673static int
romane028ef92023-02-24 16:33:08 +01001674nc_server_config_local_address(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001675{
1676 struct nc_endpt *endpt;
1677 struct nc_bind *bind;
1678 int ret = 0;
1679
1680 (void) op;
1681
1682 assert(!strcmp(LYD_NAME(node), "local-address"));
1683
1684 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001685 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001686 ret = 1;
1687 goto cleanup;
1688 }
1689
roman874fed12023-05-25 10:20:01 +02001690 nc_server_config_del_local_address(bind);
romanc1d2b092023-02-02 08:58:27 +01001691 bind->address = strdup(lyd_get_value(node));
1692 if (!bind->address) {
1693 ERRMEM;
1694 ret = 1;
1695 goto cleanup;
1696 }
1697
1698 ret = nc_server_config_set_address_port(endpt, bind, lyd_get_value(node), 0);
1699 if (ret) {
1700 goto cleanup;
1701 }
1702 }
1703
1704cleanup:
1705 return ret;
1706}
1707
1708/* leaf with default value */
1709static int
romane028ef92023-02-24 16:33:08 +01001710nc_server_config_local_port(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001711{
1712 struct nc_endpt *endpt;
1713 struct nc_bind *bind;
1714 int ret = 0;
1715
1716 assert(!strcmp(LYD_NAME(node), "local-port"));
1717
1718 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001719 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001720 ret = 1;
1721 goto cleanup;
1722 }
1723
1724 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1725 bind->port = strtoul(lyd_get_value(node), NULL, 10);
1726 } else {
1727 /* delete -> set to default */
1728 bind->port = 0;
1729 }
1730
1731 ret = nc_server_config_set_address_port(endpt, bind, NULL, bind->port);
1732 if (ret) {
1733 goto cleanup;
1734 }
1735 }
1736
1737cleanup:
1738 return ret;
1739}
1740
1741/* P container */
1742static int
romane028ef92023-02-24 16:33:08 +01001743nc_server_config_keepalives(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001744{
roman5cbb6532023-06-22 12:53:17 +02001745 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01001746 struct nc_endpt *endpt;
1747 struct nc_bind *bind;
roman5cbb6532023-06-22 12:53:17 +02001748 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02001749 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001750
1751 assert(!strcmp(LYD_NAME(node), "keepalives"));
1752
roman5cbb6532023-06-22 12:53:17 +02001753 if (is_listen(node) && equal_parent_name(node, 1, "tcp-server-parameters")) {
romanf02273a2023-05-25 09:44:11 +02001754 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001755 ret = 1;
1756 goto cleanup;
1757 }
1758
1759 if (op == NC_OP_CREATE) {
1760 endpt->ka.enabled = 1;
1761 } else {
1762 endpt->ka.enabled = 0;
1763 }
1764 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
1765 if (ret) {
1766 goto cleanup;
1767 }
roman5cbb6532023-06-22 12:53:17 +02001768 } else if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
romanba93eac2023-07-18 14:36:48 +02001769 /* LOCK */
1770 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1771 return 1;
1772 }
1773
roman4cb8bb12023-06-29 09:16:46 +02001774 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02001775 ret = 1;
1776 goto cleanup;
1777 }
1778
1779 if (op == NC_OP_CREATE) {
1780 ch_endpt->ka.enabled = 1;
1781 } else {
1782 ch_endpt->ka.enabled = 0;
1783 }
romanc1d2b092023-02-02 08:58:27 +01001784 }
1785
1786cleanup:
romanba93eac2023-07-18 14:36:48 +02001787 if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
1788 /* UNLOCK */
1789 nc_ch_client_unlock(ch_client);
1790 }
romanc1d2b092023-02-02 08:58:27 +01001791 return ret;
1792}
1793
1794/* mandatory leaf */
1795static int
romane028ef92023-02-24 16:33:08 +01001796nc_server_config_idle_time(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001797{
roman5cbb6532023-06-22 12:53:17 +02001798 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01001799 struct nc_endpt *endpt;
1800 struct nc_bind *bind;
roman5cbb6532023-06-22 12:53:17 +02001801 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02001802 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001803
1804 assert(!strcmp(LYD_NAME(node), "idle-time"));
1805
roman5cbb6532023-06-22 12:53:17 +02001806 if (is_listen(node) && equal_parent_name(node, 2, "tcp-server-parameters")) {
romanf02273a2023-05-25 09:44:11 +02001807 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001808 ret = 1;
1809 goto cleanup;
1810 }
1811
1812 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1813 endpt->ka.idle_time = strtoul(lyd_get_value(node), NULL, 10);
1814 } else {
1815 endpt->ka.idle_time = 0;
1816 }
1817 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
1818 if (ret) {
1819 goto cleanup;
1820 }
roman5cbb6532023-06-22 12:53:17 +02001821 } else if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) {
romanba93eac2023-07-18 14:36:48 +02001822 /* LOCK */
1823 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1824 return 1;
1825 }
1826
roman4cb8bb12023-06-29 09:16:46 +02001827 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02001828 ret = 1;
1829 goto cleanup;
1830 }
1831
1832 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1833 ch_endpt->ka.idle_time = strtoul(lyd_get_value(node), NULL, 10);
1834 } else {
1835 ch_endpt->ka.idle_time = 0;
1836 }
romanc1d2b092023-02-02 08:58:27 +01001837 }
1838
1839cleanup:
romanba93eac2023-07-18 14:36:48 +02001840 if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
1841 /* UNLOCK */
1842 nc_ch_client_unlock(ch_client);
1843 }
romanc1d2b092023-02-02 08:58:27 +01001844 return ret;
1845}
1846
1847/* mandatory leaf */
1848static int
romane028ef92023-02-24 16:33:08 +01001849nc_server_config_max_probes(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001850{
roman5cbb6532023-06-22 12:53:17 +02001851 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01001852 struct nc_endpt *endpt;
1853 struct nc_bind *bind;
roman5cbb6532023-06-22 12:53:17 +02001854 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02001855 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001856
1857 assert(!strcmp(LYD_NAME(node), "max-probes"));
1858
roman5cbb6532023-06-22 12:53:17 +02001859 if (is_listen(node) && equal_parent_name(node, 2, "tcp-server-parameters")) {
romanf02273a2023-05-25 09:44:11 +02001860 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001861 ret = 1;
1862 goto cleanup;
1863 }
1864
1865 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1866 endpt->ka.max_probes = strtoul(lyd_get_value(node), NULL, 10);
1867 } else {
1868 endpt->ka.max_probes = 0;
1869 }
1870 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
1871 if (ret) {
1872 goto cleanup;
1873 }
roman5cbb6532023-06-22 12:53:17 +02001874 } else if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) {
romanba93eac2023-07-18 14:36:48 +02001875 /* LOCK */
1876 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1877 return 1;
1878 }
1879
roman4cb8bb12023-06-29 09:16:46 +02001880 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02001881 ret = 1;
1882 goto cleanup;
1883 }
1884
1885 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1886 ch_endpt->ka.max_probes = strtoul(lyd_get_value(node), NULL, 10);
1887 } else {
1888 ch_endpt->ka.max_probes = 0;
1889 }
romanc1d2b092023-02-02 08:58:27 +01001890 }
1891
1892cleanup:
romanba93eac2023-07-18 14:36:48 +02001893 if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
1894 /* UNLOCK */
1895 nc_ch_client_unlock(ch_client);
1896 }
romanc1d2b092023-02-02 08:58:27 +01001897 return ret;
1898}
1899
1900/* mandatory leaf */
1901static int
romane028ef92023-02-24 16:33:08 +01001902nc_server_config_probe_interval(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001903{
roman5cbb6532023-06-22 12:53:17 +02001904 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01001905 struct nc_endpt *endpt;
1906 struct nc_bind *bind;
roman5cbb6532023-06-22 12:53:17 +02001907 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02001908 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001909
1910 assert(!strcmp(LYD_NAME(node), "probe-interval"));
1911
roman5cbb6532023-06-22 12:53:17 +02001912 if (is_listen(node) && equal_parent_name(node, 2, "tcp-server-parameters")) {
romanf02273a2023-05-25 09:44:11 +02001913 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001914 ret = 1;
1915 goto cleanup;
1916 }
1917
1918 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1919 endpt->ka.probe_interval = strtoul(lyd_get_value(node), NULL, 10);
1920 } else {
1921 endpt->ka.probe_interval = 0;
1922 }
1923 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
1924 if (ret) {
1925 goto cleanup;
1926 }
roman5cbb6532023-06-22 12:53:17 +02001927 } else if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) {
romanba93eac2023-07-18 14:36:48 +02001928 /* LOCK */
1929 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1930 return 1;
1931 }
1932
roman4cb8bb12023-06-29 09:16:46 +02001933 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02001934 ret = 1;
1935 goto cleanup;
1936 }
1937
1938 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1939 ch_endpt->ka.probe_interval = strtoul(lyd_get_value(node), NULL, 10);
1940 } else {
1941 ch_endpt->ka.max_probes = 0;
1942 }
romanc1d2b092023-02-02 08:58:27 +01001943 }
1944
1945cleanup:
romanba93eac2023-07-18 14:36:48 +02001946 if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
1947 /* UNLOCK */
1948 nc_ch_client_unlock(ch_client);
1949 }
romanc1d2b092023-02-02 08:58:27 +01001950 return ret;
1951}
1952
1953static int
roman874fed12023-05-25 10:20:01 +02001954nc_server_config_create_host_key(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +01001955{
romanf02273a2023-05-25 09:44:11 +02001956 node = lyd_child(node);
1957 assert(!strcmp(LYD_NAME(node), "name"));
romanc1d2b092023-02-02 08:58:27 +01001958
romanf02273a2023-05-25 09:44:11 +02001959 return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->hostkeys, sizeof *opts->hostkeys, &opts->hostkey_count);
romanc1d2b092023-02-02 08:58:27 +01001960}
1961
1962/* list */
1963static int
romane028ef92023-02-24 16:33:08 +01001964nc_server_config_host_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001965{
roman5cbb6532023-06-22 12:53:17 +02001966 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01001967 struct nc_hostkey *hostkey;
roman4cb8bb12023-06-29 09:16:46 +02001968 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02001969 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001970
1971 assert(!strcmp(LYD_NAME(node), "host-key"));
1972
roman4cb8bb12023-06-29 09:16:46 +02001973 if (nc_server_config_get_ssh_opts(node, &opts)) {
1974 ret = 1;
1975 goto cleanup;
1976 }
romanc1d2b092023-02-02 08:58:27 +01001977
roman4cb8bb12023-06-29 09:16:46 +02001978 if (equal_parent_name(node, 1, "server-identity")) {
romanba93eac2023-07-18 14:36:48 +02001979 /* LOCK */
1980 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1981 return 1;
1982 }
1983
romanc1d2b092023-02-02 08:58:27 +01001984 if (op == NC_OP_CREATE) {
roman4cb8bb12023-06-29 09:16:46 +02001985 ret = nc_server_config_create_host_key(node, opts);
romanc1d2b092023-02-02 08:58:27 +01001986 if (ret) {
1987 goto cleanup;
1988 }
1989 } else if (op == NC_OP_DELETE) {
roman4cb8bb12023-06-29 09:16:46 +02001990 if (nc_server_config_get_hostkey(node, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01001991 ret = 1;
1992 goto cleanup;
1993 }
roman4cb8bb12023-06-29 09:16:46 +02001994 nc_server_config_del_hostkey(opts, hostkey);
roman5cbb6532023-06-22 12:53:17 +02001995 }
romanc1d2b092023-02-02 08:58:27 +01001996 }
1997
1998cleanup:
romanba93eac2023-07-18 14:36:48 +02001999 if (is_ch(node) && equal_parent_name(node, 1, "server-identity")) {
2000 /* UNLOCK */
2001 nc_ch_client_unlock(ch_client);
2002 }
romanc1d2b092023-02-02 08:58:27 +01002003 return ret;
2004}
2005
2006/* mandatory leaf */
romane028ef92023-02-24 16:33:08 +01002007static int
2008nc_server_config_public_key_format(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002009{
roman3f9b65c2023-06-05 14:26:58 +02002010 int ret = 0;
roman5cbb6532023-06-22 12:53:17 +02002011 const char *format;
roman3f9b65c2023-06-05 14:26:58 +02002012 NC_PUBKEY_FORMAT pubkey_type;
roman8edee342023-03-31 13:25:48 +02002013 struct nc_public_key *pubkey;
romanc1d2b092023-02-02 08:58:27 +01002014 struct nc_hostkey *hostkey;
romanb6f44032023-06-30 15:07:56 +02002015 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002016 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002017
2018 assert(!strcmp(LYD_NAME(node), "public-key-format"));
2019
2020 format = ((struct lyd_node_term *)node)->value.ident->name;
roman3f9b65c2023-06-05 14:26:58 +02002021 if (!strcmp(format, "ssh-public-key-format")) {
roman13145912023-08-17 15:36:54 +02002022 pubkey_type = NC_PUBKEY_FORMAT_SSH;
roman3f9b65c2023-06-05 14:26:58 +02002023 } else if (!strcmp(format, "subject-public-key-info-format")) {
2024 pubkey_type = NC_PUBKEY_FORMAT_X509;
2025 } else {
2026 ERR(NULL, "Public key format (%s) not supported.", format);
2027 ret = 1;
2028 goto cleanup;
2029 }
romanc1d2b092023-02-02 08:58:27 +01002030
romanba93eac2023-07-18 14:36:48 +02002031 /* LOCK */
2032 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2033 return 1;
2034 }
2035
roman4cb8bb12023-06-29 09:16:46 +02002036 if (is_ssh(node) && equal_parent_name(node, 4, "server-identity")) {
roman5cbb6532023-06-22 12:53:17 +02002037 /* SSH hostkey public key fmt */
roman4cb8bb12023-06-29 09:16:46 +02002038 if (nc_server_config_get_hostkey(node, &hostkey)) {
roman5cbb6532023-06-22 12:53:17 +02002039 ret = 1;
2040 goto cleanup;
2041 }
2042
2043 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2044 hostkey->key.pubkey_type = pubkey_type;
2045 }
roman4cb8bb12023-06-29 09:16:46 +02002046 } else if (is_ssh(node) && equal_parent_name(node, 6, "client-authentication")) {
roman5cbb6532023-06-22 12:53:17 +02002047 /* SSH client auth public key fmt */
roman4cb8bb12023-06-29 09:16:46 +02002048 if (nc_server_config_get_pubkey(node, &pubkey)) {
romanc1d2b092023-02-02 08:58:27 +01002049 ret = 1;
2050 goto cleanup;
2051 }
2052
2053 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman3f9b65c2023-06-05 14:26:58 +02002054 pubkey->type = pubkey_type;
romanc1d2b092023-02-02 08:58:27 +01002055 }
romanb6f44032023-06-30 15:07:56 +02002056 } else if (is_tls(node) && equal_parent_name(node, 3, "server-identity")) {
2057 /* TLS server-identity */
2058 if (nc_server_config_get_tls_opts(node, &opts)) {
roman4cb8bb12023-06-29 09:16:46 +02002059 ret = 1;
2060 goto cleanup;
2061 }
2062
roman5cbb6532023-06-22 12:53:17 +02002063 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02002064 opts->pubkey_type = pubkey_type;
romanc1d2b092023-02-02 08:58:27 +01002065 }
romanc1d2b092023-02-02 08:58:27 +01002066 }
2067
2068cleanup:
romanba93eac2023-07-18 14:36:48 +02002069 if (is_ch(node)) {
2070 /* UNLOCK */
2071 nc_ch_client_unlock(ch_client);
2072 }
romanc1d2b092023-02-02 08:58:27 +01002073 return ret;
2074}
2075
2076static int
roman874fed12023-05-25 10:20:01 +02002077nc_server_config_create_auth_key_public_key_list(const struct lyd_node *node, struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +01002078{
romanc1d2b092023-02-02 08:58:27 +01002079 assert(!strcmp(LYD_NAME(node), "public-key"));
2080
romanc1d2b092023-02-02 08:58:27 +01002081 node = lyd_child(node);
2082 assert(!strcmp(LYD_NAME(node), "name"));
2083
romanf02273a2023-05-25 09:44:11 +02002084 return nc_server_config_realloc(lyd_get_value(node), (void **)&auth_client->pubkeys, sizeof *auth_client->pubkeys, &auth_client->pubkey_count);
romanc1d2b092023-02-02 08:58:27 +01002085}
2086
2087static int
roman874fed12023-05-25 10:20:01 +02002088nc_server_config_replace_auth_key_public_key_leaf(const struct lyd_node *node, struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +01002089{
roman874fed12023-05-25 10:20:01 +02002090 nc_server_config_del_auth_client_pubkey_pub_base64(pubkey);
romanc1d2b092023-02-02 08:58:27 +01002091
roman3f9b65c2023-06-05 14:26:58 +02002092 pubkey->data = strdup(lyd_get_value(node));
2093 if (!pubkey->data) {
romanc1d2b092023-02-02 08:58:27 +01002094 ERRMEM;
2095 return 1;
2096 }
2097
2098 return 0;
2099}
2100
2101static int
roman874fed12023-05-25 10:20:01 +02002102nc_server_config_replace_host_key_public_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +01002103{
roman874fed12023-05-25 10:20:01 +02002104 nc_server_config_del_public_key(hostkey);
romanc1d2b092023-02-02 08:58:27 +01002105
roman3f9b65c2023-06-05 14:26:58 +02002106 hostkey->key.pubkey_data = strdup(lyd_get_value(node));
2107 if (!hostkey->key.pubkey_data) {
romanc1d2b092023-02-02 08:58:27 +01002108 ERRMEM;
2109 return 1;
2110 }
2111
2112 return 0;
2113}
2114
roman3f9b65c2023-06-05 14:26:58 +02002115static int
2116nc_server_config_tls_replace_server_public_key(const struct lyd_node *node, struct nc_server_tls_opts *opts)
2117{
2118 nc_server_config_tls_del_public_key(opts);
2119
2120 opts->pubkey_data = strdup(lyd_get_value(node));
2121 if (!opts->pubkey_data) {
2122 ERRMEM;
2123 return 1;
2124 }
2125
2126 return 0;
2127}
2128
romanc1d2b092023-02-02 08:58:27 +01002129static int
romane028ef92023-02-24 16:33:08 +01002130nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002131{
roman3f9b65c2023-06-05 14:26:58 +02002132 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002133 struct nc_hostkey *hostkey;
2134 struct nc_client_auth *auth_client;
roman8edee342023-03-31 13:25:48 +02002135 struct nc_public_key *pubkey;
romanb6f44032023-06-30 15:07:56 +02002136 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002137 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002138
2139 assert(!strcmp(LYD_NAME(node), "public-key"));
2140
romanba93eac2023-07-18 14:36:48 +02002141 /* LOCK */
2142 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2143 return 1;
2144 }
2145
roman4cb8bb12023-06-29 09:16:46 +02002146 if (is_ssh(node) && equal_parent_name(node, 3, "host-key")) {
roman3f9b65c2023-06-05 14:26:58 +02002147 /* server's public-key, mandatory leaf */
roman4cb8bb12023-06-29 09:16:46 +02002148 if (nc_server_config_get_hostkey(node, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01002149 ret = 1;
2150 goto cleanup;
2151 }
2152
roman13145912023-08-17 15:36:54 +02002153 /* the public key must not be SubjectPublicKeyInfoFormat, as per the ietf-netconf-server model */
roman51a1e192023-09-14 10:13:45 +02002154 if (nc_is_pk_subject_public_key_info(lyd_get_value(node))) {
roman13145912023-08-17 15:36:54 +02002155 ERR(NULL, "Using Public Key in the SubjectPublicKeyInfo format as an SSH hostkey is forbidden!");
2156 ret = 1;
2157 goto cleanup;
2158 }
2159
romanc1d2b092023-02-02 08:58:27 +01002160 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanf02273a2023-05-25 09:44:11 +02002161 /* set to local */
roman874fed12023-05-25 10:20:01 +02002162 hostkey->store = NC_STORE_LOCAL;
romanf02273a2023-05-25 09:44:11 +02002163
roman874fed12023-05-25 10:20:01 +02002164 ret = nc_server_config_replace_host_key_public_key(node, hostkey);
romanc1d2b092023-02-02 08:58:27 +01002165 if (ret) {
2166 goto cleanup;
2167 }
2168 }
roman4cb8bb12023-06-29 09:16:46 +02002169 } else if (is_ssh(node) && equal_parent_name(node, 5, "client-authentication")) {
romanc1d2b092023-02-02 08:58:27 +01002170 /* client auth pubkeys, list */
roman4cb8bb12023-06-29 09:16:46 +02002171 if (nc_server_config_get_auth_client(node, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01002172 ret = 1;
2173 goto cleanup;
2174 }
2175
2176 if (op == NC_OP_CREATE) {
romanf02273a2023-05-25 09:44:11 +02002177 /* set to local */
roman874fed12023-05-25 10:20:01 +02002178 auth_client->store = NC_STORE_LOCAL;
romanf02273a2023-05-25 09:44:11 +02002179
roman874fed12023-05-25 10:20:01 +02002180 ret = nc_server_config_create_auth_key_public_key_list(node, auth_client);
romanc1d2b092023-02-02 08:58:27 +01002181 if (ret) {
2182 goto cleanup;
2183 }
2184 } else if (op == NC_OP_DELETE) {
roman4cb8bb12023-06-29 09:16:46 +02002185 if (nc_server_config_get_pubkey(node, &pubkey)) {
romanc1d2b092023-02-02 08:58:27 +01002186 ret = 1;
2187 goto cleanup;
2188 }
2189
roman874fed12023-05-25 10:20:01 +02002190 nc_server_config_del_auth_client_pubkey(auth_client, pubkey);
romanc1d2b092023-02-02 08:58:27 +01002191 }
roman4cb8bb12023-06-29 09:16:46 +02002192 } else if (is_ssh(node) && equal_parent_name(node, 6, "client-authentication")) {
romanc1d2b092023-02-02 08:58:27 +01002193 /* client auth pubkey, leaf */
roman4cb8bb12023-06-29 09:16:46 +02002194 if (nc_server_config_get_pubkey(node, &pubkey)) {
romanc1d2b092023-02-02 08:58:27 +01002195 ret = 1;
2196 goto cleanup;
2197 }
2198
roman13145912023-08-17 15:36:54 +02002199 /* the public key must not be SubjectPublicKeyInfoFormat, as per the ietf-netconf-server model */
roman51a1e192023-09-14 10:13:45 +02002200 if (nc_is_pk_subject_public_key_info(lyd_get_value(node))) {
roman13145912023-08-17 15:36:54 +02002201 ERR(NULL, "Using Public Key in the SubjectPublicKeyInfo format as an SSH user's key is forbidden!");
2202 ret = 1;
2203 goto cleanup;
2204 }
2205
romanc1d2b092023-02-02 08:58:27 +01002206 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman874fed12023-05-25 10:20:01 +02002207 ret = nc_server_config_replace_auth_key_public_key_leaf(node, pubkey);
romanc1d2b092023-02-02 08:58:27 +01002208 if (ret) {
2209 goto cleanup;
2210 }
2211 } else {
roman874fed12023-05-25 10:20:01 +02002212 nc_server_config_del_auth_client_pubkey_pub_base64(pubkey);
romanc1d2b092023-02-02 08:58:27 +01002213 }
romanb6f44032023-06-30 15:07:56 +02002214 } else if (is_tls(node) && equal_parent_name(node, 3, "server-identity")) {
2215 /* TLS server-identity */
2216 if (nc_server_config_get_tls_opts(node, &opts)) {
roman4cb8bb12023-06-29 09:16:46 +02002217 ret = 1;
2218 goto cleanup;
2219 }
2220
roman13145912023-08-17 15:36:54 +02002221 /* the public key must be SubjectPublicKeyInfoFormat, as per the ietf-netconf-server model */
roman51a1e192023-09-14 10:13:45 +02002222 if (!nc_is_pk_subject_public_key_info(lyd_get_value(node))) {
roman13145912023-08-17 15:36:54 +02002223 ERR(NULL, "TLS server certificate's Public Key must be in the SubjectPublicKeyInfo format!");
2224 ret = 1;
2225 goto cleanup;
2226 }
2227
roman3f9b65c2023-06-05 14:26:58 +02002228 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2229 /* set to local */
romanb6f44032023-06-30 15:07:56 +02002230 opts->store = NC_STORE_LOCAL;
roman3f9b65c2023-06-05 14:26:58 +02002231
romanb6f44032023-06-30 15:07:56 +02002232 ret = nc_server_config_tls_replace_server_public_key(node, opts);
roman3f9b65c2023-06-05 14:26:58 +02002233 if (ret) {
2234 goto cleanup;
2235 }
2236 }
roman5cbb6532023-06-22 12:53:17 +02002237 }
2238
2239cleanup:
romanba93eac2023-07-18 14:36:48 +02002240 if (is_ch(node)) {
2241 /* UNLOCK */
2242 nc_ch_client_unlock(ch_client);
2243 }
roman5cbb6532023-06-22 12:53:17 +02002244 return ret;
2245}
2246
2247/* leaf */
2248static int
2249nc_server_config_private_key_format(const struct lyd_node *node, NC_OPERATION op)
2250{
2251 int ret = 0;
2252 const char *format;
2253 NC_PRIVKEY_FORMAT privkey_type;
roman5cbb6532023-06-22 12:53:17 +02002254 struct nc_hostkey *hostkey;
romanb6f44032023-06-30 15:07:56 +02002255 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002256 struct nc_ch_client *ch_client;
roman5cbb6532023-06-22 12:53:17 +02002257
2258 (void) op;
2259
2260 assert(!strcmp(LYD_NAME(node), "private-key-format"));
2261
2262 format = ((struct lyd_node_term *)node)->value.ident->name;
2263 if (!format) {
2264 ret = 1;
2265 goto cleanup;
2266 }
2267
2268 privkey_type = nc_server_config_get_private_key_type(format);
2269 if (privkey_type == NC_PRIVKEY_FORMAT_UNKNOWN) {
2270 ERR(NULL, "Unknown private key format.");
2271 ret = 1;
2272 goto cleanup;
2273 }
2274
romanba93eac2023-07-18 14:36:48 +02002275 /* LOCK */
2276 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2277 return 1;
2278 }
2279
roman4cb8bb12023-06-29 09:16:46 +02002280 if (is_ssh(node)) {
2281 /* ssh */
2282 if (nc_server_config_get_hostkey(node, &hostkey)) {
roman5cbb6532023-06-22 12:53:17 +02002283 ret = 1;
2284 goto cleanup;
2285 }
2286
2287 hostkey->key.privkey_type = privkey_type;
romanb6f44032023-06-30 15:07:56 +02002288 } else if (is_tls(node)) {
2289 /* tls */
2290 if (nc_server_config_get_tls_opts(node, &opts)) {
roman5cbb6532023-06-22 12:53:17 +02002291 ret = 1;
2292 goto cleanup;
2293 }
2294
romanb6f44032023-06-30 15:07:56 +02002295 opts->privkey_type = privkey_type;
roman5cbb6532023-06-22 12:53:17 +02002296 }
2297
2298cleanup:
romanba93eac2023-07-18 14:36:48 +02002299 if (is_ch(node)) {
2300 /* UNLOCK */
2301 nc_ch_client_unlock(ch_client);
2302 }
roman5cbb6532023-06-22 12:53:17 +02002303 return ret;
2304}
2305
2306static int
2307nc_server_config_replace_cleartext_private_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
2308{
2309 nc_server_config_del_private_key(hostkey);
2310 hostkey->key.privkey_data = strdup(lyd_get_value(node));
2311 if (!hostkey->key.privkey_data) {
2312 ERRMEM;
2313 return 1;
2314 }
2315
2316 return 0;
2317}
2318
2319static int
2320nc_server_config_tls_replace_cleartext_private_key(const struct lyd_node *node, struct nc_server_tls_opts *opts)
2321{
2322 nc_server_config_tls_del_cleartext_private_key(opts);
2323 opts->privkey_data = strdup(lyd_get_value(node));
2324 if (!opts->privkey_data) {
2325 ERRMEM;
2326 return 1;
2327 }
2328
2329 return 0;
2330}
2331
2332static int
2333nc_server_config_cleartext_private_key(const struct lyd_node *node, NC_OPERATION op)
2334{
2335 int ret = 0;
roman5cbb6532023-06-22 12:53:17 +02002336 struct nc_hostkey *hostkey;
romanb6f44032023-06-30 15:07:56 +02002337 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002338 struct nc_ch_client *ch_client;
roman5cbb6532023-06-22 12:53:17 +02002339
2340 assert(!strcmp(LYD_NAME(node), "cleartext-private-key"));
2341
romanba93eac2023-07-18 14:36:48 +02002342 /* LOCK */
2343 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2344 return 1;
2345 }
2346
roman4cb8bb12023-06-29 09:16:46 +02002347 if (is_ssh(node)) {
2348 /* ssh */
2349 if (nc_server_config_get_hostkey(node, &hostkey)) {
roman5cbb6532023-06-22 12:53:17 +02002350 ret = 1;
2351 goto cleanup;
2352 }
2353
2354 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2355 ret = nc_server_config_replace_cleartext_private_key(node, hostkey);
2356 if (ret) {
2357 goto cleanup;
2358 }
2359 } else {
2360 nc_server_config_del_private_key(hostkey);
2361 }
romanb6f44032023-06-30 15:07:56 +02002362 } else if (is_tls(node)) {
2363 /* tls */
2364 if (nc_server_config_get_tls_opts(node, &opts)) {
roman4cb8bb12023-06-29 09:16:46 +02002365 ret = 1;
2366 goto cleanup;
2367 }
2368
roman5cbb6532023-06-22 12:53:17 +02002369 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02002370 ret = nc_server_config_tls_replace_cleartext_private_key(node, opts);
roman5cbb6532023-06-22 12:53:17 +02002371 if (ret) {
2372 goto cleanup;
2373 }
2374 } else {
romanb6f44032023-06-30 15:07:56 +02002375 nc_server_config_tls_del_cleartext_private_key(opts);
roman5cbb6532023-06-22 12:53:17 +02002376 }
roman5cbb6532023-06-22 12:53:17 +02002377 }
2378
2379cleanup:
romanba93eac2023-07-18 14:36:48 +02002380 if (is_ch(node)) {
2381 /* UNLOCK */
2382 nc_ch_client_unlock(ch_client);
2383 }
roman5cbb6532023-06-22 12:53:17 +02002384 return ret;
2385}
2386
roman5cbb6532023-06-22 12:53:17 +02002387/* leaf */
2388static int
2389nc_server_config_keystore_reference(const struct lyd_node *node, NC_OPERATION op)
2390{
2391 int ret = 0;
roman5cbb6532023-06-22 12:53:17 +02002392 struct nc_hostkey *hostkey;
romanba93eac2023-07-18 14:36:48 +02002393 struct nc_ch_client *ch_client;
roman5cbb6532023-06-22 12:53:17 +02002394
2395 assert(!strcmp(LYD_NAME(node), "keystore-reference"));
2396
romanba93eac2023-07-18 14:36:48 +02002397 /* LOCK */
2398 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2399 return 1;
2400 }
2401
roman4cb8bb12023-06-29 09:16:46 +02002402 if (is_ssh(node) && equal_parent_name(node, 3, "server-identity")) {
2403 if (nc_server_config_get_hostkey(node, &hostkey)) {
roman5cbb6532023-06-22 12:53:17 +02002404 ret = 1;
2405 goto cleanup;
2406 }
2407
2408 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2409 /* set to keystore */
2410 hostkey->store = NC_STORE_KEYSTORE;
2411
romandd019a92023-09-14 10:17:07 +02002412 nc_server_config_del_keystore_reference(hostkey);
2413 hostkey->ks_ref = strdup(lyd_get_value(node));
2414 if (!hostkey->ks_ref) {
2415 ERRMEM;
2416 ret = 1;
roman5cbb6532023-06-22 12:53:17 +02002417 goto cleanup;
2418 }
2419 } else {
2420 hostkey->ks_ref = NULL;
2421 }
roman3f9b65c2023-06-05 14:26:58 +02002422 }
romanc1d2b092023-02-02 08:58:27 +01002423
2424cleanup:
romanba93eac2023-07-18 14:36:48 +02002425 if (is_ch(node)) {
2426 /* UNLOCK */
2427 nc_ch_client_unlock(ch_client);
2428 }
romanc1d2b092023-02-02 08:58:27 +01002429 return ret;
2430}
2431
2432static int
roman874fed12023-05-25 10:20:01 +02002433nc_server_config_create_user(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +01002434{
romanf02273a2023-05-25 09:44:11 +02002435 node = lyd_child(node);
2436 assert(!strcmp(LYD_NAME(node), "name"));
romanc1d2b092023-02-02 08:58:27 +01002437
romanf02273a2023-05-25 09:44:11 +02002438 return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->auth_clients, sizeof *opts->auth_clients, &opts->client_count);
romanc1d2b092023-02-02 08:58:27 +01002439}
2440
2441/* list */
2442static int
romane028ef92023-02-24 16:33:08 +01002443nc_server_config_user(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002444{
roman5cbb6532023-06-22 12:53:17 +02002445 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002446 struct nc_client_auth *auth_client;
roman4cb8bb12023-06-29 09:16:46 +02002447 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002448 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002449
2450 assert(!strcmp(LYD_NAME(node), "user"));
2451
romanba93eac2023-07-18 14:36:48 +02002452 /* LOCK */
2453 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2454 return 1;
2455 }
2456
roman4cb8bb12023-06-29 09:16:46 +02002457 if (nc_server_config_get_ssh_opts(node, &opts)) {
2458 ret = 1;
2459 goto cleanup;
2460 }
2461
2462 if (op == NC_OP_CREATE) {
2463 ret = nc_server_config_create_user(node, opts);
2464 if (ret) {
2465 goto cleanup;
2466 }
2467 } else if (op == NC_OP_DELETE) {
2468 if (nc_server_config_get_auth_client(node, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01002469 ret = 1;
2470 goto cleanup;
2471 }
2472
roman4cb8bb12023-06-29 09:16:46 +02002473 nc_server_config_del_auth_client(opts, auth_client);
romanc1d2b092023-02-02 08:58:27 +01002474 }
2475
2476cleanup:
romanba93eac2023-07-18 14:36:48 +02002477 if (is_ch(node)) {
2478 /* UNLOCK */
2479 nc_ch_client_unlock(ch_client);
2480 }
romanc1d2b092023-02-02 08:58:27 +01002481 return ret;
2482}
2483
2484static int
romane028ef92023-02-24 16:33:08 +01002485nc_server_config_auth_attempts(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002486{
romanc1d2b092023-02-02 08:58:27 +01002487 int ret = 0;
roman4cb8bb12023-06-29 09:16:46 +02002488 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002489 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002490
2491 assert(!strcmp(LYD_NAME(node), "auth-attempts"));
2492
romanba93eac2023-07-18 14:36:48 +02002493 /* LOCK */
2494 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2495 return 1;
2496 }
2497
roman4cb8bb12023-06-29 09:16:46 +02002498 if (nc_server_config_get_ssh_opts(node, &opts)) {
2499 ret = 1;
2500 goto cleanup;
2501 }
romanc1d2b092023-02-02 08:58:27 +01002502
roman4cb8bb12023-06-29 09:16:46 +02002503 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2504 opts->auth_attempts = strtoul(lyd_get_value(node), NULL, 10);
romanc1d2b092023-02-02 08:58:27 +01002505 }
2506
2507cleanup:
romanba93eac2023-07-18 14:36:48 +02002508 if (is_ch(node)) {
2509 /* UNLOCK */
2510 nc_ch_client_unlock(ch_client);
2511 }
romanc1d2b092023-02-02 08:58:27 +01002512 return ret;
2513}
2514
2515static int
romane028ef92023-02-24 16:33:08 +01002516nc_server_config_auth_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002517{
romanc1d2b092023-02-02 08:58:27 +01002518 int ret = 0;
roman4cb8bb12023-06-29 09:16:46 +02002519 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002520 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002521
2522 assert(!strcmp(LYD_NAME(node), "auth-timeout"));
2523
romanba93eac2023-07-18 14:36:48 +02002524 /* LOCK */
2525 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2526 return 1;
2527 }
2528
roman4cb8bb12023-06-29 09:16:46 +02002529 if (nc_server_config_get_ssh_opts(node, &opts)) {
2530 ret = 1;
2531 goto cleanup;
2532 }
romanc1d2b092023-02-02 08:58:27 +01002533
roman4cb8bb12023-06-29 09:16:46 +02002534 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2535 opts->auth_timeout = strtoul(lyd_get_value(node), NULL, 10);
romanc1d2b092023-02-02 08:58:27 +01002536 }
2537
2538cleanup:
romanba93eac2023-07-18 14:36:48 +02002539 if (is_ch(node)) {
2540 /* UNLOCK */
2541 nc_ch_client_unlock(ch_client);
2542 }
romanc1d2b092023-02-02 08:58:27 +01002543 return ret;
2544}
2545
2546static int
roman13145912023-08-17 15:36:54 +02002547nc_server_config_ssh_replace_truststore_reference(const struct lyd_node *node, struct nc_client_auth *client_auth)
romanc1d2b092023-02-02 08:58:27 +01002548{
romand57b3722023-04-05 11:26:25 +02002549 uint16_t i;
2550 struct nc_truststore *ts = &server_opts.truststore;
romanc1d2b092023-02-02 08:58:27 +01002551
romand57b3722023-04-05 11:26:25 +02002552 /* lookup name */
2553 for (i = 0; i < ts->pub_bag_count; i++) {
2554 if (!strcmp(lyd_get_value(node), ts->pub_bags[i].name)) {
2555 break;
2556 }
2557 }
2558
2559 if (i == ts->pub_bag_count) {
roman3f9b65c2023-06-05 14:26:58 +02002560 ERR(NULL, "Public-key bag \"%s\" not found in truststore.", lyd_get_value(node));
romanc1d2b092023-02-02 08:58:27 +01002561 return 1;
2562 }
2563
romand57b3722023-04-05 11:26:25 +02002564 client_auth->ts_ref = &ts->pub_bags[i];
2565
roman13145912023-08-17 15:36:54 +02002566 /* check if any of the referenced public keys is SubjectPublicKeyInfo */
2567 for (i = 0; i < client_auth->ts_ref->pubkey_count; i++) {
roman51a1e192023-09-14 10:13:45 +02002568 if (nc_is_pk_subject_public_key_info(client_auth->ts_ref->pubkeys[i].data)) {
roman13145912023-08-17 15:36:54 +02002569 ERR(NULL, "Using Public Key in the SubjectPublicKeyInfo format as an SSH user's key is forbidden!");
2570 return 1;
2571 }
2572 }
2573
romanc1d2b092023-02-02 08:58:27 +01002574 return 0;
2575}
2576
roman3f9b65c2023-06-05 14:26:58 +02002577static int
2578nc_server_config_tls_replace_truststore_reference(const struct lyd_node *node, struct nc_cert_grouping *auth_client)
2579{
2580 uint16_t i;
2581 struct nc_truststore *ts = &server_opts.truststore;
2582
2583 /* lookup name */
2584 for (i = 0; i < ts->cert_bag_count; i++) {
2585 if (!strcmp(lyd_get_value(node), ts->cert_bags[i].name)) {
2586 break;
2587 }
2588 }
2589
2590 if (i == ts->cert_bag_count) {
2591 ERR(NULL, "Certificate bag \"%s\" not found in truststore.", lyd_get_value(node));
2592 return 1;
2593 }
2594
2595 auth_client->ts_ref = &ts->cert_bags[i];
2596
2597 return 0;
2598}
2599
romanc1d2b092023-02-02 08:58:27 +01002600/* leaf */
2601static int
romane028ef92023-02-24 16:33:08 +01002602nc_server_config_truststore_reference(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002603{
romanc1d2b092023-02-02 08:58:27 +01002604 int ret = 0;
roman3f9b65c2023-06-05 14:26:58 +02002605 struct nc_endpt *endpt;
roman3f9b65c2023-06-05 14:26:58 +02002606 struct nc_client_auth *auth_client;
romanba93eac2023-07-18 14:36:48 +02002607 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002608
2609 assert(!strcmp(LYD_NAME(node), "truststore-reference"));
2610
romanba93eac2023-07-18 14:36:48 +02002611 /* LOCK */
2612 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2613 return 1;
2614 }
2615
roman4cb8bb12023-06-29 09:16:46 +02002616 if (is_ssh(node) && equal_parent_name(node, 1, "public-keys")) {
2617 if (nc_server_config_get_auth_client(node, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01002618 ret = 1;
2619 goto cleanup;
2620 }
2621
2622 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanf02273a2023-05-25 09:44:11 +02002623 /* set to truststore */
roman874fed12023-05-25 10:20:01 +02002624 auth_client->store = NC_STORE_TRUSTSTORE;
romanf02273a2023-05-25 09:44:11 +02002625
roman13145912023-08-17 15:36:54 +02002626 ret = nc_server_config_ssh_replace_truststore_reference(node, auth_client);
romanc1d2b092023-02-02 08:58:27 +01002627 if (ret) {
2628 goto cleanup;
2629 }
2630 } else {
romand57b3722023-04-05 11:26:25 +02002631 auth_client->ts_ref = NULL;
romanc1d2b092023-02-02 08:58:27 +01002632 }
romanba93eac2023-07-18 14:36:48 +02002633 } else if (is_tls(node) && equal_parent_name(node, 1, "ca-certs")) {
roman4cb8bb12023-06-29 09:16:46 +02002634 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
2635 ret = 1;
2636 goto cleanup;
2637 }
2638
roman3f9b65c2023-06-05 14:26:58 +02002639 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2640 /* set to truststore */
2641 endpt->opts.tls->ca_certs.store = NC_STORE_TRUSTSTORE;
2642
2643 ret = nc_server_config_tls_replace_truststore_reference(node, &endpt->opts.tls->ca_certs);
2644 if (ret) {
2645 goto cleanup;
2646 }
2647 } else {
2648 endpt->opts.tls->ca_certs.ts_ref = NULL;
2649 }
romanba93eac2023-07-18 14:36:48 +02002650 } else if (is_tls(node) && equal_parent_name(node, 1, "ee-certs")) {
roman4cb8bb12023-06-29 09:16:46 +02002651 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
2652 ret = 1;
2653 goto cleanup;
2654 }
2655
roman3f9b65c2023-06-05 14:26:58 +02002656 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2657 /* set to truststore */
2658 endpt->opts.tls->ee_certs.store = NC_STORE_TRUSTSTORE;
2659
2660 ret = nc_server_config_tls_replace_truststore_reference(node, &endpt->opts.tls->ee_certs);
2661 if (ret) {
2662 goto cleanup;
2663 }
2664 } else {
2665 endpt->opts.tls->ee_certs.ts_ref = NULL;
2666 }
2667 }
romanc1d2b092023-02-02 08:58:27 +01002668
2669cleanup:
romanba93eac2023-07-18 14:36:48 +02002670 if (is_ch(node)) {
2671 /* UNLOCK */
2672 nc_ch_client_unlock(ch_client);
2673 }
romanc1d2b092023-02-02 08:58:27 +01002674 return ret;
2675}
2676
2677static int
roman874fed12023-05-25 10:20:01 +02002678nc_server_config_replace_password(const struct lyd_node *node, struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +01002679{
roman874fed12023-05-25 10:20:01 +02002680 nc_server_config_del_auth_client_password(auth_client);
romanc1d2b092023-02-02 08:58:27 +01002681
2682 auth_client->password = strdup(lyd_get_value(node));
2683 if (!auth_client->password) {
2684 ERRMEM;
2685 return 1;
2686 }
2687
2688 return 0;
2689}
2690
2691/* leaf */
2692static int
romane028ef92023-02-24 16:33:08 +01002693nc_server_config_password(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002694{
roman5cbb6532023-06-22 12:53:17 +02002695 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002696 struct nc_client_auth *auth_client;
romanba93eac2023-07-18 14:36:48 +02002697 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002698
2699 assert(!strcmp(LYD_NAME(node), "password"));
2700
romanba93eac2023-07-18 14:36:48 +02002701 /* LOCK */
2702 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2703 return 1;
2704 }
2705
roman4cb8bb12023-06-29 09:16:46 +02002706 if (nc_server_config_get_auth_client(node, &auth_client)) {
2707 ret = 1;
2708 goto cleanup;
2709 }
2710
2711 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2712 ret = nc_server_config_replace_password(node, auth_client);
2713 if (ret) {
romanc1d2b092023-02-02 08:58:27 +01002714 goto cleanup;
2715 }
roman4cb8bb12023-06-29 09:16:46 +02002716 } else {
2717 nc_server_config_del_auth_client_password(auth_client);
romanc1d2b092023-02-02 08:58:27 +01002718 }
2719
2720cleanup:
romanba93eac2023-07-18 14:36:48 +02002721 if (is_ch(node)) {
2722 /* UNLOCK */
2723 nc_ch_client_unlock(ch_client);
2724 }
romanc1d2b092023-02-02 08:58:27 +01002725 return ret;
2726}
2727
2728static int
romane028ef92023-02-24 16:33:08 +01002729nc_server_config_pam_name(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002730{
roman5cbb6532023-06-22 12:53:17 +02002731 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002732 struct nc_client_auth *auth_client;
romanba93eac2023-07-18 14:36:48 +02002733 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002734
2735 assert(!strcmp(LYD_NAME(node), "pam-config-file-name"));
2736
romanba93eac2023-07-18 14:36:48 +02002737 /* LOCK */
2738 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2739 return 1;
2740 }
2741
roman4cb8bb12023-06-29 09:16:46 +02002742 if (nc_server_config_get_auth_client(node, &auth_client)) {
2743 ret = 1;
2744 goto cleanup;
2745 }
2746
2747 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2748 nc_server_config_del_auth_client_pam_name(auth_client);
2749
2750 auth_client->pam_config_name = strdup(lyd_get_value(node));
2751 if (!auth_client->pam_config_name) {
2752 ERRMEM;
romanc1d2b092023-02-02 08:58:27 +01002753 ret = 1;
2754 goto cleanup;
2755 }
roman4cb8bb12023-06-29 09:16:46 +02002756 } else {
2757 nc_server_config_del_auth_client_pam_name(auth_client);
romanc1d2b092023-02-02 08:58:27 +01002758 }
2759
2760cleanup:
romanba93eac2023-07-18 14:36:48 +02002761 if (is_ch(node)) {
2762 /* UNLOCK */
2763 nc_ch_client_unlock(ch_client);
2764 }
romanc1d2b092023-02-02 08:58:27 +01002765 return ret;
2766}
2767
2768static int
romane028ef92023-02-24 16:33:08 +01002769nc_server_config_pam_dir(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002770{
roman5cbb6532023-06-22 12:53:17 +02002771 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002772 struct nc_client_auth *auth_client;
romanba93eac2023-07-18 14:36:48 +02002773 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002774
2775 assert(!strcmp(LYD_NAME(node), "pam-config-file-dir"));
2776
romanba93eac2023-07-18 14:36:48 +02002777 /* LOCK */
2778 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2779 return 1;
2780 }
2781
roman4cb8bb12023-06-29 09:16:46 +02002782 if (nc_server_config_get_auth_client(node, &auth_client)) {
2783 ret = 1;
2784 goto cleanup;
2785 }
2786
2787 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2788 nc_server_config_del_auth_client_pam_dir(auth_client);
2789 auth_client->pam_config_dir = strdup(lyd_get_value(node));
2790 if (!auth_client->pam_config_dir) {
2791 ERRMEM;
romanc1d2b092023-02-02 08:58:27 +01002792 ret = 1;
2793 goto cleanup;
2794 }
roman4cb8bb12023-06-29 09:16:46 +02002795 } else {
2796 nc_server_config_del_auth_client_pam_dir(auth_client);
romanc1d2b092023-02-02 08:58:27 +01002797 }
2798
2799cleanup:
romanba93eac2023-07-18 14:36:48 +02002800 if (is_ch(node)) {
2801 /* UNLOCK */
2802 nc_ch_client_unlock(ch_client);
2803 }
romanc1d2b092023-02-02 08:58:27 +01002804 return ret;
2805}
2806
2807/* leaf */
2808static int
romane028ef92023-02-24 16:33:08 +01002809nc_server_config_none(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002810{
roman5cbb6532023-06-22 12:53:17 +02002811 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002812 struct nc_client_auth *auth_client;
romanba93eac2023-07-18 14:36:48 +02002813 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002814
2815 assert(!strcmp(LYD_NAME(node), "none"));
2816
romanba93eac2023-07-18 14:36:48 +02002817 /* LOCK */
2818 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2819 return 1;
2820 }
2821
roman4cb8bb12023-06-29 09:16:46 +02002822 if (nc_server_config_get_auth_client(node, &auth_client)) {
2823 ret = 1;
2824 goto cleanup;
2825 }
romanc1d2b092023-02-02 08:58:27 +01002826
roman4cb8bb12023-06-29 09:16:46 +02002827 if (op == NC_OP_CREATE) {
2828 auth_client->supports_none = 1;
2829 } else {
2830 auth_client->supports_none = 0;
romanc1d2b092023-02-02 08:58:27 +01002831 }
2832
2833cleanup:
romanba93eac2023-07-18 14:36:48 +02002834 if (is_ch(node)) {
2835 /* UNLOCK */
2836 nc_ch_client_unlock(ch_client);
2837 }
romanc1d2b092023-02-02 08:58:27 +01002838 return ret;
2839}
2840
2841static int
romana6bf6ab2023-05-26 13:26:02 +02002842nc_server_config_transport_params(const char *algorithm, char **alg_store, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002843{
2844 int ret = 0, alg_found = 0;
romana6bf6ab2023-05-26 13:26:02 +02002845 char *substr, *haystack, *alg = NULL;
2846 size_t alg_len;
2847
2848 if (!strncmp(algorithm, "openssh-", 8)) {
2849 /* if the name starts with openssh, convert it to it's original libssh accepted form */
2850 asprintf(&alg, "%s@openssh.com", algorithm + 8);
2851 if (!alg) {
2852 ERRMEM;
2853 ret = 1;
2854 goto cleanup;
2855 }
2856 } else if (!strncmp(algorithm, "libssh-", 7)) {
2857 /* if the name starts with libssh, convert it to it's original libssh accepted form */
2858 asprintf(&alg, "%s@libssh.org", algorithm + 7);
2859 if (!alg) {
2860 ERRMEM;
2861 ret = 1;
2862 goto cleanup;
2863 }
2864 } else {
2865 alg = strdup(algorithm);
2866 if (!alg) {
2867 ERRMEM;
2868 ret = 1;
2869 goto cleanup;
2870 }
2871 }
2872
2873 alg_len = strlen(alg);
romanc1d2b092023-02-02 08:58:27 +01002874
2875 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2876 if (!*alg_store) {
2877 /* first call */
2878 *alg_store = strdup(alg);
2879 if (!*alg_store) {
2880 ERRMEM;
2881 ret = 1;
2882 goto cleanup;
2883 }
2884 } else {
2885 /* +1 because of ',' between algorithms */
2886 *alg_store = nc_realloc(*alg_store, strlen(*alg_store) + alg_len + 1 + 1);
2887 if (!*alg_store) {
2888 ERRMEM;
2889 ret = 1;
2890 goto cleanup;
2891 }
roman08f67f42023-06-08 13:51:54 +02002892 strcat(*alg_store, ",");
2893 strcat(*alg_store, alg);
romanc1d2b092023-02-02 08:58:27 +01002894 }
2895 } else {
2896 /* delete */
2897 haystack = *alg_store;
2898 while ((substr = strstr(haystack, alg))) {
2899 /* iterate over all the substrings */
2900 if (((substr == haystack) && (*(substr + alg_len) == ',')) ||
2901 ((substr != haystack) && (*(substr - 1) == ',') && (*(substr + alg_len) == ','))) {
2902 /* either the first element of the string or somewhere in the middle */
2903 memmove(substr, substr + alg_len + 1, strlen(substr + alg_len + 1));
2904 alg_found = 1;
2905 break;
2906 } else if ((*(substr - 1) == ',') && (*(substr + alg_len) == '\0')) {
2907 /* the last element of the string */
2908 *(substr - 1) = '\0';
2909 alg_found = 1;
2910 break;
2911 }
2912 haystack++;
2913 }
2914 if (!alg_found) {
2915 ERR(NULL, "Unable to delete an algorithm (%s), which was not previously added.", alg);
2916 ret = 1;
2917 }
2918 }
2919
2920cleanup:
romana6bf6ab2023-05-26 13:26:02 +02002921 free(alg);
romanc1d2b092023-02-02 08:58:27 +01002922 return ret;
2923}
2924
2925/* leaf-list */
2926static int
romane028ef92023-02-24 16:33:08 +01002927nc_server_config_host_key_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002928{
roman5cbb6532023-06-22 12:53:17 +02002929 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002930 const char *alg;
2931 uint8_t i;
roman5cbb6532023-06-22 12:53:17 +02002932 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002933 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002934
roman5cbb6532023-06-22 12:53:17 +02002935 assert(!strcmp(LYD_NAME(node), "host-key-alg"));
romanc1d2b092023-02-02 08:58:27 +01002936
romanba93eac2023-07-18 14:36:48 +02002937 /* LOCK */
2938 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2939 return 1;
2940 }
2941
roman4cb8bb12023-06-29 09:16:46 +02002942 if (nc_server_config_get_ssh_opts(node, &opts)) {
2943 ret = 1;
2944 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002945 }
2946
roman5cbb6532023-06-22 12:53:17 +02002947 /* get the algorithm name and compare it with algs supported by libssh */
2948 alg = ((struct lyd_node_term *)node)->value.ident->name;
romanc1d2b092023-02-02 08:58:27 +01002949 i = 0;
2950 while (supported_hostkey_algs[i]) {
2951 if (!strcmp(supported_hostkey_algs[i], alg)) {
roman5cbb6532023-06-22 12:53:17 +02002952 if (nc_server_config_transport_params(alg, &opts->hostkey_algs, op)) {
2953 ret = 1;
2954 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002955 }
2956 break;
2957 }
2958 i++;
2959 }
2960 if (!supported_hostkey_algs[i]) {
2961 /* algorithm not supported */
2962 ERR(NULL, "Public key algorithm (%s) not supported by libssh.", alg);
2963 ret = 1;
2964 }
2965
2966cleanup:
romanba93eac2023-07-18 14:36:48 +02002967 if (is_ch(node)) {
2968 /* UNLOCK */
2969 nc_ch_client_unlock(ch_client);
2970 }
romanc1d2b092023-02-02 08:58:27 +01002971 return ret;
2972}
2973
2974/* leaf-list */
2975static int
romane028ef92023-02-24 16:33:08 +01002976nc_server_config_kex_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002977{
roman5cbb6532023-06-22 12:53:17 +02002978 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002979 const char *alg;
2980 uint8_t i;
roman5cbb6532023-06-22 12:53:17 +02002981 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002982 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002983
roman5cbb6532023-06-22 12:53:17 +02002984 assert(!strcmp(LYD_NAME(node), "key-exchange-alg"));
romanc1d2b092023-02-02 08:58:27 +01002985
romanba93eac2023-07-18 14:36:48 +02002986 /* LOCK */
2987 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2988 return 1;
2989 }
2990
roman4cb8bb12023-06-29 09:16:46 +02002991 if (nc_server_config_get_ssh_opts(node, &opts)) {
2992 ret = 1;
2993 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002994 }
2995
roman5cbb6532023-06-22 12:53:17 +02002996 /* get the algorithm name and compare it with algs supported by libssh */
2997 alg = ((struct lyd_node_term *)node)->value.ident->name;
romanc1d2b092023-02-02 08:58:27 +01002998 i = 0;
2999 while (supported_kex_algs[i]) {
3000 if (!strcmp(supported_kex_algs[i], alg)) {
roman5cbb6532023-06-22 12:53:17 +02003001 if (nc_server_config_transport_params(alg, &opts->kex_algs, op)) {
3002 ret = 1;
3003 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01003004 }
3005 break;
3006 }
3007 i++;
3008 }
3009 if (!supported_kex_algs[i]) {
3010 /* algorithm not supported */
3011 ERR(NULL, "Key exchange algorithm (%s) not supported by libssh.", alg);
3012 ret = 1;
3013 }
3014
3015cleanup:
romanba93eac2023-07-18 14:36:48 +02003016 if (is_ch(node)) {
3017 /* UNLOCK */
3018 nc_ch_client_unlock(ch_client);
3019 }
romanc1d2b092023-02-02 08:58:27 +01003020 return ret;
3021}
3022
3023/* leaf-list */
3024static int
romane028ef92023-02-24 16:33:08 +01003025nc_server_config_encryption_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01003026{
roman5cbb6532023-06-22 12:53:17 +02003027 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01003028 const char *alg;
3029 uint8_t i;
roman5cbb6532023-06-22 12:53:17 +02003030 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02003031 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01003032
roman5cbb6532023-06-22 12:53:17 +02003033 assert(!strcmp(LYD_NAME(node), "encryption-alg"));
romanc1d2b092023-02-02 08:58:27 +01003034
romanba93eac2023-07-18 14:36:48 +02003035 /* LOCK */
3036 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3037 return 1;
3038 }
3039
roman4cb8bb12023-06-29 09:16:46 +02003040 if (nc_server_config_get_ssh_opts(node, &opts)) {
3041 ret = 1;
3042 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01003043 }
3044
roman5cbb6532023-06-22 12:53:17 +02003045 /* get the algorithm name and compare it with algs supported by libssh */
3046 alg = ((struct lyd_node_term *)node)->value.ident->name;
romanc1d2b092023-02-02 08:58:27 +01003047 i = 0;
3048 while (supported_encryption_algs[i]) {
3049 if (!strcmp(supported_encryption_algs[i], alg)) {
roman5cbb6532023-06-22 12:53:17 +02003050 if (nc_server_config_transport_params(alg, &opts->encryption_algs, op)) {
3051 ret = 1;
3052 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01003053 }
3054 break;
3055 }
3056 i++;
3057 }
3058 if (!supported_encryption_algs[i]) {
3059 /* algorithm not supported */
3060 ERR(NULL, "Encryption algorithm (%s) not supported by libssh.", alg);
3061 ret = 1;
3062 }
3063
3064cleanup:
romanba93eac2023-07-18 14:36:48 +02003065 if (is_ch(node)) {
3066 /* UNLOCK */
3067 nc_ch_client_unlock(ch_client);
3068 }
romanc1d2b092023-02-02 08:58:27 +01003069 return ret;
3070}
3071
3072/* leaf-list */
3073static int
romane028ef92023-02-24 16:33:08 +01003074nc_server_config_mac_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01003075{
roman5cbb6532023-06-22 12:53:17 +02003076 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01003077 const char *alg;
3078 uint8_t i;
roman5cbb6532023-06-22 12:53:17 +02003079 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02003080 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01003081
roman5cbb6532023-06-22 12:53:17 +02003082 assert(!strcmp(LYD_NAME(node), "mac-alg"));
romanc1d2b092023-02-02 08:58:27 +01003083
romanba93eac2023-07-18 14:36:48 +02003084 /* LOCK */
3085 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3086 return 1;
3087 }
3088
roman4cb8bb12023-06-29 09:16:46 +02003089 if (nc_server_config_get_ssh_opts(node, &opts)) {
3090 ret = 1;
3091 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01003092 }
3093
roman5cbb6532023-06-22 12:53:17 +02003094 /* get the algorithm name and compare it with algs supported by libssh */
3095 alg = ((struct lyd_node_term *)node)->value.ident->name;
romanc1d2b092023-02-02 08:58:27 +01003096 i = 0;
3097 while (supported_mac_algs[i]) {
3098 if (!strcmp(supported_mac_algs[i], alg)) {
roman5cbb6532023-06-22 12:53:17 +02003099 if (nc_server_config_transport_params(alg, &opts->mac_algs, op)) {
3100 ret = 1;
3101 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01003102 }
3103 break;
3104 }
3105 i++;
3106 }
3107 if (!supported_mac_algs[i]) {
3108 /* algorithm not supported */
3109 ERR(NULL, "MAC algorithm (%s) not supported by libssh.", alg);
3110 ret = 1;
3111 }
3112
3113cleanup:
romanba93eac2023-07-18 14:36:48 +02003114 if (is_ch(node)) {
3115 /* UNLOCK */
3116 nc_ch_client_unlock(ch_client);
3117 }
romanc1d2b092023-02-02 08:58:27 +01003118 return ret;
3119}
3120
roman2eab4742023-06-06 10:00:26 +02003121#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +02003122
romanc1d2b092023-02-02 08:58:27 +01003123static int
roman874fed12023-05-25 10:20:01 +02003124nc_server_config_create_unix_socket(struct nc_endpt *endpt)
roman83683fb2023-02-24 09:15:23 +01003125{
3126 endpt->ti = NC_TI_UNIX;
3127 endpt->opts.unixsock = calloc(1, sizeof *endpt->opts.unixsock);
3128 if (!endpt->opts.unixsock) {
3129 ERRMEM;
3130 return 1;
3131 }
3132
3133 /* set default values */
3134 endpt->opts.unixsock->mode = -1;
3135 endpt->opts.unixsock->uid = -1;
3136 endpt->opts.unixsock->gid = -1;
3137
3138 return 0;
3139}
3140
3141static int
romane028ef92023-02-24 16:33:08 +01003142nc_server_config_unix_socket(const struct lyd_node *node, NC_OPERATION op)
roman83683fb2023-02-24 09:15:23 +01003143{
3144 int ret = 0;
3145 uint32_t prev_lo;
3146 struct nc_endpt *endpt;
3147 struct nc_bind *bind;
3148 struct nc_server_unix_opts *opts;
3149 struct lyd_node *data = NULL;
3150
3151 assert(!strcmp(LYD_NAME(node), "unix-socket"));
3152
romanf02273a2023-05-25 09:44:11 +02003153 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
roman83683fb2023-02-24 09:15:23 +01003154 ret = 1;
3155 goto cleanup;
3156 }
3157
3158 if (op == NC_OP_CREATE) {
roman874fed12023-05-25 10:20:01 +02003159 if (nc_server_config_create_unix_socket(endpt)) {
roman83683fb2023-02-24 09:15:23 +01003160 ret = 1;
3161 goto cleanup;
3162 }
3163
3164 opts = endpt->opts.unixsock;
3165
3166 lyd_find_path(node, "path", 0, &data);
3167 assert(data);
3168
3169 opts->address = strdup(lyd_get_value(data));
3170 bind->address = strdup(lyd_get_value(data));
3171 if (!opts->address || !bind->address) {
3172 ERRMEM;
3173 ret = 1;
3174 goto cleanup;
3175 }
3176
3177 /* silently search for non-mandatory parameters */
3178 prev_lo = ly_log_options(0);
3179 ret = lyd_find_path(node, "mode", 0, &data);
3180 if (!ret) {
3181 opts->mode = strtol(lyd_get_value(data), NULL, 8);
3182 }
3183
3184 ret = lyd_find_path(node, "uid", 0, &data);
3185 if (!ret) {
3186 opts->uid = strtol(lyd_get_value(data), NULL, 10);
3187 }
3188
3189 ret = lyd_find_path(node, "gid", 0, &data);
3190 if (!ret) {
3191 opts->gid = strtol(lyd_get_value(data), NULL, 10);
3192 }
3193
3194 /* reset the logging options */
3195 ly_log_options(prev_lo);
3196
3197 ret = nc_server_config_set_address_port(endpt, bind, NULL, 0);
3198 if (ret) {
3199 goto cleanup;
3200 }
3201 } else if (op == NC_OP_DELETE) {
roman874fed12023-05-25 10:20:01 +02003202 nc_server_config_del_unix_socket(bind, endpt->opts.unixsock);
roman83683fb2023-02-24 09:15:23 +01003203 }
3204
3205cleanup:
3206 return ret;
3207}
3208
roman2eab4742023-06-06 10:00:26 +02003209#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +02003210
roman0bbc19c2023-05-26 09:59:09 +02003211/**
3212 * @brief Set all endpoint client auth references, which couldn't be set beforehand.
3213 *
3214 * The references that could not be set are those, which reference endpoints, which
3215 * lie below the given endpoint in the YANG data (because of DFS tree parsing).
3216 *
3217 * @return 0 on success, 1 on error.
3218 */
3219static int
3220nc_server_config_fill_endpt_client_auth(void)
3221{
3222 uint16_t i, j;
3223
3224 for (i = 0; i < server_opts.endpt_count; i++) {
3225 /* go through all the endpoint */
3226 if (server_opts.endpts[i].referenced_endpt_name) {
3227 /* endpt has a reference, that hasn't been set yet */
3228 for (j = i + 1; j < server_opts.endpt_count; j++) {
3229 /* go through all the remaining endpts */
3230 if (!strcmp(server_opts.endpts[i].referenced_endpt_name, server_opts.endpts[j].name)) {
3231 /* found the endpoint we were looking for */
3232 if (server_opts.endpts[i].ti == NC_TI_LIBSSH) {
3233 server_opts.endpts[i].opts.ssh->endpt_client_ref = &server_opts.endpts[j];
3234 break;
roman2e797ef2023-06-19 10:47:49 +02003235 } else if (server_opts.endpts[i].ti == NC_TI_OPENSSL) {
3236 server_opts.endpts[i].opts.tls->endpt_client_ref = &server_opts.endpts[j];
3237 break;
roman0bbc19c2023-05-26 09:59:09 +02003238 } else {
3239 ERRINT;
3240 return 1;
3241 }
3242 }
3243 }
3244
3245 /* didn't find the endpoint */
3246 if (j == server_opts.endpt_count) {
3247 ERR(NULL, "Endpoint \"%s\" referenced by \"%s\" not found.",
3248 server_opts.endpts[i].referenced_endpt_name, server_opts.endpts[i].name);
3249 return 1;
3250 }
3251 }
3252 }
3253
3254 return 0;
3255}
3256
3257static int
roman2e797ef2023-06-19 10:47:49 +02003258nc_server_config_endpoint_client_auth_has_cycle(struct nc_endpt *original, struct nc_endpt *next)
roman0bbc19c2023-05-26 09:59:09 +02003259{
roman2e797ef2023-06-19 10:47:49 +02003260 if (original->ti == NC_TI_LIBSSH) {
3261 if (!next->opts.ssh->endpt_client_ref) {
3262 /* no further reference -> no cycle */
roman0bbc19c2023-05-26 09:59:09 +02003263 return 0;
3264 }
roman2e797ef2023-06-19 10:47:49 +02003265
3266 if (next->opts.ssh->endpt_client_ref == original) {
3267 /* found cycle */
3268 return 1;
3269 } else {
3270 /* continue further */
3271 return nc_server_config_endpoint_client_auth_has_cycle(original, next->opts.ssh->endpt_client_ref);
3272 }
3273 } else if (original->ti == NC_TI_OPENSSL) {
3274 if (!next->opts.tls->endpt_client_ref) {
3275 /* no further reference -> no cycle */
3276 return 0;
3277 }
3278
3279 if (next->opts.tls->endpt_client_ref == original) {
3280 /* found cycle */
3281 return 1;
3282 } else {
3283 /* continue further */
3284 return nc_server_config_endpoint_client_auth_has_cycle(original, next->opts.tls->endpt_client_ref);
3285 }
roman0bbc19c2023-05-26 09:59:09 +02003286 } else {
3287 ERRINT;
3288 return 1;
3289 }
3290}
3291
3292static int
3293nc_server_config_endpoint_client_auth(const struct lyd_node *node, NC_OPERATION op)
3294{
3295 int ret = 0;
3296 uint16_t i;
3297 const char *endpt_name;
3298 struct nc_endpt *endpt;
3299
3300 assert(!strcmp(LYD_NAME(node), "endpoint-client-auth"));
3301
3302 /* get current endpoint */
3303 ret = nc_server_config_get_endpt(node, &endpt, NULL);
3304 if (ret) {
3305 goto cleanup;
3306 }
3307
3308 if (op == NC_OP_DELETE) {
roman2e797ef2023-06-19 10:47:49 +02003309 if (is_ssh(node)) {
3310 endpt->opts.ssh->endpt_client_ref = NULL;
3311 } else {
3312 endpt->opts.tls->endpt_client_ref = NULL;
3313 }
roman0bbc19c2023-05-26 09:59:09 +02003314 goto cleanup;
3315 }
3316
3317 /* find the endpoint leafref is referring to */
3318 endpt_name = lyd_get_value(node);
3319 for (i = 0; i < server_opts.endpt_count; i++) {
3320 if (!strcmp(endpt_name, server_opts.endpts[i].name)) {
3321 break;
3322 }
3323 }
3324
3325 if (i == server_opts.endpt_count) {
3326 /* endpt not found, save the name and try to look it up later */
roman2e797ef2023-06-19 10:47:49 +02003327 nc_server_config_del_endpt_reference(endpt);
roman0bbc19c2023-05-26 09:59:09 +02003328 endpt->referenced_endpt_name = strdup(endpt_name);
3329 if (!endpt->referenced_endpt_name) {
3330 ERRMEM;
3331 ret = 1;
roman0bbc19c2023-05-26 09:59:09 +02003332 }
3333 goto cleanup;
3334 }
3335
3336 /* check for self reference */
3337 if (endpt == &server_opts.endpts[i]) {
3338 ERR(NULL, "Self client authentication reference detected.");
3339 ret = 1;
3340 goto cleanup;
3341 }
3342
3343 /* check for cyclic references */
roman2e797ef2023-06-19 10:47:49 +02003344 ret = nc_server_config_endpoint_client_auth_has_cycle(endpt, &server_opts.endpts[i]);
roman0bbc19c2023-05-26 09:59:09 +02003345 if (ret) {
3346 ERR(NULL, "Cyclic client authentication reference detected.");
3347 goto cleanup;
3348 }
3349
3350 /* assign the current endpt the referrenced endpt */
roman2e797ef2023-06-19 10:47:49 +02003351 if (is_ssh(node)) {
3352 endpt->opts.ssh->endpt_client_ref = &server_opts.endpts[i];
3353 } else {
3354 endpt->opts.tls->endpt_client_ref = &server_opts.endpts[i];
3355 }
roman0bbc19c2023-05-26 09:59:09 +02003356
3357cleanup:
3358 return ret;
3359}
3360
roman3f9b65c2023-06-05 14:26:58 +02003361static int
3362nc_server_config_tls_replace_cert_data(const struct lyd_node *node, struct nc_server_tls_opts *opts)
3363{
3364 nc_server_config_tls_del_cert_data(opts);
3365 opts->cert_data = strdup(lyd_get_value(node));
3366 if (!opts->cert_data) {
3367 ERRMEM;
3368 return 1;
3369 }
3370
3371 return 0;
3372}
3373
3374static int
3375nc_server_config_tls_replace_cert_data_client_auth(const struct lyd_node *node, struct nc_certificate *cert)
3376{
3377 nc_server_config_tls_del_cert_data_certificate(cert);
3378 cert->data = strdup(lyd_get_value(node));
3379 if (!cert->data) {
3380 ERRMEM;
3381 return 1;
3382 }
3383
3384 return 0;
3385}
3386
3387static int
3388nc_server_config_cert_data(const struct lyd_node *node, NC_OPERATION op)
3389{
3390 int ret = 0;
roman3f9b65c2023-06-05 14:26:58 +02003391 struct nc_certificate *cert;
romanb6f44032023-06-30 15:07:56 +02003392 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02003393 struct nc_ch_client *ch_client;
roman3f9b65c2023-06-05 14:26:58 +02003394
3395 assert(!strcmp(LYD_NAME(node), "cert-data"));
3396
romanba93eac2023-07-18 14:36:48 +02003397 /* LOCK */
3398 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3399 return 1;
3400 }
3401
romanb6f44032023-06-30 15:07:56 +02003402 if (equal_parent_name(node, 3, "server-identity")) {
3403 if (nc_server_config_get_tls_opts(node, &opts)) {
3404 ret = 1;
3405 goto cleanup;
3406 }
roman3f9b65c2023-06-05 14:26:58 +02003407
roman3f9b65c2023-06-05 14:26:58 +02003408 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02003409 ret = nc_server_config_tls_replace_cert_data(node, opts);
roman3f9b65c2023-06-05 14:26:58 +02003410 if (ret) {
3411 goto cleanup;
3412 }
3413 }
romanb6f44032023-06-30 15:07:56 +02003414 } else if (equal_parent_name(node, 3, "ca-certs")) {
roman4cb8bb12023-06-29 09:16:46 +02003415 if (nc_server_config_get_cert(node, 0, &cert)) {
roman3f9b65c2023-06-05 14:26:58 +02003416 ret = 1;
3417 goto cleanup;
3418 }
3419
3420 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3421 ret = nc_server_config_tls_replace_cert_data_client_auth(node, cert);
3422 if (ret) {
3423 goto cleanup;
3424 }
3425 } else {
3426 nc_server_config_tls_del_cert_data_certificate(cert);
3427 }
romanb6f44032023-06-30 15:07:56 +02003428 } else if (equal_parent_name(node, 3, "ee-certs")) {
roman4cb8bb12023-06-29 09:16:46 +02003429 if (nc_server_config_get_cert(node, 1, &cert)) {
roman3f9b65c2023-06-05 14:26:58 +02003430 ret = 1;
3431 goto cleanup;
3432 }
3433
3434 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3435 ret = nc_server_config_tls_replace_cert_data_client_auth(node, cert);
3436 if (ret) {
3437 goto cleanup;
3438 }
3439 } else {
3440 nc_server_config_tls_del_cert_data_certificate(cert);
3441 }
3442 }
3443
3444cleanup:
romanba93eac2023-07-18 14:36:48 +02003445 if (is_ch(node)) {
3446 /* UNLOCK */
3447 nc_ch_client_unlock(ch_client);
3448 }
roman3f9b65c2023-06-05 14:26:58 +02003449 return ret;
3450}
3451
3452static int
3453nc_server_config_tls_create_asymmetric_key_ref(const struct lyd_node *node, struct nc_endpt *endpt)
3454{
3455 uint16_t i;
3456 struct nc_keystore *ks = &server_opts.keystore;
3457
3458 /* lookup name */
3459 for (i = 0; i < ks->asym_key_count; i++) {
3460 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
3461 break;
3462 }
3463 }
3464
3465 if (i == ks->asym_key_count) {
3466 ERR(NULL, "Asymmetric key \"%s\" not found in the keystore.", lyd_get_value(node));
3467 return 1;
3468 }
3469
3470 endpt->opts.tls->key_ref = &ks->asym_keys[i];
3471
3472 return 0;
3473}
3474
3475static int
3476nc_server_config_asymmetric_key(const struct lyd_node *node, NC_OPERATION op)
3477{
3478 int ret = 0;
3479 struct nc_endpt *endpt;
romanba93eac2023-07-18 14:36:48 +02003480 struct nc_ch_client *ch_client;
roman3f9b65c2023-06-05 14:26:58 +02003481
3482 assert(!strcmp(LYD_NAME(node), "asymmetric-key"));
3483
romanba93eac2023-07-18 14:36:48 +02003484 /* LOCK */
3485 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3486 return 1;
3487 }
3488
roman3f9b65c2023-06-05 14:26:58 +02003489 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
3490 ret = 1;
3491 goto cleanup;
3492 }
3493
3494 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3495 /* set to keystore */
3496 endpt->opts.tls->store = NC_STORE_KEYSTORE;
3497
3498 ret = nc_server_config_tls_create_asymmetric_key_ref(node, endpt);
3499 if (ret) {
3500 goto cleanup;
3501 }
3502 } else {
3503 endpt->opts.tls->key_ref = NULL;
3504 }
3505
3506cleanup:
romanba93eac2023-07-18 14:36:48 +02003507 if (is_ch(node)) {
3508 /* UNLOCK */
3509 nc_ch_client_unlock(ch_client);
3510 }
roman3f9b65c2023-06-05 14:26:58 +02003511 return ret;
3512}
3513
3514static int
romanb6f44032023-06-30 15:07:56 +02003515nc_server_config_tls_create_certificate_ref(const struct lyd_node *node, struct nc_server_tls_opts *opts, struct nc_asymmetric_key *key)
roman3f9b65c2023-06-05 14:26:58 +02003516{
3517 uint16_t i;
3518
3519 /* lookup name */
3520 for (i = 0; i < key->cert_count; i++) {
3521 if (!strcmp(lyd_get_value(node), key->certs[i].name)) {
3522 break;
3523 }
3524 }
3525
3526 if (i == key->cert_count) {
3527 ERR(NULL, "Certificate \"%s\" not found in the asymmetric key \"%s\".", lyd_get_value(node), key->name);
3528 return 1;
3529 }
3530
romanb6f44032023-06-30 15:07:56 +02003531 opts->cert_ref = &key->certs[i];
roman3f9b65c2023-06-05 14:26:58 +02003532
3533 return 0;
3534}
3535
3536static struct nc_asymmetric_key *
romanb6f44032023-06-30 15:07:56 +02003537nc_server_config_cert_get_asymmetric_key(const struct lyd_node *node)
roman3f9b65c2023-06-05 14:26:58 +02003538{
3539 uint16_t i;
3540 struct nc_keystore *ks = &server_opts.keystore;
3541
3542 /* starting with certificate node */
3543 assert(!strcmp(LYD_NAME(node), "certificate"));
3544
3545 /* switch to it's only sibling, must be asymmetric-key */
3546 node = node->prev;
3547 assert(!strcmp(LYD_NAME(node), "asymmetric-key"));
3548
3549 /* find the given asymmetric key */
3550 for (i = 0; i < ks->asym_key_count; i++) {
3551 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
3552 return &ks->asym_keys[i];
3553 }
3554 }
3555
3556 /* didn't find it */
3557 ERR(NULL, "Asymmetric key \"%s\" not found in the keystore.", lyd_get_value(node));
3558 return NULL;
3559}
3560
3561static int
3562nc_server_config_create_ca_certs_certificate(const struct lyd_node *node, struct nc_server_tls_opts *opts)
3563{
3564 assert(!strcmp(LYD_NAME(node), "certificate"));
3565
3566 node = lyd_child(node);
3567 assert(!strcmp(LYD_NAME(node), "name"));
3568
3569 return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->ca_certs.certs, sizeof *opts->ca_certs.certs, &opts->ca_certs.cert_count);
3570}
3571
3572static int
3573nc_server_config_create_ee_certs_certificate(const struct lyd_node *node, struct nc_server_tls_opts *opts)
3574{
3575 assert(!strcmp(LYD_NAME(node), "certificate"));
3576
3577 node = lyd_child(node);
3578 assert(!strcmp(LYD_NAME(node), "name"));
3579
3580 return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->ee_certs.certs, sizeof *opts->ee_certs.certs, &opts->ee_certs.cert_count);
3581}
3582
3583static int
3584nc_server_config_certificate(const struct lyd_node *node, NC_OPERATION op)
3585{
3586 int ret = 0;
roman3f9b65c2023-06-05 14:26:58 +02003587 struct nc_asymmetric_key *key;
romanb6f44032023-06-30 15:07:56 +02003588 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02003589 struct nc_ch_client *ch_client;
roman3f9b65c2023-06-05 14:26:58 +02003590
3591 assert(!strcmp(LYD_NAME(node), "certificate"));
3592
romanba93eac2023-07-18 14:36:48 +02003593 /* LOCK */
3594 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3595 return 1;
3596 }
3597
romanb6f44032023-06-30 15:07:56 +02003598 if (nc_server_config_get_tls_opts(node, &opts)) {
roman3f9b65c2023-06-05 14:26:58 +02003599 ret = 1;
3600 goto cleanup;
3601 }
3602
romanb6f44032023-06-30 15:07:56 +02003603 if (equal_parent_name(node, 1, "keystore-reference")) {
3604 /* TLS server-identity */
roman3f9b65c2023-06-05 14:26:58 +02003605 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3606 /* set to keystore */
romanb6f44032023-06-30 15:07:56 +02003607 opts->store = NC_STORE_KEYSTORE;
roman3f9b65c2023-06-05 14:26:58 +02003608
romanb6f44032023-06-30 15:07:56 +02003609 if (!opts->key_ref) {
roman3f9b65c2023-06-05 14:26:58 +02003610 /* we don't have a key from which we need the cert yet */
romanb6f44032023-06-30 15:07:56 +02003611 key = nc_server_config_cert_get_asymmetric_key(node);
roman3f9b65c2023-06-05 14:26:58 +02003612 if (!key) {
3613 ret = 1;
3614 goto cleanup;
3615 }
3616 } else {
3617 /* we have the key */
romanb6f44032023-06-30 15:07:56 +02003618 key = opts->key_ref;
roman3f9b65c2023-06-05 14:26:58 +02003619 }
3620
3621 /* find the given cert in the key and set it */
romanb6f44032023-06-30 15:07:56 +02003622 ret = nc_server_config_tls_create_certificate_ref(node, opts, key);
roman3f9b65c2023-06-05 14:26:58 +02003623 if (ret) {
3624 goto cleanup;
3625 }
3626 } else {
romanb6f44032023-06-30 15:07:56 +02003627 opts->cert_ref = NULL;
roman3f9b65c2023-06-05 14:26:58 +02003628 }
romanb6f44032023-06-30 15:07:56 +02003629 } else if (equal_parent_name(node, 2, "ca-certs")) {
3630 /* TLS client auth certificate authority */
roman3f9b65c2023-06-05 14:26:58 +02003631 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02003632 ret = nc_server_config_create_ca_certs_certificate(node, opts);
roman3f9b65c2023-06-05 14:26:58 +02003633 if (ret) {
3634 goto cleanup;
3635 }
3636 } else {
romanb6f44032023-06-30 15:07:56 +02003637 nc_server_config_tls_del_certs(&opts->ca_certs);
roman3f9b65c2023-06-05 14:26:58 +02003638 }
romanb6f44032023-06-30 15:07:56 +02003639 } else if (equal_parent_name(node, 2, "ee-certs")) {
3640 /* TLS client auth end entity */
roman3f9b65c2023-06-05 14:26:58 +02003641 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02003642 ret = nc_server_config_create_ee_certs_certificate(node, opts);
roman3f9b65c2023-06-05 14:26:58 +02003643 if (ret) {
3644 goto cleanup;
3645 }
3646 } else {
romanb6f44032023-06-30 15:07:56 +02003647 nc_server_config_tls_del_certs(&opts->ee_certs);
roman3f9b65c2023-06-05 14:26:58 +02003648 }
3649 }
3650
3651cleanup:
romanba93eac2023-07-18 14:36:48 +02003652 if (is_ch(node)) {
3653 /* UNLOCK */
3654 nc_ch_client_unlock(ch_client);
3655 }
roman3f9b65c2023-06-05 14:26:58 +02003656 return ret;
3657}
3658
3659static int
3660nc_server_config_create_cert_to_name(const struct lyd_node *node, struct nc_server_tls_opts *opts)
3661{
3662 int ret = 0;
3663 struct lyd_node *n;
3664 struct nc_ctn *new, *iter;
3665 const char *map_type, *name;
3666 uint32_t id;
3667 NC_TLS_CTN_MAPTYPE m_type;
3668
3669 assert(!strcmp(LYD_NAME(node), "cert-to-name"));
3670
3671 /* create new ctn */
3672 new = calloc(1, sizeof *new);
3673 if (!new) {
3674 ERRMEM;
3675 ret = 1;
3676 goto cleanup;
3677 }
3678
3679 /* get all the data */
3680 /* find the list's key */
3681 lyd_find_path(node, "id", 0, &n);
3682 assert(n);
3683 id = strtoul(lyd_get_value(n), NULL, 10);
3684
3685 /* find the ctn's name */
3686 lyd_find_path(node, "name", 0, &n);
3687 assert(n);
3688 name = lyd_get_value(n);
3689
3690 /* find the ctn's map-type */
3691 lyd_find_path(node, "map-type", 0, &n);
3692 assert(n);
3693 map_type = ((struct lyd_node_term *)n)->value.ident->name;
3694 if (!strcmp(map_type, "specified")) {
3695 m_type = NC_TLS_CTN_SPECIFIED;
3696 } else if (!strcmp(map_type, "san-rfc822-name")) {
3697 m_type = NC_TLS_CTN_SAN_RFC822_NAME;
3698 } else if (!strcmp(map_type, "san-dns-name")) {
3699 m_type = NC_TLS_CTN_SAN_DNS_NAME;
3700 } else if (!strcmp(map_type, "san-ip-address")) {
3701 m_type = NC_TLS_CTN_SAN_IP_ADDRESS;
3702 } else if (!strcmp(map_type, "san-any")) {
3703 m_type = NC_TLS_CTN_SAN_ANY;
3704 } else if (!strcmp(map_type, "common-name")) {
3705 m_type = NC_TLS_CTN_COMMON_NAME;
3706 } else {
3707 ERR(NULL, "Map-type identity \"%s\" not supported.", map_type);
3708 ret = 1;
3709 goto cleanup;
3710 }
3711
3712 /* find the right place for insertion */
3713 if (!opts->ctn) {
3714 /* inserting the first one */
3715 opts->ctn = new;
Roytak421eb0c2023-08-01 22:18:27 +02003716 } else if (opts->ctn->id > id) {
roman3f9b65c2023-06-05 14:26:58 +02003717 /* insert at the beginning */
3718 new->next = opts->ctn;
3719 opts->ctn = new;
3720 } else {
3721 /* have to find the right place */
Roytak421eb0c2023-08-01 22:18:27 +02003722 for (iter = opts->ctn; iter->next && iter->next->id <= id; iter = iter->next) {}
3723 if (iter->id == id) {
roman3f9b65c2023-06-05 14:26:58 +02003724 /* collision */
3725 new = iter;
3726 } else {
3727 new->next = iter->next;
3728 iter->next = new;
3729 }
3730 }
3731
3732 /* insert the right data */
3733 new->id = id;
3734 if (new->name) {
3735 free(new->name);
3736 }
3737 new->name = strdup(name);
3738 if (!new->name) {
3739 ERRMEM;
3740 ret = 1;
3741 goto cleanup;
3742 }
3743 new->map_type = m_type;
3744
3745cleanup:
3746 return ret;
3747}
3748
3749static int
3750nc_server_config_cert_to_name(const struct lyd_node *node, NC_OPERATION op)
3751{
3752 int ret = 0;
romanb6f44032023-06-30 15:07:56 +02003753 struct nc_server_tls_opts *opts;
roman3f9b65c2023-06-05 14:26:58 +02003754 struct lyd_node *key;
3755 struct nc_ctn *ctn;
romanba93eac2023-07-18 14:36:48 +02003756 struct nc_ch_client *ch_client;
roman3f9b65c2023-06-05 14:26:58 +02003757
3758 assert(!strcmp(LYD_NAME(node), "cert-to-name"));
3759
romanba93eac2023-07-18 14:36:48 +02003760 /* LOCK */
3761 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3762 return 1;
3763 }
3764
romanb6f44032023-06-30 15:07:56 +02003765 if (nc_server_config_get_tls_opts(node, &opts)) {
roman3f9b65c2023-06-05 14:26:58 +02003766 ret = 1;
3767 goto cleanup;
3768 }
3769
3770 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02003771 ret = nc_server_config_create_cert_to_name(node, opts);
roman3f9b65c2023-06-05 14:26:58 +02003772 if (ret) {
3773 goto cleanup;
3774 }
3775 } else {
3776 /* find the given ctn entry */
3777 lyd_find_path(node, "id", 0, &key);
3778 assert(key);
roman4cb8bb12023-06-29 09:16:46 +02003779 if (nc_server_config_get_ctn(node, &ctn)) {
roman3f9b65c2023-06-05 14:26:58 +02003780 ret = 1;
3781 goto cleanup;
3782 }
romanb6f44032023-06-30 15:07:56 +02003783 nc_server_config_del_ctn(opts, ctn);
roman3f9b65c2023-06-05 14:26:58 +02003784 }
3785
3786cleanup:
romanba93eac2023-07-18 14:36:48 +02003787 if (is_ch(node)) {
3788 /* UNLOCK */
3789 nc_ch_client_unlock(ch_client);
3790 }
roman3f9b65c2023-06-05 14:26:58 +02003791 return ret;
3792}
3793
3794static int
3795nc_server_config_replace_fingerprint(const struct lyd_node *node, struct nc_ctn *ctn)
3796{
3797 nc_server_config_del_fingerprint(ctn);
3798
3799 ctn->fingerprint = strdup(lyd_get_value(node));
3800 if (!ctn->fingerprint) {
3801 ERRMEM;
3802 return 1;
3803 }
3804
3805 return 0;
3806}
3807
3808static int
3809nc_server_config_fingerprint(const struct lyd_node *node, NC_OPERATION op)
3810{
3811 int ret = 0;
roman3f9b65c2023-06-05 14:26:58 +02003812 struct nc_ctn *ctn;
romanba93eac2023-07-18 14:36:48 +02003813 struct nc_ch_client *ch_client;
3814
3815 assert(!strcmp(LYD_NAME(node), "fingerprint"));
3816
3817 /* LOCK */
3818 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3819 return 1;
3820 }
roman3f9b65c2023-06-05 14:26:58 +02003821
roman4cb8bb12023-06-29 09:16:46 +02003822 if (nc_server_config_get_ctn(node, &ctn)) {
roman3f9b65c2023-06-05 14:26:58 +02003823 ret = 1;
3824 goto cleanup;
3825 }
3826
3827 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3828 ret = nc_server_config_replace_fingerprint(node, ctn);
3829 if (ret) {
3830 goto cleanup;
3831 }
3832 } else {
3833 nc_server_config_del_fingerprint(ctn);
3834 }
3835
3836cleanup:
romanba93eac2023-07-18 14:36:48 +02003837 if (is_ch(node)) {
3838 /* UNLOCK */
3839 nc_ch_client_unlock(ch_client);
3840 }
roman3f9b65c2023-06-05 14:26:58 +02003841 return ret;
3842}
3843
roman12644fe2023-06-08 11:06:42 +02003844static void
3845nc_server_config_set_tls_version(struct nc_server_tls_opts *opts, NC_TLS_VERSION version, NC_OPERATION op)
3846{
3847 if (op == NC_OP_CREATE) {
3848 /* add the version if it isn't there already */
3849 opts->tls_versions |= version;
3850 } else if ((op == NC_OP_DELETE) && (opts->tls_versions & version)) {
3851 /* delete the version if it is there */
3852 opts->tls_versions -= version;
3853 }
3854}
3855
3856static int
3857nc_server_config_tls_version(const struct lyd_node *node, NC_OPERATION op)
3858{
3859 int ret = 0;
romanb6f44032023-06-30 15:07:56 +02003860 struct nc_server_tls_opts *opts;
roman12644fe2023-06-08 11:06:42 +02003861 const char *version = NULL;
romanba93eac2023-07-18 14:36:48 +02003862 struct nc_ch_client *ch_client;
roman12644fe2023-06-08 11:06:42 +02003863
3864 assert(!strcmp(LYD_NAME(node), "tls-version"));
3865
romanba93eac2023-07-18 14:36:48 +02003866 /* LOCK */
3867 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3868 return 1;
3869 }
3870
romanb6f44032023-06-30 15:07:56 +02003871 if (nc_server_config_get_tls_opts(node, &opts)) {
roman12644fe2023-06-08 11:06:42 +02003872 ret = 1;
3873 goto cleanup;
3874 }
3875
3876 version = ((struct lyd_node_term *)node)->value.ident->name;
3877 if (!strcmp(version, "tls10")) {
romanb6f44032023-06-30 15:07:56 +02003878 nc_server_config_set_tls_version(opts, NC_TLS_VERSION_10, op);
roman12644fe2023-06-08 11:06:42 +02003879 } else if (!strcmp(version, "tls11")) {
romanb6f44032023-06-30 15:07:56 +02003880 nc_server_config_set_tls_version(opts, NC_TLS_VERSION_11, op);
roman12644fe2023-06-08 11:06:42 +02003881 } else if (!strcmp(version, "tls12")) {
romanb6f44032023-06-30 15:07:56 +02003882 nc_server_config_set_tls_version(opts, NC_TLS_VERSION_12, op);
roman12644fe2023-06-08 11:06:42 +02003883 } else if (!strcmp(version, "tls13")) {
romanb6f44032023-06-30 15:07:56 +02003884 nc_server_config_set_tls_version(opts, NC_TLS_VERSION_13, op);
roman12644fe2023-06-08 11:06:42 +02003885 } else {
3886 ERR(NULL, "TLS version \"%s\" not supported.", version);
3887 ret = 1;
3888 goto cleanup;
3889 }
3890
3891cleanup:
romanba93eac2023-07-18 14:36:48 +02003892 if (is_ch(node)) {
3893 /* UNLOCK */
3894 nc_ch_client_unlock(ch_client);
3895 }
roman12644fe2023-06-08 11:06:42 +02003896 return ret;
3897}
3898
3899static int
3900nc_server_config_create_cipher_suite(struct nc_server_tls_opts *opts, const char *cipher)
3901{
3902 int ret = 0;
3903 char *ssl_cipher = NULL;
3904 uint16_t i;
3905
3906 ssl_cipher = malloc(strlen(cipher) + 1);
3907 if (!ssl_cipher) {
3908 ERRMEM;
3909 ret = 1;
3910 goto cleanup;
3911 }
3912
3913 for (i = 0; cipher[i]; i++) {
3914 if (cipher[i] == '-') {
3915 /* OpenSSL requires _ instead of - in cipher names */
3916 ssl_cipher[i] = '_';
3917 } else {
3918 /* and requires uppercase unlike the identities */
3919 ssl_cipher[i] = toupper(cipher[i]);
3920 }
3921 }
3922 ssl_cipher[i] = '\0';
3923
3924 if (!opts->ciphers) {
3925 /* first entry */
3926 opts->ciphers = strdup(ssl_cipher);
3927 if (!opts->ciphers) {
3928 ERRMEM;
3929 ret = 1;
3930 goto cleanup;
3931 }
3932 } else {
3933 /* + 1 because of : between entries */
3934 opts->ciphers = nc_realloc(opts->ciphers, strlen(opts->ciphers) + strlen(ssl_cipher) + 1 + 1);
3935 if (!opts->ciphers) {
3936 ERRMEM;
3937 ret = 1;
3938 goto cleanup;
3939 }
roman08f67f42023-06-08 13:51:54 +02003940 strcat(opts->ciphers, ":");
3941 strcat(opts->ciphers, ssl_cipher);
roman12644fe2023-06-08 11:06:42 +02003942 }
3943
3944cleanup:
3945 free(ssl_cipher);
3946 return ret;
3947}
3948
3949static int
romanb6f44032023-06-30 15:07:56 +02003950nc_server_config_del_cipher_suite(struct nc_server_tls_opts *opts, const char *cipher)
roman12644fe2023-06-08 11:06:42 +02003951{
3952 int cipher_found = 0;
3953 char *haystack, *substr;
3954 size_t cipher_len = strlen(cipher);
3955
3956 /* delete */
3957 haystack = opts->ciphers;
3958 while ((substr = strstr(haystack, cipher))) {
3959 /* iterate over all the substrings */
3960 if (((substr == haystack) && (*(substr + cipher_len) == ':')) ||
3961 ((substr != haystack) && (*(substr - 1) == ':') && (*(substr + cipher_len) == ':'))) {
3962 /* either the first element of the string or somewhere in the middle */
3963 memmove(substr, substr + cipher_len + 1, strlen(substr + cipher_len + 1));
3964 cipher_found = 1;
3965 break;
3966 } else if ((*(substr - 1) == ':') && (*(substr + cipher_len) == '\0')) {
3967 /* the last element of the string */
3968 *(substr - 1) = '\0';
3969 cipher_found = 1;
3970 break;
3971 }
3972 haystack++;
3973 }
3974
3975 if (!cipher_found) {
3976 ERR(NULL, "Unable to delete a cipher (%s), which was not previously added.", cipher);
3977 return 1;
3978 }
3979
3980 return 0;
3981}
3982
3983static int
3984nc_server_config_cipher_suite(const struct lyd_node *node, NC_OPERATION op)
3985{
3986 int ret = 0;
romanb6f44032023-06-30 15:07:56 +02003987 struct nc_server_tls_opts *opts;
roman12644fe2023-06-08 11:06:42 +02003988 const char *cipher = NULL;
romanba93eac2023-07-18 14:36:48 +02003989 struct nc_ch_client *ch_client;
roman12644fe2023-06-08 11:06:42 +02003990
romanfaecc582023-06-15 16:13:31 +02003991 assert(!strcmp(LYD_NAME(node), "cipher-suite"));
3992
romanba93eac2023-07-18 14:36:48 +02003993 /* LOCK */
3994 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3995 return 1;
3996 }
3997
romanb6f44032023-06-30 15:07:56 +02003998 if (nc_server_config_get_tls_opts(node, &opts)) {
roman12644fe2023-06-08 11:06:42 +02003999 ret = 1;
4000 goto cleanup;
4001 }
4002
4003 cipher = ((struct lyd_node_term *)node)->value.ident->name;
4004 if (op == NC_OP_CREATE) {
romanb6f44032023-06-30 15:07:56 +02004005 ret = nc_server_config_create_cipher_suite(opts, cipher);
roman12644fe2023-06-08 11:06:42 +02004006 if (ret) {
4007 goto cleanup;
4008 }
4009 } else if (op == NC_OP_DELETE) {
romanb6f44032023-06-30 15:07:56 +02004010 ret = nc_server_config_del_cipher_suite(opts, cipher);
roman12644fe2023-06-08 11:06:42 +02004011 if (ret) {
4012 goto cleanup;
4013 }
4014 }
4015
4016cleanup:
romanba93eac2023-07-18 14:36:48 +02004017 if (is_ch(node)) {
4018 /* UNLOCK */
4019 nc_ch_client_unlock(ch_client);
4020 }
roman12644fe2023-06-08 11:06:42 +02004021 return ret;
4022}
4023
romanfaecc582023-06-15 16:13:31 +02004024static int
4025nc_server_config_crl_url(const struct lyd_node *node, NC_OPERATION op)
4026{
4027 int ret = 0;
romanb6f44032023-06-30 15:07:56 +02004028 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02004029 struct nc_ch_client *ch_client;
romanfaecc582023-06-15 16:13:31 +02004030
4031 assert(!strcmp(LYD_NAME(node), "crl-url"));
4032
romanba93eac2023-07-18 14:36:48 +02004033 /* LOCK */
4034 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4035 return 1;
4036 }
4037
romanb6f44032023-06-30 15:07:56 +02004038 if (nc_server_config_get_tls_opts(node, &opts)) {
romanfaecc582023-06-15 16:13:31 +02004039 ret = 1;
4040 goto cleanup;
4041 }
4042
4043 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02004044 nc_server_config_del_url(opts);
4045 opts->crl_url = strdup(lyd_get_value(node));
4046 if (!opts->crl_url) {
romanfaecc582023-06-15 16:13:31 +02004047 ERRMEM;
4048 ret = 1;
4049 goto cleanup;
4050 }
4051 } else if (op == NC_OP_DELETE) {
romanb6f44032023-06-30 15:07:56 +02004052 nc_server_config_del_url(opts);
romanfaecc582023-06-15 16:13:31 +02004053 }
4054
4055cleanup:
romanba93eac2023-07-18 14:36:48 +02004056 if (is_ch(node)) {
4057 /* UNLOCK */
4058 nc_ch_client_unlock(ch_client);
4059 }
romanfaecc582023-06-15 16:13:31 +02004060 return ret;
4061}
4062
4063static int
4064nc_server_config_crl_path(const struct lyd_node *node, NC_OPERATION op)
4065{
4066 int ret = 0;
romanb6f44032023-06-30 15:07:56 +02004067 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02004068 struct nc_ch_client *ch_client;
romanfaecc582023-06-15 16:13:31 +02004069
4070 assert(!strcmp(LYD_NAME(node), "crl-path"));
4071
romanba93eac2023-07-18 14:36:48 +02004072 /* LOCK */
4073 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4074 return 1;
4075 }
4076
romanb6f44032023-06-30 15:07:56 +02004077 if (nc_server_config_get_tls_opts(node, &opts)) {
romanfaecc582023-06-15 16:13:31 +02004078 ret = 1;
4079 goto cleanup;
4080 }
4081
4082 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02004083 nc_server_config_del_path(opts);
4084 opts->crl_path = strdup(lyd_get_value(node));
4085 if (!opts->crl_path) {
romanfaecc582023-06-15 16:13:31 +02004086 ERRMEM;
4087 ret = 1;
4088 goto cleanup;
4089 }
4090 } else if (op == NC_OP_DELETE) {
romanb6f44032023-06-30 15:07:56 +02004091 nc_server_config_del_path(opts);
romanfaecc582023-06-15 16:13:31 +02004092 }
4093
4094cleanup:
romanba93eac2023-07-18 14:36:48 +02004095 if (is_ch(node)) {
4096 /* UNLOCK */
4097 nc_ch_client_unlock(ch_client);
4098 }
romanfaecc582023-06-15 16:13:31 +02004099 return ret;
4100}
4101
4102static int
4103nc_server_config_crl_cert_ext(const struct lyd_node *node, NC_OPERATION op)
4104{
4105 int ret = 0;
romanb6f44032023-06-30 15:07:56 +02004106 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02004107 struct nc_ch_client *ch_client;
romanfaecc582023-06-15 16:13:31 +02004108
4109 assert(!strcmp(LYD_NAME(node), "crl-cert-ext"));
4110
romanba93eac2023-07-18 14:36:48 +02004111 /* LOCK */
4112 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4113 return 1;
4114 }
4115
romanb6f44032023-06-30 15:07:56 +02004116 if (nc_server_config_get_tls_opts(node, &opts)) {
romanfaecc582023-06-15 16:13:31 +02004117 ret = 1;
4118 goto cleanup;
4119 }
4120
4121 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02004122 opts->crl_cert_ext = 1;
romanfaecc582023-06-15 16:13:31 +02004123 } else if (op == NC_OP_DELETE) {
romanb6f44032023-06-30 15:07:56 +02004124 opts->crl_cert_ext = 0;
romanfaecc582023-06-15 16:13:31 +02004125 }
4126
4127cleanup:
romanba93eac2023-07-18 14:36:48 +02004128 if (is_ch(node)) {
4129 /* UNLOCK */
4130 nc_ch_client_unlock(ch_client);
4131 }
romanfaecc582023-06-15 16:13:31 +02004132 return ret;
4133}
4134
roman2eab4742023-06-06 10:00:26 +02004135#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +02004136
roman83683fb2023-02-24 09:15:23 +01004137static int
roman5cbb6532023-06-22 12:53:17 +02004138nc_server_config_create_netconf_client(const struct lyd_node *node)
4139{
4140 int ret = 0;
4141
4142 node = lyd_child(node);
4143 assert(!strcmp(LYD_NAME(node), "name"));
4144
4145 /* LOCK */
4146 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
4147
4148 ret = nc_server_config_realloc(lyd_get_value(node), (void **)&server_opts.ch_clients, sizeof *server_opts.ch_clients, &server_opts.ch_client_count);
4149 if (ret) {
4150 goto cleanup;
4151 }
4152
4153 server_opts.ch_clients[server_opts.ch_client_count - 1].id = ATOMIC_INC_RELAXED(server_opts.new_client_id);
4154 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
4155 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3; // TODO
4156
4157 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
4158
4159cleanup:
4160 /* UNLOCK */
4161 pthread_rwlock_unlock(&server_opts.ch_client_lock);
4162 return ret;
4163}
4164
4165static int
4166nc_server_config_netconf_client(const struct lyd_node *node, NC_OPERATION op)
4167{
4168 int ret = 0;
4169 struct nc_ch_client *ch_client;
4170
4171 assert(!strcmp(LYD_NAME(node), "netconf-client"));
4172
4173 if (op == NC_OP_CREATE) {
4174 ret = nc_server_config_create_netconf_client(node);
4175 if (ret) {
4176 goto cleanup;
4177 }
4178 } else if (op == NC_OP_DELETE) {
4179 if (nc_server_config_get_ch_client(node, &ch_client)) {
4180 ret = 1;
4181 goto cleanup;
4182 }
4183
4184 nc_server_config_ch_del_client(ch_client);
4185 }
4186
4187cleanup:
4188 return ret;
4189}
4190
4191#ifdef NC_ENABLED_SSH_TLS
4192
4193static int
4194nc_server_config_remote_address(const struct lyd_node *node, NC_OPERATION op)
4195{
4196 int ret = 0;
roman5cbb6532023-06-22 12:53:17 +02004197 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02004198 struct nc_ch_client *ch_client;
roman5cbb6532023-06-22 12:53:17 +02004199
romanb6f44032023-06-30 15:07:56 +02004200 assert(!strcmp(LYD_NAME(node), "remote-address"));
4201
romanba93eac2023-07-18 14:36:48 +02004202 /* LOCK */
4203 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4204 return 1;
4205 }
4206
roman4cb8bb12023-06-29 09:16:46 +02004207 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02004208 ret = 1;
4209 goto cleanup;
4210 }
4211
4212 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
4213 nc_server_config_del_remote_address(ch_endpt);
4214
4215 ch_endpt->address = strdup(lyd_get_value(node));
4216 if (!ch_endpt->address) {
4217 ERRMEM;
4218 ret = 1;
4219 goto cleanup;
4220 }
4221 } else {
4222 nc_server_config_del_remote_address(ch_endpt);
4223 }
4224
4225cleanup:
romanba93eac2023-07-18 14:36:48 +02004226 /* UNLOCK */
4227 nc_ch_client_unlock(ch_client);
roman5cbb6532023-06-22 12:53:17 +02004228 return ret;
4229}
4230
4231static int
4232nc_server_config_remote_port(const struct lyd_node *node, NC_OPERATION op)
4233{
4234 int ret = 0;
roman5cbb6532023-06-22 12:53:17 +02004235 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02004236 struct nc_ch_client *ch_client;
roman5cbb6532023-06-22 12:53:17 +02004237
romanb6f44032023-06-30 15:07:56 +02004238 assert(!strcmp(LYD_NAME(node), "remote-port"));
4239
romanba93eac2023-07-18 14:36:48 +02004240 /* LOCK */
4241 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4242 return 1;
4243 }
4244
roman4cb8bb12023-06-29 09:16:46 +02004245 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02004246 ret = 1;
4247 goto cleanup;
4248 }
4249
4250 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
4251 ch_endpt->port = strtoul(lyd_get_value(node), NULL, 10);
4252 } else {
4253 ch_endpt->port = 0;
4254 }
4255
4256cleanup:
romanba93eac2023-07-18 14:36:48 +02004257 /* UNLOCK */
4258 nc_ch_client_unlock(ch_client);
roman5cbb6532023-06-22 12:53:17 +02004259 return ret;
4260}
4261
4262#endif /* NC_ENABLED_SSH_TLS */
4263
4264static int
romanb6f44032023-06-30 15:07:56 +02004265nc_server_config_persistent(const struct lyd_node *node, NC_OPERATION op)
4266{
4267 int ret = 0;
4268 struct nc_ch_client *ch_client;
4269
4270 assert(!strcmp(LYD_NAME(node), "persistent"));
4271
4272 (void) op;
4273
romanba93eac2023-07-18 14:36:48 +02004274 /* LOCK */
4275 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanb6f44032023-06-30 15:07:56 +02004276 ret = 1;
4277 goto cleanup;
4278 }
4279
4280 ch_client->conn_type = NC_CH_PERSIST;
4281
romanba93eac2023-07-18 14:36:48 +02004282 /* UNLOCK */
4283 nc_ch_client_unlock(ch_client);
4284
romanb6f44032023-06-30 15:07:56 +02004285cleanup:
4286 return ret;
4287}
4288
4289static int
4290nc_server_config_periodic(const struct lyd_node *node, NC_OPERATION op)
4291{
4292 int ret = 0;
4293 struct nc_ch_client *ch_client;
4294
4295 assert(!strcmp(LYD_NAME(node), "periodic"));
4296
4297 (void) op;
4298
romanba93eac2023-07-18 14:36:48 +02004299 /* LOCK */
4300 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanb6f44032023-06-30 15:07:56 +02004301 ret = 1;
4302 goto cleanup;
4303 }
4304
4305 ch_client->conn_type = NC_CH_PERIOD;
4306 /* set default values */
4307 ch_client->period = 60;
4308 ch_client->anchor_time = 0;
4309 ch_client->idle_timeout = 180;
4310
romanba93eac2023-07-18 14:36:48 +02004311 /* UNLOCK */
4312 nc_ch_client_unlock(ch_client);
4313
romanb6f44032023-06-30 15:07:56 +02004314cleanup:
4315 return ret;
4316}
4317
4318static int
4319nc_server_config_period(const struct lyd_node *node, NC_OPERATION op)
4320{
4321 int ret = 0;
4322 struct nc_ch_client *ch_client;
4323
4324 assert(!strcmp(LYD_NAME(node), "period"));
4325
romanba93eac2023-07-18 14:36:48 +02004326 /* LOCK */
4327 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanb6f44032023-06-30 15:07:56 +02004328 ret = 1;
4329 goto cleanup;
4330 }
4331
4332 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
4333 ch_client->period = strtoul(lyd_get_value(node), NULL, 10);
4334 } else if (op == NC_OP_DELETE) {
4335 ch_client->period = 60;
4336 }
4337
romanba93eac2023-07-18 14:36:48 +02004338 /* UNLOCK */
4339 nc_ch_client_unlock(ch_client);
4340
romanb6f44032023-06-30 15:07:56 +02004341cleanup:
4342 return ret;
4343}
4344
4345static int
4346nc_server_config_anchor_time(const struct lyd_node *node, NC_OPERATION op)
4347{
4348 int ret = 0;
4349 struct nc_ch_client *ch_client;
4350 time_t anchor_time = {0};
4351
4352 assert(!strcmp(LYD_NAME(node), "anchor-time"));
4353
romanba93eac2023-07-18 14:36:48 +02004354 /* LOCK */
4355 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4356 return 1;
romanb6f44032023-06-30 15:07:56 +02004357 }
4358
4359 ret = ly_time_str2time(lyd_get_value(node), &anchor_time, NULL);
4360 if (ret) {
4361 goto cleanup;
4362 }
4363
4364 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
4365 ch_client->anchor_time = anchor_time;
4366 } else if (op == NC_OP_DELETE) {
4367 ch_client->anchor_time = 0;
4368 }
4369
4370cleanup:
romanba93eac2023-07-18 14:36:48 +02004371 /* UNLOCK */
4372 nc_ch_client_unlock(ch_client);
romanb6f44032023-06-30 15:07:56 +02004373 return ret;
4374}
4375
4376static int
4377nc_server_config_reconnect_strategy(const struct lyd_node *node, NC_OPERATION op)
4378{
4379 int ret = 0;
4380 struct nc_ch_client *ch_client;
4381
4382 assert(!strcmp(LYD_NAME(node), "reconnect-strategy"));
4383
4384 (void) op;
4385
romanba93eac2023-07-18 14:36:48 +02004386 /* LOCK */
4387 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanb6f44032023-06-30 15:07:56 +02004388 ret = 1;
4389 goto cleanup;
4390 }
4391
4392 /* set to default values */
4393 ch_client->start_with = NC_CH_FIRST_LISTED;
4394 ch_client->max_wait = 5;
4395 ch_client->max_attempts = 3;
4396
romanba93eac2023-07-18 14:36:48 +02004397 /* UNLOCK */
4398 nc_ch_client_unlock(ch_client);
4399
romanb6f44032023-06-30 15:07:56 +02004400cleanup:
4401 return ret;
4402}
4403
4404static int
4405nc_server_config_start_with(const struct lyd_node *node, NC_OPERATION op)
4406{
4407 int ret = 0;
4408 struct nc_ch_client *ch_client;
4409 const char *value;
4410
4411 assert(!strcmp(LYD_NAME(node), "start-with"));
4412
romanba93eac2023-07-18 14:36:48 +02004413 /* LOCK */
4414 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4415 return 1;
romanb6f44032023-06-30 15:07:56 +02004416 }
4417
4418 if (op == NC_OP_DELETE) {
4419 ch_client->start_with = NC_CH_FIRST_LISTED;
4420 goto cleanup;
4421 }
4422
4423 value = lyd_get_value(node);
4424 if (!strcmp(value, "first-listed")) {
4425 ch_client->start_with = NC_CH_FIRST_LISTED;
4426 } else if (!strcmp(value, "last-connected")) {
4427 ch_client->start_with = NC_CH_LAST_CONNECTED;
4428 } else if (!strcmp(value, "random-selection")) {
4429 ch_client->start_with = NC_CH_RANDOM;
4430 } else {
4431 ERR(NULL, "Unexpected start-with value \"%s\".", value);
4432 ret = 1;
4433 goto cleanup;
4434 }
4435
4436cleanup:
romanba93eac2023-07-18 14:36:48 +02004437 /* UNLOCK */
4438 nc_ch_client_unlock(ch_client);
romanb6f44032023-06-30 15:07:56 +02004439 return ret;
4440}
4441
4442static int
4443nc_server_config_max_wait(const struct lyd_node *node, NC_OPERATION op)
4444{
4445 int ret = 0;
4446 struct nc_ch_client *ch_client;
4447
4448 assert(!strcmp(LYD_NAME(node), "max-wait"));
4449
romanba93eac2023-07-18 14:36:48 +02004450 /* LOCK */
4451 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanb6f44032023-06-30 15:07:56 +02004452 ret = 1;
4453 goto cleanup;
4454 }
4455
4456 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
4457 ch_client->max_wait = strtoul(lyd_get_value(node), NULL, 10);
4458 } else {
4459 ch_client->max_wait = 5;
4460 }
4461
romanba93eac2023-07-18 14:36:48 +02004462 /* UNLOCK */
4463 nc_ch_client_unlock(ch_client);
4464
romanb6f44032023-06-30 15:07:56 +02004465cleanup:
4466 return ret;
4467}
4468
4469static int
4470nc_server_config_max_attempts(const struct lyd_node *node, NC_OPERATION op)
4471{
4472 int ret = 0;
4473 struct nc_ch_client *ch_client;
4474
4475 assert(!strcmp(LYD_NAME(node), "max-attempts"));
4476
romanba93eac2023-07-18 14:36:48 +02004477 /* LOCK */
4478 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanb6f44032023-06-30 15:07:56 +02004479 ret = 1;
4480 goto cleanup;
4481 }
4482
4483 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
4484 ch_client->max_attempts = strtoul(lyd_get_value(node), NULL, 10);
4485 } else {
4486 ch_client->max_attempts = 3;
4487 }
4488
romanba93eac2023-07-18 14:36:48 +02004489 /* UNLOCK */
4490 nc_ch_client_unlock(ch_client);
4491
romanb6f44032023-06-30 15:07:56 +02004492cleanup:
4493 return ret;
4494}
4495
4496static int
romanf02273a2023-05-25 09:44:11 +02004497nc_server_config_parse_netconf_server(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01004498{
4499 const char *name = LYD_NAME(node);
4500
4501 if (!strcmp(name, "listen")) {
romanf02273a2023-05-25 09:44:11 +02004502 if (nc_server_config_listen(NULL, op)) {
romanc1d2b092023-02-02 08:58:27 +01004503 goto error;
4504 }
4505 } else if (!strcmp(name, "idle-timeout")) {
romane028ef92023-02-24 16:33:08 +01004506 if (nc_server_config_idle_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004507 goto error;
4508 }
4509 } else if (!strcmp(name, "endpoint")) {
romane028ef92023-02-24 16:33:08 +01004510 if (nc_server_config_endpoint(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004511 goto error;
4512 }
roman2eab4742023-06-06 10:00:26 +02004513 } else if (!strcmp(name, "unix-socket")) {
4514 if (nc_server_config_unix_socket(node, op)) {
4515 goto error;
4516 }
roman3f9b65c2023-06-05 14:26:58 +02004517 }
roman2eab4742023-06-06 10:00:26 +02004518#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +02004519 else if (!strcmp(name, "ssh")) {
romane028ef92023-02-24 16:33:08 +01004520 if (nc_server_config_ssh(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004521 goto error;
4522 }
roman2eab4742023-06-06 10:00:26 +02004523 } else if (!strcmp(name, "local-address")) {
romane028ef92023-02-24 16:33:08 +01004524 if (nc_server_config_local_address(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004525 goto error;
4526 }
4527 } else if (!strcmp(name, "local-port")) {
romane028ef92023-02-24 16:33:08 +01004528 if (nc_server_config_local_port(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004529 goto error;
4530 }
4531 } else if (!strcmp(name, "keepalives")) {
romane028ef92023-02-24 16:33:08 +01004532 if (nc_server_config_keepalives(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004533 goto error;
4534 }
4535 } else if (!strcmp(name, "idle-time")) {
romane028ef92023-02-24 16:33:08 +01004536 if (nc_server_config_idle_time(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004537 goto error;
4538 }
4539 } else if (!strcmp(name, "max-probes")) {
romane028ef92023-02-24 16:33:08 +01004540 if (nc_server_config_max_probes(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004541 goto error;
4542 }
4543 } else if (!strcmp(name, "probe-interval")) {
romane028ef92023-02-24 16:33:08 +01004544 if (nc_server_config_probe_interval(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004545 goto error;
4546 }
roman2eab4742023-06-06 10:00:26 +02004547 } else if (!strcmp(name, "host-key")) {
romane028ef92023-02-24 16:33:08 +01004548 if (nc_server_config_host_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004549 goto error;
4550 }
roman2eab4742023-06-06 10:00:26 +02004551 } else if (!strcmp(name, "public-key-format")) {
romane028ef92023-02-24 16:33:08 +01004552 if (nc_server_config_public_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004553 goto error;
4554 }
4555 } else if (!strcmp(name, "public-key")) {
romane028ef92023-02-24 16:33:08 +01004556 if (nc_server_config_public_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004557 goto error;
4558 }
4559 } else if (!strcmp(name, "private-key-format")) {
romane028ef92023-02-24 16:33:08 +01004560 if (nc_server_config_private_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004561 goto error;
4562 }
4563 } else if (!strcmp(name, "cleartext-private-key")) {
romane028ef92023-02-24 16:33:08 +01004564 if (nc_server_config_cleartext_private_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004565 goto error;
4566 }
roman2eab4742023-06-06 10:00:26 +02004567 } else if (!strcmp(name, "keystore-reference")) {
romane028ef92023-02-24 16:33:08 +01004568 if (nc_server_config_keystore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004569 goto error;
4570 }
4571 } else if (!strcmp(name, "user")) {
romane028ef92023-02-24 16:33:08 +01004572 if (nc_server_config_user(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004573 goto error;
4574 }
4575 } else if (!strcmp(name, "auth-attempts")) {
romane028ef92023-02-24 16:33:08 +01004576 if (nc_server_config_auth_attempts(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004577 goto error;
4578 }
4579 } else if (!strcmp(name, "auth-timeout")) {
romane028ef92023-02-24 16:33:08 +01004580 if (nc_server_config_auth_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004581 goto error;
4582 }
roman2eab4742023-06-06 10:00:26 +02004583 } else if (!strcmp(name, "truststore-reference")) {
romane028ef92023-02-24 16:33:08 +01004584 if (nc_server_config_truststore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004585 goto error;
4586 }
roman2eab4742023-06-06 10:00:26 +02004587 } else if (!strcmp(name, "password")) {
romane028ef92023-02-24 16:33:08 +01004588 if (nc_server_config_password(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004589 goto error;
4590 }
4591 } else if (!strcmp(name, "pam-config-file-name")) {
romane028ef92023-02-24 16:33:08 +01004592 if (nc_server_config_pam_name(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004593 goto error;
4594 }
4595 } else if (!strcmp(name, "pam-config-file-dir")) {
romane028ef92023-02-24 16:33:08 +01004596 if (nc_server_config_pam_dir(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004597 goto error;
4598 }
4599 } else if (!strcmp(name, "none")) {
romane028ef92023-02-24 16:33:08 +01004600 if (nc_server_config_none(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004601 goto error;
4602 }
4603 } else if (!strcmp(name, "host-key-alg")) {
romane028ef92023-02-24 16:33:08 +01004604 if (nc_server_config_host_key_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004605 goto error;
4606 }
4607 } else if (!strcmp(name, "key-exchange-alg")) {
romane028ef92023-02-24 16:33:08 +01004608 if (nc_server_config_kex_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004609 goto error;
4610 }
4611 } else if (!strcmp(name, "encryption-alg")) {
romane028ef92023-02-24 16:33:08 +01004612 if (nc_server_config_encryption_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004613 goto error;
4614 }
4615 } else if (!strcmp(name, "mac-alg")) {
romane028ef92023-02-24 16:33:08 +01004616 if (nc_server_config_mac_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004617 goto error;
4618 }
roman2eab4742023-06-06 10:00:26 +02004619 } else if (!strcmp(name, "endpoint-client-auth")) {
roman0bbc19c2023-05-26 09:59:09 +02004620 if (nc_server_config_endpoint_client_auth(node, op)) {
4621 goto error;
4622 }
roman2eab4742023-06-06 10:00:26 +02004623 } else if (!strcmp(name, "tls")) {
roman3f9b65c2023-06-05 14:26:58 +02004624 if (nc_server_config_tls(node, op)) {
4625 goto error;
4626 }
4627 } else if (!strcmp(name, "cert-data")) {
4628 if (nc_server_config_cert_data(node, op)) {
4629 goto error;
4630 }
4631 } else if (!strcmp(name, "asymmetric-key")) {
4632 if (nc_server_config_asymmetric_key(node, op)) {
4633 goto error;
4634 }
4635 } else if (!strcmp(name, "certificate")) {
4636 if (nc_server_config_certificate(node, op)) {
4637 goto error;
4638 }
4639 } else if (!strcmp(name, "cert-to-name")) {
4640 if (nc_server_config_cert_to_name(node, op)) {
4641 goto error;
4642 }
4643 } else if (!strcmp(name, "fingerprint")) {
4644 if (nc_server_config_fingerprint(node, op)) {
4645 goto error;
4646 }
roman12644fe2023-06-08 11:06:42 +02004647 } else if (!strcmp(name, "tls-version")) {
4648 if (nc_server_config_tls_version(node, op)) {
4649 goto error;
4650 }
4651 } else if (!strcmp(name, "cipher-suite")) {
4652 if (nc_server_config_cipher_suite(node, op)) {
4653 goto error;
4654 }
romanfaecc582023-06-15 16:13:31 +02004655 } else if (!strcmp(name, "crl-url")) {
4656 if (nc_server_config_crl_url(node, op)) {
4657 goto error;
4658 }
4659 } else if (!strcmp(name, "crl-path")) {
4660 if (nc_server_config_crl_path(node, op)) {
4661 goto error;
4662 }
4663 } else if (!strcmp(name, "crl-cert-ext")) {
4664 if (nc_server_config_crl_cert_ext(node, op)) {
4665 goto error;
4666 }
roman3f9b65c2023-06-05 14:26:58 +02004667 }
roman2eab4742023-06-06 10:00:26 +02004668#endif /* NC_ENABLED_SSH_TLS */
roman5cbb6532023-06-22 12:53:17 +02004669 else if (!strcmp(name, "netconf-client")) {
4670 if (nc_server_config_netconf_client(node, op)) {
4671 goto error;
4672 }
4673 }
4674#ifdef NC_ENABLED_SSH_TLS
4675 else if (!strcmp(name, "remote-address")) {
4676 if (nc_server_config_remote_address(node, op)) {
4677 goto error;
4678 }
4679 } else if (!strcmp(name, "remote-port")) {
4680 if (nc_server_config_remote_port(node, op)) {
4681 goto error;
4682 }
4683 }
4684#endif /* NC_ENABLED_SSH_TLS */
romanb6f44032023-06-30 15:07:56 +02004685 else if (!strcmp(name, "persistent")) {
4686 if (nc_server_config_persistent(node, op)) {
4687 goto error;
4688 }
4689 } else if (!strcmp(name, "periodic")) {
4690 if (nc_server_config_periodic(node, op)) {
4691 goto error;
4692 }
4693 } else if (!strcmp(name, "period")) {
4694 if (nc_server_config_period(node, op)) {
4695 goto error;
4696 }
4697 } else if (!strcmp(name, "anchor-time")) {
4698 if (nc_server_config_anchor_time(node, op)) {
4699 goto error;
4700 }
4701 } else if (!strcmp(name, "reconnect-strategy")) {
4702 if (nc_server_config_reconnect_strategy(node, op)) {
4703 goto error;
4704 }
4705 } else if (!strcmp(name, "start-with")) {
4706 if (nc_server_config_start_with(node, op)) {
4707 goto error;
4708 }
4709 } else if (!strcmp(name, "max-wait")) {
4710 if (nc_server_config_max_wait(node, op)) {
4711 goto error;
4712 }
4713 } else if (!strcmp(name, "max-attempts")) {
4714 if (nc_server_config_max_attempts(node, op)) {
4715 goto error;
4716 }
4717 }
romanc1d2b092023-02-02 08:58:27 +01004718
4719 return 0;
4720
4721error:
romanb6f44032023-06-30 15:07:56 +02004722 ERR(NULL, "Configuring node \"%s\" failed.", LYD_NAME(node));
romanc1d2b092023-02-02 08:58:27 +01004723 return 1;
4724}
4725
4726int
roman0bbc19c2023-05-26 09:59:09 +02004727nc_server_config_parse_tree(const struct lyd_node *node, NC_OPERATION parent_op, NC_MODULE module)
romanc1d2b092023-02-02 08:58:27 +01004728{
4729 struct lyd_node *child;
4730 struct lyd_meta *m;
romanf9906b42023-05-22 14:04:29 +02004731 NC_OPERATION current_op = NC_OP_UNKNOWN;
romanf02273a2023-05-25 09:44:11 +02004732 int ret;
romanc1d2b092023-02-02 08:58:27 +01004733
4734 assert(node);
4735
romanf9906b42023-05-22 14:04:29 +02004736 /* get current op if there is any */
4737 if ((m = lyd_find_meta(node->meta, NULL, "yang:operation"))) {
4738 if (!strcmp(lyd_get_meta_value(m), "create")) {
4739 current_op = NC_OP_CREATE;
4740 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
4741 current_op = NC_OP_DELETE;
4742 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
4743 current_op = NC_OP_REPLACE;
4744 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
4745 current_op = NC_OP_NONE;
romanc1d2b092023-02-02 08:58:27 +01004746 }
4747 }
4748
4749 /* node has no op, inherit from the parent */
romanf9906b42023-05-22 14:04:29 +02004750 if (!current_op) {
4751 if (!parent_op) {
4752 ERR(NULL, "Unknown operation for node \"%s\".", LYD_NAME(node));
4753 return 1;
4754 }
4755
romanc1d2b092023-02-02 08:58:27 +01004756 current_op = parent_op;
4757 }
4758
4759 switch (current_op) {
4760 case NC_OP_NONE:
4761 break;
4762 case NC_OP_CREATE:
4763 case NC_OP_DELETE:
4764 case NC_OP_REPLACE:
roman2eab4742023-06-06 10:00:26 +02004765#ifdef NC_ENABLED_SSH_TLS
4766 if (module == NC_MODULE_KEYSTORE) {
romanf02273a2023-05-25 09:44:11 +02004767 ret = nc_server_config_parse_keystore(node, current_op);
roman2eab4742023-06-06 10:00:26 +02004768 } else if (module == NC_MODULE_TRUSTSTORE) {
romanf02273a2023-05-25 09:44:11 +02004769 ret = nc_server_config_parse_truststore(node, current_op);
roman2eab4742023-06-06 10:00:26 +02004770 } else
4771#endif /* NC_ENABLED_SSH_TLS */
4772 {
4773 ret = nc_server_config_parse_netconf_server(node, current_op);
romanf02273a2023-05-25 09:44:11 +02004774 }
4775 if (ret) {
4776 return ret;
romanc1d2b092023-02-02 08:58:27 +01004777 }
4778 break;
4779 default:
4780 break;
4781 }
4782
4783 if (current_op != NC_OP_DELETE) {
4784 LY_LIST_FOR(lyd_child(node), child) {
roman0bbc19c2023-05-26 09:59:09 +02004785 if (nc_server_config_parse_tree(child, current_op, module)) {
romanc1d2b092023-02-02 08:58:27 +01004786 return 1;
4787 }
4788 }
4789 }
4790 return 0;
4791}
4792
romanc1d2b092023-02-02 08:58:27 +01004793API int
4794nc_server_config_load_modules(struct ly_ctx **ctx)
4795{
4796 int i, new_ctx = 0;
4797
4798 if (!*ctx) {
4799 if (ly_ctx_new(NC_SERVER_SEARCH_DIR, 0, ctx)) {
4800 ERR(NULL, "Couldn't create new libyang context.\n");
4801 goto error;
4802 }
4803 new_ctx = 1;
4804 }
4805
4806 /* all features */
4807 const char *ietf_nectonf_server[] = {"ssh-listen", "tls-listen", "ssh-call-home", "tls-call-home", "central-netconf-server-supported", NULL};
4808 /* all features */
4809 const char *ietf_x509_cert_to_name[] = {NULL};
roman7fdc84d2023-06-06 13:14:53 +02004810 /* no private-key-encryption, csr-generation, p10-csr-format, certificate-expiration-notification,
roman3c6c7e62023-09-14 10:19:19 +02004811 * encrypted-passwords, hidden-symmetric-keys, encrypted-symmetric-keys, hidden-private-keys, encrypted-private-keys,
4812 * one-symmetric-key-format, one-asymmetric-key-format, symmetrically-encrypted-value-format,
4813 * asymmetrically-encrypted-value-format, cms-enveloped-data-format, cms-encrypted-data-format,
4814 * cleartext-symmetric-keys
roman7fdc84d2023-06-06 13:14:53 +02004815 */
roman3c6c7e62023-09-14 10:19:19 +02004816 const char *ietf_crypto_types[] = {"cleartext-passwords", "cleartext-private-keys", NULL};
romanc1d2b092023-02-02 08:58:27 +01004817 /* all features */
4818 const char *ietf_tcp_common[] = {"keepalives-supported", NULL};
roman7fdc84d2023-06-06 13:14:53 +02004819 /* all features */
4820 const char *ietf_tcp_server[] = {"tcp-server-keepalives", NULL};
roman3c6c7e62023-09-14 10:19:19 +02004821 /* no proxy-connect, socks5-gss-api, socks5-username-password */
4822 const char *ietf_tcp_client[] = {"local-binding-supported", "tcp-client-keepalives", NULL};
4823 /* no ssh-x509-certs, public-key-generation */
4824 const char *ietf_ssh_common[] = {"transport-params", NULL};
roman7fdc84d2023-06-06 13:14:53 +02004825 /* no ssh-server-keepalives and local-user-auth-hostbased */
4826 const char *ietf_ssh_server[] = {"local-users-supported", "local-user-auth-publickey", "local-user-auth-password", "local-user-auth-none", NULL};
romanc1d2b092023-02-02 08:58:27 +01004827 /* all features */
4828 const char *iana_ssh_encryption_algs[] = {NULL};
4829 /* all features */
4830 const char *iana_ssh_key_exchange_algs[] = {NULL};
4831 /* all features */
4832 const char *iana_ssh_mac_algs[] = {NULL};
4833 /* all features */
4834 const char *iana_ssh_public_key_algs[] = {NULL};
romanc1d2b092023-02-02 08:58:27 +01004835 /* all features */
roman7fdc84d2023-06-06 13:14:53 +02004836 const char *iana_crypt_hash[] = {"crypt-hash-md5", "crypt-hash-sha-256", "crypt-hash-sha-512", NULL};
4837 /* no symmetric-keys */
4838 const char *ietf_keystore[] = {"central-keystore-supported", "inline-definitions-supported", "asymmetric-keys", NULL};
4839 /* all features */
4840 const char *ietf_truststore[] = {"central-truststore-supported", "inline-definitions-supported", "certificates", "public-keys", NULL};
roman3c6c7e62023-09-14 10:19:19 +02004841 /* no public-key-generation */
4842 const char *ietf_tls_common[] = {"tls10", "tls11", "tls12", "tls13", "hello-params", NULL};
4843 /* no tls-server-keepalives, server-ident-raw-public-key, server-ident-tls12-psk, server-ident-tls13-epsk,
4844 * client-auth-raw-public-key, client-auth-tls12-psk, client-auth-tls13-epsk */
4845 const char *ietf_tls_server[] = {"server-ident-x509-cert", "client-auth-supported", "client-auth-x509-cert", NULL};
roman7fdc84d2023-06-06 13:14:53 +02004846 /* all features */
roman12644fe2023-06-08 11:06:42 +02004847 const char *iana_tls_cipher_suite_algs[] = {NULL};
romanc1d2b092023-02-02 08:58:27 +01004848 /* all features */
4849 const char *libnetconf2_netconf_server[] = {NULL};
4850
4851 const char *module_names[] = {
roman7fdc84d2023-06-06 13:14:53 +02004852 "ietf-netconf-server", "ietf-x509-cert-to-name", "ietf-crypto-types", "ietf-tcp-common", "ietf-tcp-server",
4853 "ietf-tcp-client", "ietf-ssh-common", "ietf-ssh-server", "iana-ssh-encryption-algs",
4854 "iana-ssh-key-exchange-algs", "iana-ssh-mac-algs", "iana-ssh-public-key-algs", "iana-crypt-hash",
roman12644fe2023-06-08 11:06:42 +02004855 "ietf-keystore", "ietf-truststore", "ietf-tls-common", "ietf-tls-server", "iana-tls-cipher-suite-algs",
4856 "libnetconf2-netconf-server", NULL
romanc1d2b092023-02-02 08:58:27 +01004857 };
4858
4859 const char **module_features[] = {
roman7fdc84d2023-06-06 13:14:53 +02004860 ietf_nectonf_server, ietf_x509_cert_to_name, ietf_crypto_types, ietf_tcp_common,
4861 ietf_tcp_server, ietf_tcp_client, ietf_ssh_common, ietf_ssh_server, iana_ssh_encryption_algs,
4862 iana_ssh_key_exchange_algs, iana_ssh_mac_algs, iana_ssh_public_key_algs, iana_crypt_hash,
roman12644fe2023-06-08 11:06:42 +02004863 ietf_keystore, ietf_truststore, ietf_tls_common, ietf_tls_server, iana_tls_cipher_suite_algs,
4864 libnetconf2_netconf_server, NULL
romanc1d2b092023-02-02 08:58:27 +01004865 };
4866
4867 for (i = 0; module_names[i] != NULL; i++) {
4868 if (!ly_ctx_load_module(*ctx, module_names[i], NULL, module_features[i])) {
4869 ERR(NULL, "Loading module \"%s\" failed.\n", module_names[i]);
4870 goto error;
4871 }
4872 }
4873
4874 return 0;
4875
4876error:
4877 if (new_ctx) {
4878 ly_ctx_destroy(*ctx);
4879 *ctx = NULL;
4880 }
4881 return 1;
4882}
4883
romanf9906b42023-05-22 14:04:29 +02004884static int
4885nc_server_config_fill_nectonf_server(const struct lyd_node *data, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01004886{
4887 int ret = 0;
romanc9b62d62023-09-14 10:19:50 +02004888 uint32_t prev_lo;
romanc1d2b092023-02-02 08:58:27 +01004889 struct lyd_node *tree;
romand57b3722023-04-05 11:26:25 +02004890
romanc9b62d62023-09-14 10:19:50 +02004891 /* silently search for ietf-netconf-server, it may not be present */
4892 prev_lo = ly_log_options(0);
4893
romanc1d2b092023-02-02 08:58:27 +01004894 ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &tree);
romanc9b62d62023-09-14 10:19:50 +02004895 if (ret || (tree->flags & LYD_DEFAULT)) {
4896 /* not found */
4897 ret = 0;
romanc1d2b092023-02-02 08:58:27 +01004898 goto cleanup;
4899 }
4900
roman0bbc19c2023-05-26 09:59:09 +02004901 if (nc_server_config_parse_tree(tree, op, NC_MODULE_NETCONF_SERVER)) {
4902 ret = 1;
4903 goto cleanup;
4904 }
4905
roman2eab4742023-06-06 10:00:26 +02004906#ifdef NC_ENABLED_SSH_TLS
4907 /* backward check of client auth reference */
roman0bbc19c2023-05-26 09:59:09 +02004908 if (nc_server_config_fill_endpt_client_auth()) {
romanf9906b42023-05-22 14:04:29 +02004909 ret = 1;
4910 goto cleanup;
4911 }
roman2eab4742023-06-06 10:00:26 +02004912#endif /* NC_ENABLED_SSH_TLS */
romanf9906b42023-05-22 14:04:29 +02004913
4914cleanup:
romanc9b62d62023-09-14 10:19:50 +02004915 /* reset the logging options back to what they were */
4916 ly_log_options(prev_lo);
romanf9906b42023-05-22 14:04:29 +02004917 return ret;
4918}
4919
4920API int
romanf6f37a52023-05-25 14:27:51 +02004921nc_server_config_setup_diff(const struct lyd_node *data)
romanf9906b42023-05-22 14:04:29 +02004922{
4923 int ret = 0;
4924
romanc9b62d62023-09-14 10:19:50 +02004925 NC_CHECK_ARG_RET(NULL, data, 1);
4926
romanf9906b42023-05-22 14:04:29 +02004927 /* LOCK */
4928 pthread_rwlock_wrlock(&server_opts.config_lock);
4929
roman2eab4742023-06-06 10:00:26 +02004930#ifdef NC_ENABLED_SSH_TLS
romanf9906b42023-05-22 14:04:29 +02004931 /* configure keystore */
romanf02273a2023-05-25 09:44:11 +02004932 ret = nc_server_config_fill_keystore(data, NC_OP_UNKNOWN);
romanf9906b42023-05-22 14:04:29 +02004933 if (ret) {
4934 ERR(NULL, "Filling keystore failed.");
4935 goto cleanup;
4936 }
4937
4938 /* configure truststore */
romanf02273a2023-05-25 09:44:11 +02004939 ret = nc_server_config_fill_truststore(data, NC_OP_UNKNOWN);
romanf9906b42023-05-22 14:04:29 +02004940 if (ret) {
4941 ERR(NULL, "Filling truststore failed.");
4942 goto cleanup;
4943 }
roman2eab4742023-06-06 10:00:26 +02004944#endif /* NC_ENABLED_SSH_TLS */
romanf9906b42023-05-22 14:04:29 +02004945
4946 /* configure netconf-server */
4947 ret = nc_server_config_fill_nectonf_server(data, NC_OP_UNKNOWN);
4948 if (ret) {
4949 ERR(NULL, "Filling netconf-server failed.");
4950 goto cleanup;
4951 }
4952
4953cleanup:
4954 /* UNLOCK */
4955 pthread_rwlock_unlock(&server_opts.config_lock);
4956 return ret;
4957}
4958
4959API int
romanf6f37a52023-05-25 14:27:51 +02004960nc_server_config_setup_data(const struct lyd_node *data)
romanf9906b42023-05-22 14:04:29 +02004961{
4962 int ret = 0;
4963 struct lyd_node *tree, *iter, *root;
4964
romanc9b62d62023-09-14 10:19:50 +02004965 NC_CHECK_ARG_RET(NULL, data, 1);
4966
romanf9906b42023-05-22 14:04:29 +02004967 /* LOCK */
4968 pthread_rwlock_wrlock(&server_opts.config_lock);
4969
4970 /* find the netconf-server node */
4971 ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &root);
4972 if (ret) {
4973 ERR(NULL, "Unable to find the netconf-server container in the YANG data.");
4974 goto cleanup;
4975 }
4976
4977 /* iterate through all the nodes and make sure there is no operation attribute */
4978 LY_LIST_FOR(root, tree) {
4979 LYD_TREE_DFS_BEGIN(tree, iter) {
4980 if (lyd_find_meta(iter->meta, NULL, "yang:operation")) {
4981 ERR(NULL, "Unexpected operation attribute in the YANG data.");
romanc1d2b092023-02-02 08:58:27 +01004982 ret = 1;
4983 goto cleanup;
4984 }
romanf9906b42023-05-22 14:04:29 +02004985 LYD_TREE_DFS_END(tree, iter);
romanc1d2b092023-02-02 08:58:27 +01004986 }
4987 }
4988
romanf9906b42023-05-22 14:04:29 +02004989 /* delete the current configuration */
romanf02273a2023-05-25 09:44:11 +02004990 nc_server_config_listen(NULL, NC_OP_DELETE);
roman4cb8bb12023-06-29 09:16:46 +02004991 nc_server_config_ch(NULL, NC_OP_DELETE);
roman2eab4742023-06-06 10:00:26 +02004992#ifdef NC_ENABLED_SSH_TLS
romanf02273a2023-05-25 09:44:11 +02004993 nc_server_config_ks_keystore(NULL, NC_OP_DELETE);
4994 nc_server_config_ts_truststore(NULL, NC_OP_DELETE);
romanf9906b42023-05-22 14:04:29 +02004995
4996 /* configure keystore */
romanf02273a2023-05-25 09:44:11 +02004997 ret = nc_server_config_fill_keystore(data, NC_OP_CREATE);
romanf9906b42023-05-22 14:04:29 +02004998 if (ret) {
4999 ERR(NULL, "Filling keystore failed.");
5000 goto cleanup;
5001 }
5002
5003 /* configure truststore */
romanf02273a2023-05-25 09:44:11 +02005004 ret = nc_server_config_fill_truststore(data, NC_OP_CREATE);
romanf9906b42023-05-22 14:04:29 +02005005 if (ret) {
5006 ERR(NULL, "Filling truststore failed.");
5007 goto cleanup;
5008 }
roman2eab4742023-06-06 10:00:26 +02005009#endif /* NC_ENABLED_SSH_TLS */
romanf9906b42023-05-22 14:04:29 +02005010
5011 /* configure netconf-server */
5012 ret = nc_server_config_fill_nectonf_server(data, NC_OP_CREATE);
5013 if (ret) {
5014 ERR(NULL, "Filling netconf-server failed.");
romanc1d2b092023-02-02 08:58:27 +01005015 goto cleanup;
5016 }
5017
5018cleanup:
5019 /* UNLOCK */
5020 pthread_rwlock_unlock(&server_opts.config_lock);
5021 return ret;
5022}
roman3f9b65c2023-06-05 14:26:58 +02005023
5024API int
5025nc_server_config_setup_path(const struct ly_ctx *ctx, const char *path)
5026{
5027 struct lyd_node *tree = NULL;
5028 int ret = 0;
5029
5030 NC_CHECK_ARG_RET(NULL, path, 1);
5031
5032 ret = lyd_parse_data_path(ctx, path, LYD_UNKNOWN, LYD_PARSE_NO_STATE | LYD_PARSE_STRICT, LYD_VALIDATE_NO_STATE, &tree);
5033 if (ret) {
5034 goto cleanup;
5035 }
5036
5037 ret = nc_server_config_setup_data(tree);
5038 if (ret) {
5039 goto cleanup;
5040 }
5041
5042cleanup:
5043 lyd_free_all(tree);
5044 return ret;
5045}