Skip to content

Commit c126c16

Browse files
committed
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.
1 parent e8995c8 commit c126c16

File tree

3 files changed

+122
-68
lines changed

3 files changed

+122
-68
lines changed

NEWS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ PHP NEWS
1515
- Core:
1616
. Allow zero length comparison in substr_compare() (Tjerk)
1717

18+
- Openssl:
19+
. Fixed memory leak in windows cert verification on verify failure.
20+
(Chris Wright)
21+
. Peer certificate capturing via SSL context options now functions even if
22+
peer verification fails. (Daniel Lowrey)
23+
1824
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1925
27 Feb 2014, PHP 5.6.0 Alpha 3
2026

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
capture_peer_cert context captures on verify failure
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded("openssl")) die("skip openssl not loaded");
6+
if (!function_exists("proc_open")) die("skip no proc_open");
7+
--FILE--
8+
<?php
9+
$serverCode = <<<'CODE'
10+
$serverUri = "ssl://127.0.0.1:64321";
11+
$serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
12+
$serverCtx = stream_context_create(['ssl' => [
13+
'local_cert' => __DIR__ . '/bug54992.pem'
14+
]]);
15+
16+
$server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx);
17+
phpt_notify();
18+
19+
@stream_socket_accept($server, 1);
20+
CODE;
21+
22+
$clientCode = <<<'CODE'
23+
$serverUri = "ssl://127.0.0.1:64321";
24+
$clientFlags = STREAM_CLIENT_CONNECT;
25+
$clientCtx = stream_context_create(['ssl' => [
26+
'capture_peer_cert' => true,
27+
'cafile' => __DIR__ . '/bug54992-ca.pem'
28+
]]);
29+
30+
phpt_wait();
31+
$client = @stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx);
32+
$cert = stream_context_get_options($clientCtx)['ssl']['peer_certificate'];
33+
var_dump(openssl_x509_parse($cert)['subject']['CN']);
34+
CODE;
35+
36+
include 'ServerClientTestCase.inc';
37+
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
38+
--EXPECTF--
39+
string(%d) "bug54992.local"

ext/openssl/xp_ssl.c

