Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 8.1k
Native Windows support for mysqlnd sha256 authentification#5210
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Uh oh!
There was an error while loading. Please reload this page.
Closed
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -694,20 +694,146 @@ 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> | ||
| #include <openssl/err.h> | ||
| typedef RSA * 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) | ||
| { | ||
| BIO * bio = BIO_new_mem_buf(buf, len); | ||
| RSA * ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); | ||
| BIO_free(bio); | ||
| return 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; | ||
| size_t server_public_key_len = (size_t) RSA_size(server_public_key); | ||
| DBG_ENTER("mysqlnd_sha256_public_encrypt"); | ||
| /* | ||
| 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 (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(NULL); | ||
| } | ||
| *auth_data_len = server_public_key_len; | ||
| ret = malloc(*auth_data_len); | ||
| RSA_public_encrypt(passwd_len + 1, (zend_uchar *) xor_str, ret, server_public_key, RSA_PKCS1_OAEP_PADDING); | ||
| RSA_free(server_public_key); | ||
| DBG_RETURN(ret); | ||
| } | ||
| /* }}} */ | ||
| #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 RSA * | ||
| static mysqlnd_rsa_t | ||
| mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn, | ||
| const MYSQLND_SESSION_OPTIONS * const session_options, | ||
| const MYSQLND_PFC_DATA * const pfc_data | ||
| ) | ||
| { | ||
| RSA * ret = NULL; | ||
| mysqlnd_rsa_t ret = NULL; | ||
| const char * fname = (pfc_data->sha256_server_public_key && pfc_data->sha256_server_public_key[0] != '\0')? | ||
| pfc_data->sha256_server_public_key: | ||
| MYSQLND_G(sha256_server_public_key); | ||
| @@ -739,11 +865,7 @@ mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn, | ||
| } | ||
| DBG_INF_FMT("Public key(%d):\n%s", pk_resp_packet.public_key_len, pk_resp_packet.public_key); | ||
| /* now extract the public key */ | ||
| { | ||
| BIO * bio = BIO_new_mem_buf(pk_resp_packet.public_key, pk_resp_packet.public_key_len); | ||
| ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); | ||
| BIO_free(bio); | ||
| } | ||
| ret = mysqlnd_sha256_get_rsa_from_pem((const char *) pk_resp_packet.public_key, pk_resp_packet.public_key_len); | ||
| } while (0); | ||
| PACKET_FREE(&pk_req_packet); | ||
| PACKET_FREE(&pk_resp_packet); | ||
| @@ -762,9 +884,7 @@ mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn, | ||
| if (stream) { | ||
| if ((key_str = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0)) != NULL) { | ||
| BIO * bio = BIO_new_mem_buf(ZSTR_VAL(key_str), ZSTR_LEN(key_str)); | ||
| ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); | ||
| BIO_free(bio); | ||
| ret = mysqlnd_sha256_get_rsa_from_pem(ZSTR_VAL(key_str), ZSTR_LEN(key_str)); | ||
| DBG_INF("Successfully loaded"); | ||
| DBG_INF_FMT("Public key:%*.s", ZSTR_LEN(key_str), ZSTR_VAL(key_str)); | ||
| zend_string_release_ex(key_str, 0); | ||
| @@ -788,7 +908,7 @@ mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self | ||
| const zend_ulong mysql_flags | ||
| ) | ||
| { | ||
| RSA * server_public_key; | ||
| mysqlnd_rsa_t server_public_key; | ||
| zend_uchar * ret = NULL; | ||
| DBG_ENTER("mysqlnd_sha256_auth_get_auth_data"); | ||
| DBG_INF_FMT("salt(%d)=[%.*s]", auth_plugin_data_len, auth_plugin_data_len, auth_plugin_data); | ||
| @@ -805,31 +925,12 @@ mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self | ||
| server_public_key = mysqlnd_sha256_get_rsa_key(conn, session_options, pfc_data); | ||
| if (server_public_key) { | ||
| int server_public_key_len; | ||
| ALLOCA_FLAG(use_heap); | ||
| char *xor_str = do_alloca(passwd_len + 1, use_heap); | ||
| memcpy(xor_str, passwd, passwd_len); | ||
| xor_str[passwd_len] = '\0'; | ||
| mysqlnd_xor_string(xor_str, passwd_len, (char *) auth_plugin_data, auth_plugin_data_len); | ||
| server_public_key_len = RSA_size(server_public_key); | ||
| /* | ||
| 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 - 41 <= passwd_len) { | ||
| /* password message is to long */ | ||
| free_alloca(xor_str, use_heap); | ||
| SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long"); | ||
| DBG_ERR("password is too long"); | ||
| DBG_RETURN(NULL); | ||
| } | ||
| *auth_data_len = server_public_key_len; | ||
| ret = malloc(*auth_data_len); | ||
| RSA_public_encrypt(passwd_len + 1, (zend_uchar *) xor_str, ret, server_public_key, RSA_PKCS1_OAEP_PADDING); | ||
| RSA_free(server_public_key); | ||
| ret = mysqlnd_sha256_public_encrypt(conn, server_public_key, passwd_len, auth_data_len, xor_str); | ||
| free_alloca(xor_str, use_heap); | ||
| } | ||
| } | ||
| @@ -901,6 +1002,73 @@ 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) | ||
| { | ||
| size_t server_public_key_len = (size_t) RSA_size(server_public_key); | ||
| DBG_ENTER("mysqlnd_caching_sha2_public_encrypt"); | ||
| /* | ||
| 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 (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); | ||
| RSA_public_encrypt(passwd_len + 1, (zend_uchar *) xor_str, *crypted, server_public_key, RSA_PKCS1_OAEP_PADDING); | ||
| DBG_RETURN(server_public_key_len); | ||
| } | ||
| /* }}} */ | ||
| #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); | ||
cmb69 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| 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 * | ||
| @@ -938,10 +1106,10 @@ mysqlnd_caching_sha2_get_auth_data(struct st_mysqlnd_authentication_plugin * sel | ||
| } | ||
| /* }}} */ | ||
| static RSA * | ||
| static mysqlnd_rsa_t | ||
| mysqlnd_caching_sha2_get_key(MYSQLND_CONN_DATA *conn) | ||
| { | ||
| RSA * ret = NULL; | ||
| mysqlnd_rsa_t ret = NULL; | ||
| const MYSQLND_PFC_DATA * const pfc_data = conn->protocol_frame_codec->data; | ||
| const char * fname = (pfc_data->sha256_server_public_key && pfc_data->sha256_server_public_key[0] != '\0')? | ||
| pfc_data->sha256_server_public_key: | ||
| @@ -975,11 +1143,7 @@ mysqlnd_caching_sha2_get_key(MYSQLND_CONN_DATA *conn) | ||
| } | ||
| DBG_INF_FMT("Public key(%d):\n%s", pk_resp_packet.public_key_len, pk_resp_packet.public_key); | ||
| /* now extract the public key */ | ||
| { | ||
| BIO * bio = BIO_new_mem_buf(pk_resp_packet.public_key, pk_resp_packet.public_key_len); | ||
| ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); | ||
| BIO_free(bio); | ||
| } | ||
| ret = mysqlnd_sha256_get_rsa_from_pem((const char *) pk_resp_packet.public_key, pk_resp_packet.public_key_len); | ||
| } while (0); | ||
| PACKET_FREE(&req_packet); | ||
| PACKET_FREE(&pk_resp_packet); | ||
| @@ -998,9 +1162,7 @@ mysqlnd_caching_sha2_get_key(MYSQLND_CONN_DATA *conn) | ||
| if (stream) { | ||
| if ((key_str = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0)) != NULL) { | ||
| BIO * bio = BIO_new_mem_buf(ZSTR_VAL(key_str), ZSTR_LEN(key_str)); | ||
| ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); | ||
| BIO_free(bio); | ||
| ret = mysqlnd_sha256_get_rsa_from_pem(ZSTR_VAL(key_str), ZSTR_LEN(key_str)); | ||
| DBG_INF("Successfully loaded"); | ||
| DBG_INF_FMT("Public key:%*.s", ZSTR_LEN(key_str), ZSTR_VAL(key_str)); | ||
| zend_string_release(key_str); | ||
| @@ -1013,16 +1175,15 @@ mysqlnd_caching_sha2_get_key(MYSQLND_CONN_DATA *conn) | ||
| } | ||
| /* {{{ mysqlnd_caching_sha2_get_key */ | ||
| /* {{{ mysqlnd_caching_sha2_get_and_use_key */ | ||
| static size_t | ||
| mysqlnd_caching_sha2_get_and_use_key(MYSQLND_CONN_DATA *conn, | ||
| const zend_uchar * auth_plugin_data, const size_t auth_plugin_data_len, | ||
| unsigned char **crypted, | ||
| const char * const passwd, | ||
| const size_t passwd_len) | ||
| { | ||
| static RSA *server_public_key; | ||
| server_public_key = mysqlnd_caching_sha2_get_key(conn); | ||
| mysqlnd_rsa_t server_public_key = mysqlnd_caching_sha2_get_key(conn); | ||
| DBG_ENTER("mysqlnd_caching_sha2_get_and_use_key("); | ||
| @@ -1033,23 +1194,7 @@ mysqlnd_caching_sha2_get_and_use_key(MYSQLND_CONN_DATA *conn, | ||
| memcpy(xor_str, passwd, passwd_len); | ||
| xor_str[passwd_len] = '\0'; | ||
| mysqlnd_xor_string(xor_str, passwd_len, (char *) auth_plugin_data, SCRAMBLE_LENGTH); | ||
| server_public_key_len = RSA_size(server_public_key); | ||
| /* | ||
| 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 - 41 <= passwd_len) { | ||
| /* password message is to long */ | ||
| free_alloca(xor_str, use_heap); | ||
| 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); | ||
| RSA_public_encrypt(passwd_len + 1, (zend_uchar *) xor_str, *crypted, server_public_key, RSA_PKCS1_OAEP_PADDING); | ||
| server_public_key_len = mysqlnd_caching_sha2_public_encrypt(conn, server_public_key, passwd_len, crypted, xor_str); | ||
| free_alloca(xor_str, use_heap); | ||
| DBG_RETURN(server_public_key_len); | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.