]> git.saurik.com Git - apple/launchd.git/blobdiff - launchd/src/launchd_core_logic.c
launchd-258.25.tar.gz
[apple/launchd.git] / launchd / src / launchd_core_logic.c
index 726ab6342a3f48d769ffed4ee51c3639c26a95aa..ec29e12f9f522955fe720cc4e9c1f6390eb0c945 100644 (file)
  * @APPLE_APACHE_LICENSE_HEADER_END@
  */
 
-static const char *const __rcs_file_version__ = "$Revision: 23433 $";
+static const char *const __rcs_file_version__ = "$Revision: 23923 $";
 
 #include "config.h"
 #include "launchd_core_logic.h"
 
+#include <TargetConditionals.h>
 #include <mach/mach.h>
 #include <mach/mach_error.h>
 #include <mach/mach_time.h>
@@ -73,15 +74,20 @@ static const char *const __rcs_file_version__ = "$Revision: 23433 $";
 #include <ctype.h>
 #include <glob.h>
 #include <spawn.h>
+#if HAVE_SANDBOX
 #include <sandbox.h>
+#endif
+#if HAVE_QUARANTINE
+#include <quarantine.h>
+#endif
 
-#include "liblaunch_public.h"
-#include "liblaunch_private.h"
-#include "liblaunch_internal.h"
-#include "libbootstrap_public.h"
-#include "libbootstrap_private.h"
-#include "libvproc_public.h"
-#include "libvproc_internal.h"
+#include "launch.h"
+#include "launch_priv.h"
+#include "launch_internal.h"
+#include "bootstrap.h"
+#include "bootstrap_priv.h"
+#include "vproc.h"
+#include "vproc_internal.h"
 
 #include "reboot2.h"
 
@@ -90,7 +96,7 @@ static const char *const __rcs_file_version__ = "$Revision: 23433 $";
 #include "launchd_unix_ipc.h"
 #include "protocol_vproc.h"
 #include "protocol_vprocServer.h"
-#include "job_reply.h"
+#include "protocol_job_reply.h"
 
 #define LAUNCHD_MIN_JOB_RUN_TIME 10
 #define LAUNCHD_DEFAULT_EXIT_TIMEOUT 20
@@ -164,7 +170,10 @@ struct socketgroup {
        SLIST_ENTRY(socketgroup) sle;
        int *fds;
        unsigned int junkfds:1, fd_cnt:31;
-       char name[0];
+       union {
+               const char name[0];
+               char name_init[0];
+       };
 };
 
 static bool socketgroup_new(job_t j, const char *name, int *fds, unsigned int fd_cnt, bool junkfds);
@@ -196,7 +205,10 @@ static void calendarinterval_sanity_check(void);
 struct envitem {
        SLIST_ENTRY(envitem) sle;
        char *value;
-       char key[0];
+       union {
+               const char key[0];
+               char key_init[0];
+       };
 };
 
 static bool envitem_new(job_t j, const char *k, const char *v, bool global);
