Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tls/http: add certificate chain setters #1125

Merged
merged 1 commit into from
May 23, 2024
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
3 changes: 3 additions & 0 deletions include/re_http.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ int http_client_set_cert(struct http_cli *cli, const char *path);
int http_client_set_certpem(struct http_cli *cli, const char *pem);
int http_client_set_key(struct http_cli *cli, const char *path);
int http_client_set_keypem(struct http_cli *cli, const char *pem);
int http_client_use_chain(struct http_cli *cli, const char *path);
int http_client_use_chainpem(struct http_cli *cli, const char *chain,
int len_chain);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use size_t len_chain which is consistant with the API ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to prevent the cast from size_t to int when calling BIO_new_mem_buf, but consistency makes sense. Done.


int http_client_set_session_reuse(struct http_cli *cli, bool enabled);
int http_client_set_tls_min_version(struct http_cli *cli, int version);
Expand Down
3 changes: 3 additions & 0 deletions include/re_tls.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ int tls_set_certificate_der(struct tls *tls, enum tls_keytype keytype,
const uint8_t *cert, size_t len_cert,
const uint8_t *key, size_t len_key);
int tls_set_certificate(struct tls *tls, const char *cert, size_t len);
int tls_set_certificate_chain_pem(struct tls *tls, const char *chain,
size_t len_chain);
int tls_set_certificate_chain(struct tls *tls, const char *path);
void tls_set_verify_client(struct tls *tls);
void tls_set_verify_client_trust_all(struct tls *tls);
int tls_set_verify_client_handler(struct tls_conn *tc, int depth,
Expand Down
52 changes: 52 additions & 0 deletions src/http/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1260,6 +1260,58 @@ int http_client_disable_verify_server(struct http_cli *cli)

return 0;
}


/**
* Change used certificate+key of TLS connection
*
* @param cli HTTP Client
* @param chain Cert (chain) + Key (PEM format)
* @param len_chain Length of certificate + key PEM string
*
* @return int 0 if success, otherwise errorcode
*/
int http_client_use_chainpem(struct http_cli *cli, const char *chain,
int len_chain)
{
if (!cli || !cli->tls)
return EINVAL;

int err = tls_set_certificate_chain_pem(cli->tls, chain, len_chain);
if (err)
return err;

cli->cert = mem_deref(cli->cert);
cli->key = mem_deref(cli->key);

return 0;
}


/**
* Change used certificate+key of TLS connection
*
* @param cli HTTP Client
* @param path Path to Cert (chain) + Key file (PEM format)
*
* @return int 0 if success, otherwise errorcode
*/
int http_client_use_chain(struct http_cli *cli, const char *path)
{
int err;

if (!cli || !cli->tls)
return EINVAL;

err = tls_set_certificate_chain(cli->tls, path);
if (err)
return err;

cli->cert = mem_deref(cli->cert);
cli->key = mem_deref(cli->key);

return 0;
}
#endif /* USE_TLS */


Expand Down
152 changes: 152 additions & 0 deletions src/tls/openssl/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <re_types.h>
Expand Down Expand Up @@ -2214,3 +2215,154 @@ int tls_set_resumption(struct tls *tls, const enum tls_resume_mode mode)

return 0;
}


/**
* Change used certificate+key of an existing SSL object
*
* @param tls TLS Object
* @param chain Cert (chain) + Key in PEM format
* @param len_chain Length of certificate + key PEM string
*
* @return int 0 if success, otherwise errorcode
*/
int tls_set_certificate_chain_pem(struct tls *tls, const char *chain,
size_t len_chain)
{
STACK_OF(X509) *cert_stack = NULL;
BIO *bio_mem = NULL;
EVP_PKEY *pkey = NULL;
X509 *leaf_cert = NULL;
int err = ENOMEM;

if (!tls || !chain || !len_chain)
return EINVAL;

bio_mem = BIO_new_mem_buf(chain, (int)len_chain);
cert_stack = sk_X509_new_null();
if (!bio_mem || !cert_stack)
goto out;

X509 *cert;
while ((cert = PEM_read_bio_X509(bio_mem, NULL, NULL, NULL)) != NULL) {
int n = sk_X509_push(cert_stack, cert);
if (n < 1) {
X509_free(cert);
goto out;
}
}

err = EINVAL;

if (sk_X509_num(cert_stack) == 0)
goto out;

leaf_cert = sk_X509_shift(cert_stack);
long ok = SSL_CTX_use_certificate(tls->ctx, leaf_cert);
if (ok <= 0) {
X509_free(leaf_cert);
goto out;
}

if (sk_X509_num(cert_stack)) {
ok = SSL_CTX_clear_chain_certs(tls->ctx);
if (!ok)
goto out;

while((cert = sk_X509_shift(cert_stack)) != NULL){
ok = SSL_CTX_add0_chain_cert(tls->ctx, cert);
if (!ok) {
X509_free(cert);
goto out;
}
}
}

BIO_free(bio_mem);
bio_mem = BIO_new_mem_buf(chain, (int)len_chain);
if (!bio_mem) {
err = ENOMEM;
goto out;
}

pkey = PEM_read_bio_PrivateKey(bio_mem, NULL, NULL, NULL);
if (!pkey)
goto out;

ok = SSL_CTX_use_PrivateKey(tls->ctx, pkey);
if (ok <= 0) {
err = EKEYREJECTED;
goto out;
}

ok = SSL_CTX_check_private_key(tls->ctx);
if (ok <= 0)
goto out;

if (tls->cert)
X509_free(tls->cert);

tls->cert = leaf_cert;
leaf_cert = NULL;

err = 0;

out:
if (bio_mem)
BIO_free(bio_mem);
if (leaf_cert)
X509_free(leaf_cert);
if (cert_stack)
sk_X509_pop_free(cert_stack, X509_free);
if (pkey)
EVP_PKEY_free(pkey);
if (err)
ERR_clear_error();

return err;
}


/**
* Change used certificate+key of an existing SSL object
*
* @param tls TLS Object
* @param path Path to Cert (chain) + Key file (PEM format)
*
* @return int 0 if success, otherwise errorcode
*/
int tls_set_certificate_chain(struct tls *tls, const char *path)
{
X509 *cert;
int ok = 0;

if (!tls || !path)
return EINVAL;

ok = SSL_CTX_use_certificate_chain_file(tls->ctx, path);
if (ok <= 0) {
ERR_clear_error();
return ENOENT;
}

ok = SSL_CTX_use_PrivateKey_file(tls->ctx, path, SSL_FILETYPE_PEM);
if (ok <= 0) {
ERR_clear_error();
return EKEYREJECTED;
}

cert = SSL_CTX_get0_certificate(tls->ctx);
if (!cert) {
ERR_clear_error();
return ENOENT;
}

X509_up_ref(cert);

if (tls->cert)
X509_free(tls->cert);

tls->cert = cert;

return 0;
}
Loading