http_authenticator.go 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. /*
  2. * Copyright (c) 2015, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package upstreamproxy
  20. import (
  21. "fmt"
  22. "net/http"
  23. "strings"
  24. )
  25. type HttpAuthState int
  26. const (
  27. HTTP_AUTH_STATE_UNCHALLENGED HttpAuthState = iota
  28. HTTP_AUTH_STATE_CHALLENGED
  29. HTTP_AUTH_STATE_FAILURE
  30. HTTP_AUTH_STATE_SUCCESS
  31. )
  32. type HttpAuthenticator interface {
  33. Authenticate(req *http.Request, resp *http.Response, username, pasword string) error
  34. }
  35. func parseAuthChallenge(resp *http.Response) (map[string]string, error) {
  36. challenges := make(map[string]string)
  37. headers := resp.Header[http.CanonicalHeaderKey("proxy-authenticate")]
  38. for _, val := range headers {
  39. s := strings.SplitN(val, " ", 2)
  40. if len(s) == 2 {
  41. challenges[s[0]] = s[1]
  42. }
  43. if len(s) == 1 && s[0] != "" {
  44. challenges[s[0]] = ""
  45. }
  46. }
  47. if len(challenges) == 0 {
  48. return nil, proxyError(fmt.Errorf("No valid challenges in the Proxy-Authenticate header"))
  49. }
  50. return challenges, nil
  51. }
  52. func NewHttpAuthenticator(resp *http.Response) (HttpAuthenticator, error) {
  53. challenges, err := parseAuthChallenge(resp)
  54. if err != nil {
  55. //Already wrapped in proxyError
  56. return nil, err
  57. }
  58. // NTLM > Digest > Basic
  59. if _, ok := challenges["NTLM"]; ok {
  60. return newNTLMAuthenticator(), nil
  61. } else if _, ok := challenges["Digest"]; ok {
  62. return newDigestAuthenticator(), nil
  63. } else if _, ok := challenges["Basic"]; ok {
  64. return newBasicAuthenticator(), nil
  65. }
  66. //Unsupported scheme
  67. schemes := make([]string, 0, len(challenges))
  68. for scheme := range challenges {
  69. schemes = append(schemes, scheme)
  70. }
  71. return nil, proxyError(fmt.Errorf("Unsupported proxy authentication scheme in %v", schemes))
  72. }