return context_has_address_families(c) ||
c->memory_deny_write_execute ||
c->restrict_realtime ||
+ c->restrict_suid_sgid ||
exec_context_restrict_namespaces_set(c) ||
c->protect_kernel_tunables ||
c->protect_kernel_modules ||
return seccomp_restrict_realtime();
}
+static int apply_restrict_suid_sgid(const Unit* u, const ExecContext *c) {
+ assert(u);
+ assert(c);
+
+ if (!c->restrict_suid_sgid)
+ return 0;
+
+ if (skip_seccomp_unavailable(u, "RestrictSUIDSGID="))
+ return 0;
+
+ return seccomp_restrict_suid_sgid();
+}
+
static int apply_protect_sysctl(const Unit *u, const ExecContext *c) {
assert(u);
assert(c);
}
/* Lock down the access mode (we use chmod_and_chown() to make this idempotent. We don't
- * specifiy UID/GID here, so that path_chown_recursive() can optimize things depending on the
+ * specify UID/GID here, so that path_chown_recursive() can optimize things depending on the
* current UID/GID ownership.) */
r = chmod_and_chown(pp ?: p, context->directories[type].mode, UID_INVALID, GID_INVALID);
if (r < 0)
goto fail;
- /* Then, change the ownership of the whole tree, if necessary */
- r = path_chown_recursive(pp ?: p, uid, gid);
+ /* Then, change the ownership of the whole tree, if necessary. When dynamic users are used we
+ * drop the suid/sgid bits, since we really don't want SUID/SGID files for dynamic UID/GID
+ * assignments to exist.*/
+ r = path_chown_recursive(pp ?: p, uid, gid, context->dynamic_user ? 01777 : 07777);
if (r < 0)
goto fail;
}
.source = s,
.destination = d,
.read_only = false,
+ .nosuid = context->dynamic_user, /* don't allow suid/sgid when DynamicUser= is on */
.recursive = true,
.ignore_enoent = false,
};
const ExecCommand *command,
const ExecContext *context,
const ExecParameters *params,
- const ExecRuntime *runtime) {
+ const ExecRuntime *runtime,
+ char **error_path) {
_cleanup_strv_free_ char **empty_directories = NULL;
char *tmp = NULL, *var = NULL;
needs_sandboxing ? context->protect_home : PROTECT_HOME_NO,
needs_sandboxing ? context->protect_system : PROTECT_SYSTEM_NO,
context->mount_flags,
- DISSECT_IMAGE_DISCARD_ON_LOOP);
+ DISSECT_IMAGE_DISCARD_ON_LOOP,
+ error_path);
bind_mount_free_many(bind_mounts, n_bind_mounts);
/* If we couldn't set up the namespace this is probably due to a missing capability. setup_namespace() reports
- * that with a special, recognizable error ENOANO. In this case, silently proceeed, but only if exclusively
+ * that with a special, recognizable error ENOANO. In this case, silently proceed, but only if exclusively
* sandboxing options were used, i.e. nothing such as RootDirectory= or BindMount= that would result in a
* completely different execution environment. */
if (r == -ENOANO) {
needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
if (needs_mount_namespace) {
- r = apply_mount_namespace(unit, command, context, params, runtime);
+ _cleanup_free_ char *error_path = NULL;
+
+ r = apply_mount_namespace(unit, command, context, params, runtime, &error_path);
if (r < 0) {
*exit_status = EXIT_NAMESPACE;
- return log_unit_error_errno(unit, r, "Failed to set up mount namespacing: %m");
+ return log_unit_error_errno(unit, r, "Failed to set up mount namespacing%s%s: %m",
+ error_path ? ": " : "", strempty(error_path));
}
}
return log_unit_error_errno(unit, r, "Failed to apply realtime restrictions: %m");
}
+ r = apply_restrict_suid_sgid(unit, context);
+ if (r < 0) {
+ *exit_status = EXIT_SECCOMP;
+ return log_unit_error_errno(unit, r, "Failed to apply SUID/SGID restrictions: %m");
+ }
+
r = apply_restrict_namespaces(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
return true; /* if we could not resolve, assume it may */
/* "tty0" means the active VC, so it may be the same sometimes */
- return streq(resolved, tty) || (streq(resolved, "tty0") && tty_is_vc(tty));
+ return path_equal(resolved, tty) || (streq(resolved, "tty0") && tty_is_vc(tty));
}
-bool exec_context_may_touch_console(const ExecContext *ec) {
+static bool exec_context_may_touch_tty(const ExecContext *ec) {
+ assert(ec);
- return (ec->tty_reset ||
+ return ec->tty_reset ||
ec->tty_vhangup ||
ec->tty_vt_disallocate ||
is_terminal_input(ec->std_input) ||
is_terminal_output(ec->std_output) ||
- is_terminal_output(ec->std_error)) &&
+ is_terminal_output(ec->std_error);
+}
+
+bool exec_context_may_touch_console(const ExecContext *ec) {
+
+ return exec_context_may_touch_tty(ec) &&
tty_may_match_dev_console(exec_context_tty_path(ec));
}
"%sIgnoreSIGPIPE: %s\n"
"%sMemoryDenyWriteExecute: %s\n"
"%sRestrictRealtime: %s\n"
+ "%sRestrictSUIDSGID: %s\n"
"%sKeyringMode: %s\n"
"%sProtectHostname: %s\n",
prefix, c->umask,
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_suid_sgid),
prefix, exec_keyring_mode_to_string(c->keyring_mode),
prefix, yes_no(c->protect_hostname));
c->n_log_extra_fields = 0;
}
+void exec_context_revert_tty(ExecContext *c) {
+ int r;
+
+ assert(c);
+
+ /* First, reset the TTY (possibly kicking everybody else from the TTY) */
+ exec_context_tty_reset(c, NULL);
+
+ /* And then undo what chown_terminal() did earlier. Note that we only do this if we have a path
+ * configured. If the TTY was passed to us as file descriptor we assume the TTY is opened and managed
+ * by whoever passed it to us and thus knows better when and how to chmod()/chown() it back. */
+
+ if (exec_context_may_touch_tty(c)) {
+ const char *path;
+
+ path = exec_context_tty_path(c);
+ if (path) {
+ r = chmod_and_chown(path, TTY_MODE, 0, TTY_GID);
+ if (r < 0 && r != -ENOENT)
+ log_warning_errno(r, "Failed to reset TTY ownership/access mode of %s, ignoring: %m", path);
+ }
+ }
+}
+
void exec_status_start(ExecStatus *s, pid_t pid) {
assert(s);
s->code = code;
s->status = status;
- if (context) {
- if (context->utmp_id)
- (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
-
- exec_context_tty_reset(context, NULL);
- }
+ if (context && context->utmp_id)
+ (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
}
void exec_status_reset(ExecStatus *s) {