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