session CHANGE api completely rewritten
It was simply wrong before, global settings
for all listening connections is not enough.
diff --git a/src/session_client_tls.c b/src/session_client_tls.c
index cb1bc9b..f999c21 100644
--- a/src/session_client_tls.c
+++ b/src/session_client_tls.c
@@ -33,7 +33,10 @@
#include "libnetconf.h"
-static struct nc_tls_client_opts tls_opts;
+static struct nc_client_tls_opts tls_opts;
+static struct nc_client_tls_opts tls_ch_opts;
+
+static int tlsauth_ch;
static int
tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
@@ -47,11 +50,19 @@
EVP_PKEY *pubkey;
int i, n, rc;
ASN1_TIME *next_update = NULL;
+ struct nc_client_tls_opts *opts;
if (!preverify_ok) {
return 0;
}
+ opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
+
+ if (!opts->crl_store) {
+ /* nothing to check */
+ return 1;
+ }
+
cert = X509_STORE_CTX_get_current_cert(x509_ctx);
subject = X509_get_subject_name(cert);
issuer = X509_get_issuer_name(cert);
@@ -59,7 +70,7 @@
/* 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, tls_opts.tls_store, NULL, NULL);
+ X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
X509_STORE_CTX_cleanup(&store_ctx);
crl = obj.data.crl;
@@ -97,7 +108,7 @@
/* 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, tls_opts.tls_store, NULL, NULL);
+ X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
X509_STORE_CTX_cleanup(&store_ctx);
crl = obj.data.crl;
@@ -119,94 +130,280 @@
return 1; /* success */
}
-API int
-nc_tls_client_init(const char *client_cert, const char *client_key, const char *ca_file, const char *ca_dir,
- const char *crl_file, const char *crl_dir)
+static int
+_nc_client_tls_set_cert_key(const char *client_cert, const char *client_key, int ch)
{
- const char *key_ = client_key;
- X509_LOOKUP *lookup;
-
- if (tls_opts.tls_ctx) {
- VRB("TLS context reinitialization.");
- SSL_CTX_free(tls_opts.tls_ctx);
- tls_opts.tls_ctx = NULL;
- }
+ struct nc_client_tls_opts *opts;
if (!client_cert) {
ERRARG;
return -1;
}
- /* prepare global SSL context, allow only mandatory TLS 1.2 */
- if (!(tls_opts.tls_ctx = SSL_CTX_new(TLSv1_2_client_method()))) {
- ERR("Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error()));
- return -1;
- }
+ opts = (ch ? &tls_ch_opts : &tls_opts);
- if (crl_file || crl_dir) {
- /* set the revocation store with the correct paths for the callback */
- tls_opts.tls_store = X509_STORE_new();
- tls_opts.tls_store->cache = 0;
+ free(opts->cert_path);
+ free(opts->key_path);
- if (crl_file) {
- if (!(lookup = X509_STORE_add_lookup(tls_opts.tls_store, X509_LOOKUP_file()))) {
- ERR("Failed to add lookup method to CRL checking.");
- return -1;
- }
- if (X509_LOOKUP_add_dir(lookup, crl_file, X509_FILETYPE_PEM) != 1) {
- ERR("Failed to add the revocation lookup file \"%s\".", crl_file);
- return -1;
- }
+ if (client_cert) {
+ opts->cert_path = strdup(client_cert);
+ if (!opts->cert_path) {
+ ERRMEM;
+ return -1;
}
-
- if (crl_dir) {
- if (!(lookup = X509_STORE_add_lookup(tls_opts.tls_store, X509_LOOKUP_hash_dir()))) {
- ERR("Failed to add lookup method to CRL checking.");
- return -1;
- }
- if (X509_LOOKUP_add_dir(lookup, crl_dir, X509_FILETYPE_PEM) != 1) {
- ERR("Failed to add the revocation lookup directory \"%s\".", crl_dir);
- return -1;
- }
- }
-
- SSL_CTX_set_verify(tls_opts.tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback);
} else {
- /* CRL checking will be skipped */
- SSL_CTX_set_verify(tls_opts.tls_ctx, SSL_VERIFY_PEER, NULL);
+ opts->cert_path = NULL;
}
- /* get peer certificate */
- if (SSL_CTX_use_certificate_file(tls_opts.tls_ctx, client_cert, SSL_FILETYPE_PEM) != 1) {
- ERR("Loading a peer certificate from \'%s\' failed (%s).", client_cert, ERR_reason_error_string(ERR_get_error()));
- return -1;
+ if (client_key) {
+ opts->key_path = strdup(client_key);
+ if (!opts->cert_path) {
+ ERRMEM;
+ return -1;
+ }
+ } else {
+ opts->key_path = NULL;
}
- if (!key_) {
- /*
- * if the file with private key not specified, expect that the private
- * key is stored altogether with the certificate
- */
- key_ = client_cert;
- }
- if (SSL_CTX_use_PrivateKey_file(tls_opts.tls_ctx, key_, SSL_FILETYPE_PEM) != 1) {
- ERR("Loading the client certificate from \'%s\' failed (%s).", key_, ERR_reason_error_string(ERR_get_error()));
- return -1;
- }
-
- if (!SSL_CTX_load_verify_locations(tls_opts.tls_ctx, ca_file, ca_dir)) {
- ERR("Failed to load the locations of trusted CA certificates (%s).", ERR_reason_error_string(ERR_get_error()));
- return -1;
- }
+ opts->tls_ctx_change = 1;
return 0;
}
-API void
-nc_tls_client_destroy(void)
+API int
+nc_client_tls_set_cert_key(const char *client_cert, const char *client_key)
{
- SSL_CTX_free(tls_opts.tls_ctx);
- tls_opts.tls_ctx = NULL;
+ return _nc_client_tls_set_cert_key(client_cert, client_key, 0);
+}
+
+API int
+nc_client_tls_ch_set_cert_key(const char *client_cert, const char *client_key)
+{
+ return _nc_client_tls_set_cert_key(client_cert, client_key, 1);
+}
+
+static int
+_nc_client_tls_set_trusted_ca_certs(const char *ca_file, const char *ca_dir, int ch)
+{
+ struct nc_client_tls_opts *opts;
+
+ if (!ca_file && !ca_dir) {
+ ERRARG;
+ return -1;
+ }
+
+ opts = (ch ? &tls_ch_opts : &tls_opts);
+
+ free(opts->ca_file);
+ free(opts->ca_dir);
+
+ if (ca_file) {
+ opts->ca_file = strdup(ca_file);
+ if (!opts->ca_file) {
+ ERRMEM;
+ return -1;
+ }
+ } else {
+ opts->ca_file = NULL;
+ }
+
+ if (ca_dir) {
+ opts->ca_dir = strdup(ca_dir);
+ if (!opts->ca_dir) {
+ ERRMEM;
+ return -1;
+ }
+ } else {
+ opts->ca_dir = NULL;
+ }
+
+ opts->tls_ctx_change = 1;
+
+ return 0;
+}
+
+API int
+nc_client_tls_set_trusted_ca_certs(const char *ca_file, const char *ca_dir)
+{
+ return _nc_client_tls_set_trusted_ca_certs(ca_file, ca_dir, 0);
+}
+
+API int
+nc_client_tls_ch_set_trusted_ca_certs(const char *ca_file, const char *ca_dir)
+{
+ return _nc_client_tls_set_trusted_ca_certs(ca_file, ca_dir, 1);
+}
+
+static int
+_nc_client_tls_set_crl(const char *crl_file, const char *crl_dir, int ch)
+{
+ struct nc_client_tls_opts *opts;
+
+ if (!crl_file && !crl_dir) {
+ ERRARG;
+ return -1;
+ }
+
+ opts = (ch ? &tls_ch_opts : &tls_opts);
+
+ free(opts->crl_file);
+ free(opts->crl_dir);
+
+ if (crl_file) {
+ opts->crl_file = strdup(crl_file);
+ if (!opts->crl_file) {
+ ERRMEM;
+ return -1;
+ }
+ } else {
+ opts->crl_file = NULL;
+ }
+
+ if (crl_dir) {
+ opts->crl_dir = strdup(crl_dir);
+ if (!opts->crl_dir) {
+ ERRMEM;
+ return -1;
+ }
+ } else {
+ opts->crl_dir = NULL;
+ }
+
+ opts->crl_store_change = 1;
+
+ return 0;
+}
+
+API int
+nc_client_tls_set_crl(const char *crl_file, const char *crl_dir)
+{
+ return _nc_client_tls_set_crl(crl_file, crl_dir, 0);
+}
+
+API int
+nc_client_tls_ch_set_crl(const char *crl_file, const char *crl_dir)
+{
+ return _nc_client_tls_set_crl(crl_file, crl_dir, 1);
+}
+
+API int
+nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port)
+{
+ return nc_client_ch_add_bind_listen(address, port, NC_TI_OPENSSL);
+}
+
+API int
+nc_client_tls_ch_del_bind(const char *address, uint16_t port)
+{
+ return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL);
+}
+
+API void
+nc_client_tls_destroy(void)
+{
+ int count = 0;
+ struct nc_client_tls_opts *opts;
+
+repeat:
+ if (count == 0) {
+ opts = &tls_ch_opts;
+ } else if (count == 1) {
+ opts = &tls_opts;
+ } else {
+ return;
+ }
+
+ free(opts->cert_path);
+ free(opts->key_path);
+ free(opts->ca_file);
+ free(opts->ca_dir);
+ SSL_CTX_free(opts->tls_ctx);
+
+ free(opts->crl_file);
+ free(opts->crl_dir);
+ X509_STORE_free(opts->crl_store);
+
+ ++count;
+ goto repeat;
+}
+
+static int
+nc_client_tls_opts_update(int ch)
+{
+ char *key;
+ X509_LOOKUP *lookup;
+ struct nc_client_tls_opts *opts;
+
+ opts = (ch ? &tls_ch_opts : &tls_opts);
+
+ if (!opts->tls_ctx || opts->tls_ctx_change) {
+ SSL_CTX_free(tls_opts.tls_ctx);
+
+ /* prepare global SSL context, allow only mandatory TLS 1.2 */
+ if (!(opts->tls_ctx = SSL_CTX_new(TLSv1_2_client_method()))) {
+ ERR("Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+ SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback);
+
+ /* get peer certificate */
+ if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) {
+ ERR("Loading the client certificate from \'%s\' failed (%s).", opts->cert_path, ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+
+ /* if the file with private key not specified, expect that the private key is stored with the certificate */
+ if (!opts->key_path) {
+ key = opts->cert_path;
+ } else {
+ key = opts->key_path;
+ }
+ if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) {
+ ERR("Loading the client priavte key from \'%s\' failed (%s).", key, ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+
+ if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) {
+ ERR("Failed to load the locations of trusted CA certificates (%s).", ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+ }
+
+ if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) {
+ /* set the revocation store with the correct paths for the callback */
+ X509_STORE_free(opts->crl_store);
+
+ opts->crl_store = X509_STORE_new();
+ if (!opts->crl_store) {
+ ERR("Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+ opts->crl_store->cache = 0;
+
+ if (opts->crl_file) {
+ if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()))) {
+ ERR("Failed to add lookup method to CRL checking.");
+ return -1;
+ }
+ if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) {
+ ERR("Failed to add the revocation lookup file \"%s\".", opts->crl_file);
+ return -1;
+ }
+ }
+
+ if (opts->crl_dir) {
+ if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) {
+ ERR("Failed to add lookup method to CRL checking.");
+ return -1;
+ }
+ if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) {
+ ERR("Failed to add the revocation lookup directory \"%s\".", opts->crl_dir);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
}
API struct nc_session *
@@ -215,8 +412,7 @@
struct nc_session *session = NULL;
int sock, verify;
- /* was init called? */
- if (!tls_opts.tls_ctx) {
+ if (!tls_opts.cert_path || (!tls_opts.ca_file && !tls_opts.ca_dir)) {
ERRARG;
return NULL;
}
@@ -230,6 +426,11 @@
port = NC_PORT_TLS;
}
+ /* create/update TLS structures */
+ if (nc_client_tls_opts_update(0)) {
+ return NULL;
+ }
+
/* prepare session structure */
session = calloc(1, sizeof *session);
if (!session) {
@@ -265,6 +466,7 @@
SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
/* connect and perform the handshake */
+ tlsauth_ch = 0;
if (SSL_connect(session->ti.tls) != 1) {
ERR("Connecting over TLS failed (%s).", ERR_reason_error_string(ERR_get_error()));
goto fail;
@@ -366,31 +568,19 @@
return NULL;
}
-API struct nc_session *
-nc_callhome_accept_tls(const char *host, uint16_t port, int timeout, struct ly_ctx *ctx)
+struct nc_session *
+nc_accept_callhome_sock_tls(int sock, const char *host, uint16_t port, struct ly_ctx *ctx)
{
- int listen_sock, sock, verify;
- char *server_host;
+ int verify;
SSL *tls;
struct nc_session *session;
- if (!port) {
- port = NC_PORT_CH_TLS;
- }
-
- listen_sock = nc_sock_listen(host, port);
- if (listen_sock < 0) {
+ if (nc_client_tls_opts_update(1)) {
+ close(sock);
return NULL;
}
- sock = nc_sock_accept(listen_sock, timeout, &server_host, NULL);
- close(listen_sock);
-
- if (sock < 0) {
- return NULL;
- }
-
- if (!(tls = SSL_new(tls_opts.tls_ctx))) {
+ if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) {
ERR("Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
close(sock);
return NULL;
@@ -402,6 +592,7 @@
SSL_set_mode(tls, SSL_MODE_AUTO_RETRY);
/* connect and perform the handshake */
+ tlsauth_ch = 1;
if (SSL_connect(tls) != 1) {
ERR("Connecting over TLS failed (%s).", ERR_reason_error_string(ERR_get_error()));
SSL_free(tls);
@@ -421,7 +612,7 @@
session = nc_connect_libssl(tls, ctx);
if (session) {
/* store information into session and the dictionary */
- session->host = lydict_insert_zc(session->ctx, server_host);
+ session->host = lydict_insert(session->ctx, host, 0);
session->port = port;
session->username = lydict_insert(session->ctx, "certificate-based", 0);
}