client.c revision 226031
1137015Sdes/* 298937Sdes * Copyright (c) 2009 Kungliga Tekniska H�gskolan 398937Sdes * (Royal Institute of Technology, Stockholm, Sweden). 498937Sdes * All rights reserved. 598937Sdes * 698937Sdes * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 798937Sdes * 898937Sdes * Redistribution and use in source and binary forms, with or without 998937Sdes * modification, are permitted provided that the following conditions 1098937Sdes * are met: 1198937Sdes * 1298937Sdes * 1. Redistributions of source code must retain the above copyright 1398937Sdes * notice, this list of conditions and the following disclaimer. 1498937Sdes * 1598937Sdes * 2. Redistributions in binary form must reproduce the above copyright 1698937Sdes * notice, this list of conditions and the following disclaimer in the 1798937Sdes * documentation and/or other materials provided with the distribution. 1898937Sdes * 1998937Sdes * 3. Neither the name of the Institute nor the names of its contributors 2098937Sdes * may be used to endorse or promote products derived from this software 2198937Sdes * without specific prior written permission. 2298937Sdes * 2398937Sdes * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 2498937Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2598937Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2698937Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 2798937Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2898937Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2999060Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30113908Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3198937Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3298937Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3398937Sdes * SUCH DAMAGE. 3498937Sdes */ 3598937Sdes 3698937Sdes#include "hi_locl.h" 3798937Sdes 3898937Sdes#if defined(__APPLE__) && defined(HAVE_GCD) 3998937Sdes 4098937Sdes#include "heim_ipc.h" 4198937Sdes#include "heim_ipc_asyncServer.h" 4298937Sdes 4398937Sdes#include <dispatch/dispatch.h> 4498937Sdes#include <mach/mach.h> 4598937Sdes 4698937Sdesstatic dispatch_once_t jobqinited = 0; 4798937Sdesstatic dispatch_queue_t jobq = NULL; 4898937Sdesstatic dispatch_queue_t syncq; 49124208Sdes 5098937Sdesstruct mach_ctx { 5198937Sdes mach_port_t server; 5298937Sdes char *name; 53113908Sdes}; 5498937Sdes 5598937Sdesstatic int 5698937Sdesmach_release(void *ctx); 5798937Sdes 5898937Sdesstatic int 5998937Sdesmach_init(const char *service, void **ctx) 6098937Sdes{ 6198937Sdes struct mach_ctx *ipc; 62113908Sdes mach_port_t sport; 6398937Sdes int ret; 64126274Sdes 65126274Sdes dispatch_once(&jobqinited, ^{ 66126274Sdes jobq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 67126274Sdes syncq = dispatch_queue_create("heim-ipc-syncq", NULL); 68126274Sdes }); 69126274Sdes 70137015Sdes ret = bootstrap_look_up(bootstrap_port, service, &sport); 71137015Sdes if (ret) 72137015Sdes return ret; 73124208Sdes 7498937Sdes ipc = malloc(sizeof(*ipc)); 75113908Sdes if (ipc == NULL) { 76113908Sdes mach_port_destroy(mach_task_self(), sport); 7798937Sdes return ENOMEM; 78113908Sdes } 79137015Sdes 80113908Sdes ipc->server = sport; 81113908Sdes ipc->name = strdup(service); 82113908Sdes if (ipc->name == NULL) { 83113908Sdes mach_release(ipc); 84137015Sdes return ENOMEM; 85124208Sdes } 86124208Sdes 87126274Sdes *ctx = ipc; 8898937Sdes 8998937Sdes return 0; 9098937Sdes} 9198937Sdes 9298937Sdesstatic int 9398937Sdesmach_ipc(void *ctx, 9498937Sdes const heim_idata *request, heim_idata *response, 9598937Sdes heim_icred *cred) 9698937Sdes{ 97113908Sdes struct mach_ctx *ipc = ctx; 98113908Sdes heim_ipc_message_inband_t requestin; 99113908Sdes mach_msg_type_number_t requestin_length = 0; 100113908Sdes heim_ipc_message_outband_t requestout = NULL; 101113908Sdes mach_msg_type_number_t requestout_length = 0; 102113908Sdes heim_ipc_message_inband_t replyin; 103113908Sdes mach_msg_type_number_t replyin_length; 104113908Sdes heim_ipc_message_outband_t replyout; 105113908Sdes mach_msg_type_number_t replyout_length; 106113908Sdes int ret, errorcode, retries = 0; 107113908Sdes 108113908Sdes memcpy(requestin, request->data, request->length); 109113908Sdes requestin_length = request->length; 110113908Sdes 111113908Sdes while (retries < 2) { 11298937Sdes __block mach_port_t sport; 113113908Sdes 11498937Sdes dispatch_sync(syncq, ^{ sport = ipc->server; }); 115124208Sdes 11698937Sdes ret = mheim_ipc_call(sport, 117124208Sdes requestin, requestin_length, 118124208Sdes requestout, requestout_length, 119124208Sdes &errorcode, 12098937Sdes replyin, &replyin_length, 12198937Sdes &replyout, &replyout_length); 12298937Sdes if (ret == MACH_SEND_INVALID_DEST) { 12398937Sdes mach_port_t nport; 12498937Sdes /* race other threads to get a new port */ 12598937Sdes ret = bootstrap_look_up(bootstrap_port, ipc->name, &nport); 12698937Sdes if (ret) 12798937Sdes return ret; 12898937Sdes dispatch_sync(syncq, ^{ 12998937Sdes /* check if we lost the race to lookup the port */ 13098937Sdes if (sport != ipc->server) { 13198937Sdes mach_port_deallocate(mach_task_self(), nport); 13298937Sdes } else { 13398937Sdes mach_port_deallocate(mach_task_self(), ipc->server); 13498937Sdes ipc->server = nport; 13598937Sdes } 13698937Sdes }); 13798937Sdes retries++; 13898937Sdes } else if (ret) { 139113908Sdes return ret; 140113908Sdes } else 14198937Sdes break; 14298937Sdes } 143126274Sdes if (retries >= 2) 14498937Sdes return EINVAL; 14598937Sdes 146126274Sdes if (errorcode) { 14798937Sdes if (replyout_length) 14898937Sdes vm_deallocate (mach_task_self (), (vm_address_t) replyout, 149126274Sdes replyout_length); 15098937Sdes return errorcode; 15198937Sdes } 152126274Sdes 15398937Sdes if (replyout_length) { 15498937Sdes response->data = malloc(replyout_length); 155126274Sdes if (response->data == NULL) { 15698937Sdes vm_deallocate (mach_task_self (), (vm_address_t) replyout, 15798937Sdes replyout_length); 158126274Sdes return ENOMEM; 15998937Sdes } 160126274Sdes memcpy(response->data, replyout, replyout_length); 161126274Sdes response->length = replyout_length; 16298937Sdes vm_deallocate (mach_task_self (), (vm_address_t) replyout, 16398937Sdes replyout_length); 16498937Sdes } else { 16598937Sdes response->data = malloc(replyin_length); 16698937Sdes if (response->data == NULL) 16798937Sdes return ENOMEM; 16898937Sdes memcpy(response->data, replyin, replyin_length); 16998937Sdes response->length = replyin_length; 17098937Sdes } 17198937Sdes 17298937Sdes return 0; 17398937Sdes} 17498937Sdes 17598937Sdesstruct async_client { 17698937Sdes mach_port_t mp; 177124208Sdes dispatch_source_t source; 17898937Sdes dispatch_queue_t queue; 17998937Sdes void (*func)(void *, int, heim_idata *, heim_icred); 18098937Sdes void *userctx; 18198937Sdes}; 18298937Sdes 18398937Sdeskern_return_t 18498937Sdesmheim_ado_acall_reply(mach_port_t server_port, 18598937Sdes audit_token_t client_creds, 186124208Sdes int returnvalue, 187124208Sdes heim_ipc_message_inband_t replyin, 188124208Sdes mach_msg_type_number_t replyinCnt, 189124208Sdes heim_ipc_message_outband_t replyout, 190124208Sdes mach_msg_type_number_t replyoutCnt) 191124208Sdes{ 192124208Sdes struct async_client *c = dispatch_get_context(dispatch_get_current_queue()); 193124208Sdes heim_idata response; 194124208Sdes 195124208Sdes if (returnvalue) { 196126274Sdes response.data = NULL; 197126274Sdes response.length = 0; 19898937Sdes } else if (replyoutCnt) { 19998937Sdes response.data = replyout; 200124208Sdes response.length = replyoutCnt; 201126274Sdes } else { 202137015Sdes response.data = replyin; 203137015Sdes response.length = replyinCnt; 20498937Sdes } 20598937Sdes 20698937Sdes (*c->func)(c->userctx, returnvalue, &response, NULL); 207137015Sdes 208137015Sdes if (replyoutCnt) 209137015Sdes vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyoutCnt); 21098937Sdes 211124208Sdes dispatch_source_cancel(c->source); 21298937Sdes 21398937Sdes return 0; 214124208Sdes 21598937Sdes 216124208Sdes} 217124208Sdes 21898937Sdes 21998937Sdesstatic int 22098937Sdesmach_async(void *ctx, const heim_idata *request, void *userctx, 22198937Sdes void (*func)(void *, int, heim_idata *, heim_icred)) 22298937Sdes{ 22398937Sdes struct mach_ctx *ipc = ctx; 22498937Sdes heim_ipc_message_inband_t requestin; 22598937Sdes mach_msg_type_number_t requestin_length = 0; 22698937Sdes heim_ipc_message_outband_t requestout = NULL; 22798937Sdes mach_msg_type_number_t requestout_length = 0; 228126274Sdes int ret, retries = 0; 22998937Sdes kern_return_t kr; 23098937Sdes struct async_client *c; 231124208Sdes 232124208Sdes /* first create the service that will catch the reply from the server */ 23398937Sdes /* XXX these object should be cached and reused */ 234106121Sdes 235106121Sdes c = malloc(sizeof(*c)); 23699060Sdes if (c == NULL) 23798937Sdes return ENOMEM; 23898937Sdes 23998937Sdes kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &c->mp); 24098937Sdes if (kr != KERN_SUCCESS) 24198937Sdes return EINVAL; 24298937Sdes 24398937Sdes c->queue = dispatch_queue_create("heim-ipc-async-client", NULL); 24498937Sdes c->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, c->mp, 0, c->queue); 24598937Sdes dispatch_set_context(c->queue, c); 24698937Sdes 24798937Sdes dispatch_source_set_event_handler(c->source, ^{ 24898937Sdes dispatch_mig_server(c->source, 249106121Sdes sizeof(union __RequestUnion__mheim_ado_mheim_aipc_subsystem), 250113908Sdes mheim_aipc_server); 251113908Sdes }); 252113908Sdes 253113908Sdes dispatch_source_set_cancel_handler(c->source, ^{ 254113908Sdes mach_port_mod_refs(mach_task_self(), c->mp, 255113908Sdes MACH_PORT_RIGHT_RECEIVE, -1); 256113908Sdes dispatch_release(c->queue); 25798937Sdes dispatch_release(c->source); 258113908Sdes free(c); 25998937Sdes }); 260113908Sdes 261113908Sdes c->func = func; 262113908Sdes c->userctx = userctx; 26398937Sdes 26498937Sdes dispatch_resume(c->source); 26598937Sdes 26698937Sdes /* ok, send the message */ 26798937Sdes 26898937Sdes memcpy(requestin, request->data, request->length); 26998937Sdes requestin_length = request->length; 27098937Sdes 27198937Sdes while (retries < 2) { 27299060Sdes __block mach_port_t sport; 27398937Sdes 27498937Sdes dispatch_sync(syncq, ^{ sport = ipc->server; }); 275113908Sdes 276113908Sdes ret = mheim_ipc_call_request(sport, c->mp, 27798937Sdes requestin, requestin_length, 27898937Sdes requestout, requestout_length); 279106121Sdes if (ret == MACH_SEND_INVALID_DEST) { 28098937Sdes ret = bootstrap_look_up(bootstrap_port, ipc->name, &sport); 281106121Sdes if (ret) { 28298937Sdes dispatch_source_cancel(c->source); 28398937Sdes return ret; 28498937Sdes } 285106121Sdes mach_port_deallocate(mach_task_self(), ipc->server); 28698937Sdes ipc->server = sport; 28798937Sdes retries++; 28898937Sdes } else if (ret) { 28998937Sdes dispatch_source_cancel(c->source); 290106121Sdes return ret; 29198937Sdes } else 29298937Sdes break; 29398937Sdes } 29498937Sdes if (retries >= 2) { 295106121Sdes dispatch_source_cancel(c->source); 29698937Sdes return EINVAL; 29798937Sdes } 29898937Sdes 29998937Sdes return 0; 30098937Sdes} 30198937Sdes 302106121Sdesstatic int 30398937Sdesmach_release(void *ctx) 30498937Sdes{ 30598937Sdes struct mach_ctx *ipc = ctx; 30698937Sdes if (ipc->server != MACH_PORT_NULL) 30798937Sdes mach_port_deallocate(mach_task_self(), ipc->server); 30898937Sdes free(ipc->name); 30998937Sdes free(ipc); 31098937Sdes return 0; 31198937Sdes} 31298937Sdes 31398937Sdes#endif 314106121Sdes 31598937Sdesstruct path_ctx { 31698937Sdes char *path; 31798937Sdes int fd; 31898937Sdes}; 31998937Sdes 32098937Sdesstatic int common_release(void *); 32198937Sdes 32298937Sdesstatic int 32398937Sdesconnect_unix(struct path_ctx *s) 32498937Sdes{ 32598937Sdes struct sockaddr_un addr; 32698937Sdes 32798937Sdes addr.sun_family = AF_UNIX; 32898937Sdes strlcpy(addr.sun_path, s->path, sizeof(addr.sun_path)); 32998937Sdes 33098937Sdes s->fd = socket(AF_UNIX, SOCK_STREAM, 0); 33198937Sdes if (s->fd < 0) 33298937Sdes return errno; 33398937Sdes rk_cloexec(s->fd); 33498937Sdes 33598937Sdes if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { 33698937Sdes close(s->fd); 33798937Sdes return errno; 33898937Sdes } 33998937Sdes 34098937Sdes return 0; 34198937Sdes} 34298937Sdes 34398937Sdesstatic int 34498937Sdescommon_path_init(const char *service, 34598937Sdes const char *file, 34698937Sdes void **ctx) 34798937Sdes{ 34898937Sdes struct path_ctx *s; 349126274Sdes 35098937Sdes s = malloc(sizeof(*s)); 35198937Sdes if (s == NULL) 35298937Sdes return ENOMEM; 35398937Sdes s->fd = -1; 35498937Sdes 35598937Sdes asprintf(&s->path, "/var/run/.heim_%s-%s", service, file); 35698937Sdes 35798937Sdes *ctx = s; 35898937Sdes 35998937Sdes return 0; 36098937Sdes} 36198937Sdes 36298937Sdesstatic int 36398937Sdesunix_socket_init(const char *service, 36498937Sdes void **ctx) 36598937Sdes{ 36698937Sdes int ret; 36798937Sdes 36898937Sdes ret = common_path_init(service, "socket", ctx); 36998937Sdes if (ret) 37098937Sdes return ret; 37198937Sdes ret = connect_unix(*ctx); 37298937Sdes if (ret) 37398937Sdes common_release(*ctx); 374124208Sdes 375124208Sdes return ret; 376124208Sdes} 377124208Sdes 378124208Sdesstatic int 379124208Sdesunix_socket_ipc(void *ctx, 380137015Sdes const heim_idata *req, heim_idata *rep, 381124208Sdes heim_icred *cred) 382124208Sdes{ 383124208Sdes struct path_ctx *s = ctx; 384124208Sdes uint32_t len = htonl(req->length); 385124208Sdes uint32_t rv; 386124208Sdes int retval; 387124208Sdes 388124208Sdes if (cred) 389124208Sdes *cred = NULL; 390124208Sdes 391124208Sdes rep->data = NULL; 392124208Sdes rep->length = 0; 393124208Sdes 394124208Sdes if (net_write(s->fd, &len, sizeof(len)) != sizeof(len)) 395124208Sdes return -1; 396126274Sdes if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length) 397124208Sdes return -1; 398124208Sdes 399124208Sdes if (net_read(s->fd, &len, sizeof(len)) != sizeof(len)) 400124208Sdes return -1; 401124208Sdes if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv)) 402124208Sdes return -1; 403124208Sdes retval = ntohl(rv); 404124208Sdes 405124208Sdes rep->length = ntohl(len); 406124208Sdes if (rep->length > 0) { 407124208Sdes rep->data = malloc(rep->length); 408124208Sdes if (rep->data == NULL) 409124208Sdes return -1; 410124208Sdes if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length) 411124208Sdes return -1; 412137015Sdes } else 413137015Sdes rep->data = NULL; 414137015Sdes 415137015Sdes return retval; 416137015Sdes} 417137015Sdes 418int 419common_release(void *ctx) 420{ 421 struct path_ctx *s = ctx; 422 if (s->fd >= 0) 423 close(s->fd); 424 free(s->path); 425 free(s); 426 return 0; 427} 428 429#ifdef HAVE_DOOR 430 431static int 432door_init(const char *service, 433 void **ctx) 434{ 435 ret = common_path_init(context, service, "door", ctx); 436 if (ret) 437 return ret; 438 ret = connect_door(*ctx); 439 if (ret) 440 common_release(*ctx); 441 return ret; 442} 443 444static int 445door_ipc(void *ctx, 446 const heim_idata *request, heim_idata *response, 447 heim_icred *cred) 448{ 449 door_arg_t arg; 450 int ret; 451 452 arg.data_ptr = request->data; 453 arg.data_size = request->length; 454 arg.desc_ptr = NULL; 455 arg.desc_num = 0; 456 arg.rbuf = NULL; 457 arg.rsize = 0; 458 459 ret = door_call(fd, &arg); 460 close(fd); 461 if (ret != 0) 462 return errno; 463 464 response->data = malloc(arg.rsize); 465 if (response->data == NULL) { 466 munmap(arg.rbuf, arg.rsize); 467 return ENOMEM; 468 } 469 memcpy(response->data, arg.rbuf, arg.rsize); 470 response->length = arg.rsize; 471 munmap(arg.rbuf, arg.rsize); 472 473 return ret; 474} 475 476#endif 477 478struct hipc_ops { 479 const char *prefix; 480 int (*init)(const char *, void **); 481 int (*release)(void *); 482 int (*ipc)(void *,const heim_idata *, heim_idata *, heim_icred *); 483 int (*async)(void *, const heim_idata *, void *, 484 void (*)(void *, int, heim_idata *, heim_icred)); 485}; 486 487struct hipc_ops ipcs[] = { 488#if defined(__APPLE__) && defined(HAVE_GCD) 489 { "MACH", mach_init, mach_release, mach_ipc, mach_async }, 490#endif 491#ifdef HAVE_DOOR 492 { "DOOR", door_init, common_release, door_ipc, NULL } 493#endif 494 { "UNIX", unix_socket_init, common_release, unix_socket_ipc, NULL } 495}; 496 497struct heim_ipc { 498 struct hipc_ops *ops; 499 void *ctx; 500}; 501 502 503int 504heim_ipc_init_context(const char *name, heim_ipc *ctx) 505{ 506 unsigned int i; 507 int ret, any = 0; 508 509 for(i = 0; i < sizeof(ipcs)/sizeof(ipcs[0]); i++) { 510 size_t prefix_len = strlen(ipcs[i].prefix); 511 heim_ipc c; 512 if(strncmp(ipcs[i].prefix, name, prefix_len) == 0 513 && name[prefix_len] == ':') { 514 } else if (strncmp("ANY:", name, 4) == 0) { 515 prefix_len = 3; 516 any = 1; 517 } else 518 continue; 519 520 c = calloc(1, sizeof(*c)); 521 if (c == NULL) 522 return ENOMEM; 523 524 c->ops = &ipcs[i]; 525 526 ret = (c->ops->init)(name + prefix_len + 1, &c->ctx); 527 if (ret) { 528 free(c); 529 if (any) 530 continue; 531 return ret; 532 } 533 534 *ctx = c; 535 return 0; 536 } 537 538 return ENOENT; 539} 540 541void 542heim_ipc_free_context(heim_ipc ctx) 543{ 544 (ctx->ops->release)(ctx->ctx); 545 free(ctx); 546} 547 548int 549heim_ipc_call(heim_ipc ctx, const heim_idata *snd, heim_idata *rcv, 550 heim_icred *cred) 551{ 552 if (cred) 553 *cred = NULL; 554 return (ctx->ops->ipc)(ctx->ctx, snd, rcv, cred); 555} 556 557int 558heim_ipc_async(heim_ipc ctx, const heim_idata *snd, void *userctx, 559 void (*func)(void *, int, heim_idata *, heim_icred)) 560{ 561 if (ctx->ops->async == NULL) { 562 heim_idata rcv; 563 heim_icred cred = NULL; 564 int ret; 565 566 ret = (ctx->ops->ipc)(ctx->ctx, snd, &rcv, &cred); 567 (*func)(userctx, ret, &rcv, cred); 568 heim_ipc_free_cred(cred); 569 free(rcv.data); 570 return ret; 571 } else { 572 return (ctx->ops->async)(ctx->ctx, snd, userctx, func); 573 } 574} 575