@@ -692,20 +692,146 @@ mysqlnd_xor_string(char * dst, const size_t dst_len, const char * xor_str, const
692
692
}
693
693
}
694
694
695
+ #ifndef PHP_WIN32
695
696
696
697
#include <openssl/rsa.h>
697
698
#include <openssl/pem.h>
698
699
#include <openssl/err.h>
699
700
701
+ typedef RSA * mysqlnd_rsa_t ;
702
+
703
+ /* {{{ mysqlnd_sha256_get_rsa_from_pem */
704
+ static mysqlnd_rsa_t
705
+ mysqlnd_sha256_get_rsa_from_pem (const char * buf , size_t len )
706
+ {
707
+ BIO * bio = BIO_new_mem_buf (buf , len );
708
+ RSA * ret = PEM_read_bio_RSA_PUBKEY (bio , NULL , NULL , NULL );
709
+ BIO_free (bio );
710
+ return ret ;
711
+ }
712
+ /* }}} */
713
+
714
+ /* {{{ mysqlnd_sha256_public_encrypt */
715
+ static zend_uchar *
716
+ mysqlnd_sha256_public_encrypt (MYSQLND_CONN_DATA * conn , mysqlnd_rsa_t server_public_key , size_t passwd_len , size_t * auth_data_len , char * xor_str )
717
+ {
718
+ zend_uchar * ret = NULL ;
719
+ size_t server_public_key_len = (size_t ) RSA_size (server_public_key );
720
+
721
+ DBG_ENTER ("mysqlnd_sha256_public_encrypt" );
722
+ /*
723
+ Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
724
+ RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
725
+ http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
726
+ */
727
+ if (server_public_key_len <= passwd_len + 41 ) {
728
+ /* password message is to long */
729
+ SET_CLIENT_ERROR (conn -> error_info , CR_UNKNOWN_ERROR , UNKNOWN_SQLSTATE , "password is too long" );
730
+ DBG_ERR ("password is too long" );
731
+ DBG_RETURN (NULL );
732
+ }
733
+
734
+ * auth_data_len = server_public_key_len ;
735
+ ret = malloc (* auth_data_len );
736
+ RSA_public_encrypt (passwd_len + 1 , (zend_uchar * ) xor_str , ret , server_public_key , RSA_PKCS1_OAEP_PADDING );
737
+ RSA_free (server_public_key );
738
+ DBG_RETURN (ret );
739
+ }
740
+ /* }}} */
741
+
742
+ #else
743
+
744
+ #include <wincrypt.h>
745
+ #include <bcrypt.h>
746
+
747
+ typedef HANDLE mysqlnd_rsa_t ;
748
+
749
+ /* {{{ mysqlnd_sha256_get_rsa_from_pem */
750
+ static mysqlnd_rsa_t
751
+ mysqlnd_sha256_get_rsa_from_pem (const char * buf , size_t len )
752
+ {
753
+ BCRYPT_KEY_HANDLE ret = 0 ;
754
+ LPCSTR der_buf = NULL ;
755
+ DWORD der_len ;
756
+ CERT_PUBLIC_KEY_INFO * key_info = NULL ;
757
+ DWORD key_info_len ;
758
+ ALLOCA_FLAG (use_heap );
759
+
760
+ if (!CryptStringToBinaryA (buf , len , CRYPT_STRING_BASE64HEADER , NULL , & der_len , NULL , NULL )) {
761
+ goto finish ;
762
+ }
763
+ der_buf = do_alloca (der_len , use_heap );
764
+ if (!CryptStringToBinaryA (buf , len , CRYPT_STRING_BASE64HEADER , der_buf , & der_len , NULL , NULL )) {
765
+ goto finish ;
766
+ }
767
+ if (!CryptDecodeObjectEx (X509_ASN_ENCODING , X509_PUBLIC_KEY_INFO , der_buf , der_len , CRYPT_ENCODE_ALLOC_FLAG , NULL , & key_info , & key_info_len )) {
768
+ goto finish ;
769
+ }
770
+ if (!CryptImportPublicKeyInfoEx2 (X509_ASN_ENCODING , key_info , CRYPT_OID_INFO_PUBKEY_ENCRYPT_KEY_FLAG , NULL , & ret )) {
771
+ goto finish ;
772
+ }
773
+
774
+ finish :
775
+ if (key_info ) {
776
+ LocalFree (key_info );
777
+ }
778
+ if (der_buf ) {
779
+ free_alloca (der_buf , use_heap );
780
+ }
781
+ return (mysqlnd_rsa_t ) ret ;
782
+ }
783
+ /* }}} */
784
+
785
+ /* {{{ mysqlnd_sha256_public_encrypt */
786
+ static zend_uchar *
787
+ mysqlnd_sha256_public_encrypt (MYSQLND_CONN_DATA * conn , mysqlnd_rsa_t server_public_key , size_t passwd_len , size_t * auth_data_len , char * xor_str )
788
+ {
789
+ zend_uchar * ret = NULL ;
790
+ DWORD server_public_key_len = passwd_len ;
791
+ BCRYPT_OAEP_PADDING_INFO padding_info ;
792
+
793
+ DBG_ENTER ("mysqlnd_sha256_public_encrypt" );
794
+
795
+ ZeroMemory (& padding_info , sizeof padding_info );
796
+ padding_info .pszAlgId = BCRYPT_SHA1_ALGORITHM ;
797
+ if (BCryptEncrypt ((BCRYPT_KEY_HANDLE ) server_public_key , xor_str , passwd_len + 1 , & padding_info ,
798
+ NULL , 0 , NULL , 0 , & server_public_key_len , BCRYPT_PAD_OAEP )) {
799
+ DBG_RETURN (0 );
800
+ }
801
+
802
+ /*
803
+ Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
804
+ RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
805
+ http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
806
+ */
807
+ if ((size_t ) server_public_key_len <= passwd_len + 41 ) {
808
+ /* password message is to long */
809
+ SET_CLIENT_ERROR (conn -> error_info , CR_UNKNOWN_ERROR , UNKNOWN_SQLSTATE , "password is too long" );
810
+ DBG_ERR ("password is too long" );
811
+ DBG_RETURN (0 );
812
+ }
813
+
814
+ * auth_data_len = server_public_key_len ;
815
+ ret = malloc (* auth_data_len );
816
+ if (BCryptEncrypt ((BCRYPT_KEY_HANDLE ) server_public_key , xor_str , passwd_len + 1 , & padding_info ,
817
+ NULL , 0 , ret , server_public_key_len , & server_public_key_len , BCRYPT_PAD_OAEP )) {
818
+ DBG_RETURN (0 );
819
+ }
820
+ BCryptDestroyKey ((BCRYPT_KEY_HANDLE ) server_public_key );
821
+ DBG_RETURN (ret );
822
+ }
823
+ /* }}} */
824
+
825
+ #endif
700
826
701
827
/* {{{ mysqlnd_sha256_get_rsa_key */
702
- static RSA *
828
+ static mysqlnd_rsa_t
703
829
mysqlnd_sha256_get_rsa_key (MYSQLND_CONN_DATA * conn ,
704
830
const MYSQLND_SESSION_OPTIONS * const session_options ,
705
831
const MYSQLND_PFC_DATA * const pfc_data
706
832
)
707
833
{
708
- RSA * ret = NULL ;
834
+ mysqlnd_rsa_t ret = NULL ;
709
835
const char * fname = (pfc_data -> sha256_server_public_key && pfc_data -> sha256_server_public_key [0 ] != '\0' )?
710
836
pfc_data -> sha256_server_public_key :
711
837
MYSQLND_G (sha256_server_public_key );
@@ -737,11 +863,7 @@ mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn,
737
863
}
738
864
DBG_INF_FMT ("Public key(%d):\n%s" , pk_resp_packet .public_key_len , pk_resp_packet .public_key );
739
865
/* now extract the public key */
740
- {
741
- BIO * bio = BIO_new_mem_buf (pk_resp_packet .public_key , pk_resp_packet .public_key_len );
742
- ret = PEM_read_bio_RSA_PUBKEY (bio , NULL , NULL , NULL );
743
- BIO_free (bio );
744
- }
866
+ ret = mysqlnd_sha256_get_rsa_from_pem ((const char * ) pk_resp_packet .public_key , pk_resp_packet .public_key_len );
745
867
} while (0 );
746
868
PACKET_FREE (& pk_req_packet );
747
869
PACKET_FREE (& pk_resp_packet );
@@ -760,9 +882,7 @@ mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn,
760
882
761
883
if (stream ) {
762
884
if ((key_str = php_stream_copy_to_mem (stream , PHP_STREAM_COPY_ALL , 0 )) != NULL ) {
763
- BIO * bio = BIO_new_mem_buf (ZSTR_VAL (key_str ), ZSTR_LEN (key_str ));
764
- ret = PEM_read_bio_RSA_PUBKEY (bio , NULL , NULL , NULL );
765
- BIO_free (bio );
885
+ ret = mysqlnd_sha256_get_rsa_from_pem (ZSTR_VAL (key_str ), ZSTR_LEN (key_str ));
766
886
DBG_INF ("Successfully loaded" );
767
887
DBG_INF_FMT ("Public key:%*.s" , ZSTR_LEN (key_str ), ZSTR_VAL (key_str ));
768
888
zend_string_release_ex (key_str , 0 );
@@ -786,7 +906,7 @@ mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
786
906
const zend_ulong mysql_flags
787
907
)
788
908
{
789
- RSA * server_public_key ;
909
+ mysqlnd_rsa_t server_public_key ;
790
910
zend_uchar * ret = NULL ;
791
911
DBG_ENTER ("mysqlnd_sha256_auth_get_auth_data" );
792
912
DBG_INF_FMT ("salt(%d)=[%.*s]" , auth_plugin_data_len , auth_plugin_data_len , auth_plugin_data );
@@ -803,31 +923,12 @@ mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
803
923
server_public_key = mysqlnd_sha256_get_rsa_key (conn , session_options , pfc_data );
804
924
805
925
if (server_public_key ) {
806
- int server_public_key_len ;
807
926
ALLOCA_FLAG (use_heap );
808
927
char * xor_str = do_alloca (passwd_len + 1 , use_heap );
809
928
memcpy (xor_str , passwd , passwd_len );
810
929
xor_str [passwd_len ] = '\0' ;
811
930
mysqlnd_xor_string (xor_str , passwd_len , (char * ) auth_plugin_data , auth_plugin_data_len );
812
-
813
- server_public_key_len = RSA_size (server_public_key );
814
- /*
815
- Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
816
- RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
817
- http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
818
- */
819
- if ((size_t ) server_public_key_len - 41 <= passwd_len ) {
820
- /* password message is to long */
821
- free_alloca (xor_str , use_heap );
822
- SET_CLIENT_ERROR (conn -> error_info , CR_UNKNOWN_ERROR , UNKNOWN_SQLSTATE , "password is too long" );
823
- DBG_ERR ("password is too long" );
824
- DBG_RETURN (NULL );
825
- }
826
-
827
- * auth_data_len = server_public_key_len ;
828
- ret = malloc (* auth_data_len );
829
- RSA_public_encrypt (passwd_len + 1 , (zend_uchar * ) xor_str , ret , server_public_key , RSA_PKCS1_OAEP_PADDING );
830
- RSA_free (server_public_key );
931
+ ret = mysqlnd_sha256_public_encrypt (conn , server_public_key , passwd_len , auth_data_len , xor_str );
831
932
free_alloca (xor_str , use_heap );
832
933
}
833
934
}
@@ -899,6 +1000,73 @@ void php_mysqlnd_scramble_sha2(zend_uchar * const buffer, const zend_uchar * con
899
1000
}
900
1001
/* }}} */
901
1002
1003
+ #ifndef PHP_WIN32
1004
+
1005
+ /* {{{ mysqlnd_caching_sha2_public_encrypt */
1006
+ static size_t
1007
+ mysqlnd_caching_sha2_public_encrypt (MYSQLND_CONN_DATA * conn , mysqlnd_rsa_t server_public_key , size_t passwd_len , unsigned char * * crypted , char * xor_str )
1008
+ {
1009
+ size_t server_public_key_len = (size_t ) RSA_size (server_public_key );
1010
+
1011
+ DBG_ENTER ("mysqlnd_caching_sha2_public_encrypt" );
1012
+ /*
1013
+ Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
1014
+ RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
1015
+ http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
1016
+ */
1017
+ if (server_public_key_len <= passwd_len + 41 ) {
1018
+ /* password message is to long */
1019
+ SET_CLIENT_ERROR (conn -> error_info , CR_UNKNOWN_ERROR , UNKNOWN_SQLSTATE , "password is too long" );
1020
+ DBG_ERR ("password is too long" );
1021
+ DBG_RETURN (0 );
1022
+ }
1023
+
1024
+ * crypted = emalloc (server_public_key_len );
1025
+ RSA_public_encrypt (passwd_len + 1 , (zend_uchar * ) xor_str , * crypted , server_public_key , RSA_PKCS1_OAEP_PADDING );
1026
+ DBG_RETURN (server_public_key_len );
1027
+ }
1028
+ /* }}} */
1029
+
1030
+ #else
1031
+
1032
+ /* {{{ mysqlnd_caching_sha2_public_encrypt */
1033
+ static size_t
1034
+ mysqlnd_caching_sha2_public_encrypt (MYSQLND_CONN_DATA * conn , mysqlnd_rsa_t server_public_key , size_t passwd_len , unsigned char * * crypted , char * xor_str )
1035
+ {
1036
+ DWORD server_public_key_len = passwd_len ;
1037
+ BCRYPT_OAEP_PADDING_INFO padding_info ;
1038
+
1039
+ DBG_ENTER ("mysqlnd_caching_sha2_public_encrypt" );
1040
+
1041
+ ZeroMemory (& padding_info , sizeof padding_info );
1042
+ padding_info .pszAlgId = BCRYPT_SHA1_ALGORITHM ;
1043
+ if (BCryptEncrypt ((BCRYPT_KEY_HANDLE ) server_public_key , xor_str , passwd_len + 1 , & padding_info ,
1044
+ NULL , 0 , NULL , 0 , & server_public_key_len , BCRYPT_PAD_OAEP )) {
1045
+ DBG_RETURN (0 );
1046
+ }
1047
+
1048
+ /*
1049
+ Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
1050
+ RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
1051
+ http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
1052
+ */
1053
+ if ((size_t ) server_public_key_len <= passwd_len + 41 ) {
1054
+ /* password message is to long */
1055
+ SET_CLIENT_ERROR (conn -> error_info , CR_UNKNOWN_ERROR , UNKNOWN_SQLSTATE , "password is too long" );
1056
+ DBG_ERR ("password is too long" );
1057
+ DBG_RETURN (0 );
1058
+ }
1059
+
1060
+ * crypted = emalloc (server_public_key_len );
1061
+ if (BCryptEncrypt ((BCRYPT_KEY_HANDLE ) server_public_key , xor_str , passwd_len + 1 , & padding_info ,
1062
+ NULL , 0 , * crypted , server_public_key_len , & server_public_key_len , BCRYPT_PAD_OAEP )) {
1063
+ DBG_RETURN (0 );
1064
+ }
1065
+ DBG_RETURN (server_public_key_len );
1066
+ }
1067
+ /* }}} */
1068
+
1069
+ #endif
902
1070
903
1071
/* {{{ mysqlnd_native_auth_get_auth_data */
904
1072
static zend_uchar *
@@ -936,10 +1104,10 @@ mysqlnd_caching_sha2_get_auth_data(struct st_mysqlnd_authentication_plugin * sel
936
1104
}
937
1105
/* }}} */
938
1106
939
- static RSA *
1107
+ static mysqlnd_rsa_t
940
1108
mysqlnd_caching_sha2_get_key (MYSQLND_CONN_DATA * conn )
941
1109
{
942
- RSA * ret = NULL ;
1110
+ mysqlnd_rsa_t ret = NULL ;
943
1111
const MYSQLND_PFC_DATA * const pfc_data = conn -> protocol_frame_codec -> data ;
944
1112
const char * fname = (pfc_data -> sha256_server_public_key && pfc_data -> sha256_server_public_key [0 ] != '\0' )?
945
1113
pfc_data -> sha256_server_public_key :
@@ -973,11 +1141,7 @@ mysqlnd_caching_sha2_get_key(MYSQLND_CONN_DATA *conn)
973
1141
}
974
1142
DBG_INF_FMT ("Public key(%d):\n%s" , pk_resp_packet .public_key_len , pk_resp_packet .public_key );
975
1143
/* now extract the public key */
976
- {
977
- BIO * bio = BIO_new_mem_buf (pk_resp_packet .public_key , pk_resp_packet .public_key_len );
978
- ret = PEM_read_bio_RSA_PUBKEY (bio , NULL , NULL , NULL );
979
- BIO_free (bio );
980
- }
1144
+ ret = mysqlnd_sha256_get_rsa_from_pem ((const char * ) pk_resp_packet .public_key , pk_resp_packet .public_key_len );
981
1145
} while (0 );
982
1146
PACKET_FREE (& req_packet );
983
1147
PACKET_FREE (& pk_resp_packet );
@@ -996,9 +1160,7 @@ mysqlnd_caching_sha2_get_key(MYSQLND_CONN_DATA *conn)
996
1160
997
1161
if (stream ) {
998
1162
if ((key_str = php_stream_copy_to_mem (stream , PHP_STREAM_COPY_ALL , 0 )) != NULL ) {
999
- BIO * bio = BIO_new_mem_buf (ZSTR_VAL (key_str ), ZSTR_LEN (key_str ));
1000
- ret = PEM_read_bio_RSA_PUBKEY (bio , NULL , NULL , NULL );
1001
- BIO_free (bio );
1163
+ ret = mysqlnd_sha256_get_rsa_from_pem (ZSTR_VAL (key_str ), ZSTR_LEN (key_str ));
1002
1164
DBG_INF ("Successfully loaded" );
1003
1165
DBG_INF_FMT ("Public key:%*.s" , ZSTR_LEN (key_str ), ZSTR_VAL (key_str ));
1004
1166
zend_string_release (key_str );
@@ -1011,16 +1173,15 @@ mysqlnd_caching_sha2_get_key(MYSQLND_CONN_DATA *conn)
1011
1173
}
1012
1174
1013
1175
1014
- /* {{{ mysqlnd_caching_sha2_get_key */
1176
+ /* {{{ mysqlnd_caching_sha2_get_and_use_key */
1015
1177
static size_t
1016
1178
mysqlnd_caching_sha2_get_and_use_key (MYSQLND_CONN_DATA * conn ,
1017
1179
const zend_uchar * auth_plugin_data , const size_t auth_plugin_data_len ,
1018
1180
unsigned char * * crypted ,
1019
1181
const char * const passwd ,
1020
1182
const size_t passwd_len )
1021
1183
{
1022
- static RSA * server_public_key ;
1023
- server_public_key = mysqlnd_caching_sha2_get_key (conn );
1184
+ mysqlnd_rsa_t server_public_key = mysqlnd_caching_sha2_get_key (conn );
1024
1185
1025
1186
DBG_ENTER ("mysqlnd_caching_sha2_get_and_use_key(" );
1026
1187
@@ -1031,23 +1192,7 @@ mysqlnd_caching_sha2_get_and_use_key(MYSQLND_CONN_DATA *conn,
1031
1192
memcpy (xor_str , passwd , passwd_len );
1032
1193
xor_str [passwd_len ] = '\0' ;
1033
1194
mysqlnd_xor_string (xor_str , passwd_len , (char * ) auth_plugin_data , SCRAMBLE_LENGTH );
1034
-
1035
- server_public_key_len = RSA_size (server_public_key );
1036
- /*
1037
- Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
1038
- RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
1039
- http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
1040
- */
1041
- if ((size_t ) server_public_key_len - 41 <= passwd_len ) {
1042
- /* password message is to long */
1043
- free_alloca (xor_str , use_heap );
1044
- SET_CLIENT_ERROR (conn -> error_info , CR_UNKNOWN_ERROR , UNKNOWN_SQLSTATE , "password is too long" );
1045
- DBG_ERR ("password is too long" );
1046
- DBG_RETURN (0 );
1047
- }
1048
-
1049
- * crypted = emalloc (server_public_key_len );
1050
- RSA_public_encrypt (passwd_len + 1 , (zend_uchar * ) xor_str , * crypted , server_public_key , RSA_PKCS1_OAEP_PADDING );
1195
+ server_public_key_len = mysqlnd_caching_sha2_public_encrypt (conn , server_public_key , passwd_len , crypted , xor_str );
1051
1196
free_alloca (xor_str , use_heap );
1052
1197
DBG_RETURN (server_public_key_len );
1053
1198
}
0 commit comments