}
/* }}} */
-static zend_bool php_openssl_match_cn(const char *subjectname, const char *certname)
+static int matches_wildcard_name(const char *subjectname, const char *certname)
{
char *wildcard;
int prefix_len, suffix_len, subject_len;
return 0;
}
+static int matches_san_list(X509 *peer, const char *subject_name)
+{
+ int is_match, i;
+ 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 int matches_common_name(X509 *peer, const char *subject_name)
+{
+ char buf[1024];
+ X509_NAME *cert_name;
+ 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");
+ return 0;
+ } 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);
+ return 0;
+ } else if (matches_wildcard_name(subject_name, buf)) {
+ return 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 0;
+ }
+}
+
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 */
}
}
- 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;
}
}
--- /dev/null
+-----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-----
--- /dev/null
+-----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-----
--- /dev/null
+--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)