forked from haochen233/socks5
-
Notifications
You must be signed in to change notification settings - Fork 0
/
auth.go
165 lines (140 loc) · 3.86 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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package socks5
import (
"bytes"
"fmt"
"hash"
"io"
"sync"
)
// Authenticator provides socks5's authentication sub negotiation.
type Authenticator interface {
Authenticate(in io.Reader, out io.Writer) error
}
// NoAuth NO_AUTHENTICATION_REQUIRED implementation.
type NoAuth struct {
}
// Authenticate NO_AUTHENTICATION_REQUIRED Authentication for socks5 Server and Client.
func (n NoAuth) Authenticate(in io.Reader, out io.Writer) error {
return nil
}
// UserPwdAuth provides socks5 Server Username/Password Authenticator.
type UserPwdAuth struct {
UserPwdStore
}
// Authenticate provide socks5 Server Username/Password authentication.
func (u UserPwdAuth) Authenticate(in io.Reader, out io.Writer) error {
uname, passwd, err := u.ReadUserPwd(in)
if err != nil {
return err
}
err = u.Validate(string(uname), string(passwd))
if err != nil {
reply := []byte{1, 1}
_, err1 := out.Write(reply)
if err1 != nil {
return err
}
return err
}
//authentication successful,then send reply to client
reply := []byte{1, 0}
_, err = out.Write(reply)
if err != nil {
return err
}
return nil
}
// ReadUserPwd read Username/Password request from client
// return username and password.
// Username/Password request format is as follows:
// +----+------+----------+------+----------+
// |VER | ULEN | UNAME | PLEN | PASSWD |
// +----+------+----------+------+----------+
// | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
// +----+------+----------+------+----------+
// For standard details, please see (https://www.rfc-editor.org/rfc/rfc1929.html)
func (u UserPwdAuth) ReadUserPwd(in io.Reader) ([]byte, []byte, error) {
ulen, err := ReadNBytes(in, 2)
if err != nil {
return nil, nil, err
}
uname, err := ReadNBytes(in, int(ulen[1]))
if err != nil {
return nil, nil, err
}
plen, err := ReadNBytes(in, 1)
if err != nil {
return nil, nil, err
}
passwd := make([]byte, plen[0])
passwd, err = ReadNBytes(in, int(plen[0]))
if err != nil {
return nil, nil, err
}
return uname, passwd, nil
}
// UserPwdStore provide username and password storage.
type UserPwdStore interface {
Set(username string, password string) error
Del(username string) error
Validate(username string, password string) error
}
// MemoryStore store username&password in memory.
// the password is encrypt with hash method.
type MemoryStore struct {
Users map[string][]byte
mu sync.Mutex
hash.Hash
algoSecret string
}
// NewMemeryStore return a new MemoryStore
func NewMemeryStore(algo hash.Hash, secret string) *MemoryStore {
return &MemoryStore{
Users: make(map[string][]byte),
Hash: algo,
algoSecret: secret,
}
}
// Set the mapping of username and password.
func (m *MemoryStore) Set(username string, password string) error {
m.mu.Lock()
defer m.mu.Unlock()
build := bytes.NewBuffer(nil)
build.WriteString(password + m.algoSecret)
cryptPasswd := m.Hash.Sum(build.Bytes())
m.Users[username] = cryptPasswd
return nil
}
// UserNotExist the error type used in UserPwdStore.Del() method and
// UserPwdStore.Validate method.
type UserNotExist struct {
username string
}
func (u UserNotExist) Error() string {
return fmt.Sprintf("user %s don't exist", u.username)
}
// Del delete by username
func (m *MemoryStore) Del(username string) error {
m.mu.Lock()
defer m.mu.Unlock()
if _, ok := m.Users[username]; !ok {
return UserNotExist{username: username}
}
delete(m.Users, username)
return nil
}
// Validate validate username and password.
func (m *MemoryStore) Validate(username string, password string) error {
m.mu.Lock()
defer m.mu.Unlock()
if _, ok := m.Users[username]; !ok {
return UserNotExist{username: username}
}
build := bytes.NewBuffer(nil)
build.WriteString(password + m.algoSecret)
cryptPasswd := m.Hash.Sum(build.Bytes())
if !bytes.Equal(cryptPasswd, m.Users[username]) {
return fmt.Errorf("user %s has bad password", username)
}
return nil
}