blob: f7f6e93a73222ddd639f0802fa14858562fde4f8 [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
29#include <openssl/x509_vfy.h> // X509_STORE_free
30#endif
31
romanc1d2b092023-02-02 08:58:27 +010032#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020033#include "config.h"
34#include "log_p.h"
romane028ef92023-02-24 16:33:08 +010035#include "server_config.h"
romanf02273a2023-05-25 09:44:11 +020036#include "server_config_p.h"
roman3f9b65c2023-06-05 14:26:58 +020037#include "session_p.h"
38
roman2eab4742023-06-06 10:00:26 +020039#ifdef NC_ENABLED_SSH_TLS
romanc1d2b092023-02-02 08:58:27 +010040
41/* All libssh supported host-key, key-exchange, encryption and mac algorithms as of version 0.10.90 */
42
43static const char *supported_hostkey_algs[] = {
romana6bf6ab2023-05-26 13:26:02 +020044 "openssh-ssh-ed25519-cert-v01", "openssh-ecdsa-sha2-nistp521-cert-v01",
45 "openssh-ecdsa-sha2-nistp384-cert-v01", "openssh-ecdsa-sha2-nistp256-cert-v01",
46 "openssh-rsa-sha2-512-cert-v01", "openssh-rsa-sha2-256-cert-v01",
47 "openssh-ssh-rsa-cert-v01", "openssh-ssh-dss-cert-v01",
romanc1d2b092023-02-02 08:58:27 +010048 "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256",
49 "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa", "ssh-dss", NULL
50};
51
52static const char *supported_kex_algs[] = {
romana6bf6ab2023-05-26 13:26:02 +020053 "diffie-hellman-group-exchange-sha1", "curve25519-sha256", "libssh-curve25519-sha256",
romanc1d2b092023-02-02 08:58:27 +010054 "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group18-sha512",
55 "diffie-hellman-group16-sha512", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group14-sha256", NULL
56};
57
58static const char *supported_encryption_algs[] = {
romana6bf6ab2023-05-26 13:26:02 +020059 "openssh-chacha20-poly1305", "openssh-aes256-gcm", "openssh-aes128-gcm",
romanc1d2b092023-02-02 08:58:27 +010060 "aes256-ctr", "aes192-ctr", "aes128-ctr", "aes256-cbc", "aes192-cbc", "aes128-cbc",
roman27215242023-03-10 14:55:00 +010061 "blowfish-cbc", "triple-des-cbc", "none", NULL
romanc1d2b092023-02-02 08:58:27 +010062};
63
64static const char *supported_mac_algs[] = {
romana6bf6ab2023-05-26 13:26:02 +020065 "openssh-hmac-sha2-256-etm", "openssh-hmac-sha2-512-etm", "openssh-hmac-sha1-etm",
romanc1d2b092023-02-02 08:58:27 +010066 "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", NULL
67};
68
roman2eab4742023-06-06 10:00:26 +020069#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +020070
romanc1d2b092023-02-02 08:58:27 +010071extern struct nc_server_opts server_opts;
72
romanf02273a2023-05-25 09:44:11 +020073int
74nc_server_config_get_endpt(const struct lyd_node *node, struct nc_endpt **endpt, struct nc_bind **bind)
romanc1d2b092023-02-02 08:58:27 +010075{
76 uint16_t i;
77 const char *endpt_name;
78
79 assert(node);
80
81 while (node) {
82 if (!strcmp(LYD_NAME(node), "endpoint")) {
83 break;
84 }
85 node = lyd_parent(node);
86 }
87
88 if (!node) {
89 ERR(NULL, "Node \"%s\" is not contained in an endpoint subtree.", LYD_NAME(node));
90 return 1;
91 }
92
93 node = lyd_child(node);
94 assert(!strcmp(LYD_NAME(node), "name"));
95 endpt_name = lyd_get_value(node);
96
97 for (i = 0; i < server_opts.endpt_count; i++) {
98 if (!strcmp(server_opts.endpts[i].name, endpt_name)) {
99 *endpt = &server_opts.endpts[i];
100 if (bind) {
101 *bind = &server_opts.binds[i];
102 }
103 return 0;
104 }
105 }
106
107 ERR(NULL, "Endpoint \"%s\" was not found.", endpt_name);
108 return 1;
109}
110
roman2eab4742023-06-06 10:00:26 +0200111#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +0200112
romanf02273a2023-05-25 09:44:11 +0200113int
114nc_server_config_get_hostkey(const struct lyd_node *node, const struct nc_server_ssh_opts *opts, struct nc_hostkey **hostkey)
romanc1d2b092023-02-02 08:58:27 +0100115{
116 uint16_t i;
117 const char *hostkey_name;
118
119 assert(node && opts);
120
121 while (node) {
122 if (!strcmp(LYD_NAME(node), "host-key")) {
123 break;
124 }
125 node = lyd_parent(node);
126 }
127
128 if (!node) {
129 ERR(NULL, "Node \"%s\" is not contained in a host-key subtree.", LYD_NAME(node));
130 return 1;
131 }
132
133 node = lyd_child(node);
134 assert(!strcmp(LYD_NAME(node), "name"));
135 hostkey_name = lyd_get_value(node);
136
137 for (i = 0; i < opts->hostkey_count; i++) {
138 if (!strcmp(opts->hostkeys[i].name, hostkey_name)) {
139 *hostkey = &opts->hostkeys[i];
140 return 0;
141 }
142 }
143
144 ERR(NULL, "Host-key \"%s\" was not found.", hostkey_name);
145 return 1;
146}
147
romanf02273a2023-05-25 09:44:11 +0200148int
149nc_server_config_get_auth_client(const struct lyd_node *node, const struct nc_server_ssh_opts *opts, struct nc_client_auth **auth_client)
romanc1d2b092023-02-02 08:58:27 +0100150{
151 uint16_t i;
152 const char *authkey_name;
153
154 assert(node && opts);
155
156 while (node) {
157 if (!strcmp(LYD_NAME(node), "user")) {
158 break;
159 }
160 node = lyd_parent(node);
161 }
162
163 if (!node) {
164 ERR(NULL, "Node \"%s\" is not contained in a client-authentication subtree.", LYD_NAME(node));
165 return 1;
166 }
167
168 node = lyd_child(node);
169 assert(!strcmp(LYD_NAME(node), "name"));
170 authkey_name = lyd_get_value(node);
171
172 for (i = 0; i < opts->client_count; i++) {
173 if (!strcmp(opts->auth_clients[i].username, authkey_name)) {
174 *auth_client = &opts->auth_clients[i];
175 return 0;
176 }
177 }
178
179 ERR(NULL, "Authorized key \"%s\" was not found.", authkey_name);
180 return 1;
181}
182
romanf02273a2023-05-25 09:44:11 +0200183int
184nc_server_config_get_pubkey(const struct lyd_node *node, const struct nc_client_auth *auth_client, struct nc_public_key **pubkey)
romanc1d2b092023-02-02 08:58:27 +0100185{
186 uint16_t i;
187 const char *pubkey_name;
188
189 assert(node && auth_client);
190
191 node = lyd_parent(node);
192 while (node) {
193 if (!strcmp(LYD_NAME(node), "public-key")) {
194 break;
195 }
196 node = lyd_parent(node);
197 }
198
199 if (!node) {
200 ERR(NULL, "Node \"%s\" is not contained in a public-key subtree.", LYD_NAME(node));
201 return 1;
202 }
203
204 node = lyd_child(node);
205 assert(!strcmp(LYD_NAME(node), "name"));
206 pubkey_name = lyd_get_value(node);
207
208 for (i = 0; i < auth_client->pubkey_count; i++) {
209 if (!strcmp(auth_client->pubkeys[i].name, pubkey_name)) {
210 *pubkey = &auth_client->pubkeys[i];
211 return 0;
212 }
213 }
214
215 ERR(NULL, "Public key \"%s\" was not found.", pubkey_name);
216 return 1;
217}
218
roman3f9b65c2023-06-05 14:26:58 +0200219int
220nc_server_config_get_cert(const struct lyd_node *node, struct nc_cert_grouping *auth_client, struct nc_certificate **cert)
221{
222 uint16_t i;
223 const char *cert_name;
224
225 assert(node && auth_client);
226
227 while (node) {
228 if (!strcmp(LYD_NAME(node), "certificate")) {
229 break;
230 }
231 node = lyd_parent(node);
232 }
233
234 if (!node) {
235 ERR(NULL, "Node \"%s\" is not contained in a certificate subtree.", LYD_NAME(node));
236 return 1;
237 }
238
239 node = lyd_child(node);
240 assert(!strcmp(LYD_NAME(node), "name"));
241 cert_name = lyd_get_value(node);
242
243 for (i = 0; i < auth_client->cert_count; i++) {
244 if (!strcmp(auth_client->certs[i].name, cert_name)) {
245 *cert = &auth_client->certs[i];
246 return 0;
247 }
248 }
249
250 ERR(NULL, "Certificate \"%s\" was not found.", cert_name);
251 return 1;
252}
253
254static int
255nc_server_config_get_ctn(const struct lyd_node *node, struct nc_endpt *endpt, struct nc_ctn **ctn)
256{
257 uint32_t id;
258 struct nc_ctn *iter;
259
260 assert(node && endpt);
261
262 node = lyd_parent(node);
263 while (node) {
264 if (!strcmp(LYD_NAME(node), "cert-to-name")) {
265 break;
266 }
267 node = lyd_parent(node);
268 }
269
270 if (!node) {
271 ERR(NULL, "Node \"%s\" is not contained in a cert-to-name subtree.", LYD_NAME(node));
272 return 1;
273 }
274
275 node = lyd_child(node);
276 assert(!strcmp(LYD_NAME(node), "id"));
277 id = strtoul(lyd_get_value(node), NULL, 10);
278
279 iter = endpt->opts.tls->ctn;
280 while (iter) {
281 if (iter->id == id) {
282 *ctn = iter;
283 return 0;
284 }
285
286 iter = iter->next;
287 }
288
289 ERR(NULL, "Cert-to-name entry with id \"%d\" was not found.", id);
290 return 1;
291}
292
roman2eab4742023-06-06 10:00:26 +0200293NC_PRIVKEY_FORMAT
294nc_server_config_get_private_key_type(const char *format)
295{
296 if (!strcmp(format, "rsa-private-key-format")) {
297 return NC_PRIVKEY_FORMAT_RSA;
298 } else if (!strcmp(format, "ec-private-key-format")) {
299 return NC_PRIVKEY_FORMAT_EC;
300 } else if (!strcmp(format, "subject-private-key-info-format")) {
301 return NC_PRIVKEY_FORMAT_X509;
302 } else if (!strcmp(format, "openssh-private-key-format")) {
303 return NC_PRIVKEY_FORMAT_OPENSSH;
304 } else {
305 ERR(NULL, "Private key format (%s) not supported.", format);
306 return NC_PRIVKEY_FORMAT_UNKNOWN;
307 }
308}
309
310#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +0200311
romanf02273a2023-05-25 09:44:11 +0200312int
romanc1d2b092023-02-02 08:58:27 +0100313equal_parent_name(const struct lyd_node *node, uint16_t parent_count, const char *parent_name)
314{
315 uint16_t i;
316
317 assert(node && parent_count > 0 && parent_name);
318
319 node = lyd_parent(node);
320 for (i = 1; i < parent_count; i++) {
321 node = lyd_parent(node);
322 }
323
324 if (!strcmp(LYD_NAME(node), parent_name)) {
325 return 1;
326 }
327
328 return 0;
329}
330
romanf02273a2023-05-25 09:44:11 +0200331int
332nc_server_config_realloc(const char *key_value, void **ptr, size_t size, uint16_t *count)
333{
334 int ret = 0;
335 void *tmp;
336 char **name;
337
338 tmp = realloc(*ptr, (*count + 1) * size);
339 if (!tmp) {
340 ERRMEM;
341 ret = 1;
342 goto cleanup;
343 }
344 *ptr = tmp;
345
346 /* set the newly allocated memory to 0 */
347 memset((char *)(*ptr) + (*count * size), 0, size);
348 (*count)++;
349
350 /* access the first member of the supposed structure */
351 name = (char **)((*ptr) + ((*count - 1) * size));
352
353 /* and set it's value */
354 *name = strdup(key_value);
355 if (!*name) {
356 ERRMEM;
357 ret = 1;
358 goto cleanup;
359 }
360
361cleanup:
362 return ret;
363}
364
roman2eab4742023-06-06 10:00:26 +0200365#ifdef NC_ENABLED_SSH_TLS
366
roman3f9b65c2023-06-05 14:26:58 +0200367static int
368is_listen(const struct lyd_node *node)
369{
370 assert(node);
371
372 while (node) {
373 if (!strcmp(LYD_NAME(node), "listen")) {
374 break;
375 }
376 node = lyd_parent(node);
377 }
378
379 return node != NULL;
380}
381
382// static int
383// is_ch(const struct lyd_node *node)
384// {
385// assert(node);
386
387// while (node) {
388// if (!strcmp(LYD_NAME(node), "call-home")) {
389// break;
390// }
391// node = lyd_parent(node);
392// }
393
394// return node != NULL;
395// }
396
roman3f9b65c2023-06-05 14:26:58 +0200397static int
398is_ssh(const struct lyd_node *node)
399{
400 assert(node);
401
402 while (node) {
403 if (!strcmp(LYD_NAME(node), "ssh")) {
404 break;
405 }
406 node = lyd_parent(node);
407 }
408
409 return node != NULL;
410}
411
roman3f9b65c2023-06-05 14:26:58 +0200412static int
413is_tls(const struct lyd_node *node)
414{
415 assert(node);
416
417 while (node) {
418 if (!strcmp(LYD_NAME(node), "tls")) {
419 break;
420 }
421 node = lyd_parent(node);
422 }
423
424 return node != NULL;
425}
426
roman2eab4742023-06-06 10:00:26 +0200427#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +0200428
429static void
430nc_server_config_del_endpt_name(struct nc_endpt *endpt)
431{
432 free(endpt->name);
433 endpt->name = NULL;
434}
435
roman2eab4742023-06-06 10:00:26 +0200436#ifdef NC_ENABLED_SSH_TLS
437
roman3f9b65c2023-06-05 14:26:58 +0200438static void
439nc_server_config_del_local_address(struct nc_bind *bind)
440{
441 free(bind->address);
442 bind->address = NULL;
443}
444
romanc1d2b092023-02-02 08:58:27 +0100445static void
roman874fed12023-05-25 10:20:01 +0200446nc_server_config_del_auth_client_pam_name(struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100447{
448 free(auth_client->pam_config_name);
449 auth_client->pam_config_name = NULL;
450}
451
452static void
roman874fed12023-05-25 10:20:01 +0200453nc_server_config_del_auth_client_pam_dir(struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100454{
455 free(auth_client->pam_config_dir);
456 auth_client->pam_config_dir = NULL;
457}
458
459static void
roman0bbc19c2023-05-26 09:59:09 +0200460nc_server_config_del_endpt_reference(struct nc_endpt *endpt)
461{
462 free(endpt->referenced_endpt_name);
463 endpt->referenced_endpt_name = NULL;
464}
465
466static void
roman874fed12023-05-25 10:20:01 +0200467nc_server_config_del_hostkey_name(struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +0100468{
469 free(hostkey->name);
470 hostkey->name = NULL;
471}
472
473static void
roman874fed12023-05-25 10:20:01 +0200474nc_server_config_del_public_key(struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +0100475{
roman3f9b65c2023-06-05 14:26:58 +0200476 free(hostkey->key.pubkey_data);
477 hostkey->key.pubkey_data = NULL;
romanc1d2b092023-02-02 08:58:27 +0100478}
479
480static void
roman874fed12023-05-25 10:20:01 +0200481nc_server_config_del_private_key(struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +0100482{
roman3f9b65c2023-06-05 14:26:58 +0200483 free(hostkey->key.privkey_data);
484 hostkey->key.privkey_data = NULL;
romanc1d2b092023-02-02 08:58:27 +0100485}
486
487static void
roman874fed12023-05-25 10:20:01 +0200488nc_server_config_del_auth_client_username(struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100489{
490 free(auth_client->username);
491 auth_client->username = NULL;
492}
493
494static void
roman874fed12023-05-25 10:20:01 +0200495nc_server_config_del_auth_client_pubkey_name(struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100496{
497 free(pubkey->name);
498 pubkey->name = NULL;
499}
500
501static void
roman874fed12023-05-25 10:20:01 +0200502nc_server_config_del_auth_client_pubkey_pub_base64(struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100503{
roman3f9b65c2023-06-05 14:26:58 +0200504 free(pubkey->data);
505 pubkey->data = NULL;
romanc1d2b092023-02-02 08:58:27 +0100506}
507
508static void
roman874fed12023-05-25 10:20:01 +0200509nc_server_config_del_auth_client_password(struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100510{
511 free(auth_client->password);
512 auth_client->password = NULL;
513}
514
515static void
roman874fed12023-05-25 10:20:01 +0200516nc_server_config_del_hostkey_algs(struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100517{
518 free(opts->hostkey_algs);
519 opts->hostkey_algs = NULL;
520}
521
522static void
roman874fed12023-05-25 10:20:01 +0200523nc_server_config_del_kex_algs(struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100524{
525 free(opts->kex_algs);
526 opts->kex_algs = NULL;
527}
528
529static void
roman874fed12023-05-25 10:20:01 +0200530nc_server_config_del_encryption_algs(struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100531{
532 free(opts->encryption_algs);
533 opts->encryption_algs = NULL;
534}
535
536static void
roman874fed12023-05-25 10:20:01 +0200537nc_server_config_del_mac_algs(struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100538{
539 free(opts->mac_algs);
540 opts->mac_algs = NULL;
541}
542
543static void
roman874fed12023-05-25 10:20:01 +0200544nc_server_config_del_hostkey(struct nc_server_ssh_opts *opts, struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +0100545{
roman874fed12023-05-25 10:20:01 +0200546 assert(hostkey->store == NC_STORE_LOCAL || hostkey->store == NC_STORE_KEYSTORE);
romanc1d2b092023-02-02 08:58:27 +0100547
roman874fed12023-05-25 10:20:01 +0200548 if (hostkey->store == NC_STORE_LOCAL) {
549 nc_server_config_del_public_key(hostkey);
550 nc_server_config_del_private_key(hostkey);
romanc1d2b092023-02-02 08:58:27 +0100551 }
552
roman874fed12023-05-25 10:20:01 +0200553 nc_server_config_del_hostkey_name(hostkey);
romanc1d2b092023-02-02 08:58:27 +0100554 opts->hostkey_count--;
555 if (!opts->hostkey_count) {
556 free(opts->hostkeys);
557 opts->hostkeys = NULL;
558 }
559}
560
561static void
roman874fed12023-05-25 10:20:01 +0200562nc_server_config_del_auth_client_pubkey(struct nc_client_auth *auth_client, struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100563{
roman874fed12023-05-25 10:20:01 +0200564 nc_server_config_del_auth_client_pubkey_name(pubkey);
565 nc_server_config_del_auth_client_pubkey_pub_base64(pubkey);
romanc1d2b092023-02-02 08:58:27 +0100566
567 auth_client->pubkey_count--;
568 if (!auth_client->pubkey_count) {
569 free(auth_client->pubkeys);
570 auth_client->pubkeys = NULL;
571 }
572}
573
574static void
roman874fed12023-05-25 10:20:01 +0200575nc_server_config_del_auth_client(struct nc_server_ssh_opts *opts, struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100576{
577 uint16_t i, pubkey_count;
578
roman874fed12023-05-25 10:20:01 +0200579 if (auth_client->store == NC_STORE_LOCAL) {
romanc1d2b092023-02-02 08:58:27 +0100580 pubkey_count = auth_client->pubkey_count;
581 for (i = 0; i < pubkey_count; i++) {
roman874fed12023-05-25 10:20:01 +0200582 nc_server_config_del_auth_client_pubkey(auth_client, &auth_client->pubkeys[i]);
romanc1d2b092023-02-02 08:58:27 +0100583 }
romanc1d2b092023-02-02 08:58:27 +0100584 }
585
roman874fed12023-05-25 10:20:01 +0200586 nc_server_config_del_auth_client_password(auth_client);
587 nc_server_config_del_auth_client_pam_name(auth_client);
588 nc_server_config_del_auth_client_pam_dir(auth_client);
589 nc_server_config_del_auth_client_username(auth_client);
romanc1d2b092023-02-02 08:58:27 +0100590
591 opts->client_count--;
592 if (!opts->client_count) {
593 free(opts->auth_clients);
594 opts->auth_clients = NULL;
595 }
596}
597
598static void
roman874fed12023-05-25 10:20:01 +0200599nc_server_config_del_ssh(struct nc_bind *bind, struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100600{
601 uint16_t i, hostkey_count, client_count;
602
roman874fed12023-05-25 10:20:01 +0200603 nc_server_config_del_local_address(bind);
romanc1d2b092023-02-02 08:58:27 +0100604 if (bind->sock > -1) {
605 close(bind->sock);
606 }
607
608 /* store in variable because it gets decremented in the function call */
609 hostkey_count = opts->hostkey_count;
610 for (i = 0; i < hostkey_count; i++) {
roman874fed12023-05-25 10:20:01 +0200611 nc_server_config_del_hostkey(opts, &opts->hostkeys[i]);
romanc1d2b092023-02-02 08:58:27 +0100612 }
613
614 client_count = opts->client_count;
615 for (i = 0; i < client_count; i++) {
roman874fed12023-05-25 10:20:01 +0200616 nc_server_config_del_auth_client(opts, &opts->auth_clients[i]);
romanc1d2b092023-02-02 08:58:27 +0100617 }
618
roman874fed12023-05-25 10:20:01 +0200619 nc_server_config_del_hostkey_algs(opts);
620 nc_server_config_del_kex_algs(opts);
621 nc_server_config_del_encryption_algs(opts);
622 nc_server_config_del_mac_algs(opts);
romanc1d2b092023-02-02 08:58:27 +0100623
624 free(opts);
625 opts = NULL;
626}
627
628void
roman874fed12023-05-25 10:20:01 +0200629nc_server_config_del_endpt_ssh(struct nc_endpt *endpt, struct nc_bind *bind)
romanc1d2b092023-02-02 08:58:27 +0100630{
roman874fed12023-05-25 10:20:01 +0200631 nc_server_config_del_endpt_name(endpt);
roman0bbc19c2023-05-26 09:59:09 +0200632 nc_server_config_del_endpt_reference(endpt);
roman874fed12023-05-25 10:20:01 +0200633 nc_server_config_del_ssh(bind, endpt->opts.ssh);
romanc1d2b092023-02-02 08:58:27 +0100634
635 server_opts.endpt_count--;
636 if (!server_opts.endpt_count) {
637 free(server_opts.endpts);
638 free(server_opts.binds);
639 server_opts.endpts = NULL;
640 server_opts.binds = NULL;
641 }
642}
643
roman2eab4742023-06-06 10:00:26 +0200644#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +0200645
roman45cec4e2023-02-17 10:21:39 +0100646void
roman874fed12023-05-25 10:20:01 +0200647nc_server_config_del_unix_socket(struct nc_bind *bind, struct nc_server_unix_opts *opts)
roman83683fb2023-02-24 09:15:23 +0100648{
649 if (bind->sock > -1) {
650 close(bind->sock);
651 }
652
653 free(bind->address);
654 free(opts->address);
655
656 free(opts);
roman83683fb2023-02-24 09:15:23 +0100657}
658
659void
roman874fed12023-05-25 10:20:01 +0200660nc_server_config_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *bind)
roman83683fb2023-02-24 09:15:23 +0100661{
roman874fed12023-05-25 10:20:01 +0200662 nc_server_config_del_endpt_name(endpt);
663 nc_server_config_del_unix_socket(bind, endpt->opts.unixsock);
roman83683fb2023-02-24 09:15:23 +0100664
665 server_opts.endpt_count--;
666 if (!server_opts.endpt_count) {
667 free(server_opts.endpts);
668 free(server_opts.binds);
669 server_opts.endpts = NULL;
670 server_opts.binds = NULL;
671 }
672}
673
roman2eab4742023-06-06 10:00:26 +0200674#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +0200675
676static void
romanfaecc582023-06-15 16:13:31 +0200677nc_server_config_del_url(struct nc_server_tls_opts *opts)
678{
679 free(opts->crl_url);
680 opts->crl_url = NULL;
681}
682
683static void
684nc_server_config_del_path(struct nc_server_tls_opts *opts)
685{
686 free(opts->crl_path);
687 opts->crl_path = NULL;
688}
689
690static void
roman12644fe2023-06-08 11:06:42 +0200691nc_server_config_tls_del_ciphers(struct nc_server_tls_opts *opts)
692{
693 free(opts->ciphers);
694 opts->ciphers = NULL;
695}
696
697static void
roman3f9b65c2023-06-05 14:26:58 +0200698nc_server_config_tls_del_public_key(struct nc_server_tls_opts *opts)
699{
700 free(opts->pubkey_data);
701 opts->pubkey_data = NULL;
702}
703
704static void
705nc_server_config_tls_del_cleartext_private_key(struct nc_server_tls_opts *opts)
706{
707 free(opts->privkey_data);
708 opts->privkey_data = NULL;
709}
710
711static void
712nc_server_config_tls_del_cert_data(struct nc_server_tls_opts *opts)
713{
714 free(opts->cert_data);
715 opts->cert_data = NULL;
716}
717
718static void
719nc_server_config_tls_del_cert_data_certificate(struct nc_certificate *cert)
720{
721 free(cert->data);
722 cert->data = NULL;
723}
724
725static void
726nc_server_config_del_fingerprint(struct nc_ctn *ctn)
727{
728 free(ctn->fingerprint);
729 ctn->fingerprint = NULL;
730}
731
732static void
733nc_server_config_del_cert(struct nc_cert_grouping *certs, struct nc_certificate *cert)
734{
735 free(cert->name);
736 cert->name = NULL;
737
738 free(cert->data);
739 cert->data = NULL;
740
741 certs->cert_count--;
742 if (!certs->cert_count) {
743 free(certs->certs);
744 certs->certs = NULL;
745 }
746}
747
748static void
749nc_server_config_tls_del_certs(struct nc_cert_grouping *ca)
750{
751 uint16_t i, cert_count;
752
753 if (ca->store == NC_STORE_LOCAL) {
754 cert_count = ca->cert_count;
755 for (i = 0; i < cert_count; i++) {
756 nc_server_config_del_cert(ca, &ca->certs[i]);
757 }
758 }
759}
760
761static void
762nc_server_config_del_ctn(struct nc_server_tls_opts *opts, struct nc_ctn *ctn)
763{
764 struct nc_ctn *iter;
765
766 free(ctn->fingerprint);
767 ctn->fingerprint = NULL;
768
769 free(ctn->name);
770 ctn->name = NULL;
771
772 if (opts->ctn == ctn) {
773 /* it's the first in the list */
774 opts->ctn = ctn->next;
775 free(ctn);
776 return;
777 }
778
779 iter = opts->ctn;
780 while (iter) {
781 if (iter->next == ctn) {
782 /* found the ctn */
783 break;
784 }
785 iter = iter->next;
786 }
787
788 iter->next = ctn->next;
789 free(ctn);
790}
791
792static void
roman12644fe2023-06-08 11:06:42 +0200793nc_server_config_tls_del_ctns(struct nc_server_tls_opts *opts)
roman3f9b65c2023-06-05 14:26:58 +0200794{
795 struct nc_ctn *cur, *next;
796
797 cur = opts->ctn;
798 while (cur) {
799 next = cur->next;
800 free(cur->fingerprint);
801 free(cur->name);
802 free(cur);
803 cur = next;
804 }
805 opts->ctn = NULL;
806}
807
808static void
809nc_server_config_del_tls(struct nc_bind *bind, struct nc_server_tls_opts *opts)
810{
811 nc_server_config_del_local_address(bind);
812 if (bind->sock > -1) {
813 close(bind->sock);
814 }
815
816 if (opts->store == NC_STORE_LOCAL) {
817 nc_server_config_tls_del_public_key(opts);
818 nc_server_config_tls_del_cleartext_private_key(opts);
819 nc_server_config_tls_del_cert_data(opts);
820 }
821
822 nc_server_config_tls_del_certs(&opts->ca_certs);
823 nc_server_config_tls_del_certs(&opts->ee_certs);
824
romanfaecc582023-06-15 16:13:31 +0200825 nc_server_config_del_path(opts);
826 nc_server_config_del_url(opts);
827 X509_STORE_free(opts->crl_store);
828 opts->crl_store = NULL;
829
roman12644fe2023-06-08 11:06:42 +0200830 nc_server_config_tls_del_ctns(opts);
831 nc_server_config_tls_del_ciphers(opts);
roman3f9b65c2023-06-05 14:26:58 +0200832
833 free(opts);
834}
835
836static void
837nc_server_config_del_endpt_tls(struct nc_endpt *endpt, struct nc_bind *bind)
838{
839 nc_server_config_del_endpt_name(endpt);
roman2e797ef2023-06-19 10:47:49 +0200840 nc_server_config_del_endpt_reference(endpt);
roman3f9b65c2023-06-05 14:26:58 +0200841 nc_server_config_del_tls(bind, endpt->opts.tls);
842
843 server_opts.endpt_count--;
844 if (!server_opts.endpt_count) {
845 free(server_opts.endpts);
846 free(server_opts.binds);
847 server_opts.endpts = NULL;
848 server_opts.binds = NULL;
849 }
850}
851
roman2eab4742023-06-06 10:00:26 +0200852#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +0200853
romanc1d2b092023-02-02 08:58:27 +0100854/* presence container */
855int
romanf02273a2023-05-25 09:44:11 +0200856nc_server_config_listen(struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100857{
roman0bbc19c2023-05-26 09:59:09 +0200858 uint16_t i, endpt_count;
romanc1d2b092023-02-02 08:58:27 +0100859
romanf02273a2023-05-25 09:44:11 +0200860 (void) node;
861
romanc1d2b092023-02-02 08:58:27 +0100862 assert(op == NC_OP_CREATE || op == NC_OP_DELETE);
863
864 if (op == NC_OP_DELETE) {
roman0bbc19c2023-05-26 09:59:09 +0200865 endpt_count = server_opts.endpt_count;
866 for (i = 0; i < endpt_count; i++) {
roman456f92d2023-04-28 10:28:12 +0200867 switch (server_opts.endpts[i].ti) {
roman2eab4742023-06-06 10:00:26 +0200868#ifdef NC_ENABLED_SSH_TLS
roman456f92d2023-04-28 10:28:12 +0200869 case NC_TI_LIBSSH:
roman874fed12023-05-25 10:20:01 +0200870 nc_server_config_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]);
roman456f92d2023-04-28 10:28:12 +0200871 break;
roman456f92d2023-04-28 10:28:12 +0200872 case NC_TI_OPENSSL:
roman3f9b65c2023-06-05 14:26:58 +0200873 nc_server_config_del_endpt_tls(&server_opts.endpts[i], &server_opts.binds[i]);
roman456f92d2023-04-28 10:28:12 +0200874 break;
roman2eab4742023-06-06 10:00:26 +0200875#endif /* NC_ENABLED_SSH_TLS */
roman456f92d2023-04-28 10:28:12 +0200876 case NC_TI_UNIX:
roman874fed12023-05-25 10:20:01 +0200877 nc_server_config_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]);
roman456f92d2023-04-28 10:28:12 +0200878 break;
879 case NC_TI_NONE:
880 case NC_TI_FD:
881 ERRINT;
882 return 1;
roman83683fb2023-02-24 09:15:23 +0100883 }
romanc1d2b092023-02-02 08:58:27 +0100884 }
885 }
886
887 return 0;
888}
889
890/* default leaf */
891static int
romane028ef92023-02-24 16:33:08 +0100892nc_server_config_idle_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100893{
894 assert(!strcmp(LYD_NAME(node), "idle-timeout"));
895
896 if (equal_parent_name(node, 1, "listen")) {
897 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
898 server_opts.idle_timeout = strtoul(lyd_get_value(node), NULL, 10);
899 } else {
900 /* default value */
901 server_opts.idle_timeout = 3600;
902 }
903 }
904
905 return 0;
906}
907
908static int
roman874fed12023-05-25 10:20:01 +0200909nc_server_config_create_bind(void)
romanc1d2b092023-02-02 08:58:27 +0100910{
911 int ret = 0;
912 void *tmp;
913
914 tmp = realloc(server_opts.binds, (server_opts.endpt_count + 1) * sizeof *server_opts.binds);
915 if (!tmp) {
916 ERRMEM;
917 ret = 1;
918 goto cleanup;
919 }
920 server_opts.binds = tmp;
921 memset(&server_opts.binds[server_opts.endpt_count], 0, sizeof *server_opts.binds);
922
923 server_opts.binds[server_opts.endpt_count].sock = -1;
924
925cleanup:
926 return ret;
927}
928
929static int
roman874fed12023-05-25 10:20:01 +0200930nc_server_config_create_endpoint(const struct lyd_node *node)
romanc1d2b092023-02-02 08:58:27 +0100931{
roman874fed12023-05-25 10:20:01 +0200932 if (nc_server_config_create_bind()) {
romanf02273a2023-05-25 09:44:11 +0200933 return 1;
romanc1d2b092023-02-02 08:58:27 +0100934 }
romanc1d2b092023-02-02 08:58:27 +0100935
936 node = lyd_child(node);
937 assert(!strcmp(LYD_NAME(node), "name"));
938
romanf02273a2023-05-25 09:44:11 +0200939 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 +0100940}
941
942/* list */
943static int
romane028ef92023-02-24 16:33:08 +0100944nc_server_config_endpoint(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100945{
946 int ret = 0;
947 struct nc_endpt *endpt;
948 struct nc_bind *bind;
949
950 assert(!strcmp(LYD_NAME(node), "endpoint"));
951
952 if (op == NC_OP_CREATE) {
roman874fed12023-05-25 10:20:01 +0200953 ret = nc_server_config_create_endpoint(node);
romanc1d2b092023-02-02 08:58:27 +0100954 if (ret) {
955 goto cleanup;
956 }
957 } else if (op == NC_OP_DELETE) {
958 /* free all children */
romanf02273a2023-05-25 09:44:11 +0200959 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +0100960 ret = 1;
961 goto cleanup;
962 }
roman3f9b65c2023-06-05 14:26:58 +0200963
964 switch (endpt->ti) {
roman2eab4742023-06-06 10:00:26 +0200965#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +0200966 case NC_TI_LIBSSH:
967 nc_server_config_del_endpt_ssh(endpt, bind);
968 break;
roman3f9b65c2023-06-05 14:26:58 +0200969 case NC_TI_OPENSSL:
970 nc_server_config_del_endpt_tls(endpt, bind);
971 break;
roman2eab4742023-06-06 10:00:26 +0200972#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +0200973 case NC_TI_UNIX:
974 nc_server_config_del_endpt_unix_socket(endpt, bind);
975 break;
976 case NC_TI_NONE:
977 case NC_TI_FD:
978 ERRINT;
979 ret = 1;
980 goto cleanup;
981 }
romanc1d2b092023-02-02 08:58:27 +0100982 }
983
984cleanup:
985 return ret;
986}
987
roman2eab4742023-06-06 10:00:26 +0200988#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +0200989
romanc1d2b092023-02-02 08:58:27 +0100990static int
roman874fed12023-05-25 10:20:01 +0200991nc_server_config_create_ssh(struct nc_endpt *endpt)
romanc1d2b092023-02-02 08:58:27 +0100992{
993 endpt->ti = NC_TI_LIBSSH;
994 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
995 if (!endpt->opts.ssh) {
996 ERRMEM;
997 return 1;
998 }
999
1000 return 0;
1001}
1002
1003/* NP container */
1004static int
romane028ef92023-02-24 16:33:08 +01001005nc_server_config_ssh(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001006{
1007 struct nc_endpt *endpt;
1008 struct nc_bind *bind;
1009 int ret = 0;
1010
1011 assert(!strcmp(LYD_NAME(node), "ssh"));
1012
romanf02273a2023-05-25 09:44:11 +02001013 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001014 ret = 1;
1015 goto cleanup;
1016 }
1017
1018 if (op == NC_OP_CREATE) {
roman874fed12023-05-25 10:20:01 +02001019 ret = nc_server_config_create_ssh(endpt);
romanc1d2b092023-02-02 08:58:27 +01001020 if (ret) {
1021 goto cleanup;
1022 }
1023 } else if (op == NC_OP_DELETE) {
roman874fed12023-05-25 10:20:01 +02001024 nc_server_config_del_ssh(bind, endpt->opts.ssh);
romanc1d2b092023-02-02 08:58:27 +01001025 }
1026
1027cleanup:
1028 return ret;
1029}
1030
roman3f9b65c2023-06-05 14:26:58 +02001031static int
1032nc_server_config_create_tls(struct nc_endpt *endpt)
1033{
1034 endpt->ti = NC_TI_OPENSSL;
1035 endpt->opts.tls = calloc(1, sizeof *endpt->opts.tls);
1036 if (!endpt->opts.tls) {
1037 ERRMEM;
1038 return 1;
1039 }
1040
1041 return 0;
1042}
1043
1044static int
1045nc_server_config_tls(const struct lyd_node *node, NC_OPERATION op)
1046{
1047 struct nc_endpt *endpt;
1048 struct nc_bind *bind;
1049 int ret = 0;
1050
1051 assert(!strcmp(LYD_NAME(node), "tls"));
1052
1053 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
1054 ret = 1;
1055 goto cleanup;
1056 }
1057
1058 if (op == NC_OP_CREATE) {
1059 ret = nc_server_config_create_tls(endpt);
1060 if (ret) {
1061 goto cleanup;
1062 }
1063 } else if (op == NC_OP_DELETE) {
1064 nc_server_config_del_tls(bind, endpt->opts.tls);
1065 }
1066
1067cleanup:
1068 return ret;
1069}
1070
roman2eab4742023-06-06 10:00:26 +02001071#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +02001072
romanc1d2b092023-02-02 08:58:27 +01001073static int
1074nc_server_config_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port)
1075{
1076 int sock = -1, set_addr, ret = 0;
1077
roman83683fb2023-02-24 09:15:23 +01001078 assert((address && !port) || (!address && port) || (endpt->ti == NC_TI_UNIX));
romanc1d2b092023-02-02 08:58:27 +01001079
1080 if (address) {
1081 set_addr = 1;
1082 } else {
1083 set_addr = 0;
1084 }
1085
1086 if (set_addr) {
1087 port = bind->port;
1088 } else {
1089 address = bind->address;
1090 }
1091
romanc1d2b092023-02-02 08:58:27 +01001092 /* we have all the information we need to create a listening socket */
roman83683fb2023-02-24 09:15:23 +01001093 if ((address && port) || (endpt->ti == NC_TI_UNIX)) {
romanc1d2b092023-02-02 08:58:27 +01001094 /* create new socket, close the old one */
roman83683fb2023-02-24 09:15:23 +01001095 if (endpt->ti == NC_TI_UNIX) {
1096 sock = nc_sock_listen_unix(endpt->opts.unixsock);
1097 } else {
1098 sock = nc_sock_listen_inet(address, port, &endpt->ka);
1099 }
1100
romanc1d2b092023-02-02 08:58:27 +01001101 if (sock == -1) {
1102 ret = 1;
1103 goto cleanup;
1104 }
1105
1106 if (bind->sock > -1) {
1107 close(bind->sock);
1108 }
1109 bind->sock = sock;
1110 }
1111
1112 if (sock > -1) {
1113 switch (endpt->ti) {
roman83683fb2023-02-24 09:15:23 +01001114 case NC_TI_UNIX:
1115 VRB(NULL, "Listening on %s for UNIX connections.", endpt->opts.unixsock->address);
1116 break;
roman2eab4742023-06-06 10:00:26 +02001117#ifdef NC_ENABLED_SSH_TLS
romanc1d2b092023-02-02 08:58:27 +01001118 case NC_TI_LIBSSH:
1119 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
1120 break;
romanc1d2b092023-02-02 08:58:27 +01001121 case NC_TI_OPENSSL:
1122 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
1123 break;
roman2eab4742023-06-06 10:00:26 +02001124#endif /* NC_ENABLED_SSH_TLS */
romanc1d2b092023-02-02 08:58:27 +01001125 default:
1126 ERRINT;
1127 ret = 1;
1128 break;
1129 }
1130 }
1131
1132cleanup:
1133 return ret;
1134}
1135
roman2eab4742023-06-06 10:00:26 +02001136#ifdef NC_ENABLED_SSH_TLS
1137
romanc1d2b092023-02-02 08:58:27 +01001138/* mandatory leaf */
1139static int
romane028ef92023-02-24 16:33:08 +01001140nc_server_config_local_address(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001141{
1142 struct nc_endpt *endpt;
1143 struct nc_bind *bind;
1144 int ret = 0;
1145
1146 (void) op;
1147
1148 assert(!strcmp(LYD_NAME(node), "local-address"));
1149
1150 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001151 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001152 ret = 1;
1153 goto cleanup;
1154 }
1155
roman874fed12023-05-25 10:20:01 +02001156 nc_server_config_del_local_address(bind);
romanc1d2b092023-02-02 08:58:27 +01001157 bind->address = strdup(lyd_get_value(node));
1158 if (!bind->address) {
1159 ERRMEM;
1160 ret = 1;
1161 goto cleanup;
1162 }
1163
1164 ret = nc_server_config_set_address_port(endpt, bind, lyd_get_value(node), 0);
1165 if (ret) {
1166 goto cleanup;
1167 }
1168 }
1169
1170cleanup:
1171 return ret;
1172}
1173
1174/* leaf with default value */
1175static int
romane028ef92023-02-24 16:33:08 +01001176nc_server_config_local_port(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001177{
1178 struct nc_endpt *endpt;
1179 struct nc_bind *bind;
1180 int ret = 0;
1181
1182 assert(!strcmp(LYD_NAME(node), "local-port"));
1183
1184 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001185 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001186 ret = 1;
1187 goto cleanup;
1188 }
1189
1190 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1191 bind->port = strtoul(lyd_get_value(node), NULL, 10);
1192 } else {
1193 /* delete -> set to default */
1194 bind->port = 0;
1195 }
1196
1197 ret = nc_server_config_set_address_port(endpt, bind, NULL, bind->port);
1198 if (ret) {
1199 goto cleanup;
1200 }
1201 }
1202
1203cleanup:
1204 return ret;
1205}
1206
1207/* P container */
1208static int
romane028ef92023-02-24 16:33:08 +01001209nc_server_config_keepalives(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001210{
1211 struct nc_endpt *endpt;
1212 struct nc_bind *bind;
1213 int ret = 0;
1214
1215 assert(!strcmp(LYD_NAME(node), "keepalives"));
1216
roman3f9b65c2023-06-05 14:26:58 +02001217 if (equal_parent_name(node, 1, "tcp-server-parameters")) {
romanf02273a2023-05-25 09:44:11 +02001218 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001219 ret = 1;
1220 goto cleanup;
1221 }
1222
1223 if (op == NC_OP_CREATE) {
1224 endpt->ka.enabled = 1;
1225 } else {
1226 endpt->ka.enabled = 0;
1227 }
1228 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
1229 if (ret) {
1230 goto cleanup;
1231 }
1232 }
1233
1234cleanup:
1235 return ret;
1236}
1237
1238/* mandatory leaf */
1239static int
romane028ef92023-02-24 16:33:08 +01001240nc_server_config_idle_time(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001241{
1242 struct nc_endpt *endpt;
1243 struct nc_bind *bind;
1244 int ret = 0;
1245
1246 assert(!strcmp(LYD_NAME(node), "idle-time"));
1247
1248 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001249 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001250 ret = 1;
1251 goto cleanup;
1252 }
1253
1254 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1255 endpt->ka.idle_time = strtoul(lyd_get_value(node), NULL, 10);
1256 } else {
1257 endpt->ka.idle_time = 0;
1258 }
1259 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
1260 if (ret) {
1261 goto cleanup;
1262 }
1263 }
1264
1265cleanup:
1266 return ret;
1267}
1268
1269/* mandatory leaf */
1270static int
romane028ef92023-02-24 16:33:08 +01001271nc_server_config_max_probes(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001272{
1273 struct nc_endpt *endpt;
1274 struct nc_bind *bind;
1275 int ret = 0;
1276
1277 assert(!strcmp(LYD_NAME(node), "max-probes"));
1278
1279 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001280 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001281 ret = 1;
1282 goto cleanup;
1283 }
1284
1285 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1286 endpt->ka.max_probes = strtoul(lyd_get_value(node), NULL, 10);
1287 } else {
1288 endpt->ka.max_probes = 0;
1289 }
1290 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
1291 if (ret) {
1292 goto cleanup;
1293 }
1294 }
1295
1296cleanup:
1297 return ret;
1298}
1299
1300/* mandatory leaf */
1301static int
romane028ef92023-02-24 16:33:08 +01001302nc_server_config_probe_interval(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001303{
1304 struct nc_endpt *endpt;
1305 struct nc_bind *bind;
1306 int ret = 0;
1307
1308 assert(!strcmp(LYD_NAME(node), "probe-interval"));
1309
1310 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001311 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +01001312 ret = 1;
1313 goto cleanup;
1314 }
1315
1316 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1317 endpt->ka.probe_interval = strtoul(lyd_get_value(node), NULL, 10);
1318 } else {
1319 endpt->ka.probe_interval = 0;
1320 }
1321 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
1322 if (ret) {
1323 goto cleanup;
1324 }
1325 }
1326
1327cleanup:
1328 return ret;
1329}
1330
1331static int
roman874fed12023-05-25 10:20:01 +02001332nc_server_config_create_host_key(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +01001333{
romanf02273a2023-05-25 09:44:11 +02001334 node = lyd_child(node);
1335 assert(!strcmp(LYD_NAME(node), "name"));
romanc1d2b092023-02-02 08:58:27 +01001336
romanf02273a2023-05-25 09:44:11 +02001337 return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->hostkeys, sizeof *opts->hostkeys, &opts->hostkey_count);
romanc1d2b092023-02-02 08:58:27 +01001338}
1339
1340/* list */
1341static int
romane028ef92023-02-24 16:33:08 +01001342nc_server_config_host_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001343{
1344 struct nc_endpt *endpt;
1345 struct nc_hostkey *hostkey;
1346 int ret = 0;
1347
1348 assert(!strcmp(LYD_NAME(node), "host-key"));
1349
1350 if ((equal_parent_name(node, 1, "server-identity")) && (equal_parent_name(node, 5, "listen"))) {
romanf02273a2023-05-25 09:44:11 +02001351 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001352 ret = 1;
1353 goto cleanup;
1354 }
1355
1356 if (op == NC_OP_CREATE) {
roman874fed12023-05-25 10:20:01 +02001357 ret = nc_server_config_create_host_key(node, endpt->opts.ssh);
romanc1d2b092023-02-02 08:58:27 +01001358 if (ret) {
1359 goto cleanup;
1360 }
1361 } else if (op == NC_OP_DELETE) {
romanf02273a2023-05-25 09:44:11 +02001362 if (nc_server_config_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01001363 ret = 1;
1364 goto cleanup;
1365 }
1366
roman874fed12023-05-25 10:20:01 +02001367 nc_server_config_del_hostkey(endpt->opts.ssh, hostkey);
romanc1d2b092023-02-02 08:58:27 +01001368 }
1369 } else if (equal_parent_name(node, 1, "transport-params")) {
1370 /* just a container with the name host-key, nothing to be done */
1371 goto cleanup;
1372 } else {
1373 ERRINT;
1374 ret = 1;
1375 goto cleanup;
1376 }
1377
1378cleanup:
1379 return ret;
1380}
1381
1382/* mandatory leaf */
romane028ef92023-02-24 16:33:08 +01001383static int
1384nc_server_config_public_key_format(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001385{
1386 const char *format;
roman3f9b65c2023-06-05 14:26:58 +02001387 int ret = 0;
1388 NC_PUBKEY_FORMAT pubkey_type;
romanc1d2b092023-02-02 08:58:27 +01001389 struct nc_endpt *endpt;
1390 struct nc_client_auth *auth_client;
roman8edee342023-03-31 13:25:48 +02001391 struct nc_public_key *pubkey;
romanc1d2b092023-02-02 08:58:27 +01001392 struct nc_hostkey *hostkey;
romanc1d2b092023-02-02 08:58:27 +01001393
1394 assert(!strcmp(LYD_NAME(node), "public-key-format"));
1395
1396 format = ((struct lyd_node_term *)node)->value.ident->name;
roman3f9b65c2023-06-05 14:26:58 +02001397 if (!strcmp(format, "ssh-public-key-format")) {
1398 pubkey_type = NC_PUBKEY_FORMAT_SSH2;
1399 } else if (!strcmp(format, "subject-public-key-info-format")) {
1400 pubkey_type = NC_PUBKEY_FORMAT_X509;
1401 } else {
1402 ERR(NULL, "Public key format (%s) not supported.", format);
1403 ret = 1;
1404 goto cleanup;
1405 }
romanc1d2b092023-02-02 08:58:27 +01001406
roman3f9b65c2023-06-05 14:26:58 +02001407 if ((equal_parent_name(node, 6, "client-authentication")) && (is_ssh(node)) && (is_listen(node))) {
romanf02273a2023-05-25 09:44:11 +02001408 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001409 ret = 1;
1410 goto cleanup;
1411 }
1412
romanf02273a2023-05-25 09:44:11 +02001413 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01001414 ret = 1;
1415 goto cleanup;
1416 }
1417
romanf02273a2023-05-25 09:44:11 +02001418 if (nc_server_config_get_pubkey(node, auth_client, &pubkey)) {
romanc1d2b092023-02-02 08:58:27 +01001419 ret = 1;
1420 goto cleanup;
1421 }
1422
1423 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman3f9b65c2023-06-05 14:26:58 +02001424 pubkey->type = pubkey_type;
romanc1d2b092023-02-02 08:58:27 +01001425 }
roman3f9b65c2023-06-05 14:26:58 +02001426 } else if (equal_parent_name(node, 5, "server-identity") && is_ssh(node) && is_listen(node)) {
romanf02273a2023-05-25 09:44:11 +02001427 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001428 ret = 1;
1429 goto cleanup;
1430 }
1431
romanf02273a2023-05-25 09:44:11 +02001432 if (nc_server_config_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01001433 ret = 1;
1434 goto cleanup;
1435 }
1436
1437 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman3f9b65c2023-06-05 14:26:58 +02001438 hostkey->key.pubkey_type = pubkey_type;
romanc1d2b092023-02-02 08:58:27 +01001439 }
roman2eab4742023-06-06 10:00:26 +02001440 } else if (equal_parent_name(node, 3, "server-identity") && is_tls(node) && is_listen(node)) {
roman3f9b65c2023-06-05 14:26:58 +02001441 /* TLS listen server-identity */
1442 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
1443 ret = 1;
1444 goto cleanup;
1445 }
1446
1447 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1448 endpt->opts.tls->pubkey_type = pubkey_type;
1449 }
1450 }
romanc1d2b092023-02-02 08:58:27 +01001451
1452cleanup:
1453 return ret;
1454}
1455
1456/* leaf */
romane028ef92023-02-24 16:33:08 +01001457static int
1458nc_server_config_private_key_format(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001459{
roman3f9b65c2023-06-05 14:26:58 +02001460 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01001461 const char *format;
1462 struct nc_endpt *endpt;
roman3f9b65c2023-06-05 14:26:58 +02001463 NC_PRIVKEY_FORMAT privkey_type;
romanc1d2b092023-02-02 08:58:27 +01001464 struct nc_hostkey *hostkey;
roman3f9b65c2023-06-05 14:26:58 +02001465
1466 (void) op;
romanc1d2b092023-02-02 08:58:27 +01001467
1468 assert(!strcmp(LYD_NAME(node), "private-key-format"));
1469
romanf02273a2023-05-25 09:44:11 +02001470 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001471 ret = 1;
1472 goto cleanup;
1473 }
1474
roman3f9b65c2023-06-05 14:26:58 +02001475 format = ((struct lyd_node_term *)node)->value.ident->name;
1476 if (!format) {
romanc1d2b092023-02-02 08:58:27 +01001477 ret = 1;
1478 goto cleanup;
1479 }
1480
roman3f9b65c2023-06-05 14:26:58 +02001481 privkey_type = nc_server_config_get_private_key_type(format);
1482 if (privkey_type == NC_PRIVKEY_FORMAT_UNKNOWN) {
1483 ret = 1;
1484 goto cleanup;
romanc1d2b092023-02-02 08:58:27 +01001485 }
1486
roman3f9b65c2023-06-05 14:26:58 +02001487 if ((is_ssh(node)) && (is_listen(node))) {
1488 /* listen ssh */
1489 if (nc_server_config_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
1490 ret = 1;
1491 goto cleanup;
1492 }
1493
1494 hostkey->key.privkey_type = privkey_type;
roman2eab4742023-06-06 10:00:26 +02001495 } else if ((is_tls(node)) && (is_listen(node))) {
roman3f9b65c2023-06-05 14:26:58 +02001496 /* listen tls */
roman3f9b65c2023-06-05 14:26:58 +02001497 endpt->opts.tls->privkey_type = privkey_type;
1498 }
roman3f9b65c2023-06-05 14:26:58 +02001499
romanc1d2b092023-02-02 08:58:27 +01001500cleanup:
1501 return ret;
1502}
1503
1504static int
roman874fed12023-05-25 10:20:01 +02001505nc_server_config_replace_cleartext_private_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +01001506{
roman874fed12023-05-25 10:20:01 +02001507 nc_server_config_del_private_key(hostkey);
roman3f9b65c2023-06-05 14:26:58 +02001508 hostkey->key.privkey_data = strdup(lyd_get_value(node));
1509 if (!hostkey->key.privkey_data) {
romanc1d2b092023-02-02 08:58:27 +01001510 ERRMEM;
1511 return 1;
1512 }
1513
1514 return 0;
1515}
1516
roman3f9b65c2023-06-05 14:26:58 +02001517static int
1518nc_server_config_tls_replace_cleartext_private_key(const struct lyd_node *node, struct nc_server_tls_opts *opts)
1519{
1520 nc_server_config_tls_del_cleartext_private_key(opts);
1521 opts->privkey_data = strdup(lyd_get_value(node));
1522 if (!opts->privkey_data) {
1523 ERRMEM;
1524 return 1;
1525 }
1526
1527 return 0;
1528}
1529
romanc1d2b092023-02-02 08:58:27 +01001530static int
romane028ef92023-02-24 16:33:08 +01001531nc_server_config_cleartext_private_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001532{
romanc1d2b092023-02-02 08:58:27 +01001533 int ret = 0;
roman3f9b65c2023-06-05 14:26:58 +02001534 struct nc_endpt *endpt;
roman3f9b65c2023-06-05 14:26:58 +02001535 struct nc_hostkey *hostkey;
romanc1d2b092023-02-02 08:58:27 +01001536
1537 assert(!strcmp(LYD_NAME(node), "cleartext-private-key"));
1538
roman3f9b65c2023-06-05 14:26:58 +02001539 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
1540 ret = 1;
1541 goto cleanup;
1542 }
1543
roman3f9b65c2023-06-05 14:26:58 +02001544 if ((is_ssh(node)) && (is_listen(node))) {
romanf02273a2023-05-25 09:44:11 +02001545 if (nc_server_config_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01001546 ret = 1;
1547 goto cleanup;
1548 }
1549
1550 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman874fed12023-05-25 10:20:01 +02001551 ret = nc_server_config_replace_cleartext_private_key(node, hostkey);
romanc1d2b092023-02-02 08:58:27 +01001552 if (ret) {
1553 goto cleanup;
1554 }
1555 } else {
roman874fed12023-05-25 10:20:01 +02001556 nc_server_config_del_private_key(hostkey);
romanc1d2b092023-02-02 08:58:27 +01001557 }
roman2eab4742023-06-06 10:00:26 +02001558 } else if ((is_tls(node)) && (is_listen(node))) {
roman3f9b65c2023-06-05 14:26:58 +02001559 /* listen tls */
roman3f9b65c2023-06-05 14:26:58 +02001560 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1561 ret = nc_server_config_tls_replace_cleartext_private_key(node, endpt->opts.tls);
1562 if (ret) {
1563 goto cleanup;
1564 }
1565 } else {
1566 nc_server_config_tls_del_cleartext_private_key(endpt->opts.tls);
1567 }
1568 }
romanc1d2b092023-02-02 08:58:27 +01001569
1570cleanup:
1571 return ret;
1572}
1573
1574static int
roman874fed12023-05-25 10:20:01 +02001575nc_server_config_create_keystore_reference(const struct lyd_node *node, struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +01001576{
1577 uint16_t i;
roman45cec4e2023-02-17 10:21:39 +01001578 struct nc_keystore *ks = &server_opts.keystore;
romanc1d2b092023-02-02 08:58:27 +01001579
1580 /* lookup name */
roman45cec4e2023-02-17 10:21:39 +01001581 for (i = 0; i < ks->asym_key_count; i++) {
1582 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
romanc1d2b092023-02-02 08:58:27 +01001583 break;
1584 }
1585 }
1586
roman45cec4e2023-02-17 10:21:39 +01001587 if (i == ks->asym_key_count) {
1588 ERR(NULL, "Keystore \"%s\" not found.", lyd_get_value(node));
romanc1d2b092023-02-02 08:58:27 +01001589 return 1;
1590 }
1591
roman45cec4e2023-02-17 10:21:39 +01001592 hostkey->ks_ref = &ks->asym_keys[i];
romanc1d2b092023-02-02 08:58:27 +01001593
1594 return 0;
1595}
1596
1597/* leaf */
1598static int
romane028ef92023-02-24 16:33:08 +01001599nc_server_config_keystore_reference(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001600{
1601 struct nc_endpt *endpt;
1602 struct nc_hostkey *hostkey;
1603 int ret = 0;
1604
1605 assert(!strcmp(LYD_NAME(node), "keystore-reference"));
1606
roman3f9b65c2023-06-05 14:26:58 +02001607 if ((equal_parent_name(node, 3, "server-identity")) && (is_ssh(node)) && (is_listen(node))) {
romanf02273a2023-05-25 09:44:11 +02001608 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001609 ret = 1;
1610 goto cleanup;
1611 }
romanf02273a2023-05-25 09:44:11 +02001612 if (nc_server_config_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01001613 ret = 1;
1614 goto cleanup;
1615 }
1616
1617 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanf02273a2023-05-25 09:44:11 +02001618 /* set to keystore */
roman874fed12023-05-25 10:20:01 +02001619 hostkey->store = NC_STORE_KEYSTORE;
romanf02273a2023-05-25 09:44:11 +02001620
roman874fed12023-05-25 10:20:01 +02001621 ret = nc_server_config_create_keystore_reference(node, hostkey);
romanc1d2b092023-02-02 08:58:27 +01001622 if (ret) {
1623 goto cleanup;
1624 }
1625 } else {
roman45cec4e2023-02-17 10:21:39 +01001626 hostkey->ks_ref = NULL;
romanc1d2b092023-02-02 08:58:27 +01001627 }
1628 }
1629
1630cleanup:
1631 return ret;
1632}
1633
1634static int
roman874fed12023-05-25 10:20:01 +02001635nc_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 +01001636{
romanc1d2b092023-02-02 08:58:27 +01001637 assert(!strcmp(LYD_NAME(node), "public-key"));
1638
romanc1d2b092023-02-02 08:58:27 +01001639 node = lyd_child(node);
1640 assert(!strcmp(LYD_NAME(node), "name"));
1641
romanf02273a2023-05-25 09:44:11 +02001642 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 +01001643}
1644
1645static int
roman874fed12023-05-25 10:20:01 +02001646nc_server_config_replace_auth_key_public_key_leaf(const struct lyd_node *node, struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +01001647{
roman874fed12023-05-25 10:20:01 +02001648 nc_server_config_del_auth_client_pubkey_pub_base64(pubkey);
romanc1d2b092023-02-02 08:58:27 +01001649
roman3f9b65c2023-06-05 14:26:58 +02001650 pubkey->data = strdup(lyd_get_value(node));
1651 if (!pubkey->data) {
romanc1d2b092023-02-02 08:58:27 +01001652 ERRMEM;
1653 return 1;
1654 }
1655
1656 return 0;
1657}
1658
1659static int
roman874fed12023-05-25 10:20:01 +02001660nc_server_config_replace_host_key_public_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +01001661{
roman874fed12023-05-25 10:20:01 +02001662 nc_server_config_del_public_key(hostkey);
romanc1d2b092023-02-02 08:58:27 +01001663
roman3f9b65c2023-06-05 14:26:58 +02001664 hostkey->key.pubkey_data = strdup(lyd_get_value(node));
1665 if (!hostkey->key.pubkey_data) {
romanc1d2b092023-02-02 08:58:27 +01001666 ERRMEM;
1667 return 1;
1668 }
1669
1670 return 0;
1671}
1672
roman3f9b65c2023-06-05 14:26:58 +02001673static int
1674nc_server_config_tls_replace_server_public_key(const struct lyd_node *node, struct nc_server_tls_opts *opts)
1675{
1676 nc_server_config_tls_del_public_key(opts);
1677
1678 opts->pubkey_data = strdup(lyd_get_value(node));
1679 if (!opts->pubkey_data) {
1680 ERRMEM;
1681 return 1;
1682 }
1683
1684 return 0;
1685}
1686
romanc1d2b092023-02-02 08:58:27 +01001687static int
romane028ef92023-02-24 16:33:08 +01001688nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001689{
roman3f9b65c2023-06-05 14:26:58 +02001690 int ret = 0;
romanc1d2b092023-02-02 08:58:27 +01001691 struct nc_endpt *endpt;
1692 struct nc_hostkey *hostkey;
1693 struct nc_client_auth *auth_client;
roman8edee342023-03-31 13:25:48 +02001694 struct nc_public_key *pubkey;
romanc1d2b092023-02-02 08:58:27 +01001695
1696 assert(!strcmp(LYD_NAME(node), "public-key"));
1697
roman3f9b65c2023-06-05 14:26:58 +02001698 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
1699 ret = 1;
1700 goto cleanup;
1701 }
romanc1d2b092023-02-02 08:58:27 +01001702
roman3f9b65c2023-06-05 14:26:58 +02001703 if ((equal_parent_name(node, 3, "host-key")) && (is_ssh(node)) && (is_listen(node))) {
1704 /* server's public-key, mandatory leaf */
romanf02273a2023-05-25 09:44:11 +02001705 if (nc_server_config_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01001706 ret = 1;
1707 goto cleanup;
1708 }
1709
1710 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanf02273a2023-05-25 09:44:11 +02001711 /* set to local */
roman874fed12023-05-25 10:20:01 +02001712 hostkey->store = NC_STORE_LOCAL;
romanf02273a2023-05-25 09:44:11 +02001713
roman874fed12023-05-25 10:20:01 +02001714 ret = nc_server_config_replace_host_key_public_key(node, hostkey);
romanc1d2b092023-02-02 08:58:27 +01001715 if (ret) {
1716 goto cleanup;
1717 }
1718 }
roman3f9b65c2023-06-05 14:26:58 +02001719 } else if ((equal_parent_name(node, 5, "client-authentication")) && (is_ssh(node)) && (is_listen(node))) {
romanc1d2b092023-02-02 08:58:27 +01001720 /* client auth pubkeys, list */
romanf02273a2023-05-25 09:44:11 +02001721 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01001722 ret = 1;
1723 goto cleanup;
1724 }
1725
1726 if (op == NC_OP_CREATE) {
romanf02273a2023-05-25 09:44:11 +02001727 /* set to local */
roman874fed12023-05-25 10:20:01 +02001728 auth_client->store = NC_STORE_LOCAL;
romanf02273a2023-05-25 09:44:11 +02001729
roman874fed12023-05-25 10:20:01 +02001730 ret = nc_server_config_create_auth_key_public_key_list(node, auth_client);
romanc1d2b092023-02-02 08:58:27 +01001731 if (ret) {
1732 goto cleanup;
1733 }
1734 } else if (op == NC_OP_DELETE) {
romanf02273a2023-05-25 09:44:11 +02001735 if (nc_server_config_get_pubkey(node, auth_client, &pubkey)) {
romanc1d2b092023-02-02 08:58:27 +01001736 ret = 1;
1737 goto cleanup;
1738 }
1739
roman874fed12023-05-25 10:20:01 +02001740 nc_server_config_del_auth_client_pubkey(auth_client, pubkey);
romanc1d2b092023-02-02 08:58:27 +01001741 }
roman3f9b65c2023-06-05 14:26:58 +02001742 } else if ((equal_parent_name(node, 6, "client-authentication")) && (is_ssh(node)) && (is_listen(node))) {
romanc1d2b092023-02-02 08:58:27 +01001743 /* client auth pubkey, leaf */
romanf02273a2023-05-25 09:44:11 +02001744 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01001745 ret = 1;
1746 goto cleanup;
1747 }
1748
romanf02273a2023-05-25 09:44:11 +02001749 if (nc_server_config_get_pubkey(node, auth_client, &pubkey)) {
romanc1d2b092023-02-02 08:58:27 +01001750 ret = 1;
1751 goto cleanup;
1752 }
1753
1754 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman874fed12023-05-25 10:20:01 +02001755 ret = nc_server_config_replace_auth_key_public_key_leaf(node, pubkey);
romanc1d2b092023-02-02 08:58:27 +01001756 if (ret) {
1757 goto cleanup;
1758 }
1759 } else {
roman874fed12023-05-25 10:20:01 +02001760 nc_server_config_del_auth_client_pubkey_pub_base64(pubkey);
romanc1d2b092023-02-02 08:58:27 +01001761 }
roman2eab4742023-06-06 10:00:26 +02001762 } else if ((equal_parent_name(node, 3, "server-identity")) && (is_tls(node)) && (is_listen(node))) {
roman3f9b65c2023-06-05 14:26:58 +02001763 /* tls listen server-identity */
roman3f9b65c2023-06-05 14:26:58 +02001764 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1765 /* set to local */
1766 endpt->opts.tls->store = NC_STORE_LOCAL;
1767
1768 ret = nc_server_config_tls_replace_server_public_key(node, endpt->opts.tls);
1769 if (ret) {
1770 goto cleanup;
1771 }
1772 }
1773 }
romanc1d2b092023-02-02 08:58:27 +01001774
1775cleanup:
1776 return ret;
1777}
1778
1779static int
roman874fed12023-05-25 10:20:01 +02001780nc_server_config_create_user(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +01001781{
romanf02273a2023-05-25 09:44:11 +02001782 node = lyd_child(node);
1783 assert(!strcmp(LYD_NAME(node), "name"));
romanc1d2b092023-02-02 08:58:27 +01001784
romanf02273a2023-05-25 09:44:11 +02001785 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 +01001786}
1787
1788/* list */
1789static int
romane028ef92023-02-24 16:33:08 +01001790nc_server_config_user(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001791{
1792 struct nc_endpt *endpt;
1793 struct nc_client_auth *auth_client;
1794 int ret = 0;
1795
1796 assert(!strcmp(LYD_NAME(node), "user"));
1797
1798 if (equal_parent_name(node, 6, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001799 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001800 ret = 1;
1801 goto cleanup;
1802 }
1803
1804 if (op == NC_OP_CREATE) {
roman874fed12023-05-25 10:20:01 +02001805 ret = nc_server_config_create_user(node, endpt->opts.ssh);
romanc1d2b092023-02-02 08:58:27 +01001806 if (ret) {
1807 goto cleanup;
1808 }
1809 } else if (op == NC_OP_DELETE) {
romanf02273a2023-05-25 09:44:11 +02001810 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01001811 ret = 1;
1812 goto cleanup;
1813 }
1814
roman874fed12023-05-25 10:20:01 +02001815 nc_server_config_del_auth_client(endpt->opts.ssh, auth_client);
romanc1d2b092023-02-02 08:58:27 +01001816 }
1817 }
1818
1819cleanup:
1820 return ret;
1821}
1822
1823static int
romane028ef92023-02-24 16:33:08 +01001824nc_server_config_auth_attempts(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001825{
1826 struct nc_endpt *endpt;
1827 int ret = 0;
1828
1829 assert(!strcmp(LYD_NAME(node), "auth-attempts"));
1830
1831 if (equal_parent_name(node, 5, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001832 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001833 ret = 1;
1834 goto cleanup;
1835 }
1836
1837 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1838 endpt->opts.ssh->auth_attempts = strtoul(lyd_get_value(node), NULL, 10);
1839 }
1840 }
1841
1842cleanup:
1843 return ret;
1844}
1845
1846static int
romane028ef92023-02-24 16:33:08 +01001847nc_server_config_auth_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001848{
1849 struct nc_endpt *endpt;
1850 int ret = 0;
1851
1852 assert(!strcmp(LYD_NAME(node), "auth-timeout"));
1853
1854 if (equal_parent_name(node, 5, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001855 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001856 ret = 1;
1857 goto cleanup;
1858 }
1859
1860 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1861 endpt->opts.ssh->auth_timeout = strtoul(lyd_get_value(node), NULL, 10);
1862 }
1863 }
1864
1865cleanup:
1866 return ret;
1867}
1868
1869static int
roman874fed12023-05-25 10:20:01 +02001870nc_server_config_replace_truststore_reference(const struct lyd_node *node, struct nc_client_auth *client_auth)
romanc1d2b092023-02-02 08:58:27 +01001871{
romand57b3722023-04-05 11:26:25 +02001872 uint16_t i;
1873 struct nc_truststore *ts = &server_opts.truststore;
romanc1d2b092023-02-02 08:58:27 +01001874
romand57b3722023-04-05 11:26:25 +02001875 /* lookup name */
1876 for (i = 0; i < ts->pub_bag_count; i++) {
1877 if (!strcmp(lyd_get_value(node), ts->pub_bags[i].name)) {
1878 break;
1879 }
1880 }
1881
1882 if (i == ts->pub_bag_count) {
roman3f9b65c2023-06-05 14:26:58 +02001883 ERR(NULL, "Public-key bag \"%s\" not found in truststore.", lyd_get_value(node));
romanc1d2b092023-02-02 08:58:27 +01001884 return 1;
1885 }
1886
romand57b3722023-04-05 11:26:25 +02001887 client_auth->ts_ref = &ts->pub_bags[i];
1888
romanc1d2b092023-02-02 08:58:27 +01001889 return 0;
1890}
1891
roman3f9b65c2023-06-05 14:26:58 +02001892static int
1893nc_server_config_tls_replace_truststore_reference(const struct lyd_node *node, struct nc_cert_grouping *auth_client)
1894{
1895 uint16_t i;
1896 struct nc_truststore *ts = &server_opts.truststore;
1897
1898 /* lookup name */
1899 for (i = 0; i < ts->cert_bag_count; i++) {
1900 if (!strcmp(lyd_get_value(node), ts->cert_bags[i].name)) {
1901 break;
1902 }
1903 }
1904
1905 if (i == ts->cert_bag_count) {
1906 ERR(NULL, "Certificate bag \"%s\" not found in truststore.", lyd_get_value(node));
1907 return 1;
1908 }
1909
1910 auth_client->ts_ref = &ts->cert_bags[i];
1911
1912 return 0;
1913}
1914
romanc1d2b092023-02-02 08:58:27 +01001915/* leaf */
1916static int
romane028ef92023-02-24 16:33:08 +01001917nc_server_config_truststore_reference(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001918{
romanc1d2b092023-02-02 08:58:27 +01001919 int ret = 0;
roman3f9b65c2023-06-05 14:26:58 +02001920 struct nc_endpt *endpt;
roman3f9b65c2023-06-05 14:26:58 +02001921 struct nc_client_auth *auth_client;
romanc1d2b092023-02-02 08:58:27 +01001922
1923 assert(!strcmp(LYD_NAME(node), "truststore-reference"));
1924
roman3f9b65c2023-06-05 14:26:58 +02001925 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
1926 ret = 1;
1927 goto cleanup;
1928 }
romanc1d2b092023-02-02 08:58:27 +01001929
roman3f9b65c2023-06-05 14:26:58 +02001930 if ((equal_parent_name(node, 1, "public-keys")) && (is_ssh(node)) && (is_listen(node))) {
romanf02273a2023-05-25 09:44:11 +02001931 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01001932 ret = 1;
1933 goto cleanup;
1934 }
1935
1936 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanf02273a2023-05-25 09:44:11 +02001937 /* set to truststore */
roman874fed12023-05-25 10:20:01 +02001938 auth_client->store = NC_STORE_TRUSTSTORE;
romanf02273a2023-05-25 09:44:11 +02001939
roman874fed12023-05-25 10:20:01 +02001940 ret = nc_server_config_replace_truststore_reference(node, auth_client);
romanc1d2b092023-02-02 08:58:27 +01001941 if (ret) {
1942 goto cleanup;
1943 }
1944 } else {
romand57b3722023-04-05 11:26:25 +02001945 auth_client->ts_ref = NULL;
romanc1d2b092023-02-02 08:58:27 +01001946 }
roman2eab4742023-06-06 10:00:26 +02001947 } else if ((equal_parent_name(node, 1, "ca-certs")) && (is_listen(node))) {
roman3f9b65c2023-06-05 14:26:58 +02001948 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1949 /* set to truststore */
1950 endpt->opts.tls->ca_certs.store = NC_STORE_TRUSTSTORE;
1951
1952 ret = nc_server_config_tls_replace_truststore_reference(node, &endpt->opts.tls->ca_certs);
1953 if (ret) {
1954 goto cleanup;
1955 }
1956 } else {
1957 endpt->opts.tls->ca_certs.ts_ref = NULL;
1958 }
1959 } else if ((equal_parent_name(node, 1, "ee-certs")) && (is_listen(node))) {
1960 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1961 /* set to truststore */
1962 endpt->opts.tls->ee_certs.store = NC_STORE_TRUSTSTORE;
1963
1964 ret = nc_server_config_tls_replace_truststore_reference(node, &endpt->opts.tls->ee_certs);
1965 if (ret) {
1966 goto cleanup;
1967 }
1968 } else {
1969 endpt->opts.tls->ee_certs.ts_ref = NULL;
1970 }
1971 }
romanc1d2b092023-02-02 08:58:27 +01001972
1973cleanup:
1974 return ret;
1975}
1976
1977static int
roman874fed12023-05-25 10:20:01 +02001978nc_server_config_replace_password(const struct lyd_node *node, struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +01001979{
roman874fed12023-05-25 10:20:01 +02001980 nc_server_config_del_auth_client_password(auth_client);
romanc1d2b092023-02-02 08:58:27 +01001981
1982 auth_client->password = strdup(lyd_get_value(node));
1983 if (!auth_client->password) {
1984 ERRMEM;
1985 return 1;
1986 }
1987
1988 return 0;
1989}
1990
1991/* leaf */
1992static int
romane028ef92023-02-24 16:33:08 +01001993nc_server_config_password(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001994{
1995 struct nc_endpt *endpt;
1996 struct nc_client_auth *auth_client;
1997 int ret = 0;
1998
1999 assert(!strcmp(LYD_NAME(node), "password"));
2000
2001 if (equal_parent_name(node, 7, "listen")) {
romanf02273a2023-05-25 09:44:11 +02002002 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01002003 ret = 1;
2004 goto cleanup;
2005 }
2006
romanf02273a2023-05-25 09:44:11 +02002007 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01002008 ret = 1;
2009 goto cleanup;
2010 }
2011
2012 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman874fed12023-05-25 10:20:01 +02002013 ret = nc_server_config_replace_password(node, auth_client);
romanc1d2b092023-02-02 08:58:27 +01002014 if (ret) {
2015 goto cleanup;
2016 }
2017 } else {
roman874fed12023-05-25 10:20:01 +02002018 nc_server_config_del_auth_client_password(auth_client);
romanc1d2b092023-02-02 08:58:27 +01002019 }
2020 }
2021
2022cleanup:
2023 return ret;
2024}
2025
2026static int
romane028ef92023-02-24 16:33:08 +01002027nc_server_config_pam_name(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002028{
2029 struct nc_endpt *endpt;
2030 struct nc_client_auth *auth_client;
2031 int ret = 0;
2032
2033 assert(!strcmp(LYD_NAME(node), "pam-config-file-name"));
2034
2035 if (equal_parent_name(node, 8, "listen")) {
romanf02273a2023-05-25 09:44:11 +02002036 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01002037 ret = 1;
2038 goto cleanup;
2039 }
2040
romanf02273a2023-05-25 09:44:11 +02002041 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01002042 ret = 1;
2043 goto cleanup;
2044 }
2045
2046 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman874fed12023-05-25 10:20:01 +02002047 nc_server_config_del_auth_client_pam_name(auth_client);
romanc1d2b092023-02-02 08:58:27 +01002048
2049 auth_client->pam_config_name = strdup(lyd_get_value(node));
2050 if (!auth_client->pam_config_name) {
2051 ERRMEM;
2052 ret = 1;
2053 goto cleanup;
2054 }
2055 }
2056 }
2057
2058cleanup:
2059 return ret;
2060}
2061
2062static int
romane028ef92023-02-24 16:33:08 +01002063nc_server_config_pam_dir(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002064{
2065 struct nc_endpt *endpt;
2066 struct nc_client_auth *auth_client;
2067 int ret = 0;
2068
2069 assert(!strcmp(LYD_NAME(node), "pam-config-file-dir"));
2070
2071 if (equal_parent_name(node, 8, "listen")) {
romanf02273a2023-05-25 09:44:11 +02002072 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01002073 ret = 1;
2074 goto cleanup;
2075 }
2076
romanf02273a2023-05-25 09:44:11 +02002077 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01002078 ret = 1;
2079 goto cleanup;
2080 }
2081
2082 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman874fed12023-05-25 10:20:01 +02002083 nc_server_config_del_auth_client_pam_dir(auth_client);
romanc1d2b092023-02-02 08:58:27 +01002084 auth_client->pam_config_dir = strdup(lyd_get_value(node));
2085 if (!auth_client->pam_config_dir) {
2086 ERRMEM;
2087 ret = 1;
2088 goto cleanup;
2089 }
2090 }
2091 }
2092
2093cleanup:
2094 return ret;
2095}
2096
2097/* leaf */
2098static int
romane028ef92023-02-24 16:33:08 +01002099nc_server_config_none(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002100{
2101 struct nc_endpt *endpt;
2102 struct nc_client_auth *auth_client;
2103 int ret = 0;
2104
2105 assert(!strcmp(LYD_NAME(node), "none"));
2106
2107 if (equal_parent_name(node, 7, "listen")) {
romanf02273a2023-05-25 09:44:11 +02002108 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01002109 ret = 1;
2110 goto cleanup;
2111 }
2112
romanf02273a2023-05-25 09:44:11 +02002113 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01002114 ret = 1;
2115 goto cleanup;
2116 }
2117
2118 if (op == NC_OP_CREATE) {
2119 auth_client->supports_none = 1;
2120 } else {
2121 auth_client->supports_none = 0;
2122 }
2123 }
2124
2125cleanup:
2126 return ret;
2127}
2128
2129static int
romana6bf6ab2023-05-26 13:26:02 +02002130nc_server_config_transport_params(const char *algorithm, char **alg_store, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002131{
2132 int ret = 0, alg_found = 0;
romana6bf6ab2023-05-26 13:26:02 +02002133 char *substr, *haystack, *alg = NULL;
2134 size_t alg_len;
2135
2136 if (!strncmp(algorithm, "openssh-", 8)) {
2137 /* if the name starts with openssh, convert it to it's original libssh accepted form */
2138 asprintf(&alg, "%s@openssh.com", algorithm + 8);
2139 if (!alg) {
2140 ERRMEM;
2141 ret = 1;
2142 goto cleanup;
2143 }
2144 } else if (!strncmp(algorithm, "libssh-", 7)) {
2145 /* if the name starts with libssh, convert it to it's original libssh accepted form */
2146 asprintf(&alg, "%s@libssh.org", algorithm + 7);
2147 if (!alg) {
2148 ERRMEM;
2149 ret = 1;
2150 goto cleanup;
2151 }
2152 } else {
2153 alg = strdup(algorithm);
2154 if (!alg) {
2155 ERRMEM;
2156 ret = 1;
2157 goto cleanup;
2158 }
2159 }
2160
2161 alg_len = strlen(alg);
romanc1d2b092023-02-02 08:58:27 +01002162
2163 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2164 if (!*alg_store) {
2165 /* first call */
2166 *alg_store = strdup(alg);
2167 if (!*alg_store) {
2168 ERRMEM;
2169 ret = 1;
2170 goto cleanup;
2171 }
2172 } else {
2173 /* +1 because of ',' between algorithms */
2174 *alg_store = nc_realloc(*alg_store, strlen(*alg_store) + alg_len + 1 + 1);
2175 if (!*alg_store) {
2176 ERRMEM;
2177 ret = 1;
2178 goto cleanup;
2179 }
roman08f67f42023-06-08 13:51:54 +02002180 strcat(*alg_store, ",");
2181 strcat(*alg_store, alg);
romanc1d2b092023-02-02 08:58:27 +01002182 }
2183 } else {
2184 /* delete */
2185 haystack = *alg_store;
2186 while ((substr = strstr(haystack, alg))) {
2187 /* iterate over all the substrings */
2188 if (((substr == haystack) && (*(substr + alg_len) == ',')) ||
2189 ((substr != haystack) && (*(substr - 1) == ',') && (*(substr + alg_len) == ','))) {
2190 /* either the first element of the string or somewhere in the middle */
2191 memmove(substr, substr + alg_len + 1, strlen(substr + alg_len + 1));
2192 alg_found = 1;
2193 break;
2194 } else if ((*(substr - 1) == ',') && (*(substr + alg_len) == '\0')) {
2195 /* the last element of the string */
2196 *(substr - 1) = '\0';
2197 alg_found = 1;
2198 break;
2199 }
2200 haystack++;
2201 }
2202 if (!alg_found) {
2203 ERR(NULL, "Unable to delete an algorithm (%s), which was not previously added.", alg);
2204 ret = 1;
2205 }
2206 }
2207
2208cleanup:
romana6bf6ab2023-05-26 13:26:02 +02002209 free(alg);
romanc1d2b092023-02-02 08:58:27 +01002210 return ret;
2211}
2212
2213/* leaf-list */
2214static int
romane028ef92023-02-24 16:33:08 +01002215nc_server_config_host_key_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002216{
2217 struct nc_endpt *endpt;
2218 int ret = 0, listen = 0;
2219 const char *alg;
2220 uint8_t i;
2221
2222 /* get the algorithm name and compare it with algs supported by libssh */
2223 alg = ((struct lyd_node_term *)node)->value.ident->name;
2224
2225 if (equal_parent_name(node, 6, "listen")) {
2226 listen = 1;
romanf02273a2023-05-25 09:44:11 +02002227 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01002228 ret = 1;
2229 goto cleanup;
2230 }
2231 }
2232
2233 i = 0;
2234 while (supported_hostkey_algs[i]) {
2235 if (!strcmp(supported_hostkey_algs[i], alg)) {
2236 if (listen) {
romane028ef92023-02-24 16:33:08 +01002237 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->hostkey_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01002238 ret = 1;
2239 goto cleanup;
2240 }
2241 }
2242 break;
2243 }
2244 i++;
2245 }
2246 if (!supported_hostkey_algs[i]) {
2247 /* algorithm not supported */
2248 ERR(NULL, "Public key algorithm (%s) not supported by libssh.", alg);
2249 ret = 1;
2250 }
2251
2252cleanup:
2253 return ret;
2254}
2255
2256/* leaf-list */
2257static int
romane028ef92023-02-24 16:33:08 +01002258nc_server_config_kex_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002259{
2260 struct nc_endpt *endpt;
2261 int ret = 0, listen = 0;
2262 const char *alg;
2263 uint8_t i;
2264
2265 /* get the algorithm name and compare it with algs supported by libssh */
2266 alg = ((struct lyd_node_term *)node)->value.ident->name;
2267
2268 if (equal_parent_name(node, 6, "listen")) {
2269 listen = 1;
romanf02273a2023-05-25 09:44:11 +02002270 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01002271 ret = 1;
2272 goto cleanup;
2273 }
2274 }
2275
2276 i = 0;
2277 while (supported_kex_algs[i]) {
2278 if (!strcmp(supported_kex_algs[i], alg)) {
2279 if (listen) {
romane028ef92023-02-24 16:33:08 +01002280 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->kex_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01002281 ret = 1;
2282 goto cleanup;
2283 }
2284 }
2285 break;
2286 }
2287 i++;
2288 }
2289 if (!supported_kex_algs[i]) {
2290 /* algorithm not supported */
2291 ERR(NULL, "Key exchange algorithm (%s) not supported by libssh.", alg);
2292 ret = 1;
2293 }
2294
2295cleanup:
2296 return ret;
2297}
2298
2299/* leaf-list */
2300static int
romane028ef92023-02-24 16:33:08 +01002301nc_server_config_encryption_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002302{
2303 struct nc_endpt *endpt;
2304 int ret = 0, listen = 0;
2305 const char *alg;
2306 uint8_t i;
2307
2308 /* get the algorithm name and compare it with algs supported by libssh */
2309 alg = ((struct lyd_node_term *)node)->value.ident->name;
2310
2311 if (equal_parent_name(node, 6, "listen")) {
2312 listen = 1;
romanf02273a2023-05-25 09:44:11 +02002313 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01002314 ret = 1;
2315 goto cleanup;
2316 }
2317 }
2318
2319 i = 0;
2320 while (supported_encryption_algs[i]) {
2321 if (!strcmp(supported_encryption_algs[i], alg)) {
2322 if (listen) {
romane028ef92023-02-24 16:33:08 +01002323 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->encryption_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01002324 ret = 1;
2325 goto cleanup;
2326 }
2327 }
2328 break;
2329 }
2330 i++;
2331 }
2332 if (!supported_encryption_algs[i]) {
2333 /* algorithm not supported */
2334 ERR(NULL, "Encryption algorithm (%s) not supported by libssh.", alg);
2335 ret = 1;
2336 }
2337
2338cleanup:
2339 return ret;
2340}
2341
2342/* leaf-list */
2343static int
romane028ef92023-02-24 16:33:08 +01002344nc_server_config_mac_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002345{
2346 struct nc_endpt *endpt;
2347 int ret = 0, listen = 0;
2348 const char *alg;
2349 uint8_t i;
2350
2351 /* get the algorithm name and compare it with algs supported by libssh */
2352 alg = ((struct lyd_node_term *)node)->value.ident->name;
2353
2354 if (equal_parent_name(node, 6, "listen")) {
2355 listen = 1;
romanf02273a2023-05-25 09:44:11 +02002356 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01002357 ret = 1;
2358 goto cleanup;
2359 }
2360 }
2361
2362 i = 0;
2363 while (supported_mac_algs[i]) {
2364 if (!strcmp(supported_mac_algs[i], alg)) {
2365 if (listen) {
romane028ef92023-02-24 16:33:08 +01002366 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->mac_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01002367 ret = 1;
2368 goto cleanup;
2369 }
2370 }
2371 break;
2372 }
2373 i++;
2374 }
2375 if (!supported_mac_algs[i]) {
2376 /* algorithm not supported */
2377 ERR(NULL, "MAC algorithm (%s) not supported by libssh.", alg);
2378 ret = 1;
2379 }
2380
2381cleanup:
2382 return ret;
2383}
2384
roman2eab4742023-06-06 10:00:26 +02002385#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +02002386
romanc1d2b092023-02-02 08:58:27 +01002387static int
roman874fed12023-05-25 10:20:01 +02002388nc_server_config_create_unix_socket(struct nc_endpt *endpt)
roman83683fb2023-02-24 09:15:23 +01002389{
2390 endpt->ti = NC_TI_UNIX;
2391 endpt->opts.unixsock = calloc(1, sizeof *endpt->opts.unixsock);
2392 if (!endpt->opts.unixsock) {
2393 ERRMEM;
2394 return 1;
2395 }
2396
2397 /* set default values */
2398 endpt->opts.unixsock->mode = -1;
2399 endpt->opts.unixsock->uid = -1;
2400 endpt->opts.unixsock->gid = -1;
2401
2402 return 0;
2403}
2404
2405static int
romane028ef92023-02-24 16:33:08 +01002406nc_server_config_unix_socket(const struct lyd_node *node, NC_OPERATION op)
roman83683fb2023-02-24 09:15:23 +01002407{
2408 int ret = 0;
2409 uint32_t prev_lo;
2410 struct nc_endpt *endpt;
2411 struct nc_bind *bind;
2412 struct nc_server_unix_opts *opts;
2413 struct lyd_node *data = NULL;
2414
2415 assert(!strcmp(LYD_NAME(node), "unix-socket"));
2416
romanf02273a2023-05-25 09:44:11 +02002417 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
roman83683fb2023-02-24 09:15:23 +01002418 ret = 1;
2419 goto cleanup;
2420 }
2421
2422 if (op == NC_OP_CREATE) {
roman874fed12023-05-25 10:20:01 +02002423 if (nc_server_config_create_unix_socket(endpt)) {
roman83683fb2023-02-24 09:15:23 +01002424 ret = 1;
2425 goto cleanup;
2426 }
2427
2428 opts = endpt->opts.unixsock;
2429
2430 lyd_find_path(node, "path", 0, &data);
2431 assert(data);
2432
2433 opts->address = strdup(lyd_get_value(data));
2434 bind->address = strdup(lyd_get_value(data));
2435 if (!opts->address || !bind->address) {
2436 ERRMEM;
2437 ret = 1;
2438 goto cleanup;
2439 }
2440
2441 /* silently search for non-mandatory parameters */
2442 prev_lo = ly_log_options(0);
2443 ret = lyd_find_path(node, "mode", 0, &data);
2444 if (!ret) {
2445 opts->mode = strtol(lyd_get_value(data), NULL, 8);
2446 }
2447
2448 ret = lyd_find_path(node, "uid", 0, &data);
2449 if (!ret) {
2450 opts->uid = strtol(lyd_get_value(data), NULL, 10);
2451 }
2452
2453 ret = lyd_find_path(node, "gid", 0, &data);
2454 if (!ret) {
2455 opts->gid = strtol(lyd_get_value(data), NULL, 10);
2456 }
2457
2458 /* reset the logging options */
2459 ly_log_options(prev_lo);
2460
2461 ret = nc_server_config_set_address_port(endpt, bind, NULL, 0);
2462 if (ret) {
2463 goto cleanup;
2464 }
2465 } else if (op == NC_OP_DELETE) {
roman874fed12023-05-25 10:20:01 +02002466 nc_server_config_del_unix_socket(bind, endpt->opts.unixsock);
roman83683fb2023-02-24 09:15:23 +01002467 }
2468
2469cleanup:
2470 return ret;
2471}
2472
roman2eab4742023-06-06 10:00:26 +02002473#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +02002474
roman0bbc19c2023-05-26 09:59:09 +02002475/**
2476 * @brief Set all endpoint client auth references, which couldn't be set beforehand.
2477 *
2478 * The references that could not be set are those, which reference endpoints, which
2479 * lie below the given endpoint in the YANG data (because of DFS tree parsing).
2480 *
2481 * @return 0 on success, 1 on error.
2482 */
2483static int
2484nc_server_config_fill_endpt_client_auth(void)
2485{
2486 uint16_t i, j;
2487
2488 for (i = 0; i < server_opts.endpt_count; i++) {
2489 /* go through all the endpoint */
2490 if (server_opts.endpts[i].referenced_endpt_name) {
2491 /* endpt has a reference, that hasn't been set yet */
2492 for (j = i + 1; j < server_opts.endpt_count; j++) {
2493 /* go through all the remaining endpts */
2494 if (!strcmp(server_opts.endpts[i].referenced_endpt_name, server_opts.endpts[j].name)) {
2495 /* found the endpoint we were looking for */
2496 if (server_opts.endpts[i].ti == NC_TI_LIBSSH) {
2497 server_opts.endpts[i].opts.ssh->endpt_client_ref = &server_opts.endpts[j];
2498 break;
roman2e797ef2023-06-19 10:47:49 +02002499 } else if (server_opts.endpts[i].ti == NC_TI_OPENSSL) {
2500 server_opts.endpts[i].opts.tls->endpt_client_ref = &server_opts.endpts[j];
2501 break;
roman0bbc19c2023-05-26 09:59:09 +02002502 } else {
2503 ERRINT;
2504 return 1;
2505 }
2506 }
2507 }
2508
2509 /* didn't find the endpoint */
2510 if (j == server_opts.endpt_count) {
2511 ERR(NULL, "Endpoint \"%s\" referenced by \"%s\" not found.",
2512 server_opts.endpts[i].referenced_endpt_name, server_opts.endpts[i].name);
2513 return 1;
2514 }
2515 }
2516 }
2517
2518 return 0;
2519}
2520
2521static int
roman2e797ef2023-06-19 10:47:49 +02002522nc_server_config_endpoint_client_auth_has_cycle(struct nc_endpt *original, struct nc_endpt *next)
roman0bbc19c2023-05-26 09:59:09 +02002523{
roman2e797ef2023-06-19 10:47:49 +02002524 if (original->ti == NC_TI_LIBSSH) {
2525 if (!next->opts.ssh->endpt_client_ref) {
2526 /* no further reference -> no cycle */
roman0bbc19c2023-05-26 09:59:09 +02002527 return 0;
2528 }
roman2e797ef2023-06-19 10:47:49 +02002529
2530 if (next->opts.ssh->endpt_client_ref == original) {
2531 /* found cycle */
2532 return 1;
2533 } else {
2534 /* continue further */
2535 return nc_server_config_endpoint_client_auth_has_cycle(original, next->opts.ssh->endpt_client_ref);
2536 }
2537 } else if (original->ti == NC_TI_OPENSSL) {
2538 if (!next->opts.tls->endpt_client_ref) {
2539 /* no further reference -> no cycle */
2540 return 0;
2541 }
2542
2543 if (next->opts.tls->endpt_client_ref == original) {
2544 /* found cycle */
2545 return 1;
2546 } else {
2547 /* continue further */
2548 return nc_server_config_endpoint_client_auth_has_cycle(original, next->opts.tls->endpt_client_ref);
2549 }
roman0bbc19c2023-05-26 09:59:09 +02002550 } else {
2551 ERRINT;
2552 return 1;
2553 }
2554}
2555
2556static int
2557nc_server_config_endpoint_client_auth(const struct lyd_node *node, NC_OPERATION op)
2558{
2559 int ret = 0;
2560 uint16_t i;
2561 const char *endpt_name;
2562 struct nc_endpt *endpt;
2563
2564 assert(!strcmp(LYD_NAME(node), "endpoint-client-auth"));
2565
2566 /* get current endpoint */
2567 ret = nc_server_config_get_endpt(node, &endpt, NULL);
2568 if (ret) {
2569 goto cleanup;
2570 }
2571
2572 if (op == NC_OP_DELETE) {
roman2e797ef2023-06-19 10:47:49 +02002573 if (is_ssh(node)) {
2574 endpt->opts.ssh->endpt_client_ref = NULL;
2575 } else {
2576 endpt->opts.tls->endpt_client_ref = NULL;
2577 }
roman0bbc19c2023-05-26 09:59:09 +02002578 goto cleanup;
2579 }
2580
2581 /* find the endpoint leafref is referring to */
2582 endpt_name = lyd_get_value(node);
2583 for (i = 0; i < server_opts.endpt_count; i++) {
2584 if (!strcmp(endpt_name, server_opts.endpts[i].name)) {
2585 break;
2586 }
2587 }
2588
2589 if (i == server_opts.endpt_count) {
2590 /* endpt not found, save the name and try to look it up later */
roman2e797ef2023-06-19 10:47:49 +02002591 nc_server_config_del_endpt_reference(endpt);
roman0bbc19c2023-05-26 09:59:09 +02002592 endpt->referenced_endpt_name = strdup(endpt_name);
2593 if (!endpt->referenced_endpt_name) {
2594 ERRMEM;
2595 ret = 1;
roman0bbc19c2023-05-26 09:59:09 +02002596 }
2597 goto cleanup;
2598 }
2599
2600 /* check for self reference */
2601 if (endpt == &server_opts.endpts[i]) {
2602 ERR(NULL, "Self client authentication reference detected.");
2603 ret = 1;
2604 goto cleanup;
2605 }
2606
2607 /* check for cyclic references */
roman2e797ef2023-06-19 10:47:49 +02002608 ret = nc_server_config_endpoint_client_auth_has_cycle(endpt, &server_opts.endpts[i]);
roman0bbc19c2023-05-26 09:59:09 +02002609 if (ret) {
2610 ERR(NULL, "Cyclic client authentication reference detected.");
2611 goto cleanup;
2612 }
2613
2614 /* assign the current endpt the referrenced endpt */
roman2e797ef2023-06-19 10:47:49 +02002615 if (is_ssh(node)) {
2616 endpt->opts.ssh->endpt_client_ref = &server_opts.endpts[i];
2617 } else {
2618 endpt->opts.tls->endpt_client_ref = &server_opts.endpts[i];
2619 }
roman0bbc19c2023-05-26 09:59:09 +02002620
2621cleanup:
2622 return ret;
2623}
2624
roman3f9b65c2023-06-05 14:26:58 +02002625static int
2626nc_server_config_tls_replace_cert_data(const struct lyd_node *node, struct nc_server_tls_opts *opts)
2627{
2628 nc_server_config_tls_del_cert_data(opts);
2629 opts->cert_data = strdup(lyd_get_value(node));
2630 if (!opts->cert_data) {
2631 ERRMEM;
2632 return 1;
2633 }
2634
2635 return 0;
2636}
2637
2638static int
2639nc_server_config_tls_replace_cert_data_client_auth(const struct lyd_node *node, struct nc_certificate *cert)
2640{
2641 nc_server_config_tls_del_cert_data_certificate(cert);
2642 cert->data = strdup(lyd_get_value(node));
2643 if (!cert->data) {
2644 ERRMEM;
2645 return 1;
2646 }
2647
2648 return 0;
2649}
2650
2651static int
2652nc_server_config_cert_data(const struct lyd_node *node, NC_OPERATION op)
2653{
2654 int ret = 0;
2655 struct nc_endpt *endpt;
2656 struct nc_certificate *cert;
2657
2658 assert(!strcmp(LYD_NAME(node), "cert-data"));
2659
2660 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
2661 ret = 1;
2662 goto cleanup;
2663 }
2664
2665 if ((equal_parent_name(node, 3, "server-identity")) && (is_listen(node))) {
2666 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2667 ret = nc_server_config_tls_replace_cert_data(node, endpt->opts.tls);
2668 if (ret) {
2669 goto cleanup;
2670 }
2671 }
2672 } else if ((equal_parent_name(node, 3, "ca-certs")) && (is_listen(node))) {
2673 if (nc_server_config_get_cert(node, &endpt->opts.tls->ca_certs, &cert)) {
2674 ret = 1;
2675 goto cleanup;
2676 }
2677
2678 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2679 ret = nc_server_config_tls_replace_cert_data_client_auth(node, cert);
2680 if (ret) {
2681 goto cleanup;
2682 }
2683 } else {
2684 nc_server_config_tls_del_cert_data_certificate(cert);
2685 }
2686 } else if ((equal_parent_name(node, 3, "ee-certs")) && (is_listen(node))) {
2687 if (nc_server_config_get_cert(node, &endpt->opts.tls->ee_certs, &cert)) {
2688 ret = 1;
2689 goto cleanup;
2690 }
2691
2692 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2693 ret = nc_server_config_tls_replace_cert_data_client_auth(node, cert);
2694 if (ret) {
2695 goto cleanup;
2696 }
2697 } else {
2698 nc_server_config_tls_del_cert_data_certificate(cert);
2699 }
2700 }
2701
2702cleanup:
2703 return ret;
2704}
2705
2706static int
2707nc_server_config_tls_create_asymmetric_key_ref(const struct lyd_node *node, struct nc_endpt *endpt)
2708{
2709 uint16_t i;
2710 struct nc_keystore *ks = &server_opts.keystore;
2711
2712 /* lookup name */
2713 for (i = 0; i < ks->asym_key_count; i++) {
2714 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
2715 break;
2716 }
2717 }
2718
2719 if (i == ks->asym_key_count) {
2720 ERR(NULL, "Asymmetric key \"%s\" not found in the keystore.", lyd_get_value(node));
2721 return 1;
2722 }
2723
2724 endpt->opts.tls->key_ref = &ks->asym_keys[i];
2725
2726 return 0;
2727}
2728
2729static int
2730nc_server_config_asymmetric_key(const struct lyd_node *node, NC_OPERATION op)
2731{
2732 int ret = 0;
2733 struct nc_endpt *endpt;
2734
2735 assert(!strcmp(LYD_NAME(node), "asymmetric-key"));
2736
2737 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
2738 ret = 1;
2739 goto cleanup;
2740 }
2741
2742 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2743 /* set to keystore */
2744 endpt->opts.tls->store = NC_STORE_KEYSTORE;
2745
2746 ret = nc_server_config_tls_create_asymmetric_key_ref(node, endpt);
2747 if (ret) {
2748 goto cleanup;
2749 }
2750 } else {
2751 endpt->opts.tls->key_ref = NULL;
2752 }
2753
2754cleanup:
2755 return ret;
2756}
2757
2758static int
2759nc_server_config_tls_create_certificate_ref(const struct lyd_node *node, struct nc_endpt *endpt, struct nc_asymmetric_key *key)
2760{
2761 uint16_t i;
2762
2763 /* lookup name */
2764 for (i = 0; i < key->cert_count; i++) {
2765 if (!strcmp(lyd_get_value(node), key->certs[i].name)) {
2766 break;
2767 }
2768 }
2769
2770 if (i == key->cert_count) {
2771 ERR(NULL, "Certificate \"%s\" not found in the asymmetric key \"%s\".", lyd_get_value(node), key->name);
2772 return 1;
2773 }
2774
2775 endpt->opts.tls->cert_ref = &key->certs[i];
2776
2777 return 0;
2778}
2779
2780static struct nc_asymmetric_key *
2781cert_get_asymmetric_key(const struct lyd_node *node)
2782{
2783 uint16_t i;
2784 struct nc_keystore *ks = &server_opts.keystore;
2785
2786 /* starting with certificate node */
2787 assert(!strcmp(LYD_NAME(node), "certificate"));
2788
2789 /* switch to it's only sibling, must be asymmetric-key */
2790 node = node->prev;
2791 assert(!strcmp(LYD_NAME(node), "asymmetric-key"));
2792
2793 /* find the given asymmetric key */
2794 for (i = 0; i < ks->asym_key_count; i++) {
2795 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
2796 return &ks->asym_keys[i];
2797 }
2798 }
2799
2800 /* didn't find it */
2801 ERR(NULL, "Asymmetric key \"%s\" not found in the keystore.", lyd_get_value(node));
2802 return NULL;
2803}
2804
2805static int
2806nc_server_config_create_ca_certs_certificate(const struct lyd_node *node, struct nc_server_tls_opts *opts)
2807{
2808 assert(!strcmp(LYD_NAME(node), "certificate"));
2809
2810 node = lyd_child(node);
2811 assert(!strcmp(LYD_NAME(node), "name"));
2812
2813 return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->ca_certs.certs, sizeof *opts->ca_certs.certs, &opts->ca_certs.cert_count);
2814}
2815
2816static int
2817nc_server_config_create_ee_certs_certificate(const struct lyd_node *node, struct nc_server_tls_opts *opts)
2818{
2819 assert(!strcmp(LYD_NAME(node), "certificate"));
2820
2821 node = lyd_child(node);
2822 assert(!strcmp(LYD_NAME(node), "name"));
2823
2824 return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->ee_certs.certs, sizeof *opts->ee_certs.certs, &opts->ee_certs.cert_count);
2825}
2826
2827static int
2828nc_server_config_certificate(const struct lyd_node *node, NC_OPERATION op)
2829{
2830 int ret = 0;
2831 struct nc_endpt *endpt;
2832 struct nc_asymmetric_key *key;
2833
2834 assert(!strcmp(LYD_NAME(node), "certificate"));
2835
2836 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
2837 ret = 1;
2838 goto cleanup;
2839 }
2840
2841 if ((equal_parent_name(node, 1, "keystore-reference")) && (is_listen(node))) {
2842 /* server-identity TLS listen */
2843
2844 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2845 /* set to keystore */
2846 endpt->opts.tls->store = NC_STORE_KEYSTORE;
2847
2848 if (!endpt->opts.tls->key_ref) {
2849 /* we don't have a key from which we need the cert yet */
2850 key = cert_get_asymmetric_key(node);
2851 if (!key) {
2852 ret = 1;
2853 goto cleanup;
2854 }
2855 } else {
2856 /* we have the key */
2857 key = endpt->opts.tls->key_ref;
2858 }
2859
2860 /* find the given cert in the key and set it */
2861 ret = nc_server_config_tls_create_certificate_ref(node, endpt, key);
2862 if (ret) {
2863 goto cleanup;
2864 }
2865 } else {
2866 endpt->opts.tls->cert_ref = NULL;
2867 }
2868 } else if ((equal_parent_name(node, 2, "ca-certs")) && (is_listen(node))) {
2869 /* client auth TLS listen */
2870 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2871 ret = nc_server_config_create_ca_certs_certificate(node, endpt->opts.tls);
2872 if (ret) {
2873 goto cleanup;
2874 }
2875 } else {
2876 nc_server_config_tls_del_certs(&endpt->opts.tls->ca_certs);
2877 }
2878 } else if ((equal_parent_name(node, 2, "ee-certs")) && (is_listen(node))) {
2879 /* client auth TLS listen */
2880 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
2881 ret = nc_server_config_create_ee_certs_certificate(node, endpt->opts.tls);
2882 if (ret) {
2883 goto cleanup;
2884 }
2885 } else {
2886 nc_server_config_tls_del_certs(&endpt->opts.tls->ee_certs);
2887 }
2888 }
2889
2890cleanup:
2891 return ret;
2892}
2893
2894static int
2895nc_server_config_create_cert_to_name(const struct lyd_node *node, struct nc_server_tls_opts *opts)
2896{
2897 int ret = 0;
2898 struct lyd_node *n;
2899 struct nc_ctn *new, *iter;
2900 const char *map_type, *name;
2901 uint32_t id;
2902 NC_TLS_CTN_MAPTYPE m_type;
2903
2904 assert(!strcmp(LYD_NAME(node), "cert-to-name"));
2905
2906 /* create new ctn */
2907 new = calloc(1, sizeof *new);
2908 if (!new) {
2909 ERRMEM;
2910 ret = 1;
2911 goto cleanup;
2912 }
2913
2914 /* get all the data */
2915 /* find the list's key */
2916 lyd_find_path(node, "id", 0, &n);
2917 assert(n);
2918 id = strtoul(lyd_get_value(n), NULL, 10);
2919
2920 /* find the ctn's name */
2921 lyd_find_path(node, "name", 0, &n);
2922 assert(n);
2923 name = lyd_get_value(n);
2924
2925 /* find the ctn's map-type */
2926 lyd_find_path(node, "map-type", 0, &n);
2927 assert(n);
2928 map_type = ((struct lyd_node_term *)n)->value.ident->name;
2929 if (!strcmp(map_type, "specified")) {
2930 m_type = NC_TLS_CTN_SPECIFIED;
2931 } else if (!strcmp(map_type, "san-rfc822-name")) {
2932 m_type = NC_TLS_CTN_SAN_RFC822_NAME;
2933 } else if (!strcmp(map_type, "san-dns-name")) {
2934 m_type = NC_TLS_CTN_SAN_DNS_NAME;
2935 } else if (!strcmp(map_type, "san-ip-address")) {
2936 m_type = NC_TLS_CTN_SAN_IP_ADDRESS;
2937 } else if (!strcmp(map_type, "san-any")) {
2938 m_type = NC_TLS_CTN_SAN_ANY;
2939 } else if (!strcmp(map_type, "common-name")) {
2940 m_type = NC_TLS_CTN_COMMON_NAME;
2941 } else {
2942 ERR(NULL, "Map-type identity \"%s\" not supported.", map_type);
2943 ret = 1;
2944 goto cleanup;
2945 }
2946
2947 /* find the right place for insertion */
2948 if (!opts->ctn) {
2949 /* inserting the first one */
2950 opts->ctn = new;
2951 } else if (opts->ctn->id > new->id) {
2952 /* insert at the beginning */
2953 new->next = opts->ctn;
2954 opts->ctn = new;
2955 } else {
2956 /* have to find the right place */
2957 for (iter = opts->ctn; iter->next && iter->next->id <= new->id; iter = iter->next) {}
2958 if (iter->id == new->id) {
2959 /* collision */
2960 new = iter;
2961 } else {
2962 new->next = iter->next;
2963 iter->next = new;
2964 }
2965 }
2966
2967 /* insert the right data */
2968 new->id = id;
2969 if (new->name) {
2970 free(new->name);
2971 }
2972 new->name = strdup(name);
2973 if (!new->name) {
2974 ERRMEM;
2975 ret = 1;
2976 goto cleanup;
2977 }
2978 new->map_type = m_type;
2979
2980cleanup:
2981 return ret;
2982}
2983
2984static int
2985nc_server_config_cert_to_name(const struct lyd_node *node, NC_OPERATION op)
2986{
2987 int ret = 0;
2988 struct nc_endpt *endpt;
2989 struct lyd_node *key;
2990 struct nc_ctn *ctn;
2991
2992 assert(!strcmp(LYD_NAME(node), "cert-to-name"));
2993
2994 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
2995 ret = 1;
2996 goto cleanup;
2997 }
2998
2999 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3000 ret = nc_server_config_create_cert_to_name(node, endpt->opts.tls);
3001 if (ret) {
3002 goto cleanup;
3003 }
3004 } else {
3005 /* find the given ctn entry */
3006 lyd_find_path(node, "id", 0, &key);
3007 assert(key);
3008 if (nc_server_config_get_ctn(node, endpt, &ctn)) {
3009 ret = 1;
3010 goto cleanup;
3011 }
3012 nc_server_config_del_ctn(endpt->opts.tls, ctn);
3013 }
3014
3015cleanup:
3016 return ret;
3017}
3018
3019static int
3020nc_server_config_replace_fingerprint(const struct lyd_node *node, struct nc_ctn *ctn)
3021{
3022 nc_server_config_del_fingerprint(ctn);
3023
3024 ctn->fingerprint = strdup(lyd_get_value(node));
3025 if (!ctn->fingerprint) {
3026 ERRMEM;
3027 return 1;
3028 }
3029
3030 return 0;
3031}
3032
3033static int
3034nc_server_config_fingerprint(const struct lyd_node *node, NC_OPERATION op)
3035{
3036 int ret = 0;
3037 struct nc_endpt *endpt;
3038 struct nc_ctn *ctn;
3039
3040 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
3041 ret = 1;
3042 goto cleanup;
3043 }
3044
3045 if (nc_server_config_get_ctn(node, endpt, &ctn)) {
3046 ret = 1;
3047 goto cleanup;
3048 }
3049
3050 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3051 ret = nc_server_config_replace_fingerprint(node, ctn);
3052 if (ret) {
3053 goto cleanup;
3054 }
3055 } else {
3056 nc_server_config_del_fingerprint(ctn);
3057 }
3058
3059cleanup:
3060 return ret;
3061}
3062
roman12644fe2023-06-08 11:06:42 +02003063static void
3064nc_server_config_set_tls_version(struct nc_server_tls_opts *opts, NC_TLS_VERSION version, NC_OPERATION op)
3065{
3066 if (op == NC_OP_CREATE) {
3067 /* add the version if it isn't there already */
3068 opts->tls_versions |= version;
3069 } else if ((op == NC_OP_DELETE) && (opts->tls_versions & version)) {
3070 /* delete the version if it is there */
3071 opts->tls_versions -= version;
3072 }
3073}
3074
3075static int
3076nc_server_config_tls_version(const struct lyd_node *node, NC_OPERATION op)
3077{
3078 int ret = 0;
3079 struct nc_endpt *endpt;
3080 const char *version = NULL;
3081
3082 assert(!strcmp(LYD_NAME(node), "tls-version"));
3083
3084 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
3085 ret = 1;
3086 goto cleanup;
3087 }
3088
3089 version = ((struct lyd_node_term *)node)->value.ident->name;
3090 if (!strcmp(version, "tls10")) {
3091 nc_server_config_set_tls_version(endpt->opts.tls, NC_TLS_VERSION_10, op);
3092 } else if (!strcmp(version, "tls11")) {
3093 nc_server_config_set_tls_version(endpt->opts.tls, NC_TLS_VERSION_11, op);
3094 } else if (!strcmp(version, "tls12")) {
3095 nc_server_config_set_tls_version(endpt->opts.tls, NC_TLS_VERSION_12, op);
3096 } else if (!strcmp(version, "tls13")) {
3097 nc_server_config_set_tls_version(endpt->opts.tls, NC_TLS_VERSION_13, op);
3098 } else {
3099 ERR(NULL, "TLS version \"%s\" not supported.", version);
3100 ret = 1;
3101 goto cleanup;
3102 }
3103
3104cleanup:
3105 return ret;
3106}
3107
3108static int
3109nc_server_config_create_cipher_suite(struct nc_server_tls_opts *opts, const char *cipher)
3110{
3111 int ret = 0;
3112 char *ssl_cipher = NULL;
3113 uint16_t i;
3114
3115 ssl_cipher = malloc(strlen(cipher) + 1);
3116 if (!ssl_cipher) {
3117 ERRMEM;
3118 ret = 1;
3119 goto cleanup;
3120 }
3121
3122 for (i = 0; cipher[i]; i++) {
3123 if (cipher[i] == '-') {
3124 /* OpenSSL requires _ instead of - in cipher names */
3125 ssl_cipher[i] = '_';
3126 } else {
3127 /* and requires uppercase unlike the identities */
3128 ssl_cipher[i] = toupper(cipher[i]);
3129 }
3130 }
3131 ssl_cipher[i] = '\0';
3132
3133 if (!opts->ciphers) {
3134 /* first entry */
3135 opts->ciphers = strdup(ssl_cipher);
3136 if (!opts->ciphers) {
3137 ERRMEM;
3138 ret = 1;
3139 goto cleanup;
3140 }
3141 } else {
3142 /* + 1 because of : between entries */
3143 opts->ciphers = nc_realloc(opts->ciphers, strlen(opts->ciphers) + strlen(ssl_cipher) + 1 + 1);
3144 if (!opts->ciphers) {
3145 ERRMEM;
3146 ret = 1;
3147 goto cleanup;
3148 }
roman08f67f42023-06-08 13:51:54 +02003149 strcat(opts->ciphers, ":");
3150 strcat(opts->ciphers, ssl_cipher);
roman12644fe2023-06-08 11:06:42 +02003151 }
3152
3153cleanup:
3154 free(ssl_cipher);
3155 return ret;
3156}
3157
3158static int
3159nc_server_config_del_concrete_cipher_suite(struct nc_server_tls_opts *opts, const char *cipher)
3160{
3161 int cipher_found = 0;
3162 char *haystack, *substr;
3163 size_t cipher_len = strlen(cipher);
3164
3165 /* delete */
3166 haystack = opts->ciphers;
3167 while ((substr = strstr(haystack, cipher))) {
3168 /* iterate over all the substrings */
3169 if (((substr == haystack) && (*(substr + cipher_len) == ':')) ||
3170 ((substr != haystack) && (*(substr - 1) == ':') && (*(substr + cipher_len) == ':'))) {
3171 /* either the first element of the string or somewhere in the middle */
3172 memmove(substr, substr + cipher_len + 1, strlen(substr + cipher_len + 1));
3173 cipher_found = 1;
3174 break;
3175 } else if ((*(substr - 1) == ':') && (*(substr + cipher_len) == '\0')) {
3176 /* the last element of the string */
3177 *(substr - 1) = '\0';
3178 cipher_found = 1;
3179 break;
3180 }
3181 haystack++;
3182 }
3183
3184 if (!cipher_found) {
3185 ERR(NULL, "Unable to delete a cipher (%s), which was not previously added.", cipher);
3186 return 1;
3187 }
3188
3189 return 0;
3190}
3191
3192static int
3193nc_server_config_cipher_suite(const struct lyd_node *node, NC_OPERATION op)
3194{
3195 int ret = 0;
3196 struct nc_endpt *endpt;
3197 const char *cipher = NULL;
3198
romanfaecc582023-06-15 16:13:31 +02003199 assert(!strcmp(LYD_NAME(node), "cipher-suite"));
3200
roman12644fe2023-06-08 11:06:42 +02003201 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
3202 ret = 1;
3203 goto cleanup;
3204 }
3205
3206 cipher = ((struct lyd_node_term *)node)->value.ident->name;
3207 if (op == NC_OP_CREATE) {
3208 ret = nc_server_config_create_cipher_suite(endpt->opts.tls, cipher);
3209 if (ret) {
3210 goto cleanup;
3211 }
3212 } else if (op == NC_OP_DELETE) {
3213 ret = nc_server_config_del_concrete_cipher_suite(endpt->opts.tls, cipher);
3214 if (ret) {
3215 goto cleanup;
3216 }
3217 }
3218
3219cleanup:
3220 return ret;
3221}
3222
romanfaecc582023-06-15 16:13:31 +02003223static int
3224nc_server_config_crl_url(const struct lyd_node *node, NC_OPERATION op)
3225{
3226 int ret = 0;
3227 struct nc_endpt *endpt;
3228
3229 assert(!strcmp(LYD_NAME(node), "crl-url"));
3230
3231 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
3232 ret = 1;
3233 goto cleanup;
3234 }
3235
3236 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3237 nc_server_config_del_url(endpt->opts.tls);
3238 endpt->opts.tls->crl_url = strdup(lyd_get_value(node));
3239 if (!endpt->opts.tls->crl_url) {
3240 ERRMEM;
3241 ret = 1;
3242 goto cleanup;
3243 }
3244 } else if (op == NC_OP_DELETE) {
3245 nc_server_config_del_url(endpt->opts.tls);
3246 }
3247
3248cleanup:
3249 return ret;
3250}
3251
3252static int
3253nc_server_config_crl_path(const struct lyd_node *node, NC_OPERATION op)
3254{
3255 int ret = 0;
3256 struct nc_endpt *endpt;
3257
3258 assert(!strcmp(LYD_NAME(node), "crl-path"));
3259
3260 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
3261 ret = 1;
3262 goto cleanup;
3263 }
3264
3265 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3266 nc_server_config_del_path(endpt->opts.tls);
3267 endpt->opts.tls->crl_path = strdup(lyd_get_value(node));
3268 if (!endpt->opts.tls->crl_path) {
3269 ERRMEM;
3270 ret = 1;
3271 goto cleanup;
3272 }
3273 } else if (op == NC_OP_DELETE) {
3274 nc_server_config_del_path(endpt->opts.tls);
3275 }
3276
3277cleanup:
3278 return ret;
3279}
3280
3281static int
3282nc_server_config_crl_cert_ext(const struct lyd_node *node, NC_OPERATION op)
3283{
3284 int ret = 0;
3285 struct nc_endpt *endpt;
3286
3287 assert(!strcmp(LYD_NAME(node), "crl-cert-ext"));
3288
3289 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
3290 ret = 1;
3291 goto cleanup;
3292 }
3293
3294 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
3295 endpt->opts.tls->crl_cert_ext = 1;
3296 } else if (op == NC_OP_DELETE) {
3297 endpt->opts.tls->crl_cert_ext = 0;
3298 }
3299
3300cleanup:
3301 return ret;
3302}
3303
roman2eab4742023-06-06 10:00:26 +02003304#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +02003305
roman83683fb2023-02-24 09:15:23 +01003306static int
romanf02273a2023-05-25 09:44:11 +02003307nc_server_config_parse_netconf_server(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01003308{
3309 const char *name = LYD_NAME(node);
3310
3311 if (!strcmp(name, "listen")) {
romanf02273a2023-05-25 09:44:11 +02003312 if (nc_server_config_listen(NULL, op)) {
romanc1d2b092023-02-02 08:58:27 +01003313 goto error;
3314 }
3315 } else if (!strcmp(name, "idle-timeout")) {
romane028ef92023-02-24 16:33:08 +01003316 if (nc_server_config_idle_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003317 goto error;
3318 }
3319 } else if (!strcmp(name, "endpoint")) {
romane028ef92023-02-24 16:33:08 +01003320 if (nc_server_config_endpoint(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003321 goto error;
3322 }
roman2eab4742023-06-06 10:00:26 +02003323 } else if (!strcmp(name, "unix-socket")) {
3324 if (nc_server_config_unix_socket(node, op)) {
3325 goto error;
3326 }
roman3f9b65c2023-06-05 14:26:58 +02003327 }
roman2eab4742023-06-06 10:00:26 +02003328#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +02003329 else if (!strcmp(name, "ssh")) {
romane028ef92023-02-24 16:33:08 +01003330 if (nc_server_config_ssh(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003331 goto error;
3332 }
roman2eab4742023-06-06 10:00:26 +02003333 } else if (!strcmp(name, "local-address")) {
romane028ef92023-02-24 16:33:08 +01003334 if (nc_server_config_local_address(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003335 goto error;
3336 }
3337 } else if (!strcmp(name, "local-port")) {
romane028ef92023-02-24 16:33:08 +01003338 if (nc_server_config_local_port(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003339 goto error;
3340 }
3341 } else if (!strcmp(name, "keepalives")) {
romane028ef92023-02-24 16:33:08 +01003342 if (nc_server_config_keepalives(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003343 goto error;
3344 }
3345 } else if (!strcmp(name, "idle-time")) {
romane028ef92023-02-24 16:33:08 +01003346 if (nc_server_config_idle_time(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003347 goto error;
3348 }
3349 } else if (!strcmp(name, "max-probes")) {
romane028ef92023-02-24 16:33:08 +01003350 if (nc_server_config_max_probes(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003351 goto error;
3352 }
3353 } else if (!strcmp(name, "probe-interval")) {
romane028ef92023-02-24 16:33:08 +01003354 if (nc_server_config_probe_interval(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003355 goto error;
3356 }
roman2eab4742023-06-06 10:00:26 +02003357 } else if (!strcmp(name, "host-key")) {
romane028ef92023-02-24 16:33:08 +01003358 if (nc_server_config_host_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003359 goto error;
3360 }
roman2eab4742023-06-06 10:00:26 +02003361 } else if (!strcmp(name, "public-key-format")) {
romane028ef92023-02-24 16:33:08 +01003362 if (nc_server_config_public_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003363 goto error;
3364 }
3365 } else if (!strcmp(name, "public-key")) {
romane028ef92023-02-24 16:33:08 +01003366 if (nc_server_config_public_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003367 goto error;
3368 }
3369 } else if (!strcmp(name, "private-key-format")) {
romane028ef92023-02-24 16:33:08 +01003370 if (nc_server_config_private_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003371 goto error;
3372 }
3373 } else if (!strcmp(name, "cleartext-private-key")) {
romane028ef92023-02-24 16:33:08 +01003374 if (nc_server_config_cleartext_private_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003375 goto error;
3376 }
roman2eab4742023-06-06 10:00:26 +02003377 } else if (!strcmp(name, "keystore-reference")) {
romane028ef92023-02-24 16:33:08 +01003378 if (nc_server_config_keystore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003379 goto error;
3380 }
3381 } else if (!strcmp(name, "user")) {
romane028ef92023-02-24 16:33:08 +01003382 if (nc_server_config_user(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003383 goto error;
3384 }
3385 } else if (!strcmp(name, "auth-attempts")) {
romane028ef92023-02-24 16:33:08 +01003386 if (nc_server_config_auth_attempts(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003387 goto error;
3388 }
3389 } else if (!strcmp(name, "auth-timeout")) {
romane028ef92023-02-24 16:33:08 +01003390 if (nc_server_config_auth_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003391 goto error;
3392 }
roman2eab4742023-06-06 10:00:26 +02003393 } else if (!strcmp(name, "truststore-reference")) {
romane028ef92023-02-24 16:33:08 +01003394 if (nc_server_config_truststore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003395 goto error;
3396 }
roman2eab4742023-06-06 10:00:26 +02003397 } else if (!strcmp(name, "password")) {
romane028ef92023-02-24 16:33:08 +01003398 if (nc_server_config_password(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003399 goto error;
3400 }
3401 } else if (!strcmp(name, "pam-config-file-name")) {
romane028ef92023-02-24 16:33:08 +01003402 if (nc_server_config_pam_name(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003403 goto error;
3404 }
3405 } else if (!strcmp(name, "pam-config-file-dir")) {
romane028ef92023-02-24 16:33:08 +01003406 if (nc_server_config_pam_dir(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003407 goto error;
3408 }
3409 } else if (!strcmp(name, "none")) {
romane028ef92023-02-24 16:33:08 +01003410 if (nc_server_config_none(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003411 goto error;
3412 }
3413 } else if (!strcmp(name, "host-key-alg")) {
romane028ef92023-02-24 16:33:08 +01003414 if (nc_server_config_host_key_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003415 goto error;
3416 }
3417 } else if (!strcmp(name, "key-exchange-alg")) {
romane028ef92023-02-24 16:33:08 +01003418 if (nc_server_config_kex_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003419 goto error;
3420 }
3421 } else if (!strcmp(name, "encryption-alg")) {
romane028ef92023-02-24 16:33:08 +01003422 if (nc_server_config_encryption_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003423 goto error;
3424 }
3425 } else if (!strcmp(name, "mac-alg")) {
romane028ef92023-02-24 16:33:08 +01003426 if (nc_server_config_mac_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01003427 goto error;
3428 }
roman2eab4742023-06-06 10:00:26 +02003429 } else if (!strcmp(name, "endpoint-client-auth")) {
roman0bbc19c2023-05-26 09:59:09 +02003430 if (nc_server_config_endpoint_client_auth(node, op)) {
3431 goto error;
3432 }
roman2eab4742023-06-06 10:00:26 +02003433 } else if (!strcmp(name, "tls")) {
roman3f9b65c2023-06-05 14:26:58 +02003434 if (nc_server_config_tls(node, op)) {
3435 goto error;
3436 }
3437 } else if (!strcmp(name, "cert-data")) {
3438 if (nc_server_config_cert_data(node, op)) {
3439 goto error;
3440 }
3441 } else if (!strcmp(name, "asymmetric-key")) {
3442 if (nc_server_config_asymmetric_key(node, op)) {
3443 goto error;
3444 }
3445 } else if (!strcmp(name, "certificate")) {
3446 if (nc_server_config_certificate(node, op)) {
3447 goto error;
3448 }
3449 } else if (!strcmp(name, "cert-to-name")) {
3450 if (nc_server_config_cert_to_name(node, op)) {
3451 goto error;
3452 }
3453 } else if (!strcmp(name, "fingerprint")) {
3454 if (nc_server_config_fingerprint(node, op)) {
3455 goto error;
3456 }
roman12644fe2023-06-08 11:06:42 +02003457 } else if (!strcmp(name, "tls-version")) {
3458 if (nc_server_config_tls_version(node, op)) {
3459 goto error;
3460 }
3461 } else if (!strcmp(name, "cipher-suite")) {
3462 if (nc_server_config_cipher_suite(node, op)) {
3463 goto error;
3464 }
romanfaecc582023-06-15 16:13:31 +02003465 } else if (!strcmp(name, "crl-url")) {
3466 if (nc_server_config_crl_url(node, op)) {
3467 goto error;
3468 }
3469 } else if (!strcmp(name, "crl-path")) {
3470 if (nc_server_config_crl_path(node, op)) {
3471 goto error;
3472 }
3473 } else if (!strcmp(name, "crl-cert-ext")) {
3474 if (nc_server_config_crl_cert_ext(node, op)) {
3475 goto error;
3476 }
roman3f9b65c2023-06-05 14:26:58 +02003477 }
roman2eab4742023-06-06 10:00:26 +02003478#endif /* NC_ENABLED_SSH_TLS */
romanc1d2b092023-02-02 08:58:27 +01003479
3480 return 0;
3481
3482error:
3483 ERR(NULL, "Configuring (%s) failed.", LYD_NAME(node));
3484 return 1;
3485}
3486
3487int
roman0bbc19c2023-05-26 09:59:09 +02003488nc_server_config_parse_tree(const struct lyd_node *node, NC_OPERATION parent_op, NC_MODULE module)
romanc1d2b092023-02-02 08:58:27 +01003489{
3490 struct lyd_node *child;
3491 struct lyd_meta *m;
romanf9906b42023-05-22 14:04:29 +02003492 NC_OPERATION current_op = NC_OP_UNKNOWN;
romanf02273a2023-05-25 09:44:11 +02003493 int ret;
romanc1d2b092023-02-02 08:58:27 +01003494
3495 assert(node);
3496
romanf9906b42023-05-22 14:04:29 +02003497 /* get current op if there is any */
3498 if ((m = lyd_find_meta(node->meta, NULL, "yang:operation"))) {
3499 if (!strcmp(lyd_get_meta_value(m), "create")) {
3500 current_op = NC_OP_CREATE;
3501 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
3502 current_op = NC_OP_DELETE;
3503 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
3504 current_op = NC_OP_REPLACE;
3505 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
3506 current_op = NC_OP_NONE;
romanc1d2b092023-02-02 08:58:27 +01003507 }
3508 }
3509
3510 /* node has no op, inherit from the parent */
romanf9906b42023-05-22 14:04:29 +02003511 if (!current_op) {
3512 if (!parent_op) {
3513 ERR(NULL, "Unknown operation for node \"%s\".", LYD_NAME(node));
3514 return 1;
3515 }
3516
romanc1d2b092023-02-02 08:58:27 +01003517 current_op = parent_op;
3518 }
3519
3520 switch (current_op) {
3521 case NC_OP_NONE:
3522 break;
3523 case NC_OP_CREATE:
3524 case NC_OP_DELETE:
3525 case NC_OP_REPLACE:
roman2eab4742023-06-06 10:00:26 +02003526#ifdef NC_ENABLED_SSH_TLS
3527 if (module == NC_MODULE_KEYSTORE) {
romanf02273a2023-05-25 09:44:11 +02003528 ret = nc_server_config_parse_keystore(node, current_op);
roman2eab4742023-06-06 10:00:26 +02003529 } else if (module == NC_MODULE_TRUSTSTORE) {
romanf02273a2023-05-25 09:44:11 +02003530 ret = nc_server_config_parse_truststore(node, current_op);
roman2eab4742023-06-06 10:00:26 +02003531 } else
3532#endif /* NC_ENABLED_SSH_TLS */
3533 {
3534 ret = nc_server_config_parse_netconf_server(node, current_op);
romanf02273a2023-05-25 09:44:11 +02003535 }
3536 if (ret) {
3537 return ret;
romanc1d2b092023-02-02 08:58:27 +01003538 }
3539 break;
3540 default:
3541 break;
3542 }
3543
3544 if (current_op != NC_OP_DELETE) {
3545 LY_LIST_FOR(lyd_child(node), child) {
roman0bbc19c2023-05-26 09:59:09 +02003546 if (nc_server_config_parse_tree(child, current_op, module)) {
romanc1d2b092023-02-02 08:58:27 +01003547 return 1;
3548 }
3549 }
3550 }
3551 return 0;
3552}
3553
romanc1d2b092023-02-02 08:58:27 +01003554API int
3555nc_server_config_load_modules(struct ly_ctx **ctx)
3556{
3557 int i, new_ctx = 0;
3558
3559 if (!*ctx) {
3560 if (ly_ctx_new(NC_SERVER_SEARCH_DIR, 0, ctx)) {
3561 ERR(NULL, "Couldn't create new libyang context.\n");
3562 goto error;
3563 }
3564 new_ctx = 1;
3565 }
3566
3567 /* all features */
3568 const char *ietf_nectonf_server[] = {"ssh-listen", "tls-listen", "ssh-call-home", "tls-call-home", "central-netconf-server-supported", NULL};
3569 /* all features */
3570 const char *ietf_x509_cert_to_name[] = {NULL};
roman7fdc84d2023-06-06 13:14:53 +02003571 /* no private-key-encryption, csr-generation, p10-csr-format, certificate-expiration-notification,
3572 * encrypted-passwords, hidden-symmetric-keys, encrypted-symmetric-keys, hidden-private-keys, encrypted-private-keys
3573 */
romanc1d2b092023-02-02 08:58:27 +01003574 const char *ietf_crypto_types[] = {
3575 "one-symmetric-key-format", "one-asymmetric-key-format", "symmetrically-encrypted-value-format",
3576 "asymmetrically-encrypted-value-format", "cms-enveloped-data-format", "cms-encrypted-data-format",
roman7fdc84d2023-06-06 13:14:53 +02003577 "cleartext-passwords", "cleartext-symmetric-keys", "cleartext-private-keys", NULL
romanc1d2b092023-02-02 08:58:27 +01003578 };
3579 /* all features */
3580 const char *ietf_tcp_common[] = {"keepalives-supported", NULL};
roman7fdc84d2023-06-06 13:14:53 +02003581 /* all features */
3582 const char *ietf_tcp_server[] = {"tcp-server-keepalives", NULL};
3583 /* all features */
3584 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 +01003585 /* no ssh-x509-certs */
3586 const char *ietf_ssh_common[] = {"transport-params", "public-key-generation", NULL};
roman7fdc84d2023-06-06 13:14:53 +02003587 /* no ssh-server-keepalives and local-user-auth-hostbased */
3588 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 +01003589 /* all features */
3590 const char *iana_ssh_encryption_algs[] = {NULL};
3591 /* all features */
3592 const char *iana_ssh_key_exchange_algs[] = {NULL};
3593 /* all features */
3594 const char *iana_ssh_mac_algs[] = {NULL};
3595 /* all features */
3596 const char *iana_ssh_public_key_algs[] = {NULL};
romanc1d2b092023-02-02 08:58:27 +01003597 /* all features */
roman7fdc84d2023-06-06 13:14:53 +02003598 const char *iana_crypt_hash[] = {"crypt-hash-md5", "crypt-hash-sha-256", "crypt-hash-sha-512", NULL};
3599 /* no symmetric-keys */
3600 const char *ietf_keystore[] = {"central-keystore-supported", "inline-definitions-supported", "asymmetric-keys", NULL};
3601 /* all features */
3602 const char *ietf_truststore[] = {"central-truststore-supported", "inline-definitions-supported", "certificates", "public-keys", NULL};
3603 /* all features */
3604 const char *ietf_tls_common[] = {"tls10", "tls11", "tls12", "tls13", "hello-params", "public-key-generation", NULL};
romanc1d2b092023-02-02 08:58:27 +01003605 /* all features */
3606 const char *ietf_tls_server[] = {
3607 "tls-server-keepalives", "server-ident-x509-cert", "server-ident-raw-public-key", "server-ident-tls12-psk",
3608 "server-ident-tls13-epsk", "client-auth-supported", "client-auth-x509-cert", "client-auth-raw-public-key",
3609 "client-auth-tls12-psk", "client-auth-tls13-epsk", NULL
3610 };
roman12644fe2023-06-08 11:06:42 +02003611 const char *iana_tls_cipher_suite_algs[] = {NULL};
romanc1d2b092023-02-02 08:58:27 +01003612 /* all features */
3613 const char *libnetconf2_netconf_server[] = {NULL};
3614
3615 const char *module_names[] = {
roman7fdc84d2023-06-06 13:14:53 +02003616 "ietf-netconf-server", "ietf-x509-cert-to-name", "ietf-crypto-types", "ietf-tcp-common", "ietf-tcp-server",
3617 "ietf-tcp-client", "ietf-ssh-common", "ietf-ssh-server", "iana-ssh-encryption-algs",
3618 "iana-ssh-key-exchange-algs", "iana-ssh-mac-algs", "iana-ssh-public-key-algs", "iana-crypt-hash",
roman12644fe2023-06-08 11:06:42 +02003619 "ietf-keystore", "ietf-truststore", "ietf-tls-common", "ietf-tls-server", "iana-tls-cipher-suite-algs",
3620 "libnetconf2-netconf-server", NULL
romanc1d2b092023-02-02 08:58:27 +01003621 };
3622
3623 const char **module_features[] = {
roman7fdc84d2023-06-06 13:14:53 +02003624 ietf_nectonf_server, ietf_x509_cert_to_name, ietf_crypto_types, ietf_tcp_common,
3625 ietf_tcp_server, ietf_tcp_client, ietf_ssh_common, ietf_ssh_server, iana_ssh_encryption_algs,
3626 iana_ssh_key_exchange_algs, iana_ssh_mac_algs, iana_ssh_public_key_algs, iana_crypt_hash,
roman12644fe2023-06-08 11:06:42 +02003627 ietf_keystore, ietf_truststore, ietf_tls_common, ietf_tls_server, iana_tls_cipher_suite_algs,
3628 libnetconf2_netconf_server, NULL
romanc1d2b092023-02-02 08:58:27 +01003629 };
3630
3631 for (i = 0; module_names[i] != NULL; i++) {
3632 if (!ly_ctx_load_module(*ctx, module_names[i], NULL, module_features[i])) {
3633 ERR(NULL, "Loading module \"%s\" failed.\n", module_names[i]);
3634 goto error;
3635 }
3636 }
3637
3638 return 0;
3639
3640error:
3641 if (new_ctx) {
3642 ly_ctx_destroy(*ctx);
3643 *ctx = NULL;
3644 }
3645 return 1;
3646}
3647
romanf9906b42023-05-22 14:04:29 +02003648static int
3649nc_server_config_fill_nectonf_server(const struct lyd_node *data, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01003650{
3651 int ret = 0;
3652 struct lyd_node *tree;
romand57b3722023-04-05 11:26:25 +02003653
romanc1d2b092023-02-02 08:58:27 +01003654 ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &tree);
3655 if (ret) {
3656 ERR(NULL, "Unable to find the netconf-server container in the YANG data.");
3657 goto cleanup;
3658 }
3659
roman0bbc19c2023-05-26 09:59:09 +02003660 if (nc_server_config_parse_tree(tree, op, NC_MODULE_NETCONF_SERVER)) {
3661 ret = 1;
3662 goto cleanup;
3663 }
3664
roman2eab4742023-06-06 10:00:26 +02003665#ifdef NC_ENABLED_SSH_TLS
3666 /* backward check of client auth reference */
roman0bbc19c2023-05-26 09:59:09 +02003667 if (nc_server_config_fill_endpt_client_auth()) {
romanf9906b42023-05-22 14:04:29 +02003668 ret = 1;
3669 goto cleanup;
3670 }
roman2eab4742023-06-06 10:00:26 +02003671#endif /* NC_ENABLED_SSH_TLS */
romanf9906b42023-05-22 14:04:29 +02003672
3673cleanup:
3674 return ret;
3675}
3676
3677API int
romanf6f37a52023-05-25 14:27:51 +02003678nc_server_config_setup_diff(const struct lyd_node *data)
romanf9906b42023-05-22 14:04:29 +02003679{
3680 int ret = 0;
3681
3682 /* LOCK */
3683 pthread_rwlock_wrlock(&server_opts.config_lock);
3684
roman2eab4742023-06-06 10:00:26 +02003685#ifdef NC_ENABLED_SSH_TLS
romanf9906b42023-05-22 14:04:29 +02003686 /* configure keystore */
romanf02273a2023-05-25 09:44:11 +02003687 ret = nc_server_config_fill_keystore(data, NC_OP_UNKNOWN);
romanf9906b42023-05-22 14:04:29 +02003688 if (ret) {
3689 ERR(NULL, "Filling keystore failed.");
3690 goto cleanup;
3691 }
3692
3693 /* configure truststore */
romanf02273a2023-05-25 09:44:11 +02003694 ret = nc_server_config_fill_truststore(data, NC_OP_UNKNOWN);
romanf9906b42023-05-22 14:04:29 +02003695 if (ret) {
3696 ERR(NULL, "Filling truststore failed.");
3697 goto cleanup;
3698 }
roman2eab4742023-06-06 10:00:26 +02003699#endif /* NC_ENABLED_SSH_TLS */
romanf9906b42023-05-22 14:04:29 +02003700
3701 /* configure netconf-server */
3702 ret = nc_server_config_fill_nectonf_server(data, NC_OP_UNKNOWN);
3703 if (ret) {
3704 ERR(NULL, "Filling netconf-server failed.");
3705 goto cleanup;
3706 }
3707
3708cleanup:
3709 /* UNLOCK */
3710 pthread_rwlock_unlock(&server_opts.config_lock);
3711 return ret;
3712}
3713
3714API int
romanf6f37a52023-05-25 14:27:51 +02003715nc_server_config_setup_data(const struct lyd_node *data)
romanf9906b42023-05-22 14:04:29 +02003716{
3717 int ret = 0;
3718 struct lyd_node *tree, *iter, *root;
3719
3720 /* LOCK */
3721 pthread_rwlock_wrlock(&server_opts.config_lock);
3722
3723 /* find the netconf-server node */
3724 ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &root);
3725 if (ret) {
3726 ERR(NULL, "Unable to find the netconf-server container in the YANG data.");
3727 goto cleanup;
3728 }
3729
3730 /* iterate through all the nodes and make sure there is no operation attribute */
3731 LY_LIST_FOR(root, tree) {
3732 LYD_TREE_DFS_BEGIN(tree, iter) {
3733 if (lyd_find_meta(iter->meta, NULL, "yang:operation")) {
3734 ERR(NULL, "Unexpected operation attribute in the YANG data.");
romanc1d2b092023-02-02 08:58:27 +01003735 ret = 1;
3736 goto cleanup;
3737 }
romanf9906b42023-05-22 14:04:29 +02003738 LYD_TREE_DFS_END(tree, iter);
romanc1d2b092023-02-02 08:58:27 +01003739 }
3740 }
3741
romanf9906b42023-05-22 14:04:29 +02003742 /* delete the current configuration */
romanf02273a2023-05-25 09:44:11 +02003743 nc_server_config_listen(NULL, NC_OP_DELETE);
roman2eab4742023-06-06 10:00:26 +02003744#ifdef NC_ENABLED_SSH_TLS
romanf02273a2023-05-25 09:44:11 +02003745 nc_server_config_ks_keystore(NULL, NC_OP_DELETE);
3746 nc_server_config_ts_truststore(NULL, NC_OP_DELETE);
romanf9906b42023-05-22 14:04:29 +02003747
3748 /* configure keystore */
romanf02273a2023-05-25 09:44:11 +02003749 ret = nc_server_config_fill_keystore(data, NC_OP_CREATE);
romanf9906b42023-05-22 14:04:29 +02003750 if (ret) {
3751 ERR(NULL, "Filling keystore failed.");
3752 goto cleanup;
3753 }
3754
3755 /* configure truststore */
romanf02273a2023-05-25 09:44:11 +02003756 ret = nc_server_config_fill_truststore(data, NC_OP_CREATE);
romanf9906b42023-05-22 14:04:29 +02003757 if (ret) {
3758 ERR(NULL, "Filling truststore failed.");
3759 goto cleanup;
3760 }
roman2eab4742023-06-06 10:00:26 +02003761#endif /* NC_ENABLED_SSH_TLS */
romanf9906b42023-05-22 14:04:29 +02003762
3763 /* configure netconf-server */
3764 ret = nc_server_config_fill_nectonf_server(data, NC_OP_CREATE);
3765 if (ret) {
3766 ERR(NULL, "Filling netconf-server failed.");
romanc1d2b092023-02-02 08:58:27 +01003767 goto cleanup;
3768 }
3769
3770cleanup:
3771 /* UNLOCK */
3772 pthread_rwlock_unlock(&server_opts.config_lock);
3773 return ret;
3774}
roman3f9b65c2023-06-05 14:26:58 +02003775
3776API int
3777nc_server_config_setup_path(const struct ly_ctx *ctx, const char *path)
3778{
3779 struct lyd_node *tree = NULL;
3780 int ret = 0;
3781
3782 NC_CHECK_ARG_RET(NULL, path, 1);
3783
3784 ret = lyd_parse_data_path(ctx, path, LYD_UNKNOWN, LYD_PARSE_NO_STATE | LYD_PARSE_STRICT, LYD_VALIDATE_NO_STATE, &tree);
3785 if (ret) {
3786 goto cleanup;
3787 }
3788
3789 ret = nc_server_config_setup_data(tree);
3790 if (ret) {
3791 goto cleanup;
3792 }
3793
3794cleanup:
3795 lyd_free_all(tree);
3796 return ret;
3797}