https://bugs.gentoo.org/979437
https://github.com/squid-cache/squid/pull/2441
https://github.com/squid-cache/squid/pull/2501

From 00a8938a5561ae11c6e4d563c67ee85621499e18 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavol=20=C5=BD=C3=A1=C4=8Dik?= <pzacik@redhat.com>
Date: Wed, 6 May 2026 17:18:58 +0200
Subject: [PATCH] Fix OpenSSL 4.0 compatibility

Replace direct ASN1 structure field access with accessor functions.
and use ASN1_STRING_set() instead of direct field assignment.

Fix issues caused by X509* API constness changes; in some cases,
casting the const away is necessary to keep compatibility with
OpenSSL 3.
---
 .../cert_generators/file/certificate_db.cc    |  2 +-
 src/ssl/gadgets.cc                            | 92 +++++++++++--------
 src/ssl/gadgets.h                             |  4 +-
 src/ssl/support.cc                            | 18 ++--
 4 files changed, 66 insertions(+), 50 deletions(-)

diff --git a/src/security/cert_generators/file/certificate_db.cc b/src/security/cert_generators/file/certificate_db.cc
index 4b729b3..35849aa 100644
--- a/src/security/cert_generators/file/certificate_db.cc
+++ b/src/security/cert_generators/file/certificate_db.cc
@@ -335,7 +335,7 @@ Ssl::CertificateDb::addCertAndPrivateKey(std::string const &useKey, const Securi
     }
 
     const auto tm = X509_getm_notAfter(cert.get());
-    row.setValue(cnlExp_date, std::string(reinterpret_cast<char *>(tm->data), tm->length).c_str());
+    row.setValue(cnlExp_date, std::string(reinterpret_cast<const char *>(ASN1_STRING_get0_data(tm)), ASN1_STRING_length(tm)).c_str());
     const auto subject = OneLineSummary(*X509_get_subject_name(cert.get()));
     row.setValue(cnlName, subject.get());
     row.setValue(cnlKey, useKey.c_str());
