1 /* $NetBSD: getent.c,v 1.16 2009/04/12 10:27:08 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 2004-2006 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __RCSID("$NetBSD: getent.c,v 1.16 2009/04/12 10:27:08 lukem Exp $"); 35 #endif /* not lint */ 36 37 #include <sys/socket.h> 38 39 #include <assert.h> 40 #include <ctype.h> 41 #include <errno.h> 42 #include <grp.h> 43 #include <limits.h> 44 #include <netdb.h> 45 #include <netgroup.h> 46 #include <pwd.h> 47 #include <stdio.h> 48 #include <stdarg.h> 49 #include <stdbool.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <paths.h> 54 #include <err.h> 55 56 #include <arpa/inet.h> 57 #include <arpa/nameser.h> 58 59 #include <net/if.h> 60 #include <net/if_ether.h> 61 62 #include <netinet/in.h> /* for INET6_ADDRSTRLEN */ 63 64 #include <rpc/rpcent.h> 65 66 #include <disktab.h> 67 68 static int usage(void) __attribute__((__noreturn__)); 69 static int parsenum(const char *, unsigned long *); 70 static int disktab(int, char *[]); 71 static int gettytab(int, char *[]); 72 static int ethers(int, char *[]); 73 static int group(int, char *[]); 74 static int hosts(int, char *[]); 75 static int netgroup(int, char *[]); 76 static int networks(int, char *[]); 77 static int passwd(int, char *[]); 78 static int printcap(int, char *[]); 79 static int protocols(int, char *[]); 80 static int rpc(int, char *[]); 81 static int services(int, char *[]); 82 static int shells(int, char *[]); 83 static int termcap(int, char *[]); 84 85 enum { 86 RV_OK = 0, 87 RV_USAGE = 1, 88 RV_NOTFOUND = 2, 89 RV_NOENUM = 3 90 }; 91 92 static struct getentdb { 93 const char *name; 94 int (*callback)(int, char *[]); 95 } databases[] = { 96 { "disktab", disktab, }, 97 { "ethers", ethers, }, 98 { "gettytab", gettytab, }, 99 { "group", group, }, 100 { "hosts", hosts, }, 101 { "netgroup", netgroup, }, 102 { "networks", networks, }, 103 { "passwd", passwd, }, 104 { "printcap", printcap, }, 105 { "protocols", protocols, }, 106 { "rpc", rpc, }, 107 { "services", services, }, 108 { "shells", shells, }, 109 { "termcap", termcap, }, 110 111 { NULL, NULL, }, 112 }; 113 114 115 int 116 main(int argc, char *argv[]) 117 { 118 struct getentdb *curdb; 119 120 setprogname(argv[0]); 121 122 if (argc < 2) 123 usage(); 124 for (curdb = databases; curdb->name != NULL; curdb++) 125 if (strcmp(curdb->name, argv[1]) == 0) 126 return (*curdb->callback)(argc, argv); 127 128 warn("Unknown database `%s'", argv[1]); 129 usage(); 130 /* NOTREACHED */ 131 } 132 133 static int 134 usage(void) 135 { 136 struct getentdb *curdb; 137 138 (void)fprintf(stderr, "Usage: %s database [key ...]\n", 139 getprogname()); 140 (void)fprintf(stderr, " database may be one of:\n\t"); 141 for (curdb = databases; curdb->name != NULL; curdb++) 142 (void)fprintf(stderr, " %s", curdb->name); 143 (void)fprintf(stderr, "\n"); 144 exit(RV_USAGE); 145 /* NOTREACHED */ 146 } 147 148 static int 149 parsenum(const char *word, unsigned long *result) 150 { 151 unsigned long num; 152 char *ep; 153 154 assert(word != NULL); 155 assert(result != NULL); 156 157 if (!isdigit((unsigned char)word[0])) 158 return 0; 159 errno = 0; 160 num = strtoul(word, &ep, 10); 161 if (num == ULONG_MAX && errno == ERANGE) 162 return 0; 163 if (*ep != '\0') 164 return 0; 165 *result = num; 166 return 1; 167 } 168 169 /* 170 * printfmtstrings -- 171 * vprintf(format, ...), 172 * then the aliases (beginning with prefix, separated by sep), 173 * then a newline 174 */ 175 static void 176 printfmtstrings(char *strings[], const char *prefix, const char *sep, 177 const char *fmt, ...) 178 { 179 va_list ap; 180 const char *curpref; 181 size_t i; 182 183 va_start(ap, fmt); 184 (void)vprintf(fmt, ap); 185 va_end(ap); 186 187 curpref = prefix; 188 for (i = 0; strings[i] != NULL; i++) { 189 (void)printf("%s%s", curpref, strings[i]); 190 curpref = sep; 191 } 192 (void)printf("\n"); 193 } 194 195 196 /* 197 * ethers 198 */ 199 200 static int 201 ethers(int argc, char *argv[]) 202 { 203 char hostname[MAXHOSTNAMELEN + 1], *hp; 204 struct ether_addr ea, *eap; 205 int i, rv; 206 207 assert(argc > 1); 208 assert(argv != NULL); 209 210 #define ETHERSPRINT (void)printf("%-17s %s\n", ether_ntoa(eap), hp) 211 212 rv = RV_OK; 213 if (argc == 2) { 214 warnx("Enumeration not supported on ethers"); 215 rv = RV_NOENUM; 216 } else { 217 for (i = 2; i < argc; i++) { 218 if ((eap = ether_aton(argv[i])) == NULL) { 219 eap = &ea; 220 hp = argv[i]; 221 if (ether_hostton(hp, eap) != 0) { 222 rv = RV_NOTFOUND; 223 break; 224 } 225 } else { 226 hp = hostname; 227 if (ether_ntohost(hp, eap) != 0) { 228 rv = RV_NOTFOUND; 229 break; 230 } 231 } 232 ETHERSPRINT; 233 } 234 } 235 return rv; 236 } 237 238 /* 239 * group 240 */ 241 242 static int 243 group(int argc, char *argv[]) 244 { 245 struct group *gr; 246 unsigned long id; 247 int i, rv; 248 249 assert(argc > 1); 250 assert(argv != NULL); 251 252 #define GROUPPRINT printfmtstrings(gr->gr_mem, ":", ",", "%s:%s:%u", \ 253 gr->gr_name, gr->gr_passwd, gr->gr_gid) 254 255 (void)setgroupent(1); 256 rv = RV_OK; 257 if (argc == 2) { 258 while ((gr = getgrent()) != NULL) 259 GROUPPRINT; 260 } else { 261 for (i = 2; i < argc; i++) { 262 if (parsenum(argv[i], &id)) 263 gr = getgrgid((gid_t)id); 264 else 265 gr = getgrnam(argv[i]); 266 if (gr != NULL) 267 GROUPPRINT; 268 else { 269 rv = RV_NOTFOUND; 270 break; 271 } 272 } 273 } 274 endgrent(); 275 return rv; 276 } 277 278 279 /* 280 * hosts 281 */ 282 283 static void 284 hostsprint(const struct hostent *he) 285 { 286 char buf[INET6_ADDRSTRLEN]; 287 288 assert(he != NULL); 289 if (inet_ntop(he->h_addrtype, he->h_addr, buf, sizeof(buf)) == NULL) 290 (void)strlcpy(buf, "# unknown", sizeof(buf)); 291 printfmtstrings(he->h_aliases, " ", " ", "%-16s %s", buf, he->h_name); 292 } 293 294 static int 295 hosts(int argc, char *argv[]) 296 { 297 struct hostent *he; 298 char addr[IN6ADDRSZ]; 299 int i, rv; 300 301 assert(argc > 1); 302 assert(argv != NULL); 303 304 sethostent(1); 305 rv = RV_OK; 306 if (argc == 2) { 307 while ((he = gethostent()) != NULL) 308 hostsprint(he); 309 } else { 310 for (i = 2; i < argc; i++) { 311 if (inet_pton(AF_INET6, argv[i], (void *)addr) > 0) 312 he = gethostbyaddr(addr, IN6ADDRSZ, AF_INET6); 313 else if (inet_pton(AF_INET, argv[i], (void *)addr) > 0) 314 he = gethostbyaddr(addr, INADDRSZ, AF_INET); 315 else 316 he = gethostbyname(argv[i]); 317 if (he != NULL) 318 hostsprint(he); 319 else { 320 rv = RV_NOTFOUND; 321 break; 322 } 323 } 324 } 325 endhostent(); 326 return rv; 327 } 328 329 /* 330 * netgroup 331 */ 332 static int 333 netgroup(int argc, char *argv[]) 334 { 335 int rv, i; 336 bool first; 337 const char *host, *user, *domain; 338 339 assert(argc > 1); 340 assert(argv != NULL); 341 342 #define NETGROUPPRINT(s) (((s) != NULL) ? (s) : "") 343 344 rv = RV_OK; 345 if (argc == 2) { 346 warnx("Enumeration not supported on netgroup"); 347 rv = RV_NOENUM; 348 } else { 349 for (i = 2; i < argc; i++) { 350 setnetgrent(argv[i]); 351 first = true; 352 while (getnetgrent(&host, &user, &domain) != 0) { 353 if (first) { 354 first = false; 355 (void)fputs(argv[i], stdout); 356 } 357 (void)printf(" (%s,%s,%s)", 358 NETGROUPPRINT(host), 359 NETGROUPPRINT(user), 360 NETGROUPPRINT(domain)); 361 } 362 if (!first) 363 (void)putchar('\n'); 364 endnetgrent(); 365 } 366 } 367 368 return rv; 369 } 370 371 /* 372 * networks 373 */ 374 375 static void 376 networksprint(const struct netent *ne) 377 { 378 char buf[INET6_ADDRSTRLEN]; 379 struct in_addr ianet; 380 381 assert(ne != NULL); 382 ianet = inet_makeaddr(ne->n_net, 0); 383 if (inet_ntop(ne->n_addrtype, &ianet, buf, sizeof(buf)) == NULL) 384 (void)strlcpy(buf, "# unknown", sizeof(buf)); 385 printfmtstrings(ne->n_aliases, " ", " ", "%-16s %s", ne->n_name, buf); 386 } 387 388 static int 389 networks(int argc, char *argv[]) 390 { 391 struct netent *ne; 392 in_addr_t net; 393 int i, rv; 394 395 assert(argc > 1); 396 assert(argv != NULL); 397 398 setnetent(1); 399 rv = RV_OK; 400 if (argc == 2) { 401 while ((ne = getnetent()) != NULL) 402 networksprint(ne); 403 } else { 404 for (i = 2; i < argc; i++) { 405 net = inet_network(argv[i]); 406 if (net != INADDR_NONE) 407 ne = getnetbyaddr(net, AF_INET); 408 else 409 ne = getnetbyname(argv[i]); 410 if (ne != NULL) 411 networksprint(ne); 412 else { 413 rv = RV_NOTFOUND; 414 break; 415 } 416 } 417 } 418 endnetent(); 419 return rv; 420 } 421 422 423 /* 424 * passwd 425 */ 426 427 static int 428 passwd(int argc, char *argv[]) 429 { 430 struct passwd *pw; 431 unsigned long id; 432 int i, rv; 433 434 assert(argc > 1); 435 assert(argv != NULL); 436 437 #define PASSWDPRINT (void)printf("%s:%s:%u:%u:%s:%s:%s\n", \ 438 pw->pw_name, pw->pw_passwd, pw->pw_uid, \ 439 pw->pw_gid, pw->pw_gecos, pw->pw_dir, pw->pw_shell) 440 441 (void)setpassent(1); 442 rv = RV_OK; 443 if (argc == 2) { 444 while ((pw = getpwent()) != NULL) 445 PASSWDPRINT; 446 } else { 447 for (i = 2; i < argc; i++) { 448 if (parsenum(argv[i], &id)) 449 pw = getpwuid((uid_t)id); 450 else 451 pw = getpwnam(argv[i]); 452 if (pw != NULL) 453 PASSWDPRINT; 454 else { 455 rv = RV_NOTFOUND; 456 break; 457 } 458 } 459 } 460 endpwent(); 461 return rv; 462 } 463 464 static char * 465 mygetent(const char * const * db_array, const char *name) 466 { 467 char *buf = NULL; 468 int error; 469 470 switch (error = cgetent(&buf, db_array, name)) { 471 case -3: 472 warnx("tc= loop in record `%s' in `%s'", name, db_array[0]); 473 break; 474 case -2: 475 warn("system error fetching record `%s' in `%s'", name, 476 db_array[0]); 477 break; 478 case -1: 479 case 0: 480 break; 481 case 1: 482 warnx("tc= reference not found in record for `%s' in `%s'", 483 name, db_array[0]); 484 break; 485 default: 486 warnx("unknown error %d in record `%s' in `%s'", error, name, 487 db_array[0]); 488 break; 489 } 490 return buf; 491 } 492 493 static char * 494 mygetone(const char * const * db_array, int first) 495 { 496 char *buf = NULL; 497 int error; 498 499 switch (error = (first ? cgetfirst : cgetnext)(&buf, db_array)) { 500 case -2: 501 warnx("tc= loop in `%s'", db_array[0]); 502 break; 503 case -1: 504 warn("system error fetching record in `%s'", db_array[0]); 505 break; 506 case 0: 507 case 1: 508 break; 509 case 2: 510 warnx("tc= reference not found in `%s'", db_array[0]); 511 break; 512 default: 513 warnx("unknown error %d in `%s'", error, db_array[0]); 514 break; 515 } 516 return buf; 517 } 518 519 static void 520 capprint(const char *cap) 521 { 522 char *c = strchr(cap, ':'); 523 if (c) 524 if (c == cap) 525 (void)printf("true\n"); 526 else { 527 int l = (int)(c - cap); 528 (void)printf("%*.*s\n", l, l, cap); 529 } 530 else 531 (void)printf("%s\n", cap); 532 } 533 534 static void 535 prettyprint(char *b) 536 { 537 #define TERMWIDTH 65 538 int did = 0; 539 size_t len; 540 char *s, c; 541 542 for (;;) { 543 len = strlen(b); 544 if (len <= TERMWIDTH) { 545 done: 546 if (did) 547 printf("\t:"); 548 printf("%s\n", b); 549 return; 550 } 551 for (s = b + TERMWIDTH; s > b && *s != ':'; s--) 552 continue; 553 if (*s++ != ':') 554 goto done; 555 c = *s; 556 *s = '\0'; 557 if (did) 558 printf("\t:"); 559 did++; 560 printf("%s\\\n", b); 561 *s = c; 562 b = s; 563 } 564 } 565 566 static void 567 handleone(const char * const *db_array, char *b, int recurse, int pretty, 568 int level) 569 { 570 char *tc; 571 572 if (level && pretty) 573 printf("\n"); 574 if (pretty) 575 prettyprint(b); 576 else 577 printf("%s\n", b); 578 if (!recurse || cgetstr(b, "tc", &tc) <= 0) 579 return; 580 581 b = mygetent(db_array, tc); 582 free(tc); 583 584 if (b == NULL) 585 return; 586 587 handleone(db_array, b, recurse, pretty, ++level); 588 free(b); 589 } 590 591 static int 592 handlecap(const char *db, int argc, char *argv[]) 593 { 594 static const char sfx[] = "=#:"; 595 const char *db_array[] = { db, NULL }; 596 char *b, *cap; 597 int i, rv, c; 598 size_t j; 599 int expand = 1, recurse = 0, pretty = 0; 600 601 assert(argc > 1); 602 assert(argv != NULL); 603 604 argc--; 605 argv++; 606 while ((c = getopt(argc, argv, "pnr")) != -1) 607 switch (c) { 608 case 'n': 609 expand = 0; 610 break; 611 case 'r': 612 expand = 0; 613 recurse = 1; 614 break; 615 case 'p': 616 pretty = 1; 617 break; 618 default: 619 usage(); 620 break; 621 } 622 623 argc -= optind; 624 argv += optind; 625 csetexpandtc(expand); 626 rv = RV_OK; 627 if (argc == 0) { 628 for (b = mygetone(db_array, 1); b; b = mygetone(db_array, 0)) { 629 handleone(db_array, b, recurse, pretty, 0); 630 free(b); 631 } 632 } else { 633 if ((b = mygetent(db_array, argv[0])) == NULL) 634 return RV_NOTFOUND; 635 if (argc == 1) 636 handleone(db_array, b, recurse, pretty, 0); 637 else { 638 for (i = 2; i < argc; i++) { 639 for (j = 0; j < sizeof(sfx) - 1; j++) { 640 cap = cgetcap(b, argv[i], sfx[j]); 641 if (cap) { 642 capprint(cap); 643 break; 644 } 645 } 646 if (j == sizeof(sfx) - 1) 647 printf("false\n"); 648 } 649 } 650 free(b); 651 } 652 return rv; 653 } 654 655 /* 656 * gettytab 657 */ 658 659 static int 660 gettytab(int argc, char *argv[]) 661 { 662 return handlecap(_PATH_GETTYTAB, argc, argv); 663 } 664 665 /* 666 * printcap 667 */ 668 669 static int 670 printcap(int argc, char *argv[]) 671 { 672 return handlecap(_PATH_PRINTCAP, argc, argv); 673 } 674 675 /* 676 * disktab 677 */ 678 679 static int 680 disktab(int argc, char *argv[]) 681 { 682 return handlecap(_PATH_DISKTAB, argc, argv); 683 } 684 685 /* 686 * termcap 687 */ 688 689 static int 690 termcap(int argc, char *argv[]) 691 { 692 return handlecap(_PATH_TERMCAP, argc, argv); 693 } 694 /* 695 * protocols 696 */ 697 698 static int 699 protocols(int argc, char *argv[]) 700 { 701 struct protoent *pe; 702 unsigned long id; 703 int i, rv; 704 705 assert(argc > 1); 706 assert(argv != NULL); 707 708 #define PROTOCOLSPRINT printfmtstrings(pe->p_aliases, " ", " ", \ 709 "%-16s %5d", pe->p_name, pe->p_proto) 710 711 setprotoent(1); 712 rv = RV_OK; 713 if (argc == 2) { 714 while ((pe = getprotoent()) != NULL) 715 PROTOCOLSPRINT; 716 } else { 717 for (i = 2; i < argc; i++) { 718 if (parsenum(argv[i], &id)) 719 pe = getprotobynumber((int)id); 720 else 721 pe = getprotobyname(argv[i]); 722 if (pe != NULL) 723 PROTOCOLSPRINT; 724 else { 725 rv = RV_NOTFOUND; 726 break; 727 } 728 } 729 } 730 endprotoent(); 731 return rv; 732 } 733 734 /* 735 * rpc 736 */ 737 738 static int 739 rpc(int argc, char *argv[]) 740 { 741 struct rpcent *re; 742 unsigned long id; 743 int i, rv; 744 745 assert(argc > 1); 746 assert(argv != NULL); 747 748 #define RPCPRINT printfmtstrings(re->r_aliases, " ", " ", \ 749 "%-16s %6d", \ 750 re->r_name, re->r_number) 751 752 setrpcent(1); 753 rv = RV_OK; 754 if (argc == 2) { 755 while ((re = getrpcent()) != NULL) 756 RPCPRINT; 757 } else { 758 for (i = 2; i < argc; i++) { 759 if (parsenum(argv[i], &id)) 760 re = getrpcbynumber((int)id); 761 else 762 re = getrpcbyname(argv[i]); 763 if (re != NULL) 764 RPCPRINT; 765 else { 766 rv = RV_NOTFOUND; 767 break; 768 } 769 } 770 } 771 endrpcent(); 772 return rv; 773 } 774 775 /* 776 * services 777 */ 778 779 static int 780 services(int argc, char *argv[]) 781 { 782 struct servent *se; 783 unsigned long id; 784 char *proto; 785 int i, rv; 786 787 assert(argc > 1); 788 assert(argv != NULL); 789 790 #define SERVICESPRINT printfmtstrings(se->s_aliases, " ", " ", \ 791 "%-16s %5d/%s", \ 792 se->s_name, ntohs(se->s_port), se->s_proto) 793 794 setservent(1); 795 rv = RV_OK; 796 if (argc == 2) { 797 while ((se = getservent()) != NULL) 798 SERVICESPRINT; 799 } else { 800 for (i = 2; i < argc; i++) { 801 proto = strchr(argv[i], '/'); 802 if (proto != NULL) 803 *proto++ = '\0'; 804 if (parsenum(argv[i], &id)) 805 se = getservbyport(htons(id), proto); 806 else 807 se = getservbyname(argv[i], proto); 808 if (se != NULL) 809 SERVICESPRINT; 810 else { 811 rv = RV_NOTFOUND; 812 break; 813 } 814 } 815 } 816 endservent(); 817 return rv; 818 } 819 820 821 /* 822 * shells 823 */ 824 825 static int 826 shells(int argc, char *argv[]) 827 { 828 const char *sh; 829 int i, rv; 830 831 assert(argc > 1); 832 assert(argv != NULL); 833 834 #define SHELLSPRINT (void)printf("%s\n", sh) 835 836 setusershell(); 837 rv = RV_OK; 838 if (argc == 2) { 839 while ((sh = getusershell()) != NULL) 840 SHELLSPRINT; 841 } else { 842 for (i = 2; i < argc; i++) { 843 setusershell(); 844 while ((sh = getusershell()) != NULL) { 845 if (strcmp(sh, argv[i]) == 0) { 846 SHELLSPRINT; 847 break; 848 } 849 } 850 if (sh == NULL) { 851 rv = RV_NOTFOUND; 852 break; 853 } 854 } 855 } 856 endusershell(); 857 return rv; 858 } 859