mirror of
https://github.com/systemd/systemd
synced 2026-04-03 05:34:50 +02:00
Compare commits
20 Commits
98ca65c36a
...
a2366debce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2366debce | ||
|
|
19db9a99e6 | ||
|
|
a1cb4fae86 | ||
|
|
f379784550 | ||
|
|
31b4dea5f0 | ||
|
|
036100d745 | ||
|
|
1d73c596fa | ||
|
|
52efca4723 | ||
|
|
ebd222b1c8 | ||
|
|
423a8ffccc | ||
|
|
cedc98c581 | ||
|
|
2a64b56da4 | ||
|
|
4175cd4f40 | ||
|
|
bc48ac39c4 | ||
|
|
44f23805b0 | ||
|
|
d796c6b7c6 | ||
|
|
457a39a866 | ||
|
|
e4c7e4c27a | ||
|
|
c7b5f7f30f | ||
|
|
4338440c97 |
@ -76,6 +76,7 @@
|
|||||||
<constant>SD_PATH_SEARCH_CONFIGURATION_FACTORY</constant>,
|
<constant>SD_PATH_SEARCH_CONFIGURATION_FACTORY</constant>,
|
||||||
<constant>SD_PATH_SEARCH_STATE_FACTORY</constant>,
|
<constant>SD_PATH_SEARCH_STATE_FACTORY</constant>,
|
||||||
<constant>SD_PATH_SEARCH_CONFIGURATION</constant>,
|
<constant>SD_PATH_SEARCH_CONFIGURATION</constant>,
|
||||||
|
<constant>SD_PATH_SEARCH_SYSCTL</constant>,
|
||||||
|
|
||||||
<constant>SD_PATH_SYSTEMD_UTIL</constant>,
|
<constant>SD_PATH_SYSTEMD_UTIL</constant>,
|
||||||
<constant>SD_PATH_SYSTEMD_SYSTEM_UNIT</constant>,
|
<constant>SD_PATH_SYSTEMD_SYSTEM_UNIT</constant>,
|
||||||
|
|||||||
@ -95,6 +95,16 @@
|
|||||||
Desktop Environments</ulink>.</para>
|
Desktop Environments</ulink>.</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Signal</title>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><constant>SIGHUP</constant></term>
|
||||||
|
<listitem><para>Reloads the service configuration file.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title>See Also</title>
|
<title>See Also</title>
|
||||||
<para><simplelist type="inline">
|
<para><simplelist type="inline">
|
||||||
|
|||||||
@ -377,10 +377,11 @@ SecureBootMode efi_get_secure_boot_mode(void) {
|
|||||||
int audit = read_flag(EFI_GLOBAL_VARIABLE_STR("AuditMode"));
|
int audit = read_flag(EFI_GLOBAL_VARIABLE_STR("AuditMode"));
|
||||||
int deployed = read_flag(EFI_GLOBAL_VARIABLE_STR("DeployedMode"));
|
int deployed = read_flag(EFI_GLOBAL_VARIABLE_STR("DeployedMode"));
|
||||||
int setup = read_flag(EFI_GLOBAL_VARIABLE_STR("SetupMode"));
|
int setup = read_flag(EFI_GLOBAL_VARIABLE_STR("SetupMode"));
|
||||||
log_debug("Secure boot variables: SecureBoot=%d AuditMode=%d DeployedMode=%d SetupMode=%d",
|
int moksb = read_flag(EFI_SHIMLOCK_VARIABLE_STR("MokSBStateRT"));
|
||||||
secure, audit, deployed, setup);
|
log_debug("Secure boot variables: SecureBoot=%d AuditMode=%d DeployedMode=%d SetupMode=%d MokSBStateRT=%d",
|
||||||
|
secure, audit, deployed, setup, moksb);
|
||||||
|
|
||||||
return (cache = decode_secure_boot_mode(secure, audit > 0, deployed > 0, setup > 0));
|
return (cache = decode_secure_boot_mode(secure, audit > 0, deployed > 0, setup > 0, moksb > 0));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,8 @@
|
|||||||
#define EFI_VENDOR_DATABASE_STR SD_ID128_MAKE_UUID_STR(d7,19,b2,cb,3d,3a,45,96,a3,bc,da,d0,0e,67,65,6f)
|
#define EFI_VENDOR_DATABASE_STR SD_ID128_MAKE_UUID_STR(d7,19,b2,cb,3d,3a,45,96,a3,bc,da,d0,0e,67,65,6f)
|
||||||
#define EFI_VENDOR_SYSTEMD SD_ID128_MAKE(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
|
#define EFI_VENDOR_SYSTEMD SD_ID128_MAKE(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
|
||||||
#define EFI_VENDOR_SYSTEMD_STR SD_ID128_MAKE_UUID_STR(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
|
#define EFI_VENDOR_SYSTEMD_STR SD_ID128_MAKE_UUID_STR(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
|
||||||
|
#define EFI_VENDOR_SHIMLOCK SD_ID128_MAKE(60,5d,ab,50,e0,46,43,00,ab,b6,3d,d8,10,dd,8b,23)
|
||||||
|
#define EFI_VENDOR_SHIMLOCK_STR SD_ID128_MAKE_UUID_STR(60,5d,ab,50,e0,46,43,00,ab,b6,3d,d8,10,dd,8b,23)
|
||||||
|
|
||||||
#define EFI_VARIABLE_NON_VOLATILE UINT32_C(0x00000001)
|
#define EFI_VARIABLE_NON_VOLATILE UINT32_C(0x00000001)
|
||||||
#define EFI_VARIABLE_BOOTSERVICE_ACCESS UINT32_C(0x00000002)
|
#define EFI_VARIABLE_BOOTSERVICE_ACCESS UINT32_C(0x00000002)
|
||||||
@ -30,6 +32,7 @@
|
|||||||
#define EFI_GLOBAL_VARIABLE_STR(name) EFI_VENDOR_VARIABLE_STR(EFI_VENDOR_GLOBAL_STR, name)
|
#define EFI_GLOBAL_VARIABLE_STR(name) EFI_VENDOR_VARIABLE_STR(EFI_VENDOR_GLOBAL_STR, name)
|
||||||
#define EFI_LOADER_VARIABLE_STR(name) EFI_VENDOR_VARIABLE_STR(EFI_VENDOR_LOADER_STR, name)
|
#define EFI_LOADER_VARIABLE_STR(name) EFI_VENDOR_VARIABLE_STR(EFI_VENDOR_LOADER_STR, name)
|
||||||
#define EFI_SYSTEMD_VARIABLE_STR(name) EFI_VENDOR_VARIABLE_STR(EFI_VENDOR_SYSTEMD_STR, name)
|
#define EFI_SYSTEMD_VARIABLE_STR(name) EFI_VENDOR_VARIABLE_STR(EFI_VENDOR_SYSTEMD_STR, name)
|
||||||
|
#define EFI_SHIMLOCK_VARIABLE_STR(name) EFI_VENDOR_VARIABLE_STR(EFI_VENDOR_SHIMLOCK_STR, name)
|
||||||
|
|
||||||
#define EFIVAR_PATH(variable) "/sys/firmware/efi/efivars/" variable
|
#define EFIVAR_PATH(variable) "/sys/firmware/efi/efivars/" variable
|
||||||
#define EFIVAR_CACHE_PATH(variable) "/run/systemd/efivars/" variable
|
#define EFIVAR_CACHE_PATH(variable) "/run/systemd/efivars/" variable
|
||||||
|
|||||||
@ -20,7 +20,7 @@ bool secure_boot_enabled(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SecureBootMode secure_boot_mode(void) {
|
SecureBootMode secure_boot_mode(void) {
|
||||||
bool secure, audit = false, deployed = false, setup = false;
|
bool secure, audit = false, deployed = false, setup = false, moksb = false;
|
||||||
EFI_STATUS err;
|
EFI_STATUS err;
|
||||||
|
|
||||||
err = efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"SecureBoot", &secure);
|
err = efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"SecureBoot", &secure);
|
||||||
@ -32,8 +32,9 @@ SecureBootMode secure_boot_mode(void) {
|
|||||||
(void) efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"AuditMode", &audit);
|
(void) efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"AuditMode", &audit);
|
||||||
(void) efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"DeployedMode", &deployed);
|
(void) efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"DeployedMode", &deployed);
|
||||||
(void) efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"SetupMode", &setup);
|
(void) efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"SetupMode", &setup);
|
||||||
|
(void) efivar_get_boolean_u8(MAKE_GUID_PTR(SHIM_LOCK), u"MokSBStateRT", &moksb);
|
||||||
|
|
||||||
return decode_secure_boot_mode(secure, audit, deployed, setup);
|
return decode_secure_boot_mode(secure, audit, deployed, setup, moksb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -30,9 +30,6 @@ struct ShimLock {
|
|||||||
EFI_STATUS __sysv_abi__ (*read_header) (void *data, uint32_t datasize, void *context);
|
EFI_STATUS __sysv_abi__ (*read_header) (void *data, uint32_t datasize, void *context);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SHIM_LOCK_GUID \
|
|
||||||
{ 0x605dab50, 0xe046, 0x4300, { 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } }
|
|
||||||
|
|
||||||
#define SHIM_IMAGE_LOADER_GUID \
|
#define SHIM_IMAGE_LOADER_GUID \
|
||||||
{ 0x1f492041, 0xfadb, 0x4e59, { 0x9e, 0x57, 0x7c, 0xaf, 0xe7, 0x3a, 0x55, 0xab } }
|
{ 0x1f492041, 0xfadb, 0x4e59, { 0x9e, 0x57, 0x7c, 0xaf, 0xe7, 0x3a, 0x55, 0xab } }
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,6 @@
|
|||||||
#include "siphash24.h"
|
#include "siphash24.h"
|
||||||
#include "stat-util.h"
|
#include "stat-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "tmpfile-util.h"
|
|
||||||
#include "user-util.h"
|
#include "user-util.h"
|
||||||
|
|
||||||
ExecSetCredential* exec_set_credential_free(ExecSetCredential *sc) {
|
ExecSetCredential* exec_set_credential_free(ExecSetCredential *sc) {
|
||||||
@ -320,7 +319,6 @@ static int write_credential(
|
|||||||
gid_t gid,
|
gid_t gid,
|
||||||
bool ownership_ok) {
|
bool ownership_ok) {
|
||||||
|
|
||||||
_cleanup_free_ char *tmp = NULL;
|
|
||||||
_cleanup_close_ int fd = -EBADF;
|
_cleanup_close_ int fd = -EBADF;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -328,16 +326,10 @@ static int write_credential(
|
|||||||
assert(id);
|
assert(id);
|
||||||
assert(data || size == 0);
|
assert(data || size == 0);
|
||||||
|
|
||||||
r = tempfn_random_child("", "cred", &tmp);
|
fd = openat(dfd, id, O_CREAT|O_EXCL|O_WRONLY|O_CLOEXEC, 0600);
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
fd = openat(dfd, tmp, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL|O_NOFOLLOW|O_NOCTTY, 0600);
|
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
CLEANUP_TMPFILE_AT(dfd, tmp);
|
|
||||||
|
|
||||||
r = loop_write(fd, data, size);
|
r = loop_write(fd, data, size);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -359,11 +351,6 @@ static int write_credential(
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = RET_NERRNO(renameat(dfd, tmp, dfd, id));
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
tmp = mfree(tmp); /* disarm CLEANUP_TMPFILE_AT() */
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,36 +413,20 @@ static int credential_search_path(const ExecParameters *params, CredentialSearch
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool device_nodes_restricted(
|
|
||||||
const ExecContext *c,
|
|
||||||
const CGroupContext *cgroup_context) {
|
|
||||||
|
|
||||||
assert(c);
|
|
||||||
assert(cgroup_context);
|
|
||||||
|
|
||||||
/* Returns true if we have any reason to believe we might not be able to access the TPM device
|
|
||||||
* directly, even if we run as root/PID 1. This could be because /dev/ is replaced by a private
|
|
||||||
* version, or because a device node access list is configured. */
|
|
||||||
|
|
||||||
if (c->private_devices)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (cgroup_context_has_device_policy(cgroup_context))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct load_cred_args {
|
struct load_cred_args {
|
||||||
const ExecContext *context;
|
const ExecContext *context;
|
||||||
const CGroupContext *cgroup_context;
|
|
||||||
const ExecParameters *params;
|
const ExecParameters *params;
|
||||||
const char *unit;
|
const char *unit;
|
||||||
|
|
||||||
|
bool always_ipc;
|
||||||
|
|
||||||
bool encrypted;
|
bool encrypted;
|
||||||
|
|
||||||
int write_dfd;
|
int write_dfd;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
gid_t gid;
|
gid_t gid;
|
||||||
bool ownership_ok;
|
bool ownership_ok;
|
||||||
|
|
||||||
uint64_t left;
|
uint64_t left;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -486,7 +457,7 @@ static int maybe_decrypt_and_write_credential(
|
|||||||
|
|
||||||
flags |= CREDENTIAL_ANY_SCOPE;
|
flags |= CREDENTIAL_ANY_SCOPE;
|
||||||
|
|
||||||
if (!device_nodes_restricted(args->context, args->cgroup_context)) {
|
if (!args->always_ipc) {
|
||||||
r = decrypt_credential_and_warn(
|
r = decrypt_credential_and_warn(
|
||||||
id,
|
id,
|
||||||
now(CLOCK_REALTIME),
|
now(CLOCK_REALTIME),
|
||||||
@ -787,38 +758,49 @@ static int load_cred_recurse_dir_cb(
|
|||||||
return RECURSE_DIR_CONTINUE;
|
return RECURSE_DIR_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool device_nodes_restricted(
|
||||||
|
const ExecContext *c,
|
||||||
|
const CGroupContext *cgroup_context) {
|
||||||
|
|
||||||
|
assert(c);
|
||||||
|
assert(cgroup_context);
|
||||||
|
|
||||||
|
/* Returns true if we have any reason to believe we might not be able to access the TPM device
|
||||||
|
* directly, even if we run as root/PID 1. This could be because /dev/ is replaced by a private
|
||||||
|
* version, or because a device node access list is configured. */
|
||||||
|
|
||||||
|
if (c->private_devices)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (cgroup_context_has_device_policy(cgroup_context))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static int acquire_credentials(
|
static int acquire_credentials(
|
||||||
const ExecContext *context,
|
const ExecContext *context,
|
||||||
const CGroupContext *cgroup_context,
|
const CGroupContext *cgroup_context,
|
||||||
const ExecParameters *params,
|
const ExecParameters *params,
|
||||||
const char *unit,
|
const char *unit,
|
||||||
const char *p,
|
int dfd,
|
||||||
uid_t uid,
|
uid_t uid,
|
||||||
gid_t gid,
|
gid_t gid,
|
||||||
bool ownership_ok) {
|
bool ownership_ok) {
|
||||||
|
|
||||||
_cleanup_close_ int dfd = -EBADF;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(context);
|
assert(context);
|
||||||
assert(cgroup_context);
|
assert(cgroup_context);
|
||||||
assert(params);
|
assert(params);
|
||||||
assert(unit);
|
assert(unit);
|
||||||
assert(p);
|
assert(dfd >= 0);
|
||||||
|
|
||||||
dfd = open(p, O_DIRECTORY|O_CLOEXEC);
|
|
||||||
if (dfd < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
r = fd_acl_make_writable(dfd); /* Add the "w" bit, if we are reusing an already set up credentials dir where it was unset */
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
struct load_cred_args args = {
|
struct load_cred_args args = {
|
||||||
.context = context,
|
.context = context,
|
||||||
.cgroup_context = cgroup_context,
|
|
||||||
.params = params,
|
.params = params,
|
||||||
.unit = unit,
|
.unit = unit,
|
||||||
|
.always_ipc = device_nodes_restricted(context, cgroup_context),
|
||||||
.write_dfd = dfd,
|
.write_dfd = dfd,
|
||||||
.uid = uid,
|
.uid = uid,
|
||||||
.gid = gid,
|
.gid = gid,
|
||||||
@ -919,7 +901,15 @@ static int acquire_credentials(
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = fd_acl_make_read_only(dfd); /* Now take away the "w" bit */
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int credentials_dir_finalize_permissions(int dfd, uid_t uid, gid_t gid, bool ownership_ok) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(dfd >= 0);
|
||||||
|
|
||||||
|
r = fd_acl_make_read_only(dfd); /* Take away the "w" bit */
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -943,43 +933,90 @@ static int acquire_credentials(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int setup_credentials_plain_dir(
|
||||||
|
const ExecContext *context,
|
||||||
|
const CGroupContext *cgroup_context,
|
||||||
|
const ExecParameters *params,
|
||||||
|
const char *unit,
|
||||||
|
const char *cred_dir,
|
||||||
|
uid_t uid,
|
||||||
|
gid_t gid) {
|
||||||
|
|
||||||
|
_cleanup_free_ char *t = NULL, *workspace = NULL;
|
||||||
|
_cleanup_(rm_rf_safep) const char *workspace_rm = NULL;
|
||||||
|
_cleanup_close_ int dfd = -EBADF;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(context);
|
||||||
|
assert(params);
|
||||||
|
assert(unit);
|
||||||
|
assert(cred_dir);
|
||||||
|
|
||||||
|
/* Temporary workspace, that remains inaccessible all the time. We prepare stuff there before moving
|
||||||
|
* it into place, so that users can't access half-initialized credential stores. */
|
||||||
|
t = path_join(params->prefix[EXEC_DIRECTORY_RUNTIME], "systemd/temporary-credentials");
|
||||||
|
if (!t)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = mkdir_label(t, 0700);
|
||||||
|
if (r < 0 && r != -EEXIST)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
workspace = path_join(t, unit);
|
||||||
|
if (!workspace)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dfd = open_mkdir(workspace, O_CLOEXEC|O_EXCL, 0700);
|
||||||
|
if (dfd < 0)
|
||||||
|
return log_debug_errno(dfd, "Failed to create workspace for credentials: %m");
|
||||||
|
workspace_rm = workspace;
|
||||||
|
|
||||||
|
(void) label_fix_full(dfd, /* inode_path = */ NULL, cred_dir, /* flags = */ 0);
|
||||||
|
|
||||||
|
r = acquire_credentials(context, cgroup_context, params, unit, dfd, uid, gid, /* ownership_ok = */ false);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = RET_NERRNO(rename(workspace, cred_dir));
|
||||||
|
if (r >= 0)
|
||||||
|
workspace_rm = NULL;
|
||||||
|
if (r == -EEXIST) {
|
||||||
|
log_debug_errno(r, "Credential dir '%s' already populated, exchanging with workspace.", cred_dir);
|
||||||
|
r = RET_NERRNO(renameat2(AT_FDCWD, workspace, AT_FDCWD, cred_dir, RENAME_EXCHANGE));
|
||||||
|
}
|
||||||
|
if (r < 0)
|
||||||
|
return log_debug_errno(r, "Failed to move credentials workspace into place: %m");
|
||||||
|
|
||||||
|
/* rename() requires both the source and target to be writable, hence lock down write permission
|
||||||
|
* as last step. */
|
||||||
|
r = credentials_dir_finalize_permissions(dfd, uid, gid, /* ownership_ok = */ false);
|
||||||
|
if (r < 0)
|
||||||
|
return log_debug_errno(r, "Failed to adjust ACLs of credentials dir: %m");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int setup_credentials_internal(
|
static int setup_credentials_internal(
|
||||||
const ExecContext *context,
|
const ExecContext *context,
|
||||||
const CGroupContext *cgroup_context,
|
const CGroupContext *cgroup_context,
|
||||||
const ExecParameters *params,
|
const ExecParameters *params,
|
||||||
const char *unit,
|
const char *unit,
|
||||||
const char *final, /* This is where the credential store shall eventually end up at */
|
const char *cred_dir,
|
||||||
const char *workspace, /* This is where we can prepare it before moving it to the final place */
|
|
||||||
bool reuse_workspace, /* Whether to reuse any existing workspace mount if it already is a mount */
|
|
||||||
bool must_mount, /* Whether to require that we mount something, it's not OK to use the plain directory fall back */
|
|
||||||
uid_t uid,
|
uid_t uid,
|
||||||
gid_t gid) {
|
gid_t gid) {
|
||||||
|
|
||||||
bool final_mounted;
|
_cleanup_close_ int fs_fd = -EBADF, mfd = -EBADF, dfd = -EBADF;
|
||||||
int r, workspace_mounted; /* negative if we don't know yet whether we have/can mount something; true
|
bool dir_mounted;
|
||||||
* if we mounted something; false if we definitely can't mount anything */
|
int r;
|
||||||
|
|
||||||
assert(context);
|
assert(context);
|
||||||
assert(params);
|
assert(params);
|
||||||
assert(unit);
|
assert(unit);
|
||||||
assert(final);
|
assert(cred_dir);
|
||||||
assert(workspace);
|
|
||||||
|
|
||||||
r = path_is_mount_point(final);
|
if (!FLAGS_SET(params->flags, EXEC_SETUP_CREDENTIALS_FRESH)) {
|
||||||
if (r < 0)
|
/* We may reuse the previous credential dir */
|
||||||
return log_debug_errno(r, "Failed to determine if '%s' is a mountpoint: %m", final);
|
r = dir_is_empty(cred_dir, /* ignore_hidden_or_backup = */ false);
|
||||||
final_mounted = r > 0;
|
|
||||||
|
|
||||||
if (final_mounted) {
|
|
||||||
if (FLAGS_SET(params->flags, EXEC_SETUP_CREDENTIALS_FRESH)) {
|
|
||||||
r = umount_verbose(LOG_DEBUG, final, MNT_DETACH|UMOUNT_NOFOLLOW);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
final_mounted = false;
|
|
||||||
} else {
|
|
||||||
/* We can reuse the previous credential dir */
|
|
||||||
r = dir_is_empty(final, /* ignore_hidden_or_backup = */ false);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
@ -987,113 +1024,68 @@ static int setup_credentials_internal(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r = path_is_mount_point(cred_dir);
|
||||||
|
if (r < 0)
|
||||||
|
return log_debug_errno(r, "Failed to determine if '%s' is a mountpoint: %m", cred_dir);
|
||||||
|
dir_mounted = r > 0;
|
||||||
|
|
||||||
|
mfd = fsmount_credentials_fs(&fs_fd);
|
||||||
|
if (ERRNO_IS_NEG_PRIVILEGE(mfd) && !dir_mounted) {
|
||||||
|
log_debug_errno(mfd, "Lacking privilege to mount credentials fs, falling back to plain directory.");
|
||||||
|
return setup_credentials_plain_dir(context, cgroup_context, params, unit, cred_dir, uid, gid);
|
||||||
}
|
}
|
||||||
|
if (mfd < 0)
|
||||||
|
return log_debug_errno(mfd, "Failed to mount credentials fs: %m");
|
||||||
|
|
||||||
if (reuse_workspace) {
|
dfd = fd_reopen(mfd, O_DIRECTORY|O_CLOEXEC);
|
||||||
r = path_is_mount_point(workspace);
|
if (dfd < 0)
|
||||||
if (r < 0)
|
return dfd;
|
||||||
return r;
|
|
||||||
if (r > 0)
|
|
||||||
workspace_mounted = true; /* If this is already a mount, and we are supposed to reuse
|
|
||||||
* it, let's keep this in mind */
|
|
||||||
else
|
|
||||||
workspace_mounted = -1; /* We need to figure out if we can mount something to the workspace */
|
|
||||||
} else
|
|
||||||
workspace_mounted = -1; /* ditto */
|
|
||||||
|
|
||||||
/* If both the final place and the workspace are mounted, we have no mounts to set up, based on
|
(void) label_fix_full(dfd, /* inode_path = */ NULL, cred_dir, /* flags = */ 0);
|
||||||
* the assumption that they're actually the same tmpfs (but the latter with MS_RDONLY different).
|
|
||||||
* If the workspace is not mounted, we just bind the final place over and make it writable. */
|
|
||||||
must_mount = must_mount || final_mounted;
|
|
||||||
|
|
||||||
if (workspace_mounted < 0) {
|
r = acquire_credentials(context, cgroup_context, params, unit, dfd, uid, gid, /* ownership_ok = */ true);
|
||||||
if (!final_mounted)
|
|
||||||
/* Nothing is mounted on the workspace yet, let's try to mount a new tmpfs if
|
|
||||||
* not using the final place. */
|
|
||||||
r = mount_credentials_fs(workspace, CREDENTIALS_TOTAL_SIZE_MAX, /* ro= */ false);
|
|
||||||
if (final_mounted || r < 0) {
|
|
||||||
/* If using final place or failed to mount new tmpfs, make a bind mount from
|
|
||||||
* the final to the workspace, so that we can make it writable there. */
|
|
||||||
r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL);
|
|
||||||
if (r < 0) {
|
|
||||||
if (!ERRNO_IS_PRIVILEGE(r))
|
|
||||||
/* Propagate anything that isn't a permission problem. */
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (must_mount)
|
|
||||||
/* If it's not OK to use the plain directory fallback, propagate all
|
|
||||||
* errors too. */
|
|
||||||
return r;
|
|
||||||
|
|
||||||
/* If we lack privileges to bind mount stuff, then let's gracefully proceed
|
|
||||||
* for compat with container envs, and just use the final dir as is.
|
|
||||||
* Final place must not be mounted in this case (refused by must_mount
|
|
||||||
* above) */
|
|
||||||
|
|
||||||
workspace_mounted = false;
|
|
||||||
} else {
|
|
||||||
/* Make the new bind mount writable (i.e. drop MS_RDONLY) */
|
|
||||||
r = mount_nofollow_verbose(LOG_DEBUG,
|
|
||||||
NULL,
|
|
||||||
workspace,
|
|
||||||
NULL,
|
|
||||||
MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false),
|
|
||||||
NULL);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
workspace_mounted = true;
|
r = credentials_dir_finalize_permissions(dfd, uid, gid, /* ownership_ok = */ true);
|
||||||
}
|
|
||||||
} else
|
|
||||||
workspace_mounted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(workspace_mounted >= 0);
|
|
||||||
assert(!must_mount || workspace_mounted);
|
|
||||||
|
|
||||||
const char *where = workspace_mounted ? workspace : final;
|
|
||||||
|
|
||||||
(void) label_fix_full(AT_FDCWD, where, final, 0);
|
|
||||||
|
|
||||||
r = acquire_credentials(context, cgroup_context, params, unit, where, uid, gid, workspace_mounted);
|
|
||||||
if (r < 0) {
|
|
||||||
/* If we're using final place as workspace, and failed to acquire credentials, we might
|
|
||||||
* have left half-written creds there. Let's get rid of the whole mount, so future
|
|
||||||
* calls won't reuse it. */
|
|
||||||
if (final_mounted)
|
|
||||||
(void) umount_verbose(LOG_DEBUG, final, MNT_DETACH|UMOUNT_NOFOLLOW);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (workspace_mounted) {
|
|
||||||
if (!final_mounted) {
|
|
||||||
/* Make workspace read-only now, so that any bind mount we make from it defaults to
|
|
||||||
* read-only too */
|
|
||||||
r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ true), NULL);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return log_debug_errno(r, "Failed to adjust ACLs of credentials dir: %m");
|
||||||
|
|
||||||
/* And mount it to the final place, read-only */
|
// Work around a kernel bug that results in tmpfs reconfiguration failure.
|
||||||
r = mount_nofollow_verbose(LOG_DEBUG, workspace, final, NULL, MS_MOVE, NULL);
|
// FIXME: drop this once https://lore.kernel.org/linux-fsdevel/20251108190930.440685-1-me@yhndnzj.com/
|
||||||
} else
|
// is merged and hits the distro kernels.
|
||||||
/* Otherwise we just get rid of the bind mount of final place */
|
(void) fsconfig(fs_fd, FSCONFIG_SET_FLAG, "noswap", NULL, 0);
|
||||||
r = umount_verbose(LOG_DEBUG, workspace, MNT_DETACH|UMOUNT_NOFOLLOW);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
} else {
|
|
||||||
_cleanup_free_ char *parent = NULL;
|
|
||||||
|
|
||||||
/* If we do not have our own mount put used the plain directory fallback, then we need to
|
if (fsconfig(fs_fd, FSCONFIG_SET_FLAG, "ro", NULL, 0) < 0)
|
||||||
* open access to the top-level credential directory and the per-service directory now */
|
|
||||||
|
|
||||||
r = path_extract_directory(final, &parent);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
if (chmod(parent, 0755) < 0)
|
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
|
if (fsconfig(fs_fd, FSCONFIG_CMD_RECONFIGURE, NULL, NULL, 0) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
log_debug("Successfully reconfigured credentials fs to be read only.");
|
||||||
|
|
||||||
|
if (dir_mounted) {
|
||||||
|
/* Firstly, try to move beneath the existing mount, which guarantees strictly atomic replacement
|
||||||
|
* (needs kernel >= 6.5) */
|
||||||
|
r = move_mount(mfd, "", AT_FDCWD, cred_dir, MOVE_MOUNT_F_EMPTY_PATH|MOVE_MOUNT_BENEATH);
|
||||||
|
if (r >= 0)
|
||||||
|
return umount_verbose(LOG_DEBUG, cred_dir, MNT_DETACH|UMOUNT_NOFOLLOW);
|
||||||
|
if (errno != EINVAL)
|
||||||
|
return log_debug_errno(errno, "Failed to move credentials fs into place: %m");
|
||||||
|
|
||||||
|
log_debug_errno(errno, "Unable to move credentials fs beneath existing mount '%s', unmounting instead: %m",
|
||||||
|
cred_dir);
|
||||||
|
|
||||||
|
r = umount_verbose(LOG_DEBUG, cred_dir, MNT_DETACH|UMOUNT_NOFOLLOW);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r = move_mount(mfd, "", AT_FDCWD, cred_dir, MOVE_MOUNT_F_EMPTY_PATH);
|
||||||
|
if (r < 0)
|
||||||
|
return log_debug_errno(errno, "Failed to move credentials fs into place: %m");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1136,96 +1128,12 @@ int exec_setup_credentials(
|
|||||||
if (r < 0 && r != -EEXIST)
|
if (r < 0 && r != -EEXIST)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = safe_fork("(sd-mkdcreds)", FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_NEW_MOUNTNS, NULL);
|
r = setup_credentials_internal(context, cgroup_context, params, unit, p, uid, gid);
|
||||||
if (r < 0) {
|
|
||||||
_cleanup_(rmdir_and_freep) char *u = NULL; /* remove the temporary workspace if we can */
|
|
||||||
_cleanup_free_ char *t = NULL;
|
|
||||||
|
|
||||||
/* If this is not a privilege or support issue then propagate the error */
|
|
||||||
if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
|
|
||||||
return r;
|
|
||||||
|
|
||||||
/* Temporary workspace, that remains inaccessible all the time. We prepare stuff there before moving
|
|
||||||
* it into place, so that users can't access half-initialized credential stores. */
|
|
||||||
t = path_join(params->prefix[EXEC_DIRECTORY_RUNTIME], "systemd/temporary-credentials");
|
|
||||||
if (!t)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* We can't set up a mount namespace. In that case operate on a fixed, inaccessible per-unit
|
|
||||||
* directory outside of /run/credentials/ first, and then move it over to /run/credentials/
|
|
||||||
* after it is fully set up */
|
|
||||||
u = path_join(t, unit);
|
|
||||||
if (!u)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
FOREACH_STRING(i, t, u) {
|
|
||||||
r = mkdir_label(i, 0700);
|
|
||||||
if (r < 0 && r != -EEXIST)
|
|
||||||
return log_debug_errno(r, "Failed to make directory '%s': %m", i);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = setup_credentials_internal(
|
|
||||||
context,
|
|
||||||
cgroup_context,
|
|
||||||
params,
|
|
||||||
unit,
|
|
||||||
p, /* final mount point */
|
|
||||||
u, /* temporary workspace to overmount */
|
|
||||||
true, /* reuse the workspace if it is already a mount */
|
|
||||||
false, /* it's OK to fall back to a plain directory if we can't mount anything */
|
|
||||||
uid,
|
|
||||||
gid);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
} else if (r == 0) {
|
|
||||||
|
|
||||||
/* We managed to set up a mount namespace, and are now in a child. That's great. In this case
|
|
||||||
* we can use the same directory for all cases, after turning off propagation. Question
|
|
||||||
* though is: where do we turn off propagation exactly, and where do we place the workspace
|
|
||||||
* directory? We need some place that is guaranteed to be a mount point in the host, and
|
|
||||||
* which is guaranteed to have a subdir we can mount over. /run/ is not suitable for this,
|
|
||||||
* since we ultimately want to move the resulting file system there, i.e. we need propagation
|
|
||||||
* for /run/ eventually. We could use our own /run/systemd/bind mount on itself, but that
|
|
||||||
* would be visible in the host mount table all the time, which we want to avoid. Hence, what
|
|
||||||
* we do here instead we use /dev/ and /dev/shm/ for our purposes. We know for sure that
|
|
||||||
* /dev/ is a mount point and we now for sure that /dev/shm/ exists. Hence we can turn off
|
|
||||||
* propagation on the former, and then overmount the latter.
|
|
||||||
*
|
|
||||||
* Yes it's nasty playing games with /dev/ and /dev/shm/ like this, since it does not exist
|
|
||||||
* for this purpose, but there are few other candidates that work equally well for us, and
|
|
||||||
* given that we do this in a privately namespaced short-lived single-threaded process that
|
|
||||||
* no one else sees this should be OK to do. */
|
|
||||||
|
|
||||||
/* Turn off propagation from our namespace to host */
|
|
||||||
r = mount_nofollow_verbose(LOG_DEBUG, NULL, "/dev", NULL, MS_SLAVE|MS_REC, NULL);
|
|
||||||
if (r < 0)
|
|
||||||
goto child_fail;
|
|
||||||
|
|
||||||
r = setup_credentials_internal(
|
|
||||||
context,
|
|
||||||
cgroup_context,
|
|
||||||
params,
|
|
||||||
unit,
|
|
||||||
p, /* final mount point */
|
|
||||||
"/dev/shm", /* temporary workspace to overmount */
|
|
||||||
false, /* do not reuse /dev/shm if it is already a mount, under no circumstances */
|
|
||||||
true, /* insist that something is mounted, do not allow fallback to plain directory */
|
|
||||||
uid,
|
|
||||||
gid);
|
|
||||||
if (r < 0)
|
|
||||||
goto child_fail;
|
|
||||||
|
|
||||||
_exit(EXIT_SUCCESS);
|
|
||||||
|
|
||||||
child_fail:
|
|
||||||
_exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the credentials dir is empty and not a mount point, then there's no point in having it. Let's
|
/* If the credentials dir is empty and not a mount point, then there's no point in having it. Let's
|
||||||
* try to remove it. This matters in particular if we created the dir as mount point but then didn't
|
* try to remove it. This matters in particular if we created the dir as mount point but then didn't
|
||||||
* actually end up mounting anything on it. In that case we'd rather have ENOENT than EACCESS being
|
* actually end up mounting anything on it. In that case we'd rather have ENOENT than EACCESS being
|
||||||
* seen by users when trying access this inode. */
|
* seen by users when trying access this inode. */
|
||||||
(void) rmdir(p);
|
(void) rmdir(p);
|
||||||
return 0;
|
return r;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2134,7 +2134,7 @@ static int build_environment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!sd_id128_is_null(p->invocation_id)) {
|
if (!sd_id128_is_null(p->invocation_id)) {
|
||||||
assert(p->invocation_id_string);
|
assert(!isempty(p->invocation_id_string));
|
||||||
|
|
||||||
r = strv_extend_joined_with_size(&e, &n, "INVOCATION_ID=", p->invocation_id_string);
|
r = strv_extend_joined_with_size(&e, &n, "INVOCATION_ID=", p->invocation_id_string);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|||||||
@ -103,7 +103,7 @@ static int acquire_credential_directory(ImportCredentialsContext *c, const char
|
|||||||
(void) mount_nofollow_verbose(LOG_WARNING, NULL, path, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
|
(void) mount_nofollow_verbose(LOG_WARNING, NULL, path, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
|
||||||
else if (with_mount)
|
else if (with_mount)
|
||||||
/* If not a mount point yet, and the credentials are not encrypted, then let's try to mount a no-swap fs there */
|
/* If not a mount point yet, and the credentials are not encrypted, then let's try to mount a no-swap fs there */
|
||||||
(void) mount_credentials_fs(path, CREDENTIALS_TOTAL_SIZE_MAX, /* ro= */ false);
|
(void) mount_credentials_fs(path);
|
||||||
|
|
||||||
c->target_dir_fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
c->target_dir_fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||||
if (c->target_dir_fd < 0)
|
if (c->target_dir_fd < 0)
|
||||||
|
|||||||
@ -80,3 +80,6 @@ typedef struct {
|
|||||||
GUID_DEF(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72)
|
GUID_DEF(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72)
|
||||||
#define EFI_CERT_TYPE_PKCS7_GUID \
|
#define EFI_CERT_TYPE_PKCS7_GUID \
|
||||||
GUID_DEF(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7)
|
GUID_DEF(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7)
|
||||||
|
|
||||||
|
#define SHIM_LOCK_GUID \
|
||||||
|
GUID_DEF(0x605dab50, 0xe046, 0x4300 ,0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23)
|
||||||
|
|||||||
@ -10,13 +10,17 @@ static const sd_char * const table[_SECURE_BOOT_MAX] = {
|
|||||||
[SECURE_BOOT_DEPLOYED] = STR_C("deployed"),
|
[SECURE_BOOT_DEPLOYED] = STR_C("deployed"),
|
||||||
[SECURE_BOOT_SETUP] = STR_C("setup"),
|
[SECURE_BOOT_SETUP] = STR_C("setup"),
|
||||||
[SECURE_BOOT_USER] = STR_C("user"),
|
[SECURE_BOOT_USER] = STR_C("user"),
|
||||||
|
[SECURE_BOOT_TAINTED] = STR_C("tainted"),
|
||||||
};
|
};
|
||||||
|
|
||||||
const sd_char *secure_boot_mode_to_string(SecureBootMode m) {
|
const sd_char *secure_boot_mode_to_string(SecureBootMode m) {
|
||||||
return (m >= 0 && m < _SECURE_BOOT_MAX) ? table[m] : NULL;
|
return (m >= 0 && m < _SECURE_BOOT_MAX) ? table[m] : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SecureBootMode decode_secure_boot_mode(bool secure, bool audit, bool deployed, bool setup) {
|
SecureBootMode decode_secure_boot_mode(bool secure, bool audit, bool deployed, bool setup, bool moksb) {
|
||||||
|
/* shim verification can be disabled by moksb, some OEMs set it */
|
||||||
|
if (secure && moksb)
|
||||||
|
return SECURE_BOOT_TAINTED;
|
||||||
/* See figure 32-4 Secure Boot Modes from UEFI Specification 2.9 */
|
/* See figure 32-4 Secure Boot Modes from UEFI Specification 2.9 */
|
||||||
if (secure && deployed && !audit && !setup)
|
if (secure && deployed && !audit && !setup)
|
||||||
return SECURE_BOOT_DEPLOYED;
|
return SECURE_BOOT_DEPLOYED;
|
||||||
|
|||||||
@ -51,9 +51,10 @@ typedef enum SecureBootMode {
|
|||||||
SECURE_BOOT_DEPLOYED,
|
SECURE_BOOT_DEPLOYED,
|
||||||
SECURE_BOOT_SETUP,
|
SECURE_BOOT_SETUP,
|
||||||
SECURE_BOOT_USER,
|
SECURE_BOOT_USER,
|
||||||
|
SECURE_BOOT_TAINTED,
|
||||||
_SECURE_BOOT_MAX,
|
_SECURE_BOOT_MAX,
|
||||||
_SECURE_BOOT_INVALID = -EINVAL,
|
_SECURE_BOOT_INVALID = -EINVAL,
|
||||||
} SecureBootMode;
|
} SecureBootMode;
|
||||||
|
|
||||||
const sd_char *secure_boot_mode_to_string(SecureBootMode m);
|
const sd_char *secure_boot_mode_to_string(SecureBootMode m);
|
||||||
SecureBootMode decode_secure_boot_mode(bool secure, bool audit, bool deployed, bool setup);
|
SecureBootMode decode_secure_boot_mode(bool secure, bool audit, bool deployed, bool setup, bool moksb);
|
||||||
|
|||||||
@ -683,7 +683,11 @@ static int get_search(uint64_t type, char ***ret) {
|
|||||||
|
|
||||||
*ret = TAKE_PTR(l);
|
*ret = TAKE_PTR(l);
|
||||||
return 0;
|
return 0;
|
||||||
}}
|
}
|
||||||
|
|
||||||
|
case SD_PATH_SEARCH_SYSCTL:
|
||||||
|
return strv_from_nulstr(ret, CONF_PATHS_NULSTR("sysctl.d"));
|
||||||
|
}
|
||||||
|
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -66,6 +66,7 @@ static const char* const path_table[_SD_PATH_MAX] = {
|
|||||||
[SD_PATH_SEARCH_CONFIGURATION_FACTORY] = "search-configuration-factory",
|
[SD_PATH_SEARCH_CONFIGURATION_FACTORY] = "search-configuration-factory",
|
||||||
[SD_PATH_SEARCH_STATE_FACTORY] = "search-state-factory",
|
[SD_PATH_SEARCH_STATE_FACTORY] = "search-state-factory",
|
||||||
[SD_PATH_SEARCH_CONFIGURATION] = "search-configuration",
|
[SD_PATH_SEARCH_CONFIGURATION] = "search-configuration",
|
||||||
|
[SD_PATH_SEARCH_SYSCTL] = "search-sysctl",
|
||||||
|
|
||||||
[SD_PATH_SYSTEMD_UTIL] = "systemd-util",
|
[SD_PATH_SYSTEMD_UTIL] = "systemd-util",
|
||||||
|
|
||||||
|
|||||||
@ -682,16 +682,12 @@ int fd_acl_make_read_only(int fd) {
|
|||||||
|
|
||||||
r = dlopen_libacl();
|
r = dlopen_libacl();
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
goto maybe_fallback;
|
||||||
|
|
||||||
acl = sym_acl_get_fd(fd);
|
acl = sym_acl_get_fd(fd);
|
||||||
if (!acl) {
|
if (!acl) {
|
||||||
|
r = -errno;
|
||||||
if (!ERRNO_IS_NOT_SUPPORTED(errno))
|
goto maybe_fallback;
|
||||||
return -errno;
|
|
||||||
|
|
||||||
/* No ACLs? Then just update the regular mode_t */
|
|
||||||
return fd_acl_make_read_only_fallback(fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (r = sym_acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
|
for (r = sym_acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
|
||||||
@ -729,75 +725,18 @@ int fd_acl_make_read_only(int fd) {
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (sym_acl_set_fd(fd, acl) < 0) {
|
if (sym_acl_set_fd(fd, acl) < 0) {
|
||||||
if (!ERRNO_IS_NOT_SUPPORTED(errno))
|
r = -errno;
|
||||||
return -errno;
|
goto maybe_fallback;
|
||||||
|
|
||||||
return fd_acl_make_read_only_fallback(fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
|
|
||||||
int fd_acl_make_writable(int fd) {
|
maybe_fallback:
|
||||||
_cleanup_(acl_freep) acl_t acl = NULL;
|
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||||
acl_entry_t i;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
/* Safely adds the writable bit to the owner's ACL entry of this inode. (And only the owner's! – This
|
|
||||||
* not the obvious inverse of fd_acl_make_read_only() hence!) */
|
|
||||||
|
|
||||||
r = dlopen_libacl();
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
acl = sym_acl_get_fd(fd);
|
|
||||||
if (!acl) {
|
|
||||||
if (!ERRNO_IS_NOT_SUPPORTED(errno))
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
/* No ACLs? Then just update the regular mode_t */
|
/* No ACLs? Then just update the regular mode_t */
|
||||||
return fd_acl_make_writable_fallback(fd);
|
return fd_acl_make_read_only_fallback(fd);
|
||||||
}
|
|
||||||
|
|
||||||
for (r = sym_acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
|
|
||||||
r > 0;
|
|
||||||
r = sym_acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
|
|
||||||
acl_permset_t permset;
|
|
||||||
acl_tag_t tag;
|
|
||||||
int b;
|
|
||||||
|
|
||||||
if (sym_acl_get_tag_type(i, &tag) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
if (tag != ACL_USER_OBJ)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (sym_acl_get_permset(i, &permset) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
b = sym_acl_get_perm(permset, ACL_WRITE);
|
|
||||||
if (b < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
if (b)
|
|
||||||
return 0; /* Already set? Then there's nothing to do. */
|
|
||||||
|
|
||||||
if (sym_acl_add_perm(permset, ACL_WRITE) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (r < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
if (sym_acl_set_fd(fd, acl) < 0) {
|
|
||||||
if (!ERRNO_IS_NOT_SUPPORTED(errno))
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
return fd_acl_make_writable_fallback(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -818,23 +757,6 @@ int fd_acl_make_read_only_fallback(int fd) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fd_acl_make_writable_fallback(int fd) {
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
assert(fd >= 0);
|
|
||||||
|
|
||||||
if (fstat(fd, &st) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
if ((st.st_mode & 0200) != 0) /* already set */
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (fchmod(fd, (st.st_mode & 07777) | 0200) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int inode_type_can_acl(mode_t mode) {
|
int inode_type_can_acl(mode_t mode) {
|
||||||
return IN_SET(mode & S_IFMT, S_IFSOCK, S_IFREG, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO);
|
return IN_SET(mode & S_IFMT, S_IFSOCK, S_IFREG, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
#include "shared-forward.h"
|
#include "shared-forward.h"
|
||||||
|
|
||||||
int fd_acl_make_read_only_fallback(int fd);
|
int fd_acl_make_read_only_fallback(int fd);
|
||||||
int fd_acl_make_writable_fallback(int fd);
|
|
||||||
|
|
||||||
#if HAVE_ACL
|
#if HAVE_ACL
|
||||||
#include <acl/libacl.h> /* IWYU pragma: export */
|
#include <acl/libacl.h> /* IWYU pragma: export */
|
||||||
@ -56,7 +55,6 @@ int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *ret);
|
|||||||
int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask);
|
int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask);
|
||||||
|
|
||||||
int fd_acl_make_read_only(int fd);
|
int fd_acl_make_read_only(int fd);
|
||||||
int fd_acl_make_writable(int fd);
|
|
||||||
|
|
||||||
/* acl_free() takes multiple argument types. Multiple cleanup functions are necessary. */
|
/* acl_free() takes multiple argument types. Multiple cleanup functions are necessary. */
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(acl_t, sym_acl_free, acl_freep, NULL);
|
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(acl_t, sym_acl_free, acl_freep, NULL);
|
||||||
@ -89,10 +87,6 @@ static inline int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask) {
|
|||||||
static inline int fd_acl_make_read_only(int fd) {
|
static inline int fd_acl_make_read_only(int fd) {
|
||||||
return fd_acl_make_read_only_fallback(fd);
|
return fd_acl_make_read_only_fallback(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int fd_acl_make_writable(int fd) {
|
|
||||||
return fd_acl_make_writable_fallback(fd);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int inode_type_can_acl(mode_t mode);
|
int inode_type_can_acl(mode_t mode);
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
#define CREDENTIAL_NAME_MAX FDNAME_MAX
|
#define CREDENTIAL_NAME_MAX FDNAME_MAX
|
||||||
|
|
||||||
/* Put a size limit on the individual credential */
|
/* Put a size limit on the individual credential */
|
||||||
#define CREDENTIAL_SIZE_MAX (1024U*1024U)
|
#define CREDENTIAL_SIZE_MAX (1U * U64_MB)
|
||||||
|
|
||||||
/* Refuse to store more than 1M per service, after all this is unswappable memory. Note that for now we put
|
/* Refuse to store more than 1M per service, after all this is unswappable memory. Note that for now we put
|
||||||
* this to the same limit as the per-credential limit, i.e. if the user has n > 1 credentials instead of 1 it
|
* this to the same limit as the per-credential limit, i.e. if the user has n > 1 credentials instead of 1 it
|
||||||
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
/* Put a size limit on encrypted credentials (which is the same as the unencrypted size plus a spacious 128K of extra
|
/* Put a size limit on encrypted credentials (which is the same as the unencrypted size plus a spacious 128K of extra
|
||||||
* space for headers, IVs, exported TPM2 key material and so on. */
|
* space for headers, IVs, exported TPM2 key material and so on. */
|
||||||
#define CREDENTIAL_ENCRYPTED_SIZE_MAX (CREDENTIAL_SIZE_MAX + 128U*1024U)
|
#define CREDENTIAL_ENCRYPTED_SIZE_MAX (CREDENTIAL_SIZE_MAX + 128U * U64_KB)
|
||||||
|
|
||||||
bool credential_name_valid(const char *s);
|
bool credential_name_valid(const char *s);
|
||||||
bool credential_glob_valid(const char *s);
|
bool credential_glob_valid(const char *s);
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "chase.h"
|
#include "chase.h"
|
||||||
|
#include "creds-util.h"
|
||||||
#include "dissect-image.h"
|
#include "dissect-image.h"
|
||||||
#include "errno-util.h"
|
#include "errno-util.h"
|
||||||
#include "extract-word.h"
|
#include "extract-word.h"
|
||||||
@ -1831,58 +1832,65 @@ unsigned long credentials_fs_mount_flags(bool ro) {
|
|||||||
return MS_NODEV|MS_NOEXEC|MS_NOSUID|ms_nosymfollow_supported()|(ro ? MS_RDONLY : 0);
|
return MS_NODEV|MS_NOEXEC|MS_NOSUID|ms_nosymfollow_supported()|(ro ? MS_RDONLY : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int mount_credentials_fs(const char *path, size_t size, bool ro) {
|
int fsmount_credentials_fs(int *ret_fsfd) {
|
||||||
_cleanup_free_ char *opts = NULL;
|
_cleanup_close_ int fs_fd = -EBADF;
|
||||||
int r, noswap_supported;
|
char size_str[DECIMAL_STR_MAX(uint64_t)];
|
||||||
|
|
||||||
/* Mounts a file system we can place credentials in, i.e. with tight access modes right from the
|
/* Mounts a file system we can place credentials in, i.e. with tight access modes right from the
|
||||||
* beginning, and ideally swapping turned off. In order of preference:
|
* beginning, and ideally swapping turned off. In order of preference:
|
||||||
*
|
*
|
||||||
* 1. tmpfs if it supports "noswap"
|
* 1. tmpfs if it supports "noswap" (needs kernel >= 6.3)
|
||||||
* 2. ramfs
|
* 2. ramfs
|
||||||
* 3. tmpfs if it doesn't support "noswap"
|
* 3. tmpfs without "noswap"
|
||||||
*/
|
*/
|
||||||
|
|
||||||
noswap_supported = mount_option_supported("tmpfs", "noswap", NULL); /* Check explicitly to avoid kmsg noise */
|
fs_fd = fsopen("tmpfs", FSOPEN_CLOEXEC);
|
||||||
if (noswap_supported > 0) {
|
if (fs_fd < 0)
|
||||||
_cleanup_free_ char *noswap_opts = NULL;
|
return -errno;
|
||||||
|
|
||||||
if (asprintf(&noswap_opts, "mode=0700,nr_inodes=1024,size=%zu,noswap", size) < 0)
|
if (fsconfig(fs_fd, FSCONFIG_SET_STRING, "nr_inodes", "1024", 0) < 0)
|
||||||
return -ENOMEM;
|
return -errno;
|
||||||
|
|
||||||
/* Best case: tmpfs with noswap (needs kernel >= 6.3) */
|
xsprintf(size_str, "%" PRIu64, CREDENTIALS_TOTAL_SIZE_MAX);
|
||||||
|
if (fsconfig(fs_fd, FSCONFIG_SET_STRING, "size", size_str, 0) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
r = mount_nofollow_verbose(
|
if (fsconfig(fs_fd, FSCONFIG_SET_FLAG, "noswap", NULL, 0) < 0) {
|
||||||
LOG_DEBUG,
|
if (errno != EINVAL)
|
||||||
"tmpfs",
|
return -errno;
|
||||||
path,
|
|
||||||
"tmpfs",
|
int ramfs_fd = fsopen("ramfs", FSOPEN_CLOEXEC);
|
||||||
credentials_fs_mount_flags(ro),
|
if (ramfs_fd >= 0)
|
||||||
noswap_opts);
|
close_and_replace(fs_fd, ramfs_fd);
|
||||||
if (r >= 0)
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r = mount_nofollow_verbose(
|
if (fsconfig(fs_fd, FSCONFIG_SET_STRING, "mode", "0700", 0) < 0)
|
||||||
LOG_DEBUG,
|
return -errno;
|
||||||
"ramfs",
|
|
||||||
path,
|
|
||||||
"ramfs",
|
|
||||||
credentials_fs_mount_flags(ro),
|
|
||||||
"mode=0700");
|
|
||||||
if (r >= 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (asprintf(&opts, "mode=0700,nr_inodes=1024,size=%zu", size) < 0)
|
if (fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0)
|
||||||
return -ENOMEM;
|
return -errno;
|
||||||
|
|
||||||
return mount_nofollow_verbose(
|
int mfd = fsmount(fs_fd, FSMOUNT_CLOEXEC,
|
||||||
LOG_DEBUG,
|
ms_flags_to_mount_attr(credentials_fs_mount_flags(/* ro = */ false)));
|
||||||
"tmpfs",
|
if (mfd < 0)
|
||||||
path,
|
return -errno;
|
||||||
"tmpfs",
|
|
||||||
credentials_fs_mount_flags(ro),
|
if (ret_fsfd)
|
||||||
opts);
|
*ret_fsfd = TAKE_FD(fs_fd);
|
||||||
|
|
||||||
|
return mfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mount_credentials_fs(const char *path) {
|
||||||
|
_cleanup_close_ int mfd = -EBADF;
|
||||||
|
|
||||||
|
assert(path);
|
||||||
|
|
||||||
|
mfd = fsmount_credentials_fs(/* ret_fsfd = */ NULL);
|
||||||
|
if (mfd < 0)
|
||||||
|
return mfd;
|
||||||
|
|
||||||
|
return RET_NERRNO(move_mount(mfd, "", AT_FDCWD, path, MOVE_MOUNT_F_EMPTY_PATH));
|
||||||
}
|
}
|
||||||
|
|
||||||
int make_fsmount(
|
int make_fsmount(
|
||||||
|
|||||||
@ -159,7 +159,8 @@ int make_mount_point_inode_from_path(const char *source, const char *dest, mode_
|
|||||||
int trigger_automount_at(int dir_fd, const char *path);
|
int trigger_automount_at(int dir_fd, const char *path);
|
||||||
|
|
||||||
unsigned long credentials_fs_mount_flags(bool ro);
|
unsigned long credentials_fs_mount_flags(bool ro);
|
||||||
int mount_credentials_fs(const char *path, size_t size, bool ro);
|
int fsmount_credentials_fs(int *ret_fsfd);
|
||||||
|
int mount_credentials_fs(const char *path);
|
||||||
|
|
||||||
int make_fsmount(int error_log_level, const char *what, const char *type, unsigned long flags, const char *options, int userns_fd);
|
int make_fsmount(int error_log_level, const char *what, const char *type, unsigned long flags, const char *options, int userns_fd);
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "sd-path.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "build.h"
|
#include "build.h"
|
||||||
#include "conf-files.h"
|
#include "conf-files.h"
|
||||||
@ -279,10 +281,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ignore_enoent) {
|
static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ignore_enoent, const char **search_paths) {
|
||||||
return conf_file_read(
|
return conf_file_read(
|
||||||
/* root = */ NULL,
|
/* root = */ NULL,
|
||||||
(const char**) CONF_PATHS_STRV("sysctl.d"),
|
search_paths,
|
||||||
path,
|
path,
|
||||||
parse_line,
|
parse_line,
|
||||||
sysctl_options,
|
sysctl_options,
|
||||||
@ -305,7 +307,7 @@ static int read_credential_lines(OrderedHashmap **sysctl_options) {
|
|||||||
if (!j)
|
if (!j)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
return parse_file(sysctl_options, j, /* ignore_enoent= */ true);
|
return parse_file(sysctl_options, j, /* ignore_enoent= */ true, /* search_paths= */ NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cat_config(char **files) {
|
static int cat_config(char **files) {
|
||||||
@ -440,8 +442,13 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
|
|
||||||
static int run(int argc, char *argv[]) {
|
static int run(int argc, char *argv[]) {
|
||||||
_cleanup_ordered_hashmap_free_ OrderedHashmap *sysctl_options = NULL;
|
_cleanup_ordered_hashmap_free_ OrderedHashmap *sysctl_options = NULL;
|
||||||
|
_cleanup_strv_free_ char **search_paths = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
r = sd_path_lookup_strv(SD_PATH_SEARCH_SYSCTL, /* suffix= */ NULL, &search_paths);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get sysctl.d/ search paths: %m");
|
||||||
|
|
||||||
r = parse_argv(argc, argv);
|
r = parse_argv(argc, argv);
|
||||||
if (r <= 0)
|
if (r <= 0)
|
||||||
return r;
|
return r;
|
||||||
@ -458,12 +465,12 @@ static int run(int argc, char *argv[]) {
|
|||||||
/* Use (argument):n, where n==1 for the first positional arg */
|
/* Use (argument):n, where n==1 for the first positional arg */
|
||||||
RET_GATHER(r, parse_line("(argument)", ++pos, *arg, /* invalid_config = */ NULL, &sysctl_options));
|
RET_GATHER(r, parse_line("(argument)", ++pos, *arg, /* invalid_config = */ NULL, &sysctl_options));
|
||||||
else
|
else
|
||||||
RET_GATHER(r, parse_file(&sysctl_options, *arg, false));
|
RET_GATHER(r, parse_file(&sysctl_options, *arg, /* ignore_enoent= */ false, (const char**) search_paths));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_cleanup_strv_free_ char **files = NULL;
|
_cleanup_strv_free_ char **files = NULL;
|
||||||
|
|
||||||
r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("sysctl.d"));
|
r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) search_paths);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
|
return log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
|
||||||
|
|
||||||
@ -471,7 +478,7 @@ static int run(int argc, char *argv[]) {
|
|||||||
return cat_config(files);
|
return cat_config(files);
|
||||||
|
|
||||||
STRV_FOREACH(f, files)
|
STRV_FOREACH(f, files)
|
||||||
RET_GATHER(r, parse_file(&sysctl_options, *f, true));
|
RET_GATHER(r, parse_file(&sysctl_options, *f, /* ignore_enoent= */ true, /* search_paths= */ NULL));
|
||||||
|
|
||||||
RET_GATHER(r, read_credential_lines(&sysctl_options));
|
RET_GATHER(r, read_credential_lines(&sysctl_options));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ int verb_cat(int argc, char *argv[], void *userdata) {
|
|||||||
_cleanup_hashmap_free_ Hashmap *cached_id_map = NULL, *cached_name_map = NULL;
|
_cleanup_hashmap_free_ Hashmap *cached_id_map = NULL, *cached_name_map = NULL;
|
||||||
_cleanup_(lookup_paths_done) LookupPaths lp = {};
|
_cleanup_(lookup_paths_done) LookupPaths lp = {};
|
||||||
_cleanup_strv_free_ char **names = NULL;
|
_cleanup_strv_free_ char **names = NULL;
|
||||||
sd_bus *bus;
|
sd_bus *bus = NULL;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
int r, rc = 0;
|
int r, rc = 0;
|
||||||
|
|
||||||
@ -39,6 +39,7 @@ int verb_cat(int argc, char *argv[], void *userdata) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
if (install_client_side() == INSTALL_CLIENT_SIDE_NO) {
|
||||||
r = acquire_bus(BUS_MANAGER, &bus);
|
r = acquire_bus(BUS_MANAGER, &bus);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -50,6 +51,12 @@ int verb_cat(int argc, char *argv[], void *userdata) {
|
|||||||
r = maybe_extend_with_unit_dependencies(bus, &names);
|
r = maybe_extend_with_unit_dependencies(bus, &names);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
} else {
|
||||||
|
/* In client-side mode (--global, --root, etc.), just mangle names without bus interaction */
|
||||||
|
r = mangle_names("to cat", strv_skip(argv, 1), &names);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
pager_open(arg_pager_flags);
|
pager_open(arg_pager_flags);
|
||||||
|
|
||||||
@ -57,7 +64,7 @@ int verb_cat(int argc, char *argv[], void *userdata) {
|
|||||||
_cleanup_free_ char *fragment_path = NULL;
|
_cleanup_free_ char *fragment_path = NULL;
|
||||||
_cleanup_strv_free_ char **dropin_paths = NULL;
|
_cleanup_strv_free_ char **dropin_paths = NULL;
|
||||||
|
|
||||||
r = unit_find_paths(bus, *name, &lp, false, &cached_id_map, &cached_name_map, &fragment_path, &dropin_paths);
|
r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ !bus, &cached_id_map, &cached_name_map, &fragment_path, &dropin_paths);
|
||||||
if (r == -ERFKILL) {
|
if (r == -ERFKILL) {
|
||||||
printf("%s# Unit %s is masked%s.\n",
|
printf("%s# Unit %s is masked%s.\n",
|
||||||
ansi_highlight_magenta(),
|
ansi_highlight_magenta(),
|
||||||
@ -87,7 +94,7 @@ int verb_cat(int argc, char *argv[], void *userdata) {
|
|||||||
else
|
else
|
||||||
puts("");
|
puts("");
|
||||||
|
|
||||||
if (need_daemon_reload(bus, *name) > 0) /* ignore errors (<0), this is informational output */
|
if (bus && need_daemon_reload(bus, *name) > 0) /* ignore errors (<0), this is informational output */
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%s# Warning: %s changed on disk, the version systemd has loaded is outdated.\n"
|
"%s# Warning: %s changed on disk, the version systemd has loaded is outdated.\n"
|
||||||
"%s# This output shows the current version of the unit's original fragment and drop-in files.\n"
|
"%s# This output shows the current version of the unit's original fragment and drop-in files.\n"
|
||||||
@ -211,7 +218,6 @@ static int find_paths_to_edit(
|
|||||||
const char *drop_in;
|
const char *drop_in;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(bus);
|
|
||||||
assert(context);
|
assert(context);
|
||||||
assert(names);
|
assert(names);
|
||||||
|
|
||||||
@ -241,8 +247,8 @@ static int find_paths_to_edit(
|
|||||||
_cleanup_free_ char *path = NULL;
|
_cleanup_free_ char *path = NULL;
|
||||||
_cleanup_strv_free_ char **unit_paths = NULL;
|
_cleanup_strv_free_ char **unit_paths = NULL;
|
||||||
|
|
||||||
r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ false, &cached_id_map, &cached_name_map, &path, &unit_paths);
|
r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ !bus, &cached_id_map, &cached_name_map, &path, &unit_paths);
|
||||||
if (r == -EKEYREJECTED) {
|
if (r == -EKEYREJECTED && bus) {
|
||||||
/* If loading of the unit failed server side complete, then the server won't tell us
|
/* If loading of the unit failed server side complete, then the server won't tell us
|
||||||
* the unit file path. In that case, find the file client side. */
|
* the unit file path. In that case, find the file client side. */
|
||||||
|
|
||||||
@ -327,7 +333,7 @@ int verb_edit(int argc, char *argv[], void *userdata) {
|
|||||||
.read_from_stdin = arg_stdin,
|
.read_from_stdin = arg_stdin,
|
||||||
};
|
};
|
||||||
_cleanup_strv_free_ char **names = NULL;
|
_cleanup_strv_free_ char **names = NULL;
|
||||||
sd_bus *bus;
|
sd_bus *bus = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (!on_tty() && !arg_stdin)
|
if (!on_tty() && !arg_stdin)
|
||||||
@ -340,6 +346,7 @@ int verb_edit(int argc, char *argv[], void *userdata) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
if (install_client_side() == INSTALL_CLIENT_SIDE_NO) {
|
||||||
r = acquire_bus(BUS_MANAGER, &bus);
|
r = acquire_bus(BUS_MANAGER, &bus);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -347,6 +354,12 @@ int verb_edit(int argc, char *argv[], void *userdata) {
|
|||||||
r = expand_unit_names(bus, strv_skip(argv, 1), NULL, &names, NULL);
|
r = expand_unit_names(bus, strv_skip(argv, 1), NULL, &names, NULL);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to expand names: %m");
|
return log_error_errno(r, "Failed to expand names: %m");
|
||||||
|
} else {
|
||||||
|
/* In client-side mode (--global, --root, etc.), just mangle names without bus interaction */
|
||||||
|
r = mangle_names("to edit", strv_skip(argv, 1), &names);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
if (strv_isempty(names))
|
if (strv_isempty(names))
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No units matched the specified patterns.");
|
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No units matched the specified patterns.");
|
||||||
|
|
||||||
@ -354,14 +367,6 @@ int verb_edit(int argc, char *argv[], void *userdata) {
|
|||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
"With 'edit --stdin --full', exactly one unit for editing must be specified.");
|
"With 'edit --stdin --full', exactly one unit for editing must be specified.");
|
||||||
|
|
||||||
STRV_FOREACH(tmp, names) {
|
|
||||||
r = unit_is_masked(bus, *tmp);
|
|
||||||
if (r < 0 && r != -ENOENT)
|
|
||||||
return log_error_errno(r, "Failed to check if unit %s is masked: %m", *tmp);
|
|
||||||
if (r > 0)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit %s: unit is masked.", *tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = find_paths_to_edit(bus, &context, names);
|
r = find_paths_to_edit(bus, &context, names);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
#include "reboot-util.h"
|
#include "reboot-util.h"
|
||||||
#include "runtime-scope.h"
|
#include "runtime-scope.h"
|
||||||
#include "set.h"
|
#include "set.h"
|
||||||
|
#include "stat-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "systemctl.h"
|
#include "systemctl.h"
|
||||||
@ -589,6 +590,13 @@ int unit_find_paths(
|
|||||||
return log_error_errno(r, "Failed to find fragment for '%s': %m", unit_name);
|
return log_error_errno(r, "Failed to find fragment for '%s': %m", unit_name);
|
||||||
|
|
||||||
if (_path) {
|
if (_path) {
|
||||||
|
/* Check if unit is masked (symlinked to /dev/null or empty) */
|
||||||
|
r = null_or_empty_path(_path);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to check if '%s' is masked: %m", unit_name);
|
||||||
|
if (r > 0)
|
||||||
|
return -ERFKILL; /* special case: no logging */
|
||||||
|
|
||||||
path = strdup(_path);
|
path = strdup(_path);
|
||||||
if (!path)
|
if (!path)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|||||||
@ -129,6 +129,8 @@ __extension__ enum {
|
|||||||
SD_PATH_USER_CREDENTIAL_STORE_ENCRYPTED,
|
SD_PATH_USER_CREDENTIAL_STORE_ENCRYPTED,
|
||||||
SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED,
|
SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED,
|
||||||
|
|
||||||
|
SD_PATH_SEARCH_SYSCTL,
|
||||||
|
|
||||||
_SD_PATH_MAX,
|
_SD_PATH_MAX,
|
||||||
_SD_PATH_INVALID = UINT64_MAX
|
_SD_PATH_INVALID = UINT64_MAX
|
||||||
};
|
};
|
||||||
|
|||||||
@ -112,30 +112,6 @@ TEST_RET(fd_acl_make_read_only) {
|
|||||||
cmd = strjoina("stat ", fn);
|
cmd = strjoina("stat ", fn);
|
||||||
ASSERT_OK_ZERO_ERRNO(system(cmd));
|
ASSERT_OK_ZERO_ERRNO(system(cmd));
|
||||||
|
|
||||||
log_info("writable");
|
|
||||||
ASSERT_OK_POSITIVE(fd_acl_make_writable(fd));
|
|
||||||
|
|
||||||
ASSERT_OK_ERRNO(fstat(fd, &st));
|
|
||||||
ASSERT_EQ(st.st_mode & 0222, 0200u);
|
|
||||||
|
|
||||||
cmd = strjoina("getfacl -p ", fn);
|
|
||||||
ASSERT_OK_ZERO_ERRNO(system(cmd));
|
|
||||||
|
|
||||||
cmd = strjoina("stat ", fn);
|
|
||||||
ASSERT_OK_ZERO_ERRNO(system(cmd));
|
|
||||||
|
|
||||||
log_info("read-only");
|
|
||||||
ASSERT_OK_POSITIVE(fd_acl_make_read_only(fd));
|
|
||||||
|
|
||||||
ASSERT_OK_ERRNO(fstat(fd, &st));
|
|
||||||
ASSERT_EQ(st.st_mode & 0222, 0000u);
|
|
||||||
|
|
||||||
cmd = strjoina("getfacl -p ", fn);
|
|
||||||
ASSERT_OK_ZERO_ERRNO(system(cmd));
|
|
||||||
|
|
||||||
cmd = strjoina("stat ", fn);
|
|
||||||
ASSERT_OK_ZERO_ERRNO(system(cmd));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -573,4 +573,48 @@ systemctl daemon-reload
|
|||||||
systemctl enable --now test-WantedBy.service || :
|
systemctl enable --now test-WantedBy.service || :
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
|
|
||||||
|
# Test systemctl edit --global and systemctl cat --global (issue #31272)
|
||||||
|
GLOBAL_UNIT_NAME="systemctl-test-$RANDOM.service"
|
||||||
|
GLOBAL_MASKED_UNIT="systemctl-test-masked-$RANDOM.service"
|
||||||
|
|
||||||
|
# Test 1: Create a new global user unit with --force and --runtime
|
||||||
|
systemctl edit --global --runtime --stdin --full --force "$GLOBAL_UNIT_NAME" <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Test global unit
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/bin/true
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Verify the unit file was created in /run/systemd/user/
|
||||||
|
test -f "/run/systemd/user/$GLOBAL_UNIT_NAME"
|
||||||
|
|
||||||
|
# Test 2: Read the global unit with systemctl cat --global
|
||||||
|
systemctl cat --global "$GLOBAL_UNIT_NAME" | grep -q "ExecStart=/bin/true"
|
||||||
|
|
||||||
|
# Test 3: Edit existing global unit (add a drop-in)
|
||||||
|
systemctl edit --global --runtime --stdin "$GLOBAL_UNIT_NAME" <<EOF
|
||||||
|
[Service]
|
||||||
|
Environment=TEST=value
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Verify drop-in was created
|
||||||
|
test -f "/run/systemd/user/$GLOBAL_UNIT_NAME.d/override.conf"
|
||||||
|
systemctl cat --global "$GLOBAL_UNIT_NAME" | grep -q "Environment=TEST=value"
|
||||||
|
|
||||||
|
# Test 4: Create a masked global unit in /run/
|
||||||
|
mkdir -p /run/systemd/user
|
||||||
|
ln -sf /dev/null "/run/systemd/user/$GLOBAL_MASKED_UNIT"
|
||||||
|
|
||||||
|
# Test 5: Verify cat shows it's masked
|
||||||
|
systemctl cat --global "$GLOBAL_MASKED_UNIT" 2>&1 | grep -q "masked"
|
||||||
|
|
||||||
|
# Test 6: Verify edit refuses to edit masked unit
|
||||||
|
(! systemctl edit --global --runtime --stdin --full "$GLOBAL_MASKED_UNIT" </dev/null 2>&1) | grep -q "masked"
|
||||||
|
|
||||||
|
# Cleanup global test units
|
||||||
|
rm -f "/run/systemd/user/$GLOBAL_UNIT_NAME"
|
||||||
|
rm -rf "/run/systemd/user/$GLOBAL_UNIT_NAME.d"
|
||||||
|
rm -f "/run/systemd/user/$GLOBAL_MASKED_UNIT"
|
||||||
|
|
||||||
touch /testok
|
touch /testok
|
||||||
|
|||||||
@ -163,6 +163,7 @@ systemd-inhibit.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="
|
|||||||
systemd-inhibit.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--why="]
|
systemd-inhibit.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--why="]
|
||||||
systemd-inhibit.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--mode="]
|
systemd-inhibit.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--mode="]
|
||||||
systemd-inhibit.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--list"]
|
systemd-inhibit.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--list"]
|
||||||
|
systemd-logind.service.xml ./refsect1[title="Signal"]/
|
||||||
systemd-notify.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--ready"]
|
systemd-notify.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--ready"]
|
||||||
systemd-notify.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--pid="]
|
systemd-notify.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--pid="]
|
||||||
systemd-notify.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--status="]
|
systemd-notify.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--status="]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user