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