OpenSolaris_b135/lib/libproject/common/getprojent.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 (c) 1999-2001 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/types.h>
#include <user_attr.h>
#include <pwd.h>
#include <grp.h>
#include <userdefs.h>
#include <project.h>
#include <memory.h>
#include <nss_dbdefs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/mman.h>

#pragma weak setprojent = _setprojent
#pragma weak endprojent = _endprojent
#pragma weak getprojent = _getprojent
#pragma weak fgetprojent = _fgetprojent
#pragma weak getprojbyid = _getprojbyid
#pragma weak getprojbyname = _getprojbyname
#pragma weak getdefaultproj = _getdefaultproj
#pragma weak inproj = _inproj
#pragma weak getprojidbyname = _getprojidbyname

#define	DEFAULT_PROJECT	1
#define	NORMAL_PROJECT	0

static int ismember(struct project *, const char *, gid_t, int);
static int str2project(const char *, int, void *, char *, int);

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

void
_nss_initf_project(nss_db_params_t *p)
{
	p->name	= NSS_DBNAM_PROJECT;
	p->default_config = NSS_DEFCONF_PROJECT;
}

void
_setprojent(void)
{
	nss_setent(&db_root, _nss_initf_project, &context);
}

void
_endprojent(void)
{
	nss_endent(&db_root, _nss_initf_project, &context);
	nss_delete(&db_root);
}

struct project *
_getprojent(struct project *result, void *buffer, size_t buflen)
{
	nss_XbyY_args_t arg;

	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2project);
	(void) nss_getent(&db_root, _nss_initf_project, &context, &arg);
	return ((struct project *)NSS_XbyY_FINI(&arg));
}

struct project *
_fgetprojent(FILE *f, struct project *result, void *buffer, size_t buflen)
{
	extern void _nss_XbyY_fgets(FILE *, nss_XbyY_args_t *);
	nss_XbyY_args_t arg;

	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2project);
	_nss_XbyY_fgets(f, &arg);
	return ((struct project *)NSS_XbyY_FINI(&arg));
}

struct project *
_getprojbyid(projid_t projid, struct project *result,
    void *buffer, size_t buflen)
{
	nss_XbyY_args_t arg;

	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2project);
	arg.key.projid = projid;
	(void) nss_search(&db_root, _nss_initf_project,
	    NSS_DBOP_PROJECT_BYID, &arg);
	return ((struct project *)NSS_XbyY_FINI(&arg));
}

struct project *
_getprojbyname(const char *name, struct project *result,
    void *buffer, size_t buflen)
{
	nss_XbyY_args_t arg;
	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2project);
	arg.key.name = name;
	(void) nss_search(&db_root, _nss_initf_project,
	    NSS_DBOP_PROJECT_BYNAME, &arg);
	return ((struct project *)NSS_XbyY_FINI(&arg));
}

/*
 * The following routine checks if user specified by the second argument
 * is allowed to join the project specified as project structure in first
 * argument.  Information about user's default group and whether or not
 * the project specified in the first argument is user's default project
 * (i.e., user_attr, "default", "user.username", or "group.groupname"
 * should also be provided.  If is_default is set to DEFAULT_PROJECT,
 * then this function returns 1 (true), unless specified user explicitly
 * excluded with "!user", or "!group" wildcards.
 */
static int
ismember(struct project *proj, const char *user, gid_t gid, int is_default)
{
	char grbuf[NSS_BUFLEN_GROUP];
	char groupname[MAXGLEN + 1];
	int res = is_default;
	struct group grp;
	int group_ok = 0;
	char **u, **g;
	char *member;

	if (getgrgid_r(gid, &grp, grbuf, NSS_BUFLEN_GROUP) != NULL) {
		group_ok = 1;
		(void) snprintf(groupname, MAXGLEN, grp.gr_name);
	}

	/*
	 * Scan project's user list.
	 */
	for (u = proj->pj_users; *u; u++) {
		member = *u;
		if (member[0] == '!' &&
		    (strcmp(member + 1, user) == 0 ||
		    strcmp(member + 1, "*") == 0))
			return (0);
		if (strcmp(member, "*") == 0 || strcmp(member, user) == 0)
			res = 1;
	}

	/*
	 * Scan project's group list.
	 */
	for (g = proj->pj_groups; *g; g++) {
		member = *g;
		/*
		 * Check if user's default group is included here.
		 */
		if (group_ok) {
			if (member[0] == '!' &&
			    (strcmp(member + 1, groupname) == 0 ||
			    strcmp(member + 1, "*") == 0))
				return (0);
			if (strcmp(member, "*") == 0 ||
			    strcmp(member, groupname) == 0)
				res = 1;
		}
		/*
		 * Check if user is a member of one of project's groups.
		 */
		if (getgrnam_r(member, &grp, grbuf, NSS_BUFLEN_GROUP) != NULL) {
			for (u = grp.gr_mem; *u; u++)
				if (strcmp(*u, user) == 0)
					res = 1;
		}
	}
	return (res);
}

