Diff for /embedaddon/sudo/plugins/sudoers/pwutil.c between versions 1.1.1.2 and 1.1.1.5

version 1.1.1.2, 2012/05/29 12:26:49 version 1.1.1.5, 2014/06/15 16:12:54
Line 1 Line 1
 /*  /*
 * Copyright (c) 1996, 1998-2005, 2007-2011 * Copyright (c) 1996, 1998-2005, 2007-2013
  *      Todd C. Miller <Todd.Miller@courtesan.com>   *      Todd C. Miller <Todd.Miller@courtesan.com>
  *   *
  * Permission to use, copy, modify, and distribute this software for any   * Permission to use, copy, modify, and distribute this software for any
Line 22 Line 22
 #include <config.h>  #include <config.h>
   
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/stat.h>  
 #include <sys/param.h>  
 #include <stdio.h>  #include <stdio.h>
 #ifdef STDC_HEADERS  #ifdef STDC_HEADERS
 # include <stdlib.h>  # include <stdlib.h>
Line 48 Line 46
 #ifdef HAVE_SETAUTHDB  #ifdef HAVE_SETAUTHDB
 # include <usersec.h>  # include <usersec.h>
 #endif /* HAVE_SETAUTHDB */  #endif /* HAVE_SETAUTHDB */
 #ifdef HAVE_UTMPX_H  
 # include <utmpx.h>  
 #else  
 # include <utmp.h>  
 #endif /* HAVE_UTMPX_H */  
 #include <limits.h>  
 #include <pwd.h>  #include <pwd.h>
 #include <grp.h>  #include <grp.h>
   
 #include "sudoers.h"  #include "sudoers.h"
 #include "redblack.h"  #include "redblack.h"
   #include "pwutil.h"
   
 /*  /*
  * The passwd and group caches.   * The passwd and group caches.
Line 73  static int  cmp_grgid(const void *, const void *); Line 66  static int  cmp_grgid(const void *, const void *);
   
 #define cmp_grnam       cmp_pwnam  #define cmp_grnam       cmp_pwnam
   
 #define ptr_to_item(p) ((struct cache_item *)((char *)p - offsetof(struct cache_item_##p, p)))  
   
 /*  /*
  * Generic cache element.  
  */  
 struct cache_item {  
     unsigned int refcnt;  
     /* key */  
     union {  
         uid_t uid;  
         gid_t gid;  
         char *name;  
     } k;  
     /* datum */  
     union {  
         struct passwd *pw;  
         struct group *gr;  
         struct group_list *grlist;  
     } d;  
 };  
   
 /*  
  * Container structs to simpify size and offset calculations and guarantee  
  * proper aligment of struct passwd, group and group_list.  
  */  
 struct cache_item_pw {  
     struct cache_item cache;  
     struct passwd pw;  
 };  
   
 struct cache_item_gr {  
     struct cache_item cache;  
     struct group gr;  
 };  
   
 struct cache_item_grlist {  
     struct cache_item cache;  
     struct group_list grlist;  
     /* actually bigger */  
 };  
   
 /*  
  * Compare by uid.   * Compare by uid.
  */   */
 static int  static int
Line 136  cmp_pwnam(const void *v1, const void *v2) Line 88  cmp_pwnam(const void *v1, const void *v2)
     return strcmp(ci1->k.name, ci2->k.name);      return strcmp(ci1->k.name, ci2->k.name);
 }  }
   
 #define FIELD_SIZE(src, name, size)                     \  
 do {                                                    \  
         if (src->name) {                                \  
                 size = strlen(src->name) + 1;           \  
                 total += size;                          \  
         }                                               \  
 } while (0)  
   
 #define FIELD_COPY(src, dst, name, size)                \  
 do {                                                    \  
         if (src->name) {                                \  
                 memcpy(cp, src->name, size);            \  
                 dst->name = cp;                         \  
                 cp += size;                             \  
         }                                               \  
 } while (0)  
   
 /*  
  * Dynamically allocate space for a struct item plus the key and data  
  * elements.  If name is non-NULL it is used as the key, else the  
  * uid is the key.  Fills in datum from struct password.  
  */  
 static struct cache_item *  
 make_pwitem(const struct passwd *pw, const char *name)  
 {  
     char *cp;  
     const char *pw_shell;  
     size_t nsize, psize, csize, gsize, dsize, ssize, total;  
     struct cache_item_pw *pwitem;  
     struct passwd *newpw;  
     debug_decl(make_pwitem, SUDO_DEBUG_NSS)  
   
     /* If shell field is empty, expand to _PATH_BSHELL. */  
     pw_shell = (pw->pw_shell == NULL || pw->pw_shell[0] == '\0')  
         ? _PATH_BSHELL : pw->pw_shell;  
   
     /* Allocate in one big chunk for easy freeing. */  
     nsize = psize = csize = gsize = dsize = ssize = 0;  
     total = sizeof(*pwitem);  
     FIELD_SIZE(pw, pw_name, nsize);  
     FIELD_SIZE(pw, pw_passwd, psize);  
 #ifdef HAVE_LOGIN_CAP_H  
     FIELD_SIZE(pw, pw_class, csize);  
 #endif  
     FIELD_SIZE(pw, pw_gecos, gsize);  
     FIELD_SIZE(pw, pw_dir, dsize);  
     /* Treat shell specially since we expand "" -> _PATH_BSHELL */  
     ssize = strlen(pw_shell) + 1;  
     total += ssize;  
     if (name != NULL)  
         total += strlen(name) + 1;  
   
     /* Allocate space for struct item, struct passwd and the strings. */  
     pwitem = ecalloc(1, total);  
     newpw = &pwitem->pw;  
   
     /*  
      * Copy in passwd contents and make strings relative to space  
      * at the end of the struct.  
      */  
     memcpy(newpw, pw, sizeof(*pw));  
     cp = (char *)(pwitem + 1);  
     FIELD_COPY(pw, newpw, pw_name, nsize);  
     FIELD_COPY(pw, newpw, pw_passwd, psize);  
 #ifdef HAVE_LOGIN_CAP_H  
     FIELD_COPY(pw, newpw, pw_class, csize);  
 #endif  
     FIELD_COPY(pw, newpw, pw_gecos, gsize);  
     FIELD_COPY(pw, newpw, pw_dir, dsize);  
     /* Treat shell specially since we expand "" -> _PATH_BSHELL */  
     memcpy(cp, pw_shell, ssize);  
     newpw->pw_shell = cp;  
     cp += ssize;  
   
     /* Set key and datum. */  
     if (name != NULL) {  
         memcpy(cp, name, strlen(name) + 1);  
         pwitem->cache.k.name = cp;  
     } else {  
         pwitem->cache.k.uid = pw->pw_uid;  
     }  
     pwitem->cache.d.pw = newpw;  
     pwitem->cache.refcnt = 1;  
   
     debug_return_ptr(&pwitem->cache);  
 }  
   
 void  void
