summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/basic/exit-status.c3
-rw-r--r--src/basic/exit-status.h1
-rw-r--r--src/basic/missing.h50
-rw-r--r--src/core/execute.c44
-rw-r--r--src/core/execute.h9
-rw-r--r--src/core/service.c1
6 files changed, 104 insertions, 4 deletions
diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c
index 59557f8afe..1e23c32c3f 100644
--- a/src/basic/exit-status.c
+++ b/src/basic/exit-status.c
@@ -148,6 +148,9 @@ const char* exit_status_to_string(int status, ExitStatusLevel level) {
case EXIT_SMACK_PROCESS_LABEL:
return "SMACK_PROCESS_LABEL";
+
+ case EXIT_KEYRING:
+ return "KEYRING";
}
}
diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h
index 0cfdfd7891..d22b2c00e4 100644
--- a/src/basic/exit-status.h
+++ b/src/basic/exit-status.h
@@ -82,6 +82,7 @@ enum {
EXIT_MAKE_STARTER,
EXIT_CHOWN,
EXIT_SMACK_PROCESS_LABEL,
+ EXIT_KEYRING,
};
typedef enum ExitStatusLevel {
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 1502b3f4f4..dd4425697f 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -1026,6 +1026,22 @@ struct btrfs_ioctl_quota_ctl_args {
typedef int32_t key_serial_t;
#endif
+#ifndef KEYCTL_JOIN_SESSION_KEYRING
+#define KEYCTL_JOIN_SESSION_KEYRING 1
+#endif
+
+#ifndef KEYCTL_CHOWN
+#define KEYCTL_CHOWN 4
+#endif
+
+#ifndef KEYCTL_SETPERM
+#define KEYCTL_SETPERM 5
+#endif
+
+#ifndef KEYCTL_DESCRIBE
+#define KEYCTL_DESCRIBE 6
+#endif
+
#ifndef KEYCTL_READ
#define KEYCTL_READ 11
#endif
@@ -1034,10 +1050,44 @@ typedef int32_t key_serial_t;
#define KEYCTL_SET_TIMEOUT 15
#endif
+#ifndef KEY_POS_VIEW
+#define KEY_POS_VIEW 0x01000000
+#define KEY_POS_READ 0x02000000
+#define KEY_POS_WRITE 0x04000000
+#define KEY_POS_SEARCH 0x08000000
+#define KEY_POS_LINK 0x10000000
+#define KEY_POS_SETATTR 0x20000000
+
+#define KEY_USR_VIEW 0x00010000
+#define KEY_USR_READ 0x00020000
+#define KEY_USR_WRITE 0x00040000
+#define KEY_USR_SEARCH 0x00080000
+#define KEY_USR_LINK 0x00100000
+#define KEY_USR_SETATTR 0x00200000
+
+#define KEY_GRP_VIEW 0x00000100
+#define KEY_GRP_READ 0x00000200
+#define KEY_GRP_WRITE 0x00000400
+#define KEY_GRP_SEARCH 0x00000800
+#define KEY_GRP_LINK 0x00001000
+#define KEY_GRP_SETATTR 0x00002000
+
+#define KEY_OTH_VIEW 0x00000001
+#define KEY_OTH_READ 0x00000002
+#define KEY_OTH_WRITE 0x00000004
+#define KEY_OTH_SEARCH 0x00000008
+#define KEY_OTH_LINK 0x00000010
+#define KEY_OTH_SETATTR 0x00000020
+#endif
+
#ifndef KEY_SPEC_USER_KEYRING
#define KEY_SPEC_USER_KEYRING -4
#endif
+#ifndef KEY_SPEC_SESSION_KEYRING
+#define KEY_SPEC_SESSION_KEYRING -3
+#endif
+
#ifndef PR_CAP_AMBIENT
#define PR_CAP_AMBIENT 47
#endif
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;