blob: 9ec3e43330583a7c6fa50f0ee2893efb528bb0dd [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>
19#include <stdlib.h>
20#include <string.h>
21
22#include "compat.h"
romanc1d2b092023-02-02 08:58:27 +010023#include "libnetconf.h"
romane028ef92023-02-24 16:33:08 +010024#include "server_config.h"
romanf02273a2023-05-25 09:44:11 +020025#include "server_config_p.h"
romanc1d2b092023-02-02 08:58:27 +010026#include "session_server.h"
27#include "session_server_ch.h"
28
29/* All libssh supported host-key, key-exchange, encryption and mac algorithms as of version 0.10.90 */
30
31static const char *supported_hostkey_algs[] = {
romana6bf6ab2023-05-26 13:26:02 +020032 "openssh-ssh-ed25519-cert-v01", "openssh-ecdsa-sha2-nistp521-cert-v01",
33 "openssh-ecdsa-sha2-nistp384-cert-v01", "openssh-ecdsa-sha2-nistp256-cert-v01",
34 "openssh-rsa-sha2-512-cert-v01", "openssh-rsa-sha2-256-cert-v01",
35 "openssh-ssh-rsa-cert-v01", "openssh-ssh-dss-cert-v01",
romanc1d2b092023-02-02 08:58:27 +010036 "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256",
37 "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa", "ssh-dss", NULL
38};
39
40static const char *supported_kex_algs[] = {
romana6bf6ab2023-05-26 13:26:02 +020041 "diffie-hellman-group-exchange-sha1", "curve25519-sha256", "libssh-curve25519-sha256",
romanc1d2b092023-02-02 08:58:27 +010042 "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group18-sha512",
43 "diffie-hellman-group16-sha512", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group14-sha256", NULL
44};
45
46static const char *supported_encryption_algs[] = {
romana6bf6ab2023-05-26 13:26:02 +020047 "openssh-chacha20-poly1305", "openssh-aes256-gcm", "openssh-aes128-gcm",
romanc1d2b092023-02-02 08:58:27 +010048 "aes256-ctr", "aes192-ctr", "aes128-ctr", "aes256-cbc", "aes192-cbc", "aes128-cbc",
roman27215242023-03-10 14:55:00 +010049 "blowfish-cbc", "triple-des-cbc", "none", NULL
romanc1d2b092023-02-02 08:58:27 +010050};
51
52static const char *supported_mac_algs[] = {
romana6bf6ab2023-05-26 13:26:02 +020053 "openssh-hmac-sha2-256-etm", "openssh-hmac-sha2-512-etm", "openssh-hmac-sha1-etm",
romanc1d2b092023-02-02 08:58:27 +010054 "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", NULL
55};
56
57extern struct nc_server_opts server_opts;
58
romanf02273a2023-05-25 09:44:11 +020059int
60nc_server_config_get_endpt(const struct lyd_node *node, struct nc_endpt **endpt, struct nc_bind **bind)
romanc1d2b092023-02-02 08:58:27 +010061{
62 uint16_t i;
63 const char *endpt_name;
64
65 assert(node);
66
67 while (node) {
68 if (!strcmp(LYD_NAME(node), "endpoint")) {
69 break;
70 }
71 node = lyd_parent(node);
72 }
73
74 if (!node) {
75 ERR(NULL, "Node \"%s\" is not contained in an endpoint subtree.", LYD_NAME(node));
76 return 1;
77 }
78
79 node = lyd_child(node);
80 assert(!strcmp(LYD_NAME(node), "name"));
81 endpt_name = lyd_get_value(node);
82
83 for (i = 0; i < server_opts.endpt_count; i++) {
84 if (!strcmp(server_opts.endpts[i].name, endpt_name)) {
85 *endpt = &server_opts.endpts[i];
86 if (bind) {
87 *bind = &server_opts.binds[i];
88 }
89 return 0;
90 }
91 }
92
93 ERR(NULL, "Endpoint \"%s\" was not found.", endpt_name);
94 return 1;
95}
96
romanf02273a2023-05-25 09:44:11 +020097int
98nc_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 +010099{
100 uint16_t i;
101 const char *hostkey_name;
102
103 assert(node && opts);
104
105 while (node) {
106 if (!strcmp(LYD_NAME(node), "host-key")) {
107 break;
108 }
109 node = lyd_parent(node);
110 }
111
112 if (!node) {
113 ERR(NULL, "Node \"%s\" is not contained in a host-key subtree.", LYD_NAME(node));
114 return 1;
115 }
116
117 node = lyd_child(node);
118 assert(!strcmp(LYD_NAME(node), "name"));
119 hostkey_name = lyd_get_value(node);
120
121 for (i = 0; i < opts->hostkey_count; i++) {
122 if (!strcmp(opts->hostkeys[i].name, hostkey_name)) {
123 *hostkey = &opts->hostkeys[i];
124 return 0;
125 }
126 }
127
128 ERR(NULL, "Host-key \"%s\" was not found.", hostkey_name);
129 return 1;
130}
131
romanf02273a2023-05-25 09:44:11 +0200132int
133nc_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 +0100134{
135 uint16_t i;
136 const char *authkey_name;
137
138 assert(node && opts);
139
140 while (node) {
141 if (!strcmp(LYD_NAME(node), "user")) {
142 break;
143 }
144 node = lyd_parent(node);
145 }
146
147 if (!node) {
148 ERR(NULL, "Node \"%s\" is not contained in a client-authentication subtree.", LYD_NAME(node));
149 return 1;
150 }
151
152 node = lyd_child(node);
153 assert(!strcmp(LYD_NAME(node), "name"));
154 authkey_name = lyd_get_value(node);
155
156 for (i = 0; i < opts->client_count; i++) {
157 if (!strcmp(opts->auth_clients[i].username, authkey_name)) {
158 *auth_client = &opts->auth_clients[i];
159 return 0;
160 }
161 }
162
163 ERR(NULL, "Authorized key \"%s\" was not found.", authkey_name);
164 return 1;
165}
166
romanf02273a2023-05-25 09:44:11 +0200167int
168nc_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 +0100169{
170 uint16_t i;
171 const char *pubkey_name;
172
173 assert(node && auth_client);
174
175 node = lyd_parent(node);
176 while (node) {
177 if (!strcmp(LYD_NAME(node), "public-key")) {
178 break;
179 }
180 node = lyd_parent(node);
181 }
182
183 if (!node) {
184 ERR(NULL, "Node \"%s\" is not contained in a public-key subtree.", LYD_NAME(node));
185 return 1;
186 }
187
188 node = lyd_child(node);
189 assert(!strcmp(LYD_NAME(node), "name"));
190 pubkey_name = lyd_get_value(node);
191
192 for (i = 0; i < auth_client->pubkey_count; i++) {
193 if (!strcmp(auth_client->pubkeys[i].name, pubkey_name)) {
194 *pubkey = &auth_client->pubkeys[i];
195 return 0;
196 }
197 }
198
199 ERR(NULL, "Public key \"%s\" was not found.", pubkey_name);
200 return 1;
201}
202
romanf02273a2023-05-25 09:44:11 +0200203int
romanc1d2b092023-02-02 08:58:27 +0100204equal_parent_name(const struct lyd_node *node, uint16_t parent_count, const char *parent_name)
205{
206 uint16_t i;
207
208 assert(node && parent_count > 0 && parent_name);
209
210 node = lyd_parent(node);
211 for (i = 1; i < parent_count; i++) {
212 node = lyd_parent(node);
213 }
214
215 if (!strcmp(LYD_NAME(node), parent_name)) {
216 return 1;
217 }
218
219 return 0;
220}
221
romanf02273a2023-05-25 09:44:11 +0200222int
223nc_server_config_realloc(const char *key_value, void **ptr, size_t size, uint16_t *count)
224{
225 int ret = 0;
226 void *tmp;
227 char **name;
228
229 tmp = realloc(*ptr, (*count + 1) * size);
230 if (!tmp) {
231 ERRMEM;
232 ret = 1;
233 goto cleanup;
234 }
235 *ptr = tmp;
236
237 /* set the newly allocated memory to 0 */
238 memset((char *)(*ptr) + (*count * size), 0, size);
239 (*count)++;
240
241 /* access the first member of the supposed structure */
242 name = (char **)((*ptr) + ((*count - 1) * size));
243
244 /* and set it's value */
245 *name = strdup(key_value);
246 if (!*name) {
247 ERRMEM;
248 ret = 1;
249 goto cleanup;
250 }
251
252cleanup:
253 return ret;
254}
255
romanc1d2b092023-02-02 08:58:27 +0100256static void
roman874fed12023-05-25 10:20:01 +0200257nc_server_config_del_auth_client_pam_name(struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100258{
259 free(auth_client->pam_config_name);
260 auth_client->pam_config_name = NULL;
261}
262
263static void
roman874fed12023-05-25 10:20:01 +0200264nc_server_config_del_auth_client_pam_dir(struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100265{
266 free(auth_client->pam_config_dir);
267 auth_client->pam_config_dir = NULL;
268}
269
270static void
roman874fed12023-05-25 10:20:01 +0200271nc_server_config_del_endpt_name(struct nc_endpt *endpt)
romanc1d2b092023-02-02 08:58:27 +0100272{
273 free(endpt->name);
274 endpt->name = NULL;
275}
276
277static void
roman0bbc19c2023-05-26 09:59:09 +0200278nc_server_config_del_endpt_reference(struct nc_endpt *endpt)
279{
280 free(endpt->referenced_endpt_name);
281 endpt->referenced_endpt_name = NULL;
282}
283
284static void
roman874fed12023-05-25 10:20:01 +0200285nc_server_config_del_local_address(struct nc_bind *bind)
romanc1d2b092023-02-02 08:58:27 +0100286{
287 free(bind->address);
288 bind->address = NULL;
289}
290
291static void
roman874fed12023-05-25 10:20:01 +0200292nc_server_config_del_hostkey_name(struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +0100293{
294 free(hostkey->name);
295 hostkey->name = NULL;
296}
297
298static void
roman874fed12023-05-25 10:20:01 +0200299nc_server_config_del_public_key(struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +0100300{
roman8edee342023-03-31 13:25:48 +0200301 free(hostkey->key.pub_base64);
302 hostkey->key.pub_base64 = NULL;
romanc1d2b092023-02-02 08:58:27 +0100303}
304
305static void
roman874fed12023-05-25 10:20:01 +0200306nc_server_config_del_private_key(struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +0100307{
roman8edee342023-03-31 13:25:48 +0200308 free(hostkey->key.priv_base64);
309 hostkey->key.priv_base64 = NULL;
romanc1d2b092023-02-02 08:58:27 +0100310}
311
312static void
roman874fed12023-05-25 10:20:01 +0200313nc_server_config_del_auth_client_username(struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100314{
315 free(auth_client->username);
316 auth_client->username = NULL;
317}
318
319static void
roman874fed12023-05-25 10:20:01 +0200320nc_server_config_del_auth_client_pubkey_name(struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100321{
322 free(pubkey->name);
323 pubkey->name = NULL;
324}
325
326static void
roman874fed12023-05-25 10:20:01 +0200327nc_server_config_del_auth_client_pubkey_pub_base64(struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100328{
329 free(pubkey->pub_base64);
330 pubkey->pub_base64 = NULL;
331}
332
333static void
roman874fed12023-05-25 10:20:01 +0200334nc_server_config_del_auth_client_password(struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100335{
336 free(auth_client->password);
337 auth_client->password = NULL;
338}
339
340static void
roman874fed12023-05-25 10:20:01 +0200341nc_server_config_del_hostkey_algs(struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100342{
343 free(opts->hostkey_algs);
344 opts->hostkey_algs = NULL;
345}
346
347static void
roman874fed12023-05-25 10:20:01 +0200348nc_server_config_del_kex_algs(struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100349{
350 free(opts->kex_algs);
351 opts->kex_algs = NULL;
352}
353
354static void
roman874fed12023-05-25 10:20:01 +0200355nc_server_config_del_encryption_algs(struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100356{
357 free(opts->encryption_algs);
358 opts->encryption_algs = NULL;
359}
360
361static void
roman874fed12023-05-25 10:20:01 +0200362nc_server_config_del_mac_algs(struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100363{
364 free(opts->mac_algs);
365 opts->mac_algs = NULL;
366}
367
368static void
roman874fed12023-05-25 10:20:01 +0200369nc_server_config_del_hostkey(struct nc_server_ssh_opts *opts, struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +0100370{
roman874fed12023-05-25 10:20:01 +0200371 assert(hostkey->store == NC_STORE_LOCAL || hostkey->store == NC_STORE_KEYSTORE);
romanc1d2b092023-02-02 08:58:27 +0100372
roman874fed12023-05-25 10:20:01 +0200373 if (hostkey->store == NC_STORE_LOCAL) {
374 nc_server_config_del_public_key(hostkey);
375 nc_server_config_del_private_key(hostkey);
romanc1d2b092023-02-02 08:58:27 +0100376 }
377
roman874fed12023-05-25 10:20:01 +0200378 nc_server_config_del_hostkey_name(hostkey);
romanc1d2b092023-02-02 08:58:27 +0100379 opts->hostkey_count--;
380 if (!opts->hostkey_count) {
381 free(opts->hostkeys);
382 opts->hostkeys = NULL;
383 }
384}
385
386static void
roman874fed12023-05-25 10:20:01 +0200387nc_server_config_del_auth_client_pubkey(struct nc_client_auth *auth_client, struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +0100388{
roman874fed12023-05-25 10:20:01 +0200389 nc_server_config_del_auth_client_pubkey_name(pubkey);
390 nc_server_config_del_auth_client_pubkey_pub_base64(pubkey);
romanc1d2b092023-02-02 08:58:27 +0100391
392 auth_client->pubkey_count--;
393 if (!auth_client->pubkey_count) {
394 free(auth_client->pubkeys);
395 auth_client->pubkeys = NULL;
396 }
397}
398
399static void
roman874fed12023-05-25 10:20:01 +0200400nc_server_config_del_auth_client(struct nc_server_ssh_opts *opts, struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +0100401{
402 uint16_t i, pubkey_count;
403
roman874fed12023-05-25 10:20:01 +0200404 if (auth_client->store == NC_STORE_LOCAL) {
romanc1d2b092023-02-02 08:58:27 +0100405 pubkey_count = auth_client->pubkey_count;
406 for (i = 0; i < pubkey_count; i++) {
roman874fed12023-05-25 10:20:01 +0200407 nc_server_config_del_auth_client_pubkey(auth_client, &auth_client->pubkeys[i]);
romanc1d2b092023-02-02 08:58:27 +0100408 }
romanc1d2b092023-02-02 08:58:27 +0100409 }
410
roman874fed12023-05-25 10:20:01 +0200411 nc_server_config_del_auth_client_password(auth_client);
412 nc_server_config_del_auth_client_pam_name(auth_client);
413 nc_server_config_del_auth_client_pam_dir(auth_client);
414 nc_server_config_del_auth_client_username(auth_client);
romanc1d2b092023-02-02 08:58:27 +0100415
416 opts->client_count--;
417 if (!opts->client_count) {
418 free(opts->auth_clients);
419 opts->auth_clients = NULL;
420 }
421}
422
423static void
roman874fed12023-05-25 10:20:01 +0200424nc_server_config_del_ssh(struct nc_bind *bind, struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100425{
426 uint16_t i, hostkey_count, client_count;
427
roman874fed12023-05-25 10:20:01 +0200428 nc_server_config_del_local_address(bind);
romanc1d2b092023-02-02 08:58:27 +0100429 if (bind->sock > -1) {
430 close(bind->sock);
431 }
432
433 /* store in variable because it gets decremented in the function call */
434 hostkey_count = opts->hostkey_count;
435 for (i = 0; i < hostkey_count; i++) {
roman874fed12023-05-25 10:20:01 +0200436 nc_server_config_del_hostkey(opts, &opts->hostkeys[i]);
romanc1d2b092023-02-02 08:58:27 +0100437 }
438
439 client_count = opts->client_count;
440 for (i = 0; i < client_count; i++) {
roman874fed12023-05-25 10:20:01 +0200441 nc_server_config_del_auth_client(opts, &opts->auth_clients[i]);
romanc1d2b092023-02-02 08:58:27 +0100442 }
443
roman874fed12023-05-25 10:20:01 +0200444 nc_server_config_del_hostkey_algs(opts);
445 nc_server_config_del_kex_algs(opts);
446 nc_server_config_del_encryption_algs(opts);
447 nc_server_config_del_mac_algs(opts);
romanc1d2b092023-02-02 08:58:27 +0100448
449 free(opts);
450 opts = NULL;
451}
452
453void
roman874fed12023-05-25 10:20:01 +0200454nc_server_config_del_endpt_ssh(struct nc_endpt *endpt, struct nc_bind *bind)
romanc1d2b092023-02-02 08:58:27 +0100455{
roman874fed12023-05-25 10:20:01 +0200456 nc_server_config_del_endpt_name(endpt);
roman0bbc19c2023-05-26 09:59:09 +0200457 nc_server_config_del_endpt_reference(endpt);
roman874fed12023-05-25 10:20:01 +0200458 nc_server_config_del_ssh(bind, endpt->opts.ssh);
romanc1d2b092023-02-02 08:58:27 +0100459
460 server_opts.endpt_count--;
461 if (!server_opts.endpt_count) {
462 free(server_opts.endpts);
463 free(server_opts.binds);
464 server_opts.endpts = NULL;
465 server_opts.binds = NULL;
466 }
467}
468
roman45cec4e2023-02-17 10:21:39 +0100469void
roman874fed12023-05-25 10:20:01 +0200470nc_server_config_del_unix_socket(struct nc_bind *bind, struct nc_server_unix_opts *opts)
roman83683fb2023-02-24 09:15:23 +0100471{
472 if (bind->sock > -1) {
473 close(bind->sock);
474 }
475
476 free(bind->address);
477 free(opts->address);
478
479 free(opts);
480 opts = NULL;
481}
482
483void
roman874fed12023-05-25 10:20:01 +0200484nc_server_config_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *bind)
roman83683fb2023-02-24 09:15:23 +0100485{
roman874fed12023-05-25 10:20:01 +0200486 nc_server_config_del_endpt_name(endpt);
487 nc_server_config_del_unix_socket(bind, endpt->opts.unixsock);
roman83683fb2023-02-24 09:15:23 +0100488
489 server_opts.endpt_count--;
490 if (!server_opts.endpt_count) {
491 free(server_opts.endpts);
492 free(server_opts.binds);
493 server_opts.endpts = NULL;
494 server_opts.binds = NULL;
495 }
496}
497
romanc1d2b092023-02-02 08:58:27 +0100498/* presence container */
499int
romanf02273a2023-05-25 09:44:11 +0200500nc_server_config_listen(struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100501{
roman0bbc19c2023-05-26 09:59:09 +0200502 uint16_t i, endpt_count;
romanc1d2b092023-02-02 08:58:27 +0100503
romanf02273a2023-05-25 09:44:11 +0200504 (void) node;
505
romanc1d2b092023-02-02 08:58:27 +0100506 assert(op == NC_OP_CREATE || op == NC_OP_DELETE);
507
508 if (op == NC_OP_DELETE) {
roman0bbc19c2023-05-26 09:59:09 +0200509 endpt_count = server_opts.endpt_count;
510 for (i = 0; i < endpt_count; i++) {
roman456f92d2023-04-28 10:28:12 +0200511 switch (server_opts.endpts[i].ti) {
512#ifdef NC_ENABLED_SSH
513 case NC_TI_LIBSSH:
roman874fed12023-05-25 10:20:01 +0200514 nc_server_config_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]);
roman456f92d2023-04-28 10:28:12 +0200515 break;
516#endif
517#ifdef NC_ENABLED_TLS
518 case NC_TI_OPENSSL:
roman83683fb2023-02-24 09:15:23 +0100519 /* todo */
roman456f92d2023-04-28 10:28:12 +0200520 break;
521#endif
522 case NC_TI_UNIX:
roman874fed12023-05-25 10:20:01 +0200523 nc_server_config_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]);
roman456f92d2023-04-28 10:28:12 +0200524 break;
525 case NC_TI_NONE:
526 case NC_TI_FD:
527 ERRINT;
528 return 1;
roman83683fb2023-02-24 09:15:23 +0100529 }
romanc1d2b092023-02-02 08:58:27 +0100530 }
531 }
532
533 return 0;
534}
535
536/* default leaf */
537static int
romane028ef92023-02-24 16:33:08 +0100538nc_server_config_idle_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100539{
540 assert(!strcmp(LYD_NAME(node), "idle-timeout"));
541
542 if (equal_parent_name(node, 1, "listen")) {
543 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
544 server_opts.idle_timeout = strtoul(lyd_get_value(node), NULL, 10);
545 } else {
546 /* default value */
547 server_opts.idle_timeout = 3600;
548 }
549 }
550
551 return 0;
552}
553
554static int
roman874fed12023-05-25 10:20:01 +0200555nc_server_config_create_bind(void)
romanc1d2b092023-02-02 08:58:27 +0100556{
557 int ret = 0;
558 void *tmp;
559
560 tmp = realloc(server_opts.binds, (server_opts.endpt_count + 1) * sizeof *server_opts.binds);
561 if (!tmp) {
562 ERRMEM;
563 ret = 1;
564 goto cleanup;
565 }
566 server_opts.binds = tmp;
567 memset(&server_opts.binds[server_opts.endpt_count], 0, sizeof *server_opts.binds);
568
569 server_opts.binds[server_opts.endpt_count].sock = -1;
570
571cleanup:
572 return ret;
573}
574
575static int
roman874fed12023-05-25 10:20:01 +0200576nc_server_config_create_endpoint(const struct lyd_node *node)
romanc1d2b092023-02-02 08:58:27 +0100577{
roman874fed12023-05-25 10:20:01 +0200578 if (nc_server_config_create_bind()) {
romanf02273a2023-05-25 09:44:11 +0200579 return 1;
romanc1d2b092023-02-02 08:58:27 +0100580 }
romanc1d2b092023-02-02 08:58:27 +0100581
582 node = lyd_child(node);
583 assert(!strcmp(LYD_NAME(node), "name"));
584
romanf02273a2023-05-25 09:44:11 +0200585 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 +0100586}
587
588/* list */
589static int
romane028ef92023-02-24 16:33:08 +0100590nc_server_config_endpoint(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100591{
592 int ret = 0;
593 struct nc_endpt *endpt;
594 struct nc_bind *bind;
595
596 assert(!strcmp(LYD_NAME(node), "endpoint"));
597
598 if (op == NC_OP_CREATE) {
roman874fed12023-05-25 10:20:01 +0200599 ret = nc_server_config_create_endpoint(node);
romanc1d2b092023-02-02 08:58:27 +0100600 if (ret) {
601 goto cleanup;
602 }
603 } else if (op == NC_OP_DELETE) {
604 /* free all children */
romanf02273a2023-05-25 09:44:11 +0200605 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +0100606 ret = 1;
607 goto cleanup;
608 }
roman874fed12023-05-25 10:20:01 +0200609 nc_server_config_del_endpt_ssh(endpt, bind);
romanc1d2b092023-02-02 08:58:27 +0100610 }
611
612cleanup:
613 return ret;
614}
615
616static int
roman874fed12023-05-25 10:20:01 +0200617nc_server_config_create_ssh(struct nc_endpt *endpt)
romanc1d2b092023-02-02 08:58:27 +0100618{
619 endpt->ti = NC_TI_LIBSSH;
620 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
621 if (!endpt->opts.ssh) {
622 ERRMEM;
623 return 1;
624 }
625
626 return 0;
627}
628
629/* NP container */
630static int
romane028ef92023-02-24 16:33:08 +0100631nc_server_config_ssh(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100632{
633 struct nc_endpt *endpt;
634 struct nc_bind *bind;
635 int ret = 0;
636
637 assert(!strcmp(LYD_NAME(node), "ssh"));
638
romanf02273a2023-05-25 09:44:11 +0200639 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +0100640 ret = 1;
641 goto cleanup;
642 }
643
644 if (op == NC_OP_CREATE) {
roman874fed12023-05-25 10:20:01 +0200645 ret = nc_server_config_create_ssh(endpt);
romanc1d2b092023-02-02 08:58:27 +0100646 if (ret) {
647 goto cleanup;
648 }
649 } else if (op == NC_OP_DELETE) {
roman874fed12023-05-25 10:20:01 +0200650 nc_server_config_del_ssh(bind, endpt->opts.ssh);
romanc1d2b092023-02-02 08:58:27 +0100651 }
652
653cleanup:
654 return ret;
655}
656
657static int
658nc_server_config_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port)
659{
660 int sock = -1, set_addr, ret = 0;
661
roman83683fb2023-02-24 09:15:23 +0100662 assert((address && !port) || (!address && port) || (endpt->ti == NC_TI_UNIX));
romanc1d2b092023-02-02 08:58:27 +0100663
664 if (address) {
665 set_addr = 1;
666 } else {
667 set_addr = 0;
668 }
669
670 if (set_addr) {
671 port = bind->port;
672 } else {
673 address = bind->address;
674 }
675
romanc1d2b092023-02-02 08:58:27 +0100676 /* we have all the information we need to create a listening socket */
roman83683fb2023-02-24 09:15:23 +0100677 if ((address && port) || (endpt->ti == NC_TI_UNIX)) {
romanc1d2b092023-02-02 08:58:27 +0100678 /* create new socket, close the old one */
roman83683fb2023-02-24 09:15:23 +0100679 if (endpt->ti == NC_TI_UNIX) {
680 sock = nc_sock_listen_unix(endpt->opts.unixsock);
681 } else {
682 sock = nc_sock_listen_inet(address, port, &endpt->ka);
683 }
684
romanc1d2b092023-02-02 08:58:27 +0100685 if (sock == -1) {
686 ret = 1;
687 goto cleanup;
688 }
689
690 if (bind->sock > -1) {
691 close(bind->sock);
692 }
693 bind->sock = sock;
694 }
695
696 if (sock > -1) {
697 switch (endpt->ti) {
roman83683fb2023-02-24 09:15:23 +0100698 case NC_TI_UNIX:
699 VRB(NULL, "Listening on %s for UNIX connections.", endpt->opts.unixsock->address);
700 break;
romanc1d2b092023-02-02 08:58:27 +0100701#ifdef NC_ENABLED_SSH
702 case NC_TI_LIBSSH:
703 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
704 break;
705#endif
706#ifdef NC_ENABLED_TLS
707 case NC_TI_OPENSSL:
708 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
709 break;
710#endif
711 default:
712 ERRINT;
713 ret = 1;
714 break;
715 }
716 }
717
718cleanup:
719 return ret;
720}
721
722/* mandatory leaf */
723static int
romane028ef92023-02-24 16:33:08 +0100724nc_server_config_local_address(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100725{
726 struct nc_endpt *endpt;
727 struct nc_bind *bind;
728 int ret = 0;
729
730 (void) op;
731
732 assert(!strcmp(LYD_NAME(node), "local-address"));
733
734 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +0200735 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +0100736 ret = 1;
737 goto cleanup;
738 }
739
roman874fed12023-05-25 10:20:01 +0200740 nc_server_config_del_local_address(bind);
romanc1d2b092023-02-02 08:58:27 +0100741 bind->address = strdup(lyd_get_value(node));
742 if (!bind->address) {
743 ERRMEM;
744 ret = 1;
745 goto cleanup;
746 }
747
748 ret = nc_server_config_set_address_port(endpt, bind, lyd_get_value(node), 0);
749 if (ret) {
750 goto cleanup;
751 }
752 }
753
754cleanup:
755 return ret;
756}
757
758/* leaf with default value */
759static int
romane028ef92023-02-24 16:33:08 +0100760nc_server_config_local_port(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100761{
762 struct nc_endpt *endpt;
763 struct nc_bind *bind;
764 int ret = 0;
765
766 assert(!strcmp(LYD_NAME(node), "local-port"));
767
768 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +0200769 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +0100770 ret = 1;
771 goto cleanup;
772 }
773
774 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
775 bind->port = strtoul(lyd_get_value(node), NULL, 10);
776 } else {
777 /* delete -> set to default */
778 bind->port = 0;
779 }
780
781 ret = nc_server_config_set_address_port(endpt, bind, NULL, bind->port);
782 if (ret) {
783 goto cleanup;
784 }
785 }
786
787cleanup:
788 return ret;
789}
790
791/* P container */
792static int
romane028ef92023-02-24 16:33:08 +0100793nc_server_config_keepalives(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100794{
795 struct nc_endpt *endpt;
796 struct nc_bind *bind;
797 int ret = 0;
798
799 assert(!strcmp(LYD_NAME(node), "keepalives"));
800
801 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +0200802 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +0100803 ret = 1;
804 goto cleanup;
805 }
806
807 if (op == NC_OP_CREATE) {
808 endpt->ka.enabled = 1;
809 } else {
810 endpt->ka.enabled = 0;
811 }
812 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
813 if (ret) {
814 goto cleanup;
815 }
816 }
817
818cleanup:
819 return ret;
820}
821
822/* mandatory leaf */
823static int
romane028ef92023-02-24 16:33:08 +0100824nc_server_config_idle_time(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100825{
826 struct nc_endpt *endpt;
827 struct nc_bind *bind;
828 int ret = 0;
829
830 assert(!strcmp(LYD_NAME(node), "idle-time"));
831
832 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +0200833 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +0100834 ret = 1;
835 goto cleanup;
836 }
837
838 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
839 endpt->ka.idle_time = strtoul(lyd_get_value(node), NULL, 10);
840 } else {
841 endpt->ka.idle_time = 0;
842 }
843 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
844 if (ret) {
845 goto cleanup;
846 }
847 }
848
849cleanup:
850 return ret;
851}
852
853/* mandatory leaf */
854static int
romane028ef92023-02-24 16:33:08 +0100855nc_server_config_max_probes(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100856{
857 struct nc_endpt *endpt;
858 struct nc_bind *bind;
859 int ret = 0;
860
861 assert(!strcmp(LYD_NAME(node), "max-probes"));
862
863 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +0200864 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +0100865 ret = 1;
866 goto cleanup;
867 }
868
869 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
870 endpt->ka.max_probes = strtoul(lyd_get_value(node), NULL, 10);
871 } else {
872 endpt->ka.max_probes = 0;
873 }
874 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
875 if (ret) {
876 goto cleanup;
877 }
878 }
879
880cleanup:
881 return ret;
882}
883
884/* mandatory leaf */
885static int
romane028ef92023-02-24 16:33:08 +0100886nc_server_config_probe_interval(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100887{
888 struct nc_endpt *endpt;
889 struct nc_bind *bind;
890 int ret = 0;
891
892 assert(!strcmp(LYD_NAME(node), "probe-interval"));
893
894 if (equal_parent_name(node, 4, "listen")) {
romanf02273a2023-05-25 09:44:11 +0200895 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
romanc1d2b092023-02-02 08:58:27 +0100896 ret = 1;
897 goto cleanup;
898 }
899
900 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
901 endpt->ka.probe_interval = strtoul(lyd_get_value(node), NULL, 10);
902 } else {
903 endpt->ka.probe_interval = 0;
904 }
905 ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka);
906 if (ret) {
907 goto cleanup;
908 }
909 }
910
911cleanup:
912 return ret;
913}
914
915static int
roman874fed12023-05-25 10:20:01 +0200916nc_server_config_create_host_key(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +0100917{
romanf02273a2023-05-25 09:44:11 +0200918 node = lyd_child(node);
919 assert(!strcmp(LYD_NAME(node), "name"));
romanc1d2b092023-02-02 08:58:27 +0100920
romanf02273a2023-05-25 09:44:11 +0200921 return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->hostkeys, sizeof *opts->hostkeys, &opts->hostkey_count);
romanc1d2b092023-02-02 08:58:27 +0100922}
923
924/* list */
925static int
romane028ef92023-02-24 16:33:08 +0100926nc_server_config_host_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100927{
928 struct nc_endpt *endpt;
929 struct nc_hostkey *hostkey;
930 int ret = 0;
931
932 assert(!strcmp(LYD_NAME(node), "host-key"));
933
934 if ((equal_parent_name(node, 1, "server-identity")) && (equal_parent_name(node, 5, "listen"))) {
romanf02273a2023-05-25 09:44:11 +0200935 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +0100936 ret = 1;
937 goto cleanup;
938 }
939
940 if (op == NC_OP_CREATE) {
roman874fed12023-05-25 10:20:01 +0200941 ret = nc_server_config_create_host_key(node, endpt->opts.ssh);
romanc1d2b092023-02-02 08:58:27 +0100942 if (ret) {
943 goto cleanup;
944 }
945 } else if (op == NC_OP_DELETE) {
romanf02273a2023-05-25 09:44:11 +0200946 if (nc_server_config_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +0100947 ret = 1;
948 goto cleanup;
949 }
950
roman874fed12023-05-25 10:20:01 +0200951 nc_server_config_del_hostkey(endpt->opts.ssh, hostkey);
romanc1d2b092023-02-02 08:58:27 +0100952 }
953 } else if (equal_parent_name(node, 1, "transport-params")) {
954 /* just a container with the name host-key, nothing to be done */
955 goto cleanup;
956 } else {
957 ERRINT;
958 ret = 1;
959 goto cleanup;
960 }
961
962cleanup:
963 return ret;
964}
965
966/* mandatory leaf */
romane028ef92023-02-24 16:33:08 +0100967static int
968nc_server_config_public_key_format(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +0100969{
970 const char *format;
971 struct nc_endpt *endpt;
972 struct nc_client_auth *auth_client;
roman8edee342023-03-31 13:25:48 +0200973 struct nc_public_key *pubkey;
romanc1d2b092023-02-02 08:58:27 +0100974 struct nc_hostkey *hostkey;
975 int ret = 0;
976
977 assert(!strcmp(LYD_NAME(node), "public-key-format"));
978
979 format = ((struct lyd_node_term *)node)->value.ident->name;
980
981 if ((equal_parent_name(node, 6, "client-authentication")) && (equal_parent_name(node, 10, "listen"))) {
romanf02273a2023-05-25 09:44:11 +0200982 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +0100983 ret = 1;
984 goto cleanup;
985 }
986
romanf02273a2023-05-25 09:44:11 +0200987 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +0100988 ret = 1;
989 goto cleanup;
990 }
991
romanf02273a2023-05-25 09:44:11 +0200992 if (nc_server_config_get_pubkey(node, auth_client, &pubkey)) {
romanc1d2b092023-02-02 08:58:27 +0100993 ret = 1;
994 goto cleanup;
995 }
996
997 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
998 if (!strcmp(format, "ssh-public-key-format")) {
romanc1d2b092023-02-02 08:58:27 +0100999 pubkey->pubkey_type = NC_SSH_PUBKEY_SSH2;
romanf02273a2023-05-25 09:44:11 +02001000 } else if (!strcmp(format, "subject-public-key-info-format")) {
1001 pubkey->pubkey_type = NC_SSH_PUBKEY_X509;
romanc1d2b092023-02-02 08:58:27 +01001002 } else {
1003 ERR(NULL, "Public key format (%s) not supported.", format);
1004 }
1005 }
1006 } else if ((equal_parent_name(node, 5, "server-identity")) && (equal_parent_name(node, 11, "listen"))) {
romanf02273a2023-05-25 09:44:11 +02001007 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001008 ret = 1;
1009 goto cleanup;
1010 }
1011
romanf02273a2023-05-25 09:44:11 +02001012 if (nc_server_config_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01001013 ret = 1;
1014 goto cleanup;
1015 }
1016
1017 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1018 if (!strcmp(format, "ssh-public-key-format")) {
roman8edee342023-03-31 13:25:48 +02001019 hostkey->key.pubkey_type = NC_SSH_PUBKEY_SSH2;
romane028ef92023-02-24 16:33:08 +01001020 } else if (!strcmp(format, "subject-public-key-info-format")) {
roman8edee342023-03-31 13:25:48 +02001021 hostkey->key.pubkey_type = NC_SSH_PUBKEY_X509;
romanc1d2b092023-02-02 08:58:27 +01001022 } else {
1023 ERR(NULL, "Public key format (%s) not supported.", format);
1024 }
1025 }
1026 }
1027
1028cleanup:
1029 return ret;
1030}
1031
1032/* leaf */
romane028ef92023-02-24 16:33:08 +01001033static int
1034nc_server_config_private_key_format(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001035{
1036 const char *format;
1037 struct nc_endpt *endpt;
1038 struct nc_hostkey *hostkey;
1039 int ret = 0;
1040
1041 assert(!strcmp(LYD_NAME(node), "private-key-format"));
1042
romanf02273a2023-05-25 09:44:11 +02001043 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001044 ret = 1;
1045 goto cleanup;
1046 }
1047
romanf02273a2023-05-25 09:44:11 +02001048 if (nc_server_config_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01001049 ret = 1;
1050 goto cleanup;
1051 }
1052
1053 format = ((struct lyd_node_term *)node)->value.ident->name;
1054 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1055 if (!strcmp(format, "rsa-private-key-format")) {
roman466719d2023-05-05 16:14:37 +02001056 hostkey->key.privkey_type = NC_PRIVKEY_FORMAT_RSA;
romanc1d2b092023-02-02 08:58:27 +01001057 } else if (!strcmp(format, "ec-private-key-format")) {
roman466719d2023-05-05 16:14:37 +02001058 hostkey->key.privkey_type = NC_PRIVKEY_FORMAT_EC;
1059 } else if (!strcmp(format, "subject-private-key-info-format")) {
1060 hostkey->key.privkey_type = NC_PRIVKEY_FORMAT_PKCS8;
1061 } else if (!strcmp(format, "openssh-private-key-format")) {
1062 hostkey->key.privkey_type = NC_PRIVKEY_FORMAT_OPENSSH;
romanc1d2b092023-02-02 08:58:27 +01001063 } else {
1064 ERR(NULL, "Private key format (%s) not supported.", format);
1065 }
1066 }
1067
1068cleanup:
1069 return ret;
1070}
1071
1072static int
roman874fed12023-05-25 10:20:01 +02001073nc_server_config_replace_cleartext_private_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +01001074{
roman874fed12023-05-25 10:20:01 +02001075 nc_server_config_del_private_key(hostkey);
roman8edee342023-03-31 13:25:48 +02001076 hostkey->key.priv_base64 = strdup(lyd_get_value(node));
1077 if (!hostkey->key.priv_base64) {
romanc1d2b092023-02-02 08:58:27 +01001078 ERRMEM;
1079 return 1;
1080 }
1081
1082 return 0;
1083}
1084
1085static int
romane028ef92023-02-24 16:33:08 +01001086nc_server_config_cleartext_private_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001087{
1088 struct nc_endpt *endpt;
1089 struct nc_hostkey *hostkey;
1090 int ret = 0;
1091
1092 assert(!strcmp(LYD_NAME(node), "cleartext-private-key"));
1093
1094 if ((equal_parent_name(node, 6, "ssh")) && (equal_parent_name(node, 8, "listen"))) {
romanf02273a2023-05-25 09:44:11 +02001095 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001096 ret = 1;
1097 goto cleanup;
1098 }
romanf02273a2023-05-25 09:44:11 +02001099 if (nc_server_config_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01001100 ret = 1;
1101 goto cleanup;
1102 }
1103
1104 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman874fed12023-05-25 10:20:01 +02001105 ret = nc_server_config_replace_cleartext_private_key(node, hostkey);
romanc1d2b092023-02-02 08:58:27 +01001106 if (ret) {
1107 goto cleanup;
1108 }
1109 } else {
roman874fed12023-05-25 10:20:01 +02001110 nc_server_config_del_private_key(hostkey);
romanc1d2b092023-02-02 08:58:27 +01001111 }
1112 }
1113
1114cleanup:
1115 return ret;
1116}
1117
1118static int
roman874fed12023-05-25 10:20:01 +02001119nc_server_config_create_keystore_reference(const struct lyd_node *node, struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +01001120{
1121 uint16_t i;
roman45cec4e2023-02-17 10:21:39 +01001122 struct nc_keystore *ks = &server_opts.keystore;
romanc1d2b092023-02-02 08:58:27 +01001123
1124 /* lookup name */
roman45cec4e2023-02-17 10:21:39 +01001125 for (i = 0; i < ks->asym_key_count; i++) {
1126 if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
romanc1d2b092023-02-02 08:58:27 +01001127 break;
1128 }
1129 }
1130
roman45cec4e2023-02-17 10:21:39 +01001131 if (i == ks->asym_key_count) {
1132 ERR(NULL, "Keystore \"%s\" not found.", lyd_get_value(node));
romanc1d2b092023-02-02 08:58:27 +01001133 return 1;
1134 }
1135
roman45cec4e2023-02-17 10:21:39 +01001136 hostkey->ks_ref = &ks->asym_keys[i];
romanc1d2b092023-02-02 08:58:27 +01001137
1138 return 0;
1139}
1140
1141/* leaf */
1142static int
romane028ef92023-02-24 16:33:08 +01001143nc_server_config_keystore_reference(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001144{
1145 struct nc_endpt *endpt;
1146 struct nc_hostkey *hostkey;
1147 int ret = 0;
1148
1149 assert(!strcmp(LYD_NAME(node), "keystore-reference"));
1150
roman45cec4e2023-02-17 10:21:39 +01001151 if ((equal_parent_name(node, 3, "server-identity")) && (equal_parent_name(node, 7, "listen"))) {
romanf02273a2023-05-25 09:44:11 +02001152 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001153 ret = 1;
1154 goto cleanup;
1155 }
romanf02273a2023-05-25 09:44:11 +02001156 if (nc_server_config_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01001157 ret = 1;
1158 goto cleanup;
1159 }
1160
1161 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanf02273a2023-05-25 09:44:11 +02001162 /* set to keystore */
roman874fed12023-05-25 10:20:01 +02001163 hostkey->store = NC_STORE_KEYSTORE;
romanf02273a2023-05-25 09:44:11 +02001164
roman874fed12023-05-25 10:20:01 +02001165 ret = nc_server_config_create_keystore_reference(node, hostkey);
romanc1d2b092023-02-02 08:58:27 +01001166 if (ret) {
1167 goto cleanup;
1168 }
1169 } else {
roman45cec4e2023-02-17 10:21:39 +01001170 hostkey->ks_ref = NULL;
romanc1d2b092023-02-02 08:58:27 +01001171 }
1172 }
1173
1174cleanup:
1175 return ret;
1176}
1177
1178static int
roman874fed12023-05-25 10:20:01 +02001179nc_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 +01001180{
romanc1d2b092023-02-02 08:58:27 +01001181 assert(!strcmp(LYD_NAME(node), "public-key"));
1182
romanc1d2b092023-02-02 08:58:27 +01001183 node = lyd_child(node);
1184 assert(!strcmp(LYD_NAME(node), "name"));
1185
romanf02273a2023-05-25 09:44:11 +02001186 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 +01001187}
1188
1189static int
roman874fed12023-05-25 10:20:01 +02001190nc_server_config_replace_auth_key_public_key_leaf(const struct lyd_node *node, struct nc_public_key *pubkey)
romanc1d2b092023-02-02 08:58:27 +01001191{
roman874fed12023-05-25 10:20:01 +02001192 nc_server_config_del_auth_client_pubkey_pub_base64(pubkey);
romanc1d2b092023-02-02 08:58:27 +01001193
1194 pubkey->pub_base64 = strdup(lyd_get_value(node));
1195 if (!pubkey->pub_base64) {
1196 ERRMEM;
1197 return 1;
1198 }
1199
1200 return 0;
1201}
1202
1203static int
roman874fed12023-05-25 10:20:01 +02001204nc_server_config_replace_host_key_public_key(const struct lyd_node *node, struct nc_hostkey *hostkey)
romanc1d2b092023-02-02 08:58:27 +01001205{
roman874fed12023-05-25 10:20:01 +02001206 nc_server_config_del_public_key(hostkey);
romanc1d2b092023-02-02 08:58:27 +01001207
roman8edee342023-03-31 13:25:48 +02001208 hostkey->key.pub_base64 = strdup(lyd_get_value(node));
1209 if (!hostkey->key.pub_base64) {
romanc1d2b092023-02-02 08:58:27 +01001210 ERRMEM;
1211 return 1;
1212 }
1213
1214 return 0;
1215}
1216
1217static int
romane028ef92023-02-24 16:33:08 +01001218nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001219{
1220 struct nc_endpt *endpt;
1221 struct nc_hostkey *hostkey;
1222 struct nc_client_auth *auth_client;
roman8edee342023-03-31 13:25:48 +02001223 struct nc_public_key *pubkey;
romanc1d2b092023-02-02 08:58:27 +01001224 int ret = 0;
1225
1226 assert(!strcmp(LYD_NAME(node), "public-key"));
1227
1228 if ((equal_parent_name(node, 3, "host-key")) && (equal_parent_name(node, 8, "listen"))) {
1229 /* server's public-key, mandatory leaf */
romanf02273a2023-05-25 09:44:11 +02001230 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001231 ret = 1;
1232 goto cleanup;
1233 }
1234
romanf02273a2023-05-25 09:44:11 +02001235 if (nc_server_config_get_hostkey(node, endpt->opts.ssh, &hostkey)) {
romanc1d2b092023-02-02 08:58:27 +01001236 ret = 1;
1237 goto cleanup;
1238 }
1239
1240 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanf02273a2023-05-25 09:44:11 +02001241 /* set to local */
roman874fed12023-05-25 10:20:01 +02001242 hostkey->store = NC_STORE_LOCAL;
romanf02273a2023-05-25 09:44:11 +02001243
roman874fed12023-05-25 10:20:01 +02001244 ret = nc_server_config_replace_host_key_public_key(node, hostkey);
romanc1d2b092023-02-02 08:58:27 +01001245 if (ret) {
1246 goto cleanup;
1247 }
1248 }
1249 } else if ((equal_parent_name(node, 5, "client-authentication")) && (equal_parent_name(node, 9, "listen"))) {
1250 /* client auth pubkeys, list */
romanf02273a2023-05-25 09:44:11 +02001251 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001252 ret = 1;
1253 goto cleanup;
1254 }
1255
romanf02273a2023-05-25 09:44:11 +02001256 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01001257 ret = 1;
1258 goto cleanup;
1259 }
1260
1261 if (op == NC_OP_CREATE) {
romanf02273a2023-05-25 09:44:11 +02001262 /* set to local */
roman874fed12023-05-25 10:20:01 +02001263 auth_client->store = NC_STORE_LOCAL;
romanf02273a2023-05-25 09:44:11 +02001264
roman874fed12023-05-25 10:20:01 +02001265 ret = nc_server_config_create_auth_key_public_key_list(node, auth_client);
romanc1d2b092023-02-02 08:58:27 +01001266 if (ret) {
1267 goto cleanup;
1268 }
1269 } else if (op == NC_OP_DELETE) {
romanf02273a2023-05-25 09:44:11 +02001270 if (nc_server_config_get_pubkey(node, auth_client, &pubkey)) {
romanc1d2b092023-02-02 08:58:27 +01001271 ret = 1;
1272 goto cleanup;
1273 }
1274
roman874fed12023-05-25 10:20:01 +02001275 nc_server_config_del_auth_client_pubkey(auth_client, pubkey);
romanc1d2b092023-02-02 08:58:27 +01001276 }
1277 } else if ((equal_parent_name(node, 6, "client-authentication")) && (equal_parent_name(node, 10, "listen"))) {
1278 /* client auth pubkey, leaf */
romanf02273a2023-05-25 09:44:11 +02001279 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001280 ret = 1;
1281 goto cleanup;
1282 }
1283
romanf02273a2023-05-25 09:44:11 +02001284 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01001285 ret = 1;
1286 goto cleanup;
1287 }
1288
romanf02273a2023-05-25 09:44:11 +02001289 if (nc_server_config_get_pubkey(node, auth_client, &pubkey)) {
romanc1d2b092023-02-02 08:58:27 +01001290 ret = 1;
1291 goto cleanup;
1292 }
1293
1294 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman874fed12023-05-25 10:20:01 +02001295 ret = nc_server_config_replace_auth_key_public_key_leaf(node, pubkey);
romanc1d2b092023-02-02 08:58:27 +01001296 if (ret) {
1297 goto cleanup;
1298 }
1299 } else {
roman874fed12023-05-25 10:20:01 +02001300 nc_server_config_del_auth_client_pubkey_pub_base64(pubkey);
romanc1d2b092023-02-02 08:58:27 +01001301 }
1302 }
1303
1304cleanup:
1305 return ret;
1306}
1307
1308static int
roman874fed12023-05-25 10:20:01 +02001309nc_server_config_create_user(const struct lyd_node *node, struct nc_server_ssh_opts *opts)
romanc1d2b092023-02-02 08:58:27 +01001310{
romanf02273a2023-05-25 09:44:11 +02001311 node = lyd_child(node);
1312 assert(!strcmp(LYD_NAME(node), "name"));
romanc1d2b092023-02-02 08:58:27 +01001313
romanf02273a2023-05-25 09:44:11 +02001314 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 +01001315}
1316
1317/* list */
1318static int
romane028ef92023-02-24 16:33:08 +01001319nc_server_config_user(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001320{
1321 struct nc_endpt *endpt;
1322 struct nc_client_auth *auth_client;
1323 int ret = 0;
1324
1325 assert(!strcmp(LYD_NAME(node), "user"));
1326
1327 if (equal_parent_name(node, 6, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001328 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001329 ret = 1;
1330 goto cleanup;
1331 }
1332
1333 if (op == NC_OP_CREATE) {
roman874fed12023-05-25 10:20:01 +02001334 ret = nc_server_config_create_user(node, endpt->opts.ssh);
romanc1d2b092023-02-02 08:58:27 +01001335 if (ret) {
1336 goto cleanup;
1337 }
1338 } else if (op == NC_OP_DELETE) {
romanf02273a2023-05-25 09:44:11 +02001339 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01001340 ret = 1;
1341 goto cleanup;
1342 }
1343
roman874fed12023-05-25 10:20:01 +02001344 nc_server_config_del_auth_client(endpt->opts.ssh, auth_client);
romanc1d2b092023-02-02 08:58:27 +01001345 }
1346 }
1347
1348cleanup:
1349 return ret;
1350}
1351
1352static int
romane028ef92023-02-24 16:33:08 +01001353nc_server_config_auth_attempts(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001354{
1355 struct nc_endpt *endpt;
1356 int ret = 0;
1357
1358 assert(!strcmp(LYD_NAME(node), "auth-attempts"));
1359
1360 if (equal_parent_name(node, 5, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001361 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001362 ret = 1;
1363 goto cleanup;
1364 }
1365
1366 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1367 endpt->opts.ssh->auth_attempts = strtoul(lyd_get_value(node), NULL, 10);
1368 }
1369 }
1370
1371cleanup:
1372 return ret;
1373}
1374
1375static int
romane028ef92023-02-24 16:33:08 +01001376nc_server_config_auth_timeout(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001377{
1378 struct nc_endpt *endpt;
1379 int ret = 0;
1380
1381 assert(!strcmp(LYD_NAME(node), "auth-timeout"));
1382
1383 if (equal_parent_name(node, 5, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001384 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001385 ret = 1;
1386 goto cleanup;
1387 }
1388
1389 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1390 endpt->opts.ssh->auth_timeout = strtoul(lyd_get_value(node), NULL, 10);
1391 }
1392 }
1393
1394cleanup:
1395 return ret;
1396}
1397
1398static int
roman874fed12023-05-25 10:20:01 +02001399nc_server_config_replace_truststore_reference(const struct lyd_node *node, struct nc_client_auth *client_auth)
romanc1d2b092023-02-02 08:58:27 +01001400{
romand57b3722023-04-05 11:26:25 +02001401 uint16_t i;
1402 struct nc_truststore *ts = &server_opts.truststore;
romanc1d2b092023-02-02 08:58:27 +01001403
romand57b3722023-04-05 11:26:25 +02001404 /* lookup name */
1405 for (i = 0; i < ts->pub_bag_count; i++) {
1406 if (!strcmp(lyd_get_value(node), ts->pub_bags[i].name)) {
1407 break;
1408 }
1409 }
1410
1411 if (i == ts->pub_bag_count) {
1412 ERR(NULL, "Truststore \"%s\" not found.", lyd_get_value(node));
romanc1d2b092023-02-02 08:58:27 +01001413 return 1;
1414 }
1415
romand57b3722023-04-05 11:26:25 +02001416 client_auth->ts_ref = &ts->pub_bags[i];
1417
romanc1d2b092023-02-02 08:58:27 +01001418 return 0;
1419}
1420
1421/* leaf */
1422static int
romane028ef92023-02-24 16:33:08 +01001423nc_server_config_truststore_reference(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001424{
1425 struct nc_endpt *endpt;
1426 struct nc_client_auth *auth_client;
1427 int ret = 0;
1428
1429 assert(!strcmp(LYD_NAME(node), "truststore-reference"));
1430
1431 if ((equal_parent_name(node, 1, "public-keys")) && (equal_parent_name(node, 8, "listen"))) {
romanf02273a2023-05-25 09:44:11 +02001432 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001433 ret = 1;
1434 goto cleanup;
1435 }
1436
romanf02273a2023-05-25 09:44:11 +02001437 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01001438 ret = 1;
1439 goto cleanup;
1440 }
1441
1442 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
romanf02273a2023-05-25 09:44:11 +02001443 /* set to truststore */
roman874fed12023-05-25 10:20:01 +02001444 auth_client->store = NC_STORE_TRUSTSTORE;
romanf02273a2023-05-25 09:44:11 +02001445
roman874fed12023-05-25 10:20:01 +02001446 ret = nc_server_config_replace_truststore_reference(node, auth_client);
romanc1d2b092023-02-02 08:58:27 +01001447 if (ret) {
1448 goto cleanup;
1449 }
1450 } else {
romand57b3722023-04-05 11:26:25 +02001451 auth_client->ts_ref = NULL;
romanc1d2b092023-02-02 08:58:27 +01001452 }
1453 }
1454
1455cleanup:
1456 return ret;
1457}
1458
1459static int
roman874fed12023-05-25 10:20:01 +02001460nc_server_config_replace_password(const struct lyd_node *node, struct nc_client_auth *auth_client)
romanc1d2b092023-02-02 08:58:27 +01001461{
roman874fed12023-05-25 10:20:01 +02001462 nc_server_config_del_auth_client_password(auth_client);
romanc1d2b092023-02-02 08:58:27 +01001463
1464 auth_client->password = strdup(lyd_get_value(node));
1465 if (!auth_client->password) {
1466 ERRMEM;
1467 return 1;
1468 }
1469
1470 return 0;
1471}
1472
1473/* leaf */
1474static int
romane028ef92023-02-24 16:33:08 +01001475nc_server_config_password(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001476{
1477 struct nc_endpt *endpt;
1478 struct nc_client_auth *auth_client;
1479 int ret = 0;
1480
1481 assert(!strcmp(LYD_NAME(node), "password"));
1482
1483 if (equal_parent_name(node, 7, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001484 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001485 ret = 1;
1486 goto cleanup;
1487 }
1488
romanf02273a2023-05-25 09:44:11 +02001489 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01001490 ret = 1;
1491 goto cleanup;
1492 }
1493
1494 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman874fed12023-05-25 10:20:01 +02001495 ret = nc_server_config_replace_password(node, auth_client);
romanc1d2b092023-02-02 08:58:27 +01001496 if (ret) {
1497 goto cleanup;
1498 }
1499 } else {
roman874fed12023-05-25 10:20:01 +02001500 nc_server_config_del_auth_client_password(auth_client);
romanc1d2b092023-02-02 08:58:27 +01001501 }
1502 }
1503
1504cleanup:
1505 return ret;
1506}
1507
1508static int
romane028ef92023-02-24 16:33:08 +01001509nc_server_config_pam_name(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001510{
1511 struct nc_endpt *endpt;
1512 struct nc_client_auth *auth_client;
1513 int ret = 0;
1514
1515 assert(!strcmp(LYD_NAME(node), "pam-config-file-name"));
1516
1517 if (equal_parent_name(node, 8, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001518 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001519 ret = 1;
1520 goto cleanup;
1521 }
1522
romanf02273a2023-05-25 09:44:11 +02001523 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01001524 ret = 1;
1525 goto cleanup;
1526 }
1527
1528 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman874fed12023-05-25 10:20:01 +02001529 nc_server_config_del_auth_client_pam_name(auth_client);
romanc1d2b092023-02-02 08:58:27 +01001530
1531 auth_client->pam_config_name = strdup(lyd_get_value(node));
1532 if (!auth_client->pam_config_name) {
1533 ERRMEM;
1534 ret = 1;
1535 goto cleanup;
1536 }
1537 }
1538 }
1539
1540cleanup:
1541 return ret;
1542}
1543
1544static int
romane028ef92023-02-24 16:33:08 +01001545nc_server_config_pam_dir(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001546{
1547 struct nc_endpt *endpt;
1548 struct nc_client_auth *auth_client;
1549 int ret = 0;
1550
1551 assert(!strcmp(LYD_NAME(node), "pam-config-file-dir"));
1552
1553 if (equal_parent_name(node, 8, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001554 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001555 ret = 1;
1556 goto cleanup;
1557 }
1558
romanf02273a2023-05-25 09:44:11 +02001559 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01001560 ret = 1;
1561 goto cleanup;
1562 }
1563
1564 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
roman874fed12023-05-25 10:20:01 +02001565 nc_server_config_del_auth_client_pam_dir(auth_client);
romanc1d2b092023-02-02 08:58:27 +01001566 auth_client->pam_config_dir = strdup(lyd_get_value(node));
1567 if (!auth_client->pam_config_dir) {
1568 ERRMEM;
1569 ret = 1;
1570 goto cleanup;
1571 }
1572 }
1573 }
1574
1575cleanup:
1576 return ret;
1577}
1578
1579/* leaf */
1580static int
romane028ef92023-02-24 16:33:08 +01001581nc_server_config_none(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001582{
1583 struct nc_endpt *endpt;
1584 struct nc_client_auth *auth_client;
1585 int ret = 0;
1586
1587 assert(!strcmp(LYD_NAME(node), "none"));
1588
1589 if (equal_parent_name(node, 7, "listen")) {
romanf02273a2023-05-25 09:44:11 +02001590 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001591 ret = 1;
1592 goto cleanup;
1593 }
1594
romanf02273a2023-05-25 09:44:11 +02001595 if (nc_server_config_get_auth_client(node, endpt->opts.ssh, &auth_client)) {
romanc1d2b092023-02-02 08:58:27 +01001596 ret = 1;
1597 goto cleanup;
1598 }
1599
1600 if (op == NC_OP_CREATE) {
1601 auth_client->supports_none = 1;
1602 } else {
1603 auth_client->supports_none = 0;
1604 }
1605 }
1606
1607cleanup:
1608 return ret;
1609}
1610
1611static int
romana6bf6ab2023-05-26 13:26:02 +02001612nc_server_config_transport_params(const char *algorithm, char **alg_store, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001613{
1614 int ret = 0, alg_found = 0;
romana6bf6ab2023-05-26 13:26:02 +02001615 char *substr, *haystack, *alg = NULL;
1616 size_t alg_len;
1617
1618 if (!strncmp(algorithm, "openssh-", 8)) {
1619 /* if the name starts with openssh, convert it to it's original libssh accepted form */
1620 asprintf(&alg, "%s@openssh.com", algorithm + 8);
1621 if (!alg) {
1622 ERRMEM;
1623 ret = 1;
1624 goto cleanup;
1625 }
1626 } else if (!strncmp(algorithm, "libssh-", 7)) {
1627 /* if the name starts with libssh, convert it to it's original libssh accepted form */
1628 asprintf(&alg, "%s@libssh.org", algorithm + 7);
1629 if (!alg) {
1630 ERRMEM;
1631 ret = 1;
1632 goto cleanup;
1633 }
1634 } else {
1635 alg = strdup(algorithm);
1636 if (!alg) {
1637 ERRMEM;
1638 ret = 1;
1639 goto cleanup;
1640 }
1641 }
1642
1643 alg_len = strlen(alg);
romanc1d2b092023-02-02 08:58:27 +01001644
1645 if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
1646 if (!*alg_store) {
1647 /* first call */
1648 *alg_store = strdup(alg);
1649 if (!*alg_store) {
1650 ERRMEM;
1651 ret = 1;
1652 goto cleanup;
1653 }
1654 } else {
1655 /* +1 because of ',' between algorithms */
1656 *alg_store = nc_realloc(*alg_store, strlen(*alg_store) + alg_len + 1 + 1);
1657 if (!*alg_store) {
1658 ERRMEM;
1659 ret = 1;
1660 goto cleanup;
1661 }
1662 sprintf(*alg_store, "%s,%s", *alg_store, alg);
1663 }
1664 } else {
1665 /* delete */
1666 haystack = *alg_store;
1667 while ((substr = strstr(haystack, alg))) {
1668 /* iterate over all the substrings */
1669 if (((substr == haystack) && (*(substr + alg_len) == ',')) ||
1670 ((substr != haystack) && (*(substr - 1) == ',') && (*(substr + alg_len) == ','))) {
1671 /* either the first element of the string or somewhere in the middle */
1672 memmove(substr, substr + alg_len + 1, strlen(substr + alg_len + 1));
1673 alg_found = 1;
1674 break;
1675 } else if ((*(substr - 1) == ',') && (*(substr + alg_len) == '\0')) {
1676 /* the last element of the string */
1677 *(substr - 1) = '\0';
1678 alg_found = 1;
1679 break;
1680 }
1681 haystack++;
1682 }
1683 if (!alg_found) {
1684 ERR(NULL, "Unable to delete an algorithm (%s), which was not previously added.", alg);
1685 ret = 1;
1686 }
1687 }
1688
1689cleanup:
romana6bf6ab2023-05-26 13:26:02 +02001690 free(alg);
romanc1d2b092023-02-02 08:58:27 +01001691 return ret;
1692}
1693
1694/* leaf-list */
1695static int
romane028ef92023-02-24 16:33:08 +01001696nc_server_config_host_key_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001697{
1698 struct nc_endpt *endpt;
1699 int ret = 0, listen = 0;
1700 const char *alg;
1701 uint8_t i;
1702
1703 /* get the algorithm name and compare it with algs supported by libssh */
1704 alg = ((struct lyd_node_term *)node)->value.ident->name;
1705
1706 if (equal_parent_name(node, 6, "listen")) {
1707 listen = 1;
romanf02273a2023-05-25 09:44:11 +02001708 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001709 ret = 1;
1710 goto cleanup;
1711 }
1712 }
1713
1714 i = 0;
1715 while (supported_hostkey_algs[i]) {
1716 if (!strcmp(supported_hostkey_algs[i], alg)) {
1717 if (listen) {
romane028ef92023-02-24 16:33:08 +01001718 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->hostkey_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001719 ret = 1;
1720 goto cleanup;
1721 }
1722 }
1723 break;
1724 }
1725 i++;
1726 }
1727 if (!supported_hostkey_algs[i]) {
1728 /* algorithm not supported */
1729 ERR(NULL, "Public key algorithm (%s) not supported by libssh.", alg);
1730 ret = 1;
1731 }
1732
1733cleanup:
1734 return ret;
1735}
1736
1737/* leaf-list */
1738static int
romane028ef92023-02-24 16:33:08 +01001739nc_server_config_kex_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001740{
1741 struct nc_endpt *endpt;
1742 int ret = 0, listen = 0;
1743 const char *alg;
1744 uint8_t i;
1745
1746 /* get the algorithm name and compare it with algs supported by libssh */
1747 alg = ((struct lyd_node_term *)node)->value.ident->name;
1748
1749 if (equal_parent_name(node, 6, "listen")) {
1750 listen = 1;
romanf02273a2023-05-25 09:44:11 +02001751 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001752 ret = 1;
1753 goto cleanup;
1754 }
1755 }
1756
1757 i = 0;
1758 while (supported_kex_algs[i]) {
1759 if (!strcmp(supported_kex_algs[i], alg)) {
1760 if (listen) {
romane028ef92023-02-24 16:33:08 +01001761 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->kex_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001762 ret = 1;
1763 goto cleanup;
1764 }
1765 }
1766 break;
1767 }
1768 i++;
1769 }
1770 if (!supported_kex_algs[i]) {
1771 /* algorithm not supported */
1772 ERR(NULL, "Key exchange algorithm (%s) not supported by libssh.", alg);
1773 ret = 1;
1774 }
1775
1776cleanup:
1777 return ret;
1778}
1779
1780/* leaf-list */
1781static int
romane028ef92023-02-24 16:33:08 +01001782nc_server_config_encryption_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001783{
1784 struct nc_endpt *endpt;
1785 int ret = 0, listen = 0;
1786 const char *alg;
1787 uint8_t i;
1788
1789 /* get the algorithm name and compare it with algs supported by libssh */
1790 alg = ((struct lyd_node_term *)node)->value.ident->name;
1791
1792 if (equal_parent_name(node, 6, "listen")) {
1793 listen = 1;
romanf02273a2023-05-25 09:44:11 +02001794 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001795 ret = 1;
1796 goto cleanup;
1797 }
1798 }
1799
1800 i = 0;
1801 while (supported_encryption_algs[i]) {
1802 if (!strcmp(supported_encryption_algs[i], alg)) {
1803 if (listen) {
romane028ef92023-02-24 16:33:08 +01001804 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->encryption_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001805 ret = 1;
1806 goto cleanup;
1807 }
1808 }
1809 break;
1810 }
1811 i++;
1812 }
1813 if (!supported_encryption_algs[i]) {
1814 /* algorithm not supported */
1815 ERR(NULL, "Encryption algorithm (%s) not supported by libssh.", alg);
1816 ret = 1;
1817 }
1818
1819cleanup:
1820 return ret;
1821}
1822
1823/* leaf-list */
1824static int
romane028ef92023-02-24 16:33:08 +01001825nc_server_config_mac_alg(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01001826{
1827 struct nc_endpt *endpt;
1828 int ret = 0, listen = 0;
1829 const char *alg;
1830 uint8_t i;
1831
1832 /* get the algorithm name and compare it with algs supported by libssh */
1833 alg = ((struct lyd_node_term *)node)->value.ident->name;
1834
1835 if (equal_parent_name(node, 6, "listen")) {
1836 listen = 1;
romanf02273a2023-05-25 09:44:11 +02001837 if (nc_server_config_get_endpt(node, &endpt, NULL)) {
romanc1d2b092023-02-02 08:58:27 +01001838 ret = 1;
1839 goto cleanup;
1840 }
1841 }
1842
1843 i = 0;
1844 while (supported_mac_algs[i]) {
1845 if (!strcmp(supported_mac_algs[i], alg)) {
1846 if (listen) {
romane028ef92023-02-24 16:33:08 +01001847 if (nc_server_config_transport_params(alg, &endpt->opts.ssh->mac_algs, op)) {
romanc1d2b092023-02-02 08:58:27 +01001848 ret = 1;
1849 goto cleanup;
1850 }
1851 }
1852 break;
1853 }
1854 i++;
1855 }
1856 if (!supported_mac_algs[i]) {
1857 /* algorithm not supported */
1858 ERR(NULL, "MAC algorithm (%s) not supported by libssh.", alg);
1859 ret = 1;
1860 }
1861
1862cleanup:
1863 return ret;
1864}
1865
1866static int
roman874fed12023-05-25 10:20:01 +02001867nc_server_config_create_unix_socket(struct nc_endpt *endpt)
roman83683fb2023-02-24 09:15:23 +01001868{
1869 endpt->ti = NC_TI_UNIX;
1870 endpt->opts.unixsock = calloc(1, sizeof *endpt->opts.unixsock);
1871 if (!endpt->opts.unixsock) {
1872 ERRMEM;
1873 return 1;
1874 }
1875
1876 /* set default values */
1877 endpt->opts.unixsock->mode = -1;
1878 endpt->opts.unixsock->uid = -1;
1879 endpt->opts.unixsock->gid = -1;
1880
1881 return 0;
1882}
1883
1884static int
romane028ef92023-02-24 16:33:08 +01001885nc_server_config_unix_socket(const struct lyd_node *node, NC_OPERATION op)
roman83683fb2023-02-24 09:15:23 +01001886{
1887 int ret = 0;
1888 uint32_t prev_lo;
1889 struct nc_endpt *endpt;
1890 struct nc_bind *bind;
1891 struct nc_server_unix_opts *opts;
1892 struct lyd_node *data = NULL;
1893
1894 assert(!strcmp(LYD_NAME(node), "unix-socket"));
1895
romanf02273a2023-05-25 09:44:11 +02001896 if (nc_server_config_get_endpt(node, &endpt, &bind)) {
roman83683fb2023-02-24 09:15:23 +01001897 ret = 1;
1898 goto cleanup;
1899 }
1900
1901 if (op == NC_OP_CREATE) {
roman874fed12023-05-25 10:20:01 +02001902 if (nc_server_config_create_unix_socket(endpt)) {
roman83683fb2023-02-24 09:15:23 +01001903 ret = 1;
1904 goto cleanup;
1905 }
1906
1907 opts = endpt->opts.unixsock;
1908
1909 lyd_find_path(node, "path", 0, &data);
1910 assert(data);
1911
1912 opts->address = strdup(lyd_get_value(data));
1913 bind->address = strdup(lyd_get_value(data));
1914 if (!opts->address || !bind->address) {
1915 ERRMEM;
1916 ret = 1;
1917 goto cleanup;
1918 }
1919
1920 /* silently search for non-mandatory parameters */
1921 prev_lo = ly_log_options(0);
1922 ret = lyd_find_path(node, "mode", 0, &data);
1923 if (!ret) {
1924 opts->mode = strtol(lyd_get_value(data), NULL, 8);
1925 }
1926
1927 ret = lyd_find_path(node, "uid", 0, &data);
1928 if (!ret) {
1929 opts->uid = strtol(lyd_get_value(data), NULL, 10);
1930 }
1931
1932 ret = lyd_find_path(node, "gid", 0, &data);
1933 if (!ret) {
1934 opts->gid = strtol(lyd_get_value(data), NULL, 10);
1935 }
1936
1937 /* reset the logging options */
1938 ly_log_options(prev_lo);
1939
1940 ret = nc_server_config_set_address_port(endpt, bind, NULL, 0);
1941 if (ret) {
1942 goto cleanup;
1943 }
1944 } else if (op == NC_OP_DELETE) {
roman874fed12023-05-25 10:20:01 +02001945 nc_server_config_del_unix_socket(bind, endpt->opts.unixsock);
roman83683fb2023-02-24 09:15:23 +01001946 }
1947
1948cleanup:
1949 return ret;
1950}
1951
roman0bbc19c2023-05-26 09:59:09 +02001952/**
1953 * @brief Set all endpoint client auth references, which couldn't be set beforehand.
1954 *
1955 * The references that could not be set are those, which reference endpoints, which
1956 * lie below the given endpoint in the YANG data (because of DFS tree parsing).
1957 *
1958 * @return 0 on success, 1 on error.
1959 */
1960static int
1961nc_server_config_fill_endpt_client_auth(void)
1962{
1963 uint16_t i, j;
1964
1965 for (i = 0; i < server_opts.endpt_count; i++) {
1966 /* go through all the endpoint */
1967 if (server_opts.endpts[i].referenced_endpt_name) {
1968 /* endpt has a reference, that hasn't been set yet */
1969 for (j = i + 1; j < server_opts.endpt_count; j++) {
1970 /* go through all the remaining endpts */
1971 if (!strcmp(server_opts.endpts[i].referenced_endpt_name, server_opts.endpts[j].name)) {
1972 /* found the endpoint we were looking for */
1973 if (server_opts.endpts[i].ti == NC_TI_LIBSSH) {
1974 server_opts.endpts[i].opts.ssh->endpt_client_ref = &server_opts.endpts[j];
1975 break;
1976 } else {
1977 ERRINT;
1978 return 1;
1979 }
1980 }
1981 }
1982
1983 /* didn't find the endpoint */
1984 if (j == server_opts.endpt_count) {
1985 ERR(NULL, "Endpoint \"%s\" referenced by \"%s\" not found.",
1986 server_opts.endpts[i].referenced_endpt_name, server_opts.endpts[i].name);
1987 return 1;
1988 }
1989 }
1990 }
1991
1992 return 0;
1993}
1994
1995static int
1996nc_server_config_endpoint_client_auth_has_cycle(struct nc_endpt *original, struct nc_endpt *next, NC_TRANSPORT_IMPL transport)
1997{
1998 if (transport == NC_TI_LIBSSH) {
1999 if (next->opts.ssh->endpt_client_ref) {
2000 if (next->opts.ssh->endpt_client_ref == original) {
2001 return 1;
2002 } else {
2003 return nc_server_config_endpoint_client_auth_has_cycle(original, next->opts.ssh->endpt_client_ref, NC_TI_LIBSSH);
2004 }
2005 } else {
2006 return 0;
2007 }
2008 } else {
2009 ERRINT;
2010 return 1;
2011 }
2012}
2013
2014static int
2015nc_server_config_endpoint_client_auth(const struct lyd_node *node, NC_OPERATION op)
2016{
2017 int ret = 0;
2018 uint16_t i;
2019 const char *endpt_name;
2020 struct nc_endpt *endpt;
2021
2022 assert(!strcmp(LYD_NAME(node), "endpoint-client-auth"));
2023
2024 /* get current endpoint */
2025 ret = nc_server_config_get_endpt(node, &endpt, NULL);
2026 if (ret) {
2027 goto cleanup;
2028 }
2029
2030 if (op == NC_OP_DELETE) {
2031 endpt->opts.ssh->endpt_client_ref = NULL;
2032 goto cleanup;
2033 }
2034
2035 /* find the endpoint leafref is referring to */
2036 endpt_name = lyd_get_value(node);
2037 for (i = 0; i < server_opts.endpt_count; i++) {
2038 if (!strcmp(endpt_name, server_opts.endpts[i].name)) {
2039 break;
2040 }
2041 }
2042
2043 if (i == server_opts.endpt_count) {
2044 /* endpt not found, save the name and try to look it up later */
2045 endpt->referenced_endpt_name = strdup(endpt_name);
2046 if (!endpt->referenced_endpt_name) {
2047 ERRMEM;
2048 ret = 1;
2049 goto cleanup;
2050 }
2051 goto cleanup;
2052 }
2053
2054 /* check for self reference */
2055 if (endpt == &server_opts.endpts[i]) {
2056 ERR(NULL, "Self client authentication reference detected.");
2057 ret = 1;
2058 goto cleanup;
2059 }
2060
2061 /* check for cyclic references */
2062 ret = nc_server_config_endpoint_client_auth_has_cycle(endpt, &server_opts.endpts[i], endpt->ti);
2063 if (ret) {
2064 ERR(NULL, "Cyclic client authentication reference detected.");
2065 goto cleanup;
2066 }
2067
2068 /* assign the current endpt the referrenced endpt */
2069 endpt->opts.ssh->endpt_client_ref = &server_opts.endpts[i];
2070
2071cleanup:
2072 return ret;
2073}
2074
roman83683fb2023-02-24 09:15:23 +01002075static int
romanf02273a2023-05-25 09:44:11 +02002076nc_server_config_parse_netconf_server(const struct lyd_node *node, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002077{
2078 const char *name = LYD_NAME(node);
2079
2080 if (!strcmp(name, "listen")) {
romanf02273a2023-05-25 09:44:11 +02002081 if (nc_server_config_listen(NULL, op)) {
romanc1d2b092023-02-02 08:58:27 +01002082 goto error;
2083 }
2084 } else if (!strcmp(name, "idle-timeout")) {
romane028ef92023-02-24 16:33:08 +01002085 if (nc_server_config_idle_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002086 goto error;
2087 }
2088 } else if (!strcmp(name, "endpoint")) {
romane028ef92023-02-24 16:33:08 +01002089 if (nc_server_config_endpoint(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002090 goto error;
2091 }
2092 } else if (!strcmp(name, "ssh")) {
romane028ef92023-02-24 16:33:08 +01002093 if (nc_server_config_ssh(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002094 goto error;
2095 }
2096 } else if (!strcmp(name, "local-address")) {
romane028ef92023-02-24 16:33:08 +01002097 if (nc_server_config_local_address(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002098 goto error;
2099 }
2100 } else if (!strcmp(name, "local-port")) {
romane028ef92023-02-24 16:33:08 +01002101 if (nc_server_config_local_port(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002102 goto error;
2103 }
2104 } else if (!strcmp(name, "keepalives")) {
romane028ef92023-02-24 16:33:08 +01002105 if (nc_server_config_keepalives(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002106 goto error;
2107 }
2108 } else if (!strcmp(name, "idle-time")) {
romane028ef92023-02-24 16:33:08 +01002109 if (nc_server_config_idle_time(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002110 goto error;
2111 }
2112 } else if (!strcmp(name, "max-probes")) {
romane028ef92023-02-24 16:33:08 +01002113 if (nc_server_config_max_probes(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002114 goto error;
2115 }
2116 } else if (!strcmp(name, "probe-interval")) {
romane028ef92023-02-24 16:33:08 +01002117 if (nc_server_config_probe_interval(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002118 goto error;
2119 }
2120 } else if (!strcmp(name, "host-key")) {
romane028ef92023-02-24 16:33:08 +01002121 if (nc_server_config_host_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002122 goto error;
2123 }
2124 } else if (!strcmp(name, "public-key-format")) {
romane028ef92023-02-24 16:33:08 +01002125 if (nc_server_config_public_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002126 goto error;
2127 }
2128 } else if (!strcmp(name, "public-key")) {
romane028ef92023-02-24 16:33:08 +01002129 if (nc_server_config_public_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002130 goto error;
2131 }
2132 } else if (!strcmp(name, "private-key-format")) {
romane028ef92023-02-24 16:33:08 +01002133 if (nc_server_config_private_key_format(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002134 goto error;
2135 }
2136 } else if (!strcmp(name, "cleartext-private-key")) {
romane028ef92023-02-24 16:33:08 +01002137 if (nc_server_config_cleartext_private_key(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002138 goto error;
2139 }
2140 } else if (!strcmp(name, "keystore-reference")) {
romane028ef92023-02-24 16:33:08 +01002141 if (nc_server_config_keystore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002142 goto error;
2143 }
2144 } else if (!strcmp(name, "user")) {
romane028ef92023-02-24 16:33:08 +01002145 if (nc_server_config_user(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002146 goto error;
2147 }
2148 } else if (!strcmp(name, "auth-attempts")) {
romane028ef92023-02-24 16:33:08 +01002149 if (nc_server_config_auth_attempts(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002150 goto error;
2151 }
2152 } else if (!strcmp(name, "auth-timeout")) {
romane028ef92023-02-24 16:33:08 +01002153 if (nc_server_config_auth_timeout(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002154 goto error;
2155 }
2156 } else if (!strcmp(name, "truststore-reference")) {
romane028ef92023-02-24 16:33:08 +01002157 if (nc_server_config_truststore_reference(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002158 goto error;
2159 }
2160 } else if (!strcmp(name, "password")) {
romane028ef92023-02-24 16:33:08 +01002161 if (nc_server_config_password(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002162 goto error;
2163 }
2164 } else if (!strcmp(name, "pam-config-file-name")) {
romane028ef92023-02-24 16:33:08 +01002165 if (nc_server_config_pam_name(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002166 goto error;
2167 }
2168 } else if (!strcmp(name, "pam-config-file-dir")) {
romane028ef92023-02-24 16:33:08 +01002169 if (nc_server_config_pam_dir(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002170 goto error;
2171 }
2172 } else if (!strcmp(name, "none")) {
romane028ef92023-02-24 16:33:08 +01002173 if (nc_server_config_none(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002174 goto error;
2175 }
2176 } else if (!strcmp(name, "host-key-alg")) {
romane028ef92023-02-24 16:33:08 +01002177 if (nc_server_config_host_key_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002178 goto error;
2179 }
2180 } else if (!strcmp(name, "key-exchange-alg")) {
romane028ef92023-02-24 16:33:08 +01002181 if (nc_server_config_kex_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002182 goto error;
2183 }
2184 } else if (!strcmp(name, "encryption-alg")) {
romane028ef92023-02-24 16:33:08 +01002185 if (nc_server_config_encryption_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002186 goto error;
2187 }
2188 } else if (!strcmp(name, "mac-alg")) {
romane028ef92023-02-24 16:33:08 +01002189 if (nc_server_config_mac_alg(node, op)) {
romanc1d2b092023-02-02 08:58:27 +01002190 goto error;
2191 }
roman83683fb2023-02-24 09:15:23 +01002192 } else if (!strcmp(name, "unix-socket")) {
romane028ef92023-02-24 16:33:08 +01002193 if (nc_server_config_unix_socket(node, op)) {
roman83683fb2023-02-24 09:15:23 +01002194 goto error;
2195 }
roman0bbc19c2023-05-26 09:59:09 +02002196 } else if (!strcmp(name, "endpoint-client-auth")) {
2197 if (nc_server_config_endpoint_client_auth(node, op)) {
2198 goto error;
2199 }
romanc1d2b092023-02-02 08:58:27 +01002200 } else if (!strcmp(name, "cert-data")) {} else if (!strcmp(name, "expiration-date")) {} else if (!strcmp(name, "asymmetric-key")) {} else if (!strcmp(name, "certificate")) {} else if (!strcmp(name, "key-format")) {} else if (!strcmp(name,
2201 "cleartext-key")) {} else if (!strcmp(name, "hidden-key")) {} else if (!strcmp(name, "id_hint")) {} else if (!strcmp(name, "external-identity")) {} else if (!strcmp(name, "hash")) {} else if (!strcmp(name, "context")) {} else if (!strcmp(name,
2202 "target-protocol")) {} else if (!strcmp(name, "target-kdf")) {} else if (!strcmp(name, "client-authentication")) {} else if (!strcmp(name, "ca-certs")) {} else if (!strcmp(name, "ee-certs")) {} else if (!strcmp(name,
2203 "raw-public-keys")) {} else if (!strcmp(name, "tls12-psks")) {} else if (!strcmp(name, "tls13-epsks")) {} else if (!strcmp(name, "tls-version")) {} else if (!strcmp(name, "cipher-suite")) {} else if (!strcmp(name,
2204 "peer-allowed-to-send")) {} else if (!strcmp(name, "test-peer-aliveness")) {} else if (!strcmp(name, "max-wait")) {} else if (!strcmp(name, "max-attempts")) {} else if (!strcmp(name, "cert-to-name")) {} else if (!strcmp(name,
2205 "id")) {} else if (!strcmp(name, "fingerprint")) {} else if (!strcmp(name, "map-type")) {}
2206
2207 return 0;
2208
2209error:
2210 ERR(NULL, "Configuring (%s) failed.", LYD_NAME(node));
2211 return 1;
2212}
2213
2214int
roman0bbc19c2023-05-26 09:59:09 +02002215nc_server_config_parse_tree(const struct lyd_node *node, NC_OPERATION parent_op, NC_MODULE module)
romanc1d2b092023-02-02 08:58:27 +01002216{
2217 struct lyd_node *child;
2218 struct lyd_meta *m;
romanf9906b42023-05-22 14:04:29 +02002219 NC_OPERATION current_op = NC_OP_UNKNOWN;
romanf02273a2023-05-25 09:44:11 +02002220 int ret;
romanc1d2b092023-02-02 08:58:27 +01002221
2222 assert(node);
2223
romanf9906b42023-05-22 14:04:29 +02002224 /* get current op if there is any */
2225 if ((m = lyd_find_meta(node->meta, NULL, "yang:operation"))) {
2226 if (!strcmp(lyd_get_meta_value(m), "create")) {
2227 current_op = NC_OP_CREATE;
2228 } else if (!strcmp(lyd_get_meta_value(m), "delete")) {
2229 current_op = NC_OP_DELETE;
2230 } else if (!strcmp(lyd_get_meta_value(m), "replace")) {
2231 current_op = NC_OP_REPLACE;
2232 } else if (!strcmp(lyd_get_meta_value(m), "none")) {
2233 current_op = NC_OP_NONE;
romanc1d2b092023-02-02 08:58:27 +01002234 }
2235 }
2236
2237 /* node has no op, inherit from the parent */
romanf9906b42023-05-22 14:04:29 +02002238 if (!current_op) {
2239 if (!parent_op) {
2240 ERR(NULL, "Unknown operation for node \"%s\".", LYD_NAME(node));
2241 return 1;
2242 }
2243
romanc1d2b092023-02-02 08:58:27 +01002244 current_op = parent_op;
2245 }
2246
2247 switch (current_op) {
2248 case NC_OP_NONE:
2249 break;
2250 case NC_OP_CREATE:
2251 case NC_OP_DELETE:
2252 case NC_OP_REPLACE:
romanf02273a2023-05-25 09:44:11 +02002253 if (module == NC_MODULE_NETCONF_SERVER) {
2254 ret = nc_server_config_parse_netconf_server(node, current_op);
2255 } else if (module == NC_MODULE_KEYSTORE) {
2256 ret = nc_server_config_parse_keystore(node, current_op);
2257 } else {
2258 ret = nc_server_config_parse_truststore(node, current_op);
2259 }
2260 if (ret) {
2261 return ret;
romanc1d2b092023-02-02 08:58:27 +01002262 }
2263 break;
2264 default:
2265 break;
2266 }
2267
2268 if (current_op != NC_OP_DELETE) {
2269 LY_LIST_FOR(lyd_child(node), child) {
roman0bbc19c2023-05-26 09:59:09 +02002270 if (nc_server_config_parse_tree(child, current_op, module)) {
romanc1d2b092023-02-02 08:58:27 +01002271 return 1;
2272 }
2273 }
2274 }
2275 return 0;
2276}
2277
romanc1d2b092023-02-02 08:58:27 +01002278API int
2279nc_server_config_load_modules(struct ly_ctx **ctx)
2280{
2281 int i, new_ctx = 0;
2282
2283 if (!*ctx) {
2284 if (ly_ctx_new(NC_SERVER_SEARCH_DIR, 0, ctx)) {
2285 ERR(NULL, "Couldn't create new libyang context.\n");
2286 goto error;
2287 }
2288 new_ctx = 1;
2289 }
2290
2291 /* all features */
2292 const char *ietf_nectonf_server[] = {"ssh-listen", "tls-listen", "ssh-call-home", "tls-call-home", "central-netconf-server-supported", NULL};
2293 /* all features */
2294 const char *ietf_x509_cert_to_name[] = {NULL};
2295 /* no private-key-encryption and csr-generation */
2296 const char *ietf_crypto_types[] = {
2297 "one-symmetric-key-format", "one-asymmetric-key-format", "symmetrically-encrypted-value-format",
2298 "asymmetrically-encrypted-value-format", "cms-enveloped-data-format", "cms-encrypted-data-format",
2299 "p10-based-csrs", "certificate-expiration-notification", "hidden-keys", "password-encryption",
2300 "symmetric-key-encryption", NULL
2301 };
2302 /* all features */
2303 const char *ietf_tcp_common[] = {"keepalives-supported", NULL};
2304 /* no ssh-x509-certs */
2305 const char *ietf_ssh_common[] = {"transport-params", "public-key-generation", NULL};
2306 /* all features */
2307 const char *iana_ssh_encryption_algs[] = {NULL};
2308 /* all features */
2309 const char *iana_ssh_key_exchange_algs[] = {NULL};
2310 /* all features */
2311 const char *iana_ssh_mac_algs[] = {NULL};
2312 /* all features */
2313 const char *iana_ssh_public_key_algs[] = {NULL};
romanf02273a2023-05-25 09:44:11 +02002314 /* no symmetric-keys */
2315 const char *ietf_keystore[] = {"central-keystore-supported", "local-definitions-supported", "asymmetric-keys", NULL};
romanc1d2b092023-02-02 08:58:27 +01002316 /* no ssh-server-keepalives and local-user-auth-hostbased */
2317 const char *ietf_ssh_server[] = {"local-users-supported", "local-user-auth-publickey", "local-user-auth-password", "local-user-auth-none", NULL};
2318 /* all features */
2319 const char *ietf_truststore[] = {"central-truststore-supported", "local-definitions-supported", "certificates", "public-keys", NULL};
2320 /* all features */
2321 const char *ietf_tls_server[] = {
2322 "tls-server-keepalives", "server-ident-x509-cert", "server-ident-raw-public-key", "server-ident-tls12-psk",
2323 "server-ident-tls13-epsk", "client-auth-supported", "client-auth-x509-cert", "client-auth-raw-public-key",
2324 "client-auth-tls12-psk", "client-auth-tls13-epsk", NULL
2325 };
2326 /* all features */
2327 const char *libnetconf2_netconf_server[] = {NULL};
2328
2329 const char *module_names[] = {
2330 "ietf-netconf-server", "ietf-x509-cert-to-name", "ietf-crypto-types",
2331 "ietf-tcp-common", "ietf-ssh-common", "iana-ssh-encryption-algs",
2332 "iana-ssh-key-exchange-algs", "iana-ssh-mac-algs", "iana-ssh-public-key-algs",
2333 "ietf-keystore", "ietf-ssh-server", "ietf-truststore",
2334 "ietf-tls-server", "libnetconf2-netconf-server", NULL
2335 };
2336
2337 const char **module_features[] = {
2338 ietf_nectonf_server, ietf_x509_cert_to_name, ietf_crypto_types,
2339 ietf_tcp_common, ietf_ssh_common, iana_ssh_encryption_algs,
2340 iana_ssh_key_exchange_algs, iana_ssh_mac_algs, iana_ssh_public_key_algs,
2341 ietf_keystore, ietf_ssh_server, ietf_truststore,
2342 ietf_tls_server, libnetconf2_netconf_server, NULL
2343 };
2344
2345 for (i = 0; module_names[i] != NULL; i++) {
2346 if (!ly_ctx_load_module(*ctx, module_names[i], NULL, module_features[i])) {
2347 ERR(NULL, "Loading module \"%s\" failed.\n", module_names[i]);
2348 goto error;
2349 }
2350 }
2351
2352 return 0;
2353
2354error:
2355 if (new_ctx) {
2356 ly_ctx_destroy(*ctx);
2357 *ctx = NULL;
2358 }
2359 return 1;
2360}
2361
2362API int
2363nc_server_config_setup_path(const struct ly_ctx *ctx, const char *path)
2364{
2365 struct lyd_node *tree = NULL;
2366 int ret = 0;
2367
roman40672412023-05-04 11:10:22 +02002368 NC_CHECK_ARG_RET(NULL, path, 1);
romanc1d2b092023-02-02 08:58:27 +01002369
romanf6f37a52023-05-25 14:27:51 +02002370 ret = lyd_parse_data_path(ctx, path, LYD_UNKNOWN, LYD_PARSE_NO_STATE | LYD_PARSE_STRICT, LYD_VALIDATE_NO_STATE, &tree);
romanc1d2b092023-02-02 08:58:27 +01002371 if (ret) {
2372 goto cleanup;
2373 }
2374
romanf6f37a52023-05-25 14:27:51 +02002375 ret = nc_server_config_setup_data(tree);
romanc1d2b092023-02-02 08:58:27 +01002376 if (ret) {
2377 goto cleanup;
2378 }
2379
2380cleanup:
2381 lyd_free_all(tree);
2382 return ret;
2383}
2384
romanf9906b42023-05-22 14:04:29 +02002385static int
2386nc_server_config_fill_nectonf_server(const struct lyd_node *data, NC_OPERATION op)
romanc1d2b092023-02-02 08:58:27 +01002387{
2388 int ret = 0;
2389 struct lyd_node *tree;
romand57b3722023-04-05 11:26:25 +02002390
romanc1d2b092023-02-02 08:58:27 +01002391 ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &tree);
2392 if (ret) {
2393 ERR(NULL, "Unable to find the netconf-server container in the YANG data.");
2394 goto cleanup;
2395 }
2396
roman0bbc19c2023-05-26 09:59:09 +02002397 if (nc_server_config_parse_tree(tree, op, NC_MODULE_NETCONF_SERVER)) {
2398 ret = 1;
2399 goto cleanup;
2400 }
2401
2402 if (nc_server_config_fill_endpt_client_auth()) {
romanf9906b42023-05-22 14:04:29 +02002403 ret = 1;
2404 goto cleanup;
2405 }
2406
2407cleanup:
2408 return ret;
2409}
2410
2411API int
romanf6f37a52023-05-25 14:27:51 +02002412nc_server_config_setup_diff(const struct lyd_node *data)
romanf9906b42023-05-22 14:04:29 +02002413{
2414 int ret = 0;
2415
2416 /* LOCK */
2417 pthread_rwlock_wrlock(&server_opts.config_lock);
2418
2419 /* configure keystore */
romanf02273a2023-05-25 09:44:11 +02002420 ret = nc_server_config_fill_keystore(data, NC_OP_UNKNOWN);
romanf9906b42023-05-22 14:04:29 +02002421 if (ret) {
2422 ERR(NULL, "Filling keystore failed.");
2423 goto cleanup;
2424 }
2425
2426 /* configure truststore */
romanf02273a2023-05-25 09:44:11 +02002427 ret = nc_server_config_fill_truststore(data, NC_OP_UNKNOWN);
romanf9906b42023-05-22 14:04:29 +02002428 if (ret) {
2429 ERR(NULL, "Filling truststore failed.");
2430 goto cleanup;
2431 }
2432
2433 /* configure netconf-server */
2434 ret = nc_server_config_fill_nectonf_server(data, NC_OP_UNKNOWN);
2435 if (ret) {
2436 ERR(NULL, "Filling netconf-server failed.");
2437 goto cleanup;
2438 }
2439
2440cleanup:
2441 /* UNLOCK */
2442 pthread_rwlock_unlock(&server_opts.config_lock);
2443 return ret;
2444}
2445
2446API int
romanf6f37a52023-05-25 14:27:51 +02002447nc_server_config_setup_data(const struct lyd_node *data)
romanf9906b42023-05-22 14:04:29 +02002448{
2449 int ret = 0;
2450 struct lyd_node *tree, *iter, *root;
2451
2452 /* LOCK */
2453 pthread_rwlock_wrlock(&server_opts.config_lock);
2454
2455 /* find the netconf-server node */
2456 ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &root);
2457 if (ret) {
2458 ERR(NULL, "Unable to find the netconf-server container in the YANG data.");
2459 goto cleanup;
2460 }
2461
2462 /* iterate through all the nodes and make sure there is no operation attribute */
2463 LY_LIST_FOR(root, tree) {
2464 LYD_TREE_DFS_BEGIN(tree, iter) {
2465 if (lyd_find_meta(iter->meta, NULL, "yang:operation")) {
2466 ERR(NULL, "Unexpected operation attribute in the YANG data.");
romanc1d2b092023-02-02 08:58:27 +01002467 ret = 1;
2468 goto cleanup;
2469 }
romanf9906b42023-05-22 14:04:29 +02002470 LYD_TREE_DFS_END(tree, iter);
romanc1d2b092023-02-02 08:58:27 +01002471 }
2472 }
2473
romanf9906b42023-05-22 14:04:29 +02002474 /* delete the current configuration */
romanf02273a2023-05-25 09:44:11 +02002475 nc_server_config_listen(NULL, NC_OP_DELETE);
2476 nc_server_config_ks_keystore(NULL, NC_OP_DELETE);
2477 nc_server_config_ts_truststore(NULL, NC_OP_DELETE);
romanf9906b42023-05-22 14:04:29 +02002478
2479 /* configure keystore */
romanf02273a2023-05-25 09:44:11 +02002480 ret = nc_server_config_fill_keystore(data, NC_OP_CREATE);
romanf9906b42023-05-22 14:04:29 +02002481 if (ret) {
2482 ERR(NULL, "Filling keystore failed.");
2483 goto cleanup;
2484 }
2485
2486 /* configure truststore */
romanf02273a2023-05-25 09:44:11 +02002487 ret = nc_server_config_fill_truststore(data, NC_OP_CREATE);
romanf9906b42023-05-22 14:04:29 +02002488 if (ret) {
2489 ERR(NULL, "Filling truststore failed.");
2490 goto cleanup;
2491 }
2492
2493 /* configure netconf-server */
2494 ret = nc_server_config_fill_nectonf_server(data, NC_OP_CREATE);
2495 if (ret) {
2496 ERR(NULL, "Filling netconf-server failed.");
romanc1d2b092023-02-02 08:58:27 +01002497 goto cleanup;
2498 }
2499
2500cleanup:
2501 /* UNLOCK */
2502 pthread_rwlock_unlock(&server_opts.config_lock);
2503 return ret;
2504}