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