1 /*	$OpenBSD: getgrent.c,v 1.23 2005/08/08 08:05:34 espie 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/param.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <grp.h>
37 #include <errno.h>
38 #include "thread_private.h"
39 
40 __RCSID("$MirOS: src/lib/libc/gen/getgrent.c,v 1.3 2005/09/22 20:39:59 tg Exp $");
41 
42 /* This global storage is locked for the non-rentrant functions */
43 _THREAD_PRIVATE_KEY(gr_storage);
44 static struct group_storage {
45 #define	MAXGRP		200
46 	char *members[MAXGRP];
47 #define	MAXLINELENGTH	1024
48 	char line[MAXLINELENGTH];
49 } gr_storage;
50 #define GETGR_R_SIZE_MAX	(1024+200*sizeof(char*))
51 
52 /* File pointers are locked with the 'gr' mutex */
53 _THREAD_PRIVATE_KEY(gr);
54 static FILE *_gr_fp;
55 static struct group _gr_group;
56 static int _gr_stayopen;
57 static int grscan(int, gid_t, const char *, struct group *, struct group_storage *);
58 static int start_gr(void);
59 static void endgrent_basic(void);
60 
61 static struct group *getgrnam_gs(const char *, struct group *,
62 	struct group_storage *);
63 static struct group *getgrgid_gs(gid_t, struct group *,
64 	struct group_storage *);
65 
66 struct group *
getgrent(void)67 getgrent(void)
68 {
69 	struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr, _gr_group, NULL);
70 	struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage,
71 	    gr_storage, NULL);
72 
73 	_THREAD_PRIVATE_MUTEX_LOCK(gr);
74 	if ((!_gr_fp && !start_gr()) || !grscan(0, 0, NULL, p_gr, gs))
75 		p_gr = NULL;
76 	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
77 	return (p_gr);
78 }
79 
80 static struct group *
getgrnam_gs(const char * name,struct group * p_gr,struct group_storage * gs)81 getgrnam_gs(const char *name, struct group *p_gr, struct group_storage *gs)
82 {
83 	int rval;
84 
85 	_THREAD_PRIVATE_MUTEX_LOCK(gr);
86 	if (!start_gr())
87 		rval = 0;
88 	else {
89 		rval = grscan(1, 0, name, p_gr, gs);
90 		if (!_gr_stayopen)
91 			endgrent_basic();
92 	}
93 	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
94 	return(rval ? p_gr : NULL);
95 }
96 
97 struct group *
getgrnam(const char * name)98 getgrnam(const char *name)
99 {
100 	struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr,_gr_group,NULL);
101 	struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage,
102 	    gr_storage, NULL);
103 
104 	return getgrnam_gs(name, p_gr, gs);
105 }
106 
107 int
getgrnam_r(const char * name,struct group * grp,char * buffer,size_t bufsize,struct group ** result)108 getgrnam_r(const char *name, struct group *grp, char *buffer,
109 	size_t bufsize, struct group **result)
110 {
111 	int errnosave;
112 	int ret;
113 
114 	if (bufsize < GETGR_R_SIZE_MAX)
115 		return ERANGE;
116 	errnosave = errno;
117 	*result = getgrnam_gs(name, grp, (struct group_storage *)buffer);
118 	if (*result == NULL)
119 		ret = errno;
120 	else
121 		ret = 0;
122 	errno = errnosave;
123 	return ret;
124 }
125 
126 static struct group *
getgrgid_gs(gid_t gid,struct group * p_gr,struct group_storage * gs)127 getgrgid_gs(gid_t gid, struct group *p_gr, struct group_storage *gs)
128 {
129 	int rval;
130 
131 	_THREAD_PRIVATE_MUTEX_LOCK(gr);
132 	if (!start_gr())
133 		rval = 0;
134 	else {
135 		rval = grscan(1, gid, NULL, p_gr, gs);
136 		if (!_gr_stayopen)
137 			endgrent_basic();
138 	}
139 	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
140 	return(rval ? p_gr : NULL);
141 }
142 
143 struct group *
getgrgid(gid_t gid)144 getgrgid(gid_t gid)
145 {
146 	struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr, _gr_group, NULL);
147 	struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage,
148 	    gr_storage, NULL);
149 
150 	return getgrgid_gs(gid, p_gr, gs);
151 }
152 
153 int
getgrgid_r(gid_t gid,struct group * grp,char * buffer,size_t bufsize,struct group ** result)154 getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
155 	struct group **result)
156 {
157 	int errnosave;
158 	int ret;
159 
160 	if (bufsize < GETGR_R_SIZE_MAX)
161 		return ERANGE;
162 	errnosave = errno;
163 	*result = getgrgid_gs(gid, grp, (struct group_storage *)buffer);
164 	if (*result == NULL)
165 		ret = errno;
166 	else
167 		ret = 0;
168 	errno = errnosave;
169 	return ret;
170 }
171 
172 static int
start_gr(void)173 start_gr(void)
174 {
175 	if (_gr_fp) {
176 		rewind(_gr_fp);
177 		return(1);
178 	}
179 	return((_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0);
180 }
181 
182 void
setgrent(void)183 setgrent(void)
184 {
185 	(void) setgroupent(0);
186 }
187 
188 int
setgroupent(int stayopen)189 setgroupent(int stayopen)
190 {
191 	int retval;
192 
193 	_THREAD_PRIVATE_MUTEX_LOCK(gr);
194 	if (!start_gr())
195 		retval = 0;
196 	else {
197 		_gr_stayopen = stayopen;
198 		retval = 1;
199 	}
200 	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
201 	return (retval);
202 }
203 
204 static
205 void
endgrent_basic(void)206 endgrent_basic(void)
207 {
208 	if (_gr_fp) {
209 		(void)fclose(_gr_fp);
210 		_gr_fp = NULL;
211 	}
212 }
213 
214 void
endgrent(void)215 endgrent(void)
216 {
217 	_THREAD_PRIVATE_MUTEX_LOCK(gr);
218 	endgrent_basic();
219 	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
220 }
221 
222 static int
grscan(int search,gid_t gid,const char * name,struct group * p_gr,struct group_storage * gs)223 grscan(int search, gid_t gid, const char *name, struct group *p_gr,
224     struct group_storage *gs)
225 {
226 	char *cp, **m;
227 	char *bp, *endp;
228 	u_long ul;
229 	char **members;
230 	char *line;
231 
232 	if (gs == NULL)
233 		return 0;
234 	members = gs->members;
235 	line = gs->line;
236 
237 	for (;;) {
238 		if (!fgets(line, sizeof(gs->line), _gr_fp))
239 			return(0);
240 		bp = line;
241 		/* skip lines that are too big */
242 		if (!strchr(line, '\n')) {
243 			int ch;
244 
245 			while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
246 				;
247 			continue;
248 		}
249 		p_gr->gr_name = strsep(&bp, ":\n");
250 		if (search && name && strcmp(p_gr->gr_name, name))
251 			continue;
252 		p_gr->gr_passwd = strsep(&bp, ":\n");
253 		if (!(cp = strsep(&bp, ":\n")))
254 			continue;
255 		ul = strtoul(cp, &endp, 10);
256 		if (endp == cp || *endp != '\0' || ul >= GID_MAX)
257 			continue;
258 		p_gr->gr_gid = ul;
259 		if (search && name == NULL && p_gr->gr_gid != gid)
260 			continue;
261 /*	found_it:	*/
262 		cp = NULL;
263 		if (bp == NULL)
264 			continue;
265 		for (m = p_gr->gr_mem = members;; bp++) {
266 			if (m == &members[MAXGRP - 1])
267 				break;
268 			if (*bp == ',') {
269 				if (cp) {
270 					*bp = '\0';
271 					*m++ = cp;
272 					cp = NULL;
273 				}
274 			} else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
275 				if (cp) {
276 					*bp = '\0';
277 					*m++ = cp;
278 				}
279 				break;
280 			} else if (cp == NULL)
281 				cp = bp;
282 		}
283 		*m = NULL;
284 		return(1);
285 	}
286 	/* NOTREACHED */
287 }
288