Skip to content

Commit

Permalink
Merge pull request #6716 from vamc19/mysql8-auth
Browse files Browse the repository at this point in the history
Support for caching_sha2_password plugin in mysql/client
  • Loading branch information
harshit-gangal committed Feb 22, 2021
2 parents 0a9e2dd + f92c55c commit 08c841d
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 68 deletions.
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.
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

0 comments on commit 08c841d

Please sign in to comment.