Skip to content
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

add support for authentication plugins. #552

Closed
wants to merge 2 commits into from

Conversation

craiggwilson
Copy link

@craiggwilson craiggwilson commented Mar 17, 2017

Description

Currently, the driver does not support authentication plugins. I have refactored the code to support plugins and the existing authentication verification to be plugin-based. That is to say that cleartext, native, and old are now plugins. All tests pass on windows, although I have not tested on linux, so before merging, it would be great if that were vetted as well. I don't believe this warrants README file changes.

Motivation for this is that my company (MongoDB) needs to use a different authentication mechanism. We have developed the auth plugin separately and tested it against this implementation.

Thanks for your consideration.

Checklist

  • Code compiles correctly
  • Created tests which fail without the change (if possible)
  • All tests passing
  • Extended the README / documentation, if necessary
  • Added myself / the copyright holder to the AUTHORS file

auth.go Outdated

import "bytes"

const mysqlClearPassword = "mysql_clear_password"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use

const (
     mysqlClearPassword = "mysql_clear_password"
     mysqlNativePassword = "mysql_native_password"
     ...
)

for a list of constants like this. I think go vet or go fmt should also catch this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

type AuthPlugin interface {
// Next takes a server's challenge and returns
// the bytes to send back or an error.
Next(challenge []byte) ([]byte, error)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this called Next and not e.g. Auth (which I believe is what this does)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is called next because it might be called more than once. All the current plugins, clear, native, and old all are single step. Next is called once and they are complete. However, not all plugins will be this way, so calling it Auth and having it called multiple times is weird (to me). Precedent was pulled from Java's driver, which calls it Next as well. However, I'm certainly not tied to that name.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. Maybe also add a short comment to the code that it might be called more than once.

auth.go Outdated
return nil, ErrCleartextPassword
}

// \0-terminated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other parts of the driver we refer to \0 as NUL, please also use it here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@julienschmidt
Copy link
Member

julienschmidt commented Mar 17, 2017

I very much would like to see support for authentication plugins. However, one problem with the current implementation is, that it seems to change existing behavior. We had many problems because of slightly broken server implementations in the past. Since these were problems with specific server implementations, I think we also do not have regression tests for most of these, since we don't have a mock implementation of a MySQL server in our test suite. I think older clients also don't send an authentication plugin name by default, so it might not work with older servers.

PS: I can find neither your name nor MongoDB in the AUTHORS file

@craiggwilson
Copy link
Author

Oops, had it in the AUTHORS file but accidentally reverted.

Yeah, that was my concern also. I have tested this against mysql 5.7 and against our MySQL proxy. I don't think behavior is changed in that I tried very hard to make sure that the code flow remained identical, albeit through different functions. But I can't be completely sure and certainly have not exhaustively tested all versions.

@craiggwilson
Copy link
Author

Oh, as for older servers not sending a plugin name, I believe you are correct. This should be accounted for by defaulting to use the default plugin (i.e. attempting to use native) and then falling back to old, the same way it was handled previously.

@@ -92,6 +93,10 @@ func (mc *mysqlConn) Close() (err error) {
// closed the network connection.
func (mc *mysqlConn) cleanup() {
// Makes cleanup idempotent
if mc.authPlugin != nil {
mc.authPlugin.Close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The auth plugin is not used again after the connection is established and can therefore be closed in the init phase already, I believe. We also don't have to add it as a field to mc then.

@julienschmidt
Copy link
Member

We're probably also not the first mysql driver to implement this. We should also take a look how it is implemented in drivers for other languages. Maybe @methane knows more

@julienschmidt
Copy link
Member

@methane please take a look when time permits it

var authPluginName string
if len(data) > pos {
// auth-plugin name (string[NUL])
authPluginName = string(data[pos : len(data)-1])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Search NUL byte. When NUL not found, plugin name is data[pos:].
When NUL found, plugin name is data[pos:nul_index].

return err
}

return handleAuthResult(mc, authData)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like recursion. Please loop outside of this function.

@arvenil
Copy link
Contributor

arvenil commented May 1, 2018

@craiggwilson Any update on this?

@methane methane mentioned this pull request May 15, 2018
5 tasks
@julienschmidt julienschmidt mentioned this pull request May 24, 2018
5 tasks
@julienschmidt
Copy link
Member

@craiggwilson are you interested in updating your pull request? We changed much of the auth handling in the meantime, the updated PR should actually be a lot simpler now.

@craiggwilson
Copy link
Author

No, we can close it. Feel free to use any code that might still be pertinent, but I don't have time at this moment. Thanks for the great work!

@julienschmidt
Copy link
Member

Thanks for the PR anyway! We already applied many of proposed changes in #807 (and of course gave you credit for it). The missing bit is the API to add custom auth plugins. I opened #931 for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants