| /** |
| * @file server_config_ts.c |
| * @author Roman Janota <janota@cesnet.cz> |
| * @brief libnetconf2 truststore configuration functions |
| * |
| * @copyright |
| * Copyright (c) 2023 CESNET, z.s.p.o. |
| * |
| * This source code is licensed under BSD 3-Clause License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * https://opensource.org/licenses/BSD-3-Clause |
| */ |
| |
| #define _GNU_SOURCE |
| |
| #include <assert.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <libyang/libyang.h> |
| |
| #include "compat.h" |
| #include "log_p.h" |
| #include "server_config_p.h" |
| #include "session_p.h" |
| |
| extern struct nc_server_opts server_opts; |
| |
| /** |
| * @brief Get the pointer to a certificate bag structure based on node's location in the YANG data. |
| * |
| * @param[in] node Node from which the certificate bag containing this node is derived. |
| * @param[out] cbag Certificate bag containing the node. |
| * @return 0 on success, 1 on error. |
| */ |
| static int |
| nc_server_config_get_certificate_bag(const struct lyd_node *node, struct nc_certificate_bag **cbag) |
| { |
| uint16_t i; |
| const char *cbag_name; |
| struct nc_truststore *ts; |
| |
| assert(node); |
| |
| while (node) { |
| if (!strcmp(LYD_NAME(node), "certificate-bag")) { |
| break; |
| } |
| node = lyd_parent(node); |
| } |
| |
| if (!node) { |
| ERR(NULL, "Node \"%s\" is not contained in a certificate-bag subtree.", LYD_NAME(node)); |
| return 1; |
| } |
| |
| node = lyd_child(node); |
| assert(!strcmp(LYD_NAME(node), "name")); |
| cbag_name = lyd_get_value(node); |
| |
| ts = &server_opts.truststore; |
| for (i = 0; i < ts->cert_bag_count; i++) { |
| if (!strcmp(ts->cert_bags[i].name, cbag_name)) { |
| *cbag = &ts->cert_bags[i]; |
| return 0; |
| } |
| } |
| |
| ERR(NULL, "Certificate bag \"%s\" was not found.", cbag_name); |
| return 1; |
| } |
| |
| /** |
| * @brief Get the pointer to a certificate structure based on node's location in the YANG data. |
| * |
| * @param[in] node Node from which the certificate containing this node is derived. |
| * @param[in] cbag Certificate bag containing the certificate. |
| * @param[out] cert Certificate containing the node. |
| * @return 0 on success, 1 on error. |
| */ |
| static int |
| nc_server_config_get_certificate(const struct lyd_node *node, const struct nc_certificate_bag *cbag, struct nc_certificate **cert) |
| { |
| uint16_t i; |
| const char *cert_name; |
| |
| assert(node); |
| |
| while (node) { |
| if (!strcmp(LYD_NAME(node), "certificate")) { |
| break; |
| } |
| node = lyd_parent(node); |
| } |
| |
| if (!node) { |
| ERR(NULL, "Node \"%s\" is not contained in a certificate subtree.", LYD_NAME(node)); |
| return 1; |
| } |
| |
| node = lyd_child(node); |
| assert(!strcmp(LYD_NAME(node), "name")); |
| cert_name = lyd_get_value(node); |
| |
| for (i = 0; i < cbag->cert_count; i++) { |
| if (!strcmp(cbag->certs[i].name, cert_name)) { |
| *cert = &cbag->certs[i]; |
| return 0; |
| } |
| } |
| |
| ERR(NULL, "Certificate \"%s\" was not found.", cert_name); |
| return 1; |
| } |
| |
| /** |
| * @brief Get the pointer to a public key bag structure based on node's location in the YANG data. |
| * |
| * @param[in] node Node from which the public key bag containing this node is derived. |
| * @param[out] pbag Public key bag containing the node. |
| * @return 0 on success, 1 on error. |
| */ |
| static int |
| nc_server_config_get_public_key_bag(const struct lyd_node *node, struct nc_public_key_bag **pbag) |
| { |
| uint16_t i; |
| const char *pbag_name; |
| struct nc_truststore *ts; |
| |
| assert(node); |
| |
| while (node) { |
| if (!strcmp(LYD_NAME(node), "public-key-bag")) { |
| break; |
| } |
| node = lyd_parent(node); |
| } |
| |
| if (!node) { |
| ERR(NULL, "Node \"%s\" is not contained in a public-key-bag subtree.", LYD_NAME(node)); |
| return 1; |
| } |
| |
| node = lyd_child(node); |
| assert(!strcmp(LYD_NAME(node), "name")); |
| pbag_name = lyd_get_value(node); |
| |
| ts = &server_opts.truststore; |
| for (i = 0; i < ts->pub_bag_count; i++) { |
| if (!strcmp(ts->pub_bags[i].name, pbag_name)) { |
| *pbag = &ts->pub_bags[i]; |
| return 0; |
| } |
| } |
| |
| ERR(NULL, "Public key bag \"%s\" was not found.", pbag_name); |
| return 1; |
| } |
| |
| /** |
| * @brief Get the pointer to a public key structure based on node's location in the YANG data. |
| * |
| * @param[in] node Node from which the public key containing this node is derived. |
| * @param[in] pbag Public key bag containing the public key. |
| * @param[out] pkey Public key containing the node. |
| * @return 0 on success, 1 on error. |
| */ |
| static int |
| nc_server_config_get_public_key(const struct lyd_node *node, const struct nc_public_key_bag *pbag, struct nc_public_key **pkey) |
| { |
| uint16_t i; |
| const char *pkey_name; |
| |
| assert(node); |
| |
| while (node) { |
| if (!strcmp(LYD_NAME(node), "public-key")) { |
| if (lyd_child(node)) { |
| /* check if it's not the leaf public-key, only case about the list */ |
| break; |
| } |
| } |
| |
| node = lyd_parent(node); |
| } |
| |
| if (!node) { |
| ERR(NULL, "Node \"%s\" is not contained in a public-key subtree.", LYD_NAME(node)); |
| return 1; |
| } |
| |
| node = lyd_child(node); |
| assert(!strcmp(LYD_NAME(node), "name")); |
| pkey_name = lyd_get_value(node); |
| |
| for (i = 0; i < pbag->pubkey_count; i++) { |
| if (!strcmp(pbag->pubkeys[i].name, pkey_name)) { |
| *pkey = &pbag->pubkeys[i]; |
| return 0; |
| } |
| } |
| |
| ERR(NULL, "Public key \"%s\" was not found.", pkey_name); |
| return 1; |
| } |
| |
| static void |
| nc_server_config_ts_del_cert_data(struct nc_certificate *cert) |
| { |
| free(cert->data); |
| cert->data = NULL; |
| } |
| |
| static void |
| nc_server_config_ts_del_public_key_base64(struct nc_public_key *pkey) |
| { |
| free(pkey->data); |
| pkey->data = NULL; |
| } |
| |
| static void |
| nc_server_config_ts_del_certificate(struct nc_certificate_bag *cbag, struct nc_certificate *cert) |
| { |
| free(cert->name); |
| cert->name = NULL; |
| |
| nc_server_config_ts_del_cert_data(cert); |
| |
| cbag->cert_count--; |
| if (cbag->cert_count == 0) { |
| free(cbag->certs); |
| cbag->certs = NULL; |
| } |
| } |
| |
| static void |
| nc_server_config_ts_del_public_key(struct nc_public_key_bag *pbag, struct nc_public_key *pkey) |
| { |
| free(pkey->name); |
| pkey->name = NULL; |
| |
| nc_server_config_ts_del_public_key_base64(pkey); |
| |
| pbag->pubkey_count--; |
| if (pbag->pubkey_count == 0) { |
| free(pbag->pubkeys); |
| pbag->pubkeys = NULL; |
| } |
| } |
| |
| static void |
| nc_server_config_ts_del_certificate_bag(struct nc_certificate_bag *cbag) |
| { |
| uint16_t i, cert_count; |
| struct nc_truststore *ts = &server_opts.truststore; |
| |
| free(cbag->name); |
| cbag->name = NULL; |
| |
| cert_count = cbag->cert_count; |
| for (i = 0; i < cert_count; i++) { |
| nc_server_config_ts_del_certificate(cbag, &cbag->certs[i]); |
| } |
| |
| ts->cert_bag_count--; |
| if (ts->cert_bag_count == 0) { |
| free(ts->cert_bags); |
| ts->cert_bags = NULL; |
| } |
| } |
| |
| static void |
| nc_server_config_ts_del_public_key_bag(struct nc_public_key_bag *pbag) |
| { |
| uint16_t i, pubkey_count; |
| struct nc_truststore *ts = &server_opts.truststore; |
| |
| free(pbag->name); |
| pbag->name = NULL; |
| |
| pubkey_count = pbag->pubkey_count; |
| for (i = 0; i < pubkey_count; i++) { |
| nc_server_config_ts_del_public_key(pbag, &pbag->pubkeys[i]); |
| } |
| |
| ts->pub_bag_count--; |
| if (ts->pub_bag_count == 0) { |
| free(ts->pub_bags); |
| ts->pub_bags = NULL; |
| } |
| } |
| |
| static int |
| nc_server_config_ts_certificate_bags(const struct lyd_node *node, NC_OPERATION op) |
| { |
| uint16_t i, cert_bag_count; |
| struct nc_truststore *ts = &server_opts.truststore; |
| |
| (void) node; |
| |
| if (op == NC_OP_DELETE) { |
| cert_bag_count = ts->cert_bag_count; |
| for (i = 0; i < cert_bag_count; i++) { |
| nc_server_config_ts_del_certificate_bag(&ts->cert_bags[i]); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int |
| nc_server_config_ts_public_key_bags(const struct lyd_node *node, NC_OPERATION op) |
| { |
| uint16_t i, pub_bag_count; |
| struct nc_truststore *ts = &server_opts.truststore; |
| |
| (void) node; |
| |
| if (op == NC_OP_DELETE) { |
| pub_bag_count = ts->pub_bag_count; |
| for (i = 0; i < pub_bag_count; i++) { |
| nc_server_config_ts_del_public_key_bag(&ts->pub_bags[i]); |
| } |
| } |
| |
| return 0; |
| } |
| |
| int |
| nc_server_config_ts_truststore(const struct lyd_node *node, NC_OPERATION op) |
| { |
| (void) node; |
| |
| if (op == NC_OP_DELETE) { |
| nc_server_config_ts_certificate_bags(NULL, NC_OP_DELETE); |
| nc_server_config_ts_public_key_bags(NULL, NC_OP_DELETE); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| nc_server_config_ts_create_certificate_bag(const struct lyd_node *node) |
| { |
| struct nc_truststore *ts = &server_opts.truststore; |
| |
| node = lyd_child(node); |
| assert(!strcmp(LYD_NAME(node), "name")); |
| |
| return nc_server_config_realloc(lyd_get_value(node), (void **)&ts->cert_bags, sizeof *ts->cert_bags, &ts->cert_bag_count); |
| } |
| |
| static int |
| nc_server_config_ts_certificate_bag(const struct lyd_node *node, NC_OPERATION op) |
| { |
| struct nc_certificate_bag *bag; |
| |
| assert(!strcmp(LYD_NAME(node), "certificate-bag")); |
| |
| if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { |
| if (nc_server_config_ts_create_certificate_bag(node)) { |
| return 1; |
| } |
| } else { |
| if (nc_server_config_get_certificate_bag(node, &bag)) { |
| return 1; |
| } |
| |
| nc_server_config_ts_del_certificate_bag(bag); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| nc_server_config_ts_create_certificate(const struct lyd_node *node, struct nc_certificate_bag *bag) |
| { |
| node = lyd_child(node); |
| assert(!strcmp(LYD_NAME(node), "name")); |
| |
| return nc_server_config_realloc(lyd_get_value(node), (void **)&bag->certs, sizeof *bag->certs, &bag->cert_count); |
| } |
| |
| static int |
| nc_server_config_ts_certificate(const struct lyd_node *node, NC_OPERATION op) |
| { |
| struct nc_certificate_bag *bag; |
| struct nc_certificate *cert; |
| |
| assert(!strcmp(LYD_NAME(node), "certificate")); |
| |
| if (nc_server_config_get_certificate_bag(node, &bag)) { |
| return 1; |
| } |
| |
| if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { |
| if (nc_server_config_ts_create_certificate(node, bag)) { |
| return 1; |
| } |
| } else { |
| if (nc_server_config_get_certificate(node, bag, &cert)) { |
| return 1; |
| } |
| |
| nc_server_config_ts_del_certificate(bag, cert); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| nc_server_config_ts_cert_data(const struct lyd_node *node, NC_OPERATION op) |
| { |
| struct nc_certificate_bag *bag; |
| struct nc_certificate *cert; |
| |
| if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { |
| if (nc_server_config_get_certificate_bag(node, &bag)) { |
| return 1; |
| } |
| if (nc_server_config_get_certificate(node, bag, &cert)) { |
| return 1; |
| } |
| |
| nc_server_config_ts_del_cert_data(cert); |
| cert->data = strdup(lyd_get_value(node)); |
| if (!cert->data) { |
| ERRMEM; |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int |
| nc_server_config_ts_create_public_key_bag(const struct lyd_node *node) |
| { |
| struct nc_truststore *ts = &server_opts.truststore; |
| |
| node = lyd_child(node); |
| assert(!strcmp(LYD_NAME(node), "name")); |
| |
| return nc_server_config_realloc(lyd_get_value(node), (void **)&ts->pub_bags, sizeof *ts->pub_bags, &ts->pub_bag_count); |
| } |
| |
| static int |
| nc_server_config_ts_public_key_bag(const struct lyd_node *node, NC_OPERATION op) |
| { |
| struct nc_public_key_bag *pbag; |
| |
| assert(!strcmp(LYD_NAME(node), "public-key-bag")); |
| |
| if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { |
| if (nc_server_config_ts_create_public_key_bag(node)) { |
| return 1; |
| } |
| } else { |
| if (nc_server_config_get_public_key_bag(node, &pbag)) { |
| return 1; |
| } |
| |
| nc_server_config_ts_del_public_key_bag(pbag); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| nc_server_config_ts_create_public_key(const struct lyd_node *node, struct nc_public_key_bag *bag) |
| { |
| node = lyd_child(node); |
| assert(!strcmp(LYD_NAME(node), "name")); |
| |
| return nc_server_config_realloc(lyd_get_value(node), (void **)&bag->pubkeys, sizeof *bag->pubkeys, &bag->pubkey_count); |
| } |
| |
| static int |
| nc_server_config_ts_public_key(const struct lyd_node *node, NC_OPERATION op) |
| { |
| int ret = 0; |
| struct nc_public_key_bag *bag; |
| struct nc_public_key *pkey; |
| |
| if (nc_server_config_get_public_key_bag(node, &bag)) { |
| ret = 1; |
| goto cleanup; |
| } |
| |
| if (equal_parent_name(node, 1, "public-key-bag")) { |
| /* public-key list */ |
| if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { |
| ret = nc_server_config_ts_create_public_key(node, bag); |
| if (ret) { |
| goto cleanup; |
| } |
| } else { |
| if (nc_server_config_get_public_key(node, bag, &pkey)) { |
| ret = 1; |
| goto cleanup; |
| } |
| |
| nc_server_config_ts_del_public_key(bag, pkey); |
| } |
| } else { |
| /* public-key leaf */ |
| if (nc_server_config_get_public_key(node, bag, &pkey)) { |
| ret = 1; |
| goto cleanup; |
| } |
| |
| /* replace the public key */ |
| nc_server_config_ts_del_public_key_base64(pkey); |
| pkey->data = strdup(lyd_get_value(node)); |
| if (!pkey->data) { |
| ERRMEM; |
| ret = 1; |
| goto cleanup; |
| } |
| } |
| |
| cleanup: |
| return ret; |
| } |
| |
| static int |
| nc_server_config_ts_public_key_format(const struct lyd_node *node, NC_OPERATION op) |
| { |
| const char *format; |
| struct nc_public_key_bag *bag; |
| struct nc_public_key *pkey; |
| |
| (void) op; |
| |
| if (nc_server_config_get_public_key_bag(node, &bag)) { |
| return 1; |
| } |
| |
| if (nc_server_config_get_public_key(node, bag, &pkey)) { |
| return 1; |
| } |
| |
| format = ((struct lyd_node_term *)node)->value.ident->name; |
| if (!strcmp(format, "ssh-public-key-format")) { |
| pkey->type = NC_PUBKEY_FORMAT_SSH2; |
| } else if (!strcmp(format, "subject-public-key-info-format")) { |
| pkey->type = NC_PUBKEY_FORMAT_X509; |
| } else { |
| ERR(NULL, "Public key format (%s) not supported.", format); |
| } |
| |
| return 0; |
| } |
| |
| int |
| nc_server_config_parse_truststore(const struct lyd_node *node, NC_OPERATION op) |
| { |
| const char *name = LYD_NAME(node); |
| |
| if (!strcmp(name, "truststore")) { |
| if (nc_server_config_ts_truststore(node, op)) { |
| goto error; |
| } |
| } else if (!strcmp(name, "certificate-bags")) { |
| if (nc_server_config_ts_certificate_bags(node, op)) { |
| goto error; |
| } |
| } else if (!strcmp(name, "certificate-bag")) { |
| if (nc_server_config_ts_certificate_bag(node, op)) { |
| goto error; |
| } |
| } else if (!strcmp(name, "certificate")) { |
| if (nc_server_config_ts_certificate(node, op)) { |
| goto error; |
| } |
| } else if (!strcmp(name, "cert-data")) { |
| if (nc_server_config_ts_cert_data(node, op)) { |
| goto error; |
| } |
| } else if (!strcmp(name, "public-key-bags")) { |
| if (nc_server_config_ts_public_key_bags(node, op)) { |
| goto error; |
| } |
| } else if (!strcmp(name, "public-key-bag")) { |
| if (nc_server_config_ts_public_key_bag(node, op)) { |
| goto error; |
| } |
| } else if (!strcmp(name, "public-key")) { |
| if (nc_server_config_ts_public_key(node, op)) { |
| goto error; |
| } |
| } else if (!strcmp(name, "public-key-format")) { |
| if (nc_server_config_ts_public_key_format(node, op)) { |
| goto error; |
| } |
| } |
| |
| return 0; |
| |
| error: |
| ERR(NULL, "Configuring (%s) failed.", name); |
| return 1; |
| } |
| |
| int |
| nc_server_config_fill_truststore(const struct lyd_node *data, NC_OPERATION op) |
| { |
| int ret = 0; |
| uint32_t prev_lo; |
| struct lyd_node *tree; |
| |
| /* silently search for nodes, some of them may not be present */ |
| prev_lo = ly_log_options(0); |
| |
| ret = lyd_find_path(data, "/ietf-truststore:truststore", 0, &tree); |
| if (ret || (tree->flags & LYD_DEFAULT)) { |
| VRB(NULL, "Truststore container not found in the YANG data."); |
| ret = 0; |
| goto cleanup; |
| } |
| |
| if (nc_server_config_parse_tree(tree, op, NC_MODULE_TRUSTSTORE)) { |
| ret = 1; |
| goto cleanup; |
| } |
| |
| cleanup: |
| /* reset the logging options back to what they were */ |
| ly_log_options(prev_lo); |
| return ret; |
| } |