Skip to content

Commit 84f6f69

Browse files
committed
Merge branch 'PHP-7.1'
2 parents 524f524 + d6e81f0 commit 84f6f69

File tree

7 files changed

+386
-29
lines changed

7 files changed

+386
-29
lines changed

ext/mysqlnd/mysqlnd_auth.c

Lines changed: 281 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ mysqlnd_run_authentication(
8989
}
9090
}
9191

92+
9293
{
9394
zend_uchar * switch_to_auth_protocol_data = NULL;
9495
size_t switch_to_auth_protocol_data_len = 0;
@@ -113,10 +114,11 @@ mysqlnd_run_authentication(
113114
DBG_INF_FMT("salt(%d)=[%.*s]", plugin_data_len, plugin_data_len, plugin_data);
114115
/* The data should be allocated with malloc() */
115116
if (auth_plugin) {
116-
scrambled_data =
117-
auth_plugin->methods.get_auth_data(NULL, &scrambled_data_len, conn, user, passwd, passwd_len,
118-
plugin_data, plugin_data_len, session_options,
119-
conn->protocol_frame_codec->data, mysql_flags);
117+
scrambled_data = auth_plugin->methods.get_auth_data(
118+
NULL, &scrambled_data_len, conn, user, passwd,
119+
passwd_len, plugin_data, plugin_data_len,
120+
session_options, conn->protocol_frame_codec->data,
121+
mysql_flags);
120122
}
121123

122124
if (conn->error_info->error_no) {
@@ -127,6 +129,7 @@ mysqlnd_run_authentication(
127129
charset_no,
128130
first_call,
129131
requested_protocol,
132+
auth_plugin, plugin_data, plugin_data_len,
130133
scrambled_data, scrambled_data_len,
131134
&switch_to_auth_protocol, &switch_to_auth_protocol_len,
132135
&switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
@@ -244,6 +247,9 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
244247
unsigned int server_charset_no,
245248
zend_bool use_full_blown_auth_packet,
246249
const char * const auth_protocol,
250+
struct st_mysqlnd_authentication_plugin * auth_plugin,
251+
const zend_uchar * const orig_auth_plugin_data,
252+
const size_t orig_auth_plugin_data_len,
247253
const zend_uchar * const auth_plugin_data,
248254
const size_t auth_plugin_data_len,
249255
char ** switch_to_auth_protocol,
@@ -313,6 +319,11 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
313319
PACKET_FREE(&auth_packet);
314320
}
315321

322+
if (auth_plugin && auth_plugin->methods.handle_server_response) {
323+
auth_plugin->methods.handle_server_response(auth_plugin, conn,
324+
orig_auth_plugin_data, orig_auth_plugin_data_len, passwd, passwd_len);
325+
}
326+
316327
if (FAIL == PACKET_READ(conn, &auth_resp_packet) || auth_resp_packet.response_code >= 0xFE) {
317328
if (auth_resp_packet.response_code == 0xFE) {
318329
/* old authentication with new server !*/
@@ -596,7 +607,8 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_native_auth_plugin =
596607
}
597608
},
598609
{/* methods */
599-
mysqlnd_native_auth_get_auth_data
610+
mysqlnd_native_auth_get_auth_data,
611+
NULL
600612
}
601613
};
602614

@@ -645,7 +657,8 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_pam_authentication_plugin
645657
}
646658
},
647659
{/* methods */
648-
mysqlnd_pam_auth_get_auth_data
660+
mysqlnd_pam_auth_get_auth_data,
661+
NULL
649662
}
650663
};
651664

@@ -820,17 +833,278 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_sha256_authentication_plu
820833
}
821834
},
822835
{/* methods */
823-
mysqlnd_sha256_auth_get_auth_data
836+
mysqlnd_sha256_auth_get_auth_data,
837+
NULL
824838
}
825839
};
826840
#endif
827841

