From 9846e562adc55c46d9a80f276e05a737c16cb1e8 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Tue, 16 Nov 2004 04:24:11 +0000 Subject: [PATCH] Implement group caching and use the passwd and group caches throughout. --- check.c | 2 +- getspwuid.c | 202 +++++++++++++++++++++++++++++++++++++++++++------ glob.c | 7 +- ldap.c | 4 +- logging.c | 2 +- match.c | 14 +++- mon_systrace.h | 3 - sudo.c | 16 ++-- sudo.h | 14 +++- testsudoers.c | 34 ++++++++- visudo.c | 33 ++++++++ 11 files changed, 286 insertions(+), 45 deletions(-) diff --git a/check.c b/check.c index 511bec004..d55a96fe8 100644 --- a/check.c +++ b/check.c @@ -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) diff --git a/getspwuid.c b/getspwuid.c index 4ffcba93b..b9670df89 100644 --- a/getspwuid.c +++ b/getspwuid.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 1998-2002 Todd C. Miller + * Copyright (c) 1996, 1998-2004 Todd C. Miller * * 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 #endif /* HAVE_UNISTD_H */ #include +#include #ifdef HAVE_GETSPNAM # include #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); +} + diff --git a/glob.c b/glob.c index 09cecc0ad..ac98020e5 100644 --- a/glob.c +++ b/glob.c @@ -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; diff --git a/ldap.c b/ldap.c index 14f4edde2..2f30209bd 100644 --- a/ldap.c +++ b/ldap.c @@ -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 && (0gr_name); ncat(&b,&sz,")"); diff --git a/logging.c b/logging.c index 381757f64..cf9053d7b 100644 --- a/logging.c +++ b/logging.c @@ -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 diff --git a/match.c b/match.c index 9a6696186..8c9f25f66 100644 --- a/match.c +++ b/match.c @@ -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); diff --git a/mon_systrace.h b/mon_systrace.h index 8441cf446..a28f7a301 100644 --- a/mon_systrace.h +++ b/mon_systrace.h @@ -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, diff --git a/sudo.c b/sudo.c index 5c8438dba..bc40007a5 100644 --- a/sudo.c +++ b/sudo.c @@ -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() */ diff --git a/sudo.h b/sudo.h index 5f5c0c70c..91b358615 100644 --- a/sudo.h +++ b/sudo.h @@ -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 diff --git a/testsudoers.c b/testsudoers.c index 055fa2bbd..7603f449d 100644 --- a/testsudoers.c +++ b/testsudoers.c @@ -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() { diff --git a/visudo.c b/visudo.c index cb4248df1..1d849ec07 100644 --- a/visudo.c +++ b/visudo.c @@ -60,6 +60,7 @@ #endif /* HAVE_UNISTD_H */ #include #include +#include #include #include #include @@ -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. -- 2.50.1