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