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