diff --git a/debian/rules b/debian/rules index c7410c64..ddae758a 100755 --- a/debian/rules +++ b/debian/rules @@ -17,7 +17,7 @@ endif BUILD_DIR := obj-$(DEB_HOST_GNU_TYPE) export DH_GOPKG := github.com/git-lfs/git-lfs # DH_GOLANG_EXCLUDES typically incorporates vendor exclusions from script/test -export DH_GOLANG_EXCLUDES := test github.com/olekukonko/ts/* github.com/xeipuuv/* github.com/spf13/cobra/* github.com/kr/* github.com/pkg/errors +export DH_GOLANG_EXCLUDES := test github.com/olekukonko/ts/* github.com/xeipuuv/* github.com/spf13/cobra/* github.com/kr/* github.com/pkg/errors github.com/alexbrainman/sspi/* export PATH := $(CURDIR)/$(BUILD_DIR)/bin:$(PATH) # by-default, dh_golang only copies *.go and other source - this upsets a bunch of vendor test routines diff --git a/glide.lock b/glide.lock index 110937ea..846aa7f4 100644 --- a/glide.lock +++ b/glide.lock @@ -1,10 +1,18 @@ -hash: e19b925b9eaca9a10a7742b4a4b1dc8047bff437584538dda59f4f10e69fa6ca -updated: 2018-01-29T17:36:52.158516-08:00 +hash: 61a26ad942b6cdacffd945003b76991b4caeb6a16baab8e86943f6bbc83410e9 +updated: 2018-02-15T23:41:56.5827516+01:00 imports: +- name: github.com/alexbrainman/sspi + version: 4729b3d4d8581b2db83864d1018926e4154f9406 + subpackages: + - ntlm - name: github.com/bgentry/go-netrc version: 9fd32a8b3d3d3f9d43c341bfe098430e07609480 subpackages: - netrc +- name: github.com/davecgh/go-spew + version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d + subpackages: + - spew - name: github.com/inconshreveable/mousetrap version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 - name: github.com/kr/pty @@ -13,6 +21,10 @@ imports: version: ecf753e7c962639ab5a1fb46f7da627d4c0a04b8 - name: github.com/pkg/errors version: c605e284fe17294bda444b34710735b29d1a9d90 +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib - name: github.com/rubyist/tracerx version: 787959303086f44a8c361240dfac53d3e9d53ed2 - name: github.com/spf13/cobra @@ -32,14 +44,6 @@ imports: - name: github.com/xeipuuv/gojsonschema version: 6b67b3fab74d992bd07f72550006ab2c6907c416 testImports: -- name: github.com/davecgh/go-spew - version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d - subpackages: - - spew -- name: github.com/pmezard/go-difflib - version: d8ed2627bdf02c080bf22230dbb337003b7aba2d - subpackages: - - difflib - name: github.com/xeipuuv/gojsonpointer version: 4e3ac2762d5f479393488629ee9370b50873b3a6 - name: github.com/xeipuuv/gojsonreference diff --git a/glide.yaml b/glide.yaml index 9faa9006..63ced2ab 100644 --- a/glide.yaml +++ b/glide.yaml @@ -26,3 +26,7 @@ import: version: 6b67b3fab74d992bd07f72550006ab2c6907c416 - package: github.com/pkg/errors version: c605e284fe17294bda444b34710735b29d1a9d90 +- package: github.com/alexbrainman/sspi + version: 4729b3d4d8581b2db83864d1018926e4154f9406 + subpackages: + - ntlm \ No newline at end of file diff --git a/script/test b/script/test index 80e28a70..b8fb7fcf 100755 --- a/script/test +++ b/script/test @@ -16,6 +16,7 @@ if [ $# -eq 0 ]; then | grep -v "github.com/stretchr/testify" \ | grep -v "github.com/xeipuuv/gojsonreference" \ | grep -v "github.com/xeipuuv/gojsonschema" \ + | grep -v "github.com/alexbrainman/sspi" \ )" else PACKAGES="$(echo "$PACKAGES" \ diff --git a/vendor/github.com/alexbrainman/sspi/LICENSE b/vendor/github.com/alexbrainman/sspi/LICENSE new file mode 100644 index 00000000..74487567 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/alexbrainman/sspi/README.md b/vendor/github.com/alexbrainman/sspi/README.md new file mode 100644 index 00000000..848b6b58 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/README.md @@ -0,0 +1 @@ +This repository holds Go packages for accessing Security Support Provider Interface on Windows. diff --git a/vendor/github.com/alexbrainman/sspi/buffer.go b/vendor/github.com/alexbrainman/sspi/buffer.go new file mode 100644 index 00000000..f4b0ef32 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/buffer.go @@ -0,0 +1,57 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package sspi + +import ( + "io" + "unsafe" +) + +func (b *SecBuffer) Set(buftype uint32, data []byte) { + b.BufferType = buftype + if len(data) > 0 { + b.Buffer = &data[0] + b.BufferSize = uint32(len(data)) + } else { + b.Buffer = nil + b.BufferSize = 0 + } +} + +func (b *SecBuffer) Free() error { + if b.Buffer == nil { + return nil + } + return FreeContextBuffer((*byte)(unsafe.Pointer(b.Buffer))) +} + +func (b *SecBuffer) Bytes() []byte { + if b.Buffer == nil || b.BufferSize <= 0 { + return nil + } + return (*[2 << 20]byte)(unsafe.Pointer(b.Buffer))[:b.BufferSize] +} + +func (b *SecBuffer) WriteAll(w io.Writer) (int, error) { + if b.BufferSize == 0 || b.Buffer == nil { + return 0, nil + } + data := b.Bytes() + total := 0 + for { + n, err := w.Write(data) + total += n + if err != nil { + return total, err + } + if n >= len(data) { + break + } + data = data[n:] + } + return total, nil +} diff --git a/vendor/github.com/alexbrainman/sspi/mksyscall.go b/vendor/github.com/alexbrainman/sspi/mksyscall.go new file mode 100644 index 00000000..19e11959 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/mksyscall.go @@ -0,0 +1,7 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sspi + +//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -systemdll=false -output=zsyscall_windows.go syscall.go diff --git a/vendor/github.com/alexbrainman/sspi/negotiate/http_test.go b/vendor/github.com/alexbrainman/sspi/negotiate/http_test.go new file mode 100644 index 00000000..bfd81875 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/negotiate/http_test.go @@ -0,0 +1,283 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package negotiate_test + +import ( + "encoding/base64" + "flag" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + "time" + + "github.com/alexbrainman/sspi/negotiate" +) + +var ( + testURL = flag.String("url", "", "server URL for TestNegotiateHTTPClient") +) + +// TODO: perhaps add Transport that is similar to http.Transport +// TODO: perhaps implement separate NTLMTransport and KerberosTransport (not sure about this idea) +// TODO: KerberosTransport is (I beleive) sinlge leg protocol, so it can be implemented easily (unlike NTLM) +// TODO: perhaps implement both server and client Transport + +type httpClient struct { + client *http.Client + transport *http.Transport + url string +} + +func newHTTPClient(url string) *httpClient { + transport := &http.Transport{ + Dial: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + } + return &httpClient{ + client: &http.Client{Transport: transport}, + transport: transport, + url: url, + } +} + +func (c *httpClient) CloseIdleConnections() { + c.transport.CloseIdleConnections() +} + +func (c *httpClient) get(req *http.Request) (*http.Response, string, error) { + res, err := c.client.Do(req) + if err != nil { + return nil, "", err + } + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, "", err + } + return res, string(body), nil +} + +func (c *httpClient) canDoNegotiate() error { + req, err := http.NewRequest("GET", c.url, nil) + if err != nil { + return err + } + res, _, err := c.get(req) + if err != nil { + return err + } + if res.StatusCode != http.StatusUnauthorized { + return fmt.Errorf("Unauthorized expected, but got %v", res.StatusCode) + } + authHeaders, found := res.Header["Www-Authenticate"] + if !found { + return fmt.Errorf("Www-Authenticate not found") + } + for _, h := range authHeaders { + if h == "Negotiate" { + return nil + } + } + return fmt.Errorf("Www-Authenticate header does not contain Negotiate, but has %v", authHeaders) +} + +func findAuthHeader(res *http.Response) ([]byte, error) { + authHeaders, found := res.Header["Www-Authenticate"] + if !found { + return nil, fmt.Errorf("Www-Authenticate not found") + } + if len(authHeaders) != 1 { + return nil, fmt.Errorf("Only one Www-Authenticate header expected, but %d found: %v", len(authHeaders), authHeaders) + } + if len(authHeaders[0]) < 10 { + return nil, fmt.Errorf("Www-Authenticate header is to short: %q", authHeaders[0]) + } + if !strings.HasPrefix(authHeaders[0], "Negotiate ") { + return nil, fmt.Errorf("Www-Authenticate header is suppose to starts with \"Negotiate \", but is %q", authHeaders[0]) + } + token, err := base64.StdEncoding.DecodeString(authHeaders[0][10:]) + if err != nil { + return nil, err + } + return token, nil +} + +func (c *httpClient) startAuthorization(inputToken []byte) ([]byte, error) { + req, err := http.NewRequest("GET", c.url, nil) + if err != nil { + return nil, err + } + req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(inputToken)) + res, _, err := c.get(req) + if err != nil { + return nil, err + } + if res.StatusCode != http.StatusUnauthorized { + return nil, fmt.Errorf("Unauthorized expected, but got %v", res.StatusCode) + } + outputToken, err := findAuthHeader(res) + if err != nil { + return nil, err + } + return outputToken, nil +} + +func (c *httpClient) completeAuthorization(inputToken []byte) (*http.Response, string, error) { + req, err := http.NewRequest("GET", c.url, nil) + if err != nil { + return nil, "", err + } + req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(inputToken)) + res, body, err := c.get(req) + if err != nil { + return nil, "", err + } + if res.StatusCode != http.StatusOK { + return nil, "", fmt.Errorf("OK expected, but got %v", res.StatusCode) + } + return res, body, nil +} + +func TestNTLMHTTPClient(t *testing.T) { + // TODO: combine client and server tests so we don't need external server + if len(*testURL) == 0 { + t.Skip("Skipping due to empty \"url\" parameter") + } + + cred, err := negotiate.AcquireCurrentUserCredentials() + if err != nil { + t.Fatal(err) + } + defer cred.Release() + + secctx, clientToken1, err := negotiate.NewClientContext(cred, "") + if err != nil { + t.Fatal(err) + } + defer secctx.Release() + + client := newHTTPClient(*testURL) + defer client.CloseIdleConnections() + + err = client.canDoNegotiate() + if err != nil { + t.Fatal(err) + } + serverToken1, err := client.startAuthorization(clientToken1) + if err != nil { + t.Fatal(err) + } + authCompleted, clientToken2, err := secctx.Update(serverToken1) + if err != nil { + t.Fatal(err) + } + if len(clientToken2) == 0 { + t.Fatal("secctx.Update returns empty token for the peer, but our authentication is not done yet") + } + res, _, err := client.completeAuthorization(clientToken2) + if err != nil { + t.Fatal(err) + } + if authCompleted { + return + } + serverToken2, err := findAuthHeader(res) + if err != nil { + t.Fatal(err) + } + authCompleted, lastToken, err := secctx.Update(serverToken2) + if err != nil { + t.Fatal(err) + } + if !authCompleted { + t.Fatal("client authentication should be completed now") + } + if len(lastToken) > 0 { + t.Fatalf("last token supposed to be empty, but %v returned", lastToken) + } +} + +func TestKerberosHTTPClient(t *testing.T) { + // TODO: combine client and server tests so we don't need external server + if len(*testURL) == 0 { + t.Skip("Skipping due to empty \"url\" parameter") + } + + u, err := url.Parse(*testURL) + if err != nil { + t.Fatal(err) + } + targetName := "http/" + strings.ToUpper(u.Host) + + cred, err := negotiate.AcquireCurrentUserCredentials() + if err != nil { + t.Fatal(err) + } + defer cred.Release() + + secctx, token, err := negotiate.NewClientContext(cred, targetName) + if err != nil { + t.Fatal(err) + } + defer secctx.Release() + + client := newHTTPClient(*testURL) + defer client.CloseIdleConnections() + + err = client.canDoNegotiate() + if err != nil { + t.Fatal(err) + } + res, _, err := client.completeAuthorization(token) + if err != nil { + t.Fatal(err) + } + serverToken, err := findAuthHeader(res) + if err != nil { + t.Fatal(err) + } + authCompleted, lastToken, err := secctx.Update(serverToken) + if err != nil { + t.Fatal(err) + } + if !authCompleted { + t.Fatal("client authentication should be completed now") + } + if len(lastToken) > 0 { + t.Fatalf("last token supposed to be empty, but %v returned", lastToken) + } +} + +// TODO: See http://www.innovation.ch/personal/ronald/ntlm.html#connections about needed to keep connection alive during authentication. + +func TestNegotiateHTTPServer(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // TODO: implement Negotiate authentication here + w.Write([]byte("hello")) + })) + defer ts.Close() + + res, err := http.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + got, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + if string(got) != "hello" { + t.Errorf("got %q, want hello", string(got)) + } +} diff --git a/vendor/github.com/alexbrainman/sspi/negotiate/negotiate.go b/vendor/github.com/alexbrainman/sspi/negotiate/negotiate.go new file mode 100644 index 00000000..3d22b0c0 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/negotiate/negotiate.go @@ -0,0 +1,348 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +// Package negotiate provides access to the Microsoft Negotiate SSP Package. +// +package negotiate + +import ( + "errors" + "syscall" + "time" + "unsafe" + + "github.com/alexbrainman/sspi" +) + +// TODO: maybe (if possible) move all winapi related out of sspi and into sspi/internal/winapi + +// PackageInfo contains Negotiate SSP package description. +var PackageInfo *sspi.PackageInfo + +func init() { + var err error + PackageInfo, err = sspi.QueryPackageInfo(sspi.NEGOSSP_NAME) + if err != nil { + panic("failed to fetch Negotiate package info: " + err.Error()) + } +} + +func acquireCredentials(creduse uint32, ai *sspi.SEC_WINNT_AUTH_IDENTITY) (*sspi.Credentials, error) { + c, err := sspi.AcquireCredentials(sspi.NEGOSSP_NAME, creduse, (*byte)(unsafe.Pointer(ai))) + if err != nil { + return nil, err + } + return c, nil +} + +// AcquireCurrentUserCredentials acquires credentials of currently +// logged on user. These will be used by the client to authenticate +// itself to the server. It will also be used by the server +// to impersonate the user. +func AcquireCurrentUserCredentials() (*sspi.Credentials, error) { + return acquireCredentials(sspi.SECPKG_CRED_OUTBOUND, nil) +} + +// TODO: see if I can share this common ntlm and negotiate code + +// AcquireUserCredentials acquires credentials of user described by +// domain, username and password. These will be used by the client to +// authenticate itself to the server. It will also be used by the +// server to impersonate the user. +func AcquireUserCredentials(domain, username, password string) (*sspi.Credentials, error) { + if len(domain) == 0 { + return nil, errors.New("domain parameter cannot be empty") + } + if len(username) == 0 { + return nil, errors.New("username parameter cannot be empty") + } + d, err := syscall.UTF16FromString(domain) + if err != nil { + return nil, err + } + u, err := syscall.UTF16FromString(username) + if err != nil { + return nil, err + } + var p []uint16 + var plen uint32 + if len(password) > 0 { + p, err = syscall.UTF16FromString(password) + if err != nil { + return nil, err + } + plen = uint32(len(p) - 1) // do not count terminating 0 + } + ai := sspi.SEC_WINNT_AUTH_IDENTITY{ + User: &u[0], + UserLength: uint32(len(u) - 1), // do not count terminating 0 + Domain: &d[0], + DomainLength: uint32(len(d) - 1), // do not count terminating 0 + Password: &p[0], + PasswordLength: plen, + Flags: sspi.SEC_WINNT_AUTH_IDENTITY_UNICODE, + } + return acquireCredentials(sspi.SECPKG_CRED_OUTBOUND, &ai) +} + +// AcquireServerCredentials acquires server credentials that will +// be used to authenticate client. +func AcquireServerCredentials() (*sspi.Credentials, error) { + return acquireCredentials(sspi.SECPKG_CRED_INBOUND, nil) +} + +func updateContext(c *sspi.Context, dst, src []byte, targetName *uint16) (authCompleted bool, n int, err error) { + var inBuf, outBuf [1]sspi.SecBuffer + inBuf[0].Set(sspi.SECBUFFER_TOKEN, src) + inBufs := &sspi.SecBufferDesc{ + Version: sspi.SECBUFFER_VERSION, + BuffersCount: 1, + Buffers: &inBuf[0], + } + outBuf[0].Set(sspi.SECBUFFER_TOKEN, dst) + outBufs := &sspi.SecBufferDesc{ + Version: sspi.SECBUFFER_VERSION, + BuffersCount: 1, + Buffers: &outBuf[0], + } + ret := c.Update(targetName, outBufs, inBufs) + switch ret { + case sspi.SEC_E_OK: + // session established -> return success + return true, int(outBuf[0].BufferSize), nil + case sspi.SEC_I_COMPLETE_NEEDED, sspi.SEC_I_COMPLETE_AND_CONTINUE: + ret = sspi.CompleteAuthToken(c.Handle, outBufs) + if ret != sspi.SEC_E_OK { + return false, 0, ret + } + case sspi.SEC_I_CONTINUE_NEEDED: + default: + return false, 0, ret + } + return false, int(outBuf[0].BufferSize), nil +} + +func makeSignature(c *sspi.Context, msg []byte, qop, seqno uint32) ([]byte, error) { + _, maxSignature, _, _, err := c.Sizes() + if err != nil { + return nil, err + } + + if maxSignature == 0 { + return nil, errors.New("integrity services are not requested or unavailable") + } + + var b [2]sspi.SecBuffer + b[0].Set(sspi.SECBUFFER_DATA, msg) + b[1].Set(sspi.SECBUFFER_TOKEN, make([]byte, maxSignature)) + + ret := sspi.MakeSignature(c.Handle, qop, sspi.NewSecBufferDesc(b[:]), seqno) + if ret != sspi.SEC_E_OK { + return nil, ret + } + + return b[1].Bytes(), nil +} + +func verifySignature(c *sspi.Context, msg, token []byte, seqno uint32) (uint32, error) { + var b [2]sspi.SecBuffer + b[0].Set(sspi.SECBUFFER_DATA, msg) + b[1].Set(sspi.SECBUFFER_TOKEN, token) + + var qop uint32 + + ret := sspi.VerifySignature(c.Handle, sspi.NewSecBufferDesc(b[:]), seqno, &qop) + if ret != sspi.SEC_E_OK { + return 0, ret + } + + return qop, nil +} + +// ClientContext is used by the client to manage all steps of Negotiate negotiation. +type ClientContext struct { + sctxt *sspi.Context + targetName *uint16 +} + +// NewClientContext creates new client context. It uses client +// credentials cred generated by AcquireCurrentUserCredentials or +// AcquireUserCredentials and SPN to start client Negotiate +// negotiation sequence. targetName is the service principal name +// (SPN) or the security context of the destination server. +// NewClientContext returns new token to be sent to the server. +func NewClientContext(cred *sspi.Credentials, targetName string) (cc *ClientContext, outputToken []byte, err error) { + var tname *uint16 + if len(targetName) > 0 { + p, err2 := syscall.UTF16FromString(targetName) + if err2 != nil { + return nil, nil, err2 + } + if len(p) > 0 { + tname = &p[0] + } + } + otoken := make([]byte, PackageInfo.MaxToken) + c := sspi.NewClientContext(cred, sspi.ISC_REQ_CONNECTION) + authCompleted, n, err2 := updateContext(c, otoken, nil, tname) + if err2 != nil { + return nil, nil, err2 + } + if authCompleted { + c.Release() + return nil, nil, errors.New("negotiate authentication should not be completed yet") + } + if n == 0 { + c.Release() + return nil, nil, errors.New("negotiate token should not be empty") + } + otoken = otoken[:n] + return &ClientContext{sctxt: c, targetName: tname}, otoken, nil +} + +// Release free up resources associated with client context c. +func (c *ClientContext) Release() error { + return c.sctxt.Release() +} + +// Expiry returns c expiry time. +func (c *ClientContext) Expiry() time.Time { + return c.sctxt.Expiry() +} + +// Update advances client part of Negotiate negotiation c. It uses +// token received from the server and returns true if client part +// of authentication is complete. It also returns new token to be +// sent to the server. +func (c *ClientContext) Update(token []byte) (authCompleted bool, outputToken []byte, err error) { + otoken := make([]byte, PackageInfo.MaxToken) + authDone, n, err2 := updateContext(c.sctxt, otoken, token, c.targetName) + if err2 != nil { + return false, nil, err2 + } + if n == 0 && !authDone { + return false, nil, errors.New("negotiate token should not be empty") + } + otoken = otoken[:n] + return authDone, otoken, nil +} + +// Sizes queries the client context for the sizes used in per-message +// functions. It returns the maximum token size used in authentication +// exchanges, the maximum signature size, the preferred integral size of +// messages, the size of any security trailer, and any error. +func (c *ClientContext) Sizes() (uint32, uint32, uint32, uint32, error) { + return c.sctxt.Sizes() +} + +// MakeSignature uses the established client context to create a signature +// for the given message using the provided quality of protection flags and +// sequence number. It returns the signature token in addition to any error. +func (c *ClientContext) MakeSignature(msg []byte, qop, seqno uint32) ([]byte, error) { + return makeSignature(c.sctxt, msg, qop, seqno) +} + +// VerifySignature uses the established client context and signature token +// to check that the provided message hasn't been tampered or received out +// of sequence. It returns any quality of protection flags and any error +// that occurred. +func (c *ClientContext) VerifySignature(msg, token []byte, seqno uint32) (uint32, error) { + return verifySignature(c.sctxt, msg, token, seqno) +} + +// ServerContext is used by the server to manage all steps of Negotiate +// negotiation. Once authentication is completed the context can be +// used to impersonate client. +type ServerContext struct { + sctxt *sspi.Context +} + +// TODO: I suspect NewServerContext might be the call to complete auth sometimes (see http://blogs.technet.com/b/tristank/archive/2006/08/02/negotiate-this.aspx) - we might need to redesign this call to return authCompleted or similar + +// NewServerContext creates new server context. It uses server +// credentials created by AcquireServerCredentials and token from +// the client to start server Negotiate negotiation sequence. +// It also returns new token to be sent to the client. +func NewServerContext(cred *sspi.Credentials, token []byte) (sc *ServerContext, outputToken []byte, err error) { + otoken := make([]byte, PackageInfo.MaxToken) + c := sspi.NewServerContext(cred, sspi.ASC_REQ_CONNECTION) + authDone, n, err2 := updateContext(c, otoken, token, nil) + if err2 != nil { + return nil, nil, err2 + } + if authDone { + c.Release() + return nil, nil, errors.New("negotiate authentication should not be completed yet") + } + if n == 0 { + c.Release() + return nil, nil, errors.New("negotiate token should not be empty") + } + otoken = otoken[:n] + return &ServerContext{sctxt: c}, otoken, nil +} + +// Release free up resources associated with server context c. +func (c *ServerContext) Release() error { + return c.sctxt.Release() +} + +// Expiry returns c expiry time. +func (c *ServerContext) Expiry() time.Time { + return c.sctxt.Expiry() +} + +// Update advances server part of Negotiate negotiation c. It uses +// token received from the client and returns true if server part +// of authentication is complete. It also returns new token to be +// sent to the client. +func (c *ServerContext) Update(token []byte) (authCompleted bool, outputToken []byte, err error) { + otoken := make([]byte, PackageInfo.MaxToken) + authDone, n, err2 := updateContext(c.sctxt, otoken, token, nil) + if err2 != nil { + return false, nil, err2 + } + if n == 0 && !authDone { + return false, nil, errors.New("negotiate token should not be empty") + } + otoken = otoken[:n] + return authDone, otoken, nil +} + +// ImpersonateUser changes current OS thread user. New user is +// the user as specified by client credentials. +func (c *ServerContext) ImpersonateUser() error { + return c.sctxt.ImpersonateUser() +} + +// RevertToSelf stops impersonation. It changes current OS thread +// user to what it was before ImpersonateUser was executed. +func (c *ServerContext) RevertToSelf() error { + return c.sctxt.RevertToSelf() +} + +// Sizes queries the server context for the sizes used in per-message +// functions. It returns the maximum token size used in authentication +// exchanges, the maximum signature size, the preferred integral size of +// messages, the size of any security trailer, and any error. +func (c *ServerContext) Sizes() (uint32, uint32, uint32, uint32, error) { + return c.sctxt.Sizes() +} + +// MakeSignature uses the established server context to create a signature +// for the given message using the provided quality of protection flags and +// sequence number. It returns the signature token in addition to any error. +func (c *ServerContext) MakeSignature(msg []byte, qop, seqno uint32) ([]byte, error) { + return makeSignature(c.sctxt, msg, qop, seqno) +} + +// VerifySignature uses the established server context and signature token +// to check that the provided message hasn't been tampered or received out +// of sequence. It returns any quality of protection flags and any error +// that occurred. +func (c *ServerContext) VerifySignature(msg, token []byte, seqno uint32) (uint32, error) { + return verifySignature(c.sctxt, msg, token, seqno) +} diff --git a/vendor/github.com/alexbrainman/sspi/negotiate/negotiate_test.go b/vendor/github.com/alexbrainman/sspi/negotiate/negotiate_test.go new file mode 100644 index 00000000..e47898c7 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/negotiate/negotiate_test.go @@ -0,0 +1,312 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package negotiate_test + +import ( + "crypto/rand" + "flag" + "os" + "os/user" + "runtime" + "strings" + "syscall" + "testing" + "time" + + "github.com/alexbrainman/sspi" + "github.com/alexbrainman/sspi/negotiate" +) + +var ( + testDomain = flag.String("domain", "", "domain parameter for TestAcquireUserCredentials") + testUsername = flag.String("username", "", "username parameter for TestAcquireUserCredentials") + testPassword = flag.String("password", "", "password parameter for TestAcquireUserCredentials") +) + +func TestPackageInfo(t *testing.T) { + if negotiate.PackageInfo.Name != "Negotiate" { + t.Fatalf(`invalid Negotiate package name of %q, "Negotiate" is expected.`, negotiate.PackageInfo.Name) + } +} + +func testContextExpiry(t *testing.T, name string, c interface { + Expiry() time.Time +}) { + validFor := c.Expiry().Sub(time.Now()) + if validFor < time.Hour { + t.Errorf("%v expires in %v, more than 1 hour expected", name, validFor) + } + if validFor > 10*24*time.Hour { + t.Errorf("%v expires in %v, less than 10 days expected", name, validFor) + } +} + +func testNegotiate(t *testing.T, clientCred *sspi.Credentials, SPN string) { + if len(SPN) == 0 { + t.Log("testing with blank SPN") + } else { + t.Logf("testing with SPN=%s", SPN) + } + + serverCred, err := negotiate.AcquireServerCredentials() + if err != nil { + t.Fatal(err) + } + defer serverCred.Release() + + client, toServerToken, err := negotiate.NewClientContext(clientCred, SPN) + if err != nil { + t.Fatal(err) + } + defer client.Release() + + if len(toServerToken) == 0 { + t.Fatal("token for server cannot be empty") + } + t.Logf("sent %d bytes to server", len(toServerToken)) + + testContextExpiry(t, "client security context", client) + + server, toClientToken, err := negotiate.NewServerContext(serverCred, toServerToken) + if err != nil { + t.Fatal(err) + } + defer server.Release() + + testContextExpiry(t, "server security context", server) + + var clientDone, serverDone bool + for { + if len(toClientToken) == 0 { + break + } + t.Logf("sent %d bytes to client", len(toClientToken)) + clientDone, toServerToken, err = client.Update(toClientToken) + if err != nil { + t.Fatal(err) + } + if len(toServerToken) == 0 { + break + } + t.Logf("sent %d bytes to server", len(toServerToken)) + serverDone, toClientToken, err = server.Update(toServerToken) + if err != nil { + t.Fatal(err) + } + } + if !clientDone { + t.Fatal("client authentication should be completed now") + } + if !serverDone { + t.Fatal("server authentication should be completed now") + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + err = server.ImpersonateUser() + if err != nil { + t.Fatal(err) + } + defer server.RevertToSelf() + + _, err = user.Current() + if err != nil { + t.Fatal(err) + } +} + +func TestNegotiate(t *testing.T) { + cred, err := negotiate.AcquireCurrentUserCredentials() + if err != nil { + t.Fatal(err) + } + defer cred.Release() + + testNegotiate(t, cred, "") + + hostname, err := os.Hostname() + if err != nil { + t.Fatal(err) + } + testNegotiate(t, cred, "HOST/"+strings.ToUpper(hostname)) + + testNegotiate(t, cred, "HOST/127.0.0.1") +} + +func TestNegotiateFailure(t *testing.T) { + clientCred, err := negotiate.AcquireCurrentUserCredentials() + if err != nil { + t.Fatal(err) + } + defer clientCred.Release() + + serverCred, err := negotiate.AcquireServerCredentials() + if err != nil { + t.Fatal(err) + } + defer serverCred.Release() + + client, toServerToken, err := negotiate.NewClientContext(clientCred, "HOST/UNKNOWN_HOST_NAME") + if err != nil { + t.Fatal(err) + } + defer client.Release() + + if len(toServerToken) == 0 { + t.Fatal("token for server cannot be empty") + } + t.Logf("sent %d bytes to server", len(toServerToken)) + + server, toClientToken, err := negotiate.NewServerContext(serverCred, toServerToken) + if err != nil { + t.Fatal(err) + } + defer server.Release() + + for { + var clientDone, serverDone bool + if len(toClientToken) == 0 { + t.Fatal("token for client cannot be empty") + } + t.Logf("sent %d bytes to client", len(toClientToken)) + clientDone, toServerToken, err = client.Update(toClientToken) + if err != nil { + t.Fatal(err) + } + t.Logf("clientDone=%v serverDone=%v", clientDone, serverDone) + if clientDone { + // t.Fatal("client authentication cannot be completed") + } + if len(toServerToken) == 0 { + t.Fatal("token for server cannot be empty") + } + t.Logf("sent %d bytes to server", len(toServerToken)) + serverDone, toClientToken, err = server.Update(toServerToken) + if err != nil { + if err == sspi.SEC_E_LOGON_DENIED { + return + } + t.Fatalf("unexpected failure 0x%x: %v", uintptr(err.(syscall.Errno)), err) + } + t.Logf("clientDone=%v serverDone=%v", clientDone, serverDone) + if serverDone { + t.Fatal("server authentication cannot be completed") + } + } +} + +func TestAcquireUserCredentials(t *testing.T) { + if len(*testDomain) == 0 { + t.Skip("Skipping due to empty \"domain\" parameter") + } + if len(*testUsername) == 0 { + t.Skip("Skipping due to empty \"username\" parameter") + } + if len(*testPassword) == 0 { + t.Skip("Skipping due to empty \"password\" parameter") + } + cred, err := negotiate.AcquireUserCredentials(*testDomain, *testUsername, *testPassword) + if err != nil { + t.Fatal(err) + } + defer cred.Release() + + testNegotiate(t, cred, "") +} + +func TestSignature(t *testing.T) { + clientCred, err := negotiate.AcquireCurrentUserCredentials() + if err != nil { + t.Fatal(err) + } + defer clientCred.Release() + + serverCred, err := negotiate.AcquireServerCredentials() + if err != nil { + t.Fatal(err) + } + defer serverCred.Release() + + client, toServerToken, err := negotiate.NewClientContext(clientCred, "") + if err != nil { + t.Fatal(err) + } + defer client.Release() + + if len(toServerToken) == 0 { + t.Fatal("token for server cannot be empty") + } + + server, toClientToken, err := negotiate.NewServerContext(serverCred, toServerToken) + if err != nil { + t.Fatal(err) + } + defer server.Release() + + var clientDone, serverDone bool + for { + if len(toClientToken) == 0 { + break + } + clientDone, toServerToken, err = client.Update(toClientToken) + if err != nil { + t.Fatal(err) + } + if len(toServerToken) == 0 { + break + } + serverDone, toClientToken, err = server.Update(toServerToken) + if err != nil { + t.Fatal(err) + } + } + if !clientDone { + t.Fatal("client authentication should be completed now") + } + if !serverDone { + t.Fatal("server authentication should be completed now") + } + + clientMsg := make([]byte, 10) + _, err = rand.Read(clientMsg) + if err != nil { + t.Fatal(err) + } + t.Logf("clientMsg=%v", clientMsg) + + clientSig, err := client.MakeSignature(clientMsg, 0, 0) + if err != nil { + t.Fatal(err) + } + t.Logf("clientSig=%v", clientSig) + + _, err = server.VerifySignature(clientMsg, clientSig, 0) + if err != nil { + t.Fatal(err) + } + t.Logf("server verified client signature") + + serverMsg := make([]byte, 10) + _, err = rand.Read(serverMsg) + if err != nil { + t.Fatal(err) + } + t.Logf("serverMsg=%v", serverMsg) + + serverSig, err := server.MakeSignature(serverMsg, 0, 0) + if err != nil { + t.Fatal(err) + } + t.Logf("serverSig=%v", serverSig) + + _, err = client.VerifySignature(serverMsg, serverSig, 0) + if err != nil { + t.Fatal(err) + } + t.Logf("client verified server signature") +} diff --git a/vendor/github.com/alexbrainman/sspi/ntlm/http_test.go b/vendor/github.com/alexbrainman/sspi/ntlm/http_test.go new file mode 100644 index 00000000..72d47a53 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/ntlm/http_test.go @@ -0,0 +1,177 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package ntlm_test + +import ( + "encoding/base64" + "flag" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/alexbrainman/sspi/ntlm" +) + +var ( + testURL = flag.String("url", "", "server URL for TestNTLMHTTPClient") +) + +func newRequest() (*http.Request, error) { + req, err := http.NewRequest("GET", *testURL, nil) + if err != nil { + return nil, err + } + return req, nil +} + +func get(req *http.Request) (*http.Response, string, error) { + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, "", err + } + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, "", err + } + return res, string(body), nil +} + +func canDoNTLM() error { + req, err := newRequest() + if err != nil { + return err + } + res, _, err := get(req) + if err != nil { + return err + } + if res.StatusCode != http.StatusUnauthorized { + return fmt.Errorf("Unauthorized expected, but got %v", res.StatusCode) + } + authHeaders, found := res.Header["Www-Authenticate"] + if !found { + return fmt.Errorf("Www-Authenticate not found") + } + for _, h := range authHeaders { + if h == "NTLM" { + return nil + } + } + return fmt.Errorf("Www-Authenticate header does not contain NTLM, but has %v", authHeaders) +} + +func doNTLMNegotiate(negotiate []byte) ([]byte, error) { + req, err := newRequest() + if err != nil { + return nil, err + } + req.Header.Set("Authorization", "NTLM "+base64.StdEncoding.EncodeToString(negotiate)) + res, _, err := get(req) + if err != nil { + return nil, err + } + if res.StatusCode != http.StatusUnauthorized { + return nil, fmt.Errorf("Unauthorized expected, but got %v", res.StatusCode) + } + authHeaders, found := res.Header["Www-Authenticate"] + if !found { + return nil, fmt.Errorf("Www-Authenticate not found") + } + if len(authHeaders) != 1 { + return nil, fmt.Errorf("Only one Www-Authenticate header expected, but %d found: %v", len(authHeaders), authHeaders) + } + if len(authHeaders[0]) < 6 { + return nil, fmt.Errorf("Www-Authenticate header is to short: %q", authHeaders[0]) + } + if !strings.HasPrefix(authHeaders[0], "NTLM ") { + return nil, fmt.Errorf("Www-Authenticate header is suppose to starts with \"NTLM \", but is %q", authHeaders[0]) + } + authenticate, err := base64.StdEncoding.DecodeString(authHeaders[0][5:]) + if err != nil { + return nil, err + } + return authenticate, nil +} + +func doNTLMAuthenticate(authenticate []byte) (string, error) { + req, err := newRequest() + if err != nil { + return "", err + } + req.Header.Set("Authorization", "NTLM "+base64.StdEncoding.EncodeToString(authenticate)) + res, body, err := get(req) + if err != nil { + return "", err + } + if res.StatusCode != http.StatusOK { + return "", fmt.Errorf("OK expected, but got %v", res.StatusCode) + } + return body, nil +} + +func TestNTLMHTTPClient(t *testing.T) { + // TODO: combine client and server tests so we don't need external server + if len(*testURL) == 0 { + t.Skip("Skipping due to empty \"url\" parameter") + } + + cred, err := ntlm.AcquireCurrentUserCredentials() + if err != nil { + t.Fatal(err) + } + defer cred.Release() + + secctx, negotiate, err := ntlm.NewClientContext(cred) + if err != nil { + t.Fatal(err) + } + defer secctx.Release() + + err = canDoNTLM() + if err != nil { + t.Fatal(err) + } + challenge, err := doNTLMNegotiate(negotiate) + if err != nil { + t.Fatal(err) + } + authenticate, err := secctx.Update(challenge) + if err != nil { + t.Fatal(err) + } + _, err = doNTLMAuthenticate(authenticate) + if err != nil { + t.Fatal(err) + } +} + +// TODO: See http://www.innovation.ch/personal/ronald/ntlm.html#connections about needed to keep connection alive during authentication. + +func TestNTLMHTTPServer(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // TODO: implement NTLM authentication here + w.Write([]byte("hello")) + })) + defer ts.Close() + + res, err := http.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + got, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + if string(got) != "hello" { + t.Errorf("got %q, want hello", string(got)) + } +} diff --git a/vendor/github.com/alexbrainman/sspi/ntlm/ntlm.go b/vendor/github.com/alexbrainman/sspi/ntlm/ntlm.go new file mode 100644 index 00000000..b0a07359 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/ntlm/ntlm.go @@ -0,0 +1,265 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +// Package ntlm provides access to the Microsoft NTLM SSP Package. +// +package ntlm + +import ( + "errors" + "syscall" + "time" + "unsafe" + + "github.com/alexbrainman/sspi" +) + +// PackageInfo contains NTLM SSP package description. +var PackageInfo *sspi.PackageInfo + +func init() { + var err error + PackageInfo, err = sspi.QueryPackageInfo(sspi.NTLMSP_NAME) + if err != nil { + panic("failed to fetch NTLM package info: " + err.Error()) + } +} + +func acquireCredentials(creduse uint32, ai *sspi.SEC_WINNT_AUTH_IDENTITY) (*sspi.Credentials, error) { + c, err := sspi.AcquireCredentials(sspi.NTLMSP_NAME, creduse, (*byte)(unsafe.Pointer(ai))) + if err != nil { + return nil, err + } + return c, nil +} + +// AcquireCurrentUserCredentials acquires credentials of currently +// logged on user. These will be used by the client to authenticate +// itself to the server. It will also be used by the server +// to impersonate the user. +func AcquireCurrentUserCredentials() (*sspi.Credentials, error) { + return acquireCredentials(sspi.SECPKG_CRED_OUTBOUND, nil) +} + +// AcquireUserCredentials acquires credentials of user described by +// domain, username and password. These will be used by the client to +// authenticate itself to the server. It will also be used by the +// server to impersonate the user. +func AcquireUserCredentials(domain, username, password string) (*sspi.Credentials, error) { + if len(domain) == 0 { + return nil, errors.New("domain parameter cannot be empty") + } + if len(username) == 0 { + return nil, errors.New("username parameter cannot be empty") + } + d, err := syscall.UTF16FromString(domain) + if err != nil { + return nil, err + } + u, err := syscall.UTF16FromString(username) + if err != nil { + return nil, err + } + var p []uint16 + var plen uint32 + if len(password) > 0 { + p, err = syscall.UTF16FromString(password) + if err != nil { + return nil, err + } + plen = uint32(len(p) - 1) // do not count terminating 0 + } + ai := sspi.SEC_WINNT_AUTH_IDENTITY{ + User: &u[0], + UserLength: uint32(len(u) - 1), // do not count terminating 0 + Domain: &d[0], + DomainLength: uint32(len(d) - 1), // do not count terminating 0 + Password: &p[0], + PasswordLength: plen, + Flags: sspi.SEC_WINNT_AUTH_IDENTITY_UNICODE, + } + return acquireCredentials(sspi.SECPKG_CRED_OUTBOUND, &ai) +} + +// AcquireServerCredentials acquires server credentials that will +// be used to authenticate client. +func AcquireServerCredentials() (*sspi.Credentials, error) { + return acquireCredentials(sspi.SECPKG_CRED_INBOUND, nil) +} + +func updateContext(c *sspi.Context, dst, src []byte) (authCompleted bool, n int, err error) { + var inBuf, outBuf [1]sspi.SecBuffer + inBuf[0].Set(sspi.SECBUFFER_TOKEN, src) + inBufs := &sspi.SecBufferDesc{ + Version: sspi.SECBUFFER_VERSION, + BuffersCount: 1, + Buffers: &inBuf[0], + } + outBuf[0].Set(sspi.SECBUFFER_TOKEN, dst) + outBufs := &sspi.SecBufferDesc{ + Version: sspi.SECBUFFER_VERSION, + BuffersCount: 1, + Buffers: &outBuf[0], + } + ret := c.Update(nil, outBufs, inBufs) + switch ret { + case sspi.SEC_E_OK: + // session established -> return success + return true, int(outBuf[0].BufferSize), nil + case sspi.SEC_I_COMPLETE_NEEDED, sspi.SEC_I_COMPLETE_AND_CONTINUE: + ret = sspi.CompleteAuthToken(c.Handle, outBufs) + if ret != sspi.SEC_E_OK { + return false, 0, ret + } + case sspi.SEC_I_CONTINUE_NEEDED: + default: + return false, 0, ret + } + return false, int(outBuf[0].BufferSize), nil +} + +// ClientContext is used by the client to manage all steps of NTLM negotiation. +type ClientContext struct { + sctxt *sspi.Context +} + +// NewClientContext creates new client context. It uses client +// credentials cred generated by AcquireCurrentUserCredentials or +// AcquireUserCredentials and, if successful, outputs negotiate +// message. Negotiate message needs to be sent to the server to +// start NTLM negotiation sequence. +func NewClientContext(cred *sspi.Credentials) (*ClientContext, []byte, error) { + negotiate := make([]byte, PackageInfo.MaxToken) + c := sspi.NewClientContext(cred, sspi.ISC_REQ_CONNECTION) + authCompleted, n, err := updateContext(c, negotiate, nil) + if err != nil { + return nil, nil, err + } + if authCompleted { + c.Release() + return nil, nil, errors.New("ntlm authentication should not be completed yet") + } + if n == 0 { + c.Release() + return nil, nil, errors.New("ntlm token should not be empty") + } + negotiate = negotiate[:n] + return &ClientContext{sctxt: c}, negotiate, nil +} + +// Release free up resources associated with client context c. +func (c *ClientContext) Release() error { + return c.sctxt.Release() +} + +// Expiry returns c expiry time. +func (c *ClientContext) Expiry() time.Time { + return c.sctxt.Expiry() +} + +// Update completes client part of NTLM negotiation c. It uses +// challenge message received from the server, and generates +// authenticate message to be returned to the server. +func (c *ClientContext) Update(challenge []byte) ([]byte, error) { + authenticate := make([]byte, PackageInfo.MaxToken) + authCompleted, n, err := updateContext(c.sctxt, authenticate, challenge) + if err != nil { + return nil, err + } + if !authCompleted { + return nil, errors.New("ntlm authentication should be completed now") + } + if n == 0 { + return nil, errors.New("ntlm token should not be empty") + } + authenticate = authenticate[:n] + return authenticate, nil +} + +// Sizes queries the client context for the sizes used in per-message +// functions. It returns the maximum token size used in authentication +// exchanges, the maximum signature size, the preferred integral size of +// messages, the size of any security trailer, and any error. +func (c *ClientContext) Sizes() (uint32, uint32, uint32, uint32, error) { + return c.sctxt.Sizes() +} + +// ServerContext is used by the server to manage all steps of NTLM +// negotiation. Once authentication is completed the context can be +// used to impersonate client. +type ServerContext struct { + sctxt *sspi.Context +} + +// NewServerContext creates new server context. It uses server +// credentials created by AcquireServerCredentials and client +// negotiate message and, if successful, outputs challenge message. +// Challenge message needs to be sent to the client to continue +// NTLM negotiation sequence. +func NewServerContext(cred *sspi.Credentials, negotiate []byte) (*ServerContext, []byte, error) { + challenge := make([]byte, PackageInfo.MaxToken) + c := sspi.NewServerContext(cred, sspi.ASC_REQ_CONNECTION) + authCompleted, n, err := updateContext(c, challenge, negotiate) + if err != nil { + return nil, nil, err + } + if authCompleted { + c.Release() + return nil, nil, errors.New("ntlm authentication should not be completed yet") + } + if n == 0 { + c.Release() + return nil, nil, errors.New("ntlm token should not be empty") + } + challenge = challenge[:n] + return &ServerContext{sctxt: c}, challenge, nil +} + +// Release free up resources associated with server context c. +func (c *ServerContext) Release() error { + return c.sctxt.Release() +} + +// Expiry returns c expiry time. +func (c *ServerContext) Expiry() time.Time { + return c.sctxt.Expiry() +} + +// Update completes server part of NTLM negotiation c. It uses +// authenticate message received from the client. +func (c *ServerContext) Update(authenticate []byte) error { + authCompleted, n, err := updateContext(c.sctxt, nil, authenticate) + if err != nil { + return err + } + if !authCompleted { + return errors.New("ntlm authentication should be completed now") + } + if n != 0 { + return errors.New("ntlm token should be empty now") + } + return nil +} + +// ImpersonateUser changes current OS thread user. New user is +// the user as specified by client credentials. +func (c *ServerContext) ImpersonateUser() error { + return c.sctxt.ImpersonateUser() +} + +// RevertToSelf stops impersonation. It changes current OS thread +// user to what it was before ImpersonateUser was executed. +func (c *ServerContext) RevertToSelf() error { + return c.sctxt.RevertToSelf() +} + +// Sizes queries the server context for the sizes used in per-message +// functions. It returns the maximum token size used in authentication +// exchanges, the maximum signature size, the preferred integral size of +// messages, the size of any security trailer, and any error. +func (c *ServerContext) Sizes() (uint32, uint32, uint32, uint32, error) { + return c.sctxt.Sizes() +} diff --git a/vendor/github.com/alexbrainman/sspi/ntlm/ntlm_test.go b/vendor/github.com/alexbrainman/sspi/ntlm/ntlm_test.go new file mode 100644 index 00000000..730d1509 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/ntlm/ntlm_test.go @@ -0,0 +1,119 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package ntlm_test + +import ( + "flag" + "os/user" + "runtime" + "testing" + "time" + + "github.com/alexbrainman/sspi" + "github.com/alexbrainman/sspi/ntlm" +) + +var ( + testDomain = flag.String("domain", "", "domain parameter for TestAcquireUserCredentials") + testUsername = flag.String("username", "", "username parameter for TestAcquireUserCredentials") + testPassword = flag.String("password", "", "password parameter for TestAcquireUserCredentials") +) + +func TestPackageInfo(t *testing.T) { + if ntlm.PackageInfo.Name != "NTLM" { + t.Fatalf(`invalid NTLM package name of %q, "NTLM" is expected.`, ntlm.PackageInfo.Name) + } +} + +func testContextExpiry(t *testing.T, name string, c interface { + Expiry() time.Time +}) { + validFor := c.Expiry().Sub(time.Now()) + if validFor < time.Hour { + t.Errorf("%v exipries in %v, more then 1 hour expected", name, validFor) + } + if validFor > 10*24*time.Hour { + t.Errorf("%v exipries in %v, less then 10 days expected", name, validFor) + } +} + +func testNTLM(t *testing.T, clientCred *sspi.Credentials) { + serverCred, err := ntlm.AcquireServerCredentials() + if err != nil { + t.Fatal(err) + } + defer serverCred.Release() + + client, token1, err := ntlm.NewClientContext(clientCred) + if err != nil { + t.Fatal(err) + } + defer client.Release() + + testContextExpiry(t, "clent security context", client) + + server, token2, err := ntlm.NewServerContext(serverCred, token1) + if err != nil { + t.Fatal(err) + } + defer server.Release() + + testContextExpiry(t, "server security context", server) + + token3, err := client.Update(token2) + if err != nil { + t.Fatal(err) + } + + err = server.Update(token3) + if err != nil { + t.Fatal(err) + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + err = server.ImpersonateUser() + if err != nil { + t.Fatal(err) + } + defer server.RevertToSelf() + + _, err = user.Current() + if err != nil { + t.Fatal(err) + } +} + +func TestNTLM(t *testing.T) { + cred, err := ntlm.AcquireCurrentUserCredentials() + if err != nil { + t.Fatal(err) + } + defer cred.Release() + + testNTLM(t, cred) +} + +func TestAcquireUserCredentials(t *testing.T) { + if len(*testDomain) == 0 { + t.Skip("Skipping due to empty \"domain\" parameter") + } + if len(*testUsername) == 0 { + t.Skip("Skipping due to empty \"username\" parameter") + } + if len(*testPassword) == 0 { + t.Skip("Skipping due to empty \"password\" parameter") + } + cred, err := ntlm.AcquireUserCredentials(*testDomain, *testUsername, *testPassword) + if err != nil { + t.Fatal(err) + } + defer cred.Release() + + testNTLM(t, cred) +} diff --git a/vendor/github.com/alexbrainman/sspi/schannel/attribute.go b/vendor/github.com/alexbrainman/sspi/schannel/attribute.go new file mode 100644 index 00000000..79d8ed2b --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/schannel/attribute.go @@ -0,0 +1,82 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package schannel + +import ( + "syscall" + "unsafe" + + "github.com/alexbrainman/sspi" +) + +// TODO: maybe move all these into a separate package or something + +func (c *Client) streamSizes() (*_SecPkgContext_StreamSizes, error) { + // TODO: do not retrive _SecPkgContext_StreamSizes every time (cache the data and invalidate it every time is possible can be changed: handshake, redo, ...) + // TODO: maybe return (header, trailer, maxmsg int, err error) instead + // TODO: maybe this needs to be exported + var ss _SecPkgContext_StreamSizes + ret := sspi.QueryContextAttributes(c.ctx.Handle, _SECPKG_ATTR_STREAM_SIZES, (*byte)(unsafe.Pointer(&ss))) + if ret != sspi.SEC_E_OK { + return nil, ret + } + return &ss, nil +} + +func (c *Client) ProtocolInfo() (name string, major, minor uint32, err error) { + var pi _SecPkgContext_ProtoInfo + ret := sspi.QueryContextAttributes(c.ctx.Handle, _SECPKG_ATTR_PROTO_INFO, (*byte)(unsafe.Pointer(&pi))) + if ret != sspi.SEC_E_OK { + return "", 0, 0, ret + } + defer sspi.FreeContextBuffer((*byte)(unsafe.Pointer(pi.ProtocolName))) + s := syscall.UTF16ToString((*[2 << 20]uint16)(unsafe.Pointer(pi.ProtocolName))[:]) + return s, pi.MajorVersion, pi.MinorVersion, nil +} + +func (c *Client) UserName() (string, error) { + var ns _SecPkgContext_Names + ret := sspi.QueryContextAttributes(c.ctx.Handle, _SECPKG_ATTR_NAMES, (*byte)(unsafe.Pointer(&ns))) + if ret != sspi.SEC_E_OK { + return "", ret + } + defer sspi.FreeContextBuffer((*byte)(unsafe.Pointer(ns.UserName))) + s := syscall.UTF16ToString((*[2 << 20]uint16)(unsafe.Pointer(ns.UserName))[:]) + return s, nil +} + +func (c *Client) AuthorityName() (string, error) { + var a _SecPkgContext_Authority + ret := sspi.QueryContextAttributes(c.ctx.Handle, _SECPKG_ATTR_AUTHORITY, (*byte)(unsafe.Pointer(&a))) + if ret != sspi.SEC_E_OK { + return "", ret + } + defer sspi.FreeContextBuffer((*byte)(unsafe.Pointer(a.AuthorityName))) + s := syscall.UTF16ToString((*[2 << 20]uint16)(unsafe.Pointer(a.AuthorityName))[:]) + return s, nil +} + +func (c *Client) KeyInfo() (sessionKeySize uint32, sigAlg uint32, sigAlgName string, encAlg uint32, encAlgName string, err error) { + var ki _SecPkgContext_KeyInfo + ret := sspi.QueryContextAttributes(c.ctx.Handle, _SECPKG_ATTR_KEY_INFO, (*byte)(unsafe.Pointer(&ki))) + if ret != sspi.SEC_E_OK { + return 0, 0, "", 0, "", ret + } + defer sspi.FreeContextBuffer((*byte)(unsafe.Pointer(ki.SignatureAlgorithmName))) + defer sspi.FreeContextBuffer((*byte)(unsafe.Pointer(ki.EncryptAlgorithmName))) + saname := syscall.UTF16ToString((*[2 << 20]uint16)(unsafe.Pointer(ki.SignatureAlgorithmName))[:]) + eaname := syscall.UTF16ToString((*[2 << 20]uint16)(unsafe.Pointer(ki.EncryptAlgorithmName))[:]) + return ki.KeySize, ki.SignatureAlgorithm, saname, ki.EncryptAlgorithm, eaname, nil +} + +// Sizes queries the context for the sizes used in per-message functions. +// It returns the maximum token size used in authentication exchanges, the +// maximum signature size, the preferred integral size of messages, the +// size of any security trailer, and any error. +func (c *Client) Sizes() (uint32, uint32, uint32, uint32, error) { + return c.ctx.Sizes() +} diff --git a/vendor/github.com/alexbrainman/sspi/schannel/buffer.go b/vendor/github.com/alexbrainman/sspi/schannel/buffer.go new file mode 100644 index 00000000..7c0aa4c6 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/schannel/buffer.go @@ -0,0 +1,78 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package schannel + +import ( + "io" + + "github.com/alexbrainman/sspi" +) + +type inputBuffer struct { + data []byte + reader io.Reader +} + +func newInputBuffer(initialsize int, reader io.Reader) *inputBuffer { + return &inputBuffer{ + data: make([]byte, 0, initialsize), + reader: reader, + } +} + +// copy copies data d into buffer ib. copy grows destination if needed. +func (ib *inputBuffer) copy(d []byte) int { + // TODO: check all call sites, maybe this can be made more efficient + return copy(ib.data, d) +} + +func (ib *inputBuffer) reset() { + ib.data = ib.data[:0] +} + +func (ib *inputBuffer) grow() { + b := make([]byte, len(ib.data), cap(ib.data)*2) + copy(b, ib.data) + ib.data = b +} + +func (ib *inputBuffer) readMore() error { + if len(ib.data) == cap(ib.data) { + ib.grow() + } + n0 := len(ib.data) + ib.data = ib.data[:cap(ib.data)] + n, err := ib.reader.Read(ib.data[n0:]) + if err != nil { + return err + } + ib.data = ib.data[:n0+n] + return nil +} + +func (ib *inputBuffer) bytes() []byte { + return ib.data +} + +func sendOutBuffer(w io.Writer, b *sspi.SecBuffer) error { + _, err := b.WriteAll(w) + // TODO: see if I can preallocate buffers instead + b.Free() + b.Set(sspi.SECBUFFER_TOKEN, nil) + return err +} + +// indexOfSecBuffer searches buffers bs for buffer type buftype. +// It returns -1 if not found. +func indexOfSecBuffer(bs []sspi.SecBuffer, buftype uint32) int { + for i := range bs { + if bs[i].BufferType == buftype { + return i + } + } + return -1 +} diff --git a/vendor/github.com/alexbrainman/sspi/schannel/client.go b/vendor/github.com/alexbrainman/sspi/schannel/client.go new file mode 100644 index 00000000..99f3016d --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/schannel/client.go @@ -0,0 +1,276 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package schannel + +import ( + "errors" + "io" + "syscall" + "unsafe" + + "github.com/alexbrainman/sspi" +) + +// TODO: add documentation + +// TODO: maybe come up with a better name + +type Client struct { + ctx *sspi.Context + conn io.ReadWriter + inbuf *inputBuffer +} + +func NewClientContext(cred *sspi.Credentials, conn io.ReadWriter) *Client { + return &Client{ + ctx: sspi.NewClientContext(cred, sspi.ISC_REQ_STREAM|sspi.ISC_REQ_ALLOCATE_MEMORY|sspi.ISC_REQ_EXTENDED_ERROR|sspi.ISC_REQ_MANUAL_CRED_VALIDATION), + conn: conn, + // TODO: decide how large this buffer needs to be (it cannot be too small otherwise messages won't fit) + inbuf: newInputBuffer(1000, conn), + } +} + +func (c *Client) Handshake(serverName string) error { + name, err := syscall.UTF16PtrFromString(serverName) + if err != nil { + return err + } + inBuf := []sspi.SecBuffer{ + {BufferType: sspi.SECBUFFER_TOKEN}, + {BufferType: sspi.SECBUFFER_EMPTY}, + } + // TODO: InitializeSecurityContext doco says that inBufs should be nil on the first call + inBufs := sspi.NewSecBufferDesc(inBuf[:]) + outBuf := []sspi.SecBuffer{ + {BufferType: sspi.SECBUFFER_TOKEN}, + } + outBufs := sspi.NewSecBufferDesc(outBuf) + + for { + ret := c.ctx.Update(name, outBufs, inBufs) + + // send data to peer + err := sendOutBuffer(c.conn, &outBuf[0]) + if err != nil { + return err + } + + // update input buffer + fetchMore := true + switch ret { + case sspi.SEC_E_OK, sspi.SEC_I_CONTINUE_NEEDED: + if inBuf[1].BufferType == sspi.SECBUFFER_EXTRA { + c.inbuf.copy(inBuf[1].Bytes()) + fetchMore = false + } else { + c.inbuf.reset() + } + } + + // decide what to do next + switch ret { + case sspi.SEC_E_OK: + // negotiation is competed + return nil + case sspi.SEC_I_CONTINUE_NEEDED, sspi.SEC_E_INCOMPLETE_MESSAGE: + // continue on + default: + return ret + } + + // fetch more input data if needed + if fetchMore { + err := c.inbuf.readMore() + if err != nil { + return err + } + } + inBuf[0].Set(sspi.SECBUFFER_TOKEN, c.inbuf.bytes()) + inBuf[1].Set(sspi.SECBUFFER_EMPTY, nil) + } +} + +// TODO: protect Handshake, Read, Write and Shutdown with locks +// TODO: call Handshake at the start Read and Write unless handshake is already complete + +func (c *Client) writeBlock(data []byte) (int, error) { + ss, err := c.streamSizes() + if err != nil { + return 0, err + } + // TODO: maybe make this buffer (and header and trailer buffers) part of Context struct + var b [4]sspi.SecBuffer + b[0].Set(sspi.SECBUFFER_STREAM_HEADER, make([]byte, ss.Header)) + b[1].Set(sspi.SECBUFFER_DATA, data) + b[2].Set(sspi.SECBUFFER_STREAM_TRAILER, make([]byte, ss.Trailer)) + b[3].Set(sspi.SECBUFFER_EMPTY, nil) + ret := sspi.EncryptMessage(c.ctx.Handle, 0, sspi.NewSecBufferDesc(b[:]), 0) + switch ret { + case sspi.SEC_E_OK: + case sspi.SEC_E_CONTEXT_EXPIRED: + // TODO: handle this + panic("writeBlock: SEC_E_CONTEXT_EXPIRED") + default: + return 0, ret + } + n1, err := b[0].WriteAll(c.conn) + if err != nil { + return n1, err + } + n2, err := b[1].WriteAll(c.conn) + if err != nil { + return n1 + n2, err + } + n3, err := b[2].WriteAll(c.conn) + return n1 + n2 + n3, err +} + +func (c *Client) Write(b []byte) (int, error) { + ss, err := c.streamSizes() + if err != nil { + return 0, err + } + // TODO: handle redoing context here + total := 0 + for len(b) > 0 { + // TODO: maybe use ss.BlockSize to decide on optimum block size + b2 := b + if len(b) > int(ss.MaximumMessage) { + b2 = b2[:ss.MaximumMessage] + } + n, err := c.writeBlock(b2) + total += n + if err != nil { + return total, err + } + b = b[len(b2):] + } + return total, nil +} + +func (c *Client) Read(data []byte) (int, error) { + if len(c.inbuf.bytes()) == 0 { + err := c.inbuf.readMore() + if err != nil { + return 0, err + } + } + var b [4]sspi.SecBuffer + desc := sspi.NewSecBufferDesc(b[:]) +loop: + for { + b[0].Set(sspi.SECBUFFER_DATA, c.inbuf.bytes()) + b[1].Set(sspi.SECBUFFER_EMPTY, nil) + b[2].Set(sspi.SECBUFFER_EMPTY, nil) + b[3].Set(sspi.SECBUFFER_EMPTY, nil) + ret := sspi.DecryptMessage(c.ctx.Handle, desc, 0, nil) + switch ret { + case sspi.SEC_E_OK: + break loop + case sspi.SEC_E_INCOMPLETE_MESSAGE: + // TODO: it seems b[0].BufferSize or b[1].BufferSize contains "how many more bytes needed for full message" - maybe use it somehow + // read more and try again + err := c.inbuf.readMore() + if err != nil { + return 0, err + } + default: + // TODO: handle other ret values + return 0, errors.New("not implemented") + } + } + i := indexOfSecBuffer(b[:], sspi.SECBUFFER_DATA) + if i == -1 { + return 0, errors.New("DecryptMessage did not return SECBUFFER_DATA") + } + n := copy(data, b[i].Bytes()) + i = indexOfSecBuffer(b[:], sspi.SECBUFFER_EXTRA) + if i == -1 { + c.inbuf.reset() + } else { + c.inbuf.copy(b[i].Bytes()) + } + return n, nil +} + +func (c *Client) applyShutdownControlToken() error { + data := uint32(_SCHANNEL_SHUTDOWN) + b := sspi.SecBuffer{ + BufferType: sspi.SECBUFFER_TOKEN, + Buffer: (*byte)(unsafe.Pointer(&data)), + BufferSize: uint32(unsafe.Sizeof(data)), + } + desc := sspi.SecBufferDesc{ + Version: sspi.SECBUFFER_VERSION, + BuffersCount: 1, + Buffers: &b, + } + ret := sspi.ApplyControlToken(c.ctx.Handle, &desc) + if ret != sspi.SEC_E_OK { + return ret + } + return nil +} + +func (c *Client) Shutdown() error { + err := c.applyShutdownControlToken() + if err != nil { + return err + } + inBuf := []sspi.SecBuffer{ + {BufferType: sspi.SECBUFFER_TOKEN}, + {BufferType: sspi.SECBUFFER_EMPTY}, + } + inBufs := sspi.NewSecBufferDesc(inBuf[:]) + outBuf := []sspi.SecBuffer{ + {BufferType: sspi.SECBUFFER_TOKEN}, + } + outBufs := sspi.NewSecBufferDesc(outBuf) + for { + // TODO: I am not sure if I can pass nil as targname + ret := c.ctx.Update(nil, outBufs, inBufs) + + // send data to peer + err := sendOutBuffer(c.conn, &outBuf[0]) + if err != nil { + return err + } + + // update input buffer + fetchMore := true + switch ret { + case sspi.SEC_E_OK, sspi.SEC_I_CONTINUE_NEEDED: + if inBuf[1].BufferType == sspi.SECBUFFER_EXTRA { + c.inbuf.copy(inBuf[1].Bytes()) + fetchMore = false + } else { + c.inbuf.reset() + } + } + + // decide what to do next + switch ret { + case sspi.SEC_E_OK, sspi.SEC_E_CONTEXT_EXPIRED: + // shutdown is competed + return nil + case sspi.SEC_I_CONTINUE_NEEDED, sspi.SEC_E_INCOMPLETE_MESSAGE: + // continue on + default: + return ret + } + + // fetch more input data if needed + if fetchMore { + err := c.inbuf.readMore() + if err != nil { + return err + } + } + inBuf[0].Set(sspi.SECBUFFER_TOKEN, c.inbuf.bytes()) + inBuf[1].Set(sspi.SECBUFFER_EMPTY, nil) + } +} diff --git a/vendor/github.com/alexbrainman/sspi/schannel/creds.go b/vendor/github.com/alexbrainman/sspi/schannel/creds.go new file mode 100644 index 00000000..058ac623 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/schannel/creds.go @@ -0,0 +1,47 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +// Package schannel provides access to the Secure Channel SSP Package. +// +package schannel + +import ( + "unsafe" + + "github.com/alexbrainman/sspi" +) + +// TODO: add documentation + +// PackageInfo contains Secure Channel SSP package description. +var PackageInfo *sspi.PackageInfo + +func init() { + var err error + PackageInfo, err = sspi.QueryPackageInfo(sspi.UNISP_NAME) + if err != nil { + panic("failed to fetch Schannel package info: " + err.Error()) + } +} + +func acquireCredentials(creduse uint32) (*sspi.Credentials, error) { + sc := &__SCHANNEL_CRED{ + Version: __SCHANNEL_CRED_VERSION, + // TODO: allow for Creds / CredCount + // TODO: allow for RootStore + // TODO: allow for EnabledProtocols + // TODO: allow for MinimumCipherStrength / MaximumCipherStrength + } + c, err := sspi.AcquireCredentials(sspi.UNISP_NAME, creduse, (*byte)(unsafe.Pointer(sc))) + if err != nil { + return nil, err + } + return c, nil +} + +func AcquireClientCredentials() (*sspi.Credentials, error) { + return acquireCredentials(sspi.SECPKG_CRED_OUTBOUND) +} diff --git a/vendor/github.com/alexbrainman/sspi/schannel/schannel_test.go b/vendor/github.com/alexbrainman/sspi/schannel/schannel_test.go new file mode 100644 index 00000000..ed7b7946 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/schannel/schannel_test.go @@ -0,0 +1,77 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package schannel_test + +import ( + "fmt" + "io/ioutil" + "net" + "testing" + + "github.com/alexbrainman/sspi/schannel" +) + +func TestPackageInfo(t *testing.T) { + want := "Microsoft Unified Security Protocol Provider" + if schannel.PackageInfo.Name != want { + t.Fatalf(`invalid Schannel package name of %q, %q is expected.`, schannel.PackageInfo.Name, want) + } +} + +func TestSchannel(t *testing.T) { + cred, err := schannel.AcquireClientCredentials() + if err != nil { + t.Fatal(err) + } + defer cred.Release() + + conn, err := net.Dial("tcp", "microsoft.com:https") + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + client := schannel.NewClientContext(cred, conn) + err = client.Handshake("microsoft.com") + if err != nil { + t.Fatal(err) + } + protoName, major, minor, err := client.ProtocolInfo() + if err != nil { + t.Fatal(err) + } + t.Logf("protocol info: %s %d.%d", protoName, major, minor) + userName, err := client.UserName() + if err != nil { + t.Fatal(err) + } + t.Logf("user name: %q", userName) + authorityName, err := client.AuthorityName() + if err != nil { + t.Fatal(err) + } + t.Logf("authority name: %q", authorityName) + sessionKeySize, sigAlg, sigAlgName, encAlg, encAlgName, err := client.KeyInfo() + if err != nil { + t.Fatal(err) + } + t.Logf("key info: session_key_size=%d signature_alg=%q(%d) encryption_alg=%q(%d)", sessionKeySize, sigAlgName, sigAlg, encAlgName, encAlg) + // TODO: add some code to verify if negotiated connection is suitable (ciper and so on) + _, err = fmt.Fprintf(client, "GET / HTTP/1.1\r\nHost: foo\r\n\r\n") + if err != nil { + t.Fatal(err) + } + data, err := ioutil.ReadAll(client) + if err != nil { + t.Fatal(err) + } + t.Logf("web page: %q", data) + err = client.Shutdown() + if err != nil { + t.Fatal(err) + } +} diff --git a/vendor/github.com/alexbrainman/sspi/schannel/syscall.go b/vendor/github.com/alexbrainman/sspi/schannel/syscall.go new file mode 100644 index 00000000..d449ecf9 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/schannel/syscall.go @@ -0,0 +1,109 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package schannel + +import ( + "syscall" +) + +// TODO: maybe put all these into a separate package, like sspi/schannel/winapi or similar + +const ( + __SCHANNEL_CRED_VERSION = 4 + + _SP_PROT_PCT1_SERVER = 0x00000001 + _SP_PROT_PCT1_CLIENT = 0x00000002 + _SP_PROT_PCT1 = _SP_PROT_PCT1_SERVER | _SP_PROT_PCT1_CLIENT + _SP_PROT_SSL2_SERVER = 0x00000004 + _SP_PROT_SSL2_CLIENT = 0x00000008 + _SP_PROT_SSL2 = _SP_PROT_SSL2_SERVER | _SP_PROT_SSL2_CLIENT + _SP_PROT_SSL3_SERVER = 0x00000010 + _SP_PROT_SSL3_CLIENT = 0x00000020 + _SP_PROT_SSL3 = _SP_PROT_SSL3_SERVER | _SP_PROT_SSL3_CLIENT + _SP_PROT_TLS1_SERVER = 0x00000040 + _SP_PROT_TLS1_CLIENT = 0x00000080 + _SP_PROT_TLS1 = _SP_PROT_TLS1_SERVER | _SP_PROT_TLS1_CLIENT + _SP_PROT_SSL3TLS1_CLIENTS = _SP_PROT_TLS1_CLIENT | _SP_PROT_SSL3_CLIENT + _SP_PROT_SSL3TLS1_SERVERS = _SP_PROT_TLS1_SERVER | _SP_PROT_SSL3_SERVER + _SP_PROT_SSL3TLS1 = _SP_PROT_SSL3 | _SP_PROT_TLS1 +) + +type __SCHANNEL_CRED struct { + Version uint32 + CredCount uint32 + Creds *syscall.CertContext + RootStore syscall.Handle // TODO: make sure this field is syscall.Handle + cMappers uint32 + aphMappers uintptr + SupportedAlgCount uint32 + SupportedAlgs *uint32 + EnabledProtocols uint32 + MinimumCipherStrength uint32 + MaximumCipherStrength uint32 + SessionLifespan uint32 + Flags uint32 + CredFormat uint32 +} + +const ( + _SECPKG_ATTR_SIZES = 0 + _SECPKG_ATTR_NAMES = 1 + _SECPKG_ATTR_LIFESPAN = 2 + _SECPKG_ATTR_DCE_INFO = 3 + _SECPKG_ATTR_STREAM_SIZES = 4 + _SECPKG_ATTR_KEY_INFO = 5 + _SECPKG_ATTR_AUTHORITY = 6 + _SECPKG_ATTR_PROTO_INFO = 7 + _SECPKG_ATTR_PASSWORD_EXPIRY = 8 + _SECPKG_ATTR_SESSION_KEY = 9 + _SECPKG_ATTR_PACKAGE_INFO = 10 + _SECPKG_ATTR_USER_FLAGS = 11 + _SECPKG_ATTR_NEGOTIATION_INFO = 12 + _SECPKG_ATTR_NATIVE_NAMES = 13 + _SECPKG_ATTR_FLAGS = 14 + + _SCHANNEL_RENEGOTIATE = 0 + _SCHANNEL_SHUTDOWN = 1 + _SCHANNEL_ALERT = 2 +) + +type _SecPkgContext_StreamSizes struct { + Header uint32 + Trailer uint32 + MaximumMessage uint32 + Buffers uint32 + BlockSize uint32 +} + +type _SecPkgContext_ProtoInfo struct { + ProtocolName *uint16 + MajorVersion uint32 + MinorVersion uint32 +} + +type _SecPkgContext_Names struct { + UserName *uint16 +} + +type _SecPkgContext_Authority struct { + AuthorityName *uint16 +} + +type _SecPkgContext_KeyInfo struct { + SignatureAlgorithmName *uint16 + EncryptAlgorithmName *uint16 + KeySize uint32 + SignatureAlgorithm uint32 + EncryptAlgorithm uint32 +} + +// TODO: SecPkgContext_ConnectionInfo + +// TODO: SECPKG_ATTR_REMOTE_CERT_CONTEXT +// TODO: SECPKG_ATTR_LOCAL_CERT_CONTEXT + +// TODO: SecPkgContext_IssuerListInfoEx diff --git a/vendor/github.com/alexbrainman/sspi/sspi.go b/vendor/github.com/alexbrainman/sspi/sspi.go new file mode 100644 index 00000000..79b00c41 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/sspi.go @@ -0,0 +1,177 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package sspi + +import ( + "syscall" + "time" + "unsafe" +) + +// TODO: add documentation + +type PackageInfo struct { + Capabilities uint32 + Version uint16 + RPCID uint16 + MaxToken uint32 + Name string + Comment string +} + +func QueryPackageInfo(pkgname string) (*PackageInfo, error) { + name, err := syscall.UTF16PtrFromString(pkgname) + if err != nil { + return nil, err + } + var pi *SecPkgInfo + ret := QuerySecurityPackageInfo(name, &pi) + if ret != SEC_E_OK { + return nil, ret + } + defer FreeContextBuffer((*byte)(unsafe.Pointer(pi))) + + return &PackageInfo{ + Capabilities: pi.Capabilities, + Version: pi.Version, + RPCID: pi.RPCID, + MaxToken: pi.MaxToken, + Name: syscall.UTF16ToString((*[2 << 12]uint16)(unsafe.Pointer(pi.Name))[:]), + Comment: syscall.UTF16ToString((*[2 << 12]uint16)(unsafe.Pointer(pi.Comment))[:]), + }, nil +} + +type Credentials struct { + Handle CredHandle + expiry syscall.Filetime +} + +func AcquireCredentials(pkgname string, creduse uint32, authdata *byte) (*Credentials, error) { + name, err := syscall.UTF16PtrFromString(pkgname) + if err != nil { + return nil, err + } + var c Credentials + ret := AcquireCredentialsHandle(nil, name, creduse, nil, authdata, 0, 0, &c.Handle, &c.expiry) + if ret != SEC_E_OK { + return nil, ret + } + return &c, nil +} + +func (c *Credentials) Release() error { + ret := FreeCredentialsHandle(&c.Handle) + if ret != SEC_E_OK { + return ret + } + return nil +} + +func (c *Credentials) Expiry() time.Time { + return time.Unix(0, c.expiry.Nanoseconds()) +} + +// TODO: add functions to display and manage RequestedFlags and EstablishedFlags fields. +// TODO: maybe get rid of RequestedFlags and EstablishedFlags fields, and replace them with input parameter for New...Context and return value of Update (instead of current bool parameter). + +type updateFunc func(c *Context, targname *uint16, h, newh *CtxtHandle, out, in *SecBufferDesc) syscall.Errno + +type Context struct { + Cred *Credentials + Handle *CtxtHandle + handle CtxtHandle + updFn updateFunc + expiry syscall.Filetime + RequestedFlags uint32 + EstablishedFlags uint32 +} + +func NewClientContext(cred *Credentials, flags uint32) *Context { + return &Context{ + Cred: cred, + updFn: initialize, + RequestedFlags: flags, + } +} + +func NewServerContext(cred *Credentials, flags uint32) *Context { + return &Context{ + Cred: cred, + updFn: accept, + RequestedFlags: flags, + } +} + +func initialize(c *Context, targname *uint16, h, newh *CtxtHandle, out, in *SecBufferDesc) syscall.Errno { + return InitializeSecurityContext(&c.Cred.Handle, h, targname, c.RequestedFlags, + 0, SECURITY_NATIVE_DREP, in, 0, newh, out, &c.EstablishedFlags, &c.expiry) +} + +func accept(c *Context, targname *uint16, h, newh *CtxtHandle, out, in *SecBufferDesc) syscall.Errno { + return AcceptSecurityContext(&c.Cred.Handle, h, in, c.RequestedFlags, + SECURITY_NATIVE_DREP, newh, out, &c.EstablishedFlags, &c.expiry) +} + +func (c *Context) Update(targname *uint16, out, in *SecBufferDesc) syscall.Errno { + h := c.Handle + if c.Handle == nil { + c.Handle = &c.handle + } + return c.updFn(c, targname, h, c.Handle, out, in) +} + +func (c *Context) Release() error { + ret := DeleteSecurityContext(c.Handle) + if ret != SEC_E_OK { + return ret + } + return nil +} + +func (c *Context) Expiry() time.Time { + return time.Unix(0, c.expiry.Nanoseconds()) +} + +// TODO: add comment to function doco that this "impersonation" is applied to current OS thread. +func (c *Context) ImpersonateUser() error { + ret := ImpersonateSecurityContext(c.Handle) + if ret != SEC_E_OK { + return ret + } + return nil +} + +func (c *Context) RevertToSelf() error { + ret := RevertSecurityContext(c.Handle) + if ret != SEC_E_OK { + return ret + } + return nil +} + +// Sizes queries the context for the sizes used in per-message functions. +// It returns the maximum token size used in authentication exchanges, the +// maximum signature size, the preferred integral size of messages, the +// size of any security trailer, and any error. +func (c *Context) Sizes() (uint32, uint32, uint32, uint32, error) { + var s _SecPkgContext_Sizes + ret := QueryContextAttributes(c.Handle, _SECPKG_ATTR_SIZES, (*byte)(unsafe.Pointer(&s))) + if ret != SEC_E_OK { + return 0, 0, 0, 0, ret + } + return s.MaxToken, s.MaxSignature, s.BlockSize, s.SecurityTrailer, nil +} + +// NewSecBufferDesc returns an initialized SecBufferDesc describing the +// provided SecBuffer. +func NewSecBufferDesc(b []SecBuffer) *SecBufferDesc { + return &SecBufferDesc{ + Version: SECBUFFER_VERSION, + BuffersCount: uint32(len(b)), + Buffers: &b[0], + } +} diff --git a/vendor/github.com/alexbrainman/sspi/sspi_test.go b/vendor/github.com/alexbrainman/sspi/sspi_test.go new file mode 100644 index 00000000..5edf5617 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/sspi_test.go @@ -0,0 +1,33 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package sspi_test + +import ( + "testing" + + "github.com/alexbrainman/sspi" +) + +func TestQueryPackageInfo(t *testing.T) { + pkgnames := []string{ + sspi.NTLMSP_NAME, + sspi.MICROSOFT_KERBEROS_NAME, + sspi.NEGOSSP_NAME, + sspi.UNISP_NAME, + } + for _, name := range pkgnames { + pi, err := sspi.QueryPackageInfo(name) + if err != nil { + t.Error(err) + continue + } + if pi.Name != name { + t.Errorf("unexpected package name %q returned for %q package: package info is %#v", pi.Name, name, pi) + continue + } + } +} diff --git a/vendor/github.com/alexbrainman/sspi/syscall.go b/vendor/github.com/alexbrainman/sspi/syscall.go new file mode 100644 index 00000000..04660df2 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/syscall.go @@ -0,0 +1,174 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package sspi + +import ( + "syscall" +) + +const ( + SEC_E_OK = syscall.Errno(0) + + SEC_I_COMPLETE_AND_CONTINUE = syscall.Errno(590612) + SEC_I_COMPLETE_NEEDED = syscall.Errno(590611) + SEC_I_CONTINUE_NEEDED = syscall.Errno(590610) + + SEC_E_LOGON_DENIED = syscall.Errno(0x8009030c) + SEC_E_CONTEXT_EXPIRED = syscall.Errno(0x80090317) // not sure if the value is valid + SEC_E_INCOMPLETE_MESSAGE = syscall.Errno(0x80090318) + + NTLMSP_NAME = "NTLM" + MICROSOFT_KERBEROS_NAME = "Kerberos" + NEGOSSP_NAME = "Negotiate" + UNISP_NAME = "Microsoft Unified Security Protocol Provider" + + _SECPKG_ATTR_SIZES = 0 + _SECPKG_ATTR_NAMES = 1 + _SECPKG_ATTR_LIFESPAN = 2 + _SECPKG_ATTR_DCE_INFO = 3 + _SECPKG_ATTR_STREAM_SIZES = 4 + _SECPKG_ATTR_KEY_INFO = 5 + _SECPKG_ATTR_AUTHORITY = 6 + _SECPKG_ATTR_PROTO_INFO = 7 + _SECPKG_ATTR_PASSWORD_EXPIRY = 8 + _SECPKG_ATTR_SESSION_KEY = 9 + _SECPKG_ATTR_PACKAGE_INFO = 10 + _SECPKG_ATTR_USER_FLAGS = 11 + _SECPKG_ATTR_NEGOTIATION_INFO = 12 + _SECPKG_ATTR_NATIVE_NAMES = 13 + _SECPKG_ATTR_FLAGS = 14 +) + +type SecPkgInfo struct { + Capabilities uint32 + Version uint16 + RPCID uint16 + MaxToken uint32 + Name *uint16 + Comment *uint16 +} + +type _SecPkgContext_Sizes struct { + MaxToken uint32 + MaxSignature uint32 + BlockSize uint32 + SecurityTrailer uint32 +} + +//sys QuerySecurityPackageInfo(pkgname *uint16, pkginfo **SecPkgInfo) (ret syscall.Errno) = secur32.QuerySecurityPackageInfoW +//sys FreeContextBuffer(buf *byte) (ret syscall.Errno) = secur32.FreeContextBuffer + +const ( + SECPKG_CRED_INBOUND = 1 + SECPKG_CRED_OUTBOUND = 2 + SECPKG_CRED_BOTH = (SECPKG_CRED_OUTBOUND | SECPKG_CRED_INBOUND) + + SEC_WINNT_AUTH_IDENTITY_UNICODE = 0x2 +) + +type SEC_WINNT_AUTH_IDENTITY struct { + User *uint16 + UserLength uint32 + Domain *uint16 + DomainLength uint32 + Password *uint16 + PasswordLength uint32 + Flags uint32 +} + +type LUID struct { + LowPart uint32 + HighPart int32 +} + +type CredHandle struct { + Lower uintptr + Upper uintptr +} + +//sys AcquireCredentialsHandle(principal *uint16, pkgname *uint16, creduse uint32, logonid *LUID, authdata *byte, getkeyfn uintptr, getkeyarg uintptr, handle *CredHandle, expiry *syscall.Filetime) (ret syscall.Errno) = secur32.AcquireCredentialsHandleW +//sys FreeCredentialsHandle(handle *CredHandle) (ret syscall.Errno) = secur32.FreeCredentialsHandle + +const ( + SECURITY_NATIVE_DREP = 16 + + SECBUFFER_DATA = 1 + SECBUFFER_TOKEN = 2 + SECBUFFER_PKG_PARAMS = 3 + SECBUFFER_MISSING = 4 + SECBUFFER_EXTRA = 5 + SECBUFFER_STREAM_TRAILER = 6 + SECBUFFER_STREAM_HEADER = 7 + SECBUFFER_PADDING = 9 + SECBUFFER_STREAM = 10 + SECBUFFER_READONLY = 0x80000000 + SECBUFFER_ATTRMASK = 0xf0000000 + SECBUFFER_VERSION = 0 + SECBUFFER_EMPTY = 0 + + ISC_REQ_DELEGATE = 1 + ISC_REQ_MUTUAL_AUTH = 2 + ISC_REQ_REPLAY_DETECT = 4 + ISC_REQ_SEQUENCE_DETECT = 8 + ISC_REQ_CONFIDENTIALITY = 16 + ISC_REQ_USE_SESSION_KEY = 32 + ISC_REQ_PROMPT_FOR_CREDS = 64 + ISC_REQ_USE_SUPPLIED_CREDS = 128 + ISC_REQ_ALLOCATE_MEMORY = 256 + ISC_REQ_USE_DCE_STYLE = 512 + ISC_REQ_DATAGRAM = 1024 + ISC_REQ_CONNECTION = 2048 + ISC_REQ_EXTENDED_ERROR = 16384 + ISC_REQ_STREAM = 32768 + ISC_REQ_INTEGRITY = 65536 + ISC_REQ_MANUAL_CRED_VALIDATION = 524288 + ISC_REQ_HTTP = 268435456 + + ASC_REQ_DELEGATE = 1 + ASC_REQ_MUTUAL_AUTH = 2 + ASC_REQ_REPLAY_DETECT = 4 + ASC_REQ_SEQUENCE_DETECT = 8 + ASC_REQ_CONFIDENTIALITY = 16 + ASC_REQ_USE_SESSION_KEY = 32 + ASC_REQ_ALLOCATE_MEMORY = 256 + ASC_REQ_USE_DCE_STYLE = 512 + ASC_REQ_DATAGRAM = 1024 + ASC_REQ_CONNECTION = 2048 + ASC_REQ_EXTENDED_ERROR = 32768 + ASC_REQ_STREAM = 65536 + ASC_REQ_INTEGRITY = 131072 +) + +type CtxtHandle struct { + Lower uintptr + Upper uintptr +} + +type SecBuffer struct { + BufferSize uint32 + BufferType uint32 + Buffer *byte +} + +type SecBufferDesc struct { + Version uint32 + BuffersCount uint32 + Buffers *SecBuffer +} + +//sys InitializeSecurityContext(credential *CredHandle, context *CtxtHandle, targname *uint16, contextreq uint32, reserved1 uint32, targdatarep uint32, input *SecBufferDesc, reserved2 uint32, newcontext *CtxtHandle, output *SecBufferDesc, contextattr *uint32, expiry *syscall.Filetime) (ret syscall.Errno) = secur32.InitializeSecurityContextW +//sys AcceptSecurityContext(credential *CredHandle, context *CtxtHandle, input *SecBufferDesc, contextreq uint32, targdatarep uint32, newcontext *CtxtHandle, output *SecBufferDesc, contextattr *uint32, expiry *syscall.Filetime) (ret syscall.Errno) = secur32.AcceptSecurityContext +//sys CompleteAuthToken(context *CtxtHandle, token *SecBufferDesc) (ret syscall.Errno) = secur32.CompleteAuthToken +//sys DeleteSecurityContext(context *CtxtHandle) (ret syscall.Errno) = secur32.DeleteSecurityContext +//sys ImpersonateSecurityContext(context *CtxtHandle) (ret syscall.Errno) = secur32.ImpersonateSecurityContext +//sys RevertSecurityContext(context *CtxtHandle) (ret syscall.Errno) = secur32.RevertSecurityContext +//sys QueryContextAttributes(context *CtxtHandle, attribute uint32, buf *byte) (ret syscall.Errno) = secur32.QueryContextAttributesW +//sys EncryptMessage(context *CtxtHandle, qop uint32, message *SecBufferDesc, messageseqno uint32) (ret syscall.Errno) = secur32.EncryptMessage +//sys DecryptMessage(context *CtxtHandle, message *SecBufferDesc, messageseqno uint32, qop *uint32) (ret syscall.Errno) = secur32.DecryptMessage +//sys ApplyControlToken(context *CtxtHandle, input *SecBufferDesc) (ret syscall.Errno) = secur32.ApplyControlToken +//sys MakeSignature(context *CtxtHandle, qop uint32, message *SecBufferDesc, messageseqno uint32) (ret syscall.Errno) = secur32.MakeSignature +//sys VerifySignature(context *CtxtHandle, message *SecBufferDesc, messageseqno uint32, qop *uint32) (ret syscall.Errno) = secur32.VerifySignature diff --git a/vendor/github.com/alexbrainman/sspi/zsyscall_windows.go b/vendor/github.com/alexbrainman/sspi/zsyscall_windows.go new file mode 100644 index 00000000..55e82099 --- /dev/null +++ b/vendor/github.com/alexbrainman/sspi/zsyscall_windows.go @@ -0,0 +1,152 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package sspi + +import ( + "syscall" + "unsafe" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modsecur32 = syscall.NewLazyDLL("secur32.dll") + + procQuerySecurityPackageInfoW = modsecur32.NewProc("QuerySecurityPackageInfoW") + procFreeContextBuffer = modsecur32.NewProc("FreeContextBuffer") + procAcquireCredentialsHandleW = modsecur32.NewProc("AcquireCredentialsHandleW") + procFreeCredentialsHandle = modsecur32.NewProc("FreeCredentialsHandle") + procInitializeSecurityContextW = modsecur32.NewProc("InitializeSecurityContextW") + procAcceptSecurityContext = modsecur32.NewProc("AcceptSecurityContext") + procCompleteAuthToken = modsecur32.NewProc("CompleteAuthToken") + procDeleteSecurityContext = modsecur32.NewProc("DeleteSecurityContext") + procImpersonateSecurityContext = modsecur32.NewProc("ImpersonateSecurityContext") + procRevertSecurityContext = modsecur32.NewProc("RevertSecurityContext") + procQueryContextAttributesW = modsecur32.NewProc("QueryContextAttributesW") + procEncryptMessage = modsecur32.NewProc("EncryptMessage") + procDecryptMessage = modsecur32.NewProc("DecryptMessage") + procApplyControlToken = modsecur32.NewProc("ApplyControlToken") + procMakeSignature = modsecur32.NewProc("MakeSignature") + procVerifySignature = modsecur32.NewProc("VerifySignature") +) + +func QuerySecurityPackageInfo(pkgname *uint16, pkginfo **SecPkgInfo) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall(procQuerySecurityPackageInfoW.Addr(), 2, uintptr(unsafe.Pointer(pkgname)), uintptr(unsafe.Pointer(pkginfo)), 0) + ret = syscall.Errno(r0) + return +} + +func FreeContextBuffer(buf *byte) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall(procFreeContextBuffer.Addr(), 1, uintptr(unsafe.Pointer(buf)), 0, 0) + ret = syscall.Errno(r0) + return +} + +func AcquireCredentialsHandle(principal *uint16, pkgname *uint16, creduse uint32, logonid *LUID, authdata *byte, getkeyfn uintptr, getkeyarg uintptr, handle *CredHandle, expiry *syscall.Filetime) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall9(procAcquireCredentialsHandleW.Addr(), 9, uintptr(unsafe.Pointer(principal)), uintptr(unsafe.Pointer(pkgname)), uintptr(creduse), uintptr(unsafe.Pointer(logonid)), uintptr(unsafe.Pointer(authdata)), uintptr(getkeyfn), uintptr(getkeyarg), uintptr(unsafe.Pointer(handle)), uintptr(unsafe.Pointer(expiry))) + ret = syscall.Errno(r0) + return +} + +func FreeCredentialsHandle(handle *CredHandle) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall(procFreeCredentialsHandle.Addr(), 1, uintptr(unsafe.Pointer(handle)), 0, 0) + ret = syscall.Errno(r0) + return +} + +func InitializeSecurityContext(credential *CredHandle, context *CtxtHandle, targname *uint16, contextreq uint32, reserved1 uint32, targdatarep uint32, input *SecBufferDesc, reserved2 uint32, newcontext *CtxtHandle, output *SecBufferDesc, contextattr *uint32, expiry *syscall.Filetime) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall12(procInitializeSecurityContextW.Addr(), 12, uintptr(unsafe.Pointer(credential)), uintptr(unsafe.Pointer(context)), uintptr(unsafe.Pointer(targname)), uintptr(contextreq), uintptr(reserved1), uintptr(targdatarep), uintptr(unsafe.Pointer(input)), uintptr(reserved2), uintptr(unsafe.Pointer(newcontext)), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(contextattr)), uintptr(unsafe.Pointer(expiry))) + ret = syscall.Errno(r0) + return +} + +func AcceptSecurityContext(credential *CredHandle, context *CtxtHandle, input *SecBufferDesc, contextreq uint32, targdatarep uint32, newcontext *CtxtHandle, output *SecBufferDesc, contextattr *uint32, expiry *syscall.Filetime) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall9(procAcceptSecurityContext.Addr(), 9, uintptr(unsafe.Pointer(credential)), uintptr(unsafe.Pointer(context)), uintptr(unsafe.Pointer(input)), uintptr(contextreq), uintptr(targdatarep), uintptr(unsafe.Pointer(newcontext)), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(contextattr)), uintptr(unsafe.Pointer(expiry))) + ret = syscall.Errno(r0) + return +} + +func CompleteAuthToken(context *CtxtHandle, token *SecBufferDesc) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall(procCompleteAuthToken.Addr(), 2, uintptr(unsafe.Pointer(context)), uintptr(unsafe.Pointer(token)), 0) + ret = syscall.Errno(r0) + return +} + +func DeleteSecurityContext(context *CtxtHandle) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall(procDeleteSecurityContext.Addr(), 1, uintptr(unsafe.Pointer(context)), 0, 0) + ret = syscall.Errno(r0) + return +} + +func ImpersonateSecurityContext(context *CtxtHandle) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall(procImpersonateSecurityContext.Addr(), 1, uintptr(unsafe.Pointer(context)), 0, 0) + ret = syscall.Errno(r0) + return +} + +func RevertSecurityContext(context *CtxtHandle) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall(procRevertSecurityContext.Addr(), 1, uintptr(unsafe.Pointer(context)), 0, 0) + ret = syscall.Errno(r0) + return +} + +func QueryContextAttributes(context *CtxtHandle, attribute uint32, buf *byte) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall(procQueryContextAttributesW.Addr(), 3, uintptr(unsafe.Pointer(context)), uintptr(attribute), uintptr(unsafe.Pointer(buf))) + ret = syscall.Errno(r0) + return +} + +func EncryptMessage(context *CtxtHandle, qop uint32, message *SecBufferDesc, messageseqno uint32) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall6(procEncryptMessage.Addr(), 4, uintptr(unsafe.Pointer(context)), uintptr(qop), uintptr(unsafe.Pointer(message)), uintptr(messageseqno), 0, 0) + ret = syscall.Errno(r0) + return +} + +func DecryptMessage(context *CtxtHandle, message *SecBufferDesc, messageseqno uint32, qop *uint32) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall6(procDecryptMessage.Addr(), 4, uintptr(unsafe.Pointer(context)), uintptr(unsafe.Pointer(message)), uintptr(messageseqno), uintptr(unsafe.Pointer(qop)), 0, 0) + ret = syscall.Errno(r0) + return +} + +func ApplyControlToken(context *CtxtHandle, input *SecBufferDesc) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall(procApplyControlToken.Addr(), 2, uintptr(unsafe.Pointer(context)), uintptr(unsafe.Pointer(input)), 0) + ret = syscall.Errno(r0) + return +} + +func MakeSignature(context *CtxtHandle, qop uint32, message *SecBufferDesc, messageseqno uint32) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall6(procMakeSignature.Addr(), 4, uintptr(unsafe.Pointer(context)), uintptr(qop), uintptr(unsafe.Pointer(message)), uintptr(messageseqno), 0, 0) + ret = syscall.Errno(r0) + return +} + +func VerifySignature(context *CtxtHandle, message *SecBufferDesc, messageseqno uint32, qop *uint32) (ret syscall.Errno) { + r0, _, _ := syscall.Syscall6(procVerifySignature.Addr(), 4, uintptr(unsafe.Pointer(context)), uintptr(unsafe.Pointer(message)), uintptr(messageseqno), uintptr(unsafe.Pointer(qop)), 0, 0) + ret = syscall.Errno(r0) + return +}