xref: /trueos/lib/liblaunch/libvproc.c (revision 3ee2823f304a9be5b84cb3e62092041294567d1d)
1 /*
2  * Copyright (c) 1999-2012 Apple Inc. All rights reserved.
3  *
4  * @APPLE_APACHE_LICENSE_HEADER_START@
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * @APPLE_APACHE_LICENSE_HEADER_END@
19  */
20 
21 #include "config.h"
22 #include "vproc.h"
23 #include "vproc_priv.h"
24 #include "vproc_internal.h"
25 
26 #include <dispatch/dispatch.h>
27 #include <libproc.h>
28 #include <mach/mach.h>
29 #include <mach/mach_port.h>
30 #include <mach/mig.h>
31 #include <sys/param.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <syslog.h>
37 #include <pthread.h>
38 #include <signal.h>
39 #include <assert.h>
40 #include <sys/syscall.h>
41 #include <sys/event.h>
42 #include <System/sys/fileport.h>
43 
44 #include <sys/types.h>
45 #include <machine/atomic.h>
46 
47 #include <os/assumes.h>
48 
49 #if HAVE_QUARANTINE
50 #include <quarantine.h>
51 #endif
52 
53 #include "launch.h"
54 #include "launch_priv.h"
55 #include "launch_internal.h"
56 #include "ktrace.h"
57 
58 #include "job.h"
59 
60 #include "helper.h"
61 #include "helperServer.h"
62 
63 #include "reboot2.h"
64 
65 #define likely(x) __builtin_expect((bool)(x), true)
66 #define unlikely(x) __builtin_expect((bool)(x), false)
67 
68 #define _vproc_set_crash_log_message(x)
69 
70 void _vproc_transactions_enable_internal(void *arg);
71 void _vproc_transaction_begin_internal(void *arg __unused);
72 void _vproc_transaction_end_internal(void *arg __unused);
73 
74 #pragma mark vproc Object
75 struct vproc_s {
76 	int32_t refcount;
77 	mach_port_t j_port;
78 };
79 
80 vproc_t
vprocmgr_lookup_vproc(const char * label)81 vprocmgr_lookup_vproc(const char *label)
82 {
83 	struct vproc_s *vp = NULL;
84 
85 	mach_port_t mp = MACH_PORT_NULL;
86 	kern_return_t kr = vproc_mig_port_for_label(bootstrap_port, (char *)label, &mp);
87 	if (kr == BOOTSTRAP_SUCCESS) {
88 		vp = (struct vproc_s *)calloc(1, sizeof(struct vproc_s));
89 		if (vp) {
90 			vp->refcount = 1;
91 			mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, 1);
92 			vp->j_port = mp;
93 		}
94 		(void)mach_port_deallocate(mach_task_self(), mp);
95 	}
96 
97 	return vp;
98 }
99 
100 vproc_t
vproc_retain(vproc_t vp)101 vproc_retain(vproc_t vp)
102 {
103 	int32_t orig = atomic_fetchadd_int(&vp->refcount, 1) - 1;
104 	if (orig <= 0) {
105 		_vproc_set_crash_log_message("Under-retain / over-release of vproc_t.");
106 		abort();
107 	}
108 
109 	return vp;
110 }
111 
112 void
vproc_release(vproc_t vp)113 vproc_release(vproc_t vp)
114 {
115 	int32_t newval = atomic_fetchadd_int(&vp->refcount, -1);
116 	if (newval < 0) {
117 		_vproc_set_crash_log_message("Over-release of vproc_t.");
118 		abort();
119 	} else if (newval == 0) {
120 		mach_port_deallocate(mach_task_self(), vp->j_port);
121 		free(vp);
122 	}
123 }
124 
125 #pragma mark Transactions
126 static void
_vproc_transaction_init_once(void * arg __unused)127 _vproc_transaction_init_once(void *arg __unused)
128 {
129 	launch_globals_t globals = _launch_globals();
130 
131 	int64_t enable_transactions = 0;
132 	(void)vproc_swap_integer(NULL, VPROC_GSK_TRANSACTIONS_ENABLED, 0, &enable_transactions);
133 	if (enable_transactions != 0) {
134 		(void)os_assumes_zero(proc_track_dirty(getpid(), PROC_DIRTY_TRACK));
135 		globals->_vproc_transaction_enabled = 1;
136 	}
137 	globals->_vproc_transaction_queue = dispatch_queue_create("com.apple.idle-exit-queue", NULL);
138 }
139 
140 void
_vproc_transactions_enable_internal(void * arg __unused)141 _vproc_transactions_enable_internal(void *arg __unused)
142 {
143 	launch_globals_t globals = _launch_globals();
144 
145 	if (!globals->_vproc_transaction_enabled) {
146 		(void)os_assumes_zero(proc_track_dirty(getpid(), PROC_DIRTY_TRACK));
147 		globals->_vproc_transaction_enabled = 1;
148 	}
149 
150 	if (globals->_vproc_transaction_cnt > 0) {
151 		(void)os_assumes_zero(proc_set_dirty(getpid(), true));
152 	}
153 }
154 
155 void
_vproc_transactions_enable(void)156 _vproc_transactions_enable(void)
157 {
158 	launch_globals_t globals = _launch_globals();
159 
160 	dispatch_once_f(&globals->_vproc_transaction_once, NULL, _vproc_transaction_init_once);
161 	dispatch_sync_f(globals->_vproc_transaction_queue, NULL, _vproc_transactions_enable_internal);
162 }
163 
164 void
_vproc_transaction_begin_internal(void * ctx __unused)165 _vproc_transaction_begin_internal(void *ctx __unused)
166 {
167 	launch_globals_t globals = _launch_globals();
168 
169 	int64_t new = ++globals->_vproc_transaction_cnt;
170 	if (!globals->_vproc_transaction_enabled || new > 1) {
171 		return;
172 	}
173 
174 	if (new < 1) {
175 		_vproc_set_crash_log_message("Underflow of transaction count.");
176 		abort();
177 	}
178 
179 	(void)os_assumes_zero(proc_set_dirty(getpid(), true));
180 }
181 
182 void
_vproc_transaction_begin(void)183 _vproc_transaction_begin(void)
184 {
185 	launch_globals_t globals = _launch_globals();
186 
187 	dispatch_once_f(&globals->_vproc_transaction_once, NULL, _vproc_transaction_init_once);
188 	dispatch_sync_f(globals->_vproc_transaction_queue, NULL, _vproc_transaction_begin_internal);
189 }
190 
191 vproc_transaction_t
vproc_transaction_begin(vproc_t vp __unused)192 vproc_transaction_begin(vproc_t vp __unused)
193 {
194 	_vproc_transaction_begin();
195 
196 	/* Return non-NULL on success. Originally, there were dreams of returning
197 	 * an object or something, but those never panned out.
198 	 */
199 	return (vproc_transaction_t)vproc_transaction_begin;
200 }
201 
202 void _vproc_transaction_end_flush(void);
203 
204 static void
_vproc_transaction_end_internal2(void * ctx)205 _vproc_transaction_end_internal2(void *ctx)
206 {
207 	launch_globals_t globals = _launch_globals();
208 
209 	globals->_vproc_gone2zero_callout(ctx);
210 	_vproc_transaction_end_flush();
211 }
212 
213 void
_vproc_transaction_end_internal(void * arg)214 _vproc_transaction_end_internal(void *arg)
215 {
216 	launch_globals_t globals = _launch_globals();
217 
218 	int64_t new = --globals->_vproc_transaction_cnt;
219 	if (!globals->_vproc_transaction_enabled || new > 0) {
220 		return;
221 	}
222 
223 	if (new < 0) {
224 		_vproc_set_crash_log_message("Underflow of transaction count.");
225 		abort();
226 	}
227 
228 	if (globals->_vproc_gone2zero_callout && !arg) {
229 		globals->_vproc_transaction_cnt = 1;
230 		dispatch_async_f(globals->_vproc_gone2zero_queue, globals->_vproc_gone2zero_ctx, _vproc_transaction_end_internal2);
231 	} else {
232 		(void)os_assumes_zero(proc_set_dirty(getpid(), false));
233 	}
234 }
235 
236 static void
_vproc_transaction_end_flush2(void * ctx __unused)237 _vproc_transaction_end_flush2(void *ctx __unused)
238 {
239 	_vproc_transaction_end_internal((void *)1);
240 }
241 
242 void
_vproc_transaction_end_flush(void)243 _vproc_transaction_end_flush(void)
244 {
245 	launch_globals_t globals = _launch_globals();
246 
247 	dispatch_sync_f(globals->_vproc_transaction_queue, NULL, _vproc_transaction_end_flush2);
248 }
249 
250 void
_vproc_transaction_end(void)251 _vproc_transaction_end(void)
252 {
253 	launch_globals_t globals = _launch_globals();
254 
255 	dispatch_once_f(&globals->_vproc_transaction_once, NULL, _vproc_transaction_init_once);
256 	dispatch_sync_f(globals->_vproc_transaction_queue, NULL, _vproc_transaction_end_internal);
257 }
258 
259 void
vproc_transaction_end(vproc_t vp __unused,vproc_transaction_t vpt __unused)260 vproc_transaction_end(vproc_t vp __unused, vproc_transaction_t vpt __unused)
261 {
262 	_vproc_transaction_end();
263 }
264 
265 size_t
_vproc_transaction_count(void)266 _vproc_transaction_count(void)
267 {
268 	launch_globals_t globals = _launch_globals();
269 
270 	return globals->_vproc_transaction_cnt;
271 }
272 
273 size_t
_vproc_standby_count(void)274 _vproc_standby_count(void)
275 {
276 	return 0;
277 }
278 
279 size_t
_vproc_standby_timeout(void)280 _vproc_standby_timeout(void)
281 {
282 	return 0;
283 }
284 
285 bool
_vproc_pid_is_managed(pid_t p)286 _vproc_pid_is_managed(pid_t p)
287 {
288 	boolean_t result = false;
289 	vproc_mig_pid_is_managed(bootstrap_port, p, &result);
290 
291 	return result;
292 }
293 
294 kern_return_t
_vproc_transaction_count_for_pid(pid_t p,int32_t * count,bool * condemned)295 _vproc_transaction_count_for_pid(pid_t p, int32_t *count, bool *condemned)
296 {
297 	/* Activity Monitor relies on us returning this error code when the process
298 	 * is not opted into Instant Off.
299 	 */
300 	kern_return_t error = BOOTSTRAP_NO_MEMORY;
301 
302 	if (condemned) {
303 		*condemned = false;
304 	}
305 
306 	if (count) {
307 		uint32_t flags;
308 		int ret = proc_get_dirty(p, &flags);
309 		if (ret == 0) {
310 			if (flags & PROC_DIRTY_TRACKED) {
311 				*count = (flags & PROC_DIRTY_IS_DIRTY) ? 1 : 0;
312 				error = BOOTSTRAP_SUCCESS;
313 			} else {
314 				error = BOOTSTRAP_NO_MEMORY;
315 			}
316 		} else if (ret == ENOTSUP) {
317 			error = BOOTSTRAP_NO_MEMORY;
318 		} else if (ret == ESRCH) {
319 			error = BOOTSTRAP_UNKNOWN_SERVICE;
320 		} else if (ret == EPERM) {
321 			error = BOOTSTRAP_NOT_PRIVILEGED;
322 		} else {
323 			error = ret;
324 		}
325 	}
326 	return error;
327 }
328 void
_vproc_transaction_try_exit(int status)329 _vproc_transaction_try_exit(int status)
330 {
331 #if !TARGET_OS_EMBEDDED
332 	launch_globals_t globals = _launch_globals();
333 	if (globals->_vproc_transaction_cnt == 0) {
334 		_exit(status);
335 	}
336 #else
337 	_exit(status);
338 #endif
339 }
340 
341 void
_vproc_standby_begin(void)342 _vproc_standby_begin(void)
343 {
344 
345 }
346 
347 vproc_standby_t
vproc_standby_begin(vproc_t vp __unused)348 vproc_standby_begin(vproc_t vp __unused)
349 {
350 	return (vproc_standby_t)vproc_standby_begin;
351 }
352 
353 void
_vproc_standby_end(void)354 _vproc_standby_end(void)
355 {
356 
357 }
358 
359 void
_vproc_transaction_set_clean_callback(dispatch_queue_t targetq,void * ctx,dispatch_function_t func)360 _vproc_transaction_set_clean_callback(dispatch_queue_t targetq, void *ctx, dispatch_function_t func)
361 {
362 	launch_globals_t globals = _launch_globals();
363 
364 	globals->_vproc_gone2zero_queue = targetq;
365 	dispatch_retain(targetq);
366 
367 	globals->_vproc_gone2zero_callout = func;
368 	globals->_vproc_gone2zero_ctx = ctx;
369 }
370 
371 void
vproc_standby_end(vproc_t vp __unused,vproc_standby_t vpt __unused)372 vproc_standby_end(vproc_t vp __unused, vproc_standby_t vpt __unused)
373 {
374 
375 }
376 
377 #pragma mark Miscellaneous SPI
378 kern_return_t
_vproc_grab_subset(mach_port_t bp,mach_port_t * reqport,mach_port_t * rcvright,launch_data_t * outval,mach_port_array_t * ports,mach_msg_type_number_t * portCnt)379 _vproc_grab_subset(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright,
380 	launch_data_t *outval, mach_port_array_t *ports,
381 	mach_msg_type_number_t *portCnt)
382 {
383 	mach_msg_type_number_t outdata_cnt;
384 	vm_offset_t outdata = 0;
385 	size_t data_offset = 0;
386 	launch_data_t out_obj;
387 	kern_return_t kr;
388 
389 	if ((kr = vproc_mig_take_subset(bp, reqport, rcvright, &outdata, &outdata_cnt, ports, portCnt))) {
390 		goto out;
391 	}
392 
393 	if ((out_obj = launch_data_unpack((void *)outdata, outdata_cnt, NULL, 0, &data_offset, NULL))) {
394 		*outval = launch_data_copy(out_obj);
395 	} else {
396 		kr = 1;
397 	}
398 
399 out:
400 	if (outdata) {
401 		mig_deallocate(outdata, outdata_cnt);
402 	}
403 
404 	return kr;
405 }
406 
407 vproc_err_t
_vprocmgr_move_subset_to_user(uid_t target_user,const char * session_type,uint64_t flags)408 _vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type, uint64_t flags)
409 {
410 	kern_return_t kr = 0;
411 	bool is_bkgd = (strcmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0);
412 	int64_t ldpid, lduid;
413 
414 	if (vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, 0, &ldpid) != 0) {
415 		return (vproc_err_t)_vprocmgr_move_subset_to_user;
416 	}
417 
418 	if (vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, 0, &lduid) != 0) {
419 		return (vproc_err_t)_vprocmgr_move_subset_to_user;
420 	}
421 
422 	if (!is_bkgd && ldpid != 1) {
423 		if (lduid == getuid()) {
424 			return NULL;
425 		}
426 		/*
427 		 * Not all sessions can be moved.
428 		 * We should clean up this mess someday.
429 		 */
430 		return (vproc_err_t)_vprocmgr_move_subset_to_user;
431 	}
432 
433 	mach_port_t puc = 0;
434 	mach_port_t rootbs = MACH_PORT_NULL;
435 	(void)bootstrap_get_root(bootstrap_port, &rootbs);
436 
437 	if (vproc_mig_lookup_per_user_context(rootbs, target_user, &puc) != 0) {
438 		return (vproc_err_t)_vprocmgr_move_subset_to_user;
439 	}
440 
441 	if (is_bkgd) {
442 		task_set_bootstrap_port(mach_task_self(), puc);
443 		mach_port_deallocate(mach_task_self(), bootstrap_port);
444 		bootstrap_port = puc;
445 	} else {
446 		kr = vproc_mig_move_subset(puc, bootstrap_port, (char *)session_type, _audit_session_self(), flags);
447 		mach_port_deallocate(mach_task_self(), puc);
448 	}
449 
450 	if (kr) {
451 		return (vproc_err_t)_vprocmgr_move_subset_to_user;
452 	}
453 
454 	return _vproc_post_fork_ping();
455 }
456 
457 vproc_err_t
_vprocmgr_switch_to_session(const char * target_session,vproc_flags_t flags)458 _vprocmgr_switch_to_session(const char *target_session, vproc_flags_t flags __attribute__((unused)))
459 {
460 	mach_port_t new_bsport = MACH_PORT_NULL;
461 	kern_return_t kr = KERN_FAILURE;
462 
463 	mach_port_t tnp = MACH_PORT_NULL;
464 	task_name_for_pid(mach_task_self(), getpid(), &tnp);
465 	if ((kr = vproc_mig_switch_to_session(bootstrap_port, tnp, (char *)target_session, _audit_session_self(), &new_bsport)) != KERN_SUCCESS) {
466 		_vproc_log(LOG_NOTICE, "_vprocmgr_switch_to_session(): kr = 0x%x", kr);
467 		return (vproc_err_t)_vprocmgr_switch_to_session;
468 	}
469 
470 	task_set_bootstrap_port(mach_task_self(), new_bsport);
471 	mach_port_deallocate(mach_task_self(), bootstrap_port);
472 	bootstrap_port = new_bsport;
473 
474 	return !issetugid() ? _vproc_post_fork_ping() : NULL;
475 }
476 
477 vproc_err_t
_vprocmgr_detach_from_console(vproc_flags_t flags)478 _vprocmgr_detach_from_console(vproc_flags_t flags __attribute__((unused)))
479 {
480 	return _vprocmgr_switch_to_session(VPROCMGR_SESSION_BACKGROUND, 0);
481 }
482 
483 vproc_err_t
_vproc_post_fork_ping(void)484 _vproc_post_fork_ping(void)
485 {
486 	mach_port_t session = MACH_PORT_NULL;
487 	kern_return_t kr = vproc_mig_post_fork_ping(bootstrap_port, mach_task_self(), &session);
488 	if (kr) {
489 		syslog(LOG_DEBUG, "vproc_mig_post_fork_ping kr=%x bootstrap_port=%d\n", kr, bootstrap_port);
490 		return _vproc_post_fork_ping;
491 	}
492 
493 	if (session) {
494 		(void)_audit_session_join(session);
495 		(void)mach_port_deallocate(mach_task_self(), session);
496 	}
497 
498 	return NULL;
499 }
500 
501 vproc_err_t
_vprocmgr_init(const char * session_type)502 _vprocmgr_init(const char *session_type)
503 {
504 	if (vproc_mig_init_session(bootstrap_port, (char *)session_type, _audit_session_self()) == 0) {
505 		return NULL;
506 	}
507 
508 	return (vproc_err_t)_vprocmgr_init;
509 }
510 
511 pid_t
_spawn_via_launchd(const char * label,const char * const * argv,const struct spawn_via_launchd_attr * spawn_attrs,int struct_version)512 _spawn_via_launchd(const char *label, const char *const *argv, const struct spawn_via_launchd_attr *spawn_attrs, int struct_version)
513 {
514 	size_t i, good_enough_size = 10*1024*1024;
515 	mach_msg_type_number_t indata_cnt = 0;
516 	vm_offset_t indata = 0;
517 	mach_port_t obsvr_port = MACH_PORT_NULL;
518 	launch_data_t tmp, tmp_array, in_obj;
519 	const char *const *tmpp;
520 	kern_return_t kr = 1;
521 	void *buf = NULL;
522 	pid_t p = -1;
523 
524 	if ((in_obj = launch_data_alloc(LAUNCH_DATA_DICTIONARY)) == NULL) {
525 		goto out;
526 	}
527 
528 	if ((tmp = launch_data_new_string(label)) == NULL) {
529 		goto out;
530 	}
531 
532 	launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_LABEL);
533 
534 	if ((tmp_array = launch_data_alloc(LAUNCH_DATA_ARRAY)) == NULL) {
535 		goto out;
536 	}
537 
538 	for (i = 0; *argv; i++, argv++) {
539 		tmp = launch_data_new_string(*argv);
540 		if (tmp == NULL) {
541 			goto out;
542 		}
543 
544 		launch_data_array_set_index(tmp_array, tmp, i);
545 	}
546 
547 	launch_data_dict_insert(in_obj, tmp_array, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
548 
549 	if (spawn_attrs) switch (struct_version) {
550 	case 3:
551 	case 2:
552 #if HAVE_QUARANTINE
553 		if (spawn_attrs->spawn_quarantine) {
554 			char qbuf[QTN_SERIALIZED_DATA_MAX];
555 			size_t qbuf_sz = QTN_SERIALIZED_DATA_MAX;
556 
557 			if (qtn_proc_to_data(spawn_attrs->spawn_quarantine, qbuf, &qbuf_sz) == 0) {
558 				tmp = launch_data_new_opaque(qbuf, qbuf_sz);
559 				launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_QUARANTINEDATA);
560 			}
561 		}
562 #endif
563 
564 		if (spawn_attrs->spawn_seatbelt_profile) {
565 			tmp = launch_data_new_string(spawn_attrs->spawn_seatbelt_profile);
566 			launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_SANDBOXPROFILE);
567 		}
568 
569 		if (spawn_attrs->spawn_seatbelt_flags) {
570 			tmp = launch_data_new_integer(*spawn_attrs->spawn_seatbelt_flags);
571 			launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_SANDBOXFLAGS);
572 		}
573 
574 		/* fall through */
575 	case 1:
576 		if (spawn_attrs->spawn_binpref) {
577 			tmp_array = launch_data_alloc(LAUNCH_DATA_ARRAY);
578 			for (i = 0; i < spawn_attrs->spawn_binpref_cnt; i++) {
579 				tmp = launch_data_new_integer(spawn_attrs->spawn_binpref[i]);
580 				launch_data_array_set_index(tmp_array, tmp, i);
581 			}
582 			launch_data_dict_insert(in_obj, tmp_array, LAUNCH_JOBKEY_BINARYORDERPREFERENCE);
583 		}
584 		/* fall through */
585 	case 0:
586 		if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_STOPPED) {
587 			tmp = launch_data_new_bool(true);
588 			launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_WAITFORDEBUGGER);
589 		}
590 		if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_TALAPP) {
591 			tmp = launch_data_new_string(LAUNCH_KEY_POSIXSPAWNTYPE_TALAPP);
592 			launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_POSIXSPAWNTYPE);
593 		}
594 		if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_DISABLE_ASLR) {
595 			tmp = launch_data_new_bool(true);
596 			launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_DISABLEASLR);
597 		}
598 
599 		if (spawn_attrs->spawn_env) {
600 			launch_data_t tmp_dict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
601 
602 			for (tmpp = spawn_attrs->spawn_env; *tmpp; tmpp++) {
603 				char *eqoff, tmpstr[strlen(*tmpp) + 1];
604 
605 				strcpy(tmpstr, *tmpp);
606 
607 				eqoff = strchr(tmpstr, '=');
608 
609 				if (!eqoff) {
610 					goto out;
611 				}
612 
613 				*eqoff = '\0';
614 
615 				launch_data_dict_insert(tmp_dict, launch_data_new_string(eqoff + 1), tmpstr);
616 			}
617 
618 			launch_data_dict_insert(in_obj, tmp_dict, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES);
619 		}
620 
621 		if (spawn_attrs->spawn_path) {
622 			tmp = launch_data_new_string(spawn_attrs->spawn_path);
623 			launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_PROGRAM);
624 		}
625 
626 		if (spawn_attrs->spawn_chdir) {
627 			tmp = launch_data_new_string(spawn_attrs->spawn_chdir);
628 			launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_WORKINGDIRECTORY);
629 		}
630 
631 		if (spawn_attrs->spawn_umask) {
632 			tmp = launch_data_new_integer(*spawn_attrs->spawn_umask);
633 			launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_UMASK);
634 		}
635 
636 		break;
637 	default:
638 		break;
639 	}
640 
641 	if (!(buf = malloc(good_enough_size))) {
642 		goto out;
643 	}
644 
645 	if ((indata_cnt = launch_data_pack(in_obj, buf, good_enough_size, NULL, NULL)) == 0) {
646 		goto out;
647 	}
648 
649 	indata = (vm_offset_t)buf;
650 
651 	if (struct_version == 3) {
652 		kr = vproc_mig_spawn2(bootstrap_port, indata, indata_cnt, _audit_session_self(), &p, &obsvr_port);
653 	} else {
654 		_vproc_set_crash_log_message("Bogus version passed to _spawn_via_launchd(). For this release, the only valid version is 3.");
655 	}
656 
657 	if (kr == VPROC_ERR_TRY_PER_USER) {
658 		mach_port_t puc;
659 
660 		if (vproc_mig_lookup_per_user_context(bootstrap_port, 0, &puc) == 0) {
661 			if (struct_version == 3) {
662 				kr = vproc_mig_spawn2(puc, indata, indata_cnt, _audit_session_self(), &p, &obsvr_port);
663 			}
664 			mach_port_deallocate(mach_task_self(), puc);
665 		}
666 	}
667 
668 out:
669 	if (in_obj) {
670 		launch_data_free(in_obj);
671 	}
672 
673 	if (buf) {
674 		free(buf);
675 	}
676 
677 	switch (kr) {
678 	case BOOTSTRAP_SUCCESS:
679 		if (spawn_attrs && spawn_attrs->spawn_observer_port) {
680 			*spawn_attrs->spawn_observer_port = obsvr_port;
681 		} else {
682 			if (struct_version == 3) {
683 				mach_port_mod_refs(mach_task_self(), obsvr_port, MACH_PORT_RIGHT_RECEIVE, -1);
684 			} else {
685 				mach_port_deallocate(mach_task_self(), obsvr_port);
686 			}
687 		}
688 		return p;
689 	case BOOTSTRAP_NOT_PRIVILEGED:
690 		errno = EPERM; break;
691 	case BOOTSTRAP_NO_MEMORY:
692 		errno = ENOMEM; break;
693 	case BOOTSTRAP_NAME_IN_USE:
694 		errno = EEXIST; break;
695 	case 1:
696 		errno = EIO; break;
697 	default:
698 		errno = EINVAL; break;
699 	}
700 
701 	return -1;
702 }
703 
704 kern_return_t
mpm_wait(mach_port_t ajob,int * wstatus)705 mpm_wait(mach_port_t ajob __attribute__((unused)), int *wstatus)
706 {
707 	*wstatus = 0;
708 	return 0;
709 }
710 
711 kern_return_t
mpm_uncork_fork(mach_port_t ajob)712 mpm_uncork_fork(mach_port_t ajob __attribute__((unused)))
713 {
714 	return KERN_FAILURE;
715 }
716 
717 kern_return_t
_vprocmgr_getsocket(name_t sockpath)718 _vprocmgr_getsocket(name_t sockpath)
719 {
720 	return vproc_mig_getsocket(bootstrap_port, sockpath);
721 }
722 
723 vproc_err_t
_vproc_get_last_exit_status(int * wstatus)724 _vproc_get_last_exit_status(int *wstatus)
725 {
726 	int64_t val;
727 
728 	if (vproc_swap_integer(NULL, VPROC_GSK_LAST_EXIT_STATUS, 0, &val) == 0) {
729 		*wstatus = (int)val;
730 		return NULL;
731 	}
732 
733 	return (vproc_err_t)_vproc_get_last_exit_status;
734 }
735 
736 vproc_err_t
_vproc_send_signal_by_label(const char * label,int sig)737 _vproc_send_signal_by_label(const char *label, int sig)
738 {
739 	if (vproc_mig_send_signal(bootstrap_port, (char *)label, sig) == 0) {
740 		return NULL;
741 	}
742 
743 	return _vproc_send_signal_by_label;
744 }
745 
746 vproc_err_t
_vprocmgr_log_forward(mach_port_t mp,void * data,size_t len)747 _vprocmgr_log_forward(mach_port_t mp, void *data, size_t len)
748 {
749 	if (vproc_mig_log_forward(mp, (vm_offset_t)data, len) == 0) {
750 		return NULL;
751 	}
752 
753 	return _vprocmgr_log_forward;
754 }
755 
756 vproc_err_t
_vprocmgr_log_drain(vproc_t vp,pthread_mutex_t * mutex,_vprocmgr_log_drain_callback_t func)757 _vprocmgr_log_drain(vproc_t vp __attribute__((unused)), pthread_mutex_t *mutex, _vprocmgr_log_drain_callback_t func)
758 {
759 	mach_msg_type_number_t outdata_cnt, tmp_cnt;
760 	vm_offset_t outdata = 0;
761 	struct timeval tv;
762 	struct logmsg_s *lm;
763 
764 	if (!func) {
765 		return _vprocmgr_log_drain;
766 	}
767 
768 	if (vproc_mig_log_drain(bootstrap_port, &outdata, &outdata_cnt) != 0) {
769 		return _vprocmgr_log_drain;
770 	}
771 
772 	tmp_cnt = outdata_cnt;
773 
774 	if (mutex) {
775 		pthread_mutex_lock(mutex);
776 		for (lm = (struct logmsg_s *)outdata; tmp_cnt > 0; lm = (void *)((uint64_t *)lm + lm->obj_sz/8)) {
777 			lm->from_name = (char *)lm + lm->from_name_offset;
778 			lm->about_name = (char *)lm + lm->about_name_offset;
779 			lm->msg = (char *)lm + lm->msg_offset;
780 			lm->session_name = (char *)lm + lm->session_name_offset;
781 
782 			tv.tv_sec = lm->when / USEC_PER_SEC;
783 			tv.tv_usec = lm->when % USEC_PER_SEC;
784 
785 			func(&tv, lm->from_pid, lm->about_pid, lm->sender_uid, lm->sender_gid, lm->pri,
786 				 lm->from_name, lm->about_name, lm->session_name, lm->msg);
787 
788 			tmp_cnt -= lm->obj_sz;
789 		}
790 		pthread_mutex_unlock(mutex);
791 	} else {
792 		/* the compiler can't tell that mutex isn't modified
793 		* in the function so we duplicate :(
794 		*/
795 		for (lm = (struct logmsg_s *)outdata; tmp_cnt > 0; lm = (void *)((uint64_t *)lm + lm->obj_sz/8)) {
796 			lm->from_name = (char *)lm + lm->from_name_offset;
797 			lm->about_name = (char *)lm + lm->about_name_offset;
798 			lm->msg = (char *)lm + lm->msg_offset;
799 			lm->session_name = (char *)lm + lm->session_name_offset;
800 
801 			tv.tv_sec = lm->when / USEC_PER_SEC;
802 			tv.tv_usec = lm->when % USEC_PER_SEC;
803 
804 			func(&tv, lm->from_pid, lm->about_pid, lm->sender_uid, lm->sender_gid, lm->pri,
805 				 lm->from_name, lm->about_name, lm->session_name, lm->msg);
806 
807 			tmp_cnt -= lm->obj_sz;
808 		}
809 	}
810 
811 	if (outdata) {
812 		mig_deallocate(outdata, outdata_cnt);
813 	}
814 
815 	return NULL;
816 }
817 
818 vproc_err_t
vproc_swap_integer(vproc_t vp,vproc_gsk_t key,int64_t * inval,int64_t * outval)819 vproc_swap_integer(vproc_t vp, vproc_gsk_t key, int64_t *inval, int64_t *outval)
820 {
821 	kern_return_t kr = KERN_FAILURE;
822 	int64_t dummyval = 0;
823 	mach_port_t mp = vp ? vp->j_port : bootstrap_port;
824 	if ((kr = vproc_mig_swap_integer(mp, inval ? key : 0, outval ? key : 0, inval ? *inval : 0, outval ? outval : &dummyval)) == 0) {
825 		switch (key) {
826 		case VPROC_GSK_PERUSER_SUSPEND:
827 			if (dummyval) {
828 				/* Wait for the per-user launchd to exit before returning. */
829 				int kq = kqueue();
830 				struct kevent kev;
831 				EV_SET(&kev, dummyval, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0);
832 				int r = kevent(kq, &kev, 1, &kev, 1, NULL);
833 				(void)close(kq);
834 				if (r != 1) {
835 					return NULL;
836 				}
837 				break;
838 			}
839 		default:
840 			break;
841 		}
842 		return NULL;
843 	}
844 
845 	return (vproc_err_t)vproc_swap_integer;
846 }
847 
848 vproc_err_t
vproc_swap_complex(vproc_t vp,vproc_gsk_t key,launch_data_t inval,launch_data_t * outval)849 vproc_swap_complex(vproc_t vp, vproc_gsk_t key, launch_data_t inval, launch_data_t *outval)
850 {
851 	size_t data_offset = 0, good_enough_size = 10*1024*1024;
852 	mach_msg_type_number_t indata_cnt = 0, outdata_cnt;
853 	vm_offset_t indata = 0, outdata = 0;
854 	launch_data_t out_obj;
855 	void *rval = vproc_swap_complex;
856 	void *buf = NULL;
857 
858 	if (inval) {
859 		if (!(buf = malloc(good_enough_size))) {
860 			goto out;
861 		}
862 
863 		if ((indata_cnt = launch_data_pack(inval, buf, good_enough_size, NULL, NULL)) == 0) {
864 			goto out;
865 		}
866 
867 		indata = (vm_offset_t)buf;
868 	}
869 
870 	mach_port_t mp = vp ? vp->j_port : bootstrap_port;
871 	if (vproc_mig_swap_complex(mp, inval ? key : 0, outval ? key : 0, indata, indata_cnt, &outdata, &outdata_cnt) != 0) {
872 		goto out;
873 	}
874 
875 	if (outval) {
876 		if (!(out_obj = launch_data_unpack((void *)outdata, outdata_cnt, NULL, 0, &data_offset, NULL))) {
877 			goto out;
878 		}
879 
880 		if (!(*outval = launch_data_copy(out_obj))) {
881 			goto out;
882 		}
883 	}
884 
885 	rval = NULL;
886 out:
887 	if (buf) {
888 		free(buf);
889 	}
890 
891 	if (outdata) {
892 		mig_deallocate(outdata, outdata_cnt);
893 	}
894 
895 	return rval;
896 }
897 
898 vproc_err_t
vproc_swap_string(vproc_t vp,vproc_gsk_t key,const char * instr,const char ** outstr)899 vproc_swap_string(vproc_t vp, vproc_gsk_t key, const char *instr, const char **outstr)
900 {
901 	launch_data_t instr_data = instr ? launch_data_new_string(instr) : NULL;
902 	launch_data_t outstr_data = NULL;
903 
904 	vproc_err_t verr = vproc_swap_complex(vp, key, instr_data, &outstr_data);
905 	if (!verr && outstr) {
906 		if (launch_data_get_type(outstr_data) == LAUNCH_DATA_STRING) {
907 			*outstr = strdup(launch_data_get_string(outstr_data));
908 		} else {
909 			verr = (vproc_err_t)vproc_swap_string;
910 		}
911 		launch_data_free(outstr_data);
912 	}
913 	if (instr_data) {
914 		launch_data_free(instr_data);
915 	}
916 
917 	return verr;
918 }
919 
920 void *
reboot2(uint64_t flags)921 reboot2(uint64_t flags)
922 {
923 	mach_port_t rootbs = MACH_PORT_NULL;
924 	(void)bootstrap_get_root(bootstrap_port, &rootbs);
925 	if (vproc_mig_reboot2(rootbs, flags) == 0) {
926 		(void)mach_port_deallocate(mach_task_self(), rootbs);
927 		return NULL;
928 	}
929 
930 	return reboot2;
931 }
932 
933 vproc_err_t
_vproc_kickstart_by_label(const char * label,pid_t * out_pid,mach_port_t * out_port_name __unused,mach_port_t * out_obsrvr_port __unused,vproc_flags_t flags)934 _vproc_kickstart_by_label(const char *label, pid_t *out_pid,
935 	mach_port_t *out_port_name __unused, mach_port_t *out_obsrvr_port __unused,
936 	vproc_flags_t flags)
937 {
938 	/* Ignore the two port parameters. This SPI isn't long for this world, and
939 	 * all the current clients just leak them anyway.
940 	 */
941 	kern_return_t kr = vproc_mig_kickstart(bootstrap_port, (char *)label, out_pid, flags);
942 	if (kr == KERN_SUCCESS) {
943 		return NULL;
944 	}
945 
946 	return (vproc_err_t)_vproc_kickstart_by_label;
947 }
948 
949 vproc_err_t
_vproc_set_global_on_demand(bool state)950 _vproc_set_global_on_demand(bool state)
951 {
952 	int64_t val = state ? ~0 : 0;
953 
954 	if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_ON_DEMAND, &val, NULL) == 0) {
955 		return NULL;
956 	}
957 
958 	return (vproc_err_t)_vproc_set_global_on_demand;
959 }
960 
961 void
_vproc_logv(int pri,int err,const char * msg,va_list ap)962 _vproc_logv(int pri, int err, const char *msg, va_list ap)
963 {
964 	char flat_msg[3000];
965 
966 	vsnprintf(flat_msg, sizeof(flat_msg), msg, ap);
967 
968 	vproc_mig_log(bootstrap_port, pri, err, flat_msg);
969 }
970 
971 void
_vproc_log(int pri,const char * msg,...)972 _vproc_log(int pri, const char *msg, ...)
973 {
974 	va_list ap;
975 
976 	va_start(ap, msg);
977 	_vproc_logv(pri, 0, msg, ap);
978 	va_end(ap);
979 }
980 
981 void
_vproc_log_error(int pri,const char * msg,...)982 _vproc_log_error(int pri, const char *msg, ...)
983 {
984 	int saved_errno = errno;
985 	va_list ap;
986 
987 	va_start(ap, msg);
988 	_vproc_logv(pri, saved_errno, msg, ap);
989 	va_end(ap);
990 }
991 
992 /* The type naming convention is as follows:
993  * For requests...
994  *     union __RequestUnion__<userprefix><subsystem>_subsystem
995  * For replies...
996  *     union __ReplyUnion__<userprefix><subsystem>_subsystem
997  */
998 union maxmsgsz {
999 	union __RequestUnion__helper_downcall_launchd_helper_subsystem req;
1000 	union __ReplyUnion__helper_downcall_launchd_helper_subsystem rep;
1001 };
1002 
1003 static const size_t vprocmgr_helper_maxmsgsz = sizeof(union maxmsgsz);
1004 
1005 kern_return_t
helper_recv_wait(mach_port_t p,int status)1006 helper_recv_wait(mach_port_t p, int status)
1007 {
1008 #if __LAUNCH_MACH_PORT_CONTEXT_T_DEFINED__
1009 	mach_port_context_t ctx = status;
1010 #else
1011 	mach_vm_address_t ctx = status;
1012 #endif
1013 
1014 	return (errno = mach_port_set_context(mach_task_self(), p, ctx));
1015 }
1016 
1017 int
launch_wait(mach_port_t port)1018 launch_wait(mach_port_t port)
1019 {
1020 	int status = -1;
1021 	errno = mach_msg_server_once(launchd_helper_server, vprocmgr_helper_maxmsgsz, port, 0);
1022 	if (errno == MACH_MSG_SUCCESS) {
1023 #if __LAUNCH_MACH_PORT_CONTEXT_T_DEFINED__
1024 		mach_port_context_t ctx = 0;
1025 #else
1026 		mach_vm_address_t ctx = 0;
1027 #endif
1028 		if ((errno = mach_port_get_context(mach_task_self(), port, &ctx)) == KERN_SUCCESS) {
1029 			status = ctx;
1030 		}
1031 	}
1032 
1033 	return status;
1034 }
1035 
1036 #ifndef __FreeBSD__
1037 launch_data_t
launch_socket_service_check_in(void)1038 launch_socket_service_check_in(void)
1039 {
1040 	launch_data_t reply = NULL;
1041 
1042 	size_t big_enough = 10 * 1024;
1043 	void *buff = malloc(big_enough);
1044 	if (buff) {
1045 		launch_data_t req = launch_data_new_string(LAUNCH_KEY_CHECKIN);
1046 		if (req) {
1047 			size_t sz = launch_data_pack(req, buff, big_enough, NULL, NULL);
1048 			if (sz) {
1049 				vm_address_t sreply = 0;
1050 				mach_msg_size_t sreplyCnt = 0;
1051 				mach_port_array_t fdps = NULL;
1052 				mach_msg_size_t fdpsCnt = 0;
1053 				kern_return_t kr = vproc_mig_legacy_ipc_request(bootstrap_port, (vm_address_t)buff, sz, NULL, 0, &sreply, &sreplyCnt, &fdps, &fdpsCnt, _audit_session_self());
1054 				if (kr == BOOTSTRAP_SUCCESS) {
1055 					int fds[128];
1056 
1057 					size_t i = 0;
1058 					size_t nfds = fdpsCnt / sizeof(fdps[0]);
1059 					for (i = 0; i < nfds; i++) {
1060 						fds[i] = fileport_makefd(fdps[i]);
1061 						(void)mach_port_deallocate(mach_task_self(), fdps[i]);
1062 					}
1063 
1064 					size_t dataoff = 0;
1065 					size_t fdoff = 0;
1066 					reply = launch_data_unpack((void *)sreply, sreplyCnt, fds, nfds, &dataoff, &fdoff);
1067 					reply = launch_data_copy(reply);
1068 
1069 					mig_deallocate(sreply, sreplyCnt);
1070 					mig_deallocate((vm_address_t)fdps, fdpsCnt);
1071 				}
1072 			}
1073 
1074 			launch_data_free(req);
1075 		}
1076 
1077 		free(buff);
1078 	}
1079 
1080 	return reply;
1081 }
1082 #endif
1083