Skip to content

Commit 440b6c7

Browse files
committed
Provide a mechanism to setup TLS cert chain
This commit from @jwwilcox, together with a corresponding commit to _netopeer2_, fixes the TLS connection scenario in which the server's certificate has been signed by an intermediate CA, but the client only has the root CA available locally. In this case, the client will reject the connection attempt, because it does not know about the intermediate CA. The changes here use the new _netopeer2_ callback (which supplies the intermediate certificate(s)) to call `SSL_CTX_add_extra_chain_cert()`, which allows the server's TLS context to automatically provide the intermediate certificate(s) to the client. This scenario is demonstrated in the integration test `test_tls_client_missing_server_intermediate()` in [ADTRAN:netopeer2-integration-tests](https://github.com/ADTRAN/netopeer2-integration-tests/blob/master/tests/test_tls.py#L73). The changes here, together with the corresponding commit in _netopeer2_, will allow [the currently failing test case](https://travis-ci.org/ADTRAN/netopeer2-integration-tests/jobs/420293391#L7434) to pass.
1 parent 1bbe159 commit 440b6c7

4 files changed

Lines changed: 116 additions & 16 deletions

File tree

src/libnetconf.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,9 @@
394394
* So, after starting listening on an endpoint you need to set the server
395395
* certificate (nc_server_tls_endpt_set_server_cert()). Its actual content
396396
* together with the matching private key will be loaded using a callback
397-
* from nc_server_tls_set_server_cert_clb().
397+
* from nc_server_tls_set_server_cert_clb(). Additional certificates needed
398+
* for the client to verify the server's certificate chain can be loaded using
399+
* a callback from nc_server_tls_set_server_cert_chain_clb().
398400
*
399401
* To accept client certificates, they must first be considered trusted,
400402
* which you have three ways of achieving. You can add each of their Certificate Authority
@@ -428,6 +430,7 @@
428430
* - nc_server_tls_endpt_get_ctn()
429431
*
430432
* - nc_server_tls_set_server_cert_clb()
433+
* - nc_server_tls_set_server_cert_chain_clb()
431434
* - nc_server_tls_set_trusted_cert_list_clb()
432435
*
433436
* FD

src/session_p.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,11 @@ struct nc_server_opts {
186186
void *server_cert_data;
187187
void (*server_cert_data_free)(void *data);
188188

189+
int (*server_cert_chain_clb)(const char *name, void *user_data, char ***cert_paths, int *cert_path_count,
190+
char ***cert_data, int *cert_data_count);
191+
void *server_cert_chain_data;
192+
void (*server_cert_chain_data_free)(void *data);
193+
189194
int (*trusted_cert_list_clb)(const char *name, void *user_data, char ***cert_paths, int *cert_path_count,
190195
char ***cert_data, int *cert_data_count);
191196
void *trusted_cert_list_data;

src/session_server.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,23 @@ void nc_server_tls_set_server_cert_clb(int (*cert_clb)(const char *name, void *u
663663
char **privkey_path, char **privkey_data, int *privkey_data_rsa),
664664
void *user_data, void (*free_user_data)(void *user_data));
665665

666+
/**
667+
* @brief Set the callback for retrieving server certificate chain
668+
*
669+
* @param[in] cert_chain_clb Callback that should return all the certificates of the chain. Zero return indicates success,
670+
* non-zero an error. On success, \p cert_paths and \p cert_data are expected to be set or left
671+
* NULL. Both will be (deeply) freed.
672+
* - \p cert_paths expect an array of PEM files,
673+
* - \p cert_path_count number of \p cert_paths array members,
674+
* - \p cert_data expect an array of base-64 encoded ASN.1 DER cert data,
675+
* - \p cert_data_count number of \p cert_data array members.
676+
* @param[in] user_data Optional arbitrary user data that will be passed to \p cert_clb.
677+
* @param[in] free_user_data Optional callback that will be called during cleanup to free any \p user_data.
678+
*/
679+
void nc_server_tls_set_server_cert_chain_clb(int (*cert_chain_clb)(const char *name, void *user_data, char ***cert_paths,
680+
int *cert_path_count, char ***cert_data, int *cert_data_count),
681+
void *user_data, void (*free_user_data)(void *user_data));
682+
666683
/**
667684
* @brief Add a trusted certificate list. Can be both a CA or a client one. Can be
668685
* safely used together with nc_server_tls_endpt_set_trusted_ca_paths().

src/session_server_tls.c

Lines changed: 90 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,21 @@ nc_server_tls_set_server_cert_clb(int (*cert_clb)(const char *name, void *user_d
986986
server_opts.server_cert_data_free = free_user_data;
987987
}
988988

989+
API void
990+
nc_server_tls_set_server_cert_chain_clb(int (*cert_chain_clb)(const char *name, void *user_data, char ***cert_paths,
991+
int *cert_path_count, char ***cert_data, int *cert_data_count),
992+
void *user_data, void (*free_user_data)(void *user_data))
993+
{
994+
if (!cert_chain_clb) {
995+
ERRARG("cert_chain_clb");
996+
return;
997+
}
998+
999+
server_opts.server_cert_chain_clb = cert_chain_clb;
1000+
server_opts.server_cert_chain_data = user_data;
1001+
server_opts.server_cert_chain_data_free = free_user_data;
1002+
}
1003+
9891004
static int
9901005
nc_server_tls_add_trusted_cert_list(const char *name, struct nc_server_tls_opts *opts)
9911006
{
@@ -1705,6 +1720,78 @@ nc_tls_make_verify_key(void)
17051720
pthread_key_create(&verify_key, NULL);
17061721
}
17071722

1723+
static X509*
1724+
tls_load_cert(const char *cert_path, const char *cert_data)
1725+
{
1726+
X509 *cert;
1727+
1728+
if (cert_path) {
1729+
cert = pem_to_cert(cert_path);
1730+
} else {
1731+
cert = base64der_to_cert(cert_data);
1732+
}
1733+
1734+
if (!cert) {
1735+
if (cert_path) {
1736+
ERR("Loading a trusted certificate (path \"%s\") failed (%s).", cert_path,
1737+
ERR_reason_error_string(ERR_get_error()));
1738+
} else {
1739+
ERR("Loading a trusted certificate (data \"%s\") failed (%s).", cert_data,
1740+
ERR_reason_error_string(ERR_get_error()));
1741+
}
1742+
}
1743+
return cert;
1744+
}
1745+
1746+
static int
1747+
nc_tls_ctx_set_server_cert_chain(SSL_CTX *tls_ctx, const char *cert_name)
1748+
{
1749+
char **cert_paths = NULL, **cert_data = NULL;
1750+
int cert_path_count = 0, cert_data_count = 0, ret = 0, i = 0;
1751+
X509 *cert = NULL;
1752+
1753+
if (!server_opts.server_cert_chain_clb) {
1754+
/* This is optional, so return OK */
1755+
return 0;
1756+
}
1757+
1758+
if (server_opts.server_cert_chain_clb(cert_name, server_opts.server_cert_chain_data, &cert_paths,
1759+
&cert_path_count, &cert_data, &cert_data_count)) {
1760+
ERR("Server certificate chain callback failed.");
1761+
return -1;
1762+
}
1763+
1764+
for (i = 0; i < cert_path_count; ++i) {
1765+
cert = tls_load_cert(cert_paths[i], NULL);
1766+
if (!cert || SSL_CTX_add_extra_chain_cert(tls_ctx, cert) != 1) {
1767+
ERR("Loading the server certificate chain failed (%s).", ERR_reason_error_string(ERR_get_error()));
1768+
ret = -1;
1769+
goto cleanup;
1770+
}
1771+
}
1772+
1773+
for (i = 0; i < cert_data_count; ++i) {
1774+
cert = tls_load_cert(NULL, cert_data[i]);
1775+
if (!cert || SSL_CTX_add_extra_chain_cert(tls_ctx, cert) != 1) {
1776+
ERR("Loading the server certificate chain failed (%s).", ERR_reason_error_string(ERR_get_error()));
1777+
ret = -1;
1778+
goto cleanup;
1779+
}
1780+
}
1781+
cleanup:
1782+
for (i = 0; i < cert_path_count; ++i) {
1783+
free(cert_paths[i]);
1784+
}
1785+
free(cert_paths);
1786+
for (i = 0; i < cert_data_count; ++i) {
1787+
free(cert_data[i]);
1788+
}
1789+
free(cert_data);
1790+
/* cert is owned by the SSL_CTX */
1791+
1792+
return ret;
1793+
}
1794+
17081795
static int
17091796
nc_tls_ctx_set_server_cert_key(SSL_CTX *tls_ctx, const char *cert_name)
17101797
{
@@ -1759,6 +1846,8 @@ nc_tls_ctx_set_server_cert_key(SSL_CTX *tls_ctx, const char *cert_name)
17591846
}
17601847
}
17611848

