1 /* $OpenBSD: getgrent.c,v 1.48 2019/07/02 15:54:05 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 <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 *
_getgrent_yp(int * foundyp)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 *
getgrent(void)96 getgrent(void)
97 {
98 return (_getgrent_yp(NULL));
99 }
100
101 static struct group *
getgrnam_gs(const char * name,struct group * p_gr,struct group_storage * gs)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 *
getgrnam(const char * name)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
getgrnam_r(const char * name,struct group * grp,char * buffer,size_t bufsize,struct group ** result)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 *
getgrgid_gs(gid_t gid,struct group * p_gr,struct group_storage * gs)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 *
getgrgid(gid_t gid)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
getgrgid_r(gid_t gid,struct group * grp,char * buffer,size_t bufsize,struct group ** result)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
start_gr(void)198 start_gr(void)
199 {
200 #ifdef YP
201 int saved_errno = errno;
202 #endif
203
204 if (_gr_fp) {
205 rewind(_gr_fp);
206 #ifdef YP
207 __ypmode = 0;
208 free(__ypcurrent);
209 __ypcurrent = NULL;
210 if (__ypexhead)
211 __ypexclude_free(&__ypexhead);
212 __ypexhead = NULL;
213 #endif
214 return(1);
215 }
216
217 #ifdef YP
218 /*
219 * Hint to the kernel that a passwd database operation is happening.
220 */
221 saved_errno = errno;
222 (void)access("/var/run/ypbind.lock", R_OK);
223 errno = saved_errno;
224 #endif
225
226 return((_gr_fp = fopen(_PATH_GROUP, "re")) ? 1 : 0);
227 }
228
229 void
setgrent(void)230 setgrent(void)
231 {
232 int saved_errno;
233
234 saved_errno = errno;
235 setgroupent(0);
236 errno = saved_errno;
237 }
238 DEF_WEAK(setgrent);
239
240 int
setgroupent(int stayopen)241 setgroupent(int stayopen)
242 {
243 int retval;
244
245 _THREAD_PRIVATE_MUTEX_LOCK(gr);
246 if (!start_gr())
247 retval = 0;
248 else {
249 _gr_stayopen = stayopen;
250 retval = 1;
251 }
252 _THREAD_PRIVATE_MUTEX_UNLOCK(gr);
253 return (retval);
254 }
255 DEF_WEAK(setgroupent);
256
257 static
258 void
endgrent_basic(void)259 endgrent_basic(void)
260 {
261 int saved_errno;
262
263 if (_gr_fp) {
264 saved_errno = errno;
265 fclose(_gr_fp);
266 _gr_fp = NULL;
267 #ifdef YP
268 __ypmode = 0;
269 free(__ypcurrent);
270 __ypcurrent = NULL;
271 if (__ypexhead)
272 __ypexclude_free(&__ypexhead);
273 __ypexhead = NULL;
274 #endif
275 errno = saved_errno;
276 }
277 }
278
279 void
endgrent(void)280 endgrent(void)
281 {
282 _THREAD_PRIVATE_MUTEX_LOCK(gr);
283 endgrent_basic();
284 _THREAD_PRIVATE_MUTEX_UNLOCK(gr);
285 }
286 DEF_WEAK(endgrent);
287
288 static int
grscan(int search,gid_t gid,const char * name,struct group * p_gr,struct group_storage * gs,int * foundyp)289 grscan(int search, gid_t gid, const char *name, struct group *p_gr,
290 struct group_storage *gs, int *foundyp)
291 {
292 char *cp, **m;
293 char *bp, *endp;
294 u_long ul;
295 #ifdef YP
296 char *key, *data;
297 int keylen, datalen;
298 int r;
299 #endif
300 char **members;
301 char *line;
302 int saved_errno;
303
304 if (gs == NULL)
305 return 0;
306 members = gs->members;
307 line = gs->line;
308 saved_errno = errno;
309
310 for (;;) {
311 #ifdef YP
312 if (__ypmode) {
313 if (__ypcurrent) {
314 r = yp_next(__ypdomain, "group.byname",
315 __ypcurrent, __ypcurrentlen,
316 &key, &keylen, &data, &datalen);
317 free(__ypcurrent);
318 __ypcurrent = key;
319 __ypcurrentlen = keylen;
320 } else {
321 r = yp_first(__ypdomain, "group.byname",
322 &__ypcurrent, &__ypcurrentlen,
323 &data, &datalen);
324 }
325 if (r) {
326 __ypmode = 0;
327 __ypcurrent = NULL;
328 if (r == YPERR_NOMORE)
329 continue;
330 else
331 return 0;
332 }
333 bcopy(data, line, datalen);
334 free(data);
335 line[datalen] = '\0';
336 bp = line;
337 goto parse;
338 }
339 #endif
340 if (!fgets(line, sizeof(gs->line), _gr_fp)) {
341 if (feof(_gr_fp) && !ferror(_gr_fp))
342 errno = saved_errno;
343 return 0;
344 }
345 bp = line;
346 /* skip lines that are too big */
347 if (!strchr(line, '\n')) {
348 int ch;
349
350 while ((ch = getc_unlocked(_gr_fp)) != '\n' &&
351 ch != EOF)
352 ;
353 continue;
354 }
355 #ifdef YP
356 if (line[0] == '+' || line[0] == '-') {
357 if (__ypdomain == NULL &&
358 yp_get_default_domain(&__ypdomain))
359 goto parse;
360 switch (yp_bind(__ypdomain)) {
361 case 0:
362 break;
363 case YPERR_BADARGS:
364 case YPERR_YPBIND:
365 goto parse;
366 default:
367 return 0;
368 }
369 }
370 if (line[0] == '+') {
371 switch (line[1]) {
372 case ':':
373 case '\0':
374 case '\n':
375 if (foundyp) {
376 *foundyp = 1;
377 errno = saved_errno;
378 return 0;
379 }
380 if (!search) {
381 __ypmode = 1;
382 continue;
383 }
384 if (name) {
385 r = yp_match(__ypdomain,
386 "group.byname", name, strlen(name),
387 &data, &datalen);
388 } else {
389 char buf[20];
390 snprintf(buf, sizeof buf, "%u", gid);
391 r = yp_match(__ypdomain, "group.bygid",
392 buf, strlen(buf), &data, &datalen);
393 }
394 switch (r) {
395 case 0:
396 break;
397 case YPERR_KEY:
398 continue;
399 default:
400 return 0;
401 }
402 bcopy(data, line, datalen);
403 free(data);
404 line[datalen] = '\0';
405 bp = line;
406 p_gr->gr_name = strsep(&bp, ":\n");
407 if (__ypexclude_is(&__ypexhead, p_gr->gr_name))
408 continue;
409 p_gr->gr_passwd = strsep(&bp, ":\n");
410 if (!(cp = strsep(&bp, ":\n")))
411 continue;
412 if (name) {
413 ul = strtoul(cp, &endp, 10);
414 if (*endp != '\0' || endp == cp ||
415 ul >= GID_MAX)
416 continue;
417 p_gr->gr_gid = ul;
418 } else
419 p_gr->gr_gid = gid;
420 goto found_it;
421 default:
422 bp = strsep(&bp, ":\n") + 1;
423 if ((search && name && strcmp(bp, name)) ||
424 __ypexclude_is(&__ypexhead, bp))
425 continue;
426 r = yp_match(__ypdomain, "group.byname",
427 bp, strlen(bp), &data, &datalen);
428 switch (r) {
429 case 0:
430 break;
431 case YPERR_KEY:
432 continue;
433 default:
434 return 0;
435 }
436 bcopy(data, line, datalen);
437 free(data);
438 line[datalen] = '\0';
439 bp = line;
440 }
441 } else if (line[0] == '-') {
442 if (__ypexclude_add(&__ypexhead,
443 strsep(&line, ":\n") + 1))
444 return 0;
445 if (foundyp) {
446 *foundyp = -1;
447 errno = saved_errno;
448 return 0;
449 }
450 continue;
451 }
452 parse:
453 #endif
454 p_gr->gr_name = strsep(&bp, ":\n");
455 if (search && name && strcmp(p_gr->gr_name, name))
456 continue;
457 #ifdef YP
458 if (__ypmode && __ypexclude_is(&__ypexhead, p_gr->gr_name))
459 continue;
460 #endif
461 p_gr->gr_passwd = strsep(&bp, ":\n");
462 if (!(cp = strsep(&bp, ":\n")))
463 continue;
464 ul = strtoul(cp, &endp, 10);
465 if (endp == cp || *endp != '\0' || ul >= GID_MAX)
466 continue;
467 p_gr->gr_gid = ul;
468 if (search && name == NULL && p_gr->gr_gid != gid)
469 continue;
470 #ifdef YP
471 found_it:
472 #endif
473 cp = NULL;
474 if (bp == NULL)
475 continue;
476 for (m = p_gr->gr_mem = members;; bp++) {
477 if (m == &members[MAXGRP - 1])
478 break;
479 if (*bp == ',') {
480 if (cp) {
481 *bp = '\0';
482 *m++ = cp;
483 cp = NULL;
484 }
485 } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
486 if (cp) {
487 *bp = '\0';
488 *m++ = cp;
489 }
490 break;
491 } else if (cp == NULL)
492 cp = bp;
493 }
494 *m = NULL;
495 errno = saved_errno;
496 return 1;
497 }
498 /* NOTREACHED */
499 }
500