Return to mysqlnd_auth.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / mysqlnd |
1.1 ! misho 1: /* ! 2: +----------------------------------------------------------------------+ ! 3: | PHP Version 5 | ! 4: +----------------------------------------------------------------------+ ! 5: | Copyright (c) 2006-2012 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: +----------------------------------------------------------------------+ ! 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: */