842+
/*************************************** CACHING SHA2 Password *******************************/
843+
844+
#undef L64
845+
846+
#include "ext/hash/php_hash.h"
847+
#include "ext/hash/php_hash_sha.h"
848+
849+
#define SHA256_LENGTH 32
850+
851+
/* {{{ php_mysqlnd_scramble_sha2 */
852+
void php_mysqlnd_scramble_sha2(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password, const size_t password_len)
853+
{
854+
PHP_SHA256_CTX context;
855+
zend_uchar sha1[SHA256_LENGTH];
856+
zend_uchar sha2[SHA256_LENGTH];
857+
858+
/* Phase 1: hash password */
859+
PHP_SHA256Init(&context);
860+
PHP_SHA256Update(&context, password, password_len);
861+
PHP_SHA256Final(sha1, &context);
862+
863+
/* Phase 2: hash sha1 */
864+
PHP_SHA256Init(&context);
865+
PHP_SHA256Update(&context, (zend_uchar*)sha1, SHA256_LENGTH);
866+
PHP_SHA256Final(sha2, &context);
867+
868+
/* Phase 3: hash scramble + sha2 */
869+
PHP_SHA256Init(&context);
870+
PHP_SHA256Update(&context, (zend_uchar*)sha2, SHA256_LENGTH);
871+
PHP_SHA256Update(&context, scramble, SCRAMBLE_LENGTH);
872+
PHP_SHA256Final(buffer, &context);
873+
874+
/* let's crypt buffer now */
875+
php_mysqlnd_crypt(buffer, (const zend_uchar *)sha1, (const zend_uchar *)buffer, SHA256_LENGTH);
876+
}
877+
/* }}} */
878+
879+
880+
/* {{{ mysqlnd_native_auth_get_auth_data */
881+
static zend_uchar *
882+
mysqlnd_caching_sha2_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
883+
size_t * auth_data_len,
884+
MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
885+
const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
886+
const MYSQLND_SESSION_OPTIONS * const session_options,
887+
const MYSQLND_PFC_DATA * const pfc_data,
888+
zend_ulong mysql_flags
889+
)
890+
{
891+
zend_uchar * ret = NULL;
892+
DBG_ENTER("mysqlnd_caching_sha2_get_auth_data");
893+
DBG_INF_FMT("salt(%d)=[%.*s]", auth_plugin_data_len, auth_plugin_data_len, auth_plugin_data);
894+
*auth_data_len = 0;
895+
896+
DBG_INF("First auth step: send hashed password");
897+
/* copy scrambled pass*/
898+
if (passwd && passwd_len) {
899+
ret = malloc(SHA256_LENGTH + 1);
900+
*auth_data_len = SHA256_LENGTH;
901+
php_mysqlnd_scramble_sha2((zend_uchar*)ret, auth_plugin_data, (zend_uchar*)passwd, passwd_len);
902+
ret[SHA256_LENGTH] = '\0';
903+
DBG_INF_FMT("hash(%d)=[%.*s]", *auth_data_len, *auth_data_len, ret);
904+
}
905+
906+
DBG_RETURN(ret);
907+
}
908+
/* }}} */
909+
910+
#ifdef MYSQLND_HAVE_SSL
911+
static RSA *
912+
mysqlnd_caching_sha2_get_key(MYSQLND_CONN_DATA *conn)
913+
{
914+
RSA * ret = NULL;
915+
const MYSQLND_PFC_DATA * const pfc_data = conn->protocol_frame_codec->data;
916+
const char * fname = (pfc_data->sha256_server_public_key && pfc_data->sha256_server_public_key[0] != '\0')?
917+
pfc_data->sha256_server_public_key:
918+
MYSQLND_G(sha256_server_public_key);
919+
php_stream * stream;
920+
DBG_ENTER("mysqlnd_cached_sha2_get_key");
921+
DBG_INF_FMT("options_s256_pk=[%s] MYSQLND_G(sha256_server_public_key)=[%s]",
922+
pfc_data->sha256_server_public_key? pfc_data->sha256_server_public_key:"n/a",
923+
MYSQLND_G(sha256_server_public_key)? MYSQLND_G(sha256_server_public_key):"n/a");
924+
if (!fname || fname[0] == '\0') {
925+
MYSQLND_PACKET_CACHED_SHA2_RESULT req_packet;
926+
MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE pk_resp_packet;
927+
928+
do {
929+
DBG_INF("requesting the public key from the server");
930+
conn->payload_decoder_factory->m.init_cached_sha2_result_packet(&req_packet);
931+
conn->payload_decoder_factory->m.init_sha256_pk_request_response_packet(&pk_resp_packet);
932+
req_packet.request = 1;
933+
934+
if (! PACKET_WRITE(conn, &req_packet)) {
935+
DBG_ERR_FMT("Error while sending public key request packet");
936+
php_error(E_WARNING, "Error while sending public key request packet. PID=%d", getpid());
937+
SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
938+
break;
939+
}
940+
if (FAIL == PACKET_READ(conn, &pk_resp_packet) || NULL == pk_resp_packet.public_key) {
941+
DBG_ERR_FMT("Error while receiving public key");
942+
php_error(E_WARNING, "Error while receiving public key. PID=%d", getpid());
943+
SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
944+
break;
945+
}
946+
DBG_INF_FMT("Public key(%d):\n%s", pk_resp_packet.public_key_len, pk_resp_packet.public_key);
947+
/* now extract the public key */
948+
{
949+
BIO * bio = BIO_new_mem_buf(pk_resp_packet.public_key, pk_resp_packet.public_key_len);
950+
ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
951+
BIO_free(bio);
952+
}
953+
} while (0);
954+
PACKET_FREE(&req_packet);
955+
PACKET_FREE(&pk_resp_packet);
956+
957+
DBG_INF_FMT("ret=%p", ret);
958+
DBG_RETURN(ret);
959+
960+
SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE,
961+
"caching_sha2_server_public_key is not set for the connection or as mysqlnd.sha256_server_public_key");
962+
DBG_ERR("server_public_key is not set");
963+
DBG_RETURN(NULL);
964+
} else {
965+
zend_string * key_str;
966+
DBG_INF_FMT("Key in a file. [%s]", fname);
967+
stream = php_stream_open_wrapper((char *) fname, "rb", REPORT_ERRORS, NULL);
968+
969+
if (stream) {
970+
if ((key_str = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0)) != NULL) {
971+
BIO * bio = BIO_new_mem_buf(ZSTR_VAL(key_str), ZSTR_LEN(key_str));
972+
ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
973+
BIO_free(bio);
974+
DBG_INF("Successfully loaded");
975+
DBG_INF_FMT("Public key:%*.s", ZSTR_LEN(key_str), ZSTR_VAL(key_str));
976+
zend_string_release(key_str);
977+
}
978+
php_stream_close(stream);
979+
}
980+
}
981+
DBG_RETURN(ret);
982+
983+
}
984+
#endif
985+
986+
987+
/* {{{ mysqlnd_caching_sha2_get_key */
988+
static size_t
989+
mysqlnd_caching_sha2_get_and_use_key(MYSQLND_CONN_DATA *conn,
990+
const zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
991+
unsigned char **crypted,
992+
const char * const passwd,
993+
const size_t passwd_len)
994+
{
995+
#ifdef MYSQLND_HAVE_SSL
996+
static RSA *server_public_key;
997+
server_public_key = mysqlnd_caching_sha2_get_key(conn);
998+
999+
DBG_ENTER("mysqlnd_caching_sha2_get_and_use_key(");
1000+
1001+
if (server_public_key) {
1002+
int server_public_key_len;
1003+
char xor_str[passwd_len + 1];
1004+
memcpy(xor_str, passwd, passwd_len);
1005+
xor_str[passwd_len] = '\0';
1006+
mysqlnd_xor_string(xor_str, passwd_len, (char *) auth_plugin_data, auth_plugin_data_len);
1007+
1008+
server_public_key_len = RSA_size(server_public_key);
1009+
/*
1010+
Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
1011+
RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
1012+
http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
1013+
*/
1014+
if ((size_t) server_public_key_len - 41 <= passwd_len) {
1015+
/* password message is to long */
1016+
SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long");
1017+
DBG_ERR("password is too long");
1018+
DBG_RETURN(0);
1019+
}
1020+
1021+
*crypted = emalloc(server_public_key_len);
1022+
RSA_public_encrypt(passwd_len + 1, (zend_uchar *) xor_str, *crypted, server_public_key, RSA_PKCS1_OAEP_PADDING);
1023+
DBG_RETURN(server_public_key_len);
1024+
}
1025+
DBG_RETURN(0);
1026+
#else
1027+
php_error_docref(NULL, E_WARNING, "PHP was built without openssl extension, can't send password encrypted");
1028+
DBG_RETURN(0);
1029+
#endif
1030+
}
1031+
/* }}} */
1032+
1033+
/* {{{ mysqlnd_native_auth_get_auth_data */
1034+
static void
1035+
mysqlnd_caching_sha2_handle_server_response(struct st_mysqlnd_authentication_plugin *self,
1036+
MYSQLND_CONN_DATA * conn,
1037+
const zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
1038+
const char * const passwd,
1039+
const size_t passwd_len)
1040+
{
1041+
DBG_ENTER("mysqlnd_caching_sha2_handle_server_response");
1042+
MYSQLND_PACKET_CACHED_SHA2_RESULT result_packet;
1043+
conn->payload_decoder_factory->m.init_cached_sha2_result_packet(&result_packet);
1044+
1045+
if (FAIL == PACKET_READ(conn, &result_packet)) {
1046+
DBG_VOID_RETURN;
1047+
}
1048+
1049+
switch (result_packet.response_code) {
1050+
case 3:
1051+
DBG_INF("fast path suceeded");
1052+
DBG_VOID_RETURN;
1053+
case 4:
1054+
if (conn->vio->data->ssl || conn->unix_socket.s) {
1055+
DBG_INF("fast path failed, doing full auth via SSL");
1056+
result_packet.password = (zend_uchar *)passwd;
1057+
result_packet.password_len = passwd_len + 1;
1058+
PACKET_WRITE(conn, &result_packet);
1059+
} else {
1060+
DBG_INF("fast path failed, doing full auth without SSL");
1061+
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);
1062+
PACKET_WRITE(conn, &result_packet);
1063+
efree(result_packet.password);
1064+
}
1065+
DBG_VOID_RETURN;
1066+
case 2:
1067+
// The server tried to send a key, which we didn't expect
1068+
// fall-through
1069+
default:
1070+
php_error_docref(NULL, E_WARNING, "Unexpected server respose while doing caching_sha2 auth: %i", result_packet.response_code);
1071+
}
1072+
1073+
DBG_VOID_RETURN;
1074+
}
1075+
/* }}} */
1076+
1077+
static struct st_mysqlnd_authentication_plugin mysqlnd_caching_sha2_auth_plugin =
1078+
{
1079+
{
1080+
MYSQLND_PLUGIN_API_VERSION,
1081+
"auth_plugin_caching_sha2_password",
1082+
MYSQLND_VERSION_ID,
1083+
PHP_MYSQLND_VERSION,
1084+
"PHP License 3.01",
1085+
"Johannes Schlüter <johannes.schlueter@php.net>",
1086+
{
1087+
NULL, /* no statistics , will be filled later if there are some */
1088+
NULL, /* no statistics */
1089+
},
1090+
{
1091+
NULL /* plugin shutdown */
1092+
}
1093+
},
1094+
{/* methods */
1095+
mysqlnd_caching_sha2_get_auth_data,
1096+
mysqlnd_caching_sha2_handle_server_response
1097+
}
1098+
};
1099+
1100+
8281101
/* {{{ mysqlnd_register_builtin_authentication_plugins */
8291102
void
8301103
mysqlnd_register_builtin_authentication_plugins(void)
8311104
{
8321105
mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_native_auth_plugin);
8331106
mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_pam_authentication_plugin);
1107+
mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_caching_sha2_auth_plugin);
8341108
#ifdef MYSQLND_HAVE_SSL
8351109
mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_sha256_authentication_plugin);
8361110
#endif

