@@ -89,6 +89,7 @@ mysqlnd_run_authentication(
89
89
}
90
90
}
91
91
92
+
92
93
{
93
94
zend_uchar * switch_to_auth_protocol_data = NULL ;
94
95
size_t switch_to_auth_protocol_data_len = 0 ;
@@ -113,10 +114,11 @@ mysqlnd_run_authentication(
113
114
DBG_INF_FMT ("salt(%d)=[%.*s]" , plugin_data_len , plugin_data_len , plugin_data );
114
115
/* The data should be allocated with malloc() */
115
116
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 );
120
122
}
121
123
122
124
if (conn -> error_info -> error_no ) {
@@ -127,6 +129,7 @@ mysqlnd_run_authentication(
127
129
charset_no ,
128
130
first_call ,
129
131
requested_protocol ,
132
+ auth_plugin , plugin_data , plugin_data_len ,
130
133
scrambled_data , scrambled_data_len ,
131
134
& switch_to_auth_protocol , & switch_to_auth_protocol_len ,
132
135
& switch_to_auth_protocol_data , & switch_to_auth_protocol_data_len
@@ -244,6 +247,9 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
244
247
unsigned int server_charset_no ,
245
248
zend_bool use_full_blown_auth_packet ,
246
249
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 ,
247
253
const zend_uchar * const auth_plugin_data ,
248
254
const size_t auth_plugin_data_len ,
249
255
char * * switch_to_auth_protocol ,
@@ -313,6 +319,11 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
313
319
PACKET_FREE (& auth_packet );
314
320
}
315
321
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
+
316
327
if (FAIL == PACKET_READ (conn , & auth_resp_packet ) || auth_resp_packet .response_code >= 0xFE ) {
317
328
if (auth_resp_packet .response_code == 0xFE ) {
318
329
/* old authentication with new server !*/
@@ -596,7 +607,8 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_native_auth_plugin =
596
607
}
597
608
},
598
609
{/* methods */
599
- mysqlnd_native_auth_get_auth_data
610
+ mysqlnd_native_auth_get_auth_data ,
611
+ NULL
600
612
}
601
613
};
602
614
@@ -645,7 +657,8 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_pam_authentication_plugin
645
657
}
646
658
},
647
659
{/* methods */
648
- mysqlnd_pam_auth_get_auth_data
660
+ mysqlnd_pam_auth_get_auth_data ,
661
+ NULL
649
662
}
650
663
};
651
664
@@ -820,17 +833,278 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_sha256_authentication_plu
820
833
}
821
834
},
822
835
{/* methods */
823
- mysqlnd_sha256_auth_get_auth_data
836
+ mysqlnd_sha256_auth_get_auth_data ,
837
+ NULL
824
838
}
825
839
};
826
840
#endif
827
841
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
+
828
1101
/* {{{ mysqlnd_register_builtin_authentication_plugins */
829
1102
void
830
1103
mysqlnd_register_builtin_authentication_plugins (void )
831
1104
{
832
1105
mysqlnd_plugin_register_ex ((struct st_mysqlnd_plugin_header * ) & mysqlnd_native_auth_plugin );
833
1106
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 );
834
1108
#ifdef MYSQLND_HAVE_SSL
835
1109
mysqlnd_plugin_register_ex ((struct st_mysqlnd_plugin_header * ) & mysqlnd_sha256_authentication_plugin );
836
1110
#endif
0 commit comments