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

privileges: Upgrade stored password format to Compatible with MySQL. #3292

Merged
merged 25 commits into from
Jun 1, 2017

Conversation

louishust
Copy link
Contributor

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.

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.
@shenli
Copy link
Member

shenli commented May 20, 2017

@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`)")
Copy link
Contributor

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.

Copy link
Member

Choose a reason for hiding this comment

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

agree

Copy link
Contributor Author

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.

Copy link
Contributor

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

Copy link
Contributor Author

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

}

// 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) {
Copy link
Contributor

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.


hash2 := util.Sha1Hash(hash1)
d.SetString(fmt.Sprintf("*%X", hash2))
log.Infof("old password: %v new password:%v", pass, fmt.Sprintf("*%X", hash2))
Copy link
Contributor

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!

Copy link
Contributor Author

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 {
Copy link
Contributor

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 ?

Copy link
Contributor Author

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 {
Copy link
Contributor

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 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no test cover this

Copy link
Contributor Author

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 {
Copy link
Contributor

Choose a reason for hiding this comment

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

why remove this check?

Copy link
Contributor Author

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]
Copy link
Contributor

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 ?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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

louishust added 2 commits May 21, 2017 15:10
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 != ''"
Copy link
Contributor

Choose a reason for hiding this comment

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

s.Execute("begin")

Copy link
Contributor

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)
}
Copy link
Contributor

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) {
Copy link
Contributor

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?

Copy link
Contributor Author

@louishust louishust May 22, 2017

Choose a reason for hiding this comment

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

Add test case done

Copy link
Contributor Author

@louishust louishust left a 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 {
Copy link
Contributor Author

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 {
Copy link
Contributor

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.

Copy link
Contributor Author

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)
}

Copy link
Contributor

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

Copy link
Contributor Author

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)

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, you're right!

@louishust
Copy link
Contributor Author

@tiancaiamao what's wrong with the check:

license/cla — Waiting for status to be reported

last for long time this status...

@tiancaiamao
Copy link
Contributor

LGTM
PTAL @shenli @zimulala @hanfei1991

@XuHuaiyu XuHuaiyu added status/LGT1 Indicates that a PR has LGTM 1. contribution This PR is from a community contributor. labels May 24, 2017
@tiancaiamao
Copy link
Contributor

PTAL @shenli

@louishust
Copy link
Contributor Author

@shenli PTAL

@tiancaiamao
Copy link
Contributor

PTAL @coocood @shenli

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 {
Copy link
Member

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.

Copy link
Contributor Author

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)
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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.

louishust added 4 commits May 31, 2017 11:49
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.
@coocood
Copy link
Member

coocood commented May 31, 2017

LGTM

bootstrap.go Outdated
return
}
r := rs[0]
sqls := make([]string, 1)
Copy link
Contributor

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)

@tiancaiamao tiancaiamao merged commit f453b19 into pingcap:master Jun 1, 2017
@tiancaiamao
Copy link
Contributor

Good job! Thanks for your contribution! @louishust

@louishust louishust deleted the auth branch July 15, 2017 07:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contribution This PR is from a community contributor. status/LGT1 Indicates that a PR has LGTM 1.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants