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