Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@ set(CMAKE_MACOSX_RPATH TRUE)
# micro version is changed with a set of small changes or bugfixes anywhere in the project.
set(LIBNETCONF2_MAJOR_VERSION 4)
set(LIBNETCONF2_MINOR_VERSION 4)
set(LIBNETCONF2_MICRO_VERSION 9)
set(LIBNETCONF2_MICRO_VERSION 10)
set(LIBNETCONF2_VERSION ${LIBNETCONF2_MAJOR_VERSION}.${LIBNETCONF2_MINOR_VERSION}.${LIBNETCONF2_MICRO_VERSION})

# Version of the library
# Major version is changed with every backward non-compatible API/ABI change in the library, minor version changes
# with backward compatible change and micro version is connected with any internal change of the library.
set(LIBNETCONF2_MAJOR_SOVERSION 5)
set(LIBNETCONF2_MINOR_SOVERSION 4)
set(LIBNETCONF2_MICRO_SOVERSION 8)
set(LIBNETCONF2_MICRO_SOVERSION 9)
set(LIBNETCONF2_SOVERSION_FULL ${LIBNETCONF2_MAJOR_SOVERSION}.${LIBNETCONF2_MINOR_SOVERSION}.${LIBNETCONF2_MICRO_SOVERSION})
set(LIBNETCONF2_SOVERSION ${LIBNETCONF2_MAJOR_SOVERSION})

Expand Down
163 changes: 88 additions & 75 deletions src/session.c
Original file line number Diff line number Diff line change
Expand Up @@ -1919,6 +1919,53 @@ nc_session_curl_fetch(CURL *handle, const char *url)
return 0;
}

/**
* @brief Check if a URI has already been processed, add it to the seen list if not.
*
* @param[in] uri URI to check.
* @param[in,out] seen_uris Array of already seen URIs.
* @param[in,out] seen_count Number of seen URIs.
* @return 1 if already seen, 0 if added (not seen before), -1 on error.
*/
static int
nc_session_uri_seen(const char *uri, char ***seen_uris, int *seen_count)
{
int i;
char **tmp;

for (i = 0; i < *seen_count; i++) {
if (!strcmp((*seen_uris)[i], uri)) {
return 1;
}
}

tmp = nc_realloc(*seen_uris, (*seen_count + 1) * sizeof **seen_uris);
NC_CHECK_ERRMEM_RET(!tmp, -1);
*seen_uris = tmp;
(*seen_uris)[*seen_count] = strdup(uri);
NC_CHECK_ERRMEM_RET(!(*seen_uris)[*seen_count], -1);
(*seen_count)++;

return 0;
}

/**
* @brief Free the seen URIs array.
*
* @param[in] seen_uris Array of seen URIs.
* @param[in] seen_count Number of seen URIs.
*/
static void
nc_session_seen_uris_free(char **seen_uris, int seen_count)
{
int i;

for (i = 0; i < seen_count; i++) {
free(seen_uris[i]);
}
free(seen_uris);
}

/**
* @brief Initialize CURL handle for downloading CRL.
*
Expand All @@ -1939,6 +1986,18 @@ nc_session_curl_init(CURL **handle, struct nc_curl_data *data)
return 1;
}

/* limit connection phase to avoid long delays on unreachable CRL hosts */
if (curl_easy_setopt(*handle, CURLOPT_CONNECTTIMEOUT_MS, NC_CURL_CONNECT_TIMEOUT_MS)) {
ERR(NULL, "Setting curl connection timeout failed.");
return 1;
}

/* do not use signals for timeouts, required for thread safety */
if (curl_easy_setopt(*handle, CURLOPT_NOSIGNAL, 1L)) {
ERR(NULL, "Setting CURLOPT_NOSIGNAL failed.");
return 1;
}

if (curl_easy_setopt(*handle, CURLOPT_WRITEFUNCTION, nc_session_curl_cb)) {
ERR(NULL, "Setting curl callback failed.");
return 1;
Expand All @@ -1952,74 +2011,6 @@ nc_session_curl_init(CURL **handle, struct nc_curl_data *data)
return 0;
}

