-
Notifications
You must be signed in to change notification settings - Fork 2.3k
add support for authentication plugins. #552
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,9 @@ type AuthPlugin interface { | |
// Next takes a server's challenge and returns | ||
// the bytes to send back or an error. | ||
Next(challenge []byte) ([]byte, error) | ||
|
||
// Close cleans up the resources of the plugin. | ||
Close() | ||
} | ||
|
||
type clearTextPlugin struct { | ||
|
@@ -51,6 +54,8 @@ func (p *clearTextPlugin) Next(challenge []byte) ([]byte, error) { | |
return append([]byte(p.cfg.Passwd), 0), nil | ||
} | ||
|
||
func (p *clearTextPlugin) Close() {} | ||
|
||
type nativePasswordPlugin struct { | ||
cfg *Config | ||
} | ||
|
@@ -64,6 +69,8 @@ func (p *nativePasswordPlugin) Next(challenge []byte) ([]byte, error) { | |
return scramblePassword(challenge, []byte(p.cfg.Passwd)), nil | ||
} | ||
|
||
func (p *nativePasswordPlugin) Close() {} | ||
|
||
type oldPasswordPlugin struct { | ||
cfg *Config | ||
} | ||
|
@@ -77,7 +84,9 @@ func (p *oldPasswordPlugin) Next(challenge []byte) ([]byte, error) { | |
return append(scrambleOldPassword(challenge, []byte(p.cfg.Passwd)), 0), nil | ||
} | ||
|
||
func handleAuthResult(mc *mysqlConn, plugin AuthPlugin, oldCipher []byte) error { | ||
func (p *oldPasswordPlugin) Close() {} | ||
|
||
func handleAuthResult(mc *mysqlConn, oldCipher []byte) error { | ||
data, err := mc.readPacket() | ||
if err != nil { | ||
return err | ||
|
@@ -91,11 +100,12 @@ func handleAuthResult(mc *mysqlConn, plugin AuthPlugin, oldCipher []byte) error | |
return mc.handleOkPacket(data) | ||
|
||
case iEOF: // auth switch | ||
mc.authPlugin.Close() | ||
if len(data) > 1 { | ||
pluginEndIndex := bytes.IndexByte(data, 0x00) | ||
pluginName := string(data[1:pluginEndIndex]) | ||
if apf, ok := authPluginFactories[pluginName]; ok { | ||
plugin = apf(mc.cfg) | ||
mc.authPlugin = apf(mc.cfg) | ||
} else { | ||
return ErrUnknownPlugin | ||
} | ||
|
@@ -105,7 +115,7 @@ func handleAuthResult(mc *mysqlConn, plugin AuthPlugin, oldCipher []byte) error | |
} | ||
} else { | ||
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest | ||
plugin = authPluginFactories[mysqlOldPassword](mc.cfg) | ||
mc.authPlugin = authPluginFactories[mysqlOldPassword](mc.cfg) | ||
authData = oldCipher | ||
} | ||
case iAuthContinue: | ||
|
@@ -115,7 +125,7 @@ func handleAuthResult(mc *mysqlConn, plugin AuthPlugin, oldCipher []byte) error | |
return mc.handleErrorPacket(data) | ||
} | ||
|
||
authData, err = plugin.Next(authData) | ||
authData, err = mc.authPlugin.Next(authData) | ||
if err != nil { | ||
return err | ||
} | ||
|
@@ -125,5 +135,5 @@ func handleAuthResult(mc *mysqlConn, plugin AuthPlugin, oldCipher []byte) error | |
return err | ||
} | ||
|
||
return handleAuthResult(mc, plugin, authData) | ||
return handleAuthResult(mc, authData) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like recursion. Please loop outside of this function. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ type mysqlConn struct { | |
sequence uint8 | ||
parseTime bool | ||
strict bool | ||
authPlugin AuthPlugin | ||
} | ||
|
||
// Handles parameters set in DSN after the connection is established | ||
|
@@ -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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.authPlugin = nil | ||
} | ||
if mc.netConn != nil { | ||
if err := mc.netConn.Close(); err != nil { | ||
errLog.Print(err) | ||
|
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.
Why is this called
Next
and not e.g.Auth
(which I believe is what this does)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.
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.
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.
That makes sense. Maybe also add a short comment to the code that it might be called more than once.