blob: b6f693e0c5acc5156b423c21e6868c098bbe3073 [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
roman874fed12023-05-25 10:20:01 +0200753nc_server_config_del_hostkey(struct nc_server_ssh_opts *opts, struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +0100754{
roman874fed12023-05-25 10:20:01 +0200755 assert(hostkey->store == NC_STORE_LOCAL || hostkey->store == NC_STORE_KEYSTORE);
romanc1d2b092023-02-02 08:58:27 +0100756
roman874fed12023-05-25 10:20:01 +0200757 if (hostkey->store == NC_STORE_LOCAL) {
758 nc_server_config_del_public_key(hostkey);
759 nc_server_config_del_private_key(hostkey);
romanc1d2b092023-02-02 08:58:27 +0100760 }
761
roman874fed12023-05-25 10:20:01 +0200762 nc_server_config_del_hostkey_name(hostkey);
romanc1d2b092023-02-02 08:58:27 +0100763 opts->hostkey_count--;
764 if (!opts->hostkey_count) {
765 free(opts->hostkeys);
766 opts->hostkeys = NULL;
roman33981232023-07-08 11:55:13 +0200767 } else if (hostkey == &opts->hostkeys[opts->hostkey_count + 1]) {
768 memcpy(hostkey, &opts->hostkeys[opts->hostkey_count], sizeof *opts->hostkeys);
romanc1d2b092023-02-02 08:58:27 +0100769 }
770}
771
772static void
roman874fed12023-05-25 10:20:01 +0200773nc_server_config_del_auth_client_pubkey(struct nc_client_auth *auth_client, struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100774{
roman874fed12023-05-25 10:20:01 +0200775 nc_server_config_del_auth_client_pubkey_name(pubkey);
776 nc_server_config_del_auth_client_pubkey_pub_base64(pubkey);
romanc1d2b092023-02-02 08:58:27 +0100777
778 auth_client->pubkey_count--;
779 if (!auth_client->pubkey_count) {
780 free(auth_client->pubkeys);
781 auth_client->pubkeys = NULL;
roman33981232023-07-08 11:55:13 +0200782 } else if (pubkey == &auth_client->pubkeys[auth_client->pubkey_count + 1]) {
783 memcpy(pubkey, &auth_client->pubkeys[auth_client->pubkey_count], sizeof *auth_client->pubkeys);
romanc1d2b092023-02-02 08:58:27 +0100784 }
785}
786
787static void
roman874fed12023-05-25 10:20:01 +0200788nc_server_config_del_auth_client(struct nc_server_ssh_opts *opts, struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100789{
790 uint16_t i, pubkey_count;
791
roman4cb8bb12023-06-29 09:16:46 +0200792 free(auth_client->username);
793 auth_client->username = NULL;
794
roman874fed12023-05-25 10:20:01 +0200795 if (auth_client->store == NC_STORE_LOCAL) {
romanc1d2b092023-02-02 08:58:27 +0100796 pubkey_count = auth_client->pubkey_count;
797 for (i = 0; i < pubkey_count; i++) {
roman874fed12023-05-25 10:20:01 +0200798 nc_server_config_del_auth_client_pubkey(auth_client, &auth_client->pubkeys[i]);
romanc1d2b092023-02-02 08:58:27 +0100799 }
romanc1d2b092023-02-02 08:58:27 +0100800 }
801
roman874fed12023-05-25 10:20:01 +0200802 nc_server_config_del_auth_client_password(auth_client);
803 nc_server_config_del_auth_client_pam_name(auth_client);
804 nc_server_config_del_auth_client_pam_dir(auth_client);
romanc1d2b092023-02-02 08:58:27 +0100805
806 opts->client_count--;
807 if (!opts->client_count) {
808 free(opts->auth_clients);
809 opts->auth_clients = NULL;
roman33981232023-07-08 11:55:13 +0200810 } else if (auth_client == &opts->auth_clients[opts->client_count + 1]) {
811 memcpy(auth_client, &opts->auth_clients[opts->client_count], sizeof *opts->auth_clients);
romanc1d2b092023-02-02 08:58:27 +0100812 }
813}
814
815static void
roman874fed12023-05-25 10:20:01 +0200816nc_server_config_del_ssh(struct nc_bind *bind, struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100817{
818 uint16_t i, hostkey_count, client_count;
819
roman874fed12023-05-25 10:20:01 +0200820 nc_server_config_del_local_address(bind);
romanc1d2b092023-02-02 08:58:27 +0100821 if (bind->sock > -1) {
822 close(bind->sock);
823 }
824
825 /* store in variable because it gets decremented in the function call */
826 hostkey_count = opts->hostkey_count;
827 for (i = 0; i < hostkey_count; i++) {
roman874fed12023-05-25 10:20:01 +0200828 nc_server_config_del_hostkey(opts, &opts->hostkeys[i]);
romanc1d2b092023-02-02 08:58:27 +0100829 }
830
831 client_count = opts->client_count;
832 for (i = 0; i < client_count; i++) {
roman874fed12023-05-25 10:20:01 +0200833 nc_server_config_del_auth_client(opts, &opts->auth_clients[i]);
romanc1d2b092023-02-02 08:58:27 +0100834 }
835
roman874fed12023-05-25 10:20:01 +0200836 nc_server_config_del_hostkey_algs(opts);
837 nc_server_config_del_kex_algs(opts);
838 nc_server_config_del_encryption_algs(opts);
839 nc_server_config_del_mac_algs(opts);
romanc1d2b092023-02-02 08:58:27 +0100840
841 free(opts);
842 opts = NULL;
843}
844
845void
roman874fed12023-05-25 10:20:01 +0200846nc_server_config_del_endpt_ssh(struct nc_endpt *endpt, struct nc_bind *bind)
romanc1d2b092023-02-02 08:58:27 +0100847{
roman874fed12023-05-25 10:20:01 +0200848 nc_server_config_del_endpt_name(endpt);
roman0bbc19c2023-05-26 09:59:09 +0200849 nc_server_config_del_endpt_reference(endpt);
roman874fed12023-05-25 10:20:01 +0200850 nc_server_config_del_ssh(bind, endpt->opts.ssh);
romanc1d2b092023-02-02 08:58:27 +0100851
852 server_opts.endpt_count--;
853 if (!server_opts.endpt_count) {
854 free(server_opts.endpts);
855 free(server_opts.binds);
856 server_opts.endpts = NULL;
857 server_opts.binds = NULL;
roman33981232023-07-08 11:55:13 +0200858 } else if (endpt == &server_opts.endpts[server_opts.endpt_count + 1]) {
859 memcpy(endpt, &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
860 memcpy(bind, &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
romanc1d2b092023-02-02 08:58:27 +0100861 }
862}
863
roman2eab4742023-06-06 10:00:26 +0200864#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +0200865
roman45cec4e2023-02-17 10:21:39 +0100866void
roman874fed12023-05-25 10:20:01 +0200867nc_server_config_del_unix_socket(struct nc_bind *bind, struct nc_server_unix_opts *opts)
roman83683fb2023-02-24 09:15:23 +0100868{
869 if (bind->sock > -1) {
870 close(bind->sock);
871 }
872
873 free(bind->address);
874 free(opts->address);
875
876 free(opts);
roman83683fb2023-02-24 09:15:23 +0100877}
878
879void
roman874fed12023-05-25 10:20:01 +0200880nc_server_config_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *bind)
roman83683fb2023-02-24 09:15:23 +0100881{
roman874fed12023-05-25 10:20:01 +0200882 nc_server_config_del_endpt_name(endpt);
883 nc_server_config_del_unix_socket(bind, endpt->opts.unixsock);
roman83683fb2023-02-24 09:15:23 +0100884
885 server_opts.endpt_count--;
886 if (!server_opts.endpt_count) {
887 free(server_opts.endpts);
888 free(server_opts.binds);
889 server_opts.endpts = NULL;
890 server_opts.binds = NULL;
roman33981232023-07-08 11:55:13 +0200891 } else if (endpt == &server_opts.endpts[server_opts.endpt_count + 1]) {
892 memcpy(endpt, &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
893 memcpy(bind, &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
roman83683fb2023-02-24 09:15:23 +0100894 }
895}
896
roman2eab4742023-06-06 10:00:26 +0200897#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +0200898
899static void
romanfaecc582023-06-15 16:13:31 +0200900nc_server_config_del_url(struct nc_server_tls_opts *opts)
901{
902 free(opts->crl_url);
903 opts->crl_url = NULL;
904}
905
906static void
907nc_server_config_del_path(struct nc_server_tls_opts *opts)
908{
909 free(opts->crl_path);
910 opts->crl_path = NULL;
911}
912
913static void
roman12644fe2023-06-08 11:06:42 +0200914nc_server_config_tls_del_ciphers(struct nc_server_tls_opts *opts)
915{
916 free(opts->ciphers);
917 opts->ciphers = NULL;
918}
919
920static void
roman3f9b65c2023-06-05 14:26:58 +0200921nc_server_config_tls_del_public_key(struct nc_server_tls_opts *opts)
922{
923 free(opts->pubkey_data);
924 opts->pubkey_data = NULL;
925}
926
927static void
928nc_server_config_tls_del_cleartext_private_key(struct nc_server_tls_opts *opts)
929{
930 free(opts->privkey_data);
931 opts->privkey_data = NULL;
932}
933
934static void
935nc_server_config_tls_del_cert_data(struct nc_server_tls_opts *opts)
936{
937 free(opts->cert_data);
938 opts->cert_data = NULL;
939}
940
941static void
942nc_server_config_tls_del_cert_data_certificate(struct nc_certificate *cert)
943{
944 free(cert->data);
945 cert->data = NULL;
946}
947
948static void
949nc_server_config_del_fingerprint(struct nc_ctn *ctn)
950{
951 free(ctn->fingerprint);
952 ctn->fingerprint = NULL;
953}
954
955static void
956nc_server_config_del_cert(struct nc_cert_grouping *certs, struct nc_certificate *cert)
957{
958 free(cert->name);
959 cert->name = NULL;
960
961 free(cert->data);
962 cert->data = NULL;
963
964 certs->cert_count--;
965 if (!certs->cert_count) {
966 free(certs->certs);
967 certs->certs = NULL;
roman33981232023-07-08 11:55:13 +0200968 } else if (cert == &certs->certs[certs->cert_count + 1]) {
969 memcpy(cert, &certs->certs[certs->cert_count], sizeof *certs->certs);
roman3f9b65c2023-06-05 14:26:58 +0200970 }
971}
972
973static void
974nc_server_config_tls_del_certs(struct nc_cert_grouping *ca)
975{
976 uint16_t i, cert_count;
977
978 if (ca->store == NC_STORE_LOCAL) {
979 cert_count = ca->cert_count;
980 for (i = 0; i < cert_count; i++) {
981 nc_server_config_del_cert(ca, &ca->certs[i]);
982 }
983 }
984}
985
986static void
987nc_server_config_del_ctn(struct nc_server_tls_opts *opts, struct nc_ctn *ctn)
988{
989 struct nc_ctn *iter;
990
991 free(ctn->fingerprint);
992 ctn->fingerprint = NULL;
993
994 free(ctn->name);
995 ctn->name = NULL;
996
997 if (opts->ctn == ctn) {
998 /* it's the first in the list */
999 opts->ctn = ctn->next;
1000 free(ctn);
1001 return;
1002 }
1003
1004 iter = opts->ctn;
1005 while (iter) {
1006 if (iter->next == ctn) {
1007 /* found the ctn */
1008 break;
1009 }
1010 iter = iter->next;
1011 }
1012
1013 iter->next = ctn->next;
1014 free(ctn);
1015}
1016
1017static void
roman12644fe2023-06-08 11:06:42 +02001018nc_server_config_tls_del_ctns(struct nc_server_tls_opts *opts)
roman3f9b65c2023-06-05 14:26:58 +02001019{
1020 struct nc_ctn *cur, *next;
1021
1022 cur = opts->ctn;
1023 while (cur) {
1024 next = cur->next;
1025 free(cur->fingerprint);
1026 free(cur->name);
1027 free(cur);
1028 cur = next;
1029 }
1030 opts->ctn = NULL;
1031}
1032
1033static void
1034nc_server_config_del_tls(struct nc_bind *bind, struct nc_server_tls_opts *opts)
1035{
1036 nc_server_config_del_local_address(bind);
1037 if (bind->sock > -1) {
1038 close(bind->sock);
1039 }
1040
1041 if (opts->store == NC_STORE_LOCAL) {
1042 nc_server_config_tls_del_public_key(opts);
1043 nc_server_config_tls_del_cleartext_private_key(opts);
1044 nc_server_config_tls_del_cert_data(opts);
1045 }
1046
1047 nc_server_config_tls_del_certs(&opts->ca_certs);
1048 nc_server_config_tls_del_certs(&opts->ee_certs);
1049
romanfaecc582023-06-15 16:13:31 +02001050 nc_server_config_del_path(opts);
1051 nc_server_config_del_url(opts);
1052 X509_STORE_free(opts->crl_store);
1053 opts->crl_store = NULL;
1054
roman12644fe2023-06-08 11:06:42 +02001055 nc_server_config_tls_del_ctns(opts);
1056 nc_server_config_tls_del_ciphers(opts);
roman3f9b65c2023-06-05 14:26:58 +02001057
1058 free(opts);
1059}
1060
1061static void
1062nc_server_config_del_endpt_tls(struct nc_endpt *endpt, struct nc_bind *bind)
1063{
1064 nc_server_config_del_endpt_name(endpt);
roman2e797ef2023-06-19 10:47:49 +02001065 nc_server_config_del_endpt_reference(endpt);
roman3f9b65c2023-06-05 14:26:58 +02001066 nc_server_config_del_tls(bind, endpt->opts.tls);
1067
1068 server_opts.endpt_count--;
1069 if (!server_opts.endpt_count) {
1070 free(server_opts.endpts);
1071 free(server_opts.binds);
1072 server_opts.endpts = NULL;
1073 server_opts.binds = NULL;
roman33981232023-07-08 11:55:13 +02001074 } else if (endpt == &server_opts.endpts[server_opts.endpt_count + 1]) {
1075 memcpy(endpt, &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
1076 memcpy(bind, &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
roman3f9b65c2023-06-05 14:26:58 +02001077 }
1078}
1079
roman5cbb6532023-06-22 12:53:17 +02001080static void
1081nc_server_config_del_remote_address(struct nc_ch_endpt *ch_endpt)
1082{
1083 free(ch_endpt->address);
1084 ch_endpt->address = NULL;
1085}
1086
roman2eab4742023-06-06 10:00:26 +02001087#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +02001088
romanc1d2b092023-02-02 08:58:27 +01001089/* presence container */
1090int
romanf02273a2023-05-25 09:44:11 +02001091nc_server_config_listen(struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001092{
roman0bbc19c2023-05-26 09:59:09 +02001093 uint16_t i, endpt_count;
romanc1d2b092023-02-02 08:58:27 +01001094
romanf02273a2023-05-25 09:44:11 +02001095 (void) node;
1096
romanc1d2b092023-02-02 08:58:27 +01001097 assert(op == NC_OP_CREATE || op == NC_OP_DELETE);
1098
1099 if (op == NC_OP_DELETE) {
roman0bbc19c2023-05-26 09:59:09 +02001100 endpt_count = server_opts.endpt_count;
1101 for (i = 0; i < endpt_count; i++) {
roman456f92d2023-04-28 10:28:12 +02001102 switch (server_opts.endpts[i].ti) {
roman2eab4742023-06-06 10:00:26 +02001103#ifdef NC_ENABLED_SSH_TLS
roman456f92d2023-04-28 10:28:12 +02001104 case NC_TI_LIBSSH:
roman874fed12023-05-25 10:20:01 +02001105 nc_server_config_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]);
roman456f92d2023-04-28 10:28:12 +02001106 break;
roman456f92d2023-04-28 10:28:12 +02001107 case NC_TI_OPENSSL:
roman3f9b65c2023-06-05 14:26:58 +02001108 nc_server_config_del_endpt_tls(&server_opts.endpts[i], &server_opts.binds[i]);
roman456f92d2023-04-28 10:28:12 +02001109 break;
roman2eab4742023-06-06 10:00:26 +02001110#endif /* NC_ENABLED_SSH_TLS */
roman456f92d2023-04-28 10:28:12 +02001111 case NC_TI_UNIX:
roman874fed12023-05-25 10:20:01 +02001112 nc_server_config_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]);
roman456f92d2023-04-28 10:28:12 +02001113 break;
1114 case NC_TI_NONE:
1115 case NC_TI_FD:
1116 ERRINT;
1117 return 1;
roman83683fb2023-02-24 09:15:23 +01001118 }
romanc1d2b092023-02-02 08:58:27 +01001119 }
1120 }
1121
1122 return 0;
1123}
1124
roman5cbb6532023-06-22 12:53:17 +02001125#ifdef NC_ENABLED_SSH_TLS
1126
1127static void
1128nc_server_config_ch_del_ssh(struct nc_server_ssh_opts *opts)
1129{
1130 uint16_t i, hostkey_count, client_count;
1131
1132 /* store in variable because it gets decremented in the function call */
1133 hostkey_count = opts->hostkey_count;
1134 for (i = 0; i < hostkey_count; i++) {
1135 nc_server_config_del_hostkey(opts, &opts->hostkeys[i]);
1136 }
1137
1138 client_count = opts->client_count;
1139 for (i = 0; i < client_count; i++) {
1140 nc_server_config_del_auth_client(opts, &opts->auth_clients[i]);
1141 }
1142
1143 nc_server_config_del_hostkey_algs(opts);
1144 nc_server_config_del_kex_algs(opts);
1145 nc_server_config_del_encryption_algs(opts);
1146 nc_server_config_del_mac_algs(opts);
1147
1148 free(opts);
1149 opts = NULL;
1150}
1151
1152static void
romanb6f44032023-06-30 15:07:56 +02001153nc_server_config_ch_del_tls(struct nc_server_tls_opts *opts)
1154{
1155 if (opts->store == NC_STORE_LOCAL) {
1156 nc_server_config_tls_del_public_key(opts);
1157 nc_server_config_tls_del_cleartext_private_key(opts);
1158 nc_server_config_tls_del_cert_data(opts);
1159 }
1160
1161 nc_server_config_tls_del_certs(&opts->ca_certs);
1162 nc_server_config_tls_del_certs(&opts->ee_certs);
1163
1164 nc_server_config_del_path(opts);
1165 nc_server_config_del_url(opts);
1166 X509_STORE_free(opts->crl_store);
1167 opts->crl_store = NULL;
1168
1169 nc_server_config_tls_del_ctns(opts);
1170 nc_server_config_tls_del_ciphers(opts);
1171
1172 free(opts);
1173}
1174
1175static void
roman5cbb6532023-06-22 12:53:17 +02001176nc_server_config_ch_del_endpt_address(struct nc_ch_endpt *ch_endpt)
1177{
1178 free(ch_endpt->address);
1179 ch_endpt->address = NULL;
1180}
1181
1182#endif /* NC_ENABLED_SSH_TLS */
1183
1184static void
1185nc_server_config_ch_del_endpt(struct nc_ch_client *ch_client, struct nc_ch_endpt *ch_endpt)
1186{
1187 free(ch_endpt->name);
1188 ch_endpt->name = NULL;
1189
1190#ifdef NC_ENABLED_SSH_TLS
1191 nc_server_config_ch_del_endpt_address(ch_endpt);
1192 if (ch_endpt->sock_pending > -1) {
1193 close(ch_endpt->sock_pending);
1194 ch_endpt->sock_pending = -1;
1195 }
1196#endif /* NC_ENABLED_SSH_TLS */
1197
1198 switch (ch_endpt->ti) {
1199#ifdef NC_ENABLED_SSH_TLS
1200 case NC_TI_LIBSSH:
1201 nc_server_config_ch_del_ssh(ch_endpt->opts.ssh);
1202 break;
romanb6f44032023-06-30 15:07:56 +02001203 case NC_TI_OPENSSL:
1204 nc_server_config_ch_del_tls(ch_endpt->opts.tls);
1205 break;
roman5cbb6532023-06-22 12:53:17 +02001206#endif /* NC_ENABLED_SSH_TLS */
1207 default:
1208 ERRINT;
1209 break;
1210 }
1211
1212 ch_client->ch_endpt_count--;
1213 if (!ch_client->ch_endpt_count) {
1214 free(ch_client->ch_endpts);
1215 ch_client->ch_endpts = NULL;
1216 }
1217}
1218
1219static void
1220nc_server_config_ch_del_client(struct nc_ch_client *ch_client)
1221{
1222 uint16_t i, ch_endpt_count;
romanba93eac2023-07-18 14:36:48 +02001223 struct nc_ch_client client;
1224 pthread_t tid;
roman5cbb6532023-06-22 12:53:17 +02001225
romanba93eac2023-07-18 14:36:48 +02001226 /* WR LOCK */
roman5cbb6532023-06-22 12:53:17 +02001227 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1228
romanba93eac2023-07-18 14:36:48 +02001229 /* copy the client we want to delete into a local variable */
1230 memcpy(&client, ch_client, sizeof *ch_client);
1231 /* get his tid */
1232 tid = client.tid;
roman5cbb6532023-06-22 12:53:17 +02001233
romanba93eac2023-07-18 14:36:48 +02001234 /* delete the client */
roman5cbb6532023-06-22 12:53:17 +02001235 server_opts.ch_client_count--;
1236 if (!server_opts.ch_client_count) {
1237 free(server_opts.ch_clients);
1238 server_opts.ch_clients = NULL;
romanba93eac2023-07-18 14:36:48 +02001239 } else {
1240 memcpy(ch_client, &server_opts.ch_clients[server_opts.ch_client_count], sizeof *server_opts.ch_clients);
roman5cbb6532023-06-22 12:53:17 +02001241 }
1242
romanba93eac2023-07-18 14:36:48 +02001243 /* WR UNLOCK */
roman5cbb6532023-06-22 12:53:17 +02001244 pthread_rwlock_unlock(&server_opts.ch_client_lock);
romanba93eac2023-07-18 14:36:48 +02001245
1246 /* RD LOCK */
1247 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
1248
1249 if (client.thread_data->thread_running) {
1250 /* CH COND LOCK */
1251 pthread_mutex_lock(&client.thread_data->cond_lock);
1252 client.thread_data->thread_running = 0;
1253 pthread_cond_signal(&client.thread_data->cond);
1254 /* CH COND UNLOCK */
1255 pthread_mutex_unlock(&client.thread_data->cond_lock);
1256
1257 /* RD UNLOCK */
1258 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1259
1260 /* wait for the thread to terminate */
1261 pthread_join(tid, NULL);
1262 }
1263
1264 /* free its members */
1265 free(client.name);
1266
1267 ch_endpt_count = client.ch_endpt_count;
1268 for (i = 0; i < ch_endpt_count; i++) {
1269 nc_server_config_ch_del_endpt(&client, &client.ch_endpts[i]);
1270 }
roman5cbb6532023-06-22 12:53:17 +02001271}
1272
1273void
1274nc_server_config_ch(const struct lyd_node *node, NC_OPERATION op)
1275{
1276 uint16_t i, ch_client_count;
1277
1278 (void) node;
1279
1280 if (op == NC_OP_DELETE) {
1281 ch_client_count = server_opts.ch_client_count;
1282 for (i = 0; i < ch_client_count; i++) {
1283 nc_server_config_ch_del_client(&server_opts.ch_clients[i]);
1284 }
1285 }
1286}
1287
romanc1d2b092023-02-02 08:58:27 +01001288/* default leaf */
1289static int
romane028ef92023-02-24 16:33:08 +01001290nc_server_config_idle_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001291{
romanb6f44032023-06-30 15:07:56 +02001292 struct nc_ch_client *ch_client;
1293
romanc1d2b092023-02-02 08:58:27 +01001294 assert(!strcmp(LYD_NAME(node), "idle-timeout"));
1295
romanb6f44032023-06-30 15:07:56 +02001296 if (is_listen(node)) {
romanc1d2b092023-02-02 08:58:27 +01001297 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1298 server_opts.idle_timeout = strtoul(lyd_get_value(node), NULL, 10);
1299 } else {
1300 /* default value */
1301 server_opts.idle_timeout = 3600;
1302 }
romanb6f44032023-06-30 15:07:56 +02001303 } else {
1304 /* call-home idle timeout */
1305 if (nc_server_config_get_ch_client(node, &ch_client)) {
1306 return 1;
1307 }
1308
1309 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1310 ch_client->idle_timeout = strtoul(lyd_get_value(node), NULL, 10);
1311 } else if (op == NC_OP_DELETE) {
1312 ch_client->idle_timeout = 180;
1313 }
romanc1d2b092023-02-02 08:58:27 +01001314 }
1315
1316 return 0;
1317}
1318
1319static int
roman874fed12023-05-25 10:20:01 +02001320nc_server_config_create_bind(void)
romanc1d2b092023-02-02 08:58:27 +01001321{
1322 int ret = 0;
1323 void *tmp;
1324
1325 tmp = realloc(server_opts.binds, (server_opts.endpt_count + 1) * sizeof *server_opts.binds);
1326 if (!tmp) {
1327 ERRMEM;
1328 ret = 1;
1329 goto cleanup;
1330 }
1331 server_opts.binds = tmp;
1332 memset(&server_opts.binds[server_opts.endpt_count], 0, sizeof *server_opts.binds);
1333
1334 server_opts.binds[server_opts.endpt_count].sock = -1;
1335
1336cleanup:
1337 return ret;
1338}
1339
1340static int
roman874fed12023-05-25 10:20:01 +02001341nc_server_config_create_endpoint(const struct lyd_node *node)
romanc1d2b092023-02-02 08:58:27 +01001342{
roman874fed12023-05-25 10:20:01 +02001343 if (nc_server_config_create_bind()) {
romanf02273a2023-05-25 09:44:11 +02001344 return 1;
romanc1d2b092023-02-02 08:58:27 +01001345 }
romanc1d2b092023-02-02 08:58:27 +01001346
1347 node = lyd_child(node);
1348 assert(!strcmp(LYD_NAME(node), "name"));
1349
romanf02273a2023-05-25 09:44:11 +02001350 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 +01001351}
1352
roman5cbb6532023-06-22 12:53:17 +02001353static int
1354nc_server_config_ch_create_endpoint(const struct lyd_node *node, struct nc_ch_client *ch_client)
1355{
1356 node = lyd_child(node);
1357 assert(!strcmp(LYD_NAME(node), "name"));
1358
1359 return nc_server_config_realloc(lyd_get_value(node), (void **)&ch_client->ch_endpts, sizeof *ch_client->ch_endpts, &ch_client->ch_endpt_count);
1360}
1361
romanc1d2b092023-02-02 08:58:27 +01001362/* list */
1363static int
romane028ef92023-02-24 16:33:08 +01001364nc_server_config_endpoint(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001365{
1366 int ret = 0;
1367 struct nc_endpt *endpt;
1368 struct nc_bind *bind;
roman5cbb6532023-06-22 12:53:17 +02001369 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001370
1371 assert(!strcmp(LYD_NAME(node), "endpoint"));
1372
roman5cbb6532023-06-22 12:53:17 +02001373 if (is_listen(node)) {
1374 /* listen */
1375 if (op == NC_OP_CREATE) {
1376 ret = nc_server_config_create_endpoint(node);
1377 if (ret) {
1378 goto cleanup;
1379 }
1380 } else if (op == NC_OP_DELETE) {
1381 /* free all children */
1382 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
1383 ret = 1;
1384 goto cleanup;
1385 }
1386
1387 switch (endpt->ti) {
1388#ifdef NC_ENABLED_SSH_TLS
1389 case NC_TI_LIBSSH:
1390 nc_server_config_del_endpt_ssh(endpt, bind);
1391 break;
1392 case NC_TI_OPENSSL:
1393 nc_server_config_del_endpt_tls(endpt, bind);
1394 break;
1395#endif /* NC_ENABLED_SSH_TLS */
1396 case NC_TI_UNIX:
1397 nc_server_config_del_endpt_unix_socket(endpt, bind);
1398 break;
1399 case NC_TI_NONE:
1400 case NC_TI_FD:
1401 ERRINT;
1402 ret = 1;
1403 goto cleanup;
1404 }
romanc1d2b092023-02-02 08:58:27 +01001405 }
roman5cbb6532023-06-22 12:53:17 +02001406 } else if (is_ch(node)) {
romanba93eac2023-07-18 14:36:48 +02001407 /* LOCK */
1408 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanc1d2b092023-02-02 08:58:27 +01001409 ret = 1;
1410 goto cleanup;
1411 }
roman3f9b65c2023-06-05 14:26:58 +02001412
roman5cbb6532023-06-22 12:53:17 +02001413 if (op == NC_OP_CREATE) {
1414 ret = nc_server_config_ch_create_endpoint(node, ch_client);
1415 if (ret) {
1416 goto cleanup;
1417 }
1418
1419 /* init ch sock */
1420 ch_client->ch_endpts[ch_client->ch_endpt_count - 1].sock_pending = -1;
roman3f9b65c2023-06-05 14:26:58 +02001421 }
romanc1d2b092023-02-02 08:58:27 +01001422 }
1423
1424cleanup:
romanba93eac2023-07-18 14:36:48 +02001425 if (is_ch(node)) {
1426 /* UNLOCK */
1427 nc_ch_client_unlock(ch_client);
1428 }
romanc1d2b092023-02-02 08:58:27 +01001429 return ret;
1430}
1431
roman2eab4742023-06-06 10:00:26 +02001432#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +02001433
romanc1d2b092023-02-02 08:58:27 +01001434static int
roman874fed12023-05-25 10:20:01 +02001435nc_server_config_create_ssh(struct nc_endpt *endpt)
romanc1d2b092023-02-02 08:58:27 +01001436{
1437 endpt->ti = NC_TI_LIBSSH;
1438 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1439 if (!endpt->opts.ssh) {
1440 ERRMEM;
1441 return 1;
1442 }
1443
1444 return 0;
1445}
1446
roman5cbb6532023-06-22 12:53:17 +02001447static int
1448nc_server_config_ch_create_ssh(struct nc_ch_endpt *ch_endpt)
1449{
1450 ch_endpt->ti = NC_TI_LIBSSH;
1451 ch_endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1452 if (!ch_endpt->opts.ssh) {
1453 ERRMEM;
1454 return 1;
1455 }
1456
1457 return 0;
1458}
1459
romanc1d2b092023-02-02 08:58:27 +01001460/* NP container */
1461static int
romane028ef92023-02-24 16:33:08 +01001462nc_server_config_ssh(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001463{
1464 struct nc_endpt *endpt;
1465 struct nc_bind *bind;
roman5cbb6532023-06-22 12:53:17 +02001466 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02001467 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001468 int ret = 0;
1469
1470 assert(!strcmp(LYD_NAME(node), "ssh"));
1471
roman5cbb6532023-06-22 12:53:17 +02001472 if (is_listen(node)) {
1473 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
1474 ret = 1;
romanc1d2b092023-02-02 08:58:27 +01001475 goto cleanup;
1476 }
roman5cbb6532023-06-22 12:53:17 +02001477
1478 if (op == NC_OP_CREATE) {
1479 ret = nc_server_config_create_ssh(endpt);
1480 if (ret) {
1481 goto cleanup;
1482 }
1483 } else if (op == NC_OP_DELETE) {
1484 nc_server_config_del_ssh(bind, endpt->opts.ssh);
1485 }
1486 } else {
romanba93eac2023-07-18 14:36:48 +02001487 /* LOCK */
1488 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1489 ret = 1;
1490 goto cleanup;
1491 }
1492
roman4cb8bb12023-06-29 09:16:46 +02001493 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02001494 ret = 1;
1495 goto cleanup;
1496 }
1497
1498 if (op == NC_OP_CREATE) {
1499 ret = nc_server_config_ch_create_ssh(ch_endpt);
1500 if (ret) {
1501 goto cleanup;
1502 }
romanb6f44032023-06-30 15:07:56 +02001503 } else if (op == NC_OP_DELETE) {
1504 nc_server_config_ch_del_ssh(ch_endpt->opts.ssh);
roman5cbb6532023-06-22 12:53:17 +02001505 }
romanc1d2b092023-02-02 08:58:27 +01001506 }
1507
1508cleanup:
romanba93eac2023-07-18 14:36:48 +02001509 if (is_ch(node)) {
1510 /* UNLOCK */
1511 nc_ch_client_unlock(ch_client);
1512 }
romanc1d2b092023-02-02 08:58:27 +01001513 return ret;
1514}
1515
roman3f9b65c2023-06-05 14:26:58 +02001516static int
1517nc_server_config_create_tls(struct nc_endpt *endpt)
1518{
1519 endpt->ti = NC_TI_OPENSSL;
1520 endpt->opts.tls = calloc(1, sizeof *endpt->opts.tls);
1521 if (!endpt->opts.tls) {
1522 ERRMEM;
1523 return 1;
1524 }
1525
1526 return 0;
1527}
1528
1529static int
romanb6f44032023-06-30 15:07:56 +02001530nc_server_config_ch_create_tls(struct nc_ch_endpt *ch_endpt)
1531{
1532 ch_endpt->ti = NC_TI_OPENSSL;
1533 ch_endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1534 if (!ch_endpt->opts.tls) {
1535 ERRMEM;
1536 return 1;
1537 }
1538
1539 return 0;
1540}
1541
1542static int
roman3f9b65c2023-06-05 14:26:58 +02001543nc_server_config_tls(const struct lyd_node *node, NC_OPERATION op)
1544{
1545 struct nc_endpt *endpt;
1546 struct nc_bind *bind;
romanb6f44032023-06-30 15:07:56 +02001547 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02001548 struct nc_ch_client *ch_client;
roman3f9b65c2023-06-05 14:26:58 +02001549 int ret = 0;
1550
1551 assert(!strcmp(LYD_NAME(node), "tls"));
1552
romanb6f44032023-06-30 15:07:56 +02001553 if (is_listen(node)) {
1554 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
1555 ret = 1;
roman3f9b65c2023-06-05 14:26:58 +02001556 goto cleanup;
1557 }
romanb6f44032023-06-30 15:07:56 +02001558
1559 if (op == NC_OP_CREATE) {
1560 ret = nc_server_config_create_tls(endpt);
1561 if (ret) {
1562 goto cleanup;
1563 }
1564 } else if (op == NC_OP_DELETE) {
1565 nc_server_config_del_tls(bind, endpt->opts.tls);
1566 }
1567 } else {
romanba93eac2023-07-18 14:36:48 +02001568 /* LOCK */
1569 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1570 ret = 1;
1571 goto cleanup;
1572 }
1573
romanb6f44032023-06-30 15:07:56 +02001574 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
1575 ret = 1;
1576 goto cleanup;
1577 }
1578
1579 if (op == NC_OP_CREATE) {
1580 ret = nc_server_config_ch_create_tls(ch_endpt);
1581 if (ret) {
1582 goto cleanup;
1583 }
1584 }
roman3f9b65c2023-06-05 14:26:58 +02001585 }
1586
1587cleanup:
romanba93eac2023-07-18 14:36:48 +02001588 if (is_ch(node)) {
1589 /* UNLOCK */
1590 nc_ch_client_unlock(ch_client);
1591 }
roman3f9b65c2023-06-05 14:26:58 +02001592 return ret;
1593}
1594
roman2eab4742023-06-06 10:00:26 +02001595#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +02001596
romanc1d2b092023-02-02 08:58:27 +01001597static int
1598nc_server_config_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port)
1599{
1600 int sock = -1, set_addr, ret = 0;
1601
roman83683fb2023-02-24 09:15:23 +01001602 assert((address && !port) || (!address && port) || (endpt->ti == NC_TI_UNIX));
romanc1d2b092023-02-02 08:58:27 +01001603
1604 if (address) {
1605 set_addr = 1;
1606 } else {
1607 set_addr = 0;
1608 }
1609
1610 if (set_addr) {
1611 port = bind->port;
1612 } else {
1613 address = bind->address;
1614 }
1615
romanc1d2b092023-02-02 08:58:27 +01001616 /* we have all the information we need to create a listening socket */
roman83683fb2023-02-24 09:15:23 +01001617 if ((address && port) || (endpt->ti == NC_TI_UNIX)) {
romanc1d2b092023-02-02 08:58:27 +01001618 /* create new socket, close the old one */
roman83683fb2023-02-24 09:15:23 +01001619 if (endpt->ti == NC_TI_UNIX) {
1620 sock = nc_sock_listen_unix(endpt->opts.unixsock);
1621 } else {
1622 sock = nc_sock_listen_inet(address, port, &endpt->ka);
1623 }
1624
romanc1d2b092023-02-02 08:58:27 +01001625 if (sock == -1) {
1626 ret = 1;
1627 goto cleanup;
1628 }
1629
1630 if (bind->sock > -1) {
1631 close(bind->sock);
1632 }
1633 bind->sock = sock;
1634 }
1635
1636 if (sock > -1) {
1637 switch (endpt->ti) {
roman83683fb2023-02-24 09:15:23 +01001638 case NC_TI_UNIX:
1639 VRB(NULL, "Listening on %s for UNIX connections.", endpt->opts.unixsock->address);
1640 break;
roman2eab4742023-06-06 10:00:26 +02001641#ifdef NC_ENABLED_SSH_TLS
romanc1d2b092023-02-02 08:58:27 +01001642 case NC_TI_LIBSSH:
1643 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
1644 break;
romanc1d2b092023-02-02 08:58:27 +01001645 case NC_TI_OPENSSL:
1646 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
1647 break;
roman2eab4742023-06-06 10:00:26 +02001648#endif /* NC_ENABLED_SSH_TLS */
romanc1d2b092023-02-02 08:58:27 +01001649 default:
1650 ERRINT;
1651 ret = 1;
1652 break;
1653 }
1654 }
1655
1656cleanup:
1657 return ret;
1658}
1659
roman2eab4742023-06-06 10:00:26 +02001660#ifdef NC_ENABLED_SSH_TLS
1661
romanc1d2b092023-02-02 08:58:27 +01001662/* mandatory leaf */
1663static int
romane028ef92023-02-24 16:33:08 +01001664nc_server_config_local_address(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001665{
1666 struct nc_endpt *endpt;
1667 struct nc_bind *bind;
1668 int ret = 0;
1669
1670 (void) op;
1671
1672 assert(!strcmp(LYD_NAME(node), "local-address"));
1673
1674 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001675 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001676 ret = 1;
1677 goto cleanup;
1678 }
1679
roman874fed12023-05-25 10:20:01 +02001680 nc_server_config_del_local_address(bind);
romanc1d2b092023-02-02 08:58:27 +01001681 bind->address = strdup(lyd_get_value(node));
1682 if (!bind->address) {
1683 ERRMEM;
1684 ret = 1;
1685 goto cleanup;
1686 }
1687
1688 ret = nc_server_config_set_address_port(endpt, bind, lyd_get_value(node), 0);
1689 if (ret) {
1690 goto cleanup;
1691 }
1692 }
1693
1694cleanup:
1695 return ret;
1696}
1697
1698/* leaf with default value */
1699static int
romane028ef92023-02-24 16:33:08 +01001700nc_server_config_local_port(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001701{
1702 struct nc_endpt *endpt;
1703 struct nc_bind *bind;
1704 int ret = 0;
1705
1706 assert(!strcmp(LYD_NAME(node), "local-port"));
1707
1708 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001709 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001710 ret = 1;
1711 goto cleanup;
1712 }
1713
1714 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1715 bind->port = strtoul(lyd_get_value(node), NULL, 10);
1716 } else {
1717 /* delete -> set to default */
1718 bind->port = 0;
1719 }
1720
1721 ret = nc_server_config_set_address_port(endpt, bind, NULL, bind->port);
1722 if (ret) {
1723 goto cleanup;
1724 }
1725 }
1726
1727cleanup:
1728 return ret;
1729}
1730
1731/* P container */
1732static int
romane028ef92023-02-24 16:33:08 +01001733nc_server_config_keepalives(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001734{
roman5cbb6532023-06-22 12:53:17 +02001735 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01001736 struct nc_endpt *endpt;
1737 struct nc_bind *bind;
roman5cbb6532023-06-22 12:53:17 +02001738 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02001739 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001740
1741 assert(!strcmp(LYD_NAME(node), "keepalives"));
1742
roman5cbb6532023-06-22 12:53:17 +02001743 if (is_listen(node) && equal_parent_name(node, 1, "tcp-server-parameters")) {
romanf02273a2023-05-25 09:44:11 +02001744 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001745 ret = 1;
1746 goto cleanup;
1747 }
1748
1749 if (op == NC_OP_CREATE) {
1750 endpt->ka.enabled = 1;
1751 } else {
1752 endpt->ka.enabled = 0;
1753 }
1754 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
1755 if (ret) {
1756 goto cleanup;
1757 }
roman5cbb6532023-06-22 12:53:17 +02001758 } else if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
romanba93eac2023-07-18 14:36:48 +02001759 /* LOCK */
1760 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1761 return 1;
1762 }
1763
roman4cb8bb12023-06-29 09:16:46 +02001764 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02001765 ret = 1;
1766 goto cleanup;
1767 }
1768
1769 if (op == NC_OP_CREATE) {
1770 ch_endpt->ka.enabled = 1;
1771 } else {
1772 ch_endpt->ka.enabled = 0;
1773 }
romanc1d2b092023-02-02 08:58:27 +01001774 }
1775
1776cleanup:
romanba93eac2023-07-18 14:36:48 +02001777 if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
1778 /* UNLOCK */
1779 nc_ch_client_unlock(ch_client);
1780 }
romanc1d2b092023-02-02 08:58:27 +01001781 return ret;
1782}
1783
1784/* mandatory leaf */
1785static int
romane028ef92023-02-24 16:33:08 +01001786nc_server_config_idle_time(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001787{
roman5cbb6532023-06-22 12:53:17 +02001788 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01001789 struct nc_endpt *endpt;
1790 struct nc_bind *bind;
roman5cbb6532023-06-22 12:53:17 +02001791 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02001792 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001793
1794 assert(!strcmp(LYD_NAME(node), "idle-time"));
1795
roman5cbb6532023-06-22 12:53:17 +02001796 if (is_listen(node) && equal_parent_name(node, 2, "tcp-server-parameters")) {
romanf02273a2023-05-25 09:44:11 +02001797 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001798 ret = 1;
1799 goto cleanup;
1800 }
1801
1802 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1803 endpt->ka.idle_time = strtoul(lyd_get_value(node), NULL, 10);
1804 } else {
1805 endpt->ka.idle_time = 0;
1806 }
1807 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
1808 if (ret) {
1809 goto cleanup;
1810 }
roman5cbb6532023-06-22 12:53:17 +02001811 } else if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) {
romanba93eac2023-07-18 14:36:48 +02001812 /* LOCK */
1813 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1814 return 1;
1815 }
1816
roman4cb8bb12023-06-29 09:16:46 +02001817 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02001818 ret = 1;
1819 goto cleanup;
1820 }
1821
1822 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1823 ch_endpt->ka.idle_time = strtoul(lyd_get_value(node), NULL, 10);
1824 } else {
1825 ch_endpt->ka.idle_time = 0;
1826 }
romanc1d2b092023-02-02 08:58:27 +01001827 }
1828
1829cleanup:
romanba93eac2023-07-18 14:36:48 +02001830 if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
1831 /* UNLOCK */
1832 nc_ch_client_unlock(ch_client);
1833 }
romanc1d2b092023-02-02 08:58:27 +01001834 return ret;
1835}
1836
1837/* mandatory leaf */
1838static int
romane028ef92023-02-24 16:33:08 +01001839nc_server_config_max_probes(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001840{
roman5cbb6532023-06-22 12:53:17 +02001841 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01001842 struct nc_endpt *endpt;
1843 struct nc_bind *bind;
roman5cbb6532023-06-22 12:53:17 +02001844 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02001845 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001846
1847 assert(!strcmp(LYD_NAME(node), "max-probes"));
1848
roman5cbb6532023-06-22 12:53:17 +02001849 if (is_listen(node) && equal_parent_name(node, 2, "tcp-server-parameters")) {
romanf02273a2023-05-25 09:44:11 +02001850 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001851 ret = 1;
1852 goto cleanup;
1853 }
1854
1855 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1856 endpt->ka.max_probes = strtoul(lyd_get_value(node), NULL, 10);
1857 } else {
1858 endpt->ka.max_probes = 0;
1859 }
1860 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
1861 if (ret) {
1862 goto cleanup;
1863 }
roman5cbb6532023-06-22 12:53:17 +02001864 } else if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) {
romanba93eac2023-07-18 14:36:48 +02001865 /* LOCK */
1866 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1867 return 1;
1868 }
1869
roman4cb8bb12023-06-29 09:16:46 +02001870 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02001871 ret = 1;
1872 goto cleanup;
1873 }
1874
1875 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1876 ch_endpt->ka.max_probes = strtoul(lyd_get_value(node), NULL, 10);
1877 } else {
1878 ch_endpt->ka.max_probes = 0;
1879 }
romanc1d2b092023-02-02 08:58:27 +01001880 }
1881
1882cleanup:
romanba93eac2023-07-18 14:36:48 +02001883 if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
1884 /* UNLOCK */
1885 nc_ch_client_unlock(ch_client);
1886 }
romanc1d2b092023-02-02 08:58:27 +01001887 return ret;
1888}
1889
1890/* mandatory leaf */
1891static int
romane028ef92023-02-24 16:33:08 +01001892nc_server_config_probe_interval(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001893{
roman5cbb6532023-06-22 12:53:17 +02001894 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01001895 struct nc_endpt *endpt;
1896 struct nc_bind *bind;
roman5cbb6532023-06-22 12:53:17 +02001897 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02001898 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001899
1900 assert(!strcmp(LYD_NAME(node), "probe-interval"));
1901
roman5cbb6532023-06-22 12:53:17 +02001902 if (is_listen(node) && equal_parent_name(node, 2, "tcp-server-parameters")) {
romanf02273a2023-05-25 09:44:11 +02001903 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001904 ret = 1;
1905 goto cleanup;
1906 }
1907
1908 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1909 endpt->ka.probe_interval = strtoul(lyd_get_value(node), NULL, 10);
1910 } else {
1911 endpt->ka.probe_interval = 0;
1912 }
1913 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
1914 if (ret) {
1915 goto cleanup;
1916 }
roman5cbb6532023-06-22 12:53:17 +02001917 } else if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) {
romanba93eac2023-07-18 14:36:48 +02001918 /* LOCK */
1919 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1920 return 1;
1921 }
1922
roman4cb8bb12023-06-29 09:16:46 +02001923 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02001924 ret = 1;
1925 goto cleanup;
1926 }
1927
1928 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1929 ch_endpt->ka.probe_interval = strtoul(lyd_get_value(node), NULL, 10);
1930 } else {
1931 ch_endpt->ka.max_probes = 0;
1932 }
romanc1d2b092023-02-02 08:58:27 +01001933 }
1934
1935cleanup:
romanba93eac2023-07-18 14:36:48 +02001936 if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
1937 /* UNLOCK */
1938 nc_ch_client_unlock(ch_client);
1939 }
romanc1d2b092023-02-02 08:58:27 +01001940 return ret;
1941}
1942
1943static int
roman874fed12023-05-25 10:20:01 +02001944nc_server_config_create_host_key(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +01001945{
romanf02273a2023-05-25 09:44:11 +02001946 node = lyd_child(node);
1947 assert(!strcmp(LYD_NAME(node), "name"));
romanc1d2b092023-02-02 08:58:27 +01001948
romanf02273a2023-05-25 09:44:11 +02001949 return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->hostkeys, sizeof *opts->hostkeys, &opts->hostkey_count);
romanc1d2b092023-02-02 08:58:27 +01001950}
1951
1952/* list */
1953static int
romane028ef92023-02-24 16:33:08 +01001954nc_server_config_host_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001955{
roman5cbb6532023-06-22 12:53:17 +02001956 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01001957 struct nc_hostkey *hostkey;
roman4cb8bb12023-06-29 09:16:46 +02001958 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02001959 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01001960
1961 assert(!strcmp(LYD_NAME(node), "host-key"));
1962
roman4cb8bb12023-06-29 09:16:46 +02001963 if (nc_server_config_get_ssh_opts(node, &opts)) {
1964 ret = 1;
1965 goto cleanup;
1966 }
romanc1d2b092023-02-02 08:58:27 +01001967
roman4cb8bb12023-06-29 09:16:46 +02001968 if (equal_parent_name(node, 1, "server-identity")) {
romanba93eac2023-07-18 14:36:48 +02001969 /* LOCK */
1970 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
1971 return 1;
1972 }
1973
romanc1d2b092023-02-02 08:58:27 +01001974 if (op == NC_OP_CREATE) {
roman4cb8bb12023-06-29 09:16:46 +02001975 ret = nc_server_config_create_host_key(node, opts);
romanc1d2b092023-02-02 08:58:27 +01001976 if (ret) {
1977 goto cleanup;
1978 }
1979 } else if (op == NC_OP_DELETE) {
roman4cb8bb12023-06-29 09:16:46 +02001980 if (nc_server_config_get_hostkey(node, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01001981 ret = 1;
1982 goto cleanup;
1983 }
roman4cb8bb12023-06-29 09:16:46 +02001984 nc_server_config_del_hostkey(opts, hostkey);
roman5cbb6532023-06-22 12:53:17 +02001985 }
romanc1d2b092023-02-02 08:58:27 +01001986 }
1987
1988cleanup:
romanba93eac2023-07-18 14:36:48 +02001989 if (is_ch(node) && equal_parent_name(node, 1, "server-identity")) {
1990 /* UNLOCK */
1991 nc_ch_client_unlock(ch_client);
1992 }
romanc1d2b092023-02-02 08:58:27 +01001993 return ret;
1994}
1995
1996/* mandatory leaf */
romane028ef92023-02-24 16:33:08 +01001997static int
1998nc_server_config_public_key_format(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001999{
roman3f9b65c2023-06-05 14:26:58 +02002000 int ret = 0;
roman5cbb6532023-06-22 12:53:17 +02002001 const char *format;
roman3f9b65c2023-06-05 14:26:58 +02002002 NC_PUBKEY_FORMAT pubkey_type;
roman8edee342023-03-31 13:25:48 +02002003 struct nc_public_key *pubkey;
romanc1d2b092023-02-02 08:58:27 +01002004 struct nc_hostkey *hostkey;
romanb6f44032023-06-30 15:07:56 +02002005 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002006 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002007
2008 assert(!strcmp(LYD_NAME(node), "public-key-format"));
2009
2010 format = ((struct lyd_node_term *)node)->value.ident->name;
roman3f9b65c2023-06-05 14:26:58 +02002011 if (!strcmp(format, "ssh-public-key-format")) {
roman13145912023-08-17 15:36:54 +02002012 pubkey_type = NC_PUBKEY_FORMAT_SSH;
roman3f9b65c2023-06-05 14:26:58 +02002013 } else if (!strcmp(format, "subject-public-key-info-format")) {
2014 pubkey_type = NC_PUBKEY_FORMAT_X509;
2015 } else {
2016 ERR(NULL, "Public key format (%s) not supported.", format);
2017 ret = 1;
2018 goto cleanup;
2019 }
romanc1d2b092023-02-02 08:58:27 +01002020
romanba93eac2023-07-18 14:36:48 +02002021 /* LOCK */
2022 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2023 return 1;
2024 }
2025
roman4cb8bb12023-06-29 09:16:46 +02002026 if (is_ssh(node) && equal_parent_name(node, 4, "server-identity")) {
roman5cbb6532023-06-22 12:53:17 +02002027 /* SSH hostkey public key fmt */
roman4cb8bb12023-06-29 09:16:46 +02002028 if (nc_server_config_get_hostkey(node, &hostkey)) {
roman5cbb6532023-06-22 12:53:17 +02002029 ret = 1;
2030 goto cleanup;
2031 }
2032
2033 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2034 hostkey->key.pubkey_type = pubkey_type;
2035 }
roman4cb8bb12023-06-29 09:16:46 +02002036 } else if (is_ssh(node) && equal_parent_name(node, 6, "client-authentication")) {
roman5cbb6532023-06-22 12:53:17 +02002037 /* SSH client auth public key fmt */
roman4cb8bb12023-06-29 09:16:46 +02002038 if (nc_server_config_get_pubkey(node, &pubkey)) {
romanc1d2b092023-02-02 08:58:27 +01002039 ret = 1;
2040 goto cleanup;
2041 }
2042
2043 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman3f9b65c2023-06-05 14:26:58 +02002044 pubkey->type = pubkey_type;
romanc1d2b092023-02-02 08:58:27 +01002045 }
romanb6f44032023-06-30 15:07:56 +02002046 } else if (is_tls(node) && equal_parent_name(node, 3, "server-identity")) {
2047 /* TLS server-identity */
2048 if (nc_server_config_get_tls_opts(node, &opts)) {
roman4cb8bb12023-06-29 09:16:46 +02002049 ret = 1;
2050 goto cleanup;
2051 }
2052
roman5cbb6532023-06-22 12:53:17 +02002053 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02002054 opts->pubkey_type = pubkey_type;
romanc1d2b092023-02-02 08:58:27 +01002055 }
romanc1d2b092023-02-02 08:58:27 +01002056 }
2057
2058cleanup:
romanba93eac2023-07-18 14:36:48 +02002059 if (is_ch(node)) {
2060 /* UNLOCK */
2061 nc_ch_client_unlock(ch_client);
2062 }
romanc1d2b092023-02-02 08:58:27 +01002063 return ret;
2064}
2065
2066static int
roman874fed12023-05-25 10:20:01 +02002067nc_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 +01002068{
romanc1d2b092023-02-02 08:58:27 +01002069 assert(!strcmp(LYD_NAME(node), "public-key"));
2070
romanc1d2b092023-02-02 08:58:27 +01002071 node = lyd_child(node);
2072 assert(!strcmp(LYD_NAME(node), "name"));
2073
romanf02273a2023-05-25 09:44:11 +02002074 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 +01002075}
2076
2077static int
roman874fed12023-05-25 10:20:01 +02002078nc_server_config_replace_auth_key_public_key_leaf(const struct lyd_node *node, struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +01002079{
roman874fed12023-05-25 10:20:01 +02002080 nc_server_config_del_auth_client_pubkey_pub_base64(pubkey);
romanc1d2b092023-02-02 08:58:27 +01002081
roman3f9b65c2023-06-05 14:26:58 +02002082 pubkey->data = strdup(lyd_get_value(node));
2083 if (!pubkey->data) {
romanc1d2b092023-02-02 08:58:27 +01002084 ERRMEM;
2085 return 1;
2086 }
2087
2088 return 0;
2089}
2090
2091static int
roman874fed12023-05-25 10:20:01 +02002092nc_server_config_replace_host_key_public_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +01002093{
roman874fed12023-05-25 10:20:01 +02002094 nc_server_config_del_public_key(hostkey);
romanc1d2b092023-02-02 08:58:27 +01002095
roman3f9b65c2023-06-05 14:26:58 +02002096 hostkey->key.pubkey_data = strdup(lyd_get_value(node));
2097 if (!hostkey->key.pubkey_data) {
romanc1d2b092023-02-02 08:58:27 +01002098 ERRMEM;
2099 return 1;
2100 }
2101
2102 return 0;
2103}
2104
roman3f9b65c2023-06-05 14:26:58 +02002105static int
2106nc_server_config_tls_replace_server_public_key(const struct lyd_node *node, struct nc_server_tls_opts *opts)
2107{
2108 nc_server_config_tls_del_public_key(opts);
2109
2110 opts->pubkey_data = strdup(lyd_get_value(node));
2111 if (!opts->pubkey_data) {
2112 ERRMEM;
2113 return 1;
2114 }
2115
2116 return 0;
2117}
2118
romanc1d2b092023-02-02 08:58:27 +01002119static int
roman13145912023-08-17 15:36:54 +02002120nc_server_config_is_pk_subject_public_key_info(const char *b64)
2121{
2122 int ret = 0;
2123 long len;
2124 char *bin = NULL, *tmp;
2125 EVP_PKEY *pkey = NULL;
2126
2127 /* base64 2 binary */
2128 len = nc_base64_to_bin(b64, &bin);
2129 if (len == -1) {
2130 ERR(NULL, "Decoding base64 public key to binary failed.");
2131 ret = -1;
2132 goto cleanup;
2133 }
2134
2135 /* for deallocation later */
2136 tmp = bin;
2137
2138 /* try to create EVP_PKEY from the supposed SubjectPublicKeyInfo binary data */
2139 pkey = d2i_PUBKEY(NULL, (const unsigned char **)&tmp, len);
2140 if (pkey) {
2141 /* success, it's most likely SubjectPublicKeyInfo pubkey */
2142 ret = 1;
2143 } else {
2144 /* fail, it's most likely not SubjectPublicKeyInfo pubkey */
2145 ret = 0;
2146 }
2147
2148cleanup:
2149 EVP_PKEY_free(pkey);
2150 free(bin);
2151 return ret;
2152}
2153
2154static int
romane028ef92023-02-24 16:33:08 +01002155nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002156{
roman3f9b65c2023-06-05 14:26:58 +02002157 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002158 struct nc_hostkey *hostkey;
2159 struct nc_client_auth *auth_client;
roman8edee342023-03-31 13:25:48 +02002160 struct nc_public_key *pubkey;
romanb6f44032023-06-30 15:07:56 +02002161 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002162 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002163
2164 assert(!strcmp(LYD_NAME(node), "public-key"));
2165
romanba93eac2023-07-18 14:36:48 +02002166 /* LOCK */
2167 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2168 return 1;
2169 }
2170
roman4cb8bb12023-06-29 09:16:46 +02002171 if (is_ssh(node) && equal_parent_name(node, 3, "host-key")) {
roman3f9b65c2023-06-05 14:26:58 +02002172 /* server's public-key, mandatory leaf */
roman4cb8bb12023-06-29 09:16:46 +02002173 if (nc_server_config_get_hostkey(node, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01002174 ret = 1;
2175 goto cleanup;
2176 }
2177
roman13145912023-08-17 15:36:54 +02002178 /* the public key must not be SubjectPublicKeyInfoFormat, as per the ietf-netconf-server model */
2179 if (nc_server_config_is_pk_subject_public_key_info(lyd_get_value(node))) {
2180 ERR(NULL, "Using Public Key in the SubjectPublicKeyInfo format as an SSH hostkey is forbidden!");
2181 ret = 1;
2182 goto cleanup;
2183 }
2184
romanc1d2b092023-02-02 08:58:27 +01002185 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanf02273a2023-05-25 09:44:11 +02002186 /* set to local */
roman874fed12023-05-25 10:20:01 +02002187 hostkey->store = NC_STORE_LOCAL;
romanf02273a2023-05-25 09:44:11 +02002188
roman874fed12023-05-25 10:20:01 +02002189 ret = nc_server_config_replace_host_key_public_key(node, hostkey);
romanc1d2b092023-02-02 08:58:27 +01002190 if (ret) {
2191 goto cleanup;
2192 }
2193 }
roman4cb8bb12023-06-29 09:16:46 +02002194 } else if (is_ssh(node) && equal_parent_name(node, 5, "client-authentication")) {
romanc1d2b092023-02-02 08:58:27 +01002195 /* client auth pubkeys, list */
roman4cb8bb12023-06-29 09:16:46 +02002196 if (nc_server_config_get_auth_client(node, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01002197 ret = 1;
2198 goto cleanup;
2199 }
2200
2201 if (op == NC_OP_CREATE) {
romanf02273a2023-05-25 09:44:11 +02002202 /* set to local */
roman874fed12023-05-25 10:20:01 +02002203 auth_client->store = NC_STORE_LOCAL;
romanf02273a2023-05-25 09:44:11 +02002204
roman874fed12023-05-25 10:20:01 +02002205 ret = nc_server_config_create_auth_key_public_key_list(node, auth_client);
romanc1d2b092023-02-02 08:58:27 +01002206 if (ret) {
2207 goto cleanup;
2208 }
2209 } else if (op == NC_OP_DELETE) {
roman4cb8bb12023-06-29 09:16:46 +02002210 if (nc_server_config_get_pubkey(node, &pubkey)) {
romanc1d2b092023-02-02 08:58:27 +01002211 ret = 1;
2212 goto cleanup;
2213 }
2214
roman874fed12023-05-25 10:20:01 +02002215 nc_server_config_del_auth_client_pubkey(auth_client, pubkey);
romanc1d2b092023-02-02 08:58:27 +01002216 }
roman4cb8bb12023-06-29 09:16:46 +02002217 } else if (is_ssh(node) && equal_parent_name(node, 6, "client-authentication")) {
romanc1d2b092023-02-02 08:58:27 +01002218 /* client auth pubkey, leaf */
roman4cb8bb12023-06-29 09:16:46 +02002219 if (nc_server_config_get_pubkey(node, &pubkey)) {
romanc1d2b092023-02-02 08:58:27 +01002220 ret = 1;
2221 goto cleanup;
2222 }
2223
roman13145912023-08-17 15:36:54 +02002224 /* the public key must not be SubjectPublicKeyInfoFormat, as per the ietf-netconf-server model */
2225 if (nc_server_config_is_pk_subject_public_key_info(lyd_get_value(node))) {
2226 ERR(NULL, "Using Public Key in the SubjectPublicKeyInfo format as an SSH user's key is forbidden!");
2227 ret = 1;
2228 goto cleanup;
2229 }
2230
romanc1d2b092023-02-02 08:58:27 +01002231 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman874fed12023-05-25 10:20:01 +02002232 ret = nc_server_config_replace_auth_key_public_key_leaf(node, pubkey);
romanc1d2b092023-02-02 08:58:27 +01002233 if (ret) {
2234 goto cleanup;
2235 }
2236 } else {
roman874fed12023-05-25 10:20:01 +02002237 nc_server_config_del_auth_client_pubkey_pub_base64(pubkey);
romanc1d2b092023-02-02 08:58:27 +01002238 }
romanb6f44032023-06-30 15:07:56 +02002239 } else if (is_tls(node) && equal_parent_name(node, 3, "server-identity")) {
2240 /* TLS server-identity */
2241 if (nc_server_config_get_tls_opts(node, &opts)) {
roman4cb8bb12023-06-29 09:16:46 +02002242 ret = 1;
2243 goto cleanup;
2244 }
2245
roman13145912023-08-17 15:36:54 +02002246 /* the public key must be SubjectPublicKeyInfoFormat, as per the ietf-netconf-server model */
2247 if (!nc_server_config_is_pk_subject_public_key_info(lyd_get_value(node))) {
2248 ERR(NULL, "TLS server certificate's Public Key must be in the SubjectPublicKeyInfo format!");
2249 ret = 1;
2250 goto cleanup;
2251 }
2252
roman3f9b65c2023-06-05 14:26:58 +02002253 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2254 /* set to local */
romanb6f44032023-06-30 15:07:56 +02002255 opts->store = NC_STORE_LOCAL;
roman3f9b65c2023-06-05 14:26:58 +02002256
romanb6f44032023-06-30 15:07:56 +02002257 ret = nc_server_config_tls_replace_server_public_key(node, opts);
roman3f9b65c2023-06-05 14:26:58 +02002258 if (ret) {
2259 goto cleanup;
2260 }
2261 }
roman5cbb6532023-06-22 12:53:17 +02002262 }
2263
2264cleanup:
romanba93eac2023-07-18 14:36:48 +02002265 if (is_ch(node)) {
2266 /* UNLOCK */
2267 nc_ch_client_unlock(ch_client);
2268 }
roman5cbb6532023-06-22 12:53:17 +02002269 return ret;
2270}
2271
2272/* leaf */
2273static int
2274nc_server_config_private_key_format(const struct lyd_node *node, NC_OPERATION op)
2275{
2276 int ret = 0;
2277 const char *format;
2278 NC_PRIVKEY_FORMAT privkey_type;
roman5cbb6532023-06-22 12:53:17 +02002279 struct nc_hostkey *hostkey;
romanb6f44032023-06-30 15:07:56 +02002280 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002281 struct nc_ch_client *ch_client;
roman5cbb6532023-06-22 12:53:17 +02002282
2283 (void) op;
2284
2285 assert(!strcmp(LYD_NAME(node), "private-key-format"));
2286
2287 format = ((struct lyd_node_term *)node)->value.ident->name;
2288 if (!format) {
2289 ret = 1;
2290 goto cleanup;
2291 }
2292
2293 privkey_type = nc_server_config_get_private_key_type(format);
2294 if (privkey_type == NC_PRIVKEY_FORMAT_UNKNOWN) {
2295 ERR(NULL, "Unknown private key format.");
2296 ret = 1;
2297 goto cleanup;
2298 }
2299
romanba93eac2023-07-18 14:36:48 +02002300 /* LOCK */
2301 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2302 return 1;
2303 }
2304
roman4cb8bb12023-06-29 09:16:46 +02002305 if (is_ssh(node)) {
2306 /* ssh */
2307 if (nc_server_config_get_hostkey(node, &hostkey)) {
roman5cbb6532023-06-22 12:53:17 +02002308 ret = 1;
2309 goto cleanup;
2310 }
2311
2312 hostkey->key.privkey_type = privkey_type;
romanb6f44032023-06-30 15:07:56 +02002313 } else if (is_tls(node)) {
2314 /* tls */
2315 if (nc_server_config_get_tls_opts(node, &opts)) {
roman5cbb6532023-06-22 12:53:17 +02002316 ret = 1;
2317 goto cleanup;
2318 }
2319
romanb6f44032023-06-30 15:07:56 +02002320 opts->privkey_type = privkey_type;
roman5cbb6532023-06-22 12:53:17 +02002321 }
2322
2323cleanup:
romanba93eac2023-07-18 14:36:48 +02002324 if (is_ch(node)) {
2325 /* UNLOCK */
2326 nc_ch_client_unlock(ch_client);
2327 }
roman5cbb6532023-06-22 12:53:17 +02002328 return ret;
2329}
2330
2331static int
2332nc_server_config_replace_cleartext_private_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
2333{
2334 nc_server_config_del_private_key(hostkey);
2335 hostkey->key.privkey_data = strdup(lyd_get_value(node));
2336 if (!hostkey->key.privkey_data) {
2337 ERRMEM;
2338 return 1;
2339 }
2340
2341 return 0;
2342}
2343
2344static int
2345nc_server_config_tls_replace_cleartext_private_key(const struct lyd_node *node, struct nc_server_tls_opts *opts)
2346{
2347 nc_server_config_tls_del_cleartext_private_key(opts);
2348 opts->privkey_data = strdup(lyd_get_value(node));
2349 if (!opts->privkey_data) {
2350 ERRMEM;
2351 return 1;
2352 }
2353
2354 return 0;
2355}
2356
2357static int
2358nc_server_config_cleartext_private_key(const struct lyd_node *node, NC_OPERATION op)
2359{
2360 int ret = 0;
roman5cbb6532023-06-22 12:53:17 +02002361 struct nc_hostkey *hostkey;
romanb6f44032023-06-30 15:07:56 +02002362 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002363 struct nc_ch_client *ch_client;
roman5cbb6532023-06-22 12:53:17 +02002364
2365 assert(!strcmp(LYD_NAME(node), "cleartext-private-key"));
2366
romanba93eac2023-07-18 14:36:48 +02002367 /* LOCK */
2368 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2369 return 1;
2370 }
2371
roman4cb8bb12023-06-29 09:16:46 +02002372 if (is_ssh(node)) {
2373 /* ssh */
2374 if (nc_server_config_get_hostkey(node, &hostkey)) {
roman5cbb6532023-06-22 12:53:17 +02002375 ret = 1;
2376 goto cleanup;
2377 }
2378
2379 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2380 ret = nc_server_config_replace_cleartext_private_key(node, hostkey);
2381 if (ret) {
2382 goto cleanup;
2383 }
2384 } else {
2385 nc_server_config_del_private_key(hostkey);
2386 }
romanb6f44032023-06-30 15:07:56 +02002387 } else if (is_tls(node)) {
2388 /* tls */
2389 if (nc_server_config_get_tls_opts(node, &opts)) {
roman4cb8bb12023-06-29 09:16:46 +02002390 ret = 1;
2391 goto cleanup;
2392 }
2393
roman5cbb6532023-06-22 12:53:17 +02002394 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02002395 ret = nc_server_config_tls_replace_cleartext_private_key(node, opts);
roman5cbb6532023-06-22 12:53:17 +02002396 if (ret) {
2397 goto cleanup;
2398 }
2399 } else {
romanb6f44032023-06-30 15:07:56 +02002400 nc_server_config_tls_del_cleartext_private_key(opts);
roman5cbb6532023-06-22 12:53:17 +02002401 }
roman5cbb6532023-06-22 12:53:17 +02002402 }
2403
2404cleanup:
romanba93eac2023-07-18 14:36:48 +02002405 if (is_ch(node)) {
2406 /* UNLOCK */
2407 nc_ch_client_unlock(ch_client);
2408 }
roman5cbb6532023-06-22 12:53:17 +02002409 return ret;
2410}
2411
2412static int
2413nc_server_config_create_keystore_reference(const struct lyd_node *node, struct nc_hostkey *hostkey)
2414{
2415 uint16_t i;
2416 struct nc_keystore *ks = &server_opts.keystore;
2417
2418 /* lookup name */
2419 for (i = 0; i < ks->asym_key_count; i++) {
2420 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
2421 break;
2422 }
2423 }
2424
2425 if (i == ks->asym_key_count) {
roman13145912023-08-17 15:36:54 +02002426 ERR(NULL, "Keystore entry \"%s\" not found.", lyd_get_value(node));
roman5cbb6532023-06-22 12:53:17 +02002427 return 1;
2428 }
2429
2430 hostkey->ks_ref = &ks->asym_keys[i];
2431
roman13145912023-08-17 15:36:54 +02002432 /* check if the referenced public key is SubjectPublicKeyInfo */
2433 if (nc_server_config_is_pk_subject_public_key_info(hostkey->ks_ref->pubkey_data)) {
2434 ERR(NULL, "Using Public Key in the SubjectPublicKeyInfo format as an SSH hostkey is forbidden!");
2435 return 1;
2436 }
2437
roman5cbb6532023-06-22 12:53:17 +02002438 return 0;
2439}
2440
2441/* leaf */
2442static int
2443nc_server_config_keystore_reference(const struct lyd_node *node, NC_OPERATION op)
2444{
2445 int ret = 0;
roman5cbb6532023-06-22 12:53:17 +02002446 struct nc_hostkey *hostkey;
romanba93eac2023-07-18 14:36:48 +02002447 struct nc_ch_client *ch_client;
roman5cbb6532023-06-22 12:53:17 +02002448
2449 assert(!strcmp(LYD_NAME(node), "keystore-reference"));
2450
romanba93eac2023-07-18 14:36:48 +02002451 /* LOCK */
2452 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2453 return 1;
2454 }
2455
roman4cb8bb12023-06-29 09:16:46 +02002456 if (is_ssh(node) && equal_parent_name(node, 3, "server-identity")) {
2457 if (nc_server_config_get_hostkey(node, &hostkey)) {
roman5cbb6532023-06-22 12:53:17 +02002458 ret = 1;
2459 goto cleanup;
2460 }
2461
2462 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2463 /* set to keystore */
2464 hostkey->store = NC_STORE_KEYSTORE;
2465
2466 ret = nc_server_config_create_keystore_reference(node, hostkey);
2467 if (ret) {
2468 goto cleanup;
2469 }
2470 } else {
2471 hostkey->ks_ref = NULL;
2472 }
roman3f9b65c2023-06-05 14:26:58 +02002473 }
romanc1d2b092023-02-02 08:58:27 +01002474
2475cleanup:
romanba93eac2023-07-18 14:36:48 +02002476 if (is_ch(node)) {
2477 /* UNLOCK */
2478 nc_ch_client_unlock(ch_client);
2479 }
romanc1d2b092023-02-02 08:58:27 +01002480 return ret;
2481}
2482
2483static int
roman874fed12023-05-25 10:20:01 +02002484nc_server_config_create_user(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +01002485{
romanf02273a2023-05-25 09:44:11 +02002486 node = lyd_child(node);
2487 assert(!strcmp(LYD_NAME(node), "name"));
romanc1d2b092023-02-02 08:58:27 +01002488
romanf02273a2023-05-25 09:44:11 +02002489 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 +01002490}
2491
2492/* list */
2493static int
romane028ef92023-02-24 16:33:08 +01002494nc_server_config_user(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002495{
roman5cbb6532023-06-22 12:53:17 +02002496 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002497 struct nc_client_auth *auth_client;
roman4cb8bb12023-06-29 09:16:46 +02002498 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002499 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002500
2501 assert(!strcmp(LYD_NAME(node), "user"));
2502
romanba93eac2023-07-18 14:36:48 +02002503 /* LOCK */
2504 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2505 return 1;
2506 }
2507
roman4cb8bb12023-06-29 09:16:46 +02002508 if (nc_server_config_get_ssh_opts(node, &opts)) {
2509 ret = 1;
2510 goto cleanup;
2511 }
2512
2513 if (op == NC_OP_CREATE) {
2514 ret = nc_server_config_create_user(node, opts);
2515 if (ret) {
2516 goto cleanup;
2517 }
2518 } else if (op == NC_OP_DELETE) {
2519 if (nc_server_config_get_auth_client(node, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01002520 ret = 1;
2521 goto cleanup;
2522 }
2523
roman4cb8bb12023-06-29 09:16:46 +02002524 nc_server_config_del_auth_client(opts, auth_client);
romanc1d2b092023-02-02 08:58:27 +01002525 }
2526
2527cleanup:
romanba93eac2023-07-18 14:36:48 +02002528 if (is_ch(node)) {
2529 /* UNLOCK */
2530 nc_ch_client_unlock(ch_client);
2531 }
romanc1d2b092023-02-02 08:58:27 +01002532 return ret;
2533}
2534
2535static int
romane028ef92023-02-24 16:33:08 +01002536nc_server_config_auth_attempts(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002537{
romanc1d2b092023-02-02 08:58:27 +01002538 int ret = 0;
roman4cb8bb12023-06-29 09:16:46 +02002539 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002540 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002541
2542 assert(!strcmp(LYD_NAME(node), "auth-attempts"));
2543
romanba93eac2023-07-18 14:36:48 +02002544 /* LOCK */
2545 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2546 return 1;
2547 }
2548
roman4cb8bb12023-06-29 09:16:46 +02002549 if (nc_server_config_get_ssh_opts(node, &opts)) {
2550 ret = 1;
2551 goto cleanup;
2552 }
romanc1d2b092023-02-02 08:58:27 +01002553
roman4cb8bb12023-06-29 09:16:46 +02002554 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2555 opts->auth_attempts = strtoul(lyd_get_value(node), NULL, 10);
romanc1d2b092023-02-02 08:58:27 +01002556 }
2557
2558cleanup:
romanba93eac2023-07-18 14:36:48 +02002559 if (is_ch(node)) {
2560 /* UNLOCK */
2561 nc_ch_client_unlock(ch_client);
2562 }
romanc1d2b092023-02-02 08:58:27 +01002563 return ret;
2564}
2565
2566static int
romane028ef92023-02-24 16:33:08 +01002567nc_server_config_auth_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002568{
romanc1d2b092023-02-02 08:58:27 +01002569 int ret = 0;
roman4cb8bb12023-06-29 09:16:46 +02002570 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002571 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002572
2573 assert(!strcmp(LYD_NAME(node), "auth-timeout"));
2574
romanba93eac2023-07-18 14:36:48 +02002575 /* LOCK */
2576 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2577 return 1;
2578 }
2579
roman4cb8bb12023-06-29 09:16:46 +02002580 if (nc_server_config_get_ssh_opts(node, &opts)) {
2581 ret = 1;
2582 goto cleanup;
2583 }
romanc1d2b092023-02-02 08:58:27 +01002584
roman4cb8bb12023-06-29 09:16:46 +02002585 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2586 opts->auth_timeout = strtoul(lyd_get_value(node), NULL, 10);
romanc1d2b092023-02-02 08:58:27 +01002587 }
2588
2589cleanup:
romanba93eac2023-07-18 14:36:48 +02002590 if (is_ch(node)) {
2591 /* UNLOCK */
2592 nc_ch_client_unlock(ch_client);
2593 }
romanc1d2b092023-02-02 08:58:27 +01002594 return ret;
2595}
2596
2597static int
roman13145912023-08-17 15:36:54 +02002598nc_server_config_ssh_replace_truststore_reference(const struct lyd_node *node, struct nc_client_auth *client_auth)
romanc1d2b092023-02-02 08:58:27 +01002599{
romand57b3722023-04-05 11:26:25 +02002600 uint16_t i;
2601 struct nc_truststore *ts = &server_opts.truststore;
romanc1d2b092023-02-02 08:58:27 +01002602
romand57b3722023-04-05 11:26:25 +02002603 /* lookup name */
2604 for (i = 0; i < ts->pub_bag_count; i++) {
2605 if (!strcmp(lyd_get_value(node), ts->pub_bags[i].name)) {
2606 break;
2607 }
2608 }
2609
2610 if (i == ts->pub_bag_count) {
roman3f9b65c2023-06-05 14:26:58 +02002611 ERR(NULL, "Public-key bag \"%s\" not found in truststore.", lyd_get_value(node));
romanc1d2b092023-02-02 08:58:27 +01002612 return 1;
2613 }
2614
romand57b3722023-04-05 11:26:25 +02002615 client_auth->ts_ref = &ts->pub_bags[i];
2616
roman13145912023-08-17 15:36:54 +02002617 /* check if any of the referenced public keys is SubjectPublicKeyInfo */
2618 for (i = 0; i < client_auth->ts_ref->pubkey_count; i++) {
2619 if (nc_server_config_is_pk_subject_public_key_info(client_auth->ts_ref->pubkeys[i].data)) {
2620 ERR(NULL, "Using Public Key in the SubjectPublicKeyInfo format as an SSH user's key is forbidden!");
2621 return 1;
2622 }
2623 }
2624
romanc1d2b092023-02-02 08:58:27 +01002625 return 0;
2626}
2627
roman3f9b65c2023-06-05 14:26:58 +02002628static int
2629nc_server_config_tls_replace_truststore_reference(const struct lyd_node *node, struct nc_cert_grouping *auth_client)
2630{
2631 uint16_t i;
2632 struct nc_truststore *ts = &server_opts.truststore;
2633
2634 /* lookup name */
2635 for (i = 0; i < ts->cert_bag_count; i++) {
2636 if (!strcmp(lyd_get_value(node), ts->cert_bags[i].name)) {
2637 break;
2638 }
2639 }
2640
2641 if (i == ts->cert_bag_count) {
2642 ERR(NULL, "Certificate bag \"%s\" not found in truststore.", lyd_get_value(node));
2643 return 1;
2644 }
2645
2646 auth_client->ts_ref = &ts->cert_bags[i];
2647
2648 return 0;
2649}
2650
romanc1d2b092023-02-02 08:58:27 +01002651/* leaf */
2652static int
romane028ef92023-02-24 16:33:08 +01002653nc_server_config_truststore_reference(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002654{
romanc1d2b092023-02-02 08:58:27 +01002655 int ret = 0;
roman3f9b65c2023-06-05 14:26:58 +02002656 struct nc_endpt *endpt;
roman3f9b65c2023-06-05 14:26:58 +02002657 struct nc_client_auth *auth_client;
romanba93eac2023-07-18 14:36:48 +02002658 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002659
2660 assert(!strcmp(LYD_NAME(node), "truststore-reference"));
2661
romanba93eac2023-07-18 14:36:48 +02002662 /* LOCK */
2663 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2664 return 1;
2665 }
2666
roman4cb8bb12023-06-29 09:16:46 +02002667 if (is_ssh(node) && equal_parent_name(node, 1, "public-keys")) {
2668 if (nc_server_config_get_auth_client(node, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01002669 ret = 1;
2670 goto cleanup;
2671 }
2672
2673 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanf02273a2023-05-25 09:44:11 +02002674 /* set to truststore */
roman874fed12023-05-25 10:20:01 +02002675 auth_client->store = NC_STORE_TRUSTSTORE;
romanf02273a2023-05-25 09:44:11 +02002676
roman13145912023-08-17 15:36:54 +02002677 ret = nc_server_config_ssh_replace_truststore_reference(node, auth_client);
romanc1d2b092023-02-02 08:58:27 +01002678 if (ret) {
2679 goto cleanup;
2680 }
2681 } else {
romand57b3722023-04-05 11:26:25 +02002682 auth_client->ts_ref = NULL;
romanc1d2b092023-02-02 08:58:27 +01002683 }
romanba93eac2023-07-18 14:36:48 +02002684 } else if (is_tls(node) && equal_parent_name(node, 1, "ca-certs")) {
roman4cb8bb12023-06-29 09:16:46 +02002685 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
2686 ret = 1;
2687 goto cleanup;
2688 }
2689
roman3f9b65c2023-06-05 14:26:58 +02002690 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2691 /* set to truststore */
2692 endpt->opts.tls->ca_certs.store = NC_STORE_TRUSTSTORE;
2693
2694 ret = nc_server_config_tls_replace_truststore_reference(node, &endpt->opts.tls->ca_certs);
2695 if (ret) {
2696 goto cleanup;
2697 }
2698 } else {
2699 endpt->opts.tls->ca_certs.ts_ref = NULL;
2700 }
romanba93eac2023-07-18 14:36:48 +02002701 } else if (is_tls(node) && equal_parent_name(node, 1, "ee-certs")) {
roman4cb8bb12023-06-29 09:16:46 +02002702 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
2703 ret = 1;
2704 goto cleanup;
2705 }
2706
roman3f9b65c2023-06-05 14:26:58 +02002707 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2708 /* set to truststore */
2709 endpt->opts.tls->ee_certs.store = NC_STORE_TRUSTSTORE;
2710
2711 ret = nc_server_config_tls_replace_truststore_reference(node, &endpt->opts.tls->ee_certs);
2712 if (ret) {
2713 goto cleanup;
2714 }
2715 } else {
2716 endpt->opts.tls->ee_certs.ts_ref = NULL;
2717 }
2718 }
romanc1d2b092023-02-02 08:58:27 +01002719
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
roman874fed12023-05-25 10:20:01 +02002729nc_server_config_replace_password(const struct lyd_node *node, struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +01002730{
roman874fed12023-05-25 10:20:01 +02002731 nc_server_config_del_auth_client_password(auth_client);
romanc1d2b092023-02-02 08:58:27 +01002732
2733 auth_client->password = strdup(lyd_get_value(node));
2734 if (!auth_client->password) {
2735 ERRMEM;
2736 return 1;
2737 }
2738
2739 return 0;
2740}
2741
2742/* leaf */
2743static int
romane028ef92023-02-24 16:33:08 +01002744nc_server_config_password(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002745{
roman5cbb6532023-06-22 12:53:17 +02002746 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002747 struct nc_client_auth *auth_client;
romanba93eac2023-07-18 14:36:48 +02002748 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002749
2750 assert(!strcmp(LYD_NAME(node), "password"));
2751
romanba93eac2023-07-18 14:36:48 +02002752 /* LOCK */
2753 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2754 return 1;
2755 }
2756
roman4cb8bb12023-06-29 09:16:46 +02002757 if (nc_server_config_get_auth_client(node, &auth_client)) {
2758 ret = 1;
2759 goto cleanup;
2760 }
2761
2762 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2763 ret = nc_server_config_replace_password(node, auth_client);
2764 if (ret) {
romanc1d2b092023-02-02 08:58:27 +01002765 goto cleanup;
2766 }
roman4cb8bb12023-06-29 09:16:46 +02002767 } else {
2768 nc_server_config_del_auth_client_password(auth_client);
romanc1d2b092023-02-02 08:58:27 +01002769 }
2770
2771cleanup:
romanba93eac2023-07-18 14:36:48 +02002772 if (is_ch(node)) {
2773 /* UNLOCK */
2774 nc_ch_client_unlock(ch_client);
2775 }
romanc1d2b092023-02-02 08:58:27 +01002776 return ret;
2777}
2778
2779static int
romane028ef92023-02-24 16:33:08 +01002780nc_server_config_pam_name(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002781{
roman5cbb6532023-06-22 12:53:17 +02002782 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002783 struct nc_client_auth *auth_client;
romanba93eac2023-07-18 14:36:48 +02002784 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002785
2786 assert(!strcmp(LYD_NAME(node), "pam-config-file-name"));
2787
romanba93eac2023-07-18 14:36:48 +02002788 /* LOCK */
2789 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2790 return 1;
2791 }
2792
roman4cb8bb12023-06-29 09:16:46 +02002793 if (nc_server_config_get_auth_client(node, &auth_client)) {
2794 ret = 1;
2795 goto cleanup;
2796 }
2797
2798 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2799 nc_server_config_del_auth_client_pam_name(auth_client);
2800
2801 auth_client->pam_config_name = strdup(lyd_get_value(node));
2802 if (!auth_client->pam_config_name) {
2803 ERRMEM;
romanc1d2b092023-02-02 08:58:27 +01002804 ret = 1;
2805 goto cleanup;
2806 }
roman4cb8bb12023-06-29 09:16:46 +02002807 } else {
2808 nc_server_config_del_auth_client_pam_name(auth_client);
romanc1d2b092023-02-02 08:58:27 +01002809 }
2810
2811cleanup:
romanba93eac2023-07-18 14:36:48 +02002812 if (is_ch(node)) {
2813 /* UNLOCK */
2814 nc_ch_client_unlock(ch_client);
2815 }
romanc1d2b092023-02-02 08:58:27 +01002816 return ret;
2817}
2818
2819static int
romane028ef92023-02-24 16:33:08 +01002820nc_server_config_pam_dir(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002821{
roman5cbb6532023-06-22 12:53:17 +02002822 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002823 struct nc_client_auth *auth_client;
romanba93eac2023-07-18 14:36:48 +02002824 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002825
2826 assert(!strcmp(LYD_NAME(node), "pam-config-file-dir"));
2827
romanba93eac2023-07-18 14:36:48 +02002828 /* LOCK */
2829 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2830 return 1;
2831 }
2832
roman4cb8bb12023-06-29 09:16:46 +02002833 if (nc_server_config_get_auth_client(node, &auth_client)) {
2834 ret = 1;
2835 goto cleanup;
2836 }
2837
2838 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2839 nc_server_config_del_auth_client_pam_dir(auth_client);
2840 auth_client->pam_config_dir = strdup(lyd_get_value(node));
2841 if (!auth_client->pam_config_dir) {
2842 ERRMEM;
romanc1d2b092023-02-02 08:58:27 +01002843 ret = 1;
2844 goto cleanup;
2845 }
roman4cb8bb12023-06-29 09:16:46 +02002846 } else {
2847 nc_server_config_del_auth_client_pam_dir(auth_client);
romanc1d2b092023-02-02 08:58:27 +01002848 }
2849
2850cleanup:
romanba93eac2023-07-18 14:36:48 +02002851 if (is_ch(node)) {
2852 /* UNLOCK */
2853 nc_ch_client_unlock(ch_client);
2854 }
romanc1d2b092023-02-02 08:58:27 +01002855 return ret;
2856}
2857
2858/* leaf */
2859static int
romane028ef92023-02-24 16:33:08 +01002860nc_server_config_none(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002861{
roman5cbb6532023-06-22 12:53:17 +02002862 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002863 struct nc_client_auth *auth_client;
romanba93eac2023-07-18 14:36:48 +02002864 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002865
2866 assert(!strcmp(LYD_NAME(node), "none"));
2867
romanba93eac2023-07-18 14:36:48 +02002868 /* LOCK */
2869 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2870 return 1;
2871 }
2872
roman4cb8bb12023-06-29 09:16:46 +02002873 if (nc_server_config_get_auth_client(node, &auth_client)) {
2874 ret = 1;
2875 goto cleanup;
2876 }
romanc1d2b092023-02-02 08:58:27 +01002877
roman4cb8bb12023-06-29 09:16:46 +02002878 if (op == NC_OP_CREATE) {
2879 auth_client->supports_none = 1;
2880 } else {
2881 auth_client->supports_none = 0;
romanc1d2b092023-02-02 08:58:27 +01002882 }
2883
2884cleanup:
romanba93eac2023-07-18 14:36:48 +02002885 if (is_ch(node)) {
2886 /* UNLOCK */
2887 nc_ch_client_unlock(ch_client);
2888 }
romanc1d2b092023-02-02 08:58:27 +01002889 return ret;
2890}
2891
2892static int
romana6bf6ab2023-05-26 13:26:02 +02002893nc_server_config_transport_params(const char *algorithm, char **alg_store, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002894{
2895 int ret = 0, alg_found = 0;
romana6bf6ab2023-05-26 13:26:02 +02002896 char *substr, *haystack, *alg = NULL;
2897 size_t alg_len;
2898
2899 if (!strncmp(algorithm, "openssh-", 8)) {
2900 /* if the name starts with openssh, convert it to it's original libssh accepted form */
2901 asprintf(&alg, "%s@openssh.com", algorithm + 8);
2902 if (!alg) {
2903 ERRMEM;
2904 ret = 1;
2905 goto cleanup;
2906 }
2907 } else if (!strncmp(algorithm, "libssh-", 7)) {
2908 /* if the name starts with libssh, convert it to it's original libssh accepted form */
2909 asprintf(&alg, "%s@libssh.org", algorithm + 7);
2910 if (!alg) {
2911 ERRMEM;
2912 ret = 1;
2913 goto cleanup;
2914 }
2915 } else {
2916 alg = strdup(algorithm);
2917 if (!alg) {
2918 ERRMEM;
2919 ret = 1;
2920 goto cleanup;
2921 }
2922 }
2923
2924 alg_len = strlen(alg);
romanc1d2b092023-02-02 08:58:27 +01002925
2926 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2927 if (!*alg_store) {
2928 /* first call */
2929 *alg_store = strdup(alg);
2930 if (!*alg_store) {
2931 ERRMEM;
2932 ret = 1;
2933 goto cleanup;
2934 }
2935 } else {
2936 /* +1 because of ',' between algorithms */
2937 *alg_store = nc_realloc(*alg_store, strlen(*alg_store) + alg_len + 1 + 1);
2938 if (!*alg_store) {
2939 ERRMEM;
2940 ret = 1;
2941 goto cleanup;
2942 }
roman08f67f42023-06-08 13:51:54 +02002943 strcat(*alg_store, ",");
2944 strcat(*alg_store, alg);
romanc1d2b092023-02-02 08:58:27 +01002945 }
2946 } else {
2947 /* delete */
2948 haystack = *alg_store;
2949 while ((substr = strstr(haystack, alg))) {
2950 /* iterate over all the substrings */
2951 if (((substr == haystack) && (*(substr + alg_len) == ',')) ||
2952 ((substr != haystack) && (*(substr - 1) == ',') && (*(substr + alg_len) == ','))) {
2953 /* either the first element of the string or somewhere in the middle */
2954 memmove(substr, substr + alg_len + 1, strlen(substr + alg_len + 1));
2955 alg_found = 1;
2956 break;
2957 } else if ((*(substr - 1) == ',') && (*(substr + alg_len) == '\0')) {
2958 /* the last element of the string */
2959 *(substr - 1) = '\0';
2960 alg_found = 1;
2961 break;
2962 }
2963 haystack++;
2964 }
2965 if (!alg_found) {
2966 ERR(NULL, "Unable to delete an algorithm (%s), which was not previously added.", alg);
2967 ret = 1;
2968 }
2969 }
2970
2971cleanup:
romana6bf6ab2023-05-26 13:26:02 +02002972 free(alg);
romanc1d2b092023-02-02 08:58:27 +01002973 return ret;
2974}
2975
2976/* leaf-list */
2977static int
romane028ef92023-02-24 16:33:08 +01002978nc_server_config_host_key_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002979{
roman5cbb6532023-06-22 12:53:17 +02002980 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01002981 const char *alg;
2982 uint8_t i;
roman5cbb6532023-06-22 12:53:17 +02002983 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02002984 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01002985
roman5cbb6532023-06-22 12:53:17 +02002986 assert(!strcmp(LYD_NAME(node), "host-key-alg"));
romanc1d2b092023-02-02 08:58:27 +01002987
romanba93eac2023-07-18 14:36:48 +02002988 /* LOCK */
2989 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
2990 return 1;
2991 }
2992
roman4cb8bb12023-06-29 09:16:46 +02002993 if (nc_server_config_get_ssh_opts(node, &opts)) {
2994 ret = 1;
2995 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01002996 }
2997
roman5cbb6532023-06-22 12:53:17 +02002998 /* get the algorithm name and compare it with algs supported by libssh */
2999 alg = ((struct lyd_node_term *)node)->value.ident->name;
romanc1d2b092023-02-02 08:58:27 +01003000 i = 0;
3001 while (supported_hostkey_algs[i]) {
3002 if (!strcmp(supported_hostkey_algs[i], alg)) {
roman5cbb6532023-06-22 12:53:17 +02003003 if (nc_server_config_transport_params(alg, &opts->hostkey_algs, op)) {
3004 ret = 1;
3005 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01003006 }
3007 break;
3008 }
3009 i++;
3010 }
3011 if (!supported_hostkey_algs[i]) {
3012 /* algorithm not supported */
3013 ERR(NULL, "Public key algorithm (%s) not supported by libssh.", alg);
3014 ret = 1;
3015 }
3016
3017cleanup:
romanba93eac2023-07-18 14:36:48 +02003018 if (is_ch(node)) {
3019 /* UNLOCK */
3020 nc_ch_client_unlock(ch_client);
3021 }
romanc1d2b092023-02-02 08:58:27 +01003022 return ret;
3023}
3024
3025/* leaf-list */
3026static int
romane028ef92023-02-24 16:33:08 +01003027nc_server_config_kex_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01003028{
roman5cbb6532023-06-22 12:53:17 +02003029 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01003030 const char *alg;
3031 uint8_t i;
roman5cbb6532023-06-22 12:53:17 +02003032 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02003033 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01003034
roman5cbb6532023-06-22 12:53:17 +02003035 assert(!strcmp(LYD_NAME(node), "key-exchange-alg"));
romanc1d2b092023-02-02 08:58:27 +01003036
romanba93eac2023-07-18 14:36:48 +02003037 /* LOCK */
3038 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3039 return 1;
3040 }
3041
roman4cb8bb12023-06-29 09:16:46 +02003042 if (nc_server_config_get_ssh_opts(node, &opts)) {
3043 ret = 1;
3044 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01003045 }
3046
roman5cbb6532023-06-22 12:53:17 +02003047 /* get the algorithm name and compare it with algs supported by libssh */
3048 alg = ((struct lyd_node_term *)node)->value.ident->name;
romanc1d2b092023-02-02 08:58:27 +01003049 i = 0;
3050 while (supported_kex_algs[i]) {
3051 if (!strcmp(supported_kex_algs[i], alg)) {
roman5cbb6532023-06-22 12:53:17 +02003052 if (nc_server_config_transport_params(alg, &opts->kex_algs, op)) {
3053 ret = 1;
3054 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01003055 }
3056 break;
3057 }
3058 i++;
3059 }
3060 if (!supported_kex_algs[i]) {
3061 /* algorithm not supported */
3062 ERR(NULL, "Key exchange algorithm (%s) not supported by libssh.", alg);
3063 ret = 1;
3064 }
3065
3066cleanup:
romanba93eac2023-07-18 14:36:48 +02003067 if (is_ch(node)) {
3068 /* UNLOCK */
3069 nc_ch_client_unlock(ch_client);
3070 }
romanc1d2b092023-02-02 08:58:27 +01003071 return ret;
3072}
3073
3074/* leaf-list */
3075static int
romane028ef92023-02-24 16:33:08 +01003076nc_server_config_encryption_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01003077{
roman5cbb6532023-06-22 12:53:17 +02003078 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01003079 const char *alg;
3080 uint8_t i;
roman5cbb6532023-06-22 12:53:17 +02003081 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02003082 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01003083
roman5cbb6532023-06-22 12:53:17 +02003084 assert(!strcmp(LYD_NAME(node), "encryption-alg"));
romanc1d2b092023-02-02 08:58:27 +01003085
romanba93eac2023-07-18 14:36:48 +02003086 /* LOCK */
3087 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3088 return 1;
3089 }
3090
roman4cb8bb12023-06-29 09:16:46 +02003091 if (nc_server_config_get_ssh_opts(node, &opts)) {
3092 ret = 1;
3093 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01003094 }
3095
roman5cbb6532023-06-22 12:53:17 +02003096 /* get the algorithm name and compare it with algs supported by libssh */
3097 alg = ((struct lyd_node_term *)node)->value.ident->name;
romanc1d2b092023-02-02 08:58:27 +01003098 i = 0;
3099 while (supported_encryption_algs[i]) {
3100 if (!strcmp(supported_encryption_algs[i], alg)) {
roman5cbb6532023-06-22 12:53:17 +02003101 if (nc_server_config_transport_params(alg, &opts->encryption_algs, op)) {
3102 ret = 1;
3103 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01003104 }
3105 break;
3106 }
3107 i++;
3108 }
3109 if (!supported_encryption_algs[i]) {
3110 /* algorithm not supported */
3111 ERR(NULL, "Encryption algorithm (%s) not supported by libssh.", alg);
3112 ret = 1;
3113 }
3114
3115cleanup:
romanba93eac2023-07-18 14:36:48 +02003116 if (is_ch(node)) {
3117 /* UNLOCK */
3118 nc_ch_client_unlock(ch_client);
3119 }
romanc1d2b092023-02-02 08:58:27 +01003120 return ret;
3121}
3122
3123/* leaf-list */
3124static int
romane028ef92023-02-24 16:33:08 +01003125nc_server_config_mac_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01003126{
roman5cbb6532023-06-22 12:53:17 +02003127 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01003128 const char *alg;
3129 uint8_t i;
roman5cbb6532023-06-22 12:53:17 +02003130 struct nc_server_ssh_opts *opts;
romanba93eac2023-07-18 14:36:48 +02003131 struct nc_ch_client *ch_client;
romanc1d2b092023-02-02 08:58:27 +01003132
roman5cbb6532023-06-22 12:53:17 +02003133 assert(!strcmp(LYD_NAME(node), "mac-alg"));
romanc1d2b092023-02-02 08:58:27 +01003134
romanba93eac2023-07-18 14:36:48 +02003135 /* LOCK */
3136 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3137 return 1;
3138 }
3139
roman4cb8bb12023-06-29 09:16:46 +02003140 if (nc_server_config_get_ssh_opts(node, &opts)) {
3141 ret = 1;
3142 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01003143 }
3144
roman5cbb6532023-06-22 12:53:17 +02003145 /* get the algorithm name and compare it with algs supported by libssh */
3146 alg = ((struct lyd_node_term *)node)->value.ident->name;
romanc1d2b092023-02-02 08:58:27 +01003147 i = 0;
3148 while (supported_mac_algs[i]) {
3149 if (!strcmp(supported_mac_algs[i], alg)) {
roman5cbb6532023-06-22 12:53:17 +02003150 if (nc_server_config_transport_params(alg, &opts->mac_algs, op)) {
3151 ret = 1;
3152 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01003153 }
3154 break;
3155 }
3156 i++;
3157 }
3158 if (!supported_mac_algs[i]) {
3159 /* algorithm not supported */
3160 ERR(NULL, "MAC algorithm (%s) not supported by libssh.", alg);
3161 ret = 1;
3162 }
3163
3164cleanup:
romanba93eac2023-07-18 14:36:48 +02003165 if (is_ch(node)) {
3166 /* UNLOCK */
3167 nc_ch_client_unlock(ch_client);
3168 }
romanc1d2b092023-02-02 08:58:27 +01003169 return ret;
3170}
3171
roman2eab4742023-06-06 10:00:26 +02003172#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +02003173
romanc1d2b092023-02-02 08:58:27 +01003174static int
roman874fed12023-05-25 10:20:01 +02003175nc_server_config_create_unix_socket(struct nc_endpt *endpt)
roman83683fb2023-02-24 09:15:23 +01003176{
3177 endpt->ti = NC_TI_UNIX;
3178 endpt->opts.unixsock = calloc(1, sizeof *endpt->opts.unixsock);
3179 if (!endpt->opts.unixsock) {
3180 ERRMEM;
3181 return 1;
3182 }
3183
3184 /* set default values */
3185 endpt->opts.unixsock->mode = -1;
3186 endpt->opts.unixsock->uid = -1;
3187 endpt->opts.unixsock->gid = -1;
3188
3189 return 0;
3190}
3191
3192static int
romane028ef92023-02-24 16:33:08 +01003193nc_server_config_unix_socket(const struct lyd_node *node, NC_OPERATION op)
roman83683fb2023-02-24 09:15:23 +01003194{
3195 int ret = 0;
3196 uint32_t prev_lo;
3197 struct nc_endpt *endpt;
3198 struct nc_bind *bind;
3199 struct nc_server_unix_opts *opts;
3200 struct lyd_node *data = NULL;
3201
3202 assert(!strcmp(LYD_NAME(node), "unix-socket"));
3203
romanf02273a2023-05-25 09:44:11 +02003204 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
roman83683fb2023-02-24 09:15:23 +01003205 ret = 1;
3206 goto cleanup;
3207 }
3208
3209 if (op == NC_OP_CREATE) {
roman874fed12023-05-25 10:20:01 +02003210 if (nc_server_config_create_unix_socket(endpt)) {
roman83683fb2023-02-24 09:15:23 +01003211 ret = 1;
3212 goto cleanup;
3213 }
3214
3215 opts = endpt->opts.unixsock;
3216
3217 lyd_find_path(node, "path", 0, &data);
3218 assert(data);
3219
3220 opts->address = strdup(lyd_get_value(data));
3221 bind->address = strdup(lyd_get_value(data));
3222 if (!opts->address || !bind->address) {
3223 ERRMEM;
3224 ret = 1;
3225 goto cleanup;
3226 }
3227
3228 /* silently search for non-mandatory parameters */
3229 prev_lo = ly_log_options(0);
3230 ret = lyd_find_path(node, "mode", 0, &data);
3231 if (!ret) {
3232 opts->mode = strtol(lyd_get_value(data), NULL, 8);
3233 }
3234
3235 ret = lyd_find_path(node, "uid", 0, &data);
3236 if (!ret) {
3237 opts->uid = strtol(lyd_get_value(data), NULL, 10);
3238 }
3239
3240 ret = lyd_find_path(node, "gid", 0, &data);
3241 if (!ret) {
3242 opts->gid = strtol(lyd_get_value(data), NULL, 10);
3243 }
3244
3245 /* reset the logging options */
3246 ly_log_options(prev_lo);
3247
3248 ret = nc_server_config_set_address_port(endpt, bind, NULL, 0);
3249 if (ret) {
3250 goto cleanup;
3251 }
3252 } else if (op == NC_OP_DELETE) {
roman874fed12023-05-25 10:20:01 +02003253 nc_server_config_del_unix_socket(bind, endpt->opts.unixsock);
roman83683fb2023-02-24 09:15:23 +01003254 }
3255
3256cleanup:
3257 return ret;
3258}
3259
roman2eab4742023-06-06 10:00:26 +02003260#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +02003261
roman0bbc19c2023-05-26 09:59:09 +02003262/**
3263 * @brief Set all endpoint client auth references, which couldn't be set beforehand.
3264 *
3265 * The references that could not be set are those, which reference endpoints, which
3266 * lie below the given endpoint in the YANG data (because of DFS tree parsing).
3267 *
3268 * @return 0 on success, 1 on error.
3269 */
3270static int
3271nc_server_config_fill_endpt_client_auth(void)
3272{
3273 uint16_t i, j;
3274
3275 for (i = 0; i < server_opts.endpt_count; i++) {
3276 /* go through all the endpoint */
3277 if (server_opts.endpts[i].referenced_endpt_name) {
3278 /* endpt has a reference, that hasn't been set yet */
3279 for (j = i + 1; j < server_opts.endpt_count; j++) {
3280 /* go through all the remaining endpts */
3281 if (!strcmp(server_opts.endpts[i].referenced_endpt_name, server_opts.endpts[j].name)) {
3282 /* found the endpoint we were looking for */
3283 if (server_opts.endpts[i].ti == NC_TI_LIBSSH) {
3284 server_opts.endpts[i].opts.ssh->endpt_client_ref = &server_opts.endpts[j];
3285 break;
roman2e797ef2023-06-19 10:47:49 +02003286 } else if (server_opts.endpts[i].ti == NC_TI_OPENSSL) {
3287 server_opts.endpts[i].opts.tls->endpt_client_ref = &server_opts.endpts[j];
3288 break;
roman0bbc19c2023-05-26 09:59:09 +02003289 } else {
3290 ERRINT;
3291 return 1;
3292 }
3293 }
3294 }
3295
3296 /* didn't find the endpoint */
3297 if (j == server_opts.endpt_count) {
3298 ERR(NULL, "Endpoint \"%s\" referenced by \"%s\" not found.",
3299 server_opts.endpts[i].referenced_endpt_name, server_opts.endpts[i].name);
3300 return 1;
3301 }
3302 }
3303 }
3304
3305 return 0;
3306}
3307
3308static int
roman2e797ef2023-06-19 10:47:49 +02003309nc_server_config_endpoint_client_auth_has_cycle(struct nc_endpt *original, struct nc_endpt *next)
roman0bbc19c2023-05-26 09:59:09 +02003310{
roman2e797ef2023-06-19 10:47:49 +02003311 if (original->ti == NC_TI_LIBSSH) {
3312 if (!next->opts.ssh->endpt_client_ref) {
3313 /* no further reference -> no cycle */
roman0bbc19c2023-05-26 09:59:09 +02003314 return 0;
3315 }
roman2e797ef2023-06-19 10:47:49 +02003316
3317 if (next->opts.ssh->endpt_client_ref == original) {
3318 /* found cycle */
3319 return 1;
3320 } else {
3321 /* continue further */
3322 return nc_server_config_endpoint_client_auth_has_cycle(original, next->opts.ssh->endpt_client_ref);
3323 }
3324 } else if (original->ti == NC_TI_OPENSSL) {
3325 if (!next->opts.tls->endpt_client_ref) {
3326 /* no further reference -> no cycle */
3327 return 0;
3328 }
3329
3330 if (next->opts.tls->endpt_client_ref == original) {
3331 /* found cycle */
3332 return 1;
3333 } else {
3334 /* continue further */
3335 return nc_server_config_endpoint_client_auth_has_cycle(original, next->opts.tls->endpt_client_ref);
3336 }
roman0bbc19c2023-05-26 09:59:09 +02003337 } else {
3338 ERRINT;
3339 return 1;
3340 }
3341}
3342
3343static int
3344nc_server_config_endpoint_client_auth(const struct lyd_node *node, NC_OPERATION op)
3345{
3346 int ret = 0;
3347 uint16_t i;
3348 const char *endpt_name;
3349 struct nc_endpt *endpt;
3350
3351 assert(!strcmp(LYD_NAME(node), "endpoint-client-auth"));
3352
3353 /* get current endpoint */
3354 ret = nc_server_config_get_endpt(node, &endpt, NULL);
3355 if (ret) {
3356 goto cleanup;
3357 }
3358
3359 if (op == NC_OP_DELETE) {
roman2e797ef2023-06-19 10:47:49 +02003360 if (is_ssh(node)) {
3361 endpt->opts.ssh->endpt_client_ref = NULL;
3362 } else {
3363 endpt->opts.tls->endpt_client_ref = NULL;
3364 }
roman0bbc19c2023-05-26 09:59:09 +02003365 goto cleanup;
3366 }
3367
3368 /* find the endpoint leafref is referring to */
3369 endpt_name = lyd_get_value(node);
3370 for (i = 0; i < server_opts.endpt_count; i++) {
3371 if (!strcmp(endpt_name, server_opts.endpts[i].name)) {
3372 break;
3373 }
3374 }
3375
3376 if (i == server_opts.endpt_count) {
3377 /* endpt not found, save the name and try to look it up later */
roman2e797ef2023-06-19 10:47:49 +02003378 nc_server_config_del_endpt_reference(endpt);
roman0bbc19c2023-05-26 09:59:09 +02003379 endpt->referenced_endpt_name = strdup(endpt_name);
3380 if (!endpt->referenced_endpt_name) {
3381 ERRMEM;
3382 ret = 1;
roman0bbc19c2023-05-26 09:59:09 +02003383 }
3384 goto cleanup;
3385 }
3386
3387 /* check for self reference */
3388 if (endpt == &server_opts.endpts[i]) {
3389 ERR(NULL, "Self client authentication reference detected.");
3390 ret = 1;
3391 goto cleanup;
3392 }
3393
3394 /* check for cyclic references */
roman2e797ef2023-06-19 10:47:49 +02003395 ret = nc_server_config_endpoint_client_auth_has_cycle(endpt, &server_opts.endpts[i]);
roman0bbc19c2023-05-26 09:59:09 +02003396 if (ret) {
3397 ERR(NULL, "Cyclic client authentication reference detected.");
3398 goto cleanup;
3399 }
3400
3401 /* assign the current endpt the referrenced endpt */
roman2e797ef2023-06-19 10:47:49 +02003402 if (is_ssh(node)) {
3403 endpt->opts.ssh->endpt_client_ref = &server_opts.endpts[i];
3404 } else {
3405 endpt->opts.tls->endpt_client_ref = &server_opts.endpts[i];
3406 }
roman0bbc19c2023-05-26 09:59:09 +02003407
3408cleanup:
3409 return ret;
3410}
3411
roman3f9b65c2023-06-05 14:26:58 +02003412static int
3413nc_server_config_tls_replace_cert_data(const struct lyd_node *node, struct nc_server_tls_opts *opts)
3414{
3415 nc_server_config_tls_del_cert_data(opts);
3416 opts->cert_data = strdup(lyd_get_value(node));
3417 if (!opts->cert_data) {
3418 ERRMEM;
3419 return 1;
3420 }
3421
3422 return 0;
3423}
3424
3425static int
3426nc_server_config_tls_replace_cert_data_client_auth(const struct lyd_node *node, struct nc_certificate *cert)
3427{
3428 nc_server_config_tls_del_cert_data_certificate(cert);
3429 cert->data = strdup(lyd_get_value(node));
3430 if (!cert->data) {
3431 ERRMEM;
3432 return 1;
3433 }
3434
3435 return 0;
3436}
3437
3438static int
3439nc_server_config_cert_data(const struct lyd_node *node, NC_OPERATION op)
3440{
3441 int ret = 0;
roman3f9b65c2023-06-05 14:26:58 +02003442 struct nc_certificate *cert;
romanb6f44032023-06-30 15:07:56 +02003443 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02003444 struct nc_ch_client *ch_client;
roman3f9b65c2023-06-05 14:26:58 +02003445
3446 assert(!strcmp(LYD_NAME(node), "cert-data"));
3447
romanba93eac2023-07-18 14:36:48 +02003448 /* LOCK */
3449 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3450 return 1;
3451 }
3452
romanb6f44032023-06-30 15:07:56 +02003453 if (equal_parent_name(node, 3, "server-identity")) {
3454 if (nc_server_config_get_tls_opts(node, &opts)) {
3455 ret = 1;
3456 goto cleanup;
3457 }
roman3f9b65c2023-06-05 14:26:58 +02003458
roman3f9b65c2023-06-05 14:26:58 +02003459 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02003460 ret = nc_server_config_tls_replace_cert_data(node, opts);
roman3f9b65c2023-06-05 14:26:58 +02003461 if (ret) {
3462 goto cleanup;
3463 }
3464 }
romanb6f44032023-06-30 15:07:56 +02003465 } else if (equal_parent_name(node, 3, "ca-certs")) {
roman4cb8bb12023-06-29 09:16:46 +02003466 if (nc_server_config_get_cert(node, 0, &cert)) {
roman3f9b65c2023-06-05 14:26:58 +02003467 ret = 1;
3468 goto cleanup;
3469 }
3470
3471 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3472 ret = nc_server_config_tls_replace_cert_data_client_auth(node, cert);
3473 if (ret) {
3474 goto cleanup;
3475 }
3476 } else {
3477 nc_server_config_tls_del_cert_data_certificate(cert);
3478 }
romanb6f44032023-06-30 15:07:56 +02003479 } else if (equal_parent_name(node, 3, "ee-certs")) {
roman4cb8bb12023-06-29 09:16:46 +02003480 if (nc_server_config_get_cert(node, 1, &cert)) {
roman3f9b65c2023-06-05 14:26:58 +02003481 ret = 1;
3482 goto cleanup;
3483 }
3484
3485 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3486 ret = nc_server_config_tls_replace_cert_data_client_auth(node, cert);
3487 if (ret) {
3488 goto cleanup;
3489 }
3490 } else {
3491 nc_server_config_tls_del_cert_data_certificate(cert);
3492 }
3493 }
3494
3495cleanup:
romanba93eac2023-07-18 14:36:48 +02003496 if (is_ch(node)) {
3497 /* UNLOCK */
3498 nc_ch_client_unlock(ch_client);
3499 }
roman3f9b65c2023-06-05 14:26:58 +02003500 return ret;
3501}
3502
3503static int
3504nc_server_config_tls_create_asymmetric_key_ref(const struct lyd_node *node, struct nc_endpt *endpt)
3505{
3506 uint16_t i;
3507 struct nc_keystore *ks = &server_opts.keystore;
3508
3509 /* lookup name */
3510 for (i = 0; i < ks->asym_key_count; i++) {
3511 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
3512 break;
3513 }
3514 }
3515
3516 if (i == ks->asym_key_count) {
3517 ERR(NULL, "Asymmetric key \"%s\" not found in the keystore.", lyd_get_value(node));
3518 return 1;
3519 }
3520
3521 endpt->opts.tls->key_ref = &ks->asym_keys[i];
3522
3523 return 0;
3524}
3525
3526static int
3527nc_server_config_asymmetric_key(const struct lyd_node *node, NC_OPERATION op)
3528{
3529 int ret = 0;
3530 struct nc_endpt *endpt;
romanba93eac2023-07-18 14:36:48 +02003531 struct nc_ch_client *ch_client;
roman3f9b65c2023-06-05 14:26:58 +02003532
3533 assert(!strcmp(LYD_NAME(node), "asymmetric-key"));
3534
romanba93eac2023-07-18 14:36:48 +02003535 /* LOCK */
3536 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3537 return 1;
3538 }
3539
roman3f9b65c2023-06-05 14:26:58 +02003540 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
3541 ret = 1;
3542 goto cleanup;
3543 }
3544
3545 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3546 /* set to keystore */
3547 endpt->opts.tls->store = NC_STORE_KEYSTORE;
3548
3549 ret = nc_server_config_tls_create_asymmetric_key_ref(node, endpt);
3550 if (ret) {
3551 goto cleanup;
3552 }
3553 } else {
3554 endpt->opts.tls->key_ref = NULL;
3555 }
3556
3557cleanup:
romanba93eac2023-07-18 14:36:48 +02003558 if (is_ch(node)) {
3559 /* UNLOCK */
3560 nc_ch_client_unlock(ch_client);
3561 }
roman3f9b65c2023-06-05 14:26:58 +02003562 return ret;
3563}
3564
3565static int
romanb6f44032023-06-30 15:07:56 +02003566nc_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 +02003567{
3568 uint16_t i;
3569
3570 /* lookup name */
3571 for (i = 0; i < key->cert_count; i++) {
3572 if (!strcmp(lyd_get_value(node), key->certs[i].name)) {
3573 break;
3574 }
3575 }
3576
3577 if (i == key->cert_count) {
3578 ERR(NULL, "Certificate \"%s\" not found in the asymmetric key \"%s\".", lyd_get_value(node), key->name);
3579 return 1;
3580 }
3581
romanb6f44032023-06-30 15:07:56 +02003582 opts->cert_ref = &key->certs[i];
roman3f9b65c2023-06-05 14:26:58 +02003583
3584 return 0;
3585}
3586
3587static struct nc_asymmetric_key *
romanb6f44032023-06-30 15:07:56 +02003588nc_server_config_cert_get_asymmetric_key(const struct lyd_node *node)
roman3f9b65c2023-06-05 14:26:58 +02003589{
3590 uint16_t i;
3591 struct nc_keystore *ks = &server_opts.keystore;
3592
3593 /* starting with certificate node */
3594 assert(!strcmp(LYD_NAME(node), "certificate"));
3595
3596 /* switch to it's only sibling, must be asymmetric-key */
3597 node = node->prev;
3598 assert(!strcmp(LYD_NAME(node), "asymmetric-key"));
3599
3600 /* find the given asymmetric key */
3601 for (i = 0; i < ks->asym_key_count; i++) {
3602 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
3603 return &ks->asym_keys[i];
3604 }
3605 }
3606
3607 /* didn't find it */
3608 ERR(NULL, "Asymmetric key \"%s\" not found in the keystore.", lyd_get_value(node));
3609 return NULL;
3610}
3611
3612static int
3613nc_server_config_create_ca_certs_certificate(const struct lyd_node *node, struct nc_server_tls_opts *opts)
3614{
3615 assert(!strcmp(LYD_NAME(node), "certificate"));
3616
3617 node = lyd_child(node);
3618 assert(!strcmp(LYD_NAME(node), "name"));
3619
3620 return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->ca_certs.certs, sizeof *opts->ca_certs.certs, &opts->ca_certs.cert_count);
3621}
3622
3623static int
3624nc_server_config_create_ee_certs_certificate(const struct lyd_node *node, struct nc_server_tls_opts *opts)
3625{
3626 assert(!strcmp(LYD_NAME(node), "certificate"));
3627
3628 node = lyd_child(node);
3629 assert(!strcmp(LYD_NAME(node), "name"));
3630
3631 return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->ee_certs.certs, sizeof *opts->ee_certs.certs, &opts->ee_certs.cert_count);
3632}
3633
3634static int
3635nc_server_config_certificate(const struct lyd_node *node, NC_OPERATION op)
3636{
3637 int ret = 0;
roman3f9b65c2023-06-05 14:26:58 +02003638 struct nc_asymmetric_key *key;
romanb6f44032023-06-30 15:07:56 +02003639 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02003640 struct nc_ch_client *ch_client;
roman3f9b65c2023-06-05 14:26:58 +02003641
3642 assert(!strcmp(LYD_NAME(node), "certificate"));
3643
romanba93eac2023-07-18 14:36:48 +02003644 /* LOCK */
3645 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3646 return 1;
3647 }
3648
romanb6f44032023-06-30 15:07:56 +02003649 if (nc_server_config_get_tls_opts(node, &opts)) {
roman3f9b65c2023-06-05 14:26:58 +02003650 ret = 1;
3651 goto cleanup;
3652 }
3653
romanb6f44032023-06-30 15:07:56 +02003654 if (equal_parent_name(node, 1, "keystore-reference")) {
3655 /* TLS server-identity */
roman3f9b65c2023-06-05 14:26:58 +02003656 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3657 /* set to keystore */
romanb6f44032023-06-30 15:07:56 +02003658 opts->store = NC_STORE_KEYSTORE;
roman3f9b65c2023-06-05 14:26:58 +02003659
romanb6f44032023-06-30 15:07:56 +02003660 if (!opts->key_ref) {
roman3f9b65c2023-06-05 14:26:58 +02003661 /* we don't have a key from which we need the cert yet */
romanb6f44032023-06-30 15:07:56 +02003662 key = nc_server_config_cert_get_asymmetric_key(node);
roman3f9b65c2023-06-05 14:26:58 +02003663 if (!key) {
3664 ret = 1;
3665 goto cleanup;
3666 }
3667 } else {
3668 /* we have the key */
romanb6f44032023-06-30 15:07:56 +02003669 key = opts->key_ref;
roman3f9b65c2023-06-05 14:26:58 +02003670 }
3671
3672 /* find the given cert in the key and set it */
romanb6f44032023-06-30 15:07:56 +02003673 ret = nc_server_config_tls_create_certificate_ref(node, opts, key);
roman3f9b65c2023-06-05 14:26:58 +02003674 if (ret) {
3675 goto cleanup;
3676 }
3677 } else {
romanb6f44032023-06-30 15:07:56 +02003678 opts->cert_ref = NULL;
roman3f9b65c2023-06-05 14:26:58 +02003679 }
romanb6f44032023-06-30 15:07:56 +02003680 } else if (equal_parent_name(node, 2, "ca-certs")) {
3681 /* TLS client auth certificate authority */
roman3f9b65c2023-06-05 14:26:58 +02003682 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02003683 ret = nc_server_config_create_ca_certs_certificate(node, opts);
roman3f9b65c2023-06-05 14:26:58 +02003684 if (ret) {
3685 goto cleanup;
3686 }
3687 } else {
romanb6f44032023-06-30 15:07:56 +02003688 nc_server_config_tls_del_certs(&opts->ca_certs);
roman3f9b65c2023-06-05 14:26:58 +02003689 }
romanb6f44032023-06-30 15:07:56 +02003690 } else if (equal_parent_name(node, 2, "ee-certs")) {
3691 /* TLS client auth end entity */
roman3f9b65c2023-06-05 14:26:58 +02003692 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02003693 ret = nc_server_config_create_ee_certs_certificate(node, opts);
roman3f9b65c2023-06-05 14:26:58 +02003694 if (ret) {
3695 goto cleanup;
3696 }
3697 } else {
romanb6f44032023-06-30 15:07:56 +02003698 nc_server_config_tls_del_certs(&opts->ee_certs);
roman3f9b65c2023-06-05 14:26:58 +02003699 }
3700 }
3701
3702cleanup:
romanba93eac2023-07-18 14:36:48 +02003703 if (is_ch(node)) {
3704 /* UNLOCK */
3705 nc_ch_client_unlock(ch_client);
3706 }
roman3f9b65c2023-06-05 14:26:58 +02003707 return ret;
3708}
3709
3710static int
3711nc_server_config_create_cert_to_name(const struct lyd_node *node, struct nc_server_tls_opts *opts)
3712{
3713 int ret = 0;
3714 struct lyd_node *n;
3715 struct nc_ctn *new, *iter;
3716 const char *map_type, *name;
3717 uint32_t id;
3718 NC_TLS_CTN_MAPTYPE m_type;
3719
3720 assert(!strcmp(LYD_NAME(node), "cert-to-name"));
3721
3722 /* create new ctn */
3723 new = calloc(1, sizeof *new);
3724 if (!new) {
3725 ERRMEM;
3726 ret = 1;
3727 goto cleanup;
3728 }
3729
3730 /* get all the data */
3731 /* find the list's key */
3732 lyd_find_path(node, "id", 0, &n);
3733 assert(n);
3734 id = strtoul(lyd_get_value(n), NULL, 10);
3735
3736 /* find the ctn's name */
3737 lyd_find_path(node, "name", 0, &n);
3738 assert(n);
3739 name = lyd_get_value(n);
3740
3741 /* find the ctn's map-type */
3742 lyd_find_path(node, "map-type", 0, &n);
3743 assert(n);
3744 map_type = ((struct lyd_node_term *)n)->value.ident->name;
3745 if (!strcmp(map_type, "specified")) {
3746 m_type = NC_TLS_CTN_SPECIFIED;
3747 } else if (!strcmp(map_type, "san-rfc822-name")) {
3748 m_type = NC_TLS_CTN_SAN_RFC822_NAME;
3749 } else if (!strcmp(map_type, "san-dns-name")) {
3750 m_type = NC_TLS_CTN_SAN_DNS_NAME;
3751 } else if (!strcmp(map_type, "san-ip-address")) {
3752 m_type = NC_TLS_CTN_SAN_IP_ADDRESS;
3753 } else if (!strcmp(map_type, "san-any")) {
3754 m_type = NC_TLS_CTN_SAN_ANY;
3755 } else if (!strcmp(map_type, "common-name")) {
3756 m_type = NC_TLS_CTN_COMMON_NAME;
3757 } else {
3758 ERR(NULL, "Map-type identity \"%s\" not supported.", map_type);
3759 ret = 1;
3760 goto cleanup;
3761 }
3762
3763 /* find the right place for insertion */
3764 if (!opts->ctn) {
3765 /* inserting the first one */
3766 opts->ctn = new;
Roytak421eb0c2023-08-01 22:18:27 +02003767 } else if (opts->ctn->id > id) {
roman3f9b65c2023-06-05 14:26:58 +02003768 /* insert at the beginning */
3769 new->next = opts->ctn;
3770 opts->ctn = new;
3771 } else {
3772 /* have to find the right place */
Roytak421eb0c2023-08-01 22:18:27 +02003773 for (iter = opts->ctn; iter->next && iter->next->id <= id; iter = iter->next) {}
3774 if (iter->id == id) {
roman3f9b65c2023-06-05 14:26:58 +02003775 /* collision */
3776 new = iter;
3777 } else {
3778 new->next = iter->next;
3779 iter->next = new;
3780 }
3781 }
3782
3783 /* insert the right data */
3784 new->id = id;
3785 if (new->name) {
3786 free(new->name);
3787 }
3788 new->name = strdup(name);
3789 if (!new->name) {
3790 ERRMEM;
3791 ret = 1;
3792 goto cleanup;
3793 }
3794 new->map_type = m_type;
3795
3796cleanup:
3797 return ret;
3798}
3799
3800static int
3801nc_server_config_cert_to_name(const struct lyd_node *node, NC_OPERATION op)
3802{
3803 int ret = 0;
romanb6f44032023-06-30 15:07:56 +02003804 struct nc_server_tls_opts *opts;
roman3f9b65c2023-06-05 14:26:58 +02003805 struct lyd_node *key;
3806 struct nc_ctn *ctn;
romanba93eac2023-07-18 14:36:48 +02003807 struct nc_ch_client *ch_client;
roman3f9b65c2023-06-05 14:26:58 +02003808
3809 assert(!strcmp(LYD_NAME(node), "cert-to-name"));
3810
romanba93eac2023-07-18 14:36:48 +02003811 /* LOCK */
3812 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3813 return 1;
3814 }
3815
romanb6f44032023-06-30 15:07:56 +02003816 if (nc_server_config_get_tls_opts(node, &opts)) {
roman3f9b65c2023-06-05 14:26:58 +02003817 ret = 1;
3818 goto cleanup;
3819 }
3820
3821 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02003822 ret = nc_server_config_create_cert_to_name(node, opts);
roman3f9b65c2023-06-05 14:26:58 +02003823 if (ret) {
3824 goto cleanup;
3825 }
3826 } else {
3827 /* find the given ctn entry */
3828 lyd_find_path(node, "id", 0, &key);
3829 assert(key);
roman4cb8bb12023-06-29 09:16:46 +02003830 if (nc_server_config_get_ctn(node, &ctn)) {
roman3f9b65c2023-06-05 14:26:58 +02003831 ret = 1;
3832 goto cleanup;
3833 }
romanb6f44032023-06-30 15:07:56 +02003834 nc_server_config_del_ctn(opts, ctn);
roman3f9b65c2023-06-05 14:26:58 +02003835 }
3836
3837cleanup:
romanba93eac2023-07-18 14:36:48 +02003838 if (is_ch(node)) {
3839 /* UNLOCK */
3840 nc_ch_client_unlock(ch_client);
3841 }
roman3f9b65c2023-06-05 14:26:58 +02003842 return ret;
3843}
3844
3845static int
3846nc_server_config_replace_fingerprint(const struct lyd_node *node, struct nc_ctn *ctn)
3847{
3848 nc_server_config_del_fingerprint(ctn);
3849
3850 ctn->fingerprint = strdup(lyd_get_value(node));
3851 if (!ctn->fingerprint) {
3852 ERRMEM;
3853 return 1;
3854 }
3855
3856 return 0;
3857}
3858
3859static int
3860nc_server_config_fingerprint(const struct lyd_node *node, NC_OPERATION op)
3861{
3862 int ret = 0;
roman3f9b65c2023-06-05 14:26:58 +02003863 struct nc_ctn *ctn;
romanba93eac2023-07-18 14:36:48 +02003864 struct nc_ch_client *ch_client;
3865
3866 assert(!strcmp(LYD_NAME(node), "fingerprint"));
3867
3868 /* LOCK */
3869 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3870 return 1;
3871 }
roman3f9b65c2023-06-05 14:26:58 +02003872
roman4cb8bb12023-06-29 09:16:46 +02003873 if (nc_server_config_get_ctn(node, &ctn)) {
roman3f9b65c2023-06-05 14:26:58 +02003874 ret = 1;
3875 goto cleanup;
3876 }
3877
3878 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3879 ret = nc_server_config_replace_fingerprint(node, ctn);
3880 if (ret) {
3881 goto cleanup;
3882 }
3883 } else {
3884 nc_server_config_del_fingerprint(ctn);
3885 }
3886
3887cleanup:
romanba93eac2023-07-18 14:36:48 +02003888 if (is_ch(node)) {
3889 /* UNLOCK */
3890 nc_ch_client_unlock(ch_client);
3891 }
roman3f9b65c2023-06-05 14:26:58 +02003892 return ret;
3893}
3894
roman12644fe2023-06-08 11:06:42 +02003895static void
3896nc_server_config_set_tls_version(struct nc_server_tls_opts *opts, NC_TLS_VERSION version, NC_OPERATION op)
3897{
3898 if (op == NC_OP_CREATE) {
3899 /* add the version if it isn't there already */
3900 opts->tls_versions |= version;
3901 } else if ((op == NC_OP_DELETE) && (opts->tls_versions & version)) {
3902 /* delete the version if it is there */
3903 opts->tls_versions -= version;
3904 }
3905}
3906
3907static int
3908nc_server_config_tls_version(const struct lyd_node *node, NC_OPERATION op)
3909{
3910 int ret = 0;
romanb6f44032023-06-30 15:07:56 +02003911 struct nc_server_tls_opts *opts;
roman12644fe2023-06-08 11:06:42 +02003912 const char *version = NULL;
romanba93eac2023-07-18 14:36:48 +02003913 struct nc_ch_client *ch_client;
roman12644fe2023-06-08 11:06:42 +02003914
3915 assert(!strcmp(LYD_NAME(node), "tls-version"));
3916
romanba93eac2023-07-18 14:36:48 +02003917 /* LOCK */
3918 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
3919 return 1;
3920 }
3921
romanb6f44032023-06-30 15:07:56 +02003922 if (nc_server_config_get_tls_opts(node, &opts)) {
roman12644fe2023-06-08 11:06:42 +02003923 ret = 1;
3924 goto cleanup;
3925 }
3926
3927 version = ((struct lyd_node_term *)node)->value.ident->name;
3928 if (!strcmp(version, "tls10")) {
romanb6f44032023-06-30 15:07:56 +02003929 nc_server_config_set_tls_version(opts, NC_TLS_VERSION_10, op);
roman12644fe2023-06-08 11:06:42 +02003930 } else if (!strcmp(version, "tls11")) {
romanb6f44032023-06-30 15:07:56 +02003931 nc_server_config_set_tls_version(opts, NC_TLS_VERSION_11, op);
roman12644fe2023-06-08 11:06:42 +02003932 } else if (!strcmp(version, "tls12")) {
romanb6f44032023-06-30 15:07:56 +02003933 nc_server_config_set_tls_version(opts, NC_TLS_VERSION_12, op);
roman12644fe2023-06-08 11:06:42 +02003934 } else if (!strcmp(version, "tls13")) {
romanb6f44032023-06-30 15:07:56 +02003935 nc_server_config_set_tls_version(opts, NC_TLS_VERSION_13, op);
roman12644fe2023-06-08 11:06:42 +02003936 } else {
3937 ERR(NULL, "TLS version \"%s\" not supported.", version);
3938 ret = 1;
3939 goto cleanup;
3940 }
3941
3942cleanup:
romanba93eac2023-07-18 14:36:48 +02003943 if (is_ch(node)) {
3944 /* UNLOCK */
3945 nc_ch_client_unlock(ch_client);
3946 }
roman12644fe2023-06-08 11:06:42 +02003947 return ret;
3948}
3949
3950static int
3951nc_server_config_create_cipher_suite(struct nc_server_tls_opts *opts, const char *cipher)
3952{
3953 int ret = 0;
3954 char *ssl_cipher = NULL;
3955 uint16_t i;
3956
3957 ssl_cipher = malloc(strlen(cipher) + 1);
3958 if (!ssl_cipher) {
3959 ERRMEM;
3960 ret = 1;
3961 goto cleanup;
3962 }
3963
3964 for (i = 0; cipher[i]; i++) {
3965 if (cipher[i] == '-') {
3966 /* OpenSSL requires _ instead of - in cipher names */
3967 ssl_cipher[i] = '_';
3968 } else {
3969 /* and requires uppercase unlike the identities */
3970 ssl_cipher[i] = toupper(cipher[i]);
3971 }
3972 }
3973 ssl_cipher[i] = '\0';
3974
3975 if (!opts->ciphers) {
3976 /* first entry */
3977 opts->ciphers = strdup(ssl_cipher);
3978 if (!opts->ciphers) {
3979 ERRMEM;
3980 ret = 1;
3981 goto cleanup;
3982 }
3983 } else {
3984 /* + 1 because of : between entries */
3985 opts->ciphers = nc_realloc(opts->ciphers, strlen(opts->ciphers) + strlen(ssl_cipher) + 1 + 1);
3986 if (!opts->ciphers) {
3987 ERRMEM;
3988 ret = 1;
3989 goto cleanup;
3990 }
roman08f67f42023-06-08 13:51:54 +02003991 strcat(opts->ciphers, ":");
3992 strcat(opts->ciphers, ssl_cipher);
roman12644fe2023-06-08 11:06:42 +02003993 }
3994
3995cleanup:
3996 free(ssl_cipher);
3997 return ret;
3998}
3999
4000static int
romanb6f44032023-06-30 15:07:56 +02004001nc_server_config_del_cipher_suite(struct nc_server_tls_opts *opts, const char *cipher)
roman12644fe2023-06-08 11:06:42 +02004002{
4003 int cipher_found = 0;
4004 char *haystack, *substr;
4005 size_t cipher_len = strlen(cipher);
4006
4007 /* delete */
4008 haystack = opts->ciphers;
4009 while ((substr = strstr(haystack, cipher))) {
4010 /* iterate over all the substrings */
4011 if (((substr == haystack) && (*(substr + cipher_len) == ':')) ||
4012 ((substr != haystack) && (*(substr - 1) == ':') && (*(substr + cipher_len) == ':'))) {
4013 /* either the first element of the string or somewhere in the middle */
4014 memmove(substr, substr + cipher_len + 1, strlen(substr + cipher_len + 1));
4015 cipher_found = 1;
4016 break;
4017 } else if ((*(substr - 1) == ':') && (*(substr + cipher_len) == '\0')) {
4018 /* the last element of the string */
4019 *(substr - 1) = '\0';
4020 cipher_found = 1;
4021 break;
4022 }
4023 haystack++;
4024 }
4025
4026 if (!cipher_found) {
4027 ERR(NULL, "Unable to delete a cipher (%s), which was not previously added.", cipher);
4028 return 1;
4029 }
4030
4031 return 0;
4032}
4033
4034static int
4035nc_server_config_cipher_suite(const struct lyd_node *node, NC_OPERATION op)
4036{
4037 int ret = 0;
romanb6f44032023-06-30 15:07:56 +02004038 struct nc_server_tls_opts *opts;
roman12644fe2023-06-08 11:06:42 +02004039 const char *cipher = NULL;
romanba93eac2023-07-18 14:36:48 +02004040 struct nc_ch_client *ch_client;
roman12644fe2023-06-08 11:06:42 +02004041
romanfaecc582023-06-15 16:13:31 +02004042 assert(!strcmp(LYD_NAME(node), "cipher-suite"));
4043
romanba93eac2023-07-18 14:36:48 +02004044 /* LOCK */
4045 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4046 return 1;
4047 }
4048
romanb6f44032023-06-30 15:07:56 +02004049 if (nc_server_config_get_tls_opts(node, &opts)) {
roman12644fe2023-06-08 11:06:42 +02004050 ret = 1;
4051 goto cleanup;
4052 }
4053
4054 cipher = ((struct lyd_node_term *)node)->value.ident->name;
4055 if (op == NC_OP_CREATE) {
romanb6f44032023-06-30 15:07:56 +02004056 ret = nc_server_config_create_cipher_suite(opts, cipher);
roman12644fe2023-06-08 11:06:42 +02004057 if (ret) {
4058 goto cleanup;
4059 }
4060 } else if (op == NC_OP_DELETE) {
romanb6f44032023-06-30 15:07:56 +02004061 ret = nc_server_config_del_cipher_suite(opts, cipher);
roman12644fe2023-06-08 11:06:42 +02004062 if (ret) {
4063 goto cleanup;
4064 }
4065 }
4066
4067cleanup:
romanba93eac2023-07-18 14:36:48 +02004068 if (is_ch(node)) {
4069 /* UNLOCK */
4070 nc_ch_client_unlock(ch_client);
4071 }
roman12644fe2023-06-08 11:06:42 +02004072 return ret;
4073}
4074
romanfaecc582023-06-15 16:13:31 +02004075static int
4076nc_server_config_crl_url(const struct lyd_node *node, NC_OPERATION op)
4077{
4078 int ret = 0;
romanb6f44032023-06-30 15:07:56 +02004079 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02004080 struct nc_ch_client *ch_client;
romanfaecc582023-06-15 16:13:31 +02004081
4082 assert(!strcmp(LYD_NAME(node), "crl-url"));
4083
romanba93eac2023-07-18 14:36:48 +02004084 /* LOCK */
4085 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4086 return 1;
4087 }
4088
romanb6f44032023-06-30 15:07:56 +02004089 if (nc_server_config_get_tls_opts(node, &opts)) {
romanfaecc582023-06-15 16:13:31 +02004090 ret = 1;
4091 goto cleanup;
4092 }
4093
4094 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02004095 nc_server_config_del_url(opts);
4096 opts->crl_url = strdup(lyd_get_value(node));
4097 if (!opts->crl_url) {
romanfaecc582023-06-15 16:13:31 +02004098 ERRMEM;
4099 ret = 1;
4100 goto cleanup;
4101 }
4102 } else if (op == NC_OP_DELETE) {
romanb6f44032023-06-30 15:07:56 +02004103 nc_server_config_del_url(opts);
romanfaecc582023-06-15 16:13:31 +02004104 }
4105
4106cleanup:
romanba93eac2023-07-18 14:36:48 +02004107 if (is_ch(node)) {
4108 /* UNLOCK */
4109 nc_ch_client_unlock(ch_client);
4110 }
romanfaecc582023-06-15 16:13:31 +02004111 return ret;
4112}
4113
4114static int
4115nc_server_config_crl_path(const struct lyd_node *node, NC_OPERATION op)
4116{
4117 int ret = 0;
romanb6f44032023-06-30 15:07:56 +02004118 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02004119 struct nc_ch_client *ch_client;
romanfaecc582023-06-15 16:13:31 +02004120
4121 assert(!strcmp(LYD_NAME(node), "crl-path"));
4122
romanba93eac2023-07-18 14:36:48 +02004123 /* LOCK */
4124 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4125 return 1;
4126 }
4127
romanb6f44032023-06-30 15:07:56 +02004128 if (nc_server_config_get_tls_opts(node, &opts)) {
romanfaecc582023-06-15 16:13:31 +02004129 ret = 1;
4130 goto cleanup;
4131 }
4132
4133 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02004134 nc_server_config_del_path(opts);
4135 opts->crl_path = strdup(lyd_get_value(node));
4136 if (!opts->crl_path) {
romanfaecc582023-06-15 16:13:31 +02004137 ERRMEM;
4138 ret = 1;
4139 goto cleanup;
4140 }
4141 } else if (op == NC_OP_DELETE) {
romanb6f44032023-06-30 15:07:56 +02004142 nc_server_config_del_path(opts);
romanfaecc582023-06-15 16:13:31 +02004143 }
4144
4145cleanup:
romanba93eac2023-07-18 14:36:48 +02004146 if (is_ch(node)) {
4147 /* UNLOCK */
4148 nc_ch_client_unlock(ch_client);
4149 }
romanfaecc582023-06-15 16:13:31 +02004150 return ret;
4151}
4152
4153static int
4154nc_server_config_crl_cert_ext(const struct lyd_node *node, NC_OPERATION op)
4155{
4156 int ret = 0;
romanb6f44032023-06-30 15:07:56 +02004157 struct nc_server_tls_opts *opts;
romanba93eac2023-07-18 14:36:48 +02004158 struct nc_ch_client *ch_client;
romanfaecc582023-06-15 16:13:31 +02004159
4160 assert(!strcmp(LYD_NAME(node), "crl-cert-ext"));
4161
romanba93eac2023-07-18 14:36:48 +02004162 /* LOCK */
4163 if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4164 return 1;
4165 }
4166
romanb6f44032023-06-30 15:07:56 +02004167 if (nc_server_config_get_tls_opts(node, &opts)) {
romanfaecc582023-06-15 16:13:31 +02004168 ret = 1;
4169 goto cleanup;
4170 }
4171
4172 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanb6f44032023-06-30 15:07:56 +02004173 opts->crl_cert_ext = 1;
romanfaecc582023-06-15 16:13:31 +02004174 } else if (op == NC_OP_DELETE) {
romanb6f44032023-06-30 15:07:56 +02004175 opts->crl_cert_ext = 0;
romanfaecc582023-06-15 16:13:31 +02004176 }
4177
4178cleanup:
romanba93eac2023-07-18 14:36:48 +02004179 if (is_ch(node)) {
4180 /* UNLOCK */
4181 nc_ch_client_unlock(ch_client);
4182 }
romanfaecc582023-06-15 16:13:31 +02004183 return ret;
4184}
4185
roman2eab4742023-06-06 10:00:26 +02004186#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +02004187
roman83683fb2023-02-24 09:15:23 +01004188static int
roman5cbb6532023-06-22 12:53:17 +02004189nc_server_config_create_netconf_client(const struct lyd_node *node)
4190{
4191 int ret = 0;
4192
4193 node = lyd_child(node);
4194 assert(!strcmp(LYD_NAME(node), "name"));
4195
4196 /* LOCK */
4197 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
4198
4199 ret = nc_server_config_realloc(lyd_get_value(node), (void **)&server_opts.ch_clients, sizeof *server_opts.ch_clients, &server_opts.ch_client_count);
4200 if (ret) {
4201 goto cleanup;
4202 }
4203
4204 server_opts.ch_clients[server_opts.ch_client_count - 1].id = ATOMIC_INC_RELAXED(server_opts.new_client_id);
4205 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
4206 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3; // TODO
4207
4208 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
4209
4210cleanup:
4211 /* UNLOCK */
4212 pthread_rwlock_unlock(&server_opts.ch_client_lock);
4213 return ret;
4214}
4215
4216static int
4217nc_server_config_netconf_client(const struct lyd_node *node, NC_OPERATION op)
4218{
4219 int ret = 0;
4220 struct nc_ch_client *ch_client;
4221
4222 assert(!strcmp(LYD_NAME(node), "netconf-client"));
4223
4224 if (op == NC_OP_CREATE) {
4225 ret = nc_server_config_create_netconf_client(node);
4226 if (ret) {
4227 goto cleanup;
4228 }
4229 } else if (op == NC_OP_DELETE) {
4230 if (nc_server_config_get_ch_client(node, &ch_client)) {
4231 ret = 1;
4232 goto cleanup;
4233 }
4234
4235 nc_server_config_ch_del_client(ch_client);
4236 }
4237
4238cleanup:
4239 return ret;
4240}
4241
4242#ifdef NC_ENABLED_SSH_TLS
4243
4244static int
4245nc_server_config_remote_address(const struct lyd_node *node, NC_OPERATION op)
4246{
4247 int ret = 0;
roman5cbb6532023-06-22 12:53:17 +02004248 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02004249 struct nc_ch_client *ch_client;
roman5cbb6532023-06-22 12:53:17 +02004250
romanb6f44032023-06-30 15:07:56 +02004251 assert(!strcmp(LYD_NAME(node), "remote-address"));
4252
romanba93eac2023-07-18 14:36:48 +02004253 /* LOCK */
4254 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4255 return 1;
4256 }
4257
roman4cb8bb12023-06-29 09:16:46 +02004258 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02004259 ret = 1;
4260 goto cleanup;
4261 }
4262
4263 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
4264 nc_server_config_del_remote_address(ch_endpt);
4265
4266 ch_endpt->address = strdup(lyd_get_value(node));
4267 if (!ch_endpt->address) {
4268 ERRMEM;
4269 ret = 1;
4270 goto cleanup;
4271 }
4272 } else {
4273 nc_server_config_del_remote_address(ch_endpt);
4274 }
4275
4276cleanup:
romanba93eac2023-07-18 14:36:48 +02004277 /* UNLOCK */
4278 nc_ch_client_unlock(ch_client);
roman5cbb6532023-06-22 12:53:17 +02004279 return ret;
4280}
4281
4282static int
4283nc_server_config_remote_port(const struct lyd_node *node, NC_OPERATION op)
4284{
4285 int ret = 0;
roman5cbb6532023-06-22 12:53:17 +02004286 struct nc_ch_endpt *ch_endpt;
romanba93eac2023-07-18 14:36:48 +02004287 struct nc_ch_client *ch_client;
roman5cbb6532023-06-22 12:53:17 +02004288
romanb6f44032023-06-30 15:07:56 +02004289 assert(!strcmp(LYD_NAME(node), "remote-port"));
4290
romanba93eac2023-07-18 14:36:48 +02004291 /* LOCK */
4292 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4293 return 1;
4294 }
4295
roman4cb8bb12023-06-29 09:16:46 +02004296 if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
roman5cbb6532023-06-22 12:53:17 +02004297 ret = 1;
4298 goto cleanup;
4299 }
4300
4301 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
4302 ch_endpt->port = strtoul(lyd_get_value(node), NULL, 10);
4303 } else {
4304 ch_endpt->port = 0;
4305 }
4306
4307cleanup:
romanba93eac2023-07-18 14:36:48 +02004308 /* UNLOCK */
4309 nc_ch_client_unlock(ch_client);
roman5cbb6532023-06-22 12:53:17 +02004310 return ret;
4311}
4312
4313#endif /* NC_ENABLED_SSH_TLS */
4314
4315static int
romanb6f44032023-06-30 15:07:56 +02004316nc_server_config_persistent(const struct lyd_node *node, NC_OPERATION op)
4317{
4318 int ret = 0;
4319 struct nc_ch_client *ch_client;
4320
4321 assert(!strcmp(LYD_NAME(node), "persistent"));
4322
4323 (void) op;
4324
romanba93eac2023-07-18 14:36:48 +02004325 /* LOCK */
4326 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanb6f44032023-06-30 15:07:56 +02004327 ret = 1;
4328 goto cleanup;
4329 }
4330
4331 ch_client->conn_type = NC_CH_PERSIST;
4332
romanba93eac2023-07-18 14:36:48 +02004333 /* UNLOCK */
4334 nc_ch_client_unlock(ch_client);
4335
romanb6f44032023-06-30 15:07:56 +02004336cleanup:
4337 return ret;
4338}
4339
4340static int
4341nc_server_config_periodic(const struct lyd_node *node, NC_OPERATION op)
4342{
4343 int ret = 0;
4344 struct nc_ch_client *ch_client;
4345
4346 assert(!strcmp(LYD_NAME(node), "periodic"));
4347
4348 (void) op;
4349
romanba93eac2023-07-18 14:36:48 +02004350 /* LOCK */
4351 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanb6f44032023-06-30 15:07:56 +02004352 ret = 1;
4353 goto cleanup;
4354 }
4355
4356 ch_client->conn_type = NC_CH_PERIOD;
4357 /* set default values */
4358 ch_client->period = 60;
4359 ch_client->anchor_time = 0;
4360 ch_client->idle_timeout = 180;
4361
romanba93eac2023-07-18 14:36:48 +02004362 /* UNLOCK */
4363 nc_ch_client_unlock(ch_client);
4364
romanb6f44032023-06-30 15:07:56 +02004365cleanup:
4366 return ret;
4367}
4368
4369static int
4370nc_server_config_period(const struct lyd_node *node, NC_OPERATION op)
4371{
4372 int ret = 0;
4373 struct nc_ch_client *ch_client;
4374
4375 assert(!strcmp(LYD_NAME(node), "period"));
4376
romanba93eac2023-07-18 14:36:48 +02004377 /* LOCK */
4378 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanb6f44032023-06-30 15:07:56 +02004379 ret = 1;
4380 goto cleanup;
4381 }
4382
4383 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
4384 ch_client->period = strtoul(lyd_get_value(node), NULL, 10);
4385 } else if (op == NC_OP_DELETE) {
4386 ch_client->period = 60;
4387 }
4388
romanba93eac2023-07-18 14:36:48 +02004389 /* UNLOCK */
4390 nc_ch_client_unlock(ch_client);
4391
romanb6f44032023-06-30 15:07:56 +02004392cleanup:
4393 return ret;
4394}
4395
4396static int
4397nc_server_config_anchor_time(const struct lyd_node *node, NC_OPERATION op)
4398{
4399 int ret = 0;
4400 struct nc_ch_client *ch_client;
4401 time_t anchor_time = {0};
4402
4403 assert(!strcmp(LYD_NAME(node), "anchor-time"));
4404
romanba93eac2023-07-18 14:36:48 +02004405 /* LOCK */
4406 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4407 return 1;
romanb6f44032023-06-30 15:07:56 +02004408 }
4409
4410 ret = ly_time_str2time(lyd_get_value(node), &anchor_time, NULL);
4411 if (ret) {
4412 goto cleanup;
4413 }
4414
4415 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
4416 ch_client->anchor_time = anchor_time;
4417 } else if (op == NC_OP_DELETE) {
4418 ch_client->anchor_time = 0;
4419 }
4420
4421cleanup:
romanba93eac2023-07-18 14:36:48 +02004422 /* UNLOCK */
4423 nc_ch_client_unlock(ch_client);
romanb6f44032023-06-30 15:07:56 +02004424 return ret;
4425}
4426
4427static int
4428nc_server_config_reconnect_strategy(const struct lyd_node *node, NC_OPERATION op)
4429{
4430 int ret = 0;
4431 struct nc_ch_client *ch_client;
4432
4433 assert(!strcmp(LYD_NAME(node), "reconnect-strategy"));
4434
4435 (void) op;
4436
romanba93eac2023-07-18 14:36:48 +02004437 /* LOCK */
4438 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanb6f44032023-06-30 15:07:56 +02004439 ret = 1;
4440 goto cleanup;
4441 }
4442
4443 /* set to default values */
4444 ch_client->start_with = NC_CH_FIRST_LISTED;
4445 ch_client->max_wait = 5;
4446 ch_client->max_attempts = 3;
4447
romanba93eac2023-07-18 14:36:48 +02004448 /* UNLOCK */
4449 nc_ch_client_unlock(ch_client);
4450
romanb6f44032023-06-30 15:07:56 +02004451cleanup:
4452 return ret;
4453}
4454
4455static int
4456nc_server_config_start_with(const struct lyd_node *node, NC_OPERATION op)
4457{
4458 int ret = 0;
4459 struct nc_ch_client *ch_client;
4460 const char *value;
4461
4462 assert(!strcmp(LYD_NAME(node), "start-with"));
4463
romanba93eac2023-07-18 14:36:48 +02004464 /* LOCK */
4465 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
4466 return 1;
romanb6f44032023-06-30 15:07:56 +02004467 }
4468
4469 if (op == NC_OP_DELETE) {
4470 ch_client->start_with = NC_CH_FIRST_LISTED;
4471 goto cleanup;
4472 }
4473
4474 value = lyd_get_value(node);
4475 if (!strcmp(value, "first-listed")) {
4476 ch_client->start_with = NC_CH_FIRST_LISTED;
4477 } else if (!strcmp(value, "last-connected")) {
4478 ch_client->start_with = NC_CH_LAST_CONNECTED;
4479 } else if (!strcmp(value, "random-selection")) {
4480 ch_client->start_with = NC_CH_RANDOM;
4481 } else {
4482 ERR(NULL, "Unexpected start-with value \"%s\".", value);
4483 ret = 1;
4484 goto cleanup;
4485 }
4486
4487cleanup:
romanba93eac2023-07-18 14:36:48 +02004488 /* UNLOCK */
4489 nc_ch_client_unlock(ch_client);
romanb6f44032023-06-30 15:07:56 +02004490 return ret;
4491}
4492
4493static int
4494nc_server_config_max_wait(const struct lyd_node *node, NC_OPERATION op)
4495{
4496 int ret = 0;
4497 struct nc_ch_client *ch_client;
4498
4499 assert(!strcmp(LYD_NAME(node), "max-wait"));
4500
romanba93eac2023-07-18 14:36:48 +02004501 /* LOCK */
4502 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanb6f44032023-06-30 15:07:56 +02004503 ret = 1;
4504 goto cleanup;
4505 }
4506
4507 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
4508 ch_client->max_wait = strtoul(lyd_get_value(node), NULL, 10);
4509 } else {
4510 ch_client->max_wait = 5;
4511 }
4512
romanba93eac2023-07-18 14:36:48 +02004513 /* UNLOCK */
4514 nc_ch_client_unlock(ch_client);
4515
romanb6f44032023-06-30 15:07:56 +02004516cleanup:
4517 return ret;
4518}
4519
4520static int
4521nc_server_config_max_attempts(const struct lyd_node *node, NC_OPERATION op)
4522{
4523 int ret = 0;
4524 struct nc_ch_client *ch_client;
4525
4526 assert(!strcmp(LYD_NAME(node), "max-attempts"));
4527
romanba93eac2023-07-18 14:36:48 +02004528 /* LOCK */
4529 if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
romanb6f44032023-06-30 15:07:56 +02004530 ret = 1;
4531 goto cleanup;
4532 }
4533
4534 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
4535 ch_client->max_attempts = strtoul(lyd_get_value(node), NULL, 10);
4536 } else {
4537 ch_client->max_attempts = 3;
4538 }
4539
romanba93eac2023-07-18 14:36:48 +02004540 /* UNLOCK */
4541 nc_ch_client_unlock(ch_client);
4542
romanb6f44032023-06-30 15:07:56 +02004543cleanup:
4544 return ret;
4545}
4546
4547static int
romanf02273a2023-05-25 09:44:11 +02004548nc_server_config_parse_netconf_server(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01004549{
4550 const char *name = LYD_NAME(node);
4551
4552 if (!strcmp(name, "listen")) {
romanf02273a2023-05-25 09:44:11 +02004553 if (nc_server_config_listen(NULL, op)) {
romanc1d2b092023-02-02 08:58:27 +01004554 goto error;
4555 }
4556 } else if (!strcmp(name, "idle-timeout")) {
romane028ef92023-02-24 16:33:08 +01004557 if (nc_server_config_idle_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004558 goto error;
4559 }
4560 } else if (!strcmp(name, "endpoint")) {
romane028ef92023-02-24 16:33:08 +01004561 if (nc_server_config_endpoint(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004562 goto error;
4563 }
roman2eab4742023-06-06 10:00:26 +02004564 } else if (!strcmp(name, "unix-socket")) {
4565 if (nc_server_config_unix_socket(node, op)) {
4566 goto error;
4567 }
roman3f9b65c2023-06-05 14:26:58 +02004568 }
roman2eab4742023-06-06 10:00:26 +02004569#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +02004570 else if (!strcmp(name, "ssh")) {
romane028ef92023-02-24 16:33:08 +01004571 if (nc_server_config_ssh(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004572 goto error;
4573 }
roman2eab4742023-06-06 10:00:26 +02004574 } else if (!strcmp(name, "local-address")) {
romane028ef92023-02-24 16:33:08 +01004575 if (nc_server_config_local_address(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004576 goto error;
4577 }
4578 } else if (!strcmp(name, "local-port")) {
romane028ef92023-02-24 16:33:08 +01004579 if (nc_server_config_local_port(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004580 goto error;
4581 }
4582 } else if (!strcmp(name, "keepalives")) {
romane028ef92023-02-24 16:33:08 +01004583 if (nc_server_config_keepalives(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004584 goto error;
4585 }
4586 } else if (!strcmp(name, "idle-time")) {
romane028ef92023-02-24 16:33:08 +01004587 if (nc_server_config_idle_time(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004588 goto error;
4589 }
4590 } else if (!strcmp(name, "max-probes")) {
romane028ef92023-02-24 16:33:08 +01004591 if (nc_server_config_max_probes(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004592 goto error;
4593 }
4594 } else if (!strcmp(name, "probe-interval")) {
romane028ef92023-02-24 16:33:08 +01004595 if (nc_server_config_probe_interval(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004596 goto error;
4597 }
roman2eab4742023-06-06 10:00:26 +02004598 } else if (!strcmp(name, "host-key")) {
romane028ef92023-02-24 16:33:08 +01004599 if (nc_server_config_host_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004600 goto error;
4601 }
roman2eab4742023-06-06 10:00:26 +02004602 } else if (!strcmp(name, "public-key-format")) {
romane028ef92023-02-24 16:33:08 +01004603 if (nc_server_config_public_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004604 goto error;
4605 }
4606 } else if (!strcmp(name, "public-key")) {
romane028ef92023-02-24 16:33:08 +01004607 if (nc_server_config_public_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004608 goto error;
4609 }
4610 } else if (!strcmp(name, "private-key-format")) {
romane028ef92023-02-24 16:33:08 +01004611 if (nc_server_config_private_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004612 goto error;
4613 }
4614 } else if (!strcmp(name, "cleartext-private-key")) {
romane028ef92023-02-24 16:33:08 +01004615 if (nc_server_config_cleartext_private_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004616 goto error;
4617 }
roman2eab4742023-06-06 10:00:26 +02004618 } else if (!strcmp(name, "keystore-reference")) {
romane028ef92023-02-24 16:33:08 +01004619 if (nc_server_config_keystore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004620 goto error;
4621 }
4622 } else if (!strcmp(name, "user")) {
romane028ef92023-02-24 16:33:08 +01004623 if (nc_server_config_user(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004624 goto error;
4625 }
4626 } else if (!strcmp(name, "auth-attempts")) {
romane028ef92023-02-24 16:33:08 +01004627 if (nc_server_config_auth_attempts(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004628 goto error;
4629 }
4630 } else if (!strcmp(name, "auth-timeout")) {
romane028ef92023-02-24 16:33:08 +01004631 if (nc_server_config_auth_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004632 goto error;
4633 }
roman2eab4742023-06-06 10:00:26 +02004634 } else if (!strcmp(name, "truststore-reference")) {
romane028ef92023-02-24 16:33:08 +01004635 if (nc_server_config_truststore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004636 goto error;
4637 }
roman2eab4742023-06-06 10:00:26 +02004638 } else if (!strcmp(name, "password")) {
romane028ef92023-02-24 16:33:08 +01004639 if (nc_server_config_password(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004640 goto error;
4641 }
4642 } else if (!strcmp(name, "pam-config-file-name")) {
romane028ef92023-02-24 16:33:08 +01004643 if (nc_server_config_pam_name(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004644 goto error;
4645 }
4646 } else if (!strcmp(name, "pam-config-file-dir")) {
romane028ef92023-02-24 16:33:08 +01004647 if (nc_server_config_pam_dir(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004648 goto error;
4649 }
4650 } else if (!strcmp(name, "none")) {
romane028ef92023-02-24 16:33:08 +01004651 if (nc_server_config_none(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004652 goto error;
4653 }
4654 } else if (!strcmp(name, "host-key-alg")) {
romane028ef92023-02-24 16:33:08 +01004655 if (nc_server_config_host_key_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004656 goto error;
4657 }
4658 } else if (!strcmp(name, "key-exchange-alg")) {
romane028ef92023-02-24 16:33:08 +01004659 if (nc_server_config_kex_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004660 goto error;
4661 }
4662 } else if (!strcmp(name, "encryption-alg")) {
romane028ef92023-02-24 16:33:08 +01004663 if (nc_server_config_encryption_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004664 goto error;
4665 }
4666 } else if (!strcmp(name, "mac-alg")) {
romane028ef92023-02-24 16:33:08 +01004667 if (nc_server_config_mac_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01004668 goto error;
4669 }
roman2eab4742023-06-06 10:00:26 +02004670 } else if (!strcmp(name, "endpoint-client-auth")) {
roman0bbc19c2023-05-26 09:59:09 +02004671 if (nc_server_config_endpoint_client_auth(node, op)) {
4672 goto error;
4673 }
roman2eab4742023-06-06 10:00:26 +02004674 } else if (!strcmp(name, "tls")) {
roman3f9b65c2023-06-05 14:26:58 +02004675 if (nc_server_config_tls(node, op)) {
4676 goto error;
4677 }
4678 } else if (!strcmp(name, "cert-data")) {
4679 if (nc_server_config_cert_data(node, op)) {
4680 goto error;
4681 }
4682 } else if (!strcmp(name, "asymmetric-key")) {
4683 if (nc_server_config_asymmetric_key(node, op)) {
4684 goto error;
4685 }
4686 } else if (!strcmp(name, "certificate")) {
4687 if (nc_server_config_certificate(node, op)) {
4688 goto error;
4689 }
4690 } else if (!strcmp(name, "cert-to-name")) {
4691 if (nc_server_config_cert_to_name(node, op)) {
4692 goto error;
4693 }
4694 } else if (!strcmp(name, "fingerprint")) {
4695 if (nc_server_config_fingerprint(node, op)) {
4696 goto error;
4697 }
roman12644fe2023-06-08 11:06:42 +02004698 } else if (!strcmp(name, "tls-version")) {
4699 if (nc_server_config_tls_version(node, op)) {
4700 goto error;
4701 }
4702 } else if (!strcmp(name, "cipher-suite")) {
4703 if (nc_server_config_cipher_suite(node, op)) {
4704 goto error;
4705 }
romanfaecc582023-06-15 16:13:31 +02004706 } else if (!strcmp(name, "crl-url")) {
4707 if (nc_server_config_crl_url(node, op)) {
4708 goto error;
4709 }
4710 } else if (!strcmp(name, "crl-path")) {
4711 if (nc_server_config_crl_path(node, op)) {
4712 goto error;
4713 }
4714 } else if (!strcmp(name, "crl-cert-ext")) {
4715 if (nc_server_config_crl_cert_ext(node, op)) {
4716 goto error;
4717 }
roman3f9b65c2023-06-05 14:26:58 +02004718 }
roman2eab4742023-06-06 10:00:26 +02004719#endif /* NC_ENABLED_SSH_TLS */
roman5cbb6532023-06-22 12:53:17 +02004720 else if (!strcmp(name, "netconf-client")) {
4721 if (nc_server_config_netconf_client(node, op)) {
4722 goto error;
4723 }
4724 }
4725#ifdef NC_ENABLED_SSH_TLS
4726 else if (!strcmp(name, "remote-address")) {
4727 if (nc_server_config_remote_address(node, op)) {
4728 goto error;
4729 }
4730 } else if (!strcmp(name, "remote-port")) {
4731 if (nc_server_config_remote_port(node, op)) {
4732 goto error;
4733 }
4734 }
4735#endif /* NC_ENABLED_SSH_TLS */
romanb6f44032023-06-30 15:07:56 +02004736 else if (!strcmp(name, "persistent")) {
4737 if (nc_server_config_persistent(node, op)) {
4738 goto error;
4739 }
4740 } else if (!strcmp(name, "periodic")) {
4741 if (nc_server_config_periodic(node, op)) {
4742 goto error;
4743 }
4744 } else if (!strcmp(name, "period")) {
4745 if (nc_server_config_period(node, op)) {
4746 goto error;
4747 }
4748 } else if (!strcmp(name, "anchor-time")) {
4749 if (nc_server_config_anchor_time(node, op)) {
4750 goto error;
4751 }
4752 } else if (!strcmp(name, "reconnect-strategy")) {
4753 if (nc_server_config_reconnect_strategy(node, op)) {
4754 goto error;
4755 }
4756 } else if (!strcmp(name, "start-with")) {
4757 if (nc_server_config_start_with(node, op)) {
4758 goto error;
4759 }
4760 } else if (!strcmp(name, "max-wait")) {
4761 if (nc_server_config_max_wait(node, op)) {
4762 goto error;
4763 }
4764 } else if (!strcmp(name, "max-attempts")) {
4765 if (nc_server_config_max_attempts(node, op)) {
4766 goto error;
4767 }
4768 }
romanc1d2b092023-02-02 08:58:27 +01004769
4770 return 0;
4771
4772error:
romanb6f44032023-06-30 15:07:56 +02004773 ERR(NULL, "Configuring node \"%s\" failed.", LYD_NAME(node));
romanc1d2b092023-02-02 08:58:27 +01004774 return 1;
4775}
4776
4777int
roman0bbc19c2023-05-26 09:59:09 +02004778nc_server_config_parse_tree(const struct lyd_node *node, NC_OPERATION parent_op, NC_MODULE module)
romanc1d2b092023-02-02 08:58:27 +01004779{
4780 struct lyd_node *child;
4781 struct lyd_meta *m;
romanf9906b42023-05-22 14:04:29 +02004782 NC_OPERATION current_op = NC_OP_UNKNOWN;
romanf02273a2023-05-25 09:44:11 +02004783 int ret;
romanc1d2b092023-02-02 08:58:27 +01004784
4785 assert(node);
4786
romanf9906b42023-05-22 14:04:29 +02004787 /* get current op if there is any */
4788 if ((m = lyd_find_meta(node->meta, NULL, "yang:operation"))) {
4789 if (!strcmp(lyd_get_meta_value(m), "create")) {
4790 current_op = NC_OP_CREATE;
4791 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
4792 current_op = NC_OP_DELETE;
4793 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
4794 current_op = NC_OP_REPLACE;
4795 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
4796 current_op = NC_OP_NONE;
romanc1d2b092023-02-02 08:58:27 +01004797 }
4798 }
4799
4800 /* node has no op, inherit from the parent */
romanf9906b42023-05-22 14:04:29 +02004801 if (!current_op) {
4802 if (!parent_op) {
4803 ERR(NULL, "Unknown operation for node \"%s\".", LYD_NAME(node));
4804 return 1;
4805 }
4806
romanc1d2b092023-02-02 08:58:27 +01004807 current_op = parent_op;
4808 }
4809
4810 switch (current_op) {
4811 case NC_OP_NONE:
4812 break;
4813 case NC_OP_CREATE:
4814 case NC_OP_DELETE:
4815 case NC_OP_REPLACE:
roman2eab4742023-06-06 10:00:26 +02004816#ifdef NC_ENABLED_SSH_TLS
4817 if (module == NC_MODULE_KEYSTORE) {
romanf02273a2023-05-25 09:44:11 +02004818 ret = nc_server_config_parse_keystore(node, current_op);
roman2eab4742023-06-06 10:00:26 +02004819 } else if (module == NC_MODULE_TRUSTSTORE) {
romanf02273a2023-05-25 09:44:11 +02004820 ret = nc_server_config_parse_truststore(node, current_op);
roman2eab4742023-06-06 10:00:26 +02004821 } else
4822#endif /* NC_ENABLED_SSH_TLS */
4823 {
4824 ret = nc_server_config_parse_netconf_server(node, current_op);
romanf02273a2023-05-25 09:44:11 +02004825 }
4826 if (ret) {
4827 return ret;
romanc1d2b092023-02-02 08:58:27 +01004828 }
4829 break;
4830 default:
4831 break;
4832 }
4833
4834 if (current_op != NC_OP_DELETE) {
4835 LY_LIST_FOR(lyd_child(node), child) {
roman0bbc19c2023-05-26 09:59:09 +02004836 if (nc_server_config_parse_tree(child, current_op, module)) {
romanc1d2b092023-02-02 08:58:27 +01004837 return 1;
4838 }
4839 }
4840 }
4841 return 0;
4842}
4843
romanc1d2b092023-02-02 08:58:27 +01004844API int
4845nc_server_config_load_modules(struct ly_ctx **ctx)
4846{
4847 int i, new_ctx = 0;
4848
4849 if (!*ctx) {
4850 if (ly_ctx_new(NC_SERVER_SEARCH_DIR, 0, ctx)) {
4851 ERR(NULL, "Couldn't create new libyang context.\n");
4852 goto error;
4853 }
4854 new_ctx = 1;
4855 }
4856
4857 /* all features */
4858 const char *ietf_nectonf_server[] = {"ssh-listen", "tls-listen", "ssh-call-home", "tls-call-home", "central-netconf-server-supported", NULL};
4859 /* all features */
4860 const char *ietf_x509_cert_to_name[] = {NULL};
roman7fdc84d2023-06-06 13:14:53 +02004861 /* no private-key-encryption, csr-generation, p10-csr-format, certificate-expiration-notification,
4862 * encrypted-passwords, hidden-symmetric-keys, encrypted-symmetric-keys, hidden-private-keys, encrypted-private-keys
4863 */
romanc1d2b092023-02-02 08:58:27 +01004864 const char *ietf_crypto_types[] = {
4865 "one-symmetric-key-format", "one-asymmetric-key-format", "symmetrically-encrypted-value-format",
4866 "asymmetrically-encrypted-value-format", "cms-enveloped-data-format", "cms-encrypted-data-format",
roman7fdc84d2023-06-06 13:14:53 +02004867 "cleartext-passwords", "cleartext-symmetric-keys", "cleartext-private-keys", NULL
romanc1d2b092023-02-02 08:58:27 +01004868 };
4869 /* all features */
4870 const char *ietf_tcp_common[] = {"keepalives-supported", NULL};
roman7fdc84d2023-06-06 13:14:53 +02004871 /* all features */
4872 const char *ietf_tcp_server[] = {"tcp-server-keepalives", NULL};
4873 /* all features */
4874 const char *ietf_tcp_client[] = {"local-binding-supported", "tcp-client-keepalives", "proxy-connect", "socks5-gss-api", "socks5-username-password", NULL};
romanc1d2b092023-02-02 08:58:27 +01004875 /* no ssh-x509-certs */
4876 const char *ietf_ssh_common[] = {"transport-params", "public-key-generation", NULL};
roman7fdc84d2023-06-06 13:14:53 +02004877 /* no ssh-server-keepalives and local-user-auth-hostbased */
4878 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 +01004879 /* all features */
4880 const char *iana_ssh_encryption_algs[] = {NULL};
4881 /* all features */
4882 const char *iana_ssh_key_exchange_algs[] = {NULL};
4883 /* all features */
4884 const char *iana_ssh_mac_algs[] = {NULL};
4885 /* all features */
4886 const char *iana_ssh_public_key_algs[] = {NULL};
romanc1d2b092023-02-02 08:58:27 +01004887 /* all features */
roman7fdc84d2023-06-06 13:14:53 +02004888 const char *iana_crypt_hash[] = {"crypt-hash-md5", "crypt-hash-sha-256", "crypt-hash-sha-512", NULL};
4889 /* no symmetric-keys */
4890 const char *ietf_keystore[] = {"central-keystore-supported", "inline-definitions-supported", "asymmetric-keys", NULL};
4891 /* all features */
4892 const char *ietf_truststore[] = {"central-truststore-supported", "inline-definitions-supported", "certificates", "public-keys", NULL};
4893 /* all features */
4894 const char *ietf_tls_common[] = {"tls10", "tls11", "tls12", "tls13", "hello-params", "public-key-generation", NULL};
romanc1d2b092023-02-02 08:58:27 +01004895 /* all features */
4896 const char *ietf_tls_server[] = {
4897 "tls-server-keepalives", "server-ident-x509-cert", "server-ident-raw-public-key", "server-ident-tls12-psk",
4898 "server-ident-tls13-epsk", "client-auth-supported", "client-auth-x509-cert", "client-auth-raw-public-key",
4899 "client-auth-tls12-psk", "client-auth-tls13-epsk", NULL
4900 };
roman12644fe2023-06-08 11:06:42 +02004901 const char *iana_tls_cipher_suite_algs[] = {NULL};
romanc1d2b092023-02-02 08:58:27 +01004902 /* all features */
4903 const char *libnetconf2_netconf_server[] = {NULL};
4904
4905 const char *module_names[] = {
roman7fdc84d2023-06-06 13:14:53 +02004906 "ietf-netconf-server", "ietf-x509-cert-to-name", "ietf-crypto-types", "ietf-tcp-common", "ietf-tcp-server",
4907 "ietf-tcp-client", "ietf-ssh-common", "ietf-ssh-server", "iana-ssh-encryption-algs",
4908 "iana-ssh-key-exchange-algs", "iana-ssh-mac-algs", "iana-ssh-public-key-algs", "iana-crypt-hash",
roman12644fe2023-06-08 11:06:42 +02004909 "ietf-keystore", "ietf-truststore", "ietf-tls-common", "ietf-tls-server", "iana-tls-cipher-suite-algs",
4910 "libnetconf2-netconf-server", NULL
romanc1d2b092023-02-02 08:58:27 +01004911 };
4912
4913 const char **module_features[] = {
roman7fdc84d2023-06-06 13:14:53 +02004914 ietf_nectonf_server, ietf_x509_cert_to_name, ietf_crypto_types, ietf_tcp_common,
4915 ietf_tcp_server, ietf_tcp_client, ietf_ssh_common, ietf_ssh_server, iana_ssh_encryption_algs,
4916 iana_ssh_key_exchange_algs, iana_ssh_mac_algs, iana_ssh_public_key_algs, iana_crypt_hash,
roman12644fe2023-06-08 11:06:42 +02004917 ietf_keystore, ietf_truststore, ietf_tls_common, ietf_tls_server, iana_tls_cipher_suite_algs,
4918 libnetconf2_netconf_server, NULL
romanc1d2b092023-02-02 08:58:27 +01004919 };
4920
4921 for (i = 0; module_names[i] != NULL; i++) {
4922 if (!ly_ctx_load_module(*ctx, module_names[i], NULL, module_features[i])) {
4923 ERR(NULL, "Loading module \"%s\" failed.\n", module_names[i]);
4924 goto error;
4925 }
4926 }
4927
4928 return 0;
4929
4930error:
4931 if (new_ctx) {
4932 ly_ctx_destroy(*ctx);
4933 *ctx = NULL;
4934 }
4935 return 1;
4936}
4937
romanf9906b42023-05-22 14:04:29 +02004938static int
4939nc_server_config_fill_nectonf_server(const struct lyd_node *data, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01004940{
4941 int ret = 0;
4942 struct lyd_node *tree;
romand57b3722023-04-05 11:26:25 +02004943
romanc1d2b092023-02-02 08:58:27 +01004944 ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &tree);
4945 if (ret) {
4946 ERR(NULL, "Unable to find the netconf-server container in the YANG data.");
4947 goto cleanup;
4948 }
4949
roman0bbc19c2023-05-26 09:59:09 +02004950 if (nc_server_config_parse_tree(tree, op, NC_MODULE_NETCONF_SERVER)) {
4951 ret = 1;
4952 goto cleanup;
4953 }
4954
roman2eab4742023-06-06 10:00:26 +02004955#ifdef NC_ENABLED_SSH_TLS
4956 /* backward check of client auth reference */
roman0bbc19c2023-05-26 09:59:09 +02004957 if (nc_server_config_fill_endpt_client_auth()) {
romanf9906b42023-05-22 14:04:29 +02004958 ret = 1;
4959 goto cleanup;
4960 }
roman2eab4742023-06-06 10:00:26 +02004961#endif /* NC_ENABLED_SSH_TLS */
romanf9906b42023-05-22 14:04:29 +02004962
4963cleanup:
4964 return ret;
4965}
4966
4967API int
romanf6f37a52023-05-25 14:27:51 +02004968nc_server_config_setup_diff(const struct lyd_node *data)
romanf9906b42023-05-22 14:04:29 +02004969{
4970 int ret = 0;
4971
4972 /* LOCK */
4973 pthread_rwlock_wrlock(&server_opts.config_lock);
4974
roman2eab4742023-06-06 10:00:26 +02004975#ifdef NC_ENABLED_SSH_TLS
romanf9906b42023-05-22 14:04:29 +02004976 /* configure keystore */
romanf02273a2023-05-25 09:44:11 +02004977 ret = nc_server_config_fill_keystore(data, NC_OP_UNKNOWN);
romanf9906b42023-05-22 14:04:29 +02004978 if (ret) {
4979 ERR(NULL, "Filling keystore failed.");
4980 goto cleanup;
4981 }
4982
4983 /* configure truststore */
romanf02273a2023-05-25 09:44:11 +02004984 ret = nc_server_config_fill_truststore(data, NC_OP_UNKNOWN);
romanf9906b42023-05-22 14:04:29 +02004985 if (ret) {
4986 ERR(NULL, "Filling truststore failed.");
4987 goto cleanup;
4988 }
roman2eab4742023-06-06 10:00:26 +02004989#endif /* NC_ENABLED_SSH_TLS */
romanf9906b42023-05-22 14:04:29 +02004990
4991 /* configure netconf-server */
4992 ret = nc_server_config_fill_nectonf_server(data, NC_OP_UNKNOWN);
4993 if (ret) {
4994 ERR(NULL, "Filling netconf-server failed.");
4995 goto cleanup;
4996 }
4997
4998cleanup:
4999 /* UNLOCK */
5000 pthread_rwlock_unlock(&server_opts.config_lock);
5001 return ret;
5002}
5003
5004API int
romanf6f37a52023-05-25 14:27:51 +02005005nc_server_config_setup_data(const struct lyd_node *data)
romanf9906b42023-05-22 14:04:29 +02005006{
5007 int ret = 0;
5008 struct lyd_node *tree, *iter, *root;
5009
5010 /* LOCK */
5011 pthread_rwlock_wrlock(&server_opts.config_lock);
5012
5013 /* find the netconf-server node */
5014 ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &root);
5015 if (ret) {
5016 ERR(NULL, "Unable to find the netconf-server container in the YANG data.");
5017 goto cleanup;
5018 }
5019
5020 /* iterate through all the nodes and make sure there is no operation attribute */
5021 LY_LIST_FOR(root, tree) {
5022 LYD_TREE_DFS_BEGIN(tree, iter) {
5023 if (lyd_find_meta(iter->meta, NULL, "yang:operation")) {
5024 ERR(NULL, "Unexpected operation attribute in the YANG data.");
romanc1d2b092023-02-02 08:58:27 +01005025 ret = 1;
5026 goto cleanup;
5027 }
romanf9906b42023-05-22 14:04:29 +02005028 LYD_TREE_DFS_END(tree, iter);
romanc1d2b092023-02-02 08:58:27 +01005029 }
5030 }
5031
romanf9906b42023-05-22 14:04:29 +02005032 /* delete the current configuration */
romanf02273a2023-05-25 09:44:11 +02005033 nc_server_config_listen(NULL, NC_OP_DELETE);
roman4cb8bb12023-06-29 09:16:46 +02005034 nc_server_config_ch(NULL, NC_OP_DELETE);
roman2eab4742023-06-06 10:00:26 +02005035#ifdef NC_ENABLED_SSH_TLS
romanf02273a2023-05-25 09:44:11 +02005036 nc_server_config_ks_keystore(NULL, NC_OP_DELETE);
5037 nc_server_config_ts_truststore(NULL, NC_OP_DELETE);
romanf9906b42023-05-22 14:04:29 +02005038
5039 /* configure keystore */
romanf02273a2023-05-25 09:44:11 +02005040 ret = nc_server_config_fill_keystore(data, NC_OP_CREATE);
romanf9906b42023-05-22 14:04:29 +02005041 if (ret) {
5042 ERR(NULL, "Filling keystore failed.");
5043 goto cleanup;
5044 }
5045
5046 /* configure truststore */
romanf02273a2023-05-25 09:44:11 +02005047 ret = nc_server_config_fill_truststore(data, NC_OP_CREATE);
romanf9906b42023-05-22 14:04:29 +02005048 if (ret) {
5049 ERR(NULL, "Filling truststore failed.");
5050 goto cleanup;
5051 }
roman2eab4742023-06-06 10:00:26 +02005052#endif /* NC_ENABLED_SSH_TLS */
romanf9906b42023-05-22 14:04:29 +02005053
5054 /* configure netconf-server */
5055 ret = nc_server_config_fill_nectonf_server(data, NC_OP_CREATE);
5056 if (ret) {
5057 ERR(NULL, "Filling netconf-server failed.");
romanc1d2b092023-02-02 08:58:27 +01005058 goto cleanup;
5059 }
5060
5061cleanup:
5062 /* UNLOCK */
5063 pthread_rwlock_unlock(&server_opts.config_lock);
5064 return ret;
5065}
roman3f9b65c2023-06-05 14:26:58 +02005066
5067API int
5068nc_server_config_setup_path(const struct ly_ctx *ctx, const char *path)
5069{
5070 struct lyd_node *tree = NULL;
5071 int ret = 0;
5072
5073 NC_CHECK_ARG_RET(NULL, path, 1);
5074
5075 ret = lyd_parse_data_path(ctx, path, LYD_UNKNOWN, LYD_PARSE_NO_STATE | LYD_PARSE_STRICT, LYD_VALIDATE_NO_STATE, &tree);
5076 if (ret) {
5077 goto cleanup;
5078 }
5079
5080 ret = nc_server_config_setup_data(tree);
5081 if (ret) {
5082 goto cleanup;
5083 }
5084
5085cleanup:
5086 lyd_free_all(tree);
5087 return ret;
5088}