summaryrefslogtreecommitdiff
path: root/ext/mysqlnd/mysqlnd.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/mysqlnd/mysqlnd.c')
-rw-r--r--ext/mysqlnd/mysqlnd.c264
1 files changed, 105 insertions, 159 deletions
diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c
index 6ee994c097..94cd5b89a8 100644
--- a/ext/mysqlnd/mysqlnd.c
+++ b/ext/mysqlnd/mysqlnd.c
@@ -92,6 +92,10 @@ MYSQLND_METHOD(mysqlnd_conn, free_options)(MYSQLND * conn TSRMLS_DC)
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++) {
@@ -427,9 +431,8 @@ MYSQLND_METHOD(mysqlnd_conn, end_psession)(MYSQLND * conn TSRMLS_DC)
/* }}} */
-#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,
@@ -438,13 +441,15 @@ mysqlnd_switch_to_ssl_if_needed(
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;
@@ -466,20 +471,20 @@ mysqlnd_switch_to_ssl_if_needed(
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);
}
/* }}} */
@@ -498,76 +503,52 @@ mysqlnd_connect_run_authentication(
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,
@@ -594,7 +575,7 @@ 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) {
@@ -618,6 +599,9 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn,
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]) {
@@ -714,7 +698,6 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn,
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) {
@@ -1936,15 +1919,7 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn,
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",
@@ -1952,11 +1927,6 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn,
SET_ERROR_AFF_ROWS(conn);
- if (!auth_packet) {
- SET_OOM_ERROR(conn->error_info);
- goto end;
- }
-
if (!user) {
user = "";
}
@@ -1966,86 +1936,43 @@ MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn,
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.
@@ -2165,7 +2092,25 @@ MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn,
/* 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:
@@ -2403,6 +2348,7 @@ PHPAPI void mysqlnd_library_init(TSRMLS_D)
}
mysqlnd_example_plugin_register(TSRMLS_C);
mysqlnd_debug_trace_plugin_register(TSRMLS_C);
+ mysqlnd_native_authentication_plugin_register(TSRMLS_C);
}
}
/* }}} */