xref: /NextBSD/lib/liblaunch/liblaunch.c (revision 2c3410036d6d08784dd6a9bbf39d6534488046a1)
1 /*
2  * Copyright (c) 2005-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 "launch.h"
23 #include "launch_priv.h"
24 #include "launch_internal.h"
25 #include "ktrace.h"
26 
27 #include <mach/mach.h>
28 #include <mach/mach_port.h>
29 #include <libkern/OSByteOrder.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/fcntl.h>
33 #include <sys/un.h>
34 #include <sys/uio.h>
35 #include <sys/stat.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <pthread.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <pwd.h>
43 #include <assert.h>
44 #include <uuid/uuid.h>
45 #include <sys/syscall.h>
46 #include <sys/fileport.h>
47 #include <dlfcn.h>
48 
49 #include "job.h"
50 
51 struct _launch_data {
52 	uint64_t type;
53 	union {
54 		struct {
55 			union {
56 				launch_data_t *_array;
57 				char *string;
58 				void *opaque;
59 				int64_t __junk;
60 			};
61 			union {
62 				uint64_t _array_cnt;
63 				uint64_t string_len;
64 				uint64_t opaque_size;
65 			};
66 		};
67 		int64_t fd;
68 		uint64_t  mp;
69 		uint64_t err;
70 		int64_t number;
71 		uint64_t boolean; /* We'd use 'bool' but this struct needs to be used under Rosetta, and sizeof(bool) is different between PowerPC and Intel */
72 		double float_num;
73 	};
74 };
75 
76 #include "bootstrap.h"
77 #include "vproc.h"
78 #include "vproc_priv.h"
79 #include "vproc_internal.h"
80 
81 /* __OSBogusByteSwap__() must not really exist in the symbol namespace
82  * in order for the following to generate an error at build time.
83  */
84 extern void __OSBogusByteSwap__(void);
85 
86 #define host2wire(x)				\
87 	({ typeof (x) _X, _x = (x);		\
88 	 switch (sizeof(_x)) {			\
89 	 case 8:				\
90 	 	_X = OSSwapHostToLittleInt64(_x);	\
91 	 	break;				\
92 	 case 4:				\
93 	 	_X = OSSwapHostToLittleInt32(_x);	\
94 	 	break;				\
95 	 case 2:				\
96 	 	_X = OSSwapHostToLittleInt16(_x);	\
97 	 	break;				\
98 	 case 1:				\
99 	 	_X = _x;			\
100 		break;				\
101 	 default:				\
102 	 	__OSBogusByteSwap__();		\
103 		break;				\
104 	 }					\
105 	 _X;					\
106 	 })
107 
108 
109 #define big2wire(x)				\
110 	({ typeof (x) _X, _x = (x);		\
111 	 switch (sizeof(_x)) {			\
112 	 case 8:				\
113 	 	_X = OSSwapLittleToHostInt64(_x);	\
114 	 	break;				\
115 	 case 4:				\
116 	 	_X = OSSwapLittleToHostInt32(_x);	\
117 	 	break;				\
118 	 case 2:				\
119 	 	_X = OSSwapLittleToHostInt16(_x);	\
120 	 	break;				\
121 	 case 1:				\
122 	 	_X = _x;			\
123 		break;				\
124 	 default:				\
125 	 	__OSBogusByteSwap__();		\
126 		break;				\
127 	 }					\
128 	 _X;					\
129 	 })
130 
131 union _launch_double_u {
132 	uint64_t iv;
133 	double dv;
134 };
135 
136 #define host2wire_f(x) ({ \
137 	typeof(x) _F, _f = (x); \
138 	union _launch_double_u s; \
139 	s.dv = _f; \
140 	s.iv = host2wire(s.iv); \
141 	_F = s.dv; \
142 	_F; \
143 })
144 
145 #define big2wire_f(x) ({ \
146 	typeof(x) _F, _f = (x); \
147 	union _launch_double_u s; \
148 	s.dv = _f; \
149 	s.iv = big2wire(s.iv); \
150 	_F = s.dv; \
151 	_F; \
152 })
153 
154 
155 struct launch_msg_header {
156 	uint64_t magic;
157 	uint64_t len;
158 };
159 
160 #define LAUNCH_MSG_HEADER_MAGIC 0xD2FEA02366B39A41ull
161 
162 enum {
163 	LAUNCHD_USE_CHECKIN_FD,
164 	LAUNCHD_USE_OTHER_FD,
165 };
166 struct _launch {
167 	uint8_t	*sendbuf;
168 	int	*sendfds;
169 	uint8_t	*recvbuf;
170 	int	*recvfds;
171 	size_t	sendlen;
172 	size_t	sendfdcnt;
173 	size_t	recvlen;
174 	size_t	recvfdcnt;
175 	int which;
176 	int cifd;
177 	int	fd;
178 };
179 
180 #if 0
181 static launch_data_t launch_data_array_pop_first(launch_data_t where);
182 #endif
183 static int _fd(int fd);
184 static void launch_client_init(void);
185 #if 0
186 static void launch_msg_getmsgs(launch_data_t m, void *context);
187 #endif
188 static launch_data_t launch_msg_internal(launch_data_t d);
189 static void launch_mach_checkin_service(launch_data_t obj, const char *key, void *context);
190 
191 void
_launch_init_globals(launch_globals_t globals)192 _launch_init_globals(launch_globals_t globals)
193 {
194 	pthread_once_t once = PTHREAD_ONCE_INIT;
195 	globals->lc_once = once;
196 	pthread_mutex_init(&globals->lc_mtx, NULL);
197 }
198 
199 #if !_LIBLAUNCH_HAS_ALLOC_ONCE
200 static launch_globals_t __launch_globals;
201 
202 static void
_launch_globals_init(void)203 _launch_globals_init(void)
204 {
205 	__launch_globals = calloc(1, sizeof(struct launch_globals_s));
206 	_launch_init_globals(__launch_globals);
207 }
208 
209 launch_globals_t
_launch_globals_impl(void)210 _launch_globals_impl(void)
211 {
212 	static pthread_once_t once = PTHREAD_ONCE_INIT;
213 	pthread_once(&once, &_launch_globals_init);
214 	return __launch_globals;
215 }
216 #endif
217 
218 void
launch_client_init(void)219 launch_client_init(void)
220 {
221 	struct sockaddr_un sun;
222 	char *where = getenv(LAUNCHD_SOCKET_ENV);
223 	char *_launchd_fd = getenv(LAUNCHD_TRUSTED_FD_ENV);
224 	int dfd, lfd = -1, cifd = -1;
225 	kern_return_t kr;
226 	name_t spath;
227 
228 	if (_launchd_fd) {
229 		cifd = strtol(_launchd_fd, NULL, 10);
230 		if ((dfd = dup(cifd)) >= 0) {
231 			close(dfd);
232 			_fd(cifd);
233 		} else {
234 			cifd = -1;
235 		}
236 		unsetenv(LAUNCHD_TRUSTED_FD_ENV);
237 	}
238 
239 	memset(&sun, 0, sizeof(sun));
240 	sun.sun_family = AF_UNIX;
241 
242 	/* The rules are as follows.
243 	 * - All users (including root) talk to their per-user launchd's by default.
244 	 * - If we have been invoked under sudo, talk to the system launchd.
245 	 * - If we're the root user and the __USE_SYSTEM_LAUNCHD environment variable is set, then
246 	 *   talk to the system launchd.
247 	 */
248 	if (where && where[0] != '\0') {
249 		strncpy(sun.sun_path, where, sizeof(sun.sun_path));
250 	} else {
251 		kr = _vprocmgr_getsocket(spath);
252 		if (kr == 0) {
253 			if ((getenv("SUDO_COMMAND") || getenv("__USE_SYSTEM_LAUNCHD")) && geteuid() == 0) {
254 				/* Talk to the system launchd. */
255 				strncpy(sun.sun_path, LAUNCHD_SOCK_PREFIX "/sock", sizeof(sun.sun_path));
256 			} else {
257 				/* Talk to our per-user launchd. */
258 				size_t min_len;
259 
260 				min_len = sizeof(sun.sun_path) < sizeof(spath) ? sizeof(sun.sun_path) : sizeof(spath);
261 
262 				strncpy(sun.sun_path, spath, min_len);
263 			}
264 		} else
265 			fprintf(stderr, "_vprocmgr_getsocket(): 0x%x\n", kr);
266 	}
267 
268 	launch_globals_t globals = _launch_globals();
269 	if ((lfd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) {
270 		goto out_bad;
271 	}
272 
273 #if TARGET_OS_EMBEDDED
274 	(void)vproc_swap_integer(NULL, VPROC_GSK_EMBEDDEDROOTEQUIVALENT, NULL, &globals->s_am_embedded_god);
275 #endif
276 	if (-1 == connect(lfd, (struct sockaddr *)&sun, sizeof(sun))) {
277 		if (cifd != -1 || globals->s_am_embedded_god) {
278 			/* There is NO security enforced by this check. This is just a hint to our
279 			 * library that we shouldn't error out due to failing to open this socket. If
280 			 * we inherited a trusted file descriptor, we shouldn't fail. This should be
281 			 * adequate for clients' expectations.
282 			 */
283 			close(lfd);
284 			lfd = -1;
285 		} else {
286 			goto out_bad;
287 		}
288 	}
289 
290 	if (!(globals->l = launchd_fdopen(lfd, cifd))) {
291 		goto out_bad;
292 	}
293 
294 	if (!(globals->async_resp = launch_data_alloc(LAUNCH_DATA_ARRAY))) {
295 		goto out_bad;
296 	}
297 
298 	return;
299 out_bad:
300 	if (globals->l) {
301 		launchd_close(globals->l, close);
302 		globals->l = NULL;
303 	} else if (lfd != -1) {
304 		close(lfd);
305 	}
306 	if (cifd != -1) {
307 		close(cifd);
308 	}
309 }
310 
311 launch_data_t
launch_data_alloc(launch_data_type_t t)312 launch_data_alloc(launch_data_type_t t)
313 {
314 	launch_data_t d = calloc(1, sizeof(struct _launch_data));
315 
316 	if (d) {
317 		d->type = t;
318 		switch (t) {
319 		case LAUNCH_DATA_DICTIONARY:
320 		case LAUNCH_DATA_ARRAY:
321 			d->_array = malloc(0);
322 			break;
323 		case LAUNCH_DATA_OPAQUE:
324 			d->opaque = malloc(0);
325 		default:
326 			break;
327 		}
328 	}
329 
330 	return d;
331 }
332 
333 launch_data_type_t
launch_data_get_type(launch_data_t d)334 launch_data_get_type(launch_data_t d)
335 {
336 	return d->type;
337 }
338 
339 void
launch_data_free(launch_data_t d)340 launch_data_free(launch_data_t d)
341 {
342 	size_t i;
343 
344 	switch (d->type) {
345 	case LAUNCH_DATA_DICTIONARY:
346 	case LAUNCH_DATA_ARRAY:
347 		for (i = 0; i < d->_array_cnt; i++) {
348 			if (d->_array[i]) {
349 				launch_data_free(d->_array[i]);
350 			}
351 		}
352 		free(d->_array);
353 		break;
354 	case LAUNCH_DATA_STRING:
355 		if (d->string)
356 			free(d->string);
357 		break;
358 	case LAUNCH_DATA_OPAQUE:
359 		if (d->opaque)
360 			free(d->opaque);
361 		break;
362 	default:
363 		break;
364 	}
365 	free(d);
366 }
367 
368 size_t
launch_data_dict_get_count(launch_data_t dict)369 launch_data_dict_get_count(launch_data_t dict)
370 {
371 	return dict->_array_cnt / 2;
372 }
373 
374 bool
launch_data_dict_insert(launch_data_t dict,launch_data_t what,const char * key)375 launch_data_dict_insert(launch_data_t dict, launch_data_t what, const char *key)
376 {
377 	size_t i;
378 	launch_data_t thekey = launch_data_alloc(LAUNCH_DATA_STRING);
379 
380 	launch_data_set_string(thekey, key);
381 
382 	for (i = 0; i < dict->_array_cnt; i += 2) {
383 		if (!strcasecmp(key, dict->_array[i]->string)) {
384 			launch_data_array_set_index(dict, thekey, i);
385 			launch_data_array_set_index(dict, what, i + 1);
386 			return true;
387 		}
388 	}
389 	launch_data_array_set_index(dict, thekey, i);
390 	launch_data_array_set_index(dict, what, i + 1);
391 	return true;
392 }
393 
394 launch_data_t
launch_data_dict_lookup(launch_data_t dict,const char * key)395 launch_data_dict_lookup(launch_data_t dict, const char *key)
396 {
397 	size_t i;
398 
399 	if (LAUNCH_DATA_DICTIONARY != dict->type)
400 		return NULL;
401 
402 	for (i = 0; i < dict->_array_cnt; i += 2) {
403 		if (!strcasecmp(key, dict->_array[i]->string))
404 			return dict->_array[i + 1];
405 	}
406 
407 	return NULL;
408 }
409 
410 bool
launch_data_dict_remove(launch_data_t dict,const char * key)411 launch_data_dict_remove(launch_data_t dict, const char *key)
412 {
413 	size_t i;
414 
415 	for (i = 0; i < dict->_array_cnt; i += 2) {
416 		if (!strcasecmp(key, dict->_array[i]->string))
417 			break;
418 	}
419 	if (i == dict->_array_cnt)
420 		return false;
421 	launch_data_free(dict->_array[i]);
422 	launch_data_free(dict->_array[i + 1]);
423 	memmove(dict->_array + i, dict->_array + i + 2, (dict->_array_cnt - (i + 2)) * sizeof(launch_data_t));
424 	dict->_array_cnt -= 2;
425 	return true;
426 }
427 
428 void
launch_data_dict_iterate(launch_data_t dict,void (* cb)(launch_data_t,const char *,void *),void * context)429 launch_data_dict_iterate(launch_data_t dict, void (*cb)(launch_data_t, const char *, void *), void *context)
430 {
431 	size_t i;
432 
433 	if (LAUNCH_DATA_DICTIONARY != dict->type) {
434 		return;
435 	}
436 
437 	for (i = 0; i < dict->_array_cnt; i += 2) {
438 		cb(dict->_array[i + 1], dict->_array[i]->string, context);
439 	}
440 }
441 
442 bool
launch_data_array_set_index(launch_data_t where,launch_data_t what,size_t ind)443 launch_data_array_set_index(launch_data_t where, launch_data_t what, size_t ind)
444 {
445 	if ((ind + 1) >= where->_array_cnt) {
446 		where->_array = reallocf(where->_array, (ind + 1) * sizeof(launch_data_t));
447 		memset(where->_array + where->_array_cnt, 0, (ind + 1 - where->_array_cnt) * sizeof(launch_data_t));
448 		where->_array_cnt = ind + 1;
449 	}
450 
451 	if (where->_array[ind]) {
452 		launch_data_free(where->_array[ind]);
453 	}
454 
455 	where->_array[ind] = what;
456 	return true;
457 }
458 
459 launch_data_t
launch_data_array_get_index(launch_data_t where,size_t ind)460 launch_data_array_get_index(launch_data_t where, size_t ind)
461 {
462 	if (LAUNCH_DATA_ARRAY != where->type || ind >= where->_array_cnt) {
463 		return NULL;
464 	} else {
465 		return where->_array[ind];
466 	}
467 }
468 
469 #if 0
470 launch_data_t
471 launch_data_array_pop_first(launch_data_t where)
472 {
473 	launch_data_t r = NULL;
474 
475 	if (where->_array_cnt > 0) {
476 		r = where->_array[0];
477 		memmove(where->_array, where->_array + 1, (where->_array_cnt - 1) * sizeof(launch_data_t));
478 		where->_array_cnt--;
479 	}
480 	return r;
481 }
482 #endif
483 
484 size_t
launch_data_array_get_count(launch_data_t where)485 launch_data_array_get_count(launch_data_t where)
486 {
487 	if (LAUNCH_DATA_ARRAY != where->type)
488 		return 0;
489 	return where->_array_cnt;
490 }
491 
492 bool
launch_data_set_errno(launch_data_t d,int e)493 launch_data_set_errno(launch_data_t d, int e)
494 {
495 	d->err = e;
496 	return true;
497 }
498 
499 bool
launch_data_set_fd(launch_data_t d,int fd)500 launch_data_set_fd(launch_data_t d, int fd)
501 {
502 	d->fd = fd;
503 	return true;
504 }
505 
506 bool
launch_data_set_machport(launch_data_t d,mach_port_t p)507 launch_data_set_machport(launch_data_t d, mach_port_t p)
508 {
509 	d->mp = p;
510 	return true;
511 }
512 
513 bool
launch_data_set_integer(launch_data_t d,long long n)514 launch_data_set_integer(launch_data_t d, long long n)
515 {
516 	d->number = n;
517 	return true;
518 }
519 
520 bool
launch_data_set_bool(launch_data_t d,bool b)521 launch_data_set_bool(launch_data_t d, bool b)
522 {
523 	d->boolean = b;
524 	return true;
525 }
526 
527 bool
launch_data_set_real(launch_data_t d,double n)528 launch_data_set_real(launch_data_t d, double n)
529 {
530 	d->float_num = n;
531 	return true;
532 }
533 
534 bool
launch_data_set_string(launch_data_t d,const char * s)535 launch_data_set_string(launch_data_t d, const char *s)
536 {
537 	if (d->string)
538 		free(d->string);
539 	d->string = strdup(s);
540 	if (d->string) {
541 		d->string_len = strlen(d->string);
542 		return true;
543 	}
544 	return false;
545 }
546 
547 bool
launch_data_set_opaque(launch_data_t d,const void * o,size_t os)548 launch_data_set_opaque(launch_data_t d, const void *o, size_t os)
549 {
550 	d->opaque_size = os;
551 	if (d->opaque)
552 		free(d->opaque);
553 	d->opaque = malloc(os);
554 	if (d->opaque) {
555 		memcpy(d->opaque, o, os);
556 		return true;
557 	}
558 	return false;
559 }
560 
561 int
launch_data_get_errno(launch_data_t d)562 launch_data_get_errno(launch_data_t d)
563 {
564 	return d->err;
565 }
566 
567 int
launch_data_get_fd(launch_data_t d)568 launch_data_get_fd(launch_data_t d)
569 {
570 	return d->fd;
571 }
572 
573 mach_port_t
launch_data_get_machport(launch_data_t d)574 launch_data_get_machport(launch_data_t d)
575 {
576 	return d->mp;
577 }
578 
579 long long
launch_data_get_integer(launch_data_t d)580 launch_data_get_integer(launch_data_t d)
581 {
582 	return d->number;
583 }
584 
585 bool
launch_data_get_bool(launch_data_t d)586 launch_data_get_bool(launch_data_t d)
587 {
588 	return d->boolean;
589 }
590 
591 double
launch_data_get_real(launch_data_t d)592 launch_data_get_real(launch_data_t d)
593 {
594 	return d->float_num;
595 }
596 
597 const char *
launch_data_get_string(launch_data_t d)598 launch_data_get_string(launch_data_t d)
599 {
600 	if (LAUNCH_DATA_STRING != d->type)
601 		return NULL;
602 	return d->string;
603 }
604 
605 void *
launch_data_get_opaque(launch_data_t d)606 launch_data_get_opaque(launch_data_t d)
607 {
608 	if (LAUNCH_DATA_OPAQUE != d->type)
609 		return NULL;
610 	return d->opaque;
611 }
612 
613 size_t
launch_data_get_opaque_size(launch_data_t d)614 launch_data_get_opaque_size(launch_data_t d)
615 {
616 	return d->opaque_size;
617 }
618 
619 int
launchd_getfd(launch_t l)620 launchd_getfd(launch_t l)
621 {
622 	return (l->which == LAUNCHD_USE_CHECKIN_FD) ? l->cifd : l->fd;
623 }
624 
625 launch_t
launchd_fdopen(int fd,int cifd)626 launchd_fdopen(int fd, int cifd)
627 {
628 	launch_t c;
629 
630 	c = calloc(1, sizeof(struct _launch));
631 	if (!c)
632 		return NULL;
633 
634 	c->fd = fd;
635 	c->cifd = cifd;
636 
637 	if (c->fd == -1 || (c->fd != -1 && c->cifd != -1)) {
638 		c->which = LAUNCHD_USE_CHECKIN_FD;
639 	} else if (c->cifd == -1) {
640 		c->which = LAUNCHD_USE_OTHER_FD;
641 	}
642 
643 	fcntl(fd, F_SETFL, O_NONBLOCK);
644 	fcntl(cifd, F_SETFL, O_NONBLOCK);
645 
646 	if ((c->sendbuf = malloc(0)) == NULL)
647 		goto out_bad;
648 	if ((c->sendfds = malloc(0)) == NULL)
649 		goto out_bad;
650 	if ((c->recvbuf = malloc(0)) == NULL)
651 		goto out_bad;
652 	if ((c->recvfds = malloc(0)) == NULL)
653 		goto out_bad;
654 
655 	return c;
656 
657 out_bad:
658 	if (c->sendbuf)
659 		free(c->sendbuf);
660 	if (c->sendfds)
661 		free(c->sendfds);
662 	if (c->recvbuf)
663 		free(c->recvbuf);
664 	if (c->recvfds)
665 		free(c->recvfds);
666 	free(c);
667 	return NULL;
668 }
669 
670 void
launchd_close(launch_t lh,typeof (close)closefunc)671 launchd_close(launch_t lh, typeof(close) closefunc)
672 {
673 	launch_globals_t globals = _launch_globals();
674 
675 	if (globals->in_flight_msg_recv_client == lh) {
676 		globals->in_flight_msg_recv_client = NULL;
677 	}
678 
679 	if (lh->sendbuf)
680 		free(lh->sendbuf);
681 	if (lh->sendfds)
682 		free(lh->sendfds);
683 	if (lh->recvbuf)
684 		free(lh->recvbuf);
685 	if (lh->recvfds)
686 		free(lh->recvfds);
687 	closefunc(lh->fd);
688 	closefunc(lh->cifd);
689 	free(lh);
690 }
691 
692 #define ROUND_TO_64BIT_WORD_SIZE(x)	((x + 7) & ~7)
693 
694 size_t
launch_data_pack(launch_data_t d,uint8_t * where,size_t len,int * fd_where,size_t * fd_cnt)695 launch_data_pack(launch_data_t d, uint8_t *where, size_t len, int *fd_where, size_t *fd_cnt)
696 {
697 	launch_data_t o_in_w = (void *)where;
698 	size_t i, rsz, node_data_len = sizeof(struct _launch_data);
699 
700 	if (node_data_len > len) {
701 		return 0;
702 	}
703 
704 	where += node_data_len;
705 
706 	o_in_w->type = host2wire(d->type);
707 
708 	size_t pad_len = 0;
709 	switch (d->type) {
710 	case LAUNCH_DATA_INTEGER:
711 		o_in_w->number = host2wire(d->number);
712 		break;
713 	case LAUNCH_DATA_REAL:
714 		o_in_w->float_num = host2wire_f(d->float_num);
715 		break;
716 	case LAUNCH_DATA_BOOL:
717 		o_in_w->boolean = host2wire(d->boolean);
718 		break;
719 	case LAUNCH_DATA_ERRNO:
720 		o_in_w->err = host2wire(d->err);
721 		break;
722 	case LAUNCH_DATA_FD:
723 		o_in_w->fd = host2wire(d->fd);
724 		if (fd_where && d->fd != -1) {
725 			fd_where[*fd_cnt] = d->fd;
726 			(*fd_cnt)++;
727 		}
728 		break;
729 	case LAUNCH_DATA_STRING:
730 		o_in_w->string_len = host2wire(d->string_len);
731 		node_data_len += ROUND_TO_64BIT_WORD_SIZE(d->string_len + 1);
732 
733 		if (node_data_len > len) {
734 			return 0;
735 		}
736 		memcpy(where, d->string, d->string_len + 1);
737 
738 		/* Zero padded data. */
739 		pad_len = ROUND_TO_64BIT_WORD_SIZE(d->string_len + 1) - (d->string_len + 1);
740 		bzero(where + d->string_len + 1, pad_len);
741 
742 		break;
743 	case LAUNCH_DATA_OPAQUE:
744 		o_in_w->opaque_size = host2wire(d->opaque_size);
745 		node_data_len += ROUND_TO_64BIT_WORD_SIZE(d->opaque_size);
746 		if (node_data_len > len) {
747 			return 0;
748 		}
749 		memcpy(where, d->opaque, d->opaque_size);
750 
751 		/* Zero padded data. */
752 		pad_len = ROUND_TO_64BIT_WORD_SIZE(d->opaque_size) - d->opaque_size;
753 		bzero(where + d->opaque_size, pad_len);
754 
755 		break;
756 	case LAUNCH_DATA_DICTIONARY:
757 	case LAUNCH_DATA_ARRAY:
758 		o_in_w->_array_cnt = host2wire(d->_array_cnt);
759 		node_data_len += d->_array_cnt * sizeof(uint64_t);
760 		if (node_data_len > len) {
761 			return 0;
762 		}
763 
764 		where += d->_array_cnt * sizeof(uint64_t);
765 
766 		for (i = 0; i < d->_array_cnt; i++) {
767 			rsz = launch_data_pack(d->_array[i], where, len - node_data_len, fd_where, fd_cnt);
768 			if (rsz == 0) {
769 				return 0;
770 			}
771 			where += rsz;
772 			node_data_len += rsz;
773 		}
774 		break;
775 	default:
776 		break;
777 	}
778 
779 	return node_data_len;
780 }
781 
782 launch_data_t
launch_data_unpack(uint8_t * data,size_t data_size,int * fds,size_t fd_cnt,size_t * data_offset,size_t * fdoffset)783 launch_data_unpack(uint8_t *data, size_t data_size, int *fds, size_t fd_cnt, size_t *data_offset, size_t *fdoffset)
784 {
785 	launch_data_t r = (void*)(data + *data_offset);
786 	size_t i, tmpcnt;
787 
788 	//Check for integer underflow
789 	if (data_size < *data_offset)
790 		return NULL;
791 
792 	if ((data_size - *data_offset) < sizeof(struct _launch_data))
793 		return NULL;
794 	*data_offset += sizeof(struct _launch_data);
795 
796 	switch (big2wire(r->type)) {
797 	case LAUNCH_DATA_DICTIONARY:
798 	case LAUNCH_DATA_ARRAY:
799 		tmpcnt = big2wire(r->_array_cnt);
800 
801 		//Check for integer overflows
802 		if (tmpcnt > SIZE_MAX / sizeof(uint64_t)) {
803 			errno = EAGAIN;
804 			return NULL;
805 		}
806 
807 		if ((data_size - *data_offset) < (tmpcnt * sizeof(uint64_t))) {
808 			errno = EAGAIN;
809 			return NULL;
810 		}
811 		r->_array = (void *)(data + *data_offset);
812 		*data_offset += tmpcnt * sizeof(uint64_t);
813 		for (i = 0; i < tmpcnt; i++) {
814 			r->_array[i] = launch_data_unpack(data, data_size, fds, fd_cnt, data_offset, fdoffset);
815 			if (r->_array[i] == NULL)
816 				return NULL;
817 		}
818 		r->_array_cnt = tmpcnt;
819 		break;
820 	case LAUNCH_DATA_STRING:
821 		tmpcnt = big2wire(r->string_len);
822 		if ((data_size - *data_offset) < (tmpcnt + 1)) {
823 			errno = EAGAIN;
824 			return NULL;
825 		}
826 		r->string = data + *data_offset;
827 		r->string_len = tmpcnt;
828 		*data_offset += ROUND_TO_64BIT_WORD_SIZE(tmpcnt + 1);
829 		break;
830 	case LAUNCH_DATA_OPAQUE:
831 		tmpcnt = big2wire(r->opaque_size);
832 		if ((data_size - *data_offset) < tmpcnt) {
833 			errno = EAGAIN;
834 			return NULL;
835 		}
836 		r->opaque = data + *data_offset;
837 		r->opaque_size = tmpcnt;
838 		*data_offset += ROUND_TO_64BIT_WORD_SIZE(tmpcnt);
839 		break;
840 	case LAUNCH_DATA_FD:
841 		if (r->fd != -1 && fd_cnt > *fdoffset) {
842 			r->fd = _fd(fds[*fdoffset]);
843 			*fdoffset += 1;
844 		}
845 		break;
846 	case LAUNCH_DATA_INTEGER:
847 		r->number = big2wire(r->number);
848 		break;
849 	case LAUNCH_DATA_REAL:
850 		r->float_num = big2wire_f(r->float_num);
851 		break;
852 	case LAUNCH_DATA_BOOL:
853 		r->boolean = big2wire(r->boolean);
854 		break;
855 	case LAUNCH_DATA_ERRNO:
856 		r->err = big2wire(r->err);
857 	case LAUNCH_DATA_MACHPORT:
858 		break;
859 	default:
860 		errno = EINVAL;
861 		return NULL;
862 		break;
863 	}
864 
865 	r->type = big2wire(r->type);
866 
867 	return r;
868 }
869 
870 launch_data_t
launch_msg_internal(launch_data_t d)871 launch_msg_internal(launch_data_t d)
872 {
873 	vm_offset_t request;
874 	mach_msg_type_number_t requestCnt;
875 	mach_port_array_t request_fds;
876 	mach_msg_type_number_t request_fdsCnt;
877 	vm_offset_t reply ;
878 	mach_msg_type_number_t replyCnt;
879 	mach_port_array_t reply_fds;
880 	mach_msg_type_number_t reply_fdsCnt;
881 	launch_data_t ldreply;
882 	size_t i;
883 	size_t nfds = 0;
884 	kern_return_t kr;
885 
886 	requestCnt = 1024 * 1024;
887 	mig_allocate(&request, requestCnt);
888 
889 	int out_fds[128];
890 	size_t nout_fds = 0;
891 	size_t sz = launch_data_pack(d, (void *)request, requestCnt, out_fds, &nout_fds);
892 	if (!sz) {
893 		goto out_bad;
894 	}
895 
896 	if (nout_fds) {
897 		if (nout_fds > 128) {
898 			goto out_bad;
899 		}
900 
901 		request_fdsCnt = nout_fds * sizeof(sizeof(out_fds[0]));
902 		mig_allocate((vm_address_t *)&request_fds, request_fdsCnt);
903 		if (!request_fds) {
904 			goto out_bad;
905 		}
906 
907 		for (i = 0; i < nout_fds; i++) {
908 			mach_port_t fp = MACH_PORT_NULL;
909 			/* Whatever. Worst case is that we insert MACH_PORT_NULL. Not a big
910 			 * deal. Note, these get stuffed into an array whose disposition is
911 			 * mach_port_move_send_t, so we don't have to worry about them after
912 			 * returning.
913 			 */
914 			if (fileport_makeport(out_fds[i], &fp) != 0) {
915 				fprintf(stderr, "Could not pack response descriptor at index: %zu: %d: %s", i, errno, strerror(errno));
916 			}
917 			request_fds[i] = fp;
918 		}
919 	} else {
920 		request_fds = NULL;
921 		request_fdsCnt = 0;
922 	}
923 
924 	kr = vproc_mig_ipc_request(bootstrap_port,
925 		request,
926 		requestCnt,
927 		request_fds,
928 		request_fdsCnt,
929 		&reply,
930 		&replyCnt,
931 		&reply_fds,
932 		&reply_fdsCnt,
933 		0);
934 
935 	if (kr != KERN_SUCCESS) {
936 		fprintf(stderr, "vproc_mig_ipc_request: kr=%x\n", kr);
937 		return NULL;
938 	}
939 
940 	nfds = reply_fdsCnt / sizeof((reply_fds)[0]);
941 	if (nfds > 128) {
942 		fprintf(stderr, "Too many incoming descriptors: %zu", nfds);
943 		return NULL;
944 	}
945 
946 	int in_fds[128];
947 	for (i = 0; i < nfds; i++) {
948 		in_fds[i] = _fd(fileport_makefd(reply_fds[i]));
949 		if (in_fds[i] == -1) {
950 			fprintf(stderr, "Bad descriptor passed in legacy IPC request at index: %zu", i);
951 		}
952 	}
953 
954 
955 	size_t dataoff = 0;
956 	size_t fdoff = 0;
957 	ldreply = launch_data_unpack((void *)reply, replyCnt, in_fds, nfds, &dataoff, &fdoff);
958 	if (!ldreply) {
959 		fprintf(stderr, "Invalid legacy IPC reply passed.");
960 		goto out_bad;
961 	}
962 
963 	mig_deallocate(request, requestCnt);
964 
965 	return (ldreply);
966 
967 out_bad:
968 	for (i = 0; i < nfds; i++) {
969 		(void)close(in_fds[i]);
970 	}
971 
972 //	for (i = 0; i < nout_fds; i++) {
973 //		(void)launchd_mport_deallocate((*reply_fds)[i]);
974 //	}
975 
976 	if (request) {
977 		mig_deallocate(request, requestCnt);
978 	}
979 
980 	if (ldreply) {
981 		launch_data_free(ldreply);
982 	}
983 
984 	return (NULL);
985 }
986 
987 int
launchd_msg_send(launch_t lh,launch_data_t d)988 launchd_msg_send(launch_t lh, launch_data_t d)
989 {
990 
991 	struct launch_msg_header lmh;
992 	struct cmsghdr *cm = NULL;
993 	struct msghdr mh;
994 	struct iovec iov[2];
995 	size_t sentctrllen = 0;
996 	int r;
997 
998 	int fd2use = launchd_getfd(lh);
999 	if (fd2use == -1) {
1000 		errno = EPERM;
1001 		return -1;
1002 	}
1003 
1004 	memset(&mh, 0, sizeof(mh));
1005 
1006 	/* confirm that the next hack works */
1007 	assert((d && lh->sendlen == 0) || (!d && lh->sendlen));
1008 
1009 	if (d) {
1010 		size_t fd_slots_used = 0;
1011 		size_t good_enough_size = 10 * 1024 * 1024;
1012 		uint64_t msglen;
1013 
1014 		/* hack, see the above assert to verify "correctness" */
1015 		free(lh->sendbuf);
1016 		lh->sendbuf = malloc(good_enough_size);
1017 		if (!lh->sendbuf) {
1018 			errno = ENOMEM;
1019 			return -1;
1020 		}
1021 
1022 		free(lh->sendfds);
1023 		lh->sendfds = malloc(4 * 1024);
1024 		if (!lh->sendfds) {
1025 			free(lh->sendbuf);
1026 			lh->sendbuf = NULL;
1027 			errno = ENOMEM;
1028 			return -1;
1029 		}
1030 
1031 		lh->sendlen = launch_data_pack(d, lh->sendbuf, good_enough_size, lh->sendfds, &fd_slots_used);
1032 
1033 		if (lh->sendlen == 0) {
1034 			errno = ENOMEM;
1035 			return -1;
1036 		}
1037 
1038 		lh->sendfdcnt = fd_slots_used;
1039 
1040 		msglen = lh->sendlen + sizeof(struct launch_msg_header); /* type promotion to make the host2wire() macro work right */
1041 		lmh.len = host2wire(msglen);
1042 		lmh.magic = host2wire(LAUNCH_MSG_HEADER_MAGIC);
1043 
1044 		iov[0].iov_base = &lmh;
1045 		iov[0].iov_len = sizeof(lmh);
1046 		mh.msg_iov = iov;
1047 		mh.msg_iovlen = 2;
1048 	} else {
1049 		mh.msg_iov = iov + 1;
1050 		mh.msg_iovlen = 1;
1051 	}
1052 
1053 	iov[1].iov_base = lh->sendbuf;
1054 	iov[1].iov_len = lh->sendlen;
1055 
1056 
1057 	if (lh->sendfdcnt > 0) {
1058 		sentctrllen = mh.msg_controllen = CMSG_SPACE(lh->sendfdcnt * sizeof(int));
1059 		cm = alloca(mh.msg_controllen);
1060 		mh.msg_control = cm;
1061 
1062 		memset(cm, 0, mh.msg_controllen);
1063 
1064 
1065 		cm->cmsg_len = CMSG_LEN(lh->sendfdcnt * sizeof(int));
1066 		cm->cmsg_level = SOL_SOCKET;
1067 		cm->cmsg_type = SCM_RIGHTS;
1068 
1069 		memcpy(CMSG_DATA(cm), lh->sendfds, lh->sendfdcnt * sizeof(int));
1070 	}
1071 
1072 	if ((r = sendmsg(fd2use, &mh, 0)) == -1) {
1073 		return -1;
1074 	} else if (r == 0) {
1075 		errno = ECONNRESET;
1076 		return -1;
1077 	} else if (sentctrllen != mh.msg_controllen) {
1078 		errno = ECONNRESET;
1079 		return -1;
1080 	}
1081 
1082 	if (d) {
1083 		r -= sizeof(struct launch_msg_header);
1084 	}
1085 
1086 	lh->sendlen -= r;
1087 	if (lh->sendlen > 0) {
1088 		memmove(lh->sendbuf, lh->sendbuf + r, lh->sendlen);
1089 	} else {
1090 		free(lh->sendbuf);
1091 		lh->sendbuf = malloc(0);
1092 	}
1093 
1094 	lh->sendfdcnt = 0;
1095 	free(lh->sendfds);
1096 	lh->sendfds = malloc(0);
1097 
1098 	if (lh->sendlen > 0) {
1099 		errno = EAGAIN;
1100 		return -1;
1101 	}
1102 
1103 	return 0;
1104 }
1105 
1106 int
launch_get_fd(void)1107 launch_get_fd(void)
1108 {
1109 	launch_globals_t globals = _launch_globals();
1110 	pthread_once(&globals->lc_once, launch_client_init);
1111 
1112 	if (!globals->l) {
1113 		errno = ENOTCONN;
1114 		return -1;
1115 	}
1116 
1117 	return globals->l->fd;
1118 }
1119 
1120 #if 0
1121 void
1122 launch_msg_getmsgs(launch_data_t m, void *context)
1123 {
1124 	launch_data_t async_resp, *sync_resp = context;
1125 
1126 	launch_globals_t globals = _launch_globals();
1127 
1128 	if ((LAUNCH_DATA_DICTIONARY == launch_data_get_type(m)) && (async_resp = launch_data_dict_lookup(m, LAUNCHD_ASYNC_MSG_KEY))) {
1129 		launch_data_array_set_index(globals->async_resp, launch_data_copy(async_resp), launch_data_array_get_count(globals->async_resp));
1130 	} else {
1131 		*sync_resp = launch_data_copy(m);
1132 	}
1133 }
1134 #endif
1135 
1136 void
launch_mach_checkin_service(launch_data_t obj,const char * key,void * context)1137 launch_mach_checkin_service(launch_data_t obj, const char *key, void *context __attribute__((unused)))
1138 {
1139 	kern_return_t result;
1140 	mach_port_t p;
1141 	name_t srvnm;
1142 
1143 	strlcpy(srvnm, key, sizeof(srvnm));
1144 
1145 	result = bootstrap_check_in(bootstrap_port, srvnm, &p);
1146 
1147 	if (result == BOOTSTRAP_SUCCESS)
1148 		launch_data_set_machport(obj, p);
1149 }
1150 
1151 launch_data_t
launch_msg(launch_data_t d)1152 launch_msg(launch_data_t d)
1153 {
1154 	launch_data_t mps, r = launch_msg_internal(d);
1155 
1156 	if (launch_data_get_type(d) == LAUNCH_DATA_STRING) {
1157 		if (strcmp(launch_data_get_string(d), LAUNCH_KEY_CHECKIN) != 0)
1158 			return r;
1159 		if (r == NULL)
1160 			return r;
1161 		if (launch_data_get_type(r) != LAUNCH_DATA_DICTIONARY)
1162 			return r;
1163 		mps = launch_data_dict_lookup(r, LAUNCH_JOBKEY_MACHSERVICES);
1164 		if (mps == NULL)
1165 			return r;
1166 		launch_data_dict_iterate(mps, launch_mach_checkin_service, NULL);
1167 	}
1168 
1169 	return r;
1170 }
1171 
1172 extern kern_return_t vproc_mig_set_security_session(mach_port_t, uuid_t, mach_port_t);
1173 
1174 #if 0
1175 static inline bool
1176 uuid_data_is_null(launch_data_t d)
1177 {
1178 	bool result = false;
1179 	if (launch_data_get_type(d) == LAUNCH_DATA_OPAQUE && launch_data_get_opaque_size(d) == sizeof(uuid_t)) {
1180 		uuid_t existing_uuid;
1181 		memcpy(existing_uuid, launch_data_get_opaque(d), sizeof(uuid_t));
1182 
1183 		/* A NULL UUID tells us to keep the session inherited from the parent. */
1184 		result = (bool)uuid_is_null(existing_uuid);
1185 	}
1186 
1187 	return result;
1188 }
1189 #endif
1190 
1191 #if 0
1192 launch_data_t
1193 launch_msg_internal(launch_data_t d)
1194 {
1195 	launch_data_t resp = NULL;
1196 
1197 #ifdef notyet
1198 	if (d && (launch_data_get_type(d) == LAUNCH_DATA_STRING)
1199 			&& (strcmp(launch_data_get_string(d), LAUNCH_KEY_GETJOBS) == 0)
1200 			&& vproc_swap_complex(NULL, VPROC_GSK_ALLJOBS, NULL, &resp) == NULL) {
1201 		return resp;
1202 	}
1203 #endif
1204 
1205 	launch_globals_t globals = _launch_globals();
1206 	pthread_once(&globals->lc_once, launch_client_init);
1207 	if (!globals->l) {
1208 		errno = ENOTCONN;
1209 		return NULL;
1210 	}
1211 
1212 	int fd2use = -1;
1213 	if ((launch_data_get_type(d) == LAUNCH_DATA_STRING && strcmp(launch_data_get_string(d), LAUNCH_KEY_CHECKIN) == 0) || globals->s_am_embedded_god) {
1214 		globals->l->which = LAUNCHD_USE_CHECKIN_FD;
1215 	} else {
1216 		globals->l->which = LAUNCHD_USE_OTHER_FD;
1217 	}
1218 
1219 	fd2use = launchd_getfd(globals->l);
1220 
1221 	if (fd2use == -1) {
1222 		errno = EPERM;
1223 		return NULL;
1224 	}
1225 
1226 #if !TARGET_OS_EMBEDDED
1227 	uuid_t uuid;
1228 	launch_data_t uuid_d = NULL;
1229 	size_t jobs_that_need_sessions = 0;
1230 	if (d && launch_data_get_type(d) == LAUNCH_DATA_DICTIONARY) {
1231 		launch_data_t v = launch_data_dict_lookup(d, LAUNCH_KEY_SUBMITJOB);
1232 
1233 		if (v && launch_data_get_type(v) == LAUNCH_DATA_ARRAY) {
1234 			size_t cnt = launch_data_array_get_count(v);
1235 			size_t i = 0;
1236 
1237 			uuid_generate(uuid);
1238 			for (i = 0; i < cnt; i++) {
1239 				launch_data_t ji = launch_data_array_get_index(v, i);
1240 				if (launch_data_get_type(ji) == LAUNCH_DATA_DICTIONARY) {
1241 					launch_data_t existing_v = launch_data_dict_lookup(ji, LAUNCH_JOBKEY_SECURITYSESSIONUUID);
1242 					if (!existing_v) {
1243 						/* I really wish these were reference-counted. Sigh... */
1244 						uuid_d = launch_data_new_opaque(uuid, sizeof(uuid));
1245 						launch_data_dict_insert(ji, uuid_d, LAUNCH_JOBKEY_SECURITYSESSIONUUID);
1246 						jobs_that_need_sessions++;
1247 					} else if (launch_data_get_type(existing_v) == LAUNCH_DATA_OPAQUE) {
1248 						jobs_that_need_sessions += uuid_data_is_null(existing_v) ? 0 : 1;
1249 					}
1250 				}
1251 			}
1252 		} else if (v && launch_data_get_type(v) == LAUNCH_DATA_DICTIONARY) {
1253 			launch_data_t existing_v = launch_data_dict_lookup(v, LAUNCH_JOBKEY_SECURITYSESSIONUUID);
1254 			if (!existing_v) {
1255 				uuid_generate(uuid);
1256 				uuid_d = launch_data_new_opaque(uuid, sizeof(uuid));
1257 				launch_data_dict_insert(v, uuid_d, LAUNCH_JOBKEY_SECURITYSESSIONUUID);
1258 				jobs_that_need_sessions++;
1259 			} else {
1260 				jobs_that_need_sessions += uuid_data_is_null(existing_v) ? 0 : 1;
1261 			}
1262 		}
1263 	}
1264 #endif
1265 
1266 	pthread_mutex_lock(&globals->lc_mtx);
1267 
1268 	if (d && launchd_msg_send(globals->l, d) == -1) {
1269 		do {
1270 			if (errno != EAGAIN)
1271 				goto out;
1272 		} while (launchd_msg_send(globals->l, NULL) == -1);
1273 	}
1274 
1275 	while (resp == NULL) {
1276 		if (d == NULL && launch_data_array_get_count(globals->async_resp) > 0) {
1277 			resp = launch_data_array_pop_first(globals->async_resp);
1278 			goto out;
1279 		}
1280 		if (launchd_msg_recv(globals->l, launch_msg_getmsgs, &resp) == -1) {
1281 			if (errno != EAGAIN) {
1282 				goto out;
1283 			} else if (d == NULL) {
1284 				errno = 0;
1285 				goto out;
1286 			} else {
1287 				fd_set rfds;
1288 
1289 				FD_ZERO(&rfds);
1290 				FD_SET(fd2use, &rfds);
1291 
1292 				select(fd2use + 1, &rfds, NULL, NULL, NULL);
1293 			}
1294 		}
1295 	}
1296 
1297 out:
1298 #if !TARGET_OS_EMBEDDED
1299 	if (!uuid_is_null(uuid) && resp && jobs_that_need_sessions > 0) {
1300 		mach_port_t session_port = _audit_session_self();
1301 		launch_data_type_t resp_type = launch_data_get_type(resp);
1302 
1303 		bool set_session = false;
1304 		if (resp_type == LAUNCH_DATA_ERRNO) {
1305 			set_session = (launch_data_get_errno(resp) == ENEEDAUTH);
1306 		} else if (resp_type == LAUNCH_DATA_ARRAY) {
1307 			set_session = true;
1308 		}
1309 
1310 		kern_return_t kr = KERN_FAILURE;
1311 		if (set_session) {
1312 			kr = vproc_mig_set_security_session(bootstrap_port, uuid, session_port);
1313 		}
1314 
1315 		if (kr == KERN_SUCCESS) {
1316 			if (resp_type == LAUNCH_DATA_ERRNO) {
1317 				launch_data_set_errno(resp, 0);
1318 			} else {
1319 				size_t i = 0;
1320 				for (i = 0; i < launch_data_array_get_count(resp); i++) {
1321 					launch_data_t ri = launch_data_array_get_index(resp, i);
1322 
1323 					int recvd_err = 0;
1324 					if (launch_data_get_type(ri) == LAUNCH_DATA_ERRNO && (recvd_err = launch_data_get_errno(ri))) {
1325 						launch_data_set_errno(ri, recvd_err == ENEEDAUTH ? 0 : recvd_err);
1326 					}
1327 				}
1328 			}
1329 		}
1330 
1331 		mach_port_deallocate(mach_task_self(), session_port);
1332 	}
1333 #endif
1334 
1335 	pthread_mutex_unlock(&globals->lc_mtx);
1336 
1337 	return resp;
1338 }
1339 #endif
1340 
1341 int
launchd_msg_recv(launch_t lh,void (* cb)(launch_data_t,void *),void * context)1342 launchd_msg_recv(launch_t lh, void (*cb)(launch_data_t, void *), void *context)
1343 {
1344 	struct cmsghdr *cm = alloca(4096);
1345 	launch_data_t rmsg = NULL;
1346 	size_t data_offset, fd_offset;
1347 	struct msghdr mh;
1348 	struct iovec iov;
1349 	int r;
1350 
1351 	int fd2use = launchd_getfd(lh);
1352 	if (fd2use == -1) {
1353 		errno = EPERM;
1354 		return -1;
1355 	}
1356 
1357 	memset(&mh, 0, sizeof(mh));
1358 	mh.msg_iov = &iov;
1359 	mh.msg_iovlen = 1;
1360 
1361 	lh->recvbuf = reallocf(lh->recvbuf, lh->recvlen + 8*1024);
1362 
1363 	iov.iov_base = lh->recvbuf + lh->recvlen;
1364 	iov.iov_len = 8*1024;
1365 	mh.msg_control = cm;
1366 	mh.msg_controllen = 4096;
1367 
1368 	if ((r = recvmsg(fd2use, &mh, 0)) == -1)
1369 		return -1;
1370 	if (r == 0) {
1371 		errno = ECONNRESET;
1372 		return -1;
1373 	}
1374 	if (mh.msg_flags & MSG_CTRUNC) {
1375 		errno = ECONNABORTED;
1376 		return -1;
1377 	}
1378 	lh->recvlen += r;
1379 	if (mh.msg_controllen > 0) {
1380 		lh->recvfds = reallocf(lh->recvfds, lh->recvfdcnt * sizeof(int) + mh.msg_controllen - sizeof(struct cmsghdr));
1381 		memcpy(lh->recvfds + lh->recvfdcnt, CMSG_DATA(cm), mh.msg_controllen - sizeof(struct cmsghdr));
1382 		lh->recvfdcnt += (mh.msg_controllen - sizeof(struct cmsghdr)) / sizeof(int);
1383 	}
1384 
1385 	r = 0;
1386 
1387 	while (lh->recvlen > 0) {
1388 		struct launch_msg_header *lmhp = (void *)lh->recvbuf;
1389 		uint64_t tmplen;
1390 		data_offset = sizeof(struct launch_msg_header);
1391 		fd_offset = 0;
1392 
1393 		if (lh->recvlen < sizeof(struct launch_msg_header))
1394 			goto need_more_data;
1395 
1396 		tmplen = big2wire(lmhp->len);
1397 
1398 		if (big2wire(lmhp->magic) != LAUNCH_MSG_HEADER_MAGIC || tmplen <= sizeof(struct launch_msg_header)) {
1399 			errno = EBADRPC;
1400 			goto out_bad;
1401 		}
1402 
1403 		if (lh->recvlen < tmplen) {
1404 			goto need_more_data;
1405 		}
1406 
1407 		if ((rmsg = launch_data_unpack(lh->recvbuf, lh->recvlen, lh->recvfds, lh->recvfdcnt, &data_offset, &fd_offset)) == NULL) {
1408 			errno = EBADRPC;
1409 			goto out_bad;
1410 		}
1411 
1412 		launch_globals_t globals = _launch_globals();
1413 
1414 		globals->in_flight_msg_recv_client = lh;
1415 
1416 		cb(rmsg, context);
1417 
1418 		/* launchd and only launchd can call launchd_close() as a part of the callback */
1419 		if (globals->in_flight_msg_recv_client == NULL) {
1420 			r = 0;
1421 			break;
1422 		}
1423 
1424 		lh->recvlen -= data_offset;
1425 		if (lh->recvlen > 0) {
1426 			memmove(lh->recvbuf, lh->recvbuf + data_offset, lh->recvlen);
1427 		} else {
1428 			free(lh->recvbuf);
1429 			lh->recvbuf = malloc(0);
1430 		}
1431 
1432 		lh->recvfdcnt -= fd_offset;
1433 		if (lh->recvfdcnt > 0) {
1434 			memmove(lh->recvfds, lh->recvfds + fd_offset, lh->recvfdcnt * sizeof(int));
1435 		} else {
1436 			free(lh->recvfds);
1437 			lh->recvfds = malloc(0);
1438 		}
1439 	}
1440 
1441 	return r;
1442 
1443 need_more_data:
1444 	errno = EAGAIN;
1445 out_bad:
1446 	return -1;
1447 }
1448 
1449 launch_data_t
launch_data_copy(launch_data_t o)1450 launch_data_copy(launch_data_t o)
1451 {
1452 	launch_data_t r = launch_data_alloc(o->type);
1453 	size_t i;
1454 
1455 	free(r->_array);
1456 	memcpy(r, o, sizeof(struct _launch_data));
1457 
1458 	switch (o->type) {
1459 	case LAUNCH_DATA_DICTIONARY:
1460 	case LAUNCH_DATA_ARRAY:
1461 		r->_array = calloc(1, o->_array_cnt * sizeof(launch_data_t));
1462 		for (i = 0; i < o->_array_cnt; i++) {
1463 			if (o->_array[i])
1464 				r->_array[i] = launch_data_copy(o->_array[i]);
1465 		}
1466 		break;
1467 	case LAUNCH_DATA_STRING:
1468 		r->string = strdup(o->string);
1469 		break;
1470 	case LAUNCH_DATA_OPAQUE:
1471 		r->opaque = malloc(o->opaque_size);
1472 		memcpy(r->opaque, o->opaque, o->opaque_size);
1473 		break;
1474 	default:
1475 		break;
1476 	}
1477 
1478 	return r;
1479 }
1480 
1481 int
_fd(int fd)1482 _fd(int fd)
1483 {
1484 	if (fd >= 0)
1485 		if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
1486 			syslog(LOG_ERR | LOG_CONS, "fcntl failed for fd %d: %m", fd);
1487 		}
1488 	return fd;
1489 }
1490 
1491 launch_data_t
launch_data_new_errno(int e)1492 launch_data_new_errno(int e)
1493 {
1494 	launch_data_t r = launch_data_alloc(LAUNCH_DATA_ERRNO);
1495 
1496 	if (r)
1497 		launch_data_set_errno(r, e);
1498 
1499 	return r;
1500 }
1501 
1502 launch_data_t
launch_data_new_fd(int fd)1503 launch_data_new_fd(int fd)
1504 {
1505 	launch_data_t r = launch_data_alloc(LAUNCH_DATA_FD);
1506 
1507 	if (r)
1508 		launch_data_set_fd(r, fd);
1509 
1510 	return r;
1511 }
1512 
1513 launch_data_t
launch_data_new_machport(mach_port_t p)1514 launch_data_new_machport(mach_port_t p)
1515 {
1516 	launch_data_t r = launch_data_alloc(LAUNCH_DATA_MACHPORT);
1517 
1518 	if (r)
1519 		launch_data_set_machport(r, p);
1520 
1521 	return r;
1522 }
1523 
1524 launch_data_t
launch_data_new_integer(long long n)1525 launch_data_new_integer(long long n)
1526 {
1527 	launch_data_t r = launch_data_alloc(LAUNCH_DATA_INTEGER);
1528 
1529 	if (r)
1530 		launch_data_set_integer(r, n);
1531 
1532 	return r;
1533 }
1534 
1535 launch_data_t
launch_data_new_bool(bool b)1536 launch_data_new_bool(bool b)
1537 {
1538 	launch_data_t r = launch_data_alloc(LAUNCH_DATA_BOOL);
1539 
1540 	if (r)
1541 		launch_data_set_bool(r, b);
1542 
1543 	return r;
1544 }
1545 
1546 launch_data_t
launch_data_new_real(double d)1547 launch_data_new_real(double d)
1548 {
1549 	launch_data_t r = launch_data_alloc(LAUNCH_DATA_REAL);
1550 
1551 	if (r)
1552 		launch_data_set_real(r, d);
1553 
1554 	return r;
1555 }
1556 
1557 launch_data_t
launch_data_new_string(const char * s)1558 launch_data_new_string(const char *s)
1559 {
1560 	launch_data_t r = launch_data_alloc(LAUNCH_DATA_STRING);
1561 
1562 	if (r == NULL)
1563 		return NULL;
1564 
1565 	if (!launch_data_set_string(r, s)) {
1566 		launch_data_free(r);
1567 		return NULL;
1568 	}
1569 
1570 	return r;
1571 }
1572 
1573 launch_data_t
launch_data_new_opaque(const void * o,size_t os)1574 launch_data_new_opaque(const void *o, size_t os)
1575 {
1576 	launch_data_t r = launch_data_alloc(LAUNCH_DATA_OPAQUE);
1577 
1578 	if (r == NULL)
1579 		return NULL;
1580 
1581 	if (!launch_data_set_opaque(r, o, os)) {
1582 		launch_data_free(r);
1583 		return NULL;
1584 	}
1585 
1586 	return r;
1587 }
1588 
1589 void
load_launchd_jobs_at_loginwindow_prompt(int flags,...)1590 load_launchd_jobs_at_loginwindow_prompt(int flags __attribute__((unused)), ...)
1591 {
1592 	_vprocmgr_init(VPROCMGR_SESSION_LOGINWINDOW);
1593 }
1594 
1595 pid_t
create_and_switch_to_per_session_launchd(const char * login,int flags,...)1596 create_and_switch_to_per_session_launchd(const char *login __attribute__((unused)), int flags, ...)
1597 {
1598 	uid_t target_user = geteuid() ? geteuid() : getuid();
1599 	if (_vprocmgr_move_subset_to_user(target_user, VPROCMGR_SESSION_AQUA, flags)) {
1600 		return -1;
1601 	}
1602 
1603 	return 1;
1604 }
1605