Skip to content

Utilize SAN extension matching during SSL/TLS peer verification #482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 9, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 55 additions & 19 deletions ext/openssl/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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 (cnmatch) {
if (matches_san_list(peer, cnmatch)) {
return SUCCESS;
} else if (matches_common_name(peer, cnmatch)) {
return SUCCESS;
} else {
return FAILURE;
}
}
Expand Down
15 changes: 15 additions & 0 deletions ext/openssl/tests/san-ca.pem
Original file line number Diff line number Diff line change
@@ -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-----
31 changes: 31 additions & 0 deletions ext/openssl/tests/san-cert.pem
Original file line number Diff line number Diff line change
@@ -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-----
60 changes: 60 additions & 0 deletions ext/openssl/tests/san_peer_matching.phpt
Original file line number Diff line number Diff line change
@@ -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)