Skip to content

Commit 0dcbff2

Browse files
committed
auth: sync NUL termination with official connectors
1 parent caae030 commit 0dcbff2

File tree

4 files changed

+72
-47
lines changed

4 files changed

+72
-47
lines changed

auth.go

+18-15
Original file line numberDiff line numberDiff line change
@@ -154,39 +154,42 @@ func scrambleSHA256Password(scramble []byte, password string) []byte {
154154
return message1
155155
}
156156

157-
func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
157+
func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, bool, error) {
158158
switch plugin {
159159
case "caching_sha2_password":
160-
return scrambleSHA256Password(authData, mc.cfg.Passwd), nil
160+
authResp := scrambleSHA256Password(authData, mc.cfg.Passwd)
161+
return authResp, (authResp == nil), nil
161162

162163
case "mysql_old_password":
163164
if !mc.cfg.AllowOldPasswords {
164-
return nil, ErrOldPassword
165+
return nil, false, ErrOldPassword
165166
}
166167
// Note: there are edge cases where this should work but doesn't;
167168
// this is currently "wontfix":
168169
// https://github.com/go-sql-driver/mysql/issues/184
169-
return scrambleOldPassword(authData[:8], mc.cfg.Passwd), nil
170+
authResp := scrambleOldPassword(authData[:8], mc.cfg.Passwd)
171+
return authResp, true, nil
170172

171173
case "mysql_clear_password":
172174
if !mc.cfg.AllowCleartextPasswords {
173-
return nil, ErrCleartextPassword
175+
return nil, false, ErrCleartextPassword
174176
}
175177
// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
176178
// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
177-
return []byte(mc.cfg.Passwd), nil
179+
return []byte(mc.cfg.Passwd), true, nil
178180

179181
case "mysql_native_password":
180182
if !mc.cfg.AllowNativePasswords {
181-
return nil, ErrNativePassword
183+
return nil, false, ErrNativePassword
182184
}
183185
// https://dev.mysql.com/doc/internals/en/secure-password-authentication.html
184186
// Native password authentication only need and will need 20-byte challenge.
185-
return scramblePassword(authData[0:20], mc.cfg.Passwd), nil
187+
authResp := scramblePassword(authData[:20], mc.cfg.Passwd)
188+
return authResp, (authResp == nil), nil
186189

187190
default:
188191
errLog.Print("unknown auth plugin:", plugin)
189-
return nil, ErrUnknownPlugin
192+
return nil, false, ErrUnknownPlugin
190193
}
191194
}
192195

@@ -207,11 +210,11 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
207210

208211
plugin = newPlugin
209212

