Commit 4f2f4892061 for php

commit 4f2f4892061ec9d475cba3bfb31ce21c4b337ec5
Author: Weilin Du <weilindu@php.net>
Date:   Tue Nov 3 14:42:06 2026 +0800

    ext/openssl: Restore OpenSSL 4.x compatibility on PHP 8.5 (#25139)

    Backport OpenSSL 4 compatibility and the 4.1 ASN.1 API updates from
    PHP 8.6. Preserve raw common-name matching, keep the TLS and certificate
    tests compatible with OpenSSL 4, and cover binary SPKI challenges.

    Closes #25139

    Co-authored-by: Shivam Mathur <shivam_jpr@hotmail.com>
    Co-authored-by: Sam James <sam@gentoo.org>

diff --git a/NEWS b/NEWS
index 77324231844..1d651387239 100644
--- a/NEWS
+++ b/NEWS
@@ -219,6 +219,8 @@ PHP                                                                        NEWS
   . Fix opcache phar check. (ndossche)

 - OpenSSL:
+  . Fixed build and test compatibility with OpenSSL 4.0 and 4.1.
+    (Sam James, Weilin Du, Shivam Mathur)
   . Fixed bug GH-24784 (openssl_pkcs7_read() and openssl_cms_read() overwrite
     certificates with CRLs). (Alexander Danilov)

diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c
index fc029d6f734..3cacfc71552 100644
--- a/ext/openssl/openssl.c
+++ b/ext/openssl/openssl.c
@@ -425,7 +425,7 @@ PHP_MINIT_FUNCTION(openssl)
 	ssl_stream_data_index = SSL_get_ex_new_index(0, "PHP stream index", NULL, NULL, NULL);

 	php_stream_xport_register("ssl", php_openssl_ssl_socket_factory);
-#ifndef OPENSSL_NO_SSL3
+#if OPENSSL_VERSION_NUMBER < 0x40000000L && !defined(OPENSSL_NO_SSL3)
 	php_stream_xport_register("sslv3", php_openssl_ssl_socket_factory);
 #endif
 	php_stream_xport_register("tls", php_openssl_ssl_socket_factory);
@@ -499,7 +499,7 @@ PHP_MSHUTDOWN_FUNCTION(openssl)
 	php_unregister_url_stream_wrapper("ftps");

 	php_stream_xport_unregister("ssl");
-#ifndef OPENSSL_NO_SSL3
+#if OPENSSL_VERSION_NUMBER < 0x40000000L && !defined(OPENSSL_NO_SSL3)
 	php_stream_xport_unregister("sslv3");
 #endif
 	php_stream_xport_unregister("tls");
@@ -629,7 +629,7 @@ PHP_FUNCTION(openssl_spki_new)
 	}

 	if (challenge) {
-		if (!ASN1_STRING_set(spki->spkac->challenge, challenge, (int)challenge_len)) {
+		if (!ASN1_STRING_set1_data(spki->spkac->challenge, (const unsigned char *) challenge, challenge_len)) {
 			php_openssl_store_errors();
 			php_error_docref(NULL, E_WARNING, "Unable to set challenge data");
 			goto cleanup;
@@ -818,7 +818,8 @@ PHP_FUNCTION(openssl_spki_export_challenge)
 		goto cleanup;
 	}

-	RETVAL_STRING((const char *)ASN1_STRING_get0_data(spki->spkac->challenge));
+	RETVAL_STRINGL_FAST((const char *)ASN1_STRING_get0_data(spki->spkac->challenge),
+		ASN1_STRING_get_length(spki->spkac->challenge));
 	goto cleanup;

 cleanup:
@@ -999,8 +1000,8 @@ PHP_FUNCTION(openssl_x509_parse)
 	bool useshortnames = 1;
 	char * tmpstr;
 	zval subitem;
-	X509_EXTENSION *extension;
-	X509_NAME *subject_name;
+	PHP_OPENSSL_X509_EXTENSION *extension;
+	const X509_NAME *subject_name;
 	char *cert_name;
 	char *extname;
 	BIO *bio_out;
@@ -1969,7 +1970,7 @@ PHP_FUNCTION(openssl_csr_get_subject)
 	zend_object *csr_obj;
 	zend_string *csr_str;
 	bool use_shortnames = 1;
-	X509_NAME *subject;
+	const X509_NAME *subject;

 	ZEND_PARSE_PARAMETERS_START(1, 2)
 		Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c
index e5f1dbd67be..a5bd7238d9e 100644
--- a/ext/openssl/openssl_backend_common.c
+++ b/ext/openssl/openssl_backend_common.c
@@ -35,16 +35,16 @@
 /* true global; readonly after module startup */
 static char default_ssl_conf_filename[MAXPATHLEN];

-void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname)
+void php_openssl_add_assoc_name_entry(zval * val, char * key, const X509_NAME * name, int shortname)
 {
 	zval *data;
 	zval subitem, tmp;
 	int i;
 	char *sname;
 	int nid;
-	X509_NAME_ENTRY * ne;
-	ASN1_STRING * str = NULL;
-	ASN1_OBJECT * obj;
+	const X509_NAME_ENTRY * ne;
+	const ASN1_STRING * str = NULL;
+	const ASN1_OBJECT * obj;

 	if (key != NULL) {
 		array_init(&subitem);
@@ -75,7 +75,7 @@ void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name,
 		} else {
 			/* ASN1_STRING_get0_data(3): Since this is an internal pointer it should not be freed or modified in any way */
 			to_add = ASN1_STRING_get0_data(str);
-			to_add_len = ASN1_STRING_length(str);
+			to_add_len = ASN1_STRING_get_length(str);
 		}

 		if (to_add_len != -1) {
@@ -108,9 +108,9 @@ void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name,
 	}
 }

-void php_openssl_add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str)
+void php_openssl_add_assoc_asn1_string(zval * val, char * key, const ASN1_STRING * str)
 {
-	add_assoc_stringl(val, key, (const char *)ASN1_STRING_get0_data(str), ASN1_STRING_length(str));
+	add_assoc_stringl(val, key, (const char *)ASN1_STRING_get0_data(str), ASN1_STRING_get_length(str));
 }

 time_t php_openssl_asn1_time_to_time_t(ASN1_UTCTIME * timestr)