1849+
ret = nc_tls_ctx_set_server_cert_chain(tls_ctx, cert_name);
1850+
17621851
cleanup:
17631852
X509_free(cert);
17641853
EVP_PKEY_free(pkey);
@@ -1772,22 +1861,8 @@ nc_tls_ctx_set_server_cert_key(SSL_CTX *tls_ctx, const char *cert_name)
17721861
static void
17731862
tls_store_add_trusted_cert(X509_STORE *cert_store, const char *cert_path, const char *cert_data)
17741863
{
1775-
X509 *cert;
1776-
1777-
if (cert_path) {
1778-
cert = pem_to_cert(cert_path);
1779-
} else {
1780-
cert = base64der_to_cert(cert_data);
1781-
}
1782-
1864+
X509 *cert = tls_load_cert(cert_path, cert_data);
17831865
if (!cert) {
1784-
if (cert_path) {
1785-
ERR("Loading a trusted certificate (path \"%s\") failed (%s).", cert_path,
1786-
ERR_reason_error_string(ERR_get_error()));
1787-
} else {
1788-
ERR("Loading a trusted certificate (data \"%s\") failed (%s).", cert_data,
1789-
ERR_reason_error_string(ERR_get_error()));
1790-
}
17911866
return;
17921867
}
17931868

0 commit comments

Comments
 (0)