struct project *
_getdefaultproj(const char *user, struct project *result,
    void *buffer, size_t buflen)
{
	char projname[PROJNAME_MAX + 1];
	nss_XbyY_args_t arg;
	userattr_t *uattr;
	struct passwd p;
	struct group g;
	char *attrproj;

	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2project);

	/*
	 * Need user's default group ID for ismember() calls later
	 */
	if (getpwnam_r(user, &p, buffer, buflen) == NULL)
		return (NULL);

	/*
	 * Check user_attr database first
	 */
	if ((uattr = getusernam(user)) != NULL) {
		if ((attrproj = kva_match(uattr->attr, "project")) != NULL) {
			arg.key.name = attrproj;
			(void) nss_search(&db_root, _nss_initf_project,
			    NSS_DBOP_PROJECT_BYNAME, &arg);
			if ((result = NSS_XbyY_FINI(&arg)) != NULL) {
				free_userattr(uattr);
				return (result);
			}
		}
		free_userattr(uattr);
	}

	/*
	 * Check user.{username} and group.{groupname} projects
	 */
	(void) snprintf(projname, PROJNAME_MAX, "user.%s", user);
	arg.key.name = projname;
	(void) nss_search(&db_root, _nss_initf_project,
	    NSS_DBOP_PROJECT_BYNAME, &arg);
	if ((result = NSS_XbyY_FINI(&arg)) != NULL &&
	    ismember(result, user, p.pw_gid, DEFAULT_PROJECT))
		return (result);
	if (getgrgid_r(p.pw_gid, &g, buffer, buflen) != NULL) {
		(void) snprintf(projname, PROJNAME_MAX, "group.%s", g.gr_name);
		arg.key.name = projname;
		(void) nss_search(&db_root, _nss_initf_project,
		    NSS_DBOP_PROJECT_BYNAME, &arg);
		if ((result = NSS_XbyY_FINI(&arg)) != NULL &&
		    ismember(result, user, p.pw_gid, DEFAULT_PROJECT))
			return (result);
	}
	arg.key.name = "default";
	(void) nss_search(&db_root, _nss_initf_project,
	    NSS_DBOP_PROJECT_BYNAME, &arg);
	if ((result = NSS_XbyY_FINI(&arg)) != NULL &&
	    ismember(result, user, p.pw_gid, DEFAULT_PROJECT))
		return (result);
	return (NULL);
}

int
_inproj(const char *user, const char *name, void *buffer, size_t buflen)
{
	char projname[PROJNAME_MAX + 1];
	char grbuf[NSS_BUFLEN_GROUP];
	nss_XbyY_args_t arg;
	struct project proj;
	struct passwd pwd;
	userattr_t *uattr;
	struct group grp;
	char *attrproj;
	gid_t gid;

	NSS_XbyY_INIT(&arg, &proj, buffer, buflen, str2project);

	/*
	 * 0. Sanity checks.
	 */
	if (getpwnam_r(user, &pwd, buffer, buflen) == NULL)
		return (0);		/* user does not exist */
	gid = pwd.pw_gid;
	if (getprojbyname(name, &proj, buffer, buflen) == NULL)
		return (0);		/* project does not exist */

	/*
	 * 1. Check for special "default" project.
	 */
	if (strcmp("default", name) == 0)
		return (ismember(&proj, user, gid, DEFAULT_PROJECT));

	/*
	 * 2. Check user_attr database.
	 */
	if ((uattr = getusernam(user)) != NULL) {
		if ((attrproj = kva_match(uattr->attr, "project")) != NULL) {
			if (strcmp(attrproj, name) == 0) {
				free_userattr(uattr);
				return (ismember(&proj, user, gid,
				    DEFAULT_PROJECT));
			}
		}
		free_userattr(uattr);
	}

	/*
	 * 3. Check if this is a special "user.username" project.
	 *
	 * User "username" is considered to be a member of project
	 * "user.username" even if project's user lists do not
	 * include "username".
	 */
	(void) snprintf(projname, PROJNAME_MAX, "user.%s", user);
	if (strcmp(projname, name) == 0)
		return (ismember(&proj, user, gid, DEFAULT_PROJECT));

	/*
	 * 4. Check if this is a special "group.groupname" project.
	 *
	 * User "username" with default group "groupname" is considered
	 * to be a member of project "group.groupname" even if project's
	 * group list does not include "groupname".
	 */
	if (getgrgid_r(gid, &grp, grbuf, NSS_LINELEN_GROUP) != NULL) {
		(void) snprintf(projname, PROJNAME_MAX,
		    "group.%s", grp.gr_name);
		if (strcmp(projname, name) == 0)
			return (ismember(&proj, user, gid, DEFAULT_PROJECT));
	}

	/*
	 * 5. Handle all other (non-default) projects.
	 */
	return (ismember(&proj, user, gid, NORMAL_PROJECT));
}