int
nc_session_tls_crl_from_cert_ext_fetch(void *leaf_cert, void *cert_store, void **crl_store)
{
int ret = 0, uri_count = 0, i;
CURL *handle = NULL;
struct nc_curl_data downloaded = {0};
char **uris = NULL;
void *crl_store_aux = NULL;

*crl_store = NULL;

crl_store_aux = nc_tls_crl_store_new_wrap();
if (!crl_store_aux) {
goto cleanup;
}

/* init curl */
ret = nc_session_curl_init(&handle, &downloaded);
if (ret) {
goto cleanup;
}

/* get all the uris we can, even though some may point to the same CRL */
ret = nc_server_tls_get_crl_distpoint_uris_wrap(leaf_cert, cert_store, &uris, &uri_count);
if (ret) {
goto cleanup;
}

if (!uri_count) {
/* no CRL distribution points, nothing to download */
goto cleanup;
}

for (i = 0; i < uri_count; i++) {
VRB(NULL, "Downloading CRL from \"%s\".", uris[i]);
ret = nc_session_curl_fetch(handle, uris[i]);
if (ret) {
/* failed to download the CRL from this entry, try the next entry */
WRN(NULL, "Failed to fetch CRL from \"%s\".", uris[i]);
continue;
}

/* convert the downloaded data to CRL and add it to the store */
ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, crl_store_aux);

/* free the downloaded data */
free(downloaded.data);
downloaded.data = NULL;
downloaded.size = 0;

if (ret) {
goto cleanup;
}
}

*crl_store = crl_store_aux;
crl_store_aux = NULL;

cleanup:
for (i = 0; i < uri_count; i++) {
free(uris[i]);
}
free(uris);
curl_easy_cleanup(handle);
nc_tls_crl_store_destroy_wrap(crl_store_aux);
return ret;
}

/**
* @brief Download CRLs for certificates in the peer's chain and merge them into crl_store.
*
Expand All @@ -2031,11 +2022,11 @@ nc_session_tls_crl_from_cert_ext_fetch(void *leaf_cert, void *cert_store, void *
static int
nc_session_tls_crl_fetch_for_peer_chain(void *peer_chain, void *cert_store, void *crl_store)
{
int i, ret = 0, uri_count = 0, j;
int i, ret = 0, uri_count = 0, j, seen_count = 0, uri_seen;
void *cert = NULL;
CURL *handle = NULL;
struct nc_curl_data downloaded = {0};
char **uris = NULL;
char **uris = NULL, **seen_uris = NULL;

/* init curl */
ret = nc_session_curl_init(&handle, &downloaded);
Expand All @@ -2060,6 +2051,20 @@ nc_session_tls_crl_fetch_for_peer_chain(void *peer_chain, void *cert_store, void
}

