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

Support for caching_sha2_password plugin in mysql/client #6716

Merged
merged 6 commits into from
Feb 22, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
58 changes: 56 additions & 2 deletions go/mysql/auth_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ package mysql
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"encoding/hex"
"net"
"strings"
Expand Down Expand Up @@ -117,8 +119,8 @@ func NewSalt() ([]byte, error) {
return salt, nil
}

// ScramblePassword computes the hash of the password using 4.1+ method.
Copy link
Member

Choose a reason for hiding this comment

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

We are changing the interface here. If there is someone out there who built their own plugin auth server, this will break them.
We need to (at least) document this in the 10.0 release notes.

Copy link
Member

Choose a reason for hiding this comment

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

I do not see this as interface. If you think it can break someone's build, we can mark this as breaking change.

Copy link
Member

@deepthi deepthi Mar 2, 2021

Choose a reason for hiding this comment

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

You are correct, it is not part of the AuthServer interface. My mistake. There should be no impact on anyone.

func ScramblePassword(salt, password []byte) []byte {
// ScrambleMysqlNativePassword computes the hash of the password using 4.1+ method.
func ScrambleMysqlNativePassword(salt, password []byte) []byte {
if len(password) == 0 {
return nil
}
Expand Down Expand Up @@ -189,6 +191,58 @@ func isPassScrambleMysqlNativePassword(reply, salt []byte, mysqlNativePassword s
return bytes.Equal(candidateHash2, hash)
}

// ScrambleCachingSha2Password computes the hash of the password using SHA256 as required by
// caching_sha2_password plugin for "fast" authentication
func ScrambleCachingSha2Password(salt []byte, password []byte) []byte {
if len(password) == 0 {
return nil
}

// stage1Hash = SHA256(password)
crypt := sha256.New()
crypt.Write(password)
stage1 := crypt.Sum(nil)

// scrambleHash = SHA256(SHA256(stage1Hash) + salt)
crypt.Reset()
crypt.Write(stage1)
innerHash := crypt.Sum(nil)

crypt.Reset()
crypt.Write(innerHash)
crypt.Write(salt)
scramble := crypt.Sum(nil)

// token = stage1Hash XOR scrambleHash
for i := range stage1 {
stage1[i] ^= scramble[i]
}

return stage1
}

// EncryptPasswordWithPublicKey obfuscates the password and encrypts it with server's public key as required by
// caching_sha2_password plugin for "full" authentication
func EncryptPasswordWithPublicKey(salt []byte, password []byte, pub *rsa.PublicKey) ([]byte, error) {
if len(password) == 0 {
return nil, nil
}

buffer := make([]byte, len(password)+1)
copy(buffer, password)
for i := range buffer {
buffer[i] ^= salt[i%len(salt)]
}

sha1Hash := sha1.New()
enc, err := rsa.EncryptOAEP(sha1Hash, rand.Reader, pub, buffer, nil)
if err != nil {
return nil, err
}

return enc, nil
}

// Constants for the dialog plugin.
const (
mysqlDialogMessage = "Enter password: "
Expand Down
2 changes: 1 addition & 1 deletion go/mysql/auth_server_static.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ func (a *AuthServerStatic) ValidateHash(salt []byte, user string, authResponse [
return &StaticUserData{entry.UserData, entry.Groups}, nil
}
} else {
computedAuthResponse := ScramblePassword(salt, []byte(entry.Password))
computedAuthResponse := ScrambleMysqlNativePassword(salt, []byte(entry.Password))
// Validate the password.
if matchSourceHost(remoteAddr, entry.SourceHost) && bytes.Equal(authResponse, computedAuthResponse) {
return &StaticUserData{entry.UserData, entry.Groups}, nil
Expand Down
4 changes: 2 additions & 2 deletions go/mysql/auth_server_static_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func TestValidateHashGetter(t *testing.T) {
t.Fatalf("error generating salt: %v", err)
}

scrambled := ScramblePassword(salt, []byte("password"))
scrambled := ScrambleMysqlNativePassword(salt, []byte("password"))
getter, err := auth.ValidateHash(salt, "mysql_user", scrambled, addr)
if err != nil {
t.Fatalf("error validating password: %v", err)
Expand Down Expand Up @@ -270,7 +270,7 @@ func TestStaticPasswords(t *testing.T) {
t.Fatalf("error generating salt: %v", err)
}

scrambled := ScramblePassword(salt, []byte(c.password))
scrambled := ScrambleMysqlNativePassword(salt, []byte(c.password))
_, err = auth.ValidateHash(salt, c.user, scrambled, addr)

if c.success {
Expand Down
2 changes: 1 addition & 1 deletion go/mysql/auth_server_vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ func (a *AuthServerVault) ValidateHash(salt []byte, user string, authResponse []
return &StaticUserData{entry.UserData, entry.Groups}, nil
}
} else {
computedAuthResponse := ScramblePassword(salt, []byte(entry.Password))
computedAuthResponse := ScrambleMysqlNativePassword(salt, []byte(entry.Password))
// Validate the password.
if matchSourceHost(remoteAddr, entry.SourceHost) && bytes.Equal(authResponse, computedAuthResponse) {
return &StaticUserData{entry.UserData, entry.Groups}, nil
Expand Down