xref: /freebsd/lib/libc/gen/getgrent.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
1 /*-
2  * Copyright (c) 2003 Networks Associates Technology, Inc.
3  * All rights reserved.
4  *
5  * This software was developed for the FreeBSD Project by
6  * Jacques A. Vidrine, Safeport Network Services, and Network
7  * Associates Laboratories, the Security Research Division of Network
8  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
9  * ("CBOSS"), as part of the DARPA CHATS research program.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  */
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include "namespace.h"
37 #include <sys/param.h>
38 #ifdef YP
39 #include <rpc/rpc.h>
40 #include <rpcsvc/yp_prot.h>
41 #include <rpcsvc/ypclnt.h>
42 #endif
43 #include <ctype.h>
44 #include <errno.h>
45 #ifdef HESIOD
46 #include <hesiod.h>
47 #endif
48 #include <grp.h>
49 #include <nsswitch.h>
50 #include <pthread.h>
51 #include <pthread_np.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <syslog.h>
56 #include <unistd.h>
57 #include "un-namespace.h"
58 #include "libc_private.h"
59 #include "nss_tls.h"
60 
61 
62 enum constants {
63 	GRP_STORAGE_INITIAL	= 1 << 10, /* 1 KByte */
64 	GRP_STORAGE_MAX		= 1 << 20, /* 1 MByte */
65 	SETGRENT		= 1,
66 	ENDGRENT		= 2,
67 	HESIOD_NAME_MAX		= 256,
68 };
69 
70 static const ns_src defaultsrc[] = {
71 	{ NSSRC_COMPAT, NS_SUCCESS },
72 	{ NULL, 0 }
73 };
74 
75 int	 __gr_match_entry(const char *, size_t, enum nss_lookup_type,
76 	    const char *, gid_t);
77 int	 __gr_parse_entry(char *, size_t, struct group *, char *, size_t,
78 	    int *);
79 
80 static	int	 is_comment_line(const char *, size_t);
81 
82 union key {
83 	const char	*name;
84 	gid_t		 gid;
85 };
86 static	struct group *getgr(int (*)(union key, struct group *, char *, size_t,
87 		    struct group **), union key);
88 static	int	 wrap_getgrnam_r(union key, struct group *, char *, size_t,
89 		    struct group **);
90 static	int	 wrap_getgrgid_r(union key, struct group *, char *, size_t,
91 		    struct group **);
92 static	int	 wrap_getgrent_r(union key, struct group *, char *, size_t,
93 		    struct group **);
94 
95 struct files_state {
96 	FILE	*fp;
97 	int	 stayopen;
98 };
99 static	void	 files_endstate(void *);
100 NSS_TLS_HANDLING(files);
101 static	int	 files_setgrent(void *, void *, va_list);
102 static	int	 files_group(void *, void *, va_list);
103 
104 
105 #ifdef HESIOD
106 struct dns_state {
107 	long	counter;
108 };
109 static	void	 dns_endstate(void *);
110 NSS_TLS_HANDLING(dns);
111 static	int	 dns_setgrent(void *, void *, va_list);
112 static	int	 dns_group(void *, void *, va_list);
113 #endif
114 
115 
116 #ifdef YP
117 struct nis_state {
118 	char	 domain[MAXHOSTNAMELEN];
119 	int	 done;
120 	char	*key;
121 	int	 keylen;
122 };
123 static	void	 nis_endstate(void *);
124 NSS_TLS_HANDLING(nis);
125 static	int	 nis_setgrent(void *, void *, va_list);
126 static	int	 nis_group(void *, void *, va_list);
127 #endif
128 
129 struct compat_state {
130 	FILE	*fp;
131 	int	 stayopen;
132 	char	*name;
133 	enum _compat {
134 		COMPAT_MODE_OFF = 0,
135 		COMPAT_MODE_ALL,
136 		COMPAT_MODE_NAME
137 	}	 compat;
138 };
139 static	void	 compat_endstate(void *);
140 NSS_TLS_HANDLING(compat);
141 static	int	 compat_setgrent(void *, void *, va_list);
142 static	int	 compat_group(void *, void *, va_list);
143 
144 
145 /* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */
146 int
147 setgrent(void)
148 {
149 	static const ns_dtab dtab[] = {
150 		{ NSSRC_FILES, files_setgrent, (void *)SETGRENT },
151 #ifdef HESIOD
152 		{ NSSRC_DNS, dns_setgrent, (void *)SETGRENT },
153 #endif
154 #ifdef YP
155 		{ NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
156 #endif
157 		{ NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
158 		{ NULL, NULL, NULL }
159 	};
160 	(void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, 0);
161 	return (1);
162 }
163 
164 
165 int
166 setgroupent(int stayopen)
167 {
168 	static const ns_dtab dtab[] = {
169 		{ NSSRC_FILES, files_setgrent, (void *)SETGRENT },
170 #ifdef HESIOD
171 		{ NSSRC_DNS, dns_setgrent, (void *)SETGRENT },
172 #endif
173 #ifdef YP
174 		{ NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
175 #endif
176 		{ NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
177 		{ NULL, NULL, NULL }
178 	};
179 	(void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc,
180 	    stayopen);
181 	return (1);
182 }
183 
184 
185 void
186 endgrent(void)
187 {
188 	static const ns_dtab dtab[] = {
189 		{ NSSRC_FILES, files_setgrent, (void *)ENDGRENT },
190 #ifdef HESIOD
191 		{ NSSRC_DNS, dns_setgrent, (void *)ENDGRENT },
192 #endif
193 #ifdef YP
194 		{ NSSRC_NIS, nis_setgrent, (void *)ENDGRENT },
195 #endif
196 		{ NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT },
197 		{ NULL, NULL, NULL }
198 	};
199 	(void)_nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent", defaultsrc);
200 }
201 
202 
203 int
204 getgrent_r(struct group *grp, char *buffer, size_t bufsize,
205     struct group **result)
206 {
207 	static const ns_dtab dtab[] = {
208 		{ NSSRC_FILES, files_group, (void *)nss_lt_all },
209 #ifdef HESIOD
210 		{ NSSRC_DNS, dns_group, (void *)nss_lt_all },
211 #endif
212 #ifdef YP
213 		{ NSSRC_NIS, nis_group, (void *)nss_lt_all },
214 #endif
215 		{ NSSRC_COMPAT, compat_group, (void *)nss_lt_all },
216 		{ NULL, NULL, NULL }
217 	};
218 	int	rv, ret_errno;
219 
220 	ret_errno = 0;
221 	*result = NULL;
222 	rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrent_r", defaultsrc,
223 	    grp, buffer, bufsize, &ret_errno);
224 	if (rv == NS_SUCCESS)
225 		return (0);
226 	else
227 		return (ret_errno);
228 }
229 
230 
231 int
232 getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize,
233     struct group **result)
234 {
235 	static const ns_dtab dtab[] = {
236 		{ NSSRC_FILES, files_group, (void *)nss_lt_name },
237 #ifdef HESIOD
238 		{ NSSRC_DNS, dns_group, (void *)nss_lt_name },
239 #endif
240 #ifdef YP
241 		{ NSSRC_NIS, nis_group, (void *)nss_lt_name },
242 #endif
243 		{ NSSRC_COMPAT, compat_group, (void *)nss_lt_name },
244 		{ NULL, NULL, NULL }
245 	};
246 	int	rv, ret_errno;
247 
248 	ret_errno = 0;
249 	*result = NULL;
250 	rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrnam_r", defaultsrc,
251 	    name, grp, buffer, bufsize, &ret_errno);
252 	if (rv == NS_SUCCESS)
253 		return (0);
254 	else
255 		return (ret_errno);
256 }
257 
258 
259 int
260 getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
261     struct group **result)
262 {
263 	static const ns_dtab dtab[] = {
264 		{ NSSRC_FILES, files_group, (void *)nss_lt_id },
265 #ifdef HESIOD
266 		{ NSSRC_DNS, dns_group, (void *)nss_lt_id },
267 #endif
268 #ifdef YP
269 		{ NSSRC_NIS, nis_group, (void *)nss_lt_id },
270 #endif
271 		{ NSSRC_COMPAT, compat_group, (void *)nss_lt_id },
272 		{ NULL, NULL, NULL }
273 	};
274 	int	rv, ret_errno;
275 
276 	ret_errno = 0;
277 	*result = NULL;
278 	rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrgid_r", defaultsrc,
279 	    gid, grp, buffer, bufsize, &ret_errno);
280 	if (rv == NS_SUCCESS)
281 		return (0);
282 	else
283 		return (ret_errno);
284 }
285 
286 
287 static struct group	 grp;
288 static char		*grp_storage;
289 static size_t		 grp_storage_size;
290 
291 static struct group *
292 getgr(int (*fn)(union key, struct group *, char *, size_t, struct group **),
293     union key key)
294 {
295 	int		 rv;
296 	struct group	*res;
297 
298 	if (grp_storage == NULL) {
299 		grp_storage = malloc(GRP_STORAGE_INITIAL);
300 		if (grp_storage == NULL)
301 			return (NULL);
302 		grp_storage_size = GRP_STORAGE_INITIAL;
303 	}
304 	do {
305 		rv = fn(key, &grp, grp_storage, grp_storage_size, &res);
306 		if (res == NULL && rv == ERANGE) {
307 			free(grp_storage);
308 			if ((grp_storage_size << 1) > GRP_STORAGE_MAX) {
309 				grp_storage = NULL;
310 				return (NULL);
311 			}
312 			grp_storage_size <<= 1;
313 			grp_storage = malloc(grp_storage_size);
314 			if (grp_storage == NULL)
315 				return (NULL);
316 		}
317 	} while (res == NULL && rv == ERANGE);
318 	return (res);
319 }
320 
321 
322 static int
323 wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize,
324     struct group **res)
325 {
326 	return (getgrnam_r(key.name, grp, buffer, bufsize, res));
327 }
328 
329 
330 static int
331 wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize,
332     struct group **res)
333 {
334 	return (getgrgid_r(key.gid, grp, buffer, bufsize, res));
335 }
336 
337 
338 static int
339 wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer,
340     size_t bufsize, struct group **res)
341 {
342 	return (getgrent_r(grp, buffer, bufsize, res));
343 }
344 
345 
346 struct group *
347 getgrnam(const char *name)
348 {
349 	union key key;
350 
351 	key.name = name;
352 	return (getgr(wrap_getgrnam_r, key));
353 }
354 
355 
356 struct group *
357 getgrgid(gid_t gid)
358 {
359 	union key key;
360 
361 	key.gid = gid;
362 	return (getgr(wrap_getgrgid_r, key));
363 }
364 
365 
366 struct group *
367 getgrent(void)
368 {
369 	union key key;
370 
371 	key.gid = 0; /* not used */
372 	return (getgr(wrap_getgrent_r, key));
373 }
374 
375 
376 static int
377 is_comment_line(const char *s, size_t n)
378 {
379 	const char	*eom;
380 
381 	eom = &s[n];
382 
383 	for (; s < eom; s++)
384 		if (*s == '#' || !isspace((unsigned char)*s))
385 			break;
386 	return (*s == '#' || s == eom);
387 }
388 
389 
390 /*
391  * files backend
392  */
393 static void
394 files_endstate(void *p)
395 {
396 
397 	if (p == NULL)
398 		return;
399 	if (((struct files_state *)p)->fp != NULL)
400 		fclose(((struct files_state *)p)->fp);
401 	free(p);
402 }
403 
404 
405 static int
406 files_setgrent(void *retval, void *mdata, va_list ap)
407 {
408 	struct files_state *st;
409 	int		 rv, stayopen;
410 
411 	rv = files_getstate(&st);
412 	if (rv != 0)
413 		return (NS_UNAVAIL);
414 	switch ((enum constants)mdata) {
415 	case SETGRENT:
416 		stayopen = va_arg(ap, int);
417 		if (st->fp != NULL)
418 			rewind(st->fp);
419 		else if (stayopen)
420 			st->fp = fopen(_PATH_GROUP, "r");
421 		break;
422 	case ENDGRENT:
423 		if (st->fp != NULL) {
424 			fclose(st->fp);
425 			st->fp = NULL;
426 		}
427 		break;
428 	default:
429 		break;
430 	}
431 	return (NS_UNAVAIL);
432 }
433 
434 
435 static int
436 files_group(void *retval, void *mdata, va_list ap)
437 {
438 	struct files_state	*st;
439 	enum nss_lookup_type	 how;
440 	const char		*name, *line;
441 	struct group		*grp;
442 	gid_t			 gid;
443 	char			*buffer;
444 	size_t			 bufsize, linesize;
445 	int			 rv, stayopen, *errnop;
446 
447 	name = NULL;
448 	gid = (gid_t)-1;
449 	how = (enum nss_lookup_type)mdata;
450 	switch (how) {
451 	case nss_lt_name:
452 		name = va_arg(ap, const char *);
453 		break;
454 	case nss_lt_id:
455 		gid = va_arg(ap, gid_t);
456 		break;
457 	case nss_lt_all:
458 		break;
459 	default:
460 		return (NS_NOTFOUND);
461 	}
462 	grp = va_arg(ap, struct group *);
463 	buffer = va_arg(ap, char *);
464 	bufsize = va_arg(ap, size_t);
465 	errnop = va_arg(ap, int *);
466 	*errnop = files_getstate(&st);
467 	if (*errnop != 0)
468 		return (NS_UNAVAIL);
469 	if (st->fp == NULL &&
470 	    ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) {
471 		*errnop = errno;
472 		return (NS_UNAVAIL);
473 	}
474 	if (how == nss_lt_all)
475 		stayopen = 1;
476 	else {
477 		rewind(st->fp);
478 		stayopen = st->stayopen;
479 	}
480 	rv = NS_NOTFOUND;
481 	while ((line = fgetln(st->fp, &linesize)) != NULL) {
482 		if (line[linesize-1] == '\n')
483 			linesize--;
484 		rv = __gr_match_entry(line, linesize, how, name, gid);
485 		if (rv != NS_SUCCESS)
486 			continue;
487 		/* We need room at least for the line, a string NUL
488 		 * terminator, alignment padding, and one (char *)
489 		 * pointer for the member list terminator.
490 		 */
491 		if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
492 			*errnop = ERANGE;
493 			rv = NS_RETURN;
494 			break;
495 		}
496 		memcpy(buffer, line, linesize);
497 		buffer[linesize] = '\0';
498 		rv = __gr_parse_entry(buffer, linesize, grp,
499 		    &buffer[linesize + 1], bufsize - linesize - 1, errnop);
500 		if (rv & NS_TERMINATE)
501 			break;
502 	}
503 	if (!stayopen && st->fp != NULL) {
504 		fclose(st->fp);
505 		st->fp = NULL;
506 	}
507 	if (rv == NS_SUCCESS && retval != NULL)
508 		*(struct group **)retval = grp;
509 	return (rv);
510 }
511 
512 
513 #ifdef HESIOD
514 /*
515  * dns backend
516  */
517 static void
518 dns_endstate(void *p)
519 {
520 
521 	free(p);
522 }
523 
524 
525 static int
526 dns_setgrent(void *retval, void *cb_data, va_list ap)
527 {
528 	struct dns_state	*st;
529 	int			 rv;
530 
531 	rv = dns_getstate(&st);
532 	if (rv != 0)
533 		return (NS_UNAVAIL);
534 	st->counter = 0;
535 	return (NS_UNAVAIL);
536 }
537 
538 
539 static int
540 dns_group(void *retval, void *mdata, va_list ap)
541 {
542 	char			 buf[HESIOD_NAME_MAX];
543 	struct dns_state	*st;
544 	struct group		*grp;
545 	const char		*name, *label;
546 	void			*ctx;
547 	char			*buffer, **hes;
548 	size_t			 bufsize, adjsize, linesize;
549 	gid_t			 gid;
550 	enum nss_lookup_type	 how;
551 	int			 rv, *errnop;
552 
553 	ctx = NULL;
554 	hes = NULL;
555 	name = NULL;
556 	gid = (gid_t)-1;
557 	how = (enum nss_lookup_type)mdata;
558 	switch (how) {
559 	case nss_lt_name:
560 		name = va_arg(ap, const char *);
561 		break;
562 	case nss_lt_id:
563 		gid = va_arg(ap, gid_t);
564 		break;
565 	case nss_lt_all:
566 		break;
567 	}
568 	grp     = va_arg(ap, struct group *);
569 	buffer  = va_arg(ap, char *);
570 	bufsize = va_arg(ap, size_t);
571 	errnop  = va_arg(ap, int *);
572 	*errnop = dns_getstate(&st);
573 	if (*errnop != 0)
574 		return (NS_UNAVAIL);
575 	if (hesiod_init(&ctx) != 0) {
576 		*errnop = errno;
577 		rv = NS_UNAVAIL;
578 		goto fin;
579 	}
580 	do {
581 		rv = NS_NOTFOUND;
582 		switch (how) {
583 		case nss_lt_name:
584 			label = name;
585 			break;
586 		case nss_lt_id:
587 			if (snprintf(buf, sizeof(buf), "%lu",
588 			    (unsigned long)gid) >= sizeof(buf))
589 				goto fin;
590 			label = buf;
591 			break;
592 		case nss_lt_all:
593 			if (st->counter < 0)
594 				goto fin;
595 			if (snprintf(buf, sizeof(buf), "group-%ld",
596 			    st->counter++) >= sizeof(buf))
597 				goto fin;
598 			label = buf;
599 			break;
600 		}
601 		hes = hesiod_resolve(ctx, label,
602 		    how == nss_lt_id ? "gid" : "group");
603 		if ((how == nss_lt_id && hes == NULL &&
604 		    (hes = hesiod_resolve(ctx, buf, "group")) == NULL) ||
605 		    hes == NULL) {
606 			if (how == nss_lt_all)
607 				st->counter = -1;
608 			if (errno != ENOENT)
609 				*errnop = errno;
610 			goto fin;
611 		}
612 		rv = __gr_match_entry(hes[0], strlen(hes[0]), how, name, gid);
613 		if (rv != NS_SUCCESS) {
614 			hesiod_free_list(ctx, hes);
615 			hes = NULL;
616 			continue;
617 		}
618 		/* We need room at least for the line, a string NUL
619 		 * terminator, alignment padding, and one (char *)
620 		 * pointer for the member list terminator.
621 		 */
622 		adjsize = bufsize - _ALIGNBYTES - sizeof(char *);
623 		linesize = strlcpy(buffer, hes[0], adjsize);
624 		if (linesize >= adjsize) {
625 			*errnop = ERANGE;
626 			rv = NS_RETURN;
627 			goto fin;
628 		}
629 		hesiod_free_list(ctx, hes);
630 		hes = NULL;
631 		rv = __gr_parse_entry(buffer, linesize, grp,
632 		    &buffer[linesize + 1], bufsize - linesize - 1, errnop);
633 	} while (how == nss_lt_all && !(rv & NS_TERMINATE));
634 fin:
635 	if (hes != NULL)
636 		hesiod_free_list(ctx, hes);
637 	if (ctx != NULL)
638 		hesiod_end(ctx);
639 	if (rv == NS_SUCCESS && retval != NULL)
640 		*(struct group **)retval = grp;
641 	return (rv);
642 }
643 #endif /* HESIOD */
644 
645 
646 #ifdef YP
647 /*
648  * nis backend
649  */
650 static void
651 nis_endstate(void *p)
652 {
653 
654 	if (p == NULL)
655 		return;
656 	free(((struct nis_state *)p)->key);
657 	free(p);
658 }
659 
660 
661 static int
662 nis_setgrent(void *retval, void *cb_data, va_list ap)
663 {
664 	struct nis_state	*st;
665 	int			 rv;
666 
667 	rv = nis_getstate(&st);
668 	if (rv != 0)
669 		return (NS_UNAVAIL);
670 	st->done = 0;
671 	free(st->key);
672 	st->key = NULL;
673 	return (NS_UNAVAIL);
674 }
675 
676 
677 static int
678 nis_group(void *retval, void *mdata, va_list ap)
679 {
680 	char		 *map;
681 	struct nis_state *st;
682 	struct group	*grp;
683 	const char	*name;
684 	char		*buffer, *key, *result;
685 	size_t		 bufsize;
686 	gid_t		 gid;
687 	enum nss_lookup_type how;
688 	int		*errnop, keylen, resultlen, rv;
689 
690 	name = NULL;
691 	gid = (gid_t)-1;
692 	how = (enum nss_lookup_type)mdata;
693 	switch (how) {
694 	case nss_lt_name:
695 		name = va_arg(ap, const char *);
696 		map = "group.byname";
697 		break;
698 	case nss_lt_id:
699 		gid = va_arg(ap, gid_t);
700 		map = "group.bygid";
701 		break;
702 	case nss_lt_all:
703 		map = "group.byname";
704 		break;
705 	}
706 	grp     = va_arg(ap, struct group *);
707 	buffer  = va_arg(ap, char *);
708 	bufsize = va_arg(ap, size_t);
709 	errnop  = va_arg(ap, int *);
710 	*errnop = nis_getstate(&st);
711 	if (*errnop != 0)
712 		return (NS_UNAVAIL);
713 	if (st->domain[0] == '\0') {
714 		if (getdomainname(st->domain, sizeof(st->domain)) != 0) {
715 			*errnop = errno;
716 			return (NS_UNAVAIL);
717 		}
718 	}
719 	result = NULL;
720 	do {
721 		rv = NS_NOTFOUND;
722 		switch (how) {
723 		case nss_lt_name:
724 			if (strlcpy(buffer, name, bufsize) >= bufsize)
725 				goto erange;
726 			break;
727 		case nss_lt_id:
728 			if (snprintf(buffer, bufsize, "%lu",
729 			    (unsigned long)gid) >= bufsize)
730 				goto erange;
731 			break;
732 		case nss_lt_all:
733 			if (st->done)
734 				goto fin;
735 			break;
736 		}
737 		result = NULL;
738 		if (how == nss_lt_all) {
739 			if (st->key == NULL)
740 				rv = yp_first(st->domain, map, &st->key,
741 				    &st->keylen, &result, &resultlen);
742 			else {
743 				key = st->key;
744 				keylen = st->keylen;
745 				st->key = NULL;
746 				rv = yp_next(st->domain, map, key, keylen,
747 				    &st->key, &st->keylen, &result,
748 				    &resultlen);
749 				free(key);
750 			}
751 			if (rv != 0) {
752 				free(result);
753 				free(st->key);
754 				st->key = NULL;
755 				if (rv == YPERR_NOMORE) {
756 					st->done = 1;
757 					rv = NS_NOTFOUND;
758 				} else
759 					rv = NS_UNAVAIL;
760 				goto fin;
761 			}
762 		} else {
763 			rv = yp_match(st->domain, map, buffer, strlen(buffer),
764 			    &result, &resultlen);
765 			if (rv == YPERR_KEY) {
766 				rv = NS_NOTFOUND;
767 				continue;
768 			} else if (rv != 0) {
769 				free(result);
770 				rv = NS_UNAVAIL;
771 				continue;
772 			}
773 		}
774 		/* We need room at least for the line, a string NUL
775 		 * terminator, alignment padding, and one (char *)
776 		 * pointer for the member list terminator.
777 		 */
778 		if (resultlen >= bufsize - _ALIGNBYTES - sizeof(char *))
779 			goto erange;
780 		memcpy(buffer, result, resultlen);
781 		buffer[resultlen] = '\0';
782 		free(result);
783 		rv = __gr_match_entry(buffer, resultlen, how, name, gid);
784 		if (rv == NS_SUCCESS)
785 			rv = __gr_parse_entry(buffer, resultlen, grp,
786 			    &buffer[resultlen+1], bufsize - resultlen - 1,
787 			    errnop);
788 	} while (how == nss_lt_all && !(rv & NS_TERMINATE));
789 fin:
790 	if (rv == NS_SUCCESS && retval != NULL)
791 		*(struct group **)retval = grp;
792 	return (rv);
793 erange:
794 	*errnop = ERANGE;
795 	return (NS_RETURN);
796 }
797 #endif /* YP */
798 
799 
800 
801 /*
802  * compat backend
803  */
804 static void
805 compat_endstate(void *p)
806 {
807 	struct compat_state *st;
808 
809 	if (p == NULL)
810 		return;
811 	st = (struct compat_state *)p;
812 	free(st->name);
813 	if (st->fp != NULL)
814 		fclose(st->fp);
815 	free(p);
816 }
817 
818 
819 static int
820 compat_setgrent(void *retval, void *mdata, va_list ap)
821 {
822 	static const ns_src compatsrc[] = {
823 #ifdef YP
824 		{ NSSRC_NIS, NS_SUCCESS },
825 #endif
826 		{ NULL, 0 }
827 	};
828 	ns_dtab dtab[] = {
829 #ifdef HESIOD
830 		{ NSSRC_DNS, dns_setgrent, NULL },
831 #endif
832 #ifdef YP
833 		{ NSSRC_NIS, nis_setgrent, NULL },
834 #endif
835 		{ NULL, NULL, NULL }
836 	};
837 	struct compat_state *st;
838 	int		 rv, stayopen;
839 
840 #define set_setent(x, y) do {	 				\
841 	int i;							\
842 								\
843 	for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++)	\
844 		x[i].mdata = (void *)y;				\
845 } while (0)
846 
847 	rv = compat_getstate(&st);
848 	if (rv != 0)
849 		return (NS_UNAVAIL);
850 	switch ((enum constants)mdata) {
851 	case SETGRENT:
852 		stayopen = va_arg(ap, int);
853 		if (st->fp != NULL)
854 			rewind(st->fp);
855 		else if (stayopen)
856 			st->fp = fopen(_PATH_GROUP, "r");
857 		set_setent(dtab, mdata);
858 		(void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent",
859 		    compatsrc, 0);
860 		break;
861 	case ENDGRENT:
862 		if (st->fp != NULL) {
863 			fclose(st->fp);
864 			st->fp = NULL;
865 		}
866 		set_setent(dtab, mdata);
867 		(void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent",
868 		    compatsrc, 0);
869 		break;
870 	default:
871 		break;
872 	}
873 	st->compat = COMPAT_MODE_OFF;
874 	free(st->name);
875 	st->name = NULL;
876 	return (NS_UNAVAIL);
877 #undef set_setent
878 }
879 
880 
881 static int
882 compat_group(void *retval, void *mdata, va_list ap)
883 {
884 	static const ns_src compatsrc[] = {
885 #ifdef YP
886 		{ NSSRC_NIS, NS_SUCCESS },
887 #endif
888 		{ NULL, 0 }
889 	};
890 	ns_dtab dtab[] = {
891 #ifdef YP
892 		{ NSSRC_NIS, nis_group, NULL },
893 #endif
894 #ifdef HESIOD
895 		{ NSSRC_DNS, dns_group, NULL },
896 #endif
897 		{ NULL, NULL, NULL }
898 	};
899 	struct compat_state	*st;
900 	enum nss_lookup_type	 how;
901 	const char		*name, *line;
902 	struct group		*grp;
903 	gid_t			 gid;
904 	char			*buffer, *p;
905 	void			*discard;
906 	size_t			 bufsize, linesize;
907 	int			 rv, stayopen, *errnop;
908 
909 #define set_lookup_type(x, y) do { 				\
910 	int i;							\
911 								\
912 	for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++)	\
913 		x[i].mdata = (void *)y;				\
914 } while (0)
915 
916 	name = NULL;
917 	gid = (gid_t)-1;
918 	how = (enum nss_lookup_type)mdata;
919 	switch (how) {
920 	case nss_lt_name:
921 		name = va_arg(ap, const char *);
922 		break;
923 	case nss_lt_id:
924 		gid = va_arg(ap, gid_t);
925 		break;
926 	case nss_lt_all:
927 		break;
928 	default:
929 		return (NS_NOTFOUND);
930 	}
931 	grp = va_arg(ap, struct group *);
932 	buffer = va_arg(ap, char *);
933 	bufsize = va_arg(ap, size_t);
934 	errnop = va_arg(ap, int *);
935 	*errnop = compat_getstate(&st);
936 	if (*errnop != 0)
937 		return (NS_UNAVAIL);
938 	if (st->fp == NULL &&
939 	    ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) {
940 		*errnop = errno;
941 		rv = NS_UNAVAIL;
942 		goto fin;
943 	}
944 	if (how == nss_lt_all)
945 		stayopen = 1;
946 	else {
947 		rewind(st->fp);
948 		stayopen = st->stayopen;
949 	}
950 docompat:
951 	switch (st->compat) {
952 	case COMPAT_MODE_ALL:
953 		set_lookup_type(dtab, how);
954 		switch (how) {
955 		case nss_lt_all:
956 			rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
957 			    "getgrent_r", compatsrc, grp, buffer, bufsize,
958 			    errnop);
959 			break;
960 		case nss_lt_id:
961 			rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
962 			    "getgrgid_r", compatsrc, gid, grp, buffer, bufsize,
963 			    errnop);
964 			break;
965 		case nss_lt_name:
966 			rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
967 			    "getgrnam_r", compatsrc, name, grp, buffer,
968 			    bufsize, errnop);
969 			break;
970 		}
971 		if (rv & NS_TERMINATE)
972 			goto fin;
973 		st->compat = COMPAT_MODE_OFF;
974 		break;
975 	case COMPAT_MODE_NAME:
976 		set_lookup_type(dtab, nss_lt_name);
977 		rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
978 		    "getgrnam_r", compatsrc, st->name, grp, buffer, bufsize,
979 		    errnop);
980 		switch (rv) {
981 		case NS_SUCCESS:
982 			switch (how) {
983 			case nss_lt_name:
984 				if (strcmp(name, grp->gr_name) != 0)
985 					rv = NS_NOTFOUND;
986 				break;
987 			case nss_lt_id:
988 				if (gid != grp->gr_gid)
989 					rv = NS_NOTFOUND;
990 				break;
991 			default:
992 				break;
993 			}
994 			break;
995 		case NS_RETURN:
996 			goto fin;
997 		default:
998 			break;
999 		}
1000 		free(st->name);
1001 		st->name = NULL;
1002 		st->compat = COMPAT_MODE_OFF;
1003 		if (rv == NS_SUCCESS)
1004 			goto fin;
1005 		break;
1006 	default:
1007 		break;
1008 	}
1009 	rv = NS_NOTFOUND;
1010 	while ((line = fgetln(st->fp, &linesize)) != NULL) {
1011 		if (line[linesize-1] == '\n')
1012 			linesize--;
1013 		if (linesize > 2 && line[0] == '+') {
1014 			p = memchr(&line[1], ':', linesize);
1015 			if (p == NULL || p == &line[1])
1016 				st->compat = COMPAT_MODE_ALL;
1017 			else {
1018 				st->name = malloc(p - line);
1019 				if (st->name == NULL) {
1020 					syslog(LOG_ERR,
1021 					 "getgrent memory allocation failure");
1022 					*errnop = ENOMEM;
1023 					rv = NS_UNAVAIL;
1024 					break;
1025 				}
1026 				memcpy(st->name, &line[1], p - line - 1);
1027 				st->name[p - line - 1] = '\0';
1028 				st->compat = COMPAT_MODE_NAME;
1029 			}
1030 			goto docompat;
1031 		}
1032 		rv = __gr_match_entry(line, linesize, how, name, gid);
1033 		if (rv != NS_SUCCESS)
1034 			continue;
1035 		/* We need room at least for the line, a string NUL
1036 		 * terminator, alignment padding, and one (char *)
1037 		 * pointer for the member list terminator.
1038 		 */
1039 		if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
1040 			*errnop = ERANGE;
1041 			rv = NS_RETURN;
1042 			break;
1043 		}
1044 		memcpy(buffer, line, linesize);
1045 		buffer[linesize] = '\0';
1046 		rv = __gr_parse_entry(buffer, linesize, grp,
1047 		    &buffer[linesize + 1], bufsize - linesize - 1, errnop);
1048 		if (rv & NS_TERMINATE)
1049 			break;
1050 	}
1051 fin:
1052 	if (!stayopen && st->fp != NULL) {
1053 		fclose(st->fp);
1054 		st->fp = NULL;
1055 	}
1056 	if (rv == NS_SUCCESS && retval != NULL)
1057 		*(struct group **)retval = grp;
1058 	return (rv);
1059 #undef set_lookup_type
1060 }
1061 
1062 
1063 /*
1064  * common group line matching and parsing
1065  */
1066 int
1067 __gr_match_entry(const char *line, size_t linesize, enum nss_lookup_type how,
1068     const char *name, gid_t gid)
1069 {
1070 	size_t		 namesize;
1071 	const char	*p, *eol;
1072 	char		*q;
1073 	unsigned long	 n;
1074 	int		 i, needed;
1075 
1076 	if (linesize == 0 || is_comment_line(line, linesize))
1077 		return (NS_NOTFOUND);
1078 	switch (how) {
1079 	case nss_lt_name:	needed = 1; break;
1080 	case nss_lt_id:		needed = 2; break;
1081 	default:		needed = 2; break;
1082 	}
1083 	eol = &line[linesize];
1084 	for (p = line, i = 0; i < needed && p < eol; p++)
1085 		if (*p == ':')
1086 			i++;
1087 	if (i < needed)
1088 		return (NS_NOTFOUND);
1089 	switch (how) {
1090 	case nss_lt_name:
1091 		namesize = strlen(name);
1092 		if (namesize + 1 == (size_t)(p - line) &&
1093 		    memcmp(line, name, namesize) == 0)
1094 			return (NS_SUCCESS);
1095 		break;
1096 	case nss_lt_id:
1097 		n = strtoul(p, &q, 10);
1098 		if (q < eol && *q == ':' && gid == (gid_t)n)
1099 			return (NS_SUCCESS);
1100 		break;
1101 	case nss_lt_all:
1102 		return (NS_SUCCESS);
1103 	default:
1104 		break;
1105 	}
1106 	return (NS_NOTFOUND);
1107 }
1108 
1109 
1110 int
1111 __gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf,
1112     size_t membufsize, int *errnop)
1113 {
1114 	char	       *s_gid, *s_mem, *p, **members;
1115 	unsigned long	n;
1116 	int		maxmembers;
1117 
1118 	memset(grp, 0, sizeof(*grp));
1119 	members = (char **)_ALIGN(membuf);
1120 	membufsize -= (char *)members - membuf;
1121 	maxmembers = membufsize / sizeof(*members);
1122 	if (maxmembers <= 0 ||
1123 	    (grp->gr_name = strsep(&line, ":")) == NULL ||
1124 	    grp->gr_name[0] == '\0' ||
1125 	    (grp->gr_passwd = strsep(&line, ":")) == NULL ||
1126 	    (s_gid = strsep(&line, ":")) == NULL ||
1127 	    s_gid[0] == '\0')
1128 		return (NS_NOTFOUND);
1129 	s_mem = line;
1130 	n = strtoul(s_gid, &s_gid, 10);
1131 	if (s_gid[0] != '\0')
1132 		return (NS_NOTFOUND);
1133 	grp->gr_gid = (gid_t)n;
1134 	grp->gr_mem = members;
1135 	while (maxmembers > 1 && s_mem != NULL) {
1136 		p = strsep(&s_mem, ",");
1137 		if (p != NULL && *p != '\0') {
1138 			*members++ = p;
1139 			maxmembers--;
1140 		}
1141 	}
1142 	*members = NULL;
1143 	if (s_mem == NULL)
1144 		return (NS_SUCCESS);
1145 	else {
1146 		*errnop = ERANGE;
1147 		return (NS_RETURN);
1148 	}
1149 }
1150