Skip to content

Commit

Permalink
Fix Auth Resnponse packet for cleartext password (#887)
Browse files Browse the repository at this point in the history
Trailing NUL char should be in `string[n] auth-response`.
But NUL was after auth-response.
  • Loading branch information
methane authored and julienschmidt committed Nov 14, 2018
1 parent 2b52e21 commit fb9c42f
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 75 deletions.
36 changes: 18 additions & 18 deletions auth.go
Expand Up @@ -234,64 +234,64 @@ func (mc *mysqlConn) sendEncryptedPassword(seed []byte, pub *rsa.PublicKey) erro
if err != nil {
return err
}
return mc.writeAuthSwitchPacket(enc, false)
return mc.writeAuthSwitchPacket(enc)
}

func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, bool, error) {
func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
switch plugin {
case "caching_sha2_password":
authResp := scrambleSHA256Password(authData, mc.cfg.Passwd)
return authResp, false, nil
return authResp, nil

case "mysql_old_password":
if !mc.cfg.AllowOldPasswords {
return nil, false, ErrOldPassword
return nil, ErrOldPassword
}
// Note: there are edge cases where this should work but doesn't;
// this is currently "wontfix":
// https://github.com/go-sql-driver/mysql/issues/184
authResp := scrambleOldPassword(authData[:8], mc.cfg.Passwd)
return authResp, true, nil
authResp := append(scrambleOldPassword(authData[:8], mc.cfg.Passwd), 0)
return authResp, nil

case "mysql_clear_password":
if !mc.cfg.AllowCleartextPasswords {
return nil, false, ErrCleartextPassword
return nil, ErrCleartextPassword
}
// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
return []byte(mc.cfg.Passwd), true, nil
return append([]byte(mc.cfg.Passwd), 0), nil

case "mysql_native_password":
if !mc.cfg.AllowNativePasswords {
return nil, false, ErrNativePassword
return nil, ErrNativePassword
}
// https://dev.mysql.com/doc/internals/en/secure-password-authentication.html
// Native password authentication only need and will need 20-byte challenge.
authResp := scramblePassword(authData[:20], mc.cfg.Passwd)
return authResp, false, nil
return authResp, nil

case "sha256_password":
if len(mc.cfg.Passwd) == 0 {
return nil, true, nil
return []byte{0}, nil
}
if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
// write cleartext auth packet
return []byte(mc.cfg.Passwd), true, nil
return append([]byte(mc.cfg.Passwd), 0), nil
}

pubKey := mc.cfg.pubKey
if pubKey == nil {
// request public key from server
return []byte{1}, false, nil
return []byte{1}, nil
}

// encrypted password
enc, err := encryptPassword(mc.cfg.Passwd, authData, pubKey)
return enc, false, err
return enc, err

default:
errLog.Print("unknown auth plugin:", plugin)
return nil, false, ErrUnknownPlugin
return nil, ErrUnknownPlugin
}
}

