xref: /trueos/lib/libxpc/xpc_misc.c (revision 40782981b22ef4e5389f32da5c91672c6e5e7429)
1 /*
2  * Copyright 2014-2015 iXsystems, Inc.
3  * All rights reserved
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted providing that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #include <sys/types.h>
29 #include <sys/errno.h>
30 #include <sys/sbuf.h>
31 #include <mach/mach.h>
32 #include <xpc/launchd.h>
33 #include <machine/atomic.h>
34 #include <assert.h>
35 #include <syslog.h>
36 #include <stdarg.h>
37 #include <uuid.h>
38 
39 #include "xpc_internal.h"
40 
41 #define MAX_RECV 8192
42 #define XPC_RECV_SIZE			\
43     MAX_RECV - 				\
44     sizeof(mach_msg_header_t) - 	\
45     sizeof(mach_msg_trailer_t) - 	\
46     sizeof(uint64_t) - 			\
47     sizeof(size_t)
48 
49 struct xpc_message {
50 	mach_msg_header_t header;
51 	size_t size;
52 	uint64_t id;
53 	char data[0];
54 	mach_msg_trailer_t trailer;
55 };
56 
57 struct xpc_recv_message {
58 	mach_msg_header_t header;
59 	size_t size;
60 	uint64_t id;
61 	char data[XPC_RECV_SIZE];
62 	mach_msg_trailer_t trailer;
63 };
64 
65 static void xpc_copy_description_level(xpc_object_t obj, struct sbuf *sbuf,
66     int level);
67 
68 void
fail_log(const char * exp)69 fail_log(const char *exp)
70 {
71 	syslog(LOG_ERR, "%s", exp);
72 	//sleep(1);
73 	printf("%s", exp);
74 	//abort();
75 }
76 
77 static void nvlist_add_prim(nvlist_t *nv, const char *key, xpc_object_t xobj);
78 
79 static void
xpc_dictionary_destroy(struct xpc_object * dict)80 xpc_dictionary_destroy(struct xpc_object *dict)
81 {
82 	struct xpc_dict_head *head;
83 	struct xpc_dict_pair *p, *ptmp;
84 
85 	head = &dict->xo_dict;
86 
87 	TAILQ_FOREACH_SAFE(p, head, xo_link, ptmp) {
88 		TAILQ_REMOVE(head, p, xo_link);
89 		xpc_object_destroy(p->value);
90 		free(p);
91 	}
92 }
93 
94 static void
xpc_array_destroy(struct xpc_object * dict)95 xpc_array_destroy(struct xpc_object *dict)
96 {
97 	struct xpc_object *p, *ptmp;
98 	struct xpc_array_head *head;
99 
100 	head = &dict->xo_array;
101 
102 	TAILQ_FOREACH_SAFE(p, head, xo_link, ptmp) {
103 		TAILQ_REMOVE(head, p, xo_link);
104 		xpc_object_destroy(p);
105 	}
106 }
107 
108 static int
xpc_pack(struct xpc_object * xo,void * buf,size_t * size)109 xpc_pack(struct xpc_object *xo, void *buf, size_t *size)
110 {
111 	nvlist_t *nv;
112 	void *packed;
113 
114 	nv = xpc2nv(xo);
115 
116 	packed = nvlist_pack_buffer(nv, NULL, size);
117 	if (packed == NULL) {
118 		errno = EINVAL;
119 		return (-1);
120 	}
121 
122 	memcpy(buf, packed, *size);
123 	return (0);
124 }
125 
126 static struct xpc_object *
xpc_unpack(void * buf,size_t size)127 xpc_unpack(void *buf, size_t size)
128 {
129 	struct xpc_object *xo;
130 	nvlist_t *nv;
131 
132 	nv = nvlist_unpack(buf, size);
133 	xo = nv2xpc(nv);
134 	return (xo);
135 }
136 
137 void
xpc_object_destroy(struct xpc_object * xo)138 xpc_object_destroy(struct xpc_object *xo)
139 {
140 	if (xo->xo_xpc_type == _XPC_TYPE_DICTIONARY)
141 		xpc_dictionary_destroy(xo);
142 
143 	if (xo->xo_xpc_type == _XPC_TYPE_ARRAY)
144 		xpc_array_destroy(xo);
145 
146 	free(xo);
147 }
148 
149 xpc_object_t
xpc_retain(xpc_object_t obj)150 xpc_retain(xpc_object_t obj)
151 {
152 	struct xpc_object *xo;
153 
154 	xo = obj;
155 	atomic_add_int(&xo->xo_refcnt, 1);
156 	return (obj);
157 }
158 
159 void
xpc_release(xpc_object_t obj)160 xpc_release(xpc_object_t obj)
161 {
162 	struct xpc_object *xo;
163 
164 	xo = obj;
165 	if (atomic_fetchadd_int(&xo->xo_refcnt, -1) > 1)
166 		return;
167 
168 	xpc_object_destroy(xo);
169 }
170 
171 static const char *xpc_errors[] = {
172 	"No Error Found",
173 	"No Memory",
174 	"Invalid Argument",
175 	"No Such Process"
176 };
177 
178 
179 const char *
xpc_strerror(int error)180 xpc_strerror(int error)
181 {
182 
183 	if (error > EXMAX || error < 0)
184 		return "BAD ERROR";
185 	return (xpc_errors[error]);
186 }
187 
188 char *
xpc_copy_description(xpc_object_t obj)189 xpc_copy_description(xpc_object_t obj)
190 {
191 	char *result;
192 	struct sbuf *sbuf;
193 
194 	sbuf = sbuf_new_auto();
195 	xpc_copy_description_level(obj, sbuf, 0);
196 	sbuf_finish(sbuf);
197 	result = strdup(sbuf_data(sbuf));
198 	sbuf_delete(sbuf);
199 
200 	return (result);
201 }
202 
203 static void
xpc_copy_description_level(xpc_object_t obj,struct sbuf * sbuf,int level)204 xpc_copy_description_level(xpc_object_t obj, struct sbuf *sbuf, int level)
205 {
206 	struct xpc_object *xo = obj;
207 	struct uuid *id;
208 	char *uuid_str;
209 	uint32_t uuid_status;
210 
211 	if (obj == NULL) {
212 		sbuf_printf(sbuf, "<null value>\n");
213 		return;
214 	}
215 
216 	sbuf_printf(sbuf, "(%s) ", _xpc_get_type_name(obj));
217 
218 	switch (xo->xo_xpc_type) {
219 	case _XPC_TYPE_DICTIONARY:
220 		sbuf_printf(sbuf, "\n");
221 		xpc_dictionary_apply(xo, ^(const char *k, xpc_object_t v) {
222 			sbuf_printf(sbuf, "%*s\"%s\": ", level * 4, " ", k);
223 			xpc_copy_description_level(v, sbuf, level + 1);
224 			return ((bool)true);
225 		});
226 		break;
227 
228 	case _XPC_TYPE_ARRAY:
229 		sbuf_printf(sbuf, "\n");
230 		xpc_array_apply(xo, ^(size_t idx, xpc_object_t v) {
231 			sbuf_printf(sbuf, "%*s%ld: ", level * 4, " ", idx);
232 			xpc_copy_description_level(v, sbuf, level + 1);
233 			return ((bool)true);
234 		});
235 		break;
236 
237 	case _XPC_TYPE_BOOL:
238 		sbuf_printf(sbuf, "%s\n",
239 		    xpc_bool_get_value(obj) ? "true" : "false");
240 		break;
241 
242 	case _XPC_TYPE_STRING:
243 		sbuf_printf(sbuf, "\"%s\"\n",
244 		    xpc_string_get_string_ptr(obj));
245 		break;
246 
247 	case _XPC_TYPE_INT64:
248 		sbuf_printf(sbuf, "%lld\n",
249 		    xpc_int64_get_value(obj));
250 		break;
251 
252 	case _XPC_TYPE_UINT64:
253 		sbuf_printf(sbuf, "0x%llx\n",
254 		    xpc_uint64_get_value(obj));
255 		break;
256 
257 	case _XPC_TYPE_DATE:
258 		sbuf_printf(sbuf, "%llu\n",
259 		    xpc_date_get_value(obj));
260 		break;
261 
262 	case _XPC_TYPE_UUID:
263 		id = (struct uuid *)xpc_uuid_get_bytes(obj);
264 		uuid_to_string(id, &uuid_str, &uuid_status);
265 		sbuf_printf(sbuf, "%s\n", uuid_str);
266 		free(uuid_str);
267 		break;
268 
269 	case _XPC_TYPE_ENDPOINT:
270 		sbuf_printf(sbuf, "<%d>\n", xo->xo_int);
271 		break;
272 
273 	case _XPC_TYPE_NULL:
274 		sbuf_printf(sbuf, "<null>\n");
275 		break;
276 	}
277 }
278 
279 struct _launch_data {
280 	uint64_t type;
281 	union {
282 		struct {
283 			union {
284 				launch_data_t *_array;
285 				char *string;
286 				void *opaque;
287 				int64_t __junk;
288 			};
289 			union {
290 				uint64_t _array_cnt;
291 				uint64_t string_len;
292 				uint64_t opaque_size;
293 			};
294 		};
295 		int64_t fd;
296 		uint64_t  mp;
297 		uint64_t err;
298 		int64_t number;
299 		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 */
300 		double float_num;
301 	};
302 };
303 
304 static uint8_t ld_to_xpc_type[] = {
305 	_XPC_TYPE_INVALID,
306 	_XPC_TYPE_DICTIONARY,
307 	_XPC_TYPE_ARRAY,
308 	_XPC_TYPE_FD,
309 	_XPC_TYPE_UINT64,
310 	_XPC_TYPE_DOUBLE,
311 	_XPC_TYPE_BOOL,
312 	_XPC_TYPE_STRING,
313 	_XPC_TYPE_DATA,
314 	_XPC_TYPE_ERROR,
315 	_XPC_TYPE_ENDPOINT
316 };
317 
318 xpc_object_t
ld2xpc(launch_data_t ld)319 ld2xpc(launch_data_t ld)
320 {
321 	struct xpc_object *xo;
322 	xpc_u val;
323 
324 
325 	if (ld->type > LAUNCH_DATA_MACHPORT)
326 		return (NULL);
327 	if (ld->type == LAUNCH_DATA_STRING || ld->type == LAUNCH_DATA_OPAQUE) {
328 		val.str = malloc(ld->string_len);
329 		memcpy(__DECONST(void *, val.str), ld->string, ld->string_len);
330 		xo = _xpc_prim_create(ld_to_xpc_type[ld->type], val, ld->string_len);
331 	} else if (ld->type == LAUNCH_DATA_BOOL) {
332 		val.b = (bool)ld->boolean;
333 		xo = _xpc_prim_create(ld_to_xpc_type[ld->type], val, 0);
334 	} else if (ld->type == LAUNCH_DATA_ARRAY) {
335 		xo = xpc_array_create(NULL, 0);
336 		for (uint64_t i = 0; i < ld->_array_cnt; i++)
337 			xpc_array_append_value(xo, ld2xpc(ld->_array[i]));
338 	} else {
339 		val.ui = ld->mp;
340 		xo = _xpc_prim_create(ld_to_xpc_type[ld->type], val, ld->string_len);
341 	}
342 	return (xo);
343 }
344 
345 xpc_object_t
xpc_copy_entitlement_for_token(const char * key __unused,audit_token_t * token __unused)346 xpc_copy_entitlement_for_token(const char *key __unused, audit_token_t *token __unused)
347 {
348 	xpc_u val;
349 
350 	val.b = true;
351 	return (_xpc_prim_create(_XPC_TYPE_BOOL, val,0));
352 }
353 
354 
355 #define XPC_RPORT "XPC remote port"
356 int
xpc_pipe_routine_reply(xpc_object_t xobj)357 xpc_pipe_routine_reply(xpc_object_t xobj)
358 {
359 	struct xpc_object *xo;
360 	size_t size, msg_size;
361 	struct xpc_message *message;
362 	kern_return_t kr;
363 	int err;
364 
365 	xo = xobj;
366 	assert(xo->xo_xpc_type == _XPC_TYPE_DICTIONARY);
367 //	size = nvlist_size(xo->xo_nv);
368 	msg_size = size + sizeof(mach_msg_header_t) + sizeof(size_t);
369 	if ((message = malloc(msg_size)) == NULL)
370 		return (ENOMEM);
371 	//if (xpc2nv(xobj, &message->data, &size) == NULL)
372 	//	return (EINVAL);
373 
374 	message->header.msgh_size = msg_size;
375 	message->header.msgh_remote_port = xpc_dictionary_copy_mach_send(xobj, XPC_RPORT);
376 	message->header.msgh_local_port = MACH_PORT_NULL;
377 	message->size = size;
378 	kr = mach_msg_send(&message->header);
379 	if (kr != KERN_SUCCESS)
380 		err = (kr == KERN_INVALID_TASK) ? EPIPE : EINVAL;
381 	else
382 		err = 0;
383 	free(message);
384 	return (err);
385 }
386 
387 int
xpc_pipe_send(xpc_object_t xobj,mach_port_t dst,mach_port_t local,uint64_t id)388 xpc_pipe_send(xpc_object_t xobj, mach_port_t dst, mach_port_t local,
389     uint64_t id)
390 {
391 	struct xpc_object *xo;
392 	size_t size, msg_size;
393 	struct xpc_message *message;
394 	kern_return_t kr;
395 	int err;
396 
397 	xo = xobj;
398 	assert(xo->xo_xpc_type == _XPC_TYPE_DICTIONARY);
399 
400 	if ((message = malloc(msg_size)) == NULL)
401 		return (ENOMEM);
402 
403 	if (xpc_pack(xo, &message->data, &size) != 0)
404 		return (EINVAL);
405 
406 	msg_size = _ALIGN(size + sizeof(mach_msg_header_t) + sizeof(size_t) + sizeof(uint64_t));
407 	message->header.msgh_size = msg_size;
408 	message->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
409 	    MACH_MSG_TYPE_MAKE_SEND);
410 	message->header.msgh_remote_port = dst;
411 	message->header.msgh_local_port = local;
412 	message->id = id;
413 	message->size = size;
414 	kr = mach_msg_send(&message->header);
415 	if (kr != KERN_SUCCESS)
416 		err = (kr == KERN_INVALID_TASK) ? EPIPE : EINVAL;
417 	else
418 		err = 0;
419 	free(message);
420 	return (err);
421 }
422 
423 #define LOG(...)	\
424 	do {            \
425 	syslog(LOG_ERR, "%s:%u: ", __FILE__, __LINE__);	\
426 	syslog(LOG_ERR, __VA_ARGS__);					\
427 	} while (0)
428 
429 int
xpc_pipe_receive(mach_port_t local,mach_port_t * remote,xpc_object_t * result,uint64_t * id)430 xpc_pipe_receive(mach_port_t local, mach_port_t *remote, xpc_object_t *result,
431     uint64_t *id)
432 {
433 	struct xpc_recv_message message;
434 	mach_msg_header_t *request;
435 	kern_return_t kr;
436 	mig_reply_error_t response;
437 	mach_msg_trailer_t *tr;
438 	int data_size;
439 	struct xpc_object *xo;
440 	audit_token_t *auditp;
441 	xpc_u val;
442 
443 	request = &message.header;
444 	/* should be size - but what about arbitrary XPC data? */
445 	request->msgh_size = MAX_RECV;
446 	request->msgh_local_port = local;
447 	kr = mach_msg(request, MACH_RCV_MSG |
448 	    MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
449 	    MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT),
450 	    0, request->msgh_size, request->msgh_local_port,
451 	    MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
452 
453 	if (kr != 0)
454 		LOG("mach_msg_receive returned %d\n", kr);
455 	*remote = request->msgh_remote_port;
456 	*id = message.id;
457 	data_size = message.size;
458 	LOG("unpacking data_size=%d", data_size);
459 	xo = xpc_unpack(&message.data, data_size);
460 
461 	tr = (mach_msg_trailer_t *)(((char *)&message) + request->msgh_size);
462 	auditp = &((mach_msg_audit_trailer_t *)tr)->msgh_audit;
463 
464 	xo->xo_audit_token = malloc(sizeof(*auditp));
465 	memcpy(xo->xo_audit_token, auditp, sizeof(*auditp));
466 
467 	xpc_dictionary_set_mach_send(xo, XPC_RPORT, request->msgh_remote_port);
468 	xpc_dictionary_set_uint64(xo, XPC_SEQID, message.id);
469 	*result = xo;
470 	return (0);
471 }
472 
473 int
xpc_pipe_try_receive(mach_port_t portset,xpc_object_t * requestobj,mach_port_t * rcvport,boolean_t (* demux)(mach_msg_header_t *,mach_msg_header_t *),mach_msg_size_t msgsize __unused,int flags __unused)474 xpc_pipe_try_receive(mach_port_t portset, xpc_object_t *requestobj, mach_port_t *rcvport,
475 	boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), mach_msg_size_t msgsize __unused,
476 	int flags __unused)
477 {
478 	struct xpc_recv_message message;
479 	mach_msg_header_t *request;
480 	kern_return_t kr;
481 	mig_reply_error_t response;
482 	mach_msg_trailer_t *tr;
483 	int data_size;
484 	struct xpc_object *xo;
485 	audit_token_t *auditp;
486 	xpc_u val;
487 
488 	request = &message.header;
489 	/* should be size - but what about arbitrary XPC data? */
490 	request->msgh_size = MAX_RECV;
491 	request->msgh_local_port = portset;
492 	kr = mach_msg(request, MACH_RCV_MSG |
493 	    MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
494 	    MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT),
495 	    0, request->msgh_size, request->msgh_local_port,
496 	    MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
497 
498 	if (kr != 0)
499 		LOG("mach_msg_receive returned %d\n", kr);
500 	*rcvport = request->msgh_remote_port;
501 	if (demux(request, (mach_msg_header_t *)&response)) {
502 		(void)mach_msg_send((mach_msg_header_t *)&response);
503 		/*  can't do anything with the return code
504 		* just tell the caller this has been handled
505 		*/
506 		return (TRUE);
507 	}
508 	LOG("demux returned false\n");
509 	data_size = request->msgh_size;
510 	LOG("unpacking data_size=%d", data_size);
511 	xo = xpc_unpack(&message.data, data_size);
512 	/* is padding for alignment enforced in the kernel?*/
513 	tr = (mach_msg_trailer_t *)(((char *)&message) + request->msgh_size);
514 	auditp = &((mach_msg_audit_trailer_t *)tr)->msgh_audit;
515 
516 	xo->xo_audit_token = malloc(sizeof(*auditp));
517 	memcpy(xo->xo_audit_token, auditp, sizeof(*auditp));
518 
519 	xpc_dictionary_set_mach_send(xo, XPC_RPORT, request->msgh_remote_port);
520 	xpc_dictionary_set_uint64(xo, XPC_SEQID, message.id);
521 	*requestobj = xo;
522 	return (0);
523 }
524 
525 int
xpc_call_wakeup(mach_port_t rport,int retcode)526 xpc_call_wakeup(mach_port_t rport, int retcode)
527 {
528 	mig_reply_error_t msg;
529 	int err;
530 	kern_return_t kr;
531 
532 	msg.Head.msgh_remote_port = rport;
533 	msg.RetCode = retcode;
534 	kr = mach_msg_send(&msg.Head);
535 	if (kr != KERN_SUCCESS)
536 		err = (kr == KERN_INVALID_TASK) ? EPIPE : EINVAL;
537 	else
538 		err = 0;
539 
540 	return (err);
541 }
542