@@ -126,6 +126,7 @@ time_t php_openssl_asn1_time_to_time_t(ASN1_UTCTIME * timestr)
 	struct tm thetime;
 	char * strbuf;
 	char * thestr;
+	const char *timestr_data;
 	long gmadjust = 0;
 	size_t timestr_len;

@@ -134,24 +135,25 @@ time_t php_openssl_asn1_time_to_time_t(ASN1_UTCTIME * timestr)
 		return (time_t)-1;
 	}

-	timestr_len = (size_t)ASN1_STRING_length(timestr);
+	timestr_len = ASN1_STRING_get_length(timestr);
+	timestr_data = timestr_len ? (const char *)ASN1_STRING_get0_data(timestr) : "";

-	if (timestr_len != strlen((const char *)ASN1_STRING_get0_data(timestr))) {
+	if (memchr(timestr_data, '\0', timestr_len) != NULL) {
 		php_error_docref(NULL, E_WARNING, "Illegal length in timestamp");
 		return (time_t)-1;
 	}

 	if (timestr_len < 13) {
-		php_error_docref(NULL, E_WARNING, "Unable to parse time string %s correctly", ASN1_STRING_get0_data(timestr));
+		php_error_docref(NULL, E_WARNING, "Unable to parse time string %.*s correctly", (int)timestr_len, timestr_data);
 		return (time_t)-1;
 	}

 	if (ASN1_STRING_type(timestr) == V_ASN1_GENERALIZEDTIME && timestr_len < 15) {
-		php_error_docref(NULL, E_WARNING, "Unable to parse time string %s correctly", ASN1_STRING_get0_data(timestr));
+		php_error_docref(NULL, E_WARNING, "Unable to parse time string %.*s correctly", (int)timestr_len, timestr_data);
 		return (time_t)-1;
 	}

-	strbuf = estrdup((const char *)ASN1_STRING_get0_data(timestr));
+	strbuf = estrndup(timestr_data, timestr_len);

 	memset(&thetime, 0, sizeof(thetime));

@@ -618,11 +620,11 @@ zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, bool r
 /* Special handling of subjectAltName, see CVE-2013-4073
  * Christian Heimes
  */
-int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)
+int openssl_x509v3_subjectAltName(BIO *bio, PHP_OPENSSL_X509_EXTENSION *extension)
 {
 	GENERAL_NAMES *names;
 	const X509V3_EXT_METHOD *method = NULL;
-	ASN1_OCTET_STRING *extension_data;
+	const ASN1_OCTET_STRING *extension_data;
 	long i, length, num;
 	const unsigned char *p;

@@ -633,7 +635,7 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)

 	extension_data = X509_EXTENSION_get_data(extension);
 	p = ASN1_STRING_get0_data(extension_data);
