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