pax_global_header00006660000000000000000000000064147713164410014522gustar00rootroot0000000000000052 comment=5d23e7a96e8e68efb202a8f79839fe6e095e5cd7 go-ntlmssp-1.0.2/000077500000000000000000000000001477131644100136255ustar00rootroot00000000000000go-ntlmssp-1.0.2/.github/000077500000000000000000000000001477131644100151655ustar00rootroot00000000000000go-ntlmssp-1.0.2/.github/ISSUE_TEMPLATE/000077500000000000000000000000001477131644100173505ustar00rootroot00000000000000go-ntlmssp-1.0.2/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000032371477131644100220470ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: 'bug' assignees: '' --- **Is this a support request?** This issue tracker is maintained by LaunchDarkly SDK developers and is intended for feedback on the SDK code. If you're not sure whether the problem you are having is specifically related to the SDK, or to the LaunchDarkly service overall, it may be more appropriate to contact the LaunchDarkly support team; they can help to investigate the problem and will consult the SDK team if necessary. You can submit a support request by going [here](https://support.launchdarkly.com/hc/en-us/requests/new) or by emailing support@launchdarkly.com. Note that issues filed on this issue tracker are publicly accessible. Do not provide any private account information on your issues. If your problem is specific to your account, you should submit a support request as described above. **Describe the bug** A clear and concise description of what the bug is. **To reproduce** Steps to reproduce the behavior. **Expected behavior** A clear and concise description of what you expected to happen. **Logs** If applicable, add any log output related to your problem. **SDK version** The version of this SDK that you are using. **Language version, developer tools** For instance, Go 1.11 or Ruby 2.5.3. If you are using a language that requires a separate compiler, such as C, please include the name and version of the compiler too. **OS/platform** For instance, Ubuntu 16.04, Windows 10, or Android 4.0.3. If your code is running in a browser, please also include the browser type and version. **Additional context** Add any other context about the problem here. go-ntlmssp-1.0.2/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000003141477131644100213360ustar00rootroot00000000000000blank_issues_enabled: false contact_links: - name: Support request url: https://support.launchdarkly.com/hc/en-us/requests/new about: File your support requests with LaunchDarkly's support team go-ntlmssp-1.0.2/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000011451477131644100230760ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: 'enhancement' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I would love to see the SDK [...does something new...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context about the feature request here. go-ntlmssp-1.0.2/.github/actions/000077500000000000000000000000001477131644100166255ustar00rootroot00000000000000go-ntlmssp-1.0.2/.github/actions/coverage/000077500000000000000000000000001477131644100204205ustar00rootroot00000000000000go-ntlmssp-1.0.2/.github/actions/coverage/action.yml000066400000000000000000000017611477131644100224250ustar00rootroot00000000000000name: Coverage description: "Runs code-coverage checker." inputs: enforce: description: 'Whether to enforce coverage thresholds.' required: false default: 'false' runs: using: composite steps: - uses: ./.github/actions/get-go-version id: go-version - name: Test with coverage shell: bash id: test-coverage run: | set +e make test-coverage status=$? echo "coverage_status=$status" >> $GITHUB_OUTPUT - name: Upload coverage results uses: actions/upload-artifact@v4 with: name: Coverage-result-${{ steps.go-version.outputs.version }} path: build/coverage* - name: Enforce coverage shell: bash run: | if [ "${{ steps.test-coverage.outputs.coverage_status }}" != "0" ]; then echo "Code isn't fully covered!" if [ "${{ inputs.enforce }}" == "true" ]; then exit 1 fi else echo "Code is fully covered!" fi go-ntlmssp-1.0.2/.github/actions/get-go-version/000077500000000000000000000000001477131644100214725ustar00rootroot00000000000000go-ntlmssp-1.0.2/.github/actions/get-go-version/action.yml000066400000000000000000000006031477131644100234710ustar00rootroot00000000000000name: Get Go Version description: "Gets the currently installed Go version." outputs: version: description: 'The currently installed Go version.' value: ${{ steps.go-version.outputs.value }} runs: using: composite steps: - name: Get Go version id: go-version shell: bash run: | echo "value=$(go version | awk '{print $3}')" >> $GITHUB_OUTPUT go-ntlmssp-1.0.2/.github/actions/unit-tests/000077500000000000000000000000001477131644100207445ustar00rootroot00000000000000go-ntlmssp-1.0.2/.github/actions/unit-tests/action.yml000066400000000000000000000016571477131644100227550ustar00rootroot00000000000000name: Unit Tests description: "Runs unit tests + linters and optionally gathers coverage." inputs: lint: description: 'Whether to run linters.' required: false default: 'false' runs: using: composite steps: - uses: ./.github/actions/get-go-version id: go-version - name: Lint if: inputs.lint == 'true' shell: bash run: make lint - name: Test shell: bash id: test run: make test | tee raw_report.txt - name: Process test results if: steps.test.outcome == 'success' id: process-test shell: bash run: go run github.com/jstemmer/go-junit-report@v0.9.1 < raw_report.txt > junit_report.xml - name: Upload test results if: steps.process-test.outcome == 'success' uses: actions/upload-artifact@v4 with: name: Test-result-${{ inputs.test-target }}${{ steps.go-version.outputs.version }} path: junit_report.xml go-ntlmssp-1.0.2/.github/pull_request_template.md000066400000000000000000000013431477131644100221270ustar00rootroot00000000000000**Requirements** - [ ] I have added test coverage for new or changed functionality - [ ] I have followed the repository's [pull request submission guidelines](../blob/master/CONTRIBUTING.md#submitting-pull-requests) - [ ] I have validated my changes against all supported platform versions **Related issues** Provide links to any issues in this repository or elsewhere relating to this pull request. **Describe the solution you've provided** Provide a clear and concise description of what you expect to happen. **Describe alternatives you've considered** Provide a clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context about the pull request here. go-ntlmssp-1.0.2/.github/variables/000077500000000000000000000000001477131644100171355ustar00rootroot00000000000000go-ntlmssp-1.0.2/.github/variables/go-versions.env000066400000000000000000000000461477131644100221220ustar00rootroot00000000000000latest=1.24 penultimate=1.23 min=1.23 go-ntlmssp-1.0.2/.github/workflows/000077500000000000000000000000001477131644100172225ustar00rootroot00000000000000go-ntlmssp-1.0.2/.github/workflows/check-go-versions.yml000066400000000000000000000062551477131644100233030ustar00rootroot00000000000000name: Check Supported Go Versions on: schedule: - cron: "0 17 * * *" workflow_dispatch: jobs: check-go-eol: runs-on: ubuntu-latest outputs: latest: ${{ steps.parse.outputs.latest }} penultimate: ${{ steps.parse.outputs.penultimate }} timeout-minutes: 2 steps: - uses: actions/checkout@v4 # Perform a GET request to endoflife.date for the Go language. The response # contains all Go releases; we're interested in the 0'th and 1'th (latest and penultimate.) - name: Fetch officially supported Go versions uses: JamesIves/fetch-api-data-action@396ebea7d13904824f85b892b1616985f847301c with: endpoint: https://endoflife.date/api/go.json configuration: '{ "method": "GET" }' debug: true # Parse the response JSON and insert into environment variables for the next step. - name: Parse officially supported Go versions id: parse run: | echo "latest=${{ fromJSON(env.fetch-api-data)[0].cycle }}" >> $GITHUB_OUTPUT echo "penultimate=${{ fromJSON(env.fetch-api-data)[1].cycle }}" >> $GITHUB_OUTPUT create-prs: permissions: contents: write pull-requests: write needs: check-go-eol runs-on: ubuntu-latest strategy: matrix: branch: ["master"] fail-fast: false env: officialLatestVersion: ${{ needs.check-go-eol.outputs.latest }} officialPenultimateVersion: ${{ needs.check-go-eol.outputs.penultimate }} steps: - uses: actions/checkout@v4 with: ref: ${{ matrix.branch }} - name: Get current Go versions id: go-versions run: cat ./.github/variables/go-versions.env > $GITHUB_OUTPUT - name: Update go-versions.env and README.md if: steps.go-versions.outputs.latest != env.officialLatestVersion id: update-go-versions run: | sed -i -e "s#latest=[^ ]*#latest=${{ env.officialLatestVersion }}#g" \ -e "s#penultimate=[^ ]*#penultimate=${{ env.officialPenultimateVersion }}#g" \ ./.github/variables/go-versions.env - name: Create pull request if: steps.update-go-versions.outcome == 'success' uses: peter-evans/create-pull-request@v6 with: token: ${{ secrets.GITHUB_TOKEN }} add-paths: | .github/variables/go-versions.env branch: "launchdarklyreleasebot/update-to-go${{ env.officialLatestVersion }}-${{ matrix.branch }}" author: "LaunchDarklyReleaseBot " committer: "LaunchDarklyReleaseBot " labels: ${{ matrix.branch }} title: "ci: bump tested Go versions to ${{ env.officialLatestVersion }} and ${{ env.officialPenultimateVersion }}" commit-message: "Bumps from Go ${{ steps.go-versions.outputs.latest }} -> ${{ env.officialLatestVersion }} and ${{ steps.go-versions.outputs.penultimate }} -> ${{ env.officialPenultimateVersion }}." body: | - [ ] I have triggered CI on this PR (either close & reopen this PR in Github UI, or `git commit -m "run ci" --allow-empty && git push`) go-ntlmssp-1.0.2/.github/workflows/ci.yml000066400000000000000000000023341477131644100203420ustar00rootroot00000000000000name: Build and Test on: push: branches: [ 'master' ] paths-ignore: - '**.md' # Don't run CI on markdown changes. pull_request: branches: [ 'master' ] paths-ignore: - '**.md' jobs: go-versions: uses: ./.github/workflows/go-versions.yml # Runs the common tasks (unit tests, lint, contract tests) for each Go version. test-linux: name: ${{ format('Linux, Go {0}', matrix.go-version) }} needs: go-versions strategy: # Let jobs fail independently, in case it's a single version that's broken. fail-fast: false matrix: go-version: ${{ fromJSON(needs.go-versions.outputs.matrix) }} uses: ./.github/workflows/common_ci.yml with: go-version: ${{ matrix.go-version }} test-windows: name: ${{ format('Windows, Go {0}', matrix.go-version) }} runs-on: windows-2022 needs: go-versions strategy: fail-fast: false matrix: go-version: ${{ fromJSON(needs.go-versions.outputs.matrix) }} steps: - uses: actions/checkout@v4 - name: Setup Go ${{ matrix.go-version }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - name: Test run: go test -race ./... go-ntlmssp-1.0.2/.github/workflows/common_ci.yml000066400000000000000000000011441477131644100217100ustar00rootroot00000000000000name: Common CI on: workflow_call: inputs: go-version: description: "Go version to use for the jobs." required: true type: string jobs: unit-test-and-coverage: runs-on: ubuntu-latest name: 'Unit Tests and Coverage' steps: - uses: actions/checkout@v4 - name: Setup Go ${{ inputs.go-version }} uses: actions/setup-go@v5 with: go-version: ${{ inputs.go-version }} - uses: ./.github/actions/unit-tests with: lint: 'true' - uses: ./.github/actions/coverage with: enforce: 'false' go-ntlmssp-1.0.2/.github/workflows/go-versions.yml000066400000000000000000000046571477131644100222340ustar00rootroot00000000000000# The following chunk of yml boils down to pulling two Go version numbers out of a file and # making them available to the other workflows in a convenient fashion. # # It's a reusable workflow instead of an action so that its output can be used in a matrix strategy # of another job. # # The idea is to define the most recent, and penultimate, Go versions that should be used to test Relay. # Ideally we'd define these in a single place - otherwise we'd need to update many different places in # each workflow. This single place is .github/variables/go-versions.env. # # This reusable workflow grabs them out of the file, then sets them as outputs. As a convenience, it # also wraps each version in an array, so it can be directly used in a matrix strategy. Single-item matrices # are nice because you can tell instantly in the Github UI which version is being tested without needing # to inspect logs. # # To use a matrix output, e.g. latest version, do: # strategy: # matrix: ${{ fromJSON(this-job.outputs.latest-matrix) }} # name: Go Versions on: workflow_call: outputs: latest: description: 'The most recent Go version to test' value: ${{ jobs.go-versions.outputs.latest }} penultimate: description: 'The second most recent Go version to test' value: ${{ jobs.go-versions.outputs.penultimate }} min: description: 'The minimum Go version to test' value: ${{ jobs.go-versions.outputs.min }} matrix: description: 'All Go versions to test as a matrix' value: ${{ jobs.go-versions.outputs.all }} jobs: go-versions: runs-on: ubuntu-latest outputs: latest: ${{ steps.set-env.outputs.latest }} penultimate: ${{ steps.set-env.outputs.penultimate }} all: ${{ steps.set-matrix.outputs.all }} steps: - uses: actions/checkout@v4 - name: Set Go Versions id: set-env run: cat ./.github/variables/go-versions.env > $GITHUB_OUTPUT - name: Set Go Version Matrices id: set-matrix run: | if [ "${{ steps.set-env.outputs.penultimate }}" == "${{ steps.set-env.outputs.min }}" ]; then echo "all=[\"${{ steps.set-env.outputs.latest }}\",\"${{ steps.set-env.outputs.penultimate }}\"]" >> $GITHUB_OUTPUT else echo "all=[\"${{ steps.set-env.outputs.latest }}\",\"${{ steps.set-env.outputs.penultimate }}\",\"${{ steps.set-env.outputs.min }}\"]" >> $GITHUB_OUTPUT fi go-ntlmssp-1.0.2/.github/workflows/release-please.yml000066400000000000000000000003671477131644100226420ustar00rootroot00000000000000name: Run Release Please on: push: branches: - master workflow_dispatch: jobs: release-please: runs-on: ubuntu-latest steps: - uses: googleapis/release-please-action@v4 with: target-branch: master go-ntlmssp-1.0.2/.github/workflows/stale.yml000066400000000000000000000003471477131644100210610ustar00rootroot00000000000000name: 'Close stale issues and PRs' on: workflow_dispatch: schedule: # Happen once per day at 1:30 AM - cron: '30 1 * * *' jobs: sdk-close-stale: uses: launchdarkly/gh-actions/.github/workflows/sdk-stale.yml@main go-ntlmssp-1.0.2/.gitignore000066400000000000000000000001211477131644100156070ustar00rootroot00000000000000bin/ build/ go-server-sdk.test allocations.out .idea .vscode go.work go.work.sum go-ntlmssp-1.0.2/.release-please-manifest.json000066400000000000000000000000231477131644100212640ustar00rootroot00000000000000{ ".": "1.0.2" } go-ntlmssp-1.0.2/.travis.yml000066400000000000000000000004301477131644100157330ustar00rootroot00000000000000sudo: false language: go before_script: - go get -u github.com/golang/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 ./... go-ntlmssp-1.0.2/CHANGELOG.md000066400000000000000000000016401477131644100154370ustar00rootroot00000000000000# Change log All notable changes to the project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org). ## [1.0.2](https://github.com/launchdarkly/go-ntlmssp/compare/v1.0.1...v1.0.2) (2025-03-27) ### Bug Fixes * Bump golang.org/x/crypto for CVE-2025-22869 ([#4](https://github.com/launchdarkly/go-ntlmssp/issues/4)) ([6d0a61e](https://github.com/launchdarkly/go-ntlmssp/commit/6d0a61e0eddc91513ad6c927159eda85397e9bca)) ## [1.0.1] - 2020-06-11 ### Added: - Added `go.mod` file so this package can now be consumed as a module. Since the major version is still `1` and it does not have any module-only dependencies, it can still be used by non-module projects as well. - Added CircleCI build. ### Fixed: - Fixed a broken unit test. ## [1.0.0] - 2019-05-31 Initial release of this fork. The only change from the upstream code is https://github.com/launchdarkly/go-ntlmssp/pull/1. go-ntlmssp-1.0.2/CONTRIBUTING.md000066400000000000000000000023061477131644100160570ustar00rootroot00000000000000# Contributing to this library The source code for this library is [here](https://github.com/launchdarkly/go-ntlmssp). We encourage pull-requests and other contributions from the community. ## Submitting bug reports and feature requests The LaunchDarkly SDK team monitors the [issue tracker](https://github.com/launchdarkly/go-ntlmssp/issues) in this repository. Bug reports and feature requests specific to this project should be filed in the issue tracker. The SDK team will respond to all newly filed issues within two business days. ## Submitting pull requests We encourage pull requests and other contributions from the community. Before submitting pull requests, ensure that all temporary or unintended code is removed. Don't worry about adding reviewers to the pull request; the LaunchDarkly SDK team will add themselves. The SDK team will acknowledge all pull requests within two business days. ## Build instructions ### Prerequisites This project should be built against the lowest supported Go version as described in [README.md](./README.md). ### Building To build the library without running any tests: ``` make ``` ### Testing To build the library and run all unit tests: ``` make test ``` go-ntlmssp-1.0.2/LICENSE000066400000000000000000000020641477131644100146340ustar00rootroot00000000000000The 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. go-ntlmssp-1.0.2/Makefile000066400000000000000000000036651477131644100152770ustar00rootroot00000000000000GOLANGCI_LINT_VERSION=v1.64.5 LINTER=./bin/golangci-lint LINTER_VERSION_FILE=./bin/.golangci-lint-version-$(GOLANGCI_LINT_VERSION) OUTPUT_DIR=./build ALL_SOURCES := $(shell find * -type f -name "*.go") COVERAGE_PROFILE_RAW=./build/coverage_raw.out COVERAGE_PROFILE_RAW_HTML=./build/coverage_raw.html COVERAGE_PROFILE_FILTERED=./build/coverage.out COVERAGE_PROFILE_FILTERED_HTML=./build/coverage.html COVERAGE_ENFORCER_FLAGS=-package github.com/launchdarkly/go-ntlmssp \ -skipfiles '(internal/sharedtest/$(COVERAGE_ENFORCER_SKIP_FILES_EXTRA))' \ -skipcode "// COVERAGE" \ -packagestats -filestats -showcode .PHONY: all build clean test test-coverage benchmarks benchmark-allocs lint bump-min-go-version: go mod edit -go=$(MIN_GO_VERSION) go.mod cd ldotel && go mod edit -go=$(MIN_GO_VERSION) go.mod cd ldai && go mod edit -go=$(MIN_GO_VERSION) go.mod cd testservice && go mod edit -go=$(MIN_GO_VERSION) go.mod cd ./.github/variables && sed -i.bak "s#min=[^ ]*#min=$(MIN_GO_VERSION)#g" go-versions.env && rm go-versions.env.bak clean: rm -rf ./bin/ build: go build ./... test: go test -v -race ./... lint: $(LINTER) run ./... test-coverage: $(COVERAGE_PROFILE_RAW) go run github.com/launchdarkly-labs/go-coverage-enforcer@latest $(COVERAGE_ENFORCER_FLAGS) -outprofile $(COVERAGE_PROFILE_FILTERED) $(COVERAGE_PROFILE_RAW) go tool cover -html $(COVERAGE_PROFILE_FILTERED) -o $(COVERAGE_PROFILE_FILTERED_HTML) go tool cover -html $(COVERAGE_PROFILE_RAW) -o $(COVERAGE_PROFILE_RAW_HTML) $(COVERAGE_PROFILE_RAW): $(ALL_SOURCES) @mkdir -p ./build go test -coverprofile $(COVERAGE_PROFILE_RAW) -coverpkg=./... ./... >/dev/null # note that -coverpkg=./... is necessary so it aggregates coverage to include inter-package references $(LINTER_VERSION_FILE): rm -f $(LINTER) curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s $(GOLANGCI_LINT_VERSION) touch $(LINTER_VERSION_FILE) lint: $(LINTER_VERSION_FILE) go-ntlmssp-1.0.2/README.md000066400000000000000000000030121477131644100151000ustar00rootroot00000000000000# go-ntlmssp Golang package that provides NTLM/Negotiate authentication over HTTP [![GoDoc](https://godoc.org/github.com/launchdarkly/go-ntlmssp?status.svg)](https://godoc.org/github.com/launchdarkly/go-ntlmssp) [![Build and Test](https://github.com/launchdarkly/go-ntlmssp/actions/workflows/ci.yml/badge.svg)](https://github.com/launchdarkly/go-ntlmssp/actions/workflows/ci.yml) This is a fork of [github.com/Azure/go-ntlmssp](https://github.com/Azure/go-ntlmssp), with minor changes for use in the [LaunchDarkly Go SDK](https://github.com/launchdarkly/go-server-sdk). 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. go-ntlmssp-1.0.2/authenticate_message.go000066400000000000000000000073031477131644100203410ustar00rootroot00000000000000package ntlmssp import ( "bytes" "crypto/rand" "encoding/binary" "errors" "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) if _, err := rand.Reader.Read(clientChallenge); err != nil { return nil, err } 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() } go-ntlmssp-1.0.2/authheader.go000066400000000000000000000013521477131644100162670ustar00rootroot00000000000000package 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 } go-ntlmssp-1.0.2/avids.go000066400000000000000000000004451477131644100152650ustar00rootroot00000000000000package ntlmssp type avID uint16 const ( avIDMsvAvEOL avID = iota avIDMsvAvNbComputerName avIDMsvAvNbDomainName avIDMsvAvDNSComputerName avIDMsvAvDNSDomainName avIDMsvAvDNSTreeName avIDMsvAvFlags avIDMsvAvTimestamp avIDMsvAvSingleHost avIDMsvAvTargetName avIDMsvChannelBindings ) go-ntlmssp-1.0.2/challenge_message.go000066400000000000000000000034201477131644100176010ustar00rootroot00000000000000package 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 } go-ntlmssp-1.0.2/go.mod000066400000000000000000000001321477131644100147270ustar00rootroot00000000000000module github.com/launchdarkly/go-ntlmssp go 1.23.0 require golang.org/x/crypto v0.36.0 go-ntlmssp-1.0.2/go.sum000066400000000000000000000004761477131644100147670ustar00rootroot00000000000000golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= go-ntlmssp-1.0.2/messageheader.go000066400000000000000000000006401477131644100167510ustar00rootroot00000000000000package 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} } go-ntlmssp-1.0.2/negotiate_flags.go000066400000000000000000000026741477131644100173200ustar00rootroot00000000000000package 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) } go-ntlmssp-1.0.2/negotiate_message.go000066400000000000000000000030631477131644100176410ustar00rootroot00000000000000package 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 | negotiateFlagNTLMSSPNEGOTIATENTLM | negotiateFlagNTLMSSPNEGOTIATEALWAYSSIGN //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 } go-ntlmssp-1.0.2/negotiator.go000066400000000000000000000075171477131644100163410ustar00rootroot00000000000000package ntlmssp import ( "bytes" "encoding/base64" "io" "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 = io.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)) if _, err := io.Copy(io.Discard, res.Body); err != nil { return nil, err } res.Body.Close() req.Body = io.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 if _, err := io.Copy(io.Discard, res.Body); err != nil { return nil, err } 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 = io.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 } if _, err := io.Copy(io.Discard, res.Body); err != nil { return nil, err } 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 = io.NopCloser(bytes.NewReader(body.Bytes())) return rt.RoundTrip(req) } return res, err } go-ntlmssp-1.0.2/nlmp.go000066400000000000000000000030441477131644100151230ustar00rootroot00000000000000// 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" "strings" "golang.org/x/crypto/md4" //nolint: staticcheck ) 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) } go-ntlmssp-1.0.2/nlmp_test.go000066400000000000000000000132751477131644100161710ustar00rootroot00000000000000package ntlmssp import ( "bytes" "testing" ) // test cases from http://davenport.sourceforge.net/ntlm.html var username = "user" var password = "SecREt01" var target = "DOMAIN" var domain = "MYDOMAIN" var workstation = "MYPC" var challenge = []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef} func TestUsernameDomainWorkstation(t *testing.T) { // taking a username and workstation as input, check that the username, domain, workstation // and negotiate message bytes all match their expected values tables := []struct { u string w string xu string xd string xb []byte }{ {username, "", username, "", []byte{ 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x82, 0x88, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x06, 0x01, 0xb1, 0x1d, 0x00, 0x00, 0x00, 0x0f}}, {domain + "\\" + username, "", username, domain, []byte{ 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x92, 0x88, 0xa0, 0x08, 0x00, 0x08, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x06, 0x01, 0xb1, 0x1d, 0x00, 0x00, 0x00, 0x0f, 0x4d, 0x59, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e}}, {domain + "\\" + username, workstation, username, domain, []byte{ 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xb2, 0x88, 0xa0, 0x08, 0x00, 0x08, 0x00, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x30, 0x00, 0x00, 0x00, 0x06, 0x01, 0xb1, 0x1d, 0x00, 0x00, 0x00, 0x0f, 0x4d, 0x59, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x4d, 0x59, 0x50, 0x43}}, {username, workstation, username, "", []byte{ 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xa2, 0x88, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0x06, 0x01, 0xb1, 0x1d, 0x00, 0x00, 0x00, 0x0f, 0x4d, 0x59, 0x50, 0x43}}, } for _, table := range tables { tuser, tdomain := GetDomain(table.u) if tuser != table.xu { t.Fatalf("username not correct, expected %v got %v", table.xu, tuser) } if tdomain != table.xd { t.Fatalf("domain not correct, expected %v got %v", table.xd, tdomain) } tb, err := NewNegotiateMessage(tdomain, table.w) if err != nil { t.Fatalf("error creating new negotiate message with domain '%v' and workstation '%v'", tdomain, table.w) } if !bytes.Equal(tb, table.xb) { t.Fatalf("negotiate message bytes not correct, expected %v got %v", table.xb, tb) } } } func TestCalculateNTLMv2Response(t *testing.T) { NTLMv2Hash := getNtlmV2Hash(password, username, target) ClientChallenge := []byte{0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44} Time := []byte{0x00, 0x90, 0xd3, 0x36, 0xb7, 0x34, 0xc3, 0x01} targetInfo := []byte{0x02, 0x00, 0x0c, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x45, 0x00, 0x52, 0x00, 0x56, 0x00, 0x45, 0x00, 0x52, 0x00, 0x04, 0x00, 0x14, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x03, 0x00, 0x22, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00} v := computeNtlmV2Response(NTLMv2Hash, challenge, ClientChallenge, Time, targetInfo) if expected := []byte{ 0xcb, 0xab, 0xbc, 0xa7, 0x13, 0xeb, 0x79, 0x5d, 0x04, 0xc9, 0x7a, 0xbc, 0x01, 0xee, 0x49, 0x83, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xd3, 0x36, 0xb7, 0x34, 0xc3, 0x01, 0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x45, 0x00, 0x52, 0x00, 0x56, 0x00, 0x45, 0x00, 0x52, 0x00, 0x04, 0x00, 0x14, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x03, 0x00, 0x22, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; !bytes.Equal(v, expected) { t.Fatalf("expected %x, got %x", expected, v) } } func TestCalculateLMv2Response(t *testing.T) { NTLMv2Hash := getNtlmV2Hash(password, username, target) ClientChallenge := []byte{0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44} v := computeLmV2Response(NTLMv2Hash, challenge, ClientChallenge) if expected := []byte{ 0xd6, 0xe6, 0x15, 0x2e, 0xa2, 0x5d, 0x03, 0xb7, 0xc6, 0xba, 0x66, 0x29, 0xc2, 0xd6, 0xaa, 0xf0, 0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, }; !bytes.Equal(v, expected) { t.Fatalf("expected %x, got %x", expected, v) } } func TestToUnicode(t *testing.T) { v := toUnicode(password) if expected := []byte{0x53, 0x00, 0x65, 0x00, 0x63, 0x00, 0x52, 0x00, 0x45, 0x00, 0x74, 0x00, 0x30, 0x00, 0x31, 0x00}; !bytes.Equal(v, expected) { t.Fatalf("expected %v, got %v", expected, v) } } func TestNTLMhash(t *testing.T) { v := getNtlmHash(password) if expected := []byte{0xcd, 0x06, 0xca, 0x7c, 0x7e, 0x10, 0xc9, 0x9b, 0x1d, 0x33, 0xb7, 0x48, 0x5a, 0x2e, 0xd8, 0x08}; !bytes.Equal(v, expected) { t.Fatalf("expected %v, got %v", expected, v) } } func TestNTLMv2Hash(t *testing.T) { v := getNtlmV2Hash(password, username, target) if expected := []byte{0x04, 0xb8, 0xe0, 0xba, 0x74, 0x28, 0x9c, 0xc5, 0x40, 0x82, 0x6b, 0xab, 0x1d, 0xee, 0x63, 0xae}; !bytes.Equal(v, expected) { t.Fatalf("expected %v, got %v", expected, v) } } go-ntlmssp-1.0.2/release-please-config.json000066400000000000000000000005011477131644100206460ustar00rootroot00000000000000{ "packages" : { "." : { "release-type" : "go", "bump-minor-pre-major" : true, "versioning" : "default", "include-component-in-tag" : false, "bootstrap-sha" : "5c0509ce5adf5fb808bd277b4f5bae0d80818745", "exclude-paths": [ ".github", ".vscode" ] } } } go-ntlmssp-1.0.2/unicode.go000066400000000000000000000012011477131644100155740ustar00rootroot00000000000000package 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) //nolint: errcheck return b.Bytes() } go-ntlmssp-1.0.2/varfield.go000066400000000000000000000016201477131644100157470ustar00rootroot00000000000000package 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 } go-ntlmssp-1.0.2/version.go000066400000000000000000000007721477131644100156470ustar00rootroot00000000000000package 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, } }