aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Azure
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/Azure')
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/.travis.yml17
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/LICENSE21
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/README.md29
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/authenticate_message.go183
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/authheader.go37
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/avids.go17
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/challenge_message.go82
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/messageheader.go21
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/negotiate_flags.go52
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/negotiate_message.go64
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/negotiator.go144
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/nlmp.go51
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/unicode.go29
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/varfield.go40
-rw-r--r--vendor/github.com/Azure/go-ntlmssp/version.go20
15 files changed, 807 insertions, 0 deletions
diff --git a/vendor/github.com/Azure/go-ntlmssp/.travis.yml b/vendor/github.com/Azure/go-ntlmssp/.travis.yml
new file mode 100644
index 0000000000..23c95fe951
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/.travis.yml
@@ -0,0 +1,17 @@
+sudo: false
+
+language: go
+
+before_script:
+ - go get -u golang.org/x/lint/golint
+
+go:
+ - 1.10.x
+ - master
+
+script:
+ - test -z "$(gofmt -s -l . | tee /dev/stderr)"
+ - test -z "$(golint ./... | tee /dev/stderr)"
+ - go vet ./...
+ - go build -v ./...
+ - go test -v ./...
diff --git a/vendor/github.com/Azure/go-ntlmssp/LICENSE b/vendor/github.com/Azure/go-ntlmssp/LICENSE
new file mode 100644
index 0000000000..dc1cf39d13
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Microsoft
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/Azure/go-ntlmssp/README.md b/vendor/github.com/Azure/go-ntlmssp/README.md
new file mode 100644
index 0000000000..55cdcefab7
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/README.md
@@ -0,0 +1,29 @@
+# go-ntlmssp
+Golang package that provides NTLM/Negotiate authentication over HTTP
+
+[![GoDoc](https://godoc.org/github.com/Azure/go-ntlmssp?status.svg)](https://godoc.org/github.com/Azure/go-ntlmssp) [![Build Status](https://travis-ci.org/Azure/go-ntlmssp.svg?branch=dev)](https://travis-ci.org/Azure/go-ntlmssp)
+
+Protocol details from https://msdn.microsoft.com/en-us/library/cc236621.aspx
+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.
diff --git a/vendor/github.com/Azure/go-ntlmssp/authenticate_message.go b/vendor/github.com/Azure/go-ntlmssp/authenticate_message.go
new file mode 100644
index 0000000000..c8930680c5
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/authenticate_message.go
@@ -0,0 +1,183 @@
+package ntlmssp
+
+import (
+ "bytes"
+ "crypto/rand"
+ "encoding/binary"
+ "encoding/hex"
+ "errors"
+ "strings"
+ "time"
+)
+
+type authenicateMessage struct {
+ LmChallengeResponse []byte
+ NtChallengeResponse []byte
+
+ TargetName string
+ UserName string
+
+ // only set if negotiateFlag_NTLMSSP_NEGOTIATE_KEY_EXCH
+ EncryptedRandomSessionKey []byte
+
+ NegotiateFlags negotiateFlags
+
+ MIC []byte
+}
+
+type authenticateMessageFields struct {
+ messageHeader
+ LmChallengeResponse varField
+ NtChallengeResponse varField
+ TargetName varField
+ UserName varField
+ Workstation varField
+ _ [8]byte
+ NegotiateFlags negotiateFlags
+}
+
+func (m authenicateMessage) MarshalBinary() ([]byte, error) {
+ if !m.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEUNICODE) {
+ return nil, errors.New("Only unicode is supported")
+ }
+
+ target, user := toUnicode(m.TargetName), toUnicode(m.UserName)
+ workstation := toUnicode("go-ntlmssp")
+
+ ptr := binary.Size(&authenticateMessageFields{})
+ f := authenticateMessageFields{
+ messageHeader: newMessageHeader(3),
+ NegotiateFlags: m.NegotiateFlags,
+ LmChallengeResponse: newVarField(&ptr, len(m.LmChallengeResponse)),
+ NtChallengeResponse: newVarField(&ptr, len(m.NtChallengeResponse)),
+ TargetName: newVarField(&ptr, len(target)),
+ UserName: newVarField(&ptr, len(user)),
+ Workstation: newVarField(&ptr, len(workstation)),
+ }
+
+ f.NegotiateFlags.Unset(negotiateFlagNTLMSSPNEGOTIATEVERSION)
+
+ b := bytes.Buffer{}
+ if err := binary.Write(&b, binary.LittleEndian, &f); err != nil {
+ return nil, err
+ }
+ if err := binary.Write(&b, binary.LittleEndian, &m.LmChallengeResponse); err != nil {
+ return nil, err
+ }
+ if err := binary.Write(&b, binary.LittleEndian, &m.NtChallengeResponse); err != nil {
+ return nil, err
+ }
+ if err := binary.Write(&b, binary.LittleEndian, &target); err != nil {
+ return nil, err
+ }
+ if err := binary.Write(&b, binary.LittleEndian, &user); err != nil {
+ return nil, err
+ }
+ if err := binary.Write(&b, binary.LittleEndian, &workstation); err != nil {
+ return nil, err
+ }
+
+ return b.Bytes(), nil
+}
+
+//ProcessChallenge crafts an AUTHENTICATE message in response to the CHALLENGE message
+//that was received from the server
+func ProcessChallenge(challengeMessageData []byte, user, password string) ([]byte, error) {
+ if user == "" && password == "" {
+ return nil, errors.New("Anonymous authentication not supported")
+ }
+
+ var cm challengeMessage
+ if err := cm.UnmarshalBinary(challengeMessageData); err != nil {
+ return nil, err
+ }
+
+ if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATELMKEY) {
+ return nil, errors.New("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)")
+ }
+ if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEKEYEXCH) {
+ return nil, errors.New("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)")
+ }
+
+ am := authenicateMessage{
+ UserName: user,
+ TargetName: cm.TargetName,
+ NegotiateFlags: cm.NegotiateFlags,
+ }
+
+ timestamp := cm.TargetInfo[avIDMsvAvTimestamp]
+ if timestamp == nil { // no time sent, take current time
+ ft := uint64(time.Now().UnixNano()) / 100
+ ft += 116444736000000000 // add time between unix & windows offset
+ timestamp = make([]byte, 8)
+ binary.LittleEndian.PutUint64(timestamp, ft)
+ }
+
+ clientChallenge := make([]byte, 8)
+ rand.Reader.Read(clientChallenge)
+
+ ntlmV2Hash := getNtlmV2Hash(password, user, cm.TargetName)
+
+ am.NtChallengeResponse = computeNtlmV2Response(ntlmV2Hash,
+ cm.ServerChallenge[:], clientChallenge, timestamp, cm.TargetInfoRaw)
+
+ if cm.TargetInfoRaw == nil {
+ am.LmChallengeResponse = computeLmV2Response(ntlmV2Hash,
+ cm.ServerChallenge[:], clientChallenge)
+ }
+ return am.MarshalBinary()
+}
+
+func ProcessChallengeWithHash(challengeMessageData []byte, user, hash string) ([]byte, error) {
+ if user == "" && hash == "" {
+ return nil, errors.New("Anonymous authentication not supported")
+ }
+
+ var cm challengeMessage
+ if err := cm.UnmarshalBinary(challengeMessageData); err != nil {
+ return nil, err
+ }
+
+ if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATELMKEY) {
+ return nil, errors.New("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)")
+ }
+ if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEKEYEXCH) {
+ return nil, errors.New("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)")
+ }
+
+ am := authenicateMessage{
+ UserName: user,
+ TargetName: cm.TargetName,
+ NegotiateFlags: cm.NegotiateFlags,
+ }
+
+ timestamp := cm.TargetInfo[avIDMsvAvTimestamp]
+ if timestamp == nil { // no time sent, take current time
+ ft := uint64(time.Now().UnixNano()) / 100
+ ft += 116444736000000000 // add time between unix & windows offset
+ timestamp = make([]byte, 8)
+ binary.LittleEndian.PutUint64(timestamp, ft)
+ }
+
+ clientChallenge := make([]byte, 8)
+ rand.Reader.Read(clientChallenge)
+
+ hashParts := strings.Split(hash, ":")
+ if len(hashParts) > 1 {
+ hash = hashParts[1]
+ }
+ hashBytes, err := hex.DecodeString(hash)
+ if err != nil {
+ return nil, err
+ }
+ ntlmV2Hash := hmacMd5(hashBytes, toUnicode(strings.ToUpper(user)+cm.TargetName))
+
+ am.NtChallengeResponse = computeNtlmV2Response(ntlmV2Hash,
+ cm.ServerChallenge[:], clientChallenge, timestamp, cm.TargetInfoRaw)
+
+ if cm.TargetInfoRaw == nil {
+ am.LmChallengeResponse = computeLmV2Response(ntlmV2Hash,
+ cm.ServerChallenge[:], clientChallenge)
+ }
+ return am.MarshalBinary()
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/authheader.go b/vendor/github.com/Azure/go-ntlmssp/authheader.go
new file mode 100644
index 0000000000..aac3f77d10
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/authheader.go
@@ -0,0 +1,37 @@
+package ntlmssp
+
+import (
+ "encoding/base64"
+ "strings"
+)
+
+type authheader string
+
+func (h authheader) IsBasic() bool {
+ return strings.HasPrefix(string(h), "Basic ")
+}
+
+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 {
+ return nil, nil
+ }
+ return base64.StdEncoding.DecodeString(string(p[1]))
+}
+
+func (h authheader) GetBasicCreds() (username, password string, err error) {
+ d, err := h.GetData()
+ if err != nil {
+ return "", "", err
+ }
+ parts := strings.SplitN(string(d), ":", 2)
+ return parts[0], parts[1], nil
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/avids.go b/vendor/github.com/Azure/go-ntlmssp/avids.go
new file mode 100644
index 0000000000..196b5f1316
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/avids.go
@@ -0,0 +1,17 @@
+package ntlmssp
+
+type avID uint16
+
+const (
+ avIDMsvAvEOL avID = iota
+ avIDMsvAvNbComputerName
+ avIDMsvAvNbDomainName
+ avIDMsvAvDNSComputerName
+ avIDMsvAvDNSDomainName
+ avIDMsvAvDNSTreeName
+ avIDMsvAvFlags
+ avIDMsvAvTimestamp
+ avIDMsvAvSingleHost
+ avIDMsvAvTargetName
+ avIDMsvChannelBindings
+)
diff --git a/vendor/github.com/Azure/go-ntlmssp/challenge_message.go b/vendor/github.com/Azure/go-ntlmssp/challenge_message.go
new file mode 100644
index 0000000000..053b55e4ad
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/challenge_message.go
@@ -0,0 +1,82 @@
+package ntlmssp
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+)
+
+type challengeMessageFields struct {
+ messageHeader
+ TargetName varField
+ NegotiateFlags negotiateFlags
+ ServerChallenge [8]byte
+ _ [8]byte
+ TargetInfo varField
+}
+
+func (m challengeMessageFields) IsValid() bool {
+ return m.messageHeader.IsValid() && m.MessageType == 2
+}
+
+type challengeMessage struct {
+ challengeMessageFields
+ TargetName string
+ TargetInfo map[avID][]byte
+ TargetInfoRaw []byte
+}
+
+func (m *challengeMessage) UnmarshalBinary(data []byte) error {
+ r := bytes.NewReader(data)
+ err := binary.Read(r, binary.LittleEndian, &m.challengeMessageFields)
+ if err != nil {
+ return err
+ }
+ if !m.challengeMessageFields.IsValid() {
+ return fmt.Errorf("Message is not a valid challenge message: %+v", m.challengeMessageFields.messageHeader)
+ }
+
+ if m.challengeMessageFields.TargetName.Len > 0 {
+ m.TargetName, err = m.challengeMessageFields.TargetName.ReadStringFrom(data, m.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEUNICODE))
+ if err != nil {
+ return err
+ }
+ }
+
+ if m.challengeMessageFields.TargetInfo.Len > 0 {
+ d, err := m.challengeMessageFields.TargetInfo.ReadFrom(data)
+ m.TargetInfoRaw = d
+ if err != nil {
+ return err
+ }
+ m.TargetInfo = make(map[avID][]byte)
+ r := bytes.NewReader(d)
+ for {
+ var id avID
+ var l uint16
+ err = binary.Read(r, binary.LittleEndian, &id)
+ if err != nil {
+ return err
+ }
+ if id == avIDMsvAvEOL {
+ break
+ }
+
+ err = binary.Read(r, binary.LittleEndian, &l)
+ if err != nil {
+ return err
+ }
+ value := make([]byte, l)
+ n, err := r.Read(value)
+ if err != nil {
+ return err
+ }
+ if n != int(l) {
+ return fmt.Errorf("Expected to read %d bytes, got only %d", l, n)
+ }
+ m.TargetInfo[id] = value
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/messageheader.go b/vendor/github.com/Azure/go-ntlmssp/messageheader.go
new file mode 100644
index 0000000000..247e284652
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/messageheader.go
@@ -0,0 +1,21 @@
+package ntlmssp
+
+import (
+ "bytes"
+)
+
+var signature = [8]byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0}
+
+type messageHeader struct {
+ Signature [8]byte
+ MessageType uint32
+}
+
+func (h messageHeader) IsValid() bool {
+ return bytes.Equal(h.Signature[:], signature[:]) &&
+ h.MessageType > 0 && h.MessageType < 4
+}
+
+func newMessageHeader(messageType uint32) messageHeader {
+ return messageHeader{signature, messageType}
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/negotiate_flags.go b/vendor/github.com/Azure/go-ntlmssp/negotiate_flags.go
new file mode 100644
index 0000000000..5905c023d6
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/negotiate_flags.go
@@ -0,0 +1,52 @@
+package ntlmssp
+
+type negotiateFlags uint32
+
+const (
+ /*A*/ negotiateFlagNTLMSSPNEGOTIATEUNICODE negotiateFlags = 1 << 0
+ /*B*/ negotiateFlagNTLMNEGOTIATEOEM = 1 << 1
+ /*C*/ negotiateFlagNTLMSSPREQUESTTARGET = 1 << 2
+
+ /*D*/
+ negotiateFlagNTLMSSPNEGOTIATESIGN = 1 << 4
+ /*E*/ negotiateFlagNTLMSSPNEGOTIATESEAL = 1 << 5
+ /*F*/ negotiateFlagNTLMSSPNEGOTIATEDATAGRAM = 1 << 6
+ /*G*/ negotiateFlagNTLMSSPNEGOTIATELMKEY = 1 << 7
+
+ /*H*/
+ negotiateFlagNTLMSSPNEGOTIATENTLM = 1 << 9
+
+ /*J*/
+ negotiateFlagANONYMOUS = 1 << 11
+ /*K*/ negotiateFlagNTLMSSPNEGOTIATEOEMDOMAINSUPPLIED = 1 << 12
+ /*L*/ negotiateFlagNTLMSSPNEGOTIATEOEMWORKSTATIONSUPPLIED = 1 << 13
+
+ /*M*/
+ negotiateFlagNTLMSSPNEGOTIATEALWAYSSIGN = 1 << 15
+ /*N*/ negotiateFlagNTLMSSPTARGETTYPEDOMAIN = 1 << 16
+ /*O*/ negotiateFlagNTLMSSPTARGETTYPESERVER = 1 << 17
+
+ /*P*/
+ negotiateFlagNTLMSSPNEGOTIATEEXTENDEDSESSIONSECURITY = 1 << 19
+ /*Q*/ negotiateFlagNTLMSSPNEGOTIATEIDENTIFY = 1 << 20
+
+ /*R*/
+ negotiateFlagNTLMSSPREQUESTNONNTSESSIONKEY = 1 << 22
+ /*S*/ negotiateFlagNTLMSSPNEGOTIATETARGETINFO = 1 << 23
+
+ /*T*/
+ negotiateFlagNTLMSSPNEGOTIATEVERSION = 1 << 25
+
+ /*U*/
+ negotiateFlagNTLMSSPNEGOTIATE128 = 1 << 29
+ /*V*/ negotiateFlagNTLMSSPNEGOTIATEKEYEXCH = 1 << 30
+ /*W*/ negotiateFlagNTLMSSPNEGOTIATE56 = 1 << 31
+)
+
+func (field negotiateFlags) Has(flags negotiateFlags) bool {
+ return field&flags == flags
+}
+
+func (field *negotiateFlags) Unset(flags negotiateFlags) {
+ *field = *field ^ (*field & flags)
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/negotiate_message.go b/vendor/github.com/Azure/go-ntlmssp/negotiate_message.go
new file mode 100644
index 0000000000..e466a9861d
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/negotiate_message.go
@@ -0,0 +1,64 @@
+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(domainName, workstationName string) ([]byte, error) {
+ payloadOffset := expMsgBodyLen
+ flags := defaultFlags
+
+ if domainName != "" {
+ flags |= negotiateFlagNTLMSSPNEGOTIATEOEMDOMAINSUPPLIED
+ }
+
+ 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{}
+ if err := binary.Write(&b, binary.LittleEndian, &msg); err != nil {
+ return nil, err
+ }
+ 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
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/negotiator.go b/vendor/github.com/Azure/go-ntlmssp/negotiator.go
new file mode 100644
index 0000000000..7705eae4f8
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/negotiator.go
@@ -0,0 +1,144 @@
+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 }
+
+//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
+ }
+ // If it is not basic auth, just round trip the request as usual
+ reqauth := authheader(req.Header.Get("Authorization"))
+ if !reqauth.IsBasic() {
+ return rt.RoundTrip(req)
+ }
+ // Save request body
+ body := bytes.Buffer{}
+ if req.Body != nil {
+ _, err = body.ReadFrom(req.Body)
+ 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
+ }
+
+ resauth := authheader(res.Header.Get("Www-Authenticate"))
+ 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()))
+
+ res, err = rt.RoundTrip(req)
+ if err != nil {
+ return nil, err
+ }
+ if res.StatusCode != http.StatusUnauthorized {
+ return res, err
+ }
+ resauth = authheader(res.Header.Get("Www-Authenticate"))
+ }
+
+ if resauth.IsNegotiate() || resauth.IsNTLM() {
+ // 401 with request:Basic and response:Negotiate
+ io.Copy(ioutil.Discard, res.Body)
+ res.Body.Close()
+
+ // recycle credentials
+ u, p, err := reqauth.GetBasicCreds()
+ if err != nil {
+ return nil, err
+ }
+
+ // get domain from username
+ domain := ""
+ u, domain = GetDomain(u)
+
+ // send negotiate
+ 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)
+ 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() || 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
+ authenticateMessage, err := ProcessChallenge(challengeMessage, u, p)
+ if err != nil {
+ return nil, err
+ }
+ 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()))
+
+ return rt.RoundTrip(req)
+ }
+
+ return res, err
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/nlmp.go b/vendor/github.com/Azure/go-ntlmssp/nlmp.go
new file mode 100644
index 0000000000..1e65abe8b5
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/nlmp.go
@@ -0,0 +1,51 @@
+// Package ntlmssp provides NTLM/Negotiate authentication over HTTP
+//
+// Protocol details from https://msdn.microsoft.com/en-us/library/cc236621.aspx,
+// 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.
+package ntlmssp
+
+import (
+ "crypto/hmac"
+ "crypto/md5"
+ "golang.org/x/crypto/md4"
+ "strings"
+)
+
+func getNtlmV2Hash(password, username, target string) []byte {
+ return hmacMd5(getNtlmHash(password), toUnicode(strings.ToUpper(username)+target))
+}
+
+func getNtlmHash(password string) []byte {
+ hash := md4.New()
+ hash.Write(toUnicode(password))
+ return hash.Sum(nil)
+}
+
+func computeNtlmV2Response(ntlmV2Hash, serverChallenge, clientChallenge,
+ timestamp, targetInfo []byte) []byte {
+
+ temp := []byte{1, 1, 0, 0, 0, 0, 0, 0}
+ temp = append(temp, timestamp...)
+ temp = append(temp, clientChallenge...)
+ temp = append(temp, 0, 0, 0, 0)
+ temp = append(temp, targetInfo...)
+ temp = append(temp, 0, 0, 0, 0)
+
+ NTProofStr := hmacMd5(ntlmV2Hash, serverChallenge, temp)
+ return append(NTProofStr, temp...)
+}
+
+func computeLmV2Response(ntlmV2Hash, serverChallenge, clientChallenge []byte) []byte {
+ return append(hmacMd5(ntlmV2Hash, serverChallenge, clientChallenge), clientChallenge...)
+}
+
+func hmacMd5(key []byte, data ...[]byte) []byte {
+ mac := hmac.New(md5.New, key)
+ for _, d := range data {
+ mac.Write(d)
+ }
+ return mac.Sum(nil)
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/unicode.go b/vendor/github.com/Azure/go-ntlmssp/unicode.go
new file mode 100644
index 0000000000..7b4f47163d
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/unicode.go
@@ -0,0 +1,29 @@
+package ntlmssp
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "unicode/utf16"
+)
+
+// helper func's for dealing with Windows Unicode (UTF16LE)
+
+func fromUnicode(d []byte) (string, error) {
+ if len(d)%2 > 0 {
+ return "", errors.New("Unicode (UTF 16 LE) specified, but uneven data length")
+ }
+ s := make([]uint16, len(d)/2)
+ err := binary.Read(bytes.NewReader(d), binary.LittleEndian, &s)
+ if err != nil {
+ return "", err
+ }
+ return string(utf16.Decode(s)), nil
+}
+
+func toUnicode(s string) []byte {
+ uints := utf16.Encode([]rune(s))
+ b := bytes.Buffer{}
+ binary.Write(&b, binary.LittleEndian, &uints)
+ return b.Bytes()
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/varfield.go b/vendor/github.com/Azure/go-ntlmssp/varfield.go
new file mode 100644
index 0000000000..15f9aa113d
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/varfield.go
@@ -0,0 +1,40 @@
+package ntlmssp
+
+import (
+ "errors"
+)
+
+type varField struct {
+ Len uint16
+ MaxLen uint16
+ BufferOffset uint32
+}
+
+func (f varField) ReadFrom(buffer []byte) ([]byte, error) {
+ if len(buffer) < int(f.BufferOffset+uint32(f.Len)) {
+ return nil, errors.New("Error reading data, varField extends beyond buffer")
+ }
+ return buffer[f.BufferOffset : f.BufferOffset+uint32(f.Len)], nil
+}
+
+func (f varField) ReadStringFrom(buffer []byte, unicode bool) (string, error) {
+ d, err := f.ReadFrom(buffer)
+ if err != nil {
+ return "", err
+ }
+ if unicode { // UTF-16LE encoding scheme
+ return fromUnicode(d)
+ }
+ // OEM encoding, close enough to ASCII, since no code page is specified
+ return string(d), err
+}
+
+func newVarField(ptr *int, fieldsize int) varField {
+ f := varField{
+ Len: uint16(fieldsize),
+ MaxLen: uint16(fieldsize),
+ BufferOffset: uint32(*ptr),
+ }
+ *ptr += fieldsize
+ return f
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/version.go b/vendor/github.com/Azure/go-ntlmssp/version.go
new file mode 100644
index 0000000000..6d84892124
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/version.go
@@ -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,
+ }
+}