Skip to content

Commit 0a572c9

Browse files
authored
Merge pull request #76 from ADTRAN/tls-cert-chain
Provide a mechanism to setup TLS cert chain
2 parents 1bbe159 + 440b6c7 commit 0a572c9

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)