1/*	$OpenBSD: getgrent.c,v 1.50 2022/08/02 17:00:15 deraadt Exp $ */
2/*
3 * Copyright (c) 1989, 1993
4 *	The Regents of the University of California.  All rights reserved.
5 * Portions Copyright (c) 1994, Jason Downs. All Rights Reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/types.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <limits.h>
37#include <unistd.h>
38#include <grp.h>
39#include <errno.h>
40#ifdef YP
41#include <rpc/rpc.h>
42#include <rpcsvc/yp.h>
43#include <rpcsvc/ypclnt.h>
44#include "ypinternal.h"
45#include "ypexclude.h"
46#endif
47#include "thread_private.h"
48
49/* This global storage is locked for the non-rentrant functions */
50_THREAD_PRIVATE_KEY(gr_storage);
51static struct group_storage {
52#define	MAXGRP		200
53	char *members[MAXGRP];
54#define	MAXLINELENGTH	1024
55	char line[MAXLINELENGTH];
56} gr_storage;
57#define GETGR_R_SIZE_MAX	_GR_BUF_LEN
58
59/* File pointers are locked with the 'gr' mutex */
60_THREAD_PRIVATE_KEY(gr);
61static FILE *_gr_fp;
62static struct group _gr_group;
63static int _gr_stayopen;
64static int grscan(int, gid_t, const char *, struct group *, struct group_storage *,
65	int *);
66static int start_gr(void);
67static void endgrent_basic(void);
68
69static struct group *getgrnam_gs(const char *, struct group *,
70	struct group_storage *);
71static struct group *getgrgid_gs(gid_t, struct group *,
72	struct group_storage *);
73
74#ifdef YP
75static struct _ypexclude *__ypexhead = NULL;
76static int	__ypmode = 0;
77static char	*__ypcurrent, *__ypdomain;
78static int	__ypcurrentlen;
79#endif
80
81struct group *
82_getgrent_yp(int *foundyp)
83{
84	struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr, _gr_group, NULL);
85	struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage,
86	    gr_storage, NULL);
87
88	_THREAD_PRIVATE_MUTEX_LOCK(gr);
89	if ((!_gr_fp && !start_gr()) || !grscan(0, 0, NULL, p_gr, gs, foundyp))
90		p_gr = NULL;
91	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
92	return (p_gr);
93}
94
95struct group *
96getgrent(void)
97{
98	return (_getgrent_yp(NULL));
99}
100
101static struct group *
102getgrnam_gs(const char *name, struct group *p_gr, struct group_storage *gs)
103{
104	int rval;
105
106	_THREAD_PRIVATE_MUTEX_LOCK(gr);
107	if (!start_gr())
108		rval = 0;
109	else {
110		rval = grscan(1, 0, name, p_gr, gs, NULL);
111		if (!_gr_stayopen)
112			endgrent_basic();
113	}
114	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
115	return(rval ? p_gr : NULL);
116}
117
118struct group *
119getgrnam(const char *name)
120{
121	struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr,_gr_group,NULL);
122	struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage,
123	    gr_storage, NULL);
124
125	return getgrnam_gs(name, p_gr, gs);
126}
127
128int
129getgrnam_r(const char *name, struct group *grp, char *buffer,
130	size_t bufsize, struct group **result)
131{
132	int errnosave;
133	int ret;
134
135	if (bufsize < GETGR_R_SIZE_MAX)
136		return ERANGE;
137	errnosave = errno;
138	errno = 0;
139	*result = getgrnam_gs(name, grp, (struct group_storage *)buffer);
140	if (*result == NULL)
141		ret = errno;
142	else
143		ret = 0;
144	errno = errnosave;
145	return ret;
146}
147DEF_WEAK(getgrnam_r);
148
149static struct group *
150getgrgid_gs(gid_t gid, struct group *p_gr, struct group_storage *gs)
151{
152	int rval;
153
154	_THREAD_PRIVATE_MUTEX_LOCK(gr);
155	if (!start_gr())
156		rval = 0;
157	else {
158		rval = grscan(1, gid, NULL, p_gr, gs, NULL);
159		if (!_gr_stayopen)
160			endgrent_basic();
161	}
162	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
163	return(rval ? p_gr : NULL);
164}
165
166struct group *
167getgrgid(gid_t gid)
168{
169	struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr, _gr_group, NULL);
170	struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage,
171	    gr_storage, NULL);
172
173	return getgrgid_gs(gid, p_gr, gs);
174}
175
176int
177getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
178	struct group **result)
179{
180	int errnosave;
181	int ret;
182
183	if (bufsize < GETGR_R_SIZE_MAX)
184		return ERANGE;
185	errnosave = errno;
186	errno = 0;
187	*result = getgrgid_gs(gid, grp, (struct group_storage *)buffer);
188	if (*result == NULL)
189		ret = errno;
190	else
191		ret = 0;
192	errno = errnosave;
193	return ret;
194}
195DEF_WEAK(getgrgid_r);
196
197static int
198start_gr(void)
199{
200	if (_gr_fp) {
201		rewind(_gr_fp);
202#ifdef YP
203		__ypmode = 0;
204		free(__ypcurrent);
205		__ypcurrent = NULL;
206		if (__ypexhead)
207			__ypexclude_free(&__ypexhead);
208		__ypexhead = NULL;
209#endif
210		return(1);
211	}
212
213	return((_gr_fp = fopen(_PATH_GROUP, "re")) ? 1 : 0);
214}
215
216void
217setgrent(void)
218{
219	int saved_errno;
220
221	saved_errno = errno;
222	setgroupent(0);
223	errno = saved_errno;
224}
225DEF_WEAK(setgrent);
226
227int
228setgroupent(int stayopen)
229{
230	int retval;
231
232	_THREAD_PRIVATE_MUTEX_LOCK(gr);
233	if (!start_gr())
234		retval = 0;
235	else {
236		_gr_stayopen = stayopen;
237		retval = 1;
238	}
239	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
240	return (retval);
241}
242DEF_WEAK(setgroupent);
243
244static
245void
246endgrent_basic(void)
247{
248	int saved_errno;
249
250	if (_gr_fp) {
251		saved_errno = errno;
252		fclose(_gr_fp);
253		_gr_fp = NULL;
254#ifdef YP
255		__ypmode = 0;
256		free(__ypcurrent);
257		__ypcurrent = NULL;
258		if (__ypexhead)
259			__ypexclude_free(&__ypexhead);
260		__ypexhead = NULL;
261#endif
262		errno = saved_errno;
263	}
264}
265
266void
267endgrent(void)
268{
269	_THREAD_PRIVATE_MUTEX_LOCK(gr);
270	endgrent_basic();
271	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
272}
273DEF_WEAK(endgrent);
274
275static int
276grscan(int search, gid_t gid, const char *name, struct group *p_gr,
277    struct group_storage *gs, int *foundyp)
278{
279	char *cp, **m;
280	char *bp, *endp;
281	u_long ul;
282#ifdef YP
283	char *key, *data;
284	int keylen, datalen;
285	int r;
286#endif
287	char **members;
288	char *line;
289	int saved_errno;
290
291	if (gs == NULL)
292		return 0;
293	members = gs->members;
294	line = gs->line;
295	saved_errno = errno;
296
297	for (;;) {
298#ifdef YP
299		if (__ypmode) {
300			if (__ypcurrent) {
301				r = yp_next(__ypdomain, "group.byname",
302				    __ypcurrent, __ypcurrentlen,
303				    &key, &keylen, &data, &datalen);
304				free(__ypcurrent);
305				__ypcurrent = key;
306				__ypcurrentlen = keylen;
307			} else {
308				r = yp_first(__ypdomain, "group.byname",
309				    &__ypcurrent, &__ypcurrentlen,
310				    &data, &datalen);
311			}
312			if (r) {
313				__ypmode = 0;
314				__ypcurrent = NULL;
315				if (r == YPERR_NOMORE)
316					continue;
317				else
318					return 0;
319			}
320			bcopy(data, line, datalen);
321			free(data);
322			line[datalen] = '\0';
323			bp = line;
324			goto parse;
325		}
326#endif
327		if (!fgets(line, sizeof(gs->line), _gr_fp)) {
328			if (feof(_gr_fp) && !ferror(_gr_fp))
329				errno = saved_errno;
330			return 0;
331		}
332		bp = line;
333		/* skip lines that are too big */
334		if (!strchr(line, '\n')) {
335			int ch;
336
337			while ((ch = getc_unlocked(_gr_fp)) != '\n' &&
338			    ch != EOF)
339				;
340			continue;
341		}
342#ifdef YP
343		if (line[0] == '+' || line[0] == '-') {
344			if (!__ypdomain)
345				yp_get_default_domain(&__ypdomain);
346		}
347		if (line[0] == '+') {
348			switch (line[1]) {
349			case ':':
350			case '\0':
351			case '\n':
352				if (foundyp) {
353					*foundyp = 1;
354					errno = saved_errno;
355					return 0;
356				}
357				if (!search) {
358					__ypmode = 1;
359					continue;
360				}
361				if (name) {
362					r = yp_match(__ypdomain,
363					    "group.byname", name, strlen(name),
364					    &data, &datalen);
365				} else {
366					char buf[20];
367					snprintf(buf, sizeof buf, "%u", gid);
368					r = yp_match(__ypdomain, "group.bygid",
369					    buf, strlen(buf), &data, &datalen);
370				}
371				switch (r) {
372				case 0:
373					break;
374				case YPERR_KEY:
375					continue;
376				default:
377					return 0;
378				}
379				bcopy(data, line, datalen);
380				free(data);
381				line[datalen] = '\0';
382				bp = line;
383				p_gr->gr_name = strsep(&bp, ":\n");
384				if (__ypexclude_is(&__ypexhead, p_gr->gr_name))
385					continue;
386				p_gr->gr_passwd = strsep(&bp, ":\n");
387				if (!(cp = strsep(&bp, ":\n")))
388					continue;
389				if (name) {
390					ul = strtoul(cp, &endp, 10);
391					if (*endp != '\0' || endp == cp ||
392					    ul >= GID_MAX)
393						continue;
394					p_gr->gr_gid = ul;
395				} else
396					p_gr->gr_gid = gid;
397				goto found_it;
398			default:
399				bp = strsep(&bp, ":\n") + 1;
400				if ((search && name && strcmp(bp, name)) ||
401				    __ypexclude_is(&__ypexhead, bp))
402					continue;
403				r = yp_match(__ypdomain, "group.byname",
404				    bp, strlen(bp), &data, &datalen);
405				switch (r) {
406				case 0:
407					break;
408				case YPERR_KEY:
409					continue;
410				default:
411					return 0;
412				}
413				bcopy(data, line, datalen);
414				free(data);
415				line[datalen] = '\0';
416				bp = line;
417			}
418		} else if (line[0] == '-') {
419			if (__ypexclude_add(&__ypexhead,
420			    strsep(&line, ":\n") + 1))
421				return 0;
422			if (foundyp) {
423				*foundyp = -1;
424				errno = saved_errno;
425				return 0;
426			}
427			continue;
428		}
429parse:
430#endif
431		p_gr->gr_name = strsep(&bp, ":\n");
432		if (search && name && strcmp(p_gr->gr_name, name))
433			continue;
434#ifdef YP
435		if (__ypmode && __ypexclude_is(&__ypexhead, p_gr->gr_name))
436			continue;
437#endif
438		p_gr->gr_passwd = strsep(&bp, ":\n");
439		if (!(cp = strsep(&bp, ":\n")))
440			continue;
441		ul = strtoul(cp, &endp, 10);
442		if (endp == cp || *endp != '\0' || ul >= GID_MAX)
443			continue;
444		p_gr->gr_gid = ul;
445		if (search && name == NULL && p_gr->gr_gid != gid)
446			continue;
447#ifdef YP
448	found_it:
449#endif
450		cp = NULL;
451		if (bp == NULL)
452			continue;
453		for (m = p_gr->gr_mem = members;; bp++) {
454			if (m == &members[MAXGRP - 1])
455				break;
456			if (*bp == ',') {
457				if (cp) {
458					*bp = '\0';
459					*m++ = cp;
460					cp = NULL;
461				}
462			} else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
463				if (cp) {
464					*bp = '\0';
465					*m++ = cp;
466				}
467				break;
468			} else if (cp == NULL)
469				cp = bp;
470		}
471		*m = NULL;
472		errno = saved_errno;
473		return 1;
474	}
475	/* NOTREACHED */
476}
477