getgrent.c revision 114256
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: head/lib/libc/gen/getgrent.c 114256 2003-04-29 21:13:50Z nectar $");
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
62enum 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
70static const ns_src defaultsrc[] = {
71	{ NSSRC_COMPAT, NS_SUCCESS },
72	{ NULL, 0 }
73};
74
75int	 __gr_match_entry(const char *, size_t, enum nss_lookup_type,
76	    const char *, gid_t);
77int	 __gr_parse_entry(char *, size_t, struct group *, char *, size_t,
78	    int *);
79
80static	int	 is_comment_line(const char *, size_t);
81
82union key {
83	const char	*name;
84	gid_t		 gid;
85};
86static	struct group *getgr(int (*)(union key, struct group *, char *, size_t,
87		    struct group **), union key);
88static	int	 wrap_getgrnam_r(union key, struct group *, char *, size_t,
89		    struct group **);
90static	int	 wrap_getgrgid_r(union key, struct group *, char *, size_t,
91		    struct group **);
92static	int	 wrap_getgrent_r(union key, struct group *, char *, size_t,
93		    struct group **);
94
95struct files_state {
96	FILE	*fp;
97	int	 stayopen;
98};
99static	void	 files_endstate(void *);
100NSS_TLS_HANDLING(files);
101static	int	 files_setgrent(void *, void *, va_list);
102static	int	 files_group(void *, void *, va_list);
103
104
105#ifdef HESIOD
106struct dns_state {
107	long	counter;
108};
109static	void	 dns_endstate(void *);
110NSS_TLS_HANDLING(dns);
111static	int	 dns_setgrent(void *, void *, va_list);
112static	int	 dns_group(void *, void *, va_list);
113#endif
114
115
116#ifdef YP
117struct nis_state {
118	char	 domain[MAXHOSTNAMELEN];
119	int	 done;
120	char	*key;
121	int	 keylen;
122};
123static	void	 nis_endstate(void *);
124NSS_TLS_HANDLING(nis);
125static	int	 nis_setgrent(void *, void *, va_list);
126static	int	 nis_group(void *, void *, va_list);
127#endif
128
129struct 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};
139static	void	 compat_endstate(void *);
140NSS_TLS_HANDLING(compat);
141static	int	 compat_setgrent(void *, void *, va_list);
142static	int	 compat_group(void *, void *, va_list);
143
144
145/* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */
146int
147setgrent(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
165int
166setgroupent(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
185void
186endgrent(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
203int
204getgrent_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
231int
232getgrnam_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
259int
260getgrgid_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
287static struct group	 grp;
288static char		*grp_storage;
289static size_t		 grp_storage_size;
290
291static struct group *
292getgr(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
322static int
323wrap_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
330static int
331wrap_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
338static int
339wrap_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
346struct group *
347getgrnam(const char *name)
348{
349	union key key;
350
351	key.name = name;
352	return (getgr(wrap_getgrnam_r, key));
353}
354
355
356struct group *
357getgrgid(gid_t gid)
358{
359	union key key;
360
361	key.gid = gid;
362	return (getgr(wrap_getgrgid_r, key));
363}
364
365
366struct group *
367getgrent(void)
368{
369	union key key;
370
371	key.gid = 0; /* not used */
372	return (getgr(wrap_getgrent_r, key));
373}
374
375
376static int
377is_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 */
393static void
394files_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
405static int
406files_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
435static int
436files_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 */
517static void
518dns_endstate(void *p)
519{
520
521	free(p);
522}
523
524
525static int
526dns_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
539static int
540dns_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));
634fin:
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 */
650static void
651nis_endstate(void *p)
652{
653
654	if (p == NULL)
655		return;
656	free(((struct nis_state *)p)->key);
657	free(p);
658}
659
660
661static int
662nis_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
677static int
678nis_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));
789fin:
790	if (rv == NS_SUCCESS && retval != NULL)
791		*(struct group **)retval = grp;
792	return (rv);
793erange:
794	*errnop = ERANGE;
795	return (NS_RETURN);
796}
797#endif /* YP */
798
799
800
801/*
802 * compat backend
803 */
804static void
805compat_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
819static int
820compat_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
881static int
882compat_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	}
950docompat:
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	}
1051fin:
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 */
1066int
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
1110int
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