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