Skip to content

Commit a037702

Browse files
committed
Native Windows support for mysqlnd sha256 authentification
We implement that on top of Cryptography API: Next Generation (CNG).
1 parent a7400d5 commit a037702

File tree

3 files changed

+136
-0
lines changed

3 files changed

+136
-0
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ PHP NEWS
4040
- MySQLi:
4141
. Fixed bug #64032 (mysqli reports different client_version). (cmb)
4242

43+
- MySQLnd:
44+
. Implemented FR #79275 (Support auth_plugin_caching_sha2_password on
45+
Windows). (cmb)
46+
4347
- Opcache:
4448
. Fixed bug #79252 (preloading causes php-fpm to segfault during exit).
4549
(Nikita)

ext/mysqlnd/config.w32

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ if (PHP_MYSQLND != "no") {
3636
{
3737
AC_DEFINE("MYSQLND_COMPRESSION_ENABLED", 1, "Compression support");
3838
AC_DEFINE("MYSQLND_SSL_SUPPORTED", 1, "SSL support");
39+
if (CHECK_LIB("crypt32.lib", "mysqlnd")) {
40+
AC_DEFINE("MYSQLND_HAVE_SSL", 1, "Extended SSL support");
41+
}
3942
}
4043
PHP_INSTALL_HEADERS("", "ext/mysqlnd");
4144
}

ext/mysqlnd/mysqlnd_auth.c

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,7 @@ mysqlnd_xor_string(char * dst, const size_t dst_len, const char * xor_str, const
694694
}
695695
}
696696

697+
#ifndef PHP_WIN32
697698

698699
#include <openssl/rsa.h>
699700
#include <openssl/pem.h>
@@ -740,6 +741,91 @@ mysqlnd_sha256_public_encrypt(MYSQLND_CONN_DATA * conn, mysqlnd_rsa_t server_pub
740741
}
741742
/* }}} */
742743

744+
#else
745+
746+
#include <wincrypt.h>
747+
#include <bcrypt.h>
748+
749+
typedef HANDLE mysqlnd_rsa_t;
750+
751+
/* {{{ mysqlnd_sha256_get_rsa_from_pem */
752+
static mysqlnd_rsa_t
753+
mysqlnd_sha256_get_rsa_from_pem(const char *buf, size_t len)
754+
{
755+
BCRYPT_KEY_HANDLE ret = 0;
756+
LPCSTR der_buf = NULL;
757+
DWORD der_len;
758+
CERT_PUBLIC_KEY_INFO *key_info = NULL;
759+
DWORD key_info_len;
760+
ALLOCA_FLAG(use_heap);
761+
762+
if (!CryptStringToBinaryA(buf, len, CRYPT_STRING_BASE64HEADER, NULL, &der_len, NULL, NULL)) {
763+
goto finish;
764+
}
765+
der_buf = do_alloca(der_len, use_heap);
766+
if (!CryptStringToBinaryA(buf, len, CRYPT_STRING_BASE64HEADER, der_buf, &der_len, NULL, NULL)) {
767+
goto finish;
768+
}
769+
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, der_buf, der_len, CRYPT_ENCODE_ALLOC_FLAG, NULL, &key_info, &key_info_len)) {
770+
goto finish;
771+
}
772+
if (!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, key_info, CRYPT_OID_INFO_PUBKEY_ENCRYPT_KEY_FLAG, NULL, &ret)) {
773+
goto finish;
774+
}
775+
776+
finish:
777+
if (key_info) {
778+
LocalFree(key_info);
779+
}
780+
if (der_buf) {
781+
free_alloca(der_buf, use_heap);
782+
}
783+
return (mysqlnd_rsa_t) ret;
784+
}
785+
/* }}} */
786+
787+
/* {{{ mysqlnd_sha256_public_encrypt */
788+
static zend_uchar *
789+
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)
790+
{
791+
zend_uchar * ret = NULL;
792+
DWORD server_public_key_len = passwd_len;
793+
BCRYPT_OAEP_PADDING_INFO padding_info;
794+
795+
DBG_ENTER("mysqlnd_sha256_public_encrypt");
796+
797+
ZeroMemory(&padding_info, sizeof padding_info);
798+
padding_info.pszAlgId = BCRYPT_SHA1_ALGORITHM;
799+
if (BCryptEncrypt((BCRYPT_KEY_HANDLE) server_public_key, xor_str, passwd_len + 1, &padding_info,
800+
NULL, 0, NULL, 0, &server_public_key_len, BCRYPT_PAD_OAEP)) {
801+
DBG_RETURN(0);
802+
}
803+
804+
/*
805+
Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
806+
RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
807+
http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
808+
*/
809+
if ((size_t) server_public_key_len <= passwd_len + 41) {
810+
/* password message is to long */
811+
SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long");
812+
DBG_ERR("password is too long");
813+
DBG_RETURN(0);
814+
}
815+
816+
*auth_data_len = server_public_key_len;
817+
ret = malloc(*auth_data_len);
818+
if (BCryptEncrypt((BCRYPT_KEY_HANDLE) server_public_key, xor_str, passwd_len + 1, &padding_info,
819+
NULL, 0, ret, server_public_key_len, &server_public_key_len, BCRYPT_PAD_OAEP)) {
820+
DBG_RETURN(0);
821+
}
822+
BCryptDestroyKey((BCRYPT_KEY_HANDLE) server_public_key);
823+
DBG_RETURN(ret);
824+
}
825+
/* }}} */
826+
827+
#endif
828+
743829
/* {{{ mysqlnd_sha256_get_rsa_key */
744830
static mysqlnd_rsa_t
745831
mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn,
@@ -916,6 +1002,8 @@ void php_mysqlnd_scramble_sha2(zend_uchar * const buffer, const zend_uchar * con
9161002
}
9171003
/* }}} */
9181004

