diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2016-12-13 23:24:42 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-12-13 23:24:42 -0500 |
commit | 4014818d530ad3f6f5e4b94b79a9336376feface (patch) | |
tree | cadf4eb8a8c4c0be179b02c92eb811115dd8c268 /src/core | |
parent | 9cf314f34d9ca26bb8867effdf54fc2c78b06f31 (diff) | |
parent | ab79099d1684457d040ee7c28b2012e8c1ea9a4f (diff) |
Merge pull request #4806 from poettering/keyring-init
set up a per-service session kernel keyring, and store the invocation ID in it
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/execute.c | 59 | ||||
-rw-r--r-- | src/core/execute.h | 9 | ||||
-rw-r--r-- | src/core/service.c | 1 |
3 files changed, 65 insertions, 4 deletions
diff --git a/src/core/execute.c b/src/core/execute.c index 2ee8c9a416..4ff6f4ebd0 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2201,6 +2201,59 @@ 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; + } + + /* Populate they keyring with the invocation ID by default. */ + if (!sd_id128_is_null(u->invocation_id)) { + key_serial_t key; + + key = add_key("user", "invocation_id", &u->invocation_id, sizeof(u->invocation_id), KEY_SPEC_SESSION_KEYRING); + if (key == -1) + log_debug_errno(errno, "Failed to add invocation ID to keyring, ignoring: %m"); + else { + if (keyctl(KEYCTL_SETPERM, key, + KEY_POS_VIEW|KEY_POS_READ|KEY_POS_SEARCH| + KEY_USR_VIEW|KEY_USR_READ|KEY_USR_SEARCH, 0, 0) < 0) + return log_error_errno(errno, "Failed to restrict invocation ID permission: %m"); + } + } + + /* 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); @@ -2643,6 +2696,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 84ab4339cf..f8694ef520 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -230,12 +230,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; |