-	length = ASN1_STRING_length(extension_data);
+	length = ASN1_STRING_get_length(extension_data);
 	if (method->it) {
 		names = (GENERAL_NAMES*) (ASN1_item_d2i(NULL, &p, length,
 			ASN1_ITEM_ptr(method->it)));
@@ -655,19 +657,19 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)
 				BIO_puts(bio, "email:");
 				as = name->d.rfc822Name;
 				BIO_write(bio, ASN1_STRING_get0_data(as),
-					ASN1_STRING_length(as));
+					ASN1_STRING_get_length(as));
 				break;
 			case GEN_DNS:
 				BIO_puts(bio, "DNS:");
 				as = name->d.dNSName;
 				BIO_write(bio, ASN1_STRING_get0_data(as),
-					ASN1_STRING_length(as));
+					ASN1_STRING_get_length(as));
 				break;
 			case GEN_URI:
 				BIO_puts(bio, "URI:");
 				as = name->d.uniformResourceIdentifier;
 				BIO_write(bio, ASN1_STRING_get0_data(as),
-					ASN1_STRING_length(as));
+					ASN1_STRING_get_length(as));
 				break;
 			default:
 				/* use builtin print for GEN_OTHERNAME, GEN_X400,
@@ -978,7 +980,12 @@ zend_result php_openssl_csr_make(struct php_x509_request * req, X509_REQ * csr,
 		zval *item, *subitem;
 		zend_string *strindex = NULL;

-		subj = X509_REQ_get_subject_name(csr);
+		subj = X509_NAME_new();
+		if (subj == NULL) {
+			php_openssl_store_errors();
+			return FAILURE;
+		}
+
 		/* apply values from the dn hash */
 		ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(dn), strindex, item) {
 			if (strindex) {
@@ -987,10 +994,12 @@ zend_result php_openssl_csr_make(struct php_x509_request * req, X509_REQ * csr,
 					if (Z_TYPE_P(item) == IS_ARRAY) {
 						ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(item), i, subitem) {
 							if (php_openssl_csr_add_subj_entry(subitem, subj, nid) == FAILURE) {
+								X509_NAME_free(subj);
 								return FAILURE;
 							}
 						} ZEND_HASH_FOREACH_END();
 					} else if (php_openssl_csr_add_subj_entry(item, subj, nid) == FAILURE) {
+						X509_NAME_free(subj);
 						return FAILURE;
 					}
 				} else {
@@ -1041,13 +1050,23 @@ zend_result php_openssl_csr_make(struct php_x509_request * req, X509_REQ * csr,
 			if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_UTF8, (unsigned char*)v->value, -1, -1, 0)) {
 				php_openssl_store_errors();
 				php_error_docref(NULL, E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value);
+				X509_NAME_free(subj);
 				return FAILURE;
 			}
 			if (!X509_NAME_entry_count(subj)) {
 				php_error_docref(NULL, E_WARNING, "No objects specified in config file");
+				X509_NAME_free(subj);
 				return FAILURE;
 			}
 		}
+
+		if (!X509_REQ_set_subject_name(csr, subj)) {
+			php_openssl_store_errors();
+			X509_NAME_free(subj);
+			return FAILURE;
+		}
+		X509_NAME_free(subj);
+
 		if (attribs) {
 			ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(attribs), strindex, item) {
 				int nid;
diff --git a/ext/openssl/openssl_backend_v3.c b/ext/openssl/openssl_backend_v3.c
index c23520413fc..791aae196bf 100644
--- a/ext/openssl/openssl_backend_v3.c
+++ b/ext/openssl/openssl_backend_v3.c
@@ -404,7 +404,7 @@ EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private) {
 		}

 		OPENSSL_PKEY_SET_BN(data, cofactor);
-		if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR, cofactor) ||
+		if ((cofactor && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR, cofactor)) ||
 			!EC_GROUP_set_generator(group, point_g, order, cofactor)) {
 			goto cleanup;
 		}
diff --git a/ext/openssl/php_openssl.h b/ext/openssl/php_openssl.h
index e565707a3c4..3b34ec812ef 100644
--- a/ext/openssl/php_openssl.h
+++ b/ext/openssl/php_openssl.h
@@ -25,6 +25,7 @@ extern zend_module_entry openssl_module_entry;
 #include "php_version.h"
 #define PHP_OPENSSL_VERSION PHP_VERSION

+#include <openssl/opensslconf.h>
 #include <openssl/opensslv.h>
 /* OpenSSL version check */
 #if OPENSSL_VERSION_NUMBER < 0x30000000L
