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