usr/src/lib/libc/port/gen/getgrnam_r.c
author Jon Tibble <meths@btinternet.com>
Thu, 09 Dec 2010 22:32:39 +0100
changeset 13255 4afa820d78b9
parent 10386 935ab057bcbb
permissions -rw-r--r--
298 SPARC build fails in smt_pause.o 478 Build needs fixing for pkgdepend flag day Reviewed by: [email protected] Reviewed by: [email protected] Reviewed by: [email protected] Approved by: [email protected]

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "lint.h"
#include <mtlib.h>
#include <sys/types.h>
#include <grp.h>
#include <memory.h>
#include <deflt.h>
#include <nsswitch.h>
#include <nss_dbdefs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <synch.h>
#include <sys/param.h>
#include <sys/mman.h>
#include <errno.h>

extern int _getgroupsbymember(const char *, gid_t[], int, int);
int str2group(const char *, int, void *, char *, int);

static DEFINE_NSS_DB_ROOT(db_root);
static DEFINE_NSS_GETENT(context);

#define	USE_NETID_STR	"NETID_AUTHORITATIVE=TRUE"

void
_nss_initf_group(nss_db_params_t *p)
{
	p->name	= NSS_DBNAM_GROUP;
	p->default_config = NSS_DEFCONF_GROUP;
}

#include <getxby_door.h>
#include <sys/door.h>

struct group *
_uncached_getgrnam_r(const char *name, struct group *result, char *buffer,
    int buflen);

struct group *
_uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen);

/*
 * POSIX.1c Draft-6 version of the function getgrnam_r.
 * It was implemented by Solaris 2.3.
 */
struct group *
getgrnam_r(const char *name, struct group *result, char *buffer, int buflen)
{
	nss_XbyY_args_t arg;

	if (name == (const char *)NULL) {
		errno = ERANGE;
		return (NULL);
	}
	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
	arg.key.name = name;
	(void) nss_search(&db_root, _nss_initf_group,
	    NSS_DBOP_GROUP_BYNAME, &arg);
	return ((struct group *)NSS_XbyY_FINI(&arg));
}

/*
 * POSIX.1c Draft-6 version of the function getgrgid_r.
 * It was implemented by Solaris 2.3.
 */
struct group *
getgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen)
{
	nss_XbyY_args_t arg;

	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
	arg.key.gid = gid;
	(void) nss_search(&db_root, _nss_initf_group,
	    NSS_DBOP_GROUP_BYGID, &arg);
	return ((struct group *)NSS_XbyY_FINI(&arg));
}

struct group *
_uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer,
    int buflen)
{
	nss_XbyY_args_t arg;

	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
	arg.key.gid = gid;
	(void) nss_search(&db_root, _nss_initf_group,
	    NSS_DBOP_GROUP_BYGID, &arg);
	return ((struct group *)NSS_XbyY_FINI(&arg));
}

/*
 * POSIX.1c standard version of the function getgrgid_r.
 * User gets it via static getgrgid_r from the header file.
 */
int
__posix_getgrgid_r(gid_t gid, struct group *grp, char *buffer,
    size_t bufsize, struct group **result)
{
	int nerrno = 0;
	int oerrno = errno;

	errno = 0;
	if ((*result = getgrgid_r(gid, grp, buffer, (uintptr_t)bufsize))
	    == NULL) {
			nerrno = errno;
	}
	errno = oerrno;
	return (nerrno);
}

struct group *
_uncached_getgrnam_r(const char *name, struct group *result, char *buffer,
	int buflen)
{
	nss_XbyY_args_t arg;

	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
	arg.key.name = name;
	(void) nss_search(&db_root, _nss_initf_group,
	    NSS_DBOP_GROUP_BYNAME, &arg);
	return ((struct group *)NSS_XbyY_FINI(&arg));
}

/*
 * POSIX.1c standard version of the function getgrnam_r.
 * User gets it via static getgrnam_r from the header file.
 */
int
__posix_getgrnam_r(const char *name, struct group *grp, char *buffer,
    size_t bufsize, struct group **result)
{
	int nerrno = 0;
	int oerrno = errno;

	if ((*result = getgrnam_r(name, grp, buffer, (uintptr_t)bufsize))
	    == NULL) {
			nerrno = errno;
	}
	errno = oerrno;
	return (nerrno);
}

void
setgrent(void)
{
	nss_setent(&db_root, _nss_initf_group, &context);
}

void
endgrent(void)
{
	nss_endent(&db_root, _nss_initf_group, &context);
	nss_delete(&db_root);
}

