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;
     }