Skip to content

Commit 45651f0

Browse files
committed
Handle empty password from broader set of MySQL clients
Some MySQL clients (e.g. libmysql) send a single null byte to indicate an empty password, while others (e.g. mariadb) send an empty packet. This matches MySQL server's own handling: ```c if (!pkt_len || (pkt_len == 1 && *pkt == 0)) ``` (Source: https://github.com/mysql/mysql-server/blob/8.0/sql/auth/sha2_password.cc)
1 parent a1483a5 commit 45651f0

File tree

4 files changed

+62
-6
lines changed

4 files changed

+62
-6
lines changed

server/auth.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ var (
1818
ErrAccessDeniedNoPassword = fmt.Errorf("%w without password", ErrAccessDenied)
1919
)
2020

21+
// isEmptyPassword returns true if the auth data represents an empty password.
22+
// Some clients send an empty packet (len == 0), while others (e.g. MySQL's libmysql)
23+
// send a single null byte. This matches MySQL server's own handling:
24+
// if (!pkt_len || (pkt_len == 1 && *pkt == 0))
25+
// https://github.com/mysql/mysql-server/blob/8.0/sql/auth/sha2_password.cc
26+
// https://github.com/mysql/mysql-server/blob/8.0/sql/auth/sql_authentication.cc
27+
func isEmptyPassword(authData []byte) bool {
28+
return len(authData) == 0 || (len(authData) == 1 && authData[0] == 0)
29+
}
30+
2131
func (c *Conn) compareAuthData(authPluginName string, clientAuthData []byte) error {
2232
if authPluginName != c.credential.AuthPluginName {
2333
err := c.writeAuthSwitchRequest(c.credential.AuthPluginName)
@@ -66,7 +76,7 @@ func scrambleValidation(cached, nonce, scramble []byte) bool {
6676
}
6777

6878
func (c *Conn) compareNativePasswordAuthData(clientAuthData []byte, credential Credential) error {
69-
if len(clientAuthData) == 0 {
79+
if isEmptyPassword(clientAuthData) {
7080
if credential.hasEmptyPassword() {
7181
return nil
7282
}
@@ -90,8 +100,7 @@ func (c *Conn) compareNativePasswordAuthData(clientAuthData []byte, credential C
90100
}
91101

92102
func (c *Conn) compareSha256PasswordAuthData(clientAuthData []byte, credential Credential) error {
93-
// Empty passwords are not hashed, but sent as empty string
94-
if len(clientAuthData) == 0 {
103+
if isEmptyPassword(clientAuthData) {
95104
if credential.hasEmptyPassword() {
96105
return nil
97106
}
@@ -135,8 +144,7 @@ func (c *Conn) compareSha256PasswordAuthData(clientAuthData []byte, credential C
135144
}
136145

137146
func (c *Conn) compareCacheSha2PasswordAuthData(clientAuthData []byte) error {
138-
// Empty passwords are not hashed, but sent as empty string
139-
if len(clientAuthData) == 0 {
147+
if isEmptyPassword(clientAuthData) {
140148
if c.credential.hasEmptyPassword() {
141149
return nil
142150
}

server/auth_switch_response.go

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

7373
func (c *Conn) checkSha2CacheCredentials(clientAuthData []byte, credential Credential) error {
74-
if len(clientAuthData) == 0 {
74+
if isEmptyPassword(clientAuthData) {
7575
if credential.hasEmptyPassword() {
7676
return nil
7777
}

server/auth_switch_response_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ func TestCheckSha2CacheCredentials_EmptyPassword(t *testing.T) {
2525
serverPassword: "secret",
2626
wantErr: ErrAccessDeniedNoPassword,
2727
},
28+
{
29+
name: "null byte client auth, empty server password",
30+
clientAuthData: []byte{0x00},
31+
serverPassword: "",
32+
wantErr: nil,
33+
},
34+
{
35+
name: "null byte client auth, non-empty server password",
36+
clientAuthData: []byte{0x00},
37+
serverPassword: "secret",
38+
wantErr: ErrAccessDeniedNoPassword,
39+
},
2840
}
2941

3042
for _, tt := range tests {

server/auth_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ func TestCompareNativePasswordAuthData_EmptyPassword(t *testing.T) {
3232
serverPassword: "secret",
3333
wantErr: ErrAccessDeniedNoPassword,
3434
},
35+
{
36+
name: "null byte client auth, empty server password",
37+
clientAuthData: []byte{0x00},
38+
serverPassword: "",
39+
wantErr: nil,
40+
},
41+
{
42+
name: "null byte client auth, non-empty server password",
43+
clientAuthData: []byte{0x00},
44+
serverPassword: "secret",
45+
wantErr: ErrAccessDeniedNoPassword,
46+
},
3547
}
3648

3749
for _, tt := range tests {
@@ -68,6 +80,18 @@ func TestCompareSha256PasswordAuthData_EmptyPassword(t *testing.T) {
6880
serverPassword: "secret",
6981
wantErr: ErrAccessDeniedNoPassword,
7082
},
83+
{
84+
name: "null byte client auth, empty server password",
85+
clientAuthData: []byte{0x00},
86+
serverPassword: "",
87+
wantErr: nil,
88+
},
89+
{
90+
name: "null byte client auth, non-empty server password",
91+
clientAuthData: []byte{0x00},
92+
serverPassword: "secret",
93+
wantErr: ErrAccessDeniedNoPassword,
94+
},
7195
}
7296

7397
for _, tt := range tests {
@@ -104,6 +128,18 @@ func TestCompareCacheSha2PasswordAuthData_EmptyPassword(t *testing.T) {
104128
serverPassword: "secret",
105129
wantErr: ErrAccessDeniedNoPassword,
106130
},
131+
{
132+
name: "null byte client auth, empty server password",
133+
clientAuthData: []byte{0x00},
134+
serverPassword: "",
135+
wantErr: nil,
136+
},
137+
{
138+
name: "null byte client auth, non-empty server password",
139+
clientAuthData: []byte{0x00},
140+
serverPassword: "secret",
141+
wantErr: ErrAccessDeniedNoPassword,
142+
},
107143
}
108144

109145
for _, tt := range tests {

0 commit comments

Comments
 (0)