Skip to content

Commit 45eae90

Browse files
committed
Merge branch 'PHP-8.2'
2 parents 95e6ce6 + 58141f0 commit 45eae90

File tree

2 files changed

+112
-4
lines changed

2 files changed

+112
-4
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
--TEST--
2+
IPv6 Peer verification matches SAN names
3+
--EXTENSIONS--
4+
openssl
5+
--SKIPIF--
6+
<?php
7+
if (!function_exists("proc_open")) die("skip no proc_open");
8+
if (getenv("CI_NO_IPV6") || !defined("AF_INET6")) {
9+
die('skip no IPv6 support');
10+
}
11+
if (@stream_socket_client('udp://[::1]:8888') === false)
12+
die('skip no IPv6 support');
13+
?>
14+
--FILE--
15+
<?php
16+
$certFile = __DIR__ . DIRECTORY_SEPARATOR . 'san_ipv6_peer_matching.pem.tmp';
17+
$san = 'IP:2001:db8:85a3:8d3:1319:8a2e:370:7348';
18+
19+
$serverCode = <<<'CODE'
20+
$serverUri = "ssl://[::1]:64324";
21+
$serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
22+
$serverCtx = stream_context_create(['ssl' => [
23+
'local_cert' => '%s',
24+
]]);
25+
26+
$server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx);
27+
phpt_notify();
28+
29+
@stream_socket_accept($server, 1);
30+
@stream_socket_accept($server, 1);
31+
CODE;
32+
$serverCode = sprintf($serverCode, $certFile);
33+
34+
$clientCode = <<<'CODE'
35+
$serverUri = "ssl://[::1]:64324";
36+
$clientFlags = STREAM_CLIENT_CONNECT;
37+
$clientCtx = stream_context_create(['ssl' => [
38+
'verify_peer' => false,
39+
]]);
40+
41+
phpt_wait();
42+
43+
stream_context_set_option($clientCtx, 'ssl', 'peer_name', '2001:db8:85a3:8d3:1319:8a2e:370:7348');
44+
var_dump(stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx));
45+
46+
stream_context_set_option($clientCtx, 'ssl', 'peer_name', '2001:db8:85a3:8d3:1319:8a2e:370:7349');
47+
var_dump(stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx));
48+
CODE;
49+
50+
include 'CertificateGenerator.inc';
51+
$certificateGenerator = new CertificateGenerator();
52+
$certificateGenerator->saveNewCertAsFileWithKey(null, $certFile, null, $san);
53+
54+
include 'ServerClientTestCase.inc';
55+
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
56+
?>
57+
--CLEAN--
58+
<?php
59+
@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'san_ipv6_peer_matching.pem.tmp');
60+
?>
61+
--EXPECTF--
62+
resource(%d) of type (stream)
63+
64+
Warning: stream_socket_client(): Unable to locate peer certificate CN in %s on line %d
65+
66+
Warning: stream_socket_client(): Failed to enable crypto in %s on line %d
67+
68+
Warning: stream_socket_client(): Unable to connect to ssl://[::1]:64324 (Unknown error) in %s on line %d
69+
bool(false)

ext/openssl/xp_ssl.c

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#ifdef PHP_WIN32
4040
#include "win32/winutil.h"
4141
#include "win32/time.h"
42+
#include <Ws2tcpip.h>
4243
#include <Wincrypt.h>
4344
/* These are from Wincrypt.h, they conflict with OpenSSL */
4445
#undef X509_NAME
@@ -50,6 +51,10 @@
5051
# define MSG_DONTWAIT 0
5152
#endif
5253

54+
#ifdef HAVE_ARPA_INET_H
55+
#include <arpa/inet.h>
56+
#endif
57+
5358
/* Flags for determining allowed stream crypto methods */
5459
#define STREAM_CRYPTO_IS_CLIENT (1<<0)
5560
#define STREAM_CRYPTO_METHOD_SSLv2 (1<<1)
@@ -125,6 +130,21 @@
125130
#define PHP_X509_NAME_ENTRY_TO_UTF8(ne, i, out) \
126131
ASN1_STRING_to_UTF8(&out, X509_NAME_ENTRY_get_data(X509_NAME_get_entry(ne, i)))
127132

133+
/* Used for IPv6 Address peer verification */
134+
#define EXPAND_IPV6_ADDRESS(_str, _bytes) \
135+
do { \
136+
snprintf(_str, 40, "%X:%X:%X:%X:%X:%X:%X:%X", \
137+
_bytes[0] << 8 | _bytes[1], \
138+
_bytes[2] << 8 | _bytes[3], \
139+
_bytes[4] << 8 | _bytes[5], \
140+
_bytes[6] << 8 | _bytes[7], \
141+
_bytes[8] << 8 | _bytes[9], \
142+
_bytes[10] << 8 | _bytes[11], \
143+
_bytes[12] << 8 | _bytes[13], \
144+
_bytes[14] << 8 | _bytes[15] \
145+
); \
146+
} while(0)
147+
128148
#if PHP_OPENSSL_API_VERSION < 0x10100
129149
static RSA *php_openssl_tmp_rsa_cb(SSL *s, int is_export, int keylength);
130150
#endif
@@ -436,6 +456,18 @@ static bool php_openssl_matches_san_list(X509 *peer, const char *subject_name) /
436456
GENERAL_NAMES *alt_names = X509_get_ext_d2i(peer, NID_subject_alt_name, 0, 0);
437457
int alt_name_count = sk_GENERAL_NAME_num(alt_names);
438458

459+
#if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
460+
/* detect if subject name is an IPv6 address and expand once if required */
461+
char subject_name_ipv6_expanded[40];
462+
unsigned char ipv6[16];
463+
bool subject_name_is_ipv6 = false;
464+
subject_name_ipv6_expanded[0] = 0;
465+
if (inet_pton(AF_INET6, subject_name, &ipv6)) {
466+
EXPAND_IPV6_ADDRESS(subject_name_ipv6_expanded, ipv6);
467+
subject_name_is_ipv6 = true;
468+
}
469+
#endif
470+
439471
for (i = 0; i < alt_name_count; i++) {
440472
GENERAL_NAME *san = sk_GENERAL_NAME_value(alt_names, i);
441473

@@ -474,10 +506,17 @@ static bool php_openssl_matches_san_list(X509 *peer, const char *subject_name) /
474506
return 1;
475507
}
476508
}
477-
/* No, we aren't bothering to check IPv6 addresses. Why?
478-
* Because IP SAN names are officially deprecated and are
479-
* not allowed by CAs starting in 2015. Deal with it.
480-
*/
509+
#if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
510+
else if (san->d.ip->length == 16 && subject_name_is_ipv6) {
511+
ipbuffer[0] = 0;
512+
EXPAND_IPV6_ADDRESS(ipbuffer, san->d.iPAddress->data);
513+
if (strcasecmp((const char*)subject_name_ipv6_expanded, (const char*)ipbuffer) == 0) {
514+
sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
515+
516+
return 1;
517+
}
518+
}
519+
#endif
481520
}
482521
}
483522

0 commit comments

Comments
 (0)