1 /*
2 * @APPLE_APACHE_LICENSE_HEADER_START@
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * @APPLE_APACHE_LICENSE_HEADER_END@
17 */
18
19 #define __APPLE_API_PRIVATE
20 #define PRIVATE 1
21
22 #include "config.h"
23 #include "core.h"
24
25 #include "internal.h"
26 #include "helper.h"
27
28 #include <TargetConditionals.h>
29 #include <mach/mach.h>
30 #include <mach/mach_error.h>
31 #include <mach/boolean.h>
32 #include <mach/message.h>
33 #include <mach/notify.h>
34 #include <mach/mig_errors.h>
35 #include <mach/mach_traps.h>
36 #include <mach/mach_interface.h>
37 #include <mach/host_info.h>
38 #include <mach/mach_host.h>
39 #include <mach/exception.h>
40 #include <mach/host_reboot.h>
41 #include <sys/types.h>
42 #include <sys/queue.h>
43 #include <sys/endian.h>
44 #include <sys/event.h>
45 #include <sys/stat.h>
46 #include <sys/ucred.h>
47 #include <sys/fcntl.h>
48 #include <sys/un.h>
49 #include <sys/reboot.h>
50 #include <sys/wait.h>
51 #include <sys/sysctl.h>
52 #include <sys/sockio.h>
53 #include <sys/time.h>
54 #include <sys/resource.h>
55 #include <sys/ioctl.h>
56 #include <sys/mount.h>
57 #if 0
58 #include <sys/pipe.h>
59 #endif
60 #include <sys/mman.h>
61 #include <sys/proc.h>
62 #include <sys/socket.h>
63 #include <sys/syscall.h>
64 #include <sys/kern_memorystatus.h>
65 #include <net/if.h>
66 #include <net/if_var.h>
67 #include <netinet/in.h>
68 #include <netinet/in_var.h>
69 #include <netinet6/nd6.h>
70 #include <bsm/libbsm.h>
71 #include <unistd.h>
72 #include <signal.h>
73 #include <errno.h>
74 #include <libgen.h>
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <stdarg.h>
78 #include <stdbool.h>
79 #include <paths.h>
80 #include <pwd.h>
81 #include <grp.h>
82 #include <ttyent.h>
83 #include <dlfcn.h>
84 #include <dirent.h>
85 #include <string.h>
86 #include <ctype.h>
87 #include <glob.h>
88 #include <spawn.h>
89 #include <spawn_private.h>
90 #include <sys/spawn_internal.h>
91 #include <System/sys/spawn.h>
92 #include <System/sys/spawn_internal.h>
93
94 #include <spawn_private.h>
95 #include <time.h>
96 #include <libinfo.h>
97 #include <os/assumes.h>
98 #include <xpc/launchd.h>
99 #include <asl.h>
100 #include <_simple.h>
101 #include <mach/mach_vm.h>
102
103 #include <libproc.h>
104 #include <libproc_internal.h>
105 #include <System/sys/proc_info.h>
106
107 #include <pthread.h>
108 #if HAVE_SANDBOX
109 #define __APPLE_API_PRIVATE
110 #include <sandbox.h>
111 #endif
112 #if HAVE_QUARANTINE
113 #include <quarantine.h>
114 #endif
115 #if HAVE_RESPONSIBILITY
116 #include <responsibility.h>
117 #endif
118
119 #if !TARGET_OS_EMBEDDED
120 extern int gL1CacheEnabled;
121 #endif
122
123 #if HAVE_SYSTEMSTATS
124 #include <systemstats/systemstats.h>
125 #endif
126
127 #include "launch.h"
128 #include "launch_priv.h"
129 #include "launch_internal.h"
130 #include "bootstrap.h"
131 #include "bootstrap_priv.h"
132 #include "vproc.h"
133 #include "vproc_internal.h"
134
135 #include "reboot2.h"
136
137 #include "launchd.h"
138 #include "runtime.h"
139 #include "ipc.h"
140 #include "job.h"
141
142 #include "jobServer.h"
143 #include "job_reply.h"
144 #include "job_forward.h"
145
146 #include "mach_excServer.h"
147
148 #include "shim.h"
149
150
151 #define RETURN_NO_MEMORY() \
152 do { \
153 if (uflag) \
154 printf("launchd failed on line: %d\n", __LINE__); \
155 return BOOTSTRAP_NO_MEMORY; \
156 } while (0)
157
158 #define POSIX_SPAWN_IOS_INTERACTIVE 0
159
160 #if TARGET_OS_EMBEDDED
161 /* Default memory highwatermark for daemons as set out in <rdar://problem/10307788>. */
162 #define DEFAULT_JETSAM_DAEMON_HIGHWATERMARK 5
163 #endif
164
165 /* LAUNCHD_DEFAULT_EXIT_TIMEOUT
166 * If the job hasn't exited in the given number of seconds after sending
167 * it a SIGTERM, SIGKILL it. Can be overriden in the job plist.
168 */
169 #define LAUNCHD_MIN_JOB_RUN_TIME 10
170 #define LAUNCHD_DEFAULT_EXIT_TIMEOUT 20
171 #define LAUNCHD_SIGKILL_TIMER 30
172 #define LAUNCHD_LOG_FAILED_EXEC_FREQ 10
173
174 #define SHUTDOWN_LOG_DIR "/var/log/shutdown"
175
176 #define TAKE_SUBSET_NAME "TakeSubsetName"
177 #define TAKE_SUBSET_PID "TakeSubsetPID"
178 #define TAKE_SUBSET_PERPID "TakeSubsetPerPID"
179
180 #define IS_POWER_OF_TWO(v) (!(v & (v - 1)) && v)
181
182 extern char **environ;
183 extern bool uflag;
184
185 struct waiting_for_removal {
186 SLIST_ENTRY(waiting_for_removal) sle;
187 mach_port_t reply_port;
188 };
189
190 static bool waiting4removal_new(job_t j, mach_port_t rp);
191 static void waiting4removal_delete(job_t j, struct waiting_for_removal *w4r);
192
193 struct machservice {
194 SLIST_ENTRY(machservice) sle;
195 SLIST_ENTRY(machservice) special_port_sle;
196 LIST_ENTRY(machservice) name_hash_sle;
197 LIST_ENTRY(machservice) port_hash_sle;
198 struct machservice *alias;
199 job_t job;
200 unsigned int gen_num;
201 mach_port_name_t port;
202 unsigned int
203 isActive:1,
204 reset:1,
205 recv:1,
206 hide:1,
207 kUNCServer:1,
208 per_user_hack:1,
209 debug_on_close:1,
210 per_pid:1,
211 delete_on_destruction:1,
212 drain_one_on_crash:1,
213 drain_all_on_crash:1,
214 upfront:1,
215 event_channel:1,
216 recv_race_hack :1,
217 /* Don't let the size of this field to get too small. It has to be large
218 * enough to represent the reasonable range of special port numbers.
219 */
220 special_port_num:17;
221 const char name[0];
222 };
223
224 // HACK: This should be per jobmgr_t
225 static SLIST_HEAD(, machservice) special_ports;
226
227 #define PORT_HASH_SIZE 32
228 #define HASH_PORT(x) (IS_POWER_OF_TWO(PORT_HASH_SIZE) ? (MACH_PORT_INDEX(x) & (PORT_HASH_SIZE - 1)) : (MACH_PORT_INDEX(x) % PORT_HASH_SIZE))
229
230 static LIST_HEAD(, machservice) port_hash[PORT_HASH_SIZE];
231
232 static void machservice_setup(launch_data_t obj, const char *key, void *context);
233 static void machservice_setup_options(launch_data_t obj, const char *key, void *context);
234 static void machservice_resetport(job_t j, struct machservice *ms);
235 static void machservice_stamp_port(job_t j, struct machservice *ms);
236 static struct machservice *machservice_new(job_t j, const char *name, mach_port_t *serviceport, bool pid_local);
237 static struct machservice *machservice_new_alias(job_t aj, struct machservice *orig);
238 static void machservice_ignore(job_t j, struct machservice *ms);
239 static void machservice_watch(job_t j, struct machservice *ms);
240 static void machservice_delete(job_t j, struct machservice *, bool port_died);
241 static void machservice_request_notifications(struct machservice *);
242 static mach_port_t machservice_port(struct machservice *);
243 static job_t machservice_job(struct machservice *);
244 static bool machservice_hidden(struct machservice *);
245 static bool machservice_active(struct machservice *);
246 static const char *machservice_name(struct machservice *);
247 static bootstrap_status_t machservice_status(struct machservice *);
248 void machservice_drain_port(struct machservice *);
249
250 struct socketgroup {
251 SLIST_ENTRY(socketgroup) sle;
252 int *fds;
253 unsigned int fd_cnt;
254 union {
255 const char name[0];
256 char name_init[0];
257 };
258 };
259
260 static bool socketgroup_new(job_t j, const char *name, int *fds, size_t fd_cnt);
261 static void socketgroup_delete(job_t j, struct socketgroup *sg);
262 static void socketgroup_watch(job_t j, struct socketgroup *sg);
263 static void socketgroup_ignore(job_t j, struct socketgroup *sg);
264 static void socketgroup_callback(job_t j);
265 static void socketgroup_setup(launch_data_t obj, const char *key, void *context);
266 static void socketgroup_kevent_mod(job_t j, struct socketgroup *sg, bool do_add);
267
268 struct calendarinterval {
269 LIST_ENTRY(calendarinterval) global_sle;
270 SLIST_ENTRY(calendarinterval) sle;
271 job_t job;
272 struct tm when;
273 time_t when_next;
274 };
275
276 static LIST_HEAD(, calendarinterval) sorted_calendar_events;
277
278 static bool calendarinterval_new(job_t j, struct tm *w);
279 static bool calendarinterval_new_from_obj(job_t j, launch_data_t obj);
280 static void calendarinterval_new_from_obj_dict_walk(launch_data_t obj, const char *key, void *context);
281 static void calendarinterval_delete(job_t j, struct calendarinterval *ci);
282 static void calendarinterval_setalarm(job_t j, struct calendarinterval *ci);
283 static void calendarinterval_callback(void);
284 static void calendarinterval_sanity_check(void);
285
286 struct envitem {
287 SLIST_ENTRY(envitem) sle;
288 char *value;
289 union {
290 const char key[0];
291 char key_init[0];
292 };
293 };
294
295 static bool envitem_new(job_t j, const char *k, const char *v, bool global);
296 static void envitem_delete(job_t j, struct envitem *ei, bool global);
297 static void envitem_setup(launch_data_t obj, const char *key, void *context);
298
299 struct limititem {
300 SLIST_ENTRY(limititem) sle;
301 struct rlimit lim;
302 unsigned int setsoft:1, sethard:1, which:30;
303 };
304
305 static bool limititem_update(job_t j, int w, rlim_t r);
306 static void limititem_delete(job_t j, struct limititem *li);
307 static void limititem_setup(launch_data_t obj, const char *key, void *context);
308 #if HAVE_SANDBOX
309 static void seatbelt_setup_flags(launch_data_t obj, const char *key, void *context);
310 #endif
311
312 static void jetsam_property_setup(launch_data_t obj, const char *key, job_t j);
313
314 typedef enum {
315 NETWORK_UP = 1,
316 NETWORK_DOWN,
317 SUCCESSFUL_EXIT,
318 FAILED_EXIT,
319 CRASHED,
320 DID_NOT_CRASH,
321 OTHER_JOB_ENABLED,
322 OTHER_JOB_DISABLED,
323 OTHER_JOB_ACTIVE,
324 OTHER_JOB_INACTIVE,
325 } semaphore_reason_t;
326
327 struct semaphoreitem {
328 SLIST_ENTRY(semaphoreitem) sle;
329 semaphore_reason_t why;
330
331 union {
332 const char what[0];
333 char what_init[0];
334 };
335 };
336
337 struct semaphoreitem_dict_iter_context {
338 job_t j;
339 semaphore_reason_t why_true;
340 semaphore_reason_t why_false;
341 };
342
343 static bool semaphoreitem_new(job_t j, semaphore_reason_t why, const char *what);
344 static void semaphoreitem_delete(job_t j, struct semaphoreitem *si);
345 static void semaphoreitem_setup(launch_data_t obj, const char *key, void *context);
346 static void semaphoreitem_setup_dict_iter(launch_data_t obj, const char *key, void *context);
347 static void semaphoreitem_runtime_mod_ref(struct semaphoreitem *si, bool add);
348
349 struct externalevent {
350 LIST_ENTRY(externalevent) sys_le;
351 LIST_ENTRY(externalevent) job_le;
352 struct eventsystem *sys;
353
354 uint64_t id;
355 job_t job;
356 bool state;
357 bool wanted_state;
358 bool internal;
359 xpc_object_t event;
360 xpc_object_t entitlements;
361
362 char name[0];
363 };
364
365 struct externalevent_iter_ctx {
366 job_t j;
367 struct eventsystem *sys;
368 };
369
370 static bool externalevent_new(job_t j, struct eventsystem *sys, const char *evname, xpc_object_t event, uint64_t flags);
371 static void externalevent_delete(struct externalevent *ee);
372 static void externalevent_setup(launch_data_t obj, const char *key, void *context);
373 static struct externalevent *externalevent_find(const char *sysname, uint64_t id);
374
375 struct eventsystem {
376 LIST_ENTRY(eventsystem) global_le;
377 LIST_HEAD(, externalevent) events;
378 uint64_t curid;
379 char name[0];
380 };
381
382 static struct eventsystem *eventsystem_new(const char *name);
383 static void eventsystem_delete(struct eventsystem *sys) __attribute__((unused));
384 static void eventsystem_setup(launch_data_t obj, const char *key, void *context);
385 static struct eventsystem *eventsystem_find(const char *name);
386 static void eventsystem_ping(void);
387
388 struct waiting4attach {
389 LIST_ENTRY(waiting4attach) le;
390 mach_port_t port;
391 pid_t dest;
392 xpc_service_type_t type;
393 char name[0];
394 };
395
396 static LIST_HEAD(, waiting4attach) _launchd_domain_waiters;
397
398 static struct waiting4attach *waiting4attach_new(jobmgr_t jm, const char *name, mach_port_t port, pid_t dest, xpc_service_type_t type);
399 static void waiting4attach_delete(jobmgr_t jm, struct waiting4attach *w4a);
400 static struct waiting4attach *waiting4attach_find(jobmgr_t jm, job_t j);
401
402 #define ACTIVE_JOB_HASH_SIZE 32
403 #define ACTIVE_JOB_HASH(x) (IS_POWER_OF_TWO(ACTIVE_JOB_HASH_SIZE) ? (x & (ACTIVE_JOB_HASH_SIZE - 1)) : (x % ACTIVE_JOB_HASH_SIZE))
404
405 #define MACHSERVICE_HASH_SIZE 37
406
407 #define LABEL_HASH_SIZE 53
408 struct jobmgr_s {
409 kq_callback kqjobmgr_callback;
410 LIST_ENTRY(jobmgr_s) xpc_le;
411 SLIST_ENTRY(jobmgr_s) sle;
412 SLIST_HEAD(, jobmgr_s) submgrs;
413 LIST_HEAD(, job_s) jobs;
414 LIST_HEAD(, waiting4attach) attaches;
415
416 /* For legacy reasons, we keep all job labels that are imported in the root
417 * job manager's label hash. If a job manager is an XPC domain, then it gets
418 * its own label hash that is separate from the "global" one stored in the
419 * root job manager.
420 */
421 LIST_HEAD(, job_s) label_hash[LABEL_HASH_SIZE];
422 LIST_HEAD(, job_s) active_jobs[ACTIVE_JOB_HASH_SIZE];
423 LIST_HEAD(, machservice) ms_hash[MACHSERVICE_HASH_SIZE];
424 LIST_HEAD(, job_s) global_env_jobs;
425 mach_port_t jm_port;
426 mach_port_t req_port;
427 jobmgr_t parentmgr;
428 int reboot_flags;
429 time_t shutdown_time;
430 unsigned int global_on_demand_cnt;
431 unsigned int normal_active_cnt;
432 unsigned int
433 shutting_down:1,
434 session_initialized:1,
435 killed_stray_jobs:1,
436 monitor_shutdown:1,
437 shutdown_jobs_dirtied:1,
438 shutdown_jobs_cleaned:1,
439 xpc_singleton:1;
440 uint32_t properties;
441 // XPC-specific properties.
442 char owner[MAXCOMLEN];
443 const char *shortdesc;
444 mach_port_t req_bsport;
445 mach_port_t req_excport;
446 mach_port_t req_asport;
447 mach_port_t req_gui_asport;
448 pid_t req_pid;
449 uid_t req_euid;
450 gid_t req_egid;
451 au_asid_t req_asid;
452 vm_offset_t req_ctx;
453 mach_msg_type_number_t req_ctx_sz;
454 mach_port_t req_rport;
455 uint64_t req_uniqueid;
456 kern_return_t error;
457 union {
458 const char name[0];
459 char name_init[0];
460 };
461 };
462
463 // Global XPC domains.
464 static jobmgr_t _s_xpc_system_domain;
465 static LIST_HEAD(, jobmgr_s) _s_xpc_user_domains;
466 static LIST_HEAD(, jobmgr_s) _s_xpc_session_domains;
467
468 #define jobmgr_assumes(jm, e) os_assumes_ctx(jobmgr_log_bug, jm, (e))
469 #define jobmgr_assumes_zero(jm, e) os_assumes_zero_ctx(jobmgr_log_bug, jm, (e))
470 #define jobmgr_assumes_zero_p(jm, e) posix_assumes_zero_ctx(jobmgr_log_bug, jm, (e))
471
472 static jobmgr_t jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bool sflag, const char *name, bool no_init, mach_port_t asport);
473 static jobmgr_t jobmgr_new_xpc_singleton_domain(jobmgr_t jm, name_t name);
474 static jobmgr_t jobmgr_find_xpc_per_user_domain(jobmgr_t jm, uid_t uid);
475 static jobmgr_t jobmgr_find_xpc_per_session_domain(jobmgr_t jm, au_asid_t asid);
476 static job_t jobmgr_import2(jobmgr_t jm, launch_data_t pload);
477 static jobmgr_t jobmgr_parent(jobmgr_t jm);
478 static jobmgr_t jobmgr_do_garbage_collection(jobmgr_t jm);
479 static bool jobmgr_label_test(jobmgr_t jm, const char *str);
480 static void jobmgr_reap_bulk(jobmgr_t jm, struct kevent *kev);
481 static void jobmgr_log_stray_children(jobmgr_t jm, bool kill_strays);
482 static void jobmgr_kill_stray_children(jobmgr_t jm, pid_t *p, size_t np);
483 static void jobmgr_remove(jobmgr_t jm);
484 static void jobmgr_dispatch_all(jobmgr_t jm, bool newmounthack);
485 static job_t jobmgr_init_session(jobmgr_t jm, const char *session_type, bool sflag);
486 static job_t jobmgr_find_by_pid_deep(jobmgr_t jm, pid_t p, bool anon_okay);
487 static job_t jobmgr_find_by_pid(jobmgr_t jm, pid_t p, bool create_anon);
488 static job_t managed_job(pid_t p);
489 static jobmgr_t jobmgr_find_by_name(jobmgr_t jm, const char *where);
490 static job_t job_mig_intran2(jobmgr_t jm, mach_port_t mport, pid_t upid);
491 static job_t jobmgr_lookup_per_user_context_internal(job_t j, uid_t which_user, mach_port_t *mp);
492 static void job_export_all2(jobmgr_t jm, launch_data_t where);
493 static void jobmgr_callback(void *obj, struct kevent *kev);
494 static void jobmgr_setup_env_from_other_jobs(jobmgr_t jm);
495 static void jobmgr_export_env_from_other_jobs(jobmgr_t jm, launch_data_t dict);
496 static struct machservice *jobmgr_lookup_service(jobmgr_t jm, const char *name, bool check_parent, pid_t target_pid);
497 static void jobmgr_logv(jobmgr_t jm, int pri, int err, const char *msg, va_list ap) __attribute__((format(printf, 4, 0)));
498 static void jobmgr_log(jobmgr_t jm, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4)));
499 static void jobmgr_log_perf_statistics(jobmgr_t jm, bool signal_children);
500 // static void jobmgr_log_error(jobmgr_t jm, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4)));
501 static bool jobmgr_log_bug(_SIMPLE_STRING asl_message, void *ctx, const char *message);
502
503 #define AUTO_PICK_LEGACY_LABEL (const char *)(~0)
504 #define AUTO_PICK_ANONYMOUS_LABEL (const char *)(~1)
505 #define AUTO_PICK_XPC_LABEL (const char *)(~2)
506
507 struct suspended_peruser {
508 LIST_ENTRY(suspended_peruser) sle;
509 job_t j;
510 };
511
512 struct job_s {
513 // MUST be first element of this structure.
514 kq_callback kqjob_callback;
515 LIST_ENTRY(job_s) sle;
516 LIST_ENTRY(job_s) subjob_sle;
517 LIST_ENTRY(job_s) needing_session_sle;
518 LIST_ENTRY(job_s) jetsam_sle;
519 LIST_ENTRY(job_s) pid_hash_sle;
520 LIST_ENTRY(job_s) global_pid_hash_sle;
521 LIST_ENTRY(job_s) label_hash_sle;
522 LIST_ENTRY(job_s) global_env_sle;
523 SLIST_ENTRY(job_s) curious_jobs_sle;
524 LIST_HEAD(, suspended_peruser) suspended_perusers;
525 LIST_HEAD(, waiting_for_exit) exit_watchers;
526 LIST_HEAD(, job_s) subjobs;
527 LIST_HEAD(, externalevent) events;
528 SLIST_HEAD(, socketgroup) sockets;
529 SLIST_HEAD(, calendarinterval) cal_intervals;
530 SLIST_HEAD(, envitem) global_env;
531 SLIST_HEAD(, envitem) env;
532 SLIST_HEAD(, limititem) limits;
533 SLIST_HEAD(, machservice) machservices;
534 SLIST_HEAD(, semaphoreitem) semaphores;
535 SLIST_HEAD(, waiting_for_removal) removal_watchers;
536 struct waiting4attach *w4a;
537 job_t original;
538 job_t alias;
539 cpu_type_t *j_binpref;
540 size_t j_binpref_cnt;
541 mach_port_t j_port;
542 mach_port_t exit_status_dest;
543 mach_port_t exit_status_port;
544 mach_port_t spawn_reply_port;
545 uid_t mach_uid;
546 jobmgr_t mgr;
547 size_t argc;
548 char **argv;
549 char *prog;
550 char *rootdir;
551 char *workingdir;
552 char *username;
553 char *groupname;
554 char *stdinpath;
555 char *stdoutpath;
556 char *stderrpath;
557 char *alt_exc_handler;
558 char *cfbundleidentifier;
559 unsigned int nruns;
560 uint64_t trt;
561 #if HAVE_SANDBOX
562 char *seatbelt_profile;
563 uint64_t seatbelt_flags;
564 char *container_identifier;
565 #endif
566 #if HAVE_QUARANTINE
567 void *quarantine_data;
568 size_t quarantine_data_sz;
569 #endif
570 pid_t p;
571 uint64_t uniqueid;
572 int last_exit_status;
573 int stdin_fd;
574 int fork_fd;
575 int nice;
576 uint32_t pstype;
577 uint32_t psproctype;
578 int32_t jetsam_priority;
579 int32_t jetsam_memlimit;
580 int32_t main_thread_priority;
581 uint32_t timeout;
582 uint32_t exit_timeout;
583 uint64_t sent_signal_time;
584 uint64_t start_time;
585 uint32_t min_run_time;
586 bool unthrottle;
587 uint32_t start_interval;
588 uint32_t peruser_suspend_count;
589 uuid_t instance_id;
590 mode_t mask;
591 mach_port_t asport;
592 au_asid_t asid;
593 uuid_t expected_audit_uuid;
594 bool
595 // man launchd.plist --> Debug
596 debug:1,
597 // man launchd.plist --> KeepAlive == false
598 ondemand:1,
599 // man launchd.plist --> SessionCreate
600 session_create:1,
601 // man launchd.plist --> LowPriorityIO
602 low_pri_io:1,
603 // man launchd.plist --> InitGroups
604 no_init_groups:1,
605 /* A legacy mach_init concept to make bootstrap_create_server/service()
606 * work
607 */
608 priv_port_has_senders:1,
609 // A hack during job importing
610 importing_global_env:1,
611 // A hack during job importing
612 importing_hard_limits:1,
613 // man launchd.plist --> Umask
614 setmask:1,
615 // A process that launchd knows about but doesn't manage.
616 anonymous:1,
617 // A legacy mach_init concept to detect sick jobs
618 checkedin:1,
619 // A job created via bootstrap_create_server()
620 legacy_mach_job:1,
621 // A job created via spawn_via_launchd()
622 legacy_LS_job:1,
623 // A legacy job that wants inetd compatible semantics
624 inetcompat:1,
625 // A twist on inetd compatibility
626 inetcompat_wait:1,
627 /* An event fired and the job should start, but not necessarily right
628 * away.
629 */
630 start_pending:1,
631 // man launchd.plist --> EnableGlobbing
632 globargv:1,
633 // man launchd.plist --> WaitForDebugger
634 wait4debugger:1,
635 // One-shot WaitForDebugger.
636 wait4debugger_oneshot:1,
637 // MachExceptionHandler == true
638 internal_exc_handler:1,
639 // A hack to support an option of spawn_via_launchd()
640 stall_before_exec:1,
641 /* man launchd.plist --> LaunchOnlyOnce.
642 *
643 * Note: <rdar://problem/5465184> Rename this to "HopefullyNeverExits".
644 */
645 only_once:1,
646 /* Make job_ignore() / job_watch() work. If these calls were balanced,
647 * then this wouldn't be necessarily.
648 */
649 currently_ignored:1,
650 /* A job that forced all other jobs to be temporarily launch-on-
651 * demand
652 */
653 forced_peers_to_demand_mode:1,
654 // man launchd.plist --> Nice
655 setnice:1,
656 /* A job was asked to be unloaded/removed while running, we'll remove it
657 * after it exits.
658 */
659 removal_pending:1,
660 // job_kill() was called.
661 sent_sigkill:1,
662 // Enter the kernel debugger before killing a job.
663 debug_before_kill:1,
664 // A hack that launchd+launchctl use during jobmgr_t creation.
665 weird_bootstrap:1,
666 // man launchd.plist --> StartOnMount
667 start_on_mount:1,
668 // This job is a per-user launchd managed by the PID 1 launchd.
669 per_user:1,
670 // A job thoroughly confused launchd. We need to unload it ASAP.
671 unload_at_mig_return:1,
672 // man launchd.plist --> AbandonProcessGroup
673 abandon_pg:1,
674 /* During shutdown, do not send SIGTERM to stray processes in the
675 * process group of this job.
676 */
677 ignore_pg_at_shutdown:1,
678 /* Don't let this job create new 'job_t' objects in launchd. Has been
679 * seriously overloaded for the purposes of sandboxing.
680 */
681 deny_job_creation:1,
682 // man launchd.plist --> EnableTransactions
683 enable_transactions:1,
684 // The job was sent SIGKILL because it was clean.
685 clean_kill:1,
686 // The job has an OtherJobEnabled KeepAlive criterion.
687 nosy:1,
688 // The job exited due to a crash.
689 crashed:1,
690 // We've received NOTE_EXIT for the job and reaped it.
691 reaped:1,
692 // job_stop() was called.
693 stopped:1,
694 /* The job is to be kept alive continuously, but it must first get an
695 * initial kick off.
696 */
697 needs_kickoff:1,
698 // The job is a bootstrapper.
699 is_bootstrapper:1,
700 // The job owns the console.
701 has_console:1,
702 /* The job runs as a non-root user on embedded but has select privileges
703 * of the root user. This is SpringBoard.
704 */
705 embedded_god:1,
706 // The job is responsible for drawing the home screen on embedded.
707 embedded_home:1,
708 // We got NOTE_EXEC for the job.
709 did_exec:1,
710 // The job is an XPC service, and XPC proxy successfully exec(3)ed.
711 xpcproxy_did_exec:1,
712 // The (anonymous) job called vprocmgr_switch_to_session().
713 holds_ref:1,
714 // The job has Jetsam limits in place.
715 jetsam_properties:1,
716 // The job's Jetsam memory limits should only be applied in the background
717 jetsam_memory_limit_background:1,
718 /* This job was created as the result of a look up of a service provided
719 * by a MultipleInstance job.
720 */
721 dedicated_instance:1,
722 // The job supports creating additional instances of itself.
723 multiple_instances:1,
724 /* The sub-job was already removed from the parent's list of
725 * sub-jobs.
726 */
727 former_subjob:1,
728 /* The job is responsible for monitoring external events for this
729 * launchd.
730 */
731 event_monitor:1,
732 // The event monitor job has retrieved the initial list of events.
733 event_monitor_ready2signal:1,
734 // A lame hack.
735 removing:1,
736 // Disable ASLR when launching this job.
737 disable_aslr:1,
738 // The job is an XPC Service.
739 xpc_service:1,
740 // The job is the Performance team's shutdown monitor.
741 shutdown_monitor:1,
742 // We should open a transaction for the job when shutdown begins.
743 dirty_at_shutdown:1,
744 /* The job was sent SIGKILL but did not exit in a timely fashion,
745 * indicating a kernel bug.
746 */
747 workaround9359725:1,
748 // The job is the XPC domain bootstrapper.
749 xpc_bootstrapper:1,
750 // The job is an app (on either iOS or OS X) and has different resource
751 // limitations.
752 app:1,
753 // FairPlay decryption failed on the job. This should only ever happen
754 // to apps.
755 fpfail:1,
756 // The job failed to exec(3) for reasons that may be transient, so we're
757 // waiting for UserEventAgent to tell us when it's okay to try spawning
758 // again (i.e. when the executable path appears, when the UID appears,
759 // etc.).
760 waiting4ok:1,
761 // The job exited due to memory pressure.
762 jettisoned:1,
763 // The job supports idle-exit.
764 idle_exit:1,
765 // The job was implicitly reaped by the kernel.
766 implicit_reap:1,
767 system_app :1,
768 joins_gui_session :1,
769 low_priority_background_io :1,
770 legacy_timers :1;
771
772 const char label[0];
773 };
774
775 static size_t hash_label(const char *label) __attribute__((pure));
776 static size_t hash_ms(const char *msstr) __attribute__((pure));
777 static SLIST_HEAD(, job_s) s_curious_jobs;
778 static LIST_HEAD(, job_s) managed_actives[ACTIVE_JOB_HASH_SIZE];
779
780 #define job_assumes(j, e) os_assumes_ctx(job_log_bug, j, (e))
781 #if 1
782 #define job_assumes_zero(j, e) os_assumes_zero_ctx(job_log_bug, j, (e))
783 #else
784 static int
crapout(int value,void * j,const char * e)785 crapout(int value, void *j, const char *e)
786 {
787 if (value) {
788 printf("%s=%d j=%p\n", (e), value, j);
789 abort();
790 }
791 return (value);
792 }
793
794 #define job_assumes_zero(j, e) crapout((e), j, #e)
795 #endif
796
797 #define job_assumes_zero_p(j, e) posix_assumes_zero_ctx(job_log_bug, j, (e))
798
799 static void job_import_keys(launch_data_t obj, const char *key, void *context);
800 static void job_import_bool(job_t j, const char *key, bool value);
801 static void job_import_string(job_t j, const char *key, const char *value);
802 static void job_import_integer(job_t j, const char *key, long long value);
803 static void job_import_dictionary(job_t j, const char *key, launch_data_t value);
804 static void job_import_array(job_t j, const char *key, launch_data_t value);
805 static void job_import_opaque(job_t j, const char *key, launch_data_t value);
806 static bool job_set_global_on_demand(job_t j, bool val);
807 static const char *job_active(job_t j);
808 static void job_watch(job_t j);
809 static void job_ignore(job_t j);
810 static void job_reap(job_t j);
811 static bool job_useless(job_t j);
812 static bool job_keepalive(job_t j);
813 static void job_dispatch_curious_jobs(job_t j);
814 static void job_start(job_t j);
815 static void job_start_child(job_t j) __attribute__((noreturn));
816 static void job_setup_attributes(job_t j);
817 static bool job_setup_machport(job_t j);
818 static kern_return_t job_setup_exit_port(job_t j);
819 static void job_setup_fd(job_t j, int target_fd, const char *path, int flags);
820 static void job_postfork_become_user(job_t j);
821 static void job_postfork_test_user(job_t j);
822 static void job_log_pids_with_weird_uids(job_t j);
823 static void job_setup_exception_port(job_t j, task_t target_task);
824 static void job_callback(void *obj, struct kevent *kev);
825 static void job_callback_proc(job_t j, struct kevent *kev);
826 static void job_callback_timer(job_t j, void *ident);
827 static void job_callback_read(job_t j, int ident);
828 static void job_log_stray_pg(job_t j);
829 static void job_log_children_without_exec(job_t j);
830 static job_t job_new_anonymous(jobmgr_t jm, pid_t anonpid) __attribute__((malloc, nonnull, warn_unused_result));
831 static job_t job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *argv) __attribute__((malloc, nonnull(1,2), warn_unused_result));
832 static job_t job_new_alias(jobmgr_t jm, job_t src);
833 static job_t job_new_via_mach_init(job_t j, const char *cmd, uid_t uid, bool ond) __attribute__((malloc, nonnull, warn_unused_result));
834 static job_t job_new_subjob(job_t j, uuid_t identifier);
835 static void job_kill(job_t j);
836 static void job_uncork_fork(job_t j);
837 static void job_logv(job_t j, int pri, int err, const char *msg, va_list ap) __attribute__((format(printf, 4, 0)));
838 static void job_log_error(job_t j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4)));
839 static bool job_log_bug(_SIMPLE_STRING asl_message, void *ctx, const char *message);
840 static void job_log_perf_statistics(job_t j, struct rusage_info_v1 *ri, int64_t exit_status);
841 #if HAVE_SYSTEMSTATS
842 static void job_log_systemstats(pid_t pid, uint64_t uniqueid, uint64_t parent_uniqueid, pid_t req_pid, uint64_t req_uniqueid, const char *name, struct rusage_info_v1 *ri, int64_t exit_status);
843 #endif
844 static void job_set_exception_port(job_t j, mach_port_t port);
845 static kern_return_t job_mig_spawn_internal(job_t j, vm_offset_t indata, mach_msg_type_number_t indataCnt, mach_port_t asport, job_t *outj);
846 static void job_open_shutdown_transaction(job_t ji);
847 static void job_close_shutdown_transaction(job_t ji);
848 #ifndef __FreeBSD__
849 static launch_data_t job_do_legacy_ipc_request(job_t j, launch_data_t request, mach_port_t asport);
850 #endif
851 static void job_setup_per_user_directory(job_t j, uid_t uid, const char *path);
852 static void job_setup_per_user_directories(job_t j, uid_t uid, const char *label);
853 static void job_update_jetsam_properties(job_t j, xpc_jetsam_band_t band, uint64_t user_data);
854 static void job_update_jetsam_memory_limit(job_t j, int32_t limit);
855
856 #if TARGET_OS_EMBEDDED
857 static bool job_import_defaults(launch_data_t pload);
858 #endif
859
860
861 static struct priority_properties_t {
862 long long band;
863 int priority;
864 } _launchd_priority_map[] = {
865 { 0, 0}
866 #if 0
867 { XPC_JETSAM_BAND_SUSPENDED, JETSAM_PRIORITY_IDLE },
868 { XPC_JETSAM_BAND_BACKGROUND_OPPORTUNISTIC, JETSAM_PRIORITY_BACKGROUND_OPPORTUNISTIC },
869 { XPC_JETSAM_BAND_BACKGROUND, JETSAM_PRIORITY_BACKGROUND },
870 { XPC_JETSAM_BAND_MAIL, JETSAM_PRIORITY_MAIL },
871 { XPC_JETSAM_BAND_PHONE, JETSAM_PRIORITY_PHONE },
872 { XPC_JETSAM_BAND_UI_SUPPORT, JETSAM_PRIORITY_UI_SUPPORT },
873 { XPC_JETSAM_BAND_FOREGROUND_SUPPORT, JETSAM_PRIORITY_FOREGROUND_SUPPORT },
874 { XPC_JETSAM_BAND_FOREGROUND, JETSAM_PRIORITY_FOREGROUND },
875 { XPC_JETSAM_BAND_AUDIO, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY },
876 { XPC_JETSAM_BAND_ACCESSORY, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY },
877 { XPC_JETSAM_BAND_CRITICAL, JETSAM_PRIORITY_CRITICAL },
878 { XPC_JETSAM_BAND_TELEPHONY, JETSAM_PRIORITY_TELEPHONY },
879 #endif
880 };
881
882 static const struct {
883 const char *key;
884 int val;
885 } launchd_keys2limits[] = {
886 { LAUNCH_JOBKEY_RESOURCELIMIT_CORE, RLIMIT_CORE },
887 { LAUNCH_JOBKEY_RESOURCELIMIT_CPU, RLIMIT_CPU },
888 { LAUNCH_JOBKEY_RESOURCELIMIT_DATA, RLIMIT_DATA },
889 { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE, RLIMIT_FSIZE },
890 { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK, RLIMIT_MEMLOCK },
891 { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE, RLIMIT_NOFILE },
892 { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC, RLIMIT_NPROC },
893 { LAUNCH_JOBKEY_RESOURCELIMIT_RSS, RLIMIT_RSS },
894 { LAUNCH_JOBKEY_RESOURCELIMIT_STACK, RLIMIT_STACK },
895 };
896
897 static time_t cronemu(int mon, int mday, int hour, int min);
898 static time_t cronemu_wday(int wday, int hour, int min);
899 static bool cronemu_mon(struct tm *wtm, int mon, int mday, int hour, int min);
900 static bool cronemu_mday(struct tm *wtm, int mday, int hour, int min);
901 static bool cronemu_hour(struct tm *wtm, int hour, int min);
902 static bool cronemu_min(struct tm *wtm, int min);
903
904 // miscellaneous file local functions
905 static size_t get_kern_max_proc(void);
906 static char **mach_cmd2argv(const char *string);
907 static size_t our_strhash(const char *s) __attribute__((pure));
908
909 void eliminate_double_reboot(void);
910
911 #pragma mark XPC Domain Forward Declarations
912 static job_t _xpc_domain_import_service(jobmgr_t jm, launch_data_t pload);
913 static int _xpc_domain_import_services(job_t j, launch_data_t services);
914
915 #pragma mark XPC Event Forward Declarations
916 static int xpc_event_find_channel(job_t j, const char *stream, struct machservice **ms);
917 static int xpc_event_get_event_name(job_t j, xpc_object_t request, xpc_object_t *reply);
918 static int xpc_event_set_event(job_t j, xpc_object_t request, xpc_object_t *reply);
919 static int xpc_event_copy_event(job_t j, xpc_object_t request, xpc_object_t *reply);
920 static int xpc_event_channel_check_in(job_t j, xpc_object_t request, xpc_object_t *reply);
921 static int xpc_event_channel_look_up(job_t j, xpc_object_t request, xpc_object_t *reply);
922 static int xpc_event_provider_check_in(job_t j, xpc_object_t request, xpc_object_t *reply);
923 static int xpc_event_provider_set_state(job_t j, xpc_object_t request, xpc_object_t *reply);
924
925 #pragma mark XPC Process Forward Declarations
926 static int xpc_process_set_jetsam_band(job_t j, xpc_object_t request, xpc_object_t *reply);
927 static int xpc_process_set_jetsam_memory_limit(job_t j, xpc_object_t request, xpc_object_t *reply);
928
929 // file local globals
930 static job_t _launchd_embedded_god = NULL;
931 static job_t _launchd_embedded_home = NULL;
932 static size_t total_children;
933 static size_t total_anon_children;
934 static mach_port_t the_exception_server;
935 static job_t workaround_5477111;
936 static LIST_HEAD(, job_s) s_needing_sessions;
937 static LIST_HEAD(, eventsystem) _s_event_systems;
938 static struct eventsystem *_launchd_support_system;
939 static job_t _launchd_event_monitor;
940 static job_t _launchd_xpc_bootstrapper;
941 static job_t _launchd_shutdown_monitor;
942
943 #if TARGET_OS_EMBEDDED
944 static xpc_object_t _launchd_defaults_cache;
945
946 mach_port_t launchd_audit_port = MACH_PORT_DEAD;
947 pid_t launchd_audit_session = 0;
948 #else
949 mach_port_t launchd_audit_port = MACH_PORT_NULL;
950 au_asid_t launchd_audit_session = AU_DEFAUDITSID;
951 #endif
952
953 static int s_no_hang_fd = -1;
954
955 // process wide globals
956 mach_port_t inherited_bootstrap_port;
957 jobmgr_t root_jobmgr;
958 bool launchd_shutdown_debugging = false;
959 bool launchd_verbose_boot = false;
960 bool launchd_embedded_handofgod = false;
961 bool launchd_runtime_busy_time = false;
962
963 void
job_ignore(job_t j)964 job_ignore(job_t j)
965 {
966 struct socketgroup *sg;
967 struct machservice *ms;
968
969 if (j->currently_ignored) {
970 return;
971 }
972
973 job_log(j, LOG_DEBUG, "Ignoring...");
974
975 j->currently_ignored = true;
976
977 SLIST_FOREACH(sg, &j->sockets, sle) {
978 socketgroup_ignore(j, sg);
979 }
980
981 SLIST_FOREACH(ms, &j->machservices, sle) {
982 machservice_ignore(j, ms);
983 }
984 }
985
986 void
job_watch(job_t j)987 job_watch(job_t j)
988 {
989 struct socketgroup *sg;
990 struct machservice *ms;
991
992 if (!j->currently_ignored) {
993 return;
994 }
995
996 job_log(j, LOG_DEBUG, "Watching...");
997
998 j->currently_ignored = false;
999
1000 SLIST_FOREACH(sg, &j->sockets, sle) {
1001 socketgroup_watch(j, sg);
1002 }
1003
1004 SLIST_FOREACH(ms, &j->machservices, sle) {
1005 machservice_watch(j, ms);
1006 }
1007 }
1008
1009 void
job_stop(job_t j)1010 job_stop(job_t j)
1011 {
1012 int sig;
1013
1014 if (unlikely(!j->p || j->stopped || j->anonymous)) {
1015 return;
1016 }
1017
1018 #if TARGET_OS_EMBEDDED
1019 if (launchd_embedded_handofgod && _launchd_embedded_god) {
1020 if (!_launchd_embedded_god->username || !j->username) {
1021 errno = EPERM;
1022 return;
1023 }
1024
1025 if (strcmp(j->username, _launchd_embedded_god->username) != 0) {
1026 errno = EPERM;
1027 return;
1028 }
1029 } else if (launchd_embedded_handofgod) {
1030 errno = EINVAL;
1031 return;
1032 }
1033 #endif
1034
1035 j->sent_signal_time = runtime_get_opaque_time();
1036
1037 job_log(j, LOG_DEBUG | LOG_CONSOLE, "Stopping job...");
1038
1039 int error = -1;
1040 error = proc_terminate(j->p, &sig);
1041 if (error) {
1042 job_log(j, LOG_ERR | LOG_CONSOLE, "Could not terminate job: %d: %s", error, strerror(error));
1043 job_log(j, LOG_NOTICE | LOG_CONSOLE, "Using fallback option to terminate job...");
1044 error = kill2(j->p, SIGTERM);
1045 if (error) {
1046 job_log(j, LOG_ERR, "Could not signal job: %d: %s", error, strerror(error));
1047 } else {
1048 sig = SIGTERM;
1049 }
1050 }
1051
1052 if (!error) {
1053 switch (sig) {
1054 case SIGKILL:
1055 j->sent_sigkill = true;
1056 j->clean_kill = true;
1057
1058 /* We cannot effectively simulate an exit for jobs during the course
1059 * of a normal run. Even if we pretend that the job exited, we will
1060 * still not have gotten the receive rights associated with the
1061 * job's MachServices back, so we cannot safely respawn it.
1062 */
1063 if (j->mgr->shutting_down) {
1064 error = kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, LAUNCHD_SIGKILL_TIMER, j);
1065 (void)job_assumes_zero_p(j, error);
1066 }
1067
1068 job_log(j, LOG_DEBUG | LOG_CONSOLE, "Sent job SIGKILL.");
1069 break;
1070 case SIGTERM:
1071 if (j->exit_timeout) {
1072 error = kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, j->exit_timeout, j);
1073 (void)job_assumes_zero_p(j, error);
1074 } else {
1075 job_log(j, LOG_NOTICE, "This job has an infinite exit timeout");
1076 }
1077 job_log(j, LOG_DEBUG, "Sent job SIGTERM.");
1078 break;
1079 default:
1080 job_log(j, LOG_ERR | LOG_CONSOLE, "Job was sent unexpected signal: %d: %s", sig, strsignal(sig));
1081 break;
1082 }
1083 }
1084
1085 j->stopped = true;
1086 }
1087
1088 launch_data_t
job_export(job_t j)1089 job_export(job_t j)
1090 {
1091 launch_data_t tmp, tmp2, tmp3, r = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1092
1093 if (r == NULL) {
1094 return NULL;
1095 }
1096
1097 if ((tmp = launch_data_new_string(j->label))) {
1098 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_LABEL);
1099 }
1100 if ((tmp = launch_data_new_string(j->mgr->name))) {
1101 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE);
1102 }
1103 if ((tmp = launch_data_new_bool(j->ondemand))) {
1104 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_ONDEMAND);
1105 }
1106
1107 long long status = j->last_exit_status;
1108 if (j->fpfail) {
1109 status = LAUNCH_EXITSTATUS_FAIRPLAY_FAIL;
1110 }
1111 if ((tmp = launch_data_new_integer(status))) {
1112 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_LASTEXITSTATUS);
1113 }
1114
1115 if (j->p && (tmp = launch_data_new_integer(j->p))) {
1116 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_PID);
1117 }
1118 if ((tmp = launch_data_new_integer(j->timeout))) {
1119 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_TIMEOUT);
1120 }
1121 if (j->prog && (tmp = launch_data_new_string(j->prog))) {
1122 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_PROGRAM);
1123 }
1124 if (j->stdinpath && (tmp = launch_data_new_string(j->stdinpath))) {
1125 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_STANDARDINPATH);
1126 }
1127 if (j->stdoutpath && (tmp = launch_data_new_string(j->stdoutpath))) {
1128 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_STANDARDOUTPATH);
1129 }
1130 if (j->stderrpath && (tmp = launch_data_new_string(j->stderrpath))) {
1131 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_STANDARDERRORPATH);
1132 }
1133 if (likely(j->argv) && (tmp = launch_data_alloc(LAUNCH_DATA_ARRAY))) {
1134 size_t i;
1135
1136 for (i = 0; i < j->argc; i++) {
1137 if ((tmp2 = launch_data_new_string(j->argv[i]))) {
1138 launch_data_array_set_index(tmp, tmp2, i);
1139 }
1140 }
1141
1142 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
1143 }
1144
1145 if (!SLIST_EMPTY(&j->env) && (tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY))) {
1146 struct envitem *ei = NULL;
1147 SLIST_FOREACH(ei, &j->env, sle) {
1148 launch_data_dict_insert(tmp, launch_data_new_string(ei->value), ei->key);
1149 }
1150
1151 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES);
1152 }
1153
1154 if (j->enable_transactions && (tmp = launch_data_new_bool(true))) {
1155 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_ENABLETRANSACTIONS);
1156 }
1157
1158 if (j->session_create && (tmp = launch_data_new_bool(true))) {
1159 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_SESSIONCREATE);
1160 }
1161
1162 if (j->inetcompat && (tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY))) {
1163 if ((tmp2 = launch_data_new_bool(j->inetcompat_wait))) {
1164 launch_data_dict_insert(tmp, tmp2, LAUNCH_JOBINETDCOMPATIBILITY_WAIT);
1165 }
1166 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_INETDCOMPATIBILITY);
1167 }
1168
1169 if (!SLIST_EMPTY(&j->sockets) && (tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY))) {
1170 struct socketgroup *sg;
1171 unsigned int i;
1172
1173 SLIST_FOREACH(sg, &j->sockets, sle) {
1174 if ((tmp2 = launch_data_alloc(LAUNCH_DATA_ARRAY))) {
1175 for (i = 0; i < sg->fd_cnt; i++) {
1176 if ((tmp3 = launch_data_new_fd(sg->fds[i]))) {
1177 launch_data_array_set_index(tmp2, tmp3, i);
1178 }
1179 }
1180 launch_data_dict_insert(tmp, tmp2, sg->name);
1181 }
1182 }
1183
1184 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_SOCKETS);
1185 }
1186
1187 if (!SLIST_EMPTY(&j->machservices) && (tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY))) {
1188 struct machservice *ms;
1189
1190 tmp3 = NULL;
1191
1192 SLIST_FOREACH(ms, &j->machservices, sle) {
1193 if (ms->per_pid) {
1194 if (tmp3 == NULL) {
1195 tmp3 = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1196 }
1197 if (tmp3) {
1198 tmp2 = launch_data_new_machport(MACH_PORT_NULL);
1199 launch_data_dict_insert(tmp3, tmp2, ms->name);
1200 }
1201 } else {
1202 tmp2 = launch_data_new_machport(MACH_PORT_NULL);
1203 launch_data_dict_insert(tmp, tmp2, ms->name);
1204 }
1205 }
1206
1207 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_MACHSERVICES);
1208
1209 if (tmp3) {
1210 launch_data_dict_insert(r, tmp3, LAUNCH_JOBKEY_PERJOBMACHSERVICES);
1211 }
1212 }
1213
1214 return r;
1215 }
1216
1217 static void
jobmgr_log_active_jobs(jobmgr_t jm)1218 jobmgr_log_active_jobs(jobmgr_t jm)
1219 {
1220 const char *why_active;
1221 jobmgr_t jmi;
1222 job_t ji;
1223
1224 SLIST_FOREACH(jmi, &jm->submgrs, sle) {
1225 jobmgr_log_active_jobs(jmi);
1226 }
1227
1228 int level = LOG_DEBUG;
1229 if (pid1_magic) {
1230 level |= LOG_CONSOLE;
1231 }
1232
1233 LIST_FOREACH(ji, &jm->jobs, sle) {
1234 if ((why_active = job_active(ji))) {
1235 if (ji->p != 1) {
1236 job_log(ji, level, "%s", why_active);
1237
1238 uint32_t flags = 0;
1239 (void)proc_get_dirty(ji->p, &flags);
1240 if (!(flags & PROC_DIRTY_TRACKED)) {
1241 continue;
1242 }
1243
1244 const char *dirty = "clean";
1245 if (flags & PROC_DIRTY_IS_DIRTY) {
1246 dirty = "dirty";
1247 }
1248
1249 const char *idle_exit = "idle-exit unsupported";
1250 if (flags & PROC_DIRTY_ALLOWS_IDLE_EXIT) {
1251 idle_exit = "idle-exit supported";
1252 }
1253
1254 job_log(ji, level, "Killability: %s/%s", dirty, idle_exit);
1255 }
1256 }
1257 }
1258 }
1259
1260 static void
jobmgr_still_alive_with_check(jobmgr_t jm)1261 jobmgr_still_alive_with_check(jobmgr_t jm)
1262 {
1263 int level = LOG_DEBUG;
1264 if (pid1_magic) {
1265 level |= LOG_CONSOLE;
1266 }
1267
1268 jobmgr_log(jm, level, "Still alive with %lu/%lu (normal/anonymous) children.", total_children, total_anon_children);
1269 jobmgr_log_active_jobs(jm);
1270 launchd_log_push();
1271 }
1272
1273 jobmgr_t
jobmgr_shutdown(jobmgr_t jm)1274 jobmgr_shutdown(jobmgr_t jm)
1275 {
1276 jobmgr_t jmi, jmn;
1277 launchd_syslog(LOG_CRIT, "Beginning job manager shutdown with flags: %s", reboot_flags_to_C_names(jm->reboot_flags));
1278 jobmgr_log(jm, LOG_DEBUG, "Beginning job manager shutdown with flags: %s", reboot_flags_to_C_names(jm->reboot_flags));
1279
1280 jm->shutdown_time = runtime_get_wall_time() / USEC_PER_SEC;
1281
1282 struct tm curtime;
1283 (void)localtime_r(&jm->shutdown_time, &curtime);
1284
1285 char date[26];
1286 (void)asctime_r(&curtime, date);
1287 // Trim the new line that asctime_r(3) puts there for some reason.
1288 date[24] = 0;
1289
1290 if (jm == root_jobmgr && pid1_magic) {
1291 jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "Userspace shutdown begun at: %s", date);
1292 } else {
1293 jobmgr_log(jm, LOG_DEBUG, "Job manager shutdown begun at: %s", date);
1294 }
1295
1296 jm->shutting_down = true;
1297
1298 SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
1299 jobmgr_shutdown(jmi);
1300 }
1301
1302 if (!jm->parentmgr) {
1303 if (pid1_magic) {
1304 // Spawn the shutdown monitor.
1305 if (_launchd_shutdown_monitor && !_launchd_shutdown_monitor->p) {
1306 job_log(_launchd_shutdown_monitor, LOG_NOTICE | LOG_CONSOLE, "Starting shutdown monitor.");
1307 job_dispatch(_launchd_shutdown_monitor, true);
1308 }
1309 }
1310
1311 (void)jobmgr_assumes_zero_p(jm, kevent_mod((uintptr_t)jm, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 5, jm));
1312 }
1313
1314 return jobmgr_do_garbage_collection(jm);
1315 }
1316
1317 void
jobmgr_remove(jobmgr_t jm)1318 jobmgr_remove(jobmgr_t jm)
1319 {
1320 jobmgr_t jmi;
1321 job_t ji;
1322
1323 jobmgr_log(jm, LOG_DEBUG, "Removing job manager.");
1324 if (!SLIST_EMPTY(&jm->submgrs)) {
1325 size_t cnt = 0;
1326 while ((jmi = SLIST_FIRST(&jm->submgrs))) {
1327 jobmgr_remove(jmi);
1328 cnt++;
1329 }
1330
1331 (void)jobmgr_assumes_zero(jm, cnt);
1332 }
1333
1334 while ((ji = LIST_FIRST(&jm->jobs))) {
1335 if (!ji->anonymous && ji->p != 0) {
1336 job_log(ji, LOG_ERR, "Job is still active at job manager teardown.");
1337 ji->p = 0;
1338 }
1339
1340 job_remove(ji);
1341 }
1342
1343 struct waiting4attach *w4ai = NULL;
1344 while ((w4ai = LIST_FIRST(&jm->attaches))) {
1345 waiting4attach_delete(jm, w4ai);
1346 }
1347
1348 if (jm->req_port) {
1349 (void)jobmgr_assumes_zero(jm, launchd_mport_deallocate(jm->req_port));
1350 }
1351 if (jm->jm_port) {
1352 (void)jobmgr_assumes_zero(jm, launchd_mport_close_recv(jm->jm_port));
1353 }
1354
1355 if (jm->req_bsport) {
1356 (void)jobmgr_assumes_zero(jm, launchd_mport_deallocate(jm->req_bsport));
1357 }
1358 if (jm->req_excport) {
1359 (void)jobmgr_assumes_zero(jm, launchd_mport_deallocate(jm->req_excport));
1360 }
1361 if (MACH_PORT_VALID(jm->req_asport)) {
1362 (void)jobmgr_assumes_zero(jm, launchd_mport_deallocate(jm->req_asport));
1363 }
1364 if (jm->req_rport) {
1365 kern_return_t kr = xpc_call_wakeup(jm->req_rport, jm->error);
1366 if (!(kr == KERN_SUCCESS || kr == MACH_SEND_INVALID_DEST)) {
1367 /* If the originator went away, the reply port will be a dead name,
1368 * and we expect this to fail.
1369 */
1370 (void)jobmgr_assumes_zero(jm, kr);
1371 }
1372 }
1373 if (jm->req_ctx) {
1374 (void)jobmgr_assumes_zero(jm, vm_deallocate(mach_task_self(), jm->req_ctx, jm->req_ctx_sz));
1375 }
1376
1377 time_t ts = runtime_get_wall_time() / USEC_PER_SEC;
1378 struct tm curtime;
1379 (void)localtime_r(&ts, &curtime);
1380
1381 char date[26];
1382 (void)asctime_r(&curtime, date);
1383 date[24] = 0;
1384
1385 time_t delta = ts - jm->shutdown_time;
1386 if (jm == root_jobmgr && pid1_magic) {
1387 jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "Userspace shutdown finished at: %s", date);
1388 jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "Userspace shutdown took approximately %ld second%s.", delta, (delta != 1) ? "s" : "");
1389 } else {
1390 jobmgr_log(jm, LOG_DEBUG, "Job manager shutdown finished at: %s", date);
1391 jobmgr_log(jm, LOG_DEBUG, "Job manager shutdown took approximately %ld second%s.", delta, (delta != 1) ? "s" : "");
1392 }
1393
1394 if (jm->parentmgr) {
1395 runtime_del_weak_ref();
1396 SLIST_REMOVE(&jm->parentmgr->submgrs, jm, jobmgr_s, sle);
1397
1398 // Hack for the guest user so that its stuff doesn't persist.
1399 //
1400 // <rdar://problem/14527875>
1401 if (strcmp(jm->name, VPROCMGR_SESSION_AQUA) == 0 && getuid() == 201) {
1402 raise(SIGTERM);
1403 }
1404 } else if (pid1_magic) {
1405 eliminate_double_reboot();
1406 launchd_log_vm_stats();
1407 jobmgr_log_stray_children(jm, true);
1408 jobmgr_log(root_jobmgr, LOG_NOTICE | LOG_CONSOLE, "About to call: reboot(%s).", reboot_flags_to_C_names(jm->reboot_flags));
1409
1410 syslog(LOG_CRIT, "About to call reboot, flags = %#x (%s)", jm->reboot_flags, reboot_flags_to_C_names(jm->reboot_flags));
1411 launchd_closelog();
1412 (void)jobmgr_assumes_zero_p(jm, reboot(jm->reboot_flags));
1413 } else {
1414 jobmgr_log(jm, LOG_DEBUG, "About to exit");
1415 launchd_closelog();
1416 exit(EXIT_SUCCESS);
1417 }
1418
1419 free(jm);
1420 }
1421
1422 void
job_remove(job_t j)1423 job_remove(job_t j)
1424 {
1425 struct waiting_for_removal *w4r;
1426 struct calendarinterval *ci;
1427 struct semaphoreitem *si;
1428 struct socketgroup *sg;
1429 struct machservice *ms;
1430 struct limititem *li;
1431 struct envitem *ei;
1432
1433 if (j->alias) {
1434 /* HACK: Egregious code duplication. But as with machservice_delete(),
1435 * job aliases can't (and shouldn't) have any complex behaviors
1436 * associated with them.
1437 */
1438 while ((ms = SLIST_FIRST(&j->machservices))) {
1439 machservice_delete(j, ms, false);
1440 }
1441
1442 LIST_REMOVE(j, sle);
1443 LIST_REMOVE(j, label_hash_sle);
1444 free(j);
1445 return;
1446 }
1447
1448 #if TARGET_OS_EMBEDDED
1449 if (launchd_embedded_handofgod && _launchd_embedded_god) {
1450 if (!(_launchd_embedded_god->username && j->username)) {
1451 errno = EPERM;
1452 return;
1453 }
1454
1455 if (strcmp(j->username, _launchd_embedded_god->username) != 0) {
1456 errno = EPERM;
1457 return;
1458 }
1459 } else if (launchd_embedded_handofgod) {
1460 errno = EINVAL;
1461 return;
1462 }
1463 #endif
1464
1465 /* Do this BEFORE we check and see whether the job is still active. If we're
1466 * a sub-job, we're being removed due to the parent job removing us.
1467 * Therefore, the parent job will free itself after this call completes. So
1468 * if we defer removing ourselves from the parent's list, we'll crash when
1469 * we finally get around to it.
1470 */
1471 if (j->dedicated_instance && !j->former_subjob) {
1472 LIST_REMOVE(j, subjob_sle);
1473 j->former_subjob = true;
1474 }
1475
1476 if (unlikely(j->p)) {
1477 if (j->anonymous) {
1478 job_reap(j);
1479 } else {
1480 job_log(j, LOG_DEBUG, "Removal pended until the job exits");
1481
1482 if (!j->removal_pending) {
1483 j->removal_pending = true;
1484 job_stop(j);
1485 }
1486
1487 return;
1488 }
1489 }
1490
1491 if (!j->removing) {
1492 j->removing = true;
1493 job_dispatch_curious_jobs(j);
1494 }
1495
1496 ipc_close_all_with_job(j);
1497
1498 if (j->forced_peers_to_demand_mode) {
1499 job_set_global_on_demand(j, false);
1500 }
1501
1502 if (job_assumes_zero(j, j->fork_fd)) {
1503 (void)posix_assumes_zero(runtime_close(j->fork_fd));
1504 }
1505
1506 if (j->stdin_fd) {
1507 (void)posix_assumes_zero(runtime_close(j->stdin_fd));
1508 }
1509
1510 if (j->j_port) {
1511 (void)job_assumes_zero(j, launchd_mport_close_recv(j->j_port));
1512 }
1513
1514 while ((sg = SLIST_FIRST(&j->sockets))) {
1515 socketgroup_delete(j, sg);
1516 }
1517 while ((ci = SLIST_FIRST(&j->cal_intervals))) {
1518 calendarinterval_delete(j, ci);
1519 }
1520 while ((ei = SLIST_FIRST(&j->env))) {
1521 envitem_delete(j, ei, false);
1522 }
1523 while ((ei = SLIST_FIRST(&j->global_env))) {
1524 envitem_delete(j, ei, true);
1525 }
1526 while ((li = SLIST_FIRST(&j->limits))) {
1527 limititem_delete(j, li);
1528 }
1529 while ((ms = SLIST_FIRST(&j->machservices))) {
1530 machservice_delete(j, ms, false);
1531 }
1532 while ((si = SLIST_FIRST(&j->semaphores))) {
1533 semaphoreitem_delete(j, si);
1534 }
1535 while ((w4r = SLIST_FIRST(&j->removal_watchers))) {
1536 waiting4removal_delete(j, w4r);
1537 }
1538
1539 struct externalevent *eei = NULL;
1540 while ((eei = LIST_FIRST(&j->events))) {
1541 externalevent_delete(eei);
1542 }
1543
1544 if (j->event_monitor) {
1545 _launchd_event_monitor = NULL;
1546 }
1547 if (j->xpc_bootstrapper) {
1548 _launchd_xpc_bootstrapper = NULL;
1549 }
1550
1551 if (j->prog) {
1552 free(j->prog);
1553 }
1554 if (j->argv) {
1555 free(j->argv);
1556 }
1557 if (j->rootdir) {
1558 free(j->rootdir);
1559 }
1560 if (j->workingdir) {
1561 free(j->workingdir);
1562 }
1563 if (j->username) {
1564 free(j->username);
1565 }
1566 if (j->groupname) {
1567 free(j->groupname);
1568 }
1569 if (j->stdinpath) {
1570 free(j->stdinpath);
1571 }
1572 if (j->stdoutpath) {
1573 free(j->stdoutpath);
1574 }
1575 if (j->stderrpath) {
1576 free(j->stderrpath);
1577 }
1578 if (j->alt_exc_handler) {
1579 free(j->alt_exc_handler);
1580 }
1581 if (j->cfbundleidentifier) {
1582 free(j->cfbundleidentifier);
1583 }
1584 #if HAVE_SANDBOX
1585 if (j->seatbelt_profile) {
1586 free(j->seatbelt_profile);
1587 }
1588 if (j->container_identifier) {
1589 free(j->container_identifier);
1590 }
1591 #endif
1592 #if HAVE_QUARANTINE
1593 if (j->quarantine_data) {
1594 free(j->quarantine_data);
1595 }
1596 #endif
1597 if (j->j_binpref) {
1598 free(j->j_binpref);
1599 }
1600 if (j->start_interval) {
1601 runtime_del_weak_ref();
1602 (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL));
1603 }
1604 if (j->exit_timeout) {
1605 /* If this fails, it just means the timer's already fired, so no need to
1606 * wrap it in an assumes() macro.
1607 */
1608 (void)kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
1609 }
1610 if (j->asport != MACH_PORT_NULL) {
1611 (void)job_assumes_zero(j, launchd_mport_deallocate(j->asport));
1612 }
1613 if (!uuid_is_null(j->expected_audit_uuid)) {
1614 LIST_REMOVE(j, needing_session_sle);
1615 }
1616 if (j->embedded_god) {
1617 _launchd_embedded_god = NULL;
1618 }
1619 if (j->embedded_home) {
1620 _launchd_embedded_home = NULL;
1621 }
1622 if (j->shutdown_monitor) {
1623 _launchd_shutdown_monitor = NULL;
1624 }
1625
1626 (void)kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
1627
1628 LIST_REMOVE(j, sle);
1629 LIST_REMOVE(j, label_hash_sle);
1630
1631 job_t ji = NULL;
1632 job_t jit = NULL;
1633 LIST_FOREACH_SAFE(ji, &j->subjobs, subjob_sle, jit) {
1634 job_remove(ji);
1635 }
1636
1637 job_log(j, LOG_DEBUG, "Removed");
1638
1639 j->kqjob_callback = (kq_callback)0x8badf00d;
1640 free(j);
1641 }
1642
1643 void
socketgroup_setup(launch_data_t obj,const char * key,void * context)1644 socketgroup_setup(launch_data_t obj, const char *key, void *context)
1645 {
1646 launch_data_t tmp_oai;
1647 job_t j = context;
1648 size_t i, fd_cnt = 1;
1649 int *fds;
1650
1651 if (launch_data_get_type(obj) == LAUNCH_DATA_ARRAY) {
1652 fd_cnt = launch_data_array_get_count(obj);
1653 }
1654
1655 fds = alloca(fd_cnt * sizeof(int));
1656
1657 for (i = 0; i < fd_cnt; i++) {
1658 if (launch_data_get_type(obj) == LAUNCH_DATA_ARRAY) {
1659 tmp_oai = launch_data_array_get_index(obj, i);
1660 } else {
1661 tmp_oai = obj;
1662 }
1663
1664 fds[i] = launch_data_get_fd(tmp_oai);
1665 }
1666
1667 socketgroup_new(j, key, fds, fd_cnt);
1668
1669 ipc_revoke_fds(obj);
1670 }
1671
1672 bool
job_set_global_on_demand(job_t j,bool val)1673 job_set_global_on_demand(job_t j, bool val)
1674 {
1675 if (j->forced_peers_to_demand_mode && val) {
1676 return false;
1677 } else if (!j->forced_peers_to_demand_mode && !val) {
1678 return false;
1679 }
1680
1681 if ((j->forced_peers_to_demand_mode = val)) {
1682 j->mgr->global_on_demand_cnt++;
1683 } else {
1684 j->mgr->global_on_demand_cnt--;
1685 }
1686
1687 if (j->mgr->global_on_demand_cnt == 0) {
1688 jobmgr_dispatch_all(j->mgr, false);
1689 }
1690
1691 return true;
1692 }
1693
1694 bool
job_setup_machport(job_t j)1695 job_setup_machport(job_t j)
1696 {
1697 if (job_assumes_zero(j, launchd_mport_create_recv(&j->j_port)) != KERN_SUCCESS) {
1698 goto out_bad;
1699 }
1700
1701 if (job_assumes_zero(j, runtime_add_mport(j->j_port, job_server)) != KERN_SUCCESS) {
1702 goto out_bad2;
1703 }
1704
1705 if (job_assumes_zero(j, launchd_mport_notify_req(j->j_port, MACH_NOTIFY_NO_SENDERS)) != KERN_SUCCESS) {
1706 (void)job_assumes_zero(j, launchd_mport_close_recv(j->j_port));
1707 goto out_bad;
1708 }
1709
1710 return true;
1711 out_bad2:
1712 (void)job_assumes_zero(j, launchd_mport_close_recv(j->j_port));
1713 out_bad:
1714 return false;
1715 }
1716
1717 kern_return_t
job_setup_exit_port(job_t j)1718 job_setup_exit_port(job_t j)
1719 {
1720 kern_return_t kr = launchd_mport_create_recv(&j->exit_status_port);
1721 if (job_assumes_zero(j, kr) != KERN_SUCCESS) {
1722 return MACH_PORT_NULL;
1723 }
1724
1725 struct mach_port_limits limits = {
1726 .mpl_qlimit = 1,
1727 };
1728 kr = mach_port_set_attributes(mach_task_self(), j->exit_status_port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, sizeof(limits));
1729 (void)job_assumes_zero(j, kr);
1730
1731 kr = launchd_mport_make_send_once(j->exit_status_port, &j->exit_status_dest);
1732 if (job_assumes_zero(j, kr) != KERN_SUCCESS) {
1733 (void)job_assumes_zero(j, launchd_mport_close_recv(j->exit_status_port));
1734 j->exit_status_port = MACH_PORT_NULL;
1735 }
1736
1737 return kr;
1738 }
1739
1740 job_t
job_new_via_mach_init(job_t j,const char * cmd,uid_t uid,bool ond)1741 job_new_via_mach_init(job_t j, const char *cmd, uid_t uid, bool ond)
1742 {
1743 const char **argv = (const char **)mach_cmd2argv(cmd);
1744 job_t jr = NULL;
1745
1746 if (!argv) {
1747 goto out_bad;
1748 }
1749
1750 jr = job_new(j->mgr, AUTO_PICK_LEGACY_LABEL, NULL, argv);
1751 free(argv);
1752
1753 // Job creation can be denied during shutdown.
1754 if (unlikely(jr == NULL)) {
1755 goto out_bad;
1756 }
1757
1758 jr->mach_uid = uid;
1759 jr->ondemand = ond;
1760 jr->legacy_mach_job = true;
1761 jr->abandon_pg = true;
1762 jr->priv_port_has_senders = true; // the IPC that called us will make-send on this port
1763
1764 if (!job_setup_machport(jr)) {
1765 goto out_bad;
1766 }
1767
1768 job_log(jr, LOG_INFO, "Legacy%s server created", ond ? " on-demand" : "");
1769
1770 return jr;
1771
1772 out_bad:
1773 if (jr) {
1774 job_remove(jr);
1775 }
1776 return NULL;
1777 }
1778
1779 job_t
job_new_anonymous(jobmgr_t jm,pid_t anonpid)1780 job_new_anonymous(jobmgr_t jm, pid_t anonpid)
1781 {
1782 struct proc_bsdshortinfo proc;
1783 bool shutdown_state;
1784 job_t jp = NULL, jr = NULL;
1785 uid_t kp_euid, kp_uid, kp_svuid;
1786 gid_t kp_egid, kp_gid, kp_svgid;
1787
1788 if (anonpid == 0) {
1789 errno = EINVAL;
1790 return NULL;
1791 }
1792
1793 if (anonpid >= 100000) {
1794 /* The kernel current defines PID_MAX to be 99999, but that define isn't
1795 * exported.
1796 */
1797 launchd_syslog(LOG_WARNING, "Did PID_MAX change? Got request from PID: %d", anonpid);
1798 errno = EINVAL;
1799 return NULL;
1800 }
1801
1802 /* libproc returns the number of bytes written into the buffer upon success,
1803 * zero on failure. I'd much rather it return -1 on failure, like sysctl(3).
1804 */
1805 if (proc_pidinfo(anonpid, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) {
1806 if (errno != ESRCH) {
1807 (void)jobmgr_assumes_zero(jm, errno);
1808 }
1809 return NULL;
1810 }
1811
1812 if (proc.pbsi_comm[0] == '\0') {
1813 launchd_syslog(LOG_WARNING, "Blank command for PID: %d", anonpid);
1814 errno = EINVAL;
1815 return NULL;
1816 }
1817
1818 if (unlikely(proc.pbsi_status == SZOMB)) {
1819 jobmgr_log(jm, LOG_DEBUG, "Tried to create an anonymous job for zombie PID %u: %s", anonpid, proc.pbsi_comm);
1820 }
1821
1822 if (unlikely(proc.pbsi_flags & P_SUGID)) {
1823 jobmgr_log(jm, LOG_DEBUG, "Inconsistency: P_SUGID is set on PID %u: %s", anonpid, proc.pbsi_comm);
1824 }
1825
1826 kp_euid = proc.pbsi_uid;
1827 kp_uid = proc.pbsi_ruid;
1828 kp_svuid = proc.pbsi_svuid;
1829 kp_egid = proc.pbsi_gid;
1830 kp_gid = proc.pbsi_rgid;
1831 kp_svgid = proc.pbsi_svgid;
1832
1833 if (unlikely(kp_euid != kp_uid || kp_euid != kp_svuid || kp_uid != kp_svuid || kp_egid != kp_gid || kp_egid != kp_svgid || kp_gid != kp_svgid)) {
1834 jobmgr_log(jm, LOG_DEBUG, "Inconsistency: Mixed credentials (e/r/s UID %u/%u/%u GID %u/%u/%u) detected on PID %u: %s",
1835 kp_euid, kp_uid, kp_svuid, kp_egid, kp_gid, kp_svgid, anonpid, proc.pbsi_comm);
1836 }
1837
1838 /* "Fix" for when the kernel turns the process tree into a weird, cyclic
1839 * graph.
1840 *
1841 * See <rdar://problem/7264615> for the symptom and <rdar://problem/5020256>
1842 * as to why this can happen.
1843 */
1844 if ((pid_t)proc.pbsi_ppid == anonpid) {
1845 jobmgr_log(jm, LOG_WARNING, "Process has become its own parent through ptrace(3). Ignoring: %s", proc.pbsi_comm);
1846 errno = EINVAL;
1847 return NULL;
1848 }
1849
1850 /* HACK: Normally, job_new() returns an error during shutdown, but anonymous
1851 * jobs can pop up during shutdown and need to talk to us.
1852 */
1853 if (unlikely(shutdown_state = jm->shutting_down)) {
1854 jm->shutting_down = false;
1855 }
1856
1857 // We only set requestor_pid for XPC domains.
1858 const char *whichlabel = (jm->req_pid == anonpid) ? AUTO_PICK_XPC_LABEL : AUTO_PICK_ANONYMOUS_LABEL;
1859 if ((jr = job_new(jm, whichlabel, proc.pbsi_comm, NULL))) {
1860 u_int proc_fflags = NOTE_EXEC|NOTE_FORK|NOTE_EXIT;
1861
1862 total_anon_children++;
1863 jr->anonymous = true;
1864 jr->p = anonpid;
1865
1866 // Anonymous process reaping is messy.
1867 LIST_INSERT_HEAD(&jm->active_jobs[ACTIVE_JOB_HASH(jr->p)], jr, pid_hash_sle);
1868
1869 if (unlikely(kevent_mod(jr->p, EVFILT_PROC, EV_ADD, proc_fflags, 0, root_jobmgr) == -1)) {
1870 if (errno != ESRCH) {
1871 (void)job_assumes_zero(jr, errno);
1872 }
1873
1874 // Zombies interact weirdly with kevent(3).
1875 job_log(jr, LOG_ERR, "Failed to add kevent for PID %u. Will unload at MIG return", jr->p);
1876 jr->unload_at_mig_return = true;
1877 }
1878
1879 if (unlikely(shutdown_state)) {
1880 job_log(jr, LOG_APPLEONLY, "This process showed up to the party while all the guests were leaving. Odds are that it will have a miserable time.");
1881 }
1882
1883 job_log(jr, LOG_DEBUG, "Created PID %u anonymously by PPID %u%s%s", anonpid, proc.pbsi_ppid, jp ? ": " : "", jp ? jp->label : "");
1884 } else {
1885 (void)os_assumes_zero(errno);
1886 }
1887
1888 // Undo our hack from above.
1889 if (unlikely(shutdown_state)) {
1890 jm->shutting_down = true;
1891 }
1892
1893 /* This is down here to prevent infinite recursion due to a process
1894 * attaching to its parent through ptrace(3) -- causing a cycle in the
1895 * process tree and thereby not making it a tree anymore. We need to make
1896 * sure that the anonymous job has been added to the process list so that
1897 * we'll find the tracing parent PID of the parent process, which is the
1898 * child, when we go looking for it in jobmgr_find_by_pid().
1899 *
1900 * <rdar://problem/7264615>
1901 */
1902 switch (proc.pbsi_ppid) {
1903 case 0:
1904 // The kernel.
1905 break;
1906 case 1:
1907 if (!pid1_magic) {
1908 break;
1909 }
1910 // Fall through.
1911 default:
1912 jp = jobmgr_find_by_pid(jm, proc.pbsi_ppid, true);
1913 if (jobmgr_assumes(jm, jp != NULL)) {
1914 if (jp && !jp->anonymous && unlikely(!(proc.pbsi_flags & P_EXEC))) {
1915 job_log(jp, LOG_DEBUG, "Called *fork(). Please switch to posix_spawn*(), pthreads or launchd. Child PID %u", proc.pbsi_pid);
1916 }
1917 }
1918 break;
1919 }
1920
1921 return jr;
1922 }
1923
1924 job_t
job_new_subjob(job_t j,uuid_t identifier)1925 job_new_subjob(job_t j, uuid_t identifier)
1926 {
1927 char label[0];
1928 uuid_string_t idstr;
1929 uuid_unparse(identifier, idstr);
1930 size_t label_sz = snprintf(label, 0, "%s.%s", j->label, idstr);
1931
1932 job_t nj = (struct job_s *)calloc(1, sizeof(struct job_s) + label_sz + 1);
1933 if (nj != NULL) {
1934 nj->kqjob_callback = job_callback;
1935 nj->original = j;
1936 nj->mgr = j->mgr;
1937 nj->min_run_time = j->min_run_time;
1938 nj->timeout = j->timeout;
1939 nj->exit_timeout = j->exit_timeout;
1940
1941 snprintf((char *)nj->label, label_sz + 1, "%s.%s", j->label, idstr);
1942
1943 // Set all our simple Booleans that are applicable.
1944 nj->debug = j->debug;
1945 nj->ondemand = j->ondemand;
1946 nj->checkedin = true;
1947 nj->low_pri_io = j->low_pri_io;
1948 nj->setmask = j->setmask;
1949 nj->wait4debugger = j->wait4debugger;
1950 nj->internal_exc_handler = j->internal_exc_handler;
1951 nj->setnice = j->setnice;
1952 nj->abandon_pg = j->abandon_pg;
1953 nj->ignore_pg_at_shutdown = j->ignore_pg_at_shutdown;
1954 nj->deny_job_creation = j->deny_job_creation;
1955 nj->enable_transactions = j->enable_transactions;
1956 nj->needs_kickoff = j->needs_kickoff;
1957 nj->currently_ignored = true;
1958 nj->dedicated_instance = true;
1959 nj->xpc_service = j->xpc_service;
1960 nj->xpc_bootstrapper = j->xpc_bootstrapper;
1961 nj->jetsam_priority = j->jetsam_priority;
1962 nj->jetsam_memlimit = j->jetsam_memlimit;
1963 nj->psproctype = j->psproctype;
1964
1965 nj->mask = j->mask;
1966 uuid_copy(nj->instance_id, identifier);
1967
1968 // These jobs are purely on-demand Mach jobs.
1969 // {Hard | Soft}ResourceLimits are not supported.
1970 // JetsamPriority is not supported.
1971
1972 if (j->prog) {
1973 nj->prog = strdup(j->prog);
1974 }
1975 if (j->argv) {
1976 size_t sz = malloc_size(j->argv);
1977 nj->argv = (char **)malloc(sz);
1978 if (nj->argv != NULL) {
1979 // This is the start of our strings.
1980 char *p = ((char *)nj->argv) + ((j->argc + 1) * sizeof(char *));
1981
1982 size_t i = 0;
1983 for (i = 0; i < j->argc; i++) {
1984 (void)strcpy(p, j->argv[i]);
1985 nj->argv[i] = p;
1986 p += (strlen(j->argv[i]) + 1);
1987 }
1988 nj->argv[i] = NULL;
1989 } else {
1990 (void)job_assumes_zero(nj, errno);
1991 }
1992
1993 nj->argc = j->argc;
1994 }
1995
1996 struct machservice *msi = NULL;
1997 SLIST_FOREACH(msi, &j->machservices, sle) {
1998 /* Only copy MachServices that were actually declared in the plist.
1999 * So skip over per-PID ones and ones that were created via
2000 * bootstrap_register().
2001 */
2002 if (msi->upfront) {
2003 mach_port_t mp = MACH_PORT_NULL;
2004 struct machservice *msj = machservice_new(nj, msi->name, &mp, false);
2005 if (msj != NULL) {
2006 msj->reset = msi->reset;
2007 msj->delete_on_destruction = msi->delete_on_destruction;
2008 msj->drain_one_on_crash = msi->drain_one_on_crash;
2009 msj->drain_all_on_crash = msi->drain_all_on_crash;
2010
2011 kern_return_t kr = mach_port_set_attributes(mach_task_self(), msj->port, MACH_PORT_TEMPOWNER, NULL, 0);
2012 (void)job_assumes_zero(j, kr);
2013 } else {
2014 (void)job_assumes_zero(nj, errno);
2015 }
2016 }
2017 }
2018
2019 // We ignore global environment variables.
2020 struct envitem *ei = NULL;
2021 SLIST_FOREACH(ei, &j->env, sle) {
2022 if (envitem_new(nj, ei->key, ei->value, false)) {
2023 (void)job_assumes_zero(nj, errno);
2024 }
2025 }
2026 uuid_string_t val;
2027 uuid_unparse(identifier, val);
2028 if (envitem_new(nj, LAUNCH_ENV_INSTANCEID, val, false)) {
2029 (void)job_assumes_zero(nj, errno);
2030 }
2031
2032 if (j->rootdir) {
2033 nj->rootdir = strdup(j->rootdir);
2034 }
2035 if (j->workingdir) {
2036 nj->workingdir = strdup(j->workingdir);
2037 }
2038 if (j->username) {
2039 nj->username = strdup(j->username);
2040 }
2041 if (j->groupname) {
2042 nj->groupname = strdup(j->groupname);
2043 }
2044
2045 /* FIXME: We shouldn't redirect all the output from these jobs to the
2046 * same file. We should uniquify the file names. But this hasn't shown
2047 * to be a problem in practice.
2048 */
2049 if (j->stdinpath) {
2050 nj->stdinpath = strdup(j->stdinpath);
2051 }
2052 if (j->stdoutpath) {
2053 nj->stdoutpath = strdup(j->stdinpath);
2054 }
2055 if (j->stderrpath) {
2056 nj->stderrpath = strdup(j->stderrpath);
2057 }
2058 if (j->alt_exc_handler) {
2059 nj->alt_exc_handler = strdup(j->alt_exc_handler);
2060 }
2061 if (j->cfbundleidentifier) {
2062 nj->cfbundleidentifier = strdup(j->cfbundleidentifier);
2063 }
2064 #if HAVE_SANDBOX
2065 if (j->seatbelt_profile) {
2066 nj->seatbelt_profile = strdup(j->seatbelt_profile);
2067 }
2068 if (j->container_identifier) {
2069 nj->container_identifier = strdup(j->container_identifier);
2070 }
2071 #endif
2072
2073 #if HAVE_QUARANTINE
2074 if (j->quarantine_data) {
2075 nj->quarantine_data = strdup(j->quarantine_data);
2076 }
2077 nj->quarantine_data_sz = j->quarantine_data_sz;
2078 #endif
2079 if (j->j_binpref) {
2080 size_t sz = malloc_size(j->j_binpref);
2081 nj->j_binpref = (cpu_type_t *)malloc(sz);
2082 if (nj->j_binpref) {
2083 memcpy(&nj->j_binpref, &j->j_binpref, sz);
2084 } else {
2085 (void)job_assumes_zero(nj, errno);
2086 }
2087 }
2088
2089 if (j->asport != MACH_PORT_NULL) {
2090 (void)job_assumes_zero(nj, launchd_mport_copy_send(j->asport));
2091 nj->asport = j->asport;
2092 }
2093
2094 LIST_INSERT_HEAD(&nj->mgr->jobs, nj, sle);
2095
2096 jobmgr_t where2put = root_jobmgr;
2097 if (j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN) {
2098 where2put = j->mgr;
2099 }
2100 LIST_INSERT_HEAD(&where2put->label_hash[hash_label(nj->label)], nj, label_hash_sle);
2101 LIST_INSERT_HEAD(&j->subjobs, nj, subjob_sle);
2102 } else {
2103 (void)os_assumes_zero(errno);
2104 }
2105
2106 return nj;
2107 }
2108
2109 job_t
job_new(jobmgr_t jm,const char * label,const char * prog,const char * const * argv)2110 job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *argv)
2111 {
2112 const char *const *argv_tmp = argv;
2113 char tmp_path[PATH_MAX];
2114 char auto_label[1000];
2115 const char *bn = NULL;
2116 char *co;
2117 size_t minlabel_len;
2118 size_t i, cc = 0;
2119 job_t j;
2120
2121 __OS_COMPILETIME_ASSERT__(offsetof(struct job_s, kqjob_callback) == 0);
2122
2123 if (unlikely(jm->shutting_down)) {
2124 errno = EINVAL;
2125 return NULL;
2126 }
2127
2128 if (unlikely(prog == NULL && argv == NULL)) {
2129 errno = EINVAL;
2130 return NULL;
2131 }
2132
2133 /* I'd really like to redo this someday. Anonymous jobs carry all the
2134 * baggage of managed jobs with them, even though most of it is unused.
2135 * Maybe when we have Objective-C objects in libSystem, there can be a base
2136 * job type that anonymous and managed jobs inherit from...
2137 */
2138 const char *anon_or_legacy = (label == AUTO_PICK_ANONYMOUS_LABEL) ? "anonymous" : "mach_init";
2139 if (unlikely(label == AUTO_PICK_LEGACY_LABEL || label == AUTO_PICK_ANONYMOUS_LABEL)) {
2140 if (prog) {
2141 bn = prog;
2142 } else {
2143 strlcpy(tmp_path, argv[0], sizeof(tmp_path));
2144 // prog for auto labels is kp.kp_kproc.p_comm.
2145 bn = basename(tmp_path);
2146 }
2147
2148 (void)snprintf(auto_label, sizeof(auto_label), "%s.%s.%s", sizeof(void *) == 8 ? "0xdeadbeeffeedface" : "0xbabecafe", anon_or_legacy, bn);
2149 label = auto_label;
2150 /* This is so we can do gross things later. See NOTE_EXEC for anonymous
2151 * jobs.
2152 */
2153 minlabel_len = strlen(label) + MAXCOMLEN;
2154 } else {
2155 if (label == AUTO_PICK_XPC_LABEL) {
2156 minlabel_len = snprintf(auto_label, sizeof(auto_label), "com.apple.xpc.domain-owner.%s", jm->owner);
2157 } else {
2158 minlabel_len = strlen(label);
2159 }
2160 }
2161
2162 j = calloc(1, sizeof(struct job_s) + minlabel_len + 1);
2163
2164 if (!j) {
2165 (void)os_assumes_zero(errno);
2166 return NULL;
2167 }
2168
2169 if (unlikely(label == auto_label)) {
2170 (void)snprintf((char *)j->label, strlen(label) + 1, "%p.%s.%s", j, anon_or_legacy, bn);
2171 } else {
2172 (void)strcpy((char *)j->label, (label == AUTO_PICK_XPC_LABEL) ? auto_label : label);
2173 }
2174
2175 j->kqjob_callback = job_callback;
2176 j->mgr = jm;
2177 j->min_run_time = LAUNCHD_MIN_JOB_RUN_TIME;
2178 j->timeout = RUNTIME_ADVISABLE_IDLE_TIMEOUT;
2179 if (uflag == true)
2180 j->exit_timeout = 60;
2181 else
2182 j->exit_timeout = LAUNCHD_DEFAULT_EXIT_TIMEOUT;
2183 j->currently_ignored = true;
2184 j->ondemand = true;
2185 j->checkedin = true;
2186 j->jetsam_priority = DEFAULT_JETSAM_PRIORITY;
2187 j->jetsam_memlimit = -1;
2188 uuid_clear(j->expected_audit_uuid);
2189 #if TARGET_OS_EMBEDDED
2190 /* Run embedded daemons as background by default. SpringBoard jobs are
2191 * Interactive by default. Unfortunately, so many daemons have opted into
2192 * this priority band that its usefulness is highly questionable.
2193 *
2194 * See <rdar://problem/9539873>.
2195 *
2196 * Also ensure that daemons have a default memory highwatermark unless
2197 * otherwise specified, as per <rdar://problem/10307814>.
2198 */
2199 if (launchd_embedded_handofgod) {
2200 j->psproctype = POSIX_SPAWN_PROC_TYPE_APP_DEFAULT;
2201 j->app = true;
2202 } else {
2203 j->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_BACKGROUND;
2204 j->jetsam_memlimit = DEFAULT_JETSAM_DAEMON_HIGHWATERMARK;
2205 }
2206 #else
2207 /* Jobs on OS X that just come from disk are "standard" by default so that
2208 * third-party daemons/agents don't encounter unexpected throttling.
2209 */
2210 j->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_STANDARD;
2211 #endif
2212
2213 if (prog) {
2214 j->prog = strdup(prog);
2215 if (!j->prog) {
2216 (void)os_assumes_zero(errno);
2217 goto out_bad;
2218 }
2219 }
2220
2221 if (likely(argv)) {
2222 while (*argv_tmp++) {
2223 j->argc++;
2224 }
2225
2226 for (i = 0; i < j->argc; i++) {
2227 cc += strlen(argv[i]) + 1;
2228 }
2229
2230 j->argv = malloc((j->argc + 1) * sizeof(char *) + cc);
2231 if (!j->argv) {
2232 (void)job_assumes_zero(j, errno);
2233 goto out_bad;
2234 }
2235
2236 co = ((char *)j->argv) + ((j->argc + 1) * sizeof(char *));
2237
2238 for (i = 0; i < j->argc; i++) {
2239 j->argv[i] = co;
2240 (void)strcpy(co, argv[i]);
2241 co += strlen(argv[i]) + 1;
2242 }
2243 j->argv[i] = NULL;
2244 }
2245
2246 // Sssshhh... don't tell anyone.
2247 if (strcmp(j->label, "com.apple.WindowServer") == 0) {
2248 j->has_console = true;
2249 }
2250
2251 LIST_INSERT_HEAD(&jm->jobs, j, sle);
2252
2253 jobmgr_t where2put_label = root_jobmgr;
2254 if (j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN) {
2255 where2put_label = j->mgr;
2256 }
2257 LIST_INSERT_HEAD(&where2put_label->label_hash[hash_label(j->label)], j, label_hash_sle);
2258 uuid_clear(j->expected_audit_uuid);
2259
2260 job_log(j, LOG_DEBUG, "Conceived");
2261
2262 return j;
2263
2264 out_bad:
2265 if (j->prog) {
2266 free(j->prog);
2267 }
2268 free(j);
2269
2270 return NULL;
2271 }
2272
2273 job_t
job_new_alias(jobmgr_t jm,job_t src)2274 job_new_alias(jobmgr_t jm, job_t src)
2275 {
2276 if (job_find(jm, src->label)) {
2277 errno = EEXIST;
2278 return NULL;
2279 }
2280
2281 job_t j = calloc(1, sizeof(struct job_s) + strlen(src->label) + 1);
2282 if (!j) {
2283 (void)os_assumes_zero(errno);
2284 return NULL;
2285 }
2286
2287 (void)strcpy((char *)j->label, src->label);
2288 LIST_INSERT_HEAD(&jm->jobs, j, sle);
2289 LIST_INSERT_HEAD(&jm->label_hash[hash_label(j->label)], j, label_hash_sle);
2290 /* Bad jump address. The kqueue callback for aliases should never be
2291 * invoked.
2292 */
2293 j->kqjob_callback = (kq_callback)0xfa1afe1;
2294 j->alias = src;
2295 j->mgr = jm;
2296
2297 struct machservice *msi = NULL;
2298 SLIST_FOREACH(msi, &src->machservices, sle) {
2299 if (!machservice_new_alias(j, msi)) {
2300 jobmgr_log(jm, LOG_ERR, "Failed to alias job: %s", src->label);
2301 errno = EINVAL;
2302 job_remove(j);
2303 j = NULL;
2304 break;
2305 }
2306 }
2307
2308 if (j) {
2309 job_log(j, LOG_DEBUG, "Aliased service into domain: %s", jm->name);
2310 }
2311
2312 return j;
2313 }
2314
2315 job_t
job_import(launch_data_t pload)2316 job_import(launch_data_t pload)
2317 {
2318 #if TARGET_OS_EMBEDDED
2319 /* If this is the special payload of default values, handle it here */
2320 if (unlikely(launch_data_dict_lookup(pload, LAUNCH_JOBKEY_DEFAULTS))) {
2321 job_import_defaults(pload);
2322 return NULL;
2323 }
2324 #endif
2325
2326 job_t j = jobmgr_import2(root_jobmgr, pload);
2327
2328 if (unlikely(j == NULL)) {
2329 return NULL;
2330 }
2331
2332 /* Since jobs are effectively stalled until they get security sessions
2333 * assigned to them, we may wish to reconsider this behavior of calling the
2334 * job "enabled" as far as other jobs with the OtherJobEnabled KeepAlive
2335 * criterion set.
2336 */
2337 job_dispatch_curious_jobs(j);
2338 return job_dispatch(j, false);
2339 }
2340
2341 #if TARGET_OS_EMBEDDED
2342
2343 bool
job_import_defaults(launch_data_t pload)2344 job_import_defaults(launch_data_t pload)
2345 {
2346 bool result = false;
2347 xpc_object_t xd = NULL, defaults;
2348
2349 if (_launchd_defaults_cache) {
2350 xpc_release(_launchd_defaults_cache);
2351 _launchd_defaults_cache = NULL;
2352 }
2353
2354 xd = ld2xpc(pload);
2355 if (!xd || xpc_get_type(xd) != XPC_TYPE_DICTIONARY) {
2356 goto out;
2357 }
2358
2359 defaults = xpc_dictionary_get_value(xd, LAUNCHD_JOB_DEFAULTS);
2360 if (!defaults || xpc_get_type(defaults) != XPC_TYPE_DICTIONARY) {
2361 goto out;
2362 }
2363
2364 _launchd_defaults_cache = xpc_copy(defaults);
2365 result = true;
2366 out:
2367 if (xd) {
2368 xpc_release(xd);
2369 }
2370
2371 return result;
2372 }
2373
2374 bool
job_apply_defaults(job_t j)2375 job_apply_defaults(job_t j) {
2376 const char *test_prefix = "com.apple.test.";
2377
2378 char *sb_prefix_end, *sb_suffix_start;
2379 char true_job_label[strlen(j->label)];
2380 const char *label;
2381
2382 if (((sb_prefix_end = strchr(j->label, ':')) != NULL) &&
2383 ((sb_suffix_start = strchr(sb_prefix_end + 1, '[')) != NULL)) {
2384 /*
2385 * Workaround 'UIKitApplication:com.apple.foo[bar]' convention for the processes
2386 * we're interested in. To be removed when <rdar://problem/13066361> is addressed.
2387 */
2388 snprintf(true_job_label, sb_suffix_start - sb_prefix_end, "%s", sb_prefix_end + 1);
2389 label = true_job_label;
2390 } else {
2391 /* Just test the standard label */
2392 label = j->label;
2393 }
2394
2395 /* Test for cache presence and apply if found */
2396 if (_launchd_defaults_cache) {
2397 xpc_object_t props = xpc_dictionary_get_value(_launchd_defaults_cache, label);
2398 if (props && xpc_get_type(props) == XPC_TYPE_DICTIONARY) {
2399 launch_data_t lv = xpc2ld(props);
2400 launch_data_dict_iterate(lv, job_import_keys, j);
2401 launch_data_free(lv);
2402 return true;
2403 }
2404 }
2405
2406 /* Limit free? Disable the memory limit if this is a test job; see <rdar://problem/13180697> */
2407 if (!strncmp(label, test_prefix, strlen(test_prefix))) {
2408 j->jetsam_memlimit = -1;
2409 return true;
2410 }
2411
2412 return false;
2413 }
2414
2415 #endif
2416
2417 launch_data_t
job_import_bulk(launch_data_t pload)2418 job_import_bulk(launch_data_t pload)
2419 {
2420 launch_data_t resp = launch_data_alloc(LAUNCH_DATA_ARRAY);
2421 job_t *ja;
2422 size_t i, c = launch_data_array_get_count(pload);
2423
2424 ja = alloca(c * sizeof(job_t));
2425
2426 for (i = 0; i < c; i++) {
2427 if ((likely(ja[i] = jobmgr_import2(root_jobmgr, launch_data_array_get_index(pload, i)))) && errno != ENEEDAUTH) {
2428 errno = 0;
2429 }
2430 launch_data_array_set_index(resp, launch_data_new_errno(errno), i);
2431 }
2432
2433 for (i = 0; i < c; i++) {
2434 if (likely(ja[i])) {
2435 job_dispatch_curious_jobs(ja[i]);
2436 job_dispatch(ja[i], false);
2437 }
2438 }
2439
2440 return resp;
2441 }
2442
2443 void
job_import_bool(job_t j,const char * key,bool value)2444 job_import_bool(job_t j, const char *key, bool value)
2445 {
2446 bool found_key = false;
2447
2448 switch (key[0]) {
2449 case 'a':
2450 case 'A':
2451 if (strcasecmp(key, LAUNCH_JOBKEY_ABANDONPROCESSGROUP) == 0) {
2452 j->abandon_pg = value;
2453 found_key = true;
2454 }
2455 break;
2456 case 'b':
2457 case 'B':
2458 if (strcasecmp(key, LAUNCH_JOBKEY_BEGINTRANSACTIONATSHUTDOWN) == 0) {
2459 j->dirty_at_shutdown = value;
2460 found_key = true;
2461 }
2462 break;
2463 case 'j':
2464 case 'J':
2465 if (strcasecmp(key, LAUNCH_JOBKEY_JOINGUISESSION) == 0) {
2466 j->joins_gui_session = value;
2467 found_key = true;
2468 }
2469 break;
2470 case 'k':
2471 case 'K':
2472 if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE) == 0) {
2473 j->ondemand = !value;
2474 found_key = true;
2475 }
2476 break;
2477 case 'o':
2478 case 'O':
2479 if (strcasecmp(key, LAUNCH_JOBKEY_ONDEMAND) == 0) {
2480 j->ondemand = value;
2481 found_key = true;
2482 }
2483 break;
2484 case 'd':
2485 case 'D':
2486 if (strcasecmp(key, LAUNCH_JOBKEY_DEBUG) == 0) {
2487 j->debug = value;
2488 found_key = true;
2489 } else if (strcasecmp(key, LAUNCH_JOBKEY_DISABLED) == 0) {
2490 (void)job_assumes(j, !value);
2491 found_key = true;
2492 } else if (strcasecmp(key, LAUNCH_JOBKEY_DISABLEASLR) == 0) {
2493 j->disable_aslr = value;
2494 found_key = true;
2495 }
2496 break;
2497 case 'h':
2498 case 'H':
2499 if (strcasecmp(key, LAUNCH_JOBKEY_HOPEFULLYEXITSLAST) == 0) {
2500 job_log(j, LOG_PERF, "%s has been deprecated. Please use the new %s key instead and add EnableTransactions to your launchd.plist.", LAUNCH_JOBKEY_HOPEFULLYEXITSLAST, LAUNCH_JOBKEY_BEGINTRANSACTIONATSHUTDOWN);
2501 j->dirty_at_shutdown = value;
2502 found_key = true;
2503 }
2504 break;
2505 case 's':
2506 case 'S':
2507 if (strcasecmp(key, LAUNCH_JOBKEY_SESSIONCREATE) == 0) {
2508 j->session_create = value;
2509 found_key = true;
2510 } else if (strcasecmp(key, LAUNCH_JOBKEY_STARTONMOUNT) == 0) {
2511 j->start_on_mount = value;
2512 found_key = true;
2513 } else if (strcasecmp(key, LAUNCH_JOBKEY_SERVICEIPC) == 0) {
2514 // this only does something on Mac OS X 10.4 "Tiger"
2515 found_key = true;
2516 } else if (strcasecmp(key, LAUNCH_JOBKEY_SHUTDOWNMONITOR) == 0) {
2517 if (_launchd_shutdown_monitor) {
2518 job_log(j, LOG_ERR, "Only one job may monitor shutdown.");
2519 } else {
2520 j->shutdown_monitor = true;
2521 _launchd_shutdown_monitor = j;
2522 }
2523 found_key = true;
2524 }
2525 break;
2526 case 'l':
2527 case 'L':
2528 if (strcasecmp(key, LAUNCH_JOBKEY_LOWPRIORITYIO) == 0) {
2529 j->low_pri_io = value;
2530 found_key = true;
2531 } else if (strcasecmp(key, LAUNCH_JOBKEY_LAUNCHONLYONCE) == 0) {
2532 j->only_once = value;
2533 found_key = true;
2534 } else if (strcasecmp(key, LAUNCH_JOBKEY_LOWPRIORITYBACKGROUNDIO) == 0) {
2535 j->low_priority_background_io = true;
2536 found_key = true;
2537 } else if (strcasecmp(key, LAUNCH_JOBKEY_LEGACYTIMERS) == 0) {
2538 #if !TARGET_OS_EMBEDDED
2539 j->legacy_timers = value;
2540 #else // !TARGET_OS_EMBEDDED
2541 job_log(j, LOG_ERR, "This key is not supported on this platform: %s", key);
2542 #endif // !TARGET_OS_EMBEDDED
2543 found_key = true;
2544 }
2545 break;
2546 case 'm':
2547 case 'M':
2548 if (strcasecmp(key, LAUNCH_JOBKEY_MACHEXCEPTIONHANDLER) == 0) {
2549 j->internal_exc_handler = value;
2550 found_key = true;
2551 } else if (strcasecmp(key, LAUNCH_JOBKEY_MULTIPLEINSTANCES) == 0) {
2552 j->multiple_instances = value;
2553 found_key = true;
2554 }
2555 break;
2556 case 'i':
2557 case 'I':
2558 if (strcasecmp(key, LAUNCH_JOBKEY_INITGROUPS) == 0) {
2559 if (getuid() != 0) {
2560 job_log(j, LOG_WARNING, "Ignored this key: %s", key);
2561 return;
2562 }
2563 j->no_init_groups = !value;
2564 found_key = true;
2565 } else if (strcasecmp(key, LAUNCH_JOBKEY_IGNOREPROCESSGROUPATSHUTDOWN) == 0) {
2566 j->ignore_pg_at_shutdown = value;
2567 found_key = true;
2568 }
2569 break;
2570 case 'r':
2571 case 'R':
2572 if (strcasecmp(key, LAUNCH_JOBKEY_RUNATLOAD) == 0) {
2573 if (value) {
2574 // We don't want value == false to change j->start_pending
2575 j->start_pending = true;
2576 }
2577 found_key = true;
2578 }
2579 break;
2580 case 'e':
2581 case 'E':
2582 if (strcasecmp(key, LAUNCH_JOBKEY_ENABLEGLOBBING) == 0) {
2583 j->globargv = value;
2584 found_key = true;
2585 } else if (strcasecmp(key, LAUNCH_JOBKEY_ENABLETRANSACTIONS) == 0) {
2586 j->enable_transactions = value;
2587 found_key = true;
2588 } else if (strcasecmp(key, LAUNCH_JOBKEY_ENTERKERNELDEBUGGERBEFOREKILL) == 0) {
2589 j->debug_before_kill = value;
2590 found_key = true;
2591 } else if (strcasecmp(key, LAUNCH_JOBKEY_EMBEDDEDPRIVILEGEDISPENSATION) == 0) {
2592 #if TARGET_OS_EMBEDDED
2593 if (!_launchd_embedded_god) {
2594 if ((j->embedded_god = value)) {
2595 _launchd_embedded_god = j;
2596 }
2597 } else {
2598 job_log(j, LOG_ERR, "Job tried to claim %s after it has already been claimed.", key);
2599 }
2600 #else
2601 job_log(j, LOG_ERR, "This key is not supported on this platform: %s", key);
2602 #endif
2603 found_key = true;
2604 } else if (strcasecmp(key, LAUNCH_JOBKEY_EMBEDDEDHOMESCREEN) == 0) {
2605 #if TARGET_OS_EMBEDDED
2606 if (!_launchd_embedded_home) {
2607 if ((j->embedded_home = value)) {
2608 _launchd_embedded_home = j;
2609 }
2610 } else {
2611 job_log(j, LOG_ERR, "Job tried to claim %s after it has already been claimed.", key);
2612 }
2613 #else
2614 job_log(j, LOG_ERR, "This key is not supported on this platform: %s", key);
2615 #endif
2616 } else if (strcasecmp(key, LAUNCH_JOBKEY_EVENTMONITOR) == 0) {
2617 if (!_launchd_event_monitor) {
2618 j->event_monitor = value;
2619 if (value) {
2620 _launchd_event_monitor = j;
2621 }
2622 } else {
2623 job_log(j, LOG_NOTICE, "Job tried to steal event monitoring responsibility from: %s", _launchd_event_monitor->label);
2624 }
2625 found_key = true;
2626 }
2627 break;
2628 case 'w':
2629 case 'W':
2630 if (strcasecmp(key, LAUNCH_JOBKEY_WAITFORDEBUGGER) == 0) {
2631 j->wait4debugger = value;
2632 found_key = true;
2633 }
2634 break;
2635 case 'x':
2636 case 'X':
2637 if (strcasecmp(key, LAUNCH_JOBKEY_XPCDOMAINBOOTSTRAPPER) == 0) {
2638 if (pid1_magic) {
2639 if (_launchd_xpc_bootstrapper) {
2640 job_log(j, LOG_ERR, "This job tried to steal the XPC domain bootstrapper property from the following job: %s", _launchd_xpc_bootstrapper->label);
2641 } else {
2642 _launchd_xpc_bootstrapper = j;
2643 j->xpc_bootstrapper = value;
2644 }
2645 } else {
2646 job_log(j, LOG_ERR, "Non-daemon tried to claim XPC bootstrapper property.");
2647 }
2648 }
2649 found_key = true;
2650 break;
2651 default:
2652 break;
2653 }
2654
2655 if (unlikely(!found_key)) {
2656 job_log(j, LOG_WARNING, "Unknown key for boolean: %s", key);
2657 }
2658 }
2659
2660 void
job_import_string(job_t j,const char * key,const char * value)2661 job_import_string(job_t j, const char *key, const char *value)
2662 {
2663 char **where2put = NULL;
2664
2665 switch (key[0]) {
2666 case 'c':
2667 case 'C':
2668 if (strcasecmp(key, LAUNCH_JOBKEY_CFBUNDLEIDENTIFIER) == 0) {
2669 where2put = &j->cfbundleidentifier;
2670 }
2671 break;
2672 case 'm':
2673 case 'M':
2674 if (strcasecmp(key, LAUNCH_JOBKEY_MACHEXCEPTIONHANDLER) == 0) {
2675 where2put = &j->alt_exc_handler;
2676 }
2677 break;
2678 case 'p':
2679 case 'P':
2680 if (strcasecmp(key, LAUNCH_JOBKEY_PROGRAM) == 0) {
2681 return;
2682 } else if (strcasecmp(key, LAUNCH_JOBKEY_POSIXSPAWNTYPE) == 0
2683 || strcasecmp(key, LAUNCH_JOBKEY_PROCESSTYPE) == 0) {
2684 if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_INTERACTIVE) == 0) {
2685 j->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_INTERACTIVE;
2686 } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_ADAPTIVE) == 0) {
2687 j->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_ADAPTIVE;
2688 } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_STANDARD) == 0) {
2689 j->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_STANDARD;
2690 } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_BACKGROUND) == 0) {
2691 j->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_BACKGROUND;
2692 } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_TALAPP) == 0) {
2693 j->psproctype = POSIX_SPAWN_PROC_TYPE_APP_TAL;
2694 } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_SYSTEMAPP) == 0) {
2695 j->psproctype = POSIX_SPAWN_PROC_TYPE_APP_DEFAULT;
2696 j->system_app = true;
2697 } else if (strcasecmp(value, LAUNCH_KEY_POSIXSPAWNTYPE_APP) == 0) {
2698 j->psproctype = POSIX_SPAWN_PROC_TYPE_APP_DEFAULT;
2699 j->app = true;
2700 } else {
2701 job_log(j, LOG_ERR, "Unknown value for key %s: %s", key, value);
2702 }
2703 return;
2704 }
2705 break;
2706 case 'l':
2707 case 'L':
2708 if (strcasecmp(key, LAUNCH_JOBKEY_LABEL) == 0) {
2709 return;
2710 } else if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADTOHOSTS) == 0) {
2711 return;
2712 } else if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS) == 0) {
2713 return;
2714 } else if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE) == 0) {
2715 return;
2716 }
2717 break;
2718 case 'r':
2719 case 'R':
2720 if (strcasecmp(key, LAUNCH_JOBKEY_ROOTDIRECTORY) == 0) {
2721 if (getuid() != 0) {
2722 job_log(j, LOG_WARNING, "Ignored this key: %s", key);
2723 return;
2724 }
2725 where2put = &j->rootdir;
2726 }
2727 break;
2728 case 'w':
2729 case 'W':
2730 if (strcasecmp(key, LAUNCH_JOBKEY_WORKINGDIRECTORY) == 0) {
2731 where2put = &j->workingdir;
2732 }
2733 break;
2734 case 'u':
2735 case 'U':
2736 if (strcasecmp(key, LAUNCH_JOBKEY_USERNAME) == 0) {
2737 if (getuid() != 0) {
2738 job_log(j, LOG_WARNING, "Ignored this key: %s", key);
2739 return;
2740 } else if (strcmp(value, "root") == 0) {
2741 return;
2742 }
2743 where2put = &j->username;
2744 }
2745 break;
2746 case 'g':
2747 case 'G':
2748 if (strcasecmp(key, LAUNCH_JOBKEY_GROUPNAME) == 0) {
2749 if (getuid() != 0) {
2750 job_log(j, LOG_WARNING, "Ignored this key: %s", key);
2751 return;
2752 } else if (strcmp(value, "wheel") == 0) {
2753 return;
2754 }
2755 where2put = &j->groupname;
2756 }
2757 break;
2758 case 's':
2759 case 'S':
2760 if (strcasecmp(key, LAUNCH_JOBKEY_STANDARDOUTPATH) == 0) {
2761 where2put = &j->stdoutpath;
2762 } else if (strcasecmp(key, LAUNCH_JOBKEY_STANDARDERRORPATH) == 0) {
2763 where2put = &j->stderrpath;
2764 } else if (strcasecmp(key, LAUNCH_JOBKEY_STANDARDINPATH) == 0) {
2765 where2put = &j->stdinpath;
2766 j->stdin_fd = _fd(open(value, O_RDONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, DEFFILEMODE));
2767 if (job_assumes_zero_p(j, j->stdin_fd) != -1) {
2768 // open() should not block, but regular IO by the job should
2769 (void)job_assumes_zero_p(j, fcntl(j->stdin_fd, F_SETFL, 0));
2770 // XXX -- EV_CLEAR should make named pipes happy?
2771 (void)job_assumes_zero_p(j, kevent_mod(j->stdin_fd, EVFILT_READ, EV_ADD|EV_CLEAR, 0, 0, j));
2772 } else {
2773 j->stdin_fd = 0;
2774 }
2775 #if HAVE_SANDBOX
2776 } else if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOXPROFILE) == 0) {
2777 where2put = &j->seatbelt_profile;
2778 } else if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOXCONTAINER) == 0) {
2779 where2put = &j->container_identifier;
2780 #endif
2781 }
2782 break;
2783 case 'X':
2784 case 'x':
2785 if (strcasecmp(key, LAUNCH_JOBKEY_XPCDOMAIN) == 0) {
2786 return;
2787 }
2788 break;
2789 default:
2790 job_log(j, LOG_WARNING, "Unknown key for string: %s", key);
2791 break;
2792 }
2793
2794 if (likely(where2put)) {
2795 if (!(*where2put = strdup(value))) {
2796 (void)job_assumes_zero(j, errno);
2797 }
2798 } else {
2799 // See rdar://problem/5496612. These two are okay.
2800 if (strncmp(key, "SHAuthorizationRight", sizeof("SHAuthorizationRight")) == 0
2801 || strncmp(key, "ServiceDescription", sizeof("ServiceDescription")) == 0) {
2802 job_log(j, LOG_APPLEONLY, "This key is no longer relevant and should be removed: %s", key);
2803 } else {
2804 job_log(j, LOG_WARNING, "Unknown key: %s", key);
2805 }
2806 }
2807 }
2808
2809 void
job_import_integer(job_t j,const char * key,long long value)2810 job_import_integer(job_t j, const char *key, long long value)
2811 {
2812 switch (key[0]) {
2813 case 'a':
2814 case 'A':
2815 #if TARGET_OS_EMBEDDED
2816 if (strcasecmp(key, LAUNCH_JOBKEY_ASID) == 0) {
2817 if (launchd_embedded_handofgod) {
2818 if (audit_session_port((au_asid_t)value, &j->asport) == -1 && errno != ENOSYS) {
2819 (void)job_assumes_zero(j, errno);
2820 }
2821 }
2822 }
2823 #endif
2824 case 'e':
2825 case 'E':
2826 if (strcasecmp(key, LAUNCH_JOBKEY_EXITTIMEOUT) == 0) {
2827 if (unlikely(value < 0)) {
2828 job_log(j, LOG_WARNING, "%s less than zero. Ignoring.", LAUNCH_JOBKEY_EXITTIMEOUT);
2829 } else if (unlikely(value > UINT32_MAX)) {
2830 job_log(j, LOG_WARNING, "%s is too large. Ignoring.", LAUNCH_JOBKEY_EXITTIMEOUT);
2831 } else {
2832 j->exit_timeout = (typeof(j->exit_timeout)) value;
2833 }
2834 } else if (strcasecmp(key, LAUNCH_JOBKEY_EMBEDDEDMAINTHREADPRIORITY) == 0) {
2835 j->main_thread_priority = value;
2836 }
2837 break;
2838 case 'j':
2839 case 'J':
2840 if (strcasecmp(key, LAUNCH_JOBKEY_JETSAMPRIORITY) == 0) {
2841 job_log(j, LOG_WARNING | LOG_CONSOLE, "Please change the JetsamPriority key to be in a dictionary named JetsamProperties.");
2842
2843 launch_data_t pri = launch_data_new_integer(value);
2844 if (job_assumes(j, pri != NULL)) {
2845 jetsam_property_setup(pri, LAUNCH_JOBKEY_JETSAMPRIORITY, j);
2846 launch_data_free(pri);
2847 }
2848 }
2849 case 'n':
2850 case 'N':
2851 if (strcasecmp(key, LAUNCH_JOBKEY_NICE) == 0) {
2852 if (unlikely(value < PRIO_MIN)) {
2853 job_log(j, LOG_WARNING, "%s less than %d. Ignoring.", LAUNCH_JOBKEY_NICE, PRIO_MIN);
2854 } else if (unlikely(value > PRIO_MAX)) {
2855 job_log(j, LOG_WARNING, "%s is greater than %d. Ignoring.", LAUNCH_JOBKEY_NICE, PRIO_MAX);
2856 } else {
2857 j->nice = (typeof(j->nice)) value;
2858 j->setnice = true;
2859 }
2860 }
2861 break;
2862 case 't':
2863 case 'T':
2864 if (strcasecmp(key, LAUNCH_JOBKEY_TIMEOUT) == 0) {
2865 if (unlikely(value < 0)) {
2866 job_log(j, LOG_WARNING, "%s less than zero. Ignoring.", LAUNCH_JOBKEY_TIMEOUT);
2867 } else if (unlikely(value > UINT32_MAX)) {
2868 job_log(j, LOG_WARNING, "%s is too large. Ignoring.", LAUNCH_JOBKEY_TIMEOUT);
2869 } else {
2870 j->timeout = (typeof(j->timeout)) value;
2871 }
2872 } else if (strcasecmp(key, LAUNCH_JOBKEY_THROTTLEINTERVAL) == 0) {
2873 if (value < 0) {
2874 job_log(j, LOG_WARNING, "%s less than zero. Ignoring.", LAUNCH_JOBKEY_THROTTLEINTERVAL);
2875 } else if (value > UINT32_MAX) {
2876 job_log(j, LOG_WARNING, "%s is too large. Ignoring.", LAUNCH_JOBKEY_THROTTLEINTERVAL);
2877 } else {
2878 j->min_run_time = (typeof(j->min_run_time)) value;
2879 }
2880 }
2881 break;
2882 case 'u':
2883 case 'U':
2884 if (strcasecmp(key, LAUNCH_JOBKEY_UMASK) == 0) {
2885 j->mask = value;
2886 j->setmask = true;
2887 }
2888 break;
2889 case 's':
2890 case 'S':
2891 if (strcasecmp(key, LAUNCH_JOBKEY_STARTINTERVAL) == 0) {
2892 if (unlikely(value <= 0)) {
2893 job_log(j, LOG_WARNING, "%s is not greater than zero. Ignoring.", LAUNCH_JOBKEY_STARTINTERVAL);
2894 } else if (unlikely(value > UINT32_MAX)) {
2895 job_log(j, LOG_WARNING, "%s is too large. Ignoring.", LAUNCH_JOBKEY_STARTINTERVAL);
2896 } else {
2897 runtime_add_weak_ref();
2898 j->start_interval = (typeof(j->start_interval)) value;
2899
2900 (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, j->start_interval, j));
2901 }
2902 #if HAVE_SANDBOX
2903 } else if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOXFLAGS) == 0) {
2904 j->seatbelt_flags = value;
2905 #endif
2906 }
2907
2908 break;
2909 default:
2910 job_log(j, LOG_WARNING, "Unknown key for integer: %s", key);
2911 break;
2912 }
2913 }
2914
2915 void
job_import_opaque(job_t j,const char * key,launch_data_t value)2916 job_import_opaque(job_t j __attribute__((unused)), const char *key, launch_data_t value __attribute__((unused)))
2917 {
2918 switch (key[0]) {
2919 case 'q':
2920 case 'Q':
2921 #if HAVE_QUARANTINE
2922 if (strcasecmp(key, LAUNCH_JOBKEY_QUARANTINEDATA) == 0) {
2923 size_t tmpsz = launch_data_get_opaque_size(value);
2924
2925 if (job_assumes(j, j->quarantine_data = malloc(tmpsz))) {
2926 memcpy(j->quarantine_data, launch_data_get_opaque(value), tmpsz);
2927 j->quarantine_data_sz = tmpsz;
2928 }
2929 }
2930 #endif
2931 case 's':
2932 case 'S':
2933 if (strcasecmp(key, LAUNCH_JOBKEY_SECURITYSESSIONUUID) == 0) {
2934 size_t tmpsz = launch_data_get_opaque_size(value);
2935 if (job_assumes(j, tmpsz == sizeof(uuid_t))) {
2936 memcpy(j->expected_audit_uuid, launch_data_get_opaque(value), sizeof(uuid_t));
2937 }
2938 }
2939 break;
2940 default:
2941 break;
2942 }
2943 }
2944
2945 static void
policy_setup(launch_data_t obj,const char * key,void * context)2946 policy_setup(launch_data_t obj, const char *key, void *context)
2947 {
2948 job_t j = context;
2949 bool found_key = false;
2950
2951 switch (key[0]) {
2952 case 'd':
2953 case 'D':
2954 if (strcasecmp(key, LAUNCH_JOBPOLICY_DENYCREATINGOTHERJOBS) == 0) {
2955 j->deny_job_creation = launch_data_get_bool(obj);
2956 found_key = true;
2957 }
2958 break;
2959 default:
2960 break;
2961 }
2962
2963 if (unlikely(!found_key)) {
2964 job_log(j, LOG_WARNING, "Unknown policy: %s", key);
2965 }
2966 }
2967
2968 void
job_import_dictionary(job_t j,const char * key,launch_data_t value)2969 job_import_dictionary(job_t j, const char *key, launch_data_t value)
2970 {
2971 launch_data_t tmp;
2972
2973 switch (key[0]) {
2974 case 'p':
2975 case 'P':
2976 if (strcasecmp(key, LAUNCH_JOBKEY_POLICIES) == 0) {
2977 launch_data_dict_iterate(value, policy_setup, j);
2978 }
2979 break;
2980 case 'k':
2981 case 'K':
2982 if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE) == 0) {
2983 launch_data_dict_iterate(value, semaphoreitem_setup, j);
2984 }
2985 break;
2986 case 'i':
2987 case 'I':
2988 if (strcasecmp(key, LAUNCH_JOBKEY_INETDCOMPATIBILITY) == 0) {
2989 j->inetcompat = true;
2990 j->abandon_pg = true;
2991 if ((tmp = launch_data_dict_lookup(value, LAUNCH_JOBINETDCOMPATIBILITY_WAIT))) {
2992 j->inetcompat_wait = launch_data_get_bool(tmp);
2993 }
2994 }
2995 break;
2996 case 'j':
2997 case 'J':
2998 if (strcasecmp(key, LAUNCH_JOBKEY_JETSAMPROPERTIES) == 0) {
2999 launch_data_dict_iterate(value, (void (*)(launch_data_t, const char *, void *))jetsam_property_setup, j);
3000 }
3001 case 'e':
3002 case 'E':
3003 if (strcasecmp(key, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES) == 0) {
3004 launch_data_dict_iterate(value, envitem_setup, j);
3005 }
3006 break;
3007 case 'u':
3008 case 'U':
3009 if (strcasecmp(key, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES) == 0) {
3010 j->importing_global_env = true;
3011 launch_data_dict_iterate(value, envitem_setup, j);
3012 j->importing_global_env = false;
3013 }
3014 break;
3015 case 's':
3016 case 'S':
3017 if (strcasecmp(key, LAUNCH_JOBKEY_SOCKETS) == 0) {
3018 launch_data_dict_iterate(value, socketgroup_setup, j);
3019 } else if (strcasecmp(key, LAUNCH_JOBKEY_STARTCALENDARINTERVAL) == 0) {
3020 calendarinterval_new_from_obj(j, value);
3021 } else if (strcasecmp(key, LAUNCH_JOBKEY_SOFTRESOURCELIMITS) == 0) {
3022 launch_data_dict_iterate(value, limititem_setup, j);
3023 #if HAVE_SANDBOX
3024 } else if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOXFLAGS) == 0) {
3025 launch_data_dict_iterate(value, seatbelt_setup_flags, j);
3026 #endif
3027 }
3028 break;
3029 case 'h':
3030 case 'H':
3031 if (strcasecmp(key, LAUNCH_JOBKEY_HARDRESOURCELIMITS) == 0) {
3032 j->importing_hard_limits = true;
3033 launch_data_dict_iterate(value, limititem_setup, j);
3034 j->importing_hard_limits = false;
3035 }
3036 break;
3037 case 'm':
3038 case 'M':
3039 if (strcasecmp(key, LAUNCH_JOBKEY_MACHSERVICES) == 0) {
3040 launch_data_dict_iterate(value, machservice_setup, j);
3041 }
3042 break;
3043 case 'l':
3044 case 'L':
3045 if (strcasecmp(key, LAUNCH_JOBKEY_LAUNCHEVENTS) == 0) {
3046 launch_data_dict_iterate(value, eventsystem_setup, j);
3047 } else {
3048 if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADTOHARDWARE) == 0) {
3049 return;
3050 }
3051 if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADFROMHARDWARE) == 0) {
3052 return;
3053 }
3054 }
3055 break;
3056 default:
3057 job_log(j, LOG_WARNING, "Unknown key for dictionary: %s", key);
3058 break;
3059 }
3060 }
3061
3062 void
job_import_array(job_t j,const char * key,launch_data_t value)3063 job_import_array(job_t j, const char *key, launch_data_t value)
3064 {
3065 size_t i, value_cnt = launch_data_array_get_count(value);
3066
3067 switch (key[0]) {
3068 case 'p':
3069 case 'P':
3070 if (strcasecmp(key, LAUNCH_JOBKEY_PROGRAMARGUMENTS) == 0) {
3071 return;
3072 }
3073 break;
3074 case 'l':
3075 case 'L':
3076 if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADTOHOSTS) == 0) {
3077 return;
3078 } else if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS) == 0) {
3079 return;
3080 } else if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE) == 0) {
3081 job_log(j, LOG_NOTICE, "launchctl should have transformed the \"%s\" array to a string", LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE);
3082 return;
3083 }
3084 break;
3085 case 'b':
3086 case 'B':
3087 if (strcasecmp(key, LAUNCH_JOBKEY_BINARYORDERPREFERENCE) == 0) {
3088 if (job_assumes(j, j->j_binpref = malloc(value_cnt * sizeof(*j->j_binpref)))) {
3089 j->j_binpref_cnt = value_cnt;
3090 for (i = 0; i < value_cnt; i++) {
3091 j->j_binpref[i] = (cpu_type_t) launch_data_get_integer(launch_data_array_get_index(value, i));
3092 }
3093 }
3094 }
3095 break;
3096 case 's':
3097 case 'S':
3098 if (strcasecmp(key, LAUNCH_JOBKEY_STARTCALENDARINTERVAL) == 0) {
3099 for (i = 0; i < value_cnt; i++) {
3100 calendarinterval_new_from_obj(j, launch_data_array_get_index(value, i));
3101 }
3102 }
3103 break;
3104 default:
3105 job_log(j, LOG_WARNING, "Unknown key for array: %s", key);
3106 break;
3107 }
3108 }
3109
3110 void
job_import_keys(launch_data_t obj,const char * key,void * context)3111 job_import_keys(launch_data_t obj, const char *key, void *context)
3112 {
3113 job_t j = context;
3114 launch_data_type_t kind;
3115
3116 if (!obj) {
3117 launchd_syslog(LOG_ERR, "NULL object given to job_import_keys().");
3118 return;
3119 }
3120
3121 kind = launch_data_get_type(obj);
3122
3123 switch (kind) {
3124 case LAUNCH_DATA_BOOL:
3125 job_import_bool(j, key, launch_data_get_bool(obj));
3126 break;
3127 case LAUNCH_DATA_STRING:
3128 job_import_string(j, key, launch_data_get_string(obj));
3129 break;
3130 case LAUNCH_DATA_INTEGER:
3131 job_import_integer(j, key, launch_data_get_integer(obj));
3132 break;
3133 case LAUNCH_DATA_DICTIONARY:
3134 job_import_dictionary(j, key, obj);
3135 break;
3136 case LAUNCH_DATA_ARRAY:
3137 job_import_array(j, key, obj);
3138 break;
3139 case LAUNCH_DATA_OPAQUE:
3140 job_import_opaque(j, key, obj);
3141 break;
3142 default:
3143 job_log(j, LOG_WARNING, "Unknown value type '%d' for key: %s", kind, key);
3144 break;
3145 }
3146 }
3147
3148 job_t
jobmgr_import2(jobmgr_t jm,launch_data_t pload)3149 jobmgr_import2(jobmgr_t jm, launch_data_t pload)
3150 {
3151 launch_data_t tmp, ldpa;
3152 const char *label = NULL, *prog = NULL;
3153 const char **argv = NULL;
3154 job_t j;
3155
3156 if (!jobmgr_assumes(jm, pload != NULL)) {
3157 errno = EINVAL;
3158 return NULL;
3159 }
3160
3161 if (unlikely(launch_data_get_type(pload) != LAUNCH_DATA_DICTIONARY)) {
3162 errno = EINVAL;
3163 return NULL;
3164 }
3165
3166 if (unlikely(!(tmp = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_LABEL)))) {
3167 errno = EINVAL;
3168 return NULL;
3169 }
3170
3171 if (unlikely(launch_data_get_type(tmp) != LAUNCH_DATA_STRING)) {
3172 errno = EINVAL;
3173 return NULL;
3174 }
3175
3176 if (unlikely(!(label = launch_data_get_string(tmp)))) {
3177 errno = EINVAL;
3178 return NULL;
3179 }
3180
3181 #if TARGET_OS_EMBEDDED
3182 if (unlikely(launchd_embedded_handofgod && _launchd_embedded_god)) {
3183 if (unlikely(!(tmp = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_USERNAME)))) {
3184 errno = EPERM;
3185 return NULL;
3186 }
3187
3188 const char *username = NULL;
3189 if (likely(tmp && launch_data_get_type(tmp) == LAUNCH_DATA_STRING)) {
3190 username = launch_data_get_string(tmp);
3191 } else {
3192 errno = EPERM;
3193 return NULL;
3194 }
3195
3196 if (!jobmgr_assumes(jm, _launchd_embedded_god->username != NULL && username != NULL)) {
3197 errno = EPERM;
3198 return NULL;
3199 }
3200
3201 if (unlikely(strcmp(_launchd_embedded_god->username, username) != 0)) {
3202 errno = EPERM;
3203 return NULL;
3204 }
3205 } else if (launchd_embedded_handofgod) {
3206 errno = EINVAL;
3207 return NULL;
3208 }
3209 #endif
3210
3211 if ((tmp = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_PROGRAM))
3212 && (launch_data_get_type(tmp) == LAUNCH_DATA_STRING)) {
3213 prog = launch_data_get_string(tmp);
3214 }
3215
3216 int argc = 0;
3217 if ((ldpa = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_PROGRAMARGUMENTS))) {
3218 size_t i, c;
3219
3220 if (launch_data_get_type(ldpa) != LAUNCH_DATA_ARRAY) {
3221 errno = EINVAL;
3222 return NULL;
3223 }
3224
3225 c = launch_data_array_get_count(ldpa);
3226
3227 argv = alloca((c + 1) * sizeof(char *));
3228
3229 for (i = 0; i < c; i++) {
3230 tmp = launch_data_array_get_index(ldpa, i);
3231
3232 if (launch_data_get_type(tmp) != LAUNCH_DATA_STRING) {
3233 errno = EINVAL;
3234 return NULL;
3235 }
3236
3237 argv[i] = launch_data_get_string(tmp);
3238 }
3239
3240 argv[i] = NULL;
3241 argc = i;
3242 }
3243
3244 if (!prog && argc == 0) {
3245 jobmgr_log(jm, LOG_ERR, "Job specifies neither Program nor ProgramArguments: %s", label);
3246 errno = EINVAL;
3247 return NULL;
3248 }
3249
3250 /* Find the requested session. You cannot load services into XPC domains in
3251 * this manner.
3252 */
3253 launch_data_t session = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE);
3254 if (session) {
3255 jobmgr_t jmt = NULL;
3256 if (launch_data_get_type(session) == LAUNCH_DATA_STRING) {
3257 jmt = jobmgr_find_by_name(jm, launch_data_get_string(session));
3258 if (!jmt) {
3259 jobmgr_log(jm, LOG_ERR, "Could not find requested session: %s", launch_data_get_string(session));
3260 } else {
3261 jm = jmt;
3262 }
3263 } else {
3264 jobmgr_log(jm, LOG_ERR, "Session type is not a string.");
3265 }
3266
3267 if (!jmt) {
3268 errno = EINVAL;
3269 return NULL;
3270 }
3271 }
3272
3273 /* For legacy reasons, we have a global hash of all labels in all job
3274 * managers. So rather than make it a global, we store it in the root job
3275 * manager. But for an XPC domain, we store a local hash of all services in
3276 * the domain.
3277 */
3278 jobmgr_t where2look = (jm->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN) ? jm : root_jobmgr;
3279 if (unlikely((j = job_find(where2look, label)) != NULL)) {
3280 if (jm->xpc_singleton) {
3281 /* There can (and probably will be) multiple attemtps to import the
3282 * same XPC service from the same framework. This is okay. It's
3283 * treated as a singleton, so just return the existing one so that
3284 * it may be aliased into the requesting process' XPC domain.
3285 */
3286 errno = EEXIST;
3287 return j;
3288 } else {
3289 /* If we're not a global XPC domain, then it's an error to try
3290 * importing the same job/service multiple times.
3291 */
3292 errno = EEXIST;
3293 return NULL;
3294 }
3295 } else if (unlikely(!jobmgr_label_test(where2look, label))) {
3296 errno = EINVAL;
3297 return NULL;
3298 }
3299 jobmgr_log(jm, LOG_DEBUG, "Importing %s.", label);
3300
3301 if (likely(j = job_new(jm, label, prog, argv))) {
3302 #if TARGET_OS_EMBEDDED
3303 job_apply_defaults(j);
3304 #endif
3305 launch_data_dict_iterate(pload, job_import_keys, j);
3306 if (!uuid_is_null(j->expected_audit_uuid)) {
3307 uuid_string_t uuid_str;
3308 uuid_unparse(j->expected_audit_uuid, uuid_str);
3309 job_log(j, LOG_DEBUG, "Imported job. Waiting for session for UUID %s.", uuid_str);
3310 LIST_INSERT_HEAD(&s_needing_sessions, j, needing_session_sle);
3311 errno = ENEEDAUTH;
3312 } else {
3313 job_log(j, LOG_DEBUG, "No security session specified.");
3314 j->asport = MACH_PORT_NULL;
3315 }
3316
3317 if (pid1_magic && !jm->parentmgr) {
3318 /* Workaround reentrancy in CF. We don't make this a global variable
3319 * because we don't want per-user launchd's to inherit it. So we
3320 * just set it for every job that we import into the System session.
3321 *
3322 * See <rdar://problem/9468837>.
3323 */
3324 envitem_new(j, "__CF_USER_TEXT_ENCODING", "0x0:0:0", false);
3325 }
3326
3327 if (j->event_monitor) {
3328 eventsystem_ping();
3329 }
3330
3331 #if TARGET_OS_EMBEDDED
3332 /* SpringBoard and backboardd must run at elevated priority.
3333 *
3334 * See <rdar://problem/9539873> and <rdar://problem/10984383>.
3335 */
3336 if (j->embedded_god || j->embedded_home) {
3337 j->psproctype = POSIX_SPAWN_PROC_TYPE_APP_DEFAULT;
3338 }
3339 #endif
3340 }
3341
3342 return j;
3343 }
3344
3345 bool
jobmgr_label_test(jobmgr_t jm,const char * str)3346 jobmgr_label_test(jobmgr_t jm, const char *str)
3347 {
3348 const char *ptr;
3349
3350 if (str[0] == '\0') {
3351 jobmgr_log(jm, LOG_ERR, "Empty job labels are not allowed");
3352 return false;
3353 }
3354
3355 for (ptr = str; *ptr; ptr++) {
3356 if (iscntrl(*ptr)) {
3357 jobmgr_log(jm, LOG_ERR, "ASCII control characters are not allowed in job labels. Index: %td Value: 0x%hhx", ptr - str, *ptr);
3358 return false;
3359 }
3360 }
3361
3362 if ((strncasecmp(str, "com.apple.launchd", strlen("com.apple.launchd")) == 0)
3363 || (strncasecmp(str, "com.apple.launchctl", strlen("com.apple.launchctl")) == 0)) {
3364 jobmgr_log(jm, LOG_ERR, "Job labels are not allowed to use a reserved prefix: %s", str);
3365 return false;
3366 }
3367
3368 return true;
3369 }
3370
3371 job_t
job_find(jobmgr_t jm,const char * label)3372 job_find(jobmgr_t jm, const char *label)
3373 {
3374 job_t ji;
3375
3376 if (!jm) {
3377 jm = root_jobmgr;
3378 }
3379
3380 LIST_FOREACH(ji, &jm->label_hash[hash_label(label)], label_hash_sle) {
3381 if (unlikely(ji->removal_pending || ji->mgr->shutting_down)) {
3382 // 5351245 and 5488633 respectively
3383 continue;
3384 }
3385
3386 if (strcmp(ji->label, label) == 0) {
3387 return ji;
3388 }
3389 }
3390
3391 errno = ESRCH;
3392 return NULL;
3393 }
3394
3395 // Should try and consolidate with job_mig_intran2() and jobmgr_find_by_pid().
3396 job_t
jobmgr_find_by_pid_deep(jobmgr_t jm,pid_t p,bool anon_okay)3397 jobmgr_find_by_pid_deep(jobmgr_t jm, pid_t p, bool anon_okay)
3398 {
3399 job_t ji = NULL;
3400 LIST_FOREACH(ji, &jm->active_jobs[ACTIVE_JOB_HASH(p)], pid_hash_sle) {
3401 if (ji->p == p && (!ji->anonymous || (ji->anonymous && anon_okay))) {
3402 return ji;
3403 }
3404 }
3405
3406 jobmgr_t jmi = NULL;
3407 SLIST_FOREACH(jmi, &jm->submgrs, sle) {
3408 if ((ji = jobmgr_find_by_pid_deep(jmi, p, anon_okay))) {
3409 break;
3410 }
3411 }
3412
3413 return ji;
3414 }
3415
3416 job_t
jobmgr_find_by_pid(jobmgr_t jm,pid_t p,bool create_anon)3417 jobmgr_find_by_pid(jobmgr_t jm, pid_t p, bool create_anon)
3418 {
3419 job_t ji;
3420
3421 LIST_FOREACH(ji, &jm->active_jobs[ACTIVE_JOB_HASH(p)], pid_hash_sle) {
3422 if (ji->p == p) {
3423 return ji;
3424 }
3425 }
3426
3427 return create_anon ? job_new_anonymous(jm, p) : NULL;
3428 }
3429
3430 job_t
managed_job(pid_t p)3431 managed_job(pid_t p)
3432 {
3433 job_t ji;
3434
3435 LIST_FOREACH(ji, &managed_actives[ACTIVE_JOB_HASH(p)], pid_hash_sle) {
3436 if (ji->p == p) {
3437 return ji;
3438 }
3439 }
3440
3441 return NULL;
3442 }
3443
3444 job_t
job_mig_intran2(jobmgr_t jm,mach_port_t mport,pid_t upid)3445 job_mig_intran2(jobmgr_t jm, mach_port_t mport, pid_t upid)
3446 {
3447 jobmgr_t jmi;
3448 job_t ji;
3449
3450 if (jm->jm_port == mport) {
3451 return jobmgr_find_by_pid(jm, upid, true);
3452 }
3453
3454 SLIST_FOREACH(jmi, &jm->submgrs, sle) {
3455 job_t jr;
3456
3457 if ((jr = job_mig_intran2(jmi, mport, upid))) {
3458 return jr;
3459 }
3460 }
3461
3462 LIST_FOREACH(ji, &jm->jobs, sle) {
3463 if (ji->j_port == mport) {
3464 return ji;
3465 }
3466 }
3467
3468 return NULL;
3469 }
3470
3471 job_t
job_mig_intran(mach_port_t p)3472 job_mig_intran(mach_port_t p)
3473 {
3474 struct ldcred *ldc = runtime_get_caller_creds();
3475 job_t jr;
3476
3477 jr = job_mig_intran2(root_jobmgr, p, ldc->pid);
3478
3479 if (!jr) {
3480 struct proc_bsdshortinfo proc;
3481 if (proc_pidinfo(ldc->pid, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) {
3482 if (errno != ESRCH) {
3483 (void)jobmgr_assumes_zero(root_jobmgr, errno);
3484 } else {
3485 jobmgr_log(root_jobmgr, LOG_ERR, "%s[%i] disappeared out from under us (UID: %u EUID: %u)", proc.pbsi_comm, ldc->pid, ldc->uid, ldc->euid);
3486 }
3487 }
3488 }
3489
3490 return jr;
3491 }
3492
3493 job_t
job_find_by_service_port(mach_port_t p)3494 job_find_by_service_port(mach_port_t p)
3495 {
3496 struct machservice *ms;
3497
3498 LIST_FOREACH(ms, &port_hash[HASH_PORT(p)], port_hash_sle) {
3499 if (ms->recv && (ms->port == p)) {
3500 return ms->job;
3501 }
3502 }
3503
3504 return NULL;
3505 }
3506
3507 void
job_mig_destructor(job_t j)3508 job_mig_destructor(job_t j)
3509 {
3510 /* The job can go invalid before this point.
3511 *
3512 * <rdar://problem/5477111>
3513 */
3514 if (unlikely(j && (j != workaround_5477111) && j->unload_at_mig_return)) {
3515 job_log(j, LOG_NOTICE, "Unloading PID %u at MIG return.", j->p);
3516 job_remove(j);
3517 }
3518
3519 workaround_5477111 = NULL;
3520
3521 calendarinterval_sanity_check();
3522 }
3523
3524 void
job_export_all2(jobmgr_t jm,launch_data_t where)3525 job_export_all2(jobmgr_t jm, launch_data_t where)
3526 {
3527 jobmgr_t jmi;
3528 job_t ji;
3529
3530 SLIST_FOREACH(jmi, &jm->submgrs, sle) {
3531 job_export_all2(jmi, where);
3532 }
3533
3534 LIST_FOREACH(ji, &jm->jobs, sle) {
3535 launch_data_t tmp;
3536
3537 if (jobmgr_assumes(jm, (tmp = job_export(ji)) != NULL)) {
3538 launch_data_dict_insert(where, tmp, ji->label);
3539 }
3540 }
3541 }
3542
3543 launch_data_t
job_export_all(void)3544 job_export_all(void)
3545 {
3546 launch_data_t resp = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
3547
3548 if (resp != NULL) {
3549 job_export_all2(root_jobmgr, resp);
3550 } else {
3551 (void)os_assumes_zero(errno);
3552 }
3553
3554 return resp;
3555 }
3556
3557 void
job_log_stray_pg(job_t j)3558 job_log_stray_pg(job_t j)
3559 {
3560 pid_t *pids = NULL;
3561 size_t len = sizeof(pid_t) * get_kern_max_proc();
3562 int i = 0, kp_cnt = 0;
3563
3564 if (!launchd_apple_internal) {
3565 return;
3566 }
3567
3568 runtime_ktrace(RTKT_LAUNCHD_FINDING_STRAY_PG, j->p, 0, 0);
3569
3570 if (!job_assumes(j, (pids = malloc(len)) != NULL)) {
3571 return;
3572 }
3573 if (job_assumes_zero_p(j, (kp_cnt = proc_listpgrppids(j->p, pids, len))) == -1) {
3574 goto out;
3575 }
3576
3577 for (i = 0; i < kp_cnt; i++) {
3578 pid_t p_i = pids[i];
3579 if (p_i == j->p) {
3580 continue;
3581 } else if (p_i == 0 || p_i == 1) {
3582 continue;
3583 }
3584
3585 struct proc_bsdshortinfo proc;
3586 if (proc_pidinfo(p_i, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) {
3587 if (errno != ESRCH) {
3588 (void)job_assumes_zero(j, errno);
3589 }
3590 continue;
3591 }
3592
3593 pid_t pp_i = proc.pbsi_ppid;
3594 const char *z = (proc.pbsi_status == SZOMB) ? "zombie " : "";
3595 const char *n = proc.pbsi_comm;
3596
3597 job_log(j, LOG_WARNING, "Stray %sprocess with PGID equal to this dead job: PID %u PPID %u PGID %u %s", z, p_i, pp_i, proc.pbsi_pgid, n);
3598 }
3599
3600 out:
3601 free(pids);
3602 }
3603
3604 #if HAVE_SYSTEMSTATS
3605 static void
systemstats_timer_callback(void)3606 systemstats_timer_callback(void)
3607 {
3608 jobmgr_log_perf_statistics(root_jobmgr, true);
3609 }
3610
3611 static bool
systemstats_is_enabled(void)3612 systemstats_is_enabled(void)
3613 {
3614 static bool systemstats_enabled;
3615
3616 if (!systemstats_enabled) {
3617 char *store = launchd_copy_persistent_store(LAUNCHD_PERSISTENT_STORE_LOGS, NULL);
3618 systemstats_enabled = systemstats_init(SYSTEMSTATS_WRITER_launchd, store);
3619 free(store);
3620
3621 uint64_t interval;
3622 interval = systemstats_get_log_interval(SYSTEMSTATS_WRITER_launchd);
3623
3624 if (pid1_magic && systemstats_enabled && interval) {
3625 jobmgr_assumes_zero_p(root_jobmgr, kevent_mod((uintptr_t)systemstats_timer_callback, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, interval, root_jobmgr));
3626 }
3627 }
3628
3629 return systemstats_enabled;
3630 }
3631 #endif // HAVE_SYSTEMSTATS
3632
3633 void
job_reap(job_t j)3634 job_reap(job_t j)
3635 {
3636 bool is_system_bootstrapper = ((j->is_bootstrapper && pid1_magic) && !j->mgr->parentmgr);
3637
3638 job_log(j, LOG_DEBUG, "Reaping");
3639
3640 if (unlikely(j->weird_bootstrap)) {
3641 int64_t junk = 0;
3642 job_mig_swap_integer(j, VPROC_GSK_WEIRD_BOOTSTRAP, 0, 0, &junk);
3643 }
3644
3645 if (j->fork_fd) {
3646 (void)job_assumes_zero_p(j, runtime_close(j->fork_fd));
3647 j->fork_fd = 0;
3648 }
3649
3650 bool was_dirty = false;
3651 if (!(j->anonymous || j->implicit_reap)) {
3652 uint32_t flags = 0;
3653 #if 0
3654 (void)job_assumes_zero(j, proc_get_dirty(j->p, &flags));
3655 #endif
3656 j->idle_exit = (flags & PROC_DIRTY_ALLOWS_IDLE_EXIT);
3657 was_dirty = (flags & PROC_DIRTY_IS_DIRTY);
3658
3659 job_log(j, LOG_DEBUG, "%sob exited %s.", j->idle_exit ? "Idle-exit j" : "J", was_dirty ? "while dirty" : "cleanly");
3660 }
3661
3662 if (j->idle_exit && was_dirty) {
3663 if (j->jettisoned) {
3664 job_log(j, LOG_NOTICE, "Idle-exit job was jettisoned while dirty. Will respawn immediately.");
3665 j->unthrottle = true;
3666 j->start_pending = true;
3667 } else {
3668 job_log(j, LOG_INFO, "Idle-exit job exited while dirty.");
3669 }
3670 } else if (j->idle_exit && j->jettisoned) {
3671 /* If an idle-exit job is jettisoned, then we shouldn't throttle its
3672 * next respawn because it could not help when it exited. If it ran for
3673 * the minimum runtime, then this doesn't really matter. If it ran for
3674 * less than the minimum runtime, it will not be throttled.
3675 *
3676 * <rdar://problem/12098667>
3677 */
3678 job_log(j, LOG_NOTICE, "Idle-exit job was jettisoned. Will bypass throttle interval for next on-demand launch.");
3679 j->unthrottle = true;
3680 }
3681
3682 if (j->anonymous) {
3683 j->last_exit_status = 0;
3684 } else {
3685 uint64_t rt = runtime_get_nanoseconds_since(j->start_time);
3686 j->trt += rt;
3687
3688 job_log(j, LOG_PERF, "Last instance wall time: %06f", (double)rt / (double)NSEC_PER_SEC);
3689 j->nruns++;
3690
3691 /* The job is dead. While the PID/PGID is still known to be valid, try
3692 * to kill abandoned descendant processes.
3693 */
3694 job_log_stray_pg(j);
3695 if (!j->abandon_pg) {
3696 if (unlikely(killpg2(j->p, SIGTERM) == -1 && errno != ESRCH)) {
3697 job_log(j, LOG_APPLEONLY, "Bug: 5487498");
3698 }
3699 }
3700
3701 int r = -1;
3702 if (!j->implicit_reap) {
3703 /* If the shutdown monitor has suspended a task and not resumed it
3704 * resumed it before exiting, the kernel will not clean up after the
3705 * shutdown monitor. It will, instead, leave the task suspended and
3706 * not process any pending signals on the event loop for the task.
3707 *
3708 * There are a variety of other kernel bugs that could prevent a
3709 * process from exiting, usually having to do with faulty hardware
3710 * or talking to misbehaving drivers that mark a thread as
3711 * uninterruptible and deadlock/hang before unmarking it as such. So
3712 * we have to work around that too.
3713 *
3714 * See <rdar://problem/9284889&9359725>.
3715 */
3716 if (j->workaround9359725) {
3717 job_log(j, LOG_NOTICE, "Simulated exit: <rdar://problem/9359725>");
3718 j->last_exit_status = W_EXITCODE(-1, SIGSEGV);
3719 } else {
3720 #if HAVE_SYSTEMSTATS
3721 int r2;
3722 struct rusage_info_v1 ri;
3723 r2 = job_assumes_zero(j, proc_pid_rusage(j->p, RUSAGE_INFO_V1, (rusage_info_t)&ri));
3724 #endif
3725 if ((r = wait4(j->p, &j->last_exit_status, 0, NULL)) == -1) {
3726 job_log(j, LOG_ERR, "Reap failed. Assuming job exited: %d: %s", errno, strerror(errno));
3727 j->last_exit_status = W_EXITCODE(-1, SIGSEGV);
3728 }
3729
3730 if (j->idle_exit && j->jettisoned) {
3731 // Treat idle-exit jettisons as successful exit.
3732 //
3733 // <rdar://problem/13338973>
3734 (void)job_assumes_zero(j, WTERMSIG(j->last_exit_status));
3735 j->last_exit_status = W_EXITCODE(0, 0);
3736 }
3737 #if HAVE_SYSTEMSTATS
3738 if (r2 == 0) {
3739 job_log_perf_statistics(j, &ri, j->last_exit_status);
3740 }
3741 #endif
3742 }
3743 } else {
3744 job_log(j, LOG_INFO, "Job was implicitly reaped by the kernel.");
3745 }
3746 }
3747
3748 if (j->exit_timeout) {
3749 (void)kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
3750 }
3751
3752 LIST_REMOVE(j, pid_hash_sle);
3753 if (!j->anonymous) {
3754 LIST_REMOVE(j, global_pid_hash_sle);
3755 }
3756
3757 if (j->sent_signal_time) {
3758 uint64_t td_sec, td_usec, td = runtime_get_nanoseconds_since(j->sent_signal_time);
3759
3760 td_sec = td / NSEC_PER_SEC;
3761 td_usec = (td % NSEC_PER_SEC) / NSEC_PER_USEC;
3762
3763 job_log(j, LOG_DEBUG, "Exited %zu.%06zu seconds after the first signal was sent", td_sec, td_usec);
3764 }
3765
3766 int exit_status = WEXITSTATUS(j->last_exit_status);
3767 if (WIFEXITED(j->last_exit_status) && exit_status != 0) {
3768 if (!j->did_exec && _launchd_support_system) {
3769 xpc_object_t event = NULL;
3770 switch (exit_status) {
3771 case ENOENT:
3772 case ENOTDIR:
3773 case ESRCH:
3774 job_log(j, LOG_NOTICE, "Job failed to exec(3). Setting up event to tell us when to try again: %d: %s", exit_status, strerror(exit_status));
3775 event = xpc_dictionary_create(NULL, NULL, 0);
3776 xpc_dictionary_set_string(event, "Executable", j->prog ? j->prog : j->argv[0]);
3777 if (j->mach_uid) {
3778 xpc_dictionary_set_uint64(event, "UID", j->mach_uid);
3779 } else if (j->username) {
3780 xpc_dictionary_set_string(event, "UserName", j->username);
3781 }
3782
3783 if (j->groupname) {
3784 xpc_dictionary_set_string(event, "GroupName", j->groupname);
3785 }
3786
3787 (void)externalevent_new(j, _launchd_support_system, j->label, event, 0);
3788 xpc_release(event);
3789
3790 j->waiting4ok = true;
3791 default:
3792 job_log(j, LOG_NOTICE, "Job failed to exec(3) for weird reason: %d", exit_status);
3793 }
3794 } else {
3795 int level = LOG_INFO;
3796 if (exit_status != 0) {
3797 level = LOG_ERR;
3798 }
3799
3800 job_log(j, level, "Exited with code: %d", exit_status);
3801 }
3802 }
3803
3804 if (WIFSIGNALED(j->last_exit_status)) {
3805 int s = WTERMSIG(j->last_exit_status);
3806 if ((SIGKILL == s || SIGTERM == s) && !j->stopped) {
3807 job_log(j, LOG_NOTICE, "Exited: %s", strsignal(s));
3808 } else if (!(j->stopped || j->clean_kill || j->jettisoned)) {
3809 switch (s) {
3810 // Signals which indicate a crash.
3811 case SIGILL:
3812 case SIGABRT:
3813 case SIGFPE:
3814 case SIGBUS:
3815 case SIGSEGV:
3816 case SIGSYS:
3817 /* If the kernel has posted NOTE_EXIT and the signal sent to the process was
3818 * SIGTRAP, assume that it's a crash.
3819 */
3820 case SIGTRAP:
3821 j->crashed = true;
3822 job_log(j, LOG_WARNING, "Job appears to have crashed: %s", strsignal(s));
3823 break;
3824 default:
3825 job_log(j, LOG_WARNING, "Exited abnormally: %s", strsignal(s));
3826 break;
3827 }
3828
3829 if (is_system_bootstrapper && j->crashed) {
3830 job_log(j, LOG_ERR | LOG_CONSOLE, "The %s bootstrapper has crashed: %s", j->mgr->name, strsignal(s));
3831 }
3832 }
3833 }
3834
3835 j->reaped = true;
3836
3837 struct machservice *msi = NULL;
3838 if (j->crashed || !(j->did_exec || j->anonymous)) {
3839 SLIST_FOREACH(msi, &j->machservices, sle) {
3840 if (j->crashed && !msi->isActive && (msi->drain_one_on_crash || msi->drain_all_on_crash)) {
3841 machservice_drain_port(msi);
3842 }
3843
3844 if (!j->did_exec && msi->reset && job_assumes(j, !msi->isActive)) {
3845 machservice_resetport(j, msi);
3846 }
3847 }
3848 }
3849
3850 /* HACK: Essentially duplicating the logic directly above. But this has
3851 * gotten really hairy, and I don't want to try consolidating it right now.
3852 */
3853 if (j->xpc_service && !j->xpcproxy_did_exec) {
3854 job_log(j, LOG_ERR, "XPC Service could not exec(3). Resetting port.");
3855 SLIST_FOREACH(msi, &j->machservices, sle) {
3856 /* Drain the messages but do not reset the port. If xpcproxy could
3857 * not exec(3), then we don't want to continue trying, since there
3858 * is very likely a serious configuration error with the service.
3859 *
3860 * The above comment is weird. I originally said we should drain
3861 * messages but not reset the port, but that's exactly what we do
3862 * below, and I'm not sure which is the mistake, the comment or the
3863 * actual behavior.
3864 *
3865 * Since it's always been this way, I'll assume that the comment is
3866 * incorrect, but I'll leave it in place just to remind myself to
3867 * actually look into it at some point.
3868 *
3869 * <rdar://problem/8986802>
3870 */
3871 if (msi->upfront && job_assumes(j, !msi->isActive)) {
3872 machservice_resetport(j, msi);
3873 }
3874 }
3875 }
3876
3877 struct suspended_peruser *spi = NULL;
3878 while ((spi = LIST_FIRST(&j->suspended_perusers))) {
3879 job_log(j, LOG_ERR, "Job exited before resuming per-user launchd for UID %u. Will forcibly resume.", spi->j->mach_uid);
3880 spi->j->peruser_suspend_count--;
3881 if (spi->j->peruser_suspend_count == 0) {
3882 job_dispatch(spi->j, false);
3883 }
3884 LIST_REMOVE(spi, sle);
3885 free(spi);
3886 }
3887
3888 if (j->exit_status_dest) {
3889 errno = helper_downcall_wait(j->exit_status_dest, j->last_exit_status);
3890 if (errno && errno != MACH_SEND_INVALID_DEST) {
3891 (void)job_assumes_zero(j, errno);
3892 }
3893
3894 j->exit_status_dest = MACH_PORT_NULL;
3895 }
3896
3897 if (j->spawn_reply_port) {
3898 /* If the child never called exec(3), we must send a spawn() reply so
3899 * that the requestor can get exit status from it. If we fail to send
3900 * the reply for some reason, we have to deallocate the exit status port
3901 * ourselves.
3902 */
3903 kern_return_t kr = job_mig_spawn2_reply(j->spawn_reply_port, BOOTSTRAP_SUCCESS, j->p, j->exit_status_port);
3904 if (kr) {
3905 if (kr != MACH_SEND_INVALID_DEST) {
3906 (void)job_assumes_zero(j, kr);
3907 }
3908
3909 (void)job_assumes_zero(j, launchd_mport_close_recv(j->exit_status_port));
3910 }
3911
3912 j->exit_status_port = MACH_PORT_NULL;
3913 j->spawn_reply_port = MACH_PORT_NULL;
3914 }
3915
3916 if (j->anonymous) {
3917 total_anon_children--;
3918 if (j->holds_ref) {
3919 job_log(j, LOG_PERF, "Anonymous job exited holding reference.");
3920 runtime_del_ref();
3921 }
3922 } else {
3923 job_log(j, LOG_PERF, "Job exited.");
3924 runtime_del_ref();
3925 total_children--;
3926 }
3927
3928 if (j->has_console) {
3929 launchd_wsp = 0;
3930 }
3931
3932 if (j->shutdown_monitor) {
3933 job_log(j, LOG_NOTICE | LOG_CONSOLE, "Shutdown monitor has exited.");
3934 _launchd_shutdown_monitor = NULL;
3935 j->shutdown_monitor = false;
3936 }
3937
3938 if (!j->anonymous) {
3939 j->mgr->normal_active_cnt--;
3940 }
3941 j->sent_signal_time = 0;
3942 j->sent_sigkill = false;
3943 j->clean_kill = false;
3944 j->event_monitor_ready2signal = false;
3945 j->p = 0;
3946 j->uniqueid = 0;
3947 }
3948
3949 void
jobmgr_dispatch_all(jobmgr_t jm,bool newmounthack)3950 jobmgr_dispatch_all(jobmgr_t jm, bool newmounthack)
3951 {
3952 jobmgr_t jmi, jmn;
3953 job_t ji, jn;
3954
3955 if (jm->shutting_down) {
3956 return;
3957 }
3958
3959 SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
3960 jobmgr_dispatch_all(jmi, newmounthack);
3961 }
3962
3963 LIST_FOREACH_SAFE(ji, &jm->jobs, sle, jn) {
3964 if (newmounthack && ji->start_on_mount) {
3965 ji->start_pending = true;
3966 }
3967
3968 job_dispatch(ji, false);
3969 }
3970 }
3971
3972 void
job_dispatch_curious_jobs(job_t j)3973 job_dispatch_curious_jobs(job_t j)
3974 {
3975 job_t ji = NULL, jt = NULL;
3976 SLIST_FOREACH_SAFE(ji, &s_curious_jobs, curious_jobs_sle, jt) {
3977 struct semaphoreitem *si = NULL;
3978 SLIST_FOREACH(si, &ji->semaphores, sle) {
3979 if (!(si->why == OTHER_JOB_ENABLED || si->why == OTHER_JOB_DISABLED)) {
3980 continue;
3981 }
3982
3983 if (strcmp(si->what, j->label) == 0) {
3984 job_log(ji, LOG_DEBUG, "Dispatching out of interest in \"%s\".", j->label);
3985
3986 if (!ji->removing) {
3987 job_dispatch(ji, false);
3988 } else {
3989 job_log(ji, LOG_NOTICE, "The following job is circularly dependent upon this one: %s", j->label);
3990 }
3991
3992 /* ji could be removed here, so don't do anything with it or its semaphores
3993 * after this point.
3994 */
3995 break;
3996 }
3997 }
3998 }
3999 }
4000
4001 job_t
job_dispatch(job_t j,bool kickstart)4002 job_dispatch(job_t j, bool kickstart)
4003 {
4004 // Don't dispatch a job if it has no audit session set.
4005 syslog(LOG_DEBUG, "dispatching job j=%p kickstart=%d", j, kickstart);
4006 #ifdef notyet
4007 if (!uuid_is_null(j->expected_audit_uuid)) {
4008 job_log(j, LOG_DEBUG, "Job is still awaiting its audit session UUID. Not dispatching.");
4009 return NULL;
4010 }
4011 #endif
4012 if (j->alias) {
4013 job_log(j, LOG_DEBUG, "Job is an alias. Not dispatching.");
4014 return NULL;
4015 }
4016
4017 if (j->waiting4ok) {
4018 job_log(j, LOG_DEBUG, "Job cannot exec(3). Not dispatching.");
4019 return NULL;
4020 }
4021
4022 #if TARGET_OS_EMBEDDED
4023 if (launchd_embedded_handofgod && _launchd_embedded_god) {
4024 if (!job_assumes(j, _launchd_embedded_god->username != NULL && j->username != NULL)) {
4025 errno = EPERM;
4026 return NULL;
4027 }
4028
4029 if (strcmp(j->username, _launchd_embedded_god->username) != 0) {
4030 errno = EPERM;
4031 return NULL;
4032 }
4033 } else if (launchd_embedded_handofgod) {
4034 errno = EINVAL;
4035 return NULL;
4036 }
4037 #endif
4038
4039 /*
4040 * The whole job removal logic needs to be consolidated. The fact that
4041 * a job can be removed from just about anywhere makes it easy to have
4042 * stale pointers left behind somewhere on the stack that might get
4043 * used after the deallocation. In particular, during job iteration.
4044 *
4045 * This is a classic example. The act of dispatching a job may delete it.
4046 */
4047 if (!job_active(j)) {
4048 if (job_useless(j)) {
4049 job_log(j, LOG_DEBUG, "Job is useless. Removing.");
4050 job_remove(j);
4051 return NULL;
4052 }
4053 if (unlikely(j->per_user && j->peruser_suspend_count > 0)) {
4054 job_log(j, LOG_DEBUG, "Per-user launchd is suspended. Not dispatching.");
4055 return NULL;
4056 }
4057
4058 if (kickstart || job_keepalive(j) || uflag) {
4059 job_log(j, LOG_DEBUG, "%starting job", kickstart ? "Kicks" : "S");
4060 job_start(j);
4061 } else {
4062 job_log(j, LOG_DEBUG, "Watching job.");
4063 job_watch(j);
4064 }
4065 } else {
4066 job_log(j, LOG_DEBUG, "Tried to dispatch an already active job: %s.", job_active(j));
4067 }
4068 return j;
4069 }
4070
4071 void
job_kill(job_t j)4072 job_kill(job_t j)
4073 {
4074 if (unlikely(!j->p || j->anonymous)) {
4075 return;
4076 }
4077
4078 (void)job_assumes_zero_p(j, kill2(j->p, SIGKILL));
4079
4080 j->sent_sigkill = true;
4081 (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, LAUNCHD_SIGKILL_TIMER, j));
4082
4083 job_log(j, LOG_DEBUG, "Sent SIGKILL signal");
4084 }
4085
4086 void
job_open_shutdown_transaction(job_t j)4087 job_open_shutdown_transaction(job_t j)
4088 {
4089 int rv = proc_set_dirty(j->p, true);
4090 if (rv != 0) {
4091 job_log(j, LOG_DEBUG, "Job wants to be dirty at shutdown, but it is not Instant Off-compliant. Treating normally.");
4092 j->dirty_at_shutdown = false;
4093 }
4094 }
4095
4096 void
job_close_shutdown_transaction(job_t j)4097 job_close_shutdown_transaction(job_t j)
4098 {
4099 if (j->dirty_at_shutdown) {
4100 job_log(j, LOG_DEBUG, "Closing shutdown transaction for job.");
4101 (void)job_assumes_zero(j, proc_set_dirty(j->p, false));
4102 j->dirty_at_shutdown = false;
4103 }
4104 }
4105
4106 void
job_log_children_without_exec(job_t j)4107 job_log_children_without_exec(job_t j)
4108 {
4109 pid_t *pids = NULL;
4110 size_t len = sizeof(pid_t) * get_kern_max_proc();
4111 int i = 0, kp_cnt = 0;
4112
4113 if (!launchd_apple_internal || j->anonymous || j->per_user) {
4114 return;
4115 }
4116
4117 if (!job_assumes(j, (pids = malloc(len)) != NULL)) {
4118 return;
4119 }
4120 if (job_assumes_zero_p(j, (kp_cnt = proc_listchildpids(j->p, pids, len))) == -1) {
4121 goto out;
4122 }
4123
4124 for (i = 0; i < kp_cnt; i++) {
4125 struct proc_bsdshortinfo proc;
4126 if (proc_pidinfo(pids[i], PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) {
4127 if (errno != ESRCH) {
4128 (void)job_assumes_zero(j, errno);
4129 }
4130 continue;
4131 }
4132 if (proc.pbsi_flags & P_EXEC) {
4133 continue;
4134 }
4135
4136 job_log(j, LOG_DEBUG, "Called *fork(). Please switch to posix_spawn*(), pthreads or launchd. Child PID %u", pids[i]);
4137 }
4138
4139 out:
4140 free(pids);
4141 }
4142
4143 void
job_callback_proc(job_t j,struct kevent * kev)4144 job_callback_proc(job_t j, struct kevent *kev)
4145 {
4146 bool program_changed = false;
4147 int fflags = kev->fflags;
4148
4149 job_log(j, LOG_DEBUG, "EVFILT_PROC event for job.");
4150 log_kevent_struct(LOG_DEBUG, kev, 0);
4151
4152 if (fflags & NOTE_EXEC) {
4153 program_changed = true;
4154
4155 if (j->anonymous) {
4156 struct proc_bsdshortinfo proc;
4157 if (proc_pidinfo(j->p, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) > 0) {
4158 char newlabel[1000];
4159
4160 snprintf(newlabel, sizeof(newlabel), "%p.anonymous.%s", j, proc.pbsi_comm);
4161
4162 job_log(j, LOG_INFO, "Program changed. Updating the label to: %s", newlabel);
4163
4164 LIST_REMOVE(j, label_hash_sle);
4165 strcpy((char *)j->label, newlabel);
4166
4167 jobmgr_t where2put = root_jobmgr;
4168 if (j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN) {
4169 where2put = j->mgr;
4170 }
4171 LIST_INSERT_HEAD(&where2put->label_hash[hash_label(j->label)], j, label_hash_sle);
4172 } else if (errno != ESRCH) {
4173 (void)job_assumes_zero(j, errno);
4174 }
4175 } else {
4176 if (j->spawn_reply_port) {
4177 errno = job_mig_spawn2_reply(j->spawn_reply_port, BOOTSTRAP_SUCCESS, j->p, j->exit_status_port);
4178 if (errno) {
4179 if (errno != MACH_SEND_INVALID_DEST) {
4180 (void)job_assumes_zero(j, errno);
4181 }
4182 (void)job_assumes_zero(j, launchd_mport_close_recv(j->exit_status_port));
4183 }
4184
4185 j->spawn_reply_port = MACH_PORT_NULL;
4186 j->exit_status_port = MACH_PORT_NULL;
4187 }
4188
4189 if (j->xpc_service && j->did_exec) {
4190 j->xpcproxy_did_exec = true;
4191 }
4192
4193 j->did_exec = true;
4194 job_log(j, LOG_DEBUG, "Program changed");
4195 }
4196 }
4197
4198 if (fflags & NOTE_FORK) {
4199 job_log(j, LOG_DEBUG, "fork()ed%s", program_changed ? ". For this message only: We don't know whether this event happened before or after execve()." : "");
4200 job_log_children_without_exec(j);
4201 }
4202
4203 if (fflags & NOTE_EXIT) {
4204 if (kev->data & NOTE_EXIT_DECRYPTFAIL) {
4205 j->fpfail = true;
4206 job_log(j, LOG_WARNING, "FairPlay decryption failed on binary for job.");
4207 } else if (kev->data & NOTE_EXIT_MEMORY) {
4208 j->jettisoned = true;
4209 job_log(j, LOG_INFO, "Job was killed due to memory pressure.");
4210 }
4211
4212 job_reap(j);
4213
4214 if (j->anonymous) {
4215 job_remove(j);
4216 j = NULL;
4217 } else {
4218 struct waiting4attach *w4ai = NULL;
4219 struct waiting4attach *w4ait = NULL;
4220 LIST_FOREACH_SAFE(w4ai, &_launchd_domain_waiters, le, w4ait) {
4221 if (w4ai->dest == (pid_t)kev->ident) {
4222 waiting4attach_delete(j->mgr, w4ai);
4223 }
4224 }
4225
4226 (void)job_dispatch(j, false);
4227 }
4228 }
4229 }
4230
4231 void
job_callback_timer(job_t j,void * ident)4232 job_callback_timer(job_t j, void *ident)
4233 {
4234 if (j == ident) {
4235 job_log(j, LOG_DEBUG, "j == ident (%p)", ident);
4236 job_dispatch(j, true);
4237 } else if (&j->semaphores == ident) {
4238 job_log(j, LOG_DEBUG, "&j->semaphores == ident (%p)", ident);
4239 job_dispatch(j, false);
4240 } else if (&j->start_interval == ident) {
4241 job_log(j, LOG_DEBUG, "&j->start_interval == ident (%p)", ident);
4242 j->start_pending = true;
4243 job_dispatch(j, false);
4244 } else if (&j->exit_timeout == ident) {
4245 if (!job_assumes(j, j->p != 0)) {
4246 return;
4247 }
4248
4249 if (j->sent_sigkill) {
4250 uint64_t td = runtime_get_nanoseconds_since(j->sent_signal_time);
4251
4252 td /= NSEC_PER_SEC;
4253 td -= j->clean_kill ? 0 : j->exit_timeout;
4254
4255 job_log(j, LOG_WARNING | LOG_CONSOLE, "Job has not died after being %skilled %zu seconds ago. Simulating exit.", j->clean_kill ? "cleanly " : "", td);
4256 j->workaround9359725 = true;
4257
4258 // This basically has to be done off the main thread. We have no
4259 // mechanism for draining the main queue in our run loop (like CF
4260 // does), and the kevent mechanism wants an object to be associated
4261 // as the callback. So we just create a dispatch source and reap the
4262 // errant PID whenever we can. Note that it is not safe for us to do
4263 // any logging in this block, since logging requires exclusive
4264 // access to global data structures that is only protected by the
4265 // main thread.
4266 dispatch_source_t hack_13570156 = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, j->p, DISPATCH_PROC_EXIT, dispatch_get_global_queue(0, 0));
4267 dispatch_source_set_event_handler(hack_13570156, ^{
4268 pid_t pid = (pid_t)dispatch_source_get_handle(hack_13570156);
4269
4270 int status = 0;
4271 (void)waitpid(pid, &status, 0);
4272 dispatch_release(hack_13570156);
4273 });
4274
4275 dispatch_resume(hack_13570156);
4276
4277 if (launchd_trap_sigkill_bugs) {
4278 job_log(j, LOG_NOTICE | LOG_CONSOLE, "Trapping into kernel debugger. You can continue the machine after it has been debugged, and shutdown will proceed normally.");
4279 (void)job_assumes_zero(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER));
4280 }
4281
4282 struct kevent bogus_exit;
4283 EV_SET(&bogus_exit, j->p, EVFILT_PROC, 0, NOTE_EXIT, 0, 0);
4284 jobmgr_callback(j->mgr, &bogus_exit);
4285 } else {
4286 if (unlikely(j->debug_before_kill)) {
4287 job_log(j, LOG_NOTICE, "Exit timeout elapsed. Entering the kernel debugger");
4288 (void)job_assumes_zero(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER));
4289 }
4290
4291 job_log(j, LOG_WARNING | LOG_CONSOLE, "Exit timeout elapsed (%u seconds). Killing", j->exit_timeout);
4292 job_kill(j);
4293 }
4294 } else {
4295 job_log(j, LOG_ERR, "Unrecognized job timer callback: %p", ident);
4296 }
4297 }
4298
4299 void
job_callback_read(job_t j,int ident)4300 job_callback_read(job_t j, int ident)
4301 {
4302 if (ident == j->stdin_fd) {
4303 job_dispatch(j, true);
4304 } else {
4305 socketgroup_callback(j);
4306 }
4307 }
4308
4309 void
jobmgr_reap_bulk(jobmgr_t jm,struct kevent * kev)4310 jobmgr_reap_bulk(jobmgr_t jm, struct kevent *kev)
4311 {
4312 jobmgr_t jmi;
4313 job_t j;
4314
4315 SLIST_FOREACH(jmi, &jm->submgrs, sle) {
4316 jobmgr_reap_bulk(jmi, kev);
4317 }
4318
4319 if ((j = jobmgr_find_by_pid(jm, (pid_t)kev->ident, false))) {
4320 kev->udata = j;
4321 job_callback(j, kev);
4322 }
4323 }
4324
4325 void
jobmgr_callback(void * obj,struct kevent * kev)4326 jobmgr_callback(void *obj, struct kevent *kev)
4327 {
4328 jobmgr_t jm = obj;
4329
4330 #if TARGET_OS_EMBEDDED
4331 int flag2check = VQ_MOUNT;
4332 #else
4333 int flag2check = VQ_UPDATE;
4334 #endif
4335
4336 switch (kev->filter) {
4337 case EVFILT_PROC:
4338 jobmgr_reap_bulk(jm, kev);
4339 root_jobmgr = jobmgr_do_garbage_collection(root_jobmgr);
4340 break;
4341 case EVFILT_SIGNAL:
4342 switch (kev->ident) {
4343 case SIGTERM:
4344 jobmgr_log(jm, LOG_DEBUG, "Got SIGTERM. Shutting down.");
4345 return launchd_shutdown();
4346 case SIGUSR1:
4347 return calendarinterval_callback();
4348 case SIGUSR2:
4349 // Turn on all logging.
4350 launchd_log_perf = true;
4351 launchd_log_debug = true;
4352 launchd_log_shutdown = true;
4353 /* Hopefully /var is available by this point. If not, uh, oh well.
4354 * It's just a debugging facility.
4355 */
4356 return jobmgr_log_perf_statistics(jm, false);
4357 case SIGINFO:
4358 return jobmgr_log_perf_statistics(jm, true);
4359 default:
4360 jobmgr_log(jm, LOG_ERR, "Unrecognized signal: %lu: %s", kev->ident, strsignal(kev->ident));
4361 }
4362 break;
4363 case EVFILT_FS:
4364 if (kev->fflags & flag2check) {
4365 if (!launchd_var_available) {
4366 struct stat sb;
4367 if (stat("/var/log", &sb) == 0 && (sb.st_mode & S_IWUSR)) {
4368 launchd_var_available = true;
4369 }
4370 }
4371 } else if (kev->fflags & VQ_MOUNT) {
4372 jobmgr_dispatch_all(jm, true);
4373 }
4374 jobmgr_dispatch_all_semaphores(jm);
4375 break;
4376 case EVFILT_TIMER:
4377 if (kev->ident == (uintptr_t)&sorted_calendar_events) {
4378 calendarinterval_callback();
4379 } else if (kev->ident == (uintptr_t)jm) {
4380 jobmgr_log(jm, LOG_DEBUG, "Shutdown timer firing.");
4381 jobmgr_still_alive_with_check(jm);
4382 } else if (kev->ident == (uintptr_t)&jm->reboot_flags) {
4383 jobmgr_do_garbage_collection(jm);
4384 }
4385 else if (kev->ident == (uintptr_t)&launchd_runtime_busy_time) {
4386 #if 0
4387 jobmgr_log(jm, LOG_DEBUG, "Idle exit timer fired. Shutting down.");
4388 if (jobmgr_assumes_zero(jm, runtime_busy_cnt) == 0) {
4389 return launchd_shutdown();
4390 }
4391 #endif
4392 #if HAVE_SYSTEMSTATS
4393 } else if (kev->ident == (uintptr_t)systemstats_timer_callback) {
4394 systemstats_timer_callback();
4395 #endif
4396 }
4397 break;
4398 case EVFILT_VNODE:
4399 if (kev->ident == (uintptr_t)s_no_hang_fd) {
4400 int _no_hang_fd = open("/dev/autofs_nowait", O_EVTONLY | O_NONBLOCK);
4401 if (unlikely(_no_hang_fd != -1)) {
4402 jobmgr_log(root_jobmgr, LOG_DEBUG, "/dev/autofs_nowait has appeared!");
4403 (void)jobmgr_assumes_zero_p(root_jobmgr, kevent_mod((uintptr_t)s_no_hang_fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL));
4404 (void)jobmgr_assumes_zero_p(root_jobmgr, runtime_close(s_no_hang_fd));
4405 s_no_hang_fd = _fd(_no_hang_fd);
4406 }
4407 } else if (pid1_magic && launchd_console && kev->ident == (uintptr_t)fileno(launchd_console)) {
4408 int cfd = -1;
4409 if (jobmgr_assumes_zero_p(jm, cfd = open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY)) != -1) {
4410 _fd(cfd);
4411 if (!(launchd_console = fdopen(cfd, "w"))) {
4412 (void)jobmgr_assumes_zero(jm, errno);
4413 (void)close(cfd);
4414 }
4415 }
4416 }
4417 break;
4418 default:
4419 jobmgr_log(jm, LOG_ERR, "Unrecognized kevent filter: %hd", kev->filter);
4420 }
4421 }
4422
4423 void
job_callback(void * obj,struct kevent * kev)4424 job_callback(void *obj, struct kevent *kev)
4425 {
4426 job_t j = obj;
4427
4428 job_log(j, LOG_DEBUG, "Dispatching kevent callback.");
4429
4430 switch (kev->filter) {
4431 case EVFILT_PROC:
4432 return job_callback_proc(j, kev);
4433 case EVFILT_TIMER:
4434 return job_callback_timer(j, (void *) kev->ident);
4435 case EVFILT_READ:
4436 return job_callback_read(j, (int) kev->ident);
4437 case EVFILT_MACHPORT:
4438 return (void)job_dispatch(j, true);
4439 default:
4440 job_log(j, LOG_ERR, "Unrecognized job callback filter: %hd", kev->filter);
4441 }
4442 }
4443
4444 void
job_start(job_t j)4445 job_start(job_t j)
4446 {
4447 uint64_t td;
4448 int spair[2];
4449 int execspair[2];
4450 char nbuf[64];
4451 pid_t c;
4452 bool sipc = false;
4453 u_int proc_fflags = NOTE_EXIT|NOTE_FORK|NOTE_EXEC|NOTE_EXIT_DETAIL|NOTE_EXITSTATUS;
4454
4455 if (!job_assumes(j, j->mgr != NULL)) {
4456 return;
4457 }
4458
4459 if (unlikely(job_active(j))) {
4460 job_log(j, LOG_DEBUG, "Already started");
4461 return;
4462 }
4463
4464 if (!LIST_EMPTY(&j->mgr->attaches)) {
4465 job_log(j, LOG_DEBUG, "Looking for attachments for job: %s", j->label);
4466 (void)waiting4attach_find(j->mgr, j);
4467 }
4468
4469 /*
4470 * Some users adjust the wall-clock and then expect software to not notice.
4471 * Therefore, launchd must use an absolute clock instead of the wall clock
4472 * wherever possible.
4473 */
4474 td = runtime_get_nanoseconds_since(j->start_time);
4475 td /= NSEC_PER_SEC;
4476
4477 if (j->start_time && (td < j->min_run_time) && !j->legacy_mach_job && !j->inetcompat && !j->unthrottle) {
4478 time_t respawn_delta = j->min_run_time - (uint32_t)td;
4479 /* We technically should ref-count throttled jobs to prevent idle exit,
4480 * but we're not directly tracking the 'throttled' state at the moment.
4481 */
4482 job_log(j, LOG_NOTICE, "Throttling respawn: Will start in %ld seconds", respawn_delta);
4483 (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, respawn_delta, j));
4484 job_ignore(j);
4485 return;
4486 }
4487
4488 if (likely(!j->legacy_mach_job)) {
4489 sipc = ((!SLIST_EMPTY(&j->sockets) || !SLIST_EMPTY(&j->machservices)) && !j->deny_job_creation) || j->embedded_god;
4490 }
4491
4492 if (sipc) {
4493 (void)job_assumes_zero_p(j, socketpair(AF_UNIX, SOCK_STREAM, 0, spair));
4494 }
4495
4496 (void)job_assumes_zero_p(j, socketpair(AF_UNIX, SOCK_STREAM, 0, execspair));
4497
4498 switch (c = runtime_fork(j->weird_bootstrap ? j->j_port : j->mgr->jm_port)) {
4499 case -1:
4500 job_log_error(j, LOG_ERR, "fork() failed, will try again in one second");
4501 (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 1, j));
4502 job_ignore(j);
4503
4504 (void)job_assumes_zero(j, runtime_close(execspair[0]));
4505 (void)job_assumes_zero(j, runtime_close(execspair[1]));
4506 if (sipc) {
4507 (void)job_assumes_zero(j, runtime_close(spair[0]));
4508 (void)job_assumes_zero(j, runtime_close(spair[1]));
4509 }
4510 break;
4511 case 0:
4512 if (unlikely(_vproc_post_fork_ping())) {
4513 syslog(LOG_ERR, "_vproc_post_fork_ping() fail");
4514 DEBUG_EXIT(EXIT_FAILURE);
4515 }
4516
4517 (void)job_assumes_zero(j, runtime_close(execspair[0]));
4518 // wait for our parent to say they've attached a kevent to us
4519 read(_fd(execspair[1]), &c, sizeof(c));
4520 if (sipc) {
4521 (void)job_assumes_zero(j, runtime_close(spair[0]));
4522 snprintf(nbuf, sizeof(nbuf), "%d", spair[1]);
4523 setenv(LAUNCHD_TRUSTED_FD_ENV, nbuf, 1);
4524 }
4525 job_start_child(j);
4526 break;
4527 default:
4528 j->start_time = runtime_get_opaque_time();
4529
4530 job_log(j, LOG_DEBUG, "Started as PID: %u", c);
4531
4532 j->did_exec = false;
4533 j->fpfail = false;
4534 j->jettisoned = false;
4535 j->xpcproxy_did_exec = false;
4536 j->checkedin = false;
4537 j->start_pending = false;
4538 j->reaped = false;
4539 j->crashed = false;
4540 j->stopped = false;
4541 j->workaround9359725 = false;
4542 j->implicit_reap = false;
4543 j->unthrottle = false;
4544 if (j->needs_kickoff) {
4545 j->needs_kickoff = false;
4546
4547 if (SLIST_EMPTY(&j->semaphores)) {
4548 j->ondemand = false;
4549 }
4550 }
4551
4552 if (j->has_console) {
4553 launchd_wsp = c;
4554 }
4555
4556 job_log(j, LOG_PERF, "Job started.");
4557 runtime_add_ref();
4558 total_children++;
4559 LIST_INSERT_HEAD(&j->mgr->active_jobs[ACTIVE_JOB_HASH(c)], j, pid_hash_sle);
4560 LIST_INSERT_HEAD(&managed_actives[ACTIVE_JOB_HASH(c)], j, global_pid_hash_sle);
4561 j->p = c;
4562
4563 struct proc_uniqidentifierinfo info;
4564 if (proc_pidinfo(c, PROC_PIDUNIQIDENTIFIERINFO, 0, &info, PROC_PIDUNIQIDENTIFIERINFO_SIZE) != 0) {
4565 // ignore errors here, kevent_mod below will catch them and clean up
4566 j->uniqueid = info.p_uniqueid;
4567 }
4568
4569 j->mgr->normal_active_cnt++;
4570 j->fork_fd = _fd(execspair[0]);
4571 (void)job_assumes_zero(j, runtime_close(execspair[1]));
4572 if (sipc) {
4573 (void)job_assumes_zero(j, runtime_close(spair[1]));
4574 ipc_open(_fd(spair[0]), j);
4575 }
4576 if (kevent_mod(c, EVFILT_PROC, EV_ADD, proc_fflags, 0, root_jobmgr ? root_jobmgr : j->mgr) != -1) {
4577 job_ignore(j);
4578 } else {
4579 if (errno == ESRCH) {
4580 job_log(j, LOG_ERR, "Child was killed before we could attach a kevent.");
4581 } else {
4582 (void)job_assumes(j, errno == ESRCH);
4583 }
4584 job_reap(j);
4585
4586 /* If we have reaped this job within this same run loop pass, then
4587 * it will be currently ignored. So if there's a failure to attach a
4588 * kevent, we need to make sure that we watch the job so that we can
4589 * respawn it.
4590 *
4591 * See <rdar://problem/10140809>.
4592 */
4593 job_watch(j);
4594 }
4595
4596 #if HAVE_SYSTEMSTATS
4597 if (systemstats_is_enabled()) {
4598 /* We don't really *need* to make the full rusage call -- it
4599 * will be mostly 0s and very small numbers. We only need
4600 * ri_proc_start_abstime, because that's how we disambiguiate
4601 * PIDs when they wrap around; and the UUID.
4602 * In the future we should use the 64-bit process unique ID,
4603 * so there's nothing to disambiguiate, and skip the full
4604 * rusage call here.
4605 *
4606 * Well, the future is now.
4607 */
4608 if (_systemstats_get_property(SYSTEMSTATS_API_VERSION, SYSTEMSTATS_WRITER_launchd, SYSTEMSTATS_PROPERTY_LAUNCHD_SHOULD_LOG_JOB_START)) {
4609 job_log_perf_statistics(j, NULL, -3);
4610 }
4611 }
4612 #endif
4613 j->wait4debugger_oneshot = false;
4614 if (likely(!j->stall_before_exec)) {
4615 job_uncork_fork(j);
4616 }
4617 break;
4618 }
4619 }
4620
4621 void
job_start_child(job_t j)4622 job_start_child(job_t j)
4623 {
4624 typeof(posix_spawn) *psf;
4625 const char *file2exec = "/usr/libexec/launchproxy";
4626 const char **argv;
4627 posix_spawnattr_t spattr;
4628 int gflags = GLOB_NOSORT|GLOB_NOCHECK|GLOB_TILDE|GLOB_DOOFFS;
4629 glob_t g;
4630 short spflags = POSIX_SPAWN_SETEXEC;
4631 int psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_BACKGROUND;
4632 size_t binpref_out_cnt = 0;
4633 size_t i;
4634
4635 (void)job_assumes_zero(j, posix_spawnattr_init(&spattr));
4636
4637 job_setup_attributes(j);
4638
4639 bool use_xpcproxy = false;
4640 struct waiting4attach *w4a = waiting4attach_find(j->mgr, j);
4641 if (w4a) {
4642 (void)setenv(XPC_SERVICE_ENV_ATTACHED, "1", 1);
4643 if (!j->xpc_service) {
4644 use_xpcproxy = true;
4645 }
4646 }
4647
4648 if (use_xpcproxy) {
4649 argv = alloca(3 * sizeof(char *));
4650 argv[0] = "/usr/libexec/xpcproxy";
4651 argv[1] = "-debug";
4652 argv[2] = NULL;
4653
4654 file2exec = argv[0];
4655 } else if (unlikely(j->argv && j->globargv)) {
4656 g.gl_offs = 1;
4657 for (i = 0; i < j->argc; i++) {
4658 if (i > 0) {
4659 gflags |= GLOB_APPEND;
4660 }
4661 if (glob(j->argv[i], gflags, NULL, &g) != 0) {
4662 job_log_error(j, LOG_ERR, "glob(\"%s\")", j->argv[i]);
4663 syslog(LOG_ERR, "glob error");
4664 DEBUG_EXIT(EXIT_FAILURE);
4665 }
4666 }
4667 g.gl_pathv[0] = (char *)file2exec;
4668 argv = (const char **)g.gl_pathv;
4669 } else if (likely(j->argv)) {
4670 argv = alloca((j->argc + 2) * sizeof(char *));
4671 argv[0] = file2exec;
4672 for (i = 0; i < j->argc; i++) {
4673 argv[i + 1] = j->argv[i];
4674 }
4675 argv[i + 1] = NULL;
4676 } else {
4677 argv = alloca(3 * sizeof(char *));
4678 argv[0] = file2exec;
4679 argv[1] = j->prog;
4680 argv[2] = NULL;
4681 }
4682
4683 if (likely(!(j->inetcompat || use_xpcproxy))) {
4684 argv++;
4685 }
4686
4687 if (unlikely(j->wait4debugger || j->wait4debugger_oneshot)) {
4688 if (!j->app) {
4689 job_log(j, LOG_WARNING, "Spawned and waiting for the debugger to attach before continuing...");
4690 }
4691 spflags |= POSIX_SPAWN_START_SUSPENDED;
4692 }
4693
4694 #if !TARGET_OS_EMBEDDED
4695 if (unlikely(j->disable_aslr)) {
4696 spflags |= _POSIX_SPAWN_DISABLE_ASLR;
4697 }
4698 #endif
4699 spflags |= j->pstype;
4700
4701 (void)job_assumes_zero(j, posix_spawnattr_setflags(&spattr, spflags));
4702 if (unlikely(j->j_binpref_cnt)) {
4703 (void)job_assumes_zero(j, posix_spawnattr_setbinpref_np(&spattr, j->j_binpref_cnt, j->j_binpref, &binpref_out_cnt));
4704 (void)job_assumes(j, binpref_out_cnt == j->j_binpref_cnt);
4705 }
4706
4707 psproctype = j->psproctype;
4708 (void)job_assumes_zero(j, posix_spawnattr_setprocesstype_np(&spattr, psproctype));
4709
4710 #if TARGET_OS_EMBEDDED
4711 /* Set jetsam attributes. POSIX_SPAWN_JETSAM_USE_EFFECTIVE_PRIORITY guards
4712 * against a race which arises if, during spawn, an initial jetsam property
4713 * update occurs before the values below are applied. In this case, the flag
4714 * ensures that the subsequent change is ignored; the explicit update should
4715 * be given priority.
4716 */
4717 (void)job_assumes_zero(j, posix_spawnattr_setjetsam(&spattr,
4718 POSIX_SPAWN_JETSAM_USE_EFFECTIVE_PRIORITY | (j->jetsam_memory_limit_background ? POSIX_SPAWN_JETSAM_HIWATER_BACKGROUND : 0),
4719 j->jetsam_priority, j->jetsam_memlimit));
4720 #endif
4721
4722 #ifdef notyet
4723 /* XXX: not yet: https://bugs.freenas.org/issues/10186 */
4724 mach_port_array_t sports = NULL;
4725 mach_msg_type_number_t sports_cnt = 0;
4726 kern_return_t kr = vproc_mig_get_listener_port_rights(bootstrap_port, &sports, &sports_cnt);
4727 if (kr == 0 && sports_cnt) {
4728 /* For some reason, this SPI takes a count as a signed quantity. */
4729 (void)posix_spawnattr_set_importancewatch_port_np(&spattr, (int)sports_cnt, sports);
4730
4731 /* All "count" parameters in MIG are counts of the array. So an array of
4732 * mach_port_t containing 10 elements will have a count of ten, but it
4733 * will occupy 40 bytes. So we must do the multiplication here to pass
4734 * the correct size.
4735 *
4736 * Note that we do NOT release the send rights. We need them to be valid
4737 * at the time they are passed to posix_spawn(2). When we exec(3) using
4738 * posix_spawn(2), they'll be cleaned up anyway.
4739 */
4740 mig_deallocate((vm_address_t)sports, sports_cnt * sizeof(sports[0]));
4741 } else if (kr != BOOTSTRAP_UNKNOWN_SERVICE) {
4742 (void)job_assumes_zero(j, kr);
4743 }
4744 #endif
4745
4746 #if TARGET_OS_EMBEDDED
4747 if (!j->app || j->system_app) {
4748 (void)job_assumes_zero(j, posix_spawnattr_setcpumonitor_default(&spattr));
4749 }
4750 #else
4751 (void)job_assumes_zero(j, posix_spawnattr_setcpumonitor_default(&spattr));
4752 #endif
4753
4754 #if 0
4755 #if !TARGET_OS_EMBEDDED
4756 struct task_qos_policy qosinfo = {
4757 .task_latency_qos_tier = LATENCY_QOS_LAUNCH_DEFAULT_TIER,
4758 .task_throughput_qos_tier = THROUGHPUT_QOS_LAUNCH_DEFAULT_TIER,
4759 };
4760
4761 if (!j->legacy_timers) {
4762 kr = task_policy_set(mach_task_self(), TASK_BASE_QOS_POLICY, (task_policy_t)&qosinfo, TASK_QOS_POLICY_COUNT);
4763 (void)job_assumes_zero_p(j, kr);
4764 }
4765 #endif
4766 #endif
4767
4768 #if HAVE_RESPONSIBILITY
4769 /* Specify which process is responsible for the new job. Per-app XPC
4770 * services are the responsibility of the app. Other processes are
4771 * responsible for themselves. This decision is final and also applies
4772 * to the process's children, so don't initialize responsibility when
4773 * starting a per-user launchd.
4774 */
4775 if (j->mgr->req_pid) {
4776 responsibility_init2(j->mgr->req_pid, NULL);
4777 } else if (!j->per_user) {
4778 responsibility_init2(getpid(), j->prog ? j->prog : j->argv[0]);
4779 }
4780 #endif
4781
4782 #if HAVE_QUARANTINE
4783 if (j->quarantine_data) {
4784 qtn_proc_t qp;
4785
4786 if (job_assumes(j, qp = qtn_proc_alloc())) {
4787 if (job_assumes_zero(j, qtn_proc_init_with_data(qp, j->quarantine_data, j->quarantine_data_sz) == 0)) {
4788 (void)job_assumes_zero(j, qtn_proc_apply_to_self(qp));
4789 }
4790 }
4791 }
4792 #endif
4793
4794 #if HAVE_SANDBOX
4795 #if TARGET_OS_EMBEDDED
4796 struct sandbox_spawnattrs sbattrs;
4797 if (j->seatbelt_profile || j->container_identifier) {
4798 sandbox_spawnattrs_init(&sbattrs);
4799 if (j->seatbelt_profile) {
4800 sandbox_spawnattrs_setprofilename(&sbattrs, j->seatbelt_profile);
4801 }
4802 if (j->container_identifier) {
4803 sandbox_spawnattrs_setcontainer(&sbattrs, j->container_identifier);
4804 }
4805 (void)job_assumes_zero(j, posix_spawnattr_setmacpolicyinfo_np(&spattr, "Sandbox", &sbattrs, sizeof(sbattrs)));
4806 }
4807 #else
4808 if (j->seatbelt_profile) {
4809 char *seatbelt_err_buf = NULL;
4810
4811 if (job_assumes_zero_p(j, sandbox_init(j->seatbelt_profile, j->seatbelt_flags, &seatbelt_err_buf)) == -1) {
4812 if (seatbelt_err_buf) {
4813 job_log(j, LOG_ERR, "Sandbox failed to init: %s", seatbelt_err_buf);
4814 }
4815 goto out_bad;
4816 }
4817 }
4818 #endif
4819 #endif
4820
4821 psf = j->prog ? posix_spawn : posix_spawnp;
4822
4823 if (likely(!(j->inetcompat || use_xpcproxy))) {
4824 file2exec = j->prog ? j->prog : argv[0];
4825 }
4826
4827 errno = psf(NULL, file2exec, NULL, &spattr, (char *const *)argv, environ);
4828 syslog(LOG_ERR, "job_start failed %s\n", strerror(errno));
4829 sleep(20);
4830
4831 #if HAVE_SANDBOX && !TARGET_OS_EMBEDDED
4832 out_bad:
4833 #endif
4834 syslog(LOG_ERR, "errno=%d", errno);
4835 DEBUG_EXIT(errno);
4836 }
4837
4838 void
jobmgr_export_env_from_other_jobs(jobmgr_t jm,launch_data_t dict)4839 jobmgr_export_env_from_other_jobs(jobmgr_t jm, launch_data_t dict)
4840 {
4841 launch_data_t tmp;
4842 struct envitem *ei;
4843 job_t ji;
4844
4845 if (jm->parentmgr) {
4846 jobmgr_export_env_from_other_jobs(jm->parentmgr, dict);
4847 } else {
4848 char **tmpenviron = environ;
4849 for (; *tmpenviron; tmpenviron++) {
4850 char envkey[1024];
4851 launch_data_t s = launch_data_alloc(LAUNCH_DATA_STRING);
4852 launch_data_set_string(s, strchr(*tmpenviron, '=') + 1);
4853 strncpy(envkey, *tmpenviron, sizeof(envkey));
4854 *(strchr(envkey, '=')) = '\0';
4855 launch_data_dict_insert(dict, s, envkey);
4856 }
4857 }
4858
4859 LIST_FOREACH(ji, &jm->jobs, sle) {
4860 SLIST_FOREACH(ei, &ji->global_env, sle) {
4861 if ((tmp = launch_data_new_string(ei->value))) {
4862 launch_data_dict_insert(dict, tmp, ei->key);
4863 }
4864 }
4865 }
4866 }
4867
4868 void
jobmgr_setup_env_from_other_jobs(jobmgr_t jm)4869 jobmgr_setup_env_from_other_jobs(jobmgr_t jm)
4870 {
4871 struct envitem *ei;
4872 job_t ji;
4873
4874 if (jm->parentmgr) {
4875 jobmgr_setup_env_from_other_jobs(jm->parentmgr);
4876 }
4877
4878 LIST_FOREACH(ji, &jm->global_env_jobs, global_env_sle) {
4879 SLIST_FOREACH(ei, &ji->global_env, sle) {
4880 setenv(ei->key, ei->value, 1);
4881 }
4882 }
4883 }
4884
4885 void
job_log_pids_with_weird_uids(job_t j)4886 job_log_pids_with_weird_uids(job_t j)
4887 {
4888 size_t len = sizeof(pid_t) * get_kern_max_proc();
4889 pid_t *pids = NULL;
4890 uid_t u = j->mach_uid;
4891 int i = 0, kp_cnt = 0;
4892
4893 if (!launchd_apple_internal) {
4894 return;
4895 }
4896
4897 pids = malloc(len);
4898 if (!job_assumes(j, pids != NULL)) {
4899 return;
4900 }
4901
4902 runtime_ktrace(RTKT_LAUNCHD_FINDING_WEIRD_UIDS, j->p, u, 0);
4903
4904 /* libproc actually has some serious performance drawbacks when used over sysctl(3) in
4905 * scenarios like this. Whereas sysctl(3) can give us back all the kinfo_proc's in
4906 * one kernel call, libproc requires that we get a list of PIDs we're interested in
4907 * (in this case, all PIDs on the system) and then get a single proc_bsdshortinfo
4908 * struct back in a single call for each one.
4909 *
4910 * This kind of thing is also more inherently racy than sysctl(3). While sysctl(3)
4911 * returns a snapshot, it returns the whole shebang at once. Any PIDs given to us by
4912 * libproc could go stale before we call proc_pidinfo().
4913 *
4914 * Note that proc_list*() APIs return the number of PIDs given back, not the number
4915 * of bytes written to the buffer.
4916 */
4917 if (job_assumes_zero_p(j, (kp_cnt = proc_listallpids(pids, len))) == -1) {
4918 goto out;
4919 }
4920
4921 for (i = 0; i < kp_cnt; i++) {
4922 struct proc_bsdshortinfo proc;
4923 /* We perhaps should not log a bug here if we get ESRCH back, due to the race
4924 * detailed above.
4925 */
4926 if (proc_pidinfo(pids[i], PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) {
4927 if (errno != ESRCH) {
4928 (void)job_assumes_zero(j, errno);
4929 }
4930 continue;
4931 }
4932
4933 uid_t i_euid = proc.pbsi_uid;
4934 uid_t i_uid = proc.pbsi_ruid;
4935 uid_t i_svuid = proc.pbsi_svuid;
4936 pid_t i_pid = pids[i];
4937
4938 if (i_euid != u && i_uid != u && i_svuid != u) {
4939 continue;
4940 }
4941
4942 job_log(j, LOG_ERR, "PID %u \"%s\" has no account to back it! Real/effective/saved UIDs: %u/%u/%u", i_pid, proc.pbsi_comm, i_uid, i_euid, i_svuid);
4943
4944 // Temporarily disabled due to 5423935 and 4946119.
4945 #if 0
4946 // Ask the accountless process to exit.
4947 (void)job_assumes_zero_p(j, kill2(i_pid, SIGTERM));
4948 #endif
4949 }
4950
4951 out:
4952 free(pids);
4953 }
4954
4955 static struct passwd *
job_getpwnam(job_t j,const char * name)4956 job_getpwnam(job_t j, const char *name)
4957 {
4958 /*
4959 * methodology for system daemons
4960 *
4961 * first lookup user record without any opendirectoryd interaction,
4962 * we don't know what interprocess dependencies might be in flight.
4963 * if that fails, we re-enable opendirectoryd interaction and
4964 * re-issue the lookup. We have to disable the libinfo L1 cache
4965 * otherwise libinfo will return the negative cache entry on the retry
4966 */
4967 #if !TARGET_OS_EMBEDDED
4968 struct passwd *pw = NULL;
4969
4970 if (pid1_magic && j->mgr == root_jobmgr) {
4971 // 1 == SEARCH_MODULE_FLAG_DISABLED
4972 si_search_module_set_flags("ds", 1);
4973 gL1CacheEnabled = false;
4974
4975 pw = getpwnam(name);
4976 si_search_module_set_flags("ds", 0);
4977 }
4978
4979 if (pw == NULL) {
4980 pw = getpwnam(name);
4981 }
4982
4983 return pw;
4984 #else
4985 #pragma unused (j)
4986 return getpwnam(name);
4987 #endif
4988 }
4989
4990 static struct group *
job_getgrnam(job_t j,const char * name)4991 job_getgrnam(job_t j, const char *name)
4992 {
4993 #if !TARGET_OS_EMBEDDED
4994 struct group *gr = NULL;
4995
4996 if (pid1_magic && j->mgr == root_jobmgr) {
4997 si_search_module_set_flags("ds", 1);
4998 gL1CacheEnabled = false;
4999
5000 gr = getgrnam(name);
5001
5002 si_search_module_set_flags("ds", 0);
5003 }
5004
5005 if (gr == NULL) {
5006 gr = getgrnam(name);
5007 }
5008
5009 return gr;
5010 #else
5011 #pragma unused (j)
5012 return getgrnam(name);
5013 #endif
5014 }
5015
5016 void
job_postfork_test_user(job_t j)5017 job_postfork_test_user(job_t j)
5018 {
5019 // This function is all about 5201578
5020
5021 const char *home_env_var = getenv("HOME");
5022 const char *user_env_var = getenv("USER");
5023 const char *logname_env_var = getenv("LOGNAME");
5024 uid_t tmp_uid, local_uid = getuid();
5025 gid_t tmp_gid, local_gid = getgid();
5026 char shellpath[PATH_MAX];
5027 char homedir[PATH_MAX];
5028 char loginname[2000];
5029 struct passwd *pwe;
5030
5031
5032 if (!job_assumes(j, home_env_var && user_env_var && logname_env_var
5033 && strcmp(user_env_var, logname_env_var) == 0)) {
5034 goto out_bad;
5035 }
5036
5037 if ((pwe = job_getpwnam(j, user_env_var)) == NULL) {
5038 job_log(j, LOG_ERR, "The account \"%s\" has been deleted out from under us!", user_env_var);
5039 goto out_bad;
5040 }
5041
5042 /*
5043 * We must copy the results of getpw*().
5044 *
5045 * Why? Because subsequent API calls may call getpw*() as a part of
5046 * their implementation. Since getpw*() returns a [now thread scoped]
5047 * global, we must therefore cache the results before continuing.
5048 */
5049
5050 tmp_uid = pwe->pw_uid;
5051 tmp_gid = pwe->pw_gid;
5052
5053 strlcpy(shellpath, pwe->pw_shell, sizeof(shellpath));
5054 strlcpy(loginname, pwe->pw_name, sizeof(loginname));
5055 strlcpy(homedir, pwe->pw_dir, sizeof(homedir));
5056
5057 if (strcmp(loginname, logname_env_var) != 0) {
5058 job_log(j, LOG_ERR, "The %s environmental variable changed out from under us!", "USER");
5059 goto out_bad;
5060 }
5061 if (strcmp(homedir, home_env_var) != 0) {
5062 job_log(j, LOG_ERR, "The %s environmental variable changed out from under us!", "HOME");
5063 goto out_bad;
5064 }
5065 if (local_uid != tmp_uid) {
5066 job_log(j, LOG_ERR, "The %cID of the account (%u) changed out from under us (%u)!",
5067 'U', tmp_uid, local_uid);
5068 goto out_bad;
5069 }
5070 if (local_gid != tmp_gid) {
5071 job_log(j, LOG_ERR, "The %cID of the account (%u) changed out from under us (%u)!",
5072 'G', tmp_gid, local_gid);
5073 goto out_bad;
5074 }
5075
5076 return;
5077 out_bad:
5078 #if 0
5079 (void)job_assumes_zero_p(j, kill2(getppid(), SIGTERM));
5080 DEBUG_EXIT(EXIT_FAILURE);
5081 #else
5082 job_log(j, LOG_WARNING, "In a future build of the OS, this error will be fatal.");
5083 #endif
5084 }
5085
5086 void
job_postfork_become_user(job_t j)5087 job_postfork_become_user(job_t j)
5088 {
5089 char loginname[2000];
5090 char tmpdirpath[PATH_MAX];
5091 char shellpath[PATH_MAX];
5092 char homedir[PATH_MAX];
5093 struct passwd *pwe;
5094 size_t r;
5095 gid_t desired_gid = -1;
5096 uid_t desired_uid = -1;
5097
5098 if (getuid() != 0) {
5099 return job_postfork_test_user(j);
5100 }
5101
5102 /*
5103 * I contend that having UID == 0 and GID != 0 is of dubious value.
5104 * Nevertheless, this used to work in Tiger. See: 5425348
5105 */
5106 if (j->groupname && !j->username) {
5107 j->username = strdup("root");
5108 }
5109
5110 if (j->username) {
5111 if ((pwe = job_getpwnam(j, j->username)) == NULL) {
5112 job_log(j, LOG_ERR, "getpwnam(\"%s\") failed", j->username);
5113 DEBUG_EXIT(ESRCH);
5114 }
5115 } else if (j->mach_uid) {
5116 if ((pwe = getpwuid(j->mach_uid)) == NULL) {
5117 job_log(j, LOG_ERR, "getpwuid(\"%u\") failed", j->mach_uid);
5118 job_log_pids_with_weird_uids(j);
5119 DEBUG_EXIT(ESRCH);
5120 }
5121 } else {
5122 return;
5123 }
5124
5125 /*
5126 * We must copy the results of getpw*().
5127 *
5128 * Why? Because subsequent API calls may call getpw*() as a part of
5129 * their implementation. Since getpw*() returns a [now thread scoped]
5130 * global, we must therefore cache the results before continuing.
5131 */
5132
5133 desired_uid = pwe->pw_uid;
5134 desired_gid = pwe->pw_gid;
5135
5136 strlcpy(shellpath, pwe->pw_shell, sizeof(shellpath));
5137 strlcpy(loginname, pwe->pw_name, sizeof(loginname));
5138 strlcpy(homedir, pwe->pw_dir, sizeof(homedir));
5139
5140 if (unlikely(pwe->pw_expire && time(NULL) >= pwe->pw_expire)) {
5141 job_log(j, LOG_ERR, "Expired account");
5142 DEBUG_EXIT(EXIT_FAILURE);
5143 }
5144
5145
5146 if (unlikely(j->username && strcmp(j->username, loginname) != 0)) {
5147 job_log(j, LOG_WARNING, "Suspicious setup: User \"%s\" maps to user: %s", j->username, loginname);
5148 } else if (unlikely(j->mach_uid && (j->mach_uid != desired_uid))) {
5149 job_log(j, LOG_WARNING, "Suspicious setup: UID %u maps to UID %u", j->mach_uid, desired_uid);
5150 }
5151
5152 if (j->groupname) {
5153 struct group *gre;
5154
5155 if (unlikely((gre = job_getgrnam(j, j->groupname)) == NULL)) {
5156 job_log(j, LOG_ERR, "getgrnam(\"%s\") failed", j->groupname);
5157 DEBUG_EXIT(ESRCH);
5158 }
5159
5160 desired_gid = gre->gr_gid;
5161 }
5162
5163 if (job_assumes_zero_p(j, setlogin(loginname)) == -1) {
5164 DEBUG_EXIT(EXIT_FAILURE);
5165 }
5166
5167 if (job_assumes_zero_p(j, setgid(desired_gid)) == -1) {
5168 DEBUG_EXIT(EXIT_FAILURE);
5169 }
5170
5171 /*
5172 * The kernel team and the DirectoryServices team want initgroups()
5173 * called after setgid(). See 4616864 for more information.
5174 */
5175
5176 if (likely(!j->no_init_groups)) {
5177 #if 1
5178 if (job_assumes_zero_p(j, initgroups(loginname, desired_gid)) == -1) {
5179 DEBUG_EXIT(EXIT_FAILURE);
5180 }
5181 #else
5182 /* Do our own little initgroups(). We do this to guarantee that we're
5183 * always opted into dynamic group resolution in the kernel. initgroups(3)
5184 * does not make this guarantee.
5185 */
5186 int groups[NGROUPS], ngroups;
5187
5188 // A failure here isn't fatal, and we'll still get data we can use.
5189 (void)job_assumes_zero_p(j, getgrouplist(j->username, desired_gid, groups, &ngroups));
5190
5191 if (job_assumes_zero_p(j, syscall(SYS_initgroups, ngroups, groups, desired_uid)) == -1) {
5192 DEBUG_EXIT(EXIT_FAILURE);
5193 }
5194 #endif
5195 }
5196
5197 if (job_assumes_zero_p(j, setuid(desired_uid)) == -1) {
5198 DEBUG_EXIT(EXIT_FAILURE);
5199 }
5200
5201 r = confstr(_CS_DARWIN_USER_TEMP_DIR, tmpdirpath, sizeof(tmpdirpath));
5202
5203 if (likely(r > 0 && r < sizeof(tmpdirpath))) {
5204 setenv("TMPDIR", tmpdirpath, 0);
5205 }
5206
5207 setenv("SHELL", shellpath, 0);
5208 setenv("HOME", homedir, 0);
5209 setenv("USER", loginname, 0);
5210 setenv("LOGNAME", loginname, 0);
5211 }
5212
5213 void
job_setup_attributes(job_t j)5214 job_setup_attributes(job_t j)
5215 {
5216 struct limititem *li;
5217 struct envitem *ei;
5218
5219 if (unlikely(j->setnice)) {
5220 (void)job_assumes_zero_p(j, setpriority(PRIO_PROCESS, 0, j->nice));
5221 }
5222
5223 SLIST_FOREACH(li, &j->limits, sle) {
5224 struct rlimit rl;
5225
5226 if (job_assumes_zero_p(j, getrlimit(li->which, &rl) == -1)) {
5227 continue;
5228 }
5229
5230 if (li->sethard) {
5231 rl.rlim_max = li->lim.rlim_max;
5232 }
5233 if (li->setsoft) {
5234 rl.rlim_cur = li->lim.rlim_cur;
5235 }
5236
5237 if (setrlimit(li->which, &rl) == -1) {
5238 job_log_error(j, LOG_WARNING, "setrlimit()");
5239 }
5240 }
5241
5242 if (unlikely(!j->inetcompat && j->session_create)) {
5243 launchd_SessionCreate();
5244 }
5245
5246 if (unlikely(j->low_pri_io)) {
5247 (void)job_assumes_zero_p(j, setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE));
5248 }
5249 if (j->low_priority_background_io) {
5250 (void)job_assumes_zero_p(j, setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_DARWIN_BG, IOPOL_THROTTLE));
5251 }
5252 if (unlikely(j->rootdir)) {
5253 (void)job_assumes_zero_p(j, chroot(j->rootdir));
5254 (void)job_assumes_zero_p(j, chdir("."));
5255 }
5256
5257 job_postfork_become_user(j);
5258
5259 if (unlikely(j->workingdir)) {
5260 if (chdir(j->workingdir) == -1) {
5261 if (errno == ENOENT || errno == ENOTDIR) {
5262 job_log(j, LOG_ERR, "Job specified non-existent working directory: %s", j->workingdir);
5263 } else {
5264 (void)job_assumes_zero(j, errno);
5265 }
5266 }
5267 }
5268
5269 if (unlikely(j->setmask)) {
5270 umask(j->mask);
5271 }
5272
5273 if (j->stdin_fd) {
5274 (void)job_assumes_zero_p(j, dup2(j->stdin_fd, STDIN_FILENO));
5275 } else {
5276 job_setup_fd(j, STDIN_FILENO, j->stdinpath, O_RDONLY|O_CREAT);
5277 }
5278 job_setup_fd(j, STDOUT_FILENO, j->stdoutpath, O_WRONLY|O_CREAT|O_APPEND);
5279 job_setup_fd(j, STDERR_FILENO, j->stderrpath, O_WRONLY|O_CREAT|O_APPEND);
5280
5281 jobmgr_setup_env_from_other_jobs(j->mgr);
5282
5283 SLIST_FOREACH(ei, &j->env, sle) {
5284 setenv(ei->key, ei->value, 1);
5285 }
5286
5287 #if !TARGET_OS_EMBEDDED
5288 if (j->jetsam_properties) {
5289 (void)job_assumes_zero(j, proc_setpcontrol(PROC_SETPC_TERMINATE));
5290 }
5291 #endif
5292
5293 #if TARGET_OS_EMBEDDED
5294 if (j->main_thread_priority != 0) {
5295 struct sched_param params;
5296 bzero(¶ms, sizeof(params));
5297 params.sched_priority = j->main_thread_priority;
5298 (void)job_assumes_zero_p(j, pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶ms));
5299 }
5300 #endif
5301
5302 /*
5303 * We'd like to call setsid() unconditionally, but we have reason to
5304 * believe that prevents launchd from being able to send signals to
5305 * setuid children. We'll settle for process-groups.
5306 */
5307 if (getppid() != 1) {
5308 (void)job_assumes_zero_p(j, setpgid(0, 0));
5309 } else {
5310 (void)job_assumes_zero_p(j, setsid());
5311 }
5312 }
5313
5314 void
job_setup_fd(job_t j,int target_fd,const char * path,int flags)5315 job_setup_fd(job_t j, int target_fd, const char *path, int flags)
5316 {
5317 int fd;
5318
5319 if (!path) {
5320 return;
5321 }
5322
5323 if ((fd = open(path, flags|O_NOCTTY, DEFFILEMODE)) == -1) {
5324 job_log_error(j, LOG_WARNING, "open(\"%s\", ...)", path);
5325 return;
5326 }
5327
5328 (void)job_assumes_zero_p(j, dup2(fd, target_fd));
5329 (void)job_assumes_zero(j, runtime_close(fd));
5330 }
5331
5332 void
calendarinterval_setalarm(job_t j,struct calendarinterval * ci)5333 calendarinterval_setalarm(job_t j, struct calendarinterval *ci)
5334 {
5335 struct calendarinterval *ci_iter, *ci_prev = NULL;
5336 time_t later, head_later;
5337
5338 later = cronemu(ci->when.tm_mon, ci->when.tm_mday, ci->when.tm_hour, ci->when.tm_min);
5339
5340 if (ci->when.tm_wday != -1) {
5341 time_t otherlater = cronemu_wday(ci->when.tm_wday, ci->when.tm_hour, ci->when.tm_min);
5342
5343 if (ci->when.tm_mday == -1) {
5344 later = otherlater;
5345 } else {
5346 later = later < otherlater ? later : otherlater;
5347 }
5348 }
5349
5350 ci->when_next = later;
5351
5352 LIST_FOREACH(ci_iter, &sorted_calendar_events, global_sle) {
5353 if (ci->when_next < ci_iter->when_next) {
5354 LIST_INSERT_BEFORE(ci_iter, ci, global_sle);
5355 break;
5356 }
5357
5358 ci_prev = ci_iter;
5359 }
5360
5361 if (ci_iter == NULL) {
5362 // ci must want to fire after every other timer, or there are no timers
5363
5364 if (LIST_EMPTY(&sorted_calendar_events)) {
5365 LIST_INSERT_HEAD(&sorted_calendar_events, ci, global_sle);
5366 } else {
5367 LIST_INSERT_AFTER(ci_prev, ci, global_sle);
5368 }
5369 }
5370
5371 head_later = LIST_FIRST(&sorted_calendar_events)->when_next;
5372
5373 if (job_assumes_zero_p(j, kevent_mod((uintptr_t)&sorted_calendar_events, EVFILT_TIMER, EV_ADD, NOTE_ABSOLUTE|NOTE_SECONDS, head_later, root_jobmgr)) != -1) {
5374 char time_string[100];
5375 size_t time_string_len;
5376
5377 ctime_r(&later, time_string);
5378 time_string_len = strlen(time_string);
5379
5380 if (likely(time_string_len && time_string[time_string_len - 1] == '\n')) {
5381 time_string[time_string_len - 1] = '\0';
5382 }
5383
5384 job_log(j, LOG_INFO, "Scheduled to run again at %s", time_string);
5385 }
5386 }
5387
5388 bool
jobmgr_log_bug(_SIMPLE_STRING asl_message,void * ctx,const char * message)5389 jobmgr_log_bug(_SIMPLE_STRING asl_message __attribute__((unused)), void *ctx, const char *message)
5390 {
5391 jobmgr_t jm = ctx;
5392 jobmgr_log(jm, LOG_ERR, "%s", message);
5393
5394 return true;
5395 }
5396
5397 bool
job_log_bug(_SIMPLE_STRING asl_message,void * ctx,const char * message)5398 job_log_bug(_SIMPLE_STRING asl_message __attribute__((unused)), void *ctx, const char *message)
5399 {
5400 job_t j = ctx;
5401 job_log(j, LOG_ERR, "%s", message);
5402
5403 return true;
5404 }
5405
5406 // ri: NULL = please sample j->p; non-NULL = use this sample
5407 void
job_log_perf_statistics(job_t j,struct rusage_info_v1 * ri,int64_t exit_status)5408 job_log_perf_statistics(job_t j, struct rusage_info_v1 *ri, int64_t exit_status)
5409 {
5410 #if HAVE_SYSTEMSTATS
5411 if (j->anonymous || !j->p) {
5412 return;
5413 }
5414 if (!systemstats_is_enabled()) {
5415 return;
5416 }
5417 const char *name;
5418 if (j->cfbundleidentifier) {
5419 name = j->cfbundleidentifier;
5420 } else {
5421 name = j->label;
5422 }
5423 int r = 0;
5424 struct rusage_info_v1 ris;
5425 if (ri == NULL) {
5426 ri = &ris;
5427 r = proc_pid_rusage(j->p, RUSAGE_INFO_V1, (rusage_info_t)ri);
5428 }
5429 if (r == -1) {
5430 return;
5431 }
5432 job_log_systemstats(j->p, j->uniqueid, runtime_get_uniqueid(), j->mgr->req_pid, j->mgr->req_uniqueid, name, ri, exit_status);
5433 #else
5434 #pragma unused (j, ri, exit_status)
5435 #endif
5436 }
5437
5438 #if HAVE_SYSTEMSTATS
5439 // ri: NULL = don't write fields from ri; non-NULL = use this sample
5440 static
5441 void
job_log_systemstats(pid_t pid,uint64_t uniqueid,uint64_t parent_uniqueid,pid_t req_pid,uint64_t req_uniqueid,const char * name,struct rusage_info_v1 * ri,int64_t exit_status)5442 job_log_systemstats(pid_t pid, uint64_t uniqueid, uint64_t parent_uniqueid, pid_t req_pid, uint64_t req_uniqueid, const char *name, struct rusage_info_v1 *ri, int64_t exit_status)
5443 {
5444 if (!systemstats_is_enabled()) {
5445 return;
5446 }
5447
5448 struct systemstats_process_usage_s info;
5449 bzero(&info, sizeof(info));
5450 info.name = name;
5451 info.pid = pid;
5452 info.exit_status = exit_status;
5453 info.uid = getuid();
5454 info.ppid = getpid();
5455 info.responsible_pid = req_pid;
5456
5457 if (likely(ri)) {
5458 info.macho_uuid = (const uint8_t *)&ri->ri_uuid;
5459 info.user_time = ri->ri_user_time;
5460 info.system_time = ri->ri_system_time;
5461 info.pkg_idle_wkups = ri->ri_pkg_idle_wkups;
5462 info.interrupt_wkups = ri->ri_interrupt_wkups;
5463 info.proc_start_abstime = ri->ri_proc_start_abstime;
5464 info.proc_exit_abstime = ri->ri_proc_exit_abstime;
5465 #if SYSTEMSTATS_API_VERSION >= 20130319
5466 info.pageins = ri->ri_pageins;
5467 info.wired_size = ri->ri_wired_size;
5468 info.resident_size = ri->ri_resident_size;
5469 info.phys_footprint = ri->ri_phys_footprint;
5470 // info.purgeablesize = ???
5471 #endif
5472 #if SYSTEMSTATS_API_VERSION >= 20130328
5473 info.child_user_time = ri->ri_child_user_time;
5474 info.child_system_time = ri->ri_child_system_time;
5475 info.child_pkg_idle_wkups = ri->ri_child_pkg_idle_wkups;
5476 info.child_interrupt_wkups = ri->ri_child_interrupt_wkups;
5477 info.child_pageins = ri->ri_child_pageins;
5478 info.child_elapsed_abstime = ri->ri_child_elapsed_abstime;
5479 #endif
5480 }
5481 #if SYSTEMSTATS_API_VERSION >= 20130410
5482 info.uniqueid = uniqueid;
5483 info.parent_uniqueid = parent_uniqueid;
5484 info.responsible_uniqueid = req_uniqueid;
5485 #endif
5486 systemstats_write_process_usage(&info);
5487 }
5488 #endif /* HAVE_SYSTEMSTATS */
5489
5490 struct waiting4attach *
waiting4attach_new(jobmgr_t jm,const char * name,mach_port_t port,pid_t dest,xpc_service_type_t type)5491 waiting4attach_new(jobmgr_t jm, const char *name, mach_port_t port, pid_t dest, xpc_service_type_t type)
5492 {
5493 size_t xtra = strlen(name) + 1;
5494
5495 struct waiting4attach *w4a = malloc(sizeof(*w4a) + xtra);
5496 if (!w4a) {
5497 return NULL;
5498 }
5499
5500 w4a->port = port;
5501 w4a->dest = dest;
5502 w4a->type = type;
5503 (void)strcpy(w4a->name, name);
5504
5505 if (dest) {
5506 LIST_INSERT_HEAD(&_launchd_domain_waiters, w4a, le);
5507 } else {
5508 LIST_INSERT_HEAD(&jm->attaches, w4a, le);
5509 }
5510
5511
5512 (void)jobmgr_assumes_zero(jm, launchd_mport_notify_req(port, MACH_NOTIFY_DEAD_NAME));
5513 return w4a;
5514 }
5515
5516 void
waiting4attach_delete(jobmgr_t jm,struct waiting4attach * w4a)5517 waiting4attach_delete(jobmgr_t jm, struct waiting4attach *w4a)
5518 {
5519 jobmgr_log(jm, LOG_DEBUG, "Canceling dead-name notification for waiter port: 0x%x", w4a->port);
5520
5521 LIST_REMOVE(w4a, le);
5522
5523 mach_port_t previous = MACH_PORT_NULL;
5524 (void)jobmgr_assumes_zero(jm, mach_port_request_notification(mach_task_self(), w4a->port, MACH_NOTIFY_DEAD_NAME, 0, MACH_PORT_NULL, MACH_MSG_TYPE_MOVE_SEND_ONCE, &previous));
5525 if (previous) {
5526 (void)jobmgr_assumes_zero(jm, launchd_mport_deallocate(previous));
5527 }
5528
5529 jobmgr_assumes_zero(jm, launchd_mport_deallocate(w4a->port));
5530 free(w4a);
5531 }
5532
5533 struct waiting4attach *
waiting4attach_find(jobmgr_t jm,job_t j)5534 waiting4attach_find(jobmgr_t jm, job_t j)
5535 {
5536 char *name2use = (char *)j->label;
5537 if (j->app) {
5538 struct envitem *ei = NULL;
5539 SLIST_FOREACH(ei, &j->env, sle) {
5540 if (strcmp(ei->key, XPC_SERVICE_RENDEZVOUS_TOKEN) == 0) {
5541 name2use = ei->value;
5542 break;
5543 }
5544 }
5545 }
5546
5547 struct waiting4attach *w4ai = NULL;
5548 LIST_FOREACH(w4ai, &jm->attaches, le) {
5549 if (strcmp(name2use, w4ai->name) == 0) {
5550 job_log(j, LOG_DEBUG, "Found attachment: %s", name2use);
5551 break;
5552 }
5553 }
5554
5555 return w4ai;
5556 }
5557
5558 void
job_logv(job_t j,int pri,int err,const char * msg,va_list ap)5559 job_logv(job_t j, int pri, int err, const char *msg, va_list ap)
5560 {
5561 const char *label2use = j ? j->label : "com.apple.launchd.job-unknown";
5562 const char *mgr2use = j ? j->mgr->name : "com.apple.launchd.jobmanager-unknown";
5563 char *newmsg;
5564 int oldmask = 0;
5565 size_t newmsgsz;
5566
5567 struct launchd_syslog_attr attr = {
5568 .from_name = launchd_label,
5569 .about_name = label2use,
5570 .session_name = mgr2use,
5571 .priority = pri,
5572 .from_uid = getuid(),
5573 .from_pid = getpid(),
5574 .about_pid = j ? j->p : 0,
5575 };
5576
5577 /* Hack: If bootstrap_port is set, we must be on the child side of a
5578 * fork(2), but before the exec*(3). Let's route the log message back to
5579 * launchd proper.
5580 */
5581 if (bootstrap_port && uflag == false) {
5582 return _vproc_logv(pri, err, msg, ap);
5583 }
5584
5585 newmsgsz = strlen(msg) + 200;
5586 newmsg = alloca(newmsgsz);
5587
5588 if (err) {
5589 #if !TARGET_OS_EMBEDDED
5590 snprintf(newmsg, newmsgsz, "%s: %d: %s", msg, err, strerror(err));
5591 #else
5592 snprintf(newmsg, newmsgsz, "(%s) %s: %d: %s", label2use, msg, err, strerror(err));
5593 #endif
5594 } else {
5595 #if !TARGET_OS_EMBEDDED
5596 snprintf(newmsg, newmsgsz, "%s", msg);
5597 #else
5598 snprintf(newmsg, newmsgsz, "(%s) %s", label2use, msg);
5599 #endif
5600 }
5601
5602 if (j && unlikely(j->debug)) {
5603 oldmask = setlogmask(LOG_UPTO(LOG_DEBUG));
5604 }
5605
5606 launchd_vsyslog(&attr, newmsg, ap);
5607
5608 if (j && unlikely(j->debug)) {
5609 setlogmask(oldmask);
5610 }
5611 }
5612
5613 void
job_log_error(job_t j,int pri,const char * msg,...)5614 job_log_error(job_t j, int pri, const char *msg, ...)
5615 {
5616 va_list ap;
5617
5618 va_start(ap, msg);
5619 job_logv(j, pri, errno, msg, ap);
5620 va_end(ap);
5621 }
5622
5623 void
job_log(job_t j,int pri,const char * msg,...)5624 job_log(job_t j, int pri, const char *msg, ...)
5625 {
5626 va_list ap;
5627
5628 va_start(ap, msg);
5629 job_logv(j, pri, 0, msg, ap);
5630 va_end(ap);
5631 }
5632
5633 #if 0
5634 void
5635 jobmgr_log_error(jobmgr_t jm, int pri, const char *msg, ...)
5636 {
5637 va_list ap;
5638
5639 va_start(ap, msg);
5640 jobmgr_logv(jm, pri, errno, msg, ap);
5641 va_end(ap);
5642 }
5643 #endif
5644
5645 void
jobmgr_log_perf_statistics(jobmgr_t jm,bool signal_children)5646 jobmgr_log_perf_statistics(jobmgr_t jm, bool signal_children)
5647 {
5648 #if HAVE_SYSTEMSTATS
5649 // Log information for kernel_task and pid 1 launchd.
5650 if (systemstats_is_enabled() && pid1_magic && jm == root_jobmgr) {
5651 #if SYSTEMSTATS_API_VERSION >= 20130328
5652 if (_systemstats_get_property(SYSTEMSTATS_API_VERSION, SYSTEMSTATS_WRITER_launchd, SYSTEMSTATS_PROPERTY_SHOULD_LOG_ENERGY_STATISTICS)) {
5653 systemstats_write_intel_energy_statistics(NULL);
5654 }
5655 #else
5656 systemstats_write_intel_energy_statistics(NULL);
5657 #endif
5658 job_log_systemstats(0, 0, 0, 0, 0, "com.apple.kernel", NULL, -1);
5659 job_log_systemstats(1, 1, 0, 1, 1, "com.apple.launchd", NULL, -1);
5660 }
5661 #endif
5662 jobmgr_t jmi = NULL;
5663 SLIST_FOREACH(jmi, &jm->submgrs, sle) {
5664 jobmgr_log_perf_statistics(jmi, signal_children);
5665 }
5666
5667 if (jm->xpc_singleton) {
5668 jobmgr_log(jm, LOG_PERF, "XPC Singleton Domain: %s", jm->shortdesc);
5669 } else if (jm->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN) {
5670 jobmgr_log(jm, LOG_PERF, "XPC Private Domain: %s", jm->owner);
5671 } else if (jm->properties & BOOTSTRAP_PROPERTY_EXPLICITSUBSET) {
5672 jobmgr_log(jm, LOG_PERF, "Created via bootstrap_subset()");
5673 }
5674
5675 jobmgr_log(jm, LOG_PERF, "Jobs in job manager:");
5676
5677 job_t ji = NULL;
5678 LIST_FOREACH(ji, &jm->jobs, sle) {
5679 job_log_perf_statistics(ji, NULL, -1);
5680 if (unlikely(signal_children) && unlikely(strstr(ji->label, "com.apple.launchd.peruser.") == ji->label)) {
5681 jobmgr_log(jm, LOG_PERF, "Sending SIGINFO to peruser launchd %d", ji->p);
5682 kill(ji->p, SIGINFO);
5683 }
5684 }
5685
5686 jobmgr_log(jm, LOG_PERF, "End of job list.");
5687 }
5688
5689 void
jobmgr_log(jobmgr_t jm,int pri,const char * msg,...)5690 jobmgr_log(jobmgr_t jm, int pri, const char *msg, ...)
5691 {
5692 va_list ap;
5693
5694 va_start(ap, msg);
5695 jobmgr_logv(jm, pri, 0, msg, ap);
5696 va_end(ap);
5697 }
5698
5699 void
jobmgr_logv(jobmgr_t jm,int pri,int err,const char * msg,va_list ap)5700 jobmgr_logv(jobmgr_t jm, int pri, int err, const char *msg, va_list ap)
5701 {
5702 if (!jm) {
5703 jm = root_jobmgr;
5704 }
5705
5706 char *newmsg;
5707 char *newname;
5708 size_t i, o, jmname_len = strlen(jm->name), newmsgsz;
5709
5710 newname = alloca((jmname_len + 1) * 2);
5711 newmsgsz = (jmname_len + 1) * 2 + strlen(msg) + 100;
5712 newmsg = alloca(newmsgsz);
5713
5714 for (i = 0, o = 0; i < jmname_len; i++, o++) {
5715 if (jm->name[i] == '%') {
5716 newname[o] = '%';
5717 o++;
5718 }
5719 newname[o] = jm->name[i];
5720 }
5721 newname[o] = '\0';
5722
5723 if (err) {
5724 snprintf(newmsg, newmsgsz, "%s: %s: %s", newname, msg, strerror(err));
5725 } else {
5726 snprintf(newmsg, newmsgsz, "%s: %s", newname, msg);
5727 }
5728
5729 if (jm->parentmgr) {
5730 jobmgr_logv(jm->parentmgr, pri, 0, newmsg, ap);
5731 } else {
5732 struct launchd_syslog_attr attr = {
5733 .from_name = launchd_label,
5734 .about_name = launchd_label,
5735 .session_name = jm->name,
5736 .priority = pri,
5737 .from_uid = getuid(),
5738 .from_pid = getpid(),
5739 .about_pid = getpid(),
5740 };
5741
5742 launchd_vsyslog(&attr, newmsg, ap);
5743 }
5744 }
5745
5746 struct cal_dict_walk {
5747 job_t j;
5748 struct tm tmptm;
5749 };
5750
5751 void
calendarinterval_new_from_obj_dict_walk(launch_data_t obj,const char * key,void * context)5752 calendarinterval_new_from_obj_dict_walk(launch_data_t obj, const char *key, void *context)
5753 {
5754 struct cal_dict_walk *cdw = context;
5755 struct tm *tmptm = &cdw->tmptm;
5756 job_t j = cdw->j;
5757 int64_t val;
5758
5759 if (unlikely(LAUNCH_DATA_INTEGER != launch_data_get_type(obj))) {
5760 // hack to let caller know something went wrong
5761 tmptm->tm_sec = -1;
5762 return;
5763 }
5764
5765 val = launch_data_get_integer(obj);
5766
5767 if (val < 0) {
5768 job_log(j, LOG_WARNING, "The interval for key \"%s\" is less than zero.", key);
5769 } else if (strcasecmp(key, LAUNCH_JOBKEY_CAL_MINUTE) == 0) {
5770 if (val > 59) {
5771 job_log(j, LOG_WARNING, "The interval for key \"%s\" is not between 0 and 59 (inclusive).", key);
5772 tmptm->tm_sec = -1;
5773 } else {
5774 tmptm->tm_min = (typeof(tmptm->tm_min)) val;
5775 }
5776 } else if (strcasecmp(key, LAUNCH_JOBKEY_CAL_HOUR) == 0) {
5777 if (val > 23) {
5778 job_log(j, LOG_WARNING, "The interval for key \"%s\" is not between 0 and 23 (inclusive).", key);
5779 tmptm->tm_sec = -1;
5780 } else {
5781 tmptm->tm_hour = (typeof(tmptm->tm_hour)) val;
5782 }
5783 } else if (strcasecmp(key, LAUNCH_JOBKEY_CAL_DAY) == 0) {
5784 if (val < 1 || val > 31) {
5785 job_log(j, LOG_WARNING, "The interval for key \"%s\" is not between 1 and 31 (inclusive).", key);
5786 tmptm->tm_sec = -1;
5787 } else {
5788 tmptm->tm_mday = (typeof(tmptm->tm_mday)) val;
5789 }
5790 } else if (strcasecmp(key, LAUNCH_JOBKEY_CAL_WEEKDAY) == 0) {
5791 if (val > 7) {
5792 job_log(j, LOG_WARNING, "The interval for key \"%s\" is not between 0 and 7 (inclusive).", key);
5793 tmptm->tm_sec = -1;
5794 } else {
5795 tmptm->tm_wday = (typeof(tmptm->tm_wday)) val;
5796 }
5797 } else if (strcasecmp(key, LAUNCH_JOBKEY_CAL_MONTH) == 0) {
5798 if (val > 12) {
5799 job_log(j, LOG_WARNING, "The interval for key \"%s\" is not between 0 and 12 (inclusive).", key);
5800 tmptm->tm_sec = -1;
5801 } else {
5802 tmptm->tm_mon = (typeof(tmptm->tm_mon)) val;
5803 tmptm->tm_mon -= 1; // 4798263 cron compatibility
5804 }
5805 }
5806 }
5807
5808 bool
calendarinterval_new_from_obj(job_t j,launch_data_t obj)5809 calendarinterval_new_from_obj(job_t j, launch_data_t obj)
5810 {
5811 struct cal_dict_walk cdw;
5812
5813 cdw.j = j;
5814 memset(&cdw.tmptm, 0, sizeof(0));
5815
5816 cdw.tmptm.tm_min = -1;
5817 cdw.tmptm.tm_hour = -1;
5818 cdw.tmptm.tm_mday = -1;
5819 cdw.tmptm.tm_wday = -1;
5820 cdw.tmptm.tm_mon = -1;
5821
5822 if (!job_assumes(j, obj != NULL)) {
5823 return false;
5824 }
5825
5826 if (unlikely(LAUNCH_DATA_DICTIONARY != launch_data_get_type(obj))) {
5827 return false;
5828 }
5829
5830 launch_data_dict_iterate(obj, calendarinterval_new_from_obj_dict_walk, &cdw);
5831
5832 if (unlikely(cdw.tmptm.tm_sec == -1)) {
5833 return false;
5834 }
5835
5836 return calendarinterval_new(j, &cdw.tmptm);
5837 }
5838
5839 bool
calendarinterval_new(job_t j,struct tm * w)5840 calendarinterval_new(job_t j, struct tm *w)
5841 {
5842 struct calendarinterval *ci = calloc(1, sizeof(struct calendarinterval));
5843
5844 if (!job_assumes(j, ci != NULL)) {
5845 return false;
5846 }
5847
5848 ci->when = *w;
5849 ci->job = j;
5850
5851 SLIST_INSERT_HEAD(&j->cal_intervals, ci, sle);
5852
5853 calendarinterval_setalarm(j, ci);
5854
5855 runtime_add_weak_ref();
5856
5857 return true;
5858 }
5859
5860 void
calendarinterval_delete(job_t j,struct calendarinterval * ci)5861 calendarinterval_delete(job_t j, struct calendarinterval *ci)
5862 {
5863 SLIST_REMOVE(&j->cal_intervals, ci, calendarinterval, sle);
5864 LIST_REMOVE(ci, global_sle);
5865
5866 free(ci);
5867
5868 runtime_del_weak_ref();
5869 }
5870
5871 void
calendarinterval_sanity_check(void)5872 calendarinterval_sanity_check(void)
5873 {
5874 struct calendarinterval *ci = LIST_FIRST(&sorted_calendar_events);
5875 time_t now = time(NULL);
5876
5877 if (unlikely(ci && (ci->when_next < now))) {
5878 (void)jobmgr_assumes_zero_p(root_jobmgr, raise(SIGUSR1));
5879 }
5880 }
5881
5882 void
calendarinterval_callback(void)5883 calendarinterval_callback(void)
5884 {
5885 struct calendarinterval *ci, *ci_next;
5886 time_t now = time(NULL);
5887
5888 LIST_FOREACH_SAFE(ci, &sorted_calendar_events, global_sle, ci_next) {
5889 job_t j = ci->job;
5890
5891 if (ci->when_next > now) {
5892 break;
5893 }
5894
5895 LIST_REMOVE(ci, global_sle);
5896 calendarinterval_setalarm(j, ci);
5897
5898 j->start_pending = true;
5899 job_dispatch(j, false);
5900 }
5901 }
5902
5903 bool
socketgroup_new(job_t j,const char * name,int * fds,size_t fd_cnt)5904 socketgroup_new(job_t j, const char *name, int *fds, size_t fd_cnt)
5905 {
5906 struct socketgroup *sg = calloc(1, sizeof(struct socketgroup) + strlen(name) + 1);
5907
5908 if (!job_assumes(j, sg != NULL)) {
5909 return false;
5910 }
5911
5912 sg->fds = calloc(1, fd_cnt * sizeof(int));
5913 sg->fd_cnt = fd_cnt;
5914
5915 if (!job_assumes(j, sg->fds != NULL)) {
5916 free(sg);
5917 return false;
5918 }
5919
5920 memcpy(sg->fds, fds, fd_cnt * sizeof(int));
5921 strcpy(sg->name_init, name);
5922
5923 SLIST_INSERT_HEAD(&j->sockets, sg, sle);
5924
5925 runtime_add_weak_ref();
5926
5927 return true;
5928 }
5929
5930 void
socketgroup_delete(job_t j,struct socketgroup * sg)5931 socketgroup_delete(job_t j, struct socketgroup *sg)
5932 {
5933 unsigned int i;
5934
5935 for (i = 0; i < sg->fd_cnt; i++) {
5936 #if 0
5937 struct sockaddr_storage ss;
5938 struct sockaddr_un *sun = (struct sockaddr_un *)&ss;
5939 socklen_t ss_len = sizeof(ss);
5940
5941 // 5480306
5942 if (job_assumes_zero(j, getsockname(sg->fds[i], (struct sockaddr *)&ss, &ss_len) != -1)
5943 && job_assumes(j, ss_len > 0) && (ss.ss_family == AF_UNIX)) {
5944 (void)job_assumes(j, unlink(sun->sun_path) != -1);
5945 // We might conditionally need to delete a directory here
5946 }
5947 #endif
5948 (void)job_assumes_zero_p(j, runtime_close(sg->fds[i]));
5949 }
5950
5951 SLIST_REMOVE(&j->sockets, sg, socketgroup, sle);
5952
5953 free(sg->fds);
5954 free(sg);
5955
5956 runtime_del_weak_ref();
5957 }
5958
5959 void
socketgroup_kevent_mod(job_t j,struct socketgroup * sg,bool do_add)5960 socketgroup_kevent_mod(job_t j, struct socketgroup *sg, bool do_add)
5961 {
5962 struct kevent kev[sg->fd_cnt];
5963 char buf[10000];
5964 unsigned int i, buf_off = 0;
5965
5966 for (i = 0; i < sg->fd_cnt; i++) {
5967 EV_SET(&kev[i], sg->fds[i], EVFILT_READ, do_add ? EV_ADD : EV_DELETE, 0, 0, j);
5968 buf_off += snprintf(buf + buf_off, sizeof(buf) - buf_off, " %d", sg->fds[i]);
5969 }
5970
5971 job_log(j, LOG_DEBUG, "%s Sockets:%s", do_add ? "Watching" : "Ignoring", buf);
5972
5973 (void)job_assumes_zero_p(j, kevent_bulk_mod(kev, sg->fd_cnt));
5974
5975 for (i = 0; i < sg->fd_cnt; i++) {
5976 (void)job_assumes(j, kev[i].flags & EV_ERROR);
5977 errno = (typeof(errno)) kev[i].data;
5978 (void)job_assumes_zero(j, kev[i].data);
5979 }
5980 }
5981
5982 void
socketgroup_ignore(job_t j,struct socketgroup * sg)5983 socketgroup_ignore(job_t j, struct socketgroup *sg)
5984 {
5985 socketgroup_kevent_mod(j, sg, false);
5986 }
5987
5988 void
socketgroup_watch(job_t j,struct socketgroup * sg)5989 socketgroup_watch(job_t j, struct socketgroup *sg)
5990 {
5991 socketgroup_kevent_mod(j, sg, true);
5992 }
5993
5994 void
socketgroup_callback(job_t j)5995 socketgroup_callback(job_t j)
5996 {
5997 job_dispatch(j, true);
5998 }
5999
6000 bool
envitem_new(job_t j,const char * k,const char * v,bool global)6001 envitem_new(job_t j, const char *k, const char *v, bool global)
6002 {
6003 if (global && !launchd_allow_global_dyld_envvars) {
6004 if (strncmp("DYLD_", k, sizeof("DYLD_") - 1) == 0) {
6005 job_log(j, LOG_ERR, "Ignoring global environment variable submitted by job (variable=value): %s=%s", k, v);
6006 return false;
6007 }
6008 }
6009
6010 struct envitem *ei = calloc(1, sizeof(struct envitem) + strlen(k) + 1 + strlen(v) + 1);
6011
6012 if (!job_assumes(j, ei != NULL)) {
6013 return false;
6014 }
6015
6016 strcpy(ei->key_init, k);
6017 ei->value = ei->key_init + strlen(k) + 1;
6018 strcpy(ei->value, v);
6019
6020 if (global) {
6021 if (SLIST_EMPTY(&j->global_env)) {
6022 LIST_INSERT_HEAD(&j->mgr->global_env_jobs, j, global_env_sle);
6023 }
6024 SLIST_INSERT_HEAD(&j->global_env, ei, sle);
6025 } else {
6026 SLIST_INSERT_HEAD(&j->env, ei, sle);
6027 }
6028
6029 job_log(j, LOG_DEBUG, "Added environmental variable: %s=%s", k, v);
6030
6031 return true;
6032 }
6033
6034 void
envitem_delete(job_t j,struct envitem * ei,bool global)6035 envitem_delete(job_t j, struct envitem *ei, bool global)
6036 {
6037 if (global) {
6038 SLIST_REMOVE(&j->global_env, ei, envitem, sle);
6039 if (SLIST_EMPTY(&j->global_env)) {
6040 LIST_REMOVE(j, global_env_sle);
6041 }
6042 } else {
6043 SLIST_REMOVE(&j->env, ei, envitem, sle);
6044 }
6045
6046 free(ei);
6047 }
6048
6049 void
envitem_setup(launch_data_t obj,const char * key,void * context)6050 envitem_setup(launch_data_t obj, const char *key, void *context)
6051 {
6052 job_t j = context;
6053
6054 if (launch_data_get_type(obj) != LAUNCH_DATA_STRING) {
6055 return;
6056 }
6057
6058 if (strncmp(LAUNCHD_TRUSTED_FD_ENV, key, sizeof(LAUNCHD_TRUSTED_FD_ENV) - 1) != 0) {
6059 envitem_new(j, key, launch_data_get_string(obj), j->importing_global_env);
6060 } else {
6061 job_log(j, LOG_DEBUG, "Ignoring reserved environmental variable: %s", key);
6062 }
6063 }
6064
6065 bool
limititem_update(job_t j,int w,rlim_t r)6066 limititem_update(job_t j, int w, rlim_t r)
6067 {
6068 struct limititem *li;
6069
6070 SLIST_FOREACH(li, &j->limits, sle) {
6071 if (li->which == w) {
6072 break;
6073 }
6074 }
6075
6076 if (li == NULL) {
6077 li = calloc(1, sizeof(struct limititem));
6078
6079 if (!job_assumes(j, li != NULL)) {
6080 return false;
6081 }
6082
6083 SLIST_INSERT_HEAD(&j->limits, li, sle);
6084
6085 li->which = w;
6086 }
6087
6088 if (j->importing_hard_limits) {
6089 li->lim.rlim_max = r;
6090 li->sethard = true;
6091 } else {
6092 li->lim.rlim_cur = r;
6093 li->setsoft = true;
6094 }
6095
6096 return true;
6097 }
6098
6099 void
limititem_delete(job_t j,struct limititem * li)6100 limititem_delete(job_t j, struct limititem *li)
6101 {
6102 SLIST_REMOVE(&j->limits, li, limititem, sle);
6103
6104 free(li);
6105 }
6106
6107 #if HAVE_SANDBOX
6108 void
seatbelt_setup_flags(launch_data_t obj,const char * key,void * context)6109 seatbelt_setup_flags(launch_data_t obj, const char *key, void *context)
6110 {
6111 job_t j = context;
6112
6113 if (launch_data_get_type(obj) != LAUNCH_DATA_BOOL) {
6114 job_log(j, LOG_WARNING, "Sandbox flag value must be boolean: %s", key);
6115 return;
6116 }
6117
6118 if (launch_data_get_bool(obj) == false) {
6119 return;
6120 }
6121
6122 if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOX_NAMED) == 0) {
6123 j->seatbelt_flags |= SANDBOX_NAMED;
6124 }
6125 }
6126 #endif
6127
6128 void
limititem_setup(launch_data_t obj,const char * key,void * context)6129 limititem_setup(launch_data_t obj, const char *key, void *context)
6130 {
6131 job_t j = context;
6132 size_t i, limits_cnt = (sizeof(launchd_keys2limits) / sizeof(launchd_keys2limits[0]));
6133 rlim_t rl;
6134
6135 if (launch_data_get_type(obj) != LAUNCH_DATA_INTEGER) {
6136 return;
6137 }
6138
6139 rl = launch_data_get_integer(obj);
6140
6141 for (i = 0; i < limits_cnt; i++) {
6142 if (strcasecmp(launchd_keys2limits[i].key, key) == 0) {
6143 break;
6144 }
6145 }
6146
6147 if (i == limits_cnt) {
6148 return;
6149 }
6150
6151 limititem_update(j, launchd_keys2limits[i].val, rl);
6152 }
6153
6154 bool
job_useless(job_t j)6155 job_useless(job_t j)
6156 {
6157 if ((j->legacy_LS_job || j->only_once) && j->start_time != 0) {
6158 if (j->legacy_LS_job && j->j_port) {
6159 return false;
6160 }
6161 job_log(j, LOG_INFO, "Exited. Was only configured to run once.");
6162 return true;
6163 } else if (j->removal_pending) {
6164 job_log(j, LOG_DEBUG, "Exited while removal was pending.");
6165 return true;
6166 } else if (j->shutdown_monitor) {
6167 return false;
6168 } else if (j->mgr->shutting_down && !j->mgr->parentmgr) {
6169 job_log(j, LOG_DEBUG, "Exited while shutdown in progress. Processes remaining: %lu/%lu", total_children, total_anon_children);
6170 if (total_children == 0 && !j->anonymous) {
6171 job_log(j, LOG_DEBUG | LOG_CONSOLE, "Job was last to exit during shutdown of: %s.", j->mgr->name);
6172 }
6173 return true;
6174 } else if (j->legacy_mach_job) {
6175 if (SLIST_EMPTY(&j->machservices)) {
6176 job_log(j, LOG_INFO, "Garbage collecting");
6177 return true;
6178 } else if (!j->checkedin) {
6179 job_log(j, LOG_WARNING, "Failed to check-in!");
6180 return true;
6181 }
6182 } else {
6183 /* If the job's executable does not have any valid architectures (for
6184 * example, if it's a PowerPC-only job), then we don't even bother
6185 * trying to relaunch it, as we have no reasonable expectation that
6186 * the situation will change.
6187 *
6188 * <rdar://problem/9106979>
6189 */
6190 if (!j->did_exec && WEXITSTATUS(j->last_exit_status) == EBADARCH) {
6191 job_log(j, LOG_ERR, "Job executable does not contain supported architectures. Unloading it. Its plist should be removed.");
6192 return true;
6193 }
6194 }
6195
6196 return false;
6197 }
6198
6199 bool
job_keepalive(job_t j)6200 job_keepalive(job_t j)
6201 {
6202 mach_msg_type_number_t statusCnt;
6203 mach_port_status_t status;
6204 struct semaphoreitem *si;
6205 struct machservice *ms;
6206 bool good_exit = (WIFEXITED(j->last_exit_status) && WEXITSTATUS(j->last_exit_status) == 0);
6207 bool is_not_kextd = (launchd_apple_internal || (strcmp(j->label, "com.apple.kextd") != 0));
6208
6209 if (unlikely(j->mgr->shutting_down)) {
6210 return false;
6211 }
6212
6213 /*
6214 * 5066316
6215 *
6216 * We definitely need to revisit this after Leopard ships. Please see
6217 * launchctl.c for the other half of this hack.
6218 */
6219 if (unlikely((j->mgr->global_on_demand_cnt > 0) && is_not_kextd)) {
6220 return false;
6221 }
6222
6223 if (unlikely(j->needs_kickoff)) {
6224 job_log(j, LOG_DEBUG, "KeepAlive check: Job needs to be kicked off on-demand before KeepAlive sets in.");
6225 return false;
6226 }
6227
6228 if (j->start_pending) {
6229 job_log(j, LOG_DEBUG, "KeepAlive check: Pent-up non-IPC launch criteria.");
6230 return true;
6231 }
6232
6233 if (!j->ondemand) {
6234 job_log(j, LOG_DEBUG, "KeepAlive check: job configured to run continuously.");
6235 return true;
6236 }
6237
6238 SLIST_FOREACH(ms, &j->machservices, sle) {
6239 statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT;
6240 if (mach_port_get_attributes(mach_task_self(), ms->port, MACH_PORT_RECEIVE_STATUS,
6241 (mach_port_info_t)&status, &statusCnt) != KERN_SUCCESS) {
6242 continue;
6243 }
6244 if (status.mps_msgcount) {
6245 job_log(j, LOG_DEBUG, "KeepAlive check: %d queued Mach messages on service: %s",
6246 status.mps_msgcount, ms->name);
6247 return true;
6248 }
6249 }
6250
6251 /* TODO: Coalesce external events and semaphore items, since they're basically
6252 * the same thing.
6253 */
6254 struct externalevent *ei = NULL;
6255 LIST_FOREACH(ei, &j->events, job_le) {
6256 if (ei->state == ei->wanted_state) {
6257 return true;
6258 }
6259 }
6260
6261 SLIST_FOREACH(si, &j->semaphores, sle) {
6262 bool wanted_state = false;
6263 job_t other_j;
6264
6265 switch (si->why) {
6266 case NETWORK_UP:
6267 wanted_state = true;
6268 case NETWORK_DOWN:
6269 if (network_up == wanted_state) {
6270 job_log(j, LOG_DEBUG, "KeepAlive: The network is %s.", wanted_state ? "up" : "down");
6271 return true;
6272 }
6273 break;
6274 case SUCCESSFUL_EXIT:
6275 wanted_state = true;
6276 case FAILED_EXIT:
6277 if (good_exit == wanted_state) {
6278 job_log(j, LOG_DEBUG, "KeepAlive: The exit state was %s.", wanted_state ? "successful" : "failure");
6279 return true;
6280 }
6281 break;
6282 case CRASHED:
6283 wanted_state = true;
6284 case DID_NOT_CRASH:
6285 if (j->crashed == wanted_state) {
6286 return true;
6287 }
6288 break;
6289 case OTHER_JOB_ENABLED:
6290 wanted_state = true;
6291 case OTHER_JOB_DISABLED:
6292 if ((bool)job_find(NULL, si->what) == wanted_state) {
6293 job_log(j, LOG_DEBUG, "KeepAlive: The following job is %s: %s", wanted_state ? "enabled" : "disabled", si->what);
6294 return true;
6295 }
6296 break;
6297 case OTHER_JOB_ACTIVE:
6298 wanted_state = true;
6299 case OTHER_JOB_INACTIVE:
6300 if ((other_j = job_find(NULL, si->what))) {
6301 if ((bool)other_j->p == wanted_state) {
6302 job_log(j, LOG_DEBUG, "KeepAlive: The following job is %s: %s", wanted_state ? "active" : "inactive", si->what);
6303 return true;
6304 }
6305 }
6306 break;
6307 }
6308 }
6309
6310 return false;
6311 }
6312
6313 const char *
job_active(job_t j)6314 job_active(job_t j)
6315 {
6316 if (j->p && j->shutdown_monitor) {
6317 return "Monitoring shutdown";
6318 }
6319 if (j->p) {
6320 return "PID is still valid";
6321 }
6322
6323 if (j->priv_port_has_senders) {
6324 return "Privileged Port still has outstanding senders";
6325 }
6326
6327 struct machservice *ms;
6328 SLIST_FOREACH(ms, &j->machservices, sle) {
6329 /* If we've simulated an exit, we mark the job as non-active, even
6330 * though doing so will leave it in an unsafe state. We do this so that
6331 * shutdown can proceed. See <rdar://problem/11126530>.
6332 */
6333 if (!j->workaround9359725 && ms->recv && machservice_active(ms)) {
6334 job_log(j, LOG_INFO, "Mach service is still active despite being killed: %s", ms->name);
6335 /*
6336 * It is not at all clear to me why this returns a string. The process
6337 * has been killed, and the loop at this point doesn't do anything other
6338 * than wait. Which is probably not going to do anything useful.
6339 */
6340 return NULL;
6341 // return "Mach service is still active despite being killed";
6342 }
6343 }
6344
6345 return NULL;
6346 }
6347
6348 void
machservice_watch(job_t j,struct machservice * ms)6349 machservice_watch(job_t j, struct machservice *ms)
6350 {
6351 if (ms->recv) {
6352 if (job_assumes_zero(j, runtime_add_mport(ms->port, NULL)) == KERN_INVALID_RIGHT) {
6353 ms->recv_race_hack = true;
6354 }
6355 }
6356 }
6357
6358 void
machservice_ignore(job_t j,struct machservice * ms)6359 machservice_ignore(job_t j, struct machservice *ms)
6360 {
6361 /* We only add ports whose receive rights we control into the port set, so
6362 * don't attempt to remove te service from the port set if we didn't put it
6363 * there in the first place. Otherwise, we could wind up trying to access a
6364 * bogus index (like MACH_PORT_DEAD) or zeroing a valid one out.
6365 *
6366 * <rdar://problem/10898014>
6367 */
6368 if (ms->recv) {
6369 (void)job_assumes_zero(j, runtime_remove_mport(ms->port));
6370 }
6371 }
6372
6373 void
machservice_resetport(job_t j,struct machservice * ms)6374 machservice_resetport(job_t j, struct machservice *ms)
6375 {
6376 LIST_REMOVE(ms, port_hash_sle);
6377 (void)job_assumes_zero(j, launchd_mport_close_recv(ms->port));
6378 (void)job_assumes_zero(j, launchd_mport_deallocate(ms->port));
6379
6380 ms->gen_num++;
6381 (void)job_assumes_zero(j, launchd_mport_create_recv(&ms->port));
6382 (void)job_assumes_zero(j, launchd_mport_make_send(ms->port));
6383 LIST_INSERT_HEAD(&port_hash[HASH_PORT(ms->port)], ms, port_hash_sle);
6384 }
6385
6386 void
machservice_stamp_port(job_t j,struct machservice * ms)6387 machservice_stamp_port(job_t j, struct machservice *ms)
6388 {
6389 mach_port_context_t ctx = 0;
6390 char *where2get = j->prog ? j->prog : j->argv[0];
6391
6392 char *prog = NULL;
6393 if ((prog = strrchr(where2get, '/'))) {
6394 prog++;
6395 } else {
6396 prog = where2get;
6397 }
6398
6399 (void)strncpy((char *)&ctx, prog, sizeof(ctx));
6400 #if __LITTLE_ENDIAN__
6401 #if __LP64__
6402 ctx = be64toh(ctx);
6403 #else
6404 ctx = be32toh(ctx);
6405 #endif
6406 #endif
6407
6408 (void)job_assumes_zero(j, mach_port_set_context(mach_task_self(), ms->port, ctx));
6409 }
6410
6411 struct machservice *
machservice_new(job_t j,const char * name,mach_port_t * serviceport,bool pid_local)6412 machservice_new(job_t j, const char *name, mach_port_t *serviceport, bool pid_local)
6413 {
6414 /* Don't create new MachServices for dead ports. This is primarily for
6415 * clients who use bootstrap_register2(). They can pass in a send right, but
6416 * then that port can immediately go dead. Hilarity ensues.
6417 *
6418 * <rdar://problem/10898014>
6419 */
6420 if (*serviceport == MACH_PORT_DEAD) {
6421 return NULL;
6422 }
6423
6424 struct machservice *ms = calloc(1, sizeof(struct machservice) + strlen(name) + 1);
6425 if (!job_assumes(j, ms != NULL)) {
6426 return NULL;
6427 }
6428
6429 strcpy((char *)ms->name, name);
6430 ms->job = j;
6431 ms->gen_num = 1;
6432 ms->per_pid = pid_local;
6433
6434 if (likely(*serviceport == MACH_PORT_NULL)) {
6435 if (job_assumes_zero(j, launchd_mport_create_recv(&ms->port)) != KERN_SUCCESS) {
6436 goto out_bad;
6437 }
6438
6439 if (job_assumes_zero(j, launchd_mport_make_send(ms->port)) != KERN_SUCCESS) {
6440 goto out_bad2;
6441 }
6442 *serviceport = ms->port;
6443 ms->recv = true;
6444 } else {
6445 ms->port = *serviceport;
6446 ms->isActive = true;
6447 }
6448
6449 SLIST_INSERT_HEAD(&j->machservices, ms, sle);
6450
6451 jobmgr_t where2put = j->mgr;
6452 // XPC domains are separate from Mach bootstraps.
6453 if (!(j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) {
6454 if (launchd_flat_mach_namespace && !(j->mgr->properties & BOOTSTRAP_PROPERTY_EXPLICITSUBSET)) {
6455 where2put = root_jobmgr;
6456 }
6457 }
6458
6459 /* Don't allow MachServices added by multiple-instance jobs to be looked up
6460 * by others. We could just do this with a simple bit, but then we'd have to
6461 * uniquify the names ourselves to avoid collisions. This is just easier.
6462 */
6463 if (!j->dedicated_instance) {
6464 LIST_INSERT_HEAD(&where2put->ms_hash[hash_ms(ms->name)], ms, name_hash_sle);
6465 }
6466 LIST_INSERT_HEAD(&port_hash[HASH_PORT(ms->port)], ms, port_hash_sle);
6467
6468 if (ms->recv) {
6469 machservice_stamp_port(j, ms);
6470 }
6471
6472 job_log(j, LOG_DEBUG, "Mach service added%s: %s", (j->mgr->properties & BOOTSTRAP_PROPERTY_EXPLICITSUBSET) ? " to private namespace" : "", name);
6473
6474 return ms;
6475 out_bad2:
6476 (void)job_assumes_zero(j, launchd_mport_close_recv(ms->port));
6477 out_bad:
6478 free(ms);
6479 return NULL;
6480 }
6481
6482 struct machservice *
machservice_new_alias(job_t j,struct machservice * orig)6483 machservice_new_alias(job_t j, struct machservice *orig)
6484 {
6485 struct machservice *ms = calloc(1, sizeof(struct machservice) + strlen(orig->name) + 1);
6486 if (job_assumes(j, ms != NULL)) {
6487 strcpy((char *)ms->name, orig->name);
6488 ms->alias = orig;
6489 ms->job = j;
6490
6491 LIST_INSERT_HEAD(&j->mgr->ms_hash[hash_ms(ms->name)], ms, name_hash_sle);
6492 SLIST_INSERT_HEAD(&j->machservices, ms, sle);
6493 jobmgr_log(j->mgr, LOG_DEBUG, "Service aliased into job manager: %s", orig->name);
6494 }
6495
6496 return ms;
6497 }
6498
6499 bootstrap_status_t
machservice_status(struct machservice * ms)6500 machservice_status(struct machservice *ms)
6501 {
6502 ms = ms->alias ? ms->alias : ms;
6503 if (ms->isActive) {
6504 return BOOTSTRAP_STATUS_ACTIVE;
6505 } else if (ms->job->ondemand) {
6506 return BOOTSTRAP_STATUS_ON_DEMAND;
6507 } else {
6508 return BOOTSTRAP_STATUS_INACTIVE;
6509 }
6510 }
6511
6512 #include <sys/mach/thread_status.h>
6513 void
job_setup_exception_port(job_t j,task_t target_task)6514 job_setup_exception_port(job_t j, task_t target_task)
6515 {
6516 struct machservice *ms;
6517 thread_state_flavor_t f = 0;
6518 mach_port_t exc_port = the_exception_server;
6519
6520 if (unlikely(j->alt_exc_handler)) {
6521 ms = jobmgr_lookup_service(j->mgr, j->alt_exc_handler, true, 0);
6522 if (likely(ms)) {
6523 exc_port = machservice_port(ms);
6524 } else {
6525 job_log(j, LOG_WARNING, "Falling back to default Mach exception handler. Could not find: %s", j->alt_exc_handler);
6526 }
6527 } else if (unlikely(j->internal_exc_handler)) {
6528 exc_port = runtime_get_kernel_port();
6529 } else if (unlikely(!exc_port)) {
6530 return;
6531 }
6532
6533 #if defined (__ppc__) || defined(__ppc64__)
6534 f = PPC_THREAD_STATE64;
6535 #elif defined(__i386__) || defined(__x86_64__)
6536 f = x86_THREAD_STATE;
6537 #elif defined(__arm__)
6538 f = ARM_THREAD_STATE;
6539 #else
6540 #error "unknown architecture"
6541 #endif
6542
6543 if (likely(target_task)) {
6544 kern_return_t kr = task_set_exception_ports(target_task, EXC_MASK_CRASH | EXC_MASK_GUARD | EXC_MASK_RESOURCE, exc_port, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f);
6545 if (kr) {
6546 if (kr != MACH_SEND_INVALID_DEST) {
6547 (void)job_assumes_zero(j, kr);
6548 } else {
6549 job_log(j, LOG_WARNING, "Task died before exception port could be set.");
6550 }
6551 }
6552 } else if (pid1_magic && the_exception_server) {
6553 mach_port_t mhp = mach_host_self();
6554 (void)job_assumes_zero(j, host_set_exception_ports(mhp, EXC_MASK_CRASH | EXC_MASK_GUARD | EXC_MASK_RESOURCE, the_exception_server, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f));
6555 (void)job_assumes_zero(j, launchd_mport_deallocate(mhp));
6556 }
6557 }
6558
6559 void
job_set_exception_port(job_t j,mach_port_t port)6560 job_set_exception_port(job_t j, mach_port_t port)
6561 {
6562 if (unlikely(!the_exception_server)) {
6563 the_exception_server = port;
6564 job_setup_exception_port(j, 0);
6565 } else {
6566 job_log(j, LOG_WARNING, "The exception server is already claimed!");
6567 }
6568 }
6569
6570 void
machservice_setup_options(launch_data_t obj,const char * key,void * context)6571 machservice_setup_options(launch_data_t obj, const char *key, void *context)
6572 {
6573 struct machservice *ms = context;
6574 mach_port_t mhp = mach_host_self();
6575 int which_port;
6576 bool b;
6577
6578 if (!job_assumes(ms->job, mhp != MACH_PORT_NULL)) {
6579 return;
6580 }
6581
6582 switch (launch_data_get_type(obj)) {
6583 case LAUNCH_DATA_INTEGER:
6584 which_port = (int)launch_data_get_integer(obj); // XXX we should bound check this...
6585 if (strcasecmp(key, LAUNCH_JOBKEY_MACH_TASKSPECIALPORT) == 0) {
6586 switch (which_port) {
6587 case TASK_KERNEL_PORT:
6588 case TASK_HOST_PORT:
6589 case TASK_NAME_PORT:
6590 case TASK_BOOTSTRAP_PORT:
6591 /* I find it a little odd that zero isn't reserved in the header.
6592 * Normally Mach is fairly good about this convention...
6593 */
6594 case 0:
6595 job_log(ms->job, LOG_WARNING, "Tried to set a reserved task special port: %d", which_port);
6596 break;
6597 default:
6598 ms->special_port_num = which_port;
6599 SLIST_INSERT_HEAD(&special_ports, ms, special_port_sle);
6600 break;
6601 }
6602 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_HOSTSPECIALPORT) == 0 && pid1_magic) {
6603 if (which_port > HOST_MAX_SPECIAL_KERNEL_PORT) {
6604 (void)job_assumes_zero(ms->job, (errno = host_set_special_port(mhp, which_port, ms->port)));
6605 } else {
6606 job_log(ms->job, LOG_WARNING, "Tried to set a reserved host special port: %d", which_port);
6607 }
6608 }
6609 case LAUNCH_DATA_BOOL:
6610 b = launch_data_get_bool(obj);
6611 if (strcasecmp(key, LAUNCH_JOBKEY_MACH_ENTERKERNELDEBUGGERONCLOSE) == 0) {
6612 ms->debug_on_close = b;
6613 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_RESETATCLOSE) == 0) {
6614 ms->reset = b;
6615 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN) == 0) {
6616 ms->hide = b;
6617 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_EXCEPTIONSERVER) == 0) {
6618 job_set_exception_port(ms->job, ms->port);
6619 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_KUNCSERVER) == 0) {
6620 ms->kUNCServer = b;
6621 (void)job_assumes_zero(ms->job, host_set_UNDServer(mhp, ms->port));
6622 }
6623 break;
6624 case LAUNCH_DATA_STRING:
6625 if (strcasecmp(key, LAUNCH_JOBKEY_MACH_DRAINMESSAGESONCRASH) == 0) {
6626 const char *option = launch_data_get_string(obj);
6627 if (strcasecmp(option, "One") == 0) {
6628 ms->drain_one_on_crash = true;
6629 } else if (strcasecmp(option, "All") == 0) {
6630 ms->drain_all_on_crash = true;
6631 }
6632 }
6633 break;
6634 case LAUNCH_DATA_DICTIONARY:
6635 if (launch_data_dict_get_count(obj) == 0) {
6636 job_set_exception_port(ms->job, ms->port);
6637 }
6638 break;
6639 default:
6640 break;
6641 }
6642
6643 (void)job_assumes_zero(ms->job, launchd_mport_deallocate(mhp));
6644 }
6645
6646 void
machservice_setup(launch_data_t obj,const char * key,void * context)6647 machservice_setup(launch_data_t obj, const char *key, void *context)
6648 {
6649 job_t j = context;
6650 struct machservice *ms;
6651 mach_port_t p = MACH_PORT_NULL;
6652
6653 if (unlikely(ms = jobmgr_lookup_service(j->mgr, key, false, 0))) {
6654 job_log(j, LOG_WARNING, "Conflict with job: %s over Mach service: %s", ms->job->label, key);
6655 return;
6656 }
6657
6658 if (!job_assumes(j, (ms = machservice_new(j, key, &p, false)) != NULL)) {
6659 return;
6660 }
6661
6662 ms->isActive = false;
6663 ms->upfront = true;
6664
6665 if (launch_data_get_type(obj) == LAUNCH_DATA_DICTIONARY) {
6666 launch_data_dict_iterate(obj, machservice_setup_options, ms);
6667 }
6668
6669 kern_return_t kr = mach_port_set_attributes(mach_task_self(), ms->port, MACH_PORT_TEMPOWNER, NULL, 0);
6670 (void)job_assumes_zero(j, kr);
6671 }
6672
6673 jobmgr_t
jobmgr_do_garbage_collection(jobmgr_t jm)6674 jobmgr_do_garbage_collection(jobmgr_t jm)
6675 {
6676 jobmgr_t jmi = NULL, jmn = NULL;
6677 SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
6678 jobmgr_do_garbage_collection(jmi);
6679 }
6680
6681 if (!jm->shutting_down) {
6682 return jm;
6683 }
6684
6685 if (SLIST_EMPTY(&jm->submgrs)) {
6686 jobmgr_log(jm, LOG_DEBUG, "No submanagers left.");
6687 } else {
6688 jobmgr_log(jm, LOG_DEBUG, "Still have submanagers.");
6689 SLIST_FOREACH(jmi, &jm->submgrs, sle) {
6690 jobmgr_log(jm, LOG_DEBUG, "Submanager: %s", jmi->name);
6691 }
6692 }
6693
6694 size_t actives = 0;
6695 job_t ji = NULL, jn = NULL;
6696 LIST_FOREACH_SAFE(ji, &jm->jobs, sle, jn) {
6697 if (ji->anonymous) {
6698 continue;
6699 }
6700
6701 // Let the shutdown monitor be up until the very end.
6702 if (ji->shutdown_monitor) {
6703 continue;
6704 }
6705
6706 /* On our first pass through, open a transaction for all the jobs that
6707 * need to be dirty at shutdown. We'll close these transactions once the
6708 * jobs that do not need to be dirty at shutdown have all exited.
6709 */
6710 if (ji->dirty_at_shutdown && !jm->shutdown_jobs_dirtied) {
6711 job_open_shutdown_transaction(ji);
6712 }
6713
6714 const char *active = job_active(ji);
6715 if (!active) {
6716 job_remove(ji);
6717 } else {
6718 job_log(ji, LOG_DEBUG, "Job is active: %s", active);
6719 job_stop(ji);
6720
6721 if (!ji->dirty_at_shutdown) {
6722 actives++;
6723 }
6724
6725 if (ji->clean_kill) {
6726 job_log(ji, LOG_DEBUG, "Job was killed cleanly.");
6727 } else {
6728 job_log(ji, LOG_DEBUG, "Job was sent SIGTERM%s.", ji->sent_sigkill ? " and SIGKILL" : "");
6729 }
6730 }
6731 }
6732
6733 jm->shutdown_jobs_dirtied = true;
6734 if (actives == 0) {
6735 if (!jm->shutdown_jobs_cleaned) {
6736 /* Once all normal jobs have exited, we clean the dirty-at-shutdown
6737 * jobs and make them into normal jobs so that the above loop will
6738 * handle them appropriately.
6739 */
6740 LIST_FOREACH(ji, &jm->jobs, sle) {
6741 if (ji->anonymous) {
6742 continue;
6743 }
6744
6745 if (!job_active(ji)) {
6746 continue;
6747 }
6748
6749 if (ji->shutdown_monitor) {
6750 continue;
6751 }
6752
6753 job_close_shutdown_transaction(ji);
6754 actives++;
6755 }
6756
6757 jm->shutdown_jobs_cleaned = true;
6758 }
6759
6760 if (SLIST_EMPTY(&jm->submgrs) && actives == 0) {
6761 /* We may be in a situation where the shutdown monitor is all that's
6762 * left, in which case we want to stop it. Like dirty-at-shutdown
6763 * jobs, we turn it back into a normal job so that the main loop
6764 * treats it appropriately.
6765 *
6766 * See:
6767 * <rdar://problem/10756306>
6768 * <rdar://problem/11034971>
6769 * <rdar://problem/11549541>
6770 */
6771 if (jm->monitor_shutdown && _launchd_shutdown_monitor) {
6772 /* The rest of shutdown has completed, so we can kill the shutdown
6773 * monitor now like it was any other job.
6774 */
6775 _launchd_shutdown_monitor->shutdown_monitor = false;
6776
6777 job_log(_launchd_shutdown_monitor, LOG_NOTICE | LOG_CONSOLE, "Stopping shutdown monitor.");
6778 job_stop(_launchd_shutdown_monitor);
6779 _launchd_shutdown_monitor = NULL;
6780 } else {
6781 jobmgr_log(jm, LOG_DEBUG, "Removing.");
6782 jobmgr_remove(jm);
6783 return NULL;
6784 }
6785 }
6786 }
6787
6788 return jm;
6789 }
6790
6791 void
jobmgr_kill_stray_children(jobmgr_t jm,pid_t * p,size_t np)6792 jobmgr_kill_stray_children(jobmgr_t jm, pid_t *p, size_t np)
6793 {
6794 /* I maintain that stray processes should be at the mercy of launchd during
6795 * shutdown, but nevertheless, things like diskimages-helper can stick
6796 * around, and SIGKILLing them can result in data loss. So we send SIGTERM
6797 * to all the strays and don't wait for them to exit before moving on.
6798 *
6799 * See rdar://problem/6562592
6800 */
6801 size_t i = 0;
6802 for (i = 0; i < np; i++) {
6803 if (p[i] != 0) {
6804 jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "Sending SIGTERM to PID %u and continuing...", p[i]);
6805 (void)jobmgr_assumes_zero_p(jm, kill2(p[i], SIGTERM));
6806 }
6807 }
6808 }
6809
6810 void
jobmgr_log_stray_children(jobmgr_t jm,bool kill_strays)6811 jobmgr_log_stray_children(jobmgr_t jm, bool kill_strays)
6812 {
6813 size_t kp_skipped = 0, len = sizeof(pid_t) * get_kern_max_proc();
6814 pid_t *pids = NULL;
6815 int i = 0, kp_cnt = 0;
6816
6817 if (likely(jm->parentmgr || !pid1_magic)) {
6818 return;
6819 }
6820
6821 if (!jobmgr_assumes(jm, (pids = malloc(len)) != NULL)) {
6822 return;
6823 }
6824
6825 runtime_ktrace0(RTKT_LAUNCHD_FINDING_ALL_STRAYS);
6826
6827 if (jobmgr_assumes_zero_p(jm, (kp_cnt = proc_listallpids(pids, len))) == -1) {
6828 goto out;
6829 }
6830
6831 pid_t *ps = (pid_t *)calloc(sizeof(pid_t), kp_cnt);
6832 for (i = 0; i < kp_cnt; i++) {
6833 struct proc_bsdshortinfo proc;
6834 if (proc_pidinfo(pids[i], PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) {
6835 if (errno != ESRCH) {
6836 (void)jobmgr_assumes_zero(jm, errno);
6837 }
6838
6839 kp_skipped++;
6840 continue;
6841 }
6842
6843 pid_t p_i = pids[i];
6844 pid_t pp_i = proc.pbsi_ppid;
6845 pid_t pg_i = proc.pbsi_pgid;
6846 const char *z = (proc.pbsi_status == SZOMB) ? "zombie " : "";
6847 const char *n = proc.pbsi_comm;
6848
6849 if (unlikely(p_i == 0 || p_i == 1)) {
6850 kp_skipped++;
6851 continue;
6852 }
6853
6854 if (_launchd_shutdown_monitor && pp_i == _launchd_shutdown_monitor->p) {
6855 kp_skipped++;
6856 continue;
6857 }
6858
6859 // We might have some jobs hanging around that we've decided to shut down in spite of.
6860 job_t j = jobmgr_find_by_pid(jm, p_i, false);
6861 if (!j || (j && j->anonymous)) {
6862 jobmgr_log(jm, LOG_INFO | LOG_CONSOLE, "Stray %s%s at shutdown: PID %u PPID %u PGID %u %s", z, j ? "anonymous job" : "process", p_i, pp_i, pg_i, n);
6863
6864 int status = 0;
6865 if (pp_i == getpid() && !jobmgr_assumes(jm, proc.pbsi_status != SZOMB)) {
6866 if (jobmgr_assumes_zero(jm, waitpid(p_i, &status, WNOHANG)) == 0) {
6867 jobmgr_log(jm, LOG_INFO | LOG_CONSOLE, "Unreaped zombie stray exited with status %i.", WEXITSTATUS(status));
6868 }
6869 kp_skipped++;
6870 } else {
6871 job_t leader = jobmgr_find_by_pid(jm, pg_i, false);
6872 /* See rdar://problem/6745714. Some jobs have child processes that back kernel state,
6873 * so we don't want to terminate them. Long-term, I'd really like to provide shutdown
6874 * hints to the kernel along the way, so that it could shutdown certain subsystems when
6875 * their userspace emissaries go away, before the call to reboot(2).
6876 */
6877 if (leader && leader->ignore_pg_at_shutdown) {
6878 kp_skipped++;
6879 } else {
6880 ps[i] = p_i;
6881 }
6882 }
6883 } else {
6884 kp_skipped++;
6885 }
6886 }
6887
6888 if ((kp_cnt - kp_skipped > 0) && kill_strays) {
6889 jobmgr_kill_stray_children(jm, ps, kp_cnt - kp_skipped);
6890 }
6891
6892 free(ps);
6893 out:
6894 free(pids);
6895 }
6896
6897 jobmgr_t
jobmgr_parent(jobmgr_t jm)6898 jobmgr_parent(jobmgr_t jm)
6899 {
6900 return jm->parentmgr;
6901 }
6902
6903 void
job_uncork_fork(job_t j)6904 job_uncork_fork(job_t j)
6905 {
6906 pid_t c = j->p;
6907
6908 job_log(j, LOG_DEBUG, "Uncorking the fork().");
6909 /* this unblocks the child and avoids a race
6910 * between the above fork() and the kevent_mod() */
6911 (void)job_assumes(j, write(j->fork_fd, &c, sizeof(c)) == sizeof(c));
6912 (void)job_assumes_zero_p(j, runtime_close(j->fork_fd));
6913 j->fork_fd = 0;
6914 }
6915
6916 jobmgr_t
jobmgr_new(jobmgr_t jm,mach_port_t requestorport,mach_port_t transfer_port,bool sflag,const char * name,bool skip_init,mach_port_t asport)6917 jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bool sflag, const char *name, bool skip_init, mach_port_t asport)
6918 {
6919 job_t bootstrapper = NULL;
6920 jobmgr_t jmr;
6921
6922 __OS_COMPILETIME_ASSERT__(offsetof(struct jobmgr_s, kqjobmgr_callback) == 0);
6923
6924 if (unlikely(jm && requestorport == MACH_PORT_NULL)) {
6925 syslog(LOG_ERR, "no requester port!!!!");
6926 jobmgr_log(jm, LOG_ERR, "Mach sub-bootstrap create request requires a requester port");
6927 return NULL;
6928 }
6929
6930 jmr = calloc(1, sizeof(struct jobmgr_s) + (name ? (strlen(name) + 1) : NAME_MAX + 1));
6931
6932 if (!jobmgr_assumes(jm, jmr != NULL)) {
6933 return NULL;
6934 }
6935
6936 if (jm == NULL) {
6937 root_jobmgr = jmr;
6938 }
6939
6940 jmr->kqjobmgr_callback = jobmgr_callback;
6941 strcpy(jmr->name_init, name ? name : "Under construction");
6942
6943 jmr->req_port = requestorport;
6944
6945 if ((jmr->parentmgr = jm)) {
6946 SLIST_INSERT_HEAD(&jm->submgrs, jmr, sle);
6947 }
6948 syslog(LOG_ERR, "if jm=%p then launchd_mport_notify_req transfer_port=%d", jm, transfer_port);
6949 if (jm && jobmgr_assumes_zero(jmr, launchd_mport_notify_req(jmr->req_port, MACH_NOTIFY_DEAD_NAME)) != KERN_SUCCESS) {
6950 syslog(LOG_ERR, "launchd_mport_notify_req failed!!!");
6951 sleep(1);
6952 goto out_bad;
6953 }
6954
6955 if (transfer_port != MACH_PORT_NULL) {
6956 syslog(LOG_ERR, "transfer_port=%d\n", transfer_port);
6957 (void)jobmgr_assumes(jmr, jm != NULL);
6958 jmr->jm_port = transfer_port;
6959 } else if (!jm && !pid1_magic && uflag == false) {
6960 char *trusted_fd = getenv(LAUNCHD_TRUSTED_FD_ENV);
6961 name_t service_buf;
6962
6963 snprintf(service_buf, sizeof(service_buf), "com.apple.launchd.peruser.%u", getuid());
6964
6965 if (uflag == false && jobmgr_assumes_zero(jmr, bootstrap_check_in(bootstrap_port, service_buf, &jmr->jm_port)) != 0) {
6966 goto out_bad;
6967 }
6968
6969 if (trusted_fd) {
6970 int dfd, lfd = (int) strtol(trusted_fd, NULL, 10);
6971
6972 if ((dfd = dup(lfd)) >= 0) {
6973 (void)jobmgr_assumes_zero_p(jmr, runtime_close(dfd));
6974 (void)jobmgr_assumes_zero_p(jmr, runtime_close(lfd));
6975 }
6976
6977 unsetenv(LAUNCHD_TRUSTED_FD_ENV);
6978 }
6979
6980 // cut off the Libc cache, we don't want to deadlock against ourself
6981 inherited_bootstrap_port = bootstrap_port;
6982 bootstrap_port = MACH_PORT_NULL;
6983 os_assert_zero(launchd_mport_notify_req(inherited_bootstrap_port, MACH_NOTIFY_DEAD_NAME));
6984
6985 // We set this explicitly as we start each child
6986 os_assert_zero(launchd_set_bport(MACH_PORT_NULL));
6987 } else if (jobmgr_assumes_zero(jmr, launchd_mport_create_recv(&jmr->jm_port)) != KERN_SUCCESS) {
6988 syslog(LOG_ERR, "launchd_mport_create_recv failed");
6989 goto out_bad;
6990 }
6991 syslog(LOG_ERR, "launchd_mport_create_recv(=%d)\n", jmr->jm_port);
6992 if (!name) {
6993 sprintf(jmr->name_init, "%u", MACH_PORT_INDEX(jmr->jm_port));
6994 }
6995
6996 if (!jm) {
6997 syslog(LOG_ERR, "kevent_moddding");
6998 (void)jobmgr_assumes_zero_p(jmr, kevent_mod(SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr));
6999 (void)jobmgr_assumes_zero_p(jmr, kevent_mod(SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr));
7000 (void)jobmgr_assumes_zero_p(jmr, kevent_mod(SIGUSR2, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr));
7001 (void)jobmgr_assumes_zero_p(jmr, kevent_mod(SIGINFO, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr));
7002 (void)jobmgr_assumes_zero_p(jmr, kevent_mod(0, EVFILT_FS, EV_ADD, VQ_MOUNT|VQ_UNMOUNT|VQ_UPDATE, 0, jmr));
7003 }
7004
7005 if (name && !skip_init) {
7006 syslog(LOG_ERR, "jobmgr_init_session");
7007 bootstrapper = jobmgr_init_session(jmr, name, sflag);
7008 }
7009
7010 if (!bootstrapper || !bootstrapper->weird_bootstrap || uflag == true) {
7011 if (jobmgr_assumes_zero(jmr, runtime_add_mport(jmr->jm_port, job_server)) != KERN_SUCCESS) {
7012 goto out_bad;
7013 }
7014 }
7015 syslog(LOG_ERR, "jobmgr created!!!!");
7016 jobmgr_log(jmr, LOG_DEBUG, "Created job manager%s%s", jm ? " with parent: " : ".", jm ? jm->name : "");
7017
7018 if (bootstrapper) {
7019 bootstrapper->asport = asport;
7020
7021 jobmgr_log(jmr, LOG_DEBUG, "Bootstrapping new job manager with audit session %u", asport);
7022 (void)jobmgr_assumes(jmr, job_dispatch(bootstrapper, true) != NULL);
7023 } else {
7024 jmr->req_asport = asport;
7025 }
7026
7027 if (asport != MACH_PORT_NULL) {
7028 (void)jobmgr_assumes_zero(jmr, launchd_mport_copy_send(asport));
7029 }
7030
7031 if (jmr->parentmgr) {
7032 runtime_add_weak_ref();
7033 }
7034
7035 return jmr;
7036
7037 out_bad:
7038 if (jmr) {
7039 jobmgr_remove(jmr);
7040 if (jm == NULL) {
7041 root_jobmgr = NULL;
7042 }
7043 }
7044 return NULL;
7045 }
7046
7047 jobmgr_t
jobmgr_new_xpc_singleton_domain(jobmgr_t jm,name_t name)7048 jobmgr_new_xpc_singleton_domain(jobmgr_t jm, name_t name)
7049 {
7050 jobmgr_t new = NULL;
7051
7052 /* These job managers are basically singletons, so we use the root Mach
7053 * bootstrap port as their requestor ports so they'll never go away.
7054 */
7055 mach_port_t req_port = root_jobmgr->jm_port;
7056 if (jobmgr_assumes_zero(jm, launchd_mport_make_send(req_port)) == KERN_SUCCESS) {
7057 new = jobmgr_new(root_jobmgr, req_port, MACH_PORT_NULL, false, name, true, MACH_PORT_NULL);
7058 if (new) {
7059 new->properties |= BOOTSTRAP_PROPERTY_XPC_SINGLETON;
7060 new->properties |= BOOTSTRAP_PROPERTY_XPC_DOMAIN;
7061 new->xpc_singleton = true;
7062 }
7063 }
7064
7065 return new;
7066 }
7067
7068 jobmgr_t
jobmgr_find_xpc_per_user_domain(jobmgr_t jm,uid_t uid)7069 jobmgr_find_xpc_per_user_domain(jobmgr_t jm, uid_t uid)
7070 {
7071 jobmgr_t jmi = NULL;
7072 LIST_FOREACH(jmi, &_s_xpc_user_domains, xpc_le) {
7073 if (jmi->req_euid == uid) {
7074 return jmi;
7075 }
7076 }
7077
7078 name_t name;
7079 (void)snprintf(name, sizeof(name), "com.apple.xpc.domain.peruser.%u", uid);
7080 jmi = jobmgr_new_xpc_singleton_domain(jm, name);
7081 if (jobmgr_assumes(jm, jmi != NULL)) {
7082 /* We need to create a per-user launchd for this UID if there isn't one
7083 * already so we can grab the bootstrap port.
7084 */
7085 job_t puj = jobmgr_lookup_per_user_context_internal(NULL, uid, &jmi->req_bsport);
7086 if (jobmgr_assumes(jmi, puj != NULL)) {
7087 (void)jobmgr_assumes_zero(jmi, launchd_mport_copy_send(puj->asport));
7088 (void)jobmgr_assumes_zero(jmi, launchd_mport_copy_send(jmi->req_bsport));
7089 jmi->shortdesc = "per-user";
7090 jmi->req_asport = puj->asport;
7091 jmi->req_asid = puj->asid;
7092 jmi->req_euid = uid;
7093 jmi->req_egid = -1;
7094
7095 LIST_INSERT_HEAD(&_s_xpc_user_domains, jmi, xpc_le);
7096 } else {
7097 jobmgr_remove(jmi);
7098 }
7099 }
7100
7101 return jmi;
7102 }
7103
7104 jobmgr_t
jobmgr_find_xpc_per_session_domain(jobmgr_t jm,au_asid_t asid)7105 jobmgr_find_xpc_per_session_domain(jobmgr_t jm, au_asid_t asid)
7106 {
7107 jobmgr_t jmi = NULL;
7108 LIST_FOREACH(jmi, &_s_xpc_session_domains, xpc_le) {
7109 if (jmi->req_asid == asid) {
7110 return jmi;
7111 }
7112 }
7113
7114 name_t name;
7115 (void)snprintf(name, sizeof(name), "com.apple.xpc.domain.persession.%i", asid);
7116 jmi = jobmgr_new_xpc_singleton_domain(jm, name);
7117 if (jobmgr_assumes(jm, jmi != NULL)) {
7118 (void)jobmgr_assumes_zero(jmi, launchd_mport_make_send(root_jobmgr->jm_port));
7119 jmi->shortdesc = "per-session";
7120 jmi->req_bsport = root_jobmgr->jm_port;
7121 #ifdef notyet
7122 (void)jobmgr_assumes_zero(jmi, audit_session_port(asid, &jmi->req_asport));
7123 #endif
7124 jmi->req_asid = asid;
7125 jmi->req_euid = -1;
7126 jmi->req_egid = -1;
7127
7128 LIST_INSERT_HEAD(&_s_xpc_session_domains, jmi, xpc_le);
7129 } else {
7130 jobmgr_remove(jmi);
7131 }
7132
7133 return jmi;
7134 }
7135
7136 job_t
jobmgr_init_session(jobmgr_t jm,const char * session_type,bool sflag)7137 jobmgr_init_session(jobmgr_t jm, const char *session_type, bool sflag)
7138 {
7139 const char *bootstrap_tool[] = { "/bin/launchctl", "bootstrap", "-S", session_type, sflag ? "-s" : NULL, NULL };
7140 char thelabel[1000];
7141 job_t bootstrapper;
7142
7143 snprintf(thelabel, sizeof(thelabel), "com.apple.launchctl.%s", session_type);
7144 bootstrapper = job_new(jm, thelabel, NULL, bootstrap_tool);
7145
7146 if (jobmgr_assumes(jm, bootstrapper != NULL) && (jm->parentmgr || !pid1_magic)) {
7147 bootstrapper->is_bootstrapper = true;
7148 char buf[100];
7149
7150 // <rdar://problem/5042202> launchd-201: can't ssh in with AFP OD account (hangs)
7151 snprintf(buf, sizeof(buf), "0x%X:0:0", getuid());
7152 envitem_new(bootstrapper, "__CF_USER_TEXT_ENCODING", buf, false);
7153 bootstrapper->weird_bootstrap = true;
7154 (void)jobmgr_assumes(jm, job_setup_machport(bootstrapper));
7155 } else if (bootstrapper && strncmp(session_type, VPROCMGR_SESSION_SYSTEM, sizeof(VPROCMGR_SESSION_SYSTEM)) == 0) {
7156 #if TARGET_OS_EMBEDDED
7157 bootstrapper->psproctype = POSIX_SPAWN_PROC_TYPE_DAEMON_INTERACTIVE;
7158 #endif
7159 bootstrapper->is_bootstrapper = true;
7160 if (jobmgr_assumes(jm, pid1_magic)) {
7161 // Have our system bootstrapper print out to the console.
7162 bootstrapper->stdoutpath = strdup(_PATH_CONSOLE);
7163 bootstrapper->stderrpath = strdup(_PATH_CONSOLE);
7164
7165 if (launchd_console) {
7166 (void)jobmgr_assumes_zero_p(jm, kevent_mod((uintptr_t)fileno(launchd_console), EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_REVOKE, 0, jm));
7167 }
7168 }
7169 }
7170
7171 jm->session_initialized = true;
7172 return bootstrapper;
7173 }
7174
7175 jobmgr_t
jobmgr_delete_anything_with_port(jobmgr_t jm,mach_port_t port)7176 jobmgr_delete_anything_with_port(jobmgr_t jm, mach_port_t port)
7177 {
7178 struct machservice *ms, *next_ms;
7179 jobmgr_t jmi, jmn;
7180
7181 /* Mach ports, unlike Unix descriptors, are reference counted. In other
7182 * words, when some program hands us a second or subsequent send right to a
7183 * port we already have open, the Mach kernel gives us the same port number
7184 * back and increments an reference count associated with the port. This
7185 * This forces us, when discovering that a receive right at the other end
7186 * has been deleted, to wander all of our objects to see what weird places
7187 * clients might have handed us the same send right to use.
7188 */
7189
7190 if (jm == root_jobmgr) {
7191 if (port == inherited_bootstrap_port) {
7192 (void)jobmgr_assumes_zero(jm, launchd_mport_deallocate(port));
7193 inherited_bootstrap_port = MACH_PORT_NULL;
7194
7195 return jobmgr_shutdown(jm);
7196 }
7197
7198 LIST_FOREACH_SAFE(ms, &port_hash[HASH_PORT(port)], port_hash_sle, next_ms) {
7199 if (ms->port == port && !ms->recv) {
7200 machservice_delete(ms->job, ms, true);
7201 }
7202 }
7203 }
7204
7205 SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
7206 jobmgr_delete_anything_with_port(jmi, port);
7207 }
7208
7209 if (jm->req_port == port) {
7210 jobmgr_log(jm, LOG_DEBUG, "Request port died: %i", MACH_PORT_INDEX(port));
7211 return jobmgr_shutdown(jm);
7212 }
7213
7214 struct waiting4attach *w4ai = NULL;
7215 struct waiting4attach *w4ait = NULL;
7216 LIST_FOREACH_SAFE(w4ai, &jm->attaches, le, w4ait) {
7217 if (port == w4ai->port) {
7218 waiting4attach_delete(jm, w4ai);
7219 break;
7220 }
7221 }
7222
7223 return jm;
7224 }
7225
7226 void
jobmgr_reap_pid(jobmgr_t jm,pid_t pid)7227 jobmgr_reap_pid(jobmgr_t jm, pid_t pid)
7228 {
7229 if (jobmgr_find_by_pid_deep(jm, pid, true) != NULL)
7230 return;
7231
7232 waitpid(pid, (int *) 0, 0);
7233 jobmgr_log(jm, LOG_DEBUG, "Reaping PID %d", pid);
7234 }
7235
7236 struct machservice *
jobmgr_lookup_service(jobmgr_t jm,const char * name,bool check_parent,pid_t target_pid)7237 jobmgr_lookup_service(jobmgr_t jm, const char *name, bool check_parent, pid_t target_pid)
7238 {
7239 struct machservice *ms;
7240 job_t target_j;
7241
7242 jobmgr_log(jm, LOG_DEBUG, "Looking up %sservice %s", target_pid ? "per-PID " : "", name);
7243
7244 if (target_pid) {
7245 /* This is a hack to let FileSyncAgent look up per-PID Mach services from the Background
7246 * bootstrap in other bootstraps.
7247 */
7248
7249 // Start in the given bootstrap.
7250 if (unlikely((target_j = jobmgr_find_by_pid(jm, target_pid, false)) == NULL)) {
7251 // If we fail, do a deep traversal.
7252 if (unlikely((target_j = jobmgr_find_by_pid_deep(root_jobmgr, target_pid, true)) == NULL)) {
7253 jobmgr_log(jm, LOG_DEBUG, "Didn't find PID %i", target_pid);
7254 return NULL;
7255 }
7256 }
7257
7258 SLIST_FOREACH(ms, &target_j->machservices, sle) {
7259 if (ms->per_pid && strcmp(name, ms->name) == 0) {
7260 return ms;
7261 }
7262 }
7263
7264 job_log(target_j, LOG_DEBUG, "Didn't find per-PID Mach service: %s", name);
7265 return NULL;
7266 }
7267
7268 jobmgr_t where2look = jm;
7269 // XPC domains are separate from Mach bootstraps.
7270 if (!(jm->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) {
7271 if (launchd_flat_mach_namespace && !(jm->properties & BOOTSTRAP_PROPERTY_EXPLICITSUBSET)) {
7272 where2look = root_jobmgr;
7273 }
7274 }
7275
7276 LIST_FOREACH(ms, &where2look->ms_hash[hash_ms(name)], name_hash_sle) {
7277 if (!ms->per_pid && strcmp(name, ms->name) == 0) {
7278 return ms;
7279 }
7280 }
7281
7282 if (jm->parentmgr == NULL || !check_parent) {
7283 return NULL;
7284 }
7285
7286 return jobmgr_lookup_service(jm->parentmgr, name, true, 0);
7287 }
7288
7289 mach_port_t
machservice_port(struct machservice * ms)7290 machservice_port(struct machservice *ms)
7291 {
7292 return ms->port;
7293 }
7294
7295 job_t
machservice_job(struct machservice * ms)7296 machservice_job(struct machservice *ms)
7297 {
7298 return ms->job;
7299 }
7300
7301 bool
machservice_hidden(struct machservice * ms)7302 machservice_hidden(struct machservice *ms)
7303 {
7304 return ms->hide;
7305 }
7306
7307 bool
machservice_active(struct machservice * ms)7308 machservice_active(struct machservice *ms)
7309 {
7310 return ms->isActive;
7311 }
7312
7313 const char *
machservice_name(struct machservice * ms)7314 machservice_name(struct machservice *ms)
7315 {
7316 return ms->name;
7317 }
7318
7319 void
machservice_drain_port(struct machservice * ms)7320 machservice_drain_port(struct machservice *ms)
7321 {
7322 bool drain_one = ms->drain_one_on_crash;
7323 bool drain_all = ms->drain_all_on_crash;
7324
7325
7326 if (!job_assumes(ms->job, (drain_one || drain_all) == true)) {
7327 return;
7328 }
7329
7330 job_log(ms->job, LOG_INFO, "Draining %s...", ms->name);
7331 char *req_buff = calloc(2, sizeof(union __RequestUnion__catch_mach_exc_subsystem));
7332 char *rep_buff = calloc(1, sizeof(union __ReplyUnion__catch_mach_exc_subsystem));
7333 mig_reply_error_t *req_hdr = (mig_reply_error_t *)&req_buff;
7334 mig_reply_error_t *rep_hdr = (mig_reply_error_t *)&rep_buff;
7335
7336 mach_msg_return_t mr = ~MACH_MSG_SUCCESS;
7337
7338 do {
7339 /* This should be a direct check on the Mach service to see if it's an exception-handling
7340 * port, and it will break things if ReportCrash or SafetyNet start advertising other
7341 * Mach services. But for now, it should be okay.
7342 */
7343 if (ms->job->alt_exc_handler || ms->job->internal_exc_handler) {
7344 mr = launchd_exc_runtime_once(ms->port, sizeof(req_buff), sizeof(rep_buff), req_hdr, rep_hdr, 0);
7345 } else {
7346 mach_msg_options_t options = MACH_RCV_MSG |
7347 MACH_RCV_TIMEOUT ;
7348
7349 mr = mach_msg((mach_msg_header_t *)req_hdr, options, 0, sizeof(req_buff), ms->port, 0, MACH_PORT_NULL);
7350 switch (mr) {
7351 case MACH_MSG_SUCCESS:
7352 mach_msg_destroy((mach_msg_header_t *)req_hdr);
7353 break;
7354 case MACH_RCV_TIMED_OUT:
7355 break;
7356 case MACH_RCV_TOO_LARGE:
7357 launchd_syslog(LOG_WARNING, "Tried to receive message that was larger than %lu bytes", sizeof(req_buff));
7358 break;
7359 default:
7360 break;
7361 }
7362 }
7363 } while (drain_all && mr != MACH_RCV_TIMED_OUT);
7364 }
7365
7366 void
machservice_delete(job_t j,struct machservice * ms,bool port_died)7367 machservice_delete(job_t j, struct machservice *ms, bool port_died)
7368 {
7369 if (ms->alias) {
7370 /* HACK: Egregious code duplication. But dealing with aliases is a
7371 * pretty simple affair since they can't and shouldn't have any complex
7372 * behaviors associated with them.
7373 */
7374 LIST_REMOVE(ms, name_hash_sle);
7375 SLIST_REMOVE(&j->machservices, ms, machservice, sle);
7376 free(ms);
7377 return;
7378 }
7379
7380 if (unlikely(ms->debug_on_close)) {
7381 job_log(j, LOG_NOTICE, "About to enter kernel debugger because of Mach port: 0x%x", ms->port);
7382 (void)job_assumes_zero(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER));
7383 }
7384
7385 if (ms->recv && job_assumes(j, !machservice_active(ms))) {
7386 job_log(j, LOG_DEBUG, "Closing receive right for %s", ms->name);
7387 (void)job_assumes_zero(j, launchd_mport_close_recv(ms->port));
7388 }
7389
7390 (void)job_assumes_zero(j, launchd_mport_deallocate(ms->port));
7391
7392 if (unlikely(ms->port == the_exception_server)) {
7393 the_exception_server = 0;
7394 }
7395
7396 job_log(j, LOG_DEBUG, "Mach service deleted%s: %s", port_died ? " (port died)" : "", ms->name);
7397
7398 if (ms->special_port_num) {
7399 SLIST_REMOVE(&special_ports, ms, machservice, special_port_sle);
7400 }
7401 SLIST_REMOVE(&j->machservices, ms, machservice, sle);
7402
7403 if (!(j->dedicated_instance || ms->event_channel)) {
7404 LIST_REMOVE(ms, name_hash_sle);
7405 }
7406 LIST_REMOVE(ms, port_hash_sle);
7407
7408 free(ms);
7409 }
7410
7411 void
machservice_request_notifications(struct machservice * ms)7412 machservice_request_notifications(struct machservice *ms)
7413 {
7414 mach_msg_id_t which = MACH_NOTIFY_DEAD_NAME;
7415
7416 ms->isActive = true;
7417
7418 if (ms->recv) {
7419 which = MACH_NOTIFY_PORT_DESTROYED;
7420 job_checkin(ms->job);
7421 }
7422
7423 (void)job_assumes_zero(ms->job, launchd_mport_notify_req(ms->port, which));
7424 }
7425
7426 #define NELEM(x) (sizeof(x)/sizeof(x[0]))
7427 #define END_OF(x) (&(x)[NELEM(x)])
7428
7429 char **
mach_cmd2argv(const char * string)7430 mach_cmd2argv(const char *string)
7431 {
7432 char *argv[100], args[1000];
7433 const char *cp;
7434 char *argp = args, term, **argv_ret, *co;
7435 unsigned int nargs = 0, i;
7436
7437 for (cp = string; *cp;) {
7438 while (isspace(*cp))
7439 cp++;
7440 term = (*cp == '"') ? *cp++ : '\0';
7441 if (nargs < NELEM(argv)) {
7442 argv[nargs++] = argp;
7443 }
7444 while (*cp && (term ? *cp != term : !isspace(*cp)) && argp < END_OF(args)) {
7445 if (*cp == '\\') {
7446 cp++;
7447 }
7448 *argp++ = *cp;
7449 if (*cp) {
7450 cp++;
7451 }
7452 }
7453 *argp++ = '\0';
7454 }
7455 argv[nargs] = NULL;
7456
7457 if (nargs == 0) {
7458 return NULL;
7459 }
7460
7461 argv_ret = malloc((nargs + 1) * sizeof(char *) + strlen(string) + 1);
7462
7463 if (!argv_ret) {
7464 (void)os_assumes_zero(errno);
7465 return NULL;
7466 }
7467
7468 co = (char *)argv_ret + (nargs + 1) * sizeof(char *);
7469
7470 for (i = 0; i < nargs; i++) {
7471 strcpy(co, argv[i]);
7472 argv_ret[i] = co;
7473 co += strlen(argv[i]) + 1;
7474 }
7475 argv_ret[i] = NULL;
7476
7477 return argv_ret;
7478 }
7479
7480 void
job_checkin(job_t j)7481 job_checkin(job_t j)
7482 {
7483 j->checkedin = true;
7484 }
7485
job_is_god(job_t j)7486 bool job_is_god(job_t j)
7487 {
7488 return j->embedded_god;
7489 }
7490
7491 bool
job_ack_port_destruction(mach_port_t p)7492 job_ack_port_destruction(mach_port_t p)
7493 {
7494 struct machservice *ms;
7495 job_t j;
7496
7497 LIST_FOREACH(ms, &port_hash[HASH_PORT(p)], port_hash_sle) {
7498 if (ms->recv && (ms->port == p)) {
7499 break;
7500 }
7501 }
7502
7503 if (!ms) {
7504 launchd_syslog(LOG_WARNING, "Could not find MachService to match receive right: 0x%x", p);
7505 return false;
7506 }
7507
7508 j = ms->job;
7509
7510 jobmgr_log(root_jobmgr, LOG_DEBUG, "Receive right returned to us: %s", ms->name);
7511
7512 /* Without being the exception handler, NOTE_EXIT is our only way to tell if
7513 * the job crashed, and we can't rely on NOTE_EXIT always being processed
7514 * after all the job's receive rights have been returned.
7515 *
7516 * So when we get receive rights back, check to see if the job has been
7517 * reaped yet. If not, then we add this service to a list of services to be
7518 * drained on crash if it's requested that behavior. So, for a job with N
7519 * receive rights all requesting that they be drained on crash, we can
7520 * safely handle the following sequence of events.
7521 *
7522 * ReceiveRight0Returned
7523 * ReceiveRight1Returned
7524 * ReceiveRight2Returned
7525 * NOTE_EXIT (reap, get exit status)
7526 * ReceiveRight3Returned
7527 * .
7528 * .
7529 * .
7530 * ReceiveRight(N - 1)Returned
7531 */
7532 if (ms->drain_one_on_crash || ms->drain_all_on_crash) {
7533 if (j->crashed && j->reaped) {
7534 job_log(j, LOG_DEBUG, "Job has crashed. Draining port...");
7535 machservice_drain_port(ms);
7536 } else if (!(j->crashed || j->reaped)) {
7537 job_log(j, LOG_DEBUG, "Job's exit status is still unknown. Deferring drain.");
7538 }
7539 }
7540
7541 ms->isActive = false;
7542 if (ms->delete_on_destruction) {
7543 machservice_delete(j, ms, false);
7544 } else if (ms->reset) {
7545 machservice_resetport(j, ms);
7546 }
7547
7548 kern_return_t kr = mach_port_set_attributes(mach_task_self(), ms->port, MACH_PORT_TEMPOWNER, NULL, 0);
7549 (void)job_assumes_zero(j, kr);
7550 machservice_stamp_port(j, ms);
7551 job_dispatch(j, false);
7552
7553 if (ms->recv_race_hack) {
7554 ms->recv_race_hack = false;
7555 machservice_watch(ms->job, ms);
7556 }
7557
7558 root_jobmgr = jobmgr_do_garbage_collection(root_jobmgr);
7559
7560 return true;
7561 }
7562
7563 void
job_ack_no_senders(job_t j)7564 job_ack_no_senders(job_t j)
7565 {
7566 j->priv_port_has_senders = false;
7567
7568 (void)job_assumes_zero(j, launchd_mport_close_recv(j->j_port));
7569 j->j_port = 0;
7570
7571 job_log(j, LOG_DEBUG, "No more senders on privileged Mach bootstrap port");
7572
7573 job_dispatch(j, false);
7574 }
7575
7576 bool
semaphoreitem_new(job_t j,semaphore_reason_t why,const char * what)7577 semaphoreitem_new(job_t j, semaphore_reason_t why, const char *what)
7578 {
7579 struct semaphoreitem *si;
7580 size_t alloc_sz = sizeof(struct semaphoreitem);
7581
7582 if (what) {
7583 alloc_sz += strlen(what) + 1;
7584 }
7585
7586 if (job_assumes(j, si = calloc(1, alloc_sz)) == NULL) {
7587 return false;
7588 }
7589
7590 si->why = why;
7591
7592 if (what) {
7593 strcpy(si->what_init, what);
7594 }
7595
7596 SLIST_INSERT_HEAD(&j->semaphores, si, sle);
7597
7598 if ((why == OTHER_JOB_ENABLED || why == OTHER_JOB_DISABLED) && !j->nosy) {
7599 job_log(j, LOG_DEBUG, "Job is interested in \"%s\".", what);
7600 SLIST_INSERT_HEAD(&s_curious_jobs, j, curious_jobs_sle);
7601 j->nosy = true;
7602 }
7603
7604 semaphoreitem_runtime_mod_ref(si, true);
7605
7606 return true;
7607 }
7608
7609 void
semaphoreitem_runtime_mod_ref(struct semaphoreitem * si,bool add)7610 semaphoreitem_runtime_mod_ref(struct semaphoreitem *si, bool add)
7611 {
7612 /*
7613 * External events need to be tracked.
7614 * Internal events do NOT need to be tracked.
7615 */
7616
7617 switch (si->why) {
7618 case SUCCESSFUL_EXIT:
7619 case FAILED_EXIT:
7620 case OTHER_JOB_ENABLED:
7621 case OTHER_JOB_DISABLED:
7622 case OTHER_JOB_ACTIVE:
7623 case OTHER_JOB_INACTIVE:
7624 return;
7625 default:
7626 break;
7627 }
7628
7629 if (add) {
7630 runtime_add_weak_ref();
7631 } else {
7632 runtime_del_weak_ref();
7633 }
7634 }
7635
7636 void
semaphoreitem_delete(job_t j,struct semaphoreitem * si)7637 semaphoreitem_delete(job_t j, struct semaphoreitem *si)
7638 {
7639 semaphoreitem_runtime_mod_ref(si, false);
7640
7641 SLIST_REMOVE(&j->semaphores, si, semaphoreitem, sle);
7642
7643 // We'll need to rethink this if it ever becomes possible to dynamically add or remove semaphores.
7644 if ((si->why == OTHER_JOB_ENABLED || si->why == OTHER_JOB_DISABLED) && j->nosy) {
7645 j->nosy = false;
7646 SLIST_REMOVE(&s_curious_jobs, j, job_s, curious_jobs_sle);
7647 }
7648
7649 free(si);
7650 }
7651
7652 void
semaphoreitem_setup_dict_iter(launch_data_t obj,const char * key,void * context)7653 semaphoreitem_setup_dict_iter(launch_data_t obj, const char *key, void *context)
7654 {
7655 struct semaphoreitem_dict_iter_context *sdic = context;
7656 semaphore_reason_t why;
7657
7658 why = launch_data_get_bool(obj) ? sdic->why_true : sdic->why_false;
7659
7660 semaphoreitem_new(sdic->j, why, key);
7661 }
7662
7663 void
semaphoreitem_setup(launch_data_t obj,const char * key,void * context)7664 semaphoreitem_setup(launch_data_t obj, const char *key, void *context)
7665 {
7666 struct semaphoreitem_dict_iter_context sdic = { context, 0, 0 };
7667 job_t j = context;
7668 semaphore_reason_t why;
7669
7670 switch (launch_data_get_type(obj)) {
7671 case LAUNCH_DATA_BOOL:
7672 if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_NETWORKSTATE) == 0) {
7673 why = launch_data_get_bool(obj) ? NETWORK_UP : NETWORK_DOWN;
7674 semaphoreitem_new(j, why, NULL);
7675 } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT) == 0) {
7676 why = launch_data_get_bool(obj) ? SUCCESSFUL_EXIT : FAILED_EXIT;
7677 semaphoreitem_new(j, why, NULL);
7678 j->start_pending = true;
7679 } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_AFTERINITIALDEMAND) == 0) {
7680 j->needs_kickoff = launch_data_get_bool(obj);
7681 } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_CRASHED) == 0) {
7682 why = launch_data_get_bool(obj) ? CRASHED : DID_NOT_CRASH;
7683 semaphoreitem_new(j, why, NULL);
7684 j->start_pending = true;
7685 } else {
7686 job_log(j, LOG_ERR, "Unrecognized KeepAlive attribute: %s", key);
7687 }
7688 break;
7689 case LAUNCH_DATA_DICTIONARY:
7690 if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBACTIVE) == 0) {
7691 sdic.why_true = OTHER_JOB_ACTIVE;
7692 sdic.why_false = OTHER_JOB_INACTIVE;
7693 } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBENABLED) == 0) {
7694 sdic.why_true = OTHER_JOB_ENABLED;
7695 sdic.why_false = OTHER_JOB_DISABLED;
7696 } else {
7697 job_log(j, LOG_ERR, "Unrecognized KeepAlive attribute: %s", key);
7698 break;
7699 }
7700
7701 launch_data_dict_iterate(obj, semaphoreitem_setup_dict_iter, &sdic);
7702 break;
7703 default:
7704 job_log(j, LOG_ERR, "Unrecognized KeepAlive type: %u", launch_data_get_type(obj));
7705 break;
7706 }
7707 }
7708
7709 bool
externalevent_new(job_t j,struct eventsystem * sys,const char * evname,xpc_object_t event,uint64_t flags __unused)7710 externalevent_new(job_t j, struct eventsystem *sys, const char *evname, xpc_object_t event, uint64_t flags __unused)
7711 {
7712 if (j->event_monitor) {
7713 job_log(j, LOG_ERR, "The event monitor job cannot use LaunchEvents or XPC Events.");
7714 return false;
7715 }
7716
7717 struct externalevent *ee = (struct externalevent *)calloc(1, sizeof(struct externalevent) + strlen(evname) + 1);
7718 if (!ee) {
7719 return false;
7720 }
7721
7722 ee->event = xpc_retain(event);
7723 (void)strcpy(ee->name, evname);
7724 ee->job = j;
7725 ee->id = sys->curid;
7726 ee->sys = sys;
7727 ee->state = false;
7728 ee->wanted_state = true;
7729 sys->curid++;
7730
7731 #ifdef notyet
7732 if (flags & XPC_EVENT_FLAG_ENTITLEMENTS) {
7733 struct ldcred *ldc = runtime_get_caller_creds();
7734 if (ldc) {
7735 ee->entitlements = xpc_copy_entitlements_for_pid(ldc->pid);
7736 }
7737 }
7738 #endif
7739 if (sys == _launchd_support_system) {
7740 ee->internal = true;
7741 }
7742
7743 LIST_INSERT_HEAD(&j->events, ee, job_le);
7744 LIST_INSERT_HEAD(&sys->events, ee, sys_le);
7745
7746 job_log(j, LOG_DEBUG, "New event: %s/%s", sys->name, evname);
7747
7748 eventsystem_ping();
7749 return true;
7750 }
7751
7752 void
externalevent_delete(struct externalevent * ee)7753 externalevent_delete(struct externalevent *ee)
7754 {
7755 xpc_release(ee->event);
7756 if (ee->entitlements) {
7757 xpc_release(ee->entitlements);
7758 }
7759 LIST_REMOVE(ee, job_le);
7760 LIST_REMOVE(ee, sys_le);
7761
7762 free(ee);
7763
7764 eventsystem_ping();
7765 }
7766
7767 void
externalevent_setup(launch_data_t obj,const char * key,void * context)7768 externalevent_setup(launch_data_t obj, const char *key, void *context)
7769 {
7770 /* This method can ONLY be called on the job_import() path, as it assumes
7771 * the input is a launch_data_t.
7772 */
7773 struct externalevent_iter_ctx *ctx = (struct externalevent_iter_ctx *)context;
7774
7775 xpc_object_t xobj = ld2xpc(obj);
7776 if (xobj) {
7777 job_log(ctx->j, LOG_DEBUG, "Importing stream/event: %s/%s", ctx->sys->name, key);
7778 externalevent_new(ctx->j, ctx->sys, key, xobj, 0);
7779 xpc_release(xobj);
7780 } else {
7781 job_log(ctx->j, LOG_ERR, "Could not import event for job: %s", key);
7782 }
7783 }
7784
7785 struct externalevent *
externalevent_find(const char * sysname,uint64_t id)7786 externalevent_find(const char *sysname, uint64_t id)
7787 {
7788 struct externalevent *ei = NULL;
7789
7790 struct eventsystem *es = eventsystem_find(sysname);
7791 if (es != NULL) {
7792 LIST_FOREACH(ei, &es->events, sys_le) {
7793 if (ei->id == id) {
7794 break;
7795 }
7796 }
7797 } else {
7798 launchd_syslog(LOG_ERR, "Could not find event system: %s", sysname);
7799 }
7800
7801 return ei;
7802 }
7803
7804 struct eventsystem *
eventsystem_new(const char * name)7805 eventsystem_new(const char *name)
7806 {
7807 struct eventsystem *es = (struct eventsystem *)calloc(1, sizeof(struct eventsystem) + strlen(name) + 1);
7808 if (es != NULL) {
7809 es->curid = 1;
7810 (void)strcpy(es->name, name);
7811 LIST_INSERT_HEAD(&_s_event_systems, es, global_le);
7812 } else {
7813 (void)os_assumes_zero(errno);
7814 }
7815
7816 return es;
7817 }
7818
7819 void
eventsystem_delete(struct eventsystem * es)7820 eventsystem_delete(struct eventsystem *es)
7821 {
7822 struct externalevent *ei = NULL;
7823 while ((ei = LIST_FIRST(&es->events))) {
7824 externalevent_delete(ei);
7825 }
7826
7827 LIST_REMOVE(es, global_le);
7828
7829 free(es);
7830 }
7831
7832 void
eventsystem_setup(launch_data_t obj,const char * key,void * context)7833 eventsystem_setup(launch_data_t obj, const char *key, void *context)
7834 {
7835 job_t j = (job_t)context;
7836 if (!job_assumes(j, launch_data_get_type(obj) == LAUNCH_DATA_DICTIONARY)) {
7837 return;
7838 }
7839
7840 struct eventsystem *sys = eventsystem_find(key);
7841 if (unlikely(sys == NULL)) {
7842 sys = eventsystem_new(key);
7843 job_log(j, LOG_DEBUG, "New event system: %s", key);
7844 }
7845
7846 if (job_assumes(j, sys != NULL)) {
7847 struct externalevent_iter_ctx ctx = {
7848 .j = j,
7849 .sys = sys,
7850 };
7851
7852 job_log(j, LOG_DEBUG, "Importing events for stream: %s", key);
7853 launch_data_dict_iterate(obj, externalevent_setup, &ctx);
7854 }
7855 }
7856
7857 struct eventsystem *
eventsystem_find(const char * name)7858 eventsystem_find(const char *name)
7859 {
7860 struct eventsystem *esi = NULL;
7861 LIST_FOREACH(esi, &_s_event_systems, global_le) {
7862 if (strcmp(name, esi->name) == 0) {
7863 break;
7864 }
7865 }
7866
7867 return esi;
7868 }
7869
7870 void
eventsystem_ping(void)7871 eventsystem_ping(void)
7872 {
7873 if (!_launchd_event_monitor) {
7874 return;
7875 }
7876
7877 if (!_launchd_event_monitor->p) {
7878 (void)job_dispatch(_launchd_event_monitor, true);
7879 } else {
7880 if (_launchd_event_monitor->event_monitor_ready2signal) {
7881 (void)job_assumes_zero_p(_launchd_event_monitor, kill(_launchd_event_monitor->p, SIGUSR1));
7882 }
7883 }
7884 }
7885
7886 void
jobmgr_dispatch_all_semaphores(jobmgr_t jm)7887 jobmgr_dispatch_all_semaphores(jobmgr_t jm)
7888 {
7889 jobmgr_t jmi, jmn;
7890 job_t ji, jn;
7891
7892
7893 SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
7894 jobmgr_dispatch_all_semaphores(jmi);
7895 }
7896
7897 LIST_FOREACH_SAFE(ji, &jm->jobs, sle, jn) {
7898 if (!SLIST_EMPTY(&ji->semaphores)) {
7899 job_dispatch(ji, false);
7900 }
7901 }
7902 }
7903
7904 time_t
cronemu(int mon,int mday,int hour,int min)7905 cronemu(int mon, int mday, int hour, int min)
7906 {
7907 struct tm workingtm;
7908 time_t now;
7909
7910 now = time(NULL);
7911 workingtm = *localtime(&now);
7912
7913 workingtm.tm_isdst = -1;
7914 workingtm.tm_sec = 0;
7915 workingtm.tm_min++;
7916
7917 while (!cronemu_mon(&workingtm, mon, mday, hour, min)) {
7918 workingtm.tm_year++;
7919 workingtm.tm_mon = 0;
7920 workingtm.tm_mday = 1;
7921 workingtm.tm_hour = 0;
7922 workingtm.tm_min = 0;
7923 mktime(&workingtm);
7924 }
7925
7926 return mktime(&workingtm);
7927 }
7928
7929 time_t
cronemu_wday(int wday,int hour,int min)7930 cronemu_wday(int wday, int hour, int min)
7931 {
7932 struct tm workingtm;
7933 time_t now;
7934
7935 now = time(NULL);
7936 workingtm = *localtime(&now);
7937
7938 workingtm.tm_isdst = -1;
7939 workingtm.tm_sec = 0;
7940 workingtm.tm_min++;
7941
7942 if (wday == 7) {
7943 wday = 0;
7944 }
7945
7946 while (!(workingtm.tm_wday == wday && cronemu_hour(&workingtm, hour, min))) {
7947 workingtm.tm_mday++;
7948 workingtm.tm_hour = 0;
7949 workingtm.tm_min = 0;
7950 mktime(&workingtm);
7951 }
7952
7953 return mktime(&workingtm);
7954 }
7955
7956 bool
cronemu_mon(struct tm * wtm,int mon,int mday,int hour,int min)7957 cronemu_mon(struct tm *wtm, int mon, int mday, int hour, int min)
7958 {
7959 if (mon == -1) {
7960 struct tm workingtm = *wtm;
7961 int carrytest;
7962
7963 while (!cronemu_mday(&workingtm, mday, hour, min)) {
7964 workingtm.tm_mon++;
7965 workingtm.tm_mday = 1;
7966 workingtm.tm_hour = 0;
7967 workingtm.tm_min = 0;
7968 carrytest = workingtm.tm_mon;
7969 mktime(&workingtm);
7970 if (carrytest != workingtm.tm_mon) {
7971 return false;
7972 }
7973 }
7974 *wtm = workingtm;
7975 return true;
7976 }
7977
7978 if (mon < wtm->tm_mon) {
7979 return false;
7980 }
7981
7982 if (mon > wtm->tm_mon) {
7983 wtm->tm_mon = mon;
7984 wtm->tm_mday = 1;
7985 wtm->tm_hour = 0;
7986 wtm->tm_min = 0;
7987 }
7988
7989 return cronemu_mday(wtm, mday, hour, min);
7990 }
7991
7992 bool
cronemu_mday(struct tm * wtm,int mday,int hour,int min)7993 cronemu_mday(struct tm *wtm, int mday, int hour, int min)
7994 {
7995 if (mday == -1) {
7996 struct tm workingtm = *wtm;
7997 int carrytest;
7998
7999 while (!cronemu_hour(&workingtm, hour, min)) {
8000 workingtm.tm_mday++;
8001 workingtm.tm_hour = 0;
8002 workingtm.tm_min = 0;
8003 carrytest = workingtm.tm_mday;
8004 mktime(&workingtm);
8005 if (carrytest != workingtm.tm_mday) {
8006 return false;
8007 }
8008 }
8009 *wtm = workingtm;
8010 return true;
8011 }
8012
8013 if (mday < wtm->tm_mday) {
8014 return false;
8015 }
8016
8017 if (mday > wtm->tm_mday) {
8018 wtm->tm_mday = mday;
8019 wtm->tm_hour = 0;
8020 wtm->tm_min = 0;
8021 }
8022
8023 return cronemu_hour(wtm, hour, min);
8024 }
8025
8026 bool
cronemu_hour(struct tm * wtm,int hour,int min)8027 cronemu_hour(struct tm *wtm, int hour, int min)
8028 {
8029 if (hour == -1) {
8030 struct tm workingtm = *wtm;
8031 int carrytest;
8032
8033 while (!cronemu_min(&workingtm, min)) {
8034 workingtm.tm_hour++;
8035 workingtm.tm_min = 0;
8036 carrytest = workingtm.tm_hour;
8037 mktime(&workingtm);
8038 if (carrytest != workingtm.tm_hour) {
8039 return false;
8040 }
8041 }
8042 *wtm = workingtm;
8043 return true;
8044 }
8045
8046 if (hour < wtm->tm_hour) {
8047 return false;
8048 }
8049
8050 if (hour > wtm->tm_hour) {
8051 wtm->tm_hour = hour;
8052 wtm->tm_min = 0;
8053 }
8054
8055 return cronemu_min(wtm, min);
8056 }
8057
8058 bool
cronemu_min(struct tm * wtm,int min)8059 cronemu_min(struct tm *wtm, int min)
8060 {
8061 if (min == -1) {
8062 return true;
8063 }
8064
8065 if (min < wtm->tm_min) {
8066 return false;
8067 }
8068
8069 if (min > wtm->tm_min) {
8070 wtm->tm_min = min;
8071 }
8072
8073 return true;
8074 }
8075
8076 kern_return_t
job_mig_create_server(job_t j,cmd_t server_cmd,uid_t server_uid,boolean_t on_demand,mach_port_t * server_portp)8077 job_mig_create_server(job_t j, cmd_t server_cmd, uid_t server_uid, boolean_t on_demand, mach_port_t *server_portp)
8078 {
8079 struct ldcred *ldc = runtime_get_caller_creds();
8080 job_t js;
8081
8082 if (!j) {
8083 RETURN_NO_MEMORY();
8084 }
8085
8086 if (unlikely(j->deny_job_creation)) {
8087 return BOOTSTRAP_NOT_PRIVILEGED;
8088 }
8089
8090 #if HAVE_SANDBOX
8091 const char **argv = (const char **)mach_cmd2argv(server_cmd);
8092 if (unlikely(argv == NULL)) {
8093 RETURN_NO_MEMORY();
8094 }
8095 if (unlikely(sandbox_check(ldc->pid, "job-creation", SANDBOX_FILTER_PATH, argv[0]) > 0)) {
8096 free(argv);
8097 return BOOTSTRAP_NOT_PRIVILEGED;
8098 }
8099 free(argv);
8100 #endif
8101
8102 job_log(j, LOG_DEBUG, "Server create attempt: %s", server_cmd);
8103
8104 if (pid1_magic) {
8105 if (ldc->euid || ldc->uid) {
8106 job_log(j, LOG_WARNING, "Server create attempt moved to per-user launchd: %s", server_cmd);
8107 return VPROC_ERR_TRY_PER_USER;
8108 }
8109 } else {
8110 if (unlikely(server_uid != getuid())) {
8111 job_log(j, LOG_WARNING, "Server create: \"%s\": As UID %d, we will not be able to switch to UID %d",
8112 server_cmd, getuid(), server_uid);
8113 }
8114 server_uid = 0; // zero means "do nothing"
8115 }
8116
8117 js = job_new_via_mach_init(j, server_cmd, server_uid, on_demand);
8118
8119 if (unlikely(js == NULL)) {
8120 RETURN_NO_MEMORY();
8121 }
8122
8123 *server_portp = js->j_port;
8124 return BOOTSTRAP_SUCCESS;
8125 }
8126
8127 kern_return_t
job_mig_send_signal(job_t j,mach_port_t srp,name_t targetlabel,int sig)8128 job_mig_send_signal(job_t j, mach_port_t srp, name_t targetlabel, int sig)
8129 {
8130 struct ldcred *ldc = runtime_get_caller_creds();
8131 job_t otherj;
8132
8133 if (!j) {
8134 RETURN_NO_MEMORY();
8135 }
8136
8137 if (unlikely(ldc->euid != 0 && ldc->euid != getuid()) || j->deny_job_creation) {
8138 #if TARGET_OS_EMBEDDED
8139 if (!j->embedded_god) {
8140 return BOOTSTRAP_NOT_PRIVILEGED;
8141 }
8142 #else
8143 return BOOTSTRAP_NOT_PRIVILEGED;
8144 #endif
8145 }
8146
8147 #if HAVE_SANDBOX
8148 if (unlikely(sandbox_check(ldc->pid, "job-creation", SANDBOX_FILTER_NONE) > 0)) {
8149 return BOOTSTRAP_NOT_PRIVILEGED;
8150 }
8151 #endif
8152
8153 if (unlikely(!(otherj = job_find(NULL, targetlabel)))) {
8154 return BOOTSTRAP_UNKNOWN_SERVICE;
8155 }
8156
8157 #if TARGET_OS_EMBEDDED
8158 if (j->embedded_god) {
8159 if (j->username && otherj->username) {
8160 if (strcmp(j->username, otherj->username) != 0) {
8161 return BOOTSTRAP_NOT_PRIVILEGED;
8162 }
8163 } else {
8164 return BOOTSTRAP_NOT_PRIVILEGED;
8165 }
8166 }
8167 #endif
8168
8169 if (sig == VPROC_MAGIC_UNLOAD_SIGNAL) {
8170 bool do_block = otherj->p;
8171
8172 if (otherj->anonymous) {
8173 return BOOTSTRAP_NOT_PRIVILEGED;
8174 }
8175
8176 job_remove(otherj);
8177
8178 if (do_block) {
8179 job_log(j, LOG_DEBUG, "Blocking MIG return of job_remove(): %s", otherj->label);
8180 // this is messy. We shouldn't access 'otherj' after job_remove(), but we check otherj->p first...
8181 (void)job_assumes(otherj, waiting4removal_new(otherj, srp));
8182 return MIG_NO_REPLY;
8183 } else {
8184 return 0;
8185 }
8186 } else if (otherj->p) {
8187 (void)job_assumes_zero_p(j, kill2(otherj->p, sig));
8188 }
8189
8190 return 0;
8191 }
8192
8193 kern_return_t
job_mig_log_forward(job_t j,vm_offset_t inval,mach_msg_type_number_t invalCnt)8194 job_mig_log_forward(job_t j, vm_offset_t inval, mach_msg_type_number_t invalCnt)
8195 {
8196 struct ldcred *ldc = runtime_get_caller_creds();
8197
8198 if (!j) {
8199 RETURN_NO_MEMORY();
8200 }
8201
8202 if (!job_assumes(j, j->per_user)) {
8203 return BOOTSTRAP_NOT_PRIVILEGED;
8204 }
8205
8206 return launchd_log_forward(ldc->euid, ldc->egid, inval, invalCnt);
8207 }
8208
8209 kern_return_t
job_mig_log_drain(job_t j,mach_port_t srp,vm_offset_t * outval,mach_msg_type_number_t * outvalCnt)8210 job_mig_log_drain(job_t j, mach_port_t srp, vm_offset_t *outval, mach_msg_type_number_t *outvalCnt)
8211 {
8212 struct ldcred *ldc = runtime_get_caller_creds();
8213
8214 if (!j) {
8215 RETURN_NO_MEMORY();
8216 }
8217
8218 if (unlikely(ldc->euid)) {
8219 return BOOTSTRAP_NOT_PRIVILEGED;
8220 }
8221
8222 return launchd_log_drain(srp, outval, outvalCnt);
8223 }
8224
8225 kern_return_t
job_mig_swap_complex(job_t j,vproc_gsk_t inkey,vproc_gsk_t outkey,vm_offset_t inval,mach_msg_type_number_t invalCnt,vm_offset_t * outval,mach_msg_type_number_t * outvalCnt)8226 job_mig_swap_complex(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey,
8227 vm_offset_t inval, mach_msg_type_number_t invalCnt, vm_offset_t *outval,
8228 mach_msg_type_number_t *outvalCnt)
8229 {
8230 const char *action;
8231 launch_data_t input_obj = NULL, output_obj = NULL;
8232 size_t data_offset = 0;
8233 size_t packed_size;
8234 struct ldcred *ldc = runtime_get_caller_creds();
8235
8236 if (!j) {
8237 RETURN_NO_MEMORY();
8238 }
8239
8240 if (inkey && ldc->pid != j->p) {
8241 if (ldc->euid && ldc->euid != getuid()) {
8242 return BOOTSTRAP_NOT_PRIVILEGED;
8243 }
8244 }
8245
8246 if (unlikely(inkey && outkey && !job_assumes(j, inkey == outkey))) {
8247 return 1;
8248 }
8249
8250 if (inkey && outkey) {
8251 action = "Swapping";
8252 } else if (inkey) {
8253 action = "Setting";
8254 } else {
8255 action = "Getting";
8256 }
8257
8258 job_log(j, LOG_DEBUG, "%s key: %u", action, inkey ? inkey : outkey);
8259
8260 *outvalCnt = 20 * 1024 * 1024;
8261 mig_allocate(outval, *outvalCnt);
8262 if (!job_assumes(j, *outval != 0)) {
8263 return 1;
8264 }
8265
8266 /* Note to future maintainers: launch_data_unpack() does NOT return a heap
8267 * object. The data is decoded in-place. So do not call launch_data_free()
8268 * on input_obj.
8269 */
8270 runtime_ktrace0(RTKT_LAUNCHD_DATA_UNPACK);
8271 if (unlikely(invalCnt && !job_assumes(j, (input_obj = launch_data_unpack((void *)inval, invalCnt, NULL, 0, &data_offset, NULL)) != NULL))) {
8272 goto out_bad;
8273 }
8274
8275 char *store = NULL;
8276 switch (outkey) {
8277 case VPROC_GSK_ENVIRONMENT:
8278 if (!job_assumes(j, (output_obj = launch_data_alloc(LAUNCH_DATA_DICTIONARY)))) {
8279 goto out_bad;
8280 }
8281 jobmgr_export_env_from_other_jobs(j->mgr, output_obj);
8282 runtime_ktrace0(RTKT_LAUNCHD_DATA_PACK);
8283 if (!job_assumes(j, launch_data_pack(output_obj, (void *)*outval, *outvalCnt, NULL, NULL) != 0)) {
8284 goto out_bad;
8285 }
8286 launch_data_free(output_obj);
8287 break;
8288 case VPROC_GSK_ALLJOBS:
8289 if (!job_assumes(j, (output_obj = job_export_all()) != NULL)) {
8290 goto out_bad;
8291 }
8292 ipc_revoke_fds(output_obj);
8293 runtime_ktrace0(RTKT_LAUNCHD_DATA_PACK);
8294 packed_size = launch_data_pack(output_obj, (void *)*outval, *outvalCnt, NULL, NULL);
8295 if (!job_assumes(j, packed_size != 0)) {
8296 goto out_bad;
8297 }
8298 launch_data_free(output_obj);
8299 break;
8300 case VPROC_GSK_MGR_NAME:
8301 if (!job_assumes(j, (output_obj = launch_data_new_string(j->mgr->name)) != NULL)) {
8302 goto out_bad;
8303 }
8304 packed_size = launch_data_pack(output_obj, (void *)*outval, *outvalCnt, NULL, NULL);
8305 if (!job_assumes(j, packed_size != 0)) {
8306 goto out_bad;
8307 }
8308
8309 launch_data_free(output_obj);
8310 break;
8311 case VPROC_GSK_JOB_OVERRIDES_DB:
8312 store = launchd_copy_persistent_store(LAUNCHD_PERSISTENT_STORE_DB, "overrides.plist");
8313 if (!store || !job_assumes(j, (output_obj = launch_data_new_string(store)) != NULL)) {
8314 free(store);
8315 goto out_bad;
8316 }
8317
8318 free(store);
8319 packed_size = launch_data_pack(output_obj, (void *)*outval, *outvalCnt, NULL, NULL);
8320 if (!job_assumes(j, packed_size != 0)) {
8321 goto out_bad;
8322 }
8323
8324 launch_data_free(output_obj);
8325 break;
8326 case VPROC_GSK_ZERO:
8327 mig_deallocate(*outval, *outvalCnt);
8328 *outval = 0;
8329 *outvalCnt = 0;
8330 break;
8331 default:
8332 goto out_bad;
8333 }
8334
8335 mig_deallocate(inval, invalCnt);
8336 return 0;
8337
8338 out_bad:
8339 mig_deallocate(inval, invalCnt);
8340 if (*outval) {
8341 mig_deallocate(*outval, *outvalCnt);
8342 }
8343 if (output_obj) {
8344 launch_data_free(output_obj);
8345 }
8346
8347 return 1;
8348 }
8349
8350 kern_return_t
job_mig_swap_integer(job_t j,vproc_gsk_t inkey,vproc_gsk_t outkey,int64_t inval,int64_t * outval)8351 job_mig_swap_integer(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, int64_t inval, int64_t *outval)
8352 {
8353 const char *action;
8354 kern_return_t kr = 0;
8355 struct ldcred *ldc = runtime_get_caller_creds();
8356 int oldmask;
8357
8358 if (!j) {
8359 RETURN_NO_MEMORY();
8360 }
8361
8362 if (inkey && ldc->pid != j->p) {
8363 if (ldc->euid && ldc->euid != getuid()) {
8364 return BOOTSTRAP_NOT_PRIVILEGED;
8365 }
8366 }
8367
8368 if (unlikely(inkey && outkey && !job_assumes(j, inkey == outkey))) {
8369 return 1;
8370 }
8371
8372 if (inkey && outkey) {
8373 action = "Swapping";
8374 } else if (inkey) {
8375 action = "Setting";
8376 } else {
8377 action = "Getting";
8378 }
8379
8380 job_log(j, LOG_DEBUG, "%s key: %u", action, inkey ? inkey : outkey);
8381
8382 switch (outkey) {
8383 case VPROC_GSK_ABANDON_PROCESS_GROUP:
8384 *outval = j->abandon_pg;
8385 break;
8386 case VPROC_GSK_LAST_EXIT_STATUS:
8387 *outval = j->last_exit_status;
8388 break;
8389 case VPROC_GSK_MGR_UID:
8390 *outval = getuid();
8391 break;
8392 case VPROC_GSK_MGR_PID:
8393 *outval = getpid();
8394 break;
8395 case VPROC_GSK_IS_MANAGED:
8396 *outval = j->anonymous ? 0 : 1;
8397 break;
8398 case VPROC_GSK_BASIC_KEEPALIVE:
8399 *outval = !j->ondemand;
8400 break;
8401 case VPROC_GSK_START_INTERVAL:
8402 *outval = j->start_interval;
8403 break;
8404 case VPROC_GSK_IDLE_TIMEOUT:
8405 *outval = j->timeout;
8406 break;
8407 case VPROC_GSK_EXIT_TIMEOUT:
8408 *outval = j->exit_timeout;
8409 break;
8410 case VPROC_GSK_GLOBAL_LOG_MASK:
8411 oldmask = runtime_setlogmask(LOG_UPTO(LOG_DEBUG));
8412 *outval = oldmask;
8413 runtime_setlogmask(oldmask);
8414 break;
8415 case VPROC_GSK_GLOBAL_UMASK:
8416 oldmask = umask(0);
8417 *outval = oldmask;
8418 umask(oldmask);
8419 break;
8420 case VPROC_GSK_TRANSACTIONS_ENABLED:
8421 job_log(j, LOG_DEBUG, "Reading EnableTransactions value.");
8422 *outval = j->enable_transactions;
8423 break;
8424 case VPROC_GSK_WAITFORDEBUGGER:
8425 *outval = j->wait4debugger;
8426 break;
8427 case VPROC_GSK_EMBEDDEDROOTEQUIVALENT:
8428 *outval = j->embedded_god;
8429 break;
8430 case VPROC_GSK_ZERO:
8431 *outval = 0;
8432 break;
8433 default:
8434 kr = 1;
8435 break;
8436 }
8437
8438 switch (inkey) {
8439 case VPROC_GSK_ABANDON_PROCESS_GROUP:
8440 j->abandon_pg = (bool)inval;
8441 break;
8442 case VPROC_GSK_GLOBAL_ON_DEMAND:
8443 job_log(j, LOG_DEBUG, "Job has set global on-demand mode to: %s", inval ? "true" : "false");
8444 kr = job_set_global_on_demand(j, inval);
8445 break;
8446 case VPROC_GSK_BASIC_KEEPALIVE:
8447 j->ondemand = !inval;
8448 break;
8449 case VPROC_GSK_START_INTERVAL:
8450 if (inval > UINT32_MAX || inval < 0) {
8451 kr = 1;
8452 } else if (inval) {
8453 if (j->start_interval == 0) {
8454 runtime_add_weak_ref();
8455 }
8456 j->start_interval = (typeof(j->start_interval)) inval;
8457 (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, j->start_interval, j));
8458 } else if (j->start_interval) {
8459 (void)job_assumes_zero_p(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL));
8460 if (j->start_interval != 0) {
8461 runtime_del_weak_ref();
8462 }
8463 j->start_interval = 0;
8464 }
8465 break;
8466 case VPROC_GSK_IDLE_TIMEOUT:
8467 if (inval < 0 || inval > UINT32_MAX) {
8468 kr = 1;
8469 } else {
8470 j->timeout = (typeof(j->timeout)) inval;
8471 }
8472 break;
8473 case VPROC_GSK_EXIT_TIMEOUT:
8474 if (inval < 0 || inval > UINT32_MAX) {
8475 kr = 1;
8476 } else {
8477 j->exit_timeout = (typeof(j->exit_timeout)) inval;
8478 }
8479 break;
8480 case VPROC_GSK_GLOBAL_LOG_MASK:
8481 if (inval < 0 || inval > UINT32_MAX) {
8482 kr = 1;
8483 } else {
8484 runtime_setlogmask((int) inval);
8485 }
8486 break;
8487 case VPROC_GSK_GLOBAL_UMASK:
8488 __OS_COMPILETIME_ASSERT__(sizeof (mode_t) == 2);
8489 if (inval < 0 || inval > UINT16_MAX) {
8490 kr = 1;
8491 } else {
8492 #if HAVE_SANDBOX
8493 if (unlikely(sandbox_check(ldc->pid, "job-creation", SANDBOX_FILTER_NONE) > 0)) {
8494 kr = 1;
8495 } else {
8496 umask((mode_t) inval);
8497 }
8498 #endif
8499 }
8500 break;
8501 case VPROC_GSK_TRANSACTIONS_ENABLED:
8502 /* No-op. */
8503 break;
8504 case VPROC_GSK_WEIRD_BOOTSTRAP:
8505 if (job_assumes(j, j->weird_bootstrap)) {
8506 job_log(j, LOG_DEBUG, "Unsetting weird bootstrap.");
8507
8508 mach_msg_size_t mxmsgsz = (typeof(mxmsgsz)) sizeof(union __RequestUnion__job_mig_job_subsystem);
8509
8510 if (job_mig_job_subsystem.maxsize > mxmsgsz) {
8511 mxmsgsz = job_mig_job_subsystem.maxsize;
8512 }
8513
8514 (void)job_assumes_zero(j, runtime_add_mport(j->mgr->jm_port, job_server));
8515 j->weird_bootstrap = false;
8516 }
8517 break;
8518 case VPROC_GSK_WAITFORDEBUGGER:
8519 j->wait4debugger_oneshot = inval;
8520 break;
8521 case VPROC_GSK_PERUSER_SUSPEND:
8522 if (job_assumes(j, pid1_magic && ldc->euid == 0)) {
8523 mach_port_t junk = MACH_PORT_NULL;
8524 job_t jpu = jobmgr_lookup_per_user_context_internal(j, (uid_t)inval, &junk);
8525 if (job_assumes(j, jpu != NULL)) {
8526 struct suspended_peruser *spi = NULL;
8527 LIST_FOREACH(spi, &j->suspended_perusers, sle) {
8528 if ((int64_t)(spi->j->mach_uid) == inval) {
8529 job_log(j, LOG_WARNING, "Job tried to suspend per-user launchd for UID %zi twice.", inval);
8530 break;
8531 }
8532 }
8533
8534 if (spi == NULL) {
8535 job_log(j, LOG_INFO, "Job is suspending the per-user launchd for UID %zi.", inval);
8536 spi = (struct suspended_peruser *)calloc(sizeof(struct suspended_peruser), 1);
8537 if (job_assumes(j, spi != NULL)) {
8538 /* Stop listening for events.
8539 *
8540 * See <rdar://problem/9014146>.
8541 */
8542 if (jpu->peruser_suspend_count == 0) {
8543 job_ignore(jpu);
8544 }
8545
8546 spi->j = jpu;
8547 spi->j->peruser_suspend_count++;
8548 LIST_INSERT_HEAD(&j->suspended_perusers, spi, sle);
8549 job_stop(spi->j);
8550 *outval = jpu->p;
8551 } else {
8552 kr = BOOTSTRAP_NO_MEMORY;
8553 }
8554 }
8555 }
8556 } else {
8557 kr = 1;
8558 }
8559 break;
8560 case VPROC_GSK_PERUSER_RESUME:
8561 if (job_assumes(j, pid1_magic == true)) {
8562 struct suspended_peruser *spi = NULL, *spt = NULL;
8563 LIST_FOREACH_SAFE(spi, &j->suspended_perusers, sle, spt) {
8564 if ((int64_t)(spi->j->mach_uid) == inval) {
8565 spi->j->peruser_suspend_count--;
8566 LIST_REMOVE(spi, sle);
8567 job_log(j, LOG_INFO, "Job is resuming the per-user launchd for UID %zi.", inval);
8568 break;
8569 }
8570 }
8571
8572 if (!job_assumes(j, spi != NULL)) {
8573 job_log(j, LOG_WARNING, "Job tried to resume per-user launchd for UID %zi that it did not suspend.", inval);
8574 kr = BOOTSTRAP_NOT_PRIVILEGED;
8575 } else if (spi->j->peruser_suspend_count == 0) {
8576 job_watch(spi->j);
8577 job_dispatch(spi->j, false);
8578 free(spi);
8579 }
8580 } else {
8581 kr = 1;
8582 }
8583 break;
8584 case VPROC_GSK_ZERO:
8585 break;
8586 default:
8587 kr = 1;
8588 break;
8589 }
8590
8591 return kr;
8592 }
8593
8594 kern_return_t
job_mig_post_fork_ping(job_t j,task_t child_task,mach_port_t * asport)8595 job_mig_post_fork_ping(job_t j, task_t child_task, mach_port_t *asport)
8596 {
8597 if (!j) {
8598 RETURN_NO_MEMORY();
8599 }
8600
8601 job_log(j, LOG_DEBUG, "Post fork ping.");
8602
8603 struct machservice *ms;
8604 job_setup_exception_port(j, child_task);
8605 SLIST_FOREACH(ms, &special_ports, special_port_sle) {
8606 if (j->per_user && (ms->special_port_num != TASK_ACCESS_PORT)) {
8607 // The TASK_ACCESS_PORT funny business is to workaround 5325399.
8608 continue;
8609 }
8610
8611 errno = task_set_special_port(child_task, ms->special_port_num, ms->port);
8612 if (errno) {
8613 if (errno == MACH_SEND_INVALID_DEST) {
8614 job_log(j, LOG_WARNING, "Task died before special ports could be set.");
8615 break;
8616 }
8617
8618 int desired_log_level = LOG_ERR;
8619 if (j->anonymous) {
8620 // 5338127
8621
8622 desired_log_level = LOG_WARNING;
8623
8624 if (ms->special_port_num == TASK_SEATBELT_PORT) {
8625 desired_log_level = LOG_DEBUG;
8626 }
8627 }
8628
8629 job_log(j, desired_log_level, "Could not setup Mach task special port %u: %s", ms->special_port_num, mach_error_string(errno));
8630 }
8631 }
8632
8633 /* MIG will not zero-initialize this pointer, so we must always do so.
8634 *
8635 * <rdar://problem/8562593>.
8636 */
8637 *asport = MACH_PORT_NULL;
8638 #if !TARGET_OS_EMBEDDED
8639 if (!j->anonymous) {
8640 /* XPC services will spawn into the root security session by default.
8641 * xpcproxy will switch them away if needed.
8642 */
8643 if (!(j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) {
8644 job_log(j, LOG_DEBUG, "Returning session port: 0x%x", j->asport);
8645 *asport = j->asport;
8646 }
8647 }
8648 #endif
8649 (void)job_assumes_zero(j, launchd_mport_deallocate(child_task));
8650
8651 return 0;
8652 }
8653
8654 kern_return_t
job_mig_get_listener_port_rights(job_t j,mach_port_array_t * sports,mach_msg_type_number_t * sports_cnt)8655 job_mig_get_listener_port_rights(job_t j, mach_port_array_t *sports, mach_msg_type_number_t *sports_cnt)
8656 {
8657 if (!j) {
8658 RETURN_NO_MEMORY();
8659 }
8660
8661 size_t cnt = 0;
8662 struct machservice *msi = NULL;
8663 SLIST_FOREACH(msi, &j->machservices, sle) {
8664 if (msi->upfront && job_assumes(j, msi->recv)) {
8665 cnt++;
8666 }
8667 }
8668
8669 if (cnt == 0) {
8670 return BOOTSTRAP_UNKNOWN_SERVICE;
8671 }
8672
8673 mach_port_array_t sports2 = NULL;
8674 mig_allocate((vm_address_t *)&sports2, cnt * sizeof(sports2[0]));
8675 if (!sports2) {
8676 RETURN_NO_MEMORY();
8677 }
8678
8679 size_t i = 0;
8680 SLIST_FOREACH(msi, &j->machservices, sle) {
8681 if (msi->upfront && msi->recv) {
8682 sports2[i] = msi->port;
8683 i++;
8684 }
8685 }
8686
8687 *sports = sports2;
8688 *sports_cnt = cnt;
8689
8690 return KERN_SUCCESS;
8691 }
8692
8693 kern_return_t
job_mig_register_gui_session(job_t j,mach_port_t asport)8694 job_mig_register_gui_session(job_t j, mach_port_t asport)
8695 {
8696 if (!j->per_user) {
8697 return BOOTSTRAP_NOT_PRIVILEGED;
8698 }
8699
8700 jobmgr_t jm = jobmgr_find_xpc_per_user_domain(root_jobmgr, j->mach_uid);
8701 if (!jm) {
8702 return BOOTSTRAP_UNKNOWN_SERVICE;
8703 }
8704
8705 if (jm->req_gui_asport) {
8706 // This job manager persists, so we need to allow the per-user launchd
8707 // to update the GUI session as it comes and goes.
8708 jobmgr_assumes_zero(jm, launchd_mport_deallocate(jm->req_gui_asport));
8709 }
8710
8711 jm->req_gui_asport = asport;
8712 return KERN_SUCCESS;
8713 }
8714
8715 kern_return_t
job_mig_reboot2(job_t j,uint64_t flags)8716 job_mig_reboot2(job_t j, uint64_t flags)
8717 {
8718 char who_started_the_reboot[2048] = "";
8719 struct proc_bsdshortinfo proc;
8720 struct ldcred *ldc = runtime_get_caller_creds();
8721 pid_t pid_to_log;
8722
8723 if (!j) {
8724 RETURN_NO_MEMORY();
8725 }
8726
8727 if (unlikely(!pid1_magic)) {
8728 return BOOTSTRAP_NOT_PRIVILEGED;
8729 }
8730
8731 #if !TARGET_OS_EMBEDDED
8732 if (unlikely(ldc->euid)) {
8733 #else
8734 if (unlikely(ldc->euid) && !j->embedded_god) {
8735 #endif
8736 return BOOTSTRAP_NOT_PRIVILEGED;
8737 }
8738
8739 for (pid_to_log = ldc->pid; pid_to_log; pid_to_log = proc.pbsi_ppid) {
8740 size_t who_offset;
8741 if (proc_pidinfo(pid_to_log, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) {
8742 if (errno != ESRCH) {
8743 (void)job_assumes_zero(j, errno);
8744 }
8745 return 1;
8746 }
8747
8748 if (!job_assumes(j, pid_to_log != (pid_t)proc.pbsi_ppid)) {
8749 job_log(j, LOG_WARNING, "Job which is its own parent started reboot.");
8750 snprintf(who_started_the_reboot, sizeof(who_started_the_reboot), "%s[%u]->%s[%u]->%s[%u]->...", proc.pbsi_comm, pid_to_log, proc.pbsi_comm, pid_to_log, proc.pbsi_comm, pid_to_log);
8751 break;
8752 }
8753
8754 who_offset = strlen(who_started_the_reboot);
8755 snprintf(who_started_the_reboot + who_offset, sizeof(who_started_the_reboot) - who_offset,
8756 " %s[%u]%s", proc.pbsi_comm, pid_to_log, proc.pbsi_ppid ? " ->" : "");
8757 }
8758
8759 root_jobmgr->reboot_flags = (int)flags;
8760
8761 job_log(j, LOG_DEBUG, "reboot2() initiated by:%s", who_started_the_reboot);
8762 launchd_shutdown();
8763
8764 return 0;
8765 }
8766
8767 kern_return_t
8768 job_mig_getsocket(job_t j, name_t spr)
8769 {
8770 if (!j) {
8771 RETURN_NO_MEMORY();
8772 }
8773
8774 if (j->deny_job_creation) {
8775 return BOOTSTRAP_NOT_PRIVILEGED;
8776 }
8777
8778 #if HAVE_SANDBOX
8779 struct ldcred *ldc = runtime_get_caller_creds();
8780 if (unlikely(sandbox_check(ldc->pid, "job-creation", SANDBOX_FILTER_NONE) > 0)) {
8781 return BOOTSTRAP_NOT_PRIVILEGED;
8782 }
8783 #endif
8784
8785 ipc_server_init();
8786
8787 if (unlikely(!sockpath)) {
8788 RETURN_NO_MEMORY();
8789 }
8790
8791 strncpy(spr, sockpath, sizeof(name_t));
8792
8793 return BOOTSTRAP_SUCCESS;
8794 }
8795
8796 kern_return_t
8797 job_mig_log(job_t j, int pri, int err, logmsg_t msg)
8798 {
8799 if (!j) {
8800 RETURN_NO_MEMORY();
8801 }
8802
8803 if ((errno = err)) {
8804 job_log_error(j, pri, "%s", msg);
8805 } else {
8806 job_log(j, pri, "%s", msg);
8807 }
8808
8809 return 0;
8810 }
8811
8812 void
8813 job_setup_per_user_directory(job_t j, uid_t uid, const char *path)
8814 {
8815 struct stat sb;
8816
8817 bool created = false;
8818 int r = stat(path, &sb);
8819 if ((r == -1 && errno == ENOENT) || (r == 0 && !S_ISDIR(sb.st_mode))) {
8820 if (r == 0) {
8821 job_log(j, LOG_NOTICE, "File at location of per-user launchd directory is not a directory. Moving aside: %s", path);
8822
8823 char old[PATH_MAX];
8824 snprintf(old, sizeof(old), "%s.movedaside", path);
8825 (void)job_assumes_zero_p(j, rename(path, old));
8826 }
8827
8828 (void)job_assumes_zero_p(j, mkdir(path, S_IRWXU));
8829 (void)job_assumes_zero_p(j, chown(path, uid, 0));
8830 created = true;
8831 }
8832
8833 if (!created) {
8834 if (sb.st_uid != uid) {
8835 job_log(j, LOG_NOTICE, "Per-user launchd directory has improper user ownership. Repairing: %s", path);
8836 (void)job_assumes_zero_p(j, chown(path, uid, 0));
8837 }
8838 if (sb.st_gid != 0) {
8839 job_log(j, LOG_NOTICE, "Per-user launchd directory has improper group ownership. Repairing: %s", path);
8840 (void)job_assumes_zero_p(j, chown(path, uid, 0));
8841 }
8842 if (sb.st_mode != (S_IRWXU | S_IFDIR)) {
8843 job_log(j, LOG_NOTICE, "Per-user launchd directory has improper mode. Repairing: %s", path);
8844 (void)job_assumes_zero_p(j, chmod(path, S_IRWXU));
8845 }
8846 }
8847 }
8848
8849 void
8850 job_setup_per_user_directories(job_t j, uid_t uid, const char *label)
8851 {
8852 char path[PATH_MAX];
8853
8854 (void)snprintf(path, sizeof(path), LAUNCHD_DB_PREFIX "/%s", label);
8855 job_setup_per_user_directory(j, uid, path);
8856
8857 (void)snprintf(path, sizeof(path), LAUNCHD_LOG_PREFIX "/%s", label);
8858 job_setup_per_user_directory(j, uid, path);
8859 }
8860
8861 job_t
8862 jobmgr_lookup_per_user_context_internal(job_t j, uid_t which_user, mach_port_t *mp)
8863 {
8864 job_t ji = NULL;
8865 LIST_FOREACH(ji, &root_jobmgr->jobs, sle) {
8866 if (!ji->per_user) {
8867 continue;
8868 }
8869 if (ji->mach_uid != which_user) {
8870 continue;
8871 }
8872 if (SLIST_EMPTY(&ji->machservices)) {
8873 continue;
8874 }
8875 if (!SLIST_FIRST(&ji->machservices)->per_user_hack) {
8876 continue;
8877 }
8878 break;
8879 }
8880
8881 if (unlikely(ji == NULL)) {
8882 struct machservice *ms;
8883 char lbuf[1024];
8884
8885 job_log(j, LOG_DEBUG, "Creating per user launchd job for UID: %u", which_user);
8886
8887 sprintf(lbuf, "com.apple.launchd.peruser.%u", which_user);
8888
8889 ji = job_new(root_jobmgr, lbuf, "/sbin/launchd", NULL);
8890
8891 if (ji != NULL) {
8892 auditinfo_addr_t auinfo = {
8893 .ai_termid = {
8894 .at_type = AU_IPv4
8895 },
8896 .ai_auid = which_user,
8897 .ai_asid = AU_ASSIGN_ASID,
8898 };
8899
8900 if (setaudit_addr(&auinfo, sizeof(auinfo)) == 0) {
8901 job_log(ji, LOG_DEBUG, "Created new security session for per-user launchd: %u", auinfo.ai_asid);
8902 #ifdef notyet
8903 (void)job_assumes(ji, (ji->asport = audit_session_self()) != MACH_PORT_NULL);
8904
8905 /* Kinda lame that we have to do this, but we can't create an
8906 * audit session without joining it.
8907 */
8908 (void)job_assumes(ji, audit_session_join(launchd_audit_port));
8909 #endif
8910 ji->asid = auinfo.ai_asid;
8911 } else {
8912 job_log(ji, LOG_WARNING, "Could not set audit session!");
8913 job_remove(ji);
8914 return NULL;
8915 }
8916
8917 ji->mach_uid = which_user;
8918 ji->per_user = true;
8919 ji->enable_transactions = true;
8920 job_setup_per_user_directories(ji, which_user, lbuf);
8921
8922 if ((ms = machservice_new(ji, lbuf, mp, false)) == NULL) {
8923 job_remove(ji);
8924 ji = NULL;
8925 } else {
8926 ms->upfront = true;
8927 ms->per_user_hack = true;
8928 ms->hide = true;
8929
8930 ji = job_dispatch(ji, false);
8931 }
8932 }
8933 } else {
8934 *mp = machservice_port(SLIST_FIRST(&ji->machservices));
8935 job_log(j, LOG_DEBUG, "Per user launchd job found for UID: %u", which_user);
8936 }
8937
8938 return ji;
8939 }
8940
8941 kern_return_t
8942 job_mig_lookup_per_user_context(job_t j, uid_t which_user, mach_port_t *up_cont)
8943 {
8944 struct ldcred *ldc = runtime_get_caller_creds();
8945 job_t jpu;
8946
8947 if (!j) {
8948 RETURN_NO_MEMORY();
8949 }
8950
8951 if (launchd_osinstaller) {
8952 return BOOTSTRAP_UNKNOWN_SERVICE;
8953 }
8954
8955 #if TARGET_OS_EMBEDDED
8956 // There is no need for per-user launchd's on embedded.
8957 job_log(j, LOG_ERR, "Per-user launchds are not supported on this platform.");
8958 return BOOTSTRAP_UNKNOWN_SERVICE;
8959 #endif
8960
8961 #if HAVE_SANDBOX
8962 if (unlikely(sandbox_check(ldc->pid, "mach-per-user-lookup", SANDBOX_FILTER_NONE) > 0)) {
8963 return BOOTSTRAP_NOT_PRIVILEGED;
8964 }
8965 #endif
8966
8967 job_log(j, LOG_INFO, "Looking up per user launchd for UID: %u", which_user);
8968
8969 if (unlikely(!pid1_magic)) {
8970 job_log(j, LOG_ERR, "Only PID 1 supports per user launchd lookups.");
8971 return BOOTSTRAP_NOT_PRIVILEGED;
8972 }
8973
8974 if (ldc->euid || ldc->uid) {
8975 which_user = ldc->euid ?: ldc->uid;
8976 }
8977
8978 *up_cont = MACH_PORT_NULL;
8979
8980 jpu = jobmgr_lookup_per_user_context_internal(j, which_user, up_cont);
8981
8982 return 0;
8983 }
8984
8985 kern_return_t
8986 job_mig_check_in2(job_t j, name_t servicename, mach_port_t *serviceportp, uuid_t instance_id, uint64_t flags)
8987 {
8988 bool per_pid_service = flags & BOOTSTRAP_PER_PID_SERVICE;
8989 bool strict = flags & BOOTSTRAP_STRICT_CHECKIN;
8990 struct ldcred *ldc = runtime_get_caller_creds();
8991 struct machservice *ms = NULL;
8992 job_t jo;
8993
8994 if (!j) {
8995 RETURN_NO_MEMORY();
8996 }
8997
8998 if (j->dedicated_instance) {
8999 struct machservice *msi = NULL;
9000 SLIST_FOREACH(msi, &j->machservices, sle) {
9001 if (strncmp(servicename, msi->name, sizeof(name_t) - 1) == 0) {
9002 uuid_copy(instance_id, j->instance_id);
9003 ms = msi;
9004 break;
9005 }
9006 }
9007 } else {
9008 ms = jobmgr_lookup_service(j->mgr, servicename, false, per_pid_service ? ldc->pid : 0);
9009 }
9010
9011 if (strict) {
9012 if (likely(ms != NULL)) {
9013 if (ms->job != j) {
9014 return BOOTSTRAP_NOT_PRIVILEGED;
9015 } else if (ms->isActive) {
9016 return BOOTSTRAP_SERVICE_ACTIVE;
9017 }
9018 } else {
9019 return BOOTSTRAP_UNKNOWN_SERVICE;
9020 }
9021 } else if (ms == NULL) {
9022 if (job_assumes(j, !j->dedicated_instance)) {
9023 *serviceportp = MACH_PORT_NULL;
9024
9025 #if HAVE_SANDBOX
9026 if (unlikely(sandbox_check(ldc->pid, "mach-register", per_pid_service ? SANDBOX_FILTER_LOCAL_NAME : SANDBOX_FILTER_GLOBAL_NAME, servicename) > 0)) {
9027 return BOOTSTRAP_NOT_PRIVILEGED;
9028 }
9029 #endif
9030 if (unlikely((ms = machservice_new(j, servicename, serviceportp, per_pid_service)) == NULL)) {
9031 RETURN_NO_MEMORY();
9032 }
9033
9034 // Treat this like a legacy job.
9035 if (!j->legacy_mach_job) {
9036 ms->isActive = true;
9037 ms->recv = false;
9038 }
9039
9040 if (!(j->anonymous || j->legacy_LS_job || j->legacy_mach_job)) {
9041 job_log(j, LOG_APPLEONLY, "Please add the following service to the configuration file for this job: %s", servicename);
9042 }
9043 } else {
9044 return BOOTSTRAP_UNKNOWN_SERVICE;
9045 }
9046 } else {
9047 if (unlikely((jo = machservice_job(ms)) != j)) {
9048 static pid_t last_warned_pid;
9049
9050 if (last_warned_pid != ldc->pid) {
9051 job_log(jo, LOG_WARNING, "The following job tried to hijack the service \"%s\" from this job: %s", servicename, j->label);
9052 last_warned_pid = ldc->pid;
9053 }
9054
9055 return BOOTSTRAP_NOT_PRIVILEGED;
9056 }
9057 if (unlikely(machservice_active(ms))) {
9058 job_log(j, LOG_WARNING, "Check-in of Mach service failed. Already active: %s", servicename);
9059 return BOOTSTRAP_SERVICE_ACTIVE;
9060 }
9061 }
9062
9063 job_checkin(j);
9064 machservice_request_notifications(ms);
9065
9066 job_log(j, LOG_INFO, "Check-in of service: %s", servicename);
9067
9068 *serviceportp = machservice_port(ms);
9069 return BOOTSTRAP_SUCCESS;
9070 }
9071
9072 kern_return_t
9073 job_mig_register2(job_t j, name_t servicename, mach_port_t serviceport, uint64_t flags)
9074 {
9075 struct machservice *ms;
9076 struct ldcred *ldc = runtime_get_caller_creds();
9077 bool per_pid_service = flags & BOOTSTRAP_PER_PID_SERVICE;
9078
9079 if (!j) {
9080 RETURN_NO_MEMORY();
9081 }
9082
9083 if (!per_pid_service && !j->legacy_LS_job) {
9084 job_log(j, LOG_APPLEONLY, "Performance: bootstrap_register() is deprecated. Service: %s", servicename);
9085 }
9086
9087 job_log(j, LOG_DEBUG, "%sMach service registration attempt: %s", flags & BOOTSTRAP_PER_PID_SERVICE ? "Per PID " : "", servicename);
9088
9089 #if HAVE_SANDBOX
9090 if (unlikely(sandbox_check(ldc->pid, "mach-register", per_pid_service ? SANDBOX_FILTER_LOCAL_NAME : SANDBOX_FILTER_GLOBAL_NAME, servicename) > 0)) {
9091 return BOOTSTRAP_NOT_PRIVILEGED;
9092 }
9093 #endif
9094
9095 // 5641783 for the embedded hack
9096 #if !TARGET_OS_EMBEDDED
9097 /*
9098 * From a per-user/session launchd's perspective, SecurityAgent (UID
9099 * 92) is a rogue application (not our UID, not root and not a child of
9100 * us). We'll have to reconcile this design friction at a later date.
9101 */
9102 if (unlikely(j->anonymous && j->mgr->parentmgr == NULL && ldc->uid != 0 && ldc->uid != getuid() && ldc->uid != 92)) {
9103 if (pid1_magic) {
9104 return VPROC_ERR_TRY_PER_USER;
9105 } else {
9106 return BOOTSTRAP_NOT_PRIVILEGED;
9107 }
9108 }
9109 #endif
9110
9111 ms = jobmgr_lookup_service(j->mgr, servicename, false, flags & BOOTSTRAP_PER_PID_SERVICE ? ldc->pid : 0);
9112
9113 if (unlikely(ms)) {
9114 if (machservice_job(ms) != j) {
9115 return BOOTSTRAP_NOT_PRIVILEGED;
9116 }
9117 if (machservice_active(ms)) {
9118 job_log(j, LOG_DEBUG, "Mach service registration failed. Already active: %s", servicename);
9119 return BOOTSTRAP_SERVICE_ACTIVE;
9120 }
9121 if (ms->recv && (serviceport != MACH_PORT_NULL)) {
9122 job_log(j, LOG_ERR, "bootstrap_register() erroneously called instead of bootstrap_check_in(). Mach service: %s", servicename);
9123 return BOOTSTRAP_NOT_PRIVILEGED;
9124 }
9125 job_checkin(j);
9126 machservice_delete(j, ms, false);
9127 }
9128
9129 if (likely(serviceport != MACH_PORT_NULL)) {
9130 if (likely(ms = machservice_new(j, servicename, &serviceport, flags & BOOTSTRAP_PER_PID_SERVICE ? true : false))) {
9131 machservice_request_notifications(ms);
9132 } else {
9133 RETURN_NO_MEMORY();
9134 }
9135 }
9136
9137
9138 return BOOTSTRAP_SUCCESS;
9139 }
9140
9141 kern_return_t
9142 job_mig_look_up2(job_t j, mach_port_t srp, name_t servicename, mach_port_t *serviceportp, pid_t target_pid, uuid_t instance_id, uint64_t flags)
9143 {
9144 struct machservice *ms = NULL;
9145 struct ldcred *ldc = runtime_get_caller_creds();
9146 kern_return_t kr;
9147 bool per_pid_lookup = flags & BOOTSTRAP_PER_PID_SERVICE;
9148 bool specific_instance = flags & BOOTSTRAP_SPECIFIC_INSTANCE;
9149 bool strict_lookup = flags & BOOTSTRAP_STRICT_LOOKUP;
9150 bool privileged = flags & BOOTSTRAP_PRIVILEGED_SERVER;
9151
9152 if (!j) {
9153 RETURN_NO_MEMORY();
9154 }
9155
9156 bool xpc_req = (j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN);
9157
9158 // 5641783 for the embedded hack
9159 #if !TARGET_OS_EMBEDDED
9160 if (unlikely(pid1_magic && j->anonymous && j->mgr->parentmgr == NULL && ldc->uid != 0 && ldc->euid != 0)) {
9161 return VPROC_ERR_TRY_PER_USER;
9162 }
9163 #endif
9164
9165 #if HAVE_SANDBOX
9166 /* We don't do sandbox checking for XPC domains because, by definition, all
9167 * the services within your domain should be accessible to you.
9168 */
9169 if (!xpc_req && unlikely(sandbox_check(ldc->pid, "mach-lookup", per_pid_lookup ? SANDBOX_FILTER_LOCAL_NAME : SANDBOX_FILTER_GLOBAL_NAME, servicename) > 0)) {
9170 return BOOTSTRAP_NOT_PRIVILEGED;
9171 }
9172 #endif
9173
9174 if (per_pid_lookup) {
9175 ms = jobmgr_lookup_service(j->mgr, servicename, false, target_pid);
9176 } else {
9177 if (xpc_req) {
9178 // Requests from XPC domains stay local.
9179 ms = jobmgr_lookup_service(j->mgr, servicename, false, 0);
9180 } else {
9181 /* A strict lookup which is privileged won't even bother trying to
9182 * find a service if we're not hosting the root Mach bootstrap.
9183 */
9184 if (strict_lookup && privileged) {
9185 if (inherited_bootstrap_port == MACH_PORT_NULL) {
9186 ms = jobmgr_lookup_service(j->mgr, servicename, true, 0);
9187 }
9188 } else {
9189 ms = jobmgr_lookup_service(j->mgr, servicename, true, 0);
9190 }
9191 }
9192 }
9193
9194 if (likely(ms)) {
9195 ms = ms->alias ? ms->alias : ms;
9196 if (unlikely(specific_instance && ms->job->multiple_instances)) {
9197 job_t ji = NULL;
9198 job_t instance = NULL;
9199 LIST_FOREACH(ji, &ms->job->subjobs, subjob_sle) {
9200 if (uuid_compare(instance_id, ji->instance_id) == 0) {
9201 instance = ji;
9202 break;
9203 }
9204 }
9205
9206 if (unlikely(instance == NULL)) {
9207 job_log(ms->job, LOG_DEBUG, "Creating new instance of job based on lookup of service %s", ms->name);
9208 instance = job_new_subjob(ms->job, instance_id);
9209 if (job_assumes(j, instance != NULL)) {
9210 /* Disable this support for now. We only support having
9211 * multi-instance jobs within private XPC domains.
9212 */
9213 #if 0
9214 /* If the job is multi-instance, in a singleton XPC domain
9215 * and the request is not coming from within that singleton
9216 * domain, we need to alias the new job into the requesting
9217 * domain.
9218 */
9219 if (!j->mgr->xpc_singleton && xpc_req) {
9220 (void)job_assumes(instance, job_new_alias(j->mgr, instance));
9221 }
9222 #endif
9223 job_dispatch(instance, false);
9224 }
9225 }
9226
9227 ms = NULL;
9228 if (job_assumes(j, instance != NULL)) {
9229 struct machservice *msi = NULL;
9230 SLIST_FOREACH(msi, &instance->machservices, sle) {
9231 /* sizeof(servicename) will return the size of a pointer,
9232 * even though it's an array type, because when passing
9233 * arrays as parameters in C, they implicitly degrade to
9234 * pointers.
9235 */
9236 if (strncmp(servicename, msi->name, sizeof(name_t) - 1) == 0) {
9237 ms = msi;
9238 break;
9239 }
9240 }
9241 }
9242 } else {
9243 if (machservice_hidden(ms) && !machservice_active(ms)) {
9244 ms = NULL;
9245 } else if (unlikely(ms->per_user_hack)) {
9246 ms = NULL;
9247 }
9248 }
9249 }
9250
9251 if (likely(ms)) {
9252 (void)job_assumes(j, machservice_port(ms) != MACH_PORT_NULL);
9253 job_log(j, LOG_DEBUG, "%sMach service lookup: %s", per_pid_lookup ? "Per PID " : "", servicename);
9254 *serviceportp = machservice_port(ms);
9255
9256 job_log(j, LOG_DEBUG, "Service port: <%d>\n", *serviceportp);
9257
9258 kr = BOOTSTRAP_SUCCESS;
9259 } else if (strict_lookup && !privileged) {
9260 /* Hack: We need to simulate XPC's desire not to establish a hierarchy.
9261 * So if XPC is doing the lookup, and it's not a privileged lookup, we
9262 * won't forward. But if it is a privileged lookup, then we must
9263 * forward.
9264 */
9265 return BOOTSTRAP_UNKNOWN_SERVICE;
9266 } else if (inherited_bootstrap_port != MACH_PORT_NULL) {
9267 // Requests from within an XPC domain don't get forwarded.
9268 job_log(j, LOG_DEBUG, "Mach service lookup forwarded: %s", servicename);
9269 /* Clients potentially check the audit token of the reply to verify that
9270 * the returned send right is trustworthy.
9271 */
9272 (void)job_assumes_zero(j, vproc_mig_look_up2_forward(inherited_bootstrap_port, srp, servicename, target_pid, instance_id, flags));
9273 return MIG_NO_REPLY;
9274 } else if (pid1_magic && j->anonymous && ldc->euid >= 500 && strcasecmp(j->mgr->name, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
9275 /* 5240036 Should start background session when a lookup of CCacheServer
9276 * occurs
9277 *
9278 * This is a total hack. We sniff out loginwindow session, and attempt
9279 * to guess what it is up to. If we find a EUID that isn't root, we
9280 * force it over to the per-user context.
9281 */
9282 return VPROC_ERR_TRY_PER_USER;
9283 } else {
9284 job_log(j, LOG_DEBUG, "%sMach service lookup failed: %s", per_pid_lookup ? "Per PID " : "", servicename);
9285 kr = BOOTSTRAP_UNKNOWN_SERVICE;
9286 }
9287
9288 return kr;
9289 }
9290
9291 kern_return_t
9292 job_mig_parent(job_t j, mach_port_t srp, mach_port_t *parentport)
9293 {
9294 if (!j) {
9295 RETURN_NO_MEMORY();
9296 }
9297
9298 job_log(j, LOG_DEBUG, "Requested parent bootstrap port");
9299 jobmgr_t jm = j->mgr;
9300
9301 if (jobmgr_parent(jm)) {
9302 *parentport = jobmgr_parent(jm)->jm_port;
9303 } else if (MACH_PORT_NULL == inherited_bootstrap_port) {
9304 *parentport = jm->jm_port;
9305 } else {
9306 (void)job_assumes_zero(j, vproc_mig_parent_forward(inherited_bootstrap_port, srp));
9307 // The previous routine moved the reply port, we're forced to return MIG_NO_REPLY now
9308 return MIG_NO_REPLY;
9309 }
9310 return BOOTSTRAP_SUCCESS;
9311 }
9312
9313 kern_return_t
9314 job_mig_get_root_bootstrap(job_t j, mach_port_t *rootbsp)
9315 {
9316 if (!j) {
9317 RETURN_NO_MEMORY();
9318 }
9319
9320 if (inherited_bootstrap_port == MACH_PORT_NULL) {
9321 *rootbsp = root_jobmgr->jm_port;
9322 (void)job_assumes_zero(j, launchd_mport_make_send(root_jobmgr->jm_port));
9323 } else {
9324 *rootbsp = inherited_bootstrap_port;
9325 (void)job_assumes_zero(j, launchd_mport_copy_send(inherited_bootstrap_port));
9326 }
9327
9328 return BOOTSTRAP_SUCCESS;
9329 }
9330
9331 kern_return_t
9332 job_mig_info(job_t j, name_array_t *servicenamesp,
9333 unsigned int *servicenames_cnt, name_array_t *servicejobsp,
9334 unsigned int *servicejobs_cnt, bootstrap_status_array_t *serviceactivesp,
9335 unsigned int *serviceactives_cnt, uint64_t flags)
9336 {
9337 name_array_t service_names = NULL;
9338 name_array_t service_jobs = NULL;
9339 bootstrap_status_array_t service_actives = NULL;
9340 unsigned int cnt = 0, cnt2 = 0;
9341 jobmgr_t jm;
9342
9343 if (!j) {
9344 RETURN_NO_MEMORY();
9345 }
9346
9347 #if TARGET_OS_EMBEDDED
9348 struct ldcred *ldc = runtime_get_caller_creds();
9349 if (ldc->euid) {
9350 return EPERM;
9351 }
9352 #endif // TARGET_OS_EMBEDDED
9353
9354 if (launchd_flat_mach_namespace) {
9355 if ((j->mgr->properties & BOOTSTRAP_PROPERTY_EXPLICITSUBSET) || (flags & BOOTSTRAP_FORCE_LOCAL)) {
9356 jm = j->mgr;
9357 } else {
9358 jm = root_jobmgr;
9359 }
9360 } else {
9361 jm = j->mgr;
9362 }
9363
9364 unsigned int i = 0;
9365 struct machservice *msi = NULL;
9366 for (i = 0; i < MACHSERVICE_HASH_SIZE; i++) {
9367 LIST_FOREACH(msi, &jm->ms_hash[i], name_hash_sle) {
9368 cnt += !msi->per_pid ? 1 : 0;
9369 }
9370 }
9371
9372 if (cnt == 0) {
9373 goto out;
9374 }
9375
9376 mig_allocate((vm_address_t *)&service_names, cnt * sizeof(service_names[0]));
9377 if (!job_assumes(j, service_names != NULL)) {
9378 goto out_bad;
9379 }
9380
9381 mig_allocate((vm_address_t *)&service_jobs, cnt * sizeof(service_jobs[0]));
9382 if (!job_assumes(j, service_jobs != NULL)) {
9383 goto out_bad;
9384 }
9385
9386 mig_allocate((vm_address_t *)&service_actives, cnt * sizeof(service_actives[0]));
9387 if (!job_assumes(j, service_actives != NULL)) {
9388 goto out_bad;
9389 }
9390
9391 for (i = 0; i < MACHSERVICE_HASH_SIZE; i++) {
9392 LIST_FOREACH(msi, &jm->ms_hash[i], name_hash_sle) {
9393 if (!msi->per_pid) {
9394 strlcpy(service_names[cnt2], machservice_name(msi), sizeof(service_names[0]));
9395 msi = msi->alias ? msi->alias : msi;
9396 if (msi->job->mgr->shortdesc) {
9397 strlcpy(service_jobs[cnt2], msi->job->mgr->shortdesc, sizeof(service_jobs[0]));
9398 } else {
9399 strlcpy(service_jobs[cnt2], msi->job->label, sizeof(service_jobs[0]));
9400 }
9401 service_actives[cnt2] = machservice_status(msi);
9402 cnt2++;
9403 }
9404 }
9405 }
9406
9407 (void)job_assumes(j, cnt == cnt2);
9408
9409 out:
9410 *servicenamesp = service_names;
9411 *servicejobsp = service_jobs;
9412 *serviceactivesp = service_actives;
9413 *servicenames_cnt = *servicejobs_cnt = *serviceactives_cnt = cnt;
9414
9415 return BOOTSTRAP_SUCCESS;
9416
9417 out_bad:
9418 if (service_names) {
9419 mig_deallocate((vm_address_t)service_names, cnt * sizeof(service_names[0]));
9420 }
9421 if (service_jobs) {
9422 mig_deallocate((vm_address_t)service_jobs, cnt * sizeof(service_jobs[0]));
9423 }
9424 if (service_actives) {
9425 mig_deallocate((vm_address_t)service_actives, cnt * sizeof(service_actives[0]));
9426 }
9427
9428 RETURN_NO_MEMORY();
9429 }
9430
9431 kern_return_t
9432 job_mig_lookup_children(job_t j, mach_port_array_t *child_ports,
9433 mach_msg_type_number_t *child_ports_cnt, name_array_t *child_names,
9434 mach_msg_type_number_t *child_names_cnt,
9435 bootstrap_property_array_t *child_properties,
9436 mach_msg_type_number_t *child_properties_cnt)
9437 {
9438 kern_return_t kr = BOOTSTRAP_NO_MEMORY;
9439 if (!j) {
9440 RETURN_NO_MEMORY();
9441 }
9442
9443 struct ldcred *ldc = runtime_get_caller_creds();
9444
9445 /* Only allow root processes to look up children, even if we're in the per-user launchd.
9446 * Otherwise, this could be used to cross sessions, which counts as a security vulnerability
9447 * in a non-flat namespace.
9448 */
9449 if (ldc->euid != 0) {
9450 job_log(j, LOG_WARNING, "Attempt to look up children of bootstrap by unprivileged job.");
9451 return BOOTSTRAP_NOT_PRIVILEGED;
9452 }
9453
9454 unsigned int cnt = 0;
9455
9456 jobmgr_t jmr = j->mgr;
9457 jobmgr_t jmi = NULL;
9458 SLIST_FOREACH(jmi, &jmr->submgrs, sle) {
9459 cnt++;
9460 }
9461
9462 // Find our per-user launchds if we're PID 1.
9463 job_t ji = NULL;
9464 if (pid1_magic) {
9465 LIST_FOREACH(ji, &jmr->jobs, sle) {
9466 cnt += ji->per_user ? 1 : 0;
9467 }
9468 }
9469
9470 if (cnt == 0) {
9471 return BOOTSTRAP_NO_CHILDREN;
9472 }
9473
9474 mach_port_array_t _child_ports = NULL;
9475 name_array_t _child_names = NULL;
9476 bootstrap_property_array_t _child_properties = NULL;
9477
9478 mig_allocate((vm_address_t *)&_child_ports, cnt * sizeof(_child_ports[0]));
9479 if (!job_assumes(j, _child_ports != NULL)) {
9480 kr = BOOTSTRAP_NO_MEMORY;
9481 goto out_bad;
9482 }
9483
9484 mig_allocate((vm_address_t *)&_child_names, cnt * sizeof(_child_names[0]));
9485 if (!job_assumes(j, _child_names != NULL)) {
9486 kr = BOOTSTRAP_NO_MEMORY;
9487 goto out_bad;
9488 }
9489
9490 mig_allocate((vm_address_t *)&_child_properties, cnt * sizeof(_child_properties[0]));
9491 if (!job_assumes(j, _child_properties != NULL)) {
9492 kr = BOOTSTRAP_NO_MEMORY;
9493 goto out_bad;
9494 }
9495
9496 unsigned int cnt2 = 0;
9497 SLIST_FOREACH(jmi, &jmr->submgrs, sle) {
9498 if (jobmgr_assumes_zero(jmi, launchd_mport_make_send(jmi->jm_port)) == KERN_SUCCESS) {
9499 _child_ports[cnt2] = jmi->jm_port;
9500 } else {
9501 _child_ports[cnt2] = MACH_PORT_NULL;
9502 }
9503
9504 strlcpy(_child_names[cnt2], jmi->name, sizeof(_child_names[0]));
9505 _child_properties[cnt2] = jmi->properties;
9506
9507 cnt2++;
9508 }
9509
9510 if (pid1_magic) LIST_FOREACH(ji, &jmr->jobs, sle) {
9511 if (ji->per_user) {
9512 if (job_assumes(ji, SLIST_FIRST(&ji->machservices)->per_user_hack == true)) {
9513 mach_port_t port = machservice_port(SLIST_FIRST(&ji->machservices));
9514
9515 if (job_assumes_zero(ji, launchd_mport_copy_send(port)) == KERN_SUCCESS) {
9516 _child_ports[cnt2] = port;
9517 } else {
9518 _child_ports[cnt2] = MACH_PORT_NULL;
9519 }
9520 } else {
9521 _child_ports[cnt2] = MACH_PORT_NULL;
9522 }
9523
9524 strlcpy(_child_names[cnt2], ji->label, sizeof(_child_names[0]));
9525 _child_properties[cnt2] |= BOOTSTRAP_PROPERTY_PERUSER;
9526
9527 cnt2++;
9528 }
9529 }
9530
9531 *child_names_cnt = cnt;
9532 *child_ports_cnt = cnt;
9533 *child_properties_cnt = cnt;
9534
9535 *child_names = _child_names;
9536 *child_ports = _child_ports;
9537 *child_properties = _child_properties;
9538
9539 unsigned int i = 0;
9540 for (i = 0; i < cnt; i++) {
9541 job_log(j, LOG_DEBUG, "child_names[%u] = %s", i, (char *)_child_names[i]);
9542 }
9543
9544 return BOOTSTRAP_SUCCESS;
9545 out_bad:
9546 if (_child_ports) {
9547 mig_deallocate((vm_address_t)_child_ports, cnt * sizeof(_child_ports[0]));
9548 }
9549
9550 if (_child_names) {
9551 mig_deallocate((vm_address_t)_child_names, cnt * sizeof(_child_names[0]));
9552 }
9553
9554 if (_child_properties) {
9555 mig_deallocate((vm_address_t)_child_properties, cnt * sizeof(_child_properties[0]));
9556 }
9557
9558 return kr;
9559 }
9560
9561 kern_return_t
9562 job_mig_pid_is_managed(job_t j __attribute__((unused)), pid_t p, boolean_t *managed)
9563 {
9564 struct ldcred *ldc = runtime_get_caller_creds();
9565 if ((ldc->euid != geteuid()) && (ldc->euid != 0)) {
9566 return BOOTSTRAP_NOT_PRIVILEGED;
9567 }
9568
9569 /* This is so loginwindow doesn't try to quit GUI apps that have been launched
9570 * directly by launchd as agents.
9571 */
9572 job_t j_for_pid = jobmgr_find_by_pid_deep(root_jobmgr, p, false);
9573 if (j_for_pid && !j_for_pid->anonymous && !j_for_pid->legacy_LS_job) {
9574 *managed = true;
9575 }
9576
9577 return BOOTSTRAP_SUCCESS;
9578 }
9579
9580 kern_return_t
9581 job_mig_port_for_label(job_t j __attribute__((unused)), name_t label, mach_port_t *mp)
9582 {
9583 if (!j) {
9584 RETURN_NO_MEMORY();
9585 }
9586
9587 struct ldcred *ldc = runtime_get_caller_creds();
9588 kern_return_t kr = BOOTSTRAP_NOT_PRIVILEGED;
9589
9590 #if HAVE_SANDBOX
9591 if (unlikely(sandbox_check(ldc->pid, "job-creation", SANDBOX_FILTER_NONE) > 0)) {
9592 return BOOTSTRAP_NOT_PRIVILEGED;
9593 }
9594 #endif
9595
9596 mach_port_t _mp = MACH_PORT_NULL;
9597 if (!j->deny_job_creation && (ldc->euid == 0 || ldc->euid == geteuid())) {
9598 job_t target_j = job_find(NULL, label);
9599 if (jobmgr_assumes(root_jobmgr, target_j != NULL)) {
9600 if (target_j->j_port == MACH_PORT_NULL) {
9601 (void)job_assumes(target_j, job_setup_machport(target_j) == true);
9602 }
9603
9604 _mp = target_j->j_port;
9605 kr = _mp != MACH_PORT_NULL ? BOOTSTRAP_SUCCESS : BOOTSTRAP_NO_MEMORY;
9606 } else {
9607 kr = BOOTSTRAP_NO_MEMORY;
9608 }
9609 }
9610
9611 *mp = _mp;
9612 return kr;
9613 }
9614
9615 kern_return_t
9616 job_mig_set_security_session(job_t j, uuid_t uuid, mach_port_t asport)
9617 {
9618 #if TARGET_OS_EMBEDDED
9619 return KERN_SUCCESS;
9620 #endif
9621
9622 if (!j) {
9623 RETURN_NO_MEMORY();
9624 }
9625
9626 uuid_string_t uuid_str;
9627 uuid_unparse(uuid, uuid_str);
9628 job_log(j, LOG_DEBUG, "Setting session %u for UUID %s...", asport, uuid_str);
9629
9630 job_t ji = NULL, jt = NULL;
9631 LIST_FOREACH_SAFE(ji, &s_needing_sessions, sle, jt) {
9632 uuid_string_t uuid_str2;
9633 uuid_unparse(ji->expected_audit_uuid, uuid_str2);
9634
9635 if (uuid_compare(uuid, ji->expected_audit_uuid) == 0) {
9636 uuid_clear(ji->expected_audit_uuid);
9637 if (asport != MACH_PORT_NULL) {
9638 job_log(ji, LOG_DEBUG, "Job should join session with port 0x%x", asport);
9639 (void)job_assumes_zero(j, launchd_mport_copy_send(asport));
9640 } else {
9641 job_log(ji, LOG_DEBUG, "No session to set for job. Using our session.");
9642 }
9643
9644 ji->asport = asport;
9645 LIST_REMOVE(ji, needing_session_sle);
9646
9647 if (ji->event_monitor) {
9648 eventsystem_ping();
9649 } else {
9650 job_dispatch(ji, false);
9651 }
9652 }
9653 }
9654
9655 /* Each job that the session port was set for holds a reference. At the end of
9656 * the loop, there will be one extra reference belonging to this MiG protocol.
9657 * We need to release it so that the session goes away when all the jobs
9658 * referencing it are unloaded.
9659 */
9660 (void)job_assumes_zero(j, launchd_mport_deallocate(asport));
9661
9662 return KERN_SUCCESS;
9663 }
9664
9665 jobmgr_t
9666 jobmgr_find_by_name(jobmgr_t jm, const char *where)
9667 {
9668 jobmgr_t jmi, jmi2;
9669
9670 // NULL is only passed for our custom API for LaunchServices. If that is the case, we do magic.
9671 if (where == NULL) {
9672 if (strcasecmp(jm->name, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
9673 where = VPROCMGR_SESSION_LOGINWINDOW;
9674 } else {
9675 where = VPROCMGR_SESSION_AQUA;
9676 }
9677 }
9678
9679 if (strcasecmp(jm->name, where) == 0) {
9680 return jm;
9681 }
9682
9683 if (strcasecmp(where, VPROCMGR_SESSION_BACKGROUND) == 0 && !pid1_magic) {
9684 jmi = root_jobmgr;
9685 goto jm_found;
9686 }
9687
9688 SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) {
9689 if (unlikely(jmi->shutting_down)) {
9690 continue;
9691 } else if (jmi->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN) {
9692 continue;
9693 } else if (strcasecmp(jmi->name, where) == 0) {
9694 goto jm_found;
9695 } else if (strcasecmp(jmi->name, VPROCMGR_SESSION_BACKGROUND) == 0 && pid1_magic) {
9696 SLIST_FOREACH(jmi2, &jmi->submgrs, sle) {
9697 if (strcasecmp(jmi2->name, where) == 0) {
9698 jmi = jmi2;
9699 goto jm_found;
9700 }
9701 }
9702 }
9703 }
9704
9705 jm_found:
9706 return jmi;
9707 }
9708
9709 kern_return_t
9710 job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type, mach_port_t asport, uint64_t flags)
9711 {
9712 mach_msg_type_number_t l2l_i, l2l_port_cnt = 0;
9713 mach_port_array_t l2l_ports = NULL;
9714 mach_port_t reqport, rcvright;
9715 kern_return_t kr = 1;
9716 launch_data_t out_obj_array = NULL;
9717 struct ldcred *ldc = runtime_get_caller_creds();
9718 jobmgr_t jmr = NULL;
9719
9720 if (!j) {
9721 RETURN_NO_MEMORY();
9722 }
9723
9724 if (job_mig_intran2(root_jobmgr, target_subset, ldc->pid)) {
9725 job_log(j, LOG_ERR, "Moving a session to ourself is bogus.");
9726
9727 kr = BOOTSTRAP_NOT_PRIVILEGED;
9728 goto out;
9729 }
9730
9731 job_log(j, LOG_DEBUG, "Move subset attempt: 0x%x", target_subset);
9732
9733 kr = _vproc_grab_subset(target_subset, &reqport, &rcvright, &out_obj_array, &l2l_ports, &l2l_port_cnt);
9734 if (job_assumes_zero(j, kr) != 0) {
9735 goto out;
9736 }
9737
9738 if (launch_data_array_get_count(out_obj_array) != l2l_port_cnt) {
9739 os_assert_zero(l2l_port_cnt);
9740 }
9741
9742 if (!job_assumes(j, (jmr = jobmgr_new(j->mgr, reqport, rcvright, false, session_type, false, asport)) != NULL)) {
9743 kr = BOOTSTRAP_NO_MEMORY;
9744 goto out;
9745 }
9746
9747 if (strcmp(session_type, VPROCMGR_SESSION_AQUA) == 0) {
9748 jobmgr_log(jmr, LOG_NOTICE, "Registering new GUI session.");
9749 kr = vproc_mig_register_gui_session(inherited_bootstrap_port, asport);
9750 if (kr) {
9751 jobmgr_log(jmr, LOG_ERR, "Failed to register GUI session with PID 1: 0x%x/0x%x", inherited_bootstrap_port, kr);
9752 }
9753 }
9754
9755 jmr->properties |= BOOTSTRAP_PROPERTY_MOVEDSUBSET;
9756
9757 /* This is a hack. We should be doing this in jobmgr_new(), but since we're in the middle of
9758 * processing an IPC request, we'll do this action before the new job manager can get any IPC
9759 * requests. This serialization is guaranteed since we are single-threaded in that respect.
9760 */
9761 if (flags & LAUNCH_GLOBAL_ON_DEMAND) {
9762 // This is so awful.
9763 // Remove the job from its current job manager.
9764 LIST_REMOVE(j, sle);
9765 LIST_REMOVE(j, pid_hash_sle);
9766
9767 // Put the job into the target job manager.
9768 LIST_INSERT_HEAD(&jmr->jobs, j, sle);
9769 LIST_INSERT_HEAD(&jmr->active_jobs[ACTIVE_JOB_HASH(j->p)], j, pid_hash_sle);
9770
9771 j->mgr = jmr;
9772 job_set_global_on_demand(j, true);
9773
9774 if (!j->holds_ref) {
9775 job_log(j, LOG_PERF, "Job moved subset into: %s", j->mgr->name);
9776 j->holds_ref = true;
9777 runtime_add_ref();
9778 }
9779 }
9780
9781 for (l2l_i = 0; l2l_i < l2l_port_cnt; l2l_i++) {
9782 launch_data_t tmp, obj_at_idx;
9783 struct machservice *ms;
9784 job_t j_for_service;
9785 const char *serv_name;
9786 pid_t target_pid;
9787 bool serv_perpid;
9788
9789 (void)job_assumes(j, obj_at_idx = launch_data_array_get_index(out_obj_array, l2l_i));
9790 (void)job_assumes(j, tmp = launch_data_dict_lookup(obj_at_idx, TAKE_SUBSET_PID));
9791 target_pid = (pid_t)launch_data_get_integer(tmp);
9792 (void)job_assumes(j, tmp = launch_data_dict_lookup(obj_at_idx, TAKE_SUBSET_PERPID));
9793 serv_perpid = launch_data_get_bool(tmp);
9794 (void)job_assumes(j, tmp = launch_data_dict_lookup(obj_at_idx, TAKE_SUBSET_NAME));
9795 serv_name = launch_data_get_string(tmp);
9796
9797 j_for_service = jobmgr_find_by_pid(jmr, target_pid, true);
9798
9799 if (unlikely(!j_for_service)) {
9800 // The PID probably exited
9801 (void)job_assumes_zero(j, launchd_mport_deallocate(l2l_ports[l2l_i]));
9802 continue;
9803 }
9804
9805 if (likely(ms = machservice_new(j_for_service, serv_name, &l2l_ports[l2l_i], serv_perpid))) {
9806 job_log(j, LOG_DEBUG, "Importing %s into new bootstrap.", serv_name);
9807 machservice_request_notifications(ms);
9808 }
9809 }
9810
9811 kr = 0;
9812
9813 out:
9814 if (out_obj_array) {
9815 launch_data_free(out_obj_array);
9816 }
9817
9818 if (l2l_ports) {
9819 mig_deallocate((vm_address_t)l2l_ports, l2l_port_cnt * sizeof(l2l_ports[0]));
9820 }
9821
9822 if (kr == 0) {
9823 if (target_subset) {
9824 (void)job_assumes_zero(j, launchd_mport_deallocate(target_subset));
9825 }
9826 if (asport) {
9827 (void)job_assumes_zero(j, launchd_mport_deallocate(asport));
9828 }
9829 } else if (jmr) {
9830 jobmgr_shutdown(jmr);
9831 }
9832
9833 return kr;
9834 }
9835
9836 kern_return_t
9837 job_mig_init_session(job_t j, name_t session_type, mach_port_t asport)
9838 {
9839 if (!j) {
9840 RETURN_NO_MEMORY();
9841 }
9842
9843 job_t j2;
9844
9845 kern_return_t kr = BOOTSTRAP_NO_MEMORY;
9846 if (j->mgr->session_initialized) {
9847 job_log(j, LOG_ERR, "Tried to initialize an already setup session!");
9848 kr = BOOTSTRAP_NOT_PRIVILEGED;
9849 return kr;
9850 } else if (strcmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
9851 jobmgr_t jmi;
9852
9853 /*
9854 * 5330262
9855 *
9856 * We're working around LoginWindow and the WindowServer.
9857 *
9858 * In practice, there is only one LoginWindow session. Unfortunately, for certain
9859 * scenarios, the WindowServer spawns loginwindow, and in those cases, it frequently
9860 * spawns a replacement loginwindow session before cleaning up the previous one.
9861 *
9862 * We're going to use the creation of a new LoginWindow context as a clue that the
9863 * previous LoginWindow context is on the way out and therefore we should just
9864 * kick-start the shutdown of it.
9865 */
9866
9867 SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) {
9868 if (unlikely(jmi->shutting_down)) {
9869 continue;
9870 } else if (strcasecmp(jmi->name, session_type) == 0) {
9871 jobmgr_shutdown(jmi);
9872 break;
9873 }
9874 }
9875 } else if (strcmp(session_type, VPROCMGR_SESSION_AQUA) == 0) {
9876 (void)job_assumes_zero(j, runtime_remove_mport(j->mgr->jm_port));
9877 }
9878
9879 jobmgr_log(j->mgr, LOG_DEBUG, "Initializing as %s", session_type);
9880 strcpy(j->mgr->name_init, session_type);
9881
9882 if (job_assumes(j, (j2 = jobmgr_init_session(j->mgr, session_type, false)))) {
9883 j2->asport = asport;
9884 (void)job_assumes(j, job_dispatch(j2, true));
9885 kr = BOOTSTRAP_SUCCESS;
9886 }
9887
9888 return kr;
9889 }
9890
9891 kern_return_t
9892 job_mig_switch_to_session(job_t j, mach_port_t requestor_port, name_t session_name, mach_port_t asport, mach_port_t *new_bsport)
9893 {
9894 struct ldcred *ldc = runtime_get_caller_creds();
9895 if (!jobmgr_assumes(root_jobmgr, j != NULL)) {
9896 jobmgr_log(root_jobmgr, LOG_ERR, "%s() called with NULL job: PID %d", __func__, ldc->pid);
9897 RETURN_NO_MEMORY();
9898 }
9899
9900 if (j->mgr->shutting_down) {
9901 return BOOTSTRAP_UNKNOWN_SERVICE;
9902 }
9903
9904 job_log(j, LOG_DEBUG, "Job wants to move to %s session.", session_name);
9905
9906 if (!job_assumes(j, pid1_magic == false)) {
9907 job_log(j, LOG_WARNING, "Switching sessions is not allowed in the system Mach bootstrap.");
9908 return BOOTSTRAP_NOT_PRIVILEGED;
9909 }
9910
9911 if (!j->anonymous) {
9912 job_log(j, LOG_NOTICE, "Non-anonymous job tried to switch sessions. Please use LimitLoadToSessionType instead.");
9913 return BOOTSTRAP_NOT_PRIVILEGED;
9914 }
9915
9916 jobmgr_t target_jm = jobmgr_find_by_name(root_jobmgr, session_name);
9917 if (target_jm == j->mgr) {
9918 job_log(j, LOG_DEBUG, "Job is already in its desired session (%s).", session_name);
9919 (void)job_assumes_zero(j, launchd_mport_deallocate(asport));
9920 (void)job_assumes_zero(j, launchd_mport_deallocate(requestor_port));
9921 *new_bsport = target_jm->jm_port;
9922 return BOOTSTRAP_SUCCESS;
9923 }
9924
9925 if (!target_jm) {
9926 target_jm = jobmgr_new(j->mgr, requestor_port, MACH_PORT_NULL, false, session_name, false, asport);
9927 if (target_jm) {
9928 target_jm->properties |= BOOTSTRAP_PROPERTY_IMPLICITSUBSET;
9929 (void)job_assumes_zero(j, launchd_mport_deallocate(asport));
9930 }
9931 }
9932
9933 if (!job_assumes(j, target_jm != NULL)) {
9934 job_log(j, LOG_WARNING, "Could not find %s session!", session_name);
9935 RETURN_NO_MEMORY();
9936 }
9937
9938 // Remove the job from it's current job manager.
9939 LIST_REMOVE(j, sle);
9940 LIST_REMOVE(j, pid_hash_sle);
9941
9942 job_t ji = NULL, jit = NULL;
9943 LIST_FOREACH_SAFE(ji, &j->mgr->global_env_jobs, global_env_sle, jit) {
9944 if (ji == j) {
9945 LIST_REMOVE(ji, global_env_sle);
9946 break;
9947 }
9948 }
9949
9950 // Put the job into the target job manager.
9951 LIST_INSERT_HEAD(&target_jm->jobs, j, sle);
9952 LIST_INSERT_HEAD(&target_jm->active_jobs[ACTIVE_JOB_HASH(j->p)], j, pid_hash_sle);
9953
9954 if (ji) {
9955 LIST_INSERT_HEAD(&target_jm->global_env_jobs, j, global_env_sle);
9956 }
9957
9958 // Move our Mach services over if we're not in a flat namespace.
9959 if (!launchd_flat_mach_namespace && !SLIST_EMPTY(&j->machservices)) {
9960 struct machservice *msi = NULL, *msit = NULL;
9961 SLIST_FOREACH_SAFE(msi, &j->machservices, sle, msit) {
9962 LIST_REMOVE(msi, name_hash_sle);
9963 LIST_INSERT_HEAD(&target_jm->ms_hash[hash_ms(msi->name)], msi, name_hash_sle);
9964 }
9965 }
9966
9967 j->mgr = target_jm;
9968
9969 if (!j->holds_ref) {
9970 /* Anonymous jobs which move around are particularly interesting to us, so we want to
9971 * stick around while they're still around.
9972 * For example, login calls into the PAM launchd module, which moves the process into
9973 * the StandardIO session by default. So we'll hold a reference on that job to prevent
9974 * ourselves from going away.
9975 */
9976 j->holds_ref = true;
9977 job_log(j, LOG_PERF, "Job switched into manager: %s", j->mgr->name);
9978 runtime_add_ref();
9979 }
9980
9981 *new_bsport = target_jm->jm_port;
9982
9983 return KERN_SUCCESS;
9984 }
9985
9986 kern_return_t
9987 job_mig_take_subset(job_t j, mach_port_t *reqport, mach_port_t *rcvright,
9988 vm_offset_t *outdata, mach_msg_type_number_t *outdataCnt,
9989 mach_port_array_t *portsp, unsigned int *ports_cnt)
9990 {
9991 launch_data_t tmp_obj, tmp_dict, outdata_obj_array = NULL;
9992 mach_port_array_t ports = NULL;
9993 unsigned int cnt = 0, cnt2 = 0;
9994 size_t packed_size;
9995 struct machservice *ms;
9996 jobmgr_t jm;
9997 job_t ji;
9998
9999 if (!j) {
10000 RETURN_NO_MEMORY();
10001 }
10002
10003 jm = j->mgr;
10004
10005 if (unlikely(!pid1_magic)) {
10006 job_log(j, LOG_ERR, "Only the system launchd will transfer Mach sub-bootstraps.");
10007 return BOOTSTRAP_NOT_PRIVILEGED;
10008 }
10009 if (unlikely(jobmgr_parent(jm) == NULL)) {
10010 job_log(j, LOG_ERR, "Root Mach bootstrap cannot be transferred.");
10011 return BOOTSTRAP_NOT_PRIVILEGED;
10012 }
10013 if (unlikely(strcasecmp(jm->name, VPROCMGR_SESSION_AQUA) == 0)) {
10014 job_log(j, LOG_ERR, "Cannot transfer a setup GUI session.");
10015 return BOOTSTRAP_NOT_PRIVILEGED;
10016 }
10017 if (unlikely(!j->anonymous)) {
10018 job_log(j, LOG_ERR, "Only the anonymous job can transfer Mach sub-bootstraps.");
10019 return BOOTSTRAP_NOT_PRIVILEGED;
10020 }
10021
10022 job_log(j, LOG_DEBUG, "Transferring sub-bootstrap to the per session launchd.");
10023
10024 outdata_obj_array = launch_data_alloc(LAUNCH_DATA_ARRAY);
10025 if (!job_assumes(j, outdata_obj_array)) {
10026 goto out_bad;
10027 }
10028
10029 *outdataCnt = 20 * 1024 * 1024;
10030 mig_allocate(outdata, *outdataCnt);
10031 if (!job_assumes(j, *outdata != 0)) {
10032 return 1;
10033 }
10034
10035 LIST_FOREACH(ji, &j->mgr->jobs, sle) {
10036 if (!ji->anonymous) {
10037 continue;
10038 }
10039 SLIST_FOREACH(ms, &ji->machservices, sle) {
10040 cnt++;
10041 }
10042 }
10043
10044 mig_allocate((vm_address_t *)&ports, cnt * sizeof(ports[0]));
10045 if (!job_assumes(j, ports != NULL)) {
10046 goto out_bad;
10047 }
10048
10049 LIST_FOREACH(ji, &j->mgr->jobs, sle) {
10050 if (!ji->anonymous) {
10051 continue;
10052 }
10053
10054 SLIST_FOREACH(ms, &ji->machservices, sle) {
10055 if (job_assumes(j, (tmp_dict = launch_data_alloc(LAUNCH_DATA_DICTIONARY)))) {
10056 (void)job_assumes(j, launch_data_array_set_index(outdata_obj_array, tmp_dict, cnt2));
10057 } else {
10058 goto out_bad;
10059 }
10060
10061 if (job_assumes(j, (tmp_obj = launch_data_new_string(machservice_name(ms))))) {
10062 (void)job_assumes(j, launch_data_dict_insert(tmp_dict, tmp_obj, TAKE_SUBSET_NAME));
10063 } else {
10064 goto out_bad;
10065 }
10066
10067 if (job_assumes(j, (tmp_obj = launch_data_new_integer((ms->job->p))))) {
10068 (void)job_assumes(j, launch_data_dict_insert(tmp_dict, tmp_obj, TAKE_SUBSET_PID));
10069 } else {
10070 goto out_bad;
10071 }
10072
10073 if (job_assumes(j, (tmp_obj = launch_data_new_bool((ms->per_pid))))) {
10074 (void)job_assumes(j, launch_data_dict_insert(tmp_dict, tmp_obj, TAKE_SUBSET_PERPID));
10075 } else {
10076 goto out_bad;
10077 }
10078
10079 ports[cnt2] = machservice_port(ms);
10080
10081 // Increment the send right by one so we can shutdown the jobmgr cleanly
10082 (void)jobmgr_assumes_zero(jm, launchd_mport_copy_send(ports[cnt2]));
10083 cnt2++;
10084 }
10085 }
10086
10087 (void)job_assumes(j, cnt == cnt2);
10088
10089 runtime_ktrace0(RTKT_LAUNCHD_DATA_PACK);
10090 packed_size = launch_data_pack(outdata_obj_array, (void *)*outdata, *outdataCnt, NULL, NULL);
10091 if (!job_assumes(j, packed_size != 0)) {
10092 goto out_bad;
10093 }
10094
10095 launch_data_free(outdata_obj_array);
10096
10097 *portsp = ports;
10098 *ports_cnt = cnt;
10099
10100 *reqport = jm->req_port;
10101 *rcvright = jm->jm_port;
10102
10103 jm->req_port = 0;
10104 jm->jm_port = 0;
10105
10106 workaround_5477111 = j;
10107
10108 jobmgr_shutdown(jm);
10109
10110 return BOOTSTRAP_SUCCESS;
10111
10112 out_bad:
10113 if (outdata_obj_array) {
10114 launch_data_free(outdata_obj_array);
10115 }
10116 if (*outdata) {
10117 mig_deallocate(*outdata, *outdataCnt);
10118 }
10119 if (ports) {
10120 mig_deallocate((vm_address_t)ports, cnt * sizeof(ports[0]));
10121 }
10122
10123 RETURN_NO_MEMORY();
10124 }
10125
10126 kern_return_t
10127 job_mig_subset(job_t j, mach_port_t requestorport, mach_port_t *subsetportp)
10128 {
10129 int bsdepth = 0;
10130 jobmgr_t jmr;
10131
10132 if (!j) {
10133 RETURN_NO_MEMORY();
10134 }
10135 if (j->mgr->shutting_down) {
10136 return BOOTSTRAP_UNKNOWN_SERVICE;
10137 }
10138
10139 jmr = j->mgr;
10140
10141 while ((jmr = jobmgr_parent(jmr)) != NULL) {
10142 bsdepth++;
10143 }
10144
10145 // Since we use recursion, we need an artificial depth for subsets
10146 if (unlikely(bsdepth > 100)) {
10147 job_log(j, LOG_ERR, "Mach sub-bootstrap create request failed. Depth greater than: %d", bsdepth);
10148 RETURN_NO_MEMORY();
10149 }
10150
10151 char name[NAME_MAX];
10152 snprintf(name, sizeof(name), "%s[%i].subset.%i", j->anonymous ? j->prog : j->label, j->p, MACH_PORT_INDEX(requestorport));
10153
10154 if (!job_assumes(j, (jmr = jobmgr_new(j->mgr, requestorport, MACH_PORT_NULL, false, name, true, j->asport)) != NULL)) {
10155 if (unlikely(requestorport == MACH_PORT_NULL)) {
10156 return BOOTSTRAP_NOT_PRIVILEGED;
10157 }
10158 RETURN_NO_MEMORY();
10159 }
10160
10161 *subsetportp = jmr->jm_port;
10162 jmr->properties |= BOOTSTRAP_PROPERTY_EXPLICITSUBSET;
10163
10164 /* A job could create multiple subsets, so only add a reference the first time
10165 * it does so we don't have to keep a count.
10166 */
10167 if (j->anonymous && !j->holds_ref) {
10168 job_log(j, LOG_PERF, "Job created subset: %s", jmr->name);
10169 j->holds_ref = true;
10170 runtime_add_ref();
10171 }
10172
10173 job_log(j, LOG_DEBUG, "Job created a subset named \"%s\"", jmr->name);
10174 return BOOTSTRAP_SUCCESS;
10175 }
10176
10177 job_t
10178 _xpc_domain_import_service(jobmgr_t jm, launch_data_t pload)
10179 {
10180 jobmgr_t where2put = NULL;
10181
10182 if (launch_data_get_type(pload) != LAUNCH_DATA_DICTIONARY) {
10183 errno = EINVAL;
10184 return NULL;
10185 }
10186
10187 launch_data_t ldlabel = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_LABEL);
10188 if (!ldlabel || launch_data_get_type(ldlabel) != LAUNCH_DATA_STRING) {
10189 errno = EINVAL;
10190 return NULL;
10191 }
10192
10193 const char *label = launch_data_get_string(ldlabel);
10194 jobmgr_log(jm, LOG_DEBUG, "Importing service: %s", label);
10195
10196 launch_data_t destname = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_XPCDOMAIN);
10197 if (destname) {
10198 bool supported_domain = false;
10199
10200 if (launch_data_get_type(destname) == LAUNCH_DATA_STRING) {
10201 const char *str = launch_data_get_string(destname);
10202 if (strcmp(str, XPC_DOMAIN_TYPE_SYSTEM) == 0) {
10203 where2put = _s_xpc_system_domain;
10204 } else if (strcmp(str, XPC_DOMAIN_TYPE_PERUSER) == 0) {
10205 where2put = jobmgr_find_xpc_per_user_domain(jm, jm->req_euid);
10206 supported_domain = true;
10207 } else if (strcmp(str, XPC_DOMAIN_TYPE_PERSESSION) == 0) {
10208 where2put = jobmgr_find_xpc_per_session_domain(jm, jm->req_asid);
10209 } else {
10210 jobmgr_log(jm, LOG_ERR, "Invalid XPC domain type: %s", str);
10211 errno = EINVAL;
10212 }
10213 } else {
10214 jobmgr_log(jm, LOG_ERR, "XPC domain type is not a string.");
10215 errno = EINVAL;
10216 }
10217
10218 if (where2put && !supported_domain) {
10219 launch_data_t mi = NULL;
10220 if ((mi = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_MULTIPLEINSTANCES))) {
10221 if (launch_data_get_type(mi) == LAUNCH_DATA_BOOL && launch_data_get_bool(mi)) {
10222 jobmgr_log(where2put, LOG_ERR, "Multiple-instance services are not supported in this domain.");
10223 where2put = NULL;
10224 errno = EINVAL;
10225 }
10226 }
10227 }
10228 } else {
10229 where2put = jm;
10230 }
10231
10232 job_t j = NULL;
10233 if (where2put) {
10234 /* Gross. If the service already exists in a singleton domain, then
10235 * jobmgr_import2() will return the existing job. But if we fail to alias
10236 * this job, we will normally want to remove it. But if we did not create
10237 * it in the first place, then we need to avoid removing it. So check
10238 * errno against EEXIST in the success case and if it's EEXIST, then do
10239 * not remove the original job in the event of a failed alias.
10240 *
10241 * This really needs to be re-thought, but I think it'll require a larger
10242 * evaluation of launchd's data structures. Right now, once a job is
10243 * imported into a singleton domain, it won't be removed until the system
10244 * shuts down, but that may not always be true. If it ever changes, we'll
10245 * have a problem because we'll have to account for all existing aliases
10246 * and clean them up somehow. Or just start ref-counting. I knew this
10247 * aliasing stuff would be trouble...
10248 *
10249 * <rdar://problem/10646503>
10250 */
10251 jobmgr_log(where2put, LOG_DEBUG, "Importing service...");
10252
10253 errno = 0;
10254 if ((j = jobmgr_import2(where2put, pload))) {
10255 bool created = (errno != EEXIST);
10256 j->xpc_service = true;
10257
10258 if (where2put->xpc_singleton) {
10259 /* If the service was destined for one of the global domains,
10260 * then we have to alias it into our local domain to reserve the
10261 * name.
10262 */
10263 job_t ja = NULL;
10264 if (!(ja = job_new_alias(jm, j))) {
10265 /* If we failed to alias the job because of a conflict over
10266 * the label, then we remove it from the global domain. We
10267 * don't want to risk having imported a malicious job into
10268 * one of the global domains.
10269 */
10270 if (errno != EEXIST) {
10271 job_log(j, LOG_ERR, "Failed to alias job into: %s: %d: %s", where2put->name, errno, strerror(errno));
10272 } else {
10273 errno = 0;
10274 }
10275
10276 if (created) {
10277 jobmgr_log(jm, LOG_WARNING, "Singleton service already existed in job-local namespace. Removing: %s", j->label);
10278 job_remove(j);
10279 }
10280
10281 j = NULL;
10282 } else {
10283 jobmgr_log(jm, LOG_DEBUG, "Aliased service into local domain: %s", j->label);
10284 (void)job_dispatch(j, false);
10285 ja->xpc_service = true;
10286 j = ja;
10287 }
10288 } else {
10289 (void)job_dispatch(j, false);
10290 }
10291 }
10292 } else {
10293 jobmgr_log(jm, LOG_DEBUG, "Could not find destination for service: %s", label);
10294 }
10295
10296 return j;
10297 }
10298
10299 int
10300 _xpc_domain_import_services(job_t j, launch_data_t services)
10301 {
10302 int error = EINVAL;
10303 if (launch_data_get_type(services) != LAUNCH_DATA_ARRAY) {
10304 return error;
10305 }
10306
10307 size_t i = 0;
10308 size_t c = launch_data_array_get_count(services);
10309 jobmgr_log(j->mgr, LOG_DEBUG, "Importing new services: %lu", c);
10310
10311 for (i = 0; i < c; i++) {
10312 jobmgr_log(j->mgr, LOG_DEBUG, "Importing service at index: %lu", i);
10313
10314 job_t nj = NULL;
10315 launch_data_t ploadi = launch_data_array_get_index(services, i);
10316 if (!(nj = _xpc_domain_import_service(j->mgr, ploadi))) {
10317 if (!j->mgr->session_initialized && errno) {
10318 /* Service import failures are only fatal if the domain is being
10319 * initialized. If we're extending the domain, we can run into
10320 * errors with services already existing, so we just ignore them.
10321 * In the case of a domain extension, we don't want to halt the
10322 * operation if we run into an error with one service.
10323 *
10324 * <rdar://problem/10842779>
10325 */
10326 jobmgr_log(j->mgr, LOG_ERR, "Failed to import service at index: %lu: %d: %s", i, errno, strerror(errno));
10327 error = errno;
10328 break;
10329 }
10330 } else {
10331 jobmgr_log(j->mgr, LOG_DEBUG, "Imported service: %s", nj->label);
10332 }
10333 }
10334
10335 if (i == c) {
10336 error = 0;
10337 }
10338
10339 return error;
10340 }
10341
10342 kern_return_t
10343 xpc_domain_import2(job_t j, mach_port_t reqport, mach_port_t dport)
10344 {
10345 if (unlikely(!pid1_magic)) {
10346 job_log(j, LOG_ERR, "XPC domains may only reside in PID 1.");
10347 return BOOTSTRAP_NOT_PRIVILEGED;
10348 }
10349 if (!j || !MACH_PORT_VALID(reqport)) {
10350 return BOOTSTRAP_UNKNOWN_SERVICE;
10351 }
10352 if (root_jobmgr->shutting_down) {
10353 jobmgr_log(root_jobmgr, LOG_ERR, "Attempt to create new domain while shutting down.");
10354 return BOOTSTRAP_NOT_PRIVILEGED;
10355 }
10356 if (!j->xpc_bootstrapper) {
10357 job_log(j, LOG_ERR, "Attempt to create new XPC domain by unprivileged job.");
10358 return BOOTSTRAP_NOT_PRIVILEGED;
10359 }
10360
10361 kern_return_t kr = BOOTSTRAP_NO_MEMORY;
10362 /* All XPC domains are children of the root job manager. What we're creating
10363 * here is really just a skeleton. By creating it, we're adding reqp to our
10364 * port set. It will have two messages on it. The first specifies the
10365 * environment of the originator. This is so we can cache it and hand it to
10366 * xpcproxy to bootstrap our services. The second is the set of jobs that is
10367 * to be bootstrapped in.
10368 */
10369 jobmgr_t jm = jobmgr_new(root_jobmgr, reqport, dport, false, NULL, true, MACH_PORT_NULL);
10370 if (job_assumes(j, jm != NULL)) {
10371 jm->properties |= BOOTSTRAP_PROPERTY_XPC_DOMAIN;
10372 jm->shortdesc = "private";
10373 kr = BOOTSTRAP_SUCCESS;
10374 }
10375
10376 return kr;
10377 }
10378
10379 kern_return_t
10380 xpc_domain_set_environment(job_t j, mach_port_t rp, mach_port_t bsport, mach_port_t excport, vm_offset_t ctx, mach_msg_type_number_t ctx_sz)
10381 {
10382 if (!j) {
10383 /* Due to the whacky nature of XPC service bootstrapping, we can end up
10384 * getting this message long after the requesting process has gone away.
10385 * See <rdar://problem/8593143>.
10386 */
10387 return BOOTSTRAP_UNKNOWN_SERVICE;
10388 }
10389
10390 jobmgr_t jm = j->mgr;
10391 if (!(jm->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) {
10392 return BOOTSTRAP_NOT_PRIVILEGED;
10393 }
10394
10395 if (jm->req_asport != MACH_PORT_NULL) {
10396 return BOOTSTRAP_NOT_PRIVILEGED;
10397 }
10398
10399 struct ldcred *ldc = runtime_get_caller_creds();
10400 struct proc_bsdinfowithuniqid proc;
10401 if (proc_pidinfo(ldc->pid, PROC_PIDT_BSDINFOWITHUNIQID, 1, &proc, PROC_PIDT_BSDINFOWITHUNIQID_SIZE) == 0) {
10402 if (errno != ESRCH) {
10403 (void)jobmgr_assumes_zero(jm, errno);
10404 }
10405
10406 jm->error = errno;
10407 jobmgr_remove(jm);
10408 RETURN_NO_MEMORY();
10409 }
10410 #ifdef notyet
10411 #if !TARGET_OS_EMBEDDED
10412 if (jobmgr_assumes_zero(jm, audit_session_port(ldc->asid, &jm->req_asport)) != 0) {
10413 jm->error = EPERM;
10414 jobmgr_remove(jm);
10415 job_log(j, LOG_ERR, "Failed to get port for ASID: %u", ldc->asid);
10416 return BOOTSTRAP_NOT_PRIVILEGED;
10417 }
10418 #else
10419 jm->req_asport = MACH_PORT_DEAD;
10420 #endif
10421 #endif
10422 struct waiting4attach *w4ai = NULL;
10423 struct waiting4attach *w4ait = NULL;
10424 LIST_FOREACH_SAFE(w4ai, &_launchd_domain_waiters, le, w4ait) {
10425 if (w4ai->dest == ldc->pid) {
10426 jobmgr_log(jm, LOG_DEBUG, "Migrating attach for: %s", w4ai->name);
10427 LIST_REMOVE(w4ai, le);
10428 LIST_INSERT_HEAD(&jm->attaches, w4ai, le);
10429 w4ai->dest = 0;
10430 }
10431 }
10432
10433 (void)snprintf(jm->name_init, NAME_MAX, "com.apple.xpc.domain.%s.%d", proc.pbsd.pbi_comm, ldc->pid);
10434 strlcpy(jm->owner, proc.pbsd.pbi_comm, sizeof(jm->owner));
10435 jm->req_bsport = bsport;
10436 jm->req_excport = excport;
10437 jm->req_rport = rp;
10438 jm->req_ctx = ctx;
10439 jm->req_ctx_sz = ctx_sz;
10440 jm->req_pid = ldc->pid;
10441 jm->req_euid = ldc->euid;
10442 jm->req_egid = ldc->egid;
10443 jm->req_asid = ldc->asid;
10444 jm->req_uniqueid = proc.p_uniqidentifier.p_uniqueid;
10445
10446 return KERN_SUCCESS;
10447 }
10448
10449 kern_return_t
10450 xpc_domain_load_services(job_t j, vm_offset_t services_buff, mach_msg_type_number_t services_sz)
10451 {
10452 if (!j) {
10453 return BOOTSTRAP_UNKNOWN_SERVICE;
10454 }
10455
10456 job_t rootj = jobmgr_find_by_pid(root_jobmgr, j->p, false);
10457 if (!(rootj && rootj->xpc_bootstrapper)) {
10458 job_log(j, LOG_ERR, "Attempt to load services into XPC domain by unprivileged job.");
10459 return BOOTSTRAP_NOT_PRIVILEGED;
10460 }
10461
10462 // This is just for XPC domains (for now).
10463 if (!(j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) {
10464 return BOOTSTRAP_NOT_PRIVILEGED;
10465 }
10466 if (j->mgr->session_initialized) {
10467 jobmgr_log(j->mgr, LOG_ERR, "Attempt to initialize an already-initialized XPC domain.");
10468 return BOOTSTRAP_NOT_PRIVILEGED;
10469 }
10470
10471 size_t offset = 0;
10472 launch_data_t services = launch_data_unpack((void *)services_buff, services_sz, NULL, 0, &offset, NULL);
10473 if (!services) {
10474 RETURN_NO_MEMORY();
10475 }
10476
10477 int error = _xpc_domain_import_services(j, services);
10478 if (error) {
10479 j->mgr->error = error;
10480 jobmgr_log(j->mgr, LOG_ERR, "Obliterating domain.");
10481 jobmgr_remove(j->mgr);
10482 } else {
10483 j->mgr->session_initialized = true;
10484 (void)jobmgr_assumes_zero(j->mgr, xpc_call_wakeup(j->mgr->req_rport, BOOTSTRAP_SUCCESS));
10485 j->mgr->req_rport = MACH_PORT_NULL;
10486
10487 /* Returning a failure code will destroy the message, whereas returning
10488 * success will not, so we need to clean up here.
10489 */
10490 mig_deallocate(services_buff, services_sz);
10491 error = BOOTSTRAP_SUCCESS;
10492 }
10493
10494 return error;
10495 }
10496
10497 kern_return_t
10498 xpc_domain_check_in(job_t j, mach_port_t *bsport, mach_port_t *sbsport,
10499 mach_port_t *excport, mach_port_t *asport, uint32_t *uid, uint32_t *gid,
10500 int32_t *asid, vm_offset_t *ctx, mach_msg_type_number_t *ctx_sz)
10501 {
10502 if (!jobmgr_assumes(root_jobmgr, j != NULL)) {
10503 return BOOTSTRAP_UNKNOWN_SERVICE;
10504 }
10505 jobmgr_t jm = j->mgr;
10506 if (!(jm->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) {
10507 return BOOTSTRAP_NOT_PRIVILEGED;
10508 }
10509
10510 if (jm->req_asport == MACH_PORT_NULL) {
10511 return BOOTSTRAP_NOT_PRIVILEGED;
10512 }
10513
10514 *bsport = jm->req_bsport;
10515 *sbsport = root_jobmgr->jm_port;
10516 *excport = jm->req_excport;
10517 if (j->joins_gui_session) {
10518 if (jm->req_gui_asport) {
10519 *asport = jm->req_gui_asport;
10520 } else {
10521 job_log(j, LOG_NOTICE, "No GUI session set for UID of user service. This service may not act properly.");
10522 *asport = jm->req_asport;
10523 }
10524 } else {
10525 *asport = jm->req_asport;
10526 }
10527
10528 *uid = jm->req_euid;
10529 *gid = jm->req_egid;
10530 *asid = jm->req_asid;
10531
10532 *ctx = jm->req_ctx;
10533 *ctx_sz = jm->req_ctx_sz;
10534
10535 return KERN_SUCCESS;
10536 }
10537
10538 kern_return_t
10539 xpc_domain_get_service_name(job_t j, event_name_t name)
10540 {
10541 if (!j) {
10542 RETURN_NO_MEMORY();
10543 }
10544
10545 if (!j->xpc_service) {
10546 jobmgr_log(j->mgr, LOG_ERR, "Attempt to get service name by non-XPC service: %s", j->label);
10547 return BOOTSTRAP_NOT_PRIVILEGED;
10548 }
10549
10550 const char *what2find = j->label;
10551 if (j->dedicated_instance) {
10552 what2find = j->original->label;
10553 }
10554
10555 struct machservice *msi = NULL;
10556 SLIST_FOREACH(msi, &j->machservices, sle) {
10557 if (strcmp(msi->name, what2find) == 0) {
10558 break;
10559 }
10560 }
10561
10562 if (!msi) {
10563 jobmgr_log(j->mgr, LOG_ERR, "Attempt to get service name that does not exist: %s", j->label);
10564 return BOOTSTRAP_UNKNOWN_SERVICE;
10565 }
10566
10567 (void)strlcpy(name, msi->name, sizeof(event_name_t));
10568 return BOOTSTRAP_SUCCESS;
10569 }
10570
10571 #if XPC_LPI_VERSION >= 20111216
10572 kern_return_t
10573 xpc_domain_add_services(job_t j, vm_offset_t services_buff, mach_msg_type_number_t services_sz)
10574 {
10575 if (!j) {
10576 return BOOTSTRAP_UNKNOWN_SERVICE;
10577 }
10578
10579 job_t rootj = jobmgr_find_by_pid(root_jobmgr, j->p, false);
10580 if (!(rootj && rootj->xpc_bootstrapper)) {
10581 job_log(j, LOG_ERR, "Attempt to add service to XPC domain by unprivileged job.");
10582 return BOOTSTRAP_NOT_PRIVILEGED;
10583 }
10584
10585 if (!(j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN)) {
10586 return BOOTSTRAP_NOT_PRIVILEGED;
10587 }
10588
10589 size_t offset = 0;
10590 launch_data_t services = launch_data_unpack((void *)services_buff, services_sz, NULL, 0, &offset, NULL);
10591 if (!services) {
10592 RETURN_NO_MEMORY();
10593 }
10594
10595 int error = _xpc_domain_import_services(j, services);
10596 if (!error) {
10597 mig_deallocate(services_buff, services_sz);
10598 }
10599
10600 return error;
10601 }
10602 #endif
10603
10604 #pragma mark XPC Events
10605 int
10606 xpc_event_find_channel(job_t j, const char *stream, struct machservice **ms)
10607 {
10608 int error = EXNOMEM;
10609 struct machservice *msi = NULL;
10610 SLIST_FOREACH(msi, &j->machservices, sle) {
10611 if (strcmp(stream, msi->name) == 0) {
10612 break;
10613 }
10614 }
10615
10616 if (!msi) {
10617 mach_port_t sp = MACH_PORT_NULL;
10618 msi = machservice_new(j, stream, &sp, false);
10619 if (!msi) {
10620 return EXNOMEM;
10621 }
10622
10623 job_log(j, LOG_DEBUG, "Creating new MachService for stream: %s", stream);
10624 /* Hack to keep this from being publicly accessible through
10625 * bootstrap_look_up().
10626 */
10627 if (!j->dedicated_instance) {
10628 LIST_REMOVE(msi, name_hash_sle);
10629 }
10630 msi->event_channel = true;
10631
10632 /* If we call job_dispatch() here before the audit session for the job
10633 * has been set, we'll end up not watching this service. But we also have
10634 * to take care not to watch the port if the job is active.
10635 *
10636 * See <rdar://problem/10357855>.
10637 */
10638 if (!j->currently_ignored) {
10639 machservice_watch(j, msi);
10640 }
10641
10642 error = 0;
10643 *ms = msi;
10644 } else if (!msi->event_channel) {
10645 job_log(j, LOG_ERR, "This job registered a MachService name identical to the requested event channel name: %s", stream);
10646 error = EEXIST;
10647 } else {
10648 error = 0;
10649 *ms = msi;
10650 }
10651
10652 return error;
10653 }
10654
10655 int
10656 xpc_event_get_event_name(job_t j, xpc_object_t request, xpc_object_t *reply)
10657 {
10658 const char *stream = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_STREAM);
10659 if (!stream) {
10660 return EXINVAL;
10661 }
10662
10663 uint64_t token = xpc_dictionary_get_uint64(request, XPC_EVENT_ROUTINE_KEY_TOKEN);
10664 if (!token) {
10665 return EXINVAL;
10666 }
10667
10668 job_log(j, LOG_DEBUG, "Getting event name for stream/token: %s/0x%zu", stream, token);
10669
10670 int result = ESRCH;
10671 struct externalevent *event = externalevent_find(stream, token);
10672 if (event && j->event_monitor) {
10673 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
10674 xpc_dictionary_set_string(reply2, XPC_EVENT_ROUTINE_KEY_NAME, event->name);
10675 *reply = reply2;
10676
10677 job_log(j, LOG_DEBUG, "Found: %s", event->name);
10678 result = 0;
10679 }
10680
10681 return result;
10682 }
10683
10684 static int
10685 xpc_event_copy_entitlements(job_t j, xpc_object_t request, xpc_object_t *reply)
10686 {
10687 const char *stream = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_STREAM);
10688 if (!stream) {
10689 return EXINVAL;
10690 }
10691
10692 uint64_t token = xpc_dictionary_get_uint64(request, XPC_EVENT_ROUTINE_KEY_TOKEN);
10693 if (!token) {
10694 return EXINVAL;
10695 }
10696
10697 job_log(j, LOG_DEBUG, "Getting entitlements for stream/token: %s/0x%zu", stream, token);
10698
10699 int result = ESRCH;
10700 struct externalevent *event = externalevent_find(stream, token);
10701 if (event && j->event_monitor) {
10702 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
10703 xpc_dictionary_set_value(reply2, XPC_EVENT_ROUTINE_KEY_ENTITLEMENTS, event->entitlements);
10704 *reply = reply2;
10705
10706 job_log(j, LOG_DEBUG, "Found: %s", event->name);
10707 result = 0;
10708 }
10709
10710 return result;
10711 }
10712
10713 // TODO - can be removed with rdar://problem/12666150
10714 #ifndef XPC_EVENT_FLAG_ALLOW_UNMANAGED
10715 #define XPC_EVENT_FLAG_ALLOW_UNMANAGED (1 << 1)
10716 #endif
10717
10718 int
10719 xpc_event_set_event(job_t j, xpc_object_t request, xpc_object_t *reply)
10720 {
10721 const char *stream = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_STREAM);
10722 if (!stream) {
10723 return EXINVAL;
10724 }
10725
10726 const char *key = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_NAME);
10727 if (!key) {
10728 return EXINVAL;
10729 }
10730
10731 xpc_object_t event = xpc_dictionary_get_value(request, XPC_EVENT_ROUTINE_KEY_EVENT);
10732 if (event && xpc_get_type(event) != XPC_TYPE_DICTIONARY) {
10733 return EXINVAL;
10734 }
10735
10736 uint64_t flags = xpc_dictionary_get_uint64(request, XPC_EVENT_ROUTINE_KEY_FLAGS);
10737
10738 /* Don't allow events to be set for anonymous jobs unless specifically
10739 * requested in the flags. Only permit this for internal development.
10740 */
10741 if (j->anonymous && ((flags & XPC_EVENT_FLAG_ALLOW_UNMANAGED) == 0 || !launchd_apple_internal)) {
10742 job_log(j, LOG_ERR, "Unmanaged jobs may not make XPC Events requests.");
10743 return EPERM;
10744 }
10745
10746 job_log(j, LOG_DEBUG, "%s event for stream/key: %s/%s", event ? "Setting" : "Removing", stream, key);
10747
10748 struct externalevent *eei = NULL;
10749 LIST_FOREACH(eei, &j->events, job_le) {
10750 /* If the event for the given key already exists for the job, we need to
10751 * remove the old one first.
10752 */
10753 if (strcmp(eei->name, key) == 0 && strcmp(eei->sys->name, stream) == 0) {
10754 job_log(j, LOG_DEBUG, "Event exists. Removing.");
10755 externalevent_delete(eei);
10756 break;
10757 }
10758 }
10759
10760 int result = EXNOMEM;
10761 if (event) {
10762 struct eventsystem *es = eventsystem_find(stream);
10763 if (!es) {
10764 job_log(j, LOG_DEBUG, "Creating stream.");
10765 es = eventsystem_new(stream);
10766 }
10767
10768 if (es) {
10769 job_log(j, LOG_DEBUG, "Adding event.");
10770 if (externalevent_new(j, es, key, event, flags)) {
10771 job_log(j, LOG_DEBUG, "Added new event for key: %s", key);
10772 result = 0;
10773 } else {
10774 job_log(j, LOG_ERR, "Could not create event for key: %s", key);
10775 }
10776 } else {
10777 job_log(j, LOG_ERR, "Event stream could not be created: %s", stream);
10778 }
10779 } else {
10780 /* If the event was NULL, then we just remove it and return. */
10781 result = 0;
10782 }
10783
10784 if (result == 0) {
10785 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
10786 *reply = reply2;
10787 }
10788
10789 return result;
10790 }
10791
10792 int
10793 xpc_event_copy_event(job_t j, xpc_object_t request, xpc_object_t *reply)
10794 {
10795 const char *stream = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_STREAM);
10796 const char *key = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_NAME);
10797
10798 bool all_streams = (stream == NULL);
10799 bool all_events = (key == NULL || strcmp(key, "") == 0); // strcmp for libxpc compatibility
10800 xpc_object_t events = NULL;
10801
10802 if (all_streams && !all_events) {
10803 return EXINVAL;
10804 }
10805
10806 if (all_streams || all_events) {
10807 job_log(j, LOG_DEBUG, "Fetching all events%s%s", stream ? " for stream: " : "", stream ? stream : "");
10808 events = xpc_dictionary_create(NULL, NULL, 0);
10809 } else {
10810 job_log(j, LOG_DEBUG, "Fetching stream/key: %s/%s", stream, key);
10811 }
10812
10813 int result = ESRCH;
10814 struct externalevent *eei = NULL;
10815 LIST_FOREACH(eei, &j->events, job_le) {
10816 if (all_streams) {
10817 xpc_object_t sub = xpc_dictionary_get_value(events, eei->sys->name);
10818 if (sub == NULL) {
10819 sub = xpc_dictionary_create(NULL, NULL, 0);
10820 xpc_dictionary_set_value(events, eei->sys->name, sub);
10821 xpc_release(sub);
10822 }
10823 xpc_dictionary_set_value(sub, eei->name, eei->event);
10824 } else if (strcmp(eei->sys->name, stream) == 0) {
10825 if (all_events) {
10826 xpc_dictionary_set_value(events, eei->name, eei->event);
10827 } else if (strcmp(eei->name, key) == 0) {
10828 job_log(j, LOG_DEBUG, "Found event.");
10829 events = xpc_retain(eei->event);
10830 break;
10831 }
10832 }
10833 }
10834
10835 if (events) {
10836 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
10837 xpc_dictionary_set_value(reply2, XPC_EVENT_ROUTINE_KEY_EVENT, events);
10838 xpc_release(events);
10839
10840 *reply = reply2;
10841 result = 0;
10842 }
10843
10844 return result;
10845 }
10846
10847 int
10848 xpc_event_channel_check_in(job_t j, xpc_object_t request, xpc_object_t *reply)
10849 {
10850 const char *stream = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_STREAM);
10851 if (!stream) {
10852 return EXINVAL;
10853 }
10854
10855 job_log(j, LOG_DEBUG, "Checking in stream: %s", stream);
10856
10857 struct machservice *ms = NULL;
10858 int error = xpc_event_find_channel(j, stream, &ms);
10859 if (error) {
10860 job_log(j, LOG_ERR, "Failed to check in: 0x%x: %s", error, xpc_strerror(error));
10861 } else if (ms->isActive) {
10862 job_log(j, LOG_ERR, "Attempt to check in on event channel multiple times: %s", stream);
10863 error = EBUSY;
10864 } else {
10865 machservice_request_notifications(ms);
10866
10867 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
10868 xpc_dictionary_set_mach_recv(reply2, XPC_EVENT_ROUTINE_KEY_PORT, ms->port);
10869 *reply = reply2;
10870 error = 0;
10871 }
10872
10873 return error;
10874 }
10875
10876 int
10877 xpc_event_channel_look_up(job_t j, xpc_object_t request, xpc_object_t *reply)
10878 {
10879 if (!j->event_monitor) {
10880 return EPERM;
10881 }
10882
10883 const char *stream = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_STREAM);
10884 if (!stream) {
10885 return EXINVAL;
10886 }
10887
10888 uint64_t token = xpc_dictionary_get_uint64(request, XPC_EVENT_ROUTINE_KEY_TOKEN);
10889 if (!token) {
10890 return EXINVAL;
10891 }
10892
10893 job_log(j, LOG_DEBUG, "Looking up channel for stream/token: %s/%zu", stream, token);
10894
10895 struct externalevent *ee = externalevent_find(stream, token);
10896 if (!ee) {
10897 return ESRCH;
10898 }
10899
10900 struct machservice *ms = NULL;
10901 int error = xpc_event_find_channel(ee->job, stream, &ms);
10902 if (!error) {
10903 job_log(j, LOG_DEBUG, "Found event channel port: 0x%x", ms->port);
10904 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
10905 xpc_dictionary_set_mach_send(reply2, XPC_EVENT_ROUTINE_KEY_PORT, ms->port);
10906 *reply = reply2;
10907 error = 0;
10908 } else {
10909 job_log(j, LOG_ERR, "Could not find event channel for stream/token: %s/%zu: 0x%x: %s", stream, token, error, xpc_strerror(error));
10910 }
10911
10912 return error;
10913 }
10914
10915 int
10916 xpc_event_provider_check_in(job_t j, xpc_object_t request, xpc_object_t *reply)
10917 {
10918 if (!j->event_monitor) {
10919 return EPERM;
10920 }
10921
10922 /* This indicates that the event monitor is now safe to signal. This state
10923 * is independent of whether this operation actually succeeds; we just need
10924 * it to ignore SIGUSR1.
10925 */
10926 j->event_monitor_ready2signal = true;
10927
10928 const char *stream = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_STREAM);
10929 if (!stream) {
10930 return EXINVAL;
10931 }
10932
10933 job_log(j, LOG_DEBUG, "Provider checking in for stream: %s", stream);
10934
10935 xpc_object_t events = xpc_array_create(NULL, 0);
10936 struct eventsystem *es = eventsystem_find(stream);
10937 if (!es) {
10938 /* If we had to create the event stream, there were no events, so just
10939 * give back the empty array.
10940 */
10941 job_log(j, LOG_DEBUG, "Creating event stream.");
10942 es = eventsystem_new(stream);
10943 if (!job_assumes(j, es)) {
10944 xpc_release(events);
10945 return EXNOMEM;
10946 }
10947
10948 if (strcmp(stream, "com.apple.launchd.helper") == 0) {
10949 _launchd_support_system = es;
10950 }
10951 } else {
10952 job_log(j, LOG_DEBUG, "Filling event array.");
10953
10954 struct externalevent *ei = NULL;
10955 LIST_FOREACH(ei, &es->events, sys_le) {
10956 xpc_array_set_uint64(events, XPC_ARRAY_APPEND, ei->id);
10957 xpc_array_append_value(events, ei->event);
10958 }
10959 }
10960
10961 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
10962 xpc_dictionary_set_value(reply2, XPC_EVENT_ROUTINE_KEY_EVENTS, events);
10963 xpc_release(events);
10964 *reply = reply2;
10965
10966 return 0;
10967 }
10968
10969 int
10970 xpc_event_provider_set_state(job_t j, xpc_object_t request, xpc_object_t *reply)
10971 {
10972 job_t other_j = NULL;
10973
10974 if (!j->event_monitor) {
10975 return EPERM;
10976 }
10977
10978 const char *stream = xpc_dictionary_get_string(request, XPC_EVENT_ROUTINE_KEY_STREAM);
10979 if (!stream) {
10980 return EXINVAL;
10981 }
10982
10983 uint64_t token = xpc_dictionary_get_uint64(request, XPC_EVENT_ROUTINE_KEY_TOKEN);
10984 if (!token) {
10985 return EXINVAL;
10986 }
10987
10988 bool state = false;
10989 xpc_object_t xstate = xpc_dictionary_get_value(request, XPC_EVENT_ROUTINE_KEY_STATE);
10990 if (!xstate || xpc_get_type(xstate) != XPC_TYPE_BOOL) {
10991 return EXINVAL;
10992 } else {
10993 state = xpc_bool_get_value(xstate);
10994 }
10995
10996 job_log(j, LOG_DEBUG, "Setting event state to %s for stream/token: %s/%zu", state ? "true" : "false", stream, token);
10997
10998 struct externalevent *ei = externalevent_find(stream, token);
10999 if (!ei) {
11000 job_log(j, LOG_ERR, "Could not find stream/token: %s/%zu", stream, token);
11001 return ESRCH;
11002 }
11003
11004 other_j = ei->job;
11005 ei->state = state;
11006
11007 if (ei->internal) {
11008 job_log(ei->job, LOG_NOTICE, "Job should be able to exec(3) now.");
11009 ei->job->waiting4ok = false;
11010 externalevent_delete(ei);
11011 }
11012
11013 (void)job_dispatch(other_j, false);
11014
11015 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
11016 *reply = reply2;
11017
11018 return 0;
11019 }
11020
11021 bool
11022 xpc_event_demux(mach_port_t p, xpc_object_t request, xpc_object_t *reply)
11023 {
11024 uint64_t op = xpc_dictionary_get_uint64(request, XPC_EVENT_ROUTINE_KEY_OP);
11025 if (!op) {
11026 return false;
11027 }
11028
11029 audit_token_t token;
11030 xpc_dictionary_get_audit_token(request, &token);
11031 runtime_record_caller_creds(&token);
11032
11033 struct ldcred *ldc = runtime_get_caller_creds();
11034 job_t j = managed_job(ldc->pid);
11035 if (!j) {
11036 j = job_mig_intran(p);
11037 if (!j) {
11038 op = -1;
11039 }
11040 }
11041
11042 job_log(j, LOG_DEBUG, "Incoming XPC event request: %zu", op);
11043
11044 int error = -1;
11045 switch (op) {
11046 case XPC_EVENT_GET_NAME:
11047 error = xpc_event_get_event_name(j, request, reply);
11048 break;
11049 case XPC_EVENT_SET:
11050 error = xpc_event_set_event(j, request, reply);
11051 break;
11052 case XPC_EVENT_COPY:
11053 error = xpc_event_copy_event(j, request, reply);
11054 break;
11055 case XPC_EVENT_CHECK_IN:
11056 error = xpc_event_channel_check_in(j, request, reply);
11057 break;
11058 case XPC_EVENT_LOOK_UP:
11059 error = xpc_event_channel_look_up(j, request, reply);
11060 break;
11061 case XPC_EVENT_PROVIDER_CHECK_IN:
11062 error = xpc_event_provider_check_in(j, request, reply);
11063 break;
11064 case XPC_EVENT_PROVIDER_SET_STATE:
11065 error = xpc_event_provider_set_state(j, request, reply);
11066 break;
11067 case XPC_EVENT_COPY_ENTITLEMENTS:
11068 error = xpc_event_copy_entitlements(j, request, reply);
11069 break;
11070 case -1:
11071 error = EINVAL;
11072 break;
11073 default:
11074 job_log(j, LOG_ERR, "Bogus opcode.");
11075 error = EDOM;
11076 }
11077
11078 if (error) {
11079 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
11080 xpc_dictionary_set_uint64(reply2, XPC_EVENT_ROUTINE_KEY_ERROR, error);
11081 *reply = reply2;
11082 }
11083
11084 return true;
11085 }
11086
11087 static uint64_t
11088 xpc_get_jetsam_entitlement(const char *key)
11089 {
11090 uint64_t entitlement = 0;
11091
11092 audit_token_t *token = runtime_get_caller_token();
11093 xpc_object_t value = xpc_copy_entitlement_for_token(key, token);
11094 if (value) {
11095 if (xpc_get_type(value) == XPC_TYPE_UINT64) {
11096 entitlement = xpc_uint64_get_value(value);
11097 }
11098
11099 xpc_release(value);
11100 }
11101
11102 return entitlement;
11103 }
11104
11105 int
11106 xpc_process_set_jetsam_band(job_t j, xpc_object_t request, xpc_object_t *reply)
11107 {
11108 if (!j) {
11109 return EINVAL;
11110 }
11111
11112 const char *label = xpc_dictionary_get_string(request, XPC_PROCESS_ROUTINE_KEY_LABEL);
11113 if (!label) {
11114 return EXINVAL;
11115 }
11116
11117 xpc_jetsam_band_t entitled_band = -1;
11118 xpc_jetsam_band_t requested_band = (xpc_jetsam_band_t)xpc_dictionary_get_uint64(request, XPC_PROCESS_ROUTINE_KEY_PRIORITY_BAND);
11119 if (!requested_band) {
11120 return EXINVAL;
11121 }
11122
11123 if (!(requested_band >= XPC_JETSAM_BAND_SUSPENDED && requested_band < XPC_JETSAM_BAND_LAST)) {
11124 return EXINVAL;
11125 }
11126
11127 uint64_t rcdata = xpc_dictionary_get_uint64(request, XPC_PROCESS_ROUTINE_KEY_RCDATA);
11128
11129 job_t tj = job_find(root_jobmgr, label);
11130 if (!tj) {
11131 return EXSRCH;
11132 }
11133
11134 boolean_t allow = false;
11135 if (j->embedded_god) {
11136 allow = true;
11137 } else {
11138 entitled_band = xpc_get_jetsam_entitlement("com.apple.private.jetsam.modify-priority");
11139 if (entitled_band >= requested_band) {
11140 allow = true;
11141 }
11142 }
11143
11144 if (!allow) {
11145 if (launchd_no_jetsam_perm_check) {
11146 job_log(j, LOG_NOTICE, "Jetsam priority checks disabled; allowing job to set priority: %d", requested_band);
11147 } else {
11148 job_log(j, LOG_ERR, "Job cannot decrease Jetsam priority band (requested/maximum): %d/%d", requested_band, entitled_band);
11149 return EPERM;
11150 }
11151 }
11152
11153 job_log(j, LOG_INFO, "Setting Jetsam band: %d.", requested_band);
11154 job_update_jetsam_properties(tj, requested_band, rcdata);
11155
11156 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
11157 *reply = reply2;
11158
11159 return 0;
11160 }
11161
11162 int
11163 xpc_process_set_jetsam_memory_limit(job_t j, xpc_object_t request, xpc_object_t *reply)
11164 {
11165 if (!j) {
11166 return EINVAL;
11167 }
11168
11169 const char *label = xpc_dictionary_get_string(request, XPC_PROCESS_ROUTINE_KEY_LABEL);
11170 if (!label) {
11171 return EXINVAL;
11172 }
11173
11174 int32_t entitlement_limit = 0;
11175 int32_t requested_limit = (int32_t)xpc_dictionary_get_uint64(request, XPC_PROCESS_ROUTINE_KEY_MEMORY_LIMIT);
11176
11177 job_t tj = job_find(root_jobmgr, label);
11178 if (!tj) {
11179 return EXSRCH;
11180 }
11181
11182 boolean_t allow = false;
11183 if (j->embedded_god) {
11184 allow = true;
11185 } else {
11186 entitlement_limit = (int32_t)xpc_get_jetsam_entitlement("com.apple.private.jetsam.memory_limit");
11187 if (entitlement_limit >= requested_limit) {
11188 allow = true;
11189 }
11190 }
11191
11192 if (!allow) {
11193 if (launchd_no_jetsam_perm_check) {
11194 job_log(j, LOG_NOTICE, "Jetsam priority checks disabled; allowing job to set memory limit: %d", requested_limit);
11195 } else {
11196 job_log(j, LOG_ERR, "Job cannot set Jetsam memory limit (requested/maximum): %d/%d", requested_limit, entitlement_limit);
11197 return EPERM;
11198 }
11199 }
11200
11201 job_log(j, LOG_INFO, "Setting Jetsam memory limit: %d.", requested_limit);
11202 job_update_jetsam_memory_limit(tj, requested_limit);
11203
11204 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
11205 *reply = reply2;
11206
11207 return 0;
11208 }
11209
11210 static jobmgr_t
11211 _xpc_process_find_target_manager(job_t j, xpc_service_type_t type, pid_t pid)
11212 {
11213 jobmgr_t target = NULL;
11214 if (type == XPC_SERVICE_TYPE_BUNDLED) {
11215 job_log(j, LOG_DEBUG, "Bundled service. Searching for XPC domains for PID: %d", pid);
11216
11217 jobmgr_t jmi = NULL;
11218 SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) {
11219 if (jmi->req_pid && jmi->req_pid == pid) {
11220 jobmgr_log(jmi, LOG_DEBUG, "Found job manager for PID.");
11221 target = jmi;
11222 break;
11223 }
11224 }
11225 } else if (type == XPC_SERVICE_TYPE_LAUNCHD || type == XPC_SERVICE_TYPE_APP) {
11226 target = j->mgr;
11227 }
11228
11229 return target;
11230 }
11231
11232 static int
11233 xpc_process_attach(job_t j, xpc_object_t request, xpc_object_t *reply)
11234 {
11235 if (!j) {
11236 return EINVAL;
11237 }
11238
11239 audit_token_t *token = runtime_get_caller_token();
11240 xpc_object_t entitlement = xpc_copy_entitlement_for_token(XPC_SERVICE_ENTITLEMENT_ATTACH, token);
11241 if (!entitlement) {
11242 job_log(j, LOG_ERR, "Job does not have entitlement: %s", XPC_SERVICE_ENTITLEMENT_ATTACH);
11243 return EPERM;
11244 }
11245
11246 if (entitlement != XPC_BOOL_TRUE) {
11247 char *desc = xpc_copy_description(entitlement);
11248 job_log(j, LOG_ERR, "Job has bad value for entitlement: %s:\n%s", XPC_SERVICE_ENTITLEMENT_ATTACH, desc);
11249 free(desc);
11250
11251 xpc_release(entitlement);
11252 return EPERM;
11253 }
11254
11255 const char *name = xpc_dictionary_get_string(request, XPC_PROCESS_ROUTINE_KEY_NAME);
11256 if (!name) {
11257 return EXINVAL;
11258 }
11259
11260 xpc_service_type_t type = xpc_dictionary_get_int64(request, XPC_PROCESS_ROUTINE_KEY_TYPE);
11261 if (!type) {
11262 return EXINVAL;
11263 }
11264
11265 mach_port_t port = xpc_dictionary_copy_mach_send(request, XPC_PROCESS_ROUTINE_KEY_NEW_INSTANCE_PORT);
11266 if (!MACH_PORT_VALID(port)) {
11267 return EXINVAL;
11268 }
11269
11270 pid_t pid = xpc_dictionary_get_int64(request, XPC_PROCESS_ROUTINE_KEY_HANDLE);
11271
11272 job_log(j, LOG_DEBUG, "Attaching to service: %s", name);
11273
11274 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
11275 jobmgr_t target = _xpc_process_find_target_manager(j, type, pid);
11276 if (target) {
11277 jobmgr_log(target, LOG_DEBUG, "Found target job manager for service: %s", name);
11278 (void)jobmgr_assumes(target, waiting4attach_new(target, name, port, 0, type));
11279
11280 /* HACK: This is awful. For legacy reasons, launchd job labels are all
11281 * stored in a global namespace, which is stored in the root job
11282 * manager. But XPC domains have a per-domain namespace. So if we're
11283 * looking for a legacy launchd job, we have to redirect any attachment
11284 * attempts to the root job manager to find existing instances.
11285 *
11286 * But because we store attachments on a per-job manager basis, we have
11287 * to create the new attachment in the actual target job manager, hence
11288 * why we change the target only after we've created the attachment.
11289 */
11290 if (strcmp(target->name, VPROCMGR_SESSION_AQUA) == 0) {
11291 target = root_jobmgr;
11292 }
11293
11294 job_t existing = job_find(target, name);
11295 if (existing && existing->p) {
11296 job_log(existing, LOG_DEBUG, "Found existing instance of service.");
11297 xpc_dictionary_set_int64(reply2, XPC_PROCESS_ROUTINE_KEY_PID, existing->p);
11298 } else {
11299 xpc_dictionary_set_uint64(reply2, XPC_PROCESS_ROUTINE_KEY_ERROR, ESRCH);
11300 }
11301 } else if (type == XPC_SERVICE_TYPE_BUNDLED) {
11302 (void)job_assumes(j, waiting4attach_new(target, name, port, pid, type));
11303 xpc_dictionary_set_uint64(reply2, XPC_PROCESS_ROUTINE_KEY_ERROR, ESRCH);
11304 } else {
11305 xpc_dictionary_set_uint64(reply2, XPC_PROCESS_ROUTINE_KEY_ERROR, EXSRCH);
11306 }
11307
11308 *reply = reply2;
11309 return 0;
11310 }
11311
11312 static int
11313 xpc_process_detach(job_t j, xpc_object_t request, xpc_object_t *reply __unused)
11314 {
11315 if (!j) {
11316 return EINVAL;
11317 }
11318
11319 const char *name = xpc_dictionary_get_string(request, XPC_PROCESS_ROUTINE_KEY_NAME);
11320 if (!name) {
11321 return EXINVAL;
11322 }
11323
11324 xpc_service_type_t type = xpc_dictionary_get_int64(request, XPC_PROCESS_ROUTINE_KEY_TYPE);
11325 if (!type) {
11326 return EXINVAL;
11327 }
11328
11329 job_log(j, LOG_DEBUG, "Deatching from service: %s", name);
11330
11331 pid_t pid = xpc_dictionary_get_int64(request, XPC_PROCESS_ROUTINE_KEY_PID);
11332 jobmgr_t target = _xpc_process_find_target_manager(j, type, pid);
11333 if (target) {
11334 jobmgr_log(target, LOG_DEBUG, "Found target job manager for service: %s", name);
11335
11336 struct waiting4attach *w4ai = NULL;
11337 struct waiting4attach *w4ait = NULL;
11338 LIST_FOREACH_SAFE(w4ai, &target->attaches, le, w4ait) {
11339 if (strcmp(name, w4ai->name) == 0) {
11340 jobmgr_log(target, LOG_DEBUG, "Found attachment. Deleting.");
11341 waiting4attach_delete(target, w4ai);
11342 break;
11343 }
11344 }
11345 }
11346
11347 return 0;
11348 }
11349
11350 static int
11351 xpc_process_get_properties(job_t j, xpc_object_t request, xpc_object_t *reply)
11352 {
11353 if (j->anonymous) {
11354 /* Total hack. libxpc will send requests to the pipe created out of the
11355 * process' bootstrap port, so when job_mig_intran() tries to resolve
11356 * the process into a job, it'll wind up creating an anonymous job if
11357 * the requestor was an XPC service, whose job manager is an XPC domain.
11358 */
11359 pid_t pid = j->p;
11360 jobmgr_t jmi = NULL;
11361 SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) {
11362 if ((j = jobmgr_find_by_pid(jmi, pid, false))) {
11363 break;
11364 }
11365 }
11366 }
11367
11368 if (!j || j->anonymous) {
11369 return EXINVAL;
11370 }
11371
11372 struct waiting4attach *w4a = waiting4attach_find(j->mgr, j);
11373 if (!w4a) {
11374 return EXINVAL;
11375 }
11376
11377 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
11378 xpc_dictionary_set_uint64(reply2, XPC_PROCESS_ROUTINE_KEY_TYPE, w4a->type);
11379 xpc_dictionary_set_mach_send(reply2, XPC_PROCESS_ROUTINE_KEY_NEW_INSTANCE_PORT, w4a->port);
11380 if (j->prog) {
11381 xpc_dictionary_set_string(reply2, XPC_PROCESS_ROUTINE_KEY_PATH, j->prog);
11382 } else {
11383 xpc_dictionary_set_string(reply2, XPC_PROCESS_ROUTINE_KEY_PATH, j->argv[0]);
11384 }
11385
11386 if (j->argv) {
11387 xpc_object_t xargv = xpc_array_create(NULL, 0);
11388
11389 size_t i = 0;
11390 for (i = 0; i < j->argc; i++) {
11391 if (j->argv[i]) {
11392 xpc_array_set_string(xargv, XPC_ARRAY_APPEND, j->argv[i]);
11393 }
11394 }
11395
11396 xpc_dictionary_set_value(reply2, XPC_PROCESS_ROUTINE_KEY_ARGV, xargv);
11397 xpc_release(xargv);
11398 }
11399
11400 *reply = reply2;
11401 return 0;
11402 }
11403
11404 static int
11405 xpc_process_service_kill(job_t j, xpc_object_t request, xpc_object_t *reply)
11406 {
11407 #if XPC_LPI_VERSION >= 20130426
11408 if (!j) {
11409 return ESRCH;
11410 }
11411
11412 jobmgr_t jm = _xpc_process_find_target_manager(j, XPC_SERVICE_TYPE_BUNDLED, j->p);
11413 if (!jm) {
11414 return ENOENT;
11415 }
11416
11417 const char *name = xpc_dictionary_get_string(request, XPC_PROCESS_ROUTINE_KEY_NAME);
11418 if (!name) {
11419 return EINVAL;
11420 }
11421
11422 int64_t whichsig = xpc_dictionary_get_int64(request, XPC_PROCESS_ROUTINE_KEY_SIGNAL);
11423 if (!whichsig) {
11424 return EINVAL;
11425 }
11426
11427 job_t j2kill = job_find(jm, name);
11428 if (!j2kill) {
11429 return ESRCH;
11430 }
11431
11432 if (j2kill->alias) {
11433 // Only allow for private instances to be killed.
11434 return EPERM;
11435 }
11436
11437 struct proc_bsdshortinfo proc;
11438 if (proc_pidinfo(j2kill->p, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) {
11439 if (errno != ESRCH) {
11440 (void)jobmgr_assumes_zero(root_jobmgr, errno);
11441 }
11442
11443 return errno;
11444 }
11445
11446 struct ldcred *ldc = runtime_get_caller_creds();
11447 if (proc.pbsi_uid != ldc->euid) {
11448 // Do not allow non-root to kill RoleAccount services running as a
11449 // different user.
11450 return EPERM;
11451 }
11452
11453 if (!j2kill->p) {
11454 return EALREADY;
11455 }
11456
11457 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
11458 if (!reply2) {
11459 return EINVAL;
11460 }
11461
11462 int error = 0;
11463 int ret = kill(j2kill->p, whichsig);
11464 if (ret) {
11465 error = errno;
11466 }
11467
11468 xpc_dictionary_set_int64(reply2, XPC_PROCESS_ROUTINE_KEY_ERROR, error);
11469 *reply = reply2;
11470 return 0;
11471 #else
11472 return ENOTSUP;
11473 #endif
11474 }
11475
11476 bool
11477 xpc_process_demux(mach_port_t p, xpc_object_t request, xpc_object_t *reply)
11478 {
11479 uint64_t op = xpc_dictionary_get_uint64(request, XPC_PROCESS_ROUTINE_KEY_OP);
11480 if (!op) {
11481 return false;
11482 }
11483
11484 audit_token_t token;
11485 xpc_dictionary_get_audit_token(request, &token);
11486 runtime_record_caller_creds(&token);
11487
11488 job_t j = job_mig_intran(p);
11489 job_log(j, LOG_DEBUG, "Incoming XPC process request: %zu", op);
11490
11491 int error = -1;
11492 switch (op) {
11493 case XPC_PROCESS_JETSAM_SET_BAND:
11494 error = xpc_process_set_jetsam_band(j, request, reply);
11495 break;
11496 case XPC_PROCESS_JETSAM_SET_MEMORY_LIMIT:
11497 error = xpc_process_set_jetsam_memory_limit(j, request, reply);
11498 break;
11499 case XPC_PROCESS_SERVICE_ATTACH:
11500 error = xpc_process_attach(j, request, reply);
11501 break;
11502 case XPC_PROCESS_SERVICE_DETACH:
11503 error = xpc_process_detach(j, request, reply);
11504 break;
11505 case XPC_PROCESS_SERVICE_GET_PROPERTIES:
11506 error = xpc_process_get_properties(j, request, reply);
11507 break;
11508 case XPC_PROCESS_SERVICE_KILL:
11509 error = xpc_process_service_kill(j, request, reply);
11510 break;
11511 default:
11512 job_log(j, LOG_ERR, "Bogus process opcode.");
11513 error = EDOM;
11514 }
11515
11516 if (error) {
11517 xpc_object_t reply2 = xpc_dictionary_create_reply(request);
11518 if (reply2) {
11519 xpc_dictionary_set_uint64(reply2, XPC_PROCESS_ROUTINE_KEY_ERROR, error);
11520 }
11521
11522 *reply = reply2;
11523 }
11524
11525 return true;
11526 }
11527
11528 kern_return_t
11529 job_mig_kickstart(job_t j, name_t targetlabel, pid_t *out_pid, unsigned int flags)
11530 {
11531 struct ldcred *ldc = runtime_get_caller_creds();
11532 job_t otherj;
11533
11534 if (!j) {
11535 RETURN_NO_MEMORY();
11536 }
11537
11538 if (unlikely(!(otherj = job_find(NULL, targetlabel)))) {
11539 return BOOTSTRAP_UNKNOWN_SERVICE;
11540 }
11541
11542 #if TARGET_OS_EMBEDDED
11543 bool allow_non_root_kickstart = j->username && otherj->username && (strcmp(j->username, otherj->username) == 0);
11544 #else
11545 bool allow_non_root_kickstart = false;
11546 #endif
11547
11548 if (ldc->euid != 0 && ldc->euid != geteuid() && !allow_non_root_kickstart) {
11549 return BOOTSTRAP_NOT_PRIVILEGED;
11550 }
11551
11552 #if HAVE_SANDBOX
11553 if (unlikely(sandbox_check(ldc->pid, "job-creation", SANDBOX_FILTER_NONE) > 0)) {
11554 return BOOTSTRAP_NOT_PRIVILEGED;
11555 }
11556 #endif
11557
11558 if (otherj->p && (flags & VPROCFLAG_STALL_JOB_EXEC)) {
11559 return BOOTSTRAP_SERVICE_ACTIVE;
11560 }
11561
11562 otherj->stall_before_exec = (flags & VPROCFLAG_STALL_JOB_EXEC);
11563 otherj = job_dispatch(otherj, true);
11564
11565 if (!job_assumes(j, otherj && otherj->p)) {
11566 // <rdar://problem/6787083> Clear this flag if we failed to start the job.
11567 otherj->stall_before_exec = false;
11568 RETURN_NO_MEMORY();
11569 }
11570
11571 *out_pid = otherj->p;
11572
11573 return 0;
11574 }
11575
11576 kern_return_t
11577 job_mig_spawn_internal(job_t j, vm_offset_t indata, mach_msg_type_number_t indataCnt, mach_port_t asport, job_t *outj)
11578 {
11579 launch_data_t jobdata = NULL;
11580 size_t data_offset = 0;
11581 struct ldcred *ldc = runtime_get_caller_creds();
11582 job_t jr;
11583
11584 if (!j) {
11585 RETURN_NO_MEMORY();
11586 }
11587
11588 if (unlikely(j->deny_job_creation)) {
11589 return BOOTSTRAP_NOT_PRIVILEGED;
11590 }
11591
11592 #if HAVE_SANDBOX
11593 if (unlikely(sandbox_check(ldc->pid, "job-creation", SANDBOX_FILTER_NONE) > 0)) {
11594 return BOOTSTRAP_NOT_PRIVILEGED;
11595 }
11596 #endif
11597
11598 if (unlikely(pid1_magic && ldc->euid && ldc->uid)) {
11599 job_log(j, LOG_DEBUG, "Punting spawn to per-user-context");
11600 return VPROC_ERR_TRY_PER_USER;
11601 }
11602
11603 if (!job_assumes(j, indataCnt != 0)) {
11604 return 1;
11605 }
11606
11607 runtime_ktrace0(RTKT_LAUNCHD_DATA_UNPACK);
11608 if (!job_assumes(j, (jobdata = launch_data_unpack((void *)indata, indataCnt, NULL, 0, &data_offset, NULL)) != NULL)) {
11609 return 1;
11610 }
11611
11612 jobmgr_t target_jm = jobmgr_find_by_name(j->mgr, NULL);
11613 if (!jobmgr_assumes(j->mgr, target_jm != NULL)) {
11614 jobmgr_log(j->mgr, LOG_ERR, "This API can only be used by a process running within an Aqua session.");
11615 return 1;
11616 }
11617
11618 jr = jobmgr_import2(target_jm ?: j->mgr, jobdata);
11619
11620 launch_data_t label = NULL;
11621 launch_data_t wait4debugger = NULL;
11622 if (!jr) {
11623 switch (errno) {
11624 case EEXIST:
11625 /* If EEXIST was returned, we know that there is a label string in
11626 * the dictionary. So we don't need to check the types here; that
11627 * has already been done.
11628 */
11629 label = launch_data_dict_lookup(jobdata, LAUNCH_JOBKEY_LABEL);
11630 jr = job_find(NULL, launch_data_get_string(label));
11631 if (job_assumes(j, jr != NULL) && !jr->p) {
11632 wait4debugger = launch_data_dict_lookup(jobdata, LAUNCH_JOBKEY_WAITFORDEBUGGER);
11633 if (wait4debugger && launch_data_get_type(wait4debugger) == LAUNCH_DATA_BOOL) {
11634 if (launch_data_get_bool(wait4debugger)) {
11635 /* If the job exists, we're going to kick-start it, but
11636 * we need to give the caller the opportunity to start
11637 * it suspended if it so desires. But this will only
11638 * take effect if the job isn't running.
11639 */
11640 jr->wait4debugger_oneshot = true;
11641 }
11642 }
11643 }
11644
11645 *outj = jr;
11646 return BOOTSTRAP_NAME_IN_USE;
11647 default:
11648 RETURN_NO_MEMORY();
11649 }
11650 }
11651
11652 if (pid1_magic) {
11653 jr->mach_uid = ldc->uid;
11654 }
11655
11656 // TODO: Consolidate the app and legacy_LS_job bits.
11657 jr->legacy_LS_job = true;
11658 jr->abandon_pg = true;
11659 jr->asport = asport;
11660 jr->app = true;
11661 uuid_clear(jr->expected_audit_uuid);
11662 jr = job_dispatch(jr, true);
11663
11664 if (!job_assumes(j, jr != NULL)) {
11665 job_remove(jr);
11666 RETURN_NO_MEMORY();
11667 }
11668
11669 if (!job_assumes(jr, jr->p)) {
11670 job_remove(jr);
11671 RETURN_NO_MEMORY();
11672 }
11673
11674 job_log(jr, LOG_DEBUG, "Spawned by PID %u: %s", j->p, j->label);
11675 *outj = jr;
11676
11677 return BOOTSTRAP_SUCCESS;
11678 }
11679
11680 kern_return_t
11681 job_mig_spawn2(job_t j, mach_port_t rp, vm_offset_t indata, mach_msg_type_number_t indataCnt, mach_port_t asport, pid_t *child_pid, mach_port_t *obsvr_port)
11682 {
11683 job_t nj = NULL;
11684 kern_return_t kr = job_mig_spawn_internal(j, indata, indataCnt, asport, &nj);
11685 if (likely(kr == KERN_SUCCESS)) {
11686 if (job_setup_exit_port(nj) != KERN_SUCCESS) {
11687 job_remove(nj);
11688 kr = BOOTSTRAP_NO_MEMORY;
11689 } else {
11690 /* Do not return until the job has called exec(3), thereby making it
11691 * safe for the caller to send it SIGCONT.
11692 *
11693 * <rdar://problem/9042798>
11694 */
11695 nj->spawn_reply_port = rp;
11696 kr = MIG_NO_REPLY;
11697 }
11698 } else if (kr == BOOTSTRAP_NAME_IN_USE) {
11699 bool was_running = nj->p;
11700 if (job_dispatch(nj, true)) {
11701 if (!was_running) {
11702 job_log(nj, LOG_DEBUG, "Job exists but is not running. Kick-starting.");
11703
11704 if (job_setup_exit_port(nj) == KERN_SUCCESS) {
11705 nj->spawn_reply_port = rp;
11706 kr = MIG_NO_REPLY;
11707 } else {
11708 kr = BOOTSTRAP_NO_MEMORY;
11709 }
11710 } else {
11711 *obsvr_port = MACH_PORT_NULL;
11712 *child_pid = nj->p;
11713 kr = KERN_SUCCESS;
11714 }
11715 } else {
11716 job_log(nj, LOG_ERR, "Failed to dispatch job, requestor: %s", j->label);
11717 kr = BOOTSTRAP_UNKNOWN_SERVICE;
11718 }
11719 }
11720
11721 mig_deallocate(indata, indataCnt);
11722 return kr;
11723 }
11724
11725 launch_data_t
11726 job_do_ipc_request(job_t j, launch_data_t request, mach_port_t asport __attribute__((unused)))
11727 {
11728 return (ipc_process_msg(j, request));
11729 }
11730
11731 #define LAUNCHD_MAX_LEGACY_FDS 128
11732 #define countof(x) (sizeof((x)) / sizeof((x[0])))
11733 #define LOG_USER_FAIL() \
11734 do { \
11735 if (uflag) \
11736 printf("launchd ipc_request failed on line: %d\n", __LINE__); \
11737 goto out_bad; \
11738 } while (0)
11739
11740 kern_return_t
11741 job_mig_ipc_request(job_t j, vm_offset_t request,
11742 mach_msg_type_number_t requestCnt, mach_port_array_t request_fds,
11743 mach_msg_type_number_t request_fdsCnt, vm_offset_t *reply,
11744 mach_msg_type_number_t *replyCnt, mach_port_array_t *reply_fdps,
11745 mach_msg_type_number_t *reply_fdsCnt, mach_port_t asport)
11746 {
11747 if (!j) {
11748 RETURN_NO_MEMORY();
11749 }
11750
11751 /* TODO: Once we support actions other than checking in, we must check the
11752 * sandbox capabilities and EUID of the requestort.
11753 */
11754 size_t nout_fdps = 0;
11755 size_t nfds = request_fdsCnt / sizeof(request_fds[0]);
11756 if (nfds > LAUNCHD_MAX_LEGACY_FDS) {
11757 job_log(j, LOG_ERR, "Too many incoming descriptors: %lu", nfds);
11758 RETURN_NO_MEMORY();
11759 }
11760
11761 int in_fds[LAUNCHD_MAX_LEGACY_FDS];
11762 size_t i = 0;
11763 for (i = 0; i < nfds; i++) {
11764 in_fds[i] = _fd(fileport_makefd(request_fds[i]));
11765 if (in_fds[i] == -1) {
11766 job_log(j, LOG_ERR, "Bad descriptor passed in legacy IPC request at index: %lu", i);
11767 }
11768 }
11769
11770 // DON'T goto outbad before this point.
11771 *reply = 0;
11772 *reply_fdps = NULL;
11773 launch_data_t ldreply = NULL;
11774
11775 size_t dataoff = 0;
11776 size_t fdoff = 0;
11777 launch_data_t ldrequest = launch_data_unpack((void *)request, requestCnt, in_fds, nfds, &dataoff, &fdoff);
11778 if (!ldrequest) {
11779 job_log(j, LOG_ERR, "Invalid legacy IPC request passed.");
11780 LOG_USER_FAIL();
11781 }
11782
11783 ldreply = job_do_ipc_request(j, ldrequest, asport);
11784 if (!ldreply) {
11785 ldreply = launch_data_new_errno(errno);
11786 if (!ldreply) {
11787 LOG_USER_FAIL();
11788 }
11789 }
11790
11791 *replyCnt = 10 * 1024 * 1024;
11792 mig_allocate(reply, *replyCnt);
11793 if (!*reply) {
11794 LOG_USER_FAIL();
11795 }
11796
11797 int out_fds[LAUNCHD_MAX_LEGACY_FDS];
11798 size_t nout_fds = 0;
11799 size_t sz = launch_data_pack(ldreply, (void *)*reply, *replyCnt, out_fds, &nout_fds);
11800 if (!sz) {
11801 job_log(j, LOG_ERR, "Could not pack legacy IPC reply.");
11802 LOG_USER_FAIL();
11803 }
11804
11805 if (nout_fds) {
11806 if (nout_fds > 128) {
11807 job_log(j, LOG_ERR, "Too many outgoing descriptors: %lu", nout_fds);
11808 LOG_USER_FAIL();
11809 }
11810
11811 *reply_fdsCnt = nout_fds * sizeof((*reply_fdps)[0]);
11812 mig_allocate((vm_address_t *)reply_fdps, *reply_fdsCnt);
11813 if (!*reply_fdps) {
11814 LOG_USER_FAIL();
11815 }
11816
11817 for (i = 0; i < nout_fds; i++) {
11818 mach_port_t fp = MACH_PORT_NULL;
11819 /* Whatever. Worst case is that we insert MACH_PORT_NULL. Not a big
11820 * deal. Note, these get stuffed into an array whose disposition is
11821 * mach_port_move_send_t, so we don't have to worry about them after
11822 * returning.
11823 */
11824 if (fileport_makeport(out_fds[i], &fp) != 0) {
11825 job_log(j, LOG_ERR, "Could not pack response descriptor at index: %lu: %d: %s", i, errno, strerror(errno));
11826 }
11827 (*reply_fdps)[i] = fp;
11828 }
11829
11830 nout_fdps = nout_fds;
11831 } else {
11832 *reply_fdsCnt = 0;
11833 }
11834
11835 mig_deallocate(request, requestCnt);
11836 launch_data_free(ldreply);
11837 ldreply = NULL;
11838
11839 // Unused for now.
11840 (void)launchd_mport_deallocate(asport);
11841
11842 return BOOTSTRAP_SUCCESS;
11843
11844 out_bad:
11845 for (i = 0; i < nfds; i++) {
11846 (void)close(in_fds[i]);
11847 }
11848
11849 for (i = 0; i < nout_fds; i++) {
11850 (void)launchd_mport_deallocate((*reply_fdps)[i]);
11851 }
11852
11853 if (*reply) {
11854 mig_deallocate(*reply, *replyCnt);
11855 }
11856
11857 /* We should never hit this since the last goto out is in the case that
11858 * allocating this fails.
11859 */
11860 if (*reply_fdps) {
11861 mig_deallocate((vm_address_t)*reply_fdps, *reply_fdsCnt);
11862 }
11863
11864 if (ldreply) {
11865 launch_data_free(ldreply);
11866 }
11867
11868 RETURN_NO_MEMORY();
11869 }
11870
11871 void
11872 jobmgr_init(bool sflag)
11873 {
11874 const char *root_session_type = pid1_magic ? VPROCMGR_SESSION_SYSTEM : VPROCMGR_SESSION_BACKGROUND;
11875 SLIST_INIT(&s_curious_jobs);
11876 LIST_INIT(&s_needing_sessions);
11877 syslog(LOG_ERR, "starting root_jobmgr");
11878 os_assert((root_jobmgr = jobmgr_new(NULL, MACH_PORT_NULL, MACH_PORT_NULL, sflag, root_session_type, false, MACH_PORT_NULL)) != NULL);
11879 #if 0
11880 os_assert((_s_xpc_system_domain = jobmgr_new_xpc_singleton_domain(root_jobmgr, strdup("com.apple.xpc.system"))) != NULL);
11881 _s_xpc_system_domain->req_asid = launchd_audit_session;
11882 _s_xpc_system_domain->req_asport = launchd_audit_port;
11883 _s_xpc_system_domain->shortdesc = "system";
11884 #endif
11885 if (pid1_magic) {
11886 root_jobmgr->monitor_shutdown = true;
11887 }
11888
11889 uint32_t fflags = NOTE_ATTRIB | NOTE_LINK | NOTE_REVOKE | NOTE_EXTEND | NOTE_WRITE;
11890 s_no_hang_fd = open("/dev/autofs_nowait", O_EVTONLY | O_NONBLOCK);
11891 if (likely(s_no_hang_fd == -1)) {
11892 if (jobmgr_assumes_zero_p(root_jobmgr, (s_no_hang_fd = open("/dev", O_EVTONLY | O_NONBLOCK))) != -1) {
11893 (void)jobmgr_assumes_zero_p(root_jobmgr, kevent_mod((uintptr_t)s_no_hang_fd, EVFILT_VNODE, EV_ADD, fflags, 0, root_jobmgr));
11894 }
11895 }
11896 s_no_hang_fd = _fd(s_no_hang_fd);
11897 }
11898
11899 size_t
11900 our_strhash(const char *s)
11901 {
11902 size_t c, r = 5381;
11903
11904 /* djb2
11905 * This algorithm was first reported by Dan Bernstein many years ago in comp.lang.c
11906 */
11907
11908 while ((c = *s++)) {
11909 r = ((r << 5) + r) + c; // hash*33 + c
11910 }
11911
11912 return r;
11913 }
11914
11915 size_t
11916 hash_label(const char *label)
11917 {
11918 return our_strhash(label) % LABEL_HASH_SIZE;
11919 }
11920
11921 size_t
11922 hash_ms(const char *msstr)
11923 {
11924 return our_strhash(msstr) % MACHSERVICE_HASH_SIZE;
11925 }
11926
11927 bool
11928 waiting4removal_new(job_t j, mach_port_t rp)
11929 {
11930 struct waiting_for_removal *w4r;
11931
11932 if (!job_assumes(j, (w4r = malloc(sizeof(struct waiting_for_removal))) != NULL)) {
11933 return false;
11934 }
11935
11936 w4r->reply_port = rp;
11937
11938 SLIST_INSERT_HEAD(&j->removal_watchers, w4r, sle);
11939
11940 return true;
11941 }
11942
11943 void
11944 waiting4removal_delete(job_t j, struct waiting_for_removal *w4r)
11945 {
11946 (void)job_assumes_zero(j, job_mig_send_signal_reply(w4r->reply_port, 0));
11947
11948 SLIST_REMOVE(&j->removal_watchers, w4r, waiting_for_removal, sle);
11949
11950 free(w4r);
11951 }
11952
11953 size_t
11954 get_kern_max_proc(void)
11955 {
11956 int mib[] = { CTL_KERN, KERN_MAXPROC };
11957 int max = 100;
11958 size_t max_sz = sizeof(max);
11959
11960 (void)posix_assumes_zero(sysctl(mib, 2, &max, &max_sz, NULL, 0));
11961
11962 return max;
11963 }
11964
11965 // See rdar://problem/6271234
11966 void
11967 eliminate_double_reboot(void)
11968 {
11969 if (unlikely(!pid1_magic)) {
11970 return;
11971 }
11972
11973 struct stat sb;
11974 const char *argv[] = { _PATH_BSHELL, "/etc/rc.deferred_install", NULL };
11975 int result = -1;
11976
11977 if (unlikely(stat(argv[1], &sb) != -1)) {
11978 jobmgr_log(root_jobmgr, LOG_DEBUG | LOG_CONSOLE, "Going to run deferred install script.");
11979
11980 pid_t p = 0;
11981 result = posix_spawnp(&p, argv[0], NULL, NULL, (char **)argv, environ);
11982 if (result == -1) {
11983 jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Couldn't run deferred install script: %d: %s", result, strerror(result));
11984 goto out;
11985 }
11986
11987 int wstatus = 0;
11988 result = waitpid(p, &wstatus, 0);
11989 if (result == -1) {
11990 jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Failed to reap deferred install script: %d: %s", errno, strerror(errno));
11991 goto out;
11992 }
11993
11994 if (WIFEXITED(wstatus)) {
11995 if ((result = WEXITSTATUS(wstatus)) == 0) {
11996 jobmgr_log(root_jobmgr, LOG_DEBUG | LOG_CONSOLE, "Deferred install script completed successfully.");
11997 } else {
11998 jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Deferred install script failed with status: %d", WEXITSTATUS(wstatus));
11999 }
12000 } else {
12001 jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Weirdness with install script: %d", wstatus);
12002 }
12003 }
12004 out:
12005 if (result == 0) {
12006 /* If the unlink(2) was to fail, it would be most likely fail with
12007 * EBUSY. All the other failure cases for unlink(2) don't apply when
12008 * we're running under PID 1 and have verified that the file exists.
12009 * Outside of someone deliberately messing with us (like if
12010 * /etc/rc.deferredinstall is actually a looping sym-link or a mount
12011 * point for a filesystem) and I/O errors, we should be good.
12012 */
12013 if (unlink(argv[1]) == -1) {
12014 jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Failed to remove deferred install script: %d: %s", errno, strerror(errno));
12015 }
12016 }
12017 }
12018
12019 void
12020 jetsam_property_setup(launch_data_t obj, const char *key, job_t j)
12021 {
12022 job_log(j, LOG_DEBUG, "Setting Jetsam properties for job...");
12023 if (strcasecmp(key, LAUNCH_JOBKEY_JETSAMPRIORITY) == 0 && launch_data_get_type(obj) == LAUNCH_DATA_INTEGER) {
12024 j->jetsam_priority = (typeof(j->jetsam_priority))launch_data_get_integer(obj);
12025
12026 #if XPC_LPI_VERSION >= 20120810
12027 if (j->jetsam_priority > XPC_JETSAM_PRIORITY_RESERVED && j->jetsam_priority < XPC_JETSAM_PRIORITY_RESERVED + XPC_JETSAM_BAND_LAST) {
12028 size_t band = j->jetsam_priority - XPC_JETSAM_PRIORITY_RESERVED;
12029 j->jetsam_priority = _launchd_priority_map[band - 1].priority;
12030 }
12031 #endif
12032 job_log(j, LOG_DEBUG, "Priority: %d", j->jetsam_priority);
12033 } else if (strcasecmp(key, LAUNCH_JOBKEY_JETSAMMEMORYLIMIT) == 0 && launch_data_get_type(obj) == LAUNCH_DATA_INTEGER) {
12034 j->jetsam_memlimit = (typeof(j->jetsam_memlimit))launch_data_get_integer(obj);
12035 job_log(j, LOG_DEBUG, "Memory limit: %d", j->jetsam_memlimit);
12036 } else if (strcasecmp(key, LAUNCH_JOBKEY_JETSAMMEMORYLIMITBACKGROUND) == 0) {
12037 j->jetsam_memory_limit_background = true;
12038 job_log(j, LOG_DEBUG, "Memory limit is for background state only");
12039 } else if (strcasecmp(key, LAUNCH_KEY_JETSAMFRONTMOST) == 0) {
12040 /* Ignore. We only recognize this key so we don't complain when we get SpringBoard's request.
12041 * You can't set this in a plist.
12042 */
12043 } else if (strcasecmp(key, LAUNCH_KEY_JETSAMACTIVE) == 0) {
12044 // Ignore.
12045 } else if (strcasecmp(key, LAUNCH_KEY_JETSAMLABEL) == 0) {
12046 /* Ignore. This key is present in SpringBoard's request dictionary, so we don't want to
12047 * complain about it.
12048 */
12049 } else {
12050 job_log(j, LOG_ERR, "Unknown Jetsam key: %s", key);
12051 }
12052
12053 if (unlikely(!j->jetsam_properties)) {
12054 j->jetsam_properties = true;
12055 }
12056 }
12057
12058 void
12059 job_update_jetsam_properties(job_t j, xpc_jetsam_band_t band, uint64_t user_data)
12060 {
12061 #if TARGET_OS_EMBEDDED
12062 j->jetsam_priority = _launchd_priority_map[band - 1].priority;
12063 j->jetsam_properties = true;
12064
12065 memorystatus_priority_properties_t mjp;
12066 mjp.priority = j->jetsam_priority;
12067 mjp.user_data = user_data;
12068
12069 size_t size = sizeof(mjp);
12070 int r = memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES, j->p, 0, &mjp, size);
12071 if (r == -1 && errno != ESRCH) {
12072 (void)job_assumes_zero(j, errno);
12073 }
12074 #else
12075 #pragma unused(j, band, user_data)
12076 #endif
12077 }
12078
12079 void
12080 job_update_jetsam_memory_limit(job_t j, int32_t limit)
12081 {
12082 #if TARGET_OS_EMBEDDED
12083 j->jetsam_memlimit = limit;
12084 j->jetsam_properties = true;
12085
12086 int r = memorystatus_control(MEMORYSTATUS_CMD_SET_JETSAM_HIGH_WATER_MARK, j->p, limit, NULL, 0);
12087 if (r == -1 && errno != ESRCH) {
12088 (void)job_assumes_zero(j, errno);
12089 }
12090 #else
12091 #pragma unused(j, limit)
12092 #endif
12093 }
12094
12095 void
12096 _log_launchd_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test)
12097 {
12098 int saved_errno = errno;
12099 char buf[100];
12100 const char *file = strrchr(path, '/');
12101 char *rcs_rev_tmp = strchr(rcs_rev, ' ');
12102
12103 if (!file) {
12104 file = path;
12105 } else {
12106 file += 1;
12107 }
12108
12109 if (!rcs_rev_tmp) {
12110 strlcpy(buf, rcs_rev, sizeof(buf));
12111 } else {
12112 strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf));
12113 rcs_rev_tmp = strchr(buf, ' ');
12114 if (rcs_rev_tmp)
12115 *rcs_rev_tmp = '\0';
12116 }
12117
12118 syslog(LOG_NOTICE, "Bug: %s:%u (%s):%u: %s", file, line, buf, saved_errno, test);
12119 }
12120
12121 pid_t
12122 launchd_fork(void)
12123 {
12124
12125 return (runtime_fork(root_jobmgr->jm_port));
12126 }
12127