2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 1991, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "lint.h"
2N/A#include <mtlib.h>
2N/A#include <sys/types.h>
2N/A#include <grp.h>
2N/A#include <memory.h>
2N/A#include <deflt.h>
2N/A#include <nsswitch.h>
2N/A#include <nss_dbdefs.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <synch.h>
2N/A#include <sys/param.h>
2N/A#include <sys/mman.h>
2N/A#include <errno.h>
2N/A
2N/Aextern int _getgroupsbymember(const char *, gid_t[], int, int);
2N/Aint str2group(const char *, int, void *, char *, int);
2N/A
2N/Astatic DEFINE_NSS_DB_ROOT(db_root);
2N/Astatic DEFINE_NSS_GETENT(context);
2N/A
2N/A#define USE_NETID_STR "NETID_AUTHORITATIVE=TRUE"
2N/A
2N/Avoid
2N/A_nss_initf_group(nss_db_params_t *p)
2N/A{
2N/A p->name = NSS_DBNAM_GROUP;
2N/A p->default_config = NSS_DEFCONF_GROUP;
2N/A}
2N/A
2N/A#include <getxby_door.h>
2N/A#include <sys/door.h>
2N/A
2N/Astruct group *
2N/A_uncached_getgrnam_r(const char *name, struct group *result, char *buffer,
2N/A int buflen);
2N/A
2N/Astruct group *
2N/A_uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen);
2N/A
2N/A/*
2N/A * POSIX.1c Draft-6 version of the function getgrnam_r.
2N/A * It was implemented by Solaris 2.3.
2N/A */
2N/Astruct group *
2N/Agetgrnam_r(const char *name, struct group *result, char *buffer, int buflen)
2N/A{
2N/A nss_XbyY_args_t arg;
2N/A
2N/A if (name == (const char *)NULL) {
2N/A errno = ERANGE;
2N/A return (NULL);
2N/A }
2N/A NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
2N/A arg.key.name = name;
2N/A (void) nss_search(&db_root, _nss_initf_group,
2N/A NSS_DBOP_GROUP_BYNAME, &arg);
2N/A return ((struct group *)NSS_XbyY_FINI(&arg));
2N/A}
2N/A
2N/A/*
2N/A * POSIX.1c Draft-6 version of the function getgrgid_r.
2N/A * It was implemented by Solaris 2.3.
2N/A */
2N/Astruct group *
2N/Agetgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen)
2N/A{
2N/A nss_XbyY_args_t arg;
2N/A
2N/A NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
2N/A arg.key.gid = gid;
2N/A (void) nss_search(&db_root, _nss_initf_group,
2N/A NSS_DBOP_GROUP_BYGID, &arg);
2N/A return ((struct group *)NSS_XbyY_FINI(&arg));
2N/A}
2N/A
2N/Astruct group *
2N/A_uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer,
2N/A int buflen)
2N/A{
2N/A nss_XbyY_args_t arg;
2N/A
2N/A NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
2N/A arg.key.gid = gid;
2N/A (void) nss_search(&db_root, _nss_initf_group,
2N/A NSS_DBOP_GROUP_BYGID, &arg);
2N/A return ((struct group *)NSS_XbyY_FINI(&arg));
2N/A}
2N/A
2N/A/*
2N/A * POSIX.1c standard version of the function getgrgid_r.
2N/A * User gets it via static getgrgid_r from the header file.
2N/A */
2N/Aint
2N/A__posix_getgrgid_r(gid_t gid, struct group *grp, char *buffer,
2N/A size_t bufsize, struct group **result)
2N/A{
2N/A int nerrno = 0;
2N/A int oerrno = errno;
2N/A
2N/A errno = 0;
2N/A if ((*result = getgrgid_r(gid, grp, buffer, (uintptr_t)bufsize))
2N/A == NULL) {
2N/A nerrno = errno;
2N/A }
2N/A errno = oerrno;
2N/A return (nerrno);
2N/A}
2N/A
2N/Astruct group *
2N/A_uncached_getgrnam_r(const char *name, struct group *result, char *buffer,
2N/A int buflen)
2N/A{
2N/A nss_XbyY_args_t arg;
2N/A
2N/A NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
2N/A arg.key.name = name;
2N/A (void) nss_search(&db_root, _nss_initf_group,
2N/A NSS_DBOP_GROUP_BYNAME, &arg);
2N/A return ((struct group *)NSS_XbyY_FINI(&arg));
2N/A}
2N/A
2N/A/*
2N/A * POSIX.1c standard version of the function getgrnam_r.
2N/A * User gets it via static getgrnam_r from the header file.
2N/A */
2N/Aint
2N/A__posix_getgrnam_r(const char *name, struct group *grp, char *buffer,
2N/A size_t bufsize, struct group **result)
2N/A{
2N/A int nerrno = 0;
2N/A int oerrno = errno;
2N/A
2N/A if ((*result = getgrnam_r(name, grp, buffer, (uintptr_t)bufsize))
2N/A == NULL) {
2N/A nerrno = errno;
2N/A }
2N/A errno = oerrno;
2N/A return (nerrno);
2N/A}
2N/A
2N/Avoid
2N/Asetgrent(void)
2N/A{
2N/A nss_setent(&db_root, _nss_initf_group, &context);
2N/A}
2N/A
2N/Avoid
2N/Aendgrent(void)
2N/A{
2N/A nss_endent(&db_root, _nss_initf_group, &context);
2N/A nss_delete(&db_root);
2N/A}
2N/A
2N/Astruct group *
2N/Agetgrent_r(struct group *result, char *buffer, int buflen)
2N/A{
2N/A nss_XbyY_args_t arg;
2N/A char *nam;
2N/A
2N/A /* In getXXent_r(), protect the unsuspecting caller from +/- entries */
2N/A
2N/A do {
2N/A NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
2N/A /* No key to fill in */
2N/A (void) nss_getent(&db_root, _nss_initf_group, &context, &arg);
2N/A } while (arg.returnval != 0 &&
2N/A (nam = ((struct group *)arg.returnval)->gr_name) != 0 &&
2N/A (*nam == '+' || *nam == '-'));
2N/A
2N/A return ((struct group *)NSS_XbyY_FINI(&arg));
2N/A}
2N/A
2N/Astruct group *
2N/Afgetgrent_r(FILE *f, struct group *result, char *buffer, int buflen)
2N/A{
2N/A extern void _nss_XbyY_fgets(FILE *, nss_XbyY_args_t *);
2N/A nss_XbyY_args_t arg;
2N/A
2N/A /* ... but in fgetXXent_r, the caller deserves any +/- entry he gets */
2N/A
2N/A /* No key to fill in */
2N/A NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group);
2N/A _nss_XbyY_fgets(f, &arg);
2N/A return ((struct group *)NSS_XbyY_FINI(&arg));
2N/A}
2N/A
2N/A/*
2N/A * _getgroupsbymember(uname, gid_array, maxgids, numgids):
2N/A * Private interface for initgroups(). It returns the group ids of
2N/A * groups of which the specified user is a member.
2N/A *
2N/A * Arguments:
2N/A * username Username of the putative member
2N/A * gid_array Space in which to return the gids. The first [numgids]
2N/A * elements are assumed to already contain valid gids.
2N/A * maxgids Maximum number of elements in gid_array.
2N/A * numgids Number of elements (normally 0 or 1) that already contain
2N/A * valid gids.
2N/A * Return value:
2N/A * number of valid gids in gid_array (may be zero)
2N/A * or
2N/A * -1 (and errno set appropriately) on errors (none currently defined)
2N/A *
2N/A * NSS2 Consistency enhancements:
2N/A * The "files normal" format between an application and nscd for the
2N/A * NSS_DBOP_GROUP_BYMEMBER nss_search operation is defined to be a
2N/A * processed array of numgids [up to maxgids] gid_t values. gid_t
2N/A * values in the array are unique.
2N/A */
2N/A
2N/Aextern nss_status_t process_cstr(const char *, int, struct nss_groupsbymem *);
2N/A
2N/Aint
2N/A_getgroupsbymember(const char *username, gid_t gid_array[],
2N/A int maxgids, int numgids)
2N/A{
2N/A struct nss_groupsbymem arg;
2N/A void *defp;
2N/A
2N/A arg.username = username;
2N/A arg.gid_array = gid_array;
2N/A arg.maxgids = maxgids;
2N/A arg.numgids = numgids;
2N/A /*
2N/A * In backwards compatibility mode, use the old str2group &
2N/A * process_cstr interfaces. Ditto within nscd processing.
2N/A */
2N/A arg.str2ent = str2group;
2N/A arg.process_cstr = process_cstr;
2N/A
2N/A /*
2N/A * The old value being provided here was 0, ie do the quick
2N/A * way. Given that this was never actually used under NIS
2N/A * we need to change the default to be 1 (TRUE) as we now
2N/A * need the admin to decided to use netid, setting
2N/A * NETID_AUTHORITATIVE in /etc/default/nss to TRUE gets us
2N/A * a value of 0 for force_slow_way - don't you just love
2N/A * double negatives ;-)
2N/A *
2N/A * We need to do this to preserve the behaviour seen when the
2N/A * admin makes no changes.
2N/A */
2N/A arg.force_slow_way = 1;
2N/A
2N/A if ((defp = defopen_r(__NSW_DEFAULT_FILE)) != NULL) {
2N/A if (defread_r(USE_NETID_STR, defp) != NULL)
2N/A arg.force_slow_way = 0;
2N/A defclose_r(defp);
2N/A }
2N/A
2N/A (void) nss_search(&db_root, _nss_initf_group,
2N/A NSS_DBOP_GROUP_BYMEMBER, &arg);
2N/A
2N/A return (arg.numgids);
2N/A}
2N/A
2N/A
2N/Astatic char *
2N/Agettok(char **nextpp, char sep)
2N/A{
2N/A char *p = *nextpp;
2N/A char *q = p;
2N/A char c;
2N/A
2N/A if (p == 0)
2N/A return (0);
2N/A
2N/A while ((c = *q) != '\0' && c != sep)
2N/A q++;
2N/A
2N/A if (c == '\0')
2N/A *nextpp = 0;
2N/A else {
2N/A *q++ = '\0';
2N/A *nextpp = q;
2N/A }
2N/A return (p);
2N/A}
2N/A
2N/A/*
2N/A * Return values: 0 = success, 1 = parse error, 2 = erange ...
2N/A * The structure pointer passed in is a structure in the caller's space
2N/A * wherein the field pointers would be set to areas in the buffer if
2N/A * need be. instring and buffer should be separate areas.
2N/A */
2N/Aint
2N/Astr2group(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
2N/A{
2N/A struct group *group = (struct group *)ent;
2N/A char *p, *next;
2N/A int black_magic; /* "+" or "-" entry */
2N/A char **memlist, **limit;
2N/A ulong_t tmp;
2N/A
2N/A if (lenstr + 1 > buflen)
2N/A return (NSS_STR_PARSE_ERANGE);
2N/A
2N/A /*
2N/A * We copy the input string into the output buffer and
2N/A * operate on it in place.
2N/A */
2N/A if (instr != buffer) {
2N/A /* Overlapping buffer copies are OK */
2N/A (void) memmove(buffer, instr, lenstr);
2N/A buffer[lenstr] = '\0';
2N/A }
2N/A
2N/A /* quick exit do not entry fill if not needed */
2N/A if (ent == (void *)NULL)
2N/A return (NSS_STR_PARSE_SUCCESS);
2N/A
2N/A next = buffer;
2N/A
2N/A /*
2N/A * Parsers for passwd and group have always been pretty rigid;
2N/A * we wouldn't want to buck a Unix tradition
2N/A */
2N/A
2N/A group->gr_name = p = gettok(&next, ':');
2N/A if (*p == '\0') {
2N/A /* Empty group-name; not allowed */
2N/A return (NSS_STR_PARSE_PARSE);
2N/A }
2N/A
2N/A /* Always return at least an empty gr_mem list */
2N/A memlist = (char **)ROUND_UP(buffer + lenstr + 1, sizeof (char *));
2N/A limit = (char **)ROUND_DOWN(buffer + buflen, sizeof (char *));
2N/A *memlist = 0;
2N/A group->gr_mem = memlist;
2N/A
2N/A black_magic = (*p == '+' || *p == '-');
2N/A if (black_magic) {
2N/A /* Then the rest of the group entry is optional */
2N/A group->gr_passwd = 0;
2N/A group->gr_gid = 0;
2N/A }
2N/A
2N/A group->gr_passwd = p = gettok(&next, ':');
2N/A if (p == 0) {
2N/A if (black_magic)
2N/A return (NSS_STR_PARSE_SUCCESS);
2N/A else
2N/A return (NSS_STR_PARSE_PARSE);
2N/A }
2N/A
2N/A p = next; /* gid */
2N/A if (p == 0 || *p == '\0') {
2N/A if (black_magic)
2N/A return (NSS_STR_PARSE_SUCCESS);
2N/A else
2N/A return (NSS_STR_PARSE_PARSE);
2N/A }
2N/A if (!black_magic) {
2N/A errno = 0;
2N/A tmp = strtoul(p, &next, 10);
2N/A if (next == p || errno != 0) {
2N/A /* gid field should be nonempty */
2N/A /* also check errno from strtoul */
2N/A return (NSS_STR_PARSE_PARSE);
2N/A }
2N/A if (tmp >= UINT32_MAX)
2N/A group->gr_gid = GID_NOBODY;
2N/A else
2N/A group->gr_gid = (gid_t)tmp;
2N/A }
2N/A if (*next++ != ':') {
2N/A /* Parse error, even for a '+' entry (which should have */
2N/A /* an empty gid field, since it's always overridden) */
2N/A return (NSS_STR_PARSE_PARSE);
2N/A }
2N/A
2N/A /* === Could check and complain if there are any extra colons */
2N/A while (memlist < limit) {
2N/A p = gettok(&next, ',');
2N/A if (p == 0 || *p == '\0') {
2N/A *memlist = 0;
2N/A /* Successfully parsed and stored */
2N/A return (NSS_STR_PARSE_SUCCESS);
2N/A }
2N/A *memlist++ = p;
2N/A }
2N/A /* Out of space; error even for black_magic */
2N/A return (NSS_STR_PARSE_ERANGE);
2N/A}
2N/A
2N/Anss_status_t
2N/Aprocess_cstr(const char *instr, int instr_len, struct nss_groupsbymem *gbm)
2N/A{
2N/A /*
2N/A * It's possible to do a much less inefficient version of this by
2N/A * selectively duplicating code from str2group(). For now,
2N/A * however, we'll take the easy way out and implement this on
2N/A * top of str2group().
2N/A */
2N/A
2N/A const char *username = gbm->username;
2N/A nss_XbyY_buf_t *buf;
2N/A struct group *grp;
2N/A char **memp;
2N/A char *mem;
2N/A int parsestat;
2N/A
2N/A buf = _nss_XbyY_buf_alloc(sizeof (struct group), NSS_BUFLEN_GROUP);
2N/A if (buf == 0)
2N/A return (NSS_UNAVAIL);
2N/A
2N/A grp = (struct group *)buf->result;
2N/A
2N/A parsestat = (*gbm->str2ent)(instr, instr_len,
2N/A grp, buf->buffer, buf->buflen);
2N/A
2N/A if (parsestat != NSS_STR_PARSE_SUCCESS) {
2N/A _nss_XbyY_buf_free(buf);
2N/A return (NSS_NOTFOUND); /* === ? */
2N/A }
2N/A
2N/A if (grp->gr_mem) {
2N/A for (memp = grp->gr_mem; (memp) && ((mem = *memp) != 0);
2N/A memp++) {
2N/A if (strcmp(mem, username) == 0) {
2N/A gid_t gid = grp->gr_gid;
2N/A gid_t *gidp = gbm->gid_array;
2N/A int numgids = gbm->numgids;
2N/A int i;
2N/A
2N/A _nss_XbyY_buf_free(buf);
2N/A
2N/A for (i = 0; i < numgids && *gidp != gid; i++,
2N/A gidp++) {
2N/A ;
2N/A }
2N/A if (i >= numgids) {
2N/A if (i >= gbm->maxgids) {
2N/A /* Filled the array; stop searching */
2N/A return (NSS_SUCCESS);
2N/A }
2N/A *gidp = gid;
2N/A gbm->numgids = numgids + 1;
2N/A }
2N/A return (NSS_NOTFOUND); /* Explained in */
2N/A /* <nss_dbdefs.h> */
2N/A }
2N/A }
2N/A }
2N/A _nss_XbyY_buf_free(buf);
2N/A return (NSS_NOTFOUND);
2N/A}