-
Notifications
You must be signed in to change notification settings - Fork 72
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
Implement support for cleartext authentication plugin #168
Conversation
e2f106c
to
c6f3eae
Compare
} else { | ||
auth_plugin = handshake->auth_plugin; | ||
} | ||
|
||
rc = trilogy_build_auth_packet(&builder, conn->socket->opts.username, conn->socket->opts.password, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can refactor this function to trilogy_build_auth_packet(&builder, conn->socket->opts, handshake, auth_plugin)
in another PR.
src/protocol.c
Outdated
@@ -568,7 +568,9 @@ int trilogy_build_auth_packet(trilogy_builder_t *builder, const char *user, cons | |||
|
|||
if (pass_len > 0) { | |||
// Fallback to te default unless we have SHA2 requested |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think this comment should likely be moved down to between line 573/4 and another comment should be added here to add context to the new "cleartext" behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great call, thanks! I didn't add a comment for the cleartext if-block because I think it's pretty straightforward. Happy to add it if maintainers think it's helpful.
auth_plugin = "mysql_clear_password"; | ||
} else { | ||
auth_plugin = handshake->auth_plugin; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's a difference between enabling the plugin so that it's used of the server switches to it and making it the default advertised by the client.
Is the behavior the same with libmysqlclient
that if it's enabled, it also becomes the default that the client advertises? Or does it only allow switching to it there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just so I understand correctly, you're asking if the server will accept the cleartext plugin password without the client informing the server of the plugin?
If so, then I believe that to be the case. According to the libmysqlclient
source code / documentation, we don't need to preemptively inform the server that we are using the cleartext plugin before the authentication connection. To put it in another way, if cleartext plugin is enabled, it does become the "default" that the client advertises, in a sense where the client can just send it to the server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if we connect with enable_cleartext_plugin :true
but then the server handshake indicates a non-cleartext plugin like caching_sha2_password or whatever? Will this code ignore what the server told us and send the cleartext password anyway? Does that then turn into an auth switch?
(I'm still learning about how these plugins work, so not sure I'm making sense.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, the first handshake we get back from the server will indicate the server's authentication plugin preference. However, according to the MySQL docs on "Determining Authentication Method", the client does not have to use the server's authentication method.
When including authentication reply in the Protocol::HandshakeResponse:, client is not obligated to use the same authentication method that was used by the server in the Protocol::Handshake packet.
As the code is right now, we will send the cleartext password but the server can still respond with an auth switch request, and we will do so accordingly. We think this is OK because the request from the user to use cleartext password has been fulfilled. If the server is still not happy then it's OK for us to do the auth switch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a good way of testing out one of the plugins that needs a cleartext password? I'm curious what plugin is advertised in the handshake for those. Like, if the server wants to use authentication_pam
does the handshake advertise the server plugin authentication_pam
or the client plugin mysql_clear_password
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the way, I should have started by saying thank you for working on this!
@HParker and I found a test server plugin that uses mysql_clear_password as the client plugin, and I was able to enable it in a locally with:
INSTALL PLUGIN cleartext_plugin_server SONAME 'auth_test_plugin.so';
CREATE USER 'cleartext_user'@'localhost' IDENTIFIED WITH cleartext_plugin_server BY 'password';
Then I connected via the command line client and looked at the packets:
mysql --protocol=tcp --ssl-mode=disable -p -u cleartext_user --enable-cleartext-plugin
- Server sends handshake, with auth plugin caching_sha2_password
- Client sends handshake response, still with auth plugin caching_sha2_password
- Server sends auth switch request, now with auth plugin
mysql_clear_password
- Client sends auth switch response with just the cleartext password and nothing else
- Server sends OK and we are connected
That's more what I was expecting. The server is telling the client to use mysql_clear_password
, and the --enable-cleartext-plugin
option only affects step 4. While a client can choose a different plugin than what the server advertised in the handshake, I don't know how the server handles mysql_clear_password
in particular since it is a client-side-only plugin.
Trying to connect to this same user with Trilogy using this PR results in:
Trilogy.new(host: '127.0.0.1', username: 'cleartext_user', password: 'password', enable_cleartext_plugin: true)
- Server sends handshake, with auth plugin caching_sha2_password
- Client sends handshake response, switching to
mysql_clear_password
- Server responds with "Access denied"
I'm not totally sure why that fails (including with the fix so we send the password). But maybe we should try to behave more like mysql
here?
// Fallback to te default unless we have SHA2 requested | ||
if (!strcmp("caching_sha2_password", auth_plugin)) { | ||
if (!strcmp("mysql_clear_password", auth_plugin)) { | ||
memcpy(auth_response, pass, pass_len); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this memcpy segfault if pass_len is longer than auth_response?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're also not setting auth_response_len
, which we check below to decide whether to write it at all.
err = trilogy_build_auth_packet(&builder, user, pass, strlen(pass), NULL, 45, "mysql_clear_password", scramble, 0); | ||
ASSERT_OK(err); | ||
|
||
static const uint8_t expected[] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a cleartext password in this expected output? I think it's the same as the same bytes as in test_build_auth_packet_cleartext (although a little difficult to visually diff). The missing auth_response_len
would explain it, I think.
auth_plugin = "mysql_clear_password"; | ||
} else { | ||
auth_plugin = handshake->auth_plugin; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if we connect with enable_cleartext_plugin :true
but then the server handshake indicates a non-cleartext plugin like caching_sha2_password or whatever? Will this code ignore what the server told us and send the cleartext password anyway? Does that then turn into an auth switch?
(I'm still learning about how these plugins work, so not sure I'm making sense.)
@@ -522,6 +522,10 @@ static VALUE rb_trilogy_connect(VALUE self, VALUE encoding, VALUE charset, VALUE | |||
connopt.flags |= TRILOGY_CAPABILITIES_CONNECT_WITH_DB; | |||
} | |||
|
|||
if (RTEST(rb_hash_aref(opts, ID2SYM(id_enable_cleartext_plugin)))) { | |||
connopt.enable_cleartext_plugin = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should warn or refuse to do this if the appropriate ssl options are not set.
Thanks for the feedback everyone! Chatted with @composerinteralia on Zoom and closing this in favour of: #171 |
Resolves: #157
The cleartext pluggable authentication is a client side plugin that can be set through the
--enable-cleartext-plugin
flag. We override the auth plugin received from the server inside the client, before building and sending the auth packet.I'm not sure if people like the variable name
enable_cleartext_plugin
but I defaulted to that since that's the flag.