From: Andrey Hristov Date: Tue, 27 Oct 2015 11:59:09 +0000 (+0100) Subject: Another Fix for Bug #68344 MySQLi does not provide way to disable peer certificate... X-Git-Tag: php-7.0.1RC1~194^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6d51b7b2e3468601acdaaf9041c9131b5aa47f98;p=php Another Fix for Bug #68344 MySQLi does not provide way to disable peer certificate validation Added the possibility to explicitly state that the peer certificate should not be checked. Back to the default - checking the certificate. Exported MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT Usage : mysqli_real_connect( , , , , , MYSQLI_CLIENT_SSL | MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT) If mysqli_ssl_set() is not called, but only MYSQLI_CLIENT_SSL is passed, without the (don't) very flag, then no verification takes place. --- diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 198ed83116..5e40d19130 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -717,6 +717,9 @@ PHP_MINIT_FUNCTION(mysqli) REGISTER_LONG_CONSTANT("MYSQLI_CLIENT_FOUND_ROWS", CLIENT_FOUND_ROWS, CONST_CS | CONST_PERSISTENT); #ifdef CLIENT_SSL_VERIFY_SERVER_CERT REGISTER_LONG_CONSTANT("MYSQLI_CLIENT_SSL_VERIFY_SERVER_CERT", CLIENT_SSL_VERIFY_SERVER_CERT, CONST_CS | CONST_PERSISTENT); +#if defined(MYSQLI_USE_MYSQLND) + REGISTER_LONG_CONSTANT("MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT", CLIENT_SSL_DONT_VERIFY_SERVER_CERT, CONST_CS | CONST_PERSISTENT); +#endif #endif #if (MYSQL_VERSION_ID >= 50611 && defined(CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS)) || defined(MYSQLI_USE_MYSQLND) REGISTER_LONG_CONSTANT("MYSQLI_CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS", CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS, CONST_CS | CONST_PERSISTENT); diff --git a/ext/mysqli/tests/bug51647.phpt b/ext/mysqli/tests/bug51647.phpt index 349d6dbbb0..7385538fbb 100644 --- a/ext/mysqli/tests/bug51647.phpt +++ b/ext/mysqli/tests/bug51647.phpt @@ -41,11 +41,7 @@ $link->close(); if (!is_object($link = mysqli_init())) printf("[001] Cannot create link\n"); - $path_to_pems = !$IS_MYSQLND? "ext/mysqli/tests/" : ""; - if (!$link->ssl_set("{$path_to_pems}client-key.pem", "{$path_to_pems}client-cert.pem", "{$path_to_pems}cacert.pem","","")) - printf("[002] [%d] %s\n", $link->errno, $link->error); - - if (!my_mysqli_real_connect($link, $host, $user, $passwd, $db, $port, $socket)) { + if (!my_mysqli_real_connect($link, $host, $user, $passwd, $db, $port, $socket, MYSQLI_CLIENT_SSL | MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT)) { printf("[003] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()); } @@ -67,9 +63,9 @@ $link->close(); printf("[006] [%d] %s\n", $link->errno, $link->error); if (!strlen($row["Value"])) printf("[007] Empty cipher. No encrytion!"); + var_dump($row); } - var_dump($row); $link->close(); if (!is_object($link = mysqli_init())) @@ -97,10 +93,9 @@ $link->close(); printf("[012] [%d] %s\n", $link->errno, $link->error); if (!strlen($row["Value"])) printf("[013] Empty cipher. No encrytion!"); + var_dump($row); } - var_dump($row); - $link->close(); print "done!"; diff --git a/ext/mysqli/tests/bug55283.phpt b/ext/mysqli/tests/bug55283.phpt index d03daaee88..a10c604fdd 100644 --- a/ext/mysqli/tests/bug55283.phpt +++ b/ext/mysqli/tests/bug55283.phpt @@ -40,7 +40,7 @@ $link->close(); $db1 = new mysqli(); - $flags = MYSQLI_CLIENT_SSL; + $flags = MYSQLI_CLIENT_SSL | MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT; $link = mysqli_init(); mysqli_ssl_set($link, null, null, null, null, "RC4-MD5"); diff --git a/ext/mysqli/tests/connect.inc b/ext/mysqli/tests/connect.inc index 67ce60a48b..606d1d32eb 100644 --- a/ext/mysqli/tests/connect.inc +++ b/ext/mysqli/tests/connect.inc @@ -9,7 +9,7 @@ $driver = new mysqli_driver; $host = getenv("MYSQL_TEST_HOST") ? getenv("MYSQL_TEST_HOST") : "127.0.0.1"; - $port = getenv("MYSQL_TEST_PORT") ? getenv("MYSQL_TEST_PORT") : 3308; + $port = getenv("MYSQL_TEST_PORT") ? getenv("MYSQL_TEST_PORT") : 3306; $user = getenv("MYSQL_TEST_USER") ? getenv("MYSQL_TEST_USER") : "root"; $passwd = getenv("MYSQL_TEST_PASSWD") ? getenv("MYSQL_TEST_PASSWD") : ""; $db = getenv("MYSQL_TEST_DB") ? getenv("MYSQL_TEST_DB") : "test"; @@ -87,9 +87,8 @@ function my_mysqli_connect($host, $user, $passwd, $db, $port, $socket, $enable_env_flags = true) { global $connect_flags; - $flags = ($enable_env_flags) ? $connect_flags : false; - - if ($flags !== false) { + $flags = $enable_env_flags? $connect_flags:0; + if ($flags !== 0) { $link = mysqli_init(); if (!mysqli_real_connect($link, $host, $user, $passwd, $db, $port, $socket, $flags)) $link = false; @@ -109,7 +108,7 @@ global $connect_flags; if ($enable_env_flags) - $flags & $connect_flags; + $flags = $flags | $connect_flags; return mysqli_real_connect($link, $host, $user, $passwd, $db, $port, $socket, $flags); } @@ -118,7 +117,7 @@ public function __construct($host, $user, $passwd, $db, $port, $socket, $enable_env_flags = true) { global $connect_flags; - $flags = ($enable_env_flags) ? $connect_flags : false; + $flags = ($enable_env_flags) ? $connect_flags : 0; if ($flags !== false) { parent::init(); diff --git a/ext/mysqli/tests/mysqli_constants.phpt b/ext/mysqli/tests/mysqli_constants.phpt index 1cb31cc2a7..cc5fa9f1c4 100644 --- a/ext/mysqli/tests/mysqli_constants.phpt +++ b/ext/mysqli/tests/mysqli_constants.phpt @@ -139,6 +139,9 @@ require_once('skipifconnectfailure.inc'); if ($version >= 50033 || $IS_MYSQLND) { $expected_constants['MYSQLI_CLIENT_SSL_VERIFY_SERVER_CERT'] = true; } + if ($IS_MYSQLND) { + $expected_constants['MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT'] = true; + } /* First introduced in MySQL 6.0, backported to MySQL 5.5 */ if ($version >= 50606 || $IS_MYSQLND) { diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index f008986227..94a314964d 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -472,6 +472,7 @@ mysqlnd_switch_to_ssl_if_needed( DBG_INF_FMT("CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA= %d", mysql_flags & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA? 1:0); DBG_INF_FMT("CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS= %d", mysql_flags & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS? 1:0); DBG_INF_FMT("CLIENT_SESSION_TRACK= %d", mysql_flags & CLIENT_SESSION_TRACK? 1:0); + DBG_INF_FMT("CLIENT_SSL_DONT_VERIFY_SERVER_CERT= %d", mysql_flags & CLIENT_SSL_DONT_VERIFY_SERVER_CERT? 1:0); DBG_INF_FMT("CLIENT_SSL_VERIFY_SERVER_CERT= %d", mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? 1:0); DBG_INF_FMT("CLIENT_REMEMBER_OPTIONS= %d", mysql_flags & CLIENT_REMEMBER_OPTIONS? 1:0); @@ -495,7 +496,11 @@ mysqlnd_switch_to_ssl_if_needed( if (server_has_ssl == FALSE) { goto close_conn; } else { - zend_bool verify = mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? TRUE:FALSE; + enum mysqlnd_ssl_peer verify = mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? + MYSQLND_SSL_PEER_VERIFY: + (mysql_flags & CLIENT_SSL_DONT_VERIFY_SERVER_CERT? + MYSQLND_SSL_PEER_DONT_VERIFY: + MYSQLND_SSL_PEER_DEFAULT); DBG_INF("Switching to SSL"); if (!PACKET_WRITE(auth_packet, conn)) { goto close_conn; diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h index c1ede7e656..9e29da29dd 100644 --- a/ext/mysqlnd/mysqlnd_enum_n_def.h +++ b/ext/mysqlnd/mysqlnd_enum_n_def.h @@ -101,6 +101,10 @@ #define CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA (1UL << 21) /* Enable authentication response packet to be larger than 255 bytes. */ #define CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS (1UL << 22) /* Don't close the connection for a connection with expired password. */ #define CLIENT_SESSION_TRACK (1UL << 23) /* Extended OK */ +/* + This is a mysqlnd extension. CLIENT_ODBC is not used anyway. We will reuse it for our case and translate it to not using SSL peer verification +*/ +#define CLIENT_SSL_DONT_VERIFY_SERVER_CERT CLIENT_ODBC #define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30) #define CLIENT_REMEMBER_OPTIONS (1UL << 31) diff --git a/ext/mysqlnd/mysqlnd_net.c b/ext/mysqlnd/mysqlnd_net.c index 7b164ac294..3e8d0993fa 100644 --- a/ext/mysqlnd/mysqlnd_net.c +++ b/ext/mysqlnd/mysqlnd_net.c @@ -798,8 +798,27 @@ MYSQLND_METHOD(mysqlnd_net, set_client_option)(MYSQLND_NET * const net, enum mys break; } case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: - net->data->options.ssl_verify_peer = value? ((*(zend_bool *)value)? TRUE:FALSE): FALSE; + { + enum mysqlnd_ssl_peer val = *((enum mysqlnd_ssl_peer *)value); + switch (val) { + case MYSQLND_SSL_PEER_VERIFY: + DBG_INF("MYSQLND_SSL_PEER_VERIFY"); + break; + case MYSQLND_SSL_PEER_DONT_VERIFY: + DBG_INF("MYSQLND_SSL_PEER_DONT_VERIFY"); + break; + case MYSQLND_SSL_PEER_DEFAULT: + DBG_INF("MYSQLND_SSL_PEER_DEFAULT"); + val = MYSQLND_SSL_PEER_DEFAULT; + break; + default: + DBG_INF("default = MYSQLND_SSL_PEER_DEFAULT_ACTION"); + val = MYSQLND_SSL_PEER_DEFAULT; + break; + } + net->data->options.ssl_verify_peer = val; break; + } case MYSQL_OPT_READ_TIMEOUT: net->data->options.timeout_read = *(unsigned int*) value; break; @@ -886,6 +905,7 @@ MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net TSRMLS_DC) #ifdef MYSQLND_SSL_SUPPORTED php_stream_context * context = php_stream_context_alloc(TSRMLS_C); php_stream * net_stream = net->data->m.get_stream(net TSRMLS_CC); + zend_bool any_flag = FALSE; DBG_ENTER("mysqlnd_net::enable_ssl"); if (!context) { @@ -896,12 +916,7 @@ MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net TSRMLS_DC) zval key_zval; ZVAL_STRING(&key_zval, net->data->options.ssl_key, 0); php_stream_context_set_option(context, "ssl", "local_pk", &key_zval); - } - { - zval verify_peer_zval; - ZVAL_BOOL(&verify_peer_zval, net->data->options.ssl_verify_peer); - php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval); - php_stream_context_set_option(context, "ssl", "verify_peer_name", &verify_peer_zval); + any_flag = TRUE; } if (net->data->options.ssl_cert) { zval cert_zval; @@ -910,27 +925,48 @@ MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net TSRMLS_DC) if (!net->data->options.ssl_key) { php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval); } + any_flag = TRUE; } if (net->data->options.ssl_ca) { zval cafile_zval; ZVAL_STRING(&cafile_zval, net->data->options.ssl_ca, 0); php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval); + any_flag = TRUE; } if (net->data->options.ssl_capath) { zval capath_zval; ZVAL_STRING(&capath_zval, net->data->options.ssl_capath, 0); php_stream_context_set_option(context, "ssl", "capath", &capath_zval); + any_flag = TRUE; } if (net->data->options.ssl_passphrase) { zval passphrase_zval; ZVAL_STRING(&passphrase_zval, net->data->options.ssl_passphrase, 0); php_stream_context_set_option(context, "ssl", "passphrase", &passphrase_zval); + any_flag = TRUE; } if (net->data->options.ssl_cipher) { zval cipher_zval; ZVAL_STRING(&cipher_zval, net->data->options.ssl_cipher, 0); php_stream_context_set_option(context, "ssl", "ciphers", &cipher_zval); + any_flag = TRUE; + } + { + zval verify_peer_zval; + zend_bool verify; + + if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DEFAULT) { + net->data->options.ssl_verify_peer = any_flag? MYSQLND_SSL_PEER_DEFAULT_ACTION:MYSQLND_SSL_PEER_DONT_VERIFY; + } + + verify = net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_VERIFY? TRUE:FALSE; + + DBG_INF_FMT("VERIFY=%d", verify); + ZVAL_BOOL(&verify_peer_zval, verify); + php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval); + php_stream_context_set_option(context, "ssl", "verify_peer_name", &verify_peer_zval); } + php_stream_context_set(net_stream, context); if (php_stream_xport_crypto_setup(net_stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL TSRMLS_CC) < 0 || php_stream_xport_crypto_enable(net_stream, 1 TSRMLS_CC) < 0) diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h index 170c977c2b..f5d0b47a6f 100644 --- a/ext/mysqlnd/mysqlnd_structs.h +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -207,7 +207,13 @@ typedef struct st_mysqlnd_net_options char *ssl_capath; char *ssl_cipher; char *ssl_passphrase; - zend_bool ssl_verify_peer; + enum mysqlnd_ssl_peer { + MYSQLND_SSL_PEER_DEFAULT = 0, + MYSQLND_SSL_PEER_VERIFY = 1, + MYSQLND_SSL_PEER_DONT_VERIFY = 2, + +#define MYSQLND_SSL_PEER_DEFAULT_ACTION MYSQLND_SSL_PEER_VERIFY + } ssl_verify_peer; uint64_t flags; char * sha256_server_public_key; @@ -219,6 +225,7 @@ typedef struct st_mysqlnd_net_options } MYSQLND_NET_OPTIONS; + typedef struct st_mysqlnd_connection MYSQLND; typedef struct st_mysqlnd_connection_data MYSQLND_CONN_DATA; typedef struct st_mysqlnd_net MYSQLND_NET;