]> granicus.if.org Git - php/commitdiff
Capture peer cert even if verify fails
authorDaniel Lowrey <rdlowrey@php.net>
Sun, 2 Mar 2014 14:03:39 +0000 (07:03 -0700)
committerDaniel Lowrey <rdlowrey@php.net>
Sun, 2 Mar 2014 17:35:52 +0000 (10:35 -0700)
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.

NEWS
ext/openssl/tests/capture_peer_cert_001.phpt [new file with mode: 0644]
ext/openssl/xp_ssl.c

diff --git a/NEWS b/NEWS
index 1adb05e6851ead13c6337b9a046b7ba3b4ba3422..3ba4a1cefa6e3aa7da3ba1257e3a5dac119dea3e 100644 (file)
--- 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 (file)
index 0000000..2cec06a
--- /dev/null
@@ -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"
index 7424cd8de2af6851eabd53fcad8db30ad0722a65..d63519e949dcbc26a3fdba20a230ba607f1c1de4 100644 (file)
@@ -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;
 }