From f39878e011c8e7675572f710abcc4cba4a600cc5 Mon Sep 17 00:00:00 2001 From: sthetz Date: Mon, 18 Mar 2024 20:53:32 +0300 Subject: [PATCH 1/2] Update masterzen/winrm --- go.mod | 18 +- go.sum | 55 +- .../github.com/Azure/go-ntlmssp/.travis.yml | 2 +- .../github.com/Azure/go-ntlmssp/SECURITY.md | 41 ++ .../Azure/go-ntlmssp/authenticate_message.go | 63 +- .../github.com/Azure/go-ntlmssp/authheader.go | 45 +- .../github.com/Azure/go-ntlmssp/negotiator.go | 29 +- .../ChrisTrenkamp/goxpath/README.md | 3 +- .../ChrisTrenkamp/goxpath/parser/ast.go | 6 +- .../ChrisTrenkamp/goxpath/parser/parser.go | 6 + vendor/github.com/bodgit/ntlmssp/LICENSE | 30 + vendor/github.com/bodgit/ntlmssp/README.md | 17 + .../bodgit/ntlmssp/authenticate_message.go | 246 ++++++++ vendor/github.com/bodgit/ntlmssp/avpair.go | 150 +++++ vendor/github.com/bodgit/ntlmssp/bindings.go | 55 ++ .../bodgit/ntlmssp/challenge_message.go | 133 +++++ vendor/github.com/bodgit/ntlmssp/client.go | 267 +++++++++ vendor/github.com/bodgit/ntlmssp/crypto.go | 133 +++++ .../github.com/bodgit/ntlmssp/domain_posix.go | 9 + .../bodgit/ntlmssp/domain_windows.go | 25 + vendor/github.com/bodgit/ntlmssp/flags.go | 116 ++++ .../github.com/bodgit/ntlmssp/http/client.go | 297 +++++++++ .../github.com/bodgit/ntlmssp/http/crypto.go | 67 +++ vendor/github.com/bodgit/ntlmssp/http/mime.go | 186 ++++++ vendor/github.com/bodgit/ntlmssp/http/util.go | 7 + .../bodgit/ntlmssp/message_header.go | 25 + .../bodgit/ntlmssp/negotiate_message.go | 110 ++++ vendor/github.com/bodgit/ntlmssp/ntlm.go | 251 ++++++++ vendor/github.com/bodgit/ntlmssp/payload.go | 74 +++ vendor/github.com/bodgit/ntlmssp/security.go | 204 +++++++ vendor/github.com/bodgit/ntlmssp/unicode.go | 21 + vendor/github.com/bodgit/ntlmssp/util.go | 18 + vendor/github.com/bodgit/ntlmssp/version.go | 40 ++ .../bodgit/ntlmssp/version_posix.go | 9 + .../bodgit/ntlmssp/version_windows.go | 20 + .../github.com/bodgit/ntlmssp/workstation.go | 16 + .../github.com/bodgit/windows/.golangci.yaml | 13 + .../github.com/bodgit/windows/.goreleaser.yml | 7 + vendor/github.com/bodgit/windows/LICENSE | 30 + vendor/github.com/bodgit/windows/README.md | 14 + vendor/github.com/bodgit/windows/filetime.go | 44 ++ vendor/github.com/go-logr/logr/.golangci.yaml | 26 + vendor/github.com/go-logr/logr/CHANGELOG.md | 6 + .../github.com/go-logr/logr/CONTRIBUTING.md | 17 + vendor/github.com/go-logr/logr/LICENSE | 201 +++++++ vendor/github.com/go-logr/logr/README.md | 393 ++++++++++++ vendor/github.com/go-logr/logr/SECURITY.md | 18 + vendor/github.com/go-logr/logr/discard.go | 24 + vendor/github.com/go-logr/logr/logr.go | 563 ++++++++++++++++++ vendor/github.com/gofrs/uuid/.travis.yml | 23 - vendor/github.com/gofrs/uuid/README.md | 10 +- vendor/github.com/gofrs/uuid/codec.go | 244 ++++---- vendor/github.com/gofrs/uuid/fuzz.go | 11 +- vendor/github.com/gofrs/uuid/generator.go | 243 ++++++-- vendor/github.com/gofrs/uuid/sql.go | 35 +- vendor/github.com/gofrs/uuid/uuid.go | 148 ++++- .../jcmturner/gokrb5/v8/client/TGSExchange.go | 7 +- .../jcmturner/gokrb5/v8/client/network.go | 3 +- .../jcmturner/gokrb5/v8/client/session.go | 4 +- .../jcmturner/gokrb5/v8/config/krb5conf.go | 8 +- .../jcmturner/gokrb5/v8/credentials/ccache.go | 4 +- .../jcmturner/gokrb5/v8/keytab/keytab.go | 6 +- .../jcmturner/gokrb5/v8/spnego/http.go | 15 +- .../gokrb5/v8/types/Authenticator.go | 4 +- .../github.com/masterzen/winrm/.golangci.yml | 44 ++ vendor/github.com/masterzen/winrm/.travis.yml | 26 - vendor/github.com/masterzen/winrm/README.md | 143 ++++- vendor/github.com/masterzen/winrm/auth.go | 29 +- vendor/github.com/masterzen/winrm/client.go | 157 +++-- vendor/github.com/masterzen/winrm/command.go | 75 ++- .../github.com/masterzen/winrm/encryption.go | 438 ++++++++++++++ vendor/github.com/masterzen/winrm/error.go | 10 +- vendor/github.com/masterzen/winrm/http.go | 44 +- vendor/github.com/masterzen/winrm/kerberos.go | 128 ++++ vendor/github.com/masterzen/winrm/ntlm.go | 24 +- .../github.com/masterzen/winrm/powershell.go | 24 +- vendor/github.com/masterzen/winrm/request.go | 32 +- vendor/github.com/masterzen/winrm/response.go | 57 +- vendor/github.com/masterzen/winrm/shell.go | 11 +- .../github.com/masterzen/winrm/soap/header.go | 152 ++--- .../masterzen/winrm/soap/namespaces.go | 26 +- vendor/github.com/tidwall/transform/LICENSE | 15 + vendor/github.com/tidwall/transform/README.md | 230 +++++++ .../github.com/tidwall/transform/transform.go | 54 ++ vendor/modules.txt | 33 +- 85 files changed, 6071 insertions(+), 596 deletions(-) create mode 100644 vendor/github.com/Azure/go-ntlmssp/SECURITY.md create mode 100644 vendor/github.com/bodgit/ntlmssp/LICENSE create mode 100644 vendor/github.com/bodgit/ntlmssp/README.md create mode 100644 vendor/github.com/bodgit/ntlmssp/authenticate_message.go create mode 100644 vendor/github.com/bodgit/ntlmssp/avpair.go create mode 100644 vendor/github.com/bodgit/ntlmssp/bindings.go create mode 100644 vendor/github.com/bodgit/ntlmssp/challenge_message.go create mode 100644 vendor/github.com/bodgit/ntlmssp/client.go create mode 100644 vendor/github.com/bodgit/ntlmssp/crypto.go create mode 100644 vendor/github.com/bodgit/ntlmssp/domain_posix.go create mode 100644 vendor/github.com/bodgit/ntlmssp/domain_windows.go create mode 100644 vendor/github.com/bodgit/ntlmssp/flags.go create mode 100644 vendor/github.com/bodgit/ntlmssp/http/client.go create mode 100644 vendor/github.com/bodgit/ntlmssp/http/crypto.go create mode 100644 vendor/github.com/bodgit/ntlmssp/http/mime.go create mode 100644 vendor/github.com/bodgit/ntlmssp/http/util.go create mode 100644 vendor/github.com/bodgit/ntlmssp/message_header.go create mode 100644 vendor/github.com/bodgit/ntlmssp/negotiate_message.go create mode 100644 vendor/github.com/bodgit/ntlmssp/ntlm.go create mode 100644 vendor/github.com/bodgit/ntlmssp/payload.go create mode 100644 vendor/github.com/bodgit/ntlmssp/security.go create mode 100644 vendor/github.com/bodgit/ntlmssp/unicode.go create mode 100644 vendor/github.com/bodgit/ntlmssp/util.go create mode 100644 vendor/github.com/bodgit/ntlmssp/version.go create mode 100644 vendor/github.com/bodgit/ntlmssp/version_posix.go create mode 100644 vendor/github.com/bodgit/ntlmssp/version_windows.go create mode 100644 vendor/github.com/bodgit/ntlmssp/workstation.go create mode 100644 vendor/github.com/bodgit/windows/.golangci.yaml create mode 100644 vendor/github.com/bodgit/windows/.goreleaser.yml create mode 100644 vendor/github.com/bodgit/windows/LICENSE create mode 100644 vendor/github.com/bodgit/windows/README.md create mode 100644 vendor/github.com/bodgit/windows/filetime.go create mode 100644 vendor/github.com/go-logr/logr/.golangci.yaml create mode 100644 vendor/github.com/go-logr/logr/CHANGELOG.md create mode 100644 vendor/github.com/go-logr/logr/CONTRIBUTING.md create mode 100644 vendor/github.com/go-logr/logr/LICENSE create mode 100644 vendor/github.com/go-logr/logr/README.md create mode 100644 vendor/github.com/go-logr/logr/SECURITY.md create mode 100644 vendor/github.com/go-logr/logr/discard.go create mode 100644 vendor/github.com/go-logr/logr/logr.go delete mode 100644 vendor/github.com/gofrs/uuid/.travis.yml create mode 100644 vendor/github.com/masterzen/winrm/.golangci.yml delete mode 100644 vendor/github.com/masterzen/winrm/.travis.yml create mode 100644 vendor/github.com/masterzen/winrm/encryption.go create mode 100644 vendor/github.com/masterzen/winrm/kerberos.go create mode 100644 vendor/github.com/tidwall/transform/LICENSE create mode 100644 vendor/github.com/tidwall/transform/README.md create mode 100644 vendor/github.com/tidwall/transform/transform.go diff --git a/go.mod b/go.mod index 274f31ce..e473607c 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.21 require ( github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 - github.com/jcmturner/gokrb5/v8 v8.4.2 - github.com/masterzen/winrm v0.0.0-20190308153735-1d17eaf15943 + github.com/jcmturner/gokrb5/v8 v8.4.4 + github.com/masterzen/winrm v0.0.0-20231227165926-e811dad5ac77 github.com/mitchellh/mapstructure v1.5.0 github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db golang.org/x/text v0.14.0 @@ -14,15 +14,18 @@ require ( ) require ( - github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 // indirect - github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022 // indirect + github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect + github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 // indirect github.com/ProtonMail/go-crypto v1.1.0-alpha.0 // indirect github.com/agext/levenshtein v1.2.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect + github.com/bodgit/ntlmssp v0.0.0-20231122144230-2b2bca29f22b // indirect + github.com/bodgit/windows v1.0.1 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/dylanmei/iso8601 v0.1.0 // indirect github.com/fatih/color v1.16.0 // indirect - github.com/gofrs/uuid v3.2.0+incompatible // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect @@ -45,10 +48,10 @@ require ( github.com/hashicorp/yamux v0.1.1 // indirect github.com/jcmturner/aescts/v2 v2.0.0 // indirect github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect - github.com/jcmturner/gofork v1.0.0 // indirect + github.com/jcmturner/gofork v1.7.6 // indirect github.com/jcmturner/goidentity/v6 v6.0.1 // indirect github.com/jcmturner/rpc/v2 v2.0.3 // indirect - github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9 // indirect + github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -58,6 +61,7 @@ require ( github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/oklog/run v1.0.0 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect + github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index 5ba8431f..b0443a69 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 h1:pSm8mp0T2OH2CPmPDPtwHPr3VAQaOwVF/JbllOPP4xA= -github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022 h1:y8Gs8CzNfDF5AZvjr+5UyGQvQEBL7pwo+v+wX6q9JI8= -github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 h1:w0E0fgc1YafGEh5cROhlROMWXiNoZqApk2PDN0M1+Ns= +github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/ProtonMail/go-crypto v1.1.0-alpha.0 h1:nHGfwXmFvJrSR9xu8qL7BkO4DqTHXE9N5vPhgY2I+j0= @@ -13,6 +13,10 @@ github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= +github.com/bodgit/ntlmssp v0.0.0-20231122144230-2b2bca29f22b h1:/rxp4dz0wtk+sumWvQpaUft0yM+YqPegRgKxqU3MXBE= +github.com/bodgit/ntlmssp v0.0.0-20231122144230-2b2bca29f22b/go.mod h1:t46HDKvw4bCyAsVTayprrRiC9UItu18q9Zo29vTGN10= +github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= +github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= @@ -35,10 +39,12 @@ github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+ github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -105,12 +111,12 @@ github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFK github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= -github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8= -github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= -github.com/jcmturner/gokrb5/v8 v8.4.2 h1:6ZIM6b/JJN0X8UM43ZOM6Z4SJzla+a/u7scXFJzodkA= -github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= +github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= @@ -126,10 +132,10 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9 h1:SmVbOZFWAlyQshuMfOkiAx1f5oUTsOGG5IXplAEYeeM= -github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= -github.com/masterzen/winrm v0.0.0-20190308153735-1d17eaf15943 h1:Bteu9XN1gkBePnKr0v1edkUo2LJRsmK5ne2FrC6yVW4= -github.com/masterzen/winrm v0.0.0-20190308153735-1d17eaf15943/go.mod h1:bsMsaiOA3CXjbJxW0a94G4PfPDj9zUmH5JoFuJ9P4o0= +github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg= +github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= +github.com/masterzen/winrm v0.0.0-20231227165926-e811dad5ac77 h1:psY7rHKhnfqjTEgkleIYpF1vVxVfYsUYFTO/cL5Z6xM= +github.com/masterzen/winrm v0.0.0-20231227165926-e811dad5ac77/go.mod h1:otHfftEJdo9JWGoq9GcJRaeNLp/uhqNq8JOk5lL+8Ks= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -168,10 +174,17 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde h1:AMNpJRc7P+GTwVbl8DkK2I9I8BBUzNiHuH/tlxrpan0= +github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde/go.mod h1:MvrEmduDUz4ST5pGZ7CABCnOU5f3ZiOAZzT6b1A6nX8= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -184,29 +197,28 @@ github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.14.2 h1:kTG7lqmBou0Zkx35r6HJHUQTvaRPr5bIAf3AoHS0izI= github.com/zclconf/go-cty v1.14.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= -golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -217,16 +229,19 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/vendor/github.com/Azure/go-ntlmssp/.travis.yml b/vendor/github.com/Azure/go-ntlmssp/.travis.yml index 7951372c..23c95fe9 100644 --- a/vendor/github.com/Azure/go-ntlmssp/.travis.yml +++ b/vendor/github.com/Azure/go-ntlmssp/.travis.yml @@ -3,7 +3,7 @@ sudo: false language: go before_script: - - go get -u github.com/golang/lint/golint + - go get -u golang.org/x/lint/golint go: - 1.10.x diff --git a/vendor/github.com/Azure/go-ntlmssp/SECURITY.md b/vendor/github.com/Azure/go-ntlmssp/SECURITY.md new file mode 100644 index 00000000..e138ec5d --- /dev/null +++ b/vendor/github.com/Azure/go-ntlmssp/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/vendor/github.com/Azure/go-ntlmssp/authenticate_message.go b/vendor/github.com/Azure/go-ntlmssp/authenticate_message.go index c6fbe444..ab183db6 100644 --- a/vendor/github.com/Azure/go-ntlmssp/authenticate_message.go +++ b/vendor/github.com/Azure/go-ntlmssp/authenticate_message.go @@ -4,7 +4,9 @@ import ( "bytes" "crypto/rand" "encoding/binary" + "encoding/hex" "errors" + "strings" "time" ) @@ -40,7 +42,7 @@ func (m authenicateMessage) MarshalBinary() ([]byte, error) { } target, user := toUnicode(m.TargetName), toUnicode(m.UserName) - workstation := toUnicode("go-ntlmssp") + workstation := toUnicode("") ptr := binary.Size(&authenticateMessageFields{}) f := authenticateMessageFields{ @@ -80,7 +82,7 @@ func (m authenicateMessage) MarshalBinary() ([]byte, error) { //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) { +func ProcessChallenge(challengeMessageData []byte, user, password string, domainNeeded bool) ([]byte, error) { if user == "" && password == "" { return nil, errors.New("Anonymous authentication not supported") } @@ -96,6 +98,10 @@ func ProcessChallenge(challengeMessageData []byte, user, password string) ([]byt if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEKEYEXCH) { return nil, errors.New("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)") } + + if !domainNeeded { + cm.TargetName = "" + } am := authenicateMessage{ UserName: user, @@ -123,6 +129,59 @@ func ProcessChallenge(challengeMessageData []byte, user, password string) ([]byt am.LmChallengeResponse = computeLmV2Response(ntlmV2Hash, cm.ServerChallenge[:], clientChallenge) } + return am.MarshalBinary() +} + +func ProcessChallengeWithHash(challengeMessageData []byte, user, hash string) ([]byte, error) { + if user == "" && hash == "" { + return nil, errors.New("Anonymous authentication not supported") + } + + var cm challengeMessage + if err := cm.UnmarshalBinary(challengeMessageData); err != nil { + return nil, err + } + + if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATELMKEY) { + return nil, errors.New("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)") + } + if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEKEYEXCH) { + return nil, errors.New("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)") + } + + am := authenicateMessage{ + UserName: user, + TargetName: cm.TargetName, + NegotiateFlags: cm.NegotiateFlags, + } + + timestamp := cm.TargetInfo[avIDMsvAvTimestamp] + if timestamp == nil { // no time sent, take current time + ft := uint64(time.Now().UnixNano()) / 100 + ft += 116444736000000000 // add time between unix & windows offset + timestamp = make([]byte, 8) + binary.LittleEndian.PutUint64(timestamp, ft) + } + + clientChallenge := make([]byte, 8) + rand.Reader.Read(clientChallenge) + hashParts := strings.Split(hash, ":") + if len(hashParts) > 1 { + hash = hashParts[1] + } + hashBytes, err := hex.DecodeString(hash) + if err != nil { + return nil, err + } + ntlmV2Hash := hmacMd5(hashBytes, toUnicode(strings.ToUpper(user)+cm.TargetName)) + + am.NtChallengeResponse = computeNtlmV2Response(ntlmV2Hash, + cm.ServerChallenge[:], clientChallenge, timestamp, cm.TargetInfoRaw) + + if cm.TargetInfoRaw == nil { + am.LmChallengeResponse = computeLmV2Response(ntlmV2Hash, + cm.ServerChallenge[:], clientChallenge) + } return am.MarshalBinary() } diff --git a/vendor/github.com/Azure/go-ntlmssp/authheader.go b/vendor/github.com/Azure/go-ntlmssp/authheader.go index aac3f77d..c9d30d32 100644 --- a/vendor/github.com/Azure/go-ntlmssp/authheader.go +++ b/vendor/github.com/Azure/go-ntlmssp/authheader.go @@ -5,26 +5,55 @@ import ( "strings" ) -type authheader string +type authheader []string func (h authheader) IsBasic() bool { - return strings.HasPrefix(string(h), "Basic ") + for _, s := range h { + if strings.HasPrefix(string(s), "Basic ") { + return true + } + } + return false +} + +func (h authheader) Basic() string { + for _, s := range h { + if strings.HasPrefix(string(s), "Basic ") { + return s + } + } + return "" } func (h authheader) IsNegotiate() bool { - return strings.HasPrefix(string(h), "Negotiate") + for _, s := range h { + if strings.HasPrefix(string(s), "Negotiate") { + return true + } + } + return false } func (h authheader) IsNTLM() bool { - return strings.HasPrefix(string(h), "NTLM") + for _, s := range h { + if strings.HasPrefix(string(s), "NTLM") { + return true + } + } + return false } func (h authheader) GetData() ([]byte, error) { - p := strings.Split(string(h), " ") - if len(p) < 2 { - return nil, nil + for _, s := range h { + if strings.HasPrefix(string(s), "NTLM") || strings.HasPrefix(string(s), "Negotiate") || strings.HasPrefix(string(s), "Basic ") { + p := strings.Split(string(s), " ") + if len(p) < 2 { + return nil, nil + } + return base64.StdEncoding.DecodeString(string(p[1])) + } } - return base64.StdEncoding.DecodeString(string(p[1])) + return nil, nil } func (h authheader) GetBasicCreds() (username, password string, err error) { diff --git a/vendor/github.com/Azure/go-ntlmssp/negotiator.go b/vendor/github.com/Azure/go-ntlmssp/negotiator.go index 6e304544..cce4955d 100644 --- a/vendor/github.com/Azure/go-ntlmssp/negotiator.go +++ b/vendor/github.com/Azure/go-ntlmssp/negotiator.go @@ -10,15 +10,22 @@ import ( ) // GetDomain : parse domain name from based on slashes in the input -func GetDomain(user string) (string, string) { +// Need to check for upn as well +func GetDomain(user string) (string, string, bool) { domain := "" + domainNeeded := false if strings.Contains(user, "\\") { ucomponents := strings.SplitN(user, "\\", 2) domain = ucomponents[0] user = ucomponents[1] + domainNeeded = true + } else if strings.Contains(user, "@") { + domainNeeded = false + } else { + domainNeeded = true } - return user, domain + return user, domain, domainNeeded } //Negotiator is a http.Roundtripper decorator that automatically @@ -34,10 +41,11 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error) rt = http.DefaultTransport } // If it is not basic auth, just round trip the request as usual - reqauth := authheader(req.Header.Get("Authorization")) + reqauth := authheader(req.Header.Values("Authorization")) if !reqauth.IsBasic() { return rt.RoundTrip(req) } + reqauthBasic := reqauth.Basic() // Save request body body := bytes.Buffer{} if req.Body != nil { @@ -59,11 +67,10 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error) if res.StatusCode != http.StatusUnauthorized { return res, err } - - resauth := authheader(res.Header.Get("Www-Authenticate")) + resauth := authheader(res.Header.Values("Www-Authenticate")) if !resauth.IsNegotiate() && !resauth.IsNTLM() { // Unauthorized, Negotiate not requested, let's try with basic auth - req.Header.Set("Authorization", string(reqauth)) + req.Header.Set("Authorization", string(reqauthBasic)) io.Copy(ioutil.Discard, res.Body) res.Body.Close() req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes())) @@ -75,7 +82,7 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error) if res.StatusCode != http.StatusUnauthorized { return res, err } - resauth = authheader(res.Header.Get("Www-Authenticate")) + resauth = authheader(res.Header.Values("Www-Authenticate")) } if resauth.IsNegotiate() || resauth.IsNTLM() { @@ -91,7 +98,7 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error) // get domain from username domain := "" - u, domain = GetDomain(u) + u, domain, domainNeeded := GetDomain(u) // send negotiate negotiateMessage, err := NewNegotiateMessage(domain, "") @@ -112,7 +119,7 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error) } // receive challenge? - resauth = authheader(res.Header.Get("Www-Authenticate")) + resauth = authheader(res.Header.Values("Www-Authenticate")) challengeMessage, err := resauth.GetData() if err != nil { return nil, err @@ -125,7 +132,7 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error) res.Body.Close() // send authenticate - authenticateMessage, err := ProcessChallenge(challengeMessage, u, p) + authenticateMessage, err := ProcessChallenge(challengeMessage, u, p, domainNeeded) if err != nil { return nil, err } @@ -137,7 +144,7 @@ func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error) req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes())) - res, err = rt.RoundTrip(req) + return rt.RoundTrip(req) } return res, err diff --git a/vendor/github.com/ChrisTrenkamp/goxpath/README.md b/vendor/github.com/ChrisTrenkamp/goxpath/README.md index deb49624..49407dd7 100644 --- a/vendor/github.com/ChrisTrenkamp/goxpath/README.md +++ b/vendor/github.com/ChrisTrenkamp/goxpath/README.md @@ -1,2 +1 @@ -# goxpath [![GoDoc](https://godoc.org/gopkg.in/src-d/go-git.v2?status.svg)](https://godoc.org/github.com/ChrisTrenkamp/goxpath) [![Build Status](https://travis-ci.org/ChrisTrenkamp/goxpath.svg?branch=master)](https://travis-ci.org/ChrisTrenkamp/goxpath) [![codecov.io](https://codecov.io/github/ChrisTrenkamp/goxpath/coverage.svg?branch=master)](https://codecov.io/github/ChrisTrenkamp/goxpath?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/ChrisTrenkamp/goxpath)](https://goreportcard.com/report/github.com/ChrisTrenkamp/goxpath) -An XPath 1.0 implementation written in Go. See the [wiki](https://github.com/ChrisTrenkamp/goxpath/wiki) for more information. +# Deprecated. Use [xsel](https://github.com/ChrisTrenkamp/xsel) instead. diff --git a/vendor/github.com/ChrisTrenkamp/goxpath/parser/ast.go b/vendor/github.com/ChrisTrenkamp/goxpath/parser/ast.go index 89678f6b..08196c62 100644 --- a/vendor/github.com/ChrisTrenkamp/goxpath/parser/ast.go +++ b/vendor/github.com/ChrisTrenkamp/goxpath/parser/ast.go @@ -27,12 +27,12 @@ var beginPathType = map[lexer.XItemType]bool{ func (n *Node) add(i lexer.XItem) { if n.Val.Typ == Empty { n.Val = i - } else if n.Left == nil { + } else if n.Left == nil && n.Right == nil { n.Left = &Node{Val: n.Val, Parent: n} n.Val = i } else if beginPathType[n.Val.Typ] { - next := &Node{Val: n.Val, Left: n.Left, Parent: n} - n.Left = next + next := &Node{Val: n.Val, Left: n.Left, Right: n.Right, Parent: n} + n.Left, n.Right = next, nil n.Val = i } else if n.Right == nil { n.Right = &Node{Val: i, Parent: n} diff --git a/vendor/github.com/ChrisTrenkamp/goxpath/parser/parser.go b/vendor/github.com/ChrisTrenkamp/goxpath/parser/parser.go index ff664ff7..e5ce8222 100644 --- a/vendor/github.com/ChrisTrenkamp/goxpath/parser/parser.go +++ b/vendor/github.com/ChrisTrenkamp/goxpath/parser/parser.go @@ -128,6 +128,12 @@ func xiEndPath(p *parseStack, i lexer.XItem) { } func xiFunc(p *parseStack, i lexer.XItem) { + if p.cur.Val.Typ == Empty { + p.cur.pushNotEmpty(i) + p.push(funcState) + p.cur = p.cur.next + return + } p.cur.push(i) p.cur = p.cur.next p.push(funcState) diff --git a/vendor/github.com/bodgit/ntlmssp/LICENSE b/vendor/github.com/bodgit/ntlmssp/LICENSE new file mode 100644 index 00000000..5df2ec52 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/LICENSE @@ -0,0 +1,30 @@ +BSD 3-Clause License + +Copyright (c) 2019, Matt Dainty +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 the copyright holder 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 HOLDER 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/bodgit/ntlmssp/README.md b/vendor/github.com/bodgit/ntlmssp/README.md new file mode 100644 index 00000000..35053ab1 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/README.md @@ -0,0 +1,17 @@ +[![Build Status](https://img.shields.io/github/actions/workflow/status/bodgit/ntlmssp/main.yml?branch=master)](https://github.com/bodgit/ntlmssp/actions?query=workflow%3Abuild) +[![Coverage Status](https://coveralls.io/repos/github/bodgit/ntlmssp/badge.svg?branch=master)](https://coveralls.io/github/bodgit/ntlmssp?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/bodgit/ntlmssp)](https://goreportcard.com/report/github.com/bodgit/ntlmssp) +[![GoDoc](https://godoc.org/github.com/bodgit/ntlmssp?status.svg)](https://godoc.org/github.com/bodgit/ntlmssp) +![Go version](https://img.shields.io/badge/Go-1.20-brightgreen.svg) +![Go version](https://img.shields.io/badge/Go-1.19-brightgreen.svg) + +ntlmssp +======= + +Feature comparison: + +| Library | NTLMv1 | NTLMv2 | Connection-orientated | Connectionless | Signing & sealing | Channel Bindings | Client | Server | Notes | +| ---------------------------------------------- |:------:|:------:|:---------------------:|:--------------:|:-----------------:|:----------------:|:------:|:------:| --------- | +| https://github.com/bodgit/ntlmssp | Yes | Yes | Yes | No | Yes | Yes | Yes | No | | +| https://github.com/Azure/go-ntlmssp | No | Yes | Yes | No | No | No | Yes | No | HTTP only | +| https://github.com/ThomsonReutersEikon/go-ntlm | Yes | Yes | No | Yes | No | No | Yes | Yes | Archived | diff --git a/vendor/github.com/bodgit/ntlmssp/authenticate_message.go b/vendor/github.com/bodgit/ntlmssp/authenticate_message.go new file mode 100644 index 00000000..064782c0 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/authenticate_message.go @@ -0,0 +1,246 @@ +package ntlmssp + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "sort" + "unsafe" +) + +var ( + errInvalidAuthenticateMessage = errors.New("invalid NTLM authenticate message") +) + +type authenticateMessageFields struct { + messageHeader + LmChallengeResponseFields payload + NtChallengeResponseFields payload + DomainNameFields payload + UserNameFields payload + WorkstationFields payload + EncryptedRandomSessionKeyFields payload + NegotiateFlags uint32 +} + +func (m *authenticateMessageFields) IsValid() bool { + return m.messageHeader.IsValid() && m.MessageType == ntLmAuthenticate +} + +type authenticateMessage struct { + authenticateMessageFields + LmChallengeResponse []uint8 + NtChallengeResponse []uint8 + DomainName string + UserName string + Workstation string + EncryptedRandomSessionKey []uint8 + Version *Version + MIC []uint8 + + // These aren't transmitted, but necessary in order to compute the MIC + ExportedSessionKey []uint8 + TargetInfo targetInfo +} + +func (m *authenticateMessage) UpdateMIC(preamble []byte) error { + if v, ok := m.TargetInfo.Get(msvAvFlags); ok { + flags := binary.LittleEndian.Uint32(v) + if flags&msvAvFlagMICProvided != 0 { + // Add an all-zero MIC first + m.MIC = zeroBytes(16) + + b, err := m.Marshal() + if err != nil { + return err + } + + // Now compute the actual MIC + m.MIC = hmacMD5(m.ExportedSessionKey, concat(preamble, b)) + } + } + + return nil +} + +func (m *authenticateMessage) Marshal() ([]byte, error) { + offset := int(unsafe.Sizeof(m.authenticateMessageFields) + unsafe.Sizeof(*m.Version)) + if m.MIC != nil { + offset += len(m.MIC) + } + + var domainName, userName, workstation []byte + if ntlmsspNegotiateUnicode.IsSet(m.NegotiateFlags) { + var err error + domainName, err = utf16FromString(m.DomainName) + if err != nil { + return nil, err + } + userName, err = utf16FromString(m.UserName) + if err != nil { + return nil, err + } + workstation, err = utf16FromString(m.Workstation) + if err != nil { + return nil, err + } + } else { + domainName = []byte(m.DomainName) + userName = []byte(m.UserName) + workstation = []byte(m.Workstation) + } + + // Microsoft MS-NLMP spec doesn't write out the payloads in the same + // order as they are defined in the struct. Copy the behaviour just so + // the various test values can be used + + m.DomainNameFields = newPayload(len(domainName), &offset) + m.UserNameFields = newPayload(len(userName), &offset) + m.WorkstationFields = newPayload(len(workstation), &offset) + + m.LmChallengeResponseFields = newPayload(len(m.LmChallengeResponse), &offset) + m.NtChallengeResponseFields = newPayload(len(m.NtChallengeResponse), &offset) + + m.EncryptedRandomSessionKeyFields = newPayload(len(m.EncryptedRandomSessionKey), &offset) + + var version = &Version{} + + m.NegotiateFlags = ntlmsspNegotiateVersion.Unset(m.NegotiateFlags) + if m.Version != nil { + m.NegotiateFlags = ntlmsspNegotiateVersion.Set(m.NegotiateFlags) + version = m.Version + } + + b := bytes.Buffer{} + if err := binary.Write(&b, binary.LittleEndian, &m.authenticateMessageFields); err != nil { + return nil, err + } + + v, err := version.marshal() + if err != nil { + return nil, err + } + b.Write(v) + + if m.MIC != nil { + b.Write(m.MIC) + } + + b.Write(domainName) + b.Write(userName) + b.Write(workstation) + + b.Write(m.LmChallengeResponse) + b.Write(m.NtChallengeResponse) + + b.Write(m.EncryptedRandomSessionKey) + + return b.Bytes(), nil +} + +func (m *authenticateMessage) Unmarshal(b []byte) error { + reader := bytes.NewReader(b) + + err := binary.Read(reader, binary.LittleEndian, &m.authenticateMessageFields) + if err != nil { + return err + } + + if !m.authenticateMessageFields.IsValid() { + return errInvalidAuthenticateMessage + } + + var offsets []int + + for _, x := range []struct { + payload *payload + bytes *[]uint8 + }{ + { + &m.LmChallengeResponseFields, + &m.LmChallengeResponse, + }, + { + &m.NtChallengeResponseFields, + &m.NtChallengeResponse, + }, + { + &m.EncryptedRandomSessionKeyFields, + &m.EncryptedRandomSessionKey, + }, + } { + offsets = append(offsets, int(x.payload.Offset)) + + if x.payload.Len > 0 { + *x.bytes, err = x.payload.ReadValue(reader) + if err != nil { + return err + } + } + } + + for _, x := range []struct { + payload *payload + string *string + }{ + { + &m.DomainNameFields, + &m.DomainName, + }, + { + &m.UserNameFields, + &m.UserName, + }, + { + &m.WorkstationFields, + &m.Workstation, + }, + } { + offsets = append(offsets, int(x.payload.Offset)) + + if x.payload.Len > 0 { + *x.string, err = x.payload.ReadString(reader, ntlmsspNegotiateUnicode.IsSet(m.NegotiateFlags)) + if err != nil { + return err + } + } + } + + if ntlmsspNegotiateVersion.IsSet(m.NegotiateFlags) { + v := Version{} + if err := v.unmarshal(reader); err != nil { + return err + } + m.Version = &v + } else { + // Seek past the version + if _, err := reader.Seek(int64(unsafe.Sizeof(*m.Version)), io.SeekCurrent); err != nil { + return err + } + } + + pos, err := reader.Seek(0, io.SeekCurrent) + if err != nil { + return err + } + + // Sort the offsets into ascending order + sort.Ints(offsets) + + // If there's a gap between the end of the version where we are and + // the first payload offset then treat the gap as a MIC + if length := offsets[0] - int(pos); length > 0 { + m.MIC = make([]byte, length) + n, err := reader.Read(m.MIC) + if err != nil { + return err + } + if n != length { + return fmt.Errorf("expected %d bytes, only read %d", length, n) + } + } + + return nil +} diff --git a/vendor/github.com/bodgit/ntlmssp/avpair.go b/vendor/github.com/bodgit/ntlmssp/avpair.go new file mode 100644 index 00000000..dc28e5c8 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/avpair.go @@ -0,0 +1,150 @@ +package ntlmssp + +import ( + "bytes" + "encoding/binary" + "encoding/gob" + "fmt" +) + +type avID uint16 + +const ( + msvAvEOL avID = iota + msvAvNbComputerName + msvAvNbDomainName + msvAvDNSComputerName + msvAvDNSDomainName + msvAvDNSTreeName + msvAvFlags + msvAvTimestamp + msvAvSingleHost + msvAvTargetName + msvChannelBindings +) + +const ( + msvAvFlagAuthenticationConstrained uint32 = 1 << iota + msvAvFlagMICProvided + msvAvFlagUntrustedSPNSource +) + +type avPair struct { + ID avID + Length uint16 +} + +type targetInfo struct { + Pairs map[avID][]uint8 + Order []avID +} + +func newTargetInfo() targetInfo { + return targetInfo{ + Pairs: make(map[avID][]uint8), + Order: []avID{}, + } +} + +func (t *targetInfo) Get(id avID) ([]uint8, bool) { + v, ok := t.Pairs[id] + return v, ok +} + +func (t *targetInfo) Set(id avID, value []uint8) { + if id == msvAvEOL { + return + } + if _, ok := t.Get(id); !ok { + t.Order = append(t.Order, id) + } + t.Pairs[id] = value +} + +func (t *targetInfo) Del(id avID) { + delete(t.Pairs, id) + j := 0 + for _, n := range t.Order { + if n != id { + t.Order[j] = n + j++ + } + } + t.Order = t.Order[:j] +} + +func (t *targetInfo) Len() int { + return len(t.Pairs) +} + +func init() { + gob.Register(targetInfo{}) +} + +func (t *targetInfo) Clone() (*targetInfo, error) { + b := bytes.Buffer{} + enc := gob.NewEncoder(&b) + dec := gob.NewDecoder(&b) + if err := enc.Encode(*t); err != nil { + return nil, err + } + var copy targetInfo + if err := dec.Decode(©); err != nil { + return nil, err + } + return ©, nil +} + +func (t *targetInfo) Marshal() ([]byte, error) { + b := bytes.Buffer{} + + for _, k := range t.Order { + if k == msvAvEOL { + continue + } + + v := t.Pairs[k] + + if err := binary.Write(&b, binary.LittleEndian, &avPair{k, uint16(len(v))}); err != nil { + return nil, err + } + + b.Write(v) + } + + // Append required MsvAvEOL pair + if err := binary.Write(&b, binary.LittleEndian, &avPair{msvAvEOL, 0}); err != nil { + return nil, err + } + + return b.Bytes(), nil +} + +func (t *targetInfo) Unmarshal(b []byte) error { + reader := bytes.NewReader(b) + + for { + var pair avPair + + if err := binary.Read(reader, binary.LittleEndian, &pair); err != nil { + return err + } + + if pair.ID == msvAvEOL { + break + } + + value := make([]byte, pair.Length) + n, err := reader.Read(value) + if err != nil { + return err + } + if n != int(pair.Length) { + return fmt.Errorf("expected %d bytes, only read %d", pair.Length, n) + } + + t.Set(pair.ID, value) + } + + return nil +} diff --git a/vendor/github.com/bodgit/ntlmssp/bindings.go b/vendor/github.com/bodgit/ntlmssp/bindings.go new file mode 100644 index 00000000..a5d530c7 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/bindings.go @@ -0,0 +1,55 @@ +package ntlmssp + +import ( + "bytes" + "encoding/binary" +) + +const ( + // TLSServerEndPoint is defined in RFC 5929 and stores the hash of the server certificate. + TLSServerEndPoint string = "tls-server-end-point" +) + +// ChannelBindings models the GSS-API channel bindings defined in RFC 2744. +type ChannelBindings struct { + InitiatorAddrtype uint32 + InitiatorAddress []uint8 + AcceptorAddrtype uint32 + AcceptorAddress []uint8 + ApplicationData []uint8 +} + +func (c *ChannelBindings) marshal() ([]byte, error) { + b := bytes.Buffer{} + + if err := binary.Write(&b, binary.LittleEndian, &c.InitiatorAddrtype); err != nil { + return nil, err + } + + length := uint32(len(c.InitiatorAddress)) + if err := binary.Write(&b, binary.LittleEndian, &length); err != nil { + return nil, err + } + + b.Write(c.InitiatorAddress) + + if err := binary.Write(&b, binary.LittleEndian, &c.AcceptorAddrtype); err != nil { + return nil, err + } + + length = uint32(len(c.AcceptorAddress)) + if err := binary.Write(&b, binary.LittleEndian, &length); err != nil { + return nil, err + } + + b.Write(c.AcceptorAddress) + + length = uint32(len(c.ApplicationData)) + if err := binary.Write(&b, binary.LittleEndian, &length); err != nil { + return nil, err + } + + b.Write(c.ApplicationData) + + return b.Bytes(), nil +} diff --git a/vendor/github.com/bodgit/ntlmssp/challenge_message.go b/vendor/github.com/bodgit/ntlmssp/challenge_message.go new file mode 100644 index 00000000..0a498b57 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/challenge_message.go @@ -0,0 +1,133 @@ +package ntlmssp + +import ( + "bytes" + "encoding/binary" + "errors" + "unsafe" +) + +var ( + errInvalidChallengeMessage = errors.New("invalid NTLM challenge message") +) + +type challengeMessageFields struct { + messageHeader + TargetNameFields payload + NegotiateFlags uint32 + ServerChallenge [8]uint8 + _ [8]uint8 + TargetInfoFields payload +} + +func (m *challengeMessageFields) IsValid() bool { + return m.messageHeader.IsValid() && m.MessageType == ntLmChallenge +} + +type challengeMessage struct { + challengeMessageFields + TargetName string + TargetInfo targetInfo + Version *Version +} + +func (m *challengeMessage) Marshal() ([]byte, error) { + offset := int(unsafe.Sizeof(m.challengeMessageFields) + unsafe.Sizeof(*m.Version)) + + m.NegotiateFlags = ntlmsspRequestTarget.Unset(m.NegotiateFlags) + if m.TargetName != "" { + m.NegotiateFlags = ntlmsspRequestTarget.Set(m.NegotiateFlags) + } + + m.NegotiateFlags = ntlmsspNegotiateTargetInfo.Unset(m.NegotiateFlags) + if m.TargetInfo.Len() > 0 { + m.NegotiateFlags = ntlmsspNegotiateTargetInfo.Set(m.NegotiateFlags) + } + + var targetName []byte + if ntlmsspNegotiateUnicode.IsSet(m.NegotiateFlags) { + var err error + targetName, err = utf16FromString(m.TargetName) + if err != nil { + return nil, err + } + } else { + targetName = []byte(m.TargetName) + } + + m.TargetNameFields = newPayload(len(targetName), &offset) + + targetInfo, err := m.TargetInfo.Marshal() + if err != nil { + return nil, err + } + + m.TargetInfoFields = newPayload(len(targetInfo), &offset) + + var version = &Version{} + + m.NegotiateFlags = ntlmsspNegotiateVersion.Unset(m.NegotiateFlags) + if m.Version != nil { + m.NegotiateFlags = ntlmsspNegotiateVersion.Set(m.NegotiateFlags) + version = m.Version + } + + b := bytes.Buffer{} + if err := binary.Write(&b, binary.LittleEndian, &m.challengeMessageFields); err != nil { + return nil, err + } + + v, err := version.marshal() + if err != nil { + return nil, err + } + b.Write(v) + + b.Write(targetName) + b.Write(targetInfo) + + return b.Bytes(), nil +} + +func (m *challengeMessage) Unmarshal(b []byte) error { + reader := bytes.NewReader(b) + + err := binary.Read(reader, binary.LittleEndian, &m.challengeMessageFields) + if err != nil { + return err + } + + if !m.challengeMessageFields.IsValid() { + return errInvalidChallengeMessage + } + + if ntlmsspRequestTarget.IsSet(m.NegotiateFlags) && m.TargetNameFields.Len > 0 { + m.TargetName, err = m.TargetNameFields.ReadString(reader, ntlmsspNegotiateUnicode.IsSet(m.NegotiateFlags)) + if err != nil { + return err + } + } + + m.TargetInfo = newTargetInfo() + + if ntlmsspNegotiateTargetInfo.IsSet(m.NegotiateFlags) && m.TargetInfoFields.Len > 0 { + v, err := m.TargetInfoFields.ReadValue(reader) + if err != nil { + return err + } + + if err := m.TargetInfo.Unmarshal(v); err != nil { + return err + } + } + + if ntlmsspNegotiateVersion.IsSet(m.NegotiateFlags) { + v := Version{} + if err := v.unmarshal(reader); err != nil { + return err + } + m.Version = &v + } + + return nil +} diff --git a/vendor/github.com/bodgit/ntlmssp/client.go b/vendor/github.com/bodgit/ntlmssp/client.go new file mode 100644 index 00000000..f8a41c93 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/client.go @@ -0,0 +1,267 @@ +package ntlmssp + +import ( + "fmt" + "strings" +) + +type lmCompatibilityLevel int + +const ( + DefaultClientCompatibilityLevel lmCompatibilityLevel = 3 +) + +type Client struct { + compatibilityLevel lmCompatibilityLevel + defaultFlags uint32 + domain string + password string + username string + workstation string + version *Version + + negotiatedFlags uint32 + negotiateMessage []byte + challengeMessage []byte + complete bool + securitySession *SecuritySession +} + +func realClientChallenge() ([]byte, error) { + return nonce(8) +} + +func realExportedSessionKey() ([]byte, error) { + return nonce(16) +} + +var ( + generateClientChallenge = realClientChallenge + generateExportedSessionKey = realExportedSessionKey +) + +func NewClient(options ...func(*Client) error) (*Client, error) { + c := &Client{} + + // Set the defaults + if err := c.SetOption(SetCompatibilityLevel(DefaultClientCompatibilityLevel)); err != nil { + return nil, err + } + + if err := c.SetOption(options...); err != nil { + return nil, err + } + + return c, nil +} + +func defaultClientFlags() uint32 { + flags := uint32(0) + + flags = ntlmsspNegotiateUnicode.Set(flags) + flags = ntlmsspNegotiateSign.Set(flags) + flags = ntlmsspNegotiateSeal.Set(flags) + flags = ntlmsspNegotiateAlwaysSign.Set(flags) + flags = ntlmsspNegotiateTargetInfo.Set(flags) + flags = ntlmsspNegotiate128.Set(flags) + flags = ntlmsspNegotiateKeyExch.Set(flags) + flags = ntlmsspNegotiate56.Set(flags) + + return flags +} + +func (c *Client) SetOption(options ...func(*Client) error) error { + for _, option := range options { + if err := option(c); err != nil { + return err + } + } + return nil +} + +func SetCompatibilityLevel(level lmCompatibilityLevel) func(*Client) error { + return func(c *Client) error { + flags := defaultClientFlags() + + // Ideally these should be constants if there's official naming for them + switch level { + case 0: // LM Auth and NTLMv1 Auth + flags = ntlmsspNegotiateLMKey.Set(flags) + flags = ntlmsspNegotiateNTLM.Set(flags) + case 1: // LM Auth and NTLMv1 Auth with Extended Session Security (NTLM2) + flags = ntlmsspNegotiateNTLM.Set(flags) + flags = ntlmsspNegotiateExtendedSessionsecurity.Set(flags) + case 2: // NTLMv1 Auth with Extended Session Security (NTLM2) + fallthrough + case 3, 4, 5: // NTLMv2 Auth + flags = ntlmsspNegotiateExtendedSessionsecurity.Set(flags) + default: + return fmt.Errorf("invalid compatibility level(0-5): %d", level) + } + + c.compatibilityLevel = level + c.defaultFlags = flags + + return nil + } +} + +func SetDomain(domain string) func(*Client) error { + return func(c *Client) error { + c.domain = strings.TrimSpace(domain) + return nil + } +} + +func SetUserInfo(username, password string) func(*Client) error { + return func(c *Client) error { + c.username = username + c.password = password + return nil + } +} + +func SetWorkstation(workstation string) func(*Client) error { + return func(c *Client) error { + c.workstation = strings.TrimSpace(workstation) + return nil + } +} + +func SetVersion(version *Version) func(*Client) error { + return func(c *Client) error { + c.version = version + return nil + } +} + +func (c *Client) Authenticate(input []byte, bindings *ChannelBindings) ([]byte, error) { + if input != nil { + return c.processChallengeMessage(input, bindings) + } + return c.newNegotiateMessage() +} + +func (c *Client) newNegotiateMessage() ([]byte, error) { + m := &negotiateMessage{ + negotiateMessageFields: negotiateMessageFields{ + messageHeader: newMessageHeader(ntLmNegotiate), + NegotiateFlags: c.defaultFlags, + }, + DomainName: c.domain, + Workstation: c.workstation, + Version: c.version, + } + + b, err := m.Marshal() + if err != nil { + return nil, err + } + + // Store the message bytes in case we need to generate a MIC + c.negotiateMessage = b + + return b, nil +} + +func (c *Client) processChallengeMessage(input []byte, bindings *ChannelBindings) ([]byte, error) { + cm := &challengeMessage{} + if err := cm.Unmarshal(input); err != nil { + return nil, err + } + + // Store the message bytes in case we need to generate a MIC + c.challengeMessage = input + + c.negotiatedFlags = cm.NegotiateFlags + if ntlmsspNegotiateUnicode.IsSet(c.negotiatedFlags) { + c.negotiatedFlags = ntlmNegotiateOEM.Unset(c.negotiatedFlags) + } + + // Set anonymous flag + if c.username == "" && c.password == "" { + c.negotiatedFlags = ntlmsspAnonymous.Set(c.negotiatedFlags) + } + + clientChallenge, err := generateClientChallenge() + if err != nil { + return nil, err + } + + lmChallengeResponse, err := lmChallengeResponse(c.negotiatedFlags, c.compatibilityLevel, clientChallenge, c.username, c.password, c.domain, cm) + if err != nil { + return nil, err + } + + targetInfo, err := cm.TargetInfo.Clone() + if err != nil { + return nil, err + } + + ntChallengeResponse, keyExchangeKey, err := ntChallengeResponse(c.negotiatedFlags, c.compatibilityLevel, clientChallenge, c.username, c.password, c.domain, cm, lmChallengeResponse, *targetInfo, bindings) + if err != nil { + return nil, err + } + + var encryptedRandomSessionKey, exportedSessionKey []byte + + if ntlmsspNegotiateKeyExch.IsSet(c.negotiatedFlags) { + exportedSessionKey, err = generateExportedSessionKey() + if err != nil { + return nil, err + } + + encryptedRandomSessionKey, err = encryptRC4K(keyExchangeKey, exportedSessionKey) + if err != nil { + return nil, err + } + } else { + exportedSessionKey = keyExchangeKey + } + + if ntlmsspNegotiateSeal.IsSet(c.negotiatedFlags) || ntlmsspNegotiateSign.IsSet(c.negotiatedFlags) { + c.securitySession, err = newSecuritySession(c.negotiatedFlags, exportedSessionKey, sourceClient) + if err != nil { + return nil, err + } + } + + am := &authenticateMessage{ + authenticateMessageFields: authenticateMessageFields{ + messageHeader: newMessageHeader(ntLmAuthenticate), + NegotiateFlags: c.negotiatedFlags, + }, + LmChallengeResponse: lmChallengeResponse, + NtChallengeResponse: ntChallengeResponse, + DomainName: c.domain, + UserName: c.username, + Workstation: c.workstation, + EncryptedRandomSessionKey: encryptedRandomSessionKey, + Version: c.version, + // Needed for computing the MIC + ExportedSessionKey: exportedSessionKey, + TargetInfo: *targetInfo, + } + + if err := am.UpdateMIC(concat(c.negotiateMessage, c.challengeMessage)); err != nil { + return nil, err + } + + b, err := am.Marshal() + if err != nil { + return nil, err + } + + // Mark transaction as complete + c.complete = true + + return b, nil +} + +func (c *Client) Complete() bool { + return c.complete +} + +func (c *Client) SecuritySession() *SecuritySession { + return c.securitySession +} diff --git a/vendor/github.com/bodgit/ntlmssp/crypto.go b/vendor/github.com/bodgit/ntlmssp/crypto.go new file mode 100644 index 00000000..b4250c90 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/crypto.go @@ -0,0 +1,133 @@ +package ntlmssp + +import ( + "bytes" + "crypto/des" + "crypto/hmac" + "crypto/md5" + "crypto/rand" + "crypto/rc4" + "encoding/binary" + "errors" + "hash/crc32" + + "golang.org/x/crypto/md4" +) + +func nonce(length int) ([]byte, error) { + result := make([]byte, length) + if _, err := rand.Read(result); err != nil { + return nil, err + } + + return result, nil +} + +func createDESKey(b []byte) ([]byte, error) { + if len(b) < 7 { + return nil, errors.New("need at least 7 bytes") + } + + key := zeroBytes(8) + + key[0] = b[0] + key[1] = b[0]<<7 | b[1]>>1 + key[2] = b[1]<<6 | b[2]>>2 + key[3] = b[2]<<5 | b[3]>>3 + key[4] = b[3]<<4 | b[4]>>4 + key[5] = b[4]<<3 | b[5]>>5 + key[6] = b[5]<<2 | b[6]>>6 + key[7] = b[6] << 1 + + // Calculate odd parity + for i, x := range key { + key[i] = (x & 0xfe) | ((((x >> 1) ^ (x >> 2) ^ (x >> 3) ^ (x >> 4) ^ (x >> 5) ^ (x >> 6) ^ (x >> 7)) ^ 0x01) & 0x01) + } + + return key, nil +} + +func encryptDES(k, d []byte) ([]byte, error) { + key, err := createDESKey(k) + if err != nil { + return nil, err + } + + cipher, err := des.NewCipher(key) + if err != nil { + return nil, err + } + + result := make([]byte, len(d)) + cipher.Encrypt(result, d) + + return result, nil +} + +func encryptDESL(k, d []byte) ([]byte, error) { + b := bytes.Buffer{} + + padded := zeroPad(k, 21) + + for _, i := range []int{0, 7, 14} { + result, err := encryptDES(padded[i:], d) + if err != nil { + return nil, err + } + + if _, err := b.Write(result); err != nil { + return nil, err + } + } + + return b.Bytes(), nil +} + +func initRC4(k []byte) (*rc4.Cipher, error) { + cipher, err := rc4.NewCipher(k) + if err != nil { + return nil, err + } + return cipher, nil +} + +func encryptRC4(cipher *rc4.Cipher, d []byte) []byte { + result := make([]byte, len(d)) + cipher.XORKeyStream(result, d) + return result +} + +func encryptRC4K(k, d []byte) ([]byte, error) { + cipher, err := initRC4(k) + if err != nil { + return nil, err + } + return encryptRC4(cipher, d), nil +} + +func hashMD4(b []byte) []byte { + md4 := md4.New() + md4.Write(b) + + return md4.Sum(nil) +} + +func hashMD5(b []byte) []byte { + md5 := md5.New() + md5.Write(b) + + return md5.Sum(nil) +} + +func hmacMD5(k, m []byte) []byte { + mac := hmac.New(md5.New, k) + mac.Write(m) + + return mac.Sum(nil) +} + +func hashCRC32(b []byte) []byte { + checksum := make([]byte, 4) + binary.LittleEndian.PutUint32(checksum, crc32.ChecksumIEEE(b)) + return checksum +} diff --git a/vendor/github.com/bodgit/ntlmssp/domain_posix.go b/vendor/github.com/bodgit/ntlmssp/domain_posix.go new file mode 100644 index 00000000..bd4e26f8 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/domain_posix.go @@ -0,0 +1,9 @@ +// +build !windows + +package ntlmssp + +// DefaultDomain returns the Windows domain that the host is joined to. This +// will never be successful on non-Windows as there's no standard API. +func DefaultDomain() (string, error) { + return "", nil +} diff --git a/vendor/github.com/bodgit/ntlmssp/domain_windows.go b/vendor/github.com/bodgit/ntlmssp/domain_windows.go new file mode 100644 index 00000000..96c52894 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/domain_windows.go @@ -0,0 +1,25 @@ +package ntlmssp + +import ( + "syscall" + "unsafe" +) + +// DefaultDomain returns the Windows domain that the host is joined to. This +// will never be successful on non-Windows as there's no standard API. +func DefaultDomain() (string, error) { + var domain *uint16 + var status uint32 + err := syscall.NetGetJoinInformation(nil, &domain, &status) + if err != nil { + return "", err + } + defer syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain))) + + // Not joined to a domain + if status != syscall.NetSetupDomainName { + return "", nil + } + + return syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(domain))[:]), nil +} diff --git a/vendor/github.com/bodgit/ntlmssp/flags.go b/vendor/github.com/bodgit/ntlmssp/flags.go new file mode 100644 index 00000000..98cafa0d --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/flags.go @@ -0,0 +1,116 @@ +package ntlmssp + +import "strings" + +type negotiateFlag uint32 + +const ( + ntlmsspNegotiateUnicode negotiateFlag = 1 << iota + ntlmNegotiateOEM + ntlmsspRequestTarget + _ + ntlmsspNegotiateSign + ntlmsspNegotiateSeal + ntlmsspNegotiateDatagram + ntlmsspNegotiateLMKey + _ + ntlmsspNegotiateNTLM + _ + ntlmsspAnonymous + ntlmsspNegotiateOEMDomainSupplied + ntlmsspNegotiateOEMWorkstationSupplied + _ + ntlmsspNegotiateAlwaysSign + ntlmsspTargetTypeDomain + ntlmsspTargetTypeServer + _ + ntlmsspNegotiateExtendedSessionsecurity + ntlmsspNegotiateIdentity + _ + ntlmsspRequestNonNTSessionKey + ntlmsspNegotiateTargetInfo + _ + ntlmsspNegotiateVersion + _ + _ + _ + ntlmsspNegotiate128 + ntlmsspNegotiateKeyExch + ntlmsspNegotiate56 +) + +func (f negotiateFlag) Set(flags uint32) uint32 { + return flags | uint32(f) +} + +func (f negotiateFlag) IsSet(flags uint32) bool { + return (flags & uint32(f)) != 0 +} + +func (f negotiateFlag) Unset(flags uint32) uint32 { + return flags & ^uint32(f) +} + +func (f negotiateFlag) String() string { + strings := map[negotiateFlag]string{ + ntlmsspNegotiateUnicode: "NTLMSSP_NEGOTIATE_UNICODE", + ntlmNegotiateOEM: "NTLM_NEGOTIATE_OEM", + ntlmsspRequestTarget: "NTLMSSP_REQUEST_TARGET", + ntlmsspNegotiateSign: "NTLMSSP_NEGOTIATE_SIGN", + ntlmsspNegotiateSeal: "NTLMSSP_NEGOTIATE_SEAL", + ntlmsspNegotiateDatagram: "NTLMSSP_NEGOTIATE_DATAGRAM", + ntlmsspNegotiateLMKey: "NTLMSSP_NEGOTIATE_LM_KEY", + ntlmsspNegotiateNTLM: "NTLMSSP_NEGOTIATE_NTLM", + ntlmsspAnonymous: "NTLMSSP_ANONYMOUS", + ntlmsspNegotiateOEMDomainSupplied: "NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED", + ntlmsspNegotiateOEMWorkstationSupplied: "NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED", + ntlmsspNegotiateAlwaysSign: "NTLMSSP_NEGOTIATE_ALWAYS_SIGN", + ntlmsspTargetTypeDomain: "NTLMSSP_TARGET_TYPE_DOMAIN", + ntlmsspTargetTypeServer: "NTLMSSP_TARGET_TYPE_SERVER", + ntlmsspNegotiateExtendedSessionsecurity: "NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY", + ntlmsspNegotiateIdentity: "NTLMSSP_NEGOTIATE_IDENTITY", + ntlmsspRequestNonNTSessionKey: "NTLMSSP_REQUEST_NON_NT_SESSION_KEY", + ntlmsspNegotiateTargetInfo: "NTLMSSP_NEGOTIATE_TARGET_INFO", + ntlmsspNegotiateVersion: "NTLMSSP_NEGOTIATE_VERSION", + ntlmsspNegotiate128: "NTLMSSP_NEGOTIATE_128", + ntlmsspNegotiateKeyExch: "NTLMSSP_NEGOTIATE_KEY_EXCH", + ntlmsspNegotiate56: "NTLMSSP_NEGOTIATE_56", + } + + return strings[f] +} + +func flagsToString(flags uint32) string { + var set []string + + for _, f := range [...]negotiateFlag{ + ntlmsspNegotiateUnicode, + ntlmNegotiateOEM, + ntlmsspRequestTarget, + ntlmsspNegotiateSign, + ntlmsspNegotiateSeal, + ntlmsspNegotiateDatagram, + ntlmsspNegotiateLMKey, + ntlmsspNegotiateNTLM, + ntlmsspAnonymous, + ntlmsspNegotiateOEMDomainSupplied, + ntlmsspNegotiateOEMWorkstationSupplied, + ntlmsspNegotiateAlwaysSign, + ntlmsspTargetTypeDomain, + ntlmsspTargetTypeServer, + ntlmsspNegotiateExtendedSessionsecurity, + ntlmsspNegotiateIdentity, + ntlmsspRequestNonNTSessionKey, + ntlmsspNegotiateTargetInfo, + ntlmsspNegotiateVersion, + ntlmsspNegotiate128, + ntlmsspNegotiateKeyExch, + ntlmsspNegotiate56, + } { + if f.IsSet(flags) { + set = append(set, f.String()) + } + } + + return strings.Join(set, " | ") +} diff --git a/vendor/github.com/bodgit/ntlmssp/http/client.go b/vendor/github.com/bodgit/ntlmssp/http/client.go new file mode 100644 index 00000000..138dab65 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/http/client.go @@ -0,0 +1,297 @@ +package http + +import ( + "bytes" + "encoding/base64" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/http/cookiejar" + "net/textproto" + "net/url" + "strings" + + "github.com/bodgit/ntlmssp" + "github.com/go-logr/logr" + "github.com/hashicorp/go-cleanhttp" +) + +var ( + httpAuthenticateHeader = textproto.CanonicalMIMEHeaderKey("WWW-Authenticate") +) + +type Client struct { + http *http.Client + ntlm *ntlmssp.Client + encryption bool + sendCBT bool + logger logr.Logger +} + +type teeReadCloser struct { + io.Reader + io.Closer +} + +func NewClient(httpClient *http.Client, ntlmClient *ntlmssp.Client, options ...func(*Client) error) (*Client, error) { + if httpClient == nil { + httpClient = cleanhttp.DefaultClient() + } + if httpClient.Jar == nil { + httpClient.Jar, _ = cookiejar.New(nil) + } + if httpClient.Transport != nil && httpClient.Transport.(*http.Transport).DisableKeepAlives { + return nil, errors.New("NTLM cannot work without keepalives") + } + + // FIXME CheckRedirect + + if ntlmClient == nil { + domain, err := ntlmssp.DefaultDomain() + if err != nil { + return nil, err + } + + workstation, err := ntlmssp.DefaultWorkstation() + if err != nil { + return nil, err + } + + ntlmClient, _ = ntlmssp.NewClient(ntlmssp.SetDomain(domain), ntlmssp.SetWorkstation(workstation), ntlmssp.SetVersion(ntlmssp.DefaultVersion())) + } + + c := &Client{ + http: httpClient, + ntlm: ntlmClient, + logger: logr.Discard(), + } + + if err := c.SetOption(options...); err != nil { + return nil, err + } + + return c, nil +} + +func (c *Client) SetOption(options ...func(*Client) error) error { + for _, option := range options { + if err := option(c); err != nil { + return err + } + } + return nil +} + +func SendCBT(value bool) func(*Client) error { + return func(c *Client) error { + c.sendCBT = value + return nil + } +} + +func Encryption(value bool) func(*Client) error { + return func(c *Client) error { + c.encryption = value + return nil + } +} + +func Logger(logger logr.Logger) func(*Client) error { + return func(c *Client) error { + c.logger = logger + return nil + } +} + +func (c *Client) wrap(req *http.Request) error { + if session := c.ntlm.SecuritySession(); c.ntlm.Complete() && c.encryption && session != nil && req.Body != nil { + + contentType := req.Header.Get(contentTypeHeader) + if contentType == "" { + return errors.New("no Content-Type header") + } + + body, err := ioutil.ReadAll(req.Body) + if err != nil { + return err + } + + sealed, signature, err := session.Wrap(body) + if err != nil { + return err + } + + length := make([]byte, 4) + binary.LittleEndian.PutUint32(length, uint32(len(signature))) + + body, newContentType, err := Wrap(concat(length, signature, sealed), contentType) + if err != nil { + return err + } + + req.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + req.Header.Set(contentTypeHeader, newContentType) + } + + return nil +} + +func (c *Client) unwrap(resp *http.Response) error { + if session := c.ntlm.SecuritySession(); c.ntlm.Complete() && c.encryption && session != nil && resp.Body != nil { + + contentType := resp.Header.Get(contentTypeHeader) + if contentType == "" { + return errors.New("no Content-Type header") + } + + sealed, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + defer resp.Body.Close() + + data, newContentType, err := Unwrap(sealed, contentType) + if err != nil { + return err + } + + length := binary.LittleEndian.Uint32(data[:4]) + + signature := make([]byte, length) + copy(signature, data[4:4+length]) + + body, err := session.Unwrap(data[4+length:], signature) + if err != nil { + return err + } + + resp.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + resp.Header.Set(contentTypeHeader, newContentType) + resp.Header.Set("Content-Length", fmt.Sprint(len(body))) + } + + return nil +} + +func (c *Client) Do(req *http.Request) (resp *http.Response, err error) { + + // Potentially replace the request body with a signed and sealed copy + if err := c.wrap(req); err != nil { + return nil, err + } + + var body bytes.Buffer + + if req.Body != nil { + tr := io.TeeReader(req.Body, &body) + req.Body = teeReadCloser{tr, req.Body} + } + + c.logger.Info("request", req) + + resp, err = c.http.Do(req) + if err != nil { + return nil, err + } + + if c.ntlm.Complete() || resp.StatusCode != http.StatusUnauthorized { + // Potentially unseal and check signature + if err := c.unwrap(resp); err != nil { + return nil, err + } + + return resp, nil + } + + for i := 0; i < 2; i++ { + + ok, input, err := isAuthenticationMethod(resp.Header, "Negotiate") + if err != nil { + return nil, err + } + + if !ok { + return resp, nil + } + + var cbt *ntlmssp.ChannelBindings + + if c.sendCBT && resp.TLS != nil { + cbt = generateChannelBindings(resp.TLS.PeerCertificates[0]) // Presume it's the first one? + } + + b, err := c.ntlm.Authenticate(input, cbt) + if err != nil { + return nil, err + } + req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(b)) + + if req.Body != nil { + req.Body = ioutil.NopCloser(&body) + } + + c.logger.Info("request", req) + + resp, err = c.http.Do(req) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusUnauthorized { + return resp, nil + } + } + + return resp, nil +} + +func isAuthenticationMethod(headers http.Header, method string) (bool, []byte, error) { + if h, ok := headers[httpAuthenticateHeader]; ok { + for _, x := range h { + if x == method { + return true, nil, nil + } + if strings.HasPrefix(x, method+" ") { + parts := strings.SplitN(x, " ", 2) + if len(parts) < 2 { + return true, nil, errors.New("malformed " + method + " header value") + } + b, err := base64.StdEncoding.DecodeString(parts[1]) + return true, b, err + } + } + } + return false, nil, nil +} + +func (c *Client) Get(url string) (resp *http.Response, err error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + return c.Do(req) +} + +func (c *Client) Head(url string) (resp *http.Response, err error) { + req, err := http.NewRequest("HEAD", url, nil) + if err != nil { + return nil, err + } + return c.Do(req) +} + +func (c *Client) Post(url, contentType string, body io.Reader) (resp *http.Response, err error) { + req, err := http.NewRequest("POST", url, body) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", contentType) + return c.Do(req) +} + +func (c *Client) PostForm(url string, data url.Values) (resp *http.Response, err error) { + return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) +} diff --git a/vendor/github.com/bodgit/ntlmssp/http/crypto.go b/vendor/github.com/bodgit/ntlmssp/http/crypto.go new file mode 100644 index 00000000..989a11e9 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/http/crypto.go @@ -0,0 +1,67 @@ +package http + +import ( + "crypto" + "crypto/x509" + "hash" + + "github.com/bodgit/ntlmssp" +) + +func signatureAlgorithmHash(algo x509.SignatureAlgorithm) crypto.Hash { + for _, details := range []struct { + algo x509.SignatureAlgorithm + hash crypto.Hash + }{ + {x509.MD2WithRSA, crypto.Hash(0)}, + {x509.MD5WithRSA, crypto.MD5}, + {x509.SHA1WithRSA, crypto.SHA1}, + {x509.SHA256WithRSA, crypto.SHA256}, + {x509.SHA384WithRSA, crypto.SHA384}, + {x509.SHA512WithRSA, crypto.SHA512}, + {x509.DSAWithSHA1, crypto.SHA1}, + {x509.DSAWithSHA256, crypto.SHA256}, + {x509.ECDSAWithSHA1, crypto.SHA1}, + {x509.ECDSAWithSHA256, crypto.SHA256}, + {x509.ECDSAWithSHA384, crypto.SHA384}, + {x509.ECDSAWithSHA512, crypto.SHA512}, + {x509.SHA256WithRSAPSS, crypto.SHA256}, + {x509.SHA384WithRSAPSS, crypto.SHA384}, + {x509.SHA512WithRSAPSS, crypto.SHA512}, + } { + if details.algo == algo { + return details.hash + } + } + return crypto.Hash(0) +} + +func generateCertificateHash(cert *x509.Certificate) []byte { + algorithm := signatureAlgorithmHash(cert.SignatureAlgorithm) + + var hash hash.Hash + + switch algorithm { + case crypto.Hash(0): + return nil + case crypto.MD5, crypto.SHA1: + hash = crypto.SHA256.New() + default: + hash = algorithm.New() + } + + hash.Write(cert.Raw) + + return hash.Sum(nil) +} + +func generateChannelBindings(cert *x509.Certificate) *ntlmssp.ChannelBindings { + b := generateCertificateHash(cert) + if b == nil { + return nil + } + + return &ntlmssp.ChannelBindings{ + ApplicationData: concat([]byte(ntlmssp.TLSServerEndPoint+":"), b), + } +} diff --git a/vendor/github.com/bodgit/ntlmssp/http/mime.go b/vendor/github.com/bodgit/ntlmssp/http/mime.go new file mode 100644 index 00000000..e044baaf --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/http/mime.go @@ -0,0 +1,186 @@ +package http + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/textproto" + "strings" + + "github.com/tidwall/transform" +) + +const ( + mimeBoundary string = "Encrypted Boundary" + mimeProtocol string = "application/HTTP-SPNEGO-session-encrypted" + dashBoundary string = "--" + mimeBoundary + contentTypeHeader string = "Content-Type" + contentTypeValue string = "multipart/encrypted;protocol=\"" + mimeProtocol + "\";boundary=\"" + mimeBoundary + "\"" + octetStream string = "application/octet-stream" +) + +func isHeader(line []byte) bool { + b := make([]byte, len(line)) + copy(b, line) + b = append(b, '\r', '\n') + r := textproto.NewReader(bufio.NewReader(bytes.NewBuffer(b))) + _, err := r.ReadMIMEHeader() + if err != nil { + return false + } + return true +} + +func toWSMV(r io.Reader) io.Reader { + br := bufio.NewReader(r) + return transform.NewTransformer(func() ([]byte, error) { + for { + line, err := br.ReadBytes('\n') + if err != nil { + return nil, err + } + + switch { + case bytes.Equal(line, []byte{'\r', '\n'}): + break + case bytes.HasPrefix(line, []byte(dashBoundary)): + return line, nil + case isHeader(line): + return append([]byte{'\t'}, line...), nil + default: + next, err := br.Peek(len(dashBoundary)) + if err != nil { + return nil, err + } + if bytes.Equal(next, []byte(dashBoundary)) { + return line[:len(line)-2], nil + } + return line, nil + } + } + }) +} + +func fromWSMV(r io.Reader) io.Reader { + br := bufio.NewReader(r) + header := false + return transform.NewTransformer(func() ([]byte, error) { + for { + line, err := br.ReadBytes('\n') + if err != nil { + return nil, err + } + + switch { + case bytes.HasPrefix(line, []byte{'\t'}) && isHeader(line[1:]): + header = true + return line[1:], nil + default: + if header { + line = append([]byte{'\r', '\n'}, line...) + header = false + } + if bytes.Contains(line[1:], []byte(dashBoundary)) { + for i := 0; i < len(line); i++ { + if bytes.HasPrefix(line[i:], []byte(dashBoundary)) { + line = append(line[:i], append([]byte{'\r', '\n'}, line[i:]...)...) + break + } + } + } + return line, nil + } + } + }) +} + +func Wrap(input []byte, ct string) ([]byte, string, error) { + b := &bytes.Buffer{} + + writer := multipart.NewWriter(b) + if err := writer.SetBoundary(mimeBoundary); err != nil { + return nil, "", err + } + + header := make(textproto.MIMEHeader) + header.Set(contentTypeHeader, mimeProtocol) + // Using Set() will canonicalise this to "Originalcontent" which may cause problems + header["OriginalContent"] = []string{fmt.Sprintf("type=%s;Length=%d", ct, len(input))} + + if _, err := writer.CreatePart(header); err != nil { + return nil, "", err + } + + body := make(textproto.MIMEHeader) + body.Set(contentTypeHeader, octetStream) + + part, err := writer.CreatePart(body) + if err != nil { + return nil, "", err + } + + if _, err := io.Copy(part, bytes.NewBuffer(input)); err != nil { + return nil, "", err + } + + writer.Close() + + output, err := ioutil.ReadAll(toWSMV(b)) + if err != nil { + return nil, "", err + } + + return output, contentTypeValue, nil +} + +func Unwrap(input []byte, ct string) ([]byte, string, error) { + if ct != contentTypeValue { + return nil, "", errors.New("incorrect Content-Type value") + } + + reader := multipart.NewReader(fromWSMV(bytes.NewBuffer(input)), mimeBoundary) + output := bytes.Buffer{} + + var originalContent string + +Loop: + for i := 0; true; i++ { + part, err := reader.NextPart() + switch err { + case nil: + break + case io.EOF: + break Loop + default: + return nil, "", err + } + + switch i { + case 0: + if part.Header.Get(contentTypeHeader) != mimeProtocol { + return nil, "", errors.New("incorrect Content-Type value") + } + if originalContent = part.Header.Get("OriginalContent"); originalContent == "" { + return nil, "", errors.New("missing OriginalContent header") + } + case 1: + if part.Header.Get(contentTypeHeader) != octetStream { + return nil, "", errors.New("incorrect Content-Type value") + } + if _, err := output.ReadFrom(part); err != nil { + return nil, "", err + } + default: + return nil, "", errors.New("additional MIME parts encountered") + } + } + + // TODO Better way of parsing this + parts := strings.Split(originalContent, ";") + + return output.Bytes(), parts[0][5:] + ";" + parts[1], nil +} diff --git a/vendor/github.com/bodgit/ntlmssp/http/util.go b/vendor/github.com/bodgit/ntlmssp/http/util.go new file mode 100644 index 00000000..cd2fae08 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/http/util.go @@ -0,0 +1,7 @@ +package http + +import "bytes" + +func concat(bs ...[]byte) []byte { + return bytes.Join(bs, nil) +} diff --git a/vendor/github.com/bodgit/ntlmssp/message_header.go b/vendor/github.com/bodgit/ntlmssp/message_header.go new file mode 100644 index 00000000..fb8d3c15 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/message_header.go @@ -0,0 +1,25 @@ +package ntlmssp + +import "bytes" + +const ( + ntLmNegotiate uint32 = iota + 1 + ntLmChallenge + ntLmAuthenticate +) + +type messageHeader struct { + Signature [8]byte + MessageType uint32 +} + +var signature = [8]byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0} + +func (h messageHeader) IsValid() bool { + return bytes.Equal(h.Signature[:], signature[:]) && + h.MessageType >= ntLmNegotiate && h.MessageType <= ntLmAuthenticate +} + +func newMessageHeader(messageType uint32) messageHeader { + return messageHeader{signature, messageType} +} diff --git a/vendor/github.com/bodgit/ntlmssp/negotiate_message.go b/vendor/github.com/bodgit/ntlmssp/negotiate_message.go new file mode 100644 index 00000000..a7c758be --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/negotiate_message.go @@ -0,0 +1,110 @@ +package ntlmssp + +import ( + "bytes" + "encoding/binary" + "errors" + "unsafe" +) + +var ( + errInvalidNegotiateMessage = errors.New("invalid NTLM negotiate message") +) + +type negotiateMessageFields struct { + messageHeader + NegotiateFlags uint32 + DomainNameFields payload + WorkstationFields payload +} + +func (m *negotiateMessageFields) IsValid() bool { + return m.messageHeader.IsValid() && m.MessageType == ntLmNegotiate +} + +type negotiateMessage struct { + negotiateMessageFields + DomainName string + Workstation string + Version *Version +} + +func (m *negotiateMessage) Marshal() ([]byte, error) { + offset := int(unsafe.Sizeof(m.negotiateMessageFields) + unsafe.Sizeof(*m.Version)) + + m.NegotiateFlags = ntlmsspNegotiateOEMDomainSupplied.Unset(m.NegotiateFlags) + if m.DomainName != "" { + m.NegotiateFlags = ntlmsspNegotiateOEMDomainSupplied.Set(m.NegotiateFlags) + } + + m.NegotiateFlags = ntlmsspNegotiateOEMWorkstationSupplied.Unset(m.NegotiateFlags) + if m.Workstation != "" { + m.NegotiateFlags = ntlmsspNegotiateOEMWorkstationSupplied.Set(m.NegotiateFlags) + } + + // Always OEM + m.DomainNameFields = newPayload(len(m.DomainName), &offset) + m.WorkstationFields = newPayload(len(m.Workstation), &offset) + + var version = &Version{} + + m.NegotiateFlags = ntlmsspNegotiateVersion.Unset(m.NegotiateFlags) + if m.Version != nil { + m.NegotiateFlags = ntlmsspNegotiateVersion.Set(m.NegotiateFlags) + version = m.Version + } + + b := bytes.Buffer{} + if err := binary.Write(&b, binary.LittleEndian, &m.negotiateMessageFields); err != nil { + return nil, err + } + + v, err := version.marshal() + if err != nil { + return nil, err + } + b.Write(v) + + // Always OEM + // XXX Should they be forced uppercase? + b.WriteString(m.DomainName + m.Workstation) + + return b.Bytes(), nil +} + +func (m *negotiateMessage) Unmarshal(b []byte) error { + reader := bytes.NewReader(b) + + err := binary.Read(reader, binary.LittleEndian, &m.negotiateMessageFields) + if err != nil { + return err + } + + if !m.negotiateMessageFields.IsValid() { + return errInvalidNegotiateMessage + } + + if ntlmsspNegotiateOEMDomainSupplied.IsSet(m.NegotiateFlags) && m.DomainNameFields.Len > 0 { + m.DomainName, err = m.DomainNameFields.ReadString(reader, false) // Always OEM + if err != nil { + return err + } + } + + if ntlmsspNegotiateOEMWorkstationSupplied.IsSet(m.NegotiateFlags) && m.WorkstationFields.Len > 0 { + m.Workstation, err = m.WorkstationFields.ReadString(reader, false) // Always OEM + if err != nil { + return err + } + } + + if ntlmsspNegotiateVersion.IsSet(m.NegotiateFlags) { + v := Version{} + if err := v.unmarshal(reader); err != nil { + return err + } + m.Version = &v + } + + return nil +} diff --git a/vendor/github.com/bodgit/ntlmssp/ntlm.go b/vendor/github.com/bodgit/ntlmssp/ntlm.go new file mode 100644 index 00000000..a5268cad --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/ntlm.go @@ -0,0 +1,251 @@ +package ntlmssp + +import ( + "bytes" + "encoding/binary" + "strings" + "time" + + "github.com/bodgit/windows" + "golang.org/x/crypto/md4" +) + +const ( + lmCiphertext string = "KGS!@#$%" +) + +func realCurrentTime() ([]byte, error) { + ft := windows.NsecToFiletime(time.Now().UnixNano()) + + b := bytes.Buffer{} + if err := binary.Write(&b, binary.LittleEndian, ft); err != nil { + return nil, err + } + + return b.Bytes(), nil +} + +var currentTime = realCurrentTime + +func lmowfV1(password string) ([]byte, error) { + b := bytes.Buffer{} + + padded := zeroPad([]byte(strings.ToUpper(password)), 14) + + for _, i := range []int{0, 7} { + result, err := encryptDES(padded[i:], []byte(lmCiphertext)) + if err != nil { + return nil, err + } + + b.Write(result) + } + + return b.Bytes(), nil +} + +func ntowfV1(password string) ([]byte, error) { + b, err := utf16FromString(password) + if err != nil { + return nil, err + } + return hashMD4(b), nil +} + +func ntowfV2(username, password, domain string) ([]byte, error) { + m, err := utf16FromString(strings.ToUpper(username) + domain) + if err != nil { + return nil, err + } + + k, err := ntowfV1(password) + if err != nil { + return nil, err + } + + return hmacMD5(k, m), nil +} + +func lmV1WithSessionSecurityResponse(clientChallenge []byte) []byte { + return zeroPad(clientChallenge, 24) +} + +func lmV1Response(password string, serverChallenge []byte) ([]byte, error) { + lmHash, err := lmowfV1(password) + if err != nil { + return nil, err + } + return encryptDESL(lmHash, serverChallenge) +} + +func lmV2Response(username, password, domain string, serverChallenge, clientChallenge []byte) ([]byte, error) { + ntlmHash, err := ntowfV2(username, password, domain) + if err != nil { + return nil, err + } + return concat(hmacMD5(ntlmHash, concat(serverChallenge, clientChallenge)), clientChallenge), nil +} + +func ntlmV1Response(password string, serverChallenge []byte) ([]byte, []byte, error) { + ntlmHash, err := ntowfV1(password) + if err != nil { + return nil, nil, err + } + + response, err := encryptDESL(ntlmHash, serverChallenge) + if err != nil { + return nil, nil, err + } + + return response, hashMD4(ntlmHash), nil +} + +func ntlm2Response(password string, serverChallenge, clientChallenge []byte) ([]byte, []byte, error) { + ntlmHash, err := ntowfV1(password) + if err != nil { + return nil, nil, err + } + + response, err := encryptDESL(ntlmHash, hashMD5(concat(serverChallenge, clientChallenge))[:8]) + if err != nil { + return nil, nil, err + } + + return response, hashMD4(ntlmHash), nil +} + +func ntlmV2Temp(timestamp []byte, clientChallenge []byte, targetInfo targetInfo) ([]byte, error) { + b, err := targetInfo.Marshal() + if err != nil { + return nil, err + } + + return concat([]byte{0x01}, []byte{0x01}, zeroBytes(6), timestamp, clientChallenge, zeroBytes(4), b, zeroBytes(4)), nil +} + +func ntlmV2Response(username, password, domain string, serverChallenge, clientChallenge []byte, timestamp []byte, targetInfo targetInfo) ([]byte, []byte, error) { + ntlmHash, err := ntowfV2(username, password, domain) + if err != nil { + return nil, nil, err + } + + temp, err := ntlmV2Temp(timestamp, clientChallenge, targetInfo) + if err != nil { + return nil, nil, err + } + + ntProofStr := hmacMD5(ntlmHash, concat(serverChallenge, temp)) + + return concat(ntProofStr, temp), hmacMD5(ntlmHash, ntProofStr), nil +} + +func ntlmV1ExchangeKey(flags uint32, sessionBaseKey []byte, serverChallenge []byte, lmChallengeResponse []byte, lmHash []byte) ([]byte, error) { + switch { + case ntlmsspNegotiateExtendedSessionsecurity.IsSet(flags): + return hmacMD5(sessionBaseKey, concat(serverChallenge, lmChallengeResponse[:8])), nil + case ntlmsspNegotiateLMKey.IsSet(flags): + b := bytes.Buffer{} + + for _, k := range [][]byte{lmHash[:7], concat(lmHash[7:8], bytes.Repeat([]byte{0xbd}, 6))} { + result, err := encryptDES(k, lmChallengeResponse[:8]) + if err != nil { + return nil, err + } + + b.Write(result) + } + + return b.Bytes(), nil + case ntlmsspRequestNonNTSessionKey.IsSet(flags): + return zeroPad(lmHash[:8], 16), nil + default: + return sessionBaseKey, nil + } +} + +func lmChallengeResponse(flags uint32, level lmCompatibilityLevel, clientChallenge []byte, username, password, domain string, cm *challengeMessage) ([]byte, error) { + switch { + case ntlmsspAnonymous.IsSet(flags): + return zeroBytes(1), nil + case ntlmsspNegotiateExtendedSessionsecurity.IsSet(flags) && level < 3: + // LMv1 with session security + return lmV1WithSessionSecurityResponse(clientChallenge), nil + case level < 2: + // LMv1 response + return lmV1Response(password, cm.ServerChallenge[:]) + case level == 2: + // NTLMv1 response + response, _, err := ntlmV1Response(password, cm.ServerChallenge[:]) + return response, err + default: + // LMv2 response + if _, ok := cm.TargetInfo.Get(msvAvTimestamp); ok { + return zeroBytes(24), nil + } + return lmV2Response(username, password, domain, cm.ServerChallenge[:], clientChallenge) + } +} + +func ntChallengeResponse(flags uint32, level lmCompatibilityLevel, clientChallenge []byte, username, password, domain string, cm *challengeMessage, lmChallengeResponse []byte, targetInfo targetInfo, channelBindings *ChannelBindings) ([]byte, []byte, error) { + switch { + case ntlmsspAnonymous.IsSet(flags): + return []byte{}, zeroBytes(md4.Size), nil + case level < 3: + var response, sessionBaseKey []byte + var err error + + if ntlmsspNegotiateExtendedSessionsecurity.IsSet(flags) { + // NTLMv1 authentication with NTLM2 + response, sessionBaseKey, err = ntlm2Response(password, cm.ServerChallenge[:], clientChallenge) + } else { + // NTLMv1 authentication + response, sessionBaseKey, err = ntlmV1Response(password, cm.ServerChallenge[:]) + } + if err != nil { + return nil, nil, err + } + + lmHash, err := lmowfV1(password) + if err != nil { + return nil, nil, err + } + + keyExchangeKey, err := ntlmV1ExchangeKey(flags, sessionBaseKey, cm.ServerChallenge[:], lmChallengeResponse, lmHash) + if err != nil { + return nil, nil, err + } + + return response, keyExchangeKey, nil + default: + // NTLMv2 authentication + timestamp, ok := targetInfo.Get(msvAvTimestamp) + if ok { + var flags uint32 + if v, ok := targetInfo.Get(msvAvFlags); ok { + flags = binary.LittleEndian.Uint32(v) + flags |= msvAvFlagMICProvided + } else { + flags = msvAvFlagMICProvided + } + v := make([]byte, 4) + binary.LittleEndian.PutUint32(v, flags) + targetInfo.Set(msvAvFlags, v) + } else { + var err error + timestamp, err = currentTime() + if err != nil { + return nil, nil, err + } + } + + if channelBindings != nil { + b, err := channelBindings.marshal() + if err != nil { + return nil, nil, err + } + targetInfo.Set(msvChannelBindings, hashMD5(b)) + } + + return ntlmV2Response(username, password, domain, cm.ServerChallenge[:], clientChallenge, timestamp, targetInfo) + } +} diff --git a/vendor/github.com/bodgit/ntlmssp/payload.go b/vendor/github.com/bodgit/ntlmssp/payload.go new file mode 100644 index 00000000..59023fe4 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/payload.go @@ -0,0 +1,74 @@ +package ntlmssp + +import ( + "bytes" + "fmt" + "io" +) + +type payload struct { + Len uint16 + MaxLen uint16 + Offset uint32 +} + +func newPayload(length int, offset *int) payload { + p := payload{ + Len: uint16(length), + MaxLen: uint16(length), + Offset: uint32(*offset), + } + + *offset += length + + return p +} + +func (p *payload) ReadValue(reader *bytes.Reader) ([]byte, error) { + // Remember where we are + pos, err := reader.Seek(0, io.SeekCurrent) + if err != nil { + return nil, err + } + + // Seek to where the payload is + _, err = reader.Seek(int64(p.Offset), io.SeekStart) + if err != nil { + return nil, err + } + + // Read the value + b := make([]byte, p.Len) + n, err := reader.Read(b) + if err != nil { + return nil, err + } + if n != int(p.Len) { + return nil, fmt.Errorf("expected %d bytes, only read %d", p.Len, n) + } + + // Seek back to where we were + _, err = reader.Seek(pos, io.SeekStart) + if err != nil { + return nil, err + } + + return b, nil +} + +func (p *payload) ReadString(reader *bytes.Reader, utf16 bool) (string, error) { + b, err := p.ReadValue(reader) + if err != nil { + return "", err + } + + if utf16 { + s, err := utf16ToString(b) + if err != nil { + return "", err + } + return s, nil + } + + return string(b), nil +} diff --git a/vendor/github.com/bodgit/ntlmssp/security.go b/vendor/github.com/bodgit/ntlmssp/security.go new file mode 100644 index 00000000..9a94aa81 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/security.go @@ -0,0 +1,204 @@ +package ntlmssp + +import ( + "bytes" + "crypto/rc4" + "encoding/binary" + "errors" +) + +type SecuritySession struct { + negotiateFlags, outgoingSeqNum, incomingSeqNum uint32 + outgoingSigningKey, incomingSigningKey []byte + outgoingHandle, incomingHandle *rc4.Cipher +} + +var ( + clientSigning = concat([]byte("session key to client-to-server signing key magic constant"), []byte{0x00}) + serverSigning = concat([]byte("session key to server-to-client signing key magic constant"), []byte{0x00}) + clientSealing = concat([]byte("session key to client-to-server sealing key magic constant"), []byte{0x00}) + serverSealing = concat([]byte("session key to server-to-client sealing key magic constant"), []byte{0x00}) +) + +type securitySource int + +const ( + sourceClient securitySource = iota + sourceServer +) + +func newSecuritySession(flags uint32, exportedSessionKey []byte, source securitySource) (*SecuritySession, error) { + s := &SecuritySession{ + negotiateFlags: flags, + } + + clientSealingKey := sealKey(s.negotiateFlags, exportedSessionKey, clientSealing) + serverSealingKey := sealKey(s.negotiateFlags, exportedSessionKey, serverSealing) + + var err error + + switch source { + case sourceClient: + s.outgoingSigningKey = signKey(exportedSessionKey, clientSigning) + s.incomingSigningKey = signKey(exportedSessionKey, serverSigning) + + s.outgoingHandle, err = initRC4(clientSealingKey) + if err != nil { + return nil, err + } + + s.incomingHandle, err = initRC4(serverSealingKey) + if err != nil { + return nil, err + } + case sourceServer: + s.outgoingSigningKey = signKey(exportedSessionKey, serverSigning) + s.incomingSigningKey = signKey(exportedSessionKey, clientSigning) + + s.outgoingHandle, err = initRC4(serverSealingKey) + if err != nil { + return nil, err + } + + s.incomingHandle, err = initRC4(clientSealingKey) + if err != nil { + return nil, err + } + default: + return nil, errors.New("unknown source") + } + + return s, nil +} + +func (s *SecuritySession) Wrap(b []byte) ([]byte, []byte, error) { + m := append(b[:0:0], b...) + switch { + case ntlmsspNegotiateSeal.IsSet(s.negotiateFlags): + m = encryptRC4(s.outgoingHandle, m) + fallthrough + case ntlmsspNegotiateSign.IsSet(s.negotiateFlags): + // Signature is always created on the original unencrypted message + signature, err := calculateSignature(b, s.negotiateFlags, s.outgoingSigningKey, s.outgoingSeqNum, s.outgoingHandle) + if err != nil { + return nil, nil, err + } + + s.outgoingSeqNum++ + + return m, signature, nil + default: + return m, nil, nil + } +} + +func (s *SecuritySession) Unwrap(b, signature []byte) ([]byte, error) { + m := append(b[:0:0], b...) + switch { + case ntlmsspNegotiateSeal.IsSet(s.negotiateFlags): + m = encryptRC4(s.incomingHandle, m) + fallthrough + case ntlmsspNegotiateSign.IsSet(s.negotiateFlags): + // Signature is checked after any decryption + expected, err := calculateSignature(m, s.negotiateFlags, s.incomingSigningKey, s.incomingSeqNum, s.incomingHandle) + if err != nil { + return nil, err + } + + offset := 8 + if ntlmsspNegotiateExtendedSessionsecurity.IsSet(s.negotiateFlags) { + offset = 4 + } + + // Compare the checksum portion of the signature + if !bytes.Equal(signature[offset:12], expected[offset:12]) { + return nil, errors.New("checksum does not match") + } + + // Compare the sequence number portion of the signature + if !bytes.Equal(signature[12:16], expected[12:16]) { + return nil, errors.New("sequence number does not match") + } + + s.incomingSeqNum++ + + fallthrough + default: + return m, nil + } +} + +func signKey(exportedSessionKey, constant []byte) []byte { + return hashMD5(concat(exportedSessionKey, constant)) +} + +func sealKey(flags uint32, exportedSessionKey, constant []byte) []byte { + switch { + case ntlmsspNegotiateExtendedSessionsecurity.IsSet(flags) && ntlmsspNegotiate128.IsSet(flags): + // NTLM2 with 128-bit key + return hashMD5(concat(exportedSessionKey, constant)) + case ntlmsspNegotiateExtendedSessionsecurity.IsSet(flags) && ntlmsspNegotiate56.IsSet(flags): + // NTLM2 with 56-bit key + return hashMD5(concat(exportedSessionKey[:7], constant)) + case ntlmsspNegotiateExtendedSessionsecurity.IsSet(flags): + // NTLM2 with 40-bit key + return hashMD5(concat(exportedSessionKey[:5], constant)) + case ntlmsspNegotiateLMKey.IsSet(flags) && ntlmsspNegotiate56.IsSet(flags): + // NTLM1 with 56-bit key + return concat(exportedSessionKey[:7], []byte{0xa0}) + case ntlmsspNegotiateLMKey.IsSet(flags): + // NTLM1 with 40-bit key + return concat(exportedSessionKey[:5], []byte{0xe5, 0x38, 0xb0}) + default: + return exportedSessionKey + } +} + +func calculateSignature(message []byte, flags uint32, signingKey []byte, sequenceNumber uint32, handle *rc4.Cipher) ([]byte, error) { + var version uint32 = 1 + + b := bytes.Buffer{} + if err := binary.Write(&b, binary.LittleEndian, &version); err != nil { + return nil, err + } + + seqNum := make([]byte, 4) + binary.LittleEndian.PutUint32(seqNum, sequenceNumber) + + switch { + case ntlmsspNegotiateExtendedSessionsecurity.IsSet(flags): + checksum := hmacMD5(signingKey, concat(seqNum, message))[:8] + + if ntlmsspNegotiateKeyExch.IsSet(flags) { + checksum = encryptRC4(handle, checksum) + } + + // Checksum + b.Write(checksum) + + // Sequence Number + b.Write(seqNum) + default: + // The MS-NLMP specification doesn't match the examples. The + // examples write out the encrypted random pad in the signature + // whereas the pseudo code writes out zeroes. + + // RandomPad of 0 + _ = encryptRC4(handle, zeroBytes(4)) + b.Write(zeroBytes(4)) + + // Checksum + b.Write(encryptRC4(handle, hashCRC32(message))) + + encryptedSeqNum := encryptRC4(handle, zeroBytes(4)) + + for i := 0; i < 4; i++ { + encryptedSeqNum[i] ^= seqNum[i] + } + + // Sequence Number + b.Write(encryptedSeqNum) + } + + return b.Bytes(), nil +} diff --git a/vendor/github.com/bodgit/ntlmssp/unicode.go b/vendor/github.com/bodgit/ntlmssp/unicode.go new file mode 100644 index 00000000..771563e0 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/unicode.go @@ -0,0 +1,21 @@ +package ntlmssp + +import "golang.org/x/text/encoding/unicode" + +func utf16FromString(s string) ([]byte, error) { + b, err := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder().Bytes([]byte(s)) + if err != nil { + return nil, err + } + + return b, nil +} + +func utf16ToString(b []byte) (string, error) { + s, err := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder().Bytes(b) + if err != nil { + return "", err + } + + return string(s), nil +} diff --git a/vendor/github.com/bodgit/ntlmssp/util.go b/vendor/github.com/bodgit/ntlmssp/util.go new file mode 100644 index 00000000..5b43e48d --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/util.go @@ -0,0 +1,18 @@ +package ntlmssp + +import "bytes" + +func concat(bs ...[]byte) []byte { + return bytes.Join(bs, nil) +} + +func zeroBytes(length int) []byte { + return make([]byte, length, length) +} + +func zeroPad(s []byte, length int) []byte { + d := zeroBytes(length) + copy(d, s) + + return d +} diff --git a/vendor/github.com/bodgit/ntlmssp/version.go b/vendor/github.com/bodgit/ntlmssp/version.go new file mode 100644 index 00000000..32cddc7b --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/version.go @@ -0,0 +1,40 @@ +package ntlmssp + +import ( + "bytes" + "encoding/binary" + "unsafe" +) + +const ( + // NTLMSSPRevisionW2K3 is the current revision of NTLM. + NTLMSSPRevisionW2K3 uint8 = 0x0f +) + +// Version models the NTLM version passed in the various messages. +type Version struct { + ProductMajorVersion uint8 + ProductMinorVersion uint8 + ProductBuild uint16 + _ [3]uint8 + NTLMRevisionCurrent uint8 +} + +func (v *Version) marshal() ([]byte, error) { + dest := make([]byte, 0, unsafe.Sizeof(Version{})) + buffer := bytes.NewBuffer(dest) + + if err := binary.Write(buffer, binary.LittleEndian, v); err != nil { + return nil, err + } + + return buffer.Bytes(), nil +} + +func (v *Version) unmarshal(reader *bytes.Reader) error { + if err := binary.Read(reader, binary.LittleEndian, v); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/bodgit/ntlmssp/version_posix.go b/vendor/github.com/bodgit/ntlmssp/version_posix.go new file mode 100644 index 00000000..98b01525 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/version_posix.go @@ -0,0 +1,9 @@ +// +build !windows + +package ntlmssp + +// DefaultVersion returns a pointer to a NTLM Version struct for the OS which +// will be populated on Windows or nil otherwise. +func DefaultVersion() *Version { + return nil +} diff --git a/vendor/github.com/bodgit/ntlmssp/version_windows.go b/vendor/github.com/bodgit/ntlmssp/version_windows.go new file mode 100644 index 00000000..5f9d22b7 --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/version_windows.go @@ -0,0 +1,20 @@ +package ntlmssp + +import ( + "syscall" +) + +// DefaultVersion returns a pointer to a NTLM Version struct for the OS which +// will be populated on Windows or nil otherwise. +func DefaultVersion() *Version { + dll := syscall.MustLoadDLL("kernel32.dll") + p := dll.MustFindProc("GetVersion") + v, _, _ := p.Call() + + return &Version{ + ProductMajorVersion: uint8(v), + ProductMinorVersion: uint8(v >> 8), + ProductBuild: uint16(v >> 16), + NTLMRevisionCurrent: NTLMSSPRevisionW2K3, + } +} diff --git a/vendor/github.com/bodgit/ntlmssp/workstation.go b/vendor/github.com/bodgit/ntlmssp/workstation.go new file mode 100644 index 00000000..cb89a02d --- /dev/null +++ b/vendor/github.com/bodgit/ntlmssp/workstation.go @@ -0,0 +1,16 @@ +package ntlmssp + +import ( + "os" + "strings" +) + +// DefaultWorkstation returns the current workstation name. +func DefaultWorkstation() (string, error) { + hostname, err := os.Hostname() + if err != nil { + return "", err + } + + return strings.ToUpper(hostname), nil +} diff --git a/vendor/github.com/bodgit/windows/.golangci.yaml b/vendor/github.com/bodgit/windows/.golangci.yaml new file mode 100644 index 00000000..f74dd154 --- /dev/null +++ b/vendor/github.com/bodgit/windows/.golangci.yaml @@ -0,0 +1,13 @@ +--- +linters: + enable-all: true + disable: + - exhaustivestruct + - exhaustruct + - godox + - goerr113 + - gomnd + - ireturn + - nonamedreturns + - varnamelen + - wrapcheck diff --git a/vendor/github.com/bodgit/windows/.goreleaser.yml b/vendor/github.com/bodgit/windows/.goreleaser.yml new file mode 100644 index 00000000..75e2a1f7 --- /dev/null +++ b/vendor/github.com/bodgit/windows/.goreleaser.yml @@ -0,0 +1,7 @@ +--- +builds: + - skip: true +release: + prerelease: auto +changelog: + use: github-native diff --git a/vendor/github.com/bodgit/windows/LICENSE b/vendor/github.com/bodgit/windows/LICENSE new file mode 100644 index 00000000..08172a91 --- /dev/null +++ b/vendor/github.com/bodgit/windows/LICENSE @@ -0,0 +1,30 @@ +BSD 3-Clause License + +Copyright (c) 2020, Matt Dainty +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 the copyright holder 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 HOLDER 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/bodgit/windows/README.md b/vendor/github.com/bodgit/windows/README.md new file mode 100644 index 00000000..1c3fb31a --- /dev/null +++ b/vendor/github.com/bodgit/windows/README.md @@ -0,0 +1,14 @@ +[![GitHub release](https://img.shields.io/github/v/release/bodgit/windows)](https://github.com/bodgit/windows/releases) +[![Build Status](https://img.shields.io/github/workflow/status/bodgit/windows/build)](https://github.com/bodgit/windows/actions?query=workflow%3Abuild) +[![Coverage Status](https://coveralls.io/repos/github/bodgit/windows/badge.svg?branch=main)](https://coveralls.io/github/bodgit/windows?branch=main) +[![Go Report Card](https://goreportcard.com/badge/github.com/bodgit/windows)](https://goreportcard.com/report/github.com/bodgit/windows) +[![GoDoc](https://godoc.org/github.com/bodgit/windows?status.svg)](https://godoc.org/github.com/bodgit/windows) +![Go version](https://img.shields.io/badge/Go-1.18-brightgreen.svg) +![Go version](https://img.shields.io/badge/Go-1.17-brightgreen.svg) + +windows +======= + +A collection of types native to Windows but are useful on non-Windows platforms. + +The `FILETIME`-equivalent type is the sole export which is a 1:1 copy of the type found in the `golang.org/x/sys/windows` package. That package only builds on `GOOS=windows` and this particular type gets used in other protocols and file types such as NTLMv2 and 7-zip. diff --git a/vendor/github.com/bodgit/windows/filetime.go b/vendor/github.com/bodgit/windows/filetime.go new file mode 100644 index 00000000..96e678d6 --- /dev/null +++ b/vendor/github.com/bodgit/windows/filetime.go @@ -0,0 +1,44 @@ +// Package windows is a collection of types native to Windows platforms but +// are useful on non-Windows platforms. +package windows + +// Taken from golang.org/x/sys/windows + +const offset int64 = 116444736000000000 + +// Filetime mirrors the Windows FILETIME structure which represents time +// as the number of 100-nanosecond intervals that have elapsed since +// 00:00:00 UTC, January 1, 1601. This code is taken from the +// golang.org/x/sys/windows package where it's not available for non-Windows +// platforms however various file formats and protocols pass this structure +// about so it's useful to have it available for interoperability purposes. +type Filetime struct { + LowDateTime uint32 + HighDateTime uint32 +} + +// Nanoseconds returns Filetime ft in nanoseconds +// since Epoch (00:00:00 UTC, January 1, 1970). +func (ft *Filetime) Nanoseconds() int64 { + // 100-nanosecond intervals since January 1, 1601 + nsec := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime) + // change starting time to the Epoch (00:00:00 UTC, January 1, 1970) + nsec -= offset + // convert into nanoseconds + nsec *= 100 + + return nsec +} + +// NsecToFiletime converts nanoseconds to the equivalent Filetime type. +func NsecToFiletime(nsec int64) (ft Filetime) { + // convert into 100-nanosecond + nsec /= 100 + // change starting time to January 1, 1601 + nsec += offset + // split into high / low + ft.LowDateTime = uint32(nsec & 0xffffffff) + ft.HighDateTime = uint32(nsec >> 32 & 0xffffffff) + + return ft +} diff --git a/vendor/github.com/go-logr/logr/.golangci.yaml b/vendor/github.com/go-logr/logr/.golangci.yaml new file mode 100644 index 00000000..0cffafa7 --- /dev/null +++ b/vendor/github.com/go-logr/logr/.golangci.yaml @@ -0,0 +1,26 @@ +run: + timeout: 1m + tests: true + +linters: + disable-all: true + enable: + - asciicheck + - errcheck + - forcetypeassert + - gocritic + - gofmt + - goimports + - gosimple + - govet + - ineffassign + - misspell + - revive + - staticcheck + - typecheck + - unused + +issues: + exclude-use-default: false + max-issues-per-linter: 0 + max-same-issues: 10 diff --git a/vendor/github.com/go-logr/logr/CHANGELOG.md b/vendor/github.com/go-logr/logr/CHANGELOG.md new file mode 100644 index 00000000..c3569600 --- /dev/null +++ b/vendor/github.com/go-logr/logr/CHANGELOG.md @@ -0,0 +1,6 @@ +# CHANGELOG + +## v1.0.0-rc1 + +This is the first logged release. Major changes (including breaking changes) +have occurred since earlier tags. diff --git a/vendor/github.com/go-logr/logr/CONTRIBUTING.md b/vendor/github.com/go-logr/logr/CONTRIBUTING.md new file mode 100644 index 00000000..5d37e294 --- /dev/null +++ b/vendor/github.com/go-logr/logr/CONTRIBUTING.md @@ -0,0 +1,17 @@ +# Contributing + +Logr is open to pull-requests, provided they fit within the intended scope of +the project. Specifically, this library aims to be VERY small and minimalist, +with no external dependencies. + +## Compatibility + +This project intends to follow [semantic versioning](http://semver.org) and +is very strict about compatibility. Any proposed changes MUST follow those +rules. + +## Performance + +As a logging library, logr must be as light-weight as possible. Any proposed +code change must include results of running the [benchmark](./benchmark) +before and after the change. diff --git a/vendor/github.com/go-logr/logr/LICENSE b/vendor/github.com/go-logr/logr/LICENSE new file mode 100644 index 00000000..8dada3ed --- /dev/null +++ b/vendor/github.com/go-logr/logr/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/go-logr/logr/README.md b/vendor/github.com/go-logr/logr/README.md new file mode 100644 index 00000000..a8c29bfb --- /dev/null +++ b/vendor/github.com/go-logr/logr/README.md @@ -0,0 +1,393 @@ +# A minimal logging API for Go + +[![Go Reference](https://pkg.go.dev/badge/github.com/go-logr/logr.svg)](https://pkg.go.dev/github.com/go-logr/logr) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/go-logr/logr/badge)](https://securityscorecards.dev/viewer/?platform=github.com&org=go-logr&repo=logr) + +logr offers an(other) opinion on how Go programs and libraries can do logging +without becoming coupled to a particular logging implementation. This is not +an implementation of logging - it is an API. In fact it is two APIs with two +different sets of users. + +The `Logger` type is intended for application and library authors. It provides +a relatively small API which can be used everywhere you want to emit logs. It +defers the actual act of writing logs (to files, to stdout, or whatever) to the +`LogSink` interface. + +The `LogSink` interface is intended for logging library implementers. It is a +pure interface which can be implemented by logging frameworks to provide the actual logging +functionality. + +This decoupling allows application and library developers to write code in +terms of `logr.Logger` (which has very low dependency fan-out) while the +implementation of logging is managed "up stack" (e.g. in or near `main()`.) +Application developers can then switch out implementations as necessary. + +Many people assert that libraries should not be logging, and as such efforts +like this are pointless. Those people are welcome to convince the authors of +the tens-of-thousands of libraries that *DO* write logs that they are all +wrong. In the meantime, logr takes a more practical approach. + +## Typical usage + +Somewhere, early in an application's life, it will make a decision about which +logging library (implementation) it actually wants to use. Something like: + +``` + func main() { + // ... other setup code ... + + // Create the "root" logger. We have chosen the "logimpl" implementation, + // which takes some initial parameters and returns a logr.Logger. + logger := logimpl.New(param1, param2) + + // ... other setup code ... +``` + +Most apps will call into other libraries, create structures to govern the flow, +etc. The `logr.Logger` object can be passed to these other libraries, stored +in structs, or even used as a package-global variable, if needed. For example: + +``` + app := createTheAppObject(logger) + app.Run() +``` + +Outside of this early setup, no other packages need to know about the choice of +implementation. They write logs in terms of the `logr.Logger` that they +received: + +``` + type appObject struct { + // ... other fields ... + logger logr.Logger + // ... other fields ... + } + + func (app *appObject) Run() { + app.logger.Info("starting up", "timestamp", time.Now()) + + // ... app code ... +``` + +## Background + +If the Go standard library had defined an interface for logging, this project +probably would not be needed. Alas, here we are. + +When the Go developers started developing such an interface with +[slog](https://github.com/golang/go/issues/56345), they adopted some of the +logr design but also left out some parts and changed others: + +| Feature | logr | slog | +|---------|------|------| +| High-level API | `Logger` (passed by value) | `Logger` (passed by [pointer](https://github.com/golang/go/issues/59126)) | +| Low-level API | `LogSink` | `Handler` | +| Stack unwinding | done by `LogSink` | done by `Logger` | +| Skipping helper functions | `WithCallDepth`, `WithCallStackHelper` | [not supported by Logger](https://github.com/golang/go/issues/59145) | +| Generating a value for logging on demand | `Marshaler` | `LogValuer` | +| Log levels | >= 0, higher meaning "less important" | positive and negative, with 0 for "info" and higher meaning "more important" | +| Error log entries | always logged, don't have a verbosity level | normal log entries with level >= `LevelError` | +| Passing logger via context | `NewContext`, `FromContext` | no API | +| Adding a name to a logger | `WithName` | no API | +| Modify verbosity of log entries in a call chain | `V` | no API | +| Grouping of key/value pairs | not supported | `WithGroup`, `GroupValue` | + +The high-level slog API is explicitly meant to be one of many different APIs +that can be layered on top of a shared `slog.Handler`. logr is one such +alternative API, with [interoperability](#slog-interoperability) provided by the [`slogr`](slogr) +package. + +### Inspiration + +Before you consider this package, please read [this blog post by the +inimitable Dave Cheney][warning-makes-no-sense]. We really appreciate what +he has to say, and it largely aligns with our own experiences. + +### Differences from Dave's ideas + +The main differences are: + +1. Dave basically proposes doing away with the notion of a logging API in favor +of `fmt.Printf()`. We disagree, especially when you consider things like output +locations, timestamps, file and line decorations, and structured logging. This +package restricts the logging API to just 2 types of logs: info and error. + +Info logs are things you want to tell the user which are not errors. Error +logs are, well, errors. If your code receives an `error` from a subordinate +function call and is logging that `error` *and not returning it*, use error +logs. + +2. Verbosity-levels on info logs. This gives developers a chance to indicate +arbitrary grades of importance for info logs, without assigning names with +semantic meaning such as "warning", "trace", and "debug." Superficially this +may feel very similar, but the primary difference is the lack of semantics. +Because verbosity is a numerical value, it's safe to assume that an app running +with higher verbosity means more (and less important) logs will be generated. + +## Implementations (non-exhaustive) + +There are implementations for the following logging libraries: + +- **a function** (can bridge to non-structured libraries): [funcr](https://github.com/go-logr/logr/tree/master/funcr) +- **a testing.T** (for use in Go tests, with JSON-like output): [testr](https://github.com/go-logr/logr/tree/master/testr) +- **github.com/google/glog**: [glogr](https://github.com/go-logr/glogr) +- **k8s.io/klog** (for Kubernetes): [klogr](https://git.k8s.io/klog/klogr) +- **a testing.T** (with klog-like text output): [ktesting](https://git.k8s.io/klog/ktesting) +- **go.uber.org/zap**: [zapr](https://github.com/go-logr/zapr) +- **log** (the Go standard library logger): [stdr](https://github.com/go-logr/stdr) +- **github.com/sirupsen/logrus**: [logrusr](https://github.com/bombsimon/logrusr) +- **github.com/wojas/genericr**: [genericr](https://github.com/wojas/genericr) (makes it easy to implement your own backend) +- **logfmt** (Heroku style [logging](https://www.brandur.org/logfmt)): [logfmtr](https://github.com/iand/logfmtr) +- **github.com/rs/zerolog**: [zerologr](https://github.com/go-logr/zerologr) +- **github.com/go-kit/log**: [gokitlogr](https://github.com/tonglil/gokitlogr) (also compatible with github.com/go-kit/kit/log since v0.12.0) +- **bytes.Buffer** (writing to a buffer): [bufrlogr](https://github.com/tonglil/buflogr) (useful for ensuring values were logged, like during testing) + +## slog interoperability + +Interoperability goes both ways, using the `logr.Logger` API with a `slog.Handler` +and using the `slog.Logger` API with a `logr.LogSink`. [slogr](./slogr) provides `NewLogr` and +`NewSlogHandler` API calls to convert between a `logr.Logger` and a `slog.Handler`. +As usual, `slog.New` can be used to wrap such a `slog.Handler` in the high-level +slog API. `slogr` itself leaves that to the caller. + +## Using a `logr.Sink` as backend for slog + +Ideally, a logr sink implementation should support both logr and slog by +implementing both the normal logr interface(s) and `slogr.SlogSink`. Because +of a conflict in the parameters of the common `Enabled` method, it is [not +possible to implement both slog.Handler and logr.Sink in the same +type](https://github.com/golang/go/issues/59110). + +If both are supported, log calls can go from the high-level APIs to the backend +without the need to convert parameters. `NewLogr` and `NewSlogHandler` can +convert back and forth without adding additional wrappers, with one exception: +when `Logger.V` was used to adjust the verbosity for a `slog.Handler`, then +`NewSlogHandler` has to use a wrapper which adjusts the verbosity for future +log calls. + +Such an implementation should also support values that implement specific +interfaces from both packages for logging (`logr.Marshaler`, `slog.LogValuer`, +`slog.GroupValue`). logr does not convert those. + +Not supporting slog has several drawbacks: +- Recording source code locations works correctly if the handler gets called + through `slog.Logger`, but may be wrong in other cases. That's because a + `logr.Sink` does its own stack unwinding instead of using the program counter + provided by the high-level API. +- slog levels <= 0 can be mapped to logr levels by negating the level without a + loss of information. But all slog levels > 0 (e.g. `slog.LevelWarning` as + used by `slog.Logger.Warn`) must be mapped to 0 before calling the sink + because logr does not support "more important than info" levels. +- The slog group concept is supported by prefixing each key in a key/value + pair with the group names, separated by a dot. For structured output like + JSON it would be better to group the key/value pairs inside an object. +- Special slog values and interfaces don't work as expected. +- The overhead is likely to be higher. + +These drawbacks are severe enough that applications using a mixture of slog and +logr should switch to a different backend. + +## Using a `slog.Handler` as backend for logr + +Using a plain `slog.Handler` without support for logr works better than the +other direction: +- All logr verbosity levels can be mapped 1:1 to their corresponding slog level + by negating them. +- Stack unwinding is done by the `slogr.SlogSink` and the resulting program + counter is passed to the `slog.Handler`. +- Names added via `Logger.WithName` are gathered and recorded in an additional + attribute with `logger` as key and the names separated by slash as value. +- `Logger.Error` is turned into a log record with `slog.LevelError` as level + and an additional attribute with `err` as key, if an error was provided. + +The main drawback is that `logr.Marshaler` will not be supported. Types should +ideally support both `logr.Marshaler` and `slog.Valuer`. If compatibility +with logr implementations without slog support is not important, then +`slog.Valuer` is sufficient. + +## Context support for slog + +Storing a logger in a `context.Context` is not supported by +slog. `logr.NewContext` and `logr.FromContext` can be used with slog like this +to fill this gap: + + func HandlerFromContext(ctx context.Context) slog.Handler { + logger, err := logr.FromContext(ctx) + if err == nil { + return slogr.NewSlogHandler(logger) + } + return slog.Default().Handler() + } + + func ContextWithHandler(ctx context.Context, handler slog.Handler) context.Context { + return logr.NewContext(ctx, slogr.NewLogr(handler)) + } + +The downside is that storing and retrieving a `slog.Handler` needs more +allocations compared to using a `logr.Logger`. Therefore the recommendation is +to use the `logr.Logger` API in code which uses contextual logging. + +## FAQ + +### Conceptual + +#### Why structured logging? + +- **Structured logs are more easily queryable**: Since you've got + key-value pairs, it's much easier to query your structured logs for + particular values by filtering on the contents of a particular key -- + think searching request logs for error codes, Kubernetes reconcilers for + the name and namespace of the reconciled object, etc. + +- **Structured logging makes it easier to have cross-referenceable logs**: + Similarly to searchability, if you maintain conventions around your + keys, it becomes easy to gather all log lines related to a particular + concept. + +- **Structured logs allow better dimensions of filtering**: if you have + structure to your logs, you've got more precise control over how much + information is logged -- you might choose in a particular configuration + to log certain keys but not others, only log lines where a certain key + matches a certain value, etc., instead of just having v-levels and names + to key off of. + +- **Structured logs better represent structured data**: sometimes, the + data that you want to log is inherently structured (think tuple-link + objects.) Structured logs allow you to preserve that structure when + outputting. + +#### Why V-levels? + +**V-levels give operators an easy way to control the chattiness of log +operations**. V-levels provide a way for a given package to distinguish +the relative importance or verbosity of a given log message. Then, if +a particular logger or package is logging too many messages, the user +of the package can simply change the v-levels for that library. + +#### Why not named levels, like Info/Warning/Error? + +Read [Dave Cheney's post][warning-makes-no-sense]. Then read [Differences +from Dave's ideas](#differences-from-daves-ideas). + +#### Why not allow format strings, too? + +**Format strings negate many of the benefits of structured logs**: + +- They're not easily searchable without resorting to fuzzy searching, + regular expressions, etc. + +- They don't store structured data well, since contents are flattened into + a string. + +- They're not cross-referenceable. + +- They don't compress easily, since the message is not constant. + +(Unless you turn positional parameters into key-value pairs with numerical +keys, at which point you've gotten key-value logging with meaningless +keys.) + +### Practical + +#### Why key-value pairs, and not a map? + +Key-value pairs are *much* easier to optimize, especially around +allocations. Zap (a structured logger that inspired logr's interface) has +[performance measurements](https://github.com/uber-go/zap#performance) +that show this quite nicely. + +While the interface ends up being a little less obvious, you get +potentially better performance, plus avoid making users type +`map[string]string{}` every time they want to log. + +#### What if my V-levels differ between libraries? + +That's fine. Control your V-levels on a per-logger basis, and use the +`WithName` method to pass different loggers to different libraries. + +Generally, you should take care to ensure that you have relatively +consistent V-levels within a given logger, however, as this makes deciding +on what verbosity of logs to request easier. + +#### But I really want to use a format string! + +That's not actually a question. Assuming your question is "how do +I convert my mental model of logging with format strings to logging with +constant messages": + +1. Figure out what the error actually is, as you'd write in a TL;DR style, + and use that as a message. + +2. For every place you'd write a format specifier, look to the word before + it, and add that as a key value pair. + +For instance, consider the following examples (all taken from spots in the +Kubernetes codebase): + +- `klog.V(4).Infof("Client is returning errors: code %v, error %v", + responseCode, err)` becomes `logger.Error(err, "client returned an + error", "code", responseCode)` + +- `klog.V(4).Infof("Got a Retry-After %ds response for attempt %d to %v", + seconds, retries, url)` becomes `logger.V(4).Info("got a retry-after + response when requesting url", "attempt", retries, "after + seconds", seconds, "url", url)` + +If you *really* must use a format string, use it in a key's value, and +call `fmt.Sprintf` yourself. For instance: `log.Printf("unable to +reflect over type %T")` becomes `logger.Info("unable to reflect over +type", "type", fmt.Sprintf("%T"))`. In general though, the cases where +this is necessary should be few and far between. + +#### How do I choose my V-levels? + +This is basically the only hard constraint: increase V-levels to denote +more verbose or more debug-y logs. + +Otherwise, you can start out with `0` as "you always want to see this", +`1` as "common logging that you might *possibly* want to turn off", and +`10` as "I would like to performance-test your log collection stack." + +Then gradually choose levels in between as you need them, working your way +down from 10 (for debug and trace style logs) and up from 1 (for chattier +info-type logs). For reference, slog pre-defines -4 for debug logs +(corresponds to 4 in logr), which matches what is +[recommended for Kubernetes](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md#what-method-to-use). + +#### How do I choose my keys? + +Keys are fairly flexible, and can hold more or less any string +value. For best compatibility with implementations and consistency +with existing code in other projects, there are a few conventions you +should consider. + +- Make your keys human-readable. +- Constant keys are generally a good idea. +- Be consistent across your codebase. +- Keys should naturally match parts of the message string. +- Use lower case for simple keys and + [lowerCamelCase](https://en.wiktionary.org/wiki/lowerCamelCase) for + more complex ones. Kubernetes is one example of a project that has + [adopted that + convention](https://github.com/kubernetes/community/blob/HEAD/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments). + +While key names are mostly unrestricted (and spaces are acceptable), +it's generally a good idea to stick to printable ascii characters, or at +least match the general character set of your log lines. + +#### Why should keys be constant values? + +The point of structured logging is to make later log processing easier. Your +keys are, effectively, the schema of each log message. If you use different +keys across instances of the same log line, you will make your structured logs +much harder to use. `Sprintf()` is for values, not for keys! + +#### Why is this not a pure interface? + +The Logger type is implemented as a struct in order to allow the Go compiler to +optimize things like high-V `Info` logs that are not triggered. Not all of +these implementations are implemented yet, but this structure was suggested as +a way to ensure they *can* be implemented. All of the real work is behind the +`LogSink` interface. + +[warning-makes-no-sense]: http://dave.cheney.net/2015/11/05/lets-talk-about-logging diff --git a/vendor/github.com/go-logr/logr/SECURITY.md b/vendor/github.com/go-logr/logr/SECURITY.md new file mode 100644 index 00000000..1ca756fc --- /dev/null +++ b/vendor/github.com/go-logr/logr/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +If you have discovered a security vulnerability in this project, please report it +privately. **Do not disclose it as a public issue.** This gives us time to work with you +to fix the issue before public exposure, reducing the chance that the exploit will be +used before a patch is released. + +You may submit the report in the following ways: + +- send an email to go-logr-security@googlegroups.com +- send us a [private vulnerability report](https://github.com/go-logr/logr/security/advisories/new) + +Please provide the following information in your report: + +- A description of the vulnerability and its impact +- How to reproduce the issue + +We ask that you give us 90 days to work on a fix before public exposure. diff --git a/vendor/github.com/go-logr/logr/discard.go b/vendor/github.com/go-logr/logr/discard.go new file mode 100644 index 00000000..99fe8be9 --- /dev/null +++ b/vendor/github.com/go-logr/logr/discard.go @@ -0,0 +1,24 @@ +/* +Copyright 2020 The logr Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logr + +// Discard returns a Logger that discards all messages logged to it. It can be +// used whenever the caller is not interested in the logs. Logger instances +// produced by this function always compare as equal. +func Discard() Logger { + return New(nil) +} diff --git a/vendor/github.com/go-logr/logr/logr.go b/vendor/github.com/go-logr/logr/logr.go new file mode 100644 index 00000000..2a5075a1 --- /dev/null +++ b/vendor/github.com/go-logr/logr/logr.go @@ -0,0 +1,563 @@ +/* +Copyright 2019 The logr Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This design derives from Dave Cheney's blog: +// http://dave.cheney.net/2015/11/05/lets-talk-about-logging + +// Package logr defines a general-purpose logging API and abstract interfaces +// to back that API. Packages in the Go ecosystem can depend on this package, +// while callers can implement logging with whatever backend is appropriate. +// +// # Usage +// +// Logging is done using a Logger instance. Logger is a concrete type with +// methods, which defers the actual logging to a LogSink interface. The main +// methods of Logger are Info() and Error(). Arguments to Info() and Error() +// are key/value pairs rather than printf-style formatted strings, emphasizing +// "structured logging". +// +// With Go's standard log package, we might write: +// +// log.Printf("setting target value %s", targetValue) +// +// With logr's structured logging, we'd write: +// +// logger.Info("setting target", "value", targetValue) +// +// Errors are much the same. Instead of: +// +// log.Printf("failed to open the pod bay door for user %s: %v", user, err) +// +// We'd write: +// +// logger.Error(err, "failed to open the pod bay door", "user", user) +// +// Info() and Error() are very similar, but they are separate methods so that +// LogSink implementations can choose to do things like attach additional +// information (such as stack traces) on calls to Error(). Error() messages are +// always logged, regardless of the current verbosity. If there is no error +// instance available, passing nil is valid. +// +// # Verbosity +// +// Often we want to log information only when the application in "verbose +// mode". To write log lines that are more verbose, Logger has a V() method. +// The higher the V-level of a log line, the less critical it is considered. +// Log-lines with V-levels that are not enabled (as per the LogSink) will not +// be written. Level V(0) is the default, and logger.V(0).Info() has the same +// meaning as logger.Info(). Negative V-levels have the same meaning as V(0). +// Error messages do not have a verbosity level and are always logged. +// +// Where we might have written: +// +// if flVerbose >= 2 { +// log.Printf("an unusual thing happened") +// } +// +// We can write: +// +// logger.V(2).Info("an unusual thing happened") +// +// # Logger Names +// +// Logger instances can have name strings so that all messages logged through +// that instance have additional context. For example, you might want to add +// a subsystem name: +// +// logger.WithName("compactor").Info("started", "time", time.Now()) +// +// The WithName() method returns a new Logger, which can be passed to +// constructors or other functions for further use. Repeated use of WithName() +// will accumulate name "segments". These name segments will be joined in some +// way by the LogSink implementation. It is strongly recommended that name +// segments contain simple identifiers (letters, digits, and hyphen), and do +// not contain characters that could muddle the log output or confuse the +// joining operation (e.g. whitespace, commas, periods, slashes, brackets, +// quotes, etc). +// +// # Saved Values +// +// Logger instances can store any number of key/value pairs, which will be +// logged alongside all messages logged through that instance. For example, +// you might want to create a Logger instance per managed object: +// +// With the standard log package, we might write: +// +// log.Printf("decided to set field foo to value %q for object %s/%s", +// targetValue, object.Namespace, object.Name) +// +// With logr we'd write: +// +// // Elsewhere: set up the logger to log the object name. +// obj.logger = mainLogger.WithValues( +// "name", obj.name, "namespace", obj.namespace) +// +// // later on... +// obj.logger.Info("setting foo", "value", targetValue) +// +// # Best Practices +// +// Logger has very few hard rules, with the goal that LogSink implementations +// might have a lot of freedom to differentiate. There are, however, some +// things to consider. +// +// The log message consists of a constant message attached to the log line. +// This should generally be a simple description of what's occurring, and should +// never be a format string. Variable information can then be attached using +// named values. +// +// Keys are arbitrary strings, but should generally be constant values. Values +// may be any Go value, but how the value is formatted is determined by the +// LogSink implementation. +// +// Logger instances are meant to be passed around by value. Code that receives +// such a value can call its methods without having to check whether the +// instance is ready for use. +// +// The zero logger (= Logger{}) is identical to Discard() and discards all log +// entries. Code that receives a Logger by value can simply call it, the methods +// will never crash. For cases where passing a logger is optional, a pointer to Logger +// should be used. +// +// # Key Naming Conventions +// +// Keys are not strictly required to conform to any specification or regex, but +// it is recommended that they: +// - be human-readable and meaningful (not auto-generated or simple ordinals) +// - be constant (not dependent on input data) +// - contain only printable characters +// - not contain whitespace or punctuation +// - use lower case for simple keys and lowerCamelCase for more complex ones +// +// These guidelines help ensure that log data is processed properly regardless +// of the log implementation. For example, log implementations will try to +// output JSON data or will store data for later database (e.g. SQL) queries. +// +// While users are generally free to use key names of their choice, it's +// generally best to avoid using the following keys, as they're frequently used +// by implementations: +// - "caller": the calling information (file/line) of a particular log line +// - "error": the underlying error value in the `Error` method +// - "level": the log level +// - "logger": the name of the associated logger +// - "msg": the log message +// - "stacktrace": the stack trace associated with a particular log line or +// error (often from the `Error` message) +// - "ts": the timestamp for a log line +// +// Implementations are encouraged to make use of these keys to represent the +// above concepts, when necessary (for example, in a pure-JSON output form, it +// would be necessary to represent at least message and timestamp as ordinary +// named values). +// +// # Break Glass +// +// Implementations may choose to give callers access to the underlying +// logging implementation. The recommended pattern for this is: +// +// // Underlier exposes access to the underlying logging implementation. +// // Since callers only have a logr.Logger, they have to know which +// // implementation is in use, so this interface is less of an abstraction +// // and more of way to test type conversion. +// type Underlier interface { +// GetUnderlying() +// } +// +// Logger grants access to the sink to enable type assertions like this: +// +// func DoSomethingWithImpl(log logr.Logger) { +// if underlier, ok := log.GetSink().(impl.Underlier); ok { +// implLogger := underlier.GetUnderlying() +// ... +// } +// } +// +// Custom `With*` functions can be implemented by copying the complete +// Logger struct and replacing the sink in the copy: +// +// // WithFooBar changes the foobar parameter in the log sink and returns a +// // new logger with that modified sink. It does nothing for loggers where +// // the sink doesn't support that parameter. +// func WithFoobar(log logr.Logger, foobar int) logr.Logger { +// if foobarLogSink, ok := log.GetSink().(FoobarSink); ok { +// log = log.WithSink(foobarLogSink.WithFooBar(foobar)) +// } +// return log +// } +// +// Don't use New to construct a new Logger with a LogSink retrieved from an +// existing Logger. Source code attribution might not work correctly and +// unexported fields in Logger get lost. +// +// Beware that the same LogSink instance may be shared by different logger +// instances. Calling functions that modify the LogSink will affect all of +// those. +package logr + +import ( + "context" +) + +// New returns a new Logger instance. This is primarily used by libraries +// implementing LogSink, rather than end users. Passing a nil sink will create +// a Logger which discards all log lines. +func New(sink LogSink) Logger { + logger := Logger{} + logger.setSink(sink) + if sink != nil { + sink.Init(runtimeInfo) + } + return logger +} + +// setSink stores the sink and updates any related fields. It mutates the +// logger and thus is only safe to use for loggers that are not currently being +// used concurrently. +func (l *Logger) setSink(sink LogSink) { + l.sink = sink +} + +// GetSink returns the stored sink. +func (l Logger) GetSink() LogSink { + return l.sink +} + +// WithSink returns a copy of the logger with the new sink. +func (l Logger) WithSink(sink LogSink) Logger { + l.setSink(sink) + return l +} + +// Logger is an interface to an abstract logging implementation. This is a +// concrete type for performance reasons, but all the real work is passed on to +// a LogSink. Implementations of LogSink should provide their own constructors +// that return Logger, not LogSink. +// +// The underlying sink can be accessed through GetSink and be modified through +// WithSink. This enables the implementation of custom extensions (see "Break +// Glass" in the package documentation). Normally the sink should be used only +// indirectly. +type Logger struct { + sink LogSink + level int +} + +// Enabled tests whether this Logger is enabled. For example, commandline +// flags might be used to set the logging verbosity and disable some info logs. +func (l Logger) Enabled() bool { + // Some implementations of LogSink look at the caller in Enabled (e.g. + // different verbosity levels per package or file), but we only pass one + // CallDepth in (via Init). This means that all calls from Logger to the + // LogSink's Enabled, Info, and Error methods must have the same number of + // frames. In other words, Logger methods can't call other Logger methods + // which call these LogSink methods unless we do it the same in all paths. + return l.sink != nil && l.sink.Enabled(l.level) +} + +// Info logs a non-error message with the given key/value pairs as context. +// +// The msg argument should be used to add some constant description to the log +// line. The key/value pairs can then be used to add additional variable +// information. The key/value pairs must alternate string keys and arbitrary +// values. +func (l Logger) Info(msg string, keysAndValues ...any) { + if l.sink == nil { + return + } + if l.sink.Enabled(l.level) { // see comment in Enabled + if withHelper, ok := l.sink.(CallStackHelperLogSink); ok { + withHelper.GetCallStackHelper()() + } + l.sink.Info(l.level, msg, keysAndValues...) + } +} + +// Error logs an error, with the given message and key/value pairs as context. +// It functions similarly to Info, but may have unique behavior, and should be +// preferred for logging errors (see the package documentations for more +// information). The log message will always be emitted, regardless of +// verbosity level. +// +// The msg argument should be used to add context to any underlying error, +// while the err argument should be used to attach the actual error that +// triggered this log line, if present. The err parameter is optional +// and nil may be passed instead of an error instance. +func (l Logger) Error(err error, msg string, keysAndValues ...any) { + if l.sink == nil { + return + } + if withHelper, ok := l.sink.(CallStackHelperLogSink); ok { + withHelper.GetCallStackHelper()() + } + l.sink.Error(err, msg, keysAndValues...) +} + +// V returns a new Logger instance for a specific verbosity level, relative to +// this Logger. In other words, V-levels are additive. A higher verbosity +// level means a log message is less important. Negative V-levels are treated +// as 0. +func (l Logger) V(level int) Logger { + if l.sink == nil { + return l + } + if level < 0 { + level = 0 + } + l.level += level + return l +} + +// GetV returns the verbosity level of the logger. If the logger's LogSink is +// nil as in the Discard logger, this will always return 0. +func (l Logger) GetV() int { + // 0 if l.sink nil because of the if check in V above. + return l.level +} + +// WithValues returns a new Logger instance with additional key/value pairs. +// See Info for documentation on how key/value pairs work. +func (l Logger) WithValues(keysAndValues ...any) Logger { + if l.sink == nil { + return l + } + l.setSink(l.sink.WithValues(keysAndValues...)) + return l +} + +// WithName returns a new Logger instance with the specified name element added +// to the Logger's name. Successive calls with WithName append additional +// suffixes to the Logger's name. It's strongly recommended that name segments +// contain only letters, digits, and hyphens (see the package documentation for +// more information). +func (l Logger) WithName(name string) Logger { + if l.sink == nil { + return l + } + l.setSink(l.sink.WithName(name)) + return l +} + +// WithCallDepth returns a Logger instance that offsets the call stack by the +// specified number of frames when logging call site information, if possible. +// This is useful for users who have helper functions between the "real" call +// site and the actual calls to Logger methods. If depth is 0 the attribution +// should be to the direct caller of this function. If depth is 1 the +// attribution should skip 1 call frame, and so on. Successive calls to this +// are additive. +// +// If the underlying log implementation supports a WithCallDepth(int) method, +// it will be called and the result returned. If the implementation does not +// support CallDepthLogSink, the original Logger will be returned. +// +// To skip one level, WithCallStackHelper() should be used instead of +// WithCallDepth(1) because it works with implementions that support the +// CallDepthLogSink and/or CallStackHelperLogSink interfaces. +func (l Logger) WithCallDepth(depth int) Logger { + if l.sink == nil { + return l + } + if withCallDepth, ok := l.sink.(CallDepthLogSink); ok { + l.setSink(withCallDepth.WithCallDepth(depth)) + } + return l +} + +// WithCallStackHelper returns a new Logger instance that skips the direct +// caller when logging call site information, if possible. This is useful for +// users who have helper functions between the "real" call site and the actual +// calls to Logger methods and want to support loggers which depend on marking +// each individual helper function, like loggers based on testing.T. +// +// In addition to using that new logger instance, callers also must call the +// returned function. +// +// If the underlying log implementation supports a WithCallDepth(int) method, +// WithCallDepth(1) will be called to produce a new logger. If it supports a +// WithCallStackHelper() method, that will be also called. If the +// implementation does not support either of these, the original Logger will be +// returned. +func (l Logger) WithCallStackHelper() (func(), Logger) { + if l.sink == nil { + return func() {}, l + } + var helper func() + if withCallDepth, ok := l.sink.(CallDepthLogSink); ok { + l.setSink(withCallDepth.WithCallDepth(1)) + } + if withHelper, ok := l.sink.(CallStackHelperLogSink); ok { + helper = withHelper.GetCallStackHelper() + } else { + helper = func() {} + } + return helper, l +} + +// IsZero returns true if this logger is an uninitialized zero value +func (l Logger) IsZero() bool { + return l.sink == nil +} + +// contextKey is how we find Loggers in a context.Context. +type contextKey struct{} + +// FromContext returns a Logger from ctx or an error if no Logger is found. +func FromContext(ctx context.Context) (Logger, error) { + if v, ok := ctx.Value(contextKey{}).(Logger); ok { + return v, nil + } + + return Logger{}, notFoundError{} +} + +// notFoundError exists to carry an IsNotFound method. +type notFoundError struct{} + +func (notFoundError) Error() string { + return "no logr.Logger was present" +} + +func (notFoundError) IsNotFound() bool { + return true +} + +// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this +// returns a Logger that discards all log messages. +func FromContextOrDiscard(ctx context.Context) Logger { + if v, ok := ctx.Value(contextKey{}).(Logger); ok { + return v + } + + return Discard() +} + +// NewContext returns a new Context, derived from ctx, which carries the +// provided Logger. +func NewContext(ctx context.Context, logger Logger) context.Context { + return context.WithValue(ctx, contextKey{}, logger) +} + +// RuntimeInfo holds information that the logr "core" library knows which +// LogSinks might want to know. +type RuntimeInfo struct { + // CallDepth is the number of call frames the logr library adds between the + // end-user and the LogSink. LogSink implementations which choose to print + // the original logging site (e.g. file & line) should climb this many + // additional frames to find it. + CallDepth int +} + +// runtimeInfo is a static global. It must not be changed at run time. +var runtimeInfo = RuntimeInfo{ + CallDepth: 1, +} + +// LogSink represents a logging implementation. End-users will generally not +// interact with this type. +type LogSink interface { + // Init receives optional information about the logr library for LogSink + // implementations that need it. + Init(info RuntimeInfo) + + // Enabled tests whether this LogSink is enabled at the specified V-level. + // For example, commandline flags might be used to set the logging + // verbosity and disable some info logs. + Enabled(level int) bool + + // Info logs a non-error message with the given key/value pairs as context. + // The level argument is provided for optional logging. This method will + // only be called when Enabled(level) is true. See Logger.Info for more + // details. + Info(level int, msg string, keysAndValues ...any) + + // Error logs an error, with the given message and key/value pairs as + // context. See Logger.Error for more details. + Error(err error, msg string, keysAndValues ...any) + + // WithValues returns a new LogSink with additional key/value pairs. See + // Logger.WithValues for more details. + WithValues(keysAndValues ...any) LogSink + + // WithName returns a new LogSink with the specified name appended. See + // Logger.WithName for more details. + WithName(name string) LogSink +} + +// CallDepthLogSink represents a LogSink that knows how to climb the call stack +// to identify the original call site and can offset the depth by a specified +// number of frames. This is useful for users who have helper functions +// between the "real" call site and the actual calls to Logger methods. +// Implementations that log information about the call site (such as file, +// function, or line) would otherwise log information about the intermediate +// helper functions. +// +// This is an optional interface and implementations are not required to +// support it. +type CallDepthLogSink interface { + // WithCallDepth returns a LogSink that will offset the call + // stack by the specified number of frames when logging call + // site information. + // + // If depth is 0, the LogSink should skip exactly the number + // of call frames defined in RuntimeInfo.CallDepth when Info + // or Error are called, i.e. the attribution should be to the + // direct caller of Logger.Info or Logger.Error. + // + // If depth is 1 the attribution should skip 1 call frame, and so on. + // Successive calls to this are additive. + WithCallDepth(depth int) LogSink +} + +// CallStackHelperLogSink represents a LogSink that knows how to climb +// the call stack to identify the original call site and can skip +// intermediate helper functions if they mark themselves as +// helper. Go's testing package uses that approach. +// +// This is useful for users who have helper functions between the +// "real" call site and the actual calls to Logger methods. +// Implementations that log information about the call site (such as +// file, function, or line) would otherwise log information about the +// intermediate helper functions. +// +// This is an optional interface and implementations are not required +// to support it. Implementations that choose to support this must not +// simply implement it as WithCallDepth(1), because +// Logger.WithCallStackHelper will call both methods if they are +// present. This should only be implemented for LogSinks that actually +// need it, as with testing.T. +type CallStackHelperLogSink interface { + // GetCallStackHelper returns a function that must be called + // to mark the direct caller as helper function when logging + // call site information. + GetCallStackHelper() func() +} + +// Marshaler is an optional interface that logged values may choose to +// implement. Loggers with structured output, such as JSON, should +// log the object return by the MarshalLog method instead of the +// original value. +type Marshaler interface { + // MarshalLog can be used to: + // - ensure that structs are not logged as strings when the original + // value has a String method: return a different type without a + // String method + // - select which fields of a complex type should get logged: + // return a simpler struct with fewer fields + // - log unexported fields: return a different struct + // with exported fields + // + // It may return any value of any type. + MarshalLog() any +} diff --git a/vendor/github.com/gofrs/uuid/.travis.yml b/vendor/github.com/gofrs/uuid/.travis.yml deleted file mode 100644 index ee1e4fa0..00000000 --- a/vendor/github.com/gofrs/uuid/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: go -sudo: false -go: - - 1.7.x - - 1.8.x - - 1.9.x - - 1.10.x - - 1.11.x - - tip -matrix: - allow_failures: - - go: tip - fast_finish: true -env: - - GO111MODULE=on -before_install: - - go get golang.org/x/tools/cmd/cover -script: - - go test ./... -race -coverprofile=coverage.txt -covermode=atomic -after_success: - - bash <(curl -s https://codecov.io/bash) -notifications: - email: false diff --git a/vendor/github.com/gofrs/uuid/README.md b/vendor/github.com/gofrs/uuid/README.md index efc3204f..4f73bec8 100644 --- a/vendor/github.com/gofrs/uuid/README.md +++ b/vendor/github.com/gofrs/uuid/README.md @@ -12,11 +12,18 @@ and parsing of UUIDs in different formats. This package supports the following UUID versions: * Version 1, based on timestamp and MAC address (RFC-4122) -* Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1) * Version 3, based on MD5 hashing of a named value (RFC-4122) * Version 4, based on random numbers (RFC-4122) * Version 5, based on SHA-1 hashing of a named value (RFC-4122) +This package also supports experimental Universally Unique Identifier implementations based on a +[draft RFC](https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html) that updates RFC-4122 +* Version 6, a k-sortable id based on timestamp, and field-compatible with v1 (draft-peabody-dispatch-new-uuid-format, RFC-4122) +* Version 7, a k-sortable id based on timestamp (draft-peabody-dispatch-new-uuid-format, RFC-4122) + +The v6 and v7 IDs are **not** considered a part of the stable API, and may be subject to behavior or API changes as part of minor releases +to this package. They will be updated as the draft RFC changes, and will become stable if and when the draft RFC is accepted. + ## Project History This project was originally forked from the @@ -107,3 +114,4 @@ func main() { * [RFC-4122](https://tools.ietf.org/html/rfc4122) * [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01) +* [New UUID Formats RFC Draft (Peabody) Rev 04](https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html#) diff --git a/vendor/github.com/gofrs/uuid/codec.go b/vendor/github.com/gofrs/uuid/codec.go index e3d8cfb4..66502641 100644 --- a/vendor/github.com/gofrs/uuid/codec.go +++ b/vendor/github.com/gofrs/uuid/codec.go @@ -22,8 +22,7 @@ package uuid import ( - "bytes" - "encoding/hex" + "errors" "fmt" ) @@ -45,11 +44,77 @@ func FromBytesOrNil(input []byte) UUID { return uuid } +var errInvalidFormat = errors.New("uuid: invalid UUID format") + +func fromHexChar(c byte) byte { + switch { + case '0' <= c && c <= '9': + return c - '0' + case 'a' <= c && c <= 'f': + return c - 'a' + 10 + case 'A' <= c && c <= 'F': + return c - 'A' + 10 + } + return 255 +} + +// Parse parses the UUID stored in the string text. Parsing and supported +// formats are the same as UnmarshalText. +func (u *UUID) Parse(s string) error { + switch len(s) { + case 32: // hash + case 36: // canonical + case 34, 38: + if s[0] != '{' || s[len(s)-1] != '}' { + return fmt.Errorf("uuid: incorrect UUID format in string %q", s) + } + s = s[1 : len(s)-1] + case 41, 45: + if s[:9] != "urn:uuid:" { + return fmt.Errorf("uuid: incorrect UUID format in string %q", s[:9]) + } + s = s[9:] + default: + return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(s), s) + } + // canonical + if len(s) == 36 { + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return fmt.Errorf("uuid: incorrect UUID format in string %q", s) + } + for i, x := range [16]byte{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34, + } { + v1 := fromHexChar(s[x]) + v2 := fromHexChar(s[x+1]) + if v1|v2 == 255 { + return errInvalidFormat + } + u[i] = (v1 << 4) | v2 + } + return nil + } + // hash like + for i := 0; i < 32; i += 2 { + v1 := fromHexChar(s[i]) + v2 := fromHexChar(s[i+1]) + if v1|v2 == 255 { + return errInvalidFormat + } + u[i/2] = (v1 << 4) | v2 + } + return nil +} + // FromString returns a UUID parsed from the input string. // Input is expected in a form accepted by UnmarshalText. -func FromString(input string) (UUID, error) { - u := UUID{} - err := u.UnmarshalText([]byte(input)) +func FromString(text string) (UUID, error) { + var u UUID + err := u.Parse(text) return u, err } @@ -66,133 +131,90 @@ func FromStringOrNil(input string) UUID { // MarshalText implements the encoding.TextMarshaler interface. // The encoding is the same as returned by the String() method. func (u UUID) MarshalText() ([]byte, error) { - return []byte(u.String()), nil + var buf [36]byte + encodeCanonical(buf[:], u) + return buf[:], nil } // UnmarshalText implements the encoding.TextUnmarshaler interface. // Following formats are supported: // -// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", -// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", -// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" -// "6ba7b8109dad11d180b400c04fd430c8" -// "{6ba7b8109dad11d180b400c04fd430c8}", -// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8" +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" +// "6ba7b8109dad11d180b400c04fd430c8" +// "{6ba7b8109dad11d180b400c04fd430c8}", +// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8" // // ABNF for supported UUID text representation follows: // -// URN := 'urn' -// UUID-NID := 'uuid' -// -// hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | -// 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | -// 'A' | 'B' | 'C' | 'D' | 'E' | 'F' +// URN := 'urn' +// UUID-NID := 'uuid' // -// hexoct := hexdig hexdig -// 2hexoct := hexoct hexoct -// 4hexoct := 2hexoct 2hexoct -// 6hexoct := 4hexoct 2hexoct -// 12hexoct := 6hexoct 6hexoct +// hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | +// 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | +// 'A' | 'B' | 'C' | 'D' | 'E' | 'F' // -// hashlike := 12hexoct -// canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct +// hexoct := hexdig hexdig +// 2hexoct := hexoct hexoct +// 4hexoct := 2hexoct 2hexoct +// 6hexoct := 4hexoct 2hexoct +// 12hexoct := 6hexoct 6hexoct // -// plain := canonical | hashlike -// uuid := canonical | hashlike | braced | urn +// hashlike := 12hexoct +// canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct // -// braced := '{' plain '}' | '{' hashlike '}' -// urn := URN ':' UUID-NID ':' plain +// plain := canonical | hashlike +// uuid := canonical | hashlike | braced | urn // -func (u *UUID) UnmarshalText(text []byte) error { - switch len(text) { - case 32: - return u.decodeHashLike(text) +// braced := '{' plain '}' | '{' hashlike '}' +// urn := URN ':' UUID-NID ':' plain +func (u *UUID) UnmarshalText(b []byte) error { + switch len(b) { + case 32: // hash + case 36: // canonical case 34, 38: - return u.decodeBraced(text) - case 36: - return u.decodeCanonical(text) + if b[0] != '{' || b[len(b)-1] != '}' { + return fmt.Errorf("uuid: incorrect UUID format in string %q", b) + } + b = b[1 : len(b)-1] case 41, 45: - return u.decodeURN(text) + if string(b[:9]) != "urn:uuid:" { + return fmt.Errorf("uuid: incorrect UUID format in string %q", b[:9]) + } + b = b[9:] default: - return fmt.Errorf("uuid: incorrect UUID length: %s", text) - } -} - -// decodeCanonical decodes UUID strings that are formatted as defined in RFC-4122 (section 3): -// "6ba7b810-9dad-11d1-80b4-00c04fd430c8". -func (u *UUID) decodeCanonical(t []byte) error { - if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' { - return fmt.Errorf("uuid: incorrect UUID format %s", t) + return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(b), b) } - - src := t - dst := u[:] - - for i, byteGroup := range byteGroups { - if i > 0 { - src = src[1:] // skip dash + if len(b) == 36 { + if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { + return fmt.Errorf("uuid: incorrect UUID format in string %q", b) } - _, err := hex.Decode(dst[:byteGroup/2], src[:byteGroup]) - if err != nil { - return err + for i, x := range [16]byte{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34, + } { + v1 := fromHexChar(b[x]) + v2 := fromHexChar(b[x+1]) + if v1|v2 == 255 { + return errInvalidFormat + } + u[i] = (v1 << 4) | v2 } - src = src[byteGroup:] - dst = dst[byteGroup/2:] - } - - return nil -} - -// decodeHashLike decodes UUID strings that are using the following format: -// "6ba7b8109dad11d180b400c04fd430c8". -func (u *UUID) decodeHashLike(t []byte) error { - src := t[:] - dst := u[:] - - _, err := hex.Decode(dst, src) - return err -} - -// decodeBraced decodes UUID strings that are using the following formats: -// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" -// "{6ba7b8109dad11d180b400c04fd430c8}". -func (u *UUID) decodeBraced(t []byte) error { - l := len(t) - - if t[0] != '{' || t[l-1] != '}' { - return fmt.Errorf("uuid: incorrect UUID format %s", t) + return nil } - - return u.decodePlain(t[1 : l-1]) -} - -// decodeURN decodes UUID strings that are using the following formats: -// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" -// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8". -func (u *UUID) decodeURN(t []byte) error { - total := len(t) - - urnUUIDPrefix := t[:9] - - if !bytes.Equal(urnUUIDPrefix, urnPrefix) { - return fmt.Errorf("uuid: incorrect UUID format: %s", t) - } - - return u.decodePlain(t[9:total]) -} - -// decodePlain decodes UUID strings that are using the following formats: -// "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format -// "6ba7b8109dad11d180b400c04fd430c8". -func (u *UUID) decodePlain(t []byte) error { - switch len(t) { - case 32: - return u.decodeHashLike(t) - case 36: - return u.decodeCanonical(t) - default: - return fmt.Errorf("uuid: incorrect UUID length: %s", t) + for i := 0; i < 32; i += 2 { + v1 := fromHexChar(b[i]) + v2 := fromHexChar(b[i+1]) + if v1|v2 == 255 { + return errInvalidFormat + } + u[i/2] = (v1 << 4) | v2 } + return nil } // MarshalBinary implements the encoding.BinaryMarshaler interface. diff --git a/vendor/github.com/gofrs/uuid/fuzz.go b/vendor/github.com/gofrs/uuid/fuzz.go index afaefbc8..ccf8d4ca 100644 --- a/vendor/github.com/gofrs/uuid/fuzz.go +++ b/vendor/github.com/gofrs/uuid/fuzz.go @@ -19,6 +19,7 @@ // 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:build gofuzz // +build gofuzz package uuid @@ -27,15 +28,15 @@ package uuid // // To run: // -// $ go get github.com/dvyukov/go-fuzz/... -// $ cd $GOPATH/src/github.com/gofrs/uuid -// $ go-fuzz-build github.com/gofrs/uuid -// $ go-fuzz -bin=uuid-fuzz.zip -workdir=./testdata +// $ go get github.com/dvyukov/go-fuzz/... +// $ cd $GOPATH/src/github.com/gofrs/uuid +// $ go-fuzz-build github.com/gofrs/uuid +// $ go-fuzz -bin=uuid-fuzz.zip -workdir=./testdata // // If you make significant changes to FromString / UnmarshalText and add // new cases to fromStringTests (in codec_test.go), please run // -// $ go test -seed_fuzz_corpus +// $ go test -seed_fuzz_corpus // // to seed the corpus with the new interesting inputs, then run the fuzzer. func Fuzz(data []byte) int { diff --git a/vendor/github.com/gofrs/uuid/generator.go b/vendor/github.com/gofrs/uuid/generator.go index 4257761f..44be9e15 100644 --- a/vendor/github.com/gofrs/uuid/generator.go +++ b/vendor/github.com/gofrs/uuid/generator.go @@ -30,7 +30,6 @@ import ( "hash" "io" "net" - "os" "sync" "time" ) @@ -39,7 +38,8 @@ import ( // UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). const epochStart = 122192928000000000 -type epochFunc func() time.Time +// EpochFunc is the function type used to provide the current time. +type EpochFunc func() time.Time // HWAddrFunc is the function type used to provide hardware (MAC) addresses. type HWAddrFunc func() (net.HardwareAddr, error) @@ -47,21 +47,11 @@ type HWAddrFunc func() (net.HardwareAddr, error) // DefaultGenerator is the default UUID Generator used by this package. var DefaultGenerator Generator = NewGen() -var ( - posixUID = uint32(os.Getuid()) - posixGID = uint32(os.Getgid()) -) - // NewV1 returns a UUID based on the current timestamp and MAC address. func NewV1() (UUID, error) { return DefaultGenerator.NewV1() } -// NewV2 returns a DCE Security UUID based on the POSIX UID/GID. -func NewV2(domain byte) (UUID, error) { - return DefaultGenerator.NewV2(domain) -} - // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name. func NewV3(ns UUID, name string) UUID { return DefaultGenerator.NewV3(ns, name) @@ -77,13 +67,39 @@ func NewV5(ns UUID, name string) UUID { return DefaultGenerator.NewV5(ns, name) } +// NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of +// pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit +// order being adjusted to allow the UUID to be k-sortable. +// +// This is implemented based on revision 03 of the Peabody UUID draft, and may +// be subject to change pending further revisions. Until the final specification +// revision is finished, changes required to implement updates to the spec will +// not be considered a breaking change. They will happen as a minor version +// releases until the spec is final. +func NewV6() (UUID, error) { + return DefaultGenerator.NewV6() +} + +// NewV7 returns a k-sortable UUID based on the current millisecond precision +// UNIX epoch and 74 bits of pseudorandom data. It supports single-node batch generation (multiple UUIDs in the same timestamp) with a Monotonic Random counter. +// +// This is implemented based on revision 04 of the Peabody UUID draft, and may +// be subject to change pending further revisions. Until the final specification +// revision is finished, changes required to implement updates to the spec will +// not be considered a breaking change. They will happen as a minor version +// releases until the spec is final. +func NewV7() (UUID, error) { + return DefaultGenerator.NewV7() +} + // Generator provides an interface for generating UUIDs. type Generator interface { NewV1() (UUID, error) - NewV2(domain byte) (UUID, error) NewV3(ns UUID, name string) UUID NewV4() (UUID, error) NewV5(ns UUID, name string) UUID + NewV6() (UUID, error) + NewV7() (UUID, error) } // Gen is a reference UUID generator based on the specifications laid out in @@ -104,13 +120,16 @@ type Gen struct { rand io.Reader - epochFunc epochFunc + epochFunc EpochFunc hwAddrFunc HWAddrFunc lastTime uint64 clockSequence uint16 hardwareAddr [6]byte } +// GenOption is a function type that can be used to configure a Gen generator. +type GenOption func(*Gen) + // interface check -- build will fail if *Gen doesn't satisfy Generator var _ Generator = (*Gen)(nil) @@ -132,18 +151,82 @@ func NewGen() *Gen { // MAC address being used, you'll need to create a new generator using this // function. func NewGenWithHWAF(hwaf HWAddrFunc) *Gen { - return &Gen{ + return NewGenWithOptions(WithHWAddrFunc(hwaf)) +} + +// NewGenWithOptions returns a new instance of Gen with the options provided. +// Most people should use NewGen() or NewGenWithHWAF() instead. +// +// To customize the generator, you can pass in one or more GenOption functions. +// For example: +// +// gen := NewGenWithOptions( +// WithHWAddrFunc(myHWAddrFunc), +// WithEpochFunc(myEpochFunc), +// WithRandomReader(myRandomReader), +// ) +// +// NewGenWithOptions(WithHWAddrFunc(myHWAddrFunc)) is equivalent to calling +// NewGenWithHWAF(myHWAddrFunc) +// NewGenWithOptions() is equivalent to calling NewGen() +func NewGenWithOptions(opts ...GenOption) *Gen { + gen := &Gen{ epochFunc: time.Now, - hwAddrFunc: hwaf, + hwAddrFunc: defaultHWAddrFunc, rand: rand.Reader, } + + for _, opt := range opts { + opt(gen) + } + + return gen +} + +// WithHWAddrFunc is a GenOption that allows you to provide your own HWAddrFunc +// function. +// When this option is nil, the defaultHWAddrFunc is used. +func WithHWAddrFunc(hwaf HWAddrFunc) GenOption { + return func(gen *Gen) { + if hwaf == nil { + hwaf = defaultHWAddrFunc + } + + gen.hwAddrFunc = hwaf + } +} + +// WithEpochFunc is a GenOption that allows you to provide your own EpochFunc +// function. +// When this option is nil, time.Now is used. +func WithEpochFunc(epochf EpochFunc) GenOption { + return func(gen *Gen) { + if epochf == nil { + epochf = time.Now + } + + gen.epochFunc = epochf + } +} + +// WithRandomReader is a GenOption that allows you to provide your own random +// reader. +// When this option is nil, the default rand.Reader is used. +func WithRandomReader(reader io.Reader) GenOption { + return func(gen *Gen) { + if reader == nil { + reader = rand.Reader + } + + gen.rand = reader + } } // NewV1 returns a UUID based on the current timestamp and MAC address. func (g *Gen) NewV1() (UUID, error) { u := UUID{} - timeNow, clockSeq, err := g.getClockSequence() + timeNow, clockSeq, err := g.getClockSequence(false) if err != nil { return Nil, err } @@ -164,28 +247,6 @@ func (g *Gen) NewV1() (UUID, error) { return u, nil } -// NewV2 returns a DCE Security UUID based on the POSIX UID/GID. -func (g *Gen) NewV2(domain byte) (UUID, error) { - u, err := g.NewV1() - if err != nil { - return Nil, err - } - - switch domain { - case DomainPerson: - binary.BigEndian.PutUint32(u[:], posixUID) - case DomainGroup: - binary.BigEndian.PutUint32(u[:], posixGID) - } - - u[9] = domain - - u.SetVersion(V2) - u.SetVariant(VariantRFC4122) - - return u, nil -} - // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name. func (g *Gen) NewV3(ns UUID, name string) UUID { u := newFromHash(md5.New(), ns, name) @@ -216,8 +277,44 @@ func (g *Gen) NewV5(ns UUID, name string) UUID { return u } -// Returns the epoch and clock sequence. -func (g *Gen) getClockSequence() (uint64, uint16, error) { +// NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of +// pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit +// order being adjusted to allow the UUID to be k-sortable. +// +// This is implemented based on revision 03 of the Peabody UUID draft, and may +// be subject to change pending further revisions. Until the final specification +// revision is finished, changes required to implement updates to the spec will +// not be considered a breaking change. They will happen as a minor version +// releases until the spec is final. +func (g *Gen) NewV6() (UUID, error) { + var u UUID + + if _, err := io.ReadFull(g.rand, u[10:]); err != nil { + return Nil, err + } + + timeNow, clockSeq, err := g.getClockSequence(false) + if err != nil { + return Nil, err + } + + binary.BigEndian.PutUint32(u[0:], uint32(timeNow>>28)) // set time_high + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>12)) // set time_mid + binary.BigEndian.PutUint16(u[6:], uint16(timeNow&0xfff)) // set time_low (minus four version bits) + binary.BigEndian.PutUint16(u[8:], clockSeq&0x3fff) // set clk_seq_hi_res (minus two variant bits) + + u.SetVersion(V6) + u.SetVariant(VariantRFC4122) + + return u, nil +} + +// getClockSequence returns the epoch and clock sequence for V1,V6 and V7 UUIDs. +// +// When useUnixTSMs is false, it uses the Coordinated Universal Time (UTC) as a count of 100- +// +// nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of Gregorian reform to the Christian calendar). +func (g *Gen) getClockSequence(useUnixTSMs bool) (uint64, uint16, error) { var err error g.clockSequenceOnce.Do(func() { buf := make([]byte, 2) @@ -233,7 +330,12 @@ func (g *Gen) getClockSequence() (uint64, uint16, error) { g.storageMutex.Lock() defer g.storageMutex.Unlock() - timeNow := g.getEpoch() + var timeNow uint64 + if useUnixTSMs { + timeNow = uint64(g.epochFunc().UnixMilli()) + } else { + timeNow = g.getEpoch() + } // Clock didn't change since last UUID generation. // Should increase clock sequence. if timeNow <= g.lastTime { @@ -244,6 +346,59 @@ func (g *Gen) getClockSequence() (uint64, uint16, error) { return timeNow, g.clockSequence, nil } +// NewV7 returns a k-sortable UUID based on the current millisecond precision +// UNIX epoch and 74 bits of pseudorandom data. +// +// This is implemented based on revision 04 of the Peabody UUID draft, and may +// be subject to change pending further revisions. Until the final specification +// revision is finished, changes required to implement updates to the spec will +// not be considered a breaking change. They will happen as a minor version +// releases until the spec is final. +func (g *Gen) NewV7() (UUID, error) { + var u UUID + /* https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html#name-uuid-version-7 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unix_ts_ms | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unix_ts_ms | ver | rand_a | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |var| rand_b | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | rand_b | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + + ms, clockSeq, err := g.getClockSequence(true) + if err != nil { + return Nil, err + } + //UUIDv7 features a 48 bit timestamp. First 32bit (4bytes) represents seconds since 1970, followed by 2 bytes for the ms granularity. + u[0] = byte(ms >> 40) //1-6 bytes: big-endian unsigned number of Unix epoch timestamp + u[1] = byte(ms >> 32) + u[2] = byte(ms >> 24) + u[3] = byte(ms >> 16) + u[4] = byte(ms >> 8) + u[5] = byte(ms) + + //support batching by using a monotonic pseudo-random sequence + //The 6th byte contains the version and partially rand_a data. + //We will lose the most significant bites from the clockSeq (with SetVersion), but it is ok, we need the least significant that contains the counter to ensure the monotonic property + binary.BigEndian.PutUint16(u[6:8], clockSeq) // set rand_a with clock seq which is random and monotonic + + //override first 4bits of u[6]. + u.SetVersion(V7) + + //set rand_b 64bits of pseudo-random bits (first 2 will be overridden) + if _, err = io.ReadFull(g.rand, u[8:16]); err != nil { + return Nil, err + } + //override first 2 bits of byte[8] for the variant + u.SetVariant(VariantRFC4122) + + return u, nil +} + // Returns the hardware address. func (g *Gen) getHardwareAddr() ([]byte, error) { var err error @@ -284,9 +439,11 @@ func newFromHash(h hash.Hash, ns UUID, name string) UUID { return u } +var netInterfaces = net.Interfaces + // Returns the hardware address. func defaultHWAddrFunc() (net.HardwareAddr, error) { - ifaces, err := net.Interfaces() + ifaces, err := netInterfaces() if err != nil { return []byte{}, err } diff --git a/vendor/github.com/gofrs/uuid/sql.go b/vendor/github.com/gofrs/uuid/sql.go index 6f254a4f..01d5d884 100644 --- a/vendor/github.com/gofrs/uuid/sql.go +++ b/vendor/github.com/gofrs/uuid/sql.go @@ -22,12 +22,14 @@ package uuid import ( - "bytes" + "database/sql" "database/sql/driver" - "encoding/json" "fmt" ) +var _ driver.Valuer = UUID{} +var _ sql.Scanner = (*UUID)(nil) + // Value implements the driver.Valuer interface. func (u UUID) Value() (driver.Value, error) { return u.String(), nil @@ -49,7 +51,9 @@ func (u *UUID) Scan(src interface{}) error { return u.UnmarshalText(src) case string: - return u.UnmarshalText([]byte(src)) + uu, err := FromString(src) + *u = uu + return err } return fmt.Errorf("uuid: cannot convert %T to UUID", src) @@ -83,27 +87,30 @@ func (u *NullUUID) Scan(src interface{}) error { return u.UUID.Scan(src) } +var nullJSON = []byte("null") + // MarshalJSON marshals the NullUUID as null or the nested UUID func (u NullUUID) MarshalJSON() ([]byte, error) { if !u.Valid { - return json.Marshal(nil) + return nullJSON, nil } - - return json.Marshal(u.UUID) + var buf [38]byte + buf[0] = '"' + encodeCanonical(buf[1:37], u.UUID) + buf[37] = '"' + return buf[:], nil } // UnmarshalJSON unmarshals a NullUUID func (u *NullUUID) UnmarshalJSON(b []byte) error { - if bytes.Equal(b, []byte("null")) { + if string(b) == "null" { u.UUID, u.Valid = Nil, false return nil } - - if err := json.Unmarshal(b, &u.UUID); err != nil { - return err + if n := len(b); n >= 2 && b[0] == '"' { + b = b[1 : n-1] } - - u.Valid = true - - return nil + err := u.UUID.UnmarshalText(b) + u.Valid = (err == nil) + return err } diff --git a/vendor/github.com/gofrs/uuid/uuid.go b/vendor/github.com/gofrs/uuid/uuid.go index 29ef4405..5320fb53 100644 --- a/vendor/github.com/gofrs/uuid/uuid.go +++ b/vendor/github.com/gofrs/uuid/uuid.go @@ -19,14 +19,25 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// Package uuid provides implementations of the Universally Unique Identifier (UUID), as specified in RFC-4122 and DCE 1.1. +// Package uuid provides implementations of the Universally Unique Identifier +// (UUID), as specified in RFC-4122 and the Peabody RFC Draft (revision 03). // -// RFC-4122[1] provides the specification for versions 1, 3, 4, and 5. +// RFC-4122[1] provides the specification for versions 1, 3, 4, and 5. The +// Peabody UUID RFC Draft[2] provides the specification for the new k-sortable +// UUIDs, versions 6 and 7. // -// DCE 1.1[2] provides the specification for version 2. +// DCE 1.1[3] provides the specification for version 2, but version 2 support +// was removed from this package in v4 due to some concerns with the +// specification itself. Reading the spec, it seems that it would result in +// generating UUIDs that aren't very unique. In having read the spec it seemed +// that our implementation did not meet the spec. It also seems to be at-odds +// with RFC 4122, meaning we would need quite a bit of special code to support +// it. Lastly, there were no Version 2 implementations that we could find to +// ensure we were understanding the specification correctly. // // [1] https://tools.ietf.org/html/rfc4122 -// [2] http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01 +// [2] https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03 +// [3] http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01 package uuid import ( @@ -46,10 +57,13 @@ type UUID [Size]byte const ( _ byte = iota V1 // Version 1 (date-time and MAC address) - V2 // Version 2 (date-time and MAC address, DCE security version) + _ // Version 2 (date-time and MAC address, DCE security version) [removed] V3 // Version 3 (namespace name-based) V4 // Version 4 (random) V5 // Version 5 (namespace name-based) + V6 // Version 6 (k-sortable timestamp and random data, field-compatible with v1) [peabody draft] + V7 // Version 7 (k-sortable timestamp and random data) [peabody draft] + _ // Version 8 (k-sortable timestamp, meant for custom implementations) [peabody draft] [not implemented] ) // UUID layout variants. @@ -68,8 +82,8 @@ const ( ) // Timestamp is the count of 100-nanosecond intervals since 00:00:00.00, -// 15 October 1582 within a V1 UUID. This type has no meaning for V2-V5 -// UUIDs since they don't have an embedded timestamp. +// 15 October 1582 within a V1 UUID. This type has no meaning for other +// UUID versions since they don't have an embedded timestamp. type Timestamp uint64 const _100nsPerSecond = 10000000 @@ -78,6 +92,7 @@ const _100nsPerSecond = 10000000 func (t Timestamp) Time() (time.Time, error) { secs := uint64(t) / _100nsPerSecond nsecs := 100 * (uint64(t) % _100nsPerSecond) + return time.Unix(int64(secs)-(epochStart/_100nsPerSecond), int64(nsecs)), nil } @@ -88,17 +103,33 @@ func TimestampFromV1(u UUID) (Timestamp, error) { err := fmt.Errorf("uuid: %s is version %d, not version 1", u, u.Version()) return 0, err } + low := binary.BigEndian.Uint32(u[0:4]) mid := binary.BigEndian.Uint16(u[4:6]) hi := binary.BigEndian.Uint16(u[6:8]) & 0xfff + return Timestamp(uint64(low) + (uint64(mid) << 32) + (uint64(hi) << 48)), nil } -// String parse helpers. -var ( - urnPrefix = []byte("urn:uuid:") - byteGroups = []int{8, 4, 4, 4, 12} -) +// TimestampFromV6 returns the Timestamp embedded within a V6 UUID. This +// function returns an error if the UUID is any version other than 6. +// +// This is implemented based on revision 03 of the Peabody UUID draft, and may +// be subject to change pending further revisions. Until the final specification +// revision is finished, changes required to implement updates to the spec will +// not be considered a breaking change. They will happen as a minor version +// releases until the spec is final. +func TimestampFromV6(u UUID) (Timestamp, error) { + if u.Version() != 6 { + return 0, fmt.Errorf("uuid: %s is version %d, not version 6", u, u.Version()) + } + + hi := binary.BigEndian.Uint32(u[0:4]) + mid := binary.BigEndian.Uint16(u[4:6]) + low := binary.BigEndian.Uint16(u[6:8]) & 0xfff + + return Timestamp(uint64(low) + (uint64(mid) << 12) + (uint64(hi) << 28)), nil +} // Nil is the nil UUID, as specified in RFC-4122, that has all 128 bits set to // zero. @@ -112,6 +143,11 @@ var ( NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) ) +// IsNil returns if the UUID is equal to the nil UUID +func (u UUID) IsNil() bool { + return u == Nil +} + // Version returns the algorithm version used to generate the UUID. func (u UUID) Version() byte { return u[6] >> 4 @@ -138,22 +174,81 @@ func (u UUID) Bytes() []byte { return u[:] } +// encodeCanonical encodes the canonical RFC-4122 form of UUID u into the +// first 36 bytes dst. +func encodeCanonical(dst []byte, u UUID) { + const hextable = "0123456789abcdef" + dst[8] = '-' + dst[13] = '-' + dst[18] = '-' + dst[23] = '-' + for i, x := range [16]byte{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34, + } { + c := u[i] + dst[x] = hextable[c>>4] + dst[x+1] = hextable[c&0x0f] + } +} + // String returns a canonical RFC-4122 string representation of the UUID: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. func (u UUID) String() string { - buf := make([]byte, 36) - - hex.Encode(buf[0:8], u[0:4]) - buf[8] = '-' - hex.Encode(buf[9:13], u[4:6]) - buf[13] = '-' - hex.Encode(buf[14:18], u[6:8]) - buf[18] = '-' - hex.Encode(buf[19:23], u[8:10]) - buf[23] = '-' - hex.Encode(buf[24:], u[10:]) - - return string(buf) + var buf [36]byte + encodeCanonical(buf[:], u) + return string(buf[:]) +} + +// Format implements fmt.Formatter for UUID values. +// +// The behavior is as follows: +// The 'x' and 'X' verbs output only the hex digits of the UUID, using a-f for 'x' and A-F for 'X'. +// The 'v', '+v', 's' and 'q' verbs return the canonical RFC-4122 string representation. +// The 'S' verb returns the RFC-4122 format, but with capital hex digits. +// The '#v' verb returns the "Go syntax" representation, which is a 16 byte array initializer. +// All other verbs not handled directly by the fmt package (like '%p') are unsupported and will return +// "%!verb(uuid.UUID=value)" as recommended by the fmt package. +func (u UUID) Format(f fmt.State, c rune) { + if c == 'v' && f.Flag('#') { + fmt.Fprintf(f, "%#v", [Size]byte(u)) + return + } + switch c { + case 'x', 'X': + b := make([]byte, 32) + hex.Encode(b, u[:]) + if c == 'X' { + toUpperHex(b) + } + _, _ = f.Write(b) + case 'v', 's', 'S': + b, _ := u.MarshalText() + if c == 'S' { + toUpperHex(b) + } + _, _ = f.Write(b) + case 'q': + b := make([]byte, 38) + b[0] = '"' + encodeCanonical(b[1:], u) + b[37] = '"' + _, _ = f.Write(b) + default: + // invalid/unsupported format verb + fmt.Fprintf(f, "%%!%c(uuid.UUID=%s)", c, u.String()) + } +} + +func toUpperHex(b []byte) { + for i, c := range b { + if 'a' <= c && c <= 'f' { + b[i] = c - ('a' - 'A') + } + } } // SetVersion sets the version bits. @@ -180,7 +275,8 @@ func (u *UUID) SetVariant(v byte) { // Must is a helper that wraps a call to a function returning (UUID, error) // and panics if the error is non-nil. It is intended for use in variable // initializations such as -// var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000")) +// +// var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000")) func Must(u UUID, err error) UUID { if err != nil { panic(err) diff --git a/vendor/github.com/jcmturner/gokrb5/v8/client/TGSExchange.go b/vendor/github.com/jcmturner/gokrb5/v8/client/TGSExchange.go index e4571ce8..fd01342e 100644 --- a/vendor/github.com/jcmturner/gokrb5/v8/client/TGSExchange.go +++ b/vendor/github.com/jcmturner/gokrb5/v8/client/TGSExchange.go @@ -89,7 +89,12 @@ func (cl *Client) GetServiceTicket(spn string) (messages.Ticket, types.Encryptio return tkt, skey, nil } princ := types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, spn) - realm := cl.Config.ResolveRealm(princ.NameString[len(princ.NameString)-1]) + realm := cl.spnRealm(princ) + + // if we don't know the SPN's realm, ask the client realm's KDC + if realm == "" { + realm = cl.Credentials.Realm() + } tgt, skey, err := cl.sessionTGT(realm) if err != nil { diff --git a/vendor/github.com/jcmturner/gokrb5/v8/client/network.go b/vendor/github.com/jcmturner/gokrb5/v8/client/network.go index 634f015c..688ad3a1 100644 --- a/vendor/github.com/jcmturner/gokrb5/v8/client/network.go +++ b/vendor/github.com/jcmturner/gokrb5/v8/client/network.go @@ -2,7 +2,6 @@ package client import ( "encoding/binary" - "errors" "fmt" "io" "net" @@ -173,7 +172,7 @@ func dialSendTCP(kdcs map[int]string, b []byte) ([]byte, error) { } return rb, nil } - return nil, errors.New("error in getting a TCP connection to any of the KDCs") + return nil, fmt.Errorf("error sending to a KDC: %s", strings.Join(errs, "; ")) } // sendTCP sends bytes to connection over TCP. diff --git a/vendor/github.com/jcmturner/gokrb5/v8/client/session.go b/vendor/github.com/jcmturner/gokrb5/v8/client/session.go index f7654d0d..7e1e65cf 100644 --- a/vendor/github.com/jcmturner/gokrb5/v8/client/session.go +++ b/vendor/github.com/jcmturner/gokrb5/v8/client/session.go @@ -41,7 +41,9 @@ func (s *sessions) update(sess *session) { // Cancel the one in the cache and add this one. i.mux.Lock() defer i.mux.Unlock() - i.cancel <- true + if i.cancel != nil { + i.cancel <- true + } s.Entries[sess.realm] = sess return } diff --git a/vendor/github.com/jcmturner/gokrb5/v8/config/krb5conf.go b/vendor/github.com/jcmturner/gokrb5/v8/config/krb5conf.go index a7638433..f448c8a4 100644 --- a/vendor/github.com/jcmturner/gokrb5/v8/config/krb5conf.go +++ b/vendor/github.com/jcmturner/gokrb5/v8/config/krb5conf.go @@ -95,7 +95,7 @@ func newLibDefaults() LibDefaults { opts := asn1.BitString{} opts.Bytes, _ = hex.DecodeString("00000010") opts.BitLength = len(opts.Bytes) * 8 - return LibDefaults{ + l := LibDefaults{ CCacheType: 4, Clockskew: time.Duration(300) * time.Second, DefaultClientKeytabName: fmt.Sprintf("/usr/local/var/krb5/user/%s/client.keytab", uid), @@ -115,6 +115,10 @@ func newLibDefaults() LibDefaults { UDPPreferenceLimit: 1465, PreferredPreauthTypes: []int{17, 16, 15, 14}, } + l.DefaultTGSEnctypeIDs = parseETypes(l.DefaultTGSEnctypes, l.AllowWeakCrypto) + l.DefaultTktEnctypeIDs = parseETypes(l.DefaultTktEnctypes, l.AllowWeakCrypto) + l.PermittedEnctypeIDs = parseETypes(l.PermittedEnctypes, l.AllowWeakCrypto) + return l } // Parse the lines of the [libdefaults] section of the configuration into the LibDefaults struct. @@ -507,7 +511,7 @@ func (c *Config) ResolveRealm(domainName string) string { return r } } - return c.LibDefaults.DefaultRealm + return "" } // Load the KRB5 configuration from the specified file path. diff --git a/vendor/github.com/jcmturner/gokrb5/v8/credentials/ccache.go b/vendor/github.com/jcmturner/gokrb5/v8/credentials/ccache.go index c3b35c77..a021e414 100644 --- a/vendor/github.com/jcmturner/gokrb5/v8/credentials/ccache.go +++ b/vendor/github.com/jcmturner/gokrb5/v8/credentials/ccache.go @@ -4,7 +4,7 @@ import ( "bytes" "encoding/binary" "errors" - "io/ioutil" + "os" "strings" "time" "unsafe" @@ -63,7 +63,7 @@ type Credential struct { // LoadCCache loads a credential cache file into a CCache type. func LoadCCache(cpath string) (*CCache, error) { c := new(CCache) - b, err := ioutil.ReadFile(cpath) + b, err := os.ReadFile(cpath) if err != nil { return c, err } diff --git a/vendor/github.com/jcmturner/gokrb5/v8/keytab/keytab.go b/vendor/github.com/jcmturner/gokrb5/v8/keytab/keytab.go index 5c2e9d79..cd1b8b1e 100644 --- a/vendor/github.com/jcmturner/gokrb5/v8/keytab/keytab.go +++ b/vendor/github.com/jcmturner/gokrb5/v8/keytab/keytab.go @@ -8,7 +8,7 @@ import ( "errors" "fmt" "io" - "io/ioutil" + "os" "strings" "time" "unsafe" @@ -93,7 +93,7 @@ func (kt *Keytab) GetEncryptionKey(princName types.PrincipalName, realm string, } } if len(key.KeyValue) < 1 { - return key, 0, fmt.Errorf("matching key not found in keytab. Looking for %v realm: %v kvno: %v etype: %v", princName.NameString, realm, kvno, etype) + return key, 0, fmt.Errorf("matching key not found in keytab. Looking for %q realm: %v kvno: %v etype: %v", princName.PrincipalNameString(), realm, kvno, etype) } return key, kv, nil } @@ -170,7 +170,7 @@ func newPrincipal() principal { // Load a Keytab file into a Keytab type. func Load(ktPath string) (*Keytab, error) { kt := new(Keytab) - b, err := ioutil.ReadFile(ktPath) + b, err := os.ReadFile(ktPath) if err != nil { return kt, err } diff --git a/vendor/github.com/jcmturner/gokrb5/v8/spnego/http.go b/vendor/github.com/jcmturner/gokrb5/v8/spnego/http.go index 53fe1a3d..6bc2c0ed 100644 --- a/vendor/github.com/jcmturner/gokrb5/v8/spnego/http.go +++ b/vendor/github.com/jcmturner/gokrb5/v8/spnego/http.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net" "net/http" "net/http/cookiejar" @@ -100,7 +99,7 @@ func (c *Client) Do(req *http.Request) (resp *http.Response, err error) { } if req.Body != nil { // Refresh the body reader so the body can be sent again - e.reqTarget.Body = ioutil.NopCloser(&body) + e.reqTarget.Body = io.NopCloser(&body) } return c.Do(e.reqTarget) } @@ -114,9 +113,9 @@ func (c *Client) Do(req *http.Request) (resp *http.Response, err error) { } if req.Body != nil { // Refresh the body reader so the body can be sent again - req.Body = ioutil.NopCloser(&body) + req.Body = io.NopCloser(&body) } - io.Copy(ioutil.Discard, resp.Body) + io.Copy(io.Discard, resp.Body) resp.Body.Close() return c.Do(req) } @@ -175,18 +174,18 @@ func setRequestSPN(r *http.Request) (types.PrincipalName, error) { return types.PrincipalName{}, err } name, err := net.LookupCNAME(h) - if err == nil { + if name != "" && err == nil { // Underlyng canonical name should be used for SPN - h = name + h = strings.ToLower(name) } h = strings.TrimSuffix(h, ".") r.Host = fmt.Sprintf("%s:%s", h, p) return types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, "HTTP/"+h), nil } name, err := net.LookupCNAME(h) - if err == nil { + if name != "" && err == nil { // Underlyng canonical name should be used for SPN - h = name + h = strings.ToLower(name) } h = strings.TrimSuffix(h, ".") r.Host = h diff --git a/vendor/github.com/jcmturner/gokrb5/v8/types/Authenticator.go b/vendor/github.com/jcmturner/gokrb5/v8/types/Authenticator.go index 1fdba78a..115a02ae 100644 --- a/vendor/github.com/jcmturner/gokrb5/v8/types/Authenticator.go +++ b/vendor/github.com/jcmturner/gokrb5/v8/types/Authenticator.go @@ -43,7 +43,7 @@ func NewAuthenticator(realm string, cname PrincipalName) (Authenticator, error) Cksum: Checksum{}, Cusec: int((t.UnixNano() / int64(time.Microsecond)) - (t.Unix() * 1e6)), CTime: t, - SeqNumber: seq.Int64(), + SeqNumber: seq.Int64() & 0x3fffffff, }, nil } @@ -53,7 +53,7 @@ func (a *Authenticator) GenerateSeqNumberAndSubKey(keyType int32, keySize int) e if err != nil { return err } - a.SeqNumber = seq.Int64() + a.SeqNumber = seq.Int64() & 0x3fffffff //Generate subkey value sk := make([]byte, keySize, keySize) rand.Read(sk) diff --git a/vendor/github.com/masterzen/winrm/.golangci.yml b/vendor/github.com/masterzen/winrm/.golangci.yml new file mode 100644 index 00000000..05f69e53 --- /dev/null +++ b/vendor/github.com/masterzen/winrm/.golangci.yml @@ -0,0 +1,44 @@ +run: + go: "1.21" + deadline: 10m + allow-parallel-runners: true +linters: + disable-all: true + enable: + # Default linters + - deadcode + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - structcheck + - typecheck + - unused + - varcheck + # Optional linters + - asciicheck + - bidichk + - bodyclose + - containedctx + - contextcheck + - durationcheck + - errchkjson + - errname + - errorlint + - exportloopref + - gosec + - misspell + - noctx + - nolintlint + - revive + - stylecheck + - whitespace +linters-settings: + revive: + ignore-generated-header: true + severity: error + staticcheck: + go: "1.21" + stylecheck: + go: "1.21" diff --git a/vendor/github.com/masterzen/winrm/.travis.yml b/vendor/github.com/masterzen/winrm/.travis.yml deleted file mode 100644 index 18c04d4c..00000000 --- a/vendor/github.com/masterzen/winrm/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -sudo: false -language: go -go: -- 1.7 -- 1.8 -- 1.9 -- '1.10' -install: -- make deps -- go build -o winrm-cli -before_install: -- go get github.com/axw/gocov/gocov -- go get github.com/mattn/goveralls -- go get golang.org/x/tools/cmd/cover -- go get github.com/modocache/gover -- go get gopkg.in/check.v1 -script: -- make ci -after_script: -- $HOME/gopath/bin/goveralls -service="travis-ci" -coverprofile=profile.cov -repotoken $COVERALLS_TOKEN -matrix: - allow_failures: - - go: tip -env: - global: - secure: "GTrEtbp3sq14Jjz34pgIO0/Zv19YaDOOUJay4qnzGxs527HkW7YdsWENz0/yGHet+0jMOPatfP3uLaQHVFCNFZLfMYVmD5apMl7hPFrCaDZVvI9+ZwngIZ8gHzcf2Q+L6LxUT523ypjmRR+T1qYfbOy4UXlfGCyHFBbRB7AbMDk=" diff --git a/vendor/github.com/masterzen/winrm/README.md b/vendor/github.com/masterzen/winrm/README.md index ac4aff97..f3f13ee5 100644 --- a/vendor/github.com/masterzen/winrm/README.md +++ b/vendor/github.com/masterzen/winrm/README.md @@ -16,7 +16,7 @@ _Note_: this library doesn't support domain users (it doesn't support GSSAPI nor ## Getting Started -WinRM is available on Windows Server 2008 and up. This project natively supports basic authentication for local accounts, see the steps in the next section on how to prepare the remote Windows machine for this scenario. The authentication model is pluggable, see below for an example on using Negotiate/NTLM authentication (e.g. for connecting to vanilla Azure VMs). +WinRM is available on Windows Server 2008 and up. This project natively supports basic authentication for local accounts, see the steps in the next section on how to prepare the remote Windows machine for this scenario. The authentication model is pluggable, see below for an example on using Negotiate/NTLM authentication (e.g. for connecting to vanilla Azure VMs) or Kerberos authentication (using domain accounts). _Note_: This library only supports Golang 1.7+ @@ -37,10 +37,23 @@ __N.B.:__ The Windows Firewall needs to be running to run this command. See [Mic __N.B.:__ Do not disable Negotiate authentication as the `winrm` command itself uses this for internal authentication, and you risk getting a system where `winrm` doesn't work anymore. -__N.B.:__ The `MaxMemoryPerShellMB` option has no effects on some Windows 2008R2 systems because of a WinRM bug. Make sure to install the hotfix described [Microsoft Knowledge Base article #2842230](http://support.microsoft.com/kb/2842230) if you need to run commands that uses more than 150MB of memory. +__N.B.:__ The `MaxMemoryPerShellMB` option has no effects on some Windows 2008R2 systems because of a WinRM bug. Make sure to install the hotfix described [Microsoft Knowledge Base article #2842230](http://support.microsoft.com/kb/2842230) if you need to run commands that use more than 150MB of memory. For more information on WinRM, please refer to the online documentation at Microsoft's DevCenter. +### Preparing the remote Windows machine for kerberos authentication +This project supports domain users via kerberos authentication. The remote windows system must be prepared for winrm: + +On the remote host, a PowerShell prompt, using the __Run as Administrator__ option and paste in the following lines: + + winrm quickconfig + y + winrm set winrm/config/service '@{AllowUnencrypted="true"}' + winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}' + +All __N.B__ points of "Preparing the remote Windows machine for Basic authentication" also applies. + + ### Building the winrm go and executable You can build winrm from source: @@ -82,7 +95,9 @@ client, err := winrm.NewClient(endpoint, "Administrator", "secret") if err != nil { panic(err) } -client.Run("ipconfig /all", os.Stdout, os.Stderr) +ctx, cancel := context.WithCancel(context.Background()) +defer cancel() +client.RunWithContext(ctx, "ipconfig /all", os.Stdout, os.Stderr) ``` or @@ -100,7 +115,9 @@ if err != nil { panic(err) } -_, err := client.RunWithInput("ipconfig", os.Stdout, os.Stderr, os.Stdin) +ctx, cancel := context.WithCancel(context.Background()) +defer cancel() +_, err := client.RunWithContextWithInput(ctx, "ipconfig", os.Stdout, os.Stderr, os.Stdin) if err != nil { panic(err) } @@ -134,6 +151,47 @@ if err != nil { ``` +Passing a TransportDecorator also permit to use Kerberos authentication + +```go +package main +import ( + "os" + "fmt" + "github.com/masterzen/winrm" +) + +endpoint := winrm.NewEndpoint("srv-win", 5985, false, false, nil, nil, nil, 0) + +params := winrm.DefaultParameters +params.TransportDecorator = func() Transporter { + return &winrm.ClientKerberos{ + Username: "test", + Password: "s3cr3t", + Hostname: "srv-win", + Realm: "DOMAIN.LAN", + Port: 5985, + Proto: "http", + KrbConf: "/etc/krb5.conf", + SPN: fmt.Sprintf("HTTP/%s", hostname), + } +} + +client, err := NewClientWithParameters(endpoint, "test", "s3cr3t", params) +if err != nil { + panic(err) +} + +ctx, cancel := context.WithCancel(context.Background()) +defer cancel() +_, err := client.RunWithContextWithInput(ctx, "ipconfig", os.Stdout, os.Stderr, os.Stdin) +if err != nil { + panic(err) +} + +``` + + By passing a Dial in the Parameters struct it is possible to use different dialer (e.g. tunnel through SSH) ```go @@ -163,7 +221,9 @@ package main panic(err) } - _, err = client.RunWithInput("ipconfig", os.Stdout, os.Stderr, os.Stdin) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + _, err = client.RunWithContextWithInput(ctx, "ipconfig", os.Stdout, os.Stderr, os.Stdin) if err != nil { panic(err) } @@ -194,8 +254,10 @@ shell, err := client.CreateShell() if err != nil { panic(err) } +ctx, cancel := context.WithCancel(context.Background()) +defer cancel() var cmd *winrm.Command -cmd, err = shell.Execute("cmd.exe") +cmd, err = shell.ExecuteWithContext(ctx, "cmd.exe") if err != nil { panic(err) } @@ -210,37 +272,58 @@ shell.Close() For using HTTPS authentication with x 509 cert without checking the CA ```go - package main +package main - import ( - "github.com/masterzen/winrm" - "os" - "io/ioutil" - ) +import ( + "github.com/masterzen/winrm" + "log" + "os" +) - clientCert, err := ioutil.ReadFile("path/to/cert") - if err != nil { - panic(err) - } +func main() { + clientCert, err := os.ReadFile("/home/example/winrm_client_cert.pem") + if err != nil { + log.Fatalf("failed to read client certificate: %q", err) + } - clientKey, err := ioutil.ReadFile("path/to/key") - if err != nil { - panic(err) - } + clientKey, err := os.ReadFile("/home/example/winrm_client_key.pem") + if err != nil { + log.Fatalf("failed to read client key: %q", err) + } - winrm.DefaultParameters.TransportDecorator = func() winrm.Transporter { - // winrm https module - return &winrm.ClientAuthRequest{} - } + winrm.DefaultParameters.TransportDecorator = func() winrm.Transporter { + // winrm https module + return &winrm.ClientAuthRequest{} + } - endpoint := winrm.NewEndpoint(host, 5986, false, false, clientCert, clientKey, nil, 0) - client, err := winrm.NewClient(endpoint, "Administrator", "" - if err != nil { - panic(err) - } - client.Run("ipconfig /all", os.Stdout, os.Stderr) + endpoint := winrm.NewEndpoint( + "192.168.100.2", // host to connect to + 5986, // winrm port + true, // use TLS + true, // Allow insecure connection + nil, // CA certificate + clientCert, // Client Certificate + clientKey, // Client Key + 0, // Timeout + ) + client, err := winrm.NewClient(endpoint, "Administrator", "") + if err != nil { + log.Fatalf("failed to create client: %q", err) + } + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + _, err = client.RunWithContext(ctx, "whoami", os.Stdout, os.Stderr) + if err != nil { + log.Fatalf("failed to run command: %q", err) + } +} ``` +Note: canceling the `context.Context` passed as first argument to the various +functions of the API will not cancel the HTTP requests themselves, it will +rather cause a running command to be aborted on the remote machine via a call to +`command.Stop()`. + ## Developing on WinRM If you wish to work on `winrm` itself, you'll first need [Go](http://golang.org) diff --git a/vendor/github.com/masterzen/winrm/auth.go b/vendor/github.com/masterzen/winrm/auth.go index 0ae5d997..2ea0656e 100644 --- a/vendor/github.com/masterzen/winrm/auth.go +++ b/vendor/github.com/masterzen/winrm/auth.go @@ -3,7 +3,7 @@ package winrm import ( "crypto/tls" "fmt" - "io/ioutil" + "io" "net" "net/http" "strings" @@ -12,11 +12,13 @@ import ( "github.com/masterzen/winrm/soap" ) +// ClientAuthRequest ClientAuthRequest type ClientAuthRequest struct { transport http.RoundTripper - dial func(network, addr string) (net.Conn, error) + dial func(network, addr string) (net.Conn, error) } +// Transport Transport func (c *ClientAuthRequest) Transport(endpoint *Endpoint) error { cert, err := tls.X509KeyPair(endpoint.Cert, endpoint.Key) if err != nil { @@ -32,13 +34,15 @@ func (c *ClientAuthRequest) Transport(endpoint *Endpoint) error { dial = c.dial } + //nolint:gosec transport := &http.Transport{ Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{ + Renegotiation: tls.RenegotiateOnceAsClient, InsecureSkipVerify: endpoint.Insecure, Certificates: []tls.Certificate{cert}, }, - Dial: dial, + Dial: dial, ResponseHeaderTimeout: endpoint.Timeout, } @@ -58,10 +62,9 @@ func (c *ClientAuthRequest) Transport(endpoint *Endpoint) error { // parse func reads the response body and return it as a string func parse(response *http.Response) (string, error) { - - // if we recived the content we expected + // if we received the content we expected if strings.Contains(response.Header.Get("Content-Type"), "application/soap+xml") { - body, err := ioutil.ReadAll(response.Body) + body, err := io.ReadAll(response.Body) defer func() { // defer can modify the returned value before // it is actually passed to the calling statement @@ -70,7 +73,7 @@ func parse(response *http.Response) (string, error) { } }() if err != nil { - return "", fmt.Errorf("error while reading request body %s", err) + return "", fmt.Errorf("error while reading request body %w", err) } return string(body), nil @@ -79,12 +82,13 @@ func parse(response *http.Response) (string, error) { return "", fmt.Errorf("invalid content type") } +// Post Post func (c ClientAuthRequest) Post(client *Client, request *soap.SoapMessage) (string, error) { httpClient := &http.Client{Transport: c.transport} req, err := http.NewRequest("POST", client.url, strings.NewReader(request.String())) if err != nil { - return "", fmt.Errorf("impossible to create http request %s", err) + return "", fmt.Errorf("impossible to create http request %w", err) } req.Header.Set("Content-Type", soapXML+";charset=UTF-8") @@ -92,12 +96,12 @@ func (c ClientAuthRequest) Post(client *Client, request *soap.SoapMessage) (stri resp, err := httpClient.Do(req) if err != nil { - return "", fmt.Errorf("unknown error %s", err) + return "", fmt.Errorf("unknown error %w", err) } body, err := parse(resp) if err != nil { - return "", fmt.Errorf("http response error: %d - %s", resp.StatusCode, err.Error()) + return "", fmt.Errorf("http response error: %d - %w", resp.StatusCode, err) } // if we have different 200 http status code @@ -111,8 +115,9 @@ func (c ClientAuthRequest) Post(client *Client, request *soap.SoapMessage) (stri return body, err } +// NewClientAuthRequestWithDial NewClientAuthRequestWithDial func NewClientAuthRequestWithDial(dial func(network, addr string) (net.Conn, error)) *ClientAuthRequest { return &ClientAuthRequest{ - dial:dial, + dial: dial, } -} \ No newline at end of file +} diff --git a/vendor/github.com/masterzen/winrm/client.go b/vendor/github.com/masterzen/winrm/client.go index fb68c3ca..113ce0b8 100644 --- a/vendor/github.com/masterzen/winrm/client.go +++ b/vendor/github.com/masterzen/winrm/client.go @@ -2,9 +2,12 @@ package winrm import ( "bytes" + "context" "crypto/x509" + "errors" "fmt" "io" + "strings" "sync" "github.com/masterzen/winrm/soap" @@ -37,7 +40,6 @@ func NewClient(endpoint *Endpoint, user, password string) (*Client, error) { // NewClientWithParameters will create a new remote client on url, connecting with user and password // This function doesn't connect (connection happens only when CreateShell is called) func NewClientWithParameters(endpoint *Endpoint, user, password string, params *Parameters) (*Client, error) { - // alloc a new client client := &Client{ Parameters: *params, @@ -46,7 +48,7 @@ func NewClientWithParameters(endpoint *Endpoint, user, password string, params * url: endpoint.url(), useHTTPS: endpoint.HTTPS, // default transport - http: &clientRequest{ dial:params.Dial }, + http: &clientRequest{dial: params.Dial}, } // switch to other transport if provided @@ -56,7 +58,7 @@ func NewClientWithParameters(endpoint *Endpoint, user, password string, params * // set the transport to some endpoint configuration if err := client.http.Transport(endpoint); err != nil { - return nil, fmt.Errorf("Can't parse this key and certs: %s", err) + return nil, fmt.Errorf("can't parse this key and certs: %w", err) } return client, nil @@ -66,7 +68,7 @@ func readCACerts(certs []byte) (*x509.CertPool, error) { certPool := x509.NewCertPool() if !certPool.AppendCertsFromPEM(certs) { - return nil, fmt.Errorf("Unable to read certificates") + return nil, errors.New("unable to read certificates") } return certPool, nil @@ -89,7 +91,6 @@ func (c *Client) CreateShell() (*Shell, error) { } return c.NewShell(shellID), nil - } // NewShell will create a new WinRM Shell for the given shellID @@ -104,70 +105,80 @@ func (c *Client) sendRequest(request *soap.SoapMessage) (string, error) { // Run will run command on the the remote host, writing the process stdout and stderr to // the given writers. Note with this method it isn't possible to inject stdin. +// +// Deprecated: use RunWithContext() func (c *Client) Run(command string, stdout io.Writer, stderr io.Writer) (int, error) { - shell, err := c.CreateShell() - if err != nil { - return 1, err - } - defer shell.Close() - cmd, err := shell.Execute(command) - if err != nil { - return 1, err - } + return c.RunWithContext(context.Background(), command, stdout, stderr) +} - var wg sync.WaitGroup - wg.Add(2) +// RunWithContext will run command on the the remote host, writing the process stdout and stderr to +// the given writers. Note with this method it isn't possible to inject stdin. +// If the context is canceled, the remote command is canceled. +func (c *Client) RunWithContext(ctx context.Context, command string, stdout io.Writer, stderr io.Writer) (int, error) { + return c.RunWithContextWithInput(ctx, command, stdout, stderr, nil) +} - go func() { - defer wg.Done() - io.Copy(stdout, cmd.Stdout) - }() +// RunWithString will run command on the the remote host, returning the process stdout and stderr +// as strings, and using the input stdin string as the process input +// +// Deprecated: use RunWithContextWithString() +func (c *Client) RunWithString(command string, stdin string) (string, string, int, error) { + return c.RunWithContextWithString(context.Background(), command, stdin) +} - go func() { - defer wg.Done() - io.Copy(stderr, cmd.Stderr) - }() +// RunWithContextWithString will run command on the the remote host, returning the process stdout and stderr +// as strings, and using the input stdin string as the process input +// If the context is canceled, the remote command is canceled. +func (c *Client) RunWithContextWithString(ctx context.Context, command string, stdin string) (string, string, int, error) { + var outWriter, errWriter bytes.Buffer + exitCode, err := c.RunWithContextWithInput(ctx, command, &outWriter, &errWriter, strings.NewReader(stdin)) + return outWriter.String(), errWriter.String(), exitCode, err +} - cmd.Wait() - wg.Wait() +// RunCmdWithContext will run command on the the remote host, returning the process stdout and stderr +// as strings +// If the context is canceled, the remote command is canceled. +func (c *Client) RunCmdWithContext(ctx context.Context, command string) (string, string, int, error) { + var outWriter, errWriter bytes.Buffer + exitCode, err := c.RunWithContextWithInput(ctx, command, &outWriter, &errWriter, nil) + return outWriter.String(), errWriter.String(), exitCode, err +} - return cmd.ExitCode(), cmd.err +// RunPSWithString will basically wrap your code to execute commands in powershell.exe. Default RunWithString +// runs commands in cmd.exe +// +// Deprecated: use RunPSWithContextWithString() +func (c *Client) RunPSWithString(command string, stdin string) (string, string, int, error) { + return c.RunPSWithContextWithString(context.Background(), command, stdin) } -// RunWithString will run command on the the remote host, returning the process stdout and stderr -// as strings, and using the input stdin string as the process input -func (c *Client) RunWithString(command string, stdin string) (string, string, int, error) { - shell, err := c.CreateShell() - if err != nil { - return "", "", 1, err - } - defer shell.Close() +// RunPSWithContextWithString will basically wrap your code to execute commands in powershell.exe. Default RunWithString +// runs commands in cmd.exe +func (c *Client) RunPSWithContextWithString(ctx context.Context, command string, stdin string) (string, string, int, error) { + command = Powershell(command) - cmd, err := shell.Execute(command) - if err != nil { - return "", "", 1, err - } - if len(stdin) > 0 { - cmd.Stdin.Write([]byte(stdin)) + // Let's check if we actually created a command + if command == "" { + return "", "", 1, errors.New("cannot encode the given command") } - var outWriter, errWriter bytes.Buffer - var wg sync.WaitGroup - wg.Add(2) - go func() { - defer wg.Done() - io.Copy(&outWriter, cmd.Stdout) - }() + // Specify powershell.exe to run encoded command + return c.RunWithContextWithString(ctx, command, stdin) +} - go func() { - defer wg.Done() - io.Copy(&errWriter, cmd.Stderr) - }() +// RunPSWithContext will basically wrap your code to execute commands in powershell.exe. +// runs commands in cmd.exe +func (c *Client) RunPSWithContext(ctx context.Context, command string) (string, string, int, error) { + command = Powershell(command) - cmd.Wait() - wg.Wait() + // Let's check if we actually created a command + if command == "" { + return "", "", 1, errors.New("cannot encode the given command") + } - return outWriter.String(), errWriter.String(), cmd.ExitCode(), cmd.err + var outWriter, errWriter bytes.Buffer + exitCode, err := c.RunWithContextWithInput(ctx, command, &outWriter, &errWriter, nil) + return outWriter.String(), errWriter.String(), exitCode, err } // RunWithInput will run command on the the remote host, writing the process stdout and stderr to @@ -175,13 +186,27 @@ func (c *Client) RunWithString(command string, stdin string) (string, string, in // Warning stdin (not stdout/stderr) are bufferized, which means reading only one byte in stdin will // send a winrm http packet to the remote host. If stdin is a pipe, it might be better for // performance reasons to buffer it. -func (c Client) RunWithInput(command string, stdout, stderr io.Writer, stdin io.Reader) (int, error) { +// If stdin is nil, this is equivalent to c.Run() +// +// Deprecated: use RunWithContextWithInput() +func (c *Client) RunWithInput(command string, stdout, stderr io.Writer, stdin io.Reader) (int, error) { + return c.RunWithContextWithInput(context.Background(), command, stdout, stderr, stdin) +} + +// RunWithContextWithInput will run command on the the remote host, writing the process stdout and stderr to +// the given writers, and injecting the process stdin with the stdin reader. +// If the context is canceled, the command on the remote machine is canceled. +// Warning stdin (not stdout/stderr) are bufferized, which means reading only one byte in stdin will +// send a winrm http packet to the remote host. If stdin is a pipe, it might be better for +// performance reasons to buffer it. +// If stdin is nil, this is equivalent to c.RunWithContext() +func (c *Client) RunWithContextWithInput(ctx context.Context, command string, stdout, stderr io.Writer, stdin io.Reader) (int, error) { shell, err := c.CreateShell() if err != nil { return 1, err } defer shell.Close() - cmd, err := shell.Execute(command) + cmd, err := shell.ExecuteWithContext(ctx, command) if err != nil { return 1, err } @@ -190,21 +215,29 @@ func (c Client) RunWithInput(command string, stdout, stderr io.Writer, stdin io. wg.Add(3) go func() { - defer wg.Done() - io.Copy(cmd.Stdin, stdin) + defer func() { + wg.Done() + }() + if stdin == nil { + return + } + defer func() { + cmd.Stdin.Close() + }() + _, _ = io.Copy(cmd.Stdin, stdin) }() go func() { defer wg.Done() - io.Copy(stdout, cmd.Stdout) + _, _ = io.Copy(stdout, cmd.Stdout) }() go func() { defer wg.Done() - io.Copy(stderr, cmd.Stderr) + _, _ = io.Copy(stderr, cmd.Stderr) }() cmd.Wait() wg.Wait() + cmd.Close() return cmd.ExitCode(), cmd.err - } diff --git a/vendor/github.com/masterzen/winrm/command.go b/vendor/github.com/masterzen/winrm/command.go index 4d2372a5..f7c35514 100644 --- a/vendor/github.com/masterzen/winrm/command.go +++ b/vendor/github.com/masterzen/winrm/command.go @@ -2,14 +2,18 @@ package winrm import ( "bytes" + "context" "errors" "io" + "net/url" "strings" + "sync" ) type commandWriter struct { *Command - eof bool + mutex sync.Mutex + eof bool } type commandReader struct { @@ -26,7 +30,6 @@ type Command struct { shell *Shell id string exitCode int - finished bool err error Stdin *commandWriter @@ -37,7 +40,7 @@ type Command struct { cancel chan struct{} } -func newCommand(shell *Shell, ids string) *Command { +func newCommand(ctx context.Context, shell *Shell, ids string) *Command { command := &Command{ shell: shell, client: shell.client, @@ -55,7 +58,7 @@ func newCommand(shell *Shell, ids string) *Command { } command.Stderr = newCommandReader("stderr", command) - go fetchOutput(command) + go fetchOutput(ctx, command) return command } @@ -70,12 +73,21 @@ func newCommandReader(stream string, command *Command) *commandReader { } } -func fetchOutput(command *Command) { +func fetchOutput(ctx context.Context, command *Command) { + ctxDone := ctx.Done() for { select { case <-command.cancel: + _, _ = command.slurpAllOutput() + err := errors.New("canceled") + command.Stderr.write.CloseWithError(err) + command.Stdout.write.CloseWithError(err) close(command.done) return + case <-ctxDone: + command.err = ctx.Err() + ctxDone = nil + command.Close() default: finished, err := command.slurpAllOutput() if finished { @@ -131,6 +143,11 @@ func (c *Command) slurpAllOutput() (bool, error) { response, err := c.client.sendRequest(request) if err != nil { + var errWithTimeout *url.Error + if errors.As(err, &errWithTimeout) && errWithTimeout.Timeout() { + // Operation timeout because the server didn't respond in time + return false, err + } if strings.Contains(err.Error(), "OperationTimeout") { // Operation timeout because there was no command output return false, err @@ -153,26 +170,26 @@ func (c *Command) slurpAllOutput() (bool, error) { return true, err } if stdout.Len() > 0 { - c.Stdout.write.Write(stdout.Bytes()) + _, _ = c.Stdout.write.Write(stdout.Bytes()) } if stderr.Len() > 0 { - c.Stderr.write.Write(stderr.Bytes()) + _, _ = c.Stderr.write.Write(stderr.Bytes()) } if finished { c.exitCode = exitCode - c.Stderr.write.Close() - c.Stdout.write.Close() + _ = c.Stderr.write.Close() + _ = c.Stdout.write.Close() } return finished, nil } -func (c *Command) sendInput(data []byte) error { +func (c *Command) sendInput(data []byte, eof bool) error { if err := c.check(); err != nil { return err } - request := NewSendInputRequest(c.client.url, c.shell.id, c.id, data, &c.client.Parameters) + request := NewSendInputRequest(c.client.url, c.shell.id, c.id, data, eof, &c.client.Parameters) defer request.Free() _, err := c.client.sendRequest(request) @@ -191,30 +208,44 @@ func (c *Command) Wait() { } // Write data to this Pipe -// commandWriter implements io.Writer interface +// commandWriter implements io.Writer and io.Closer interface func (w *commandWriter) Write(data []byte) (int, error) { + w.mutex.Lock() + defer w.mutex.Unlock() + + if w.eof { + return 0, io.ErrClosedPipe + } var ( written int err error ) - + origLen := len(data) for len(data) > 0 { - if w.eof { - return written, io.EOF - } // never send more data than our EnvelopeSize. n := min(w.client.Parameters.EnvelopeSize-1000, len(data)) - if err := w.sendInput(data[:n]); err != nil { + if err := w.sendInput(data[:n], false); err != nil { break } data = data[n:] written += n } + // signal that we couldn't write all data + if err == nil && written < origLen { + err = io.ErrShortWrite + } + return written, err } +// Write data to this Pipe and mark EOF +func (w *commandWriter) WriteClose(data []byte) (int, error) { + w.eof = true + return w.Write(data) +} + func min(a int, b int) int { if a < b { return a @@ -225,14 +256,20 @@ func min(a int, b int) int { // Close method wrapper // commandWriter implements io.Closer interface func (w *commandWriter) Close() error { + w.mutex.Lock() + defer w.mutex.Unlock() + + if w.eof { + return io.ErrClosedPipe + } w.eof = true - return w.Close() + return w.sendInput(nil, w.eof) } // Read data from this Pipe func (r *commandReader) Read(buf []byte) (int, error) { n, err := r.read.Read(buf) - if err != nil && err != io.EOF { + if err != nil && errors.Is(err, io.EOF) { return 0, err } return n, err diff --git a/vendor/github.com/masterzen/winrm/encryption.go b/vendor/github.com/masterzen/winrm/encryption.go new file mode 100644 index 00000000..d492afa3 --- /dev/null +++ b/vendor/github.com/masterzen/winrm/encryption.go @@ -0,0 +1,438 @@ +package winrm + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "strings" + + "github.com/bodgit/ntlmssp" + ntlmhttp "github.com/bodgit/ntlmssp/http" + "github.com/masterzen/winrm/soap" +) + +type Encryption struct { + ntlm *ClientNTLM + protocol string + protocolString []byte + httpClient *http.Client + ntlmClient *ntlmssp.Client + ntlmhttp *ntlmhttp.Client +} + +const ( + sixTenKB = 16384 + mimeBoundary = "--Encrypted Boundary" + defaultCipher = "RC4-HMAC-NTLM" + boundaryLength = len(mimeBoundary) +) + +/* +Encrypted Message Types +When using Encryption, there are three options available + + 1. Negotiate/SPNEGO + + 2. Kerberos + + 3. CredSSP + + protocol: The protocol string used for the particular auth protocol + + The auth protocol used, will determine the wrapping and unwrapping method plus + the protocol string to use. Currently only NTLM is supported + + based on the python code from https://pypi.org/project/pywinrm/ + + see https://github.com/diyan/pywinrm/blob/master/winrm/encryption.py + + uses the most excellent NTLM library from https://github.com/bodgit/ntlmssp +*/ +func NewEncryption(protocol string) (*Encryption, error) { + encryption := &Encryption{ + ntlm: &ClientNTLM{}, + protocol: protocol, + } + + switch protocol { + case "ntlm": + encryption.protocolString = []byte("application/HTTP-SPNEGO-session-encrypted") + return encryption, nil + /* credssp and kerberos is currently unimplemented, leave holder for future to keep in sync with python implementation + case "credssp": + encryption.protocolString = []byte("application/HTTP-CredSSP-session-encrypted") + case "kerberos": // kerberos is currently unimplemented, leave holder for future to keep in sync with python implementation + encryption.protocolString = []byte("application/HTTP-SPNEGO-session-encrypted") + */ + } + + return nil, fmt.Errorf("Encryption for protocol '%s' not supported", protocol) +} + +func (e *Encryption) Transport(endpoint *Endpoint) error { + e.httpClient = &http.Client{} + return e.ntlm.Transport(endpoint) +} + +func (e *Encryption) Post(client *Client, message *soap.SoapMessage) (string, error) { + var userName, domain string + if strings.Contains(client.username, "@") { + parts := strings.Split(client.username, "@") + domain = parts[1] + userName = parts[0] + } else if strings.Contains(client.username, "\\") { + parts := strings.Split(client.username, "\\") + domain = parts[0] + userName = parts[1] + } else { + userName = client.username + } + + e.ntlmClient, _ = ntlmssp.NewClient(ntlmssp.SetUserInfo(userName, client.password), ntlmssp.SetDomain(domain), ntlmssp.SetVersion(ntlmssp.DefaultVersion())) + e.ntlmhttp, _ = ntlmhttp.NewClient(e.httpClient, e.ntlmClient) + + var err error + if err = e.PrepareRequest(client, client.url); err == nil { + return e.PrepareEncryptedRequest(client, client.url, []byte(message.String())) + } else { + return e.ntlm.Post(client, message) + } +} + +func (e *Encryption) PrepareRequest(client *Client, endpoint string) error { + req, err := http.NewRequest("POST", endpoint, nil) + if err != nil { + return err + } + + req.Header.Set("User-Agent", "WinRM client") + req.Header.Set("Content-Length", "0") + req.Header.Set("Content-Type", "application/soap+xml;charset=UTF-8") + req.Header.Set("Connection", "Keep-Alive") + + resp, err := e.ntlmhttp.Do(req) + if err != nil { + return fmt.Errorf("unknown error %w", err) + } + + if _, err := io.ReadAll(resp.Body); err != nil { + return fmt.Errorf("read response body: %w", err) + } + + if err := resp.Body.Close(); err != nil { + return fmt.Errorf("close request body: %w", err) + } + + if resp.StatusCode != 200 { + return fmt.Errorf("http error %d", resp.StatusCode) + } + + return nil +} + +/* +Creates a prepared request to send to the server with an encrypted message +and correct headers + +:param endpoint: The endpoint/server to prepare requests to +:param message: The unencrypted message to send to the server +:return: A prepared request that has an decrypted message +*/ +func (e *Encryption) PrepareEncryptedRequest(client *Client, endpoint string, message []byte) (string, error) { + url, err := url.Parse(endpoint) + if err != nil { + return "", err + } + host := strings.Split(url.Hostname(), ":")[0] + + var content_type string + var encrypted_message []byte + + if e.protocol == "credssp" && len(message) > sixTenKB { + content_type = "multipart/x-multi-encrypted" + encrypted_message = []byte{} + message_chunks := [][]byte{} + for i := 0; i < len(message); i += sixTenKB { + message_chunks = append(message_chunks, message[i:i+sixTenKB]) + } + for _, message_chunk := range message_chunks { + encrypted_chunk := e.encryptMessage(message_chunk, host) + encrypted_message = append(encrypted_message, encrypted_chunk...) + } + } else { + content_type = "multipart/encrypted" + encrypted_message = e.encryptMessage(message, host) + } + + encrypted_message = append(encrypted_message, []byte(mimeBoundary)...) + encrypted_message = append(encrypted_message, []byte("--\r\n")...) + + req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(encrypted_message)) + if err != nil { + return "", err + } + + req.Header.Set("User-Agent", "WinRM client") + req.Header.Set("Connection", "Keep-Alive") + req.Header.Set("Content-Length", fmt.Sprintf("%d", len(encrypted_message))) + req.Header.Set("Content-Type", fmt.Sprintf(`%s;protocol="%s";boundary="Encrypted Boundary"`, content_type, e.protocolString)) + + resp, err := e.ntlmhttp.Do(req) + if err != nil { + return "", fmt.Errorf("unknown error %w", err) + } + + body, err := e.ParseEncryptedResponse(resp) + + return string(body), err +} + +/* +Takes in the encrypted response from the server and decrypts it + +:param response: The response that needs to be decrytped +:return: The unencrypted message from the server +*/ +func (e *Encryption) ParseEncryptedResponse(response *http.Response) ([]byte, error) { + contentType := response.Header.Get("Content-Type") + if strings.Contains(contentType, fmt.Sprintf(`protocol="%s"`, e.protocolString)) { + return e.decryptResponse(response, response.Request.URL.Hostname()) + } + body, err := io.ReadAll(response.Body) + response.Body.Close() + if err != nil { + return nil, err + } + return body, nil +} + +func (e *Encryption) encryptMessage(message []byte, host string) []byte { + encryptedStream, _ := e.buildMessage(message, host) + + messagePayload := bytes.Join([][]byte{ + []byte(mimeBoundary), + []byte("\r\n"), + []byte(fmt.Sprintf("\tContent-Type: %s\r\n", string(e.protocolString))), + []byte(fmt.Sprintf("\tOriginalContent: type=application/soap+xml;charset=UTF-8;Length=%d\r\n", len(message))), + []byte(mimeBoundary), + []byte("\r\n"), + []byte("\tContent-Type: application/octet-stream\r\n"), + encryptedStream, + }, []byte{}) + + return messagePayload +} + +func deleteEmpty(b [][]byte) [][]byte { + var r [][]byte + for _, by := range b { + if len(by) != 0 { + r = append(r, by) + } + } + return r +} + +// tried using pkg.go.dev/mime/multipart here but parsing fails with with +// because in the header we have "\tContent-Type: application/HTTP-SPNEGO-session-encrypted\r\n" +// on call to textproto.ReadMIMEHeader +// because of "The first line cannot start with a leading space." +func (e *Encryption) decryptResponse(response *http.Response, host string) ([]byte, error) { + body, _ := io.ReadAll(response.Body) + parts := deleteEmpty(bytes.Split(body, []byte(fmt.Sprintf("%s\r\n", mimeBoundary)))) + var message []byte + + for i := 0; i < len(parts); i += 2 { + header := parts[i] + payload := parts[i+1] + + expectedLengthStr := bytes.SplitAfter(header, []byte("Length="))[1] + expectedLength, err := strconv.Atoi(string(bytes.TrimSpace(expectedLengthStr))) + if err != nil { + return nil, err + } + + // remove the end MIME block if it exists + if bytes.HasSuffix(payload, []byte(fmt.Sprintf("%s--\r\n", mimeBoundary))) { + payload = payload[:len(payload)-boundaryLength-4] + } + encryptedData := bytes.ReplaceAll(payload, []byte("\tContent-Type: application/octet-stream\r\n"), []byte{}) + decryptedMessage, err := e.decryptMessage(encryptedData, host) + if err != nil { + return nil, err + } + + actualLength := int(len(decryptedMessage)) + if actualLength != expectedLength { + return nil, errors.New("encrypted length from server does not match the expected size, message has been tampered with") + } + + message = append(message, decryptedMessage...) + } + + return message, nil +} + +func (e *Encryption) decryptMessage(encryptedData []byte, host string) ([]byte, error) { + switch e.protocol { + case "ntlm": + return e.decryptNtlmMessage(encryptedData, host) + /* credssp and kerberos is currently unimplemented, leave holder for future to keep in sync with python implementation + case "credssp": + return e.decryptCredsspMessage(encryptedData, host) + case "kerberos": + return e.decryptKerberosMessage(encryptedData, host) + */ + default: + return nil, errors.New("Encryption for protocol " + e.protocol + " not supported") + } +} + +func (e *Encryption) decryptNtlmMessage(encryptedData []byte, host string) ([]byte, error) { + signatureLength := int(binary.LittleEndian.Uint32(encryptedData[:4])) + signature := encryptedData[4 : signatureLength+4] + encryptedMessage := encryptedData[signatureLength+4:] + + message, err := e.ntlmClient.SecuritySession().Unwrap(encryptedMessage, signature) + if err != nil { + return nil, err + } + return message, nil +} + +/* credssp and kerberos is currently unimplemented, leave holder for future to keep in sync with python implementation +func (e *Encryption) decryptCredsspMessage(encryptedData []byte, host string) ([]byte, error) { + // // TODO + // encryptedMessage := encryptedData[4:] + + // credsspContext, ok := e.session.Auth.Contexts()[host] + // if !ok { + // return nil, fmt.Errorf("credssp context not found for host: %s", host) + // } + + // message, err := credsspContext.Unwrap(encryptedMessage) + // if err != nil { + // return nil, err + // } + // return message, nil +} + +func (enc *Encryption) decryptKerberosMessage(encryptedData []byte, host string) ([]byte, error) { + // //TODO + // signatureLength := binary.LittleEndian.Uint32(encryptedData[0:4]) + // signature := encryptedData[4 : 4+signatureLength] + // encryptedMessage := encryptedData[4+signatureLength:] + + // message, err := enc.session.Auth.UnwrapWinrm(host, encryptedMessage, signature) + // if err != nil { + // return nil, err + // } + + // return message, nil +} +*/ + +func (e *Encryption) buildMessage(encryptedData []byte, host string) ([]byte, error) { + switch e.protocol { + case "ntlm": + return e.buildNTLMMessage(encryptedData, host) + /* credssp and kerberos is currently unimplemented, leave holder for future to keep in sync with python implementation + case "credssp": + return e.buildCredSSPMessage(encryptedData, host) + case "kerberos": + return e.buildKerberosMessage(encryptedData, host) + */ + default: + return nil, errors.New("Encryption for protocol " + e.protocol + " not supported") + } +} + +func (enc *Encryption) buildNTLMMessage(message []byte, host string) ([]byte, error) { + if enc.ntlmClient.SecuritySession() == nil { + return nil, nil + } + sealedMessage, signature, err := enc.ntlmClient.SecuritySession().Wrap(message) + if err != nil { + return nil, err + } + + buf := new(bytes.Buffer) + if err = binary.Write(buf, binary.LittleEndian, uint32(len(signature))); err != nil { + return nil, err + } + + buf.Write(signature) + buf.Write(sealedMessage) + + return buf.Bytes(), nil +} + +/* credssp and kerberos is currently unimplemented, leave holder for future to keep in sync with python implementation +func (e *Encryption) buildCredSSPMessage(message []byte, host string) ([]byte, error) { + // //TODO + // context := e.session.Auth.Contexts[host] + // sealedMessage := context.Wrap(message) + + // cipherNegotiated := context.TLSConnection.ConnectionState().CipherSuite.Name + // trailerLength := e.getCredSSPTrailerLength(len(message), cipherNegotiated) + + // trailer := make([]byte, 4) + // binary.LittleEndian.PutUint32(trailer, uint32(trailerLength)) + + // return append(trailer, sealedMessage...), nil +} + +func (e *Encryption) buildKerberosMessage(message []byte, host string) ([]byte, error) { + // //TODO + // sealedMessage, signature := e.session.Auth.WrapWinrm(host, message) + + // signatureLength := make([]byte, 4) + // binary.LittleEndian.PutUint32(signatureLength, uint32(len(signature))) + + // return append(append(signatureLength, signature...), sealedMessage...), nil +} + +func (e *Encryption) getCredSSPTrailerLength(messageLength int, cipherSuite string) int { + var trailerLength int + + if match, _ := regexp.MatchString("^.*-GCM-[\\w\\d]*$", cipherSuite); match { + trailerLength = 16 + } else { + hashAlgorithm := cipherSuite[strings.LastIndex(cipherSuite, "-")+1:] + var hashLength int + + if hashAlgorithm == "MD5" { + hashLength = 16 + } else if hashAlgorithm == "SHA" { + hashLength = 20 + } else if hashAlgorithm == "SHA256" { + hashLength = 32 + } else if hashAlgorithm == "SHA384" { + hashLength = 48 + } else { + hashLength = 0 + } + + prePadLength := messageLength + hashLength + paddingLength := 0 + + if strings.Contains(cipherSuite, "RC4") { + paddingLength = 0 + } else if strings.Contains(cipherSuite, "DES") || strings.Contains(cipherSuite, "3DES") { + paddingLength = 8 - (prePadLength % 8) + + } else { + // AES is a 128 bit block cipher + paddingLength = 16 - (prePadLength % 16) + } + + trailerLength = (prePadLength + paddingLength) - messageLength + } + return trailerLength +} +*/ diff --git a/vendor/github.com/masterzen/winrm/error.go b/vendor/github.com/masterzen/winrm/error.go index aaa50dcf..f2df4e68 100644 --- a/vendor/github.com/masterzen/winrm/error.go +++ b/vendor/github.com/masterzen/winrm/error.go @@ -1,13 +1,11 @@ package winrm -import "fmt" - -// errWinrm generic error struct -type errWinrm struct { +// winrmError generic error struct +type winrmError struct { message string } // ErrWinrm implements the Error type interface -func (e errWinrm) Error() string { - return fmt.Sprintf("%s", e.message) +func (e winrmError) Error() string { + return e.message } diff --git a/vendor/github.com/masterzen/winrm/http.go b/vendor/github.com/masterzen/winrm/http.go index 9767c214..31038cb1 100644 --- a/vendor/github.com/masterzen/winrm/http.go +++ b/vendor/github.com/masterzen/winrm/http.go @@ -3,9 +3,10 @@ package winrm import ( "crypto/tls" "fmt" - "io/ioutil" + "io" "net" "net/http" + "net/url" "strings" "time" @@ -16,10 +17,9 @@ var soapXML = "application/soap+xml" // body func reads the response body and return it as a string func body(response *http.Response) (string, error) { - - // if we recived the content we expected + // if we received the content we expected if strings.Contains(response.Header.Get("Content-Type"), "application/soap+xml") { - body, err := ioutil.ReadAll(response.Body) + body, err := io.ReadAll(response.Body) defer func() { // defer can modify the returned value before // it is actually passed to the calling statement @@ -28,7 +28,7 @@ func body(response *http.Response) (string, error) { } }() if err != nil { - return "", fmt.Errorf("error while reading request body %s", err) + return "", fmt.Errorf("error while reading request body %w", err) } return string(body), nil @@ -39,11 +39,11 @@ func body(response *http.Response) (string, error) { type clientRequest struct { transport http.RoundTripper - dial func(network, addr string) (net.Conn, error) + dial func(network, addr string) (net.Conn, error) + proxyfunc func(req *http.Request) (*url.URL, error) } func (c *clientRequest) Transport(endpoint *Endpoint) error { - dial := (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, @@ -53,13 +53,19 @@ func (c *clientRequest) Transport(endpoint *Endpoint) error { dial = c.dial } + proxyfunc := http.ProxyFromEnvironment + if c.proxyfunc != nil { + proxyfunc = c.proxyfunc + } + + //nolint:gosec transport := &http.Transport{ - Proxy: http.ProxyFromEnvironment, + Proxy: proxyfunc, TLSClientConfig: &tls.Config{ InsecureSkipVerify: endpoint.Insecure, ServerName: endpoint.TLSServerName, }, - Dial: dial, + Dial: dial, ResponseHeaderTimeout: endpoint.Timeout, } @@ -81,20 +87,21 @@ func (c *clientRequest) Transport(endpoint *Endpoint) error { func (c clientRequest) Post(client *Client, request *soap.SoapMessage) (string, error) { httpClient := &http.Client{Transport: c.transport} + //nolint:noctx req, err := http.NewRequest("POST", client.url, strings.NewReader(request.String())) if err != nil { - return "", fmt.Errorf("impossible to create http request %s", err) + return "", fmt.Errorf("impossible to create http request %w", err) } req.Header.Set("Content-Type", soapXML+";charset=UTF-8") req.SetBasicAuth(client.username, client.password) resp, err := httpClient.Do(req) if err != nil { - return "", fmt.Errorf("unknown error %s", err) + return "", fmt.Errorf("unknown error %w", err) } body, err := body(resp) if err != nil { - return "", fmt.Errorf("http response error: %d - %s", resp.StatusCode, err.Error()) + return "", fmt.Errorf("http response error: %d - %w", resp.StatusCode, err) } // if we have different 200 http status code @@ -108,9 +115,16 @@ func (c clientRequest) Post(client *Client, request *soap.SoapMessage) (string, return body, err } - +// NewClientWithDial NewClientWithDial func NewClientWithDial(dial func(network, addr string) (net.Conn, error)) *clientRequest { return &clientRequest{ - dial:dial, + dial: dial, } -} \ No newline at end of file +} + +// NewClientWithProxyFunc NewClientWithProxyFunc +func NewClientWithProxyFunc(proxyfunc func(req *http.Request) (*url.URL, error)) *clientRequest { + return &clientRequest{ + proxyfunc: proxyfunc, + } +} diff --git a/vendor/github.com/masterzen/winrm/kerberos.go b/vendor/github.com/masterzen/winrm/kerberos.go new file mode 100644 index 00000000..8a98e6e8 --- /dev/null +++ b/vendor/github.com/masterzen/winrm/kerberos.go @@ -0,0 +1,128 @@ +package winrm + +import ( + "fmt" + "io" + "net/http" + "os" + "strings" + + "github.com/masterzen/winrm/soap" + + "github.com/jcmturner/gokrb5/v8/client" + "github.com/jcmturner/gokrb5/v8/config" + "github.com/jcmturner/gokrb5/v8/credentials" + "github.com/jcmturner/gokrb5/v8/spnego" +) + +// Settings holds all the information necessary to configure the provider +type Settings struct { + WinRMUsername string + WinRMPassword string + WinRMHost string + WinRMPort int + WinRMProto string + WinRMInsecure bool + KrbRealm string + KrbConfig string + KrbSpn string + KrbCCache string + WinRMUseNTLM bool + WinRMPassCredentials bool +} + +type ClientKerberos struct { + clientRequest + Username string + Password string + Realm string + Hostname string + Port int + Proto string + SPN string + KrbConf string + KrbCCache string +} + +func NewClientKerberos(settings *Settings) *ClientKerberos { + return &ClientKerberos{ + Username: settings.WinRMUsername, + Password: settings.WinRMPassword, + Realm: settings.KrbRealm, + Hostname: settings.WinRMHost, + Port: settings.WinRMPort, + Proto: settings.WinRMProto, + KrbConf: settings.KrbConfig, + KrbCCache: settings.KrbCCache, + SPN: settings.KrbSpn, + } +} + +func (c *ClientKerberos) Transport(endpoint *Endpoint) error { + return c.clientRequest.Transport(endpoint) +} + +func (c *ClientKerberos) Post(clt *Client, request *soap.SoapMessage) (string, error) { + cfg, err := config.Load(c.KrbConf) + if err != nil { + return "", err + } + + // setup the kerberos client + var kerberosClient *client.Client + if len(c.KrbCCache) > 0 { + b, err := os.ReadFile(c.KrbCCache) + if err != nil { + return "", fmt.Errorf("unable to read ccache file %s: %w", c.KrbCCache, err) + } + + cc := new(credentials.CCache) + err = cc.Unmarshal(b) + if err != nil { + return "", fmt.Errorf("unable to parse ccache file %s: %w", c.KrbCCache, err) + } + kerberosClient, err = client.NewFromCCache(cc, cfg, client.DisablePAFXFAST(true)) + if err != nil { + return "", fmt.Errorf("unable to create kerberos client from ccache: %w", err) + } + } else { + kerberosClient = client.NewWithPassword(c.Username, c.Realm, c.Password, cfg, + client.DisablePAFXFAST(true), client.AssumePreAuthentication(true)) + } + + //create an http request + winrmURL := fmt.Sprintf("%s://%s:%d/wsman", c.Proto, c.Hostname, c.Port) + //nolint:noctx + winRMRequest, _ := http.NewRequest("POST", winrmURL, strings.NewReader(request.String())) + winRMRequest.Header.Add("Content-Type", "application/soap+xml;charset=UTF-8") + + err = spnego.SetSPNEGOHeader(kerberosClient, winRMRequest, c.SPN) + if err != nil { + return "", fmt.Errorf("unable to set SPNego Header: %w", err) + } + + httpClient := &http.Client{Transport: c.transport} + + resp, err := httpClient.Do(winRMRequest) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + var bodyMsg string + respBody, err := io.ReadAll(resp.Body) + if err != nil { + bodyMsg = fmt.Sprintf("Error retrieving the response's body: %s", err) + } else { + bodyMsg = fmt.Sprintf("Response body:\n%s", string(respBody)) + } + return "", fmt.Errorf("request returned: %d - %s. %s", resp.StatusCode, resp.Status, bodyMsg) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + return string(body), err +} diff --git a/vendor/github.com/masterzen/winrm/ntlm.go b/vendor/github.com/masterzen/winrm/ntlm.go index 08de8a52..219c54d1 100644 --- a/vendor/github.com/masterzen/winrm/ntlm.go +++ b/vendor/github.com/masterzen/winrm/ntlm.go @@ -1,9 +1,12 @@ package winrm import ( + "net" + "net/http" + "net/url" + "github.com/Azure/go-ntlmssp" "github.com/masterzen/winrm/soap" - "net" ) // ClientNTLM provides a transport via NTLMv2 @@ -13,7 +16,9 @@ type ClientNTLM struct { // Transport creates the wrapped NTLM transport func (c *ClientNTLM) Transport(endpoint *Endpoint) error { - c.clientRequest.Transport(endpoint) + if err := c.clientRequest.Transport(endpoint); err != nil { + return err + } c.clientRequest.transport = &ntlmssp.Negotiator{RoundTripper: c.clientRequest.transport} return nil } @@ -23,11 +28,20 @@ func (c ClientNTLM) Post(client *Client, request *soap.SoapMessage) (string, err return c.clientRequest.Post(client, request) } - +//NewClientNTLMWithDial NewClientNTLMWithDial func NewClientNTLMWithDial(dial func(network, addr string) (net.Conn, error)) *ClientNTLM { return &ClientNTLM{ clientRequest{ - dial:dial, + dial: dial, + }, + } +} + +//NewClientNTLMWithProxyFunc NewClientNTLMWithProxyFunc +func NewClientNTLMWithProxyFunc(proxyfunc func(req *http.Request) (*url.URL, error)) *ClientNTLM { + return &ClientNTLM{ + clientRequest{ + proxyfunc: proxyfunc, }, } -} \ No newline at end of file +} diff --git a/vendor/github.com/masterzen/winrm/powershell.go b/vendor/github.com/masterzen/winrm/powershell.go index 423f0381..fd196395 100644 --- a/vendor/github.com/masterzen/winrm/powershell.go +++ b/vendor/github.com/masterzen/winrm/powershell.go @@ -2,22 +2,26 @@ package winrm import ( "encoding/base64" - "fmt" + + "golang.org/x/text/encoding/unicode" ) // Powershell wraps a PowerShell script // and prepares it for execution by the winrm client func Powershell(psCmd string) string { - // 2 byte chars to make PowerShell happy - wideCmd := "" - for _, b := range []byte(psCmd) { - wideCmd += string(b) + "\x00" + // Disable unnecessary progress bars which considered as stderr. + psCmd = "$ProgressPreference = 'SilentlyContinue';" + psCmd + + // Encode string to UTF16-LE + encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder() + encoded, err := encoder.String(psCmd) + if err != nil { + return "" } - // Base64 encode the command - input := []uint8(wideCmd) - encodedCmd := base64.StdEncoding.EncodeToString(input) + // Finally make it base64 encoded which is required for powershell. + psCmd = base64.StdEncoding.EncodeToString([]byte(encoded)) - // Create the powershell.exe command line to execute the script - return fmt.Sprintf("powershell.exe -EncodedCommand %s", encodedCmd) + // Specify powershell.exe to run encoded command + return "powershell.exe -EncodedCommand " + psCmd } diff --git a/vendor/github.com/masterzen/winrm/request.go b/vendor/github.com/masterzen/winrm/request.go index 5a0a5ea5..e66bfd76 100644 --- a/vendor/github.com/masterzen/winrm/request.go +++ b/vendor/github.com/masterzen/winrm/request.go @@ -47,14 +47,14 @@ func NewOpenShellRequest(uri string, params *Parameters) *soap.SoapMessage { } // NewDeleteShellRequest ... -func NewDeleteShellRequest(uri, shellId string, params *Parameters) *soap.SoapMessage { +func NewDeleteShellRequest(uri, shellID string, params *Parameters) *soap.SoapMessage { if params == nil { params = DefaultParameters } message := soap.NewMessage() defaultHeaders(message, uri, params). Action("http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete"). - ShellId(shellId). + ShellId(shellID). ResourceURI("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd"). Build() @@ -64,7 +64,7 @@ func NewDeleteShellRequest(uri, shellId string, params *Parameters) *soap.SoapMe } // NewExecuteCommandRequest exec command on specific shellID -func NewExecuteCommandRequest(uri, shellId, command string, arguments []string, params *Parameters) *soap.SoapMessage { +func NewExecuteCommandRequest(uri, shellID, command string, arguments []string, params *Parameters) *soap.SoapMessage { if params == nil { params = DefaultParameters } @@ -72,7 +72,7 @@ func NewExecuteCommandRequest(uri, shellId, command string, arguments []string, defaultHeaders(message, uri, params). Action("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command"). ResourceURI("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd"). - ShellId(shellId). + ShellId(shellID). AddOption(soap.NewHeaderOption("WINRS_CONSOLEMODE_STDIN", "TRUE")). AddOption(soap.NewHeaderOption("WINRS_SKIP_CMD_SHELL", "FALSE")). Build() @@ -93,7 +93,8 @@ func NewExecuteCommandRequest(uri, shellId, command string, arguments []string, return message } -func NewGetOutputRequest(uri, shellId, commandId, streams string, params *Parameters) *soap.SoapMessage { +//NewGetOutputRequest NewGetOutputRequest +func NewGetOutputRequest(uri, shellID, commandID, streams string, params *Parameters) *soap.SoapMessage { if params == nil { params = DefaultParameters } @@ -101,18 +102,19 @@ func NewGetOutputRequest(uri, shellId, commandId, streams string, params *Parame defaultHeaders(message, uri, params). Action("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive"). ResourceURI("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd"). - ShellId(shellId). + ShellId(shellID). Build() receive := message.CreateBodyElement("Receive", soap.DOM_NS_WIN_SHELL) desiredStreams := message.CreateElement(receive, "DesiredStream", soap.DOM_NS_WIN_SHELL) - desiredStreams.SetAttr("CommandId", commandId) + desiredStreams.SetAttr("CommandId", commandID) desiredStreams.SetContent(streams) return message } -func NewSendInputRequest(uri, shellId, commandId string, input []byte, params *Parameters) *soap.SoapMessage { +//NewSendInputRequest NewSendInputRequest +func NewSendInputRequest(uri, shellID, commandID string, input []byte, eof bool, params *Parameters) *soap.SoapMessage { if params == nil { params = DefaultParameters } @@ -121,7 +123,7 @@ func NewSendInputRequest(uri, shellId, commandId string, input []byte, params *P defaultHeaders(message, uri, params). Action("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Send"). ResourceURI("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd"). - ShellId(shellId). + ShellId(shellID). Build() content := base64.StdEncoding.EncodeToString(input) @@ -129,12 +131,16 @@ func NewSendInputRequest(uri, shellId, commandId string, input []byte, params *P send := message.CreateBodyElement("Send", soap.DOM_NS_WIN_SHELL) streams := message.CreateElement(send, "Stream", soap.DOM_NS_WIN_SHELL) streams.SetAttr("Name", "stdin") - streams.SetAttr("CommandId", commandId) + streams.SetAttr("CommandId", commandID) streams.SetContent(content) + if eof { + streams.SetAttr("End", "true") + } return message } -func NewSignalRequest(uri string, shellId string, commandId string, params *Parameters) *soap.SoapMessage { +//NewSignalRequest NewSignalRequest +func NewSignalRequest(uri string, shellID string, commandID string, params *Parameters) *soap.SoapMessage { if params == nil { params = DefaultParameters } @@ -143,11 +149,11 @@ func NewSignalRequest(uri string, shellId string, commandId string, params *Para defaultHeaders(message, uri, params). Action("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal"). ResourceURI("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd"). - ShellId(shellId). + ShellId(shellID). Build() signal := message.CreateBodyElement("Signal", soap.DOM_NS_WIN_SHELL) - signal.SetAttr("CommandId", commandId) + signal.SetAttr("CommandId", commandID) code := message.CreateElement(signal, "Code", soap.DOM_NS_WIN_SHELL) code.SetContent("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate") diff --git a/vendor/github.com/masterzen/winrm/response.go b/vendor/github.com/masterzen/winrm/response.go index b5ca8cc9..792ead59 100644 --- a/vendor/github.com/masterzen/winrm/response.go +++ b/vendor/github.com/masterzen/winrm/response.go @@ -13,6 +13,28 @@ import ( "github.com/masterzen/winrm/soap" ) +type ExecuteCommandError struct { + Inner error + Body string +} + +func (e *ExecuteCommandError) Error() string { + if e.Inner == nil { + return "error" + } + + return e.Inner.Error() +} + +func (e *ExecuteCommandError) Is(err error) bool { + _, ok := err.(*ExecuteCommandError) + return ok +} + +func (b *ExecuteCommandError) Unwrap() error { + return b.Inner +} + func first(node tree.Node, xpath string) (string, error) { nodes, err := xPath(node, xpath) if err != nil { @@ -44,6 +66,7 @@ func xPath(node tree.Node, xpath string) (tree.NodeSet, error) { return nodes, nil } +// ParseOpenShellResponse ParseOpenShellResponse func ParseOpenShellResponse(response string) (string, error) { doc, err := xmltree.ParseXML(strings.NewReader(response)) if err != nil { @@ -52,14 +75,39 @@ func ParseOpenShellResponse(response string) (string, error) { return first(doc, "//w:Selector[@Name='ShellId']") } -func ParseExecuteCommandResponse(response string) (string, error) { +// ParseExecuteCommandResponse ParseExecuteCommandResponse +func ParseExecuteCommandResponse(response string) (commandId string, err error) { + defer func() { + if err != nil { + err = &ExecuteCommandError{Inner: err, Body: response} + } + }() + doc, err := xmltree.ParseXML(strings.NewReader(response)) if err != nil { - return "", err + return "", fmt.Errorf("parsing xml response: %w", err) + } + + action, err := first(doc, "//a:Action") + if err != nil { + return "", fmt.Errorf("getting response action: %w", err) + } + + switch action { + case "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandResponse": + commandId, err = first(doc, "//rsp:CommandId") + if err != nil { + return "", fmt.Errorf("finding command id: %w", err) + } + + return commandId, nil + + default: + return "", fmt.Errorf("unsupported action: %v", action) } - return first(doc, "//rsp:CommandId") } +// ParseSlurpOutputErrResponse ParseSlurpOutputErrResponse func ParseSlurpOutputErrResponse(response string, stdout, stderr io.Writer) (bool, int, error) { var ( finished bool @@ -94,6 +142,7 @@ func ParseSlurpOutputErrResponse(response string, stdout, stderr io.Writer) (boo return finished, exitCode, err } +// ParseSlurpOutputResponse ParseSlurpOutputResponse func ParseSlurpOutputResponse(response string, stream io.Writer, streamType string) (bool, int, error) { var ( finished bool @@ -105,7 +154,7 @@ func ParseSlurpOutputResponse(response string, stream io.Writer, streamType stri nodes, _ := xPath(doc, fmt.Sprintf("//rsp:Stream[@Name='%s']", streamType)) for _, node := range nodes { content, _ := base64.StdEncoding.DecodeString(node.ResValue()) - stream.Write(content) + _, _ = stream.Write(content) } ended, _ := any(doc, "//*[@State='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Done']") diff --git a/vendor/github.com/masterzen/winrm/shell.go b/vendor/github.com/masterzen/winrm/shell.go index 0f33348f..79c6bd2e 100644 --- a/vendor/github.com/masterzen/winrm/shell.go +++ b/vendor/github.com/masterzen/winrm/shell.go @@ -1,5 +1,7 @@ package winrm +import "context" + // Shell is the local view of a WinRM Shell of a given Client type Shell struct { client *Client @@ -7,7 +9,14 @@ type Shell struct { } // Execute command on the given Shell, returning either an error or a Command +// +// Deprecated: user ExecuteWithContext func (s *Shell) Execute(command string, arguments ...string) (*Command, error) { + return s.ExecuteWithContext(context.Background(), command, arguments...) +} + +// ExecuteWithContext command on the given Shell, returning either an error or a Command +func (s *Shell) ExecuteWithContext(ctx context.Context, command string, arguments ...string) (*Command, error) { request := NewExecuteCommandRequest(s.client.url, s.id, command, arguments, &s.client.Parameters) defer request.Free() @@ -21,7 +30,7 @@ func (s *Shell) Execute(command string, arguments ...string) (*Command, error) { return nil, err } - cmd := newCommand(s, commandID) + cmd := newCommand(ctx, s, commandID) return cmd, nil } diff --git a/vendor/github.com/masterzen/winrm/soap/header.go b/vendor/github.com/masterzen/winrm/soap/header.go index 6aeaebe2..d9f5dd38 100644 --- a/vendor/github.com/masterzen/winrm/soap/header.go +++ b/vendor/github.com/masterzen/winrm/soap/header.go @@ -23,7 +23,7 @@ type SoapHeader struct { locale string id string action string - shellId string + shellID string resourceURI string options []HeaderOption message *SoapMessage @@ -44,135 +44,137 @@ type HeaderBuilder interface { Build(*SoapMessage) *SoapMessage } -func (self *SoapHeader) To(uri string) *SoapHeader { - self.to = uri - return self +func (sh *SoapHeader) To(uri string) *SoapHeader { + sh.to = uri + return sh } -func (self *SoapHeader) ReplyTo(uri string) *SoapHeader { - self.replyTo = uri - return self +func (sh *SoapHeader) ReplyTo(uri string) *SoapHeader { + sh.replyTo = uri + return sh } -func (self *SoapHeader) MaxEnvelopeSize(size int) *SoapHeader { - self.maxEnvelopeSize = strconv.Itoa(size) - return self +func (sh *SoapHeader) MaxEnvelopeSize(size int) *SoapHeader { + sh.maxEnvelopeSize = strconv.Itoa(size) + return sh } -func (self *SoapHeader) Timeout(timeout string) *SoapHeader { - self.timeout = timeout - return self +func (sh *SoapHeader) Timeout(timeout string) *SoapHeader { + sh.timeout = timeout + return sh } -func (self *SoapHeader) Id(id string) *SoapHeader { - self.id = id - return self +//nolint:stylecheck // Should be ShellID, but we stay compatible +func (sh *SoapHeader) Id(id string) *SoapHeader { + sh.id = id + return sh } -func (self *SoapHeader) Action(action string) *SoapHeader { - self.action = action - return self +func (sh *SoapHeader) Action(action string) *SoapHeader { + sh.action = action + return sh } -func (self *SoapHeader) Locale(locale string) *SoapHeader { - self.locale = locale - return self +func (sh *SoapHeader) Locale(locale string) *SoapHeader { + sh.locale = locale + return sh } -func (self *SoapHeader) ShellId(shellId string) *SoapHeader { - self.shellId = shellId - return self +//nolint:stylecheck // Should be ShellID, but we stay compatible +func (sh *SoapHeader) ShellId(shellId string) *SoapHeader { + sh.shellID = shellId + return sh } -func (self *SoapHeader) ResourceURI(resourceURI string) *SoapHeader { - self.resourceURI = resourceURI - return self +func (sh *SoapHeader) ResourceURI(resourceURI string) *SoapHeader { + sh.resourceURI = resourceURI + return sh } -func (self *SoapHeader) AddOption(option *HeaderOption) *SoapHeader { - self.options = append(self.options, *option) - return self +func (sh *SoapHeader) AddOption(option *HeaderOption) *SoapHeader { + sh.options = append(sh.options, *option) + return sh } -func (self *SoapHeader) Options(options []HeaderOption) *SoapHeader { - self.options = options - return self +func (sh *SoapHeader) Options(options []HeaderOption) *SoapHeader { + sh.options = options + return sh } -func (self *SoapHeader) Build() *SoapMessage { - header := self.createElement(self.message.envelope, "Header", DOM_NS_SOAP_ENV) +func (sh *SoapHeader) Build() *SoapMessage { + header := sh.createElement(sh.message.envelope, "Header", DOM_NS_SOAP_ENV) - if self.to != "" { - to := self.createElement(header, "To", DOM_NS_ADDRESSING) - to.SetContent(self.to) + if sh.to != "" { + to := sh.createElement(header, "To", DOM_NS_ADDRESSING) + to.SetContent(sh.to) } - if self.replyTo != "" { - replyTo := self.createElement(header, "ReplyTo", DOM_NS_ADDRESSING) - a := self.createMUElement(replyTo, "Address", DOM_NS_ADDRESSING, true) - a.SetContent(self.replyTo) + if sh.replyTo != "" { + replyTo := sh.createElement(header, "ReplyTo", DOM_NS_ADDRESSING) + a := sh.createMUElement(replyTo, "Address", DOM_NS_ADDRESSING, true) + a.SetContent(sh.replyTo) } - if self.maxEnvelopeSize != "" { - envelope := self.createMUElement(header, "MaxEnvelopeSize", DOM_NS_WSMAN_DMTF, true) - envelope.SetContent(self.maxEnvelopeSize) + if sh.maxEnvelopeSize != "" { + envelope := sh.createMUElement(header, "MaxEnvelopeSize", DOM_NS_WSMAN_DMTF, true) + envelope.SetContent(sh.maxEnvelopeSize) } - if self.timeout != "" { - timeout := self.createElement(header, "OperationTimeout", DOM_NS_WSMAN_DMTF) - timeout.SetContent(self.timeout) + if sh.timeout != "" { + timeout := sh.createElement(header, "OperationTimeout", DOM_NS_WSMAN_DMTF) + timeout.SetContent(sh.timeout) } - if self.id != "" { - id := self.createElement(header, "MessageID", DOM_NS_ADDRESSING) - id.SetContent(self.id) + if sh.id != "" { + id := sh.createElement(header, "MessageID", DOM_NS_ADDRESSING) + id.SetContent(sh.id) } - if self.locale != "" { - locale := self.createMUElement(header, "Locale", DOM_NS_WSMAN_DMTF, false) - locale.SetAttr("xml:lang", self.locale) - datalocale := self.createMUElement(header, "DataLocale", DOM_NS_WSMAN_MSFT, false) - datalocale.SetAttr("xml:lang", self.locale) + if sh.locale != "" { + locale := sh.createMUElement(header, "Locale", DOM_NS_WSMAN_DMTF, false) + locale.SetAttr("xml:lang", sh.locale) + datalocale := sh.createMUElement(header, "DataLocale", DOM_NS_WSMAN_MSFT, false) + datalocale.SetAttr("xml:lang", sh.locale) } - if self.action != "" { - action := self.createMUElement(header, "Action", DOM_NS_ADDRESSING, true) - action.SetContent(self.action) + if sh.action != "" { + action := sh.createMUElement(header, "Action", DOM_NS_ADDRESSING, true) + action.SetContent(sh.action) } - if self.shellId != "" { - selectorSet := self.createElement(header, "SelectorSet", DOM_NS_WSMAN_DMTF) - selector := self.createElement(selectorSet, "Selector", DOM_NS_WSMAN_DMTF) + if sh.shellID != "" { + selectorSet := sh.createElement(header, "SelectorSet", DOM_NS_WSMAN_DMTF) + selector := sh.createElement(selectorSet, "Selector", DOM_NS_WSMAN_DMTF) selector.SetAttr("Name", "ShellId") - selector.SetContent(self.shellId) + selector.SetContent(sh.shellID) } - if self.resourceURI != "" { - resource := self.createMUElement(header, "ResourceURI", DOM_NS_WSMAN_DMTF, true) - resource.SetContent(self.resourceURI) + if sh.resourceURI != "" { + resource := sh.createMUElement(header, "ResourceURI", DOM_NS_WSMAN_DMTF, true) + resource.SetContent(sh.resourceURI) } - if len(self.options) > 0 { - set := self.createElement(header, "OptionSet", DOM_NS_WSMAN_DMTF) - for _, option := range self.options { - e := self.createElement(set, "Option", DOM_NS_WSMAN_DMTF) + if len(sh.options) > 0 { + set := sh.createElement(header, "OptionSet", DOM_NS_WSMAN_DMTF) + for _, option := range sh.options { + e := sh.createElement(set, "Option", DOM_NS_WSMAN_DMTF) e.SetAttr("Name", option.key) e.SetContent(option.value) } } - return self.message + return sh.message } -func (self *SoapHeader) createElement(parent *dom.Element, name string, ns dom.Namespace) (element *dom.Element) { +func (sh *SoapHeader) createElement(parent *dom.Element, name string, ns dom.Namespace) (element *dom.Element) { element = dom.CreateElement(name) parent.AddChild(element) ns.SetTo(element) return } -func (self *SoapHeader) createMUElement(parent *dom.Element, name string, ns dom.Namespace, mustUnderstand bool) (element *dom.Element) { - element = self.createElement(parent, name, ns) +func (sh *SoapHeader) createMUElement(parent *dom.Element, name string, ns dom.Namespace, mustUnderstand bool) (element *dom.Element) { + element = sh.createElement(parent, name, ns) value := "false" if mustUnderstand { value = "true" diff --git a/vendor/github.com/masterzen/winrm/soap/namespaces.go b/vendor/github.com/masterzen/winrm/soap/namespaces.go index e7f5e22f..4f11a681 100644 --- a/vendor/github.com/masterzen/winrm/soap/namespaces.go +++ b/vendor/github.com/masterzen/winrm/soap/namespaces.go @@ -6,6 +6,8 @@ import ( ) // Namespaces +// +//nolint:stylecheck // we keep the ALL_CAPS names const ( NS_SOAP_ENV = "http://www.w3.org/2003/05/soap-envelope" NS_ADDRESSING = "http://schemas.xmlsoap.org/ws/2004/08/addressing" @@ -20,6 +22,8 @@ const ( ) // Namespace Prefixes +// +//nolint:stylecheck // we keep the ALL_CAPS names const ( NSP_SOAP_ENV = "env" NSP_ADDRESSING = "a" @@ -34,17 +38,19 @@ const ( ) // DOM Namespaces +// +//nolint:stylecheck var ( - DOM_NS_SOAP_ENV = dom.Namespace{"env", "http://www.w3.org/2003/05/soap-envelope"} - DOM_NS_ADDRESSING = dom.Namespace{"a", "http://schemas.xmlsoap.org/ws/2004/08/addressing"} - DOM_NS_CIMBINDING = dom.Namespace{"b", "http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd"} - DOM_NS_ENUM = dom.Namespace{"n", "http://schemas.xmlsoap.org/ws/2004/09/enumeration"} - DOM_NS_TRANSFER = dom.Namespace{"x", "http://schemas.xmlsoap.org/ws/2004/09/transfer"} - DOM_NS_WSMAN_DMTF = dom.Namespace{"w", "http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"} - DOM_NS_WSMAN_MSFT = dom.Namespace{"p", "http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd"} - DOM_NS_SCHEMA_INST = dom.Namespace{"xsi", "http://www.w3.org/2001/XMLSchema-instance"} - DOM_NS_WIN_SHELL = dom.Namespace{"rsp", "http://schemas.microsoft.com/wbem/wsman/1/windows/shell"} - DOM_NS_WSMAN_FAULT = dom.Namespace{"f", "http://schemas.microsoft.com/wbem/wsman/1/wsmanfault"} + DOM_NS_SOAP_ENV = dom.Namespace{Prefix: NSP_SOAP_ENV, Uri: NS_SOAP_ENV} + DOM_NS_ADDRESSING = dom.Namespace{Prefix: NSP_ADDRESSING, Uri: NS_ADDRESSING} + DOM_NS_CIMBINDING = dom.Namespace{Prefix: NSP_CIMBINDING, Uri: NS_CIMBINDING} + DOM_NS_ENUM = dom.Namespace{Prefix: NSP_ENUM, Uri: NS_ENUM} + DOM_NS_TRANSFER = dom.Namespace{Prefix: NSP_TRANSFER, Uri: NS_TRANSFER} + DOM_NS_WSMAN_DMTF = dom.Namespace{Prefix: NSP_WSMAN_DMTF, Uri: NS_WSMAN_DMTF} + DOM_NS_WSMAN_MSFT = dom.Namespace{Prefix: NSP_WSMAN_MSFT, Uri: NS_WSMAN_MSFT} + DOM_NS_SCHEMA_INST = dom.Namespace{Prefix: NSP_SCHEMA_INST, Uri: NS_SCHEMA_INST} + DOM_NS_WIN_SHELL = dom.Namespace{Prefix: NSP_WIN_SHELL, Uri: NS_WIN_SHELL} + DOM_NS_WSMAN_FAULT = dom.Namespace{Prefix: NSP_WSMAN_FAULT, Uri: NS_WSMAN_FAULT} ) var MostUsed = [...]dom.Namespace{ diff --git a/vendor/github.com/tidwall/transform/LICENSE b/vendor/github.com/tidwall/transform/LICENSE new file mode 100644 index 00000000..3df8052b --- /dev/null +++ b/vendor/github.com/tidwall/transform/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2017, Joshua J Baker + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/vendor/github.com/tidwall/transform/README.md b/vendor/github.com/tidwall/transform/README.md new file mode 100644 index 00000000..28ee249c --- /dev/null +++ b/vendor/github.com/tidwall/transform/README.md @@ -0,0 +1,230 @@ +# Transform + +[![GoDoc](https://img.shields.io/badge/api-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/tidwall/transform) + + +Transform is a Go package that provides a simple pattern for performing [chainable](#chaining) data transformations on streams of bytes. It conforms to the [io.Reader](https://golang.org/pkg/io/#Reader) interface and is useful for operations such as converting data formats, audio/video resampling, image transforms, log filters, regex line matching, etc. + +The [transutil package](#transutil-package) provides few examples that work with JSON such as `JSONToMsgPack`, `MsgPackToJSON`, `JSONToPrettyJSON`, `JSONToUglyJSON`, `JSONToProtoBuf`, and `ProtoBufToJSON`. It also includes a handy `Gzipper` and `Gunzipper`. + + +Getting Started +=============== + +## Installing + +To start using Transform, install Go and run `go get`: + +```sh +$ go get -u github.com/tidwall/transform +``` + +## Using + +Below are a few very simple examples of custom transformers. + +### ToUpper + +Convert a string to uppper case. Unicode aware. In this example +we only process one rune at a time. + +```go +func ToUpper(r io.Reader) io.Reader { + br := bufio.NewReader(r) + return transform.NewTransformer(func() ([]byte, error) { + c, _, err := br.ReadRune() + if err != nil { + return nil, err + } + return []byte(strings.ToUpper(string([]rune{c}))), nil + }) +} +``` +```go +msg := "Hello World" +data, err := ioutil.ReadAll(ToUpper(bytes.NewBufferString(msg))) +if err != nil { + log.Fatal(err) +} +fmt.Println(string(data)) +``` + +Output: + +``` +HELLO WORLD +``` + + +### Rot13 + +The [Rot13](https://en.wikipedia.org/wiki/ROT13) cipher. + +```go +func Rot13(r io.Reader) io.Reader { + buf := make([]byte, 256) + return transform.NewTransformer(func() ([]byte, error) { + n, err := r.Read(buf) + if err != nil { + return nil, err + } + for i := 0; i < n; i++ { + if buf[i] >= 'a' && buf[i] <= 'z' { + buf[i] = ((buf[i] - 'a' + 13) % 26) + 'a' + } else if buf[i] >= 'A' && buf[i] <= 'Z' { + buf[i] = ((buf[i] - 'A' + 13) % 26) + 'A' + } + } + return buf[:n], nil + }) +} +``` +```go +msg := "Hello World" +data, err := ioutil.ReadAll(Rot13(bytes.NewBufferString(msg))) +if err != nil { + log.Fatal(err) +} +fmt.Println(string(data)) +``` + +Output: + +``` +Uryyb Jbeyq +``` + +### RegExp Line Matcher + +A line reader that filters lines that match on a RegExp pattern. + +```go +func LineMatch(r io.Reader, pattern string) io.Reader { + br := bufio.NewReader(r) + return NewTransformer(func() ([]byte, error) { + for { + line, err := br.ReadBytes('\n') + matched, _ := regexp.Match(pattern, line) + if matched { + return line, err + } + if err != nil { + return nil, err + } + } + }) +} +``` +```go +logs := ` +23 Apr 17:32:23.604 [INFO] DB loaded in 0.551 seconds +23 Apr 17:32:23.605 [WARN] Disk space is low +23 Apr 17:32:23.054 [INFO] Server started on port 7812 +23 Apr 17:32:23.141 [INFO] Ready for connections +` +data, err := ioutil.ReadAll(LineMatch(bytes.NewBufferString(logs), "WARN")) +if err != nil { + log.Fatal(err) +} +fmt.Println(string(data)) +``` + +Output: + +``` +23 Apr 17:32:23.605 [WARN] Disk space is low +``` + +### LineTrimSpace + +A line reader that trims the spaces from all lines. + +```go +func LineTrimSpace(r io.Reader, pattern string) io.Reader { + br := bufio.NewReader(r) + return transform.NewTransformer(func() ([]byte, error) { + for { + line, err := br.ReadBytes('\n') + if len(line) > 0 { + line = append(bytes.TrimSpace(line), '\n') + } + return line, err + } + }) +} +``` +```go +phrases := " lacy timber \n" +phrases += "\t\thybrid gossiping\t\n" +phrases += " coy radioactivity\n" +phrases += "rocky arrow \n" +out, err := ioutil.ReadAll(LineTrimSpace(bytes.NewBufferString(phrases))) +if err != nil { + log.Fatal(err) +} +fmt.Printf("%s\n", out) +``` + +Output: + +``` +lacy timber +hybrid gossiping +coy radioactivity +rocky arrow +``` + +### Chaining + +A reader that matches lines on the letter 'o', trims the +space from the lines, and transforms everything to upper case. + +```go +phrases := " lacy timber \n" +phrases += "\t\thybrid gossiping\t\n" +phrases += " coy radioactivity\n" +phrases += "rocky arrow \n" + +r := ToUpper(LineTrimSpace(LineMatch(bytes.NewBufferString(phrases), "o"))) + +// Pass the string though the transformer. +out, err := ioutil.ReadAll(r) +if err != nil { + log.Fatal(err) +} + +fmt.Printf("%s\n", out) +``` + +Output: + +``` +HYBRID GOSSIPING +COY RADIOACTIVITY +ROCKY ARROW +``` + +## Transutil package +[![GoDoc](https://img.shields.io/badge/api-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/tidwall/transform/transutil) + + +The `github.com/tidwall/transform/transutil` package includes additional examples. + +``` +func Gunzipper(r io.Reader) io.Reader +func Gzipper(r io.Reader) io.Reader +func JSONToMsgPack(r io.Reader) io.Reader +func JSONToPrettyJSON(r io.Reader) io.Reader +func JSONToProtoBuf(r io.Reader, pb proto.Message, multimessage bool) io.Reader +func JSONToUglyJSON(r io.Reader) io.Reader +func MsgPackToJSON(r io.Reader) io.Reader +func ProtoBufToJSON(r io.Reader, pb proto.Message, multimessage bool) io.Reader +``` + +## Contact +Josh Baker [@tidwall](http://twitter.com/tidwall) + +## License +Transform source code is available under the ISC [License](/LICENSE). + + diff --git a/vendor/github.com/tidwall/transform/transform.go b/vendor/github.com/tidwall/transform/transform.go new file mode 100644 index 00000000..42ed2985 --- /dev/null +++ b/vendor/github.com/tidwall/transform/transform.go @@ -0,0 +1,54 @@ +// Package transform provides a convenient utility for transforming one data +// format to another. +package transform + +// Transformer represents a transform reader. +type Transformer struct { + tfn func() ([]byte, error) // user-defined transform function + buf []byte // read buffer + idx int // read buffer index + err error // last error +} + +// NewTransformer returns an object that can be used for transforming one +// data formant to another. The param is a function that performs the +// conversion and returns the transformed data in chunks/messages. +func NewTransformer(fn func() ([]byte, error)) *Transformer { + return &Transformer{tfn: fn} +} + +// ReadMessage allows for reading a one transformed message at a time. +func (r *Transformer) ReadMessage() ([]byte, error) { + return r.tfn() +} + +// Read conforms to io.Reader +func (r *Transformer) Read(p []byte) (n int, err error) { + if len(r.buf)-r.idx > 0 { + // There's data in the read buffer, return it prior to returning errors + // or reading more messages. + if len(r.buf)-r.idx > len(p) { + // The input slice is smaller than the read buffer, copy a subslice + // of the read buffer and increase the read index. + copy(p, r.buf[r.idx:r.idx+len(p)]) + r.idx += len(p) + return len(p), nil + } + // Copy the entire read buffer to the input slice. + n = len(r.buf) - r.idx + copy(p[:n], r.buf[r.idx:]) + r.buf = r.buf[:0] // reset the read buffer, keeping it's capacity + r.idx = 0 // rewind the read buffer index + return n, nil + } + if r.err != nil { + return 0, r.err + } + var msg []byte + msg, r.err = r.ReadMessage() + // We should immediately append the incoming message to the read + // buffer to allow for the implemented transformer to repurpose + // it's own message space if needed. + r.buf = append(r.buf, msg...) + return r.Read(p) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 1c3f4fdf..cd9787c5 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,7 +1,7 @@ -# github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 +# github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 ## explicit github.com/Azure/go-ntlmssp -# github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022 +# github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 ## explicit github.com/ChrisTrenkamp/goxpath github.com/ChrisTrenkamp/goxpath/internal/execxp @@ -47,6 +47,13 @@ github.com/agext/levenshtein # github.com/apparentlymart/go-textseg/v15 v15.0.0 ## explicit; go 1.16 github.com/apparentlymart/go-textseg/v15/textseg +# github.com/bodgit/ntlmssp v0.0.0-20231122144230-2b2bca29f22b +## explicit; go 1.18 +github.com/bodgit/ntlmssp +github.com/bodgit/ntlmssp/http +# github.com/bodgit/windows v1.0.1 +## explicit; go 1.13 +github.com/bodgit/windows # github.com/cloudflare/circl v1.3.7 ## explicit; go 1.19 github.com/cloudflare/circl/dh/x25519 @@ -67,7 +74,10 @@ github.com/dylanmei/iso8601 # github.com/fatih/color v1.16.0 ## explicit; go 1.17 github.com/fatih/color -# github.com/gofrs/uuid v3.2.0+incompatible +# github.com/go-logr/logr v1.3.0 +## explicit; go 1.18 +github.com/go-logr/logr +# github.com/gofrs/uuid v4.4.0+incompatible ## explicit github.com/gofrs/uuid # github.com/golang/protobuf v1.5.3 @@ -217,15 +227,15 @@ github.com/jcmturner/aescts/v2 # github.com/jcmturner/dnsutils/v2 v2.0.0 ## explicit; go 1.13 github.com/jcmturner/dnsutils/v2 -# github.com/jcmturner/gofork v1.0.0 -## explicit +# github.com/jcmturner/gofork v1.7.6 +## explicit; go 1.7 github.com/jcmturner/gofork/encoding/asn1 github.com/jcmturner/gofork/x/crypto/pbkdf2 # github.com/jcmturner/goidentity/v6 v6.0.1 ## explicit; go 1.13 github.com/jcmturner/goidentity/v6 -# github.com/jcmturner/gokrb5/v8 v8.4.2 -## explicit; go 1.14 +# github.com/jcmturner/gokrb5/v8 v8.4.4 +## explicit; go 1.16 github.com/jcmturner/gokrb5/v8/asn1tools github.com/jcmturner/gokrb5/v8/client github.com/jcmturner/gokrb5/v8/config @@ -262,11 +272,11 @@ github.com/jcmturner/gokrb5/v8/types ## explicit; go 1.13 github.com/jcmturner/rpc/v2/mstypes github.com/jcmturner/rpc/v2/ndr -# github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9 +# github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 ## explicit github.com/masterzen/simplexml/dom -# github.com/masterzen/winrm v0.0.0-20190308153735-1d17eaf15943 -## explicit +# github.com/masterzen/winrm v0.0.0-20231227165926-e811dad5ac77 +## explicit; go 1.21 github.com/masterzen/winrm github.com/masterzen/winrm/soap # github.com/mattn/go-colorable v0.1.13 @@ -301,6 +311,9 @@ github.com/oklog/run github.com/packer-community/winrmcp/winrmcp # github.com/smartystreets/goconvey v1.6.4 ## explicit +# github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde +## explicit +github.com/tidwall/transform # github.com/vmihailenco/msgpack v4.0.4+incompatible ## explicit github.com/vmihailenco/msgpack From ae26db5b8f6c8971b4a4edf31e4972a444f7697c Mon Sep 17 00:00:00 2001 From: sthetz Date: Mon, 18 Mar 2024 20:55:01 +0300 Subject: [PATCH 2/2] Use RunPSWithString instead of RunWithString --- ad/internal/winrmhelper/powershell_command.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ad/internal/winrmhelper/powershell_command.go b/ad/internal/winrmhelper/powershell_command.go index 6343a538..d2d02bcd 100644 --- a/ad/internal/winrmhelper/powershell_command.go +++ b/ad/internal/winrmhelper/powershell_command.go @@ -97,16 +97,15 @@ func (p *PSCommand) Run(conf *config.ProviderConf) (*PSCommandResult, error) { } defer conf.ReleaseWinRMClient(conn) - encodedCmd := winrm.Powershell(p.cmd) - if !p.ExecLocally && conn != nil { log.Printf("[DEBUG] Executing command on remote host") - stdout, stderr, res, err = conn.RunWithString(encodedCmd, "") + stdout, stderr, res, err = conn.RunPSWithString(p.cmd, "") log.Printf("[DEBUG] Powershell command exited with code %d", res) } else { log.Printf("[DEBUG] Creating local shell") localShell := NewLocalPSSession() log.Printf("[DEBUG] Executing command on local host") + encodedCmd := winrm.Powershell(p.cmd) stdout, stderr, res, err = localShell.ExecutePScmd(encodedCmd) }