210-
authResp, err := mc.auth(authData, plugin)
213+
authResp, addNUL, err := mc.auth(authData, plugin)
211214
if err != nil {
212215
return err
213216
}
214-
if err = mc.writeAuthSwitchPacket(authResp); err != nil {
217+
if err = mc.writeAuthSwitchPacket(authResp, addNUL); err != nil {
215218
return err
216219
}
217220

@@ -242,9 +245,9 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
242245

243246
case cachingSha2PasswordPerformFullAuthentication:
244247
if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
245-
// write cleartext auth packet
246-
authData := []byte(mc.cfg.Passwd)
247-
if err = mc.writeAuthSwitchPacket(authData); err != nil {
248+
// write cleartext auth packets
249+
err = mc.writeAuthSwitchPacket([]byte(mc.cfg.Passwd), true)
250+
if err != nil {
248251
return err
249252
}
250253
} else {
@@ -283,7 +286,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
283286
return err
284287
}
285288

286-
if err = mc.writeAuthSwitchPacket(enc); err != nil {
289+
if err = mc.writeAuthSwitchPacket(enc, false); err != nil {
287290
return err
288291
}
289292
}

auth_test.go

+34-26
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,12 @@ func TestAuthFastCachingSHA256PasswordCached(t *testing.T) {
8989
plugin := "caching_sha2_password"
9090

9191
// Send Client Authentication Packet
92-
authResp, err := mc.auth(authData, plugin)
92+
authResp, addNUL, err := mc.auth(authData, plugin)
9393
if err != nil {
9494
t.Fatal(err)
9595
}
96-
if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
96+
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
97+
if err != nil {
9798
t.Fatal(err)
9899
}
99100

@@ -133,11 +134,12 @@ func TestAuthFastCachingSHA256PasswordEmpty(t *testing.T) {
133134
plugin := "caching_sha2_password"
134135

135136
// Send Client Authentication Packet
136-
authResp, err := mc.auth(authData, plugin)
137+
authResp, addNUL, err := mc.auth(authData, plugin)
137138
if err != nil {
138139
t.Fatal(err)
139140
}
140-
if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
141+
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
142+
if err != nil {
141143
t.Fatal(err)
142144
}
143145

@@ -174,11 +176,12 @@ func TestAuthFastCachingSHA256PasswordFullRSA(t *testing.T) {
174176
plugin := "caching_sha2_password"
175177

176178
// Send Client Authentication Packet
177-
authResp, err := mc.auth(authData, plugin)
179+
authResp, addNUL, err := mc.auth(authData, plugin)
178180
if err != nil {
179181
t.Fatal(err)
180182
}
181-
if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
183+
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
184+
if err != nil {
182185
t.Fatal(err)
183186
}
184187

@@ -228,11 +231,12 @@ func TestAuthFastCachingSHA256PasswordFullSecure(t *testing.T) {
228231
plugin := "caching_sha2_password"
229232

230233
// Send Client Authentication Packet
231-
authResp, err := mc.auth(authData, plugin)
234+
authResp, addNUL, err := mc.auth(authData, plugin)
232235
if err != nil {
233236
t.Fatal(err)
234237
}
235-
if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
238+
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
239+
if err != nil {
236240
t.Fatal(err)
237241
}
238242

@@ -268,7 +272,7 @@ func TestAuthFastCachingSHA256PasswordFullSecure(t *testing.T) {
268272
t.Errorf("got error: %v", err)
269273
}
270274

271-
if !bytes.Equal(conn.written, []byte{6, 0, 0, 3, 115, 101, 99, 114, 101, 116}) {
275+
if !bytes.Equal(conn.written, []byte{7, 0, 0, 3, 115, 101, 99, 114, 101, 116, 0}) {
272276
t.Errorf("unexpected written data: %v", conn.written)
273277
}
274278
}
@@ -283,7 +287,7 @@ func TestAuthFastCleartextPasswordNotAllowed(t *testing.T) {
283287
plugin := "mysql_clear_password"
284288

285289
// Send Client Authentication Packet
286-
_, err := mc.auth(authData, plugin)
290+
_, _, err := mc.auth(authData, plugin)
287291
if err != ErrCleartextPassword {
288292
t.Errorf("expected ErrCleartextPassword, got %v", err)
289293
}
@@ -300,11 +304,12 @@ func TestAuthFastCleartextPassword(t *testing.T) {
300304
plugin := "mysql_clear_password"
301305

302306
// Send Client Authentication Packet
303-
authResp, err := mc.auth(authData, plugin)
307+
authResp, addNUL, err := mc.auth(authData, plugin)
304308
if err != nil {
305309
t.Fatal(err)
306310
}
307-
if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
311+
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
312+
if err != nil {
308313
t.Fatal(err)
309314
}
310315

@@ -342,11 +347,12 @@ func TestAuthFastCleartextPasswordEmpty(t *testing.T) {
342347
plugin := "mysql_clear_password"
343348

344349
// Send Client Authentication Packet
345-
authResp, err := mc.auth(authData, plugin)
350+
authResp, addNUL, err := mc.auth(authData, plugin)
346351
if err != nil {
347352
t.Fatal(err)
348353
}
349-
if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
354+
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
355+
if err != nil {
350356
t.Fatal(err)
351357
}
352358

@@ -384,7 +390,7 @@ func TestAuthFastNativePasswordNotAllowed(t *testing.T) {
384390
plugin := "mysql_native_password"
385391

386392
// Send Client Authentication Packet
387-
_, err := mc.auth(authData, plugin)
393+
_, _, err := mc.auth(authData, plugin)
388394
if err != ErrNativePassword {
389395
t.Errorf("expected ErrNativePassword, got %v", err)
390396
}
@@ -400,11 +406,12 @@ func TestAuthFastNativePassword(t *testing.T) {
400406
plugin := "mysql_native_password"
401407

402408
// Send Client Authentication Packet
403-
authResp, err := mc.auth(authData, plugin)
409+
authResp, addNUL, err := mc.auth(authData, plugin)
404410
if err != nil {
405411
t.Fatal(err)
406412
}
407-
if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
413+
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
414+
if err != nil {
408415
t.Fatal(err)
409416
}
410417

@@ -442,11 +449,12 @@ func TestAuthFastNativePasswordEmpty(t *testing.T) {
442449
plugin := "mysql_native_password"
443450

444451
// Send Client Authentication Packet
445-
authResp, err := mc.auth(authData, plugin)
452+
authResp, addNUL, err := mc.auth(authData, plugin)
446453
if err != nil {
447454
t.Fatal(err)
448455
}
449-
if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
456+
err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin)
457+
if err != nil {
450458
t.Fatal(err)
451459
}
452460

@@ -530,7 +538,7 @@ func TestAuthSwitchCachingSHA256PasswordEmpty(t *testing.T) {
530538
t.Errorf("got error: %v", err)
531539
}
532540

533-
expectedReply := []byte{0, 0, 0, 3}
541+
expectedReply := []byte{1, 0, 0, 3, 0}
534542
if !bytes.Equal(conn.written, expectedReply) {
535543
t.Errorf("got unexpected data: %v", conn.written)
536544
}
@@ -619,7 +627,7 @@ func TestAuthSwitchCachingSHA256PasswordFullSecure(t *testing.T) {
619627
153, 9, 130,
620628

621629
// 2. Packet: Cleartext password
622-
6, 0, 0, 5, 115, 101, 99, 114, 101, 116,
630+
7, 0, 0, 5, 115, 101, 99, 114, 101, 116, 0,
623631
}
624632
if !bytes.Equal(conn.written, expectedReply) {
625633
t.Errorf("got unexpected data: %v", conn.written)
@@ -662,7 +670,7 @@ func TestAuthSwitchCleartextPassword(t *testing.T) {
662670
t.Errorf("got error: %v", err)
663671
}
664672

665-
expectedReply := []byte{6, 0, 0, 3, 115, 101, 99, 114, 101, 116}
673+
expectedReply := []byte{7, 0, 0, 3, 115, 101, 99, 114, 101, 116, 0}
666674
if !bytes.Equal(conn.written, expectedReply) {
667675
t.Errorf("got unexpected data: %v", conn.written)
668676
}
@@ -689,7 +697,7 @@ func TestAuthSwitchCleartextPasswordEmpty(t *testing.T) {
689697
t.Errorf("got error: %v", err)
690698
}
691699

692-
expectedReply := []byte{0, 0, 0, 3}
700+
expectedReply := []byte{1, 0, 0, 3, 0}
693701
if !bytes.Equal(conn.written, expectedReply) {
694702
t.Errorf("got unexpected data: %v", conn.written)
695703
}
@@ -766,7 +774,7 @@ func TestAuthSwitchNativePasswordEmpty(t *testing.T) {
766774
t.Errorf("got error: %v", err)
767775
}
768776

769-
expectedReply := []byte{0, 0, 0, 3}
777+
expectedReply := []byte{1, 0, 0, 3, 0}
770778
if !bytes.Equal(conn.written, expectedReply) {
771779
t.Errorf("got unexpected data: %v", conn.written)
772780
}
@@ -810,7 +818,7 @@ func TestAuthSwitchOldPassword(t *testing.T) {
810818
t.Errorf("got error: %v", err)
811819
}
812820

813-
expectedReply := []byte{8, 0, 0, 3, 86, 83, 83, 79, 74, 78, 65, 66}
821+
expectedReply := []byte{9, 0, 0, 3, 86, 83, 83, 79, 74, 78, 65, 66, 0}
814822
if !bytes.Equal(conn.written, expectedReply) {
815823
t.Errorf("got unexpected data: %v", conn.written)
816824
}
@@ -838,7 +846,7 @@ func TestAuthSwitchOldPasswordEmpty(t *testing.T) {
838846
t.Errorf("got error: %v", err)
839847
}
840848

841-
expectedReply := []byte{0, 0, 0, 3}
849+
expectedReply := []byte{1, 0, 0, 3, 0}
842850
if !bytes.Equal(conn.written, expectedReply) {
843851
t.Errorf("got unexpected data: %v", conn.written)
844852
}

driver.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -114,18 +114,18 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
114114
}
115115

116116
// Send Client Authentication Packet
117-
authResp, err := mc.auth(authData, plugin)
117+
authResp, addNUL, err := mc.auth(authData, plugin)
118118
if err != nil {
119119
// try the default auth plugin, if using the requested plugin failed
120120
errLog.Print("could not use requested auth plugin '"+plugin+"': ", err.Error())
121121
plugin = defaultAuthPlugin
122-
authResp, err = mc.auth(authData, plugin)
122+
authResp, addNUL, err = mc.auth(authData, plugin)
123123
if err != nil {
124124
mc.cleanup()
125125
return nil, err
126126
}
127127
}
128-
if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
128+
if err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin); err != nil {
129129
mc.cleanup()
130130
return nil, err
131131
}

packets.go

+17-3
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ func (mc *mysqlConn) readHandshakePacket() ([]byte, string, error) {
246246

247247
// Client Authentication Packet
248248
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse
249-
func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string) error {
249+
func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool, plugin string) error {
250250
// Adjust client flags based on server support
251251
clientFlags := clientProtocol41 |
252252
clientSecureConn |
@@ -271,6 +271,9 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
271271
}
272272

273273
pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + 1 + len(authResp) + 21 + 1
274+
if addNUL {
275+
pktLen++
276+
}
274277

275278
// To specify a db name
276279
if n := len(mc.cfg.DBName); n > 0 {
@@ -341,6 +344,10 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
341344
// Auth Data [length encoded integer]
342345
data[pos] = byte(len(authResp))
343346
pos += 1 + copy(data[pos+1:], authResp)
347+
if addNUL {
348+
data[pos] = 0x00
349+
pos++
350+
}
344351

345352
// Databasename [null terminated string]
346353
if len(mc.cfg.DBName) > 0 {
@@ -357,8 +364,12 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
357364
}
358365

359366
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
360-
func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte) error {
361-
data := mc.buf.takeSmallBuffer(4 + len(authData))
367+
func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte, addNUL bool) error {
368+
pktLen := 4 + len(authData)
369+
if addNUL {
370+
pktLen++
371+
}
372+
data := mc.buf.takeSmallBuffer(pktLen)
362373
if data == nil {
363374
// cannot take the buffer. Something must be wrong with the connection
364375
errLog.Print(ErrBusyBuffer)
@@ -367,6 +378,9 @@ func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte) error {
367378

368379
// Add the auth data [EOF]
369380
copy(data[4:], authData)
381+
if addNUL {
382+
data[pktLen-1] = 0x00
383+
}
370384

371385
return mc.writePacket(data)
372386
}

0 commit comments

Comments
 (0)