diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index 3a8dd550fe..32b26ceeb0 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -97,7 +97,7 @@ github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7 google.golang.org/genproto b3e7c2fb04031add52c4817f53f43757ccbf9c18 # native credentials -github.com/docker/docker-credential-helpers f72c04f1d8e71959a6d103f808c50ccbad79b9fd +github.com/docker/docker-credential-helpers v0.5.0 # containerd github.com/docker/containerd 9f68f96b8c0746e254b52bc1defcf7cc0c1a62eb diff --git a/components/engine/vendor/github.com/docker/docker-credential-helpers/README.md b/components/engine/vendor/github.com/docker/docker-credential-helpers/README.md index 9c11e1f9fe..e795f7c5e4 100644 --- a/components/engine/vendor/github.com/docker/docker-credential-helpers/README.md +++ b/components/engine/vendor/github.com/docker/docker-credential-helpers/README.md @@ -58,11 +58,12 @@ You can see examples of each function in the [client](https://godoc.org/github.c ## 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 three valid values: +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. diff --git a/components/engine/vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go b/components/engine/vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go index 3c4eec7bd3..cfe0cb134e 100644 --- a/components/engine/vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go +++ b/components/engine/vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go @@ -17,6 +17,15 @@ type Credentials struct { Secret string } +// 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" + +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. diff --git a/components/engine/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.c b/components/engine/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.c index d8105fccee..f84d61ee5d 100644 --- a/components/engine/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.c +++ b/components/engine/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.c @@ -1,5 +1,6 @@ #include "osxkeychain_darwin.h" #include +#include #include #include @@ -13,7 +14,9 @@ char *get_error(OSStatus status) { return buf; } -char *keychain_add(struct Server *server, char *username, char *secret) { +char *keychain_add(struct Server *server, char *label, char *username, char *secret) { + SecKeychainItemRef item; + OSStatus status = SecKeychainAddInternetPassword( NULL, strlen(server->host), server->host, @@ -24,11 +27,27 @@ char *keychain_add(struct Server *server, char *username, char *secret) { server->proto, kSecAuthenticationTypeDefault, strlen(secret), secret, - NULL + &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; } @@ -115,44 +134,42 @@ char * CFStringToCharArr(CFStringRef aString) { return NULL; } -char *keychain_list(char *** paths, char *** accts, unsigned int *list_l) { +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); + query, + &result); + + CFRelease(credsLabelCF); + //Ran a search and store the results in result if (status) { return get_error(status); } - int numKeys = CFArrayGetCount(result); + CFIndex numKeys = CFArrayGetCount(result); *paths = (char **) malloc((int)sizeof(char *)*numKeys); *accts = (char **) malloc((int)sizeof(char *)*numKeys); //result is of type CFArray - for(int i=0; i @@ -10,11 +10,12 @@ package osxkeychain import "C" import ( "errors" - "github.com/docker/docker-credential-helpers/credentials" "net/url" "strconv" "strings" "unsafe" + + "github.com/docker/docker-credential-helpers/credentials" ) // errCredentialsNotFound is the specific error message returned by OS X @@ -26,18 +27,22 @@ 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, username, secret) + errMsg := C.keychain_add(s, label, username, secret) if errMsg != nil { defer C.free(unsafe.Pointer(errMsg)) return errors.New(C.GoString(errMsg)) @@ -96,12 +101,15 @@ func (h Osxkeychain) Get(serverURL string) (string, string, error) { // 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(&pathsC, &acctsC, &listLenC) + errMsg := C.keychain_list(credsLabelC, &pathsC, &acctsC, &listLenC) if errMsg != nil { defer C.free(unsafe.Pointer(errMsg)) goMsg := C.GoString(errMsg) diff --git a/components/engine/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.h b/components/engine/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.h index 6b01e97847..c54e7d7288 100644 --- a/components/engine/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.h +++ b/components/engine/vendor/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_darwin.h @@ -7,8 +7,8 @@ struct Server { unsigned int port; }; -char *keychain_add(struct Server *server, char *username, char *secret); +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 *** data, char *** accts, unsigned int *list_l); +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/components/engine/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.c b/components/engine/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.c index ab23a5ebd6..35dea92da5 100644 --- a/components/engine/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.c +++ b/components/engine/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.c @@ -7,6 +7,7 @@ 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 }, @@ -16,11 +17,12 @@ const SecretSchema *docker_get_schema(void) return &docker_schema; } -GError *add(char *server, char *username, char *secret) { +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", @@ -40,7 +42,7 @@ GError *delete(char *server) { return NULL; } -char *get_username(SecretItem *item) { +char *get_attribute(const char *attribute, SecretItem *item) { GHashTable *attributes; GHashTableIter iter; gchar *value, *key; @@ -48,7 +50,7 @@ char *get_username(SecretItem *item) { 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, "username", strlen(key)) == 0) + if (strncmp(key, attribute, strlen(key)) == 0) return (char *)value; } g_hash_table_unref(attributes); @@ -71,7 +73,7 @@ GError *get(char *server, char **username, char **secret) { service = secret_service_get_sync(SECRET_SERVICE_NONE, NULL, &err); if (err == NULL) { - items = secret_service_search_sync(service, NULL, attributes, flags, NULL, &err); + 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); @@ -85,7 +87,7 @@ GError *get(char *server, char **username, char **secret) { *secret = strdup(secret_value_get(secretValue, &length)); secret_value_unref(secretValue); } - *username = get_username(l->data); + *username = get_attribute("username", l->data); } g_list_free_full(items, g_object_unref); } @@ -98,22 +100,30 @@ GError *get(char *server, char **username, char **secret) { return NULL; } -GError *list(char *** paths, char *** accts, unsigned int *list_l) { +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); - attributes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + 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; } - *paths = (char **) malloc((int)sizeof(char *)*numKeys); - *accts = (char **) malloc((int)sizeof(char *)*numKeys); + + 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; @@ -121,21 +131,25 @@ GError *list(char *** paths, char *** accts, unsigned int *list_l) { 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_username(current->data); + char *acctTmp = get_attribute("username",current->data); if (acctTmp==NULL) { acctTmp = "account not defined"; } - char *path = (char *) malloc(strlen(pathTmp)); - char *acct = (char *) malloc(strlen(acctTmp)); - path = pathTmp; - acct = acctTmp; - (*paths)[listNumber] = (char *) malloc(sizeof(char)*(strlen(path))); - memcpy((*paths)[listNumber], path, sizeof(char)*(strlen(path))); - (*accts)[listNumber] = (char *) malloc(sizeof(char)*(strlen(acct))); - memcpy((*accts)[listNumber], acct, sizeof(char)*(strlen(acct))); + + 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; } - *list_l = numKeys; + + *paths = (char **) realloc(tmp_paths, (int)sizeof(char *)*listNumber); + *accts = (char **) realloc(tmp_accts, (int)sizeof(char *)*listNumber); + + *list_l = listNumber; + return NULL; } diff --git a/components/engine/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.go b/components/engine/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.go index f3264ce240..ec1c5d3af9 100644 --- a/components/engine/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.go +++ b/components/engine/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.go @@ -22,6 +22,8 @@ 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) @@ -29,7 +31,7 @@ func (h Secretservice) Add(creds *credentials.Credentials) error { secret := C.CString(creds.Secret) defer C.free(unsafe.Pointer(secret)) - if err := C.add(server, username, secret); err != nil { + 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)) @@ -79,14 +81,17 @@ func (h Secretservice) Get(serverURL string) (string, string, error) { return user, pass, nil } -// List returns the stored URLs and corresponding usernames. +// 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(&pathsC, &acctsC, &listLenC) + 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") @@ -94,10 +99,14 @@ func (h Secretservice) List() (map[string]string, error) { defer C.freeListData(&pathsC, listLenC) defer C.freeListData(&acctsC, listLenC) + resp := make(map[string]string) + listLen := int(listLenC) + if listLen == 0 { + return resp, nil + } pathTmp := (*[1 << 30]*C.char)(unsafe.Pointer(pathsC))[:listLen:listLen] acctTmp := (*[1 << 30]*C.char)(unsafe.Pointer(acctsC))[:listLen:listLen] - resp := make(map[string]string) for i := 0; i < listLen; i++ { resp[C.GoString(pathTmp[i])] = C.GoString(acctTmp[i]) } diff --git a/components/engine/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.h b/components/engine/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.h index 319cdc06ab..a28179db3e 100644 --- a/components/engine/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.h +++ b/components/engine/vendor/github.com/docker/docker-credential-helpers/secretservice/secretservice_linux.h @@ -6,8 +6,8 @@ const SecretSchema *docker_get_schema(void) G_GNUC_CONST; #define DOCKER_SCHEMA docker_get_schema() -GError *add(char *server, char *username, char *secret); +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 *** paths, char *** accts, unsigned int *list_l); +GError *list(char *label, char *** paths, char *** accts, unsigned int *list_l); void freeListData(char *** data, unsigned int length);