From Anon Ymous:

- Fix a memory leak in newgrp().  (Noted by ginsbach@.)

- If the new and old groups are the same, addgrp() does nothing so
  exit early.  (Noted by ginsbach@.)

- In newgrp(), if the new group isn't found in the in-core, check the
  group membership list anyway as the in-core list may be full or
  /etc/groups may have been edited since login.  (Requested by
  ginsbach@.)

- Deal with a full in-core group[] array in addgid() rather than
  silently ignoring it and ending up with a group[] array that is
  inconsistent with the current group.

- More explicit error handling in addgroup().
This commit is contained in:
christos 2007-10-27 15:33:25 +00:00
parent 4556f89a2d
commit 40a10c757f

View File

@ -1,4 +1,4 @@
/* $NetBSD: grutil.c,v 1.2 2007/10/18 13:04:06 christos Exp $ */
/* $NetBSD: grutil.c,v 1.3 2007/10/27 15:33:25 christos Exp $ */
/*-
* Copyright (c) 2007 The NetBSD Foundation, Inc.
@ -36,7 +36,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: grutil.c,v 1.2 2007/10/18 13:04:06 christos Exp $");
__RCSID("$NetBSD: grutil.c,v 1.3 2007/10/27 15:33:25 christos Exp $");
#include <sys/param.h>
#include <err.h>
@ -92,7 +92,7 @@ alloc_groups(int *ngroups, gid_t **groups, int *ngroupsmax)
}
static addgrp_ret_t
addgid(gid_t *groups, int ngroups, int ngroupsmax, gid_t gid)
addgid(gid_t *groups, int ngroups, int ngroupsmax, gid_t gid, int makespace)
{
int i;
@ -101,8 +101,40 @@ addgid(gid_t *groups, int ngroups, int ngroupsmax, gid_t gid)
continue;
/* add the gid to the supplemental group list */
if (i == ngroups && ngroups < ngroupsmax) {
groups[ngroups++] = gid;
if (i == ngroups) {
if (ngroups < ngroupsmax)
groups[ngroups++] = gid;
else { /*
* setgroups(2) will fail with errno = EINVAL
* if ngroups > nmaxgroups. If makespace is
* set, replace the last group with the new
* one. Otherwise, fail the way setgroups(2)
* would if we passed the larger groups array.
*/
if (makespace) {
/*
* Find a slot that doesn't contain
* the primary group.
*/
struct passwd *pwd;
gid_t pgid;
pwd = getpwuid(getuid());
if (pwd == NULL)
goto error;
pgid = pwd->pw_gid;
for (i = ngroupsmax - 1; i >= 0; i--)
if (groups[i] != pgid)
break;
if (i < 0)
goto error;
groups[i] = gid;
}
else {
error:
errno = EINVAL;
return ADDGRP_ESETGROUPS;
}
}
if (setgroups(ngroups, groups) < 0)
return ADDGRP_ESETGROUPS;
}
@ -110,10 +142,15 @@ addgid(gid_t *groups, int ngroups, int ngroupsmax, gid_t gid)
}
static addgrp_ret_t
addgrp(gid_t newgid)
addgrp(gid_t newgid, int makespace)
{
int ngroups, ngroupsmax, rval;
gid_t *groups;
gid_t oldgid;
oldgid = getgid();
if (oldgid == newgid) /* nothing to do */
return ADDGRP_NOERROR;
rval = alloc_groups(&ngroups, &groups, &ngroupsmax);
if (rval != 0)
@ -135,7 +172,7 @@ addgrp(gid_t newgid)
* add the new egid to the list if there is room.
*/
rval = addgid(groups, ngroups, ngroupsmax, newgid);
rval = addgid(groups, ngroups, ngroupsmax, newgid, makespace);
#else
/*
* According to POSIX/XPG6:
@ -147,10 +184,8 @@ addgrp(gid_t newgid)
* o If the old egid is not in the supplemental group list,
* add the old egid to the list if there is room.
*/
{
int i;
gid_t oldgid;
/* search for new egid in supplemental group list */
for (i = 0; i < ngroups && groups[i] != newgid; i++)
@ -161,9 +196,7 @@ addgrp(gid_t newgid)
for (--ngroups; i < ngroups; i++)
groups[i] = groups[i + 1];
oldgid = getgid();
if (oldgid != newgid)
rval = addgid(groups, ngroups, ngroupsmax, oldgid);
rval = addgid(groups, ngroups, ngroupsmax, oldgid, makespace);
}
#endif
free_groups(groups);
@ -171,7 +204,8 @@ addgrp(gid_t newgid)
}
/*
* If newgrp fails, it returns -1, and the errno variable is set to:
* If newgrp fails, it returns (gid_t)-1 and the errno variable is
* set to:
* [EINVAL] Unknown group.
* [EPERM] Bad password.
*/
@ -179,9 +213,10 @@ static gid_t
newgrp(const char *gname, struct passwd *pwd, uid_t ruid, const char *prompt)
{
struct group *grp;
char **ap;
char *p;
gid_t *groups;
int ngroups, ngroupsmax;
char *p;
if (gname == NULL)
return pwd->pw_gid;
@ -209,15 +244,21 @@ newgrp(const char *gname, struct passwd *pwd, uid_t ruid, const char *prompt)
if (alloc_groups(&ngroups, &groups, &ngroupsmax) == 0) {
int i;
for (i = 0; i < ngroups; i++)
if (groups[i] == grp->gr_gid)
return grp->gr_gid;
} else {
char **ap;
for (ap = grp->gr_mem; *ap != NULL; ap++)
if (strcmp(*ap, pwd->pw_name) == 0)
if (groups[i] == grp->gr_gid) {
free_groups(groups);
return grp->gr_gid;
}
free_groups(groups);
}
/*
* Check the group membership list in case the groups[] array
* was maxed out or the user has been added to it since login.
*/
for (ap = grp->gr_mem; *ap != NULL; ap++)
if (strcmp(*ap, pwd->pw_name) == 0)
return grp->gr_gid;
if (*grp->gr_passwd != '\0') {
p = getpass(prompt);
if (strcmp(grp->gr_passwd, crypt(p, grp->gr_passwd)) == 0) {
@ -231,6 +272,12 @@ newgrp(const char *gname, struct passwd *pwd, uid_t ruid, const char *prompt)
return (gid_t)-1;
}
#ifdef GRUTIL_SETGROUPS_MAKESPACE
# define ADDGRP_MAKESPACE 1
#else
# define ADDGRP_MAKESPACE 0
#endif
#ifdef GRUTIL_ALLOW_GROUP_ERRORS
# define maybe_exit(e)
#else
@ -262,7 +309,7 @@ addgroup(
pwd->pw_gid = getgid();
}
switch (addgrp(pwd->pw_gid)) {
switch (addgrp(pwd->pw_gid, ADDGRP_MAKESPACE)) {
case ADDGRP_NOERROR:
break;
case ADDGRP_EMALLOC:
@ -272,8 +319,18 @@ addgroup(
err(EXIT_FAILURE, "getgroups");
break;
case ADDGRP_ESETGROUPS:
warn("setgroups");
maybe_exit(EXIT_FAILURE);
switch(errno) {
case EINVAL:
warnx("setgroups: ngroups > ngroupsmax");
maybe_exit(EXIT_FAILURE);
break;
case EPERM:
case EFAULT:
default:
warn("setgroups");
maybe_exit(EXIT_FAILURE);
break;
}
break;
}