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