summaryrefslogtreecommitdiff
path: root/vendor/github.com/docker/docker-credential-helpers/osxkeychain
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/docker/docker-credential-helpers/osxkeychain')
-rw-r--r--vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.c228
-rw-r--r--vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.go196
-rw-r--r--vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.h14
-rw-r--r--vendor/github.com/docker/docker-credential-helpers/osxkeychain/url_go18.go13
-rw-r--r--vendor/github.com/docker/docker-credential-helpers/osxkeychain/url_non_go18.go41
5 files changed, 492 insertions, 0 deletions
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 000000000..f84d61ee5
--- /dev/null
+++ b/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.c
@@ -0,0 +1,228 @@
+#include "osxkeychain_darwin.h"
+#include <CoreFoundation/CoreFoundation.h>
+#include <Foundation/NSValue.h>
+#include <stdio.h>
+#include <string.h>
+
+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<numKeys; i++) {
+ CFDictionaryRef currKey = CFArrayGetValueAtIndex(result,i);
+
+ CFStringRef protocolTmp = CFDictionaryGetValue(currKey, CFSTR("ptcl"));
+ if (protocolTmp != NULL) {
+ CFStringRef protocolStr = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), protocolTmp);
+ if (CFStringCompare(protocolStr, CFSTR("htps"), 0) == kCFCompareEqualTo) {
+ protocolTmp = CFSTR("https://");
+ }
+ else {
+ protocolTmp = CFSTR("http://");
+ }
+ CFRelease(protocolStr);
+ }
+ else {
+ char * path = "0";
+ char * acct = "0";
+ (*paths)[i] = (char *) malloc(sizeof(char)*(strlen(path)));
+ memcpy((*paths)[i], path, sizeof(char)*(strlen(path)));
+ (*accts)[i] = (char *) malloc(sizeof(char)*(strlen(acct)));
+ memcpy((*accts)[i], acct, sizeof(char)*(strlen(acct)));
+ continue;
+ }
+
+ CFMutableStringRef str = CFStringCreateMutableCopy(NULL, 0, protocolTmp);
+ CFStringRef serverTmp = CFDictionaryGetValue(currKey, CFSTR("srvr"));
+ if (serverTmp != NULL) {
+ CFStringAppend(str, serverTmp);
+ }
+
+ CFStringRef pathTmp = CFDictionaryGetValue(currKey, CFSTR("path"));
+ if (pathTmp != NULL) {
+ CFStringAppend(str, pathTmp);
+ }
+
+ const NSNumber * portTmp = CFDictionaryGetValue(currKey, CFSTR("port"));
+ if (portTmp != NULL && portTmp.integerValue != 0) {
+ CFStringRef portStr = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), portTmp);
+ CFStringAppend(str, CFSTR(":"));
+ CFStringAppend(str, portStr);
+ CFRelease(portStr);
+ }
+
+ CFStringRef acctTmp = CFDictionaryGetValue(currKey, CFSTR("acct"));
+ if (acctTmp == NULL) {
+ acctTmp = CFSTR("account not defined");
+ }
+
+ char * path = CFStringToCharArr(str);
+ char * acct = CFStringToCharArr(acctTmp);
+
+ //We now have all we need, username and servername. Now export this to .go
+ (*paths)[i] = (char *) malloc(sizeof(char)*(strlen(path)+1));
+ memcpy((*paths)[i], path, sizeof(char)*(strlen(path)+1));
+ (*accts)[i] = (char *) malloc(sizeof(char)*(strlen(acct)+1));
+ memcpy((*accts)[i], acct, sizeof(char)*(strlen(acct)+1));
+
+ CFRelease(str);
+ }
+ *list_l = (int)numKeys;
+ return NULL;
+}
+
+void freeListData(char *** data, unsigned int length) {
+ for(int i=0; i<length; i++) {
+ free((*data)[i]);
+ }
+ free(*data);
+}
diff --git a/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.go b/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.go
new file mode 100644
index 000000000..439126761
--- /dev/null
+++ b/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.go
@@ -0,0 +1,196 @@
+package osxkeychain
+
+/*
+#cgo CFLAGS: -x objective-c -mmacosx-version-min=10.10
+#cgo LDFLAGS: -framework Security -framework Foundation -mmacosx-version-min=10.10
+
+#include "osxkeychain_darwin.h"
+#include <stdlib.h>
+*/
+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 000000000..c54e7d728
--- /dev/null
+++ b/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.h
@@ -0,0 +1,14 @@
+#include <Security/Security.h>
+
+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 000000000..0b7297d2f
--- /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 000000000..bdf9b7b00
--- /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(":"):]
+}