xref: /illumos-gate/usr/src/lib/libc/port/gen/getgrnam_r.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #pragma weak endgrent = _endgrent
30 #pragma weak setgrent = _setgrent
31 
32 #pragma weak getgrnam_r = _getgrnam_r
33 #pragma weak getgrgid_r = _getgrgid_r
34 #pragma weak getgrent_r = _getgrent_r
35 #pragma weak fgetgrent_r = _fgetgrent_r
36 
37 #include "synonyms.h"
38 #include <mtlib.h>
39 #include <sys/types.h>
40 #include <grp.h>
41 #include <memory.h>
42 #include <nsswitch.h>
43 #include <nss_dbdefs.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <synch.h>
48 #include <sys/param.h>
49 #include <sys/mman.h>
50 
51 #define	__NSS_PRIVATE_INTERFACE
52 #include "nsswitch_priv.h"
53 #undef	__NSS_PRIVATE_INTERFACE
54 
55 extern int _getgroupsbymember(const char *, gid_t[], int, int);
56 int str2group(const char *, int, void *,
57 	char *, int);
58 
59 static DEFINE_NSS_DB_ROOT(db_root);
60 static DEFINE_NSS_GETENT(context);
61 
62 #define	USE_NETID_STR	"NETID_AUTHORITATIVE=TRUE"
63 
64 void
65 _nss_initf_group(nss_db_params_t *p)
66 {
67 	p->name	= NSS_DBNAM_GROUP;
68 	p->default_config = NSS_DEFCONF_GROUP;
69 }
70 
71 #include <getxby_door.h>
72 #include <sys/door.h>
73 
74 static struct group *
75 process_getgr(struct group *result, char *buffer, int buflen,
76     nsc_data_t *sptr, int ndata);
77 
78 struct group *
79 _uncached_getgrnam_r(const char *name, struct group *result, char *buffer,
80     int buflen);
81 
82 struct group *
83 _uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen);
84 
85 /*
86  * POSIX.1c Draft-6 version of the function getgrnam_r.
87  * It was implemented by Solaris 2.3.
88  */
89 struct group *
90 _getgrnam_r(const char *name, struct group *result, char *buffer, int buflen)
91 {
92 	/*
93 	 * allocate room on the stack for the nscd to return
94 	 * group and group member information
95 	 */
96 	union {
97 		nsc_data_t	s_d;
98 		char		s_b[8192];
99 	} space;
100 	nsc_data_t	*sptr;
101 	int		ndata;
102 	int		adata;
103 	struct group	*resptr = NULL;
104 
105 	if ((name == (const char *)NULL) ||
106 	    (strlen(name) >= (sizeof (space) - sizeof (nsc_data_t)))) {
107 		errno = ERANGE;
108 		return (NULL);
109 	}
110 
111 	ndata = sizeof (space);
112 	adata = strlen(name) + sizeof (nsc_call_t) + 1;
113 	space.s_d.nsc_call.nsc_callnumber = GETGRNAM;
114 	(void) strcpy(space.s_d.nsc_call.nsc_u.name, name);
115 	sptr = &space.s_d;
116 
117 	switch (_nsc_trydoorcall(&sptr, &ndata, &adata)) {
118 	case SUCCESS:	/* positive cache hit */
119 		break;
120 	case NOTFOUND:	/* negative cache hit */
121 		return (NULL);
122 	default:
123 		return ((struct group *)_uncached_getgrnam_r(name, result,
124 		    buffer, buflen));
125 	}
126 
127 	resptr = process_getgr(result, buffer, buflen, sptr, ndata);
128 
129 	/*
130 	 * check to see if doors reallocated memory underneath us
131 	 * if they did munmap the memory or suffer a memory leak
132 	 */
133 
134 	if (sptr != &space.s_d)
135 		munmap((void *)sptr, ndata);
136 
137 	return (resptr);
138 }
139 
140 /*
141  * POSIX.1c Draft-6 version of the function getgrgid_r.
142  * It was implemented by Solaris 2.3.
143  */
144 struct group *
145 _getgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen)
146 {
147 	/*
148 	 * allocate room on the stack for the nscd to return
149 	 * group and group member information
150 	 */
151 	union {
152 		nsc_data_t	s_d;
153 		char		s_b[8192];
154 	} space;
155 	nsc_data_t	*sptr;
156 	int		ndata;
157 	int		adata;
158 	struct group	*resptr = NULL;
159 
160 	ndata = sizeof (space);
161 	adata = sizeof (nsc_call_t) + 1;
162 	space.s_d.nsc_call.nsc_callnumber = GETGRGID;
163 	space.s_d.nsc_call.nsc_u.gid = gid;
164 	sptr = &space.s_d;
165 
166 	switch (_nsc_trydoorcall(&sptr, &ndata, &adata)) {
167 	case SUCCESS:	/* positive cache hit */
168 		break;
169 	case NOTFOUND:	/* negative cache hit */
170 		return (NULL);
171 	default:
172 		return ((struct group *)_uncached_getgrgid_r(gid, result,
173 		    buffer, buflen));
174 	}
175 
176 	resptr = process_getgr(result, buffer, buflen, sptr, ndata);
177 
178 	/*
179 	 * check to see if doors reallocated memory underneath us
180 	 * if they did munmap the memory or suffer a memory leak
181 	 */
182 
183 	if (sptr != &space.s_d)
184 		munmap((void *)sptr, ndata);
185 
186 	return (resptr);
187 }
188 /*
189  *  This routine should be rewritten - there's no reason it
190  *  cannot be the same code for 32 and 64 bit w/ a bit of care.
191  */
192 /* ARGSUSED4 */
193 static struct group *
194 process_getgr(struct group *result, char *buffer, int buflen,
195     nsc_data_t *sptr, int ndata)
196 {
197 	int i;
198 	char *fixed;
199 #ifdef	_LP64
200 	char	*buffer32;
201 	char	**gr_mem32;
202 	uptr32_t	index;
203 	struct group	group64;
204 #endif	/*	_LP64	*/
205 
206 /* align buffer on a pointer boundry 4bytes in 32bits and 8 bytes in 64bits */
207 #ifdef	_LP64
208 	fixed = (char *)(((uintptr_t)buffer + 7) & ~7);
209 #else
210 	fixed = (char *)(((uintptr_t)buffer + 3) & ~3);
211 #endif	/*	_LP64	*/
212 
213 	if (buflen <= fixed - buffer) { /* watch out for wrap-around */
214 		errno = ERANGE;
215 		return (NULL);
216 	}
217 
218 	buflen -= fixed - buffer;
219 
220 	buffer = fixed;
221 
222 #ifdef	_LP64
223 	/*
224 	 * this is just a rationality check; we need to make
225 	 * sure that there's enough space for the gr_mem array
226 	 * as well... easiest place to do that is when we copy
227 	 * them in place.
228 	 */
229 
230 	if (sptr->nsc_ret.nsc_bufferbytesused +
231 		/*
232 		 * ^^^ number of bytes from nscd
233 		 */
234 	    (sizeof (char **)) +
235 		/*
236 		 * ^^^ we need 8 bytes for gr_mem
237 		 */
238 	    (sizeof (char *) - 1) -
239 		/*
240 		 * ^^^ plus space for pssibly fixing aligment of gr_mem
241 		 */
242 	    sizeof (group32_t)
243 		/*
244 		 * ^^^ because we don't put this in the usr buffer
245 		 */
246 	    > buflen) {
247 #else
248 	if (sptr->nsc_ret.nsc_bufferbytesused - sizeof (struct group)
249 	    > buflen) {
250 #endif	/*	_LP64	*/
251 		errno = ERANGE;
252 		return (NULL);
253 	}
254 
255 	if (sptr->nsc_ret.nsc_return_code != SUCCESS)
256 		return (NULL);
257 
258 /*
259  * ncsd is a 32bit application, so use 32bit data items if we are in 64bit mode
260  */
261 #ifdef	_LP64
262 
263 	(void) memcpy(buffer,
264 	    (sptr->nsc_ret.nsc_u.buff + sizeof (group32_t)),
265 	    (sptr->nsc_ret.nsc_bufferbytesused - sizeof (group32_t)));
266 
267 	group64.gr_name = (char *)(sptr->nsc_ret.nsc_u.grp.gr_name +
268 				(uintptr_t)buffer);
269 	group64.gr_passwd = (char *)(sptr->nsc_ret.nsc_u.grp.gr_passwd +
270 				(uintptr_t)buffer);
271 	group64.gr_gid = sptr->nsc_ret.nsc_u.grp.gr_gid;
272 
273 	group64.gr_mem = (char **)((uintptr_t)buffer +
274 			sptr->nsc_ret.nsc_bufferbytesused - sizeof (group32_t));
275 	group64.gr_mem = (char **)(((uintptr_t)group64.gr_mem + 7) & ~7);
276 
277 	gr_mem32 = (char **)(uintptr_t)sptr->nsc_ret.nsc_u.grp.gr_mem;
278 	buffer32 = buffer;
279 
280 	for (i = 0; ; i++) {
281 		index = *((uptr32_t *)
282 		    ((uintptr_t)gr_mem32 + (uintptr_t)buffer32));
283 
284 		/*
285 		 * make sure there's enough space to copy the pointer...
286 		 */
287 		if (&group64.gr_mem[i + 1] >
288 		    (char **)((uintptr_t)buffer + buflen)) {
289 			errno = ERANGE;
290 			return (NULL);
291 		}
292 
293 		if (index == 0)
294 			break;
295 
296 		group64.gr_mem[i] = (char *)(index + buffer);
297 		buffer32 += sizeof (uptr32_t);
298 
299 	}
300 	group64.gr_mem[i] = NULL;
301 
302 	*result = group64;
303 #else
304 
305 	(void) memcpy(buffer,
306 	    (sptr->nsc_ret.nsc_u.buff + sizeof (struct group)),
307 	    (sptr->nsc_ret.nsc_bufferbytesused - sizeof (struct group)));
308 
309 	sptr->nsc_ret.nsc_u.grp.gr_name += (uintptr_t)buffer;
310 	sptr->nsc_ret.nsc_u.grp.gr_passwd += (uintptr_t)buffer;
311 
312 	sptr->nsc_ret.nsc_u.grp.gr_mem =
313 	    (char **)((uintptr_t)sptr->nsc_ret.nsc_u.grp.gr_mem +
314 		(uintptr_t)buffer);
315 
316 	i = 0;
317 	while (sptr->nsc_ret.nsc_u.grp.gr_mem[i]) {
318 		sptr->nsc_ret.nsc_u.grp.gr_mem[i] += (uintptr_t)buffer;
319 		i++;
320 	}
321 
322 	*result = sptr->nsc_ret.nsc_u.grp;
323 
324 #endif	/*	_LP64	*/
325 
326 	return (result);
327 }
328 
329 struct group *
330 _uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer,
331     int buflen)
332 {
333 	nss_XbyY_args_t arg;
334 
335 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
336 	arg.key.gid = gid;
337 	(void) nss_search(&db_root, _nss_initf_group,
338 				NSS_DBOP_GROUP_BYGID, &arg);
339 	return ((struct group *)NSS_XbyY_FINI(&arg));
340 }
341 
342 /*
343  * POSIX.1c standard version of the function getgrgid_r.
344  * User gets it via static getgrgid_r from the header file.
345  */
346 int
347 __posix_getgrgid_r(gid_t gid, struct group *grp, char *buffer,
348     size_t bufsize, struct group **result)
349 {
350 	int nerrno = 0;
351 	int oerrno = errno;
352 
353 	errno = 0;
354 	if ((*result = _getgrgid_r(gid, grp, buffer, (uintptr_t)bufsize))
355 		== NULL) {
356 			nerrno = errno;
357 	}
358 	errno = oerrno;
359 	return (nerrno);
360 }
361 
362 extern struct group *
363 _getgrnam_r(const char *name, struct group *result, char *buffer,
364 	int buflen);
365 
366 struct group *
367 _uncached_getgrnam_r(const char *name, struct group *result, char *buffer,
368 	int buflen)
369 {
370 	nss_XbyY_args_t arg;
371 
372 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
373 	arg.key.name = name;
374 	(void) nss_search(&db_root, _nss_initf_group,
375 			NSS_DBOP_GROUP_BYNAME, &arg);
376 	return ((struct group *)NSS_XbyY_FINI(&arg));
377 }
378 
379 /*
380  * POSIX.1c standard version of the function getgrnam_r.
381  * User gets it via static getgrnam_r from the header file.
382  */
383 int
384 __posix_getgrnam_r(const char *name, struct group *grp, char *buffer,
385     size_t bufsize, struct group **result)
386 {
387 	int nerrno = 0;
388 	int oerrno = errno;
389 
390 	if ((*result = _getgrnam_r(name, grp, buffer, (uintptr_t)bufsize))
391 		== NULL) {
392 			nerrno = errno;
393 	}
394 	errno = oerrno;
395 	return (nerrno);
396 }
397 
398 void
399 setgrent(void)
400 {
401 	nss_setent(&db_root, _nss_initf_group, &context);
402 }
403 
404 void
405 endgrent(void)
406 {
407 	nss_endent(&db_root, _nss_initf_group, &context);
408 	nss_delete(&db_root);
409 }
410 
411 struct group *
412 getgrent_r(struct group *result, char *buffer, int buflen)
413 {
414 	nss_XbyY_args_t arg;
415 	char		*nam;
416 
417 	/* In getXXent_r(), protect the unsuspecting caller from +/- entries */
418 
419 	do {
420 		NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
421 		/* No key to fill in */
422 		(void) nss_getent(&db_root, _nss_initf_group, &context, &arg);
423 	} while (arg.returnval != 0 &&
424 		(nam = ((struct group *)arg.returnval)->gr_name) != 0 &&
425 		(*nam == '+' || *nam == '-'));
426 
427 	return ((struct group *)NSS_XbyY_FINI(&arg));
428 }
429 
430 struct group *
431 fgetgrent_r(FILE *f, struct group *result, char *buffer, int buflen)
432 {
433 	extern void	_nss_XbyY_fgets(FILE *, nss_XbyY_args_t *);
434 	nss_XbyY_args_t	arg;
435 
436 	/* ... but in fgetXXent_r, the caller deserves any +/- entry he gets */
437 
438 	/* No key to fill in */
439 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
440 	_nss_XbyY_fgets(f, &arg);
441 	return ((struct group *)NSS_XbyY_FINI(&arg));
442 }
443 
444 /*
445  * _getgroupsbymember(uname, gid_array, maxgids, numgids):
446  *	Private interface for initgroups().  It returns the group ids of
447  *	groups of which the specified user is a member.
448  *
449  * Arguments:
450  *   username	Username of the putative member
451  *   gid_array	Space in which to return the gids.  The first [numgids]
452  *		elements are assumed to already contain valid gids.
453  *   maxgids	Maximum number of elements in gid_array.
454  *   numgids	Number of elements (normally 0 or 1) that already contain
455  *		valid gids.
456  * Return value:
457  *   number of valid gids in gid_array (may be zero)
458  *	or
459  *   -1 (and errno set appropriately) on errors (none currently defined)
460  */
461 
462 static nss_status_t process_cstr(const char *, int, struct nss_groupsbymem *);
463 
464 int
465 _getgroupsbymember(const char *username, gid_t gid_array[],
466     int maxgids, int numgids)
467 {
468 	struct nss_groupsbymem	arg;
469 	char defval[BUFSIZ];
470 	__NSL_FILE *defl;
471 
472 	arg.username	= username;
473 	arg.gid_array	= gid_array;
474 	arg.maxgids	= maxgids;
475 	arg.numgids	= numgids;
476 	arg.str2ent	= str2group;
477 	arg.process_cstr = process_cstr;
478 
479 	/*
480 	 * The old value being provided here was 0, ie do the quick
481 	 * way.  Given that this was never actually used under NIS
482 	 * and had the wrong (now corrected) meaning for NIS+ we need
483 	 * to change the default to be 1 (TRUE) as we now need the
484 	 * admin to decided to use netid, setting NETID_AUTHORITATIVE
485 	 * in /etc/default/nss to TRUE gets us a value of 0 for
486 	 * force_slow_way - don't you just love double negatives ;-)
487 	 *
488 	 * We need to do this to preserve the behaviour seen when the
489 	 * admin makes no changes.
490 	 */
491 	arg.force_slow_way = 1;
492 
493 	/*
494 	 * The "easy" way to do /etc/default/nss is to use the defread()
495 	 * stuff from libcmd, but since we are in libc we don't want to
496 	 * link ourselfs against libcmd, so instead we just do it by hand
497 	 */
498 
499 	if ((defl = __nsl_c_fopen(__NSW_DEFAULT_FILE, "r")) != NULL) {
500 		while (__nsl_c_fgets(defval, sizeof (defval), defl) != NULL) {
501 			if (strncmp(USE_NETID_STR, defval,
502 			    sizeof (USE_NETID_STR) - 1) == 0) {
503 				arg.force_slow_way = 0;
504 				break;
505 			}
506 		}
507 		(void) __nsl_c_fclose(defl);
508 	}
509 
510 	(void) nss_search(&db_root, _nss_initf_group,
511 			NSS_DBOP_GROUP_BYMEMBER, &arg);
512 
513 #ifdef	undef
514 	/*
515 	 * Only do this if there's existing code somewhere that relies on
516 	 *   initgroups() doing an endgrent() -- most unlikely.
517 	 */
518 	endgrent();
519 #endif	/* undef */
520 
521 	return (arg.numgids);
522 }
523 
524 
525 static char *
526 gettok(char **nextpp, char sep)
527 {
528 	char	*p = *nextpp;
529 	char	*q = p;
530 	char	c;
531 
532 	if (p == 0)
533 		return (0);
534 
535 	while ((c = *q) != '\0' && c != sep)
536 		q++;
537 
538 	if (c == '\0')
539 		*nextpp = 0;
540 	else {
541 		*q++ = '\0';
542 		*nextpp = q;
543 	}
544 	return (p);
545 }
546 
547 /*
548  * Return values: 0 = success, 1 = parse error, 2 = erange ...
549  * The structure pointer passed in is a structure in the caller's space
550  * wherein the field pointers would be set to areas in the buffer if
551  * need be. instring and buffer should be separate areas.
552  */
553 int
554 str2group(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
555 {
556 	struct group		*group	= (struct group *)ent;
557 	char			*p, *next;
558 	int			black_magic;	/* "+" or "-" entry */
559 	char			**memlist, **limit;
560 
561 	if (lenstr + 1 > buflen)
562 		return (NSS_STR_PARSE_ERANGE);
563 
564 	/*
565 	 * We copy the input string into the output buffer and
566 	 * operate on it in place.
567 	 */
568 	(void) memcpy(buffer, instr, lenstr);
569 	buffer[lenstr] = '\0';
570 
571 	next = buffer;
572 
573 	/*
574 	 * Parsers for passwd and group have always been pretty rigid;
575 	 * we wouldn't want to buck a Unix tradition
576 	 */
577 
578 	group->gr_name = p = gettok(&next, ':');
579 	if (*p == '\0') {
580 		/* Empty group-name;  not allowed */
581 		return (NSS_STR_PARSE_PARSE);
582 	}
583 
584 	/* Always return at least an empty gr_mem list */
585 	memlist	= (char **)ROUND_UP(buffer + lenstr + 1, sizeof (char *));
586 	limit	= (char **)ROUND_DOWN(buffer + buflen, sizeof (char *));
587 	*memlist = 0;
588 	group->gr_mem = memlist;
589 
590 	black_magic = (*p == '+' || *p == '-');
591 	if (black_magic) {
592 		/* Then the rest of the group entry is optional */
593 		group->gr_passwd = 0;
594 		group->gr_gid = 0;
595 	}
596 
597 	group->gr_passwd = p = gettok(&next, ':');
598 	if (p == 0) {
599 		if (black_magic)
600 			return (NSS_STR_PARSE_SUCCESS);
601 		else
602 			return (NSS_STR_PARSE_PARSE);
603 	}
604 
605 	p = next;					/* gid */
606 	if (p == 0 || *p == '\0') {
607 		if (black_magic)
608 			return (NSS_STR_PARSE_SUCCESS);
609 		else
610 			return (NSS_STR_PARSE_PARSE);
611 	}
612 	if (!black_magic) {
613 		group->gr_gid = (gid_t)strtol(p, &next, 10);
614 		if (next == p) {
615 			/* gid field should be nonempty */
616 			return (NSS_STR_PARSE_PARSE);
617 		}
618 		/*
619 		 * gids should be non-negative; anything else
620 		 * is administrative policy.
621 		 */
622 		if (group->gr_gid < 0)
623 			group->gr_gid = GID_NOBODY;
624 	}
625 	if (*next++ != ':') {
626 		/* Parse error, even for a '+' entry (which should have	*/
627 		/*   an empty gid field, since it's always overridden)	*/
628 		return (NSS_STR_PARSE_PARSE);
629 	}
630 
631 	/* === Could check and complain if there are any extra colons */
632 	while (memlist < limit) {
633 		p = gettok(&next, ',');
634 		if (p == 0 || *p == '\0') {
635 			*memlist = 0;
636 			/* Successfully parsed and stored */
637 			return (NSS_STR_PARSE_SUCCESS);
638 		}
639 		*memlist++ = p;
640 	}
641 	/* Out of space;  error even for black_magic */
642 	return (NSS_STR_PARSE_ERANGE);
643 }
644 
645 static nss_status_t
646 process_cstr(const char *instr, int instr_len, struct nss_groupsbymem *gbm)
647 {
648 	/*
649 	 * It's possible to do a much less inefficient version of this by
650 	 * selectively duplicating code from str2group().  For now,
651 	 * however, we'll take the easy way out and implement this on
652 	 * top of str2group().
653 	 */
654 
655 	const char		*username = gbm->username;
656 	nss_XbyY_buf_t		*buf;
657 	struct group		*grp;
658 	char			**memp;
659 	char			*mem;
660 	int	parsestat;
661 
662 	buf = _nss_XbyY_buf_alloc(sizeof (struct group), NSS_BUFLEN_GROUP);
663 	if (buf == 0)
664 		return (NSS_UNAVAIL);
665 
666 	grp = (struct group *)buf->result;
667 
668 	parsestat = (*gbm->str2ent)(instr, instr_len,
669 				    grp, buf->buffer, buf->buflen);
670 
671 	if (parsestat != NSS_STR_PARSE_SUCCESS) {
672 		_nss_XbyY_buf_free(buf);
673 		return (NSS_NOTFOUND);	/* === ? */
674 	}
675 
676 	if (grp->gr_mem) {
677 		for (memp = grp->gr_mem; (memp) && ((mem = *memp) != 0);
678 								memp++) {
679 			if (strcmp(mem, username) == 0) {
680 				gid_t	gid 	= grp->gr_gid;
681 				gid_t	*gidp	= gbm->gid_array;
682 				int	numgids	= gbm->numgids;
683 				int	i;
684 
685 				_nss_XbyY_buf_free(buf);
686 
687 				for (i = 0; i < numgids && *gidp != gid; i++,
688 								gidp++) {
689 					;
690 				}
691 				if (i >= numgids) {
692 					if (i >= gbm->maxgids) {
693 					/* Filled the array;  stop searching */
694 						return (NSS_SUCCESS);
695 					}
696 					*gidp = gid;
697 					gbm->numgids = numgids + 1;
698 				}
699 				return (NSS_NOTFOUND);	/* Explained in   */
700 							/* <nss_dbdefs.h> */
701 			}
702 		}
703 	}
704 	_nss_XbyY_buf_free(buf);
705 	return (NSS_NOTFOUND);
706 }
707