Skip to content

Commit db326c7

Browse files
shivammathurthesamesam
authored andcommitted
ext/openssl: fix OpenSSL 4 build and test compatibility (#22267)
* Fix OpenSSL 4 test compatibility * Fix OpenSSL 4 build compatibility * Use OpenSSL 4 in macOS CI * Fix OpenSSL 1.1 build compatibility * Preserve raw CN matching with OpenSSL 4 (cherry picked from commit 6aca6e3)
1 parent 8ee6865 commit db326c7

6 files changed

Lines changed: 122 additions & 27 deletions

File tree

‎ext/openssl/openssl.c‎

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -663,16 +663,16 @@ static STACK_OF(X509) * php_openssl_load_all_certs_from_file(
663663
char *cert_file, size_t cert_file_len, uint32_t arg_num);
664664
static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req);
665665

666-
static void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname) /* {{{ */
666+
static void php_openssl_add_assoc_name_entry(zval * val, char * key, const X509_NAME * name, int shortname) /* {{{ */
667667
{
668668
zval *data;
669669
zval subitem, tmp;
670670
int i;
671671
char *sname;
672672
int nid;
673-
X509_NAME_ENTRY * ne;
674-
ASN1_STRING * str = NULL;
675-
ASN1_OBJECT * obj;
673+
const X509_NAME_ENTRY * ne;
674+
const ASN1_STRING * str = NULL;
675+
const ASN1_OBJECT * obj;
676676

677677
if (key != NULL) {
678678
array_init(&subitem);
@@ -737,7 +737,7 @@ static void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME *
737737
}
738738
/* }}} */
739739

740-
static void php_openssl_add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str) /* {{{ */
740+
static void php_openssl_add_assoc_asn1_string(zval * val, char * key, const ASN1_STRING * str) /* {{{ */
741741
{
742742
add_assoc_stringl(val, key, (const char *)ASN1_STRING_get0_data(str), ASN1_STRING_length(str));
743743
}
@@ -1324,7 +1324,7 @@ PHP_MINIT_FUNCTION(openssl)
13241324
}
13251325

13261326
php_stream_xport_register("ssl", php_openssl_ssl_socket_factory);
1327-
#ifndef OPENSSL_NO_SSL3
1327+
#if OPENSSL_VERSION_NUMBER < 0x40000000L && !defined(OPENSSL_NO_SSL3)
13281328
php_stream_xport_register("sslv3", php_openssl_ssl_socket_factory);
13291329
#endif
13301330
php_stream_xport_register("tls", php_openssl_ssl_socket_factory);
@@ -1410,7 +1410,7 @@ PHP_MSHUTDOWN_FUNCTION(openssl)
14101410
php_unregister_url_stream_wrapper("ftps");
14111411

14121412
php_stream_xport_unregister("ssl");
1413-
#ifndef OPENSSL_NO_SSL3
1413+
#if OPENSSL_VERSION_NUMBER < 0x40000000L && !defined(OPENSSL_NO_SSL3)
14141414
php_stream_xport_unregister("sslv3");
14151415
#endif
14161416
php_stream_xport_unregister("tls");
@@ -2026,11 +2026,11 @@ PHP_FUNCTION(openssl_x509_verify)
20262026
* Christian Heimes
20272027
*/
20282028

