Ver Fonte

Added support for old_password authentication method

Nicola Peduzzi há 12 anos atrás
pai
commit
b03abe27ce
2 ficheiros alterados com 92 adições e 2 exclusões
  1. 30 2
      packets.go
  2. 62 0
      utils.go

+ 30 - 2
packets.go

@@ -307,6 +307,29 @@ func (mc *mysqlConn) writeAuthPacket() error {
 	return mc.writePacket(data)
 }
 
+//  Client old authentication packet
+// http://dev.mysql.com/doc/internals/en/connection-phase.html#packet-Protocol::AuthSwitchResponse
+func (mc *mysqlConn) writeOldAuthPacket() error {
+	// User password
+	scrambleBuff := scrambleOldPassword(mc.cipher, []byte(mc.cfg.passwd))
+	mc.cipher = nil
+
+	// Calculate the packet lenght and add a tailing 0
+	pktLen := len(scrambleBuff) + 1
+	data := make([]byte, pktLen+4)
+
+	// Add the packet header  [24bit length + 1 byte sequence]
+	data[0] = byte(pktLen)
+	data[1] = byte(pktLen >> 8)
+	data[2] = byte(pktLen >> 16)
+	data[3] = mc.sequence
+
+	// Add the scrambled password (it will be terminated by 0)
+	copy(data[4:], scrambleBuff)
+
+	return mc.writePacket(data)
+}
+
 /******************************************************************************
 *                             Command Packets                                 *
 ******************************************************************************/
@@ -388,8 +411,13 @@ func (mc *mysqlConn) readResultOK() error {
 		case iOK:
 			return mc.handleOkPacket(data)
 
-		case iEOF: // someone is using old_passwords
-			return errOldPassword
+		case iEOF:
+			// someone is using old_passwords
+			err = mc.writeOldAuthPacket()
+			if err != nil {
+				return err
+			}
+			return mc.readResultOK()
 
 		default: // Error otherwise
 			return mc.handleErrorPacket(data)

+ 62 - 0
utils.go

@@ -17,6 +17,7 @@ import (
 	"fmt"
 	"io"
 	"log"
+	"math"
 	"os"
 	"regexp"
 	"strings"
@@ -213,6 +214,67 @@ func scramblePassword(scramble, password []byte) []byte {
 	return scramble
 }
 
+// Encrypt password using pre 4.1 (old password) method
+// https://github.com/atcurtis/mariadb/blob/master/mysys/my_rnd.c
+type myRnd struct {
+	seed1, seed2 uint32
+}
+
+const myRndMaxVal = 0x3FFFFFFF
+
+func newMyRnd(seed1, seed2 uint32) *myRnd {
+	r := new(myRnd)
+	r.seed1 = seed1 % myRndMaxVal
+	r.seed2 = seed2 % myRndMaxVal
+	return r
+}
+
+func (r *myRnd) Float64() float64 {
+	r.seed1 = (r.seed1*3 + r.seed2) % myRndMaxVal
+	r.seed2 = (r.seed1 + r.seed2 + 33) % myRndMaxVal
+	return float64(r.seed1) / myRndMaxVal
+}
+
+// https://github.com/atcurtis/mariadb/blob/master/sql/password.c
+func pwHash(password []byte) (result [2]uint32) {
+	var nr, add, nr2, tmp uint32
+	nr, add, nr2 = 1345345333, 7, 0x12345671
+
+	for _, c := range password {
+		if c == ' ' || c == '\t' {
+			continue // skip space in password
+		}
+
+		tmp = uint32(c)
+		nr ^= (((nr & 63) + add) * tmp) + (nr << 8)
+		nr2 += (nr2 << 8) ^ nr
+		add += tmp
+	}
+
+	result[0] = nr & ((1 << 31) - 1) // Don't use sign bit (str2int)
+	result[1] = nr2 & ((1 << 31) - 1)
+	return
+}
+
+func scrambleOldPassword(scramble, password []byte) []byte {
+	if len(password) == 0 {
+		return nil
+	}
+	scramble = scramble[:8]
+	hashPw := pwHash(password)
+	hashSc := pwHash(scramble)
+	r := newMyRnd(hashPw[0]^hashSc[0], hashPw[1]^hashSc[1])
+	var out [8]byte
+	for i := range out {
+		out[i] = byte(math.Floor(r.Float64()*31) + 64)
+	}
+	extra := byte(math.Floor(r.Float64() * 31))
+	for i := range out {
+		out[i] ^= extra
+	}
+	return out[:]
+}
+
 // Returns the bool value of the input.
 // The 2nd return value indicates if the input was a valid bool value
 func readBool(input string) (value bool, valid bool) {