1 /* $NetBSD: getent.c,v 1.19 2012/03/15 02:02:23 joerg 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.19 2012/03/15 02:02:23 joerg 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 #if !defined(__minix) 65 #include <rpc/rpcent.h> 66 #endif /* !defined(__minix) */ 67 68 #include <disktab.h> 69 70 static int usage(void) __attribute__((__noreturn__)); 71 static int parsenum(const char *, unsigned long *); 72 static int disktab(int, char *[]); 73 static int gettytab(int, char *[]); 74 static int ethers(int, char *[]); 75 static int group(int, char *[]); 76 static int hosts(int, char *[]); 77 static int netgroup(int, char *[]); 78 static int networks(int, char *[]); 79 static int passwd(int, char *[]); 80 static int printcap(int, char *[]); 81 static int protocols(int, char *[]); 82 #if !defined(__minix) 83 static int rpc(int, char *[]); 84 #endif /* !defined(__minix) */ 85 static int services(int, char *[]); 86 static int shells(int, char *[]); 87 88 enum { 89 RV_OK = 0, 90 RV_USAGE = 1, 91 RV_NOTFOUND = 2, 92 RV_NOENUM = 3 93 }; 94 95 static struct getentdb { 96 const char *name; 97 int (*callback)(int, char *[]); 98 } databases[] = { 99 { "disktab", disktab, }, 100 { "ethers", ethers, }, 101 { "gettytab", gettytab, }, 102 { "group", group, }, 103 { "hosts", hosts, }, 104 { "netgroup", netgroup, }, 105 { "networks", networks, }, 106 { "passwd", passwd, }, 107 { "printcap", printcap, }, 108 { "protocols", protocols, }, 109 #if !defined(__minix) 110 { "rpc", rpc, }, 111 #endif /* !defined(__minix) */ 112 { "services", services, }, 113 { "shells", shells, }, 114 115 { NULL, NULL, }, 116 }; 117 118 119 int 120 main(int argc, char *argv[]) 121 { 122 struct getentdb *curdb; 123 124 setprogname(argv[0]); 125 126 if (argc < 2) 127 usage(); 128 for (curdb = databases; curdb->name != NULL; curdb++) 129 if (strcmp(curdb->name, argv[1]) == 0) 130 return (*curdb->callback)(argc, argv); 131 132 warn("Unknown database `%s'", argv[1]); 133 usage(); 134 /* NOTREACHED */ 135 } 136 137 static int 138 usage(void) 139 { 140 struct getentdb *curdb; 141 size_t i; 142 143 (void)fprintf(stderr, "Usage: %s database [key ...]\n", 144 getprogname()); 145 (void)fprintf(stderr, "\tdatabase may be one of:"); 146 for (i = 0, curdb = databases; curdb->name != NULL; curdb++, i++) { 147 if (i % 7 == 0) 148 (void)fputs("\n\t\t", stderr); 149 (void)fprintf(stderr, "%s%s", i % 7 == 0 ? "" : " ", 150 curdb->name); 151 } 152 (void)fprintf(stderr, "\n"); 153 exit(RV_USAGE); 154 /* NOTREACHED */ 155 } 156 157 static int 158 parsenum(const char *word, unsigned long *result) 159 { 160 unsigned long num; 161 char *ep; 162 163 assert(word != NULL); 164 assert(result != NULL); 165 166 if (!isdigit((unsigned char)word[0])) 167 return 0; 168 errno = 0; 169 num = strtoul(word, &ep, 10); 170 if (num == ULONG_MAX && errno == ERANGE) 171 return 0; 172 if (*ep != '\0') 173 return 0; 174 *result = num; 175 return 1; 176 } 177 178 /* 179 * printfmtstrings -- 180 * vprintf(format, ...), 181 * then the aliases (beginning with prefix, separated by sep), 182 * then a newline 183 */ 184 static __printflike(4, 5) void 185 printfmtstrings(char *strings[], const char *prefix, const char *sep, 186 const char *fmt, ...) 187 { 188 va_list ap; 189 const char *curpref; 190 size_t i; 191 192 va_start(ap, fmt); 193 (void)vprintf(fmt, ap); 194 va_end(ap); 195 196 curpref = prefix; 197 for (i = 0; strings[i] != NULL; i++) { 198 (void)printf("%s%s", curpref, strings[i]); 199 curpref = sep; 200 } 201 (void)printf("\n"); 202 } 203 204 205 /* 206 * ethers 207 */ 208 209 static int 210 ethers(int argc, char *argv[]) 211 { 212 char hostname[MAXHOSTNAMELEN + 1], *hp; 213 struct ether_addr ea, *eap; 214 int i, rv; 215 216 assert(argc > 1); 217 assert(argv != NULL); 218 219 #define ETHERSPRINT (void)printf("%-17s %s\n", ether_ntoa(eap), hp) 220 221 rv = RV_OK; 222 if (argc == 2) { 223 warnx("Enumeration not supported on ethers"); 224 rv = RV_NOENUM; 225 } else { 226 for (i = 2; i < argc; i++) { 227 if ((eap = ether_aton(argv[i])) == NULL) { 228 eap = &ea; 229 hp = argv[i]; 230 if (ether_hostton(hp, eap) != 0) { 231 rv = RV_NOTFOUND; 232 break; 233 } 234 } else { 235 hp = hostname; 236 if (ether_ntohost(hp, eap) != 0) { 237 rv = RV_NOTFOUND; 238 break; 239 } 240 } 241 ETHERSPRINT; 242 } 243 } 244 return rv; 245 } 246 247 /* 248 * group 249 */ 250 251 static int 252 group(int argc, char *argv[]) 253 { 254 struct group *gr; 255 unsigned long id; 256 int i, rv; 257 258 assert(argc > 1); 259 assert(argv != NULL); 260 261 #define GROUPPRINT printfmtstrings(gr->gr_mem, ":", ",", "%s:%s:%u", \ 262 gr->gr_name, gr->gr_passwd, gr->gr_gid) 263 264 (void)setgroupent(1); 265 rv = RV_OK; 266 if (argc == 2) { 267 while ((gr = getgrent()) != NULL) 268 GROUPPRINT; 269 } else { 270 for (i = 2; i < argc; i++) { 271 if (parsenum(argv[i], &id)) 272 gr = getgrgid((gid_t)id); 273 else 274 gr = getgrnam(argv[i]); 275 if (gr != NULL) 276 GROUPPRINT; 277 else { 278 rv = RV_NOTFOUND; 279 break; 280 } 281 } 282 } 283 endgrent(); 284 return rv; 285 } 286 287 288 /* 289 * hosts 290 */ 291 292 static void 293 hostsprint(const struct hostent *he) 294 { 295 char buf[INET6_ADDRSTRLEN]; 296 297 assert(he != NULL); 298 if (inet_ntop(he->h_addrtype, he->h_addr, buf, sizeof(buf)) == NULL) 299 (void)strlcpy(buf, "# unknown", sizeof(buf)); 300 printfmtstrings(he->h_aliases, " ", " ", "%-16s %s", buf, he->h_name); 301 } 302 303 static int 304 hosts(int argc, char *argv[]) 305 { 306 struct hostent *he; 307 char addr[IN6ADDRSZ]; 308 int i, rv; 309 310 assert(argc > 1); 311 assert(argv != NULL); 312 313 sethostent(1); 314 rv = RV_OK; 315 if (argc == 2) { 316 while ((he = gethostent()) != NULL) 317 hostsprint(he); 318 } else { 319 for (i = 2; i < argc; i++) { 320 if (inet_pton(AF_INET6, argv[i], (void *)addr) > 0) 321 he = gethostbyaddr(addr, IN6ADDRSZ, AF_INET6); 322 else if (inet_pton(AF_INET, argv[i], (void *)addr) > 0) 323 he = gethostbyaddr(addr, INADDRSZ, AF_INET); 324 else 325 he = gethostbyname(argv[i]); 326 if (he != NULL) 327 hostsprint(he); 328 else { 329 rv = RV_NOTFOUND; 330 break; 331 } 332 } 333 } 334 endhostent(); 335 return rv; 336 } 337 338 /* 339 * netgroup 340 */ 341 static int 342 netgroup(int argc, char *argv[]) 343 { 344 int rv, i; 345 bool first; 346 const char *host, *user, *domain; 347 348 assert(argc > 1); 349 assert(argv != NULL); 350 351 #define NETGROUPPRINT(s) (((s) != NULL) ? (s) : "") 352 353 rv = RV_OK; 354 if (argc == 2) { 355 warnx("Enumeration not supported on netgroup"); 356 rv = RV_NOENUM; 357 } else { 358 for (i = 2; i < argc; i++) { 359 setnetgrent(argv[i]); 360 first = true; 361 while (getnetgrent(&host, &user, &domain) != 0) { 362 if (first) { 363 first = false; 364 (void)fputs(argv[i], stdout); 365 } 366 (void)printf(" (%s,%s,%s)", 367 NETGROUPPRINT(host), 368 NETGROUPPRINT(user), 369 NETGROUPPRINT(domain)); 370 } 371 if (!first) 372 (void)putchar('\n'); 373 endnetgrent(); 374 } 375 } 376 377 return rv; 378 } 379 380 /* 381 * networks 382 */ 383 384 static void 385 networksprint(const struct netent *ne) 386 { 387 char buf[INET6_ADDRSTRLEN]; 388 struct in_addr ianet; 389 390 assert(ne != NULL); 391 ianet = inet_makeaddr(ne->n_net, 0); 392 if (inet_ntop(ne->n_addrtype, &ianet, buf, sizeof(buf)) == NULL) 393 (void)strlcpy(buf, "# unknown", sizeof(buf)); 394 printfmtstrings(ne->n_aliases, " ", " ", "%-16s %s", ne->n_name, buf); 395 } 396 397 static int 398 networks(int argc, char *argv[]) 399 { 400 struct netent *ne; 401 in_addr_t net; 402 int i, rv; 403 404 assert(argc > 1); 405 assert(argv != NULL); 406 407 setnetent(1); 408 rv = RV_OK; 409 if (argc == 2) { 410 while ((ne = getnetent()) != NULL) 411 networksprint(ne); 412 } else { 413 for (i = 2; i < argc; i++) { 414 net = inet_network(argv[i]); 415 if (net != INADDR_NONE) 416 ne = getnetbyaddr(net, AF_INET); 417 else 418 ne = getnetbyname(argv[i]); 419 if (ne != NULL) 420 networksprint(ne); 421 else { 422 rv = RV_NOTFOUND; 423 break; 424 } 425 } 426 } 427 endnetent(); 428 return rv; 429 } 430 431 432 /* 433 * passwd 434 */ 435 436 static int 437 passwd(int argc, char *argv[]) 438 { 439 struct passwd *pw; 440 unsigned long id; 441 int i, rv; 442 443 assert(argc > 1); 444 assert(argv != NULL); 445 446 #define PASSWDPRINT (void)printf("%s:%s:%u:%u:%s:%s:%s\n", \ 447 pw->pw_name, pw->pw_passwd, pw->pw_uid, \ 448 pw->pw_gid, pw->pw_gecos, pw->pw_dir, pw->pw_shell) 449 450 (void)setpassent(1); 451 rv = RV_OK; 452 if (argc == 2) { 453 while ((pw = getpwent()) != NULL) 454 PASSWDPRINT; 455 } else { 456 for (i = 2; i < argc; i++) { 457 if (parsenum(argv[i], &id)) 458 pw = getpwuid((uid_t)id); 459 else 460 pw = getpwnam(argv[i]); 461 if (pw != NULL) 462 PASSWDPRINT; 463 else { 464 rv = RV_NOTFOUND; 465 break; 466 } 467 } 468 } 469 endpwent(); 470 return rv; 471 } 472 473 static char * 474 mygetent(const char * const * db_array, const char *name) 475 { 476 char *buf = NULL; 477 int error; 478 479 switch (error = cgetent(&buf, db_array, name)) { 480 case -3: 481 warnx("tc= loop in record `%s' in `%s'", name, db_array[0]); 482 break; 483 case -2: 484 warn("system error fetching record `%s' in `%s'", name, 485 db_array[0]); 486 break; 487 case -1: 488 case 0: 489 break; 490 case 1: 491 warnx("tc= reference not found in record for `%s' in `%s'", 492 name, db_array[0]); 493 break; 494 default: 495 warnx("unknown error %d in record `%s' in `%s'", error, name, 496 db_array[0]); 497 break; 498 } 499 return buf; 500 } 501 502 static char * 503 mygetone(const char * const * db_array, int first) 504 { 505 char *buf = NULL; 506 int error; 507 508 switch (error = (first ? cgetfirst : cgetnext)(&buf, db_array)) { 509 case -2: 510 warnx("tc= loop in `%s'", db_array[0]); 511 break; 512 case -1: 513 warn("system error fetching record in `%s'", db_array[0]); 514 break; 515 case 0: 516 case 1: 517 break; 518 case 2: 519 warnx("tc= reference not found in `%s'", db_array[0]); 520 break; 521 default: 522 warnx("unknown error %d in `%s'", error, db_array[0]); 523 break; 524 } 525 return buf; 526 } 527 528 static void 529 capprint(const char *cap) 530 { 531 char *c = strchr(cap, ':'); 532 if (c) 533 if (c == cap) 534 (void)printf("true\n"); 535 else { 536 int l = (int)(c - cap); 537 (void)printf("%*.*s\n", l, l, cap); 538 } 539 else 540 (void)printf("%s\n", cap); 541 } 542 543 static void 544 prettyprint(char *b) 545 { 546 #define TERMWIDTH 65 547 int did = 0; 548 size_t len; 549 char *s, c; 550 551 for (;;) { 552 len = strlen(b); 553 if (len <= TERMWIDTH) { 554 done: 555 if (did) 556 printf("\t:"); 557 printf("%s\n", b); 558 return; 559 } 560 for (s = b + TERMWIDTH; s > b && *s != ':'; s--) 561 continue; 562 if (*s++ != ':') 563 goto done; 564 c = *s; 565 *s = '\0'; 566 if (did) 567 printf("\t:"); 568 did++; 569 printf("%s\\\n", b); 570 *s = c; 571 b = s; 572 } 573 } 574 575 static void 576 handleone(const char * const *db_array, char *b, int recurse, int pretty, 577 int level) 578 { 579 char *tc; 580 581 if (level && pretty) 582 printf("\n"); 583 if (pretty) 584 prettyprint(b); 585 else 586 printf("%s\n", b); 587 if (!recurse || cgetstr(b, "tc", &tc) <= 0) 588 return; 589 590 b = mygetent(db_array, tc); 591 free(tc); 592 593 if (b == NULL) 594 return; 595 596 handleone(db_array, b, recurse, pretty, ++level); 597 free(b); 598 } 599 600 static int 601 handlecap(const char *db, int argc, char *argv[]) 602 { 603 static const char sfx[] = "=#:"; 604 const char *db_array[] = { db, NULL }; 605 char *b, *cap; 606 int i, rv, c; 607 size_t j; 608 int expand = 1, recurse = 0, pretty = 0; 609 610 assert(argc > 1); 611 assert(argv != NULL); 612 613 argc--; 614 argv++; 615 while ((c = getopt(argc, argv, "pnr")) != -1) 616 switch (c) { 617 case 'n': 618 expand = 0; 619 break; 620 case 'r': 621 expand = 0; 622 recurse = 1; 623 break; 624 case 'p': 625 pretty = 1; 626 break; 627 default: 628 usage(); 629 break; 630 } 631 632 argc -= optind; 633 argv += optind; 634 csetexpandtc(expand); 635 rv = RV_OK; 636 if (argc == 0) { 637 for (b = mygetone(db_array, 1); b; b = mygetone(db_array, 0)) { 638 handleone(db_array, b, recurse, pretty, 0); 639 free(b); 640 } 641 } else { 642 if ((b = mygetent(db_array, argv[0])) == NULL) 643 return RV_NOTFOUND; 644 if (argc == 1) 645 handleone(db_array, b, recurse, pretty, 0); 646 else { 647 for (i = 2; i < argc; i++) { 648 for (j = 0; j < sizeof(sfx) - 1; j++) { 649 cap = cgetcap(b, argv[i], sfx[j]); 650 if (cap) { 651 capprint(cap); 652 break; 653 } 654 } 655 if (j == sizeof(sfx) - 1) 656 printf("false\n"); 657 } 658 } 659 free(b); 660 } 661 return rv; 662 } 663 664 /* 665 * gettytab 666 */ 667 668 static int 669 gettytab(int argc, char *argv[]) 670 { 671 return handlecap(_PATH_GETTYTAB, argc, argv); 672 } 673 674 /* 675 * printcap 676 */ 677 678 static int 679 printcap(int argc, char *argv[]) 680 { 681 return handlecap(_PATH_PRINTCAP, argc, argv); 682 } 683 684 /* 685 * disktab 686 */ 687 688 static int 689 disktab(int argc, char *argv[]) 690 { 691 return handlecap(_PATH_DISKTAB, argc, argv); 692 } 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 #if !defined(__minix) 735 /* 736 * rpc 737 */ 738 739 static int 740 rpc(int argc, char *argv[]) 741 { 742 struct rpcent *re; 743 unsigned long id; 744 int i, rv; 745 746 assert(argc > 1); 747 assert(argv != NULL); 748 749 #define RPCPRINT printfmtstrings(re->r_aliases, " ", " ", \ 750 "%-16s %6d", \ 751 re->r_name, re->r_number) 752 753 setrpcent(1); 754 rv = RV_OK; 755 if (argc == 2) { 756 while ((re = getrpcent()) != NULL) 757 RPCPRINT; 758 } else { 759 for (i = 2; i < argc; i++) { 760 if (parsenum(argv[i], &id)) 761 re = getrpcbynumber((int)id); 762 else 763 re = getrpcbyname(argv[i]); 764 if (re != NULL) 765 RPCPRINT; 766 else { 767 rv = RV_NOTFOUND; 768 break; 769 } 770 } 771 } 772 endrpcent(); 773 return rv; 774 } 775 #endif /* !defined(__minix) */ 776 777 /* 778 * services 779 */ 780 781 static int 782 services(int argc, char *argv[]) 783 { 784 struct servent *se; 785 unsigned long id; 786 char *proto; 787 int i, rv; 788 789 assert(argc > 1); 790 assert(argv != NULL); 791 792 #define SERVICESPRINT printfmtstrings(se->s_aliases, " ", " ", \ 793 "%-16s %5d/%s", \ 794 se->s_name, ntohs(se->s_port), se->s_proto) 795 796 setservent(1); 797 rv = RV_OK; 798 if (argc == 2) { 799 while ((se = getservent()) != NULL) 800 SERVICESPRINT; 801 } else { 802 for (i = 2; i < argc; i++) { 803 proto = strchr(argv[i], '/'); 804 if (proto != NULL) 805 *proto++ = '\0'; 806 if (parsenum(argv[i], &id)) 807 se = getservbyport(htons(id), proto); 808 else 809 se = getservbyname(argv[i], proto); 810 if (se != NULL) 811 SERVICESPRINT; 812 else { 813 rv = RV_NOTFOUND; 814 break; 815 } 816 } 817 } 818 endservent(); 819 return rv; 820 } 821 822 823 /* 824 * shells 825 */ 826 827 static int 828 shells(int argc, char *argv[]) 829 { 830 const char *sh; 831 int i, rv; 832 833 assert(argc > 1); 834 assert(argv != NULL); 835 836 #define SHELLSPRINT (void)printf("%s\n", sh) 837 838 setusershell(); 839 rv = RV_OK; 840 if (argc == 2) { 841 while ((sh = getusershell()) != NULL) 842 SHELLSPRINT; 843 } else { 844 for (i = 2; i < argc; i++) { 845 setusershell(); 846 while ((sh = getusershell()) != NULL) { 847 if (strcmp(sh, argv[i]) == 0) { 848 SHELLSPRINT; 849 break; 850 } 851 } 852 if (sh == NULL) { 853 rv = RV_NOTFOUND; 854 break; 855 } 856 } 857 } 858 endusershell(); 859 return rv; 860 } 861