133965Sjdp/*	$OpenBSD: getent.c,v 1.23 2021/10/11 14:28:26 deraadt Exp $	*/
2218822Sdim/*	$NetBSD: getent.c,v 1.7 2005/08/24 14:31:02 ginsbach Exp $	*/
389857Sobrien
433965Sjdp/*-
533965Sjdp * Copyright (c) 2004 The NetBSD Foundation, Inc.
633965Sjdp * All rights reserved.
733965Sjdp *
833965Sjdp * This code is derived from software contributed to The NetBSD Foundation
933965Sjdp * by Luke Mewburn.
1033965Sjdp *
1133965Sjdp * Redistribution and use in source and binary forms, with or without
1233965Sjdp * modification, are permitted provided that the following conditions
1333965Sjdp * are met:
1433965Sjdp * 1. Redistributions of source code must retain the above copyright
1533965Sjdp *    notice, this list of conditions and the following disclaimer.
1633965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1733965Sjdp *    notice, this list of conditions and the following disclaimer in the
1833965Sjdp *    documentation and/or other materials provided with the distribution.
1933965Sjdp *
20218822Sdim * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2133965Sjdp * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22218822Sdim * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2333965Sjdp * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2433965Sjdp * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2533965Sjdp * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2633965Sjdp * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2733965Sjdp * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2833965Sjdp * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2933965Sjdp * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3033965Sjdp * POSSIBILITY OF SUCH DAMAGE.
3133965Sjdp */
3233965Sjdp
3333965Sjdp#include <sys/types.h>
3433965Sjdp#include <sys/socket.h>
3533965Sjdp
3633965Sjdp#include <ctype.h>
3733965Sjdp#include <err.h>
3833965Sjdp#include <errno.h>
3933965Sjdp#include <grp.h>
4033965Sjdp#include <limits.h>
4133965Sjdp#include <netdb.h>
4233965Sjdp#include <pwd.h>
4333965Sjdp#include <stdio.h>
4433965Sjdp#include <stdarg.h>
4533965Sjdp#include <stdlib.h>
4633965Sjdp#include <string.h>
4733965Sjdp#include <unistd.h>
4833965Sjdp
4933965Sjdp#include <net/if.h>
5033965Sjdp#include <netinet/in.h>		/* for INET6_ADDRSTRLEN */
5133965Sjdp#include <netinet/if_ether.h>
5233965Sjdp
5333965Sjdp#include <arpa/inet.h>
5433965Sjdp
5533965Sjdp#include <rpc/rpc.h>
5633965Sjdp
5733965Sjdpstatic void	usage(void);
5833965Sjdpstatic int	ethers(int, char *[]);
5933965Sjdpstatic int	group(int, char *[]);
6033965Sjdpstatic int	hosts(int, char *[]);
6133965Sjdpstatic int	passwd(int, char *[]);
6233965Sjdpstatic int	protocols(int, char *[]);
6333965Sjdpstatic int	rpc(int, char *[]);
6433965Sjdpstatic int	services(int, char *[]);
6533965Sjdpstatic int	shells(int, char *[]);
6633965Sjdpextern char *__progname;
6733965Sjdp
6833965Sjdpenum {
6933965Sjdp	RV_OK		= 0,
7033965Sjdp	RV_USAGE	= 1,
7133965Sjdp	RV_NOTFOUND	= 2,
7233965Sjdp	RV_NOENUM	= 3
7333965Sjdp};
7433965Sjdp
7533965Sjdpstatic struct getentdb {
7633965Sjdp	const char	*name;
7733965Sjdp	int		(*fn)(int, char *[]);
7833965Sjdp	const char	*pledge;
7933965Sjdp	const char	*unveil;
8033965Sjdp} databases[] = {
8189857Sobrien	{	"ethers",	ethers,		"stdio rpath",	"/etc/ethers"	},
8289857Sobrien	{	"group",	group,		"stdio getpw",	NULL	},
8389857Sobrien	{	"hosts",	hosts,		"stdio dns",	NULL	},
8489857Sobrien	{	"passwd",	passwd,		"stdio getpw",	NULL	},
8533965Sjdp	{	"protocols",	protocols,	"stdio rpath",	"/etc/protocols"	},
8633965Sjdp	{	"rpc",		rpc,		"stdio rpath",	"/etc/rpc"	},
8733965Sjdp	{	"services",	services,	"stdio rpath",	"/etc/services"	},
88	{	"shells",	shells,		"stdio rpath",	"/etc/shells"	},
89
90	{	NULL,		NULL,				},
91};
92
93int
94main(int argc, char *argv[])
95{
96	struct getentdb	*curdb;
97
98	if (argc < 2)
99		usage();
100	for (curdb = databases; curdb->name != NULL; curdb++) {
101		if (strcmp(curdb->name, argv[1]) == 0) {
102			if (curdb->unveil != NULL) {
103				if (unveil(curdb->unveil, "r") == -1)
104					err(1, "unveil %s", curdb->unveil);
105			}
106			if (pledge(curdb->pledge, NULL) == -1)
107				err(1, "pledge");
108
109			exit(curdb->fn(argc, argv));
110			break;
111		}
112	}
113	fprintf(stderr, "%s: unknown database: %s\n", __progname, argv[1]);
114	return RV_USAGE;
115}
116
117static void
118usage(void)
119{
120	fprintf(stderr, "usage: %s database [key ...]\n", __progname);
121	exit(RV_USAGE);
122}
123
124/*
125 * printfmtstrings --
126 *	vprintf(format, ...),
127 *	then the aliases (beginning with prefix, separated by sep),
128 *	then a newline
129 */
130static void
131printfmtstrings(char *strings[], const char *prefix, const char *sep,
132	const char *fmt, ...)
133{
134	va_list		ap;
135	const char	*curpref;
136	int		i;
137
138	va_start(ap, fmt);
139	vprintf(fmt, ap);
140	va_end(ap);
141
142	curpref = prefix;
143	for (i = 0; strings[i] != NULL; i++) {
144		printf("%s%s", curpref, strings[i]);
145		curpref = sep;
146	}
147	printf("\n");
148}
149
150#define ETHERSPRINT	printf("%-17s  %s\n", ether_ntoa(eap), hp)
151
152static int
153ethers(int argc, char *argv[])
154{
155	char		hostname[HOST_NAME_MAX+1], *hp;
156	int		i, rv = RV_OK;
157	struct ether_addr ea, *eap;
158
159	if (argc == 2) {
160		fprintf(stderr, "%s: Enumeration not supported on ethers\n",
161		    __progname);
162		rv = RV_NOENUM;
163	} else {
164		for (i = 2; i < argc; i++) {
165			if ((eap = ether_aton(argv[i])) == NULL) {
166				eap = &ea;
167				hp = argv[i];
168				if (ether_hostton(hp, eap) != 0) {
169					rv = RV_NOTFOUND;
170					break;
171				}
172			} else {
173				hp = hostname;
174				if (ether_ntohost(hp, eap) != 0) {
175					rv = RV_NOTFOUND;
176					break;
177				}
178			}
179			ETHERSPRINT;
180		}
181	}
182	return rv;
183}
184
185#define GROUPPRINT	\
186	printfmtstrings(gr->gr_mem, ":", ",", "%s:%s:%u", \
187	    gr->gr_name, gr->gr_passwd, gr->gr_gid)
188
189static int
190group(int argc, char *argv[])
191{
192	struct group	*gr;
193	const char	*err;
194	gid_t		gid;
195	int		i, rv = RV_OK;
196
197	setgroupent(1);
198	if (argc == 2) {
199		while ((gr = getgrent()) != NULL)
200			GROUPPRINT;
201	} else {
202		for (i = 2; i < argc; i++) {
203			if ((gr = getgrnam(argv[i])) == NULL) {
204				gid = strtonum(argv[i], 0, GID_MAX, &err);
205				if (err == NULL)
206					gr = getgrgid(gid);
207			}
208			if (gr != NULL)
209				GROUPPRINT;
210			else {
211				rv = RV_NOTFOUND;
212				break;
213			}
214		}
215	}
216	endgrent();
217	return rv;
218}
219
220static void
221hostsprint(const struct hostent *he)
222{
223	char	buf[INET6_ADDRSTRLEN];
224
225	if (inet_ntop(he->h_addrtype, he->h_addr, buf, sizeof(buf)) == NULL)
226		strlcpy(buf, "# unknown", sizeof(buf));
227	printfmtstrings(he->h_aliases, "  ", " ", "%-39s %s", buf, he->h_name);
228}
229static int
230hostsaddrinfo(const char *name)
231{
232	struct addrinfo	 hints, *res, *res0;
233	char		 buf[INET6_ADDRSTRLEN];
234	int		 rv;
235
236	rv = RV_NOTFOUND;
237	memset(buf, 0, sizeof(buf));
238	memset(&hints, 0, sizeof(hints));
239	hints.ai_family = AF_UNSPEC;
240	hints.ai_socktype = SOCK_DGRAM;
241
242	if (getaddrinfo(name, NULL, &hints, &res0) != 0)
243		return (rv);
244	for (res = res0; res; res = res->ai_next) {
245		if ((res->ai_family != AF_INET6 && res->ai_family != AF_INET) ||
246		    getnameinfo(res->ai_addr, res->ai_addrlen, buf, sizeof(buf),
247		    NULL, 0, NI_NUMERICHOST) != 0)
248			strlcpy(buf, "# unknown", sizeof(buf));
249		else
250			rv = RV_OK;
251		printf("%-39s %s\n", buf, name);
252	}
253	freeaddrinfo(res0);
254
255	return (rv);
256}
257
258static int
259hosts(int argc, char *argv[])
260{
261	struct in6_addr	in6;
262	struct in_addr	in;
263	int		i, rv = RV_OK;
264	struct hostent	*he;
265
266	if (argc == 2) {
267		fprintf(stderr, "%s: Enumeration not supported on hosts\n",
268		    __progname);
269		rv = RV_NOENUM;
270	} else {
271		for (i = 2; i < argc; i++) {
272			he = NULL;
273			if (inet_pton(AF_INET6, argv[i], (void *)&in6) > 0)
274				he = gethostbyaddr(&in6, sizeof(in6), AF_INET6);
275			else if (inet_pton(AF_INET, argv[i], (void *)&in) > 0)
276				he = gethostbyaddr(&in, sizeof(in), AF_INET);
277			if (he != NULL)
278				hostsprint(he);
279			else if ((rv = hostsaddrinfo(argv[i])) == RV_NOTFOUND)
280				break;
281		}
282	}
283	return rv;
284}
285
286#define PASSWDPRINT	\
287	printf("%s:%s:%u:%u:%s:%s:%s\n", \
288	    pw->pw_name, pw->pw_passwd, pw->pw_uid, \
289	    pw->pw_gid, pw->pw_gecos, pw->pw_dir, pw->pw_shell)
290
291static int
292passwd(int argc, char *argv[])
293{
294	struct passwd	*pw;
295	const char	*err;
296	uid_t		uid;
297	int		i, rv = RV_OK;
298
299	setpassent(1);
300	if (argc == 2) {
301		while ((pw = getpwent()) != NULL)
302			PASSWDPRINT;
303	} else {
304		for (i = 2; i < argc; i++) {
305			if ((pw = getpwnam(argv[i])) == NULL) {
306				uid = strtonum(argv[i], 0, UID_MAX, &err);
307				if (err == NULL)
308					pw = getpwuid(uid);
309			}
310			if (pw != NULL)
311				PASSWDPRINT;
312			else {
313				rv = RV_NOTFOUND;
314				break;
315			}
316		}
317	}
318	endpwent();
319	return rv;
320}
321
322#define PROTOCOLSPRINT	\
323	printfmtstrings(pe->p_aliases, "  ", " ", \
324	    "%-16s  %5d", pe->p_name, pe->p_proto)
325
326static int
327protocols(int argc, char *argv[])
328{
329	struct protoent	*pe;
330	const char	*err;
331	int		proto;
332	int		i, rv = RV_OK;
333
334	setprotoent(1);
335	if (argc == 2) {
336		while ((pe = getprotoent()) != NULL)
337			PROTOCOLSPRINT;
338	} else {
339		for (i = 2; i < argc; i++) {
340			proto = strtonum(argv[i], 0, INT_MAX, &err);
341			if (!err)
342				pe = getprotobynumber(proto);
343			else
344				pe = getprotobyname(argv[i]);
345			if (pe != NULL)
346				PROTOCOLSPRINT;
347			else {
348				rv = RV_NOTFOUND;
349				break;
350			}
351		}
352	}
353	endprotoent();
354	return rv;
355}
356
357#define RPCPRINT	\
358	printfmtstrings(re->r_aliases, "  ", " ", \
359	    "%-16s  %6d", re->r_name, re->r_number)
360
361static int
362rpc(int argc, char *argv[])
363{
364	struct rpcent	*re;
365	const char	*err;
366	int		rpc;
367	int		i, rv = RV_OK;
368
369	setrpcent(1);
370	if (argc == 2) {
371		while ((re = getrpcent()) != NULL)
372			RPCPRINT;
373	} else {
374		for (i = 2; i < argc; i++) {
375			rpc = strtonum(argv[i], 0, INT_MAX, &err);
376			if (!err)
377				re = getrpcbynumber(rpc);
378			else
379				re = getrpcbyname(argv[i]);
380			if (re != NULL)
381				RPCPRINT;
382			else {
383				rv = RV_NOTFOUND;
384				break;
385			}
386		}
387	}
388	endrpcent();
389	return rv;
390}
391
392#define SERVICESPRINT	\
393	printfmtstrings(se->s_aliases, "  ", " ", \
394	    "%-16s  %5d/%s", se->s_name, ntohs(se->s_port), se->s_proto)
395
396static int
397services(int argc, char *argv[])
398{
399	struct servent	*se;
400	const char	*err;
401	char		*proto;
402	in_port_t	port;
403	int		i, rv = RV_OK;
404
405	setservent(1);
406	if (argc == 2) {
407		while ((se = getservent()) != NULL)
408			SERVICESPRINT;
409	} else {
410		for (i = 2; i < argc; i++) {
411			if ((proto = strchr(argv[i], '/')) != NULL)
412				*proto++ = '\0';
413			port = strtonum(argv[i], 0, IPPORT_HILASTAUTO, &err);
414			if (!err)
415				se = getservbyport(htons(port), proto);
416			else
417				se = getservbyname(argv[i], proto);
418			if (se != NULL)
419				SERVICESPRINT;
420			else {
421				rv = RV_NOTFOUND;
422				break;
423			}
424		}
425	}
426	endservent();
427	return rv;
428}
429
430#define SHELLSPRINT	printf("%s\n", sh)
431
432static int
433shells(int argc, char *argv[])
434{
435	const char	*sh;
436	int		i, rv = RV_OK;
437
438	setusershell();
439	if (argc == 2) {
440		while ((sh = getusershell()) != NULL)
441			SHELLSPRINT;
442	} else {
443		for (i = 2; i < argc; i++) {
444			setusershell();
445			while ((sh = getusershell()) != NULL) {
446				if (strcmp(sh, argv[i]) == 0) {
447					SHELLSPRINT;
448					break;
449				}
450			}
451			if (sh == NULL) {
452				rv = RV_NOTFOUND;
453				break;
454			}
455		}
456	}
457	endusershell();
458	return rv;
459}
460