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/execute.c | |
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/execute.c')
-rw-r--r-- | src/core/execute.c | 59 |
1 files changed, 59 insertions, 0 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); |