Merge branch 'san_peer_matching' of https://github.com/rdlowrey/php-src

* 'san_peer_matching' of https://github.com/rdlowrey/php-src:
  Changed return types to zend_bool, renamed test
  Added SAN matching during peer verification
This commit is contained in:
Michael Wallner 2013-10-09 17:09:03 +02:00
commit c85c50e35c
4 changed files with 161 additions and 19 deletions

View File

@ -4951,7 +4951,7 @@ static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) /* {{{ */
}
/* }}} */
static zend_bool php_openssl_match_cn(const char *subjectname, const char *certname)
static zend_bool matches_wildcard_name(const char *subjectname, const char *certname)
{
char *wildcard;
int prefix_len, suffix_len, subject_len;
@ -4983,12 +4983,58 @@ static zend_bool php_openssl_match_cn(const char *subjectname, const char *certn
return 0;
}
static zend_bool matches_san_list(X509 *peer, const char *subject_name)
{
int i;
zend_bool is_match = 0;
unsigned char *cert_name;
GENERAL_NAMES *alt_names = X509_get_ext_d2i(peer, NID_subject_alt_name, 0, 0);
int alt_name_count = sk_GENERAL_NAME_num(alt_names);
for (i = 0; i < alt_name_count; i++) {
GENERAL_NAME *san = sk_GENERAL_NAME_value(alt_names, i);
if (GEN_DNS == san->type) {
ASN1_STRING_to_UTF8(&cert_name, san->d.dNSName);
is_match = matches_wildcard_name(subject_name, cert_name);
OPENSSL_free(cert_name);
}
if (is_match) {
break;
}
}
return is_match;
}
static zend_bool matches_common_name(X509 *peer, const char *subject_name)
{
char buf[1024];
X509_NAME *cert_name;
zend_bool is_match = 0;
cert_name = X509_get_subject_name(peer);
int cert_name_len = X509_NAME_get_text_by_NID(cert_name, NID_commonName, buf, sizeof(buf));
if (cert_name_len == -1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to locate peer certificate CN");
} else if (cert_name_len != strlen(buf)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' is malformed", cert_name_len, buf);
} else if (matches_wildcard_name(subject_name, buf)) {
is_match = 1;
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' did not match expected CN=`%s'", cert_name_len, buf, subject_name);
}
return is_match;
}
int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stream TSRMLS_DC) /* {{{ */
{
zval **val = NULL;
char *cnmatch = NULL;
X509_NAME *name;
char buf[1024];
int err;
/* verification is turned off */
@ -5030,24 +5076,14 @@ int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stre
}
}
name = X509_get_subject_name(peer);
/* Does the common name match ? (used primarily for https://) */
GET_VER_OPT_STRING("CN_match", cnmatch);
if (cnmatch) {
int name_len = X509_NAME_get_text_by_NID(name, NID_commonName, buf, sizeof(buf));
if (name_len == -1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to locate peer certificate CN");
return FAILURE;
} else if (name_len != strlen(buf)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' is malformed", name_len, buf);
return FAILURE;
}
if (!php_openssl_match_cn(cnmatch, buf)) {
/* didn't match */
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer certificate CN=`%.*s' did not match expected CN=`%s'", name_len, buf, cnmatch);
if (matches_san_list(peer, cnmatch)) {
return SUCCESS;
} else if (matches_common_name(peer, cnmatch)) {
return SUCCESS;
} else {
return FAILURE;
}
}

View File

@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICYTCCAcqgAwIBAgIJAIaqxtY5dwjtMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV
BAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UEBxMLTWlubmVhcG9saXMxITAfBgNV
BAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRlZDAeFw0xMzA5MjQwODA1NTFaFw0y
MTEyMTEwODA1NTFaMFMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UE
BxMLTWlubmVhcG9saXMxITAfBgNVBAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRl
ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsFGqfbU/8D+KjroQl4XMyt9m
dcSP7iZtqphOu9nVZxYAAqfaqj8FnC/pwYV3TU6ZHndLTQAllwYT3sQBQPPGmZQ9
clSIMEL003t3pi4ZVXkttG6Vvr+Z9PBcHhlKLQ7WMHnn4qctllWXTSoyTQpkETF3
Fc3mrG5G37BhoUno7NECAwEAAaM9MDswOQYDVR0RBDIwMIILZXhhbXBsZS5vcmeC
D3d3dy5leGFtcGxlLm9yZ4IQdGVzdC5leGFtcGxlLm9yZzANBgkqhkiG9w0BAQUF
AAOBgQBf/FZhzheIcQJ+dyTk8xQ/nJLvpmBhbd1LNtfwk/MsC9UHsz4QXs9sBw1k
rH0FjoqgM6avj7zKHJFTj6q7Rd+OX5V4HynYPhX67sWbN3KWEHffL98nGGd/bo3X
pSjNk5vnyKYiwdUUe11Ac9csh0HcSBbhOYjy0T/i9AlQcKbuCg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIICYTCCAcqgAwIBAgIJAIaqxtY5dwjtMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV
BAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UEBxMLTWlubmVhcG9saXMxITAfBgNV
BAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRlZDAeFw0xMzA5MjQwODA1NTFaFw0y
MTEyMTEwODA1NTFaMFMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UE
BxMLTWlubmVhcG9saXMxITAfBgNVBAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRl
ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsFGqfbU/8D+KjroQl4XMyt9m
dcSP7iZtqphOu9nVZxYAAqfaqj8FnC/pwYV3TU6ZHndLTQAllwYT3sQBQPPGmZQ9
clSIMEL003t3pi4ZVXkttG6Vvr+Z9PBcHhlKLQ7WMHnn4qctllWXTSoyTQpkETF3
Fc3mrG5G37BhoUno7NECAwEAAaM9MDswOQYDVR0RBDIwMIILZXhhbXBsZS5vcmeC
D3d3dy5leGFtcGxlLm9yZ4IQdGVzdC5leGFtcGxlLm9yZzANBgkqhkiG9w0BAQUF
AAOBgQBf/FZhzheIcQJ+dyTk8xQ/nJLvpmBhbd1LNtfwk/MsC9UHsz4QXs9sBw1k
rH0FjoqgM6avj7zKHJFTj6q7Rd+OX5V4HynYPhX67sWbN3KWEHffL98nGGd/bo3X
pSjNk5vnyKYiwdUUe11Ac9csh0HcSBbhOYjy0T/i9AlQcKbuCg==
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALBRqn21P/A/io66
EJeFzMrfZnXEj+4mbaqYTrvZ1WcWAAKn2qo/BZwv6cGFd01OmR53S00AJZcGE97E
AUDzxpmUPXJUiDBC9NN7d6YuGVV5LbRulb6/mfTwXB4ZSi0O1jB55+KnLZZVl00q
Mk0KZBExdxXN5qxuRt+wYaFJ6OzRAgMBAAECgYB11e5iWvqjPmQEZRdnnJU0VD8u
n7ItT+Nk6qtb4gY8Abj6DWIW+01th5vqqJ8FvGyartFVYa69kuM+srG/zevAZWeu
fGZtwiwZR4DRSyRcPp4rnNiksK3dkAZA6UewmRDPv8uyHJlXc5i+Ft1ILJ5Q5jgn
UkC4z3EJP5Se9KZywQJBAOO4lRq42wLsYr2SDrQDSs4leie3FKc2bgvjF7Djosh1
ZYbf55F5b9w1zgnccmni2HkqOnyFu4SKarmXyCsYxrkCQQDGNvnUh7/zZswrdWZ/
PMp9zVDTh/5Oc2B4ByNLw1ERDwYhjchKgPRlQvn4cp3Pwf3UYPQ/8XGXzzEJey3A
r0rZAkBf/tDEOgcBPXsGZQrTscuYCU5sbY5ESvqrAilbhSp7DJom+D5bIfEYyIm5
uHd20Yzlzvpmwc1huyPwZt6X5FLpAkATDReoGMAXSesXxjnqwtIHk2NQYYLM0YQV
JUJ8NrKk/Bevw+vbVVeoH+7ctU97t36JGiR/vNoZKD3jVmaIXZDJAkEA4wJbwzIo
L32mu9VmZa7wjmfkraQEmXTPaA5D9lNC0AwRTgkj+x2Qe1vawNblNK9PPLBDdplQ
L//53ADq/wv5rA==
-----END PRIVATE KEY-----

View File

@ -0,0 +1,60 @@
--TEST--
Peer verification matches SAN names
--SKIPIF--
<?php
if (!extension_loaded("openssl")) die("skip");
if (!function_exists('pcntl_fork')) die("skip no fork");
--FILE--
<?php
$context = stream_context_create(array(
'ssl' => array(
'local_cert' => __DIR__ . '/san-cert.pem',
'allow_self_signed' => true,
),
));
$server = stream_socket_server('ssl://127.0.0.1:64321', $errno, $errstr,
STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context);
$pid = pcntl_fork();
if ($pid == -1) {
die('could not fork');
} else if ($pid) {
$contextC = stream_context_create(
array(
'ssl' => array(
'verify_peer' => true,
'cafile' => __DIR__ . '/san-ca.pem',
'CN_match' => 'example.org',
)
)
);
var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1,
STREAM_CLIENT_CONNECT, $contextC));
$contextC = stream_context_create(array(
'ssl' => array(
'verify_peer' => true,
'cafile' => __DIR__ . '/san-ca.pem',
'CN_match' => 'moar.example.org',
)
));
var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1,
STREAM_CLIENT_CONNECT, $contextC));
} else {
@pcntl_wait($status);
@stream_socket_accept($server, 1);
@stream_socket_accept($server, 1);
}
--EXPECTF--
resource(%d) of type (stream)
Warning: stream_socket_client(): Unable to locate peer certificate CN in %s on line %d
Warning: stream_socket_client(): Failed to enable crypto in %s on line %d
Warning: stream_socket_client(): unable to connect to ssl://127.0.0.1:64321 (Unknown error) in %s on line %d
bool(false)