From 5959494afdc32dc650904ff55d0c496b0092853c Mon Sep 17 00:00:00 2001 From: Andrew Babichev Date: Mon, 4 Jan 2021 17:17:32 +0200 Subject: [PATCH 1/3] Add STS Regional Endpoint Support To Other STS Clients (#308) --- lib/provider.go | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/lib/provider.go b/lib/provider.go index 3847360..6a7cf53 100644 --- a/lib/provider.go +++ b/lib/provider.go @@ -8,6 +8,7 @@ import ( "errors" "github.com/segmentio/aws-okta/sessioncache" + "github.com/aws/aws-sdk-go/aws/endpoints" log "github.com/sirupsen/logrus" "github.com/99designs/keyring" @@ -301,11 +302,19 @@ func (p *Provider) GetSAMLLoginURL() (*url.URL, error) { // assumeRoleFromSession takes a session created with an okta SAML login and uses that to assume a role func (p *Provider) assumeRoleFromSession(creds sts.Credentials, roleArn string) (sts.Credentials, error) { - client := sts.New(aws_session.New(&aws.Config{Credentials: credentials.NewStaticCredentials( - *creds.AccessKeyId, - *creds.SecretAccessKey, - *creds.SessionToken, - )})) + conf := &aws.Config{ + Credentials: credentials.NewStaticCredentials( + *creds.AccessKeyId, + *creds.SecretAccessKey, + *creds.SessionToken, + ), + } + if region := p.profiles[sourceProfile(p.profile, p.profiles)]["region"]; region != "" { + conf.WithRegion(region) + conf.WithSTSRegionalEndpoint(endpoints.RegionalSTSEndpoint) + } + sess := aws_session.Must(aws_session.NewSession(conf)) + client := sts.New(sess) input := &sts.AssumeRoleInput{ RoleArn: aws.String(roleArn), @@ -341,15 +350,19 @@ func (p *Provider) roleSessionName() string { // GetRoleARN uses temporary credentials to call AWS's get-caller-identity and // returns the assumed role's ARN func (p *Provider) GetRoleARNWithRegion(creds credentials.Value) (string, error) { - config := aws.Config{Credentials: credentials.NewStaticCredentials( - creds.AccessKeyID, - creds.SecretAccessKey, - creds.SessionToken, - )} + conf := &aws.Config{ + Credentials: credentials.NewStaticCredentials( + creds.AccessKeyID, + creds.SecretAccessKey, + creds.SessionToken, + ), + } if region := p.profiles[sourceProfile(p.profile, p.profiles)]["region"]; region != "" { - config.WithRegion(region) + conf.WithRegion(region) + conf.WithSTSRegionalEndpoint(endpoints.RegionalSTSEndpoint) } - client := sts.New(aws_session.New(&config)) + sess := aws_session.Must(aws_session.NewSession(conf)) + client := sts.New(sess) indentity, err := client.GetCallerIdentity(&sts.GetCallerIdentityInput{}) if err != nil { From 2b49403a747b636794b5d31245ee28d31a7bdb51 Mon Sep 17 00:00:00 2001 From: Will Gardner Date: Mon, 4 Jan 2021 16:36:03 +0000 Subject: [PATCH 2/3] Update keyring to v1.1.6 (#309) Recent versions of kwallet have removed the old support for the kde4 compatible kwallet dbus interface. This means newer kde5 based OS installs (e.g. kubuntu 20.04) can no longer use the kwallet backend with aws-okta. This was fixed upstream in the keyring lib back in 2019 but the dependency hasn't been bumped since then. --- go.mod | 4 +- go.sum | 26 +- .../github.com/99designs/keyring/.gitignore | 1 + .../github.com/99designs/keyring/.travis.yml | 19 - vendor/github.com/99designs/keyring/README.md | 2 +- vendor/github.com/99designs/keyring/array.go | 4 + vendor/github.com/99designs/keyring/config.go | 3 + vendor/github.com/99designs/keyring/file.go | 59 +- vendor/github.com/99designs/keyring/go.mod | 15 +- vendor/github.com/99designs/keyring/go.sum | 22 +- .../github.com/99designs/keyring/keychain.go | 97 ++- .../github.com/99designs/keyring/keyring.go | 17 + .../github.com/99designs/keyring/kwallet.go | 14 +- .../github.com/99designs/keyring/libsecret.go | 93 ++- vendor/github.com/99designs/keyring/pass.go | 44 +- .../github.com/99designs/keyring/wincred.go | 20 +- .../github.com/dvsekhvalnov/jose2go/README.md | 4 +- .../dvsekhvalnov/jose2go/kdf/pbkdf2.go | 7 +- .../dvsekhvalnov/jose2go/pbse2_hmac_aeskw.go | 111 +-- vendor/github.com/godbus/dbus/.travis.yml | 10 +- vendor/github.com/godbus/dbus/README.markdown | 2 +- vendor/github.com/godbus/dbus/auth.go | 3 +- .../github.com/godbus/dbus/auth_anonymous.go | 16 + vendor/github.com/godbus/dbus/call.go | 26 +- vendor/github.com/godbus/dbus/conn.go | 656 +++++++++++------- vendor/github.com/godbus/dbus/conn_darwin.go | 4 + vendor/github.com/godbus/dbus/conn_other.go | 67 +- vendor/github.com/godbus/dbus/conn_unix.go | 17 + vendor/github.com/godbus/dbus/conn_windows.go | 15 + vendor/github.com/godbus/dbus/decoder.go | 62 +- .../github.com/godbus/dbus/default_handler.go | 117 ++-- vendor/github.com/godbus/dbus/export.go | 17 +- vendor/github.com/godbus/dbus/go.mod | 3 + vendor/github.com/godbus/dbus/match.go | 62 ++ vendor/github.com/godbus/dbus/object.go | 114 ++- .../godbus/dbus/server_interfaces.go | 18 + .../godbus/dbus/transport_generic.go | 2 +- .../godbus/dbus/transport_nonce_tcp.go | 39 ++ .../github.com/godbus/dbus/transport_tcp.go | 2 - .../github.com/godbus/dbus/transport_unix.go | 32 +- .../github.com/godbus/dbus/variant_lexer.go | 8 +- vendor/github.com/mtibben/percent/LICENSE | 21 + vendor/github.com/mtibben/percent/README.md | 5 + vendor/github.com/mtibben/percent/go.mod | 3 + vendor/github.com/mtibben/percent/percent.go | 64 ++ vendor/golang.org/x/sys/unix/mkerrors.sh | 3 + .../x/sys/unix/zerrors_linux_386.go | 14 + .../x/sys/unix/zerrors_linux_amd64.go | 14 + .../x/sys/unix/zerrors_linux_arm.go | 14 + .../x/sys/unix/zerrors_linux_arm64.go | 14 + .../x/sys/unix/zerrors_linux_mips.go | 14 + .../x/sys/unix/zerrors_linux_mips64.go | 14 + .../x/sys/unix/zerrors_linux_mips64le.go | 14 + .../x/sys/unix/zerrors_linux_mipsle.go | 14 + .../x/sys/unix/zerrors_linux_ppc64.go | 14 + .../x/sys/unix/zerrors_linux_ppc64le.go | 14 + .../x/sys/unix/zerrors_linux_riscv64.go | 14 + .../x/sys/unix/zerrors_linux_s390x.go | 14 + .../x/sys/unix/zerrors_linux_sparc64.go | 14 + .../golang.org/x/sys/unix/ztypes_linux_386.go | 37 + .../x/sys/unix/ztypes_linux_amd64.go | 38 + .../golang.org/x/sys/unix/ztypes_linux_arm.go | 37 + .../x/sys/unix/ztypes_linux_arm64.go | 38 + .../x/sys/unix/ztypes_linux_mips.go | 37 + .../x/sys/unix/ztypes_linux_mips64.go | 38 + .../x/sys/unix/ztypes_linux_mips64le.go | 38 + .../x/sys/unix/ztypes_linux_mipsle.go | 37 + .../x/sys/unix/ztypes_linux_ppc64.go | 38 + .../x/sys/unix/ztypes_linux_ppc64le.go | 38 + .../x/sys/unix/ztypes_linux_riscv64.go | 38 + .../x/sys/unix/ztypes_linux_s390x.go | 38 + .../x/sys/unix/ztypes_linux_sparc64.go | 38 + vendor/modules.txt | 12 +- 73 files changed, 2107 insertions(+), 527 deletions(-) create mode 100644 vendor/github.com/99designs/keyring/.gitignore delete mode 100644 vendor/github.com/99designs/keyring/.travis.yml create mode 100644 vendor/github.com/godbus/dbus/auth_anonymous.go create mode 100644 vendor/github.com/godbus/dbus/conn_unix.go create mode 100644 vendor/github.com/godbus/dbus/conn_windows.go create mode 100644 vendor/github.com/godbus/dbus/go.mod create mode 100644 vendor/github.com/godbus/dbus/match.go create mode 100644 vendor/github.com/godbus/dbus/transport_nonce_tcp.go create mode 100644 vendor/github.com/mtibben/percent/LICENSE create mode 100644 vendor/github.com/mtibben/percent/README.md create mode 100644 vendor/github.com/mtibben/percent/go.mod create mode 100644 vendor/github.com/mtibben/percent/percent.go diff --git a/go.mod b/go.mod index 83dfda9..b563f36 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,12 @@ module github.com/segmentio/aws-okta require ( - github.com/99designs/keyring v1.0.0 + github.com/99designs/keyring v1.1.6 github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053 github.com/aws/aws-sdk-go v1.25.35 github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/karalabe/hid v1.0.0 // indirect - github.com/keybase/go-keychain v0.0.0-20190604185112-cc436cc9fe98 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/marshallbrekka/go-u2fhost v0.0.0-20200114212649-cc764c209ee9 github.com/mitchellh/go-homedir v1.1.0 @@ -23,7 +22,6 @@ require ( github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 golang.org/x/net v0.0.0-20190628185345-da137c7871d7 - golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 // indirect golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 gopkg.in/ini.v1 v1.42.0 ) diff --git a/go.sum b/go.sum index 69160a4..28b4f38 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/99designs/keyring v1.0.0 h1:O2nCc8QRO2yeO7ncdR7bd17cdofhM8AXug42ahzEswU= -github.com/99designs/keyring v1.0.0/go.mod h1:qVCfjMddwdzjgy848stNe9eTPmovvxF1lhJvOD6LUEo= +github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM= +github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU= github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053 h1:H/GMMKYPkEIC3DF/JWQz8Pdd+Feifov2EIgGfNpeogI= github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053/go.mod h1:xW8sBma2LE3QxFSzCnH9qe6gAE2yO9GvQaWwX89HxbE= github.com/aws/aws-sdk-go v1.25.35 h1:fe2tJnqty/v/50pyngKdNk/NP8PFphYDA1Z7N3EiiiE= @@ -11,10 +11,10 @@ github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3E github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a h1:mq+R6XEM6lJX5VlLyZIrUSP8tSuJp82xTK89hvBwJbU= -github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= -github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4= -github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ= +github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= @@ -27,9 +27,8 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/karalabe/hid v1.0.0 h1:+/CIMNXhSU/zIJgnIvBD2nKHxS/bnRHhhs9xBryLpPo= github.com/karalabe/hid v1.0.0/go.mod h1:Vr51f8rUOLYrfrWDFlV12GGQgM5AT8sVh+2fY4MPeu8= -github.com/keybase/go-keychain v0.0.0-20190423185029-8441f7257eb1/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= -github.com/keybase/go-keychain v0.0.0-20190604185112-cc436cc9fe98 h1:CIcvKEAP7i7v/SWSwzAvq1ATWWs4+J/ezHqZT116+JA= -github.com/keybase/go-keychain v0.0.0-20190604185112-cc436cc9fe98/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= +github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= +github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -42,6 +41,8 @@ github.com/marshallbrekka/go-u2fhost v0.0.0-20200114212649-cc764c209ee9 h1:7dYgl github.com/marshallbrekka/go-u2fhost v0.0.0-20200114212649-cc764c209ee9/go.mod h1:U9kRL9P37LGrkikKWuekWsReXRKe2fkZdRSXpI7pP3A= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/segmentio/analytics-go v3.0.1+incompatible h1:W7T3ieNQjPFMb+SE8SAVYo6mPkKK/Y37wYdiNf5lCVg= @@ -73,7 +74,6 @@ github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEAB github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= @@ -84,10 +84,8 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20190516110030-61b9204099cb h1:k07iPOt0d6nEnwXF+kHB+iEg+WSuKe/SOQuFM2QoD+E= -golang.org/x/sys v0.0.0-20190516110030-61b9204099cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ= -golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI= +golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= diff --git a/vendor/github.com/99designs/keyring/.gitignore b/vendor/github.com/99designs/keyring/.gitignore new file mode 100644 index 0000000..8000dd9 --- /dev/null +++ b/vendor/github.com/99designs/keyring/.gitignore @@ -0,0 +1 @@ +.vagrant diff --git a/vendor/github.com/99designs/keyring/.travis.yml b/vendor/github.com/99designs/keyring/.travis.yml deleted file mode 100644 index 88af66d..0000000 --- a/vendor/github.com/99designs/keyring/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -language: go -go: - - "1.12.x" -os: - - linux - - osx -addons: - homebrew: - packages: - - gnupg - - pass - apt: - packages: - - pass - -script: - - diff -u <(echo -n) <(gofmt -d $(git ls-files '*.go')) - - go vet ./... - - go test -race ./... diff --git a/vendor/github.com/99designs/keyring/README.md b/vendor/github.com/99designs/keyring/README.md index 815f53a..7b07ab7 100644 --- a/vendor/github.com/99designs/keyring/README.md +++ b/vendor/github.com/99designs/keyring/README.md @@ -1,6 +1,6 @@ Keyring ======= -[![Build Status](https://travis-ci.org/99designs/keyring.svg?branch=master)](https://travis-ci.org/99designs/keyring) +[![Build Status](https://github.com/99designs/keyring/workflows/Continuous%20Integration/badge.svg)](https://github.com/99designs/keyring/actions) [![Documentation](https://godoc.org/github.com/99designs/keyring?status.svg)](https://godoc.org/github.com/99designs/keyring) Keyring provides utility functions for and a common interface to a range of secure credential storage services. Originally developed as part of [AWS Vault](https://github.com/99designs/aws-vault), a command line tool for securely managing AWS access from developer workstations. diff --git a/vendor/github.com/99designs/keyring/array.go b/vendor/github.com/99designs/keyring/array.go index 67dee17..fca61b8 100644 --- a/vendor/github.com/99designs/keyring/array.go +++ b/vendor/github.com/99designs/keyring/array.go @@ -48,3 +48,7 @@ func (k *ArrayKeyring) Keys() ([]string, error) { } return keys, nil } + +func (k *ArrayKeyring) GetMetadata(_ string) (Metadata, error) { + return Metadata{}, ErrMetadataNeedsCredentials +} diff --git a/vendor/github.com/99designs/keyring/config.go b/vendor/github.com/99designs/keyring/config.go index 8e34b80..1c5d0f7 100644 --- a/vendor/github.com/99designs/keyring/config.go +++ b/vendor/github.com/99designs/keyring/config.go @@ -46,4 +46,7 @@ type Config struct { // PassPrefix is a string prefix to prepend to the item path stored in pass PassPrefix string + + // WinCredPrefix is a string prefix to prepend to the key name + WinCredPrefix string } diff --git a/vendor/github.com/99designs/keyring/file.go b/vendor/github.com/99designs/keyring/file.go index 7b0b636..ea0c8bb 100644 --- a/vendor/github.com/99designs/keyring/file.go +++ b/vendor/github.com/99designs/keyring/file.go @@ -11,6 +11,7 @@ import ( jose "github.com/dvsekhvalnov/jose2go" homedir "github.com/mitchellh/go-homedir" + "github.com/mtibben/percent" ) func init() { @@ -22,6 +23,11 @@ func init() { }) } +var filenameEscape = func(s string) string { + return percent.Encode(s, "/") +} +var filenameUnescape = percent.Decode + type fileKeyring struct { dir string passwordFunc PromptFunc @@ -73,12 +79,12 @@ func (k *fileKeyring) unlock() error { } func (k *fileKeyring) Get(key string) (Item, error) { - dir, err := k.resolveDir() + filename, err := k.filename(key) if err != nil { return Item{}, err } - bytes, err := ioutil.ReadFile(filepath.Join(dir, key)) + bytes, err := ioutil.ReadFile(filename) if os.IsNotExist(err) { return Item{}, ErrKeyNotFound } else if err != nil { @@ -100,13 +106,33 @@ func (k *fileKeyring) Get(key string) (Item, error) { return decoded, err } -func (k *fileKeyring) Set(i Item) error { - bytes, err := json.Marshal(i) +func (k *fileKeyring) GetMetadata(key string) (Metadata, error) { + filename, err := k.filename(key) if err != nil { - return err + return Metadata{}, err } - dir, err := k.resolveDir() + stat, err := os.Stat(filename) + if os.IsNotExist(err) { + return Metadata{}, ErrKeyNotFound + } else if err != nil { + return Metadata{}, err + } + + // For the File provider, all internal data is encrypted, not just the + // credentials. Thus we only have the timestamps. Return a nil *Item. + // + // If we want to change this ... how portable are extended file attributes + // these days? Would it break user expectations of the security model to + // leak data into those? I'm hesitant to do so. + + return Metadata{ + ModificationTime: stat.ModTime(), + }, nil +} + +func (k *fileKeyring) Set(i Item) error { + bytes, err := json.Marshal(i) if err != nil { return err } @@ -123,16 +149,29 @@ func (k *fileKeyring) Set(i Item) error { return err } - return ioutil.WriteFile(filepath.Join(dir, i.Key), []byte(token), 0600) + filename, err := k.filename(i.Key) + if err != nil { + return err + } + return ioutil.WriteFile(filename, []byte(token), 0600) } -func (k *fileKeyring) Remove(key string) error { +func (k *fileKeyring) filename(key string) (string, error) { dir, err := k.resolveDir() + if err != nil { + return "", err + } + + return filepath.Join(dir, filenameEscape(key)), nil +} + +func (k *fileKeyring) Remove(key string) error { + filename, err := k.filename(key) if err != nil { return err } - return os.Remove(filepath.Join(dir, key)) + return os.Remove(filename) } func (k *fileKeyring) Keys() ([]string, error) { @@ -144,7 +183,7 @@ func (k *fileKeyring) Keys() ([]string, error) { var keys = []string{} files, _ := ioutil.ReadDir(dir) for _, f := range files { - keys = append(keys, f.Name()) + keys = append(keys, filenameUnescape(f.Name())) } return keys, nil diff --git a/vendor/github.com/99designs/keyring/go.mod b/vendor/github.com/99designs/keyring/go.mod index 37bb6c7..c80b402 100644 --- a/vendor/github.com/99designs/keyring/go.mod +++ b/vendor/github.com/99designs/keyring/go.mod @@ -1,17 +1,20 @@ module github.com/99designs/keyring -go 1.12 +go 1.14 require ( github.com/danieljoos/wincred v1.0.2 - github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a - github.com/godbus/dbus v4.1.0+incompatible + github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c - github.com/keybase/go-keychain v0.0.0-20190423185029-8441f7257eb1 + github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d github.com/kr/pretty v0.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 + github.com/mtibben/percent v0.2.1 github.com/stretchr/objx v0.2.0 // indirect - golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f - golang.org/x/sys v0.0.0-20190516110030-61b9204099cb // indirect + golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 + golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) + +replace github.com/keybase/go-keychain => github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 diff --git a/vendor/github.com/99designs/keyring/go.sum b/vendor/github.com/99designs/keyring/go.sum index 78bdaa4..ab8baf5 100644 --- a/vendor/github.com/99designs/keyring/go.sum +++ b/vendor/github.com/99designs/keyring/go.sum @@ -1,17 +1,17 @@ +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU= github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a h1:mq+R6XEM6lJX5VlLyZIrUSP8tSuJp82xTK89hvBwJbU= -github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= -github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4= -github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ= +github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= -github.com/keybase/go-keychain v0.0.0-20190423185029-8441f7257eb1 h1:ClrUvxpmRAr0TdWbWxApjTTjAQ9W2u4N4bJxHQTpwuE= -github.com/keybase/go-keychain v0.0.0-20190423185029-8441f7257eb1/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -19,6 +19,8 @@ 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/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= @@ -28,13 +30,13 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo= -golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190516110030-61b9204099cb h1:k07iPOt0d6nEnwXF+kHB+iEg+WSuKe/SOQuFM2QoD+E= -golang.org/x/sys v0.0.0-20190516110030-61b9204099cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI= +golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/vendor/github.com/99designs/keyring/keychain.go b/vendor/github.com/99designs/keyring/keychain.go index 96fd9e3..9caf54d 100644 --- a/vendor/github.com/99designs/keyring/keychain.go +++ b/vendor/github.com/99designs/keyring/keychain.go @@ -3,6 +3,7 @@ package keyring import ( + "errors" "fmt" gokeychain "github.com/keybase/go-keychain" @@ -77,6 +78,70 @@ func (k *keychain) Get(key string) (Item, error) { return item, nil } +func (k *keychain) GetMetadata(key string) (Metadata, error) { + query := gokeychain.NewItem() + query.SetSecClass(gokeychain.SecClassGenericPassword) + query.SetService(k.service) + query.SetAccount(key) + query.SetMatchLimit(gokeychain.MatchLimitOne) + query.SetReturnAttributes(true) + query.SetReturnData(false) + query.SetReturnRef(true) + + debugf("Querying keychain for metadata of service=%q, account=%q, keychain=%q", k.service, key, k.path) + results, err := gokeychain.QueryItem(query) + if err == gokeychain.ErrorItemNotFound || len(results) == 0 { + debugf("No results found") + return Metadata{}, ErrKeyNotFound + } else if err != nil { + debugf("Error: %#v", err) + return Metadata{}, err + } + + md := Metadata{ + Item: &Item{ + Key: key, + Label: results[0].Label, + Description: results[0].Description, + }, + ModificationTime: results[0].ModificationDate, + } + + debugf("Found metadata for %q", md.Item.Label) + + return md, nil +} + +func (k *keychain) updateItem(kc gokeychain.Keychain, kcItem gokeychain.Item, account string) error { + queryItem := gokeychain.NewItem() + queryItem.SetSecClass(gokeychain.SecClassGenericPassword) + queryItem.SetService(k.service) + queryItem.SetAccount(account) + queryItem.SetMatchLimit(gokeychain.MatchLimitOne) + queryItem.SetReturnAttributes(true) + + if k.path != "" { + queryItem.SetMatchSearchList(kc) + } + + results, err := gokeychain.QueryItem(queryItem) + if err != nil { + return fmt.Errorf("Failed to query keychain: %v", err) + } + if len(results) == 0 { + return errors.New("no results") + } + + // Don't call SetAccess() as this will cause multiple prompts on update, even when we are not updating the AccessList + kcItem.SetAccess(nil) + + if err := gokeychain.UpdateItem(queryItem, kcItem); err != nil { + return fmt.Errorf("Failed to update item in keychain: %v", err) + } + + return nil +} + func (k *keychain) Set(item Item) error { var kc gokeychain.Keychain @@ -112,13 +177,13 @@ func (k *keychain) Set(item Item) error { isTrusted := k.isTrusted && !item.KeychainNotTrustApplication if isTrusted { - debugf("Keychain item trusts aws-vault") + debugf("Keychain item trusts keyring") kcItem.SetAccess(&gokeychain.Access{ Label: item.Label, TrustedApplications: nil, }) } else { - debugf("Keychain item doesn't trust aws-vault") + debugf("Keychain item doesn't trust keyring") kcItem.SetAccess(&gokeychain.Access{ Label: item.Label, TrustedApplications: []string{}, @@ -127,23 +192,15 @@ func (k *keychain) Set(item Item) error { debugf("Adding service=%q, label=%q, account=%q, trusted=%v to osx keychain %q", k.service, item.Label, item.Key, isTrusted, k.path) - if err := gokeychain.AddItem(kcItem); err == gokeychain.ErrorDuplicateItem { - debugf("Item already exists, deleting") - delItem := gokeychain.NewItem() - delItem.SetSecClass(gokeychain.SecClassGenericPassword) - delItem.SetService(k.service) - delItem.SetAccount(item.Key) + err := gokeychain.AddItem(kcItem) - if k.path != "" { - delItem.SetMatchSearchList(kc) - } - - if err = gokeychain.DeleteItem(delItem); err != nil { - return fmt.Errorf("Error deleting existing item: %v", err) - } + if err == gokeychain.ErrorDuplicateItem { + debugf("Item already exists, updating") + err = k.updateItem(kc, kcItem, item.Key) + } - debugf("Adding item again") - return gokeychain.AddItem(kcItem) + if err != nil { + return err } return nil @@ -159,6 +216,9 @@ func (k *keychain) Remove(key string) error { kc := gokeychain.NewWithPath(k.path) if err := kc.Status(); err != nil { + if err == gokeychain.ErrorNoSuchKeychain { + return ErrKeyNotFound + } return err } @@ -180,6 +240,9 @@ func (k *keychain) Keys() ([]string, error) { kc := gokeychain.NewWithPath(k.path) if err := kc.Status(); err != nil { + if err == gokeychain.ErrorNoSuchKeychain { + return []string{}, nil + } return nil, err } diff --git a/vendor/github.com/99designs/keyring/keyring.go b/vendor/github.com/99designs/keyring/keyring.go index 341abe7..5349b6f 100644 --- a/vendor/github.com/99designs/keyring/keyring.go +++ b/vendor/github.com/99designs/keyring/keyring.go @@ -6,6 +6,7 @@ package keyring import ( "errors" "log" + "time" ) // A BackendType is an identifier for a credential storage service @@ -84,10 +85,22 @@ type Item struct { KeychainNotSynchronizable bool } +// Metadata is information about a thing stored on the keyring; retrieving +// metadata must not require authentication. The embedded Item should be +// filled in with an empty Data field. +// It's allowed for Item to be a nil pointer, indicating that all we +// have is the timestamps. +type Metadata struct { + *Item + ModificationTime time.Time +} + // Keyring provides the uniform interface over the underlying backends type Keyring interface { // Returns an Item matching the key or ErrKeyNotFound Get(key string) (Item, error) + // Returns the non-secret parts of an Item + GetMetadata(key string) (Metadata, error) // Stores an Item on the keyring Set(item Item) error // Removes the item with matching key @@ -102,6 +115,10 @@ var ErrNoAvailImpl = errors.New("Specified keyring backend not available") // ErrKeyNotFound is returned by Keyring Get when the item is not on the keyring var ErrKeyNotFound = errors.New("The specified item could not be found in the keyring") +// ErrMetadataNeedsCredentials is returned when Metadata is called against a +// backend which requires credentials even to see metadata. +var ErrMetadataNeedsCredentials = errors.New("The keyring backend requires credentials for metadata access") + var ( // Debug specifies whether to print debugging output Debug bool diff --git a/vendor/github.com/99designs/keyring/kwallet.go b/vendor/github.com/99designs/keyring/kwallet.go index b4a01a3..1e5a16e 100644 --- a/vendor/github.com/99designs/keyring/kwallet.go +++ b/vendor/github.com/99designs/keyring/kwallet.go @@ -10,8 +10,8 @@ import ( ) const ( - dbusServiceName = "org.kde.kwalletd" - dbusPath = "/modules/kwalletd" + dbusServiceName = "org.kde.kwalletd5" + dbusPath = "/modules/kwalletd5" ) func init() { @@ -100,6 +100,16 @@ func (k *kwalletKeyring) Get(key string) (Item, error) { return item, nil } +// GetMetadata for kwallet returns an error indicating that it's unsupported +// for this backend. +// +// The only APIs found around KWallet are for retrieving content, no indication +// found in docs for methods to use to retrieve metadata without needing unlock +// credentials. +func (k *kwalletKeyring) GetMetadata(_ string) (Metadata, error) { + return Metadata{}, ErrMetadataNeedsCredentials +} + func (k *kwalletKeyring) Set(item Item) error { err := k.openWallet() if err != nil { diff --git a/vendor/github.com/99designs/keyring/libsecret.go b/vendor/github.com/99designs/keyring/libsecret.go index 2b0ae64..ca4913d 100644 --- a/vendor/github.com/99designs/keyring/libsecret.go +++ b/vendor/github.com/99designs/keyring/libsecret.go @@ -4,7 +4,7 @@ package keyring import ( "encoding/json" - "fmt" + "errors" "github.com/godbus/dbus" "github.com/gsterjov/go-libsecret" @@ -54,6 +54,8 @@ func (e *secretsError) Error() string { return e.message } +var errCollectionNotFound = errors.New("The collection does not exist. Please add a key first") + func (k *secretsKeyring) openSecrets() error { session, err := k.service.Open() if err != nil { @@ -85,10 +87,11 @@ func (k *secretsKeyring) openCollection() error { } if k.collection == nil { - return &secretsError{fmt.Sprintf( - "The collection %q does not exist. Please add a key first", - k.name, - )} + return errCollectionNotFound + // return &secretsError{fmt.Sprintf( + // "The collection %q does not exist. Please add a key first", + // k.name, + // )} } return nil @@ -96,6 +99,9 @@ func (k *secretsKeyring) openCollection() error { func (k *secretsKeyring) Get(key string) (Item, error) { if err := k.openCollection(); err != nil { + if err == errCollectionNotFound { + return Item{}, ErrKeyNotFound + } return Item{}, err } @@ -105,7 +111,7 @@ func (k *secretsKeyring) Get(key string) (Item, error) { } if len(items) == 0 { - return Item{}, err + return Item{}, ErrKeyNotFound } // use the first item whenever there are multiples @@ -128,7 +134,7 @@ func (k *secretsKeyring) Get(key string) (Item, error) { return Item{}, err } - // pack the secret into the aws-vault item + // pack the secret into the item var ret Item if err = json.Unmarshal(secret.Value, &ret); err != nil { return Item{}, err @@ -137,6 +143,18 @@ func (k *secretsKeyring) Get(key string) (Item, error) { return ret, err } +// GetMetadata for libsecret returns an error indicating that it's unsupported +// for this backend. +// +// libsecret actually implements a metadata system which we could use, "Secret +// Attributes"; I found no indication in documentation of anything like an +// automatically maintained last-modification timestamp, so to use this we'd +// need to have a SetMetadata API too. Which we're not yet doing, but feel +// free to contribute patches. +func (k *secretsKeyring) GetMetadata(key string) (Metadata, error) { + return Metadata{}, ErrMetadataNeedsCredentials +} + func (k *secretsKeyring) Set(item Item) error { err := k.openSecrets() if err != nil { @@ -153,25 +171,17 @@ func (k *secretsKeyring) Set(item Item) error { k.collection = collection } - // create the new item - data, err := json.Marshal(item) - if err != nil { + if err := k.ensureCollectionUnlocked(); err != nil { return err } - secret := libsecret.NewSecret(k.session, []byte{}, data, "application/json") - - // unlock the collection first - locked, err := k.collection.Locked() + // create the new item + data, err := json.Marshal(item) if err != nil { return err } - if locked { - if err := k.service.Unlock(k.collection); err != nil { - return err - } - } + secret := libsecret.NewSecret(k.session, []byte{}, data, "application/json") if _, err := k.collection.CreateItem(item.Key, secret, true); err != nil { return err @@ -181,8 +191,10 @@ func (k *secretsKeyring) Set(item Item) error { } func (k *secretsKeyring) Remove(key string) error { - err := k.openCollection() - if err != nil { + if err := k.openCollection(); err != nil { + if err == errCollectionNotFound { + return ErrKeyNotFound + } return err } @@ -219,24 +231,47 @@ func (k *secretsKeyring) Remove(key string) error { } func (k *secretsKeyring) Keys() ([]string, error) { - err := k.openCollection() - if err != nil { - return []string{}, err + if err := k.openCollection(); err != nil { + if err == errCollectionNotFound { + return []string{}, nil + } + return nil, err + } + if err := k.ensureCollectionUnlocked(); err != nil { + return nil, err } - items, err := k.collection.Items() if err != nil { - return []string{}, err + return nil, err } - keys := []string{} - for _, item := range items { label, err := item.Label() if err == nil { keys = append(keys, label) + } else { + // err is being silently ignored here, not sure if that's good or bad } } - return keys, nil } + +// deleteCollection deletes the keyring's collection if it exists. This is mainly to support testing. +func (k *secretsKeyring) deleteCollection() error { + if err := k.openCollection(); err != nil { + return err + } + return k.collection.Delete() +} + +// unlock the collection if it's locked +func (k *secretsKeyring) ensureCollectionUnlocked() error { + locked, err := k.collection.Locked() + if err != nil { + return err + } + if !locked { + return nil + } + return k.service.Unlock(k.collection) +} diff --git a/vendor/github.com/99designs/keyring/pass.go b/vendor/github.com/99designs/keyring/pass.go index 3509e23..bfe796c 100644 --- a/vendor/github.com/99designs/keyring/pass.go +++ b/vendor/github.com/99designs/keyring/pass.go @@ -4,7 +4,6 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -52,6 +51,10 @@ func (k *passKeyring) pass(args ...string) (*exec.Cmd, error) { } func (k *passKeyring) Get(key string) (Item, error) { + if !k.itemExists(key) { + return Item{}, ErrKeyNotFound + } + name := filepath.Join(k.prefix, key) cmd, err := k.pass("show", name) if err != nil { @@ -69,6 +72,10 @@ func (k *passKeyring) Get(key string) (Item, error) { return decoded, err } +func (k *passKeyring) GetMetadata(key string) (Metadata, error) { + return Metadata{}, nil +} + func (k *passKeyring) Set(i Item) error { bytes, err := json.Marshal(i) if err != nil { @@ -92,6 +99,10 @@ func (k *passKeyring) Set(i Item) error { } func (k *passKeyring) Remove(key string) error { + if !k.itemExists(key) { + return ErrKeyNotFound + } + name := filepath.Join(k.prefix, key) cmd, err := k.pass("rm", "-f", name) if err != nil { @@ -106,6 +117,15 @@ func (k *passKeyring) Remove(key string) error { return nil } +func (k *passKeyring) itemExists(key string) bool { + var path = filepath.Join(k.dir, k.prefix, key+".gpg") + _, err := os.Stat(path) + if err != nil { + return false + } + return true +} + func (k *passKeyring) Keys() ([]string, error) { var keys = []string{} var path = filepath.Join(k.dir, k.prefix) @@ -121,18 +141,20 @@ func (k *passKeyring) Keys() ([]string, error) { return keys, fmt.Errorf("%s is not a directory", path) } - files, err := ioutil.ReadDir(path) - if err != nil { - return keys, err - } + err = filepath.Walk(path, func(p string, info os.FileInfo, err error) error { + if err != nil { + return err + } - for _, f := range files { - if filepath.Ext(f.Name()) == ".gpg" { - name := filepath.Base(f.Name()) + if !info.IsDir() && filepath.Ext(p) == ".gpg" { + name := strings.TrimPrefix(p, path) + if name[0] == os.PathSeparator { + name = name[1:] + } keys = append(keys, name[:len(name)-4]) - } - } + return nil + }) - return keys, nil + return keys, err } diff --git a/vendor/github.com/99designs/keyring/wincred.go b/vendor/github.com/99designs/keyring/wincred.go index 5b6d59d..b3dc01a 100644 --- a/vendor/github.com/99designs/keyring/wincred.go +++ b/vendor/github.com/99designs/keyring/wincred.go @@ -9,7 +9,8 @@ import ( ) type windowsKeyring struct { - name string + name string + prefix string } func init() { @@ -19,8 +20,14 @@ func init() { name = "default" } + prefix := cfg.WinCredPrefix + if prefix == "" { + prefix = "keyring" + } + return &windowsKeyring{ - name: name, + name: name, + prefix: prefix, }, nil }) } @@ -42,6 +49,13 @@ func (k *windowsKeyring) Get(key string) (Item, error) { return item, nil } +// GetMetadata for pass returns an error indicating that it's unsupported +// for this backend. +// TODO: This is a stub. Look into whether pass would support metadata in a usable way for keyring. +func (k *windowsKeyring) GetMetadata(_ string) (Metadata, error) { + return Metadata{}, ErrMetadataNeedsCredentials +} + func (k *windowsKeyring) Set(item Item) error { cred := wincred.NewGenericCredential(k.credentialName(item.Key)) cred.CredentialBlob = item.Data @@ -75,5 +89,5 @@ func (k *windowsKeyring) Keys() ([]string, error) { } func (k *windowsKeyring) credentialName(key string) string { - return "aws-vault:" + k.name + ":" + key + return k.prefix + ":" + k.name + ":" + key } diff --git a/vendor/github.com/dvsekhvalnov/jose2go/README.md b/vendor/github.com/dvsekhvalnov/jose2go/README.md index 2c3d76b..9df801c 100644 --- a/vendor/github.com/dvsekhvalnov/jose2go/README.md +++ b/vendor/github.com/dvsekhvalnov/jose2go/README.md @@ -12,9 +12,11 @@ Extensively unit tested and cross tested (100+ tests) for compatibility with [jo ## Status -Used in production. GA ready. Current version is 1.4. +Used in production. GA ready. Current version is 1.5. ## Important +v1.5 bug fix release + v1.4 changes default behavior of inserting `typ=JWT` header if not overriden. As of 1.4 no extra headers added by library automatically. To mimic pre 1.4 behaviour use: ```Go diff --git a/vendor/github.com/dvsekhvalnov/jose2go/kdf/pbkdf2.go b/vendor/github.com/dvsekhvalnov/jose2go/kdf/pbkdf2.go index b09cd2c..aec58e7 100644 --- a/vendor/github.com/dvsekhvalnov/jose2go/kdf/pbkdf2.go +++ b/vendor/github.com/dvsekhvalnov/jose2go/kdf/pbkdf2.go @@ -4,15 +4,16 @@ package kdf import ( "crypto/hmac" "fmt" - "github.com/dvsekhvalnov/jose2go/arrays" "hash" "math" + + "github.com/dvsekhvalnov/jose2go/arrays" ) // DerivePBKDF2 implements Password Based Key Derivation Function 2, RFC 2898. Derives key of keyBitLength size, given password, salt, iteration count and hash function -func DerivePBKDF2(password, salt []byte, iterationCount, keyBitLength int, h hash.Hash) []byte { +func DerivePBKDF2(password, salt []byte, iterationCount, keyBitLength int, h func() hash.Hash) []byte { - prf := hmac.New(func() hash.Hash { return h }, password) + prf := hmac.New(h, password) hLen := prf.Size() dkLen := keyBitLength >> 3 //size of derived key in bytes diff --git a/vendor/github.com/dvsekhvalnov/jose2go/pbse2_hmac_aeskw.go b/vendor/github.com/dvsekhvalnov/jose2go/pbse2_hmac_aeskw.go index 395992c..baeaf9c 100644 --- a/vendor/github.com/dvsekhvalnov/jose2go/pbse2_hmac_aeskw.go +++ b/vendor/github.com/dvsekhvalnov/jose2go/pbse2_hmac_aeskw.go @@ -1,96 +1,103 @@ package jose import ( + "crypto/sha256" + "crypto/sha512" "errors" + "hash" + "github.com/dvsekhvalnov/jose2go/arrays" "github.com/dvsekhvalnov/jose2go/base64url" "github.com/dvsekhvalnov/jose2go/kdf" - "crypto/sha256" - "crypto/sha512" - "hash" ) func init() { - RegisterJwa(&Pbse2HmacAesKW{keySizeBits: 128,aesKW: &AesKW{ keySizeBits: 128}}) - RegisterJwa(&Pbse2HmacAesKW{keySizeBits: 192,aesKW: &AesKW{ keySizeBits: 192}}) - RegisterJwa(&Pbse2HmacAesKW{keySizeBits: 256,aesKW: &AesKW{ keySizeBits: 256}}) + RegisterJwa(&Pbse2HmacAesKW{keySizeBits: 128, aesKW: &AesKW{keySizeBits: 128}}) + RegisterJwa(&Pbse2HmacAesKW{keySizeBits: 192, aesKW: &AesKW{keySizeBits: 192}}) + RegisterJwa(&Pbse2HmacAesKW{keySizeBits: 256, aesKW: &AesKW{keySizeBits: 256}}) } // PBSE2 with HMAC key management algorithm implementation -type Pbse2HmacAesKW struct{ +type Pbse2HmacAesKW struct { keySizeBits int - aesKW JwaAlgorithm + aesKW JwaAlgorithm } func (alg *Pbse2HmacAesKW) Name() string { switch alg.keySizeBits { - case 128: return PBES2_HS256_A128KW - case 192: return PBES2_HS384_A192KW - default: return PBES2_HS512_A256KW + case 128: + return PBES2_HS256_A128KW + case 192: + return PBES2_HS384_A192KW + default: + return PBES2_HS512_A256KW } } -func (alg *Pbse2HmacAesKW) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { - if passphrase,ok:=key.(string); ok { +func (alg *Pbse2HmacAesKW) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { + if passphrase, ok := key.(string); ok { algId := []byte(header["alg"].(string)) - - iterationCount := 8192; - var saltInput []byte - - if saltInput,err = arrays.Random(12);err!=nil { - return nil,nil,err + + iterationCount := 8192 + var saltInput []byte + + if saltInput, err = arrays.Random(12); err != nil { + return nil, nil, err } - header["p2c"] = iterationCount; - header["p2s"] = base64url.Encode(saltInput) - - salt := arrays.Concat(algId, []byte{0}, saltInput); - - kek := kdf.DerivePBKDF2([]byte(passphrase), salt, iterationCount, alg.keySizeBits, alg.prf()) + header["p2c"] = iterationCount + header["p2s"] = base64url.Encode(saltInput) + + salt := arrays.Concat(algId, []byte{0}, saltInput) + + kek := kdf.DerivePBKDF2([]byte(passphrase), salt, iterationCount, alg.keySizeBits, alg.prf) return alg.aesKW.WrapNewKey(cekSizeBits, kek, header) } - return nil,nil,errors.New("Pbse2HmacAesKW.WrapNewKey(): expected key to be 'string' array") + return nil, nil, errors.New("Pbse2HmacAesKW.WrapNewKey(): expected key to be 'string' array") } -func (alg *Pbse2HmacAesKW) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { - - if passphrase,ok:=key.(string); ok { - +func (alg *Pbse2HmacAesKW) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { + + if passphrase, ok := key.(string); ok { + var p2s string var p2c float64 - - if p2c,ok = header["p2c"].(float64);!ok { - return nil,errors.New("Pbse2HmacAesKW.Unwrap(): expected 'p2c' param in JWT header, but was not found.") + + if p2c, ok = header["p2c"].(float64); !ok { + return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected 'p2c' param in JWT header, but was not found.") } - - if p2s,ok = header["p2s"].(string);!ok { - return nil,errors.New("Pbse2HmacAesKW.Unwrap(): expected 'p2s' param in JWT header, but was not found.") + + if p2s, ok = header["p2s"].(string); !ok { + return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected 'p2s' param in JWT header, but was not found.") } - + var saltInput []byte - + algId := []byte(header["alg"].(string)) - - if saltInput,err = base64url.Decode(p2s);err!=nil { - return nil,err + + if saltInput, err = base64url.Decode(p2s); err != nil { + return nil, err } - - salt := arrays.Concat(algId,[]byte{0},saltInput) - - kek := kdf.DerivePBKDF2([]byte(passphrase), salt, int(p2c), alg.keySizeBits, alg.prf()) - + + salt := arrays.Concat(algId, []byte{0}, saltInput) + + kek := kdf.DerivePBKDF2([]byte(passphrase), salt, int(p2c), alg.keySizeBits, alg.prf) + return alg.aesKW.Unwrap(encryptedCek, kek, cekSizeBits, header) } - - return nil,errors.New("Pbse2HmacAesKW.Unwrap(): expected key to be 'string' array") + + return nil, errors.New("Pbse2HmacAesKW.Unwrap(): expected key to be 'string' array") } func (alg *Pbse2HmacAesKW) prf() hash.Hash { switch alg.keySizeBits { - case 128: return sha256.New() - case 192: return sha512.New384() - default: return sha512.New() + case 128: + return sha256.New() + case 192: + return sha512.New384() + default: + return sha512.New() } -} \ No newline at end of file +} diff --git a/vendor/github.com/godbus/dbus/.travis.yml b/vendor/github.com/godbus/dbus/.travis.yml index 2e1bbb7..752dc0a 100644 --- a/vendor/github.com/godbus/dbus/.travis.yml +++ b/vendor/github.com/godbus/dbus/.travis.yml @@ -1,11 +1,13 @@ -dist: precise +dist: xenial language: go go_import_path: github.com/godbus/dbus sudo: true go: - - 1.6.3 - 1.7.3 + - 1.8.7 + - 1.9.5 + - 1.10.1 - tip env: @@ -38,3 +40,7 @@ addons: - dbus-x11 before_install: + +script: + - go test -v -race ./... # Run all the tests with the race detector enabled + - go vet ./... # go vet is the official Go static analyzer diff --git a/vendor/github.com/godbus/dbus/README.markdown b/vendor/github.com/godbus/dbus/README.markdown index d37f4e2..fd29648 100644 --- a/vendor/github.com/godbus/dbus/README.markdown +++ b/vendor/github.com/godbus/dbus/README.markdown @@ -14,7 +14,7 @@ D-Bus message bus system. ### Installation -This packages requires Go 1.1. If you installed it and set up your GOPATH, just run: +This packages requires Go 1.7. If you installed it and set up your GOPATH, just run: ``` go get github.com/godbus/dbus diff --git a/vendor/github.com/godbus/dbus/auth.go b/vendor/github.com/godbus/dbus/auth.go index 98017b6..37f5491 100644 --- a/vendor/github.com/godbus/dbus/auth.go +++ b/vendor/github.com/godbus/dbus/auth.go @@ -77,7 +77,7 @@ func (conn *Conn) Auth(methods []Auth) error { for _, m := range methods { if name, data, status := m.FirstData(); bytes.Equal(v, name) { var ok bool - err = authWriteLine(conn.transport, []byte("AUTH"), []byte(v), data) + err = authWriteLine(conn.transport, []byte("AUTH"), v, data) if err != nil { return err } @@ -116,7 +116,6 @@ func (conn *Conn) Auth(methods []Auth) error { return err } go conn.inWorker() - go conn.outWorker() return nil } } diff --git a/vendor/github.com/godbus/dbus/auth_anonymous.go b/vendor/github.com/godbus/dbus/auth_anonymous.go new file mode 100644 index 0000000..75f3ad3 --- /dev/null +++ b/vendor/github.com/godbus/dbus/auth_anonymous.go @@ -0,0 +1,16 @@ +package dbus + +// AuthAnonymous returns an Auth that uses the ANONYMOUS mechanism. +func AuthAnonymous() Auth { + return &authAnonymous{} +} + +type authAnonymous struct{} + +func (a *authAnonymous) FirstData() (name, resp []byte, status AuthStatus) { + return []byte("ANONYMOUS"), nil, AuthOk +} + +func (a *authAnonymous) HandleData(data []byte) (resp []byte, status AuthStatus) { + return nil, AuthError +} diff --git a/vendor/github.com/godbus/dbus/call.go b/vendor/github.com/godbus/dbus/call.go index ba6e73f..2cb1890 100644 --- a/vendor/github.com/godbus/dbus/call.go +++ b/vendor/github.com/godbus/dbus/call.go @@ -1,9 +1,12 @@ package dbus import ( + "context" "errors" ) +var errSignature = errors.New("dbus: mismatched signature") + // Call represents a pending or completed method call. type Call struct { Destination string @@ -20,9 +23,25 @@ type Call struct { // Holds the response once the call is done. Body []interface{} + + // tracks context and canceler + ctx context.Context + ctxCanceler context.CancelFunc } -var errSignature = errors.New("dbus: mismatched signature") +func (c *Call) Context() context.Context { + if c.ctx == nil { + return context.Background() + } + + return c.ctx +} + +func (c *Call) ContextCancel() { + if c.ctxCanceler != nil { + c.ctxCanceler() + } +} // Store stores the body of the reply into the provided pointers. It returns // an error if the signatures of the body and retvalues don't match, or if @@ -34,3 +53,8 @@ func (c *Call) Store(retvalues ...interface{}) error { return Store(c.Body, retvalues...) } + +func (c *Call) done() { + c.Done <- c + c.ContextCancel() +} diff --git a/vendor/github.com/godbus/dbus/conn.go b/vendor/github.com/godbus/dbus/conn.go index 5720e2e..174e844 100644 --- a/vendor/github.com/godbus/dbus/conn.go +++ b/vendor/github.com/godbus/dbus/conn.go @@ -1,6 +1,7 @@ package dbus import ( + "context" "errors" "io" "os" @@ -14,7 +15,6 @@ var ( systemBusLck sync.Mutex sessionBus *Conn sessionBusLck sync.Mutex - sessionEnvLck sync.Mutex ) // ErrClosed is the error returned by calls on a closed connection. @@ -35,23 +35,13 @@ type Conn struct { unixFD bool uuid string - names []string - namesLck sync.RWMutex - - serialLck sync.Mutex - nextSerial uint32 - serialUsed map[uint32]bool - - calls map[uint32]*Call - callsLck sync.RWMutex - - handler Handler - - out chan *Message - closed bool - outLck sync.RWMutex - + handler Handler signalHandler SignalHandler + serialGen SerialGenerator + + names *nameTracker + calls *callTracker + outHandler *outputHandler eavesdropped chan<- *Message eavesdroppedLck sync.Mutex @@ -87,32 +77,31 @@ func SessionBus() (conn *Conn, err error) { } func getSessionBusAddress() (string, error) { - sessionEnvLck.Lock() - defer sessionEnvLck.Unlock() - address := os.Getenv("DBUS_SESSION_BUS_ADDRESS") - if address != "" && address != "autolaunch:" { + if address := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); address != "" && address != "autolaunch:" { + return address, nil + + } else if address := tryDiscoverDbusSessionBusAddress(); address != "" { + os.Setenv("DBUS_SESSION_BUS_ADDRESS", address) return address, nil } return getSessionBusPlatformAddress() } // SessionBusPrivate returns a new private connection to the session bus. -func SessionBusPrivate() (*Conn, error) { +func SessionBusPrivate(opts ...ConnOption) (*Conn, error) { address, err := getSessionBusAddress() if err != nil { return nil, err } - return Dial(address) + return Dial(address, opts...) } // SessionBusPrivate returns a new private connection to the session bus. +// +// Deprecated: use SessionBusPrivate with options instead. func SessionBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) { - address, err := getSessionBusAddress() - if err != nil { - return nil, err - } - return DialHandler(address, handler, signalHandler) + return SessionBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler)) } // SystemBus returns a shared connection to the system bus, connecting to it if @@ -145,53 +134,95 @@ func SystemBus() (conn *Conn, err error) { } // SystemBusPrivate returns a new private connection to the system bus. -func SystemBusPrivate() (*Conn, error) { - return Dial(getSystemBusPlatformAddress()) +// Note: this connection is not ready to use. One must perform Auth and Hello +// on the connection before it is useable. +func SystemBusPrivate(opts ...ConnOption) (*Conn, error) { + return Dial(getSystemBusPlatformAddress(), opts...) } // SystemBusPrivateHandler returns a new private connection to the system bus, using the provided handlers. +// +// Deprecated: use SystemBusPrivate with options instead. func SystemBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) { - return DialHandler(getSystemBusPlatformAddress(), handler, signalHandler) + return SystemBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler)) } // Dial establishes a new private connection to the message bus specified by address. -func Dial(address string) (*Conn, error) { +func Dial(address string, opts ...ConnOption) (*Conn, error) { tr, err := getTransport(address) if err != nil { return nil, err } - return newConn(tr, NewDefaultHandler(), NewDefaultSignalHandler()) + return newConn(tr, opts...) } // DialHandler establishes a new private connection to the message bus specified by address, using the supplied handlers. +// +// Deprecated: use Dial with options instead. func DialHandler(address string, handler Handler, signalHandler SignalHandler) (*Conn, error) { - tr, err := getTransport(address) - if err != nil { - return nil, err + return Dial(address, WithSignalHandler(signalHandler)) +} + +// ConnOption is a connection option. +type ConnOption func(conn *Conn) error + +// WithHandler overrides the default handler. +func WithHandler(handler Handler) ConnOption { + return func(conn *Conn) error { + conn.handler = handler + return nil + } +} + +// WithSignalHandler overrides the default signal handler. +func WithSignalHandler(handler SignalHandler) ConnOption { + return func(conn *Conn) error { + conn.signalHandler = handler + return nil + } +} + +// WithSerialGenerator overrides the default signals generator. +func WithSerialGenerator(gen SerialGenerator) ConnOption { + return func(conn *Conn) error { + conn.serialGen = gen + return nil } - return newConn(tr, handler, signalHandler) } // NewConn creates a new private *Conn from an already established connection. -func NewConn(conn io.ReadWriteCloser) (*Conn, error) { - return NewConnHandler(conn, NewDefaultHandler(), NewDefaultSignalHandler()) +func NewConn(conn io.ReadWriteCloser, opts ...ConnOption) (*Conn, error) { + return newConn(genericTransport{conn}, opts...) } // NewConnHandler creates a new private *Conn from an already established connection, using the supplied handlers. +// +// Deprecated: use NewConn with options instead. func NewConnHandler(conn io.ReadWriteCloser, handler Handler, signalHandler SignalHandler) (*Conn, error) { - return newConn(genericTransport{conn}, handler, signalHandler) + return NewConn(genericTransport{conn}, WithHandler(handler), WithSignalHandler(signalHandler)) } // newConn creates a new *Conn from a transport. -func newConn(tr transport, handler Handler, signalHandler SignalHandler) (*Conn, error) { +func newConn(tr transport, opts ...ConnOption) (*Conn, error) { conn := new(Conn) conn.transport = tr - conn.calls = make(map[uint32]*Call) - conn.out = make(chan *Message, 10) - conn.handler = handler - conn.signalHandler = signalHandler - conn.nextSerial = 1 - conn.serialUsed = map[uint32]bool{0: true} + for _, opt := range opts { + if err := opt(conn); err != nil { + return nil, err + } + } + conn.calls = newCallTracker() + if conn.handler == nil { + conn.handler = NewDefaultHandler() + } + if conn.signalHandler == nil { + conn.signalHandler = NewDefaultSignalHandler() + } + if conn.serialGen == nil { + conn.serialGen = newSerialGenerator() + } + conn.outHandler = &outputHandler{conn: conn} + conn.names = newNameTracker() conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus") return conn, nil } @@ -206,18 +237,7 @@ func (conn *Conn) BusObject() BusObject { // and the channels passed to Eavesdrop and Signal are closed. This method must // not be called on shared connections. func (conn *Conn) Close() error { - conn.outLck.Lock() - if conn.closed { - // inWorker calls Close on read error, the read error may - // be caused by another caller calling Close to shutdown the - // dbus connection, a double-close scenario we prevent here. - conn.outLck.Unlock() - return nil - } - close(conn.out) - conn.closed = true - conn.outLck.Unlock() - + conn.outHandler.close() if term, ok := conn.signalHandler.(Terminator); ok { term.Terminate() } @@ -251,15 +271,7 @@ func (conn *Conn) Eavesdrop(ch chan<- *Message) { // getSerial returns an unused serial. func (conn *Conn) getSerial() uint32 { - conn.serialLck.Lock() - defer conn.serialLck.Unlock() - n := conn.nextSerial - for conn.serialUsed[n] { - n++ - } - conn.serialUsed[n] = true - conn.nextSerial = n + 1 - return n + return conn.serialGen.GetSerial() } // Hello sends the initial org.freedesktop.DBus.Hello call. This method must be @@ -271,10 +283,7 @@ func (conn *Conn) Hello() error { if err != nil { return err } - conn.namesLck.Lock() - conn.names = make([]string, 1) - conn.names[0] = s - conn.namesLck.Unlock() + conn.names.acquireUniqueConnectionName(s) return nil } @@ -283,109 +292,48 @@ func (conn *Conn) Hello() error { func (conn *Conn) inWorker() { for { msg, err := conn.ReadMessage() - if err == nil { - conn.eavesdroppedLck.Lock() - if conn.eavesdropped != nil { - select { - case conn.eavesdropped <- msg: - default: - } - conn.eavesdroppedLck.Unlock() - continue - } - conn.eavesdroppedLck.Unlock() - dest, _ := msg.Headers[FieldDestination].value.(string) - found := false - if dest == "" { - found = true - } else { - conn.namesLck.RLock() - if len(conn.names) == 0 { - found = true - } - for _, v := range conn.names { - if dest == v { - found = true - break - } - } - conn.namesLck.RUnlock() - } - if !found { - // Eavesdropped a message, but no channel for it is registered. - // Ignore it. - continue - } - switch msg.Type { - case TypeMethodReply, TypeError: - serial := msg.Headers[FieldReplySerial].value.(uint32) - conn.callsLck.Lock() - if c, ok := conn.calls[serial]; ok { - if msg.Type == TypeError { - name, _ := msg.Headers[FieldErrorName].value.(string) - c.Err = Error{name, msg.Body} - } else { - c.Body = msg.Body - } - c.Done <- c - conn.serialLck.Lock() - delete(conn.serialUsed, serial) - conn.serialLck.Unlock() - delete(conn.calls, serial) - } - conn.callsLck.Unlock() - case TypeSignal: - iface := msg.Headers[FieldInterface].value.(string) - member := msg.Headers[FieldMember].value.(string) - // as per http://dbus.freedesktop.org/doc/dbus-specification.html , - // sender is optional for signals. - sender, _ := msg.Headers[FieldSender].value.(string) - if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" { - if member == "NameLost" { - // If we lost the name on the bus, remove it from our - // tracking list. - name, ok := msg.Body[0].(string) - if !ok { - panic("Unable to read the lost name") - } - conn.namesLck.Lock() - for i, v := range conn.names { - if v == name { - conn.names = append(conn.names[:i], - conn.names[i+1:]...) - } - } - conn.namesLck.Unlock() - } else if member == "NameAcquired" { - // If we acquired the name on the bus, add it to our - // tracking list. - name, ok := msg.Body[0].(string) - if !ok { - panic("Unable to read the acquired name") - } - conn.namesLck.Lock() - conn.names = append(conn.names, name) - conn.namesLck.Unlock() - } - } - conn.handleSignal(msg) - case TypeMethodCall: - go conn.handleCall(msg) + if err != nil { + if _, ok := err.(InvalidMessageError); !ok { + // Some read error occured (usually EOF); we can't really do + // anything but to shut down all stuff and returns errors to all + // pending replies. + conn.Close() + conn.calls.finalizeAllWithError(err) + return } - } else if _, ok := err.(InvalidMessageError); !ok { - // Some read error occured (usually EOF); we can't really do - // anything but to shut down all stuff and returns errors to all - // pending replies. - conn.Close() - conn.callsLck.RLock() - for _, v := range conn.calls { - v.Err = err - v.Done <- v + // invalid messages are ignored + continue + } + conn.eavesdroppedLck.Lock() + if conn.eavesdropped != nil { + select { + case conn.eavesdropped <- msg: + default: } - conn.callsLck.RUnlock() - return + conn.eavesdroppedLck.Unlock() + continue } - // invalid messages are ignored + conn.eavesdroppedLck.Unlock() + dest, _ := msg.Headers[FieldDestination].value.(string) + found := dest == "" || + !conn.names.uniqueNameIsKnown() || + conn.names.isKnownName(dest) + if !found { + // Eavesdropped a message, but no channel for it is registered. + // Ignore it. + continue + } + switch msg.Type { + case TypeError: + conn.serialGen.RetireSerial(conn.calls.handleDBusError(msg)) + case TypeMethodReply: + conn.serialGen.RetireSerial(conn.calls.handleReply(msg)) + case TypeSignal: + conn.handleSignal(msg) + case TypeMethodCall: + go conn.handleCall(msg) + } + } } @@ -395,6 +343,25 @@ func (conn *Conn) handleSignal(msg *Message) { // as per http://dbus.freedesktop.org/doc/dbus-specification.html , // sender is optional for signals. sender, _ := msg.Headers[FieldSender].value.(string) + if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" { + if member == "NameLost" { + // If we lost the name on the bus, remove it from our + // tracking list. + name, ok := msg.Body[0].(string) + if !ok { + panic("Unable to read the lost name") + } + conn.names.loseName(name) + } else if member == "NameAcquired" { + // If we acquired the name on the bus, add it to our + // tracking list. + name, ok := msg.Body[0].(string) + if !ok { + panic("Unable to read the acquired name") + } + conn.names.acquireName(name) + } + } signal := &Signal{ Sender: sender, Path: msg.Headers[FieldPath].value.(ObjectPath), @@ -408,12 +375,7 @@ func (conn *Conn) handleSignal(msg *Message) { // connection. The slice is always at least one element long, the first element // being the unique name of the connection. func (conn *Conn) Names() []string { - conn.namesLck.RLock() - // copy the slice so it can't be modified - s := make([]string, len(conn.names)) - copy(s, conn.names) - conn.namesLck.RUnlock() - return s + return conn.names.listKnownNames() } // Object returns the object identified by the given destination name and path. @@ -421,26 +383,17 @@ func (conn *Conn) Object(dest string, path ObjectPath) BusObject { return &Object{conn, dest, path} } -// outWorker runs in an own goroutine, encoding and sending messages that are -// sent to conn.out. -func (conn *Conn) outWorker() { - for msg := range conn.out { - err := conn.SendMessage(msg) - conn.callsLck.RLock() - if err != nil { - if c := conn.calls[msg.serial]; c != nil { - c.Err = err - c.Done <- c - } - conn.serialLck.Lock() - delete(conn.serialUsed, msg.serial) - conn.serialLck.Unlock() - } else if msg.Type != TypeMethodCall { - conn.serialLck.Lock() - delete(conn.serialUsed, msg.serial) - conn.serialLck.Unlock() - } - conn.callsLck.RUnlock() +func (conn *Conn) sendMessage(msg *Message) { + conn.sendMessageAndIfClosed(msg, func() {}) +} + +func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) { + err := conn.outHandler.sendAndIfClosed(msg, ifClosed) + conn.calls.handleSendError(msg, err) + if err != nil { + conn.serialGen.RetireSerial(msg.serial) + } else if msg.Type != TypeMethodCall { + conn.serialGen.RetireSerial(msg.serial) } } @@ -451,8 +404,21 @@ func (conn *Conn) outWorker() { // once the call is complete. Otherwise, ch is ignored and a Call structure is // returned of which only the Err member is valid. func (conn *Conn) Send(msg *Message, ch chan *Call) *Call { - var call *Call + return conn.send(context.Background(), msg, ch) +} +// SendWithContext acts like Send but takes a context +func (conn *Conn) SendWithContext(ctx context.Context, msg *Message, ch chan *Call) *Call { + return conn.send(ctx, msg, ch) +} + +func (conn *Conn) send(ctx context.Context, msg *Message, ch chan *Call) *Call { + if ctx == nil { + panic("nil context") + } + + var call *Call + ctx, canceler := context.WithCancel(ctx) msg.serial = conn.getSerial() if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 { if ch == nil { @@ -468,26 +434,23 @@ func (conn *Conn) Send(msg *Message, ch chan *Call) *Call { call.Method = iface + "." + member call.Args = msg.Body call.Done = ch - conn.callsLck.Lock() - conn.calls[msg.serial] = call - conn.callsLck.Unlock() - conn.outLck.RLock() - if conn.closed { - call.Err = ErrClosed - call.Done <- call - } else { - conn.out <- msg - } - conn.outLck.RUnlock() + call.ctx = ctx + call.ctxCanceler = canceler + conn.calls.track(msg.serial, call) + go func() { + <-ctx.Done() + conn.calls.handleSendError(msg, ctx.Err()) + }() + conn.sendMessageAndIfClosed(msg, func() { + conn.calls.handleSendError(msg, ErrClosed) + canceler() + }) } else { - conn.outLck.RLock() - if conn.closed { + canceler() + call = &Call{Err: nil} + conn.sendMessageAndIfClosed(msg, func() { call = &Call{Err: ErrClosed} - } else { - conn.out <- msg - call = &Call{Err: nil} - } - conn.outLck.RUnlock() + }) } return call } @@ -520,11 +483,7 @@ func (conn *Conn) sendError(err error, dest string, serial uint32) { if len(e.Body) > 0 { msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...)) } - conn.outLck.RLock() - if !conn.closed { - conn.out <- msg - } - conn.outLck.RUnlock() + conn.sendMessage(msg) } // sendReply creates a method reply message corresponding to the parameters and @@ -542,37 +501,54 @@ func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) { if len(values) > 0 { msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) } - conn.outLck.RLock() - if !conn.closed { - conn.out <- msg - } - conn.outLck.RUnlock() + conn.sendMessage(msg) } -func (conn *Conn) defaultSignalAction(fn func(h *defaultSignalHandler, ch chan<- *Signal), ch chan<- *Signal) { - if !isDefaultSignalHandler(conn.signalHandler) { - return - } - handler := conn.signalHandler.(*defaultSignalHandler) - fn(handler, ch) +// AddMatchSignal registers the given match rule to receive broadcast +// signals based on their contents. +func (conn *Conn) AddMatchSignal(options ...MatchOption) error { + options = append([]MatchOption{withMatchType("signal")}, options...) + return conn.busObj.Call( + "org.freedesktop.DBus.AddMatch", 0, + formatMatchOptions(options), + ).Store() +} + +// RemoveMatchSignal removes the first rule that matches previously registered with AddMatchSignal. +func (conn *Conn) RemoveMatchSignal(options ...MatchOption) error { + options = append([]MatchOption{withMatchType("signal")}, options...) + return conn.busObj.Call( + "org.freedesktop.DBus.RemoveMatch", 0, + formatMatchOptions(options), + ).Store() } // Signal registers the given channel to be passed all received signal messages. -// The caller has to make sure that ch is sufficiently buffered; if a message -// arrives when a write to c is not possible, it is discarded. // // Multiple of these channels can be registered at the same time. // // These channels are "overwritten" by Eavesdrop; i.e., if there currently is a // channel for eavesdropped messages, this channel receives all signals, and // none of the channels passed to Signal will receive any signals. +// +// Panics if the signal handler is not a `SignalRegistrar`. func (conn *Conn) Signal(ch chan<- *Signal) { - conn.defaultSignalAction((*defaultSignalHandler).addSignal, ch) + handler, ok := conn.signalHandler.(SignalRegistrar) + if !ok { + panic("cannot use this method with a non SignalRegistrar handler") + } + handler.AddSignal(ch) } // RemoveSignal removes the given channel from the list of the registered channels. +// +// Panics if the signal handler is not a `SignalRegistrar`. func (conn *Conn) RemoveSignal(ch chan<- *Signal) { - conn.defaultSignalAction((*defaultSignalHandler).removeSignal, ch) + handler, ok := conn.signalHandler.(SignalRegistrar) + if !ok { + panic("cannot use this method with a non SignalRegistrar handler") + } + handler.RemoveSignal(ch) } // SupportsUnixFDs returns whether the underlying transport supports passing of @@ -681,3 +657,209 @@ func getKey(s, key string) string { } return "" } + +type outputHandler struct { + conn *Conn + sendLck sync.Mutex + closed struct { + isClosed bool + lck sync.RWMutex + } +} + +func (h *outputHandler) sendAndIfClosed(msg *Message, ifClosed func()) error { + h.closed.lck.RLock() + defer h.closed.lck.RUnlock() + if h.closed.isClosed { + ifClosed() + return nil + } + h.sendLck.Lock() + defer h.sendLck.Unlock() + return h.conn.SendMessage(msg) +} + +func (h *outputHandler) close() { + h.closed.lck.Lock() + defer h.closed.lck.Unlock() + h.closed.isClosed = true +} + +type serialGenerator struct { + lck sync.Mutex + nextSerial uint32 + serialUsed map[uint32]bool +} + +func newSerialGenerator() *serialGenerator { + return &serialGenerator{ + serialUsed: map[uint32]bool{0: true}, + nextSerial: 1, + } +} + +func (gen *serialGenerator) GetSerial() uint32 { + gen.lck.Lock() + defer gen.lck.Unlock() + n := gen.nextSerial + for gen.serialUsed[n] { + n++ + } + gen.serialUsed[n] = true + gen.nextSerial = n + 1 + return n +} + +func (gen *serialGenerator) RetireSerial(serial uint32) { + gen.lck.Lock() + defer gen.lck.Unlock() + delete(gen.serialUsed, serial) +} + +type nameTracker struct { + lck sync.RWMutex + unique string + names map[string]struct{} +} + +func newNameTracker() *nameTracker { + return &nameTracker{names: map[string]struct{}{}} +} +func (tracker *nameTracker) acquireUniqueConnectionName(name string) { + tracker.lck.Lock() + defer tracker.lck.Unlock() + tracker.unique = name +} +func (tracker *nameTracker) acquireName(name string) { + tracker.lck.Lock() + defer tracker.lck.Unlock() + tracker.names[name] = struct{}{} +} +func (tracker *nameTracker) loseName(name string) { + tracker.lck.Lock() + defer tracker.lck.Unlock() + delete(tracker.names, name) +} + +func (tracker *nameTracker) uniqueNameIsKnown() bool { + tracker.lck.RLock() + defer tracker.lck.RUnlock() + return tracker.unique != "" +} +func (tracker *nameTracker) isKnownName(name string) bool { + tracker.lck.RLock() + defer tracker.lck.RUnlock() + _, ok := tracker.names[name] + return ok || name == tracker.unique +} +func (tracker *nameTracker) listKnownNames() []string { + tracker.lck.RLock() + defer tracker.lck.RUnlock() + out := make([]string, 0, len(tracker.names)+1) + out = append(out, tracker.unique) + for k := range tracker.names { + out = append(out, k) + } + return out +} + +type callTracker struct { + calls map[uint32]*Call + lck sync.RWMutex +} + +func newCallTracker() *callTracker { + return &callTracker{calls: map[uint32]*Call{}} +} + +func (tracker *callTracker) track(sn uint32, call *Call) { + tracker.lck.Lock() + tracker.calls[sn] = call + tracker.lck.Unlock() +} + +func (tracker *callTracker) handleReply(msg *Message) uint32 { + serial := msg.Headers[FieldReplySerial].value.(uint32) + tracker.lck.RLock() + _, ok := tracker.calls[serial] + tracker.lck.RUnlock() + if ok { + tracker.finalizeWithBody(serial, msg.Body) + } + return serial +} + +func (tracker *callTracker) handleDBusError(msg *Message) uint32 { + serial := msg.Headers[FieldReplySerial].value.(uint32) + tracker.lck.RLock() + _, ok := tracker.calls[serial] + tracker.lck.RUnlock() + if ok { + name, _ := msg.Headers[FieldErrorName].value.(string) + tracker.finalizeWithError(serial, Error{name, msg.Body}) + } + return serial +} + +func (tracker *callTracker) handleSendError(msg *Message, err error) { + if err == nil { + return + } + tracker.lck.RLock() + _, ok := tracker.calls[msg.serial] + tracker.lck.RUnlock() + if ok { + tracker.finalizeWithError(msg.serial, err) + } +} + +// finalize was the only func that did not strobe Done +func (tracker *callTracker) finalize(sn uint32) { + tracker.lck.Lock() + defer tracker.lck.Unlock() + c, ok := tracker.calls[sn] + if ok { + delete(tracker.calls, sn) + c.ContextCancel() + } +} + +func (tracker *callTracker) finalizeWithBody(sn uint32, body []interface{}) { + tracker.lck.Lock() + c, ok := tracker.calls[sn] + if ok { + delete(tracker.calls, sn) + } + tracker.lck.Unlock() + if ok { + c.Body = body + c.done() + } +} + +func (tracker *callTracker) finalizeWithError(sn uint32, err error) { + tracker.lck.Lock() + c, ok := tracker.calls[sn] + if ok { + delete(tracker.calls, sn) + } + tracker.lck.Unlock() + if ok { + c.Err = err + c.done() + } +} + +func (tracker *callTracker) finalizeAllWithError(err error) { + tracker.lck.Lock() + closedCalls := make([]*Call, 0, len(tracker.calls)) + for sn := range tracker.calls { + closedCalls = append(closedCalls, tracker.calls[sn]) + } + tracker.calls = map[uint32]*Call{} + tracker.lck.Unlock() + for _, call := range closedCalls { + call.Err = err + call.done() + } +} diff --git a/vendor/github.com/godbus/dbus/conn_darwin.go b/vendor/github.com/godbus/dbus/conn_darwin.go index c015f80..6e2e402 100644 --- a/vendor/github.com/godbus/dbus/conn_darwin.go +++ b/vendor/github.com/godbus/dbus/conn_darwin.go @@ -31,3 +31,7 @@ func getSystemBusPlatformAddress() string { } return defaultSystemBusAddress } + +func tryDiscoverDbusSessionBusAddress() string { + return "" +} diff --git a/vendor/github.com/godbus/dbus/conn_other.go b/vendor/github.com/godbus/dbus/conn_other.go index 254c9f2..616dcf6 100644 --- a/vendor/github.com/godbus/dbus/conn_other.go +++ b/vendor/github.com/godbus/dbus/conn_other.go @@ -6,14 +6,18 @@ import ( "bytes" "errors" "fmt" + "io/ioutil" "os" "os/exec" + "os/user" + "path" + "strings" ) -const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" +var execCommand = exec.Command func getSessionBusPlatformAddress() (string, error) { - cmd := exec.Command("dbus-launch") + cmd := execCommand("dbus-launch") b, err := cmd.CombinedOutput() if err != nil { @@ -23,7 +27,7 @@ func getSessionBusPlatformAddress() (string, error) { i := bytes.IndexByte(b, '=') j := bytes.IndexByte(b, '\n') - if i == -1 || j == -1 { + if i == -1 || j == -1 || i > j { return "", errors.New("dbus: couldn't determine address of session bus") } @@ -33,10 +37,57 @@ func getSessionBusPlatformAddress() (string, error) { return addr, nil } -func getSystemBusPlatformAddress() string { - address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") - if address != "" { - return fmt.Sprintf("unix:path=%s", address) +// tryDiscoverDbusSessionBusAddress tries to discover an existing dbus session +// and return the value of its DBUS_SESSION_BUS_ADDRESS. +// It tries different techniques employed by different operating systems, +// returning the first valid address it finds, or an empty string. +// +// * /run/user//bus if this exists, it *is* the bus socket. present on +// Ubuntu 18.04 +// * /run/user//dbus-session: if this exists, it can be parsed for the bus +// address. present on Ubuntu 16.04 +// +// See https://dbus.freedesktop.org/doc/dbus-launch.1.html +func tryDiscoverDbusSessionBusAddress() string { + if runtimeDirectory, err := getRuntimeDirectory(); err == nil { + + if runUserBusFile := path.Join(runtimeDirectory, "bus"); fileExists(runUserBusFile) { + // if /run/user//bus exists, that file itself + // *is* the unix socket, so return its path + return fmt.Sprintf("unix:path=%s", runUserBusFile) + } + if runUserSessionDbusFile := path.Join(runtimeDirectory, "dbus-session"); fileExists(runUserSessionDbusFile) { + // if /run/user//dbus-session exists, it's a + // text file // containing the address of the socket, e.g.: + // DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-E1c73yNqrG + + if f, err := ioutil.ReadFile(runUserSessionDbusFile); err == nil { + fileContent := string(f) + + prefix := "DBUS_SESSION_BUS_ADDRESS=" + + if strings.HasPrefix(fileContent, prefix) { + address := strings.TrimRight(strings.TrimPrefix(fileContent, prefix), "\n\r") + return address + } + } + } + } + return "" +} + +func getRuntimeDirectory() (string, error) { + if currentUser, err := user.Current(); err != nil { + return "", err + } else { + return fmt.Sprintf("/run/user/%s", currentUser.Uid), nil + } +} + +func fileExists(filename string) bool { + if _, err := os.Stat(filename); !os.IsNotExist(err) { + return true + } else { + return false } - return defaultSystemBusAddress } diff --git a/vendor/github.com/godbus/dbus/conn_unix.go b/vendor/github.com/godbus/dbus/conn_unix.go new file mode 100644 index 0000000..58aee7d --- /dev/null +++ b/vendor/github.com/godbus/dbus/conn_unix.go @@ -0,0 +1,17 @@ +//+build !windows,!solaris,!darwin + +package dbus + +import ( + "os" +) + +const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" + +func getSystemBusPlatformAddress() string { + address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") + if address != "" { + return address + } + return defaultSystemBusAddress +} diff --git a/vendor/github.com/godbus/dbus/conn_windows.go b/vendor/github.com/godbus/dbus/conn_windows.go new file mode 100644 index 0000000..4291e45 --- /dev/null +++ b/vendor/github.com/godbus/dbus/conn_windows.go @@ -0,0 +1,15 @@ +//+build windows + +package dbus + +import "os" + +const defaultSystemBusAddress = "tcp:host=127.0.0.1,port=12434" + +func getSystemBusPlatformAddress() string { + address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") + if address != "" { + return address + } + return defaultSystemBusAddress +} diff --git a/vendor/github.com/godbus/dbus/decoder.go b/vendor/github.com/godbus/dbus/decoder.go index ef50dca..ede9157 100644 --- a/vendor/github.com/godbus/dbus/decoder.go +++ b/vendor/github.com/godbus/dbus/decoder.go @@ -188,10 +188,23 @@ func (dec *decoder) decode(s string, depth int) interface{} { if depth >= 64 { panic(FormatError("input exceeds container depth limit")) } + sig := s[1:] length := dec.decode("u", depth).(uint32) - v := reflect.MakeSlice(reflect.SliceOf(typeFor(s[1:])), 0, int(length)) + // capacity can be determined only for fixed-size element types + var capacity int + if s := sigByteSize(sig); s != 0 { + capacity = int(length) / s + } + v := reflect.MakeSlice(reflect.SliceOf(typeFor(sig)), 0, capacity) // Even for empty arrays, the correct padding must be included - dec.align(alignment(typeFor(s[1:]))) + align := alignment(typeFor(s[1:])) + if len(s) > 1 && s[1] == '(' { + //Special case for arrays of structs + //structs decode as a slice of interface{} values + //but the dbus alignment does not match this + align = 8 + } + dec.align(align) spos := dec.pos for dec.pos < spos+int(length) { ev := dec.decode(s[1:], depth+1) @@ -220,6 +233,51 @@ func (dec *decoder) decode(s string, depth int) interface{} { } } +// sigByteSize tries to calculates size of the given signature in bytes. +// +// It returns zero when it can't, for example when it contains non-fixed size +// types such as strings, maps and arrays that require reading of the transmitted +// data, for that we would need to implement the unread method for Decoder first. +func sigByteSize(sig string) int { + var total int + for offset := 0; offset < len(sig); { + switch sig[offset] { + case 'y': + total += 1 + offset += 1 + case 'n', 'q': + total += 2 + offset += 1 + case 'b', 'i', 'u', 'h': + total += 4 + offset += 1 + case 'x', 't', 'd': + total += 8 + offset += 1 + case '(': + i := 1 + depth := 1 + for i < len(sig[offset:]) && depth != 0 { + if sig[offset+i] == '(' { + depth++ + } else if sig[offset+i] == ')' { + depth-- + } + i++ + } + s := sigByteSize(sig[offset+1 : offset+i-1]) + if s == 0 { + return 0 + } + total += s + offset += i + default: + return 0 + } + } + return total +} + // A FormatError is an error in the wire format. type FormatError string diff --git a/vendor/github.com/godbus/dbus/default_handler.go b/vendor/github.com/godbus/dbus/default_handler.go index e81f73a..6d8bf32 100644 --- a/vendor/github.com/godbus/dbus/default_handler.go +++ b/vendor/github.com/godbus/dbus/default_handler.go @@ -21,6 +21,8 @@ func newIntrospectIntf(h *defaultHandler) *exportedIntf { //NewDefaultHandler returns an instance of the default //call handler. This is useful if you want to implement only //one of the two handlers but not both. +// +// Deprecated: this is the default value, don't use it, it will be unexported. func NewDefaultHandler() *defaultHandler { h := &defaultHandler{ objects: make(map[ObjectPath]*exportedObj), @@ -45,7 +47,7 @@ func (h *defaultHandler) introspectPath(path ObjectPath) string { subpath := make(map[string]struct{}) var xml bytes.Buffer xml.WriteString("") - for obj, _ := range h.objects { + for obj := range h.objects { p := string(path) if p != "/" { p += "/" @@ -55,7 +57,7 @@ func (h *defaultHandler) introspectPath(path ObjectPath) string { subpath[node_name] = struct{}{} } } - for s, _ := range subpath { + for s := range subpath { xml.WriteString("\n\t") } xml.WriteString("\n") @@ -161,6 +163,7 @@ func newExportedObject() *exportedObj { } type exportedObj struct { + mu sync.RWMutex interfaces map[string]*exportedIntf } @@ -168,19 +171,27 @@ func (obj *exportedObj) LookupInterface(name string) (Interface, bool) { if name == "" { return obj, true } + obj.mu.RLock() + defer obj.mu.RUnlock() intf, exists := obj.interfaces[name] return intf, exists } func (obj *exportedObj) AddInterface(name string, iface *exportedIntf) { + obj.mu.Lock() + defer obj.mu.Unlock() obj.interfaces[name] = iface } func (obj *exportedObj) DeleteInterface(name string) { + obj.mu.Lock() + defer obj.mu.Unlock() delete(obj.interfaces, name) } func (obj *exportedObj) LookupMethod(name string) (Method, bool) { + obj.mu.RLock() + defer obj.mu.RUnlock() for _, intf := range obj.interfaces { method, exists := intf.LookupMethod(name) if exists { @@ -220,72 +231,98 @@ func (obj *exportedIntf) isFallbackInterface() bool { //NewDefaultSignalHandler returns an instance of the default //signal handler. This is useful if you want to implement only //one of the two handlers but not both. +// +// Deprecated: this is the default value, don't use it, it will be unexported. func NewDefaultSignalHandler() *defaultSignalHandler { return &defaultSignalHandler{} } -func isDefaultSignalHandler(handler SignalHandler) bool { - _, ok := handler.(*defaultSignalHandler) - return ok -} - type defaultSignalHandler struct { - sync.RWMutex + mu sync.RWMutex closed bool - signals []chan<- *Signal + signals []*signalChannelData } func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) { - go func() { - sh.RLock() - defer sh.RUnlock() - if sh.closed { - return - } - for _, ch := range sh.signals { - ch <- signal - } - }() -} - -func (sh *defaultSignalHandler) Init() error { - sh.Lock() - sh.signals = make([]chan<- *Signal, 0) - sh.Unlock() - return nil + sh.mu.RLock() + defer sh.mu.RUnlock() + if sh.closed { + return + } + for _, scd := range sh.signals { + scd.deliver(signal) + } } func (sh *defaultSignalHandler) Terminate() { - sh.Lock() - sh.closed = true - for _, ch := range sh.signals { - close(ch) + sh.mu.Lock() + defer sh.mu.Unlock() + if sh.closed { + return } + + for _, scd := range sh.signals { + scd.close() + close(scd.ch) + } + sh.closed = true sh.signals = nil - sh.Unlock() } -func (sh *defaultSignalHandler) addSignal(ch chan<- *Signal) { - sh.Lock() - defer sh.Unlock() +func (sh *defaultSignalHandler) AddSignal(ch chan<- *Signal) { + sh.mu.Lock() + defer sh.mu.Unlock() if sh.closed { return } - sh.signals = append(sh.signals, ch) - + sh.signals = append(sh.signals, &signalChannelData{ + ch: ch, + done: make(chan struct{}), + }) } -func (sh *defaultSignalHandler) removeSignal(ch chan<- *Signal) { - sh.Lock() - defer sh.Unlock() +func (sh *defaultSignalHandler) RemoveSignal(ch chan<- *Signal) { + sh.mu.Lock() + defer sh.mu.Unlock() if sh.closed { return } for i := len(sh.signals) - 1; i >= 0; i-- { - if ch == sh.signals[i] { + if ch == sh.signals[i].ch { + sh.signals[i].close() copy(sh.signals[i:], sh.signals[i+1:]) sh.signals[len(sh.signals)-1] = nil sh.signals = sh.signals[:len(sh.signals)-1] } } } + +type signalChannelData struct { + wg sync.WaitGroup + ch chan<- *Signal + done chan struct{} +} + +func (scd *signalChannelData) deliver(signal *Signal) { + select { + case scd.ch <- signal: + case <-scd.done: + return + default: + scd.wg.Add(1) + go scd.deferredDeliver(signal) + } +} + +func (scd *signalChannelData) deferredDeliver(signal *Signal) { + select { + case scd.ch <- signal: + case <-scd.done: + } + scd.wg.Done() +} + +func (scd *signalChannelData) close() { + close(scd.done) + scd.wg.Wait() // wait until all spawned goroutines return +} diff --git a/vendor/github.com/godbus/dbus/export.go b/vendor/github.com/godbus/dbus/export.go index aae9708..95d0e29 100644 --- a/vendor/github.com/godbus/dbus/export.go +++ b/vendor/github.com/godbus/dbus/export.go @@ -170,11 +170,8 @@ func (conn *Conn) handleCall(msg *Message) { reply.Body[i] = ret[i] } reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...)) - conn.outLck.RLock() - if !conn.closed { - conn.out <- reply - } - conn.outLck.RUnlock() + + conn.sendMessage(reply) } } @@ -207,12 +204,14 @@ func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) erro if len(values) > 0 { msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) } - conn.outLck.RLock() - defer conn.outLck.RUnlock() - if conn.closed { + + var closed bool + conn.sendMessageAndIfClosed(msg, func() { + closed = true + }) + if closed { return ErrClosed } - conn.out <- msg return nil } diff --git a/vendor/github.com/godbus/dbus/go.mod b/vendor/github.com/godbus/dbus/go.mod new file mode 100644 index 0000000..57014e4 --- /dev/null +++ b/vendor/github.com/godbus/dbus/go.mod @@ -0,0 +1,3 @@ +module github.com/godbus/dbus + +go 1.12 diff --git a/vendor/github.com/godbus/dbus/match.go b/vendor/github.com/godbus/dbus/match.go new file mode 100644 index 0000000..086ee33 --- /dev/null +++ b/vendor/github.com/godbus/dbus/match.go @@ -0,0 +1,62 @@ +package dbus + +import ( + "strings" +) + +// MatchOption specifies option for dbus routing match rule. Options can be constructed with WithMatch* helpers. +// For full list of available options consult +// https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules +type MatchOption struct { + key string + value string +} + +func formatMatchOptions(options []MatchOption) string { + items := make([]string, 0, len(options)) + for _, option := range options { + items = append(items, option.key+"='"+option.value+"'") + } + return strings.Join(items, ",") +} + +// WithMatchOption creates match option with given key and value +func WithMatchOption(key, value string) MatchOption { + return MatchOption{key, value} +} + +// doesn't make sense to export this option because clients can only +// subscribe to messages with signal type. +func withMatchType(typ string) MatchOption { + return WithMatchOption("type", typ) +} + +// WithMatchSender sets sender match option. +func WithMatchSender(sender string) MatchOption { + return WithMatchOption("sender", sender) +} + +// WithMatchSender sets interface match option. +func WithMatchInterface(iface string) MatchOption { + return WithMatchOption("interface", iface) +} + +// WithMatchMember sets member match option. +func WithMatchMember(member string) MatchOption { + return WithMatchOption("member", member) +} + +// WithMatchObjectPath creates match option that filters events based on given path +func WithMatchObjectPath(path ObjectPath) MatchOption { + return WithMatchOption("path", string(path)) +} + +// WithMatchPathNamespace sets path_namespace match option. +func WithMatchPathNamespace(namespace ObjectPath) MatchOption { + return WithMatchOption("path_namespace", string(namespace)) +} + +// WithMatchDestination sets destination match option. +func WithMatchDestination(destination string) MatchOption { + return WithMatchOption("destination", destination) +} diff --git a/vendor/github.com/godbus/dbus/object.go b/vendor/github.com/godbus/dbus/object.go index 6d95583..8acd7fc 100644 --- a/vendor/github.com/godbus/dbus/object.go +++ b/vendor/github.com/godbus/dbus/object.go @@ -1,6 +1,7 @@ package dbus import ( + "context" "errors" "strings" ) @@ -9,8 +10,13 @@ import ( // invoked. type BusObject interface { Call(method string, flags Flags, args ...interface{}) *Call + CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call + GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call + AddMatchSignal(iface, member string, options ...MatchOption) *Call + RemoveMatchSignal(iface, member string, options ...MatchOption) *Call GetProperty(p string) (Variant, error) + SetProperty(p string, v interface{}) error Destination() string Path() ObjectPath } @@ -24,16 +30,50 @@ type Object struct { // Call calls a method with (*Object).Go and waits for its reply. func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call { - return <-o.Go(method, flags, make(chan *Call, 1), args...).Done + return <-o.createCall(context.Background(), method, flags, make(chan *Call, 1), args...).Done } -// AddMatchSignal subscribes BusObject to signals from specified interface and -// method (member). -func (o *Object) AddMatchSignal(iface, member string) *Call { - return o.Call( +// CallWithContext acts like Call but takes a context +func (o *Object) CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call { + return <-o.createCall(ctx, method, flags, make(chan *Call, 1), args...).Done +} + +// AddMatchSignal subscribes BusObject to signals from specified interface, +// method (member). Additional filter rules can be added via WithMatch* option constructors. +// Note: To filter events by object path you have to specify this path via an option. +// +// Deprecated: use (*Conn) AddMatchSignal instead. +func (o *Object) AddMatchSignal(iface, member string, options ...MatchOption) *Call { + base := []MatchOption{ + withMatchType("signal"), + WithMatchInterface(iface), + WithMatchMember(member), + } + + options = append(base, options...) + return o.conn.BusObject().Call( "org.freedesktop.DBus.AddMatch", 0, - "type='signal',interface='"+iface+"',member='"+member+"'", + formatMatchOptions(options), + ) +} + +// RemoveMatchSignal unsubscribes BusObject from signals from specified interface, +// method (member). Additional filter rules can be added via WithMatch* option constructors +// +// Deprecated: use (*Conn) RemoveMatchSignal instead. +func (o *Object) RemoveMatchSignal(iface, member string, options ...MatchOption) *Call { + base := []MatchOption{ + withMatchType("signal"), + WithMatchInterface(iface), + WithMatchMember(member), + } + + options = append(base, options...) + return o.conn.BusObject().Call( + "org.freedesktop.DBus.RemoveMatch", + 0, + formatMatchOptions(options), ) } @@ -49,6 +89,18 @@ func (o *Object) AddMatchSignal(iface, member string) *Call { // If the method parameter contains a dot ('.'), the part before the last dot // specifies the interface on which the method is called. func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call { + return o.createCall(context.Background(), method, flags, ch, args...) +} + +// GoWithContext acts like Go but takes a context +func (o *Object) GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call { + return o.createCall(ctx, method, flags, ch, args...) +} + +func (o *Object) createCall(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call { + if ctx == nil { + panic("nil context") + } iface := "" i := strings.LastIndex(method, ".") if i != -1 { @@ -72,32 +124,32 @@ func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface } if msg.Flags&FlagNoReplyExpected == 0 { if ch == nil { - ch = make(chan *Call, 10) + ch = make(chan *Call, 1) } else if cap(ch) == 0 { panic("dbus: unbuffered channel passed to (*Object).Go") } + ctx, cancel := context.WithCancel(ctx) call := &Call{ Destination: o.dest, Path: o.path, Method: method, Args: args, Done: ch, + ctxCanceler: cancel, + ctx: ctx, } - o.conn.callsLck.Lock() - o.conn.calls[msg.serial] = call - o.conn.callsLck.Unlock() - o.conn.outLck.RLock() - if o.conn.closed { - call.Err = ErrClosed - call.Done <- call - } else { - o.conn.out <- msg - } - o.conn.outLck.RUnlock() + o.conn.calls.track(msg.serial, call) + o.conn.sendMessageAndIfClosed(msg, func() { + o.conn.calls.handleSendError(msg, ErrClosed) + cancel() + }) + go func() { + <-ctx.Done() + o.conn.calls.handleSendError(msg, ctx.Err()) + }() + return call } - o.conn.outLck.RLock() - defer o.conn.outLck.RUnlock() done := make(chan *Call, 1) call := &Call{ Err: nil, @@ -107,15 +159,13 @@ func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface call.Done <- call close(done) }() - if o.conn.closed { + o.conn.sendMessageAndIfClosed(msg, func() { call.Err = ErrClosed - return call - } - o.conn.out <- msg + }) return call } -// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given +// GetProperty calls org.freedesktop.DBus.Properties.Get on the given // object. The property name must be given in interface.member notation. func (o *Object) GetProperty(p string) (Variant, error) { idx := strings.LastIndex(p, ".") @@ -136,6 +186,20 @@ func (o *Object) GetProperty(p string) (Variant, error) { return result, nil } +// SetProperty calls org.freedesktop.DBus.Properties.Set on the given +// object. The property name must be given in interface.member notation. +func (o *Object) SetProperty(p string, v interface{}) error { + idx := strings.LastIndex(p, ".") + if idx == -1 || idx+1 == len(p) { + return errors.New("dbus: invalid property " + p) + } + + iface := p[:idx] + prop := p[idx+1:] + + return o.Call("org.freedesktop.DBus.Properties.Set", 0, iface, prop, v).Err +} + // Destination returns the destination that calls on (o *Object) are sent to. func (o *Object) Destination() string { return o.dest diff --git a/vendor/github.com/godbus/dbus/server_interfaces.go b/vendor/github.com/godbus/dbus/server_interfaces.go index 091948a..79d97ed 100644 --- a/vendor/github.com/godbus/dbus/server_interfaces.go +++ b/vendor/github.com/godbus/dbus/server_interfaces.go @@ -77,6 +77,14 @@ type SignalHandler interface { DeliverSignal(iface, name string, signal *Signal) } +// SignalRegistrar manages signal delivery channels. +// +// This is an optional set of methods for `SignalHandler`. +type SignalRegistrar interface { + AddSignal(ch chan<- *Signal) + RemoveSignal(ch chan<- *Signal) +} + // A DBusError is used to convert a generic object to a D-Bus error. // // Any custom error mechanism may implement this interface to provide @@ -87,3 +95,13 @@ type SignalHandler interface { type DBusError interface { DBusError() (string, []interface{}) } + +// SerialGenerator is responsible for serials generation. +// +// Different approaches for the serial generation can be used, +// maintaining a map guarded with a mutex (the standard way) or +// simply increment an atomic counter. +type SerialGenerator interface { + GetSerial() uint32 + RetireSerial(serial uint32) +} diff --git a/vendor/github.com/godbus/dbus/transport_generic.go b/vendor/github.com/godbus/dbus/transport_generic.go index 3fad859..718a1ff 100644 --- a/vendor/github.com/godbus/dbus/transport_generic.go +++ b/vendor/github.com/godbus/dbus/transport_generic.go @@ -11,7 +11,7 @@ var nativeEndian binary.ByteOrder func detectEndianness() binary.ByteOrder { var x uint32 = 0x01020304 - if *(*byte)(unsafe.Pointer(&x)) == 0x01 { + if *(*byte)(unsafe.Pointer(&x)) == 0x01 { return binary.BigEndian } return binary.LittleEndian diff --git a/vendor/github.com/godbus/dbus/transport_nonce_tcp.go b/vendor/github.com/godbus/dbus/transport_nonce_tcp.go new file mode 100644 index 0000000..697739e --- /dev/null +++ b/vendor/github.com/godbus/dbus/transport_nonce_tcp.go @@ -0,0 +1,39 @@ +//+build !windows + +package dbus + +import ( + "errors" + "io/ioutil" + "net" +) + +func init() { + transports["nonce-tcp"] = newNonceTcpTransport +} + +func newNonceTcpTransport(keys string) (transport, error) { + host := getKey(keys, "host") + port := getKey(keys, "port") + noncefile := getKey(keys, "noncefile") + if host == "" || port == "" || noncefile == "" { + return nil, errors.New("dbus: unsupported address (must set host, port and noncefile)") + } + protocol, err := tcpFamily(keys) + if err != nil { + return nil, err + } + socket, err := net.Dial(protocol, net.JoinHostPort(host, port)) + if err != nil { + return nil, err + } + b, err := ioutil.ReadFile(noncefile) + if err != nil { + return nil, err + } + _, err = socket.Write(b) + if err != nil { + return nil, err + } + return NewConn(socket) +} diff --git a/vendor/github.com/godbus/dbus/transport_tcp.go b/vendor/github.com/godbus/dbus/transport_tcp.go index dd1c8e5..f91c9b7 100644 --- a/vendor/github.com/godbus/dbus/transport_tcp.go +++ b/vendor/github.com/godbus/dbus/transport_tcp.go @@ -1,5 +1,3 @@ -//+build !windows - package dbus import ( diff --git a/vendor/github.com/godbus/dbus/transport_unix.go b/vendor/github.com/godbus/dbus/transport_unix.go index e56d5ca..c7cd02f 100644 --- a/vendor/github.com/godbus/dbus/transport_unix.go +++ b/vendor/github.com/godbus/dbus/transport_unix.go @@ -31,6 +31,7 @@ func (o *oobReader) Read(b []byte) (n int, err error) { type unixTransport struct { *net.UnixConn + rdr *oobReader hasUnixFDs bool } @@ -79,10 +80,15 @@ func (t *unixTransport) ReadMessage() (*Message, error) { // To be sure that all bytes of out-of-band data are read, we use a special // reader that uses ReadUnix on the underlying connection instead of Read // and gathers the out-of-band data in a buffer. - rd := &oobReader{conn: t.UnixConn} + if t.rdr == nil { + t.rdr = &oobReader{conn: t.UnixConn} + } else { + t.rdr.oob = nil + } + // read the first 16 bytes (the part of the header that has a constant size), // from which we can figure out the length of the rest of the message - if _, err := io.ReadFull(rd, csheader[:]); err != nil { + if _, err := io.ReadFull(t.rdr, csheader[:]); err != nil { return nil, err } switch csheader[0] { @@ -104,7 +110,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { // decode headers and look for unix fds headerdata := make([]byte, hlen+4) copy(headerdata, csheader[12:]) - if _, err := io.ReadFull(t, headerdata[4:]); err != nil { + if _, err := io.ReadFull(t.rdr, headerdata[4:]); err != nil { return nil, err } dec := newDecoder(bytes.NewBuffer(headerdata), order) @@ -122,7 +128,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { all := make([]byte, 16+hlen+blen) copy(all, csheader[:]) copy(all[16:], headerdata[4:]) - if _, err := io.ReadFull(rd, all[16+hlen:]); err != nil { + if _, err := io.ReadFull(t.rdr, all[16+hlen:]); err != nil { return nil, err } if unixfds != 0 { @@ -130,7 +136,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { return nil, errors.New("dbus: got unix fds on unsupported transport") } // read the fds from the OOB data - scms, err := syscall.ParseSocketControlMessage(rd.oob) + scms, err := syscall.ParseSocketControlMessage(t.rdr.oob) if err != nil { return nil, err } @@ -148,11 +154,23 @@ func (t *unixTransport) ReadMessage() (*Message, error) { // substitute the values in the message body (which are indices for the // array receiver via OOB) with the actual values for i, v := range msg.Body { - if j, ok := v.(UnixFDIndex); ok { + switch v.(type) { + case UnixFDIndex: + j := v.(UnixFDIndex) if uint32(j) >= unixfds { return nil, InvalidMessageError("invalid index for unix fd") } msg.Body[i] = UnixFD(fds[j]) + case []UnixFDIndex: + idxArray := v.([]UnixFDIndex) + fdArray := make([]UnixFD, len(idxArray)) + for k, j := range idxArray { + if uint32(j) >= unixfds { + return nil, InvalidMessageError("invalid index for unix fd") + } + fdArray[k] = UnixFD(fds[j]) + } + msg.Body[i] = fdArray } } return msg, nil @@ -185,7 +203,7 @@ func (t *unixTransport) SendMessage(msg *Message) error { } } else { if err := msg.EncodeTo(t, nativeEndian); err != nil { - return nil + return err } } return nil diff --git a/vendor/github.com/godbus/dbus/variant_lexer.go b/vendor/github.com/godbus/dbus/variant_lexer.go index 332007d..bf1398c 100644 --- a/vendor/github.com/godbus/dbus/variant_lexer.go +++ b/vendor/github.com/godbus/dbus/variant_lexer.go @@ -51,7 +51,7 @@ func varLex(s string) []varToken { } func (l *varLexer) accept(valid string) bool { - if strings.IndexRune(valid, l.next()) >= 0 { + if strings.ContainsRune(valid, l.next()) { return true } l.backup() @@ -214,17 +214,17 @@ func varLexNumber(l *varLexer) lexState { digits = "01234567" } } - for strings.IndexRune(digits, l.next()) >= 0 { + for strings.ContainsRune(digits, l.next()) { } l.backup() if l.accept(".") { - for strings.IndexRune(digits, l.next()) >= 0 { + for strings.ContainsRune(digits, l.next()) { } l.backup() } if l.accept("eE") { l.accept("+-") - for strings.IndexRune("0123456789", l.next()) >= 0 { + for strings.ContainsRune("0123456789", l.next()) { } l.backup() } diff --git a/vendor/github.com/mtibben/percent/LICENSE b/vendor/github.com/mtibben/percent/LICENSE new file mode 100644 index 0000000..4bad1f9 --- /dev/null +++ b/vendor/github.com/mtibben/percent/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Michael Tibben + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mtibben/percent/README.md b/vendor/github.com/mtibben/percent/README.md new file mode 100644 index 0000000..4e85b56 --- /dev/null +++ b/vendor/github.com/mtibben/percent/README.md @@ -0,0 +1,5 @@ +# Percent + +Package percent escapes strings using [percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding) + +[Docs](https://pkg.go.dev/github.com/mtibben/percent?tab=doc) diff --git a/vendor/github.com/mtibben/percent/go.mod b/vendor/github.com/mtibben/percent/go.mod new file mode 100644 index 0000000..f0bb33e --- /dev/null +++ b/vendor/github.com/mtibben/percent/go.mod @@ -0,0 +1,3 @@ +module github.com/mtibben/percent + +go 1.14 diff --git a/vendor/github.com/mtibben/percent/percent.go b/vendor/github.com/mtibben/percent/percent.go new file mode 100644 index 0000000..565222e --- /dev/null +++ b/vendor/github.com/mtibben/percent/percent.go @@ -0,0 +1,64 @@ +// Package percent escapes strings using percent-encoding +package percent + +import ( + "strings" +) + +const upperhex = "0123456789ABCDEF" + +func unhex(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 0 +} + +func ishex(c byte) bool { + switch { + case '0' <= c && c <= '9': + return true + case 'a' <= c && c <= 'f': + return true + case 'A' <= c && c <= 'F': + return true + } + return false +} + +// Encode escapes the string using percent-encoding, converting the runes found in charsToEncode with hex-encoded %AB sequences +func Encode(s string, charsToEncode string) string { + var t strings.Builder + for _, c := range s { + if strings.IndexRune(charsToEncode, c) != -1 || c == '%' { + for _, b := range []byte(string(c)) { + t.WriteByte('%') + t.WriteByte(upperhex[b>>4]) + t.WriteByte(upperhex[b&15]) + } + } else { + t.WriteRune(c) + } + } + return t.String() +} + +// Decode does the inverse transformation of Encode, converting each 3-byte encoded substring of the form "%AB" into the hex-decoded byte 0xAB +func Decode(s string) string { + var t []byte + for i := 0; i < len(s); i++ { + // check next 2 chars are valid + if s[i] == '%' && i+2 < len(s) && ishex(s[i+1]) && ishex(s[i+2]) { + t = append(t, (unhex(s[i+1])<<4 | unhex(s[i+2]))) + i += 2 + } else { + t = append(t, s[i]) + } + } + return string(t) +} diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index 3d85f27..14624b9 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -199,6 +199,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -435,6 +436,8 @@ ccflags="$@" $2 ~ /^TC[IO](ON|OFF)$/ || $2 ~ /^IN_/ || $2 ~ /^LOCK_(SH|EX|NB|UN)$/ || + $2 ~ /^LO_(KEY|NAME)_SIZE$/ || + $2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ || $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|MCAST|EVFILT|NOTE|EV|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ || $2 ~ /^TP_STATUS_/ || $2 ~ /^FALLOC_/ || diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index a5e32ae..5213d82 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -1149,6 +1149,20 @@ const ( LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 + LOOP_CLR_FD = 0x4c01 + LOOP_CTL_ADD = 0x4c80 + LOOP_CTL_GET_FREE = 0x4c82 + LOOP_CTL_REMOVE = 0x4c81 + LOOP_GET_STATUS = 0x4c03 + LOOP_GET_STATUS64 = 0x4c05 + LOOP_SET_BLOCK_SIZE = 0x4c09 + LOOP_SET_CAPACITY = 0x4c07 + LOOP_SET_DIRECT_IO = 0x4c08 + LOOP_SET_FD = 0x4c00 + LOOP_SET_STATUS = 0x4c02 + LOOP_SET_STATUS64 = 0x4c04 + LO_KEY_SIZE = 0x20 + LO_NAME_SIZE = 0x40 MADV_DODUMP = 0x11 MADV_DOFORK = 0xb MADV_DONTDUMP = 0x10 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index cacabfa..39b630c 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -1149,6 +1149,20 @@ const ( LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 + LOOP_CLR_FD = 0x4c01 + LOOP_CTL_ADD = 0x4c80 + LOOP_CTL_GET_FREE = 0x4c82 + LOOP_CTL_REMOVE = 0x4c81 + LOOP_GET_STATUS = 0x4c03 + LOOP_GET_STATUS64 = 0x4c05 + LOOP_SET_BLOCK_SIZE = 0x4c09 + LOOP_SET_CAPACITY = 0x4c07 + LOOP_SET_DIRECT_IO = 0x4c08 + LOOP_SET_FD = 0x4c00 + LOOP_SET_STATUS = 0x4c02 + LOOP_SET_STATUS64 = 0x4c04 + LO_KEY_SIZE = 0x20 + LO_NAME_SIZE = 0x40 MADV_DODUMP = 0x11 MADV_DOFORK = 0xb MADV_DONTDUMP = 0x10 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index c04d5b9..c59a1be 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -1148,6 +1148,20 @@ const ( LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 + LOOP_CLR_FD = 0x4c01 + LOOP_CTL_ADD = 0x4c80 + LOOP_CTL_GET_FREE = 0x4c82 + LOOP_CTL_REMOVE = 0x4c81 + LOOP_GET_STATUS = 0x4c03 + LOOP_GET_STATUS64 = 0x4c05 + LOOP_SET_BLOCK_SIZE = 0x4c09 + LOOP_SET_CAPACITY = 0x4c07 + LOOP_SET_DIRECT_IO = 0x4c08 + LOOP_SET_FD = 0x4c00 + LOOP_SET_STATUS = 0x4c02 + LOOP_SET_STATUS64 = 0x4c04 + LO_KEY_SIZE = 0x20 + LO_NAME_SIZE = 0x40 MADV_DODUMP = 0x11 MADV_DOFORK = 0xb MADV_DONTDUMP = 0x10 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index 507c44c..5f35c19 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -1151,6 +1151,20 @@ const ( LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 + LOOP_CLR_FD = 0x4c01 + LOOP_CTL_ADD = 0x4c80 + LOOP_CTL_GET_FREE = 0x4c82 + LOOP_CTL_REMOVE = 0x4c81 + LOOP_GET_STATUS = 0x4c03 + LOOP_GET_STATUS64 = 0x4c05 + LOOP_SET_BLOCK_SIZE = 0x4c09 + LOOP_SET_CAPACITY = 0x4c07 + LOOP_SET_DIRECT_IO = 0x4c08 + LOOP_SET_FD = 0x4c00 + LOOP_SET_STATUS = 0x4c02 + LOOP_SET_STATUS64 = 0x4c04 + LO_KEY_SIZE = 0x20 + LO_NAME_SIZE = 0x40 MADV_DODUMP = 0x11 MADV_DOFORK = 0xb MADV_DONTDUMP = 0x10 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index dd2a894..7f1b7be 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -1148,6 +1148,20 @@ const ( LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 + LOOP_CLR_FD = 0x4c01 + LOOP_CTL_ADD = 0x4c80 + LOOP_CTL_GET_FREE = 0x4c82 + LOOP_CTL_REMOVE = 0x4c81 + LOOP_GET_STATUS = 0x4c03 + LOOP_GET_STATUS64 = 0x4c05 + LOOP_SET_BLOCK_SIZE = 0x4c09 + LOOP_SET_CAPACITY = 0x4c07 + LOOP_SET_DIRECT_IO = 0x4c08 + LOOP_SET_FD = 0x4c00 + LOOP_SET_STATUS = 0x4c02 + LOOP_SET_STATUS64 = 0x4c04 + LO_KEY_SIZE = 0x20 + LO_NAME_SIZE = 0x40 MADV_DODUMP = 0x11 MADV_DOFORK = 0xb MADV_DONTDUMP = 0x10 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index 90a401b..603d88b 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -1148,6 +1148,20 @@ const ( LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 + LOOP_CLR_FD = 0x4c01 + LOOP_CTL_ADD = 0x4c80 + LOOP_CTL_GET_FREE = 0x4c82 + LOOP_CTL_REMOVE = 0x4c81 + LOOP_GET_STATUS = 0x4c03 + LOOP_GET_STATUS64 = 0x4c05 + LOOP_SET_BLOCK_SIZE = 0x4c09 + LOOP_SET_CAPACITY = 0x4c07 + LOOP_SET_DIRECT_IO = 0x4c08 + LOOP_SET_FD = 0x4c00 + LOOP_SET_STATUS = 0x4c02 + LOOP_SET_STATUS64 = 0x4c04 + LO_KEY_SIZE = 0x20 + LO_NAME_SIZE = 0x40 MADV_DODUMP = 0x11 MADV_DOFORK = 0xb MADV_DONTDUMP = 0x10 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index 380759e..ed178f8 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -1148,6 +1148,20 @@ const ( LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 + LOOP_CLR_FD = 0x4c01 + LOOP_CTL_ADD = 0x4c80 + LOOP_CTL_GET_FREE = 0x4c82 + LOOP_CTL_REMOVE = 0x4c81 + LOOP_GET_STATUS = 0x4c03 + LOOP_GET_STATUS64 = 0x4c05 + LOOP_SET_BLOCK_SIZE = 0x4c09 + LOOP_SET_CAPACITY = 0x4c07 + LOOP_SET_DIRECT_IO = 0x4c08 + LOOP_SET_FD = 0x4c00 + LOOP_SET_STATUS = 0x4c02 + LOOP_SET_STATUS64 = 0x4c04 + LO_KEY_SIZE = 0x20 + LO_NAME_SIZE = 0x40 MADV_DODUMP = 0x11 MADV_DOFORK = 0xb MADV_DONTDUMP = 0x10 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index 56050eb..080b789 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -1148,6 +1148,20 @@ const ( LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 + LOOP_CLR_FD = 0x4c01 + LOOP_CTL_ADD = 0x4c80 + LOOP_CTL_GET_FREE = 0x4c82 + LOOP_CTL_REMOVE = 0x4c81 + LOOP_GET_STATUS = 0x4c03 + LOOP_GET_STATUS64 = 0x4c05 + LOOP_SET_BLOCK_SIZE = 0x4c09 + LOOP_SET_CAPACITY = 0x4c07 + LOOP_SET_DIRECT_IO = 0x4c08 + LOOP_SET_FD = 0x4c00 + LOOP_SET_STATUS = 0x4c02 + LOOP_SET_STATUS64 = 0x4c04 + LO_KEY_SIZE = 0x20 + LO_NAME_SIZE = 0x40 MADV_DODUMP = 0x11 MADV_DOFORK = 0xb MADV_DONTDUMP = 0x10 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index 11047e0..961e8ea 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -1148,6 +1148,20 @@ const ( LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 + LOOP_CLR_FD = 0x4c01 + LOOP_CTL_ADD = 0x4c80 + LOOP_CTL_GET_FREE = 0x4c82 + LOOP_CTL_REMOVE = 0x4c81 + LOOP_GET_STATUS = 0x4c03 + LOOP_GET_STATUS64 = 0x4c05 + LOOP_SET_BLOCK_SIZE = 0x4c09 + LOOP_SET_CAPACITY = 0x4c07 + LOOP_SET_DIRECT_IO = 0x4c08 + LOOP_SET_FD = 0x4c00 + LOOP_SET_STATUS = 0x4c02 + LOOP_SET_STATUS64 = 0x4c04 + LO_KEY_SIZE = 0x20 + LO_NAME_SIZE = 0x40 MADV_DODUMP = 0x11 MADV_DOFORK = 0xb MADV_DONTDUMP = 0x10 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index 38bf9e8..6e0538f 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -1148,6 +1148,20 @@ const ( LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 + LOOP_CLR_FD = 0x4c01 + LOOP_CTL_ADD = 0x4c80 + LOOP_CTL_GET_FREE = 0x4c82 + LOOP_CTL_REMOVE = 0x4c81 + LOOP_GET_STATUS = 0x4c03 + LOOP_GET_STATUS64 = 0x4c05 + LOOP_SET_BLOCK_SIZE = 0x4c09 + LOOP_SET_CAPACITY = 0x4c07 + LOOP_SET_DIRECT_IO = 0x4c08 + LOOP_SET_FD = 0x4c00 + LOOP_SET_STATUS = 0x4c02 + LOOP_SET_STATUS64 = 0x4c04 + LO_KEY_SIZE = 0x20 + LO_NAME_SIZE = 0x40 MADV_DODUMP = 0x11 MADV_DOFORK = 0xb MADV_DONTDUMP = 0x10 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index 852a5c6..06c0148 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -1148,6 +1148,20 @@ const ( LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 + LOOP_CLR_FD = 0x4c01 + LOOP_CTL_ADD = 0x4c80 + LOOP_CTL_GET_FREE = 0x4c82 + LOOP_CTL_REMOVE = 0x4c81 + LOOP_GET_STATUS = 0x4c03 + LOOP_GET_STATUS64 = 0x4c05 + LOOP_SET_BLOCK_SIZE = 0x4c09 + LOOP_SET_CAPACITY = 0x4c07 + LOOP_SET_DIRECT_IO = 0x4c08 + LOOP_SET_FD = 0x4c00 + LOOP_SET_STATUS = 0x4c02 + LOOP_SET_STATUS64 = 0x4c04 + LO_KEY_SIZE = 0x20 + LO_NAME_SIZE = 0x40 MADV_DODUMP = 0x11 MADV_DOFORK = 0xb MADV_DONTDUMP = 0x10 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index 42d4549..3987509 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -1148,6 +1148,20 @@ const ( LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 + LOOP_CLR_FD = 0x4c01 + LOOP_CTL_ADD = 0x4c80 + LOOP_CTL_GET_FREE = 0x4c82 + LOOP_CTL_REMOVE = 0x4c81 + LOOP_GET_STATUS = 0x4c03 + LOOP_GET_STATUS64 = 0x4c05 + LOOP_SET_BLOCK_SIZE = 0x4c09 + LOOP_SET_CAPACITY = 0x4c07 + LOOP_SET_DIRECT_IO = 0x4c08 + LOOP_SET_FD = 0x4c00 + LOOP_SET_STATUS = 0x4c02 + LOOP_SET_STATUS64 = 0x4c04 + LO_KEY_SIZE = 0x20 + LO_NAME_SIZE = 0x40 MADV_DODUMP = 0x11 MADV_DOFORK = 0xb MADV_DONTDUMP = 0x10 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index cc80e7a..8d80f99 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -1152,6 +1152,20 @@ const ( LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 + LOOP_CLR_FD = 0x4c01 + LOOP_CTL_ADD = 0x4c80 + LOOP_CTL_GET_FREE = 0x4c82 + LOOP_CTL_REMOVE = 0x4c81 + LOOP_GET_STATUS = 0x4c03 + LOOP_GET_STATUS64 = 0x4c05 + LOOP_SET_BLOCK_SIZE = 0x4c09 + LOOP_SET_CAPACITY = 0x4c07 + LOOP_SET_DIRECT_IO = 0x4c08 + LOOP_SET_FD = 0x4c00 + LOOP_SET_STATUS = 0x4c02 + LOOP_SET_STATUS64 = 0x4c04 + LO_KEY_SIZE = 0x20 + LO_NAME_SIZE = 0x40 MADV_DODUMP = 0x11 MADV_DOFORK = 0xb MADV_DONTDUMP = 0x10 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go index 5492b96..50bc412 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go @@ -2484,3 +2484,40 @@ const ( LINUX_CAPABILITY_VERSION_2 = 0x20071026 LINUX_CAPABILITY_VERSION_3 = 0x20080522 ) + +const ( + LO_FLAGS_READ_ONLY = 0x1 + LO_FLAGS_AUTOCLEAR = 0x4 + LO_FLAGS_PARTSCAN = 0x8 + LO_FLAGS_DIRECT_IO = 0x10 +) + +type LoopInfo struct { + Number int32 + Device uint16 + Inode uint32 + Rdevice uint16 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]int8 + Encrypt_key [32]uint8 + Init [2]uint32 + Reserved [4]int8 +} +type LoopInfo64 struct { + Device uint64 + Inode uint64 + Rdevice uint64 + Offset uint64 + Sizelimit uint64 + Number uint32 + Encrypt_type uint32 + Encrypt_key_size uint32 + Flags uint32 + File_name [64]uint8 + Crypt_name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go index caf33b2..055eaa7 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go @@ -2497,3 +2497,41 @@ const ( LINUX_CAPABILITY_VERSION_2 = 0x20071026 LINUX_CAPABILITY_VERSION_3 = 0x20080522 ) + +const ( + LO_FLAGS_READ_ONLY = 0x1 + LO_FLAGS_AUTOCLEAR = 0x4 + LO_FLAGS_PARTSCAN = 0x8 + LO_FLAGS_DIRECT_IO = 0x10 +) + +type LoopInfo struct { + Number int32 + Device uint64 + Inode uint64 + Rdevice uint64 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]int8 + Encrypt_key [32]uint8 + Init [2]uint64 + Reserved [4]int8 + _ [4]byte +} +type LoopInfo64 struct { + Device uint64 + Inode uint64 + Rdevice uint64 + Offset uint64 + Sizelimit uint64 + Number uint32 + Encrypt_type uint32 + Encrypt_key_size uint32 + Flags uint32 + File_name [64]uint8 + Crypt_name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go index 93aec7e..66019c9 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go @@ -2475,3 +2475,40 @@ const ( LINUX_CAPABILITY_VERSION_2 = 0x20071026 LINUX_CAPABILITY_VERSION_3 = 0x20080522 ) + +const ( + LO_FLAGS_READ_ONLY = 0x1 + LO_FLAGS_AUTOCLEAR = 0x4 + LO_FLAGS_PARTSCAN = 0x8 + LO_FLAGS_DIRECT_IO = 0x10 +) + +type LoopInfo struct { + Number int32 + Device uint16 + Inode uint32 + Rdevice uint16 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint32 + Reserved [4]uint8 +} +type LoopInfo64 struct { + Device uint64 + Inode uint64 + Rdevice uint64 + Offset uint64 + Sizelimit uint64 + Number uint32 + Encrypt_type uint32 + Encrypt_key_size uint32 + Flags uint32 + File_name [64]uint8 + Crypt_name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go index 0a03843..3104798 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go @@ -2476,3 +2476,41 @@ const ( LINUX_CAPABILITY_VERSION_2 = 0x20071026 LINUX_CAPABILITY_VERSION_3 = 0x20080522 ) + +const ( + LO_FLAGS_READ_ONLY = 0x1 + LO_FLAGS_AUTOCLEAR = 0x4 + LO_FLAGS_PARTSCAN = 0x8 + LO_FLAGS_DIRECT_IO = 0x10 +) + +type LoopInfo struct { + Number int32 + Device uint32 + Inode uint64 + Rdevice uint32 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]int8 + Encrypt_key [32]uint8 + Init [2]uint64 + Reserved [4]int8 + _ [4]byte +} +type LoopInfo64 struct { + Device uint64 + Inode uint64 + Rdevice uint64 + Offset uint64 + Sizelimit uint64 + Number uint32 + Encrypt_type uint32 + Encrypt_key_size uint32 + Flags uint32 + File_name [64]uint8 + Crypt_name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go index 2de0e58..46c8602 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go @@ -2481,3 +2481,40 @@ const ( LINUX_CAPABILITY_VERSION_2 = 0x20071026 LINUX_CAPABILITY_VERSION_3 = 0x20080522 ) + +const ( + LO_FLAGS_READ_ONLY = 0x1 + LO_FLAGS_AUTOCLEAR = 0x4 + LO_FLAGS_PARTSCAN = 0x8 + LO_FLAGS_DIRECT_IO = 0x10 +) + +type LoopInfo struct { + Number int32 + Device uint32 + Inode uint32 + Rdevice uint32 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]int8 + Encrypt_key [32]uint8 + Init [2]uint32 + Reserved [4]int8 +} +type LoopInfo64 struct { + Device uint64 + Inode uint64 + Rdevice uint64 + Offset uint64 + Sizelimit uint64 + Number uint32 + Encrypt_type uint32 + Encrypt_key_size uint32 + Flags uint32 + File_name [64]uint8 + Crypt_name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go index 3735eb4..c2fe1a6 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go @@ -2478,3 +2478,41 @@ const ( LINUX_CAPABILITY_VERSION_2 = 0x20071026 LINUX_CAPABILITY_VERSION_3 = 0x20080522 ) + +const ( + LO_FLAGS_READ_ONLY = 0x1 + LO_FLAGS_AUTOCLEAR = 0x4 + LO_FLAGS_PARTSCAN = 0x8 + LO_FLAGS_DIRECT_IO = 0x10 +) + +type LoopInfo struct { + Number int32 + Device uint32 + Inode uint64 + Rdevice uint32 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]int8 + Encrypt_key [32]uint8 + Init [2]uint64 + Reserved [4]int8 + _ [4]byte +} +type LoopInfo64 struct { + Device uint64 + Inode uint64 + Rdevice uint64 + Offset uint64 + Sizelimit uint64 + Number uint32 + Encrypt_type uint32 + Encrypt_key_size uint32 + Flags uint32 + File_name [64]uint8 + Crypt_name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go index 073c299..f1eb0d3 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go @@ -2478,3 +2478,41 @@ const ( LINUX_CAPABILITY_VERSION_2 = 0x20071026 LINUX_CAPABILITY_VERSION_3 = 0x20080522 ) + +const ( + LO_FLAGS_READ_ONLY = 0x1 + LO_FLAGS_AUTOCLEAR = 0x4 + LO_FLAGS_PARTSCAN = 0x8 + LO_FLAGS_DIRECT_IO = 0x10 +) + +type LoopInfo struct { + Number int32 + Device uint32 + Inode uint64 + Rdevice uint32 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]int8 + Encrypt_key [32]uint8 + Init [2]uint64 + Reserved [4]int8 + _ [4]byte +} +type LoopInfo64 struct { + Device uint64 + Inode uint64 + Rdevice uint64 + Offset uint64 + Sizelimit uint64 + Number uint32 + Encrypt_type uint32 + Encrypt_key_size uint32 + Flags uint32 + File_name [64]uint8 + Crypt_name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go index 58d09f7..8759bc3 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go @@ -2481,3 +2481,40 @@ const ( LINUX_CAPABILITY_VERSION_2 = 0x20071026 LINUX_CAPABILITY_VERSION_3 = 0x20080522 ) + +const ( + LO_FLAGS_READ_ONLY = 0x1 + LO_FLAGS_AUTOCLEAR = 0x4 + LO_FLAGS_PARTSCAN = 0x8 + LO_FLAGS_DIRECT_IO = 0x10 +) + +type LoopInfo struct { + Number int32 + Device uint32 + Inode uint32 + Rdevice uint32 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]int8 + Encrypt_key [32]uint8 + Init [2]uint32 + Reserved [4]int8 +} +type LoopInfo64 struct { + Device uint64 + Inode uint64 + Rdevice uint64 + Offset uint64 + Sizelimit uint64 + Number uint32 + Encrypt_type uint32 + Encrypt_key_size uint32 + Flags uint32 + File_name [64]uint8 + Crypt_name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go index 3f1e62e..a812005 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go @@ -2486,3 +2486,41 @@ const ( LINUX_CAPABILITY_VERSION_2 = 0x20071026 LINUX_CAPABILITY_VERSION_3 = 0x20080522 ) + +const ( + LO_FLAGS_READ_ONLY = 0x1 + LO_FLAGS_AUTOCLEAR = 0x4 + LO_FLAGS_PARTSCAN = 0x8 + LO_FLAGS_DIRECT_IO = 0x10 +) + +type LoopInfo struct { + Number int32 + Device uint64 + Inode uint64 + Rdevice uint64 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 + Reserved [4]uint8 + _ [4]byte +} +type LoopInfo64 struct { + Device uint64 + Inode uint64 + Rdevice uint64 + Offset uint64 + Sizelimit uint64 + Number uint32 + Encrypt_type uint32 + Encrypt_key_size uint32 + Flags uint32 + File_name [64]uint8 + Crypt_name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go index e67be11..74b7a91 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go @@ -2486,3 +2486,41 @@ const ( LINUX_CAPABILITY_VERSION_2 = 0x20071026 LINUX_CAPABILITY_VERSION_3 = 0x20080522 ) + +const ( + LO_FLAGS_READ_ONLY = 0x1 + LO_FLAGS_AUTOCLEAR = 0x4 + LO_FLAGS_PARTSCAN = 0x8 + LO_FLAGS_DIRECT_IO = 0x10 +) + +type LoopInfo struct { + Number int32 + Device uint64 + Inode uint64 + Rdevice uint64 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 + Reserved [4]uint8 + _ [4]byte +} +type LoopInfo64 struct { + Device uint64 + Inode uint64 + Rdevice uint64 + Offset uint64 + Sizelimit uint64 + Number uint32 + Encrypt_type uint32 + Encrypt_key_size uint32 + Flags uint32 + File_name [64]uint8 + Crypt_name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go index f44f294..8344583 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go @@ -2503,3 +2503,41 @@ const ( LINUX_CAPABILITY_VERSION_2 = 0x20071026 LINUX_CAPABILITY_VERSION_3 = 0x20080522 ) + +const ( + LO_FLAGS_READ_ONLY = 0x1 + LO_FLAGS_AUTOCLEAR = 0x4 + LO_FLAGS_PARTSCAN = 0x8 + LO_FLAGS_DIRECT_IO = 0x10 +) + +type LoopInfo struct { + Number int32 + Device uint32 + Inode uint64 + Rdevice uint32 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 + Reserved [4]uint8 + _ [4]byte +} +type LoopInfo64 struct { + Device uint64 + Inode uint64 + Rdevice uint64 + Offset uint64 + Sizelimit uint64 + Number uint32 + Encrypt_type uint32 + Encrypt_key_size uint32 + Flags uint32 + File_name [64]uint8 + Crypt_name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go index 90bf5dc..d8fc0bc 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go @@ -2500,3 +2500,41 @@ const ( LINUX_CAPABILITY_VERSION_2 = 0x20071026 LINUX_CAPABILITY_VERSION_3 = 0x20080522 ) + +const ( + LO_FLAGS_READ_ONLY = 0x1 + LO_FLAGS_AUTOCLEAR = 0x4 + LO_FLAGS_PARTSCAN = 0x8 + LO_FLAGS_DIRECT_IO = 0x10 +) + +type LoopInfo struct { + Number int32 + Device uint16 + Inode uint64 + Rdevice uint16 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]int8 + Encrypt_key [32]uint8 + Init [2]uint64 + Reserved [4]int8 + _ [4]byte +} +type LoopInfo64 struct { + Device uint64 + Inode uint64 + Rdevice uint64 + Offset uint64 + Sizelimit uint64 + Number uint32 + Encrypt_type uint32 + Encrypt_key_size uint32 + Flags uint32 + File_name [64]uint8 + Crypt_name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go index 4f054dc..5e0ab93 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go @@ -2481,3 +2481,41 @@ const ( LINUX_CAPABILITY_VERSION_2 = 0x20071026 LINUX_CAPABILITY_VERSION_3 = 0x20080522 ) + +const ( + LO_FLAGS_READ_ONLY = 0x1 + LO_FLAGS_AUTOCLEAR = 0x4 + LO_FLAGS_PARTSCAN = 0x8 + LO_FLAGS_DIRECT_IO = 0x10 +) + +type LoopInfo struct { + Number int32 + Device uint32 + Inode uint64 + Rdevice uint32 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]int8 + Encrypt_key [32]uint8 + Init [2]uint64 + Reserved [4]int8 + _ [4]byte +} +type LoopInfo64 struct { + Device uint64 + Inode uint64 + Rdevice uint64 + Offset uint64 + Sizelimit uint64 + Number uint32 + Encrypt_type uint32 + Encrypt_key_size uint32 + Flags uint32 + File_name [64]uint8 + Crypt_name [64]uint8 + Encrypt_key [32]uint8 + Init [2]uint64 +} diff --git a/vendor/modules.txt b/vendor/modules.txt index ab7e532..77006fa 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# github.com/99designs/keyring v1.0.0 +# github.com/99designs/keyring v1.1.6 github.com/99designs/keyring # github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053 github.com/alessio/shellescape @@ -39,7 +39,7 @@ github.com/aws/aws-sdk-go/service/sts/stsiface github.com/danieljoos/wincred # github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew/spew -# github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a +# github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b github.com/dvsekhvalnov/jose2go github.com/dvsekhvalnov/jose2go/aes github.com/dvsekhvalnov/jose2go/arrays @@ -48,7 +48,7 @@ github.com/dvsekhvalnov/jose2go/compact github.com/dvsekhvalnov/jose2go/kdf github.com/dvsekhvalnov/jose2go/keys/ecc github.com/dvsekhvalnov/jose2go/padding -# github.com/godbus/dbus v4.1.0+incompatible +# github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 github.com/godbus/dbus # github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c github.com/gsterjov/go-libsecret @@ -58,7 +58,7 @@ github.com/inconshreveable/mousetrap github.com/jmespath/go-jmespath # github.com/karalabe/hid v1.0.0 github.com/karalabe/hid -# github.com/keybase/go-keychain v0.0.0-20190604185112-cc436cc9fe98 +# github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d github.com/keybase/go-keychain # github.com/konsorten/go-windows-terminal-sequences v1.0.2 github.com/konsorten/go-windows-terminal-sequences @@ -68,6 +68,8 @@ github.com/marshallbrekka/go-u2fhost/bytes github.com/marshallbrekka/go-u2fhost/hid # github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir +# github.com/mtibben/percent v0.2.1 +github.com/mtibben/percent # github.com/pmezard/go-difflib v1.0.0 github.com/pmezard/go-difflib/difflib # github.com/segmentio/analytics-go v3.0.1+incompatible @@ -94,7 +96,7 @@ golang.org/x/crypto/ssh/terminal golang.org/x/net/html golang.org/x/net/html/atom golang.org/x/net/publicsuffix -# golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 +# golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 golang.org/x/sys/unix golang.org/x/sys/windows # golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 From abfb53d35f350f242ca38f4d0806f4e1fd5f45c9 Mon Sep 17 00:00:00 2001 From: Sean Jones Date: Tue, 16 Feb 2021 14:09:50 +0000 Subject: [PATCH 3/3] Update for better credential_process support Update aws-okta to support `okta_aws_role_arn` so profiles can be configured easier when using the credential process feature of the AWS SDK. In the AWS config `role_arn` requires either `source_profile` or `credential_source` to be specified for the profile, otherwise it is detected as partial credentials, ignoring the `credential_process` field. Allow `aws-okta` to use the specifed role in the field `okta_aws_role_arn` reduces the number of chained profiles required to correctly use bare awscli, kubectl and other AWS SDK enabled applications. --- README.md | 205 ++++++++++++++++++++++++++++++++---------------- go.sum | 1 + lib/provider.go | 30 +++---- 3 files changed, 156 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index cd99fff..1a67dbf 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ ### Windows -See [docs/windows.md](docs/windows.md) for information on getting this working with Windows. +See [docs/windows.md](docs/windows.md) for information on getting this working +with Windows. ## Usage @@ -18,83 +19,111 @@ See [docs/windows.md](docs/windows.md) for information on getting this working w $ aws-okta add ``` -This will prompt you for your Okta organization, custom domain, region, username, and password. These credentials will then be stored in your keyring for future use. +This will prompt you for your Okta organization, custom domain, region, +username, and password. These credentials will then be stored in your keyring +for future use. -### Exec - -```bash -$ aws-okta exec -- -``` - -Exec will assume the role specified by the given aws config profile and execute a command with the proper environment variables set. This command is a drop-in replacement for `aws-vault exec` and accepts all of the same command line flags: +### Configuring your AWS config -```bash -$ aws-okta help exec -exec will run the command specified with aws credentials set in the environment - -Usage: - aws-okta exec -- +The most seamless way to use `aws-okta` is via AWS SDK config credential +process. To do so you will need to setup your AWS config as `aws-okta` assumes +that your base role is one that has been configured for Okta's SAML integration +by your Okta admin. -Flags: - -a, --assume-role-ttl duration Expiration time for assumed role (default 1h0m0s) - -h, --help help for exec - -t, --session-ttl duration Expiration time for okta role session (default 1h0m0s) +Okta provides a guide for setting up that integration +[here](https://help.okta.com/en/prod/Content/Topics/DeploymentGuides/AWS/aws-deployment.htm). +During that configuration, your admin should be able to grab the AWS App Embed +URL from the General tab of the AWS application in your Okta org. You will need +to set that value in your `~/.aws/config` file, for example: -Global Flags: - -b, --backend string Secret backend to use [kwallet secret-service file] (default "file") - -d, --debug Enable debug logging +```ini +[okta] +aws_saml_url = home/amazon_aws/0ac4qfegf372HSvKF6a3/965 ``` -### Exec for EKS and Kubernetes - -`aws-okta` can also be used to authenticate `kubectl` to your AWS EKS cluster. Assuming you have [installed `kubectl`](https://docs.aws.amazon.com/eks/latest/userguide/install-kubectl.html), [setup your kubeconfig](https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html) and [installed `aws-iam-authenticator`](https://docs.aws.amazon.com/eks/latest/userguide/configure-kubectl.html), you can now access your EKS cluster with `kubectl`. Note that on a new cluster, your Okta CLI user needs to be using the same assumed role as the one who created the cluster. Otherwise, your cluster needs to have been configured to allow your assumed role. +Next, you need to set up your base Okta role. This will be one your admin +created while setting up the integration. It should be specified similar to +to any other aws profile. However, instead of `role_arn` we will use +`okta_aws_role_arn`: -```bash -$ aws-okta exec -- kubectl version --short ``` +[profile okta-dev] +# okta_aws_role_arn is used here instead of role_arn because +# role_arn requires either source_profile or credential_source to be set +okta_aws_role_arn = arn:aws:iam:::role/ +credential_process = aws-okta cred-process okta-dev +``` + +Your setup may require additional roles to be configured if your admin has set +up a more complicated role scheme like cross account roles. For more details on +the authentication process, see the internals section. -Likewise, most Kubernetes projects should work, like Helm and Ark. +### cred-process ```bash -$ aws-okta exec -- helm version --short +aws-okta cred-process profile ``` -### Configuring your aws config +`cred-process` generates the required JSON output for the AWS CLI external +process sourcing. This is set in the `credential_process` field in the AWS +config file. -`aws-okta` assumes that your base role is one that has been configured for Okta's SAML integration by your Okta admin. Okta provides a guide for setting up that integration [here](https://support.okta.com/help/servlet/fileField?retURL=%2Fhelp%2Farticles%2FKnowledge_Article%2FAmazon-Web-Services-and-Okta-Integration-Guide&entityId=ka0F0000000MeyyIAC&field=File_Attachment__Body__s). During that configuration, your admin should be able to grab the AWS App Embed URL from the General tab of the AWS application in your Okta org. You will need to set that value in your `~/.aws/config` file, for example: +If your organization requires MFA it's advices to use [Okta +Verify](https://help.okta.com/en/prod/end-user/Content/Topics/end-user/ov-new-install.htm) +as the AWS CLI may hide output required to completing the MFA process. A fuller +example of the `credential_process` command would be: ```ini -[okta] -aws_saml_url = home/amazon_aws/0ac4qfegf372HSvKF6a3/965 +[profile okta-dev] +okta_aws_role_arn = arn:aws:iam:::role/ +credential_process = aws-okta cred-process --mfa-factor-type push --mfa-provider okta okta-dev +``` + ``` +Usage: + aws-okta cred-process [flags] -Next, you need to set up your base Okta role. This will be one your admin created while setting up the integration. It should be specified like any other aws profile: +Examples: +[profile foo] +credential_process = aws-okta cred-process profile -```ini -[profile okta-dev] -role_arn = arn:aws:iam:::role/ -region = +Flags: + -a, --assume-role-ttl duration Expiration time for assumed role (default 1h0m0s) + -h, --help help for cred-process + -p, --pretty Pretty print display + -t, --session-ttl duration Expiration time for okta role session (default 1h0m0s) + +Global Flags: + -b, --backend string Secret backend to use [keychain pass file] + -d, --debug Enable debug logging + --mfa-duo-device string Device to use phone1, phone2, u2f or token (default "phone1") + --mfa-factor-type string MFA Factor Type to use (eg push, token:software:totp) + --mfa-provider string MFA Provider to use (eg DUO, OKTA, GOOGLE) + --session-cache-single-item (alpha) Enable single-item session cache; aka AWS_OKTA_SESSION_CACHE_SINGLE_ITEM ``` -Your setup may require additional roles to be configured if your admin has set up a more complicated role scheme like cross account roles. For more details on the authentication process, see the internals section. #### A more complex example -The `aws_saml_url` can be set in the "okta" ini section, or on a per profile basis. This is useful if, for example, your organization has several Okta Apps (i.e. one for dev/qa and one for prod, or one for internal use and one for integrations with third party providers). For example: +The `aws_saml_url` can be set in the "okta" ini section, or on a per profile +basis. This is useful if, for example, your organization has several Okta Apps +(i.e. one for dev/qa and one for prod, or one for internal use and one for +integrations with third party providers). For example: ```ini [okta] # This is the "default" Okta App aws_saml_url = home/amazon_aws/cuZGoka9dAIFcyG0UllG/214 -[profile dev] -# This profile uses the default Okta app -role_arn = arn:aws:iam:::role/ +[profile okta-dev] +okta_aws_role_arn = arn:aws:iam:::role/ +credential_process = aws-okta cred-process okta-dev [profile integrations-auth] # This is a distinct Okta App aws_saml_url = home/amazon_aws/woezQTbGWUaLSrYDvINU/214 -role_arn = arn:aws:iam:::role/ +okta_aws_role_arn = arn:aws:iam:::role/ +credential_process = aws-okta cred-process integrations-auth [profile vendor] # This profile uses the "integrations-auth" Okta app combined with secondary role assumption @@ -124,12 +153,12 @@ define keyring key for each profile: [profile account-a] # This is a distinct Okta App aws_saml_url = home/amazon_aws/woezQTbGWUaLSrYDvINU/214 -role_arn = arn:aws:iam:::role/ +okta_aws_rolerole_arn = arn:aws:iam:::role/ okta_account_name = account-a [profile account-b] aws_saml_url = home/amazon_aws/woezQTbGaDAA4rYDvINU/123 -role_arn = arn:aws:iam:::role/ +okta_aws_role_arn = arn:aws:iam:::role/ okta_account_name = account-b ``` @@ -153,39 +182,95 @@ The AWS assume role TTL can also be set per-profile in the aws config: # Example with an initial and secondary role that are configured with a max session duration of 12 hours [profile ttldemo] aws_saml_url = home/amazon_aws/cuZGoka9dAIFcyG0UllG/214 -role_arn = arn:aws:iam:::role/ +okta_aws_role_arn = arn:aws:iam:::role/ session_ttl = 12h [profile ttldemo-role] source_profile = ttldemo -role_arn = arn:aws:iam:::role/ +okta_aws_role_arn = arn:aws:iam:::role/ assume_role_ttl = 12h ``` #### Multi-factor Authentication (MFA) configuration -If you have a single MFA factor configured, that factor will be automatically selected. By default, if you have multiple available MFA factors, then you will be prompted to select which one to use. However, if you have multiple factors and want to specify which factor to use, you can do one of the following: +If you have a single MFA factor configured, that factor will be automatically +selected. By default, if you have multiple available MFA factors, then you will +be prompted to select which one to use. However, if you have multiple factors +and want to specify which factor to use, you can do one of the following: * Specify on the command line with `--mfa-provider` and `--mfa-factor-type` * Specify with environment variables `AWS_OKTA_MFA_PROVIDER` and `AWS_OKTA_MFA_FACTOR_TYPE` * Specify in your aws config with `mfa_provider` and `mfa_factor_type` +### Exec + +When using applications that do not integrate the AWS SDK and need credentials +to be in the environment you can used `aws-okta exec` + +```bash +$ aws-okta exec -- +``` + +Exec will assume the role specified by the given aws config profile and execute +a command with the proper environment variables set. This command is a drop-in +replacement for `aws-vault exec` and accepts all of the same command line flags: + +```bash +$ aws-okta help exec +exec will run the command specified with aws credentials set in the environment + +Usage: + aws-okta exec -- + +Flags: + -a, --assume-role-ttl duration Expiration time for assumed role (default 1h0m0s) + -h, --help help for exec + -t, --session-ttl duration Expiration time for okta role session (default 1h0m0s) + +Global Flags: + -b, --backend string Secret backend to use [kwallet secret-service file] (default "file") + -d, --debug Enable debug logging +``` + +### Exec for EKS and Kubernetes + +`aws-okta` can also be used to authenticate `kubectl` to your AWS EKS cluster. +Assuming you have [installed +`kubectl`](https://docs.aws.amazon.com/eks/latest/userguide/install-kubectl.html), +[setup your +kubeconfig](https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html). +you can now access your EKS cluster with `kubectl`. Note that on a new cluster, +your Okta CLI user needs to be using the same assumed role as the one who +created the cluster. Otherwise, your cluster needs to have been configured to +allow your assumed role. + +```bash +$ kubectl version --short +``` + ### Shell completion -`aws-okta` provides shell completion support for BASH and ZSH via the `aws-okta completion` command. +`aws-okta` provides shell completion support for BASH and ZSH via the `aws-okta +completion` command. ## Backends -We use 99design's keyring package that they use in `aws-vault`. Because of this, you can choose between different pluggable secret storage backends just like in `aws-vault`. You can either set your backend from the command line as a flag, or set the `AWS_OKTA_BACKEND` environment variable. +We use 99design's keyring package that they use in `aws-vault`. Because of +this, you can choose between different pluggable secret storage backends just +like in `aws-vault`. You can either set your backend from the command line as a +flag, or set the `AWS_OKTA_BACKEND` environment variable. For Linux / Ubuntu add the following to your bash config / zshrc etc: + ``` export AWS_OKTA_BACKEND=secret-service ``` ## --session-cache-single-item aka AWS_OKTA_SESSION_CACHE_SINGLE_ITEM (alpha) -This flag enables a new secure session cache that stores all sessions in the same keyring item. For macOS users, this means drastically fewer authorization prompts when upgrading or running local builds. +This flag enables a new secure session cache that stores all sessions in the +same keyring item. For macOS users, this means drastically fewer authorization +prompts when upgrading or running local builds. No provision is made to migrate sessions between session caches. @@ -193,25 +278,13 @@ Implemented in [https://github.com/segmentio/aws-okta/issues/146](#146). ## Local Development -If you're developing in Linux, you'll need to get `libusb`. For Ubuntu, install the libusb-1.0-0-dev or use the `Dockerfile` provided in the repo. +If you're developing in Linux, you'll need to get `libusb`. For Ubuntu, install +the libusb-1.0-0-dev or use the `Dockerfile` provided in the repo. ## Running Tests `make test` -## Releasing - -Pushing a new tag will cause Circle to automatically create and push a linux release. After this is done, you should run (from a mac): - -```bash -$ export CIRCLE_TAG=`git describe --tags` -$ make release-mac -``` - -## Analytics - -`aws-okta` includes some usage analytics code which Segment uses internally for tracking usage of internal tools. This analytics code is turned off by default, and can only be enabled via a linker flag at build time, which we do not set for public github releases. - ## Internals ### Authentication process diff --git a/go.sum b/go.sum index f4335d1..deb5e5a 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM= github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU= diff --git a/lib/provider.go b/lib/provider.go index d740a33..77f04ec 100644 --- a/lib/provider.go +++ b/lib/provider.go @@ -217,7 +217,6 @@ func (p *Provider) getOktaAccountName() string { func (p *Provider) getSamlSessionCreds() (sts.Credentials, error) { var profileARN string - var ok bool source := sourceProfile(p.profile, p.profiles) oktaAwsSAMLUrl, err := p.getSamlURL() if err != nil { @@ -226,23 +225,26 @@ func (p *Provider) getSamlSessionCreds() (sts.Credentials, error) { oktaSessionCookieKey := p.getOktaSessionCookieKey() oktaAccountName := p.getOktaAccountName() + if _, ok := p.profiles[source]["okta_aws_role_arn"]; ok { + profileARN, _ = p.profiles[source]["okta_aws_role_arn"] + } else if _, ok := p.profiles[source]["role_arn"]; ok { + profileARN, _ = p.profiles[source]["role_arn"] + } else { + // profile does not have an okta_aws_role_arn or role_arn. This is ok + // as the user will be promted to choose a role from all available roles + // Support profiles similar to below + // [profile my-profile] + // output = json + // aws_saml_url = /home/some_saml_url + // mfa_provider = FIDO + // mfa_factor_type = u2f + log.Debugf("Profile '%s' does not have role_arn", source) + } + // if the assumable role is passed it have it override what is in the profile if p.AssumeRoleArn != "" { profileARN = p.AssumeRoleArn log.Debug("Overriding Assumable role with: ", profileARN) - } else { - profileARN, ok = p.profiles[source]["role_arn"] - if !ok { - // profile does not have a role_arn. This is ok as the user will be promted - // to choose a role from all available roles - // Support profiles similar to below - // [profile my-profile] - // output = json - // aws_saml_url = /home/some_saml_url - // mfa_provider = FIDO - // mfa_factor_type = u2f - log.Debugf("Profile '%s' does not have role_arn", source) - } } provider := OktaProvider{