auth.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. package socks5
  2. import (
  3. "fmt"
  4. "io"
  5. )
  6. const (
  7. NoAuth = uint8(0)
  8. noAcceptable = uint8(255)
  9. UserPassAuth = uint8(2)
  10. userAuthVersion = uint8(1)
  11. authSuccess = uint8(0)
  12. authFailure = uint8(1)
  13. )
  14. var (
  15. UserAuthFailed = fmt.Errorf("User authentication failed")
  16. NoSupportedAuth = fmt.Errorf("No supported authentication mechanism")
  17. )
  18. // A Request encapsulates authentication state provided
  19. // during negotiation
  20. type AuthContext struct {
  21. // Provided auth method
  22. Method uint8
  23. // Payload provided during negotiation.
  24. // Keys depend on the used auth method.
  25. // For UserPassauth contains Username
  26. Payload map[string]string
  27. }
  28. type Authenticator interface {
  29. Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error)
  30. GetCode() uint8
  31. }
  32. // NoAuthAuthenticator is used to handle the "No Authentication" mode
  33. type NoAuthAuthenticator struct{}
  34. func (a NoAuthAuthenticator) GetCode() uint8 {
  35. return NoAuth
  36. }
  37. func (a NoAuthAuthenticator) Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error) {
  38. _, err := writer.Write([]byte{socks5Version, NoAuth})
  39. return &AuthContext{NoAuth, nil}, err
  40. }
  41. // UserPassAuthenticator is used to handle username/password based
  42. // authentication
  43. type UserPassAuthenticator struct {
  44. Credentials CredentialStore
  45. }
  46. func (a UserPassAuthenticator) GetCode() uint8 {
  47. return UserPassAuth
  48. }
  49. func (a UserPassAuthenticator) Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error) {
  50. // Tell the client to use user/pass auth
  51. if _, err := writer.Write([]byte{socks5Version, UserPassAuth}); err != nil {
  52. return nil, err
  53. }
  54. // Get the version and username length
  55. header := []byte{0, 0}
  56. if _, err := io.ReadAtLeast(reader, header, 2); err != nil {
  57. return nil, err
  58. }
  59. // Ensure we are compatible
  60. if header[0] != userAuthVersion {
  61. return nil, fmt.Errorf("Unsupported auth version: %v", header[0])
  62. }
  63. // Get the user name
  64. userLen := int(header[1])
  65. user := make([]byte, userLen)
  66. if _, err := io.ReadAtLeast(reader, user, userLen); err != nil {
  67. return nil, err
  68. }
  69. // Get the password length
  70. if _, err := reader.Read(header[:1]); err != nil {
  71. return nil, err
  72. }
  73. // Get the password
  74. passLen := int(header[0])
  75. pass := make([]byte, passLen)
  76. if _, err := io.ReadAtLeast(reader, pass, passLen); err != nil {
  77. return nil, err
  78. }
  79. // Verify the password
  80. if a.Credentials.Valid(string(user), string(pass)) {
  81. if _, err := writer.Write([]byte{userAuthVersion, authSuccess}); err != nil {
  82. return nil, err
  83. }
  84. } else {
  85. if _, err := writer.Write([]byte{userAuthVersion, authFailure}); err != nil {
  86. return nil, err
  87. }
  88. return nil, UserAuthFailed
  89. }
  90. // Done
  91. return &AuthContext{UserPassAuth, map[string]string{"Username": string(user)}}, nil
  92. }
  93. // authenticate is used to handle connection authentication
  94. func (s *Server) authenticate(conn io.Writer, bufConn io.Reader) (*AuthContext, error) {
  95. // Get the methods
  96. methods, err := readMethods(bufConn)
  97. if err != nil {
  98. return nil, fmt.Errorf("Failed to get auth methods: %v", err)
  99. }
  100. // Select a usable method
  101. for _, method := range methods {
  102. cator, found := s.authMethods[method]
  103. if found {
  104. return cator.Authenticate(bufConn, conn)
  105. }
  106. }
  107. // No usable method found
  108. return nil, noAcceptableAuth(conn)
  109. }
  110. // noAcceptableAuth is used to handle when we have no eligible
  111. // authentication mechanism
  112. func noAcceptableAuth(conn io.Writer) error {
  113. conn.Write([]byte{socks5Version, noAcceptable})
  114. return NoSupportedAuth
  115. }
  116. // readMethods is used to read the number of methods
  117. // and proceeding auth methods
  118. func readMethods(r io.Reader) ([]byte, error) {
  119. header := []byte{0}
  120. if _, err := r.Read(header); err != nil {
  121. return nil, err
  122. }
  123. numMethods := int(header[0])
  124. methods := make([]byte, numMethods)
  125. _, err := io.ReadAtLeast(r, methods, numMethods)
  126. return methods, err
  127. }