1/*	$NetBSD: getgrent.c,v 1.12 2005/09/14 15:54:53 drochner Exp $	*/
2
3/*
4 * Copyright (c) 1989, 1991, 1993
5 *	The Regents of the University of California.  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/*
33 * Portions Copyright (c) 1994, Jason Downs. All Rights Reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 *    notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 *    notice, this list of conditions and the following disclaimer in the
42 *    documentation and/or other materials provided with the distribution.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
45 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
46 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
47 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
48 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
49 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
50 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
51 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * SUCH DAMAGE.
55 */
56
57/*
58 * Copied from:  lib/libc/gen/getgrent.c
59 *     NetBSD: getgrent.c,v 1.46 2003/02/17 00:11:54 simonb Exp
60 * and then gutted, leaving only /etc/group support.
61 */
62
63#include <sys/cdefs.h>
64
65#ifdef __weak_alias
66#define endgrent		_endgrent
67#define getgrent		_getgrent
68#define getgrgid		_getgrgid
69#define getgrnam		_getgrnam
70#define getgrnam_r		_getgrnam_r
71#define setgrent		_setgrent
72#define setgroupent		_setgroupent
73#define getgrouplist		_getgrouplist
74
75__weak_alias(endgrent,_endgrent)
76__weak_alias(getgrent,_getgrent)
77__weak_alias(getgrgid,_getgrgid)
78__weak_alias(getgrnam,_getgrnam)
79__weak_alias(getgrnam_r,_getgrnam_r)
80__weak_alias(setgrent,_setgrent)
81__weak_alias(setgroupent,_setgroupent)
82__weak_alias(getgrouplist,_getgrouplist)
83#endif
84
85#include <sys/param.h>
86
87#include <grp.h>
88#include <limits.h>
89#include <stdio.h>
90#include <stdlib.h>
91#include <string.h>
92#include <unistd.h>
93#include <errno.h>
94
95static FILE		*_gr_fp;
96static struct group	_gr_group;
97static int		_gr_stayopen;
98static int		_gr_filesdone;
99
100static int grscan(int, gid_t, const char *, const char *);
101static int grstart(void);
102static int grmatchline(int, gid_t, const char *, const char *);
103
104#define	MAXGRP		200
105#define	MAXLINELENGTH	1024
106
107static __aconst char	*members[MAXGRP];
108static char		grline[MAXLINELENGTH];
109
110struct group *
111getgrent(void)
112{
113
114	if ((!_gr_fp && !grstart()) || !grscan(0, 0, NULL, NULL))
115 		return (NULL);
116	return &_gr_group;
117}
118
119int
120getgrnam_r(const char *name, struct group *grp, char *buffer,
121	size_t buflen, struct group **result)
122{
123	struct group *gp, *bgp;
124
125	/*
126	 * We blatantly cheat (don't provide reentrancy)
127	 * and hope to get away with it
128	 */
129
130	*result = NULL;
131	bgp = (struct group*)buffer;
132	if (buflen < sizeof(struct group))
133		return ENOMEM;
134
135	gp = getgrnam(name);
136	if (gp) {
137		*bgp = *gp;
138		*result = bgp;
139	}
140
141	return (gp) ? ENOENT : 0;
142}
143
144struct group *
145getgrnam(const char *name)
146{
147	int rval;
148
149	if (!grstart())
150		return NULL;
151	rval = grscan(1, 0, name, NULL);
152	if (!_gr_stayopen)
153		endgrent();
154	return (rval) ? &_gr_group : NULL;
155}
156
157struct group *
158getgrgid(gid_t gid)
159{
160	int rval;
161
162	if (!grstart())
163		return NULL;
164	rval = grscan(1, gid, NULL, NULL);
165	if (!_gr_stayopen)
166		endgrent();
167	return (rval) ? &_gr_group : NULL;
168}
169
170static int
171grstart(void)
172{
173
174	_gr_filesdone = 0;
175	if (_gr_fp) {
176		rewind(_gr_fp);
177		return 1;
178	}
179	return (_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0;
180}
181
182void
183setgrent(void)
184{
185
186	(void) setgroupent(0);
187}
188
189int
190setgroupent(int stayopen)
191{
192
193	if (!grstart())
194		return 0;
195	_gr_stayopen = stayopen;
196	return 1;
197}
198
199void
200endgrent(void)
201{
202
203	_gr_filesdone = 0;
204	if (_gr_fp) {
205		(void)fclose(_gr_fp);
206		_gr_fp = NULL;
207	}
208}
209
210int
211getgrouplist(const char *uname, gid_t agroup,
212    gid_t *groups, int *grpcnt)
213{
214	struct group *grp;
215	int maxgroups, i, ngroups, ret;
216
217	maxgroups = *grpcnt;
218	ret = 0;
219	ngroups = 0;
220
221	/*
222	 * install primary group
223	 */
224	if (ngroups < maxgroups)
225		groups[ngroups] = agroup;
226	else
227		ret = -1;
228	ngroups++;
229
230	/*
231	 * Scan the group file to find additional groups.
232	 */
233	setgrent();
234 nextgroup:
235	while ((grp = getgrent()) != NULL) {
236		if (grp->gr_gid == agroup)
237			continue;
238		for (i = 0; grp->gr_mem[i]; i++) {
239			if (strcmp(grp->gr_mem[i], uname) != 0)
240				continue;
241			for (i = 0; i < MIN(ngroups, maxgroups); i++) {
242				if (grp->gr_gid == groups[i])
243					goto nextgroup;
244			}
245			if (ngroups < maxgroups)
246				groups[ngroups] = grp->gr_gid;
247			else
248				ret = -1;
249			ngroups++;
250			break;
251		}
252	}
253	endgrent();
254	*grpcnt = ngroups;
255	return ret;
256}
257
258static int
259grscan(int search, gid_t gid, const char *name, const char *user)
260{
261
262	if (_gr_filesdone)
263		return 0;
264	for (;;) {
265		if (!fgets(grline, sizeof(grline), _gr_fp)) {
266			if (!search)
267				_gr_filesdone = 1;
268			return 0;
269		}
270		/* skip lines that are too big */
271		if (!strchr(grline, '\n')) {
272			int ch;
273
274			while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
275				;
276			continue;
277		}
278		if (grmatchline(search, gid, name, user))
279			return 1;
280	}
281	/* NOTREACHED */
282}
283
284static int
285grmatchline(int search, gid_t gid, const char *name, const char *user)
286{
287	unsigned long	id;
288	__aconst char	**m;
289	char		*cp, *bp, *ep;
290
291	/* name may be NULL if search is nonzero */
292
293	bp = grline;
294	_gr_group.gr_name = strsep(&bp, ":\n");
295	if (search && name && strcmp(_gr_group.gr_name, name))
296		return 0;
297	_gr_group.gr_passwd = strsep(&bp, ":\n");
298	if (!(cp = strsep(&bp, ":\n")))
299		return 0;
300	id = strtoul(cp, &ep, 10);
301	if (id > GID_MAX || *ep != '\0')
302		return 0;
303	_gr_group.gr_gid = (gid_t)id;
304	if (search && name == NULL && _gr_group.gr_gid != gid)
305		return 0;
306	cp = NULL;
307	if (bp == NULL)
308		return 0;
309	for (_gr_group.gr_mem = m = members;; bp++) {
310		if (m == &members[MAXGRP - 1])
311			break;
312		if (*bp == ',') {
313			if (cp) {
314				*bp = '\0';
315				*m++ = cp;
316				cp = NULL;
317			}
318		} else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
319			if (cp) {
320				*bp = '\0';
321				*m++ = cp;
322			}
323			break;
324		} else if (cp == NULL)
325			cp = bp;
326	}
327	*m = NULL;
328	if (user) {
329		for (m = members; *m; m++)
330			if (!strcmp(user, *m))
331				return 1;
332		return 0;
333	}
334	return 1;
335}
336