com php-src: Capture peer cert even if verify fails: NEWS ext/openssl/tests/capture_peer_cert_001.phpt ex t/openssl/xp_ssl.c
| From: | rdlowrey@php.net | Date: | Sun, 02 Mar 2014 17:35:52 +0000 |
| Subject: | com php-src: Capture peer cert even if verify fails: NEWS ext/openssl/tests/capture_peer_cert_001.phpt ex t/openssl/xp_ssl.c | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-76306@lists.php.net to get a copy of this message | ||
Commit: c126c16479c13e5fcbb57e6fe2341736c37edf36
Author: Daniel Lowrey <rdlowrey@php.net> Sun, 2 Mar 2014 07:03:39 -0700
Parents: e8995c8cb064d36396c4570135815827aa991058
Branches: PHP-5.6
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=c126c16479c13e5fcbb57e6fe2341736c37edf36
Log:
Capture peer cert even if verify fails
Previously the "capture_peer_cert" SSL context option only
captured the peer's certificate if the verification routine
succeeded.
By also capturing the on verify failure applications have the
ability to parse the cert and ask users whether they wish to
proceed given the information presented by the peer.
Changed paths:
M NEWS
A ext/openssl/tests/capture_peer_cert_001.phpt
M ext/openssl/xp_ssl.c
Diff:
diff --git a/NEWS b/NEWS
index 1adb05e..3ba4a1c 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,12 @@ PHP NEWS
- Core:
. Allow zero length comparison in substr_compare() (Tjerk)
+- Openssl:
+ . Fixed memory leak in windows cert verification on verify failure.
+ (Chris Wright)
+ . Peer certificate capturing via SSL context options now functions even if
+ peer verification fails. (Daniel Lowrey)
+
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
27 Feb 2014, PHP 5.6.0 Alpha 3
diff --git a/ext/openssl/tests/capture_peer_cert_001.phpt
b/ext/openssl/tests/capture_peer_cert_001.phpt
new file mode 100644
index 0000000..2cec06a
--- /dev/null
+++ b/ext/openssl/tests/capture_peer_cert_001.phpt
@@ -0,0 +1,39 @@
+--TEST--
+capture_peer_cert context captures on verify failure
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip openssl not loaded");
+if (!function_exists("proc_open")) die("skip no proc_open");
+--FILE--
+<?php
+$serverCode = <<<'CODE'
+ $serverUri = "ssl://127.0.0.1:64321";
+ $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
+ $serverCtx = stream_context_create(['ssl' => [
+ 'local_cert' => __DIR__ . '/bug54992.pem'
+ ]]);
+
+ $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx);
+ phpt_notify();
+
+ @stream_socket_accept($server, 1);
+CODE;
+
+$clientCode = <<<'CODE'
+ $serverUri = "ssl://127.0.0.1:64321";
+ $clientFlags = STREAM_CLIENT_CONNECT;
+ $clientCtx = stream_context_create(['ssl' => [
+ 'capture_peer_cert' => true,
+ 'cafile' => __DIR__ . '/bug54992-ca.pem'
+ ]]);
+
+ phpt_wait();
+ $client = @stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx);
+ $cert = stream_context_get_options($clientCtx)['ssl']['peer_certificate'];
+ var_dump(openssl_x509_parse($cert)['subject']['CN']);
+CODE;
+
+include 'ServerClientTestCase.inc';
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
+--EXPECTF--
+string(%d) "bug54992.local"
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index 7424cd8..d63519e 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -822,12 +822,67 @@ static inline void enable_client_sni(php_stream *stream,
php_openssl_netstream_d
/* }}} */
#endif
+static int capture_peer_certs(php_stream *stream,
+ php_openssl_netstream_data_t *sslsock,
+ X509 *peer_cert
+ TSRMLS_DC)
+{
+ zval **val, *zcert;
+ int cert_captured = 0;
+
+ if (SUCCESS == php_stream_context_get_option(stream->context,
+ "ssl", "capture_peer_cert", &val) &&
+ zend_is_true(*val)
+ ) {
+ MAKE_STD_ZVAL(zcert);
+ ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert, php_openssl_get_x509_list_id() TSRMLS_CC));
+ php_stream_context_set_option(stream->context, "ssl", "peer_certificate",
zcert);
+ cert_captured = 1;
+ FREE_ZVAL(zcert);
+ }
+
+ if (SUCCESS == php_stream_context_get_option(stream->context,
+ "ssl", "capture_peer_cert_chain", &val) &&
+ zend_is_true(*val)
+ ) {
+ zval *arr;
+ STACK_OF(X509) *chain;
+
+ MAKE_STD_ZVAL(arr);
+ chain = SSL_get_peer_cert_chain(sslsock->ssl_handle);
+
+ if (chain && sk_X509_num(chain) > 0) {
+ int i;
+ array_init(arr);
+
+ for (i = 0; i < sk_X509_num(chain); i++) {
+ X509 *mycert = X509_dup(sk_X509_value(chain, i));
+ MAKE_STD_ZVAL(zcert);
+ ZVAL_RESOURCE(zcert, zend_list_insert(mycert, php_openssl_get_x509_list_id() TSRMLS_CC));
+ add_next_index_zval(arr, zcert);
+ }
+
+ } else {
+ ZVAL_NULL(arr);
+ }
+
+ php_stream_context_set_option(stream->context, "ssl",
"peer_certificate_chain", arr);
+ zval_dtor(arr);
+ efree(arr);
+ }
+
+ return cert_captured;
+}
+
static inline int php_openssl_enable_crypto(php_stream *stream,
php_openssl_netstream_data_t *sslsock,
php_stream_xport_crypto_param *cparam
TSRMLS_DC)
{
- int n, retry = 1;
+ int n;
+ int retry = 1;
+ int cert_captured;
+ X509 *peer_cert;
if (cparam->inputs.activate && !sslsock->ssl_active) {
struct timeval start_time,
@@ -924,9 +979,10 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
}
if (n == 1) {
- X509 *peer_cert;
-
peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
+ if (peer_cert && stream->context) {
+ cert_captured = capture_peer_certs(stream, sslsock, peer_cert TSRMLS_CC);
+ }
if (FAILURE == php_openssl_apply_verification_policy(sslsock->ssl_handle, peer_cert, stream
TSRMLS_CC)) {
SSL_shutdown(sslsock->ssl_handle);
@@ -934,80 +990,32 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
} else {
sslsock->ssl_active = 1;
- /* allow the script to capture the peer cert
- * and/or the certificate chain */
if (stream->context) {
- zval **val, *zcert;
+ zval **val;
- if (SUCCESS == php_stream_context_get_option(
- stream->context, "ssl",
- "capture_session_meta", &val) &&
- zend_is_true(*val)) {
+ if (SUCCESS == php_stream_context_get_option(stream->context,
+ "ssl", "capture_session_meta", &val) &&
+ zend_is_true(*val)
+ ) {
zval *meta_arr = php_capture_ssl_session_meta(sslsock->ssl_handle);
- php_stream_context_set_option(stream->context,
- "ssl", "session_meta",
- meta_arr);
+ php_stream_context_set_option(stream->context, "ssl", "session_meta",
meta_arr);
zval_dtor(meta_arr);
efree(meta_arr);
}
-
- if (SUCCESS == php_stream_context_get_option(
- stream->context, "ssl",
- "capture_peer_cert", &val) &&
- zend_is_true(*val)) {
- MAKE_STD_ZVAL(zcert);
- ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert,
- php_openssl_get_x509_list_id() TSRMLS_CC));
- php_stream_context_set_option(stream->context,
- "ssl", "peer_certificate",
- zcert);
- peer_cert = NULL;
- FREE_ZVAL(zcert);
- }
-
- if (SUCCESS == php_stream_context_get_option(
- stream->context, "ssl",
- "capture_peer_cert_chain", &val) &&
- zend_is_true(*val)) {
- zval *arr;
- STACK_OF(X509) *chain;
-
- MAKE_STD_ZVAL(arr);
- chain = SSL_get_peer_cert_chain(
- sslsock->ssl_handle);
-
- if (chain && sk_X509_num(chain) > 0) {
- int i;
- array_init(arr);
-
- for (i = 0; i < sk_X509_num(chain); i++) {
- X509 *mycert = X509_dup(
- sk_X509_value(chain, i));
- MAKE_STD_ZVAL(zcert);
- ZVAL_RESOURCE(zcert,
- zend_list_insert(mycert,
- php_openssl_get_x509_list_id() TSRMLS_CC));
- add_next_index_zval(arr, zcert);
- }
-
- } else {
- ZVAL_NULL(arr);
- }
-
- php_stream_context_set_option(stream->context,
- "ssl", "peer_certificate_chain",
- arr);
- zval_dtor(arr);
- efree(arr);
- }
}
}
-
- if (peer_cert) {
- X509_free(peer_cert);
+ } else if (errno == EAGAIN) {
+ n = 0;
+ } else {
+ n = -1;
+ peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
+ if (peer_cert && stream->context) {
+ cert_captured = capture_peer_certs(stream, sslsock, peer_cert TSRMLS_CC);
}
- } else {
- n = errno == EAGAIN ? 0 : -1;
+ }
+
+ if (n && peer_cert && cert_captured == 0) {
+ X509_free(peer_cert);
}
return n;
@@ -1017,6 +1025,7 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
SSL_shutdown(sslsock->ssl_handle);
sslsock->ssl_active = 0;
}
+
return -1;
}