blob: f8db1da0427092bd1755bb042903621a7697b479 [file] [log] [blame]
roman27215242023-03-10 14:55:00 +01001/**
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) 2015 - 2021 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
18#include <assert.h>
roman31820092023-03-24 15:26:21 +010019#include <crypt.h>
roman27215242023-03-10 14:55:00 +010020#include <ctype.h>
21#include <stdarg.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
roman31820092023-03-24 15:26:21 +010025#include <unistd.h>
roman27215242023-03-10 14:55:00 +010026
roman4f9bb442023-03-24 09:05:37 +010027#include <openssl/buffer.h>
roman27215242023-03-10 14:55:00 +010028#include <openssl/err.h>
roman4f9bb442023-03-24 09:05:37 +010029#include <openssl/evp.h>
roman27215242023-03-10 14:55:00 +010030#include <openssl/pem.h>
31
32#include "compat.h"
33#include "config_new.h"
34#include "libnetconf.h"
35#include "server_config.h"
roman4f9bb442023-03-24 09:05:37 +010036#include "session_server.h"
roman27215242023-03-10 14:55:00 +010037
roman31820092023-03-24 15:26:21 +010038#if !defined (HAVE_CRYPT_R)
39extern pthread_mutex_t crypt_lock;
40#endif
41
roman27215242023-03-10 14:55:00 +010042static int
43nc_server_config_ssh_new_get_keys(const char *privkey_path, const char *pubkey_path,
44 char **privkey, char **pubkey, EVP_PKEY **priv_pkey_p)
45{
46 int ret = 0;
47 EVP_PKEY *priv_pkey = NULL, *pub_pkey = NULL;
48 FILE *f_privkey = NULL, *f_pubkey = NULL;
49 BIO *bio_pub = NULL, *bio_priv = NULL;
50 int pub_len, priv_len;
51
52 assert(privkey_path);
53 assert(privkey);
54 assert(pubkey);
55 assert(priv_pkey_p);
56 *privkey = NULL;
57 *pubkey = NULL;
58 *priv_pkey_p = NULL;
59
60 /* get private key first */
61 f_privkey = fopen(privkey_path, "r");
62 if (!f_privkey) {
63 ERR(NULL, "Unable to open file \"%s\".", privkey_path);
64 ret = 1;
65 goto cleanup;
66 }
67
68 priv_pkey = PEM_read_PrivateKey(f_privkey, NULL, NULL, NULL);
69 if (!priv_pkey) {
70 ret = -1;
71 goto cleanup;
72 }
73 /* set out param */
74 *priv_pkey_p = priv_pkey;
75
76 bio_priv = BIO_new(BIO_s_mem());
77 if (!bio_priv) {
78 ret = -1;
79 goto cleanup;
80 }
81
82 ret = PEM_write_bio_PrivateKey(bio_priv, priv_pkey, NULL, NULL, 0, NULL, NULL);
83 if (!ret) {
84 ret = -1;
85 goto cleanup;
86 }
87
88 priv_len = BIO_pending(bio_priv);
89 if (priv_len <= 0) {
90 ret = -1;
91 goto cleanup;
92 }
93
94 *privkey = malloc(priv_len + 1);
95 if (!*privkey) {
96 ERRMEM;
97 ret = 1;
98 goto cleanup;
99 }
100
101 ret = BIO_read(bio_priv, *privkey, priv_len);
102 if (ret <= 0) {
103 ret = -1;
104 goto cleanup;
105 }
106 (*privkey)[priv_len] = '\0';
107
108 /* if public key is supplied, then read it */
109 if (pubkey_path) {
110 f_pubkey = fopen(pubkey_path, "r");
111 if (!f_pubkey) {
112 ERR(NULL, "Unable to open file \"%s\"", pubkey_path);
113 ret = 1;
114 goto cleanup;
115 }
116 pub_pkey = PEM_read_PUBKEY(f_pubkey, NULL, NULL, NULL);
117 if (!pub_pkey) {
118 ret = -1;
119 goto cleanup;
120 }
121 }
122
123 bio_pub = BIO_new(BIO_s_mem());
124 if (!bio_pub) {
125 ret = -1;
126 goto cleanup;
127 }
128
129 /* get public key either from the private key or from the given file */
130 if (pubkey_path) {
131 ret = PEM_write_bio_PUBKEY(bio_pub, pub_pkey);
132 } else {
133 ret = PEM_write_bio_PUBKEY(bio_pub, priv_pkey);
134 }
135 if (!ret) {
136 ret = -1;
137 goto cleanup;
138 }
139
140 pub_len = BIO_pending(bio_pub);
141 if (pub_len <= 0) {
142 ret = -1;
143 goto cleanup;
144 }
145
146 *pubkey = malloc(pub_len + 1);
147 if (!*pubkey) {
148 ERRMEM;
149 ret = 1;
150 goto cleanup;
151 }
152
153 ret = BIO_read(bio_pub, *pubkey, pub_len);
154 if (ret <= 0) {
155 ret = -1;
156 goto cleanup;
157 }
158 (*pubkey)[pub_len] = '\0';
159
160 ret = 0;
161cleanup:
162 if (ret < 0) {
163 ERR(NULL, "Error getting keys from file: \"%s\".", ERR_reason_error_string(ERR_get_error()));
164 ret = 1;
165 }
166 EVP_PKEY_free(pub_pkey);
167 if (f_privkey) {
168 fclose(f_privkey);
169 }
170 if (f_pubkey) {
171 fclose(f_pubkey);
172 }
173 BIO_free(bio_priv);
174 BIO_free(bio_pub);
175 return ret;
176}
177
178API int
179nc_server_config_ssh_new_hostkey(const char *privkey_path, const char *pubkey_path, const struct ly_ctx *ctx,
180 const char *endpt_name, const char *hostkey_name, struct lyd_node **config)
181{
182 int ret = 0;
183 char *pub_key = NULL, *priv_key = NULL, *pub_key_stripped, *priv_key_stripped;
184 struct lyd_node *new_tree;
185 char *tree_path = NULL;
186 EVP_PKEY *priv_pkey = NULL;
187
188 if (!privkey_path || !config || !ctx || !endpt_name || !hostkey_name) {
189 ERRARG("privkey_path or config or ctx or endpt_name or hostkey_name");
190 }
191
192 /* get the keys as a string from the given files */
193 ret = nc_server_config_ssh_new_get_keys(privkey_path, pubkey_path, &priv_key, &pub_key, &priv_pkey);
194 if (ret) {
195 ERR(NULL, "Getting keys from file(s) failed.");
196 goto cleanup;
197 }
198
199 /* prepare path where leaves will get inserted */
200 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/"
201 "server-identity/host-key[name='%s']/public-key/local-definition", endpt_name, hostkey_name);
202 if (!tree_path) {
203 ERRMEM;
204 ret = 1;
205 goto cleanup;
206 }
207
208 /* create all the nodes in the path if they weren't there */
209 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
210 if (ret) {
211 goto cleanup;
212 }
213 if (!*config) {
214 *config = new_tree;
215 }
216
217 /* give the top level container create operation */
218 ret = lyd_new_meta(ctx, *config, NULL, "yang:operation", "create", 0, NULL);
219 if (ret) {
220 goto cleanup;
221 }
222
223 /* find the node where leaves will get inserted */
224 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
225 if (ret) {
226 goto cleanup;
227 }
228
229 /* insert pubkey format */
230 if (!strstr(pub_key, "---- BEGIN SSH2 PUBLIC KEY ----")) {
231 ret = lyd_new_term(new_tree, NULL, "public-key-format", "ietf-crypto-types:ssh-public-key-format", 0, NULL);
232 } else {
233 ret = lyd_new_term(new_tree, NULL, "public-key-format", "ietf-crypto-types:subject-public-key-info-format", 0, NULL);
234 }
235 if (ret) {
236 goto cleanup;
237 }
238
239 /* strip pubkey's header and footer */
240 pub_key_stripped = pub_key + strlen("-----BEGIN PUBLIC KEY-----") + 1;
241 pub_key_stripped[strlen(pub_key_stripped) - strlen("-----END PUBLIC KEY-----") - 2] = '\0';
242
243 /* insert pubkey b64 */
244 ret = lyd_new_term(new_tree, NULL, "public-key", pub_key_stripped, 0, NULL);
245 if (ret) {
246 goto cleanup;
247 }
248
249 /* do the same for private key */
250 if (EVP_PKEY_is_a(priv_pkey, "RSA")) {
251 ret = lyd_new_term(new_tree, NULL, "private-key-format", "ietf-crypto-types:rsa-private-key-format", 0, NULL);
252 } else if (EVP_PKEY_is_a(priv_pkey, "EC")) {
253 ret = lyd_new_term(new_tree, NULL, "private-key-format", "ietf-crypto-types:ec-private-key-format", 0, NULL);
254 } else {
255 ERR(NULL, "Private key type not supported.");
256 ret = 1;
257 }
258 if (ret) {
259 goto cleanup;
260 }
261
262 priv_key_stripped = priv_key + strlen("-----BEGIN PRIVATE KEY-----") + 1;
263 priv_key_stripped[strlen(priv_key_stripped) - strlen("-----END PRIVATE KEY-----") - 2] = '\0';
264
265 ret = lyd_new_term(new_tree, NULL, "cleartext-private-key", priv_key_stripped, 0, NULL);
266 if (ret) {
267 goto cleanup;
268 }
269
roman4f9bb442023-03-24 09:05:37 +0100270 /* Add all default nodes */
271 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
272 if (ret) {
273 goto cleanup;
274 }
275
roman27215242023-03-10 14:55:00 +0100276cleanup:
277 EVP_PKEY_free(priv_pkey);
278 free(priv_key);
279 free(pub_key);
280 free(tree_path);
281 return ret;
282}
283
284API int
285nc_server_config_ssh_new_address_port(const char *address, const char *port, const struct ly_ctx *ctx,
286 const char *endpt_name, struct lyd_node **config)
287{
288 int ret = 0;
289 char *tree_path = NULL;
roman4f9bb442023-03-24 09:05:37 +0100290 struct lyd_node *new_tree, *port_node;
roman27215242023-03-10 14:55:00 +0100291
292 if (!address || !port || !ctx || !endpt_name || !config) {
293 ERRARG("args");
294 ret = 1;
295 goto cleanup;
296 }
297
298 /* prepare path for instertion of leaves later */
299 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters", endpt_name);
300 if (!tree_path) {
301 ERRMEM;
302 ret = 1;
303 goto cleanup;
304 }
305
306 /* create all the nodes in the path */
307 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
308 if (ret) {
309 goto cleanup;
310 }
311 if (!*config) {
312 *config = new_tree;
313 }
314
roman4f9bb442023-03-24 09:05:37 +0100315 if (!new_tree) {
316 /* no new nodes were created */
317 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
318 } else {
319 /* config was NULL */
320 ret = lyd_find_path(new_tree, tree_path, 0, &new_tree);
321 }
roman27215242023-03-10 14:55:00 +0100322 if (ret) {
323 ERR(NULL, "Unable to find tcp-server-parameters container.");
324 goto cleanup;
325 }
326
327 ret = lyd_new_term(new_tree, NULL, "local-address", address, 0, NULL);
328 if (ret) {
329 goto cleanup;
330 }
331
roman4f9bb442023-03-24 09:05:37 +0100332 ret = lyd_find_path(new_tree, "local-port", 0, &port_node);
333 if (!ret) {
334 ret = lyd_change_term(port_node, port);
335 } else if (ret == LY_ENOTFOUND) {
336 ret = lyd_new_term(new_tree, NULL, "local-port", port, 0, NULL);
337 }
338
romana08a05f2023-04-05 14:46:29 +0200339 if (ret && (ret != LY_EEXIST) && (ret != LY_ENOT)) {
340 /* only fail if there was actually an error */
roman27215242023-03-10 14:55:00 +0100341 goto cleanup;
342 }
343
roman4f9bb442023-03-24 09:05:37 +0100344 /* Add all default nodes */
345 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
346 if (ret) {
347 goto cleanup;
348 }
roman27215242023-03-10 14:55:00 +0100349cleanup:
350 free(tree_path);
351 return ret;
352}
353
354static int
355nc_server_config_ssh_new_transport_params_prep(const struct ly_ctx *ctx, const char *endpt_name,
356 struct lyd_node *config, struct lyd_node **new_tree, struct lyd_node **alg_tree)
357{
358 int ret = 0;
359 char *tree_path = NULL;
360
361 /* prepare path */
362 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/"
363 "ssh/ssh-server-parameters/transport-params", endpt_name);
364 if (!tree_path) {
365 ERRMEM;
366 ret = 1;
367 goto cleanup;
368 }
369
370 /* create all the nodes in the path */
371 ret = lyd_new_path2(config, ctx, tree_path, NULL, 0, 0, LYD_NEW_PATH_UPDATE, new_tree, alg_tree);
372 if (ret) {
373 ERR(NULL, "Creating new path to transport-params failed.");
374 goto cleanup;
375 }
376
roman4f9bb442023-03-24 09:05:37 +0100377 if (!*alg_tree) {
378 /* no new nodes added */
379 ret = lyd_find_path(config, tree_path, 0, alg_tree);
380 if (ret) {
381 goto cleanup;
382 }
383 }
384
roman27215242023-03-10 14:55:00 +0100385cleanup:
386 free(tree_path);
387 return ret;
388}
389
390static int
391nc_server_config_ssh_new_transport_params(const struct ly_ctx *ctx, NC_ALG_TYPE alg_type, int alg_count, va_list ap,
392 struct lyd_node *tree)
393{
394 int i, ret = 0;
395 char *alg, *alg_ident;
396 const char *module, *alg_path, *old_path;
397 struct lyd_node *old = NULL;
398
399 /* get the correct module with the indentity base and the path in the ietf-netconf-server module */
400 switch (alg_type) {
401 case NC_ALG_HOSTKEY:
402 module = "iana-ssh-public-key-algs";
403 alg_path = "host-key/host-key-alg";
404 old_path = "host-key";
405 break;
406 case NC_ALG_KEY_EXCHANGE:
407 module = "iana-ssh-key-exchange-algs";
408 alg_path = "key-exchange/key-exchange-alg";
409 old_path = "key-exchange";
410 break;
411 case NC_ALG_ENCRYPTION:
412 module = "iana-ssh-encryption-algs";
413 alg_path = "encryption/encryption-alg";
414 old_path = "encryption";
415 break;
416 case NC_ALG_MAC:
417 module = "iana-ssh-mac-algs";
418 alg_path = "mac/mac-alg";
419 old_path = "mac";
420 break;
421 default:
422 ret = 1;
423 ERR(NULL, "Unknown algorithm type.");
424 goto cleanup;
425 }
426
427 /* delete all older algorithms (if any) se they can be replaced by the new ones */
428 lyd_find_path(tree, old_path, 0, &old);
429 if (old) {
430 lyd_free_tree(old);
431 }
432
433 for (i = 0; i < alg_count; i++) {
434 alg = va_arg(ap, char *);
435
436 asprintf(&alg_ident, "%s:%s", module, alg);
437 if (!alg_ident) {
438 ERRMEM;
439 ret = 1;
440 goto cleanup;
441 }
442
443 /* create the leaf list */
444 ret = lyd_new_path(tree, ctx, alg_path, alg_ident, 0, NULL);
445 if (ret) {
446 ERR(NULL, "Creating new algorithm leaf-list failed.");
447 goto cleanup;
448 }
449
450 free(alg_ident);
451 }
452
453cleanup:
454 va_end(ap);
455 return ret;
456}
457
458API int
459nc_server_config_ssh_new_host_key_algs(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config,
460 int alg_count, ...)
461{
462 int ret = 0;
463 struct lyd_node *new_tree, *alg_tree;
464 va_list ap;
465
466 ret = nc_server_config_ssh_new_transport_params_prep(ctx, endpt_name, *config, &new_tree, &alg_tree);
467 if (ret) {
468 goto cleanup;
469 }
470
471 if (!*config) {
472 *config = new_tree;
473 }
474
475 va_start(ap, alg_count);
476
477 ret = nc_server_config_ssh_new_transport_params(ctx, NC_ALG_HOSTKEY, alg_count, ap, alg_tree);
478 if (ret) {
479 goto cleanup;
480 }
481
roman4f9bb442023-03-24 09:05:37 +0100482 /* Add all default nodes */
483 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
484 if (ret) {
485 goto cleanup;
486 }
roman27215242023-03-10 14:55:00 +0100487cleanup:
488 return ret;
489}
490
491API int
492nc_server_config_ssh_new_key_exchange_algs(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config,
493 int alg_count, ...)
494{
495 int ret = 0;
496 struct lyd_node *new_tree, *alg_tree;
497 va_list ap;
498
499 ret = nc_server_config_ssh_new_transport_params_prep(ctx, endpt_name, *config, &new_tree, &alg_tree);
500 if (ret) {
501 goto cleanup;
502 }
503
504 if (!*config) {
505 *config = new_tree;
506 }
507
508 va_start(ap, alg_count);
509
510 ret = nc_server_config_ssh_new_transport_params(ctx, NC_ALG_KEY_EXCHANGE, alg_count, ap, alg_tree);
511 if (ret) {
512 goto cleanup;
513 }
514
roman4f9bb442023-03-24 09:05:37 +0100515 /* Add all default nodes */
516 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
517 if (ret) {
518 goto cleanup;
519 }
roman27215242023-03-10 14:55:00 +0100520cleanup:
521 return ret;
522}
523
524API int
525nc_server_config_ssh_new_encryption_algs(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config,
526 int alg_count, ...)
527{
528 int ret = 0;
529 struct lyd_node *new_tree, *alg_tree;
530 va_list ap;
531
532 ret = nc_server_config_ssh_new_transport_params_prep(ctx, endpt_name, *config, &new_tree, &alg_tree);
533 if (ret) {
534 goto cleanup;
535 }
536
537 if (!*config) {
538 *config = new_tree;
539 }
540
541 va_start(ap, alg_count);
542
543 ret = nc_server_config_ssh_new_transport_params(ctx, NC_ALG_ENCRYPTION, alg_count, ap, alg_tree);
544 if (ret) {
545 goto cleanup;
546 }
547
roman4f9bb442023-03-24 09:05:37 +0100548 /* Add all default nodes */
549 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
550 if (ret) {
551 goto cleanup;
552 }
roman27215242023-03-10 14:55:00 +0100553cleanup:
554 return ret;
555}
556
557API int
558nc_server_config_ssh_new_mac_algs(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config,
559 int alg_count, ...)
560{
561 int ret = 0;
562 struct lyd_node *new_tree, *alg_tree;
563 va_list ap;
564
565 ret = nc_server_config_ssh_new_transport_params_prep(ctx, endpt_name, *config, &new_tree, &alg_tree);
566 if (ret) {
567 goto cleanup;
568 }
569
570 if (!*config) {
571 *config = new_tree;
572 }
573
574 va_start(ap, alg_count);
575
576 ret = nc_server_config_ssh_new_transport_params(ctx, NC_ALG_MAC, alg_count, ap, alg_tree);
577 if (ret) {
578 goto cleanup;
579 }
580
roman4f9bb442023-03-24 09:05:37 +0100581 /* Add all default nodes */
582 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
583 if (ret) {
584 goto cleanup;
585 }
roman27215242023-03-10 14:55:00 +0100586cleanup:
587 return ret;
588}
roman4f9bb442023-03-24 09:05:37 +0100589
590static int
591nc_server_config_ssh_read_openssh_pubkey(FILE *f, char **pubkey)
592{
593 int ret = 0;
594 char *buffer = NULL;
595 size_t len = 0;
596 char *start, *end;
597
598 if (getline(&buffer, &len, f) < 0) {
599 ERR(NULL, "Reading line from file failed.");
600 return 1;
601 }
602
603 if (len < 8) {
604 ERR(NULL, "Unexpected public key format.");
605 ret = 1;
606 goto cleanup;
607 }
608
609 start = buffer;
roman6e5fd702023-04-27 14:30:27 +0200610 if (!strncmp(buffer, "ssh-dss ", 8)) {
611 ERR(NULL, "DSA public keys not supported.");
612 ret = 1;
613 goto cleanup;
614 } else if (!strncmp(buffer, "ssh-rsa ", 8)) {
roman4f9bb442023-03-24 09:05:37 +0100615 start += strlen("ssh-rsa ");
roman6e5fd702023-04-27 14:30:27 +0200616 } else if (!strncmp(buffer, "ecdsa-sha2-nistp256 ", 20)) {
617 start += strlen("ecdsa-sha2-nistp256 ");
618 } else if (!strncmp(buffer, "ecdsa-sha2-nistp384 ", 20)) {
619 start += strlen("ecdsa-sha2-nistp384 ");
620 } else if (!strncmp(buffer, "ecdsa-sha2-nistp521 ", 20)) {
621 start += strlen("ecdsa-sha2-nistp521 ");
622 } else {
623 ERR(NULL, "Unknown public key type.");
624 ret = 1;
625 goto cleanup;
roman4f9bb442023-03-24 09:05:37 +0100626 }
627
roman6e5fd702023-04-27 14:30:27 +0200628 end = strchr(start, ' ');
629 if (!end) {
630 ERR(NULL, "Unexpected public key format.");
631 ret = 1;
632 goto cleanup;
633 }
634
635 *pubkey = strdup(start);
636 if (!*pubkey) {
637 ERRMEM;
638 ret = 1;
639 goto cleanup;
640 }
641
642 (*pubkey)[strlen(*pubkey) - strlen(end)] = '\0';
643
roman4f9bb442023-03-24 09:05:37 +0100644cleanup:
645 free(buffer);
646 return ret;
647}
648
649static int
650nc_server_config_ssh_read_ssh2_pubkey(FILE *f, char **pubkey)
651{
652 char *buffer = NULL;
653 size_t len = 0;
654 size_t pubkey_len = 0;
655 void *tmp;
656
657 while (getline(&buffer, &len, f) > 0) {
658 if (!strncmp(buffer, "----", 4)) {
659 free(buffer);
660 buffer = NULL;
661 continue;
662 }
663
664 if (!strncmp(buffer, "Comment:", 8)) {
665 free(buffer);
666 buffer = NULL;
667 continue;
668 }
669
670 len = strlen(buffer);
671
672 tmp = realloc(*pubkey, pubkey_len + len + 1);
673 if (!tmp) {
674 ERRMEM;
675 free(buffer);
676 buffer = NULL;
677 return 1;
678 }
679
680 *pubkey = tmp;
681 memcpy(*pubkey + pubkey_len, buffer, len);
682 pubkey_len += len;
683 free(buffer);
684 buffer = NULL;
685 }
686
687 if (!pubkey_len) {
688 ERR(NULL, "Unexpected public key format.");
689 return 1;
690 }
691
692 (*pubkey)[pubkey_len - 1] = '\0';
693 free(buffer);
694 return 0;
695}
696
697static int
698nc_server_config_ssh_read_subject_pubkey(FILE *f, char **pubkey)
699{
700 int ret = 0;
701 EVP_PKEY *pkey;
702 BIO *bio;
703 BUF_MEM *mem;
704 char *tmp;
705
706 pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
707 if (!pkey) {
708 ret = -1;
709 goto cleanup;
710 }
711
712 bio = BIO_new(BIO_s_mem());
713 if (!bio) {
714 ret = -1;
715 goto cleanup;
716 }
717
718 ret = PEM_write_bio_PUBKEY(bio, pkey);
719 if (!ret) {
720 ret = -1;
721 goto cleanup;
722 }
723 ret = 0;
724
725 BIO_get_mem_ptr(bio, &mem);
726 tmp = malloc(mem->length + 1);
727 if (!tmp) {
728 ERRMEM;
729 ret = 1;
730 goto cleanup;
731 }
732
733 memcpy(tmp, mem->data, mem->length);
734 tmp[mem->length] = '\0';
735
736 *pubkey = strdup(tmp + strlen("-----BEGIN PUBLIC KEY-----\n"));
737 (*pubkey)[strlen(*pubkey) - strlen("\n-----END PUBLIC KEY-----\n")] = '\0';
738
739cleanup:
740 if (ret == -1) {
741 ERR(NULL, "Error getting public key from file (OpenSSL Error): \"%s\".", ERR_reason_error_string(ERR_get_error()));
742 ret = 1;
743 }
744
745 BIO_free(bio);
746 EVP_PKEY_free(pkey);
747 free(tmp);
748
749 return ret;
750}
751
752static int
753nc_server_config_ssh_new_get_pubkey(const char *pubkey_path, char **pubkey, NC_SSH_PUBKEY_TYPE *pubkey_type)
754{
755 int ret = 0;
756 FILE *f = NULL;
757 char *buffer = NULL;
758 size_t len = 0;
759
760 *pubkey = NULL;
761
762 f = fopen(pubkey_path, "r");
763 if (!f) {
764 ERR(NULL, "Unable to open file \"%s\".", pubkey_path);
765 ret = 1;
766 goto cleanup;
767 }
768
769 if (getline(&buffer, &len, f) < 0) {
770 ERR(NULL, "Error reading header from file \"%s\".", pubkey_path);
771 ret = 1;
772 goto cleanup;
773 }
774
775 rewind(f);
776
777 if (!strncmp(buffer, "-----BEGIN PUBLIC KEY-----\n", strlen("-----BEGIN PUBLIC KEY-----\n"))) {
778 ret = nc_server_config_ssh_read_subject_pubkey(f, pubkey);
779 *pubkey_type = NC_SSH_PUBKEY_X509;
780 } else if (!strncmp(buffer, "---- BEGIN SSH2 PUBLIC KEY ----\n", strlen("---- BEGIN SSH2 PUBLIC KEY ----\n"))) {
781 ret = nc_server_config_ssh_read_ssh2_pubkey(f, pubkey);
782 *pubkey_type = NC_SSH_PUBKEY_SSH2;
783 } else {
784 ret = nc_server_config_ssh_read_openssh_pubkey(f, pubkey);
785 *pubkey_type = NC_SSH_PUBKEY_SSH2;
786 }
787
788 if (ret) {
789 ERR(NULL, "Error getting public key from file \"%s\".", pubkey_path);
790 goto cleanup;
791 }
792
793cleanup:
794 if (f) {
795 fclose(f);
796 }
797
798 free(buffer);
799
800 return ret;
801}
802
803API int
roman31820092023-03-24 15:26:21 +0100804nc_server_config_ssh_new_client_auth_pubkey(const char *pubkey_path, const struct ly_ctx *ctx, const char *endpt_name,
roman4f9bb442023-03-24 09:05:37 +0100805 const char *user_name, const char *pubkey_name, struct lyd_node **config)
806{
807 int ret = 0;
808 char *pubkey = NULL, *tree_path = NULL;
809 struct lyd_node *new_tree;
810 NC_SSH_PUBKEY_TYPE pubkey_type;
811
812 ret = nc_server_config_ssh_new_get_pubkey(pubkey_path, &pubkey, &pubkey_type);
813 if (ret) {
814 goto cleanup;
815 }
816
817 /* prepare path where leaves will get inserted */
818 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
819 "users/user[name='%s']/public-keys/local-definition/public-key[name='%s']", endpt_name, user_name, pubkey_name);
820 if (!tree_path) {
821 ERRMEM;
822 ret = 1;
823 goto cleanup;
824 }
825
826 /* create all the nodes in the path if they weren't there */
827 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
828 if (ret) {
829 goto cleanup;
830 }
831 if (!*config) {
832 *config = new_tree;
833 }
834
835 /* find the node where leaves will get inserted */
836 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
837 if (ret) {
838 goto cleanup;
839 }
840
841 /* insert pubkey format */
842 if (pubkey_type == NC_SSH_PUBKEY_SSH2) {
843 ret = lyd_new_term(new_tree, NULL, "public-key-format", "ietf-crypto-types:ssh-public-key-format", 0, NULL);
844 } else {
845 ret = lyd_new_term(new_tree, NULL, "public-key-format", "ietf-crypto-types:subject-public-key-info-format", 0, NULL);
846 }
847 if (ret) {
848 goto cleanup;
849 }
850
851 /* insert pubkey b64 */
852 ret = lyd_new_term(new_tree, NULL, "public-key", pubkey, 0, NULL);
853 if (ret) {
854 goto cleanup;
855 }
856
857 /* Add all default nodes */
858 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
859 if (ret) {
860 goto cleanup;
861 }
862
863cleanup:
864 free(tree_path);
865 free(pubkey);
866 return ret;
867}
roman31820092023-03-24 15:26:21 +0100868
869API int
870nc_server_config_ssh_new_client_auth_password(const char *password, const struct ly_ctx *ctx, const char *endpt_name,
871 const char *user_name, struct lyd_node **config)
872{
873 int ret = 0;
874 char *tree_path = NULL, *hashed_pw = NULL;
875 struct lyd_node *new_tree;
876 const char *salt = "$6$idsizuippipk$";
877
878#ifdef HAVE_CRYPT_R
879 struct crypt_data cdata;
880#endif
881
882 /* prepare path where the leaf will get inserted */
883 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
884 "users/user[name='%s']", endpt_name, user_name);
885 if (!tree_path) {
886 ERRMEM;
887 ret = 1;
888 goto cleanup;
889 }
890
891 /* create all the nodes in the path if they weren't there */
892 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
893 if (ret) {
894 goto cleanup;
895 }
896 if (!*config) {
897 *config = new_tree;
898 }
899
900 /* find the node where the leaf will get inserted */
901 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
902 if (ret) {
903 goto cleanup;
904 }
905
906#ifdef HAVE_CRYPT_R
907 cdata.initialized = 0;
908 hashed_pw = crypt_r(password, salt, &data);
909#else
910 pthread_mutex_lock(&crypt_lock);
911 hashed_pw = crypt(password, salt);
912 pthread_mutex_unlock(&crypt_lock);
913#endif
914
915 if (!hashed_pw) {
916 ERR(NULL, "Hashing password failed.");
917 ret = 1;
918 goto cleanup;
919 }
920
921 /* insert SHA-512 hashed password */
922 ret = lyd_new_term(new_tree, NULL, "password", hashed_pw, 0, NULL);
923 if (ret) {
924 goto cleanup;
925 }
926
927 /* Add all default nodes */
928 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
929 if (ret) {
930 goto cleanup;
931 }
932
933cleanup:
934 free(tree_path);
935 return ret;
936}
937
938API int
939nc_server_config_ssh_new_client_auth_none(const struct ly_ctx *ctx, const char *endpt_name,
940 const char *user_name, struct lyd_node **config)
941{
942 int ret = 0;
943 char *tree_path = NULL;
944 struct lyd_node *new_tree;
945
946 /* prepare path where the leaf will get inserted */
947 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
948 "users/user[name='%s']", endpt_name, user_name);
949 if (!tree_path) {
950 ERRMEM;
951 ret = 1;
952 goto cleanup;
953 }
954
955 /* create all the nodes in the path if they weren't there */
956 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
957 if (ret) {
958 goto cleanup;
959 }
960 if (!*config) {
961 *config = new_tree;
962 }
963
964 /* find the node where the leaf will get inserted */
965 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
966 if (ret) {
967 goto cleanup;
968 }
969
970 /* insert none leaf */
971 ret = lyd_new_term(new_tree, NULL, "none", NULL, 0, NULL);
972 if (ret) {
973 goto cleanup;
974 }
975
976 /* Add all default nodes */
977 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
978 if (ret) {
979 goto cleanup;
980 }
981
982cleanup:
983 free(tree_path);
984 return ret;
985}
986
987API int
988nc_server_config_ssh_new_client_auth_interactive(const char *pam_config_name, const char *pam_config_dir,
989 const struct ly_ctx *ctx, const char *endpt_name,
990 const char *user_name, struct lyd_node **config)
991{
992 int ret = 0;
993 char *tree_path = NULL;
994 struct lyd_node *new_tree;
995
996 /* prepare path where the leaf will get inserted */
997 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
998 "users/user[name='%s']/libnetconf2-netconf-server:keyboard-interactive", endpt_name, user_name);
999 if (!tree_path) {
1000 ERRMEM;
1001 ret = 1;
1002 goto cleanup;
1003 }
1004
1005 /* create all the nodes in the path if they weren't there */
1006 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
1007 if (ret) {
1008 goto cleanup;
1009 }
1010 if (!*config) {
1011 *config = new_tree;
1012 }
1013
1014 /* find the node where the leaf will get inserted */
1015 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
1016 if (ret) {
1017 goto cleanup;
1018 }
1019
1020 /* insert file-name leaf */
1021 ret = lyd_new_term(new_tree, NULL, "pam-config-file-name", pam_config_name, 0, NULL);
1022 if (ret) {
1023 goto cleanup;
1024 }
1025
1026 if (pam_config_dir) {
1027 /* insert file-path leaf */
1028 ret = lyd_new_term(new_tree, NULL, "pam-config-file-dir", pam_config_dir, 0, NULL);
1029 if (ret) {
1030 goto cleanup;
1031 }
1032 }
1033
1034 /* Add all default nodes */
1035 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
1036 if (ret) {
1037 goto cleanup;
1038 }
1039
1040cleanup:
1041 free(tree_path);
1042 return ret;
1043}