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 8)
set(LIBNETCONF2_MICRO_VERSION 9)
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 7)
set(LIBNETCONF2_MICRO_SOVERSION 8)
set(LIBNETCONF2_SOVERSION_FULL ${LIBNETCONF2_MAJOR_SOVERSION}.${LIBNETCONF2_MINOR_SOVERSION}.${LIBNETCONF2_MICRO_SOVERSION})
set(LIBNETCONF2_SOVERSION ${LIBNETCONF2_MAJOR_SOVERSION})

Expand Down
116 changes: 115 additions & 1 deletion src/session.c
Original file line number Diff line number Diff line change
Expand Up @@ -2020,7 +2020,121 @@ nc_session_tls_crl_from_cert_ext_fetch(void *leaf_cert, void *cert_store, void *
return ret;
}

#endif
/**
* @brief Download CRLs for certificates in the peer's chain and merge them into crl_store.
*
* @param[in] peer_chain Peer's certificate chain.
* @param[in] cert_store Certificate store containing CAs (for resolving CRL distribution points).
* @param[in,out] crl_store CRL store to add downloaded CRLs to.
* @return 0 on success, non-zero on error.
*/
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;
void *cert = NULL;
CURL *handle = NULL;
struct nc_curl_data downloaded = {0};
char **uris = NULL;

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

/* iterate over all certificates in the peer chain and collect CRL distribution point URIs */
for (i = 0; i < nc_tls_get_num_certs_wrap(peer_chain); i++) {
cert = nc_tls_get_cert_wrap(peer_chain, i);
uris = NULL;
uri_count = 0;

/*
* For the first (leaf) cert, pass cert_store to also extract CRL DPs
* from the CA certificates. For subsequent certs, pass NULL to only
* extract their own CRL DPs, avoiding duplicates from cert_store.
*/
ret = nc_server_tls_get_crl_distpoint_uris_wrap(cert, i == 0 ? cert_store : NULL, &uris, &uri_count);
if (ret) {
goto cleanup;
}

for (j = 0; j < uri_count; j++) {
VRB(NULL, "Downloading CRL from \"%s\".", uris[j]);
ret = nc_session_curl_fetch(handle, uris[j]);
if (ret) {
WRN(NULL, "Failed to fetch CRL from \"%s\".", uris[j]);
continue;
}

ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, crl_store);

free(downloaded.data);
downloaded.data = NULL;
downloaded.size = 0;

if (ret) {
for (j = j + 1; j < uri_count; j++) {
free(uris[j]);
}
free(uris);
uris = NULL;
goto cleanup;
}
}

for (j = 0; j < uri_count; j++) {
free(uris[j]);
}
free(uris);
uris = NULL;
}

cleanup:
curl_easy_cleanup(handle);
free(downloaded.data);
if (uris) {
for (i = 0; i < uri_count; i++) {
free(uris[i]);
}
free(uris);
}
return ret;
}

int
nc_session_tls_crl_verify_post_handshake(void *tls_session, void *cert_store, void *crl_store)
{
int ret = 0;
void *peer_chain = 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 */
return 0;
}

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

/* verify the peer chain against the CRLs */
ret = nc_tls_verify_cert_chain_crl_wrap(peer_chain, cert_store, crl_store);
if (ret) {
ERR(NULL, "Post-handshake CRL verification failed.");
}

return ret;
}

#endif /* NC_ENABLED_SSH_TLS */

API const char *
nc_yang_module_dir(void)
Expand Down
8 changes: 8 additions & 0 deletions src/session_client_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,14 @@ nc_client_tls_session_new(int sock, const char *host, struct nc_client_tls_opts
goto fail;
}

/* 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))) {
ERR(NULL, "TLS connect failed (post-handshake CRL verification).");
goto fail;
}

*out_tls_cfg = tls_cfg;
return tls_session;

Expand Down
73 changes: 63 additions & 10 deletions src/session_mbedtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,48 @@ nc_client_tls_handshake_step_wrap(void *tls_session, int sock)
}
}

void *
nc_tls_get_peer_cert_chain_wrap(void *tls_session)
{
return (mbedtls_x509_crt *)mbedtls_ssl_get_peer_cert(tls_session);
}

int
nc_tls_verify_cert_chain_crl_wrap(void *cert_chain, void *cert_store, void *crl_store)
{
const mbedtls_x509_crt *peer_chain = cert_chain;
const mbedtls_x509_crt *trust_ca = cert_store;
const mbedtls_x509_crl *ca_crl = crl_store;
uint32_t flags = 0;
int ret;

if (!crl_store || !cert_store) {
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);
if (ret != 0) {
ERR(NULL, "CRL verification failed (%s).", nc_tls_get_verify_err_str(flags));
return 1;
}

return 0;
}

void *
nc_tls_get_cert_store_wrap(void *UNUSED(tls_cfg), struct nc_tls_ctx *tls_ctx)
{
return tls_ctx->cert_store;
}

void *
nc_tls_get_crl_store_wrap(void *UNUSED(tls_cfg), struct nc_tls_ctx *tls_ctx)
{
return tls_ctx->crl_store;
}

void
nc_tls_ctx_destroy_wrap(struct nc_tls_ctx *tls_ctx)
{
Expand Down Expand Up @@ -1315,8 +1357,12 @@ nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, void *tls_cfg)
mbedtls_ssl_conf_rng(tls_cfg, mbedtls_ctr_drbg_random, tls_ctx->ctr_drbg);
/* set config's cert and key */
mbedtls_ssl_conf_own_cert(tls_cfg, tls_ctx->cert, tls_ctx->pkey);
/* set config's CA and CRL cert store */
mbedtls_ssl_conf_ca_chain(tls_cfg, tls_ctx->cert_store, tls_ctx->crl_store);
/*
* 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.
*/
mbedtls_ssl_conf_ca_chain(tls_cfg, tls_ctx->cert_store, NULL);

return 0;
}
Expand Down Expand Up @@ -2000,20 +2046,27 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *leaf_cert, void *cert_store, cha
size_t len;
mbedtls_x509_buf ext_oid = {0};

NC_CHECK_ARG_RET(NULL, cert_store, uris, uri_count, 1);
if (!uris || !uri_count) {
ERRINT;
return 1;
}

*uris = NULL;
*uri_count = 0;

/* get the number of certs in the store */
cert = cert_store;
cert_count = 0;
while (cert) {
++cert_count;
cert = cert->next;
if (cert_store) {
/* get the number of certs in the store */
cert = cert_store;
cert_count = 0;
while (cert) {
++cert_count;
cert = cert->next;
}
} else {
cert_count = 0;
}

/* iterate over all the certs */
/* iterate over leaf cert (i = -1) and all the certs in the store */
for (i = -1; i < cert_count; i++) {
if (i == -1) {
cert = leaf_cert;
Expand Down
Loading