diff --git a/vendor.conf b/vendor.conf index 9ca085aa..ec2d732b 100644 --- a/vendor.conf +++ b/vendor.conf @@ -5,7 +5,8 @@ k8s.io/apimachinery release-1.7 https://github.com/kubernetes/apimachinery k8s.io/apiserver release-1.7 https://github.com/kubernetes/apiserver # github.com/sirupsen/logrus v1.0.0 -github.com/containers/image 960e7256f8203360172ef97d5ace673dc085e30c +github.com/containers/image abb4cd79e3427bb2b02a5930814ef2ad19983c24 +github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1 github.com/ostreedev/ostree-go master github.com/containers/storage f8cff0727cf0802f0752ca58d2c05ec5270a47d5 github.com/containernetworking/cni v0.4.0 diff --git a/vendor/github.com/containers/image/README.md b/vendor/github.com/containers/image/README.md index 8e812bb7..237e9e38 100644 --- a/vendor/github.com/containers/image/README.md +++ b/vendor/github.com/containers/image/README.md @@ -62,9 +62,8 @@ or use the build tags described below to avoid the dependencies (e.g. using `go - `containers_image_openpgp`: Use a Golang-only OpenPGP implementation for signature verification instead of the default cgo/gpgme-based implementation; the primary downside is that creating new signatures with the Golang-only implementation is not supported. -- `containers_image_ostree_stub`: Instead of importing `ostree:` transport in `github.com/containers/image/transports/alltransports`, use a stub which reports that the transport is not supported. This allows building the library without requiring the `libostree` development libraries. - - (Note that explicitly importing `github.com/containers/image/ostree` will still depend on the `libostree` library, this build tag only affects generic users of …`/alltransports`.) +- `containers_image_ostree_stub`: Instead of importing `ostree:` transport in `github.com/containers/image/transports/alltransports`, use a stub which reports that the transport is not supported. This allows building the library without requiring the `libostree` development libraries. The `github.com/containers/image/ostree` package is completely disabled +and impossible to import when this build tag is in use. ## Contributing diff --git a/vendor/github.com/containers/image/docker/docker_client.go b/vendor/github.com/containers/image/docker/docker_client.go index 03737537..4c3d8b9f 100644 --- a/vendor/github.com/containers/image/docker/docker_client.go +++ b/vendor/github.com/containers/image/docker/docker_client.go @@ -19,6 +19,7 @@ import ( "github.com/containers/image/types" "github.com/containers/storage/pkg/homedir" "github.com/docker/distribution/registry/client" + helperclient "github.com/docker/docker-credential-helpers/client" "github.com/docker/go-connections/sockets" "github.com/docker/go-connections/tlsconfig" "github.com/opencontainers/go-digest" @@ -435,7 +436,12 @@ func getAuth(ctx *types.SystemContext, registry string) (string, string, error) return "", "", errors.Wrap(err, dockerCfgPath) } - // I'm feeling lucky + // First try cred helpers. They should always be normalized. + if ch, exists := dockerAuth.CredHelpers[registry]; exists { + return getAuthFromCredHelper(ch, registry) + } + + // I'm feeling lucky. if c, exists := dockerAuth.AuthConfigs[registry]; exists { return decodeDockerAuth(c.Auth) } @@ -545,6 +551,18 @@ type dockerAuthConfig struct { type dockerConfigFile struct { AuthConfigs map[string]dockerAuthConfig `json:"auths"` + CredHelpers map[string]string `json:"credHelpers,omitempty"` +} + +func getAuthFromCredHelper(credHelper, registry string) (string, string, error) { + helperName := fmt.Sprintf("docker-credential-%s", credHelper) + p := helperclient.NewShellProgramFunc(helperName) + creds, err := helperclient.Get(p, registry) + if err != nil { + return "", "", err + } + + return creds.Username, creds.Secret, nil } func decodeDockerAuth(s string) (string, string, error) { diff --git a/vendor/github.com/containers/image/image/oci.go b/vendor/github.com/containers/image/image/oci.go index 048387ec..7ea5d2bb 100644 --- a/vendor/github.com/containers/image/image/oci.go +++ b/vendor/github.com/containers/image/image/oci.go @@ -56,7 +56,7 @@ func (m *manifestOCI1) manifestMIMEType() string { // ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object. // Note that the config object may not exist in the underlying storage in the return value of UpdatedImage! Use ConfigBlob() below. func (m *manifestOCI1) ConfigInfo() types.BlobInfo { - return types.BlobInfo{Digest: m.ConfigDescriptor.Digest, Size: m.ConfigDescriptor.Size} + return types.BlobInfo{Digest: m.ConfigDescriptor.Digest, Size: m.ConfigDescriptor.Size, Annotations: m.ConfigDescriptor.Annotations} } // ConfigBlob returns the blob described by ConfigInfo, iff ConfigInfo().Digest != ""; nil otherwise. @@ -109,7 +109,7 @@ func (m *manifestOCI1) OCIConfig() (*imgspecv1.Image, error) { func (m *manifestOCI1) LayerInfos() []types.BlobInfo { blobs := []types.BlobInfo{} for _, layer := range m.LayersDescriptors { - blobs = append(blobs, types.BlobInfo{Digest: layer.Digest, Size: layer.Size}) + blobs = append(blobs, types.BlobInfo{Digest: layer.Digest, Size: layer.Size, Annotations: layer.Annotations}) } return blobs } @@ -159,6 +159,7 @@ func (m *manifestOCI1) UpdatedImage(options types.ManifestUpdateOptions) (types. copy.LayersDescriptors[i].MediaType = m.LayersDescriptors[i].MediaType copy.LayersDescriptors[i].Digest = info.Digest copy.LayersDescriptors[i].Size = info.Size + copy.LayersDescriptors[i].Annotations = info.Annotations } } // Ignore options.EmbeddedDockerReference: it may be set when converting from schema1, but we really don't care. diff --git a/vendor/github.com/containers/image/ostree/ostree_dest.go b/vendor/github.com/containers/image/ostree/ostree_dest.go index c056b83b..f03ef65a 100644 --- a/vendor/github.com/containers/image/ostree/ostree_dest.go +++ b/vendor/github.com/containers/image/ostree/ostree_dest.go @@ -1,3 +1,5 @@ +// +build !containers_image_ostree_stub + package ostree import ( diff --git a/vendor/github.com/containers/image/ostree/ostree_transport.go b/vendor/github.com/containers/image/ostree/ostree_transport.go index f165b13f..defdc63c 100644 --- a/vendor/github.com/containers/image/ostree/ostree_transport.go +++ b/vendor/github.com/containers/image/ostree/ostree_transport.go @@ -1,3 +1,5 @@ +// +build !containers_image_ostree_stub + package ostree import ( diff --git a/vendor/github.com/containers/image/types/types.go b/vendor/github.com/containers/image/types/types.go index 6bcd392f..78afe09f 100644 --- a/vendor/github.com/containers/image/types/types.go +++ b/vendor/github.com/containers/image/types/types.go @@ -94,9 +94,10 @@ type ImageReference interface { // BlobInfo collects known information about a blob (layer/config). // In some situations, some fields may be unknown, in others they may be mandatory; documenting an “unknown” value here does not override that. type BlobInfo struct { - Digest digest.Digest // "" if unknown. - Size int64 // -1 if unknown - URLs []string + Digest digest.Digest // "" if unknown. + Size int64 // -1 if unknown + URLs []string + Annotations map[string]string } // ImageSource is a service, possibly remote (= slow), to download components of a single image. diff --git a/vendor/github.com/containers/image/vendor.conf b/vendor/github.com/containers/image/vendor.conf index 669b5a76..d5bae3b0 100644 --- a/vendor/github.com/containers/image/vendor.conf +++ b/vendor/github.com/containers/image/vendor.conf @@ -1,6 +1,7 @@ github.com/sirupsen/logrus v1.0.0 github.com/containers/storage 47536c89fcc545a87745e1a1573addc439409165 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 +github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1 github.com/docker/distribution 5f6282db7d65e6d72ad7c2cc66310724a57be716 github.com/docker/docker 30eb4d8cdc422b023d5f11f29a82ecb73554183b github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d diff --git a/vendor/github.com/docker/docker-credential-helpers/LICENSE b/vendor/github.com/docker/docker-credential-helpers/LICENSE new file mode 100644 index 00000000..1ea555e2 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2016 David Calavera + +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/docker/docker-credential-helpers/README.md b/vendor/github.com/docker/docker-credential-helpers/README.md new file mode 100644 index 00000000..f9cbc3fb --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/README.md @@ -0,0 +1,82 @@ +## Introduction + +docker-credential-helpers is a suite of programs to use native stores to keep Docker credentials safe. + +## Installation + +Go to the [Releases](https://github.com/docker/docker-credential-helpers/releases) page and download the binary that works better for you. Put that binary in your `$PATH`, so Docker can find it. + +### Building from scratch + +The programs in this repository are written with the Go programming language. These instructions assume that you have previous knowledge about the language and you have it installed in your machine. + +1 - Download the source and put it in your `$GOPATH` with `go get`. + +``` +$ go get github.com/docker/docker-credential-helpers +``` + +2 - Use `make` to build the program you want. That will leave any executable in the `bin` directory inside the repository. + +``` +$ cd $GOPATH/docker/docker-credentials-helpers +$ make osxkeychain +``` + +3 - Put that binary in your `$PATH`, so Docker can find it. + +## Usage + +### With the Docker Engine + +Set the `credsStore` option in your `.docker/config.json` file with the suffix of the program you want to use. For instance, set it to `osxkeychain` if you want to use `docker-credential-osxkeychain`. + +```json +{ + "credsStore": "osxkeychain" +} +``` + +### With other command line applications + +The sub-package [client](https://godoc.org/github.com/docker/docker-credential-helpers/client) includes +functions to call external programs from your own command line applications. + +There are three things you need to know if you need to interact with a helper: + +1. The name of the program to execute, for instance `docker-credential-osxkeychain`. +2. The server address to identify the credentials, for instance `https://example.com`. +3. The username and secret to store, when you want to store credentials. + +You can see examples of each function in the [client](https://godoc.org/github.com/docker/docker-credential-helpers/client) documentation. + +### Available programs + +1. osxkeychain: Provides a helper to use the OS X keychain as credentials store. +2. secretservice: Provides a helper to use the D-Bus secret service as credentials store. +3. wincred: Provides a helper to use Windows credentials manager as store. +4. pass: Provides a helper to use `pass` as credentials store. + +#### Note + +`pass` needs to be configured for `docker-credential-pass` to work properly. +It must be initialized with a `gpg2` key ID. Make sure your GPG key exists is in `gpg2` keyring as `pass` uses `gpg2` instead of the regular `gpg`. + +## Development + +A credential helper can be any program that can read values from the standard input. We use the first argument in the command line to differentiate the kind of command to execute. There are four valid values: + +- `store`: Adds credentials to the keychain. The payload in the standard input is a JSON document with `ServerURL`, `Username` and `Secret`. +- `get`: Retrieves credentials from the keychain. The payload in the standard input is the raw value for the `ServerURL`. +- `erase`: Removes credentials from the keychain. The payload in the standard input is the raw value for the `ServerURL`. +- `list`: Lists stored credentials. There is no standard input payload. + +This repository also includes libraries to implement new credentials programs in Go. Adding a new helper program is pretty easy. You can see how the OS X keychain helper works in the [osxkeychain](osxkeychain) directory. + +1. Implement the interface `credentials.Helper` in `YOUR_PACKAGE/YOUR_PACKAGE_$GOOS.go` +2. Create a main program in `YOUR_PACKAGE/cmd/main_$GOOS.go`. +3. Add make tasks to build your program and run tests. + +## License + +MIT. See [LICENSE](LICENSE) for more information. diff --git a/vendor/github.com/docker/docker-credential-helpers/client/client.go b/vendor/github.com/docker/docker-credential-helpers/client/client.go new file mode 100644 index 00000000..d1d0434c --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/client/client.go @@ -0,0 +1,121 @@ +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + + "github.com/docker/docker-credential-helpers/credentials" +) + +// isValidCredsMessage checks if 'msg' contains invalid credentials error message. +// It returns whether the logs are free of invalid credentials errors and the error if it isn't. +// error values can be errCredentialsMissingServerURL or errCredentialsMissingUsername. +func isValidCredsMessage(msg string) error { + if credentials.IsCredentialsMissingServerURLMessage(msg) { + return credentials.NewErrCredentialsMissingServerURL() + } + + if credentials.IsCredentialsMissingUsernameMessage(msg) { + return credentials.NewErrCredentialsMissingUsername() + } + + return nil +} + +// Store uses an external program to save credentials. +func Store(program ProgramFunc, creds *credentials.Credentials) error { + cmd := program("store") + + buffer := new(bytes.Buffer) + if err := json.NewEncoder(buffer).Encode(creds); err != nil { + return err + } + cmd.Input(buffer) + + out, err := cmd.Output() + if err != nil { + t := strings.TrimSpace(string(out)) + + if isValidErr := isValidCredsMessage(t); isValidErr != nil { + err = isValidErr + } + + return fmt.Errorf("error storing credentials - err: %v, out: `%s`", err, t) + } + + return nil +} + +// Get executes an external program to get the credentials from a native store. +func Get(program ProgramFunc, serverURL string) (*credentials.Credentials, error) { + cmd := program("get") + cmd.Input(strings.NewReader(serverURL)) + + out, err := cmd.Output() + if err != nil { + t := strings.TrimSpace(string(out)) + + if credentials.IsErrCredentialsNotFoundMessage(t) { + return nil, credentials.NewErrCredentialsNotFound() + } + + if isValidErr := isValidCredsMessage(t); isValidErr != nil { + err = isValidErr + } + + return nil, fmt.Errorf("error getting credentials - err: %v, out: `%s`", err, t) + } + + resp := &credentials.Credentials{ + ServerURL: serverURL, + } + + if err := json.NewDecoder(bytes.NewReader(out)).Decode(resp); err != nil { + return nil, err + } + + return resp, nil +} + +// Erase executes a program to remove the server credentials from the native store. +func Erase(program ProgramFunc, serverURL string) error { + cmd := program("erase") + cmd.Input(strings.NewReader(serverURL)) + out, err := cmd.Output() + if err != nil { + t := strings.TrimSpace(string(out)) + + if isValidErr := isValidCredsMessage(t); isValidErr != nil { + err = isValidErr + } + + return fmt.Errorf("error erasing credentials - err: %v, out: `%s`", err, t) + } + + return nil +} + +// List executes a program to list server credentials in the native store. +func List(program ProgramFunc) (map[string]string, error) { + cmd := program("list") + cmd.Input(strings.NewReader("unused")) + out, err := cmd.Output() + if err != nil { + t := strings.TrimSpace(string(out)) + + if isValidErr := isValidCredsMessage(t); isValidErr != nil { + err = isValidErr + } + + return nil, fmt.Errorf("error listing credentials - err: %v, out: `%s`", err, t) + } + + var resp map[string]string + if err = json.NewDecoder(bytes.NewReader(out)).Decode(&resp); err != nil { + return nil, err + } + + return resp, nil +} diff --git a/vendor/github.com/docker/docker-credential-helpers/client/command.go b/vendor/github.com/docker/docker-credential-helpers/client/command.go new file mode 100644 index 00000000..8da33430 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/client/command.go @@ -0,0 +1,56 @@ +package client + +import ( + "fmt" + "io" + "os" + "os/exec" +) + +// Program is an interface to execute external programs. +type Program interface { + Output() ([]byte, error) + Input(in io.Reader) +} + +// ProgramFunc is a type of function that initializes programs based on arguments. +type ProgramFunc func(args ...string) Program + +// NewShellProgramFunc creates programs that are executed in a Shell. +func NewShellProgramFunc(name string) ProgramFunc { + return NewShellProgramFuncWithEnv(name, nil) +} + +// NewShellProgramFuncWithEnv creates programs that are executed in a Shell with environment variables +func NewShellProgramFuncWithEnv(name string, env *map[string]string) ProgramFunc { + return func(args ...string) Program { + return &Shell{cmd: createProgramCmdRedirectErr(name, args, env)} + } +} + +func createProgramCmdRedirectErr(commandName string, args []string, env *map[string]string) *exec.Cmd { + programCmd := exec.Command(commandName, args...) + programCmd.Env = os.Environ() + if env != nil { + for k, v := range *env { + programCmd.Env = append(programCmd.Env, fmt.Sprintf("%s=%s", k, v)) + } + } + programCmd.Stderr = os.Stderr + return programCmd +} + +// Shell invokes shell commands to talk with a remote credentials helper. +type Shell struct { + cmd *exec.Cmd +} + +// Output returns responses from the remote credentials helper. +func (s *Shell) Output() ([]byte, error) { + return s.cmd.Output() +} + +// Input sets the input to send to a remote credentials helper. +func (s *Shell) Input(in io.Reader) { + s.cmd.Stdin = in +} diff --git a/vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go b/vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go new file mode 100644 index 00000000..da8b594e --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go @@ -0,0 +1,186 @@ +package credentials + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "strings" +) + +// Credentials holds the information shared between docker and the credentials store. +type Credentials struct { + ServerURL string + Username string + Secret string +} + +// isValid checks the integrity of Credentials object such that no credentials lack +// a server URL or a username. +// It returns whether the credentials are valid and the error if it isn't. +// error values can be errCredentialsMissingServerURL or errCredentialsMissingUsername +func (c *Credentials) isValid() (bool, error) { + if len(c.ServerURL) == 0 { + return false, NewErrCredentialsMissingServerURL() + } + + if len(c.Username) == 0 { + return false, NewErrCredentialsMissingUsername() + } + + return true, nil +} + +// CredsLabel holds the way Docker credentials should be labeled as such in credentials stores that allow labelling. +// That label allows to filter out non-Docker credentials too at lookup/search in macOS keychain, +// Windows credentials manager and Linux libsecret. Default value is "Docker Credentials" +var CredsLabel = "Docker Credentials" + +// SetCredsLabel is a simple setter for CredsLabel +func SetCredsLabel(label string) { + CredsLabel = label +} + +// Serve initializes the credentials helper and parses the action argument. +// This function is designed to be called from a command line interface. +// It uses os.Args[1] as the key for the action. +// It uses os.Stdin as input and os.Stdout as output. +// This function terminates the program with os.Exit(1) if there is an error. +func Serve(helper Helper) { + var err error + if len(os.Args) != 2 { + err = fmt.Errorf("Usage: %s ", os.Args[0]) + } + + if err == nil { + err = HandleCommand(helper, os.Args[1], os.Stdin, os.Stdout) + } + + if err != nil { + fmt.Fprintf(os.Stdout, "%v\n", err) + os.Exit(1) + } +} + +// HandleCommand uses a helper and a key to run a credential action. +func HandleCommand(helper Helper, key string, in io.Reader, out io.Writer) error { + switch key { + case "store": + return Store(helper, in) + case "get": + return Get(helper, in, out) + case "erase": + return Erase(helper, in) + case "list": + return List(helper, out) + case "version": + return PrintVersion(out) + } + return fmt.Errorf("Unknown credential action `%s`", key) +} + +// Store uses a helper and an input reader to save credentials. +// The reader must contain the JSON serialization of a Credentials struct. +func Store(helper Helper, reader io.Reader) error { + scanner := bufio.NewScanner(reader) + + buffer := new(bytes.Buffer) + for scanner.Scan() { + buffer.Write(scanner.Bytes()) + } + + if err := scanner.Err(); err != nil && err != io.EOF { + return err + } + + var creds Credentials + if err := json.NewDecoder(buffer).Decode(&creds); err != nil { + return err + } + + if ok, err := creds.isValid(); !ok { + return err + } + + return helper.Add(&creds) +} + +// Get retrieves the credentials for a given server url. +// The reader must contain the server URL to search. +// The writer is used to write the JSON serialization of the credentials. +func Get(helper Helper, reader io.Reader, writer io.Writer) error { + scanner := bufio.NewScanner(reader) + + buffer := new(bytes.Buffer) + for scanner.Scan() { + buffer.Write(scanner.Bytes()) + } + + if err := scanner.Err(); err != nil && err != io.EOF { + return err + } + + serverURL := strings.TrimSpace(buffer.String()) + if len(serverURL) == 0 { + return NewErrCredentialsMissingServerURL() + } + + username, secret, err := helper.Get(serverURL) + if err != nil { + return err + } + + resp := Credentials{ + ServerURL: serverURL, + Username: username, + Secret: secret, + } + + buffer.Reset() + if err := json.NewEncoder(buffer).Encode(resp); err != nil { + return err + } + + fmt.Fprint(writer, buffer.String()) + return nil +} + +// Erase removes credentials from the store. +// The reader must contain the server URL to remove. +func Erase(helper Helper, reader io.Reader) error { + scanner := bufio.NewScanner(reader) + + buffer := new(bytes.Buffer) + for scanner.Scan() { + buffer.Write(scanner.Bytes()) + } + + if err := scanner.Err(); err != nil && err != io.EOF { + return err + } + + serverURL := strings.TrimSpace(buffer.String()) + if len(serverURL) == 0 { + return NewErrCredentialsMissingServerURL() + } + + return helper.Delete(serverURL) +} + +//List returns all the serverURLs of keys in +//the OS store as a list of strings +func List(helper Helper, writer io.Writer) error { + accts, err := helper.List() + if err != nil { + return err + } + return json.NewEncoder(writer).Encode(accts) +} + +//PrintVersion outputs the current version. +func PrintVersion(writer io.Writer) error { + fmt.Fprintln(writer, Version) + return nil +} diff --git a/vendor/github.com/docker/docker-credential-helpers/credentials/error.go b/vendor/github.com/docker/docker-credential-helpers/credentials/error.go new file mode 100644 index 00000000..fe6a5aef --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/credentials/error.go @@ -0,0 +1,102 @@ +package credentials + +const ( + // ErrCredentialsNotFound standardizes the not found error, so every helper returns + // the same message and docker can handle it properly. + errCredentialsNotFoundMessage = "credentials not found in native keychain" + + // ErrCredentialsMissingServerURL and ErrCredentialsMissingUsername standardize + // invalid credentials or credentials management operations + errCredentialsMissingServerURLMessage = "no credentials server URL" + errCredentialsMissingUsernameMessage = "no credentials username" +) + +// errCredentialsNotFound represents an error +// raised when credentials are not in the store. +type errCredentialsNotFound struct{} + +// Error returns the standard error message +// for when the credentials are not in the store. +func (errCredentialsNotFound) Error() string { + return errCredentialsNotFoundMessage +} + +// NewErrCredentialsNotFound creates a new error +// for when the credentials are not in the store. +func NewErrCredentialsNotFound() error { + return errCredentialsNotFound{} +} + +// IsErrCredentialsNotFound returns true if the error +// was caused by not having a set of credentials in a store. +func IsErrCredentialsNotFound(err error) bool { + _, ok := err.(errCredentialsNotFound) + return ok +} + +// IsErrCredentialsNotFoundMessage returns true if the error +// was caused by not having a set of credentials in a store. +// +// This function helps to check messages returned by an +// external program via its standard output. +func IsErrCredentialsNotFoundMessage(err string) bool { + return err == errCredentialsNotFoundMessage +} + +// errCredentialsMissingServerURL represents an error raised +// when the credentials object has no server URL or when no +// server URL is provided to a credentials operation requiring +// one. +type errCredentialsMissingServerURL struct{} + +func (errCredentialsMissingServerURL) Error() string { + return errCredentialsMissingServerURLMessage +} + +// errCredentialsMissingUsername represents an error raised +// when the credentials object has no username or when no +// username is provided to a credentials operation requiring +// one. +type errCredentialsMissingUsername struct{} + +func (errCredentialsMissingUsername) Error() string { + return errCredentialsMissingUsernameMessage +} + +// NewErrCredentialsMissingServerURL creates a new error for +// errCredentialsMissingServerURL. +func NewErrCredentialsMissingServerURL() error { + return errCredentialsMissingServerURL{} +} + +// NewErrCredentialsMissingUsername creates a new error for +// errCredentialsMissingUsername. +func NewErrCredentialsMissingUsername() error { + return errCredentialsMissingUsername{} +} + +// IsCredentialsMissingServerURL returns true if the error +// was an errCredentialsMissingServerURL. +func IsCredentialsMissingServerURL(err error) bool { + _, ok := err.(errCredentialsMissingServerURL) + return ok +} + +// IsCredentialsMissingServerURLMessage checks for an +// errCredentialsMissingServerURL in the error message. +func IsCredentialsMissingServerURLMessage(err string) bool { + return err == errCredentialsMissingServerURLMessage +} + +// IsCredentialsMissingUsername returns true if the error +// was an errCredentialsMissingUsername. +func IsCredentialsMissingUsername(err error) bool { + _, ok := err.(errCredentialsMissingUsername) + return ok +} + +// IsCredentialsMissingUsernameMessage checks for an +// errCredentialsMissingUsername in the error message. +func IsCredentialsMissingUsernameMessage(err string) bool { + return err == errCredentialsMissingUsernameMessage +} diff --git a/vendor/github.com/docker/docker-credential-helpers/credentials/helper.go b/vendor/github.com/docker/docker-credential-helpers/credentials/helper.go new file mode 100644 index 00000000..135acd25 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/credentials/helper.go @@ -0,0 +1,14 @@ +package credentials + +// Helper is the interface a credentials store helper must implement. +type Helper interface { + // Add appends credentials to the store. + Add(*Credentials) error + // Delete removes credentials from the store. + Delete(serverURL string) error + // Get retrieves credentials from the store. + // It returns username and secret as strings. + Get(serverURL string) (string, string, error) + // List returns the stored serverURLs and their associated usernames. + List() (map[string]string, error) +} diff --git a/vendor/github.com/docker/docker-credential-helpers/credentials/version.go b/vendor/github.com/docker/docker-credential-helpers/credentials/version.go new file mode 100644 index 00000000..033a5fee --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/credentials/version.go @@ -0,0 +1,4 @@ +package credentials + +// Version holds a string describing the current version +const Version = "0.6.0" diff --git a/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.c b/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.c new file mode 100644 index 00000000..f84d61ee --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.c @@ -0,0 +1,228 @@ +#include "osxkeychain_darwin.h" +#include +#include +#include +#include + +char *get_error(OSStatus status) { + char *buf = malloc(128); + CFStringRef str = SecCopyErrorMessageString(status, NULL); + int success = CFStringGetCString(str, buf, 128, kCFStringEncodingUTF8); + if (!success) { + strncpy(buf, "Unknown error", 128); + } + return buf; +} + +char *keychain_add(struct Server *server, char *label, char *username, char *secret) { + SecKeychainItemRef item; + + OSStatus status = SecKeychainAddInternetPassword( + NULL, + strlen(server->host), server->host, + 0, NULL, + strlen(username), username, + strlen(server->path), server->path, + server->port, + server->proto, + kSecAuthenticationTypeDefault, + strlen(secret), secret, + &item + ); + + if (status) { + return get_error(status); + } + + SecKeychainAttribute attribute; + SecKeychainAttributeList attrs; + attribute.tag = kSecLabelItemAttr; + attribute.data = label; + attribute.length = strlen(label); + attrs.count = 1; + attrs.attr = &attribute; + + status = SecKeychainItemModifyContent(item, &attrs, 0, NULL); + + if (status) { + return get_error(status); + } + + return NULL; +} + +char *keychain_get(struct Server *server, unsigned int *username_l, char **username, unsigned int *secret_l, char **secret) { + char *tmp; + SecKeychainItemRef item; + + OSStatus status = SecKeychainFindInternetPassword( + NULL, + strlen(server->host), server->host, + 0, NULL, + 0, NULL, + strlen(server->path), server->path, + server->port, + server->proto, + kSecAuthenticationTypeDefault, + secret_l, (void **)&tmp, + &item); + + if (status) { + return get_error(status); + } + + *secret = strdup(tmp); + SecKeychainItemFreeContent(NULL, tmp); + + SecKeychainAttributeList list; + SecKeychainAttribute attr; + + list.count = 1; + list.attr = &attr; + attr.tag = kSecAccountItemAttr; + + status = SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL); + if (status) { + return get_error(status); + } + + *username = strdup(attr.data); + *username_l = attr.length; + SecKeychainItemFreeContent(&list, NULL); + + return NULL; +} + +char *keychain_delete(struct Server *server) { + SecKeychainItemRef item; + + OSStatus status = SecKeychainFindInternetPassword( + NULL, + strlen(server->host), server->host, + 0, NULL, + 0, NULL, + strlen(server->path), server->path, + server->port, + server->proto, + kSecAuthenticationTypeDefault, + 0, NULL, + &item); + + if (status) { + return get_error(status); + } + + status = SecKeychainItemDelete(item); + if (status) { + return get_error(status); + } + return NULL; +} + +char * CFStringToCharArr(CFStringRef aString) { + if (aString == NULL) { + return NULL; + } + CFIndex length = CFStringGetLength(aString); + CFIndex maxSize = + CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; + char *buffer = (char *)malloc(maxSize); + if (CFStringGetCString(aString, buffer, maxSize, + kCFStringEncodingUTF8)) { + return buffer; + } + return NULL; +} + +char *keychain_list(char *credsLabel, char *** paths, char *** accts, unsigned int *list_l) { + CFStringRef credsLabelCF = CFStringCreateWithCString(NULL, credsLabel, kCFStringEncodingUTF8); + CFMutableDictionaryRef query = CFDictionaryCreateMutable (NULL, 1, NULL, NULL); + CFDictionaryAddValue(query, kSecClass, kSecClassInternetPassword); + CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue); + CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll); + CFDictionaryAddValue(query, kSecAttrLabel, credsLabelCF); + //Use this query dictionary + CFTypeRef result= NULL; + OSStatus status = SecItemCopyMatching( + query, + &result); + + CFRelease(credsLabelCF); + + //Ran a search and store the results in result + if (status) { + return get_error(status); + } + CFIndex numKeys = CFArrayGetCount(result); + *paths = (char **) malloc((int)sizeof(char *)*numKeys); + *accts = (char **) malloc((int)sizeof(char *)*numKeys); + //result is of type CFArray + for(CFIndex i=0; i +*/ +import "C" +import ( + "errors" + "net/url" + "strconv" + "strings" + "unsafe" + + "github.com/docker/docker-credential-helpers/credentials" +) + +// errCredentialsNotFound is the specific error message returned by OS X +// when the credentials are not in the keychain. +const errCredentialsNotFound = "The specified item could not be found in the keychain." + +// Osxkeychain handles secrets using the OS X Keychain as store. +type Osxkeychain struct{} + +// Add adds new credentials to the keychain. +func (h Osxkeychain) Add(creds *credentials.Credentials) error { + h.Delete(creds.ServerURL) + + s, err := splitServer(creds.ServerURL) + if err != nil { + return err + } + defer freeServer(s) + + label := C.CString(credentials.CredsLabel) + defer C.free(unsafe.Pointer(label)) + username := C.CString(creds.Username) + defer C.free(unsafe.Pointer(username)) + secret := C.CString(creds.Secret) + defer C.free(unsafe.Pointer(secret)) + + errMsg := C.keychain_add(s, label, username, secret) + if errMsg != nil { + defer C.free(unsafe.Pointer(errMsg)) + return errors.New(C.GoString(errMsg)) + } + + return nil +} + +// Delete removes credentials from the keychain. +func (h Osxkeychain) Delete(serverURL string) error { + s, err := splitServer(serverURL) + if err != nil { + return err + } + defer freeServer(s) + + errMsg := C.keychain_delete(s) + if errMsg != nil { + defer C.free(unsafe.Pointer(errMsg)) + return errors.New(C.GoString(errMsg)) + } + + return nil +} + +// Get returns the username and secret to use for a given registry server URL. +func (h Osxkeychain) Get(serverURL string) (string, string, error) { + s, err := splitServer(serverURL) + if err != nil { + return "", "", err + } + defer freeServer(s) + + var usernameLen C.uint + var username *C.char + var secretLen C.uint + var secret *C.char + defer C.free(unsafe.Pointer(username)) + defer C.free(unsafe.Pointer(secret)) + + errMsg := C.keychain_get(s, &usernameLen, &username, &secretLen, &secret) + if errMsg != nil { + defer C.free(unsafe.Pointer(errMsg)) + goMsg := C.GoString(errMsg) + if goMsg == errCredentialsNotFound { + return "", "", credentials.NewErrCredentialsNotFound() + } + + return "", "", errors.New(goMsg) + } + + user := C.GoStringN(username, C.int(usernameLen)) + pass := C.GoStringN(secret, C.int(secretLen)) + return user, pass, nil +} + +// List returns the stored URLs and corresponding usernames. +func (h Osxkeychain) List() (map[string]string, error) { + credsLabelC := C.CString(credentials.CredsLabel) + defer C.free(unsafe.Pointer(credsLabelC)) + + var pathsC **C.char + defer C.free(unsafe.Pointer(pathsC)) + var acctsC **C.char + defer C.free(unsafe.Pointer(acctsC)) + var listLenC C.uint + errMsg := C.keychain_list(credsLabelC, &pathsC, &acctsC, &listLenC) + if errMsg != nil { + defer C.free(unsafe.Pointer(errMsg)) + goMsg := C.GoString(errMsg) + return nil, errors.New(goMsg) + } + + defer C.freeListData(&pathsC, listLenC) + defer C.freeListData(&acctsC, listLenC) + + var listLen int + listLen = int(listLenC) + pathTmp := (*[1 << 30]*C.char)(unsafe.Pointer(pathsC))[:listLen:listLen] + acctTmp := (*[1 << 30]*C.char)(unsafe.Pointer(acctsC))[:listLen:listLen] + //taking the array of c strings into go while ignoring all the stuff irrelevant to credentials-helper + resp := make(map[string]string) + for i := 0; i < listLen; i++ { + if C.GoString(pathTmp[i]) == "0" { + continue + } + resp[C.GoString(pathTmp[i])] = C.GoString(acctTmp[i]) + } + return resp, nil +} + +func splitServer(serverURL string) (*C.struct_Server, error) { + u, err := parseURL(serverURL) + if err != nil { + return nil, err + } + + proto := C.kSecProtocolTypeHTTPS + if u.Scheme == "http" { + proto = C.kSecProtocolTypeHTTP + } + var port int + p := getPort(u) + if p != "" { + port, err = strconv.Atoi(p) + if err != nil { + return nil, err + } + } + + return &C.struct_Server{ + proto: C.SecProtocolType(proto), + host: C.CString(getHostname(u)), + port: C.uint(port), + path: C.CString(u.Path), + }, nil +} + +func freeServer(s *C.struct_Server) { + C.free(unsafe.Pointer(s.host)) + C.free(unsafe.Pointer(s.path)) +} + +// parseURL parses and validates a given serverURL to an url.URL, and +// returns an error if validation failed. Querystring parameters are +// omitted in the resulting URL, because they are not used in the helper. +// +// If serverURL does not have a valid scheme, `//` is used as scheme +// before parsing. This prevents the hostname being used as path, +// and the credentials being stored without host. +func parseURL(serverURL string) (*url.URL, error) { + // Check if serverURL has a scheme, otherwise add `//` as scheme. + if !strings.Contains(serverURL, "://") && !strings.HasPrefix(serverURL, "//") { + serverURL = "//" + serverURL + } + + u, err := url.Parse(serverURL) + if err != nil { + return nil, err + } + + if u.Scheme != "" && u.Scheme != "https" && u.Scheme != "http" { + return nil, errors.New("unsupported scheme: " + u.Scheme) + } + if getHostname(u) == "" { + return nil, errors.New("no hostname in URL") + } + + u.RawQuery = "" + return u, nil +} diff --git a/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.h b/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.h new file mode 100644 index 00000000..c54e7d72 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.h @@ -0,0 +1,14 @@ +#include + +struct Server { + SecProtocolType proto; + char *host; + char *path; + unsigned int port; +}; + +char *keychain_add(struct Server *server, char *label, char *username, char *secret); +char *keychain_get(struct Server *server, unsigned int *username_l, char **username, unsigned int *secret_l, char **secret); +char *keychain_delete(struct Server *server); +char *keychain_list(char *credsLabel, char *** data, char *** accts, unsigned int *list_l); +void freeListData(char *** data, unsigned int length); \ No newline at end of file diff --git a/vendor/github.com/docker/docker-credential-helpers/osxkeychain/url_go18.go b/vendor/github.com/docker/docker-credential-helpers/osxkeychain/url_go18.go new file mode 100644 index 00000000..0b7297d2 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/osxkeychain/url_go18.go @@ -0,0 +1,13 @@ +//+build go1.8 + +package osxkeychain + +import "net/url" + +func getHostname(u *url.URL) string { + return u.Hostname() +} + +func getPort(u *url.URL) string { + return u.Port() +} diff --git a/vendor/github.com/docker/docker-credential-helpers/osxkeychain/url_non_go18.go b/vendor/github.com/docker/docker-credential-helpers/osxkeychain/url_non_go18.go new file mode 100644 index 00000000..bdf9b7b0 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/osxkeychain/url_non_go18.go @@ -0,0 +1,41 @@ +//+build !go1.8 + +package osxkeychain + +import ( + "net/url" + "strings" +) + +func getHostname(u *url.URL) string { + return stripPort(u.Host) +} + +func getPort(u *url.URL) string { + return portOnly(u.Host) +} + +func stripPort(hostport string) string { + colon := strings.IndexByte(hostport, ':') + if colon == -1 { + return hostport + } + if i := strings.IndexByte(hostport, ']'); i != -1 { + return strings.TrimPrefix(hostport[:i], "[") + } + return hostport[:colon] +} + +func portOnly(hostport string) string { + colon := strings.IndexByte(hostport, ':') + if colon == -1 { + return "" + } + if i := strings.Index(hostport, "]:"); i != -1 { + return hostport[i+len("]:"):] + } + if strings.Contains(hostport, "]") { + return "" + } + return hostport[colon+len(":"):] +} diff --git a/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.c b/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.c new file mode 100644 index 00000000..35dea92d --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.c @@ -0,0 +1,162 @@ +#include +#include +#include "secretservice_linux.h" + +const SecretSchema *docker_get_schema(void) +{ + static const SecretSchema docker_schema = { + "io.docker.Credentials", SECRET_SCHEMA_NONE, + { + { "label", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "server", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "username", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "docker_cli", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "NULL", 0 }, + } + }; + return &docker_schema; +} + +GError *add(char *label, char *server, char *username, char *secret) { + GError *err = NULL; + + secret_password_store_sync (DOCKER_SCHEMA, SECRET_COLLECTION_DEFAULT, + server, secret, NULL, &err, + "label", label, + "server", server, + "username", username, + "docker_cli", "1", + NULL); + return err; +} + +GError *delete(char *server) { + GError *err = NULL; + + secret_password_clear_sync(DOCKER_SCHEMA, NULL, &err, + "server", server, + "docker_cli", "1", + NULL); + if (err != NULL) + return err; + return NULL; +} + +char *get_attribute(const char *attribute, SecretItem *item) { + GHashTable *attributes; + GHashTableIter iter; + gchar *value, *key; + + attributes = secret_item_get_attributes(item); + g_hash_table_iter_init(&iter, attributes); + while (g_hash_table_iter_next(&iter, (void **)&key, (void **)&value)) { + if (strncmp(key, attribute, strlen(key)) == 0) + return (char *)value; + } + g_hash_table_unref(attributes); + return NULL; +} + +GError *get(char *server, char **username, char **secret) { + GError *err = NULL; + GHashTable *attributes; + SecretService *service; + GList *items, *l; + SecretSearchFlags flags = SECRET_SEARCH_LOAD_SECRETS | SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK; + SecretValue *secretValue; + gsize length; + gchar *value; + + attributes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_insert(attributes, g_strdup("server"), g_strdup(server)); + g_hash_table_insert(attributes, g_strdup("docker_cli"), g_strdup("1")); + + service = secret_service_get_sync(SECRET_SERVICE_NONE, NULL, &err); + if (err == NULL) { + items = secret_service_search_sync(service, DOCKER_SCHEMA, attributes, flags, NULL, &err); + if (err == NULL) { + for (l = items; l != NULL; l = g_list_next(l)) { + value = secret_item_get_schema_name(l->data); + if (strncmp(value, "io.docker.Credentials", strlen(value)) != 0) { + g_free(value); + continue; + } + g_free(value); + secretValue = secret_item_get_secret(l->data); + if (secret != NULL) { + *secret = strdup(secret_value_get(secretValue, &length)); + secret_value_unref(secretValue); + } + *username = get_attribute("username", l->data); + } + g_list_free_full(items, g_object_unref); + } + g_object_unref(service); + } + g_hash_table_unref(attributes); + if (err != NULL) { + return err; + } + return NULL; +} + +GError *list(char *ref_label, char *** paths, char *** accts, unsigned int *list_l) { + GList *items; + GError *err = NULL; + SecretService *service; + SecretSearchFlags flags = SECRET_SEARCH_LOAD_SECRETS | SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK; + GHashTable *attributes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + + // List credentials with the right label only + g_hash_table_insert(attributes, g_strdup("label"), g_strdup(ref_label)); + + service = secret_service_get_sync(SECRET_SERVICE_NONE, NULL, &err); + if (err != NULL) { + return err; + } + + items = secret_service_search_sync(service, NULL, attributes, flags, NULL, &err); + int numKeys = g_list_length(items); + if (err != NULL) { + return err; + } + + char **tmp_paths = (char **) calloc(1,(int)sizeof(char *)*numKeys); + char **tmp_accts = (char **) calloc(1,(int)sizeof(char *)*numKeys); + + // items now contains our keys from the gnome keyring + // we will now put it in our two lists to return it to go + GList *current; + int listNumber = 0; + for(current = items; current!=NULL; current = current->next) { + char *pathTmp = secret_item_get_label(current->data); + // you cannot have a key without a label in the gnome keyring + char *acctTmp = get_attribute("username",current->data); + if (acctTmp==NULL) { + acctTmp = "account not defined"; + } + + tmp_paths[listNumber] = (char *) calloc(1, sizeof(char)*(strlen(pathTmp)+1)); + tmp_accts[listNumber] = (char *) calloc(1, sizeof(char)*(strlen(acctTmp)+1)); + + memcpy(tmp_paths[listNumber], pathTmp, sizeof(char)*(strlen(pathTmp)+1)); + memcpy(tmp_accts[listNumber], acctTmp, sizeof(char)*(strlen(acctTmp)+1)); + + listNumber = listNumber + 1; + } + + *paths = (char **) realloc(tmp_paths, (int)sizeof(char *)*listNumber); + *accts = (char **) realloc(tmp_accts, (int)sizeof(char *)*listNumber); + + *list_l = listNumber; + + return NULL; +} + +void freeListData(char *** data, unsigned int length) { + int i; + for(i=0; i +*/ +import "C" +import ( + "errors" + "unsafe" + + "github.com/docker/docker-credential-helpers/credentials" +) + +// Secretservice handles secrets using Linux secret-service as a store. +type Secretservice struct{} + +// Add adds new credentials to the keychain. +func (h Secretservice) Add(creds *credentials.Credentials) error { + if creds == nil { + return errors.New("missing credentials") + } + credsLabel := C.CString(credentials.CredsLabel) + defer C.free(unsafe.Pointer(credsLabel)) + server := C.CString(creds.ServerURL) + defer C.free(unsafe.Pointer(server)) + username := C.CString(creds.Username) + defer C.free(unsafe.Pointer(username)) + secret := C.CString(creds.Secret) + defer C.free(unsafe.Pointer(secret)) + + if err := C.add(credsLabel, server, username, secret); err != nil { + defer C.g_error_free(err) + errMsg := (*C.char)(unsafe.Pointer(err.message)) + return errors.New(C.GoString(errMsg)) + } + return nil +} + +// Delete removes credentials from the store. +func (h Secretservice) Delete(serverURL string) error { + if serverURL == "" { + return errors.New("missing server url") + } + server := C.CString(serverURL) + defer C.free(unsafe.Pointer(server)) + + if err := C.delete(server); err != nil { + defer C.g_error_free(err) + errMsg := (*C.char)(unsafe.Pointer(err.message)) + return errors.New(C.GoString(errMsg)) + } + return nil +} + +// Get returns the username and secret to use for a given registry server URL. +func (h Secretservice) Get(serverURL string) (string, string, error) { + if serverURL == "" { + return "", "", errors.New("missing server url") + } + var username *C.char + defer C.free(unsafe.Pointer(username)) + var secret *C.char + defer C.free(unsafe.Pointer(secret)) + server := C.CString(serverURL) + defer C.free(unsafe.Pointer(server)) + + err := C.get(server, &username, &secret) + if err != nil { + defer C.g_error_free(err) + errMsg := (*C.char)(unsafe.Pointer(err.message)) + return "", "", errors.New(C.GoString(errMsg)) + } + user := C.GoString(username) + pass := C.GoString(secret) + if pass == "" { + return "", "", credentials.NewErrCredentialsNotFound() + } + return user, pass, nil +} + +// List returns the stored URLs and corresponding usernames for a given credentials label +func (h Secretservice) List() (map[string]string, error) { + credsLabelC := C.CString(credentials.CredsLabel) + defer C.free(unsafe.Pointer(credsLabelC)) + + var pathsC **C.char + defer C.free(unsafe.Pointer(pathsC)) + var acctsC **C.char + defer C.free(unsafe.Pointer(acctsC)) + var listLenC C.uint + err := C.list(credsLabelC, &pathsC, &acctsC, &listLenC) + if err != nil { + defer C.free(unsafe.Pointer(err)) + return nil, errors.New("Error from list function in secretservice_linux.c likely due to error in secretservice library") + } + defer C.freeListData(&pathsC, listLenC) + defer C.freeListData(&acctsC, listLenC) + + resp := make(map[string]string) + + listLen := int(listLenC) + if listLen == 0 { + return resp, nil + } + // The maximum capacity of the following two slices is limited to (2^29)-1 to remain compatible + // with 32-bit platforms. The size of a `*C.char` (a pointer) is 4 Byte on a 32-bit system + // and (2^29)*4 == math.MaxInt32 + 1. -- See issue golang/go#13656 + pathTmp := (*[(1 << 29) - 1]*C.char)(unsafe.Pointer(pathsC))[:listLen:listLen] + acctTmp := (*[(1 << 29) - 1]*C.char)(unsafe.Pointer(acctsC))[:listLen:listLen] + for i := 0; i < listLen; i++ { + resp[C.GoString(pathTmp[i])] = C.GoString(acctTmp[i]) + } + + return resp, nil +} diff --git a/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.h b/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.h new file mode 100644 index 00000000..a28179db --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.h @@ -0,0 +1,13 @@ +#define SECRET_WITH_UNSTABLE 1 +#define SECRET_API_SUBJECT_TO_CHANGE 1 +#include + +const SecretSchema *docker_get_schema(void) G_GNUC_CONST; + +#define DOCKER_SCHEMA docker_get_schema() + +GError *add(char *label, char *server, char *username, char *secret); +GError *delete(char *server); +GError *get(char *server, char **username, char **secret); +GError *list(char *label, char *** paths, char *** accts, unsigned int *list_l); +void freeListData(char *** data, unsigned int length);