Implement group caching and use the passwd and group caches throughout.

This commit is contained in:
Todd C. Miller
2004-11-16 04:24:11 +00:00
parent fd46c2c3ef
commit 9846e562ad
11 changed files with 286 additions and 45 deletions

View File

@@ -283,7 +283,7 @@ user_is_exempt()
if (!def_exempt_group)
return(FALSE);
if (!(grp = getgrnam(def_exempt_group)))
if (!(grp = sudo_getgrnam(def_exempt_group)))
return(FALSE);
if (user_gid == grp->gr_gid)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 1998-2002 Todd C. Miller <Todd.Miller@courtesan.com>
* Copyright (c) 1996, 1998-2004 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
@@ -46,6 +46,7 @@
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <grp.h>
#ifdef HAVE_GETSPNAM
# include <shadow.h>
#endif /* HAVE_GETSPNAM */
@@ -80,18 +81,20 @@ static const char rcsid[] = "$Sudo$";
#if defined(HAVE_GETPRPWNAM) && defined(__alpha)
int crypt_type = INT_MAX;
#endif /* HAVE_GETPRPWNAM && __alpha */
static struct rbtree *cache_byuid;
static struct rbtree *cache_byname;
static struct rbtree *pwcache_byuid, *pwcache_byname;
static struct rbtree *grcache_bygid, *grcache_byname;
static int cmp_byuid __P((const VOID *, const VOID *));
static int cmp_byname __P((const VOID *, const VOID *));
static int cmp_pwuid __P((const VOID *, const VOID *));
static int cmp_pwnam __P((const VOID *, const VOID *));
static void pw_free __P((VOID *));
static int cmp_grgid __P((const VOID *, const VOID *));
static int cmp_grnam __P((const VOID *, const VOID *));
/*
* Compare by uid.
*/
static int
cmp_byuid(v1, v2)
cmp_pwuid(v1, v2)
const VOID *v1;
const VOID *v2;
{
@@ -104,7 +107,7 @@ cmp_byuid(v1, v2)
* Compare by user name.
*/
static int
cmp_byname(v1, v2)
cmp_pwnam(v1, v2)
const VOID *v1;
const VOID *v2;
{
@@ -197,7 +200,7 @@ sudo_getepw(pw)
* Dynamically allocate space for a struct password and the constituent parts
* that we care about. Fills in pw_passwd from shadow file.
*/
struct passwd *
static struct passwd *
sudo_pwdup(pw)
const struct passwd *pw;
{
@@ -300,14 +303,14 @@ sudo_getpwuid(uid)
struct rbnode *node;
key.pw_uid = uid;
if ((node = rbfind(cache_byuid, &key)) != NULL)
if ((node = rbfind(pwcache_byuid, &key)) != NULL)
return((struct passwd *) node->data);
if ((pw = getpwuid(uid)) == NULL)
return(NULL);
else
pw = sudo_pwdup(pw);
rbinsert(cache_byname, (VOID *) pw);
rbinsert(cache_byuid, (VOID *) pw);
rbinsert(pwcache_byname, (VOID *) pw);
rbinsert(pwcache_byuid, (VOID *) pw);
return(pw);
}
@@ -323,14 +326,14 @@ sudo_getpwnam(name)
struct rbnode *node;
key.pw_name = (char *) name;
if ((node = rbfind(cache_byname, &key)) != NULL)
if ((node = rbfind(pwcache_byname, &key)) != NULL)
return((struct passwd *) node->data);
if ((pw = getpwnam(name)) == NULL)
return(NULL);
else
pw = sudo_pwdup(pw);
rbinsert(cache_byname, (VOID *) pw);
rbinsert(cache_byuid, (VOID *) pw);
rbinsert(pwcache_byname, (VOID *) pw);
rbinsert(pwcache_byuid, (VOID *) pw);
return(pw);
}
@@ -349,8 +352,8 @@ sudo_fakepwuid(uid)
pw->pw_name = (char *)pw + sizeof(struct passwd);
(void) snprintf(pw->pw_name, MAX_UID_T_LEN + 1, "#%lu",
(unsigned long) uid);
rbinsert(cache_byname, (VOID *) pw);
rbinsert(cache_byuid, (VOID *) pw);
rbinsert(pwcache_byname, (VOID *) pw);
rbinsert(pwcache_byuid, (VOID *) pw);
return(pw);
}
@@ -359,7 +362,7 @@ sudo_fakepwuid(uid)
*/
struct passwd *
sudo_fakepwnam(user)
char *user;
const char *user;
{
struct passwd *pw;
size_t len;
@@ -370,8 +373,8 @@ sudo_fakepwnam(user)
pw->pw_uid = (uid_t) atoi(user + 1);
pw->pw_name = (char *)pw + sizeof(struct passwd);
strlcpy(pw->pw_name, user, len + 1);
rbinsert(cache_byname, (VOID *) pw);
rbinsert(cache_byuid, (VOID *) pw);
rbinsert(pwcache_byname, (VOID *) pw);
rbinsert(pwcache_byuid, (VOID *) pw);
return(pw);
}
@@ -394,8 +397,8 @@ sudo_setpwent()
#ifdef HAVE_GETAUTHUID
setauthent();
#endif
cache_byuid = rbcreate(cmp_byuid);
cache_byname = rbcreate(cmp_byname);
pwcache_byuid = rbcreate(cmp_pwuid);
pwcache_byname = rbcreate(cmp_pwnam);
}
void
@@ -417,10 +420,10 @@ sudo_endpwent()
#ifdef HAVE_GETAUTHUID
endauthent();
#endif
rbdestroy(cache_byuid, pw_free);
cache_byuid = NULL;
rbdestroy(cache_byname, NULL);
cache_byname = NULL;
rbdestroy(pwcache_byuid, pw_free);
pwcache_byuid = NULL;
rbdestroy(pwcache_byname, NULL);
pwcache_byname = NULL;
}
static void
@@ -432,3 +435,152 @@ pw_free(v)
zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
free(pw);
}
/*
* Compare by gid.
*/
static int
cmp_grgid(v1, v2)
const VOID *v1;
const VOID *v2;
{
const struct group *grp1 = (const struct group *) v1;
const struct group *grp2 = (const struct group *) v2;
return(grp1->gr_gid - grp2->gr_gid);
}
/*
* Compare by group name.
*/
static int
cmp_grnam(v1, v2)
const VOID *v1;
const VOID *v2;
{
const struct group *grp1 = (const struct group *) v1;
const struct group *grp2 = (const struct group *) v2;
return(strcmp(grp1->gr_name, grp2->gr_name));
}
void
sudo_setgrent()
{
setgrent();
grcache_bygid = rbcreate(cmp_grgid);
grcache_byname = rbcreate(cmp_grnam);
}
void
sudo_endgrent()
{
endgrent();
rbdestroy(grcache_bygid, free);
grcache_bygid = NULL;
rbdestroy(grcache_byname, NULL);
grcache_byname = NULL;
}
static struct group *
sudo_grdup(gr)
const struct group *gr;
{
char *cp;
size_t nsize, psize, csize, num, total, len;
struct group *newgr;
/* Allocate in one big chunk for easy freeing. */
nsize = psize = csize = num = 0;
total = sizeof(struct group);
if (gr->gr_name) {
nsize = strlen(gr->gr_name) + 1;
total += nsize;
}
if (gr->gr_passwd) {
psize = strlen(gr->gr_passwd) + 1;
total += psize;
}
if (gr->gr_mem) {
for (num = 0; gr->gr_mem[num] != NULL; num++)
total += strlen(gr->gr_mem[num]) + 1;
num++;
total += sizeof(char *) * num;
}
if ((cp = malloc(total)) == NULL)
return (NULL);
newgr = (struct group *)cp;
/*
* Copy in group contents and make strings relative to space
* at the end of the buffer.
*/
(void)memcpy(newgr, gr, sizeof(struct group));
cp += sizeof(struct group);
if (nsize) {
(void)memcpy(cp, gr->gr_name, nsize);
newgr->gr_name = cp;
cp += nsize;
}
if (psize) {
(void)memcpy(cp, gr->gr_passwd, psize);
newgr->gr_passwd = cp;
cp += psize;
}
if (gr->gr_mem) {
newgr->gr_mem = (char **)cp;
cp += sizeof(char *) * num;
for (num = 0; gr->gr_mem[num] != NULL; num++) {
len = strlen(gr->gr_mem[num]) + 1;
memcpy(cp, gr->gr_mem[num], len);
newgr->gr_mem[num] = cp;
cp += len;
}
newgr->gr_mem[num] = NULL;
}
return (newgr);
}
/*
* Get a group entry by gid and allocate space for it.
*/
struct group *
sudo_getgruid(gid)
gid_t gid;
{
struct group key, *gr;
struct rbnode *node;
key.gr_gid = gid;
if ((node = rbfind(grcache_bygid, &key)) != NULL)
return((struct group *) node->data);
if ((gr = getgrgid(gid)) == NULL)
return(NULL);
else
gr = sudo_grdup(gr);
rbinsert(grcache_byname, (VOID *) gr);
rbinsert(grcache_bygid, (VOID *) gr);
return(gr);
}
/*
* Get a group entry by name and allocate space for it.
*/
struct group *
sudo_getgrnam(name)
const char *name;
{
struct group key, *gr;
struct rbnode *node;
key.gr_name = (char *) name;
if ((node = rbfind(grcache_byname, &key)) != NULL)
return((struct group *) node->data);
if ((gr = getgrnam(name)) == NULL)
return(NULL);
else
gr = sudo_grdup(gr);
rbinsert(grcache_byname, (VOID *) gr);
rbinsert(grcache_bygid, (VOID *) gr);
return(gr);
}

