Skip to content

Commit 1970b96

Browse files
committed
Added SAN matching during peer verification
1 parent cf6413a commit 1970b96

File tree

4 files changed

+158
-19
lines changed

4 files changed

+158
-19
lines changed

ext/openssl/openssl.c

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4951,7 +4951,7 @@ static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) /* {{{ */
49514951
}
49524952
/* }}} */
49534953

4954-
static zend_bool php_openssl_match_cn(const char *subjectname, const char *certname)
4954+
static int matches_wildcard_name(const char *subjectname, const char *certname)
49554955
{
49564956
char *wildcard;
49574957
int prefix_len, suffix_len, subject_len;
@@ -4983,12 +4983,55 @@ static zend_bool php_openssl_match_cn(const char *subjectname, const char *certn
49834983
return 0;
49844984
}
49854985

4986+
static int matches_san_list(X509 *peer, const char *subject_name)
4987+
{
4988+
int is_match, i;
4989+
unsigned char *cert_name;
4990+
GENERAL_NAMES *alt_names = X509_get_ext_d2i(peer, NID_subject_alt_name, 0, 0);
4991+
int alt_name_count = sk_GENERAL_NAME_num(alt_names);
4992+
4993+
for (i = 0; i < alt_name_count; i++) {
4994+
GENERAL_NAME *san = sk_GENERAL_NAME_value(alt_names, i);
4995+
4996+
if (GEN_DNS == san->type) {
4997+
ASN1_STRING_to_UTF8(&cert_name, san->d.dNSName);
4998+
is_match = matches_wildcard_name(subject_name, cert_name);
4999+
OPENSSL_free(cert_name);
5000+
}
5001+
5002+
if (is_match) {
5003+
break;
5004+
}
5005+
}
5006+
5007+
return is_match;
5008+
}
5009+
5010+
static int matches_common_name(X509 *peer, const char *subject_name)
5011+
{
5012+
char buf[1024];
5013+
X509_NAME *cert_name;
5014+
cert_name = X509_get_subject_name(peer);
5015+
int cert_name_len = X509_NAME_get_text_by_NID(cert_name, NID_commonName, buf, sizeof(buf));
5016+
5017+
if (cert_name_len == -1) {
5018+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to locate peer certificate CN");
5019+
return 0;
5020+
} else if (cert_name_len != strlen(buf)) {
5021+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' is malformed", cert_name_len, buf);
5022+
return 0;
5023+
} else if (matches_wildcard_name(subject_name, buf)) {
5024+
return 1;
5025+
} else {
5026+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' did not match expected CN=`%s'", cert_name_len, buf, subject_name);
5027+
return 0;
5028+
}
5029+
}
5030+
49865031
int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stream TSRMLS_DC) /* {{{ */
49875032
{
49885033
zval **val = NULL;
49895034
char *cnmatch = NULL;
4990-
X509_NAME *name;
4991-
char buf[1024];
49925035
int err;
49935036

49945037
/* verification is turned off */
@@ -5030,24 +5073,14 @@ int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stre
50305073
}
50315074
}
50325075

