@@ -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,18 +833,274 @@ 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
838
+ }
839
+ };
840
+ #endif
841
+
842
+ /*************************************** CACHING SHA2 Password *******************************/
843
+ #ifdef MYSQLND_HAVE_SSL
844
+
845
+ #undef L64
846
+
847
+ #include "ext/hash/php_hash.h"
848
+ #include "ext/hash/php_hash_sha.h"
849
+
850
+ #define SHA256_LENGTH 32
851
+
852
+ /* {{{ php_mysqlnd_scramble_sha2 */
853
+ void php_mysqlnd_scramble_sha2 (zend_uchar * const buffer , const zend_uchar * const scramble , const zend_uchar * const password , const size_t password_len )
854
+ {
855
+ PHP_SHA256_CTX context ;
856
+ zend_uchar sha1 [SHA256_LENGTH ];
857
+ zend_uchar sha2 [SHA256_LENGTH ];
858
+
859
+ /* Phase 1: hash password */
860
+ PHP_SHA256Init (& context );
861
+ PHP_SHA256Update (& context , password , password_len );
862
+ PHP_SHA256Final (sha1 , & context );
863
+
864
+ /* Phase 2: hash sha1 */
865
+ PHP_SHA256Init (& context );
866
+ PHP_SHA256Update (& context , (zend_uchar * )sha1 , SHA256_LENGTH );
867
+ PHP_SHA256Final (sha2 , & context );
868
+
869
+ /* Phase 3: hash scramble + sha2 */
870
+ PHP_SHA256Init (& context );
871
+ PHP_SHA256Update (& context , (zend_uchar * )sha2 , SHA256_LENGTH );
872
+ PHP_SHA256Update (& context , scramble , SCRAMBLE_LENGTH );
873
+ PHP_SHA256Final (buffer , & context );
874
+
875
+ /* let's crypt buffer now */
876
+ php_mysqlnd_crypt (buffer , (const zend_uchar * )sha1 , (const zend_uchar * )buffer , SHA256_LENGTH );
877
+ }
878
+ /* }}} */
879
+
880
+
881
+ /* {{{ mysqlnd_native_auth_get_auth_data */
882
+ static zend_uchar *
883
+ mysqlnd_caching_sha2_get_auth_data (struct st_mysqlnd_authentication_plugin * self ,
884
+ size_t * auth_data_len ,
885
+ MYSQLND_CONN_DATA * conn , const char * const user , const char * const passwd ,
886
+ const size_t passwd_len , zend_uchar * auth_plugin_data , size_t auth_plugin_data_len ,
887
+ const MYSQLND_SESSION_OPTIONS * const session_options ,
888
+ const MYSQLND_PFC_DATA * const pfc_data ,
889
+ zend_ulong mysql_flags
890
+ )
891
+ {
892
+ zend_uchar * ret = NULL ;
893
+ DBG_ENTER ("mysqlnd_caching_sha2_get_auth_data" );
894
+ DBG_INF_FMT ("salt(%d)=[%.*s]" , auth_plugin_data_len , auth_plugin_data_len , auth_plugin_data );
895
+ * auth_data_len = 0 ;
896
+
897
+ DBG_INF ("First auth step: send hashed password" );
898
+ /* copy scrambled pass*/
899
+ if (passwd && passwd_len ) {
900
+ ret = malloc (SHA256_LENGTH + 1 );
901
+ * auth_data_len = SHA256_LENGTH ;
902
+ php_mysqlnd_scramble_sha2 ((zend_uchar * )ret , auth_plugin_data , (zend_uchar * )passwd , passwd_len );
903
+ ret [SHA256_LENGTH ] = '\0' ;
904
+ DBG_INF_FMT ("hash(%d)=[%.*s]" , * auth_data_len , * auth_data_len , ret );
905
+ }
906
+
907
+ DBG_RETURN (ret );
908
+ }
909
+ /* }}} */
910
+
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
+
985
+
986
+ /* {{{ mysqlnd_caching_sha2_get_key */
987
+ static size_t
988
+ mysqlnd_caching_sha2_get_and_use_key (MYSQLND_CONN_DATA * conn ,
989
+ const zend_uchar * auth_plugin_data , size_t auth_plugin_data_len ,
990
+ unsigned char * * crypted ,
991
+ const char * const passwd ,
992
+ const size_t passwd_len )
993
+ {
994
+ static RSA * server_public_key ;
995
+ server_public_key = mysqlnd_caching_sha2_get_key (conn );
996
+
997
+ DBG_ENTER ("mysqlnd_caching_sha2_get_and_use_key(" );
998
+
999
+ if (server_public_key ) {
1000
+ int server_public_key_len ;
1001
+ char xor_str [passwd_len + 1 ];
1002
+ memcpy (xor_str , passwd , passwd_len );
1003
+ xor_str [passwd_len ] = '\0' ;
1004
+ mysqlnd_xor_string (xor_str , passwd_len , (char * ) auth_plugin_data , auth_plugin_data_len );
1005
+
1006
+ server_public_key_len = RSA_size (server_public_key );
1007
+ /*
1008
+ Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
1009
+ RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
1010
+ http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
1011
+ */
1012
+ if ((size_t ) server_public_key_len - 41 <= passwd_len ) {
1013
+ /* password message is to long */
1014
+ SET_CLIENT_ERROR (conn -> error_info , CR_UNKNOWN_ERROR , UNKNOWN_SQLSTATE , "password is too long" );
1015
+ DBG_ERR ("password is too long" );
1016
+ DBG_RETURN (0 );
1017
+ }
1018
+
1019
+ * crypted = emalloc (server_public_key_len );
1020
+ RSA_public_encrypt (passwd_len + 1 , (zend_uchar * ) xor_str , * crypted , server_public_key , RSA_PKCS1_OAEP_PADDING );
1021
+ DBG_RETURN (server_public_key_len );
1022
+ }
1023
+ DBG_RETURN (0 );
1024
+ }
1025
+ /* }}} */
1026
+
1027
+ /* {{{ mysqlnd_native_auth_get_auth_data */
1028
+ static void
1029
+ mysqlnd_caching_sha2_handle_server_response (struct st_mysqlnd_authentication_plugin * self ,
1030
+ MYSQLND_CONN_DATA * conn ,
1031
+ const zend_uchar * auth_plugin_data , size_t auth_plugin_data_len ,
1032
+ const char * const passwd ,
1033
+ const size_t passwd_len )
1034
+ {
1035
+ DBG_ENTER ("mysqlnd_caching_sha2_handle_server_response" );
1036
+ MYSQLND_PACKET_CACHED_SHA2_RESULT result_packet ;
1037
+ conn -> payload_decoder_factory -> m .init_cached_sha2_result_packet (& result_packet );
1038
+
1039
+ if (FAIL == PACKET_READ (conn , & result_packet )) {
1040
+ DBG_VOID_RETURN ;
1041
+ }
1042
+
1043
+ switch (result_packet .response_code ) {
1044
+ case 3 :
1045
+ DBG_INF ("fast path succeeded" );
1046
+ DBG_VOID_RETURN ;
1047
+ case 4 :
1048
+ if (conn -> vio -> data -> ssl || conn -> unix_socket .s ) {
1049
+ DBG_INF ("fast path failed, doing full auth via SSL" );
1050
+ result_packet .password = (zend_uchar * )passwd ;
1051
+ result_packet .password_len = passwd_len + 1 ;
1052
+ PACKET_WRITE (conn , & result_packet );
1053
+ } else {
1054
+ DBG_INF ("fast path failed, doing full auth without SSL" );
1055
+ 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 );
1056
+ PACKET_WRITE (conn , & result_packet );
1057
+ efree (result_packet .password );
1058
+ }
1059
+ DBG_VOID_RETURN ;
1060
+ case 2 :
1061
+ // The server tried to send a key, which we didn't expect
1062
+ // fall-through
1063
+ default :
1064
+ php_error_docref (NULL , E_WARNING , "Unexpected server respose while doing caching_sha2 auth: %i" , result_packet .response_code );
1065
+ }
1066
+
1067
+ DBG_VOID_RETURN ;
1068
+ }
1069
+ /* }}} */
1070
+
1071
+ static struct st_mysqlnd_authentication_plugin mysqlnd_caching_sha2_auth_plugin =
1072
+ {
1073
+ {
1074
+ MYSQLND_PLUGIN_API_VERSION ,
1075
+ "auth_plugin_caching_sha2_password" ,
1076
+ MYSQLND_VERSION_ID ,
1077
+ PHP_MYSQLND_VERSION ,
1078
+ "PHP License 3.01" ,
1079
+ "Johannes Schlüter <[email protected] >" ,
1080
+ {
1081
+ NULL , /* no statistics , will be filled later if there are some */
1082
+ NULL , /* no statistics */
1083
+ },
1084
+ {
1085
+ NULL /* plugin shutdown */
1086
+ }
1087
+ },
1088
+ {/* methods */
1089
+ mysqlnd_caching_sha2_get_auth_data ,
1090
+ mysqlnd_caching_sha2_handle_server_response
824
1091
}
825
1092
};
826
1093
#endif
827
1094
1095
+
828
1096
/* {{{ mysqlnd_register_builtin_authentication_plugins */
829
1097
void
830
1098
mysqlnd_register_builtin_authentication_plugins (void )
831
1099
{
832
1100
mysqlnd_plugin_register_ex ((struct st_mysqlnd_plugin_header * ) & mysqlnd_native_auth_plugin );
833
1101
mysqlnd_plugin_register_ex ((struct st_mysqlnd_plugin_header * ) & mysqlnd_pam_authentication_plugin );
834
1102
#ifdef MYSQLND_HAVE_SSL
1103
+ mysqlnd_plugin_register_ex ((struct st_mysqlnd_plugin_header * ) & mysqlnd_caching_sha2_auth_plugin );
835
1104
mysqlnd_plugin_register_ex ((struct st_mysqlnd_plugin_header * ) & mysqlnd_sha256_authentication_plugin );
836
1105
#endif
837
1106
}
0 commit comments