blob: 45a1e129a1aed38d85fa5dad09e1d158a065fba6 [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
roman2eab4742023-06-06 10:00:26 +020037#ifdef NC_ENABLED_SSH_TLS
38
roman3f9b65c2023-06-05 14:26:58 +020039const char *
40nc_config_new_privkey_format_to_identityref(NC_PRIVKEY_FORMAT format)
41{
42 switch (format) {
43 case NC_PRIVKEY_FORMAT_RSA:
44 return "ietf-crypto-types:rsa-private-key-format";
45 case NC_PRIVKEY_FORMAT_EC:
46 return "ietf-crypto-types:ec-private-key-format";
47 case NC_PRIVKEY_FORMAT_X509:
48 return "libnetconf2-netconf-server:subject-private-key-info-format";
49 case NC_PRIVKEY_FORMAT_OPENSSH:
50 return "libnetconf2-netconf-server:openssh-private-key-format";
51 default:
52 ERR(NULL, "Private key type not supported.");
53 return NULL;
54 }
55}
56
57int
58nc_server_config_new_read_certificate(const char *cert_path, char **cert)
59{
60 int ret = 0, cert_len;
61 X509 *x509 = NULL;
62 FILE *f = NULL;
63 BIO *bio = NULL;
64 char *c = NULL;
65
66 *cert = NULL;
67
68 f = fopen(cert_path, "r");
69 if (!f) {
70 ERR(NULL, "Unable to open certificate file \"%s\".", cert_path);
71 ret = 1;
72 goto cleanup;
73 }
74
75 /* load the cert into memory */
76 x509 = PEM_read_X509(f, NULL, NULL, NULL);
77 if (!x509) {
78 ret = -1;
79 goto cleanup;
80 }
81
82 bio = BIO_new(BIO_s_mem());
83 if (!bio) {
84 ret = -1;
85 goto cleanup;
86 }
87
88 ret = PEM_write_bio_X509(bio, x509);
89 if (!ret) {
90 ret = -1;
91 goto cleanup;
92 }
93
94 cert_len = BIO_pending(bio);
95 if (cert_len <= 0) {
96 ret = -1;
97 goto cleanup;
98 }
99
100 c = malloc(cert_len + 1);
101 if (!c) {
102 ERRMEM;
103 ret = 1;
104 goto cleanup;
105 }
106
107 /* read the cert from bio */
108 ret = BIO_read(bio, c, cert_len);
109 if (ret <= 0) {
110 ret = -1;
111 goto cleanup;
112 }
113 c[cert_len] = '\0';
114
115 /* strip the cert of the header and footer */
116 *cert = strdup(c + strlen(NC_PEM_CERTIFICATE_HEADER));
117 if (!*cert) {
118 ERRMEM;
119 ret = 1;
120 goto cleanup;
121 }
122
123 (*cert)[strlen(*cert) - strlen(NC_PEM_CERTIFICATE_FOOTER)] = '\0';
124
125 ret = 0;
126
127cleanup:
128 if (ret == -1) {
129 ERR(NULL, "Error getting certificate from file \"%s\" (OpenSSL Error): \"%s\".", cert_path, ERR_reason_error_string(ERR_get_error()));
130 ret = 1;
131 }
132 if (f) {
133 fclose(f);
134 }
135
136 BIO_free(bio);
137 X509_free(x509);
138 free(c);
139 return ret;
140}
141
142static int
143nc_server_config_new_read_ssh2_pubkey(FILE *f, char **pubkey)
144{
145 char *buffer = NULL;
146 size_t size = 0, pubkey_len = 0;
147 void *tmp;
148 ssize_t read;
149 int ret = 0;
150
151 while ((read = getline(&buffer, &size, f)) > 0) {
152 if (!strncmp(buffer, "----", 4)) {
153 continue;
154 }
155
156 if (!strncmp(buffer, "Comment:", 8)) {
157 continue;
158 }
159
160 if (buffer[read - 1] == '\n') {
161 read--;
162 }
163
164 tmp = realloc(*pubkey, pubkey_len + read + 1);
165 if (!tmp) {
166 ERRMEM;
167 ret = 1;
168 goto cleanup;
169 }
170
171 *pubkey = tmp;
172 memcpy(*pubkey + pubkey_len, buffer, read);
173 pubkey_len += read;
174 }
175
176 if (!pubkey_len) {
177 ERR(NULL, "Unexpected public key format.");
178 ret = 1;
179 goto cleanup;
180 }
181
182 (*pubkey)[pubkey_len] = '\0';
183
184cleanup:
185 free(buffer);
186 return ret;
187}
188
189static int
190nc_server_config_new_read_pubkey_openssl(FILE *f, char **pubkey)
191{
192 int ret = 0;
193 EVP_PKEY *pkey = NULL;
194 BIO *bio = NULL;
195 char *key = NULL;
196 int pub_len;
197
198 /* read the pubkey from file */
199 pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
200 if (!pkey) {
201 ret = -1;
202 goto cleanup;
203 }
204
205 bio = BIO_new(BIO_s_mem());
206 if (!bio) {
207 ret = -1;
208 goto cleanup;
209 }
210
211 /* write the pubkey into bio */
212 ret = PEM_write_bio_PUBKEY(bio, pkey);
213 if (!ret) {
214 ret = -1;
215 goto cleanup;
216 }
217
218 pub_len = BIO_pending(bio);
219 if (pub_len <= 0) {
220 ret = -1;
221 goto cleanup;
222 }
223
224 /* get pubkey's length */
225 key = malloc(pub_len + 1);
226 if (!key) {
227 ERRMEM;
228 ret = 1;
229 goto cleanup;
230 }
231
232 /* read the public key from bio */
233 ret = BIO_read(bio, key, pub_len);
234 if (ret <= 0) {
235 ret = -1;
236 goto cleanup;
237 }
238 key[pub_len] = '\0';
239
240 /* strip the pubkey of the header and footer */
241 *pubkey = strdup(key + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER));
242 if (!*pubkey) {
243 ERRMEM;
244 ret = 1;
245 goto cleanup;
246 }
247
248 (*pubkey)[strlen(*pubkey) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER)] = '\0';
249
250 ret = 0;
251cleanup:
252 if (ret == -1) {
253 ERR(NULL, "Error getting public key from file (OpenSSL Error): \"%s\".", ERR_reason_error_string(ERR_get_error()));
254 ret = 1;
255 }
256
257 BIO_free(bio);
258 EVP_PKEY_free(pkey);
259 free(key);
260
261 return ret;
262}
263
roman3f9b65c2023-06-05 14:26:58 +0200264static int
265nc_server_config_new_read_pubkey_libssh(const char *pubkey_path, char **pubkey)
266{
267 int ret = 0;
268 ssh_key pub_sshkey = NULL;
269
270 ret = ssh_pki_import_pubkey_file(pubkey_path, &pub_sshkey);
271 if (ret) {
272 ERR(NULL, "Importing public key from file \"%s\" failed.", pubkey_path);
273 return ret;
274 }
275
276 ret = ssh_pki_export_pubkey_base64(pub_sshkey, pubkey);
277 if (ret) {
278 ERR(NULL, "Exporting public key to base64 failed.");
279 }
280
281 ssh_key_free(pub_sshkey);
282 return ret;
283}
284
roman3f9b65c2023-06-05 14:26:58 +0200285int
286nc_server_config_new_get_pubkey(const char *pubkey_path, char **pubkey, NC_PUBKEY_FORMAT *pubkey_type)
287{
288 int ret = 0;
289 FILE *f = NULL;
290 char *header = NULL;
291 size_t len = 0;
292
293 NC_CHECK_ARG_RET(NULL, pubkey, pubkey_type, 1);
294
295 *pubkey = NULL;
296
297 f = fopen(pubkey_path, "r");
298 if (!f) {
299 ERR(NULL, "Unable to open file \"%s\".", pubkey_path);
300 ret = 1;
301 goto cleanup;
302 }
303
304 if (getline(&header, &len, f) < 0) {
305 ERR(NULL, "Error reading header from file \"%s\".", pubkey_path);
306 ret = 1;
307 goto cleanup;
308 }
309 rewind(f);
310
311 if (!strncmp(header, NC_SUBJECT_PUBKEY_INFO_HEADER, strlen(NC_SUBJECT_PUBKEY_INFO_HEADER))) {
312 /* it's subject public key info public key */
313 ret = nc_server_config_new_read_pubkey_openssl(f, pubkey);
314 *pubkey_type = NC_PUBKEY_FORMAT_X509;
315 } else if (!strncmp(header, NC_SSH2_PUBKEY_HEADER, strlen(NC_SSH2_PUBKEY_HEADER))) {
316 /* it's ssh2 public key */
317 ret = nc_server_config_new_read_ssh2_pubkey(f, pubkey);
318 *pubkey_type = NC_PUBKEY_FORMAT_SSH2;
roman7fdc84d2023-06-06 13:14:53 +0200319 } else {
roman3f9b65c2023-06-05 14:26:58 +0200320 /* it's probably OpenSSH public key */
321 ret = nc_server_config_new_read_pubkey_libssh(pubkey_path, pubkey);
322 *pubkey_type = NC_PUBKEY_FORMAT_SSH2;
323 }
roman3f9b65c2023-06-05 14:26:58 +0200324
325 if (ret) {
326 ERR(NULL, "Error getting public key from file \"%s\".", pubkey_path);
327 goto cleanup;
328 }
329
330cleanup:
331 if (f) {
332 fclose(f);
333 }
334
335 free(header);
336
337 return ret;
338}
339
340static int
341nc_server_config_new_get_privkey_openssl(FILE *f, char **privkey, EVP_PKEY **priv_pkey)
342{
343 int ret = 0, priv_len;
344 BIO *bio = NULL;
345
346 NC_CHECK_ARG_RET(NULL, privkey, priv_pkey, 1);
347
348 /* read private key from file */
349 *priv_pkey = PEM_read_PrivateKey(f, NULL, NULL, NULL);
350 if (!*priv_pkey) {
351 ret = -1;
352 goto cleanup;
353 }
354
355 bio = BIO_new(BIO_s_mem());
356 if (!bio) {
357 ret = -1;
358 goto cleanup;
359 }
360
361 /* write the private key in to bio */
362 ret = PEM_write_bio_PrivateKey(bio, *priv_pkey, NULL, NULL, 0, NULL, NULL);
363 if (!ret) {
364 ret = -1;
365 goto cleanup;
366 }
367
368 priv_len = BIO_pending(bio);
369 if (priv_len <= 0) {
370 ret = -1;
371 goto cleanup;
372 }
373
374 /* get private key's length */
375 *privkey = malloc(priv_len + 1);
376 if (!*privkey) {
377 ERRMEM;
378 ret = 1;
379 goto cleanup;
380 }
381
382 /* read the private key from bio */
383 ret = BIO_read(bio, *privkey, priv_len);
384 if (ret <= 0) {
385 ret = -1;
386 goto cleanup;
387 }
388 (*privkey)[priv_len] = '\0';
389
390 ret = 0;
391cleanup:
392 if (ret < 0) {
393 ERR(NULL, "Getting private key from file failed (%s).", ERR_reason_error_string(ERR_get_error()));
394 }
395 BIO_free(bio);
396 return ret;
397}
398
399static int
400nc_server_config_new_privkey_to_pubkey_openssl(EVP_PKEY *priv_pkey, char **pubkey)
401{
402 int ret = 0, pub_len;
403 BIO *bio = NULL;
404
405 bio = BIO_new(BIO_s_mem());
406 if (!bio) {
407 ret = -1;
408 goto cleanup;
409 }
410
411 /* write the pubkey into bio */
412 ret = PEM_write_bio_PUBKEY(bio, priv_pkey);
413 if (!ret) {
414 ret = -1;
415 goto cleanup;
416 }
417
418 /* get the length of the pubkey */
419 pub_len = BIO_pending(bio);
420 if (pub_len <= 0) {
421 ret = -1;
422 goto cleanup;
423 }
424
425 *pubkey = malloc(pub_len + 1);
426 if (!*pubkey) {
427 ERRMEM;
428 ret = 1;
429 goto cleanup;
430 }
431
432 /* read the pubkey from the bio */
433 ret = BIO_read(bio, *pubkey, pub_len);
434 if (ret <= 0) {
435 ret = -1;
436 goto cleanup;
437 }
438 (*pubkey)[pub_len] = '\0';
439
440 ret = 0;
441
442cleanup:
443 if (ret < 0) {
444 ERR(NULL, "Converting private to public key failed (%s).", ERR_reason_error_string(ERR_get_error()));
445 }
446 BIO_free(bio);
447 return ret;
448}
449
450static int
451nc_server_config_new_privkey_to_pubkey_libssh(const ssh_key priv_sshkey, char **pubkey)
452{
453 int ret;
454 ssh_key pub_sshkey = NULL;
455
456 ret = ssh_pki_export_privkey_to_pubkey(priv_sshkey, &pub_sshkey);
457 if (ret) {
458 ERR(NULL, "Exporting privkey to pubkey failed.");
459 return ret;
460 }
461
462 ret = ssh_pki_export_pubkey_base64(pub_sshkey, pubkey);
463 if (ret) {
464 ERR(NULL, "Exporting pubkey to base64 failed.");
465 }
466
467 ssh_key_free(pub_sshkey);
468 return ret;
469}
470
471static int
472nc_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)
473{
474 switch (privkey_type) {
roman3f9b65c2023-06-05 14:26:58 +0200475 case NC_PRIVKEY_FORMAT_RSA:
476 case NC_PRIVKEY_FORMAT_EC:
477 case NC_PRIVKEY_FORMAT_OPENSSH:
478 *pubkey_type = NC_PUBKEY_FORMAT_SSH2;
479 return nc_server_config_new_privkey_to_pubkey_libssh(priv_sshkey, pubkey);
roman3f9b65c2023-06-05 14:26:58 +0200480 case NC_PRIVKEY_FORMAT_X509:
481 *pubkey_type = NC_PUBKEY_FORMAT_X509;
482 return nc_server_config_new_privkey_to_pubkey_openssl(priv_pkey, pubkey);
483 default:
484 break;
485 }
486
487 return 1;
488}
489
roman3f9b65c2023-06-05 14:26:58 +0200490static int
491nc_server_config_new_get_privkey_libssh(const char *privkey_path, char **privkey, ssh_key *priv_sshkey)
492{
493 int ret;
494
495 *priv_sshkey = NULL;
496
497 ret = ssh_pki_import_privkey_file(privkey_path, NULL, NULL, NULL, priv_sshkey);
498 if (ret) {
499 ERR(NULL, "Importing privkey from file \"%s\" failed.", privkey_path);
500 return ret;
501 }
502
503 ret = ssh_pki_export_privkey_base64(*priv_sshkey, NULL, NULL, NULL, privkey);
504 if (ret) {
505 ERR(NULL, "Exporting privkey from file \"%s\" to base64 failed.", privkey_path);
506 }
507
508 return ret;
509}
510
roman3f9b65c2023-06-05 14:26:58 +0200511int
512nc_server_config_new_get_keys(const char *privkey_path, const char *pubkey_path,
513 char **privkey, char **pubkey, NC_PRIVKEY_FORMAT *privkey_type, NC_PUBKEY_FORMAT *pubkey_type)
514{
515 int ret = 0;
516 EVP_PKEY *priv_pkey = NULL;
517 ssh_key priv_sshkey = NULL;
518 FILE *f_privkey = NULL;
519 char *header = NULL;
520 size_t len = 0;
romand30af552023-06-16 15:18:27 +0200521 char *priv = NULL, *pub = NULL;
roman3f9b65c2023-06-05 14:26:58 +0200522
523 NC_CHECK_ARG_RET(NULL, privkey_path, privkey, pubkey, privkey_type, 1);
524
525 *privkey = NULL;
526 *pubkey = NULL;
527
528 /* get private key first */
529 f_privkey = fopen(privkey_path, "r");
530 if (!f_privkey) {
531 ERR(NULL, "Unable to open file \"%s\".", privkey_path);
532 ret = 1;
533 goto cleanup;
534 }
535
536 if (getline(&header, &len, f_privkey) < 0) {
537 ERR(NULL, "Error reading header from file \"%s\".", privkey_path);
538 ret = 1;
539 goto cleanup;
540 }
541 rewind(f_privkey);
542
543 if (!strncmp(header, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) {
544 /* it's PKCS8 (X.509) private key */
545 *privkey_type = NC_PRIVKEY_FORMAT_X509;
romand30af552023-06-16 15:18:27 +0200546 ret = nc_server_config_new_get_privkey_openssl(f_privkey, &priv, &priv_pkey);
roman2eab4742023-06-06 10:00:26 +0200547 } else if (!strncmp(header, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) {
roman3f9b65c2023-06-05 14:26:58 +0200548 /* it's OpenSSH private key */
549 *privkey_type = NC_PRIVKEY_FORMAT_OPENSSH;
romand30af552023-06-16 15:18:27 +0200550 ret = nc_server_config_new_get_privkey_libssh(privkey_path, &priv, &priv_sshkey);
roman3f9b65c2023-06-05 14:26:58 +0200551 } else if (!strncmp(header, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) {
552 /* it's RSA privkey in PKCS1 format */
553 *privkey_type = NC_PRIVKEY_FORMAT_RSA;
romand30af552023-06-16 15:18:27 +0200554 ret = nc_server_config_new_get_privkey_libssh(privkey_path, &priv, &priv_sshkey);
roman3f9b65c2023-06-05 14:26:58 +0200555 } else if (!strncmp(header, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) {
556 /* it's EC privkey in SEC1 format */
557 *privkey_type = NC_PRIVKEY_FORMAT_EC;
romand30af552023-06-16 15:18:27 +0200558 ret = nc_server_config_new_get_privkey_libssh(privkey_path, &priv, &priv_sshkey);
roman2eab4742023-06-06 10:00:26 +0200559 } else {
roman3f9b65c2023-06-05 14:26:58 +0200560 ERR(NULL, "Private key format not supported.");
561 ret = 1;
562 goto cleanup;
563 }
roman3f9b65c2023-06-05 14:26:58 +0200564 if (ret) {
565 goto cleanup;
566 }
567
568 if (pubkey_path) {
romand30af552023-06-16 15:18:27 +0200569 ret = nc_server_config_new_get_pubkey(pubkey_path, &pub, pubkey_type);
roman3f9b65c2023-06-05 14:26:58 +0200570 } else {
romand30af552023-06-16 15:18:27 +0200571 ret = nc_server_config_new_privkey_to_pubkey(priv_pkey, priv_sshkey, *privkey_type, &pub, pubkey_type);
roman3f9b65c2023-06-05 14:26:58 +0200572 }
roman3f9b65c2023-06-05 14:26:58 +0200573 if (ret) {
574 ERR(NULL, "Getting public key failed.");
575 goto cleanup;
576 }
577
romand30af552023-06-16 15:18:27 +0200578 /* strip pubkey's header and footer only if it's generated from pkcs8 key (using OpenSSL),
579 * otherwise it's already stripped
580 */
581 if (!pubkey_path && (*privkey_type == NC_PRIVKEY_FORMAT_X509)) {
582 *pubkey = strdup(pub + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER));
583 if (!*pubkey) {
584 ERRMEM;
585 ret = 1;
586 goto cleanup;
587 }
588 (*pubkey)[strlen(*pubkey) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER)] = '\0';
589 } else {
590 *pubkey = strdup(pub);
591 if (!*pubkey) {
592 ERRMEM;
593 ret = 1;
594 goto cleanup;
595 }
596 }
597
598 /* strip private key's header and footer */
599 if (*privkey_type == NC_PRIVKEY_FORMAT_OPENSSH) {
600 /* only OpenSSH private keys have different header and footer after processing */
601 *privkey = strdup(priv + strlen(NC_OPENSSH_PRIVKEY_HEADER));
602 if (!*privkey) {
603 ERRMEM;
604 ret = 1;
605 goto cleanup;
606 }
607 (*privkey)[strlen(*privkey) - strlen(NC_OPENSSH_PRIVKEY_FOOTER)] = '\0';
608 } else {
609 /* the rest share the same header and footer */
610 *privkey = strdup(priv + strlen(NC_PKCS8_PRIVKEY_HEADER));
611 if (!*privkey) {
612 ERRMEM;
613 ret = 1;
614 goto cleanup;
615 }
616 (*privkey)[strlen(*privkey) - strlen(NC_PKCS8_PRIVKEY_FOOTER)] = '\0';
617 }
618
roman3f9b65c2023-06-05 14:26:58 +0200619cleanup:
620 if (f_privkey) {
621 fclose(f_privkey);
622 }
623
624 free(header);
romand30af552023-06-16 15:18:27 +0200625 free(pub);
626 free(priv);
roman3f9b65c2023-06-05 14:26:58 +0200627
628 ssh_key_free(priv_sshkey);
629 EVP_PKEY_free(priv_pkey);
630
631 return ret;
632}
633
634API int
635nc_server_config_new_address_port(const struct ly_ctx *ctx, const char *endpt_name, NC_TRANSPORT_IMPL transport,
roman142718b2023-06-29 09:15:29 +0200636 const char *address, uint16_t port, struct lyd_node **config)
roman3f9b65c2023-06-05 14:26:58 +0200637{
638 int ret = 0;
romand30af552023-06-16 15:18:27 +0200639 const char *address_fmt, *port_fmt;
roman142718b2023-06-29 09:15:29 +0200640 char port_buf[6] = {0};
roman3f9b65c2023-06-05 14:26:58 +0200641
roman142718b2023-06-29 09:15:29 +0200642 NC_CHECK_ARG_RET(NULL, address, ctx, endpt_name, config, 1);
roman3f9b65c2023-06-05 14:26:58 +0200643
roman3f9b65c2023-06-05 14:26:58 +0200644 if (transport == NC_TI_LIBSSH) {
romand30af552023-06-16 15:18:27 +0200645 /* SSH path */
646 address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters/local-address";
647 port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters/local-port";
roman2eab4742023-06-06 10:00:26 +0200648 } else if (transport == NC_TI_OPENSSL) {
romand30af552023-06-16 15:18:27 +0200649 /* TLS path */
650 address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tcp-server-parameters/local-address";
651 port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tcp-server-parameters/local-port";
roman2eab4742023-06-06 10:00:26 +0200652 } else {
653 ERR(NULL, "Transport not supported.");
654 ret = 1;
655 goto cleanup;
roman3f9b65c2023-06-05 14:26:58 +0200656 }
roman3f9b65c2023-06-05 14:26:58 +0200657
roman5cbb6532023-06-22 12:53:17 +0200658 ret = nc_config_new_create(ctx, config, address, address_fmt, endpt_name);
roman3f9b65c2023-06-05 14:26:58 +0200659 if (ret) {
660 goto cleanup;
661 }
662
roman142718b2023-06-29 09:15:29 +0200663 sprintf(port_buf, "%d", port);
664 ret = nc_config_new_create(ctx, config, port_buf, port_fmt, endpt_name);
roman3f9b65c2023-06-05 14:26:58 +0200665 if (ret) {
666 goto cleanup;
667 }
romand30af552023-06-16 15:18:27 +0200668
roman3f9b65c2023-06-05 14:26:58 +0200669cleanup:
romand30af552023-06-16 15:18:27 +0200670 return ret;
671}
672
roman5cbb6532023-06-22 12:53:17 +0200673API int
674nc_server_config_new_ch_address_port(const struct ly_ctx *ctx, const char *ch_client_name, const char *endpt_name,
675 NC_TRANSPORT_IMPL transport, const char *address, const char *port, struct lyd_node **config)
676{
677 int ret = 0;
678 const char *address_fmt, *port_fmt;
679
680 NC_CHECK_ARG_RET(NULL, address, port, ctx, endpt_name, config, 1);
681
682 if (transport == NC_TI_LIBSSH) {
683 /* SSH path */
684 address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-address";
685 port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-port";
686 } else if (transport == NC_TI_OPENSSL) {
687 /* TLS path */
688 address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-address";
689 port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-port";
690 } else {
691 ERR(NULL, "Transport not supported.");
692 ret = 1;
693 goto cleanup;
694 }
695
696 ret = nc_config_new_create(ctx, config, address, address_fmt, ch_client_name, endpt_name);
697 if (ret) {
698 goto cleanup;
699 }
700
701 ret = nc_config_new_create(ctx, config, port, port_fmt, ch_client_name, endpt_name);
702 if (ret) {
703 goto cleanup;
704 }
705
706cleanup:
707 return ret;
708}
709
710API int
711nc_server_config_new_del_ch_client(const struct ly_ctx *ctx, const char *ch_client_name, struct lyd_node **config)
712{
713 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
714
roman142718b2023-06-29 09:15:29 +0200715 return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']", ch_client_name);
roman5cbb6532023-06-22 12:53:17 +0200716}
717
roman142718b2023-06-29 09:15:29 +0200718API int
719nc_server_config_new_keystore_asym_key(const struct ly_ctx *ctx, const char *name, const char *privkey_path,
720 const char *pubkey_path, struct lyd_node **config)
721{
722 int ret = 0;
723 char *privkey = NULL, *pubkey = NULL;
724 NC_PRIVKEY_FORMAT privkey_type;
725 NC_PUBKEY_FORMAT pubkey_type;
726 const char *privkey_format, *pubkey_format;
727
728 NC_CHECK_ARG_RET(NULL, ctx, name, privkey_path, config, 1);
729
730 /* get the keys as a string from the given files */
731 ret = nc_server_config_new_get_keys(privkey_path, pubkey_path, &privkey, &pubkey, &privkey_type, &pubkey_type);
732 if (ret) {
733 ERR(NULL, "Getting keys from file(s) failed.");
734 goto cleanup;
735 }
736
737 /* get pubkey format str */
738 if (pubkey_type == NC_PUBKEY_FORMAT_X509) {
739 pubkey_format = "ietf-crypto-types:public-key-info-format";
740 } else {
741 pubkey_format = "ietf-crypto-types:ssh-public-key-format";
742 }
743
744 /* get privkey identityref value */
745 privkey_format = nc_config_new_privkey_format_to_identityref(privkey_type);
746 if (!privkey_format) {
747 ret = 1;
748 goto cleanup;
749 }
750
751 ret = nc_config_new_create(ctx, config, pubkey_format, "/ietf-keystore:keystore/asymmetric-keys/"
roman4cb8bb12023-06-29 09:16:46 +0200752 "asymmetric-key[name='%s']/public-key-format", name);
roman142718b2023-06-29 09:15:29 +0200753 if (ret) {
754 goto cleanup;
755 }
756
757 ret = nc_config_new_create(ctx, config, pubkey, "/ietf-keystore:keystore/asymmetric-keys/"
roman4cb8bb12023-06-29 09:16:46 +0200758 "asymmetric-key[name='%s']/public-key", name);
roman142718b2023-06-29 09:15:29 +0200759 if (ret) {
760 goto cleanup;
761 }
762
763 ret = nc_config_new_create(ctx, config, privkey_format, "/ietf-keystore:keystore/asymmetric-keys/"
roman4cb8bb12023-06-29 09:16:46 +0200764 "asymmetric-key[name='%s']/private-key-format", name);
roman142718b2023-06-29 09:15:29 +0200765 if (ret) {
766 goto cleanup;
767 }
768
769 ret = nc_config_new_create(ctx, config, privkey, "/ietf-keystore:keystore/asymmetric-keys/"
roman4cb8bb12023-06-29 09:16:46 +0200770 "asymmetric-key[name='%s']/cleartext-private-key", name);
roman142718b2023-06-29 09:15:29 +0200771 if (ret) {
772 goto cleanup;
773 }
774
775cleanup:
776 free(privkey);
777 free(pubkey);
778 return ret;
779}
780
781API int
782nc_server_config_new_truststore_pubkey(const struct ly_ctx *ctx, const char *bag_name, const char *pubkey_name,
783 const char *pubkey_path, struct lyd_node **config)
784{
785 int ret = 0;
786 char *pubkey = NULL;
787 NC_PUBKEY_FORMAT pubkey_format;
788 const char *format;
789
790 NC_CHECK_ARG_RET(NULL, ctx, bag_name, pubkey_name, pubkey_path, config, 1);
791
792 ret = nc_server_config_new_get_pubkey(pubkey_path, &pubkey, &pubkey_format);
793 if (ret) {
794 goto cleanup;
795 }
796
797 /* pubkey format to str */
798 if (pubkey_format == NC_PUBKEY_FORMAT_SSH2) {
799 format = "ietf-crypto-types:ssh-public-key-format";
800 } else {
801 format = "ietf-crypto-types:subject-public-key-info-format";
802 }
803
804 ret = nc_config_new_create(ctx, config, format, "/ietf-truststore:truststore/public-key-bags/"
roman4cb8bb12023-06-29 09:16:46 +0200805 "public-key-bag[name='%s']/public-key[name='%s']/public-key-format", bag_name, pubkey_name);
roman142718b2023-06-29 09:15:29 +0200806 if (ret) {
807 goto cleanup;
808 }
809
810 ret = nc_config_new_create(ctx, config, pubkey, "/ietf-truststore:truststore/public-key-bags/"
roman4cb8bb12023-06-29 09:16:46 +0200811 "public-key-bag[name='%s']/public-key[name='%s']/public-key", bag_name, pubkey_name);
roman142718b2023-06-29 09:15:29 +0200812 if (ret) {
813 goto cleanup;
814 }
815
816cleanup:
817 free(pubkey);
818 return ret;
819}
820
roman2eab4742023-06-06 10:00:26 +0200821#endif /* NC_ENABLED_SSH_TLS */
romanb6f44032023-06-30 15:07:56 +0200822
823int
824nc_config_new_delete(struct lyd_node **tree, const char *path_fmt, ...)
825{
826 int ret = 0;
827 va_list ap;
828 char *path = NULL;
829 struct lyd_node *sub = NULL;
830
831 va_start(ap, path_fmt);
832
833 /* create the path from the format */
834 ret = vasprintf(&path, path_fmt, ap);
835 if (ret == -1) {
836 ERRMEM;
837 path = NULL;
838 goto cleanup;
839 }
840
841 /* find the node we want to delete */
842 ret = lyd_find_path(*tree, path, 0, &sub);
843 if (ret) {
844 goto cleanup;
845 }
846
847 lyd_free_tree(sub);
848
849 /* set the node to top level container */
850 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
851 if (ret) {
852 goto cleanup;
853 }
854
855 /* add all default nodes */
856 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
857 if (ret) {
858 goto cleanup;
859 }
860
861cleanup:
862 free(path);
863 va_end(ap);
864 return ret;
865}
866
867int
868nc_config_new_create(const struct ly_ctx *ctx, struct lyd_node **tree, const char *value, const char *path_fmt, ...)
869{
870 int ret = 0;
871 va_list ap;
872 char *path = NULL;
873
874 va_start(ap, path_fmt);
875
876 /* create the path from the format */
877 ret = vasprintf(&path, path_fmt, ap);
878 if (ret == -1) {
879 ERRMEM;
880 path = NULL;
881 goto cleanup;
882 }
883
884 /* create the nodes in the path */
885 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
886 if (ret) {
887 goto cleanup;
888 }
889
890 /* set the node to the top level node */
891 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
892 if (ret) {
893 goto cleanup;
894 }
895
896 /* add all default nodes */
897 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
898 if (ret) {
899 goto cleanup;
900 }
901
902cleanup:
903 free(path);
904 va_end(ap);
905 return ret;
906}
907
908int
909nc_config_new_create_append(const struct ly_ctx *ctx, const char *parent_path, const char *child_name,
910 const char *value, struct lyd_node **tree)
911{
912 int ret = 0;
913 char *path = NULL;
914
915 /* create the path by appending child to the parent path */
916 ret = asprintf(&path, "%s/%s", parent_path, child_name);
917 if (ret == -1) {
918 ERRMEM;
919 path = NULL;
920 goto cleanup;
921 }
922
923 /* create the nodes in the path */
924 ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
925 if (ret) {
926 goto cleanup;
927 }
928
929 /* set the node to the top level node */
930 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
931 if (ret) {
932 goto cleanup;
933 }
934
935 /* add all default nodes */
936 ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
937 if (ret) {
938 goto cleanup;
939 }
940
941cleanup:
942 free(path);
943 return ret;
944}
945
946int
947nc_config_new_check_delete(struct lyd_node **tree, const char *path_fmt, ...)
948{
949 int ret = 0;
950 va_list ap;
951 char *path = NULL;
952 struct lyd_node *sub = NULL;
953
954 va_start(ap, path_fmt);
955
956 /* create the path from the format */
957 ret = vasprintf(&path, path_fmt, ap);
958 if (ret == -1) {
959 ERRMEM;
960 path = NULL;
961 goto cleanup;
962 }
963
964 /* find the node we want to delete */
965 ret = lyd_find_path(*tree, path, 0, &sub);
966 if ((ret == LY_EINCOMPLETE) || (ret == LY_ENOTFOUND)) {
967 ret = 0;
968 goto cleanup;
969 } else if (ret) {
970 ERR(NULL, "Unable to delete node in the path \"%s\".", path);
971 goto cleanup;
972 }
973
974 lyd_free_tree(sub);
975
976 /* set the node to top level container */
977 ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
978 if (ret) {
979 goto cleanup;
980 }
981
982cleanup:
983 free(path);
984 va_end(ap);
985 return ret;
986}
987
988API int
989nc_server_config_new_ch_persistent(const struct ly_ctx *ctx, const char *ch_client_name, struct lyd_node **config)
990{
991 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
992
993 /* delete periodic tree if exists */
994 if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
995 "netconf-client[name='%s']/connection-type/periodic", ch_client_name)) {
996 return 1;
997 }
998
999 return nc_config_new_create(ctx, config, NULL, "/ietf-netconf-server:netconf-server/call-home/"
1000 "netconf-client[name='%s']/connection-type/persistent", ch_client_name);
1001}
1002
1003API int
1004nc_server_config_new_ch_period(const struct ly_ctx *ctx, const char *ch_client_name, uint16_t period,
1005 struct lyd_node **config)
1006{
1007 char buf[6] = {0};
1008
1009 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, period, 1);
1010
1011 /* delete persistent tree if exists */
1012 if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1013 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1014 return 1;
1015 }
1016
1017 sprintf(buf, "%u", period);
1018 return nc_config_new_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
1019 "netconf-client[name='%s']/connection-type/periodic/period", ch_client_name);
1020}
1021
1022API int
1023nc_server_config_new_ch_anchor_time(const struct ly_ctx *ctx, const char *ch_client_name,
1024 const char *anchor_time, struct lyd_node **config)
1025{
1026 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, anchor_time, 1);
1027
1028 /* delete persistent tree if exists */
1029 if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1030 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1031 return 1;
1032 }
1033
1034 return nc_config_new_create(ctx, config, anchor_time, "/ietf-netconf-server:netconf-server/call-home/"
1035 "netconf-client[name='%s']/connection-type/periodic/anchor-time", ch_client_name);
1036}
1037
1038API int
1039nc_server_config_new_ch_idle_timeout(const struct ly_ctx *ctx, const char *ch_client_name,
1040 uint16_t idle_timeout, struct lyd_node **config)
1041{
1042 char buf[6] = {0};
1043
1044 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, 1);
1045
1046 /* delete persistent tree if exists */
1047 if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
1048 "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
1049 return 1;
1050 }
1051
1052 sprintf(buf, "%u", idle_timeout);
1053 return nc_config_new_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
1054 "netconf-client[name='%s']/connection-type/periodic/idle-timeout", ch_client_name);
1055}
1056
1057API int
1058nc_server_config_new_ch_reconnect_strategy(const struct ly_ctx *ctx, const char *ch_client_name,
1059 NC_CH_START_WITH start_with, uint8_t max_attempts, uint16_t max_wait, struct lyd_node **config)
1060{
1061 int ret = 0;
1062 char *path = NULL;
1063 char buf[6] = {0};
1064 const char *start_with_val;
1065
1066 NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
1067
1068 /* prepared the path */
1069 if (asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/"
1070 "netconf-client[name='%s']/reconnect-strategy", ch_client_name) == -1) {
1071 ERRMEM;
1072 path = NULL;
1073 ret = 1;
1074 goto cleanup;
1075 }
1076
1077 if (start_with) {
1078 /* get string value from enum */
1079 if (start_with == NC_CH_FIRST_LISTED) {
1080 start_with_val = "first-listed";
1081 } else if (start_with == NC_CH_LAST_CONNECTED) {
1082 start_with_val = "last-connected";
1083 } else {
1084 start_with_val = "random-selection";
1085 }
1086
1087 ret = nc_config_new_create_append(ctx, path, "start-with", start_with_val, config);
1088 if (ret) {
1089 goto cleanup;
1090 }
1091 }
1092
1093 if (max_attempts) {
1094 sprintf(buf, "%u", max_attempts);
1095 ret = nc_config_new_create_append(ctx, path, "max-attempts", buf, config);
1096 if (ret) {
1097 goto cleanup;
1098 }
1099 memset(buf, 0, 6);
1100 }
1101
1102 if (max_wait) {
1103 sprintf(buf, "%u", max_wait);
1104 ret = nc_config_new_create_append(ctx, path, "max-wait", buf, config);
1105 if (ret) {
1106 goto cleanup;
1107 }
1108 }
1109
1110cleanup:
1111 free(path);
1112 return ret;
1113}