forked from go-mysql-org/go-mysql
/
auth.go
140 lines (104 loc) · 3.18 KB
/
auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package server
import (
"bytes"
"encoding/binary"
. "github.com/siddontang/go-mysql/mysql"
)
func (c *Conn) writeInitialHandshake() error {
capability := CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG |
CLIENT_CONNECT_WITH_DB | CLIENT_PROTOCOL_41 |
CLIENT_TRANSACTIONS | CLIENT_SECURE_CONNECTION
data := make([]byte, 4, 128)
//min version 10
data = append(data, 10)
//server version[00]
data = append(data, ServerVersion...)
data = append(data, 0)
//connection id
data = append(data, byte(c.connectionID), byte(c.connectionID>>8), byte(c.connectionID>>16), byte(c.connectionID>>24))
//auth-plugin-data-part-1
data = append(data, c.salt[0:8]...)
//filter [00]
data = append(data, 0)
//capability flag lower 2 bytes, using default capability here
data = append(data, byte(capability), byte(capability>>8))
//charset, utf-8 default
data = append(data, uint8(DEFAULT_COLLATION_ID))
//status
data = append(data, byte(c.status), byte(c.status>>8))
//below 13 byte may not be used
//capability flag upper 2 bytes, using default capability here
data = append(data, byte(capability>>16), byte(capability>>24))
//filter [0x15], for wireshark dump, value is 0x15
data = append(data, 0x15)
//reserved 10 [00]
data = append(data, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
//auth-plugin-data-part-2
data = append(data, c.salt[8:]...)
//filter [00]
data = append(data, 0)
return c.WritePacket(data)
}
func (c *Conn) readHandshakeResponse() error {
data, err := c.ReadPacket()
if err != nil {
return err
}
pos := 0
//capability
c.capability = binary.LittleEndian.Uint32(data[:4])
pos += 4
//skip max packet size
pos += 4
//charset, skip, if you want to use another charset, use set names
//c.collation = CollationId(data[pos])
pos++
//skip reserved 23[00]
pos += 23
//user name
user := string(data[pos : pos+bytes.IndexByte(data[pos:], 0)])
pos += len(user) + 1
var password string
var userExists bool
if password, userExists = c.users[user]; !userExists {
return NewDefaultError(ER_NO_SUCH_USER, user, c.RemoteAddr().String())
}
c.user = user
var auth []byte
if c.capability&CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA > 0 {
// Fix from github.com/pingcap/tidb
// MySQL client sets the wrong capability, it will set this bit even server doesn't
// support ClientPluginAuthLenencClientData.
// https://github.com/mysql/mysql-server/blob/5.7/sql-common/client.c#L3478
num, null, off := LengthEncodedInt(data[pos:])
pos += off
if !null {
auth = data[pos : pos+int(num)]
pos += int(num)
}
} else if c.capability&CLIENT_SECURE_CONNECTION > 0 {
//auth length and auth
authLen := int(data[pos])
pos++
auth = data[pos : pos+authLen]
pos += authLen
} else {
auth = data[pos : pos+bytes.IndexByte(data[pos:], 0)]
pos += len(auth) + 1
}
checkAuth := CalcPassword(c.salt, []byte(password))
if !bytes.Equal(auth, checkAuth) {
return NewDefaultError(ER_ACCESS_DENIED_ERROR, c.user, c.RemoteAddr().String(), "Yes")
}
if c.capability&CLIENT_CONNECT_WITH_DB > 0 {
if len(data[pos:]) == 0 {
return nil
}
db := string(data[pos : pos+bytes.IndexByte(data[pos:], 0)])
pos += len(db) + 1
if err = c.h.UseDB(db); err != nil {
return err
}
}
return nil
}