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