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