From c683d55e05489f37af5ff860a6fd98064cdff693 Mon Sep 17 00:00:00 2001 From: Jered Floyd Date: Tue, 28 Jul 2026 13:33:07 +0000 Subject: [PATCH 01/12] Resolve OpenSSL 4.0 build issues - accessors for ASN1_STRING and const-iness --- plugins/certifier/certifier.cc | 8 ++++++- .../cert_reporting_tool.cc | 2 +- plugins/experimental/sslheaders/expand.cc | 8 +++---- .../txn_box/plugin/src/ts_util.cc | 2 +- src/api/InkAPI.cc | 6 ++--- src/iocore/net/OCSPStapling.cc | 4 ++-- src/iocore/net/SSLNetVConnection.cc | 2 +- src/iocore/net/SSLUtils.cc | 8 +++---- src/tscore/X509HostnameValidator.cc | 22 +++++++++---------- 9 files changed, 34 insertions(+), 28 deletions(-) diff --git a/plugins/certifier/certifier.cc b/plugins/certifier/certifier.cc index 563a39bd40f..a9f0e8e614a 100644 --- a/plugins/certifier/certifier.cc +++ b/plugins/certifier/certifier.cc @@ -401,12 +401,18 @@ mkcrt(const std::string &commonName, int serial) X509_gmtime_adj(X509_get_notAfter(cert.get()), static_cast(3650) * 24 * 3600); // Get handle to subject name - X509_NAME *n = X509_get_subject_name(cert.get()); + X509_NAME *n = X509_NAME_dup(X509_get_subject_name(cert.get())); // Set common name field if (X509_NAME_add_entry_by_txt(n, "CN", MBSTRING_ASC, (unsigned char *)commonName.c_str(), -1, -1, 0) != 1) { TSError("[%s] %s: failed to add certificate subject CN", PLUGIN_NAME, __func__); return nullptr; } + if (X509_set_subject_name(cert.get(), n) != 1) { + TSError("[%s] %s: failed to set certificate subject", PLUGIN_NAME, __func__); + X509_NAME_free(n); + return nullptr; + } + X509_NAME_free(n); // Set Traffic Server public key if (X509_set_pubkey(cert.get(), ca_pkey_scoped.get()) == 0) { diff --git a/plugins/experimental/cert_reporting_tool/cert_reporting_tool.cc b/plugins/experimental/cert_reporting_tool/cert_reporting_tool.cc index fd1d49f1486..3613508890a 100644 --- a/plugins/experimental/cert_reporting_tool/cert_reporting_tool.cc +++ b/plugins/experimental/cert_reporting_tool/cert_reporting_tool.cc @@ -65,7 +65,7 @@ dump_context(const char *ca_path, const char *ck_path) // expiration date, serial number, common name, and subject alternative names const ASN1_TIME *not_after = X509_get_notAfter(cert); const ASN1_INTEGER *serial = X509_get_serialNumber(cert); - X509_NAME *subject_name = X509_get_subject_name(cert); + const X509_NAME *subject_name = X509_get_subject_name(cert); // Subject name BIO *subject_bio = BIO_new(BIO_s_mem()); diff --git a/plugins/experimental/sslheaders/expand.cc b/plugins/experimental/sslheaders/expand.cc index f6a8a0c7548..cbba6594240 100644 --- a/plugins/experimental/sslheaders/expand.cc +++ b/plugins/experimental/sslheaders/expand.cc @@ -49,14 +49,14 @@ x509_expand_certificate(X509 *x509, BIO *bio) static void x509_expand_subject(X509 *x509, BIO *bio) { - X509_NAME *name = X509_get_subject_name(x509); + const X509_NAME *name = X509_get_subject_name(x509); X509_NAME_print_ex(bio, name, 0 /* indent */, XN_FLAG_ONELINE); } static void x509_expand_issuer(X509 *x509, BIO *bio) { - X509_NAME *name = X509_get_issuer_name(x509); + const X509_NAME *name = X509_get_issuer_name(x509); X509_NAME_print_ex(bio, name, 0 /* indent */, XN_FLAG_ONELINE); } @@ -72,8 +72,8 @@ x509_expand_signature(X509 *x509, BIO *bio) { const ASN1_BIT_STRING *sig; X509_get0_signature(&sig, nullptr, x509); - const char *ptr = reinterpret_cast(sig->data); - const char *end = ptr + sig->length; + const char *ptr = reinterpret_cast(ASN1_STRING_get0_data(sig)); + const char *end = ptr + ASN1_STRING_length(sig); // The canonical OpenSSL way to format the signature seems to be // X509_signature_dump(). However that separates each byte with a ':', which is diff --git a/plugins/experimental/txn_box/plugin/src/ts_util.cc b/plugins/experimental/txn_box/plugin/src/ts_util.cc index a1881768956..4c70a0fd552 100644 --- a/plugins/experimental/txn_box/plugin/src/ts_util.cc +++ b/plugins/experimental/txn_box/plugin/src/ts_util.cc @@ -1112,7 +1112,7 @@ ssl_nid(swoc::TextView const &name) namespace { TextView - ssl_value_for(X509_NAME *name, int nid) + ssl_value_for(const X509_NAME *name, int nid) { if (int loc = X509_NAME_get_index_by_NID(name, nid, -1); loc >= 0) { if (auto entry = X509_NAME_get_entry(name, loc); entry != nullptr) { diff --git a/src/api/InkAPI.cc b/src/api/InkAPI.cc index 6d085befbb4..8317d4b90c6 100644 --- a/src/api/InkAPI.cc +++ b/src/api/InkAPI.cc @@ -8316,9 +8316,9 @@ TSSslServerCertUpdate(const char *cert_path, const char *key_path) } // Extract common name - int pos = X509_NAME_get_index_by_NID(X509_get_subject_name(cert.get()), NID_commonName, -1); - X509_NAME_ENTRY *common_name = X509_NAME_get_entry(X509_get_subject_name(cert.get()), pos); - ASN1_STRING *common_name_asn1 = X509_NAME_ENTRY_get_data(common_name); + const int pos = X509_NAME_get_index_by_NID(X509_get_subject_name(cert.get()), NID_commonName, -1); + const X509_NAME_ENTRY *common_name = X509_NAME_get_entry(X509_get_subject_name(cert.get()), pos); + const ASN1_STRING *common_name_asn1 = X509_NAME_ENTRY_get_data(common_name); char *common_name_str = reinterpret_cast(const_cast(ASN1_STRING_get0_data(common_name_asn1))); if (ASN1_STRING_length(common_name_asn1) != static_cast(strlen(common_name_str))) { // Embedded null char diff --git a/src/iocore/net/OCSPStapling.cc b/src/iocore/net/OCSPStapling.cc index 9a0592f49b3..3561daee539 100644 --- a/src/iocore/net/OCSPStapling.cc +++ b/src/iocore/net/OCSPStapling.cc @@ -490,7 +490,7 @@ TS_OCSP_cert_id_new(const EVP_MD *dgst, const X509_NAME *issuerName, const ASN1_ } /* Calculate the issuerKey hash, excluding tag and length */ - if (!EVP_Digest(issuerKey->data, issuerKey->length, md, &i, dgst, nullptr)) { + if (!EVP_Digest(ASN1_STRING_get0_data(issuerKey), ASN1_STRING_length(issuerKey), md, &i, dgst, nullptr)) { goto err; } @@ -516,7 +516,7 @@ TS_OCSP_cert_to_id(const EVP_MD *dgst, const X509 *subject, const X509 *issuer) { const X509_NAME *iname; const ASN1_INTEGER *serial; - ASN1_BIT_STRING *ikey; + const ASN1_BIT_STRING *ikey; if (!dgst) { dgst = EVP_sha1(); diff --git a/src/iocore/net/SSLNetVConnection.cc b/src/iocore/net/SSLNetVConnection.cc index bb7ccb5ddec..8d2abaf7af0 100644 --- a/src/iocore/net/SSLNetVConnection.cc +++ b/src/iocore/net/SSLNetVConnection.cc @@ -167,7 +167,7 @@ SSLNetVConnection::_unbindSSLObject() } static void -debug_certificate_name(const char *msg, X509_NAME *name) +debug_certificate_name(const char *msg, const X509_NAME *name) { BIO *bio; diff --git a/src/iocore/net/SSLUtils.cc b/src/iocore/net/SSLUtils.cc index a88bdceb564..0a07ac1b46b 100644 --- a/src/iocore/net/SSLUtils.cc +++ b/src/iocore/net/SSLUtils.cc @@ -1004,7 +1004,7 @@ SSLMultiCertConfigLoader::check_server_cert_now(X509 *cert, const char *certname } /* CheckServerCertNow() */ static char * -asn1_strdup(ASN1_STRING *s) +asn1_strdup(const ASN1_STRING *s) { // Make sure we have an 8-bit encoding. ink_assert(ASN1_STRING_type(s) == V_ASN1_IA5STRING || ASN1_STRING_type(s) == V_ASN1_UTF8STRING || @@ -2212,7 +2212,7 @@ SSLMultiCertConfigLoader::load_certs_and_cross_reference_names( std::set name_set; // Grub through the names in the certs - X509_NAME *subject = nullptr; + const X509_NAME *subject = nullptr; // Insert a key for the subject CN. subject = X509_get_subject_name(cert); @@ -2225,8 +2225,8 @@ SSLMultiCertConfigLoader::load_certs_and_cross_reference_names( break; } - X509_NAME_ENTRY *e = X509_NAME_get_entry(subject, pos); - ASN1_STRING *cn = X509_NAME_ENTRY_get_data(e); + const X509_NAME_ENTRY *e = X509_NAME_get_entry(subject, pos); + const ASN1_STRING *cn = X509_NAME_ENTRY_get_data(e); subj_name = asn1_strdup(cn); Dbg(dbg_ctl_ssl_load, "subj '%s' in certificate %s %p", subj_name.get(), data.cert_names_list[i].c_str(), cert); diff --git a/src/tscore/X509HostnameValidator.cc b/src/tscore/X509HostnameValidator.cc index 0efbcac99df..1d751339646 100644 --- a/src/tscore/X509HostnameValidator.cc +++ b/src/tscore/X509HostnameValidator.cc @@ -206,12 +206,12 @@ do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal, const unsigned cha { bool retval = false; - if (!a->data || !a->length || cmp_type != a->type) { + if (!ASN1_STRING_get0_data(a) || !ASN1_STRING_length(a) || cmp_type != ASN1_STRING_type(a)) { return false; } - retval = equal(a->data, a->length, b, blen); + retval = equal(ASN1_STRING_get0_data(a), ASN1_STRING_length(a), b, blen); if (retval && peername) { - *peername = ats_strndup((char *)a->data, a->length); + *peername = ats_strndup((char *)ASN1_STRING_get0_data(a), ASN1_STRING_length(a)); } return retval; } @@ -219,11 +219,11 @@ do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal, const unsigned cha bool validate_hostname(X509 *x, std::string_view hostname, bool is_ip, char **peername) { - GENERAL_NAMES *gens = nullptr; - X509_NAME *name = nullptr; - int i; - int alt_type; - bool retval = false; + GENERAL_NAMES *gens = nullptr; + const X509_NAME *name = nullptr; + int i; + int alt_type; + bool retval = false; ; equal_fn equal; auto const *hostname_data = reinterpret_cast(hostname.data()); @@ -273,9 +273,9 @@ validate_hostname(X509 *x, std::string_view hostname, bool is_ip, char **peernam name = X509_get_subject_name(x); while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) { - ASN1_STRING *str; - int astrlen; - unsigned char *astr; + const ASN1_STRING *str; + int astrlen; + unsigned char *astr; str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); // Convert to UTF-8 astrlen = ASN1_STRING_to_UTF8(&astr, str); From d8a37f9c28a1c16f4ee23fabf9906b84c24a6e13 Mon Sep 17 00:00:00 2001 From: Jered Floyd Date: Tue, 28 Jul 2026 18:41:28 +0000 Subject: [PATCH 02/12] Ran clang-format to fix formatting --- src/iocore/net/OCSPStapling.cc | 6 +++--- src/iocore/net/SSLUtils.cc | 2 +- src/tscore/X509HostnameValidator.cc | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/iocore/net/OCSPStapling.cc b/src/iocore/net/OCSPStapling.cc index 3561daee539..e07f18418aa 100644 --- a/src/iocore/net/OCSPStapling.cc +++ b/src/iocore/net/OCSPStapling.cc @@ -514,9 +514,9 @@ TS_OCSP_cert_id_new(const EVP_MD *dgst, const X509_NAME *issuerName, const ASN1_ TS_OCSP_CERTID * TS_OCSP_cert_to_id(const EVP_MD *dgst, const X509 *subject, const X509 *issuer) { - const X509_NAME *iname; - const ASN1_INTEGER *serial; - const ASN1_BIT_STRING *ikey; + const X509_NAME *iname; + const ASN1_INTEGER *serial; + const ASN1_BIT_STRING *ikey; if (!dgst) { dgst = EVP_sha1(); diff --git a/src/iocore/net/SSLUtils.cc b/src/iocore/net/SSLUtils.cc index 0a07ac1b46b..8baa6178b1f 100644 --- a/src/iocore/net/SSLUtils.cc +++ b/src/iocore/net/SSLUtils.cc @@ -2227,7 +2227,7 @@ SSLMultiCertConfigLoader::load_certs_and_cross_reference_names( const X509_NAME_ENTRY *e = X509_NAME_get_entry(subject, pos); const ASN1_STRING *cn = X509_NAME_ENTRY_get_data(e); - subj_name = asn1_strdup(cn); + subj_name = asn1_strdup(cn); Dbg(dbg_ctl_ssl_load, "subj '%s' in certificate %s %p", subj_name.get(), data.cert_names_list[i].c_str(), cert); name_set.insert(subj_name.get()); diff --git a/src/tscore/X509HostnameValidator.cc b/src/tscore/X509HostnameValidator.cc index 1d751339646..7202d2be2bd 100644 --- a/src/tscore/X509HostnameValidator.cc +++ b/src/tscore/X509HostnameValidator.cc @@ -221,9 +221,9 @@ validate_hostname(X509 *x, std::string_view hostname, bool is_ip, char **peernam { GENERAL_NAMES *gens = nullptr; const X509_NAME *name = nullptr; - int i; - int alt_type; - bool retval = false; + int i; + int alt_type; + bool retval = false; ; equal_fn equal; auto const *hostname_data = reinterpret_cast(hostname.data()); @@ -273,9 +273,9 @@ validate_hostname(X509 *x, std::string_view hostname, bool is_ip, char **peernam name = X509_get_subject_name(x); while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) { - const ASN1_STRING *str; - int astrlen; - unsigned char *astr; + const ASN1_STRING *str; + int astrlen; + unsigned char *astr; str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); // Convert to UTF-8 astrlen = ASN1_STRING_to_UTF8(&astr, str); From 6874c510356d88349de02f33b50636a4aee8b1c9 Mon Sep 17 00:00:00 2001 From: Masakazu Kitajo Date: Mon, 3 Aug 2026 10:56:01 -0600 Subject: [PATCH 03/12] Fix X509_NAME_get_index_by_NID const mismatch on OpenSSL 1.1.1 OpenSSL 1.1.1 declares the first argument as non-const while newer releases declare it const, so building against different versions failed depending on which signature was in effect. Add a const_cast at each call site to keep the const-qualified variables introduced by this PR buildable against both, matching the existing precedent in OCSPStapling.cc. --- plugins/experimental/txn_box/plugin/src/ts_util.cc | 3 ++- src/iocore/net/SSLUtils.cc | 3 ++- src/tscore/X509HostnameValidator.cc | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/experimental/txn_box/plugin/src/ts_util.cc b/plugins/experimental/txn_box/plugin/src/ts_util.cc index 4c70a0fd552..935fba6be8f 100644 --- a/plugins/experimental/txn_box/plugin/src/ts_util.cc +++ b/plugins/experimental/txn_box/plugin/src/ts_util.cc @@ -1114,7 +1114,8 @@ namespace TextView ssl_value_for(const X509_NAME *name, int nid) { - if (int loc = X509_NAME_get_index_by_NID(name, nid, -1); loc >= 0) { + // const_cast is needed for OpenSSL 1.1.1 + if (int loc = X509_NAME_get_index_by_NID(const_cast(name), nid, -1); loc >= 0) { if (auto entry = X509_NAME_get_entry(name, loc); entry != nullptr) { if (auto value = X509_NAME_ENTRY_get_data(entry); value != nullptr) { return {reinterpret_cast(ASN1_STRING_get0_data(value)), size_t(ASN1_STRING_length(value))}; diff --git a/src/iocore/net/SSLUtils.cc b/src/iocore/net/SSLUtils.cc index 8baa6178b1f..3de5ec5c2b1 100644 --- a/src/iocore/net/SSLUtils.cc +++ b/src/iocore/net/SSLUtils.cc @@ -2220,7 +2220,8 @@ SSLMultiCertConfigLoader::load_certs_and_cross_reference_names( if (subject) { int pos = -1; for (;;) { - pos = X509_NAME_get_index_by_NID(subject, NID_commonName, pos); + // const_cast is needed for OpenSSL 1.1.1 + pos = X509_NAME_get_index_by_NID(const_cast(subject), NID_commonName, pos); if (pos == -1) { break; } diff --git a/src/tscore/X509HostnameValidator.cc b/src/tscore/X509HostnameValidator.cc index 7202d2be2bd..80a05a189b3 100644 --- a/src/tscore/X509HostnameValidator.cc +++ b/src/tscore/X509HostnameValidator.cc @@ -272,7 +272,8 @@ validate_hostname(X509 *x, std::string_view hostname, bool is_ip, char **peernam i = -1; name = X509_get_subject_name(x); - while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) { + // const_cast is needed for OpenSSL 1.1.1 + while ((i = X509_NAME_get_index_by_NID(const_cast(name), NID_commonName, i)) >= 0) { const ASN1_STRING *str; int astrlen; unsigned char *astr; From a94e4f6f741d5871e29bd4a969b563eb390fc747 Mon Sep 17 00:00:00 2001 From: Masakazu Kitajo Date: Mon, 3 Aug 2026 11:58:21 -0600 Subject: [PATCH 04/12] Drop const_cast in favor of auto for local X509_NAME variables X509_get_subject_name and X509_NAME_get_index_by_NID have their argument constness changed together across OpenSSL versions, so a local variable declared with auto tracks whatever type is correct for the OpenSSL version in use, without a cast. This applies only to the two purely local variables; ts_util.cc ssl_value_for keeps its const_cast since its parameter type is shared across multiple callers. --- src/iocore/net/SSLUtils.cc | 6 ++---- src/tscore/X509HostnameValidator.cc | 16 +++++++--------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/iocore/net/SSLUtils.cc b/src/iocore/net/SSLUtils.cc index 3de5ec5c2b1..0d31fcf5646 100644 --- a/src/iocore/net/SSLUtils.cc +++ b/src/iocore/net/SSLUtils.cc @@ -2212,16 +2212,14 @@ SSLMultiCertConfigLoader::load_certs_and_cross_reference_names( std::set name_set; // Grub through the names in the certs - const X509_NAME *subject = nullptr; // Insert a key for the subject CN. - subject = X509_get_subject_name(cert); + auto *subject = X509_get_subject_name(cert); ats_scoped_str subj_name; if (subject) { int pos = -1; for (;;) { - // const_cast is needed for OpenSSL 1.1.1 - pos = X509_NAME_get_index_by_NID(const_cast(subject), NID_commonName, pos); + pos = X509_NAME_get_index_by_NID(subject, NID_commonName, pos); if (pos == -1) { break; } diff --git a/src/tscore/X509HostnameValidator.cc b/src/tscore/X509HostnameValidator.cc index 80a05a189b3..b70cf95b2fc 100644 --- a/src/tscore/X509HostnameValidator.cc +++ b/src/tscore/X509HostnameValidator.cc @@ -219,11 +219,10 @@ do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal, const unsigned cha bool validate_hostname(X509 *x, std::string_view hostname, bool is_ip, char **peername) { - GENERAL_NAMES *gens = nullptr; - const X509_NAME *name = nullptr; - int i; - int alt_type; - bool retval = false; + GENERAL_NAMES *gens = nullptr; + int i; + int alt_type; + bool retval = false; ; equal_fn equal; auto const *hostname_data = reinterpret_cast(hostname.data()); @@ -269,11 +268,10 @@ validate_hostname(X509 *x, std::string_view hostname, bool is_ip, char **peernam } } // No SAN match -- check the subject - i = -1; - name = X509_get_subject_name(x); + i = -1; + auto *name = X509_get_subject_name(x); - // const_cast is needed for OpenSSL 1.1.1 - while ((i = X509_NAME_get_index_by_NID(const_cast(name), NID_commonName, i)) >= 0) { + while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) { const ASN1_STRING *str; int astrlen; unsigned char *astr; From 42e630c5a863b9f1606063787c7e16ecf95ae77b Mon Sep 17 00:00:00 2001 From: Masakazu Kitajo Date: Mon, 3 Aug 2026 16:20:50 -0600 Subject: [PATCH 05/12] Fix const-cast style and X509_NAME leak flagged by Copilot review X509HostnameValidator.cc cast ASN1_STRING_get0_data return value to non-const before an ats_strndup call that only wants const char *; drop the const instead of adding it back needlessly. certifier.cc leaked the duplicated X509_NAME on the X509_NAME_add_entry_by_txt failure path, and never checked X509_NAME_dup for allocation failure. --- plugins/certifier/certifier.cc | 5 +++++ src/tscore/X509HostnameValidator.cc | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/certifier/certifier.cc b/plugins/certifier/certifier.cc index a9f0e8e614a..f3518113e3a 100644 --- a/plugins/certifier/certifier.cc +++ b/plugins/certifier/certifier.cc @@ -402,9 +402,14 @@ mkcrt(const std::string &commonName, int serial) // Get handle to subject name X509_NAME *n = X509_NAME_dup(X509_get_subject_name(cert.get())); + if (n == nullptr) { + TSError("[%s] %s: failed to duplicate certificate subject", PLUGIN_NAME, __func__); + return nullptr; + } // Set common name field if (X509_NAME_add_entry_by_txt(n, "CN", MBSTRING_ASC, (unsigned char *)commonName.c_str(), -1, -1, 0) != 1) { TSError("[%s] %s: failed to add certificate subject CN", PLUGIN_NAME, __func__); + X509_NAME_free(n); return nullptr; } if (X509_set_subject_name(cert.get(), n) != 1) { diff --git a/src/tscore/X509HostnameValidator.cc b/src/tscore/X509HostnameValidator.cc index b70cf95b2fc..6a10a55a0c8 100644 --- a/src/tscore/X509HostnameValidator.cc +++ b/src/tscore/X509HostnameValidator.cc @@ -211,7 +211,7 @@ do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal, const unsigned cha } retval = equal(ASN1_STRING_get0_data(a), ASN1_STRING_length(a), b, blen); if (retval && peername) { - *peername = ats_strndup((char *)ASN1_STRING_get0_data(a), ASN1_STRING_length(a)); + *peername = ats_strndup((const char *)ASN1_STRING_get0_data(a), ASN1_STRING_length(a)); } return retval; } From 172defe0cb92ea54d8fab62f6edc3247e301e70a Mon Sep 17 00:00:00 2001 From: Masakazu Kitajo Date: Mon, 3 Aug 2026 16:44:02 -0600 Subject: [PATCH 06/12] Make Cripts X509 accessors OpenSSL-4-compatible CertBase::X509Value took hardcoded function pointer types for X509_get_subject_name, X509_get_issuer_name, X509_getm_notBefore, and X509_getm_notAfter, but those accessors change constness in different directions across OpenSSL versions, so no single hardcoded signature builds everywhere. Deduce the parameter type from the actual accessor via decltype instead. Signature::_load and _write_ip_address also read ASN1_STRING fields directly, which breaks once the struct is opaque; switch to the accessor functions used elsewhere in this codebase. --- include/cripts/Certs.hpp | 8 ++++---- src/cripts/Certs.cc | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/cripts/Certs.hpp b/include/cripts/Certs.hpp index f19fb8f41b0..54134ccaadc 100644 --- a/include/cripts/Certs.hpp +++ b/include/cripts/Certs.hpp @@ -107,10 +107,10 @@ class CertBase } } - void _load_name(X509_NAME *(*getter)(const X509 *)) const; - void _load_integer(ASN1_INTEGER *(*getter)(X509 *)) const; - void _load_long(long (*getter)(const X509 *)) const; - void _load_time(ASN1_TIME *(*getter)(const X509 *)) const; + void _load_name(decltype(&X509_get_subject_name) getter) const; + void _load_integer(decltype(&X509_get_serialNumber) getter) const; + void _load_long(decltype(&X509_get_version) getter) const; + void _load_time(decltype(&X509_get_notBefore) getter) const; CertBase *_owner = nullptr; mutable std::unique_ptr _bio{nullptr, BIO_free}; diff --git a/src/cripts/Certs.cc b/src/cripts/Certs.cc index 8f893c14dc2..fb089c0497c 100644 --- a/src/cripts/Certs.cc +++ b/src/cripts/Certs.cc @@ -54,8 +54,8 @@ CertBase::Signature::_load() const if (!_ready && _owner->_x509) { const ASN1_BIT_STRING *sig; X509_get0_signature(&sig, nullptr, _owner->_x509); - const char *ptr = reinterpret_cast(sig->data); - const char *end = ptr + sig->length; + const char *ptr = reinterpret_cast(ASN1_STRING_get0_data(sig)); + const char *end = ptr + ASN1_STRING_length(sig); super_type::_load(); for (; ptr < end; ++ptr) { @@ -78,7 +78,7 @@ CertBase::X509Value::_update_value() const } void -CertBase::X509Value::_load_name(X509_NAME *(*getter)(const X509 *)) const +CertBase::X509Value::_load_name(decltype(&X509_get_subject_name) getter) const { if (!_ready && _owner->_x509) { auto *name = getter(_owner->_x509); @@ -95,7 +95,7 @@ CertBase::X509Value::_load_name(X509_NAME *(*getter)(const X509 *)) const } void -CertBase::X509Value::_load_integer(ASN1_INTEGER *(*getter)(X509 *)) const +CertBase::X509Value::_load_integer(decltype(&X509_get_serialNumber) getter) const { if (!_ready && _owner->_x509) { auto *value = getter(_owner->_x509); @@ -107,7 +107,7 @@ CertBase::X509Value::_load_integer(ASN1_INTEGER *(*getter)(X509 *)) const } void -CertBase::X509Value::_load_long(long (*getter)(const X509 *)) const +CertBase::X509Value::_load_long(decltype(&X509_get_version) getter) const { if (!_ready && _owner->_x509) { auto value = getter(_owner->_x509); @@ -119,7 +119,7 @@ CertBase::X509Value::_load_long(long (*getter)(const X509 *)) const } void -CertBase::X509Value::_load_time(ASN1_TIME *(*getter)(const X509 *)) const +CertBase::X509Value::_load_time(decltype(&X509_get_notBefore) getter) const { if (!_ready && _owner->_x509) { auto *time = getter(_owner->_x509); @@ -153,8 +153,8 @@ namespace _write_ip_address(const ASN1_OCTET_STRING *ip, BIO *_bio) { char buffer[INET6_ADDRSTRLEN]; - const unsigned char *raw = ip->data; - int len = ip->length; + const unsigned char *raw = ASN1_STRING_get0_data(ip); + int len = ASN1_STRING_length(ip); if (inet_ntop(len == 4 ? AF_INET : AF_INET6, raw, buffer, sizeof(buffer))) { BIO_printf(_bio, "%s", buffer); From 24666dd51b5a156a9d458cd284773335aa001838 Mon Sep 17 00:00:00 2001 From: Masakazu Kitajo Date: Mon, 3 Aug 2026 16:44:21 -0600 Subject: [PATCH 07/12] Fix lua plugin X509_NAME/ASN1_STRING OpenSSL 4 compatibility get_x509_name_string only reads through the name via X509_NAME_print_ex, so accept a const X509_NAME * to match callers that pass X509_get_subject_name/X509_get_issuer_name results directly. get_x509_signature_string read the ASN1_STRING struct fields directly, which breaks once the struct is opaque; use ASN1_STRING_get0_data/ASN1_STRING_length instead. --- plugins/lua/ts_lua_client_cert_helpers.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/lua/ts_lua_client_cert_helpers.h b/plugins/lua/ts_lua_client_cert_helpers.h index c7f20b50ebc..a7098ed2c0a 100644 --- a/plugins/lua/ts_lua_client_cert_helpers.h +++ b/plugins/lua/ts_lua_client_cert_helpers.h @@ -18,7 +18,7 @@ // Helper functions for certificate data extraction static std::string -get_x509_name_string(X509_NAME *name) +get_x509_name_string(const X509_NAME *name) { if (!name) { return ""; @@ -157,12 +157,15 @@ get_x509_signature_string(X509 *cert) return ""; } - for (int i = 0; i < sig->length; i++) { - if (BIO_printf(bio, "%02x", sig->data[i]) <= 0) { + const unsigned char *sig_data = ASN1_STRING_get0_data(sig); + int sig_len = ASN1_STRING_length(sig); + + for (int i = 0; i < sig_len; i++) { + if (BIO_printf(bio, "%02x", sig_data[i]) <= 0) { BIO_free(bio); return ""; } - if (i < sig->length - 1) { + if (i < sig_len - 1) { if (BIO_printf(bio, ":") <= 0) { BIO_free(bio); return ""; From 73f63c20aa56c19888cb70fe3cdae88e98126136 Mon Sep 17 00:00:00 2001 From: Masakazu Kitajo Date: Mon, 3 Aug 2026 16:44:51 -0600 Subject: [PATCH 08/12] Fix self-signed test cert losing its CN under OpenSSL 4 make_cert_and_key mutated the X509_NAME returned by X509_get_subject_name in place, which stops compiling once that accessor can return const, and was already fragile since the returned name is only a view into the certificate internal state. Duplicate it, add the CN to the duplicate, and set it back as both subject and issuer name since this is a self-signed certificate. --- src/iocore/net/unit_tests/test_SSLDHParams.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/iocore/net/unit_tests/test_SSLDHParams.cc b/src/iocore/net/unit_tests/test_SSLDHParams.cc index 3e75f5fe097..8d0e44a4a0a 100644 --- a/src/iocore/net/unit_tests/test_SSLDHParams.cc +++ b/src/iocore/net/unit_tests/test_SSLDHParams.cc @@ -132,9 +132,12 @@ make_cert_and_key(EVP_CIPHER const *cipher = nullptr, char *pass = nullptr) X509_gmtime_adj(X509_getm_notAfter(x509), 60L * 60L * 24L * 365L); REQUIRE(X509_set_pubkey(x509, pkey) == 1); - X509_NAME *name = X509_get_subject_name(x509); + X509_NAME *name = X509_NAME_dup(X509_get_subject_name(x509)); + REQUIRE(name != nullptr); X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast("ats-test"), -1, -1, 0); + REQUIRE(X509_set_subject_name(x509, name) == 1); REQUIRE(X509_set_issuer_name(x509, name) == 1); + X509_NAME_free(name); REQUIRE(X509_sign(x509, pkey, EVP_sha256()) > 0); BIO *cert_bio = BIO_new(BIO_s_mem()); From e3d7f5d9d4ab7e4b3f9a7fbdd1aec0d6c6beb748 Mon Sep 17 00:00:00 2001 From: Masakazu Kitajo Date: Mon, 3 Aug 2026 16:45:01 -0600 Subject: [PATCH 09/12] Fix OpenSSL 4 X509_NAME const mismatches in example plugin and test tool client_context_dump.cc, verify_cert.cc, and ssl_client_verify_test.cc all held X509_get_subject_name/X509_NAME_get_entry/ X509_NAME_ENTRY_get_data results in hardcoded non-const locals or parameters, which stops compiling once those accessors return const. Switch to auto for the local variables and const for the debug_certificate parameter, matching the read-only usage in each case. --- .../c-api/client_context_dump/client_context_dump.cc | 2 +- example/plugins/c-api/verify_cert/verify_cert.cc | 2 +- tests/tools/plugins/ssl_client_verify_test.cc | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/example/plugins/c-api/client_context_dump/client_context_dump.cc b/example/plugins/c-api/client_context_dump/client_context_dump.cc index f8572d775ce..6dcb94919bb 100644 --- a/example/plugins/c-api/client_context_dump/client_context_dump.cc +++ b/example/plugins/c-api/client_context_dump/client_context_dump.cc @@ -65,7 +65,7 @@ dump_context(const char *ca_path, const char *ck_path) // expiration date, serial number, common name, and subject alternative names const ASN1_TIME *not_after = X509_get_notAfter(cert); const ASN1_INTEGER *serial = X509_get_serialNumber(cert); - X509_NAME *subject_name = X509_get_subject_name(cert); + auto *subject_name = X509_get_subject_name(cert); // Subject name BIO *subject_bio = BIO_new(BIO_s_mem()); diff --git a/example/plugins/c-api/verify_cert/verify_cert.cc b/example/plugins/c-api/verify_cert/verify_cert.cc index a3f553057fe..4c3c75446ee 100644 --- a/example/plugins/c-api/verify_cert/verify_cert.cc +++ b/example/plugins/c-api/verify_cert/verify_cert.cc @@ -37,7 +37,7 @@ namespace DbgCtl dbg_ctl{PLUGIN_NAME}; static void -debug_certificate(const char *msg, X509_NAME *name) +debug_certificate(const char *msg, const X509_NAME *name) { BIO *bio; diff --git a/tests/tools/plugins/ssl_client_verify_test.cc b/tests/tools/plugins/ssl_client_verify_test.cc index 7f3ae7c3759..a4243818e05 100644 --- a/tests/tools/plugins/ssl_client_verify_test.cc +++ b/tests/tools/plugins/ssl_client_verify_test.cc @@ -58,7 +58,7 @@ check_names(X509 *cert) bool retval = false; // Check the common name - X509_NAME *subject = X509_get_subject_name(cert); + auto *subject = X509_get_subject_name(cert); if (subject) { int pos = -1; for (; !retval;) { @@ -67,10 +67,10 @@ check_names(X509 *cert) break; } - X509_NAME_ENTRY *e = X509_NAME_get_entry(subject, pos); - ASN1_STRING *cn = X509_NAME_ENTRY_get_data(e); - char *subj_name = strndup(reinterpret_cast(ASN1_STRING_get0_data(cn)), ASN1_STRING_length(cn)); - retval = check_name(subj_name); + auto *e = X509_NAME_get_entry(subject, pos); + auto *cn = X509_NAME_ENTRY_get_data(e); + char *subj_name = strndup(reinterpret_cast(ASN1_STRING_get0_data(cn)), ASN1_STRING_length(cn)); + retval = check_name(subj_name); free(subj_name); } } From 3f4d0658e4f2411b7acb02f3331e18e7b29a0844 Mon Sep 17 00:00:00 2001 From: Masakazu Kitajo Date: Mon, 3 Aug 2026 16:50:07 -0600 Subject: [PATCH 10/12] Drop last const_cast in txn_box ssl_value_for via a template ssl_value_for is shared by four callers, each already deducing its X509_NAME pointer type with auto from X509_get_subject_name or X509_get_issuer_name, so unlike the other three call sites fixed earlier in this series, a single hardcoded parameter type cannot track the underlying accessor across OpenSSL versions. Templating the parameter on the callers deduced type removes the cast entirely. --- plugins/experimental/txn_box/plugin/src/ts_util.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/experimental/txn_box/plugin/src/ts_util.cc b/plugins/experimental/txn_box/plugin/src/ts_util.cc index 935fba6be8f..11e25d6b996 100644 --- a/plugins/experimental/txn_box/plugin/src/ts_util.cc +++ b/plugins/experimental/txn_box/plugin/src/ts_util.cc @@ -1111,11 +1111,11 @@ ssl_nid(swoc::TextView const &name) namespace { + template TextView - ssl_value_for(const X509_NAME *name, int nid) + ssl_value_for(T *name, int nid) { - // const_cast is needed for OpenSSL 1.1.1 - if (int loc = X509_NAME_get_index_by_NID(const_cast(name), nid, -1); loc >= 0) { + if (int loc = X509_NAME_get_index_by_NID(name, nid, -1); loc >= 0) { if (auto entry = X509_NAME_get_entry(name, loc); entry != nullptr) { if (auto value = X509_NAME_ENTRY_get_data(entry); value != nullptr) { return {reinterpret_cast(ASN1_STRING_get0_data(value)), size_t(ASN1_STRING_length(value))}; From 45809f671e67e843f92bf9d6bd0bb56e3b10f572 Mon Sep 17 00:00:00 2001 From: Masakazu Kitajo Date: Mon, 3 Aug 2026 16:54:40 -0600 Subject: [PATCH 11/12] Alias the Cripts X509 getter decltypes for readability Bare decltype(&X509_get_subject_name) in a parameter list reads poorly at each of the four call sites; name each getter type once via using so the declarations and out-of-line definitions just say what kind of accessor they take. --- include/cripts/Certs.hpp | 13 +++++++++---- src/cripts/Certs.cc | 8 ++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/include/cripts/Certs.hpp b/include/cripts/Certs.hpp index 54134ccaadc..9b402a070e9 100644 --- a/include/cripts/Certs.hpp +++ b/include/cripts/Certs.hpp @@ -107,10 +107,15 @@ class CertBase } } - void _load_name(decltype(&X509_get_subject_name) getter) const; - void _load_integer(decltype(&X509_get_serialNumber) getter) const; - void _load_long(decltype(&X509_get_version) getter) const; - void _load_time(decltype(&X509_get_notBefore) getter) const; + using NameGetter = decltype(&X509_get_subject_name); + using IntegerGetter = decltype(&X509_get_serialNumber); + using LongGetter = decltype(&X509_get_version); + using TimeGetter = decltype(&X509_get_notBefore); + + void _load_name(NameGetter getter) const; + void _load_integer(IntegerGetter getter) const; + void _load_long(LongGetter getter) const; + void _load_time(TimeGetter getter) const; CertBase *_owner = nullptr; mutable std::unique_ptr _bio{nullptr, BIO_free}; diff --git a/src/cripts/Certs.cc b/src/cripts/Certs.cc index fb089c0497c..006effcb90c 100644 --- a/src/cripts/Certs.cc +++ b/src/cripts/Certs.cc @@ -78,7 +78,7 @@ CertBase::X509Value::_update_value() const } void -CertBase::X509Value::_load_name(decltype(&X509_get_subject_name) getter) const +CertBase::X509Value::_load_name(NameGetter getter) const { if (!_ready && _owner->_x509) { auto *name = getter(_owner->_x509); @@ -95,7 +95,7 @@ CertBase::X509Value::_load_name(decltype(&X509_get_subject_name) getter) const } void -CertBase::X509Value::_load_integer(decltype(&X509_get_serialNumber) getter) const +CertBase::X509Value::_load_integer(IntegerGetter getter) const { if (!_ready && _owner->_x509) { auto *value = getter(_owner->_x509); @@ -107,7 +107,7 @@ CertBase::X509Value::_load_integer(decltype(&X509_get_serialNumber) getter) cons } void -CertBase::X509Value::_load_long(decltype(&X509_get_version) getter) const +CertBase::X509Value::_load_long(LongGetter getter) const { if (!_ready && _owner->_x509) { auto value = getter(_owner->_x509); @@ -119,7 +119,7 @@ CertBase::X509Value::_load_long(decltype(&X509_get_version) getter) const } void -CertBase::X509Value::_load_time(decltype(&X509_get_notBefore) getter) const +CertBase::X509Value::_load_time(TimeGetter getter) const { if (!_ready && _owner->_x509) { auto *time = getter(_owner->_x509); From 01b93173b89ae82ffa99c8b2d16c7749f4df987e Mon Sep 17 00:00:00 2001 From: Masakazu Kitajo Date: Mon, 3 Aug 2026 17:03:24 -0600 Subject: [PATCH 12/12] Adopt RAII and decltype(func(nullptr)) patterns from PR #13482 certifier.cc freed the duplicated X509_NAME manually on every path; switch to a scoped_X509_NAME unique_ptr matching the file existing scoped_X509/scoped_EVP_PKEY/scoped_SSL_CTX aliases so no path can forget to free it. ts_util.cc templated ssl_value_for just to defer the parameter type to the caller; decltype(X509_get_subject_name(nullptr)) deduces the same pointer type directly without turning it into a template. Also drop the last C-style cast this series introduced in X509HostnameValidator.cc in favor of reinterpret_cast. --- plugins/certifier/certifier.cc | 18 ++++++++---------- .../experimental/txn_box/plugin/src/ts_util.cc | 5 +++-- src/tscore/X509HostnameValidator.cc | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/plugins/certifier/certifier.cc b/plugins/certifier/certifier.cc index f3518113e3a..94a490c2fb4 100644 --- a/plugins/certifier/certifier.cc +++ b/plugins/certifier/certifier.cc @@ -88,10 +88,11 @@ template <> struct default_delete { } // namespace std /// Name aliases for unique pts to openSSL objects -using scoped_X509 = std::unique_ptr; -using scoped_X509_REQ = std::unique_ptr; -using scoped_EVP_PKEY = std::unique_ptr; -using scoped_SSL_CTX = std::unique_ptr; +using scoped_X509 = std::unique_ptr; +using scoped_X509_REQ = std::unique_ptr; +using scoped_EVP_PKEY = std::unique_ptr; +using scoped_SSL_CTX = std::unique_ptr; +using scoped_X509_NAME = std::unique_ptr; class SslLRUList { @@ -401,23 +402,20 @@ mkcrt(const std::string &commonName, int serial) X509_gmtime_adj(X509_get_notAfter(cert.get()), static_cast(3650) * 24 * 3600); // Get handle to subject name - X509_NAME *n = X509_NAME_dup(X509_get_subject_name(cert.get())); + scoped_X509_NAME n{X509_NAME_dup(X509_get_subject_name(cert.get())), X509_NAME_free}; if (n == nullptr) { TSError("[%s] %s: failed to duplicate certificate subject", PLUGIN_NAME, __func__); return nullptr; } // Set common name field - if (X509_NAME_add_entry_by_txt(n, "CN", MBSTRING_ASC, (unsigned char *)commonName.c_str(), -1, -1, 0) != 1) { + if (X509_NAME_add_entry_by_txt(n.get(), "CN", MBSTRING_ASC, (unsigned char *)commonName.c_str(), -1, -1, 0) != 1) { TSError("[%s] %s: failed to add certificate subject CN", PLUGIN_NAME, __func__); - X509_NAME_free(n); return nullptr; } - if (X509_set_subject_name(cert.get(), n) != 1) { + if (X509_set_subject_name(cert.get(), n.get()) != 1) { TSError("[%s] %s: failed to set certificate subject", PLUGIN_NAME, __func__); - X509_NAME_free(n); return nullptr; } - X509_NAME_free(n); // Set Traffic Server public key if (X509_set_pubkey(cert.get(), ca_pkey_scoped.get()) == 0) { diff --git a/plugins/experimental/txn_box/plugin/src/ts_util.cc b/plugins/experimental/txn_box/plugin/src/ts_util.cc index 11e25d6b996..c63de2f2fd7 100644 --- a/plugins/experimental/txn_box/plugin/src/ts_util.cc +++ b/plugins/experimental/txn_box/plugin/src/ts_util.cc @@ -1111,9 +1111,10 @@ ssl_nid(swoc::TextView const &name) namespace { - template + using X509_NAME_ptr = decltype(X509_get_subject_name(nullptr)); + TextView - ssl_value_for(T *name, int nid) + ssl_value_for(X509_NAME_ptr name, int nid) { if (int loc = X509_NAME_get_index_by_NID(name, nid, -1); loc >= 0) { if (auto entry = X509_NAME_get_entry(name, loc); entry != nullptr) { diff --git a/src/tscore/X509HostnameValidator.cc b/src/tscore/X509HostnameValidator.cc index 6a10a55a0c8..252c589ee74 100644 --- a/src/tscore/X509HostnameValidator.cc +++ b/src/tscore/X509HostnameValidator.cc @@ -211,7 +211,7 @@ do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal, const unsigned cha } retval = equal(ASN1_STRING_get0_data(a), ASN1_STRING_length(a), b, blen); if (retval && peername) { - *peername = ats_strndup((const char *)ASN1_STRING_get0_data(a), ASN1_STRING_length(a)); + *peername = ats_strndup(reinterpret_cast(ASN1_STRING_get0_data(a)), ASN1_STRING_length(a)); } return retval; }