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