/*
 * Just a quick wrapper around getprojbyname so that the caller does not
 * need to allocate the buffer.
 */
projid_t
_getprojidbyname(const char *name)
{
	struct project proj;
	char buf[PROJECT_BUFSZ];

	if (getprojbyname(name, &proj, &buf, PROJECT_BUFSZ) != NULL)
		return (proj.pj_projid);
	else
		return ((projid_t)-1);
}

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

	if (p == NULL)
		return (NULL);
	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.
 */
static int
str2project(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
{
	struct project *project = ent;
	char *p, *next;
	char *users, *groups;
	char **uglist;
	char **limit;

	if (lenstr + 1 > buflen)
		return (NSS_STR_PARSE_ERANGE);
	/*
	 * We copy the input string into the output buffer and
	 * operate on it in place.
	 */
	(void) memcpy(buffer, instr, lenstr);
	buffer[lenstr] = '\0';
	next = buffer;

	limit = (char **)ROUND_DOWN(buffer + buflen, sizeof (char *));

	/*
	 * Parsers for passwd and group have always been pretty rigid;
	 * we wouldn't want to buck a Unix tradition
	 */
	p = gettok(&next, ':');
	if (p == NULL || *p == '\0' || strlen(p) > PROJNAME_MAX) {
		/*
		 * empty or very long project names are not allowed
		 */
		return (NSS_STR_PARSE_ERANGE);
	}
	project->pj_name = p;

	p = gettok(&next, ':');
	if (p == NULL || *p == '\0') {
		/*
		 * projid field shouldn't be empty
		 */
		return (NSS_STR_PARSE_PARSE);
	}
	project->pj_projid = (projid_t)strtol(p, NULL, 10);
	if (project->pj_projid < 0) {
		/*
		 * projids should be positive number
		 */
		project->pj_projid = 0;
		return (NSS_STR_PARSE_PARSE);
	}

	p = gettok(&next, ':');
	if (p == NULL) {
		/*
		 * comment field can be empty but should not be last field
		 */
		return (NSS_STR_PARSE_PARSE);
	}
	project->pj_comment = p;

	if ((users = gettok(&next, ':')) == NULL) {
		/*
		 * users field should not be last field
		 */
		return (NSS_STR_PARSE_PARSE);
	}

	if ((groups = gettok(&next, ':')) == NULL) {
		/*
		 * groups field should not be last field
		 */
		return (NSS_STR_PARSE_PARSE);
	}

	if (next == NULL) {
		/*
		 * attributes field should be last
		 */
		return (NSS_STR_PARSE_PARSE);
	}

	project->pj_attr = next;

	uglist = (char **)ROUND_UP(buffer + lenstr + 1, sizeof (char *));
	*uglist = NULL;
	project->pj_users = uglist;
	while (uglist < limit) {
		p = gettok(&users, ',');
		if (p == NULL || *p == '\0') {
			*uglist = 0;
			break;
		}
		*uglist++ = p;
	}
	if (uglist >= limit)
		return (NSS_STR_PARSE_ERANGE);

	uglist++;
	*uglist = NULL;
	project->pj_groups = uglist;
	while (uglist < limit) {
		p = gettok(&groups, ',');
		if (p == NULL || *p == '\0') {
			*uglist = 0;
			break;
		}
		*uglist++ = p;
	}
	if (uglist >= limit)
		return (NSS_STR_PARSE_ERANGE);

	return (NSS_STR_PARSE_SUCCESS);
}