blob: 8cea603c944c3b3988758f96f1e578e85d6f833a [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 */
99 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
100 if (ret) {
101 goto cleanup;
102 }
103
104 /* set the node to the top level node */
105 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
106 if (ret) {
107 goto cleanup;
108 }
109
110 /* add all default nodes */
111 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
112 if (ret) {
113 goto cleanup;
114 }
115
116cleanup:
117 free(path);
118 va_end(ap);
119 return ret;
120}
121
122int
123nc_config_new_create_append(const struct ly_ctx *ctx, const char *parent_path, const char *child_name,
124 const char *value, struct lyd_node **tree)
125{
126 int ret = 0;
127 char *path = NULL;
128
129 /* create the path by appending child to the parent path */
130 ret = asprintf(&path, "%s/%s", parent_path, child_name);
131 if (ret == -1) {
132 ERRMEM;
133 path = NULL;
134 goto cleanup;
135 }
136
137 /* create the nodes in the path */
138 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
139 if (ret) {
140 goto cleanup;
141 }
142
143 /* set the node to the top level node */
144 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
145 if (ret) {
146 goto cleanup;
147 }
148
149 /* add all default nodes */
150 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
151 if (ret) {
152 goto cleanup;
153 }
154
155cleanup:
156 free(path);
157 return ret;
158}
159
160int
161nc_config_new_check_delete(struct lyd_node **tree, const char *path_fmt, ...)
162{
163 int ret = 0;
164 va_list ap;
165 char *path = NULL;
166 struct lyd_node *sub = NULL;
167
168 va_start(ap, path_fmt);
169
170 /* create the path from the format */
171 ret = vasprintf(&path, path_fmt, ap);
172 if (ret == -1) {
173 ERRMEM;
174 path = NULL;
175 goto cleanup;
176 }
177
178 /* find the node we want to delete */
179 ret = lyd_find_path(*tree, path, 0, &sub);
180 if ((ret == LY_EINCOMPLETE) || (ret == LY_ENOTFOUND)) {
181 ret = 0;
182 goto cleanup;
183 } else if (ret) {
184 ERR(NULL, "Unable to delete node in the path \"%s\".", path);
185 goto cleanup;
186 }
187
188 lyd_free_tree(sub);
189
190 /* set the node to top level container */
191 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
192 if (ret) {
193 goto cleanup;
194 }
195
196cleanup:
197 free(path);
198 va_end(ap);
199 return ret;
200}
201
roman2eab4742023-06-06 10:00:26 +0200202#ifdef NC_ENABLED_SSH_TLS
203
roman3f9b65c2023-06-05 14:26:58 +0200204const char *
205nc_config_new_privkey_format_to_identityref(NC_PRIVKEY_FORMAT format)
206{
207 switch (format) {
208 case NC_PRIVKEY_FORMAT_RSA:
209 return "ietf-crypto-types:rsa-private-key-format";
210 case NC_PRIVKEY_FORMAT_EC:
211 return "ietf-crypto-types:ec-private-key-format";
212 case NC_PRIVKEY_FORMAT_X509:
213 return "libnetconf2-netconf-server:subject-private-key-info-format";
214 case NC_PRIVKEY_FORMAT_OPENSSH:
215 return "libnetconf2-netconf-server:openssh-private-key-format";
216 default:
217 ERR(NULL, "Private key type not supported.");
218 return NULL;
219 }
220}
221
222int
223nc_server_config_new_read_certificate(const char *cert_path, char **cert)
224{
225 int ret = 0, cert_len;
226 X509 *x509 = NULL;
227 FILE *f = NULL;
228 BIO *bio = NULL;
229 char *c = NULL;
230
231 *cert = NULL;
232
233 f = fopen(cert_path, "r");
234 if (!f) {
235 ERR(NULL, "Unable to open certificate file \"%s\".", cert_path);
236 ret = 1;
237 goto cleanup;
238 }
239
240 /* load the cert into memory */
241 x509 = PEM_read_X509(f, NULL, NULL, NULL);
242 if (!x509) {
243 ret = -1;
244 goto cleanup;
245 }
246
247 bio = BIO_new(BIO_s_mem());
248 if (!bio) {
249 ret = -1;
250 goto cleanup;
251 }
252
253 ret = PEM_write_bio_X509(bio, x509);
254 if (!ret) {
255 ret = -1;
256 goto cleanup;
257 }
258
259 cert_len = BIO_pending(bio);
260 if (cert_len <= 0) {
261 ret = -1;
262 goto cleanup;
263 }
264
265 c = malloc(cert_len + 1);
266 if (!c) {
267 ERRMEM;
268 ret = 1;
269 goto cleanup;
270 }
271
272 /* read the cert from bio */
273 ret = BIO_read(bio, c, cert_len);
274 if (ret <= 0) {
275 ret = -1;
276 goto cleanup;
277 }
278 c[cert_len] = '\0';
279
280 /* strip the cert of the header and footer */
281 *cert = strdup(c + strlen(NC_PEM_CERTIFICATE_HEADER));
282 if (!*cert) {
283 ERRMEM;
284 ret = 1;
285 goto cleanup;
286 }
287
288 (*cert)[strlen(*cert) - strlen(NC_PEM_CERTIFICATE_FOOTER)] = '\0';
289
290 ret = 0;
291
292cleanup:
293 if (ret == -1) {
294 ERR(NULL, "Error getting certificate from file \"%s\" (OpenSSL Error): \"%s\".", cert_path, ERR_reason_error_string(ERR_get_error()));
295 ret = 1;
296 }
297 if (f) {
298 fclose(f);
299 }
300
301 BIO_free(bio);
302 X509_free(x509);
303 free(c);
304 return ret;
305}
306
307static int
308nc_server_config_new_read_ssh2_pubkey(FILE *f, char **pubkey)
309{
310 char *buffer = NULL;
311 size_t size = 0, pubkey_len = 0;
312 void *tmp;
313 ssize_t read;
314 int ret = 0;
315
316 while ((read = getline(&buffer, &size, f)) > 0) {
317 if (!strncmp(buffer, "----", 4)) {
318 continue;
319 }
320
321 if (!strncmp(buffer, "Comment:", 8)) {
322 continue;
323 }
324
325 if (buffer[read - 1] == '\n') {
326 read--;
327 }
328
329 tmp = realloc(*pubkey, pubkey_len + read + 1);
330 if (!tmp) {
331 ERRMEM;
332 ret = 1;
333 goto cleanup;
334 }
335
336 *pubkey = tmp;
337 memcpy(*pubkey + pubkey_len, buffer, read);
338 pubkey_len += read;
339 }
340
341 if (!pubkey_len) {
342 ERR(NULL, "Unexpected public key format.");
343 ret = 1;
344 goto cleanup;
345 }
346
347 (*pubkey)[pubkey_len] = '\0';
348
349cleanup:
350 free(buffer);
351 return ret;
352}
353
354static int
355nc_server_config_new_read_pubkey_openssl(FILE *f, char **pubkey)
356{
357 int ret = 0;
358 EVP_PKEY *pkey = NULL;
359 BIO *bio = NULL;
360 char *key = NULL;
361 int pub_len;
362
363 /* read the pubkey from file */
364 pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
365 if (!pkey) {
366 ret = -1;
367 goto cleanup;
368 }
369
370 bio = BIO_new(BIO_s_mem());
371 if (!bio) {
372 ret = -1;
373 goto cleanup;
374 }
375
376 /* write the pubkey into bio */
377 ret = PEM_write_bio_PUBKEY(bio, pkey);
378 if (!ret) {
379 ret = -1;
380 goto cleanup;
381 }
382
383 pub_len = BIO_pending(bio);
384 if (pub_len <= 0) {
385 ret = -1;
386 goto cleanup;
387 }
388
389 /* get pubkey's length */
390 key = malloc(pub_len + 1);
391 if (!key) {
392 ERRMEM;
393 ret = 1;
394 goto cleanup;
395 }
396
397 /* read the public key from bio */
398 ret = BIO_read(bio, key, pub_len);
399 if (ret <= 0) {
400 ret = -1;
401 goto cleanup;
402 }
403 key[pub_len] = '\0';
404
405 /* strip the pubkey of the header and footer */
406 *pubkey = strdup(key + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER));
407 if (!*pubkey) {
408 ERRMEM;
409 ret = 1;
410 goto cleanup;
411 }
412
413 (*pubkey)[strlen(*pubkey) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER)] = '\0';
414
415 ret = 0;
416cleanup:
417 if (ret == -1) {
418 ERR(NULL, "Error getting public key from file (OpenSSL Error): \"%s\".", ERR_reason_error_string(ERR_get_error()));
419 ret = 1;
420 }
421
422 BIO_free(bio);
423 EVP_PKEY_free(pkey);
424 free(key);
425
426 return ret;
427}
428
roman3f9b65c2023-06-05 14:26:58 +0200429static int
430nc_server_config_new_read_pubkey_libssh(const char *pubkey_path, char **pubkey)
431{
432 int ret = 0;
433 ssh_key pub_sshkey = NULL;
434
435 ret = ssh_pki_import_pubkey_file(pubkey_path, &pub_sshkey);
436 if (ret) {
437 ERR(NULL, "Importing public key from file \"%s\" failed.", pubkey_path);
438 return ret;
439 }
440
441 ret = ssh_pki_export_pubkey_base64(pub_sshkey, pubkey);
442 if (ret) {
443 ERR(NULL, "Exporting public key to base64 failed.");
444 }
445
446 ssh_key_free(pub_sshkey);
447 return ret;
448}
449
roman3f9b65c2023-06-05 14:26:58 +0200450int
451nc_server_config_new_get_pubkey(const char *pubkey_path, char **pubkey, NC_PUBKEY_FORMAT *pubkey_type)
452{
453 int ret = 0;
454 FILE *f = NULL;
455 char *header = NULL;
456 size_t len = 0;
457
458 NC_CHECK_ARG_RET(NULL, pubkey, pubkey_type, 1);
459
460 *pubkey = NULL;
461
462 f = fopen(pubkey_path, "r");
463 if (!f) {
464 ERR(NULL, "Unable to open file \"%s\".", pubkey_path);
465 ret = 1;
466 goto cleanup;
467 }
468
469 if (getline(&header, &len, f) < 0) {
470 ERR(NULL, "Error reading header from file \"%s\".", pubkey_path);
471 ret = 1;
472 goto cleanup;
473 }
474 rewind(f);
475
476 if (!strncmp(header, NC_SUBJECT_PUBKEY_INFO_HEADER, strlen(NC_SUBJECT_PUBKEY_INFO_HEADER))) {
477 /* it's subject public key info public key */
478 ret = nc_server_config_new_read_pubkey_openssl(f, pubkey);
479 *pubkey_type = NC_PUBKEY_FORMAT_X509;
480 } else if (!strncmp(header, NC_SSH2_PUBKEY_HEADER, strlen(NC_SSH2_PUBKEY_HEADER))) {
481 /* it's ssh2 public key */
482 ret = nc_server_config_new_read_ssh2_pubkey(f, pubkey);
483 *pubkey_type = NC_PUBKEY_FORMAT_SSH2;
roman7fdc84d2023-06-06 13:14:53 +0200484 } else {
roman3f9b65c2023-06-05 14:26:58 +0200485 /* it's probably OpenSSH public key */
486 ret = nc_server_config_new_read_pubkey_libssh(pubkey_path, pubkey);
487 *pubkey_type = NC_PUBKEY_FORMAT_SSH2;
488 }
roman3f9b65c2023-06-05 14:26:58 +0200489
490 if (ret) {
491 ERR(NULL, "Error getting public key from file \"%s\".", pubkey_path);
492 goto cleanup;
493 }
494
495cleanup:
496 if (f) {
497 fclose(f);
498 }
499
500 free(header);
501
502 return ret;
503}
504
505static int
506nc_server_config_new_get_privkey_openssl(FILE *f, char **privkey, EVP_PKEY **priv_pkey)
507{
508 int ret = 0, priv_len;
509 BIO *bio = NULL;
510
511 NC_CHECK_ARG_RET(NULL, privkey, priv_pkey, 1);
512
513 /* read private key from file */
514 *priv_pkey = PEM_read_PrivateKey(f, NULL, NULL, NULL);
515 if (!*priv_pkey) {
516 ret = -1;
517 goto cleanup;
518 }
519
520 bio = BIO_new(BIO_s_mem());
521 if (!bio) {
522 ret = -1;
523 goto cleanup;
524 }
525
526 /* write the private key in to bio */
527 ret = PEM_write_bio_PrivateKey(bio, *priv_pkey, NULL, NULL, 0, NULL, NULL);
528 if (!ret) {
529 ret = -1;
530 goto cleanup;
531 }
532
533 priv_len = BIO_pending(bio);
534 if (priv_len <= 0) {
535 ret = -1;
536 goto cleanup;
537 }
538
539 /* get private key's length */
540 *privkey = malloc(priv_len + 1);
541 if (!*privkey) {
542 ERRMEM;
543 ret = 1;
544 goto cleanup;
545 }
546
547 /* read the private key from bio */
548 ret = BIO_read(bio, *privkey, priv_len);
549 if (ret <= 0) {
550 ret = -1;
551 goto cleanup;
552 }
553 (*privkey)[priv_len] = '\0';
554
555 ret = 0;
556cleanup:
557 if (ret < 0) {
558 ERR(NULL, "Getting private key from file failed (%s).", ERR_reason_error_string(ERR_get_error()));
559 }
560 BIO_free(bio);
561 return ret;
562}
563
564static int
565nc_server_config_new_privkey_to_pubkey_openssl(EVP_PKEY *priv_pkey, char **pubkey)
566{
567 int ret = 0, pub_len;
568 BIO *bio = NULL;
569
570 bio = BIO_new(BIO_s_mem());
571 if (!bio) {
572 ret = -1;
573 goto cleanup;
574 }
575
576 /* write the pubkey into bio */
577 ret = PEM_write_bio_PUBKEY(bio, priv_pkey);
578 if (!ret) {
579 ret = -1;
580 goto cleanup;
581 }
582
583 /* get the length of the pubkey */
584 pub_len = BIO_pending(bio);
585 if (pub_len <= 0) {
586 ret = -1;
587 goto cleanup;
588 }
589
590 *pubkey = malloc(pub_len + 1);
591 if (!*pubkey) {
592 ERRMEM;
593 ret = 1;
594 goto cleanup;
595 }
596
597 /* read the pubkey from the bio */
598 ret = BIO_read(bio, *pubkey, pub_len);
599 if (ret <= 0) {
600 ret = -1;
601 goto cleanup;
602 }
603 (*pubkey)[pub_len] = '\0';
604
605 ret = 0;
606
607cleanup:
608 if (ret < 0) {
609 ERR(NULL, "Converting private to public key failed (%s).", ERR_reason_error_string(ERR_get_error()));
610 }
611 BIO_free(bio);
612 return ret;
613}
614
615static int
616nc_server_config_new_privkey_to_pubkey_libssh(const ssh_key priv_sshkey, char **pubkey)
617{
618 int ret;
619 ssh_key pub_sshkey = NULL;
620
621 ret = ssh_pki_export_privkey_to_pubkey(priv_sshkey, &pub_sshkey);
622 if (ret) {
623 ERR(NULL, "Exporting privkey to pubkey failed.");
624 return ret;
625 }
626
627 ret = ssh_pki_export_pubkey_base64(pub_sshkey, pubkey);
628 if (ret) {
629 ERR(NULL, "Exporting pubkey to base64 failed.");
630 }
631
632 ssh_key_free(pub_sshkey);
633 return ret;
634}
635
636static int
637nc_server_config_new_privkey_to_pubkey(EVP_PKEY *priv_pkey, const ssh_key priv_sshkey, NC_PRIVKEY_FORMAT privkey_type, char **pubkey, NC_PUBKEY_FORMAT *pubkey_type)
638{
639 switch (privkey_type) {
roman3f9b65c2023-06-05 14:26:58 +0200640 case NC_PRIVKEY_FORMAT_RSA:
641 case NC_PRIVKEY_FORMAT_EC:
642 case NC_PRIVKEY_FORMAT_OPENSSH:
643 *pubkey_type = NC_PUBKEY_FORMAT_SSH2;
644 return nc_server_config_new_privkey_to_pubkey_libssh(priv_sshkey, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200645 case NC_PRIVKEY_FORMAT_X509:
646 *pubkey_type = NC_PUBKEY_FORMAT_X509;
647 return nc_server_config_new_privkey_to_pubkey_openssl(priv_pkey, pubkey);
648 default:
649 break;
650 }
651
652 return 1;
653}
654
roman3f9b65c2023-06-05 14:26:58 +0200655static int
656nc_server_config_new_get_privkey_libssh(const char *privkey_path, char **privkey, ssh_key *priv_sshkey)
657{
658 int ret;
659
660 *priv_sshkey = NULL;
661
662 ret = ssh_pki_import_privkey_file(privkey_path, NULL, NULL, NULL, priv_sshkey);
663 if (ret) {
664 ERR(NULL, "Importing privkey from file \"%s\" failed.", privkey_path);
665 return ret;
666 }
667
668 ret = ssh_pki_export_privkey_base64(*priv_sshkey, NULL, NULL, NULL, privkey);
669 if (ret) {
670 ERR(NULL, "Exporting privkey from file \"%s\" to base64 failed.", privkey_path);
671 }
672
673 return ret;
674}
675
roman3f9b65c2023-06-05 14:26:58 +0200676int
677nc_server_config_new_get_keys(const char *privkey_path, const char *pubkey_path,
678 char **privkey, char **pubkey, NC_PRIVKEY_FORMAT *privkey_type, NC_PUBKEY_FORMAT *pubkey_type)
679{
680 int ret = 0;
681 EVP_PKEY *priv_pkey = NULL;
682 ssh_key priv_sshkey = NULL;
683 FILE *f_privkey = NULL;
684 char *header = NULL;
685 size_t len = 0;
romand30af552023-06-16 15:18:27 +0200686 char *priv = NULL, *pub = NULL;
roman3f9b65c2023-06-05 14:26:58 +0200687
688 NC_CHECK_ARG_RET(NULL, privkey_path, privkey, pubkey, privkey_type, 1);
689
690 *privkey = NULL;
691 *pubkey = NULL;
692
693 /* get private key first */
694 f_privkey = fopen(privkey_path, "r");
695 if (!f_privkey) {
696 ERR(NULL, "Unable to open file \"%s\".", privkey_path);
697 ret = 1;
698 goto cleanup;
699 }
700
701 if (getline(&header, &len, f_privkey) < 0) {
702 ERR(NULL, "Error reading header from file \"%s\".", privkey_path);
703 ret = 1;
704 goto cleanup;
705 }
706 rewind(f_privkey);
707
708 if (!strncmp(header, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) {
709 /* it's PKCS8 (X.509) private key */
710 *privkey_type = NC_PRIVKEY_FORMAT_X509;
romand30af552023-06-16 15:18:27 +0200711 ret = nc_server_config_new_get_privkey_openssl(f_privkey, &priv, &priv_pkey);
roman2eab4742023-06-06 10:00:26 +0200712 } else if (!strncmp(header, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) {
roman3f9b65c2023-06-05 14:26:58 +0200713 /* it's OpenSSH private key */
714 *privkey_type = NC_PRIVKEY_FORMAT_OPENSSH;
romand30af552023-06-16 15:18:27 +0200715 ret = nc_server_config_new_get_privkey_libssh(privkey_path, &priv, &priv_sshkey);
roman3f9b65c2023-06-05 14:26:58 +0200716 } else if (!strncmp(header, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) {
717 /* it's RSA privkey in PKCS1 format */
718 *privkey_type = NC_PRIVKEY_FORMAT_RSA;
romand30af552023-06-16 15:18:27 +0200719 ret = nc_server_config_new_get_privkey_libssh(privkey_path, &priv, &priv_sshkey);
roman3f9b65c2023-06-05 14:26:58 +0200720 } else if (!strncmp(header, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) {
721 /* it's EC privkey in SEC1 format */
722 *privkey_type = NC_PRIVKEY_FORMAT_EC;
romand30af552023-06-16 15:18:27 +0200723 ret = nc_server_config_new_get_privkey_libssh(privkey_path, &priv, &priv_sshkey);
roman2eab4742023-06-06 10:00:26 +0200724 } else {
roman3f9b65c2023-06-05 14:26:58 +0200725 ERR(NULL, "Private key format not supported.");
726 ret = 1;
727 goto cleanup;
728 }
roman3f9b65c2023-06-05 14:26:58 +0200729 if (ret) {
730 goto cleanup;
731 }
732
733 if (pubkey_path) {
romand30af552023-06-16 15:18:27 +0200734 ret = nc_server_config_new_get_pubkey(pubkey_path, &pub, pubkey_type);
roman3f9b65c2023-06-05 14:26:58 +0200735 } else {
romand30af552023-06-16 15:18:27 +0200736 ret = nc_server_config_new_privkey_to_pubkey(priv_pkey, priv_sshkey, *privkey_type, &pub, pubkey_type);
roman3f9b65c2023-06-05 14:26:58 +0200737 }
roman3f9b65c2023-06-05 14:26:58 +0200738 if (ret) {
739 ERR(NULL, "Getting public key failed.");
740 goto cleanup;
741 }
742
romand30af552023-06-16 15:18:27 +0200743 /* strip pubkey's header and footer only if it's generated from pkcs8 key (using OpenSSL),
744 * otherwise it's already stripped
745 */
746 if (!pubkey_path && (*privkey_type == NC_PRIVKEY_FORMAT_X509)) {
747 *pubkey = strdup(pub + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER));
748 if (!*pubkey) {
749 ERRMEM;
750 ret = 1;
751 goto cleanup;
752 }
753 (*pubkey)[strlen(*pubkey) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER)] = '\0';
754 } else {
755 *pubkey = strdup(pub);
756 if (!*pubkey) {
757 ERRMEM;
758 ret = 1;
759 goto cleanup;
760 }
761 }
762
763 /* strip private key's header and footer */
764 if (*privkey_type == NC_PRIVKEY_FORMAT_OPENSSH) {
765 /* only OpenSSH private keys have different header and footer after processing */
766 *privkey = strdup(priv + strlen(NC_OPENSSH_PRIVKEY_HEADER));
767 if (!*privkey) {
768 ERRMEM;
769 ret = 1;
770 goto cleanup;
771 }
772 (*privkey)[strlen(*privkey) - strlen(NC_OPENSSH_PRIVKEY_FOOTER)] = '\0';
773 } else {
774 /* the rest share the same header and footer */
775 *privkey = strdup(priv + strlen(NC_PKCS8_PRIVKEY_HEADER));
776 if (!*privkey) {
777 ERRMEM;
778 ret = 1;
779 goto cleanup;
780 }
781 (*privkey)[strlen(*privkey) - strlen(NC_PKCS8_PRIVKEY_FOOTER)] = '\0';
782 }
783
roman3f9b65c2023-06-05 14:26:58 +0200784cleanup:
785 if (f_privkey) {
786 fclose(f_privkey);
787 }
788
789 free(header);
romand30af552023-06-16 15:18:27 +0200790 free(pub);
791 free(priv);
roman3f9b65c2023-06-05 14:26:58 +0200792
793 ssh_key_free(priv_sshkey);
794 EVP_PKEY_free(priv_pkey);
795
796 return ret;
797}
798
799API int
800nc_server_config_new_address_port(const struct ly_ctx *ctx, const char *endpt_name, NC_TRANSPORT_IMPL transport,
roman142718b2023-06-29 09:15:29 +0200801 const char *address, uint16_t port, struct lyd_node **config)
roman3f9b65c2023-06-05 14:26:58 +0200802{
803 int ret = 0;
romand30af552023-06-16 15:18:27 +0200804 const char *address_fmt, *port_fmt;
roman142718b2023-06-29 09:15:29 +0200805 char port_buf[6] = {0};
roman3f9b65c2023-06-05 14:26:58 +0200806
roman142718b2023-06-29 09:15:29 +0200807 NC_CHECK_ARG_RET(NULL, address, ctx, endpt_name, config, 1);
roman3f9b65c2023-06-05 14:26:58 +0200808
roman3f9b65c2023-06-05 14:26:58 +0200809 if (transport == NC_TI_LIBSSH) {
romand30af552023-06-16 15:18:27 +0200810 /* SSH path */
811 address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters/local-address";
812 port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters/local-port";
roman2eab4742023-06-06 10:00:26 +0200813 } else if (transport == NC_TI_OPENSSL) {
romand30af552023-06-16 15:18:27 +0200814 /* TLS path */
815 address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tcp-server-parameters/local-address";
816 port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tcp-server-parameters/local-port";
roman2eab4742023-06-06 10:00:26 +0200817 } else {
818 ERR(NULL, "Transport not supported.");
819 ret = 1;
820 goto cleanup;
roman3f9b65c2023-06-05 14:26:58 +0200821 }
roman3f9b65c2023-06-05 14:26:58 +0200822
roman5cbb6532023-06-22 12:53:17 +0200823 ret = nc_config_new_create(ctx, config, address, address_fmt, endpt_name);
roman3f9b65c2023-06-05 14:26:58 +0200824 if (ret) {
825 goto cleanup;
826 }
827
roman142718b2023-06-29 09:15:29 +0200828 sprintf(port_buf, "%d", port);
829 ret = nc_config_new_create(ctx, config, port_buf, port_fmt, endpt_name);
roman3f9b65c2023-06-05 14:26:58 +0200830 if (ret) {
831 goto cleanup;
832 }
romand30af552023-06-16 15:18:27 +0200833
roman3f9b65c2023-06-05 14:26:58 +0200834cleanup:
romand30af552023-06-16 15:18:27 +0200835 return ret;
836}
837
roman5cbb6532023-06-22 12:53:17 +0200838API int
roman8ba6efa2023-07-12 15:27:52 +0200839nc_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 +0200840 NC_TRANSPORT_IMPL transport, const char *address, const char *port, struct lyd_node **config)
841{
842 int ret = 0;
843 const char *address_fmt, *port_fmt;
844
845 NC_CHECK_ARG_RET(NULL, address, port, ctx, endpt_name, config, 1);
846
847 if (transport == NC_TI_LIBSSH) {
848 /* SSH path */
849 address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-address";
850 port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-port";
851 } else if (transport == NC_TI_OPENSSL) {
852 /* TLS path */
853 address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-address";
854 port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-port";
855 } else {
856 ERR(NULL, "Transport not supported.");
857 ret = 1;
858 goto cleanup;
859 }
860
roman8ba6efa2023-07-12 15:27:52 +0200861 ret = nc_config_new_create(ctx, config, address, address_fmt, client_name, endpt_name);
roman5cbb6532023-06-22 12:53:17 +0200862 if (ret) {
863 goto cleanup;
864 }
865
roman8ba6efa2023-07-12 15:27:52 +0200866 ret = nc_config_new_create(ctx, config, port, port_fmt, client_name, endpt_name);
roman5cbb6532023-06-22 12:53:17 +0200867 if (ret) {
868 goto cleanup;
869 }
870
871cleanup:
872 return ret;
873}
874
875API int
roman8ba6efa2023-07-12 15:27:52 +0200876nc_server_config_new_del_endpt(const char *endpt_name, struct lyd_node **config)
roman5cbb6532023-06-22 12:53:17 +0200877{
roman8ba6efa2023-07-12 15:27:52 +0200878 NC_CHECK_ARG_RET(NULL, config, 1);
roman5cbb6532023-06-22 12:53:17 +0200879
roman8ba6efa2023-07-12 15:27:52 +0200880 if (endpt_name) {
881 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']", endpt_name);
882 } else {
883 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint");
884 }
885}
886
887API int
888nc_server_config_new_del_ch_client(const char *ch_client_name, struct lyd_node **config)
889{
890 NC_CHECK_ARG_RET(NULL, config, 1);
891
892 if (ch_client_name) {
893 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']", ch_client_name);
894 } else {
895 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client");
896 }
897}
898
899API int
900nc_server_config_new_ch_del_endpt(const char *client_name, const char *endpt_name, struct lyd_node **config)
901{
902 NC_CHECK_ARG_RET(NULL, client_name, config, 1);
903
904 if (endpt_name) {
905 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
906 "endpoints/endpoint[name='%s']", client_name, endpt_name);
907 } else {
908 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
909 "endpoints/endpoint", client_name);
910 }
roman5cbb6532023-06-22 12:53:17 +0200911}
912
roman142718b2023-06-29 09:15:29 +0200913API int
914nc_server_config_new_keystore_asym_key(const struct ly_ctx *ctx, const char *name, const char *privkey_path,
915 const char *pubkey_path, struct lyd_node **config)
916{
917 int ret = 0;
918 char *privkey = NULL, *pubkey = NULL;
919 NC_PRIVKEY_FORMAT privkey_type;
920 NC_PUBKEY_FORMAT pubkey_type;
921 const char *privkey_format, *pubkey_format;
922
923 NC_CHECK_ARG_RET(NULL, ctx, name, privkey_path, config, 1);
924
925 /* get the keys as a string from the given files */
926 ret = nc_server_config_new_get_keys(privkey_path, pubkey_path, &privkey, &pubkey, &privkey_type, &pubkey_type);
927 if (ret) {
928 ERR(NULL, "Getting keys from file(s) failed.");
929 goto cleanup;
930 }
931
932 /* get pubkey format str */
933 if (pubkey_type == NC_PUBKEY_FORMAT_X509) {
934 pubkey_format = "ietf-crypto-types:public-key-info-format";
935 } else {
936 pubkey_format = "ietf-crypto-types:ssh-public-key-format";
937 }
938
939 /* get privkey identityref value */
940 privkey_format = nc_config_new_privkey_format_to_identityref(privkey_type);
941 if (!privkey_format) {
942 ret = 1;
943 goto cleanup;
944 }
945
946 ret = nc_config_new_create(ctx, config, pubkey_format, "/ietf-keystore:keystore/asymmetric-keys/"
roman4cb8bb12023-06-29 09:16:46 +0200947 "asymmetric-key[name='%s']/public-key-format", name);
roman142718b2023-06-29 09:15:29 +0200948 if (ret) {
949 goto cleanup;
950 }
951
952 ret = nc_config_new_create(ctx, config, pubkey, "/ietf-keystore:keystore/asymmetric-keys/"
roman4cb8bb12023-06-29 09:16:46 +0200953 "asymmetric-key[name='%s']/public-key", name);
roman142718b2023-06-29 09:15:29 +0200954 if (ret) {
955 goto cleanup;
956 }
957
958 ret = nc_config_new_create(ctx, config, privkey_format, "/ietf-keystore:keystore/asymmetric-keys/"
roman4cb8bb12023-06-29 09:16:46 +0200959 "asymmetric-key[name='%s']/private-key-format", name);
roman142718b2023-06-29 09:15:29 +0200960 if (ret) {
961 goto cleanup;
962 }
963
964 ret = nc_config_new_create(ctx, config, privkey, "/ietf-keystore:keystore/asymmetric-keys/"
roman4cb8bb12023-06-29 09:16:46 +0200965 "asymmetric-key[name='%s']/cleartext-private-key", name);
roman142718b2023-06-29 09:15:29 +0200966 if (ret) {
967 goto cleanup;
968 }
969
970cleanup:
971 free(privkey);
972 free(pubkey);
973 return ret;
974}
975
976API int
roman8ba6efa2023-07-12 15:27:52 +0200977nc_server_config_new_del_keystore_asym_key(const char *name, struct lyd_node **config)
978{
979 NC_CHECK_ARG_RET(NULL, config, 1);
980
981 if (name) {
982 return nc_config_new_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']", name);
983 } else {
984 return nc_config_new_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key");
985 }
986}
987
988API int
roman142718b2023-06-29 09:15:29 +0200989nc_server_config_new_truststore_pubkey(const struct ly_ctx *ctx, const char *bag_name, const char *pubkey_name,
990 const char *pubkey_path, struct lyd_node **config)
991{
992 int ret = 0;
993 char *pubkey = NULL;
994 NC_PUBKEY_FORMAT pubkey_format;
995 const char *format;
996
997 NC_CHECK_ARG_RET(NULL, ctx, bag_name, pubkey_name, pubkey_path, config, 1);
998
999 ret = nc_server_config_new_get_pubkey(pubkey_path, &pubkey, &pubkey_format);
1000 if (ret) {
1001 goto cleanup;
1002 }
1003
1004 /* pubkey format to str */
1005 if (pubkey_format == NC_PUBKEY_FORMAT_SSH2) {
1006 format = "ietf-crypto-types:ssh-public-key-format";
1007 } else {
1008 format = "ietf-crypto-types:subject-public-key-info-format";
1009 }
1010
1011 ret = nc_config_new_create(ctx, config, format, "/ietf-truststore:truststore/public-key-bags/"
roman4cb8bb12023-06-29 09:16:46 +02001012 "public-key-bag[name='%s']/public-key[name='%s']/public-key-format", bag_name, pubkey_name);
roman142718b2023-06-29 09:15:29 +02001013 if (ret) {
1014 goto cleanup;
1015 }
1016
1017 ret = nc_config_new_create(ctx, config, pubkey, "/ietf-truststore:truststore/public-key-bags/"
roman4cb8bb12023-06-29 09:16:46 +02001018 "public-key-bag[name='%s']/public-key[name='%s']/public-key", bag_name, pubkey_name);
roman142718b2023-06-29 09:15:29 +02001019 if (ret) {
1020 goto cleanup;
1021 }
1022
1023cleanup:
1024 free(pubkey);
1025 return ret;
1026}
1027
roman8ba6efa2023-07-12 15:27:52 +02001028API int
1029nc_server_config_new_del_truststore_pubkey(const char *bag_name,
1030 const char *pubkey_name, struct lyd_node **config)
1031{
1032 NC_CHECK_ARG_RET(NULL, bag_name, config, 1);
1033
1034 if (pubkey_name) {
1035 return nc_config_new_delete(config, "/ietf-truststore:truststore/public-key-bags/"
1036 "public-key-bag[name='%s']/public-key[name='%s']", bag_name, pubkey_name);
1037 } else {
1038 return nc_config_new_delete(config, "/ietf-truststore:truststore/public-key-bags/"
1039 "public-key-bag[name='%s']/public-key", bag_name);
1040 }
1041}
1042
roman2eab4742023-06-06 10:00:26 +02001043#endif /* NC_ENABLED_SSH_TLS */
romanb6f44032023-06-30 15:07:56 +02001044
romanb6f44032023-06-30 15:07:56 +02001045API int
1046nc_server_config_new_ch_persistent(const struct ly_ctx *ctx, const char *ch_client_name, struct lyd_node **config)
1047{
1048 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
1049
1050 /* delete periodic tree if exists */
1051 if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1052 "netconf-client[name='%s']/connection-type/periodic", ch_client_name)) {
1053 return 1;
1054 }
1055
1056 return nc_config_new_create(ctx, config, NULL, "/ietf-netconf-server:netconf-server/call-home/"
1057 "netconf-client[name='%s']/connection-type/persistent", ch_client_name);
1058}
1059
1060API int
1061nc_server_config_new_ch_period(const struct ly_ctx *ctx, const char *ch_client_name, uint16_t period,
1062 struct lyd_node **config)
1063{
1064 char buf[6] = {0};
1065
1066 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, period, 1);
1067
1068 /* delete persistent tree if exists */
1069 if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1070 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1071 return 1;
1072 }
1073
1074 sprintf(buf, "%u", period);
1075 return nc_config_new_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
1076 "netconf-client[name='%s']/connection-type/periodic/period", ch_client_name);
1077}
1078
1079API int
roman8ba6efa2023-07-12 15:27:52 +02001080nc_server_config_new_ch_del_period(const char *ch_client_name, struct lyd_node **config)
1081{
1082 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1083
1084 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1085 "netconf-client[name='%s']/connection-type/periodic/period", ch_client_name);
1086}
1087
1088API int
romanb6f44032023-06-30 15:07:56 +02001089nc_server_config_new_ch_anchor_time(const struct ly_ctx *ctx, const char *ch_client_name,
1090 const char *anchor_time, struct lyd_node **config)
1091{
1092 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, anchor_time, 1);
1093
1094 /* delete persistent tree if exists */
1095 if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1096 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1097 return 1;
1098 }
1099
1100 return nc_config_new_create(ctx, config, anchor_time, "/ietf-netconf-server:netconf-server/call-home/"
1101 "netconf-client[name='%s']/connection-type/periodic/anchor-time", ch_client_name);
1102}
1103
1104API int
roman8ba6efa2023-07-12 15:27:52 +02001105nc_server_config_new_ch_del_anchor_time(const char *ch_client_name, struct lyd_node **config)
1106{
1107 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1108
1109 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1110 "netconf-client[name='%s']/connection-type/periodic/anchor-time", ch_client_name);
1111}
1112
1113API int
romanb6f44032023-06-30 15:07:56 +02001114nc_server_config_new_ch_idle_timeout(const struct ly_ctx *ctx, const char *ch_client_name,
1115 uint16_t idle_timeout, struct lyd_node **config)
1116{
1117 char buf[6] = {0};
1118
1119 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, 1);
1120
1121 /* delete persistent tree if exists */
1122 if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1123 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1124 return 1;
1125 }
1126
1127 sprintf(buf, "%u", idle_timeout);
1128 return nc_config_new_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
1129 "netconf-client[name='%s']/connection-type/periodic/idle-timeout", ch_client_name);
1130}
1131
1132API int
roman8ba6efa2023-07-12 15:27:52 +02001133nc_server_config_new_ch_del_idle_timeout(const char *ch_client_name, struct lyd_node **config)
1134{
1135 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1136
1137 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1138 "netconf-client[name='%s']/connection-type/periodic/idle-timeout", ch_client_name);
1139}
1140
1141API int
romanb6f44032023-06-30 15:07:56 +02001142nc_server_config_new_ch_reconnect_strategy(const struct ly_ctx *ctx, const char *ch_client_name,
roman8ba6efa2023-07-12 15:27:52 +02001143 NC_CH_START_WITH start_with, uint16_t max_wait, uint8_t max_attempts, struct lyd_node **config)
romanb6f44032023-06-30 15:07:56 +02001144{
1145 int ret = 0;
1146 char *path = NULL;
1147 char buf[6] = {0};
1148 const char *start_with_val;
1149
1150 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
1151
1152 /* prepared the path */
1153 if (asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/"
1154 "netconf-client[name='%s']/reconnect-strategy", ch_client_name) == -1) {
1155 ERRMEM;
1156 path = NULL;
1157 ret = 1;
1158 goto cleanup;
1159 }
1160
1161 if (start_with) {
1162 /* get string value from enum */
1163 if (start_with == NC_CH_FIRST_LISTED) {
1164 start_with_val = "first-listed";
1165 } else if (start_with == NC_CH_LAST_CONNECTED) {
1166 start_with_val = "last-connected";
1167 } else {
1168 start_with_val = "random-selection";
1169 }
1170
1171 ret = nc_config_new_create_append(ctx, path, "start-with", start_with_val, config);
1172 if (ret) {
1173 goto cleanup;
1174 }
1175 }
1176
1177 if (max_attempts) {
1178 sprintf(buf, "%u", max_attempts);
1179 ret = nc_config_new_create_append(ctx, path, "max-attempts", buf, config);
1180 if (ret) {
1181 goto cleanup;
1182 }
1183 memset(buf, 0, 6);
1184 }
1185
1186 if (max_wait) {
1187 sprintf(buf, "%u", max_wait);
1188 ret = nc_config_new_create_append(ctx, path, "max-wait", buf, config);
1189 if (ret) {
1190 goto cleanup;
1191 }
1192 }
1193
1194cleanup:
1195 free(path);
1196 return ret;
1197}
roman8ba6efa2023-07-12 15:27:52 +02001198
1199API int
1200nc_server_config_new_ch_del_reconnect_strategy(const char *ch_client_name, struct lyd_node **config)
1201{
1202 NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1);
1203
1204 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1205 "netconf-client[name='%s']/reconnect-strategy", ch_client_name);
1206}