getgrent.c revision 1.35 1 /* $NetBSD: getgrent.c,v 1.35 1999/04/18 02:04:04 lukem 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.35 1999/04/18 02:04:04 lukem Exp $");
43 #endif
44 #endif /* LIBC_SCCS and not lint */
45
46 #include "namespace.h"
47
48 #include <sys/types.h>
49
50 #include <errno.h>
51 #include <grp.h>
52 #include <limits.h>
53 #include <nsswitch.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <syslog.h>
58
59 #ifdef __STDC__
60 #include <stdarg.h>
61 #else
62 #include <varargs.h>
63 #endif
64
65 #ifdef HESIOD
66 #include <hesiod.h>
67 #endif
68 #ifdef YP
69 #include <rpc/rpc.h>
70 #include <rpcsvc/yp_prot.h>
71 #include <rpcsvc/ypclnt.h>
72 #endif
73
74 #if defined(YP) || defined(HESIOD)
75 #define _GROUP_COMPAT
76 #endif
77
78 #ifdef __weak_alias
79 __weak_alias(endgrent,_endgrent);
80 __weak_alias(getgrent,_getgrent);
81 __weak_alias(getgrgid,_getgrgid);
82 __weak_alias(getgrnam,_getgrnam);
83 __weak_alias(setgrent,_setgrent);
84 __weak_alias(setgroupent,_setgroupent);
85 #endif
86
87 static FILE *_gr_fp;
88 static struct group _gr_group;
89 static int _gr_stayopen;
90 static int _gr_nomore;
91
92 static int grscan __P((int, gid_t, const char *));
93 static int matchline __P((int, gid_t, const char *));
94 static int start_gr __P((void));
95
96 #define MAXGRP 200
97 #define MAXLINELENGTH 1024
98
99 static __aconst char *members[MAXGRP];
100 static char line[MAXLINELENGTH];
101
102 #ifdef YP
103 static char *__ypcurrent, *__ypdomain;
104 static int __ypcurrentlen;
105 #endif
106
107 #ifdef HESIOD
108 static int __gr_hesnum;
109 #endif
110
111 #ifdef _GROUP_COMPAT
112 enum _grmode { GRMODE_NONE, GRMODE_FULL, GRMODE_NAME };
113 static enum _grmode __grmode;
114 #endif
115
116 struct group *
117 getgrent()
118 {
119 _gr_nomore = 0;
120 if ((!_gr_fp && !start_gr()) || !grscan(0, 0, NULL) || _gr_nomore)
121 return(NULL);
122 return &_gr_group;
123 }
124
125 struct group *
126 getgrnam(name)
127 const char *name;
128 {
129 int rval;
130
131 if (!start_gr())
132 return NULL;
133 rval = grscan(1, 0, name);
134 if (!_gr_stayopen)
135 endgrent();
136 return (rval) ? &_gr_group : NULL;
137 }
138
139 struct group *
140 getgrgid(gid)
141 gid_t gid;
142 {
143 int rval;
144
145 if (!start_gr())
146 return NULL;
147 rval = grscan(1, gid, NULL);
148 if (!_gr_stayopen)
149 endgrent();
150 return (rval) ? &_gr_group : NULL;
151 }
152
153 static int
154 start_gr()
155 {
156 #ifdef YP
157 if (__ypcurrent)
158 free(__ypcurrent);
159 __ypcurrent = NULL;
160 #endif
161 #ifdef HESIOD
162 __gr_hesnum = 0;
163 #endif
164 #ifdef _GROUP_COMPAT
165 __grmode = GRMODE_NONE;
166 #endif
167 if (_gr_fp) {
168 rewind(_gr_fp);
169 return 1;
170 }
171 return (_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0;
172 }
173
174 void
175 setgrent()
176 {
177 (void) setgroupent(0);
178 }
179
180 int
181 setgroupent(stayopen)
182 int stayopen;
183 {
184 if (!start_gr())
185 return 0;
186 _gr_stayopen = stayopen;
187 return 1;
188 }
189
190 void
191 endgrent()
192 {
193 #ifdef YP
194 if (__ypcurrent)
195 free(__ypcurrent);
196 __ypcurrent = NULL;
197 #endif
198 #ifdef HESIOD
199 __gr_hesnum = 0;
200 #endif
201 #ifdef _GROUP_COMPAT
202 __grmode = GRMODE_NONE;
203 #endif
204 if (_gr_fp) {
205 (void)fclose(_gr_fp);
206 _gr_fp = NULL;
207 }
208 }
209
210
211 static int _local_grscan __P((void *, void *, va_list));
212
213 /*ARGSUSED*/
214 static int
215 _local_grscan(rv, cb_data, ap)
216 void *rv;
217 void *cb_data;
218 va_list ap;
219 {
220 int search = va_arg(ap, int);
221 gid_t gid = va_arg(ap, gid_t);
222 const char *name = va_arg(ap, const char *);
223
224 for (;;) {
225 if (!fgets(line, sizeof(line), _gr_fp)) {
226 if (!search) {
227 _gr_nomore = 1;
228 return NS_SUCCESS;
229 }
230 return NS_NOTFOUND;
231 }
232 /* skip lines that are too big */
233 if (!strchr(line, '\n')) {
234 int ch;
235
236 while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
237 ;
238 continue;
239 }
240 if (matchline(search, gid, name))
241 return NS_SUCCESS;
242 }
243 /* NOTREACHED */
244 }
245
246 #ifdef HESIOD
247 static int _dns_grscan __P((void *, void *, va_list));
248
249 /*ARGSUSED*/
250 static int
251 _dns_grscan(rv, cb_data, ap)
252 void *rv;
253 void *cb_data;
254 va_list ap;
255 {
256 int search = va_arg(ap, int);
257 gid_t gid = va_arg(ap, gid_t);
258 const char *name = va_arg(ap, const char *);
259
260 char **hp;
261 void *context;
262 int r;
263
264 r = NS_UNAVAIL;
265 if (hesiod_init(&context) == -1)
266 return (r);
267
268 for (;;) {
269 if (search) {
270 if (name)
271 strncpy(line, name, sizeof(line));
272 else
273 snprintf(line, sizeof(line), "%u",
274 (unsigned int)gid);
275 } else {
276 snprintf(line, sizeof(line), "group-%u", __gr_hesnum);
277 __gr_hesnum++;
278 }
279
280 line[sizeof(line) - 1] = '\0';
281 hp = hesiod_resolve(context, line, "group");
282 if (hp == NULL) {
283 if (errno == ENOENT) {
284 if (!search) {
285 __gr_hesnum = 0;
286 _gr_nomore = 1;
287 r = NS_SUCCESS;
288 } else
289 r = NS_NOTFOUND;
290 }
291 break;
292 }
293
294 /* only check first elem */
295 strncpy(line, hp[0], sizeof(line));
296 line[sizeof(line) - 1] = '\0';
297 hesiod_free_list(context, hp);
298 if (matchline(search, gid, name)) {
299 r = NS_SUCCESS;
300 break;
301 } else if (search) {
302 r = NS_NOTFOUND;
303 break;
304 }
305 }
306 hesiod_end(context);
307 return (r);
308 }
309 #endif
310
311 #ifdef YP
312 static int _nis_grscan __P((void *, void *, va_list));
313
314 /*ARGSUSED*/
315 static int
316 _nis_grscan(rv, cb_data, ap)
317 void *rv;
318 void *cb_data;
319 va_list ap;
320 {
321 int search = va_arg(ap, int);
322 gid_t gid = va_arg(ap, gid_t);
323 const char *name = va_arg(ap, const char *);
324
325 char *key, *data;
326 int keylen, datalen;
327 int r;
328
329 if(__ypdomain == NULL) {
330 switch (yp_get_default_domain(&__ypdomain)) {
331 case 0:
332 break;
333 case YPERR_RESRC:
334 return NS_TRYAGAIN;
335 default:
336 return NS_UNAVAIL;
337 }
338 }
339
340 if (search) { /* specific group or gid */
341 if (name)
342 strncpy(line, name, sizeof(line));
343 else
344 snprintf(line, sizeof(line), "%u", (unsigned int)gid);
345 line[sizeof(line) - 1] = '\0';
346 data = NULL;
347 r = yp_match(__ypdomain,
348 (name) ? "group.byname" : "group.bygid",
349 line, (int)strlen(line), &data, &datalen);
350 switch (r) {
351 case 0:
352 break;
353 case YPERR_KEY:
354 if (data)
355 free(data);
356 return NS_NOTFOUND;
357 default:
358 if (data)
359 free(data);
360 return NS_UNAVAIL;
361 }
362 data[datalen] = '\0'; /* clear trailing \n */
363 strncpy(line, data, sizeof(line));
364 line[sizeof(line) - 1] = '\0';
365 free(data);
366 if (matchline(search, gid, name))
367 return NS_SUCCESS;
368 else
369 return NS_NOTFOUND;
370 }
371
372 for (;;) { /* ! search */
373 data = NULL;
374 if(__ypcurrent) {
375 key = NULL;
376 r = yp_next(__ypdomain, "group.byname",
377 __ypcurrent, __ypcurrentlen,
378 &key, &keylen, &data, &datalen);
379 free(__ypcurrent);
380 switch (r) {
381 case 0:
382 break;
383 case YPERR_NOMORE:
384 __ypcurrent = NULL;
385 if (key)
386 free(key);
387 if (data)
388 free(data);
389 _gr_nomore = 1;
390 return NS_SUCCESS;
391 default:
392 if (key)
393 free(key);
394 if (data)
395 free(data);
396 return NS_UNAVAIL;
397 }
398 __ypcurrent = key;
399 __ypcurrentlen = keylen;
400 } else {
401 if (yp_first(__ypdomain, "group.byname",
402 &__ypcurrent, &__ypcurrentlen,
403 &data, &datalen)) {
404 if (data);
405 free(data);
406 return NS_UNAVAIL;
407 }
408 }
409 data[datalen] = '\0'; /* clear trailing \n */
410 strncpy(line, data, sizeof(line));
411 line[sizeof(line) - 1] = '\0';
412 free(data);
413 if (matchline(search, gid, name))
414 return NS_SUCCESS;
415 }
416 /* NOTREACHED */
417 }
418 #endif
419
420 #ifdef _GROUP_COMPAT
421 /*
422 * log an error if "files" or "compat" is specified in group_compat database
423 */
424 static int _bad_grscan __P((void *, void *, va_list));
425
426 /*ARGSUSED*/
427 static int
428 _bad_grscan(rv, cb_data, ap)
429 void *rv;
430 void *cb_data;
431 va_list ap;
432 {
433 static int warned;
434
435 if (!warned) {
436 syslog(LOG_ERR,
437 "nsswitch.conf group_compat database can't use '%s'",
438 (char *)cb_data);
439 }
440 warned = 1;
441 return NS_UNAVAIL;
442 }
443
444 /*
445 * when a name lookup in compat mode is required, look it up in group_compat
446 * nsswitch database. only Hesiod and NIS is supported - it doesn't make
447 * sense to lookup compat names from 'files' or 'compat'
448 */
449
450 static int __grscancompat __P((int, gid_t, const char *));
451
452 static int
453 __grscancompat(search, gid, name)
454 int search;
455 gid_t gid;
456 const char *name;
457 {
458 static const ns_dtab dtab[] = {
459 NS_FILES_CB(_bad_grscan, "files")
460 NS_DNS_CB(_dns_grscan, NULL)
461 NS_NIS_CB(_nis_grscan, NULL)
462 NS_COMPAT_CB(_bad_grscan, "compat")
463 { 0 }
464 };
465 static const ns_src defaultnis[] = {
466 { NSSRC_NIS, NS_SUCCESS },
467 { 0 }
468 };
469
470 return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat",
471 defaultnis, search, gid, name));
472 }
473 #endif
474
475
476 static int _compat_grscan __P((void *, void *, va_list));
477
478 /*ARGSUSED*/
479 static int
480 _compat_grscan(rv, cb_data, ap)
481 void *rv;
482 void *cb_data;
483 va_list ap;
484 {
485 int search = va_arg(ap, int);
486 gid_t gid = va_arg(ap, gid_t);
487 const char *name = va_arg(ap, const char *);
488
489 #ifdef _GROUP_COMPAT
490 static char *grname = NULL;
491 #endif
492
493 for (;;) {
494 #ifdef _GROUP_COMPAT
495 if(__grmode != GRMODE_NONE) {
496 int r;
497
498 switch(__grmode) {
499 case GRMODE_FULL:
500 r = __grscancompat(search, gid, name);
501 if (r == NS_SUCCESS)
502 return r;
503 __grmode = GRMODE_NONE;
504 break;
505 case GRMODE_NAME:
506 if(grname == (char *)NULL) {
507 __grmode = GRMODE_NONE;
508 break;
509 }
510 r = __grscancompat(1, 0, grname);
511 free(grname);
512 grname = (char *)NULL;
513 if (r != NS_SUCCESS)
514 break;
515 if (!search)
516 return NS_SUCCESS;
517 if (name) {
518 if (! strcmp(_gr_group.gr_name, name))
519 return NS_SUCCESS;
520 } else {
521 if (_gr_group.gr_gid == gid)
522 return NS_SUCCESS;
523 }
524 break;
525 case GRMODE_NONE:
526 abort();
527 }
528 continue;
529 }
530 #endif /* _GROUP_COMPAT */
531
532 if (!fgets(line, sizeof(line), _gr_fp))
533 return NS_NOTFOUND;
534 /* skip lines that are too big */
535 if (!strchr(line, '\n')) {
536 int ch;
537
538 while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
539 ;
540 continue;
541 }
542
543 #ifdef _GROUP_COMPAT
544 if (line[0] == '+') {
545 char *tptr, *bp;
546
547 switch(line[1]) {
548 case ':':
549 case '\0':
550 case '\n':
551 __grmode = GRMODE_FULL;
552 break;
553 default:
554 __grmode = GRMODE_NAME;
555 bp = line;
556 tptr = strsep(&bp, ":\n");
557 grname = strdup(tptr + 1);
558 break;
559 }
560 continue;
561 }
562 #endif /* _GROUP_COMPAT */
563 if (matchline(search, gid, name))
564 return NS_SUCCESS;
565 }
566 /* NOTREACHED */
567 }
568
569 static int
570 grscan(search, gid, name)
571 int search;
572 gid_t gid;
573 const char *name;
574 {
575 int r;
576 static const ns_dtab dtab[] = {
577 NS_FILES_CB(_local_grscan, NULL)
578 NS_DNS_CB(_dns_grscan, NULL)
579 NS_NIS_CB(_nis_grscan, NULL)
580 NS_COMPAT_CB(_compat_grscan, NULL)
581 { 0 }
582 };
583 static const ns_src compatsrc[] = {
584 { NSSRC_COMPAT, NS_SUCCESS },
585 { 0 }
586 };
587
588 r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc,
589 search, gid, name);
590 return (r == NS_SUCCESS) ? 1 : 0;
591 }
592
593 static int
594 matchline(search, gid, name)
595 int search;
596 gid_t gid;
597 const char *name;
598 {
599 unsigned long id;
600 __aconst char **m;
601 char *cp, *bp, *ep;
602
603 if (line[0] == '+')
604 return 0; /* sanity check to prevent recursion */
605 bp = line;
606 _gr_group.gr_name = strsep(&bp, ":\n");
607 if (search && name && strcmp(_gr_group.gr_name, name))
608 return 0;
609 _gr_group.gr_passwd = strsep(&bp, ":\n");
610 if (!(cp = strsep(&bp, ":\n")))
611 return 0;
612 id = strtoul(cp, &ep, 10);
613 if (id > GID_MAX || *ep != '\0')
614 return 0;
615 _gr_group.gr_gid = (gid_t)id;
616 if (search && name == NULL && _gr_group.gr_gid != gid)
617 return 0;
618 cp = NULL;
619 if (bp == NULL)
620 return 0;
621 for (_gr_group.gr_mem = m = members;; bp++) {
622 if (m == &members[MAXGRP - 1])
623 break;
624 if (*bp == ',') {
625 if (cp) {
626 *bp = '\0';
627 *m++ = cp;
628 cp = NULL;
629 }
630 } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
631 if (cp) {
632 *bp = '\0';
633 *m++ = cp;
634 }
635 break;
636 } else if (cp == NULL)
637 cp = bp;
638 }
639 *m = NULL;
640 return 1;
641 }
642