blob: cf19e0125a8c8a37e91401e11aa13445564a121b [file] [log] [blame]
roman3f9b65c2023-06-05 14:26:58 +02001/**
Roytakb2794852023-10-18 14:30:22 +02002 * @file server_config_util.c
roman3f9b65c2023-06-05 14:26:58 +02003 * @author Roman Janota <janota@cesnet.cz>
Roytakb2794852023-10-18 14:30:22 +02004 * @brief libnetconf2 server configuration utilities
roman3f9b65c2023-06-05 14:26:58 +02005 *
6 * @copyright
7 * Copyright (c) 2023 CESNET, z.s.p.o.
8 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
16#define _GNU_SOURCE
17
Roytakb2794852023-10-18 14:30:22 +020018#include "server_config_util.h"
19
roman2eab4742023-06-06 10:00:26 +020020#include <libyang/libyang.h>
romand30af552023-06-16 15:18:27 +020021#include <stdarg.h>
roman3f9b65c2023-06-05 14:26:58 +020022#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
roman2eab4742023-06-06 10:00:26 +020026#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +020027#include <libssh/libssh.h>
roman3f9b65c2023-06-05 14:26:58 +020028#include <openssl/err.h>
29#include <openssl/evp.h>
30#include <openssl/pem.h>
roman2eab4742023-06-06 10:00:26 +020031#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +020032
33#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020034#include "log_p.h"
Roytakb2794852023-10-18 14:30:22 +020035#include "server_config.h"
roman3f9b65c2023-06-05 14:26:58 +020036#include "session.h"
37#include "session_p.h"
38
roman8ba6efa2023-07-12 15:27:52 +020039int
Roytakb2794852023-10-18 14:30:22 +020040nc_server_config_create(const struct ly_ctx *ctx, struct lyd_node **tree, const char *value, const char *path_fmt, ...)
roman8ba6efa2023-07-12 15:27:52 +020041{
42 int ret = 0;
43 va_list ap;
44 char *path = NULL;
45
Roytak7b9bf292023-10-04 14:06:38 +020046 NC_CHECK_ARG_RET(NULL, ctx, tree, path_fmt, 1);
47
roman8ba6efa2023-07-12 15:27:52 +020048 va_start(ap, path_fmt);
49
50 /* create the path from the format */
51 ret = vasprintf(&path, path_fmt, ap);
52 if (ret == -1) {
53 ERRMEM;
54 path = NULL;
55 goto cleanup;
56 }
57
58 /* create the nodes in the path */
roman5ef2a572023-08-18 15:45:44 +020059 if (!*tree) {
60 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
61 } else {
62 /* this could output NULL if no new nodes, lyd_find_path would fail then */
63 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, NULL);
64 }
roman8ba6efa2023-07-12 15:27:52 +020065 if (ret) {
66 goto cleanup;
67 }
68
69 /* set the node to the top level node */
70 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
71 if (ret) {
72 goto cleanup;
73 }
74
75 /* add all default nodes */
76 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
77 if (ret) {
78 goto cleanup;
79 }
80
81cleanup:
82 free(path);
83 va_end(ap);
84 return ret;
85}
86
87int
Roytakb2794852023-10-18 14:30:22 +020088nc_server_config_append(const struct ly_ctx *ctx, const char *parent_path, const char *child_name,
roman8ba6efa2023-07-12 15:27:52 +020089 const char *value, struct lyd_node **tree)
90{
91 int ret = 0;
92 char *path = NULL;
93
Roytak7b9bf292023-10-04 14:06:38 +020094 NC_CHECK_ARG_RET(NULL, ctx, parent_path, child_name, tree, 1);
95
roman8ba6efa2023-07-12 15:27:52 +020096 /* create the path by appending child to the parent path */
97 ret = asprintf(&path, "%s/%s", parent_path, child_name);
98 if (ret == -1) {
99 ERRMEM;
100 path = NULL;
101 goto cleanup;
102 }
103
104 /* create the nodes in the path */
roman5ef2a572023-08-18 15:45:44 +0200105 if (!*tree) {
106 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
107 } else {
108 /* this could output NULL if no new nodes, lyd_find_path would fail then */
109 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, NULL);
110 }
roman8ba6efa2023-07-12 15:27:52 +0200111 if (ret) {
112 goto cleanup;
113 }
114
115 /* set the node to the top level node */
116 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
117 if (ret) {
118 goto cleanup;
119 }
120
121 /* add all default nodes */
122 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
123 if (ret) {
124 goto cleanup;
125 }
126
127cleanup:
128 free(path);
129 return ret;
130}
131
132int
Roytakb2794852023-10-18 14:30:22 +0200133nc_server_config_delete(struct lyd_node **tree, const char *path_fmt, ...)
Roytak7b9bf292023-10-04 14:06:38 +0200134{
135 int ret = 0;
136 va_list ap;
137 char *path = NULL;
138 struct lyd_node *sub = NULL;
139
140 NC_CHECK_ARG_RET(NULL, tree, path_fmt, 1);
141
142 va_start(ap, path_fmt);
143
144 /* create the path from the format */
145 ret = vasprintf(&path, path_fmt, ap);
146 if (ret == -1) {
147 ERRMEM;
148 path = NULL;
149 goto cleanup;
150 }
151
152 /* find the node we want to delete */
153 ret = lyd_find_path(*tree, path, 0, &sub);
154 if (ret) {
155 goto cleanup;
156 }
157
158 lyd_free_tree(sub);
159
160 /* set the node to top level container */
161 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
162 if (ret) {
163 goto cleanup;
164 }
165
166 /* add all default nodes */
167 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
168 if (ret) {
169 goto cleanup;
170 }
171
172cleanup:
173 free(path);
174 va_end(ap);
175 return ret;
176}
177
178int
Roytakb2794852023-10-18 14:30:22 +0200179nc_server_config_check_delete(struct lyd_node **tree, const char *path_fmt, ...)
roman8ba6efa2023-07-12 15:27:52 +0200180{
181 int ret = 0;
182 va_list ap;
183 char *path = NULL;
184 struct lyd_node *sub = NULL;
185
Roytak7b9bf292023-10-04 14:06:38 +0200186 NC_CHECK_ARG_RET(NULL, tree, path_fmt, 1);
187
roman8ba6efa2023-07-12 15:27:52 +0200188 va_start(ap, path_fmt);
189
190 /* create the path from the format */
191 ret = vasprintf(&path, path_fmt, ap);
192 if (ret == -1) {
193 ERRMEM;
194 path = NULL;
195 goto cleanup;
196 }
197
198 /* find the node we want to delete */
199 ret = lyd_find_path(*tree, path, 0, &sub);
200 if ((ret == LY_EINCOMPLETE) || (ret == LY_ENOTFOUND)) {
201 ret = 0;
202 goto cleanup;
203 } else if (ret) {
204 ERR(NULL, "Unable to delete node in the path \"%s\".", path);
205 goto cleanup;
206 }
207
208 lyd_free_tree(sub);
209
210 /* set the node to top level container */
211 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
212 if (ret) {
213 goto cleanup;
214 }
215
216cleanup:
217 free(path);
218 va_end(ap);
219 return ret;
220}
221
roman2eab4742023-06-06 10:00:26 +0200222#ifdef NC_ENABLED_SSH_TLS
223
roman3f9b65c2023-06-05 14:26:58 +0200224const char *
Roytakb2794852023-10-18 14:30:22 +0200225nc_server_config_util_privkey_format_to_identityref(NC_PRIVKEY_FORMAT format)
roman3f9b65c2023-06-05 14:26:58 +0200226{
227 switch (format) {
228 case NC_PRIVKEY_FORMAT_RSA:
229 return "ietf-crypto-types:rsa-private-key-format";
230 case NC_PRIVKEY_FORMAT_EC:
231 return "ietf-crypto-types:ec-private-key-format";
232 case NC_PRIVKEY_FORMAT_X509:
roman13145912023-08-17 15:36:54 +0200233 return "libnetconf2-netconf-server:private-key-info-format";
roman3f9b65c2023-06-05 14:26:58 +0200234 case NC_PRIVKEY_FORMAT_OPENSSH:
235 return "libnetconf2-netconf-server:openssh-private-key-format";
236 default:
237 ERR(NULL, "Private key type not supported.");
238 return NULL;
239 }
240}
241
roman13145912023-08-17 15:36:54 +0200242static int
Roytakb2794852023-10-18 14:30:22 +0200243nc_server_config_util_pubkey_bin_to_b64(const unsigned char *pub_bin, int bin_len, char **pubkey)
roman13145912023-08-17 15:36:54 +0200244{
245 int ret = 0, b64_len;
246 char *pub_b64 = NULL;
247
Roytak7b9bf292023-10-04 14:06:38 +0200248 NC_CHECK_ARG_RET(NULL, pub_bin, bin_len, pubkey, 1);
249
roman13145912023-08-17 15:36:54 +0200250 /* get b64 buffer len, for ever 3 bytes of bin 4 bytes of b64 + NULL terminator */
251 if (bin_len % 3 == 0) {
252 pub_b64 = malloc((bin_len / 3) * 4 + 1);
253 } else {
254 /* bin len not divisible by 3, need to add 4 bytes for some padding so that the len is divisible by 4 */
255 pub_b64 = malloc((bin_len / 3) * 4 + 4 + 1);
256 }
257 if (!pub_b64) {
258 ERRMEM;
259 ret = 1;
260 goto cleanup;
261 }
262
263 /* bin to b64 */
264 b64_len = EVP_EncodeBlock((unsigned char *)pub_b64, pub_bin, bin_len);
265 *pubkey = strndup(pub_b64, b64_len);
266 if (!*pubkey) {
267 ERRMEM;
268 ret = 1;
269 goto cleanup;
270 }
271
272cleanup:
273 free(pub_b64);
274 return ret;
275}
276
277static int
Roytakb2794852023-10-18 14:30:22 +0200278nc_server_config_util_bn_to_bin(const BIGNUM *bn, unsigned char **bin, int *bin_len)
roman13145912023-08-17 15:36:54 +0200279{
280 int ret = 0;
281 unsigned char *bin_tmp = NULL;
282
283 NC_CHECK_ARG_RET(NULL, bn, bin, bin_len, 1);
284
285 *bin = NULL;
286
287 /* prepare buffer for converting BN to binary */
288 bin_tmp = calloc(BN_num_bytes(bn), sizeof *bin_tmp);
289 if (!bin_tmp) {
290 ERRMEM;
291 return 1;
292 }
293
294 /* convert to binary */
295 *bin_len = BN_bn2bin(bn, bin_tmp);
296
297 /* if the highest bit in the MSB is set a byte with the value 0 has to be prepended */
298 if (bin_tmp[0] & 0x80) {
299 *bin = malloc(*bin_len + 1);
300 if (!*bin) {
301 ERRMEM;
302 ret = 1;
303 goto cleanup;
304 }
305
306 (*bin)[0] = 0;
307 memcpy(*bin + 1, bin_tmp, *bin_len);
308 (*bin_len)++;
309 } else {
310 *bin = malloc(*bin_len);
311 if (!*bin) {
312 ERRMEM;
313 ret = 1;
314 goto cleanup;
315 }
316
317 memcpy(*bin, bin_tmp, *bin_len);
318 }
319
320cleanup:
321 free(bin_tmp);
322 return ret;
323}
324
325/* ssh pubkey defined in RFC 4253 section 6.6 */
326static int
Roytakb2794852023-10-18 14:30:22 +0200327nc_server_config_util_evp_pkey_to_ssh_pubkey(EVP_PKEY *pkey, char **pubkey)
roman13145912023-08-17 15:36:54 +0200328{
329 int ret = 0, e_len, n_len, p_len, bin_len;
330 BIGNUM *e = NULL, *n = NULL, *p = NULL;
331 unsigned char *e_bin = NULL, *n_bin = NULL, *p_bin = NULL, *bin = NULL, *bin_tmp;
332 const char *algorithm_name, *curve_name;
333 char *ec_group = NULL;
334 uint32_t alg_name_len, curve_name_len, alg_name_len_be, curve_name_len_be, p_len_be, e_len_be, n_len_be;
335 size_t ec_group_len;
336
Roytak7b9bf292023-10-04 14:06:38 +0200337 NC_CHECK_ARG_RET(NULL, pkey, pubkey, 1);
338
roman13145912023-08-17 15:36:54 +0200339 if (EVP_PKEY_is_a(pkey, "RSA")) {
340 /* RSA key */
341 algorithm_name = "ssh-rsa";
342
343 /* get the public key params */
344 if (!EVP_PKEY_get_bn_param(pkey, "e", &e) || !EVP_PKEY_get_bn_param(pkey, "n", &n)) {
345 ERR(NULL, "Getting public key parameters from RSA private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
346 ret = 1;
347 goto cleanup;
348 }
349
350 /* BIGNUM to bin */
Roytakb2794852023-10-18 14:30:22 +0200351 if (nc_server_config_util_bn_to_bin(e, &e_bin, &e_len) || nc_server_config_util_bn_to_bin(n, &n_bin, &n_len)) {
roman13145912023-08-17 15:36:54 +0200352 ret = 1;
353 goto cleanup;
354 }
355
356 alg_name_len = strlen(algorithm_name);
357 /* buffer for public key in binary, which looks like this:
358 * alg_name len (4 bytes), alg_name, PK exponent len (4 bytes), PK exponent, modulus len (4 bytes), modulus
359 */
360 bin_len = 4 + alg_name_len + 4 + e_len + 4 + n_len;
361 bin = malloc(bin_len);
362 if (!bin) {
363 ERRMEM;
364 ret = 1;
365 goto cleanup;
366 }
367
368 /* to network byte order (big endian) */
369 alg_name_len_be = htonl(alg_name_len);
370 e_len_be = htonl(e_len);
371 n_len_be = htonl(n_len);
372
373 /* create the public key in binary */
374 bin_tmp = bin;
375 memcpy(bin_tmp, &alg_name_len_be, 4);
376 bin_tmp += 4;
377 memcpy(bin_tmp, algorithm_name, alg_name_len);
378 bin_tmp += alg_name_len;
379 memcpy(bin_tmp, &e_len_be, 4);
380 bin_tmp += 4;
381 memcpy(bin_tmp, e_bin, e_len);
382 bin_tmp += e_len;
383 memcpy(bin_tmp, &n_len_be, 4);
384 bin_tmp += 4;
385 memcpy(bin_tmp, n_bin, n_len);
386 } else if (EVP_PKEY_is_a(pkey, "EC")) {
387 /* EC Private key, get it's group first */
388 /* get group len */
389 ret = EVP_PKEY_get_utf8_string_param(pkey, "group", NULL, 0, &ec_group_len);
390 if (!ret) {
391 ret = 1;
392 goto cleanup;
393 }
394 /* alloc mem for group + 1 for \0 */
395 ec_group = malloc(ec_group_len + 1);
396 if (!ec_group) {
397 ERRMEM;
398 ret = 1;
399 goto cleanup;
400 }
401 /* get the group */
402 ret = EVP_PKEY_get_utf8_string_param(pkey, "group", ec_group, ec_group_len + 1, NULL);
403 if (!ret) {
404 ERR(NULL, "Getting public key parameter from EC private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
405 ret = 1;
406 goto cleanup;
407 }
408
409 /* get alg and curve names */
410 if (!strcmp(ec_group, "P-256") || !strcmp(ec_group, "secp256r1") || !strcmp(ec_group, "prime256v1")) {
411 algorithm_name = "ecdsa-sha2-nistp256";
412 curve_name = "nistp256";
413 } else if (!strcmp(ec_group, "P-384") || !strcmp(ec_group, "secp384r1")) {
414 algorithm_name = "ecdsa-sha2-nistp384";
415 curve_name = "nistp384";
416 } else if (!strcmp(ec_group, "P-521") || !strcmp(ec_group, "secp521r1")) {
417 algorithm_name = "ecdsa-sha2-nistp521";
418 curve_name = "nistp521";
419 } else {
420 ERR(NULL, "EC group \"%s\" not supported.", ec_group);
421 ret = 1;
422 goto cleanup;
423 }
424
425 /* get the public key - p, which is a point on the elliptic curve */
426 ret = EVP_PKEY_get_bn_param(pkey, "p", &p);
427 if (!ret) {
428 ERR(NULL, "Getting public key point from the EC private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
429 ret = 1;
430 goto cleanup;
431 }
432
433 /* prepare buffer for converting p to binary */
434 p_bin = malloc(BN_num_bytes(p));
435 if (!p_bin) {
436 ERRMEM;
437 ret = 1;
438 goto cleanup;
439 }
440 /* convert to binary */
441 p_len = BN_bn2bin(p, p_bin);
442
443 alg_name_len = strlen(algorithm_name);
444 curve_name_len = strlen(curve_name);
445 /* buffer for public key in binary, which looks like so:
446 * alg_name len (4 bytes), alg_name, curve_name len (4 bytes), curve_name, PK point p len (4 bytes), PK point p
447 */
448 bin_len = 4 + alg_name_len + 4 + curve_name_len + 4 + p_len;
449 bin = malloc(bin_len);
450 if (!bin) {
451 ERRMEM;
452 ret = 1;
453 goto cleanup;
454 }
455
456 /* to network byte order (big endian) */
457 alg_name_len_be = htonl(alg_name_len);
458 curve_name_len_be = htonl(curve_name_len);
459 p_len_be = htonl(p_len);
460
461 /* create the public key in binary */
462 bin_tmp = bin;
463 memcpy(bin_tmp, &alg_name_len_be, 4);
464 bin_tmp += 4;
465 memcpy(bin_tmp, algorithm_name, alg_name_len);
466 bin_tmp += alg_name_len;
467 memcpy(bin_tmp, &curve_name_len_be, 4);
468 bin_tmp += 4;
469 memcpy(bin_tmp, curve_name, curve_name_len);
470 bin_tmp += curve_name_len;
471 memcpy(bin_tmp, &p_len_be, 4);
472 bin_tmp += 4;
473 memcpy(bin_tmp, p_bin, p_len);
474 } else if (EVP_PKEY_is_a(pkey, "ED25519")) {
475 ERR(NULL, "Generating PEM ED25519 key from OpenSSH is not supported by libssh yet.");
476 ret = 1;
477 goto cleanup;
478 } else {
479 ERR(NULL, "Unable to generate public key from private key (Private key type not supported).");
480 ret = 1;
481 goto cleanup;
482 }
483
Roytak7b9bf292023-10-04 14:06:38 +0200484 /* convert created bin to b64 */
Roytakb2794852023-10-18 14:30:22 +0200485 ret = nc_server_config_util_pubkey_bin_to_b64(bin, bin_len, pubkey);
roman13145912023-08-17 15:36:54 +0200486 if (ret) {
487 ERR(NULL, "Converting public key from binary to base64 failed.");
488 goto cleanup;
489 }
490
491cleanup:
492 free(bin);
493 free(e_bin);
494 free(n_bin);
495 free(ec_group);
496 free(p_bin);
497 BN_free(e);
498 BN_free(n);
499 BN_free(p);
500 return ret;
501}
502
503/* spki = subject public key info */
504static int
Roytakb2794852023-10-18 14:30:22 +0200505nc_server_config_util_evp_pkey_to_spki_pubkey(EVP_PKEY *pkey, char **pubkey)
roman13145912023-08-17 15:36:54 +0200506{
507 int ret = 0, len;
508 BIO *bio = NULL;
509 char *pub_b64 = NULL;
510
Roytak7b9bf292023-10-04 14:06:38 +0200511 NC_CHECK_ARG_RET(NULL, pkey, pubkey, 1);
512
roman13145912023-08-17 15:36:54 +0200513 bio = BIO_new(BIO_s_mem());
514 if (!bio) {
515 ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
516 ret = 1;
517 goto cleanup;
518 }
519
520 /* write the evp_pkey contents to bio */
521 if (!PEM_write_bio_PUBKEY(bio, pkey)) {
522 ERR(NULL, "Writing public key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
523 ret = 1;
524 goto cleanup;
525 }
526
527 /* read the pubkey from bio */
528 len = BIO_get_mem_data(bio, &pub_b64);
529 if (len <= 0) {
530 ERR(NULL, "Reading base64 private key from BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
531 ret = 1;
532 goto cleanup;
533 }
534
535 /* copy the public key without the header and footer */
536 *pubkey = strndup(pub_b64 + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER),
537 len - strlen(NC_SUBJECT_PUBKEY_INFO_HEADER) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER));
538 if (!*pubkey) {
539 ERRMEM;
540 ret = 1;
541 goto cleanup;
542 }
543
544cleanup:
545 BIO_free(bio);
546 return ret;
547}
548
roman3f9b65c2023-06-05 14:26:58 +0200549int
Roytakb2794852023-10-18 14:30:22 +0200550nc_server_config_util_read_certificate(const char *cert_path, char **cert)
roman3f9b65c2023-06-05 14:26:58 +0200551{
552 int ret = 0, cert_len;
553 X509 *x509 = NULL;
554 FILE *f = NULL;
555 BIO *bio = NULL;
556 char *c = NULL;
557
Roytak7b9bf292023-10-04 14:06:38 +0200558 NC_CHECK_ARG_RET(NULL, cert_path, cert, 1);
roman3f9b65c2023-06-05 14:26:58 +0200559
560 f = fopen(cert_path, "r");
561 if (!f) {
562 ERR(NULL, "Unable to open certificate file \"%s\".", cert_path);
563 ret = 1;
564 goto cleanup;
565 }
566
567 /* load the cert into memory */
568 x509 = PEM_read_X509(f, NULL, NULL, NULL);
569 if (!x509) {
570 ret = -1;
571 goto cleanup;
572 }
573
574 bio = BIO_new(BIO_s_mem());
575 if (!bio) {
576 ret = -1;
577 goto cleanup;
578 }
579
580 ret = PEM_write_bio_X509(bio, x509);
581 if (!ret) {
582 ret = -1;
583 goto cleanup;
584 }
585
586 cert_len = BIO_pending(bio);
587 if (cert_len <= 0) {
588 ret = -1;
589 goto cleanup;
590 }
591
592 c = malloc(cert_len + 1);
593 if (!c) {
594 ERRMEM;
595 ret = 1;
596 goto cleanup;
597 }
598
599 /* read the cert from bio */
600 ret = BIO_read(bio, c, cert_len);
601 if (ret <= 0) {
602 ret = -1;
603 goto cleanup;
604 }
605 c[cert_len] = '\0';
606
607 /* strip the cert of the header and footer */
608 *cert = strdup(c + strlen(NC_PEM_CERTIFICATE_HEADER));
609 if (!*cert) {
610 ERRMEM;
611 ret = 1;
612 goto cleanup;
613 }
614
615 (*cert)[strlen(*cert) - strlen(NC_PEM_CERTIFICATE_FOOTER)] = '\0';
616
617 ret = 0;
618
619cleanup:
620 if (ret == -1) {
621 ERR(NULL, "Error getting certificate from file \"%s\" (OpenSSL Error): \"%s\".", cert_path, ERR_reason_error_string(ERR_get_error()));
622 ret = 1;
623 }
624 if (f) {
625 fclose(f);
626 }
627
628 BIO_free(bio);
629 X509_free(x509);
630 free(c);
631 return ret;
632}
633
634static int
Roytakb2794852023-10-18 14:30:22 +0200635nc_server_config_util_read_pubkey_ssh2(FILE *f, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200636{
637 char *buffer = NULL;
638 size_t size = 0, pubkey_len = 0;
639 void *tmp;
640 ssize_t read;
641 int ret = 0;
642
Roytak7b9bf292023-10-04 14:06:38 +0200643 NC_CHECK_ARG_RET(NULL, f, pubkey, 1);
644
645 /* read lines from the file and create the public key without NL from it */
roman3f9b65c2023-06-05 14:26:58 +0200646 while ((read = getline(&buffer, &size, f)) > 0) {
647 if (!strncmp(buffer, "----", 4)) {
Roytak7b9bf292023-10-04 14:06:38 +0200648 /* skip header and footer */
roman3f9b65c2023-06-05 14:26:58 +0200649 continue;
650 }
651
652 if (!strncmp(buffer, "Comment:", 8)) {
Roytak7b9bf292023-10-04 14:06:38 +0200653 /* skip a comment */
roman3f9b65c2023-06-05 14:26:58 +0200654 continue;
655 }
656
657 if (buffer[read - 1] == '\n') {
Roytak7b9bf292023-10-04 14:06:38 +0200658 /* avoid NL */
roman3f9b65c2023-06-05 14:26:58 +0200659 read--;
660 }
661
662 tmp = realloc(*pubkey, pubkey_len + read + 1);
663 if (!tmp) {
664 ERRMEM;
665 ret = 1;
666 goto cleanup;
667 }
668
669 *pubkey = tmp;
670 memcpy(*pubkey + pubkey_len, buffer, read);
671 pubkey_len += read;
672 }
673
674 if (!pubkey_len) {
675 ERR(NULL, "Unexpected public key format.");
676 ret = 1;
677 goto cleanup;
678 }
679
680 (*pubkey)[pubkey_len] = '\0';
681
682cleanup:
683 free(buffer);
684 return ret;
685}
686
687static int
Roytakb2794852023-10-18 14:30:22 +0200688nc_server_config_util_read_pubkey_openssl(FILE *f, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200689{
690 int ret = 0;
roman13145912023-08-17 15:36:54 +0200691 EVP_PKEY *pub_pkey = NULL;
692
693 NC_CHECK_ARG_RET(NULL, f, pubkey, 1);
roman3f9b65c2023-06-05 14:26:58 +0200694
695 /* read the pubkey from file */
roman13145912023-08-17 15:36:54 +0200696 pub_pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
697 if (!pub_pkey) {
698 ERR(NULL, "Reading public key from file failed (%s).", ERR_reason_error_string(ERR_get_error()));
699 return 1;
roman3f9b65c2023-06-05 14:26:58 +0200700 }
701
Roytakb2794852023-10-18 14:30:22 +0200702 ret = nc_server_config_util_evp_pkey_to_ssh_pubkey(pub_pkey, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200703
roman13145912023-08-17 15:36:54 +0200704 EVP_PKEY_free(pub_pkey);
roman3f9b65c2023-06-05 14:26:58 +0200705 return ret;
706}
707
roman3f9b65c2023-06-05 14:26:58 +0200708static int
Roytakb2794852023-10-18 14:30:22 +0200709nc_server_config_util_read_pubkey_libssh(const char *pubkey_path, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200710{
711 int ret = 0;
712 ssh_key pub_sshkey = NULL;
713
roman13145912023-08-17 15:36:54 +0200714 NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
715
roman3f9b65c2023-06-05 14:26:58 +0200716 ret = ssh_pki_import_pubkey_file(pubkey_path, &pub_sshkey);
717 if (ret) {
718 ERR(NULL, "Importing public key from file \"%s\" failed.", pubkey_path);
719 return ret;
720 }
721
722 ret = ssh_pki_export_pubkey_base64(pub_sshkey, pubkey);
723 if (ret) {
roman13145912023-08-17 15:36:54 +0200724 ERR(NULL, "Importing pubkey failed.");
725 goto cleanup;
roman3f9b65c2023-06-05 14:26:58 +0200726 }
727
roman13145912023-08-17 15:36:54 +0200728cleanup:
roman3f9b65c2023-06-05 14:26:58 +0200729 ssh_key_free(pub_sshkey);
roman13145912023-08-17 15:36:54 +0200730 return 0;
roman3f9b65c2023-06-05 14:26:58 +0200731}
732
roman3f9b65c2023-06-05 14:26:58 +0200733int
Roytakb2794852023-10-18 14:30:22 +0200734nc_server_config_util_get_ssh_pubkey_file(const char *pubkey_path, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200735{
736 int ret = 0;
737 FILE *f = NULL;
738 char *header = NULL;
739 size_t len = 0;
740
roman13145912023-08-17 15:36:54 +0200741 NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
roman3f9b65c2023-06-05 14:26:58 +0200742
743 *pubkey = NULL;
744
745 f = fopen(pubkey_path, "r");
746 if (!f) {
747 ERR(NULL, "Unable to open file \"%s\".", pubkey_path);
748 ret = 1;
749 goto cleanup;
750 }
751
roman13145912023-08-17 15:36:54 +0200752 /* read the header */
roman3f9b65c2023-06-05 14:26:58 +0200753 if (getline(&header, &len, f) < 0) {
754 ERR(NULL, "Error reading header from file \"%s\".", pubkey_path);
755 ret = 1;
756 goto cleanup;
757 }
758 rewind(f);
759
760 if (!strncmp(header, NC_SUBJECT_PUBKEY_INFO_HEADER, strlen(NC_SUBJECT_PUBKEY_INFO_HEADER))) {
761 /* it's subject public key info public key */
Roytakb2794852023-10-18 14:30:22 +0200762 ret = nc_server_config_util_read_pubkey_openssl(f, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200763 } else if (!strncmp(header, NC_SSH2_PUBKEY_HEADER, strlen(NC_SSH2_PUBKEY_HEADER))) {
764 /* it's ssh2 public key */
Roytakb2794852023-10-18 14:30:22 +0200765 ret = nc_server_config_util_read_pubkey_ssh2(f, pubkey);
roman7fdc84d2023-06-06 13:14:53 +0200766 } else {
roman3f9b65c2023-06-05 14:26:58 +0200767 /* it's probably OpenSSH public key */
Roytakb2794852023-10-18 14:30:22 +0200768 ret = nc_server_config_util_read_pubkey_libssh(pubkey_path, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200769 }
roman3f9b65c2023-06-05 14:26:58 +0200770 if (ret) {
771 ERR(NULL, "Error getting public key from file \"%s\".", pubkey_path);
772 goto cleanup;
773 }
774
775cleanup:
776 if (f) {
777 fclose(f);
778 }
779
780 free(header);
roman3f9b65c2023-06-05 14:26:58 +0200781 return ret;
782}
783
roman3f9b65c2023-06-05 14:26:58 +0200784int
Roytakb2794852023-10-18 14:30:22 +0200785nc_server_config_util_get_spki_pubkey_file(const char *pubkey_path, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200786{
787 int ret = 0;
roman13145912023-08-17 15:36:54 +0200788 FILE *f = NULL;
789 EVP_PKEY *pub_pkey = NULL;
roman3f9b65c2023-06-05 14:26:58 +0200790
roman13145912023-08-17 15:36:54 +0200791 NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
roman3f9b65c2023-06-05 14:26:58 +0200792
roman3f9b65c2023-06-05 14:26:58 +0200793 *pubkey = NULL;
794
roman13145912023-08-17 15:36:54 +0200795 f = fopen(pubkey_path, "r");
796 if (!f) {
797 ERR(NULL, "Unable to open file \"%s\".", pubkey_path);
798 ret = 1;
799 goto cleanup;
800 }
801
802 /* read the pubkey from file */
803 pub_pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
804 if (!pub_pkey) {
805 ERR(NULL, "Reading public key from file failed (%s).", ERR_reason_error_string(ERR_get_error()));
806 return 1;
807 }
808
Roytakb2794852023-10-18 14:30:22 +0200809 ret = nc_server_config_util_evp_pkey_to_spki_pubkey(pub_pkey, pubkey);
roman13145912023-08-17 15:36:54 +0200810 if (ret) {
811 goto cleanup;
812 }
813
814cleanup:
815 if (f) {
816 fclose(f);
817 }
818
819 EVP_PKEY_free(pub_pkey);
820 return ret;
821}
822
823static int
Roytakb2794852023-10-18 14:30:22 +0200824nc_server_config_util_privkey_header_to_format(FILE *f_privkey, const char *privkey_path, NC_PRIVKEY_FORMAT *privkey_format)
roman13145912023-08-17 15:36:54 +0200825{
826 char *privkey_header = NULL;
827 size_t len = 0;
828
Roytak7b9bf292023-10-04 14:06:38 +0200829 NC_CHECK_ARG_RET(NULL, f_privkey, privkey_path, privkey_format, 1);
830
roman13145912023-08-17 15:36:54 +0200831 /* read header */
832 if (getline(&privkey_header, &len, f_privkey) < 0) {
833 ERR(NULL, "Error reading header from file \"%s\".", privkey_path);
834 return 1;
835 }
836
837 if (!strncmp(privkey_header, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) {
838 /* it's PKCS8 (X.509) private key */
839 *privkey_format = NC_PRIVKEY_FORMAT_X509;
840 } else if (!strncmp(privkey_header, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) {
841 /* it's OpenSSH private key */
842 *privkey_format = NC_PRIVKEY_FORMAT_OPENSSH;
843 } else if (!strncmp(privkey_header, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) {
844 /* it's RSA privkey in PKCS1 format */
845 *privkey_format = NC_PRIVKEY_FORMAT_RSA;
846 } else if (!strncmp(privkey_header, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) {
847 /* it's EC privkey in SEC1 format */
848 *privkey_format = NC_PRIVKEY_FORMAT_EC;
849 } else {
850 ERR(NULL, "Private key format (%s) not supported.", privkey_header);
851 free(privkey_header);
852 return 1;
853 }
854
855 /* reset the reading head */
856 rewind(f_privkey);
857 free(privkey_header);
858 return 0;
859}
860
861static int
Roytakb2794852023-10-18 14:30:22 +0200862nc_server_config_util_get_privkey_openssl(const char *privkey_path, FILE *f_privkey, char **privkey, EVP_PKEY **pkey)
roman13145912023-08-17 15:36:54 +0200863{
864 int ret = 0, len;
865 BIO *bio = NULL;
866 char *priv_b64 = NULL;
867
Roytak7b9bf292023-10-04 14:06:38 +0200868 NC_CHECK_ARG_RET(NULL, privkey_path, f_privkey, privkey, pkey, 1);
869
roman13145912023-08-17 15:36:54 +0200870 bio = BIO_new(BIO_s_mem());
871 if (!bio) {
872 ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
873 ret = 1;
874 goto cleanup;
875 }
876
877 /* read the privkey file, create EVP_PKEY */
878 *pkey = PEM_read_PrivateKey(f_privkey, NULL, NULL, NULL);
879 if (!*pkey) {
880 ERR(NULL, "Getting private key from file \"%s\" failed (%s).", privkey_path, ERR_reason_error_string(ERR_get_error()));
881 ret = 1;
882 goto cleanup;
883 }
884
885 /* write the privkey to bio */
886 if (!PEM_write_bio_PrivateKey(bio, *pkey, NULL, NULL, 0, NULL, NULL)) {
887 ERR(NULL, "Writing private key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
888 ret = 1;
889 goto cleanup;
890 }
891
892 /* read the privkey from bio */
893 len = BIO_get_mem_data(bio, &priv_b64);
894 if (len <= 0) {
895 ERR(NULL, "Reading base64 private key from BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
896 ret = 1;
897 goto cleanup;
898 }
899
900 *privkey = strndup(priv_b64, len);
Roytak7b9bf292023-10-04 14:06:38 +0200901 if (!*privkey) {
902 ERRMEM;
903 ret = 1;
904 goto cleanup;
905 }
roman13145912023-08-17 15:36:54 +0200906
907cleanup:
Roytak7b9bf292023-10-04 14:06:38 +0200908 /* priv_b64 is freed with BIO */
roman13145912023-08-17 15:36:54 +0200909 BIO_free(bio);
910 return ret;
911}
912
913static int
Roytakb2794852023-10-18 14:30:22 +0200914nc_server_config_util_get_privkey_libssh(const char *privkey_path, char **privkey, EVP_PKEY **pkey)
roman13145912023-08-17 15:36:54 +0200915{
916 int ret = 0;
917 BIO *bio = NULL;
918 char *priv_b64 = NULL;
919 ssh_key key = NULL;
920
Roytak7b9bf292023-10-04 14:06:38 +0200921 NC_CHECK_ARG_RET(NULL, privkey_path, privkey, pkey, 1);
922
roman13145912023-08-17 15:36:54 +0200923 ret = ssh_pki_import_privkey_file(privkey_path, NULL, NULL, NULL, &key);
924 if (ret) {
925 ERR(NULL, "Importing privkey from file \"%s\" failed.", privkey_path);
926 goto cleanup;
927 }
928
929 /* exports the key in a format in which OpenSSL can read it */
930 ret = ssh_pki_export_privkey_base64(key, NULL, NULL, NULL, &priv_b64);
931 if (ret) {
932 ERR(NULL, "Exporting privkey to base64 failed.");
933 goto cleanup;
934 }
935
936 bio = BIO_new(BIO_s_mem());
937 if (!bio) {
938 ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
939 ret = 1;
940 goto cleanup;
941 }
942
943 ret = BIO_write(bio, priv_b64, strlen(priv_b64));
944 if (ret <= 0) {
945 ERR(NULL, "Writing private key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
946 ret = 1;
947 goto cleanup;
948 }
949
950 /* create EVP_PKEY from the b64 */
951 *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
952 if (!*pkey) {
953 ERR(NULL, "Getting private key from file \"%s\" failed (%s).", privkey_path, ERR_reason_error_string(ERR_get_error()));
954 ret = 1;
955 goto cleanup;
956 }
957
958 *privkey = strndup(priv_b64, ret);
Roytak7b9bf292023-10-04 14:06:38 +0200959 if (!*privkey) {
960 ERRMEM;
961 ret = 1;
962 goto cleanup;
963 }
roman13145912023-08-17 15:36:54 +0200964
965 /* ok */
966 ret = 0;
967
968cleanup:
969 free(priv_b64);
970 BIO_free(bio);
971 ssh_key_free(key);
972 return ret;
973}
974
975static int
Roytakb2794852023-10-18 14:30:22 +0200976nc_server_config_util_get_privkey(const char *privkey_path, NC_PRIVKEY_FORMAT *privkey_format, char **privkey, EVP_PKEY **pkey)
roman13145912023-08-17 15:36:54 +0200977{
978 int ret = 0;
979 FILE *f_privkey = NULL;
980 char *priv = NULL;
981
Roytak7b9bf292023-10-04 14:06:38 +0200982 NC_CHECK_ARG_RET(NULL, privkey_path, privkey_format, privkey, pkey, 1);
983
roman3f9b65c2023-06-05 14:26:58 +0200984 f_privkey = fopen(privkey_path, "r");
985 if (!f_privkey) {
986 ERR(NULL, "Unable to open file \"%s\".", privkey_path);
987 ret = 1;
988 goto cleanup;
989 }
990
roman13145912023-08-17 15:36:54 +0200991 /* read the first line from the privkey to determine it's type */
Roytakb2794852023-10-18 14:30:22 +0200992 ret = nc_server_config_util_privkey_header_to_format(f_privkey, privkey_path, privkey_format);
roman13145912023-08-17 15:36:54 +0200993 if (ret) {
994 ERR(NULL, "Getting private key format from file \"%s\" failed.", privkey_path);
roman3f9b65c2023-06-05 14:26:58 +0200995 goto cleanup;
996 }
roman3f9b65c2023-06-05 14:26:58 +0200997
roman13145912023-08-17 15:36:54 +0200998 switch (*privkey_format) {
999 /* fall-through */
1000 case NC_PRIVKEY_FORMAT_RSA:
1001 case NC_PRIVKEY_FORMAT_EC:
1002 case NC_PRIVKEY_FORMAT_X509:
1003 /* OpenSSL solely can do this */
Roytakb2794852023-10-18 14:30:22 +02001004 ret = nc_server_config_util_get_privkey_openssl(privkey_path, f_privkey, &priv, pkey);
roman13145912023-08-17 15:36:54 +02001005 break;
1006 case NC_PRIVKEY_FORMAT_OPENSSH:
1007 /* need the help of libssh */
Roytakb2794852023-10-18 14:30:22 +02001008 ret = nc_server_config_util_get_privkey_libssh(privkey_path, &priv, pkey);
roman13145912023-08-17 15:36:54 +02001009 /* if the function returned successfully, the key is no longer OpenSSH, it was converted to x509 */
1010 *privkey_format = NC_PRIVKEY_FORMAT_X509;
1011 break;
1012 default:
1013 ERR(NULL, "Private key format not recognized.");
roman3f9b65c2023-06-05 14:26:58 +02001014 ret = 1;
roman13145912023-08-17 15:36:54 +02001015 break;
roman3f9b65c2023-06-05 14:26:58 +02001016 }
roman3f9b65c2023-06-05 14:26:58 +02001017 if (ret) {
1018 goto cleanup;
1019 }
1020
romand30af552023-06-16 15:18:27 +02001021 /* strip private key's header and footer */
roman13145912023-08-17 15:36:54 +02001022 *privkey = strdup(priv + strlen(NC_PKCS8_PRIVKEY_HEADER));
1023 if (!*privkey) {
1024 ERRMEM;
1025 ret = 1;
1026 goto cleanup;
romand30af552023-06-16 15:18:27 +02001027 }
roman13145912023-08-17 15:36:54 +02001028 (*privkey)[strlen(*privkey) - strlen(NC_PKCS8_PRIVKEY_FOOTER)] = '\0';
romand30af552023-06-16 15:18:27 +02001029
roman3f9b65c2023-06-05 14:26:58 +02001030cleanup:
1031 if (f_privkey) {
1032 fclose(f_privkey);
1033 }
1034
romand30af552023-06-16 15:18:27 +02001035 free(priv);
roman13145912023-08-17 15:36:54 +02001036 return ret;
1037}
roman3f9b65c2023-06-05 14:26:58 +02001038
roman13145912023-08-17 15:36:54 +02001039int
Roytakb2794852023-10-18 14:30:22 +02001040nc_server_config_util_get_asym_key_pair(const char *privkey_path, const char *pubkey_path, NC_PUBKEY_FORMAT wanted_pubkey_format,
roman13145912023-08-17 15:36:54 +02001041 char **privkey, NC_PRIVKEY_FORMAT *privkey_type, char **pubkey)
1042{
1043 int ret = 0;
1044 EVP_PKEY *priv_pkey = NULL;
1045
1046 NC_CHECK_ARG_RET(NULL, privkey_path, privkey, privkey_type, pubkey, 1);
1047
1048 *privkey = NULL;
1049 *pubkey = NULL;
1050
1051 /* get private key base64 and EVP_PKEY */
Roytakb2794852023-10-18 14:30:22 +02001052 ret = nc_server_config_util_get_privkey(privkey_path, privkey_type, privkey, &priv_pkey);
roman13145912023-08-17 15:36:54 +02001053 if (ret) {
1054 ERR(NULL, "Getting private key from file \"%s\" failed.", privkey_path);
1055 goto cleanup;
1056 }
1057
1058 /* get public key, either from file or generate it from the EVP_PKEY */
1059 if (!pubkey_path) {
1060 if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) {
Roytakb2794852023-10-18 14:30:22 +02001061 ret = nc_server_config_util_evp_pkey_to_ssh_pubkey(priv_pkey, pubkey);
roman13145912023-08-17 15:36:54 +02001062 } else {
Roytakb2794852023-10-18 14:30:22 +02001063 ret = nc_server_config_util_evp_pkey_to_spki_pubkey(priv_pkey, pubkey);
roman13145912023-08-17 15:36:54 +02001064 }
1065 } else {
1066 if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) {
Roytakb2794852023-10-18 14:30:22 +02001067 ret = nc_server_config_util_get_ssh_pubkey_file(pubkey_path, pubkey);
roman13145912023-08-17 15:36:54 +02001068 } else {
Roytakb2794852023-10-18 14:30:22 +02001069 ret = nc_server_config_util_get_spki_pubkey_file(pubkey_path, pubkey);
roman13145912023-08-17 15:36:54 +02001070 }
1071 }
1072 if (ret) {
1073 if (pubkey_path) {
1074 ERR(NULL, "Getting public key from file \"%s\" failed.", pubkey_path);
1075 } else {
1076 ERR(NULL, "Generating public key from private key failed.");
1077 }
1078 goto cleanup;
1079 }
1080
1081cleanup:
roman3f9b65c2023-06-05 14:26:58 +02001082 EVP_PKEY_free(priv_pkey);
roman3f9b65c2023-06-05 14:26:58 +02001083 return ret;
1084}
1085
1086API int
Roytakb2794852023-10-18 14:30:22 +02001087nc_server_config_add_address_port(const struct ly_ctx *ctx, const char *endpt_name, NC_TRANSPORT_IMPL transport,
roman142718b2023-06-29 09:15:29 +02001088 const char *address, uint16_t port, struct lyd_node **config)
roman3f9b65c2023-06-05 14:26:58 +02001089{
1090 int ret = 0;
romand30af552023-06-16 15:18:27 +02001091 const char *address_fmt, *port_fmt;
roman142718b2023-06-29 09:15:29 +02001092 char port_buf[6] = {0};
roman3f9b65c2023-06-05 14:26:58 +02001093
Roytak7b9bf292023-10-04 14:06:38 +02001094 NC_CHECK_ARG_RET(NULL, ctx, endpt_name, address, config, 1);
roman3f9b65c2023-06-05 14:26:58 +02001095
roman3f9b65c2023-06-05 14:26:58 +02001096 if (transport == NC_TI_LIBSSH) {
romand30af552023-06-16 15:18:27 +02001097 /* SSH path */
1098 address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters/local-address";
1099 port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters/local-port";
roman2eab4742023-06-06 10:00:26 +02001100 } else if (transport == NC_TI_OPENSSL) {
romand30af552023-06-16 15:18:27 +02001101 /* TLS path */
1102 address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tcp-server-parameters/local-address";
1103 port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tcp-server-parameters/local-port";
roman2eab4742023-06-06 10:00:26 +02001104 } else {
Roytak7b9bf292023-10-04 14:06:38 +02001105 ERR(NULL, "Can not set address and port of a non SSH/TLS endpoint.");
roman2eab4742023-06-06 10:00:26 +02001106 ret = 1;
1107 goto cleanup;
roman3f9b65c2023-06-05 14:26:58 +02001108 }
roman3f9b65c2023-06-05 14:26:58 +02001109
Roytakb2794852023-10-18 14:30:22 +02001110 ret = nc_server_config_create(ctx, config, address, address_fmt, endpt_name);
roman3f9b65c2023-06-05 14:26:58 +02001111 if (ret) {
1112 goto cleanup;
1113 }
1114
roman142718b2023-06-29 09:15:29 +02001115 sprintf(port_buf, "%d", port);
Roytakb2794852023-10-18 14:30:22 +02001116 ret = nc_server_config_create(ctx, config, port_buf, port_fmt, endpt_name);
roman3f9b65c2023-06-05 14:26:58 +02001117 if (ret) {
1118 goto cleanup;
1119 }
romand30af552023-06-16 15:18:27 +02001120
roman3f9b65c2023-06-05 14:26:58 +02001121cleanup:
romand30af552023-06-16 15:18:27 +02001122 return ret;
1123}
1124
roman5cbb6532023-06-22 12:53:17 +02001125API int
Roytakb2794852023-10-18 14:30:22 +02001126nc_server_config_add_ch_address_port(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name,
roman5cbb6532023-06-22 12:53:17 +02001127 NC_TRANSPORT_IMPL transport, const char *address, const char *port, struct lyd_node **config)
1128{
1129 int ret = 0;
1130 const char *address_fmt, *port_fmt;
1131
Roytak7b9bf292023-10-04 14:06:38 +02001132 NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, address, port, config, 1);
roman5cbb6532023-06-22 12:53:17 +02001133
1134 if (transport == NC_TI_LIBSSH) {
1135 /* SSH path */
1136 address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-address";
1137 port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-port";
1138 } else if (transport == NC_TI_OPENSSL) {
1139 /* TLS path */
1140 address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-address";
1141 port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-port";
1142 } else {
1143 ERR(NULL, "Transport not supported.");
1144 ret = 1;
1145 goto cleanup;
1146 }
1147
Roytakb2794852023-10-18 14:30:22 +02001148 ret = nc_server_config_create(ctx, config, address, address_fmt, client_name, endpt_name);
roman5cbb6532023-06-22 12:53:17 +02001149 if (ret) {
1150 goto cleanup;
1151 }
1152
Roytakb2794852023-10-18 14:30:22 +02001153 ret = nc_server_config_create(ctx, config, port, port_fmt, client_name, endpt_name);
roman5cbb6532023-06-22 12:53:17 +02001154 if (ret) {
1155 goto cleanup;
1156 }
1157
1158cleanup:
1159 return ret;
1160}
1161
1162API int
Roytakb2794852023-10-18 14:30:22 +02001163nc_server_config_del_endpt(const char *endpt_name, struct lyd_node **config)
roman5cbb6532023-06-22 12:53:17 +02001164{
roman8ba6efa2023-07-12 15:27:52 +02001165 NC_CHECK_ARG_RET(NULL, config, 1);
roman5cbb6532023-06-22 12:53:17 +02001166
roman8ba6efa2023-07-12 15:27:52 +02001167 if (endpt_name) {
Roytakb2794852023-10-18 14:30:22 +02001168 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']", endpt_name);
roman8ba6efa2023-07-12 15:27:52 +02001169 } else {
Roytakb2794852023-10-18 14:30:22 +02001170 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint");
roman8ba6efa2023-07-12 15:27:52 +02001171 }
1172}
1173
1174API int
Roytakb2794852023-10-18 14:30:22 +02001175nc_server_config_del_ch_client(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001176{
1177 NC_CHECK_ARG_RET(NULL, config, 1);
1178
1179 if (ch_client_name) {
Roytakb2794852023-10-18 14:30:22 +02001180 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']", ch_client_name);
roman8ba6efa2023-07-12 15:27:52 +02001181 } else {
Roytakb2794852023-10-18 14:30:22 +02001182 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client");
roman8ba6efa2023-07-12 15:27:52 +02001183 }
1184}
1185
1186API int
Roytakb2794852023-10-18 14:30:22 +02001187nc_server_config_del_ch_endpt(const char *client_name, const char *endpt_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001188{
1189 NC_CHECK_ARG_RET(NULL, client_name, config, 1);
1190
1191 if (endpt_name) {
Roytakb2794852023-10-18 14:30:22 +02001192 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
roman8ba6efa2023-07-12 15:27:52 +02001193 "endpoints/endpoint[name='%s']", client_name, endpt_name);
1194 } else {
Roytakb2794852023-10-18 14:30:22 +02001195 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
roman8ba6efa2023-07-12 15:27:52 +02001196 "endpoints/endpoint", client_name);
1197 }
roman5cbb6532023-06-22 12:53:17 +02001198}
1199
roman142718b2023-06-29 09:15:29 +02001200API int
Roytakb2794852023-10-18 14:30:22 +02001201nc_server_config_add_keystore_asym_key(const struct ly_ctx *ctx, NC_TRANSPORT_IMPL ti, const char *asym_key_name,
roman13145912023-08-17 15:36:54 +02001202 const char *privkey_path, const char *pubkey_path, struct lyd_node **config)
roman142718b2023-06-29 09:15:29 +02001203{
1204 int ret = 0;
1205 char *privkey = NULL, *pubkey = NULL;
1206 NC_PRIVKEY_FORMAT privkey_type;
roman142718b2023-06-29 09:15:29 +02001207 const char *privkey_format, *pubkey_format;
1208
roman12c3d522023-07-26 13:39:30 +02001209 NC_CHECK_ARG_RET(NULL, ctx, asym_key_name, privkey_path, config, 1);
roman142718b2023-06-29 09:15:29 +02001210
1211 /* get the keys as a string from the given files */
roman13145912023-08-17 15:36:54 +02001212 if (ti == NC_TI_LIBSSH) {
Roytakb2794852023-10-18 14:30:22 +02001213 ret = nc_server_config_util_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_SSH, &privkey, &privkey_type, &pubkey);
roman13145912023-08-17 15:36:54 +02001214 } else if (ti == NC_TI_OPENSSL) {
Roytakb2794852023-10-18 14:30:22 +02001215 ret = nc_server_config_util_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_X509, &privkey, &privkey_type, &pubkey);
roman13145912023-08-17 15:36:54 +02001216 } else {
1217 ERR(NULL, "Only SSH and TLS transports can be used to create an asymmetric key pair in the keystore.");
1218 ret = 1;
1219 goto cleanup;
1220 }
roman142718b2023-06-29 09:15:29 +02001221 if (ret) {
1222 ERR(NULL, "Getting keys from file(s) failed.");
1223 goto cleanup;
1224 }
1225
1226 /* get pubkey format str */
roman13145912023-08-17 15:36:54 +02001227 if (ti == NC_TI_LIBSSH) {
roman142718b2023-06-29 09:15:29 +02001228 pubkey_format = "ietf-crypto-types:ssh-public-key-format";
roman13145912023-08-17 15:36:54 +02001229 } else {
1230 pubkey_format = "ietf-crypto-types:subject-public-key-info-format";
roman142718b2023-06-29 09:15:29 +02001231 }
1232
1233 /* get privkey identityref value */
Roytakb2794852023-10-18 14:30:22 +02001234 privkey_format = nc_server_config_util_privkey_format_to_identityref(privkey_type);
roman142718b2023-06-29 09:15:29 +02001235 if (!privkey_format) {
1236 ret = 1;
1237 goto cleanup;
1238 }
1239
Roytakb2794852023-10-18 14:30:22 +02001240 ret = nc_server_config_create(ctx, config, pubkey_format, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001241 "asymmetric-key[name='%s']/public-key-format", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001242 if (ret) {
1243 goto cleanup;
1244 }
1245
Roytakb2794852023-10-18 14:30:22 +02001246 ret = nc_server_config_create(ctx, config, pubkey, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001247 "asymmetric-key[name='%s']/public-key", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001248 if (ret) {
1249 goto cleanup;
1250 }
1251
Roytakb2794852023-10-18 14:30:22 +02001252 ret = nc_server_config_create(ctx, config, privkey_format, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001253 "asymmetric-key[name='%s']/private-key-format", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001254 if (ret) {
1255 goto cleanup;
1256 }
1257
Roytakb2794852023-10-18 14:30:22 +02001258 ret = nc_server_config_create(ctx, config, privkey, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001259 "asymmetric-key[name='%s']/cleartext-private-key", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001260 if (ret) {
1261 goto cleanup;
1262 }
1263
1264cleanup:
1265 free(privkey);
1266 free(pubkey);
1267 return ret;
1268}
1269
1270API int
Roytakb2794852023-10-18 14:30:22 +02001271nc_server_config_del_keystore_asym_key(const char *asym_key_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001272{
1273 NC_CHECK_ARG_RET(NULL, config, 1);
1274
roman12c3d522023-07-26 13:39:30 +02001275 if (asym_key_name) {
Roytakb2794852023-10-18 14:30:22 +02001276 return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']", asym_key_name);
roman8ba6efa2023-07-12 15:27:52 +02001277 } else {
Roytakb2794852023-10-18 14:30:22 +02001278 return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key");
roman8ba6efa2023-07-12 15:27:52 +02001279 }
1280}
1281
1282API int
Roytakb2794852023-10-18 14:30:22 +02001283nc_server_config_add_keystore_cert(const struct ly_ctx *ctx, const char *asym_key_name, const char *cert_name,
roman12c3d522023-07-26 13:39:30 +02001284 const char *cert_path, struct lyd_node **config)
1285{
1286 int ret = 0;
1287 char *cert = NULL;
1288
1289 NC_CHECK_ARG_RET(NULL, ctx, asym_key_name, cert_name, cert_path, config, 1);
1290
1291 /* get cert data */
Roytakb2794852023-10-18 14:30:22 +02001292 ret = nc_server_config_util_read_certificate(cert_path, &cert);
roman12c3d522023-07-26 13:39:30 +02001293 if (ret) {
1294 goto cleanup;
1295 }
1296
Roytakb2794852023-10-18 14:30:22 +02001297 ret = nc_server_config_create(ctx, config, cert, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001298 "asymmetric-key[name='%s']/certificates/certificate[name='%s']/cert-data", asym_key_name, cert_name);
1299
1300cleanup:
1301 free(cert);
1302 return ret;
1303}
1304
1305API int
Roytakb2794852023-10-18 14:30:22 +02001306nc_server_config_del_keystore_cert(const char *asym_key_name, const char *cert_name, struct lyd_node **config)
roman12c3d522023-07-26 13:39:30 +02001307{
1308 NC_CHECK_ARG_RET(NULL, asym_key_name, config, 1);
1309
1310 if (cert_name) {
Roytakb2794852023-10-18 14:30:22 +02001311 return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']/"
roman12c3d522023-07-26 13:39:30 +02001312 "certificates/certificate[name='%s']", asym_key_name, cert_name);
1313 } else {
Roytakb2794852023-10-18 14:30:22 +02001314 return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']/"
roman12c3d522023-07-26 13:39:30 +02001315 "certificates/certificate", asym_key_name);
1316 }
1317}
1318
1319API int
Roytakb2794852023-10-18 14:30:22 +02001320nc_server_config_add_truststore_pubkey(const struct ly_ctx *ctx, NC_TRANSPORT_IMPL ti, const char *pub_bag_name,
1321 const char *pubkey_name, const char *pubkey_path, struct lyd_node **config)
roman142718b2023-06-29 09:15:29 +02001322{
1323 int ret = 0;
1324 char *pubkey = NULL;
roman13145912023-08-17 15:36:54 +02001325 const char *pubkey_format = "ietf-crypto-types:ssh-public-key-format";
roman142718b2023-06-29 09:15:29 +02001326
roman12c3d522023-07-26 13:39:30 +02001327 NC_CHECK_ARG_RET(NULL, ctx, pub_bag_name, pubkey_name, pubkey_path, config, 1);
roman142718b2023-06-29 09:15:29 +02001328
Roytakb2794852023-10-18 14:30:22 +02001329 if (ti == NC_TI_LIBSSH) {
1330 ret = nc_server_config_util_get_ssh_pubkey_file(pubkey_path, &pubkey);
1331 } else if (ti == NC_TI_OPENSSL) {
1332 ret = nc_server_config_util_get_spki_pubkey_file(pubkey_path, &pubkey);
1333 } else {
1334 ERR(NULL, "Public key in the truststore can only be created for SSH or TLS transports.");
1335 ret = 1;
1336 goto cleanup;
1337 }
roman142718b2023-06-29 09:15:29 +02001338 if (ret) {
1339 goto cleanup;
1340 }
1341
Roytakb2794852023-10-18 14:30:22 +02001342 ret = nc_server_config_create(ctx, config, pubkey_format, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001343 "public-key-bag[name='%s']/public-key[name='%s']/public-key-format", pub_bag_name, pubkey_name);
roman142718b2023-06-29 09:15:29 +02001344 if (ret) {
1345 goto cleanup;
1346 }
1347
Roytakb2794852023-10-18 14:30:22 +02001348 ret = nc_server_config_create(ctx, config, pubkey, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001349 "public-key-bag[name='%s']/public-key[name='%s']/public-key", pub_bag_name, pubkey_name);
roman142718b2023-06-29 09:15:29 +02001350 if (ret) {
1351 goto cleanup;
1352 }
1353
1354cleanup:
1355 free(pubkey);
1356 return ret;
1357}
1358
roman8ba6efa2023-07-12 15:27:52 +02001359API int
Roytakb2794852023-10-18 14:30:22 +02001360nc_server_config_del_truststore_pubkey(const char *pub_bag_name,
roman8ba6efa2023-07-12 15:27:52 +02001361 const char *pubkey_name, struct lyd_node **config)
1362{
roman12c3d522023-07-26 13:39:30 +02001363 NC_CHECK_ARG_RET(NULL, pub_bag_name, config, 1);
roman8ba6efa2023-07-12 15:27:52 +02001364
1365 if (pubkey_name) {
Roytakb2794852023-10-18 14:30:22 +02001366 return nc_server_config_delete(config, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001367 "public-key-bag[name='%s']/public-key[name='%s']", pub_bag_name, pubkey_name);
roman8ba6efa2023-07-12 15:27:52 +02001368 } else {
Roytakb2794852023-10-18 14:30:22 +02001369 return nc_server_config_delete(config, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001370 "public-key-bag[name='%s']/public-key", pub_bag_name);
1371 }
1372}
1373
1374API int
Roytakb2794852023-10-18 14:30:22 +02001375nc_server_config_add_truststore_cert(const struct ly_ctx *ctx, const char *cert_bag_name, const char *cert_name,
roman12c3d522023-07-26 13:39:30 +02001376 const char *cert_path, struct lyd_node **config)
1377{
1378 int ret = 0;
1379 char *cert = NULL;
1380
1381 NC_CHECK_ARG_RET(NULL, ctx, cert_bag_name, cert_name, cert_path, config, 1);
1382
Roytakb2794852023-10-18 14:30:22 +02001383 ret = nc_server_config_util_read_certificate(cert_path, &cert);
roman12c3d522023-07-26 13:39:30 +02001384 if (ret) {
1385 goto cleanup;
1386 }
1387
Roytakb2794852023-10-18 14:30:22 +02001388 ret = nc_server_config_create(ctx, config, cert, "/ietf-truststore:truststore/certificate-bags/"
roman12c3d522023-07-26 13:39:30 +02001389 "certificate-bag[name='%s']/certificate[name='%s']/cert-data", cert_bag_name, cert_name);
1390 if (ret) {
1391 goto cleanup;
1392 }
1393
1394cleanup:
1395 free(cert);
1396 return ret;
1397}
1398
1399API int
Roytakb2794852023-10-18 14:30:22 +02001400nc_server_config_del_truststore_cert(const char *cert_bag_name,
roman12c3d522023-07-26 13:39:30 +02001401 const char *cert_name, struct lyd_node **config)
1402{
1403 NC_CHECK_ARG_RET(NULL, cert_bag_name, config, 1);
1404
1405 if (cert_name) {
Roytakb2794852023-10-18 14:30:22 +02001406 return nc_server_config_delete(config, "/ietf-truststore:truststore/certificate-bags/"
roman12c3d522023-07-26 13:39:30 +02001407 "certificate-bag[name='%s']/certificate[name='%s']", cert_bag_name, cert_name);
1408 } else {
Roytakb2794852023-10-18 14:30:22 +02001409 return nc_server_config_delete(config, "/ietf-truststore:truststore/certificate-bags/"
roman12c3d522023-07-26 13:39:30 +02001410 "certificate-bag[name='%s']/certificate", cert_bag_name);
roman8ba6efa2023-07-12 15:27:52 +02001411 }
1412}
1413
roman2eab4742023-06-06 10:00:26 +02001414#endif /* NC_ENABLED_SSH_TLS */
romanb6f44032023-06-30 15:07:56 +02001415
romanb6f44032023-06-30 15:07:56 +02001416API int
Roytakb2794852023-10-18 14:30:22 +02001417nc_server_config_add_unix_socket(const struct ly_ctx *ctx, const char *endpt_name, const char *path,
romand0b78372023-09-14 10:06:03 +02001418 mode_t mode, uid_t uid, gid_t gid, struct lyd_node **config)
1419{
1420 int ret = 0;
1421 char *tree_path = NULL;
1422 char buf[12] = {0};
1423
1424 NC_CHECK_ARG_RET(NULL, ctx, endpt_name, path, config, 1);
1425
1426 if (asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/libnetconf2-netconf-server:unix-socket", endpt_name) == -1) {
1427 ERRMEM;
1428 tree_path = NULL;
1429 ret = 1;
1430 goto cleanup;
1431 }
1432
1433 /* path to unix socket */
Roytakb2794852023-10-18 14:30:22 +02001434 ret = nc_server_config_append(ctx, tree_path, "path", path, config);
romand0b78372023-09-14 10:06:03 +02001435 if (ret) {
1436 goto cleanup;
1437 }
1438
1439 /* mode */
1440 if (mode != (mode_t)-1) {
1441 if (mode > 0777) {
1442 ERR(NULL, "Invalid mode value (%o).", mode);
1443 ret = 1;
1444 goto cleanup;
1445 }
1446
1447 sprintf(buf, "%o", mode);
Roytakb2794852023-10-18 14:30:22 +02001448 ret = nc_server_config_append(ctx, tree_path, "mode", buf, config);
romand0b78372023-09-14 10:06:03 +02001449 if (ret) {
1450 goto cleanup;
1451 }
1452 }
1453
1454 /* uid */
1455 if (uid != (uid_t)-1) {
1456 memset(buf, 0, 12);
1457 sprintf(buf, "%u", uid);
Roytakb2794852023-10-18 14:30:22 +02001458 ret = nc_server_config_append(ctx, tree_path, "uid", buf, config);
romand0b78372023-09-14 10:06:03 +02001459 if (ret) {
1460 goto cleanup;
1461 }
1462 }
1463
1464 /* gid */
1465 if (gid != (gid_t)-1) {
1466 memset(buf, 0, 12);
1467 sprintf(buf, "%u", gid);
Roytakb2794852023-10-18 14:30:22 +02001468 ret = nc_server_config_append(ctx, tree_path, "gid", buf, config);
romand0b78372023-09-14 10:06:03 +02001469 if (ret) {
1470 goto cleanup;
1471 }
1472 }
1473
1474cleanup:
1475 free(tree_path);
1476 return ret;
1477}
1478
1479API int
Roytakb2794852023-10-18 14:30:22 +02001480nc_server_config_add_ch_persistent(const struct ly_ctx *ctx, const char *ch_client_name, struct lyd_node **config)
romanb6f44032023-06-30 15:07:56 +02001481{
1482 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
1483
1484 /* delete periodic tree if exists */
Roytakb2794852023-10-18 14:30:22 +02001485 if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001486 "netconf-client[name='%s']/connection-type/periodic", ch_client_name)) {
1487 return 1;
1488 }
1489
Roytakb2794852023-10-18 14:30:22 +02001490 return nc_server_config_create(ctx, config, NULL, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001491 "netconf-client[name='%s']/connection-type/persistent", ch_client_name);
1492}
1493
1494API int
Roytakb2794852023-10-18 14:30:22 +02001495nc_server_config_add_ch_period(const struct ly_ctx *ctx, const char *ch_client_name, uint16_t period,
romanb6f44032023-06-30 15:07:56 +02001496 struct lyd_node **config)
1497{
1498 char buf[6] = {0};
1499
Roytak7b9bf292023-10-04 14:06:38 +02001500 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
romanb6f44032023-06-30 15:07:56 +02001501
1502 /* delete persistent tree if exists */
Roytakb2794852023-10-18 14:30:22 +02001503 if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001504 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1505 return 1;
1506 }
1507
1508 sprintf(buf, "%u", period);
Roytakb2794852023-10-18 14:30:22 +02001509 return nc_server_config_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001510 "netconf-client[name='%s']/connection-type/periodic/period", ch_client_name);
1511}
1512
1513API int
Roytakb2794852023-10-18 14:30:22 +02001514nc_server_config_del_ch_period(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001515{
1516 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1517
Roytakb2794852023-10-18 14:30:22 +02001518 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
roman8ba6efa2023-07-12 15:27:52 +02001519 "netconf-client[name='%s']/connection-type/periodic/period", ch_client_name);
1520}
1521
1522API int
Roytakb2794852023-10-18 14:30:22 +02001523nc_server_config_add_ch_anchor_time(const struct ly_ctx *ctx, const char *ch_client_name,
romanb6f44032023-06-30 15:07:56 +02001524 const char *anchor_time, struct lyd_node **config)
1525{
Roytak7b9bf292023-10-04 14:06:38 +02001526 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, anchor_time, config, 1);
romanb6f44032023-06-30 15:07:56 +02001527
1528 /* delete persistent tree if exists */
Roytakb2794852023-10-18 14:30:22 +02001529 if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001530 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1531 return 1;
1532 }
1533
Roytakb2794852023-10-18 14:30:22 +02001534 return nc_server_config_create(ctx, config, anchor_time, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001535 "netconf-client[name='%s']/connection-type/periodic/anchor-time", ch_client_name);
1536}
1537
1538API int
Roytakb2794852023-10-18 14:30:22 +02001539nc_server_config_del_ch_anchor_time(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001540{
1541 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1542
Roytakb2794852023-10-18 14:30:22 +02001543 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
roman8ba6efa2023-07-12 15:27:52 +02001544 "netconf-client[name='%s']/connection-type/periodic/anchor-time", ch_client_name);
1545}
1546
1547API int
Roytakb2794852023-10-18 14:30:22 +02001548nc_server_config_add_ch_idle_timeout(const struct ly_ctx *ctx, const char *ch_client_name,
romanb6f44032023-06-30 15:07:56 +02001549 uint16_t idle_timeout, struct lyd_node **config)
1550{
1551 char buf[6] = {0};
1552
Roytak7b9bf292023-10-04 14:06:38 +02001553 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
romanb6f44032023-06-30 15:07:56 +02001554
1555 /* delete persistent tree if exists */
Roytakb2794852023-10-18 14:30:22 +02001556 if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001557 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1558 return 1;
1559 }
1560
1561 sprintf(buf, "%u", idle_timeout);
Roytakb2794852023-10-18 14:30:22 +02001562 return nc_server_config_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001563 "netconf-client[name='%s']/connection-type/periodic/idle-timeout", ch_client_name);
1564}
1565
1566API int
Roytakb2794852023-10-18 14:30:22 +02001567nc_server_config_del_ch_idle_timeout(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001568{
1569 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1570
Roytakb2794852023-10-18 14:30:22 +02001571 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
roman8ba6efa2023-07-12 15:27:52 +02001572 "netconf-client[name='%s']/connection-type/periodic/idle-timeout", ch_client_name);
1573}
1574
1575API int
Roytakb2794852023-10-18 14:30:22 +02001576nc_server_config_add_ch_reconnect_strategy(const struct ly_ctx *ctx, const char *ch_client_name,
roman8ba6efa2023-07-12 15:27:52 +02001577 NC_CH_START_WITH start_with, uint16_t max_wait, uint8_t max_attempts, struct lyd_node **config)
romanb6f44032023-06-30 15:07:56 +02001578{
1579 int ret = 0;
1580 char *path = NULL;
1581 char buf[6] = {0};
1582 const char *start_with_val;
1583
1584 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
1585
1586 /* prepared the path */
1587 if (asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/"
1588 "netconf-client[name='%s']/reconnect-strategy", ch_client_name) == -1) {
1589 ERRMEM;
1590 path = NULL;
1591 ret = 1;
1592 goto cleanup;
1593 }
1594
1595 if (start_with) {
1596 /* get string value from enum */
1597 if (start_with == NC_CH_FIRST_LISTED) {
1598 start_with_val = "first-listed";
1599 } else if (start_with == NC_CH_LAST_CONNECTED) {
1600 start_with_val = "last-connected";
1601 } else {
1602 start_with_val = "random-selection";
1603 }
1604
Roytakb2794852023-10-18 14:30:22 +02001605 ret = nc_server_config_append(ctx, path, "start-with", start_with_val, config);
romanb6f44032023-06-30 15:07:56 +02001606 if (ret) {
1607 goto cleanup;
1608 }
1609 }
1610
1611 if (max_attempts) {
1612 sprintf(buf, "%u", max_attempts);
Roytakb2794852023-10-18 14:30:22 +02001613 ret = nc_server_config_append(ctx, path, "max-attempts", buf, config);
romanb6f44032023-06-30 15:07:56 +02001614 if (ret) {
1615 goto cleanup;
1616 }
1617 memset(buf, 0, 6);
1618 }
1619
1620 if (max_wait) {
1621 sprintf(buf, "%u", max_wait);
Roytakb2794852023-10-18 14:30:22 +02001622 ret = nc_server_config_append(ctx, path, "max-wait", buf, config);
romanb6f44032023-06-30 15:07:56 +02001623 if (ret) {
1624 goto cleanup;
1625 }
1626 }
1627
1628cleanup:
1629 free(path);
1630 return ret;
1631}
roman8ba6efa2023-07-12 15:27:52 +02001632
1633API int
Roytakb2794852023-10-18 14:30:22 +02001634nc_server_config_del_ch_reconnect_strategy(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001635{
1636 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1637
Roytakb2794852023-10-18 14:30:22 +02001638 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
roman8ba6efa2023-07-12 15:27:52 +02001639 "netconf-client[name='%s']/reconnect-strategy", ch_client_name);
1640}