2029-
static int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)
2029+
static int openssl_x509v3_subjectAltName(BIO *bio, PHP_OPENSSL_X509_EXTENSION *extension)
20302030
{
20312031
GENERAL_NAMES *names;
20322032
const X509V3_EXT_METHOD *method = NULL;
2033-
ASN1_OCTET_STRING *extension_data;
2033+
const ASN1_OCTET_STRING *extension_data;
20342034
long i, length, num;
20352035
const unsigned char *p;
20362036

@@ -2103,8 +2103,8 @@ PHP_FUNCTION(openssl_x509_parse)
21032103
bool useshortnames = 1;
21042104
char * tmpstr;
21052105
zval subitem;
2106-
X509_EXTENSION *extension;
2107-
X509_NAME *subject_name;
2106+
PHP_OPENSSL_X509_EXTENSION *extension;
2107+
const X509_NAME *subject_name;
21082108
char *cert_name;
21092109
char *extname;
21102110
BIO *bio_out;
@@ -2989,7 +2989,12 @@ static zend_result php_openssl_csr_make(struct php_x509_request * req, X509_REQ
29892989
zval *item, *subitem;
29902990
zend_string *strindex = NULL;
29912991

2992-
subj = X509_REQ_get_subject_name(csr);
2992+
subj = X509_NAME_new();
2993+
if (subj == NULL) {
2994+
php_openssl_store_errors();
2995+
return FAILURE;
2996+
}
2997+
29932998
/* apply values from the dn hash */
29942999
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(dn), strindex, item) {
29953000
if (strindex) {
@@ -2998,10 +3003,12 @@ static zend_result php_openssl_csr_make(struct php_x509_request * req, X509_REQ
29983003
if (Z_TYPE_P(item) == IS_ARRAY) {
29993004
ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(item), i, subitem) {
30003005
if (php_openssl_csr_add_subj_entry(subitem, subj, nid) == FAILURE) {
3006+
X509_NAME_free(subj);
30013007
return FAILURE;
30023008
}
30033009
} ZEND_HASH_FOREACH_END();
30043010
} else if (php_openssl_csr_add_subj_entry(item, subj, nid) == FAILURE) {
3011+
X509_NAME_free(subj);
30053012
return FAILURE;
30063013
}
30073014
} else {
@@ -3052,13 +3059,23 @@ static zend_result php_openssl_csr_make(struct php_x509_request * req, X509_REQ
30523059
if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_UTF8, (unsigned char*)v->value, -1, -1, 0)) {
30533060
php_openssl_store_errors();
30543061
php_error_docref(NULL, E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value);
3062+
X509_NAME_free(subj);
30553063
return FAILURE;
30563064
}
30573065
if (!X509_NAME_entry_count(subj)) {
30583066
php_error_docref(NULL, E_WARNING, "No objects specified in config file");
3067+
X509_NAME_free(subj);
30593068
return FAILURE;
30603069
}
30613070
}
3071+
3072+
if (!X509_REQ_set_subject_name(csr, subj)) {
3073+
php_openssl_store_errors();
3074+
X509_NAME_free(subj);
3075+
return FAILURE;
3076+
}
3077+
X509_NAME_free(subj);
3078+
30623079
if (attribs) {
30633080
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(attribs), strindex, item) {
30643081
int nid;
@@ -3547,7 +3564,7 @@ PHP_FUNCTION(openssl_csr_get_subject)
35473564
zend_object *csr_obj;
35483565
zend_string *csr_str;
35493566
bool use_shortnames = 1;
3550-
X509_NAME *subject;
3567+
const X509_NAME *subject;
35513568

35523569
ZEND_PARSE_PARAMETERS_START(1, 2)
35533570
Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
@@ -4697,7 +4714,7 @@ static EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private) {
46974714
}
46984715

46994716
OPENSSL_PKEY_SET_BN(data, cofactor);
4700-
if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR, cofactor) ||
4717+
if ((cofactor && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR, cofactor)) ||
47014718
!EC_GROUP_set_generator(group, point_g, order, cofactor)) {
47024719
goto cleanup;
47034720
}

‎ext/openssl/php_openssl.h‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ extern zend_module_entry openssl_module_entry;
2525
#include "php_version.h"
2626
#define PHP_OPENSSL_VERSION PHP_VERSION
2727

28+
#include <openssl/opensslconf.h>
2829
#include <openssl/opensslv.h>
2930
#ifdef LIBRESSL_VERSION_NUMBER
3031
/* LibreSSL version check */
@@ -44,6 +45,12 @@ extern zend_module_entry openssl_module_entry;
4445
#endif
4546
#endif
4647

48+
#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 4
49+
typedef const X509_EXTENSION PHP_OPENSSL_X509_EXTENSION;
50+
#else
51+
typedef X509_EXTENSION PHP_OPENSSL_X509_EXTENSION;
52+
#endif
53+
4754
#define PHP_OPENSSL_ERR_BUFFER_SIZE 16
4855

