xref: /illumos-gate/usr/src/lib/libc/port/gen/getgrnam_r.c (revision cb5caa98)
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 
50 extern int _getgroupsbymember(const char *, gid_t[], int, int);
51 int str2group(const char *, int, void *, char *, int);
52 
53 static DEFINE_NSS_DB_ROOT(db_root);
54 static DEFINE_NSS_GETENT(context);
55 
56 #define	USE_NETID_STR	"NETID_AUTHORITATIVE=TRUE"
57 
58 void
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 
68 struct group *
69 _uncached_getgrnam_r(const char *name, struct group *result, char *buffer,
70     int buflen);
71 
72 struct 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  */
79 struct 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  */
99 struct 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 
111 struct 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  */
128 int
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 
144 extern struct group *
145 _getgrnam_r(const char *name, struct group *result, char *buffer,
146 	int buflen);
147 
148 struct 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  */
165 int
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 
180 void
181 setgrent(void)
182 {
183 	nss_setent(&db_root, _nss_initf_group, &context);
184 }
185 
186 void
187 endgrent(void)
188 {
189 	nss_endent(&db_root, _nss_initf_group, &context);
190 	nss_delete(&db_root);
191 }
192 
193 struct group *
194 getgrent_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 
212 struct group *
213 fgetgrent_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 
250 static nss_status_t process_cstr(const char *, int, struct nss_groupsbymem *);
251 
252 int
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 
309 static char *
310 gettok(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  */
337 int
338 str2group(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 
436 static nss_status_t
437 process_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