-
Notifications
You must be signed in to change notification settings - Fork 219
Expand file tree
/
Copy pathauth.go
More file actions
135 lines (113 loc) · 3.41 KB
/
auth.go
File metadata and controls
135 lines (113 loc) · 3.41 KB
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
/*
* Radon
*
* Copyright 2018 The Radon Authors.
* Code is licensed under the GPLv3.
*
*/
package proxy
import (
"bytes"
"crypto/sha1"
"encoding/hex"
"fmt"
"net"
"strings"
"github.com/xelabs/go-mysqlstack/driver"
"github.com/xelabs/go-mysqlstack/sqldb"
)
func localHostLogin(host string) bool {
return host == "127.0.0.1"
}
func localUserLogin(s *driver.Session) bool {
host, _, err := net.SplitHostPort(s.Addr())
if err != nil {
return false
}
if host == "127.0.0.1" && s.User() == "root" {
return true
}
return false
}
// SessionCheck used to check authentication.
func (spanner *Spanner) SessionCheck(s *driver.Session) error {
// Max connection check.
max := spanner.conf.Proxy.MaxConnections
if spanner.sessions.Reaches(max) {
return sqldb.NewSQLErrorf(sqldb.ER_CON_COUNT_ERROR, "Too many connections(max: %v)", max)
}
log := spanner.log
host, _, err := net.SplitHostPort(s.Addr())
if err != nil {
log.Error("proxy.spanner.split.address.error:%+v", s.Addr())
return sqldb.NewSQLErrorf(sqldb.ER_ACCESS_DENIED_ERROR, "Access denied for user from host '%v'", s.Addr())
}
// Local login bypass.
if localHostLogin(host) {
return nil
}
// Ip check.
if !spanner.iptable.Check(host) {
log.Warning("proxy.spanner.host[%s].denied", host)
return sqldb.NewSQLErrorf(sqldb.ER_ACCESS_DENIED_ERROR, "Access denied for user from host '%v'", host)
}
return nil
}
// AuthCheck impl.
func (spanner *Spanner) AuthCheck(s *driver.Session) error {
// Local login bypass.
if localUserLogin(s) {
return nil
}
log := spanner.log
user := s.User()
// Server salt.
salt := s.Salt()
// Client response.
resp := s.Scramble()
query := fmt.Sprintf("select authentication_string from mysql.user where user='%s'", user)
qr, err := spanner.ExecuteSingle(query)
// Query error.
if err != nil {
log.Error("proxy: auth.error:%+v", err)
return sqldb.NewSQLErrorf(sqldb.ER_ACCESS_DENIED_ERROR, "Access denied for user '%v'", user)
}
// User not exists.
if len(qr.Rows) == 0 {
log.Error("proxy: auth.can't.find.the.user:%s", user)
return sqldb.NewSQLErrorf(sqldb.ER_ACCESS_DENIED_ERROR, "Access denied for user '%v'", user)
}
// mysql.user.authentication_string is ['*' + HEX(SHA1(SHA1(password)))]
authStr := strings.TrimPrefix(qr.Rows[0][0].String(), "*")
wantStage2, err := hex.DecodeString(authStr)
if err != nil {
log.Error("proxy: auth.user[%s].decode[%s].error:%+v", user, authStr, err)
return sqldb.NewSQLErrorf(sqldb.ER_ACCESS_DENIED_ERROR, "Access denied for user '%v'", user)
}
// last= SHA1(salt <concat> SHA1(SHA1(password)))
crypt := sha1.New()
crypt.Write(salt)
crypt.Write(wantStage2)
want := crypt.Sum(nil)
// gotStage1 = SHA1(password)
gotStage1 := make([]byte, 20)
for i := range resp {
// SHA1(password) = (resp XOR want)
gotStage1[i] = (resp[i] ^ want[i])
}
// gotStage2 = SHA1(SHA1(password))
crypt.Reset()
crypt.Write(gotStage1)
gotStage2 := crypt.Sum(nil)
// last= SHA1(salt <concat> SHA1(SHA1(password)))
crypt.Reset()
crypt.Write(salt)
crypt.Write(gotStage2)
got := crypt.Sum(nil)
if !bytes.Equal(want, got) {
log.Warning("spanner.auth\nwant:\n\tstage2:%+v\n\tlast:%+v\ngot\n\tstage2:%+v\n\tlast:%+v\n\n\tsalt:%+v", wantStage2, want, gotStage2, got, salt)
log.Error("proxy: auth.user[%s].failed(password.invalid):want[%+v]!=got[%+v]", user, want, got)
return sqldb.NewSQLErrorf(sqldb.ER_ACCESS_DENIED_ERROR, "Access denied for user '%v'", user)
}
return nil
}