xref: /freebsd/lib/libc/gen/getgrent.c (revision d056fa046c6a91b90cd98165face0e42a33a5173)
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 #ifdef NS_CACHING
61 #include "nscache.h"
62 #endif
63 
64 enum constants {
65 	GRP_STORAGE_INITIAL	= 1 << 10, /* 1 KByte */
66 	GRP_STORAGE_MAX		= 1 << 20, /* 1 MByte */
67 	SETGRENT		= 1,
68 	ENDGRENT		= 2,
69 	HESIOD_NAME_MAX		= 256,
70 };
71 
72 static const ns_src defaultsrc[] = {
73 	{ NSSRC_COMPAT, NS_SUCCESS },
74 	{ NULL, 0 }
75 };
76 
77 int	 __gr_match_entry(const char *, size_t, enum nss_lookup_type,
78 	    const char *, gid_t);
79 int	 __gr_parse_entry(char *, size_t, struct group *, char *, size_t,
80 	    int *);
81 
82 static	int	 is_comment_line(const char *, size_t);
83 
84 union key {
85 	const char	*name;
86 	gid_t		 gid;
87 };
88 static	struct group *getgr(int (*)(union key, struct group *, char *, size_t,
89 		    struct group **), union key);
90 static	int	 wrap_getgrnam_r(union key, struct group *, char *, size_t,
91 		    struct group **);
92 static	int	 wrap_getgrgid_r(union key, struct group *, char *, size_t,
93 		    struct group **);
94 static	int	 wrap_getgrent_r(union key, struct group *, char *, size_t,
95 		    struct group **);
96 
97 struct files_state {
98 	FILE	*fp;
99 	int	 stayopen;
100 };
101 static	void	 files_endstate(void *);
102 NSS_TLS_HANDLING(files);
103 static	int	 files_setgrent(void *, void *, va_list);
104 static	int	 files_group(void *, void *, va_list);
105 
106 
107 #ifdef HESIOD
108 struct dns_state {
109 	long	counter;
110 };
111 static	void	 dns_endstate(void *);
112 NSS_TLS_HANDLING(dns);
113 static	int	 dns_setgrent(void *, void *, va_list);
114 static	int	 dns_group(void *, void *, va_list);
115 #endif
116 
117 
118 #ifdef YP
119 struct nis_state {
120 	char	 domain[MAXHOSTNAMELEN];
121 	int	 done;
122 	char	*key;
123 	int	 keylen;
124 };
125 static	void	 nis_endstate(void *);
126 NSS_TLS_HANDLING(nis);
127 static	int	 nis_setgrent(void *, void *, va_list);
128 static	int	 nis_group(void *, void *, va_list);
129 #endif
130 
131 struct compat_state {
132 	FILE	*fp;
133 	int	 stayopen;
134 	char	*name;
135 	enum _compat {
136 		COMPAT_MODE_OFF = 0,
137 		COMPAT_MODE_ALL,
138 		COMPAT_MODE_NAME
139 	}	 compat;
140 };
141 static	void	 compat_endstate(void *);
142 NSS_TLS_HANDLING(compat);
143 static	int	 compat_setgrent(void *, void *, va_list);
144 static	int	 compat_group(void *, void *, va_list);
145 
146 #ifdef NS_CACHING
147 static	int	 grp_id_func(char *, size_t *, va_list, void *);
148 static	int	 grp_marshal_func(char *, size_t *, void *, va_list, void *);
149 static	int	 grp_unmarshal_func(char *, size_t, void *, va_list, void *);
150 
151 static int
152 grp_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
153 {
154 	char	*name;
155 	gid_t	gid;
156 
157 	size_t	desired_size, size;
158 	int	res = NS_UNAVAIL;
159 	enum nss_lookup_type lookup_type;
160 
161 
162 	lookup_type = (enum nss_lookup_type)cache_mdata;
163 	switch (lookup_type) {
164 	case nss_lt_name:
165 		name = va_arg(ap, char *);
166 		size = strlen(name);
167 		desired_size = sizeof(enum nss_lookup_type) + size + 1;
168 		if (desired_size > *buffer_size) {
169 			res = NS_RETURN;
170 			goto fin;
171 		}
172 
173 		memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
174 		memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
175 
176 		res = NS_SUCCESS;
177 		break;
178 	case nss_lt_id:
179 		gid = va_arg(ap, gid_t);
180 		desired_size = sizeof(enum nss_lookup_type) + sizeof(gid_t);
181 		if (desired_size > *buffer_size) {
182 			res = NS_RETURN;
183 			goto fin;
184 		}
185 
186 		memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
187 		memcpy(buffer + sizeof(enum nss_lookup_type), &gid,
188 		    sizeof(gid_t));
189 
190 		res = NS_SUCCESS;
191 		break;
192 	default:
193 		/* should be unreachable */
194 		return (NS_UNAVAIL);
195 	}
196 
197 fin:
198 	*buffer_size = desired_size;
199 	return (res);
200 }
201 
202 static int
203 grp_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
204     void *cache_mdata)
205 {
206 	char *name;
207 	gid_t gid;
208 	struct group *grp;
209 	char *orig_buf;
210 	size_t orig_buf_size;
211 
212 	struct group new_grp;
213 	size_t desired_size, size, mem_size;
214 	char *p, **mem;
215 
216 	switch ((enum nss_lookup_type)cache_mdata) {
217 	case nss_lt_name:
218 		name = va_arg(ap, char *);
219 		break;
220 	case nss_lt_id:
221 		gid = va_arg(ap, gid_t);
222 		break;
223 	case nss_lt_all:
224 		break;
225 	default:
226 		/* should be unreachable */
227 		return (NS_UNAVAIL);
228 	}
229 
230 	grp = va_arg(ap, struct group *);
231 	orig_buf = va_arg(ap, char *);
232 	orig_buf_size = va_arg(ap, size_t);
233 
234 	desired_size = _ALIGNBYTES + sizeof(struct group) + sizeof(char *);
235 
236 	if (grp->gr_name != NULL)
237 		desired_size += strlen(grp->gr_name) + 1;
238 	if (grp->gr_passwd != NULL)
239 		desired_size += strlen(grp->gr_passwd) + 1;
240 
241 	if (grp->gr_mem != NULL) {
242 		mem_size = 0;
243 		for (mem = grp->gr_mem; *mem; ++mem) {
244 			desired_size += strlen(*mem) + 1;
245 			++mem_size;
246 		}
247 
248 		desired_size += _ALIGNBYTES + (mem_size + 1) * sizeof(char *);
249 	}
250 
251 	if (desired_size > *buffer_size) {
252 		/* this assignment is here for future use */
253 		*buffer_size = desired_size;
254 		return (NS_RETURN);
255 	}
256 
257 	memcpy(&new_grp, grp, sizeof(struct group));
258 	memset(buffer, 0, desired_size);
259 
260 	*buffer_size = desired_size;
261 	p = buffer + sizeof(struct group) + sizeof(char *);
262 	memcpy(buffer + sizeof(struct group), &p, sizeof(char *));
263 	p = (char *)_ALIGN(p);
264 
265 	if (new_grp.gr_name != NULL) {
266 		size = strlen(new_grp.gr_name);
267 		memcpy(p, new_grp.gr_name, size);
268 		new_grp.gr_name = p;
269 		p += size + 1;
270 	}
271 
272 	if (new_grp.gr_passwd != NULL) {
273 		size = strlen(new_grp.gr_passwd);
274 		memcpy(p, new_grp.gr_passwd, size);
275 		new_grp.gr_passwd = p;
276 		p += size + 1;
277 	}
278 
279 	if (new_grp.gr_mem != NULL) {
280 		p = (char *)_ALIGN(p);
281 		memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size);
282 		new_grp.gr_mem = (char **)p;
283 		p += sizeof(char *) * (mem_size + 1);
284 
285 		for (mem = new_grp.gr_mem; *mem; ++mem) {
286 			size = strlen(*mem);
287 			memcpy(p, *mem, size);
288 			*mem = p;
289 			p += size + 1;
290 		}
291 	}
292 
293 	memcpy(buffer, &new_grp, sizeof(struct group));
294 	return (NS_SUCCESS);
295 }
296 
297 static int
298 grp_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
299     void *cache_mdata)
300 {
301 	char *name;
302 	gid_t gid;
303 	struct group *grp;
304 	char *orig_buf;
305 	size_t orig_buf_size;
306 	int *ret_errno;
307 
308 	char *p;
309 	char **mem;
310 
311 	switch ((enum nss_lookup_type)cache_mdata) {
312 	case nss_lt_name:
313 		name = va_arg(ap, char *);
314 		break;
315 	case nss_lt_id:
316 		gid = va_arg(ap, gid_t);
317 		break;
318 	case nss_lt_all:
319 		break;
320 	default:
321 		/* should be unreachable */
322 		return (NS_UNAVAIL);
323 	}
324 
325 	grp = va_arg(ap, struct group *);
326 	orig_buf = va_arg(ap, char *);
327 	orig_buf_size = va_arg(ap, size_t);
328 	ret_errno = va_arg(ap, int *);
329 
330 	if (orig_buf_size <
331 	    buffer_size - sizeof(struct group) - sizeof(char *)) {
332 		*ret_errno = ERANGE;
333 		return (NS_RETURN);
334 	}
335 
336 	memcpy(grp, buffer, sizeof(struct group));
337 	memcpy(&p, buffer + sizeof(struct group), sizeof(char *));
338 
339 	orig_buf = (char *)_ALIGN(orig_buf);
340 	memcpy(orig_buf, buffer + sizeof(struct group) + sizeof(char *) +
341 	    _ALIGN(p) - (size_t)p,
342 	    buffer_size - sizeof(struct group) - sizeof(char *) -
343 	    _ALIGN(p) + (size_t)p);
344 	p = (char *)_ALIGN(p);
345 
346 	NS_APPLY_OFFSET(grp->gr_name, orig_buf, p, char *);
347 	NS_APPLY_OFFSET(grp->gr_passwd, orig_buf, p, char *);
348 	if (grp->gr_mem != NULL) {
349 		NS_APPLY_OFFSET(grp->gr_mem, orig_buf, p, char **);
350 
351 		for (mem = grp->gr_mem; *mem; ++mem)
352 			NS_APPLY_OFFSET(*mem, orig_buf, p, char *);
353 	}
354 
355 	if (retval != NULL)
356 		*((struct group **)retval) = grp;
357 
358 	return (NS_SUCCESS);
359 }
360 
361 NSS_MP_CACHE_HANDLING(group);
362 #endif /* NS_CACHING */
363 
364 
365 /* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */
366 int
367 setgrent(void)
368 {
369 #ifdef NS_CACHING
370 	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
371 		group, (void *)nss_lt_all,
372 		NULL, NULL);
373 #endif
374 
375 	static const ns_dtab dtab[] = {
376 		{ NSSRC_FILES, files_setgrent, (void *)SETGRENT },
377 #ifdef HESIOD
378 		{ NSSRC_DNS, dns_setgrent, (void *)SETGRENT },
379 #endif
380 #ifdef YP
381 		{ NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
382 #endif
383 		{ NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
384 #ifdef NS_CACHING
385 		NS_CACHE_CB(&cache_info)
386 #endif
387 		{ NULL, NULL, NULL }
388 	};
389 	(void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, 0);
390 	return (1);
391 }
392 
393 
394 int
395 setgroupent(int stayopen)
396 {
397 #ifdef NS_CACHING
398 	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
399 		group, (void *)nss_lt_all,
400 		NULL, NULL);
401 #endif
402 
403 	static const ns_dtab dtab[] = {
404 		{ NSSRC_FILES, files_setgrent, (void *)SETGRENT },
405 #ifdef HESIOD
406 		{ NSSRC_DNS, dns_setgrent, (void *)SETGRENT },
407 #endif
408 #ifdef YP
409 		{ NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
410 #endif
411 		{ NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
412 #ifdef NS_CACHING
413 		NS_CACHE_CB(&cache_info)
414 #endif
415 		{ NULL, NULL, NULL }
416 	};
417 	(void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc,
418 	    stayopen);
419 	return (1);
420 }
421 
422 
423 void
424 endgrent(void)
425 {
426 #ifdef NS_CACHING
427 	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
428 		group, (void *)nss_lt_all,
429 		NULL, NULL);
430 #endif
431 
432 	static const ns_dtab dtab[] = {
433 		{ NSSRC_FILES, files_setgrent, (void *)ENDGRENT },
434 #ifdef HESIOD
435 		{ NSSRC_DNS, dns_setgrent, (void *)ENDGRENT },
436 #endif
437 #ifdef YP
438 		{ NSSRC_NIS, nis_setgrent, (void *)ENDGRENT },
439 #endif
440 		{ NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT },
441 #ifdef NS_CACHING
442 		NS_CACHE_CB(&cache_info)
443 #endif
444 		{ NULL, NULL, NULL }
445 	};
446 	(void)_nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent", defaultsrc);
447 }
448 
449 
450 int
451 getgrent_r(struct group *grp, char *buffer, size_t bufsize,
452     struct group **result)
453 {
454 #ifdef NS_CACHING
455 	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
456 		group, (void *)nss_lt_all,
457 		grp_marshal_func, grp_unmarshal_func);
458 #endif
459 
460 	static const ns_dtab dtab[] = {
461 		{ NSSRC_FILES, files_group, (void *)nss_lt_all },
462 #ifdef HESIOD
463 		{ NSSRC_DNS, dns_group, (void *)nss_lt_all },
464 #endif
465 #ifdef YP
466 		{ NSSRC_NIS, nis_group, (void *)nss_lt_all },
467 #endif
468 		{ NSSRC_COMPAT, compat_group, (void *)nss_lt_all },
469 #ifdef NS_CACHING
470 		NS_CACHE_CB(&cache_info)
471 #endif
472 		{ NULL, NULL, NULL }
473 	};
474 	int	rv, ret_errno;
475 
476 	ret_errno = 0;
477 	*result = NULL;
478 	rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrent_r", defaultsrc,
479 	    grp, buffer, bufsize, &ret_errno);
480 	if (rv == NS_SUCCESS)
481 		return (0);
482 	else
483 		return (ret_errno);
484 }
485 
486 
487 int
488 getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize,
489     struct group **result)
490 {
491 #ifdef NS_CACHING
492 	static const nss_cache_info cache_info =
493     		NS_COMMON_CACHE_INFO_INITIALIZER(
494 		group, (void *)nss_lt_name,
495 		grp_id_func, grp_marshal_func, grp_unmarshal_func);
496 #endif
497 
498 	static const ns_dtab dtab[] = {
499 		{ NSSRC_FILES, files_group, (void *)nss_lt_name },
500 #ifdef HESIOD
501 		{ NSSRC_DNS, dns_group, (void *)nss_lt_name },
502 #endif
503 #ifdef YP
504 		{ NSSRC_NIS, nis_group, (void *)nss_lt_name },
505 #endif
506 		{ NSSRC_COMPAT, compat_group, (void *)nss_lt_name },
507 #ifdef NS_CACHING
508 		NS_CACHE_CB(&cache_info)
509 #endif
510 		{ NULL, NULL, NULL }
511 	};
512 	int	rv, ret_errno;
513 
514 	ret_errno = 0;
515 	*result = NULL;
516 	rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrnam_r", defaultsrc,
517 	    name, grp, buffer, bufsize, &ret_errno);
518 	if (rv == NS_SUCCESS)
519 		return (0);
520 	else
521 		return (ret_errno);
522 }
523 
524 
525 int
526 getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
527     struct group **result)
528 {
529 #ifdef NS_CACHING
530 	static const nss_cache_info cache_info =
531     		NS_COMMON_CACHE_INFO_INITIALIZER(
532 		group, (void *)nss_lt_id,
533 		grp_id_func, grp_marshal_func, grp_unmarshal_func);
534 #endif
535 
536 	static const ns_dtab dtab[] = {
537 		{ NSSRC_FILES, files_group, (void *)nss_lt_id },
538 #ifdef HESIOD
539 		{ NSSRC_DNS, dns_group, (void *)nss_lt_id },
540 #endif
541 #ifdef YP
542 		{ NSSRC_NIS, nis_group, (void *)nss_lt_id },
543 #endif
544 		{ NSSRC_COMPAT, compat_group, (void *)nss_lt_id },
545 #ifdef NS_CACHING
546 		NS_CACHE_CB(&cache_info)
547 #endif
548 		{ NULL, NULL, NULL }
549 	};
550 	int	rv, ret_errno;
551 
552 	ret_errno = 0;
553 	*result = NULL;
554 	rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrgid_r", defaultsrc,
555 	    gid, grp, buffer, bufsize, &ret_errno);
556 	if (rv == NS_SUCCESS)
557 		return (0);
558 	else
559 		return (ret_errno);
560 }
561 
562 
563 static struct group	 grp;
564 static char		*grp_storage;
565 static size_t		 grp_storage_size;
566 
567 static struct group *
568 getgr(int (*fn)(union key, struct group *, char *, size_t, struct group **),
569     union key key)
570 {
571 	int		 rv;
572 	struct group	*res;
573 
574 	if (grp_storage == NULL) {
575 		grp_storage = malloc(GRP_STORAGE_INITIAL);
576 		if (grp_storage == NULL)
577 			return (NULL);
578 		grp_storage_size = GRP_STORAGE_INITIAL;
579 	}
580 	do {
581 		rv = fn(key, &grp, grp_storage, grp_storage_size, &res);
582 		if (res == NULL && rv == ERANGE) {
583 			free(grp_storage);
584 			if ((grp_storage_size << 1) > GRP_STORAGE_MAX) {
585 				grp_storage = NULL;
586 				errno = ERANGE;
587 				return (NULL);
588 			}
589 			grp_storage_size <<= 1;
590 			grp_storage = malloc(grp_storage_size);
591 			if (grp_storage == NULL)
592 				return (NULL);
593 		}
594 	} while (res == NULL && rv == ERANGE);
595 	if (rv != 0)
596 		errno = rv;
597 	return (res);
598 }
599 
600 
601 static int
602 wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize,
603     struct group **res)
604 {
605 	return (getgrnam_r(key.name, grp, buffer, bufsize, res));
606 }
607 
608 
609 static int
610 wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize,
611     struct group **res)
612 {
613 	return (getgrgid_r(key.gid, grp, buffer, bufsize, res));
614 }
615 
616 
617 static int
618 wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer,
619     size_t bufsize, struct group **res)
620 {
621 	return (getgrent_r(grp, buffer, bufsize, res));
622 }
623 
624 
625 struct group *
626 getgrnam(const char *name)
627 {
628 	union key key;
629 
630 	key.name = name;
631 	return (getgr(wrap_getgrnam_r, key));
632 }
633 
634 
635 struct group *
636 getgrgid(gid_t gid)
637 {
638 	union key key;
639 
640 	key.gid = gid;
641 	return (getgr(wrap_getgrgid_r, key));
642 }
643 
644 
645 struct group *
646 getgrent(void)
647 {
648 	union key key;
649 
650 	key.gid = 0; /* not used */
651 	return (getgr(wrap_getgrent_r, key));
652 }
653 
654 
655 static int
656 is_comment_line(const char *s, size_t n)
657 {
658 	const char	*eom;
659 
660 	eom = &s[n];
661 
662 	for (; s < eom; s++)
663 		if (*s == '#' || !isspace((unsigned char)*s))
664 			break;
665 	return (*s == '#' || s == eom);
666 }
667 
668 
669 /*
670  * files backend
671  */
672 static void
673 files_endstate(void *p)
674 {
675 
676 	if (p == NULL)
677 		return;
678 	if (((struct files_state *)p)->fp != NULL)
679 		fclose(((struct files_state *)p)->fp);
680 	free(p);
681 }
682 
683 
684 static int
685 files_setgrent(void *retval, void *mdata, va_list ap)
686 {
687 	struct files_state *st;
688 	int		 rv, stayopen;
689 
690 	rv = files_getstate(&st);
691 	if (rv != 0)
692 		return (NS_UNAVAIL);
693 	switch ((enum constants)mdata) {
694 	case SETGRENT:
695 		stayopen = va_arg(ap, int);
696 		if (st->fp != NULL)
697 			rewind(st->fp);
698 		else if (stayopen)
699 			st->fp = fopen(_PATH_GROUP, "r");
700 		break;
701 	case ENDGRENT:
702 		if (st->fp != NULL) {
703 			fclose(st->fp);
704 			st->fp = NULL;
705 		}
706 		break;
707 	default:
708 		break;
709 	}
710 	return (NS_UNAVAIL);
711 }
712 
713 
714 static int
715 files_group(void *retval, void *mdata, va_list ap)
716 {
717 	struct files_state	*st;
718 	enum nss_lookup_type	 how;
719 	const char		*name, *line;
720 	struct group		*grp;
721 	gid_t			 gid;
722 	char			*buffer;
723 	size_t			 bufsize, linesize;
724 	off_t			 pos;
725 	int			 rv, stayopen, *errnop;
726 
727 	name = NULL;
728 	gid = (gid_t)-1;
729 	how = (enum nss_lookup_type)mdata;
730 	switch (how) {
731 	case nss_lt_name:
732 		name = va_arg(ap, const char *);
733 		break;
734 	case nss_lt_id:
735 		gid = va_arg(ap, gid_t);
736 		break;
737 	case nss_lt_all:
738 		break;
739 	default:
740 		return (NS_NOTFOUND);
741 	}
742 	grp = va_arg(ap, struct group *);
743 	buffer = va_arg(ap, char *);
744 	bufsize = va_arg(ap, size_t);
745 	errnop = va_arg(ap, int *);
746 	*errnop = files_getstate(&st);
747 	if (*errnop != 0)
748 		return (NS_UNAVAIL);
749 	if (st->fp == NULL &&
750 	    ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) {
751 		*errnop = errno;
752 		return (NS_UNAVAIL);
753 	}
754 	if (how == nss_lt_all)
755 		stayopen = 1;
756 	else {
757 		rewind(st->fp);
758 		stayopen = st->stayopen;
759 	}
760 	rv = NS_NOTFOUND;
761 	pos = ftello(st->fp);
762 	while ((line = fgetln(st->fp, &linesize)) != NULL) {
763 		if (line[linesize-1] == '\n')
764 			linesize--;
765 		rv = __gr_match_entry(line, linesize, how, name, gid);
766 		if (rv != NS_SUCCESS)
767 			continue;
768 		/* We need room at least for the line, a string NUL
769 		 * terminator, alignment padding, and one (char *)
770 		 * pointer for the member list terminator.
771 		 */
772 		if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
773 			*errnop = ERANGE;
774 			rv = NS_RETURN;
775 			break;
776 		}
777 		memcpy(buffer, line, linesize);
778 		buffer[linesize] = '\0';
779 		rv = __gr_parse_entry(buffer, linesize, grp,
780 		    &buffer[linesize + 1], bufsize - linesize - 1, errnop);
781 		if (rv & NS_TERMINATE)
782 			break;
783 		pos = ftello(st->fp);
784 	}
785 	if (!stayopen && st->fp != NULL) {
786 		fclose(st->fp);
787 		st->fp = NULL;
788 	}
789 	if (rv == NS_SUCCESS && retval != NULL)
790 		*(struct group **)retval = grp;
791 	else if (*errnop == ERANGE)
792 		fseeko(st->fp, pos, SEEK_SET);
793 	return (rv);
794 }
795 
796 
797 #ifdef HESIOD
798 /*
799  * dns backend
800  */
801 static void
802 dns_endstate(void *p)
803 {
804 
805 	free(p);
806 }
807 
808 
809 static int
810 dns_setgrent(void *retval, void *cb_data, va_list ap)
811 {
812 	struct dns_state	*st;
813 	int			 rv;
814 
815 	rv = dns_getstate(&st);
816 	if (rv != 0)
817 		return (NS_UNAVAIL);
818 	st->counter = 0;
819 	return (NS_UNAVAIL);
820 }
821 
822 
823 static int
824 dns_group(void *retval, void *mdata, va_list ap)
825 {
826 	char			 buf[HESIOD_NAME_MAX];
827 	struct dns_state	*st;
828 	struct group		*grp;
829 	const char		*name, *label;
830 	void			*ctx;
831 	char			*buffer, **hes;
832 	size_t			 bufsize, adjsize, linesize;
833 	gid_t			 gid;
834 	enum nss_lookup_type	 how;
835 	int			 rv, *errnop;
836 
837 	ctx = NULL;
838 	hes = NULL;
839 	name = NULL;
840 	gid = (gid_t)-1;
841 	how = (enum nss_lookup_type)mdata;
842 	switch (how) {
843 	case nss_lt_name:
844 		name = va_arg(ap, const char *);
845 		break;
846 	case nss_lt_id:
847 		gid = va_arg(ap, gid_t);
848 		break;
849 	case nss_lt_all:
850 		break;
851 	}
852 	grp     = va_arg(ap, struct group *);
853 	buffer  = va_arg(ap, char *);
854 	bufsize = va_arg(ap, size_t);
855 	errnop  = va_arg(ap, int *);
856 	*errnop = dns_getstate(&st);
857 	if (*errnop != 0)
858 		return (NS_UNAVAIL);
859 	if (hesiod_init(&ctx) != 0) {
860 		*errnop = errno;
861 		rv = NS_UNAVAIL;
862 		goto fin;
863 	}
864 	do {
865 		rv = NS_NOTFOUND;
866 		switch (how) {
867 		case nss_lt_name:
868 			label = name;
869 			break;
870 		case nss_lt_id:
871 			if (snprintf(buf, sizeof(buf), "%lu",
872 			    (unsigned long)gid) >= sizeof(buf))
873 				goto fin;
874 			label = buf;
875 			break;
876 		case nss_lt_all:
877 			if (st->counter < 0)
878 				goto fin;
879 			if (snprintf(buf, sizeof(buf), "group-%ld",
880 			    st->counter++) >= sizeof(buf))
881 				goto fin;
882 			label = buf;
883 			break;
884 		}
885 		hes = hesiod_resolve(ctx, label,
886 		    how == nss_lt_id ? "gid" : "group");
887 		if ((how == nss_lt_id && hes == NULL &&
888 		    (hes = hesiod_resolve(ctx, buf, "group")) == NULL) ||
889 		    hes == NULL) {
890 			if (how == nss_lt_all)
891 				st->counter = -1;
892 			if (errno != ENOENT)
893 				*errnop = errno;
894 			goto fin;
895 		}
896 		rv = __gr_match_entry(hes[0], strlen(hes[0]), how, name, gid);
897 		if (rv != NS_SUCCESS) {
898 			hesiod_free_list(ctx, hes);
899 			hes = NULL;
900 			continue;
901 		}
902 		/* We need room at least for the line, a string NUL
903 		 * terminator, alignment padding, and one (char *)
904 		 * pointer for the member list terminator.
905 		 */
906 		adjsize = bufsize - _ALIGNBYTES - sizeof(char *);
907 		linesize = strlcpy(buffer, hes[0], adjsize);
908 		if (linesize >= adjsize) {
909 			*errnop = ERANGE;
910 			rv = NS_RETURN;
911 			goto fin;
912 		}
913 		hesiod_free_list(ctx, hes);
914 		hes = NULL;
915 		rv = __gr_parse_entry(buffer, linesize, grp,
916 		    &buffer[linesize + 1], bufsize - linesize - 1, errnop);
917 	} while (how == nss_lt_all && !(rv & NS_TERMINATE));
918 fin:
919 	if (hes != NULL)
920 		hesiod_free_list(ctx, hes);
921 	if (ctx != NULL)
922 		hesiod_end(ctx);
923 	if (rv == NS_SUCCESS && retval != NULL)
924 		*(struct group **)retval = grp;
925 	return (rv);
926 }
927 #endif /* HESIOD */
928 
929 
930 #ifdef YP
931 /*
932  * nis backend
933  */
934 static void
935 nis_endstate(void *p)
936 {
937 
938 	if (p == NULL)
939 		return;
940 	free(((struct nis_state *)p)->key);
941 	free(p);
942 }
943 
944 
945 static int
946 nis_setgrent(void *retval, void *cb_data, va_list ap)
947 {
948 	struct nis_state	*st;
949 	int			 rv;
950 
951 	rv = nis_getstate(&st);
952 	if (rv != 0)
953 		return (NS_UNAVAIL);
954 	st->done = 0;
955 	free(st->key);
956 	st->key = NULL;
957 	return (NS_UNAVAIL);
958 }
959 
960 
961 static int
962 nis_group(void *retval, void *mdata, va_list ap)
963 {
964 	char		 *map;
965 	struct nis_state *st;
966 	struct group	*grp;
967 	const char	*name;
968 	char		*buffer, *key, *result;
969 	size_t		 bufsize;
970 	gid_t		 gid;
971 	enum nss_lookup_type how;
972 	int		*errnop, keylen, resultlen, rv;
973 
974 	name = NULL;
975 	gid = (gid_t)-1;
976 	how = (enum nss_lookup_type)mdata;
977 	switch (how) {
978 	case nss_lt_name:
979 		name = va_arg(ap, const char *);
980 		map = "group.byname";
981 		break;
982 	case nss_lt_id:
983 		gid = va_arg(ap, gid_t);
984 		map = "group.bygid";
985 		break;
986 	case nss_lt_all:
987 		map = "group.byname";
988 		break;
989 	}
990 	grp     = va_arg(ap, struct group *);
991 	buffer  = va_arg(ap, char *);
992 	bufsize = va_arg(ap, size_t);
993 	errnop  = va_arg(ap, int *);
994 	*errnop = nis_getstate(&st);
995 	if (*errnop != 0)
996 		return (NS_UNAVAIL);
997 	if (st->domain[0] == '\0') {
998 		if (getdomainname(st->domain, sizeof(st->domain)) != 0) {
999 			*errnop = errno;
1000 			return (NS_UNAVAIL);
1001 		}
1002 	}
1003 	result = NULL;
1004 	do {
1005 		rv = NS_NOTFOUND;
1006 		switch (how) {
1007 		case nss_lt_name:
1008 			if (strlcpy(buffer, name, bufsize) >= bufsize)
1009 				goto erange;
1010 			break;
1011 		case nss_lt_id:
1012 			if (snprintf(buffer, bufsize, "%lu",
1013 			    (unsigned long)gid) >= bufsize)
1014 				goto erange;
1015 			break;
1016 		case nss_lt_all:
1017 			if (st->done)
1018 				goto fin;
1019 			break;
1020 		}
1021 		result = NULL;
1022 		if (how == nss_lt_all) {
1023 			if (st->key == NULL)
1024 				rv = yp_first(st->domain, map, &st->key,
1025 				    &st->keylen, &result, &resultlen);
1026 			else {
1027 				key = st->key;
1028 				keylen = st->keylen;
1029 				st->key = NULL;
1030 				rv = yp_next(st->domain, map, key, keylen,
1031 				    &st->key, &st->keylen, &result,
1032 				    &resultlen);
1033 				free(key);
1034 			}
1035 			if (rv != 0) {
1036 				free(result);
1037 				free(st->key);
1038 				st->key = NULL;
1039 				if (rv == YPERR_NOMORE) {
1040 					st->done = 1;
1041 					rv = NS_NOTFOUND;
1042 				} else
1043 					rv = NS_UNAVAIL;
1044 				goto fin;
1045 			}
1046 		} else {
1047 			rv = yp_match(st->domain, map, buffer, strlen(buffer),
1048 			    &result, &resultlen);
1049 			if (rv == YPERR_KEY) {
1050 				rv = NS_NOTFOUND;
1051 				continue;
1052 			} else if (rv != 0) {
1053 				free(result);
1054 				rv = NS_UNAVAIL;
1055 				continue;
1056 			}
1057 		}
1058 		/* We need room at least for the line, a string NUL
1059 		 * terminator, alignment padding, and one (char *)
1060 		 * pointer for the member list terminator.
1061 		 */
1062 		if (resultlen >= bufsize - _ALIGNBYTES - sizeof(char *))
1063 			goto erange;
1064 		memcpy(buffer, result, resultlen);
1065 		buffer[resultlen] = '\0';
1066 		free(result);
1067 		rv = __gr_match_entry(buffer, resultlen, how, name, gid);
1068 		if (rv == NS_SUCCESS)
1069 			rv = __gr_parse_entry(buffer, resultlen, grp,
1070 			    &buffer[resultlen+1], bufsize - resultlen - 1,
1071 			    errnop);
1072 	} while (how == nss_lt_all && !(rv & NS_TERMINATE));
1073 fin:
1074 	if (rv == NS_SUCCESS && retval != NULL)
1075 		*(struct group **)retval = grp;
1076 	return (rv);
1077 erange:
1078 	*errnop = ERANGE;
1079 	return (NS_RETURN);
1080 }
1081 #endif /* YP */
1082 
1083 
1084 
1085 /*
1086  * compat backend
1087  */
1088 static void
1089 compat_endstate(void *p)
1090 {
1091 	struct compat_state *st;
1092 
1093 	if (p == NULL)
1094 		return;
1095 	st = (struct compat_state *)p;
1096 	free(st->name);
1097 	if (st->fp != NULL)
1098 		fclose(st->fp);
1099 	free(p);
1100 }
1101 
1102 
1103 static int
1104 compat_setgrent(void *retval, void *mdata, va_list ap)
1105 {
1106 	static const ns_src compatsrc[] = {
1107 #ifdef YP
1108 		{ NSSRC_NIS, NS_SUCCESS },
1109 #endif
1110 		{ NULL, 0 }
1111 	};
1112 	ns_dtab dtab[] = {
1113 #ifdef HESIOD
1114 		{ NSSRC_DNS, dns_setgrent, NULL },
1115 #endif
1116 #ifdef YP
1117 		{ NSSRC_NIS, nis_setgrent, NULL },
1118 #endif
1119 		{ NULL, NULL, NULL }
1120 	};
1121 	struct compat_state *st;
1122 	int		 rv, stayopen;
1123 
1124 #define set_setent(x, y) do {	 				\
1125 	int i;							\
1126 								\
1127 	for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++)	\
1128 		x[i].mdata = (void *)y;				\
1129 } while (0)
1130 
1131 	rv = compat_getstate(&st);
1132 	if (rv != 0)
1133 		return (NS_UNAVAIL);
1134 	switch ((enum constants)mdata) {
1135 	case SETGRENT:
1136 		stayopen = va_arg(ap, int);
1137 		if (st->fp != NULL)
1138 			rewind(st->fp);
1139 		else if (stayopen)
1140 			st->fp = fopen(_PATH_GROUP, "r");
1141 		set_setent(dtab, mdata);
1142 		(void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent",
1143 		    compatsrc, 0);
1144 		break;
1145 	case ENDGRENT:
1146 		if (st->fp != NULL) {
1147 			fclose(st->fp);
1148 			st->fp = NULL;
1149 		}
1150 		set_setent(dtab, mdata);
1151 		(void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent",
1152 		    compatsrc, 0);
1153 		break;
1154 	default:
1155 		break;
1156 	}
1157 	st->compat = COMPAT_MODE_OFF;
1158 	free(st->name);
1159 	st->name = NULL;
1160 	return (NS_UNAVAIL);
1161 #undef set_setent
1162 }
1163 
1164 
1165 static int
1166 compat_group(void *retval, void *mdata, va_list ap)
1167 {
1168 	static const ns_src compatsrc[] = {
1169 #ifdef YP
1170 		{ NSSRC_NIS, NS_SUCCESS },
1171 #endif
1172 		{ NULL, 0 }
1173 	};
1174 	ns_dtab dtab[] = {
1175 #ifdef YP
1176 		{ NSSRC_NIS, nis_group, NULL },
1177 #endif
1178 #ifdef HESIOD
1179 		{ NSSRC_DNS, dns_group, NULL },
1180 #endif
1181 		{ NULL, NULL, NULL }
1182 	};
1183 	struct compat_state	*st;
1184 	enum nss_lookup_type	 how;
1185 	const char		*name, *line;
1186 	struct group		*grp;
1187 	gid_t			 gid;
1188 	char			*buffer, *p;
1189 	void			*discard;
1190 	size_t			 bufsize, linesize;
1191 	off_t			 pos;
1192 	int			 rv, stayopen, *errnop;
1193 
1194 #define set_lookup_type(x, y) do { 				\
1195 	int i;							\
1196 								\
1197 	for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++)	\
1198 		x[i].mdata = (void *)y;				\
1199 } while (0)
1200 
1201 	name = NULL;
1202 	gid = (gid_t)-1;
1203 	how = (enum nss_lookup_type)mdata;
1204 	switch (how) {
1205 	case nss_lt_name:
1206 		name = va_arg(ap, const char *);
1207 		break;
1208 	case nss_lt_id:
1209 		gid = va_arg(ap, gid_t);
1210 		break;
1211 	case nss_lt_all:
1212 		break;
1213 	default:
1214 		return (NS_NOTFOUND);
1215 	}
1216 	grp = va_arg(ap, struct group *);
1217 	buffer = va_arg(ap, char *);
1218 	bufsize = va_arg(ap, size_t);
1219 	errnop = va_arg(ap, int *);
1220 	*errnop = compat_getstate(&st);
1221 	if (*errnop != 0)
1222 		return (NS_UNAVAIL);
1223 	if (st->fp == NULL &&
1224 	    ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) {
1225 		*errnop = errno;
1226 		rv = NS_UNAVAIL;
1227 		goto fin;
1228 	}
1229 	if (how == nss_lt_all)
1230 		stayopen = 1;
1231 	else {
1232 		rewind(st->fp);
1233 		stayopen = st->stayopen;
1234 	}
1235 docompat:
1236 	switch (st->compat) {
1237 	case COMPAT_MODE_ALL:
1238 		set_lookup_type(dtab, how);
1239 		switch (how) {
1240 		case nss_lt_all:
1241 			rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
1242 			    "getgrent_r", compatsrc, grp, buffer, bufsize,
1243 			    errnop);
1244 			break;
1245 		case nss_lt_id:
1246 			rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
1247 			    "getgrgid_r", compatsrc, gid, grp, buffer, bufsize,
1248 			    errnop);
1249 			break;
1250 		case nss_lt_name:
1251 			rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
1252 			    "getgrnam_r", compatsrc, name, grp, buffer,
1253 			    bufsize, errnop);
1254 			break;
1255 		}
1256 		if (rv & NS_TERMINATE)
1257 			goto fin;
1258 		st->compat = COMPAT_MODE_OFF;
1259 		break;
1260 	case COMPAT_MODE_NAME:
1261 		set_lookup_type(dtab, nss_lt_name);
1262 		rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
1263 		    "getgrnam_r", compatsrc, st->name, grp, buffer, bufsize,
1264 		    errnop);
1265 		switch (rv) {
1266 		case NS_SUCCESS:
1267 			switch (how) {
1268 			case nss_lt_name:
1269 				if (strcmp(name, grp->gr_name) != 0)
1270 					rv = NS_NOTFOUND;
1271 				break;
1272 			case nss_lt_id:
1273 				if (gid != grp->gr_gid)
1274 					rv = NS_NOTFOUND;
1275 				break;
1276 			default:
1277 				break;
1278 			}
1279 			break;
1280 		case NS_RETURN:
1281 			goto fin;
1282 		default:
1283 			break;
1284 		}
1285 		free(st->name);
1286 		st->name = NULL;
1287 		st->compat = COMPAT_MODE_OFF;
1288 		if (rv == NS_SUCCESS)
1289 			goto fin;
1290 		break;
1291 	default:
1292 		break;
1293 	}
1294 	rv = NS_NOTFOUND;
1295 	pos = ftello(st->fp);
1296 	while ((line = fgetln(st->fp, &linesize)) != NULL) {
1297 		if (line[linesize-1] == '\n')
1298 			linesize--;
1299 		if (linesize > 2 && line[0] == '+') {
1300 			p = memchr(&line[1], ':', linesize);
1301 			if (p == NULL || p == &line[1])
1302 				st->compat = COMPAT_MODE_ALL;
1303 			else {
1304 				st->name = malloc(p - line);
1305 				if (st->name == NULL) {
1306 					syslog(LOG_ERR,
1307 					 "getgrent memory allocation failure");
1308 					*errnop = ENOMEM;
1309 					rv = NS_UNAVAIL;
1310 					break;
1311 				}
1312 				memcpy(st->name, &line[1], p - line - 1);
1313 				st->name[p - line - 1] = '\0';
1314 				st->compat = COMPAT_MODE_NAME;
1315 			}
1316 			goto docompat;
1317 		}
1318 		rv = __gr_match_entry(line, linesize, how, name, gid);
1319 		if (rv != NS_SUCCESS)
1320 			continue;
1321 		/* We need room at least for the line, a string NUL
1322 		 * terminator, alignment padding, and one (char *)
1323 		 * pointer for the member list terminator.
1324 		 */
1325 		if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
1326 			*errnop = ERANGE;
1327 			rv = NS_RETURN;
1328 			break;
1329 		}
1330 		memcpy(buffer, line, linesize);
1331 		buffer[linesize] = '\0';
1332 		rv = __gr_parse_entry(buffer, linesize, grp,
1333 		    &buffer[linesize + 1], bufsize - linesize - 1, errnop);
1334 		if (rv & NS_TERMINATE)
1335 			break;
1336 		pos = ftello(st->fp);
1337 	}
1338 fin:
1339 	if (!stayopen && st->fp != NULL) {
1340 		fclose(st->fp);
1341 		st->fp = NULL;
1342 	}
1343 	if (rv == NS_SUCCESS && retval != NULL)
1344 		*(struct group **)retval = grp;
1345 	else if (*errnop == ERANGE)
1346 		fseeko(st->fp, pos, SEEK_SET);
1347 	return (rv);
1348 #undef set_lookup_type
1349 }
1350 
1351 
1352 /*
1353  * common group line matching and parsing
1354  */
1355 int
1356 __gr_match_entry(const char *line, size_t linesize, enum nss_lookup_type how,
1357     const char *name, gid_t gid)
1358 {
1359 	size_t		 namesize;
1360 	const char	*p, *eol;
1361 	char		*q;
1362 	unsigned long	 n;
1363 	int		 i, needed;
1364 
1365 	if (linesize == 0 || is_comment_line(line, linesize))
1366 		return (NS_NOTFOUND);
1367 	switch (how) {
1368 	case nss_lt_name:	needed = 1; break;
1369 	case nss_lt_id:		needed = 2; break;
1370 	default:		needed = 2; break;
1371 	}
1372 	eol = &line[linesize];
1373 	for (p = line, i = 0; i < needed && p < eol; p++)
1374 		if (*p == ':')
1375 			i++;
1376 	if (i < needed)
1377 		return (NS_NOTFOUND);
1378 	switch (how) {
1379 	case nss_lt_name:
1380 		namesize = strlen(name);
1381 		if (namesize + 1 == (size_t)(p - line) &&
1382 		    memcmp(line, name, namesize) == 0)
1383 			return (NS_SUCCESS);
1384 		break;
1385 	case nss_lt_id:
1386 		n = strtoul(p, &q, 10);
1387 		if (q < eol && *q == ':' && gid == (gid_t)n)
1388 			return (NS_SUCCESS);
1389 		break;
1390 	case nss_lt_all:
1391 		return (NS_SUCCESS);
1392 	default:
1393 		break;
1394 	}
1395 	return (NS_NOTFOUND);
1396 }
1397 
1398 
1399 int
1400 __gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf,
1401     size_t membufsize, int *errnop)
1402 {
1403 	char	       *s_gid, *s_mem, *p, **members;
1404 	unsigned long	n;
1405 	int		maxmembers;
1406 
1407 	memset(grp, 0, sizeof(*grp));
1408 	members = (char **)_ALIGN(membuf);
1409 	membufsize -= (char *)members - membuf;
1410 	maxmembers = membufsize / sizeof(*members);
1411 	if (maxmembers <= 0 ||
1412 	    (grp->gr_name = strsep(&line, ":")) == NULL ||
1413 	    grp->gr_name[0] == '\0' ||
1414 	    (grp->gr_passwd = strsep(&line, ":")) == NULL ||
1415 	    (s_gid = strsep(&line, ":")) == NULL ||
1416 	    s_gid[0] == '\0')
1417 		return (NS_NOTFOUND);
1418 	s_mem = line;
1419 	n = strtoul(s_gid, &s_gid, 10);
1420 	if (s_gid[0] != '\0')
1421 		return (NS_NOTFOUND);
1422 	grp->gr_gid = (gid_t)n;
1423 	grp->gr_mem = members;
1424 	while (maxmembers > 1 && s_mem != NULL) {
1425 		p = strsep(&s_mem, ",");
1426 		if (p != NULL && *p != '\0') {
1427 			*members++ = p;
1428 			maxmembers--;
1429 		}
1430 	}
1431 	*members = NULL;
1432 	if (s_mem == NULL)
1433 		return (NS_SUCCESS);
1434 	else {
1435 		*errnop = ERANGE;
1436 		return (NS_RETURN);
1437 	}
1438 }
1439