@@ -212,7 +224,9 @@ struct limititem {
 static bool limititem_update(job_t j, int w, rlim_t r);
 static void limititem_delete(job_t j, struct limititem *li);
 static void limititem_setup(launch_data_t obj, const char *key, void *context);
+#if HAVE_SANDBOX
 static void seatbelt_setup_flags(launch_data_t obj, const char *key, void *context);
+#endif
 
 typedef enum {
        NETWORK_UP = 1,
@@ -234,7 +248,10 @@ struct semaphoreitem {
        SLIST_ENTRY(semaphoreitem) sle;
        semaphore_reason_t why;
        int fd;
-       char what[0];
+       union {
+               const char what[0];
+               char what_init[0];
+       };
 };
 
 struct semaphoreitem_dict_iter_context {
@@ -271,7 +288,10 @@ struct jobmgr_s {
        unsigned int hopefully_first_cnt;
        unsigned int normal_active_cnt;
        unsigned int sent_stop_to_normal_jobs:1, sent_stop_to_hopefully_last_jobs:1, shutting_down:1, session_initialized:1;
-       char name[0];
+       union {
+               const char name[0];
+               char name_init[0];
+       };
 };
 
 #define jobmgr_assumes(jm, e)  \
@@ -281,6 +301,7 @@ static jobmgr_t jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t t
 static job_t jobmgr_import2(jobmgr_t jm, launch_data_t pload);
 static jobmgr_t jobmgr_parent(jobmgr_t jm);
 static jobmgr_t jobmgr_do_garbage_collection(jobmgr_t jm);
+static bool jobmgr_label_test(jobmgr_t jm, const char *str);
 static void jobmgr_reap_bulk(jobmgr_t jm, struct kevent *kev);
 static void jobmgr_log_stray_children(jobmgr_t jm);
 static void jobmgr_remove(jobmgr_t jm);
@@ -322,7 +343,7 @@ struct job_s {
        cpu_type_t *j_binpref;
        size_t j_binpref_cnt;
        mach_port_t j_port;
-       mach_port_t wait_reply_port;
+       mach_port_t wait_reply_port;    /* we probably should switch to a list of waiters */
        uid_t mach_uid;
        jobmgr_t mgr;
        char **argv;
@@ -333,12 +354,17 @@ struct job_s {
        char *groupname;
        char *stdoutpath;
        char *stderrpath;
+       char *alt_exc_handler;
        struct machservice *lastlookup;
        unsigned int lastlookup_gennum;
+#if HAVE_SANDBOX
        char *seatbelt_profile;
        uint64_t seatbelt_flags;
+#endif
+#if HAVE_QUARANTINE
        void *quarantine_data;
        size_t quarantine_data_sz;
+#endif
        pid_t p;
        int argc;
        int last_exit_status;
@@ -360,7 +386,7 @@ struct job_s {
                     currently_ignored:1, forced_peers_to_demand_mode:1, setnice:1, hopefully_exits_last:1, removal_pending:1,
                     wait4pipe_eof:1, sent_sigkill:1, debug_before_kill:1, weird_bootstrap:1, start_on_mount:1,
                     per_user:1, hopefully_exits_first:1, deny_unknown_mslookups:1, unload_at_mig_return:1, abandon_pg:1,
-                    poll_for_vfs_changes:1;
+                    poll_for_vfs_changes:1, internal_exc_handler:1, deny_job_creation:1;
        const char label[0];
 };
 
@@ -394,6 +420,9 @@ static void job_setup_attributes(job_t j);
 static bool job_setup_machport(job_t j);
 static void job_setup_fd(job_t j, int target_fd, const char *path, int flags);
 static void job_postfork_become_user(job_t j);
+#if !TARGET_OS_EMBEDDED
+static void job_enable_audit_for_user(job_t j, uid_t u, char *name);
+#endif
 static void job_find_and_blame_pids_with_weird_uids(job_t j);
 static void job_force_sampletool(job_t j);
 static void job_setup_exception_port(job_t j, task_t target_task);
@@ -449,9 +478,11 @@ static char **mach_cmd2argv(const char *string);
 static size_t our_strhash(const char *s) __attribute__((pure));
 static void extract_rcsid_substr(const char *i, char *o, size_t osz);
 static void do_first_per_user_launchd_hack(void);
+static size_t get_kern_max_proc(void);
 static void do_file_init(void) __attribute__((constructor));
 
 /* file local globals */
+static bool do_apple_internal_magic;
 static size_t total_children;
 static size_t total_anon_children;
 static mach_port_t the_exception_server;
@@ -861,12 +892,19 @@ job_remove(job_t j)
        if (j->stderrpath) {
                free(j->stderrpath);
        }
+       if (j->alt_exc_handler) {
+               free(j->alt_exc_handler);
+       }
+#if HAVE_SANDBOX
        if (j->seatbelt_profile) {
                free(j->seatbelt_profile);
        }
+#endif
+#if HAVE_QUARANTINE
        if (j->quarantine_data) {
                free(j->quarantine_data);
        }
+#endif
        if (j->j_binpref) {
                free(j->j_binpref);
        }
@@ -1323,6 +1361,13 @@ job_import_bool(job_t j, const char *key, bool value)
                        found_key = true;
                }
                break;
+       case 'm':
+       case 'M':
+               if (strcasecmp(key, LAUNCH_JOBKEY_MACHEXCEPTIONHANDLER) == 0) {
+                       j->internal_exc_handler = value;
+                       found_key = true;
+               }
+               break;
        case 'i':
        case 'I':
                if (strcasecmp(key, LAUNCH_JOBKEY_INITGROUPS) == 0) {
@@ -1376,6 +1421,12 @@ job_import_string(job_t j, const char *key, const char *value)
        char **where2put = NULL;
 
        switch (key[0]) {
+       case 'm':
+       case 'M':
+               if (strcasecmp(key, LAUNCH_JOBKEY_MACHEXCEPTIONHANDLER) == 0) {
+                       where2put = &j->alt_exc_handler;
+               }
+               break;
        case 'p':
        case 'P':
                if (strcasecmp(key, LAUNCH_JOBKEY_PROGRAM) == 0) {
@@ -1441,8 +1492,10 @@ job_import_string(job_t j, const char *key, const char *value)
                        where2put = &j->stdoutpath;
                } else if (strcasecmp(key, LAUNCH_JOBKEY_STANDARDERRORPATH) == 0) {
                        where2put = &j->stderrpath;
+#if HAVE_SANDBOX
                } else if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOXPROFILE) == 0) {
                        where2put = &j->seatbelt_profile;
+#endif
                }
                break;
        default:
@@ -1520,8 +1573,10 @@ job_import_integer(job_t j, const char *key, long long value)
 
                                job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, value, j) != -1);
                        }
+#if HAVE_SANDBOX
                } else if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOXFLAGS) == 0) {
                        j->seatbelt_flags = value;
+#endif
                }
 
                break;
@@ -1532,11 +1587,13 @@ job_import_integer(job_t j, const char *key, long long value)
 }
 
 void
