blob: 01f63d6deae75964e70585ea62b767db478953d5 [file] [log] [blame]
roman3f9b65c2023-06-05 14:26:58 +02001/**
2 * @file config_new.c
3 * @author Roman Janota <janota@cesnet.cz>
4 * @brief libnetconf2 server new configuration creation functions
5 *
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
roman2eab4742023-06-06 10:00:26 +020018#include <libyang/libyang.h>
romand30af552023-06-16 15:18:27 +020019#include <stdarg.h>
roman3f9b65c2023-06-05 14:26:58 +020020#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23
roman2eab4742023-06-06 10:00:26 +020024#ifdef NC_ENABLED_SSH_TLS
roman3f9b65c2023-06-05 14:26:58 +020025#include <libssh/libssh.h>
roman3f9b65c2023-06-05 14:26:58 +020026#include <openssl/err.h>
27#include <openssl/evp.h>
28#include <openssl/pem.h>
roman2eab4742023-06-06 10:00:26 +020029#endif /* NC_ENABLED_SSH_TLS */
roman3f9b65c2023-06-05 14:26:58 +020030
31#include "compat.h"
32#include "config_new.h"
33#include "log_p.h"
34#include "session.h"
35#include "session_p.h"
36
roman8ba6efa2023-07-12 15:27:52 +020037int
38nc_config_new_delete(struct lyd_node **tree, const char *path_fmt, ...)
39{
40 int ret = 0;
41 va_list ap;
42 char *path = NULL;
43 struct lyd_node *sub = NULL;
44
45 va_start(ap, path_fmt);
46
47 /* create the path from the format */
48 ret = vasprintf(&path, path_fmt, ap);
49 if (ret == -1) {
50 ERRMEM;
51 path = NULL;
52 goto cleanup;
53 }
54
55 /* find the node we want to delete */
56 ret = lyd_find_path(*tree, path, 0, &sub);
57 if (ret) {
58 goto cleanup;
59 }
60
61 lyd_free_tree(sub);
62
63 /* set the node to top level container */
64 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
65 if (ret) {
66 goto cleanup;
67 }
68
69 /* add all default nodes */
70 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
71 if (ret) {
72 goto cleanup;
73 }
74
75cleanup:
76 free(path);
77 va_end(ap);
78 return ret;
79}
80
81int
82nc_config_new_create(const struct ly_ctx *ctx, struct lyd_node **tree, const char *value, const char *path_fmt, ...)
83{
84 int ret = 0;
85 va_list ap;
86 char *path = NULL;
87
88 va_start(ap, path_fmt);
89
90 /* create the path from the format */
91 ret = vasprintf(&path, path_fmt, ap);
92 if (ret == -1) {
93 ERRMEM;
94 path = NULL;
95 goto cleanup;
96 }
97
98 /* create the nodes in the path */
roman5ef2a572023-08-18 15:45:44 +020099 if (!*tree) {
100 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
101 } else {
102 /* this could output NULL if no new nodes, lyd_find_path would fail then */
103 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, NULL);
104 }
roman8ba6efa2023-07-12 15:27:52 +0200105 if (ret) {
106 goto cleanup;
107 }
108
109 /* set the node to the top level node */
110 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
111 if (ret) {
112 goto cleanup;
113 }
114
115 /* add all default nodes */
116 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
117 if (ret) {
118 goto cleanup;
119 }
120
121cleanup:
122 free(path);
123 va_end(ap);
124 return ret;
125}
126
127int
128nc_config_new_create_append(const struct ly_ctx *ctx, const char *parent_path, const char *child_name,
129 const char *value, struct lyd_node **tree)
130{
131 int ret = 0;
132 char *path = NULL;
133
134 /* create the path by appending child to the parent path */
135 ret = asprintf(&path, "%s/%s", parent_path, child_name);
136 if (ret == -1) {
137 ERRMEM;
138 path = NULL;
139 goto cleanup;
140 }
141
142 /* create the nodes in the path */
roman5ef2a572023-08-18 15:45:44 +0200143 if (!*tree) {
144 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
145 } else {
146 /* this could output NULL if no new nodes, lyd_find_path would fail then */
147 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, NULL);
148 }
roman8ba6efa2023-07-12 15:27:52 +0200149 if (ret) {
150 goto cleanup;
151 }
152
153 /* set the node to the top level node */
154 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
155 if (ret) {
156 goto cleanup;
157 }
158
159 /* add all default nodes */
160 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
161 if (ret) {
162 goto cleanup;
163 }
164
165cleanup:
166 free(path);
167 return ret;
168}
169
170int
171nc_config_new_check_delete(struct lyd_node **tree, const char *path_fmt, ...)
172{
173 int ret = 0;
174 va_list ap;
175 char *path = NULL;
176 struct lyd_node *sub = NULL;
177
178 va_start(ap, path_fmt);
179
180 /* create the path from the format */
181 ret = vasprintf(&path, path_fmt, ap);
182 if (ret == -1) {
183 ERRMEM;
184 path = NULL;
185 goto cleanup;
186 }
187
188 /* find the node we want to delete */
189 ret = lyd_find_path(*tree, path, 0, &sub);
190 if ((ret == LY_EINCOMPLETE) || (ret == LY_ENOTFOUND)) {
191 ret = 0;
192 goto cleanup;
193 } else if (ret) {
194 ERR(NULL, "Unable to delete node in the path \"%s\".", path);
195 goto cleanup;
196 }
197
198 lyd_free_tree(sub);
199
200 /* set the node to top level container */
201 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
202 if (ret) {
203 goto cleanup;
204 }
205
206cleanup:
207 free(path);
208 va_end(ap);
209 return ret;
210}
211
roman2eab4742023-06-06 10:00:26 +0200212#ifdef NC_ENABLED_SSH_TLS
213
roman3f9b65c2023-06-05 14:26:58 +0200214const char *
215nc_config_new_privkey_format_to_identityref(NC_PRIVKEY_FORMAT format)
216{
217 switch (format) {
218 case NC_PRIVKEY_FORMAT_RSA:
219 return "ietf-crypto-types:rsa-private-key-format";
220 case NC_PRIVKEY_FORMAT_EC:
221 return "ietf-crypto-types:ec-private-key-format";
222 case NC_PRIVKEY_FORMAT_X509:
roman13145912023-08-17 15:36:54 +0200223 return "libnetconf2-netconf-server:private-key-info-format";
roman3f9b65c2023-06-05 14:26:58 +0200224 case NC_PRIVKEY_FORMAT_OPENSSH:
225 return "libnetconf2-netconf-server:openssh-private-key-format";
226 default:
227 ERR(NULL, "Private key type not supported.");
228 return NULL;
229 }
230}
231
roman13145912023-08-17 15:36:54 +0200232static int
233nc_server_config_new_pubkey_bin_to_b64(const unsigned char *pub_bin, int bin_len, char **pubkey)
234{
235 int ret = 0, b64_len;
236 char *pub_b64 = NULL;
237
238 /* get b64 buffer len, for ever 3 bytes of bin 4 bytes of b64 + NULL terminator */
239 if (bin_len % 3 == 0) {
240 pub_b64 = malloc((bin_len / 3) * 4 + 1);
241 } else {
242 /* bin len not divisible by 3, need to add 4 bytes for some padding so that the len is divisible by 4 */
243 pub_b64 = malloc((bin_len / 3) * 4 + 4 + 1);
244 }
245 if (!pub_b64) {
246 ERRMEM;
247 ret = 1;
248 goto cleanup;
249 }
250
251 /* bin to b64 */
252 b64_len = EVP_EncodeBlock((unsigned char *)pub_b64, pub_bin, bin_len);
253 *pubkey = strndup(pub_b64, b64_len);
254 if (!*pubkey) {
255 ERRMEM;
256 ret = 1;
257 goto cleanup;
258 }
259
260cleanup:
261 free(pub_b64);
262 return ret;
263}
264
265static int
266nc_server_config_new_bn_to_bin(const BIGNUM *bn, unsigned char **bin, int *bin_len)
267{
268 int ret = 0;
269 unsigned char *bin_tmp = NULL;
270
271 NC_CHECK_ARG_RET(NULL, bn, bin, bin_len, 1);
272
273 *bin = NULL;
274
275 /* prepare buffer for converting BN to binary */
276 bin_tmp = calloc(BN_num_bytes(bn), sizeof *bin_tmp);
277 if (!bin_tmp) {
278 ERRMEM;
279 return 1;
280 }
281
282 /* convert to binary */
283 *bin_len = BN_bn2bin(bn, bin_tmp);
284
285 /* if the highest bit in the MSB is set a byte with the value 0 has to be prepended */
286 if (bin_tmp[0] & 0x80) {
287 *bin = malloc(*bin_len + 1);
288 if (!*bin) {
289 ERRMEM;
290 ret = 1;
291 goto cleanup;
292 }
293
294 (*bin)[0] = 0;
295 memcpy(*bin + 1, bin_tmp, *bin_len);
296 (*bin_len)++;
297 } else {
298 *bin = malloc(*bin_len);
299 if (!*bin) {
300 ERRMEM;
301 ret = 1;
302 goto cleanup;
303 }
304
305 memcpy(*bin, bin_tmp, *bin_len);
306 }
307
308cleanup:
309 free(bin_tmp);
310 return ret;
311}
312
313/* ssh pubkey defined in RFC 4253 section 6.6 */
314static int
315nc_server_config_new_evp_pkey_to_ssh_pubkey(EVP_PKEY *pkey, char **pubkey)
316{
317 int ret = 0, e_len, n_len, p_len, bin_len;
318 BIGNUM *e = NULL, *n = NULL, *p = NULL;
319 unsigned char *e_bin = NULL, *n_bin = NULL, *p_bin = NULL, *bin = NULL, *bin_tmp;
320 const char *algorithm_name, *curve_name;
321 char *ec_group = NULL;
322 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;
323 size_t ec_group_len;
324
325 if (EVP_PKEY_is_a(pkey, "RSA")) {
326 /* RSA key */
327 algorithm_name = "ssh-rsa";
328
329 /* get the public key params */
330 if (!EVP_PKEY_get_bn_param(pkey, "e", &e) || !EVP_PKEY_get_bn_param(pkey, "n", &n)) {
331 ERR(NULL, "Getting public key parameters from RSA private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
332 ret = 1;
333 goto cleanup;
334 }
335
336 /* BIGNUM to bin */
337 if (nc_server_config_new_bn_to_bin(e, &e_bin, &e_len) || nc_server_config_new_bn_to_bin(n, &n_bin, &n_len)) {
338 ret = 1;
339 goto cleanup;
340 }
341
342 alg_name_len = strlen(algorithm_name);
343 /* buffer for public key in binary, which looks like this:
344 * alg_name len (4 bytes), alg_name, PK exponent len (4 bytes), PK exponent, modulus len (4 bytes), modulus
345 */
346 bin_len = 4 + alg_name_len + 4 + e_len + 4 + n_len;
347 bin = malloc(bin_len);
348 if (!bin) {
349 ERRMEM;
350 ret = 1;
351 goto cleanup;
352 }
353
354 /* to network byte order (big endian) */
355 alg_name_len_be = htonl(alg_name_len);
356 e_len_be = htonl(e_len);
357 n_len_be = htonl(n_len);
358
359 /* create the public key in binary */
360 bin_tmp = bin;
361 memcpy(bin_tmp, &alg_name_len_be, 4);
362 bin_tmp += 4;
363 memcpy(bin_tmp, algorithm_name, alg_name_len);
364 bin_tmp += alg_name_len;
365 memcpy(bin_tmp, &e_len_be, 4);
366 bin_tmp += 4;
367 memcpy(bin_tmp, e_bin, e_len);
368 bin_tmp += e_len;
369 memcpy(bin_tmp, &n_len_be, 4);
370 bin_tmp += 4;
371 memcpy(bin_tmp, n_bin, n_len);
372 } else if (EVP_PKEY_is_a(pkey, "EC")) {
373 /* EC Private key, get it's group first */
374 /* get group len */
375 ret = EVP_PKEY_get_utf8_string_param(pkey, "group", NULL, 0, &ec_group_len);
376 if (!ret) {
377 ret = 1;
378 goto cleanup;
379 }
380 /* alloc mem for group + 1 for \0 */
381 ec_group = malloc(ec_group_len + 1);
382 if (!ec_group) {
383 ERRMEM;
384 ret = 1;
385 goto cleanup;
386 }
387 /* get the group */
388 ret = EVP_PKEY_get_utf8_string_param(pkey, "group", ec_group, ec_group_len + 1, NULL);
389 if (!ret) {
390 ERR(NULL, "Getting public key parameter from EC private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
391 ret = 1;
392 goto cleanup;
393 }
394
395 /* get alg and curve names */
396 if (!strcmp(ec_group, "P-256") || !strcmp(ec_group, "secp256r1") || !strcmp(ec_group, "prime256v1")) {
397 algorithm_name = "ecdsa-sha2-nistp256";
398 curve_name = "nistp256";
399 } else if (!strcmp(ec_group, "P-384") || !strcmp(ec_group, "secp384r1")) {
400 algorithm_name = "ecdsa-sha2-nistp384";
401 curve_name = "nistp384";
402 } else if (!strcmp(ec_group, "P-521") || !strcmp(ec_group, "secp521r1")) {
403 algorithm_name = "ecdsa-sha2-nistp521";
404 curve_name = "nistp521";
405 } else {
406 ERR(NULL, "EC group \"%s\" not supported.", ec_group);
407 ret = 1;
408 goto cleanup;
409 }
410
411 /* get the public key - p, which is a point on the elliptic curve */
412 ret = EVP_PKEY_get_bn_param(pkey, "p", &p);
413 if (!ret) {
414 ERR(NULL, "Getting public key point from the EC private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
415 ret = 1;
416 goto cleanup;
417 }
418
419 /* prepare buffer for converting p to binary */
420 p_bin = malloc(BN_num_bytes(p));
421 if (!p_bin) {
422 ERRMEM;
423 ret = 1;
424 goto cleanup;
425 }
426 /* convert to binary */
427 p_len = BN_bn2bin(p, p_bin);
428
429 alg_name_len = strlen(algorithm_name);
430 curve_name_len = strlen(curve_name);
431 /* buffer for public key in binary, which looks like so:
432 * alg_name len (4 bytes), alg_name, curve_name len (4 bytes), curve_name, PK point p len (4 bytes), PK point p
433 */
434 bin_len = 4 + alg_name_len + 4 + curve_name_len + 4 + p_len;
435 bin = malloc(bin_len);
436 if (!bin) {
437 ERRMEM;
438 ret = 1;
439 goto cleanup;
440 }
441
442 /* to network byte order (big endian) */
443 alg_name_len_be = htonl(alg_name_len);
444 curve_name_len_be = htonl(curve_name_len);
445 p_len_be = htonl(p_len);
446
447 /* create the public key in binary */
448 bin_tmp = bin;
449 memcpy(bin_tmp, &alg_name_len_be, 4);
450 bin_tmp += 4;
451 memcpy(bin_tmp, algorithm_name, alg_name_len);
452 bin_tmp += alg_name_len;
453 memcpy(bin_tmp, &curve_name_len_be, 4);
454 bin_tmp += 4;
455 memcpy(bin_tmp, curve_name, curve_name_len);
456 bin_tmp += curve_name_len;
457 memcpy(bin_tmp, &p_len_be, 4);
458 bin_tmp += 4;
459 memcpy(bin_tmp, p_bin, p_len);
460 } else if (EVP_PKEY_is_a(pkey, "ED25519")) {
461 ERR(NULL, "Generating PEM ED25519 key from OpenSSH is not supported by libssh yet.");
462 ret = 1;
463 goto cleanup;
464 } else {
465 ERR(NULL, "Unable to generate public key from private key (Private key type not supported).");
466 ret = 1;
467 goto cleanup;
468 }
469
470 ret = nc_server_config_new_pubkey_bin_to_b64(bin, bin_len, pubkey);
471 if (ret) {
472 ERR(NULL, "Converting public key from binary to base64 failed.");
473 goto cleanup;
474 }
475
476cleanup:
477 free(bin);
478 free(e_bin);
479 free(n_bin);
480 free(ec_group);
481 free(p_bin);
482 BN_free(e);
483 BN_free(n);
484 BN_free(p);
485 return ret;
486}
487
488/* spki = subject public key info */
489static int
490nc_server_config_new_evp_pkey_to_spki_pubkey(EVP_PKEY *pkey, char **pubkey)
491{
492 int ret = 0, len;
493 BIO *bio = NULL;
494 char *pub_b64 = NULL;
495
496 bio = BIO_new(BIO_s_mem());
497 if (!bio) {
498 ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
499 ret = 1;
500 goto cleanup;
501 }
502
503 /* write the evp_pkey contents to bio */
504 if (!PEM_write_bio_PUBKEY(bio, pkey)) {
505 ERR(NULL, "Writing public key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
506 ret = 1;
507 goto cleanup;
508 }
509
510 /* read the pubkey from bio */
511 len = BIO_get_mem_data(bio, &pub_b64);
512 if (len <= 0) {
513 ERR(NULL, "Reading base64 private key from BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
514 ret = 1;
515 goto cleanup;
516 }
517
518 /* copy the public key without the header and footer */
519 *pubkey = strndup(pub_b64 + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER),
520 len - strlen(NC_SUBJECT_PUBKEY_INFO_HEADER) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER));
521 if (!*pubkey) {
522 ERRMEM;
523 ret = 1;
524 goto cleanup;
525 }
526
527cleanup:
528 BIO_free(bio);
529 return ret;
530}
531
roman3f9b65c2023-06-05 14:26:58 +0200532int
533nc_server_config_new_read_certificate(const char *cert_path, char **cert)
534{
535 int ret = 0, cert_len;
536 X509 *x509 = NULL;
537 FILE *f = NULL;
538 BIO *bio = NULL;
539 char *c = NULL;
540
541 *cert = NULL;
542
543 f = fopen(cert_path, "r");
544 if (!f) {
545 ERR(NULL, "Unable to open certificate file \"%s\".", cert_path);
546 ret = 1;
547 goto cleanup;
548 }
549
550 /* load the cert into memory */
551 x509 = PEM_read_X509(f, NULL, NULL, NULL);
552 if (!x509) {
553 ret = -1;
554 goto cleanup;
555 }
556
557 bio = BIO_new(BIO_s_mem());
558 if (!bio) {
559 ret = -1;
560 goto cleanup;
561 }
562
563 ret = PEM_write_bio_X509(bio, x509);
564 if (!ret) {
565 ret = -1;
566 goto cleanup;
567 }
568
569 cert_len = BIO_pending(bio);
570 if (cert_len <= 0) {
571 ret = -1;
572 goto cleanup;
573 }
574
575 c = malloc(cert_len + 1);
576 if (!c) {
577 ERRMEM;
578 ret = 1;
579 goto cleanup;
580 }
581
582 /* read the cert from bio */
583 ret = BIO_read(bio, c, cert_len);
584 if (ret <= 0) {
585 ret = -1;
586 goto cleanup;
587 }
588 c[cert_len] = '\0';
589
590 /* strip the cert of the header and footer */
591 *cert = strdup(c + strlen(NC_PEM_CERTIFICATE_HEADER));
592 if (!*cert) {
593 ERRMEM;
594 ret = 1;
595 goto cleanup;
596 }
597
598 (*cert)[strlen(*cert) - strlen(NC_PEM_CERTIFICATE_FOOTER)] = '\0';
599
600 ret = 0;
601
602cleanup:
603 if (ret == -1) {
604 ERR(NULL, "Error getting certificate from file \"%s\" (OpenSSL Error): \"%s\".", cert_path, ERR_reason_error_string(ERR_get_error()));
605 ret = 1;
606 }
607 if (f) {
608 fclose(f);
609 }
610
611 BIO_free(bio);
612 X509_free(x509);
613 free(c);
614 return ret;
615}
616
617static int
roman13145912023-08-17 15:36:54 +0200618nc_server_config_new_read_pubkey_ssh2(FILE *f, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200619{
620 char *buffer = NULL;
621 size_t size = 0, pubkey_len = 0;
622 void *tmp;
623 ssize_t read;
624 int ret = 0;
625
626 while ((read = getline(&buffer, &size, f)) > 0) {
627 if (!strncmp(buffer, "----", 4)) {
628 continue;
629 }
630
631 if (!strncmp(buffer, "Comment:", 8)) {
632 continue;
633 }
634
635 if (buffer[read - 1] == '\n') {
636 read--;
637 }
638
639 tmp = realloc(*pubkey, pubkey_len + read + 1);
640 if (!tmp) {
641 ERRMEM;
642 ret = 1;
643 goto cleanup;
644 }
645
646 *pubkey = tmp;
647 memcpy(*pubkey + pubkey_len, buffer, read);
648 pubkey_len += read;
649 }
650
651 if (!pubkey_len) {
652 ERR(NULL, "Unexpected public key format.");
653 ret = 1;
654 goto cleanup;
655 }
656
657 (*pubkey)[pubkey_len] = '\0';
658
659cleanup:
660 free(buffer);
661 return ret;
662}
663
664static int
665nc_server_config_new_read_pubkey_openssl(FILE *f, char **pubkey)
666{
667 int ret = 0;
roman13145912023-08-17 15:36:54 +0200668 EVP_PKEY *pub_pkey = NULL;
669
670 NC_CHECK_ARG_RET(NULL, f, pubkey, 1);
roman3f9b65c2023-06-05 14:26:58 +0200671
672 /* read the pubkey from file */
roman13145912023-08-17 15:36:54 +0200673 pub_pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
674 if (!pub_pkey) {
675 ERR(NULL, "Reading public key from file failed (%s).", ERR_reason_error_string(ERR_get_error()));
676 return 1;
roman3f9b65c2023-06-05 14:26:58 +0200677 }
678
roman13145912023-08-17 15:36:54 +0200679 ret = nc_server_config_new_evp_pkey_to_ssh_pubkey(pub_pkey, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200680
roman13145912023-08-17 15:36:54 +0200681 EVP_PKEY_free(pub_pkey);
roman3f9b65c2023-06-05 14:26:58 +0200682 return ret;
683}
684
roman3f9b65c2023-06-05 14:26:58 +0200685static int
686nc_server_config_new_read_pubkey_libssh(const char *pubkey_path, char **pubkey)
687{
688 int ret = 0;
689 ssh_key pub_sshkey = NULL;
690
roman13145912023-08-17 15:36:54 +0200691 NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
692
roman3f9b65c2023-06-05 14:26:58 +0200693 ret = ssh_pki_import_pubkey_file(pubkey_path, &pub_sshkey);
694 if (ret) {
695 ERR(NULL, "Importing public key from file \"%s\" failed.", pubkey_path);
696 return ret;
697 }
698
699 ret = ssh_pki_export_pubkey_base64(pub_sshkey, pubkey);
700 if (ret) {
roman13145912023-08-17 15:36:54 +0200701 ERR(NULL, "Importing pubkey failed.");
702 goto cleanup;
roman3f9b65c2023-06-05 14:26:58 +0200703 }
704
roman13145912023-08-17 15:36:54 +0200705cleanup:
roman3f9b65c2023-06-05 14:26:58 +0200706 ssh_key_free(pub_sshkey);
roman13145912023-08-17 15:36:54 +0200707 return 0;
roman3f9b65c2023-06-05 14:26:58 +0200708}
709
roman3f9b65c2023-06-05 14:26:58 +0200710int
roman13145912023-08-17 15:36:54 +0200711nc_server_config_new_get_ssh_pubkey_file(const char *pubkey_path, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200712{
713 int ret = 0;
714 FILE *f = NULL;
715 char *header = NULL;
716 size_t len = 0;
717
roman13145912023-08-17 15:36:54 +0200718 NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
roman3f9b65c2023-06-05 14:26:58 +0200719
720 *pubkey = NULL;
721
722 f = fopen(pubkey_path, "r");
723 if (!f) {
724 ERR(NULL, "Unable to open file \"%s\".", pubkey_path);
725 ret = 1;
726 goto cleanup;
727 }
728
roman13145912023-08-17 15:36:54 +0200729 /* read the header */
roman3f9b65c2023-06-05 14:26:58 +0200730 if (getline(&header, &len, f) < 0) {
731 ERR(NULL, "Error reading header from file \"%s\".", pubkey_path);
732 ret = 1;
733 goto cleanup;
734 }
735 rewind(f);
736
737 if (!strncmp(header, NC_SUBJECT_PUBKEY_INFO_HEADER, strlen(NC_SUBJECT_PUBKEY_INFO_HEADER))) {
738 /* it's subject public key info public key */
739 ret = nc_server_config_new_read_pubkey_openssl(f, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200740 } else if (!strncmp(header, NC_SSH2_PUBKEY_HEADER, strlen(NC_SSH2_PUBKEY_HEADER))) {
741 /* it's ssh2 public key */
roman13145912023-08-17 15:36:54 +0200742 ret = nc_server_config_new_read_pubkey_ssh2(f, pubkey);
roman7fdc84d2023-06-06 13:14:53 +0200743 } else {
roman3f9b65c2023-06-05 14:26:58 +0200744 /* it's probably OpenSSH public key */
745 ret = nc_server_config_new_read_pubkey_libssh(pubkey_path, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200746 }
roman3f9b65c2023-06-05 14:26:58 +0200747 if (ret) {
748 ERR(NULL, "Error getting public key from file \"%s\".", pubkey_path);
749 goto cleanup;
750 }
751
752cleanup:
753 if (f) {
754 fclose(f);
755 }
756
757 free(header);
roman3f9b65c2023-06-05 14:26:58 +0200758 return ret;
759}
760
roman3f9b65c2023-06-05 14:26:58 +0200761int
roman13145912023-08-17 15:36:54 +0200762nc_server_config_new_get_spki_pubkey_file(const char *pubkey_path, char **pubkey)
roman3f9b65c2023-06-05 14:26:58 +0200763{
764 int ret = 0;
roman13145912023-08-17 15:36:54 +0200765 FILE *f = NULL;
766 EVP_PKEY *pub_pkey = NULL;
roman3f9b65c2023-06-05 14:26:58 +0200767
roman13145912023-08-17 15:36:54 +0200768 NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
roman3f9b65c2023-06-05 14:26:58 +0200769
roman3f9b65c2023-06-05 14:26:58 +0200770 *pubkey = NULL;
771
roman13145912023-08-17 15:36:54 +0200772 f = fopen(pubkey_path, "r");
773 if (!f) {
774 ERR(NULL, "Unable to open file \"%s\".", pubkey_path);
775 ret = 1;
776 goto cleanup;
777 }
778
779 /* read the pubkey from file */
780 pub_pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
781 if (!pub_pkey) {
782 ERR(NULL, "Reading public key from file failed (%s).", ERR_reason_error_string(ERR_get_error()));
783 return 1;
784 }
785
786 ret = nc_server_config_new_evp_pkey_to_spki_pubkey(pub_pkey, pubkey);
787 if (ret) {
788 goto cleanup;
789 }
790
791cleanup:
792 if (f) {
793 fclose(f);
794 }
795
796 EVP_PKEY_free(pub_pkey);
797 return ret;
798}
799
800static int
801nc_server_config_new_privkey_header_to_format(FILE *f_privkey, const char *privkey_path, NC_PRIVKEY_FORMAT *privkey_format)
802{
803 char *privkey_header = NULL;
804 size_t len = 0;
805
806 /* read header */
807 if (getline(&privkey_header, &len, f_privkey) < 0) {
808 ERR(NULL, "Error reading header from file \"%s\".", privkey_path);
809 return 1;
810 }
811
812 if (!strncmp(privkey_header, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) {
813 /* it's PKCS8 (X.509) private key */
814 *privkey_format = NC_PRIVKEY_FORMAT_X509;
815 } else if (!strncmp(privkey_header, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) {
816 /* it's OpenSSH private key */
817 *privkey_format = NC_PRIVKEY_FORMAT_OPENSSH;
818 } else if (!strncmp(privkey_header, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) {
819 /* it's RSA privkey in PKCS1 format */
820 *privkey_format = NC_PRIVKEY_FORMAT_RSA;
821 } else if (!strncmp(privkey_header, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) {
822 /* it's EC privkey in SEC1 format */
823 *privkey_format = NC_PRIVKEY_FORMAT_EC;
824 } else {
825 ERR(NULL, "Private key format (%s) not supported.", privkey_header);
826 free(privkey_header);
827 return 1;
828 }
829
830 /* reset the reading head */
831 rewind(f_privkey);
832 free(privkey_header);
833 return 0;
834}
835
836static int
837nc_server_config_new_get_privkey_openssl(const char *privkey_path, FILE *f_privkey, char **privkey, EVP_PKEY **pkey)
838{
839 int ret = 0, len;
840 BIO *bio = NULL;
841 char *priv_b64 = NULL;
842
843 bio = BIO_new(BIO_s_mem());
844 if (!bio) {
845 ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
846 ret = 1;
847 goto cleanup;
848 }
849
850 /* read the privkey file, create EVP_PKEY */
851 *pkey = PEM_read_PrivateKey(f_privkey, NULL, NULL, NULL);
852 if (!*pkey) {
853 ERR(NULL, "Getting private key from file \"%s\" failed (%s).", privkey_path, ERR_reason_error_string(ERR_get_error()));
854 ret = 1;
855 goto cleanup;
856 }
857
858 /* write the privkey to bio */
859 if (!PEM_write_bio_PrivateKey(bio, *pkey, NULL, NULL, 0, NULL, NULL)) {
860 ERR(NULL, "Writing private key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
861 ret = 1;
862 goto cleanup;
863 }
864
865 /* read the privkey from bio */
866 len = BIO_get_mem_data(bio, &priv_b64);
867 if (len <= 0) {
868 ERR(NULL, "Reading base64 private key from BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
869 ret = 1;
870 goto cleanup;
871 }
872
873 *privkey = strndup(priv_b64, len);
874
875cleanup:
876 BIO_free(bio);
877 return ret;
878}
879
880static int
881nc_server_config_new_get_privkey_libssh(const char *privkey_path, char **privkey, EVP_PKEY **pkey)
882{
883 int ret = 0;
884 BIO *bio = NULL;
885 char *priv_b64 = NULL;
886 ssh_key key = NULL;
887
888 ret = ssh_pki_import_privkey_file(privkey_path, NULL, NULL, NULL, &key);
889 if (ret) {
890 ERR(NULL, "Importing privkey from file \"%s\" failed.", privkey_path);
891 goto cleanup;
892 }
893
894 /* exports the key in a format in which OpenSSL can read it */
895 ret = ssh_pki_export_privkey_base64(key, NULL, NULL, NULL, &priv_b64);
896 if (ret) {
897 ERR(NULL, "Exporting privkey to base64 failed.");
898 goto cleanup;
899 }
900
901 bio = BIO_new(BIO_s_mem());
902 if (!bio) {
903 ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
904 ret = 1;
905 goto cleanup;
906 }
907
908 ret = BIO_write(bio, priv_b64, strlen(priv_b64));
909 if (ret <= 0) {
910 ERR(NULL, "Writing private key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
911 ret = 1;
912 goto cleanup;
913 }
914
915 /* create EVP_PKEY from the b64 */
916 *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
917 if (!*pkey) {
918 ERR(NULL, "Getting private key from file \"%s\" failed (%s).", privkey_path, ERR_reason_error_string(ERR_get_error()));
919 ret = 1;
920 goto cleanup;
921 }
922
923 *privkey = strndup(priv_b64, ret);
924
925 /* ok */
926 ret = 0;
927
928cleanup:
929 free(priv_b64);
930 BIO_free(bio);
931 ssh_key_free(key);
932 return ret;
933}
934
935static int
936nc_server_config_new_get_privkey(const char *privkey_path, NC_PRIVKEY_FORMAT *privkey_format, char **privkey, EVP_PKEY **pkey)
937{
938 int ret = 0;
939 FILE *f_privkey = NULL;
940 char *priv = NULL;
941
roman3f9b65c2023-06-05 14:26:58 +0200942 f_privkey = fopen(privkey_path, "r");
943 if (!f_privkey) {
944 ERR(NULL, "Unable to open file \"%s\".", privkey_path);
945 ret = 1;
946 goto cleanup;
947 }
948
roman13145912023-08-17 15:36:54 +0200949 /* read the first line from the privkey to determine it's type */
950 ret = nc_server_config_new_privkey_header_to_format(f_privkey, privkey_path, privkey_format);
951 if (ret) {
952 ERR(NULL, "Getting private key format from file \"%s\" failed.", privkey_path);
roman3f9b65c2023-06-05 14:26:58 +0200953 goto cleanup;
954 }
roman3f9b65c2023-06-05 14:26:58 +0200955
roman13145912023-08-17 15:36:54 +0200956 switch (*privkey_format) {
957 /* fall-through */
958 case NC_PRIVKEY_FORMAT_RSA:
959 case NC_PRIVKEY_FORMAT_EC:
960 case NC_PRIVKEY_FORMAT_X509:
961 /* OpenSSL solely can do this */
962 ret = nc_server_config_new_get_privkey_openssl(privkey_path, f_privkey, &priv, pkey);
963 break;
964 case NC_PRIVKEY_FORMAT_OPENSSH:
965 /* need the help of libssh */
966 ret = nc_server_config_new_get_privkey_libssh(privkey_path, &priv, pkey);
967 /* if the function returned successfully, the key is no longer OpenSSH, it was converted to x509 */
968 *privkey_format = NC_PRIVKEY_FORMAT_X509;
969 break;
970 default:
971 ERR(NULL, "Private key format not recognized.");
roman3f9b65c2023-06-05 14:26:58 +0200972 ret = 1;
roman13145912023-08-17 15:36:54 +0200973 break;
roman3f9b65c2023-06-05 14:26:58 +0200974 }
roman3f9b65c2023-06-05 14:26:58 +0200975 if (ret) {
976 goto cleanup;
977 }
978
romand30af552023-06-16 15:18:27 +0200979 /* strip private key's header and footer */
roman13145912023-08-17 15:36:54 +0200980 *privkey = strdup(priv + strlen(NC_PKCS8_PRIVKEY_HEADER));
981 if (!*privkey) {
982 ERRMEM;
983 ret = 1;
984 goto cleanup;
romand30af552023-06-16 15:18:27 +0200985 }
roman13145912023-08-17 15:36:54 +0200986 (*privkey)[strlen(*privkey) - strlen(NC_PKCS8_PRIVKEY_FOOTER)] = '\0';
romand30af552023-06-16 15:18:27 +0200987
roman3f9b65c2023-06-05 14:26:58 +0200988cleanup:
989 if (f_privkey) {
990 fclose(f_privkey);
991 }
992
romand30af552023-06-16 15:18:27 +0200993 free(priv);
roman13145912023-08-17 15:36:54 +0200994 return ret;
995}
roman3f9b65c2023-06-05 14:26:58 +0200996
roman13145912023-08-17 15:36:54 +0200997int
998nc_server_config_new_get_asym_key_pair(const char *privkey_path, const char *pubkey_path, NC_PUBKEY_FORMAT wanted_pubkey_format,
999 char **privkey, NC_PRIVKEY_FORMAT *privkey_type, char **pubkey)
1000{
1001 int ret = 0;
1002 EVP_PKEY *priv_pkey = NULL;
1003
1004 NC_CHECK_ARG_RET(NULL, privkey_path, privkey, privkey_type, pubkey, 1);
1005
1006 *privkey = NULL;
1007 *pubkey = NULL;
1008
1009 /* get private key base64 and EVP_PKEY */
1010 ret = nc_server_config_new_get_privkey(privkey_path, privkey_type, privkey, &priv_pkey);
1011 if (ret) {
1012 ERR(NULL, "Getting private key from file \"%s\" failed.", privkey_path);
1013 goto cleanup;
1014 }
1015
1016 /* get public key, either from file or generate it from the EVP_PKEY */
1017 if (!pubkey_path) {
1018 if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) {
1019 ret = nc_server_config_new_evp_pkey_to_ssh_pubkey(priv_pkey, pubkey);
1020 } else {
1021 ret = nc_server_config_new_evp_pkey_to_spki_pubkey(priv_pkey, pubkey);
1022 }
1023 } else {
1024 if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) {
1025 ret = nc_server_config_new_get_ssh_pubkey_file(pubkey_path, pubkey);
1026 } else {
1027 ret = nc_server_config_new_get_spki_pubkey_file(pubkey_path, pubkey);
1028 }
1029 }
1030 if (ret) {
1031 if (pubkey_path) {
1032 ERR(NULL, "Getting public key from file \"%s\" failed.", pubkey_path);
1033 } else {
1034 ERR(NULL, "Generating public key from private key failed.");
1035 }
1036 goto cleanup;
1037 }
1038
1039cleanup:
roman3f9b65c2023-06-05 14:26:58 +02001040 EVP_PKEY_free(priv_pkey);
roman3f9b65c2023-06-05 14:26:58 +02001041 return ret;
1042}
1043
1044API int
1045nc_server_config_new_address_port(const struct ly_ctx *ctx, const char *endpt_name, NC_TRANSPORT_IMPL transport,
roman142718b2023-06-29 09:15:29 +02001046 const char *address, uint16_t port, struct lyd_node **config)
roman3f9b65c2023-06-05 14:26:58 +02001047{
1048 int ret = 0;
romand30af552023-06-16 15:18:27 +02001049 const char *address_fmt, *port_fmt;
roman142718b2023-06-29 09:15:29 +02001050 char port_buf[6] = {0};
roman3f9b65c2023-06-05 14:26:58 +02001051
roman142718b2023-06-29 09:15:29 +02001052 NC_CHECK_ARG_RET(NULL, address, ctx, endpt_name, config, 1);
roman3f9b65c2023-06-05 14:26:58 +02001053
roman3f9b65c2023-06-05 14:26:58 +02001054 if (transport == NC_TI_LIBSSH) {
romand30af552023-06-16 15:18:27 +02001055 /* SSH path */
1056 address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters/local-address";
1057 port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters/local-port";
roman2eab4742023-06-06 10:00:26 +02001058 } else if (transport == NC_TI_OPENSSL) {
romand30af552023-06-16 15:18:27 +02001059 /* TLS path */
1060 address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tcp-server-parameters/local-address";
1061 port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tcp-server-parameters/local-port";
roman2eab4742023-06-06 10:00:26 +02001062 } else {
1063 ERR(NULL, "Transport not supported.");
1064 ret = 1;
1065 goto cleanup;
roman3f9b65c2023-06-05 14:26:58 +02001066 }
roman3f9b65c2023-06-05 14:26:58 +02001067
roman5cbb6532023-06-22 12:53:17 +02001068 ret = nc_config_new_create(ctx, config, address, address_fmt, endpt_name);
roman3f9b65c2023-06-05 14:26:58 +02001069 if (ret) {
1070 goto cleanup;
1071 }
1072
roman142718b2023-06-29 09:15:29 +02001073 sprintf(port_buf, "%d", port);
1074 ret = nc_config_new_create(ctx, config, port_buf, port_fmt, endpt_name);
roman3f9b65c2023-06-05 14:26:58 +02001075 if (ret) {
1076 goto cleanup;
1077 }
romand30af552023-06-16 15:18:27 +02001078
roman3f9b65c2023-06-05 14:26:58 +02001079cleanup:
romand30af552023-06-16 15:18:27 +02001080 return ret;
1081}
1082
roman5cbb6532023-06-22 12:53:17 +02001083API int
roman8ba6efa2023-07-12 15:27:52 +02001084nc_server_config_new_ch_address_port(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name,
roman5cbb6532023-06-22 12:53:17 +02001085 NC_TRANSPORT_IMPL transport, const char *address, const char *port, struct lyd_node **config)
1086{
1087 int ret = 0;
1088 const char *address_fmt, *port_fmt;
1089
1090 NC_CHECK_ARG_RET(NULL, address, port, ctx, endpt_name, config, 1);
1091
1092 if (transport == NC_TI_LIBSSH) {
1093 /* SSH path */
1094 address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-address";
1095 port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-port";
1096 } else if (transport == NC_TI_OPENSSL) {
1097 /* TLS path */
1098 address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-address";
1099 port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-port";
1100 } else {
1101 ERR(NULL, "Transport not supported.");
1102 ret = 1;
1103 goto cleanup;
1104 }
1105
roman8ba6efa2023-07-12 15:27:52 +02001106 ret = nc_config_new_create(ctx, config, address, address_fmt, client_name, endpt_name);
roman5cbb6532023-06-22 12:53:17 +02001107 if (ret) {
1108 goto cleanup;
1109 }
1110
roman8ba6efa2023-07-12 15:27:52 +02001111 ret = nc_config_new_create(ctx, config, port, port_fmt, client_name, endpt_name);
roman5cbb6532023-06-22 12:53:17 +02001112 if (ret) {
1113 goto cleanup;
1114 }
1115
1116cleanup:
1117 return ret;
1118}
1119
1120API int
roman8ba6efa2023-07-12 15:27:52 +02001121nc_server_config_new_del_endpt(const char *endpt_name, struct lyd_node **config)
roman5cbb6532023-06-22 12:53:17 +02001122{
roman8ba6efa2023-07-12 15:27:52 +02001123 NC_CHECK_ARG_RET(NULL, config, 1);
roman5cbb6532023-06-22 12:53:17 +02001124
roman8ba6efa2023-07-12 15:27:52 +02001125 if (endpt_name) {
1126 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']", endpt_name);
1127 } else {
1128 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint");
1129 }
1130}
1131
1132API int
1133nc_server_config_new_del_ch_client(const char *ch_client_name, struct lyd_node **config)
1134{
1135 NC_CHECK_ARG_RET(NULL, config, 1);
1136
1137 if (ch_client_name) {
1138 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']", ch_client_name);
1139 } else {
1140 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client");
1141 }
1142}
1143
1144API int
1145nc_server_config_new_ch_del_endpt(const char *client_name, const char *endpt_name, struct lyd_node **config)
1146{
1147 NC_CHECK_ARG_RET(NULL, client_name, config, 1);
1148
1149 if (endpt_name) {
1150 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
1151 "endpoints/endpoint[name='%s']", client_name, endpt_name);
1152 } else {
1153 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
1154 "endpoints/endpoint", client_name);
1155 }
roman5cbb6532023-06-22 12:53:17 +02001156}
1157
roman142718b2023-06-29 09:15:29 +02001158API int
roman13145912023-08-17 15:36:54 +02001159nc_server_config_new_keystore_asym_key(const struct ly_ctx *ctx, NC_TRANSPORT_IMPL ti, const char *asym_key_name,
1160 const char *privkey_path, const char *pubkey_path, struct lyd_node **config)
roman142718b2023-06-29 09:15:29 +02001161{
1162 int ret = 0;
1163 char *privkey = NULL, *pubkey = NULL;
1164 NC_PRIVKEY_FORMAT privkey_type;
roman142718b2023-06-29 09:15:29 +02001165 const char *privkey_format, *pubkey_format;
1166
roman12c3d522023-07-26 13:39:30 +02001167 NC_CHECK_ARG_RET(NULL, ctx, asym_key_name, privkey_path, config, 1);
roman142718b2023-06-29 09:15:29 +02001168
1169 /* get the keys as a string from the given files */
roman13145912023-08-17 15:36:54 +02001170 if (ti == NC_TI_LIBSSH) {
1171 ret = nc_server_config_new_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_SSH, &privkey, &privkey_type, &pubkey);
1172 } else if (ti == NC_TI_OPENSSL) {
1173 ret = nc_server_config_new_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_X509, &privkey, &privkey_type, &pubkey);
1174 } else {
1175 ERR(NULL, "Only SSH and TLS transports can be used to create an asymmetric key pair in the keystore.");
1176 ret = 1;
1177 goto cleanup;
1178 }
roman142718b2023-06-29 09:15:29 +02001179 if (ret) {
1180 ERR(NULL, "Getting keys from file(s) failed.");
1181 goto cleanup;
1182 }
1183
1184 /* get pubkey format str */
roman13145912023-08-17 15:36:54 +02001185 if (ti == NC_TI_LIBSSH) {
roman142718b2023-06-29 09:15:29 +02001186 pubkey_format = "ietf-crypto-types:ssh-public-key-format";
roman13145912023-08-17 15:36:54 +02001187 } else {
1188 pubkey_format = "ietf-crypto-types:subject-public-key-info-format";
roman142718b2023-06-29 09:15:29 +02001189 }
1190
1191 /* get privkey identityref value */
1192 privkey_format = nc_config_new_privkey_format_to_identityref(privkey_type);
1193 if (!privkey_format) {
1194 ret = 1;
1195 goto cleanup;
1196 }
1197
1198 ret = nc_config_new_create(ctx, config, pubkey_format, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001199 "asymmetric-key[name='%s']/public-key-format", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001200 if (ret) {
1201 goto cleanup;
1202 }
1203
1204 ret = nc_config_new_create(ctx, config, pubkey, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001205 "asymmetric-key[name='%s']/public-key", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001206 if (ret) {
1207 goto cleanup;
1208 }
1209
1210 ret = nc_config_new_create(ctx, config, privkey_format, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001211 "asymmetric-key[name='%s']/private-key-format", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001212 if (ret) {
1213 goto cleanup;
1214 }
1215
1216 ret = nc_config_new_create(ctx, config, privkey, "/ietf-keystore:keystore/asymmetric-keys/"
roman12c3d522023-07-26 13:39:30 +02001217 "asymmetric-key[name='%s']/cleartext-private-key", asym_key_name);
roman142718b2023-06-29 09:15:29 +02001218 if (ret) {
1219 goto cleanup;
1220 }
1221
1222cleanup:
1223 free(privkey);
1224 free(pubkey);
1225 return ret;
1226}
1227
1228API int
roman12c3d522023-07-26 13:39:30 +02001229nc_server_config_new_del_keystore_asym_key(const char *asym_key_name, struct lyd_node **config)
roman8ba6efa2023-07-12 15:27:52 +02001230{
1231 NC_CHECK_ARG_RET(NULL, config, 1);
1232
roman12c3d522023-07-26 13:39:30 +02001233 if (asym_key_name) {
1234 return nc_config_new_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']", asym_key_name);
roman8ba6efa2023-07-12 15:27:52 +02001235 } else {
1236 return nc_config_new_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key");
1237 }
1238}
1239
1240API int
roman12c3d522023-07-26 13:39:30 +02001241nc_server_config_new_keystore_cert(const struct ly_ctx *ctx, const char *asym_key_name, const char *cert_name,
1242 const char *cert_path, struct lyd_node **config)
1243{
1244 int ret = 0;
1245 char *cert = NULL;
1246
1247 NC_CHECK_ARG_RET(NULL, ctx, asym_key_name, cert_name, cert_path, config, 1);
1248
1249 /* get cert data */
1250 ret = nc_server_config_new_read_certificate(cert_path, &cert);
1251 if (ret) {
1252 goto cleanup;
1253 }
1254
1255 ret = nc_config_new_create(ctx, config, cert, "/ietf-keystore:keystore/asymmetric-keys/"
1256 "asymmetric-key[name='%s']/certificates/certificate[name='%s']/cert-data", asym_key_name, cert_name);
1257
1258cleanup:
1259 free(cert);
1260 return ret;
1261}
1262
1263API int
1264nc_server_config_new_del_keystore_cert(const char *asym_key_name, const char *cert_name, struct lyd_node **config)
1265{
1266 NC_CHECK_ARG_RET(NULL, asym_key_name, config, 1);
1267
1268 if (cert_name) {
1269 return nc_config_new_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']/"
1270 "certificates/certificate[name='%s']", asym_key_name, cert_name);
1271 } else {
1272 return nc_config_new_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']/"
1273 "certificates/certificate", asym_key_name);
1274 }
1275}
1276
1277API int
1278nc_server_config_new_truststore_pubkey(const struct ly_ctx *ctx, const char *pub_bag_name, const char *pubkey_name,
roman142718b2023-06-29 09:15:29 +02001279 const char *pubkey_path, struct lyd_node **config)
1280{
1281 int ret = 0;
1282 char *pubkey = NULL;
roman13145912023-08-17 15:36:54 +02001283 const char *pubkey_format = "ietf-crypto-types:ssh-public-key-format";
roman142718b2023-06-29 09:15:29 +02001284
roman12c3d522023-07-26 13:39:30 +02001285 NC_CHECK_ARG_RET(NULL, ctx, pub_bag_name, pubkey_name, pubkey_path, config, 1);
roman142718b2023-06-29 09:15:29 +02001286
roman13145912023-08-17 15:36:54 +02001287 ret = nc_server_config_new_get_ssh_pubkey_file(pubkey_path, &pubkey);
roman142718b2023-06-29 09:15:29 +02001288 if (ret) {
1289 goto cleanup;
1290 }
1291
roman13145912023-08-17 15:36:54 +02001292 ret = nc_config_new_create(ctx, config, pubkey_format, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001293 "public-key-bag[name='%s']/public-key[name='%s']/public-key-format", pub_bag_name, pubkey_name);
roman142718b2023-06-29 09:15:29 +02001294 if (ret) {
1295 goto cleanup;
1296 }
1297
1298 ret = nc_config_new_create(ctx, config, pubkey, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001299 "public-key-bag[name='%s']/public-key[name='%s']/public-key", pub_bag_name, pubkey_name);
roman142718b2023-06-29 09:15:29 +02001300 if (ret) {
1301 goto cleanup;
1302 }
1303
1304cleanup:
1305 free(pubkey);
1306 return ret;
1307}
1308
roman8ba6efa2023-07-12 15:27:52 +02001309API int
roman12c3d522023-07-26 13:39:30 +02001310nc_server_config_new_del_truststore_pubkey(const char *pub_bag_name,
roman8ba6efa2023-07-12 15:27:52 +02001311 const char *pubkey_name, struct lyd_node **config)
1312{
roman12c3d522023-07-26 13:39:30 +02001313 NC_CHECK_ARG_RET(NULL, pub_bag_name, config, 1);
roman8ba6efa2023-07-12 15:27:52 +02001314
1315 if (pubkey_name) {
1316 return nc_config_new_delete(config, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001317 "public-key-bag[name='%s']/public-key[name='%s']", pub_bag_name, pubkey_name);
roman8ba6efa2023-07-12 15:27:52 +02001318 } else {
1319 return nc_config_new_delete(config, "/ietf-truststore:truststore/public-key-bags/"
roman12c3d522023-07-26 13:39:30 +02001320 "public-key-bag[name='%s']/public-key", pub_bag_name);
1321 }
1322}
1323
1324API int
1325nc_server_config_new_truststore_cert(const struct ly_ctx *ctx, const char *cert_bag_name, const char *cert_name,
1326 const char *cert_path, struct lyd_node **config)
1327{
1328 int ret = 0;
1329 char *cert = NULL;
1330
1331 NC_CHECK_ARG_RET(NULL, ctx, cert_bag_name, cert_name, cert_path, config, 1);
1332
1333 ret = nc_server_config_new_read_certificate(cert_path, &cert);
1334 if (ret) {
1335 goto cleanup;
1336 }
1337
1338 ret = nc_config_new_create(ctx, config, cert, "/ietf-truststore:truststore/certificate-bags/"
1339 "certificate-bag[name='%s']/certificate[name='%s']/cert-data", cert_bag_name, cert_name);
1340 if (ret) {
1341 goto cleanup;
1342 }
1343
1344cleanup:
1345 free(cert);
1346 return ret;
1347}
1348
1349API int
1350nc_server_config_new_del_truststore_cert(const char *cert_bag_name,
1351 const char *cert_name, struct lyd_node **config)
1352{
1353 NC_CHECK_ARG_RET(NULL, cert_bag_name, config, 1);
1354
1355 if (cert_name) {
1356 return nc_config_new_delete(config, "/ietf-truststore:truststore/certificate-bags/"
1357 "certificate-bag[name='%s']/certificate[name='%s']", cert_bag_name, cert_name);
1358 } else {
1359 return nc_config_new_delete(config, "/ietf-truststore:truststore/certificate-bags/"
1360 "certificate-bag[name='%s']/certificate", cert_bag_name);
roman8ba6efa2023-07-12 15:27:52 +02001361 }
1362}
1363
roman2eab4742023-06-06 10:00:26 +02001364#endif /* NC_ENABLED_SSH_TLS */
romanb6f44032023-06-30 15:07:56 +02001365
romanb6f44032023-06-30 15:07:56 +02001366API int
romand0b78372023-09-14 10:06:03 +02001367nc_server_config_new_unix_socket(const struct ly_ctx *ctx, const char *endpt_name, const char *path,
1368 mode_t mode, uid_t uid, gid_t gid, struct lyd_node **config)
1369{
1370 int ret = 0;
1371 char *tree_path = NULL;
1372 char buf[12] = {0};
1373
1374 NC_CHECK_ARG_RET(NULL, ctx, endpt_name, path, config, 1);
1375
1376 if (asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/libnetconf2-netconf-server:unix-socket", endpt_name) == -1) {
1377 ERRMEM;
1378 tree_path = NULL;
1379 ret = 1;
1380 goto cleanup;
1381 }
1382
1383 /* path to unix socket */
1384 ret = nc_config_new_create_append(ctx, tree_path, "path", path, config);
1385 if (ret) {
1386 goto cleanup;
1387 }
1388
1389 /* mode */
1390 if (mode != (mode_t)-1) {
1391 if (mode > 0777) {
1392 ERR(NULL, "Invalid mode value (%o).", mode);
1393 ret = 1;
1394 goto cleanup;
1395 }
1396
1397 sprintf(buf, "%o", mode);
1398 ret = nc_config_new_create_append(ctx, tree_path, "mode", buf, config);
1399 if (ret) {
1400 goto cleanup;
1401 }
1402 }
1403
1404 /* uid */
1405 if (uid != (uid_t)-1) {
1406 memset(buf, 0, 12);
1407 sprintf(buf, "%u", uid);
1408 ret = nc_config_new_create_append(ctx, tree_path, "uid", buf, config);
1409 if (ret) {
1410 goto cleanup;
1411 }
1412 }
1413
1414 /* gid */
1415 if (gid != (gid_t)-1) {
1416 memset(buf, 0, 12);
1417 sprintf(buf, "%u", gid);
1418 ret = nc_config_new_create_append(ctx, tree_path, "gid", buf, config);
1419 if (ret) {
1420 goto cleanup;
1421 }
1422 }
1423
1424cleanup:
1425 free(tree_path);
1426 return ret;
1427}
1428
1429API int
romanb6f44032023-06-30 15:07:56 +02001430nc_server_config_new_ch_persistent(const struct ly_ctx *ctx, const char *ch_client_name, struct lyd_node **config)
1431{
1432 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
1433
1434 /* delete periodic tree if exists */
1435 if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1436 "netconf-client[name='%s']/connection-type/periodic", ch_client_name)) {
1437 return 1;
1438 }
1439
1440 return nc_config_new_create(ctx, config, NULL, "/ietf-netconf-server:netconf-server/call-home/"
1441 "netconf-client[name='%s']/connection-type/persistent", ch_client_name);
1442}
1443
1444API int
1445nc_server_config_new_ch_period(const struct ly_ctx *ctx, const char *ch_client_name, uint16_t period,
1446 struct lyd_node **config)
1447{
1448 char buf[6] = {0};
1449
1450 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, period, 1);
1451
1452 /* delete persistent tree if exists */
1453 if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1454 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1455 return 1;
1456 }
1457
1458 sprintf(buf, "%u", period);
1459 return nc_config_new_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
1460 "netconf-client[name='%s']/connection-type/periodic/period", ch_client_name);
1461}
1462
1463API int
roman8ba6efa2023-07-12 15:27:52 +02001464nc_server_config_new_ch_del_period(const char *ch_client_name, struct lyd_node **config)
1465{
1466 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1467
1468 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1469 "netconf-client[name='%s']/connection-type/periodic/period", ch_client_name);
1470}
1471
1472API int
romanb6f44032023-06-30 15:07:56 +02001473nc_server_config_new_ch_anchor_time(const struct ly_ctx *ctx, const char *ch_client_name,
1474 const char *anchor_time, struct lyd_node **config)
1475{
1476 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, anchor_time, 1);
1477
1478 /* delete persistent tree if exists */
1479 if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1480 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1481 return 1;
1482 }
1483
1484 return nc_config_new_create(ctx, config, anchor_time, "/ietf-netconf-server:netconf-server/call-home/"
1485 "netconf-client[name='%s']/connection-type/periodic/anchor-time", ch_client_name);
1486}
1487
1488API int
roman8ba6efa2023-07-12 15:27:52 +02001489nc_server_config_new_ch_del_anchor_time(const char *ch_client_name, struct lyd_node **config)
1490{
1491 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1492
1493 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1494 "netconf-client[name='%s']/connection-type/periodic/anchor-time", ch_client_name);
1495}
1496
1497API int
romanb6f44032023-06-30 15:07:56 +02001498nc_server_config_new_ch_idle_timeout(const struct ly_ctx *ctx, const char *ch_client_name,
1499 uint16_t idle_timeout, struct lyd_node **config)
1500{
1501 char buf[6] = {0};
1502
1503 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, 1);
1504
1505 /* delete persistent tree if exists */
1506 if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1507 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1508 return 1;
1509 }
1510
1511 sprintf(buf, "%u", idle_timeout);
1512 return nc_config_new_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
1513 "netconf-client[name='%s']/connection-type/periodic/idle-timeout", ch_client_name);
1514}
1515
1516API int
roman8ba6efa2023-07-12 15:27:52 +02001517nc_server_config_new_ch_del_idle_timeout(const char *ch_client_name, struct lyd_node **config)
1518{
1519 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1520
1521 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1522 "netconf-client[name='%s']/connection-type/periodic/idle-timeout", ch_client_name);
1523}
1524
1525API int
romanb6f44032023-06-30 15:07:56 +02001526nc_server_config_new_ch_reconnect_strategy(const struct ly_ctx *ctx, const char *ch_client_name,
roman8ba6efa2023-07-12 15:27:52 +02001527 NC_CH_START_WITH start_with, uint16_t max_wait, uint8_t max_attempts, struct lyd_node **config)
romanb6f44032023-06-30 15:07:56 +02001528{
1529 int ret = 0;
1530 char *path = NULL;
1531 char buf[6] = {0};
1532 const char *start_with_val;
1533
1534 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
1535
1536 /* prepared the path */
1537 if (asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/"
1538 "netconf-client[name='%s']/reconnect-strategy", ch_client_name) == -1) {
1539 ERRMEM;
1540 path = NULL;
1541 ret = 1;
1542 goto cleanup;
1543 }
1544
1545 if (start_with) {
1546 /* get string value from enum */
1547 if (start_with == NC_CH_FIRST_LISTED) {
1548 start_with_val = "first-listed";
1549 } else if (start_with == NC_CH_LAST_CONNECTED) {
1550 start_with_val = "last-connected";
1551 } else {
1552 start_with_val = "random-selection";
1553 }
1554
1555 ret = nc_config_new_create_append(ctx, path, "start-with", start_with_val, config);
1556 if (ret) {
1557 goto cleanup;
1558 }
1559 }
1560
1561 if (max_attempts) {
1562 sprintf(buf, "%u", max_attempts);
1563 ret = nc_config_new_create_append(ctx, path, "max-attempts", buf, config);
1564 if (ret) {
1565 goto cleanup;
1566 }
1567 memset(buf, 0, 6);
1568 }
1569
1570 if (max_wait) {
1571 sprintf(buf, "%u", max_wait);
1572 ret = nc_config_new_create_append(ctx, path, "max-wait", buf, config);
1573 if (ret) {
1574 goto cleanup;
1575 }
1576 }
1577
1578cleanup:
1579 free(path);
1580 return ret;
1581}
roman8ba6efa2023-07-12 15:27:52 +02001582
1583API int
1584nc_server_config_new_ch_del_reconnect_strategy(const char *ch_client_name, struct lyd_node **config)
1585{
1586 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1587
1588 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1589 "netconf-client[name='%s']/reconnect-strategy", ch_client_name);
1590}