]> granicus.if.org Git - sudo/commitdiff
Reference count cached passwd and group structs. The cache holds
authorTodd C. Miller <Todd.Miller@courtesan.com>
Wed, 4 Aug 2010 13:13:17 +0000 (09:13 -0400)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Wed, 4 Aug 2010 13:13:17 +0000 (09:13 -0400)
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.

--HG--
branch : 1.7

check.c
get_pty.c
glob.c
ldap.c
match.c
pwutil.c
sudo.c
sudo.h

diff --git a/check.c b/check.c
index 7fd722406b2988236fbb140131c91b21e88cd73f..42b1eacd0339d23d967dc90a3546f9e9585f68e2 100644 (file)
--- a/check.c
+++ b/check.c
@@ -163,6 +163,8 @@ check_user(validated, mode)
        update_timestamp(timestampdir, timestampfile);
     efree(timestampdir);
     efree(timestampfile);
+    pw_delref(auth_pw);
+    auth_pw = NULL;
 }
 
 /*
index 5eb653faed2fe6889f5359ba6a0520ebbf1a4afc..e271bf26ecec5e76145e8bb9e2f7c058e64f85fa 100644 (file)
--- a/get_pty.c
+++ b/get_pty.c
@@ -67,8 +67,10 @@ get_pty(master, slave, name, namesz, ttyuid)
     struct group *gr;
     gid_t ttygid = -1;
 
-    if ((gr = sudo_getgrnam("tty")) != NULL)
+    if ((gr = sudo_getgrnam("tty")) != NULL) {
        ttygid = gr->gr_gid;
+       gr_delref(gr);
+    }
 
     if (openpty(master, slave, name, NULL, NULL) != 0)
        return(0);
diff --git a/glob.c b/glob.c
index 9673626531776c465f47e40f3efd9cb33d099e96..9ffce32088749e91b7cc351bc361ad29d4a188f9 100644 (file)
--- a/glob.c
+++ b/glob.c
@@ -175,6 +175,7 @@ static void  qprintf __P((const char *, Char *));
 
 extern struct passwd *sudo_getpwnam __P((const char *));
 extern struct passwd *sudo_getpwuid __P((uid_t));
+extern void pw_delref __P((struct passwd *));
 
 int
 glob(pattern, flags, errfunc, pglob)
@@ -367,7 +368,7 @@ globtilde(pattern, patbuf, patbuf_len, pglob)
        size_t patbuf_len;
        glob_t *pglob;
 {
-       struct passwd *pwd;
+       struct passwd *pwd = NULL;
        char *h;
        const Char *p;
        Char *b, *eb;
@@ -413,6 +414,9 @@ globtilde(pattern, patbuf, patbuf_len, pglob)
                continue;
        *b = EOS;
 
+       if (pwd)
+           pw_delref(pwd);
+
        return patbuf;
 }
 
diff --git a/ldap.c b/ldap.c
index 9554df22e8c4ad5637510b0f819f174b151e3249..6c7870265bfaad9d80d466cec90f1fdd996b9691 100644 (file)
--- a/ldap.c
+++ b/ldap.c
@@ -856,13 +856,17 @@ sudo_ldap_build_pass1(pw)
     sz = 29 + strlen(pw->pw_name);
 
     /* Add space for groups */
-    if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL)
+    if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
        sz += 12 + strlen(grp->gr_name);        /* primary group */
+       gr_delref(grp);
+    }
     for (i = 0; i < user_ngroups; i++) {
        if (user_groups[i] == pw->pw_gid)
            continue;
-       if ((grp = sudo_getgrgid(user_groups[i])) != NULL)
+       if ((grp = sudo_getgrgid(user_groups[i])) != NULL) {
            sz += 12 + strlen(grp->gr_name);    /* supplementary group */
+           gr_delref(grp);
+       }
     }
     buf = emalloc(sz);
 
@@ -876,6 +880,7 @@ sudo_ldap_build_pass1(pw)
        (void) strlcat(buf, "(sudoUser=%", sz);
        (void) strlcat(buf, grp->gr_name, sz);
        (void) strlcat(buf, ")", sz);
+       gr_delref(grp);
     }
 
     /* Append supplementary groups */