pw_addref(struct passwd *pw)sudo_pw_addref(struct passwd *pw)
 {  {
    debug_decl(pw_addref, SUDO_DEBUG_NSS)    debug_decl(sudo_pw_addref, SUDO_DEBUG_NSS)
     ptr_to_item(pw)->refcnt++;      ptr_to_item(pw)->refcnt++;
     debug_return;      debug_return;
 }  }
   
 static void  static void
pw_delref_item(void *v)sudo_pw_delref_item(void *v)
 {  {
     struct cache_item *item = v;      struct cache_item *item = v;
    debug_decl(pw_delref_item, SUDO_DEBUG_NSS)    debug_decl(sudo_pw_delref_item, SUDO_DEBUG_NSS)
   
     if (--item->refcnt == 0)      if (--item->refcnt == 0)
         efree(item);          efree(item);
Line 244  pw_delref_item(void *v) Line 109  pw_delref_item(void *v)
 }  }
   
 void  void
pw_delref(struct passwd *pw)sudo_pw_delref(struct passwd *pw)
 {  {
    debug_decl(pw_delref, SUDO_DEBUG_NSS)    debug_decl(sudo_pw_delref, SUDO_DEBUG_NSS)
    pw_delref_item(ptr_to_item(pw));    sudo_pw_delref_item(ptr_to_item(pw));
     debug_return;      debug_return;
 }  }
   
Line 272  sudo_getpwuid(uid_t uid) Line 137  sudo_getpwuid(uid_t uid)
 #ifdef HAVE_SETAUTHDB  #ifdef HAVE_SETAUTHDB
     aix_setauthdb(IDtouser(uid));      aix_setauthdb(IDtouser(uid));
 #endif  #endif
    if ((key.d.pw = getpwuid(uid)) != NULL) {    item = sudo_make_pwitem(uid, NULL);
        item = make_pwitem(key.d.pw, NULL);    if (item == NULL) {
        if (rbinsert(pwcache_byuid, item) != NULL) 
            errorx(1, _("unable to cache uid %u (%s), already exists"), 
                (unsigned int) uid, item->d.pw->pw_name); 
    } else { 
         item = ecalloc(1, sizeof(*item));          item = ecalloc(1, sizeof(*item));
         item->refcnt = 1;          item->refcnt = 1;
         item->k.uid = uid;          item->k.uid = uid;
         /* item->d.pw = NULL; */          /* item->d.pw = NULL; */
         if (rbinsert(pwcache_byuid, item) != NULL)  
             errorx(1, _("unable to cache uid %u, already exists"),  
                 (unsigned int) uid);  
     }      }
       if (rbinsert(pwcache_byuid, item) != NULL)
           fatalx(U_("unable to cache uid %u, already exists"),
               (unsigned int) uid);
 #ifdef HAVE_SETAUTHDB  #ifdef HAVE_SETAUTHDB
     aix_restoreauthdb();      aix_restoreauthdb();
 #endif  #endif
