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