Skip to content

Commit

Permalink
Enable cleartext plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
danschultzer committed Feb 10, 2024
1 parent edd6855 commit 9735e14
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 3 deletions.
3 changes: 3 additions & 0 deletions lib/myxql.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ defmodule MyXQL do
| {:ping_timeout, timeout()}
| {:prepare, :force_named | :named | :unnamed}
| {:disconnect_on_error_codes, [atom()]}
| {:enable_cleartext_plugin, boolean()}
| DBConnection.start_option()

@type option() :: DBConnection.option()
Expand Down Expand Up @@ -100,6 +101,8 @@ defmodule MyXQL do
will disconnect the connection. See "Disconnecting on Errors" section below for more
information.
* `:enable_cleartext_plugin` - Set to `true` to send password as cleartext (default: `false`)
The given options are passed down to DBConnection, some of the most commonly used ones are
documented below:
Expand Down
11 changes: 8 additions & 3 deletions lib/myxql/client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ defmodule MyXQL.Client do
:socket_options,
:max_packet_size,
:charset,
:collation
:collation,
:enable_cleartext_plugin
]

def new(opts) do
Expand All @@ -45,7 +46,8 @@ defmodule MyXQL.Client do
socket_options:
Keyword.merge([mode: :binary, packet: :raw, active: false], opts[:socket_options] || []),
charset: Keyword.get(opts, :charset),
collation: Keyword.get(opts, :collation)
collation: Keyword.get(opts, :collation),
enable_cleartext_plugin: Keyword.get(opts, :enable_cleartext_plugin, false)
}
end

Expand Down Expand Up @@ -404,7 +406,10 @@ defmodule MyXQL.Client do
initial_handshake(
auth_plugin_name: initial_auth_plugin_name,
auth_plugin_data: initial_auth_plugin_data
) = initial_handshake
) = case config.enable_cleartext_plugin do
true -> initial_handshake(initial_handshake, auth_plugin_name: "mysql_clear_password", auth_plugin_data: nil)
false -> initial_handshake
end

auth_response = Auth.auth_response(config, initial_auth_plugin_name, initial_auth_plugin_data)

Expand Down
3 changes: 3 additions & 0 deletions lib/myxql/protocol/auth.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ defmodule MyXQL.Protocol.Auth do
config.password == nil ->
""

auth_plugin_name == "mysql_clear_password" ->
config.password <> <<0>>

auth_plugin_name == "mysql_native_password" ->
mysql_native_password(config.password, initial_auth_plugin_data)

Expand Down
2 changes: 2 additions & 0 deletions lib/myxql/protocol/types.ex
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ defmodule MyXQL.Protocol.Types do
string
end

def take_string_nul(""), do: {nil, ""}

def take_string_nul(binary) do
[string, rest] = :binary.split(binary, <<0>>)
{string, rest}
Expand Down
73 changes: 73 additions & 0 deletions test/myxql/client_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,23 @@ defmodule MyXQL.ClientTest do
Client.com_quit(client)
end

# mysql_clear_password

test "mysql_clear_password" do
opts = [username: "mysql_clear", password: "secret", enable_cleartext_plugin: true] ++ @opts
%{port: port} = start_cleartext_fake_server()
opts = Keyword.put(opts, :port, port)
assert {:ok, client} = Client.connect(opts)
Client.com_quit(client)
end

test "mysql_clear_password (bad password)" do
opts = [username: "mysql_clear", password: "bad", enable_cleartext_plugin: true] ++ @opts
%{port: port} = start_cleartext_fake_server()
opts = Keyword.put(opts, :port, port)
{:error, err_packet(message: "Access denied" <> _)} = Client.connect(opts)
end

# sha256_password

@tag sha256_password: true, public_key_exchange: true
Expand Down Expand Up @@ -447,4 +464,60 @@ defmodule MyXQL.ClientTest do

%{pid: pid, port: port}
end


defp start_cleartext_fake_server() do
start_fake_server(fn %{accept_socket: sock} ->
initial_handshake =
<<74, 0, 0, 0, 10, 56, 46, 48, 46, 51, 53, 0, 127, 24, 4, 0, 93, 42, 61, 27, 60,
38, 85, 12, 0, 255, 255, 255, 2, 0, 255, 223, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 39, 48, 10, 117, 54, 65, 74, 37, 125, 121, 93, 6, 0, 109, 121, 115, 113,
108, 95, 110, 97, 116, 105, 118, 101, 95, 112, 97, 115, 115, 119, 111, 114,
100, 0>>

client_auth_response_1 =
<<84, 0, 0, 1, 10, 162, 11, 0, 255, 255, 255, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 121, 115, 113, 108, 95, 99,
108, 101, 97, 114, 0, 7, 115, 101, 99, 114, 101, 116, 0, 109, 121, 120, 113,
108, 95, 116, 101, 115, 116, 0, 109, 121, 115, 113, 108, 95, 99, 108, 101,
97, 114, 95, 112, 97, 115, 115, 119, 111, 114, 100, 0>>

auth_response_1 =
<<22, 0, 0, 2, 254, 109, 121, 115, 113, 108, 95, 99, 108, 101, 97, 114, 95, 112,
97, 115, 115, 119, 111, 114, 100, 0>>

client_auth_response_2 =
<<7, 0, 0, 3, 115, 101, 99, 114, 101, 116, 0>>

auth_response_2 =
<<7, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0>>

client_quit = <<1, 0, 0, 0, 1>>

close = <<7, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0>>

auth_response_invalid =
<<83, 0, 0, 1, 255, 21, 4, 35, 50, 56, 48, 48, 48, 65, 99, 99, 101, 115, 115,
32, 100, 101, 110, 105, 101, 100, 32, 102, 111, 114, 32, 117, 115, 101, 114,
32, 39, 100, 101, 102, 97, 117, 108, 116, 95, 97, 117, 116, 104, 39, 64, 39,
49, 57, 50, 46, 49, 54, 56, 46, 54, 53, 46, 49, 39, 32, 40, 117, 115, 105,
110, 103, 32, 112, 97, 115, 115, 119, 111, 114, 100, 58, 32, 89, 69, 83, 41>>

:gen_tcp.send(sock, initial_handshake)

case :gen_tcp.recv(sock, 0) do
{:ok, ^client_auth_response_1} ->
:gen_tcp.send(sock, auth_response_1)
{:ok, ^client_auth_response_2} = :gen_tcp.recv(sock, 0)
:gen_tcp.send(sock, auth_response_2)

{:ok, ^client_quit} = :gen_tcp.recv(sock, 0)

:gen_tcp.send(sock, close)

{:ok, _other} ->
:gen_tcp.send(sock, auth_response_invalid)
end
end)
end
end

0 comments on commit 9735e14

Please sign in to comment.