if (CHECK_LIB("ws2_32.lib", "mysqlnd")) {
mysqlnd_source =
"mysqlnd.c " +
+ "mysqlnd_auth.c" +
"mysqlnd_block_alloc.c " +
"mysqlnd_charset.c " +
"mysqlnd_debug.c " +
if test "$PHP_MYSQLND_ENABLED" = "yes"; then
mysqlnd_sources="mysqlnd.c mysqlnd_charset.c mysqlnd_wireprotocol.c \
mysqlnd_ps.c mysqlnd_loaddata.c mysqlnd_net.c \
- mysqlnd_ps_codec.c mysqlnd_statistics.c \
+ mysqlnd_ps_codec.c mysqlnd_statistics.c mysqlnd_auth.c \
mysqlnd_result.c mysqlnd_result_meta.c mysqlnd_debug.c\
mysqlnd_block_alloc.c mysqlnd_plugin.c php_mysqlnd.c"
mnd_pefree(conn->options.charset_name, pers);
conn->options.charset_name = NULL;
}
+ if (conn->options.auth_protocol) {
+ mnd_pefree(conn->options.auth_protocol, pers);
+ conn->options.auth_protocol = NULL;
+ }
if (conn->options.num_commands) {
unsigned int i;
for (i = 0; i < conn->options.num_commands; i++) {
/* }}} */
-#define MYSQLND_ASSEMBLED_PACKET_MAX_SIZE 3UL*1024UL*1024UL*1024UL
/* {{{ mysqlnd_switch_to_ssl_if_needed */
-static MYSQLND_PACKET_AUTH *
+static enum_func_status
mysqlnd_switch_to_ssl_if_needed(
MYSQLND * conn,
const MYSQLND_PACKET_GREET * const greet_packet,
TSRMLS_DC
)
{
- const MYSQLND_CHARSET * charset = NULL;
- MYSQLND_PACKET_AUTH * auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
+ enum_func_status ret = FAIL;
+ const MYSQLND_CHARSET * charset;
+ MYSQLND_PACKET_AUTH * auth_packet;
DBG_ENTER("mysqlnd_switch_to_ssl_if_needed");
+ auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
if (!auth_packet) {
SET_OOM_ERROR(conn->error_info);
- goto err;
+ goto end;
}
auth_packet->client_flags = mysql_flags;
auth_packet->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
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;
+ goto end;
}
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;
+ goto end;
}
}
#endif
- DBG_RETURN(auth_packet);
-err:
+ ret = PASS;
+end:
PACKET_FREE(auth_packet);
- DBG_RETURN(NULL);
+ DBG_RETURN(ret);
}
/* }}} */
TSRMLS_DC)
{
enum_func_status ret = FAIL;
- MYSQLND_PACKET_AUTH * auth_packet = NULL;
- MYSQLND_PACKET_OK * ok_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
-
DBG_ENTER("mysqlnd_connect_run_authentication");
- if (!ok_packet) {
- SET_OOM_ERROR(conn->error_info);
- goto err;
- }
-
- auth_packet = mysqlnd_switch_to_ssl_if_needed(conn, greet_packet, options, mysql_flags TSRMLS_CC);
+ ret = mysqlnd_switch_to_ssl_if_needed(conn, greet_packet, options, mysql_flags TSRMLS_CC);
+ if (PASS == ret) {
+ char * switch_to_auth_protocol = NULL;
+ char * requested_protocol = NULL;
+
+ do {
+ struct st_mysqlnd_authentication_plugin * auth_plugin;
+ char * plugin_name = NULL;
+ requested_protocol = switch_to_auth_protocol? switch_to_auth_protocol:
+ (greet_packet->auth_protocol?
+ greet_packet->auth_protocol:
+ "mysql_native_password"
+ );
+ spprintf(&plugin_name, 0, "auth_plugin_%s", requested_protocol);
+ DBG_INF_FMT("looking for %s auth plugin", plugin_name);
+ auth_plugin = mysqlnd_plugin_find(plugin_name);
+ efree(plugin_name);
+ if (!auth_plugin) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Server requested authentication method uknown to the client [%s]", requested_protocol);
+ SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Server requested authentication method uknown to the client");
+ break;
+ }
- if (!auth_packet) {
- goto err;
- }
+ DBG_INF("plugin found");
- auth_packet->send_auth_data = TRUE;
- auth_packet->user = user;
- auth_packet->password = passwd;
- auth_packet->db = db;
- auth_packet->db_len = db_len;
+ ret = auth_plugin->methods.auth_handshake(conn, user, passwd, db, db_len, greet_packet, options, mysql_flags,
+ &switch_to_auth_protocol TSRMLS_CC);
+ DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
+ } while (ret == FAIL && switch_to_auth_protocol != NULL);
- conn->scramble = auth_packet->server_scramble_buf = mnd_pemalloc(SCRAMBLE_LENGTH, conn->persistent);
- if (!conn->scramble) {
- SET_OOM_ERROR(conn->error_info);
- goto err;
- }
- 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;
- }
+ if (ret == PASS) {
+ conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol TSRMLS_CC);
+ }
- if (FAIL == PACKET_READ(ok_packet, conn) || ok_packet->field_count >= 0xFE) {
- if (ok_packet->field_count == 0xFE) {
- /* old authentication with new server !*/
- DBG_ERR(mysqlnd_old_passwd);
- SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
- } else if (ok_packet->field_count == 0xFF) {
- if (ok_packet->sqlstate[0]) {
- strlcpy(conn->error_info.sqlstate, ok_packet->sqlstate, sizeof(conn->error_info.sqlstate));
- DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", ok_packet->error_no, ok_packet->sqlstate, ok_packet->error);
- }
- conn->error_info.error_no = ok_packet->error_no;
- strlcpy(conn->error_info.error, ok_packet->error, sizeof(conn->error_info.error));
+ if (switch_to_auth_protocol) {
+ mnd_efree(switch_to_auth_protocol);
+ switch_to_auth_protocol = NULL;
}
- goto err;
}
-
- SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
- ok_packet->message, ok_packet->message_len,
- conn->persistent);
- conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no);
- ret = PASS;
-err:
- PACKET_FREE(auth_packet);
- PACKET_FREE(ok_packet);
DBG_RETURN(ret);
}
/* }}} */
-#define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \
- CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \
- CLIENT_MULTI_RESULTS)
-
-
-
/* {{{ mysqlnd_conn::connect */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn,
host?host:"", user?user:"", db?db:"", port, mysql_flags,
conn? conn->persistent:0, conn? CONN_GET_STATE(conn):-1);
- if (conn && CONN_GET_STATE(conn) > CONN_ALLOCED && CONN_GET_STATE(conn) ) {
+ if (CONN_GET_STATE(conn) > CONN_ALLOCED && CONN_GET_STATE(conn) ) {
DBG_INF("Connecting on a connected handle.");
if (CONN_GET_STATE(conn) < CONN_QUIT_SENT) {
saved_compression = TRUE;
conn->net->compressed = FALSE;
}
+ } else {
+ unsigned int max_allowed_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
+ conn->m->set_client_option(conn, MYSQLND_OPT_MAX_ALLOWED_PACKET, (char *)&max_allowed_size TSRMLS_CC);
}
if (!host || !host[0]) {
conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no);
/* we allow load data local infile by default */
- mysql_flags |= CLIENT_LOCAL_FILES | CLIENT_PS_MULTI_RESULTS;
mysql_flags |= MYSQLND_CAPABILITIES;
if (db) {
Stack space is not that expensive, so use a bit more to be protected against
buffer overflows.
*/
- size_t user_len, db_len;
enum_func_status ret = FAIL;
- MYSQLND_PACKET_CHG_USER_RESPONSE * chg_user_resp = NULL;
- char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 2 /* charset*/ ];
- char *p = buffer;
- const MYSQLND_CHARSET * old_cs = conn->charset;
-
- MYSQLND_PACKET_AUTH * auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
-
DBG_ENTER("mysqlnd_conn::change_user");
DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%u",
SET_ERROR_AFF_ROWS(conn);
- if (!auth_packet) {
- SET_OOM_ERROR(conn->error_info);
- goto end;
- }
-
if (!user) {
user = "";
}
if (!db) {
db = "";
}
- user_len = strlen(user);
- db_len = strlen(db);
-
- auth_packet->is_change_user_packet = TRUE;
- auth_packet->user = user;
- auth_packet->password = passwd;
- auth_packet->db = db;
- auth_packet->db_len = db_len;
- auth_packet->server_scramble_buf = conn->scramble;
- auth_packet->silent = silent;
- if (mysqlnd_get_server_version(conn) >= 50123) {
- auth_packet->charset_no = conn->charset->nr;
- p+=2;
- }
-
- 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 end;
- }
-
- chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE TSRMLS_CC);
- if (!chg_user_resp) {
- SET_OOM_ERROR(conn->error_info);
- goto end;
- }
- ret = PACKET_READ(chg_user_resp, conn);
- conn->error_info = chg_user_resp->error_info;
-
- if (conn->error_info.error_no) {
- ret = FAIL;
- /*
- COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune.
- bug#25371 mysql_change_user() triggers "packets out of sync"
- When it gets fixed, there should be one more check here
- */
- if (mysqlnd_get_server_version(conn) > 50113L && mysqlnd_get_server_version(conn) < 50118L) {
- MYSQLND_PACKET_OK * redundant_error_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
- if (redundant_error_packet) {
- PACKET_READ(redundant_error_packet, conn);
- PACKET_FREE(redundant_error_packet);
- DBG_INF_FMT("Server is %u, buggy, sends two ERR messages", mysqlnd_get_server_version(conn));
- } else {
- SET_OOM_ERROR(conn->error_info);
- }
- }
- }
- if (ret == PASS) {
- char * tmp = NULL;
- /* if we get conn->user as parameter and then we first free it, then estrndup it, we will crash */
- tmp = mnd_pestrndup(user, user_len, conn->persistent);
- if (conn->user) {
- mnd_pefree(conn->user, conn->persistent);
- }
- conn->user = tmp;
-
- tmp = mnd_pestrdup(passwd, conn->persistent);
- if (conn->passwd) {
- mnd_pefree(conn->passwd, conn->persistent);
- }
- conn->passwd = tmp;
- if (conn->last_message) {
- mnd_pefree(conn->last_message, conn->persistent);
- conn->last_message = NULL;
+ {
+ char * switch_to_auth_protocol = NULL;
+ const char * requested_protocol = NULL;
+
+ do {
+ struct st_mysqlnd_authentication_plugin * auth_plugin;
+ char * plugin_name = NULL;
+ requested_protocol = switch_to_auth_protocol? switch_to_auth_protocol:
+ ((conn->server_capabilities & CLIENT_PLUGIN_AUTH)?
+ conn->options.auth_protocol:
+ "mysql_native_password"
+ );
+
+ spprintf(&plugin_name, 0, "auth_plugin_%s", requested_protocol);
+ DBG_INF_FMT("looking for %s auth plugin", plugin_name);
+ auth_plugin = mysqlnd_plugin_find(plugin_name);
+ efree(plugin_name);
+ if (!auth_plugin) {
+ if (!silent) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Server requested authentication method uknown to the client [%s]", requested_protocol);
+ }
+ SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Server requested authentication method uknown to the client");
+ break;
+ }
+ DBG_INF("plugin found");
+ ret = auth_plugin->methods.auth_change_user(conn, user, strlen(user), passwd, db, strlen(db), silent, &switch_to_auth_protocol TSRMLS_CC);
+ DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
+ } while (ret == FAIL && switch_to_auth_protocol != NULL);
+ if (ret == PASS) {
+ conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol TSRMLS_CC);
}
- memset(&conn->upsert_status, 0, sizeof(conn->upsert_status));
- /* set charset for old servers */
- if (mysqlnd_get_server_version(conn) < 50123) {
- ret = conn->m->set_charset(conn, old_cs->name TSRMLS_CC);
+ if (switch_to_auth_protocol) {
+ mnd_efree(switch_to_auth_protocol);
+ switch_to_auth_protocol = NULL;
}
- } else if (ret == FAIL && chg_user_resp->server_asked_323_auth == TRUE) {
- /* old authentication with new server !*/
- DBG_ERR(mysqlnd_old_passwd);
- SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
}
-end:
- PACKET_FREE(auth_packet);
- PACKET_FREE(chg_user_resp);
-
/*
Here we should close all statements. Unbuffered queries should not be a
problem as we won't allow sending COM_CHANGE_USER.
/* todo: throw an error, we don't support embedded */
break;
#endif
-
+ case MYSQLND_OPT_MAX_ALLOWED_PACKET:
+ if (*(unsigned int*) value > (1<<16)) {
+ conn->options.max_allowed_packet = *(unsigned int*) value;
+ }
+ break;
+ case MYSQLND_OPT_AUTH_PROTOCOL:
+ {
+ char * new_auth_protocol = mnd_pestrdup(value, conn->persistent);
+ DBG_INF("MYSQLND_OPT_AUTH_PROTOCOL");
+ if (!new_auth_protocol) {
+ goto oom;
+ }
+ if (conn->options.auth_protocol) {
+ mnd_pefree(conn->options.auth_protocol, conn->persistent);
+ }
+ conn->options.auth_protocol = new_auth_protocol;
+ DBG_INF_FMT("auth_protocol=%s", conn->options.auth_protocol);
+ break;
+ }
#ifdef WHEN_SUPPORTED_BY_MYSQLI
case MYSQL_SHARED_MEMORY_BASE_NAME:
case MYSQL_OPT_USE_RESULT:
}
mysqlnd_example_plugin_register(TSRMLS_C);
mysqlnd_debug_trace_plugin_register(TSRMLS_C);
+ mysqlnd_native_authentication_plugin_register(TSRMLS_C);
}
}
/* }}} */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2011 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: mysqlnd.c 307377 2011-01-11 13:02:57Z andrey $ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_structs.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
+#include "mysqlnd_charset.h"
+#include "mysqlnd_debug.h"
+
+
+/* {{{ mysqlnd_native_auth_handshake */
+static enum_func_status
+mysqlnd_native_auth_handshake(MYSQLND * conn,
+ const char * const user,
+ const char * const passwd,
+ const char * const db,
+ const size_t db_len,
+ const MYSQLND_PACKET_GREET * const greet_packet,
+ const MYSQLND_OPTIONS * const options,
+ unsigned long mysql_flags,
+ char ** switch_to_auth_protocol
+ TSRMLS_DC)
+{
+ enum_func_status ret = FAIL;
+ const MYSQLND_CHARSET * charset = NULL;
+ MYSQLND_PACKET_OK * ok_packet = NULL;
+ MYSQLND_PACKET_AUTH * auth_packet = NULL;
+
+ DBG_ENTER("mysqlnd_native_auth_handshake");
+
+ /* 5.5.x reports 21 as scramble length because it needs to show the length of the data before the plugin name */
+ if (greet_packet->scramble_buf_len != SCRAMBLE_LENGTH && (greet_packet->scramble_buf_len != 21)) {
+ /* mysql_native_password only works with SCRAMBLE_LENGTH scramble */
+ SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "The server sent wrong length for scramble");
+ DBG_ERR_FMT("The server sent wrong length for scramble %u. Expected %u", greet_packet->scramble_buf_len, SCRAMBLE_LENGTH);
+ goto end;
+ }
+
+ ok_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
+ auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
+
+ if (!ok_packet || !auth_packet) {
+ SET_OOM_ERROR(conn->error_info);
+ goto end;
+ }
+
+ auth_packet->client_flags = mysql_flags;
+ auth_packet->max_packet_size = options->max_allowed_packet;
+ if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
+ auth_packet->charset_no = charset->nr;
+ } else {
+#if MYSQLND_UNICODE
+ auth_packet->charset_no = 200;/* utf8 - swedish collation, check mysqlnd_charset.c */
+#else
+ auth_packet->charset_no = greet_packet->charset_no;
+#endif
+ }
+
+ auth_packet->send_auth_data = TRUE;
+ auth_packet->user = user;
+ auth_packet->password = passwd;
+ auth_packet->db = db;
+ auth_packet->db_len = db_len;
+
+ auth_packet->server_scramble_buf_len = greet_packet->scramble_buf_len;
+ conn->scramble = auth_packet->server_scramble_buf = mnd_pemalloc(auth_packet->server_scramble_buf_len, conn->persistent);
+ if (!conn->scramble) {
+ SET_OOM_ERROR(conn->error_info);
+ goto end;
+ }
+ memcpy(auth_packet->server_scramble_buf, greet_packet->scramble_buf, greet_packet->scramble_buf_len);
+
+ 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 end;
+ }
+
+ if (FAIL == PACKET_READ(ok_packet, conn) || ok_packet->field_count >= 0xFE) {
+ if (ok_packet->field_count == 0xFE) {
+ /* old authentication with new server !*/
+ DBG_ERR(mysqlnd_old_passwd);
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
+ } else if (ok_packet->field_count == 0xFF) {
+ if (ok_packet->sqlstate[0]) {
+ strlcpy(conn->error_info.sqlstate, ok_packet->sqlstate, sizeof(conn->error_info.sqlstate));
+ DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", ok_packet->error_no, ok_packet->sqlstate, ok_packet->error);
+ }
+ conn->error_info.error_no = ok_packet->error_no;
+ strlcpy(conn->error_info.error, ok_packet->error, sizeof(conn->error_info.error));
+ }
+ goto end;
+ }
+
+ SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, ok_packet->message, ok_packet->message_len, conn->persistent);
+ conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no);
+ ret = PASS;
+end:
+ PACKET_FREE(auth_packet);
+ PACKET_FREE(ok_packet);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_native_auth_change_user */
+static enum_func_status
+mysqlnd_native_auth_change_user(MYSQLND * const conn,
+ const char * const user,
+ const size_t user_len,
+ const char * const passwd,
+ const char * const db,
+ const size_t db_len,
+ const zend_bool silent,
+ char ** switch_to_auth_protocol
+ TSRMLS_DC)
+{
+ /*
+ User could be max 16 * 3 (utf8), pass is 20 usually, db is up to 64*3
+ Stack space is not that expensive, so use a bit more to be protected against
+ buffer overflows.
+ */
+ enum_func_status ret = FAIL;
+ char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 2 /* charset*/ ];
+ char *p = buffer;
+ const MYSQLND_CHARSET * old_cs = conn->charset;
+ MYSQLND_PACKET_AUTH * auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
+ MYSQLND_PACKET_CHG_USER_RESPONSE * chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE TSRMLS_CC);
+
+ DBG_ENTER("mysqlnd_native_auth_change_user");
+
+ if (!auth_packet || !chg_user_resp) {
+ SET_OOM_ERROR(conn->error_info);
+ goto end;
+ }
+ auth_packet->is_change_user_packet = TRUE;
+ auth_packet->user = user;
+ auth_packet->password = passwd;
+ auth_packet->db = db;
+ auth_packet->db_len = db_len;
+ auth_packet->server_scramble_buf = conn->scramble;
+ auth_packet->silent = silent;
+ if (mysqlnd_get_server_version(conn) >= 50123) {
+ auth_packet->charset_no = conn->charset->nr;
+ p+=2;
+ }
+
+ 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 end;
+ }
+
+ ret = PACKET_READ(chg_user_resp, conn);
+ conn->error_info = chg_user_resp->error_info;
+
+ if (conn->error_info.error_no) {
+ ret = FAIL;
+ /*
+ COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune.
+ bug#25371 mysql_change_user() triggers "packets out of sync"
+ When it gets fixed, there should be one more check here
+ */
+ if (mysqlnd_get_server_version(conn) > 50113L && mysqlnd_get_server_version(conn) < 50118L) {
+ MYSQLND_PACKET_OK * redundant_error_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (redundant_error_packet) {
+ PACKET_READ(redundant_error_packet, conn);
+ PACKET_FREE(redundant_error_packet);
+ DBG_INF_FMT("Server is %u, buggy, sends two ERR messages", mysqlnd_get_server_version(conn));
+ } else {
+ SET_OOM_ERROR(conn->error_info);
+ }
+ }
+ }
+ if (ret == PASS) {
+ char * tmp = NULL;
+ /* if we get conn->user as parameter and then we first free it, then estrndup it, we will crash */
+ tmp = mnd_pestrndup(user, user_len, conn->persistent);
+ if (conn->user) {
+ mnd_pefree(conn->user, conn->persistent);
+ }
+ conn->user = tmp;
+
+ tmp = mnd_pestrdup(passwd, conn->persistent);
+ if (conn->passwd) {
+ mnd_pefree(conn->passwd, conn->persistent);
+ }
+ conn->passwd = tmp;
+
+ if (conn->last_message) {
+ mnd_pefree(conn->last_message, conn->persistent);
+ conn->last_message = NULL;
+ }
+ memset(&conn->upsert_status, 0, sizeof(conn->upsert_status));
+ /* set charset for old servers */
+ if (mysqlnd_get_server_version(conn) < 50123) {
+ ret = conn->m->set_charset(conn, old_cs->name TSRMLS_CC);
+ }
+ } else if (ret == FAIL && chg_user_resp->server_asked_323_auth == TRUE) {
+ /* old authentication with new server !*/
+ DBG_ERR(mysqlnd_old_passwd);
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
+ }
+end:
+ PACKET_FREE(auth_packet);
+ PACKET_FREE(chg_user_resp);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+static struct st_mysqlnd_authentication_plugin mysqlnd_native_auth_plugin =
+{
+ {
+ MYSQLND_PLUGIN_API_VERSION,
+ "auth_plugin_mysql_native_password",
+ MYSQLND_VERSION_ID,
+ MYSQLND_VERSION,
+ "PHP License 3.01",
+ "Andrey Hristov <andrey@mysql.com>, Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
+ {
+ NULL, /* no statistics , will be filled later if there are some */
+ NULL, /* no statistics */
+ },
+ {
+ NULL /* plugin shutdown */
+ }
+ },
+ {/* methods */
+ mysqlnd_native_auth_handshake,
+ mysqlnd_native_auth_change_user
+ }
+};
+
+/* {{{ mysqlnd_native_authentication_plugin_register */
+void
+mysqlnd_native_authentication_plugin_register(TSRMLS_D)
+{
+ mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_native_auth_plugin TSRMLS_CC);
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
#define MYSQLND_MAX_PACKET_SIZE (256L*256L*256L-1)
+#define MYSQLND_ASSEMBLED_PACKET_MAX_SIZE 3UL*1024UL*1024UL*1024UL
+
#define MYSQLND_ERRMSG_SIZE 512
#define MYSQLND_SQLSTATE_LENGTH 5
#define MYSQLND_SQLSTATE_NULL "00000"
#define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
+#define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \
+ CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \
+ CLIENT_MULTI_RESULTS | CLIENT_PS_MULTI_RESULTS | CLIENT_LOCAL_FILES)
#define MYSQLND_NET_FLAG_USE_COMPRESSION 1
MYSQLND_OPT_SSL_CA = 206,
MYSQLND_OPT_SSL_CAPATH = 207,
MYSQLND_OPT_SSL_CIPHER = 208,
- MYSQLND_OPT_SSL_PASSPHRASE = 209
+ MYSQLND_OPT_SSL_PASSPHRASE = 209,
+ MYSQLND_OPT_MAX_ALLOWED_PACKET = 210,
+ MYSQLND_OPT_AUTH_PROTOCOL = 211
} enum_mysqlnd_option;
typedef enum mysqlnd_protocol_type
void mysqlnd_plugin_subsystem_init(TSRMLS_D);
void mysqlnd_plugin_subsystem_end(TSRMLS_D);
-
+void mysqlnd_native_authentication_plugin_register(TSRMLS_D);
void mysqlnd_example_plugin_register(TSRMLS_D);
char *cfg_file;
char *cfg_section;
+ char *auth_protocol;
/*
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;
unsigned int counter;
};
+struct st_mysqlnd_packet_greet;
+
+struct st_mysqlnd_authentication_plugin
+{
+ struct st_mysqlnd_plugin_header plugin_header;
+ struct {
+ enum_func_status (*auth_handshake)(MYSQLND * conn, const char * const user, const char * const passwd, const char * const db,
+ const size_t db_len, const struct st_mysqlnd_packet_greet * const greet_packet,
+ const MYSQLND_OPTIONS * const options, unsigned long mysql_flags,
+ char ** switch_to_auth_protocol TSRMLS_DC);
+
+ enum_func_status (*auth_change_user)(MYSQLND * const conn, const char * const user, const size_t user_len, const char * const passwd,
+ const char * const db, const size_t db_len, const zend_bool silent,
+ char ** switch_to_auth_protocol TSRMLS_DC);
+ } methods;
+};
+
#endif /* MYSQLND_STRUCTS_H */
zend_uchar buf[2048];
zend_uchar *p = buf;
zend_uchar *begin = buf;
+ zend_uchar *pad_start = NULL;
MYSQLND_PACKET_GREET *packet= (MYSQLND_PACKET_GREET *) _packet;
DBG_ENTER("php_mysqlnd_greet_read");
PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting", PROT_GREET_PACKET);
BAIL_IF_NO_MORE_DATA;
+ packet->scramble_buf = &packet->intern_scramble_buf;
+ packet->scramble_buf_len = sizeof(packet->intern_scramble_buf);
+
+ if (packet->header.size < sizeof(buf)) {
+ /*
+ Null-terminate the string, so strdup can work even if the packets have a string at the end,
+ which is not ASCIIZ
+ */
+ buf[packet->header.size] = '\0';
+ }
+
packet->protocol_version = uint1korr(p);
p++;
BAIL_IF_NO_MORE_DATA;
BAIL_IF_NO_MORE_DATA;
memcpy(packet->scramble_buf, p, SCRAMBLE_LENGTH_323);
- p+= 8;
+ p+= SCRAMBLE_LENGTH_323;
BAIL_IF_NO_MORE_DATA;
/* pad1 */
BAIL_IF_NO_MORE_DATA;
/* pad2 */
+ pad_start = p;
p+= 13;
BAIL_IF_NO_MORE_DATA;
if ((size_t) (p - buf) < packet->header.size) {
/* scramble_buf is split into two parts */
- memcpy(packet->scramble_buf + SCRAMBLE_LENGTH_323,
- p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
+ memcpy(packet->scramble_buf + SCRAMBLE_LENGTH_323, p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
+ p+= SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323;
+ p++; /* 0x0 at the end of the scramble and thus last byte in the packet in 5.1 and previous */
} else {
packet->pre41 = TRUE;
}
+ /* Is this a 5.5+ server ? */
+ if ((size_t) (p - buf) < packet->header.size) {
+ /* backtrack one byte, the 0x0 at the end of the scramble in 5.1 and previous */
+ p--;
+
+ /* Additional 16 bits for server capabilities */
+ packet->server_capabilities |= uint2korr(pad_start) << 16;
+ /* And a length of the server scramble in one byte */
+ packet->scramble_buf_len = uint1korr(pad_start + 2);
+ if (packet->scramble_buf_len > SCRAMBLE_LENGTH) {
+ /* more data*/
+ char * new_scramble_buf = emalloc(packet->scramble_buf_len);
+ if (!new_scramble_buf) {
+ goto premature_end;
+ }
+ /* copy what we already have */
+ memcpy(new_scramble_buf, packet->scramble_buf, SCRAMBLE_LENGTH);
+ /* add additional scramble data 5.5+ sent us */
+ memcpy(new_scramble_buf + SCRAMBLE_LENGTH, p, packet->scramble_buf_len - SCRAMBLE_LENGTH);
+ p+= (packet->scramble_buf_len - SCRAMBLE_LENGTH);
+ packet->scramble_buf = new_scramble_buf;
+ }
+ }
+
+ if (packet->server_capabilities & CLIENT_PLUGIN_AUTH) {
+ BAIL_IF_NO_MORE_DATA;
+ /* The server is 5.5.x and supports authentication plugins */
+ packet->auth_protocol = estrdup((char *)p);
+ p+= strlen(packet->auth_protocol) + 1; /* eat the '\0' */
+ }
+
DBG_INF_FMT("proto=%u server=%s thread_id=%u",
packet->protocol_version, packet->server_version, packet->thread_id);
- DBG_INF_FMT("server_capabilities=%u charset_no=%u server_status=%i",
- packet->server_capabilities, packet->charset_no, packet->server_status);
+ DBG_INF_FMT("server_capabilities=%u charset_no=%u server_status=%i auth_protocol=%s scramble_length=%u",
+ packet->server_capabilities, packet->charset_no, packet->server_status,
+ packet->auth_protocol? packet->auth_protocol:"n/a", packet->scramble_buf_len);
DBG_RETURN(PASS);
premature_end:
efree(p->server_version);
p->server_version = NULL;
}
+ if (p->scramble_buf && p->scramble_buf != &p->intern_scramble_buf) {
+ efree(p->scramble_buf);
+ p->scramble_buf = NULL;
+ }
+ if (p->auth_protocol) {
+ efree(p->auth_protocol);
+ p->auth_protocol = NULL;
+ }
if (!stack_allocation) {
mnd_pefree(p, p->header.persistent);
}
zend_uchar sha1[SHA1_MAX_LENGTH];
zend_uchar sha2[SHA1_MAX_LENGTH];
-
/* Phase 1: hash password */
PHP_SHA1Init(&context);
PHP_SHA1Update(&context, password, strlen((char *)password));
uint8_t protocol_version;
char *server_version;
uint32_t thread_id;
- zend_uchar scramble_buf[SCRAMBLE_LENGTH];
+ zend_uchar intern_scramble_buf[SCRAMBLE_LENGTH];
+ zend_uchar * scramble_buf;
+ size_t scramble_buf_len;
/* 1 byte pad */
- uint16_t server_capabilities;
+ uint32_t server_capabilities;
uint8_t charset_no;
uint16_t server_status;
- /* 13 byte pad*/
+ /* 13 byte pad, in 5.5 first 2 bytes are more capabilities followed by 1 byte scramble_length */
zend_bool pre41;
/* If error packet, we use these */
char error[MYSQLND_ERRMSG_SIZE+1];
char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1];
unsigned int error_no;
+ char *auth_protocol;
} MYSQLND_PACKET_GREET;
const char *password;
/* +1 for \0 because of scramble() */
unsigned char *server_scramble_buf;
+ size_t server_scramble_buf_len;
size_t db_len;
zend_bool send_auth_data;
zend_bool is_change_user_packet;