Lines changed: 77 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -822,12 +822,67 @@ static inline void enable_client_sni(php_stream *stream, php_openssl_netstream_d
822822
/* }}} */
823823
#endif
824824

825+
static int capture_peer_certs(php_stream *stream,
826+
php_openssl_netstream_data_t *sslsock,
827+
X509 *peer_cert
828+
TSRMLS_DC)
829+
{
830+
zval **val, *zcert;
831+
int cert_captured = 0;
832+
833+
if (SUCCESS == php_stream_context_get_option(stream->context,
834+
"ssl", "capture_peer_cert", &val) &&
835+
zend_is_true(*val)
836+
) {
837+
MAKE_STD_ZVAL(zcert);
838+
ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert, php_openssl_get_x509_list_id() TSRMLS_CC));
839+
php_stream_context_set_option(stream->context, "ssl", "peer_certificate", zcert);
840+
cert_captured = 1;
841+
FREE_ZVAL(zcert);
842+
}
843+
844+
if (SUCCESS == php_stream_context_get_option(stream->context,
845+
"ssl", "capture_peer_cert_chain", &val) &&
846+
zend_is_true(*val)
847+
) {
848+
zval *arr;
849+
STACK_OF(X509) *chain;
850+
851+
MAKE_STD_ZVAL(arr);
852+
chain = SSL_get_peer_cert_chain(sslsock->ssl_handle);
853+
854+
if (chain && sk_X509_num(chain) > 0) {
855+
int i;
856+
array_init(arr);
857+
858+
for (i = 0; i < sk_X509_num(chain); i++) {
859+
X509 *mycert = X509_dup(sk_X509_value(chain, i));
860+
MAKE_STD_ZVAL(zcert);
861+
ZVAL_RESOURCE(zcert, zend_list_insert(mycert, php_openssl_get_x509_list_id() TSRMLS_CC));
862+
add_next_index_zval(arr, zcert);
863+
}
864+
865+
} else {
866+
ZVAL_NULL(arr);
867+
}
868+
869+
php_stream_context_set_option(stream->context, "ssl", "peer_certificate_chain", arr);
870+
zval_dtor(arr);
871+
efree(arr);
872+
}
873+
874+
return cert_captured;
875+
}
876+
825877
static inline int php_openssl_enable_crypto(php_stream *stream,
826878
php_openssl_netstream_data_t *sslsock,
827879
php_stream_xport_crypto_param *cparam
828880
TSRMLS_DC)
829881
{
830-
int n, retry = 1;
882+
int n;
883+
int retry = 1;
884+
int cert_captured;
885+
X509 *peer_cert;
831886

832887
if (cparam->inputs.activate && !sslsock->ssl_active) {
833888
struct timeval start_time,
@@ -924,90 +979,43 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
924979
}
925980

926981
if (n == 1) {
927-
X509 *peer_cert;
928-
929982
peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
983+
if (peer_cert && stream->context) {
984+
cert_captured = capture_peer_certs(stream, sslsock, peer_cert TSRMLS_CC);
985+
}
930986

931987
if (FAILURE == php_openssl_apply_verification_policy(sslsock->ssl_handle, peer_cert, stream TSRMLS_CC)) {
932988
SSL_shutdown(sslsock->ssl_handle);
933989
n = -1;
934990
} else {
935991
sslsock->ssl_active = 1;
936992

937-
/* allow the script to capture the peer cert
938-
* and/or the certificate chain */
939993
if (stream->context) {
940-
zval **val, *zcert;
994+
zval **val;
941995

942-
if (SUCCESS == php_stream_context_get_option(
943-
stream->context, "ssl",
944-
"capture_session_meta", &val) &&
945-
zend_is_true(*val)) {
996+
if (SUCCESS == php_stream_context_get_option(stream->context,
997+
"ssl", "capture_session_meta", &val) &&
998+
zend_is_true(*val)
999+
) {
9461000
zval *meta_arr = php_capture_ssl_session_meta(sslsock->ssl_handle);
947-
php_stream_context_set_option(stream->context,
948-
"ssl", "session_meta",
949-
meta_arr);
1001+
php_stream_context_set_option(stream->context, "ssl", "session_meta", meta_arr);
9501002
zval_dtor(meta_arr);
9511003
efree(meta_arr);
9521004
}
953-
954-
if (SUCCESS == php_stream_context_get_option(
955-
stream->context, "ssl",
956-
"capture_peer_cert", &val) &&
957-
zend_is_true(*val)) {
958-
MAKE_STD_ZVAL(zcert);
959-
ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert,
960-
php_openssl_get_x509_list_id() TSRMLS_CC));
961-
php_stream_context_set_option(stream->context,
962-
"ssl", "peer_certificate",
963-
zcert);
964-
peer_cert = NULL;
965-
FREE_ZVAL(zcert);
966-
}
967-
968-
if (SUCCESS == php_stream_context_get_option(
969-
stream->context, "ssl",
970-
"capture_peer_cert_chain", &val) &&
971-
zend_is_true(*val)) {
972-
zval *arr;
973-
STACK_OF(X509) *chain;
974-
975-
MAKE_STD_ZVAL(arr);
976-
chain = SSL_get_peer_cert_chain(
977-
sslsock->ssl_handle);
978-
979-
if (chain && sk_X509_num(chain) > 0) {
980-
int i;
981-
array_init(arr);
982-
983-
for (i = 0; i < sk_X509_num(chain); i++) {
984-
X509 *mycert = X509_dup(
985-
sk_X509_value(chain, i));
986-
MAKE_STD_ZVAL(zcert);
987-
ZVAL_RESOURCE(zcert,
988-
zend_list_insert(mycert,
989-
php_openssl_get_x509_list_id() TSRMLS_CC));
990-
add_next_index_zval(arr, zcert);
991-
}
992-
993-
} else {
994-
ZVAL_NULL(arr);
995-
}
996-
997-
php_stream_context_set_option(stream->context,
998-
"ssl", "peer_certificate_chain",
999-
arr);
1000-
zval_dtor(arr);
1001-
efree(arr);
1002-
}
10031005
}
10041006
}
1005-
1006-
if (peer_cert) {
1007-
X509_free(peer_cert);
1007+
} else if (errno == EAGAIN) {
1008+
n = 0;
1009+
} else {
1010+
n = -1;
1011+
peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
1012+
if (peer_cert && stream->context) {
1013+
cert_captured = capture_peer_certs(stream, sslsock, peer_cert TSRMLS_CC);
10081014
}
1009-
} else {
1010-
n = errno == EAGAIN ? 0 : -1;
1015+
}
1016+
1017+
if (n && peer_cert && cert_captured == 0) {
1018+
X509_free(peer_cert);
10111019
}
10121020

10131021
return n;
@@ -1017,6 +1025,7 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
10171025
SSL_shutdown(sslsock->ssl_handle);
10181026
sslsock->ssl_active = 0;
10191027
}
1028+
10201029
return -1;
10211030
}
10221031

0 commit comments

Comments
 (0)