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 close(s->fd); 337 return errno; 338 } 339 340 return 0; 341 } 342 343 static int 344 common_path_init(const char *service, 345 const char *file, 346 void **ctx) 347 { 348 struct path_ctx *s; 349 350 s = malloc(sizeof(*s)); 351 if (s == NULL) 352 return ENOMEM; 353 s->fd = -1; 354 355 asprintf(&s->path, "/var/run/.heim_%s-%s", service, file); 356 357 *ctx = s; 358 359 return 0; 360 } 361 362 static int 363 unix_socket_init(const char *service, 364 void **ctx) 365 { 366 int ret; 367 368 ret = common_path_init(service, "socket", ctx); 369 if (ret) 370 return ret; 371 ret = connect_unix(*ctx); 372 if (ret) 373 common_release(*ctx); 374 375 return ret; 376 } 377 378 static int 379 unix_socket_ipc(void *ctx, 380 const heim_idata *req, heim_idata *rep, 381 heim_icred *cred) 382 { 383 struct path_ctx *s = ctx; 384 uint32_t len = htonl(req->length); 385 uint32_t rv; 386 int retval; 387 388 if (cred) 389 *cred = NULL; 390 391 rep->data = NULL; 392 rep->length = 0; 393 394 if (net_write(s->fd, &len, sizeof(len)) != sizeof(len)) 395 return -1; 396 if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length) 397 return -1; 398 399 if (net_read(s->fd, &len, sizeof(len)) != sizeof(len)) 400 return -1; 401 if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv)) 402 return -1; 403 retval = ntohl(rv); 404 405 rep->length = ntohl(len); 406 if (rep->length > 0) { 407 rep->data = malloc(rep->length); 408 if (rep->data == NULL) 409 return -1; 410 if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length) 411 return -1; 412 } else 413 rep->data = NULL; 414 415 return retval; 416 } 417 418 int 419 common_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 431 static int 432 door_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 444 static int 445 door_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 478 struct 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 487 struct 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 497 struct heim_ipc { 498 struct hipc_ops *ops; 499 void *ctx; 500 }; 501 502 503 int 504 heim_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 541 void 542 heim_ipc_free_context(heim_ipc ctx) 543 { 544 (ctx->ops->release)(ctx->ctx); 545 free(ctx); 546 } 547 548 int 549 heim_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 557 int 558 heim_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