Skip to content

Commit 4bcaa02

Browse files
committed
Fix empty SHA2 password handling
Fixes #799
1 parent d03e4c2 commit 4bcaa02

File tree

3 files changed

+106
-68
lines changed

3 files changed

+106
-68
lines changed

const.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ const (
1919
// http://dev.mysql.com/doc/internals/en/client-server-protocol.html
2020

2121
const (
22-
iOK byte = 0x00
23-
iLocalInFile byte = 0xfb
24-
iEOF byte = 0xfe
25-
iERR byte = 0xff
22+
iOK byte = 0x00
23+
iAuthMoreData byte = 0x01
24+
iLocalInFile byte = 0xfb
25+
iEOF byte = 0xfe
26+
iERR byte = 0xff
2627
)
2728

2829
// https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags
@@ -166,6 +167,7 @@ const (
166167
)
167168

168169
const (
170+
cachingSha2PasswordOK = 0
169171
cachingSha2PasswordRequestPublicKey = 2
170172
cachingSha2PasswordFastAuthSuccess = 3
171173
cachingSha2PasswordPerformFullAuthentication = 4

driver.go

+48-26
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,15 @@ func handleAuthResult(mc *mysqlConn, oldCipher []byte, pluginName string) error
161161
if err != nil {
162162
return err
163163
}
164-
if auth == cachingSha2PasswordPerformFullAuthentication {
164+
165+
switch auth {
166+
case cachingSha2PasswordOK:
167+
return nil // auth successful
168+
169+
case cachingSha2PasswordFastAuthSuccess:
170+
// continue to read result packet...
171+
172+
case cachingSha2PasswordPerformFullAuthentication:
165173
if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
166174
if err = mc.writeClearAuthPacket(); err != nil {
167175
return err
@@ -171,6 +179,10 @@ func handleAuthResult(mc *mysqlConn, oldCipher []byte, pluginName string) error
171179
return err
172180
}
173181
}
182+
// continue to read result packet...
183+
184+
default:
185+
return ErrMalformPkt
174186
}
175187
}
176188

@@ -184,36 +196,46 @@ func handleAuthResult(mc *mysqlConn, oldCipher []byte, pluginName string) error
184196
return err // auth failed and retry not possible
185197
}
186198

