diff options
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | ext/mysqli/mysqli_api.c | 2 | ||||
-rw-r--r-- | ext/mysqli/mysqli_fe.c | 4 | ||||
-rw-r--r-- | ext/mysqli/tests/mysqli_class_mysqli_interface.phpt | 1 | ||||
-rw-r--r-- | ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt | 16 | ||||
-rw-r--r-- | ext/mysqlnd/config9.m4 | 1 | ||||
-rw-r--r-- | ext/mysqlnd/mysqlnd.c | 85 | ||||
-rw-r--r-- | ext/mysqlnd/mysqlnd.h | 2 | ||||
-rw-r--r-- | ext/mysqlnd/mysqlnd_enum_n_def.h | 8 | ||||
-rw-r--r-- | ext/mysqlnd/mysqlnd_libmysql_compat.h | 1 | ||||
-rw-r--r-- | ext/mysqlnd/mysqlnd_net.c | 185 | ||||
-rw-r--r-- | ext/mysqlnd/mysqlnd_structs.h | 35 | ||||
-rw-r--r-- | ext/mysqlnd/mysqlnd_wireprotocol.c | 48 | ||||
-rw-r--r-- | ext/mysqlnd/mysqlnd_wireprotocol.h | 1 | ||||
-rw-r--r-- | ext/mysqlnd/php_mysqlnd.c | 6 | ||||
-rw-r--r-- | ext/openssl/openssl.c | 11 |
16 files changed, 340 insertions, 67 deletions
@@ -61,6 +61,7 @@ PHP NEWS is created from an ISO string). (Derick) - Fixed bug #49576 (FILTER_VALIDATE_EMAIL filter needs updating) (Rasmus) - Fixed bug #49429 (odbc_autocommit doesn't work). (Felipe) +- Fixed bug #49234 (mysqli_ssl_set not found). (Andrey) - Fixed bug #49192 (PHP crashes when GC invoked on COM object). (Stas) - Fixed bug #49059 (DateTime::diff() repeats previous sub() operation). (yoarvi@gmail.com, Derick) diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index 6162cce92b..651bdeacab 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -2121,7 +2121,6 @@ PHP_FUNCTION(mysqli_sqlstate) /* {{{ proto bool mysqli_ssl_set(object link ,string key ,string cert ,string ca ,string capath ,string cipher]) U */ -#if !defined(MYSQLI_USE_MYSQLND) PHP_FUNCTION(mysqli_ssl_set) { MY_MYSQL *mysql; @@ -2144,7 +2143,6 @@ PHP_FUNCTION(mysqli_ssl_set) RETURN_TRUE; } -#endif /* }}} */ /* {{{ proto mixed mysqli_stat(object link) diff --git a/ext/mysqli/mysqli_fe.c b/ext/mysqli/mysqli_fe.c index 59cb65563c..0f9c9b6943 100644 --- a/ext/mysqli/mysqli_fe.c +++ b/ext/mysqli/mysqli_fe.c @@ -156,9 +156,7 @@ const zend_function_entry mysqli_functions[] = { PHP_FE(mysqli_stmt_reset, NULL) PHP_FE(mysqli_stmt_param_count, NULL) PHP_FE(mysqli_sqlstate, NULL) -#if !defined(MYSQLI_USE_MYSQLND) PHP_FE(mysqli_ssl_set, NULL) -#endif PHP_FE(mysqli_stat, NULL) PHP_FE(mysqli_stmt_affected_rows, NULL) PHP_FE(mysqli_stmt_close, NULL) @@ -246,9 +244,7 @@ const zend_function_entry mysqli_link_methods[] = { PHP_FALIAS(set_charset,mysqli_set_charset,NULL) #endif PHP_FALIAS(set_opt, mysqli_options,NULL) -#if !defined(MYSQLI_USE_MYSQLND) PHP_FALIAS(ssl_set,mysqli_ssl_set,NULL) -#endif PHP_FALIAS(stat,mysqli_stat,NULL) PHP_FALIAS(stmt_init,mysqli_stmt_init, NULL) PHP_FALIAS(store_result,mysqli_store_result,NULL) diff --git a/ext/mysqli/tests/mysqli_class_mysqli_interface.phpt b/ext/mysqli/tests/mysqli_class_mysqli_interface.phpt index b86f69c445..50c66d4f4d 100644 --- a/ext/mysqli/tests/mysqli_class_mysqli_interface.phpt +++ b/ext/mysqli/tests/mysqli_class_mysqli_interface.phpt @@ -50,6 +50,7 @@ require_once('skipifconnectfailure.inc'); 'select_db' => true, 'set_charset' => true, 'set_opt' => true, + 'ssl_set' => true, 'stat' => true, 'stmt_init' => true, 'store_result' => true, diff --git a/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt b/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt index 802e524337..ebe7a0285d 100644 --- a/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt +++ b/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt @@ -615,6 +615,22 @@ Modifiers: 256 Number of Parameters: 0 Number of Required Parameters: 0 +Inspecting method 'ssl_set' +isFinal: no +isAbstract: no +isPublic: yes +isPrivate: no +isProtected: no +isStatic: no +isConstructor: no +isDestructor: no +isInternal: yes +isUserDefined: no +returnsReference: no +Modifiers: 256 +Number of Parameters: 0 +Number of Required Parameters: 0 + Inspecting method 'stat' isFinal: no isAbstract: no diff --git a/ext/mysqlnd/config9.m4 b/ext/mysqlnd/config9.m4 index 72e2796195..2fcf336fc4 100644 --- a/ext/mysqlnd/config9.m4 +++ b/ext/mysqlnd/config9.m4 @@ -36,6 +36,7 @@ if test "$PHP_MYSQLND_ENABLED" = "yes"; then MYSQLND_LIBS="$MYSQLND_LIBS -lz" fi fi + AC_DEFINE([MYSQLND_SSL_SUPPORTED], 1, [Enable SSL support]) fi if test "$PHP_MYSQLND_ENABLED" = "yes" || test "$PHP_MYSQLI" != "no"; then diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index 078d2e6afa..bb26c4cd5c 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -109,27 +109,8 @@ MYSQLND_METHOD(mysqlnd_conn, free_options)(MYSQLND *conn TSRMLS_DC) mnd_pefree(conn->options.cfg_section, pers); conn->options.cfg_section = NULL; } - if (conn->options.ssl_key) { - mnd_pefree(conn->options.ssl_key, pers); - conn->options.ssl_key = NULL; - } - if (conn->options.ssl_cert) { - mnd_pefree(conn->options.ssl_cert, pers); - conn->options.ssl_cert = NULL; - } - if (conn->options.ssl_ca) { - mnd_pefree(conn->options.ssl_ca, pers); - conn->options.ssl_ca = NULL; - } - if (conn->options.ssl_capath) { - mnd_pefree(conn->options.ssl_capath, pers); - conn->options.ssl_capath = NULL; - } - if (conn->options.ssl_cipher) { - mnd_pefree(conn->options.ssl_cipher, pers); - conn->options.ssl_cipher = NULL; - } } +/* }}} */ /* {{{ mysqlnd_conn::free_contents */ @@ -356,9 +337,8 @@ MYSQLND_METHOD(mysqlnd_conn, simple_command)(MYSQLND *conn, enum php_mysqlnd_ser DBG_ERR("Server is gone"); DBG_RETURN(FAIL); default: - SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, - mysqlnd_out_of_sync); - DBG_ERR("Command out of sync"); + SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); + DBG_ERR_FMT("Command out of sync. State=%d", CONN_GET_STATE(conn)); DBG_RETURN(FAIL); } @@ -597,7 +577,20 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, mysql_flags &= ~CLIENT_COMPRESS; } #endif - +#ifndef MYSQLND_SSL_SUPPORTED + if (mysql_flags & CLIENT_SSL) { + mysql_flags &= ~CLIENT_SSL; + } +#else + if (conn->net->options.ssl_key || conn->net->options.ssl_cert || + conn->net->options.ssl_ca || conn->net->options.ssl_capath || conn->net->options.ssl_cipher) + { + mysql_flags |= CLIENT_SSL; + } + if ((greet_packet->server_capabilities & CLIENT_SSL) && (mysql_flags & CLIENT_SSL)) { + auth_packet->send_half_packet = TRUE; + } +#endif auth_packet->user = user; auth_packet->password = passwd; @@ -619,12 +612,33 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND *conn, conn->scramble = auth_packet->server_scramble_buf = mnd_pemalloc(SCRAMBLE_LENGTH, conn->persistent); memcpy(auth_packet->server_scramble_buf, greet_packet->scramble_buf, SCRAMBLE_LENGTH); + if (!PACKET_WRITE(auth_packet, conn)) { CONN_SET_STATE(conn, CONN_QUIT_SENT); SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); goto err; } +#ifdef MYSQLND_SSL_SUPPORTED + if (auth_packet->send_half_packet) { + zend_bool verify = mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? TRUE:FALSE; + DBG_INF("Switching to SSL"); + + conn->net->m.set_client_option(conn->net, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify TSRMLS_CC); + + if (FAIL == conn->net->m.enable_ssl(conn->net TSRMLS_CC)) { + goto err; + } + + auth_packet->send_half_packet = FALSE; + if (!PACKET_WRITE(auth_packet, conn)) { + CONN_SET_STATE(conn, CONN_QUIT_SENT); + SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); + goto err; + } + } +#endif + if (FAIL == PACKET_READ(ok_packet, conn) || ok_packet->field_count >= 0xFE) { if (ok_packet->field_count == 0xFE) { /* old authentication with new server !*/ @@ -1178,6 +1192,18 @@ PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, size } /* }}} */ +/* {{{ mysqlnd_conn::ssl_set */ +void +MYSQLND_METHOD(mysqlnd_conn, ssl_set)(MYSQLND * const conn, const char * key, const char * const cert, const char * const ca, const char * const capath, const char * const cipher TSRMLS_DC) +{ + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_KEY, key TSRMLS_CC); + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CERT, cert TSRMLS_CC); + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CA, ca TSRMLS_CC); + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CAPATH, capath TSRMLS_CC); + conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CIPHER, cipher TSRMLS_CC); +} +/* }}} */ + /* {{{ mysqlnd_conn::escape_string */ static ulong @@ -1875,6 +1901,12 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, case MYSQL_OPT_READ_TIMEOUT: case MYSQL_OPT_WRITE_TIMEOUT: #endif + case MYSQLND_OPT_SSL_KEY: + case MYSQLND_OPT_SSL_CERT: + case MYSQLND_OPT_SSL_CA: + case MYSQLND_OPT_SSL_CAPATH: + case MYSQLND_OPT_SSL_CIPHER: + case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: case MYSQL_OPT_CONNECT_TIMEOUT: case MYSQLND_OPT_NET_CMD_BUFFER_SIZE: case MYSQLND_OPT_NET_READ_BUFFER_SIZE: @@ -1913,7 +1945,6 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn, #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_SET_CLIENT_IP: case MYSQL_REPORT_DATA_TRUNCATION: - case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: #endif /* currently not supported. Todo!! */ break; @@ -2100,7 +2131,9 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn) MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response), MYSQLND_METHOD(mysqlnd_conn, restart_psession), MYSQLND_METHOD(mysqlnd_conn, end_psession), - MYSQLND_METHOD(mysqlnd_conn, send_close) + MYSQLND_METHOD(mysqlnd_conn, send_close), + + MYSQLND_METHOD(mysqlnd_conn, ssl_set) MYSQLND_CLASS_METHODS_END; diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h index 1539c7b525..4f15aa4fef 100644 --- a/ext/mysqlnd/mysqlnd.h +++ b/ext/mysqlnd/mysqlnd.h @@ -172,6 +172,8 @@ PHPAPI unsigned long * _mysqlnd_fetch_lengths(MYSQLND_RES * const result TSRMLS PHPAPI const char * mysqlnd_get_client_info(); PHPAPI unsigned int mysqlnd_get_client_version(); +#define mysqlnd_ssl_set(conn, key, cert, ca, capath, cipher) (conn)->m->ssl_set((conn), (key), (cert), (ca), (capath), (cipher) TSRMLS_CC) + /* PS */ #define mysqlnd_stmt_insert_id(stmt) (stmt)->m->get_last_insert_id((stmt) TSRMLS_CC) #define mysqlnd_stmt_affected_rows(stmt) (stmt)->m->get_affected_rows((stmt) TSRMLS_CC) diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h index 4550bfd621..d6aadd5478 100644 --- a/ext/mysqlnd/mysqlnd_enum_n_def.h +++ b/ext/mysqlnd/mysqlnd_enum_n_def.h @@ -90,6 +90,8 @@ #define CLIENT_MULTI_RESULTS (1UL << 17) /* Enable/disable multi-results */ #define CLIENT_PS_MULTI_RESULTS (1UL << 18) /* Multi-results in PS-protocol */ +#define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30) + typedef enum mysqlnd_extension { MYSQLND_MYSQL = 0, @@ -156,6 +158,12 @@ typedef enum mysqlnd_option #endif MYSQLND_OPT_NET_CMD_BUFFER_SIZE = 202, MYSQLND_OPT_NET_READ_BUFFER_SIZE = 203, + MYSQLND_OPT_SSL_KEY = 204, + MYSQLND_OPT_SSL_CERT = 205, + MYSQLND_OPT_SSL_CA = 206, + MYSQLND_OPT_SSL_CAPATH = 207, + MYSQLND_OPT_SSL_CIPHER = 208, + MYSQLND_OPT_SSL_PASSPHRASE = 209 } enum_mysqlnd_option; diff --git a/ext/mysqlnd/mysqlnd_libmysql_compat.h b/ext/mysqlnd/mysqlnd_libmysql_compat.h index 78f48c97b9..4044ae6a25 100644 --- a/ext/mysqlnd/mysqlnd_libmysql_compat.h +++ b/ext/mysqlnd/mysqlnd_libmysql_compat.h @@ -79,6 +79,7 @@ #define mysql_set_server_option(r,o) mysqlnd_set_server_option((r), (o)) #define mysql_set_character_set(r,a) mysqlnd_set_character_set((r), (a)) #define mysql_sqlstate(r) mysqlnd_sqlstate((r)) +#define mysql_ssl_set(c,key,cert,ca,capath,cipher) mysqlnd_ssl_set((c), (key), (cert), (ca), (capath), (cipher)) #define mysql_stmt_affected_rows(s) mysqlnd_stmt_affected_rows((s)) #define mysql_stmt_field_count(s) mysqlnd_stmt_field_count((s)) #define mysql_stmt_param_count(s) mysqlnd_stmt_param_count((s)) diff --git a/ext/mysqlnd/mysqlnd_net.c b/ext/mysqlnd/mysqlnd_net.c index d88e20418c..4705c8b4ef 100644 --- a/ext/mysqlnd/mysqlnd_net.c +++ b/ext/mysqlnd/mysqlnd_net.c @@ -172,8 +172,8 @@ MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * net, const char * const schem /* should always happen because read_timeout cannot be set via API */ net->options.timeout_read = (unsigned int) MYSQLND_G(net_read_timeout); } - if (net->options.timeout_read) - { + if (net->options.timeout_read) { + DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->options.timeout_read); tv.tv_sec = net->options.timeout_read; tv.tv_usec = 0; php_stream_set_option(net->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv); @@ -587,6 +587,63 @@ MYSQLND_METHOD(mysqlnd_net, set_client_option)(MYSQLND_NET * const net, enum mys DBG_INF("MYSQL_OPT_CONNECT_TIMEOUT"); net->options.timeout_connect = *(unsigned int*) value; break; + case MYSQLND_OPT_SSL_KEY: + { + zend_bool pers = net->persistent; + if (net->options.ssl_key) { + mnd_pefree(net->options.ssl_key, pers); + } + net->options.ssl_key = value? mnd_pestrdup(value, pers) : NULL; + break; + } + case MYSQLND_OPT_SSL_CERT: + { + zend_bool pers = net->persistent; + if (net->options.ssl_cert) { + mnd_pefree(net->options.ssl_cert, pers); + } + net->options.ssl_cert = value? mnd_pestrdup(value, pers) : NULL; + break; + } + case MYSQLND_OPT_SSL_CA: + { + zend_bool pers = net->persistent; + if (net->options.ssl_ca) { + mnd_pefree(net->options.ssl_ca, pers); + } + net->options.ssl_ca = value? mnd_pestrdup(value, pers) : NULL; + break; + } + case MYSQLND_OPT_SSL_CAPATH: + { + zend_bool pers = net->persistent; + if (net->options.ssl_capath) { + mnd_pefree(net->options.ssl_capath, pers); + } + net->options.ssl_capath = value? mnd_pestrdup(value, pers) : NULL; + break; + } + case MYSQLND_OPT_SSL_CIPHER: + { + zend_bool pers = net->persistent; + if (net->options.ssl_cipher) { + mnd_pefree(net->options.ssl_cipher, pers); + } + net->options.ssl_cipher = value? mnd_pestrdup(value, pers) : NULL; + break; + } + case MYSQLND_OPT_SSL_PASSPHRASE: + { + zend_bool pers = net->persistent; + if (net->options.ssl_passphrase) { + mnd_pefree(net->options.ssl_passphrase, pers); + } + net->options.ssl_passphrase = value? mnd_pestrdup(value, pers) : NULL; + break; + } + case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: + net->options.ssl_verify_peer = value? ((*(zend_bool *)value)? TRUE:FALSE): FALSE; + break; #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_OPT_READ_TIMEOUT: DBG_INF("MYSQL_OPT_READ_TIMEOUT"); @@ -657,12 +714,113 @@ MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data)(MYSQLND_NET * const net, enum } /* }}} */ +/* + in libmyusql, if cert and !key then key=cert +*/ +/* {{{ mysqlnd_net::enable_ssl */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net TSRMLS_DC) +{ +#ifdef MYSQLND_SSL_SUPPORTED + php_stream_context *context = php_stream_context_alloc(); + DBG_ENTER("mysqlnd_net::enable_ssl"); + if (!context) { + DBG_RETURN(FAIL); + } + + if (net->options.ssl_key) { + zval key_zval; + ZVAL_STRING(&key_zval, net->options.ssl_key, 0); + DBG_INF("key"); + php_stream_context_set_option(context, "ssl", "local_pk", &key_zval); + } + if (net->options.ssl_verify_peer) { + zval verify_peer_zval; + ZVAL_TRUE(&verify_peer_zval); + DBG_INF("verify peer"); + php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval); + } + if (net->options.ssl_cert) { + zval cert_zval; + ZVAL_STRING(&cert_zval, net->options.ssl_cert, 0); + DBG_INF_FMT("local_cert=%s", net->options.ssl_cert); + php_stream_context_set_option(context, "ssl", "local_cert", &cert_zval); + if (!net->options.ssl_key) { + php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval); + } + } + if (net->options.ssl_ca) { + zval cafile_zval; + ZVAL_STRING(&cafile_zval, net->options.ssl_ca, 0); + DBG_INF_FMT("cafile=%s", net->options.ssl_ca); + php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval); + } + if (net->options.ssl_capath) { + zval capath_zval; + ZVAL_STRING(&capath_zval, net->options.ssl_capath, 0); + DBG_INF_FMT("capath=%s", net->options.ssl_capath); + php_stream_context_set_option(context, "ssl", "cafile", &capath_zval); + } + if (net->options.ssl_passphrase) { + zval passphrase_zval; + ZVAL_STRING(&passphrase_zval, net->options.ssl_passphrase, 0); + php_stream_context_set_option(context, "ssl", "passphrase", &passphrase_zval); + } + if (net->options.ssl_cipher) { + zval cipher_zval; + ZVAL_STRING(&cipher_zval, net->options.ssl_cipher, 0); + DBG_INF_FMT("ciphers=%s", net->options.ssl_cipher); + php_stream_context_set_option(context, "ssl", "ciphers", &cipher_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) + { + DBG_ERR("Cannot connect to MySQL by using SSL"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot connect to MySQL by using SSL"); + DBG_RETURN(FAIL); + } + /* + get rid of the context. we are persistent and if this is a real pconn used by mysql/mysqli, + then the context would not survive cleaning of EG(regular_list), where it is registered, as a + resource. What happens is that after this destruction any use of the network will mean usage + of the context, which means usage of already freed memory, bad. Actually we don't need this + context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it. + */ + php_stream_context_set(net->stream, NULL); + + if (net->options.timeout_read) { + struct timeval tv; + DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->options.timeout_read); + tv.tv_sec = net->options.timeout_read; + tv.tv_usec = 0; + php_stream_set_option(net->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv); + } + + DBG_RETURN(PASS); +#else + DBG_ENTER("mysqlnd_net::enable_ssl"); + DBG_RETURN(PASS); +#endif +} +/* }}} */ + + +/* {{{ mysqlnd_net::disable_ssl */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, disable_ssl)(MYSQLND_NET * const net TSRMLS_DC) +{ + DBG_ENTER("mysqlnd_net::disable_ssl"); + DBG_RETURN(PASS); +} +/* }}} */ /* {{{ mysqlnd_net::set_client_option */ static void MYSQLND_METHOD(mysqlnd_net, free_contents)(MYSQLND_NET * net TSRMLS_DC) { + zend_bool pers = net->persistent; DBG_ENTER("mysqlnd_net::free_contents"); #ifdef MYSQLND_COMPRESSION_ENABLED @@ -670,6 +828,27 @@ MYSQLND_METHOD(mysqlnd_net, free_contents)(MYSQLND_NET * net TSRMLS_DC) net->uncompressed_data->free_buffer(&net->uncompressed_data TSRMLS_CC); } #endif + if (net->options.ssl_key) { + mnd_pefree(net->options.ssl_key, pers); + net->options.ssl_key = NULL; + } + if (net->options.ssl_cert) { + mnd_pefree(net->options.ssl_cert, pers); + net->options.ssl_cert = NULL; + } + if (net->options.ssl_ca) { + mnd_pefree(net->options.ssl_ca, pers); + net->options.ssl_ca = NULL; + } + if (net->options.ssl_capath) { + mnd_pefree(net->options.ssl_capath, pers); + net->options.ssl_capath = NULL; + } + if (net->options.ssl_cipher) { + mnd_pefree(net->options.ssl_cipher, pers); + net->options.ssl_cipher = NULL; + } + DBG_VOID_RETURN; } /* }}} */ @@ -696,6 +875,8 @@ mysqlnd_net_init(zend_bool persistent TSRMLS_DC) net->m.encode = MYSQLND_METHOD(mysqlnd_net, encode); net->m.consume_uneaten_data = MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data); net->m.free_contents = MYSQLND_METHOD(mysqlnd_net, free_contents); + net->m.enable_ssl = MYSQLND_METHOD(mysqlnd_net, enable_ssl); + net->m.disable_ssl = MYSQLND_METHOD(mysqlnd_net, disable_ssl); { unsigned int buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/ diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h index 52f27ab292..6e196e0f16 100644 --- a/ext/mysqlnd/mysqlnd_structs.h +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -155,13 +155,17 @@ typedef struct st_mysqlnd_options char *cfg_file; char *cfg_section; - /* SSL information */ - char *ssl_key; - char *ssl_cert; - char *ssl_ca; - char *ssl_capath; - char *ssl_cipher; - zend_bool use_ssl; + /* + We need to keep these because otherwise st_mysqlnd_conn will be changed. + The ABI will be broken and the methods structure will be somewhere else + in the memory which can crash external code. Feel free to reuse these. + */ + char * unused1; + char * unused2; + char * unused3; + char * unused4; + char * unused5; + zend_bool unused6; char *charset_name; /* maximum allowed packet size for communication */ @@ -181,6 +185,15 @@ typedef struct st_mysqlnd_net_options unsigned int timeout_write; unsigned int net_read_buffer_size; + + /* SSL information */ + char *ssl_key; + char *ssl_cert; + char *ssl_ca; + char *ssl_capath; + char *ssl_cipher; + char *ssl_passphrase; + zend_bool ssl_verify_peer; } MYSQLND_NET_OPTIONS; @@ -250,6 +263,8 @@ typedef enum_func_status (*func_mysqlnd_net__decode)(zend_uchar * uncompressed_d typedef enum_func_status (*func_mysqlnd_net__encode)(zend_uchar * compress_buffer, size_t compress_buffer_len, const zend_uchar * const uncompressed_data, size_t uncompressed_data_len TSRMLS_DC); typedef size_t (*func_mysqlnd_net__consume_uneaten_data)(MYSQLND_NET * const net, enum php_mysqlnd_server_command cmd TSRMLS_DC); typedef void (*func_mysqlnd_net__free_contents)(MYSQLND_NET * net TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_net__enable_ssl)(MYSQLND_NET * const net TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_net__disable_ssl)(MYSQLND_NET * const net TSRMLS_DC); struct st_mysqlnd_net_methods @@ -264,6 +279,8 @@ struct st_mysqlnd_net_methods func_mysqlnd_net__encode encode; func_mysqlnd_net__consume_uneaten_data consume_uneaten_data; func_mysqlnd_net__free_contents free_contents; + func_mysqlnd_net__enable_ssl enable_ssl; + func_mysqlnd_net__disable_ssl disable_ssl; }; @@ -375,6 +392,8 @@ typedef enum_func_status (*func_mysqlnd_conn__restart_psession)(MYSQLND *conn TS typedef enum_func_status (*func_mysqlnd_conn__end_psession)(MYSQLND *conn TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_conn__send_close)(MYSQLND * conn TSRMLS_DC); +typedef void (*func_mysqlnd_conn__ssl_set)(MYSQLND * const conn, const char * key, const char * const cert, const char * const ca, const char * const capath, const char * const cipher TSRMLS_DC); + struct st_mysqlnd_conn_methods { @@ -443,6 +462,8 @@ struct st_mysqlnd_conn_methods func_mysqlnd_conn__restart_psession restart_psession; func_mysqlnd_conn__end_psession end_psession; func_mysqlnd_conn__send_close send_close; + + func_mysqlnd_conn__ssl_set ssl_set; }; diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index 61036bc306..6145e32b3d 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -458,31 +458,33 @@ size_t php_mysqlnd_auth_write(void *_packet, MYSQLND *conn TSRMLS_DC) memset(p, 0, 23); /* filler */ p+= 23; - len= strlen(packet->user); - memcpy(p, packet->user, len); - p+= len; - *p++ = '\0'; - - /* copy scrambled pass*/ - if (packet->password && packet->password[0]) { - /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */ - int1store(p, 20); - p++; - php_mysqlnd_scramble((zend_uchar*)p, packet->server_scramble_buf, (zend_uchar*)packet->password); - p+= 20; - } else { - /* Zero length */ - int1store(p, 0); - p++; - } + if (!packet->send_half_packet) { + len = strlen(packet->user); + memcpy(p, packet->user, len); + p+= len; + *p++ = '\0'; + + /* copy scrambled pass*/ + if (packet->password && packet->password[0]) { + /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */ + int1store(p, 20); + p++; + php_mysqlnd_scramble((zend_uchar*)p, packet->server_scramble_buf, (zend_uchar*)packet->password); + p+= 20; + } else { + /* Zero length */ + int1store(p, 0); + p++; + } - if (packet->db) { - memcpy(p, packet->db, packet->db_len); - p+= packet->db_len; - *p++= '\0'; + if (packet->db) { + memcpy(p, packet->db, packet->db_len); + p+= packet->db_len; + *p++= '\0'; + } + /* Handle CLIENT_CONNECT_WITH_DB */ + /* no \0 for no DB */ } - /* Handle CLIENT_CONNECT_WITH_DB */ - /* no \0 for no DB */ DBG_RETURN(conn->net->m.send(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC)); } diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqlnd/mysqlnd_wireprotocol.h index 82f0465df3..fe4736c0ca 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.h +++ b/ext/mysqlnd/mysqlnd_wireprotocol.h @@ -101,6 +101,7 @@ typedef struct st_mysqlnd_packet_auth { /* +1 for \0 because of scramble() */ unsigned char *server_scramble_buf; size_t db_len; + zend_bool send_half_packet; } MYSQLND_PACKET_AUTH; /* OK packet */ diff --git a/ext/mysqlnd/php_mysqlnd.c b/ext/mysqlnd/php_mysqlnd.c index 1ad5b4e253..431ee4d3ef 100644 --- a/ext/mysqlnd/php_mysqlnd.c +++ b/ext/mysqlnd/php_mysqlnd.c @@ -108,6 +108,12 @@ PHP_MINFO_FUNCTION(mysqlnd) #else "not supported"); #endif + php_info_print_table_row(2, "SSL", +#ifdef MYSQLND_SSL_SUPPORTED + "supported"); +#else + "not supported"); +#endif snprintf(buf, sizeof(buf), "%ld", MYSQLND_G(net_cmd_buffer_size)); php_info_print_table_row(2, "Command buffer size", buf); snprintf(buf, sizeof(buf), "%ld", MYSQLND_G(net_read_buffer_size)); diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index b18108f7e1..430467b4dd 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -4435,7 +4435,9 @@ SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC) /* {{{ if (!cipherlist) { cipherlist = "DEFAULT"; } - SSL_CTX_set_cipher_list(ctx, cipherlist); + if (SSL_CTX_set_cipher_list(ctx, cipherlist) != 1) { + return NULL; + } GET_VER_OPT_STRING("local_cert", certfile); if (certfile) { @@ -4443,6 +4445,7 @@ SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC) /* {{{ EVP_PKEY *key = NULL; SSL *tmpssl; char resolved_path_buff[MAXPATHLEN]; + const char * private_key; if (VCWD_REALPATH(certfile, resolved_path_buff)) { /* a certificate to use for authentication */ @@ -4451,8 +4454,10 @@ SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC) /* {{{ return NULL; } - if (SSL_CTX_use_PrivateKey_file(ctx, resolved_path_buff, SSL_FILETYPE_PEM) != 1) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set private key file `%s'", resolved_path_buff); + GET_VER_OPT_STRING("local_pk", private_key); + + if (private_key && SSL_CTX_use_PrivateKey_file(ctx, private_key, SSL_FILETYPE_PEM) != 1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set private key file `%s'", private_key); return NULL; } |