config UPDATE add support for TLS
New API for configuring TLS added. For the server, only it's certificate
is currently supported. For the client, only it's end-entity and
certificate-authority certificates are supported (as compared to
ietf-netconf-server module).
Each source and header files' includes were refactored (added
missing/deleted redundant).
New file to generate documentation from added to replace the old one.
New API parameters position changed. Split the API to SSH/TLS/common
files and added new common header for these.
Made changes to some internal structures and renamed some members.
diff --git a/src/session_server_tls.c b/src/session_server_tls.c
index 0c227ce..5f5c6ea 100644
--- a/src/session_server_tls.c
+++ b/src/session_server_tls.c
@@ -1,10 +1,11 @@
/**
* @file session_server_tls.c
* @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 TLS server session manipulation functions
*
* @copyright
- * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 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.
@@ -16,9 +17,14 @@
#define _GNU_SOURCE
#include <poll.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <curl/curl.h>
+#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
@@ -26,13 +32,10 @@
#include <openssl/x509v3.h>
#include "compat.h"
-#include "libnetconf.h"
-#include "session_server.h"
-#include "session_server_ch.h"
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-#define X509_STORE_CTX_get_by_subject X509_STORE_get_by_subject
-#endif
+#include "config.h"
+#include "log_p.h"
+#include "session.h"
+#include "session_p.h"
struct nc_server_tls_opts tls_ch_opts;
pthread_mutex_t tls_ch_opts_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -51,10 +54,12 @@
if (!t) {
return NULL;
}
+
bio = BIO_new(BIO_s_mem());
if (!bio) {
return NULL;
}
+
ASN1_TIME_print(bio, t);
n = BIO_pending(bio);
cp = malloc(n + 1);
@@ -63,12 +68,14 @@
BIO_free(bio);
return NULL;
}
+
n = BIO_read(bio, cp, n);
if (n < 0) {
BIO_free(bio);
free(cp);
return NULL;
}
+
cp[n] = '\0';
BIO_free(bio);
return cp;
@@ -123,23 +130,6 @@
return out;
}
-/* return NULL - either errno or SSL error */
-static X509 *
-pem_to_cert(const char *path)
-{
- FILE *fp;
- X509 *out;
-
- fp = fopen(path, "r");
- if (!fp) {
- return NULL;
- }
-
- out = PEM_read_X509(fp, NULL, NULL, NULL);
- fclose(fp);
- return out;
-}
-
static EVP_PKEY *
base64der_to_privatekey(const char *in, const char *key_str)
{
@@ -215,7 +205,7 @@
*username = strdup(common_name);
if (!*username) {
ERRMEM;
- return -1;
+ return 1;
}
free(subject);
} else {
@@ -233,14 +223,10 @@
/* rfc822Name (email) */
if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_RFC822_NAME)) &&
(san_name->type == GEN_EMAIL)) {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
- *username = strdup((char *)ASN1_STRING_data(san_name->d.rfc822Name));
-#else
*username = strdup((char *)ASN1_STRING_get0_data(san_name->d.rfc822Name));
-#endif
if (!*username) {
ERRMEM;
- return -1;
+ return 1;
}
break;
}
@@ -248,14 +234,10 @@
/* dNSName */
if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_DNS_NAME)) &&
(san_name->type == GEN_DNS)) {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
- *username = strdup((char *)ASN1_STRING_data(san_name->d.dNSName));
-#else
*username = strdup((char *)ASN1_STRING_get0_data(san_name->d.dNSName));
-#endif
if (!*username) {
ERRMEM;
- return -1;
+ return 1;
}
break;
}
@@ -340,13 +322,17 @@
map_type = NC_TLS_CTN_UNKNOWN;
/* first make sure the entry is valid */
- if (!ctn->fingerprint || !ctn->map_type || ((ctn->map_type == NC_TLS_CTN_SPECIFIED) && !ctn->name)) {
+ if (!ctn->map_type || ((ctn->map_type == NC_TLS_CTN_SPECIFIED) && !ctn->name)) {
VRB(NULL, "Cert verify CTN: entry with id %u not valid, skipping.", ctn->id);
continue;
}
- /* MD5 */
- if (!strncmp(ctn->fingerprint, "01", 2)) {
+ /* if ctn has no fingerprint, it will match any certificate */
+ if (!ctn->fingerprint) {
+ map_type = ctn->map_type;
+
+ /* MD5 */
+ } else if (!strncmp(ctn->fingerprint, "01", 2)) {
if (!digest_md5) {
if (X509_digest(cert, EVP_md5(), buf, &buf_len) != 1) {
ERR(NULL, "Calculating MD5 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
@@ -361,6 +347,8 @@
VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
map_type = ctn->map_type;
}
+ free(digest_md5);
+ digest_md5 = NULL;
/* SHA-1 */
} else if (!strncmp(ctn->fingerprint, "02", 2)) {
@@ -378,6 +366,8 @@
VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
map_type = ctn->map_type;
}
+ free(digest_sha1);
+ digest_sha1 = NULL;
/* SHA-224 */
} else if (!strncmp(ctn->fingerprint, "03", 2)) {
@@ -395,6 +385,8 @@
VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
map_type = ctn->map_type;
}
+ free(digest_sha224);
+ digest_sha224 = NULL;
/* SHA-256 */
} else if (!strncmp(ctn->fingerprint, "04", 2)) {
@@ -412,6 +404,8 @@
VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
map_type = ctn->map_type;
}
+ free(digest_sha256);
+ digest_sha256 = NULL;
/* SHA-384 */
} else if (!strncmp(ctn->fingerprint, "05", 2)) {
@@ -429,6 +423,8 @@
VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
map_type = ctn->map_type;
}
+ free(digest_sha384);
+ digest_sha384 = NULL;
/* SHA-512 */
} else if (!strncmp(ctn->fingerprint, "06", 2)) {
@@ -446,6 +442,8 @@
VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
map_type = ctn->map_type;
}
+ free(digest_sha512);
+ digest_sha512 = NULL;
/* unknown */
} else {
@@ -499,27 +497,201 @@
return ret;
}
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
+static int
+nc_server_tls_check_crl(X509_STORE *crl_store, X509_STORE_CTX *x509_ctx, X509 *cert,
+ const X509_NAME *subject, const X509_NAME *issuer)
+{
+ int n, i, ret = 0;
+ X509_STORE_CTX *store_ctx = NULL;
+ X509_OBJECT *obj = NULL;
+ X509_CRL *crl;
+ X509_REVOKED *revoked;
+ EVP_PKEY *pubkey;
+ const ASN1_INTEGER *serial;
+ const ASN1_TIME *last_update = NULL, *next_update = NULL;
+ char *cp;
+
+ store_ctx = X509_STORE_CTX_new();
+ if (!store_ctx) {
+ ERRMEM;
+ ret = -1;
+ goto cleanup;
+ }
+
+ /* init store context */
+ ret = X509_STORE_CTX_init(store_ctx, crl_store, NULL, NULL);
+ if (!ret) {
+ ERR(NULL, "Initializing x509 store ctx failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = -1;
+ goto cleanup;
+ }
+ ret = 0;
+
+ /* try to find a CRL entry that corresponds to the current certificate in question */
+ obj = X509_STORE_CTX_get_obj_by_subject(store_ctx, X509_LU_CRL, subject);
+ crl = X509_OBJECT_get0_X509_CRL(obj);
+ X509_OBJECT_free(obj);
+ if (crl) {
+ /* found it */
+ cp = X509_NAME_oneline(subject, NULL, 0);
+ VRB(NULL, "Cert verify CRL: issuer: %s.", cp);
+ OPENSSL_free(cp);
+
+ last_update = X509_CRL_get0_lastUpdate(crl);
+ next_update = X509_CRL_get0_nextUpdate(crl);
+ cp = asn1time_to_str(last_update);
+ VRB(NULL, "Cert verify CRL: last update: %s.", cp);
+ free(cp);
+ cp = asn1time_to_str(next_update);
+ VRB(NULL, "Cert verify CRL: next update: %s.", cp);
+ free(cp);
+
+ /* verify the signature on this CRL */
+ pubkey = X509_get0_pubkey(cert);
+ if (X509_CRL_verify(crl, pubkey) <= 0) {
+ ERR(NULL, "Cert verify CRL: invalid signature.");
+ X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
+ ret = -1;
+ goto cleanup;
+ }
+
+ /* check date of CRL to make sure it's not expired */
+ if (!next_update) {
+ ERR(NULL, "Cert verify CRL: invalid nextUpdate field.");
+ X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
+ ret = -1;
+ goto cleanup;
+ }
+
+ if (X509_cmp_current_time(next_update) < 0) {
+ ERR(NULL, "Cert verify CRL: expired - revoking all certificates.");
+ X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
+ ret = -1;
+ goto cleanup;
+ }
+ }
+
+ /* try to retrieve a CRL corresponding to the _issuer_ of
+ * the current certificate in order to check for revocation */
+ obj = X509_STORE_CTX_get_obj_by_subject(store_ctx, X509_LU_CRL, issuer);
+ crl = X509_OBJECT_get0_X509_CRL(obj);
+ if (crl) {
+ n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
+ for (i = 0; i < n; i++) {
+ revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
+ serial = X509_REVOKED_get0_serialNumber(revoked);
+ if (ASN1_INTEGER_cmp(serial, X509_get_serialNumber(cert)) == 0) {
+ cp = X509_NAME_oneline(issuer, NULL, 0);
+ ERR(NULL, "Cert verify CRL: certificate with serial %ld (0x%lX) revoked per CRL from issuer %s.",
+ serial, serial, cp);
+ OPENSSL_free(cp);
+ X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
+ ret = -1;
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ X509_STORE_CTX_free(store_ctx);
+ X509_OBJECT_free(obj);
+ return ret;
+}
+
+static int
+nc_server_tls_ts_ref_get_certs(const char *referenced_name, struct nc_certificate **certs, uint16_t *cert_count)
+{
+ uint16_t i;
+ struct nc_truststore *ts = &server_opts.truststore;
+
+ *certs = NULL;
+ *cert_count = 0;
+
+ /* lookup name */
+ for (i = 0; i < ts->cert_bag_count; i++) {
+ if (!strcmp(referenced_name, ts->cert_bags[i].name)) {
+ break;
+ }
+ }
+
+ if (i == ts->cert_bag_count) {
+ ERR(NULL, "Truststore entry \"%s\" not found.", referenced_name);
+ return -1;
+ }
+
+ *certs = ts->cert_bags[i].certs;
+ *cert_count = ts->cert_bags[i].cert_count;
+ return 0;
+}
+
+/* In case a CA chain verification failed an end-entity certificate must match.
+ * The meaning of local_or_referenced is that it states, which end-entity certificates to check
+ * (1 = current endpoint's, 2 = referenced endpoint's).
+ */
+static int
+nc_server_tls_do_preverify(struct nc_session *session, X509_STORE_CTX *x509_ctx, int local_or_referenced)
+{
+ X509_STORE *store;
+ struct nc_cert_grouping *ee_certs;
+ int i, ret;
+ X509 *cert;
+ struct nc_certificate *certs;
+ uint16_t cert_count;
+
+ 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()));
+ return -1;
+ }
+
+ /* get the data from the store */
+ ee_certs = X509_STORE_get_ex_data(store, local_or_referenced);
+ if (!ee_certs) {
+ ERR(session, "Error getting data from store (%s).", ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+
+ if (ee_certs->store == NC_STORE_LOCAL) {
+ /* local definition */
+ certs = ee_certs->certs;
+ cert_count = ee_certs->cert_count;
+ } else {
+ /* truststore reference */
+ if (nc_server_tls_ts_ref_get_certs(ee_certs->ts_ref, &certs, &cert_count)) {
+ ERR(NULL, "Error getting end-entity certificates from the truststore reference \"%s\".", ee_certs->ts_ref);
+ return -1;
+ }
+ }
+
+ for (i = 0; i < cert_count; i++) {
+ cert = base64der_to_cert(certs[i].data);
+ ret = cert_pubkey_match(session->opts.server.client_cert, cert);
+ X509_free(cert);
+ if (ret) {
+ /* 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);
+ return 1;
+ }
+ }
+
+ return 0;
+}
static int
nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
- X509_STORE_CTX *store_ctx;
- X509_OBJECT *obj;
X509_NAME *subject;
X509_NAME *issuer;
X509 *cert;
- X509_CRL *crl;
- X509_REVOKED *revoked;
+ char *cp;
STACK_OF(X509) * cert_stack;
- EVP_PKEY *pubkey;
struct nc_session *session;
struct nc_server_tls_opts *opts;
- const ASN1_INTEGER *serial;
- int i, n, rc, depth;
- char *cp;
- const ASN1_TIME *last_update = NULL, *next_update = NULL;
+ int rc, depth;
/* get the thread session */
session = pthread_getspecific(verify_key);
@@ -538,224 +710,28 @@
sk_X509_pop_free(cert_stack, X509_free);
}
- /* standard certificate verification failed, so a trusted client cert must match to continue */
+ /* standard certificate verification failed, so an end-entity client cert must match to continue */
if (!preverify_ok) {
- subject = X509_get_subject_name(session->opts.server.client_cert);
- cert_stack = X509_STORE_CTX_get1_certs(x509_ctx, subject);
- if (cert_stack) {
- for (i = 0; i < sk_X509_num(cert_stack); ++i) {
- if (cert_pubkey_match(session->opts.server.client_cert, sk_X509_value(cert_stack, i))) {
- /* 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(NULL, "Cert verify: fail (%s), but the client 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);
- sk_X509_pop_free(cert_stack, X509_free);
- return 1;
- }
- }
- sk_X509_pop_free(cert_stack, X509_free);
- }
-
- ERR(NULL, "Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx)));
- return 0;
- }
-
- /* print cert verify info */
- depth = X509_STORE_CTX_get_error_depth(x509_ctx);
- VRB(NULL, "Cert verify: depth %d.", depth);
-
- cert = X509_STORE_CTX_get_current_cert(x509_ctx);
- subject = X509_get_subject_name(cert);
- issuer = X509_get_issuer_name(cert);
-
- cp = X509_NAME_oneline(subject, NULL, 0);
- VRB(NULL, "Cert verify: subject: %s.", cp);
- OPENSSL_free(cp);
- cp = X509_NAME_oneline(issuer, NULL, 0);
- VRB(NULL, "Cert verify: issuer: %s.", cp);
- OPENSSL_free(cp);
-
- /* check for revocation if set */
- if (opts->crl_store) {
- /* try to retrieve a CRL corresponding to the _subject_ of
- * the current certificate in order to verify it's integrity */
- store_ctx = X509_STORE_CTX_new();
- obj = X509_OBJECT_new();
- X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
- rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, subject, obj);
- X509_STORE_CTX_free(store_ctx);
- crl = X509_OBJECT_get0_X509_CRL(obj);
- if ((rc > 0) && crl) {
- cp = X509_NAME_oneline(subject, NULL, 0);
- VRB(NULL, "Cert verify CRL: issuer: %s.", cp);
- OPENSSL_free(cp);
-
- last_update = X509_CRL_get0_lastUpdate(crl);
- next_update = X509_CRL_get0_nextUpdate(crl);
- cp = asn1time_to_str(last_update);
- VRB(NULL, "Cert verify CRL: last update: %s.", cp);
- free(cp);
- cp = asn1time_to_str(next_update);
- VRB(NULL, "Cert verify CRL: next update: %s.", cp);
- free(cp);
-
- /* verify the signature on this CRL */
- pubkey = X509_get_pubkey(cert);
- if (X509_CRL_verify(crl, pubkey) <= 0) {
- ERR(NULL, "Cert verify CRL: invalid signature.");
- X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
- X509_OBJECT_free(obj);
- if (pubkey) {
- EVP_PKEY_free(pubkey);
- }
- return 0;
- }
- if (pubkey) {
- EVP_PKEY_free(pubkey);
- }
-
- /* check date of CRL to make sure it's not expired */
- if (!next_update) {
- ERR(NULL, "Cert verify CRL: invalid nextUpdate field.");
- X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
- X509_OBJECT_free(obj);
- return 0;
- }
- if (X509_cmp_current_time(next_update) < 0) {
- ERR(NULL, "Cert verify CRL: expired - revoking all certificates.");
- X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
- X509_OBJECT_free(obj);
- return 0;
- }
- X509_OBJECT_free(obj);
- }
-
- /* try to retrieve a CRL corresponding to the _issuer_ of
- * the current certificate in order to check for revocation */
- store_ctx = X509_STORE_CTX_new();
- obj = X509_OBJECT_new();
- X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
- rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, issuer, obj);
- X509_STORE_CTX_free(store_ctx);
- crl = X509_OBJECT_get0_X509_CRL(obj);
- if ((rc > 0) && crl) {
- /* check if the current certificate is revoked by this CRL */
- n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
- for (i = 0; i < n; i++) {
- revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
- serial = X509_REVOKED_get0_serialNumber(revoked);
- if (ASN1_INTEGER_cmp(serial, X509_get_serialNumber(cert)) == 0) {
- cp = X509_NAME_oneline(issuer, NULL, 0);
- ERR(NULL, "Cert verify CRL: certificate with serial %ld (0x%lX) revoked per CRL from issuer %s.",
- serial, serial, cp);
- OPENSSL_free(cp);
- X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
- X509_OBJECT_free(obj);
- return 0;
- }
- }
- X509_OBJECT_free(obj);
- }
- }
-
- /* cert-to-name already successful */
- if (session->username) {
- return 1;
- }
-
- /* cert-to-name */
- rc = nc_tls_cert_to_name(session, opts->ctn, cert);
- if (rc) {
+ /* check current endpoint's end-entity certs */
+ rc = nc_server_tls_do_preverify(session, x509_ctx, 1);
if (rc == -1) {
- /* fatal error */
- depth = 0;
+ return 0;
+ } else if (rc == 1) {
+ return 1;
}
- /* rc == 1 is a normal CTN fail (no match found) */
- goto fail;
- }
- VRB(NULL, "Cert verify CTN: new client username recognized as \"%s\".", session->username);
-
- if (server_opts.user_verify_clb && !server_opts.user_verify_clb(session)) {
- VRB(NULL, "Cert verify: user verify callback revoked authorization.");
- X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
- return 0;
- }
-
- return 1;
-
-fail:
- if (depth > 0) {
- VRB(NULL, "Cert verify CTN: cert fail, cert-to-name will continue on the next cert in chain.");
- return 1;
- }
-
- VRB(NULL, "Cert-to-name unsuccessful, dropping the new client.");
- X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
- return 0;
-}
-
-#else
-
-static int
-nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
-{
- X509_STORE_CTX store_ctx;
- X509_OBJECT obj;
- X509_NAME *subject;
- X509_NAME *issuer;
- X509 *cert;
- X509_CRL *crl;
- X509_REVOKED *revoked;
-
- STACK_OF(X509) * cert_stack;
- EVP_PKEY *pubkey;
- struct nc_session *session;
- struct nc_server_tls_opts *opts;
- long serial;
- int i, n, rc, depth;
- char *cp;
- ASN1_TIME *last_update = NULL, *next_update = NULL;
-
- /* get the thread session */
- session = pthread_getspecific(verify_key);
- if (!session) {
- ERRINT;
- return 0;
- }
-
- opts = session->data;
-
- /* get the last certificate, that is the peer (client) certificate */
- if (!session->opts.server.client_cert) {
- cert_stack = X509_STORE_CTX_get1_chain(x509_ctx);
- while ((cert = sk_X509_pop(cert_stack))) {
- X509_free(session->opts.server.client_cert);
- session->opts.server.client_cert = cert;
- }
- sk_X509_pop_free(cert_stack, X509_free);
- }
-
- /* standard certificate verification failed, so a trusted client cert must match to continue */
- if (!preverify_ok) {
- subject = X509_get_subject_name(session->opts.server.client_cert);
- cert_stack = X509_STORE_get1_certs(x509_ctx, subject);
- if (cert_stack) {
- for (i = 0; i < sk_X509_num(cert_stack); ++i) {
- if (cert_pubkey_match(session->opts.server.client_cert, sk_X509_value(cert_stack, i))) {
- /* 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 client 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);
- sk_X509_pop_free(cert_stack, X509_free);
- return 1;
- }
+ /* no match, continue */
+ if (opts->endpt_client_ref) {
+ /* check referenced endpoint's end-entity certs */
+ rc = nc_server_tls_do_preverify(session, x509_ctx, 2);
+ if (rc == -1) {
+ return 0;
+ } else if (rc == 1) {
+ return 1;
}
- sk_X509_pop_free(cert_stack, X509_free);
}
+ /* no match, fail */
ERR(session, "Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx)));
return 0;
}
@@ -775,84 +751,11 @@
VRB(session, "Cert verify: issuer: %s.", cp);
OPENSSL_free(cp);
- /* check for revocation if set */
+ /* check if the current certificate is revoked if CRL is set */
if (opts->crl_store) {
- /* try to retrieve a CRL corresponding to the _subject_ of
- * the current certificate in order to verify it's integrity */
- memset((char *)&obj, 0, sizeof(obj));
- X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
- rc = X509_STORE_CTX_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
- X509_STORE_CTX_cleanup(&store_ctx);
- crl = obj.data.crl;
- if ((rc > 0) && crl) {
- cp = X509_NAME_oneline(subject, NULL, 0);
- VRB(session, "Cert verify CRL: issuer: %s.", cp);
- OPENSSL_free(cp);
-
- last_update = X509_CRL_get_lastUpdate(crl);
- next_update = X509_CRL_get_nextUpdate(crl);
- cp = asn1time_to_str(last_update);
- VRB(session, "Cert verify CRL: last update: %s.", cp);
- free(cp);
- cp = asn1time_to_str(next_update);
- VRB(session, "Cert verify CRL: next update: %s.", cp);
- free(cp);
-
- /* verify the signature on this CRL */
- pubkey = X509_get_pubkey(cert);
- if (X509_CRL_verify(crl, pubkey) <= 0) {
- ERR(session, "Cert verify CRL: invalid signature.");
- X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
- X509_OBJECT_free_contents(&obj);
- if (pubkey) {
- EVP_PKEY_free(pubkey);
- }
- return 0;
- }
- if (pubkey) {
- EVP_PKEY_free(pubkey);
- }
-
- /* check date of CRL to make sure it's not expired */
- if (!next_update) {
- ERR(session, "Cert verify CRL: invalid nextUpdate field.");
- X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
- X509_OBJECT_free_contents(&obj);
- return 0;
- }
- if (X509_cmp_current_time(next_update) < 0) {
- ERR(session, "Cert verify CRL: expired - revoking all certificates.");
- X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
- X509_OBJECT_free_contents(&obj);
- return 0;
- }
- X509_OBJECT_free_contents(&obj);
- }
-
- /* try to retrieve a CRL corresponding to the _issuer_ of
- * the current certificate in order to check for revocation */
- memset((char *)&obj, 0, sizeof(obj));
- X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
- rc = X509_STORE_CTX_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
- X509_STORE_CTX_cleanup(&store_ctx);
- crl = obj.data.crl;
- if ((rc > 0) && crl) {
- /* check if the current certificate is revoked by this CRL */
- n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
- for (i = 0; i < n; i++) {
- revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
- if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
- serial = ASN1_INTEGER_get(revoked->serialNumber);
- cp = X509_NAME_oneline(issuer, NULL, 0);
- ERR(session, "Cert verify CRL: certificate with serial %ld (0x%lX) revoked per CRL from issuer %s.",
- serial, serial, cp);
- OPENSSL_free(cp);
- X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
- X509_OBJECT_free_contents(&obj);
- return 0;
- }
- }
- X509_OBJECT_free_contents(&obj);
+ rc = nc_server_tls_check_crl(opts->crl_store, x509_ctx, cert, subject, issuer);
+ if (rc) {
+ return 0;
}
}
@@ -863,13 +766,24 @@
/* cert-to-name */
rc = nc_tls_cert_to_name(session, opts->ctn, cert);
- if (rc) {
- if (rc == -1) {
- /* fatal error */
- depth = 0;
- }
- /* rc == 1 is a normal CTN fail (no match found) */
+ if (rc == -1) {
+ /* fatal error */
+ depth = 0;
goto fail;
+ } else if ((rc == 1) && !opts->endpt_client_ref) {
+ /* no match found and no referenced endpoint */
+ goto fail;
+ } else if ((rc == 1) && opts->endpt_client_ref) {
+ /* no match found, but has a referenced endpoint so try it */
+ rc = nc_tls_cert_to_name(session, opts->endpt_client_ref->opts.tls->ctn, cert);
+ if (rc) {
+ if (rc == -1) {
+ /* fatal error */
+ depth = 0;
+ }
+ /* rc == 1 is a normal CTN fail (no match found) */
+ goto fail;
+ }
}
VRB(session, "Cert verify CTN: new client username recognized as \"%s\".", session->username);
@@ -893,636 +807,6 @@
return 0;
}
-#endif
-
-static int
-nc_server_tls_set_server_cert(const char *name, struct nc_server_tls_opts *opts)
-{
- if (!name) {
- if (opts->server_cert) {
- free(opts->server_cert);
- }
- opts->server_cert = NULL;
- return 0;
- }
-
- if (opts->server_cert) {
- free(opts->server_cert);
- }
- opts->server_cert = strdup(name);
-
- return 0;
-}
-
-API int
-nc_server_tls_endpt_set_server_cert(const char *endpt_name, const char *name)
-{
- int ret;
- struct nc_endpt *endpt;
-
- NC_CHECK_ARG_RET(NULL, endpt_name, -1);
-
- /* LOCK */
- endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
- if (!endpt) {
- return -1;
- }
- ret = nc_server_tls_set_server_cert(name, endpt->opts.tls);
- /* UNLOCK */
- pthread_rwlock_unlock(&server_opts.config_lock);
-
- return ret;
-}
-
-API int
-nc_server_tls_ch_client_endpt_set_server_cert(const char *client_name, const char *endpt_name, const char *name)
-{
- int ret;
- struct nc_ch_client *client;
- struct nc_ch_endpt *endpt;
-
- /* LOCK */
- endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client);
- if (!endpt) {
- return -1;
- }
-
- ret = nc_server_tls_set_server_cert(name, endpt->opts.tls);
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-
- return ret;
-}
-
-API void
-nc_server_tls_set_server_cert_clb(int (*cert_clb)(const char *name, void *user_data, char **cert_path, char **cert_data,
- char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data,
- void (*free_user_data)(void *user_data))
-{
- if (!cert_clb) {
- ERRARG(NULL, "cert_clb");
- return;
- }
-
- server_opts.server_cert_clb = cert_clb;
- server_opts.server_cert_data = user_data;
- server_opts.server_cert_data_free = free_user_data;
-}
-
-API void
-nc_server_tls_set_server_cert_chain_clb(int (*cert_chain_clb)(const char *name, void *user_data, char ***cert_paths,
- int *cert_path_count, char ***cert_data, int *cert_data_count), void *user_data, void (*free_user_data)(void *user_data))
-{
- if (!cert_chain_clb) {
- ERRARG(NULL, "cert_chain_clb");
- return;
- }
-
- server_opts.server_cert_chain_clb = cert_chain_clb;
- server_opts.server_cert_chain_data = user_data;
- server_opts.server_cert_chain_data_free = free_user_data;
-}
-
-static int
-nc_server_tls_add_trusted_cert_list(const char *name, struct nc_server_tls_opts *opts)
-{
- NC_CHECK_ARG_RET(NULL, name, -1);
-
- ++opts->trusted_cert_list_count;
- opts->trusted_cert_lists = nc_realloc(opts->trusted_cert_lists,
- opts->trusted_cert_list_count * sizeof *opts->trusted_cert_lists);
- if (!opts->trusted_cert_lists) {
- ERRMEM;
- return -1;
- }
- opts->trusted_cert_lists[opts->trusted_cert_list_count - 1] = strdup(name);
-
- return 0;
-}
-
-API int
-nc_server_tls_endpt_add_trusted_cert_list(const char *endpt_name, const char *name)
-{
- int ret;
- struct nc_endpt *endpt;
-
- NC_CHECK_ARG_RET(NULL, endpt_name, -1);
-
- /* LOCK */
- endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
- if (!endpt) {
- return -1;
- }
- ret = nc_server_tls_add_trusted_cert_list(name, endpt->opts.tls);
- /* UNLOCK */
- pthread_rwlock_unlock(&server_opts.config_lock);
-
- return ret;
-}
-
-API int
-nc_server_tls_ch_client_endpt_add_trusted_cert_list(const char *client_name, const char *endpt_name, const char *name)
-{
- int ret;
- struct nc_ch_client *client;
- struct nc_ch_endpt *endpt;
-
- /* LOCK */
- endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client);
- if (!endpt) {
- return -1;
- }
-
- ret = nc_server_tls_add_trusted_cert_list(name, endpt->opts.tls);
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-
- return ret;
-}
-
-API void
-nc_server_tls_set_trusted_cert_list_clb(int (*cert_list_clb)(const char *name, void *user_data, char ***cert_paths,
- int *cert_path_count, char ***cert_data, int *cert_data_count),
- void *user_data, void (*free_user_data)(void *user_data))
-{
- if (!cert_list_clb) {
- ERRARG(NULL, "cert_list_clb");
- return;
- }
-
- server_opts.trusted_cert_list_clb = cert_list_clb;
- server_opts.trusted_cert_list_data = user_data;
- server_opts.trusted_cert_list_data_free = free_user_data;
-}
-
-static int
-nc_server_tls_del_trusted_cert_list(const char *name, struct nc_server_tls_opts *opts)
-{
- uint16_t i;
-
- if (!name) {
- for (i = 0; i < opts->trusted_cert_list_count; ++i) {
- free(opts->trusted_cert_lists[i]);
- }
- free(opts->trusted_cert_lists);
- opts->trusted_cert_lists = NULL;
- opts->trusted_cert_list_count = 0;
- return 0;
- } else {
- for (i = 0; i < opts->trusted_cert_list_count; ++i) {
- if (!strcmp(opts->trusted_cert_lists[i], name)) {
- free(opts->trusted_cert_lists[i]);
-
- --opts->trusted_cert_list_count;
- if (i < opts->trusted_cert_list_count - 1) {
- memmove(opts->trusted_cert_lists + i, opts->trusted_cert_lists + i + 1,
- (opts->trusted_cert_list_count - i) * sizeof *opts->trusted_cert_lists);
- }
- return 0;
- }
- }
- }
-
- ERR(NULL, "Certificate list \"%s\" not found.", name);
- return -1;
-}
-
-API int
-nc_server_tls_endpt_del_trusted_cert_list(const char *endpt_name, const char *name)
-{
- int ret;
- struct nc_endpt *endpt;
-
- NC_CHECK_ARG_RET(NULL, endpt_name, -1);
-
- /* LOCK */
- endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
- if (!endpt) {
- return -1;
- }
- ret = nc_server_tls_del_trusted_cert_list(name, endpt->opts.tls);
- /* UNLOCK */
- pthread_rwlock_unlock(&server_opts.config_lock);
-
- return ret;
-}
-
-API int
-nc_server_tls_ch_client_endpt_del_trusted_cert_list(const char *client_name, const char *endpt_name, const char *name)
-{
- int ret;
- struct nc_ch_client *client;
- struct nc_ch_endpt *endpt;
-
- /* LOCK */
- endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client);
- if (!endpt) {
- return -1;
- }
-
- ret = nc_server_tls_del_trusted_cert_list(name, endpt->opts.tls);
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-
- return ret;
-}
-
-static int
-nc_server_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir, struct nc_server_tls_opts *opts)
-{
- if (!ca_file && !ca_dir) {
- ERRARG(NULL, "ca_file and ca_dir");
- return -1;
- }
-
- if (ca_file) {
- free(opts->trusted_ca_file);
- opts->trusted_ca_file = strdup(ca_file);
- }
-
- if (ca_dir) {
- free(opts->trusted_ca_dir);
- opts->trusted_ca_dir = strdup(ca_dir);
- }
-
- return 0;
-}
-
-API int
-nc_server_tls_endpt_set_trusted_ca_paths(const char *endpt_name, const char *ca_file, const char *ca_dir)
-{
- int ret;
- struct nc_endpt *endpt;
-
- NC_CHECK_ARG_RET(NULL, endpt_name, -1);
-
- /* LOCK */
- endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
- if (!endpt) {
- return -1;
- }
- ret = nc_server_tls_set_trusted_ca_paths(ca_file, ca_dir, endpt->opts.tls);
- /* UNLOCK */
- pthread_rwlock_unlock(&server_opts.config_lock);
-
- return ret;
-}
-
-API int
-nc_server_tls_ch_client_endpt_set_trusted_ca_paths(const char *client_name, const char *endpt_name, const char *ca_file,
- const char *ca_dir)
-{
- int ret;
- struct nc_ch_client *client;
- struct nc_ch_endpt *endpt;
-
- /* LOCK */
- endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client);
- if (!endpt) {
- return -1;
- }
-
- ret = nc_server_tls_set_trusted_ca_paths(ca_file, ca_dir, endpt->opts.tls);
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-
- return ret;
-}
-
-static int
-nc_server_tls_set_crl_paths(const char *crl_file, const char *crl_dir, struct nc_server_tls_opts *opts)
-{
- X509_LOOKUP *lookup;
-
- if (!crl_file && !crl_dir) {
- ERRARG(NULL, "crl_file and crl_dir");
- return -1;
- }
-
- if (!opts->crl_store) {
- opts->crl_store = X509_STORE_new();
- }
-
- if (crl_file) {
- lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file());
- if (!lookup) {
- ERR(NULL, "Failed to add a lookup method.");
- goto fail;
- }
-
- if (X509_LOOKUP_load_file(lookup, crl_file, X509_FILETYPE_PEM) != 1) {
- ERR(NULL, "Failed to add a revocation lookup file (%s).", ERR_reason_error_string(ERR_get_error()));
- goto fail;
- }
- }
-
- if (crl_dir) {
- lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir());
- if (!lookup) {
- ERR(NULL, "Failed to add a lookup method.");
- goto fail;
- }
-
- if (X509_LOOKUP_add_dir(lookup, crl_dir, X509_FILETYPE_PEM) != 1) {
- ERR(NULL, "Failed to add a revocation lookup directory (%s).", ERR_reason_error_string(ERR_get_error()));
- goto fail;
- }
- }
-
- return 0;
-
-fail:
- return -1;
-}
-
-API int
-nc_server_tls_endpt_set_crl_paths(const char *endpt_name, const char *crl_file, const char *crl_dir)
-{
- int ret;
- struct nc_endpt *endpt;
-
- NC_CHECK_ARG_RET(NULL, endpt_name, -1);
-
- /* LOCK */
- endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
- if (!endpt) {
- return -1;
- }
- ret = nc_server_tls_set_crl_paths(crl_file, crl_dir, endpt->opts.tls);
- /* UNLOCK */
- pthread_rwlock_unlock(&server_opts.config_lock);
-
- return ret;
-}
-
-API int
-nc_server_tls_ch_client_set_crl_paths(const char *client_name, const char *endpt_name, const char *crl_file,
- const char *crl_dir)
-{
- int ret;
- struct nc_ch_client *client;
- struct nc_ch_endpt *endpt;
-
- /* LOCK */
- endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client);
- if (!endpt) {
- return -1;
- }
-
- ret = nc_server_tls_set_crl_paths(crl_file, crl_dir, endpt->opts.tls);
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-
- return ret;
-}
-
-static void
-nc_server_tls_clear_crls(struct nc_server_tls_opts *opts)
-{
- if (!opts->crl_store) {
- return;
- }
-
- X509_STORE_free(opts->crl_store);
- opts->crl_store = NULL;
-}
-
-API void
-nc_server_tls_endpt_clear_crls(const char *endpt_name)
-{
- struct nc_endpt *endpt;
-
- if (!endpt_name) {
- ERRARG(NULL, "endpt_name");
- return;
- }
-
- /* LOCK */
- endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
- if (!endpt) {
- return;
- }
- nc_server_tls_clear_crls(endpt->opts.tls);
- /* UNLOCK */
- pthread_rwlock_unlock(&server_opts.config_lock);
-}
-
-API void
-nc_server_tls_ch_client_endpt_clear_crls(const char *client_name, const char *endpt_name)
-{
- struct nc_ch_client *client;
- struct nc_ch_endpt *endpt;
-
- /* LOCK */
- endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client);
- if (!endpt) {
- return;
- }
-
- nc_server_tls_clear_crls(endpt->opts.tls);
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-}
-
-static int
-nc_server_tls_add_ctn(uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name,
- struct nc_server_tls_opts *opts)
-{
- struct nc_ctn *ctn, *new;
-
- if (!opts->ctn) {
- /* the first item */
- opts->ctn = new = calloc(1, sizeof *new);
- if (!new) {
- ERRMEM;
- return -1;
- }
- } else if (opts->ctn->id > id) {
- /* insert at the beginning */
- new = calloc(1, sizeof *new);
- if (!new) {
- ERRMEM;
- return -1;
- }
- new->next = opts->ctn;
- opts->ctn = new;
- } else {
- for (ctn = opts->ctn; ctn->next && ctn->next->id <= id; ctn = ctn->next) {}
- if (ctn->id == id) {
- /* it exists already */
- new = ctn;
- } else {
- /* insert after ctn */
- new = calloc(1, sizeof *new);
- if (!new) {
- ERRMEM;
- return -1;
- }
- new->next = ctn->next;
- ctn->next = new;
- }
- }
-
- new->id = id;
- if (fingerprint) {
- free(new->fingerprint);
- new->fingerprint = strdup(fingerprint);
- }
- if (map_type) {
- new->map_type = map_type;
- }
- if (name) {
- free(new->name);
- new->name = strdup(name);
- }
-
- return 0;
-}
-
-API int
-nc_server_tls_endpt_add_ctn(const char *endpt_name, uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type,
- const char *name)
-{
- int ret;
- struct nc_endpt *endpt;
-
- NC_CHECK_ARG_RET(NULL, endpt_name, -1);
-
- /* LOCK */
- endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
- if (!endpt) {
- return -1;
- }
- ret = nc_server_tls_add_ctn(id, fingerprint, map_type, name, endpt->opts.tls);
- /* UNLOCK */
- pthread_rwlock_unlock(&server_opts.config_lock);
-
- return ret;
-}
-
-API int
-nc_server_tls_ch_client_endpt_add_ctn(const char *client_name, const char *endpt_name, uint32_t id,
- const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name)
-{
- int ret;
- struct nc_ch_client *client;
- struct nc_ch_endpt *endpt;
-
- /* LOCK */
- endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client);
- if (!endpt) {
- return -1;
- }
-
- ret = nc_server_tls_add_ctn(id, fingerprint, map_type, name, endpt->opts.tls);
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-
- return ret;
-}
-
-static int
-nc_server_tls_del_ctn(int64_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name,
- struct nc_server_tls_opts *opts)
-{
- struct nc_ctn *ctn, *next, *prev;
- int ret = -1;
-
- if ((id < 0) && !fingerprint && !map_type && !name) {
- ctn = opts->ctn;
- while (ctn) {
- free(ctn->fingerprint);
- free(ctn->name);
-
- next = ctn->next;
- free(ctn);
- ctn = next;
-
- ret = 0;
- }
- opts->ctn = NULL;
- } else {
- prev = NULL;
- ctn = opts->ctn;
- while (ctn) {
- if (((id < 0) || (ctn->id == id)) &&
- (!fingerprint || !strcmp(ctn->fingerprint, fingerprint)) &&
- (!map_type || (ctn->map_type == map_type)) &&
- (!name || (ctn->name && !strcmp(ctn->name, name)))) {
- free(ctn->fingerprint);
- free(ctn->name);
-
- if (prev) {
- prev->next = ctn->next;
- next = ctn->next;
- } else {
- opts->ctn = ctn->next;
- next = ctn->next;
- }
- free(ctn);
- ctn = next;
-
- ret = 0;
- } else {
- prev = ctn;
- ctn = ctn->next;
- }
- }
- }
-
- return ret;
-}
-
-API int
-nc_server_tls_endpt_del_ctn(const char *endpt_name, int64_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type,
- const char *name)
-{
- int ret;
- struct nc_endpt *endpt;
-
- NC_CHECK_ARG_RET(NULL, endpt_name, -1);
-
- /* LOCK */
- endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL);
- if (!endpt) {
- return -1;
- }
- ret = nc_server_tls_del_ctn(id, fingerprint, map_type, name, endpt->opts.tls);
- /* UNLOCK */
- pthread_rwlock_unlock(&server_opts.config_lock);
-
- return ret;
-}
-
-API int
-nc_server_tls_ch_client_endpt_del_ctn(const char *client_name, const char *endpt_name, int64_t id,
- const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name)
-{
- int ret;
- struct nc_ch_client *client;
- struct nc_ch_endpt *endpt;
-
- /* LOCK */
- endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client);
- if (!endpt) {
- return -1;
- }
-
- ret = nc_server_tls_del_ctn(id, fingerprint, map_type, name, endpt->opts.tls);
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-
- return ret;
-}
-
static int
nc_server_tls_get_ctn(uint32_t *id, char **fingerprint, NC_TLS_CTN_MAPTYPE *map_type, char **name,
struct nc_server_tls_opts *opts)
@@ -1625,219 +909,485 @@
server_opts.user_verify_clb = verify_clb;
}
-void
-nc_server_tls_clear_opts(struct nc_server_tls_opts *opts)
-{
- free(opts->server_cert);
- nc_server_tls_del_trusted_cert_list(NULL, opts);
- free(opts->trusted_ca_file);
- free(opts->trusted_ca_dir);
- nc_server_tls_clear_crls(opts);
- nc_server_tls_del_ctn(-1, NULL, 0, NULL, opts);
-}
-
static void
nc_tls_make_verify_key(void)
{
pthread_key_create(&verify_key, NULL);
}
-static X509 *
-tls_load_cert(const char *cert_path, const char *cert_data)
+static int
+nc_server_tls_ks_ref_get_cert_key(const char *referenced_key_name, const char *referenced_cert_name,
+ char **privkey_data, NC_PRIVKEY_FORMAT *privkey_type, char **cert_data)
{
- X509 *cert;
+ uint16_t i, j;
+ struct nc_keystore *ks = &server_opts.keystore;
- if (cert_path) {
- cert = pem_to_cert(cert_path);
- } else {
- cert = base64der_to_cert(cert_data);
- }
+ *privkey_data = NULL;
+ *cert_data = NULL;
- if (!cert) {
- if (cert_path) {
- ERR(NULL, "Loading a trusted certificate (path \"%s\") failed (%s).", cert_path,
- ERR_reason_error_string(ERR_get_error()));
- } else {
- ERR(NULL, "Loading a trusted certificate (data \"%s\") failed (%s).", cert_data,
- ERR_reason_error_string(ERR_get_error()));
+ /* lookup name */
+ for (i = 0; i < ks->asym_key_count; i++) {
+ if (!strcmp(referenced_key_name, ks->asym_keys[i].name)) {
+ break;
}
}
- return cert;
-}
-
-static int
-nc_tls_ctx_set_server_cert_chain(SSL_CTX *tls_ctx, const char *cert_name)
-{
- char **cert_paths = NULL, **cert_data = NULL;
- int cert_path_count = 0, cert_data_count = 0, ret = 0, i = 0;
- X509 *cert = NULL;
-
- if (!server_opts.server_cert_chain_clb) {
- /* This is optional, so return OK */
- return 0;
- }
-
- if (server_opts.server_cert_chain_clb(cert_name, server_opts.server_cert_chain_data, &cert_paths,
- &cert_path_count, &cert_data, &cert_data_count)) {
- ERR(NULL, "Server certificate chain callback failed.");
+ if (i == ks->asym_key_count) {
+ ERR(NULL, "Keystore entry \"%s\" not found.", referenced_key_name);
return -1;
}
- for (i = 0; i < cert_path_count; ++i) {
- cert = tls_load_cert(cert_paths[i], NULL);
- if (!cert || (SSL_CTX_add_extra_chain_cert(tls_ctx, cert) != 1)) {
- ERR(NULL, "Loading the server certificate chain failed (%s).", ERR_reason_error_string(ERR_get_error()));
- ret = -1;
- goto cleanup;
+ for (j = 0; j < ks->asym_keys[i].cert_count; j++) {
+ if (!strcmp(referenced_cert_name, ks->asym_keys[i].certs[i].name)) {
+ break;
}
}
+ if (j == ks->asym_keys[i].cert_count) {
+ ERR(NULL, "Keystore certificate entry \"%s\" associated with the key \"%s\" not found.",
+ referenced_cert_name, referenced_key_name);
+ return -1;
+ }
- for (i = 0; i < cert_data_count; ++i) {
- cert = tls_load_cert(NULL, cert_data[i]);
- if (!cert || (SSL_CTX_add_extra_chain_cert(tls_ctx, cert) != 1)) {
- ERR(NULL, "Loading the server certificate chain failed (%s).", ERR_reason_error_string(ERR_get_error()));
- ret = -1;
- goto cleanup;
- }
- }
-cleanup:
- for (i = 0; i < cert_path_count; ++i) {
- free(cert_paths[i]);
- }
- free(cert_paths);
- for (i = 0; i < cert_data_count; ++i) {
- free(cert_data[i]);
- }
- free(cert_data);
- /* cert is owned by the SSL_CTX */
-
- return ret;
+ *privkey_data = ks->asym_keys[i].privkey_data;
+ *privkey_type = ks->asym_keys[i].privkey_type;
+ *cert_data = ks->asym_keys[i].certs[j].data;
+ return 0;
}
static int
-nc_tls_ctx_set_server_cert_key(SSL_CTX *tls_ctx, const char *cert_name)
+nc_tls_ctx_set_server_cert_key(SSL_CTX *tls_ctx, struct nc_server_tls_opts *opts)
{
- char *cert_path = NULL, *cert_data = NULL, *privkey_path = NULL, *privkey_data = NULL;
+ char *privkey_data = NULL, *cert_data = NULL;
int ret = 0;
- NC_SSH_KEY_TYPE privkey_type;
+ NC_PRIVKEY_FORMAT privkey_type;
X509 *cert = NULL;
EVP_PKEY *pkey = NULL;
- if (!cert_name) {
- ERR(NULL, "Server certificate not set.");
- return -1;
- } else if (!server_opts.server_cert_clb) {
- ERR(NULL, "Callback for retrieving the server certificate is not set.");
- return -1;
- }
+ NC_CHECK_ARG_RET(NULL, tls_ctx, opts, -1);
- if (server_opts.server_cert_clb(cert_name, server_opts.server_cert_data, &cert_path, &cert_data, &privkey_path,
- &privkey_data, &privkey_type)) {
- ERR(NULL, "Server certificate callback failed.");
- return -1;
- }
-
- /* load the certificate */
- if (cert_path) {
- if (SSL_CTX_use_certificate_file(tls_ctx, cert_path, SSL_FILETYPE_PEM) != 1) {
- ERR(NULL, "Loading the server certificate failed (%s).", ERR_reason_error_string(ERR_get_error()));
- ret = -1;
- goto cleanup;
- }
+ /* get data needed for setting the server cert */
+ if (opts->store == NC_STORE_LOCAL) {
+ /* local definition */
+ cert_data = opts->cert_data;
+ privkey_data = opts->privkey_data;
+ privkey_type = opts->privkey_type;
} else {
- cert = base64der_to_cert(cert_data);
- if (!cert || (SSL_CTX_use_certificate(tls_ctx, cert) != 1)) {
- ERR(NULL, "Loading the server certificate failed (%s).", ERR_reason_error_string(ERR_get_error()));
- ret = -1;
- goto cleanup;
+ /* keystore */
+ ret = nc_server_tls_ks_ref_get_cert_key(opts->key_ref, opts->cert_ref, &privkey_data, &privkey_type, &cert_data);
+ if (ret) {
+ ERR(NULL, "Getting server certificate from the keystore reference \"%s\" failed.", opts->key_ref);
+ return -1;
}
}
+ /* load the cert */
+ cert = base64der_to_cert(cert_data);
+ if (!cert) {
+ ERR(NULL, "Converting certificate data to certificate format failed.");
+ ret = -1;
+ goto cleanup;
+ }
+
+ /* set server cert */
+ ret = SSL_CTX_use_certificate(tls_ctx, cert);
+ if (ret != 1) {
+ ERR(NULL, "Loading the server certificate failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = -1;
+ goto cleanup;
+ }
+
/* load the private key */
- if (privkey_path) {
- if (SSL_CTX_use_PrivateKey_file(tls_ctx, privkey_path, SSL_FILETYPE_PEM) != 1) {
- ERR(NULL, "Loading the server private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
- ret = -1;
- goto cleanup;
- }
- } else {
- pkey = base64der_to_privatekey(privkey_data, nc_keytype2str(privkey_type));
- if (!pkey || (SSL_CTX_use_PrivateKey(tls_ctx, pkey) != 1)) {
- ERR(NULL, "Loading the server private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
- ret = -1;
- goto cleanup;
- }
+ pkey = base64der_to_privatekey(privkey_data, nc_privkey_format_to_str(privkey_type));
+ if (!pkey) {
+ ERR(NULL, "Converting private key data to private key format failed.");
+ ret = -1;
+ goto cleanup;
}
- ret = nc_tls_ctx_set_server_cert_chain(tls_ctx, cert_name);
+ /* set server key */
+ ret = SSL_CTX_use_PrivateKey(tls_ctx, pkey);
+ if (ret != 1) {
+ ERR(NULL, "Loading the server private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = -1;
+ goto cleanup;
+ }
+
+ ret = 0;
cleanup:
X509_free(cert);
EVP_PKEY_free(pkey);
- free(cert_path);
- free(cert_data);
- free(privkey_path);
- free(privkey_data);
return ret;
}
-static void
-tls_store_add_trusted_cert(X509_STORE *cert_store, const char *cert_path, const char *cert_data)
+static int
+tls_store_add_trusted_cert(X509_STORE *cert_store, const char *cert_data)
{
- X509 *cert = tls_load_cert(cert_path, cert_data);
+ X509 *cert;
+
+ cert = base64der_to_cert(cert_data);
if (!cert) {
- return;
+ ERR(NULL, "Loading a trusted certificate (data \"%s\") failed (%s).", cert_data,
+ ERR_reason_error_string(ERR_get_error()));
+ return -1;
}
/* add the trusted certificate */
if (X509_STORE_add_cert(cert_store, cert) != 1) {
ERR(NULL, "Adding a trusted certificate failed (%s).", ERR_reason_error_string(ERR_get_error()));
X509_free(cert);
- return;
+ return -1;
}
X509_free(cert);
+
+ return 0;
}
static int
-nc_tls_store_set_trusted_certs(X509_STORE *cert_store, char **trusted_cert_lists, uint16_t trusted_cert_list_count)
+nc_tls_store_set_trusted_certs(X509_STORE *cert_store, struct nc_cert_grouping *ca_certs)
{
uint16_t i;
- int j;
- char **cert_paths, **cert_data;
- int cert_path_count, cert_data_count;
+ struct nc_certificate *certs;
+ uint16_t cert_count;
- if (!server_opts.trusted_cert_list_clb) {
- ERR(NULL, "Callback for retrieving trusted certificate lists is not set.");
- return -1;
- }
-
- for (i = 0; i < trusted_cert_list_count; ++i) {
- cert_paths = cert_data = NULL;
- cert_path_count = cert_data_count = 0;
- if (server_opts.trusted_cert_list_clb(trusted_cert_lists[i], server_opts.trusted_cert_list_data,
- &cert_paths, &cert_path_count, &cert_data, &cert_data_count)) {
- ERR(NULL, "Trusted certificate list callback for \"%s\" failed.", trusted_cert_lists[i]);
+ if (ca_certs->store == NC_STORE_LOCAL) {
+ /* local definition */
+ certs = ca_certs->certs;
+ cert_count = ca_certs->cert_count;
+ } else {
+ /* truststore */
+ if (nc_server_tls_ts_ref_get_certs(ca_certs->ts_ref, &certs, &cert_count)) {
+ ERR(NULL, "Error getting certificate-authority certificates from the truststore reference \"%s\".", ca_certs->ts_ref);
return -1;
}
+ }
- for (j = 0; j < cert_path_count; ++j) {
- tls_store_add_trusted_cert(cert_store, cert_paths[j], NULL);
- free(cert_paths[j]);
+ for (i = 0; i < cert_count; i++) {
+ if (tls_store_add_trusted_cert(cert_store, certs[i].data)) {
+ return -1;
}
- free(cert_paths);
-
- for (j = 0; j < cert_data_count; ++j) {
- tls_store_add_trusted_cert(cert_store, NULL, cert_data[j]);
- free(cert_data[j]);
- }
- free(cert_data);
}
return 0;
}
static int
+nc_server_tls_crl_path(struct nc_session *session, const char *crl_path, X509_STORE *store)
+{
+ int ret = 0;
+ X509_CRL *crl = NULL;
+ FILE *f;
+
+ f = fopen(crl_path, "r");
+ if (!f) {
+ ERR(session, "Unable to open CRL file \"%s\".", crl_path);
+ return -1;
+ }
+
+ /* try DER first */
+ crl = d2i_X509_CRL_fp(f, NULL);
+ if (crl) {
+ /* success */
+ goto ok;
+ }
+
+ /* DER failed, try PEM */
+ rewind(f);
+ crl = PEM_read_X509_CRL(f, NULL, NULL, NULL);
+ if (!crl) {
+ ERR(session, "Reading CRL from file \"%s\" failed.", crl_path);
+ ret = -1;
+ goto cleanup;
+ }
+
+ok:
+ ret = X509_STORE_add_crl(store, crl);
+ if (!ret) {
+ ERR(session, "Error adding CRL to store (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = -1;
+ goto cleanup;
+ }
+ /* ok */
+ ret = 0;
+
+cleanup:
+ fclose(f);
+ X509_CRL_free(crl);
+ return ret;
+}
+
+static size_t
+nc_server_tls_curl_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
+{
+ struct nc_curl_data *data;
+
+ size = nmemb;
+
+ data = (struct nc_curl_data *)userdata;
+
+ data->data = nc_realloc(data->data, data->size + size);
+ if (!data->data) {
+ ERRMEM;
+ return 0;
+ }
+
+ memcpy(&data->data[data->size], ptr, size);
+ data->size += size;
+
+ return size;
+}
+
+static int
+nc_server_tls_curl_init(struct nc_session *session, CURL **handle, struct nc_curl_data *data)
+{
+ NC_CHECK_ARG_RET(session, handle, data, -1);
+
+ *handle = NULL;
+
+ *handle = curl_easy_init();
+ if (!*handle) {
+ ERR(session, "Initializing CURL failed.");
+ return -1;
+ }
+
+ if (curl_easy_setopt(*handle, CURLOPT_WRITEFUNCTION, nc_server_tls_curl_cb)) {
+ ERR(session, "Setting curl callback failed.");
+ return -1;
+ }
+
+ if (curl_easy_setopt(*handle, CURLOPT_WRITEDATA, data)) {
+ ERR(session, "Setting curl callback data failed.");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+nc_server_tls_curl_fetch(struct nc_session *session, CURL *handle, const char *url)
+{
+ char err_buf[CURL_ERROR_SIZE];
+
+ /* set uri */
+ if (curl_easy_setopt(handle, CURLOPT_URL, url)) {
+ ERR(session, "Setting URI \"%s\" to download CRL from failed.", url);
+ return -1;
+ }
+
+ /* set err buf */
+ curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, err_buf);
+
+ /* download */
+ if (curl_easy_perform(handle)) {
+ ERR(session, "Downloading CRL from \"%s\" failed (%s).", url, err_buf);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+nc_server_tls_add_crl_to_store(struct nc_session *session, struct nc_curl_data *downloaded, X509_STORE *store)
+{
+ int ret = 0;
+ X509_CRL *crl = NULL;
+ BIO *bio = NULL;
+
+ /* try DER first */
+ crl = d2i_X509_CRL(NULL, (const unsigned char **) &downloaded->data, downloaded->size);
+ if (crl) {
+ /* it was DER */
+ goto ok;
+ }
+
+ /* DER failed, try PEM next */
+ bio = BIO_new_mem_buf(downloaded->data, downloaded->size);
+ if (!bio) {
+ ERR(session, "Creating new bio failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = -1;
+ goto cleanup;
+ }
+
+ /* try to parse PEM from the downloaded data */
+ crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL);
+ if (!crl) {
+ ERR(session, "Reading downloaded CRL failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = -1;
+ goto cleanup;
+ }
+
+ok:
+ /* we obtained the CRL, now add it to the CRL store */
+ ret = X509_STORE_add_crl(store, crl);
+ if (!ret) {
+ ERR(session, "Error adding CRL to store (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = -1;
+ goto cleanup;
+ }
+ /* ok */
+ ret = 0;
+
+cleanup:
+ X509_CRL_free(crl);
+ BIO_free(bio);
+ return ret;
+}
+
+static int
+nc_server_tls_crl_url(struct nc_session *session, const char *url, X509_STORE *store)
+{
+ int ret = 0;
+ CURL *handle = NULL;
+ struct nc_curl_data downloaded = {0};
+
+ /* init curl */
+ ret = nc_server_tls_curl_init(session, &handle, &downloaded);
+ if (ret) {
+ goto cleanup;
+ }
+
+ VRB(session, "Downloading CRL from \"%s\".", url);
+
+ /* download the CRL */
+ ret = nc_server_tls_curl_fetch(session, handle, url);
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* convert the downloaded data to CRL and add it to the store */
+ ret = nc_server_tls_add_crl_to_store(session, &downloaded, store);
+ if (ret) {
+ goto cleanup;
+ }
+
+cleanup:
+ curl_easy_cleanup(handle);
+ return ret;
+}
+
+static int
+nc_server_tls_crl_cert_ext(struct nc_session *session, X509_STORE *cert_store, X509_STORE *crl_store)
+{
+ int ret = 0, i, j, k, gtype;
+ CURL *handle = NULL;
+ struct nc_curl_data downloaded = {0};
+
+ STACK_OF(X509_OBJECT) * objs;
+ X509_OBJECT *obj;
+ X509 *cert;
+
+ STACK_OF(DIST_POINT) * dist_points;
+ DIST_POINT *dist_point;
+ GENERAL_NAMES *general_names;
+ GENERAL_NAME *general_name;
+ ASN1_STRING *asn_string_uri;
+ const char *crl_distpoint_uri;
+
+ /* init curl */
+ ret = nc_server_tls_curl_init(session, &handle, &downloaded);
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* treat all entries in the cert_store as X509_OBJECTs */
+ objs = X509_STORE_get0_objects(cert_store);
+ if (!objs) {
+ ERR(session, "Getting certificates from store failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = -1;
+ goto cleanup;
+ }
+
+ /* iterate over all the CAs */
+ for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
+ obj = sk_X509_OBJECT_value(objs, i);
+ cert = X509_OBJECT_get0_X509(obj);
+ if (!cert) {
+ /* the object on this index was not a certificate */
+ continue;
+ }
+
+ /* get all the distribution points for this CA */
+ dist_points = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL);
+
+ /* iterate over all the dist points (there can be multiple for a single cert) */
+ for (j = 0; j < sk_DIST_POINT_num(dist_points); j++) {
+ dist_point = sk_DIST_POINT_value(dist_points, j);
+ if (!dist_point) {
+ continue;
+ }
+ general_names = dist_point->distpoint->name.fullname;
+
+ /* iterate over all the GeneralesNames in the distribution point */
+ for (k = 0; k < sk_GENERAL_NAME_num(general_names); k++) {
+ general_name = sk_GENERAL_NAME_value(general_names, k);
+ asn_string_uri = GENERAL_NAME_get0_value(general_name, >ype);
+
+ /* check if the general name is a URI and has a valid length */
+ if ((gtype != GEN_URI) || (ASN1_STRING_length(asn_string_uri) <= 6)) {
+ continue;
+ }
+
+ crl_distpoint_uri = (const char *) ASN1_STRING_get0_data(asn_string_uri);
+
+ VRB(session, "Downloading CRL from \"%s\".", crl_distpoint_uri);
+
+ /* download the CRL */
+ ret = nc_server_tls_curl_fetch(session, handle, crl_distpoint_uri);
+ if (ret) {
+ /* failed to download the CRL from this entry, try th next */
+ continue;
+ }
+
+ /* convert the downloaded data to CRL and add it to the store */
+ ret = nc_server_tls_add_crl_to_store(session, &downloaded, crl_store);
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* the CRL was downloaded, no need to download it again using different protocol */
+ break;
+ }
+ }
+ }
+
+cleanup:
+ curl_easy_cleanup(handle);
+ return ret;
+}
+
+static int
+nc_tls_store_set_crl(struct nc_session *session, struct nc_server_tls_opts *opts, X509_STORE *store)
+{
+ if (!opts->crl_store) {
+ /* first call on this endpoint */
+ opts->crl_store = X509_STORE_new();
+ if (!opts->crl_store) {
+ ERRMEM;
+ goto fail;
+ }
+ }
+
+ if (opts->crl_path) {
+ if (nc_server_tls_crl_path(session, opts->crl_path, opts->crl_store)) {
+ goto fail;
+ }
+ } else if (opts->crl_url) {
+ if (nc_server_tls_crl_url(session, opts->crl_url, opts->crl_store)) {
+ goto fail;
+ }
+ } else {
+ if (nc_server_tls_crl_cert_ext(session, store, opts->crl_store)) {
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ return -1;
+}
+
+static int
nc_server_tls_accept_check(int accept_ret, struct nc_session *session)
{
int verify;
@@ -1872,62 +1422,67 @@
}
int
-nc_accept_tls_session(struct nc_session *session, int sock, int timeout)
+nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opts, int sock, int timeout)
{
X509_STORE *cert_store;
SSL_CTX *tls_ctx;
- X509_LOOKUP *lookup;
- struct nc_server_tls_opts *opts;
int ret;
struct timespec ts_timeout;
- opts = session->data;
-
/* SSL_CTX */
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
tls_ctx = SSL_CTX_new(TLS_server_method());
-#else
- tls_ctx = SSL_CTX_new(TLSv1_2_server_method());
-#endif
+
if (!tls_ctx) {
ERR(session, "Failed to create TLS context.");
goto error;
}
+
SSL_CTX_set_verify(tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_tlsclb_verify);
- if (nc_tls_ctx_set_server_cert_key(tls_ctx, opts->server_cert)) {
+ if (nc_tls_ctx_set_server_cert_key(tls_ctx, opts)) {
goto error;
}
/* X509_STORE, managed (freed) with the context */
cert_store = X509_STORE_new();
- SSL_CTX_set_cert_store(tls_ctx, cert_store);
-
- if (nc_tls_store_set_trusted_certs(cert_store, opts->trusted_cert_lists, opts->trusted_cert_list_count)) {
+ if (!cert_store) {
+ ERR(session, "Creating certificate store failed (%s).", ERR_reason_error_string(ERR_get_error()));
goto error;
}
- if (opts->trusted_ca_file) {
- lookup = X509_STORE_add_lookup(cert_store, X509_LOOKUP_file());
- if (!lookup) {
- ERR(session, "Failed to add a lookup method.");
- goto error;
- }
+ /* set end-entity certs as cert store data, retrieve them if verification fails later */
+ ret = X509_STORE_set_ex_data(cert_store, 1, &opts->ee_certs);
+ if (!ret) {
+ ERR(session, "Setting certificate store data failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ goto error;
+ }
- if (X509_LOOKUP_load_file(lookup, opts->trusted_ca_file, X509_FILETYPE_PEM) != 1) {
- ERR(session, "Failed to add a trusted cert file (%s).", ERR_reason_error_string(ERR_get_error()));
+ /* do the same for referenced endpoint's end entity certs */
+ if (opts->endpt_client_ref) {
+ ret = X509_STORE_set_ex_data(cert_store, 2, &opts->endpt_client_ref->opts.tls->ee_certs);
+ if (!ret) {
+ ERR(session, "Setting certificate store data failed (%s).", ERR_reason_error_string(ERR_get_error()));
goto error;
}
}
- if (opts->trusted_ca_dir) {
- lookup = X509_STORE_add_lookup(cert_store, X509_LOOKUP_hash_dir());
- if (!lookup) {
- ERR(session, "Failed to add a lookup method.");
+ /* set store to the context */
+ SSL_CTX_set_cert_store(tls_ctx, cert_store);
+
+ /* set certificate authority certs */
+ if (nc_tls_store_set_trusted_certs(cert_store, &opts->ca_certs)) {
+ goto error;
+ }
+
+ /* set referenced endpoint's CA certs if set */
+ if (opts->endpt_client_ref) {
+ if (nc_tls_store_set_trusted_certs(cert_store, &opts->endpt_client_ref->opts.tls->ca_certs)) {
goto error;
}
+ }
- if (X509_LOOKUP_add_dir(lookup, opts->trusted_ca_dir, X509_FILETYPE_PEM) != 1) {
- ERR(session, "Failed to add a trusted cert directory (%s).", ERR_reason_error_string(ERR_get_error()));
+ /* set Certificate Revocation List if configured */
+ if (opts->crl_path || opts->crl_url || opts->crl_cert_ext) {
+ if (nc_tls_store_set_crl(session, opts, cert_store)) {
goto error;
}
}
@@ -1944,6 +1499,30 @@
goto error;
}
+ /* set TLS versions for the current SSL session */
+ if (opts->tls_versions) {
+ if (!(opts->tls_versions & NC_TLS_VERSION_10)) {
+ SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1);
+ }
+ if (!(opts->tls_versions & NC_TLS_VERSION_11)) {
+ SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_1);
+ }
+ if (!(opts->tls_versions & NC_TLS_VERSION_12)) {
+ SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_2);
+ }
+ if (!(opts->tls_versions & NC_TLS_VERSION_13)) {
+ SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_3);
+ }
+ }
+
+ /* set TLS cipher suites */
+ if (opts->ciphers) {
+ /* set for TLS1.2 and lower */
+ SSL_set_cipher_list(session->ti.tls, opts->ciphers);
+ /* set for TLS1.3 */
+ SSL_set_ciphersuites(session->ti.tls, opts->ciphers);
+ }
+
SSL_set_fd(session->ti.tls, sock);
sock = -1;
SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);