diff options
author | Lennart Poettering <lennart@poettering.net> | 2016-12-02 01:54:41 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2016-12-13 20:59:10 +0100 |
commit | 74dd6b515fa968c5710b396a7664cac335e25ca8 (patch) | |
tree | 4aa9a78c228ce5e2833e2b3ed0bbee931895f653 /src/core | |
parent | 9ef4e1e5a2d0a9cc50406f1cae05f3918d6f0c2a (diff) |
core: run each system service with a fresh session keyring
This patch ensures that each system service gets its own session kernel keyring
automatically, and implicitly. Without this a keyring is allocated for it
on-demand, but is then linked with the user's kernel keyring, which is OK
behaviour for logged in users, but not so much for system services.
With this change each service gets a session keyring that is specific to the
service and ceases to exist when the service is shut down. The session keyring
is not linked up with the user keyring and keys hence only search within the
session boundaries by default.
(This is useful in a later commit to store per-service material in the keyring,
for example the invocation ID)
(With input from David Howells)
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/execute.c | 44 | ||||
-rw-r--r-- | src/core/execute.h | 9 | ||||
-rw-r--r-- | src/core/service.c | 1 |
3 files changed, 50 insertions, 4 deletions
diff --git a/src/core/execute.c b/src/core/execute.c index 07ab067c05..5ac270aa12 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2196,6 +2196,44 @@ static int apply_working_directory(const ExecContext *context, return 0; } +static int setup_keyring(Unit *u, const ExecParameters *p, uid_t uid, gid_t gid) { + key_serial_t keyring; + + assert(u); + assert(p); + + /* Let's set up a new per-service "session" kernel keyring for each system service. This has the benefit that + * each service runs with its own keyring shared among all processes of the service, but with no hook-up beyond + * that scope, and in particular no link to the per-UID keyring. If we don't do this the keyring will be + * automatically created on-demand and then linked to the per-UID keyring, by the kernel. The kernel's built-in + * on-demand behaviour is very appropriate for login users, but probably not so much for system services, where + * UIDs are not necessarily specific to a service but reused (at least in the case of UID 0). */ + + if (!(p->flags & EXEC_NEW_KEYRING)) + return 0; + + keyring = keyctl(KEYCTL_JOIN_SESSION_KEYRING, 0, 0, 0, 0); + if (keyring == -1) { + if (errno == ENOSYS) + log_debug_errno(errno, "Kernel keyring not supported, ignoring."); + else if (IN_SET(errno, EACCES, EPERM)) + log_debug_errno(errno, "Kernel keyring access prohibited, ignoring."); + else if (errno == EDQUOT) + log_debug_errno(errno, "Out of kernel keyrings to allocate, ignoring."); + else + return log_error_errno(errno, "Setting up kernel keyring failed: %m"); + + return 0; + } + + /* And now, make the keyring owned by the service's user */ + if (uid_is_valid(uid) || gid_is_valid(gid)) + if (keyctl(KEYCTL_CHOWN, keyring, uid, gid, 0) < 0) + return log_error_errno(errno, "Failed to change ownership of session keyring: %m"); + + return 0; +} + static void append_socket_pair(int *array, unsigned *n, int pair[2]) { assert(array); assert(n); @@ -2638,6 +2676,12 @@ static int exec_child( (void) umask(context->umask); + r = setup_keyring(unit, params, uid, gid); + if (r < 0) { + *exit_status = EXIT_KEYRING; + return r; + } + if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) { if (context->pam_name && username) { r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, fds, n_fds); diff --git a/src/core/execute.h b/src/core/execute.h index 951c8f4da3..b376a6db55 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -228,12 +228,13 @@ typedef enum ExecFlags { EXEC_APPLY_PERMISSIONS = 1U << 0, EXEC_APPLY_CHROOT = 1U << 1, EXEC_APPLY_TTY_STDIN = 1U << 2, + EXEC_NEW_KEYRING = 1U << 3, /* The following are not used by execute.c, but by consumers internally */ - EXEC_PASS_FDS = 1U << 3, - EXEC_IS_CONTROL = 1U << 4, - EXEC_SETENV_RESULT = 1U << 5, - EXEC_SET_WATCHDOG = 1U << 6, + EXEC_PASS_FDS = 1U << 4, + EXEC_IS_CONTROL = 1U << 5, + EXEC_SETENV_RESULT = 1U << 6, + EXEC_SET_WATCHDOG = 1U << 7, } ExecFlags; struct ExecParameters { diff --git a/src/core/service.c b/src/core/service.c index 576416ad29..73a8104d17 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1344,6 +1344,7 @@ static int service_spawn( } else path = UNIT(s)->cgroup_path; + exec_params.flags |= MANAGER_IS_SYSTEM(UNIT(s)->manager) ? EXEC_NEW_KEYRING : 0; exec_params.argv = c->argv; exec_params.environment = final_env; exec_params.fds = fds; |