Skip to content

Commit 6ec39f6

Browse files
ramneslance6716
andauthored
Send using password: NO when no password is used (#1078)
* Send `using password: NO` when no password is used * Error should be decided against client data, not server password * Add test * Make sure to send `using password: NO` in auth switches as well --------- Co-authored-by: lance6716 <[email protected]>
1 parent 7768ced commit 6ec39f6

File tree

4 files changed

+185
-14
lines changed

4 files changed

+185
-14
lines changed

server/auth.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,6 @@ func (c *Conn) acquirePassword() error {
4545
return nil
4646
}
4747

48-
func errAccessDenied(credential Credential) error {
49-
if credential.Password == "" {
50-
return ErrAccessDeniedNoPassword
51-
}
52-
53-
return ErrAccessDenied
54-
}
55-
5648
func scrambleValidation(cached, nonce, scramble []byte) bool {
5749
// SHA256(SHA256(SHA256(STORED_PASSWORD)), NONCE)
5850
crypt := sha256.New()
@@ -74,14 +66,21 @@ func scrambleValidation(cached, nonce, scramble []byte) bool {
7466
}
7567

7668
func (c *Conn) compareNativePasswordAuthData(clientAuthData []byte, credential Credential) error {
69+
if len(clientAuthData) == 0 {
70+
if credential.Password == "" {
71+
return nil
72+
}
73+
return ErrAccessDeniedNoPassword
74+
}
75+
7776
password, err := mysql.DecodePasswordHex(c.credential.Password)
7877
if err != nil {
79-
return errAccessDenied(credential)
78+
return ErrAccessDenied
8079
}
8180
if mysql.CompareNativePassword(clientAuthData, password, c.salt) {
8281
return nil
8382
}
84-
return errAccessDenied(credential)
83+
return ErrAccessDenied
8584
}
8685

8786
func (c *Conn) compareSha256PasswordAuthData(clientAuthData []byte, credential Credential) error {
@@ -90,7 +89,7 @@ func (c *Conn) compareSha256PasswordAuthData(clientAuthData []byte, credential C
9089
if credential.Password == "" {
9190
return nil
9291
}
93-
return ErrAccessDenied
92+
return ErrAccessDeniedNoPassword
9493
}
9594
if tlsConn, ok := c.Conn.Conn.(*tls.Conn); ok {
9695
if !tlsConn.ConnectionState().HandshakeComplete {
@@ -129,7 +128,7 @@ func (c *Conn) compareCacheSha2PasswordAuthData(clientAuthData []byte) error {
129128
if c.credential.Password == "" {
130129
return nil
131130
}
132-
return ErrAccessDenied
131+
return ErrAccessDeniedNoPassword
133132
}
134133
// the caching of 'caching_sha2_password' in MySQL, see: https://dev.mysql.com/worklog/task/?id=9591
135134
// check if we have a cached value
@@ -141,7 +140,7 @@ func (c *Conn) compareCacheSha2PasswordAuthData(clientAuthData []byte) error {
141140
return c.writeAuthMoreDataFastAuth()
142141
}
143142

144-
return errAccessDenied(c.credential)
143+
return ErrAccessDenied
145144
}
146145
// cache miss, do full auth
147146
if err := c.writeAuthMoreDataFullAuth(); err != nil {

server/auth_switch_response.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,18 @@ func (c *Conn) handleCachingSha2PasswordFullAuth(authData []byte) error {
7171
}
7272

7373
func (c *Conn) checkSha2CacheCredentials(clientAuthData []byte, credential Credential) error {
74+
if len(clientAuthData) == 0 {
75+
if credential.Password == "" {
76+
return nil
77+
}
78+
return ErrAccessDeniedNoPassword
79+
}
80+
7481
match, err := auth.CheckHashingPassword([]byte(credential.Password), string(clientAuthData), mysql.AUTH_CACHING_SHA2_PASSWORD)
7582
if match && err == nil {
7683
return nil
7784
}
78-
return errAccessDenied(credential)
85+
return ErrAccessDenied
7986
}
8087

8188
func (c *Conn) writeCachingSha2Cache(authData []byte) {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package server
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestCheckSha2CacheCredentials_EmptyPassword(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
clientAuthData []byte
13+
serverPassword string
14+
wantErr error
15+
}{
16+
{
17+
name: "empty client auth, empty server password",
18+
clientAuthData: []byte{},
19+
serverPassword: "",
20+
wantErr: nil,
21+
},
22+
{
23+
name: "empty client auth, non-empty server password",
24+
clientAuthData: []byte{},
25+
serverPassword: "secret",
26+
wantErr: ErrAccessDeniedNoPassword,
27+
},
28+
}
29+
30+
for _, tt := range tests {
31+
t.Run(tt.name, func(t *testing.T) {
32+
c := &Conn{
33+
credential: Credential{Password: tt.serverPassword},
34+
}
35+
err := c.checkSha2CacheCredentials(tt.clientAuthData, c.credential)
36+
if tt.wantErr == nil {
37+
require.NoError(t, err)
38+
} else {
39+
require.ErrorIs(t, err, tt.wantErr)
40+
}
41+
})
42+
}
43+
}

server/auth_test.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package server
2+
3+
import (
4+
"errors"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestErrAccessDenied(t *testing.T) {
11+
require.True(t, errors.Is(ErrAccessDenied, ErrAccessDenied))
12+
require.True(t, errors.Is(ErrAccessDeniedNoPassword, ErrAccessDenied))
13+
require.False(t, errors.Is(ErrAccessDenied, ErrAccessDeniedNoPassword))
14+
}
15+
16+
func TestCompareNativePasswordAuthData_EmptyPassword(t *testing.T) {
17+
tests := []struct {
18+
name string
19+
clientAuthData []byte
20+
serverPassword string
21+
wantErr error
22+
}{
23+
{
24+
name: "empty client auth, empty server password",
25+
clientAuthData: []byte{},
26+
serverPassword: "",
27+
wantErr: nil,
28+
},
29+
{
30+
name: "empty client auth, non-empty server password",
31+
clientAuthData: []byte{},
32+
serverPassword: "secret",
33+
wantErr: ErrAccessDeniedNoPassword,
34+
},
35+
}
36+
37+
for _, tt := range tests {
38+
t.Run(tt.name, func(t *testing.T) {
39+
c := &Conn{
40+
credential: Credential{Password: tt.serverPassword},
41+
}
42+
err := c.compareNativePasswordAuthData(tt.clientAuthData, c.credential)
43+
if tt.wantErr == nil {
44+
require.NoError(t, err)
45+
} else {
46+
require.ErrorIs(t, err, tt.wantErr)
47+
}
48+
})
49+
}
50+
}
51+
52+
func TestCompareSha256PasswordAuthData_EmptyPassword(t *testing.T) {
53+
tests := []struct {
54+
name string
55+
clientAuthData []byte
56+
serverPassword string
57+
wantErr error
58+
}{
59+
{
60+
name: "empty client auth, empty server password",
61+
clientAuthData: []byte{},
62+
serverPassword: "",
63+
wantErr: nil,
64+
},
65+
{
66+
name: "empty client auth, non-empty server password",
67+
clientAuthData: []byte{},
68+
serverPassword: "secret",
69+
wantErr: ErrAccessDeniedNoPassword,
70+
},
71+
}
72+
73+
for _, tt := range tests {
74+
t.Run(tt.name, func(t *testing.T) {
75+
c := &Conn{
76+
credential: Credential{Password: tt.serverPassword},
77+
}
78+
err := c.compareSha256PasswordAuthData(tt.clientAuthData, c.credential)
79+
if tt.wantErr == nil {
80+
require.NoError(t, err)
81+
} else {
82+
require.ErrorIs(t, err, tt.wantErr)
83+
}
84+
})
85+
}
86+
}
87+
88+
func TestCompareCacheSha2PasswordAuthData_EmptyPassword(t *testing.T) {
89+
tests := []struct {
90+
name string
91+
clientAuthData []byte
92+
serverPassword string
93+
wantErr error
94+
}{
95+
{
96+
name: "empty client auth, empty server password",
97+
clientAuthData: []byte{},
98+
serverPassword: "",
99+
wantErr: nil,
100+
},
101+
{
102+
name: "empty client auth, non-empty server password",
103+
clientAuthData: []byte{},
104+
serverPassword: "secret",
105+
wantErr: ErrAccessDeniedNoPassword,
106+
},
107+
}
108+
109+
for _, tt := range tests {
110+
t.Run(tt.name, func(t *testing.T) {
111+
c := &Conn{
112+
credential: Credential{Password: tt.serverPassword},
113+
}
114+
err := c.compareCacheSha2PasswordAuthData(tt.clientAuthData)
115+
if tt.wantErr == nil {
116+
require.NoError(t, err)
117+
} else {
118+
require.ErrorIs(t, err, tt.wantErr)
119+
}
120+
})
121+
}
122+
}

0 commit comments

Comments
 (0)