Line 316  sudo_getpwnam(const char *name) Line 177  sudo_getpwnam(const char *name)
 #ifdef HAVE_SETAUTHDB  #ifdef HAVE_SETAUTHDB
     aix_setauthdb((char *) name);      aix_setauthdb((char *) name);
 #endif  #endif
    if ((key.d.pw = getpwnam(name)) != NULL) {    item = sudo_make_pwitem((uid_t)-1, name);
        item = make_pwitem(key.d.pw, name);    if (item == NULL) {
        if (rbinsert(pwcache_byname, item) != NULL) 
            errorx(1, _("unable to cache user %s, already exists"), name); 
    } else { 
         len = strlen(name) + 1;          len = strlen(name) + 1;
         item = ecalloc(1, sizeof(*item) + len);          item = ecalloc(1, sizeof(*item) + len);
         item->refcnt = 1;          item->refcnt = 1;
         item->k.name = (char *) item + sizeof(*item);          item->k.name = (char *) item + sizeof(*item);
         memcpy(item->k.name, name, len);          memcpy(item->k.name, name, len);
         /* item->d.pw = NULL; */          /* item->d.pw = NULL; */
         if (rbinsert(pwcache_byname, item) != NULL)  
             errorx(1, _("unable to cache user %s, already exists"), name);  
     }      }
       if (rbinsert(pwcache_byname, item) != NULL)
           fatalx(U_("unable to cache user %s, already exists"), name);
 #ifdef HAVE_SETAUTHDB  #ifdef HAVE_SETAUTHDB
     aix_restoreauthdb();      aix_restoreauthdb();
 #endif  #endif
Line 339  done: Line 197  done:
 }  }
   
 /*  /*
 * Take a user, uid and gid and return a faked up passwd struct. * Take a user, uid, gid, home and shell and return a faked up passwd struct.
  * If home or shell are NULL default values will be used.
  */   */
 struct passwd *  struct passwd *
