Skip to content

Commit 0f6bfaf

Browse files
author
unknown
committed
Support authentication switch with mysql_native_password authentication. Besides, fix bug that cipher needs to refreshed from authentication switch request packet from server.
1 parent dbc3fe2 commit 0f6bfaf

File tree

7 files changed

+66
-20
lines changed

7 files changed

+66
-20
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,15 @@ Default: false
144144
```
145145
`allowOldPasswords=true` allows the usage of the insecure old password method. This should be avoided, but is necessary in some cases. See also [the old_passwords wiki page](https://github.com/go-sql-driver/mysql/wiki/old_passwords).
146146

147+
##### `allowNativeasswords`
148+
149+
```
150+
Type: bool
151+
Valid Values: true, false
152+
Default: false
153+
```
154+
`allowNativeasswords=true` allows the usage of the mysql native password method..
155+
147156
##### `charset`
148157

149158
```

connection.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,6 @@ func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
135135
}
136136

137137
func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (string, error) {
138-
// Number of ? should be same to len(args)
139-
if strings.Count(query, "?") != len(args) {
140-
return "", driver.ErrSkip
141-
}
142-
143138
buf := mc.buf.takeCompleteBuffer()
144139
if buf == nil {
145140
// can not take the buffer. Something must be wrong with the connection

driver.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
101101
}
102102

103103
// Handle response to auth packet, switch methods if possible
104-
if err = handleAuthResult(mc, cipher); err != nil {
104+
if err = handleAuthResult(mc); err != nil {
105105
// Authentication failed and MySQL has already closed the connection
106106
// (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
107107
// Do not send COM_QUIT, just cleanup and return the error.
@@ -130,9 +130,9 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
130130
return mc, nil
131131
}
132132

133-
func handleAuthResult(mc *mysqlConn, cipher []byte) error {
133+
func handleAuthResult(mc *mysqlConn) error {
134134
// Read Result Packet
135-
err := mc.readResultOK()
135+
err, cipher := mc.readResultOK()
136136
if err == nil {
137137
return nil // auth successful
138138
}
@@ -149,15 +149,20 @@ func handleAuthResult(mc *mysqlConn, cipher []byte) error {
149149
if err = mc.writeOldAuthPacket(cipher); err != nil {
150150
return err
151151
}
152-
err = mc.readResultOK()
152+
err, _ = mc.readResultOK()
153153
} else if mc.cfg.AllowCleartextPasswords && err == ErrCleartextPassword {
154154
// Retry with clear text password for
155155
// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
156156
// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
157157
if err = mc.writeClearAuthPacket(); err != nil {
158158
return err
159159
}
160-
err = mc.readResultOK()
160+
err, _ = mc.readResultOK()
161+
} else if mc.cfg.AllowNativePasswords && err == ErrNativePassword {
162+
if err = mc.writeNativeAuthPacket(cipher); err != nil {
163+
return err
164+
}
165+
err, _ = mc.readResultOK()
161166
}
162167
return err
163168
}

dsn.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type Config struct {
4545
AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE
4646
AllowCleartextPasswords bool // Allows the cleartext client side plugin
4747
AllowOldPasswords bool // Allows the old insecure password method
48+
AllowNativePasswords bool // Allows mysql native password authentication method
4849
ClientFoundRows bool // Return number of matching rows instead of rows changed
4950
ColumnsWithAlias bool // Prepend table alias to column names
5051
InterpolateParams bool // Interpolate placeholders into query string
@@ -376,6 +377,14 @@ func parseDSNParams(cfg *Config, params string) (err error) {
376377
return errors.New("invalid bool value: " + value)
377378
}
378379

380+
// Use mysql native password authentication
381+
case "allowNativePasswords":
382+
var isBool bool
383+
cfg.AllowNativePasswords, isBool = readBool(value)
384+
if !isBool {
385+
return errors.New("invalid bool value: " + value)
386+
}
387+
379388
// Switch "rowsAffected" mode
380389
case "clientFoundRows":
381390
var isBool bool

errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ var (
2424
ErrNoTLS = errors.New("TLS requested but server does not support TLS")
2525
ErrOldPassword = errors.New("this user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords")
2626
ErrCleartextPassword = errors.New("this user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN")
27+
ErrNativePassword = errors.New("this user requires mysql native password authentication.")
2728
ErrUnknownPlugin = errors.New("this authentication plugin is not supported")
2829
ErrOldProtocol = errors.New("MySQL server does not support required protocol 41+")
2930
ErrPktSync = errors.New("commands out of sync. You can't run this command now")

infile.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
173173

174174
// read OK packet
175175
if err == nil {
176-
return mc.readResultOK()
176+
err, _ = mc.readResultOK()
177+
return err
177178
}
178179

179180
mc.readPacket()

packets.go

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,26 @@ func (mc *mysqlConn) writeClearAuthPacket() error {
372372
return mc.writePacket(data)
373373
}
374374

375+
// MySql native password authentication method
376+
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
377+
func (mc *mysqlConn) writeNativeAuthPacket(cipher []byte) error {
378+
scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.Passwd))
379+
380+
// Calculate the packet length and add a tailing 0
381+
pktLen := len(scrambleBuff)
382+
data := mc.buf.takeSmallBuffer(4 + pktLen)
383+
if data == nil {
384+
// can not take the buffer. Something must be wrong with the connection
385+
errLog.Print(ErrBusyBuffer)
386+
return driver.ErrBadConn
387+
}
388+
389+
// Add the scramble
390+
copy(data[4:], scrambleBuff)
391+
392+
return mc.writePacket(data)
393+
}
394+
375395
/******************************************************************************
376396
* Command Packets *
377397
******************************************************************************/
@@ -445,36 +465,42 @@ func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error {
445465
******************************************************************************/
446466

447467
// Returns error if Packet is not an 'Result OK'-Packet
448-
func (mc *mysqlConn) readResultOK() error {
468+
func (mc *mysqlConn) readResultOK() (error, []byte) {
449469
data, err := mc.readPacket()
450470
if err == nil {
451471
// packet indicator
452472
switch data[0] {
453473

454474
case iOK:
455-
return mc.handleOkPacket(data)
475+
return mc.handleOkPacket(data), nil
456476

457477
case iEOF:
458478
if len(data) > 1 {
459-
plugin := string(data[1:bytes.IndexByte(data, 0x00)])
479+
pluginEndIndex := bytes.IndexByte(data, 0x00)
480+
plugin := string(data[1:pluginEndIndex])
481+
cipher := data[pluginEndIndex + 1: len(data) - 1]
482+
460483
if plugin == "mysql_old_password" {
461484
// using old_passwords
462-
return ErrOldPassword
485+
return ErrOldPassword, cipher
463486
} else if plugin == "mysql_clear_password" {
464487
// using clear text password
465-
return ErrCleartextPassword
488+
return ErrCleartextPassword, cipher
489+
} else if plugin == "mysql_native_password" {
490+
// using mysql default authentication method
491+
return ErrNativePassword, cipher
466492
} else {
467-
return ErrUnknownPlugin
493+
return ErrUnknownPlugin, cipher
468494
}
469495
} else {
470-
return ErrOldPassword
496+
return ErrOldPassword, nil
471497
}
472498

473499
default: // Error otherwise
474-
return mc.handleErrorPacket(data)
500+
return mc.handleErrorPacket(data), nil
475501
}
476502
}
477-
return err
503+
return err, nil
478504
}
479505

480506
// Result Set Header Packet

0 commit comments

Comments
 (0)