for (j = 0; j < uri_count; j++) {
uri_seen = nc_session_uri_seen(uris[j], &seen_uris, &seen_count);
if (uri_seen == 1) {
/* already downloaded this URI, skip */
continue;
} else if (uri_seen == -1) {
ret = 1;
for (j++; j < uri_count; j++) {
free(uris[j]);
}
free(uris);
uris = NULL;
goto cleanup;
}

VRB(NULL, "Downloading CRL from \"%s\".", uris[j]);
ret = nc_session_curl_fetch(handle, uris[j]);
if (ret) {
Expand Down Expand Up @@ -2093,6 +2098,7 @@ nc_session_tls_crl_fetch_for_peer_chain(void *peer_chain, void *cert_store, void
cleanup:
curl_easy_cleanup(handle);
free(downloaded.data);
nc_session_seen_uris_free(seen_uris, seen_count);
if (uris) {
for (i = 0; i < uri_count; i++) {
free(uris[i]);
Expand All @@ -2103,26 +2109,31 @@ nc_session_tls_crl_fetch_for_peer_chain(void *peer_chain, void *cert_store, void
}

int
nc_session_tls_crl_verify_post_handshake(void *tls_session, void *cert_store, void *crl_store)
nc_session_tls_crl_verify_post_handshake(void *tls_session, void *cert_store)
{
int ret = 0;
void *peer_chain = NULL;
void *peer_chain = NULL, *crl_store = NULL;

peer_chain = nc_tls_get_peer_cert_chain_wrap(tls_session);
if (!peer_chain) {
/* no peer certificate, nothing to verify */
return 0;
}

if (!crl_store && !cert_store) {
/* no CRL store and no cert store, nothing to verify */
if (!cert_store) {
/* no cert store, nothing to verify */
return 0;
}

crl_store = nc_tls_crl_store_for_post_handshake_wrap(cert_store);
if (!crl_store) {
return 1;
}

/* download CRLs for peer certificates and add them to the CRL store */
ret = nc_session_tls_crl_fetch_for_peer_chain(peer_chain, cert_store, crl_store);
if (ret) {
return ret;
goto cleanup;
}

/* verify the peer chain against the CRLs */
Expand All @@ -2131,6 +2142,8 @@ nc_session_tls_crl_verify_post_handshake(void *tls_session, void *cert_store, vo
ERR(NULL, "Post-handshake CRL verification failed.");
}

cleanup:
nc_tls_crl_store_post_handshake_free_wrap(crl_store);
return ret;
}

Expand Down
17 changes: 5 additions & 12 deletions src/session_client_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,9 @@ nc_client_tls_session_new(int sock, const char *host, struct nc_client_tls_opts
{
int ret = 0;
struct timespec ts_timeout;
void *tls_session, *tls_cfg, *cli_cert, *cli_pkey, *cert_store, *crl_store;
void *tls_session, *tls_cfg, *cli_cert, *cli_pkey, *cert_store;

tls_session = tls_cfg = cli_cert = cli_pkey = cert_store = crl_store = NULL;
tls_session = tls_cfg = cli_cert = cli_pkey = cert_store = NULL;

/* prepare TLS context from which a session will be created */
tls_cfg = nc_tls_config_new_wrap(NC_CLIENT);
Expand All @@ -254,23 +254,18 @@ nc_client_tls_session_new(int sock, const char *host, struct nc_client_tls_opts
goto fail;
}

/* load CRLs from set certificates' extensions */
if (nc_session_tls_crl_from_cert_ext_fetch(cli_cert, cert_store, &crl_store)) {
goto fail;
}

/* set client's verify mode flags */
if (nc_client_tls_set_verify_wrap(tls_cfg)) {
goto fail;
}

/* init TLS context and store data which may be needed later in it */
if (nc_tls_init_ctx_wrap(cli_cert, cli_pkey, cert_store, crl_store, NULL, tls_ctx)) {
if (nc_tls_init_ctx_wrap(cli_cert, cli_pkey, cert_store, NULL, tls_ctx)) {
goto fail;
}

/* memory is managed by context now */
cli_cert = cli_pkey = cert_store = crl_store = NULL;
cli_cert = cli_pkey = cert_store = NULL;

/* setup config from ctx */
if (nc_tls_setup_config_from_ctx_wrap(tls_ctx, tls_cfg)) {
Expand Down Expand Up @@ -308,8 +303,7 @@ nc_client_tls_session_new(int sock, const char *host, struct nc_client_tls_opts

/* post-handshake CRL verification: download CRLs for peer certs and verify the full chain */
if (nc_session_tls_crl_verify_post_handshake(tls_session,
nc_tls_get_cert_store_wrap(tls_cfg, tls_ctx),
nc_tls_get_crl_store_wrap(tls_cfg, tls_ctx))) {
nc_tls_get_cert_store_wrap(tls_cfg, tls_ctx))) {
ERR(NULL, "TLS connect failed (post-handshake CRL verification).");
goto fail;
}
Expand All @@ -326,7 +320,6 @@ nc_client_tls_session_new(int sock, const char *host, struct nc_client_tls_opts
nc_tls_cert_destroy_wrap(cli_cert);
nc_tls_privkey_destroy_wrap(cli_pkey);
nc_tls_cert_store_destroy_wrap(cert_store);
nc_tls_crl_store_destroy_wrap(crl_store);
nc_tls_config_destroy_wrap(tls_cfg);
return NULL;
}
Expand Down
26 changes: 18 additions & 8 deletions src/session_mbedtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,12 @@ nc_tls_verify_cert_chain_crl_wrap(void *cert_chain, void *cert_store, void *crl_
return 0;
}

/* skip verification if the CRL store is empty (no CRLs were downloaded) */
if (((mbedtls_x509_crl *)crl_store)->raw.len == 0) {
DBG(NULL, "No CRLs in store, skipping CRL verification.");
return 0;
}

ret = mbedtls_x509_crt_verify((mbedtls_x509_crt *)peer_chain,
(mbedtls_x509_crt *)trust_ca, (mbedtls_x509_crl *)ca_crl,
NULL, &flags, NULL, NULL);
Expand All @@ -1218,9 +1224,15 @@ nc_tls_get_cert_store_wrap(void *UNUSED(tls_cfg), struct nc_tls_ctx *tls_ctx)
}

void *
nc_tls_get_crl_store_wrap(void *UNUSED(tls_cfg), struct nc_tls_ctx *tls_ctx)
nc_tls_crl_store_for_post_handshake_wrap(void *UNUSED(cert_store))
{
return nc_tls_crl_store_new_wrap();
}

void
nc_tls_crl_store_post_handshake_free_wrap(void *crl_store)
{
return tls_ctx->crl_store;
nc_tls_crl_store_destroy_wrap(crl_store);
}

void
Expand All @@ -1230,7 +1242,6 @@ nc_tls_ctx_destroy_wrap(struct nc_tls_ctx *tls_ctx)
nc_tls_cert_destroy_wrap(tls_ctx->cert);
nc_tls_privkey_destroy_wrap(tls_ctx->pkey);
nc_tls_cert_store_destroy_wrap(tls_ctx->cert_store);
nc_tls_crl_store_destroy_wrap(tls_ctx->crl_store);
free(tls_ctx->sock);
free(tls_ctx->cipher_suites);
}
Expand Down Expand Up @@ -1332,7 +1343,7 @@ nc_client_tls_set_hostname_wrap(void *tls_session, const char *hostname)

int
nc_tls_init_ctx_wrap(void *cert, void *pkey, void *cert_store,
void *crl_store, void *cipher_suites, struct nc_tls_ctx *tls_ctx)
void *cipher_suites, struct nc_tls_ctx *tls_ctx)
{
/* setup rng */
if (nc_tls_rng_new(&tls_ctx->ctr_drbg, &tls_ctx->entropy)) {
Expand All @@ -1346,7 +1357,6 @@ nc_tls_init_ctx_wrap(void *cert, void *pkey, void *cert_store,
tls_ctx->cert = cert;
tls_ctx->pkey = pkey;
tls_ctx->cert_store = cert_store;
tls_ctx->crl_store = crl_store;
return 0;
}

Expand All @@ -1358,9 +1368,9 @@ nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, void *tls_cfg)
/* set config's cert and key */
mbedtls_ssl_conf_own_cert(tls_cfg, tls_ctx->cert, tls_ctx->pkey);
/*
* Do NOT pass crl_store to mbedtls_ssl_conf_ca_chain. CRL verification is done
* post-handshake so that CRLs for peer certificates (received during the handshake)
* can also be downloaded and checked. crl_store is kept in tls_ctx for later use.
* Do NOT pass a CRL store to mbedtls_ssl_conf_ca_chain. CRL verification
* is done post-handshake so that CRLs for peer certificates (received
* during the handshake) can also be downloaded and checked.
*/
mbedtls_ssl_conf_ca_chain(tls_cfg, tls_ctx->cert_store, NULL);

Expand Down
Loading