mysqlnd_auth.c 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | https://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Authors: Andrey Hristov <andrey@php.net> |
  14. | Ulf Wendel <uw@php.net> |
  15. +----------------------------------------------------------------------+
  16. */
  17. #include "php.h"
  18. #include "mysqlnd.h"
  19. #include "mysqlnd_structs.h"
  20. #include "mysqlnd_auth.h"
  21. #include "mysqlnd_wireprotocol.h"
  22. #include "mysqlnd_connection.h"
  23. #include "mysqlnd_priv.h"
  24. #include "mysqlnd_charset.h"
  25. #include "mysqlnd_debug.h"
  26. static const char * const mysqlnd_old_passwd = "mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. "
  27. "Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will "
  28. "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 "
  29. "flag from your my.cnf file";
  30. /* {{{ mysqlnd_run_authentication */
  31. enum_func_status
  32. mysqlnd_run_authentication(
  33. MYSQLND_CONN_DATA * const conn,
  34. const char * const user,
  35. const char * const passwd,
  36. const size_t passwd_len,
  37. const char * const db,
  38. const size_t db_len,
  39. const MYSQLND_STRING auth_plugin_data,
  40. const char * const auth_protocol,
  41. const unsigned int charset_no,
  42. const MYSQLND_SESSION_OPTIONS * const session_options,
  43. const zend_ulong mysql_flags,
  44. const bool silent,
  45. const bool is_change_user
  46. )
  47. {
  48. enum_func_status ret = FAIL;
  49. bool first_call = TRUE;
  50. char * switch_to_auth_protocol = NULL;
  51. size_t switch_to_auth_protocol_len = 0;
  52. char * requested_protocol = NULL;
  53. zend_uchar * plugin_data;
  54. size_t plugin_data_len;
  55. DBG_ENTER("mysqlnd_run_authentication");
  56. plugin_data_len = auth_plugin_data.l;
  57. plugin_data = mnd_emalloc(plugin_data_len + 1);
  58. if (!plugin_data) {
  59. goto end;
  60. }
  61. memcpy(plugin_data, auth_plugin_data.s, plugin_data_len);
  62. plugin_data[plugin_data_len] = '\0';
  63. requested_protocol = mnd_pestrdup(auth_protocol? auth_protocol : MYSQLND_DEFAULT_AUTH_PROTOCOL, FALSE);
  64. if (!requested_protocol) {
  65. goto end;
  66. }
  67. do {
  68. struct st_mysqlnd_authentication_plugin * auth_plugin = conn->m->fetch_auth_plugin_by_name(requested_protocol);
  69. if (!auth_plugin) {
  70. if (first_call) {
  71. mnd_pefree(requested_protocol, FALSE);
  72. requested_protocol = mnd_pestrdup(MYSQLND_DEFAULT_AUTH_PROTOCOL, FALSE);
  73. } else {
  74. char * msg;
  75. mnd_sprintf(&msg, 0, "The server requested authentication method unknown to the client [%s]", requested_protocol);
  76. SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, msg);
  77. mnd_sprintf_free(msg);
  78. goto end;
  79. }
  80. }
  81. {
  82. zend_uchar * switch_to_auth_protocol_data = NULL;
  83. size_t switch_to_auth_protocol_data_len = 0;
  84. zend_uchar * scrambled_data = NULL;
  85. size_t scrambled_data_len = 0;
  86. switch_to_auth_protocol = NULL;
  87. switch_to_auth_protocol_len = 0;
  88. mysqlnd_set_persistent_string(&conn->authentication_plugin_data, NULL, 0, conn->persistent);
  89. conn->authentication_plugin_data.l = plugin_data_len;
  90. conn->authentication_plugin_data.s = mnd_pemalloc(conn->authentication_plugin_data.l, conn->persistent);
  91. memcpy(conn->authentication_plugin_data.s, plugin_data, plugin_data_len);
  92. DBG_INF_FMT("salt(%zu)=[%.*s]", plugin_data_len, (int) plugin_data_len, plugin_data);
  93. /* The data should be allocated with malloc() */
  94. if (auth_plugin) {
  95. scrambled_data = auth_plugin->methods.get_auth_data(
  96. NULL, &scrambled_data_len, conn, user, passwd,
  97. passwd_len, plugin_data, plugin_data_len,
  98. session_options, conn->protocol_frame_codec->data,
  99. mysql_flags);
  100. }
  101. if (conn->error_info->error_no) {
  102. goto end;
  103. }
  104. if (FALSE == is_change_user) {
  105. ret = mysqlnd_auth_handshake(conn, user, passwd, passwd_len, db, db_len, session_options, mysql_flags,
  106. charset_no,
  107. first_call,
  108. requested_protocol,
  109. auth_plugin, plugin_data, plugin_data_len,
  110. scrambled_data, scrambled_data_len,
  111. &switch_to_auth_protocol, &switch_to_auth_protocol_len,
  112. &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
  113. );
  114. } else {
  115. ret = mysqlnd_auth_change_user(conn, user, strlen(user), passwd, passwd_len, db, db_len, silent,
  116. first_call,
  117. requested_protocol,
  118. auth_plugin, plugin_data, plugin_data_len,
  119. scrambled_data, scrambled_data_len,
  120. &switch_to_auth_protocol, &switch_to_auth_protocol_len,
  121. &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
  122. );
  123. }
  124. first_call = FALSE;
  125. free(scrambled_data);
  126. DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
  127. if (requested_protocol && switch_to_auth_protocol) {
  128. mnd_efree(requested_protocol);
  129. requested_protocol = switch_to_auth_protocol;
  130. }
  131. if (plugin_data) {
  132. mnd_efree(plugin_data);
  133. }
  134. plugin_data_len = switch_to_auth_protocol_data_len;
  135. plugin_data = switch_to_auth_protocol_data;
  136. }
  137. DBG_INF_FMT("conn->error_info->error_no = %d", conn->error_info->error_no);
  138. } while (ret == FAIL && conn->error_info->error_no == 0 && switch_to_auth_protocol != NULL);
  139. if (ret == PASS) {
  140. DBG_INF_FMT("saving requested_protocol=%s", requested_protocol);
  141. conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol);
  142. }
  143. end:
  144. if (plugin_data) {
  145. mnd_efree(plugin_data);
  146. }
  147. if (requested_protocol) {
  148. mnd_efree(requested_protocol);
  149. }
  150. DBG_RETURN(ret);
  151. }
  152. /* }}} */
  153. /* {{{ mysqlnd_switch_to_ssl_if_needed */
  154. static enum_func_status
  155. mysqlnd_switch_to_ssl_if_needed(MYSQLND_CONN_DATA * const conn,
  156. unsigned int charset_no,
  157. const size_t server_capabilities,
  158. const MYSQLND_SESSION_OPTIONS * const session_options,
  159. const zend_ulong mysql_flags)
  160. {
  161. enum_func_status ret = FAIL;
  162. const MYSQLND_CHARSET * charset;
  163. DBG_ENTER("mysqlnd_switch_to_ssl_if_needed");
  164. if (session_options->charset_name && (charset = mysqlnd_find_charset_name(session_options->charset_name))) {
  165. charset_no = charset->nr;
  166. }
  167. {
  168. const size_t client_capabilities = mysql_flags;
  169. ret = conn->command->enable_ssl(conn, client_capabilities, server_capabilities, charset_no);
  170. }
  171. DBG_RETURN(ret);
  172. }
  173. /* }}} */
  174. /* {{{ mysqlnd_connect_run_authentication */
  175. enum_func_status
  176. mysqlnd_connect_run_authentication(
  177. MYSQLND_CONN_DATA * const conn,
  178. const char * const user,
  179. const char * const passwd,
  180. const char * const db,
  181. const size_t db_len,
  182. const size_t passwd_len,
  183. const MYSQLND_STRING authentication_plugin_data,
  184. const char * const authentication_protocol,
  185. const unsigned int charset_no,
  186. const size_t server_capabilities,
  187. const MYSQLND_SESSION_OPTIONS * const session_options,
  188. const zend_ulong mysql_flags
  189. )
  190. {
  191. enum_func_status ret = FAIL;
  192. DBG_ENTER("mysqlnd_connect_run_authentication");
  193. ret = mysqlnd_switch_to_ssl_if_needed(conn, charset_no, server_capabilities, session_options, mysql_flags);
  194. if (PASS == ret) {
  195. ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, db_len,
  196. authentication_plugin_data, authentication_protocol,
  197. charset_no, session_options, mysql_flags, FALSE /*silent*/, FALSE/*is_change*/);
  198. }
  199. DBG_RETURN(ret);
  200. }
  201. /* }}} */
  202. /* {{{ mysqlnd_auth_handshake */
  203. enum_func_status
  204. mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
  205. const char * const user,
  206. const char * const passwd,
  207. const size_t passwd_len,
  208. const char * const db,
  209. const size_t db_len,
  210. const MYSQLND_SESSION_OPTIONS * const session_options,
  211. const zend_ulong mysql_flags,
  212. const unsigned int server_charset_no,
  213. const bool use_full_blown_auth_packet,
  214. const char * const auth_protocol,
  215. struct st_mysqlnd_authentication_plugin * auth_plugin,
  216. const zend_uchar * const orig_auth_plugin_data,
  217. const size_t orig_auth_plugin_data_len,
  218. const zend_uchar * const auth_plugin_data,
  219. const size_t auth_plugin_data_len,
  220. char ** switch_to_auth_protocol,
  221. size_t * const switch_to_auth_protocol_len,
  222. zend_uchar ** switch_to_auth_protocol_data,
  223. size_t * const switch_to_auth_protocol_data_len
  224. )
  225. {
  226. enum_func_status ret = FAIL;
  227. const MYSQLND_CHARSET * charset = NULL;
  228. MYSQLND_PACKET_AUTH_RESPONSE auth_resp_packet;
  229. DBG_ENTER("mysqlnd_auth_handshake");
  230. conn->payload_decoder_factory->m.init_auth_response_packet(&auth_resp_packet);
  231. if (use_full_blown_auth_packet != TRUE) {
  232. MYSQLND_PACKET_CHANGE_AUTH_RESPONSE change_auth_resp_packet;
  233. conn->payload_decoder_factory->m.init_change_auth_response_packet(&change_auth_resp_packet);
  234. change_auth_resp_packet.auth_data = auth_plugin_data;
  235. change_auth_resp_packet.auth_data_len = auth_plugin_data_len;
  236. if (!PACKET_WRITE(conn, &change_auth_resp_packet)) {
  237. SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
  238. SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
  239. PACKET_FREE(&change_auth_resp_packet);
  240. goto end;
  241. }
  242. PACKET_FREE(&change_auth_resp_packet);
  243. } else {
  244. MYSQLND_PACKET_AUTH auth_packet;
  245. conn->payload_decoder_factory->m.init_auth_packet(&auth_packet);
  246. auth_packet.client_flags = mysql_flags;
  247. auth_packet.max_packet_size = session_options->max_allowed_packet;
  248. if (session_options->charset_name && (charset = mysqlnd_find_charset_name(session_options->charset_name))) {
  249. auth_packet.charset_no = charset->nr;
  250. } else {
  251. auth_packet.charset_no = server_charset_no;
  252. }
  253. auth_packet.send_auth_data = TRUE;
  254. auth_packet.user = user;
  255. auth_packet.db = db;
  256. auth_packet.db_len = db_len;
  257. auth_packet.auth_data = auth_plugin_data;
  258. auth_packet.auth_data_len = auth_plugin_data_len;
  259. auth_packet.auth_plugin_name = auth_protocol;
  260. if (conn->server_capabilities & CLIENT_CONNECT_ATTRS) {
  261. auth_packet.connect_attr = conn->options->connect_attr;
  262. }
  263. if (!PACKET_WRITE(conn, &auth_packet)) {
  264. PACKET_FREE(&auth_packet);
  265. goto end;
  266. }
  267. if (use_full_blown_auth_packet == TRUE) {
  268. conn->charset = mysqlnd_find_charset_nr(auth_packet.charset_no);
  269. }
  270. PACKET_FREE(&auth_packet);
  271. }
  272. if (auth_plugin && auth_plugin->methods.handle_server_response) {
  273. if (FAIL == auth_plugin->methods.handle_server_response(auth_plugin, conn,
  274. orig_auth_plugin_data, orig_auth_plugin_data_len, passwd, passwd_len,
  275. switch_to_auth_protocol, switch_to_auth_protocol_len,
  276. switch_to_auth_protocol_data, switch_to_auth_protocol_data_len)) {
  277. goto end;
  278. }
  279. }
  280. if (FAIL == PACKET_READ(conn, &auth_resp_packet) || auth_resp_packet.response_code >= 0xFE) {
  281. if (auth_resp_packet.response_code == 0xFE) {
  282. /* old authentication with new server !*/
  283. if (!auth_resp_packet.new_auth_protocol) {
  284. DBG_ERR(mysqlnd_old_passwd);
  285. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
  286. } else {
  287. *switch_to_auth_protocol = mnd_pestrndup(auth_resp_packet.new_auth_protocol, auth_resp_packet.new_auth_protocol_len, FALSE);
  288. *switch_to_auth_protocol_len = auth_resp_packet.new_auth_protocol_len;
  289. if (auth_resp_packet.new_auth_protocol_data) {
  290. *switch_to_auth_protocol_data_len = auth_resp_packet.new_auth_protocol_data_len;
  291. *switch_to_auth_protocol_data = mnd_emalloc(*switch_to_auth_protocol_data_len);
  292. memcpy(*switch_to_auth_protocol_data, auth_resp_packet.new_auth_protocol_data, *switch_to_auth_protocol_data_len);
  293. } else {
  294. *switch_to_auth_protocol_data = NULL;
  295. *switch_to_auth_protocol_data_len = 0;
  296. }
  297. }
  298. } else if (auth_resp_packet.response_code == 0xFF) {
  299. if (auth_resp_packet.sqlstate[0]) {
  300. strlcpy(conn->error_info->sqlstate, auth_resp_packet.sqlstate, sizeof(conn->error_info->sqlstate));
  301. DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", auth_resp_packet.error_no, auth_resp_packet.sqlstate, auth_resp_packet.error);
  302. }
  303. SET_CLIENT_ERROR(conn->error_info, auth_resp_packet.error_no, UNKNOWN_SQLSTATE, auth_resp_packet.error);
  304. }
  305. goto end;
  306. }
  307. mysqlnd_set_string(&conn->last_message, auth_resp_packet.message, auth_resp_packet.message_len);
  308. ret = PASS;
  309. end:
  310. PACKET_FREE(&auth_resp_packet);
  311. DBG_RETURN(ret);
  312. }
  313. /* }}} */
  314. /* {{{ mysqlnd_auth_change_user */
  315. enum_func_status
  316. mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
  317. const char * const user,
  318. const size_t user_len,
  319. const char * const passwd,
  320. const size_t passwd_len,
  321. const char * const db,
  322. const size_t db_len,
  323. const bool silent,
  324. const bool use_full_blown_auth_packet,
  325. const char * const auth_protocol,
  326. struct st_mysqlnd_authentication_plugin * auth_plugin,
  327. const zend_uchar * const orig_auth_plugin_data,
  328. const size_t orig_auth_plugin_data_len,
  329. const zend_uchar * const auth_plugin_data,
  330. const size_t auth_plugin_data_len,
  331. char ** switch_to_auth_protocol,
  332. size_t * const switch_to_auth_protocol_len,
  333. zend_uchar ** switch_to_auth_protocol_data,
  334. size_t * const switch_to_auth_protocol_data_len
  335. )
  336. {
  337. enum_func_status ret = FAIL;
  338. const MYSQLND_CHARSET * old_cs = conn->charset;
  339. MYSQLND_PACKET_CHG_USER_RESPONSE chg_user_resp;
  340. DBG_ENTER("mysqlnd_auth_change_user");
  341. conn->payload_decoder_factory->m.init_change_user_response_packet(&chg_user_resp);
  342. if (use_full_blown_auth_packet != TRUE) {
  343. MYSQLND_PACKET_CHANGE_AUTH_RESPONSE change_auth_resp_packet;
  344. conn->payload_decoder_factory->m.init_change_auth_response_packet(&change_auth_resp_packet);
  345. change_auth_resp_packet.auth_data = auth_plugin_data;
  346. change_auth_resp_packet.auth_data_len = auth_plugin_data_len;
  347. if (!PACKET_WRITE(conn, &change_auth_resp_packet)) {
  348. SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
  349. SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
  350. PACKET_FREE(&change_auth_resp_packet);
  351. goto end;
  352. }
  353. PACKET_FREE(&change_auth_resp_packet);
  354. } else {
  355. MYSQLND_PACKET_AUTH auth_packet;
  356. conn->payload_decoder_factory->m.init_auth_packet(&auth_packet);
  357. auth_packet.is_change_user_packet = TRUE;
  358. auth_packet.user = user;
  359. auth_packet.db = db;
  360. auth_packet.db_len = db_len;
  361. auth_packet.silent = silent;
  362. auth_packet.auth_data = auth_plugin_data;
  363. auth_packet.auth_data_len = auth_plugin_data_len;
  364. auth_packet.auth_plugin_name = auth_protocol;
  365. if (conn->server_capabilities & CLIENT_CONNECT_ATTRS) {
  366. auth_packet.connect_attr = conn->options->connect_attr;
  367. }
  368. if (conn->m->get_server_version(conn) >= 50123) {
  369. auth_packet.charset_no = conn->charset->nr;
  370. }
  371. if (!PACKET_WRITE(conn, &auth_packet)) {
  372. SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
  373. SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
  374. PACKET_FREE(&auth_packet);
  375. goto end;
  376. }
  377. PACKET_FREE(&auth_packet);
  378. }
  379. if (auth_plugin && auth_plugin->methods.handle_server_response) {
  380. if (FAIL == auth_plugin->methods.handle_server_response(auth_plugin, conn,
  381. orig_auth_plugin_data, orig_auth_plugin_data_len, passwd, passwd_len,
  382. switch_to_auth_protocol, switch_to_auth_protocol_len,
  383. switch_to_auth_protocol_data, switch_to_auth_protocol_data_len)) {
  384. goto end;
  385. }
  386. }
  387. ret = PACKET_READ(conn, &chg_user_resp);
  388. COPY_CLIENT_ERROR(conn->error_info, chg_user_resp.error_info);
  389. if (0xFE == chg_user_resp.response_code) {
  390. ret = FAIL;
  391. if (!chg_user_resp.new_auth_protocol) {
  392. DBG_ERR(mysqlnd_old_passwd);
  393. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
  394. } else {
  395. *switch_to_auth_protocol = mnd_pestrndup(chg_user_resp.new_auth_protocol, chg_user_resp.new_auth_protocol_len, FALSE);
  396. *switch_to_auth_protocol_len = chg_user_resp.new_auth_protocol_len;
  397. if (chg_user_resp.new_auth_protocol_data) {
  398. *switch_to_auth_protocol_data_len = chg_user_resp.new_auth_protocol_data_len;
  399. *switch_to_auth_protocol_data = mnd_emalloc(*switch_to_auth_protocol_data_len);
  400. memcpy(*switch_to_auth_protocol_data, chg_user_resp.new_auth_protocol_data, *switch_to_auth_protocol_data_len);
  401. } else {
  402. *switch_to_auth_protocol_data = NULL;
  403. *switch_to_auth_protocol_data_len = 0;
  404. }
  405. }
  406. }
  407. if (conn->error_info->error_no) {
  408. ret = FAIL;
  409. /*
  410. COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune.
  411. bug#25371 mysql_change_user() triggers "packets out of sync"
  412. When it gets fixed, there should be one more check here
  413. */
  414. if (conn->m->get_server_version(conn) > 50113L &&conn->m->get_server_version(conn) < 50118L) {
  415. MYSQLND_PACKET_OK redundant_error_packet;
  416. conn->payload_decoder_factory->m.init_ok_packet(&redundant_error_packet);
  417. PACKET_READ(conn, &redundant_error_packet);
  418. PACKET_FREE(&redundant_error_packet);
  419. DBG_INF_FMT("Server is " ZEND_ULONG_FMT ", buggy, sends two ERR messages", conn->m->get_server_version(conn));
  420. }
  421. }
  422. if (ret == PASS) {
  423. ZEND_ASSERT(conn->username.s != user && conn->password.s != passwd);
  424. mysqlnd_set_persistent_string(&conn->username, user, user_len, conn->persistent);
  425. mysqlnd_set_persistent_string(&conn->password, passwd, passwd_len, conn->persistent);
  426. mysqlnd_set_string(&conn->last_message, NULL, 0);
  427. UPSERT_STATUS_RESET(conn->upsert_status);
  428. /* set charset for old servers */
  429. if (conn->m->get_server_version(conn) < 50123) {
  430. ret = conn->m->set_charset(conn, old_cs->name);
  431. }
  432. } else if (ret == FAIL && chg_user_resp.server_asked_323_auth == TRUE) {
  433. /* old authentication with new server !*/
  434. DBG_ERR(mysqlnd_old_passwd);
  435. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
  436. }
  437. end:
  438. PACKET_FREE(&chg_user_resp);
  439. DBG_RETURN(ret);
  440. }
  441. /* }}} */
  442. /******************************************* MySQL Native Password ***********************************/
  443. #include "ext/standard/sha1.h"
  444. /* {{{ php_mysqlnd_crypt */
  445. static void
  446. php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len)
  447. {
  448. const zend_uchar *s1_end = s1 + len;
  449. while (s1 < s1_end) {
  450. *buffer++= *s1++ ^ *s2++;
  451. }
  452. }
  453. /* }}} */
  454. /* {{{ php_mysqlnd_scramble */
  455. void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password, const size_t password_len)
  456. {
  457. PHP_SHA1_CTX context;
  458. zend_uchar sha1[SHA1_MAX_LENGTH];
  459. zend_uchar sha2[SHA1_MAX_LENGTH];
  460. /* Phase 1: hash password */
  461. PHP_SHA1Init(&context);
  462. PHP_SHA1Update(&context, password, password_len);
  463. PHP_SHA1Final(sha1, &context);
  464. /* Phase 2: hash sha1 */
  465. PHP_SHA1Init(&context);
  466. PHP_SHA1Update(&context, (zend_uchar*)sha1, SHA1_MAX_LENGTH);
  467. PHP_SHA1Final(sha2, &context);
  468. /* Phase 3: hash scramble + sha2 */
  469. PHP_SHA1Init(&context);
  470. PHP_SHA1Update(&context, scramble, SCRAMBLE_LENGTH);
  471. PHP_SHA1Update(&context, (zend_uchar*)sha2, SHA1_MAX_LENGTH);
  472. PHP_SHA1Final(buffer, &context);
  473. /* let's crypt buffer now */
  474. php_mysqlnd_crypt(buffer, (const zend_uchar *)buffer, (const zend_uchar *)sha1, SHA1_MAX_LENGTH);
  475. }
  476. /* }}} */
  477. /* {{{ mysqlnd_native_auth_get_auth_data */
  478. static zend_uchar *
  479. mysqlnd_native_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
  480. size_t * auth_data_len,
  481. MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
  482. const size_t passwd_len, zend_uchar * auth_plugin_data, const size_t auth_plugin_data_len,
  483. const MYSQLND_SESSION_OPTIONS * const session_options,
  484. const MYSQLND_PFC_DATA * const pfc_data,
  485. const zend_ulong mysql_flags
  486. )
  487. {
  488. zend_uchar * ret = NULL;
  489. DBG_ENTER("mysqlnd_native_auth_get_auth_data");
  490. *auth_data_len = 0;
  491. /* 5.5.x reports 21 as scramble length because it needs to show the length of the data before the plugin name */
  492. if (auth_plugin_data_len < SCRAMBLE_LENGTH) {
  493. /* mysql_native_password only works with SCRAMBLE_LENGTH scramble */
  494. SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "The server sent wrong length for scramble");
  495. DBG_ERR_FMT("The server sent wrong length for scramble %zu. Expected %u", auth_plugin_data_len, SCRAMBLE_LENGTH);
  496. DBG_RETURN(NULL);
  497. }
  498. /* copy scrambled pass*/
  499. if (passwd && passwd_len) {
  500. ret = malloc(SCRAMBLE_LENGTH);
  501. *auth_data_len = SCRAMBLE_LENGTH;
  502. /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */
  503. php_mysqlnd_scramble((zend_uchar*)ret, auth_plugin_data, (zend_uchar*)passwd, passwd_len);
  504. }
  505. DBG_RETURN(ret);
  506. }
  507. /* }}} */
  508. static struct st_mysqlnd_authentication_plugin mysqlnd_native_auth_plugin =
  509. {
  510. {
  511. MYSQLND_PLUGIN_API_VERSION,
  512. "auth_plugin_mysql_native_password",
  513. MYSQLND_VERSION_ID,
  514. PHP_MYSQLND_VERSION,
  515. "PHP License 3.01",
  516. "Andrey Hristov <andrey@php.net>, Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
  517. {
  518. NULL, /* no statistics , will be filled later if there are some */
  519. NULL, /* no statistics */
  520. },
  521. {
  522. NULL /* plugin shutdown */
  523. }
  524. },
  525. {/* methods */
  526. mysqlnd_native_auth_get_auth_data,
  527. NULL
  528. }
  529. };
  530. /******************************************* PAM Authentication ***********************************/
  531. /* {{{ mysqlnd_pam_auth_get_auth_data */
  532. static zend_uchar *
  533. mysqlnd_pam_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
  534. size_t * auth_data_len,
  535. MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
  536. const size_t passwd_len, zend_uchar * auth_plugin_data, const size_t auth_plugin_data_len,
  537. const MYSQLND_SESSION_OPTIONS * const session_options,
  538. const MYSQLND_PFC_DATA * const pfc_data,
  539. const zend_ulong mysql_flags
  540. )
  541. {
  542. zend_uchar * ret = NULL;
  543. /* copy pass*/
  544. if (passwd && passwd_len) {
  545. ret = (zend_uchar*) zend_strndup(passwd, passwd_len);
  546. }
  547. /*
  548. Trailing null required. bug#78680
  549. https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_authentication_methods_clear_text_password.html
  550. */
  551. *auth_data_len = passwd_len + 1;
  552. return ret;
  553. }
  554. /* }}} */
  555. static struct st_mysqlnd_authentication_plugin mysqlnd_pam_authentication_plugin =
  556. {
  557. {
  558. MYSQLND_PLUGIN_API_VERSION,
  559. "auth_plugin_mysql_clear_password",
  560. MYSQLND_VERSION_ID,
  561. PHP_MYSQLND_VERSION,
  562. "PHP License 3.01",
  563. "Andrey Hristov <andrey@php.net>, Ulf Wendel <uw@php.net>, Georg Richter <georg@php.net>",
  564. {
  565. NULL, /* no statistics , will be filled later if there are some */
  566. NULL, /* no statistics */
  567. },
  568. {
  569. NULL /* plugin shutdown */
  570. }
  571. },
  572. {/* methods */
  573. mysqlnd_pam_auth_get_auth_data,
  574. NULL
  575. }
  576. };
  577. /******************************************* SHA256 Password ***********************************/
  578. #ifdef MYSQLND_HAVE_SSL
  579. static void
  580. mysqlnd_xor_string(char * dst, const size_t dst_len, const char * xor_str, const size_t xor_str_len)
  581. {
  582. unsigned int i;
  583. for (i = 0; i <= dst_len; ++i) {
  584. dst[i] ^= xor_str[i % xor_str_len];
  585. }
  586. }
  587. #ifndef PHP_WIN32
  588. #include <openssl/rsa.h>
  589. #include <openssl/pem.h>
  590. #include <openssl/err.h>
  591. typedef EVP_PKEY * mysqlnd_rsa_t;
  592. /* {{{ mysqlnd_sha256_get_rsa_from_pem */
  593. static mysqlnd_rsa_t
  594. mysqlnd_sha256_get_rsa_from_pem(const char *buf, size_t len)
  595. {
  596. BIO *bio = BIO_new_mem_buf(buf, len);
  597. EVP_PKEY *ret = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
  598. BIO_free(bio);
  599. return ret;
  600. }
  601. /* }}} */
  602. /* {{{ mysqlnd_sha256_public_encrypt */
  603. static zend_uchar *
  604. mysqlnd_sha256_public_encrypt(MYSQLND_CONN_DATA * conn, mysqlnd_rsa_t server_public_key, size_t passwd_len, size_t * auth_data_len, char *xor_str)
  605. {
  606. zend_uchar * ret = NULL;
  607. size_t server_public_key_len = (size_t) EVP_PKEY_size(server_public_key);
  608. DBG_ENTER("mysqlnd_sha256_public_encrypt");
  609. /*
  610. Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
  611. RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
  612. http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
  613. */
  614. if (server_public_key_len <= passwd_len + 41) {
  615. /* password message is to long */
  616. EVP_PKEY_free(server_public_key);
  617. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long");
  618. DBG_ERR("password is too long");
  619. DBG_RETURN(NULL);
  620. }
  621. *auth_data_len = server_public_key_len;
  622. ret = malloc(*auth_data_len);
  623. EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(server_public_key, NULL);
  624. if (!ctx || EVP_PKEY_encrypt_init(ctx) <= 0 ||
  625. EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0 ||
  626. EVP_PKEY_encrypt(ctx, ret, &server_public_key_len, (zend_uchar *) xor_str, passwd_len + 1) <= 0) {
  627. DBG_ERR("encrypt failed");
  628. free(ret);
  629. ret = NULL;
  630. }
  631. EVP_PKEY_CTX_free(ctx);
  632. EVP_PKEY_free(server_public_key);
  633. DBG_RETURN(ret);
  634. }
  635. /* }}} */
  636. #else
  637. #include <wincrypt.h>
  638. #include <bcrypt.h>
  639. typedef HANDLE mysqlnd_rsa_t;
  640. /* {{{ mysqlnd_sha256_get_rsa_from_pem */
  641. static mysqlnd_rsa_t
  642. mysqlnd_sha256_get_rsa_from_pem(const char *buf, size_t len)
  643. {
  644. BCRYPT_KEY_HANDLE ret = 0;
  645. LPSTR der_buf = NULL;
  646. DWORD der_len;
  647. CERT_PUBLIC_KEY_INFO *key_info = NULL;
  648. DWORD key_info_len;
  649. ALLOCA_FLAG(use_heap);
  650. if (!CryptStringToBinaryA(buf, len, CRYPT_STRING_BASE64HEADER, NULL, &der_len, NULL, NULL)) {
  651. goto finish;
  652. }
  653. der_buf = do_alloca(der_len, use_heap);
  654. if (!CryptStringToBinaryA(buf, len, CRYPT_STRING_BASE64HEADER, der_buf, &der_len, NULL, NULL)) {
  655. goto finish;
  656. }
  657. if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, der_buf, der_len, CRYPT_ENCODE_ALLOC_FLAG, NULL, &key_info, &key_info_len)) {
  658. goto finish;
  659. }
  660. if (!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, key_info, CRYPT_OID_INFO_PUBKEY_ENCRYPT_KEY_FLAG, NULL, &ret)) {
  661. goto finish;
  662. }
  663. finish:
  664. if (key_info) {
  665. LocalFree(key_info);
  666. }
  667. if (der_buf) {
  668. free_alloca(der_buf, use_heap);
  669. }
  670. return (mysqlnd_rsa_t) ret;
  671. }
  672. /* }}} */
  673. /* {{{ mysqlnd_sha256_public_encrypt */
  674. static zend_uchar *
  675. mysqlnd_sha256_public_encrypt(MYSQLND_CONN_DATA * conn, mysqlnd_rsa_t server_public_key, size_t passwd_len, size_t * auth_data_len, char *xor_str)
  676. {
  677. zend_uchar * ret = NULL;
  678. DWORD server_public_key_len = passwd_len;
  679. BCRYPT_OAEP_PADDING_INFO padding_info;
  680. DBG_ENTER("mysqlnd_sha256_public_encrypt");
  681. ZeroMemory(&padding_info, sizeof padding_info);
  682. padding_info.pszAlgId = BCRYPT_SHA1_ALGORITHM;
  683. if (BCryptEncrypt((BCRYPT_KEY_HANDLE) server_public_key, xor_str, passwd_len + 1, &padding_info,
  684. NULL, 0, NULL, 0, &server_public_key_len, BCRYPT_PAD_OAEP)) {
  685. DBG_RETURN(0);
  686. }
  687. /*
  688. Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
  689. RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
  690. http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
  691. */
  692. if ((size_t) server_public_key_len <= passwd_len + 41) {
  693. /* password message is to long */
  694. BCryptDestroyKey((BCRYPT_KEY_HANDLE) server_public_key);
  695. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long");
  696. DBG_ERR("password is too long");
  697. DBG_RETURN(0);
  698. }
  699. *auth_data_len = server_public_key_len;
  700. ret = malloc(*auth_data_len);
  701. if (BCryptEncrypt((BCRYPT_KEY_HANDLE) server_public_key, xor_str, passwd_len + 1, &padding_info,
  702. NULL, 0, ret, server_public_key_len, &server_public_key_len, BCRYPT_PAD_OAEP)) {
  703. BCryptDestroyKey((BCRYPT_KEY_HANDLE) server_public_key);
  704. DBG_RETURN(0);
  705. }
  706. BCryptDestroyKey((BCRYPT_KEY_HANDLE) server_public_key);
  707. DBG_RETURN(ret);
  708. }
  709. /* }}} */
  710. #endif
  711. /* {{{ mysqlnd_sha256_get_rsa_key */
  712. static mysqlnd_rsa_t
  713. mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn,
  714. const MYSQLND_SESSION_OPTIONS * const session_options,
  715. const MYSQLND_PFC_DATA * const pfc_data
  716. )
  717. {
  718. mysqlnd_rsa_t ret = NULL;
  719. const char * fname = (pfc_data->sha256_server_public_key && pfc_data->sha256_server_public_key[0] != '\0')?
  720. pfc_data->sha256_server_public_key:
  721. MYSQLND_G(sha256_server_public_key);
  722. php_stream * stream;
  723. DBG_ENTER("mysqlnd_sha256_get_rsa_key");
  724. DBG_INF_FMT("options_s256_pk=[%s] MYSQLND_G(sha256_server_public_key)=[%s]",
  725. pfc_data->sha256_server_public_key? pfc_data->sha256_server_public_key:"n/a",
  726. MYSQLND_G(sha256_server_public_key)? MYSQLND_G(sha256_server_public_key):"n/a");
  727. if (!fname || fname[0] == '\0') {
  728. MYSQLND_PACKET_SHA256_PK_REQUEST pk_req_packet;
  729. MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE pk_resp_packet;
  730. do {
  731. DBG_INF("requesting the public key from the server");
  732. conn->payload_decoder_factory->m.init_sha256_pk_request_packet(&pk_req_packet);
  733. conn->payload_decoder_factory->m.init_sha256_pk_request_response_packet(&pk_resp_packet);
  734. if (! PACKET_WRITE(conn, &pk_req_packet)) {
  735. DBG_ERR_FMT("Error while sending public key request packet");
  736. php_error(E_WARNING, "Error while sending public key request packet. PID=%d", getpid());
  737. SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
  738. break;
  739. }
  740. if (FAIL == PACKET_READ(conn, &pk_resp_packet) || NULL == pk_resp_packet.public_key) {
  741. DBG_ERR_FMT("Error while receiving public key");
  742. php_error(E_WARNING, "Error while receiving public key. PID=%d", getpid());
  743. SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
  744. break;
  745. }
  746. DBG_INF_FMT("Public key(%zu):\n%s", pk_resp_packet.public_key_len, pk_resp_packet.public_key);
  747. /* now extract the public key */
  748. ret = mysqlnd_sha256_get_rsa_from_pem((const char *) pk_resp_packet.public_key, pk_resp_packet.public_key_len);
  749. } while (0);
  750. PACKET_FREE(&pk_req_packet);
  751. PACKET_FREE(&pk_resp_packet);
  752. DBG_INF_FMT("ret=%p", ret);
  753. DBG_RETURN(ret);
  754. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE,
  755. "sha256_server_public_key is not set for the connection or as mysqlnd.sha256_server_public_key");
  756. DBG_ERR("server_public_key is not set");
  757. DBG_RETURN(NULL);
  758. } else {
  759. zend_string * key_str;
  760. DBG_INF_FMT("Key in a file. [%s]", fname);
  761. stream = php_stream_open_wrapper((char *) fname, "rb", REPORT_ERRORS, NULL);
  762. if (stream) {
  763. if ((key_str = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0)) != NULL) {
  764. ret = mysqlnd_sha256_get_rsa_from_pem(ZSTR_VAL(key_str), ZSTR_LEN(key_str));
  765. DBG_INF("Successfully loaded");
  766. DBG_INF_FMT("Public key:%*.s", (int) ZSTR_LEN(key_str), ZSTR_VAL(key_str));
  767. zend_string_release_ex(key_str, 0);
  768. }
  769. php_stream_close(stream);
  770. }
  771. }
  772. DBG_RETURN(ret);
  773. }
  774. /* }}} */
  775. /* {{{ mysqlnd_sha256_auth_get_auth_data */
  776. static zend_uchar *
  777. mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
  778. size_t * auth_data_len,
  779. MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
  780. const size_t passwd_len, zend_uchar * auth_plugin_data, const size_t auth_plugin_data_len,
  781. const MYSQLND_SESSION_OPTIONS * const session_options,
  782. const MYSQLND_PFC_DATA * const pfc_data,
  783. const zend_ulong mysql_flags
  784. )
  785. {
  786. mysqlnd_rsa_t server_public_key;
  787. zend_uchar * ret = NULL;
  788. DBG_ENTER("mysqlnd_sha256_auth_get_auth_data");
  789. DBG_INF_FMT("salt(%zu)=[%.*s]", auth_plugin_data_len, (int) auth_plugin_data_len, auth_plugin_data);
  790. if (conn->vio->data->ssl) {
  791. DBG_INF("simple clear text under SSL");
  792. /* clear text under SSL */
  793. *auth_data_len = passwd_len;
  794. ret = malloc(passwd_len);
  795. memcpy(ret, passwd, passwd_len);
  796. } else {
  797. *auth_data_len = 0;
  798. server_public_key = mysqlnd_sha256_get_rsa_key(conn, session_options, pfc_data);
  799. if (server_public_key) {
  800. ALLOCA_FLAG(use_heap);
  801. char *xor_str = do_alloca(passwd_len + 1, use_heap);
  802. memcpy(xor_str, passwd, passwd_len);
  803. xor_str[passwd_len] = '\0';
  804. mysqlnd_xor_string(xor_str, passwd_len, (char *) auth_plugin_data, auth_plugin_data_len);
  805. ret = mysqlnd_sha256_public_encrypt(conn, server_public_key, passwd_len, auth_data_len, xor_str);
  806. free_alloca(xor_str, use_heap);
  807. }
  808. }
  809. DBG_RETURN(ret);
  810. }
  811. /* }}} */
  812. static struct st_mysqlnd_authentication_plugin mysqlnd_sha256_authentication_plugin =
  813. {
  814. {
  815. MYSQLND_PLUGIN_API_VERSION,
  816. "auth_plugin_sha256_password",
  817. MYSQLND_VERSION_ID,
  818. PHP_MYSQLND_VERSION,
  819. "PHP License 3.01",
  820. "Andrey Hristov <andrey@php.net>, Ulf Wendel <uwendel@mysql.com>",
  821. {
  822. NULL, /* no statistics , will be filled later if there are some */
  823. NULL, /* no statistics */
  824. },
  825. {
  826. NULL /* plugin shutdown */
  827. }
  828. },
  829. {/* methods */
  830. mysqlnd_sha256_auth_get_auth_data,
  831. NULL
  832. }
  833. };
  834. #endif
  835. /*************************************** CACHING SHA2 Password *******************************/
  836. #ifdef MYSQLND_HAVE_SSL
  837. #undef L64
  838. #include "ext/hash/php_hash.h"
  839. #include "ext/hash/php_hash_sha.h"
  840. #define SHA256_LENGTH 32
  841. /* {{{ php_mysqlnd_scramble_sha2 */
  842. void php_mysqlnd_scramble_sha2(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password, const size_t password_len)
  843. {
  844. PHP_SHA256_CTX context;
  845. zend_uchar sha1[SHA256_LENGTH];
  846. zend_uchar sha2[SHA256_LENGTH];
  847. /* Phase 1: hash password */
  848. PHP_SHA256Init(&context);
  849. PHP_SHA256Update(&context, password, password_len);
  850. PHP_SHA256Final(sha1, &context);
  851. /* Phase 2: hash sha1 */
  852. PHP_SHA256Init(&context);
  853. PHP_SHA256Update(&context, (zend_uchar*)sha1, SHA256_LENGTH);
  854. PHP_SHA256Final(sha2, &context);
  855. /* Phase 3: hash scramble + sha2 */
  856. PHP_SHA256Init(&context);
  857. PHP_SHA256Update(&context, (zend_uchar*)sha2, SHA256_LENGTH);
  858. PHP_SHA256Update(&context, scramble, SCRAMBLE_LENGTH);
  859. PHP_SHA256Final(buffer, &context);
  860. /* let's crypt buffer now */
  861. php_mysqlnd_crypt(buffer, (const zend_uchar *)sha1, (const zend_uchar *)buffer, SHA256_LENGTH);
  862. }
  863. /* }}} */
  864. #ifndef PHP_WIN32
  865. /* {{{ mysqlnd_caching_sha2_public_encrypt */
  866. static size_t
  867. mysqlnd_caching_sha2_public_encrypt(MYSQLND_CONN_DATA * conn, mysqlnd_rsa_t server_public_key, size_t passwd_len, unsigned char **crypted, char *xor_str)
  868. {
  869. size_t server_public_key_len = (size_t) EVP_PKEY_size(server_public_key);
  870. DBG_ENTER("mysqlnd_caching_sha2_public_encrypt");
  871. /*
  872. Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
  873. RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
  874. http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
  875. */
  876. if (server_public_key_len <= passwd_len + 41) {
  877. /* password message is to long */
  878. EVP_PKEY_free(server_public_key);
  879. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long");
  880. DBG_ERR("password is too long");
  881. DBG_RETURN(0);
  882. }
  883. *crypted = emalloc(server_public_key_len);
  884. EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(server_public_key, NULL);
  885. if (!ctx || EVP_PKEY_encrypt_init(ctx) <= 0 ||
  886. EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0 ||
  887. EVP_PKEY_encrypt(ctx, *crypted, &server_public_key_len, (zend_uchar *) xor_str, passwd_len + 1) <= 0) {
  888. DBG_ERR("encrypt failed");
  889. server_public_key_len = 0;
  890. }
  891. EVP_PKEY_CTX_free(ctx);
  892. EVP_PKEY_free(server_public_key);
  893. DBG_RETURN(server_public_key_len);
  894. }
  895. /* }}} */
  896. #else
  897. /* {{{ mysqlnd_caching_sha2_public_encrypt */
  898. static size_t
  899. mysqlnd_caching_sha2_public_encrypt(MYSQLND_CONN_DATA * conn, mysqlnd_rsa_t server_public_key, size_t passwd_len, unsigned char **crypted, char *xor_str)
  900. {
  901. DWORD server_public_key_len = passwd_len;
  902. BCRYPT_OAEP_PADDING_INFO padding_info;
  903. DBG_ENTER("mysqlnd_caching_sha2_public_encrypt");
  904. ZeroMemory(&padding_info, sizeof padding_info);
  905. padding_info.pszAlgId = BCRYPT_SHA1_ALGORITHM;
  906. if (BCryptEncrypt((BCRYPT_KEY_HANDLE) server_public_key, xor_str, passwd_len + 1, &padding_info,
  907. NULL, 0, NULL, 0, &server_public_key_len, BCRYPT_PAD_OAEP)) {
  908. DBG_RETURN(0);
  909. }
  910. /*
  911. Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
  912. RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
  913. http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
  914. */
  915. if ((size_t) server_public_key_len <= passwd_len + 41) {
  916. /* password message is to long */
  917. BCryptDestroyKey((BCRYPT_KEY_HANDLE) server_public_key);
  918. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long");
  919. DBG_ERR("password is too long");
  920. DBG_RETURN(0);
  921. }
  922. *crypted = emalloc(server_public_key_len);
  923. if (BCryptEncrypt((BCRYPT_KEY_HANDLE) server_public_key, xor_str, passwd_len + 1, &padding_info,
  924. NULL, 0, *crypted, server_public_key_len, &server_public_key_len, BCRYPT_PAD_OAEP)) {
  925. BCryptDestroyKey((BCRYPT_KEY_HANDLE) server_public_key);
  926. DBG_RETURN(0);
  927. }
  928. BCryptDestroyKey((BCRYPT_KEY_HANDLE) server_public_key);
  929. DBG_RETURN(server_public_key_len);
  930. }
  931. /* }}} */
  932. #endif
  933. /* {{{ mysqlnd_native_auth_get_auth_data */
  934. static zend_uchar *
  935. mysqlnd_caching_sha2_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
  936. size_t * auth_data_len,
  937. MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
  938. const size_t passwd_len, zend_uchar * auth_plugin_data, const size_t auth_plugin_data_len,
  939. const MYSQLND_SESSION_OPTIONS * const session_options,
  940. const MYSQLND_PFC_DATA * const pfc_data,
  941. const zend_ulong mysql_flags
  942. )
  943. {
  944. zend_uchar * ret = NULL;
  945. DBG_ENTER("mysqlnd_caching_sha2_get_auth_data");
  946. DBG_INF_FMT("salt(%zu)=[%.*s]", auth_plugin_data_len, (int) auth_plugin_data_len, auth_plugin_data);
  947. *auth_data_len = 0;
  948. if (auth_plugin_data_len < SCRAMBLE_LENGTH) {
  949. SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "The server sent wrong length for scramble");
  950. DBG_ERR_FMT("The server sent wrong length for scramble %zu. Expected %u", auth_plugin_data_len, SCRAMBLE_LENGTH);
  951. DBG_RETURN(NULL);
  952. }
  953. DBG_INF("First auth step: send hashed password");
  954. /* copy scrambled pass*/
  955. if (passwd && passwd_len) {
  956. ret = malloc(SHA256_LENGTH + 1);
  957. *auth_data_len = SHA256_LENGTH;
  958. php_mysqlnd_scramble_sha2((zend_uchar*)ret, auth_plugin_data, (zend_uchar*)passwd, passwd_len);
  959. ret[SHA256_LENGTH] = '\0';
  960. DBG_INF_FMT("hash(%zu)=[%.*s]", *auth_data_len, (int) *auth_data_len, ret);
  961. }
  962. DBG_RETURN(ret);
  963. }
  964. /* }}} */
  965. static mysqlnd_rsa_t
  966. mysqlnd_caching_sha2_get_key(MYSQLND_CONN_DATA *conn)
  967. {
  968. mysqlnd_rsa_t ret = NULL;
  969. const MYSQLND_PFC_DATA * const pfc_data = conn->protocol_frame_codec->data;
  970. const char * fname = (pfc_data->sha256_server_public_key && pfc_data->sha256_server_public_key[0] != '\0')?
  971. pfc_data->sha256_server_public_key:
  972. MYSQLND_G(sha256_server_public_key);
  973. php_stream * stream;
  974. DBG_ENTER("mysqlnd_cached_sha2_get_key");
  975. DBG_INF_FMT("options_s256_pk=[%s] MYSQLND_G(sha256_server_public_key)=[%s]",
  976. pfc_data->sha256_server_public_key? pfc_data->sha256_server_public_key:"n/a",
  977. MYSQLND_G(sha256_server_public_key)? MYSQLND_G(sha256_server_public_key):"n/a");
  978. if (!fname || fname[0] == '\0') {
  979. MYSQLND_PACKET_CACHED_SHA2_RESULT req_packet;
  980. MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE pk_resp_packet;
  981. do {
  982. DBG_INF("requesting the public key from the server");
  983. conn->payload_decoder_factory->m.init_cached_sha2_result_packet(&req_packet);
  984. conn->payload_decoder_factory->m.init_sha256_pk_request_response_packet(&pk_resp_packet);
  985. req_packet.request = 1;
  986. if (! PACKET_WRITE(conn, &req_packet)) {
  987. DBG_ERR_FMT("Error while sending public key request packet");
  988. php_error(E_WARNING, "Error while sending public key request packet. PID=%d", getpid());
  989. SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
  990. break;
  991. }
  992. if (FAIL == PACKET_READ(conn, &pk_resp_packet) || NULL == pk_resp_packet.public_key) {
  993. DBG_ERR_FMT("Error while receiving public key");
  994. php_error(E_WARNING, "Error while receiving public key. PID=%d", getpid());
  995. SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
  996. break;
  997. }
  998. DBG_INF_FMT("Public key(%zu):\n%s", pk_resp_packet.public_key_len, pk_resp_packet.public_key);
  999. /* now extract the public key */
  1000. ret = mysqlnd_sha256_get_rsa_from_pem((const char *) pk_resp_packet.public_key, pk_resp_packet.public_key_len);
  1001. } while (0);
  1002. PACKET_FREE(&req_packet);
  1003. PACKET_FREE(&pk_resp_packet);
  1004. DBG_INF_FMT("ret=%p", ret);
  1005. DBG_RETURN(ret);
  1006. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE,
  1007. "caching_sha2_server_public_key is not set for the connection or as mysqlnd.sha256_server_public_key");
  1008. DBG_ERR("server_public_key is not set");
  1009. DBG_RETURN(NULL);
  1010. } else {
  1011. zend_string * key_str;
  1012. DBG_INF_FMT("Key in a file. [%s]", fname);
  1013. stream = php_stream_open_wrapper((char *) fname, "rb", REPORT_ERRORS, NULL);
  1014. if (stream) {
  1015. if ((key_str = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0)) != NULL) {
  1016. ret = mysqlnd_sha256_get_rsa_from_pem(ZSTR_VAL(key_str), ZSTR_LEN(key_str));
  1017. DBG_INF("Successfully loaded");
  1018. DBG_INF_FMT("Public key: %*.s", (int) ZSTR_LEN(key_str), ZSTR_VAL(key_str));
  1019. zend_string_release(key_str);
  1020. }
  1021. php_stream_close(stream);
  1022. }
  1023. }
  1024. DBG_RETURN(ret);
  1025. }
  1026. /* {{{ mysqlnd_caching_sha2_get_and_use_key */
  1027. static size_t
  1028. mysqlnd_caching_sha2_get_and_use_key(MYSQLND_CONN_DATA *conn,
  1029. const zend_uchar * auth_plugin_data, const size_t auth_plugin_data_len,
  1030. unsigned char **crypted,
  1031. const char * const passwd,
  1032. const size_t passwd_len)
  1033. {
  1034. mysqlnd_rsa_t server_public_key = mysqlnd_caching_sha2_get_key(conn);
  1035. DBG_ENTER("mysqlnd_caching_sha2_get_and_use_key(");
  1036. if (server_public_key) {
  1037. int server_public_key_len;
  1038. ALLOCA_FLAG(use_heap)
  1039. char *xor_str = do_alloca(passwd_len + 1, use_heap);
  1040. memcpy(xor_str, passwd, passwd_len);
  1041. xor_str[passwd_len] = '\0';
  1042. mysqlnd_xor_string(xor_str, passwd_len, (char *) auth_plugin_data, SCRAMBLE_LENGTH);
  1043. server_public_key_len = mysqlnd_caching_sha2_public_encrypt(conn, server_public_key, passwd_len, crypted, xor_str);
  1044. free_alloca(xor_str, use_heap);
  1045. DBG_RETURN(server_public_key_len);
  1046. }
  1047. DBG_RETURN(0);
  1048. }
  1049. /* }}} */
  1050. static int is_secure_transport(MYSQLND_CONN_DATA *conn) {
  1051. if (conn->vio->data->ssl) {
  1052. return 1;
  1053. }
  1054. return strcmp(conn->vio->data->stream->ops->label, "unix_socket") == 0;
  1055. }
  1056. /* {{{ mysqlnd_caching_sha2_handle_server_response */
  1057. static enum_func_status
  1058. mysqlnd_caching_sha2_handle_server_response(struct st_mysqlnd_authentication_plugin *self,
  1059. MYSQLND_CONN_DATA * conn,
  1060. const zend_uchar * auth_plugin_data, const size_t auth_plugin_data_len,
  1061. const char * const passwd,
  1062. const size_t passwd_len,
  1063. char **new_auth_protocol, size_t *new_auth_protocol_len,
  1064. zend_uchar **new_auth_protocol_data, size_t *new_auth_protocol_data_len
  1065. )
  1066. {
  1067. DBG_ENTER("mysqlnd_caching_sha2_handle_server_response");
  1068. MYSQLND_PACKET_CACHED_SHA2_RESULT result_packet;
  1069. if (passwd_len == 0) {
  1070. DBG_INF("empty password fast path");
  1071. DBG_RETURN(PASS);
  1072. }
  1073. conn->payload_decoder_factory->m.init_cached_sha2_result_packet(&result_packet);
  1074. if (FAIL == PACKET_READ(conn, &result_packet)) {
  1075. DBG_RETURN(PASS);
  1076. }
  1077. switch (result_packet.response_code) {
  1078. case 0xFF:
  1079. if (result_packet.sqlstate[0]) {
  1080. strlcpy(conn->error_info->sqlstate, result_packet.sqlstate, sizeof(conn->error_info->sqlstate));
  1081. DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", result_packet.error_no, result_packet.sqlstate, result_packet.error);
  1082. }
  1083. SET_CLIENT_ERROR(conn->error_info, result_packet.error_no, UNKNOWN_SQLSTATE, result_packet.error);
  1084. DBG_RETURN(FAIL);
  1085. case 0xFE:
  1086. DBG_INF("auth switch response");
  1087. *new_auth_protocol = result_packet.new_auth_protocol;
  1088. *new_auth_protocol_len = result_packet.new_auth_protocol_len;
  1089. *new_auth_protocol_data = result_packet.new_auth_protocol_data;
  1090. *new_auth_protocol_data_len = result_packet.new_auth_protocol_data_len;
  1091. DBG_RETURN(FAIL);
  1092. case 3:
  1093. DBG_INF("fast path succeeded");
  1094. DBG_RETURN(PASS);
  1095. case 4:
  1096. if (is_secure_transport(conn)) {
  1097. DBG_INF("fast path failed, doing full auth via secure transport");
  1098. result_packet.password = (zend_uchar *)passwd;
  1099. result_packet.password_len = passwd_len + 1;
  1100. PACKET_WRITE(conn, &result_packet);
  1101. } else {
  1102. DBG_INF("fast path failed, doing full auth via insecure transport");
  1103. result_packet.password_len = mysqlnd_caching_sha2_get_and_use_key(conn, auth_plugin_data, auth_plugin_data_len, &result_packet.password, passwd, passwd_len);
  1104. PACKET_WRITE(conn, &result_packet);
  1105. efree(result_packet.password);
  1106. }
  1107. DBG_RETURN(PASS);
  1108. case 2:
  1109. // The server tried to send a key, which we didn't expect
  1110. // fall-through
  1111. default: {
  1112. char * msg;
  1113. mnd_sprintf(&msg, 0, "Unexpected server response while doing caching_sha2 auth: %i", result_packet.response_code);
  1114. SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, msg);
  1115. mnd_sprintf_free(msg);
  1116. }
  1117. }
  1118. DBG_RETURN(PASS);
  1119. }
  1120. /* }}} */
  1121. static struct st_mysqlnd_authentication_plugin mysqlnd_caching_sha2_auth_plugin =
  1122. {
  1123. {
  1124. MYSQLND_PLUGIN_API_VERSION,
  1125. "auth_plugin_caching_sha2_password",
  1126. MYSQLND_VERSION_ID,
  1127. PHP_MYSQLND_VERSION,
  1128. "PHP License 3.01",
  1129. "Johannes Schlüter <johannes.schlueter@php.net>",
  1130. {
  1131. NULL, /* no statistics , will be filled later if there are some */
  1132. NULL, /* no statistics */
  1133. },
  1134. {
  1135. NULL /* plugin shutdown */
  1136. }
  1137. },
  1138. {/* methods */
  1139. mysqlnd_caching_sha2_get_auth_data,
  1140. mysqlnd_caching_sha2_handle_server_response
  1141. }
  1142. };
  1143. #endif
  1144. /* {{{ mysqlnd_register_builtin_authentication_plugins */
  1145. void
  1146. mysqlnd_register_builtin_authentication_plugins(void)
  1147. {
  1148. mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_native_auth_plugin);
  1149. mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_pam_authentication_plugin);
  1150. #ifdef MYSQLND_HAVE_SSL
  1151. mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_caching_sha2_auth_plugin);
  1152. mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_sha256_authentication_plugin);
  1153. #endif
  1154. }
  1155. /* }}} */