diff --git a/ext/openssl/php_openssl_backend.h b/ext/openssl/php_openssl_backend.h
index 00da5e74fc1..0090f4f52fe 100644
--- a/ext/openssl/php_openssl_backend.h
+++ b/ext/openssl/php_openssl_backend.h
@@ -37,6 +37,17 @@
 #include <openssl/pkcs12.h>
 #include <openssl/cms.h>

+#if OPENSSL_VERSION_NUMBER < 0x40100000L
+#define ASN1_STRING_set1_data(str, data, len) ASN1_STRING_set((str), (data), (int) (len))
+#define ASN1_STRING_get_length(str) ((size_t) ASN1_STRING_length(str))
+#endif
+
+#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 4
+typedef const X509_EXTENSION PHP_OPENSSL_X509_EXTENSION;
+#else
+typedef X509_EXTENSION PHP_OPENSSL_X509_EXTENSION;
+#endif
+
 /* number conversion flags checks */
 #define PHP_OPENSSL_CHECK_NUMBER_CONVERSION(_cond, _name, _arg_num) \
 	do { \
@@ -168,8 +179,8 @@ struct php_x509_request {
 	const EVP_CIPHER * priv_key_encrypt_cipher;
 };

-void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname);
-void php_openssl_add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str);
+void php_openssl_add_assoc_name_entry(zval * val, char * key, const X509_NAME * name, int shortname);
+void php_openssl_add_assoc_asn1_string(zval * val, char * key, const ASN1_STRING * str);
 time_t php_openssl_asn1_time_to_time_t(ASN1_UTCTIME * timestr);
 int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, CONF *config);
 char *php_openssl_conf_get_string(CONF *conf, const char *group, const char *name);
@@ -266,7 +277,7 @@ X509 *php_openssl_x509_from_zval(

 zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, bool raw);

-int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension);
+int openssl_x509v3_subjectAltName(BIO *bio, PHP_OPENSSL_X509_EXTENSION *extension);

 STACK_OF(X509) *php_openssl_load_all_certs_from_file(
 		char *cert_file, size_t cert_file_len, uint32_t arg_num);
diff --git a/ext/openssl/tests/bug28382.phpt b/ext/openssl/tests/bug28382.phpt
index e2842023135..7627da2931b 100644
--- a/ext/openssl/tests/bug28382.phpt
+++ b/ext/openssl/tests/bug28382.phpt
@@ -6,6 +6,12 @@
 <?php
 $cert = file_get_contents(__DIR__ . "/bug28382cert.txt");
 $ext = openssl_x509_parse($cert);
