blob: e08c58ddc589d10d74745de254cbaf921433894d [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);
roman44600f42023-04-28 15:54:27 +0200254 } else if (EVP_PKEY_is_a(priv_pkey, "ED25519")) {
255 ret = lyd_new_term(new_tree, NULL, "private-key-format", "libnetconf2-netconf-server:ed25519-private-key-format", 0, NULL);
roman27215242023-03-10 14:55:00 +0100256 } else {
257 ERR(NULL, "Private key type not supported.");
258 ret = 1;
259 }
260 if (ret) {
261 goto cleanup;
262 }
263
264 priv_key_stripped = priv_key + strlen("-----BEGIN PRIVATE KEY-----") + 1;
265 priv_key_stripped[strlen(priv_key_stripped) - strlen("-----END PRIVATE KEY-----") - 2] = '\0';
266
267 ret = lyd_new_term(new_tree, NULL, "cleartext-private-key", priv_key_stripped, 0, NULL);
268 if (ret) {
269 goto cleanup;
270 }
271
roman4f9bb442023-03-24 09:05:37 +0100272 /* Add all default nodes */
273 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
274 if (ret) {
275 goto cleanup;
276 }
277
roman27215242023-03-10 14:55:00 +0100278cleanup:
279 EVP_PKEY_free(priv_pkey);
280 free(priv_key);
281 free(pub_key);
282 free(tree_path);
283 return ret;
284}
285
286API int
287nc_server_config_ssh_new_address_port(const char *address, const char *port, const struct ly_ctx *ctx,
288 const char *endpt_name, struct lyd_node **config)
289{
290 int ret = 0;
291 char *tree_path = NULL;
roman4f9bb442023-03-24 09:05:37 +0100292 struct lyd_node *new_tree, *port_node;
roman27215242023-03-10 14:55:00 +0100293
294 if (!address || !port || !ctx || !endpt_name || !config) {
295 ERRARG("args");
296 ret = 1;
297 goto cleanup;
298 }
299
300 /* prepare path for instertion of leaves later */
301 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters", endpt_name);
302 if (!tree_path) {
303 ERRMEM;
304 ret = 1;
305 goto cleanup;
306 }
307
308 /* create all the nodes in the path */
309 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
310 if (ret) {
311 goto cleanup;
312 }
313 if (!*config) {
314 *config = new_tree;
315 }
316
roman4f9bb442023-03-24 09:05:37 +0100317 if (!new_tree) {
318 /* no new nodes were created */
319 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
320 } else {
321 /* config was NULL */
322 ret = lyd_find_path(new_tree, tree_path, 0, &new_tree);
323 }
roman27215242023-03-10 14:55:00 +0100324 if (ret) {
325 ERR(NULL, "Unable to find tcp-server-parameters container.");
326 goto cleanup;
327 }
328
329 ret = lyd_new_term(new_tree, NULL, "local-address", address, 0, NULL);
330 if (ret) {
331 goto cleanup;
332 }
333
roman4f9bb442023-03-24 09:05:37 +0100334 ret = lyd_find_path(new_tree, "local-port", 0, &port_node);
335 if (!ret) {
336 ret = lyd_change_term(port_node, port);
337 } else if (ret == LY_ENOTFOUND) {
338 ret = lyd_new_term(new_tree, NULL, "local-port", port, 0, NULL);
339 }
340
romana08a05f2023-04-05 14:46:29 +0200341 if (ret && (ret != LY_EEXIST) && (ret != LY_ENOT)) {
342 /* only fail if there was actually an error */
roman27215242023-03-10 14:55:00 +0100343 goto cleanup;
344 }
345
roman4f9bb442023-03-24 09:05:37 +0100346 /* Add all default nodes */
347 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
348 if (ret) {
349 goto cleanup;
350 }
roman27215242023-03-10 14:55:00 +0100351cleanup:
352 free(tree_path);
353 return ret;
354}
355
356static int
357nc_server_config_ssh_new_transport_params_prep(const struct ly_ctx *ctx, const char *endpt_name,
358 struct lyd_node *config, struct lyd_node **new_tree, struct lyd_node **alg_tree)
359{
360 int ret = 0;
361 char *tree_path = NULL;
362
363 /* prepare path */
364 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/"
365 "ssh/ssh-server-parameters/transport-params", endpt_name);
366 if (!tree_path) {
367 ERRMEM;
368 ret = 1;
369 goto cleanup;
370 }
371
372 /* create all the nodes in the path */
373 ret = lyd_new_path2(config, ctx, tree_path, NULL, 0, 0, LYD_NEW_PATH_UPDATE, new_tree, alg_tree);
374 if (ret) {
375 ERR(NULL, "Creating new path to transport-params failed.");
376 goto cleanup;
377 }
378
roman4f9bb442023-03-24 09:05:37 +0100379 if (!*alg_tree) {
380 /* no new nodes added */
381 ret = lyd_find_path(config, tree_path, 0, alg_tree);
382 if (ret) {
383 goto cleanup;
384 }
385 }
386
roman27215242023-03-10 14:55:00 +0100387cleanup:
388 free(tree_path);
389 return ret;
390}
391
392static int
393nc_server_config_ssh_new_transport_params(const struct ly_ctx *ctx, NC_ALG_TYPE alg_type, int alg_count, va_list ap,
394 struct lyd_node *tree)
395{
396 int i, ret = 0;
397 char *alg, *alg_ident;
398 const char *module, *alg_path, *old_path;
399 struct lyd_node *old = NULL;
400
401 /* get the correct module with the indentity base and the path in the ietf-netconf-server module */
402 switch (alg_type) {
403 case NC_ALG_HOSTKEY:
404 module = "iana-ssh-public-key-algs";
405 alg_path = "host-key/host-key-alg";
406 old_path = "host-key";
407 break;
408 case NC_ALG_KEY_EXCHANGE:
409 module = "iana-ssh-key-exchange-algs";
410 alg_path = "key-exchange/key-exchange-alg";
411 old_path = "key-exchange";
412 break;
413 case NC_ALG_ENCRYPTION:
414 module = "iana-ssh-encryption-algs";
415 alg_path = "encryption/encryption-alg";
416 old_path = "encryption";
417 break;
418 case NC_ALG_MAC:
419 module = "iana-ssh-mac-algs";
420 alg_path = "mac/mac-alg";
421 old_path = "mac";
422 break;
423 default:
424 ret = 1;
425 ERR(NULL, "Unknown algorithm type.");
426 goto cleanup;
427 }
428
429 /* delete all older algorithms (if any) se they can be replaced by the new ones */
430 lyd_find_path(tree, old_path, 0, &old);
431 if (old) {
432 lyd_free_tree(old);
433 }
434
435 for (i = 0; i < alg_count; i++) {
436 alg = va_arg(ap, char *);
437
438 asprintf(&alg_ident, "%s:%s", module, alg);
439 if (!alg_ident) {
440 ERRMEM;
441 ret = 1;
442 goto cleanup;
443 }
444
445 /* create the leaf list */
446 ret = lyd_new_path(tree, ctx, alg_path, alg_ident, 0, NULL);
447 if (ret) {
448 ERR(NULL, "Creating new algorithm leaf-list failed.");
449 goto cleanup;
450 }
451
452 free(alg_ident);
453 }
454
455cleanup:
456 va_end(ap);
457 return ret;
458}
459
460API int
461nc_server_config_ssh_new_host_key_algs(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config,
462 int alg_count, ...)
463{
464 int ret = 0;
465 struct lyd_node *new_tree, *alg_tree;
466 va_list ap;
467
468 ret = nc_server_config_ssh_new_transport_params_prep(ctx, endpt_name, *config, &new_tree, &alg_tree);
469 if (ret) {
470 goto cleanup;
471 }
472
473 if (!*config) {
474 *config = new_tree;
475 }
476
477 va_start(ap, alg_count);
478
479 ret = nc_server_config_ssh_new_transport_params(ctx, NC_ALG_HOSTKEY, alg_count, ap, alg_tree);
480 if (ret) {
481 goto cleanup;
482 }
483
roman4f9bb442023-03-24 09:05:37 +0100484 /* Add all default nodes */
485 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
486 if (ret) {
487 goto cleanup;
488 }
roman27215242023-03-10 14:55:00 +0100489cleanup:
490 return ret;
491}
492
493API int
494nc_server_config_ssh_new_key_exchange_algs(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config,
495 int alg_count, ...)
496{
497 int ret = 0;
498 struct lyd_node *new_tree, *alg_tree;
499 va_list ap;
500
501 ret = nc_server_config_ssh_new_transport_params_prep(ctx, endpt_name, *config, &new_tree, &alg_tree);
502 if (ret) {
503 goto cleanup;
504 }
505
506 if (!*config) {
507 *config = new_tree;
508 }
509
510 va_start(ap, alg_count);
511
512 ret = nc_server_config_ssh_new_transport_params(ctx, NC_ALG_KEY_EXCHANGE, alg_count, ap, alg_tree);
513 if (ret) {
514 goto cleanup;
515 }
516
roman4f9bb442023-03-24 09:05:37 +0100517 /* Add all default nodes */
518 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
519 if (ret) {
520 goto cleanup;
521 }
roman27215242023-03-10 14:55:00 +0100522cleanup:
523 return ret;
524}
525
526API int
527nc_server_config_ssh_new_encryption_algs(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config,
528 int alg_count, ...)
529{
530 int ret = 0;
531 struct lyd_node *new_tree, *alg_tree;
532 va_list ap;
533
534 ret = nc_server_config_ssh_new_transport_params_prep(ctx, endpt_name, *config, &new_tree, &alg_tree);
535 if (ret) {
536 goto cleanup;
537 }
538
539 if (!*config) {
540 *config = new_tree;
541 }
542
543 va_start(ap, alg_count);
544
545 ret = nc_server_config_ssh_new_transport_params(ctx, NC_ALG_ENCRYPTION, alg_count, ap, alg_tree);
546 if (ret) {
547 goto cleanup;
548 }
549
roman4f9bb442023-03-24 09:05:37 +0100550 /* Add all default nodes */
551 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
552 if (ret) {
553 goto cleanup;
554 }
roman27215242023-03-10 14:55:00 +0100555cleanup:
556 return ret;
557}
558
559API int
560nc_server_config_ssh_new_mac_algs(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config,
561 int alg_count, ...)
562{
563 int ret = 0;
564 struct lyd_node *new_tree, *alg_tree;
565 va_list ap;
566
567 ret = nc_server_config_ssh_new_transport_params_prep(ctx, endpt_name, *config, &new_tree, &alg_tree);
568 if (ret) {
569 goto cleanup;
570 }
571
572 if (!*config) {
573 *config = new_tree;
574 }
575
576 va_start(ap, alg_count);
577
578 ret = nc_server_config_ssh_new_transport_params(ctx, NC_ALG_MAC, alg_count, ap, alg_tree);
579 if (ret) {
580 goto cleanup;
581 }
582
roman4f9bb442023-03-24 09:05:37 +0100583 /* Add all default nodes */
584 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
585 if (ret) {
586 goto cleanup;
587 }
roman27215242023-03-10 14:55:00 +0100588cleanup:
589 return ret;
590}
roman4f9bb442023-03-24 09:05:37 +0100591
592static int
593nc_server_config_ssh_read_openssh_pubkey(FILE *f, char **pubkey)
594{
595 int ret = 0;
596 char *buffer = NULL;
597 size_t len = 0;
598 char *start, *end;
599
600 if (getline(&buffer, &len, f) < 0) {
601 ERR(NULL, "Reading line from file failed.");
602 return 1;
603 }
604
605 if (len < 8) {
606 ERR(NULL, "Unexpected public key format.");
607 ret = 1;
608 goto cleanup;
609 }
610
611 start = buffer;
roman6e5fd702023-04-27 14:30:27 +0200612 if (!strncmp(buffer, "ssh-dss ", 8)) {
613 ERR(NULL, "DSA public keys not supported.");
614 ret = 1;
615 goto cleanup;
616 } else if (!strncmp(buffer, "ssh-rsa ", 8)) {
roman4f9bb442023-03-24 09:05:37 +0100617 start += strlen("ssh-rsa ");
roman6e5fd702023-04-27 14:30:27 +0200618 } else if (!strncmp(buffer, "ecdsa-sha2-nistp256 ", 20)) {
619 start += strlen("ecdsa-sha2-nistp256 ");
620 } else if (!strncmp(buffer, "ecdsa-sha2-nistp384 ", 20)) {
621 start += strlen("ecdsa-sha2-nistp384 ");
622 } else if (!strncmp(buffer, "ecdsa-sha2-nistp521 ", 20)) {
623 start += strlen("ecdsa-sha2-nistp521 ");
roman44600f42023-04-28 15:54:27 +0200624 } else if (!strncmp(buffer, "ssh-ed25519 ", 12)) {
625 start += strlen("ssh-ed25519 ");
roman6e5fd702023-04-27 14:30:27 +0200626 } else {
627 ERR(NULL, "Unknown public key type.");
628 ret = 1;
629 goto cleanup;
roman4f9bb442023-03-24 09:05:37 +0100630 }
631
roman6e5fd702023-04-27 14:30:27 +0200632 end = strchr(start, ' ');
633 if (!end) {
634 ERR(NULL, "Unexpected public key format.");
635 ret = 1;
636 goto cleanup;
637 }
638
639 *pubkey = strdup(start);
640 if (!*pubkey) {
641 ERRMEM;
642 ret = 1;
643 goto cleanup;
644 }
645
646 (*pubkey)[strlen(*pubkey) - strlen(end)] = '\0';
647
roman4f9bb442023-03-24 09:05:37 +0100648cleanup:
649 free(buffer);
650 return ret;
651}
652
653static int
654nc_server_config_ssh_read_ssh2_pubkey(FILE *f, char **pubkey)
655{
656 char *buffer = NULL;
657 size_t len = 0;
658 size_t pubkey_len = 0;
659 void *tmp;
660
661 while (getline(&buffer, &len, f) > 0) {
662 if (!strncmp(buffer, "----", 4)) {
663 free(buffer);
664 buffer = NULL;
665 continue;
666 }
667
668 if (!strncmp(buffer, "Comment:", 8)) {
669 free(buffer);
670 buffer = NULL;
671 continue;
672 }
673
674 len = strlen(buffer);
675
676 tmp = realloc(*pubkey, pubkey_len + len + 1);
677 if (!tmp) {
678 ERRMEM;
679 free(buffer);
680 buffer = NULL;
681 return 1;
682 }
683
684 *pubkey = tmp;
685 memcpy(*pubkey + pubkey_len, buffer, len);
686 pubkey_len += len;
687 free(buffer);
688 buffer = NULL;
689 }
690
691 if (!pubkey_len) {
692 ERR(NULL, "Unexpected public key format.");
693 return 1;
694 }
695
696 (*pubkey)[pubkey_len - 1] = '\0';
697 free(buffer);
698 return 0;
699}
700
701static int
702nc_server_config_ssh_read_subject_pubkey(FILE *f, char **pubkey)
703{
704 int ret = 0;
705 EVP_PKEY *pkey;
706 BIO *bio;
707 BUF_MEM *mem;
708 char *tmp;
709
710 pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
711 if (!pkey) {
712 ret = -1;
713 goto cleanup;
714 }
715
716 bio = BIO_new(BIO_s_mem());
717 if (!bio) {
718 ret = -1;
719 goto cleanup;
720 }
721
722 ret = PEM_write_bio_PUBKEY(bio, pkey);
723 if (!ret) {
724 ret = -1;
725 goto cleanup;
726 }
727 ret = 0;
728
729 BIO_get_mem_ptr(bio, &mem);
730 tmp = malloc(mem->length + 1);
731 if (!tmp) {
732 ERRMEM;
733 ret = 1;
734 goto cleanup;
735 }
736
737 memcpy(tmp, mem->data, mem->length);
738 tmp[mem->length] = '\0';
739
740 *pubkey = strdup(tmp + strlen("-----BEGIN PUBLIC KEY-----\n"));
741 (*pubkey)[strlen(*pubkey) - strlen("\n-----END PUBLIC KEY-----\n")] = '\0';
742
743cleanup:
744 if (ret == -1) {
745 ERR(NULL, "Error getting public key from file (OpenSSL Error): \"%s\".", ERR_reason_error_string(ERR_get_error()));
746 ret = 1;
747 }
748
749 BIO_free(bio);
750 EVP_PKEY_free(pkey);
751 free(tmp);
752
753 return ret;
754}
755
756static int
757nc_server_config_ssh_new_get_pubkey(const char *pubkey_path, char **pubkey, NC_SSH_PUBKEY_TYPE *pubkey_type)
758{
759 int ret = 0;
760 FILE *f = NULL;
761 char *buffer = NULL;
762 size_t len = 0;
763
764 *pubkey = NULL;
765
766 f = fopen(pubkey_path, "r");
767 if (!f) {
768 ERR(NULL, "Unable to open file \"%s\".", pubkey_path);
769 ret = 1;
770 goto cleanup;
771 }
772
773 if (getline(&buffer, &len, f) < 0) {
774 ERR(NULL, "Error reading header from file \"%s\".", pubkey_path);
775 ret = 1;
776 goto cleanup;
777 }
778
779 rewind(f);
780
781 if (!strncmp(buffer, "-----BEGIN PUBLIC KEY-----\n", strlen("-----BEGIN PUBLIC KEY-----\n"))) {
782 ret = nc_server_config_ssh_read_subject_pubkey(f, pubkey);
783 *pubkey_type = NC_SSH_PUBKEY_X509;
784 } else if (!strncmp(buffer, "---- BEGIN SSH2 PUBLIC KEY ----\n", strlen("---- BEGIN SSH2 PUBLIC KEY ----\n"))) {
785 ret = nc_server_config_ssh_read_ssh2_pubkey(f, pubkey);
786 *pubkey_type = NC_SSH_PUBKEY_SSH2;
787 } else {
788 ret = nc_server_config_ssh_read_openssh_pubkey(f, pubkey);
789 *pubkey_type = NC_SSH_PUBKEY_SSH2;
790 }
791
792 if (ret) {
793 ERR(NULL, "Error getting public key from file \"%s\".", pubkey_path);
794 goto cleanup;
795 }
796
797cleanup:
798 if (f) {
799 fclose(f);
800 }
801
802 free(buffer);
803
804 return ret;
805}
806
807API int
roman31820092023-03-24 15:26:21 +0100808nc_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 +0100809 const char *user_name, const char *pubkey_name, struct lyd_node **config)
810{
811 int ret = 0;
812 char *pubkey = NULL, *tree_path = NULL;
813 struct lyd_node *new_tree;
814 NC_SSH_PUBKEY_TYPE pubkey_type;
815
816 ret = nc_server_config_ssh_new_get_pubkey(pubkey_path, &pubkey, &pubkey_type);
817 if (ret) {
818 goto cleanup;
819 }
820
821 /* prepare path where leaves will get inserted */
822 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
823 "users/user[name='%s']/public-keys/local-definition/public-key[name='%s']", endpt_name, user_name, pubkey_name);
824 if (!tree_path) {
825 ERRMEM;
826 ret = 1;
827 goto cleanup;
828 }
829
830 /* create all the nodes in the path if they weren't there */
831 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
832 if (ret) {
833 goto cleanup;
834 }
835 if (!*config) {
836 *config = new_tree;
837 }
838
839 /* find the node where leaves will get inserted */
840 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
841 if (ret) {
842 goto cleanup;
843 }
844
845 /* insert pubkey format */
846 if (pubkey_type == NC_SSH_PUBKEY_SSH2) {
847 ret = lyd_new_term(new_tree, NULL, "public-key-format", "ietf-crypto-types:ssh-public-key-format", 0, NULL);
848 } else {
849 ret = lyd_new_term(new_tree, NULL, "public-key-format", "ietf-crypto-types:subject-public-key-info-format", 0, NULL);
850 }
851 if (ret) {
852 goto cleanup;
853 }
854
855 /* insert pubkey b64 */
856 ret = lyd_new_term(new_tree, NULL, "public-key", pubkey, 0, NULL);
857 if (ret) {
858 goto cleanup;
859 }
860
861 /* Add all default nodes */
862 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
863 if (ret) {
864 goto cleanup;
865 }
866
867cleanup:
868 free(tree_path);
869 free(pubkey);
870 return ret;
871}
roman31820092023-03-24 15:26:21 +0100872
873API int
874nc_server_config_ssh_new_client_auth_password(const char *password, const struct ly_ctx *ctx, const char *endpt_name,
875 const char *user_name, struct lyd_node **config)
876{
877 int ret = 0;
878 char *tree_path = NULL, *hashed_pw = NULL;
879 struct lyd_node *new_tree;
880 const char *salt = "$6$idsizuippipk$";
881
882#ifdef HAVE_CRYPT_R
883 struct crypt_data cdata;
884#endif
885
886 /* prepare path where the leaf will get inserted */
887 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
888 "users/user[name='%s']", endpt_name, user_name);
889 if (!tree_path) {
890 ERRMEM;
891 ret = 1;
892 goto cleanup;
893 }
894
895 /* create all the nodes in the path if they weren't there */
896 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
897 if (ret) {
898 goto cleanup;
899 }
900 if (!*config) {
901 *config = new_tree;
902 }
903
904 /* find the node where the leaf will get inserted */
905 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
906 if (ret) {
907 goto cleanup;
908 }
909
910#ifdef HAVE_CRYPT_R
911 cdata.initialized = 0;
912 hashed_pw = crypt_r(password, salt, &data);
913#else
914 pthread_mutex_lock(&crypt_lock);
915 hashed_pw = crypt(password, salt);
916 pthread_mutex_unlock(&crypt_lock);
917#endif
918
919 if (!hashed_pw) {
920 ERR(NULL, "Hashing password failed.");
921 ret = 1;
922 goto cleanup;
923 }
924
925 /* insert SHA-512 hashed password */
926 ret = lyd_new_term(new_tree, NULL, "password", hashed_pw, 0, NULL);
927 if (ret) {
928 goto cleanup;
929 }
930
931 /* Add all default nodes */
932 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
933 if (ret) {
934 goto cleanup;
935 }
936
937cleanup:
938 free(tree_path);
939 return ret;
940}
941
942API int
943nc_server_config_ssh_new_client_auth_none(const struct ly_ctx *ctx, const char *endpt_name,
944 const char *user_name, struct lyd_node **config)
945{
946 int ret = 0;
947 char *tree_path = NULL;
948 struct lyd_node *new_tree;
949
950 /* prepare path where the leaf will get inserted */
951 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
952 "users/user[name='%s']", endpt_name, user_name);
953 if (!tree_path) {
954 ERRMEM;
955 ret = 1;
956 goto cleanup;
957 }
958
959 /* create all the nodes in the path if they weren't there */
960 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
961 if (ret) {
962 goto cleanup;
963 }
964 if (!*config) {
965 *config = new_tree;
966 }
967
968 /* find the node where the leaf will get inserted */
969 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
970 if (ret) {
971 goto cleanup;
972 }
973
974 /* insert none leaf */
975 ret = lyd_new_term(new_tree, NULL, "none", NULL, 0, NULL);
976 if (ret) {
977 goto cleanup;
978 }
979
980 /* Add all default nodes */
981 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
982 if (ret) {
983 goto cleanup;
984 }
985
986cleanup:
987 free(tree_path);
988 return ret;
989}
990
991API int
992nc_server_config_ssh_new_client_auth_interactive(const char *pam_config_name, const char *pam_config_dir,
993 const struct ly_ctx *ctx, const char *endpt_name,
994 const char *user_name, struct lyd_node **config)
995{
996 int ret = 0;
997 char *tree_path = NULL;
998 struct lyd_node *new_tree;
999
1000 /* prepare path where the leaf will get inserted */
1001 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/"
1002 "users/user[name='%s']/libnetconf2-netconf-server:keyboard-interactive", endpt_name, user_name);
1003 if (!tree_path) {
1004 ERRMEM;
1005 ret = 1;
1006 goto cleanup;
1007 }
1008
1009 /* create all the nodes in the path if they weren't there */
1010 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
1011 if (ret) {
1012 goto cleanup;
1013 }
1014 if (!*config) {
1015 *config = new_tree;
1016 }
1017
1018 /* find the node where the leaf will get inserted */
1019 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
1020 if (ret) {
1021 goto cleanup;
1022 }
1023
1024 /* insert file-name leaf */
1025 ret = lyd_new_term(new_tree, NULL, "pam-config-file-name", pam_config_name, 0, NULL);
1026 if (ret) {
1027 goto cleanup;
1028 }
1029
1030 if (pam_config_dir) {
1031 /* insert file-path leaf */
1032 ret = lyd_new_term(new_tree, NULL, "pam-config-file-dir", pam_config_dir, 0, NULL);
1033 if (ret) {
1034 goto cleanup;
1035 }
1036 }
1037
1038 /* Add all default nodes */
1039 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
1040 if (ret) {
1041 goto cleanup;
1042 }
1043
1044cleanup:
1045 free(tree_path);
1046 return ret;
1047}