diff --git a/src/ssl/gadgets.cc b/src/ssl/gadgets.cc
index 06b8b79..100ee6a 100644
--- a/src/ssl/gadgets.cc
+++ b/src/ssl/gadgets.cc
@@ -259,20 +259,31 @@ static bool replaceCommonName(Security::CertPointer & cert, std::string const &r
     if (cn.length() > 2 && *cn.begin() == '[' && *cn.rbegin() == ']')
         cn = cn.substr(1, cn.size()-2);
 
-    X509_NAME *name = X509_get_subject_name(cert.get());
+    const X509_NAME *subject = X509_get_subject_name(cert.get());
+    if (!subject)
+        return false;
+
+    X509_NAME *name = X509_NAME_dup(subject);
     if (!name)
         return false;
+
     // Remove the CN part:
     int loc = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
-    if (loc >=0) {
-        X509_NAME_ENTRY *tmp = X509_NAME_get_entry(name, loc);
-        X509_NAME_delete_entry(name, loc);
+    if (loc >= 0) {
+        X509_NAME_ENTRY *tmp = X509_NAME_delete_entry(name, loc);
         X509_NAME_ENTRY_free(tmp);
     }
 
     // Add a new CN
-    return X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,
-                                      (unsigned char *)(cn.c_str()), -1, -1, 0);
+    if (!X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,
+                                     (unsigned char *)(cn.c_str()), -1, -1, 0)) {
+        X509_NAME_free(name);
+        return false;
+    }
+
+    int ret = X509_set_subject_name(cert.get(), name);
+    X509_NAME_free(name);
+    return ret;
 }
 
 const char *Ssl::CertSignAlgorithmStr[] = {
@@ -301,9 +312,9 @@ static void
 printX509Signature(const Security::CertPointer &cert, std::string &out)
 {
     const ASN1_BIT_STRING *sig = Ssl::X509_get_signature(cert);
-    if (sig && sig->data) {
-        const unsigned char *s = sig->data;
-        for (int i = 0; i < sig->length; ++i) {
+    if (sig && ASN1_STRING_get0_data(sig)) {
+        const unsigned char *s = ASN1_STRING_get0_data(sig);
+        for (int i = 0; i < ASN1_STRING_length(sig); ++i) {
             char hex[3];
             snprintf(hex, sizeof(hex), "%02x", s[i]);
             out.append(hex);
@@ -377,7 +388,7 @@ mimicAuthorityKeyId(Security::CertPointer &cert, Security::CertPointer const &mi
         X509_EXTENSION *ext;
         // Check if the issuer has the Subject Key Identifier extension
         const int indx = X509_get_ext_by_NID(issuerCert.get(), NID_subject_key_identifier, -1);
-        if (indx >= 0 && (ext = X509_get_ext(issuerCert.get(), indx))) {
+        if (indx >= 0 && (ext = const_cast<X509_EXTENSION *>(X509_get_ext(issuerCert.get(), indx)))) {
             issuerKeyId.reset((ASN1_OCTET_STRING *)X509V3_EXT_d2i(ext));
         }
     }
@@ -418,8 +429,7 @@ mimicAuthorityKeyId(Security::CertPointer &cert, Security::CertPointer const &mi
     unsigned char *ext_der = nullptr;
     int ext_len = ASN1_item_i2d((ASN1_VALUE *)theAuthKeyId.get(), &ext_der, ASN1_ITEM_ptr(method->it));
     Ssl::ASN1_OCTET_STRING_Pointer extOct(ASN1_OCTET_STRING_new());
-    extOct.get()->data = ext_der;
-    extOct.get()->length = ext_len;
+    ASN1_STRING_set(extOct.get(), ext_der, ext_len);
     Ssl::X509_EXTENSION_Pointer extAuthKeyId(X509_EXTENSION_create_by_NID(nullptr, NID_authority_key_identifier, 0, extOct.get()));
     if (!extAuthKeyId.get())
         return false;
@@ -471,7 +481,7 @@ mimicExtensions(Security::CertPointer & cert, Security::CertPointer const &mimic
     int nid;
     for (int i = 0; (nid = extensions[i]) != 0; ++i) {
         const int pos = X509_get_ext_by_NID(mimicCert.get(), nid, -1);
-        if (X509_EXTENSION *ext = X509_get_ext(mimicCert.get(), pos)) {
+        if (X509_EXTENSION *ext = const_cast<X509_EXTENSION *>(X509_get_ext(mimicCert.get(), pos))) {
             // Mimic extension exactly.
             if (X509_add_ext(cert.get(), ext, -1))
                 ++added;
@@ -482,7 +492,7 @@ mimicExtensions(Security::CertPointer & cert, Security::CertPointer const &mimic
                 // that the more stringent requirements are met.
 
                 const int p = X509_get_ext_by_NID(cert.get(), NID_key_usage, -1);
-                if ((ext = X509_get_ext(cert.get(), p)) != nullptr) {
+                if ((ext = const_cast<X509_EXTENSION *>(X509_get_ext(cert.get(), p))) != nullptr) {
                     ASN1_BIT_STRING *keyusage = (ASN1_BIT_STRING *)X509V3_EXT_d2i(ext);
                     ASN1_BIT_STRING_set_bit(keyusage, KeyEncipherment, 1);
 
@@ -495,8 +505,7 @@ mimicExtensions(Security::CertPointer & cert, Security::CertPointer const &mimic
                                                 (const ASN1_ITEM *)ASN1_ITEM_ptr(method->it));
 
                     ASN1_OCTET_STRING *ext_oct = ASN1_OCTET_STRING_new();
-                    ext_oct->data = ext_der;
-                    ext_oct->length = ext_len;
+                    ASN1_STRING_set(ext_oct, ext_der, ext_len);
                     X509_EXTENSION_set_data(ext, ext_oct);
 
                     ASN1_OCTET_STRING_free(ext_oct);
@@ -518,7 +527,7 @@ mimicExtensions(Security::CertPointer & cert, Security::CertPointer const &mimic
 SBuf
 Ssl::AsnToSBuf(const ASN1_STRING &buffer)
 {
-    return SBuf(reinterpret_cast<const char *>(buffer.data), buffer.length);
+    return SBuf(reinterpret_cast<const char *>(ASN1_STRING_get0_data(&buffer)), ASN1_STRING_length(&buffer));
 }
 
 /// OpenSSL ASN1_STRING_to_UTF8() wrapper
@@ -547,7 +556,7 @@ Ssl::ParseAsSimpleDomainNameOrIp(const SBuf &text)
 }
 
 std::optional<AnyP::Host>
-Ssl::ParseCommonNameAt(X509_NAME &name, const int cnIndex)
+Ssl::ParseCommonNameAt(const X509_NAME &name, const int cnIndex)
 {
     const auto cn = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(&name, cnIndex));
     if (!cn) {
@@ -576,7 +585,7 @@ Ssl::ParseCommonNameAt(X509_NAME &name, const int cnIndex)
 static bool
 addAltNameWithSubjectCn(Security::CertPointer &cert)
 {
-    X509_NAME *name = X509_get_subject_name(cert.get());
+    const X509_NAME *name = X509_get_subject_name(cert.get());
     if (!name)
         return false;
 
@@ -610,7 +619,7 @@ static bool buildCertificate(Security::CertPointer & cert, Ssl::CertificatePrope
     // returns a pointer to the existing subject name. Nothing to clean here.
     if (properties.mimicCert.get()) {
         // Leave subject empty if we cannot extract it from true cert.
-        if (X509_NAME *name = X509_get_subject_name(properties.mimicCert.get())) {
+        if (const X509_NAME *name = X509_get_subject_name(properties.mimicCert.get())) {
             // X509_set_subject_name will call X509_dup for name
             X509_set_subject_name(cert.get(), name);
         }
@@ -656,7 +665,7 @@ static bool buildCertificate(Security::CertPointer & cert, Ssl::CertificatePrope
     bool useCommonNameAsAltName = true;
     // mimic the alias and possibly subjectAltName
     if (properties.mimicCert.get()) {
-        unsigned char *alStr;
+        const unsigned char *alStr;
         int alLen;
         alStr = X509_alias_get0(properties.mimicCert.get(), &alLen);
         if (alStr) {
@@ -667,7 +676,7 @@ static bool buildCertificate(Security::CertPointer & cert, Ssl::CertificatePrope
         // certificates with CN unrelated to subjectAltNames.
         if (!properties.setCommonName) {
             int pos = X509_get_ext_by_NID(properties.mimicCert.get(), NID_subject_alt_name, -1);
-            X509_EXTENSION *ext=X509_get_ext(properties.mimicCert.get(), pos);
+            X509_EXTENSION *ext = const_cast<X509_EXTENSION *>(X509_get_ext(properties.mimicCert.get(), pos));
             if (ext) {
                 if (X509_add_ext(cert.get(), ext, -1))
                     ++addedExtensions;
@@ -922,20 +931,23 @@ Ssl::WritePrivateKey(Ssl::BIO_Pointer &bio, const Security::PrivateKeyPointer &p
 }
 
 Ssl::UniqueCString
-Ssl::OneLineSummary(X509_NAME &name)
+Ssl::OneLineSummary(const X509_NAME &name)
 {
     return Ssl::UniqueCString(X509_NAME_oneline(&name, nullptr, 0));
 }
 
 bool Ssl::sslDateIsInTheFuture(char const * date)
 {
-    ASN1_UTCTIME tm;
-    tm.flags = 0;
-    tm.type = 23;
-    tm.data = (unsigned char *)date;
-    tm.length = strlen(date);
-
-    return (X509_cmp_current_time(&tm) > 0);
+    ASN1_UTCTIME *tm = ASN1_UTCTIME_new();
+    if (!tm)
+        return false;
+    if (!ASN1_UTCTIME_set_string(tm, date)) {
+        ASN1_UTCTIME_free(tm);
+        return false;
+    }
+    int result = X509_cmp_current_time(tm);
+    ASN1_UTCTIME_free(tm);
+    return (result > 0);
 }
 
 /// Print the time represented by a ASN1_TIME struct to a string using GeneralizedTime format
@@ -945,14 +957,18 @@ static bool asn1timeToGeneralizedTimeStr(ASN1_TIME *aTime, char *buf, int bufLen
     // UTCTime has the form YYMMDDHHMMSS[Z | [+|-]offset]
     // GeneralizedTime has the form YYYYMMDDHHMMSS[Z | [+|-] offset]
 
+    const unsigned char *data = ASN1_STRING_get0_data(aTime);
+    int length = ASN1_STRING_length(aTime);
+    int type = ASN1_STRING_type(aTime);
+
     // length should have space for data plus 2 extra bytes for the two extra year fields
     // plus the '\0' char.
-    if ((aTime->length + 3) > bufLen)
+    if ((length + 3) > bufLen)
         return false;
 
     char *str;
-    if (aTime->type == V_ASN1_UTCTIME) {
-        if (aTime->data[0] > '5') { // RFC 2459, section 4.1.2.5.1
+    if (type == V_ASN1_UTCTIME) {
+        if (data[0] > '5') { // RFC 2459, section 4.1.2.5.1
             buf[0] = '1';
             buf[1] = '9';
         } else {
@@ -960,11 +976,11 @@ static bool asn1timeToGeneralizedTimeStr(ASN1_TIME *aTime, char *buf, int bufLen
             buf[1] = '0';
         }
         str = buf +2;
-    } else // if (aTime->type == V_ASN1_GENERALIZEDTIME)
+    } else // if (type == V_ASN1_GENERALIZEDTIME)
         str = buf;
 
-    memcpy(str, aTime->data, aTime->length);
-    str[aTime->length] = '\0';
+    memcpy(str, data, length);
+    str[length] = '\0';
     return true;
 }
 
@@ -996,8 +1012,8 @@ bool Ssl::certificateMatchesProperties(X509 *cert, CertificateProperties const &
         return true;
 
     if (!properties.setCommonName) {
-        X509_NAME *cert1_name = X509_get_subject_name(cert);
-        X509_NAME *cert2_name = X509_get_subject_name(cert2);
+        const X509_NAME *cert1_name = X509_get_subject_name(cert);
+        const X509_NAME *cert2_name = X509_get_subject_name(cert2);
         if (X509_NAME_cmp(cert1_name, cert2_name) != 0)
             return false;
     } else if (properties.commonName != CommonHostName(cert))
diff --git a/src/ssl/gadgets.h b/src/ssl/gadgets.h
index e0c60c3..4c48759 100644
--- a/src/ssl/gadgets.h
+++ b/src/ssl/gadgets.h
@@ -160,7 +160,7 @@ bool WriteX509Certificate(BIO_Pointer &bio, const Security::CertPointer & cert);
 bool WritePrivateKey(BIO_Pointer &bio, const Security::PrivateKeyPointer &pkey);
 
 /// a RAII wrapper for the memory-allocating flavor of X509_NAME_oneline()
-UniqueCString OneLineSummary(X509_NAME &);
+UniqueCString OneLineSummary(const X509_NAME &);
 
 /**
   \ingroup SslCrtdSslAPI
@@ -285,7 +285,7 @@ const char *CommonHostName(X509 *x509);
 SBuf AsnToSBuf(const ASN1_STRING &);
 
 /// interprets X.509 Subject or Issuer name entry (at the given position) as CN
-std::optional<AnyP::Host> ParseCommonNameAt(X509_NAME &, int);
+std::optional<AnyP::Host> ParseCommonNameAt(const X509_NAME &, int);
 
 /// interprets the given buffer as either a textual representation of an IP
 /// address (if possible) or a domain name without wildcard support (otherwise)
diff --git a/src/ssl/support.cc b/src/ssl/support.cc
index e787203..09f268a 100644
--- a/src/ssl/support.cc
+++ b/src/ssl/support.cc
@@ -272,23 +272,23 @@ ParseSubjectAltName(const GENERAL_NAME &san)
 
         // RFC 5280 section 4.2.1.6 signals IPv4/IPv6 address family using data length
 
-        if (san.d.iPAddress->length == 4) {
+        if (ASN1_STRING_length(san.d.iPAddress) == 4) {
             struct in_addr addr;
             static_assert(sizeof(addr.s_addr) == 4);
-            memcpy(&addr.s_addr, san.d.iPAddress->data, sizeof(addr.s_addr));
+            memcpy(&addr.s_addr, ASN1_STRING_get0_data(san.d.iPAddress), sizeof(addr.s_addr));
             const Ip::Address ip(addr);
             return AnyP::Host::ParseIp(ip);
         }
 
-        if (san.d.iPAddress->length == 16) {
+        if (ASN1_STRING_length(san.d.iPAddress) == 16) {
             struct in6_addr addr;
             static_assert(sizeof(addr.s6_addr) == 16);
-            memcpy(&addr.s6_addr, san.d.iPAddress->data, sizeof(addr.s6_addr));
+            memcpy(&addr.s6_addr, ASN1_STRING_get0_data(san.d.iPAddress), sizeof(addr.s6_addr));
             const Ip::Address ip(addr);
             return AnyP::Host::ParseIp(ip);
         }
 
-        debugs(83, 3, "unexpected length of an IP address SAN: " << san.d.iPAddress->length);
+        debugs(83, 3, "unexpected length of an IP address SAN: " << ASN1_STRING_length(san.d.iPAddress));
         return std::nullopt;
     }
 
@@ -856,7 +856,7 @@ Ssl::InitClientContext(Security::ContextPointer &ctx, Security::PeerOptions &pee
 
 /// \ingroup ServerProtocolSSLInternal
 static const char *
-ssl_get_attribute(X509_NAME * name, const char *attribute_name)
+ssl_get_attribute(const X509_NAME * name, const char *attribute_name)
 {
     static char buffer[1024];
     buffer[0] = '\0';
@@ -879,7 +879,7 @@ ssl_get_attribute(X509_NAME * name, const char *attribute_name)
 const char *
 Ssl::GetX509UserAttribute(X509 * cert, const char *attribute_name)
 {
-    X509_NAME *name;
+    const X509_NAME *name;
     const char *ret;
 
     if (!cert)
@@ -933,7 +933,7 @@ const char *
 Ssl::GetX509CAAttribute(X509 * cert, const char *attribute_name)
 {
 
-    X509_NAME *name;
+    const X509_NAME *name;
     const char *ret;
 
     if (!cert)
@@ -1484,7 +1484,7 @@ void Ssl::InRamCertificateDbKey(const Ssl::CertificateProperties &certProperties
     if (certProperties.mimicCert) {
         if (auto *sig = Ssl::X509_get_signature(certProperties.mimicCert)) {
             origSignatureAsKey = true;
-            key.append((const char *)sig->data, sig->length);
+            key.append((const char *)ASN1_STRING_get0_data(sig), ASN1_STRING_length(sig));
         }
     }
 
-- 
2.53.0
