xref: /openbsd-src/usr.bin/getent/getent.c (revision 24bb5fcea3ed904bc467217bdaadb5dfc618d5bf)
1 /*	$OpenBSD: getent.c,v 1.22 2021/07/12 15:09:19 beck Exp $	*/
2 /*	$NetBSD: getent.c,v 1.7 2005/08/24 14:31:02 ginsbach Exp $	*/
3 
4 /*-
5  * Copyright (c) 2004 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Luke Mewburn.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 
36 #include <ctype.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <grp.h>
40 #include <limits.h>
41 #include <netdb.h>
42 #include <pwd.h>
43 #include <stdio.h>
44 #include <stdarg.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 
49 #include <net/if.h>
50 #include <netinet/in.h>		/* for INET6_ADDRSTRLEN */
51 #include <netinet/if_ether.h>
52 
53 #include <arpa/inet.h>
54 #include <arpa/nameser.h>
55 
56 #include <rpc/rpc.h>
57 
58 static void	usage(void);
59 static int	ethers(int, char *[]);
60 static int	group(int, char *[]);
61 static int	hosts(int, char *[]);
62 static int	passwd(int, char *[]);
63 static int	protocols(int, char *[]);
64 static int	rpc(int, char *[]);
65 static int	services(int, char *[]);
66 static int	shells(int, char *[]);
67 extern char *__progname;
68 
69 enum {
70 	RV_OK		= 0,
71 	RV_USAGE	= 1,
72 	RV_NOTFOUND	= 2,
73 	RV_NOENUM	= 3
74 };
75 
76 static struct getentdb {
77 	const char	*name;
78 	int		(*fn)(int, char *[]);
79 	const char	*pledge;
80 	const char	*unveil;
81 } databases[] = {
82 	{	"ethers",	ethers,		"stdio rpath",	"/etc/ethers"	},
83 	{	"group",	group,		"stdio getpw",	NULL	},
84 	{	"hosts",	hosts,		"stdio dns",	NULL	},
85 	{	"passwd",	passwd,		"stdio getpw",	NULL	},
86 	{	"protocols",	protocols,	"stdio rpath",	"/etc/protocols"	},
87 	{	"rpc",		rpc,		"stdio rpath",	"/etc/rpc"	},
88 	{	"services",	services,	"stdio rpath",	"/etc/services"	},
89 	{	"shells",	shells,		"stdio rpath",	"/etc/shells"	},
90 
91 	{	NULL,		NULL,				},
92 };
93 
94 int
95 main(int argc, char *argv[])
96 {
97 	struct getentdb	*curdb;
98 
99 	if (argc < 2)
100 		usage();
101 	for (curdb = databases; curdb->name != NULL; curdb++) {
102 		if (strcmp(curdb->name, argv[1]) == 0) {
103 			if (curdb->unveil != NULL) {
104 				if (unveil(curdb->unveil, "r") == -1)
105 					err(1, "unveil %s", curdb->unveil);
106 			}
107 			if (pledge(curdb->pledge, NULL) == -1)
108 				err(1, "pledge");
109 
110 			exit(curdb->fn(argc, argv));
111 			break;
112 		}
113 	}
114 	fprintf(stderr, "%s: unknown database: %s\n", __progname, argv[1]);
115 	return RV_USAGE;
116 }
117 
118 static void
119 usage(void)
120 {
121 	fprintf(stderr, "usage: %s database [key ...]\n", __progname);
122 	exit(RV_USAGE);
123 }
124 
125 /*
126  * printfmtstrings --
127  *	vprintf(format, ...),
128  *	then the aliases (beginning with prefix, separated by sep),
129  *	then a newline
130  */
131 static void
132 printfmtstrings(char *strings[], const char *prefix, const char *sep,
133 	const char *fmt, ...)
134 {
135 	va_list		ap;
136 	const char	*curpref;
137 	int		i;
138 
139 	va_start(ap, fmt);
140 	vprintf(fmt, ap);
141 	va_end(ap);
142 
143 	curpref = prefix;
144 	for (i = 0; strings[i] != NULL; i++) {
145 		printf("%s%s", curpref, strings[i]);
146 		curpref = sep;
147 	}
148 	printf("\n");
149 }
150 
151 #define ETHERSPRINT	printf("%-17s  %s\n", ether_ntoa(eap), hp)
152 
153 static int
154 ethers(int argc, char *argv[])
155 {
156 	char		hostname[HOST_NAME_MAX+1], *hp;
157 	int		i, rv = RV_OK;
158 	struct ether_addr ea, *eap;
159 
160 	if (argc == 2) {
161 		fprintf(stderr, "%s: Enumeration not supported on ethers\n",
162 		    __progname);
163 		rv = RV_NOENUM;
164 	} else {
165 		for (i = 2; i < argc; i++) {
166 			if ((eap = ether_aton(argv[i])) == NULL) {
167 				eap = &ea;
168 				hp = argv[i];
169 				if (ether_hostton(hp, eap) != 0) {
170 					rv = RV_NOTFOUND;
171 					break;
172 				}
173 			} else {
174 				hp = hostname;
175 				if (ether_ntohost(hp, eap) != 0) {
176 					rv = RV_NOTFOUND;
177 					break;
178 				}
179 			}
180 			ETHERSPRINT;
181 		}
182 	}
183 	return rv;
184 }
185 
186 #define GROUPPRINT	\
187 	printfmtstrings(gr->gr_mem, ":", ",", "%s:%s:%u", \
188 	    gr->gr_name, gr->gr_passwd, gr->gr_gid)
189 
190 static int
191 group(int argc, char *argv[])
192 {
193 	struct group	*gr;
194 	const char	*err;
195 	gid_t		gid;
196 	int		i, rv = RV_OK;
197 
198 	setgroupent(1);
199 	if (argc == 2) {
200 		while ((gr = getgrent()) != NULL)
201 			GROUPPRINT;
202 	} else {
203 		for (i = 2; i < argc; i++) {
204 			if ((gr = getgrnam(argv[i])) == NULL) {
205 				gid = strtonum(argv[i], 0, GID_MAX, &err);
206 				if (err == NULL)
207 					gr = getgrgid(gid);
208 			}
209 			if (gr != NULL)
210 				GROUPPRINT;
211 			else {
212 				rv = RV_NOTFOUND;
213 				break;
214 			}
215 		}
216 	}
217 	endgrent();
218 	return rv;
219 }
220 
221 static void
222 hostsprint(const struct hostent *he)
223 {
224 	char	buf[INET6_ADDRSTRLEN];
225 
226 	if (inet_ntop(he->h_addrtype, he->h_addr, buf, sizeof(buf)) == NULL)
227 		strlcpy(buf, "# unknown", sizeof(buf));
228 	printfmtstrings(he->h_aliases, "  ", " ", "%-39s %s", buf, he->h_name);
229 }
230 static int
231 hostsaddrinfo(const char *name)
232 {
233 	struct addrinfo	 hints, *res, *res0;
234 	char		 buf[INET6_ADDRSTRLEN];
235 	int		 rv;
236 
237 	rv = RV_NOTFOUND;
238 	memset(buf, 0, sizeof(buf));
239 	memset(&hints, 0, sizeof(hints));
240 	hints.ai_family = AF_UNSPEC;
241 	hints.ai_socktype = SOCK_DGRAM;
242 
243 	if (getaddrinfo(name, NULL, &hints, &res0) != 0)
244 		return (rv);
245 	for (res = res0; res; res = res->ai_next) {
246 		if ((res->ai_family != AF_INET6 && res->ai_family != AF_INET) ||
247 		    getnameinfo(res->ai_addr, res->ai_addrlen, buf, sizeof(buf),
248 		    NULL, 0, NI_NUMERICHOST) != 0)
249 			strlcpy(buf, "# unknown", sizeof(buf));
250 		else
251 			rv = RV_OK;
252 		printf("%-39s %s\n", buf, name);
253 	}
254 	freeaddrinfo(res0);
255 
256 	return (rv);
257 }
258 
259 static int
260 hosts(int argc, char *argv[])
261 {
262 	char		addr[IN6ADDRSZ];
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 *)addr) > 0)
274 				he = gethostbyaddr(addr, IN6ADDRSZ, AF_INET6);
275 			else if (inet_pton(AF_INET, argv[i], (void *)addr) > 0)
276 				he = gethostbyaddr(addr, INADDRSZ, 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 
291 static int
292 passwd(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 
326 static int
327 protocols(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 
361 static int
362 rpc(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 
396 static int
397 services(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 
432 static int
433 shells(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