identd.c revision 1.2
1/* $OpenBSD: identd.c,v 1.2 2013/03/18 01:20:46 dlg Exp $ */ 2 3/* 4 * Copyright (c) 2013 David Gwynne <dlg@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/ioctl.h> 20#include <sys/param.h> 21#include <sys/types.h> 22#include <sys/queue.h> 23#include <sys/socket.h> 24#include <sys/socketvar.h> 25#include <sys/sysctl.h> 26#include <sys/uio.h> 27 28#include <netinet/in.h> 29#include <netinet/in_systm.h> 30#include <netinet/ip_var.h> 31#include <netinet/tcp.h> 32#include <netinet/tcp_timer.h> 33#include <netinet/tcp_var.h> 34 35#include <arpa/inet.h> 36#include <netdb.h> 37 38#include <err.h> 39#include <ctype.h> 40#include <errno.h> 41#include <event.h> 42#include <fcntl.h> 43#include <pwd.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <string.h> 47#include <stdarg.h> 48#include <syslog.h> 49#include <unistd.h> 50 51#define IDENTD_USER "_identd" 52 53#define TIMEOUT_MIN 4 54#define TIMEOUT_MAX 240 55#define TIMEOUT_DEFAULT 120 56 57enum ident_client_state { 58 S_BEGINNING = 0, 59 S_SERVER_PORT, 60 S_PRE_COMMA, 61 S_POST_COMMA, 62 S_CLIENT_PORT, 63 S_PRE_EOL, 64 S_EOL, 65 66 S_DEAD, 67 S_DYING 68}; 69 70#define E_NONE 0 71#define E_NOUSER 1 72#define E_UNKNOWN 2 73#define E_HIDDEN 3 74 75struct ident_client { 76 struct { 77 /* from the socket */ 78 struct sockaddr_storage ss; 79 socklen_t len; 80 81 /* from the request */ 82 u_int port; 83 } client, server; 84 SIMPLEQ_ENTRY(ident_client) entry; 85 enum ident_client_state state; 86 struct event ev; 87 88 char *buf; 89 size_t buflen; 90 size_t bufoff; 91 uid_t uid; 92}; 93 94struct ident_resolver { 95 SIMPLEQ_ENTRY(ident_resolver) entry; 96 char *buf; 97 size_t buflen; 98 u_int error; 99}; 100 101struct identd_listener { 102 struct event ev, pause; 103}; 104 105void parent_rd(int, short, void *); 106void parent_wr(int, short, void *); 107 108void child_rd(int, short, void *); 109void child_wr(int, short, void *); 110 111void identd_listen(const char *, const char *, int); 112void identd_paused(int, short, void *); 113void identd_accept(int, short, void *); 114void identd_request(int, short, void *); 115enum ident_client_state 116 identd_parse(struct ident_client *, int); 117void identd_resolving(int, short, void *); 118void identd_response(int, short, void *); 119int fetchuid(struct ident_client *); 120 121const char *gethost(struct sockaddr_storage *); 122const char *getport(struct sockaddr_storage *); 123 124struct loggers { 125 void (*err)(int, const char *, ...); 126 void (*errx)(int, const char *, ...); 127 void (*warn)(const char *, ...); 128 void (*warnx)(const char *, ...); 129 void (*info)(const char *, ...); 130 void (*debug)(const char *, ...); 131}; 132 133const struct loggers conslogger = { 134 err, 135 errx, 136 warn, 137 warnx, 138 warnx, /* info */ 139 warnx /* debug */ 140}; 141 142void syslog_err(int, const char *, ...); 143void syslog_errx(int, const char *, ...); 144void syslog_warn(const char *, ...); 145void syslog_warnx(const char *, ...); 146void syslog_info(const char *, ...); 147void syslog_debug(const char *, ...); 148void syslog_vstrerror(int, int, const char *, va_list); 149 150const struct loggers syslogger = { 151 syslog_err, 152 syslog_errx, 153 syslog_warn, 154 syslog_warnx, 155 syslog_info, 156 syslog_debug 157}; 158 159const struct loggers *logger = &conslogger; 160 161#define lerr(_e, _f...) logger->err((_e), _f) 162#define lerrx(_e, _f...) logger->errx((_e), _f) 163#define lwarn(_f...) logger->warn(_f) 164#define lwarnx(_f...) logger->warnx(_f) 165#define linfo(_f...) logger->info(_f) 166#define ldebug(_f...) logger->debug(_f) 167 168const char *gethost(struct sockaddr_storage *); 169#define sa(_ss) ((struct sockaddr *)(_ss)) 170 171__dead void 172usage(void) 173{ 174 extern char *__progname; 175 fprintf(stderr, "usage: %s [-46d] [-l address] [-p port] " 176 "[-t timeout]\n", __progname); 177 exit(1); 178} 179 180struct timeval timeout = { TIMEOUT_DEFAULT, 0 }; 181int debug = 0; 182int on = 1; 183 184struct event proc_rd, proc_wr; 185union { 186 struct { 187 SIMPLEQ_HEAD(, ident_resolver) replies; 188 } parent; 189 struct { 190 SIMPLEQ_HEAD(, ident_client) pushing, popping; 191 } child; 192} sc; 193 194int 195main(int argc, char *argv[]) 196{ 197 extern char *__progname; 198 const char *errstr = NULL; 199 200 int c; 201 struct passwd *pw; 202 203 char *addr = NULL; 204 char *port = "auth"; 205 int family = AF_UNSPEC; 206 207 int pair[2]; 208 pid_t parent; 209 int sibling; 210 211 while ((c = getopt(argc, argv, "46dl:p:t:")) != -1) { 212 switch (c) { 213 case '4': 214 family = AF_INET; 215 break; 216 case '6': 217 family = AF_INET6; 218 break; 219 case 'd': 220 debug = 1; 221 break; 222 case 'l': 223 addr = optarg; 224 break; 225 case 'p': 226 port = optarg; 227 break; 228 case 't': 229 timeout.tv_sec = strtonum(optarg, 230 TIMEOUT_MIN, TIMEOUT_MAX, &errstr); 231 if (errstr != NULL) 232 errx(1, "timeout %s is %s", optarg, errstr); 233 break; 234 default: 235 usage(); 236 /* NOTREACHED */ 237 } 238 } 239 240 argc -= optind; 241 argv += optind; 242 243 if (argc != 0) 244 usage(); 245 246 if (geteuid() != 0) 247 errx(1, "need root privileges"); 248 249 if (socketpair(AF_UNIX, SOCK_SEQPACKET, PF_UNSPEC, pair) == -1) 250 err(1, "socketpair"); 251 252 pw = getpwnam(IDENTD_USER); 253 if (pw == NULL) 254 err(1, "no %s user", IDENTD_USER); 255 256 if (!debug && daemon(1, 0) == -1) 257 err(1, "daemon"); 258 259 parent = fork(); 260 switch (parent) { 261 case -1: 262 lerr(1, "fork"); 263 264 case 0: 265 /* child */ 266 setproctitle("listener"); 267 close(pair[1]); 268 sibling = pair[0]; 269 break; 270 271 default: 272 /* parent */ 273 setproctitle("resolver"); 274 close(pair[0]); 275 sibling = pair[1]; 276 break; 277 } 278 279 if (!debug) { 280 openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON); 281 tzset(); 282 logger = &syslogger; 283 } 284 285 event_init(); 286 287 if (ioctl(sibling, FIONBIO, &on) == -1) 288 lerr(1, "sibling ioctl(FIONBIO)"); 289 290 if (parent) { 291 SIMPLEQ_INIT(&sc.parent.replies); 292 293 event_set(&proc_rd, sibling, EV_READ | EV_PERSIST, 294 parent_rd, NULL); 295 event_set(&proc_wr, sibling, EV_WRITE, 296 parent_wr, NULL); 297 } else { 298 SIMPLEQ_INIT(&sc.child.pushing); 299 SIMPLEQ_INIT(&sc.child.popping); 300 301 identd_listen(addr, port, family); 302 303 if (chroot(pw->pw_dir) == -1) 304 lerr(1, "chroot(%s)", pw->pw_dir); 305 306 if (chdir("/") == -1) 307 lerr(1, "chdir(%s)", pw->pw_dir); 308 309 event_set(&proc_rd, sibling, EV_READ | EV_PERSIST, 310 child_rd, NULL); 311 event_set(&proc_wr, sibling, EV_WRITE, 312 child_wr, NULL); 313 } 314 315 if (setgroups(1, &pw->pw_gid) || 316 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 317 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 318 lerr(1, "unable to revoke privs"); 319 320 event_add(&proc_rd, NULL); 321 322 event_dispatch(); 323 324 return (0); 325} 326 327void 328parent_rd(int fd, short events, void *arg) 329{ 330 struct ident_resolver *r; 331 struct passwd *pw; 332 ssize_t n; 333 uid_t uid; 334 335 n = read(fd, &uid, sizeof(uid)); 336 switch (n) { 337 case -1: 338 switch (errno) { 339 case EAGAIN: 340 case EINTR: 341 return; 342 default: 343 lerr(1, "parent read"); 344 } 345 break; 346 case 0: 347 lerrx(1, "child has gone"); 348 case sizeof(uid): 349 break; 350 default: 351 lerrx(1, "unexpected %zd data from child", n); 352 } 353 354 event_add(&proc_wr, NULL); 355 356 r = calloc(1, sizeof(*r)); 357 if (r == NULL) 358 lerr(1, "resolver alloc"); 359 360 pw = getpwuid(uid); 361 if (pw == NULL) { 362 r->error = E_NOUSER; 363 } else { 364 n = asprintf(&r->buf, "%s", pw->pw_name); 365 if (n == -1) 366 r->error = E_UNKNOWN; 367 else { 368 r->error = E_NONE; 369 r->buflen = n; 370 } 371 } 372 373 SIMPLEQ_INSERT_TAIL(&sc.parent.replies, r, entry); 374 event_add(&proc_wr, NULL); 375} 376 377void 378parent_wr(int fd, short events, void *arg) 379{ 380 struct ident_resolver *r = SIMPLEQ_FIRST(&sc.parent.replies); 381 struct iovec iov[2]; 382 int iovcnt = 0; 383 ssize_t n; 384 385 iov[iovcnt].iov_base = &r->error; 386 iov[iovcnt].iov_len = sizeof(r->error); 387 iovcnt++; 388 389 if (r->buflen > 0) { 390 iov[iovcnt].iov_base = r->buf; 391 iov[iovcnt].iov_len = r->buflen; 392 iovcnt++; 393 } 394 395 n = writev(fd, iov, iovcnt); 396 if (n == -1) { 397 if (errno == EAGAIN) { 398 event_add(&proc_wr, NULL); 399 return; 400 } 401 lerr(1, "parent write"); 402 } 403 404 if (n != sizeof(r->error) + r->buflen) 405 lerrx(1, "unexpected parent write length %zd", n); 406 407 SIMPLEQ_REMOVE_HEAD(&sc.parent.replies, entry); 408 409 if (r->buflen > 0) 410 free(r->buf); 411 412 free(r); 413} 414 415void 416child_rd(int fd, short events, void *arg) 417{ 418 struct ident_client *c; 419 struct { 420 u_int error; 421 char buf[512]; 422 } reply; 423 ssize_t n; 424 425 n = read(fd, &reply, sizeof(reply)); 426 switch (n) { 427 case -1: 428 switch (errno) { 429 case EAGAIN: 430 case EINTR: 431 return; 432 default: 433 lerr(1, "child read"); 434 } 435 break; 436 case 0: 437 lerrx(1, "parent has gone"); 438 default: 439 break; 440 } 441 442 c = SIMPLEQ_FIRST(&sc.child.popping); 443 if (c == NULL) 444 lerrx(1, "unsolicited data from parent"); 445 446 SIMPLEQ_REMOVE_HEAD(&sc.child.popping, entry); 447 448 if (n < sizeof(reply.error)) 449 lerrx(1, "short data from parent"); 450 451 /* check if something went wrong while the parent was working */ 452 switch (c->state) { 453 case S_DYING: /* we're sending an error to the client */ 454 c->state = S_DEAD; 455 return; 456 case S_DEAD: /* we finished sending an error to the client */ 457 free(c); 458 return; 459 default: 460 break; 461 } 462 463 switch (reply.error) { 464 case E_NONE: 465 n = asprintf(&c->buf, "%u , %u : USERID : UNIX : %s\r\n", 466 c->server.port, c->client.port, reply.buf); 467 break; 468 case E_NOUSER: 469 n = asprintf(&c->buf, "%u , %u : ERROR : NO-USER\r\n", 470 c->server.port, c->client.port); 471 break; 472 case E_UNKNOWN: 473 n = asprintf(&c->buf, "%u , %u : ERROR : UNKNOWN-ERROR\r\n", 474 c->server.port, c->client.port); 475 break; 476 case E_HIDDEN: 477 n = asprintf(&c->buf, "%u , %u : ERROR : HIDDEN-USER\r\n", 478 c->server.port, c->client.port); 479 break; 480 default: 481 lerrx(1, "unexpected error from parent %u", reply.error); 482 } 483 if (n == -1) 484 goto fail; 485 486 c->buflen = n; 487 488 fd = EVENT_FD(&c->ev); 489 event_del(&c->ev); 490 event_set(&c->ev, fd, EV_READ | EV_WRITE | EV_PERSIST, 491 identd_response, c); 492 event_add(&c->ev, &timeout); 493 return; 494 495fail: 496 event_del(&c->ev); 497 close(fd); 498 free(c); 499} 500 501void 502child_wr(int fd, short events, void *arg) 503{ 504 struct ident_client *c = SIMPLEQ_FIRST(&sc.child.pushing); 505 ssize_t n; 506 507 n = write(fd, &c->uid, sizeof(c->uid)); 508 switch (n) { 509 case -1: 510 if (errno == EAGAIN) { 511 event_add(&proc_wr, NULL); 512 return; 513 } 514 lerr(1, "child write"); 515 case sizeof(c->uid): 516 break; 517 default: 518 lerrx(1, "unexpected child write length %zd", n); 519 } 520 521 SIMPLEQ_REMOVE_HEAD(&sc.child.pushing, entry); 522 SIMPLEQ_INSERT_TAIL(&sc.child.popping, c, entry); 523 524 if (!SIMPLEQ_EMPTY(&sc.child.pushing)) 525 event_add(&proc_wr, NULL); 526} 527 528void 529identd_listen(const char *addr, const char *port, int family) 530{ 531 struct identd_listener *l = NULL; 532 533 struct addrinfo hints, *res, *res0; 534 int error; 535 int s; 536 int serrno; 537 const char *cause = NULL; 538 539 memset(&hints, 0, sizeof(hints)); 540 hints.ai_family = family; 541 hints.ai_socktype = SOCK_STREAM; 542 hints.ai_flags = AI_PASSIVE; 543 544 error = getaddrinfo(addr, port, &hints, &res0); 545 if (error) 546 lerrx(1, "%s/%s: %s", addr, port, gai_strerror(error)); 547 548 for (res = res0; res != NULL; res = res->ai_next) { 549 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 550 if (s == -1) { 551 cause = "socket"; 552 continue; 553 } 554 555 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 556 &on, sizeof(on)) == -1) 557 err(1, "listener setsockopt(SO_REUSEADDR)"); 558 559 if (bind(s, res->ai_addr, res->ai_addrlen) == -1) { 560 cause = "bind"; 561 serrno = errno; 562 close(s); 563 errno = serrno; 564 continue; 565 } 566 567 if (ioctl(s, FIONBIO, &on) == -1) 568 err(1, "listener ioctl(FIONBIO)"); 569 570 if (listen(s, 5) == -1) 571 err(1, "listen"); 572 573 l = calloc(1, sizeof(*l)); 574 if (l == NULL) 575 err(1, "listener ev alloc"); 576 577 event_set(&l->ev, s, EV_READ | EV_PERSIST, identd_accept, l); 578 event_add(&l->ev, NULL); 579 evtimer_set(&l->pause, identd_paused, l); 580 } 581 if (l == NULL) 582 err(1, "%s", cause); 583 584 freeaddrinfo(res0); 585} 586 587void 588identd_paused(int fd, short events, void *arg) 589{ 590 struct identd_listener *l = arg; 591 event_add(&l->ev, NULL); 592} 593 594void 595identd_accept(int fd, short events, void *arg) 596{ 597 struct identd_listener *l = arg; 598 struct sockaddr_storage ss; 599 struct timeval pause = { 1, 0 }; 600 struct ident_client *c = NULL; 601 socklen_t len; 602 int s; 603 604 len = sizeof(ss); 605 s = accept(fd, sa(&ss), &len); 606 if (s == -1) { 607 switch (errno) { 608 case EINTR: 609 case EWOULDBLOCK: 610 case ECONNABORTED: 611 return; 612 case EMFILE: 613 case ENFILE: 614 event_del(&l->ev); 615 evtimer_add(&l->pause, &pause); 616 return; 617 618 default: 619 lerr(1, "accept"); 620 } 621 } 622 623 if (ioctl(s, FIONBIO, &on) == -1) 624 lerr(1, "client ioctl(FIONBIO)"); 625 626 c = calloc(1, sizeof(*c)); 627 if (c == NULL) { 628 lwarn("client alloc"); 629 close(fd); 630 return; 631 } 632 633 memcpy(&c->client.ss, &ss, len); 634 c->client.len = len; 635 ldebug("client: %s", gethost(&ss)); 636 637 /* lookup the local ip it connected to */ 638 c->server.len = sizeof(c->server.ss); 639 if (getsockname(s, sa(&c->server.ss), &c->server.len) == -1) 640 lerr(1, "getsockname"); 641 642 event_set(&c->ev, s, EV_READ | EV_PERSIST, identd_request, c); 643 event_add(&c->ev, &timeout); 644} 645 646void 647identd_request(int fd, short events, void *arg) 648{ 649 struct ident_client *c = arg; 650 char buf[64]; 651 ssize_t n, i; 652 char *errstr = "INVALID-PORT"; 653 654 if (events & EV_TIMEOUT) { 655 ldebug("%s request timeout", gethost(&c->client.ss)); 656 goto fail; 657 } 658 659 n = read(fd, buf, sizeof(buf)); 660 switch (n) { 661 case -1: 662 switch (errno) { 663 case EINTR: 664 case EAGAIN: 665 return; 666 default: 667 lwarn("%s read", gethost(&c->client.ss)); 668 goto fail; 669 } 670 break; 671 672 case 0: 673 ldebug("%s closed connection", gethost(&c->client.ss)); 674 goto fail; 675 default: 676 break; 677 } 678 679 for (i = 0; c->state < S_EOL && i < n; i++) 680 c->state = identd_parse(c, buf[i]); 681 682 if (c->state == S_DEAD) 683 goto error; 684 if (c->state != S_EOL) 685 return; 686 687 if (c->server.port < 1 || c->client.port < 1) 688 goto error; 689 690 event_del(&c->ev); 691 692 if (fetchuid(c) == -1) { 693 errstr = "NO-USER"; 694 goto error; 695 } 696 697 SIMPLEQ_INSERT_TAIL(&sc.child.pushing, c, entry); 698 699 event_set(&c->ev, fd, EV_READ | EV_PERSIST, identd_resolving, c); 700 event_add(&c->ev, &timeout); 701 702 event_add(&proc_wr, NULL); 703 return; 704 705error: 706 n = asprintf(&c->buf, "%u , %u : ERROR : %s\r\n", 707 c->server.port, c->client.port, errstr); 708 if (n == -1) 709 goto fail; 710 711 c->buflen = n; 712 713 event_del(&c->ev); 714 715 event_set(&c->ev, fd, EV_READ | EV_WRITE | EV_PERSIST, 716 identd_response, c); 717 event_add(&c->ev, &timeout); 718 return; 719 720fail: 721 event_del(&c->ev); 722 close(fd); 723 free(c); 724} 725 726void 727identd_resolving(int fd, short events, void *arg) 728{ 729 struct ident_client *c = arg; 730 char buf[64]; 731 ssize_t n; 732 733 /* 734 * something happened while we're waiting for the parent to lookup 735 * the user. 736 */ 737 738 if (events & EV_TIMEOUT) { 739 ldebug("%s timeout during resolving", 740 gethost(&c->client.ss)); 741 goto error; 742 } 743 744 n = read(fd, buf, sizeof(buf)); 745 switch (n) { 746 case -1: 747 switch (errno) { 748 case EINTR: 749 case EAGAIN: 750 return; 751 default: 752 lerrx(1, "resolving read"); 753 } 754 break; 755 case 0: 756 ldebug("%s closed connection during resolving", 757 gethost(&c->client.ss)); 758 goto fail; 759 default: 760 break; 761 } 762 763 /* throw it away */ 764 return; 765 766error: 767 n = asprintf(&c->buf, "%u , %u : ERROR : UNKNOWN-ERROR\r\n", 768 c->server.port, c->client.port); 769 if (n == -1) 770 goto fail; 771 772 c->buflen = n; 773 774 event_del(&c->ev); 775 event_set(&c->ev, fd, EV_READ | EV_WRITE | EV_PERSIST, 776 identd_response, c); 777 event_add(&c->ev, &timeout); 778 c->state = S_DYING; 779 return; 780 781fail: 782 event_del(&c->ev); 783 close(fd); 784 c->state = S_DEAD; 785} 786 787enum ident_client_state 788identd_parse(struct ident_client *c, int ch) 789{ 790 enum ident_client_state s = c->state; 791 792 switch (s) { 793 case S_BEGINNING: 794 /* ignore leading space */ 795 if (ch == '\t' || ch == ' ') 796 return (s); 797 798 if (ch == '0' || !isdigit(ch)) 799 return (S_DEAD); 800 801 c->server.port = ch - '0'; 802 return (S_SERVER_PORT); 803 804 case S_SERVER_PORT: 805 if (ch == '\t' || ch == ' ') 806 return (S_PRE_COMMA); 807 if (ch == ',') 808 return (S_POST_COMMA); 809 810 if (!isdigit(ch)) 811 return (S_DEAD); 812 813 c->server.port *= 10; 814 c->server.port += ch - '0'; 815 if (c->server.port > 65535) 816 return (S_DEAD); 817 818 return (s); 819 820 case S_PRE_COMMA: 821 if (ch == '\t' || ch == ' ') 822 return (s); 823 if (ch == ',') 824 return (S_POST_COMMA); 825 826 return (S_DEAD); 827 828 case S_POST_COMMA: 829 if (ch == '\t' || ch == ' ') 830 return (s); 831 832 if (ch == '0' || !isdigit(ch)) 833 return (S_DEAD); 834 835 c->client.port = ch - '0'; 836 return (S_CLIENT_PORT); 837 838 case S_CLIENT_PORT: 839 if (ch == '\t' || ch == ' ') 840 return (S_PRE_EOL); 841 if (ch == '\r' || ch == '\n') 842 return (S_EOL); 843 844 if (!isdigit(ch)) 845 return (S_DEAD); 846 847 c->client.port *= 10; 848 c->client.port += ch - '0'; 849 if (c->client.port > 65535) 850 return (S_DEAD); 851 852 return (s); 853 854 case S_PRE_EOL: 855 if (ch == '\t' || ch == ' ') 856 return (s); 857 if (ch == '\r' || ch == '\n') 858 return (S_EOL); 859 860 return (S_DEAD); 861 862 case S_EOL: 863 /* ignore trailing garbage */ 864 return (s); 865 866 default: 867 return (S_DEAD); 868 } 869} 870 871void 872identd_response(int fd, short events, void *arg) 873{ 874 struct ident_client *c = arg; 875 char buf[64]; 876 ssize_t n; 877 878 if (events & EV_TIMEOUT) { 879 ldebug("%s timeout during response", gethost(&c->client.ss)); 880 goto done; 881 } 882 883 if (events & EV_READ) { 884 n = read(fd, buf, sizeof(buf)); 885 switch (n) { 886 case -1: 887 switch (errno) { 888 case EINTR: 889 case EAGAIN: 890 /* meh, try a write */ 891 break; 892 default: 893 lerrx(1, "response read"); 894 } 895 break; 896 case 0: 897 ldebug("%s closed connection during response", 898 gethost(&c->client.ss)); 899 goto done; 900 default: 901 /* flushed socket */ 902 break; 903 } 904 } 905 906 if (!(events & EV_WRITE)) 907 return; /* try again later */ 908 909 n = write(fd, c->buf + c->bufoff, c->buflen - c->bufoff); 910 if (n == -1) { 911 switch (errno) { 912 case EAGAIN: 913 return; /* try again later */ 914 default: 915 goto done; 916 } 917 } 918 919 c->bufoff += n; 920 if (c->bufoff != c->buflen) 921 return; /* try again later */ 922 923done: 924 event_del(&c->ev); 925 close(fd); 926 free(c->buf); 927 if (c->state == S_DYING) /* it was queued for resolving */ 928 c->state = S_DEAD; 929 else 930 free(c); 931} 932 933void 934syslog_vstrerror(int e, int priority, const char *fmt, va_list ap) 935{ 936 char *s; 937 938 if (vasprintf(&s, fmt, ap) == -1) { 939 syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror"); 940 exit(1); 941 } 942 943 syslog(priority, "%s: %s", s, strerror(e)); 944 945 free(s); 946} 947 948void 949syslog_err(int ecode, const char *fmt, ...) 950{ 951 va_list ap; 952 953 va_start(ap, fmt); 954 syslog_vstrerror(errno, LOG_EMERG, fmt, ap); 955 va_end(ap); 956 957 exit(ecode); 958} 959 960void 961syslog_errx(int ecode, const char *fmt, ...) 962{ 963 va_list ap; 964 965 va_start(ap, fmt); 966 vsyslog(LOG_WARNING, fmt, ap); 967 va_end(ap); 968 969 exit(ecode); 970} 971 972void 973syslog_warn(const char *fmt, ...) 974{ 975 va_list ap; 976 977 va_start(ap, fmt); 978 syslog_vstrerror(errno, LOG_WARNING, fmt, ap); 979 va_end(ap); 980} 981 982void 983syslog_warnx(const char *fmt, ...) 984{ 985 va_list ap; 986 987 va_start(ap, fmt); 988 vsyslog(LOG_WARNING, fmt, ap); 989 va_end(ap); 990} 991 992void 993syslog_info(const char *fmt, ...) 994{ 995 va_list ap; 996 997 va_start(ap, fmt); 998 vsyslog(LOG_INFO, fmt, ap); 999 va_end(ap); 1000} 1001 1002void 1003syslog_debug(const char *fmt, ...) 1004{ 1005 va_list ap; 1006 1007 if (!debug) 1008 return; 1009 1010 va_start(ap, fmt); 1011 vsyslog(LOG_DEBUG, fmt, ap); 1012 va_end(ap); 1013} 1014 1015const char * 1016gethost(struct sockaddr_storage *ss) 1017{ 1018 struct sockaddr *sa = (struct sockaddr *)ss; 1019 static char buf[NI_MAXHOST]; 1020 1021 if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), 1022 NULL, 0, NI_NUMERICHOST) != 0) 1023 return ("(unknown)"); 1024 1025 return (buf); 1026} 1027 1028const char * 1029getport(struct sockaddr_storage *ss) 1030{ 1031 struct sockaddr *sa = (struct sockaddr *)ss; 1032 static char buf[NI_MAXSERV]; 1033 1034 if (getnameinfo(sa, sa->sa_len, NULL, 0, buf, sizeof(buf), 1035 NI_NUMERICSERV) != 0) 1036 return ("(unknown)"); 1037 1038 return (buf); 1039} 1040 1041int 1042fetchuid(struct ident_client *c) 1043{ 1044 struct tcp_ident_mapping tir; 1045 int mib[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_IDENT }; 1046 struct sockaddr_in *s4; 1047 struct sockaddr_in6 *s6; 1048 int err = 0; 1049 size_t len; 1050 1051 memset(&tir, 0, sizeof(tir)); 1052 memcpy(&tir.faddr, &c->client.ss, sizeof(&tir.faddr)); 1053 memcpy(&tir.laddr, &c->server.ss, sizeof(&tir.laddr)); 1054 1055 switch (c->server.ss.ss_family) { 1056 case AF_INET: 1057 s4 = (struct sockaddr_in *)&tir.faddr; 1058 s4->sin_port = htons(c->client.port); 1059 1060 s4 = (struct sockaddr_in *)&tir.laddr; 1061 s4->sin_port = htons(c->server.port); 1062 break; 1063 case AF_INET6: 1064 s6 = (struct sockaddr_in6 *)&tir.faddr; 1065 s6->sin6_port = htons(c->client.port); 1066 1067 s6 = (struct sockaddr_in6 *)&tir.laddr; 1068 s6->sin6_port = htons(c->server.port); 1069 break; 1070 default: 1071 lerrx(1, "unexpected family %d", c->server.ss.ss_family); 1072 } 1073 1074 len = sizeof(tir); 1075 err = sysctl(mib, sizeof(mib) / sizeof(mib[0]), &tir, &len, NULL, 0); 1076 if (err == -1) 1077 lerr(1, "sysctl"); 1078 1079 if (tir.ruid == -1) 1080 return (-1); 1081 1082 c->uid = tir.ruid; 1083 return (0); 1084} 1085