struct group *
getgrent_r(struct group *result, char *buffer, int buflen)
{
	nss_XbyY_args_t arg;
	char		*nam;

	/* In getXXent_r(), protect the unsuspecting caller from +/- entries */

	do {
		NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
		/* No key to fill in */
		(void) nss_getent(&db_root, _nss_initf_group, &context, &arg);
	} while (arg.returnval != 0 &&
	    (nam = ((struct group *)arg.returnval)->gr_name) != 0 &&
	    (*nam == '+' || *nam == '-'));

	return ((struct group *)NSS_XbyY_FINI(&arg));
}

struct group *
fgetgrent_r(FILE *f, struct group *result, char *buffer, int buflen)
{
	extern void	_nss_XbyY_fgets(FILE *, nss_XbyY_args_t *);
	nss_XbyY_args_t	arg;

	/* ... but in fgetXXent_r, the caller deserves any +/- entry he gets */

	/* No key to fill in */
	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
	_nss_XbyY_fgets(f, &arg);
	return ((struct group *)NSS_XbyY_FINI(&arg));
}

/*
 * _getgroupsbymember(uname, gid_array, maxgids, numgids):
 *	Private interface for initgroups().  It returns the group ids of
 *	groups of which the specified user is a member.
 *
 * Arguments:
 *   username	Username of the putative member
 *   gid_array	Space in which to return the gids.  The first [numgids]
 *		elements are assumed to already contain valid gids.
 *   maxgids	Maximum number of elements in gid_array.
 *   numgids	Number of elements (normally 0 or 1) that already contain
 *		valid gids.
 * Return value:
 *   number of valid gids in gid_array (may be zero)
 *	or
 *   -1 (and errno set appropriately) on errors (none currently defined)
 *
 * NSS2 Consistency enhancements:
 *   The "files normal" format between an application and nscd for the
 *   NSS_DBOP_GROUP_BYMEMBER nss_search operation is defined to be a
 *   processed array of numgids [up to maxgids] gid_t values.  gid_t
 *   values in the array are unique.
 */

extern nss_status_t process_cstr(const char *, int, struct nss_groupsbymem *);

int
_getgroupsbymember(const char *username, gid_t gid_array[],
    int maxgids, int numgids)
{
	struct nss_groupsbymem	arg;
	void	*defp;

	arg.username	= username;
	arg.gid_array	= gid_array;
	arg.maxgids	= maxgids;
	arg.numgids	= numgids;
	/*
	 * In backwards compatibility mode, use the old str2group &
	 * process_cstr interfaces.  Ditto within nscd processing.
	 */
	arg.str2ent	= str2group;
	arg.process_cstr = process_cstr;

	/*
	 * The old value being provided here was 0, ie do the quick
	 * way.  Given that this was never actually used under NIS
	 * and had the wrong (now corrected) meaning for NIS+ we need
	 * to change the default to be 1 (TRUE) as we now need the
	 * admin to decided to use netid, setting NETID_AUTHORITATIVE
	 * in /etc/default/nss to TRUE gets us a value of 0 for
	 * force_slow_way - don't you just love double negatives ;-)
	 *
	 * We need to do this to preserve the behaviour seen when the
	 * admin makes no changes.
	 */
	arg.force_slow_way = 1;

	if ((defp = defopen_r(__NSW_DEFAULT_FILE)) != NULL) {
		if (defread_r(USE_NETID_STR, defp) != NULL)
			arg.force_slow_way = 0;
		defclose_r(defp);
	}

	(void) nss_search(&db_root, _nss_initf_group,
	    NSS_DBOP_GROUP_BYMEMBER, &arg);

	return (arg.numgids);
}


static char *
gettok(char **nextpp, char sep)
{
	char	*p = *nextpp;
	char	*q = p;
	char	c;

	if (p == 0)
		return (0);

	while ((c = *q) != '\0' && c != sep)
		q++;

	if (c == '\0')
		*nextpp = 0;
	else {
		*q++ = '\0';
		*nextpp = q;
	}
	return (p);
}

/*
 * Return values: 0 = success, 1 = parse error, 2 = erange ...
 * The structure pointer passed in is a structure in the caller's space
 * wherein the field pointers would be set to areas in the buffer if
 * need be. instring and buffer should be separate areas.
 */
