go-socks5/auth.go

153 lines
3.9 KiB
Go
Raw Normal View History

2014-01-23 19:27:48 +00:00
package socks5
import (
"fmt"
"io"
)
const (
2020-04-21 06:03:20 +00:00
MethodNoAuth = uint8(0)
MethodGSSAPI = uint8(1)
MethodUserPassAuth = uint8(2)
MethodNoAcceptable = uint8(255)
UserPassAuthVersion = uint8(1)
AuthSuccess = uint8(0)
AuthFailure = uint8(1)
2014-01-23 19:27:48 +00:00
)
var (
UserAuthFailed = fmt.Errorf("User authentication failed")
NoSupportedAuth = fmt.Errorf("No supported authentication mechanism")
)
// A Request encapsulates authentication state provided
// during negotiation
type AuthContext struct {
// Provided auth method
Method uint8
// Payload provided during negotiation.
// Keys depend on the used auth method.
// For UserPassauth contains Username
Payload map[string]string
}
2014-02-18 14:21:03 +00:00
type Authenticator interface {
Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error)
2014-02-18 14:21:03 +00:00
GetCode() uint8
}
2014-01-23 19:27:48 +00:00
2014-02-18 14:21:03 +00:00
// NoAuthAuthenticator is used to handle the "No Authentication" mode
type NoAuthAuthenticator struct{}
2014-01-23 19:27:48 +00:00
2014-02-18 14:21:03 +00:00
func (a NoAuthAuthenticator) GetCode() uint8 {
2020-04-21 06:03:20 +00:00
return MethodNoAuth
2014-02-18 14:21:03 +00:00
}
2014-01-23 19:27:48 +00:00
func (a NoAuthAuthenticator) Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error) {
2020-04-21 06:03:20 +00:00
_, err := writer.Write([]byte{VersionSocks5, MethodNoAuth})
return &AuthContext{MethodNoAuth, nil}, err
2014-01-23 19:27:48 +00:00
}
2014-02-18 14:21:03 +00:00
// UserPassAuthenticator is used to handle username/password based
2014-01-23 19:27:48 +00:00
// authentication
2014-02-18 14:21:03 +00:00
type UserPassAuthenticator struct {
Credentials CredentialStore
}
func (a UserPassAuthenticator) GetCode() uint8 {
2020-04-21 06:03:20 +00:00
return MethodUserPassAuth
2014-02-18 14:21:03 +00:00
}
func (a UserPassAuthenticator) Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error) {
2014-01-23 19:27:48 +00:00
// Tell the client to use user/pass auth
2020-04-21 06:03:20 +00:00
if _, err := writer.Write([]byte{VersionSocks5, MethodUserPassAuth}); err != nil {
return nil, err
2014-01-23 19:27:48 +00:00
}
// Get the version and username length
header := []byte{0, 0}
2014-02-18 14:21:03 +00:00
if _, err := io.ReadAtLeast(reader, header, 2); err != nil {
return nil, err
2014-01-23 19:27:48 +00:00
}
// Ensure we are compatible
2020-04-21 06:03:20 +00:00
if header[0] != UserPassAuthVersion {
return nil, fmt.Errorf("Unsupported auth version: %v", header[0])
2014-01-23 19:27:48 +00:00
}
// Get the user name
userLen := int(header[1])
user := make([]byte, userLen)
2014-02-18 14:21:03 +00:00
if _, err := io.ReadAtLeast(reader, user, userLen); err != nil {
return nil, err
2014-01-23 19:27:48 +00:00
}
// Get the password length
2014-02-18 14:21:03 +00:00
if _, err := reader.Read(header[:1]); err != nil {
return nil, err
2014-01-23 19:27:48 +00:00
}
// Get the password
passLen := int(header[0])
pass := make([]byte, passLen)
2014-02-18 14:21:03 +00:00
if _, err := io.ReadAtLeast(reader, pass, passLen); err != nil {
return nil, err
2014-01-23 19:27:48 +00:00
}
// Verify the password
2014-02-18 14:21:03 +00:00
if a.Credentials.Valid(string(user), string(pass)) {
2020-04-21 06:03:20 +00:00
if _, err := writer.Write([]byte{UserPassAuthVersion, AuthSuccess}); err != nil {
return nil, err
2014-01-23 19:27:48 +00:00
}
} else {
2020-04-21 06:03:20 +00:00
if _, err := writer.Write([]byte{UserPassAuthVersion, AuthFailure}); err != nil {
return nil, err
2014-01-23 19:27:48 +00:00
}
return nil, UserAuthFailed
2014-01-23 19:27:48 +00:00
}
// Done
2020-04-21 06:03:20 +00:00
return &AuthContext{MethodUserPassAuth, map[string]string{"Username": string(user)}}, nil
2014-01-23 19:27:48 +00:00
}
2014-02-18 14:21:03 +00:00
// authenticate is used to handle connection authentication
func (s *Server) authenticate(conn io.Writer, bufConn io.Reader) (*AuthContext, error) {
2014-02-18 14:21:03 +00:00
// Get the methods
methods, err := readMethods(bufConn)
if err != nil {
return nil, fmt.Errorf("Failed to get auth methods: %v", err)
2014-02-18 14:21:03 +00:00
}
// Select a usable method
for _, method := range methods {
cator, found := s.authMethods[method]
if found {
return cator.Authenticate(bufConn, conn)
}
}
// No usable method found
return nil, noAcceptableAuth(conn)
2014-01-23 19:27:48 +00:00
}
// noAcceptableAuth is used to handle when we have no eligible
// authentication mechanism
func noAcceptableAuth(conn io.Writer) error {
2020-04-21 06:03:20 +00:00
conn.Write([]byte{VersionSocks5, MethodNoAcceptable})
2014-01-23 19:27:48 +00:00
return NoSupportedAuth
}
// readMethods is used to read the number of methods
// and proceeding auth methods
func readMethods(r io.Reader) ([]byte, error) {
header := []byte{0}
if _, err := r.Read(header); err != nil {
return nil, err
}
numMethods := int(header[0])
methods := make([]byte, numMethods)
_, err := io.ReadAtLeast(r, methods, numMethods)
return methods, err
}