Annotation of embedaddon/php/ext/mysqlnd/mysqlnd_auth.c, revision 1.1.1.2
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
1.1.1.2 ! misho 5: | Copyright (c) 2006-2013 The PHP Group |
1.1 misho 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: +----------------------------------------------------------------------+
19: */
20:
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"
30:
31:
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)
52: {
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;
58:
59: DBG_ENTER("mysqlnd_auth_handshake");
60:
61: auth_resp_packet = conn->protocol->m.get_auth_response_packet(conn->protocol, FALSE TSRMLS_CC);
62:
63: if (!auth_resp_packet) {
64: SET_OOM_ERROR(*conn->error_info);
65: goto end;
66: }
67:
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;
73: }
74:
75: change_auth_resp_packet->auth_data = auth_plugin_data;
76: change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
77:
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;
82: }
83: } else {
84: auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
85:
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
96: }
97:
98: auth_packet->send_auth_data = TRUE;
99: auth_packet->user = user;
100: auth_packet->db = db;
101: auth_packet->db_len = db_len;
102:
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;
106:
107: if (!PACKET_WRITE(auth_packet, conn)) {
108: goto end;
109: }
110: }
111: if (use_full_blown_auth_packet == TRUE) {
112: conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no);
113: }
114:
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;
131: }
132: }
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);
137: }
138: SET_CLIENT_ERROR(*conn->error_info, auth_resp_packet->error_no, UNKNOWN_SQLSTATE, auth_resp_packet->error);
139: }
140: goto end;
141: }
142:
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);
150: }
151: /* }}} */
152:
153:
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)
173: {
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;
179:
180: DBG_ENTER("mysqlnd_auth_change_user");
181:
182: chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE TSRMLS_CC);
183:
184: if (!chg_user_resp) {
185: SET_OOM_ERROR(*conn->error_info);
186: goto end;
187: }
188:
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;
194: }
195:
196: change_auth_resp_packet->auth_data = auth_plugin_data;
197: change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
198:
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;
203: }
204: } else {
205: auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
206:
207: if (!auth_packet) {
208: SET_OOM_ERROR(*conn->error_info);
209: goto end;
210: }
211:
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;
217:
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;
221:
222:
223: if (conn->m->get_server_version(conn TSRMLS_CC) >= 50123) {
224: auth_packet->charset_no = conn->charset->nr;
225: }
226:
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;
231: }
232: }
233:
234: ret = PACKET_READ(chg_user_resp, conn);
235: COPY_CLIENT_ERROR(*conn->error_info, chg_user_resp->error_info);
236:
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;
252: }
253: }
254: }
255:
256: if (conn->error_info->error_no) {
257: ret = FAIL;
258: /*
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
262: */
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);
271: }
272: }
273: }
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);
280: }
281: conn->user = tmp;
282:
283: tmp = mnd_pestrdup(passwd, conn->persistent);
284: if (conn->passwd) {
285: mnd_pefree(conn->passwd, conn->persistent);
286: }
287: conn->passwd = tmp;
288:
289: if (conn->last_message) {
290: mnd_pefree(conn->last_message, conn->persistent);
291: conn->last_message = NULL;
292: }
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);
297: }
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);
302: }
303: end:
304: PACKET_FREE(change_auth_resp_packet);
305: PACKET_FREE(auth_packet);
306: PACKET_FREE(chg_user_resp);
307: DBG_RETURN(ret);
308: }
309: /* }}} */
310:
311:
312: /******************************************* MySQL Native Password ***********************************/
313:
314: #include "ext/standard/sha1.h"
315:
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)
319: {
320: const zend_uchar *s1_end = s1 + len;
321: while (s1 < s1_end) {
322: *buffer++= *s1++ ^ *s2++;
323: }
324: }
325: /* }}} */
326:
327:
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)
330: {
331: PHP_SHA1_CTX context;
332: zend_uchar sha1[SHA1_MAX_LENGTH];
333: zend_uchar sha2[SHA1_MAX_LENGTH];
334:
335: /* Phase 1: hash password */
336: PHP_SHA1Init(&context);
337: PHP_SHA1Update(&context, password, password_len);
338: PHP_SHA1Final(sha1, &context);
339:
340: /* Phase 2: hash sha1 */
341: PHP_SHA1Init(&context);
342: PHP_SHA1Update(&context, (zend_uchar*)sha1, SHA1_MAX_LENGTH);
343: PHP_SHA1Final(sha2, &context);
344:
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);
350:
351: /* let's crypt buffer now */
352: php_mysqlnd_crypt(buffer, (const zend_uchar *)buffer, (const zend_uchar *)sha1, SHA1_MAX_LENGTH);
353: }
354: /* }}} */
355:
356:
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)
365: {
366: zend_uchar * ret = NULL;
367: DBG_ENTER("mysqlnd_native_auth_get_auth_data");
368: *auth_data_len = 0;
369:
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);
376: }
377:
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);
384: }
385: DBG_RETURN(ret);
386: }
387: /* }}} */
388:
389:
390: static struct st_mysqlnd_authentication_plugin mysqlnd_native_auth_plugin =
391: {
392: {
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>",
399: {
400: NULL, /* no statistics , will be filled later if there are some */
401: NULL, /* no statistics */
402: },
403: {
404: NULL /* plugin shutdown */
405: }
406: },
407: {/* methods */
408: mysqlnd_native_auth_get_auth_data
409: }
410: };
411:
412:
413: /******************************************* PAM Authentication ***********************************/
414:
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)
423: {
424: zend_uchar * ret = NULL;
425:
426: /* copy pass*/
427: if (passwd && passwd_len) {
428: ret = (zend_uchar*) zend_strndup(passwd, passwd_len);
429: }
430: *auth_data_len = passwd_len;
431:
432: return ret;
433: }
434: /* }}} */
435:
436:
437: static struct st_mysqlnd_authentication_plugin mysqlnd_pam_authentication_plugin =
438: {
439: {
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>",
446: {
447: NULL, /* no statistics , will be filled later if there are some */
448: NULL, /* no statistics */
449: },
450: {
451: NULL /* plugin shutdown */
452: }
453: },
454: {/* methods */
455: mysqlnd_pam_auth_get_auth_data
456: }
457: };
458:
459:
460: /* {{{ mysqlnd_register_builtin_authentication_plugins */
461: void
462: mysqlnd_register_builtin_authentication_plugins(TSRMLS_D)
463: {
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);
466: }
467: /* }}} */
468:
469:
470: /*
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
477: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>