2 +----------------------------------------------------------------------+
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 $ */
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 */
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
,
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
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
);
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
);
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
);
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
;
92 auth_packet
->charset_no
= 200;/* utf8 - swedish collation, check mysqlnd_charset.c */
94 auth_packet
->charset_no
= server_charset_no
;
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
)) {
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
);
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
);
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
);
143 SET_NEW_MESSAGE(conn
->last_message
, conn
->last_message_len
, auth_resp_packet
->message
, auth_resp_packet
->message_len
, conn
->persistent
);
146 PACKET_FREE(change_auth_resp_packet
);
147 PACKET_FREE(auth_packet
);
148 PACKET_FREE(auth_resp_packet
);
154 /* {{{ mysqlnd_auth_change_user */
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
,
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
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
);
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
);
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
);
205 auth_packet
= conn
->protocol
->m
.get_auth_packet(conn
->protocol
, FALSE TSRMLS_CC
);
208 SET_OOM_ERROR(*conn
->error_info
);
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
);
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
) {
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
);
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
);
250 *switch_to_auth_protocol_data
= NULL
;
251 *switch_to_auth_protocol_data_len
= 0;
256 if (conn
->error_info
->error_no
) {
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
));
270 SET_OOM_ERROR(*conn
->error_info
);
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
);
279 mnd_pefree(conn
->user
, conn
->persistent
);
283 tmp
= mnd_pestrdup(passwd
, conn
->persistent
);
285 mnd_pefree(conn
->passwd
, conn
->persistent
);
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
);
304 PACKET_FREE(change_auth_resp_packet
);
305 PACKET_FREE(auth_packet
);
306 PACKET_FREE(chg_user_resp
);
312 /******************************************* MySQL Native Password ***********************************/
314 #include "ext/standard/sha1.h"
316 /* {{{ php_mysqlnd_crypt */
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
++;
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
);
357 /* {{{ mysqlnd_native_auth_get_auth_data */
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
366 zend_uchar
* ret
= NULL
;
367 DBG_ENTER("mysqlnd_native_auth_get_auth_data");
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
);
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
);
390 static struct st_mysqlnd_authentication_plugin mysqlnd_native_auth_plugin
=
393 MYSQLND_PLUGIN_API_VERSION
,
394 "auth_plugin_mysql_native_password",
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 */
408 mysqlnd_native_auth_get_auth_data
413 /******************************************* PAM Authentication ***********************************/
415 /* {{{ mysqlnd_pam_auth_get_auth_data */
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
424 zend_uchar
* ret
= NULL
;
427 if (passwd
&& passwd_len
) {
428 ret
= (zend_uchar
*) zend_strndup(passwd
, passwd_len
);
430 *auth_data_len
= passwd_len
;
437 static struct st_mysqlnd_authentication_plugin mysqlnd_pam_authentication_plugin
=
440 MYSQLND_PLUGIN_API_VERSION
,
441 "auth_plugin_mysql_clear_password",
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 */
455 mysqlnd_pam_auth_get_auth_data
460 /* {{{ mysqlnd_register_builtin_authentication_plugins */
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
);
475 * vim600: noet sw=4 ts=4 fdm=marker
476 * vim<600: noet sw=4 ts=4