-
Notifications
You must be signed in to change notification settings - Fork 9.7k
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
etcdserver/auth: check empty password #5196
Conversation
Easiest way to reproduce:
err := bcrypt.CompareHashAndPassword([]byte("$2a$10$Zzmtqsix/KthzodN0t21iOHIUlejJeRESPVoH7LIFvc7RkyUK3zIG"), []byte(""))
fmt.Println(err == nil) // true |
@@ -90,8 +90,7 @@ func hasKeyPrefixAccess(sec auth.Store, r *http.Request, key string, recursive b | |||
plog.Warningf("auth: no such user: %s.", username) | |||
return false | |||
} | |||
authAsUser := sec.CheckPassword(user, 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.
we should probable fix CheckPassword function, not patch it in this func?
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.
right. Will fix the CheckPassword
.
return err == nil | ||
matched := err == nil | ||
// empty string password returns 'nil' error | ||
if user.Password != "" && 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.
move this line before bcrypt?
Still need to figure out why |
66cb407
to
fd60840
Compare
/cc @xiang90 @heyitsanthony PTAL.
|
|
password string | ||
|
||
isTLS bool | ||
isJSON 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.
this doesn't look necessary-- "value=" can be prepended to the value
string instead of setting isJSON : true
in the request
@heyitsanthony Cleaned up e2e tests. PTAL. And JSON body gets |
return old, err | ||
if user.Password != "" { | ||
var hash string | ||
hash, err = s.HashPassword(user.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 think merge should handle this? Does the behavior of hashpassword changed somehow?
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.
Actually this check was all we need. (remove empty check in HashPassword).
https://github.com/coreos/etcd/blob/master/etcdserver/api/v2http/client_auth.go#L422-L442 passes user without password (even when user has been created with password), so we need to check this line I think.
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 mean a few line under this, there is a merge call. In the merge call, the passed in user will be merged with the old user. If the old user has password, but the new user does not. The old password should be merged into new user. No?
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.
Old password gets overwritten with the hash value of empty string if user
was passed to UpdateUser
with empty password.
The line here https://github.com/coreos/etcd/blob/master/etcdserver/auth/auth.go#L457-L459
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.
So hash(empty) is not empty? And with your change, do we need that in merge at all? Or should we move your change to merge? I do not want the check to be at multiple places and one of them is wrong.
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.
Yeah it is already very confusing...
Do we want to calculate the hash inside merge
method?
I think it will be confusing too :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.
The first goal is to do this check at ONLY place, not two. Or we are making this worse.
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. I will see if we can put all into merge
method.
@gyuho sorry I mixed it up-- non-json requests would prepend |
@heyitsanthony Can you point me to the line that you mention? Think the link points to the old commit. Thanks! |
@gyuho the |
@heyitsanthony I think we need it for plain text. Doesn't work in my local machine. Without that line, I get:
|
ae915fc
to
890e07a
Compare
@gyuho right... it's doing that because the |
@heyitsanthony Just fixed to check @xiang90 Just put hash logic into PTAL. Thanks all! |
lgtm |
@@ -448,29 +442,33 @@ func (s *store) DisableAuth() error { | |||
// is called and returns a new User with these modifications applied. Think of | |||
// all Users as immutable sets of data. Merge allows you to perform the set | |||
// operations (desired grants and revokes) atomically | |||
func (u User) merge(n User) (User, error) { | |||
func (s *store) merge(ou, nu User) (User, 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.
can we pass in the password interface? i think there is an interface for this. so we do not need to make this a method on store?
@@ -448,29 +443,33 @@ func (s *store) DisableAuth() error { | |||
// is called and returns a new User with these modifications applied. Think of | |||
// all Users as immutable sets of data. Merge allows you to perform the set | |||
// operations (desired grants and revokes) atomically | |||
func (u User) merge(n User) (User, error) { | |||
func (s passwordStore) merge(ou, nu User) (User, 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.
@xiang90 Just changed this as a method to satisfy PasswordStore
interface to make it not as store
method.
PTAL.
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.
func (u User) merge (n User, passwordStore pws)
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.
Just fixed. PTAL. Thanks.
@@ -73,14 +88,14 @@ func TestMergeUser(t *testing.T) { | |||
}, | |||
{ | |||
User{User: "foo"}, | |||
User{User: "foo", Password: "$2a$10$aUPOdbOGNawaVSusg3g2wuC3AH6XxIr9/Ms4VgDvzrAVOJPYzZILa"}, |
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 do we want to change 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.
because we now do the hash inside merge.
Otherwise, since this is not empty string, merge method will recalculate the hash returning different value.
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.
that is ok. but the original test case is to ensure non-empty string password will not be overwrite. you change changes this test cases completely.
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.
Then should we drop this test case? We can only detect the empty string, not the non-empty string is already bcrypted-password, I think.
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. we can drop the old one. then why do we want to merge with an empty password user? that case is unclear. it is unclear about if the password is overwritten by the new user or is preserved for the existing user since both of them are empty.
@xiang90 I just dropped the old one.
Yeah I had made it empty just to pass the test. I also think it doesn't make sense to have that case. |
User{User: "foo", Password: "$2a$10$aUPOdbOGNawaVSusg3g2wuC3AH6XxIr9/Ms4VgDvzrAVOJPYzZILa"}, | ||
User{User: "foo", Roles: []string{}, Password: "$2a$10$aUPOdbOGNawaVSusg3g2wuC3AH6XxIr9/Ms4VgDvzrAVOJPYzZILa"}, | ||
false, | ||
}, |
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.
We need to have a test case to cover your new change. We need to ensure the empty password will not overwrite the previous password.
Can we add
{
User{User: "foo"},
User{User: "foo", "ps"},
User{User: "foo", "ps"},
false,
}
?
User{User: "foo"}, | ||
User{User: "foo", Password: "$2a$10$aUPOdbOGNawaVSusg3g2wuC3AH6XxIr9/Ms4VgDvzrAVOJPYzZILa"}, | ||
User{User: "foo", Roles: []string{}, Password: "$2a$10$aUPOdbOGNawaVSusg3g2wuC3AH6XxIr9/Ms4VgDvzrAVOJPYzZILa"}, | ||
{ // empty password will not overwrite the previous 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.
@xiang90 Just added the case to test empty password will not overwrite the previous password
. PTAL. Thanks!
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 do not think this is correct. the input password is not empty. it is foo
.
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.
This test merges tt.input
with tt.merge
. So if we want to test empty password will not overwrite the previous password
, the previous password must come from the one that gets overwritten, which is tt.input.User
?
tt.input
is the one that would get overwritten with previous implementation. Please correct me if I am wrong.
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.
tt.input is the one actually overwrites the correct one. Say a user has a password: AAA. We want to update its role only. So the input user has empty password but a role, the password should remain AAA, not overwrite to empty.
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 see what you are say... the naming in the test is really confusing.
LGTM |
Thanks! merging after all greens. |
Fix #5182.