creds: Add ImportCredential=

ImportCredential= takes a credential name and searches for a matching
credential in all the credential stores we know about it. It supports
globs which are expanded so that all matching credentials are loaded.
This commit is contained in:
Daan De Meyer
2023-06-08 14:09:18 +02:00
parent 96df2bd84b
commit bbfb25f4b9
23 changed files with 437 additions and 126 deletions
+152 -26
View File
@@ -1544,7 +1544,8 @@ bool exec_context_has_credentials(const ExecContext *context) {
assert(context);
return !hashmap_isempty(context->set_credentials) ||
!hashmap_isempty(context->load_credentials);
!hashmap_isempty(context->load_credentials) ||
!set_isempty(context->import_credentials);
}
#if HAVE_SECCOMP
@@ -2802,6 +2803,111 @@ static char **credential_search_path(const ExecParameters *params, CredentialSea
return TAKE_PTR(l);
}
static int maybe_decrypt_and_write_credential(
int dir_fd,
const char *id,
bool encrypted,
uid_t uid,
bool ownership_ok,
const char *data,
size_t size,
uint64_t *left) {
_cleanup_free_ void *plaintext = NULL;
size_t add;
int r;
if (encrypted) {
size_t plaintext_size = 0;
r = decrypt_credential_and_warn(id, now(CLOCK_REALTIME), NULL, NULL, data, size,
&plaintext, &plaintext_size);
if (r < 0)
return r;
data = plaintext;
size = plaintext_size;
}
add = strlen(id) + size;
if (add > *left)
return -E2BIG;
r = write_credential(dir_fd, id, data, size, uid, ownership_ok);
if (r < 0)
return log_debug_errno(r, "Failed to write credential '%s': %m", id);
*left -= add;
return 0;
}
static int load_credential_glob(
const char *path,
bool encrypted,
char **search_path,
ReadFullFileFlags flags,
int write_dfd,
uid_t uid,
bool ownership_ok,
uint64_t *left) {
int r;
STRV_FOREACH(d, search_path) {
_cleanup_globfree_ glob_t pglob = {};
_cleanup_free_ char *j = NULL;
j = path_join(*d, path);
if (!j)
return -ENOMEM;
r = safe_glob(j, 0, &pglob);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
for (unsigned n = 0; n < pglob.gl_pathc; n++) {
_cleanup_free_ char *fn = NULL;
_cleanup_(erase_and_freep) char *data = NULL;
size_t size;
/* path is absolute, hence pass AT_FDCWD as nop dir fd here */
r = read_full_file_full(
AT_FDCWD,
pglob.gl_pathv[n],
UINT64_MAX,
encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX,
flags,
NULL,
&data, &size);
if (r < 0)
return log_debug_errno(r, "Failed to read credential '%s': %m",
pglob.gl_pathv[n]);
r = path_extract_filename(pglob.gl_pathv[n], &fn);
if (r < 0)
return log_debug_errno(r, "Failed to extract filename from '%s': %m",
pglob.gl_pathv[n]);
r = maybe_decrypt_and_write_credential(
write_dfd,
fn,
encrypted,
uid,
ownership_ok,
data, size,
left);
if (r == -EEXIST)
continue;
if (r < 0)
return r;
}
}
return 0;
}
static int load_credential(
const ExecContext *context,
const ExecParameters *params,
@@ -2821,7 +2927,7 @@ static int load_credential(
_cleanup_free_ char *bindname = NULL;
const char *source = NULL;
bool missing_ok = true;
size_t size, add, maxsz;
size_t size, maxsz;
int r;
assert(context);
@@ -2923,28 +3029,7 @@ static int load_credential(
if (r < 0)
return log_debug_errno(r, "Failed to read credential '%s': %m", path);
if (encrypted) {
_cleanup_free_ void *plaintext = NULL;
size_t plaintext_size = 0;
r = decrypt_credential_and_warn(id, now(CLOCK_REALTIME), NULL, NULL, data, size, &plaintext, &plaintext_size);
if (r < 0)
return r;
free_and_replace(data, plaintext);
size = plaintext_size;
}
add = strlen(id) + size;
if (add > *left)
return -E2BIG;
r = write_credential(write_dfd, id, data, size, uid, ownership_ok);
if (r < 0)
return log_debug_errno(r, "Failed to write credential '%s': %m", id);
*left -= add;
return 0;
return maybe_decrypt_and_write_credential(write_dfd, id, encrypted, uid, ownership_ok, data, size, left);
}
struct load_cred_args {
@@ -3019,6 +3104,7 @@ static int acquire_credentials(
uint64_t left = CREDENTIALS_TOTAL_SIZE_MAX;
_cleanup_close_ int dfd = -EBADF;
const char *ic;
ExecLoadCredential *lc;
ExecSetCredential *sc;
int r;
@@ -3084,8 +3170,47 @@ static int acquire_credentials(
return r;
}
/* Second, we add in literally specified credentials. If the credentials already exist, we'll not add
* them, so that they can act as a "default" if the same credential is specified multiple times. */
/* Next, look for system credentials and credentials in the credentials store. Note that these do not
* override any credentials found earlier. */
SET_FOREACH(ic, context->import_credentials) {
_cleanup_free_ char **search_path = NULL;
search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED);
if (!search_path)
return -ENOMEM;
r = load_credential_glob(
ic,
/* encrypted = */ false,
search_path,
READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER,
dfd,
uid,
ownership_ok,
&left);
if (r < 0)
return r;
search_path = strv_free(search_path);
search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ENCRYPTED);
if (!search_path)
return -ENOMEM;
r = load_credential_glob(
ic,
/* encrypted = */ true,
search_path,
READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER|READ_FULL_FILE_UNBASE64,
dfd,
uid,
ownership_ok,
&left);
if (r < 0)
return r;
}
/* Finally, we add in literally specified credentials. If the credentials already exist, we'll not
* add them, so that they can act as a "default" if the same credential is specified multiple times. */
HASHMAP_FOREACH(sc, context->set_credentials) {
_cleanup_(erase_and_freep) void *plaintext = NULL;
const char *data;
@@ -5883,6 +6008,7 @@ void exec_context_done(ExecContext *c) {
c->load_credentials = hashmap_free(c->load_credentials);
c->set_credentials = hashmap_free(c->set_credentials);
c->import_credentials = set_free(c->import_credentials);
c->root_image_policy = image_policy_free(c->root_image_policy);
c->mount_image_policy = image_policy_free(c->mount_image_policy);