#include <seccomp.h>
#endif
+#ifdef HAVE_APPARMOR
+#include <sys/apparmor.h>
+#endif
+
#include "execute.h"
#include "strv.h"
#include "macro.h"
#include "async.h"
#include "selinux-util.h"
#include "errno-list.h"
+#include "af-list.h"
+#include "mkdir.h"
+#include "apparmor-util.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1);
if (r == -EEXIST)
continue;
- if (r < 0) {
- seccomp_release(seccomp);
- return r;
- }
+ if (r < 0)
+ goto finish;
}
- } else {
+ } else {
r = seccomp_add_secondary_archs(seccomp);
- if (r < 0) {
- seccomp_release(seccomp);
- return r;
- }
+ if (r < 0)
+ goto finish;
}
action = c->syscall_whitelist ? SCMP_ACT_ALLOW : negative_action;
SET_FOREACH(id, c->syscall_filter, i) {
r = seccomp_rule_add(seccomp, action, PTR_TO_INT(id) - 1, 0);
- if (r < 0) {
- seccomp_release(seccomp);
- return r;
- }
+ if (r < 0)
+ goto finish;
}
+ r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
+ if (r < 0)
+ goto finish;
+
r = seccomp_load(seccomp);
+
+finish:
seccomp_release(seccomp);
+ return r;
+}
+
+static int apply_address_families(ExecContext *c) {
+ scmp_filter_ctx *seccomp;
+ Iterator i;
+ int r;
+
+ assert(c);
+
+ seccomp = seccomp_init(SCMP_ACT_ALLOW);
+ if (!seccomp)
+ return -ENOMEM;
+
+ r = seccomp_add_secondary_archs(seccomp);
+ if (r < 0)
+ goto finish;
+
+ if (c->address_families_whitelist) {
+ int af, first = 0, last = 0;
+ void *afp;
+
+ /* If this is a whitelist, we first block the address
+ * families that are out of range and then everything
+ * that is not in the set. First, we find the lowest
+ * and highest address family in the set. */
+
+ SET_FOREACH(afp, c->address_families, i) {
+ af = PTR_TO_INT(afp);
+
+ if (af <= 0 || af >= af_max())
+ continue;
+
+ if (first == 0 || af < first)
+ first = af;
+
+ if (last == 0 || af > last)
+ last = af;
+ }
+
+ assert((first == 0) == (last == 0));
+
+ if (first == 0) {
+
+ /* No entries in the valid range, block everything */
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EPROTONOSUPPORT),
+ SCMP_SYS(socket),
+ 0);
+ if (r < 0)
+ goto finish;
+
+ } else {
+
+ /* Block everything below the first entry */
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EPROTONOSUPPORT),
+ SCMP_SYS(socket),
+ 1,
+ SCMP_A0(SCMP_CMP_LT, first));
+ if (r < 0)
+ goto finish;
+
+ /* Block everything above the last entry */
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EPROTONOSUPPORT),
+ SCMP_SYS(socket),
+ 1,
+ SCMP_A0(SCMP_CMP_GT, last));
+ if (r < 0)
+ goto finish;
+
+ /* Block everything between the first and last
+ * entry */
+ for (af = 1; af < af_max(); af++) {
+
+ if (set_contains(c->address_families, INT_TO_PTR(af)))
+ continue;
+
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EPROTONOSUPPORT),
+ SCMP_SYS(socket),
+ 1,
+ SCMP_A0(SCMP_CMP_EQ, af));
+ if (r < 0)
+ goto finish;
+ }
+ }
+
+ } else {
+ void *af;
+
+ /* If this is a blacklist, then generate one rule for
+ * each address family that are then combined in OR
+ * checks. */
+
+ SET_FOREACH(af, c->address_families, i) {
+
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EPROTONOSUPPORT),
+ SCMP_SYS(socket),
+ 1,
+ SCMP_A0(SCMP_CMP_EQ, PTR_TO_INT(af)));
+ if (r < 0)
+ goto finish;
+ }
+ }
+
+ r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
+ if (r < 0)
+ goto finish;
+ r = seccomp_load(seccomp);
+
+finish:
+ seccomp_release(seccomp);
return r;
}
+
#endif
static void do_idle_pipe_dance(int idle_pipe[4]) {
bool confirm_spawn,
CGroupControllerMask cgroup_supported,
const char *cgroup_path,
+ const char *runtime_prefix,
const char *unit_id,
usec_t watchdog_usec,
int idle_pipe[4],
}
#endif
+ if (!strv_isempty(context->runtime_directory) && runtime_prefix) {
+ char **rt;
+
+ STRV_FOREACH(rt, context->runtime_directory) {
+ _cleanup_free_ char *p;
+
+ p = strjoin(runtime_prefix, "/", *rt, NULL);
+ if (!p) {
+ r = EXIT_RUNTIME_DIRECTORY;
+ err = -ENOMEM;
+ goto fail_child;
+ }
+
+ err = mkdir_safe(p, context->runtime_directory_mode, uid, gid);
+ if (err < 0) {
+ r = EXIT_RUNTIME_DIRECTORY;
+ goto fail_child;
+ }
+ }
+ }
+
if (apply_permissions) {
err = enforce_groups(context, username, gid);
if (err < 0) {
if (apply_permissions) {
- for (i = 0; i < RLIMIT_NLIMITS; i++) {
+ for (i = 0; i < _RLIMIT_MAX; i++) {
if (!context->rlimit[i])
continue;
}
#ifdef HAVE_SECCOMP
- if (context->syscall_filter || context->syscall_archs) {
+ if (context->address_families_whitelist ||
+ !set_isempty(context->address_families)) {
+ err = apply_address_families(context);
+ if (err < 0) {
+ r = EXIT_ADDRESS_FAMILIES;
+ goto fail_child;
+ }
+ }
+
+ if (context->syscall_whitelist ||
+ !set_isempty(context->syscall_filter) ||
+ !set_isempty(context->syscall_archs)) {
err = apply_seccomp(context);
if (err < 0) {
r = EXIT_SECCOMP;
}
}
#endif
+
+#ifdef HAVE_APPARMOR
+ if (context->apparmor_profile && use_apparmor()) {
+ err = aa_change_onexec(context->apparmor_profile);
+ if (err < 0 && !context->apparmor_profile_ignore) {
+ r = EXIT_APPARMOR_PROFILE;
+ goto fail_child;
+ }
+ }
+#endif
}
err = build_environment(context, n_fds, watchdog_usec, home, username, shell, &our_env);
c->ignore_sigpipe = true;
c->timer_slack_nsec = (nsec_t) -1;
c->personality = 0xffffffffUL;
+ c->runtime_directory_mode = 0755;
}
void exec_context_done(ExecContext *c) {
free(c->selinux_context);
c->selinux_context = NULL;
-#ifdef HAVE_SECCOMP
+ free(c->apparmor_profile);
+ c->apparmor_profile = NULL;
+
set_free(c->syscall_filter);
c->syscall_filter = NULL;
set_free(c->syscall_archs);
c->syscall_archs = NULL;
-#endif
+
+ set_free(c->address_families);
+ c->address_families = NULL;
+
+ strv_free(c->runtime_directory);
+ c->runtime_directory = NULL;
+}
+
+int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_prefix) {
+ char **i;
+
+ assert(c);
+
+ if (!runtime_prefix)
+ return 0;
+
+ STRV_FOREACH(i, c->runtime_directory) {
+ _cleanup_free_ char *p;
+
+ p = strjoin(runtime_prefix, "/", *i, NULL);
+ if (!p)
+ return -ENOMEM;
+
+ /* We execute this synchronously, since we need to be
+ * sure this is gone when we start the service
+ * next. */
+ rm_rf_dangerous(p, false, true, false);
+ }
+
+ return 0;
}
void exec_command_done(ExecCommand *c) {
fprintf(f,
"%sSystemCallErrorNumber: %s\n",
prefix, strna(errno_to_name(c->syscall_errno)));
+
+ if (c->apparmor_profile)
+ fprintf(f,
+ "%sAppArmorProfile: %s%s\n",
+ prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
}
void exec_status_start(ExecStatus *s, pid_t pid) {
}
void exec_runtime_destroy(ExecRuntime *rt) {
+ int r;
+
if (!rt)
return;
if (rt->tmp_dir) {
log_debug("Spawning thread to nuke %s", rt->tmp_dir);
- asynchronous_job(remove_tmpdir_thread, rt->tmp_dir);
+
+ r = asynchronous_job(remove_tmpdir_thread, rt->tmp_dir);
+ if (r < 0) {
+ log_warning("Failed to nuke %s: %s", rt->tmp_dir, strerror(-r));
+ free(rt->tmp_dir);
+ }
+
rt->tmp_dir = NULL;
}
if (rt->var_tmp_dir) {
log_debug("Spawning thread to nuke %s", rt->var_tmp_dir);
- asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir);
+
+ r = asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir);
+ if (r < 0) {
+ log_warning("Failed to nuke %s: %s", rt->var_tmp_dir, strerror(-r));
+ free(rt->var_tmp_dir);
+ }
+
rt->var_tmp_dir = NULL;
}