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