sudo_fakepwnamid(const char *user, uid_t uid, gid_t gid)sudo_mkpwent(const char *user, uid_t uid, gid_t gid, const char *home,
     const char *shell)
 {  {
     struct cache_item_pw *pwitem;      struct cache_item_pw *pwitem;
     struct passwd *pw;      struct passwd *pw;
     struct rbnode *node;      struct rbnode *node;
    size_t len, namelen;    size_t len, name_len, home_len, shell_len;
     int i;      int i;
    debug_decl(sudo_fakepwnam, SUDO_DEBUG_NSS)    debug_decl(sudo_mkpwent, SUDO_DEBUG_NSS)
   
    namelen = strlen(user);    /* Optional arguments. */
    len = sizeof(*pwitem) + namelen + 1 /* pw_name */ +    if (home == NULL)
         home = "/";
     if (shell == NULL)
         shell = _PATH_BSHELL;
 
     name_len = strlen(user);
     home_len = strlen(home);
     shell_len = strlen(shell);
     len = sizeof(*pwitem) + name_len + 1 /* pw_name */ +
         sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +          sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
        sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL);        home_len + 1 /* pw_dir */ + shell_len + 1 /* pw_shell */;
   
     for (i = 0; i < 2; i++) {      for (i = 0; i < 2; i++) {
         pwitem = ecalloc(1, len);          pwitem = ecalloc(1, len);
Line 362  sudo_fakepwnamid(const char *user, uid_t uid, gid_t gi Line 230  sudo_fakepwnamid(const char *user, uid_t uid, gid_t gi
         pw->pw_uid = uid;          pw->pw_uid = uid;
         pw->pw_gid = gid;          pw->pw_gid = gid;
         pw->pw_name = (char *)(pwitem + 1);          pw->pw_name = (char *)(pwitem + 1);
        memcpy(pw->pw_name, user, namelen + 1);        memcpy(pw->pw_name, user, name_len + 1);
        pw->pw_passwd = pw->pw_name + namelen + 1;        pw->pw_passwd = pw->pw_name + name_len + 1;
         memcpy(pw->pw_passwd, "*", 2);          memcpy(pw->pw_passwd, "*", 2);
         pw->pw_gecos = pw->pw_passwd + 2;          pw->pw_gecos = pw->pw_passwd + 2;
         pw->pw_gecos[0] = '\0';          pw->pw_gecos[0] = '\0';
         pw->pw_dir = pw->pw_gecos + 1;          pw->pw_dir = pw->pw_gecos + 1;
        memcpy(pw->pw_dir, "/", 2);        memcpy(pw->pw_dir, home, home_len + 1);
        pw->pw_shell = pw->pw_dir + 2;        pw->pw_shell = pw->pw_dir + home_len + 1;
        memcpy(pw->pw_shell, _PATH_BSHELL, sizeof(_PATH_BSHELL));        memcpy(pw->pw_shell, shell, shell_len + 1);
   
         pwitem->cache.refcnt = 1;          pwitem->cache.refcnt = 1;
         pwitem->cache.d.pw = pw;          pwitem->cache.d.pw = pw;
         if (i == 0) {          if (i == 0) {
            /* Store by uid, overwriting cached version. */            /* Store by uid if it doesn't already exist. */
             pwitem->cache.k.uid = pw->pw_uid;              pwitem->cache.k.uid = pw->pw_uid;
             if ((node = rbinsert(pwcache_byuid, &pwitem->cache)) != NULL) {              if ((node = rbinsert(pwcache_byuid, &pwitem->cache)) != NULL) {
                pw_delref_item(node->data);                /* Already exists, free the item we created. */
                node->data = &pwitem->cache;                efree(pwitem);
                 pwitem = (struct cache_item_pw *) node->data;
             }              }
         } else {          } else {
            /* Store by name, overwriting cached version. */            /* Store by name if it doesn't already exist. */
             pwitem->cache.k.name = pw->pw_name;              pwitem->cache.k.name = pw->pw_name;
             if ((node = rbinsert(pwcache_byname, &pwitem->cache)) != NULL) {              if ((node = rbinsert(pwcache_byname, &pwitem->cache)) != NULL) {
                pw_delref_item(node->data);                /* Already exists, free the item we created. */
                node->data = &pwitem->cache;                efree(pwitem);
                 pwitem = (struct cache_item_pw *) node->data;
             }              }
         }          }
     }      }
     pwitem->cache.refcnt++;      pwitem->cache.refcnt++;
    debug_return_ptr(pw);    debug_return_ptr(&pwitem->pw);
 }  }
   
 /*  /*
Line 400  sudo_fakepwnamid(const char *user, uid_t uid, gid_t gi Line 270  sudo_fakepwnamid(const char *user, uid_t uid, gid_t gi
 struct passwd *  struct passwd *
 sudo_fakepwnam(const char *user, gid_t gid)  sudo_fakepwnam(const char *user, gid_t gid)
 {  {
       const char *errstr;
     uid_t uid;      uid_t uid;
       debug_decl(sudo_fakepwnam, SUDO_DEBUG_NSS)
   
    uid = (uid_t) atoi(user + 1);    uid = (uid_t) atoid(user + 1, NULL, NULL, &errstr);
    return sudo_fakepwnamid(user, uid, gid);    if (errstr != NULL) {
         sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_DIAG,
             "uid %s %s", user, errstr);
         debug_return_ptr(NULL);
     }
     debug_return_ptr(sudo_mkpwent(user, uid, gid, NULL, NULL));
 }  }
   
 void  void
Line 426  sudo_freepwcache(void) Line 303  sudo_freepwcache(void)
     debug_decl(sudo_freepwcache, SUDO_DEBUG_NSS)      debug_decl(sudo_freepwcache, SUDO_DEBUG_NSS)
   
     if (pwcache_byuid != NULL) {      if (pwcache_byuid != NULL) {
        rbdestroy(pwcache_byuid, pw_delref_item);        rbdestroy(pwcache_byuid, sudo_pw_delref_item);
         pwcache_byuid = NULL;          pwcache_byuid = NULL;
     }      }
     if (pwcache_byname != NULL) {      if (pwcache_byname != NULL) {
        rbdestroy(pwcache_byname, pw_delref_item);        rbdestroy(pwcache_byname, sudo_pw_delref_item);
         pwcache_byname = NULL;          pwcache_byname = NULL;
     }      }
   
Line 459  cmp_grgid(const void *v1, const void *v2) Line 336  cmp_grgid(const void *v1, const void *v2)
     return ci1->k.gid - ci2->k.gid;      return ci1->k.gid - ci2->k.gid;
 }  }
   
 /*  
  * Dynamically allocate space for a struct item plus the key and data  
  * elements.  If name is non-NULL it is used as the key, else the  
  * gid is the key.  Fills in datum from struct group.  
  */  
 static struct cache_item *  
 make_gritem(const struct group *gr, const char *name)  
 {  
     char *cp;  
     size_t nsize, psize, nmem, total, len;  
     struct cache_item_gr *gritem;  
     struct group *newgr;  
     debug_decl(make_gritem, SUDO_DEBUG_NSS)  
   
     /* Allocate in one big chunk for easy freeing. */  
     nsize = psize = nmem = 0;  
     total = sizeof(*gritem);  
     FIELD_SIZE(gr, gr_name, nsize);  
     FIELD_SIZE(gr, gr_passwd, psize);  
     if (gr->gr_mem) {  
         for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++)  
             total += strlen(gr->gr_mem[nmem]) + 1;  
         nmem++;  
         total += sizeof(char *) * nmem;  
     }  
     if (name != NULL)  
         total += strlen(name) + 1;  
   
     gritem = ecalloc(1, total);  
   
     /*  
      * Copy in group contents and make strings relative to space  
      * at the end of the buffer.  Note that gr_mem must come  
      * immediately after struct group to guarantee proper alignment.  
      */  
     newgr = &gritem->gr;  
     memcpy(newgr, gr, sizeof(*gr));  
     cp = (char *)(gritem + 1);  
     if (gr->gr_mem) {  
         newgr->gr_mem = (char **)cp;  
         cp += sizeof(char *) * nmem;  
         for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) {  
             len = strlen(gr->gr_mem[nmem]) + 1;  
             memcpy(cp, gr->gr_mem[nmem], len);  
             newgr->gr_mem[nmem] = cp;  
             cp += len;  
         }  
         newgr->gr_mem[nmem] = NULL;  
     }  
     FIELD_COPY(gr, newgr, gr_passwd, psize);  
     FIELD_COPY(gr, newgr, gr_name, nsize);  
   
     /* Set key and datum. */  
     if (name != NULL) {  
         memcpy(cp, name, strlen(name) + 1);  
         gritem->cache.k.name = cp;  
     } else {  
         gritem->cache.k.gid = gr->gr_gid;  
     }  
     gritem->cache.d.gr = newgr;  
     gritem->cache.refcnt = 1;  
   
     debug_return_ptr(&gritem->cache);  
 }  
   
 #ifdef HAVE_UTMPX_H  
 # define GROUPNAME_LEN  (sizeof((struct utmpx *)0)->ut_user + 1)  
 #else  
 # ifdef HAVE_STRUCT_UTMP_UT_USER  
 #  define GROUPNAME_LEN (sizeof((struct utmp *)0)->ut_user + 1)  
 # else  
 #  define GROUPNAME_LEN (sizeof((struct utmp *)0)->ut_name + 1)  
 # endif  
 #endif /* HAVE_UTMPX_H */  
   
 /*  
  * Dynamically allocate space for a struct item plus the key and data  
  * elements.  Fills in datum from the groups and gids arrays.  
  */  
 static struct cache_item *  
 make_grlist_item(const char *user, GETGROUPS_T *gids, int ngids)  
 {  
     char *cp;  
     size_t i, nsize, ngroups, total, len;  
     struct cache_item_grlist *grlitem;  
     struct group_list *grlist;  
     struct group *grp;  
     debug_decl(make_grlist_item, SUDO_DEBUG_NSS)  
   
 #ifdef HAVE_SETAUTHDB  
     aix_setauthdb((char *) user);  
 #endif  
   
     /* Allocate in one big chunk for easy freeing. */  
     nsize = strlen(user) + 1;  
     total = sizeof(*grlitem) + nsize;  
     total += sizeof(char *) * ngids;  
     total += sizeof(gid_t *) * ngids;  
     total += GROUPNAME_LEN * ngids;  
   
 again:  
     grlitem = ecalloc(1, total);  
   
     /*  
      * Copy in group list and make pointers relative to space  
      * at the end of the buffer.  Note that the groups array must come  
      * immediately after struct group to guarantee proper alignment.  
      */  
     grlist = &grlitem->grlist;  
     cp = (char *)(grlitem + 1);  
     grlist->groups = (char **)cp;  
     cp += sizeof(char *) * ngids;  
     grlist->gids = (gid_t *)cp;  
     cp += sizeof(gid_t) * ngids;  
   
     /* Set key and datum. */  
     memcpy(cp, user, nsize);  
     grlitem->cache.k.name = cp;  
     grlitem->cache.d.grlist = grlist;  
     grlitem->cache.refcnt = 1;  
     cp += nsize;  
   
     /*  
      * Store group IDs.  
      */  
     for (i = 0; i < ngids; i++)  
         grlist->gids[i] = gids[i];  
     grlist->ngids = ngids;  
   
     /*  
      * Resolve and store group names by ID.  
      */  
     ngroups = 0;  
     for (i = 0; i < ngids; i++) {  
         if ((grp = sudo_getgrgid(gids[i])) != NULL) {  
             len = strlen(grp->gr_name) + 1;  
             if (cp - (char *)grlitem + len > total) {  
                 total += len + GROUPNAME_LEN;  
                 efree(grlitem);  
                 gr_delref(grp);  
                 goto again;  
             }  
             memcpy(cp, grp->gr_name, len);  
             grlist->groups[ngroups++] = cp;  
             cp += len;  
             gr_delref(grp);  
         }  
     }  
     grlist->ngroups = ngroups;  
   
 #ifdef HAVE_SETAUTHDB  
     aix_restoreauthdb();  
 #endif  
   
     debug_return_ptr(&grlitem->cache);  
 }  
   
 void  void
