]> granicus.if.org Git - sudo/commitdiff
Implement group caching and use the passwd and group caches throughout.
authorTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 16 Nov 2004 04:24:11 +0000 (04:24 +0000)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 16 Nov 2004 04:24:11 +0000 (04:24 +0000)
check.c
getspwuid.c
glob.c
ldap.c
logging.c
match.c
mon_systrace.h
sudo.c
sudo.h
testsudoers.c
visudo.c

diff --git a/check.c b/check.c
index 511bec004c03bc38ba60b4c6902fa60b3f2b0582..d55a96fe89e71537ff572c309c32cba006d581ae 100644 (file)
--- 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)
index 4ffcba93b212aa84fd49c33eb947d61b3dbfe87b..b9670df895ab647820e5c7c4eba7bb2ecc774f12 100644 (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);
+}
+
diff --git a/glob.c b/glob.c
index 09cecc0ad649caa3cb4e5e0931a06365ce1088d6..ac98020e55ae3907160b7b70b9d6007a05978e23 100644 (file)
--- 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 14f4edde2b7ad2ed8ab612b07feeb326002615b4..2f30209bde469b0d210b7d2ea2764b587102a596 100644 (file)
--- 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 && (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,")");
index 381757f644fbfe14b0c9bbea7cb89fea251c17d0..cf9053d7bb516263ca4ad5605e253a72e1c1ec93 100644 (file)
--- 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 9a6696186690df53bd5bf09f42a1f122408ec20c..8c9f25f6687b2ea5e9eeaaada6eaf8acdc14d433 100644 (file)
--- 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);
index 8441cf4469777605e394e06afa8fa61e5af882fc..a28f7a3010cc16d62dcc51ec5fe5f8442076b05b 100644 (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,
diff --git a/sudo.c b/sudo.c
index 5c8438dba909f418e17daf2bb462119a367c861d..bc40007a563c533fa82a20986df44962e595bac0 100644 (file)
--- 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 5f5c0c70cd7d7ab28316dc0b39cb4addca346df2..91b358615e6c6be235d15d500baf1bbc9bb149c6 100644 (file)
--- 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
index 055fa2bbd53633df5b8c00d317d4c3acb256dd50..7603f449df573b54f4b0b0e0af16198ad2442725 100644 (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()
 {
index cb4248df19a08f6c1dae04b2f6a25a9d0d7c2774..1d849ec07fe729e3941202eb1bbff7097cf5c2dd 100644 (file)
--- a/visudo.c
+++ b/visudo.c
@@ -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.