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