-job_import_opaque(job_t j, const char *key, launch_data_t value)
+job_import_opaque(job_t j __attribute__((unused)),
+       const char *key, launch_data_t value __attribute__((unused)))
 {
        switch (key[0]) {
        case 'q':
        case 'Q':
+#if HAVE_QUARANTINE
                if (strcasecmp(key, LAUNCH_JOBKEY_QUARANTINEDATA) == 0) {
                        size_t tmpsz = launch_data_get_opaque_size(value);
 
@@ -1545,18 +1602,48 @@ job_import_opaque(job_t j, const char *key, launch_data_t value)
                                j->quarantine_data_sz = tmpsz;
                        }
                }
+#endif
                break;
        default:
                break;
        }
 }
 
+static void
+policy_setup(launch_data_t obj, const char *key, void *context)
+{
+       job_t j = context;
+       bool found_key = false;
+
+       switch (key[0]) {
+       case 'd':
+       case 'D':
+               if (strcasecmp(key, LAUNCH_JOBPOLICY_DENYCREATINGOTHERJOBS) == 0) {
+                       j->deny_job_creation = launch_data_get_bool(obj);
+                       found_key = true;
+               }
+               break;
+       default:
+               break;
+       }
+
+       if (unlikely(!found_key)) {
+               job_log(j, LOG_WARNING, "Unknown policy: %s", key);
+       }
+}
+
 void
 job_import_dictionary(job_t j, const char *key, launch_data_t value)
 {
        launch_data_t tmp;
 
        switch (key[0]) {
+       case 'p':
+       case 'P':
+               if (strcasecmp(key, LAUNCH_JOBKEY_POLICIES) == 0) {
+                       launch_data_dict_iterate(value, policy_setup, j);
+               }
+               break;
        case 'k':
        case 'K':
                if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE) == 0) {
@@ -1595,8 +1682,10 @@ job_import_dictionary(job_t j, const char *key, launch_data_t value)
                        calendarinterval_new_from_obj(j, value);
                } else if (strcasecmp(key, LAUNCH_JOBKEY_SOFTRESOURCELIMITS) == 0) {
                        launch_data_dict_iterate(value, limititem_setup, j);
+#if HAVE_SANDBOX
                } else if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOXFLAGS) == 0) {
                        launch_data_dict_iterate(value, seatbelt_setup_flags, j);
+#endif
                }
                break;
        case 'h':
@@ -1799,10 +1888,7 @@ jobmgr_import2(jobmgr_t jm, launch_data_t pload)
        if ((j = job_find(label)) != NULL) {
                errno = EEXIST;
                return NULL;
-       } else if (label[0] == '\0' || (strncasecmp(label, "", strlen("com.apple.launchd")) == 0) ||
-                       (strtol(label, NULL, 10) != 0)) {
-               jobmgr_log(jm, LOG_ERR, "Somebody attempted to use a reserved prefix for a label: %s", label);
-               /* the empty string, com.apple.launchd and number prefixes for labels are reserved */
+       } else if (!jobmgr_label_test(jm, label)) {
                errno = EINVAL;
                return NULL;
        }
@@ -1814,6 +1900,40 @@ jobmgr_import2(jobmgr_t jm, launch_data_t pload)
        return j;
 }
 
+bool
+jobmgr_label_test(jobmgr_t jm, const char *str)
+{
+       char *endstr = NULL;
+       const char *ptr;
+
+       if (str[0] == '\0') {
+               jobmgr_log(jm, LOG_ERR, "Empty job labels are not allowed");
+               return false;
+       }
+       
+       for (ptr = str; *ptr; ptr++) {
+               if (iscntrl(*ptr)) {
+                       jobmgr_log(jm, LOG_ERR, "ASCII control characters are not allowed in job labels. Index: %td Value: 0x%hhx", ptr - str, *ptr);
+                       return false;
+               }
+       }
+       
+       strtoll(str, &endstr, 0);
+
+       if (str != endstr) {
+               jobmgr_log(jm, LOG_ERR, "Job labels are not allowed to begin with numbers: %s", str);
+               return false;
+       }
+       
+       if ((strncasecmp(str, "com.apple.launchd", strlen("com.apple.launchd")) == 0) ||
+                       (strncasecmp(str, "com.apple.launchctl", strlen("com.apple.launchctl")) == 0)) {
+               jobmgr_log(jm, LOG_ERR, "Job labels are not allowed to use a reserved prefix: %s", str);
+               return false;
+       }
+
+       return true;
+}
+
 job_t 
 job_find(const char *label)
 {
@@ -1976,9 +2096,15 @@ void
 job_log_stray_pg(job_t j)
 {
        int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PGRP, j->p };
-       size_t i, kp_cnt, len = 10*1024*1024;
+       size_t i, kp_cnt, len = sizeof(struct kinfo_proc) * get_kern_max_proc();
        struct kinfo_proc *kp;
 
+#if TARGET_OS_EMBEDDED
+       if (!do_apple_internal_magic) {
+               return;
+       }
+#endif
+
        if (!job_assumes(j, (kp = malloc(len)) != NULL)) {
                return;
        }
@@ -2137,6 +2263,8 @@ job_reap(job_t j)
        }
        j->last_exit_status = status;
        j->sent_sigkill = false;
