blob: b5f7e06ed06a00d5676ab62e9d784fa93dca7cfc [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>
roman5d9fc732023-10-26 11:26:57 +020021
22#include <inttypes.h>
romand30af552023-06-16 15:18:27 +020023#include <stdarg.h>
roman3f9b65c2023-06-05 14:26:58 +020024#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
roman2eab4742023-06-06 10:00:26 +020028#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +020029#include <libssh/libssh.h>
roman3f9b65c2023-06-05 14:26:58 +020030#include <openssl/err.h>
31#include <openssl/evp.h>
32#include <openssl/pem.h>
roman2eab4742023-06-06 10:00:26 +020033#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +020034
35#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020036#include "log_p.h"
37#include "session.h"
38#include "session_p.h"
39
roman8ba6efa2023-07-12 15:27:52 +020040int
Roytakb2794852023-10-18 14:30:22 +020041nc_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 +020042{
43 int ret = 0;
44 va_list ap;
45 char *path = NULL;
46
Roytak7b9bf292023-10-04 14:06:38 +020047 NC_CHECK_ARG_RET(NULL, ctx, tree, path_fmt, 1);
48
roman8ba6efa2023-07-12 15:27:52 +020049 va_start(ap, path_fmt);
50
51 /* create the path from the format */
52 ret = vasprintf(&path, path_fmt, ap);
roman3a95bb22023-10-26 11:07:17 +020053 NC_CHECK_ERRMEM_GOTO(ret == -1, ret = 1; path = NULL, cleanup);
roman8ba6efa2023-07-12 15:27:52 +020054
55 /* create the nodes in the path */
roman5ef2a572023-08-18 15:45:44 +020056 if (!*tree) {
57 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
58 } else {
59 /* this could output NULL if no new nodes, lyd_find_path would fail then */
60 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, NULL);
61 }
roman8ba6efa2023-07-12 15:27:52 +020062 if (ret) {
63 goto cleanup;
64 }
65
66 /* set the node to the top level node */
67 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
68 if (ret) {
69 goto cleanup;
70 }
71
72 /* add all default nodes */
73 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
74 if (ret) {
75 goto cleanup;
76 }
77
78cleanup:
79 free(path);
80 va_end(ap);
81 return ret;
82}
83
84int
Roytakb2794852023-10-18 14:30:22 +020085nc_server_config_append(const struct ly_ctx *ctx, const char *parent_path, const char *child_name,
roman8ba6efa2023-07-12 15:27:52 +020086 const char *value, struct lyd_node **tree)
87{
88 int ret = 0;
89 char *path = NULL;
90
Roytak7b9bf292023-10-04 14:06:38 +020091 NC_CHECK_ARG_RET(NULL, ctx, parent_path, child_name, tree, 1);
92
roman8ba6efa2023-07-12 15:27:52 +020093 /* create the path by appending child to the parent path */
94 ret = asprintf(&path, "%s/%s", parent_path, child_name);
roman3a95bb22023-10-26 11:07:17 +020095 NC_CHECK_ERRMEM_GOTO(ret == -1, ret = 1; path = NULL, cleanup);
roman8ba6efa2023-07-12 15:27:52 +020096
97 /* create the nodes in the path */
roman5ef2a572023-08-18 15:45:44 +020098 if (!*tree) {
99 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
100 } else {
101 /* this could output NULL if no new nodes, lyd_find_path would fail then */
102 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, NULL);
103 }
roman8ba6efa2023-07-12 15:27:52 +0200104 if (ret) {
105 goto cleanup;
106 }
107
108 /* set the node to the top level node */
109 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
110 if (ret) {
111 goto cleanup;
112 }
113
114 /* add all default nodes */
115 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
116 if (ret) {
117 goto cleanup;
118 }
119
120cleanup:
121 free(path);
122 return ret;
123}
124
125int
Roytakb2794852023-10-18 14:30:22 +0200126nc_server_config_delete(struct lyd_node **tree, const char *path_fmt, ...)
Roytak7b9bf292023-10-04 14:06:38 +0200127{
128 int ret = 0;
129 va_list ap;
130 char *path = NULL;
131 struct lyd_node *sub = NULL;
132
133 NC_CHECK_ARG_RET(NULL, tree, path_fmt, 1);
134
135 va_start(ap, path_fmt);
136
137 /* create the path from the format */
138 ret = vasprintf(&path, path_fmt, ap);
roman3a95bb22023-10-26 11:07:17 +0200139 NC_CHECK_ERRMEM_GOTO(ret == -1, ret = 1; path = NULL, cleanup);
Roytak7b9bf292023-10-04 14:06:38 +0200140
141 /* find the node we want to delete */
142 ret = lyd_find_path(*tree, path, 0, &sub);
143 if (ret) {
144 goto cleanup;
145 }
146
147 lyd_free_tree(sub);
148
149 /* set the node to top level container */
150 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
151 if (ret) {
152 goto cleanup;
153 }
154
155 /* add all default nodes */
156 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
157 if (ret) {
158 goto cleanup;
159 }
160
161cleanup:
162 free(path);
163 va_end(ap);
164 return ret;
165}
166
167int
Roytakb2794852023-10-18 14:30:22 +0200168nc_server_config_check_delete(struct lyd_node **tree, const char *path_fmt, ...)
roman8ba6efa2023-07-12 15:27:52 +0200169{
170 int ret = 0;
171 va_list ap;
172 char *path = NULL;
173 struct lyd_node *sub = NULL;
174
Roytak7b9bf292023-10-04 14:06:38 +0200175 NC_CHECK_ARG_RET(NULL, tree, path_fmt, 1);
176
roman8ba6efa2023-07-12 15:27:52 +0200177 va_start(ap, path_fmt);
178
179 /* create the path from the format */
180 ret = vasprintf(&path, path_fmt, ap);
roman3a95bb22023-10-26 11:07:17 +0200181 NC_CHECK_ERRMEM_GOTO(ret == -1, ret = 1; path = NULL, cleanup);
roman8ba6efa2023-07-12 15:27:52 +0200182
183 /* find the node we want to delete */
184 ret = lyd_find_path(*tree, path, 0, &sub);
185 if ((ret == LY_EINCOMPLETE) || (ret == LY_ENOTFOUND)) {
186 ret = 0;
187 goto cleanup;
188 } else if (ret) {
189 ERR(NULL, "Unable to delete node in the path \"%s\".", path);
190 goto cleanup;
191 }
192
193 lyd_free_tree(sub);
194
195 /* set the node to top level container */
196 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
197 if (ret) {
198 goto cleanup;
199 }
200
201cleanup:
202 free(path);
203 va_end(ap);
204 return ret;
205}
206
roman2eab4742023-06-06 10:00:26 +0200207#ifdef NC_ENABLED_SSH_TLS
208
roman3f9b65c2023-06-05 14:26:58 +0200209const char *
Roytakb2794852023-10-18 14:30:22 +0200210nc_server_config_util_privkey_format_to_identityref(NC_PRIVKEY_FORMAT format)
roman3f9b65c2023-06-05 14:26:58 +0200211{
212 switch (format) {
213 case NC_PRIVKEY_FORMAT_RSA:
214 return "ietf-crypto-types:rsa-private-key-format";
215 case NC_PRIVKEY_FORMAT_EC:
216 return "ietf-crypto-types:ec-private-key-format";
217 case NC_PRIVKEY_FORMAT_X509:
roman13145912023-08-17 15:36:54 +0200218 return "libnetconf2-netconf-server:private-key-info-format";
roman3f9b65c2023-06-05 14:26:58 +0200219 case NC_PRIVKEY_FORMAT_OPENSSH:
220 return "libnetconf2-netconf-server:openssh-private-key-format";
221 default:
222 ERR(NULL, "Private key type not supported.");
223 return NULL;
224 }
225}
226
roman13145912023-08-17 15:36:54 +0200227static int
Roytakb2794852023-10-18 14:30:22 +0200228nc_server_config_util_pubkey_bin_to_b64(const unsigned char *pub_bin, int bin_len, char **pubkey)
roman13145912023-08-17 15:36:54 +0200229{
230 int ret = 0, b64_len;
231 char *pub_b64 = NULL;
232
Roytak7b9bf292023-10-04 14:06:38 +0200233 NC_CHECK_ARG_RET(NULL, pub_bin, bin_len, pubkey, 1);
234
roman13145912023-08-17 15:36:54 +0200235 /* get b64 buffer len, for ever 3 bytes of bin 4 bytes of b64 + NULL terminator */
236 if (bin_len % 3 == 0) {
237 pub_b64 = malloc((bin_len / 3) * 4 + 1);
238 } else {
239 /* bin len not divisible by 3, need to add 4 bytes for some padding so that the len is divisible by 4 */
240 pub_b64 = malloc((bin_len / 3) * 4 + 4 + 1);
241 }
roman3a95bb22023-10-26 11:07:17 +0200242 NC_CHECK_ERRMEM_GOTO(!pub_b64, ret = 1, cleanup);
roman13145912023-08-17 15:36:54 +0200243
244 /* bin to b64 */
245 b64_len = EVP_EncodeBlock((unsigned char *)pub_b64, pub_bin, bin_len);
246 *pubkey = strndup(pub_b64, b64_len);
roman3a95bb22023-10-26 11:07:17 +0200247 NC_CHECK_ERRMEM_GOTO(!*pubkey, ret = 1, cleanup);
roman13145912023-08-17 15:36:54 +0200248
249cleanup:
250 free(pub_b64);
251 return ret;
252}
253
254static int
Roytakb2794852023-10-18 14:30:22 +0200255nc_server_config_util_bn_to_bin(const BIGNUM *bn, unsigned char **bin, int *bin_len)
roman13145912023-08-17 15:36:54 +0200256{
257 int ret = 0;
258 unsigned char *bin_tmp = NULL;
259
260 NC_CHECK_ARG_RET(NULL, bn, bin, bin_len, 1);
261
262 *bin = NULL;
263
264 /* prepare buffer for converting BN to binary */
265 bin_tmp = calloc(BN_num_bytes(bn), sizeof *bin_tmp);
roman3a95bb22023-10-26 11:07:17 +0200266 NC_CHECK_ERRMEM_RET(!bin_tmp, 1);
roman13145912023-08-17 15:36:54 +0200267
268 /* convert to binary */
269 *bin_len = BN_bn2bin(bn, bin_tmp);
270
271 /* if the highest bit in the MSB is set a byte with the value 0 has to be prepended */
272 if (bin_tmp[0] & 0x80) {
273 *bin = malloc(*bin_len + 1);
roman3a95bb22023-10-26 11:07:17 +0200274 NC_CHECK_ERRMEM_GOTO(!*bin, ret = 1, cleanup);
roman13145912023-08-17 15:36:54 +0200275 (*bin)[0] = 0;
276 memcpy(*bin + 1, bin_tmp, *bin_len);
277 (*bin_len)++;
278 } else {
279 *bin = malloc(*bin_len);
roman3a95bb22023-10-26 11:07:17 +0200280 NC_CHECK_ERRMEM_GOTO(!*bin, ret = 1, cleanup);
roman13145912023-08-17 15:36:54 +0200281 memcpy(*bin, bin_tmp, *bin_len);
282 }
283
284cleanup:
285 free(bin_tmp);
286 return ret;
287}
288
289/* ssh pubkey defined in RFC 4253 section 6.6 */
290static int
Roytakb2794852023-10-18 14:30:22 +0200291nc_server_config_util_evp_pkey_to_ssh_pubkey(EVP_PKEY *pkey, char **pubkey)
roman13145912023-08-17 15:36:54 +0200292{
293 int ret = 0, e_len, n_len, p_len, bin_len;
294 BIGNUM *e = NULL, *n = NULL, *p = NULL;
295 unsigned char *e_bin = NULL, *n_bin = NULL, *p_bin = NULL, *bin = NULL, *bin_tmp;
296 const char *algorithm_name, *curve_name;
297 char *ec_group = NULL;
298 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;
299 size_t ec_group_len;
300
Roytak7b9bf292023-10-04 14:06:38 +0200301 NC_CHECK_ARG_RET(NULL, pkey, pubkey, 1);
302
roman13145912023-08-17 15:36:54 +0200303 if (EVP_PKEY_is_a(pkey, "RSA")) {
304 /* RSA key */
305 algorithm_name = "ssh-rsa";
306
307 /* get the public key params */
308 if (!EVP_PKEY_get_bn_param(pkey, "e", &e) || !EVP_PKEY_get_bn_param(pkey, "n", &n)) {
309 ERR(NULL, "Getting public key parameters from RSA private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
310 ret = 1;
311 goto cleanup;
312 }
313
314 /* BIGNUM to bin */
Roytakb2794852023-10-18 14:30:22 +0200315 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 +0200316 ret = 1;
317 goto cleanup;
318 }
319
320 alg_name_len = strlen(algorithm_name);
321 /* buffer for public key in binary, which looks like this:
322 * alg_name len (4 bytes), alg_name, PK exponent len (4 bytes), PK exponent, modulus len (4 bytes), modulus
323 */
324 bin_len = 4 + alg_name_len + 4 + e_len + 4 + n_len;
325 bin = malloc(bin_len);
roman3a95bb22023-10-26 11:07:17 +0200326 NC_CHECK_ERRMEM_GOTO(!bin, ret = 1, cleanup);
roman13145912023-08-17 15:36:54 +0200327
328 /* to network byte order (big endian) */
329 alg_name_len_be = htonl(alg_name_len);
330 e_len_be = htonl(e_len);
331 n_len_be = htonl(n_len);
332
333 /* create the public key in binary */
334 bin_tmp = bin;
335 memcpy(bin_tmp, &alg_name_len_be, 4);
336 bin_tmp += 4;
337 memcpy(bin_tmp, algorithm_name, alg_name_len);
338 bin_tmp += alg_name_len;
339 memcpy(bin_tmp, &e_len_be, 4);
340 bin_tmp += 4;
341 memcpy(bin_tmp, e_bin, e_len);
342 bin_tmp += e_len;
343 memcpy(bin_tmp, &n_len_be, 4);
344 bin_tmp += 4;
345 memcpy(bin_tmp, n_bin, n_len);
346 } else if (EVP_PKEY_is_a(pkey, "EC")) {
347 /* EC Private key, get it's group first */
348 /* get group len */
349 ret = EVP_PKEY_get_utf8_string_param(pkey, "group", NULL, 0, &ec_group_len);
350 if (!ret) {
351 ret = 1;
352 goto cleanup;
353 }
354 /* alloc mem for group + 1 for \0 */
355 ec_group = malloc(ec_group_len + 1);
roman3a95bb22023-10-26 11:07:17 +0200356 NC_CHECK_ERRMEM_GOTO(!ec_group, ret = 1, cleanup);
roman13145912023-08-17 15:36:54 +0200357 /* get the group */
358 ret = EVP_PKEY_get_utf8_string_param(pkey, "group", ec_group, ec_group_len + 1, NULL);
359 if (!ret) {
360 ERR(NULL, "Getting public key parameter from EC private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
361 ret = 1;
362 goto cleanup;
363 }
364
365 /* get alg and curve names */
366 if (!strcmp(ec_group, "P-256") || !strcmp(ec_group, "secp256r1") || !strcmp(ec_group, "prime256v1")) {
367 algorithm_name = "ecdsa-sha2-nistp256";
368 curve_name = "nistp256";
369 } else if (!strcmp(ec_group, "P-384") || !strcmp(ec_group, "secp384r1")) {
370 algorithm_name = "ecdsa-sha2-nistp384";
371 curve_name = "nistp384";
372 } else if (!strcmp(ec_group, "P-521") || !strcmp(ec_group, "secp521r1")) {
373 algorithm_name = "ecdsa-sha2-nistp521";
374 curve_name = "nistp521";
375 } else {
376 ERR(NULL, "EC group \"%s\" not supported.", ec_group);
377 ret = 1;
378 goto cleanup;
379 }
380
381 /* get the public key - p, which is a point on the elliptic curve */
382 ret = EVP_PKEY_get_bn_param(pkey, "p", &p);
383 if (!ret) {
384 ERR(NULL, "Getting public key point from the EC private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
385 ret = 1;
386 goto cleanup;
387 }
388
389 /* prepare buffer for converting p to binary */
390 p_bin = malloc(BN_num_bytes(p));
roman3a95bb22023-10-26 11:07:17 +0200391 NC_CHECK_ERRMEM_GOTO(!p_bin, ret = 1, cleanup);
roman13145912023-08-17 15:36:54 +0200392 /* convert to binary */
393 p_len = BN_bn2bin(p, p_bin);
394
395 alg_name_len = strlen(algorithm_name);
396 curve_name_len = strlen(curve_name);
397 /* buffer for public key in binary, which looks like so:
398 * alg_name len (4 bytes), alg_name, curve_name len (4 bytes), curve_name, PK point p len (4 bytes), PK point p
399 */
400 bin_len = 4 + alg_name_len + 4 + curve_name_len + 4 + p_len;
401 bin = malloc(bin_len);
roman3a95bb22023-10-26 11:07:17 +0200402 NC_CHECK_ERRMEM_GOTO(!bin, ret = 1, cleanup);
roman13145912023-08-17 15:36:54 +0200403
404 /* to network byte order (big endian) */
405 alg_name_len_be = htonl(alg_name_len);
406 curve_name_len_be = htonl(curve_name_len);
407 p_len_be = htonl(p_len);
408
409 /* create the public key in binary */
410 bin_tmp = bin;
411 memcpy(bin_tmp, &alg_name_len_be, 4);
412 bin_tmp += 4;
413 memcpy(bin_tmp, algorithm_name, alg_name_len);
414 bin_tmp += alg_name_len;
415 memcpy(bin_tmp, &curve_name_len_be, 4);
416 bin_tmp += 4;
417 memcpy(bin_tmp, curve_name, curve_name_len);
418 bin_tmp += curve_name_len;
419 memcpy(bin_tmp, &p_len_be, 4);
420 bin_tmp += 4;
421 memcpy(bin_tmp, p_bin, p_len);
422 } else if (EVP_PKEY_is_a(pkey, "ED25519")) {
423 ERR(NULL, "Generating PEM ED25519 key from OpenSSH is not supported by libssh yet.");
424 ret = 1;
425 goto cleanup;
426 } else {
427 ERR(NULL, "Unable to generate public key from private key (Private key type not supported).");
428 ret = 1;
429 goto cleanup;
430 }
431
Roytak7b9bf292023-10-04 14:06:38 +0200432 /* convert created bin to b64 */
Roytakb2794852023-10-18 14:30:22 +0200433 ret = nc_server_config_util_pubkey_bin_to_b64(bin, bin_len, pubkey);
roman13145912023-08-17 15:36:54 +0200434 if (ret) {
435 ERR(NULL, "Converting public key from binary to base64 failed.");
436 goto cleanup;
437 }
438
439cleanup:
440 free(bin);
441 free(e_bin);
442 free(n_bin);
443 free(ec_group);
444 free(p_bin);
445 BN_free(e);
446 BN_free(n);
447 BN_free(p);
448 return ret;
449}
450
451/* spki = subject public key info */
452static int
Roytakb2794852023-10-18 14:30:22 +0200453nc_server_config_util_evp_pkey_to_spki_pubkey(EVP_PKEY *pkey, char **pubkey)
roman13145912023-08-17 15:36:54 +0200454{
455 int ret = 0, len;
456 BIO *bio = NULL;
457 char *pub_b64 = NULL;
458
Roytak7b9bf292023-10-04 14:06:38 +0200459 NC_CHECK_ARG_RET(NULL, pkey, pubkey, 1);
460
roman13145912023-08-17 15:36:54 +0200461 bio = BIO_new(BIO_s_mem());
462 if (!bio) {
463 ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
464 ret = 1;
465 goto cleanup;
466 }
467
468 /* write the evp_pkey contents to bio */
469 if (!PEM_write_bio_PUBKEY(bio, pkey)) {
470 ERR(NULL, "Writing public key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
471 ret = 1;
472 goto cleanup;
473 }
474
475 /* read the pubkey from bio */
476 len = BIO_get_mem_data(bio, &pub_b64);
477 if (len <= 0) {
478 ERR(NULL, "Reading base64 private key from BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
479 ret = 1;
480 goto cleanup;
481 }
482
483 /* copy the public key without the header and footer */
484 *pubkey = strndup(pub_b64 + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER),
485 len - strlen(NC_SUBJECT_PUBKEY_INFO_HEADER) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER));
roman3a95bb22023-10-26 11:07:17 +0200486 NC_CHECK_ERRMEM_GOTO(!*pubkey, ret = 1, cleanup);
roman13145912023-08-17 15:36:54 +0200487
488cleanup:
489 BIO_free(bio);
490 return ret;
491}
492
roman3f9b65c2023-06-05 14:26:58 +0200493int
Roytakb2794852023-10-18 14:30:22 +0200494nc_server_config_util_read_certificate(const char *cert_path, char **cert)
roman3f9b65c2023-06-05 14:26:58 +0200495{
496 int ret = 0, cert_len;
497 X509 *x509 = NULL;
498 FILE *f = NULL;
499 BIO *bio = NULL;
500 char *c = NULL;
501
Roytak7b9bf292023-10-04 14:06:38 +0200502 NC_CHECK_ARG_RET(NULL, cert_path, cert, 1);
roman3f9b65c2023-06-05 14:26:58 +0200503
504 f = fopen(cert_path, "r");
505 if (!f) {
506 ERR(NULL, "Unable to open certificate file \"%s\".", cert_path);
507 ret = 1;
508 goto cleanup;
509 }
510
511 /* load the cert into memory */
512 x509 = PEM_read_X509(f, NULL, NULL, NULL);
513 if (!x509) {
514 ret = -1;
515 goto cleanup;
516 }
517
518 bio = BIO_new(BIO_s_mem());
519 if (!bio) {
520 ret = -1;
521 goto cleanup;
522 }
523
524 ret = PEM_write_bio_X509(bio, x509);
525 if (!ret) {
526 ret = -1;
527 goto cleanup;
528 }
529
530 cert_len = BIO_pending(bio);
531 if (cert_len <= 0) {
532 ret = -1;
533 goto cleanup;
534 }
535
536 c = malloc(cert_len + 1);
roman3a95bb22023-10-26 11:07:17 +0200537 NC_CHECK_ERRMEM_GOTO(!c, ret = 1, cleanup);
roman3f9b65c2023-06-05 14:26:58 +0200538
539 /* read the cert from bio */
540 ret = BIO_read(bio, c, cert_len);
541 if (ret <= 0) {
542 ret = -1;
543 goto cleanup;
544 }
545 c[cert_len] = '\0';
546
547 /* strip the cert of the header and footer */
548 *cert = strdup(c + strlen(NC_PEM_CERTIFICATE_HEADER));
roman3a95bb22023-10-26 11:07:17 +0200549 NC_CHECK_ERRMEM_GOTO(!*cert, ret = 1, cleanup);
roman3f9b65c2023-06-05 14:26:58 +0200550
551 (*cert)[strlen(*cert) - strlen(NC_PEM_CERTIFICATE_FOOTER)] = '\0';
552
553 ret = 0;
554
555cleanup:
556 if (ret == -1) {
557 ERR(NULL, "Error getting certificate from file \"%s\" (OpenSSL Error): \"%s\".", cert_path, ERR_reason_error_string(ERR_get_error()));
558 ret = 1;
559 }
560 if (f) {
561 fclose(f);
562 }
563
564 BIO_free(bio);
565 X509_free(x509);
566 free(c);
567 return ret;
568}
569
570static int
Roytakb2794852023-10-18 14:30:22 +0200571nc_server_config_util_read_pubkey_ssh2(FILE *f, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200572{
573 char *buffer = NULL;
574 size_t size = 0, pubkey_len = 0;
575 void *tmp;
576 ssize_t read;
577 int ret = 0;
578
Roytak7b9bf292023-10-04 14:06:38 +0200579 NC_CHECK_ARG_RET(NULL, f, pubkey, 1);
580
581 /* read lines from the file and create the public key without NL from it */
roman3f9b65c2023-06-05 14:26:58 +0200582 while ((read = getline(&buffer, &size, f)) > 0) {
583 if (!strncmp(buffer, "----", 4)) {
Roytak7b9bf292023-10-04 14:06:38 +0200584 /* skip header and footer */
roman3f9b65c2023-06-05 14:26:58 +0200585 continue;
586 }
587
588 if (!strncmp(buffer, "Comment:", 8)) {
Roytak7b9bf292023-10-04 14:06:38 +0200589 /* skip a comment */
roman3f9b65c2023-06-05 14:26:58 +0200590 continue;
591 }
592
593 if (buffer[read - 1] == '\n') {
Roytak7b9bf292023-10-04 14:06:38 +0200594 /* avoid NL */
roman3f9b65c2023-06-05 14:26:58 +0200595 read--;
596 }
597
598 tmp = realloc(*pubkey, pubkey_len + read + 1);
roman3a95bb22023-10-26 11:07:17 +0200599 NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup);
roman3f9b65c2023-06-05 14:26:58 +0200600
601 *pubkey = tmp;
602 memcpy(*pubkey + pubkey_len, buffer, read);
603 pubkey_len += read;
604 }
605
606 if (!pubkey_len) {
607 ERR(NULL, "Unexpected public key format.");
608 ret = 1;
609 goto cleanup;
610 }
611
612 (*pubkey)[pubkey_len] = '\0';
613
614cleanup:
615 free(buffer);
616 return ret;
617}
618
619static int
Roytakb2794852023-10-18 14:30:22 +0200620nc_server_config_util_read_pubkey_openssl(FILE *f, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200621{
622 int ret = 0;
roman13145912023-08-17 15:36:54 +0200623 EVP_PKEY *pub_pkey = NULL;
624
625 NC_CHECK_ARG_RET(NULL, f, pubkey, 1);
roman3f9b65c2023-06-05 14:26:58 +0200626
627 /* read the pubkey from file */
roman13145912023-08-17 15:36:54 +0200628 pub_pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
629 if (!pub_pkey) {
630 ERR(NULL, "Reading public key from file failed (%s).", ERR_reason_error_string(ERR_get_error()));
631 return 1;
roman3f9b65c2023-06-05 14:26:58 +0200632 }
633
Roytakb2794852023-10-18 14:30:22 +0200634 ret = nc_server_config_util_evp_pkey_to_ssh_pubkey(pub_pkey, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200635
roman13145912023-08-17 15:36:54 +0200636 EVP_PKEY_free(pub_pkey);
roman3f9b65c2023-06-05 14:26:58 +0200637 return ret;
638}
639
roman3f9b65c2023-06-05 14:26:58 +0200640static int
Roytakb2794852023-10-18 14:30:22 +0200641nc_server_config_util_read_pubkey_libssh(const char *pubkey_path, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200642{
643 int ret = 0;
644 ssh_key pub_sshkey = NULL;
645
roman13145912023-08-17 15:36:54 +0200646 NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
647
roman3f9b65c2023-06-05 14:26:58 +0200648 ret = ssh_pki_import_pubkey_file(pubkey_path, &pub_sshkey);
649 if (ret) {
650 ERR(NULL, "Importing public key from file \"%s\" failed.", pubkey_path);
651 return ret;
652 }
653
654 ret = ssh_pki_export_pubkey_base64(pub_sshkey, pubkey);
655 if (ret) {
roman13145912023-08-17 15:36:54 +0200656 ERR(NULL, "Importing pubkey failed.");
657 goto cleanup;
roman3f9b65c2023-06-05 14:26:58 +0200658 }
659
roman13145912023-08-17 15:36:54 +0200660cleanup:
roman3f9b65c2023-06-05 14:26:58 +0200661 ssh_key_free(pub_sshkey);
roman13145912023-08-17 15:36:54 +0200662 return 0;
roman3f9b65c2023-06-05 14:26:58 +0200663}
664
roman3f9b65c2023-06-05 14:26:58 +0200665int
Roytakb2794852023-10-18 14:30:22 +0200666nc_server_config_util_get_ssh_pubkey_file(const char *pubkey_path, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200667{
668 int ret = 0;
669 FILE *f = NULL;
670 char *header = NULL;
671 size_t len = 0;
672
roman13145912023-08-17 15:36:54 +0200673 NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
roman3f9b65c2023-06-05 14:26:58 +0200674
675 *pubkey = NULL;
676
677 f = fopen(pubkey_path, "r");
678 if (!f) {
679 ERR(NULL, "Unable to open file \"%s\".", pubkey_path);
680 ret = 1;
681 goto cleanup;
682 }
683
roman13145912023-08-17 15:36:54 +0200684 /* read the header */
roman3f9b65c2023-06-05 14:26:58 +0200685 if (getline(&header, &len, f) < 0) {
686 ERR(NULL, "Error reading header from file \"%s\".", pubkey_path);
687 ret = 1;
688 goto cleanup;
689 }
690 rewind(f);
691
692 if (!strncmp(header, NC_SUBJECT_PUBKEY_INFO_HEADER, strlen(NC_SUBJECT_PUBKEY_INFO_HEADER))) {
693 /* it's subject public key info public key */
Roytakb2794852023-10-18 14:30:22 +0200694 ret = nc_server_config_util_read_pubkey_openssl(f, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200695 } else if (!strncmp(header, NC_SSH2_PUBKEY_HEADER, strlen(NC_SSH2_PUBKEY_HEADER))) {
696 /* it's ssh2 public key */
Roytakb2794852023-10-18 14:30:22 +0200697 ret = nc_server_config_util_read_pubkey_ssh2(f, pubkey);
roman7fdc84d2023-06-06 13:14:53 +0200698 } else {
roman3f9b65c2023-06-05 14:26:58 +0200699 /* it's probably OpenSSH public key */
Roytakb2794852023-10-18 14:30:22 +0200700 ret = nc_server_config_util_read_pubkey_libssh(pubkey_path, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200701 }
roman3f9b65c2023-06-05 14:26:58 +0200702 if (ret) {
703 ERR(NULL, "Error getting public key from file \"%s\".", pubkey_path);
704 goto cleanup;
705 }
706
707cleanup:
708 if (f) {
709 fclose(f);
710 }
711
712 free(header);
roman3f9b65c2023-06-05 14:26:58 +0200713 return ret;
714}
715
roman3f9b65c2023-06-05 14:26:58 +0200716int
Roytakb2794852023-10-18 14:30:22 +0200717nc_server_config_util_get_spki_pubkey_file(const char *pubkey_path, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200718{
719 int ret = 0;
roman13145912023-08-17 15:36:54 +0200720 FILE *f = NULL;
721 EVP_PKEY *pub_pkey = NULL;
roman3f9b65c2023-06-05 14:26:58 +0200722
roman13145912023-08-17 15:36:54 +0200723 NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
roman3f9b65c2023-06-05 14:26:58 +0200724
roman3f9b65c2023-06-05 14:26:58 +0200725 *pubkey = NULL;
726
roman13145912023-08-17 15:36:54 +0200727 f = fopen(pubkey_path, "r");
728 if (!f) {
729 ERR(NULL, "Unable to open file \"%s\".", pubkey_path);
730 ret = 1;
731 goto cleanup;
732 }
733
734 /* read the pubkey from file */
735 pub_pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
736 if (!pub_pkey) {
737 ERR(NULL, "Reading public key from file failed (%s).", ERR_reason_error_string(ERR_get_error()));
738 return 1;
739 }
740
Roytakb2794852023-10-18 14:30:22 +0200741 ret = nc_server_config_util_evp_pkey_to_spki_pubkey(pub_pkey, pubkey);
roman13145912023-08-17 15:36:54 +0200742 if (ret) {
743 goto cleanup;
744 }
745
746cleanup:
747 if (f) {
748 fclose(f);
749 }
750
751 EVP_PKEY_free(pub_pkey);
752 return ret;
753}
754
755static int
Roytakb2794852023-10-18 14:30:22 +0200756nc_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 +0200757{
758 char *privkey_header = NULL;
759 size_t len = 0;
760
Roytak7b9bf292023-10-04 14:06:38 +0200761 NC_CHECK_ARG_RET(NULL, f_privkey, privkey_path, privkey_format, 1);
762
roman13145912023-08-17 15:36:54 +0200763 /* read header */
764 if (getline(&privkey_header, &len, f_privkey) < 0) {
765 ERR(NULL, "Error reading header from file \"%s\".", privkey_path);
766 return 1;
767 }
768
769 if (!strncmp(privkey_header, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) {
770 /* it's PKCS8 (X.509) private key */
771 *privkey_format = NC_PRIVKEY_FORMAT_X509;
772 } else if (!strncmp(privkey_header, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) {
773 /* it's OpenSSH private key */
774 *privkey_format = NC_PRIVKEY_FORMAT_OPENSSH;
775 } else if (!strncmp(privkey_header, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) {
776 /* it's RSA privkey in PKCS1 format */
777 *privkey_format = NC_PRIVKEY_FORMAT_RSA;
778 } else if (!strncmp(privkey_header, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) {
779 /* it's EC privkey in SEC1 format */
780 *privkey_format = NC_PRIVKEY_FORMAT_EC;
781 } else {
782 ERR(NULL, "Private key format (%s) not supported.", privkey_header);
783 free(privkey_header);
784 return 1;
785 }
786
787 /* reset the reading head */
788 rewind(f_privkey);
789 free(privkey_header);
790 return 0;
791}
792
793static int
Roytakb2794852023-10-18 14:30:22 +0200794nc_server_config_util_get_privkey_openssl(const char *privkey_path, FILE *f_privkey, char **privkey, EVP_PKEY **pkey)
roman13145912023-08-17 15:36:54 +0200795{
796 int ret = 0, len;
797 BIO *bio = NULL;
798 char *priv_b64 = NULL;
799
Roytak7b9bf292023-10-04 14:06:38 +0200800 NC_CHECK_ARG_RET(NULL, privkey_path, f_privkey, privkey, pkey, 1);
801
roman13145912023-08-17 15:36:54 +0200802 bio = BIO_new(BIO_s_mem());
803 if (!bio) {
804 ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
805 ret = 1;
806 goto cleanup;
807 }
808
809 /* read the privkey file, create EVP_PKEY */
810 *pkey = PEM_read_PrivateKey(f_privkey, NULL, NULL, NULL);
811 if (!*pkey) {
812 ERR(NULL, "Getting private key from file \"%s\" failed (%s).", privkey_path, ERR_reason_error_string(ERR_get_error()));
813 ret = 1;
814 goto cleanup;
815 }
816
817 /* write the privkey to bio */
818 if (!PEM_write_bio_PrivateKey(bio, *pkey, NULL, NULL, 0, NULL, NULL)) {
819 ERR(NULL, "Writing private key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
820 ret = 1;
821 goto cleanup;
822 }
823
824 /* read the privkey from bio */
825 len = BIO_get_mem_data(bio, &priv_b64);
826 if (len <= 0) {
827 ERR(NULL, "Reading base64 private key from BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
828 ret = 1;
829 goto cleanup;
830 }
831
832 *privkey = strndup(priv_b64, len);
roman3a95bb22023-10-26 11:07:17 +0200833 NC_CHECK_ERRMEM_GOTO(!*privkey, ret = 1, cleanup);
roman13145912023-08-17 15:36:54 +0200834
835cleanup:
Roytak7b9bf292023-10-04 14:06:38 +0200836 /* priv_b64 is freed with BIO */
roman13145912023-08-17 15:36:54 +0200837 BIO_free(bio);
838 return ret;
839}
840
841static int
Roytakb2794852023-10-18 14:30:22 +0200842nc_server_config_util_get_privkey_libssh(const char *privkey_path, char **privkey, EVP_PKEY **pkey)
roman13145912023-08-17 15:36:54 +0200843{
844 int ret = 0;
845 BIO *bio = NULL;
846 char *priv_b64 = NULL;
847 ssh_key key = NULL;
848
Roytak7b9bf292023-10-04 14:06:38 +0200849 NC_CHECK_ARG_RET(NULL, privkey_path, privkey, pkey, 1);
850
roman13145912023-08-17 15:36:54 +0200851 ret = ssh_pki_import_privkey_file(privkey_path, NULL, NULL, NULL, &key);
852 if (ret) {
853 ERR(NULL, "Importing privkey from file \"%s\" failed.", privkey_path);
854 goto cleanup;
855 }
856
857 /* exports the key in a format in which OpenSSL can read it */
858 ret = ssh_pki_export_privkey_base64(key, NULL, NULL, NULL, &priv_b64);
859 if (ret) {
860 ERR(NULL, "Exporting privkey to base64 failed.");
861 goto cleanup;
862 }
863
864 bio = BIO_new(BIO_s_mem());
865 if (!bio) {
866 ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
867 ret = 1;
868 goto cleanup;
869 }
870
871 ret = BIO_write(bio, priv_b64, strlen(priv_b64));
872 if (ret <= 0) {
873 ERR(NULL, "Writing private key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
874 ret = 1;
875 goto cleanup;
876 }
877
878 /* create EVP_PKEY from the b64 */
879 *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
880 if (!*pkey) {
881 ERR(NULL, "Getting private key from file \"%s\" failed (%s).", privkey_path, ERR_reason_error_string(ERR_get_error()));
882 ret = 1;
883 goto cleanup;
884 }
885
886 *privkey = strndup(priv_b64, ret);
roman3a95bb22023-10-26 11:07:17 +0200887 NC_CHECK_ERRMEM_GOTO(!*privkey, ret = 1, cleanup);
roman13145912023-08-17 15:36:54 +0200888
889 /* ok */
890 ret = 0;
891
892cleanup:
893 free(priv_b64);
894 BIO_free(bio);
895 ssh_key_free(key);
896 return ret;
897}
898
899static int
Roytakb2794852023-10-18 14:30:22 +0200900nc_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 +0200901{
902 int ret = 0;
903 FILE *f_privkey = NULL;
904 char *priv = NULL;
905
Roytak7b9bf292023-10-04 14:06:38 +0200906 NC_CHECK_ARG_RET(NULL, privkey_path, privkey_format, privkey, pkey, 1);
907
roman3f9b65c2023-06-05 14:26:58 +0200908 f_privkey = fopen(privkey_path, "r");
909 if (!f_privkey) {
910 ERR(NULL, "Unable to open file \"%s\".", privkey_path);
911 ret = 1;
912 goto cleanup;
913 }
914
roman13145912023-08-17 15:36:54 +0200915 /* read the first line from the privkey to determine it's type */
Roytakb2794852023-10-18 14:30:22 +0200916 ret = nc_server_config_util_privkey_header_to_format(f_privkey, privkey_path, privkey_format);
roman13145912023-08-17 15:36:54 +0200917 if (ret) {
918 ERR(NULL, "Getting private key format from file \"%s\" failed.", privkey_path);
roman3f9b65c2023-06-05 14:26:58 +0200919 goto cleanup;
920 }
roman3f9b65c2023-06-05 14:26:58 +0200921
roman13145912023-08-17 15:36:54 +0200922 switch (*privkey_format) {
923 /* fall-through */
924 case NC_PRIVKEY_FORMAT_RSA:
925 case NC_PRIVKEY_FORMAT_EC:
926 case NC_PRIVKEY_FORMAT_X509:
927 /* OpenSSL solely can do this */
Roytakb2794852023-10-18 14:30:22 +0200928 ret = nc_server_config_util_get_privkey_openssl(privkey_path, f_privkey, &priv, pkey);
roman13145912023-08-17 15:36:54 +0200929 break;
930 case NC_PRIVKEY_FORMAT_OPENSSH:
931 /* need the help of libssh */
Roytakb2794852023-10-18 14:30:22 +0200932 ret = nc_server_config_util_get_privkey_libssh(privkey_path, &priv, pkey);
roman13145912023-08-17 15:36:54 +0200933 /* if the function returned successfully, the key is no longer OpenSSH, it was converted to x509 */
934 *privkey_format = NC_PRIVKEY_FORMAT_X509;
935 break;
936 default:
937 ERR(NULL, "Private key format not recognized.");
roman3f9b65c2023-06-05 14:26:58 +0200938 ret = 1;
roman13145912023-08-17 15:36:54 +0200939 break;
roman3f9b65c2023-06-05 14:26:58 +0200940 }
roman3f9b65c2023-06-05 14:26:58 +0200941 if (ret) {
942 goto cleanup;
943 }
944
romand30af552023-06-16 15:18:27 +0200945 /* strip private key's header and footer */
roman13145912023-08-17 15:36:54 +0200946 *privkey = strdup(priv + strlen(NC_PKCS8_PRIVKEY_HEADER));
roman3a95bb22023-10-26 11:07:17 +0200947 NC_CHECK_ERRMEM_GOTO(!*privkey, ret = 1, cleanup);
roman13145912023-08-17 15:36:54 +0200948 (*privkey)[strlen(*privkey) - strlen(NC_PKCS8_PRIVKEY_FOOTER)] = '\0';
romand30af552023-06-16 15:18:27 +0200949
roman3f9b65c2023-06-05 14:26:58 +0200950cleanup:
951 if (f_privkey) {
952 fclose(f_privkey);
953 }
954
romand30af552023-06-16 15:18:27 +0200955 free(priv);
roman13145912023-08-17 15:36:54 +0200956 return ret;
957}
roman3f9b65c2023-06-05 14:26:58 +0200958
roman13145912023-08-17 15:36:54 +0200959int
Roytakb2794852023-10-18 14:30:22 +0200960nc_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 +0200961 char **privkey, NC_PRIVKEY_FORMAT *privkey_type, char **pubkey)
962{
963 int ret = 0;
964 EVP_PKEY *priv_pkey = NULL;
965
966 NC_CHECK_ARG_RET(NULL, privkey_path, privkey, privkey_type, pubkey, 1);
967
968 *privkey = NULL;
969 *pubkey = NULL;
970
971 /* get private key base64 and EVP_PKEY */
Roytakb2794852023-10-18 14:30:22 +0200972 ret = nc_server_config_util_get_privkey(privkey_path, privkey_type, privkey, &priv_pkey);
roman13145912023-08-17 15:36:54 +0200973 if (ret) {
974 ERR(NULL, "Getting private key from file \"%s\" failed.", privkey_path);
975 goto cleanup;
976 }
977
978 /* get public key, either from file or generate it from the EVP_PKEY */
979 if (!pubkey_path) {
980 if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) {
Roytakb2794852023-10-18 14:30:22 +0200981 ret = nc_server_config_util_evp_pkey_to_ssh_pubkey(priv_pkey, pubkey);
roman13145912023-08-17 15:36:54 +0200982 } else {
Roytakb2794852023-10-18 14:30:22 +0200983 ret = nc_server_config_util_evp_pkey_to_spki_pubkey(priv_pkey, pubkey);
roman13145912023-08-17 15:36:54 +0200984 }
985 } else {
986 if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) {
Roytakb2794852023-10-18 14:30:22 +0200987 ret = nc_server_config_util_get_ssh_pubkey_file(pubkey_path, pubkey);
roman13145912023-08-17 15:36:54 +0200988 } else {
Roytakb2794852023-10-18 14:30:22 +0200989 ret = nc_server_config_util_get_spki_pubkey_file(pubkey_path, pubkey);
roman13145912023-08-17 15:36:54 +0200990 }
991 }
992 if (ret) {
993 if (pubkey_path) {
994 ERR(NULL, "Getting public key from file \"%s\" failed.", pubkey_path);
995 } else {
996 ERR(NULL, "Generating public key from private key failed.");
997 }
998 goto cleanup;
999 }
1000
1001cleanup:
roman3f9b65c2023-06-05 14:26:58 +02001002 EVP_PKEY_free(priv_pkey);
roman3f9b65c2023-06-05 14:26:58 +02001003 return ret;
1004}
1005
1006API int
Roytakb2794852023-10-18 14:30:22 +02001007nc_server_config_add_address_port(const struct ly_ctx *ctx, const char *endpt_name, NC_TRANSPORT_IMPL transport,
roman142718b2023-06-29 09:15:29 +02001008 const char *address, uint16_t port, struct lyd_node **config)
roman3f9b65c2023-06-05 14:26:58 +02001009{
1010 int ret = 0;
romand30af552023-06-16 15:18:27 +02001011 const char *address_fmt, *port_fmt;
roman142718b2023-06-29 09:15:29 +02001012 char port_buf[6] = {0};
roman3f9b65c2023-06-05 14:26:58 +02001013
Roytak7b9bf292023-10-04 14:06:38 +02001014 NC_CHECK_ARG_RET(NULL, ctx, endpt_name, address, config, 1);
roman3f9b65c2023-06-05 14:26:58 +02001015
roman3f9b65c2023-06-05 14:26:58 +02001016 if (transport == NC_TI_LIBSSH) {
romand30af552023-06-16 15:18:27 +02001017 /* SSH path */
Michal Vaskocf898172024-01-15 15:04:28 +01001018 address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/tcp-server-parameters/local-address";
1019 port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/tcp-server-parameters/local-port";
roman2eab4742023-06-06 10:00:26 +02001020 } else if (transport == NC_TI_OPENSSL) {
romand30af552023-06-16 15:18:27 +02001021 /* TLS path */
Michal Vaskocf898172024-01-15 15:04:28 +01001022 address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/tcp-server-parameters/local-address";
1023 port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/tcp-server-parameters/local-port";
roman2eab4742023-06-06 10:00:26 +02001024 } else {
Roytak7b9bf292023-10-04 14:06:38 +02001025 ERR(NULL, "Can not set address and port of a non SSH/TLS endpoint.");
roman2eab4742023-06-06 10:00:26 +02001026 ret = 1;
1027 goto cleanup;
roman3f9b65c2023-06-05 14:26:58 +02001028 }
roman3f9b65c2023-06-05 14:26:58 +02001029
Roytakb2794852023-10-18 14:30:22 +02001030 ret = nc_server_config_create(ctx, config, address, address_fmt, endpt_name);
roman3f9b65c2023-06-05 14:26:58 +02001031 if (ret) {
1032 goto cleanup;
1033 }
1034
roman142718b2023-06-29 09:15:29 +02001035 sprintf(port_buf, "%d", port);
Roytakb2794852023-10-18 14:30:22 +02001036 ret = nc_server_config_create(ctx, config, port_buf, port_fmt, endpt_name);
roman3f9b65c2023-06-05 14:26:58 +02001037 if (ret) {
1038 goto cleanup;
1039 }
romand30af552023-06-16 15:18:27 +02001040
roman3f9b65c2023-06-05 14:26:58 +02001041cleanup:
romand30af552023-06-16 15:18:27 +02001042 return ret;
1043}
1044
roman5cbb6532023-06-22 12:53:17 +02001045API int
Roytakb2794852023-10-18 14:30:22 +02001046nc_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 +02001047 NC_TRANSPORT_IMPL transport, const char *address, const char *port, struct lyd_node **config)
1048{
1049 int ret = 0;
1050 const char *address_fmt, *port_fmt;
1051
Roytak7b9bf292023-10-04 14:06:38 +02001052 NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, address, port, config, 1);
roman5cbb6532023-06-22 12:53:17 +02001053
1054 if (transport == NC_TI_LIBSSH) {
1055 /* SSH path */
1056 address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-address";
1057 port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-port";
1058 } else if (transport == NC_TI_OPENSSL) {
1059 /* TLS path */
1060 address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-address";
1061 port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-port";
1062 } else {
1063 ERR(NULL, "Transport not supported.");
1064 ret = 1;
1065 goto cleanup;
1066 }
1067
Roytakb2794852023-10-18 14:30:22 +02001068 ret = nc_server_config_create(ctx, config, address, address_fmt, client_name, endpt_name);
roman5cbb6532023-06-22 12:53:17 +02001069 if (ret) {
1070 goto cleanup;
1071 }
1072
Roytakb2794852023-10-18 14:30:22 +02001073 ret = nc_server_config_create(ctx, config, port, port_fmt, client_name, endpt_name);
roman5cbb6532023-06-22 12:53:17 +02001074 if (ret) {
1075 goto cleanup;
1076 }
1077
1078cleanup:
1079 return ret;
1080}
1081
1082API int
Roytakb2794852023-10-18 14:30:22 +02001083nc_server_config_del_endpt(const char *endpt_name, struct lyd_node **config)
roman5cbb6532023-06-22 12:53:17 +02001084{
roman8ba6efa2023-07-12 15:27:52 +02001085 NC_CHECK_ARG_RET(NULL, config, 1);
roman5cbb6532023-06-22 12:53:17 +02001086
roman8ba6efa2023-07-12 15:27:52 +02001087 if (endpt_name) {
Michal Vaskocf898172024-01-15 15:04:28 +01001088 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']", endpt_name);
roman8ba6efa2023-07-12 15:27:52 +02001089 } else {
Michal Vaskocf898172024-01-15 15:04:28 +01001090 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint");
roman8ba6efa2023-07-12 15:27:52 +02001091 }
1092}
1093
1094API int
Roytakb2794852023-10-18 14:30:22 +02001095nc_server_config_del_ch_client(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001096{
1097 NC_CHECK_ARG_RET(NULL, config, 1);
1098
1099 if (ch_client_name) {
Roytakb2794852023-10-18 14:30:22 +02001100 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 +02001101 } else {
Roytakb2794852023-10-18 14:30:22 +02001102 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client");
roman8ba6efa2023-07-12 15:27:52 +02001103 }
1104}
1105
1106API int
Roytakb2794852023-10-18 14:30:22 +02001107nc_server_config_del_ch_endpt(const char *client_name, const char *endpt_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001108{
1109 NC_CHECK_ARG_RET(NULL, client_name, config, 1);
1110
1111 if (endpt_name) {
Roytakb2794852023-10-18 14:30:22 +02001112 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
roman8ba6efa2023-07-12 15:27:52 +02001113 "endpoints/endpoint[name='%s']", client_name, endpt_name);
1114 } else {
Roytakb2794852023-10-18 14:30:22 +02001115 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
roman8ba6efa2023-07-12 15:27:52 +02001116 "endpoints/endpoint", client_name);
1117 }
roman5cbb6532023-06-22 12:53:17 +02001118}
1119
roman142718b2023-06-29 09:15:29 +02001120API int
Roytakb2794852023-10-18 14:30:22 +02001121nc_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 +02001122 const char *privkey_path, const char *pubkey_path, struct lyd_node **config)
roman142718b2023-06-29 09:15:29 +02001123{
1124 int ret = 0;
1125 char *privkey = NULL, *pubkey = NULL;
1126 NC_PRIVKEY_FORMAT privkey_type;
roman142718b2023-06-29 09:15:29 +02001127 const char *privkey_format, *pubkey_format;
1128
roman12c3d522023-07-26 13:39:30 +02001129 NC_CHECK_ARG_RET(NULL, ctx, asym_key_name, privkey_path, config, 1);
roman142718b2023-06-29 09:15:29 +02001130
1131 /* get the keys as a string from the given files */
roman13145912023-08-17 15:36:54 +02001132 if (ti == NC_TI_LIBSSH) {
Michal Vasko7ac7c562024-03-05 09:51:21 +01001133 ret = nc_server_config_util_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_SSH, &privkey,
1134 &privkey_type, &pubkey);
roman13145912023-08-17 15:36:54 +02001135 } else if (ti == NC_TI_OPENSSL) {
Michal Vasko7ac7c562024-03-05 09:51:21 +01001136 ret = nc_server_config_util_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_X509, &privkey,
1137 &privkey_type, &pubkey);
roman13145912023-08-17 15:36:54 +02001138 } else {
1139 ERR(NULL, "Only SSH and TLS transports can be used to create an asymmetric key pair in the keystore.");
1140 ret = 1;
1141 goto cleanup;
1142 }
roman142718b2023-06-29 09:15:29 +02001143 if (ret) {
roman142718b2023-06-29 09:15:29 +02001144 goto cleanup;
1145 }
1146
1147 /* get pubkey format str */
roman13145912023-08-17 15:36:54 +02001148 if (ti == NC_TI_LIBSSH) {
roman142718b2023-06-29 09:15:29 +02001149 pubkey_format = "ietf-crypto-types:ssh-public-key-format";
roman13145912023-08-17 15:36:54 +02001150 } else {
1151 pubkey_format = "ietf-crypto-types:subject-public-key-info-format";
roman142718b2023-06-29 09:15:29 +02001152 }
1153
1154 /* get privkey identityref value */
Roytakb2794852023-10-18 14:30:22 +02001155 privkey_format = nc_server_config_util_privkey_format_to_identityref(privkey_type);
roman142718b2023-06-29 09:15:29 +02001156 if (!privkey_format) {
1157 ret = 1;
1158 goto cleanup;
1159 }
1160
Roytakb2794852023-10-18 14:30:22 +02001161 ret = nc_server_config_create(ctx, config, pubkey_format, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001162 "asymmetric-key[name='%s']/public-key-format", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001163 if (ret) {
1164 goto cleanup;
1165 }
1166
Roytakb2794852023-10-18 14:30:22 +02001167 ret = nc_server_config_create(ctx, config, pubkey, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001168 "asymmetric-key[name='%s']/public-key", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001169 if (ret) {
1170 goto cleanup;
1171 }
1172
Roytakb2794852023-10-18 14:30:22 +02001173 ret = nc_server_config_create(ctx, config, privkey_format, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001174 "asymmetric-key[name='%s']/private-key-format", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001175 if (ret) {
1176 goto cleanup;
1177 }
1178
Roytakb2794852023-10-18 14:30:22 +02001179 ret = nc_server_config_create(ctx, config, privkey, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001180 "asymmetric-key[name='%s']/cleartext-private-key", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001181 if (ret) {
1182 goto cleanup;
1183 }
1184
1185cleanup:
1186 free(privkey);
1187 free(pubkey);
1188 return ret;
1189}
1190
1191API int
Roytakb2794852023-10-18 14:30:22 +02001192nc_server_config_del_keystore_asym_key(const char *asym_key_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001193{
1194 NC_CHECK_ARG_RET(NULL, config, 1);
1195
roman12c3d522023-07-26 13:39:30 +02001196 if (asym_key_name) {
Roytakb2794852023-10-18 14:30:22 +02001197 return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']", asym_key_name);
roman8ba6efa2023-07-12 15:27:52 +02001198 } else {
Roytakb2794852023-10-18 14:30:22 +02001199 return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key");
roman8ba6efa2023-07-12 15:27:52 +02001200 }
1201}
1202
1203API int
Roytakb2794852023-10-18 14:30:22 +02001204nc_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 +02001205 const char *cert_path, struct lyd_node **config)
1206{
1207 int ret = 0;
1208 char *cert = NULL;
1209
1210 NC_CHECK_ARG_RET(NULL, ctx, asym_key_name, cert_name, cert_path, config, 1);
1211
1212 /* get cert data */
Roytakb2794852023-10-18 14:30:22 +02001213 ret = nc_server_config_util_read_certificate(cert_path, &cert);
roman12c3d522023-07-26 13:39:30 +02001214 if (ret) {
1215 goto cleanup;
1216 }
1217
Roytakb2794852023-10-18 14:30:22 +02001218 ret = nc_server_config_create(ctx, config, cert, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001219 "asymmetric-key[name='%s']/certificates/certificate[name='%s']/cert-data", asym_key_name, cert_name);
1220
1221cleanup:
1222 free(cert);
1223 return ret;
1224}
1225
1226API int
Roytakb2794852023-10-18 14:30:22 +02001227nc_server_config_del_keystore_cert(const char *asym_key_name, const char *cert_name, struct lyd_node **config)
roman12c3d522023-07-26 13:39:30 +02001228{
1229 NC_CHECK_ARG_RET(NULL, asym_key_name, config, 1);
1230
1231 if (cert_name) {
Roytakb2794852023-10-18 14:30:22 +02001232 return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']/"
roman12c3d522023-07-26 13:39:30 +02001233 "certificates/certificate[name='%s']", asym_key_name, cert_name);
1234 } else {
Roytakb2794852023-10-18 14:30:22 +02001235 return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']/"
roman12c3d522023-07-26 13:39:30 +02001236 "certificates/certificate", asym_key_name);
1237 }
1238}
1239
1240API int
romand348b942023-10-13 14:32:19 +02001241nc_server_config_add_truststore_pubkey(const struct ly_ctx *ctx, const char *pub_bag_name, const char *pubkey_name,
1242 const char *pubkey_path, struct lyd_node **config)
roman142718b2023-06-29 09:15:29 +02001243{
1244 int ret = 0;
1245 char *pubkey = NULL;
roman13145912023-08-17 15:36:54 +02001246 const char *pubkey_format = "ietf-crypto-types:ssh-public-key-format";
roman142718b2023-06-29 09:15:29 +02001247
roman12c3d522023-07-26 13:39:30 +02001248 NC_CHECK_ARG_RET(NULL, ctx, pub_bag_name, pubkey_name, pubkey_path, config, 1);
roman142718b2023-06-29 09:15:29 +02001249
romand348b942023-10-13 14:32:19 +02001250 ret = nc_server_config_util_get_ssh_pubkey_file(pubkey_path, &pubkey);
roman142718b2023-06-29 09:15:29 +02001251 if (ret) {
1252 goto cleanup;
1253 }
1254
Roytakb2794852023-10-18 14:30:22 +02001255 ret = nc_server_config_create(ctx, config, pubkey_format, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001256 "public-key-bag[name='%s']/public-key[name='%s']/public-key-format", pub_bag_name, pubkey_name);
roman142718b2023-06-29 09:15:29 +02001257 if (ret) {
1258 goto cleanup;
1259 }
1260
Roytakb2794852023-10-18 14:30:22 +02001261 ret = nc_server_config_create(ctx, config, pubkey, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001262 "public-key-bag[name='%s']/public-key[name='%s']/public-key", pub_bag_name, pubkey_name);
roman142718b2023-06-29 09:15:29 +02001263 if (ret) {
1264 goto cleanup;
1265 }
1266
1267cleanup:
1268 free(pubkey);
1269 return ret;
1270}
1271
roman8ba6efa2023-07-12 15:27:52 +02001272API int
Roytakb2794852023-10-18 14:30:22 +02001273nc_server_config_del_truststore_pubkey(const char *pub_bag_name,
roman8ba6efa2023-07-12 15:27:52 +02001274 const char *pubkey_name, struct lyd_node **config)
1275{
roman12c3d522023-07-26 13:39:30 +02001276 NC_CHECK_ARG_RET(NULL, pub_bag_name, config, 1);
roman8ba6efa2023-07-12 15:27:52 +02001277
1278 if (pubkey_name) {
Roytakb2794852023-10-18 14:30:22 +02001279 return nc_server_config_delete(config, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001280 "public-key-bag[name='%s']/public-key[name='%s']", pub_bag_name, pubkey_name);
roman8ba6efa2023-07-12 15:27:52 +02001281 } else {
Roytakb2794852023-10-18 14:30:22 +02001282 return nc_server_config_delete(config, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001283 "public-key-bag[name='%s']/public-key", pub_bag_name);
1284 }
1285}
1286
1287API int
Roytakb2794852023-10-18 14:30:22 +02001288nc_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 +02001289 const char *cert_path, struct lyd_node **config)
1290{
1291 int ret = 0;
1292 char *cert = NULL;
1293
1294 NC_CHECK_ARG_RET(NULL, ctx, cert_bag_name, cert_name, cert_path, config, 1);
1295
Roytakb2794852023-10-18 14:30:22 +02001296 ret = nc_server_config_util_read_certificate(cert_path, &cert);
roman12c3d522023-07-26 13:39:30 +02001297 if (ret) {
1298 goto cleanup;
1299 }
1300
Roytakb2794852023-10-18 14:30:22 +02001301 ret = nc_server_config_create(ctx, config, cert, "/ietf-truststore:truststore/certificate-bags/"
roman12c3d522023-07-26 13:39:30 +02001302 "certificate-bag[name='%s']/certificate[name='%s']/cert-data", cert_bag_name, cert_name);
1303 if (ret) {
1304 goto cleanup;
1305 }
1306
1307cleanup:
1308 free(cert);
1309 return ret;
1310}
1311
1312API int
Roytakb2794852023-10-18 14:30:22 +02001313nc_server_config_del_truststore_cert(const char *cert_bag_name,
roman12c3d522023-07-26 13:39:30 +02001314 const char *cert_name, struct lyd_node **config)
1315{
1316 NC_CHECK_ARG_RET(NULL, cert_bag_name, config, 1);
1317
1318 if (cert_name) {
Roytakb2794852023-10-18 14:30:22 +02001319 return nc_server_config_delete(config, "/ietf-truststore:truststore/certificate-bags/"
roman12c3d522023-07-26 13:39:30 +02001320 "certificate-bag[name='%s']/certificate[name='%s']", cert_bag_name, cert_name);
1321 } else {
Roytakb2794852023-10-18 14:30:22 +02001322 return nc_server_config_delete(config, "/ietf-truststore:truststore/certificate-bags/"
roman12c3d522023-07-26 13:39:30 +02001323 "certificate-bag[name='%s']/certificate", cert_bag_name);
roman8ba6efa2023-07-12 15:27:52 +02001324 }
1325}
1326
roman2eab4742023-06-06 10:00:26 +02001327#endif /* NC_ENABLED_SSH_TLS */
romanb6f44032023-06-30 15:07:56 +02001328
romanb6f44032023-06-30 15:07:56 +02001329API int
Roytakb2794852023-10-18 14:30:22 +02001330nc_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 +02001331{
1332 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
1333
1334 /* delete periodic tree if exists */
Roytakb2794852023-10-18 14:30:22 +02001335 if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001336 "netconf-client[name='%s']/connection-type/periodic", ch_client_name)) {
1337 return 1;
1338 }
1339
Roytakb2794852023-10-18 14:30:22 +02001340 return nc_server_config_create(ctx, config, NULL, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001341 "netconf-client[name='%s']/connection-type/persistent", ch_client_name);
1342}
1343
1344API int
Roytakb2794852023-10-18 14:30:22 +02001345nc_server_config_add_ch_period(const struct ly_ctx *ctx, const char *ch_client_name, uint16_t period,
romanb6f44032023-06-30 15:07:56 +02001346 struct lyd_node **config)
1347{
1348 char buf[6] = {0};
1349
Roytak7b9bf292023-10-04 14:06:38 +02001350 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
romanb6f44032023-06-30 15:07:56 +02001351
1352 /* delete persistent tree if exists */
Roytakb2794852023-10-18 14:30:22 +02001353 if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001354 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1355 return 1;
1356 }
1357
roman5d9fc732023-10-26 11:26:57 +02001358 sprintf(buf, "%" PRIu16, period);
Roytakb2794852023-10-18 14:30:22 +02001359 return nc_server_config_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001360 "netconf-client[name='%s']/connection-type/periodic/period", ch_client_name);
1361}
1362
1363API int
Roytakb2794852023-10-18 14:30:22 +02001364nc_server_config_del_ch_period(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001365{
1366 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1367
Roytakb2794852023-10-18 14:30:22 +02001368 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
roman8ba6efa2023-07-12 15:27:52 +02001369 "netconf-client[name='%s']/connection-type/periodic/period", ch_client_name);
1370}
1371
1372API int
Roytakb2794852023-10-18 14:30:22 +02001373nc_server_config_add_ch_anchor_time(const struct ly_ctx *ctx, const char *ch_client_name,
romanb6f44032023-06-30 15:07:56 +02001374 const char *anchor_time, struct lyd_node **config)
1375{
Roytak7b9bf292023-10-04 14:06:38 +02001376 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, anchor_time, config, 1);
romanb6f44032023-06-30 15:07:56 +02001377
1378 /* delete persistent tree if exists */
Roytakb2794852023-10-18 14:30:22 +02001379 if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001380 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1381 return 1;
1382 }
1383
Roytakb2794852023-10-18 14:30:22 +02001384 return nc_server_config_create(ctx, config, anchor_time, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001385 "netconf-client[name='%s']/connection-type/periodic/anchor-time", ch_client_name);
1386}
1387
1388API int
Roytakb2794852023-10-18 14:30:22 +02001389nc_server_config_del_ch_anchor_time(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001390{
1391 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1392
Roytakb2794852023-10-18 14:30:22 +02001393 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
roman8ba6efa2023-07-12 15:27:52 +02001394 "netconf-client[name='%s']/connection-type/periodic/anchor-time", ch_client_name);
1395}
1396
1397API int
Roytakb2794852023-10-18 14:30:22 +02001398nc_server_config_add_ch_idle_timeout(const struct ly_ctx *ctx, const char *ch_client_name,
romanb6f44032023-06-30 15:07:56 +02001399 uint16_t idle_timeout, struct lyd_node **config)
1400{
1401 char buf[6] = {0};
1402
Roytak7b9bf292023-10-04 14:06:38 +02001403 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
romanb6f44032023-06-30 15:07:56 +02001404
1405 /* delete persistent tree if exists */
Roytakb2794852023-10-18 14:30:22 +02001406 if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001407 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1408 return 1;
1409 }
1410
roman5d9fc732023-10-26 11:26:57 +02001411 sprintf(buf, "%" PRIu16, idle_timeout);
Roytakb2794852023-10-18 14:30:22 +02001412 return nc_server_config_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
romanb6f44032023-06-30 15:07:56 +02001413 "netconf-client[name='%s']/connection-type/periodic/idle-timeout", ch_client_name);
1414}
1415
1416API int
Roytakb2794852023-10-18 14:30:22 +02001417nc_server_config_del_ch_idle_timeout(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001418{
1419 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1420
Roytakb2794852023-10-18 14:30:22 +02001421 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
roman8ba6efa2023-07-12 15:27:52 +02001422 "netconf-client[name='%s']/connection-type/periodic/idle-timeout", ch_client_name);
1423}
1424
1425API int
Roytakb2794852023-10-18 14:30:22 +02001426nc_server_config_add_ch_reconnect_strategy(const struct ly_ctx *ctx, const char *ch_client_name,
roman8ba6efa2023-07-12 15:27:52 +02001427 NC_CH_START_WITH start_with, uint16_t max_wait, uint8_t max_attempts, struct lyd_node **config)
romanb6f44032023-06-30 15:07:56 +02001428{
1429 int ret = 0;
1430 char *path = NULL;
1431 char buf[6] = {0};
1432 const char *start_with_val;
1433
1434 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
1435
1436 /* prepared the path */
roman3a95bb22023-10-26 11:07:17 +02001437 ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/reconnect-strategy", ch_client_name);
1438 NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup);
romanb6f44032023-06-30 15:07:56 +02001439
romanded52582023-11-08 15:21:30 +01001440 /* get string value from enum */
1441 if (start_with == NC_CH_FIRST_LISTED) {
1442 start_with_val = "first-listed";
1443 } else if (start_with == NC_CH_LAST_CONNECTED) {
1444 start_with_val = "last-connected";
1445 } else if (start_with == NC_CH_RANDOM) {
1446 start_with_val = "random-selection";
1447 } else {
1448 ERR(NULL, "Unknown reconnect strategy.");
1449 goto cleanup;
1450 }
romanb6f44032023-06-30 15:07:56 +02001451
romanded52582023-11-08 15:21:30 +01001452 ret = nc_server_config_append(ctx, path, "start-with", start_with_val, config);
1453 if (ret) {
1454 goto cleanup;
romanb6f44032023-06-30 15:07:56 +02001455 }
1456
1457 if (max_attempts) {
roman5d9fc732023-10-26 11:26:57 +02001458 sprintf(buf, "%" PRIu8, max_attempts);
Roytakb2794852023-10-18 14:30:22 +02001459 ret = nc_server_config_append(ctx, path, "max-attempts", buf, config);
romanb6f44032023-06-30 15:07:56 +02001460 if (ret) {
1461 goto cleanup;
1462 }
1463 memset(buf, 0, 6);
1464 }
1465
1466 if (max_wait) {
roman5d9fc732023-10-26 11:26:57 +02001467 sprintf(buf, "%" PRIu16, max_wait);
Roytakb2794852023-10-18 14:30:22 +02001468 ret = nc_server_config_append(ctx, path, "max-wait", buf, config);
romanb6f44032023-06-30 15:07:56 +02001469 if (ret) {
1470 goto cleanup;
1471 }
1472 }
1473
1474cleanup:
1475 free(path);
1476 return ret;
1477}
roman8ba6efa2023-07-12 15:27:52 +02001478
1479API int
Roytakb2794852023-10-18 14:30:22 +02001480nc_server_config_del_ch_reconnect_strategy(const char *ch_client_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001481{
1482 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1483
Roytakb2794852023-10-18 14:30:22 +02001484 return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
roman8ba6efa2023-07-12 15:27:52 +02001485 "netconf-client[name='%s']/reconnect-strategy", ch_client_name);
1486}