+ kern_return_t kr = BOOTSTRAP_NO_MEMORY;
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ struct ldcred *ldc = runtime_get_caller_creds();
+
+ /* Only allow root processes to look up children, even if we're in the per-user launchd.
+ * Otherwise, this could be used to cross sessions, which counts as a security vulnerability
+ * in a non-flat namespace.
+ */
+ if (ldc->euid != 0) {
+ job_log(j, LOG_WARNING, "Attempt to look up children of bootstrap by unprivileged job.");
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ unsigned int cnt = 0;
+
+ jobmgr_t jmr = j->mgr;
+ jobmgr_t jmi = NULL;
+ SLIST_FOREACH(jmi, &jmr->submgrs, sle) {
+ cnt++;
+ }
+
+ /* Find our per-user launchds if we're PID 1. */
+ job_t ji = NULL;
+ if (pid1_magic) {
+ LIST_FOREACH(ji, &jmr->jobs, sle) {
+ cnt += ji->per_user ? 1 : 0;
+ }
+ }
+
+ if (cnt == 0) {
+ return BOOTSTRAP_NO_CHILDREN;
+ }
+
+ mach_port_array_t _child_ports = NULL;
+ mig_allocate((vm_address_t *)&_child_ports, cnt * sizeof(_child_ports[0]));
+ if (!job_assumes(j, _child_ports != NULL)) {
+ kr = BOOTSTRAP_NO_MEMORY;
+ goto out_bad;
+ }
+
+ name_array_t _child_names = NULL;
+ mig_allocate((vm_address_t *)&_child_names, cnt * sizeof(_child_names[0]));
+ if (!job_assumes(j, _child_names != NULL)) {
+ kr = BOOTSTRAP_NO_MEMORY;
+ goto out_bad;
+ }
+
+ bootstrap_property_array_t _child_properties = NULL;
+ mig_allocate((vm_address_t *)&_child_properties, cnt * sizeof(_child_properties[0]));
+ if (!job_assumes(j, _child_properties != NULL)) {
+ kr = BOOTSTRAP_NO_MEMORY;
+ goto out_bad;
+ }
+
+ unsigned int cnt2 = 0;
+ SLIST_FOREACH(jmi, &jmr->submgrs, sle) {
+ if (jobmgr_assumes(jmi, launchd_mport_make_send(jmi->jm_port) == KERN_SUCCESS)) {
+ _child_ports[cnt2] = jmi->jm_port;
+ } else {
+ _child_ports[cnt2] = MACH_PORT_NULL;
+ }
+
+ strlcpy(_child_names[cnt2], jmi->name, sizeof(_child_names[0]));
+ _child_properties[cnt2] = jmi->properties;
+
+ cnt2++;
+ }
+
+ if (pid1_magic) LIST_FOREACH( ji, &jmr->jobs, sle) {
+ if (ji->per_user) {
+ if (job_assumes(ji, SLIST_FIRST(&ji->machservices)->per_user_hack == true)) {
+ mach_port_t port = machservice_port(SLIST_FIRST(&ji->machservices));
+
+ if (job_assumes(ji, launchd_mport_copy_send(port) == KERN_SUCCESS)) {
+ _child_ports[cnt2] = port;
+ } else {
+ _child_ports[cnt2] = MACH_PORT_NULL;
+ }
+ } else {
+ _child_ports[cnt2] = MACH_PORT_NULL;
+ }
+
+ strlcpy(_child_names[cnt2], ji->label, sizeof(_child_names[0]));
+ _child_properties[cnt2] |= BOOTSTRAP_PROPERTY_PERUSER;
+
+ cnt2++;
+ }
+ }
+
+ *child_names_cnt = cnt;
+ *child_ports_cnt = cnt;
+ *child_properties_cnt = cnt;
+
+ *child_names = _child_names;
+ *child_ports = _child_ports;
+ *child_properties = _child_properties;
+
+ unsigned int i = 0;
+ for (i = 0; i < cnt; i++) {
+ job_log(j, LOG_DEBUG, "child_names[%u] = %s", i, (char *)_child_names[i]);
+ }
+
+ return BOOTSTRAP_SUCCESS;
+out_bad:
+ if (_child_ports) {
+ mig_deallocate((vm_address_t)_child_ports, cnt * sizeof(_child_ports[0]));
+ }
+
+ if (_child_names) {
+ mig_deallocate((vm_address_t)_child_names, cnt * sizeof(_child_ports[0]));
+ }
+
+ if (_child_properties) {
+ mig_deallocate((vm_address_t)_child_properties, cnt * sizeof(_child_properties[0]));
+ }
+
+ return kr;
+}
+
+kern_return_t
+job_mig_transaction_count_for_pid(job_t j, pid_t p, int32_t *cnt, boolean_t *condemned)
+{
+ kern_return_t kr = KERN_FAILURE;
+ struct ldcred *ldc = runtime_get_caller_creds();
+ if ((ldc->euid != geteuid()) && (ldc->euid != 0)) {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ job_t j_for_pid = jobmgr_find_by_pid_deep(j->mgr, p, false);
+ if (j_for_pid) {
+ if (j_for_pid->kill_via_shmem) {
+ if (j_for_pid->shmem) {
+ *cnt = j_for_pid->shmem->vp_shmem_transaction_cnt;
+ *condemned = j_for_pid->shmem->vp_shmem_flags & VPROC_SHMEM_EXITING;
+ *cnt += *condemned ? 1 : 0;
+ } else {
+ *cnt = 0;
+ *condemned = false;
+ }
+
+ kr = BOOTSTRAP_SUCCESS;
+ } else {
+ kr = BOOTSTRAP_NO_MEMORY;
+ }
+ } else {
+ kr = BOOTSTRAP_UNKNOWN_SERVICE;
+ }
+
+ return kr;
+}
+
+kern_return_t
+job_mig_pid_is_managed(job_t j __attribute__((unused)), pid_t p, boolean_t *managed)
+{
+ struct ldcred *ldc = runtime_get_caller_creds();
+ if ((ldc->euid != geteuid()) && (ldc->euid != 0)) {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ /* This is so loginwindow doesn't try to quit GUI apps that have been launched
+ * directly by launchd as agents.
+ */
+ job_t j_for_pid = jobmgr_find_by_pid_deep(root_jobmgr, p, false);
+ if (j_for_pid && !j_for_pid->anonymous && !j_for_pid->legacy_LS_job) {
+ *managed = true;
+ }
+
+ return BOOTSTRAP_SUCCESS;
+}
+
+kern_return_t
+job_mig_port_for_label(job_t j __attribute__((unused)), name_t label, mach_port_t *mp)
+{
+ struct ldcred *ldc = runtime_get_caller_creds();
+ kern_return_t kr = BOOTSTRAP_NOT_PRIVILEGED;
+
+#if HAVE_SANDBOX
+ if (unlikely(sandbox_check(ldc->pid, "job-creation", SANDBOX_FILTER_NONE) > 0)) {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+#endif
+
+ mach_port_t _mp = MACH_PORT_NULL;
+ if (!j->deny_job_creation && (ldc->euid == 0 || ldc->euid == geteuid())) {
+ job_t target_j = job_find(NULL, label);
+ if (jobmgr_assumes(root_jobmgr, target_j != NULL)) {
+ if (target_j->j_port == MACH_PORT_NULL) {
+ (void)job_assumes(target_j, job_setup_machport(target_j) == true);
+ }
+
+ _mp = target_j->j_port;
+ kr = _mp != MACH_PORT_NULL ? BOOTSTRAP_SUCCESS : BOOTSTRAP_NO_MEMORY;
+ } else {
+ kr = BOOTSTRAP_NO_MEMORY;
+ }
+ }
+
+ *mp = _mp;
+ return kr;
+}
+
+#if !TARGET_OS_EMBEDDED
+kern_return_t
+job_mig_set_security_session(job_t j, uuid_t uuid, mach_port_t asport)
+{
+ uuid_string_t uuid_str;
+ uuid_unparse(uuid, uuid_str);
+ job_log(j, LOG_DEBUG, "Setting session %u for UUID %s...", asport, uuid_str);
+
+ job_t ji = NULL, jt = NULL;
+ LIST_FOREACH_SAFE(ji, &s_needing_sessions, sle, jt) {
+ uuid_string_t uuid_str2;
+ uuid_unparse(ji->expected_audit_uuid, uuid_str2);
+
+ if (uuid_compare(uuid, ji->expected_audit_uuid) == 0) {
+ uuid_clear(ji->expected_audit_uuid);
+ if (asport != MACH_PORT_NULL ) {
+ job_log(ji, LOG_DEBUG, "Job should join session with port %u", asport);
+ (void)job_assumes(j, launchd_mport_copy_send(asport) == KERN_SUCCESS);
+ } else {
+ job_log(ji, LOG_DEBUG, "No session to set for job. Using our session.");
+ }
+
+ ji->asport = asport;
+ LIST_REMOVE(ji, needing_session_sle);
+ job_dispatch(ji, false);
+ }
+ }
+
+ /* Each job that the session port was set for holds a reference. At the end of
+ * the loop, there will be one extra reference belonging to this MiG protocol.
+ * We need to release it so that the session goes away when all the jobs
+ * referencing it are unloaded.
+ */
+ (void)job_assumes(j, launchd_mport_deallocate(asport) == KERN_SUCCESS);
+
+ return KERN_SUCCESS;
+}
+#else
+kern_return_t
+job_mig_set_security_session(job_t j __attribute__((unused)), uuid_t uuid __attribute__((unused)), mach_port_t session __attribute__((unused)))
+{
+ return KERN_SUCCESS;
+}
+#endif