blob: 2e8aed148e287a51becda767d7a1b03a769c6fde [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"
35#include "session.h"
36#include "session_p.h"
37
roman8ba6efa2023-07-12 15:27:52 +020038int
Roytakb2794852023-10-18 14:30:22 +020039nc_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 +020040{
41 int ret = 0;
42 va_list ap;
43 char *path = NULL;
44
Roytak7b9bf292023-10-04 14:06:38 +020045 NC_CHECK_ARG_RET(NULL, ctx, tree, path_fmt, 1);
46
roman8ba6efa2023-07-12 15:27:52 +020047 va_start(ap, path_fmt);
48
49 /* create the path from the format */
50 ret = vasprintf(&path, path_fmt, ap);
51 if (ret == -1) {
52 ERRMEM;
53 path = NULL;
54 goto cleanup;
55 }
56
57 /* create the nodes in the path */
roman5ef2a572023-08-18 15:45:44 +020058 if (!*tree) {
59 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
60 } else {
61 /* this could output NULL if no new nodes, lyd_find_path would fail then */
62 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, NULL);
63 }
roman8ba6efa2023-07-12 15:27:52 +020064 if (ret) {
65 goto cleanup;
66 }
67
68 /* set the node to the top level node */
69 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
70 if (ret) {
71 goto cleanup;
72 }
73
74 /* add all default nodes */
75 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
76 if (ret) {
77 goto cleanup;
78 }
79
80cleanup:
81 free(path);
82 va_end(ap);
83 return ret;
84}
85
86int
Roytakb2794852023-10-18 14:30:22 +020087nc_server_config_append(const struct ly_ctx *ctx, const char *parent_path, const char *child_name,
roman8ba6efa2023-07-12 15:27:52 +020088 const char *value, struct lyd_node **tree)
89{
90 int ret = 0;
91 char *path = NULL;
92
Roytak7b9bf292023-10-04 14:06:38 +020093 NC_CHECK_ARG_RET(NULL, ctx, parent_path, child_name, tree, 1);
94
roman8ba6efa2023-07-12 15:27:52 +020095 /* create the path by appending child to the parent path */
96 ret = asprintf(&path, "%s/%s", parent_path, child_name);
97 if (ret == -1) {
98 ERRMEM;
99 path = NULL;
100 goto cleanup;
101 }
102
103 /* create the nodes in the path */
roman5ef2a572023-08-18 15:45:44 +0200104 if (!*tree) {
105 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
106 } else {
107 /* this could output NULL if no new nodes, lyd_find_path would fail then */
108 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, NULL);
109 }
roman8ba6efa2023-07-12 15:27:52 +0200110 if (ret) {
111 goto cleanup;
112 }
113
114 /* set the node to the top level node */
115 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
116 if (ret) {
117 goto cleanup;
118 }
119
120 /* add all default nodes */
121 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
122 if (ret) {
123 goto cleanup;
124 }
125
126cleanup:
127 free(path);
128 return ret;
129}
130
131int
Roytakb2794852023-10-18 14:30:22 +0200132nc_server_config_delete(struct lyd_node **tree, const char *path_fmt, ...)
Roytak7b9bf292023-10-04 14:06:38 +0200133{
134 int ret = 0;
135 va_list ap;
136 char *path = NULL;
137 struct lyd_node *sub = NULL;
138
139 NC_CHECK_ARG_RET(NULL, tree, path_fmt, 1);
140
141 va_start(ap, path_fmt);
142
143 /* create the path from the format */
144 ret = vasprintf(&path, path_fmt, ap);
145 if (ret == -1) {
146 ERRMEM;
147 path = NULL;
148 goto cleanup;
149 }
150
151 /* find the node we want to delete */
152 ret = lyd_find_path(*tree, path, 0, &sub);
153 if (ret) {
154 goto cleanup;
155 }
156
157 lyd_free_tree(sub);
158
159 /* set the node to top level container */
160 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
161 if (ret) {
162 goto cleanup;
163 }
164
165 /* add all default nodes */
166 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
167 if (ret) {
168 goto cleanup;
169 }
170
171cleanup:
172 free(path);
173 va_end(ap);
174 return ret;
175}
176
177int
Roytakb2794852023-10-18 14:30:22 +0200178nc_server_config_check_delete(struct lyd_node **tree, const char *path_fmt, ...)
roman8ba6efa2023-07-12 15:27:52 +0200179{
180 int ret = 0;
181 va_list ap;
182 char *path = NULL;
183 struct lyd_node *sub = NULL;
184
Roytak7b9bf292023-10-04 14:06:38 +0200185 NC_CHECK_ARG_RET(NULL, tree, path_fmt, 1);
186
roman8ba6efa2023-07-12 15:27:52 +0200187 va_start(ap, path_fmt);
188
189 /* create the path from the format */
190 ret = vasprintf(&path, path_fmt, ap);
191 if (ret == -1) {
192 ERRMEM;
193 path = NULL;
194 goto cleanup;
195 }
196
197 /* find the node we want to delete */
198 ret = lyd_find_path(*tree, path, 0, &sub);
199 if ((ret == LY_EINCOMPLETE) || (ret == LY_ENOTFOUND)) {
200 ret = 0;
201 goto cleanup;
202 } else if (ret) {
203 ERR(NULL, "Unable to delete node in the path \"%s\".", path);
204 goto cleanup;
205 }
206
207 lyd_free_tree(sub);
208
209 /* set the node to top level container */
210 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
211 if (ret) {
212 goto cleanup;
213 }
214
215cleanup:
216 free(path);
217 va_end(ap);
218 return ret;
219}
220
roman2eab4742023-06-06 10:00:26 +0200221#ifdef NC_ENABLED_SSH_TLS
222
roman3f9b65c2023-06-05 14:26:58 +0200223const char *
Roytakb2794852023-10-18 14:30:22 +0200224nc_server_config_util_privkey_format_to_identityref(NC_PRIVKEY_FORMAT format)
roman3f9b65c2023-06-05 14:26:58 +0200225{
226 switch (format) {
227 case NC_PRIVKEY_FORMAT_RSA:
228 return "ietf-crypto-types:rsa-private-key-format";
229 case NC_PRIVKEY_FORMAT_EC:
230 return "ietf-crypto-types:ec-private-key-format";
231 case NC_PRIVKEY_FORMAT_X509:
roman13145912023-08-17 15:36:54 +0200232 return "libnetconf2-netconf-server:private-key-info-format";
roman3f9b65c2023-06-05 14:26:58 +0200233 case NC_PRIVKEY_FORMAT_OPENSSH:
234 return "libnetconf2-netconf-server:openssh-private-key-format";
235 default:
236 ERR(NULL, "Private key type not supported.");
237 return NULL;
238 }
239}
240
roman13145912023-08-17 15:36:54 +0200241static int
Roytakb2794852023-10-18 14:30:22 +0200242nc_server_config_util_pubkey_bin_to_b64(const unsigned char *pub_bin, int bin_len, char **pubkey)
roman13145912023-08-17 15:36:54 +0200243{
244 int ret = 0, b64_len;
245 char *pub_b64 = NULL;
246
Roytak7b9bf292023-10-04 14:06:38 +0200247 NC_CHECK_ARG_RET(NULL, pub_bin, bin_len, pubkey, 1);
248
roman13145912023-08-17 15:36:54 +0200249 /* get b64 buffer len, for ever 3 bytes of bin 4 bytes of b64 + NULL terminator */
250 if (bin_len % 3 == 0) {
251 pub_b64 = malloc((bin_len / 3) * 4 + 1);
252 } else {
253 /* bin len not divisible by 3, need to add 4 bytes for some padding so that the len is divisible by 4 */
254 pub_b64 = malloc((bin_len / 3) * 4 + 4 + 1);
255 }
256 if (!pub_b64) {
257 ERRMEM;
258 ret = 1;
259 goto cleanup;
260 }
261
262 /* bin to b64 */
263 b64_len = EVP_EncodeBlock((unsigned char *)pub_b64, pub_bin, bin_len);
264 *pubkey = strndup(pub_b64, b64_len);
265 if (!*pubkey) {
266 ERRMEM;
267 ret = 1;
268 goto cleanup;
269 }
270
271cleanup:
272 free(pub_b64);
273 return ret;
274}
275
276static int
Roytakb2794852023-10-18 14:30:22 +0200277nc_server_config_util_bn_to_bin(const BIGNUM *bn, unsigned char **bin, int *bin_len)
roman13145912023-08-17 15:36:54 +0200278{
279 int ret = 0;
280 unsigned char *bin_tmp = NULL;
281
282 NC_CHECK_ARG_RET(NULL, bn, bin, bin_len, 1);
283
284 *bin = NULL;
285
286 /* prepare buffer for converting BN to binary */
287 bin_tmp = calloc(BN_num_bytes(bn), sizeof *bin_tmp);
288 if (!bin_tmp) {
289 ERRMEM;
290 return 1;
291 }
292
293 /* convert to binary */
294 *bin_len = BN_bn2bin(bn, bin_tmp);
295
296 /* if the highest bit in the MSB is set a byte with the value 0 has to be prepended */
297 if (bin_tmp[0] & 0x80) {
298 *bin = malloc(*bin_len + 1);
299 if (!*bin) {
300 ERRMEM;
301 ret = 1;
302 goto cleanup;
303 }
304
305 (*bin)[0] = 0;
306 memcpy(*bin + 1, bin_tmp, *bin_len);
307 (*bin_len)++;
308 } else {
309 *bin = malloc(*bin_len);
310 if (!*bin) {
311 ERRMEM;
312 ret = 1;
313 goto cleanup;
314 }
315
316 memcpy(*bin, bin_tmp, *bin_len);
317 }
318
319cleanup:
320 free(bin_tmp);
321 return ret;
322}
323
324/* ssh pubkey defined in RFC 4253 section 6.6 */
325static int
Roytakb2794852023-10-18 14:30:22 +0200326nc_server_config_util_evp_pkey_to_ssh_pubkey(EVP_PKEY *pkey, char **pubkey)
roman13145912023-08-17 15:36:54 +0200327{
328 int ret = 0, e_len, n_len, p_len, bin_len;
329 BIGNUM *e = NULL, *n = NULL, *p = NULL;
330 unsigned char *e_bin = NULL, *n_bin = NULL, *p_bin = NULL, *bin = NULL, *bin_tmp;
331 const char *algorithm_name, *curve_name;
332 char *ec_group = NULL;
333 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;
334 size_t ec_group_len;
335
Roytak7b9bf292023-10-04 14:06:38 +0200336 NC_CHECK_ARG_RET(NULL, pkey, pubkey, 1);
337
roman13145912023-08-17 15:36:54 +0200338 if (EVP_PKEY_is_a(pkey, "RSA")) {
339 /* RSA key */
340 algorithm_name = "ssh-rsa";
341
342 /* get the public key params */
343 if (!EVP_PKEY_get_bn_param(pkey, "e", &e) || !EVP_PKEY_get_bn_param(pkey, "n", &n)) {
344 ERR(NULL, "Getting public key parameters from RSA private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
345 ret = 1;
346 goto cleanup;
347 }
348
349 /* BIGNUM to bin */
Roytakb2794852023-10-18 14:30:22 +0200350 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 +0200351 ret = 1;
352 goto cleanup;
353 }
354
355 alg_name_len = strlen(algorithm_name);
356 /* buffer for public key in binary, which looks like this:
357 * alg_name len (4 bytes), alg_name, PK exponent len (4 bytes), PK exponent, modulus len (4 bytes), modulus
358 */
359 bin_len = 4 + alg_name_len + 4 + e_len + 4 + n_len;
360 bin = malloc(bin_len);
361 if (!bin) {
362 ERRMEM;
363 ret = 1;
364 goto cleanup;
365 }
366
367 /* to network byte order (big endian) */
368 alg_name_len_be = htonl(alg_name_len);
369 e_len_be = htonl(e_len);
370 n_len_be = htonl(n_len);
371
372 /* create the public key in binary */
373 bin_tmp = bin;
374 memcpy(bin_tmp, &alg_name_len_be, 4);
375 bin_tmp += 4;
376 memcpy(bin_tmp, algorithm_name, alg_name_len);
377 bin_tmp += alg_name_len;
378 memcpy(bin_tmp, &e_len_be, 4);
379 bin_tmp += 4;
380 memcpy(bin_tmp, e_bin, e_len);
381 bin_tmp += e_len;
382 memcpy(bin_tmp, &n_len_be, 4);
383 bin_tmp += 4;
384 memcpy(bin_tmp, n_bin, n_len);
385 } else if (EVP_PKEY_is_a(pkey, "EC")) {
386 /* EC Private key, get it's group first */
387 /* get group len */
388 ret = EVP_PKEY_get_utf8_string_param(pkey, "group", NULL, 0, &ec_group_len);
389 if (!ret) {
390 ret = 1;
391 goto cleanup;
392 }
393 /* alloc mem for group + 1 for \0 */
394 ec_group = malloc(ec_group_len + 1);
395 if (!ec_group) {
396 ERRMEM;
397 ret = 1;
398 goto cleanup;
399 }
400 /* get the group */
401 ret = EVP_PKEY_get_utf8_string_param(pkey, "group", ec_group, ec_group_len + 1, NULL);
402 if (!ret) {
403 ERR(NULL, "Getting public key parameter from EC private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
404 ret = 1;
405 goto cleanup;
406 }
407
408 /* get alg and curve names */
409 if (!strcmp(ec_group, "P-256") || !strcmp(ec_group, "secp256r1") || !strcmp(ec_group, "prime256v1")) {
410 algorithm_name = "ecdsa-sha2-nistp256";
411 curve_name = "nistp256";
412 } else if (!strcmp(ec_group, "P-384") || !strcmp(ec_group, "secp384r1")) {
413 algorithm_name = "ecdsa-sha2-nistp384";
414 curve_name = "nistp384";
415 } else if (!strcmp(ec_group, "P-521") || !strcmp(ec_group, "secp521r1")) {
416 algorithm_name = "ecdsa-sha2-nistp521";
417 curve_name = "nistp521";
418 } else {
419 ERR(NULL, "EC group \"%s\" not supported.", ec_group);
420 ret = 1;
421 goto cleanup;
422 }
423
424 /* get the public key - p, which is a point on the elliptic curve */
425 ret = EVP_PKEY_get_bn_param(pkey, "p", &p);
426 if (!ret) {
427 ERR(NULL, "Getting public key point from the EC private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
428 ret = 1;
429 goto cleanup;
430 }
431
432 /* prepare buffer for converting p to binary */
433 p_bin = malloc(BN_num_bytes(p));
434 if (!p_bin) {
435 ERRMEM;
436 ret = 1;
437 goto cleanup;
438 }
439 /* convert to binary */
440 p_len = BN_bn2bin(p, p_bin);
441
442 alg_name_len = strlen(algorithm_name);
443 curve_name_len = strlen(curve_name);
444 /* buffer for public key in binary, which looks like so:
445 * alg_name len (4 bytes), alg_name, curve_name len (4 bytes), curve_name, PK point p len (4 bytes), PK point p
446 */
447 bin_len = 4 + alg_name_len + 4 + curve_name_len + 4 + p_len;
448 bin = malloc(bin_len);
449 if (!bin) {
450 ERRMEM;
451 ret = 1;
452 goto cleanup;
453 }
454
455 /* to network byte order (big endian) */
456 alg_name_len_be = htonl(alg_name_len);
457 curve_name_len_be = htonl(curve_name_len);
458 p_len_be = htonl(p_len);
459
460 /* create the public key in binary */
461 bin_tmp = bin;
462 memcpy(bin_tmp, &alg_name_len_be, 4);
463 bin_tmp += 4;
464 memcpy(bin_tmp, algorithm_name, alg_name_len);
465 bin_tmp += alg_name_len;
466 memcpy(bin_tmp, &curve_name_len_be, 4);
467 bin_tmp += 4;
468 memcpy(bin_tmp, curve_name, curve_name_len);
469 bin_tmp += curve_name_len;
470 memcpy(bin_tmp, &p_len_be, 4);
471 bin_tmp += 4;
472 memcpy(bin_tmp, p_bin, p_len);
473 } else if (EVP_PKEY_is_a(pkey, "ED25519")) {
474 ERR(NULL, "Generating PEM ED25519 key from OpenSSH is not supported by libssh yet.");
475 ret = 1;
476 goto cleanup;
477 } else {
478 ERR(NULL, "Unable to generate public key from private key (Private key type not supported).");
479 ret = 1;
480 goto cleanup;
481 }
482
Roytak7b9bf292023-10-04 14:06:38 +0200483 /* convert created bin to b64 */
Roytakb2794852023-10-18 14:30:22 +0200484 ret = nc_server_config_util_pubkey_bin_to_b64(bin, bin_len, pubkey);
roman13145912023-08-17 15:36:54 +0200485 if (ret) {
486 ERR(NULL, "Converting public key from binary to base64 failed.");
487 goto cleanup;
488 }
489
490cleanup:
491 free(bin);
492 free(e_bin);
493 free(n_bin);
494 free(ec_group);
495 free(p_bin);
496 BN_free(e);
497 BN_free(n);
498 BN_free(p);
499 return ret;
500}
501
502/* spki = subject public key info */
503static int
Roytakb2794852023-10-18 14:30:22 +0200504nc_server_config_util_evp_pkey_to_spki_pubkey(EVP_PKEY *pkey, char **pubkey)
roman13145912023-08-17 15:36:54 +0200505{
506 int ret = 0, len;
507 BIO *bio = NULL;
508 char *pub_b64 = NULL;
509
Roytak7b9bf292023-10-04 14:06:38 +0200510 NC_CHECK_ARG_RET(NULL, pkey, pubkey, 1);
511
roman13145912023-08-17 15:36:54 +0200512 bio = BIO_new(BIO_s_mem());
513 if (!bio) {
514 ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
515 ret = 1;
516 goto cleanup;
517 }
518
519 /* write the evp_pkey contents to bio */
520 if (!PEM_write_bio_PUBKEY(bio, pkey)) {
521 ERR(NULL, "Writing public key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
522 ret = 1;
523 goto cleanup;
524 }
525
526 /* read the pubkey from bio */
527 len = BIO_get_mem_data(bio, &pub_b64);
528 if (len <= 0) {
529 ERR(NULL, "Reading base64 private key from BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
530 ret = 1;
531 goto cleanup;
532 }
533
534 /* copy the public key without the header and footer */
535 *pubkey = strndup(pub_b64 + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER),
536 len - strlen(NC_SUBJECT_PUBKEY_INFO_HEADER) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER));
537 if (!*pubkey) {
538 ERRMEM;
539 ret = 1;
540 goto cleanup;
541 }
542
543cleanup:
544 BIO_free(bio);
545 return ret;
546}
547
roman3f9b65c2023-06-05 14:26:58 +0200548int
Roytakb2794852023-10-18 14:30:22 +0200549nc_server_config_util_read_certificate(const char *cert_path, char **cert)
roman3f9b65c2023-06-05 14:26:58 +0200550{
551 int ret = 0, cert_len;
552 X509 *x509 = NULL;
553 FILE *f = NULL;
554 BIO *bio = NULL;
555 char *c = NULL;
556
Roytak7b9bf292023-10-04 14:06:38 +0200557 NC_CHECK_ARG_RET(NULL, cert_path, cert, 1);
roman3f9b65c2023-06-05 14:26:58 +0200558
559 f = fopen(cert_path, "r");
560 if (!f) {
561 ERR(NULL, "Unable to open certificate file \"%s\".", cert_path);
562 ret = 1;
563 goto cleanup;
564 }
565
566 /* load the cert into memory */
567 x509 = PEM_read_X509(f, NULL, NULL, NULL);
568 if (!x509) {
569 ret = -1;
570 goto cleanup;
571 }
572
573 bio = BIO_new(BIO_s_mem());
574 if (!bio) {
575 ret = -1;
576 goto cleanup;
577 }
578
579 ret = PEM_write_bio_X509(bio, x509);
580 if (!ret) {
581 ret = -1;
582 goto cleanup;
583 }
584
585 cert_len = BIO_pending(bio);
586 if (cert_len <= 0) {
587 ret = -1;
588 goto cleanup;
589 }
590
591 c = malloc(cert_len + 1);
592 if (!c) {
593 ERRMEM;
594 ret = 1;
595 goto cleanup;
596 }
597
598 /* read the cert from bio */
599 ret = BIO_read(bio, c, cert_len);
600 if (ret <= 0) {
601 ret = -1;
602 goto cleanup;
603 }
604 c[cert_len] = '\0';
605
606 /* strip the cert of the header and footer */
607 *cert = strdup(c + strlen(NC_PEM_CERTIFICATE_HEADER));
608 if (!*cert) {
609 ERRMEM;
610 ret = 1;
611 goto cleanup;
612 }
613
614 (*cert)[strlen(*cert) - strlen(NC_PEM_CERTIFICATE_FOOTER)] = '\0';
615
616 ret = 0;
617
618cleanup:
619 if (ret == -1) {
620 ERR(NULL, "Error getting certificate from file \"%s\" (OpenSSL Error): \"%s\".", cert_path, ERR_reason_error_string(ERR_get_error()));
621 ret = 1;
622 }
623 if (f) {
624 fclose(f);
625 }
626
627 BIO_free(bio);
628 X509_free(x509);
629 free(c);
630 return ret;
631}
632
633static int
Roytakb2794852023-10-18 14:30:22 +0200634nc_server_config_util_read_pubkey_ssh2(FILE *f, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200635{
636 char *buffer = NULL;
637 size_t size = 0, pubkey_len = 0;
638 void *tmp;
639 ssize_t read;
640 int ret = 0;
641
Roytak7b9bf292023-10-04 14:06:38 +0200642 NC_CHECK_ARG_RET(NULL, f, pubkey, 1);
643
644 /* read lines from the file and create the public key without NL from it */
roman3f9b65c2023-06-05 14:26:58 +0200645 while ((read = getline(&buffer, &size, f)) > 0) {
646 if (!strncmp(buffer, "----", 4)) {
Roytak7b9bf292023-10-04 14:06:38 +0200647 /* skip header and footer */
roman3f9b65c2023-06-05 14:26:58 +0200648 continue;
649 }
650
651 if (!strncmp(buffer, "Comment:", 8)) {
Roytak7b9bf292023-10-04 14:06:38 +0200652 /* skip a comment */
roman3f9b65c2023-06-05 14:26:58 +0200653 continue;
654 }
655
656 if (buffer[read - 1] == '\n') {
Roytak7b9bf292023-10-04 14:06:38 +0200657 /* avoid NL */
roman3f9b65c2023-06-05 14:26:58 +0200658 read--;
659 }
660
661 tmp = realloc(*pubkey, pubkey_len + read + 1);
662 if (!tmp) {
663 ERRMEM;
664 ret = 1;
665 goto cleanup;
666 }
667
668 *pubkey = tmp;
669 memcpy(*pubkey + pubkey_len, buffer, read);
670 pubkey_len += read;
671 }
672
673 if (!pubkey_len) {
674 ERR(NULL, "Unexpected public key format.");
675 ret = 1;
676 goto cleanup;
677 }
678
679 (*pubkey)[pubkey_len] = '\0';
680
681cleanup:
682 free(buffer);
683 return ret;
684}
685
686static int
Roytakb2794852023-10-18 14:30:22 +0200687nc_server_config_util_read_pubkey_openssl(FILE *f, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200688{
689 int ret = 0;
roman13145912023-08-17 15:36:54 +0200690 EVP_PKEY *pub_pkey = NULL;
691
692 NC_CHECK_ARG_RET(NULL, f, pubkey, 1);
roman3f9b65c2023-06-05 14:26:58 +0200693
694 /* read the pubkey from file */
roman13145912023-08-17 15:36:54 +0200695 pub_pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
696 if (!pub_pkey) {
697 ERR(NULL, "Reading public key from file failed (%s).", ERR_reason_error_string(ERR_get_error()));
698 return 1;
roman3f9b65c2023-06-05 14:26:58 +0200699 }
700
Roytakb2794852023-10-18 14:30:22 +0200701 ret = nc_server_config_util_evp_pkey_to_ssh_pubkey(pub_pkey, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200702
roman13145912023-08-17 15:36:54 +0200703 EVP_PKEY_free(pub_pkey);
roman3f9b65c2023-06-05 14:26:58 +0200704 return ret;
705}
706
roman3f9b65c2023-06-05 14:26:58 +0200707static int
Roytakb2794852023-10-18 14:30:22 +0200708nc_server_config_util_read_pubkey_libssh(const char *pubkey_path, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200709{
710 int ret = 0;
711 ssh_key pub_sshkey = NULL;
712
roman13145912023-08-17 15:36:54 +0200713 NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
714
roman3f9b65c2023-06-05 14:26:58 +0200715 ret = ssh_pki_import_pubkey_file(pubkey_path, &pub_sshkey);
716 if (ret) {
717 ERR(NULL, "Importing public key from file \"%s\" failed.", pubkey_path);
718 return ret;
719 }
720
721 ret = ssh_pki_export_pubkey_base64(pub_sshkey, pubkey);
722 if (ret) {
roman13145912023-08-17 15:36:54 +0200723 ERR(NULL, "Importing pubkey failed.");
724 goto cleanup;
roman3f9b65c2023-06-05 14:26:58 +0200725 }
726
roman13145912023-08-17 15:36:54 +0200727cleanup:
roman3f9b65c2023-06-05 14:26:58 +0200728 ssh_key_free(pub_sshkey);
roman13145912023-08-17 15:36:54 +0200729 return 0;
roman3f9b65c2023-06-05 14:26:58 +0200730}
731
roman3f9b65c2023-06-05 14:26:58 +0200732int
Roytakb2794852023-10-18 14:30:22 +0200733nc_server_config_util_get_ssh_pubkey_file(const char *pubkey_path, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200734{
735 int ret = 0;
736 FILE *f = NULL;
737 char *header = NULL;
738 size_t len = 0;
739
roman13145912023-08-17 15:36:54 +0200740 NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
roman3f9b65c2023-06-05 14:26:58 +0200741
742 *pubkey = NULL;
743
744 f = fopen(pubkey_path, "r");
745 if (!f) {
746 ERR(NULL, "Unable to open file \"%s\".", pubkey_path);
747 ret = 1;
748 goto cleanup;
749 }
750
roman13145912023-08-17 15:36:54 +0200751 /* read the header */
roman3f9b65c2023-06-05 14:26:58 +0200752 if (getline(&header, &len, f) < 0) {
753 ERR(NULL, "Error reading header from file \"%s\".", pubkey_path);
754 ret = 1;
755 goto cleanup;
756 }
757 rewind(f);
758
759 if (!strncmp(header, NC_SUBJECT_PUBKEY_INFO_HEADER, strlen(NC_SUBJECT_PUBKEY_INFO_HEADER))) {
760 /* it's subject public key info public key */
Roytakb2794852023-10-18 14:30:22 +0200761 ret = nc_server_config_util_read_pubkey_openssl(f, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200762 } else if (!strncmp(header, NC_SSH2_PUBKEY_HEADER, strlen(NC_SSH2_PUBKEY_HEADER))) {
763 /* it's ssh2 public key */
Roytakb2794852023-10-18 14:30:22 +0200764 ret = nc_server_config_util_read_pubkey_ssh2(f, pubkey);
roman7fdc84d2023-06-06 13:14:53 +0200765 } else {
roman3f9b65c2023-06-05 14:26:58 +0200766 /* it's probably OpenSSH public key */
Roytakb2794852023-10-18 14:30:22 +0200767 ret = nc_server_config_util_read_pubkey_libssh(pubkey_path, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200768 }
roman3f9b65c2023-06-05 14:26:58 +0200769 if (ret) {
770 ERR(NULL, "Error getting public key from file \"%s\".", pubkey_path);
771 goto cleanup;
772 }
773
774cleanup:
775 if (f) {
776 fclose(f);
777 }
778
779 free(header);
roman3f9b65c2023-06-05 14:26:58 +0200780 return ret;
781}
782
roman3f9b65c2023-06-05 14:26:58 +0200783int
Roytakb2794852023-10-18 14:30:22 +0200784nc_server_config_util_get_spki_pubkey_file(const char *pubkey_path, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200785{
786 int ret = 0;
roman13145912023-08-17 15:36:54 +0200787 FILE *f = NULL;
788 EVP_PKEY *pub_pkey = NULL;
roman3f9b65c2023-06-05 14:26:58 +0200789
roman13145912023-08-17 15:36:54 +0200790 NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
roman3f9b65c2023-06-05 14:26:58 +0200791
roman3f9b65c2023-06-05 14:26:58 +0200792 *pubkey = NULL;
793
roman13145912023-08-17 15:36:54 +0200794 f = fopen(pubkey_path, "r");
795 if (!f) {
796 ERR(NULL, "Unable to open file \"%s\".", pubkey_path);
797 ret = 1;
798 goto cleanup;
799 }
800
801 /* read the pubkey from file */
802 pub_pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
803 if (!pub_pkey) {
804 ERR(NULL, "Reading public key from file failed (%s).", ERR_reason_error_string(ERR_get_error()));
805 return 1;
806 }
807
Roytakb2794852023-10-18 14:30:22 +0200808 ret = nc_server_config_util_evp_pkey_to_spki_pubkey(pub_pkey, pubkey);
roman13145912023-08-17 15:36:54 +0200809 if (ret) {
810 goto cleanup;
811 }
812
813cleanup:
814 if (f) {
815 fclose(f);
816 }
817
818 EVP_PKEY_free(pub_pkey);
819 return ret;
820}
821
822static int
Roytakb2794852023-10-18 14:30:22 +0200823nc_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 +0200824{
825 char *privkey_header = NULL;
826 size_t len = 0;
827
Roytak7b9bf292023-10-04 14:06:38 +0200828 NC_CHECK_ARG_RET(NULL, f_privkey, privkey_path, privkey_format, 1);
829
roman13145912023-08-17 15:36:54 +0200830 /* read header */
831 if (getline(&privkey_header, &len, f_privkey) < 0) {
832 ERR(NULL, "Error reading header from file \"%s\".", privkey_path);
833 return 1;
834 }
835
836 if (!strncmp(privkey_header, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) {
837 /* it's PKCS8 (X.509) private key */
838 *privkey_format = NC_PRIVKEY_FORMAT_X509;
839 } else if (!strncmp(privkey_header, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) {
840 /* it's OpenSSH private key */
841 *privkey_format = NC_PRIVKEY_FORMAT_OPENSSH;
842 } else if (!strncmp(privkey_header, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) {
843 /* it's RSA privkey in PKCS1 format */
844 *privkey_format = NC_PRIVKEY_FORMAT_RSA;
845 } else if (!strncmp(privkey_header, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) {
846 /* it's EC privkey in SEC1 format */
847 *privkey_format = NC_PRIVKEY_FORMAT_EC;
848 } else {
849 ERR(NULL, "Private key format (%s) not supported.", privkey_header);
850 free(privkey_header);
851 return 1;
852 }
853
854 /* reset the reading head */
855 rewind(f_privkey);
856 free(privkey_header);
857 return 0;
858}
859
860static int
Roytakb2794852023-10-18 14:30:22 +0200861nc_server_config_util_get_privkey_openssl(const char *privkey_path, FILE *f_privkey, char **privkey, EVP_PKEY **pkey)
roman13145912023-08-17 15:36:54 +0200862{
863 int ret = 0, len;
864 BIO *bio = NULL;
865 char *priv_b64 = NULL;
866
Roytak7b9bf292023-10-04 14:06:38 +0200867 NC_CHECK_ARG_RET(NULL, privkey_path, f_privkey, privkey, pkey, 1);
868
roman13145912023-08-17 15:36:54 +0200869 bio = BIO_new(BIO_s_mem());
870 if (!bio) {
871 ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
872 ret = 1;
873 goto cleanup;
874 }
875
876 /* read the privkey file, create EVP_PKEY */
877 *pkey = PEM_read_PrivateKey(f_privkey, NULL, NULL, NULL);
878 if (!*pkey) {
879 ERR(NULL, "Getting private key from file \"%s\" failed (%s).", privkey_path, ERR_reason_error_string(ERR_get_error()));
880 ret = 1;
881 goto cleanup;
882 }
883
884 /* write the privkey to bio */
885 if (!PEM_write_bio_PrivateKey(bio, *pkey, NULL, NULL, 0, NULL, NULL)) {
886 ERR(NULL, "Writing private key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
887 ret = 1;
888 goto cleanup;
889 }
890
891 /* read the privkey from bio */
892 len = BIO_get_mem_data(bio, &priv_b64);
893 if (len <= 0) {
894 ERR(NULL, "Reading base64 private key from BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
895 ret = 1;
896 goto cleanup;
897 }
898
899 *privkey = strndup(priv_b64, len);
Roytak7b9bf292023-10-04 14:06:38 +0200900 if (!*privkey) {
901 ERRMEM;
902 ret = 1;
903 goto cleanup;
904 }
roman13145912023-08-17 15:36:54 +0200905
906cleanup:
Roytak7b9bf292023-10-04 14:06:38 +0200907 /* priv_b64 is freed with BIO */
roman13145912023-08-17 15:36:54 +0200908 BIO_free(bio);
909 return ret;
910}
911
912static int
Roytakb2794852023-10-18 14:30:22 +0200913nc_server_config_util_get_privkey_libssh(const char *privkey_path, char **privkey, EVP_PKEY **pkey)
roman13145912023-08-17 15:36:54 +0200914{
915 int ret = 0;
916 BIO *bio = NULL;
917 char *priv_b64 = NULL;
918 ssh_key key = NULL;
919
Roytak7b9bf292023-10-04 14:06:38 +0200920 NC_CHECK_ARG_RET(NULL, privkey_path, privkey, pkey, 1);
921
roman13145912023-08-17 15:36:54 +0200922 ret = ssh_pki_import_privkey_file(privkey_path, NULL, NULL, NULL, &key);
923 if (ret) {
924 ERR(NULL, "Importing privkey from file \"%s\" failed.", privkey_path);
925 goto cleanup;
926 }
927
928 /* exports the key in a format in which OpenSSL can read it */
929 ret = ssh_pki_export_privkey_base64(key, NULL, NULL, NULL, &priv_b64);
930 if (ret) {
931 ERR(NULL, "Exporting privkey to base64 failed.");
932 goto cleanup;
933 }
934
935 bio = BIO_new(BIO_s_mem());
936 if (!bio) {
937 ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
938 ret = 1;
939 goto cleanup;
940 }
941
942 ret = BIO_write(bio, priv_b64, strlen(priv_b64));
943 if (ret <= 0) {
944 ERR(NULL, "Writing private key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
945 ret = 1;
946 goto cleanup;
947 }
948
949 /* create EVP_PKEY from the b64 */
950 *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
951 if (!*pkey) {
952 ERR(NULL, "Getting private key from file \"%s\" failed (%s).", privkey_path, ERR_reason_error_string(ERR_get_error()));
953 ret = 1;
954 goto cleanup;
955 }
956
957 *privkey = strndup(priv_b64, ret);
Roytak7b9bf292023-10-04 14:06:38 +0200958 if (!*privkey) {
959 ERRMEM;
960 ret = 1;
961 goto cleanup;
962 }
roman13145912023-08-17 15:36:54 +0200963
964 /* ok */
965 ret = 0;
966
967cleanup:
968 free(priv_b64);
969 BIO_free(bio);
970 ssh_key_free(key);
971 return ret;
972}
973
974static int
Roytakb2794852023-10-18 14:30:22 +0200975nc_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 +0200976{
977 int ret = 0;
978 FILE *f_privkey = NULL;
979 char *priv = NULL;
980
Roytak7b9bf292023-10-04 14:06:38 +0200981 NC_CHECK_ARG_RET(NULL, privkey_path, privkey_format, privkey, pkey, 1);
982
roman3f9b65c2023-06-05 14:26:58 +0200983 f_privkey = fopen(privkey_path, "r");
984 if (!f_privkey) {
985 ERR(NULL, "Unable to open file \"%s\".", privkey_path);
986 ret = 1;
987 goto cleanup;
988 }
989
roman13145912023-08-17 15:36:54 +0200990 /* read the first line from the privkey to determine it's type */
Roytakb2794852023-10-18 14:30:22 +0200991 ret = nc_server_config_util_privkey_header_to_format(f_privkey, privkey_path, privkey_format);
roman13145912023-08-17 15:36:54 +0200992 if (ret) {
993 ERR(NULL, "Getting private key format from file \"%s\" failed.", privkey_path);
roman3f9b65c2023-06-05 14:26:58 +0200994 goto cleanup;
995 }
roman3f9b65c2023-06-05 14:26:58 +0200996
roman13145912023-08-17 15:36:54 +0200997 switch (*privkey_format) {
998 /* fall-through */
999 case NC_PRIVKEY_FORMAT_RSA:
1000 case NC_PRIVKEY_FORMAT_EC:
1001 case NC_PRIVKEY_FORMAT_X509:
1002 /* OpenSSL solely can do this */
Roytakb2794852023-10-18 14:30:22 +02001003 ret = nc_server_config_util_get_privkey_openssl(privkey_path, f_privkey, &priv, pkey);
roman13145912023-08-17 15:36:54 +02001004 break;
1005 case NC_PRIVKEY_FORMAT_OPENSSH:
1006 /* need the help of libssh */
Roytakb2794852023-10-18 14:30:22 +02001007 ret = nc_server_config_util_get_privkey_libssh(privkey_path, &priv, pkey);
roman13145912023-08-17 15:36:54 +02001008 /* if the function returned successfully, the key is no longer OpenSSH, it was converted to x509 */
1009 *privkey_format = NC_PRIVKEY_FORMAT_X509;
1010 break;
1011 default:
1012 ERR(NULL, "Private key format not recognized.");
roman3f9b65c2023-06-05 14:26:58 +02001013 ret = 1;
roman13145912023-08-17 15:36:54 +02001014 break;
roman3f9b65c2023-06-05 14:26:58 +02001015 }
roman3f9b65c2023-06-05 14:26:58 +02001016 if (ret) {
1017 goto cleanup;
1018 }
1019
romand30af552023-06-16 15:18:27 +02001020 /* strip private key's header and footer */
roman13145912023-08-17 15:36:54 +02001021 *privkey = strdup(priv + strlen(NC_PKCS8_PRIVKEY_HEADER));
1022 if (!*privkey) {
1023 ERRMEM;
1024 ret = 1;
1025 goto cleanup;
romand30af552023-06-16 15:18:27 +02001026 }
roman13145912023-08-17 15:36:54 +02001027 (*privkey)[strlen(*privkey) - strlen(NC_PKCS8_PRIVKEY_FOOTER)] = '\0';
romand30af552023-06-16 15:18:27 +02001028
roman3f9b65c2023-06-05 14:26:58 +02001029cleanup:
1030 if (f_privkey) {
1031 fclose(f_privkey);
1032 }
1033
romand30af552023-06-16 15:18:27 +02001034 free(priv);
roman13145912023-08-17 15:36:54 +02001035 return ret;
1036}
roman3f9b65c2023-06-05 14:26:58 +02001037
roman13145912023-08-17 15:36:54 +02001038int
Roytakb2794852023-10-18 14:30:22 +02001039nc_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 +02001040 char **privkey, NC_PRIVKEY_FORMAT *privkey_type, char **pubkey)
1041{
1042 int ret = 0;
1043 EVP_PKEY *priv_pkey = NULL;
1044
1045 NC_CHECK_ARG_RET(NULL, privkey_path, privkey, privkey_type, pubkey, 1);
1046
1047 *privkey = NULL;
1048 *pubkey = NULL;
1049
1050 /* get private key base64 and EVP_PKEY */
Roytakb2794852023-10-18 14:30:22 +02001051 ret = nc_server_config_util_get_privkey(privkey_path, privkey_type, privkey, &priv_pkey);
roman13145912023-08-17 15:36:54 +02001052 if (ret) {
1053 ERR(NULL, "Getting private key from file \"%s\" failed.", privkey_path);
1054 goto cleanup;
1055 }
1056
1057 /* get public key, either from file or generate it from the EVP_PKEY */
1058 if (!pubkey_path) {
1059 if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) {
Roytakb2794852023-10-18 14:30:22 +02001060 ret = nc_server_config_util_evp_pkey_to_ssh_pubkey(priv_pkey, pubkey);
roman13145912023-08-17 15:36:54 +02001061 } else {
Roytakb2794852023-10-18 14:30:22 +02001062 ret = nc_server_config_util_evp_pkey_to_spki_pubkey(priv_pkey, pubkey);
roman13145912023-08-17 15:36:54 +02001063 }
1064 } else {
1065 if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) {
Roytakb2794852023-10-18 14:30:22 +02001066 ret = nc_server_config_util_get_ssh_pubkey_file(pubkey_path, pubkey);
roman13145912023-08-17 15:36:54 +02001067 } else {
Roytakb2794852023-10-18 14:30:22 +02001068 ret = nc_server_config_util_get_spki_pubkey_file(pubkey_path, pubkey);
roman13145912023-08-17 15:36:54 +02001069 }
1070 }
1071 if (ret) {
1072 if (pubkey_path) {
1073 ERR(NULL, "Getting public key from file \"%s\" failed.", pubkey_path);
1074 } else {
1075 ERR(NULL, "Generating public key from private key failed.");
1076 }
1077 goto cleanup;
1078 }
1079
1080cleanup:
roman3f9b65c2023-06-05 14:26:58 +02001081 EVP_PKEY_free(priv_pkey);
roman3f9b65c2023-06-05 14:26:58 +02001082 return ret;
1083}
1084
1085API int
Roytakb2794852023-10-18 14:30:22 +02001086nc_server_config_add_address_port(const struct ly_ctx *ctx, const char *endpt_name, NC_TRANSPORT_IMPL transport,
roman142718b2023-06-29 09:15:29 +02001087 const char *address, uint16_t port, struct lyd_node **config)
roman3f9b65c2023-06-05 14:26:58 +02001088{
1089 int ret = 0;
romand30af552023-06-16 15:18:27 +02001090 const char *address_fmt, *port_fmt;
roman142718b2023-06-29 09:15:29 +02001091 char port_buf[6] = {0};
roman3f9b65c2023-06-05 14:26:58 +02001092
Roytak7b9bf292023-10-04 14:06:38 +02001093 NC_CHECK_ARG_RET(NULL, ctx, endpt_name, address, config, 1);
roman3f9b65c2023-06-05 14:26:58 +02001094
roman3f9b65c2023-06-05 14:26:58 +02001095 if (transport == NC_TI_LIBSSH) {
romand30af552023-06-16 15:18:27 +02001096 /* SSH path */
1097 address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters/local-address";
1098 port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters/local-port";
roman2eab4742023-06-06 10:00:26 +02001099 } else if (transport == NC_TI_OPENSSL) {
romand30af552023-06-16 15:18:27 +02001100 /* TLS path */
1101 address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tcp-server-parameters/local-address";
1102 port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tcp-server-parameters/local-port";
roman2eab4742023-06-06 10:00:26 +02001103 } else {
Roytak7b9bf292023-10-04 14:06:38 +02001104 ERR(NULL, "Can not set address and port of a non SSH/TLS endpoint.");
roman2eab4742023-06-06 10:00:26 +02001105 ret = 1;
1106 goto cleanup;
roman3f9b65c2023-06-05 14:26:58 +02001107 }
roman3f9b65c2023-06-05 14:26:58 +02001108
Roytakb2794852023-10-18 14:30:22 +02001109 ret = nc_server_config_create(ctx, config, address, address_fmt, endpt_name);
roman3f9b65c2023-06-05 14:26:58 +02001110 if (ret) {
1111 goto cleanup;
1112 }
1113
roman142718b2023-06-29 09:15:29 +02001114 sprintf(port_buf, "%d", port);
Roytakb2794852023-10-18 14:30:22 +02001115 ret = nc_server_config_create(ctx, config, port_buf, port_fmt, endpt_name);
roman3f9b65c2023-06-05 14:26:58 +02001116 if (ret) {
1117 goto cleanup;
1118 }
romand30af552023-06-16 15:18:27 +02001119
roman3f9b65c2023-06-05 14:26:58 +02001120cleanup:
romand30af552023-06-16 15:18:27 +02001121 return ret;
1122}
1123
roman5cbb6532023-06-22 12:53:17 +02001124API int
Roytakb2794852023-10-18 14:30:22 +02001125nc_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 +02001126 NC_TRANSPORT_IMPL transport, const char *address, const char *port, struct lyd_node **config)
1127{
1128 int ret = 0;
1129 const char *address_fmt, *port_fmt;
1130
Roytak7b9bf292023-10-04 14:06:38 +02001131 NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, address, port, config, 1);
roman5cbb6532023-06-22 12:53:17 +02001132
1133 if (transport == NC_TI_LIBSSH) {
1134 /* SSH path */
1135 address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-address";
1136 port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-port";
1137 } else if (transport == NC_TI_OPENSSL) {
1138 /* TLS path */
1139 address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-address";
1140 port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-port";
1141 } else {
1142 ERR(NULL, "Transport not supported.");
1143 ret = 1;
1144 goto cleanup;
1145 }
1146
Roytakb2794852023-10-18 14:30:22 +02001147 ret = nc_server_config_create(ctx, config, address, address_fmt, client_name, endpt_name);
roman5cbb6532023-06-22 12:53:17 +02001148 if (ret) {
1149 goto cleanup;
1150 }
1151
Roytakb2794852023-10-18 14:30:22 +02001152 ret = nc_server_config_create(ctx, config, port, port_fmt, client_name, endpt_name);
roman5cbb6532023-06-22 12:53:17 +02001153 if (ret) {
1154 goto cleanup;
1155 }
1156
1157cleanup:
1158 return ret;
1159}
1160
1161API int
Roytakb2794852023-10-18 14:30:22 +02001162nc_server_config_del_endpt(const char *endpt_name, struct lyd_node **config)
roman5cbb6532023-06-22 12:53:17 +02001163{
roman8ba6efa2023-07-12 15:27:52 +02001164 NC_CHECK_ARG_RET(NULL, config, 1);
roman5cbb6532023-06-22 12:53:17 +02001165
roman8ba6efa2023-07-12 15:27:52 +02001166 if (endpt_name) {
Roytakb2794852023-10-18 14:30:22 +02001167 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']", endpt_name);
roman8ba6efa2023-07-12 15:27:52 +02001168 } else {
Roytakb2794852023-10-18 14:30:22 +02001169 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint");
roman8ba6efa2023-07-12 15:27:52 +02001170 }
1171}
1172
1173API int
Roytakb2794852023-10-18 14:30:22 +02001174nc_server_config_del_ch_client(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001175{
1176 NC_CHECK_ARG_RET(NULL, config, 1);
1177
1178 if (ch_client_name) {
Roytakb2794852023-10-18 14:30:22 +02001179 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 +02001180 } else {
Roytakb2794852023-10-18 14:30:22 +02001181 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client");
roman8ba6efa2023-07-12 15:27:52 +02001182 }
1183}
1184
1185API int
Roytakb2794852023-10-18 14:30:22 +02001186nc_server_config_del_ch_endpt(const char *client_name, const char *endpt_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001187{
1188 NC_CHECK_ARG_RET(NULL, client_name, config, 1);
1189
1190 if (endpt_name) {
Roytakb2794852023-10-18 14:30:22 +02001191 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
roman8ba6efa2023-07-12 15:27:52 +02001192 "endpoints/endpoint[name='%s']", client_name, endpt_name);
1193 } else {
Roytakb2794852023-10-18 14:30:22 +02001194 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
roman8ba6efa2023-07-12 15:27:52 +02001195 "endpoints/endpoint", client_name);
1196 }
roman5cbb6532023-06-22 12:53:17 +02001197}
1198
roman142718b2023-06-29 09:15:29 +02001199API int
Roytakb2794852023-10-18 14:30:22 +02001200nc_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 +02001201 const char *privkey_path, const char *pubkey_path, struct lyd_node **config)
roman142718b2023-06-29 09:15:29 +02001202{
1203 int ret = 0;
1204 char *privkey = NULL, *pubkey = NULL;
1205 NC_PRIVKEY_FORMAT privkey_type;
roman142718b2023-06-29 09:15:29 +02001206 const char *privkey_format, *pubkey_format;
1207
roman12c3d522023-07-26 13:39:30 +02001208 NC_CHECK_ARG_RET(NULL, ctx, asym_key_name, privkey_path, config, 1);
roman142718b2023-06-29 09:15:29 +02001209
1210 /* get the keys as a string from the given files */
roman13145912023-08-17 15:36:54 +02001211 if (ti == NC_TI_LIBSSH) {
Roytakb2794852023-10-18 14:30:22 +02001212 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 +02001213 } else if (ti == NC_TI_OPENSSL) {
Roytakb2794852023-10-18 14:30:22 +02001214 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 +02001215 } else {
1216 ERR(NULL, "Only SSH and TLS transports can be used to create an asymmetric key pair in the keystore.");
1217 ret = 1;
1218 goto cleanup;
1219 }
roman142718b2023-06-29 09:15:29 +02001220 if (ret) {
1221 ERR(NULL, "Getting keys from file(s) failed.");
1222 goto cleanup;
1223 }
1224
1225 /* get pubkey format str */
roman13145912023-08-17 15:36:54 +02001226 if (ti == NC_TI_LIBSSH) {
roman142718b2023-06-29 09:15:29 +02001227 pubkey_format = "ietf-crypto-types:ssh-public-key-format";
roman13145912023-08-17 15:36:54 +02001228 } else {
1229 pubkey_format = "ietf-crypto-types:subject-public-key-info-format";
roman142718b2023-06-29 09:15:29 +02001230 }
1231
1232 /* get privkey identityref value */
Roytakb2794852023-10-18 14:30:22 +02001233 privkey_format = nc_server_config_util_privkey_format_to_identityref(privkey_type);
roman142718b2023-06-29 09:15:29 +02001234 if (!privkey_format) {
1235 ret = 1;
1236 goto cleanup;
1237 }
1238
Roytakb2794852023-10-18 14:30:22 +02001239 ret = nc_server_config_create(ctx, config, pubkey_format, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001240 "asymmetric-key[name='%s']/public-key-format", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001241 if (ret) {
1242 goto cleanup;
1243 }
1244
Roytakb2794852023-10-18 14:30:22 +02001245 ret = nc_server_config_create(ctx, config, pubkey, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001246 "asymmetric-key[name='%s']/public-key", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001247 if (ret) {
1248 goto cleanup;
1249 }
1250
Roytakb2794852023-10-18 14:30:22 +02001251 ret = nc_server_config_create(ctx, config, privkey_format, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001252 "asymmetric-key[name='%s']/private-key-format", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001253 if (ret) {
1254 goto cleanup;
1255 }
1256
Roytakb2794852023-10-18 14:30:22 +02001257 ret = nc_server_config_create(ctx, config, privkey, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001258 "asymmetric-key[name='%s']/cleartext-private-key", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001259 if (ret) {
1260 goto cleanup;
1261 }
1262
1263cleanup:
1264 free(privkey);
1265 free(pubkey);
1266 return ret;
1267}
1268
1269API int
Roytakb2794852023-10-18 14:30:22 +02001270nc_server_config_del_keystore_asym_key(const char *asym_key_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001271{
1272 NC_CHECK_ARG_RET(NULL, config, 1);
1273
roman12c3d522023-07-26 13:39:30 +02001274 if (asym_key_name) {
Roytakb2794852023-10-18 14:30:22 +02001275 return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']", asym_key_name);
roman8ba6efa2023-07-12 15:27:52 +02001276 } else {
Roytakb2794852023-10-18 14:30:22 +02001277 return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key");
roman8ba6efa2023-07-12 15:27:52 +02001278 }
1279}
1280
1281API int
Roytakb2794852023-10-18 14:30:22 +02001282nc_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 +02001283 const char *cert_path, struct lyd_node **config)
1284{
1285 int ret = 0;
1286 char *cert = NULL;
1287
1288 NC_CHECK_ARG_RET(NULL, ctx, asym_key_name, cert_name, cert_path, config, 1);
1289
1290 /* get cert data */
Roytakb2794852023-10-18 14:30:22 +02001291 ret = nc_server_config_util_read_certificate(cert_path, &cert);
roman12c3d522023-07-26 13:39:30 +02001292 if (ret) {
1293 goto cleanup;
1294 }
1295
Roytakb2794852023-10-18 14:30:22 +02001296 ret = nc_server_config_create(ctx, config, cert, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001297 "asymmetric-key[name='%s']/certificates/certificate[name='%s']/cert-data", asym_key_name, cert_name);
1298
1299cleanup:
1300 free(cert);
1301 return ret;
1302}
1303
1304API int
Roytakb2794852023-10-18 14:30:22 +02001305nc_server_config_del_keystore_cert(const char *asym_key_name, const char *cert_name, struct lyd_node **config)
roman12c3d522023-07-26 13:39:30 +02001306{
1307 NC_CHECK_ARG_RET(NULL, asym_key_name, config, 1);
1308
1309 if (cert_name) {
Roytakb2794852023-10-18 14:30:22 +02001310 return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']/"
roman12c3d522023-07-26 13:39:30 +02001311 "certificates/certificate[name='%s']", asym_key_name, cert_name);
1312 } else {
Roytakb2794852023-10-18 14:30:22 +02001313 return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']/"
roman12c3d522023-07-26 13:39:30 +02001314 "certificates/certificate", asym_key_name);
1315 }
1316}
1317
1318API int
romand348b942023-10-13 14:32:19 +02001319nc_server_config_add_truststore_pubkey(const struct ly_ctx *ctx, const char *pub_bag_name, const char *pubkey_name,
1320 const char *pubkey_path, struct lyd_node **config)
roman142718b2023-06-29 09:15:29 +02001321{
1322 int ret = 0;
1323 char *pubkey = NULL;
roman13145912023-08-17 15:36:54 +02001324 const char *pubkey_format = "ietf-crypto-types:ssh-public-key-format";
roman142718b2023-06-29 09:15:29 +02001325
roman12c3d522023-07-26 13:39:30 +02001326 NC_CHECK_ARG_RET(NULL, ctx, pub_bag_name, pubkey_name, pubkey_path, config, 1);
roman142718b2023-06-29 09:15:29 +02001327
romand348b942023-10-13 14:32:19 +02001328 ret = nc_server_config_util_get_ssh_pubkey_file(pubkey_path, &pubkey);
roman142718b2023-06-29 09:15:29 +02001329 if (ret) {
1330 goto cleanup;
1331 }
1332
Roytakb2794852023-10-18 14:30:22 +02001333 ret = nc_server_config_create(ctx, config, pubkey_format, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001334 "public-key-bag[name='%s']/public-key[name='%s']/public-key-format", pub_bag_name, pubkey_name);
roman142718b2023-06-29 09:15:29 +02001335 if (ret) {
1336 goto cleanup;
1337 }
1338
Roytakb2794852023-10-18 14:30:22 +02001339 ret = nc_server_config_create(ctx, config, pubkey, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001340 "public-key-bag[name='%s']/public-key[name='%s']/public-key", pub_bag_name, pubkey_name);
roman142718b2023-06-29 09:15:29 +02001341 if (ret) {
1342 goto cleanup;
1343 }
1344
1345cleanup:
1346 free(pubkey);
1347 return ret;
1348}
1349
roman8ba6efa2023-07-12 15:27:52 +02001350API int
Roytakb2794852023-10-18 14:30:22 +02001351nc_server_config_del_truststore_pubkey(const char *pub_bag_name,
roman8ba6efa2023-07-12 15:27:52 +02001352 const char *pubkey_name, struct lyd_node **config)
1353{
roman12c3d522023-07-26 13:39:30 +02001354 NC_CHECK_ARG_RET(NULL, pub_bag_name, config, 1);
roman8ba6efa2023-07-12 15:27:52 +02001355
1356 if (pubkey_name) {
Roytakb2794852023-10-18 14:30:22 +02001357 return nc_server_config_delete(config, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001358 "public-key-bag[name='%s']/public-key[name='%s']", pub_bag_name, pubkey_name);
roman8ba6efa2023-07-12 15:27:52 +02001359 } else {
Roytakb2794852023-10-18 14:30:22 +02001360 return nc_server_config_delete(config, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001361 "public-key-bag[name='%s']/public-key", pub_bag_name);
1362 }
1363}
1364
1365API int
Roytakb2794852023-10-18 14:30:22 +02001366nc_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 +02001367 const char *cert_path, struct lyd_node **config)
1368{
1369 int ret = 0;
1370 char *cert = NULL;
1371
1372 NC_CHECK_ARG_RET(NULL, ctx, cert_bag_name, cert_name, cert_path, config, 1);
1373
Roytakb2794852023-10-18 14:30:22 +02001374 ret = nc_server_config_util_read_certificate(cert_path, &cert);
roman12c3d522023-07-26 13:39:30 +02001375 if (ret) {
1376 goto cleanup;
1377 }
1378
Roytakb2794852023-10-18 14:30:22 +02001379 ret = nc_server_config_create(ctx, config, cert, "/ietf-truststore:truststore/certificate-bags/"
roman12c3d522023-07-26 13:39:30 +02001380 "certificate-bag[name='%s']/certificate[name='%s']/cert-data", cert_bag_name, cert_name);
1381 if (ret) {
1382 goto cleanup;
1383 }
1384
1385cleanup:
1386 free(cert);
1387 return ret;
1388}
1389
1390API int
Roytakb2794852023-10-18 14:30:22 +02001391nc_server_config_del_truststore_cert(const char *cert_bag_name,
roman12c3d522023-07-26 13:39:30 +02001392 const char *cert_name, struct lyd_node **config)
1393{
1394 NC_CHECK_ARG_RET(NULL, cert_bag_name, config, 1);
1395
1396 if (cert_name) {
Roytakb2794852023-10-18 14:30:22 +02001397 return nc_server_config_delete(config, "/ietf-truststore:truststore/certificate-bags/"
roman12c3d522023-07-26 13:39:30 +02001398 "certificate-bag[name='%s']/certificate[name='%s']", cert_bag_name, cert_name);
1399 } else {
Roytakb2794852023-10-18 14:30:22 +02001400 return nc_server_config_delete(config, "/ietf-truststore:truststore/certificate-bags/"
roman12c3d522023-07-26 13:39:30 +02001401 "certificate-bag[name='%s']/certificate", cert_bag_name);
roman8ba6efa2023-07-12 15:27:52 +02001402 }
1403}
1404
roman2eab4742023-06-06 10:00:26 +02001405#endif /* NC_ENABLED_SSH_TLS */
romanb6f44032023-06-30 15:07:56 +02001406
romanb6f44032023-06-30 15:07:56 +02001407API int
Roytakb2794852023-10-18 14:30:22 +02001408nc_server_config_add_unix_socket(const struct ly_ctx *ctx, const char *endpt_name, const char *path,
romand0b78372023-09-14 10:06:03 +02001409 mode_t mode, uid_t uid, gid_t gid, struct lyd_node **config)
1410{
1411 int ret = 0;
1412 char *tree_path = NULL;
1413 char buf[12] = {0};
1414
1415 NC_CHECK_ARG_RET(NULL, ctx, endpt_name, path, config, 1);
1416
1417 if (asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/libnetconf2-netconf-server:unix-socket", endpt_name) == -1) {
1418 ERRMEM;
1419 tree_path = NULL;
1420 ret = 1;
1421 goto cleanup;
1422 }
1423
1424 /* path to unix socket */
Roytakb2794852023-10-18 14:30:22 +02001425 ret = nc_server_config_append(ctx, tree_path, "path", path, config);
romand0b78372023-09-14 10:06:03 +02001426 if (ret) {
1427 goto cleanup;
1428 }
1429
1430 /* mode */
1431 if (mode != (mode_t)-1) {
1432 if (mode > 0777) {
1433 ERR(NULL, "Invalid mode value (%o).", mode);
1434 ret = 1;
1435 goto cleanup;
1436 }
1437
1438 sprintf(buf, "%o", mode);
Roytakb2794852023-10-18 14:30:22 +02001439 ret = nc_server_config_append(ctx, tree_path, "mode", buf, config);
romand0b78372023-09-14 10:06:03 +02001440 if (ret) {
1441 goto cleanup;
1442 }
1443 }
1444
1445 /* uid */
1446 if (uid != (uid_t)-1) {
1447 memset(buf, 0, 12);
1448 sprintf(buf, "%u", uid);
Roytakb2794852023-10-18 14:30:22 +02001449 ret = nc_server_config_append(ctx, tree_path, "uid", buf, config);
romand0b78372023-09-14 10:06:03 +02001450 if (ret) {
1451 goto cleanup;
1452 }
1453 }
1454
1455 /* gid */
1456 if (gid != (gid_t)-1) {
1457 memset(buf, 0, 12);
1458 sprintf(buf, "%u", gid);
Roytakb2794852023-10-18 14:30:22 +02001459 ret = nc_server_config_append(ctx, tree_path, "gid", buf, config);
romand0b78372023-09-14 10:06:03 +02001460 if (ret) {
1461 goto cleanup;
1462 }
1463 }
1464
1465cleanup:
1466 free(tree_path);
1467 return ret;
1468}
1469
1470API int
Roytakb2794852023-10-18 14:30:22 +02001471nc_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 +02001472{
1473 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
1474
1475 /* delete periodic tree if exists */
Roytakb2794852023-10-18 14:30:22 +02001476 if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001477 "netconf-client[name='%s']/connection-type/periodic", ch_client_name)) {
1478 return 1;
1479 }
1480
Roytakb2794852023-10-18 14:30:22 +02001481 return nc_server_config_create(ctx, config, NULL, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001482 "netconf-client[name='%s']/connection-type/persistent", ch_client_name);
1483}
1484
1485API int
Roytakb2794852023-10-18 14:30:22 +02001486nc_server_config_add_ch_period(const struct ly_ctx *ctx, const char *ch_client_name, uint16_t period,
romanb6f44032023-06-30 15:07:56 +02001487 struct lyd_node **config)
1488{
1489 char buf[6] = {0};
1490
Roytak7b9bf292023-10-04 14:06:38 +02001491 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
romanb6f44032023-06-30 15:07:56 +02001492
1493 /* delete persistent tree if exists */
Roytakb2794852023-10-18 14:30:22 +02001494 if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001495 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1496 return 1;
1497 }
1498
1499 sprintf(buf, "%u", period);
Roytakb2794852023-10-18 14:30:22 +02001500 return nc_server_config_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001501 "netconf-client[name='%s']/connection-type/periodic/period", ch_client_name);
1502}
1503
1504API int
Roytakb2794852023-10-18 14:30:22 +02001505nc_server_config_del_ch_period(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001506{
1507 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1508
Roytakb2794852023-10-18 14:30:22 +02001509 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
roman8ba6efa2023-07-12 15:27:52 +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_add_ch_anchor_time(const struct ly_ctx *ctx, const char *ch_client_name,
romanb6f44032023-06-30 15:07:56 +02001515 const char *anchor_time, struct lyd_node **config)
1516{
Roytak7b9bf292023-10-04 14:06:38 +02001517 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, anchor_time, config, 1);
romanb6f44032023-06-30 15:07:56 +02001518
1519 /* delete persistent tree if exists */
Roytakb2794852023-10-18 14:30:22 +02001520 if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001521 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1522 return 1;
1523 }
1524
Roytakb2794852023-10-18 14:30:22 +02001525 return nc_server_config_create(ctx, config, anchor_time, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001526 "netconf-client[name='%s']/connection-type/periodic/anchor-time", ch_client_name);
1527}
1528
1529API int
Roytakb2794852023-10-18 14:30:22 +02001530nc_server_config_del_ch_anchor_time(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001531{
1532 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1533
Roytakb2794852023-10-18 14:30:22 +02001534 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
roman8ba6efa2023-07-12 15:27:52 +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_add_ch_idle_timeout(const struct ly_ctx *ctx, const char *ch_client_name,
romanb6f44032023-06-30 15:07:56 +02001540 uint16_t idle_timeout, struct lyd_node **config)
1541{
1542 char buf[6] = {0};
1543
Roytak7b9bf292023-10-04 14:06:38 +02001544 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
romanb6f44032023-06-30 15:07:56 +02001545
1546 /* delete persistent tree if exists */
Roytakb2794852023-10-18 14:30:22 +02001547 if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001548 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1549 return 1;
1550 }
1551
1552 sprintf(buf, "%u", idle_timeout);
Roytakb2794852023-10-18 14:30:22 +02001553 return nc_server_config_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001554 "netconf-client[name='%s']/connection-type/periodic/idle-timeout", ch_client_name);
1555}
1556
1557API int
Roytakb2794852023-10-18 14:30:22 +02001558nc_server_config_del_ch_idle_timeout(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001559{
1560 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1561
Roytakb2794852023-10-18 14:30:22 +02001562 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
roman8ba6efa2023-07-12 15:27:52 +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_add_ch_reconnect_strategy(const struct ly_ctx *ctx, const char *ch_client_name,
roman8ba6efa2023-07-12 15:27:52 +02001568 NC_CH_START_WITH start_with, uint16_t max_wait, uint8_t max_attempts, struct lyd_node **config)
romanb6f44032023-06-30 15:07:56 +02001569{
1570 int ret = 0;
1571 char *path = NULL;
1572 char buf[6] = {0};
1573 const char *start_with_val;
1574
1575 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
1576
1577 /* prepared the path */
1578 if (asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/"
1579 "netconf-client[name='%s']/reconnect-strategy", ch_client_name) == -1) {
1580 ERRMEM;
1581 path = NULL;
1582 ret = 1;
1583 goto cleanup;
1584 }
1585
1586 if (start_with) {
1587 /* get string value from enum */
1588 if (start_with == NC_CH_FIRST_LISTED) {
1589 start_with_val = "first-listed";
1590 } else if (start_with == NC_CH_LAST_CONNECTED) {
1591 start_with_val = "last-connected";
1592 } else {
1593 start_with_val = "random-selection";
1594 }
1595
Roytakb2794852023-10-18 14:30:22 +02001596 ret = nc_server_config_append(ctx, path, "start-with", start_with_val, config);
romanb6f44032023-06-30 15:07:56 +02001597 if (ret) {
1598 goto cleanup;
1599 }
1600 }
1601
1602 if (max_attempts) {
1603 sprintf(buf, "%u", max_attempts);
Roytakb2794852023-10-18 14:30:22 +02001604 ret = nc_server_config_append(ctx, path, "max-attempts", buf, config);
romanb6f44032023-06-30 15:07:56 +02001605 if (ret) {
1606 goto cleanup;
1607 }
1608 memset(buf, 0, 6);
1609 }
1610
1611 if (max_wait) {
1612 sprintf(buf, "%u", max_wait);
Roytakb2794852023-10-18 14:30:22 +02001613 ret = nc_server_config_append(ctx, path, "max-wait", buf, config);
romanb6f44032023-06-30 15:07:56 +02001614 if (ret) {
1615 goto cleanup;
1616 }
1617 }
1618
1619cleanup:
1620 free(path);
1621 return ret;
1622}
roman8ba6efa2023-07-12 15:27:52 +02001623
1624API int
Roytakb2794852023-10-18 14:30:22 +02001625nc_server_config_del_ch_reconnect_strategy(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001626{
1627 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1628
Roytakb2794852023-10-18 14:30:22 +02001629 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
roman8ba6efa2023-07-12 15:27:52 +02001630 "netconf-client[name='%s']/reconnect-strategy", ch_client_name);
1631}