</varlistentry>
<varlistentry>
+ <term><varname>KeyringMode=</varname></term>
+
+ <listitem><para>Controls how the kernel session keyring is set up for the service (see <citerefentry
+ project='man-pages'><refentrytitle>session-keyring</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+ details on the session keyring). Takes one of <option>inherit</option>, <option>private</option>,
+ <option>shared</option>. If set to <option>inherit</option> no special keyring setup is done, and the kernel's
+ default behaviour is applied. If <option>private</option> is used a new session keyring is allocated when a
+ service process is invoked, and it is not linked up with any user keyring. This is the recommended setting for
+ system services, as this ensures that multiple services running under the same system user ID (in particular
+ the root user) do not share their key material among each other. If <option>shared</option> is used a new
+ session keyring is allocated as for <option>private</option>, but the user keyring of the user configured with
+ <varname>User=</varname> is linked into it, so that keys assigned to the user may be requested by the unit's
+ processes. In this modes multiple units running processes under the same user ID may share key material. Unless
+ <option>inherit</option> is selected the unique invocation ID for the unit (see below) is added as a protected
+ key by the name <literal>invocation_id</literal> to the newly created session keyring. Defaults to
+ <option>private</option> for the system service manager and to <option>inherit</option> for the user service
+ manager.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>RuntimeDirectory=</varname></term>
<listitem><para>Takes a whitespace-separated list of directory names. The specified directory names must be
#define KEYCTL_DESCRIBE 6
#endif
+#ifndef KEYCTL_LINK
+#define KEYCTL_LINK 8
+#endif
+
#ifndef KEYCTL_READ
#define KEYCTL_READ 11
#endif
#include "utf8.h"
BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutput);
-
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode);
-
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_preserve_mode, exec_preserve_mode, ExecPreserveMode);
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode);
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome);
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem);
SD_BUS_PROPERTY("BindPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("BindReadOnlyPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MountAPIVFS", "b", bus_property_get_bool, offsetof(ExecContext, mount_apivfs), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("KeyringMode", "s", property_get_exec_keyring_mode, offsetof(ExecContext, keyring_mode), SD_BUS_VTABLE_PROPERTY_CONST),
/* Obsolete/redundant properties: */
SD_BUS_PROPERTY("Capabilities", "s", property_get_empty_string, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
return 1;
+ } else if (streq(name, "KeyringMode")) {
+
+ const char *s;
+ ExecKeyringMode m;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ m = exec_keyring_mode_from_string(s);
+ if (m < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid key ring mode");
+
+ if (mode != UNIT_CHECK) {
+ c->keyring_mode = m;
+
+ unit_write_drop_in_private_format(u, mode, name, "KeyringMode=%s", exec_keyring_mode_to_string(m));
+ }
+
+ return 1;
+
} else if (streq(name, "RuntimeDirectoryPreserve")) {
const char *s;
ExecPreserveMode m;
return 0;
}
-static int setup_keyring(Unit *u, const ExecParameters *p, uid_t uid, gid_t gid) {
+static int setup_keyring(
+ Unit *u,
+ const ExecContext *context,
+ const ExecParameters *p,
+ uid_t uid, gid_t gid) {
+
key_serial_t keyring;
+ int r;
assert(u);
+ assert(context);
assert(p);
/* Let's set up a new per-service "session" kernel keyring for each system service. This has the benefit that
if (!(p->flags & EXEC_NEW_KEYRING))
return 0;
+ if (context->keyring_mode == EXEC_KEYRING_INHERIT)
+ return 0;
+
keyring = keyctl(KEYCTL_JOIN_SESSION_KEYRING, 0, 0, 0, 0);
if (keyring == -1) {
if (errno == ENOSYS)
if (keyctl(KEYCTL_CHOWN, keyring, uid, gid, 0) < 0)
return log_error_errno(errno, "Failed to change ownership of session keyring: %m");
+ /* When requested link the user keyring into the session keyring. */
+ if (context->keyring_mode == EXEC_KEYRING_SHARED) {
+ uid_t saved_uid;
+ gid_t saved_gid;
+
+ /* Acquiring a reference to the user keyring is nasty. We briefly change identity in order to get things
+ * set up properly by the kernel. If we don't do that then we can't create it atomically, and that
+ * sucks for parallel execution. This mimics what pam_keyinit does, too.*/
+
+ saved_uid = getuid();
+ saved_gid = getgid();
+
+ if (gid_is_valid(gid) && gid != saved_gid) {
+ if (setregid(gid, -1) < 0)
+ return log_error_errno(errno, "Failed to change GID for user keyring: %m");
+ }
+
+ if (uid_is_valid(uid) && uid != saved_uid) {
+ if (setreuid(uid, -1) < 0) {
+ (void) setregid(saved_gid, -1);
+ return log_error_errno(errno, "Failed to change UID for user keyring: %m");
+ }
+ }
+
+ if (keyctl(KEYCTL_LINK,
+ KEY_SPEC_USER_KEYRING,
+ KEY_SPEC_SESSION_KEYRING, 0, 0) < 0) {
+
+ r = -errno;
+
+ (void) setreuid(saved_uid, -1);
+ (void) setregid(saved_gid, -1);
+
+ return log_error_errno(r, "Failed to link user keyring into session keyring: %m");
+ }
+
+ if (uid_is_valid(uid) && uid != saved_uid) {
+ if (setreuid(saved_uid, -1) < 0) {
+ (void) setregid(saved_gid, -1);
+ return log_error_errno(errno, "Failed to change UID back for user keyring: %m");
+ }
+ }
+
+ if (gid_is_valid(gid) && gid != saved_gid) {
+ if (setregid(saved_gid, -1) < 0)
+ return log_error_errno(errno, "Failed to change GID back for user keyring: %m");
+ }
+ }
+
return 0;
}
(void) umask(context->umask);
- r = setup_keyring(unit, params, uid, gid);
+ r = setup_keyring(unit, context, params, uid, gid);
if (r < 0) {
*exit_status = EXIT_KEYRING;
return r;
"%sMountAPIVFS: %s\n"
"%sIgnoreSIGPIPE: %s\n"
"%sMemoryDenyWriteExecute: %s\n"
- "%sRestrictRealtime: %s\n",
+ "%sRestrictRealtime: %s\n"
+ "%sKeyringMode: %s\n",
prefix, c->umask,
prefix, c->working_directory ? c->working_directory : "/",
prefix, c->root_directory ? c->root_directory : "/",
prefix, yes_no(c->mount_apivfs),
prefix, yes_no(c->ignore_sigpipe),
prefix, yes_no(c->memory_deny_write_execute),
- prefix, yes_no(c->restrict_realtime));
+ prefix, yes_no(c->restrict_realtime),
+ prefix, exec_keyring_mode_to_string(c->keyring_mode));
if (c->root_image)
fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
};
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
+
+static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
+ [EXEC_KEYRING_INHERIT] = "inherit",
+ [EXEC_KEYRING_PRIVATE] = "private",
+ [EXEC_KEYRING_SHARED] = "shared",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
_EXEC_PRESERVE_MODE_INVALID = -1
} ExecPreserveMode;
+typedef enum ExecKeyringMode {
+ EXEC_KEYRING_INHERIT,
+ EXEC_KEYRING_PRIVATE,
+ EXEC_KEYRING_SHARED,
+ _EXEC_KEYRING_MODE_MAX,
+ _EXEC_KEYRING_MODE_INVALID = -1,
+} ExecKeyringMode;
+
struct ExecStatus {
dual_timestamp start_timestamp;
dual_timestamp exit_timestamp;
bool smack_process_label_ignore;
char *smack_process_label;
+ ExecKeyringMode keyring_mode;
+
char **read_write_paths, **read_only_paths, **inaccessible_paths;
unsigned long mount_flags;
BindMount *bind_mounts;
const char* exec_preserve_mode_to_string(ExecPreserveMode i) _const_;
ExecPreserveMode exec_preserve_mode_from_string(const char *s) _pure_;
+const char* exec_keyring_mode_to_string(ExecKeyringMode i) _const_;
+ExecKeyringMode exec_keyring_mode_from_string(const char *s) _pure_;
+
const char* exec_directory_type_to_string(ExecDirectoryType i) _const_;
ExecDirectoryType exec_directory_type_from_string(const char *s) _pure_;
$1.AmbientCapabilities, config_parse_capability_set, 0, offsetof($1, exec_context.capability_ambient_set)
$1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec)
$1.NoNewPrivileges, config_parse_no_new_privileges, 0, offsetof($1, exec_context)
+$1.KeyringMode, config_parse_exec_keyring_mode, 0, offsetof($1, exec_context.keyring_mode)
m4_ifdef(`HAVE_SECCOMP',
`$1.SystemCallFilter, config_parse_syscall_filter, 0, offsetof($1, exec_context)
$1.SystemCallArchitectures, config_parse_syscall_archs, 0, offsetof($1, exec_context.syscall_archs)
return 0;
}
+DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
+
#define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
int config_parse_user_group_strv(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_restrict_namespaces(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bind_paths(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_keyring_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
}
ec = unit_get_exec_context(u);
- if (ec)
+ if (ec) {
exec_context_init(ec);
+ ec->keyring_mode = MANAGER_IS_SYSTEM(u->manager) ?
+ EXEC_KEYRING_PRIVATE : EXEC_KEYRING_INHERIT;
+ }
+
kc = unit_get_kill_context(u);
if (kc)
kill_context_init(kc);
"Description", "Slice", "Type", "WorkingDirectory",
"RootDirectory", "SyslogIdentifier", "ProtectSystem",
"ProtectHome", "SELinuxContext", "Restart", "RootImage",
- "NotifyAccess", "RuntimeDirectoryPreserve", "Personality"))
+ "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
+ "KeyringMode"))
r = sd_bus_message_append(m, "v", "s", eq);
else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {