forked from aksdb/CalAnonSync
104 lines
2.5 KiB
Go
104 lines
2.5 KiB
Go
package httpntlm
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"io"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/ThomsonReutersEikon/go-ntlm/ntlm"
|
|
)
|
|
|
|
// NtlmTransport is implementation of http.RoundTripper interface
|
|
type NtlmTransport struct {
|
|
TLSClientConfig *tls.Config
|
|
Domain string
|
|
User string
|
|
Password string
|
|
}
|
|
|
|
// RoundTrip method send http request and tries to perform NTLM authentication
|
|
func (t NtlmTransport) RoundTrip(req *http.Request) (res *http.Response, err error) {
|
|
// first send NTLM Negotiate header
|
|
r, _ := http.NewRequest("GET", req.URL.String(), strings.NewReader(""))
|
|
r.Header.Add("Authorization", "NTLM "+encBase64(negotiate()))
|
|
|
|
client := http.Client{Transport: &http.Transport{
|
|
Proxy: http.ProxyFromEnvironment,
|
|
DialContext: (&net.Dialer{
|
|
Timeout: 30 * time.Second,
|
|
KeepAlive: 30 * time.Second,
|
|
DualStack: true,
|
|
}).DialContext,
|
|
MaxIdleConns: 100,
|
|
IdleConnTimeout: 90 * time.Second,
|
|
TLSHandshakeTimeout: 10 * time.Second,
|
|
ExpectContinueTimeout: 1 * time.Second,
|
|
TLSClientConfig: t.TLSClientConfig,
|
|
}}
|
|
|
|
resp, err := client.Do(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err == nil && resp.StatusCode == http.StatusUnauthorized {
|
|
// it's necessary to reuse the same http connection
|
|
// in order to do that it's required to read Body and close it
|
|
_, err = io.Copy(ioutil.Discard, resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = resp.Body.Close()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// retrieve Www-Authenticate header from response
|
|
ntlmChallengeHeader := resp.Header.Get("WWW-Authenticate")
|
|
if ntlmChallengeHeader == "" {
|
|
return nil, errors.New("Wrong WWW-Authenticate header")
|
|
}
|
|
|
|
ntlmChallengeString := strings.Replace(ntlmChallengeHeader, "NTLM ", "", -1)
|
|
challengeBytes, err := decBase64(ntlmChallengeString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
session, err := ntlm.CreateClientSession(ntlm.Version2, ntlm.ConnectionlessMode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
session.SetUserInfo(t.User, t.Password, t.Domain)
|
|
|
|
// parse NTLM challenge
|
|
challenge, err := ntlm.ParseChallengeMessage(challengeBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = session.ProcessChallengeMessage(challenge)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// authenticate user
|
|
authenticate, err := session.GenerateAuthenticateMessage()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// set NTLM Authorization header
|
|
req.Header.Set("Authorization", "NTLM "+encBase64(authenticate.Bytes()))
|
|
resp, err = client.Do(req)
|
|
}
|
|
|
|
return resp, err
|
|
}
|