1005+
#ifndef PHP_WIN32
1006+
9191007
/* {{{ mysqlnd_caching_sha2_public_encrypt */
9201008
static size_t
9211009
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)
@@ -941,6 +1029,47 @@ mysqlnd_caching_sha2_public_encrypt(MYSQLND_CONN_DATA * conn, mysqlnd_rsa_t serv
9411029
}
9421030
/* }}} */
9431031

1032+
#else
1033+
1034+
/* {{{ mysqlnd_caching_sha2_public_encrypt */
1035+
static size_t
1036+
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)
1037+
{
1038+
DWORD server_public_key_len = passwd_len;
1039+
BCRYPT_OAEP_PADDING_INFO padding_info;
1040+
1041+
DBG_ENTER("mysqlnd_caching_sha2_public_encrypt");
1042+
1043+
ZeroMemory(&padding_info, sizeof padding_info);
1044+
padding_info.pszAlgId = BCRYPT_SHA1_ALGORITHM;
1045+
if (BCryptEncrypt((BCRYPT_KEY_HANDLE) server_public_key, xor_str, passwd_len + 1, &padding_info,
1046+
NULL, 0, NULL, 0, &server_public_key_len, BCRYPT_PAD_OAEP)) {
1047+
DBG_RETURN(0);
1048+
}
1049+
1050+
/*
1051+
Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len.
1052+
RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here:
1053+
http://www.openssl.org/docs/crypto/RSA_public_encrypt.html
1054+
*/
1055+
if ((size_t) server_public_key_len <= passwd_len + 41) {
1056+
/* password message is to long */
1057+
SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long");
1058+
DBG_ERR("password is too long");
1059+
DBG_RETURN(0);
1060+
}
1061+
1062+
*crypted = emalloc(server_public_key_len);
1063+
if (BCryptEncrypt((BCRYPT_KEY_HANDLE) server_public_key, xor_str, passwd_len + 1, &padding_info,
1064+
NULL, 0, *crypted, server_public_key_len, &server_public_key_len, BCRYPT_PAD_OAEP)) {
1065+
DBG_RETURN(0);
1066+
}
1067+
DBG_RETURN(server_public_key_len);
1068+
}
1069+
/* }}} */
1070+
1071+
#endif
1072+
9441073
/* {{{ mysqlnd_native_auth_get_auth_data */
9451074
static zend_uchar *
9461075
mysqlnd_caching_sha2_get_auth_data(struct st_mysqlnd_authentication_plugin * self,

0 commit comments

Comments
 (0)