int
str2group(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
{
	struct group		*group	= (struct group *)ent;
	char			*p, *next;
	int			black_magic;	/* "+" or "-" entry */
	char			**memlist, **limit;
	ulong_t			tmp;

	if (lenstr + 1 > buflen)
		return (NSS_STR_PARSE_ERANGE);

	/*
	 * We copy the input string into the output buffer and
	 * operate on it in place.
	 */
	if (instr != buffer) {
		/* Overlapping buffer copies are OK */
		(void) memmove(buffer, instr, lenstr);
		buffer[lenstr] = '\0';
	}

	/* quick exit do not entry fill if not needed */
	if (ent == (void *)NULL)
		return (NSS_STR_PARSE_SUCCESS);

	next = buffer;

	/*
	 * Parsers for passwd and group have always been pretty rigid;
	 * we wouldn't want to buck a Unix tradition
	 */

	group->gr_name = p = gettok(&next, ':');
	if (*p == '\0') {
		/* Empty group-name;  not allowed */
		return (NSS_STR_PARSE_PARSE);
	}

	/* Always return at least an empty gr_mem list */
	memlist	= (char **)ROUND_UP(buffer + lenstr + 1, sizeof (char *));
	limit	= (char **)ROUND_DOWN(buffer + buflen, sizeof (char *));
	*memlist = 0;
	group->gr_mem = memlist;

	black_magic = (*p == '+' || *p == '-');
	if (black_magic) {
		/* Then the rest of the group entry is optional */
		group->gr_passwd = 0;
		group->gr_gid = 0;
	}

	group->gr_passwd = p = gettok(&next, ':');
	if (p == 0) {
		if (black_magic)
			return (NSS_STR_PARSE_SUCCESS);
		else
			return (NSS_STR_PARSE_PARSE);
	}

	p = next;					/* gid */
	if (p == 0 || *p == '\0') {
		if (black_magic)
			return (NSS_STR_PARSE_SUCCESS);
		else
			return (NSS_STR_PARSE_PARSE);
	}
	if (!black_magic) {
		errno = 0;
		tmp = strtoul(p, &next, 10);
		if (next == p || errno != 0) {
			/* gid field should be nonempty */
			/* also check errno from strtoul */
			return (NSS_STR_PARSE_PARSE);
		}
		if (tmp >= UINT32_MAX)
			group->gr_gid = GID_NOBODY;
		else
			group->gr_gid = (gid_t)tmp;
	}
	if (*next++ != ':') {
		/* Parse error, even for a '+' entry (which should have	*/
		/*   an empty gid field, since it's always overridden)	*/
		return (NSS_STR_PARSE_PARSE);
	}

	/* === Could check and complain if there are any extra colons */
	while (memlist < limit) {
		p = gettok(&next, ',');
		if (p == 0 || *p == '\0') {
			*memlist = 0;
			/* Successfully parsed and stored */
			return (NSS_STR_PARSE_SUCCESS);
		}
		*memlist++ = p;
	}
	/* Out of space;  error even for black_magic */
	return (NSS_STR_PARSE_ERANGE);
}

nss_status_t
process_cstr(const char *instr, int instr_len, struct nss_groupsbymem *gbm)
{
	/*
	 * It's possible to do a much less inefficient version of this by
	 * selectively duplicating code from str2group().  For now,
	 * however, we'll take the easy way out and implement this on
	 * top of str2group().
	 */

	const char		*username = gbm->username;
	nss_XbyY_buf_t		*buf;
	struct group		*grp;
	char			**memp;
	char			*mem;
	int	parsestat;

	buf = _nss_XbyY_buf_alloc(sizeof (struct group), NSS_BUFLEN_GROUP);
	if (buf == 0)
		return (NSS_UNAVAIL);

	grp = (struct group *)buf->result;

	parsestat = (*gbm->str2ent)(instr, instr_len,
	    grp, buf->buffer, buf->buflen);

	if (parsestat != NSS_STR_PARSE_SUCCESS) {
		_nss_XbyY_buf_free(buf);
		return (NSS_NOTFOUND);	/* === ? */
	}

	if (grp->gr_mem) {
		for (memp = grp->gr_mem; (memp) && ((mem = *memp) != 0);
		    memp++) {
			if (strcmp(mem, username) == 0) {
				gid_t	gid 	= grp->gr_gid;
				gid_t	*gidp	= gbm->gid_array;
				int	numgids	= gbm->numgids;
				int	i;

				_nss_XbyY_buf_free(buf);

				for (i = 0; i < numgids && *gidp != gid; i++,
				    gidp++) {
					;
				}
				if (i >= numgids) {
					if (i >= gbm->maxgids) {
					/* Filled the array;  stop searching */
						return (NSS_SUCCESS);
					}
					*gidp = gid;
					gbm->numgids = numgids + 1;
				}
				return (NSS_NOTFOUND);	/* Explained in   */
							/* <nss_dbdefs.h> */
			}
		}
	}
	_nss_XbyY_buf_free(buf);
	return (NSS_NOTFOUND);
}