1/* 2 * Copyright (c) 2009 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "hi_locl.h" 37 38#if defined(__APPLE__) && defined(HAVE_GCD) 39 40#include "heim_ipc.h" 41#include "heim_ipc_asyncServer.h" 42 43#include <dispatch/dispatch.h> 44 45#include <mach/mach.h> 46#include <servers/bootstrap.h> 47#ifdef __APPLE_PRIVATE__ 48#include <bootstrap_priv.h> 49#endif 50 51static dispatch_once_t jobqinited = 0; 52static dispatch_queue_t jobq = NULL; 53static dispatch_queue_t syncq; 54 55struct mach_ctx { 56 mach_port_t server; 57 char *name; 58}; 59 60static int 61mach_release(void *ctx); 62 63static kern_return_t 64look_up(const char *service, mach_port_t *nport) 65{ 66#ifdef __APPLE_PRIVATE__ 67 return bootstrap_look_up2(bootstrap_port, service, nport, 0, BOOTSTRAP_PRIVILEGED_SERVER); 68#else 69 return bootstrap_look_up(bootstrap_port, service, nport); 70#endif 71} 72 73 74static int 75mach_init(const char *service, void **ctx) 76{ 77 struct mach_ctx *ipc; 78 mach_port_t sport; 79 int ret; 80 81 dispatch_once(&jobqinited, ^{ 82 jobq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 83 syncq = dispatch_queue_create("heim-ipc-syncq", NULL); 84 }); 85 86 ret = look_up(service, &sport); 87 if (ret) 88 return ret; 89 90 ipc = malloc(sizeof(*ipc)); 91 if (ipc == NULL) { 92 mach_port_destroy(mach_task_self(), sport); 93 return ENOMEM; 94 } 95 96 ipc->server = sport; 97 ipc->name = strdup(service); 98 if (ipc->name == NULL) { 99 mach_release(ipc); 100 return ENOMEM; 101 } 102 103 *ctx = ipc; 104 105 return 0; 106} 107 108static int 109mach_ipc(void *ctx, 110 const heim_idata *request, heim_idata *response, 111 heim_icred *cred) 112{ 113 struct mach_ctx *ipc = ctx; 114 heim_ipc_message_inband_t requestin; 115 mach_msg_type_number_t requestin_length = 0; 116 heim_ipc_message_outband_t requestout = NULL; 117 mach_msg_type_number_t requestout_length = 0; 118 heim_ipc_message_inband_t replyin; 119 mach_msg_type_number_t replyin_length; 120 heim_ipc_message_outband_t replyout; 121 mach_msg_type_number_t replyout_length; 122 int ret, errorcode, retries = 0; 123 124 if (request->length < sizeof(requestin)) { 125 memcpy(requestin, request->data, request->length); 126 requestin_length = (mach_msg_type_number_t)request->length; 127 } else { 128 ret = vm_read(mach_task_self(), 129 (vm_address_t)request->data, request->length, 130 (vm_address_t *)&requestout, &requestout_length); 131 if (ret) 132 return ENOMEM; 133 } 134 135 while (retries < 2) { 136 __block mach_port_t sport; 137 138 dispatch_sync(syncq, ^{ sport = ipc->server; }); 139 140 ret = mheim_ipc_call(sport, 141 requestin, requestin_length, 142 requestout, requestout_length, 143 &errorcode, 144 replyin, &replyin_length, 145 &replyout, &replyout_length); 146 if (ret == MACH_SEND_INVALID_DEST) { 147 mach_port_t nport; 148 /* race other threads to get a new port */ 149 ret = look_up(ipc->name, &nport); 150 if (ret) 151 return ret; 152 dispatch_sync(syncq, ^{ 153 /* check if we lost the race to lookup the port */ 154 if (sport != ipc->server) { 155 mach_port_deallocate(mach_task_self(), nport); 156 } else { 157 mach_port_deallocate(mach_task_self(), ipc->server); 158 ipc->server = nport; 159 } 160 }); 161 retries++; 162 } else if (ret) { 163 return ret; 164 } else 165 break; 166 } 167 if (retries >= 2) 168 return EINVAL; 169 170 if (errorcode) { 171 if (replyout_length) 172 vm_deallocate (mach_task_self (), (vm_address_t) replyout, 173 replyout_length); 174 return errorcode; 175 } 176 177 if (replyout_length) { 178 response->data = malloc(replyout_length); 179 if (response->data == NULL) { 180 vm_deallocate (mach_task_self (), (vm_address_t) replyout, 181 replyout_length); 182 return ENOMEM; 183 } 184 memcpy(response->data, replyout, replyout_length); 185 response->length = replyout_length; 186 vm_deallocate (mach_task_self (), (vm_address_t) replyout, 187 replyout_length); 188 } else { 189 response->data = malloc(replyin_length); 190 if (response->data == NULL) 191 return ENOMEM; 192 memcpy(response->data, replyin, replyin_length); 193 response->length = replyin_length; 194 } 195 196 return 0; 197} 198 199struct async_client { 200 mach_port_t mp; 201 dispatch_source_t source; 202 dispatch_queue_t queue; 203 void (*func)(void *, int, heim_idata *, heim_icred); 204 void *userctx; 205}; 206 207kern_return_t 208mheim_ado_acall_reply(mach_port_t server_port, 209 audit_token_t client_creds, 210 int returnvalue, 211 heim_ipc_message_inband_t replyin, 212 mach_msg_type_number_t replyinCnt, 213 heim_ipc_message_outband_t replyout, 214 mach_msg_type_number_t replyoutCnt) 215{ 216 struct async_client *c = dispatch_get_specific(mheim_ado_acall_reply); 217 heim_idata response; 218 219 if (returnvalue) { 220 response.data = NULL; 221 response.length = 0; 222 } else if (replyoutCnt) { 223 response.data = replyout; 224 response.length = replyoutCnt; 225 } else { 226 response.data = replyin; 227 response.length = replyinCnt; 228 } 229 230 (*c->func)(c->userctx, returnvalue, &response, NULL); 231 232 if (replyoutCnt) 233 vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyoutCnt); 234 235 dispatch_source_cancel(c->source); 236 237 return 0; 238 239 240} 241 242 243static int 244mach_async(void *ctx, const heim_idata *request, void *userctx, 245 void (*func)(void *, int, heim_idata *, heim_icred)) 246{ 247 struct mach_ctx *ipc = ctx; 248 heim_ipc_message_inband_t requestin; 249 mach_msg_type_number_t requestin_length = 0; 250 heim_ipc_message_outband_t requestout = NULL; 251 mach_msg_type_number_t requestout_length = 0; 252 int ret, retries = 0; 253 kern_return_t kr; 254 struct async_client *c; 255 256 /* first create the service that will catch the reply from the server */ 257 /* XXX these object should be cached and reused */ 258 259 c = malloc(sizeof(*c)); 260 if (c == NULL) 261 return ENOMEM; 262 263 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &c->mp); 264 if (kr != KERN_SUCCESS) 265 return EINVAL; 266 267 c->queue = dispatch_queue_create("heim-ipc-async-client", NULL); 268 c->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, c->mp, 0, c->queue); 269 dispatch_queue_set_specific(c->queue, mheim_ado_acall_reply, c, NULL); 270 271 dispatch_source_set_event_handler(c->source, ^{ 272 dispatch_mig_server(c->source, 273 sizeof(union __RequestUnion__mheim_ado_mheim_aipc_subsystem), 274 mheim_aipc_server); 275 }); 276 277 dispatch_source_set_cancel_handler(c->source, ^{ 278 mach_port_mod_refs(mach_task_self(), c->mp, 279 MACH_PORT_RIGHT_RECEIVE, -1); 280 dispatch_release(c->queue); 281 dispatch_release(c->source); 282 free(c); 283 }); 284 285 c->func = func; 286 c->userctx = userctx; 287 288 dispatch_resume(c->source); 289 290 /* ok, send the message */ 291 if (request->length < sizeof(requestin)) { 292 memcpy(requestin, request->data, request->length); 293 requestin_length = (mach_msg_type_number_t)request->length; 294 } else { 295 ret = vm_read(mach_task_self(), 296 (vm_address_t)request->data, request->length, 297 (vm_address_t *)&requestout, &requestout_length); 298 if (ret) 299 return ENOMEM; 300 } 301 302 while (retries < 2) { 303 __block mach_port_t sport; 304 305 dispatch_sync(syncq, ^{ sport = ipc->server; }); 306 307 ret = mheim_ipc_call_request(sport, c->mp, 308 requestin, requestin_length, 309 requestout, requestout_length); 310 if (ret == MACH_SEND_INVALID_DEST) { 311 ret = look_up(ipc->name, &sport); 312 if (ret) { 313 dispatch_source_cancel(c->source); 314 return ret; 315 } 316 mach_port_deallocate(mach_task_self(), ipc->server); 317 ipc->server = sport; 318 retries++; 319 } else if (ret) { 320 dispatch_source_cancel(c->source); 321 return ret; 322 } else 323 break; 324 } 325 if (retries >= 2) { 326 dispatch_source_cancel(c->source); 327 return EINVAL; 328 } 329 330 return 0; 331} 332 333static int 334mach_release(void *ctx) 335{ 336 struct mach_ctx *ipc = ctx; 337 if (ipc->server != MACH_PORT_NULL) 338 mach_port_deallocate(mach_task_self(), ipc->server); 339 free(ipc->name); 340 free(ipc); 341 return 0; 342} 343 344#endif 345 346struct path_ctx { 347 char *path; 348 int fd; 349}; 350 351static int common_release(void *); 352 353static int 354connect_unix(struct path_ctx *s) 355{ 356 struct sockaddr_un addr; 357 358 addr.sun_family = AF_UNIX; 359 strlcpy(addr.sun_path, s->path, sizeof(addr.sun_path)); 360 361 s->fd = socket(AF_UNIX, SOCK_STREAM, 0); 362 if (s->fd < 0) 363 return errno; 364 rk_cloexec(s->fd); 365 socket_set_nopipe(s->fd, 1); 366 367 if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { 368 close(s->fd); 369 s->fd = -1; 370 return errno; 371 } 372 373 return 0; 374} 375 376static int 377common_path_init(const char *service, 378 const char *file, 379 void **ctx) 380{ 381 struct path_ctx *s; 382 383 s = malloc(sizeof(*s)); 384 if (s == NULL) 385 return ENOMEM; 386 s->fd = -1; 387 388 asprintf(&s->path, "/var/run/.heim_%s-%s", service, file); 389 390 *ctx = s; 391 392 return 0; 393} 394 395static int 396unix_socket_init(const char *service, 397 void **ctx) 398{ 399 int ret; 400 401 ret = common_path_init(service, "socket", ctx); 402 if (ret) 403 return ret; 404 ret = connect_unix(*ctx); 405 if (ret) 406 common_release(*ctx); 407 408 return ret; 409} 410 411static int 412unix_socket_ipc(void *ctx, 413 const heim_idata *req, heim_idata *rep, 414 heim_icred *cred) 415{ 416 struct path_ctx *s = ctx; 417 uint32_t len = htonl(req->length); 418 uint32_t rv; 419 int retval; 420 421 if (cred) 422 *cred = NULL; 423 424 rep->data = NULL; 425 rep->length = 0; 426 427 if (net_write(s->fd, &len, sizeof(len)) != sizeof(len)) 428 return -1; 429 if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length) 430 return -1; 431 432 if (net_read(s->fd, &len, sizeof(len)) != sizeof(len)) 433 return -1; 434 if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv)) 435 return -1; 436 retval = ntohl(rv); 437 438 rep->length = ntohl(len); 439 if (rep->length > MAX_PACKET_SIZE) { 440 rep->length = 0; 441 return EINVAL; 442 } else { 443 rep->data = malloc(rep->length); 444 if (rep->data == NULL) 445 return -1; 446 if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length) 447 return -1; 448 } 449 450 return retval; 451} 452 453int 454common_release(void *ctx) 455{ 456 struct path_ctx *s = ctx; 457 if (s->fd >= 0) 458 close(s->fd); 459 free(s->path); 460 free(s); 461 return 0; 462} 463 464#ifdef HAVE_DOOR 465 466static int 467door_init(const char *service, 468 void **ctx) 469{ 470 ret = common_path_init(context, service, "door", ctx); 471 if (ret) 472 return ret; 473 ret = connect_door(*ctx); 474 if (ret) 475 common_release(*ctx); 476 return ret; 477} 478 479static int 480door_ipc(void *ctx, 481 const heim_idata *request, heim_idata *response, 482 heim_icred *cred) 483{ 484 door_arg_t arg; 485 int ret; 486 487 arg.data_ptr = request->data; 488 arg.data_size = request->length; 489 arg.desc_ptr = NULL; 490 arg.desc_num = 0; 491 arg.rbuf = NULL; 492 arg.rsize = 0; 493 494 ret = door_call(fd, &arg); 495 close(fd); 496 if (ret != 0) 497 return errno; 498 499 response->data = malloc(arg.rsize); 500 if (response->data == NULL) { 501 munmap(arg.rbuf, arg.rsize); 502 return ENOMEM; 503 } 504 memcpy(response->data, arg.rbuf, arg.rsize); 505 response->length = arg.rsize; 506 munmap(arg.rbuf, arg.rsize); 507 508 return ret; 509} 510 511#endif 512 513struct hipc_ops { 514 const char *prefix; 515 int (*init)(const char *, void **); 516 int (*release)(void *); 517 int (*ipc)(void *,const heim_idata *, heim_idata *, heim_icred *); 518 int (*async)(void *, const heim_idata *, void *, 519 void (*)(void *, int, heim_idata *, heim_icred)); 520}; 521 522struct hipc_ops ipcs[] = { 523#if defined(__APPLE__) && defined(HAVE_GCD) 524 { "MACH", mach_init, mach_release, mach_ipc, mach_async }, 525#endif 526#ifdef HAVE_DOOR 527 { "DOOR", door_init, common_release, door_ipc, NULL } 528#endif 529 { "UNIX", unix_socket_init, common_release, unix_socket_ipc, NULL } 530}; 531 532struct heim_ipc { 533 struct hipc_ops *ops; 534 void *ctx; 535}; 536 537 538int 539heim_ipc_init_context(const char *name, heim_ipc *ctx) 540{ 541 unsigned int i; 542 int ret, any = 0; 543 544 for(i = 0; i < sizeof(ipcs)/sizeof(ipcs[0]); i++) { 545 size_t prefix_len = strlen(ipcs[i].prefix); 546 heim_ipc c; 547 if(strncmp(ipcs[i].prefix, name, prefix_len) == 0 548 && name[prefix_len] == ':') { 549 } else if (strncmp("ANY:", name, 4) == 0) { 550 prefix_len = 3; 551 any = 1; 552 } else 553 continue; 554 555 c = calloc(1, sizeof(*c)); 556 if (c == NULL) 557 return ENOMEM; 558 559 c->ops = &ipcs[i]; 560 561 ret = (c->ops->init)(name + prefix_len + 1, &c->ctx); 562 if (ret) { 563 free(c); 564 if (any) 565 continue; 566 return ret; 567 } 568 569 *ctx = c; 570 return 0; 571 } 572 573 return ENOENT; 574} 575 576void 577heim_ipc_free_context(heim_ipc ctx) 578{ 579 (ctx->ops->release)(ctx->ctx); 580 free(ctx); 581} 582 583int 584heim_ipc_call(heim_ipc ctx, const heim_idata *snd, heim_idata *rcv, 585 heim_icred *cred) 586{ 587 if (cred) 588 *cred = NULL; 589 return (ctx->ops->ipc)(ctx->ctx, snd, rcv, cred); 590} 591 592int 593heim_ipc_async(heim_ipc ctx, const heim_idata *snd, void *userctx, 594 void (*func)(void *, int, heim_idata *, heim_icred)) 595{ 596 if (ctx->ops->async == NULL) { 597 heim_idata rcv; 598 heim_icred cred = NULL; 599 int ret; 600 601 ret = (ctx->ops->ipc)(ctx->ctx, snd, &rcv, &cred); 602 (*func)(userctx, ret, &rcv, cred); 603 heim_ipc_free_cred(cred); 604 free(rcv.data); 605 return ret; 606 } else { 607 return (ctx->ops->async)(ctx->ctx, snd, userctx, func); 608 } 609} 610