gr_addref(struct group *gr)sudo_gr_addref(struct group *gr)
 {  {
    debug_decl(gr_addref, SUDO_DEBUG_NSS)    debug_decl(sudo_gr_addref, SUDO_DEBUG_NSS)
     ptr_to_item(gr)->refcnt++;      ptr_to_item(gr)->refcnt++;
     debug_return;      debug_return;
 }  }
   
 static void  static void
gr_delref_item(void *v)sudo_gr_delref_item(void *v)
 {  {
     struct cache_item *item = v;      struct cache_item *item = v;
    debug_decl(gr_delref_item, SUDO_DEBUG_NSS)    debug_decl(sudo_gr_delref_item, SUDO_DEBUG_NSS)
   
     if (--item->refcnt == 0)      if (--item->refcnt == 0)
         efree(item);          efree(item);
Line 637  gr_delref_item(void *v) Line 357  gr_delref_item(void *v)
 }  }
   
 void  void
gr_delref(struct group *gr)sudo_gr_delref(struct group *gr)
 {  {
    debug_decl(gr_delref, SUDO_DEBUG_NSS)    debug_decl(sudo_gr_delref, SUDO_DEBUG_NSS)
    gr_delref_item(ptr_to_item(gr));    sudo_gr_delref_item(ptr_to_item(gr));
     debug_return;      debug_return;
 }  }
   
