go-ntlmssp/negotiator.go

152 строки
4.0 KiB
Go
Исходник Постоянная ссылка Обычный вид История

2015-10-17 07:41:57 +03:00
package ntlmssp
import (
"bytes"
"encoding/base64"
"io"
2015-10-17 07:41:57 +03:00
"io/ioutil"
"net/http"
"strings"
2015-10-17 07:41:57 +03:00
)
2018-04-15 05:38:39 +03:00
// GetDomain : parse domain name from based on slashes in the input
2022-06-13 16:08:32 +03:00
// Need to check for upn as well
2022-06-13 16:15:25 +03:00
func GetDomain(user string) (string, string, bool) {
domain := ""
2022-06-13 16:15:25 +03:00
domainNeeded := false
if strings.Contains(user, "\\") {
ucomponents := strings.SplitN(user, "\\", 2)
domain = ucomponents[0]
2022-06-13 17:24:43 +03:00
user = ucomponents[1]
2022-06-13 17:17:23 +03:00
domainNeeded = true
2022-06-13 16:15:25 +03:00
} else if strings.Contains(user, "@") {
domainNeeded = false
} else {
domainNeeded = true
}
2022-06-13 16:15:25 +03:00
return user, domain, domainNeeded
}
2015-10-17 07:41:57 +03:00
//Negotiator is a http.Roundtripper decorator that automatically
//converts basic authentication to NTLM/Negotiate authentication when appropriate.
type Negotiator struct{ http.RoundTripper }
//RoundTrip sends the request to the server, handling any authentication
//re-sends as needed.
func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error) {
// Use default round tripper if not provided
rt := l.RoundTripper
if rt == nil {
rt = http.DefaultTransport
2015-10-17 07:41:57 +03:00
}
// If it is not basic auth, just round trip the request as usual
reqauth := authheader(req.Header.Values("Authorization"))
if !reqauth.IsBasic() {
return rt.RoundTrip(req)
}
reqauthBasic := reqauth.Basic()
// Save request body
body := bytes.Buffer{}
if req.Body != nil {
_, err = body.ReadFrom(req.Body)
2015-10-17 07:41:57 +03:00
if err != nil {
return nil, err
}
req.Body.Close()
req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
}
// first try anonymous, in case the server still finds us
// authenticated from previous traffic
req.Header.Del("Authorization")
res, err = rt.RoundTrip(req)
if err != nil {
return nil, err
}
if res.StatusCode != http.StatusUnauthorized {
return res, err
2015-10-17 07:41:57 +03:00
}
resauth := authheader(res.Header.Values("Www-Authenticate"))
2017-01-27 15:39:00 +03:00
if !resauth.IsNegotiate() && !resauth.IsNTLM() {
2015-10-17 07:41:57 +03:00
// Unauthorized, Negotiate not requested, let's try with basic auth
req.Header.Set("Authorization", string(reqauthBasic))
io.Copy(ioutil.Discard, res.Body)
res.Body.Close()
2015-10-17 07:41:57 +03:00
req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
res, err = rt.RoundTrip(req)
2015-10-17 07:41:57 +03:00
if err != nil {
return nil, err
}
if res.StatusCode != http.StatusUnauthorized {
return res, err
}
resauth = authheader(res.Header.Values("Www-Authenticate"))
2015-10-17 07:41:57 +03:00
}
2017-01-27 15:39:00 +03:00
if resauth.IsNegotiate() || resauth.IsNTLM() {
// 401 with request:Basic and response:Negotiate
io.Copy(ioutil.Discard, res.Body)
res.Body.Close()
2015-10-17 07:41:57 +03:00
// recycle credentials
u, p, err := reqauth.GetBasicCreds()
if err != nil {
return nil, err
}
2015-10-17 07:41:57 +03:00
// get domain from username
2018-04-13 11:50:16 +03:00
domain := ""
2022-06-13 16:15:25 +03:00
u, domain, domainNeeded := GetDomain(u)
2018-04-13 11:50:16 +03:00
// send negotiate
2022-06-13 16:32:19 +03:00
negotiateMessage, err := NewNegotiateMessage(domain, "")
2018-04-13 11:50:16 +03:00
if err != nil {
return nil, err
}
if resauth.IsNTLM() {
2017-01-27 15:39:00 +03:00
req.Header.Set("Authorization", "NTLM "+base64.StdEncoding.EncodeToString(negotiateMessage))
} else {
req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(negotiateMessage))
}
req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
2015-10-17 07:41:57 +03:00
res, err = rt.RoundTrip(req)
if err != nil {
return nil, err
}
2015-10-17 07:41:57 +03:00
// receive challenge?
resauth = authheader(res.Header.Values("Www-Authenticate"))
challengeMessage, err := resauth.GetData()
if err != nil {
return nil, err
}
2017-01-27 15:39:00 +03:00
if !(resauth.IsNegotiate() || resauth.IsNTLM()) || len(challengeMessage) == 0 {
// Negotiation failed, let client deal with response
return res, nil
}
io.Copy(ioutil.Discard, res.Body)
res.Body.Close()
2015-10-17 07:41:57 +03:00
// send authenticate
2022-06-13 16:32:19 +03:00
authenticateMessage, err := ProcessChallenge(challengeMessage, u, p, domainNeeded)
if err != nil {
return nil, err
2015-10-17 07:41:57 +03:00
}
2017-01-27 15:39:00 +03:00
if resauth.IsNTLM() {
req.Header.Set("Authorization", "NTLM "+base64.StdEncoding.EncodeToString(authenticateMessage))
} else {
req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(authenticateMessage))
}
req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
2019-11-11 19:02:55 +03:00
return rt.RoundTrip(req)
2015-10-17 07:41:57 +03:00
}
return res, err
}