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 #include <mach/mach.h> 45 46 static dispatch_once_t jobqinited = 0; 47 static dispatch_queue_t jobq = NULL; 48 static dispatch_queue_t syncq; 49 50 struct mach_ctx { 51 mach_port_t server; 52 char *name; 53 }; 54 55 static int 56 mach_release(void *ctx); 57 58 static int 59 mach_init(const char *service, void **ctx) 60 { 61 struct mach_ctx *ipc; 62 mach_port_t sport; 63 int ret; 64 65 dispatch_once(&jobqinited, ^{ 66 jobq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 67 syncq = dispatch_queue_create("heim-ipc-syncq", NULL); 68 }); 69 70 ret = bootstrap_look_up(bootstrap_port, service, &sport); 71 if (ret) 72 return ret; 73 74 ipc = malloc(sizeof(*ipc)); 75 if (ipc == NULL) { 76 mach_port_destroy(mach_task_self(), sport); 77 return ENOMEM; 78 } 79 80 ipc->server = sport; 81 ipc->name = strdup(service); 82 if (ipc->name == NULL) { 83 mach_release(ipc); 84 return ENOMEM; 85 } 86 87 *ctx = ipc; 88 89 return 0; 90 } 91 92 static int 93 mach_ipc(void *ctx, 94 const heim_idata *request, heim_idata *response, 95 heim_icred *cred) 96 { 97 struct mach_ctx *ipc = ctx; 98 heim_ipc_message_inband_t requestin; 99 mach_msg_type_number_t requestin_length = 0; 100 heim_ipc_message_outband_t requestout = NULL; 101 mach_msg_type_number_t requestout_length = 0; 102 heim_ipc_message_inband_t replyin; 103 mach_msg_type_number_t replyin_length; 104 heim_ipc_message_outband_t replyout; 105 mach_msg_type_number_t replyout_length; 106 int ret, errorcode, retries = 0; 107 108 memcpy(requestin, request->data, request->length); 109 requestin_length = request->length; 110 111 while (retries < 2) { 112 __block mach_port_t sport; 113 114 dispatch_sync(syncq, ^{ sport = ipc->server; }); 115 116 ret = mheim_ipc_call(sport, 117 requestin, requestin_length, 118 requestout, requestout_length, 119 &errorcode, 120 replyin, &replyin_length, 121 &replyout, &replyout_length); 122 if (ret == MACH_SEND_INVALID_DEST) { 123 mach_port_t nport; 124 /* race other threads to get a new port */ 125 ret = bootstrap_look_up(bootstrap_port, ipc->name, &nport); 126 if (ret) 127 return ret; 128 dispatch_sync(syncq, ^{ 129 /* check if we lost the race to lookup the port */ 130 if (sport != ipc->server) { 131 mach_port_deallocate(mach_task_self(), nport); 132 } else { 133 mach_port_deallocate(mach_task_self(), ipc->server); 134 ipc->server = nport; 135 } 136 }); 137 retries++; 138 } else if (ret) { 139 return ret; 140 } else 141 break; 142 } 143 if (retries >= 2) 144 return EINVAL; 145 146 if (errorcode) { 147 if (replyout_length) 148 vm_deallocate (mach_task_self (), (vm_address_t) replyout, 149 replyout_length); 150 return errorcode; 151 } 152 153 if (replyout_length) { 154 response->data = malloc(replyout_length); 155 if (response->data == NULL) { 156 vm_deallocate (mach_task_self (), (vm_address_t) replyout, 157 replyout_length); 158 return ENOMEM; 159 } 160 memcpy(response->data, replyout, replyout_length); 161 response->length = replyout_length; 162 vm_deallocate (mach_task_self (), (vm_address_t) replyout, 163 replyout_length); 164 } else { 165 response->data = malloc(replyin_length); 166 if (response->data == NULL) 167 return ENOMEM; 168 memcpy(response->data, replyin, replyin_length); 169 response->length = replyin_length; 170 } 171 172 return 0; 173 } 174 175 struct async_client { 176 mach_port_t mp; 177 dispatch_source_t source; 178 dispatch_queue_t queue; 179 void (*func)(void *, int, heim_idata *, heim_icred); 180 void *userctx; 181 }; 182 183 kern_return_t 184 mheim_ado_acall_reply(mach_port_t server_port, 185 audit_token_t client_creds, 186 int returnvalue, 187 heim_ipc_message_inband_t replyin, 188 mach_msg_type_number_t replyinCnt, 189 heim_ipc_message_outband_t replyout, 190 mach_msg_type_number_t replyoutCnt) 191 { 192 struct async_client *c = dispatch_get_context(dispatch_get_current_queue()); 193 heim_idata response; 194 195 if (returnvalue) { 196 response.data = NULL; 197 response.length = 0; 198 } else if (replyoutCnt) { 199 response.data = replyout; 200 response.length = replyoutCnt; 201 } else { 202 response.data = replyin; 203 response.length = replyinCnt; 204 } 205 206 (*c->func)(c->userctx, returnvalue, &response, NULL); 207 208 if (replyoutCnt) 209 vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyoutCnt); 210 211 dispatch_source_cancel(c->source); 212 213 return 0; 214 215 216 } 217 218 219 static int 220 mach_async(void *ctx, const heim_idata *request, void *userctx, 221 void (*func)(void *, int, heim_idata *, heim_icred)) 222 { 223 struct mach_ctx *ipc = ctx; 224 heim_ipc_message_inband_t requestin; 225 mach_msg_type_number_t requestin_length = 0; 226 heim_ipc_message_outband_t requestout = NULL; 227 mach_msg_type_number_t requestout_length = 0; 228 int ret, retries = 0; 229 kern_return_t kr; 230 struct async_client *c; 231 232 /* first create the service that will catch the reply from the server */ 233 /* XXX these object should be cached and reused */ 234 235 c = malloc(sizeof(*c)); 236 if (c == NULL) 237 return ENOMEM; 238 239 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &c->mp); 240 if (kr != KERN_SUCCESS) 241 return EINVAL; 242 243 c->queue = dispatch_queue_create("heim-ipc-async-client", NULL); 244 c->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, c->mp, 0, c->queue); 245 dispatch_set_context(c->queue, c); 246 247 dispatch_source_set_event_handler(c->source, ^{ 248 dispatch_mig_server(c->source, 249 sizeof(union __RequestUnion__mheim_ado_mheim_aipc_subsystem), 250 mheim_aipc_server); 251 }); 252 253 dispatch_source_set_cancel_handler(c->source, ^{ 254 mach_port_mod_refs(mach_task_self(), c->mp, 255 MACH_PORT_RIGHT_RECEIVE, -1); 256 dispatch_release(c->queue); 257 dispatch_release(c->source); 258 free(c); 259 }); 260 261 c->func = func; 262 c->userctx = userctx; 263 264 dispatch_resume(c->source); 265 266 /* ok, send the message */ 267 268 memcpy(requestin, request->data, request->length); 269 requestin_length = request->length; 270 271 while (retries < 2) { 272 __block mach_port_t sport; 273 274 dispatch_sync(syncq, ^{ sport = ipc->server; }); 275 276 ret = mheim_ipc_call_request(sport, c->mp, 277 requestin, requestin_length, 278 requestout, requestout_length); 279 if (ret == MACH_SEND_INVALID_DEST) { 280 ret = bootstrap_look_up(bootstrap_port, ipc->name, &sport); 281 if (ret) { 282 dispatch_source_cancel(c->source); 283 return ret; 284 } 285 mach_port_deallocate(mach_task_self(), ipc->server); 286 ipc->server = sport; 287 retries++; 288 } else if (ret) { 289 dispatch_source_cancel(c->source); 290 return ret; 291 } else 292 break; 293 } 294 if (retries >= 2) { 295 dispatch_source_cancel(c->source); 296 return EINVAL; 297 } 298 299 return 0; 300 } 301 302 static int 303 mach_release(void *ctx) 304 { 305 struct mach_ctx *ipc = ctx; 306 if (ipc->server != MACH_PORT_NULL) 307 mach_port_deallocate(mach_task_self(), ipc->server); 308 free(ipc->name); 309 free(ipc); 310 return 0; 311 } 312 313 #endif 314 315 struct path_ctx { 316 char *path; 317 int fd; 318 }; 319 320 static int common_release(void *); 321 322 static int 323 connect_unix(struct path_ctx *s) 324 { 325 struct sockaddr_un addr; 326 327 addr.sun_family = AF_UNIX; 328 strlcpy(addr.sun_path, s->path, sizeof(addr.sun_path)); 329 330 s->fd = socket(AF_UNIX, SOCK_STREAM, 0); 331 if (s->fd < 0) 332 return errno; 333 rk_cloexec(s->fd); 334 335 if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) 336 return errno; 337 338 return 0; 339 } 340 341 static int 342 common_path_init(const char *service, 343 const char *file, 344 void **ctx) 345 { 346 struct path_ctx *s; 347 348 s = malloc(sizeof(*s)); 349 if (s == NULL) 350 return ENOMEM; 351 s->fd = -1; 352 353 asprintf(&s->path, "/var/run/.heim_%s-%s", service, file); 354 355 *ctx = s; 356 357 return 0; 358 } 359 360 static int 361 unix_socket_init(const char *service, 362 void **ctx) 363 { 364 int ret; 365 366 ret = common_path_init(service, "socket", ctx); 367 if (ret) 368 return ret; 369 ret = connect_unix(*ctx); 370 if (ret) 371 common_release(*ctx); 372 373 return ret; 374 } 375 376 static int 377 unix_socket_ipc(void *ctx, 378 const heim_idata *req, heim_idata *rep, 379 heim_icred *cred) 380 { 381 struct path_ctx *s = ctx; 382 uint32_t len = htonl(req->length); 383 uint32_t rv; 384 int retval; 385 386 if (cred) 387 *cred = NULL; 388 389 rep->data = NULL; 390 rep->length = 0; 391 392 if (net_write(s->fd, &len, sizeof(len)) != sizeof(len)) 393 return -1; 394 if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length) 395 return -1; 396 397 if (net_read(s->fd, &len, sizeof(len)) != sizeof(len)) 398 return -1; 399 if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv)) 400 return -1; 401 retval = ntohl(rv); 402 403 rep->length = ntohl(len); 404 if (rep->length > 0) { 405 rep->data = malloc(rep->length); 406 if (rep->data == NULL) 407 return -1; 408 if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length) 409 return -1; 410 } else 411 rep->data = NULL; 412 413 return retval; 414 } 415 416 int 417 common_release(void *ctx) 418 { 419 struct path_ctx *s = ctx; 420 if (s->fd >= 0) 421 close(s->fd); 422 free(s->path); 423 free(s); 424 return 0; 425 } 426 427 #ifdef HAVE_DOOR 428 429 static int 430 door_init(const char *service, 431 void **ctx) 432 { 433 ret = common_path_init(context, service, "door", ctx); 434 if (ret) 435 return ret; 436 ret = connect_door(*ctx); 437 if (ret) 438 common_release(*ctx); 439 return ret; 440 } 441 442 static int 443 door_ipc(void *ctx, 444 const heim_idata *request, heim_idata *response, 445 heim_icred *cred) 446 { 447 door_arg_t arg; 448 int ret; 449 450 arg.data_ptr = request->data; 451 arg.data_size = request->length; 452 arg.desc_ptr = NULL; 453 arg.desc_num = 0; 454 arg.rbuf = NULL; 455 arg.rsize = 0; 456 457 ret = door_call(fd, &arg); 458 close(fd); 459 if (ret != 0) 460 return errno; 461 462 response->data = malloc(arg.rsize); 463 if (response->data == NULL) { 464 munmap(arg.rbuf, arg.rsize); 465 return ENOMEM; 466 } 467 memcpy(response->data, arg.rbuf, arg.rsize); 468 response->length = arg.rsize; 469 munmap(arg.rbuf, arg.rsize); 470 471 return ret; 472 } 473 474 #endif 475 476 struct hipc_ops { 477 const char *prefix; 478 int (*init)(const char *, void **); 479 int (*release)(void *); 480 int (*ipc)(void *,const heim_idata *, heim_idata *, heim_icred *); 481 int (*async)(void *, const heim_idata *, void *, 482 void (*)(void *, int, heim_idata *, heim_icred)); 483 }; 484 485 struct hipc_ops ipcs[] = { 486 #if defined(__APPLE__) && defined(HAVE_GCD) 487 { "MACH", mach_init, mach_release, mach_ipc, mach_async }, 488 #endif 489 #ifdef HAVE_DOOR 490 { "DOOR", door_init, common_release, door_ipc, NULL } 491 #endif 492 { "UNIX", unix_socket_init, common_release, unix_socket_ipc, NULL } 493 }; 494 495 struct heim_ipc { 496 struct hipc_ops *ops; 497 void *ctx; 498 }; 499 500 501 int 502 heim_ipc_init_context(const char *name, heim_ipc *ctx) 503 { 504 unsigned int i; 505 int ret, any = 0; 506 507 for(i = 0; i < sizeof(ipcs)/sizeof(ipcs[0]); i++) { 508 size_t prefix_len = strlen(ipcs[i].prefix); 509 heim_ipc c; 510 if(strncmp(ipcs[i].prefix, name, prefix_len) == 0 511 && name[prefix_len] == ':') { 512 } else if (strncmp("ANY:", name, 4) == 0) { 513 prefix_len = 3; 514 any = 1; 515 } else 516 continue; 517 518 c = calloc(1, sizeof(*c)); 519 if (c == NULL) 520 return ENOMEM; 521 522 c->ops = &ipcs[i]; 523 524 ret = (c->ops->init)(name + prefix_len + 1, &c->ctx); 525 if (ret) { 526 free(c); 527 if (any) 528 continue; 529 return ret; 530 } 531 532 *ctx = c; 533 return 0; 534 } 535 536 return ENOENT; 537 } 538 539 void 540 heim_ipc_free_context(heim_ipc ctx) 541 { 542 (ctx->ops->release)(ctx->ctx); 543 free(ctx); 544 } 545 546 int 547 heim_ipc_call(heim_ipc ctx, const heim_idata *snd, heim_idata *rcv, 548 heim_icred *cred) 549 { 550 if (cred) 551 *cred = NULL; 552 return (ctx->ops->ipc)(ctx->ctx, snd, rcv, cred); 553 } 554 555 int 556 heim_ipc_async(heim_ipc ctx, const heim_idata *snd, void *userctx, 557 void (*func)(void *, int, heim_idata *, heim_icred)) 558 { 559 if (ctx->ops->async == NULL) { 560 heim_idata rcv; 561 heim_icred cred = NULL; 562 int ret; 563 564 ret = (ctx->ops->ipc)(ctx->ctx, snd, &rcv, &cred); 565 (*func)(userctx, ret, &rcv, cred); 566 heim_ipc_free_cred(cred); 567 free(rcv.data); 568 return ret; 569 } else { 570 return (ctx->ops->async)(ctx->ctx, snd, userctx, func); 571 } 572 } 573