xref: /openbsd-src/lib/libc/gen/getgrent.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: getgrent.c,v 1.26 2008/08/25 22:30:19 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 <sys/param.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <grp.h>
38 #include <errno.h>
39 #ifdef YP
40 #include <rpc/rpc.h>
41 #include <rpcsvc/yp.h>
42 #include <rpcsvc/ypclnt.h>
43 #include "ypinternal.h"
44 #endif
45 #include "thread_private.h"
46 
47 /* This global storage is locked for the non-rentrant functions */
48 _THREAD_PRIVATE_KEY(gr_storage);
49 static struct group_storage {
50 #define	MAXGRP		200
51 	char *members[MAXGRP];
52 #define	MAXLINELENGTH	1024
53 	char line[MAXLINELENGTH];
54 } gr_storage;
55 #define GETGR_R_SIZE_MAX	(1024+200*sizeof(char*))
56 
57 /* File pointers are locked with the 'gr' mutex */
58 _THREAD_PRIVATE_KEY(gr);
59 static FILE *_gr_fp;
60 static struct group _gr_group;
61 static int _gr_stayopen;
62 static int grscan(int, gid_t, const char *, struct group *, struct group_storage *,
63 	int *);
64 static int start_gr(void);
65 static void endgrent_basic(void);
66 
67 static struct group *getgrnam_gs(const char *, struct group *,
68 	struct group_storage *);
69 static struct group *getgrgid_gs(gid_t, struct group *,
70 	struct group_storage *);
71 
72 #ifdef YP
73 enum _ypmode { YPMODE_NONE, YPMODE_FULL, YPMODE_NAME };
74 static enum _ypmode __ypmode;
75 static char	*__ypcurrent, *__ypdomain;
76 static int	__ypcurrentlen;
77 #endif
78 
79 struct group *
80 _getgrent_yp(int *foundyp)
81 {
82 	struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr, _gr_group, NULL);
83 	struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage,
84 	    gr_storage, NULL);
85 
86 	_THREAD_PRIVATE_MUTEX_LOCK(gr);
87 	if ((!_gr_fp && !start_gr()) || !grscan(0, 0, NULL, p_gr, gs, foundyp))
88 		p_gr = NULL;
89 	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
90 	return (p_gr);
91 }
92 
93 struct group *
94 getgrent(void)
95 {
96 	return (_getgrent_yp(NULL));
97 }
98 
99 static struct group *
100 getgrnam_gs(const char *name, struct group *p_gr, 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, NULL);
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(const char *name)
118 {
119 	struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr,_gr_group,NULL);
120 	struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage,
121 	    gr_storage, NULL);
122 
123 	return getgrnam_gs(name, p_gr, gs);
124 }
125 
126 int
127 getgrnam_r(const char *name, struct group *grp, char *buffer,
128 	size_t bufsize, struct group **result)
129 {
130 	int errnosave;
131 	int ret;
132 
133 	if (bufsize < GETGR_R_SIZE_MAX)
134 		return ERANGE;
135 	errnosave = errno;
136 	*result = getgrnam_gs(name, grp, (struct group_storage *)buffer);
137 	if (*result == NULL)
138 		ret = errno;
139 	else
140 		ret = 0;
141 	errno = errnosave;
142 	return ret;
143 }
144 
145 static struct group *
146 getgrgid_gs(gid_t gid, struct group *p_gr, struct group_storage *gs)
147 {
148 	int rval;
149 
150 	_THREAD_PRIVATE_MUTEX_LOCK(gr);
151 	if (!start_gr())
152 		rval = 0;
153 	else {
154 		rval = grscan(1, gid, NULL, p_gr, gs, NULL);
155 		if (!_gr_stayopen)
156 			endgrent_basic();
157 	}
158 	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
159 	return(rval ? p_gr : NULL);
160 }
161 
162 struct group *
163 getgrgid(gid_t gid)
164 {
165 	struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr, _gr_group, NULL);
166 	struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage,
167 	    gr_storage, NULL);
168 
169 	return getgrgid_gs(gid, p_gr, gs);
170 }
171 
172 int
173 getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
174 	struct group **result)
175 {
176 	int errnosave;
177 	int ret;
178 
179 	if (bufsize < GETGR_R_SIZE_MAX)
180 		return ERANGE;
181 	errnosave = errno;
182 	*result = getgrgid_gs(gid, grp, (struct group_storage *)buffer);
183 	if (*result == NULL)
184 		ret = errno;
185 	else
186 		ret = 0;
187 	errno = errnosave;
188 	return ret;
189 }
190 
191 static int
192 start_gr(void)
193 {
194 	if (_gr_fp) {
195 		rewind(_gr_fp);
196 #ifdef YP
197 		__ypmode = YPMODE_NONE;
198 		if (__ypcurrent)
199 			free(__ypcurrent);
200 		__ypcurrent = NULL;
201 #endif
202 		return(1);
203 	}
204 	return((_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0);
205 }
206 
207 void
208 setgrent(void)
209 {
210 	(void) setgroupent(0);
211 }
212 
213 int
214 setgroupent(int stayopen)
215 {
216 	int retval;
217 
218 	_THREAD_PRIVATE_MUTEX_LOCK(gr);
219 	if (!start_gr())
220 		retval = 0;
221 	else {
222 		_gr_stayopen = stayopen;
223 		retval = 1;
224 	}
225 	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
226 	return (retval);
227 }
228 
229 static
230 void
231 endgrent_basic(void)
232 {
233 	if (_gr_fp) {
234 		(void)fclose(_gr_fp);
235 		_gr_fp = NULL;
236 #ifdef YP
237 		__ypmode = YPMODE_NONE;
238 		if (__ypcurrent)
239 			free(__ypcurrent);
240 		__ypcurrent = NULL;
241 #endif
242 	}
243 }
244 
245 void
246 endgrent(void)
247 {
248 	_THREAD_PRIVATE_MUTEX_LOCK(gr);
249 	endgrent_basic();
250 	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
251 }
252 
253 static int
254 grscan(int search, gid_t gid, const char *name, struct group *p_gr,
255     struct group_storage *gs, int *foundyp)
256 {
257 	char *cp, **m;
258 	char *bp, *endp;
259 	u_long ul;
260 #ifdef YP
261 	char *key, *data;
262 	int keylen, datalen;
263 	int r;
264 	char *grname = (char *)NULL;
265 #endif
266 	char **members;
267 	char *line;
268 
269 	if (gs == NULL)
270 		return 0;
271 	members = gs->members;
272 	line = gs->line;
273 
274 	for (;;) {
275 #ifdef YP
276 		if (__ypmode != YPMODE_NONE) {
277 
278 			if (!__ypdomain) {
279 				if (yp_get_default_domain(&__ypdomain)) {
280 					__ypmode = YPMODE_NONE;
281 					if (grname != (char *)NULL) {
282 						free(grname);
283 						grname = (char *)NULL;
284 					}
285 					continue;
286 				}
287 			}
288 			switch (__ypmode) {
289 			case YPMODE_FULL:
290 				if (__ypcurrent) {
291 					r = yp_next(__ypdomain, "group.byname",
292 					    __ypcurrent, __ypcurrentlen,
293 					    &key, &keylen, &data, &datalen);
294 					free(__ypcurrent);
295 					if (r != 0) {
296 						__ypcurrent = NULL;
297 						__ypmode = YPMODE_NONE;
298 						free(data);
299 						continue;
300 					}
301 					__ypcurrent = key;
302 					__ypcurrentlen = keylen;
303 					bcopy(data, line, datalen);
304 					free(data);
305 				} else {
306 					r = yp_first(__ypdomain, "group.byname",
307 					    &__ypcurrent, &__ypcurrentlen,
308 					    &data, &datalen);
309 					if (r != 0) {
310 						__ypmode = YPMODE_NONE;
311 						free(data);
312 						continue;
313 					}
314 					bcopy(data, line, datalen);
315 					free(data);
316 				}
317 				break;
318 			case YPMODE_NAME:
319 				if (grname != (char *)NULL) {
320 					r = yp_match(__ypdomain, "group.byname",
321 					    grname, strlen(grname),
322 					    &data, &datalen);
323 					__ypmode = YPMODE_NONE;
324 					free(grname);
325 					grname = (char *)NULL;
326 					if (r != 0) {
327 						free(data);
328 						continue;
329 					}
330 					bcopy(data, line, datalen);
331 					free(data);
332 				} else {
333 					__ypmode = YPMODE_NONE;	/* ??? */
334 					continue;
335 				}
336 				break;
337 			case YPMODE_NONE:
338 				/* NOTREACHED */
339 				break;
340 			}
341 			line[datalen] = '\0';
342 			bp = line;
343 			goto parse;
344 		}
345 #endif
346 		if (!fgets(line, sizeof(gs->line), _gr_fp))
347 			return(0);
348 		bp = line;
349 		/* skip lines that are too big */
350 		if (!strchr(line, '\n')) {
351 			int ch;
352 
353 			while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
354 				;
355 			continue;
356 		}
357 #ifdef YP
358 		if (line[0] == '+') {
359 			switch (line[1]) {
360 			case ':':
361 			case '\0':
362 			case '\n':
363 				if (foundyp) {
364 					*foundyp = 1;
365 					return (NULL);
366 				}
367 				if (_yp_check(NULL)) {
368 					if (!search) {
369 						__ypmode = YPMODE_FULL;
370 						continue;
371 					}
372 					if (!__ypdomain &&
373 					    yp_get_default_domain(&__ypdomain))
374 						continue;
375 					if (name) {
376 						r = yp_match(__ypdomain,
377 						    "group.byname",
378 						    name, strlen(name),
379 						    &data, &datalen);
380 					} else {
381 						char buf[20];
382 
383 						snprintf(buf, sizeof buf,
384 						    "%u", gid);
385 						r = yp_match(__ypdomain,
386 						    "group.bygid",
387 						    buf, strlen(buf),
388 						    &data, &datalen);
389 					}
390 					if (r != 0)
391 						continue;
392 					bcopy(data, line, datalen);
393 					free(data);
394 					line[datalen] = '\0';
395 					bp = line;
396 					p_gr->gr_name = strsep(&bp, ":\n");
397 					p_gr->gr_passwd =
398 						strsep(&bp, ":\n");
399 					if (!(cp = strsep(&bp, ":\n")))
400 						continue;
401 					if (name) {
402 						ul = strtoul(cp, &endp, 10);
403 						if (*endp != '\0' ||
404 						    endp == cp || ul >= GID_MAX)
405 							continue;
406 						p_gr->gr_gid = ul;
407 					} else
408 						p_gr->gr_gid = gid;
409 					goto found_it;
410 				}
411 				break;
412 			default:
413 				if (_yp_check(NULL)) {
414 					char *tptr;
415 
416 					tptr = strsep(&bp, ":\n");
417 					tptr++;
418 					if (search && name && strcmp(tptr, name))
419 						continue;
420 					__ypmode = YPMODE_NAME;
421 					grname = strdup(tptr);
422 					continue;
423 				}
424 				break;
425 			}
426 		}
427 parse:
428 #endif
429 		p_gr->gr_name = strsep(&bp, ":\n");
430 		if (search && name && strcmp(p_gr->gr_name, name))
431 			continue;
432 		p_gr->gr_passwd = strsep(&bp, ":\n");
433 		if (!(cp = strsep(&bp, ":\n")))
434 			continue;
435 		ul = strtoul(cp, &endp, 10);
436 		if (endp == cp || *endp != '\0' || ul >= GID_MAX)
437 			continue;
438 		p_gr->gr_gid = ul;
439 		if (search && name == NULL && p_gr->gr_gid != gid)
440 			continue;
441 #ifdef YP
442 	found_it:
443 #endif
444 		cp = NULL;
445 		if (bp == NULL)
446 			continue;
447 		for (m = p_gr->gr_mem = members;; bp++) {
448 			if (m == &members[MAXGRP - 1])
449 				break;
450 			if (*bp == ',') {
451 				if (cp) {
452 					*bp = '\0';
453 					*m++ = cp;
454 					cp = NULL;
455 				}
456 			} else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
457 				if (cp) {
458 					*bp = '\0';
459 					*m++ = cp;
460 				}
461 				break;
462 			} else if (cp == NULL)
463 				cp = bp;
464 		}
465 		*m = NULL;
466 		return(1);
467 	}
468 	/* NOTREACHED */
469 }
470