Merge pull request #23170 from poettering/creds-copy
import system credentials from sd-stub + qemu fw_cfg + kernel cmdline explicitly in PID 1
This commit is contained in:
+93
-16
@@ -2595,6 +2595,42 @@ static int write_credential(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char **credential_search_path(
|
||||
const ExecParameters *params,
|
||||
bool encrypted) {
|
||||
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
|
||||
assert(params);
|
||||
|
||||
/* Assemble a search path to find credentials in. We'll look in /etc/credstore/ (and similar
|
||||
* directories in /usr/lib/ + /run/) for all types of credentials. If we are looking for encrypted
|
||||
* credentials, also look in /etc/credstore.encrypted/ (and similar dirs). */
|
||||
|
||||
if (encrypted) {
|
||||
if (strv_extend(&l, params->received_encrypted_credentials_directory) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore.encrypted"), /* filter_duplicates= */ true) < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (params->received_credentials_directory)
|
||||
if (strv_extend(&l, params->received_credentials_directory) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0)
|
||||
return NULL;
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *t = strv_join(l, ":");
|
||||
|
||||
log_debug("Credential search path is: %s", t);
|
||||
}
|
||||
|
||||
return TAKE_PTR(l);
|
||||
}
|
||||
|
||||
static int load_credential(
|
||||
const ExecContext *context,
|
||||
const ExecParameters *params,
|
||||
@@ -2609,11 +2645,12 @@ static int load_credential(
|
||||
uint64_t *left) {
|
||||
|
||||
ReadFullFileFlags flags = READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER;
|
||||
_cleanup_strv_free_ char **search_path = NULL;
|
||||
_cleanup_(erase_and_freep) char *data = NULL;
|
||||
_cleanup_free_ char *j = NULL, *bindname = NULL;
|
||||
_cleanup_free_ char *bindname = NULL;
|
||||
const char *source = NULL;
|
||||
bool missing_ok = true;
|
||||
const char *source;
|
||||
size_t size, add;
|
||||
size_t size, add, maxsz;
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
@@ -2624,10 +2661,25 @@ static int load_credential(
|
||||
assert(write_dfd >= 0);
|
||||
assert(left);
|
||||
|
||||
if (path_is_absolute(path) || read_dfd >= 0) {
|
||||
/* If this is an absolute path (or a directory fd is specifier relative which to read), read
|
||||
* the data directly from it, and support AF_UNIX sockets */
|
||||
if (read_dfd >= 0) {
|
||||
/* If a directory fd is specified, then read the file directly from that dir. In this case we
|
||||
* won't do AF_UNIX stuff (we simply don't want to recursively iterate down a tree of AF_UNIX
|
||||
* IPC sockets). It's OK if a file vanishes here in the time we enumerate it and intend to
|
||||
* open it. */
|
||||
|
||||
if (!filename_is_valid(path)) /* safety check */
|
||||
return -EINVAL;
|
||||
|
||||
missing_ok = true;
|
||||
source = path;
|
||||
|
||||
} else if (path_is_absolute(path)) {
|
||||
/* If this is an absolute path, read the data directly from it, and support AF_UNIX
|
||||
* sockets */
|
||||
|
||||
if (!path_is_valid(path)) /* safety check */
|
||||
return -EINVAL;
|
||||
|
||||
flags |= READ_FULL_FILE_CONNECT_SOCKET;
|
||||
|
||||
/* Pass some minimal info about the unit and the credential name we are looking to acquire
|
||||
@@ -2636,25 +2688,50 @@ static int load_credential(
|
||||
return -ENOMEM;
|
||||
|
||||
missing_ok = false;
|
||||
source = path;
|
||||
|
||||
} else if (params->received_credentials) {
|
||||
/* If this is a relative path, take it relative to the credentials we received
|
||||
* ourselves. We don't support the AF_UNIX stuff in this mode, since we are operating
|
||||
* on a credential store, i.e. this is guaranteed to be regular files. */
|
||||
j = path_join(params->received_credentials, path);
|
||||
if (!j)
|
||||
} else if (credential_name_valid(path)) {
|
||||
/* If this is a relative path, take it as credential name relative to the credentials
|
||||
* directory we received ourselves. We don't support the AF_UNIX stuff in this mode, since we
|
||||
* are operating on a credential store, i.e. this is guaranteed to be regular files. */
|
||||
|
||||
search_path = credential_search_path(params, encrypted);
|
||||
if (!search_path)
|
||||
return -ENOMEM;
|
||||
|
||||
source = j;
|
||||
missing_ok = true;
|
||||
} else
|
||||
source = NULL;
|
||||
|
||||
if (source)
|
||||
if (encrypted)
|
||||
flags |= READ_FULL_FILE_UNBASE64;
|
||||
|
||||
maxsz = encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX;
|
||||
|
||||
if (search_path) {
|
||||
STRV_FOREACH(d, search_path) {
|
||||
_cleanup_free_ char *j = NULL;
|
||||
|
||||
j = path_join(*d, path);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
r = read_full_file_full(
|
||||
AT_FDCWD, j, /* path is absolute, hence pass AT_FDCWD as nop dir fd here */
|
||||
UINT64_MAX,
|
||||
maxsz,
|
||||
flags,
|
||||
NULL,
|
||||
&data, &size);
|
||||
if (r != -ENOENT)
|
||||
break;
|
||||
}
|
||||
} else if (source)
|
||||
r = read_full_file_full(
|
||||
read_dfd, source,
|
||||
UINT64_MAX,
|
||||
encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX,
|
||||
flags | (encrypted ? READ_FULL_FILE_UNBASE64 : 0),
|
||||
maxsz,
|
||||
flags,
|
||||
bindname,
|
||||
&data, &size);
|
||||
else
|
||||
|
||||
+2
-1
@@ -406,7 +406,8 @@ struct ExecParameters {
|
||||
const char *cgroup_path;
|
||||
|
||||
char **prefix;
|
||||
const char *received_credentials;
|
||||
const char *received_credentials_directory;
|
||||
const char *received_encrypted_credentials_directory;
|
||||
|
||||
const char *confirm_spawn;
|
||||
|
||||
|
||||
@@ -0,0 +1,551 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "copy.h"
|
||||
#include "creds-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "import-creds.h"
|
||||
#include "io-util.h"
|
||||
#include "mkdir-label.h"
|
||||
#include "mount-util.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "recurse-dir.h"
|
||||
#include "strv.h"
|
||||
|
||||
/* This imports credentials passed in from environments higher up (VM manager, boot loader, …) and rearranges
|
||||
* them so that later code can access them using our regular credential protocol
|
||||
* (i.e. $CREDENTIALS_DIRECTORY). It's supposed to be minimal glue to unify behaviour how PID 1 (and
|
||||
* generators invoked by it) can acquire credentials from outside, to mimic how we support it for containers,
|
||||
* but on VM/physical environments.
|
||||
*
|
||||
* This does three things:
|
||||
*
|
||||
* 1. It imports credentials picked up by sd-boot (and placed in the /.extra/credentials/ dir in the initrd)
|
||||
* and puts them in /run/credentials/@encrypted/. Note that during the initrd→host transition the initrd root
|
||||
* file system is cleaned out, thus it is essential we pick up these files before they are deleted. Note
|
||||
* that these credentials originate from an untrusted source, i.e. the ESP and are not
|
||||
* pre-authenticated. They still have to be authenticated before use.
|
||||
*
|
||||
* 2. It imports credentials from /proc/cmdline and puts them in /run/credentials/@system/. These come from a
|
||||
* trusted environment (i.e. the boot loader), and are typically authenticated (if authentication is done
|
||||
* at all). However, they are world-readable, which might be less than ideal. Hence only use this for data
|
||||
* that doesn't require trust.
|
||||
*
|
||||
* 3. It imports credentials passed in through qemu's fw_cfg logic. Specifically, credential data passed in
|
||||
* /sys/firmware/qemu_fw_cfg/by_name/opt/io.systemd.credentials/ is picked up and also placed in
|
||||
* /run/credentials/@system/.
|
||||
*
|
||||
* If it picked up any credentials it will set the $CREDENTIALS_DIRECTORY and
|
||||
* $ENCRYPTED_CREDENTIALS_DIRECTORY environment variables to point to these directories, so that processes
|
||||
* can find them there later on. If "ramfs" is available $CREDENTIALS_DIRECTORY will be backed by it (but
|
||||
* $ENCRYPTED_CREDENTIALS_DIRECTORY is just a regular tmpfs).
|
||||
*
|
||||
* Net result: the service manager can pick up trusted credentials from $CREDENTIALS_DIRECTORY afterwards,
|
||||
* and untrusted ones from $ENCRYPTED_CREDENTIALS_DIRECTORY. */
|
||||
|
||||
typedef struct ImportCredentialContext {
|
||||
int target_dir_fd;
|
||||
size_t size_sum;
|
||||
unsigned n_credentials;
|
||||
} ImportCredentialContext;
|
||||
|
||||
static void import_credentials_context_free(ImportCredentialContext *c) {
|
||||
assert(c);
|
||||
|
||||
c->target_dir_fd = safe_close(c->target_dir_fd);
|
||||
}
|
||||
|
||||
static int acquire_encrypted_credential_directory(ImportCredentialContext *c) {
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
if (c->target_dir_fd >= 0)
|
||||
return c->target_dir_fd;
|
||||
|
||||
r = mkdir_safe_label(ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, 0700, 0, 0, MKDIR_WARN_MODE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create " ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY ": %m");
|
||||
|
||||
c->target_dir_fd = open(ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (c->target_dir_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open " ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY ": %m");
|
||||
|
||||
return c->target_dir_fd;
|
||||
}
|
||||
|
||||
static int open_credential_file_for_write(int target_dir_fd, const char *dir_name, const char *n) {
|
||||
int fd;
|
||||
|
||||
assert(target_dir_fd >= 0);
|
||||
assert(dir_name);
|
||||
assert(n);
|
||||
|
||||
fd = openat(target_dir_fd, n, O_WRONLY|O_CLOEXEC|O_CREAT|O_EXCL|O_NOFOLLOW, 0400);
|
||||
if (fd < 0) {
|
||||
if (errno == EEXIST) /* In case of EEXIST we'll only debug log! */
|
||||
return log_debug_errno(errno, "Credential '%s' set twice, ignoring.", n);
|
||||
|
||||
return log_error_errno(errno, "Failed to create %s/%s: %m", dir_name, n);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static bool credential_size_ok(ImportCredentialContext *c, const char *name, uint64_t size) {
|
||||
assert(c);
|
||||
assert(name);
|
||||
|
||||
if (size > CREDENTIAL_SIZE_MAX) {
|
||||
log_warning("Credential '%s' is larger than allowed limit (%s > %s), skipping.", name, FORMAT_BYTES(size), FORMAT_BYTES(CREDENTIAL_SIZE_MAX));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size > CREDENTIALS_TOTAL_SIZE_MAX - c->size_sum) {
|
||||
log_warning("Accumulated credential size would be above allowed limit (%s+%s > %s), skipping '%s'.",
|
||||
FORMAT_BYTES(c->size_sum), FORMAT_BYTES(size), FORMAT_BYTES(CREDENTIALS_TOTAL_SIZE_MAX), name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int finalize_credentials_dir(const char *dir, const char *envvar) {
|
||||
int r;
|
||||
|
||||
assert(dir);
|
||||
assert(envvar);
|
||||
|
||||
/* Try to make the credentials directory read-only now */
|
||||
|
||||
r = make_mount_point(dir);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to make '%s' a mount point, ignoring: %m", dir);
|
||||
else
|
||||
(void) mount_nofollow_verbose(LOG_WARNING, NULL, dir, NULL, MS_BIND|MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_RDONLY|MS_REMOUNT, NULL);
|
||||
|
||||
if (setenv(envvar, dir, /* overwrite= */ true) < 0)
|
||||
return log_error_errno(errno, "Failed to set $%s environment variable: %m", envvar);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int import_credentials_boot(void) {
|
||||
_cleanup_(import_credentials_context_free) ImportCredentialContext context = {
|
||||
.target_dir_fd = -1,
|
||||
};
|
||||
int r;
|
||||
|
||||
/* systemd-stub will wrap sidecar *.cred files from the UEFI kernel image directory into initrd
|
||||
* cpios, so that they unpack into /.extra/. We'll pick them up from there and copy them into /run/
|
||||
* so that we can access them during the entire runtime (note that the initrd file system is erased
|
||||
* during the initrd → host transition). Note that these credentials originate from an untrusted
|
||||
* source (i.e. the ESP typically) and thus need to be authenticated later. We thus put them in a
|
||||
* directory separate from the usual credentials which are from a trusted source. */
|
||||
|
||||
if (!in_initrd())
|
||||
return 0;
|
||||
|
||||
FOREACH_STRING(p,
|
||||
"/.extra/credentials/", /* specific to this boot menu */
|
||||
"/.extra/global_credentials/") { /* boot partition wide */
|
||||
|
||||
_cleanup_free_ DirectoryEntries *de = NULL;
|
||||
_cleanup_close_ int source_dir_fd = -1;
|
||||
|
||||
source_dir_fd = open(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (source_dir_fd < 0) {
|
||||
if (errno == ENOENT) {
|
||||
log_debug("No credentials passed via %s.", p);
|
||||
continue;
|
||||
}
|
||||
|
||||
log_warning_errno(errno, "Failed to open '%s', ignoring: %m", p);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = readdir_all(source_dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to read '%s' contents, ignoring: %m", p);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < de->n_entries; i++) {
|
||||
const struct dirent *d = de->entries[i];
|
||||
_cleanup_close_ int cfd = -1, nfd = -1;
|
||||
_cleanup_free_ char *n = NULL;
|
||||
const char *e;
|
||||
struct stat st;
|
||||
|
||||
e = endswith(d->d_name, ".cred");
|
||||
if (!e)
|
||||
continue;
|
||||
|
||||
/* drop .cred suffix (which we want in the ESP sidecar dir, but not for our internal
|
||||
* processing) */
|
||||
n = strndup(d->d_name, e - d->d_name);
|
||||
if (!n)
|
||||
return log_oom();
|
||||
|
||||
if (!credential_name_valid(n)) {
|
||||
log_warning("Credential '%s' has invalid name, ignoring.", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
cfd = openat(source_dir_fd, d->d_name, O_RDONLY|O_CLOEXEC);
|
||||
if (cfd < 0) {
|
||||
log_warning_errno(errno, "Failed to open %s, ignoring: %m", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fstat(cfd, &st) < 0) {
|
||||
log_warning_errno(errno, "Failed to stat %s, ignoring: %m", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = stat_verify_regular(&st);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Credential file %s is not a regular file, ignoring: %m", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!credential_size_ok(&context, n, st.st_size))
|
||||
continue;
|
||||
|
||||
r = acquire_encrypted_credential_directory(&context);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
nfd = open_credential_file_for_write(context.target_dir_fd, ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, n);
|
||||
if (nfd == -EEXIST)
|
||||
continue;
|
||||
if (nfd < 0)
|
||||
return r;
|
||||
|
||||
r = copy_bytes(cfd, nfd, st.st_size, 0);
|
||||
if (r < 0) {
|
||||
(void) unlinkat(context.target_dir_fd, n, 0);
|
||||
return log_error_errno(r, "Failed to create credential '%s': %m", n);
|
||||
}
|
||||
|
||||
context.size_sum += st.st_size;
|
||||
context.n_credentials++;
|
||||
|
||||
log_debug("Successfully copied boot credential '%s'.", n);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.n_credentials > 0) {
|
||||
log_debug("Imported %u credentials from boot loader.", context.n_credentials);
|
||||
|
||||
r = finalize_credentials_dir(ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, "ENCRYPTED_CREDENTIALS_DIRECTORY");
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acquire_credential_directory(ImportCredentialContext *c) {
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
if (c->target_dir_fd >= 0)
|
||||
return c->target_dir_fd;
|
||||
|
||||
r = path_is_mount_point(SYSTEM_CREDENTIALS_DIRECTORY, NULL, 0);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to determine if " SYSTEM_CREDENTIALS_DIRECTORY " is a mount point: %m");
|
||||
|
||||
r = mkdir_safe_label(SYSTEM_CREDENTIALS_DIRECTORY, 0700, 0, 0, MKDIR_WARN_MODE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create " SYSTEM_CREDENTIALS_DIRECTORY " mount point: %m");
|
||||
|
||||
r = 0; /* Now it exists and is not a mount point */
|
||||
}
|
||||
if (r == 0)
|
||||
/* If not a mountpoint yet, try to mount a ramfs there (so that this stuff isn't swapped
|
||||
* out), but if that doesn't work, let's just use the regular tmpfs it already is. */
|
||||
(void) mount_nofollow_verbose(LOG_WARNING, "ramfs", SYSTEM_CREDENTIALS_DIRECTORY, "ramfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, "mode=0700");
|
||||
|
||||
c->target_dir_fd = open(SYSTEM_CREDENTIALS_DIRECTORY, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (c->target_dir_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open " SYSTEM_CREDENTIALS_DIRECTORY ": %m");
|
||||
|
||||
return c->target_dir_fd;
|
||||
}
|
||||
|
||||
static int proc_cmdline_callback(const char *key, const char *value, void *data) {
|
||||
ImportCredentialContext *c = ASSERT_PTR(data);
|
||||
_cleanup_free_ char *n = NULL;
|
||||
_cleanup_close_ int nfd = -1;
|
||||
const char *colon;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
|
||||
if (!proc_cmdline_key_streq(key, "systemd.set_credential"))
|
||||
return 0;
|
||||
|
||||
colon = value ? strchr(value, ':') : NULL;
|
||||
if (!colon) {
|
||||
log_warning("Credential assignment through kernel command line lacks ':' character, ignoring: %s", value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = strndup(value, colon - value);
|
||||
if (!n)
|
||||
return log_oom();
|
||||
|
||||
if (!credential_name_valid(n)) {
|
||||
log_warning("Credential name '%s' is invalid, ignoring.", n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
colon++;
|
||||
l = strlen(colon);
|
||||
|
||||
if (!credential_size_ok(c, n, l))
|
||||
return 0;
|
||||
|
||||
r = acquire_credential_directory(c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
nfd = open_credential_file_for_write(c->target_dir_fd, SYSTEM_CREDENTIALS_DIRECTORY, n);
|
||||
if (nfd == -EEXIST)
|
||||
return 0;
|
||||
if (nfd < 0)
|
||||
return r;
|
||||
|
||||
r = loop_write(nfd, colon, l, /* do_poll= */ false);
|
||||
if (r < 0) {
|
||||
(void) unlinkat(c->target_dir_fd, n, 0);
|
||||
return log_error_errno(r, "Failed to write credential: %m");
|
||||
}
|
||||
|
||||
c->size_sum += l;
|
||||
c->n_credentials++;
|
||||
|
||||
log_debug("Successfully processed kernel command line credential '%s'.", n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int import_credentials_proc_cmdline(ImportCredentialContext *c) {
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
r = proc_cmdline_parse(proc_cmdline_callback, c, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse /proc/cmdline: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define QEMU_FWCFG_PATH "/sys/firmware/qemu_fw_cfg/by_name/opt/io.systemd.credentials"
|
||||
|
||||
static int import_credentials_qemu(ImportCredentialContext *c) {
|
||||
_cleanup_free_ DirectoryEntries *de = NULL;
|
||||
_cleanup_close_ int source_dir_fd = -1;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
source_dir_fd = open(QEMU_FWCFG_PATH, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (source_dir_fd < 0) {
|
||||
if (errno == ENOENT) {
|
||||
log_debug("No credentials passed via fw_cfg.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_warning_errno(errno, "Failed to open '" QEMU_FWCFG_PATH "', ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = readdir_all(source_dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to read '" QEMU_FWCFG_PATH "' contents, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < de->n_entries; i++) {
|
||||
const struct dirent *d = de->entries[i];
|
||||
_cleanup_close_ int vfd = -1, rfd = -1, nfd = -1;
|
||||
_cleanup_free_ char *szs = NULL;
|
||||
uint64_t sz;
|
||||
|
||||
if (!credential_name_valid(d->d_name)) {
|
||||
log_warning("Credential '%s' has invalid name, ignoring.", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
vfd = openat(source_dir_fd, d->d_name, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (vfd < 0) {
|
||||
log_warning_errno(errno, "Failed to open '" QEMU_FWCFG_PATH "'/%s/, ignoring: %m", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = read_virtual_file_at(vfd, "size", LINE_MAX, &szs, NULL);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to read '" QEMU_FWCFG_PATH "'/%s/size, ignoring: %m", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = safe_atou64(strstrip(szs), &sz);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to parse size of credential '%s', ignoring: %s", d->d_name, szs);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!credential_size_ok(c, d->d_name, sz))
|
||||
continue;
|
||||
|
||||
/* Ideally we'd just symlink the data here. Alas the kernel driver exports the raw file as
|
||||
* having size zero, and we'd rather not have applications support such credential
|
||||
* files. Let's hence copy the files to make them regular. */
|
||||
|
||||
rfd = openat(vfd, "raw", O_RDONLY|O_CLOEXEC);
|
||||
if (rfd < 0) {
|
||||
log_warning_errno(r, "Failed to open '" QEMU_FWCFG_PATH "'/%s/raw, ignoring: %m", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = acquire_credential_directory(c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
nfd = open_credential_file_for_write(c->target_dir_fd, SYSTEM_CREDENTIALS_DIRECTORY, d->d_name);
|
||||
if (nfd == -EEXIST)
|
||||
continue;
|
||||
if (nfd < 0)
|
||||
return r;
|
||||
|
||||
r = copy_bytes(rfd, nfd, sz, 0);
|
||||
if (r < 0) {
|
||||
(void) unlinkat(c->target_dir_fd, d->d_name, 0);
|
||||
return log_error_errno(r, "Failed to create credential '%s': %m", d->d_name);
|
||||
}
|
||||
|
||||
c->size_sum += sz;
|
||||
c->n_credentials++;
|
||||
|
||||
log_debug("Successfully copied qemu fw_cfg credential '%s'.", d->d_name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int import_credentials_trusted(void) {
|
||||
_cleanup_(import_credentials_context_free) ImportCredentialContext c = {
|
||||
.target_dir_fd = -1,
|
||||
};
|
||||
int q, r;
|
||||
|
||||
r = import_credentials_qemu(&c);
|
||||
q = import_credentials_proc_cmdline(&c);
|
||||
|
||||
if (c.n_credentials > 0) {
|
||||
int z;
|
||||
|
||||
log_debug("Imported %u credentials from kernel command line/fw_cfg.", c.n_credentials);
|
||||
|
||||
z = finalize_credentials_dir(SYSTEM_CREDENTIALS_DIRECTORY, "CREDENTIALS_DIRECTORY");
|
||||
if (z < 0)
|
||||
return z;
|
||||
}
|
||||
|
||||
return r < 0 ? r : q;
|
||||
}
|
||||
|
||||
static int symlink_credential_dir(const char *envvar, const char *path, const char *where) {
|
||||
int r;
|
||||
|
||||
assert(envvar);
|
||||
assert(path);
|
||||
assert(where);
|
||||
|
||||
if (!path_is_valid(path) || !path_is_absolute(path))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "String specified via $%s is not a valid absolute path, refusing: %s", envvar, path);
|
||||
|
||||
/* If the env var already points to where we intend to create the symlink, then most likely we
|
||||
* already imported some creds earlier, and thus set the env var, and hence don't need to do
|
||||
* anything. */
|
||||
if (path_equal(path, where))
|
||||
return 0;
|
||||
|
||||
r = symlink_idempotent(path, where, /* make_relative= */ true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to link $%s to %s: %m", envvar, where);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int import_credentials(void) {
|
||||
const char *received_creds_dir = NULL, *received_encrypted_creds_dir = NULL;
|
||||
bool envvar_set = false;
|
||||
int r, q;
|
||||
|
||||
r = get_credentials_dir(&received_creds_dir);
|
||||
if (r < 0 && r != -ENXIO) /* ENXIO → env var not set yet */
|
||||
log_warning_errno(r, "Failed to determine credentials directory, ignoring: %m");
|
||||
|
||||
envvar_set = r >= 0;
|
||||
|
||||
r = get_encrypted_credentials_dir(&received_encrypted_creds_dir);
|
||||
if (r < 0 && r != -ENXIO) /* ENXIO → env var not set yet */
|
||||
log_warning_errno(r, "Failed to determine encrypted credentials directory, ignoring: %m");
|
||||
|
||||
envvar_set = envvar_set || r >= 0;
|
||||
|
||||
if (envvar_set) {
|
||||
/* Maybe an earlier stage initrd already set this up? If so, don't try to import anything again. */
|
||||
log_debug("Not importing credentials, $CREDENTIALS_DIRECTORY or $ENCRYPTED_CREDENTIALS_DIRECTORY already set.");
|
||||
|
||||
/* But, let's make sure the creds are available from our regular paths. */
|
||||
if (received_creds_dir)
|
||||
r = symlink_credential_dir("CREDENTIALS_DIRECTORY", received_creds_dir, SYSTEM_CREDENTIALS_DIRECTORY);
|
||||
else
|
||||
r = 0;
|
||||
|
||||
if (received_encrypted_creds_dir) {
|
||||
q = symlink_credential_dir("ENCRYPTED_CREDENTIALS_DIRECTORY", received_encrypted_creds_dir, ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY);
|
||||
if (r >= 0)
|
||||
r = q;
|
||||
}
|
||||
|
||||
} else {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
|
||||
r = proc_cmdline_get_key("systemd.import_credentials", PROC_CMDLINE_STRIP_RD_PREFIX, &v);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to check if 'systemd.import_credentials=' kernel command line option is set, ignoring: %m");
|
||||
else if (r > 0) {
|
||||
r = parse_boolean(v);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse 'systemd.import_credentials=' parameter, ignoring: %m");
|
||||
else if (r == 0) {
|
||||
log_notice("systemd.import_credentials=no is set, skipping importing of credentials.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
r = import_credentials_boot();
|
||||
|
||||
q = import_credentials_trusted();
|
||||
if (r >= 0)
|
||||
r = q;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int import_credentials(void);
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "macro.h"
|
||||
#include "recurse-dir.h"
|
||||
#include "string-util.h"
|
||||
#include "virt.h"
|
||||
|
||||
#if HAVE_KMOD
|
||||
#include "module-util.h"
|
||||
@@ -80,6 +81,10 @@ static bool has_virtio_rng(void) {
|
||||
|
||||
return r > 0;
|
||||
}
|
||||
|
||||
static bool in_qemu(void) {
|
||||
return IN_SET(detect_vm(), VIRTUALIZATION_KVM, VIRTUALIZATION_QEMU);
|
||||
}
|
||||
#endif
|
||||
|
||||
int kmod_setup(void) {
|
||||
@@ -109,6 +114,9 @@ int kmod_setup(void) {
|
||||
#endif
|
||||
/* virtio_rng would be loaded by udev later, but real entropy might be needed very early */
|
||||
{ "virtio_rng", NULL, false, false, has_virtio_rng },
|
||||
|
||||
/* qemu_fw_cfg would be loaded by udev later, but we want to import credentials from it super early */
|
||||
{ "qemu_fw_cfg", "/sys/firmware/qemu_fw_cfg", false, false, in_qemu },
|
||||
};
|
||||
_cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
|
||||
unsigned i;
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#include "hexdecoct.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "ima-setup.h"
|
||||
#include "import-creds.h"
|
||||
#include "killall.h"
|
||||
#include "kmod-setup.h"
|
||||
#include "limits-util.h"
|
||||
@@ -2180,6 +2181,10 @@ static int initialize_runtime(
|
||||
(void) bump_rlimit_nofile(saved_rlimit_nofile);
|
||||
(void) bump_rlimit_memlock(saved_rlimit_memlock);
|
||||
|
||||
/* Pull credentials from various sources into a common credential directory */
|
||||
if (arg_system && !skip_setup)
|
||||
(void) import_credentials();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+34
-8
@@ -777,9 +777,37 @@ static int manager_setup_sigchld_event_source(Manager *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_find_credentials_dirs(Manager *m) {
|
||||
const char *e;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
r = get_credentials_dir(&e);
|
||||
if (r < 0) {
|
||||
if (r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to determine credentials directory, ignoring: %m");
|
||||
} else {
|
||||
m->received_credentials_directory = strdup(e);
|
||||
if (!m->received_credentials_directory)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = get_encrypted_credentials_dir(&e);
|
||||
if (r < 0) {
|
||||
if (r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to determine encrypted credentials directory, ignoring: %m");
|
||||
} else {
|
||||
m->received_encrypted_credentials_directory = strdup(e);
|
||||
if (!m->received_encrypted_credentials_directory)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager **_m) {
|
||||
_cleanup_(manager_freep) Manager *m = NULL;
|
||||
const char *e;
|
||||
int r;
|
||||
|
||||
assert(_m);
|
||||
@@ -883,12 +911,9 @@ int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager *
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = get_credentials_dir(&e);
|
||||
if (r >= 0) {
|
||||
m->received_credentials = strdup(e);
|
||||
if (!m->received_credentials)
|
||||
return -ENOMEM;
|
||||
}
|
||||
r = manager_find_credentials_dirs(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_default(&m->event);
|
||||
if (r < 0)
|
||||
@@ -1533,7 +1558,8 @@ Manager* manager_free(Manager *m) {
|
||||
|
||||
for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++)
|
||||
m->prefix[dt] = mfree(m->prefix[dt]);
|
||||
free(m->received_credentials);
|
||||
free(m->received_credentials_directory);
|
||||
free(m->received_encrypted_credentials_directory);
|
||||
|
||||
free(m->watchdog_pretimeout_governor);
|
||||
free(m->watchdog_pretimeout_governor_overridden);
|
||||
|
||||
+2
-1
@@ -438,7 +438,8 @@ struct Manager {
|
||||
|
||||
/* Prefixes of e.g. RuntimeDirectory= */
|
||||
char *prefix[_EXEC_DIRECTORY_TYPE_MAX];
|
||||
char *received_credentials;
|
||||
char *received_credentials_directory;
|
||||
char *received_encrypted_credentials_directory;
|
||||
|
||||
/* Used in the SIGCHLD and sd_notify() message invocation logic to avoid that we dispatch the same event
|
||||
* multiple times on the same unit. */
|
||||
|
||||
@@ -73,6 +73,8 @@ libcore_sources = '''
|
||||
generator-setup.h
|
||||
ima-setup.c
|
||||
ima-setup.h
|
||||
import-creds.c
|
||||
import-creds.h
|
||||
job.c
|
||||
job.h
|
||||
kill.c
|
||||
|
||||
+2
-1
@@ -5032,7 +5032,8 @@ int unit_set_exec_params(Unit *u, ExecParameters *p) {
|
||||
p->cgroup_path = u->cgroup_path;
|
||||
SET_FLAG(p->flags, EXEC_CGROUP_DELEGATE, unit_cgroup_delegate(u));
|
||||
|
||||
p->received_credentials = u->manager->received_credentials;
|
||||
p->received_credentials_directory = u->manager->received_credentials_directory;
|
||||
p->received_encrypted_credentials_directory = u->manager->received_encrypted_credentials_directory;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+137
-52
@@ -60,63 +60,76 @@ static const char* transcode_mode_table[_TRANSCODE_MAX] = {
|
||||
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(transcode_mode, TranscodeMode);
|
||||
|
||||
static int open_credential_directory(DIR **ret) {
|
||||
_cleanup_free_ char *j = NULL;
|
||||
static int open_credential_directory(
|
||||
DIR **ret_dir,
|
||||
const char **ret_prefix,
|
||||
bool encrypted) {
|
||||
|
||||
const char *p;
|
||||
DIR *d;
|
||||
int r;
|
||||
|
||||
if (arg_system) {
|
||||
_cleanup_free_ char *cd = NULL;
|
||||
assert(ret_dir);
|
||||
|
||||
r = getenv_for_pid(1, "CREDENTIALS_DIRECTORY", &cd);
|
||||
if (arg_system)
|
||||
/* PID 1 ensures that system credentials are always accessible under the same fixed path. It
|
||||
* will create symlinks if necessary to guarantee that. */
|
||||
p = encrypted ?
|
||||
ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY :
|
||||
SYSTEM_CREDENTIALS_DIRECTORY;
|
||||
else {
|
||||
/* Otherwise take the dirs from the env vars we got passed */
|
||||
r = (encrypted ? get_encrypted_credentials_dir : get_credentials_dir)(&p);
|
||||
if (r == -ENXIO) /* No environment variable? */
|
||||
goto not_found;
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!cd)
|
||||
return -ENXIO;
|
||||
|
||||
if (!path_is_absolute(cd) || !path_is_normalized(cd))
|
||||
return -EINVAL;
|
||||
|
||||
j = path_join("/proc/1/root", cd);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
p = j;
|
||||
} else {
|
||||
r = get_credentials_dir(&p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_error_errno(r, "Failed to get credentials directory: %m");
|
||||
}
|
||||
|
||||
d = opendir(p);
|
||||
if (!d)
|
||||
return -errno;
|
||||
if (!d) {
|
||||
/* No such dir? Then no creds where passed. (We conditionalize this on arg_system, since for
|
||||
* the per-service case a non-existing path would indicate an issue since the env var would
|
||||
* be set incorrectly in that case.) */
|
||||
if (arg_system && errno == ENOENT)
|
||||
goto not_found;
|
||||
|
||||
return log_error_errno(errno, "Failed to open credentials directory '%s': %m", p);
|
||||
}
|
||||
|
||||
*ret_dir = d;
|
||||
|
||||
if (ret_prefix)
|
||||
*ret_prefix = p;
|
||||
|
||||
return 1;
|
||||
|
||||
not_found:
|
||||
*ret_dir = NULL;
|
||||
|
||||
if (ret_prefix)
|
||||
*ret_prefix = NULL;
|
||||
|
||||
*ret = d;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_list(int argc, char **argv, void *userdata) {
|
||||
_cleanup_(table_unrefp) Table *t = NULL;
|
||||
static int add_credentials_to_table(Table *t, bool encrypted) {
|
||||
_cleanup_(closedirp) DIR *d = NULL;
|
||||
const char *prefix;
|
||||
int r;
|
||||
|
||||
r = open_credential_directory(&d);
|
||||
if (r == -ENXIO)
|
||||
return log_error_errno(r, "No credentials received. (i.e. $CREDENTIALS_DIRECTORY not set or pointing to empty directory.)");
|
||||
assert(t);
|
||||
|
||||
r = open_credential_directory(&d, &prefix, encrypted);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open credentials directory: %m");
|
||||
|
||||
t = table_new("name", "secure", "size");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
(void) table_set_align_percent(t, table_get_cell(t, 0, 2), 100);
|
||||
return r;
|
||||
if (!d)
|
||||
return 0; /* No creds dir set */
|
||||
|
||||
for (;;) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
_cleanup_free_ char *j = NULL;
|
||||
const char *secure, *secure_color = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
struct dirent *de;
|
||||
struct stat st;
|
||||
|
||||
@@ -149,7 +162,10 @@ static int verb_list(int argc, char **argv, void *userdata) {
|
||||
if (!S_ISREG(st.st_mode))
|
||||
continue;
|
||||
|
||||
if ((st.st_mode & 0377) != 0) {
|
||||
if (encrypted) {
|
||||
secure = "encrypted";
|
||||
secure_color = ansi_highlight_green();
|
||||
} else if ((st.st_mode & 0377) != 0) {
|
||||
secure = "insecure"; /* Anything that is accessible more than read-only to its owner is insecure */
|
||||
secure_color = ansi_highlight_red();
|
||||
} else {
|
||||
@@ -161,16 +177,49 @@ static int verb_list(int argc, char **argv, void *userdata) {
|
||||
secure_color = r ? ansi_highlight_green() : ansi_highlight_yellow4();
|
||||
}
|
||||
|
||||
j = path_join(prefix, de->d_name);
|
||||
if (!j)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(
|
||||
t,
|
||||
TABLE_STRING, de->d_name,
|
||||
TABLE_STRING, secure,
|
||||
TABLE_SET_COLOR, secure_color,
|
||||
TABLE_SIZE, (uint64_t) st.st_size);
|
||||
TABLE_SIZE, (uint64_t) st.st_size,
|
||||
TABLE_STRING, j);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
return 1; /* Creds dir set */
|
||||
}
|
||||
|
||||
static int verb_list(int argc, char **argv, void *userdata) {
|
||||
_cleanup_(table_unrefp) Table *t = NULL;
|
||||
int r, q;
|
||||
|
||||
t = table_new("name", "secure", "size", "path");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
(void) table_set_align_percent(t, table_get_cell(t, 0, 2), 100);
|
||||
|
||||
r = add_credentials_to_table(t, /* encrypted= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
q = add_credentials_to_table(t, /* encrypted= */ false);
|
||||
if (q < 0)
|
||||
return q;
|
||||
|
||||
if (r == 0 && q == 0) {
|
||||
if (arg_system)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "No credentials passed to system.");
|
||||
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "No credentials passed. (i.e. $CREDENTIALS_DIRECTORY not set.)");
|
||||
}
|
||||
|
||||
if ((arg_json_format_flags & JSON_FORMAT_OFF) && table_get_rows(t) <= 1) {
|
||||
log_info("No credentials");
|
||||
return 0;
|
||||
@@ -310,18 +359,15 @@ static int write_blob(FILE *f, const void *data, size_t size) {
|
||||
}
|
||||
|
||||
static int verb_cat(int argc, char **argv, void *userdata) {
|
||||
_cleanup_(closedirp) DIR *d = NULL;
|
||||
usec_t timestamp;
|
||||
int r, ret = 0;
|
||||
|
||||
r = open_credential_directory(&d);
|
||||
if (r == -ENXIO)
|
||||
return log_error_errno(r, "No credentials passed.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open credentials directory: %m");
|
||||
timestamp = arg_timestamp != USEC_INFINITY ? arg_timestamp : now(CLOCK_REALTIME);
|
||||
|
||||
STRV_FOREACH(cn, strv_skip(argv, 1)) {
|
||||
_cleanup_(erase_and_freep) void *data = NULL;
|
||||
size_t size = 0;
|
||||
int encrypted;
|
||||
|
||||
if (!credential_name_valid(*cn)) {
|
||||
log_error("Credential name '%s' is not valid.", *cn);
|
||||
@@ -330,19 +376,58 @@ static int verb_cat(int argc, char **argv, void *userdata) {
|
||||
continue;
|
||||
}
|
||||
|
||||
r = read_full_file_full(
|
||||
dirfd(d), *cn,
|
||||
UINT64_MAX, SIZE_MAX,
|
||||
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE,
|
||||
NULL,
|
||||
(char**) &data, &size);
|
||||
if (r < 0) {
|
||||
/* Look both in regular and in encrypted credentials */
|
||||
for (encrypted = 0; encrypted < 2; encrypted ++) {
|
||||
_cleanup_(closedirp) DIR *d = NULL;
|
||||
|
||||
r = open_credential_directory(&d, NULL, encrypted);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open credentials directory: %m");
|
||||
if (!d) /* Not set */
|
||||
continue;
|
||||
|
||||
r = read_full_file_full(
|
||||
dirfd(d), *cn,
|
||||
UINT64_MAX, SIZE_MAX,
|
||||
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE,
|
||||
NULL,
|
||||
(char**) &data, &size);
|
||||
if (r == -ENOENT) /* Not found */
|
||||
continue;
|
||||
if (r >= 0) /* Found */
|
||||
break;
|
||||
|
||||
log_error_errno(r, "Failed to read credential '%s': %m", *cn);
|
||||
if (ret >= 0)
|
||||
ret = r;
|
||||
}
|
||||
|
||||
if (encrypted >= 2) { /* Found nowhere */
|
||||
log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Credential '%s' not set.", *cn);
|
||||
if (ret >= 0)
|
||||
ret = -ENOENT;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (encrypted) {
|
||||
_cleanup_(erase_and_freep) void *plaintext = NULL;
|
||||
size_t plaintext_size;
|
||||
|
||||
r = decrypt_credential_and_warn(
|
||||
*cn,
|
||||
timestamp,
|
||||
arg_tpm2_device,
|
||||
data, size,
|
||||
&plaintext, &plaintext_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
erase_and_free(data);
|
||||
data = TAKE_PTR(plaintext);
|
||||
size = plaintext_size;
|
||||
}
|
||||
|
||||
r = write_blob(stdout, data, size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
+10
-2
@@ -33,12 +33,12 @@ bool credential_name_valid(const char *s) {
|
||||
return filename_is_valid(s) && fdname_is_valid(s);
|
||||
}
|
||||
|
||||
int get_credentials_dir(const char **ret) {
|
||||
static int get_credentials_dir_internal(const char *envvar, const char **ret) {
|
||||
const char *e;
|
||||
|
||||
assert(ret);
|
||||
|
||||
e = secure_getenv("CREDENTIALS_DIRECTORY");
|
||||
e = secure_getenv(envvar);
|
||||
if (!e)
|
||||
return -ENXIO;
|
||||
|
||||
@@ -49,6 +49,14 @@ int get_credentials_dir(const char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_credentials_dir(const char **ret) {
|
||||
return get_credentials_dir_internal("CREDENTIALS_DIRECTORY", ret);
|
||||
}
|
||||
|
||||
int get_encrypted_credentials_dir(const char **ret) {
|
||||
return get_credentials_dir_internal("ENCRYPTED_CREDENTIALS_DIRECTORY", ret);
|
||||
}
|
||||
|
||||
int read_credential(const char *name, void **ret, size_t *ret_size) {
|
||||
_cleanup_free_ char *fn = NULL;
|
||||
const char *d;
|
||||
|
||||
@@ -26,7 +26,13 @@
|
||||
|
||||
bool credential_name_valid(const char *s);
|
||||
|
||||
/* Where creds have been passed to the local execution context */
|
||||
int get_credentials_dir(const char **ret);
|
||||
int get_encrypted_credentials_dir(const char **ret);
|
||||
|
||||
/* Where creds have been passed to the system */
|
||||
#define SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@system"
|
||||
#define ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@encrypted"
|
||||
|
||||
int read_credential(const char *name, void **ret, size_t *ret_size);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user