5033-
name = X509_get_subject_name(peer);
5034-
5035-
/* Does the common name match ? (used primarily for https://) */
50365076
GET_VER_OPT_STRING("CN_match", cnmatch);
5037-
if (cnmatch) {
5038-
int name_len = X509_NAME_get_text_by_NID(name, NID_commonName, buf, sizeof(buf));
50395077

5040-
if (name_len == -1) {
5041-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to locate peer certificate CN");
5042-
return FAILURE;
5043-
} else if (name_len != strlen(buf)) {
5044-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' is malformed", name_len, buf);
5045-
return FAILURE;
5046-
}
5047-
5048-
if (!php_openssl_match_cn(cnmatch, buf)) {
5049-
/* didn't match */
5050-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' did not match expected CN=`%s'", name_len, buf, cnmatch);
5078+
if (cnmatch) {
5079+
if (matches_san_list(peer, cnmatch)) {
5080+
return SUCCESS;
5081+
} else if (matches_common_name(peer, cnmatch)) {
5082+
return SUCCESS;
5083+
} else {
50515084
return FAILURE;
50525085
}
50535086
}

ext/openssl/tests/san-ca.pem

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICYTCCAcqgAwIBAgIJAIaqxtY5dwjtMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV
3+
BAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UEBxMLTWlubmVhcG9saXMxITAfBgNV
4+
BAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRlZDAeFw0xMzA5MjQwODA1NTFaFw0y
5+
MTEyMTEwODA1NTFaMFMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UE
6+
BxMLTWlubmVhcG9saXMxITAfBgNVBAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRl
7+
ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsFGqfbU/8D+KjroQl4XMyt9m
8+
dcSP7iZtqphOu9nVZxYAAqfaqj8FnC/pwYV3TU6ZHndLTQAllwYT3sQBQPPGmZQ9
9+
clSIMEL003t3pi4ZVXkttG6Vvr+Z9PBcHhlKLQ7WMHnn4qctllWXTSoyTQpkETF3
10+
Fc3mrG5G37BhoUno7NECAwEAAaM9MDswOQYDVR0RBDIwMIILZXhhbXBsZS5vcmeC
11+
D3d3dy5leGFtcGxlLm9yZ4IQdGVzdC5leGFtcGxlLm9yZzANBgkqhkiG9w0BAQUF
12+
AAOBgQBf/FZhzheIcQJ+dyTk8xQ/nJLvpmBhbd1LNtfwk/MsC9UHsz4QXs9sBw1k
13+
rH0FjoqgM6avj7zKHJFTj6q7Rd+OX5V4HynYPhX67sWbN3KWEHffL98nGGd/bo3X
14+
pSjNk5vnyKYiwdUUe11Ac9csh0HcSBbhOYjy0T/i9AlQcKbuCg==
15+
-----END CERTIFICATE-----

ext/openssl/tests/san-cert.pem

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICYTCCAcqgAwIBAgIJAIaqxtY5dwjtMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV
3+
BAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UEBxMLTWlubmVhcG9saXMxITAfBgNV
4+
BAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRlZDAeFw0xMzA5MjQwODA1NTFaFw0y
5+
MTEyMTEwODA1NTFaMFMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UE
6+
BxMLTWlubmVhcG9saXMxITAfBgNVBAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRl
7+
ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsFGqfbU/8D+KjroQl4XMyt9m
8+
dcSP7iZtqphOu9nVZxYAAqfaqj8FnC/pwYV3TU6ZHndLTQAllwYT3sQBQPPGmZQ9
9+
clSIMEL003t3pi4ZVXkttG6Vvr+Z9PBcHhlKLQ7WMHnn4qctllWXTSoyTQpkETF3
10+
Fc3mrG5G37BhoUno7NECAwEAAaM9MDswOQYDVR0RBDIwMIILZXhhbXBsZS5vcmeC
11+
D3d3dy5leGFtcGxlLm9yZ4IQdGVzdC5leGFtcGxlLm9yZzANBgkqhkiG9w0BAQUF
12+
AAOBgQBf/FZhzheIcQJ+dyTk8xQ/nJLvpmBhbd1LNtfwk/MsC9UHsz4QXs9sBw1k
13+
rH0FjoqgM6avj7zKHJFTj6q7Rd+OX5V4HynYPhX67sWbN3KWEHffL98nGGd/bo3X
14+
pSjNk5vnyKYiwdUUe11Ac9csh0HcSBbhOYjy0T/i9AlQcKbuCg==
15+
-----END CERTIFICATE-----
16+
-----BEGIN PRIVATE KEY-----
17+
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALBRqn21P/A/io66
18+
EJeFzMrfZnXEj+4mbaqYTrvZ1WcWAAKn2qo/BZwv6cGFd01OmR53S00AJZcGE97E
19+
AUDzxpmUPXJUiDBC9NN7d6YuGVV5LbRulb6/mfTwXB4ZSi0O1jB55+KnLZZVl00q
20+
Mk0KZBExdxXN5qxuRt+wYaFJ6OzRAgMBAAECgYB11e5iWvqjPmQEZRdnnJU0VD8u
21+
n7ItT+Nk6qtb4gY8Abj6DWIW+01th5vqqJ8FvGyartFVYa69kuM+srG/zevAZWeu
22+
fGZtwiwZR4DRSyRcPp4rnNiksK3dkAZA6UewmRDPv8uyHJlXc5i+Ft1ILJ5Q5jgn
23+
UkC4z3EJP5Se9KZywQJBAOO4lRq42wLsYr2SDrQDSs4leie3FKc2bgvjF7Djosh1
24+
ZYbf55F5b9w1zgnccmni2HkqOnyFu4SKarmXyCsYxrkCQQDGNvnUh7/zZswrdWZ/
25+
PMp9zVDTh/5Oc2B4ByNLw1ERDwYhjchKgPRlQvn4cp3Pwf3UYPQ/8XGXzzEJey3A
26+
r0rZAkBf/tDEOgcBPXsGZQrTscuYCU5sbY5ESvqrAilbhSp7DJom+D5bIfEYyIm5
27+
uHd20Yzlzvpmwc1huyPwZt6X5FLpAkATDReoGMAXSesXxjnqwtIHk2NQYYLM0YQV
28+
JUJ8NrKk/Bevw+vbVVeoH+7ctU97t36JGiR/vNoZKD3jVmaIXZDJAkEA4wJbwzIo
29+
L32mu9VmZa7wjmfkraQEmXTPaA5D9lNC0AwRTgkj+x2Qe1vawNblNK9PPLBDdplQ
30+
L//53ADq/wv5rA==
31+
-----END PRIVATE KEY-----
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
--TEST--
2+
Peer verification matches SAN names
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded("openssl")) die("skip");
6+
if (!function_exists('pcntl_fork')) die("skip no fork");
7+
--FILE--
8+
<?php
9+
$context = stream_context_create(array(
10+
'ssl' => array(
11+
'local_cert' => __DIR__ . '/san-cert.pem',
12+
'allow_self_signed' => true,
13+
),
14+
));
15+
16+
$server = stream_socket_server('ssl://127.0.0.1:64321', $errno, $errstr,
17+
STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context);
18+
19+
20+
$pid = pcntl_fork();
21+
if ($pid == -1) {
22+
die('could not fork');
23+
} else if ($pid) {
24+
$contextC = stream_context_create(
25+
array(
26+
'ssl' => array(
27+
'verify_peer' => true,
28+
'cafile' => __DIR__ . '/san-ca.pem',
29+
'CN_match' => 'example.org',
30+
)
31+
)
32+
);
33+
var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1,
34+
STREAM_CLIENT_CONNECT, $contextC));
35+
36+
$contextC = stream_context_create(array(
37+
'ssl' => array(
38+
'verify_peer' => true,
39+
'cafile' => __DIR__ . '/san-ca.pem',
40+
'CN_match' => 'moar.example.org',
41+
)
42+
));
43+
44+
var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1,
45+
STREAM_CLIENT_CONNECT, $contextC));
46+
47+
} else {
48+
@pcntl_wait($status);
49+
@stream_socket_accept($server, 1);
50+
@stream_socket_accept($server, 1);
51+
}
52+
--EXPECTF--
53+
resource(%d) of type (stream)
54+
55+
Warning: stream_socket_client(): Unable to locate peer certificate CN in %s on line %d
56+
57+
Warning: stream_socket_client(): Failed to enable crypto in %s on line %d
58+
59+
Warning: stream_socket_client(): unable to connect to ssl://127.0.0.1:64321 (Unknown error) in %s on line %d
60+
bool(false)

0 commit comments

Comments
 (0)