@@ -886,6 +891,7 @@ sudo_ldap_build_pass1(pw)
            (void) strlcat(buf, "(sudoUser=%", sz);
            (void) strlcat(buf, grp->gr_name, sz);
            (void) strlcat(buf, ")", sz);
+           gr_delref(grp);
        }
     }
 
diff --git a/match.c b/match.c
index 64d413b3def63a4e2c384e8516eff38d608b086a..ef5c53936afad881a8e4e2e5acb444d6532b0308 100644 (file)
--- a/match.c
+++ b/match.c
@@ -812,29 +812,45 @@ usergr_matches(group, user, pw)
     char *user;
     struct passwd *pw;
 {
+    int matched = FALSE;
+    struct passwd *pw0 = NULL;
+
     /* make sure we have a valid usergroup, sudo style */
     if (*group++ != '%')
-       return(FALSE);
+       goto done;
 
 #ifdef USING_NONUNIX_GROUPS
-    if (*group == ':')
-       return(sudo_nonunix_groupcheck(++group, user, pw));   
+    if (*group == ':') {
+       matched = sudo_nonunix_groupcheck(++group, user, pw);
+       goto done;
+    }
 #endif /* USING_NONUNIX_GROUPS */
 
     /* look up user's primary gid in the passwd file */
-    if (pw == NULL && (pw = sudo_getpwnam(user)) == NULL)
-       return(FALSE);
+    if (pw == NULL) {
+       if ((pw0 = sudo_getpwnam(user)) == NULL)
+           goto done;
+       pw = pw0;
+    }
 
-    if (user_in_group(pw, group))
-       return(TRUE);
+    if (user_in_group(pw, group)) {
+       matched = TRUE;
+       goto done;
+    }
 
 #ifdef USING_NONUNIX_GROUPS
     /* not a Unix group, could be an AD group */
     if (sudo_nonunix_groupcheck_available() &&
-       sudo_nonunix_groupcheck(group, user, pw))
-       return(TRUE);
+       sudo_nonunix_groupcheck(group, user, pw)) {
+       matched = TRUE;
+       goto done;
+    }
 #endif /* USING_NONUNIX_GROUPS */
 
+done:
+    if (pw0 != NULL)
+       pw_delref(pw0);
+
     return(FALSE);
 }
 
index 1b8cba271c430b6c4ce97300462091c509869ca6..9c72285307940be20a2205e110525bf1bb06525f 100644 (file)
--- a/pwutil.c
+++ b/pwutil.c
@@ -66,7 +66,10 @@ static int  cmp_grgid        __P((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;
@@ -194,10 +197,43 @@ make_pwitem(pw, name)
        item->k.uid = pw->pw_uid;
     }
     item->d.pw = newpw;
+    item->refcnt = 1;
 
     return(item);
 }
 
+void
+pw_addref(pw)
+    struct passwd *pw;
+{
+    ptr_to_item(pw)->refcnt++;
+}
+
+static void
+pw_delref_item(v)
+    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(pw)
+    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.
@@ -232,6 +268,7 @@ sudo_getpwuid(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)
@@ -241,6 +278,7 @@ sudo_getpwuid(uid)
     aix_restoreauthdb();
 #endif
 done:
+    item->refcnt++;
     return(item->d.pw);
 }
 
@@ -279,6 +317,7 @@ sudo_getpwnam(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;
@@ -289,6 +328,7 @@ sudo_getpwnam(name)
     aix_restoreauthdb();
 #endif
 done:
+    item->refcnt++;
     return(item->d.pw);
 }
 
