From 60f1b01374a1009d90eb65e52a77af68ffe09348 Mon Sep 17 00:00:00 2001 From: Christian Spielberger Date: Mon, 31 Oct 2022 13:26:13 +0100 Subject: [PATCH 1/8] tls: support multiple TLS server certificates Multiple certificates combined with private key and host name for the host name verification can be added with a new function `tls_add_certf()`. For incoming SIP requests a certificate is selected by SNI in the TLS client hello before `SSL_accept()` is called. --- CMakeLists.txt | 1 + include/re_tls.h | 1 + src/tls/openssl/sni.c | 185 ++++++++++++++++++++++++++++++++++++++++++ src/tls/openssl/sni.h | 9 ++ src/tls/openssl/tls.c | 178 +++++++++++++++++++++++++++++++++++++++- src/tls/openssl/tls.h | 11 ++- 6 files changed, 382 insertions(+), 3 deletions(-) create mode 100644 src/tls/openssl/sni.c create mode 100644 src/tls/openssl/sni.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e0d211ef..1532a6cd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -471,6 +471,7 @@ if(USE_OPENSSL) src/tls/openssl/tls_tcp.c src/tls/openssl/tls_udp.c src/tls/openssl/tls.c + src/tls/openssl/sni.c src/hmac/openssl/hmac.c ) endif() diff --git a/include/re_tls.h b/include/re_tls.h index b4e64357e..9e0c37dd5 100644 --- a/include/re_tls.h +++ b/include/re_tls.h @@ -120,3 +120,4 @@ struct evp_pkey_st; int tls_set_certificate_openssl(struct tls *tls, struct x509_st *cert, struct evp_pkey_st *pkey, bool up_ref); +int tls_add_certf(struct tls *tls, const char *certf, const struct pl *host); diff --git a/src/tls/openssl/sni.c b/src/tls/openssl/sni.c new file mode 100644 index 000000000..43cd8ad2d --- /dev/null +++ b/src/tls/openssl/sni.c @@ -0,0 +1,185 @@ +/** + * @file openssl/sni.c Server Name Indication API + * + * Copyright (C) 2022 Commend.com - c.spielberger@commend.com + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tls.h" +#include "sni.h" + + +#define DEBUG_MODULE "tls" +#define DEBUG_LEVEL 5 +#include + +#include +#include + + +struct tls_conn; + + +static int x509_match_alt_name(X509 *x509, const struct pl *sni, bool *match) +{ + GENERAL_NAMES *gs = NULL; + char *snistr; + ASN1_STRING *astr = NULL; + ASN1_OCTET_STRING *octet = NULL; + int i; + int err = 0; + + *match = false; + gs = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL); + if (!gs) + return 0; + + err = pl_strdup(&snistr, sni); + if (err) + return err; + + astr = ASN1_IA5STRING_new(); + if (!astr) { + err = ENOMEM; + goto out; + } + + if (!ASN1_STRING_set(astr, snistr, -1)) { + err = ENOMEM; + goto out; + } + + octet = a2i_IPADDRESS(snistr); + for (i = 0; i < sk_GENERAL_NAME_num(gs); i++) { + GENERAL_NAME *g = sk_GENERAL_NAME_value(gs, i); + + if (g->type == GEN_DNS) { + if (!ASN1_STRING_cmp(astr, g->d.dNSName)) { + *match = true; + break; + } + } + else if (g->type == GEN_IPADD) { + if (!ASN1_OCTET_STRING_cmp(octet, g->d.iPAddress)) { + *match = true; + break; + } + } + } + +out: + mem_deref(snistr); + ASN1_IA5STRING_free(astr); + ASN1_OCTET_STRING_free(octet); + return err; +} + + +struct tls_cert *tls_cert_for_sni(const struct tls *tls, const struct pl *sni) +{ + struct tls_cert *tls_cert = NULL; + struct le *le; + int sz; + char *cn; + const struct list *certs = tls_certs(tls); + + if (!certs) + return NULL; + + if (!pl_isset(sni)) + return list_head(certs)->data; + + if (sni->l >= TLSEXT_MAXLEN_host_name) + return NULL; + + sz = (int) sni->l + 1; + cn = mem_zalloc(sz, NULL); + LIST_FOREACH(certs, le) { + X509 *x509; + X509_NAME *nm; + bool match = false; + int err; + + tls_cert = le->data; + x509 = tls_cert_x509(tls_cert); + if (!x509) { + tls_cert = NULL; + continue; + } + + nm = X509_get_subject_name(x509); + X509_NAME_get_text_by_NID(nm, NID_commonName, cn, sz); + if (!pl_strcmp(sni, cn)) + break; + + err = x509_match_alt_name(x509, sni, &match); + if (err) { + tls_cert = NULL; + break; + } + + if (match) + break; + } + + mem_deref(cn); + return tls_cert; +} + + +static int ssl_use_cert(SSL *ssl, struct tls_cert *uc) +{ + int err; + long r; + +#if !defined(LIBRESSL_VERSION_NUMBER) + SSL_certs_clear(ssl); +#endif + r = SSL_clear_chain_certs(ssl); + if (r != 1) + return EINVAL; + + r = SSL_use_cert_and_key(ssl, tls_cert_x509(uc), tls_cert_pkey(uc), + tls_cert_chain(uc), 1); + if (r != 1) + return EINVAL; + + err = ssl_set_verify_client(ssl, tls_cert_host(uc)); + return err; +} + + +int ssl_servername_handler(SSL *ssl, int *al, void *arg) +{ + struct tls *tls = arg; + struct pl pl; + struct tls_cert *uc = NULL; + const char *sni; + (void)al; + + sni = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (!str_isset(sni)) + goto out; + + pl_set_str(&pl, sni); + + /* find and apply matching certificate */ + uc = tls_cert_for_sni(tls, &pl); + if (!uc) + goto out; + + (void)ssl_use_cert(ssl, uc); + +out: + return SSL_TLSEXT_ERR_OK; +} diff --git a/src/tls/openssl/sni.h b/src/tls/openssl/sni.h new file mode 100644 index 000000000..dbb78c34b --- /dev/null +++ b/src/tls/openssl/sni.h @@ -0,0 +1,9 @@ +/** + * @file openssl/sni.h Server Name Indication (Internal API) + * + * Copyright (C) 2022 Commend.com - c.spielberger@commend.com + */ + +struct tls_cert *tls_cert_for_sni(const struct tls *tls, const struct pl *sni); +int ssl_servername_handler(SSL *s, int *al, void *arg); +int ssl_set_verify_client(SSL *ssl, const char *host); diff --git a/src/tls/openssl/tls.c b/src/tls/openssl/tls.c index eb9906a0a..6d146fcbb 100644 --- a/src/tls/openssl/tls.c +++ b/src/tls/openssl/tls.c @@ -24,6 +24,7 @@ #include #include #include "tls.h" +#include "sni.h" #define DEBUG_MODULE "tls" @@ -45,6 +46,15 @@ struct tls { char *pass; /**< password for private key */ bool verify_server; /**< Enable SIP TLS server verification */ struct session_reuse reuse; + struct list certs; /**< Certificates for SNI selection */ +}; + +struct tls_cert { + struct le le; + X509 *x509; + EVP_PKEY *pkey; + STACK_OF(X509) *chain; + char *host; }; #if defined(TRACE_SSL) && (OPENSSL_VERSION_NUMBER >= 0x10101000L) @@ -110,6 +120,7 @@ static void destructor(void *data) hash_flush(tls->reuse.ht_sessions); mem_deref(tls->reuse.ht_sessions); mem_deref(tls->pass); + list_flush(&tls->certs); } @@ -146,7 +157,7 @@ static int keytype2int(enum tls_keytype type) #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(LIBRESSL_VERSION_NUMBER) -static int verify_handler(int ok, X509_STORE_CTX *ctx) +static int tls_verify_handler(int ok, X509_STORE_CTX *ctx) { int err, depth; @@ -266,6 +277,9 @@ int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile, } } + SSL_CTX_set_tlsext_servername_callback(tls->ctx, + ssl_servername_handler); + SSL_CTX_set_tlsext_servername_arg(tls->ctx, tls); err = hash_alloc(&tls->reuse.ht_sessions, 256); if (err) goto out; @@ -1298,7 +1312,39 @@ int tls_set_verify_server(struct tls_conn *tc, const char *host) } } - SSL_set_verify(tc->ssl, SSL_VERIFY_PEER, verify_handler); + SSL_set_verify(tc->ssl, SSL_VERIFY_PEER, tls_verify_handler); + + return 0; +#else + (void)tc; + (void)host; + + return ENOSYS; +#endif +} + + +int ssl_set_verify_client(SSL *ssl, const char *host) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) + struct sa sa; + + if (!ssl || !host) + return EINVAL; + + if (sa_set_str(&sa, host, 0)) { + SSL_set_hostflags(ssl, + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + + if (!SSL_set1_host(ssl, host)) { + DEBUG_WARNING("SSL_set1_host error\n"); + ERR_clear_error(); + return EPROTO; + } + } + + SSL_set_verify(ssl, SSL_VERIFY_PEER, tls_verify_handler); return 0; #else @@ -1779,3 +1825,131 @@ SSL_CTX *tls_ssl_ctx(const struct tls *tls) return tls->ctx; } + + +static void tls_cert_destructor(void *arg) +{ + struct tls_cert *uc = arg; + + mem_deref(uc->host); + X509_free(uc->x509); + EVP_PKEY_free(uc->pkey); + sk_X509_pop_free(uc->chain, X509_free); +} + + +int tls_add_certf(struct tls *tls, const char *certf, const struct pl *host) +{ + struct tls_cert *uc; + BIO *bio = NULL; + int err = 0; + + if (!tls || !certf) + return EINVAL; + + uc = mem_zalloc(sizeof(*uc), tls_cert_destructor); + if (pl_isset(host)) { + err = pl_strdup(&uc->host, host); + if (err) + goto out; + } + + bio = BIO_new_file(certf, "r"); + if (!bio) { + err = EIO; + goto out; + } + + uc->x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (!uc->x509) { + ERR_clear_error(); + DEBUG_WARNING("Can't read certificate from file: %s\n", certf); + err = ENOTSUP; + goto out; + } + + while (1) { + X509 *ca = PEM_read_bio_X509(bio, NULL, 0, NULL); + if (!ca) + break; + + if (!uc->chain) + uc->chain = sk_X509_new_null(); + + if (!uc->chain) { + err = ENOMEM; + goto out; + } + + if (!sk_X509_push(uc->chain, ca)) { + err = ENOMEM; + goto out; + } + } + + BIO_free(bio); + bio = BIO_new_file(certf, "r"); + if (!bio) { + err = EIO; + goto out; + } + + uc->pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + if (!uc->pkey) { + ERR_clear_error(); + DEBUG_WARNING("Can't read private key from file: %s\n", certf); + err = ENOTSUP; + goto out; + } + +out: + BIO_free(bio); + if (err) + mem_deref(uc); + else + list_append(&tls->certs, &uc->le, uc); + + return err; +} + + +X509 *tls_cert_x509(struct tls_cert *hc) +{ + return hc ? hc->x509 : NULL; +} + + +EVP_PKEY *tls_cert_pkey(struct tls_cert *hc) +{ + return hc ? hc->pkey : NULL; +} + + +STACK_OF(X509*) tls_cert_chain(struct tls_cert *hc) +{ + return hc ? hc->chain : NULL; +} + + +const char *tls_cert_host(struct tls_cert *hc) +{ + return hc ? hc->host : NULL; +} + + +const struct list *tls_certs(const struct tls *tls) +{ + return tls ? &tls->certs : NULL; +} + + +SSL *tls_conn_ssl(struct tls_conn *tc) +{ + return tc ? tc->ssl : NULL; +} + + +struct tls *tls_conn_tls(struct tls_conn *tc) +{ + return tc ? tc->tls : NULL; +} diff --git a/src/tls/openssl/tls.h b/src/tls/openssl/tls.h index ca39258d8..d11608b22 100644 --- a/src/tls/openssl/tls.h +++ b/src/tls/openssl/tls.h @@ -27,8 +27,17 @@ typedef X509_NAME*(tls_get_certfield_h)(const X509 *); typedef X509_NAME*(tls_get_certfield_h)(X509 *); #endif - struct tls; +struct tls_cert; void tls_flush_error(void); SSL_CTX *tls_ssl_ctx(const struct tls *tls); +X509 *tls_cert_x509(struct tls_cert *hc); +EVP_PKEY *tls_cert_pkey(struct tls_cert *hc); +STACK_OF(X509*) tls_cert_chain(struct tls_cert *hc); +const char *tls_cert_host(struct tls_cert *hc); +const struct list *tls_certs(const struct tls *tls); + +SSL *tls_conn_ssl(struct tls_conn *tc); +struct tls *tls_conn_tls(struct tls_conn *tc); + From 1e8ecd896e1db929eb9af869479fd7362125f1e1 Mon Sep 17 00:00:00 2001 From: Christian Spielberger Date: Wed, 14 Dec 2022 17:49:13 +0100 Subject: [PATCH 2/8] tls: some cleanup of the API - use const char * instead of struct pl in the public API - in for loops, move declaration of i into for loop - in all places where openssl return error, call ERR_clear_error() - global symbols in the tls module should have prefix: tls_ - move sni.h into tls.h - add doxygen comment to global and private API functions --- src/tls/openssl/sni.c | 117 +++++++++++++++++++++++++++------------- src/tls/openssl/sni.h | 9 ---- src/tls/openssl/tls.c | 120 +++++++++++++++++++++++------------------- src/tls/openssl/tls.h | 9 ++-- 4 files changed, 153 insertions(+), 102 deletions(-) delete mode 100644 src/tls/openssl/sni.h diff --git a/src/tls/openssl/sni.c b/src/tls/openssl/sni.c index 43cd8ad2d..7f68cdf19 100644 --- a/src/tls/openssl/sni.c +++ b/src/tls/openssl/sni.c @@ -13,30 +13,25 @@ #include #include #include +#include #include #include #include "tls.h" -#include "sni.h" #define DEBUG_MODULE "tls" #define DEBUG_LEVEL 5 #include -#include -#include - struct tls_conn; -static int x509_match_alt_name(X509 *x509, const struct pl *sni, bool *match) +static int x509_match_alt_name(X509 *x509, const char *sni, bool *match) { GENERAL_NAMES *gs = NULL; - char *snistr; ASN1_STRING *astr = NULL; ASN1_OCTET_STRING *octet = NULL; - int i; int err = 0; *match = false; @@ -44,32 +39,28 @@ static int x509_match_alt_name(X509 *x509, const struct pl *sni, bool *match) if (!gs) return 0; - err = pl_strdup(&snistr, sni); - if (err) - return err; - - astr = ASN1_IA5STRING_new(); - if (!astr) { - err = ENOMEM; - goto out; - } - - if (!ASN1_STRING_set(astr, snistr, -1)) { - err = ENOMEM; - goto out; - } - - octet = a2i_IPADDRESS(snistr); - for (i = 0; i < sk_GENERAL_NAME_num(gs); i++) { + for (int i = 0; i < sk_GENERAL_NAME_num(gs); i++) { GENERAL_NAME *g = sk_GENERAL_NAME_value(gs, i); if (g->type == GEN_DNS) { + astr = ASN1_IA5STRING_new(); + if (!astr) { + err = ENOMEM; + goto out; + } + + if (!ASN1_STRING_set(astr, sni, -1)) { + err = ENOMEM; + goto out; + } + if (!ASN1_STRING_cmp(astr, g->d.dNSName)) { *match = true; break; } } else if (g->type == GEN_IPADD) { + octet = a2i_IPADDRESS(sni); if (!ASN1_OCTET_STRING_cmp(octet, g->d.iPAddress)) { *match = true; break; @@ -78,31 +69,38 @@ static int x509_match_alt_name(X509 *x509, const struct pl *sni, bool *match) } out: - mem_deref(snistr); ASN1_IA5STRING_free(astr); ASN1_OCTET_STRING_free(octet); return err; } -struct tls_cert *tls_cert_for_sni(const struct tls *tls, const struct pl *sni) +/** + * Finds a TLS certificate that matches a given Server Name Indication (SNI) + * + * @param tls TLS Context + * @param sni Server Name Indication + * + * @return TLS certificate or NULL if not found + */ +struct tls_cert *tls_cert_for_sni(const struct tls *tls, const char *sni) { struct tls_cert *tls_cert = NULL; struct le *le; - int sz; + size_t sz; char *cn; const struct list *certs = tls_certs(tls); if (!certs) return NULL; - if (!pl_isset(sni)) + if (!str_isset(sni)) return list_head(certs)->data; - if (sni->l >= TLSEXT_MAXLEN_host_name) + sz = str_len(sni); + if (sz >= TLSEXT_MAXLEN_host_name) return NULL; - sz = (int) sni->l + 1; cn = mem_zalloc(sz, NULL); LIST_FOREACH(certs, le) { X509 *x509; @@ -119,7 +117,7 @@ struct tls_cert *tls_cert_for_sni(const struct tls *tls, const struct pl *sni) nm = X509_get_subject_name(x509); X509_NAME_get_text_by_NID(nm, NID_commonName, cn, sz); - if (!pl_strcmp(sni, cn)) + if (!str_cmp(sni, cn)) break; err = x509_match_alt_name(x509, sni, &match); @@ -133,10 +131,43 @@ struct tls_cert *tls_cert_for_sni(const struct tls *tls, const struct pl *sni) } mem_deref(cn); + ERR_clear_error(); return tls_cert; } +static int ssl_set_verify_client(SSL *ssl, const char *host) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) + struct sa sa; + + if (!ssl || !host) + return EINVAL; + + if (sa_set_str(&sa, host, 0)) { + SSL_set_hostflags(ssl, + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + + if (!SSL_set1_host(ssl, host)) { + DEBUG_WARNING("SSL_set1_host error\n"); + ERR_clear_error(); + return EPROTO; + } + } + + SSL_set_verify(ssl, SSL_VERIFY_PEER, tls_verify_handler); + + return 0; +#else + (void)tc; + (void)host; + + return ENOSYS; +#endif +} + + static int ssl_use_cert(SSL *ssl, struct tls_cert *uc) { int err; @@ -151,18 +182,19 @@ static int ssl_use_cert(SSL *ssl, struct tls_cert *uc) r = SSL_use_cert_and_key(ssl, tls_cert_x509(uc), tls_cert_pkey(uc), tls_cert_chain(uc), 1); - if (r != 1) + if (r != 1) { + ERR_clear_error(); return EINVAL; + } err = ssl_set_verify_client(ssl, tls_cert_host(uc)); return err; } -int ssl_servername_handler(SSL *ssl, int *al, void *arg) +static int ssl_servername_handler(SSL *ssl, int *al, void *arg) { struct tls *tls = arg; - struct pl pl; struct tls_cert *uc = NULL; const char *sni; (void)al; @@ -171,10 +203,8 @@ int ssl_servername_handler(SSL *ssl, int *al, void *arg) if (!str_isset(sni)) goto out; - pl_set_str(&pl, sni); - /* find and apply matching certificate */ - uc = tls_cert_for_sni(tls, &pl); + uc = tls_cert_for_sni(tls, sni); if (!uc) goto out; @@ -183,3 +213,16 @@ int ssl_servername_handler(SSL *ssl, int *al, void *arg) out: return SSL_TLSEXT_ERR_OK; } + + +/** + * Enables SNI handling on the given TLS context for incoming TLS connections + * + * @param tls TLS Context + */ +void tls_enable_sni(struct tls *tls) +{ + SSL_CTX_set_tlsext_servername_callback(tls_ssl_ctx(tls), + ssl_servername_handler); + SSL_CTX_set_tlsext_servername_arg(tls_ssl_ctx(tls), tls); +} diff --git a/src/tls/openssl/sni.h b/src/tls/openssl/sni.h deleted file mode 100644 index dbb78c34b..000000000 --- a/src/tls/openssl/sni.h +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @file openssl/sni.h Server Name Indication (Internal API) - * - * Copyright (C) 2022 Commend.com - c.spielberger@commend.com - */ - -struct tls_cert *tls_cert_for_sni(const struct tls *tls, const struct pl *sni); -int ssl_servername_handler(SSL *s, int *al, void *arg); -int ssl_set_verify_client(SSL *ssl, const char *host); diff --git a/src/tls/openssl/tls.c b/src/tls/openssl/tls.c index 6d146fcbb..a40d7ad75 100644 --- a/src/tls/openssl/tls.c +++ b/src/tls/openssl/tls.c @@ -24,7 +24,6 @@ #include #include #include "tls.h" -#include "sni.h" #define DEBUG_MODULE "tls" @@ -49,6 +48,11 @@ struct tls { struct list certs; /**< Certificates for SNI selection */ }; +/** + * A TLS certificate with private key, certificate chain and a host name that + * is passed to OpenSSL for the host name check + * + */ struct tls_cert { struct le le; X509 *x509; @@ -157,7 +161,16 @@ static int keytype2int(enum tls_keytype type) #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(LIBRESSL_VERSION_NUMBER) -static int tls_verify_handler(int ok, X509_STORE_CTX *ctx) +/** + * OpenSSL verify handler for debugging purposes. Prints only warnings in the + * default build + * + * @param ok Verification result of OpenSSL + * @param ctx OpenSSL X509 store context set by OpenSSL + * + * @return passes parameter ok unchanged + */ +int tls_verify_handler(int ok, X509_STORE_CTX *ctx) { int err, depth; @@ -277,9 +290,7 @@ int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile, } } - SSL_CTX_set_tlsext_servername_callback(tls->ctx, - ssl_servername_handler); - SSL_CTX_set_tlsext_servername_arg(tls->ctx, tls); + tls_enable_sni(tls); err = hash_alloc(&tls->reuse.ht_sessions, 256); if (err) goto out; @@ -1324,38 +1335,6 @@ int tls_set_verify_server(struct tls_conn *tc, const char *host) } -int ssl_set_verify_client(SSL *ssl, const char *host) -{ -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ - !defined(LIBRESSL_VERSION_NUMBER) - struct sa sa; - - if (!ssl || !host) - return EINVAL; - - if (sa_set_str(&sa, host, 0)) { - SSL_set_hostflags(ssl, - X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); - - if (!SSL_set1_host(ssl, host)) { - DEBUG_WARNING("SSL_set1_host error\n"); - ERR_clear_error(); - return EPROTO; - } - } - - SSL_set_verify(ssl, SSL_VERIFY_PEER, tls_verify_handler); - - return 0; -#else - (void)tc; - (void)host; - - return ENOSYS; -#endif -} - - static int print_error(const char *str, size_t len, void *unused) { (void)unused; @@ -1838,6 +1817,17 @@ static void tls_cert_destructor(void *arg) } +/** + * Adds a certificate for Server Name Indication (SNI) based certificate + * selection. An incoming client hello may contain an SNI extension which + * is used to select a local server certificate + * + * @param tls TLS context + * @param certf Filename of the certificate + * @param host Hostname that should match the SNI from client hello + * + * @return 0 if success, otherwise errorcode + */ int tls_add_certf(struct tls *tls, const char *certf, const struct pl *host) { struct tls_cert *uc; @@ -1862,7 +1852,6 @@ int tls_add_certf(struct tls *tls, const char *certf, const struct pl *host) uc->x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); if (!uc->x509) { - ERR_clear_error(); DEBUG_WARNING("Can't read certificate from file: %s\n", certf); err = ENOTSUP; goto out; @@ -1896,7 +1885,6 @@ int tls_add_certf(struct tls *tls, const char *certf, const struct pl *host) uc->pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); if (!uc->pkey) { - ERR_clear_error(); DEBUG_WARNING("Can't read private key from file: %s\n", certf); err = ENOTSUP; goto out; @@ -1904,52 +1892,78 @@ int tls_add_certf(struct tls *tls, const char *certf, const struct pl *host) out: BIO_free(bio); - if (err) + if (err) { + ERR_clear_error(); mem_deref(uc); - else + } + else { list_append(&tls->certs, &uc->le, uc); + } return err; } +/** + * Returns the X509 of the TLS certificate + * + * @param hc TLS certificate + * + * @return The OpenSSL X509 + */ X509 *tls_cert_x509(struct tls_cert *hc) { return hc ? hc->x509 : NULL; } +/** + * Returns the private key of the TLS certificate + * + * @param hc TLS certificate + * + * @return The OpenSSL EVP_PKEY + */ EVP_PKEY *tls_cert_pkey(struct tls_cert *hc) { return hc ? hc->pkey : NULL; } +/** + * Returns the certificate chain of the TLS certificate + * + * @param hc TLS certificate + * + * @return The OpenSSL stack of X509 + */ STACK_OF(X509*) tls_cert_chain(struct tls_cert *hc) { return hc ? hc->chain : NULL; } +/** + * Returns the host name of the TLS certificate + * + * @param hc TLS certificate + * + * @return The host name + */ const char *tls_cert_host(struct tls_cert *hc) { return hc ? hc->host : NULL; } +/** + * Returns the list of TLS certificates + * + * @param tls TLS context + * + * @return The list + */ const struct list *tls_certs(const struct tls *tls) { return tls ? &tls->certs : NULL; } - - -SSL *tls_conn_ssl(struct tls_conn *tc) -{ - return tc ? tc->ssl : NULL; -} - - -struct tls *tls_conn_tls(struct tls_conn *tc) -{ - return tc ? tc->tls : NULL; -} diff --git a/src/tls/openssl/tls.h b/src/tls/openssl/tls.h index d11608b22..ea435074b 100644 --- a/src/tls/openssl/tls.h +++ b/src/tls/openssl/tls.h @@ -38,6 +38,9 @@ STACK_OF(X509*) tls_cert_chain(struct tls_cert *hc); const char *tls_cert_host(struct tls_cert *hc); const struct list *tls_certs(const struct tls *tls); -SSL *tls_conn_ssl(struct tls_conn *tc); -struct tls *tls_conn_tls(struct tls_conn *tc); - +struct tls_cert *tls_cert_for_sni(const struct tls *tls, const char *sni); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) +int tls_verify_handler(int ok, X509_STORE_CTX *ctx); +void tls_enable_sni(struct tls *tls); +#endif From 28ebe1f09775d6cd4949f5913c8f332c807e8b91 Mon Sep 17 00:00:00 2001 From: Christian Spielberger Date: Tue, 20 Dec 2022 08:27:25 +0100 Subject: [PATCH 3/8] tls: replace pl by const char * in API function parameter --- include/re_tls.h | 2 +- src/tls/openssl/tls.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/re_tls.h b/include/re_tls.h index 9e0c37dd5..9f9ccdeb6 100644 --- a/include/re_tls.h +++ b/include/re_tls.h @@ -120,4 +120,4 @@ struct evp_pkey_st; int tls_set_certificate_openssl(struct tls *tls, struct x509_st *cert, struct evp_pkey_st *pkey, bool up_ref); -int tls_add_certf(struct tls *tls, const char *certf, const struct pl *host); +int tls_add_certf(struct tls *tls, const char *certf, const char *host); diff --git a/src/tls/openssl/tls.c b/src/tls/openssl/tls.c index a40d7ad75..dcc1a0c2b 100644 --- a/src/tls/openssl/tls.c +++ b/src/tls/openssl/tls.c @@ -1828,7 +1828,7 @@ static void tls_cert_destructor(void *arg) * * @return 0 if success, otherwise errorcode */ -int tls_add_certf(struct tls *tls, const char *certf, const struct pl *host) +int tls_add_certf(struct tls *tls, const char *certf, const char *host) { struct tls_cert *uc; BIO *bio = NULL; @@ -1838,8 +1838,8 @@ int tls_add_certf(struct tls *tls, const char *certf, const struct pl *host) return EINVAL; uc = mem_zalloc(sizeof(*uc), tls_cert_destructor); - if (pl_isset(host)) { - err = pl_strdup(&uc->host, host); + if (str_isset(host)) { + err = str_dup(&uc->host, host); if (err) goto out; } From 1f899a765df9592803ec99293735d5ae05868893 Mon Sep 17 00:00:00 2001 From: Christian Spielberger Date: Wed, 21 Dec 2022 09:27:54 +0100 Subject: [PATCH 4/8] tls: fix common name comparision --- src/tls/openssl/sni.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tls/openssl/sni.c b/src/tls/openssl/sni.c index 7f68cdf19..7e0ef1358 100644 --- a/src/tls/openssl/sni.c +++ b/src/tls/openssl/sni.c @@ -97,8 +97,8 @@ struct tls_cert *tls_cert_for_sni(const struct tls *tls, const char *sni) if (!str_isset(sni)) return list_head(certs)->data; - sz = str_len(sni); - if (sz >= TLSEXT_MAXLEN_host_name) + sz = str_len(sni) + 1; + if (sz > TLSEXT_MAXLEN_host_name) return NULL; cn = mem_zalloc(sz, NULL); @@ -208,6 +208,7 @@ static int ssl_servername_handler(SSL *ssl, int *al, void *arg) if (!uc) goto out; + DEBUG_INFO("found cert for sni %s\n", sni); (void)ssl_use_cert(ssl, uc); out: From 74eeb28a69875204c851c2e9e573c64f514b4d8c Mon Sep 17 00:00:00 2001 From: Christian Spielberger Date: Wed, 21 Dec 2022 09:57:31 +0100 Subject: [PATCH 5/8] tls: use BIO_reset() in tls_add_certf() --- src/tls/openssl/tls.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tls/openssl/tls.c b/src/tls/openssl/tls.c index dcc1a0c2b..5c4923276 100644 --- a/src/tls/openssl/tls.c +++ b/src/tls/openssl/tls.c @@ -1876,8 +1876,7 @@ int tls_add_certf(struct tls *tls, const char *certf, const char *host) } } - BIO_free(bio); - bio = BIO_new_file(certf, "r"); + BIO_reset(bio); if (!bio) { err = EIO; goto out; From 26868cfc51e045adce70b981d4d3b7337edfe66f Mon Sep 17 00:00:00 2001 From: Christian Spielberger Date: Wed, 21 Dec 2022 10:05:02 +0100 Subject: [PATCH 6/8] tls: cast size_t to int --- src/tls/openssl/sni.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tls/openssl/sni.c b/src/tls/openssl/sni.c index 7e0ef1358..b3f69efaf 100644 --- a/src/tls/openssl/sni.c +++ b/src/tls/openssl/sni.c @@ -116,7 +116,7 @@ struct tls_cert *tls_cert_for_sni(const struct tls *tls, const char *sni) } nm = X509_get_subject_name(x509); - X509_NAME_get_text_by_NID(nm, NID_commonName, cn, sz); + X509_NAME_get_text_by_NID(nm, NID_commonName, cn, (int) sz); if (!str_cmp(sni, cn)) break; From 41c4e729575ba183e7b2ea896903240e6e0e5548 Mon Sep 17 00:00:00 2001 From: Christian Spielberger Date: Thu, 22 Dec 2022 12:07:20 +0100 Subject: [PATCH 7/8] tls: check for out of memory --- src/tls/openssl/tls.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tls/openssl/tls.c b/src/tls/openssl/tls.c index 5c4923276..19af84444 100644 --- a/src/tls/openssl/tls.c +++ b/src/tls/openssl/tls.c @@ -1838,6 +1838,9 @@ int tls_add_certf(struct tls *tls, const char *certf, const char *host) return EINVAL; uc = mem_zalloc(sizeof(*uc), tls_cert_destructor); + if (!uc) + return ENOMEM; + if (str_isset(host)) { err = str_dup(&uc->host, host); if (err) From 6e0edb19875e43b5a42e6accab6214cec5bd09b7 Mon Sep 17 00:00:00 2001 From: Christian Spielberger Date: Thu, 29 Dec 2022 08:07:34 +0100 Subject: [PATCH 8/8] tls: enable sni when first TLS server certificate is added --- src/tls/openssl/tls.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tls/openssl/tls.c b/src/tls/openssl/tls.c index 19af84444..85438dfb7 100644 --- a/src/tls/openssl/tls.c +++ b/src/tls/openssl/tls.c @@ -290,7 +290,6 @@ int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile, } } - tls_enable_sni(tls); err = hash_alloc(&tls->reuse.ht_sessions, 256); if (err) goto out; @@ -1900,6 +1899,8 @@ int tls_add_certf(struct tls *tls, const char *certf, const char *host) } else { list_append(&tls->certs, &uc->le, uc); + if (list_count(&tls->certs) == 1) + tls_enable_sni(tls); } return err;