blob: eb05ba9523cc210b2ca1b14e9fa0551387fc151c [file] [log] [blame]
roman3f9b65c2023-06-05 14:26:58 +02001/**
2 * @file config_new_tls.c
3 * @author Roman Janota <janota@cesnet.cz>
4 * @brief libnetconf2 TLS 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
roman12644fe2023-06-08 11:06:42 +020018#include <stdarg.h>
roman3f9b65c2023-06-05 14:26:58 +020019#include <stdint.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include <libyang/libyang.h>
25
26#include "compat.h"
27#include "config.h"
28#include "config_new.h"
29#include "log_p.h"
30#include "server_config.h"
31#include "session.h"
32#include "session_p.h"
33
34API int
roman3f9b65c2023-06-05 14:26:58 +020035nc_server_config_new_tls_server_certificate(const struct ly_ctx *ctx, const char *endpt_name, const char *pubkey_path,
36 const char *privkey_path, const char *certificate_path, struct lyd_node **config)
37{
38 int ret = 0;
39 char *tree_path = NULL, *privkey = NULL, *pubkey = NULL, *pubkey_stripped = NULL, *privkey_stripped, *cert = NULL;
40 struct lyd_node *new_tree;
41 NC_PRIVKEY_FORMAT privkey_type;
42 NC_PUBKEY_FORMAT pubkey_type;
43 const char *privkey_identity;
44
45 NC_CHECK_ARG_RET(NULL, ctx, endpt_name, privkey_path, certificate_path, 1);
46 NC_CHECK_ARG_RET(NULL, config, 1);
47
48 /* get the keys as a string from the given files */
49 ret = nc_server_config_new_get_keys(privkey_path, pubkey_path, &privkey, &pubkey, &privkey_type, &pubkey_type);
50 if (ret) {
51 ERR(NULL, "Getting keys from file(s) failed.");
52 goto cleanup;
53 }
54
55 ret = nc_server_config_new_read_certificate(certificate_path, &cert);
56 if (ret) {
57 ERR(NULL, "Getting certificate from file \"%s\" failed.", certificate_path);
58 goto cleanup;
59 }
60
61 /* prepare path for instertion of leaves later */
roman7fdc84d2023-06-06 13:14:53 +020062 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/server-identity/certificate/inline-definition", endpt_name);
roman3f9b65c2023-06-05 14:26:58 +020063 if (!tree_path) {
64 ERRMEM;
65 ret = 1;
66 goto cleanup;
67 }
68
69 /* create all the nodes in the path */
70 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
71 if (ret) {
72 goto cleanup;
73 }
74 if (!*config) {
75 *config = new_tree;
76 }
77
78 if (!new_tree) {
79 /* no new nodes were created */
80 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
81 } else {
82 /* config was NULL */
83 ret = lyd_find_path(new_tree, tree_path, 0, &new_tree);
84 }
85 if (ret) {
roman7fdc84d2023-06-06 13:14:53 +020086 ERR(NULL, "Unable to find inline-definition container.");
roman3f9b65c2023-06-05 14:26:58 +020087 goto cleanup;
88 }
89
90 /* insert pubkey format */
91 if (pubkey_type == NC_PUBKEY_FORMAT_X509) {
92 ret = lyd_new_term(new_tree, NULL, "public-key-format", "ietf-crypto-types:public-key-info-format", 0, NULL);
93 } else {
94 ret = lyd_new_term(new_tree, NULL, "public-key-format", "ietf-crypto-types:ssh-public-key-format", 0, NULL);
95 }
96 if (ret) {
97 goto cleanup;
98 }
99
100 /* strip pubkey's header and footer only if it's generated from pkcs8 key (using OpenSSL),
101 * otherwise it's already stripped
102 */
103 if (!pubkey_path && (privkey_type == NC_PRIVKEY_FORMAT_X509)) {
104 pubkey_stripped = pubkey + strlen("-----BEGIN PUBLIC KEY-----") + 1;
105 pubkey_stripped[strlen(pubkey_stripped) - strlen("-----END PUBLIC KEY-----") - 2] = '\0';
106 } else {
107 pubkey_stripped = pubkey;
108 }
109
110 /* insert pubkey b64 */
111 ret = lyd_new_term(new_tree, NULL, "public-key", pubkey_stripped, 0, NULL);
112 if (ret) {
113 goto cleanup;
114 }
115
116 /* get privkey identityref value */
117 privkey_identity = nc_config_new_privkey_format_to_identityref(privkey_type);
118 if (!privkey_identity) {
119 ret = 1;
120 goto cleanup;
121 }
122
123 /* insert private key format */
124 ret = lyd_new_term(new_tree, NULL, "private-key-format", privkey_identity, 0, NULL);
125 if (ret) {
126 goto cleanup;
127 }
128
129 if (privkey_type == NC_PRIVKEY_FORMAT_OPENSSH) {
130 /* only OpenSSH private keys have different header and footer after processing */
131 privkey_stripped = privkey + strlen(NC_OPENSSH_PRIVKEY_HEADER);
132 privkey_stripped[strlen(privkey_stripped) - strlen(NC_OPENSSH_PRIVKEY_FOOTER)] = '\0';
133 } else {
134 /* the rest share the same header and footer */
135 privkey_stripped = privkey + strlen(NC_PKCS8_PRIVKEY_HEADER);
136 privkey_stripped[strlen(privkey_stripped) - strlen(NC_PKCS8_PRIVKEY_FOOTER)] = '\0';
137 }
138
139 ret = lyd_new_term(new_tree, NULL, "cleartext-private-key", privkey_stripped, 0, NULL);
140 if (ret) {
141 goto cleanup;
142 }
143
144 ret = lyd_new_term(new_tree, NULL, "cert-data", cert, 0, NULL);
145 if (ret) {
146 goto cleanup;
147 }
148
149 /* check if top-level container has operation and if not, add it */
150 ret = nc_config_new_check_add_operation(ctx, *config);
151 if (ret) {
152 goto cleanup;
153 }
154
155 /* Add all default nodes */
156 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
157 if (ret) {
158 goto cleanup;
159 }
160
161cleanup:
162 free(privkey);
163 free(pubkey);
164 free(cert);
165 free(tree_path);
166 return ret;
167}
168
169API int
170nc_server_config_new_tls_client_certificate(const struct ly_ctx *ctx, const char *endpt_name, const char *cert_name,
171 const char *cert_path, struct lyd_node **config)
172{
173 int ret = 0;
174 struct lyd_node *new_tree;
175 char *tree_path = NULL, *cert = NULL;
176
177 NC_CHECK_ARG_RET(NULL, ctx, endpt_name, cert_name, cert_path, config, 1);
178
179 ret = nc_server_config_new_read_certificate(cert_path, &cert);
180 if (ret) {
181 ERR(NULL, "Getting certificate from file \"%s\" failed.", cert_path);
182 goto cleanup;
183 }
184
185 /* prepare path for instertion of leaves later */
186 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/"
roman7fdc84d2023-06-06 13:14:53 +0200187 "client-authentication/ee-certs/inline-definition/certificate[name='%s']", endpt_name, cert_name);
roman3f9b65c2023-06-05 14:26:58 +0200188 if (!tree_path) {
189 ERRMEM;
190 ret = 1;
191 goto cleanup;
192 }
193
194 /* create all the nodes in the path */
195 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
196 if (ret) {
197 goto cleanup;
198 }
199 if (!*config) {
200 *config = new_tree;
201 }
202
203 if (!new_tree) {
204 /* no new nodes were created */
205 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
206 } else {
207 /* config was NULL */
208 ret = lyd_find_path(new_tree, tree_path, 0, &new_tree);
209 }
210 if (ret) {
211 goto cleanup;
212 }
213
214 /* insert cert-data */
215 ret = lyd_new_term(new_tree, NULL, "cert-data", cert, 0, NULL);
216 if (ret) {
217 goto cleanup;
218 }
219
220 /* check if top-level container has operation and if not, add it */
221 ret = nc_config_new_check_add_operation(ctx, *config);
222 if (ret) {
223 goto cleanup;
224 }
225
226 /* Add all default nodes */
227 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
228 if (ret) {
229 goto cleanup;
230 }
231
232cleanup:
233 free(cert);
234 free(tree_path);
235 return ret;
236}
237
238API int
239nc_server_config_new_tls_client_ca(const struct ly_ctx *ctx, const char *endpt_name, const char *cert_name,
240 const char *cert_path, struct lyd_node **config)
241{
242 int ret = 0;
243 struct lyd_node *new_tree;
244 char *tree_path = NULL, *cert = NULL;
245
246 NC_CHECK_ARG_RET(NULL, ctx, endpt_name, cert_name, cert_path, config, 1);
247
248 ret = nc_server_config_new_read_certificate(cert_path, &cert);
249 if (ret) {
250 ERR(NULL, "Getting certificate from file \"%s\" failed.", cert_path);
251 goto cleanup;
252 }
253
254 /* prepare path for instertion of leaves later */
255 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/"
roman7fdc84d2023-06-06 13:14:53 +0200256 "client-authentication/ca-certs/inline-definition/certificate[name='%s']", endpt_name, cert_name);
roman3f9b65c2023-06-05 14:26:58 +0200257 if (!tree_path) {
258 ERRMEM;
259 ret = 1;
260 goto cleanup;
261 }
262
263 /* create all the nodes in the path */
264 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
265 if (ret) {
266 goto cleanup;
267 }
268 if (!*config) {
269 *config = new_tree;
270 }
271
272 if (!new_tree) {
273 /* no new nodes were created */
274 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
275 } else {
276 /* config was NULL */
277 ret = lyd_find_path(new_tree, tree_path, 0, &new_tree);
278 }
279 if (ret) {
280 goto cleanup;
281 }
282
283 /* insert cert-data */
284 ret = lyd_new_term(new_tree, NULL, "cert-data", cert, 0, NULL);
285 if (ret) {
286 goto cleanup;
287 }
288
289 /* check if top-level container has operation and if not, add it */
290 ret = nc_config_new_check_add_operation(ctx, *config);
291 if (ret) {
292 goto cleanup;
293 }
294
295 /* Add all default nodes */
296 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
297 if (ret) {
298 goto cleanup;
299 }
300
301cleanup:
302 free(cert);
303 free(tree_path);
304 return ret;
305}
roman12644fe2023-06-08 11:06:42 +0200306
307API int
308nc_server_config_new_tls_ctn(const struct ly_ctx *ctx, const char *endpt_name, uint32_t id, const char *fingerprint,
309 NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config)
310{
311 int ret = 0;
312 char *tree_path = NULL;
313 struct lyd_node *new_tree;
314
315 NC_CHECK_ARG_RET(NULL, ctx, endpt_name, id, map_type, name, 1);
316 NC_CHECK_ARG_RET(NULL, config, 1);
317
318 /* prepare path for instertion of leaves later */
319 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/"
320 "netconf-server-parameters/client-identity-mappings/cert-to-name[id='%d']", endpt_name, id);
321 if (!tree_path) {
322 ERRMEM;
323 ret = 1;
324 goto cleanup;
325 }
326
327 /* create all the nodes in the path */
328 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
329 if (ret) {
330 goto cleanup;
331 }
332 if (!*config) {
333 *config = new_tree;
334 }
335
336 if (!new_tree) {
337 /* no new nodes were created */
338 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
339 } else {
340 /* config was NULL */
341 ret = lyd_find_path(new_tree, tree_path, 0, &new_tree);
342 }
343 if (ret) {
344 ERR(NULL, "Unable to find netconf-server-parameters container.");
345 goto cleanup;
346 }
347
348 /* not mandatory */
349 if (fingerprint) {
350 ret = lyd_new_term(new_tree, NULL, "fingerprint", fingerprint, 0, NULL);
351 if (ret) {
352 goto cleanup;
353 }
354 }
355
356 /* insert map-type */
357 switch (map_type) {
358 case NC_TLS_CTN_SPECIFIED:
359 ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:specified", 0, NULL);
360 break;
361 case NC_TLS_CTN_SAN_RFC822_NAME:
362 ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:san-rfc822-name", 0, NULL);
363 break;
364 case NC_TLS_CTN_SAN_DNS_NAME:
365 ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:san-dns-name", 0, NULL);
366 break;
367 case NC_TLS_CTN_SAN_IP_ADDRESS:
368 ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:san-ip-address", 0, NULL);
369 break;
370 case NC_TLS_CTN_SAN_ANY:
371 ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:san-any", 0, NULL);
372 break;
373 case NC_TLS_CTN_COMMON_NAME:
374 ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:common-name", 0, NULL);
375 break;
376 case NC_TLS_CTN_UNKNOWN:
377 default:
378 ERR(NULL, "Unknown map_type.");
379 ret = 1;
380 break;
381 }
382 if (ret) {
383 goto cleanup;
384 }
385
386 /* insert name */
387 ret = lyd_new_term(new_tree, NULL, "name", name, 0, NULL);
388 if (ret) {
389 goto cleanup;
390 }
391
392 /* check if top-level container has operation and if not, add it */
393 ret = nc_config_new_check_add_operation(ctx, *config);
394 if (ret) {
395 goto cleanup;
396 }
397
398 /* Add all default nodes */
399 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
400 if (ret) {
401 goto cleanup;
402 }
403
404cleanup:
405 free(tree_path);
406 return ret;
407}
408
409API int
410nc_server_config_new_tls_version(const struct ly_ctx *ctx, const char *endpt_name,
411 NC_TLS_VERSION tls_version, struct lyd_node **config)
412{
413 int ret = 0;
414 struct lyd_node *new_tree;
415 char *tree_path = NULL;
416
417 NC_CHECK_ARG_RET(NULL, ctx, endpt_name, config, 1);
418
419 /* prepare path for instertion of leaves later */
420 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/"
421 "hello-params/tls-versions", endpt_name);
422 if (!tree_path) {
423 ERRMEM;
424 ret = 1;
425 goto cleanup;
426 }
427
428 /* create all the nodes in the path */
429 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
430 if (ret) {
431 goto cleanup;
432 }
433 if (!*config) {
434 *config = new_tree;
435 }
436
437 if (!new_tree) {
438 /* no new nodes were created */
439 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
440 } else {
441 /* config was NULL */
442 ret = lyd_find_path(new_tree, tree_path, 0, &new_tree);
443 }
444 if (ret) {
445 goto cleanup;
446 }
447
448 switch (tls_version) {
449 case NC_TLS_VERSION_10:
450 ret = lyd_new_term(new_tree, NULL, "tls-version", "ietf-tls-common:tls10", 0, NULL);
451 break;
452 case NC_TLS_VERSION_11:
453 ret = lyd_new_term(new_tree, NULL, "tls-version", "ietf-tls-common:tls11", 0, NULL);
454 break;
455 case NC_TLS_VERSION_12:
456 ret = lyd_new_term(new_tree, NULL, "tls-version", "ietf-tls-common:tls12", 0, NULL);
457 break;
458 case NC_TLS_VERSION_13:
459 ret = lyd_new_term(new_tree, NULL, "tls-version", "ietf-tls-common:tls13", 0, NULL);
460 break;
461 default:
462 ERR(NULL, "Unknown TLS version.");
463 ret = 1;
464 break;
465 }
466 if (ret) {
467 ERR(NULL, "Creating new tls-version node failed.");
468 goto cleanup;
469 }
470
471 /* check if top-level container has operation and if not, add it */
472 ret = nc_config_new_check_add_operation(ctx, *config);
473 if (ret) {
474 goto cleanup;
475 }
476
477 /* Add all default nodes */
478 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
479 if (ret) {
480 goto cleanup;
481 }
482
483cleanup:
484 free(tree_path);
485 return ret;
486}
487
488API int
489nc_server_config_new_tls_ciphers(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config,
roman08f67f42023-06-08 13:51:54 +0200490 int cipher_count, ...)
roman12644fe2023-06-08 11:06:42 +0200491{
492 int ret = 0;
493 struct lyd_node *new_tree = NULL, *old = NULL;
494 va_list ap;
495 char *tree_path = NULL, *cipher = NULL, *cipher_ident = NULL;
roman08f67f42023-06-08 13:51:54 +0200496 int i;
roman12644fe2023-06-08 11:06:42 +0200497
498 NC_CHECK_ARG_RET(NULL, ctx, endpt_name, config, 1);
499
500 /* prepare path */
501 asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/"
502 "tls/tls-server-parameters/hello-params", endpt_name);
503 if (!tree_path) {
504 ERRMEM;
505 ret = 1;
506 goto cleanup;
507 }
508
509 /* create all the nodes in the path */
510 ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
511 if (ret) {
512 goto cleanup;
513 }
514 if (!*config) {
515 *config = new_tree;
516 }
517
518 if (!new_tree) {
519 /* no new nodes were created */
520 ret = lyd_find_path(*config, tree_path, 0, &new_tree);
521 } else {
522 /* config was NULL */
523 ret = lyd_find_path(new_tree, tree_path, 0, &new_tree);
524 }
525 if (ret) {
526 goto cleanup;
527 }
528
529 /* delete all older algorithms (if any) se they can be replaced by the new ones */
530 lyd_find_path(new_tree, "cipher-suites", 0, &old);
531 if (old) {
532 lyd_free_tree(old);
533 }
534
535 va_start(ap, cipher_count);
536 for (i = 0; i < cipher_count; i++) {
537 cipher = va_arg(ap, char *);
538
539 asprintf(&cipher_ident, "iana-tls-cipher-suite-algs:%s", cipher);
540 if (!cipher_ident) {
541 ERRMEM;
542 ret = 1;
543 goto cleanup;
544 }
545
546 /* create the leaf list */
547 ret = lyd_new_path(new_tree, ctx, "cipher-suites/cipher-suite", cipher_ident, 0, NULL);
548 free(cipher_ident);
549
550 if (ret) {
551 ERR(NULL, "Creating new cipher-suites leaf-list failed.");
552 goto cleanup;
553 }
554 }
555
556 /* check if top-level container has operation and if not, add it */
557 ret = nc_config_new_check_add_operation(ctx, *config);
558 if (ret) {
559 goto cleanup;
560 }
561
562 /* Add all default nodes */
563 ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
564 if (ret) {
565 goto cleanup;
566 }
567
568cleanup:
569 va_end(ap);
570 free(tree_path);
571 return ret;
572}