187-
// Retry auth if configured to do so.
188-
if mc.cfg.AllowOldPasswords && err == ErrOldPassword {
189-
// Retry with old authentication method. Note: there are edge cases
190-
// where this should work but doesn't; this is currently "wontfix":
191-
// https://github.com/go-sql-driver/mysql/issues/184
192-
193-
// If CLIENT_PLUGIN_AUTH capability is not supported, no new cipher is
194-
// sent and we have to keep using the cipher sent in the init packet.
195-
if cipher == nil {
196-
cipher = oldCipher
199+
// Retry auth if configured to do so
200+
switch err {
201+
case ErrCleartextPassword:
202+
if mc.cfg.AllowCleartextPasswords {
203+
// Retry with clear text password for
204+
// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
205+
// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
206+
if err = mc.writeClearAuthPacket(); err != nil {
207+
return err
208+
}
209+
_, err = mc.readResultOK()
197210
}
198211

199-
if err = mc.writeOldAuthPacket(cipher); err != nil {
200-
return err
201-
}
202-
_, err = mc.readResultOK()
203-
} else if mc.cfg.AllowCleartextPasswords && err == ErrCleartextPassword {
204-
// Retry with clear text password for
205-
// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
206-
// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
207-
if err = mc.writeClearAuthPacket(); err != nil {
208-
return err
212+
case ErrNativePassword:
213+
if mc.cfg.AllowNativePasswords {
214+
if err = mc.writeNativeAuthPacket(cipher); err != nil {
215+
return err
216+
}
217+
_, err = mc.readResultOK()
209218
}
210-
_, err = mc.readResultOK()
211-
} else if mc.cfg.AllowNativePasswords && err == ErrNativePassword {
212-
if err = mc.writeNativeAuthPacket(cipher); err != nil {
213-
return err
219+
220+
case ErrOldPassword:
221+
if mc.cfg.AllowOldPasswords {
222+
// Retry with old authentication method. Note: there are edge cases
223+
// where this should work but doesn't; this is currently "wontfix":
224+
// https://github.com/go-sql-driver/mysql/issues/184
225+
226+
// If CLIENT_PLUGIN_AUTH capability is not supported, no new cipher is
227+
// sent and we have to keep using the cipher sent in the init packet.
228+
if cipher == nil {
229+
cipher = oldCipher
230+
}
231+
232+
if err = mc.writeOldAuthPacket(cipher); err != nil {
233+
return err
234+
}
235+
_, err = mc.readResultOK()
214236
}
215-
_, err = mc.readResultOK()
216237
}
238+
217239
return err
218240
}
219241

packets.go

+52-38
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ func (mc *mysqlConn) readInitPacket() ([]byte, string, error) {
203203
}
204204
pos += 2
205205

206-
pluginName := ""
206+
pluginName := "mysql_native_password"
207207
if len(data) > pos {
208208
// character set [1 byte]
209209
// status flags [2 bytes]
@@ -367,7 +367,6 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte, pluginName string) error {
367367
pos++
368368
}
369369

370-
// Assume native client during response
371370
pos += copy(data[pos:], pluginName)
372371
data[pos] = 0x00
373372

@@ -544,55 +543,70 @@ func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error {
544543
* Result Packets *
545544
******************************************************************************/
546545

546+
func readAuthSwitch(data []byte) ([]byte, error) {
547+
if len(data) > 1 {
548+
pluginEndIndex := bytes.IndexByte(data, 0x00)
549+
plugin := string(data[1:pluginEndIndex])
550+
cipher := data[pluginEndIndex+1:]
551+
552+
switch plugin {
553+
case "mysql_old_password":
554+
// using old_passwords
555+
return cipher, ErrOldPassword
556+
case "mysql_clear_password":
557+
// using clear text password
558+
return cipher, ErrCleartextPassword
559+
case "mysql_native_password":
560+
// using mysql default authentication method
561+
return cipher, ErrNativePassword
562+
default:
563+
return cipher, ErrUnknownPlugin
564+
}
565+
}
566+
567+
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
568+
return nil, ErrOldPassword
569+
}
570+
547571
// Returns error if Packet is not an 'Result OK'-Packet
548572
func (mc *mysqlConn) readResultOK() ([]byte, error) {
549573
data, err := mc.readPacket()
550-
if err == nil {
551-
// packet indicator
552-
switch data[0] {
574+
if err != nil {
575+
return nil, err
576+
}
553577

554-
case iOK:
555-
return nil, mc.handleOkPacket(data)
578+
// packet indicator
579+
switch data[0] {
556580

557-
case iEOF:
558-
if len(data) > 1 {
559-
pluginEndIndex := bytes.IndexByte(data, 0x00)
560-
plugin := string(data[1:pluginEndIndex])
561-
cipher := data[pluginEndIndex+1:]
562-
563-
switch plugin {
564-
case "mysql_old_password":
565-
// using old_passwords
566-
return cipher, ErrOldPassword
567-
case "mysql_clear_password":
568-
// using clear text password
569-
return cipher, ErrCleartextPassword
570-
case "mysql_native_password":
571-
// using mysql default authentication method
572-
return cipher, ErrNativePassword
573-
default:
574-
return cipher, ErrUnknownPlugin
575-
}
576-
}
581+
case iOK:
582+
return nil, mc.handleOkPacket(data)
577583

578-
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
579-
return nil, ErrOldPassword
584+
case iEOF:
585+
return readAuthSwitch(data)
580586

581-
default: // Error otherwise
582-
return nil, mc.handleErrorPacket(data)
583-
}
587+
default: // Error otherwise
588+
return nil, mc.handleErrorPacket(data)
584589
}
585-
return nil, err
586590
}
587591

592+
// https://insidemysql.com/preparing-your-community-connector-for-mysql-8-part-2-sha256/
588593
func (mc *mysqlConn) readCachingSha2PasswordAuthResult() (int, error) {
589594
data, err := mc.readPacket()
590-
if err == nil {
591-
if data[0] != 1 {
592-
return 0, ErrMalformPkt
593-
}
595+
if err != nil {
596+
return 0, err
597+
}
598+
599+
// packet indicator
600+
switch data[0] {
601+
case iOK:
602+
return 0, nil
603+
604+
case iAuthMoreData:
605+
return int(data[1]), nil
606+
607+
default: // Error otherwise
608+
return 0, mc.handleErrorPacket(data)
594609
}
595-
return int(data[1]), err
596610
}
597611

598612
// Result Set Header Packet

0 commit comments

Comments
 (0)