Line 662  sudo_getgrgid(gid_t gid) Line 382  sudo_getgrgid(gid_t gid)
     /*      /*
      * Cache group db entry if it exists or a negative response if not.       * Cache group db entry if it exists or a negative response if not.
      */       */
    if ((key.d.gr = getgrgid(gid)) != NULL) {    item = sudo_make_gritem(gid, NULL);
        item = make_gritem(key.d.gr, NULL);    if (item == NULL) {
        if (rbinsert(grcache_bygid, item) != NULL) 
            errorx(1, _("unable to cache gid %u (%s), already exists"), 
                (unsigned int) gid, key.d.gr->gr_name); 
    } else { 
         item = ecalloc(1, sizeof(*item));          item = ecalloc(1, sizeof(*item));
         item->refcnt = 1;          item->refcnt = 1;
         item->k.gid = gid;          item->k.gid = gid;
         /* item->d.gr = NULL; */          /* item->d.gr = NULL; */
         if (rbinsert(grcache_bygid, item) != NULL)  
             errorx(1, _("unable to cache gid %u, already exists"),  
                 (unsigned int) gid);  
     }      }
       if (rbinsert(grcache_bygid, item) != NULL)
           fatalx(U_("unable to cache gid %u, already exists"),
               (unsigned int) gid);
 done:  done:
     item->refcnt++;      item->refcnt++;
     debug_return_ptr(item->d.gr);      debug_return_ptr(item->d.gr);
Line 700  sudo_getgrnam(const char *name) Line 416  sudo_getgrnam(const char *name)
     /*      /*
      * Cache group db entry if it exists or a negative response if not.       * Cache group db entry if it exists or a negative response if not.
      */       */
    if ((key.d.gr = getgrnam(name)) != NULL) {    item = sudo_make_gritem((gid_t)-1, name);
        item = make_gritem(key.d.gr, name);    if (item == NULL) {
        if (rbinsert(grcache_byname, item) != NULL) 
            errorx(1, _("unable to cache group %s, already exists"), name); 
    } else { 
         len = strlen(name) + 1;          len = strlen(name) + 1;
         item = ecalloc(1, sizeof(*item) + len);          item = ecalloc(1, sizeof(*item) + len);
         item->refcnt = 1;          item->refcnt = 1;
         item->k.name = (char *) item + sizeof(*item);          item->k.name = (char *) item + sizeof(*item);
         memcpy(item->k.name, name, len);          memcpy(item->k.name, name, len);
         /* item->d.gr = NULL; */          /* item->d.gr = NULL; */
         if (rbinsert(grcache_byname, item) != NULL)  
             errorx(1, _("unable to cache group %s, already exists"), name);  
     }      }
       if (rbinsert(grcache_byname, item) != NULL)
           fatalx(U_("unable to cache group %s, already exists"), name);
 done:  done:
     item->refcnt++;      item->refcnt++;
     debug_return_ptr(item->d.gr);      debug_return_ptr(item->d.gr);
Line 726  struct group * Line 439  struct group *
 sudo_fakegrnam(const char *group)  sudo_fakegrnam(const char *group)
 {  {
     struct cache_item_gr *gritem;      struct cache_item_gr *gritem;
       const char *errstr;
     struct group *gr;      struct group *gr;
     struct rbnode *node;      struct rbnode *node;
    size_t len, namelen;    size_t len, name_len;
     int i;      int i;
     debug_decl(sudo_fakegrnam, SUDO_DEBUG_NSS)      debug_decl(sudo_fakegrnam, SUDO_DEBUG_NSS)
   
    namelen = strlen(group);    name_len = strlen(group);
    len = sizeof(*gritem) + namelen + 1;    len = sizeof(*gritem) + name_len + 1;
   
     for (i = 0; i < 2; i++) {      for (i = 0; i < 2; i++) {
         gritem = ecalloc(1, len);          gritem = ecalloc(1, len);
         gr = &gritem->gr;          gr = &gritem->gr;
        gr->gr_gid = (gid_t) atoi(group + 1);        gr->gr_gid = (gid_t) atoid(group + 1, NULL, NULL, &errstr);
         gr->gr_name = (char *)(gritem + 1);          gr->gr_name = (char *)(gritem + 1);
        memcpy(gr->gr_name, group, namelen + 1);        memcpy(gr->gr_name, group, name_len + 1);
         if (errstr != NULL) {
             sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_DIAG,
                 "gid %s %s", group, errstr);
             efree(gritem);
             debug_return_ptr(NULL);
         }
   
         gritem->cache.refcnt = 1;          gritem->cache.refcnt = 1;
         gritem->cache.d.gr = gr;          gritem->cache.d.gr = gr;
         if (i == 0) {          if (i == 0) {
            /* Store by gid, overwriting cached version. */            /* Store by gid if it doesn't already exist. */
             gritem->cache.k.gid = gr->gr_gid;              gritem->cache.k.gid = gr->gr_gid;
             if ((node = rbinsert(grcache_bygid, &gritem->cache)) != NULL) {              if ((node = rbinsert(grcache_bygid, &gritem->cache)) != NULL) {
                gr_delref_item(node->data);                /* Already exists, free the item we created. */
                node->data = &gritem->cache;                efree(gritem);
                 gritem = (struct cache_item_gr *) node->data;
             }              }
         } else {          } else {
             /* Store by name, overwriting cached version. */              /* Store by name, overwriting cached version. */
             gritem->cache.k.name = gr->gr_name;              gritem->cache.k.name = gr->gr_name;
             if ((node = rbinsert(grcache_byname, &gritem->cache)) != NULL) {              if ((node = rbinsert(grcache_byname, &gritem->cache)) != NULL) {
                gr_delref_item(node->data);                /* Already exists, free the item we created. */
                node->data = &gritem->cache;                efree(gritem);
                 gritem = (struct cache_item_gr *) node->data;
             }              }
         }          }
     }      }
     gritem->cache.refcnt++;      gritem->cache.refcnt++;
    debug_return_ptr(gr);    debug_return_ptr(&gritem->gr);
 }  }
   
 void  void