Expand All @@ -315,11 +315,11 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {

plugin = newPlugin

authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
return err
}
if err = mc.writeAuthSwitchPacket(authResp, addNUL); err != nil {
if err = mc.writeAuthSwitchPacket(authResp); err != nil {
return err
}

Expand Down Expand Up @@ -352,7 +352,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
case cachingSha2PasswordPerformFullAuthentication:
if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
// write cleartext auth packet
err = mc.writeAuthSwitchPacket([]byte(mc.cfg.Passwd), true)
err = mc.writeAuthSwitchPacket(append([]byte(mc.cfg.Passwd), 0))
if err != nil {
return err
}
Expand Down
73 changes: 37 additions & 36 deletions auth_test.go
Expand Up @@ -85,11 +85,11 @@ func TestAuthFastCachingSHA256PasswordCached(t *testing.T) {
plugin := "caching_sha2_password"

// Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
t.Fatal(err)
}
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
err = mc.writeHandshakeResponsePacket(authResp, plugin)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -130,11 +130,11 @@ func TestAuthFastCachingSHA256PasswordEmpty(t *testing.T) {
plugin := "caching_sha2_password"

// Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
t.Fatal(err)
}
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
err = mc.writeHandshakeResponsePacket(authResp, plugin)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -172,11 +172,11 @@ func TestAuthFastCachingSHA256PasswordFullRSA(t *testing.T) {
plugin := "caching_sha2_password"

// Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
t.Fatal(err)
}
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
err = mc.writeHandshakeResponsePacket(authResp, plugin)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -228,11 +228,11 @@ func TestAuthFastCachingSHA256PasswordFullRSAWithKey(t *testing.T) {
plugin := "caching_sha2_password"

// Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
t.Fatal(err)
}
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
err = mc.writeHandshakeResponsePacket(authResp, plugin)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -280,11 +280,11 @@ func TestAuthFastCachingSHA256PasswordFullSecure(t *testing.T) {
plugin := "caching_sha2_password"

// Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
t.Fatal(err)
}
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
err = mc.writeHandshakeResponsePacket(authResp, plugin)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -336,7 +336,7 @@ func TestAuthFastCleartextPasswordNotAllowed(t *testing.T) {
plugin := "mysql_clear_password"

// Send Client Authentication Packet
_, _, err := mc.auth(authData, plugin)
_, err := mc.auth(authData, plugin)
if err != ErrCleartextPassword {
t.Errorf("expected ErrCleartextPassword, got %v", err)
}
Expand All @@ -353,11 +353,11 @@ func TestAuthFastCleartextPassword(t *testing.T) {
plugin := "mysql_clear_password"

// Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
t.Fatal(err)
}
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
err = mc.writeHandshakeResponsePacket(authResp, plugin)
if err != nil {
t.Fatal(err)
}
Expand All @@ -367,8 +367,8 @@ func TestAuthFastCleartextPassword(t *testing.T) {
authRespEnd := authRespStart + 1 + len(authResp)
writtenAuthRespLen := conn.written[authRespStart]
writtenAuthResp := conn.written[authRespStart+1 : authRespEnd]
expectedAuthResp := []byte{115, 101, 99, 114, 101, 116}
if writtenAuthRespLen != 6 || !bytes.Equal(writtenAuthResp, expectedAuthResp) {
expectedAuthResp := []byte{115, 101, 99, 114, 101, 116, 0}
if writtenAuthRespLen != 7 || !bytes.Equal(writtenAuthResp, expectedAuthResp) {
t.Fatalf("unexpected written auth response (%d bytes): %v", writtenAuthRespLen, writtenAuthResp)
}
conn.written = nil
Expand Down Expand Up @@ -396,11 +396,11 @@ func TestAuthFastCleartextPasswordEmpty(t *testing.T) {
plugin := "mysql_clear_password"

// Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
t.Fatal(err)
}
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
err = mc.writeHandshakeResponsePacket(authResp, plugin)
if err != nil {
t.Fatal(err)
}
Expand All @@ -410,9 +410,9 @@ func TestAuthFastCleartextPasswordEmpty(t *testing.T) {
authRespEnd := authRespStart + 1 + len(authResp)
writtenAuthRespLen := conn.written[authRespStart]
writtenAuthResp := conn.written[authRespStart+1 : authRespEnd]
if writtenAuthRespLen != 0 {
t.Fatalf("unexpected written auth response (%d bytes): %v",
writtenAuthRespLen, writtenAuthResp)
expectedAuthResp := []byte{0}
if writtenAuthRespLen != 1 || !bytes.Equal(writtenAuthResp, expectedAuthResp) {
t.Fatalf("unexpected written auth response (%d bytes): %v", writtenAuthRespLen, writtenAuthResp)
}
conn.written = nil

Expand All @@ -439,7 +439,7 @@ func TestAuthFastNativePasswordNotAllowed(t *testing.T) {
plugin := "mysql_native_password"

// Send Client Authentication Packet
_, _, err := mc.auth(authData, plugin)
_, err := mc.auth(authData, plugin)
if err != ErrNativePassword {
t.Errorf("expected ErrNativePassword, got %v", err)
}
Expand All @@ -455,11 +455,11 @@ func TestAuthFastNativePassword(t *testing.T) {
plugin := "mysql_native_password"

// Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
t.Fatal(err)
}
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
err = mc.writeHandshakeResponsePacket(authResp, plugin)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -498,11 +498,11 @@ func TestAuthFastNativePasswordEmpty(t *testing.T) {
plugin := "mysql_native_password"

// Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
t.Fatal(err)
}
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
err = mc.writeHandshakeResponsePacket(authResp, plugin)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -540,11 +540,11 @@ func TestAuthFastSHA256PasswordEmpty(t *testing.T) {
plugin := "sha256_password"

// Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
t.Fatal(err)
}
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
err = mc.writeHandshakeResponsePacket(authResp, plugin)
if err != nil {
t.Fatal(err)
}
Expand All @@ -554,7 +554,8 @@ func TestAuthFastSHA256PasswordEmpty(t *testing.T) {
authRespEnd := authRespStart + 1 + len(authResp)
writtenAuthRespLen := conn.written[authRespStart]
writtenAuthResp := conn.written[authRespStart+1 : authRespEnd]
if writtenAuthRespLen != 0 {
expectedAuthResp := []byte{0}
if writtenAuthRespLen != 1 || !bytes.Equal(writtenAuthResp, expectedAuthResp) {
t.Fatalf("unexpected written auth response (%d bytes): %v", writtenAuthRespLen, writtenAuthResp)
}
conn.written = nil
Expand Down Expand Up @@ -587,11 +588,11 @@ func TestAuthFastSHA256PasswordRSA(t *testing.T) {
plugin := "sha256_password"

// Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
t.Fatal(err)
}
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
err = mc.writeHandshakeResponsePacket(authResp, plugin)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -636,11 +637,11 @@ func TestAuthFastSHA256PasswordRSAWithKey(t *testing.T) {
plugin := "sha256_password"

// Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
t.Fatal(err)
}
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
err = mc.writeHandshakeResponsePacket(authResp, plugin)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -669,26 +670,26 @@ func TestAuthFastSHA256PasswordSecure(t *testing.T) {
plugin := "sha256_password"

// send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
t.Fatal(err)
}

// unset TLS config to prevent the actual establishment of a TLS wrapper
mc.cfg.tls = nil

err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
err = mc.writeHandshakeResponsePacket(authResp, plugin)
if err != nil {
t.Fatal(err)
}

// check written auth response
authRespStart := 4 + 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1
authRespEnd := authRespStart + 1 + len(authResp) + 1
authRespEnd := authRespStart + 1 + len(authResp)
writtenAuthRespLen := conn.written[authRespStart]
writtenAuthResp := conn.written[authRespStart+1 : authRespEnd]
expectedAuthResp := []byte{115, 101, 99, 114, 101, 116, 0}
if writtenAuthRespLen != 6 || !bytes.Equal(writtenAuthResp, expectedAuthResp) {
if writtenAuthRespLen != 7 || !bytes.Equal(writtenAuthResp, expectedAuthResp) {
t.Fatalf("unexpected written auth response (%d bytes): %v", writtenAuthRespLen, writtenAuthResp)
}
conn.written = nil
Expand Down
6 changes: 3 additions & 3 deletions driver.go
Expand Up @@ -117,18 +117,18 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
}

// Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin)
authResp, err := mc.auth(authData, plugin)
if err != nil {
// try the default auth plugin, if using the requested plugin failed
errLog.Print("could not use requested auth plugin '"+plugin+"': ", err.Error())
plugin = defaultAuthPlugin
authResp, addNUL, err = mc.auth(authData, plugin)
authResp, err = mc.auth(authData, plugin)
if err != nil {
mc.cleanup()
return nil, err
}
}
if err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin); err != nil {
if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
mc.cleanup()
return nil, err
}
Expand Down

0 comments on commit fb9c42f

Please sign in to comment.