[PATCH 1/1] erofs-utils: lib: oci: support reading credentials from docker config

Gao Xiang hsiangkao at linux.alibaba.com
Mon Feb 9 20:06:25 AEDT 2026



On 2026/2/9 15:53, ChengyuZhu6 wrote:

..

> --- /dev/null
> +++ b/lib/remotes/docker_config.c
> @@ -0,0 +1,243 @@
> +// SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
> +/*
> + * Copyright (C) 2026 Tencent, Inc.
> + *             http://www.tencent.com/
> + */
> +#ifdef HAVE_CONFIG_H
> +#include "config.h"
> +#endif
> +
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/stat.h>
> +
> +#ifdef HAVE_JSON_C_JSON_H
> +#include <json-c/json.h>
> +#endif
> +
> +#include "erofs/defs.h"
> +#include "erofs/print.h"
> +#include "liberofs_base64.h"
> +#include "liberofs_dockerconfig.h"
> +
> +#ifndef HAVE_JSON_C_JSON_H
> +
> +int erofs_docker_config_lookup(const char *registry,
> +			       struct erofs_docker_credential *cred)
> +{
> +	(void)registry;
> +	(void)cred;
> +	return -EOPNOTSUPP;
> +}
> +
> +void erofs_docker_credential_free(struct erofs_docker_credential *cred)
> +{
> +	(void)cred;
> +}
> +
> +#else /* HAVE_JSON_C_JSON_H */
> +
> +static char *docker_config_path(void)
> +{
> +	const char *dir;
> +	char *path = NULL;
> +
> +	dir = getenv("DOCKER_CONFIG");
> +	if (dir && *dir) {
> +		if (asprintf(&path, "%s/config.json", dir) < 0)
> +			return NULL;
> +		return path;
> +	}

Is it possible to make DOCKER_CONFIG as "" to ignore
$HOME/.docker/config.json for script usage?

> +
> +	dir = getenv("HOME");
> +	if (!dir || !*dir) {
> +		erofs_dbg("HOME is not set, cannot locate docker config");
> +		return NULL;
> +	}
> +
> +	if (asprintf(&path, "%s/.docker/config.json", dir) < 0)
> +		return NULL;
> +	return path;
> +}
> +
> +static char *read_file_to_string(const char *path)
> +{
> +	FILE *fp;
> +	struct stat st;
> +	char *buf;
> +	size_t nread;
> +
> +	if (stat(path, &st) < 0)
> +		return NULL;
> +
> +	if (st.st_size <= 0 || st.st_size > (1 << 22))
> +		return NULL;
> +
> +	fp = fopen(path, "r");
> +	if (!fp)
> +		return NULL;
> +
> +	buf = malloc(st.st_size + 1);
> +	if (!buf) {
> +		fclose(fp);
> +		return NULL;
> +	}
> +
> +	nread = fread(buf, 1, st.st_size, fp);
> +	fclose(fp);
> +
> +	if ((off_t)nread != st.st_size) {
> +		free(buf);
> +		return NULL;
> +	}
> +	buf[nread] = '\0';
> +	return buf;
> +}
> +
> +/*
> + * Check if @key (an auths entry key) matches @registry.
> + *
> + * For Docker Hub: @registry is docker.io or registry-1.docker.io.
> + * The auths key in config.json is always "https://index.docker.io/v1/".
> + * For other registries: the auths key is an exact match against @registry.
> + */
> +static bool registry_match(const char *key, const char *registry)
> +{
> +	if (!key || !registry)
> +		return false;
> +
> +	if (!strcasecmp(registry, DOCKER_REGISTRY) ||
> +	    !strcasecmp(registry, DOCKER_API_REGISTRY))
> +		return !strcmp(key, DOCKER_HUB_AUTH_KEY);
> +
> +	return !strcasecmp(key, registry);
> +}
> +
> +static int decode_auth_field(const char *b64, char **out_user, char **out_pass)
> +{
> +	int b64_len = strlen(b64);
> +	int decoded_max = b64_len;
> +	u8 *decoded;
> +	int decoded_len;
> +	char *colon;
> +
> +	decoded = malloc(decoded_max + 1);
> +	if (!decoded)
> +		return -ENOMEM;
> +
> +	decoded_len = erofs_base64_decode(b64, b64_len, decoded);
> +	if (decoded_len <= 0) {
> +		free(decoded);
> +		return -EINVAL;
> +	}
> +	decoded[decoded_len] = '\0';
> +
> +	colon = strchr((char *)decoded, ':');
> +	if (!colon) {
> +		memset(decoded, 0, decoded_len);
> +		free(decoded);
> +		return -EINVAL;
> +	}
> +
> +	*colon = '\0';
> +	*out_user = strdup((char *)decoded);
> +	*out_pass = strdup(colon + 1);
> +
> +	memset(decoded, 0, decoded_len);
> +	free(decoded);
> +
> +	if (!*out_user || !*out_pass) {
> +		free(*out_user);
> +		free(*out_pass);
> +		*out_user = NULL;
> +		*out_pass = NULL;
> +		return -ENOMEM;
> +	}
> +	return 0;
> +}
> +
> +int erofs_docker_config_lookup(const char *registry,
> +			       struct erofs_docker_credential *cred)
> +{
> +	char *path = NULL;
> +	char *content = NULL;
> +	struct json_object *root = NULL, *auths_obj = NULL;
> +	int ret = -ENOENT;
> +
> +	memset(cred, 0, sizeof(*cred));
> +
> +	path = docker_config_path();
> +	if (!path)
> +		return -ENOENT;
> +
> +	content = read_file_to_string(path);
> +	if (!content) {
> +		erofs_dbg("cannot read docker config: %s", path);
> +		free(path);
> +		return -ENOENT;
> +	}
> +	free(path);
> +
> +	root = json_tokener_parse(content);
> +	memset(content, 0, strlen(content));
> +	free(content);
> +
> +	if (!root) {
> +		erofs_warn("failed to parse docker config.json");
> +		return -EINVAL;
> +	}
> +
> +	if (!json_object_object_get_ex(root, "auths", &auths_obj)) {
> +		erofs_dbg("no \"auths\" in docker config.json");
> +		json_object_put(root);
> +		return -ENOENT;
> +	}
> +
> +	struct json_object_iterator it = json_object_iter_begin(auths_obj);
> +	struct json_object_iterator end = json_object_iter_end(auths_obj);
> +
> +	while (!json_object_iter_equal(&it, &end)) {
> +		const char *key = json_object_iter_peek_name(&it);
> +		struct json_object *entry, *auth_field;
> +		const char *b64;
> +
> +		if (!registry_match(key, registry)) {
> +			json_object_iter_next(&it);
> +			continue;
> +		}
> +
> +		entry = json_object_iter_peek_value(&it);
> +		if (json_object_object_get_ex(entry, "auth", &auth_field)) {
> +			b64 = json_object_get_string(auth_field);
> +			if (b64 && *b64) {
> +				ret = decode_auth_field(b64, &cred->username,
> +							&cred->password);
> +				if (!ret)
> +					erofs_dbg("found docker credentials for %s",
> +						  registry);
> +			}
> +		}
> +		break;
> +	}
> +
> +	json_object_put(root);
> +	return ret;
> +}
> +
> +void erofs_docker_credential_free(struct erofs_docker_credential *cred)
> +{
> +	if (cred->username) {
> +		memset(cred->username, 0, strlen(cred->username));
> +		free(cred->username);
> +		cred->username = NULL;
> +	}
> +	if (cred->password) {
> +		memset(cred->password, 0, strlen(cred->password));
> +		free(cred->password);
> +		cred->password = NULL;
> +	}

maybe introduce erofs_free_sensitive() for these usage.

Thanks,
Gao Xiang


More information about the Linux-erofs mailing list