Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Failing to strip null terminator from authData in case of AuthSwitchRequest causes Access denied #1666

Closed
bdollma-te opened this issue Jan 27, 2025 · 2 comments

Comments

@bdollma-te
Copy link
Contributor

bdollma-te commented Jan 27, 2025

We recently promoted to RDS MySQL 8 in AWS and got the following error:

Error 1045 (28000): Access denied for user 'username'@'10.XXX.XXX.XXX' (using password: YES)

It all started because of this recent change in AWS: https://aws.amazon.com/about-aws/whats-new/2024/12/amazon-rds-proxy-sha2-password-authentication-mysql-aurora-rds// . Apparently, connecting to RDS via a proxy now tries to promote the plugin to caching_sha2_password.

Our Java apps connect successfully. Also, mysql from CLI connects succesfully.

So this is what happens in code:

  1. The client tries to auth using plugin mysql_native_password.
  2. The server responds with [254 99 97 99 104 105 110 103 95 115 104 97 50 95 112 97 115 115 119 111 114 100 0 3 67 113 67 11 105 47 18 75 27 28 34 37 111 81 43 44 102 83 43 0]. Let's analyze what you do with this packet in the following method
    func (mc *mysqlConn) readAuthResult() ([]byte, string, error) {
    The first byte is 254, so it falls under case iEOF. The first null terminated string, is [99 97 99 104 105 110 103 95 115 104 97 50 95 112 97 115 115 119 111 114 100 0] which translates to caching_sha2_password. So the server is trying to promote us to caching_sha2_password as expected. The authData which is later used to scramble the SHA256 is the rest of the packet, however you are forgetting to strip the null termination from it. The current code is:
authData := data[pluginEndIndex+1:]

which in our case is [3 67 113 67 11 105 47 18 75 27 28 34 37 111 81 43 44 102 83 43 0] - i.e., null terminated.

For testing purposes, I stripped the 0 from the end, by changing it to:

authData := data[pluginEndIndex+1 : len(data)-1]

and now the client connects successfully and performs the queries without Access denied.
Thanks

@bdollma-te bdollma-te mentioned this issue Jan 27, 2025
5 tasks
@bdollma-te bdollma-te changed the title Failed to strip null termination from authData in case iEOF Failing to strip null terminator from authData in case of AuthSwitchRequest causes Access denied Jan 27, 2025
@methane
Copy link
Member

methane commented Jan 28, 2025

2. however you are forgetting to strip the null termination from it. The current code is:

Is there evidence that it is not an RDS Proxy problem but a problem with this driver?

https://dev.mysql.com/doc/dev/mysql-server/9.1.0/page_protocol_connection_phase_packets_protocol_auth_switch_request.html

auth data is terminated by EOF, not NUL.

@bdollma-te
Copy link
Contributor Author

bdollma-te commented Jan 28, 2025

  • I read this and I agree that it is ambiguous. I can't find any doc stating that EOF strings are null terminated or not. The original MySQL Wire Protocol is defined in terms of of the C language implementation, which uses null-terminated strings. The serializing is probably a packing of a struct in C, therefore I expect it to be null terminated.
  • The Java driver and mysql-client (apt get) work with the RDS proxy, meaning they are stripping. Here is the Python implementation from the official library - also stripping https://github.com/mysql/mysql-connector-python/blob/e27103535aa39ef75c3a19c2d4e7c26a73adcb2f/mysql-connector-python/lib/mysql/connector/protocol.py#L137
  • Also please see this https://github.com/mysql/mysql-router/blob/cc0179f982bb9739a834eb6fd205a56224616133/src/mock_server/src/mysql_protocol_encoder.cc#L206 which states that the authData is null terminated.
  • I tested this with a local docker mysql server. The result is also null terminated as you can see here: [254 99 97 99 104 105 110 103 95 115 104 97 50 95 112 97 115 115 119 111 114 100 0 17 7 104 61 105 18 66 75 14 78 116 113 126 43 69 112 123 7 82 87 0]. Only that in this case, both authData versions (with or without 0 at the end) work.
  • The testing byte buffer you used auth_test.go is also null terminated:
    	// auth switch request
    	conn.data = []byte{44, 0, 0, 2, 254, 99, 97, 99, 104, 105, 110, 103, 95,
    		115, 104, 97, 50, 95, 112, 97, 115, 115, 119, 111, 114, 100, 0, 101,
    		11, 26, 18, 94, 97, 22, 72, 2, 46, 70, 106, 29, 55, 45, 94, 76, 90, 84,
    		50, 0}
    so why did you add a 0 in the end, if EOFs are not null terminated?
  • I can adapt the patch to strip the last byte only if it is 0, if you find that more convincing.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants