getgrnam_r.c revision 2830:5228d1267a01
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 *, char *, int);
52
53static DEFINE_NSS_DB_ROOT(db_root);
54static DEFINE_NSS_GETENT(context);
55
56#define	USE_NETID_STR	"NETID_AUTHORITATIVE=TRUE"
57
58void
59_nss_initf_group(nss_db_params_t *p)
60{
61	p->name	= NSS_DBNAM_GROUP;
62	p->default_config = NSS_DEFCONF_GROUP;
63}
64
65#include <getxby_door.h>
66#include <sys/door.h>
67
68struct group *
69_uncached_getgrnam_r(const char *name, struct group *result, char *buffer,
70    int buflen);
71
72struct group *
73_uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen);
74
75/*
76 * POSIX.1c Draft-6 version of the function getgrnam_r.
77 * It was implemented by Solaris 2.3.
78 */
79struct group *
80_getgrnam_r(const char *name, struct group *result, char *buffer, int buflen)
81{
82	nss_XbyY_args_t arg;
83
84	if (name == (const char *)NULL) {
85		errno = ERANGE;
86		return (NULL);
87	}
88	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
89	arg.key.name = name;
90	(void) nss_search(&db_root, _nss_initf_group,
91			NSS_DBOP_GROUP_BYNAME, &arg);
92	return ((struct group *)NSS_XbyY_FINI(&arg));
93}
94
95/*
96 * POSIX.1c Draft-6 version of the function getgrgid_r.
97 * It was implemented by Solaris 2.3.
98 */
99struct group *
100_getgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen)
101{
102	nss_XbyY_args_t arg;
103
104	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
105	arg.key.gid = gid;
106	(void) nss_search(&db_root, _nss_initf_group,
107				NSS_DBOP_GROUP_BYGID, &arg);
108	return ((struct group *)NSS_XbyY_FINI(&arg));
109}
110
111struct group *
112_uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer,
113    int buflen)
114{
115	nss_XbyY_args_t arg;
116
117	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
118	arg.key.gid = gid;
119	(void) nss_search(&db_root, _nss_initf_group,
120				NSS_DBOP_GROUP_BYGID, &arg);
121	return ((struct group *)NSS_XbyY_FINI(&arg));
122}
123
124/*
125 * POSIX.1c standard version of the function getgrgid_r.
126 * User gets it via static getgrgid_r from the header file.
127 */
128int
129__posix_getgrgid_r(gid_t gid, struct group *grp, char *buffer,
130    size_t bufsize, struct group **result)
131{
132	int nerrno = 0;
133	int oerrno = errno;
134
135	errno = 0;
136	if ((*result = _getgrgid_r(gid, grp, buffer, (uintptr_t)bufsize))
137		== NULL) {
138			nerrno = errno;
139	}
140	errno = oerrno;
141	return (nerrno);
142}
143
144extern struct group *
145_getgrnam_r(const char *name, struct group *result, char *buffer,
146	int buflen);
147
148struct group *
149_uncached_getgrnam_r(const char *name, struct group *result, char *buffer,
150	int buflen)
151{
152	nss_XbyY_args_t arg;
153
154	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
155	arg.key.name = name;
156	(void) nss_search(&db_root, _nss_initf_group,
157			NSS_DBOP_GROUP_BYNAME, &arg);
158	return ((struct group *)NSS_XbyY_FINI(&arg));
159}
160
161/*
162 * POSIX.1c standard version of the function getgrnam_r.
163 * User gets it via static getgrnam_r from the header file.
164 */
165int
166__posix_getgrnam_r(const char *name, struct group *grp, char *buffer,
167    size_t bufsize, struct group **result)
168{
169	int nerrno = 0;
170	int oerrno = errno;
171
172	if ((*result = _getgrnam_r(name, grp, buffer, (uintptr_t)bufsize))
173		== NULL) {
174			nerrno = errno;
175	}
176	errno = oerrno;
177	return (nerrno);
178}
179
180void
181setgrent(void)
182{
183	nss_setent(&db_root, _nss_initf_group, &context);
184}
185
186void
187endgrent(void)
188{
189	nss_endent(&db_root, _nss_initf_group, &context);
190	nss_delete(&db_root);
191}
192
193struct group *
194getgrent_r(struct group *result, char *buffer, int buflen)
195{
196	nss_XbyY_args_t arg;
197	char		*nam;
198
199	/* In getXXent_r(), protect the unsuspecting caller from +/- entries */
200
201	do {
202		NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
203		/* No key to fill in */
204		(void) nss_getent(&db_root, _nss_initf_group, &context, &arg);
205	} while (arg.returnval != 0 &&
206		(nam = ((struct group *)arg.returnval)->gr_name) != 0 &&
207		(*nam == '+' || *nam == '-'));
208
209	return ((struct group *)NSS_XbyY_FINI(&arg));
210}
211
212struct group *
213fgetgrent_r(FILE *f, struct group *result, char *buffer, int buflen)
214{
215	extern void	_nss_XbyY_fgets(FILE *, nss_XbyY_args_t *);
216	nss_XbyY_args_t	arg;
217
218	/* ... but in fgetXXent_r, the caller deserves any +/- entry he gets */
219
220	/* No key to fill in */
221	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
222	_nss_XbyY_fgets(f, &arg);
223	return ((struct group *)NSS_XbyY_FINI(&arg));
224}
225
226/*
227 * _getgroupsbymember(uname, gid_array, maxgids, numgids):
228 *	Private interface for initgroups().  It returns the group ids of
229 *	groups of which the specified user is a member.
230 *
231 * Arguments:
232 *   username	Username of the putative member
233 *   gid_array	Space in which to return the gids.  The first [numgids]
234 *		elements are assumed to already contain valid gids.
235 *   maxgids	Maximum number of elements in gid_array.
236 *   numgids	Number of elements (normally 0 or 1) that already contain
237 *		valid gids.
238 * Return value:
239 *   number of valid gids in gid_array (may be zero)
240 *	or
241 *   -1 (and errno set appropriately) on errors (none currently defined)
242 *
243 * NSS2 Consistency enhancements:
244 *   The "files normal" format between an application and nscd for the
245 *   NSS_DBOP_GROUP_BYMEMBER nss_search operation is defined to be a
246 *   processed array of numgids [up to maxgids] gid_t values.  gid_t
247 *   values in the array are unique.
248 */
249
250static nss_status_t process_cstr(const char *, int, struct nss_groupsbymem *);
251
252int
253_getgroupsbymember(const char *username, gid_t gid_array[],
254    int maxgids, int numgids)
255{
256	struct nss_groupsbymem	arg;
257	char defval[BUFSIZ];
258	FILE *defl;
259
260	arg.username	= username;
261	arg.gid_array	= gid_array;
262	arg.maxgids	= maxgids;
263	arg.numgids	= numgids;
264	/*
265	 * In backwards compatibility mode, use the old str2group &
266	 * process_cstr interfaces.  Ditto within nscd processing.
267	 */
268	arg.str2ent	= str2group;
269	arg.process_cstr = process_cstr;
270
271	/*
272	 * The old value being provided here was 0, ie do the quick
273	 * way.  Given that this was never actually used under NIS
274	 * and had the wrong (now corrected) meaning for NIS+ we need
275	 * to change the default to be 1 (TRUE) as we now need the
276	 * admin to decided to use netid, setting NETID_AUTHORITATIVE
277	 * in /etc/default/nss to TRUE gets us a value of 0 for
278	 * force_slow_way - don't you just love double negatives ;-)
279	 *
280	 * We need to do this to preserve the behaviour seen when the
281	 * admin makes no changes.
282	 */
283	arg.force_slow_way = 1;
284
285	/*
286	 * The "easy" way to do /etc/default/nss is to use the defread()
287	 * stuff from libcmd, but since we are in libc we don't want to
288	 * link ourselfs against libcmd, so instead we just do it by hand
289	 */
290
291	if ((defl = fopen(__NSW_DEFAULT_FILE, "rF")) != NULL) {
292		while (fgets(defval, sizeof (defval), defl) != NULL) {
293			if (strncmp(USE_NETID_STR, defval,
294			    sizeof (USE_NETID_STR) - 1) == 0) {
295				arg.force_slow_way = 0;
296				break;
297			}
298		}
299		(void) fclose(defl);
300	}
301
302	(void) nss_search(&db_root, _nss_initf_group,
303			NSS_DBOP_GROUP_BYMEMBER, &arg);
304
305	return (arg.numgids);
306}
307
308
309static char *
310gettok(char **nextpp, char sep)
311{
312	char	*p = *nextpp;
313	char	*q = p;
314	char	c;
315
316	if (p == 0)
317		return (0);
318
319	while ((c = *q) != '\0' && c != sep)
320		q++;
321
322	if (c == '\0')
323		*nextpp = 0;
324	else {
325		*q++ = '\0';
326		*nextpp = q;
327	}
328	return (p);
329}
330
331/*
332 * Return values: 0 = success, 1 = parse error, 2 = erange ...
333 * The structure pointer passed in is a structure in the caller's space
334 * wherein the field pointers would be set to areas in the buffer if
335 * need be. instring and buffer should be separate areas.
336 */
337int
338str2group(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
339{
340	struct group		*group	= (struct group *)ent;
341	char			*p, *next;
342	int			black_magic;	/* "+" or "-" entry */
343	char			**memlist, **limit;
344
345	if (lenstr + 1 > buflen)
346		return (NSS_STR_PARSE_ERANGE);
347
348	/*
349	 * We copy the input string into the output buffer and
350	 * operate on it in place.
351	 */
352	if (instr != buffer) {
353		/* Overlapping buffer copies are OK */
354		(void) memmove(buffer, instr, lenstr);
355		buffer[lenstr] = '\0';
356	}
357
358	/* quick exit do not entry fill if not needed */
359	if (ent == (void *)NULL)
360		return (NSS_STR_PARSE_SUCCESS);
361
362	next = buffer;
363
364	/*
365	 * Parsers for passwd and group have always been pretty rigid;
366	 * we wouldn't want to buck a Unix tradition
367	 */
368
369	group->gr_name = p = gettok(&next, ':');
370	if (*p == '\0') {
371		/* Empty group-name;  not allowed */
372		return (NSS_STR_PARSE_PARSE);
373	}
374
375	/* Always return at least an empty gr_mem list */
376	memlist	= (char **)ROUND_UP(buffer + lenstr + 1, sizeof (char *));
377	limit	= (char **)ROUND_DOWN(buffer + buflen, sizeof (char *));
378	*memlist = 0;
379	group->gr_mem = memlist;
380
381	black_magic = (*p == '+' || *p == '-');
382	if (black_magic) {
383		/* Then the rest of the group entry is optional */
384		group->gr_passwd = 0;
385		group->gr_gid = 0;
386	}
387
388	group->gr_passwd = p = gettok(&next, ':');
389	if (p == 0) {
390		if (black_magic)
391			return (NSS_STR_PARSE_SUCCESS);
392		else
393			return (NSS_STR_PARSE_PARSE);
394	}
395
396	p = next;					/* gid */
397	if (p == 0 || *p == '\0') {
398		if (black_magic)
399			return (NSS_STR_PARSE_SUCCESS);
400		else
401			return (NSS_STR_PARSE_PARSE);
402	}
403	if (!black_magic) {
404		group->gr_gid = (gid_t)strtol(p, &next, 10);
405		if (next == p) {
406			/* gid field should be nonempty */
407			return (NSS_STR_PARSE_PARSE);
408		}
409		/*
410		 * gids should be non-negative; anything else
411		 * is administrative policy.
412		 */
413		if (group->gr_gid < 0)
414			group->gr_gid = GID_NOBODY;
415	}
416	if (*next++ != ':') {
417		/* Parse error, even for a '+' entry (which should have	*/
418		/*   an empty gid field, since it's always overridden)	*/
419		return (NSS_STR_PARSE_PARSE);
420	}
421
422	/* === Could check and complain if there are any extra colons */
423	while (memlist < limit) {
424		p = gettok(&next, ',');
425		if (p == 0 || *p == '\0') {
426			*memlist = 0;
427			/* Successfully parsed and stored */
428			return (NSS_STR_PARSE_SUCCESS);
429		}
430		*memlist++ = p;
431	}
432	/* Out of space;  error even for black_magic */
433	return (NSS_STR_PARSE_ERANGE);
434}
435
436static nss_status_t
437process_cstr(const char *instr, int instr_len, struct nss_groupsbymem *gbm)
438{
439	/*
440	 * It's possible to do a much less inefficient version of this by
441	 * selectively duplicating code from str2group().  For now,
442	 * however, we'll take the easy way out and implement this on
443	 * top of str2group().
444	 */
445
446	const char		*username = gbm->username;
447	nss_XbyY_buf_t		*buf;
448	struct group		*grp;
449	char			**memp;
450	char			*mem;
451	int	parsestat;
452
453	buf = _nss_XbyY_buf_alloc(sizeof (struct group), NSS_BUFLEN_GROUP);
454	if (buf == 0)
455		return (NSS_UNAVAIL);
456
457	grp = (struct group *)buf->result;
458
459	parsestat = (*gbm->str2ent)(instr, instr_len,
460				    grp, buf->buffer, buf->buflen);
461
462	if (parsestat != NSS_STR_PARSE_SUCCESS) {
463		_nss_XbyY_buf_free(buf);
464		return (NSS_NOTFOUND);	/* === ? */
465	}
466
467	if (grp->gr_mem) {
468		for (memp = grp->gr_mem; (memp) && ((mem = *memp) != 0);
469								memp++) {
470			if (strcmp(mem, username) == 0) {
471				gid_t	gid 	= grp->gr_gid;
472				gid_t	*gidp	= gbm->gid_array;
473				int	numgids	= gbm->numgids;
474				int	i;
475
476				_nss_XbyY_buf_free(buf);
477
478				for (i = 0; i < numgids && *gidp != gid; i++,
479								gidp++) {
480					;
481				}
482				if (i >= numgids) {
483					if (i >= gbm->maxgids) {
484					/* Filled the array;  stop searching */
485						return (NSS_SUCCESS);
486					}
487					*gidp = gid;
488					gbm->numgids = numgids + 1;
489				}
490				return (NSS_NOTFOUND);	/* Explained in   */
491							/* <nss_dbdefs.h> */
492			}
493		}
494	}
495	_nss_XbyY_buf_free(buf);
496	return (NSS_NOTFOUND);
497}
498