Merge pull request #34020 from YHNdnzj/cred-no-mountover

core/dbus-service: refuse bind mounting over /run/credentials/
This commit is contained in:
Yu Watanabe 2024-08-19 04:29:42 +09:00 committed by GitHub
commit 871b0ee995
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 47 additions and 39 deletions

View File

@ -13,6 +13,7 @@
#include "dbus-service.h"
#include "dbus-util.h"
#include "execute.h"
#include "exec-credential.h"
#include "exit-status.h"
#include "fd-util.h"
#include "fileio.h"
@ -129,33 +130,36 @@ static int property_get_exit_status_set(
}
static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_bus_error *error, bool is_image) {
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
const char *dest, *src, *propagate_directory;
int read_only, make_file_or_directory;
Unit *u = ASSERT_PTR(userdata);
ExecContext *c;
int r;
assert(message);
if (!MANAGER_IS_SYSTEM(u->manager))
return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Adding bind mounts at runtime is only supported for system managers.");
return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Adding bind mounts at runtime is only supported by system manager");
if (u->type != UNIT_SERVICE)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not of type .service");
r = mac_selinux_unit_access_check(u, message, "start", error);
if (r < 0)
return r;
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
const char *src, *dest;
int read_only, make_file_or_directory;
r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_file_or_directory);
if (r < 0)
return r;
if (!path_is_absolute(src) || !path_is_normalized(src))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized.");
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized");
if (!is_image && isempty(dest))
dest = src;
else if (!path_is_absolute(dest) || !path_is_normalized(dest))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized");
if (is_image) {
r = bus_read_mount_options(message, error, &options, NULL, "");
@ -174,26 +178,28 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
if (u->type != UNIT_SERVICE)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not of type .service");
const PidRef *unit_pid = unit_main_pid(u);
if (!pidref_is_set(unit_pid) || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
return sd_bus_error_set(error, BUS_ERROR_UNIT_INACTIVE, "Unit is not running");
/* If it would be dropped at startup time, return an error. The context should always be available, but
* there's an assert in exec_needs_mount_namespace, so double-check just in case. */
c = unit_get_exec_context(u);
/* The context should always be available, but there's an assert in exec_needs_mount_namespace,
* so double-check just in case. */
ExecContext *c = unit_get_exec_context(u);
if (!c)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot access unit execution context");
if (path_startswith_strv(dest, c->inaccessible_paths))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s is not accessible to this unit", dest);
return -ENXIO;
/* Ensure that the unit was started in a private mount namespace */
if (!exec_needs_mount_namespace(c, NULL, unit_get_exec_runtime(u)))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit not running in private mount namespace, cannot activate bind mount");
PidRef* unit_pid = unit_main_pid(u);
if (!pidref_is_set(unit_pid) || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not running");
if (mount_point_is_credentials(u->manager->prefix[EXEC_DIRECTORY_RUNTIME], dest))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Refusing to bind mount over credential mounts");
propagate_directory = strjoina("/run/systemd/propagate/", u->id);
/* If it would be dropped at startup time, return an error. */
if (path_startswith_strv(dest, c->inaccessible_paths))
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "%s is not accessible to this unit", dest);
const char *propagate_directory = strjoina("/run/systemd/propagate/", u->id);
if (is_image)
r = mount_image_in_namespace(
unit_pid,
@ -213,7 +219,7 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
read_only,
make_file_or_directory);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to mount %s on %s in unit's namespace: %m", src, dest);
return sd_bus_error_set_errnof(error, r, "Failed to mount '%s' on '%s' in unit's namespace: %m", src, dest);
return sd_bus_reply_method_return(message, NULL);
}

View File

@ -242,6 +242,19 @@ bool exec_context_has_encrypted_credentials(const ExecContext *c) {
return false;
}
bool mount_point_is_credentials(const char *runtime_prefix, const char *path) {
const char *e;
assert(runtime_prefix);
assert(path);
e = path_startswith(path, runtime_prefix);
if (!e)
return false;
return path_startswith(e, "credentials");
}
static int get_credential_directory(
const char *runtime_prefix,
const char *unit,

View File

@ -67,3 +67,5 @@ int exec_setup_credentials(
const char *unit,
uid_t uid,
gid_t gid);
bool mount_point_is_credentials(const char *runtime_prefix, const char *path);

View File

@ -11,6 +11,7 @@
#include "dbus-mount.h"
#include "dbus-unit.h"
#include "device.h"
#include "exec-credential.h"
#include "exit-status.h"
#include "format-util.h"
#include "fs-util.h"
@ -425,22 +426,6 @@ static bool mount_is_extrinsic(Unit *u) {
return false;
}
static bool mount_point_is_credentials(Manager *manager, const char *path) {
const char *e;
assert(manager);
assert(path);
/* Returns true if this is a credentials mount. We don't want to generate mount units for them,
* since their lifetime is strictly bound to services. */
e = path_startswith(path, manager->prefix[EXEC_DIRECTORY_RUNTIME]);
if (!e)
return false;
return !isempty(path_startswith(e, "credentials"));
}
static int mount_add_default_ordering_dependencies(Mount *m, MountParameters *p, UnitDependencyMask mask) {
const char *after, *before, *e;
int r;
@ -581,7 +566,7 @@ static int mount_verify(Mount *m) {
return log_unit_error_errno(UNIT(m), SYNTHETIC_ERRNO(ENOEXEC),
"Cannot create mount unit for API file system '%s'. Refusing.", m->where);
if (mount_point_is_credentials(UNIT(m)->manager, m->where))
if (mount_point_is_credentials(UNIT(m)->manager->prefix[EXEC_DIRECTORY_RUNTIME], m->where))
return log_unit_error_errno(UNIT(m), SYNTHETIC_ERRNO(ENOEXEC),
"Cannot create mount unit for credential mount '%s'. Refusing.", m->where);
@ -1830,8 +1815,10 @@ static int mount_setup_unit(
assert(fstype);
/* Ignore API and credential mount points. They should never be referenced in dependencies ever.
* Also check the comment for mount_point_is_credentials(). */
if (mount_point_is_api(where) || mount_point_ignore(where) || mount_point_is_credentials(m, where))
* Furthermore, the lifetime of credential mounts is strictly bound to the owning services,
* so mount units make little sense for them. */
if (mount_point_is_api(where) || mount_point_ignore(where) ||
mount_point_is_credentials(m->prefix[EXEC_DIRECTORY_RUNTIME], where))
return 0;
if (streq(fstype, "autofs"))