4956
#define OPENSSL_RAW_DATA 1

‎ext/openssl/tests/bug28382.phpt‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ openssl
66
<?php
77
$cert = file_get_contents(__DIR__ . "/bug28382cert.txt");
88
$ext = openssl_x509_parse($cert);
9+
10+
if (OPENSSL_VERSION_NUMBER >= 0x40000000) {
11+
$extensions = &$ext['extensions'];
12+
$extensions['crlDistributionPoints'] = preg_replace('/^Full Name:\R\s*/', '', trim($extensions['crlDistributionPoints']));
13+
}
14+
915
var_dump($ext['extensions']);
1016
/*
1117
* The reason for %A at the end of crlDistributionPoints and authorityKeyIdentifier is that
@@ -30,7 +36,7 @@ array(11) {
3036
string(59) "B0:A7:FF:F9:41:15:DE:23:39:BD:DD:31:0F:97:A0:B2:A2:74:E0:FC"
3137
["authorityKeyIdentifier"]=>
3238
string(%d) "DirName:/C=RO/ST=Romania/L=Craiova/O=Sergiu/OU=Sergiu SRL/CN=Sergiu CA/emailAddress=n_sergiu@hotmail.com
33-
serial:00%A"
39+
serial:%A"
3440
["keyUsage"]=>
3541
string(71) "Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment"
3642
["nsBaseUrl"]=>

‎ext/openssl/tests/ecc_custom_params.phpt‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,30 @@ openssl
55
--SKIPIF--
66
<?php
77
if (!defined("OPENSSL_KEYTYPE_EC")) die("skip EC disabled");
8+
9+
if (OPENSSL_VERSION_NUMBER >= 0x40000000) {
10+
$d = hex2bin('8D0AC65AAEA0D6B96254C65817D4A143A9E7A03876F1A37D');
11+
$p = hex2bin('BDB6F4FE3E8B1D9E0DA8C0D46F4C318CEFE4AFE3B6B8551F');
12+
$a = hex2bin('BB8E5E8FBC115E139FE6A814FE48AAA6F0ADA1AA5DF91985');
13+
$b = hex2bin('1854BEBDC31B21B7AEFC80AB0ECD10D5B1B3308E6DBF11C1');
14+
$g_x = hex2bin('4AD5F7048DE709AD51236DE65E4D4B482C836DC6E4106640');
15+
$g_y = hex2bin('02BB3A02D4AAADACAE24817A4CA3A1B014B5270432DB27D2');
16+
$order = hex2bin('BDB6F4FE3E8B1D9E0DA8C0D40FC962195DFAE76F56564677');
17+
18+
if (@openssl_pkey_new(array(
19+
'ec' => array(
20+
'p' => $p,
21+
'a' => $a,
22+
'b' => $b,
23+
'order' => $order,
24+
'g_x' => $g_x,
25+
'g_y' => $g_y,
26+
'd' => $d,
27+
),
28+
)) === false) {
29+
die("skip EC custom params unsupported with OpenSSL 4");
30+
}
31+
}
832
?>
933
--FILE--
1034
<?php

‎ext/openssl/tests/openssl_x509_parse_basic_openssl32.phpt‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,28 @@ if (OPENSSL_VERSION_NUMBER < 0x30200000) die('skip For OpenSSL >= 3.2');
88
?>
99
--FILE--
1010
<?php
11+
function normalize_openssl4_x509_parse_output(array $cert): array {
12+
if (OPENSSL_VERSION_NUMBER < 0x40000000 || !isset($cert['serialNumberHex'], $cert['extensions']['authorityKeyIdentifier'])) {
13+
return $cert;
14+
}
15+
16+
$serial = strtoupper($cert['serialNumberHex']);
17+
if (strlen($serial) % 2) {
18+
$serial = '0' . $serial;
19+
}
20+
$serial = implode(':', str_split($serial, 2));
21+
$cert['extensions']['authorityKeyIdentifier'] = preg_replace('/^serial:\d+$/m', 'serial:' . $serial, $cert['extensions']['authorityKeyIdentifier']);
22+
23+
return $cert;
24+
}
25+
1126
$cert = "file://" . __DIR__ . "/cert.crt";
1227

1328
$parsedCert = openssl_x509_parse($cert);
14-
var_dump($parsedCert === openssl_x509_parse(openssl_x509_read($cert)));
29+
$parsedCert = normalize_openssl4_x509_parse_output($parsedCert);
30+
var_dump($parsedCert === normalize_openssl4_x509_parse_output(openssl_x509_parse(openssl_x509_read($cert))));
1531
var_dump($parsedCert);
16-
var_dump(openssl_x509_parse($cert, false));
32+
var_dump(normalize_openssl4_x509_parse_output(openssl_x509_parse($cert, false)));
1733
?>
1834
--EXPECTF--
1935
bool(true)

‎ext/openssl/xp_ssl.c‎

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
#define HAVE_SEC_LEVEL 1
9494
#endif
9595

96-
#ifndef OPENSSL_NO_SSL3
96+
#if OPENSSL_VERSION_NUMBER < 0x40000000L && !defined(OPENSSL_NO_SSL3)
9797
#define HAVE_SSL3 1
9898
#define PHP_OPENSSL_MIN_PROTO_VERSION STREAM_CRYPTO_METHOD_SSLv3
9999
#else
@@ -544,24 +544,49 @@ static bool php_openssl_matches_san_list(
544544

545545
static bool php_openssl_matches_common_name(X509 *peer, const char *subject_name) /* {{{ */
546546
{
547-
char buf[1024];
548-
X509_NAME *cert_name;
547+
unsigned char *cert_name = NULL;
548+
#if PHP_OPENSSL_API_VERSION < 0x30000
549+
X509_NAME *name;
550+
#else
551+
const X509_NAME *name;
552+
#endif
553+
const X509_NAME_ENTRY *name_entry;
554+
const ASN1_STRING *name_asn1;
549555
bool is_match = 0;
556+
int name_index;
550557
int cert_name_len;
551558

552-
cert_name = X509_get_subject_name(peer);
553-
cert_name_len = X509_NAME_get_text_by_NID(cert_name, NID_commonName, buf, sizeof(buf));
559+
name = X509_get_subject_name(peer);
560+
name_index = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
561+
if (name_index == -1) {
562+
php_error_docref(NULL, E_WARNING, "Unable to locate peer certificate CN");
563+
return false;
564+
}
565+
566+
name_entry = X509_NAME_get_entry(name, name_index);
567+
if (name_entry == NULL) {
568+
php_error_docref(NULL, E_WARNING, "Unable to locate peer certificate CN");
569+
return false;
570+
}
554571

555-
if (cert_name_len == -1) {
572+
name_asn1 = X509_NAME_ENTRY_get_data(name_entry);
573+
cert_name_len = ASN1_STRING_length(name_asn1);
574+
cert_name = (unsigned char *) OPENSSL_strndup((const char *) ASN1_STRING_get0_data(name_asn1), cert_name_len);
575+
if (cert_name == NULL) {
556576
php_error_docref(NULL, E_WARNING, "Unable to locate peer certificate CN");
557-
} else if ((size_t)cert_name_len != strlen(buf)) {
558-
php_error_docref(NULL, E_WARNING, "Peer certificate CN=`%.*s' is malformed", cert_name_len, buf);
559-
} else if (php_openssl_matches_wildcard_name(subject_name, buf)) {
577+
return false;
578+
}
579+
580+
if ((size_t)cert_name_len != strlen((const char *)cert_name)) {
581+
php_error_docref(NULL, E_WARNING, "Peer certificate CN=`%.*s' is malformed", cert_name_len, (const char *)cert_name);
582+
return false;
583+
} else if (php_openssl_matches_wildcard_name(subject_name, (const char *)cert_name)) {
560584
is_match = 1;
561585
} else {
562586
php_error_docref(NULL, E_WARNING,
563587
"Peer certificate CN=`%.*s' did not match expected CN=`%s'",
564-
cert_name_len, buf, subject_name);
588+
cert_name_len, (const char *)cert_name, subject_name);
589+
return false;
565590
}
566591

567592
return is_match;

0 commit comments

Comments
 (0)