@@ -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
@@ -248,6 +251,9 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
248
251
unsigned int server_charset_no ,
249
252
zend_bool use_full_blown_auth_packet ,
250
253
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 ,
251
257
const zend_uchar * const auth_plugin_data ,
252
258
const size_t auth_plugin_data_len ,
253
259
char * * switch_to_auth_protocol ,
@@ -318,6 +324,11 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
318
324
conn -> charset = mysqlnd_find_charset_nr (auth_packet -> charset_no );
319
325
}
320
326
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
+
321
332
if (FAIL == PACKET_READ (auth_resp_packet ) || auth_resp_packet -> response_code >= 0xFE ) {
322
333
if (auth_resp_packet -> response_code == 0xFE ) {
323
334
/* old authentication with new server !*/
@@ -613,7 +624,8 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_native_auth_plugin =
613
624
}
614
625
},
615
626
{/* methods */
616
- mysqlnd_native_auth_get_auth_data
627
+ mysqlnd_native_auth_get_auth_data ,
628
+ NULL
617
629
}
618
630
};
619
631
@@ -662,7 +674,8 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_pam_authentication_plugin
662
674
}
663
675
},
664
676
{/* methods */
665
- mysqlnd_pam_auth_get_auth_data
677
+ mysqlnd_pam_auth_get_auth_data ,
678
+ NULL
666
679
}
667
680
};
668
681
@@ -846,17 +859,282 @@ static struct st_mysqlnd_authentication_plugin mysqlnd_sha256_authentication_plu
846
859
}
847
860
},
848
861
{/* methods */
849
- mysqlnd_sha256_auth_get_auth_data
862
+ mysqlnd_sha256_auth_get_auth_data ,
863
+ NULL
850
864
}
851
865
};
852
866
#endif
853
867
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
+
854
1131
/* {{{ mysqlnd_register_builtin_authentication_plugins */
855
1132
void
856
1133
mysqlnd_register_builtin_authentication_plugins (void )
857
1134
{
858
1135
mysqlnd_plugin_register_ex ((struct st_mysqlnd_plugin_header * ) & mysqlnd_native_auth_plugin );
859
1136
mysqlnd_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 );
860
1138
#ifdef MYSQLND_HAVE_SSL
861
1139
mysqlnd_plugin_register_ex ((struct st_mysqlnd_plugin_header * ) & mysqlnd_sha256_authentication_plugin );
862
1140
#endif
0 commit comments