@@ -89,6 +89,7 @@ mysqlnd_run_authentication(
8989}
9090}
9191
92+
9293{
9394zend_uchar * switch_to_auth_protocol_data = NULL ;
9495size_t switch_to_auth_protocol_data_len = 0 ;
@@ -113,10 +114,11 @@ mysqlnd_run_authentication(
113114DBG_INF_FMT ("salt(%d)=[%.*s]" , plugin_data_len , plugin_data_len , plugin_data );
114115/* The data should be allocated with malloc() */
115116if (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
122124if (conn -> error_info -> error_no ) {
@@ -127,6 +129,7 @@ mysqlnd_run_authentication(
127129charset_no ,
128130first_call ,
129131requested_protocol ,
132+ auth_plugin , plugin_data , plugin_data_len ,
130133scrambled_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
@@ -248,6 +251,9 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
248251 unsigned int server_charset_no ,
249252 zend_bool use_full_blown_auth_packet ,
250253 const char * const auth_protocol ,
254+ struct st_mysqlnd_authentication_plugin * auth_plugin ,
255+ const zend_uchar * const orig_auth_plugin_data ,
256+ const size_t orig_auth_plugin_data_len ,
251257 const zend_uchar * const auth_plugin_data ,
252258 const size_t auth_plugin_data_len ,
253259 char * * switch_to_auth_protocol ,
@@ -318,6 +324,11 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
318324conn -> charset = mysqlnd_find_charset_nr (auth_packet -> charset_no );
319325}
320326
327+ if (auth_plugin && auth_plugin -> methods .handle_server_response ) {
328+ auth_plugin -> methods .handle_server_response (auth_plugin , conn ,
329+ orig_auth_plugin_data , orig_auth_plugin_data_len , passwd , passwd_len );
330+ }
331+
321332if (FAIL == PACKET_READ (auth_resp_packet ) || auth_resp_packet -> response_code >= 0xFE ) {
322333if (auth_resp_packet -> response_code == 0xFE ) {
323334/* old authentication with new server !*/
@@ -613,7 +624,8 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_native_auth_plugin =
613624}
614625},
615626{/* methods */
616- mysqlnd_native_auth_get_auth_data
627+ mysqlnd_native_auth_get_auth_data ,
628+ NULL
617629}
618630};
619631
@@ -662,7 +674,8 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_pam_authentication_plugin
662674}
663675},
664676{/* methods */
665- mysqlnd_pam_auth_get_auth_data
677+ mysqlnd_pam_auth_get_auth_data ,
678+ NULL
666679}
667680};
668681
@@ -846,17 +859,282 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_sha256_authentication_plu
846859}
847860},
848861{/* methods */
849- mysqlnd_sha256_auth_get_auth_data
862+ mysqlnd_sha256_auth_get_auth_data ,
863+ NULL
850864}
851865};
852866#endif
853867
868+ /*************************************** CACHING SHA2 Password *******************************/
869+
870+ #undef L64
871+
872+ #include "ext/hash/php_hash.h"
873+ #include "ext/hash/php_hash_sha.h"
874+
875+ #define SHA256_LENGTH 32
876+
877+ /* {{{ php_mysqlnd_scramble_sha2 */
878+ void php_mysqlnd_scramble_sha2 (zend_uchar * const buffer , const zend_uchar * const scramble , const zend_uchar * const password , const size_t password_len )
879+ {
880+ PHP_SHA256_CTX context ;
881+ zend_uchar sha1 [SHA256_LENGTH ];
882+ zend_uchar sha2 [SHA256_LENGTH ];
883+
884+ /* Phase 1: hash password */
885+ PHP_SHA256Init (& context );
886+ PHP_SHA256Update (& context , password , password_len );
887+ PHP_SHA256Final (sha1 , & context );
888+
889+ /* Phase 2: hash sha1 */
890+ PHP_SHA256Init (& context );
891+ PHP_SHA256Update (& context , (zend_uchar * )sha1 , SHA256_LENGTH );
892+ PHP_SHA256Final (sha2 , & context );
893+
894+ /* Phase 3: hash scramble + sha2 */
895+ PHP_SHA256Init (& context );
896+ PHP_SHA256Update (& context , (zend_uchar * )sha2 , SHA256_LENGTH );
897+ PHP_SHA256Update (& context , scramble , SCRAMBLE_LENGTH );
898+ PHP_SHA256Final (buffer , & context );
899+
900+ /* let's crypt buffer now */
901+ php_mysqlnd_crypt (buffer , (const zend_uchar * )sha1 , (const zend_uchar * )buffer , SHA256_LENGTH );
902+ }
903+ /* }}} */
904+
905+
906+ /* {{{ mysqlnd_native_auth_get_auth_data */
907+ static zend_uchar *
908+ mysqlnd_caching_sha2_get_auth_data (struct st_mysqlnd_authentication_plugin * self ,
909+ size_t * auth_data_len ,
910+ MYSQLND_CONN_DATA * conn , const char * const user , const char * const passwd ,
911+ const size_t passwd_len , zend_uchar * auth_plugin_data , size_t auth_plugin_data_len ,
912+ const MYSQLND_SESSION_OPTIONS * const session_options ,
913+ const MYSQLND_PFC_DATA * const pfc_data ,
914+ zend_ulong mysql_flags
915+ )
916+ {
917+ zend_uchar * ret = NULL ;
918+ DBG_ENTER ("mysqlnd_caching_sha2_get_auth_data" );
919+ DBG_INF_FMT ("salt(%d)=[%.*s]" , auth_plugin_data_len , auth_plugin_data_len , auth_plugin_data );
920+ * auth_data_len = 0 ;
921+
922+ DBG_INF ("First auth step: send hashed password" );
923+ /* copy scrambled pass*/
924+ if (passwd && passwd_len ) {
925+ ret = malloc (SHA256_LENGTH + 1 );
926+ * auth_data_len = SHA256_LENGTH ;
927+ php_mysqlnd_scramble_sha2 ((zend_uchar * )ret , auth_plugin_data , (zend_uchar * )passwd , passwd_len );
928+ ret [SHA256_LENGTH ] = '\0' ;
929+ DBG_INF_FMT ("hash(%d)=[%.*s]" , * auth_data_len , * auth_data_len , ret );
930+ }
931+
932+ DBG_RETURN (ret );
933+ }
934+ /* }}} */
935+
936+ #ifdef MYSQLND_HAVE_SSL
937+ static RSA *
938+ mysqlnd_caching_sha2_get_key (MYSQLND_CONN_DATA * conn )
939+ {
940+ RSA * ret = NULL ;
941+ const MYSQLND_PFC_DATA * const pfc_data = conn -> protocol_frame_codec -> data ;
942+ const char * fname = (pfc_data -> sha256_server_public_key && pfc_data -> sha256_server_public_key [0 ] != '\0' )?
943+ pfc_data -> sha256_server_public_key :
944+ MYSQLND_G (sha256_server_public_key );
945+ php_stream * stream ;
946+ DBG_ENTER ("mysqlnd_cached_sha2_get_key" );
947+ DBG_INF_FMT ("options_s256_pk=[%s] MYSQLND_G(sha256_server_public_key)=[%s]" ,
948+ pfc_data -> sha256_server_public_key ? pfc_data -> sha256_server_public_key :"n/a" ,
949+ MYSQLND_G (sha256_server_public_key )? MYSQLND_G (sha256_server_public_key ):"n/a" );
950+ if (!fname || fname [0 ] == '\0' ) {
951+ MYSQLND_PACKET_CACHED_SHA2_RESULT * req_packet = NULL ;
952+ MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE * pk_resp_packet = NULL ;
953+
954+ do {
955+ DBG_INF ("requesting the public key from the server" );
956+ req_packet = conn -> payload_decoder_factory -> m .get_cached_sha2_result_packet (conn -> payload_decoder_factory , FALSE);
957+ pk_resp_packet = conn -> payload_decoder_factory -> m .get_sha256_pk_request_response_packet (conn -> payload_decoder_factory , FALSE);
958+ req_packet -> request = 1 ;
959+
960+ if (! PACKET_WRITE (req_packet )) {
961+ DBG_ERR_FMT ("Error while sending public key request packet" );
962+ php_error (E_WARNING , "Error while sending public key request packet. PID=%d" , getpid ());
963+ SET_CONNECTION_STATE (& conn -> state , CONN_QUIT_SENT );
964+ break ;
965+ }
966+ if (FAIL == PACKET_READ (pk_resp_packet ) || NULL == pk_resp_packet -> public_key ) {
967+ DBG_ERR_FMT ("Error while receiving public key" );
968+ php_error (E_WARNING , "Error while receiving public key. PID=%d" , getpid ());
969+ SET_CONNECTION_STATE (& conn -> state , CONN_QUIT_SENT );
970+ break ;
971+ }
972+ DBG_INF_FMT ("Public key(%d):\n%s" , pk_resp_packet -> public_key_len , pk_resp_packet -> public_key );
973+ /* now extract the public key */
974+ {
975+ BIO * bio = BIO_new_mem_buf (pk_resp_packet -> public_key , pk_resp_packet -> public_key_len );
976+ ret = PEM_read_bio_RSA_PUBKEY (bio , NULL , NULL , NULL );
977+ BIO_free (bio );
978+ }
979+ } while (0 );
980+ PACKET_FREE (req_packet );
981+ PACKET_FREE (pk_resp_packet );
982+
983+ DBG_INF_FMT ("ret=%p" , ret );
984+ DBG_RETURN (ret );
985+
986+ SET_CLIENT_ERROR (conn -> error_info , CR_UNKNOWN_ERROR , UNKNOWN_SQLSTATE ,
987+ "caching_sha2_server_public_key is not set for the connection or as mysqlnd.sha256_server_public_key" );
988+ DBG_ERR ("server_public_key is not set" );
989+ DBG_RETURN (NULL );
990+ } else {
991+ zend_string * key_str ;
992+ DBG_INF_FMT ("Key in a file. [%s]" , fname );
993+ stream = php_stream_open_wrapper ((char * ) fname , "rb" , REPORT_ERRORS , NULL );
994+
995+ if (stream ) {
996+ if ((key_str = php_stream_copy_to_mem (stream , PHP_STREAM_COPY_ALL , 0 )) != NULL ) {
997+ BIO * bio = BIO_new_mem_buf (ZSTR_VAL (key_str ), ZSTR_LEN (key_str ));
998+ ret = PEM_read_bio_RSA_PUBKEY (bio , NULL , NULL , NULL );
999+ BIO_free (bio );
1000+ DBG_INF ("Successfully loaded" );
1001+ DBG_INF_FMT ("Public key:%*.s" , ZSTR_LEN (key_str ), ZSTR_VAL (key_str ));
1002+ zend_string_release (key_str );
1003+ }
1004+ php_stream_close (stream );
1005+ }
1006+ }
1007+ DBG_RETURN (ret );
1008+
1009+ }
1010+ #endif
1011+
1012+
1013+ /* {{{ mysqlnd_caching_sha2_get_key */
1014+ static size_t
1015+ mysqlnd_caching_sha2_get_and_use_key (MYSQLND_CONN_DATA * conn ,
1016+ const zend_uchar * auth_plugin_data , size_t auth_plugin_data_len ,
1017+ unsigned char * * crypted ,
1018+ const char * const passwd ,
1019+ const size_t passwd_len )
1020+ {
1021+ #ifdef MYSQLND_HAVE_SSL
1022+ static RSA * server_public_key ;
1023+ server_public_key = mysqlnd_caching_sha2_get_key (conn );
1024+
1025+ DBG_ENTER ("mysqlnd_caching_sha2_get_and_use_key(" );
1026+
1027+ if (server_public_key ) {
1028+ int server_public_key_len ;
1029+ char xor_str [passwd_len + 1 ];
1030+ memcpy (xor_str , passwd , passwd_len );
1031+ xor_str [passwd_len ] = '\0' ;
1032+ mysqlnd_xor_string (xor_str , passwd_len , (char * ) auth_plugin_data , auth_plugin_data_len );
1033+
1034+ server_public_key_len = RSA_size (server_public_key );
1035+ /*
1036+ Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
1037+ RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
1038+ http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
1039+ */
1040+ if ((size_t ) server_public_key_len - 41 <= passwd_len ) {
1041+ /* password message is to long */
1042+ SET_CLIENT_ERROR (conn -> error_info , CR_UNKNOWN_ERROR , UNKNOWN_SQLSTATE , "password is too long" );
1043+ DBG_ERR ("password is too long" );
1044+ DBG_RETURN (0 );
1045+ }
1046+
1047+ * crypted = emalloc (server_public_key_len );
1048+ RSA_public_encrypt (passwd_len + 1 , (zend_uchar * ) xor_str , * crypted , server_public_key , RSA_PKCS1_OAEP_PADDING );
1049+ DBG_RETURN (server_public_key_len );
1050+ }
1051+ DBG_RETURN (0 );
1052+ #else
1053+ php_error_docref (NULL , E_WARNING , "PHP was built without openssl extension, can't send password encrypted" );
1054+ DBG_RETURN (0 );
1055+ #endif
1056+ }
1057+ /* }}} */
1058+
1059+ /* {{{ mysqlnd_native_auth_get_auth_data */
1060+ static void
1061+ mysqlnd_caching_sha2_handle_server_response (struct st_mysqlnd_authentication_plugin * self ,
1062+ MYSQLND_CONN_DATA * conn ,
1063+ const zend_uchar * auth_plugin_data , size_t auth_plugin_data_len ,
1064+ const char * const passwd ,
1065+ const size_t passwd_len )
1066+ {
1067+ DBG_ENTER ("mysqlnd_caching_sha2_handle_server_response" );
1068+ MYSQLND_PACKET_CACHED_SHA2_RESULT * result_packet ;
1069+ result_packet = conn -> payload_decoder_factory -> m .get_cached_sha2_result_packet (conn -> payload_decoder_factory , FALSE);
1070+
1071+ if (FAIL == PACKET_READ (result_packet )) {
1072+ DBG_VOID_RETURN ;
1073+ }
1074+
1075+ switch (result_packet -> response_code ) {
1076+ case 3 :
1077+ DBG_INF ("fast path suceeded" );
1078+ PACKET_FREE (result_packet );
1079+ DBG_VOID_RETURN ;
1080+ case 4 :
1081+ if (conn -> vio -> data -> ssl || conn -> unix_socket .s ) {
1082+ DBG_INF ("fast path failed, doing full auth via SSL" );
1083+ result_packet -> password = (zend_uchar * )passwd ;
1084+ result_packet -> password_len = passwd_len + 1 ;
1085+ PACKET_WRITE (result_packet );
1086+ } else {
1087+ DBG_INF ("fast path failed, doing full auth without SSL" );
1088+ 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 );
1089+ PACKET_WRITE (result_packet );
1090+ efree (result_packet -> password );
1091+ }
1092+ PACKET_FREE (result_packet );
1093+ DBG_VOID_RETURN ;
1094+ case 2 :
1095+ // The server tried to send a key, which we didn't expect
1096+ // fall-through
1097+ default :
1098+ php_error_docref (NULL , E_WARNING , "Unexpected server respose while doing caching_sha2 auth: %i" , result_packet -> response_code );
1099+ }
1100+
1101+ PACKET_FREE (result_packet );
1102+
1103+ DBG_VOID_RETURN ;
1104+ }
1105+ /* }}} */
1106+
1107+ static struct st_mysqlnd_authentication_plugin mysqlnd_caching_sha2_auth_plugin =
1108+ {
1109+ {
1110+ MYSQLND_PLUGIN_API_VERSION ,
1111+ "auth_plugin_caching_sha2_password" ,
1112+ MYSQLND_VERSION_ID ,
1113+ PHP_MYSQLND_VERSION ,
1114+ "PHP License 3.01" ,
1115+ "Johannes Schlüter <johannes.schlueter@php.net>" ,
1116+ {
1117+ NULL , /* no statistics , will be filled later if there are some */
1118+ NULL , /* no statistics */
1119+ },
1120+ {
1121+ NULL /* plugin shutdown */
1122+ }
1123+ },
1124+ {/* methods */
1125+ mysqlnd_caching_sha2_get_auth_data ,
1126+ mysqlnd_caching_sha2_handle_server_response
1127+ }
1128+ };
1129+
1130+
8541131/* {{{ mysqlnd_register_builtin_authentication_plugins */
8551132void
8561133mysqlnd_register_builtin_authentication_plugins (void )
8571134{
8581135mysqlnd_plugin_register_ex ((struct st_mysqlnd_plugin_header * ) & mysqlnd_native_auth_plugin );
8591136mysqlnd_plugin_register_ex ((struct st_mysqlnd_plugin_header * ) & mysqlnd_pam_authentication_plugin );
1137+ mysqlnd_plugin_register_ex ((struct st_mysqlnd_plugin_header * ) & mysqlnd_caching_sha2_auth_plugin );
8601138#ifdef MYSQLND_HAVE_SSL
8611139mysqlnd_plugin_register_ex ((struct st_mysqlnd_plugin_header * ) & mysqlnd_sha256_authentication_plugin );
8621140#endif
0 commit comments