Skip to content

Instantly share code, notes, and snippets.

@ddelazerda
Created December 23, 2015 21:22
Show Gist options
  • Save ddelazerda/fc6e962d3487cfef0708 to your computer and use it in GitHub Desktop.
Save ddelazerda/fc6e962d3487cfef0708 to your computer and use it in GitHub Desktop.
diff --git a/authheader.go b/authheader.go
index 4ca6f1b..959a811 100644
--- a/authheader.go
+++ b/authheader.go
@@ -15,6 +15,11 @@ func (h authheader) IsNegotiate() bool {
return strings.HasPrefix(string(h), "Negotiate")
}
+func (h authheader) IsChallenge() bool {
+ p := strings.Split(string(h), " ")
+ return len(p) == 2 && len(p[1]) > 0 && strings.HasPrefix(p[0], "NTLM")
+}
+
func (h authheader) GetData() ([]byte, error) {
p := strings.Split(string(h), " ")
if len(p) < 2 {
diff --git a/negotiator.go b/negotiator.go
index 8e15558..215da31 100644
--- a/negotiator.go
+++ b/negotiator.go
@@ -15,12 +15,15 @@ type Negotiator struct{ http.RoundTripper }
//re-sends as needed.
func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error) {
body := bytes.Buffer{}
- _, err = body.ReadFrom(req.Body)
- if err != nil {
- return nil, err
+
+ if req.Body != nil {
+ _, err = body.ReadFrom(req.Body)
+ if err != nil {
+ return nil, err
+ }
+ req.Body.Close()
}
- req.Body.Close()
req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
reqauth := authheader(req.Header.Get("Authorization"))
@@ -65,7 +68,11 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error)
// send negotiate
negotiateMessage := NewNegotiateMessage()
- req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(negotiateMessage))
+
+ // format for the Authorization request header is found here:
+ // http://davenport.sourceforge.net/ntlm.html#ntlmHttpAuthentication
+ // and https://msdn.microsoft.com/en-us/library/cc237505.aspx
+ req.Header.Set("Authorization", "NTLM " + base64.StdEncoding.EncodeToString(negotiateMessage))
req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
res, err = l.RoundTripper.RoundTrip(req)
@@ -73,13 +80,26 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error)
return nil, err
}
+ // The body contents need to be read because the default Transport
+ // does not attempt to reuse HTTP/1.0 or HTTP/1.1 TCP connections
+ // ("keep-alive") unless the Body is read to completion and is closed.
+ // Without this, the subsequent request will be treated as a brand new
+ // tcp connection, which resets the NTLM handshake.
+ // Reference: https://godoc.org/net/http#Response
+ if res.Body != nil {
+ _, err = body.ReadFrom(res.Body)
+ if err != nil {
+ return nil, err
+ }
+ }
+
// receive challenge?
resauth = authheader(res.Header.Get("Www-Authenticate"))
challengeMessage, err := resauth.GetData()
if err != nil {
return nil, err
}
- if !resauth.IsNegotiate() || len(challengeMessage) == 0 {
+ if !resauth.IsChallenge() || len(challengeMessage) == 0 {
// Negotiation failed, let client deal with response
return res, nil
}
@@ -90,7 +110,11 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error)
if err != nil {
return nil, err
}
- req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(authenticateMessage))
+
+ // format for the Authorization request header is found here:
+ // http://davenport.sourceforge.net/ntlm.html#ntlmHttpAuthentication
+ // and https://msdn.microsoft.com/en-us/library/cc237505.aspx
+ req.Header.Set("Authorization", "NTLM " + base64.StdEncoding.EncodeToString(authenticateMessage))
req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
res, err = l.RoundTripper.RoundTrip(req)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment