-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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
privileges: Upgrade stored password format to Compatible with MySQL. #3292
Conversation
Password stored in mysql.user of TIDB is the format of SHA1(pwd), which is not compatible with MySQL. Different password format will cause user migrate data from MySQL login failed on TIDB.
@tiancaiamao PTAL Please test updating an exist cluster with this branch. |
bootstrap.go
Outdated
@@ -376,6 +381,13 @@ func upgradeToVer9(s Session) { | |||
s.Execute("UPDATE mysql.user SET Trigger_priv='Y'") | |||
} | |||
|
|||
func upgradeToVer10(s Session) { | |||
_, err := s.Execute("UPDATE mysql.user SET `password` = old_password_upgrade(`password`)") |
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.
I don't think we need introduce the old_password_upgrade
builtin function.
This is not a standard MySQL function, define it as a local function or put it to util package is enough, no need to implement as builtin.
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.
agree
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.
if use local function, password can not be updated in one UPDATE statement, we should fetch rows from mysql.user and update each rows.
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.
You can begin; select; update; commit
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.
ok, i'll try that way
expression/builtin_encryption.go
Outdated
} | ||
|
||
// eval evals an upgrade from old password format stored in TIDB to MySQL format | ||
func (b *builtinOldPasswordUpgradeSig) eval(row []types.Datum) (d types.Datum, err error) { |
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.
You can put it to util package, or bootstarp.go in which it's used.
expression/builtin_encryption.go
Outdated
|
||
hash2 := util.Sha1Hash(hash1) | ||
d.SetString(fmt.Sprintf("*%X", hash2)) | ||
log.Infof("old password: %v new password:%v", pass, fmt.Sprintf("*%X", hash2)) |
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.
Don't do that, this log will leak user's information!
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.
OK
log.Errorf("User [%s] password from SystemDB not like a sha1sum", user) | ||
return false | ||
} | ||
|
||
// empty password | ||
if len(pwd) == 0 && len(auth) == 0 { |
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.
When will len(auth) == 0
?
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.
when password is empty
return true | ||
} | ||
|
||
if len(pwd) == 0 || len(auth) == 0 { |
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.
Is there any test cover this change ?
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.
no test cover this
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.
add test case done
util/auth.go
Outdated
|
||
"github.com/juju/errors" | ||
) | ||
|
||
// CalcPassword is the algorithm convert hashed password to auth string. | ||
// See https://dev.mysql.com/doc/internals/en/secure-password-authentication.html | ||
// SHA1( password ) XOR SHA1( "20-bytes random data from server" <concat> SHA1( SHA1( password ) ) ) | ||
func CalcPassword(scramble, sha1pwd []byte) []byte { | ||
if len(sha1pwd) == 0 { |
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 remove this check?
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.
in ConnectionVerification have handled this situation
// token = scrambleHash XOR stage1Hash | ||
for i := range scramble { | ||
scramble[i] ^= sha1pwd[i] |
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.
I'm confused with this function and the added auth parameter, could you explain it a bit ?
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.
I transfer the check logic from mysql source code, you can find the logic in function check_scramble in the file sql/password.c.
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.
The new authentication is performed in following manner:
SERVER: public_seed=create_random_string()
send(public_seed)
CLIENT: recv(public_seed)
hash_stage1=sha1("password")
hash_stage2=sha1(hash_stage1)
reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
// this three steps are done in scramble()
send(reply)
SERVER: recv(reply)
hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
candidate_hash2=sha1(hash_stage1)
check(candidate_hash2==hash_stage2)
// this three steps are done in check_scramble()
We need to update the comment;
And CalcPassword
is actually check_scramble
now? Maybe we need chose a better function name for it.
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.
change the function name to CheckScramble
Password stored in mysql.user of TIDB is the format of SHA1(pwd), which is not compatible with MySQL. Different password format will cause user migrate data from MySQL login failed on TIDB.
bootstrap.go
Outdated
@@ -382,9 +383,37 @@ func upgradeToVer9(s Session) { | |||
} | |||
|
|||
func upgradeToVer10(s Session) { | |||
_, err := s.Execute("UPDATE mysql.user SET `password` = old_password_upgrade(`password`)") | |||
sql := "SELECT user, host, password FROM mysql.user WHERE password != ''" |
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.
s.Execute("begin")
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.
Do we need select for update? @shenli
bootstrap.go
Outdated
} | ||
|
||
for _, sql := range sqls { | ||
mustExecute(s, sql) | ||
} |
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.
mustExecute(s, "commit")
util/auth.go
Outdated
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
return x, nil | ||
} | ||
|
||
// OldPasswordUpgrade upgrade password to MySQL compatible format | ||
func OldPasswordUpgrade(pass string) (string, error) { |
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.
Is there any test that cover this new added function?
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.
Add test case done
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.
update
return true | ||
} | ||
|
||
if len(pwd) == 0 || len(auth) == 0 { |
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.
add test case done
util/auth.go
Outdated
// candidate_hash2=sha1(hash_stage1) | ||
// check(candidate_hash2==hash_stage2) | ||
// // this three steps are done in check_scramble() | ||
func CheckScramble(scramble, hpwd, auth []byte) []byte { |
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.
Check inside it and let this function return bool, CheckXXX
return []byte is strange.
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.
Already fixed!
for _, sql := range sqls { | ||
mustExecute(s, sql) | ||
} | ||
|
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.
Add updateBootstrapVer(s)
here.
This upgrade is not reentrant, right? So it must be success/fail along with bootstrap version change.
Rest LGTM
PTAL @shenli
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.
Add updateBootstrapVer(s) in upgradeToVer11 is not a good choice,
cause updateBootstrapVer update tidb to currentBootstrapVersion instead of Version11,
we should update to version11, so i'd like to add the following code:
sql := fmt.Sprintf(`INSERT INTO %s.%s VALUES ("%s", "%d", "TiDB bootstrap version.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE="%d"`,
mysql.SystemDB, mysql.TiDBTable, tidbServerVersionVar, version11, version11)
mustExecute(s, sql)
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.
Yes, you're right!
@tiancaiamao what's wrong with the check:
last for long time this status... |
LGTM |
PTAL @shenli |
@shenli PTAL |
util/auth.go
Outdated
// candidate_hash2=sha1(hash_stage1) | ||
// check(candidate_hash2==hash_stage2) | ||
// // this three steps are done in check_scramble() | ||
func CheckScramble(scramble, hpwd, auth []byte) bool { |
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.
Rename to CheckScrambledPassword
is more clear.
And I think the first argument should be salt
instead of scramble
.
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.
Yes, i will modify it.
bootstrap.go
Outdated
user := row.Data[0].GetString() | ||
host := row.Data[1].GetString() | ||
pass := row.Data[2].GetString() | ||
newpass, err := util.OldPasswordUpgrade(pass) |
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.
I think move OldPasswordUpgrade
implementation here is better, because this function is only used here and nowhere else.
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.
i think leave this function in util will make bootstrap concise although it is just used in bootstrap.
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.
Semantically upgrade
operation doesn't belong to util
.
I think this need to be considered with higher priority.
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.
ok, i will move the function to bootstrap.
Password stored in mysql.user of TIDB is the format of SHA1(pwd), which is not compatible with MySQL. Different password format will cause user migrate data from MySQL login failed on TIDB.
LGTM |
bootstrap.go
Outdated
return | ||
} | ||
r := rs[0] | ||
sqls := make([]string, 1) |
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.
sqls := make([]string, 0, 1)
Good job! Thanks for your contribution! @louishust |
Password stored in mysql.user of TIDB is the format of SHA1(pwd),
which is not compatible with MySQL.
Different password format will cause user migrate data from MySQL
login failed on TIDB.