Merge pull request #6802 from riverar/update_ntlmssp_dep
Update Azure/go-ntlmssp dep
This commit is contained in:
commit
ecb128111f
|
@ -9,3 +9,21 @@ Implementation hints from http://davenport.sourceforge.net/ntlm.html
|
|||
This package only implements authentication, no key exchange or encryption. It
|
||||
only supports Unicode (UTF16LE) encoding of protocol strings, no OEM encoding.
|
||||
This package implements NTLMv2.
|
||||
|
||||
# Usage
|
||||
|
||||
```
|
||||
url, user, password := "http://www.example.com/secrets", "robpike", "pw123"
|
||||
client := &http.Client{
|
||||
Transport: ntlmssp.Negotiator{
|
||||
RoundTripper:&http.Transport{},
|
||||
},
|
||||
}
|
||||
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.SetBasicAuth(user, password)
|
||||
res, _ := client.Do(req)
|
||||
```
|
||||
|
||||
-----
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
|
|
@ -15,6 +15,10 @@ func (h authheader) IsNegotiate() bool {
|
|||
return strings.HasPrefix(string(h), "Negotiate")
|
||||
}
|
||||
|
||||
func (h authheader) IsNTLM() bool {
|
||||
return strings.HasPrefix(string(h), "NTLM")
|
||||
}
|
||||
|
||||
func (h authheader) GetData() ([]byte, error) {
|
||||
p := strings.Split(string(h), " ")
|
||||
if len(p) < 2 {
|
||||
|
|
|
@ -7,30 +7,38 @@ const (
|
|||
/*B*/ negotiateFlagNTLMNEGOTIATEOEM = 1 << 1
|
||||
/*C*/ negotiateFlagNTLMSSPREQUESTTARGET = 1 << 2
|
||||
|
||||
/*D*/ negotiateFlagNTLMSSPNEGOTIATESIGN = 1 << 4
|
||||
/*D*/
|
||||
negotiateFlagNTLMSSPNEGOTIATESIGN = 1 << 4
|
||||
/*E*/ negotiateFlagNTLMSSPNEGOTIATESEAL = 1 << 5
|
||||
/*F*/ negotiateFlagNTLMSSPNEGOTIATEDATAGRAM = 1 << 6
|
||||
/*G*/ negotiateFlagNTLMSSPNEGOTIATELMKEY = 1 << 7
|
||||
|
||||
/*H*/ negotiateFlagNTLMSSPNEGOTIATENTLM = 1 << 9
|
||||
/*H*/
|
||||
negotiateFlagNTLMSSPNEGOTIATENTLM = 1 << 9
|
||||
|
||||
/*J*/ negotiateFlagANONYMOUS = 1 << 11
|
||||
/*J*/
|
||||
negotiateFlagANONYMOUS = 1 << 11
|
||||
/*K*/ negotiateFlagNTLMSSPNEGOTIATEOEMDOMAINSUPPLIED = 1 << 12
|
||||
/*L*/ negotiateFlagNTLMSSPNEGOTIATEOEMWORKSTATIONSUPPLIED = 1 << 13
|
||||
|
||||
/*M*/ negotiateFlagNTLMSSPNEGOTIATEALWAYSSIGN = 1 << 15
|
||||
/*M*/
|
||||
negotiateFlagNTLMSSPNEGOTIATEALWAYSSIGN = 1 << 15
|
||||
/*N*/ negotiateFlagNTLMSSPTARGETTYPEDOMAIN = 1 << 16
|
||||
/*O*/ negotiateFlagNTLMSSPTARGETTYPESERVER = 1 << 17
|
||||
|
||||
/*P*/ negotiateFlagNTLMSSPNEGOTIATEEXTENDEDSESSIONSECURITY = 1 << 19
|
||||
/*P*/
|
||||
negotiateFlagNTLMSSPNEGOTIATEEXTENDEDSESSIONSECURITY = 1 << 19
|
||||
/*Q*/ negotiateFlagNTLMSSPNEGOTIATEIDENTIFY = 1 << 20
|
||||
|
||||
/*R*/ negotiateFlagNTLMSSPREQUESTNONNTSESSIONKEY = 1 << 22
|
||||
/*R*/
|
||||
negotiateFlagNTLMSSPREQUESTNONNTSESSIONKEY = 1 << 22
|
||||
/*S*/ negotiateFlagNTLMSSPNEGOTIATETARGETINFO = 1 << 23
|
||||
|
||||
/*T*/ negotiateFlagNTLMSSPNEGOTIATEVERSION = 1 << 25
|
||||
/*T*/
|
||||
negotiateFlagNTLMSSPNEGOTIATEVERSION = 1 << 25
|
||||
|
||||
/*U*/ negotiateFlagNTLMSSPNEGOTIATE128 = 1 << 29
|
||||
/*U*/
|
||||
negotiateFlagNTLMSSPNEGOTIATE128 = 1 << 29
|
||||
/*V*/ negotiateFlagNTLMSSPNEGOTIATEKEYEXCH = 1 << 30
|
||||
/*W*/ negotiateFlagNTLMSSPNEGOTIATE56 = 1 << 31
|
||||
)
|
||||
|
|
|
@ -3,29 +3,62 @@ package ntlmssp
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const expMsgBodyLen = 40
|
||||
|
||||
type negotiateMessageFields struct {
|
||||
messageHeader
|
||||
NegotiateFlags negotiateFlags
|
||||
|
||||
Domain varField
|
||||
Workstation varField
|
||||
|
||||
Version
|
||||
}
|
||||
|
||||
var defaultFlags = negotiateFlagNTLMSSPNEGOTIATETARGETINFO |
|
||||
negotiateFlagNTLMSSPNEGOTIATE56 |
|
||||
negotiateFlagNTLMSSPNEGOTIATE128 |
|
||||
negotiateFlagNTLMSSPNEGOTIATEUNICODE |
|
||||
negotiateFlagNTLMSSPNEGOTIATEEXTENDEDSESSIONSECURITY
|
||||
|
||||
//NewNegotiateMessage creates a new NEGOTIATE message with the
|
||||
//flags that this package supports.
|
||||
func NewNegotiateMessage() []byte {
|
||||
m := negotiateMessageFields{
|
||||
messageHeader: newMessageHeader(1),
|
||||
func NewNegotiateMessage(domainName, workstationName string) ([]byte, error) {
|
||||
payloadOffset := expMsgBodyLen
|
||||
flags := defaultFlags
|
||||
|
||||
if domainName != "" {
|
||||
flags |= negotiateFlagNTLMSSPNEGOTIATEOEMDOMAINSUPPLIED
|
||||
}
|
||||
|
||||
m.NegotiateFlags = negotiateFlagNTLMSSPREQUESTTARGET |
|
||||
negotiateFlagNTLMSSPNEGOTIATENTLM |
|
||||
negotiateFlagNTLMSSPNEGOTIATEALWAYSSIGN |
|
||||
negotiateFlagNTLMSSPNEGOTIATEUNICODE
|
||||
if workstationName != "" {
|
||||
flags |= negotiateFlagNTLMSSPNEGOTIATEOEMWORKSTATIONSUPPLIED
|
||||
}
|
||||
|
||||
msg := negotiateMessageFields{
|
||||
messageHeader: newMessageHeader(1),
|
||||
NegotiateFlags: flags,
|
||||
Domain: newVarField(&payloadOffset, len(domainName)),
|
||||
Workstation: newVarField(&payloadOffset, len(workstationName)),
|
||||
Version: DefaultVersion(),
|
||||
}
|
||||
|
||||
b := bytes.Buffer{}
|
||||
err := binary.Write(&b, binary.LittleEndian, &m)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
if err := binary.Write(&b, binary.LittleEndian, &msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b.Bytes()
|
||||
if b.Len() != expMsgBodyLen {
|
||||
return nil, errors.New("incorrect body length")
|
||||
}
|
||||
|
||||
payload := strings.ToUpper(domainName + workstationName)
|
||||
if _, err := b.WriteString(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
|
|
@ -3,10 +3,24 @@ package ntlmssp
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetDomain : parse domain name from based on slashes in the input
|
||||
func GetDomain(user string) (string, string) {
|
||||
domain := ""
|
||||
|
||||
if strings.Contains(user, "\\") {
|
||||
ucomponents := strings.SplitN(user, "\\", 2)
|
||||
domain = ucomponents[0]
|
||||
user = ucomponents[1]
|
||||
}
|
||||
return user, domain
|
||||
}
|
||||
|
||||
//Negotiator is a http.Roundtripper decorator that automatically
|
||||
//converts basic authentication to NTLM/Negotiate authentication when appropriate.
|
||||
type Negotiator struct{ http.RoundTripper }
|
||||
|
@ -47,9 +61,10 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error)
|
|||
}
|
||||
|
||||
resauth := authheader(res.Header.Get("Www-Authenticate"))
|
||||
if !resauth.IsNegotiate() {
|
||||
if !resauth.IsNegotiate() && !resauth.IsNTLM() {
|
||||
// Unauthorized, Negotiate not requested, let's try with basic auth
|
||||
req.Header.Set("Authorization", string(reqauth))
|
||||
io.Copy(ioutil.Discard, res.Body)
|
||||
res.Body.Close()
|
||||
req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
|
||||
|
||||
|
@ -63,8 +78,9 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error)
|
|||
resauth = authheader(res.Header.Get("Www-Authenticate"))
|
||||
}
|
||||
|
||||
if resauth.IsNegotiate() {
|
||||
if resauth.IsNegotiate() || resauth.IsNTLM() {
|
||||
// 401 with request:Basic and response:Negotiate
|
||||
io.Copy(ioutil.Discard, res.Body)
|
||||
res.Body.Close()
|
||||
|
||||
// recycle credentials
|
||||
|
@ -73,9 +89,21 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// get domain from username
|
||||
domain := ""
|
||||
u, domain = GetDomain(u)
|
||||
|
||||
// send negotiate
|
||||
negotiateMessage := NewNegotiateMessage()
|
||||
req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(negotiateMessage))
|
||||
negotiateMessage, err := NewNegotiateMessage(domain, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resauth.IsNTLM() {
|
||||
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()))
|
||||
|
||||
res, err = rt.RoundTrip(req)
|
||||
|
@ -89,10 +117,11 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error)
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !resauth.IsNegotiate() || len(challengeMessage) == 0 {
|
||||
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()
|
||||
|
||||
// send authenticate
|
||||
|
@ -100,7 +129,12 @@ 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))
|
||||
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()))
|
||||
|
||||
res, err = rt.RoundTrip(req)
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package ntlmssp
|
||||
|
||||
// Version is a struct representing https://msdn.microsoft.com/en-us/library/cc236654.aspx
|
||||
type Version struct {
|
||||
ProductMajorVersion uint8
|
||||
ProductMinorVersion uint8
|
||||
ProductBuild uint16
|
||||
_ [3]byte
|
||||
NTLMRevisionCurrent uint8
|
||||
}
|
||||
|
||||
// DefaultVersion returns a Version with "sensible" defaults (Windows 7)
|
||||
func DefaultVersion() Version {
|
||||
return Version{
|
||||
ProductMajorVersion: 6,
|
||||
ProductMinorVersion: 1,
|
||||
ProductBuild: 7601,
|
||||
NTLMRevisionCurrent: 15,
|
||||
}
|
||||
}
|
|
@ -117,9 +117,10 @@
|
|||
"versionExact": "v10.12.0"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "TgrN0l/E16deTlLYNt8wf66urSU=",
|
||||
"checksumSHA1": "4LNEtQCFSZ/NbHBdkB7XPrwO+kI=",
|
||||
"path": "github.com/Azure/go-ntlmssp",
|
||||
"revision": "e0b63eb299a769ea4b04dadfe530f6074b277afb"
|
||||
"revision": "4a21cbd618b459155f8b8ee7f4491cd54f5efa77",
|
||||
"revisionTime": "2018-08-10T17:55:52Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "++XyYR/ZfYoCxGn5vTlXzQZ9eo0=",
|
||||
|
|
Loading…
Reference in New Issue