ext/mysqlnd/mysqlnd_auth.h

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,26 +31,9 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
3131
unsigned int server_charset_no,
3232
zend_bool use_full_blown_auth_packet,
3333
const char * const auth_protocol,
34-
const zend_uchar * const auth_plugin_data,
35-
const size_t auth_plugin_data_len,
36-
char ** switch_to_auth_protocol,
37-
size_t * switch_to_auth_protocol_len,
38-
zend_uchar ** switch_to_auth_protocol_data,
39-
size_t * switch_to_auth_protocol_data_len
40-
);
41-
42-
enum_func_status
43-
mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
44-
const char * const user,
45-
const char * const passwd,
46-
const size_t passwd_len,
47-
const char * const db,
48-
const size_t db_len,
49-
const MYSQLND_SESSION_OPTIONS * const session_options,
50-
zend_ulong mysql_flags,
51-
unsigned int server_charset_no,
52-
zend_bool use_full_blown_auth_packet,
53-
const char * const auth_protocol,
34+
struct st_mysqlnd_authentication_plugin * auth_plugin,
35+
const zend_uchar * const orig_auth_plugin_data,
36+
const size_t orig_auth_plugin_data_len,
5437
const zend_uchar * const auth_plugin_data,
5538
const size_t auth_plugin_data_len,
5639
char ** switch_to_auth_protocol,

ext/mysqlnd/mysqlnd_connection.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,10 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn,
666666

667667
{
668668
const MYSQLND_CSTRING scheme = { transport.s, transport.l };
669+
/* This will be overwritten below with a copy, but we can use it during authentication */
670+
conn->unix_socket.s = (char *)socket_or_pipe.s;
669671
if (FAIL == conn->m->connect_handshake(conn, &scheme, &username, &password, &database, mysql_flags)) {
672+
conn->unix_socket.s = NULL;
670673
goto err;
671674
}
672675
}

0 commit comments

Comments
 (0)