+
+if (OPENSSL_VERSION_NUMBER >= 0x40000000) {
+    $extensions = &$ext['extensions'];
+    $extensions['crlDistributionPoints'] = preg_replace('/^Full Name:\R\s*/', '', trim($extensions['crlDistributionPoints']));
+}
+
 var_dump($ext['extensions']);
 /*
  * The reason for %A at the end of crlDistributionPoints and authorityKeyIdentifier is that
@@ -30,7 +36,7 @@
   string(59) "B0:A7:FF:F9:41:15:DE:23:39:BD:DD:31:0F:97:A0:B2:A2:74:E0:FC"
   ["authorityKeyIdentifier"]=>
   string(%d) "DirName:/C=RO/ST=Romania/L=Craiova/O=Sergiu/OU=Sergiu SRL/CN=Sergiu CA/emailAddress=n_sergiu@hotmail.com
-serial:00%A"
+serial:%A"
   ["keyUsage"]=>
   string(71) "Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment"
   ["nsBaseUrl"]=>
diff --git a/ext/openssl/tests/ecc_custom_params.phpt b/ext/openssl/tests/ecc_custom_params.phpt
index 0c63af1450b..e3854eefa4d 100644
--- a/ext/openssl/tests/ecc_custom_params.phpt
+++ b/ext/openssl/tests/ecc_custom_params.phpt
@@ -5,6 +5,30 @@
 --SKIPIF--
 <?php
 if (!defined("OPENSSL_KEYTYPE_EC")) die("skip EC disabled");
+
+if (OPENSSL_VERSION_NUMBER >= 0x40000000) {
+    $d = hex2bin('8D0AC65AAEA0D6B96254C65817D4A143A9E7A03876F1A37D');
+    $p = hex2bin('BDB6F4FE3E8B1D9E0DA8C0D46F4C318CEFE4AFE3B6B8551F');
+    $a = hex2bin('BB8E5E8FBC115E139FE6A814FE48AAA6F0ADA1AA5DF91985');
+    $b = hex2bin('1854BEBDC31B21B7AEFC80AB0ECD10D5B1B3308E6DBF11C1');
+    $g_x = hex2bin('4AD5F7048DE709AD51236DE65E4D4B482C836DC6E4106640');
+    $g_y = hex2bin('02BB3A02D4AAADACAE24817A4CA3A1B014B5270432DB27D2');
+    $order = hex2bin('BDB6F4FE3E8B1D9E0DA8C0D40FC962195DFAE76F56564677');
+
+    if (@openssl_pkey_new(array(
+        'ec' => array(
+            'p' => $p,
+            'a' => $a,
+            'b' => $b,
+            'order' => $order,
+            'g_x' => $g_x,
+            'g_y' => $g_y,
+            'd' => $d,
+        ),
+    )) === false) {
+        die("skip EC custom params unsupported with OpenSSL 4");
+    }
+}
 ?>
 --FILE--
 <?php
diff --git a/ext/openssl/tests/openssl_spki_export_challenge_binary.phpt b/ext/openssl/tests/openssl_spki_export_challenge_binary.phpt
new file mode 100644
index 00000000000..d5eb8617cb1
--- /dev/null
+++ b/ext/openssl/tests/openssl_spki_export_challenge_binary.phpt
@@ -0,0 +1,19 @@
+--TEST--
+openssl_spki_export_challenge() preserves empty challenges and embedded NUL bytes
+--EXTENSIONS--
+openssl
+--FILE--
+<?php
+$key = openssl_pkey_get_private('file://' . __DIR__ . '/private_rsa_2048.key');
+
+foreach (["", "challenge", "\0challenge", "challenge\0", "left\0right"] as $challenge) {
+    $spkac = openssl_spki_new($key, $challenge, OPENSSL_ALGO_SHA256);
+    var_dump(openssl_spki_export_challenge(substr($spkac, 6)) === $challenge);
+}
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
diff --git a/ext/openssl/tests/openssl_x509_parse_basic_openssl32.phpt b/ext/openssl/tests/openssl_x509_parse_basic_openssl32.phpt
index a0b125fb4ea..84415d133f5 100644
--- a/ext/openssl/tests/openssl_x509_parse_basic_openssl32.phpt
+++ b/ext/openssl/tests/openssl_x509_parse_basic_openssl32.phpt
@@ -8,12 +8,28 @@
 ?>
 --FILE--
 <?php
+function normalize_openssl4_x509_parse_output(array $cert): array {
+    if (OPENSSL_VERSION_NUMBER < 0x40000000 || !isset($cert['serialNumberHex'], $cert['extensions']['authorityKeyIdentifier'])) {
+        return $cert;
+    }
+
+    $serial = strtoupper($cert['serialNumberHex']);
+    if (strlen($serial) % 2) {
+        $serial = '0' . $serial;
+    }
+    $serial = implode(':', str_split($serial, 2));
+    $cert['extensions']['authorityKeyIdentifier'] = preg_replace('/^serial:\d+$/m', 'serial:' . $serial, $cert['extensions']['authorityKeyIdentifier']);
+
+    return $cert;
+}
+
 $cert = "file://" . __DIR__ . "/cert.crt";

 $parsedCert = openssl_x509_parse($cert);
-var_dump($parsedCert === openssl_x509_parse(openssl_x509_read($cert)));
+$parsedCert = normalize_openssl4_x509_parse_output($parsedCert);
+var_dump($parsedCert === normalize_openssl4_x509_parse_output(openssl_x509_parse(openssl_x509_read($cert))));
 var_dump($parsedCert);
-var_dump(openssl_x509_parse($cert, false));
+var_dump(normalize_openssl4_x509_parse_output(openssl_x509_parse($cert, false)));
 ?>
 --EXPECTF--
 bool(true)
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index d5425f980b6..8e3463907df 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -95,7 +95,7 @@
 #define HAVE_SEC_LEVEL 1
 #endif

-#ifndef OPENSSL_NO_SSL3
+#if OPENSSL_VERSION_NUMBER < 0x40000000L && !defined(OPENSSL_NO_SSL3)
 #define HAVE_SSL3 1
 #define PHP_OPENSSL_MIN_PROTO_VERSION STREAM_CRYPTO_METHOD_SSLv3
 #else
@@ -478,7 +478,7 @@ static bool php_openssl_matches_san_list(
 				/* TODO: warn ? */
 				continue;
 			}
