Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
adaea700ae | |||
468f0240be | |||
f8cadb2287 | |||
befb826740 | |||
f1ae4c4171 |
@@ -1,100 +0,0 @@
|
|||||||
version: 2.1
|
|
||||||
|
|
||||||
orbs:
|
|
||||||
win: circleci/windows@1.0.0
|
|
||||||
|
|
||||||
workflows:
|
|
||||||
workflow:
|
|
||||||
jobs:
|
|
||||||
- go-test:
|
|
||||||
name: Go 1.14
|
|
||||||
docker-image: circleci/golang:1.14
|
|
||||||
build-as-module: true
|
|
||||||
- go-test:
|
|
||||||
name: Go 1.13
|
|
||||||
docker-image: circleci/golang:1.13
|
|
||||||
build-as-module: true
|
|
||||||
- go-test:
|
|
||||||
name: Go 1.12
|
|
||||||
docker-image: circleci/golang:1.12
|
|
||||||
- go-test:
|
|
||||||
name: Go 1.11
|
|
||||||
docker-image: circleci/golang:1.11
|
|
||||||
- go-test:
|
|
||||||
name: Go 1.10
|
|
||||||
docker-image: circleci/golang:1.10
|
|
||||||
- go-test:
|
|
||||||
name: Go 1.9
|
|
||||||
docker-image: circleci/golang:1.9
|
|
||||||
- go-test:
|
|
||||||
name: Go 1.8
|
|
||||||
docker-image: circleci/golang:1.8
|
|
||||||
- go-test-windows:
|
|
||||||
name: Windows
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
go-test:
|
|
||||||
parameters:
|
|
||||||
docker-image:
|
|
||||||
type: string
|
|
||||||
build-as-module:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
|
|
||||||
docker:
|
|
||||||
- image: <<parameters.docker-image>>
|
|
||||||
environment:
|
|
||||||
CIRCLE_TEST_REPORTS: /tmp/circle-reports
|
|
||||||
CIRCLE_ARTIFACTS: /tmp/circle-artifacts
|
|
||||||
COMMON_GO_PACKAGES: >
|
|
||||||
github.com/jstemmer/go-junit-report
|
|
||||||
|
|
||||||
working_directory: /go/src/github.com/launchdarkly/go-ntlmssp
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: go get -u $COMMON_GO_PACKAGES
|
|
||||||
|
|
||||||
- unless:
|
|
||||||
condition: <<parameters.build-as-module>>
|
|
||||||
steps:
|
|
||||||
- run: go get -t .
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Run tests
|
|
||||||
command: |
|
|
||||||
mkdir -p $CIRCLE_TEST_REPORTS
|
|
||||||
mkdir -p $CIRCLE_ARTIFACTS
|
|
||||||
trap "go-junit-report < $CIRCLE_ARTIFACTS/report.txt > $CIRCLE_TEST_REPORTS/junit.xml" EXIT
|
|
||||||
go test -v -race | tee $CIRCLE_ARTIFACTS/report.txt
|
|
||||||
|
|
||||||
- store_test_results:
|
|
||||||
path: /tmp/circle-reports
|
|
||||||
|
|
||||||
- store_artifacts:
|
|
||||||
path: /tmp/circle-artifacts
|
|
||||||
|
|
||||||
go-test-windows:
|
|
||||||
executor:
|
|
||||||
name: win/vs2019
|
|
||||||
shell: powershell.exe
|
|
||||||
|
|
||||||
environment:
|
|
||||||
GOPATH: C:\Users\VssAdministrator\go
|
|
||||||
PACKAGE_PATH: github.com/launchdarkly/go-ntlmssp
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: go version
|
|
||||||
- run:
|
|
||||||
name: move source
|
|
||||||
command: |
|
|
||||||
go env GOPATH
|
|
||||||
mkdir ${env:GOPATH}\src\${env:PACKAGE_PATH}
|
|
||||||
mv * ${env:GOPATH}\src\${env:PACKAGE_PATH}
|
|
||||||
- run:
|
|
||||||
name: build and test
|
|
||||||
command: |
|
|
||||||
cd ${env:GOPATH}\src\${env:PACKAGE_PATH}
|
|
||||||
go get -t .
|
|
||||||
go test -v -race ./...
|
|
@@ -1,8 +0,0 @@
|
|||||||
template:
|
|
||||||
name: go
|
|
||||||
env:
|
|
||||||
LD_RELEASE_GO_IMPORT_PATH: github.com/launchdarkly/go-ntlmssp
|
|
||||||
|
|
||||||
publications:
|
|
||||||
- url: https://godoc.org/github.com/launchdarkly/go-ntlmssp
|
|
||||||
description: documentation
|
|
@@ -3,6 +3,7 @@ package ntlmssp
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
@@ -78,22 +79,30 @@ func (m authenicateMessage) MarshalBinary() ([]byte, error) {
|
|||||||
return b.Bytes(), nil
|
return b.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//ProcessChallenge crafts an AUTHENTICATE message in response to the CHALLENGE message
|
// ProcessChallenge crafts an AUTHENTICATE message in response to the CHALLENGE message
|
||||||
//that was received from the server
|
// that was received from the server
|
||||||
func ProcessChallenge(challengeMessageData []byte, user, password string) ([]byte, error) {
|
func ProcessChallenge(challengeMessageData []byte, user, password string) ([]byte, error) {
|
||||||
if user == "" && password == "" {
|
if user == "" && password == "" {
|
||||||
|
PrintDebug("User %s, Pass Length %d", user, len(password))
|
||||||
return nil, errors.New("Anonymous authentication not supported")
|
return nil, errors.New("Anonymous authentication not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
PrintDebug("Received NTLM Type 2 Challenge: %s", base64.StdEncoding.EncodeToString(challengeMessageData))
|
||||||
|
DecodeNTLMMessage(challengeMessageData)
|
||||||
|
|
||||||
var cm challengeMessage
|
var cm challengeMessage
|
||||||
if err := cm.UnmarshalBinary(challengeMessageData); err != nil {
|
if err := cm.UnmarshalBinary(challengeMessageData); err != nil {
|
||||||
|
PrintDebug("Failed to unmarshal challenge data")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATELMKEY) {
|
if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATELMKEY) {
|
||||||
|
PrintDebug("server requested NTLM v1")
|
||||||
return nil, errors.New("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)")
|
return nil, errors.New("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)")
|
||||||
}
|
}
|
||||||
if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEKEYEXCH) {
|
if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEKEYEXCH) {
|
||||||
|
PrintDebug("Key exchange requested but not supported")
|
||||||
return nil, errors.New("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)")
|
return nil, errors.New("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +124,7 @@ func ProcessChallenge(challengeMessageData []byte, user, password string) ([]byt
|
|||||||
rand.Reader.Read(clientChallenge)
|
rand.Reader.Read(clientChallenge)
|
||||||
|
|
||||||
ntlmV2Hash := getNtlmV2Hash(password, user, cm.TargetName)
|
ntlmV2Hash := getNtlmV2Hash(password, user, cm.TargetName)
|
||||||
|
PrintDebug("NTLM V2 hash '%s'", base64.StdEncoding.EncodeToString(ntlmV2Hash))
|
||||||
|
|
||||||
am.NtChallengeResponse = computeNtlmV2Response(ntlmV2Hash,
|
am.NtChallengeResponse = computeNtlmV2Response(ntlmV2Hash,
|
||||||
cm.ServerChallenge[:], clientChallenge, timestamp, cm.TargetInfoRaw)
|
cm.ServerChallenge[:], clientChallenge, timestamp, cm.TargetInfoRaw)
|
||||||
@@ -124,5 +134,7 @@ func ProcessChallenge(challengeMessageData []byte, user, password string) ([]byt
|
|||||||
cm.ServerChallenge[:], clientChallenge)
|
cm.ServerChallenge[:], clientChallenge)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PrintDebug("Challenge response:\nNT '%s';\nLM '%s'", base64.StdEncoding.EncodeToString(am.NtChallengeResponse), base64.StdEncoding.EncodeToString(am.LmChallengeResponse))
|
||||||
|
|
||||||
return am.MarshalBinary()
|
return am.MarshalBinary()
|
||||||
}
|
}
|
||||||
|
153
debug.go
Normal file
153
debug.go
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
package ntlmssp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Debugging flag
|
||||||
|
var DebugMode = true
|
||||||
|
|
||||||
|
// PrintDebug logs debug messages when DebugMode is enabled
|
||||||
|
func PrintDebug(format string, args ...interface{}) {
|
||||||
|
if DebugMode {
|
||||||
|
fmt.Printf(format+"\n", args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeNTLMMessage decodes NTLM messages and prints details
|
||||||
|
func DecodeNTLMMessage(blob []byte) {
|
||||||
|
if len(blob) < 12 {
|
||||||
|
PrintDebug("Invalid NTLM message (too short)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(blob[:8]) != "NTLMSSP\x00" {
|
||||||
|
PrintDebug("Invalid NTLM signature")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msgType := binary.LittleEndian.Uint32(blob[8:12])
|
||||||
|
switch msgType {
|
||||||
|
case 1:
|
||||||
|
DecodeType1Message(blob)
|
||||||
|
case 2:
|
||||||
|
DecodeType2Message(blob)
|
||||||
|
case 3:
|
||||||
|
DecodeType3Message(blob)
|
||||||
|
default:
|
||||||
|
PrintDebug("Unknown NTLM message type: %d", msgType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeType1Message prints details of an NTLM Type 1 message
|
||||||
|
func DecodeType1Message(blob []byte) {
|
||||||
|
PrintDebug("==== Type1 ----")
|
||||||
|
PrintDebug("Signature: NTLMSSP")
|
||||||
|
PrintDebug("Type: 1")
|
||||||
|
|
||||||
|
if len(blob) < 32 {
|
||||||
|
PrintDebug("Invalid NTLM Type 1 message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := binary.LittleEndian.Uint32(blob[12:16])
|
||||||
|
PrintDebug("Flags: %08X", flags)
|
||||||
|
PrintDebug(DecodeFlags(flags))
|
||||||
|
|
||||||
|
domainLen := binary.LittleEndian.Uint16(blob[16:18])
|
||||||
|
domainMaxLen := binary.LittleEndian.Uint16(blob[18:20])
|
||||||
|
domainOffset := binary.LittleEndian.Uint32(blob[20:24])
|
||||||
|
|
||||||
|
workstationLen := binary.LittleEndian.Uint16(blob[24:26])
|
||||||
|
workstationMaxLen := binary.LittleEndian.Uint16(blob[26:28])
|
||||||
|
workstationOffset := binary.LittleEndian.Uint32(blob[28:32])
|
||||||
|
|
||||||
|
if domainMaxLen > 0 && int(domainOffset+uint32(domainLen)) <= len(blob) {
|
||||||
|
domain := string(blob[domainOffset : domainOffset+uint32(domainLen)])
|
||||||
|
PrintDebug("Domain: %s", domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
if workstationMaxLen > 0 && int(workstationOffset+uint32(workstationLen)) <= len(blob) {
|
||||||
|
workstation := string(blob[workstationOffset : workstationOffset+uint32(workstationLen)])
|
||||||
|
PrintDebug("Workstation: %s", workstation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeType2Message prints details of an NTLM Type 2 message
|
||||||
|
func DecodeType2Message(blob []byte) {
|
||||||
|
PrintDebug("==== Type2 ----")
|
||||||
|
PrintDebug("Signature: NTLMSSP")
|
||||||
|
PrintDebug("Type: 2")
|
||||||
|
|
||||||
|
if len(blob) < 48 {
|
||||||
|
PrintDebug("Invalid NTLM Type 2 message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := binary.LittleEndian.Uint32(blob[20:24])
|
||||||
|
PrintDebug("Flags: %08X", flags)
|
||||||
|
PrintDebug(DecodeFlags(flags))
|
||||||
|
|
||||||
|
challenge := blob[24:32]
|
||||||
|
PrintDebug("Challenge: %X", challenge)
|
||||||
|
|
||||||
|
context := blob[40:48]
|
||||||
|
PrintDebug("Context: %X:%X", context[:4], context[4:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeType3Message prints details of an NTLM Type 3 message
|
||||||
|
func DecodeType3Message(blob []byte) {
|
||||||
|
PrintDebug("==== Type3 ----")
|
||||||
|
PrintDebug("Signature: NTLMSSP")
|
||||||
|
PrintDebug("Type: 3")
|
||||||
|
|
||||||
|
if len(blob) < 64 {
|
||||||
|
PrintDebug("Invalid NTLM Type 3 message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := binary.LittleEndian.Uint32(blob[60:64])
|
||||||
|
PrintDebug("Flags: %08X", flags)
|
||||||
|
PrintDebug(DecodeFlags(flags))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFlags returns a formatted string listing NTLM flag names
|
||||||
|
func DecodeFlags(flags uint32) string {
|
||||||
|
var flagStrings []string
|
||||||
|
|
||||||
|
flagMap := map[uint32]string{
|
||||||
|
0x00000001: "NTLMSSP_NEGOTIATE_UNICODE",
|
||||||
|
0x00000002: "NTLMSSP_NEGOTIATE_OEM",
|
||||||
|
0x00000004: "NTLMSSP_REQUEST_TARGET",
|
||||||
|
0x00000010: "NTLMSSP_NEGOTIATE_SIGN",
|
||||||
|
0x00000020: "NTLMSSP_NEGOTIATE_SEAL",
|
||||||
|
0x00000040: "NTLMSSP_NEGOTIATE_DATAGRAM",
|
||||||
|
0x00000080: "NTLMSSP_NEGOTIATE_LM_KEY",
|
||||||
|
0x00000100: "NTLMSSP_NEGOTIATE_NETWARE",
|
||||||
|
0x00000200: "NTLMSSP_NEGOTIATE_NTLM",
|
||||||
|
0x00000800: "NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED",
|
||||||
|
0x00001000: "NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED",
|
||||||
|
0x00002000: "NTLMSSP_NEGOTIATE_ALWAYS_SIGN",
|
||||||
|
0x00020000: "NTLMSSP_NEGOTIATE_TARGET_INFO",
|
||||||
|
0x00040000: "NTLMSSP_REQUEST_NON_NT_SESSION_KEY",
|
||||||
|
0x00080000: "NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY",
|
||||||
|
0x00100000: "NTLMSSP_NEGOTIATE_IDENTIFY",
|
||||||
|
0x00200000: "NTLMSSP_REQUEST_TARGET",
|
||||||
|
0x00800000: "NTLMSSP_TARGET_TYPE_DOMAIN",
|
||||||
|
0x01000000: "NTLMSSP_TARGET_TYPE_SERVER",
|
||||||
|
0x02000000: "NTLMSSP_TARGET_TYPE_SHARE",
|
||||||
|
0x08000000: "NTLMSSP_NEGOTIATE_KEY_EXCH",
|
||||||
|
0x20000000: "NTLMSSP_NEGOTIATE_128",
|
||||||
|
0x80000000: "NTLMSSP_NEGOTIATE_56",
|
||||||
|
}
|
||||||
|
|
||||||
|
for bit, name := range flagMap {
|
||||||
|
if flags&bit != 0 {
|
||||||
|
flagStrings = append(flagStrings, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(flagStrings, "\n")
|
||||||
|
}
|
6
go.mod
6
go.mod
@@ -1,5 +1,5 @@
|
|||||||
module github.com/launchdarkly/go-ntlmssp
|
module git.coadcorp.com/nathan/go-ntlmssp
|
||||||
|
|
||||||
go 1.13
|
go 1.24.1
|
||||||
|
|
||||||
require golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
|
require golang.org/x/crypto v0.36.0
|
||||||
|
9
go.sum
9
go.sum
@@ -1,7 +1,2 @@
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
|
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
|
@@ -4,7 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -21,12 +20,12 @@ func GetDomain(user string) (string, string) {
|
|||||||
return user, domain
|
return user, domain
|
||||||
}
|
}
|
||||||
|
|
||||||
//Negotiator is a http.Roundtripper decorator that automatically
|
// Negotiator is a http.Roundtripper decorator that automatically
|
||||||
//converts basic authentication to NTLM/Negotiate authentication when appropriate.
|
// converts basic authentication to NTLM/Negotiate authentication when appropriate.
|
||||||
type Negotiator struct{ http.RoundTripper }
|
type Negotiator struct{ http.RoundTripper }
|
||||||
|
|
||||||
//RoundTrip sends the request to the server, handling any authentication
|
// RoundTrip sends the request to the server, handling any authentication
|
||||||
//re-sends as needed.
|
// re-sends as needed.
|
||||||
func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error) {
|
func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error) {
|
||||||
// Use default round tripper if not provided
|
// Use default round tripper if not provided
|
||||||
rt := l.RoundTripper
|
rt := l.RoundTripper
|
||||||
@@ -47,7 +46,7 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
req.Body.Close()
|
req.Body.Close()
|
||||||
req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
|
req.Body = io.NopCloser(bytes.NewReader(body.Bytes()))
|
||||||
}
|
}
|
||||||
// first try anonymous, in case the server still finds us
|
// first try anonymous, in case the server still finds us
|
||||||
// authenticated from previous traffic
|
// authenticated from previous traffic
|
||||||
@@ -64,9 +63,9 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error)
|
|||||||
if !resauth.IsNegotiate() && !resauth.IsNTLM() {
|
if !resauth.IsNegotiate() && !resauth.IsNTLM() {
|
||||||
// Unauthorized, Negotiate not requested, let's try with basic auth
|
// Unauthorized, Negotiate not requested, let's try with basic auth
|
||||||
req.Header.Set("Authorization", string(reqauth))
|
req.Header.Set("Authorization", string(reqauth))
|
||||||
io.Copy(ioutil.Discard, res.Body)
|
io.Copy(io.Discard, res.Body)
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
|
req.Body = io.NopCloser(bytes.NewReader(body.Bytes()))
|
||||||
|
|
||||||
res, err = rt.RoundTrip(req)
|
res, err = rt.RoundTrip(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -80,7 +79,7 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error)
|
|||||||
|
|
||||||
if resauth.IsNegotiate() || resauth.IsNTLM() {
|
if resauth.IsNegotiate() || resauth.IsNTLM() {
|
||||||
// 401 with request:Basic and response:Negotiate
|
// 401 with request:Basic and response:Negotiate
|
||||||
io.Copy(ioutil.Discard, res.Body)
|
io.Copy(io.Discard, res.Body)
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
|
|
||||||
// recycle credentials
|
// recycle credentials
|
||||||
@@ -98,13 +97,18 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
PrintDebug("Generated NTLM Type 1 Message: %s", base64.StdEncoding.EncodeToString(negotiateMessage))
|
||||||
|
DecodeNTLMMessage(negotiateMessage)
|
||||||
|
|
||||||
if resauth.IsNTLM() {
|
if resauth.IsNTLM() {
|
||||||
req.Header.Set("Authorization", "NTLM "+base64.StdEncoding.EncodeToString(negotiateMessage))
|
req.Header.Set("Authorization", "NTLM "+base64.StdEncoding.EncodeToString(negotiateMessage))
|
||||||
} else {
|
} else {
|
||||||
req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(negotiateMessage))
|
req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(negotiateMessage))
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
|
req.Body = io.NopCloser(bytes.NewReader(body.Bytes()))
|
||||||
|
|
||||||
res, err = rt.RoundTrip(req)
|
res, err = rt.RoundTrip(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -117,11 +121,17 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
//PrintDebug("Received NTLM Type 2 Challenge: %s", base64.StdEncoding.EncodeToString(challengeMessage))
|
||||||
|
//DecodeNTLMMessage(challengeMessage)
|
||||||
|
|
||||||
if !(resauth.IsNegotiate() || resauth.IsNTLM()) || len(challengeMessage) == 0 {
|
if !(resauth.IsNegotiate() || resauth.IsNTLM()) || len(challengeMessage) == 0 {
|
||||||
// Negotiation failed, let client deal with response
|
// Negotiation failed, let client deal with response
|
||||||
|
PrintDebug("Negotiation failed")
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
io.Copy(ioutil.Discard, res.Body)
|
io.Copy(io.Discard, res.Body)
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
|
|
||||||
// send authenticate
|
// send authenticate
|
||||||
@@ -129,13 +139,18 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
PrintDebug("Generated NTLM Type 3 Response: %s", base64.StdEncoding.EncodeToString(authenticateMessage))
|
||||||
|
DecodeNTLMMessage(authenticateMessage)
|
||||||
|
|
||||||
if resauth.IsNTLM() {
|
if resauth.IsNTLM() {
|
||||||
req.Header.Set("Authorization", "NTLM "+base64.StdEncoding.EncodeToString(authenticateMessage))
|
req.Header.Set("Authorization", "NTLM "+base64.StdEncoding.EncodeToString(authenticateMessage))
|
||||||
} else {
|
} else {
|
||||||
req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(authenticateMessage))
|
req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(authenticateMessage))
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
|
req.Body = io.NopCloser(bytes.NewReader(body.Bytes()))
|
||||||
|
|
||||||
res, err = rt.RoundTrip(req)
|
res, err = rt.RoundTrip(req)
|
||||||
}
|
}
|
||||||
|
10
nlmp.go
10
nlmp.go
@@ -10,8 +10,9 @@ package ntlmssp
|
|||||||
import (
|
import (
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"golang.org/x/crypto/md4"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/md4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getNtlmV2Hash(password, username, target string) []byte {
|
func getNtlmV2Hash(password, username, target string) []byte {
|
||||||
@@ -28,13 +29,20 @@ func computeNtlmV2Response(ntlmV2Hash, serverChallenge, clientChallenge,
|
|||||||
timestamp, targetInfo []byte) []byte {
|
timestamp, targetInfo []byte) []byte {
|
||||||
|
|
||||||
temp := []byte{1, 1, 0, 0, 0, 0, 0, 0}
|
temp := []byte{1, 1, 0, 0, 0, 0, 0, 0}
|
||||||
|
PrintDebug("NTLMv2 response", temp)
|
||||||
temp = append(temp, timestamp...)
|
temp = append(temp, timestamp...)
|
||||||
|
PrintDebug("NTLMv2 response", temp)
|
||||||
temp = append(temp, clientChallenge...)
|
temp = append(temp, clientChallenge...)
|
||||||
|
PrintDebug("NTLMv2 response", temp)
|
||||||
temp = append(temp, 0, 0, 0, 0)
|
temp = append(temp, 0, 0, 0, 0)
|
||||||
|
PrintDebug("NTLMv2 response", temp)
|
||||||
temp = append(temp, targetInfo...)
|
temp = append(temp, targetInfo...)
|
||||||
|
PrintDebug("NTLMv2 response", temp)
|
||||||
temp = append(temp, 0, 0, 0, 0)
|
temp = append(temp, 0, 0, 0, 0)
|
||||||
|
PrintDebug("NTLMv2 response", temp)
|
||||||
|
|
||||||
NTProofStr := hmacMd5(ntlmV2Hash, serverChallenge, temp)
|
NTProofStr := hmacMd5(ntlmV2Hash, serverChallenge, temp)
|
||||||
|
PrintDebug("Proof string", NTProofStr)
|
||||||
return append(NTProofStr, temp...)
|
return append(NTProofStr, temp...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user