xref: /openbsd-src/lib/libc/gen/getgrent.c (revision 5054e3e78af0749a9bb00ba9a024b3ee2d90290f)
1 /*	$OpenBSD: getgrent.c,v 1.35 2009/11/09 00:18:27 kurt 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 #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	(1024+200*sizeof(char*))
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 	*result = getgrnam_gs(name, grp, (struct group_storage *)buffer);
138 	if (*result == NULL)
139 		ret = errno;
140 	else
141 		ret = 0;
142 	errno = errnosave;
143 	return ret;
144 }
145 
146 static struct group *
147 getgrgid_gs(gid_t gid, struct group *p_gr, struct group_storage *gs)
148 {
149 	int rval;
150 
151 	_THREAD_PRIVATE_MUTEX_LOCK(gr);
152 	if (!start_gr())
153 		rval = 0;
154 	else {
155 		rval = grscan(1, gid, NULL, p_gr, gs, NULL);
156 		if (!_gr_stayopen)
157 			endgrent_basic();
158 	}
159 	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
160 	return(rval ? p_gr : NULL);
161 }
162 
163 struct group *
164 getgrgid(gid_t gid)
165 {
166 	struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr, _gr_group, NULL);
167 	struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage,
168 	    gr_storage, NULL);
169 
170 	return getgrgid_gs(gid, p_gr, gs);
171 }
172 
173 int
174 getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
175 	struct group **result)
176 {
177 	int errnosave;
178 	int ret;
179 
180 	if (bufsize < GETGR_R_SIZE_MAX)
181 		return ERANGE;
182 	errnosave = errno;
183 	*result = getgrgid_gs(gid, grp, (struct group_storage *)buffer);
184 	if (*result == NULL)
185 		ret = errno;
186 	else
187 		ret = 0;
188 	errno = errnosave;
189 	return ret;
190 }
191 
192 static int
193 start_gr(void)
194 {
195 	if (_gr_fp) {
196 		rewind(_gr_fp);
197 #ifdef YP
198 		__ypmode = 0;
199 		if (__ypcurrent)
200 			free(__ypcurrent);
201 		__ypcurrent = NULL;
202 		if (__ypexhead)
203 			__ypexclude_free(&__ypexhead);
204 		__ypexhead = NULL;
205 #endif
206 		return(1);
207 	}
208 	return((_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0);
209 }
210 
211 void
212 setgrent(void)
213 {
214 	(void) setgroupent(0);
215 }
216 
217 int
218 setgroupent(int stayopen)
219 {
220 	int retval;
221 
222 	_THREAD_PRIVATE_MUTEX_LOCK(gr);
223 	if (!start_gr())
224 		retval = 0;
225 	else {
226 		_gr_stayopen = stayopen;
227 		retval = 1;
228 	}
229 	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
230 	return (retval);
231 }
232 
233 static
234 void
235 endgrent_basic(void)
236 {
237 	if (_gr_fp) {
238 		(void)fclose(_gr_fp);
239 		_gr_fp = NULL;
240 #ifdef YP
241 		__ypmode = 0;
242 		if (__ypcurrent)
243 			free(__ypcurrent);
244 		__ypcurrent = NULL;
245 		if (__ypexhead)
246 			__ypexclude_free(&__ypexhead);
247 		__ypexhead = NULL;
248 #endif
249 	}
250 }
251 
252 void
253 endgrent(void)
254 {
255 	_THREAD_PRIVATE_MUTEX_LOCK(gr);
256 	endgrent_basic();
257 	_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
258 }
259 
260 static int
261 grscan(int search, gid_t gid, const char *name, struct group *p_gr,
262     struct group_storage *gs, int *foundyp)
263 {
264 	char *cp, **m;
265 	char *bp, *endp;
266 	u_long ul;
267 #ifdef YP
268 	char *key, *data;
269 	int keylen, datalen;
270 	int r;
271 #endif
272 	char **members;
273 	char *line;
274 
275 	if (gs == NULL)
276 		return 0;
277 	members = gs->members;
278 	line = gs->line;
279 
280 	for (;;) {
281 #ifdef YP
282 		if (__ypmode) {
283 			if (__ypcurrent) {
284 				r = yp_next(__ypdomain, "group.byname",
285 				    __ypcurrent, __ypcurrentlen,
286 				    &key, &keylen, &data, &datalen);
287 				free(__ypcurrent);
288 				if (r) {
289 					__ypcurrent = NULL;
290 					__ypmode = 0;
291 					free(data);
292 					continue;
293 				}
294 				__ypcurrent = key;
295 				__ypcurrentlen = keylen;
296 				bcopy(data, line, datalen);
297 				free(data);
298 			} else {
299 				r = yp_first(__ypdomain, "group.byname",
300 				    &__ypcurrent, &__ypcurrentlen,
301 				    &data, &datalen);
302 				if (r) {
303 					__ypmode = 0;
304 					free(data);
305 					continue;
306 				}
307 				bcopy(data, line, datalen);
308 				free(data);
309 			}
310 			line[datalen] = '\0';
311 			bp = line;
312 			goto parse;
313 		}
314 #endif
315 		if (!fgets(line, sizeof(gs->line), _gr_fp))
316 			return(0);
317 		bp = line;
318 		/* skip lines that are too big */
319 		if (!strchr(line, '\n')) {
320 			int ch;
321 
322 			while ((ch = getc_unlocked(_gr_fp)) != '\n' &&
323 			    ch != EOF)
324 				;
325 			continue;
326 		}
327 #ifdef YP
328 		if (line[0] == '+' || line[0] == '-') {
329 			if (__ypdomain == NULL &&
330 			    yp_get_default_domain(&__ypdomain))
331 				goto parse;
332 			switch (yp_bind(__ypdomain)) {
333 			case 0:
334 				break;
335 			case YPERR_BADARGS:
336 			case YPERR_YPBIND:
337 				goto parse;
338 			default:
339 				return 0;
340 			}
341 		}
342 		if (line[0] == '+') {
343 			switch (line[1]) {
344 			case ':':
345 			case '\0':
346 			case '\n':
347 				if (foundyp) {
348 					*foundyp = 1;
349 					return (NULL);
350 				}
351 				if (!search) {
352 					__ypmode = 1;
353 					continue;
354 				}
355 				if (name) {
356 					r = yp_match(__ypdomain,
357 					    "group.byname", name, strlen(name),
358 					    &data, &datalen);
359 				} else {
360 					char buf[20];
361 					snprintf(buf, sizeof buf, "%u", gid);
362 					r = yp_match(__ypdomain, "group.bygid",
363 					    buf, strlen(buf), &data, &datalen);
364 				}
365 				if (r != 0)
366 					continue;
367 				bcopy(data, line, datalen);
368 				free(data);
369 				line[datalen] = '\0';
370 				bp = line;
371 				p_gr->gr_name = strsep(&bp, ":\n");
372 				if (__ypexclude_is(&__ypexhead, p_gr->gr_name))
373 					continue;
374 				p_gr->gr_passwd = strsep(&bp, ":\n");
375 				if (!(cp = strsep(&bp, ":\n")))
376 					continue;
377 				if (name) {
378 					ul = strtoul(cp, &endp, 10);
379 					if (*endp != '\0' || endp == cp ||
380 					    ul >= GID_MAX)
381 						continue;
382 					p_gr->gr_gid = ul;
383 				} else
384 					p_gr->gr_gid = gid;
385 				goto found_it;
386 			default:
387 				bp = strsep(&bp, ":\n") + 1;
388 				if (search && name && strcmp(bp, name) ||
389 				    __ypexclude_is(&__ypexhead, bp))
390 					continue;
391 				r = yp_match(__ypdomain, "group.byname",
392 				    bp, strlen(bp), &data, &datalen);
393 				if (r)
394 					continue;
395 				bcopy(data, line, datalen);
396 				free(data);
397 				line[datalen] = '\0';
398 				bp = line;
399 			}
400 		} else if (line[0] == '-') {
401 			if(!__ypexclude_add(&__ypexhead,
402 					    strsep(&line, ":\n") + 1))
403 				if (foundyp) {
404 					*foundyp = -1;
405 					return (NULL);
406 				}
407 			continue;
408 		}
409 parse:
410 #endif
411 		p_gr->gr_name = strsep(&bp, ":\n");
412 		if (search && name && strcmp(p_gr->gr_name, name))
413 			continue;
414 #ifdef YP
415 		if (__ypmode && __ypexclude_is(&__ypexhead, p_gr->gr_name))
416 			continue;
417 #endif
418 		p_gr->gr_passwd = strsep(&bp, ":\n");
419 		if (!(cp = strsep(&bp, ":\n")))
420 			continue;
421 		ul = strtoul(cp, &endp, 10);
422 		if (endp == cp || *endp != '\0' || ul >= GID_MAX)
423 			continue;
424 		p_gr->gr_gid = ul;
425 		if (search && name == NULL && p_gr->gr_gid != gid)
426 			continue;
427 #ifdef YP
428 	found_it:
429 #endif
430 		cp = NULL;
431 		if (bp == NULL)
432 			continue;
433 		for (m = p_gr->gr_mem = members;; bp++) {
434 			if (m == &members[MAXGRP - 1])
435 				break;
436 			if (*bp == ',') {
437 				if (cp) {
438 					*bp = '\0';
439 					*m++ = cp;
440 					cp = NULL;
441 				}
442 			} else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
443 				if (cp) {
444 					*bp = '\0';
445 					*m++ = cp;
446 				}
447 				break;
448 			} else if (cp == NULL)
449 				cp = bp;
450 		}
451 		*m = NULL;
452 		return(1);
453 	}
454 	/* NOTREACHED */
455 }
456