Adding authentication handlers
parent
aa25f19bf1
commit
6c690b91b0
|
@ -0,0 +1,126 @@
|
|||
package socks5
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
noAuth = uint8(0)
|
||||
noAcceptable = uint8(255)
|
||||
userPassAuth = uint8(2)
|
||||
userAuthVersion = uint8(1)
|
||||
authSuccess = uint8(0)
|
||||
authFailure = uint8(1)
|
||||
)
|
||||
|
||||
var (
|
||||
UserAuthFailed = fmt.Errorf("User authentication failed")
|
||||
NoSupportedAuth = fmt.Errorf("No supported authentication mechanism")
|
||||
)
|
||||
|
||||
// authenticate is used to handle connection authentication
|
||||
func (s *Server) authenticate(conn io.Writer, bufConn io.Reader) error {
|
||||
// Get the methods
|
||||
methods, err := readMethods(bufConn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get auth methods: %v", err)
|
||||
}
|
||||
|
||||
// Determine what is supported
|
||||
supportUserPass := s.config.Credentials != nil
|
||||
|
||||
// Select a usable method
|
||||
for _, method := range methods {
|
||||
if method == noAuth && !supportUserPass {
|
||||
return noAuthMode(conn)
|
||||
}
|
||||
if method == userPassAuth && supportUserPass {
|
||||
return s.userPassAuth(conn, bufConn)
|
||||
}
|
||||
}
|
||||
|
||||
// No usable method found
|
||||
return noAcceptableAuth(conn)
|
||||
}
|
||||
|
||||
// userPassAuth is used to handle username/password based
|
||||
// authentication
|
||||
func (s *Server) userPassAuth(conn io.Writer, bufConn io.Reader) error {
|
||||
// Tell the client to use user/pass auth
|
||||
if _, err := conn.Write([]byte{socks5Version, userPassAuth}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the version and username length
|
||||
header := []byte{0, 0}
|
||||
if _, err := io.ReadAtLeast(bufConn, header, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure we are compatible
|
||||
if header[0] != userAuthVersion {
|
||||
return fmt.Errorf("Unsupported auth version: %v", header[0])
|
||||
}
|
||||
|
||||
// Get the user name
|
||||
userLen := int(header[1])
|
||||
user := make([]byte, userLen)
|
||||
if _, err := io.ReadAtLeast(bufConn, user, userLen); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the password length
|
||||
if _, err := bufConn.Read(header[:1]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the password
|
||||
passLen := int(header[0])
|
||||
pass := make([]byte, passLen)
|
||||
if _, err := io.ReadAtLeast(bufConn, pass, passLen); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify the password
|
||||
if s.config.Credentials.Valid(string(user), string(pass)) {
|
||||
if _, err := conn.Write([]byte{userAuthVersion, authSuccess}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := conn.Write([]byte{userAuthVersion, authFailure}); err != nil {
|
||||
return err
|
||||
}
|
||||
return UserAuthFailed
|
||||
}
|
||||
|
||||
// Done
|
||||
return nil
|
||||
}
|
||||
|
||||
// noAuth is used to handle the "No Authentication" mode
|
||||
func noAuthMode(conn io.Writer) error {
|
||||
_, err := conn.Write([]byte{socks5Version, noAuth})
|
||||
return err
|
||||
}
|
||||
|
||||
// noAcceptableAuth is used to handle when we have no eligible
|
||||
// authentication mechanism
|
||||
func noAcceptableAuth(conn io.Writer) error {
|
||||
conn.Write([]byte{socks5Version, noAcceptable})
|
||||
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
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package socks5
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNoAuth(t *testing.T) {
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{1, noAuth})
|
||||
var resp bytes.Buffer
|
||||
|
||||
s := &Server{config: &Config{}}
|
||||
if err := s.authenticate(&resp, req); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
out := resp.Bytes()
|
||||
if !bytes.Equal(out, []byte{socks5Version, noAuth}) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPasswordAuth_Valid(t *testing.T) {
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{2, noAuth, userPassAuth})
|
||||
req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'r'})
|
||||
var resp bytes.Buffer
|
||||
|
||||
cred := StaticCredentials{
|
||||
"foo": "bar",
|
||||
}
|
||||
s := &Server{config: &Config{Credentials: cred}}
|
||||
if err := s.authenticate(&resp, req); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
out := resp.Bytes()
|
||||
if !bytes.Equal(out, []byte{socks5Version, userPassAuth, 1, authSuccess}) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPasswordAuth_Invalid(t *testing.T) {
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{2, noAuth, userPassAuth})
|
||||
req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'z'})
|
||||
var resp bytes.Buffer
|
||||
|
||||
cred := StaticCredentials{
|
||||
"foo": "bar",
|
||||
}
|
||||
s := &Server{config: &Config{Credentials: cred}}
|
||||
if err := s.authenticate(&resp, req); err != UserAuthFailed {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
out := resp.Bytes()
|
||||
if !bytes.Equal(out, []byte{socks5Version, userPassAuth, 1, authFailure}) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoSupportedAuth(t *testing.T) {
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{1, noAuth})
|
||||
var resp bytes.Buffer
|
||||
|
||||
cred := StaticCredentials{
|
||||
"foo": "bar",
|
||||
}
|
||||
s := &Server{config: &Config{Credentials: cred}}
|
||||
if err := s.authenticate(&resp, req); err != NoSupportedAuth {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
out := resp.Bytes()
|
||||
if !bytes.Equal(out, []byte{socks5Version, noAcceptable}) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue