From 0a04d6c08739c1aa3f05c6488782e7eeba8812e6 Mon Sep 17 00:00:00 2001 From: Steve Wall Date: Mon, 6 Oct 2025 13:01:09 -0400 Subject: [PATCH 1/7] Extend x.509 parsing to include machine-readable info about subjectAlternativeNames This adds an additional array element to the output of `openssl_x509_parse()` that includes detailed info about the subjectAlternativeName extension in an easily machine parsable format. Example: If the contents of the extension appear as this in the current openssl_x509_parse() output (wrapped for readability): ``` Array ( [name] => ... ... [extensions] => Array ( [subjectAltName] => DNS:www.good.org, email:good@good.org, IP Address:192.168.0.1, othername:, othername:SmtpUTF8Mailbox:test@test.example.com, URI:sip:6000@192.168.0.1, DirName/C=US/ST=CA/L=San Francisco/O=Example Company/OU=Example Company Unit/CN=Bob, Registered ID:1.2.3.4.5 ) ) ``` you would see a new top-level array element like this: ``` Array ( ... [subjectAlternativeName] => Array ( [0] => Array ( [type] => DNS [value] => www.good.org ) [1] => Array ( [type] => email [value] => good@good.org ) [2] => Array ( [type] => IP Address [value] => 192.168.0.1 ) [3] => Array ( [type] => othername [value] => Array ( [1.3.6.1.5.5.7.8.7] => foo@example.org ) ) [4] => Array ( [type] => othername [value] => Array ( [1.3.6.1.5.5.7.8.9] => test@test.example.com ) ) [5] => Array ( [type] => URI [value] => sip:6000@192.168.0.1 ) [6] => Array ( [type] => DirName [value] => Array ( [2.5.4.6] => US [2.5.4.8] => CA [2.5.4.7] => San Francisco [2.5.4.10] => Example Company [2.5.4.11] => Example Company Unit [2.5.4.3] => Bob ) ) [7] => Array ( [type] => Registered ID [value] => 1.2.3.4.5 ) ) ) ``` --- ext/openssl/openssl.c | 15 +- ext/openssl/openssl_backend_common.c | 187 ++++++++++++++++-- ext/openssl/php_openssl_backend.h | 12 +- ext/openssl/tests/subjectAlternativeName.crt | 20 ++ ext/openssl/tests/subjectAlternativeName.phpt | 108 ++++++++++ 5 files changed, 321 insertions(+), 21 deletions(-) create mode 100644 ext/openssl/tests/subjectAlternativeName.crt create mode 100644 ext/openssl/tests/subjectAlternativeName.phpt diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 2c09b89e31200..55afbf50c4a38 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -1014,6 +1014,7 @@ PHP_FUNCTION(openssl_x509_parse) char *str_serial; char *hex_serial; char buf[256]; + zval *altname = NULL; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str) @@ -1033,7 +1034,8 @@ PHP_FUNCTION(openssl_x509_parse) add_assoc_string(return_value, "name", cert_name); OPENSSL_free(cert_name); - php_openssl_add_assoc_name_entry(return_value, "subject", subject_name, useshortnames); + php_openssl_add_assoc_name_entry(return_value, "subject", subject_name, useshortnames ? + PHP_OPENSSL_SHORT_NAME : PHP_OPENSSL_LONG_NAME); /* hash as used in CA directories to lookup cert by subject name */ { char buf[32]; @@ -1041,7 +1043,8 @@ PHP_FUNCTION(openssl_x509_parse) add_assoc_string(return_value, "hash", buf); } - php_openssl_add_assoc_name_entry(return_value, "issuer", X509_get_issuer_name(cert), useshortnames); + php_openssl_add_assoc_name_entry(return_value, "issuer", X509_get_issuer_name(cert), useshortnames ? + PHP_OPENSSL_SHORT_NAME : PHP_OPENSSL_LONG_NAME); add_assoc_long(return_value, "version", X509_get_version(cert)); asn1_serial = X509_get_serialNumber(cert); @@ -1133,7 +1136,7 @@ PHP_FUNCTION(openssl_x509_parse) goto err_subitem; } if (nid == NID_subject_alt_name) { - if (openssl_x509v3_subjectAltName(bio_out, extension) == 0) { + if (openssl_x509v3_subjectAltName(bio_out, extension, &altname) == 0) { BIO_get_mem_ptr(bio_out, &bio_buf); add_assoc_stringl(&subitem, extname, bio_buf->data, bio_buf->length); } else { @@ -1150,6 +1153,9 @@ PHP_FUNCTION(openssl_x509_parse) BIO_free(bio_out); } add_assoc_zval(return_value, "extensions", &subitem); + if (altname != NULL) { + add_assoc_zval(return_value, "subjectAlternativeName", altname); + } if (cert_str) { X509_free(cert); } @@ -1953,7 +1959,8 @@ PHP_FUNCTION(openssl_csr_get_subject) subject = X509_REQ_get_subject_name(csr); array_init(return_value); - php_openssl_add_assoc_name_entry(return_value, NULL, subject, use_shortnames); + php_openssl_add_assoc_name_entry(return_value, NULL, subject, use_shortnames ? + PHP_OPENSSL_SHORT_NAME : PHP_OPENSSL_LONG_NAME); if (csr_str) { X509_REQ_free(csr); diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c index 611359cccaba6..ec7b49944ef7f 100644 --- a/ext/openssl/openssl_backend_common.c +++ b/ext/openssl/openssl_backend_common.c @@ -25,6 +25,11 @@ /* Common */ #include +#ifdef PHP_WIN32 +# include +#else +# include +#endif #if (defined(PHP_WIN32) && defined(_MSC_VER)) #define timezone _timezone /* timezone is called _timezone in LibC */ @@ -35,12 +40,14 @@ /* 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, X509_NAME * name, + enum php_openssl_name_type nametype) { zval *data; zval subitem, tmp; int i; char *sname; + char oname[1024]; int nid; X509_NAME_ENTRY * ne; ASN1_STRING * str = NULL; @@ -59,12 +66,20 @@ void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name, ne = X509_NAME_get_entry(name, i); obj = X509_NAME_ENTRY_get_object(ne); - nid = OBJ_obj2nid(obj); - if (shortname) { - sname = (char *) OBJ_nid2sn(nid); - } else { - sname = (char *) OBJ_nid2ln(nid); + switch (nametype) { + case PHP_OPENSSL_SHORT_NAME: + nid = OBJ_obj2nid(obj); + sname = (char *) OBJ_nid2sn(nid); + break; + case PHP_OPENSSL_LONG_NAME: + nid = OBJ_obj2nid(obj); + sname = (char *) OBJ_nid2ln(nid); + break; + case PHP_OPENSSL_OID: + OBJ_obj2txt(oname, sizeof(oname)-1, obj, 1); + sname = oname; + break; } str = X509_NAME_ENTRY_get_data(ne); @@ -613,16 +628,61 @@ zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, bool r return ret; } +/* proto void print_asn1_type(BIO *bio, ASN1_TYPE *ptr) + Print the value of an ASN1_TYPE to a BIO */ +static void print_asn1_type(BIO *bio, ASN1_TYPE *ptr) +{ + char objbuf[1024]; + + // Collect the things from the ASN1_TYPE structure + switch (ptr->type) { + case V_ASN1_BOOLEAN: + BIO_puts(bio, ptr->value.boolean ? "true" : "false"); + break; + + case V_ASN1_INTEGER: + BIO_puts(bio, i2s_ASN1_INTEGER(NULL, ptr->value.integer)); + break; + + case V_ASN1_ENUMERATED: + BIO_puts(bio, i2s_ASN1_INTEGER(NULL, ptr->value.enumerated)); + break; + + case V_ASN1_NULL: + BIO_puts(bio, "NULL"); + break; + + case V_ASN1_UTCTIME: + ASN1_UTCTIME_print(bio, ptr->value.utctime); + break; + + case V_ASN1_GENERALIZEDTIME: + ASN1_GENERALIZEDTIME_print(bio, ptr->value.generalizedtime); + break; + + case V_ASN1_OBJECT: + OBJ_obj2txt(objbuf, sizeof(objbuf), ptr->value.object, 1); + BIO_puts(bio, objbuf); + break; + + default : + ASN1_STRING_print_ex(bio, ptr->value.visiblestring, + ASN1_STRFLGS_DUMP_UNKNOWN); + break; + } +} + /* 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, X509_EXTENSION *extension, zval **altname) { GENERAL_NAMES *names; const X509V3_EXT_METHOD *method = NULL; ASN1_OCTET_STRING *extension_data; long i, length, num; const unsigned char *p; + zend_ulong index = 0; method = X509V3_EXT_get(extension); if (method == NULL) { @@ -644,9 +704,17 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension) } num = sk_GENERAL_NAME_num(names); + if (altname != NULL) { + if (*altname == NULL) { + *altname = (zval *)safe_emalloc(1, sizeof(zval), 0); + } + array_init(*altname); + } for (i = 0; i < num; i++) { GENERAL_NAME *name; ASN1_STRING *as; + zval entry; + array_init(&entry); name = sk_GENERAL_NAME_value(names, i); switch (name->type) { case GEN_EMAIL: @@ -654,29 +722,118 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension) as = name->d.rfc822Name; BIO_write(bio, ASN1_STRING_get0_data(as), ASN1_STRING_length(as)); + if (altname != NULL) { + add_assoc_string(&entry, "type", "email"); + php_openssl_add_assoc_asn1_string(&entry, "value", as); + add_index_zval(*altname, index++, &entry); + } break; case GEN_DNS: BIO_puts(bio, "DNS:"); as = name->d.dNSName; BIO_write(bio, ASN1_STRING_get0_data(as), ASN1_STRING_length(as)); + if (altname != NULL) { + add_assoc_string(&entry, "type", "DNS"); + php_openssl_add_assoc_asn1_string(&entry, "value", as); + add_index_zval(*altname, index++, &entry); + } break; case GEN_URI: BIO_puts(bio, "URI:"); as = name->d.uniformResourceIdentifier; BIO_write(bio, ASN1_STRING_get0_data(as), ASN1_STRING_length(as)); + if (altname != NULL) { + add_assoc_string(&entry, "type", "URI"); + php_openssl_add_assoc_asn1_string(&entry, "value", as); + add_index_zval(*altname, index++, &entry); + } + break; + case GEN_DIRNAME: + GENERAL_NAME_print(bio, name); + if (altname != NULL) { + add_assoc_string(&entry, "type", "DirName"); + php_openssl_add_assoc_name_entry(&entry, "value", name->d.dirn, PHP_OPENSSL_OID); + add_index_zval(*altname, index++, &entry); + } + break; + case GEN_RID: + GENERAL_NAME_print(bio, name); + if (altname != NULL) { + char buf[1024]; + OBJ_obj2txt(buf, sizeof(buf)-1, name->d.rid, 1); + add_assoc_string(&entry, "type", "Registered ID"); + add_assoc_string(&entry, "value", buf); + add_index_zval(*altname, index++, &entry); + } + break; + case GEN_IPADD: + GENERAL_NAME_print(bio, name); + if (altname != NULL) { + char buf[1024]; + if (name->d.ip->length == 4) { + inet_ntop(AF_INET, name->d.ip->data, buf, sizeof(buf)-1); + } else if (name->d.ip->length == 16) { + inet_ntop(AF_INET6, name->d.ip->data, buf, sizeof(buf)-1); + } else { + sprintf(buf, ""); + } + add_assoc_string(&entry, "type", "IP Address"); + add_assoc_string(&entry, "value", buf); + add_index_zval(*altname, index++, &entry); + } + break; + case GEN_OTHERNAME: + GENERAL_NAME_print(bio, name); + if (altname != NULL) { + char oid[1024]; + zval value; + array_init(&value); + + OBJ_obj2txt(oid, sizeof(oid)-1, name->d.otherName->type_id, 1); + + BIO *bio_out; + BUF_MEM *bio_buf; + bio_out = BIO_new(BIO_s_mem()); + print_asn1_type(bio_out, name->d.otherName->value); + BIO_get_mem_ptr(bio_out, &bio_buf); + + add_assoc_stringl(&value, oid, bio_buf->data, bio_buf->length); + add_assoc_string(&entry, "type", "othername"); + add_assoc_zval(&entry, "value", &value); + add_index_zval(*altname, index++, &entry); + BIO_free(bio_out); + } break; default: - /* use builtin print for GEN_OTHERNAME, GEN_X400, - * GEN_EDIPARTY, GEN_DIRNAME, GEN_IPADD and GEN_RID - */ GENERAL_NAME_print(bio, name); - } - /* trailing ', ' except for last element */ - if (i < (num - 1)) { - BIO_puts(bio, ", "); - } + if (altname != NULL) { + BIO *bio_out; + BUF_MEM *bio_buf; + bio_out = BIO_new(BIO_s_mem()); + GENERAL_NAME_print(bio_out, name); + BIO_get_mem_ptr(bio_out, &bio_buf); + switch (name->type) { + case GEN_X400: + add_assoc_string(&entry, "type", "X400Name"); + break; + case GEN_EDIPARTY: + add_assoc_string(&entry, "type", "EdiPartyName"); + break; + default: + add_assoc_string(&entry, "type", "Unknown"); + break; + } + add_assoc_stringl(&entry, "value", bio_buf->data, bio_buf->length); + add_index_zval(*altname, index++, &entry); + BIO_free(bio_out); + } + } + /* trailing ', ' except for last element */ + if (i < (num - 1)) { + BIO_puts(bio, ", "); + } } sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); diff --git a/ext/openssl/php_openssl_backend.h b/ext/openssl/php_openssl_backend.h index 00da5e74fc1b8..920845ddc467c 100644 --- a/ext/openssl/php_openssl_backend.h +++ b/ext/openssl/php_openssl_backend.h @@ -99,6 +99,13 @@ enum php_openssl_cipher_type { PHP_OPENSSL_CIPHER_DEFAULT = PHP_OPENSSL_CIPHER_AES_128_CBC }; +/* Name display options for php_openssl_add_assoc_name_entry */ +enum php_openssl_name_type { + PHP_OPENSSL_LONG_NAME, + PHP_OPENSSL_SHORT_NAME, + PHP_OPENSSL_OID +}; + /* Add some encoding rules. This is normally handled through filters * in the OpenSSL code, but we will do that part as if we were one * of the OpenSSL binaries along the lines of -outform {DER|CMS|PEM} @@ -168,7 +175,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_name_entry(zval * val, char * key, X509_NAME * name, + enum php_openssl_name_type nametype); void php_openssl_add_assoc_asn1_string(zval * val, char * key, 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); @@ -266,7 +274,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, X509_EXTENSION *extension, zval **altname); 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/subjectAlternativeName.crt b/ext/openssl/tests/subjectAlternativeName.crt new file mode 100644 index 0000000000000..0d4a9b9ecfb40 --- /dev/null +++ b/ext/openssl/tests/subjectAlternativeName.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDRzCCAuygAwIBAgIUDcQjrtk7F/g4Mdm+NiAzKpInXDEwCgYIKoZIzj0EAwIw +ezELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNh +biBGcmFuY2lzY28xKTARBgNVBAoMCk15IENvbXBhbnkwFAYDVQQLDA1NeSBEZXBh +cnRtZW50MRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0yNTExMDMxOTEzNDBaFw0y +NjExMDMxOTEzNDBaMHsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh +MRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMSkwEQYDVQQKDApNeSBDb21wYW55MBQG +A1UECwwNTXkgRGVwYXJ0bWVudDEUMBIGA1UEAwwLZXhhbXBsZS5jb20wWTATBgcq +hkjOPQIBBggqhkjOPQMBBwNCAAQ+riFshYe8HnWt1avx6OuNajipU1ZW6BgW0+D/ +EtDDSYeQg9ngO8qyo5M6cyh7ORtKZVUy7DP1+W+eocaZC+a6o4IBTDCCAUgwggEl +BgNVHREEggEcMIIBGIILZXhhbXBsZS5jb22CD3d3dy5leGFtcGxlLmNvbYIVc3Vi +ZG9tYWluLmV4YW1wbGUuY29thwTAqAEBhxAmB/DQEAIAUQAAAAAAAAAEgRFhZG1p +bkBleGFtcGxlLmNvbaROMEwxETAPBgNVBAMMCEpvaG4gRG9lMSowDgYDVQQLDAdU +ZXN0aW5nMBgGA1UECgwRRXhhbXBsZSBPcmcsIEluYy4xCzAJBgNVBAYTAlVToCMG +CSqGSIb3DQEJAqAWDBRVSURfdW5zdHJ1Y3R1cmVkTmFtZaAfBgkqhkiG9w0BCRSg +EhYQVUlEX2ZyaWVuZGx5TmFtZYgDKgMEhhtodHRwOi8vZXhhbXBsZS5jb20vcmVz +b3VyY2UwHQYDVR0OBBYEFICesJGN6QyOP89fyTVAmhL28E0NMAoGCCqGSM49BAMC +A0kAMEYCIQDah0YhiFdhHPZIMkHS91QsN2A9HZ4YwZi0wObHcB8r5gIhAJc+Szee +2PfEQatjoWj/K1xZBV8ZNF6UtjzdY/5VD52z +-----END CERTIFICATE----- diff --git a/ext/openssl/tests/subjectAlternativeName.phpt b/ext/openssl/tests/subjectAlternativeName.phpt new file mode 100644 index 0000000000000..86165bec040ff --- /dev/null +++ b/ext/openssl/tests/subjectAlternativeName.phpt @@ -0,0 +1,108 @@ +--TEST-- +openssl_x509_parse() subjectAlternativeName test +--EXTENSIONS-- +openssl +--SKIPIF-- += 0x30200000) die('skip For OpenSSL < 3.2'); +?> +--FILE-- + + array(2) { + ["type"]=> + string(3) "DNS" + ["value"]=> + string(11) "example.com" + } + [1]=> + array(2) { + ["type"]=> + string(3) "DNS" + ["value"]=> + string(15) "www.example.com" + } + [2]=> + array(2) { + ["type"]=> + string(3) "DNS" + ["value"]=> + string(21) "subdomain.example.com" + } + [3]=> + array(2) { + ["type"]=> + string(10) "IP Address" + ["value"]=> + string(11) "192.168.1.1" + } + [4]=> + array(2) { + ["type"]=> + string(10) "IP Address" + ["value"]=> + string(20) "2607:f0d0:1002:51::4" + } + [5]=> + array(2) { + ["type"]=> + string(5) "email" + ["value"]=> + string(17) "admin@example.com" + } + [6]=> + array(2) { + ["type"]=> + string(7) "DirName" + ["value"]=> + array(4) { + ["2.5.4.3"]=> + string(8) "John Doe" + ["2.5.4.11"]=> + string(7) "Testing" + ["2.5.4.10"]=> + string(17) "Example Org, Inc." + ["2.5.4.6"]=> + string(2) "US" + } + } + [7]=> + array(2) { + ["type"]=> + string(9) "othername" + ["value"]=> + array(1) { + ["1.2.840.113549.1.9.2"]=> + string(20) "UID_unstructuredName" + } + } + [8]=> + array(2) { + ["type"]=> + string(9) "othername" + ["value"]=> + array(1) { + ["1.2.840.113549.1.9.20"]=> + string(16) "UID_friendlyName" + } + } + [9]=> + array(2) { + ["type"]=> + string(13) "Registered ID" + ["value"]=> + string(7) "1.2.3.4" + } + [10]=> + array(2) { + ["type"]=> + string(3) "URI" + ["value"]=> + string(27) "http://example.com/resource" + } +} From deec9793d5f220addb14371e040b04f983653a4d Mon Sep 17 00:00:00 2001 From: Steve Wall Date: Tue, 13 Jan 2026 22:12:06 -0500 Subject: [PATCH 2/7] Compiler couldn't tell that isn't really uninitialized --- ext/openssl/openssl_backend_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c index ec7b49944ef7f..27367b5688281 100644 --- a/ext/openssl/openssl_backend_common.c +++ b/ext/openssl/openssl_backend_common.c @@ -46,7 +46,7 @@ void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name, zval *data; zval subitem, tmp; int i; - char *sname; + char *sname = NULL; char oname[1024]; int nid; X509_NAME_ENTRY * ne; From 72259f3b016364869b5aa0ff8319167ff31bef48 Mon Sep 17 00:00:00 2001 From: Steve Wall Date: Tue, 13 Jan 2026 22:16:52 -0500 Subject: [PATCH 3/7] Remove nonsense comment --- ext/openssl/openssl_backend_common.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c index 27367b5688281..e8cbc38d602af 100644 --- a/ext/openssl/openssl_backend_common.c +++ b/ext/openssl/openssl_backend_common.c @@ -634,7 +634,6 @@ static void print_asn1_type(BIO *bio, ASN1_TYPE *ptr) { char objbuf[1024]; - // Collect the things from the ASN1_TYPE structure switch (ptr->type) { case V_ASN1_BOOLEAN: BIO_puts(bio, ptr->value.boolean ? "true" : "false"); From 68930826cb615fc7c4a525bfcec76fccd52255d8 Mon Sep 17 00:00:00 2001 From: Steve Wall Date: Tue, 13 Jan 2026 22:41:02 -0500 Subject: [PATCH 4/7] Free allocated altname zval in the case of an error --- ext/openssl/openssl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 55afbf50c4a38..f368096ca88d9 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -1163,6 +1163,10 @@ PHP_FUNCTION(openssl_x509_parse) err_subitem: zval_ptr_dtor(&subitem); + if (altname != NULL) { + zval_ptr_dtor(altname); + efree(altname); + } err: zend_array_destroy(Z_ARR_P(return_value)); if (cert_str) { From cc0efa6cd60118ef2ba1ab6383ad8b84cfb28b8d Mon Sep 17 00:00:00 2001 From: Steve Wall Date: Wed, 14 Jan 2026 10:16:49 -0500 Subject: [PATCH 5/7] Some simplification to eliminate memory management issues --- ext/openssl/openssl.c | 15 ++++++--------- ext/openssl/openssl_backend_common.c | 24 +++++++++--------------- ext/openssl/php_openssl_backend.h | 2 +- 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index f368096ca88d9..2762f39692eed 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -1014,7 +1014,7 @@ PHP_FUNCTION(openssl_x509_parse) char *str_serial; char *hex_serial; char buf[256]; - zval *altname = NULL; + zval altname; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str) @@ -1118,8 +1118,7 @@ PHP_FUNCTION(openssl_x509_parse) add_assoc_zval(return_value, "purposes", &subitem); array_init(&subitem); - - + array_init(&altname); for (i = 0; i < X509_get_ext_count(cert); i++) { int nid; extension = X509_get_ext(cert, i); @@ -1153,8 +1152,9 @@ PHP_FUNCTION(openssl_x509_parse) BIO_free(bio_out); } add_assoc_zval(return_value, "extensions", &subitem); - if (altname != NULL) { - add_assoc_zval(return_value, "subjectAlternativeName", altname); + ulong altcount = zend_hash_num_elements(Z_ARRVAL_P(&altname)); + if (altcount > 0) { + add_assoc_zval(return_value, "subjectAlternativeName", &altname); } if (cert_str) { X509_free(cert); @@ -1163,10 +1163,7 @@ PHP_FUNCTION(openssl_x509_parse) err_subitem: zval_ptr_dtor(&subitem); - if (altname != NULL) { - zval_ptr_dtor(altname); - efree(altname); - } + zval_ptr_dtor(&altname); err: zend_array_destroy(Z_ARR_P(return_value)); if (cert_str) { diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c index e8cbc38d602af..8dccd7c417650 100644 --- a/ext/openssl/openssl_backend_common.c +++ b/ext/openssl/openssl_backend_common.c @@ -674,7 +674,7 @@ static void print_asn1_type(BIO *bio, ASN1_TYPE *ptr) /* Special handling of subjectAltName, see CVE-2013-4073 * Christian Heimes */ -int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension, zval **altname) +int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension, zval *altname) { GENERAL_NAMES *names; const X509V3_EXT_METHOD *method = NULL; @@ -703,12 +703,6 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension, zval **al } num = sk_GENERAL_NAME_num(names); - if (altname != NULL) { - if (*altname == NULL) { - *altname = (zval *)safe_emalloc(1, sizeof(zval), 0); - } - array_init(*altname); - } for (i = 0; i < num; i++) { GENERAL_NAME *name; ASN1_STRING *as; @@ -724,7 +718,7 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension, zval **al if (altname != NULL) { add_assoc_string(&entry, "type", "email"); php_openssl_add_assoc_asn1_string(&entry, "value", as); - add_index_zval(*altname, index++, &entry); + add_index_zval(altname, index++, &entry); } break; case GEN_DNS: @@ -735,7 +729,7 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension, zval **al if (altname != NULL) { add_assoc_string(&entry, "type", "DNS"); php_openssl_add_assoc_asn1_string(&entry, "value", as); - add_index_zval(*altname, index++, &entry); + add_index_zval(altname, index++, &entry); } break; case GEN_URI: @@ -746,7 +740,7 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension, zval **al if (altname != NULL) { add_assoc_string(&entry, "type", "URI"); php_openssl_add_assoc_asn1_string(&entry, "value", as); - add_index_zval(*altname, index++, &entry); + add_index_zval(altname, index++, &entry); } break; case GEN_DIRNAME: @@ -754,7 +748,7 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension, zval **al if (altname != NULL) { add_assoc_string(&entry, "type", "DirName"); php_openssl_add_assoc_name_entry(&entry, "value", name->d.dirn, PHP_OPENSSL_OID); - add_index_zval(*altname, index++, &entry); + add_index_zval(altname, index++, &entry); } break; case GEN_RID: @@ -764,7 +758,7 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension, zval **al OBJ_obj2txt(buf, sizeof(buf)-1, name->d.rid, 1); add_assoc_string(&entry, "type", "Registered ID"); add_assoc_string(&entry, "value", buf); - add_index_zval(*altname, index++, &entry); + add_index_zval(altname, index++, &entry); } break; case GEN_IPADD: @@ -780,7 +774,7 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension, zval **al } add_assoc_string(&entry, "type", "IP Address"); add_assoc_string(&entry, "value", buf); - add_index_zval(*altname, index++, &entry); + add_index_zval(altname, index++, &entry); } break; case GEN_OTHERNAME: @@ -801,7 +795,7 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension, zval **al add_assoc_stringl(&value, oid, bio_buf->data, bio_buf->length); add_assoc_string(&entry, "type", "othername"); add_assoc_zval(&entry, "value", &value); - add_index_zval(*altname, index++, &entry); + add_index_zval(altname, index++, &entry); BIO_free(bio_out); } break; @@ -825,7 +819,7 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension, zval **al break; } add_assoc_stringl(&entry, "value", bio_buf->data, bio_buf->length); - add_index_zval(*altname, index++, &entry); + add_index_zval(altname, index++, &entry); BIO_free(bio_out); } } diff --git a/ext/openssl/php_openssl_backend.h b/ext/openssl/php_openssl_backend.h index 920845ddc467c..f9b300a8ee67c 100644 --- a/ext/openssl/php_openssl_backend.h +++ b/ext/openssl/php_openssl_backend.h @@ -274,7 +274,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, zval **altname); +int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension, zval *altname); STACK_OF(X509) *php_openssl_load_all_certs_from_file( char *cert_file, size_t cert_file_len, uint32_t arg_num); From 060387ea7b30f232b1e7b99c4c92f9a74cec1fc9 Mon Sep 17 00:00:00 2001 From: Steve Wall Date: Wed, 14 Jan 2026 10:40:49 -0500 Subject: [PATCH 6/7] fix type --- ext/openssl/openssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 2762f39692eed..6e36646bcb0c1 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -1152,7 +1152,7 @@ PHP_FUNCTION(openssl_x509_parse) BIO_free(bio_out); } add_assoc_zval(return_value, "extensions", &subitem); - ulong altcount = zend_hash_num_elements(Z_ARRVAL_P(&altname)); + zend_long altcount = zend_hash_num_elements(Z_ARRVAL_P(&altname)); if (altcount > 0) { add_assoc_zval(return_value, "subjectAlternativeName", &altname); } From b2f67ff6d109a76a881f831fbafbf9b6422288eb Mon Sep 17 00:00:00 2001 From: Steve Wall Date: Wed, 14 Jan 2026 13:29:25 -0500 Subject: [PATCH 7/7] Free altname if not using it --- ext/openssl/openssl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 6e36646bcb0c1..a332e152da186 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -1155,6 +1155,8 @@ PHP_FUNCTION(openssl_x509_parse) zend_long altcount = zend_hash_num_elements(Z_ARRVAL_P(&altname)); if (altcount > 0) { add_assoc_zval(return_value, "subjectAlternativeName", &altname); + } else { + zval_ptr_dtor(&altname); } if (cert_str) { X509_free(cert);