PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/mysqlnd - mysqlnd_auth.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 197 358 55.0 %
Date: 2016-07-19 Functions: 9 13 69.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   +----------------------------------------------------------------------+
       3             :   | PHP Version 7                                                        |
       4             :   +----------------------------------------------------------------------+
       5             :   | Copyright (c) 2006-2016 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: Andrey Hristov <andrey@php.net>                             |
      16             :   |          Ulf Wendel <uw@php.net>                                     |
      17             :   +----------------------------------------------------------------------+
      18             : */
      19             : 
      20             : #include "php.h"
      21             : #include "mysqlnd.h"
      22             : #include "mysqlnd_structs.h"
      23             : #include "mysqlnd_auth.h"
      24             : #include "mysqlnd_wireprotocol.h"
      25             : #include "mysqlnd_connection.h"
      26             : #include "mysqlnd_priv.h"
      27             : #include "mysqlnd_charset.h"
      28             : #include "mysqlnd_debug.h"
      29             : 
      30             : static const char * const mysqlnd_old_passwd  = "mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. "
      31             : "Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will "
      32             : "store a new, and more secure, hash value in mysql.user. If this user is used in other scripts executed by PHP 5.2 or earlier you might need to remove the old-passwords "
      33             : "flag from your my.cnf file";
      34             : 
      35             : 
      36             : /* {{{ mysqlnd_run_authentication */
      37             : enum_func_status
      38        1768 : mysqlnd_run_authentication(
      39             :                         MYSQLND_CONN_DATA * conn,
      40             :                         const char * const user,
      41             :                         const char * const passwd,
      42             :                         const size_t passwd_len,
      43             :                         const char * const db,
      44             :                         const size_t db_len,
      45             :                         const MYSQLND_STRING auth_plugin_data,
      46             :                         const char * const auth_protocol,
      47             :                         unsigned int charset_no,
      48             :                         const MYSQLND_SESSION_OPTIONS * const session_options,
      49             :                         zend_ulong mysql_flags,
      50             :                         zend_bool silent,
      51             :                         zend_bool is_change_user
      52             :                         )
      53             : {
      54        1768 :         enum_func_status ret = FAIL;
      55        1768 :         zend_bool first_call = TRUE;
      56             : 
      57        1768 :         char * switch_to_auth_protocol = NULL;
      58        1768 :         size_t switch_to_auth_protocol_len = 0;
      59        1768 :         char * requested_protocol = NULL;
      60             :         zend_uchar * plugin_data;
      61             :         size_t plugin_data_len;
      62             : 
      63        1768 :         DBG_ENTER("mysqlnd_run_authentication");
      64             : 
      65        1768 :         plugin_data_len = auth_plugin_data.l;
      66        1768 :         plugin_data = mnd_emalloc(plugin_data_len + 1);
      67        1768 :         if (!plugin_data) {
      68           0 :                 goto end;
      69             :         }
      70        1768 :         memcpy(plugin_data, auth_plugin_data.s, plugin_data_len);
      71        1768 :         plugin_data[plugin_data_len] = '\0';
      72             : 
      73        1768 :         requested_protocol = mnd_pestrdup(auth_protocol? auth_protocol : MYSQLND_DEFAULT_AUTH_PROTOCOL, FALSE);
      74        1768 :         if (!requested_protocol) {
      75           0 :                 goto end;
      76             :         }
      77             : 
      78             :         do {
      79        1768 :                 struct st_mysqlnd_authentication_plugin * auth_plugin = conn->m->fetch_auth_plugin_by_name(requested_protocol);
      80             : 
      81        1768 :                 if (!auth_plugin) {
      82           0 :                         php_error_docref(NULL, E_WARNING, "The server requested authentication method unknown to the client [%s]", requested_protocol);
      83           0 :                         SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "The server requested authentication method unknown to the client");
      84           0 :                         goto end;
      85             :                 }
      86        1768 :                 DBG_INF("plugin found");
      87             : 
      88             :                 {
      89        1768 :                         zend_uchar * switch_to_auth_protocol_data = NULL;
      90        1768 :                         size_t switch_to_auth_protocol_data_len = 0;
      91        1768 :                         zend_uchar * scrambled_data = NULL;
      92        1768 :                         size_t scrambled_data_len = 0;
      93             : 
      94        1768 :                         switch_to_auth_protocol = NULL;
      95        1768 :                         switch_to_auth_protocol_len = 0;
      96             : 
      97        1768 :                         if (conn->authentication_plugin_data.s) {
      98          75 :                                 mnd_pefree(conn->authentication_plugin_data.s, conn->persistent);
      99          75 :                                 conn->authentication_plugin_data.s = NULL;
     100             :                         }
     101        1768 :                         conn->authentication_plugin_data.l = plugin_data_len;
     102        1768 :                         conn->authentication_plugin_data.s = mnd_pemalloc(conn->authentication_plugin_data.l, conn->persistent);
     103        1768 :                         if (!conn->authentication_plugin_data.s) {
     104           0 :                                 SET_OOM_ERROR(conn->error_info);
     105           0 :                                 goto end;
     106             :                         }
     107        1768 :                         memcpy(conn->authentication_plugin_data.s, plugin_data, plugin_data_len);
     108             : 
     109        1768 :                         DBG_INF_FMT("salt(%d)=[%.*s]", plugin_data_len, plugin_data_len, plugin_data);
     110             :                         /* The data should be allocated with malloc() */
     111        1768 :                         scrambled_data =
     112        3536 :                                 auth_plugin->methods.get_auth_data(NULL, &scrambled_data_len, conn, user, passwd, passwd_len,
     113             :                                                                                                    plugin_data, plugin_data_len, session_options,
     114        1768 :                                                                                                    conn->protocol_frame_codec->data, mysql_flags);
     115        1768 :                         if (conn->error_info->error_no) {
     116           0 :                                 goto end;
     117             :                         }
     118        1768 :                         if (FALSE == is_change_user) {
     119        1693 :                                 ret = mysqlnd_auth_handshake(conn, user, passwd, passwd_len, db, db_len, session_options, mysql_flags,
     120             :                                                                                         charset_no,
     121             :                                                                                         first_call,
     122             :                                                                                         requested_protocol,
     123             :                                                                                         scrambled_data, scrambled_data_len,
     124             :                                                                                         &switch_to_auth_protocol, &switch_to_auth_protocol_len,
     125             :                                                                                         &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
     126             :                                                                                         );
     127             :                         } else {
     128          75 :                                 ret = mysqlnd_auth_change_user(conn, user, strlen(user), passwd, passwd_len, db, db_len, silent,
     129             :                                                                                            first_call,
     130             :                                                                                            requested_protocol,
     131             :                                                                                            scrambled_data, scrambled_data_len,
     132             :                                                                                            &switch_to_auth_protocol, &switch_to_auth_protocol_len,
     133             :                                                                                            &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
     134             :                                                                                           );
     135             :                         }
     136        1768 :                         first_call = FALSE;
     137        1768 :                         free(scrambled_data);
     138             : 
     139        1768 :                         DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
     140        1768 :                         if (requested_protocol && switch_to_auth_protocol) {
     141           0 :                                 mnd_efree(requested_protocol);
     142           0 :                                 requested_protocol = switch_to_auth_protocol;
     143             :                         }
     144             : 
     145        1768 :                         if (plugin_data) {
     146        1768 :                                 mnd_efree(plugin_data);
     147             :                         }
     148        1768 :                         plugin_data_len = switch_to_auth_protocol_data_len;
     149        1768 :                         plugin_data = switch_to_auth_protocol_data;
     150             :                 }
     151        1768 :                 DBG_INF_FMT("conn->error_info->error_no = %d", conn->error_info->error_no);
     152        1768 :         } while (ret == FAIL && conn->error_info->error_no == 0 && switch_to_auth_protocol != NULL);
     153             : 
     154        1768 :         if (ret == PASS) {
     155        1729 :                 DBG_INF_FMT("saving requested_protocol=%s", requested_protocol);
     156        1729 :                 conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol);
     157             :         }
     158             : end:
     159        1768 :         if (plugin_data) {
     160           0 :                 mnd_efree(plugin_data);
     161             :         }
     162        1768 :         if (requested_protocol) {
     163        1768 :                 mnd_efree(requested_protocol);
     164             :         }
     165             : 
     166        1768 :         DBG_RETURN(ret);
     167             : }
     168             : /* }}} */
     169             : 
     170             : 
     171             : /* {{{ mysqlnd_switch_to_ssl_if_needed */
     172             : static enum_func_status
     173        1697 : mysqlnd_switch_to_ssl_if_needed(MYSQLND_CONN_DATA * conn,
     174             :                                                                 unsigned int charset_no,
     175             :                                                                 size_t server_capabilities,
     176             :                                                                 const MYSQLND_SESSION_OPTIONS * const session_options,
     177             :                                                                 zend_ulong mysql_flags)
     178             : {
     179        1697 :         enum_func_status ret = FAIL;
     180             :         const MYSQLND_CHARSET * charset;
     181        1697 :         DBG_ENTER("mysqlnd_switch_to_ssl_if_needed");
     182             : 
     183        1697 :         if (session_options->charset_name && (charset = mysqlnd_find_charset_name(session_options->charset_name))) {
     184           4 :                 charset_no      = charset->nr;
     185             :         }
     186             : 
     187             :         {
     188        1697 :                 size_t client_capabilities = mysql_flags;
     189        1697 :                 struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_ENABLE_SSL, conn, client_capabilities, server_capabilities, charset_no);
     190        1697 :                 if (command) {
     191        1697 :                         ret = command->run(command);
     192        1697 :                         command->free_command(command);
     193             :                 }
     194             :         }
     195        1697 :         DBG_RETURN(ret);
     196             : }
     197             : /* }}} */
     198             : 
     199             : 
     200             : /* {{{ mysqlnd_connect_run_authentication */
     201             : enum_func_status
     202        1697 : mysqlnd_connect_run_authentication(
     203             :                         MYSQLND_CONN_DATA * conn,
     204             :                         const char * const user,
     205             :                         const char * const passwd,
     206             :                         const char * const db,
     207             :                         size_t db_len,
     208             :                         size_t passwd_len,
     209             :                         MYSQLND_STRING authentication_plugin_data,
     210             :                         const char * const authentication_protocol,
     211             :                         const unsigned int charset_no,
     212             :                         size_t server_capabilities,
     213             :                         const MYSQLND_SESSION_OPTIONS * const session_options,
     214             :                         zend_ulong mysql_flags
     215             :                         )
     216             : {
     217        1697 :         enum_func_status ret = FAIL;
     218        1697 :         DBG_ENTER("mysqlnd_connect_run_authentication");
     219             : 
     220        1697 :         ret = mysqlnd_switch_to_ssl_if_needed(conn, charset_no, server_capabilities, session_options, mysql_flags);
     221        1697 :         if (PASS == ret) {
     222        1693 :                 ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, db_len,
     223             :                                                                                  authentication_plugin_data, authentication_protocol,
     224             :                                                                                  charset_no, session_options, mysql_flags, FALSE /*silent*/, FALSE/*is_change*/);
     225             :         }
     226        1697 :         DBG_RETURN(ret);
     227             : }
     228             : /* }}} */
     229             : 
     230             : 
     231             : /* {{{ mysqlnd_auth_handshake */
     232             : enum_func_status
     233        1693 : mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
     234             :                                                           const char * const user,
     235             :                                                           const char * const passwd,
     236             :                                                           const size_t passwd_len,
     237             :                                                           const char * const db,
     238             :                                                           const size_t db_len,
     239             :                                                           const MYSQLND_SESSION_OPTIONS * const session_options,
     240             :                                                           zend_ulong mysql_flags,
     241             :                                                           unsigned int server_charset_no,
     242             :                                                           zend_bool use_full_blown_auth_packet,
     243             :                                                           const char * const auth_protocol,
     244             :                                                           const zend_uchar * const auth_plugin_data,
     245             :                                                           const size_t auth_plugin_data_len,
     246             :                                                           char ** switch_to_auth_protocol,
     247             :                                                           size_t * switch_to_auth_protocol_len,
     248             :                                                           zend_uchar ** switch_to_auth_protocol_data,
     249             :                                                           size_t * switch_to_auth_protocol_data_len
     250             :                                                          )
     251             : {
     252        1693 :         enum_func_status ret = FAIL;
     253        1693 :         const MYSQLND_CHARSET * charset = NULL;
     254        1693 :         MYSQLND_PACKET_CHANGE_AUTH_RESPONSE * change_auth_resp_packet = NULL;
     255        1693 :         MYSQLND_PACKET_AUTH_RESPONSE * auth_resp_packet = NULL;
     256        1693 :         MYSQLND_PACKET_AUTH * auth_packet = NULL;
     257             : 
     258        1693 :         DBG_ENTER("mysqlnd_auth_handshake");
     259             : 
     260        1693 :         auth_resp_packet = conn->payload_decoder_factory->m.get_auth_response_packet(conn->payload_decoder_factory, FALSE);
     261             : 
     262        1693 :         if (!auth_resp_packet) {
     263           0 :                 SET_OOM_ERROR(conn->error_info);
     264           0 :                 goto end;
     265             :         }
     266             : 
     267        1693 :         if (use_full_blown_auth_packet != TRUE) {
     268           0 :                 change_auth_resp_packet = conn->payload_decoder_factory->m.get_change_auth_response_packet(conn->payload_decoder_factory, FALSE);
     269           0 :                 if (!change_auth_resp_packet) {
     270           0 :                         SET_OOM_ERROR(conn->error_info);
     271           0 :                         goto end;
     272             :                 }
     273             : 
     274           0 :                 change_auth_resp_packet->auth_data = auth_plugin_data;
     275           0 :                 change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
     276             : 
     277           0 :                 if (!PACKET_WRITE(change_auth_resp_packet)) {
     278           0 :                         SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
     279           0 :                         SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
     280           0 :                         goto end;
     281             :                 }
     282             :         } else {
     283        1693 :                 auth_packet = conn->payload_decoder_factory->m.get_auth_packet(conn->payload_decoder_factory, FALSE);
     284             : 
     285        1693 :                 auth_packet->client_flags = mysql_flags;
     286        1693 :                 auth_packet->max_packet_size = session_options->max_allowed_packet;
     287        1697 :                 if (session_options->charset_name && (charset = mysqlnd_find_charset_name(session_options->charset_name))) {
     288           4 :                         auth_packet->charset_no      = charset->nr;
     289             :                 } else {
     290        1689 :                         auth_packet->charset_no      = server_charset_no;
     291             :                 }
     292             : 
     293        1693 :                 auth_packet->send_auth_data = TRUE;
     294        1693 :                 auth_packet->user            = user;
     295        1693 :                 auth_packet->db                      = db;
     296        1693 :                 auth_packet->db_len          = db_len;
     297             : 
     298        1693 :                 auth_packet->auth_data = auth_plugin_data;
     299        1693 :                 auth_packet->auth_data_len = auth_plugin_data_len;
     300        1693 :                 auth_packet->auth_plugin_name = auth_protocol;
     301             : 
     302        1693 :                 if (conn->server_capabilities & CLIENT_CONNECT_ATTRS) {
     303           0 :                         auth_packet->connect_attr = conn->options->connect_attr;
     304             :                 }
     305             : 
     306        1693 :                 if (!PACKET_WRITE(auth_packet)) {
     307           0 :                         goto end;
     308             :                 }
     309             :         }
     310        1693 :         if (use_full_blown_auth_packet == TRUE) {
     311        1693 :                 conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no);
     312             :         }
     313             : 
     314        1693 :         if (FAIL == PACKET_READ(auth_resp_packet) || auth_resp_packet->response_code >= 0xFE) {
     315          24 :                 if (auth_resp_packet->response_code == 0xFE) {
     316             :                         /* old authentication with new server  !*/
     317           0 :                         if (!auth_resp_packet->new_auth_protocol) {
     318           0 :                                 DBG_ERR(mysqlnd_old_passwd);
     319           0 :                                 SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
     320             :                         } else {
     321           0 :                                 *switch_to_auth_protocol = mnd_pestrndup(auth_resp_packet->new_auth_protocol, auth_resp_packet->new_auth_protocol_len, FALSE);
     322           0 :                                 *switch_to_auth_protocol_len = auth_resp_packet->new_auth_protocol_len;
     323           0 :                                 if (auth_resp_packet->new_auth_protocol_data) {
     324           0 :                                         *switch_to_auth_protocol_data_len = auth_resp_packet->new_auth_protocol_data_len;
     325           0 :                                         *switch_to_auth_protocol_data = mnd_emalloc(*switch_to_auth_protocol_data_len);
     326           0 :                                         memcpy(*switch_to_auth_protocol_data, auth_resp_packet->new_auth_protocol_data, *switch_to_auth_protocol_data_len);
     327             :                                 } else {
     328           0 :                                         *switch_to_auth_protocol_data = NULL;
     329           0 :                                         *switch_to_auth_protocol_data_len = 0;
     330             :                                 }
     331             :                         }
     332          24 :                 } else if (auth_resp_packet->response_code == 0xFF) {
     333          24 :                         if (auth_resp_packet->sqlstate[0]) {
     334          24 :                                 strlcpy(conn->error_info->sqlstate, auth_resp_packet->sqlstate, sizeof(conn->error_info->sqlstate));
     335          24 :                                 DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", auth_resp_packet->error_no, auth_resp_packet->sqlstate, auth_resp_packet->error);
     336             :                         }
     337          24 :                         SET_CLIENT_ERROR(conn->error_info, auth_resp_packet->error_no, UNKNOWN_SQLSTATE, auth_resp_packet->error);
     338             :                 }
     339          24 :                 goto end;
     340             :         }
     341             : 
     342        1669 :         SET_NEW_MESSAGE(conn->last_message.s, conn->last_message.l, auth_resp_packet->message, auth_resp_packet->message_len, conn->persistent);
     343        1669 :         ret = PASS;
     344             : end:
     345        1693 :         PACKET_FREE(change_auth_resp_packet);
     346        1693 :         PACKET_FREE(auth_packet);
     347        1693 :         PACKET_FREE(auth_resp_packet);
     348        1693 :         DBG_RETURN(ret);
     349             : }
     350             : /* }}} */
     351             : 
     352             : 
     353             : /* {{{ mysqlnd_auth_change_user */
     354             : enum_func_status
     355          75 : mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
     356             :                                                                 const char * const user,
     357             :                                                                 const size_t user_len,
     358             :                                                                 const char * const passwd,
     359             :                                                                 const size_t passwd_len,
     360             :                                                                 const char * const db,
     361             :                                                                 const size_t db_len,
     362             :                                                                 const zend_bool silent,
     363             :                                                                 zend_bool use_full_blown_auth_packet,
     364             :                                                                 const char * const auth_protocol,
     365             :                                                                 zend_uchar * auth_plugin_data,
     366             :                                                                 size_t auth_plugin_data_len,
     367             :                                                                 char ** switch_to_auth_protocol,
     368             :                                                                 size_t * switch_to_auth_protocol_len,
     369             :                                                                 zend_uchar ** switch_to_auth_protocol_data,
     370             :                                                                 size_t * switch_to_auth_protocol_data_len
     371             :                                                                 )
     372             : {
     373          75 :         enum_func_status ret = FAIL;
     374          75 :         const MYSQLND_CHARSET * old_cs = conn->charset;
     375          75 :         MYSQLND_PACKET_CHANGE_AUTH_RESPONSE * change_auth_resp_packet = NULL;
     376          75 :         MYSQLND_PACKET_CHG_USER_RESPONSE * chg_user_resp = NULL;
     377          75 :         MYSQLND_PACKET_AUTH * auth_packet = NULL;
     378             : 
     379          75 :         DBG_ENTER("mysqlnd_auth_change_user");
     380             : 
     381          75 :         chg_user_resp = conn->payload_decoder_factory->m.get_change_user_response_packet(conn->payload_decoder_factory, FALSE);
     382             : 
     383          75 :         if (!chg_user_resp) {
     384           0 :                 SET_OOM_ERROR(conn->error_info);
     385           0 :                 goto end;
     386             :         }
     387             : 
     388          75 :         if (use_full_blown_auth_packet != TRUE) {
     389           0 :                 change_auth_resp_packet = conn->payload_decoder_factory->m.get_change_auth_response_packet(conn->payload_decoder_factory, FALSE);
     390           0 :                 if (!change_auth_resp_packet) {
     391           0 :                         SET_OOM_ERROR(conn->error_info);
     392           0 :                         goto end;
     393             :                 }
     394             : 
     395           0 :                 change_auth_resp_packet->auth_data = auth_plugin_data;
     396           0 :                 change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
     397             : 
     398           0 :                 if (!PACKET_WRITE(change_auth_resp_packet)) {
     399           0 :                         SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
     400           0 :                         SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
     401           0 :                         goto end;
     402             :                 }
     403             :         } else {
     404          75 :                 auth_packet = conn->payload_decoder_factory->m.get_auth_packet(conn->payload_decoder_factory, FALSE);
     405             : 
     406          75 :                 if (!auth_packet) {
     407           0 :                         SET_OOM_ERROR(conn->error_info);
     408           0 :                         goto end;
     409             :                 }
     410             : 
     411          75 :                 auth_packet->is_change_user_packet = TRUE;
     412          75 :                 auth_packet->user            = user;
     413          75 :                 auth_packet->db                      = db;
     414          75 :                 auth_packet->db_len          = db_len;
     415          75 :                 auth_packet->silent          = silent;
     416             : 
     417          75 :                 auth_packet->auth_data = auth_plugin_data;
     418          75 :                 auth_packet->auth_data_len = auth_plugin_data_len;
     419          75 :                 auth_packet->auth_plugin_name = auth_protocol;
     420             : 
     421             : 
     422          75 :                 if (conn->m->get_server_version(conn) >= 50123) {
     423          75 :                         auth_packet->charset_no      = conn->charset->nr;
     424             :                 }
     425             : 
     426          75 :                 if (!PACKET_WRITE(auth_packet)) {
     427           1 :                         SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
     428           1 :                         SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
     429           1 :                         goto end;
     430             :                 }
     431             :         }
     432             : 
     433          74 :         ret = PACKET_READ(chg_user_resp);
     434          74 :         COPY_CLIENT_ERROR(conn->error_info, chg_user_resp->error_info);
     435             : 
     436          74 :         if (0xFE == chg_user_resp->response_code) {
     437           0 :                 ret = FAIL;
     438           0 :                 if (!chg_user_resp->new_auth_protocol) {
     439           0 :                         DBG_ERR(mysqlnd_old_passwd);
     440           0 :                         SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
     441             :                 } else {
     442           0 :                         *switch_to_auth_protocol = mnd_pestrndup(chg_user_resp->new_auth_protocol, chg_user_resp->new_auth_protocol_len, FALSE);
     443           0 :                         *switch_to_auth_protocol_len = chg_user_resp->new_auth_protocol_len;
     444           0 :                         if (chg_user_resp->new_auth_protocol_data) {
     445           0 :                                 *switch_to_auth_protocol_data_len = chg_user_resp->new_auth_protocol_data_len;
     446           0 :                                 *switch_to_auth_protocol_data = mnd_emalloc(*switch_to_auth_protocol_data_len);
     447           0 :                                 memcpy(*switch_to_auth_protocol_data, chg_user_resp->new_auth_protocol_data, *switch_to_auth_protocol_data_len);
     448             :                         } else {
     449           0 :                                 *switch_to_auth_protocol_data = NULL;
     450           0 :                                 *switch_to_auth_protocol_data_len = 0;
     451             :                         }
     452             :                 }
     453             :         }
     454             : 
     455          74 :         if (conn->error_info->error_no) {
     456          14 :                 ret = FAIL;
     457             :                 /*
     458             :                   COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune.
     459             :                   bug#25371 mysql_change_user() triggers "packets out of sync"
     460             :                   When it gets fixed, there should be one more check here
     461             :                 */
     462          14 :                 if (conn->m->get_server_version(conn) > 50113L &&conn->m->get_server_version(conn) < 50118L) {
     463           0 :                         MYSQLND_PACKET_OK * redundant_error_packet = conn->payload_decoder_factory->m.get_ok_packet(conn->payload_decoder_factory, FALSE);
     464           0 :                         if (redundant_error_packet) {
     465           0 :                                 PACKET_READ(redundant_error_packet);
     466           0 :                                 PACKET_FREE(redundant_error_packet);
     467           0 :                                 DBG_INF_FMT("Server is %u, buggy, sends two ERR messages", conn->m->get_server_version(conn));
     468             :                         } else {
     469           0 :                                 SET_OOM_ERROR(conn->error_info);
     470             :                         }
     471             :                 }
     472             :         }
     473          74 :         if (ret == PASS) {
     474          60 :                 char * tmp = NULL;
     475             :                 /* if we get conn->username as parameter and then we first free it, then estrndup it, we will crash */
     476          60 :                 tmp = mnd_pestrndup(user, user_len, conn->persistent);
     477          60 :                 if (conn->username.s) {
     478          60 :                         mnd_pefree(conn->username.s, conn->persistent);
     479             :                 }
     480          60 :                 conn->username.s = tmp;
     481             : 
     482          60 :                 tmp = mnd_pestrdup(passwd, conn->persistent);
     483          60 :                 if (conn->password.s) {
     484          60 :                         mnd_pefree(conn->password.s, conn->persistent);
     485             :                 }
     486          60 :                 conn->password.s = tmp;
     487             : 
     488          60 :                 if (conn->last_message.s) {
     489           0 :                         mnd_pefree(conn->last_message.s, conn->persistent);
     490           0 :                         conn->last_message.s = NULL;
     491             :                 }
     492          60 :                 UPSERT_STATUS_RESET(conn->upsert_status);
     493             :                 /* set charset for old servers */
     494          60 :                 if (conn->m->get_server_version(conn) < 50123) {
     495           0 :                         ret = conn->m->set_charset(conn, old_cs->name);
     496             :                 }
     497          14 :         } else if (ret == FAIL && chg_user_resp->server_asked_323_auth == TRUE) {
     498             :                 /* old authentication with new server  !*/
     499           0 :                 DBG_ERR(mysqlnd_old_passwd);
     500           0 :                 SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
     501             :         }
     502             : end:
     503          75 :         PACKET_FREE(change_auth_resp_packet);
     504          75 :         PACKET_FREE(auth_packet);
     505          75 :         PACKET_FREE(chg_user_resp);
     506          75 :         DBG_RETURN(ret);
     507             : }
     508             : /* }}} */
     509             : 
     510             : 
     511             : /******************************************* MySQL Native Password ***********************************/
     512             : 
     513             : #include "ext/standard/sha1.h"
     514             : 
     515             : /* {{{ php_mysqlnd_crypt */
     516             : static void
     517        1760 : php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len)
     518             : {
     519        1760 :         const zend_uchar *s1_end = s1 + len;
     520       38720 :         while (s1 < s1_end) {
     521       35200 :                 *buffer++= *s1++ ^ *s2++;
     522             :         }
     523        1760 : }
     524             : /* }}} */
     525             : 
     526             : 
     527             : /* {{{ php_mysqlnd_scramble */
     528        1760 : void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password, const size_t password_len)
     529             : {
     530             :         PHP_SHA1_CTX context;
     531             :         zend_uchar sha1[SHA1_MAX_LENGTH];
     532             :         zend_uchar sha2[SHA1_MAX_LENGTH];
     533             : 
     534             :         /* Phase 1: hash password */
     535        1760 :         PHP_SHA1Init(&context);
     536        1760 :         PHP_SHA1Update(&context, password, password_len);
     537        1760 :         PHP_SHA1Final(sha1, &context);
     538             : 
     539             :         /* Phase 2: hash sha1 */
     540        1760 :         PHP_SHA1Init(&context);
     541        1760 :         PHP_SHA1Update(&context, (zend_uchar*)sha1, SHA1_MAX_LENGTH);
     542        1760 :         PHP_SHA1Final(sha2, &context);
     543             : 
     544             :         /* Phase 3: hash scramble + sha2 */
     545        1760 :         PHP_SHA1Init(&context);
     546        1760 :         PHP_SHA1Update(&context, scramble, SCRAMBLE_LENGTH);
     547        1760 :         PHP_SHA1Update(&context, (zend_uchar*)sha2, SHA1_MAX_LENGTH);
     548        1760 :         PHP_SHA1Final(buffer, &context);
     549             : 
     550             :         /* let's crypt buffer now */
     551        1760 :         php_mysqlnd_crypt(buffer, (const zend_uchar *)buffer, (const zend_uchar *)sha1, SHA1_MAX_LENGTH);
     552        1760 : }
     553             : /* }}} */
     554             : 
     555             : 
     556             : /* {{{ mysqlnd_native_auth_get_auth_data */
     557             : static zend_uchar *
     558        1768 : mysqlnd_native_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
     559             :                                                                   size_t * auth_data_len,
     560             :                                                                   MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
     561             :                                                                   const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
     562             :                                                                   const MYSQLND_SESSION_OPTIONS * const session_options,
     563             :                                                                   const MYSQLND_PFC_DATA * const pfc_data,
     564             :                                                                   zend_ulong mysql_flags
     565             :                                                                  )
     566             : {
     567        1768 :         zend_uchar * ret = NULL;
     568        1768 :         DBG_ENTER("mysqlnd_native_auth_get_auth_data");
     569        1768 :         *auth_data_len = 0;
     570             : 
     571             :         /* 5.5.x reports 21 as scramble length because it needs to show the length of the data before the plugin name */
     572        1768 :         if (auth_plugin_data_len < SCRAMBLE_LENGTH) {
     573             :                 /* mysql_native_password only works with SCRAMBLE_LENGTH scramble */
     574           0 :                 SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "The server sent wrong length for scramble");
     575           0 :                 DBG_ERR_FMT("The server sent wrong length for scramble %u. Expected %u", auth_plugin_data_len, SCRAMBLE_LENGTH);
     576           0 :                 DBG_RETURN(NULL);
     577             :         }
     578             : 
     579             :         /* copy scrambled pass*/
     580        1768 :         if (passwd && passwd_len) {
     581        1760 :                 ret = malloc(SCRAMBLE_LENGTH);
     582        1760 :                 *auth_data_len = SCRAMBLE_LENGTH;
     583             :                 /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */
     584        1760 :                 php_mysqlnd_scramble((zend_uchar*)ret, auth_plugin_data, (zend_uchar*)passwd, passwd_len);
     585             :         }
     586        1768 :         DBG_RETURN(ret);
     587             : }
     588             : /* }}} */
     589             : 
     590             : 
     591             : static struct st_mysqlnd_authentication_plugin mysqlnd_native_auth_plugin =
     592             : {
     593             :         {
     594             :                 MYSQLND_PLUGIN_API_VERSION,
     595             :                 "auth_plugin_mysql_native_password",
     596             :                 MYSQLND_VERSION_ID,
     597             :                 PHP_MYSQLND_VERSION,
     598             :                 "PHP License 3.01",
     599             :                 "Andrey Hristov <andrey@php.net>,  Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
     600             :                 {
     601             :                         NULL, /* no statistics , will be filled later if there are some */
     602             :                         NULL, /* no statistics */
     603             :                 },
     604             :                 {
     605             :                         NULL /* plugin shutdown */
     606             :                 }
     607             :         },
     608             :         {/* methods */
     609             :                 mysqlnd_native_auth_get_auth_data
     610             :         }
     611             : };
     612             : 
     613             : 
     614             : /******************************************* PAM Authentication ***********************************/
     615             : 
     616             : /* {{{ mysqlnd_pam_auth_get_auth_data */
     617             : static zend_uchar *
     618           0 : mysqlnd_pam_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
     619             :                                                            size_t * auth_data_len,
     620             :                                                            MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
     621             :                                                            const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
     622             :                                                            const MYSQLND_SESSION_OPTIONS * const session_options,
     623             :                                                            const MYSQLND_PFC_DATA * const pfc_data,
     624             :                                                            zend_ulong mysql_flags
     625             :                                                           )
     626             : {
     627           0 :         zend_uchar * ret = NULL;
     628             : 
     629             :         /* copy pass*/
     630           0 :         if (passwd && passwd_len) {
     631           0 :                 ret = (zend_uchar*) zend_strndup(passwd, passwd_len);
     632             :         }
     633           0 :         *auth_data_len = passwd_len;
     634             : 
     635           0 :         return ret;
     636             : }
     637             : /* }}} */
     638             : 
     639             : 
     640             : static struct st_mysqlnd_authentication_plugin mysqlnd_pam_authentication_plugin =
     641             : {
     642             :         {
     643             :                 MYSQLND_PLUGIN_API_VERSION,
     644             :                 "auth_plugin_mysql_clear_password",
     645             :                 MYSQLND_VERSION_ID,
     646             :                 PHP_MYSQLND_VERSION,
     647             :                 "PHP License 3.01",
     648             :                 "Andrey Hristov <andrey@php.net>,  Ulf Wendel <uw@php.net>, Georg Richter <georg@php.net>",
     649             :                 {
     650             :                         NULL, /* no statistics , will be filled later if there are some */
     651             :                         NULL, /* no statistics */
     652             :                 },
     653             :                 {
     654             :                         NULL /* plugin shutdown */
     655             :                 }
     656             :         },
     657             :         {/* methods */
     658             :                 mysqlnd_pam_auth_get_auth_data
     659             :         }
     660             : };
     661             : 
     662             : 
     663             : /******************************************* SHA256 Password ***********************************/
     664             : #ifdef MYSQLND_HAVE_SSL
     665             : static void
     666           0 : mysqlnd_xor_string(char * dst, const size_t dst_len, const char * xor_str, const size_t xor_str_len)
     667             : {
     668             :         unsigned int i;
     669           0 :         for (i = 0; i <= dst_len; ++i) {
     670           0 :                 dst[i] ^= xor_str[i % xor_str_len];
     671             :         }
     672           0 : }
     673             : 
     674             : 
     675             : #include <openssl/rsa.h>
     676             : #include <openssl/pem.h>
     677             : #include <openssl/err.h>
     678             : 
     679             : 
     680             : /* {{{ mysqlnd_sha256_get_rsa_key */
     681             : static RSA *
     682           0 : mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn,
     683             :                                                    const MYSQLND_SESSION_OPTIONS * const session_options,
     684             :                                                    const MYSQLND_PFC_DATA * const pfc_data
     685             :                                                   )
     686             : {
     687           0 :         RSA * ret = NULL;
     688           0 :         const char * fname = (pfc_data->sha256_server_public_key && pfc_data->sha256_server_public_key[0] != '\0')?
     689             :                                                                 pfc_data->sha256_server_public_key:
     690           0 :                                                                 MYSQLND_G(sha256_server_public_key);
     691             :         php_stream * stream;
     692           0 :         DBG_ENTER("mysqlnd_sha256_get_rsa_key");
     693           0 :         DBG_INF_FMT("options_s256_pk=[%s] MYSQLND_G(sha256_server_public_key)=[%s]",
     694           0 :                                  pfc_data->sha256_server_public_key? pfc_data->sha256_server_public_key:"n/a",
     695           0 :                                  MYSQLND_G(sha256_server_public_key)? MYSQLND_G(sha256_server_public_key):"n/a");
     696           0 :         if (!fname || fname[0] == '\0') {
     697           0 :                 MYSQLND_PACKET_SHA256_PK_REQUEST * pk_req_packet = NULL;
     698           0 :                 MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE * pk_resp_packet = NULL;
     699             : 
     700             :                 do {
     701           0 :                         DBG_INF("requesting the public key from the server");
     702           0 :                         pk_req_packet = conn->payload_decoder_factory->m.get_sha256_pk_request_packet(conn->payload_decoder_factory, FALSE);
     703           0 :                         if (!pk_req_packet) {
     704           0 :                                 SET_OOM_ERROR(conn->error_info);
     705           0 :                                 break;
     706             :                         }
     707           0 :                         pk_resp_packet = conn->payload_decoder_factory->m.get_sha256_pk_request_response_packet(conn->payload_decoder_factory, FALSE);
     708           0 :                         if (!pk_resp_packet) {
     709           0 :                                 SET_OOM_ERROR(conn->error_info);
     710           0 :                                 PACKET_FREE(pk_req_packet);
     711           0 :                                 break;
     712             :                         }
     713             : 
     714           0 :                         if (! PACKET_WRITE(pk_req_packet)) {
     715           0 :                                 DBG_ERR_FMT("Error while sending public key request packet");
     716           0 :                                 php_error(E_WARNING, "Error while sending public key request packet. PID=%d", getpid());
     717           0 :                                 SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
     718           0 :                                 break;
     719             :                         }
     720           0 :                         if (FAIL == PACKET_READ(pk_resp_packet) || NULL == pk_resp_packet->public_key) {
     721           0 :                                 DBG_ERR_FMT("Error while receiving public key");
     722           0 :                                 php_error(E_WARNING, "Error while receiving public key. PID=%d", getpid());
     723           0 :                                 SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
     724           0 :                                 break;
     725             :                         }
     726           0 :                         DBG_INF_FMT("Public key(%d):\n%s", pk_resp_packet->public_key_len, pk_resp_packet->public_key);
     727             :                         /* now extract the public key */
     728             :                         {
     729           0 :                                 BIO * bio = BIO_new_mem_buf(pk_resp_packet->public_key, pk_resp_packet->public_key_len);
     730           0 :                                 ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
     731           0 :                                 BIO_free(bio);
     732             :                         }
     733             :                 } while (0);
     734           0 :                 PACKET_FREE(pk_req_packet);
     735           0 :                 PACKET_FREE(pk_resp_packet);
     736             : 
     737           0 :                 DBG_INF_FMT("ret=%p", ret);
     738           0 :                 DBG_RETURN(ret);
     739             : 
     740             :                 SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE,
     741             :                         "sha256_server_public_key is not set for the connection or as mysqlnd.sha256_server_public_key");
     742             :                 DBG_ERR("server_public_key is not set");
     743             :                 DBG_RETURN(NULL);
     744             :         } else {
     745             :                 zend_string * key_str;
     746           0 :                 DBG_INF_FMT("Key in a file. [%s]", fname);
     747           0 :                 stream = php_stream_open_wrapper((char *) fname, "rb", REPORT_ERRORS, NULL);
     748             : 
     749           0 :                 if (stream) {
     750           0 :                         if ((key_str = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0)) != NULL) {
     751           0 :                                 BIO * bio = BIO_new_mem_buf(ZSTR_VAL(key_str), ZSTR_LEN(key_str));
     752           0 :                                 ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
     753           0 :                                 BIO_free(bio);
     754           0 :                                 DBG_INF("Successfully loaded");
     755           0 :                                 DBG_INF_FMT("Public key:%*.s", ZSTR_LEN(key_str), ZSTR_VAL(key_str));
     756             :                                 zend_string_release(key_str);
     757             :                         }
     758           0 :                         php_stream_close(stream);
     759             :                 }
     760             :         }
     761           0 :         DBG_RETURN(ret);
     762             : }
     763             : /* }}} */
     764             : 
     765             : 
     766             : /* {{{ mysqlnd_sha256_auth_get_auth_data */
     767             : static zend_uchar *
     768           0 : mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
     769             :                                                                   size_t * auth_data_len,
     770             :                                                                   MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
     771             :                                                                   const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
     772             :                                                                   const MYSQLND_SESSION_OPTIONS * const session_options,
     773             :                                                                   const MYSQLND_PFC_DATA * const pfc_data,
     774             :                                                                   zend_ulong mysql_flags
     775             :                                                                  )
     776             : {
     777             :         RSA * server_public_key;
     778           0 :         zend_uchar * ret = NULL;
     779           0 :         DBG_ENTER("mysqlnd_sha256_auth_get_auth_data");
     780           0 :         DBG_INF_FMT("salt(%d)=[%.*s]", auth_plugin_data_len, auth_plugin_data_len, auth_plugin_data);
     781             : 
     782             : 
     783           0 :         if (conn->protocol_frame_codec->data->ssl) {
     784           0 :                 DBG_INF("simple clear text under SSL");
     785             :                 /* clear text under SSL */
     786           0 :                 *auth_data_len = passwd_len;
     787           0 :                 ret = malloc(passwd_len);
     788           0 :                 memcpy(ret, passwd, passwd_len);
     789             :         } else {
     790           0 :                 *auth_data_len = 0;
     791           0 :                 server_public_key = mysqlnd_sha256_get_rsa_key(conn, session_options, pfc_data);
     792             : 
     793           0 :                 if (server_public_key) {
     794             :                         int server_public_key_len;
     795           0 :                         char xor_str[passwd_len + 1];
     796           0 :                         memcpy(xor_str, passwd, passwd_len);
     797           0 :                         xor_str[passwd_len] = '\0';
     798           0 :                         mysqlnd_xor_string(xor_str, passwd_len, (char *) auth_plugin_data, auth_plugin_data_len);
     799             : 
     800           0 :                         server_public_key_len = RSA_size(server_public_key);
     801             :                         /*
     802             :                           Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
     803             :                           RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
     804             :                           http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
     805             :                         */
     806           0 :                         if ((size_t) server_public_key_len - 41 <= passwd_len) {
     807             :                                 /* password message is to long */
     808           0 :                                 SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long");
     809           0 :                                 DBG_ERR("password is too long");
     810           0 :                                 DBG_RETURN(NULL);
     811             :                         }
     812             : 
     813           0 :                         *auth_data_len = server_public_key_len;
     814           0 :                         ret = malloc(*auth_data_len);
     815           0 :                         RSA_public_encrypt(passwd_len + 1, (zend_uchar *) xor_str, ret, server_public_key, RSA_PKCS1_OAEP_PADDING);
     816             :                 }
     817             :         }
     818             : 
     819           0 :         DBG_RETURN(ret);
     820             : }
     821             : /* }}} */
     822             : 
     823             : 
     824             : static struct st_mysqlnd_authentication_plugin mysqlnd_sha256_authentication_plugin =
     825             : {
     826             :         {
     827             :                 MYSQLND_PLUGIN_API_VERSION,
     828             :                 "auth_plugin_sha256_password",
     829             :                 MYSQLND_VERSION_ID,
     830             :                 PHP_MYSQLND_VERSION,
     831             :                 "PHP License 3.01",
     832             :                 "Andrey Hristov <andrey@php.net>,  Ulf Wendel <uwendel@mysql.com>",
     833             :                 {
     834             :                         NULL, /* no statistics , will be filled later if there are some */
     835             :                         NULL, /* no statistics */
     836             :                 },
     837             :                 {
     838             :                         NULL /* plugin shutdown */
     839             :                 }
     840             :         },
     841             :         {/* methods */
     842             :                 mysqlnd_sha256_auth_get_auth_data
     843             :         }
     844             : };
     845             : #endif
     846             : 
     847             : /* {{{ mysqlnd_register_builtin_authentication_plugins */
     848             : void
     849       23409 : mysqlnd_register_builtin_authentication_plugins(void)
     850             : {
     851       23409 :         mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_native_auth_plugin);
     852       23409 :         mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_pam_authentication_plugin);
     853             : #ifdef MYSQLND_HAVE_SSL
     854       23409 :         mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_sha256_authentication_plugin);
     855             : #endif
     856       23409 : }
     857             : /* }}} */
     858             : 
     859             : 
     860             : /*
     861             :  * Local variables:
     862             :  * tab-width: 4
     863             :  * c-basic-offset: 4
     864             :  * End:
     865             :  * vim600: noet sw=4 ts=4 fdm=marker
     866             :  * vim<600: noet sw=4 ts=4
     867             :  */

Generated by: LCOV version 1.10

Generated at Wed, 20 Jul 2016 02:56:21 +0000 (3 days ago)

Copyright © 2005-2016 The PHP Group
All rights reserved.