xref: /illumos-gate/usr/src/lib/libc/port/gen/getgrnam_r.c (revision d48be21240dfd051b689384ce2b23479d757f2d8)
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  * Copyright (c) 2016 by Delphix. All rights reserved.
26  */
27 
28 #include "lint.h"
29 #include <mtlib.h>
30 #include <sys/types.h>
31 #include <grp.h>
32 #include <memory.h>
33 #include <deflt.h>
34 #include <nsswitch.h>
35 #include <nss_dbdefs.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <synch.h>
40 #include <sys/param.h>
41 #include <sys/mman.h>
42 #include <errno.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 it 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 	void	*defp;
248 
249 	arg.username	= username;
250 	arg.gid_array	= gid_array;
251 	arg.maxgids	= maxgids;
252 	arg.numgids	= numgids;
253 	/*
254 	 * In backwards compatibility mode, use the old str2group &
255 	 * process_cstr interfaces.  Ditto within nscd processing.
256 	 */
257 	arg.str2ent	= str2group;
258 	arg.process_cstr = process_cstr;
259 
260 	/*
261 	 * The old value being provided here was 0, ie do the quick
262 	 * way.  Given that this was never actually used under NIS
263 	 * and had the wrong (now corrected) meaning for NIS+ we need
264 	 * to change the default to be 1 (TRUE) as we now need the
265 	 * admin to decided to use netid, setting NETID_AUTHORITATIVE
266 	 * in /etc/default/nss to TRUE gets us a value of 0 for
267 	 * force_slow_way - don't you just love double negatives ;-)
268 	 *
269 	 * We need to do this to preserve the behaviour seen when the
270 	 * admin makes no changes.
271 	 */
272 	arg.force_slow_way = 1;
273 
274 	if ((defp = defopen_r(__NSW_DEFAULT_FILE)) != NULL) {
275 		if (defread_r(USE_NETID_STR, defp) != NULL)
276 			arg.force_slow_way = 0;
277 		defclose_r(defp);
278 	}
279 
280 	(void) nss_search(&db_root, _nss_initf_group,
281 	    NSS_DBOP_GROUP_BYMEMBER, &arg);
282 
283 	return (arg.numgids);
284 }
285 
286 
287 static char *
288 gettok(char **nextpp, char sep)
289 {
290 	char	*p = *nextpp;
291 	char	*q = p;
292 	char	c;
293 
294 	if (p == 0)
295 		return (0);
296 
297 	while ((c = *q) != '\0' && c != sep)
298 		q++;
299 
300 	if (c == '\0')
301 		*nextpp = 0;
302 	else {
303 		*q++ = '\0';
304 		*nextpp = q;
305 	}
306 	return (p);
307 }
308 
309 /*
310  * Return values: 0 = success, 1 = parse error, 2 = erange ...
311  * The structure pointer passed in is a structure in the caller's space
312  * wherein the field pointers would be set to areas in the buffer if
313  * need be. instring and buffer should be separate areas.
314  */
315 int
316 str2group(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
317 {
318 	struct group		*group	= (struct group *)ent;
319 	char			*p, *next;
320 	int			black_magic;	/* "+" or "-" entry */
321 	char			**memlist, **limit;
322 	ulong_t			tmp;
323 
324 	if (lenstr + 1 > buflen)
325 		return (NSS_STR_PARSE_ERANGE);
326 
327 	/*
328 	 * We copy the input string into the output buffer and
329 	 * operate on it in place.
330 	 */
331 	if (instr != buffer) {
332 		/* Overlapping buffer copies are OK */
333 		(void) memmove(buffer, instr, lenstr);
334 		buffer[lenstr] = '\0';
335 	}
336 
337 	/* quick exit do not entry fill if not needed */
338 	if (ent == (void *)NULL)
339 		return (NSS_STR_PARSE_SUCCESS);
340 
341 	next = buffer;
342 
343 	/*
344 	 * Parsers for passwd and group have always been pretty rigid;
345 	 * we wouldn't want to buck a Unix tradition
346 	 */
347 
348 	group->gr_name = p = gettok(&next, ':');
349 	if (*p == '\0') {
350 		/* Empty group-name;  not allowed */
351 		return (NSS_STR_PARSE_PARSE);
352 	}
353 
354 	/* Always return at least an empty gr_mem list */
355 	memlist	= (char **)ROUND_UP(buffer + lenstr + 1, sizeof (char *));
356 	limit	= (char **)ROUND_DOWN(buffer + buflen, sizeof (char *));
357 	*memlist = 0;
358 	group->gr_mem = memlist;
359 
360 	black_magic = (*p == '+' || *p == '-');
361 	if (black_magic) {
362 		/* Then the rest of the group entry is optional */
363 		group->gr_passwd = 0;
364 		group->gr_gid = 0;
365 	}
366 
367 	group->gr_passwd = p = gettok(&next, ':');
368 	if (p == 0) {
369 		if (black_magic)
370 			return (NSS_STR_PARSE_SUCCESS);
371 		else
372 			return (NSS_STR_PARSE_PARSE);
373 	}
374 
375 	p = next;					/* gid */
376 	if (p == 0 || *p == '\0') {
377 		if (black_magic)
378 			return (NSS_STR_PARSE_SUCCESS);
379 		else
380 			return (NSS_STR_PARSE_PARSE);
381 	}
382 	if (!black_magic) {
383 		errno = 0;
384 		tmp = strtoul(p, &next, 10);
385 		if (next == p || errno != 0) {
386 			/* gid field should be nonempty */
387 			/* also check errno from strtoul */
388 			return (NSS_STR_PARSE_PARSE);
389 		}
390 		if (tmp >= UINT32_MAX)
391 			group->gr_gid = GID_NOBODY;
392 		else
393 			group->gr_gid = (gid_t)tmp;
394 	}
395 	if (*next++ != ':') {
396 		/* Parse error, even for a '+' entry (which should have	*/
397 		/*   an empty gid field, since it's always overridden)	*/
398 		return (NSS_STR_PARSE_PARSE);
399 	}
400 
401 	/* === Could check and complain if there are any extra colons */
402 	while (memlist < limit) {
403 		p = gettok(&next, ',');
404 		if (p == 0 || *p == '\0') {
405 			*memlist = 0;
406 			/* Successfully parsed and stored */
407 			return (NSS_STR_PARSE_SUCCESS);
408 		}
409 		*memlist++ = p;
410 	}
411 	/* Out of space;  error even for black_magic */
412 	return (NSS_STR_PARSE_ERANGE);
413 }
414 
415 nss_status_t
416 process_cstr(const char *instr, int instr_len, struct nss_groupsbymem *gbm)
417 {
418 	/*
419 	 * It's possible to do a much less inefficient version of this by
420 	 * selectively duplicating code from str2group().  For now,
421 	 * however, we'll take the easy way out and implement this on
422 	 * top of str2group().
423 	 */
424 
425 	const char		*username = gbm->username;
426 	nss_XbyY_buf_t		*buf;
427 	struct group		*grp;
428 	char			**memp;
429 	char			*mem;
430 	int	parsestat;
431 
432 	buf = _nss_XbyY_buf_alloc(sizeof (struct group), NSS_BUFLEN_GROUP);
433 	if (buf == 0)
434 		return (NSS_UNAVAIL);
435 
436 	grp = (struct group *)buf->result;
437 
438 	parsestat = (*gbm->str2ent)(instr, instr_len,
439 	    grp, buf->buffer, buf->buflen);
440 
441 	if (parsestat != NSS_STR_PARSE_SUCCESS) {
442 		_nss_XbyY_buf_free(buf);
443 		return (NSS_NOTFOUND);	/* === ? */
444 	}
445 
446 	if (grp->gr_mem) {
447 		for (memp = grp->gr_mem; (memp) && ((mem = *memp) != 0);
448 		    memp++) {
449 			if (strcmp(mem, username) == 0) {
450 				gid_t	gid 	= grp->gr_gid;
451 				gid_t	*gidp	= gbm->gid_array;
452 				int	numgids	= gbm->numgids;
453 				int	i;
454 
455 				_nss_XbyY_buf_free(buf);
456 
457 				for (i = 0; i < numgids && *gidp != gid; i++,
458 				    gidp++) {
459 					;
460 				}
461 				if (i >= numgids) {
462 					if (i >= gbm->maxgids) {
463 					/* Filled the array;  stop searching */
464 						return (NSS_SUCCESS);
465 					}
466 					*gidp = gid;
467 					gbm->numgids = numgids + 1;
468 				}
469 				return (NSS_NOTFOUND);	/* Explained in   */
470 							/* <nss_dbdefs.h> */
471 			}
472 		}
473 	}
474 	_nss_XbyY_buf_free(buf);
475 	return (NSS_NOTFOUND);
476 }
477