Busybox: Upgrade to 1.21.1 (stable). lsof active.
[tomato.git] / release / src / router / php / ext / mysqlnd / mysqlnd_auth.c
blob918697a5f52d4cf9f2cf40d535431e7d31324962
1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2013 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Georg Richter <georg@mysql.com> |
16 | Andrey Hristov <andrey@mysql.com> |
17 | Ulf Wendel <uwendel@mysql.com> |
18 +----------------------------------------------------------------------+
21 /* $Id: mysqlnd.c 307377 2011-01-11 13:02:57Z andrey $ */
22 #include "php.h"
23 #include "mysqlnd.h"
24 #include "mysqlnd_structs.h"
25 #include "mysqlnd_wireprotocol.h"
26 #include "mysqlnd_priv.h"
27 #include "mysqlnd_result.h"
28 #include "mysqlnd_charset.h"
29 #include "mysqlnd_debug.h"
32 /* {{{ mysqlnd_auth_handshake */
33 enum_func_status
34 mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
35 const char * const user,
36 const char * const passwd,
37 const size_t passwd_len,
38 const char * const db,
39 const size_t db_len,
40 const MYSQLND_OPTIONS * const options,
41 unsigned long mysql_flags,
42 unsigned int server_charset_no,
43 zend_bool use_full_blown_auth_packet,
44 const char * const auth_protocol,
45 const zend_uchar * const auth_plugin_data,
46 const size_t auth_plugin_data_len,
47 char ** switch_to_auth_protocol,
48 size_t * switch_to_auth_protocol_len,
49 zend_uchar ** switch_to_auth_protocol_data,
50 size_t * switch_to_auth_protocol_data_len
51 TSRMLS_DC)
53 enum_func_status ret = FAIL;
54 const MYSQLND_CHARSET * charset = NULL;
55 MYSQLND_PACKET_CHANGE_AUTH_RESPONSE * change_auth_resp_packet = NULL;
56 MYSQLND_PACKET_AUTH_RESPONSE * auth_resp_packet = NULL;
57 MYSQLND_PACKET_AUTH * auth_packet = NULL;
59 DBG_ENTER("mysqlnd_auth_handshake");
61 auth_resp_packet = conn->protocol->m.get_auth_response_packet(conn->protocol, FALSE TSRMLS_CC);
63 if (!auth_resp_packet) {
64 SET_OOM_ERROR(*conn->error_info);
65 goto end;
68 if (use_full_blown_auth_packet != TRUE) {
69 change_auth_resp_packet = conn->protocol->m.get_change_auth_response_packet(conn->protocol, FALSE TSRMLS_CC);
70 if (!change_auth_resp_packet) {
71 SET_OOM_ERROR(*conn->error_info);
72 goto end;
75 change_auth_resp_packet->auth_data = auth_plugin_data;
76 change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
78 if (!PACKET_WRITE(change_auth_resp_packet, conn)) {
79 CONN_SET_STATE(conn, CONN_QUIT_SENT);
80 SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
81 goto end;
83 } else {
84 auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
86 auth_packet->client_flags = mysql_flags;
87 auth_packet->max_packet_size = options->max_allowed_packet;
88 if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
89 auth_packet->charset_no = charset->nr;
90 } else {
91 #if MYSQLND_UNICODE
92 auth_packet->charset_no = 200;/* utf8 - swedish collation, check mysqlnd_charset.c */
93 #else
94 auth_packet->charset_no = server_charset_no;
95 #endif
98 auth_packet->send_auth_data = TRUE;
99 auth_packet->user = user;
100 auth_packet->db = db;
101 auth_packet->db_len = db_len;
103 auth_packet->auth_data = auth_plugin_data;
104 auth_packet->auth_data_len = auth_plugin_data_len;
105 auth_packet->auth_plugin_name = auth_protocol;
107 if (!PACKET_WRITE(auth_packet, conn)) {
108 goto end;
111 if (use_full_blown_auth_packet == TRUE) {
112 conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no);
115 if (FAIL == PACKET_READ(auth_resp_packet, conn) || auth_resp_packet->response_code >= 0xFE) {
116 if (auth_resp_packet->response_code == 0xFE) {
117 /* old authentication with new server !*/
118 if (!auth_resp_packet->new_auth_protocol) {
119 DBG_ERR(mysqlnd_old_passwd);
120 SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
121 } else {
122 *switch_to_auth_protocol = mnd_pestrndup(auth_resp_packet->new_auth_protocol, auth_resp_packet->new_auth_protocol_len, FALSE);
123 *switch_to_auth_protocol_len = auth_resp_packet->new_auth_protocol_len;
124 if (auth_resp_packet->new_auth_protocol_data) {
125 *switch_to_auth_protocol_data_len = auth_resp_packet->new_auth_protocol_data_len;
126 *switch_to_auth_protocol_data = mnd_emalloc(*switch_to_auth_protocol_data_len);
127 memcpy(*switch_to_auth_protocol_data, auth_resp_packet->new_auth_protocol_data, *switch_to_auth_protocol_data_len);
128 } else {
129 *switch_to_auth_protocol_data = NULL;
130 *switch_to_auth_protocol_data_len = 0;
133 } else if (auth_resp_packet->response_code == 0xFF) {
134 if (auth_resp_packet->sqlstate[0]) {
135 strlcpy(conn->error_info->sqlstate, auth_resp_packet->sqlstate, sizeof(conn->error_info->sqlstate));
136 DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", auth_resp_packet->error_no, auth_resp_packet->sqlstate, auth_resp_packet->error);
138 SET_CLIENT_ERROR(*conn->error_info, auth_resp_packet->error_no, UNKNOWN_SQLSTATE, auth_resp_packet->error);
140 goto end;
143 SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, auth_resp_packet->message, auth_resp_packet->message_len, conn->persistent);
144 ret = PASS;
145 end:
146 PACKET_FREE(change_auth_resp_packet);
147 PACKET_FREE(auth_packet);
148 PACKET_FREE(auth_resp_packet);
149 DBG_RETURN(ret);
151 /* }}} */
154 /* {{{ mysqlnd_auth_change_user */
155 enum_func_status
156 mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
157 const char * const user,
158 const size_t user_len,
159 const char * const passwd,
160 const size_t passwd_len,
161 const char * const db,
162 const size_t db_len,
163 const zend_bool silent,
164 zend_bool use_full_blown_auth_packet,
165 const char * const auth_protocol,
166 zend_uchar * auth_plugin_data,
167 size_t auth_plugin_data_len,
168 char ** switch_to_auth_protocol,
169 size_t * switch_to_auth_protocol_len,
170 zend_uchar ** switch_to_auth_protocol_data,
171 size_t * switch_to_auth_protocol_data_len
172 TSRMLS_DC)
174 enum_func_status ret = FAIL;
175 const MYSQLND_CHARSET * old_cs = conn->charset;
176 MYSQLND_PACKET_CHANGE_AUTH_RESPONSE * change_auth_resp_packet = NULL;
177 MYSQLND_PACKET_CHG_USER_RESPONSE * chg_user_resp = NULL;
178 MYSQLND_PACKET_AUTH * auth_packet = NULL;
180 DBG_ENTER("mysqlnd_auth_change_user");
182 chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE TSRMLS_CC);
184 if (!chg_user_resp) {
185 SET_OOM_ERROR(*conn->error_info);
186 goto end;
189 if (use_full_blown_auth_packet != TRUE) {
190 change_auth_resp_packet = conn->protocol->m.get_change_auth_response_packet(conn->protocol, FALSE TSRMLS_CC);
191 if (!change_auth_resp_packet) {
192 SET_OOM_ERROR(*conn->error_info);
193 goto end;
196 change_auth_resp_packet->auth_data = auth_plugin_data;
197 change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
199 if (!PACKET_WRITE(change_auth_resp_packet, conn)) {
200 CONN_SET_STATE(conn, CONN_QUIT_SENT);
201 SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
202 goto end;
204 } else {
205 auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
207 if (!auth_packet) {
208 SET_OOM_ERROR(*conn->error_info);
209 goto end;
212 auth_packet->is_change_user_packet = TRUE;
213 auth_packet->user = user;
214 auth_packet->db = db;
215 auth_packet->db_len = db_len;
216 auth_packet->silent = silent;
218 auth_packet->auth_data = auth_plugin_data;
219 auth_packet->auth_data_len = auth_plugin_data_len;
220 auth_packet->auth_plugin_name = auth_protocol;
223 if (conn->m->get_server_version(conn TSRMLS_CC) >= 50123) {
224 auth_packet->charset_no = conn->charset->nr;
227 if (!PACKET_WRITE(auth_packet, conn)) {
228 CONN_SET_STATE(conn, CONN_QUIT_SENT);
229 SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
230 goto end;
234 ret = PACKET_READ(chg_user_resp, conn);
235 COPY_CLIENT_ERROR(*conn->error_info, chg_user_resp->error_info);
237 if (0xFE == chg_user_resp->response_code) {
238 ret = FAIL;
239 if (!chg_user_resp->new_auth_protocol) {
240 DBG_ERR(mysqlnd_old_passwd);
241 SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
242 } else {
243 *switch_to_auth_protocol = mnd_pestrndup(chg_user_resp->new_auth_protocol, chg_user_resp->new_auth_protocol_len, FALSE);
244 *switch_to_auth_protocol_len = chg_user_resp->new_auth_protocol_len;
245 if (chg_user_resp->new_auth_protocol_data) {
246 *switch_to_auth_protocol_data_len = chg_user_resp->new_auth_protocol_data_len;
247 *switch_to_auth_protocol_data = mnd_emalloc(*switch_to_auth_protocol_data_len);
248 memcpy(*switch_to_auth_protocol_data, chg_user_resp->new_auth_protocol_data, *switch_to_auth_protocol_data_len);
249 } else {
250 *switch_to_auth_protocol_data = NULL;
251 *switch_to_auth_protocol_data_len = 0;
256 if (conn->error_info->error_no) {
257 ret = FAIL;
259 COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune.
260 bug#25371 mysql_change_user() triggers "packets out of sync"
261 When it gets fixed, there should be one more check here
263 if (conn->m->get_server_version(conn TSRMLS_CC) > 50113L &&conn->m->get_server_version(conn TSRMLS_CC) < 50118L) {
264 MYSQLND_PACKET_OK * redundant_error_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
265 if (redundant_error_packet) {
266 PACKET_READ(redundant_error_packet, conn);
267 PACKET_FREE(redundant_error_packet);
268 DBG_INF_FMT("Server is %u, buggy, sends two ERR messages", conn->m->get_server_version(conn TSRMLS_CC));
269 } else {
270 SET_OOM_ERROR(*conn->error_info);
274 if (ret == PASS) {
275 char * tmp = NULL;
276 /* if we get conn->user as parameter and then we first free it, then estrndup it, we will crash */
277 tmp = mnd_pestrndup(user, user_len, conn->persistent);
278 if (conn->user) {
279 mnd_pefree(conn->user, conn->persistent);
281 conn->user = tmp;
283 tmp = mnd_pestrdup(passwd, conn->persistent);
284 if (conn->passwd) {
285 mnd_pefree(conn->passwd, conn->persistent);
287 conn->passwd = tmp;
289 if (conn->last_message) {
290 mnd_pefree(conn->last_message, conn->persistent);
291 conn->last_message = NULL;
293 memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
294 /* set charset for old servers */
295 if (conn->m->get_server_version(conn TSRMLS_CC) < 50123) {
296 ret = conn->m->set_charset(conn, old_cs->name TSRMLS_CC);
298 } else if (ret == FAIL && chg_user_resp->server_asked_323_auth == TRUE) {
299 /* old authentication with new server !*/
300 DBG_ERR(mysqlnd_old_passwd);
301 SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
303 end:
304 PACKET_FREE(change_auth_resp_packet);
305 PACKET_FREE(auth_packet);
306 PACKET_FREE(chg_user_resp);
307 DBG_RETURN(ret);
309 /* }}} */
312 /******************************************* MySQL Native Password ***********************************/
314 #include "ext/standard/sha1.h"
316 /* {{{ php_mysqlnd_crypt */
317 static void
318 php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len)
320 const zend_uchar *s1_end = s1 + len;
321 while (s1 < s1_end) {
322 *buffer++= *s1++ ^ *s2++;
325 /* }}} */
328 /* {{{ php_mysqlnd_scramble */
329 void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password, size_t password_len)
331 PHP_SHA1_CTX context;
332 zend_uchar sha1[SHA1_MAX_LENGTH];
333 zend_uchar sha2[SHA1_MAX_LENGTH];
335 /* Phase 1: hash password */
336 PHP_SHA1Init(&context);
337 PHP_SHA1Update(&context, password, password_len);
338 PHP_SHA1Final(sha1, &context);
340 /* Phase 2: hash sha1 */
341 PHP_SHA1Init(&context);
342 PHP_SHA1Update(&context, (zend_uchar*)sha1, SHA1_MAX_LENGTH);
343 PHP_SHA1Final(sha2, &context);
345 /* Phase 3: hash scramble + sha2 */
346 PHP_SHA1Init(&context);
347 PHP_SHA1Update(&context, scramble, SCRAMBLE_LENGTH);
348 PHP_SHA1Update(&context, (zend_uchar*)sha2, SHA1_MAX_LENGTH);
349 PHP_SHA1Final(buffer, &context);
351 /* let's crypt buffer now */
352 php_mysqlnd_crypt(buffer, (const zend_uchar *)buffer, (const zend_uchar *)sha1, SHA1_MAX_LENGTH);
354 /* }}} */
357 /* {{{ mysqlnd_native_auth_get_auth_data */
358 static zend_uchar *
359 mysqlnd_native_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
360 size_t * auth_data_len,
361 MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
362 const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
363 const MYSQLND_OPTIONS * const options, unsigned long mysql_flags
364 TSRMLS_DC)
366 zend_uchar * ret = NULL;
367 DBG_ENTER("mysqlnd_native_auth_get_auth_data");
368 *auth_data_len = 0;
370 /* 5.5.x reports 21 as scramble length because it needs to show the length of the data before the plugin name */
371 if (auth_plugin_data_len < SCRAMBLE_LENGTH) {
372 /* mysql_native_password only works with SCRAMBLE_LENGTH scramble */
373 SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "The server sent wrong length for scramble");
374 DBG_ERR_FMT("The server sent wrong length for scramble %u. Expected %u", auth_plugin_data_len, SCRAMBLE_LENGTH);
375 DBG_RETURN(NULL);
378 /* copy scrambled pass*/
379 if (passwd && passwd_len) {
380 ret = malloc(SCRAMBLE_LENGTH);
381 *auth_data_len = SCRAMBLE_LENGTH;
382 /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */
383 php_mysqlnd_scramble((zend_uchar*)ret, auth_plugin_data, (zend_uchar*)passwd, passwd_len);
385 DBG_RETURN(ret);
387 /* }}} */
390 static struct st_mysqlnd_authentication_plugin mysqlnd_native_auth_plugin =
393 MYSQLND_PLUGIN_API_VERSION,
394 "auth_plugin_mysql_native_password",
395 MYSQLND_VERSION_ID,
396 MYSQLND_VERSION,
397 "PHP License 3.01",
398 "Andrey Hristov <andrey@mysql.com>, Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
400 NULL, /* no statistics , will be filled later if there are some */
401 NULL, /* no statistics */
404 NULL /* plugin shutdown */
407 {/* methods */
408 mysqlnd_native_auth_get_auth_data
413 /******************************************* PAM Authentication ***********************************/
415 /* {{{ mysqlnd_pam_auth_get_auth_data */
416 static zend_uchar *
417 mysqlnd_pam_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
418 size_t * auth_data_len,
419 MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
420 const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
421 const MYSQLND_OPTIONS * const options, unsigned long mysql_flags
422 TSRMLS_DC)
424 zend_uchar * ret = NULL;
426 /* copy pass*/
427 if (passwd && passwd_len) {
428 ret = (zend_uchar*) zend_strndup(passwd, passwd_len);
430 *auth_data_len = passwd_len;
432 return ret;
434 /* }}} */
437 static struct st_mysqlnd_authentication_plugin mysqlnd_pam_authentication_plugin =
440 MYSQLND_PLUGIN_API_VERSION,
441 "auth_plugin_mysql_clear_password",
442 MYSQLND_VERSION_ID,
443 MYSQLND_VERSION,
444 "PHP License 3.01",
445 "Andrey Hristov <andrey@mysql.com>, Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
447 NULL, /* no statistics , will be filled later if there are some */
448 NULL, /* no statistics */
451 NULL /* plugin shutdown */
454 {/* methods */
455 mysqlnd_pam_auth_get_auth_data
460 /* {{{ mysqlnd_register_builtin_authentication_plugins */
461 void
462 mysqlnd_register_builtin_authentication_plugins(TSRMLS_D)
464 mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_native_auth_plugin TSRMLS_CC);
465 mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_pam_authentication_plugin TSRMLS_CC);
467 /* }}} */
471 * Local variables:
472 * tab-width: 4
473 * c-basic-offset: 4
474 * End:
475 * vim600: noet sw=4 ts=4 fdm=marker
476 * vim<600: noet sw=4 ts=4