+#if CONFIG_COALITIONS
+static inline void spawn_coalitions_release_all(coalition_t coal[COALITION_NUM_TYPES])
+{
+ for (int c = 0; c < COALITION_NUM_TYPES; c++) {
+ if (coal[c]) {
+ coalition_remove_active(coal[c]);
+ coalition_release(coal[c]);
+ }
+ }
+}
+#endif
+
+#if CONFIG_PERSONAS
+static int spawn_validate_persona(struct _posix_spawn_persona_info *px_persona)
+{
+ int error = 0;
+ struct persona *persona = NULL;
+ int verify = px_persona->pspi_flags & POSIX_SPAWN_PERSONA_FLAGS_VERIFY;
+
+ /*
+ * TODO: rdar://problem/19981151
+ * Add entitlement check!
+ */
+ if (!kauth_cred_issuser(kauth_cred_get()))
+ return EPERM;
+
+ persona = persona_lookup(px_persona->pspi_id);
+ if (!persona) {
+ error = ESRCH;
+ goto out;
+ }
+
+ if (verify) {
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_UID) {
+ if (px_persona->pspi_uid != persona_get_uid(persona)) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_GID) {
+ if (px_persona->pspi_gid != persona_get_gid(persona)) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_GROUPS) {
+ int ngroups = 0;
+ gid_t groups[NGROUPS_MAX];
+
+ if (persona_get_groups(persona, &ngroups, groups,
+ px_persona->pspi_ngroups) != 0) {
+ error = EINVAL;
+ goto out;
+ }
+ if (ngroups != (int)px_persona->pspi_ngroups) {
+ error = EINVAL;
+ goto out;
+ }
+ while (ngroups--) {
+ if (px_persona->pspi_groups[ngroups] != groups[ngroups]) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ if (px_persona->pspi_gmuid != persona_get_gmuid(persona)) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ }
+
+out:
+ if (persona)
+ persona_put(persona);
+
+ return error;
+}
+
+static int spawn_persona_adopt(proc_t p, struct _posix_spawn_persona_info *px_persona)
+{
+ int ret;
+ kauth_cred_t cred;
+ struct persona *persona = NULL;
+ int override = !!(px_persona->pspi_flags & POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE);
+
+ if (!override)
+ return persona_proc_adopt_id(p, px_persona->pspi_id, NULL);
+
+ /*
+ * we want to spawn into the given persona, but we want to override
+ * the kauth with a different UID/GID combo
+ */
+ persona = persona_lookup(px_persona->pspi_id);
+ if (!persona)
+ return ESRCH;
+
+ cred = persona_get_cred(persona);
+ if (!cred) {
+ ret = EINVAL;
+ goto out;
+ }
+
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_UID) {
+ cred = kauth_cred_setresuid(cred,
+ px_persona->pspi_uid,
+ px_persona->pspi_uid,
+ px_persona->pspi_uid,
+ KAUTH_UID_NONE);
+ }
+
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_GID) {
+ cred = kauth_cred_setresgid(cred,
+ px_persona->pspi_gid,
+ px_persona->pspi_gid,
+ px_persona->pspi_gid);
+ }
+
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_GROUPS) {
+ cred = kauth_cred_setgroups(cred,
+ px_persona->pspi_groups,
+ px_persona->pspi_ngroups,
+ px_persona->pspi_gmuid);
+ }
+
+ ret = persona_proc_adopt(p, persona, cred);
+
+out:
+ persona_put(persona);
+ return ret;
+}
+#endif
+
+void
+proc_set_return_wait(proc_t p)
+{
+ proc_lock(p);
+ p->p_lflag |= P_LRETURNWAIT;
+ proc_unlock(p);
+}
+
+void
+proc_clear_return_wait(proc_t p, thread_t child_thread)
+{
+ proc_lock(p);
+
+ p->p_lflag &= ~P_LRETURNWAIT;
+ if (p->p_lflag & P_LRETURNWAITER) {
+ wakeup(&p->p_lflag);
+ }
+
+ proc_unlock(p);
+
+ (void)thread_resume(child_thread);
+}
+
+void
+proc_wait_to_return()
+{
+ proc_t p;
+
+ p = current_proc();
+ proc_lock(p);
+
+ if (p->p_lflag & P_LRETURNWAIT) {
+ p->p_lflag |= P_LRETURNWAITER;
+ do {
+ msleep(&p->p_lflag, &p->p_mlock, 0,
+ "thread_check_setup_complete", NULL);
+ } while (p->p_lflag & P_LRETURNWAIT);
+ p->p_lflag &= ~P_LRETURNWAITER;
+ }
+
+ proc_unlock(p);
+ thread_bootstrap_return();
+}
+