+static int get_group_creds(const char *groupname, gid_t *gid) {
+ struct group *g;
+ unsigned long lu;
+
+ assert(groupname);
+ assert(gid);
+
+ /* We enforce some special rules for gid=0: in order to avoid
+ * NSS lookups for root we hardcode its data. */
+
+ if (streq(groupname, "root") || streq(groupname, "0")) {
+ *gid = 0;
+ return 0;
+ }
+
+ if (safe_atolu(groupname, &lu) >= 0) {
+ errno = 0;
+ g = getgrgid((gid_t) lu);
+ } else {
+ errno = 0;
+ g = getgrnam(groupname);
+ }
+
+ if (!g)
+ return errno != 0 ? -errno : -ESRCH;
+
+ *gid = g->gr_gid;
+ return 0;
+}
+
+static int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
+ struct passwd *p;
+ unsigned long lu;
+
+ assert(username);
+ assert(*username);
+ assert(uid);
+ assert(gid);
+ assert(home);
+
+ /* We enforce some special rules for uid=0: in order to avoid
+ * NSS lookups for root we hardcode its data. */
+
+ if (streq(*username, "root") || streq(*username, "0")) {
+ *username = "root";
+ *uid = 0;
+ *gid = 0;
+ *home = "/root";
+ return 0;
+ }
+
+ if (safe_atolu(*username, &lu) >= 0) {
+ errno = 0;
+ p = getpwuid((uid_t) lu);
+
+ /* If there are multiple users with the same id, make
+ * sure to leave $USER to the configured value instead
+ * of the first occurence in the database. However if
+ * the uid was configured by a numeric uid, then let's
+ * pick the real username from /etc/passwd. */
+ if (*username && p)
+ *username = p->pw_name;
+ } else {
+ errno = 0;
+ p = getpwnam(*username);
+ }
+
+ if (!p)
+ return errno != 0 ? -errno : -ESRCH;
+
+ *uid = p->pw_uid;
+ *gid = p->pw_gid;
+ *home = p->pw_dir;
+ return 0;
+}
+
+static int enforce_groups(const ExecContext *context, const char *username, gid_t gid) {
+ bool keep_groups = false;
+ int r;
+
+ assert(context);
+
+ /* Lookup and ser GID and supplementary group list. Here too
+ * we avoid NSS lookups for gid=0. */
+
+ if (context->group || username) {
+
+ if (context->group)
+ if ((r = get_group_creds(context->group, &gid)) < 0)
+ return r;
+
+ /* First step, initialize groups from /etc/groups */
+ if (username && gid != 0) {
+ if (initgroups(username, gid) < 0)
+ return -errno;
+
+ keep_groups = true;
+ }
+
+ /* Second step, set our gids */
+ if (setresgid(gid, gid, gid) < 0)
+ return -errno;
+ }
+
+ if (context->supplementary_groups) {
+ int ngroups_max, k;
+ gid_t *gids;
+ char **i;
+
+ /* Final step, initialize any manually set supplementary groups */
+ ngroups_max = (int) sysconf(_SC_NGROUPS_MAX);
+
+ if (!(gids = new(gid_t, ngroups_max)))
+ return -ENOMEM;
+
+ if (keep_groups) {
+ if ((k = getgroups(ngroups_max, gids)) < 0) {
+ free(gids);
+ return -errno;
+ }
+ } else
+ k = 0;
+
+ STRV_FOREACH(i, context->supplementary_groups) {
+
+ if (k >= ngroups_max) {
+ free(gids);
+ return -E2BIG;
+ }
+
+ if ((r = get_group_creds(*i, gids+k)) < 0) {
+ free(gids);
+ return r;
+ }
+
+ k++;
+ }
+
+ if (setgroups(k, gids) < 0) {
+ free(gids);
+ return -errno;
+ }
+
+ free(gids);
+ }
+
+ return 0;
+}
+
+static int enforce_user(const ExecContext *context, uid_t uid) {
+ int r;
+ assert(context);
+
+ /* Sets (but doesn't lookup) the uid and make sure we keep the
+ * capabilities while doing so. */
+
+ if (context->capabilities) {
+ cap_t d;
+ static const cap_value_t bits[] = {
+ CAP_SETUID, /* Necessary so that we can run setresuid() below */
+ CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
+ };
+
+ /* First step: If we need to keep capabilities but
+ * drop privileges we need to make sure we keep our
+ * caps, whiel we drop priviliges. */
+ if (uid != 0) {
+ int sb = context->secure_bits|SECURE_KEEP_CAPS;
+
+ if (prctl(PR_GET_SECUREBITS) != sb)
+ if (prctl(PR_SET_SECUREBITS, sb) < 0)
+ return -errno;
+ }
+
+ /* Second step: set the capabilites. This will reduce
+ * the capabilities to the minimum we need. */
+
+ if (!(d = cap_dup(context->capabilities)))
+ return -errno;
+
+ if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
+ cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0) {
+ r = -errno;
+ cap_free(d);
+ return r;
+ }
+
+ if (cap_set_proc(d) < 0) {
+ r = -errno;
+ cap_free(d);
+ return r;
+ }
+
+ cap_free(d);
+ }
+
+ /* Third step: actually set the uids */
+ if (setresuid(uid, uid, uid) < 0)
+ return -errno;
+
+ /* At this point we should have all necessary capabilities but
+ are otherwise a normal user. However, the caps might got
+ corrupted due to the setresuid() so we need clean them up
+ later. This is done outside of this call. */
+
+ return 0;
+}
+
+int exec_spawn(const ExecCommand *command,
+ const ExecContext *context,
+ int *fds, unsigned n_fds,
+ bool apply_permissions,
+ bool apply_chroot,
+ CGroupBonding *cgroup_bondings,
+ pid_t *ret) {
+