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