-			if ((size_t)ASN1_STRING_length(san->d.dNSName) != strlen((const char*)cert_name)) {
+			if (ASN1_STRING_get_length(san->d.dNSName) != strlen((const char*)cert_name)) {
 				OPENSSL_free(cert_name);
 				/* prevent null-byte poisoning*/
 				continue;
@@ -498,7 +498,7 @@ static bool php_openssl_matches_san_list(
 			}
 			OPENSSL_free(cert_name);
 		} else if (san->type == GEN_IPADD) {
-			if (ASN1_STRING_length(san->d.iPAddress) == 4) {
+			if (ASN1_STRING_get_length(san->d.iPAddress) == 4) {
 				snprintf(ipbuffer, sizeof(ipbuffer), "%d.%d.%d.%d",
 					ASN1_STRING_get0_data(san->d.iPAddress)[0],
 					ASN1_STRING_get0_data(san->d.iPAddress)[1],
@@ -512,7 +512,7 @@ static bool php_openssl_matches_san_list(
 				}
 			}
 #ifdef HAVE_IPV6_SAN
-			else if (ASN1_STRING_length(san->d.ip) == 16 && subject_name_is_ipv6) {
+			else if (ASN1_STRING_get_length(san->d.ip) == 16 && subject_name_is_ipv6) {
 				ipbuffer[0] = 0;
 				EXPAND_IPV6_ADDRESS(ipbuffer, ASN1_STRING_get0_data(san->d.iPAddress));
 				if (strcasecmp((const char*)subject_name_ipv6_expanded, (const char*)ipbuffer) == 0) {
@@ -542,26 +542,55 @@ static bool php_openssl_matches_san_list(

 static bool php_openssl_matches_common_name(X509 *peer, const char *subject_name) /* {{{ */
 {
-	char buf[1024];
-	X509_NAME *cert_name;
+	unsigned char *cert_name = NULL;
+#if PHP_OPENSSL_API_VERSION < 0x30000
+	X509_NAME *name;
+#else
+	const X509_NAME *name;
+#endif
+	const X509_NAME_ENTRY *name_entry;
+	const ASN1_STRING *name_asn1;
 	bool is_match = 0;
+	int name_index;
 	int cert_name_len;

-	cert_name = X509_get_subject_name(peer);
-	cert_name_len = X509_NAME_get_text_by_NID(cert_name, NID_commonName, buf, sizeof(buf));
+	name = X509_get_subject_name(peer);
+	name_index = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
+	if (name_index == -1) {
+		php_error_docref(NULL, E_WARNING, "Unable to locate peer certificate CN");
+		return false;
+	}

-	if (cert_name_len == -1) {
+	name_entry = X509_NAME_get_entry(name, name_index);
+	if (name_entry == NULL) {
 		php_error_docref(NULL, E_WARNING, "Unable to locate peer certificate CN");
-	} else if ((size_t)cert_name_len != strlen(buf)) {
-		php_error_docref(NULL, E_WARNING, "Peer certificate CN=`%.*s' is malformed", cert_name_len, buf);
-	} else if (php_openssl_matches_wildcard_name(subject_name, buf)) {
+		return false;
+	}
+
+	name_asn1 = X509_NAME_ENTRY_get_data(name_entry);
+	cert_name_len = ASN1_STRING_get_length(name_asn1);
+	cert_name = (unsigned char *) OPENSSL_strndup((const char *) ASN1_STRING_get0_data(name_asn1), cert_name_len);
+	if (cert_name == NULL) {
+		php_error_docref(NULL, E_WARNING, "Unable to locate peer certificate CN");
+		return false;
+	}
+
+	if ((size_t)cert_name_len != strlen((const char *)cert_name)) {
+		php_error_docref(NULL, E_WARNING, "Peer certificate CN=`%.*s' is malformed", cert_name_len, (const char *)cert_name);
+		OPENSSL_free(cert_name);
+		return false;
+	} else if (php_openssl_matches_wildcard_name(subject_name, (const char *)cert_name)) {
 		is_match = 1;
 	} else {
 		php_error_docref(NULL, E_WARNING,
 			"Peer certificate CN=`%.*s' did not match expected CN=`%s'",
-			cert_name_len, buf, subject_name);
+			cert_name_len, (const char *)cert_name, subject_name);
+		OPENSSL_free(cert_name);
+		return false;
 	}

+	OPENSSL_free(cert_name);
+
 	return is_match;
 }
 /* }}} */