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