elixir-ecto / myxql

MySQL 5.5+ driver for Elixir
Apache License 2.0
273 stars 67 forks source link

Support long passwords (sha256 and caching_sha2) #125

Closed bschmeck closed 4 years ago

bschmeck commented 4 years ago

An argument error is encountered when trying to authenticate with a sha256 or caching_sha2 password longer than 20 bytes:

20:49:08.828 [error] GenServer #PID<0.406.0> terminating
** (ArgumentError) argument error
    (stdlib 3.5) :binary.part(<<105, 5, 61, 82, 25, 87, 87, 33, 9, 14, 54, 64, 20, 74, 2, 93, 37, 68, 77, 111>>, 0, 50)
    (myxql 0.3.4) lib/myxql/protocol/auth.ex:19: MyXQL.Protocol.Auth.encrypt_sha_password/3
    (myxql 0.3.4) lib/myxql/client.ex:402: MyXQL.Client.perform_public_key_auth/5
    (myxql 0.3.4) lib/myxql/client.ex:271: MyXQL.Client.do_handshake/2
    (myxql 0.3.4) lib/myxql/client.ex:252: MyXQL.Client.handshake/2
    (myxql 0.3.4) lib/myxql/connection.ex:32: MyXQL.Connection.connect/1
    (db_connection 2.2.2) lib/db_connection/connection.ex:69: DBConnection.Connection.connect/2
    (connection 1.0.4) lib/connection.ex:622: Connection.enter_connect/5
    (stdlib 3.5) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: nil
State: MyXQL.Connection

As part of the login process, MySQL sends a scramble value that is 20 bytes long and meant to be XOR-ed (by MyXQL) with the password supplied by the user. The first line of the backtrace shows that we pass that 20 byte value (stored as auth_plugin_data) to :binary.part and request 50 bytes (the length of the user's password). Naturally, that request fails.

Natively, MySQL will cycle through the scramble data as many times as needed, in order to match the length of the given password: https://github.com/mysql/mysql-server/blob/f8cdce86448a211511e8a039c62580ae16cb96f5/mysys/crypt_genhash_impl.cc#L438-L444

This PR adds tests demonstrating the error and a fix that will cycle through auth_plugin_data when the user's given password exceeds the length of auth_plugin_data.

wojtekmach commented 4 years ago

Thank you!