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 #include <assert.h> 38 39 #define MAX_PACKET_SIZE (128 * 1024) 40 41 struct heim_sipc { 42 int (*release)(heim_sipc ctx); 43 heim_ipc_callback callback; 44 void *userctx; 45 void *mech; 46 }; 47 48 #if defined(__APPLE__) && defined(HAVE_GCD) 49 50 #include "heim_ipcServer.h" 51 #include "heim_ipc_reply.h" 52 #include "heim_ipc_async.h" 53 54 static dispatch_source_t timer; 55 static dispatch_queue_t timerq; 56 static uint64_t timeoutvalue; 57 58 static dispatch_queue_t eventq; 59 60 static dispatch_queue_t workq; 61 62 static void 63 default_timer_ev(void) 64 { 65 exit(0); 66 } 67 68 static void (*timer_ev)(void) = default_timer_ev; 69 70 static void 71 set_timer(void) 72 { 73 dispatch_source_set_timer(timer, 74 dispatch_time(DISPATCH_TIME_NOW, 75 timeoutvalue * NSEC_PER_SEC), 76 timeoutvalue * NSEC_PER_SEC, 1000000); 77 } 78 79 static void 80 init_globals(void) 81 { 82 static dispatch_once_t once; 83 dispatch_once(&once, ^{ 84 timerq = dispatch_queue_create("hiem-sipc-timer-q", NULL); 85 timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timerq); 86 dispatch_source_set_event_handler(timer, ^{ timer_ev(); } ); 87 88 workq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 89 eventq = dispatch_queue_create("heim-ipc.event-queue", NULL); 90 }); 91 } 92 93 static void 94 suspend_timer(void) 95 { 96 dispatch_suspend(timer); 97 } 98 99 static void 100 restart_timer(void) 101 { 102 dispatch_sync(timerq, ^{ set_timer(); }); 103 dispatch_resume(timer); 104 } 105 106 struct mach_service { 107 mach_port_t sport; 108 dispatch_source_t source; 109 dispatch_queue_t queue; 110 }; 111 112 struct mach_call_ctx { 113 mach_port_t reply_port; 114 heim_icred cred; 115 heim_idata req; 116 }; 117 118 119 static void 120 mach_complete_sync(heim_sipc_call ctx, int returnvalue, heim_idata *reply) 121 { 122 struct mach_call_ctx *s = (struct mach_call_ctx *)ctx; 123 heim_ipc_message_inband_t replyin; 124 mach_msg_type_number_t replyinCnt; 125 heim_ipc_message_outband_t replyout; 126 mach_msg_type_number_t replyoutCnt; 127 kern_return_t kr; 128 129 if (returnvalue) { 130 /* on error, no reply */ 131 replyinCnt = 0; 132 replyout = 0; replyoutCnt = 0; 133 kr = KERN_SUCCESS; 134 } else if (reply->length < 2048) { 135 replyinCnt = reply->length; 136 memcpy(replyin, reply->data, replyinCnt); 137 replyout = 0; replyoutCnt = 0; 138 kr = KERN_SUCCESS; 139 } else { 140 replyinCnt = 0; 141 kr = vm_read(mach_task_self(), 142 (vm_address_t)reply->data, reply->length, 143 (vm_address_t *)&replyout, &replyoutCnt); 144 } 145 146 mheim_ripc_call_reply(s->reply_port, returnvalue, 147 replyin, replyinCnt, 148 replyout, replyoutCnt); 149 150 heim_ipc_free_cred(s->cred); 151 free(s->req.data); 152 free(s); 153 restart_timer(); 154 } 155 156 static void 157 mach_complete_async(heim_sipc_call ctx, int returnvalue, heim_idata *reply) 158 { 159 struct mach_call_ctx *s = (struct mach_call_ctx *)ctx; 160 heim_ipc_message_inband_t replyin; 161 mach_msg_type_number_t replyinCnt; 162 heim_ipc_message_outband_t replyout; 163 mach_msg_type_number_t replyoutCnt; 164 kern_return_t kr; 165 166 if (returnvalue) { 167 /* on error, no reply */ 168 replyinCnt = 0; 169 replyout = 0; replyoutCnt = 0; 170 kr = KERN_SUCCESS; 171 } else if (reply->length < 2048) { 172 replyinCnt = reply->length; 173 memcpy(replyin, reply->data, replyinCnt); 174 replyout = 0; replyoutCnt = 0; 175 kr = KERN_SUCCESS; 176 } else { 177 replyinCnt = 0; 178 kr = vm_read(mach_task_self(), 179 (vm_address_t)reply->data, reply->length, 180 (vm_address_t *)&replyout, &replyoutCnt); 181 } 182 183 kr = mheim_aipc_acall_reply(s->reply_port, returnvalue, 184 replyin, replyinCnt, 185 replyout, replyoutCnt); 186 heim_ipc_free_cred(s->cred); 187 free(s->req.data); 188 free(s); 189 restart_timer(); 190 } 191 192 193 kern_return_t 194 mheim_do_call(mach_port_t server_port, 195 audit_token_t client_creds, 196 mach_port_t reply_port, 197 heim_ipc_message_inband_t requestin, 198 mach_msg_type_number_t requestinCnt, 199 heim_ipc_message_outband_t requestout, 200 mach_msg_type_number_t requestoutCnt, 201 int *returnvalue, 202 heim_ipc_message_inband_t replyin, 203 mach_msg_type_number_t *replyinCnt, 204 heim_ipc_message_outband_t *replyout, 205 mach_msg_type_number_t *replyoutCnt) 206 { 207 heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue()); 208 struct mach_call_ctx *s; 209 kern_return_t kr; 210 uid_t uid; 211 gid_t gid; 212 pid_t pid; 213 au_asid_t session; 214 215 *replyout = NULL; 216 *replyoutCnt = 0; 217 *replyinCnt = 0; 218 219 s = malloc(sizeof(*s)); 220 if (s == NULL) 221 return KERN_MEMORY_FAILURE; /* XXX */ 222 223 s->reply_port = reply_port; 224 225 audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL); 226 227 kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred); 228 if (kr) { 229 free(s); 230 return kr; 231 } 232 233 suspend_timer(); 234 235 if (requestinCnt) { 236 s->req.data = malloc(requestinCnt); 237 memcpy(s->req.data, requestin, requestinCnt); 238 s->req.length = requestinCnt; 239 } else { 240 s->req.data = malloc(requestoutCnt); 241 memcpy(s->req.data, requestout, requestoutCnt); 242 s->req.length = requestoutCnt; 243 } 244 245 dispatch_async(workq, ^{ 246 (ctx->callback)(ctx->userctx, &s->req, s->cred, 247 mach_complete_sync, (heim_sipc_call)s); 248 }); 249 250 return MIG_NO_REPLY; 251 } 252 253 kern_return_t 254 mheim_do_call_request(mach_port_t server_port, 255 audit_token_t client_creds, 256 mach_port_t reply_port, 257 heim_ipc_message_inband_t requestin, 258 mach_msg_type_number_t requestinCnt, 259 heim_ipc_message_outband_t requestout, 260 mach_msg_type_number_t requestoutCnt) 261 { 262 heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue()); 263 struct mach_call_ctx *s; 264 kern_return_t kr; 265 uid_t uid; 266 gid_t gid; 267 pid_t pid; 268 au_asid_t session; 269 270 s = malloc(sizeof(*s)); 271 if (s == NULL) 272 return KERN_MEMORY_FAILURE; /* XXX */ 273 274 s->reply_port = reply_port; 275 276 audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL); 277 278 kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred); 279 if (kr) { 280 free(s); 281 return kr; 282 } 283 284 suspend_timer(); 285 286 if (requestinCnt) { 287 s->req.data = malloc(requestinCnt); 288 memcpy(s->req.data, requestin, requestinCnt); 289 s->req.length = requestinCnt; 290 } else { 291 s->req.data = malloc(requestoutCnt); 292 memcpy(s->req.data, requestout, requestoutCnt); 293 s->req.length = requestoutCnt; 294 } 295 296 dispatch_async(workq, ^{ 297 (ctx->callback)(ctx->userctx, &s->req, s->cred, 298 mach_complete_async, (heim_sipc_call)s); 299 }); 300 301 return KERN_SUCCESS; 302 } 303 304 static int 305 mach_init(const char *service, mach_port_t sport, heim_sipc ctx) 306 { 307 struct mach_service *s; 308 char *name; 309 310 init_globals(); 311 312 s = calloc(1, sizeof(*s)); 313 if (s == NULL) 314 return ENOMEM; 315 316 asprintf(&name, "heim-ipc-mach-%s", service); 317 318 s->queue = dispatch_queue_create(name, NULL); 319 free(name); 320 s->sport = sport; 321 322 s->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, 323 s->sport, 0, s->queue); 324 if (s->source == NULL) { 325 dispatch_release(s->queue); 326 free(s); 327 return ENOMEM; 328 } 329 ctx->mech = s; 330 331 dispatch_set_context(s->queue, ctx); 332 dispatch_set_context(s->source, s); 333 334 dispatch_source_set_event_handler(s->source, ^{ 335 dispatch_mig_server(s->source, sizeof(union __RequestUnion__mheim_do_mheim_ipc_subsystem), mheim_ipc_server); 336 }); 337 338 dispatch_source_set_cancel_handler(s->source, ^{ 339 heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue()); 340 struct mach_service *st = ctx->mech; 341 mach_port_mod_refs(mach_task_self(), st->sport, 342 MACH_PORT_RIGHT_RECEIVE, -1); 343 dispatch_release(st->queue); 344 dispatch_release(st->source); 345 free(st); 346 free(ctx); 347 }); 348 349 dispatch_resume(s->source); 350 351 return 0; 352 } 353 354 static int 355 mach_release(heim_sipc ctx) 356 { 357 struct mach_service *s = ctx->mech; 358 dispatch_source_cancel(s->source); 359 dispatch_release(s->source); 360 return 0; 361 } 362 363 static mach_port_t 364 mach_checkin_or_register(const char *service) 365 { 366 mach_port_t mp; 367 kern_return_t kr; 368 369 kr = bootstrap_check_in(bootstrap_port, service, &mp); 370 if (kr == KERN_SUCCESS) 371 return mp; 372 373 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1050 374 /* Pre SnowLeopard version */ 375 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp); 376 if (kr != KERN_SUCCESS) 377 return MACH_PORT_NULL; 378 379 kr = mach_port_insert_right(mach_task_self(), mp, mp, 380 MACH_MSG_TYPE_MAKE_SEND); 381 if (kr != KERN_SUCCESS) { 382 mach_port_destroy(mach_task_self(), mp); 383 return MACH_PORT_NULL; 384 } 385 386 kr = bootstrap_register(bootstrap_port, rk_UNCONST(service), mp); 387 if (kr != KERN_SUCCESS) { 388 mach_port_destroy(mach_task_self(), mp); 389 return MACH_PORT_NULL; 390 } 391 392 return mp; 393 #else 394 return MACH_PORT_NULL; 395 #endif 396 } 397 398 399 #endif /* __APPLE__ && HAVE_GCD */ 400 401 402 int 403 heim_sipc_launchd_mach_init(const char *service, 404 heim_ipc_callback callback, 405 void *user, heim_sipc *ctx) 406 { 407 #if defined(__APPLE__) && defined(HAVE_GCD) 408 mach_port_t sport = MACH_PORT_NULL; 409 heim_sipc c = NULL; 410 int ret; 411 412 *ctx = NULL; 413 414 sport = mach_checkin_or_register(service); 415 if (sport == MACH_PORT_NULL) { 416 ret = ENOENT; 417 goto error; 418 } 419 420 c = calloc(1, sizeof(*c)); 421 if (c == NULL) { 422 ret = ENOMEM; 423 goto error; 424 } 425 c->release = mach_release; 426 c->userctx = user; 427 c->callback = callback; 428 429 ret = mach_init(service, sport, c); 430 if (ret) 431 goto error; 432 433 *ctx = c; 434 return 0; 435 error: 436 if (c) 437 free(c); 438 if (sport != MACH_PORT_NULL) 439 mach_port_mod_refs(mach_task_self(), sport, 440 MACH_PORT_RIGHT_RECEIVE, -1); 441 return ret; 442 #else /* !(__APPLE__ && HAVE_GCD) */ 443 *ctx = NULL; 444 return EINVAL; 445 #endif /* __APPLE__ && HAVE_GCD */ 446 } 447 448 struct client { 449 int fd; 450 heim_ipc_callback callback; 451 void *userctx; 452 int flags; 453 #define LISTEN_SOCKET 1 454 #define WAITING_READ 2 455 #define WAITING_WRITE 4 456 #define WAITING_CLOSE 8 457 458 #define HTTP_REPLY 16 459 460 #define INHERIT_MASK 0xffff0000 461 #define INCLUDE_ERROR_CODE (1 << 16) 462 #define ALLOW_HTTP (1<<17) 463 #define UNIX_SOCKET (1<<18) 464 unsigned calls; 465 size_t ptr, len; 466 uint8_t *inmsg; 467 size_t olen; 468 uint8_t *outmsg; 469 #ifdef HAVE_GCD 470 dispatch_source_t in; 471 dispatch_source_t out; 472 #endif 473 struct { 474 uid_t uid; 475 gid_t gid; 476 pid_t pid; 477 } unixrights; 478 }; 479 480 #ifndef HAVE_GCD 481 static unsigned num_clients = 0; 482 static struct client **clients = NULL; 483 #endif 484 485 static void handle_read(struct client *); 486 static void handle_write(struct client *); 487 static int maybe_close(struct client *); 488 489 /* 490 * Update peer credentials from socket. 491 * 492 * SCM_CREDS can only be updated the first time there is read data to 493 * read from the filedescriptor, so if we read do it before this 494 * point, the cred data might not be is not there yet. 495 */ 496 497 static int 498 update_client_creds(struct client *c) 499 { 500 #ifdef HAVE_GETPEERUCRED 501 /* Solaris 10 */ 502 { 503 ucred_t *peercred; 504 505 if (getpeerucred(c->fd, &peercred) != 0) { 506 c->unixrights.uid = ucred_geteuid(peercred); 507 c->unixrights.gid = ucred_getegid(peercred); 508 c->unixrights.pid = 0; 509 ucred_free(peercred); 510 return 1; 511 } 512 } 513 #endif 514 #ifdef HAVE_GETPEEREID 515 /* FreeBSD, OpenBSD */ 516 { 517 uid_t uid; 518 gid_t gid; 519 520 if (getpeereid(c->fd, &uid, &gid) == 0) { 521 c->unixrights.uid = uid; 522 c->unixrights.gid = gid; 523 c->unixrights.pid = 0; 524 return 1; 525 } 526 } 527 #endif 528 #ifdef SO_PEERCRED 529 /* Linux */ 530 { 531 struct ucred pc; 532 socklen_t pclen = sizeof(pc); 533 534 if (getsockopt(c->fd, SOL_SOCKET, SO_PEERCRED, (void *)&pc, &pclen) == 0) { 535 c->unixrights.uid = pc.uid; 536 c->unixrights.gid = pc.gid; 537 c->unixrights.pid = pc.pid; 538 return 1; 539 } 540 } 541 #endif 542 #if defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION) 543 { 544 struct xucred peercred; 545 socklen_t peercredlen = sizeof(peercred); 546 547 if (getsockopt(c->fd, LOCAL_PEERCRED, 1, 548 (void *)&peercred, &peercredlen) == 0 549 && peercred.cr_version == XUCRED_VERSION) 550 { 551 c->unixrights.uid = peercred.cr_uid; 552 c->unixrights.gid = peercred.cr_gid; 553 c->unixrights.pid = peercred.cr_pid; 554 return 1; 555 } 556 } 557 #endif 558 #if defined(SOCKCREDSIZE) && defined(SCM_CREDS) 559 /* NetBSD */ 560 if (c->unixrights.uid == (uid_t)-1) { 561 struct msghdr msg; 562 socklen_t crmsgsize; 563 void *crmsg; 564 struct cmsghdr *cmp; 565 struct sockcred *sc; 566 567 memset(&msg, 0, sizeof(msg)); 568 crmsgsize = CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)); 569 if (crmsgsize == 0) 570 return 1 ; 571 572 crmsg = malloc(crmsgsize); 573 if (crmsg == NULL) 574 goto failed_scm_creds; 575 576 memset(crmsg, 0, crmsgsize); 577 578 msg.msg_control = crmsg; 579 msg.msg_controllen = crmsgsize; 580 581 if (recvmsg(c->fd, &msg, 0) < 0) { 582 free(crmsg); 583 goto failed_scm_creds; 584 } 585 586 if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) { 587 free(crmsg); 588 goto failed_scm_creds; 589 } 590 591 cmp = CMSG_FIRSTHDR(&msg); 592 if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) { 593 free(crmsg); 594 goto failed_scm_creds; 595 } 596 597 sc = (struct sockcred *)(void *)CMSG_DATA(cmp); 598 599 c->unixrights.uid = sc->sc_euid; 600 c->unixrights.gid = sc->sc_egid; 601 c->unixrights.pid = 0; 602 603 free(crmsg); 604 return 1; 605 } else { 606 /* we already got the cred, just return it */ 607 return 1; 608 } 609 failed_scm_creds: 610 #endif 611 return 0; 612 } 613 614 615 static struct client * 616 add_new_socket(int fd, 617 int flags, 618 heim_ipc_callback callback, 619 void *userctx) 620 { 621 struct client *c; 622 int fileflags; 623 624 c = calloc(1, sizeof(*c)); 625 if (c == NULL) 626 return NULL; 627 628 if (flags & LISTEN_SOCKET) { 629 c->fd = fd; 630 } else { 631 c->fd = accept(fd, NULL, NULL); 632 if(c->fd < 0) { 633 free(c); 634 return NULL; 635 } 636 } 637 638 c->flags = flags; 639 c->callback = callback; 640 c->userctx = userctx; 641 642 fileflags = fcntl(c->fd, F_GETFL, 0); 643 fcntl(c->fd, F_SETFL, fileflags | O_NONBLOCK); 644 645 #ifdef HAVE_GCD 646 init_globals(); 647 648 c->in = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, 649 c->fd, 0, eventq); 650 c->out = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, 651 c->fd, 0, eventq); 652 653 dispatch_source_set_event_handler(c->in, ^{ 654 int rw = (c->flags & WAITING_WRITE); 655 handle_read(c); 656 if (rw == 0 && (c->flags & WAITING_WRITE)) 657 dispatch_resume(c->out); 658 if ((c->flags & WAITING_READ) == 0) 659 dispatch_suspend(c->in); 660 maybe_close(c); 661 }); 662 dispatch_source_set_event_handler(c->out, ^{ 663 handle_write(c); 664 if ((c->flags & WAITING_WRITE) == 0) { 665 dispatch_suspend(c->out); 666 } 667 maybe_close(c); 668 }); 669 670 dispatch_resume(c->in); 671 #else 672 clients = erealloc(clients, sizeof(clients[0]) * (num_clients + 1)); 673 clients[num_clients] = c; 674 num_clients++; 675 #endif 676 677 return c; 678 } 679 680 static int 681 maybe_close(struct client *c) 682 { 683 if (c->calls != 0) 684 return 0; 685 if (c->flags & (WAITING_READ|WAITING_WRITE)) 686 return 0; 687 688 #ifdef HAVE_GCD 689 dispatch_source_cancel(c->in); 690 if ((c->flags & WAITING_READ) == 0) 691 dispatch_resume(c->in); 692 dispatch_release(c->in); 693 694 dispatch_source_cancel(c->out); 695 if ((c->flags & WAITING_WRITE) == 0) 696 dispatch_resume(c->out); 697 dispatch_release(c->out); 698 #endif 699 close(c->fd); /* ref count fd close */ 700 free(c); 701 return 1; 702 } 703 704 705 struct socket_call { 706 heim_idata in; 707 struct client *c; 708 heim_icred cred; 709 }; 710 711 static void 712 output_data(struct client *c, const void *data, size_t len) 713 { 714 if (c->olen + len < c->olen) 715 abort(); 716 c->outmsg = erealloc(c->outmsg, c->olen + len); 717 memcpy(&c->outmsg[c->olen], data, len); 718 c->olen += len; 719 c->flags |= WAITING_WRITE; 720 } 721 722 static void 723 socket_complete(heim_sipc_call ctx, int returnvalue, heim_idata *reply) 724 { 725 struct socket_call *sc = (struct socket_call *)ctx; 726 struct client *c = sc->c; 727 728 /* double complete ? */ 729 if (c == NULL) 730 abort(); 731 732 if ((c->flags & WAITING_CLOSE) == 0) { 733 uint32_t u32; 734 735 /* length */ 736 u32 = htonl(reply->length); 737 output_data(c, &u32, sizeof(u32)); 738 739 /* return value */ 740 if (c->flags & INCLUDE_ERROR_CODE) { 741 u32 = htonl(returnvalue); 742 output_data(c, &u32, sizeof(u32)); 743 } 744 745 /* data */ 746 output_data(c, reply->data, reply->length); 747 748 /* if HTTP, close connection */ 749 if (c->flags & HTTP_REPLY) { 750 c->flags |= WAITING_CLOSE; 751 c->flags &= ~WAITING_READ; 752 } 753 } 754 755 c->calls--; 756 if (sc->cred) 757 heim_ipc_free_cred(sc->cred); 758 free(sc->in.data); 759 sc->c = NULL; /* so we can catch double complete */ 760 free(sc); 761 762 maybe_close(c); 763 } 764 765 /* remove HTTP %-quoting from buf */ 766 static int 767 de_http(char *buf) 768 { 769 unsigned char *p, *q; 770 for(p = q = (unsigned char *)buf; *p; p++, q++) { 771 if(*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) { 772 unsigned int x; 773 if(sscanf((char *)p + 1, "%2x", &x) != 1) 774 return -1; 775 *q = x; 776 p += 2; 777 } else 778 *q = *p; 779 } 780 *q = '\0'; 781 return 0; 782 } 783 784 static struct socket_call * 785 handle_http_tcp(struct client *c) 786 { 787 struct socket_call *cs; 788 char *s, *p, *t; 789 void *data; 790 char *proto; 791 int len; 792 793 s = (char *)c->inmsg; 794 795 p = strstr(s, "\r\n"); 796 if (p == NULL) 797 return NULL; 798 799 *p = 0; 800 801 p = NULL; 802 t = strtok_r(s, " \t", &p); 803 if (t == NULL) 804 return NULL; 805 806 t = strtok_r(NULL, " \t", &p); 807 if (t == NULL) 808 return NULL; 809 810 data = malloc(strlen(t)); 811 if (data == NULL) 812 return NULL; 813 814 if(*t == '/') 815 t++; 816 if(de_http(t) != 0) { 817 free(data); 818 return NULL; 819 } 820 proto = strtok_r(NULL, " \t", &p); 821 if (proto == NULL) { 822 free(data); 823 return NULL; 824 } 825 len = base64_decode(t, data); 826 if(len <= 0){ 827 const char *msg = 828 " 404 Not found\r\n" 829 "Server: Heimdal/" VERSION "\r\n" 830 "Cache-Control: no-cache\r\n" 831 "Pragma: no-cache\r\n" 832 "Content-type: text/html\r\n" 833 "Content-transfer-encoding: 8bit\r\n\r\n" 834 "<TITLE>404 Not found</TITLE>\r\n" 835 "<H1>404 Not found</H1>\r\n" 836 "That page doesn't exist, maybe you are looking for " 837 "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n"; 838 free(data); 839 output_data(c, proto, strlen(proto)); 840 output_data(c, msg, strlen(msg)); 841 return NULL; 842 } 843 844 cs = emalloc(sizeof(*cs)); 845 cs->c = c; 846 cs->in.data = data; 847 cs->in.length = len; 848 c->ptr = 0; 849 850 { 851 const char *msg = 852 " 200 OK\r\n" 853 "Server: Heimdal/" VERSION "\r\n" 854 "Cache-Control: no-cache\r\n" 855 "Pragma: no-cache\r\n" 856 "Content-type: application/octet-stream\r\n" 857 "Content-transfer-encoding: binary\r\n\r\n"; 858 output_data(c, proto, strlen(proto)); 859 output_data(c, msg, strlen(msg)); 860 } 861 862 return cs; 863 } 864 865 866 static void 867 handle_read(struct client *c) 868 { 869 ssize_t len; 870 uint32_t dlen; 871 872 if (c->flags & LISTEN_SOCKET) { 873 add_new_socket(c->fd, 874 WAITING_READ | (c->flags & INHERIT_MASK), 875 c->callback, 876 c->userctx); 877 return; 878 } 879 880 if (c->ptr - c->len < 1024) { 881 c->inmsg = erealloc(c->inmsg, 882 c->len + 1024); 883 c->len += 1024; 884 } 885 886 len = read(c->fd, c->inmsg + c->ptr, c->len - c->ptr); 887 if (len <= 0) { 888 c->flags |= WAITING_CLOSE; 889 c->flags &= ~WAITING_READ; 890 return; 891 } 892 c->ptr += len; 893 if (c->ptr > c->len) 894 abort(); 895 896 while (c->ptr >= sizeof(dlen)) { 897 struct socket_call *cs; 898 899 if((c->flags & ALLOW_HTTP) && c->ptr >= 4 && 900 strncmp((char *)c->inmsg, "GET ", 4) == 0 && 901 strncmp((char *)c->inmsg + c->ptr - 4, "\r\n\r\n", 4) == 0) { 902 903 /* remove the trailing \r\n\r\n so the string is NUL terminated */ 904 c->inmsg[c->ptr - 4] = '\0'; 905 906 c->flags |= HTTP_REPLY; 907 908 cs = handle_http_tcp(c); 909 if (cs == NULL) { 910 c->flags |= WAITING_CLOSE; 911 c->flags &= ~WAITING_READ; 912 break; 913 } 914 } else { 915 memcpy(&dlen, c->inmsg, sizeof(dlen)); 916 dlen = ntohl(dlen); 917 918 if (dlen > MAX_PACKET_SIZE) { 919 c->flags |= WAITING_CLOSE; 920 c->flags &= ~WAITING_READ; 921 return; 922 } 923 if (dlen > c->ptr - sizeof(dlen)) { 924 break; 925 } 926 927 cs = emalloc(sizeof(*cs)); 928 cs->c = c; 929 cs->in.data = emalloc(dlen); 930 memcpy(cs->in.data, c->inmsg + sizeof(dlen), dlen); 931 cs->in.length = dlen; 932 933 c->ptr -= sizeof(dlen) + dlen; 934 memmove(c->inmsg, 935 c->inmsg + sizeof(dlen) + dlen, 936 c->ptr); 937 } 938 939 c->calls++; 940 941 if ((c->flags & UNIX_SOCKET) != 0) { 942 if (update_client_creds(c)) 943 _heim_ipc_create_cred(c->unixrights.uid, c->unixrights.gid, 944 c->unixrights.pid, -1, &cs->cred); 945 } 946 947 c->callback(c->userctx, &cs->in, 948 cs->cred, socket_complete, 949 (heim_sipc_call)cs); 950 } 951 } 952 953 static void 954 handle_write(struct client *c) 955 { 956 ssize_t len; 957 958 len = write(c->fd, c->outmsg, c->olen); 959 if (len <= 0) { 960 c->flags |= WAITING_CLOSE; 961 c->flags &= ~(WAITING_WRITE); 962 } else if (c->olen != (size_t)len) { 963 memmove(&c->outmsg[0], &c->outmsg[len], c->olen - len); 964 c->olen -= len; 965 } else { 966 c->olen = 0; 967 free(c->outmsg); 968 c->outmsg = NULL; 969 c->flags &= ~(WAITING_WRITE); 970 } 971 } 972 973 974 #ifndef HAVE_GCD 975 976 static void 977 process_loop(void) 978 { 979 struct pollfd *fds; 980 unsigned n; 981 unsigned num_fds; 982 983 while(num_clients > 0) { 984 985 fds = malloc(num_clients * sizeof(fds[0])); 986 if(fds == NULL) 987 abort(); 988 989 num_fds = num_clients; 990 991 for (n = 0 ; n < num_fds; n++) { 992 fds[n].fd = clients[n]->fd; 993 fds[n].events = 0; 994 if (clients[n]->flags & WAITING_READ) 995 fds[n].events |= POLLIN; 996 if (clients[n]->flags & WAITING_WRITE) 997 fds[n].events |= POLLOUT; 998 999 fds[n].revents = 0; 1000 } 1001 1002 poll(fds, num_fds, -1); 1003 1004 for (n = 0 ; n < num_fds; n++) { 1005 if (clients[n] == NULL) 1006 continue; 1007 if (fds[n].revents & POLLERR) { 1008 clients[n]->flags |= WAITING_CLOSE; 1009 continue; 1010 } 1011 1012 if (fds[n].revents & POLLIN) 1013 handle_read(clients[n]); 1014 if (fds[n].revents & POLLOUT) 1015 handle_write(clients[n]); 1016 } 1017 1018 n = 0; 1019 while (n < num_clients) { 1020 struct client *c = clients[n]; 1021 if (maybe_close(c)) { 1022 if (n < num_clients - 1) 1023 clients[n] = clients[num_clients - 1]; 1024 num_clients--; 1025 } else 1026 n++; 1027 } 1028 1029 free(fds); 1030 } 1031 } 1032 1033 #endif 1034 1035 static int 1036 socket_release(heim_sipc ctx) 1037 { 1038 struct client *c = ctx->mech; 1039 c->flags |= WAITING_CLOSE; 1040 return 0; 1041 } 1042 1043 int 1044 heim_sipc_stream_listener(int fd, int type, 1045 heim_ipc_callback callback, 1046 void *user, heim_sipc *ctx) 1047 { 1048 heim_sipc ct = calloc(1, sizeof(*ct)); 1049 struct client *c; 1050 1051 if ((type & HEIM_SIPC_TYPE_IPC) && (type & (HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP))) 1052 return EINVAL; 1053 1054 switch (type) { 1055 case HEIM_SIPC_TYPE_IPC: 1056 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|INCLUDE_ERROR_CODE, callback, user); 1057 break; 1058 case HEIM_SIPC_TYPE_UINT32: 1059 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ, callback, user); 1060 break; 1061 case HEIM_SIPC_TYPE_HTTP: 1062 case HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP: 1063 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|ALLOW_HTTP, callback, user); 1064 break; 1065 default: 1066 free(ct); 1067 return EINVAL; 1068 } 1069 1070 ct->mech = c; 1071 ct->release = socket_release; 1072 1073 c->unixrights.uid = (uid_t) -1; 1074 c->unixrights.gid = (gid_t) -1; 1075 c->unixrights.pid = (pid_t) 0; 1076 1077 *ctx = ct; 1078 return 0; 1079 } 1080 1081 int 1082 heim_sipc_service_unix(const char *service, 1083 heim_ipc_callback callback, 1084 void *user, heim_sipc *ctx) 1085 { 1086 struct sockaddr_un un; 1087 int fd, ret; 1088 1089 un.sun_family = AF_UNIX; 1090 1091 snprintf(un.sun_path, sizeof(un.sun_path), 1092 "/var/run/.heim_%s-socket", service); 1093 fd = socket(AF_UNIX, SOCK_STREAM, 0); 1094 if (fd < 0) 1095 return errno; 1096 1097 socket_set_reuseaddr(fd, 1); 1098 #ifdef LOCAL_CREDS 1099 { 1100 int one = 1; 1101 setsockopt(fd, 0, LOCAL_CREDS, (void *)&one, sizeof(one)); 1102 } 1103 #endif 1104 1105 unlink(un.sun_path); 1106 1107 if (bind(fd, (struct sockaddr *)&un, sizeof(un)) < 0) { 1108 close(fd); 1109 return errno; 1110 } 1111 1112 if (listen(fd, SOMAXCONN) < 0) { 1113 close(fd); 1114 return errno; 1115 } 1116 1117 chmod(un.sun_path, 0666); 1118 1119 ret = heim_sipc_stream_listener(fd, HEIM_SIPC_TYPE_IPC, 1120 callback, user, ctx); 1121 if (ret == 0) { 1122 struct client *c = (*ctx)->mech; 1123 c->flags |= UNIX_SOCKET; 1124 } 1125 1126 return ret; 1127 } 1128 1129 /** 1130 * Set the idle timeout value 1131 1132 * The timeout event handler is triggered recurrently every idle 1133 * period `t'. The default action is rather draconian and just calls 1134 * exit(0), so you might want to change this to something more 1135 * graceful using heim_sipc_set_timeout_handler(). 1136 */ 1137 1138 void 1139 heim_sipc_timeout(time_t t) 1140 { 1141 #ifdef HAVE_GCD 1142 static dispatch_once_t timeoutonce; 1143 init_globals(); 1144 dispatch_sync(timerq, ^{ 1145 timeoutvalue = t; 1146 set_timer(); 1147 }); 1148 dispatch_once(&timeoutonce, ^{ dispatch_resume(timer); }); 1149 #else 1150 abort(); 1151 #endif 1152 } 1153 1154 /** 1155 * Set the timeout event handler 1156 * 1157 * Replaces the default idle timeout action. 1158 */ 1159 1160 void 1161 heim_sipc_set_timeout_handler(void (*func)(void)) 1162 { 1163 #ifdef HAVE_GCD 1164 init_globals(); 1165 dispatch_sync(timerq, ^{ timer_ev = func; }); 1166 #else 1167 abort(); 1168 #endif 1169 } 1170 1171 1172 void 1173 heim_sipc_free_context(heim_sipc ctx) 1174 { 1175 (ctx->release)(ctx); 1176 } 1177 1178 void 1179 heim_ipc_main(void) 1180 { 1181 #ifdef HAVE_GCD 1182 dispatch_main(); 1183 #else 1184 process_loop(); 1185 #endif 1186 } 1187 1188