grlist_addref(struct group_list *grlist)sudo_grlist_addref(struct group_list *grlist)
 {  {
    debug_decl(gr_addref, SUDO_DEBUG_NSS)    debug_decl(sudo_gr_addref, SUDO_DEBUG_NSS)
     ptr_to_item(grlist)->refcnt++;      ptr_to_item(grlist)->refcnt++;
     debug_return;      debug_return;
 }  }
   
 static void  static void
grlist_delref_item(void *v)sudo_grlist_delref_item(void *v)
 {  {
     struct cache_item *item = v;      struct cache_item *item = v;
    debug_decl(gr_delref_item, SUDO_DEBUG_NSS)    debug_decl(sudo_gr_delref_item, SUDO_DEBUG_NSS)
   
     if (--item->refcnt == 0)      if (--item->refcnt == 0)
         efree(item);          efree(item);
Line 785  grlist_delref_item(void *v) Line 507  grlist_delref_item(void *v)
 }  }
   
 void  void
grlist_delref(struct group_list *grlist)sudo_grlist_delref(struct group_list *grlist)
 {  {
    debug_decl(gr_delref, SUDO_DEBUG_NSS)    debug_decl(sudo_gr_delref, SUDO_DEBUG_NSS)
    grlist_delref_item(ptr_to_item(grlist));    sudo_grlist_delref_item(ptr_to_item(grlist));
     debug_return;      debug_return;
 }  }
   
Line 814  sudo_freegrcache(void) Line 536  sudo_freegrcache(void)
     debug_decl(sudo_freegrcache, SUDO_DEBUG_NSS)      debug_decl(sudo_freegrcache, SUDO_DEBUG_NSS)
   
     if (grcache_bygid != NULL) {      if (grcache_bygid != NULL) {
        rbdestroy(grcache_bygid, gr_delref_item);        rbdestroy(grcache_bygid, sudo_gr_delref_item);
         grcache_bygid = NULL;          grcache_bygid = NULL;
     }      }
     if (grcache_byname != NULL) {      if (grcache_byname != NULL) {
        rbdestroy(grcache_byname, gr_delref_item);        rbdestroy(grcache_byname, sudo_gr_delref_item);
         grcache_byname = NULL;          grcache_byname = NULL;
     }      }
     if (grlist_cache != NULL) {      if (grlist_cache != NULL) {
        rbdestroy(grlist_cache, grlist_delref_item);        rbdestroy(grlist_cache, sudo_grlist_delref_item);
         grlist_cache = NULL;          grlist_cache = NULL;
     }      }
   
Line 841  sudo_endgrent(void) Line 563  sudo_endgrent(void)
 }  }
   
 struct group_list *  struct group_list *
