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 = 0; 120 heim_ipc_message_outband_t replyout = 0; 121 mach_msg_type_number_t replyout_length = 0; 122 int ret, errorcode = -1, 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((void *)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 free(c); 266 return EINVAL; 267 } 268 269 c->queue = dispatch_queue_create("heim-ipc-async-client", NULL); 270 c->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, c->mp, 0, c->queue); 271 dispatch_queue_set_specific(c->queue, (void *)mheim_ado_acall_reply, c, NULL); 272 273 dispatch_source_set_event_handler(c->source, ^{ 274 dispatch_mig_server(c->source, 275 sizeof(union __RequestUnion__mheim_ado_mheim_aipc_subsystem), 276 mheim_aipc_server); 277 }); 278 279 dispatch_source_set_cancel_handler(c->source, ^{ 280 mach_port_mod_refs(mach_task_self(), c->mp, 281 MACH_PORT_RIGHT_RECEIVE, -1); 282 dispatch_release(c->queue); 283 dispatch_release(c->source); 284 free(c); 285 }); 286 287 c->func = func; 288 c->userctx = userctx; 289 290 dispatch_resume(c->source); 291 292 /* ok, send the message */ 293 if (request->length < sizeof(requestin)) { 294 memcpy(requestin, request->data, request->length); 295 requestin_length = (mach_msg_type_number_t)request->length; 296 } else { 297 ret = vm_read(mach_task_self(), 298 (vm_address_t)request->data, request->length, 299 (vm_address_t *)&requestout, &requestout_length); 300 if (ret) 301 return ENOMEM; 302 } 303 304 while (retries < 2) { 305 __block mach_port_t sport; 306 307 dispatch_sync(syncq, ^{ sport = ipc->server; }); 308 309 ret = mheim_ipc_call_request(sport, c->mp, 310 requestin, requestin_length, 311 requestout, requestout_length); 312 if (ret == MACH_SEND_INVALID_DEST) { 313 ret = look_up(ipc->name, &sport); 314 if (ret) { 315 dispatch_source_cancel(c->source); 316 return ret; 317 } 318 mach_port_deallocate(mach_task_self(), ipc->server); 319 ipc->server = sport; 320 retries++; 321 } else if (ret) { 322 dispatch_source_cancel(c->source); 323 return ret; 324 } else 325 break; 326 } 327 if (retries >= 2) { 328 dispatch_source_cancel(c->source); 329 return EINVAL; 330 } 331 332 return 0; 333} 334 335static int 336mach_release(void *ctx) 337{ 338 struct mach_ctx *ipc = ctx; 339 if (ipc->server != MACH_PORT_NULL) 340 mach_port_deallocate(mach_task_self(), ipc->server); 341 free(ipc->name); 342 free(ipc); 343 return 0; 344} 345 346#endif 347 348struct path_ctx { 349 char *path; 350 int fd; 351}; 352 353static int common_release(void *); 354 355static int 356connect_unix(struct path_ctx *s) 357{ 358 struct sockaddr_un addr; 359 360 addr.sun_family = AF_UNIX; 361 strlcpy(addr.sun_path, s->path, sizeof(addr.sun_path)); 362 363 s->fd = socket(AF_UNIX, SOCK_STREAM, 0); 364 if (s->fd < 0) 365 return errno; 366 rk_cloexec(s->fd); 367 socket_set_nopipe(s->fd, 1); 368 369 if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { 370 close(s->fd); 371 s->fd = -1; 372 return errno; 373 } 374 375 return 0; 376} 377 378static int 379common_path_init(const char *service, 380 const char *file, 381 void **ctx) 382{ 383 struct path_ctx *s; 384 385 s = malloc(sizeof(*s)); 386 if (s == NULL) 387 return ENOMEM; 388 s->fd = -1; 389 390 asprintf(&s->path, "/var/run/.heim_%s-%s", service, file); 391 392 *ctx = s; 393 394 return 0; 395} 396 397static int 398unix_socket_init(const char *service, 399 void **ctx) 400{ 401 int ret; 402 403 ret = common_path_init(service, "socket", ctx); 404 if (ret) 405 return ret; 406 ret = connect_unix(*ctx); 407 if (ret) 408 common_release(*ctx); 409 410 return ret; 411} 412 413static int 414unix_socket_ipc(void *ctx, 415 const heim_idata *req, heim_idata *rep, 416 heim_icred *cred) 417{ 418 struct path_ctx *s = ctx; 419 uint32_t len = htonl(req->length); 420 uint32_t rv; 421 int retval; 422 423 if (cred) 424 *cred = NULL; 425 426 rep->data = NULL; 427 rep->length = 0; 428 429 if (net_write(s->fd, &len, sizeof(len)) != sizeof(len)) 430 return -1; 431 if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length) 432 return -1; 433 434 if (net_read(s->fd, &len, sizeof(len)) != sizeof(len)) 435 return -1; 436 if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv)) 437 return -1; 438 retval = ntohl(rv); 439 440 rep->length = ntohl(len); 441 if (rep->length > MAX_PACKET_SIZE) { 442 rep->length = 0; 443 return EINVAL; 444 } else { 445 rep->data = malloc(rep->length); 446 if (rep->data == NULL) 447 return -1; 448 if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length) 449 return -1; 450 } 451 452 return retval; 453} 454 455int 456common_release(void *ctx) 457{ 458 struct path_ctx *s = ctx; 459 if (s->fd >= 0) 460 close(s->fd); 461 free(s->path); 462 free(s); 463 return 0; 464} 465 466#ifdef HAVE_DOOR 467 468static int 469door_init(const char *service, 470 void **ctx) 471{ 472 ret = common_path_init(context, service, "door", ctx); 473 if (ret) 474 return ret; 475 ret = connect_door(*ctx); 476 if (ret) 477 common_release(*ctx); 478 return ret; 479} 480 481static int 482door_ipc(void *ctx, 483 const heim_idata *request, heim_idata *response, 484 heim_icred *cred) 485{ 486 door_arg_t arg; 487 int ret; 488 489 arg.data_ptr = request->data; 490 arg.data_size = request->length; 491 arg.desc_ptr = NULL; 492 arg.desc_num = 0; 493 arg.rbuf = NULL; 494 arg.rsize = 0; 495 496 ret = door_call(fd, &arg); 497 close(fd); 498 if (ret != 0) 499 return errno; 500 501 response->data = malloc(arg.rsize); 502 if (response->data == NULL) { 503 munmap(arg.rbuf, arg.rsize); 504 return ENOMEM; 505 } 506 memcpy(response->data, arg.rbuf, arg.rsize); 507 response->length = arg.rsize; 508 munmap(arg.rbuf, arg.rsize); 509 510 return ret; 511} 512 513#endif 514 515struct hipc_ops { 516 const char *prefix; 517 int (*init)(const char *, void **); 518 int (*release)(void *); 519 int (*ipc)(void *,const heim_idata *, heim_idata *, heim_icred *); 520 int (*async)(void *, const heim_idata *, void *, 521 void (*)(void *, int, heim_idata *, heim_icred)); 522}; 523 524struct hipc_ops ipcs[] = { 525#if defined(__APPLE__) && defined(HAVE_GCD) 526 { "MACH", mach_init, mach_release, mach_ipc, mach_async }, 527#endif 528#ifdef HAVE_DOOR 529 { "DOOR", door_init, common_release, door_ipc, NULL } 530#endif 531 { "UNIX", unix_socket_init, common_release, unix_socket_ipc, NULL } 532}; 533 534struct heim_ipc { 535 struct hipc_ops *ops; 536 void *ctx; 537}; 538 539 540int 541heim_ipc_init_context(const char *name, heim_ipc *ctx) 542{ 543 unsigned int i; 544 int ret, any = 0; 545 546 for(i = 0; i < sizeof(ipcs)/sizeof(ipcs[0]); i++) { 547 size_t prefix_len = strlen(ipcs[i].prefix); 548 heim_ipc c; 549 if(strncmp(ipcs[i].prefix, name, prefix_len) == 0 550 && name[prefix_len] == ':') { 551 } else if (strncmp("ANY:", name, 4) == 0) { 552 prefix_len = 3; 553 any = 1; 554 } else 555 continue; 556 557 c = calloc(1, sizeof(*c)); 558 if (c == NULL) 559 return ENOMEM; 560 561 c->ops = &ipcs[i]; 562 563 ret = (c->ops->init)(name + prefix_len + 1, &c->ctx); 564 if (ret) { 565 free(c); 566 if (any) 567 continue; 568 return ret; 569 } 570 571 *ctx = c; 572 return 0; 573 } 574 575 return ENOENT; 576} 577 578void 579heim_ipc_free_context(heim_ipc ctx) 580{ 581 (ctx->ops->release)(ctx->ctx); 582 free(ctx); 583} 584 585int 586heim_ipc_call(heim_ipc ctx, const heim_idata *snd, heim_idata *rcv, 587 heim_icred *cred) 588{ 589 if (cred) 590 *cred = NULL; 591 return (ctx->ops->ipc)(ctx->ctx, snd, rcv, cred); 592} 593 594int 595heim_ipc_async(heim_ipc ctx, const heim_idata *snd, void *userctx, 596 void (*func)(void *, int, heim_idata *, heim_icred)) 597{ 598 if (ctx->ops->async == NULL) { 599 heim_idata rcv; 600 heim_icred cred = NULL; 601 int ret; 602 603 ret = (ctx->ops->ipc)(ctx->ctx, snd, &rcv, &cred); 604 (*func)(userctx, ret, &rcv, cred); 605 heim_ipc_free_cred(cred); 606 free(rcv.data); 607 return ret; 608 } else { 609 return (ctx->ops->async)(ctx->ctx, snd, userctx, func); 610 } 611} 612