xref: /illumos-gate/usr/src/lib/libc/port/gen/getgrnam_r.c (revision b9175c69)
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 
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 	void	*defp;
246 
247 	arg.username	= username;
248 	arg.gid_array	= gid_array;
249 	arg.maxgids	= maxgids;
250 	arg.numgids	= numgids;
251 	/*
252 	 * In backwards compatibility mode, use the old str2group &
253 	 * process_cstr interfaces.  Ditto within nscd processing.
254 	 */
255 	arg.str2ent	= str2group;
256 	arg.process_cstr = process_cstr;
257 
258 	/*
259 	 * The old value being provided here was 0, ie do the quick
260 	 * way.  Given that this was never actually used under NIS
261 	 * and had the wrong (now corrected) meaning for NIS+ we need
262 	 * to change the default to be 1 (TRUE) as we now need the
263 	 * admin to decided to use netid, setting NETID_AUTHORITATIVE
264 	 * in /etc/default/nss to TRUE gets us a value of 0 for
265 	 * force_slow_way - don't you just love double negatives ;-)
266 	 *
267 	 * We need to do this to preserve the behaviour seen when the
268 	 * admin makes no changes.
269 	 */
270 	arg.force_slow_way = 1;
271 
272 	if ((defp = defopen_r(__NSW_DEFAULT_FILE)) != NULL) {
273 		if (defread_r(USE_NETID_STR, defp) != NULL)
274 			arg.force_slow_way = 0;
275 		defclose_r(defp);
276 	}
277 
278 	(void) nss_search(&db_root, _nss_initf_group,
279 	    NSS_DBOP_GROUP_BYMEMBER, &arg);
280 
281 	return (arg.numgids);
282 }
283 
284 
285 static char *
286 gettok(char **nextpp, char sep)
287 {
288 	char	*p = *nextpp;
289 	char	*q = p;
290 	char	c;
291 
292 	if (p == 0)
293 		return (0);
294 
295 	while ((c = *q) != '\0' && c != sep)
296 		q++;
297 
298 	if (c == '\0')
299 		*nextpp = 0;
300 	else {
301 		*q++ = '\0';
302 		*nextpp = q;
303 	}
304 	return (p);
305 }
306 
307 /*
308  * Return values: 0 = success, 1 = parse error, 2 = erange ...
309  * The structure pointer passed in is a structure in the caller's space
310  * wherein the field pointers would be set to areas in the buffer if
311  * need be. instring and buffer should be separate areas.
312  */
313 int
314 str2group(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
315 {
316 	struct group		*group	= (struct group *)ent;
317 	char			*p, *next;
318 	int			black_magic;	/* "+" or "-" entry */
319 	char			**memlist, **limit;
320 	ulong_t			tmp;
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 		tmp = strtoul(p, &next, 10);
382 		if (next == p) {
383 			/* gid field should be nonempty */
384 			return (NSS_STR_PARSE_PARSE);
385 		}
386 		if (group->gr_gid >= UINT32_MAX)
387 			group->gr_gid = GID_NOBODY;
388 		else
389 			group->gr_gid = (gid_t)tmp;
390 	}
391 	if (*next++ != ':') {
392 		/* Parse error, even for a '+' entry (which should have	*/
393 		/*   an empty gid field, since it's always overridden)	*/
394 		return (NSS_STR_PARSE_PARSE);
395 	}
396 
397 	/* === Could check and complain if there are any extra colons */
398 	while (memlist < limit) {
399 		p = gettok(&next, ',');
400 		if (p == 0 || *p == '\0') {
401 			*memlist = 0;
402 			/* Successfully parsed and stored */
403 			return (NSS_STR_PARSE_SUCCESS);
404 		}
405 		*memlist++ = p;
406 	}
407 	/* Out of space;  error even for black_magic */
408 	return (NSS_STR_PARSE_ERANGE);
409 }
410 
411 nss_status_t
412 process_cstr(const char *instr, int instr_len, struct nss_groupsbymem *gbm)
413 {
414 	/*
415 	 * It's possible to do a much less inefficient version of this by
416 	 * selectively duplicating code from str2group().  For now,
417 	 * however, we'll take the easy way out and implement this on
418 	 * top of str2group().
419 	 */
420 
421 	const char		*username = gbm->username;
422 	nss_XbyY_buf_t		*buf;
423 	struct group		*grp;
424 	char			**memp;
425 	char			*mem;
426 	int	parsestat;
427 
428 	buf = _nss_XbyY_buf_alloc(sizeof (struct group), NSS_BUFLEN_GROUP);
429 	if (buf == 0)
430 		return (NSS_UNAVAIL);
431 
432 	grp = (struct group *)buf->result;
433 
434 	parsestat = (*gbm->str2ent)(instr, instr_len,
435 	    grp, buf->buffer, buf->buflen);
436 
437 	if (parsestat != NSS_STR_PARSE_SUCCESS) {
438 		_nss_XbyY_buf_free(buf);
439 		return (NSS_NOTFOUND);	/* === ? */
440 	}
441 
442 	if (grp->gr_mem) {
443 		for (memp = grp->gr_mem; (memp) && ((mem = *memp) != 0);
444 		    memp++) {
445 			if (strcmp(mem, username) == 0) {
446 				gid_t	gid 	= grp->gr_gid;
447 				gid_t	*gidp	= gbm->gid_array;
448 				int	numgids	= gbm->numgids;
449 				int	i;
450 
451 				_nss_XbyY_buf_free(buf);
452 
453 				for (i = 0; i < numgids && *gidp != gid; i++,
454 				    gidp++) {
455 					;
456 				}
457 				if (i >= numgids) {
458 					if (i >= gbm->maxgids) {
459 					/* Filled the array;  stop searching */
460 						return (NSS_SUCCESS);
461 					}
462 					*gidp = gid;
463 					gbm->numgids = numgids + 1;
464 				}
465 				return (NSS_NOTFOUND);	/* Explained in   */
466 							/* <nss_dbdefs.h> */
467 			}
468 		}
469 	}
470 	_nss_XbyY_buf_free(buf);
471 	return (NSS_NOTFOUND);
472 }
473