Files
sudo/plugins/sudoers/pwutil.c
Todd C. Miller a6602fa0e4 Instead of trying to grow the buffer in make_grlist_item(), simply
increase the total length, free the old buffer and allocate a new
one.  This is less error prone and saves us from having to adjust
all the pointers in the buffer.  This code path is only taken when
there are groups longer than the length of the user field in struct
utmp or utmpx, which should be quite rare.
2011-10-10 11:10:59 -04:00

901 lines
22 KiB
C

/*
* Copyright (c) 1996, 1998-2005, 2007-2011
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#ifdef HAVE_SETAUTHDB
# include <usersec.h>
#endif /* HAVE_SETAUTHDB */
#ifdef HAVE_UTMPX_H
# include <utmpx.h>
#else
# include <utmp.h>
#endif /* HAVE_UTMPX_H */
#include <limits.h>
#include <pwd.h>
#include <grp.h>
#include "sudoers.h"
#include "redblack.h"
/*
* The passwd and group caches.
*/
static struct rbtree *pwcache_byuid, *pwcache_byname;
static struct rbtree *grcache_bygid, *grcache_byname;
static struct rbtree *grlist_cache;
static int cmp_pwuid(const void *, const void *);
static int cmp_pwnam(const void *, const void *);
static int cmp_grgid(const void *, const void *);
#define cmp_grnam cmp_pwnam
#define ptr_to_item(p) ((struct cache_item *)((char *)(p) - sizeof(struct cache_item)))
struct cache_item {
unsigned int refcnt;
/* key */
union {
uid_t uid;
gid_t gid;
char *name;
} k;
/* datum */
union {
struct passwd *pw;
struct group *gr;
struct group_list *grlist;
} d;
};
/*
* Compare by uid.
*/
static int
cmp_pwuid(const void *v1, const void *v2)
{
const struct cache_item *ci1 = (const struct cache_item *) v1;
const struct cache_item *ci2 = (const struct cache_item *) v2;
return ci1->k.uid - ci2->k.uid;
}
/*
* Compare by user name.
*/
static int
cmp_pwnam(const void *v1, const void *v2)
{
const struct cache_item *ci1 = (const struct cache_item *) v1;
const struct cache_item *ci2 = (const struct cache_item *) v2;
return strcmp(ci1->k.name, ci2->k.name);
}
#define FIELD_SIZE(src, name, size) \
do { \
if (src->name) { \
size = strlen(src->name) + 1; \
total += size; \
} \
} while (0)
#define FIELD_COPY(src, dst, name, size) \
do { \
if (src->name) { \
memcpy(cp, src->name, size); \
dst->name = cp; \
cp += size; \
} \
} while (0)
/*
* Dynamically allocate space for a struct item plus the key and data
* elements. If name is non-NULL it is used as the key, else the
* uid is the key. Fills in datum from struct password.
*
* We would like to fill in the encrypted password too but the
* call to the shadow function could overwrite the pw buffer (NIS).
*/
static struct cache_item *
make_pwitem(const struct passwd *pw, const char *name)
{
char *cp;
const char *pw_shell;
size_t nsize, psize, csize, gsize, dsize, ssize, total;
struct cache_item *item;
struct passwd *newpw;
/* If shell field is empty, expand to _PATH_BSHELL. */
pw_shell = (pw->pw_shell == NULL || pw->pw_shell[0] == '\0')
? _PATH_BSHELL : pw->pw_shell;
/* Allocate in one big chunk for easy freeing. */
nsize = psize = csize = gsize = dsize = ssize = 0;
total = sizeof(struct cache_item) + sizeof(struct passwd);
FIELD_SIZE(pw, pw_name, nsize);
FIELD_SIZE(pw, pw_passwd, psize);
#ifdef HAVE_LOGIN_CAP_H
FIELD_SIZE(pw, pw_class, csize);
#endif
FIELD_SIZE(pw, pw_gecos, gsize);
FIELD_SIZE(pw, pw_dir, dsize);
/* Treat shell specially since we expand "" -> _PATH_BSHELL */
ssize = strlen(pw_shell) + 1;
total += ssize;
if (name != NULL)
total += strlen(name) + 1;
/* Allocate space for struct item, struct passwd and the strings. */
item = emalloc(total);
cp = (char *) item + sizeof(struct cache_item);
/*
* Copy in passwd contents and make strings relative to space
* at the end of the buffer.
*/
newpw = (struct passwd *) cp;
memcpy(newpw, pw, sizeof(struct passwd));
cp += sizeof(struct passwd);
FIELD_COPY(pw, newpw, pw_name, nsize);
FIELD_COPY(pw, newpw, pw_passwd, psize);
#ifdef HAVE_LOGIN_CAP_H
FIELD_COPY(pw, newpw, pw_class, csize);
#endif
FIELD_COPY(pw, newpw, pw_gecos, gsize);
FIELD_COPY(pw, newpw, pw_dir, dsize);
/* Treat shell specially since we expand "" -> _PATH_BSHELL */
memcpy(cp, pw_shell, ssize);
newpw->pw_shell = cp;
cp += ssize;
/* Set key and datum. */
if (name != NULL) {
memcpy(cp, name, strlen(name) + 1);
item->k.name = cp;
} else {
item->k.uid = pw->pw_uid;
}
item->d.pw = newpw;
item->refcnt = 1;
return item;
}
void
pw_addref(struct passwd *pw)
{
ptr_to_item(pw)->refcnt++;
}
static void
pw_delref_item(void *v)
{
struct cache_item *item = v;
if (--item->refcnt == 0)
efree(item);
}
void
pw_delref(struct passwd *pw)
{
pw_delref_item(ptr_to_item(pw));
}
/*
* Get a password entry by uid and allocate space for it.
* Fills in pw_passwd from shadow file if necessary.
*/
struct passwd *
sudo_getpwuid(uid_t uid)
{
struct cache_item key, *item;
struct rbnode *node;
key.k.uid = uid;
if ((node = rbfind(pwcache_byuid, &key)) != NULL) {
item = (struct cache_item *) node->data;
goto done;
}
/*
* Cache passwd db entry if it exists or a negative response if not.
*/
#ifdef HAVE_SETAUTHDB
aix_setauthdb(IDtouser(uid));
#endif
if ((key.d.pw = getpwuid(uid)) != NULL) {
item = make_pwitem(key.d.pw, NULL);
if (rbinsert(pwcache_byuid, item) != NULL)
errorx(1, _("unable to cache uid %u (%s), already exists"),
(unsigned int) uid, item->d.pw->pw_name);
} else {
item = emalloc(sizeof(*item));
item->refcnt = 1;
item->k.uid = uid;
item->d.pw = NULL;
if (rbinsert(pwcache_byuid, item) != NULL)
errorx(1, _("unable to cache uid %u, already exists"),
(unsigned int) uid);
}
#ifdef HAVE_SETAUTHDB
aix_restoreauthdb();
#endif
done:
item->refcnt++;
return item->d.pw;
}
/*
* Get a password entry by name and allocate space for it.
* Fills in pw_passwd from shadow file if necessary.
*/
struct passwd *
sudo_getpwnam(const char *name)
{
struct cache_item key, *item;
struct rbnode *node;
size_t len;
key.k.name = (char *) name;
if ((node = rbfind(pwcache_byname, &key)) != NULL) {
item = (struct cache_item *) node->data;
goto done;
}
/*
* Cache passwd db entry if it exists or a negative response if not.
*/
#ifdef HAVE_SETAUTHDB
aix_setauthdb((char *) name);
#endif
if ((key.d.pw = getpwnam(name)) != NULL) {
item = make_pwitem(key.d.pw, name);
if (rbinsert(pwcache_byname, item) != NULL)
errorx(1, _("unable to cache user %s, already exists"), name);
} else {
len = strlen(name) + 1;
item = emalloc(sizeof(*item) + len);
item->refcnt = 1;
item->k.name = (char *) item + sizeof(*item);
memcpy(item->k.name, name, len);
item->d.pw = NULL;
if (rbinsert(pwcache_byname, item) != NULL)
errorx(1, _("unable to cache user %s, already exists"), name);
}
#ifdef HAVE_SETAUTHDB
aix_restoreauthdb();
#endif
done:
item->refcnt++;
return item->d.pw;
}
/*
* Take a user, uid and gid and return a faked up passwd struct.
*/
struct passwd *
sudo_fakepwnamid(const char *user, uid_t uid, gid_t gid)
{
struct cache_item *item;
struct passwd *pw;
struct rbnode *node;
size_t len, namelen;
int i;
namelen = strlen(user);
len = sizeof(*item) + sizeof(*pw) + namelen + 1 /* pw_name */ +
sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL);
for (i = 0; i < 2; i++) {
item = emalloc(len);
zero_bytes(item, sizeof(*item) + sizeof(*pw));
pw = (struct passwd *) ((char *)item + sizeof(*item));
pw->pw_uid = uid;
pw->pw_gid = gid;
pw->pw_name = (char *)pw + sizeof(struct passwd);
memcpy(pw->pw_name, user, namelen + 1);
pw->pw_passwd = pw->pw_name + namelen + 1;
memcpy(pw->pw_passwd, "*", 2);
pw->pw_gecos = pw->pw_passwd + 2;
pw->pw_gecos[0] = '\0';
pw->pw_dir = pw->pw_gecos + 1;
memcpy(pw->pw_dir, "/", 2);
pw->pw_shell = pw->pw_dir + 2;
memcpy(pw->pw_shell, _PATH_BSHELL, sizeof(_PATH_BSHELL));
item->refcnt = 1;
item->d.pw = pw;
if (i == 0) {
/* Store by uid, overwriting cached version. */
item->k.uid = pw->pw_uid;
if ((node = rbinsert(pwcache_byuid, item)) != NULL) {
pw_delref_item(node->data);
node->data = item;
}
} else {
/* Store by name, overwriting cached version. */
item->k.name = pw->pw_name;
if ((node = rbinsert(pwcache_byname, item)) != NULL) {
pw_delref_item(node->data);
node->data = item;
}
}
}
item->refcnt++;
return pw;
}
/*
* Take a uid in string form "#123" and return a faked up passwd struct.
*/
struct passwd *
sudo_fakepwnam(const char *user, gid_t gid)
{
uid_t uid;
uid = (uid_t) atoi(user + 1);
return sudo_fakepwnamid(user, uid, gid);
}
void
sudo_setpwent(void)
{
setpwent();
if (pwcache_byuid == NULL)
pwcache_byuid = rbcreate(cmp_pwuid);
if (pwcache_byname == NULL)
pwcache_byname = rbcreate(cmp_pwnam);
}
void
sudo_freepwcache(void)
{
if (pwcache_byuid != NULL) {
rbdestroy(pwcache_byuid, pw_delref_item);
pwcache_byuid = NULL;
}
if (pwcache_byname != NULL) {
rbdestroy(pwcache_byname, pw_delref_item);
pwcache_byname = NULL;
}
}
void
sudo_endpwent(void)
{
endpwent();
sudo_freepwcache();
}
/*
* Compare by gid.
*/
static int
cmp_grgid(const void *v1, const void *v2)
{
const struct cache_item *ci1 = (const struct cache_item *) v1;
const struct cache_item *ci2 = (const struct cache_item *) v2;
return ci1->k.gid - ci2->k.gid;
}
/*
* Dynamically allocate space for a struct item plus the key and data
* elements. If name is non-NULL it is used as the key, else the
* gid is the key. Fills in datum from struct group.
*/
static struct cache_item *
make_gritem(const struct group *gr, const char *name)
{
char *cp;
size_t nsize, psize, nmem, total, len;
struct cache_item *item;
struct group *newgr;
/* Allocate in one big chunk for easy freeing. */
nsize = psize = nmem = 0;
total = sizeof(struct cache_item) + sizeof(struct group);
FIELD_SIZE(gr, gr_name, nsize);
FIELD_SIZE(gr, gr_passwd, psize);
if (gr->gr_mem) {
for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++)
total += strlen(gr->gr_mem[nmem]) + 1;
nmem++;
total += sizeof(char *) * nmem;
}
if (name != NULL)
total += strlen(name) + 1;
item = emalloc(total);
cp = (char *) item + sizeof(struct cache_item);
/*
* Copy in group contents and make strings relative to space
* at the end of the buffer. Note that gr_mem must come
* immediately after struct group to guarantee proper alignment.
*/
newgr = (struct group *)cp;
memcpy(newgr, gr, sizeof(struct group));
cp += sizeof(struct group);
if (gr->gr_mem) {
newgr->gr_mem = (char **)cp;
cp += sizeof(char *) * nmem;
for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) {
len = strlen(gr->gr_mem[nmem]) + 1;
memcpy(cp, gr->gr_mem[nmem], len);
newgr->gr_mem[nmem] = cp;
cp += len;
}
newgr->gr_mem[nmem] = NULL;
}
FIELD_COPY(gr, newgr, gr_passwd, psize);
FIELD_COPY(gr, newgr, gr_name, nsize);
/* Set key and datum. */
if (name != NULL) {
memcpy(cp, name, strlen(name) + 1);
item->k.name = cp;
} else {
item->k.gid = gr->gr_gid;
}
item->d.gr = newgr;
item->refcnt = 1;
return item;
}
#ifdef HAVE_UTMPX_H
# define GROUPNAME_LEN (sizeof((struct utmpx *)0)->ut_user + 1)
#else
# ifdef HAVE_STRUCT_UTMP_UT_USER
# define GROUPNAME_LEN (sizeof((struct utmp *)0)->ut_user + 1)
# else
# define GROUPNAME_LEN (sizeof((struct utmp *)0)->ut_name + 1)
# endif
#endif /* HAVE_UTMPX_H */
/*
* Dynamically allocate space for a struct item plus the key and data
* elements. Fills in datum from the groups and gids arrays.
*/
static struct cache_item *
make_grlist_item(const char *user, GETGROUPS_T *gids, int ngids)
{
char *cp;
size_t i, nsize, ngroups, total, len;
struct cache_item *item;
struct group_list *grlist;
struct group *grp;
#ifdef HAVE_SETAUTHDB
aix_setauthdb((char *) user);
#endif
/* Allocate in one big chunk for easy freeing. */
nsize = strlen(user) + 1;
total = sizeof(struct cache_item) + sizeof(struct group_list) + nsize;
total += sizeof(char *) * ngids;
total += sizeof(gid_t *) * ngids;
total += GROUPNAME_LEN * ngids;
again:
item = emalloc(total);
cp = (char *) item + sizeof(struct cache_item);
/*
* Copy in group list and make pointers relative to space
* at the end of the buffer. Note that the groups array must come
* immediately after struct group to guarantee proper alignment.
*/
grlist = (struct group_list *)cp;
zero_bytes(grlist, sizeof(struct group_list));
cp += sizeof(struct group_list);
grlist->groups = (char **)cp;
cp += sizeof(char *) * ngids;
grlist->gids = (gid_t *)cp;
cp += sizeof(gid_t) * ngids;
/* Set key and datum. */
memcpy(cp, user, nsize);
item->k.name = cp;
item->d.grlist = grlist;
item->refcnt = 1;
cp += nsize;
/*
* Store group IDs.
*/
for (i = 0; i < ngids; i++)
grlist->gids[i] = gids[i];
grlist->ngids = ngids;
/*
* Resolve and store group names by ID.
*/
ngroups = 0;
for (i = 0; i < ngids; i++) {
if ((grp = sudo_getgrgid(gids[i])) != NULL) {
len = strlen(grp->gr_name) + 1;
if (cp - (char *)item + len > total) {
total += len + GROUPNAME_LEN;
efree(item);
gr_delref(grp);
goto again;
}
memcpy(cp, grp->gr_name, len);
grlist->groups[ngroups++] = cp;
cp += len;
gr_delref(grp);
}
}
grlist->ngroups = ngroups;
#ifdef HAVE_SETAUTHDB
aix_restoreauthdb();
#endif
return item;
}
void
gr_addref(struct group *gr)
{
ptr_to_item(gr)->refcnt++;
}
static void
gr_delref_item(void *v)
{
struct cache_item *item = v;
if (--item->refcnt == 0)
efree(item);
}
void
gr_delref(struct group *gr)
{
gr_delref_item(ptr_to_item(gr));
}
/*
* Get a group entry by gid and allocate space for it.
*/
struct group *
sudo_getgrgid(gid_t gid)
{
struct cache_item key, *item;
struct rbnode *node;
key.k.gid = gid;
if ((node = rbfind(grcache_bygid, &key)) != NULL) {
item = (struct cache_item *) node->data;
goto done;
}
/*
* Cache group db entry if it exists or a negative response if not.
*/
if ((key.d.gr = getgrgid(gid)) != NULL) {
item = make_gritem(key.d.gr, NULL);
if (rbinsert(grcache_bygid, item) != NULL)
errorx(1, _("unable to cache gid %u (%s), already exists"),
(unsigned int) gid, key.d.gr->gr_name);
} else {
item = emalloc(sizeof(*item));
item->refcnt = 1;
item->k.gid = gid;
item->d.gr = NULL;
if (rbinsert(grcache_bygid, item) != NULL)
errorx(1, _("unable to cache gid %u, already exists"),
(unsigned int) gid);
}
done:
item->refcnt++;
return item->d.gr;
}
/*
* Get a group entry by name and allocate space for it.
*/
struct group *
sudo_getgrnam(const char *name)
{
struct cache_item key, *item;
struct rbnode *node;
size_t len;
key.k.name = (char *) name;
if ((node = rbfind(grcache_byname, &key)) != NULL) {
item = (struct cache_item *) node->data;
goto done;
}
/*
* Cache group db entry if it exists or a negative response if not.
*/
if ((key.d.gr = getgrnam(name)) != NULL) {
item = make_gritem(key.d.gr, name);
if (rbinsert(grcache_byname, item) != NULL)
errorx(1, _("unable to cache group %s, already exists"), name);
} else {
len = strlen(name) + 1;
item = emalloc(sizeof(*item) + len);
item->refcnt = 1;
item->k.name = (char *) item + sizeof(*item);
memcpy(item->k.name, name, len);
item->d.gr = NULL;
if (rbinsert(grcache_byname, item) != NULL)
errorx(1, _("unable to cache group %s, already exists"), name);
}
done:
item->refcnt++;
return item->d.gr;
}
/*
* Take a gid in string form "#123" and return a faked up group struct.
*/
struct group *
sudo_fakegrnam(const char *group)
{
struct cache_item *item;
struct group *gr;
struct rbnode *node;
size_t len, namelen;
int i;
namelen = strlen(group);
len = sizeof(*item) + sizeof(*gr) + namelen + 1;
for (i = 0; i < 2; i++) {
item = emalloc(len);
zero_bytes(item, sizeof(*item) + sizeof(*gr));
gr = (struct group *) ((char *)item + sizeof(*item));
gr->gr_gid = (gid_t) atoi(group + 1);
gr->gr_name = (char *)gr + sizeof(struct group);
memcpy(gr->gr_name, group, namelen + 1);
item->refcnt = 1;
item->d.gr = gr;
if (i == 0) {
/* Store by gid, overwriting cached version. */
item->k.gid = gr->gr_gid;
if ((node = rbinsert(grcache_bygid, item)) != NULL) {
gr_delref_item(node->data);
node->data = item;
}
} else {
/* Store by name, overwriting cached version. */
item->k.name = gr->gr_name;
if ((node = rbinsert(grcache_byname, item)) != NULL) {
gr_delref_item(node->data);
node->data = item;
}
}
}
item->refcnt++;
return gr;
}
void
grlist_addref(struct group_list *grlist)
{
ptr_to_item(grlist)->refcnt++;
}
static void
grlist_delref_item(void *v)
{
struct cache_item *item = v;
if (--item->refcnt == 0)
efree(item);
}
void
grlist_delref(struct group_list *grlist)
{
grlist_delref_item(ptr_to_item(grlist));
}
void
sudo_setgrent(void)
{
setgrent();
if (grcache_bygid == NULL)
grcache_bygid = rbcreate(cmp_grgid);
if (grcache_byname == NULL)
grcache_byname = rbcreate(cmp_grnam);
if (grlist_cache == NULL)
grlist_cache = rbcreate(cmp_grnam);
}
void
sudo_freegrcache(void)
{
if (grcache_bygid != NULL) {
rbdestroy(grcache_bygid, gr_delref_item);
grcache_bygid = NULL;
}
if (grcache_byname != NULL) {
rbdestroy(grcache_byname, gr_delref_item);
grcache_byname = NULL;
}
if (grlist_cache != NULL) {
rbdestroy(grlist_cache, grlist_delref_item);
grlist_cache = NULL;
}
}
void
sudo_endgrent(void)
{
endgrent();
sudo_freegrcache();
}
struct group_list *
get_group_list(struct passwd *pw)
{
struct cache_item key, *item;
struct rbnode *node;
size_t len;
GETGROUPS_T *gids;
int ngids;
key.k.name = pw->pw_name;
if ((node = rbfind(grlist_cache, &key)) != NULL) {
item = (struct cache_item *) node->data;
goto done;
}
/*
* Cache group db entry if it exists or a negative response if not.
*/
#if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)
ngids = (int)sysconf(_SC_NGROUPS_MAX) * 2;
if (ngids < 0)
#endif
ngids = NGROUPS_MAX * 2;
gids = emalloc2(ngids, sizeof(GETGROUPS_T));
if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) {
efree(gids);
gids = emalloc2(ngids, sizeof(GETGROUPS_T));
if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) {
efree(gids);
return NULL;
}
}
if (ngids > 0) {
if ((item = make_grlist_item(pw->pw_name, gids, ngids)) == NULL)
errorx(1, "unable to parse group list for %s", pw->pw_name);
efree(gids);
if (rbinsert(grlist_cache, item) != NULL)
errorx(1, "unable to cache group list for %s, already exists",
pw->pw_name);
} else {
/* Should not happen. */
len = strlen(pw->pw_name) + 1;
item = emalloc(sizeof(*item) + len);
item->refcnt = 1;
item->k.name = (char *) item + sizeof(*item);
memcpy(item->k.name, pw->pw_name, len);
item->d.grlist = NULL;
if (rbinsert(grlist_cache, item) != NULL)
errorx(1, "unable to cache group list for %s, already exists",
pw->pw_name);
}
done:
item->refcnt++;
return item->d.grlist;
}
void
set_group_list(const char *user, GETGROUPS_T *gids, int ngids)
{
struct cache_item key, *item;
struct rbnode *node;
/*
* Cache group db entry if it doesn't already exist
*/
key.k.name = (char *) user;
if ((node = rbfind(grlist_cache, &key)) == NULL) {
if ((item = make_grlist_item(user, gids, ngids)) == NULL)
errorx(1, "unable to parse group list for %s", user);
if (rbinsert(grlist_cache, item) != NULL)
errorx(1, "unable to cache group list for %s, already exists",
user);
}
}
int
user_in_group(struct passwd *pw, const char *group)
{
struct group_list *grlist;
struct group *grp = NULL;
int i, matched = FALSE;
if ((grlist = get_group_list(pw)) != NULL) {
/*
* If it could be a sudo-style group ID check gids first.
*/
if (group[0] == '#') {
gid_t gid = atoi(group + 1);
if (gid == pw->pw_gid) {
matched = TRUE;
goto done;
}
for (i = 0; i < grlist->ngids; i++) {
if (gid == grlist->gids[i]) {
matched = TRUE;
goto done;
}
}
}
/*
* Next check the supplementary group vector.
* It usually includes the password db group too.
*/
for (i = 0; i < grlist->ngroups; i++) {
if (strcasecmp(group, grlist->groups[i]) == 0) {
matched = TRUE;
goto done;
}
}
/* Finally check against user's primary (passwd file) group. */
if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
if (strcasecmp(group, grp->gr_name) == 0) {
matched = TRUE;
goto done;
}
}
done:
if (grp != NULL)
gr_delref(grp);
grlist_delref(grlist);
}
return matched;
}