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