7
glob.c
View File

@@ -172,6 +172,9 @@ static int match __P((Char *, Char *, Char *));
static void qprintf __P((const char *, Char *));
#endif
extern struct passwd *sudo_getpwnam __P((const char *));
extern struct passwd *sudo_getpwuid __P((uid_t));
int
glob(pattern, flags, errfunc, pglob)
const char *pattern;
@@ -385,7 +388,7 @@ globtilde(pattern, patbuf, patbuf_len, pglob)
* first and then trying the password file
*/
if ((h = getenv("HOME")) == NULL) {
if ((pwd = getpwuid(getuid())) == NULL)
if ((pwd = sudo_getpwuid(getuid())) == NULL)
return pattern;
else
h = pwd->pw_dir;
@@ -394,7 +397,7 @@ globtilde(pattern, patbuf, patbuf_len, pglob)
/*
* Expand a ~user
*/
if ((pwd = getpwnam((char*) patbuf)) == NULL)
if ((pwd = sudo_getpwnam((char*) patbuf)) == NULL)
return pattern;
else
h = pwd->pw_dir;

4
ldap.c
View File

@@ -440,7 +440,7 @@ sudo_ldap_build_pass1()
ncat(&b,&sz,")");
/* Append primary group */
grp=getgrgid(getgid());
grp=sudo_getgrgid(getgid());
if (grp!=NULL){
ncat(&b,&sz,"(sudoUser=%");
ncat(&b,&sz,grp->gr_name);
@@ -452,7 +452,7 @@ sudo_ldap_build_pass1()
grplist=calloc(ngrps,sizeof(gid_t));
if (grplist!=NULL && (0<getgroups(ngrps,grplist)))
for(i=0;i<ngrps;i++){
if((grp=getgrgid(grplist[i]))!=NULL){
if((grp=sudo_getgrgid(grplist[i]))!=NULL){
ncat(&b,&sz,"(sudoUser=%");
ncat(&b,&sz,grp->gr_name);
ncat(&b,&sz,")");

View File

@@ -492,7 +492,7 @@ send_mail(line)
/* Close password and group files so we don't leak fds. */
sudo_endpwent();
endgrent();
sudo_endgrent();
/*
* Depending on the config, either run the mailer as root

14
match.c
View File

@@ -528,7 +528,7 @@ userpw_matches(sudoers_user, user, pw)
/*
* Returns TRUE if the given user belongs to the named group,
* else returns FALSE.
* XXX - reduce the number of passwd/group lookups
* XXX - reduce the number of group lookups
*/
int
usergr_matches(group, user, pw)
@@ -539,24 +539,32 @@ usergr_matches(group, user, pw)
struct group *grp;
gid_t pw_gid;
char **cur;
int n;
/* make sure we have a valid usergroup, sudo style */
if (*group++ != '%')
return(FALSE);
/* look up user's primary gid in the passwd file */
if (pw == NULL && (pw = getpwnam(user)) == NULL)
if (pw == NULL && (pw = sudo_getpwnam(user)) == NULL)
return(FALSE);
pw_gid = pw->pw_gid;
if ((grp = getgrnam(group)) == NULL)
if ((grp = sudo_getgrnam(group)) == NULL)
return(FALSE);
/* check against user's primary (passwd file) gid */
if (grp->gr_gid == pw_gid)
return(TRUE);
/* check the user's group vector */
n = user_ngroups;
while (n--)
if (grp->gr_gid == user_groups[n])
return(TRUE);
/* check to see if user is explicitly listed in the group */
/* XXX - skip if group vector is set? */
for (cur = grp->gr_mem; *cur; cur++) {
if (strcmp(*cur, user) == 0)
return(TRUE);

View File

@@ -22,9 +22,6 @@ typedef int (*schandler_t)
struct childinfo;
struct listhead;
extern struct passwd *sudo_getpwuid __P((uid_t));
extern struct passwd *sudo_fakepwuid __P((uid_t));
static int check_execv __P((int, pid_t, u_int16_t,
struct str_msg_ask *, int, int *, int *));
static int check_execve __P((int, pid_t, u_int16_t,

16
sudo.c
View File

@@ -107,9 +107,6 @@ static struct passwd *get_authpw __P((void));
extern int sudo_edit __P((int, char **));
extern char **rebuild_env __P((char **, int, int));
extern char **zero_env __P((char **));
extern struct passwd *sudo_fakepwnam __P((const char *));
extern struct passwd *sudo_getpwnam __P((const char *));
extern struct passwd *sudo_getpwuid __P((uid_t));
/*
* Globals
@@ -194,6 +191,7 @@ main(argc, argv, envp)
*/
initial_setup();
sudo_setpwent();
sudo_setgrent();
/* Parse our arguments. */
sudo_mode = parse_args(Argc, Argv);
@@ -294,9 +292,9 @@ main(argc, argv, envp)
struct passwd *pw;
if (*def_timestampowner == '#')
pw = getpwuid(atoi(def_timestampowner + 1));
pw = sudo_getpwuid(atoi(def_timestampowner + 1));
else
pw = getpwnam(def_timestampowner);
pw = sudo_getpwnam(def_timestampowner);
if (!pw)
log_error(0, "timestamp owner (%s): No such user",
def_timestampowner);
@@ -397,7 +395,7 @@ main(argc, argv, envp)
/* Close the password and group files */
sudo_endpwent();
endgrent();
sudo_endgrent();
/* Install the real environment. */
environ = new_environ;
@@ -555,6 +553,12 @@ init_vars(sudo_mode)
/* It is now safe to use log_error() and set_perms() */
if ((user_ngroups = getgroups(0, NULL)) > 0) {
user_groups = emalloc2(user_ngroups, sizeof(gid_t));
if (getgroups(user_ngroups, user_groups) < 0)
log_error(USE_ERRNO|MSG_ONLY, "can't get group vector");
}
if (def_fqdn)
set_fqdn(); /* may call log_error() */

14
sudo.h
View File

@@ -40,7 +40,6 @@ struct sudo_user {
char *path;
char *shell;
char *tty;
char cwd[PATH_MAX];
char *host;
char *shost;
char **runas;
@@ -50,6 +49,9 @@ struct sudo_user {
char *cmnd_base;
char *cmnd_safe;
char *class_name;
int ngroups;
gid_t *groups;
char cwd[PATH_MAX];
};
/*
@@ -123,6 +125,8 @@ struct sudo_user {
#define user_gid (sudo_user.pw->pw_gid)
#define user_dir (sudo_user.pw->pw_dir)
#define user_shell (sudo_user.shell)
#define user_ngroups (sudo_user.ngroups)
#define user_groups (sudo_user.groups)
#define user_tty (sudo_user.tty)
#define user_cwd (sudo_user.cwd)
#define user_runas (sudo_user.runas)
@@ -240,7 +244,15 @@ FILE *open_sudoers __P((const char *, int *));
void display_privs __P((struct passwd *));
void sudo_setpwent __P((void));
void sudo_endpwent __P((void));
void sudo_setgrent __P((void));
void sudo_endgrent __P((void));
void cleanup __P((void));
struct passwd *sudo_getpwnam __P((const char *));
struct passwd *sudo_fakepwnam __P((const char *));
struct passwd *sudo_getpwuid __P((uid_t));
struct passwd *sudo_fakepwuid __P((uid_t));
struct group *sudo_getgrnam __P((const char *));
struct group *sudo_getgrgid __P((gid_t));
#ifdef HAVE_SYSTRACE
void systrace_attach __P((pid_t));
#endif

View File

@@ -147,10 +147,14 @@ main(argc, argv)
if (!dflag)
usage();
user_name = "nobody";
user_cmnd = "true";
user_cmnd = user_base = "true";
} else {
user_name = *argv++;
user_cmnd = *argv;
if ((p = strrchr(user_cmnd, '/')) != NULL)
user_base = p + 1;
else
user_base = user_cmnd;
NewArgc -= 2;
}
@@ -275,6 +279,34 @@ set_perms(perm)
return;
}
struct passwd *
sudo_getpwuid(uid)
uid_t uid;
{
return(getpwuid(uid));
}
struct passwd *
sudo_getpwnam(name)
const char *name;
{
return(getpwnam(name));
}
struct group *
sudo_getgrgid(gid)
gid_t gid;
{
return(getgrgid(gid));
}
struct group *
sudo_getgrnam(name)
const char *name;
{
return(getgrnam(name));
}
void
cleanup()
{

View File

@@ -60,6 +60,7 @@
#endif /* HAVE_UNISTD_H */
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
@@ -524,6 +525,38 @@ user_is_exempt()
return(FALSE);
}
/* STUB */
struct passwd *
sudo_getpwuid(uid)
uid_t uid;
{
return(getpwuid(uid));
}
/* STUB */
struct passwd *
sudo_getpwnam(name)
const char *name;
{
return(getpwnam(name));
}
/* STUB */
struct group *
sudo_getgrgid(gid)
gid_t gid;
{
return(getgrgid(gid));
}
/* STUB */
struct group *
sudo_getgrnam(name)
const char *name;
{
return(getgrnam(name));
}
/*
* Assuming a parse error occurred, prompt the user for what they want
* to do now. Returns the first letter of their choice.