config UPDATE implemented CRL for TLS
Certificate Revocation List now supported, this means a new dependency -
libcurl.
diff --git a/src/config_new_tls.c b/src/config_new_tls.c
index eb05ba9..2a2036c 100644
--- a/src/config_new_tls.c
+++ b/src/config_new_tls.c
@@ -570,3 +570,231 @@
free(tree_path);
return ret;
}
+
+API int
+nc_server_config_new_tls_crl_path(const struct ly_ctx *ctx, const char *endpt_name, const char *path, struct lyd_node **config)
+{
+ int ret = 0;
+ struct lyd_node *new_tree, *node = NULL;
+ char *tree_path = NULL;
+ struct lys_module *mod;
+
+ NC_CHECK_ARG_RET(NULL, ctx, endpt_name, path, config, 1);
+
+ /* prepare path for instertion of leaves later */
+ asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/"
+ "client-authentication", endpt_name);
+ if (!tree_path) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* create all the nodes in the path */
+ ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
+ if (ret) {
+ goto cleanup;
+ }
+ if (!*config) {
+ *config = new_tree;
+ }
+
+ if (!new_tree) {
+ /* no new nodes were created */
+ ret = lyd_find_path(*config, tree_path, 0, &new_tree);
+ } else {
+ /* config was NULL */
+ ret = lyd_find_path(new_tree, tree_path, 0, &new_tree);
+ }
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* delete other choice nodes if they are present */
+ lyd_find_path(new_tree, "libnetconf2-netconf-server:crl-url", 0, &node);
+ lyd_free_tree(node);
+ lyd_find_path(new_tree, "libnetconf2-netconf-server:crl-cert-ext", 0, &node);
+ lyd_free_tree(node);
+
+ /* get the wanted module, because parent of the inserted node has a different one */
+ mod = ly_ctx_get_module_implemented(ctx, "libnetconf2-netconf-server");
+ if (!mod) {
+ ERR(NULL, "Error getting libnetconf2-netconf-server module.");
+ ret = 1;
+ goto cleanup;
+ }
+
+ ret = lyd_new_term(new_tree, mod, "crl-path", path, 0, NULL);
+ if (ret) {
+ ERR(NULL, "Creating new Certificate Revocation List node failed.");
+ goto cleanup;
+ }
+
+ /* check if top-level container has operation and if not, add it */
+ ret = nc_config_new_check_add_operation(ctx, *config);
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* Add all default nodes */
+ ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
+ if (ret) {
+ goto cleanup;
+ }
+
+cleanup:
+ free(tree_path);
+ return ret;
+}
+
+API int
+nc_server_config_new_tls_crl_url(const struct ly_ctx *ctx, const char *endpt_name, const char *url, struct lyd_node **config)
+{
+ int ret = 0;
+ struct lyd_node *new_tree, *node = NULL;
+ char *tree_path = NULL;
+ struct lys_module *mod;
+
+ NC_CHECK_ARG_RET(NULL, ctx, endpt_name, url, config, 1);
+
+ /* prepare path for instertion of leaves later */
+ asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/"
+ "client-authentication", endpt_name);
+ if (!tree_path) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* create all the nodes in the path */
+ ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
+ if (ret) {
+ goto cleanup;
+ }
+ if (!*config) {
+ *config = new_tree;
+ }
+
+ if (!new_tree) {
+ /* no new nodes were created */
+ ret = lyd_find_path(*config, tree_path, 0, &new_tree);
+ } else {
+ /* config was NULL */
+ ret = lyd_find_path(new_tree, tree_path, 0, &new_tree);
+ }
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* delete other choice nodes if they are present */
+ lyd_find_path(new_tree, "libnetconf2-netconf-server:crl-path", 0, &node);
+ lyd_free_tree(node);
+ lyd_find_path(new_tree, "libnetconf2-netconf-server:crl-cert-ext", 0, &node);
+ lyd_free_tree(node);
+
+ /* get the wanted module, because parent of the inserted node has a different one */
+ mod = ly_ctx_get_module_implemented(ctx, "libnetconf2-netconf-server");
+ if (!mod) {
+ ERR(NULL, "Error getting libnetconf2-netconf-server module.");
+ ret = 1;
+ goto cleanup;
+ }
+
+ ret = lyd_new_term(new_tree, mod, "crl-url", url, 0, NULL);
+ if (ret) {
+ ERR(NULL, "Creating new Certificate Revocation List node failed.");
+ goto cleanup;
+ }
+
+ /* check if top-level container has operation and if not, add it */
+ ret = nc_config_new_check_add_operation(ctx, *config);
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* Add all default nodes */
+ ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
+ if (ret) {
+ goto cleanup;
+ }
+
+cleanup:
+ free(tree_path);
+ return ret;
+}
+
+API int
+nc_server_config_new_tls_crl_cert_ext(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config)
+{
+ int ret = 0;
+ struct lyd_node *new_tree, *node = NULL;
+ char *tree_path = NULL;
+ struct lys_module *mod;
+
+ NC_CHECK_ARG_RET(NULL, ctx, endpt_name, config, 1);
+
+ /* prepare path for instertion of leaves later */
+ asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/"
+ "client-authentication", endpt_name);
+ if (!tree_path) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* create all the nodes in the path */
+ ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree);
+ if (ret) {
+ goto cleanup;
+ }
+ if (!*config) {
+ *config = new_tree;
+ }
+
+ if (!new_tree) {
+ /* no new nodes were created */
+ ret = lyd_find_path(*config, tree_path, 0, &new_tree);
+ } else {
+ /* config was NULL */
+ ret = lyd_find_path(new_tree, tree_path, 0, &new_tree);
+ }
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* delete other choice nodes if they are present */
+ lyd_find_path(new_tree, "libnetconf2-netconf-server:crl-path", 0, &node);
+ lyd_free_tree(node);
+ lyd_find_path(new_tree, "libnetconf2-netconf-server:crl-url", 0, &node);
+ lyd_free_tree(node);
+
+ /* get the wanted module, because parent of the inserted node has a different one */
+ mod = ly_ctx_get_module_implemented(ctx, "libnetconf2-netconf-server");
+ if (!mod) {
+ ERR(NULL, "Error getting libnetconf2-netconf-server module.");
+ ret = 1;
+ goto cleanup;
+ }
+
+ ret = lyd_new_term(new_tree, mod, "crl-cert-ext", NULL, 0, NULL);
+ if (ret) {
+ ERR(NULL, "Creating new Certificate Revocation List node failed.");
+ goto cleanup;
+ }
+
+ /* check if top-level container has operation and if not, add it */
+ ret = nc_config_new_check_add_operation(ctx, *config);
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* Add all default nodes */
+ ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL);
+ if (ret) {
+ goto cleanup;
+ }
+
+cleanup:
+ free(tree_path);
+ return ret;
+}
diff --git a/src/server_config.c b/src/server_config.c
index ff0265f..a94bc09 100644
--- a/src/server_config.c
+++ b/src/server_config.c
@@ -25,6 +25,10 @@
#include <libyang/libyang.h>
+#ifdef NC_ENABLED_SSH_TLS
+#include <openssl/x509_vfy.h> // X509_STORE_free
+#endif
+
#include "compat.h"
#include "config.h"
#include "log_p.h"
@@ -670,6 +674,20 @@
#ifdef NC_ENABLED_SSH_TLS
static void
+nc_server_config_del_url(struct nc_server_tls_opts *opts)
+{
+ free(opts->crl_url);
+ opts->crl_url = NULL;
+}
+
+static void
+nc_server_config_del_path(struct nc_server_tls_opts *opts)
+{
+ free(opts->crl_path);
+ opts->crl_path = NULL;
+}
+
+static void
nc_server_config_tls_del_ciphers(struct nc_server_tls_opts *opts)
{
free(opts->ciphers);
@@ -804,6 +822,11 @@
nc_server_config_tls_del_certs(&opts->ca_certs);
nc_server_config_tls_del_certs(&opts->ee_certs);
+ nc_server_config_del_path(opts);
+ nc_server_config_del_url(opts);
+ X509_STORE_free(opts->crl_store);
+ opts->crl_store = NULL;
+
nc_server_config_tls_del_ctns(opts);
nc_server_config_tls_del_ciphers(opts);
@@ -3145,6 +3168,8 @@
struct nc_endpt *endpt;
const char *cipher = NULL;
+ assert(!strcmp(LYD_NAME(node), "cipher-suite"));
+
if (nc_server_config_get_endpt(node, &endpt, NULL)) {
ret = 1;
goto cleanup;
@@ -3167,6 +3192,87 @@
return ret;
}
+static int
+nc_server_config_crl_url(const struct lyd_node *node, NC_OPERATION op)
+{
+ int ret = 0;
+ struct nc_endpt *endpt;
+
+ assert(!strcmp(LYD_NAME(node), "crl-url"));
+
+ if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
+ nc_server_config_del_url(endpt->opts.tls);
+ endpt->opts.tls->crl_url = strdup(lyd_get_value(node));
+ if (!endpt->opts.tls->crl_url) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+ } else if (op == NC_OP_DELETE) {
+ nc_server_config_del_url(endpt->opts.tls);
+ }
+
+cleanup:
+ return ret;
+}
+
+static int
+nc_server_config_crl_path(const struct lyd_node *node, NC_OPERATION op)
+{
+ int ret = 0;
+ struct nc_endpt *endpt;
+
+ assert(!strcmp(LYD_NAME(node), "crl-path"));
+
+ if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
+ nc_server_config_del_path(endpt->opts.tls);
+ endpt->opts.tls->crl_path = strdup(lyd_get_value(node));
+ if (!endpt->opts.tls->crl_path) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+ } else if (op == NC_OP_DELETE) {
+ nc_server_config_del_path(endpt->opts.tls);
+ }
+
+cleanup:
+ return ret;
+}
+
+static int
+nc_server_config_crl_cert_ext(const struct lyd_node *node, NC_OPERATION op)
+{
+ int ret = 0;
+ struct nc_endpt *endpt;
+
+ assert(!strcmp(LYD_NAME(node), "crl-cert-ext"));
+
+ if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
+ endpt->opts.tls->crl_cert_ext = 1;
+ } else if (op == NC_OP_DELETE) {
+ endpt->opts.tls->crl_cert_ext = 0;
+ }
+
+cleanup:
+ return ret;
+}
+
#endif /* NC_ENABLED_SSH_TLS */
static int
@@ -3328,6 +3434,18 @@
if (nc_server_config_cipher_suite(node, op)) {
goto error;
}
+ } else if (!strcmp(name, "crl-url")) {
+ if (nc_server_config_crl_url(node, op)) {
+ goto error;
+ }
+ } else if (!strcmp(name, "crl-path")) {
+ if (nc_server_config_crl_path(node, op)) {
+ goto error;
+ }
+ } else if (!strcmp(name, "crl-cert-ext")) {
+ if (nc_server_config_crl_cert_ext(node, op)) {
+ goto error;
+ }
}
#endif /* NC_ENABLED_SSH_TLS */
diff --git a/src/server_config.h b/src/server_config.h
index d75de3b..02362f2 100644
--- a/src/server_config.h
+++ b/src/server_config.h
@@ -363,6 +363,56 @@
int nc_server_config_new_tls_ciphers(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config,
int cipher_count, ...);
+/**
+ * @brief Creates new YANG configuration data nodes for a Certificate Revocation List via a local file.
+ *
+ * Beware that you can choose up to one function between the three CRL alternatives on a given endpoint and calling
+ * this function will remove any CRL YANG nodes created by the other two functions.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] endpt_name Arbitrary identifier of the endpoint.
+ * If an endpoint with this identifier already exists, it's contents will be changed.
+ * @param[in] path Path to a DER/PEM encoded CRL file.
+ * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created.
+ * Otherwise the new YANG data will be added to the previous data and may override it.
+ * @return 0 on success, non-zero otherwise.
+ */
+int nc_server_config_new_tls_crl_path(const struct ly_ctx *ctx, const char *endpt_name, const char *path, struct lyd_node **config);
+
+/**
+ * @brief Creates new YANG configuration data nodes for a Certificate Revocation List via an URL.
+ *
+ * Beware that you can choose up to one function between the three CRL alternatives on a given endpoint and calling
+ * this function will remove any CRL YANG nodes created by the other two functions.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] endpt_name Arbitrary identifier of the endpoint.
+ * If an endpoint with this identifier already exists, it's contents will be changed.
+ * @param[in] url URL from which the CRL file will be downloaded. The file has to be in the DER or PEM format.
+ * The allowed protocols are all the protocols supported by CURL.
+ * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created.
+ * Otherwise the new YANG data will be added to the previous data and may override it.
+ * @return 0 on success, non-zero otherwise.
+ */
+int nc_server_config_new_tls_crl_url(const struct ly_ctx *ctx, const char *endpt_name, const char *url, struct lyd_node **config);
+
+/**
+ * @brief Creates new YANG configuration data nodes for a Certificate Revocation List via certificate extensions.
+ *
+ * The chain of configured Certificate Authorities will be examined. For each certificate in this chain all the
+ * CRLs from the URLs specified in their extension fields CRL Distribution Points will be downloaded and used.
+ * Beware that you can choose up to one function between the three CRL alternatives on a given endpoint and calling
+ * this function will remove any CRL YANG nodes created by the other two functions.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] endpt_name Arbitrary identifier of the endpoint.
+ * If an endpoint with this identifier already exists, it's contents will be changed.
+ * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created.
+ * Otherwise the new YANG data will be added to the previous data and may override it.
+ * @return 0 on success, non-zero otherwise.
+ */
+int nc_server_config_new_tls_crl_cert_ext(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config);
+
#endif /* NC_ENABLED_SSH_TLS */
#ifdef __cplusplus
diff --git a/src/session_p.h b/src/session_p.h
index 16100c4..13dfd31 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -227,6 +227,14 @@
};
/**
+ * @brief Storing downloaded data via CURL.
+ */
+struct nc_curl_data {
+ unsigned char *data; /**< Downloaded data */
+ size_t size; /**< Size of downloaded data */
+};
+
+/**
* @brief Cert-to-name entries.
*/
struct nc_ctn {
@@ -261,6 +269,10 @@
struct nc_cert_grouping ca_certs; /**< Client certificate authorities */
struct nc_cert_grouping ee_certs; /**< Client end-entity certificates */
+ char *crl_url; /**< URI to download the CRL from */
+ char *crl_path; /**< Path to a CRL file */
+ int crl_cert_ext; /**< Indicates to use CA's distribution points to obtain CRLs */
+ X509_STORE *crl_store; /**< Stores all the CRLs */
unsigned int tls_versions; /**< TLS versions */
char *ciphers; /**< TLS ciphers */
diff --git a/src/session_server_tls.c b/src/session_server_tls.c
index 5f5c6ea..ea63bc8 100644
--- a/src/session_server_tls.c
+++ b/src/session_server_tls.c
@@ -599,6 +599,7 @@
}
static int
+<<<<<<< HEAD
nc_server_tls_ts_ref_get_certs(const char *referenced_name, struct nc_certificate **certs, uint16_t *cert_count)
{
uint16_t i;
@@ -681,17 +682,29 @@
}
static int
+=======
+>>>>>>> d301c76 (config UPDATE implemented CRL for TLS)
nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
X509_NAME *subject;
X509_NAME *issuer;
X509 *cert;
+<<<<<<< HEAD
+=======
+ struct nc_cert_grouping *ee_certs;
+>>>>>>> d301c76 (config UPDATE implemented CRL for TLS)
char *cp;
STACK_OF(X509) * cert_stack;
struct nc_session *session;
struct nc_server_tls_opts *opts;
+<<<<<<< HEAD
int rc, depth;
+=======
+ int i, rc, depth;
+ const char *username = NULL;
+ NC_TLS_CTN_MAPTYPE map_type = 0;
+>>>>>>> d301c76 (config UPDATE implemented CRL for TLS)
/* get the thread session */
session = pthread_getspecific(verify_key);
@@ -712,14 +725,23 @@
/* standard certificate verification failed, so an end-entity client cert must match to continue */
if (!preverify_ok) {
+<<<<<<< HEAD
/* check current endpoint's end-entity certs */
rc = nc_server_tls_do_preverify(session, x509_ctx, 1);
if (rc == -1) {
+=======
+ /* get the store from the current context */
+ X509_STORE *store = X509_STORE_CTX_get0_store(x509_ctx);
+
+ if (!store) {
+ ERR(session, "Error getting store from context (%s).", ERR_reason_error_string(ERR_get_error()));
+>>>>>>> d301c76 (config UPDATE implemented CRL for TLS)
return 0;
} else if (rc == 1) {
return 1;
}
+<<<<<<< HEAD
/* no match, continue */
if (opts->endpt_client_ref) {
/* check referenced endpoint's end-entity certs */
@@ -727,11 +749,33 @@
if (rc == -1) {
return 0;
} else if (rc == 1) {
+=======
+ /* get the data from the store */
+ ee_certs = X509_STORE_get_ex_data(store, 1);
+ if (!ee_certs) {
+ ERR(session, "Error getting data from store (%s).", ERR_reason_error_string(ERR_get_error()));
+ return 0;
+ }
+
+ for (i = 0; i < ee_certs->cert_count; i++) {
+ cert = base64der_to_cert(ee_certs->certs[i].data);
+ rc = cert_pubkey_match(session->opts.server.client_cert, cert);
+ X509_free(cert);
+ if (rc) {
+ /* we are just overriding the failed standard certificate verification (preverify_ok == 0),
+ * this callback will be called again with the same current certificate and preverify_ok == 1 */
+ VRB(session, "Cert verify: fail (%s), but the end-entity certificate is trusted, continuing.",
+ X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx)));
+ X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
+>>>>>>> d301c76 (config UPDATE implemented CRL for TLS)
return 1;
}
}
+<<<<<<< HEAD
/* no match, fail */
+=======
+>>>>>>> d301c76 (config UPDATE implemented CRL for TLS)
ERR(session, "Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx)));
return 0;
}