diff options
author | Christoph M. Becker <cmbecker69@gmx.de> | 2020-02-26 17:01:37 +0100 |
---|---|---|
committer | Christoph M. Becker <cmbecker69@gmx.de> | 2020-03-02 16:32:51 +0100 |
commit | a0377021c5640cd6e6ad719a76dd38f91f7c367b (patch) | |
tree | 7f37060e40e18a435a951b5e3c8ca0c0e97227da | |
parent | a7400d5fd3337e3cc0067f5e50a65ba20377af4f (diff) | |
download | php-git-a0377021c5640cd6e6ad719a76dd38f91f7c367b.tar.gz |
Native Windows support for mysqlnd sha256 authentification
We implement that on top of Cryptography API: Next Generation (CNG).
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | ext/mysqlnd/config.w32 | 3 | ||||
-rw-r--r-- | ext/mysqlnd/mysqlnd_auth.c | 129 |
3 files changed, 136 insertions, 0 deletions
@@ -40,6 +40,10 @@ PHP NEWS - MySQLi: . Fixed bug #64032 (mysqli reports different client_version). (cmb) +- MySQLnd: + . Implemented FR #79275 (Support auth_plugin_caching_sha2_password on + Windows). (cmb) + - Opcache: . Fixed bug #79252 (preloading causes php-fpm to segfault during exit). (Nikita) diff --git a/ext/mysqlnd/config.w32 b/ext/mysqlnd/config.w32 index 1dac93578a..5f191297d1 100644 --- a/ext/mysqlnd/config.w32 +++ b/ext/mysqlnd/config.w32 @@ -36,6 +36,9 @@ if (PHP_MYSQLND != "no") { { AC_DEFINE("MYSQLND_COMPRESSION_ENABLED", 1, "Compression support"); AC_DEFINE("MYSQLND_SSL_SUPPORTED", 1, "SSL support"); + if (CHECK_LIB("crypt32.lib", "mysqlnd")) { + AC_DEFINE("MYSQLND_HAVE_SSL", 1, "Extended SSL support"); + } } PHP_INSTALL_HEADERS("", "ext/mysqlnd"); } diff --git a/ext/mysqlnd/mysqlnd_auth.c b/ext/mysqlnd/mysqlnd_auth.c index 42afe795bc..fa156fa0e1 100644 --- a/ext/mysqlnd/mysqlnd_auth.c +++ b/ext/mysqlnd/mysqlnd_auth.c @@ -694,6 +694,7 @@ mysqlnd_xor_string(char * dst, const size_t dst_len, const char * xor_str, const } } +#ifndef PHP_WIN32 #include <openssl/rsa.h> #include <openssl/pem.h> @@ -740,6 +741,91 @@ mysqlnd_sha256_public_encrypt(MYSQLND_CONN_DATA * conn, mysqlnd_rsa_t server_pub } /* }}} */ +#else + +#include <wincrypt.h> +#include <bcrypt.h> + +typedef HANDLE mysqlnd_rsa_t; + +/* {{{ mysqlnd_sha256_get_rsa_from_pem */ +static mysqlnd_rsa_t +mysqlnd_sha256_get_rsa_from_pem(const char *buf, size_t len) +{ + BCRYPT_KEY_HANDLE ret = 0; + LPCSTR der_buf = NULL; + DWORD der_len; + CERT_PUBLIC_KEY_INFO *key_info = NULL; + DWORD key_info_len; + ALLOCA_FLAG(use_heap); + + if (!CryptStringToBinaryA(buf, len, CRYPT_STRING_BASE64HEADER, NULL, &der_len, NULL, NULL)) { + goto finish; + } + der_buf = do_alloca(der_len, use_heap); + if (!CryptStringToBinaryA(buf, len, CRYPT_STRING_BASE64HEADER, der_buf, &der_len, NULL, NULL)) { + goto finish; + } + if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, der_buf, der_len, CRYPT_ENCODE_ALLOC_FLAG, NULL, &key_info, &key_info_len)) { + goto finish; + } + if (!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, key_info, CRYPT_OID_INFO_PUBKEY_ENCRYPT_KEY_FLAG, NULL, &ret)) { + goto finish; + } + +finish: + if (key_info) { + LocalFree(key_info); + } + if (der_buf) { + free_alloca(der_buf, use_heap); + } + return (mysqlnd_rsa_t) ret; +} +/* }}} */ + +/* {{{ mysqlnd_sha256_public_encrypt */ +static zend_uchar * +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) +{ + zend_uchar * ret = NULL; + DWORD server_public_key_len = passwd_len; + BCRYPT_OAEP_PADDING_INFO padding_info; + + DBG_ENTER("mysqlnd_sha256_public_encrypt"); + + ZeroMemory(&padding_info, sizeof padding_info); + padding_info.pszAlgId = BCRYPT_SHA1_ALGORITHM; + if (BCryptEncrypt((BCRYPT_KEY_HANDLE) server_public_key, xor_str, passwd_len + 1, &padding_info, + NULL, 0, NULL, 0, &server_public_key_len, BCRYPT_PAD_OAEP)) { + DBG_RETURN(0); + } + + /* + Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len. + RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here: + http://www.openssl.org/docs/crypto/RSA_public_encrypt.html + */ + if ((size_t) server_public_key_len <= passwd_len + 41) { + /* password message is to long */ + SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long"); + DBG_ERR("password is too long"); + DBG_RETURN(0); + } + + *auth_data_len = server_public_key_len; + ret = malloc(*auth_data_len); + if (BCryptEncrypt((BCRYPT_KEY_HANDLE) server_public_key, xor_str, passwd_len + 1, &padding_info, + NULL, 0, ret, server_public_key_len, &server_public_key_len, BCRYPT_PAD_OAEP)) { + DBG_RETURN(0); + } + BCryptDestroyKey((BCRYPT_KEY_HANDLE) server_public_key); + DBG_RETURN(ret); +} +/* }}} */ + +#endif + /* {{{ mysqlnd_sha256_get_rsa_key */ static mysqlnd_rsa_t 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 } /* }}} */ +#ifndef PHP_WIN32 + /* {{{ mysqlnd_caching_sha2_public_encrypt */ static size_t 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 } /* }}} */ +#else + +/* {{{ mysqlnd_caching_sha2_public_encrypt */ +static size_t +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) +{ + DWORD server_public_key_len = passwd_len; + BCRYPT_OAEP_PADDING_INFO padding_info; + + DBG_ENTER("mysqlnd_caching_sha2_public_encrypt"); + + ZeroMemory(&padding_info, sizeof padding_info); + padding_info.pszAlgId = BCRYPT_SHA1_ALGORITHM; + if (BCryptEncrypt((BCRYPT_KEY_HANDLE) server_public_key, xor_str, passwd_len + 1, &padding_info, + NULL, 0, NULL, 0, &server_public_key_len, BCRYPT_PAD_OAEP)) { + DBG_RETURN(0); + } + + /* + Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len. + RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here: + http://www.openssl.org/docs/crypto/RSA_public_encrypt.html + */ + if ((size_t) server_public_key_len <= passwd_len + 41) { + /* password message is to long */ + SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long"); + DBG_ERR("password is too long"); + DBG_RETURN(0); + } + + *crypted = emalloc(server_public_key_len); + if (BCryptEncrypt((BCRYPT_KEY_HANDLE) server_public_key, xor_str, passwd_len + 1, &padding_info, + NULL, 0, *crypted, server_public_key_len, &server_public_key_len, BCRYPT_PAD_OAEP)) { + DBG_RETURN(0); + } + DBG_RETURN(server_public_key_len); +} +/* }}} */ + +#endif + /* {{{ mysqlnd_native_auth_get_auth_data */ static zend_uchar * mysqlnd_caching_sha2_get_auth_data(struct st_mysqlnd_authentication_plugin * self, |