Reference count cached passwd and group structs. The cache holds

one reference itself and another is added by sudo_getgr{gid,nam}
and sudo_getpw{uid,nam}.  The final ref on the runas and user passwd
and group structs are persistent for now.
This commit is contained in:
Todd C. Miller
2010-08-04 09:58:50 -04:00
parent 7b011cf152
commit 0186018d3d
6 changed files with 201 additions and 108 deletions

View File

@@ -66,7 +66,10 @@ 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;
@@ -188,10 +191,40 @@ make_pwitem(const struct passwd *pw, const char *name)
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;
struct passwd *pw = item->d.pw;
if (--item->refcnt == 0) {
if (pw != NULL && pw->pw_passwd != NULL) {
zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
if ((char *)pw->pw_passwd < (char *)pw ||
(char *)pw->pw_passwd > (char *)pw->pw_gecos)
efree(pw->pw_passwd); /* free if separate allocation */
}
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.
@@ -225,6 +258,7 @@ sudo_getpwuid(uid_t uid)
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)
@@ -234,6 +268,7 @@ sudo_getpwuid(uid_t uid)
aix_restoreauthdb();
#endif
done:
item->refcnt++;
return(item->d.pw);
}
@@ -271,6 +306,7 @@ sudo_getpwnam(const char *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;
@@ -281,6 +317,7 @@ sudo_getpwnam(const char *name)
aix_restoreauthdb();
#endif
done:
item->refcnt++;
return(item->d.pw);
}
@@ -318,71 +355,28 @@ sudo_fakepwnam(const char *user, gid_t gid)
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;
item->d.pw = pw;
if ((node = rbinsert(pwcache_byuid, item)) != NULL) {
efree(node->data);
pw_delref_item(node->data);
node->data = item;
}
} else {
/* Store by name, overwriting cached version. */
item->k.name = pw->pw_name;
item->d.pw = pw;
if ((node = rbinsert(pwcache_byname, item)) != NULL) {
efree(node->data);
pw_delref_item(node->data);
node->data = item;
}
}
}
item->refcnt++;
return(pw);
}
/*
* 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);
if (i == 0) {
/* Store by gid, overwriting cached version. */
item->k.gid = gr->gr_gid;
item->d.gr = gr;
if ((node = rbinsert(grcache_bygid, item)) != NULL) {
efree(node->data);
node->data = item;
}
} else {
/* Store by name, overwriting cached version. */
item->k.name = gr->gr_name;
item->d.gr = gr;
if ((node = rbinsert(grcache_byname, item)) != NULL) {
efree(node->data);
node->data = item;
}
}
}
return(gr);
}
void
sudo_setpwent(void)
{
@@ -394,46 +388,25 @@ sudo_setpwent(void)
pwcache_byname = rbcreate(cmp_pwnam);
}
#ifdef PURIFY
static void pw_free(void *);
void
sudo_freepwcache(void)
{
if (pwcache_byuid != NULL) {
rbdestroy(pwcache_byuid, pw_free);
rbdestroy(pwcache_byuid, pw_delref_item);
pwcache_byuid = NULL;
}
if (pwcache_byname != NULL) {
rbdestroy(pwcache_byname, pw_free);
rbdestroy(pwcache_byname, pw_delref_item);
pwcache_byname = NULL;
}
}
static void
pw_free(void *v)
{
struct cache_item *item = (struct cache_item *) v;
struct passwd *pw = item->d.pw;
if (pw != NULL && pw->pw_passwd != NULL) {
zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
if ((char *)pw->pw_passwd < (char *)pw ||
(char *)pw->pw_passwd > (char *)pw->pw_gecos)
efree(pw->pw_passwd); /* free if separate allocation */
}
efree(item);
}
#endif /* PURIFY */
void
sudo_endpwent(void)
{
endpwent();
sudo_endspent();
#ifdef PURIFY
sudo_freepwcache(); /* XXX - use after free */
#endif
sudo_freepwcache();
}
/*
@@ -508,10 +481,32 @@ make_gritem(const struct group *gr, const char *name)
item->k.gid = gr->gr_gid;
}
item->d.gr = newgr;
item->refcnt = 1;
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.
*/
@@ -536,12 +531,14 @@ sudo_getgrgid(gid_t gid)
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 %lu, already exists, gid");
}
done:
item->refcnt++;
return(item->d.gr);
}
@@ -570,6 +567,7 @@ sudo_getgrnam(const char *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;
@@ -577,9 +575,55 @@ sudo_getgrnam(const char *name)
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
sudo_setgrent(void)
{
@@ -590,28 +634,24 @@ sudo_setgrent(void)
grcache_byname = rbcreate(cmp_grnam);
}
#ifdef PURIFY
void
sudo_freegrcache(void)
{
if (grcache_bygid != NULL) {
rbdestroy(grcache_bygid, free);
rbdestroy(grcache_bygid, gr_delref_item);
grcache_bygid = NULL;
}
if (grcache_byname != NULL) {
rbdestroy(grcache_byname, NULL);
rbdestroy(grcache_byname, gr_delref_item);
grcache_byname = NULL;
}
}
#endif /* PURIFY */
void
sudo_endgrent(void)
{
endgrent();
#ifdef PURIFY
sudo_freegrcache(); /* XXX - use after free */
#endif
sudo_freegrcache();
}
int
@@ -625,6 +665,7 @@ user_in_group(struct passwd *pw, const char *group)
int i;
#endif
struct group *grp;
int retval = FALSE;
#ifdef HAVE_SETAUTHDB
aix_setauthdb(pw->pw_name);
@@ -634,23 +675,29 @@ user_in_group(struct passwd *pw, const char *group)
aix_restoreauthdb();
#endif
if (grp == NULL)
return(FALSE);
goto done;
/* check against user's primary (passwd file) gid */
if (grp->gr_gid == pw->pw_gid)
return(TRUE);
if (grp->gr_gid == pw->pw_gid) {
retval = TRUE;
goto done;
}
#ifdef HAVE_MBR_CHECK_MEMBERSHIP
/* If we are matching the invoking user use the stashed uuid. */
if (strcmp(pw->pw_name, user_name) == 0) {
if (mbr_gid_to_uuid(grp->gr_gid, gu) == 0 &&
mbr_check_membership(user_uuid, gu, &ismember) == 0 && ismember)
return(TRUE);
mbr_check_membership(user_uuid, gu, &ismember) == 0 && ismember) {
retval = TRUE;
goto done;
}
} else {
if (mbr_uid_to_uuid(pw->pw_uid, uu) == 0 &&
mbr_gid_to_uuid(grp->gr_gid, gu) == 0 &&
mbr_check_membership(uu, gu, &ismember) == 0 && ismember)
return(TRUE);
mbr_check_membership(uu, gu, &ismember) == 0 && ismember) {
retval = TRUE;
goto done;
}
}
#else /* HAVE_MBR_CHECK_MEMBERSHIP */
# ifdef HAVE_GETGROUPS
@@ -661,20 +708,26 @@ user_in_group(struct passwd *pw, const char *group)
if (user_ngroups >= 0 &&
strcmp(pw->pw_name, list_pw ? list_pw->pw_name : user_name) == 0) {
for (i = 0; i < user_ngroups; i++) {
if (grp->gr_gid == user_groups[i])
return(TRUE);
if (grp->gr_gid == user_groups[i]) {
retval = TRUE;
goto done;
}
}
} else
# endif /* HAVE_GETGROUPS */
{
if (grp != NULL && grp->gr_mem != NULL) {
for (gr_mem = grp->gr_mem; *gr_mem; gr_mem++) {
if (strcmp(*gr_mem, pw->pw_name) == 0)
return(TRUE);
if (strcmp(*gr_mem, pw->pw_name) == 0) {
retval = TRUE;
goto done;
}
}
}
}
#endif /* HAVE_MBR_CHECK_MEMBERSHIP */
return(FALSE);
done:
gr_delref(grp);
return(retval);
}