From d021190fd5a3b8f9e1ae338647b5fe99f2f511c3 Mon Sep 17 00:00:00 2001 From: Maximilian Fridrich Date: Thu, 23 May 2024 09:34:10 +0200 Subject: [PATCH] tls/http: add certificate chain setters (#1125) --- include/re_http.h | 3 + include/re_tls.h | 3 + src/http/client.c | 52 +++++++++++++++ src/tls/openssl/tls.c | 152 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 210 insertions(+) diff --git a/include/re_http.h b/include/re_http.h index 322eb1abf..1ac3eafba 100644 --- a/include/re_http.h +++ b/include/re_http.h @@ -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); 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); diff --git a/include/re_tls.h b/include/re_tls.h index eb944e57e..dc5539af5 100644 --- a/include/re_tls.h +++ b/include/re_tls.h @@ -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, diff --git a/src/http/client.c b/src/http/client.c index c97623bc5..06186f269 100644 --- a/src/http/client.c +++ b/src/http/client.c @@ -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 */ diff --git a/src/tls/openssl/tls.c b/src/tls/openssl/tls.c index f3d111ceb..b53f70283 100644 --- a/src/tls/openssl/tls.c +++ b/src/tls/openssl/tls.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -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; +}