core: do not leak mount for credentials directory if mount namespace is enabled

Since kernel v5.2, open_tree() and move_mount() are added. If a service
loads or sets credentials, then let's try to clone the mount that contains
credentials with open_tree(), then mount it after a (private) mount
namespace is initialized for the service. Then, we can setup a mount for
credentials directory without leaking it to the main shared mount
namespace.

With this change, the credentials for services that request their own
private mount namespace become much much safer. And, the number of mount
events triggered by setting up credential directories can be decreased.

Unfortunately, this does not 'fix' the original issue #25527, as the
reported service does not requests private mount namespace, but the
situation should be better now.
This commit is contained in:
Yu Watanabe
2023-08-22 11:50:16 +09:00
parent 7241b9cd72
commit 94fe4cf255
7 changed files with 115 additions and 17 deletions
+20 -2
View File
@@ -7,6 +7,7 @@
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/shm.h>
@@ -72,6 +73,7 @@
#include "missing_fs.h"
#include "missing_ioprio.h"
#include "missing_prctl.h"
#include "missing_syscall.h"
#include "mkdir-label.h"
#include "namespace.h"
#include "parse-util.h"
@@ -3112,6 +3114,7 @@ static int apply_mount_namespace(
ExecRuntime *runtime,
const char *memory_pressure_path,
const char *creds_path,
int creds_fd,
char **error_path) {
_cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
@@ -3287,6 +3290,7 @@ static int apply_mount_namespace(
tmp_dir,
var_tmp_dir,
creds_path,
creds_fd,
context->log_namespace,
context->mount_propagation_flag,
&verity,
@@ -3971,6 +3975,7 @@ static int exec_child(
int ngids_after_pam = 0;
_cleanup_free_ int *fds = NULL;
_cleanup_strv_free_ char **fdnames = NULL;
_cleanup_close_ int creds_fd = -EBADF;
assert(unit);
assert(command);
@@ -4421,7 +4426,7 @@ static int exec_child(
}
if (FLAGS_SET(params->flags, EXEC_WRITE_CREDENTIALS)) {
r = setup_credentials(context, params, unit->id, uid, gid, &creds_path);
r = setup_credentials(context, params, unit->id, uid, gid, &creds_path, &creds_fd);
if (r < 0) {
*exit_status = EXIT_CREDENTIALS;
return log_unit_error_errno(unit, r, "Failed to set up credentials: %m");
@@ -4635,7 +4640,7 @@ static int exec_child(
if (needs_mount_namespace) {
_cleanup_free_ char *error_path = NULL;
r = apply_mount_namespace(unit, command->flags, context, params, runtime, memory_pressure_path, creds_path, &error_path);
r = apply_mount_namespace(unit, command->flags, context, params, runtime, memory_pressure_path, creds_path, creds_fd, &error_path);
if (r < 0) {
*exit_status = EXIT_NAMESPACE;
return log_unit_error_errno(unit, r, "Failed to set up mount namespacing%s%s: %m",
@@ -4643,6 +4648,19 @@ static int exec_child(
}
}
if (creds_fd >= 0) {
assert(creds_path);
/* When a mount namespace is not requested, then the target directory may not exist yet.
* Here, we ignore the failure, as if it fails, the subsequent move_mount() will fail. */
(void) mkdir_p_label(creds_path, 0755);
if (move_mount(creds_fd, "", AT_FDCWD, creds_path, MOVE_MOUNT_F_EMPTY_PATH) < 0) {
*exit_status = EXIT_CREDENTIALS;
return log_unit_error_errno(unit, errno, "Failed to mount credentials directory on %s: %m", creds_path);
}
}
if (needs_sandboxing) {
r = apply_protect_hostname(unit, context, exit_status);
if (r < 0)