get_group_list(struct passwd *pw)sudo_get_grlist(const struct passwd *pw)
 {  {
     struct cache_item key, *item;      struct cache_item key, *item;
     struct rbnode *node;      struct rbnode *node;
     size_t len;      size_t len;
    GETGROUPS_T *gids;    debug_decl(sudo_get_grlist, SUDO_DEBUG_NSS)
    int ngids; 
    debug_decl(get_group_list, SUDO_DEBUG_NSS) 
   
     key.k.name = pw->pw_name;      key.k.name = pw->pw_name;
     if ((node = rbfind(grlist_cache, &key)) != NULL) {      if ((node = rbfind(grlist_cache, &key)) != NULL) {
Line 858  get_group_list(struct passwd *pw) Line 578  get_group_list(struct passwd *pw)
     /*      /*
      * Cache group db entry if it exists or a negative response if not.       * Cache group db entry if it exists or a negative response if not.
      */       */
#if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)    item = sudo_make_grlist_item(pw, NULL, NULL);
    ngids = (int)sysconf(_SC_NGROUPS_MAX) * 2;    if (item == NULL) {
    if (ngids < 0) 
#endif 
        ngids = NGROUPS_MAX * 2; 
    gids = emalloc2(ngids, sizeof(GETGROUPS_T)); 
    if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) { 
        efree(gids); 
        gids = emalloc2(ngids, sizeof(GETGROUPS_T)); 
        if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) { 
            efree(gids); 
            debug_return_ptr(NULL); 
        } 
    } 
    if (ngids > 0) { 
        if ((item = make_grlist_item(pw->pw_name, gids, ngids)) == NULL) 
            errorx(1, "unable to parse group list for %s", pw->pw_name); 
        efree(gids); 
        if (rbinsert(grlist_cache, item) != NULL) 
            errorx(1, "unable to cache group list for %s, already exists", 
                pw->pw_name); 
    } else { 
         /* Should not happen. */          /* Should not happen. */
         len = strlen(pw->pw_name) + 1;          len = strlen(pw->pw_name) + 1;
         item = ecalloc(1, sizeof(*item) + len);          item = ecalloc(1, sizeof(*item) + len);
Line 887  get_group_list(struct passwd *pw) Line 587  get_group_list(struct passwd *pw)
         item->k.name = (char *) item + sizeof(*item);          item->k.name = (char *) item + sizeof(*item);
         memcpy(item->k.name, pw->pw_name, len);          memcpy(item->k.name, pw->pw_name, len);
         /* item->d.grlist = NULL; */          /* item->d.grlist = NULL; */
         if (rbinsert(grlist_cache, item) != NULL)  
             errorx(1, "unable to cache group list for %s, already exists",  
                 pw->pw_name);  
     }      }
       if (rbinsert(grlist_cache, item) != NULL)
           fatalx(U_("unable to cache group list for %s, already exists"),
               pw->pw_name);
 done:  done:
     item->refcnt++;      item->refcnt++;
     debug_return_ptr(item->d.grlist);      debug_return_ptr(item->d.grlist);
 }  }
   
 void  void
set_group_list(const char *user, GETGROUPS_T *gids, int ngids)sudo_set_grlist(struct passwd *pw, char * const *groups, char * const *gids)
 {  {
     struct cache_item key, *item;      struct cache_item key, *item;
     struct rbnode *node;      struct rbnode *node;
    debug_decl(set_group_list, SUDO_DEBUG_NSS)    debug_decl(sudo_set_grlist, SUDO_DEBUG_NSS)
   
     /*      /*
      * Cache group db entry if it doesn't already exist       * Cache group db entry if it doesn't already exist
      */       */
    key.k.name = (char *) user;    key.k.name = pw->pw_name;
     if ((node = rbfind(grlist_cache, &key)) == NULL) {      if ((node = rbfind(grlist_cache, &key)) == NULL) {
        if ((item = make_grlist_item(user, gids, ngids)) == NULL)        if ((item = sudo_make_grlist_item(pw, groups, gids)) == NULL)
            errorx(1, "unable to parse group list for %s", user);            fatalx(U_("unable to parse groups for %s"), pw->pw_name);
         if (rbinsert(grlist_cache, item) != NULL)          if (rbinsert(grlist_cache, item) != NULL)
            errorx(1, "unable to cache group list for %s, already exists",            fatalx(U_("unable to cache group list for %s, already exists"),
                user);                pw->pw_name);
     }      }
     debug_return;      debug_return;
 }  }
   
 bool  bool
user_in_group(struct passwd *pw, const char *group)user_in_group(const struct passwd *pw, const char *group)
 {  {
     struct group_list *grlist;      struct group_list *grlist;
     struct group *grp = NULL;      struct group *grp = NULL;
       const char *errstr;
     int i;      int i;
     bool matched = false;      bool matched = false;
     debug_decl(user_in_group, SUDO_DEBUG_NSS)      debug_decl(user_in_group, SUDO_DEBUG_NSS)
   
    if ((grlist = get_group_list(pw)) != NULL) {    if ((grlist = sudo_get_grlist(pw)) != NULL) {
         /*          /*
          * If it could be a sudo-style group ID check gids first.           * If it could be a sudo-style group ID check gids first.
          */           */
         if (group[0] == '#') {          if (group[0] == '#') {
            gid_t gid = atoi(group + 1);            gid_t gid = (gid_t) atoid(group + 1, NULL, NULL, &errstr);
            if (gid == pw->pw_gid) {            if (errstr != NULL) {
                matched = true;                sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_DIAG,
                goto done;                    "gid %s %s", group, errstr);
            }            } else {
            for (i = 0; i < grlist->ngids; i++) {                if (gid == pw->pw_gid) {
                if (gid == grlist->gids[i]) { 
                     matched = true;                      matched = true;
                     goto done;                      goto done;
                 }                  }
                   for (i = 0; i < grlist->ngids; i++) {
                       if (gid == grlist->gids[i]) {
                           matched = true;
                           goto done;
                       }
                   }
             }              }
         }          }
   
Line 964  user_in_group(struct passwd *pw, const char *group) Line 670  user_in_group(struct passwd *pw, const char *group)
         }          }
 done:  done:
         if (grp != NULL)          if (grp != NULL)
            gr_delref(grp);            sudo_gr_delref(grp);
        grlist_delref(grlist);        sudo_grlist_delref(grlist);
     }      }
     debug_return_bool(matched);      debug_return_bool(matched);
 }  }

Removed from v.1.1.1.2  
changed lines
  Added in v.1.1.1.5


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>