+       j->lastlookup = NULL;
+       j->lastlookup_gennum = 0;
        j->p = 0;
 
        /*
@@ -2274,7 +2402,7 @@ job_kill(job_t j)
 }
 
 void
-job_callback_proc(job_t j, int flags, int fflags)
+job_callback_proc(job_t j, int flags __attribute__((unused)), int fflags)
 {
        if ((fflags & NOTE_EXEC) && j->anonymous) {
                int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, j->p };
@@ -2488,6 +2616,11 @@ job_start(job_t j)
 
        if (!j->legacy_mach_job) {
                sipc = (!SLIST_EMPTY(&j->sockets) || !SLIST_EMPTY(&j->machservices));
+#if TARGET_OS_EMBEDDED
+               if (j->username && strcmp(j->username, "mobile") == 0 && strncmp(j->label, "com.apple.", strlen("com.apple.")) != 0) {
+                       sipc = false;
+               }
+#endif
        }
 
        j->checkedin = false;
@@ -2515,6 +2648,13 @@ job_start(job_t j)
                        job_assumes(j, runtime_close(spair[0]) == 0);
                        job_assumes(j, runtime_close(spair[1]) == 0);
                }
+               if (!j->legacy_mach_job) {
+                       job_assumes(j, runtime_close(oepair[0]) != -1);
+                       job_assumes(j, runtime_close(oepair[1]) != -1);
+                       j->log_redirect_fd = 0;
+               }
+               job_assumes(j, kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 1, j) != -1);
+               job_ignore(j);
                break;
        case 0:
                if (_vproc_post_fork_ping()) {
@@ -2658,6 +2798,7 @@ job_start_child(job_t j)
                job_assumes(j, binpref_out_cnt == j->j_binpref_cnt);
        }
 
+#if HAVE_QUARANTINE
        if (j->quarantine_data) {
                qtn_proc_t qp;
 
@@ -2667,7 +2808,9 @@ job_start_child(job_t j)
                        }
                }
        }
+#endif
 
+#if HAVE_SANDBOX
        if (j->seatbelt_profile) {
                char *seatbelt_err_buf = NULL;
 
@@ -2678,6 +2821,7 @@ job_start_child(job_t j)
                        goto out_bad;
                }
        }
+#endif
 
        if (j->prog) {
                errno = posix_spawn(&junk_pid, j->inetcompat ? file2exec : j->prog, NULL, &spattr, (char *const*)argv, environ);
@@ -2687,7 +2831,9 @@ job_start_child(job_t j)
                job_log_error(j, LOG_ERR, "posix_spawnp(\"%s\", ...)", argv[0]);
        }
 
+#if HAVE_SANDBOX
 out_bad:
+#endif
        _exit(EXIT_FAILURE);
 }
 
@@ -2742,10 +2888,17 @@ void
 job_find_and_blame_pids_with_weird_uids(job_t j)
 {
        int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
-       size_t i, kp_cnt, len = 10*1024*1024;
-       struct kinfo_proc *kp = malloc(len);
+       size_t i, kp_cnt, len = sizeof(struct kinfo_proc) * get_kern_max_proc();
+       struct kinfo_proc *kp;
        uid_t u = j->mach_uid;
 
+#if TARGET_OS_EMBEDDED
+       if (!do_apple_internal_magic) {
+               return;
+       }
+#endif
+       kp = malloc(len);
+
        if (!job_assumes(j, kp != NULL)) {
                return;
        }
@@ -2779,6 +2932,30 @@ out:
        free(kp);
 }
 
+#if !TARGET_OS_EMBEDDED
+void
+job_enable_audit_for_user(job_t j, uid_t u, char *name)
+{
+       auditinfo_t auinfo = {
+               .ai_auid = u,
+               .ai_asid = j->p,
+       };
+       long au_cond;
+
+       if (!job_assumes(j, auditon(A_GETCOND, &au_cond, sizeof(long)) == 0)) {
+               _exit(EXIT_FAILURE);
+       }
+
+       if (au_cond != AUC_NOAUDIT) {
+               if (!job_assumes(j, au_user_mask(name, &auinfo.ai_mask) == 0)) {
+                       _exit(EXIT_FAILURE);
+               } else if (!job_assumes(j, setaudit(&auinfo) == 0)) {
+                       _exit(EXIT_FAILURE);
+               }
+       }
+}
+#endif
+
 void
 job_postfork_become_user(job_t j)
 {
@@ -2856,6 +3033,10 @@ job_postfork_become_user(job_t j)
                desired_gid = gre->gr_gid;
        }
 
+#if !TARGET_OS_EMBEDDED
+       job_enable_audit_for_user(j, desired_uid, loginname);
+#endif
+
        if (!job_assumes(j, setlogin(loginname) != -1)) {
                _exit(EXIT_FAILURE);
        }
@@ -3240,11 +3421,10 @@ semaphoreitem_ignore(job_t j, struct semaphoreitem *si)
 void
 semaphoreitem_watch(job_t j, struct semaphoreitem *si)
 {
-       char parentdir_path[PATH_MAX], *which_path = si->what;
+       char *parentdir, tmp_path[PATH_MAX];
+       const char *which_path = si->what;
        int saved_errno = 0;
        int fflags = 0;
-       
-       strlcpy(parentdir_path, dirname(si->what), sizeof(parentdir_path));
 
        switch (si->why) {
        case PATH_EXISTS:
@@ -3261,11 +3441,18 @@ semaphoreitem_watch(job_t j, struct semaphoreitem *si)
                return;
        }
 
+       /* dirname() may modify tmp_path */
+       strlcpy(tmp_path, si->what, sizeof(tmp_path));
+
+       if (!job_assumes(j, (parentdir = dirname(tmp_path)))) {
+               return;
+       }
+
        /* See 5321044 for why we do the do-while loop and 5415523 for why ENOENT is checked */
        do {
                if (si->fd == -1) {
                        if ((si->fd = _fd(open(which_path, O_EVTONLY|O_NOCTTY))) == -1) {
-                               which_path = parentdir_path;
+                               which_path = parentdir;
                                si->fd = _fd(open(which_path, O_EVTONLY|O_NOCTTY));
                        }
                }
@@ -3506,7 +3693,7 @@ socketgroup_new(job_t j, const char *name, int *fds, unsigned int fd_cnt, bool j
        }
 
        memcpy(sg->fds, fds, fd_cnt * sizeof(int));
-       strcpy(sg->name, name);
+       strcpy(sg->name_init, name);
 
        SLIST_INSERT_HEAD(&j->sockets, sg, sle);
 
@@ -3598,8 +3785,8 @@ envitem_new(job_t j, const char *k, const char *v, bool global)
                return false;
        }
 