@@ -328,72 +368,28 @@ sudo_fakepwnam(user, 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(group)
-    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()
 {
@@ -405,47 +401,25 @@ sudo_setpwent()
        pwcache_byname = rbcreate(cmp_pwnam);
 }
 
-#ifdef PURIFY
-static void pw_free    __P((void *));
-
 void
 sudo_freepwcache()
 {
     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(v)
-    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()
 {
     endpwent();
     sudo_endspent();
-#ifdef PURIFY
-    sudo_freepwcache();        /* XXX - use after free */
-#endif
+    sudo_freepwcache();
 }
 
 /*
@@ -524,10 +498,35 @@ make_gritem(gr, name)
        item->k.gid = gr->gr_gid;
     }
     item->d.gr = newgr;
+    item->refcnt = 1;
 
     return(item);
 }
 
+void
+gr_addref(gr)
+    struct group *gr;
+{
+    ptr_to_item(gr)->refcnt++;
+}
+
+static void
+gr_delref_item(v)
+    void *v;
+{
+    struct cache_item *item = v;
+
+    if (--item->refcnt == 0)
+       efree(item);
+}
+
+void
+gr_delref(gr)
+    struct group *gr;
+{
+    gr_delref_item(ptr_to_item(gr));
+}
+
 /*
  * Get a group entry by gid and allocate space for it.
  */
@@ -553,12 +552,14 @@ sudo_getgrgid(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);
 }
 
@@ -588,6 +589,7 @@ sudo_getgrnam(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;
@@ -595,9 +597,56 @@ sudo_getgrnam(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(group)
+    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()
 {
@@ -608,28 +657,24 @@ sudo_setgrent()
        grcache_byname = rbcreate(cmp_grnam);
 }
 
-#ifdef PURIFY
 void
 sudo_freegrcache()
 {
     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()
 {
     endgrent();
-#ifdef PURIFY
-    sudo_freegrcache();        /* XXX - use after free */
-#endif
+    sudo_freegrcache();
 }
 
 int
diff --git a/sudo.c b/sudo.c
index 3bccdd34c484fab61e2e2068c257658664c2346e..8e25d24a2681c342ed67b4d6cd5f27685704a300 100644 (file)
--- a/sudo.c
+++ b/sudo.c
@@ -393,6 +393,7 @@ main(argc, argv, envp)
            log_error(0, "timestamp owner (%s): No such user",
                def_timestampowner);
        timestamp_uid = pw->pw_uid;
+       pw_delref(pw);
     }
 
     /* If given the -P option, set the "preserve_groups" flag. */
@@ -441,6 +442,8 @@ main(argc, argv, envp)
            struct passwd *pw;
 
            if ((pw = sudo_getpwnam(prev_user)) != NULL) {
+                   if (sudo_user.pw != NULL)
+                       pw_delref(sudo_user.pw);
                    sudo_user.pw = pw;
 #ifdef HAVE_MBR_CHECK_MEMBERSHIP
                    mbr_uid_to_uuid(user_uid, user_uuid);
@@ -1252,13 +1255,15 @@ set_fqdn()
 }
 
 /*
- * Get passwd entry for the user we are going to run commands as.
- * By default, this is "root".  Updates runas_pw as a side effect.
+ * Get passwd entry for the user we are going to run commands as
+ * and store it in runas_pw.  By default, commands run as "root".
  */
 static void
 set_runaspw(user)
     char *user;
 {
+    if (runas_pw != NULL)
+       pw_delref(runas_pw);
     if (*user == '#') {
        if ((runas_pw = sudo_getpwuid(atoi(user + 1))) == NULL)
            runas_pw = sudo_fakepwnam(user, runas_gr ? runas_gr->gr_gid : 0);
@@ -1307,9 +1312,12 @@ get_authpw()
        if (runas_pw->pw_name == NULL)
            log_error(NO_MAIL|MSG_ONLY, "unknown uid: %lu",
                (unsigned long) runas_pw->pw_uid);
+       pw_addref(runas_pw);
        pw = runas_pw;
-    } else
+    } else {
+       pw_addref(sudo_user.pw);
        pw = sudo_user.pw;
+    }
 
     return(pw);
 }
diff --git a/sudo.h b/sudo.h
index f946291d9eb1d986c264bc456377a0be0ad5bc9b..2e734cc1c54a8b7b0fce9130d49258f84e120014 100644 (file)
--- a/sudo.h
+++ b/sudo.h
@@ -301,6 +301,10 @@ void sudo_endspent __P((void));
 void sudo_setgrent     __P((void));
 void sudo_setpwent     __P((void));
 void sudo_setspent     __P((void));
+void gr_addref         __P((struct group *));
+void gr_delref         __P((struct group *));
+void pw_addref         __P((struct passwd *));
+void pw_delref         __P((struct passwd *));
 
 /* selinux.c */
 int selinux_restore_tty __P((void));