]> 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:58:50 +0000 (09:58 -0400)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Wed, 4 Aug 2010 13:58:50 +0000 (09:58 -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.

plugins/sudoers/check.c
plugins/sudoers/ldap.c
plugins/sudoers/match.c
plugins/sudoers/pwutil.c
plugins/sudoers/sudoers.c
plugins/sudoers/sudoers.h

index 156f051b859b776649a9cfd5d8505c8f4ccf4b88..8b2940b0ede74e94e32035b2c9b2fd8f67057086 100644 (file)
@@ -648,6 +648,10 @@ remove_timestamp(int remove)
 
     efree(timestampdir);
     efree(timestampfile);
+    if (auth_pw) {
+       pw_delref(auth_pw);
+       auth_pw = NULL;
+    }
 }
 
 /*
index 4f340742c2f3cd0f96ff7b6e52aa1f2e0795db8c..aa64396a8d8ab293ca8385c40f65da88145288e6 100644 (file)
@@ -801,13 +801,17 @@ sudo_ldap_build_pass1(struct passwd *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);
 
@@ -821,6 +825,7 @@ sudo_ldap_build_pass1(struct passwd *pw)
        (void) strlcat(buf, "(sudoUser=%", sz);
        (void) strlcat(buf, grp->gr_name, sz);
        (void) strlcat(buf, ")", sz);
+       gr_delref(grp);
     }
 
     /* Append supplementary groups */
@@ -831,6 +836,7 @@ sudo_ldap_build_pass1(struct passwd *pw)
            (void) strlcat(buf, "(sudoUser=%", sz);
            (void) strlcat(buf, grp->gr_name, sz);
            (void) strlcat(buf, ")", sz);
+           gr_delref(grp);
        }
     }
 
index 6093b8893d632199853aa36b47b3f2aed4d9bc08..bee12a529acb0f4c0151b1e15d4e2398c80db572 100644 (file)
@@ -771,25 +771,41 @@ group_matches(char *sudoers_group, struct group *gr)
 int
 usergr_matches(char *group, 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;
 
-    if (*group == ':' && def_group_plugin)
-       return(group_plugin_query(user, group + 1, pw));
+    if (*group == ':' && def_group_plugin) {
+       matched = group_plugin_query(user, group + 1, pw);
+       goto done;
+    }
 
     /* 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;
+    }
 
     /* not a Unix group, could be an external group */
-    if (def_group_plugin && group_plugin_query(user, group, pw))
-       return(TRUE);
+    if (def_group_plugin && group_plugin_query(user, group, pw)) {
+       matched = TRUE;
+       goto done;
+    }
 
-    return(FALSE);
+done:
+    if (pw0 != NULL)
+       pw_delref(pw0);
+
+    return(matched);
 }
 
 /*
index 485161320176aa06749e8bcb0642ec9e91d0c0b4..4f25cdb2924db14373de592fcbbf832e6ee2703e 100644 (file)
@@ -66,7 +66,10 @@ static int  cmp_grgid(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;
@@ -188,10 +191,40 @@ make_pwitem(const struct passwd *pw, const char *name)
        item->k.uid = pw->pw_uid;
     }
     item->d.pw = newpw;
+    item->refcnt = 1;
 
     return(item);
 }
 
+void
+pw_addref(struct passwd *pw)
+{
+    ptr_to_item(pw)->refcnt++;
+}
+
+static void
+pw_delref_item(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(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.
@@ -225,6 +258,7 @@ sudo_getpwuid(uid_t 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)
@@ -234,6 +268,7 @@ sudo_getpwuid(uid_t uid)
     aix_restoreauthdb();
 #endif
 done:
+    item->refcnt++;
     return(item->d.pw);
 }
 
@@ -271,6 +306,7 @@ sudo_getpwnam(const char *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;
@@ -281,6 +317,7 @@ sudo_getpwnam(const char *name)
     aix_restoreauthdb();
 #endif
 done:
+    item->refcnt++;
     return(item->d.pw);
 }
 
@@ -318,71 +355,28 @@ sudo_fakepwnam(const char *user, gid_t 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(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(void)
 {
@@ -394,46 +388,25 @@ sudo_setpwent(void)
        pwcache_byname = rbcreate(cmp_pwnam);
 }
 
-#ifdef PURIFY
-static void pw_free(void *);
-
 void
 sudo_freepwcache(void)
 {
     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(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(void)
 {
     endpwent();
     sudo_endspent();
-#ifdef PURIFY
-    sudo_freepwcache();        /* XXX - use after free */
-#endif
+    sudo_freepwcache();
 }
 
 /*
@@ -508,10 +481,32 @@ make_gritem(const struct group *gr, const char *name)
        item->k.gid = gr->gr_gid;
     }
     item->d.gr = newgr;
+    item->refcnt = 1;
 
     return(item);
 }
 
+void
+gr_addref(struct group *gr)
+{
+    ptr_to_item(gr)->refcnt++;
+}
+
+static void
+gr_delref_item(void *v)
+{
+    struct cache_item *item = v;
+
+    if (--item->refcnt == 0)
+       efree(item);
+}
+
+void
+gr_delref(struct group *gr)
+{
+    gr_delref_item(ptr_to_item(gr));
+}
+
 /*
  * Get a group entry by gid and allocate space for it.
  */