-       strcpy(ei->key, k);
-       ei->value = ei->key + strlen(k) + 1;
+       strcpy(ei->key_init, k);
+       ei->value = ei->key_init + strlen(k) + 1;
        strcpy(ei->value, v);
 
        if (global) {
@@ -3679,6 +3866,7 @@ limititem_delete(job_t j, struct limititem *li)
        free(li);
 }
 
+#if HAVE_SANDBOX
 void
 seatbelt_setup_flags(launch_data_t obj, const char *key, void *context)
 {
@@ -3697,6 +3885,7 @@ seatbelt_setup_flags(launch_data_t obj, const char *key, void *context)
                j->seatbelt_flags |= SANDBOX_NAMED;
        }
 }
+#endif
 
 void
 limititem_setup(launch_data_t obj, const char *key, void *context)
@@ -3738,7 +3927,7 @@ job_useless(job_t j)
        } else if (j->removal_pending) {
                job_log(j, LOG_DEBUG, "Exited while removal was pending.");
                return true;
-       } else if (j->mgr->shutting_down) {
+       } else if (j->mgr->shutting_down && (j->hopefully_exits_first || j->mgr->hopefully_first_cnt == 0)) {
                job_log(j, LOG_DEBUG, "Exited while shutdown in progress. Processes remaining: %lu/%lu", total_children, total_anon_children);
                return true;
        } else if (j->legacy_mach_job) {
@@ -3764,6 +3953,10 @@ job_keepalive(job_t j)
        struct stat sb;
        bool good_exit = (WIFEXITED(j->last_exit_status) && WEXITSTATUS(j->last_exit_status) == 0);
 
+       if (j->mgr->shutting_down) {
+               return false;
+       }
+
        /*
         * 5066316
         *
@@ -3997,22 +4190,37 @@ machservice_status(struct machservice *ms)
 void
 job_setup_exception_port(job_t j, task_t target_task)
 {
+       struct machservice *ms;
        thread_state_flavor_t f = 0;
+       mach_port_t exc_port = the_exception_server;
 
-       if (!the_exception_server) {
+       if (j->alt_exc_handler) {
+               ms = jobmgr_lookup_service(j->mgr, j->alt_exc_handler, true, 0);
+               if (ms) {
+                       exc_port = machservice_port(ms);
+               } else {
+                       job_log(j, LOG_WARNING, "Falling back to default Mach exception handler. Could not find: %s", j->alt_exc_handler);
+               }
+       } else if (j->internal_exc_handler) {
+               exc_port = runtime_get_kernel_port();
+       } else if (!exc_port) {
                return;
        }
 
-#if defined (__ppc__)
+#if defined (__ppc__) || defined (__ppc64__)
        f = PPC_THREAD_STATE64;
-#elif defined(__i386__)
+#elif defined(__i386__) || defined(__x86_64__)
        f = x86_THREAD_STATE;
+#elif defined(__arm__)
+       f = ARM_THREAD_STATE;
+#else
+#error "unknown architecture"
 #endif
 
        if (target_task) {
-               job_assumes(j, task_set_exception_ports(target_task, EXC_MASK_CRASH, the_exception_server,
+               job_assumes(j, task_set_exception_ports(target_task, EXC_MASK_CRASH, exc_port,
                                        EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f) == KERN_SUCCESS);
-       } else if (getpid() == 1) {
+       } else if (getpid() == 1 && the_exception_server) {
                mach_port_t mhp = mach_host_self();
                job_assumes(j, host_set_exception_ports(mhp, EXC_MASK_CRASH, the_exception_server,
                                        EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f) == KERN_SUCCESS);
@@ -4206,9 +4414,14 @@ void
 jobmgr_log_stray_children(jobmgr_t jm)
 {
        int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
-       size_t i, kp_cnt, len = 10*1024*1024;
+       size_t i, kp_cnt, len = sizeof(struct kinfo_proc) * get_kern_max_proc();
        struct kinfo_proc *kp;
 
+#if TARGET_OS_EMBEDDED
+       if (!do_apple_internal_magic) {
+               return;
+       }
+#endif
        if (jm->parentmgr || getpid() != 1) {
                return;
        }
@@ -4285,7 +4498,7 @@ jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bo
        }
 
        jmr->kqjobmgr_callback = jobmgr_callback;
-       strcpy(jmr->name, name ? name : "Under construction");
+       strcpy(jmr->name_init, name ? name : "Under construction");
 
        jmr->req_port = requestorport;
 
@@ -4333,7 +4546,7 @@ jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bo
        }
 
        if (!name) {
-               sprintf(jmr->name, "%u", MACH_PORT_INDEX(jmr->jm_port));
+               sprintf(jmr->name_init, "%u", MACH_PORT_INDEX(jmr->jm_port));
        }
 
        /* Sigh... at the moment, MIG has maxsize == sizeof(reply union) */
@@ -4773,7 +4986,7 @@ semaphoreitem_new(job_t j, semaphore_reason_t why, const char *what)
        si->why = why;
 
        if (what) {
-               strcpy(si->what, what);
+               strcpy(si->what_init, what);
        }
 
        SLIST_INSERT_HEAD(&j->semaphores, si, sle);
@@ -5074,10 +5287,18 @@ job_mig_create_server(job_t j, cmd_t server_cmd, uid_t server_uid, boolean_t on_
        struct ldcred ldc;
        job_t js;
 
+#if TARGET_OS_EMBEDDED
+       return BOOTSTRAP_NOT_PRIVILEGED;
+#endif
+
        if (!launchd_assumes(j != NULL)) {
                return BOOTSTRAP_NO_MEMORY;
        }
 
+       if (unlikely(j->deny_job_creation)) {
+               return BOOTSTRAP_NOT_PRIVILEGED;
+       }
+
        runtime_get_caller_creds(&ldc);
 
        job_log(j, LOG_DEBUG, "Server create attempt: %s", server_cmd);
@@ -5558,6 +5779,10 @@ job_mig_lookup_per_user_context(job_t j, uid_t which_user, mach_port_t *up_cont)
        struct ldcred ldc;
        job_t ji;
 
+#if TARGET_OS_EMBEDDED
+       return BOOTSTRAP_NOT_PRIVILEGED;
+#endif
+
        if (!launchd_assumes(j != NULL)) {
                return BOOTSTRAP_NO_MEMORY;
        }
@@ -5696,6 +5921,8 @@ job_mig_register2(job_t j, name_t servicename, mach_port_t serviceport, uint64_t
 
        job_log(j, LOG_DEBUG, "%sMach service registration attempt: %s", flags & BOOTSTRAP_PER_PID_SERVICE ? "Per PID " : "", servicename);
 
+       /* 5641783 for the embedded hack */
+#if !TARGET_OS_EMBEDDED
        /*
         * From a per-user/session launchd's perspective, SecurityAgent (UID
         * 92) is a rogue application (not our UID, not root and not a child of
@@ -5708,6 +5935,7 @@ job_mig_register2(job_t j, name_t servicename, mach_port_t serviceport, uint64_t
                        return BOOTSTRAP_NOT_PRIVILEGED;
                }
        }
+#endif
        
        ms = jobmgr_lookup_service(j->mgr, servicename, false, flags & BOOTSTRAP_PER_PID_SERVICE ? ldc.pid : 0);
 
@@ -5747,9 +5975,12 @@ job_mig_look_up2(job_t j, name_t servicename, mach_port_t *serviceportp, mach_ms
 
        runtime_get_caller_creds(&ldc);
 
+       /* 5641783 for the embedded hack */
+#if !TARGET_OS_EMBEDDED
        if (getpid() == 1 && j->anonymous && job_get_bs(j)->parentmgr == NULL && ldc.uid != 0 && ldc.euid != 0) {
                return VPROC_ERR_TRY_PER_USER;
        }
+#endif
 
        if (!mspolicy_check(j, servicename, flags & BOOTSTRAP_PER_PID_SERVICE)) {
                job_log(j, LOG_NOTICE, "Policy denied Mach service lookup: %s", servicename);
@@ -5762,7 +5993,7 @@ job_mig_look_up2(job_t j, name_t servicename, mach_port_t *serviceportp, mach_ms
                ms = jobmgr_lookup_service(j->mgr, servicename, true, 0);
        }
 
-       if (ms && machservice_hidden(ms) && !job_active(machservice_job(ms))) {
+       if (ms && machservice_hidden(ms) && !machservice_active(ms)) {
                ms = NULL;
        } else if (ms && ms->per_user_hack) {
                ms = NULL;
@@ -5838,6 +6069,10 @@ job_mig_info(job_t j, name_array_t *servicenamesp, unsigned int *servicenames_cn
        jobmgr_t jm;
        job_t ji;
 
+#if TARGET_OS_EMBEDDED
+       return BOOTSTRAP_NOT_PRIVILEGED;
+#endif
+
        if (!launchd_assumes(j != NULL)) {
                return BOOTSTRAP_NO_MEMORY;
        }
@@ -5960,6 +6195,10 @@ job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type)
        struct ldcred ldc;
        jobmgr_t jmr = NULL;
 
+#if TARGET_OS_EMBEDDED
+       return BOOTSTRAP_NOT_PRIVILEGED;
+#endif
+
        if (!launchd_assumes(j != NULL)) {
                return BOOTSTRAP_NO_MEMORY;
        }
@@ -6042,7 +6281,7 @@ job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type)
                }
 
                jobmgr_log(j->mgr, LOG_DEBUG, "Renaming to: %s", session_type);
-               strcpy(j->mgr->name, session_type);
+               strcpy(j->mgr->name_init, session_type);
 
                if (job_assumes(j, (j2 = jobmgr_init_session(j->mgr, session_type, false)))) {
                        job_assumes(j, job_dispatch(j2, true));
@@ -6059,7 +6298,7 @@ job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type)
 
        job_log(j, LOG_DEBUG, "Move subset attempt: 0x%x", target_subset);
 
-       kr = _vproc_grab_subset(target_subset, &reqport, &rcvright, &out_obj_array, &l2l_ports, &l2l_port_cnt);
+       errno = kr = _vproc_grab_subset(target_subset, &reqport, &rcvright, &out_obj_array, &l2l_ports, &l2l_port_cnt);
 
        if (!job_assumes(j, kr == 0)) {
                goto out;
@@ -6136,6 +6375,10 @@ job_mig_take_subset(job_t j, mach_port_t *reqport, mach_port_t *rcvright,
        jobmgr_t jm;
        job_t ji;
 
+#if TARGET_OS_EMBEDDED
+       return BOOTSTRAP_NOT_PRIVILEGED;
+#endif
+
        if (!launchd_assumes(j != NULL)) {
                return BOOTSTRAP_NO_MEMORY;
        }
@@ -6333,6 +6576,66 @@ out_bad:
        return BOOTSTRAP_NO_MEMORY;
 }
 
+kern_return_t
+job_mig_embedded_wait(job_t j, name_t targetlabel, integer_t *waitstatus)
+{
+       job_t otherj;
+
+       if (!launchd_assumes(j != NULL)) {
+               return BOOTSTRAP_NO_MEMORY;
+       }
+
+       if (unlikely(!(otherj = job_find(targetlabel)))) {
+               return BOOTSTRAP_UNKNOWN_SERVICE;
+       }
+
+       *waitstatus = j->last_exit_status;
+
+       return 0;
+}
+
+kern_return_t
+job_mig_embedded_kickstart(job_t j, name_t targetlabel, pid_t *out_pid, mach_port_t *out_name_port)
+{
+       struct ldcred ldc;
+       kern_return_t kr;
+       job_t otherj;
+
+       if (!launchd_assumes(j != NULL)) {
+               return BOOTSTRAP_NO_MEMORY;
+       }
+
+       if (unlikely(!(otherj = job_find(targetlabel)))) {
+               return BOOTSTRAP_UNKNOWN_SERVICE;
+       }
+
+       runtime_get_caller_creds(&ldc);
+
+       if (ldc.euid != 0 && ldc.euid != geteuid()
+#if TARGET_OS_EMBEDDED
+                       && j->username && otherj->username
+                       && strcmp(j->username, otherj->username) != 0
+#endif
+                       ) {
+               return BOOTSTRAP_NOT_PRIVILEGED;
+       }
+
+       otherj = job_dispatch(otherj, true);
+
+       if (!job_assumes(j, otherj && otherj->p)) {
+               return BOOTSTRAP_NO_MEMORY;
+       }
+
+       kr = task_name_for_pid(mach_task_self(), otherj->p, out_name_port);
+       if (!job_assumes(j, kr == 0)) {
+               return kr;
+       }
+
+       *out_pid = otherj->p;
+
+       return 0;
+}
+
 kern_return_t
 job_mig_wait(job_t j, mach_port_t srp, integer_t *waitstatus)
 {
@@ -6366,12 +6669,41 @@ job_mig_uncork_fork(job_t j)
 kern_return_t
 job_mig_set_service_policy(job_t j, pid_t target_pid, uint64_t flags, name_t target_service)
 {
+       struct ldcred ldc;
        job_t target_j;
 
        if (!launchd_assumes(j != NULL)) {
                return BOOTSTRAP_NO_MEMORY;
        }
 
+       runtime_get_caller_creds(&ldc);
+
+#if TARGET_OS_EMBEDDED
+       if( ldc.euid ) {
+               return BOOTSTRAP_NOT_PRIVILEGED;
+       }
+#else
+       if( ldc.euid && (ldc.euid != getuid()) ) {
+               int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, target_pid };
+               struct kinfo_proc kp;
+               size_t len = sizeof(kp);
+
+               job_assumes(j, sysctl(mib, 4, &kp, &len, NULL, 0) != -1);
+               job_assumes(j, len == sizeof(kp));
+
+               uid_t kp_euid = kp.kp_eproc.e_ucred.cr_uid;
+               uid_t kp_uid = kp.kp_eproc.e_pcred.p_ruid;
+
+               if( ldc.euid == kp_euid ) {
+                       job_log(j, LOG_DEBUG, "Working around rdar://problem/5982485 and allowing job to set policy for PID %u.", target_pid);
+               } else {
+                       job_log(j, LOG_ERR, "Denied Mach service policy update requested by UID/EUID %u/%u against PID %u with UID/EUID %u/%u due to mismatched credentials.", ldc.uid, ldc.euid, target_pid, kp_uid, kp_euid);
+
+                       return BOOTSTRAP_NOT_PRIVILEGED;
+               }
+       }
+#endif
+
        if (!job_assumes(j, (target_j = jobmgr_find_by_pid(j->mgr, target_pid, true)) != NULL)) {
                return BOOTSTRAP_NO_MEMORY;
        }
@@ -6382,6 +6714,7 @@ job_mig_set_service_policy(job_t j, pid_t target_pid, uint64_t flags, name_t tar
                        job_assumes(j, mspolicy_new(target_j, target_service, flags & BOOTSTRAP_ALLOW_LOOKUP, flags & BOOTSTRAP_PER_PID_SERVICE, false));
                } else {
                        target_j->deny_unknown_mslookups = !(flags & BOOTSTRAP_ALLOW_LOOKUP);
+                       target_j->deny_job_creation = (bool)(flags & BOOTSTRAP_DENY_JOB_CREATION);
                }
        } else {
                job_log(j, LOG_WARNING, "Jobs that have policies assigned to them may not set policies.");
@@ -6399,12 +6732,20 @@ job_mig_spawn(job_t j, vm_offset_t indata, mach_msg_type_number_t indataCnt, pid
        struct ldcred ldc;
        job_t jr;
 
+#if TARGET_OS_EMBEDDED
+       return BOOTSTRAP_NOT_PRIVILEGED;
+#endif
+
        runtime_get_caller_creds(&ldc);
 
        if (!launchd_assumes(j != NULL)) {
                return BOOTSTRAP_NO_MEMORY;
        }
 
+       if (unlikely(j->deny_job_creation)) {
+               return BOOTSTRAP_NOT_PRIVILEGED;
+       }
+
        if (getpid() == 1 && ldc.euid && ldc.uid) {
                job_log(j, LOG_DEBUG, "Punting spawn to per-user-context");
                return VPROC_ERR_TRY_PER_USER;
@@ -6447,14 +6788,17 @@ job_mig_spawn(job_t j, vm_offset_t indata, mach_msg_type_number_t indataCnt, pid
                return BOOTSTRAP_NO_MEMORY;
        }
 
-       job_assumes(jr, jr->p);
+       if (!job_assumes(jr, jr->p)) {
+               job_remove(jr);
+               return BOOTSTRAP_NO_MEMORY;
+       }
 
        if (!job_setup_machport(jr)) {
                job_remove(jr);
                return BOOTSTRAP_NO_MEMORY;
        }
 
-       job_log(j, LOG_INFO, "Spawned");
+       job_log(jr, LOG_DEBUG, "Spawned by PID %u: %s", j->p, j->label);
 
        *child_pid = jr->p;
        *obsvr_port = jr->j_port;
@@ -6604,9 +6948,26 @@ waiting4removal_delete(job_t j, struct waiting_for_removal *w4r)
        free(w4r);
 }
 
+size_t
+get_kern_max_proc(void)
+{
+       int mib[] = { CTL_KERN, KERN_MAXPROC };
+       int max = 100;
+       size_t max_sz = sizeof(max);
+
+       launchd_assumes(sysctl(mib, 2, &max, &max_sz, NULL, 0) != -1);
+
+       return max;
+}
+
 void
 do_file_init(void)
 {
+       struct stat sb;
+
        launchd_assert(mach_timebase_info(&tbi) == 0);
 
+       if (stat("/AppleInternal", &sb) == 0) {
+               do_apple_internal_magic = true;
+       }
 }