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