getgrent.c revision 1.39 1 /* $NetBSD: getgrent.c,v 1.39 2000/01/22 22:19:10 mycroft Exp $ */
2
3 /*
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
6 * Portions Copyright (c) 1994, Jason Downs. All Rights Reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #include <sys/cdefs.h>
38 #if defined(LIBC_SCCS) && !defined(lint)
39 #if 0
40 static char sccsid[] = "@(#)getgrent.c 8.2 (Berkeley) 3/21/94";
41 #else
42 __RCSID("$NetBSD: getgrent.c,v 1.39 2000/01/22 22:19:10 mycroft Exp $");
43 #endif
44 #endif /* LIBC_SCCS and not lint */
45
46 #include "namespace.h"
47
48 #include <sys/types.h>
49
50 #include <assert.h>
51 #include <errno.h>
52 #include <grp.h>
53 #include <limits.h>
54 #include <nsswitch.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <syslog.h>
59
60 #ifdef __STDC__
61 #include <stdarg.h>
62 #else
63 #include <varargs.h>
64 #endif
65
66 #ifdef HESIOD
67 #include <hesiod.h>
68 #endif
69 #ifdef YP
70 #include <rpc/rpc.h>
71 #include <rpcsvc/yp_prot.h>
72 #include <rpcsvc/ypclnt.h>
73 #endif
74
75 #if defined(YP) || defined(HESIOD)
76 #define _GROUP_COMPAT
77 #endif
78
79 #ifdef __weak_alias
80 __weak_alias(endgrent,_endgrent)
81 __weak_alias(getgrent,_getgrent)
82 __weak_alias(getgrgid,_getgrgid)
83 __weak_alias(getgrnam,_getgrnam)
84 __weak_alias(setgrent,_setgrent)
85 __weak_alias(setgroupent,_setgroupent)
86 #endif
87
88 static FILE *_gr_fp;
89 static struct group _gr_group;
90 static int _gr_stayopen;
91 static int _gr_filesdone;
92
93 static void grcleanup __P((void));
94 static int grscan __P((int, gid_t, const char *));
95 static int matchline __P((int, gid_t, const char *));
96 static int start_gr __P((void));
97
98 #define MAXGRP 200
99 #define MAXLINELENGTH 1024
100
101 static __aconst char *members[MAXGRP];
102 static char line[MAXLINELENGTH];
103
104 #ifdef YP
105 static char *__ypcurrent, *__ypdomain;
106 static int __ypcurrentlen;
107 static int _gr_ypdone;
108 #endif
109
110 #ifdef HESIOD
111 static int _gr_hesnum;
112 #endif
113
114 #ifdef _GROUP_COMPAT
115 enum _grmode { GRMODE_NONE, GRMODE_FULL, GRMODE_NAME };
116 static enum _grmode __grmode;
117 #endif
118
119 struct group *
120 getgrent()
121 {
122 if ((!_gr_fp && !start_gr()) || !grscan(0, 0, NULL))
123 return (NULL);
124 return &_gr_group;
125 }
126
127 struct group *
128 getgrnam(name)
129 const char *name;
130 {
131 int rval;
132
133 _DIAGASSERT(name != NULL);
134
135 if (!start_gr())
136 return NULL;
137 rval = grscan(1, 0, name);
138 if (!_gr_stayopen)
139 endgrent();
140 return (rval) ? &_gr_group : NULL;
141 }
142
143 struct group *
144 getgrgid(gid)
145 gid_t gid;
146 {
147 int rval;
148
149 if (!start_gr())
150 return NULL;
151 rval = grscan(1, gid, NULL);
152 if (!_gr_stayopen)
153 endgrent();
154 return (rval) ? &_gr_group : NULL;
155 }
156
157 void
158 grcleanup()
159 {
160 _gr_filesdone = 0;
161 #ifdef YP
162 if (__ypcurrent)
163 free(__ypcurrent);
164 __ypcurrent = NULL;
165 _gr_ypdone = 0;
166 #endif
167 #ifdef HESIOD
168 _gr_hesnum = 0;
169 #endif
170 #ifdef _GROUP_COMPAT
171 __grmode = GRMODE_NONE;
172 #endif
173 }
174
175 static int
176 start_gr()
177 {
178 grcleanup();
179 if (_gr_fp) {
180 rewind(_gr_fp);
181 return 1;
182 }
183 return (_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0;
184 }
185
186 void
187 setgrent()
188 {
189 (void) setgroupent(0);
190 }
191
192 int
193 setgroupent(stayopen)
194 int stayopen;
195 {
196 if (!start_gr())
197 return 0;
198 _gr_stayopen = stayopen;
199 return 1;
200 }
201
202 void
203 endgrent()
204 {
205 grcleanup();
206 if (_gr_fp) {
207 (void)fclose(_gr_fp);
208 _gr_fp = NULL;
209 }
210 }
211
212
213 static int _local_grscan __P((void *, void *, va_list));
214
215 /*ARGSUSED*/
216 static int
217 _local_grscan(rv, cb_data, ap)
218 void *rv;
219 void *cb_data;
220 va_list ap;
221 {
222 int search = va_arg(ap, int);
223 gid_t gid = va_arg(ap, gid_t);
224 const char *name = va_arg(ap, const char *);
225
226 if (_gr_filesdone)
227 return NS_NOTFOUND;
228 for (;;) {
229 if (!fgets(line, sizeof(line), _gr_fp)) {
230 if (!search)
231 _gr_filesdone = 1;
232 return NS_NOTFOUND;
233 }
234 /* skip lines that are too big */
235 if (!strchr(line, '\n')) {
236 int ch;
237
238 while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
239 ;
240 continue;
241 }
242 if (matchline(search, gid, name))
243 return NS_SUCCESS;
244 }
245 /* NOTREACHED */
246 }
247
248 #ifdef HESIOD
249 static int _dns_grscan __P((void *, void *, va_list));
250
251 /*ARGSUSED*/
252 static int
253 _dns_grscan(rv, cb_data, ap)
254 void *rv;
255 void *cb_data;
256 va_list ap;
257 {
258 int search = va_arg(ap, int);
259 gid_t gid = va_arg(ap, gid_t);
260 const char *name = va_arg(ap, const char *);
261
262 char **hp;
263 void *context;
264 int r;
265
266 r = NS_UNAVAIL;
267 if (!search && _gr_hesnum == -1)
268 return NS_NOTFOUND;
269 if (hesiod_init(&context) == -1)
270 return (r);
271
272 for (;;) {
273 if (search) {
274 if (name)
275 strncpy(line, name, sizeof(line));
276 else
277 snprintf(line, sizeof(line), "%u",
278 (unsigned int)gid);
279 } else {
280 snprintf(line, sizeof(line), "group-%u", _gr_hesnum);
281 _gr_hesnum++;
282 }
283
284 line[sizeof(line) - 1] = '\0';
285 hp = hesiod_resolve(context, line, "group");
286 if (hp == NULL) {
287 if (errno == ENOENT) {
288 if (!search)
289 _gr_hesnum = -1;
290 r = NS_NOTFOUND;
291 }
292 break;
293 }
294
295 /* only check first elem */
296 strncpy(line, hp[0], sizeof(line));
297 line[sizeof(line) - 1] = '\0';
298 hesiod_free_list(context, hp);
299 if (matchline(search, gid, name)) {
300 r = NS_SUCCESS;
301 break;
302 } else if (search) {
303 r = NS_NOTFOUND;
304 break;
305 }
306 }
307 hesiod_end(context);
308 return (r);
309 }
310 #endif
311
312 #ifdef YP
313 static int _nis_grscan __P((void *, void *, va_list));
314
315 /*ARGSUSED*/
316 static int
317 _nis_grscan(rv, cb_data, ap)
318 void *rv;
319 void *cb_data;
320 va_list ap;
321 {
322 int search = va_arg(ap, int);
323 gid_t gid = va_arg(ap, gid_t);
324 const char *name = va_arg(ap, const char *);
325
326 char *key, *data;
327 int keylen, datalen;
328 int r;
329
330 if(__ypdomain == NULL) {
331 switch (yp_get_default_domain(&__ypdomain)) {
332 case 0:
333 break;
334 case YPERR_RESRC:
335 return NS_TRYAGAIN;
336 default:
337 return NS_UNAVAIL;
338 }
339 }
340
341 if (search) { /* specific group or gid */
342 if (name)
343 strncpy(line, name, sizeof(line));
344 else
345 snprintf(line, sizeof(line), "%u", (unsigned int)gid);
346 line[sizeof(line) - 1] = '\0';
347 data = NULL;
348 r = yp_match(__ypdomain,
349 (name) ? "group.byname" : "group.bygid",
350 line, (int)strlen(line), &data, &datalen);
351 switch (r) {
352 case 0:
353 break;
354 case YPERR_KEY:
355 if (data)
356 free(data);
357 return NS_NOTFOUND;
358 default:
359 if (data)
360 free(data);
361 return NS_UNAVAIL;
362 }
363 data[datalen] = '\0'; /* clear trailing \n */
364 strncpy(line, data, sizeof(line));
365 line[sizeof(line) - 1] = '\0';
366 free(data);
367 if (matchline(search, gid, name))
368 return NS_SUCCESS;
369 else
370 return NS_NOTFOUND;
371 }
372
373 /* ! search */
374 if (_gr_ypdone)
375 return NS_NOTFOUND;
376 for (;;) {
377 data = NULL;
378 if(__ypcurrent) {
379 key = NULL;
380 r = yp_next(__ypdomain, "group.byname",
381 __ypcurrent, __ypcurrentlen,
382 &key, &keylen, &data, &datalen);
383 free(__ypcurrent);
384 switch (r) {
385 case 0:
386 break;
387 case YPERR_NOMORE:
388 __ypcurrent = NULL;
389 if (key)
390 free(key);
391 if (data)
392 free(data);
393 _gr_ypdone = 1;
394 return NS_NOTFOUND;
395 default:
396 if (key)
397 free(key);
398 if (data)
399 free(data);
400 return NS_UNAVAIL;
401 }
402 __ypcurrent = key;
403 __ypcurrentlen = keylen;
404 } else {
405 if (yp_first(__ypdomain, "group.byname",
406 &__ypcurrent, &__ypcurrentlen,
407 &data, &datalen)) {
408 if (data);
409 free(data);
410 return NS_UNAVAIL;
411 }
412 }
413 data[datalen] = '\0'; /* clear trailing \n */
414 strncpy(line, data, sizeof(line));
415 line[sizeof(line) - 1] = '\0';
416 free(data);
417 if (matchline(search, gid, name))
418 return NS_SUCCESS;
419 }
420 /* NOTREACHED */
421 }
422 #endif
423
424 #ifdef _GROUP_COMPAT
425 /*
426 * log an error if "files" or "compat" is specified in group_compat database
427 */
428 static int _bad_grscan __P((void *, void *, va_list));
429
430 /*ARGSUSED*/
431 static int
432 _bad_grscan(rv, cb_data, ap)
433 void *rv;
434 void *cb_data;
435 va_list ap;
436 {
437 static int warned;
438
439 _DIAGASSERT(cb_data != NULL);
440
441 if (!warned) {
442 syslog(LOG_ERR,
443 "nsswitch.conf group_compat database can't use '%s'",
444 (char *)cb_data);
445 }
446 warned = 1;
447 return NS_UNAVAIL;
448 }
449
450 /*
451 * when a name lookup in compat mode is required, look it up in group_compat
452 * nsswitch database. only Hesiod and NIS is supported - it doesn't make
453 * sense to lookup compat names from 'files' or 'compat'
454 */
455
456 static int __grscancompat __P((int, gid_t, const char *));
457
458 static int
459 __grscancompat(search, gid, name)
460 int search;
461 gid_t gid;
462 const char *name;
463 {
464 static const ns_dtab dtab[] = {
465 NS_FILES_CB(_bad_grscan, "files")
466 NS_DNS_CB(_dns_grscan, NULL)
467 NS_NIS_CB(_nis_grscan, NULL)
468 NS_COMPAT_CB(_bad_grscan, "compat")
469 { 0 }
470 };
471 static const ns_src defaultnis[] = {
472 { NSSRC_NIS, NS_SUCCESS },
473 { 0 }
474 };
475
476 _DIAGASSERT(name != NULL);
477
478 return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat",
479 defaultnis, search, gid, name));
480 }
481 #endif
482
483
484 static int _compat_grscan __P((void *, void *, va_list));
485
486 /*ARGSUSED*/
487 static int
488 _compat_grscan(rv, cb_data, ap)
489 void *rv;
490 void *cb_data;
491 va_list ap;
492 {
493 int search = va_arg(ap, int);
494 gid_t gid = va_arg(ap, gid_t);
495 const char *name = va_arg(ap, const char *);
496
497 #ifdef _GROUP_COMPAT
498 static char *grname = NULL;
499 #endif
500
501 for (;;) {
502 #ifdef _GROUP_COMPAT
503 if(__grmode != GRMODE_NONE) {
504 int r;
505
506 switch(__grmode) {
507 case GRMODE_FULL:
508 r = __grscancompat(search, gid, name);
509 if (r == NS_SUCCESS)
510 return r;
511 __grmode = GRMODE_NONE;
512 break;
513 case GRMODE_NAME:
514 if(grname == (char *)NULL) {
515 __grmode = GRMODE_NONE;
516 break;
517 }
518 r = __grscancompat(1, 0, grname);
519 free(grname);
520 grname = (char *)NULL;
521 if (r != NS_SUCCESS)
522 break;
523 if (!search)
524 return NS_SUCCESS;
525 if (name) {
526 if (! strcmp(_gr_group.gr_name, name))
527 return NS_SUCCESS;
528 } else {
529 if (_gr_group.gr_gid == gid)
530 return NS_SUCCESS;
531 }
532 break;
533 case GRMODE_NONE:
534 abort();
535 }
536 continue;
537 }
538 #endif /* _GROUP_COMPAT */
539
540 if (!fgets(line, sizeof(line), _gr_fp))
541 return NS_NOTFOUND;
542 /* skip lines that are too big */
543 if (!strchr(line, '\n')) {
544 int ch;
545
546 while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
547 ;
548 continue;
549 }
550
551 #ifdef _GROUP_COMPAT
552 if (line[0] == '+') {
553 char *tptr, *bp;
554
555 switch(line[1]) {
556 case ':':
557 case '\0':
558 case '\n':
559 __grmode = GRMODE_FULL;
560 break;
561 default:
562 __grmode = GRMODE_NAME;
563 bp = line;
564 tptr = strsep(&bp, ":\n");
565 grname = strdup(tptr + 1);
566 break;
567 }
568 continue;
569 }
570 #endif /* _GROUP_COMPAT */
571 if (matchline(search, gid, name))
572 return NS_SUCCESS;
573 }
574 /* NOTREACHED */
575 }
576
577 static int
578 grscan(search, gid, name)
579 int search;
580 gid_t gid;
581 const char *name;
582 {
583 int r;
584 static const ns_dtab dtab[] = {
585 NS_FILES_CB(_local_grscan, NULL)
586 NS_DNS_CB(_dns_grscan, NULL)
587 NS_NIS_CB(_nis_grscan, NULL)
588 NS_COMPAT_CB(_compat_grscan, NULL)
589 { 0 }
590 };
591 static const ns_src compatsrc[] = {
592 { NSSRC_COMPAT, NS_SUCCESS },
593 { 0 }
594 };
595
596 /* name may be NULL if search is nonzero */
597
598 r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc,
599 search, gid, name);
600 return (r == NS_SUCCESS) ? 1 : 0;
601 }
602
603 static int
604 matchline(search, gid, name)
605 int search;
606 gid_t gid;
607 const char *name;
608 {
609 unsigned long id;
610 __aconst char **m;
611 char *cp, *bp, *ep;
612
613 /* name may be NULL if search is nonzero */
614
615 if (line[0] == '+')
616 return 0; /* sanity check to prevent recursion */
617 bp = line;
618 _gr_group.gr_name = strsep(&bp, ":\n");
619 if (search && name && strcmp(_gr_group.gr_name, name))
620 return 0;
621 _gr_group.gr_passwd = strsep(&bp, ":\n");
622 if (!(cp = strsep(&bp, ":\n")))
623 return 0;
624 id = strtoul(cp, &ep, 10);
625 if (id > GID_MAX || *ep != '\0')
626 return 0;
627 _gr_group.gr_gid = (gid_t)id;
628 if (search && name == NULL && _gr_group.gr_gid != gid)
629 return 0;
630 cp = NULL;
631 if (bp == NULL)
632 return 0;
633 for (_gr_group.gr_mem = m = members;; bp++) {
634 if (m == &members[MAXGRP - 1])
635 break;
636 if (*bp == ',') {
637 if (cp) {
638 *bp = '\0';
639 *m++ = cp;
640 cp = NULL;
641 }
642 } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
643 if (cp) {
644 *bp = '\0';
645 *m++ = cp;
646 }
647 break;
648 } else if (cp == NULL)
649 cp = bp;
650 }
651 *m = NULL;
652 return 1;
653 }
654