12 "github.com/git-lfs/git-lfs/errors"
15 type ntmlCredentials struct {
21 func (c *Client) doWithNTLM(req *http.Request, credHelper CredentialHelper, creds Creds, credsURL *url.URL) (*http.Response, error) {
23 if err != nil && !errors.IsAuthError(err) {
27 if res.StatusCode != 401 {
31 return c.ntlmReAuth(req, credHelper, creds, true)
34 // If the status is 401 then we need to re-authenticate
35 func (c *Client) ntlmReAuth(req *http.Request, credHelper CredentialHelper, creds Creds, retry bool) (*http.Response, error) {
36 ntmlCreds, err := ntlmGetCredentials(creds)
41 res, err := c.ntlmAuthenticateRequest(req, ntmlCreds)
42 if err != nil && !errors.IsAuthError(err) {
46 switch res.StatusCode {
48 credHelper.Reject(creds)
50 return c.ntlmReAuth(req, credHelper, creds, false)
53 credHelper.Reject(creds)
55 if res.StatusCode < 300 && res.StatusCode > 199 {
56 credHelper.Approve(creds)
63 func (c *Client) ntlmSendType1Message(req *http.Request, message []byte) (*http.Response, []byte, error) {
64 res, err := c.ntlmSendMessage(req, message)
65 if err != nil && !errors.IsAuthError(err) {
69 io.Copy(ioutil.Discard, res.Body)
72 by, err := parseChallengeResponse(res)
76 func (c *Client) ntlmSendType3Message(req *http.Request, authenticate []byte) (*http.Response, error) {
77 return c.ntlmSendMessage(req, authenticate)
80 func (c *Client) ntlmSendMessage(req *http.Request, message []byte) (*http.Response, error) {
81 body, err := rewoundRequestBody(req)
87 msg := base64.StdEncoding.EncodeToString(message)
88 req.Header.Set("Authorization", "NTLM "+msg)
92 func parseChallengeResponse(res *http.Response) ([]byte, error) {
93 header := res.Header.Get("Www-Authenticate")
95 return nil, fmt.Errorf("Invalid NTLM challenge response: %q", header)
98 //parse out the "NTLM " at the beginning of the response
99 challenge := header[5:]
100 val, err := base64.StdEncoding.DecodeString(challenge)
105 return []byte(val), nil
108 func rewoundRequestBody(req *http.Request) (io.ReadCloser, error) {
113 body, ok := req.Body.(ReadSeekCloser)
115 return nil, fmt.Errorf("Request body must implement io.ReadCloser and io.Seeker. Got: %T", body)
118 _, err := body.Seek(0, io.SeekStart)
122 func ntlmGetCredentials(creds Creds) (*ntmlCredentials, error) {
123 username := creds["username"]
124 password := creds["password"]
126 if username == "" && password == "" {
130 splits := strings.Split(username, "\\")
131 if len(splits) != 2 {
132 return nil, fmt.Errorf("Your user name must be of the form DOMAIN\\user. It is currently '%s'", username)
135 domain := strings.ToUpper(splits[0])
138 return &ntmlCredentials{domain: domain, username: username, password: password}, nil