@@ -536,12 +531,14 @@ sudo_getgrgid(gid_t 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);
 }
 
@@ -570,6 +567,7 @@ sudo_getgrnam(const char *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;
@@ -577,9 +575,55 @@ sudo_getgrnam(const char *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(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(void)
 {
@@ -590,28 +634,24 @@ sudo_setgrent(void)
        grcache_byname = rbcreate(cmp_grnam);
 }
 
-#ifdef PURIFY
 void
 sudo_freegrcache(void)
 {
     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(void)
 {
     endgrent();
-#ifdef PURIFY
-    sudo_freegrcache();        /* XXX - use after free */
-#endif
+    sudo_freegrcache();
 }
 
 int
@@ -625,6 +665,7 @@ user_in_group(struct passwd *pw, const char *group)
     int i;
 #endif
     struct group *grp;
+    int retval = FALSE;
 
 #ifdef HAVE_SETAUTHDB
     aix_setauthdb(pw->pw_name);
@@ -634,23 +675,29 @@ user_in_group(struct passwd *pw, const char *group)
     aix_restoreauthdb();
 #endif
     if (grp == NULL)
-       return(FALSE);
+       goto done;
 
     /* check against user's primary (passwd file) gid */
-    if (grp->gr_gid == pw->pw_gid)
-       return(TRUE);
+    if (grp->gr_gid == pw->pw_gid) {
+       retval = TRUE;
+       goto done;
+    }
 
 #ifdef HAVE_MBR_CHECK_MEMBERSHIP
     /* If we are matching the invoking user use the stashed uuid. */
     if (strcmp(pw->pw_name, user_name) == 0) {
        if (mbr_gid_to_uuid(grp->gr_gid, gu) == 0 &&
-           mbr_check_membership(user_uuid, gu, &ismember) == 0 && ismember)
-           return(TRUE);
+           mbr_check_membership(user_uuid, gu, &ismember) == 0 && ismember) {
+           retval = TRUE;
+           goto done;
+       }
     } else {
        if (mbr_uid_to_uuid(pw->pw_uid, uu) == 0 &&
            mbr_gid_to_uuid(grp->gr_gid, gu) == 0 &&
-           mbr_check_membership(uu, gu, &ismember) == 0 && ismember)
-           return(TRUE);
+           mbr_check_membership(uu, gu, &ismember) == 0 && ismember) {
+           retval = TRUE;
+           goto done;
+       }
     }
 #else /* HAVE_MBR_CHECK_MEMBERSHIP */
 # ifdef HAVE_GETGROUPS
@@ -661,20 +708,26 @@ user_in_group(struct passwd *pw, const char *group)
     if (user_ngroups >= 0 &&
        strcmp(pw->pw_name, list_pw ? list_pw->pw_name : user_name) == 0) {
        for (i = 0; i < user_ngroups; i++) {
-           if (grp->gr_gid == user_groups[i])
-               return(TRUE);
+           if (grp->gr_gid == user_groups[i]) {
+               retval = TRUE;
+               goto done;
+           }
        }
     } else
 # endif /* HAVE_GETGROUPS */
     {
        if (grp != NULL && grp->gr_mem != NULL) {
            for (gr_mem = grp->gr_mem; *gr_mem; gr_mem++) {
-               if (strcmp(*gr_mem, pw->pw_name) == 0)
-                   return(TRUE);
+               if (strcmp(*gr_mem, pw->pw_name) == 0) {
+                   retval = TRUE;
+                   goto done;
+               }
            }
        }
     }
 #endif /* HAVE_MBR_CHECK_MEMBERSHIP */
 
-    return(FALSE);
+done:
+    gr_delref(grp);
+    return(retval);
 }
index 328d90389b3b49a2bbbc60bbbcc4f3fdd6a6225d..47b07d670af952621ec7cbeff98771417db4fa22 100644 (file)
@@ -406,6 +406,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
            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. */
@@ -459,6 +460,8 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
            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);
@@ -1066,12 +1069,14 @@ set_fqdn(void)
 }
 
 /*
- * 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(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);
@@ -1084,12 +1089,14 @@ set_runaspw(char *user)
 }
 
 /*
- * Get group entry for the group we are going to run commands as.
- * Updates runas_pw as a side effect.
+ * Get group entry for the group we are going to run commands as
+ * and store it in runas_gr.
  */
 static void
 set_runasgr(char *group)
 {
+    if (runas_gr != NULL)
+       gr_delref(runas_gr);
     if (*group == '#') {
        if ((runas_gr = sudo_getgrgid(atoi(group + 1))) == NULL)
            runas_gr = sudo_fakegrnam(group);
@@ -1119,9 +1126,12 @@ get_authpw(void)
        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);
 }
index 5b726af6af15a625742bd5f3bc2ec00df48bff30..ccd880d2f6b58108c215a52da8ae4f03f4b78648 100644 (file)
@@ -270,6 +270,10 @@ struct passwd *sudo_getpwuid(uid_t);
 struct group *sudo_getgrnam(const char *);
 struct group *sudo_fakegrnam(const char *);
 struct group *sudo_getgrgid(gid_t);
+void gr_addref(struct group *);
+void gr_delref(struct group *);
+void pw_addref(struct passwd *);
+void pw_delref(struct passwd *);
 int user_in_group(struct passwd *, const char *);
 
 /* timestr.c */