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