Diff for /embedaddon/sudo/plugins/sudoers/pwutil.c between versions 1.1 and 1.1.1.4

version 1.1, 2012/02/21 16:23:02 version 1.1.1.4, 2013/07/22 10:46:12
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) - sizeof(struct cache_item)))  
   
 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;  
 };  
   
 /*  /*
  * Compare by uid.   * Compare by uid.
  */   */
Line 113  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.  
  *  
  * We would like to fill in the encrypted password too but the  
  * call to the shadow function could overwrite the pw buffer (NIS).  
  */  
 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 *item;  
     struct passwd *newpw;  
   
     /* 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(struct cache_item) + sizeof(struct passwd);  
     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. */  
     item = emalloc(total);  
     cp = (char *) item + sizeof(struct cache_item);  
   
     /*  
      * Copy in passwd contents and make strings relative to space  
      * at the end of the buffer.  
      */  
     newpw = (struct passwd *) cp;  
     memcpy(newpw, pw, sizeof(struct passwd));  
     cp += sizeof(struct passwd);  
     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);  
         item->k.name = cp;  
     } else {  
         item->k.uid = pw->pw_uid;  
     }  
     item->d.pw = newpw;  
     item->refcnt = 1;  
   
     return item;  
 }  
   
 void  void
pw_addref(struct passwd *pw)sudo_pw_addref(struct passwd *pw)
 {  {
       debug_decl(sudo_pw_addref, SUDO_DEBUG_NSS)
     ptr_to_item(pw)->refcnt++;      ptr_to_item(pw)->refcnt++;
       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(sudo_pw_delref_item, SUDO_DEBUG_NSS)
   
     if (--item->refcnt == 0)      if (--item->refcnt == 0)
         efree(item);          efree(item);
   
       debug_return;
 }  }
   
 void  void
pw_delref(struct passwd *pw)sudo_pw_delref(struct passwd *pw)
 {  {
    pw_delref_item(ptr_to_item(pw));    debug_decl(sudo_pw_delref, SUDO_DEBUG_NSS)
     sudo_pw_delref_item(ptr_to_item(pw));
     debug_return;
 }  }
   
 /*  /*
  * Get a password entry by uid and allocate space for it.   * Get a password entry by uid and allocate space for it.
  * Fills in pw_passwd from shadow file if necessary.  
  */   */
 struct passwd *  struct passwd *
 sudo_getpwuid(uid_t uid)  sudo_getpwuid(uid_t uid)
 {  {
     struct cache_item key, *item;      struct cache_item key, *item;
     struct rbnode *node;      struct rbnode *node;
       debug_decl(sudo_getpwuid, SUDO_DEBUG_NSS)
   
     key.k.uid = uid;      key.k.uid = uid;
     if ((node = rbfind(pwcache_byuid, &key)) != NULL) {      if ((node = rbfind(pwcache_byuid, &key)) != NULL) {
Line 245  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)        item = ecalloc(1, sizeof(*item));
            errorx(1, _("unable to cache uid %u (%s), already exists"), 
                (unsigned int) uid, item->d.pw->pw_name); 
    } else { 
        item = emalloc(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(_("unable to cache uid %u, already exists"),
               (unsigned int) uid);
 #ifdef HAVE_SETAUTHDB  #ifdef HAVE_SETAUTHDB
     aix_restoreauthdb();      aix_restoreauthdb();
 #endif  #endif
 done:  done:
     item->refcnt++;      item->refcnt++;
    return item->d.pw;    debug_return_ptr(item->d.pw);
 }  }
   
 /*  /*
  * Get a password entry by name and allocate space for it.   * Get a password entry by name and allocate space for it.
  * Fills in pw_passwd from shadow file if necessary.  
  */   */
 struct passwd *  struct passwd *
 sudo_getpwnam(const char *name)  sudo_getpwnam(const char *name)
Line 277  sudo_getpwnam(const char *name) Line 164  sudo_getpwnam(const char *name)
     struct cache_item key, *item;      struct cache_item key, *item;
     struct rbnode *node;      struct rbnode *node;
     size_t len;      size_t len;
       debug_decl(sudo_getpwnam, SUDO_DEBUG_NSS)
   
     key.k.name = (char *) name;      key.k.name = (char *) name;
     if ((node = rbfind(pwcache_byname, &key)) != NULL) {      if ((node = rbfind(pwcache_byname, &key)) != NULL) {
Line 289  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 = emalloc(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(_("unable to cache user %s, already exists"), name);
 #ifdef HAVE_SETAUTHDB  #ifdef HAVE_SETAUTHDB
     aix_restoreauthdb();      aix_restoreauthdb();
 #endif  #endif
 done:  done:
     item->refcnt++;      item->refcnt++;
    return item->d.pw;    debug_return_ptr(item->d.pw);
 }  }
   
 /*  /*
 * 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 *item;    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_mkpwent, SUDO_DEBUG_NSS)
   
    namelen = strlen(user);    /* Optional arguments. */
    len = sizeof(*item) + sizeof(*pw) + 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++) {
        item = emalloc(len);        pwitem = ecalloc(1, len);
        zero_bytes(item, sizeof(*item) + sizeof(*pw));        pw = &pwitem->pw;
        pw = (struct passwd *) ((char *)item + sizeof(*item)); 
         pw->pw_uid = uid;          pw->pw_uid = uid;
         pw->pw_gid = gid;          pw->pw_gid = gid;
        pw->pw_name = (char *)pw + sizeof(struct passwd);        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);
   
        item->refcnt = 1;        pwitem->cache.refcnt = 1;
        item->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. */
            item->k.uid = pw->pw_uid;            pwitem->cache.k.uid = pw->pw_uid;
            if ((node = rbinsert(pwcache_byuid, item)) != NULL) {            if ((node = rbinsert(pwcache_byuid, &pwitem->cache)) != NULL) {
                pw_delref_item(node->data);                /* Already exists, free the item we created. */
                node->data = item;                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. */
            item->k.name = pw->pw_name;            pwitem->cache.k.name = pw->pw_name;
            if ((node = rbinsert(pwcache_byname, item)) != NULL) {            if ((node = rbinsert(pwcache_byname, &pwitem->cache)) != NULL) {
                pw_delref_item(node->data);                /* Already exists, free the item we created. */
                node->data = item;                efree(pwitem);
                 pwitem = (struct cache_item_pw *) node->data;
             }              }
         }          }
     }      }
    item->refcnt++;    pwitem->cache.refcnt++;
    return pw;    debug_return_ptr(&pwitem->pw);
 }  }
   
 /*  /*
Line 376  sudo_fakepwnam(const char *user, gid_t gid) Line 273  sudo_fakepwnam(const char *user, gid_t gid)
     uid_t uid;      uid_t uid;
   
     uid = (uid_t) atoi(user + 1);      uid = (uid_t) atoi(user + 1);
    return sudo_fakepwnamid(user, uid, gid);    return sudo_mkpwent(user, uid, gid, NULL, NULL);
 }  }
   
 void  void
 sudo_setpwent(void)  sudo_setpwent(void)
 {  {
       debug_decl(sudo_setpwent, SUDO_DEBUG_NSS)
   
     setpwent();      setpwent();
     if (pwcache_byuid == NULL)      if (pwcache_byuid == NULL)
         pwcache_byuid = rbcreate(cmp_pwuid);          pwcache_byuid = rbcreate(cmp_pwuid);
     if (pwcache_byname == NULL)      if (pwcache_byname == NULL)
         pwcache_byname = rbcreate(cmp_pwnam);          pwcache_byname = rbcreate(cmp_pwnam);
   
       debug_return;
 }  }
   
 void  void
 sudo_freepwcache(void)  sudo_freepwcache(void)
 {  {
       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;
     }      }
   
       debug_return;
 }  }
   
 void  void
 sudo_endpwent(void)  sudo_endpwent(void)
 {  {
       debug_decl(sudo_endpwent, SUDO_DEBUG_NSS)
   
     endpwent();      endpwent();
     sudo_freepwcache();      sudo_freepwcache();
   
       debug_return;
 }  }
   
 /*  /*
Line 420  cmp_grgid(const void *v1, const void *v2) Line 329  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 *item;  
     struct group *newgr;  
   
     /* Allocate in one big chunk for easy freeing. */  
     nsize = psize = nmem = 0;  
     total = sizeof(struct cache_item) + sizeof(struct group);  
     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;  
   
     item = emalloc(total);  
     cp = (char *) item + sizeof(struct cache_item);  
   
     /*  
      * 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 = (struct group *)cp;  
     memcpy(newgr, gr, sizeof(struct group));  
     cp += sizeof(struct group);  
     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);  
         item->k.name = cp;  
     } else {  
         item->k.gid = gr->gr_gid;  
     }  
     item->d.gr = newgr;  
     item->refcnt = 1;  
   
     return item;  
 }  
   
 #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 *item;  
     struct group_list *grlist;  
     struct group *grp;  
   
 #ifdef HAVE_SETAUTHDB  
     aix_setauthdb((char *) user);  
 #endif  
   
     /* Allocate in one big chunk for easy freeing. */  
     nsize = strlen(user) + 1;  
     total = sizeof(struct cache_item) + sizeof(struct group_list) + nsize;  
     total += sizeof(char *) * ngids;  
     total += sizeof(gid_t *) * ngids;  
     total += GROUPNAME_LEN * ngids;  
   
 again:  
     item = emalloc(total);  
     cp = (char *) item + sizeof(struct cache_item);  
   
     /*  
      * 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 = (struct group_list *)cp;  
     zero_bytes(grlist, sizeof(struct group_list));  
     cp += sizeof(struct group_list);  
     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);  
     item->k.name = cp;  
     item->d.grlist = grlist;  
     item->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 *)item + len > total) {  
                 total += len + GROUPNAME_LEN;  
                 efree(item);  
                 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  
   
     return item;  
 }  
   
 void  void
gr_addref(struct group *gr)sudo_gr_addref(struct group *gr)
 {  {
       debug_decl(sudo_gr_addref, SUDO_DEBUG_NSS)
     ptr_to_item(gr)->refcnt++;      ptr_to_item(gr)->refcnt++;
       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(sudo_gr_delref_item, SUDO_DEBUG_NSS)
   
     if (--item->refcnt == 0)      if (--item->refcnt == 0)
         efree(item);          efree(item);
   
       debug_return;
 }  }
   
 void  void
gr_delref(struct group *gr)sudo_gr_delref(struct group *gr)
 {  {
    gr_delref_item(ptr_to_item(gr));    debug_decl(sudo_gr_delref, SUDO_DEBUG_NSS)
     sudo_gr_delref_item(ptr_to_item(gr));
     debug_return;
 }  }
   
 /*  /*
Line 607  sudo_getgrgid(gid_t gid) Line 365  sudo_getgrgid(gid_t gid)
 {  {
     struct cache_item key, *item;      struct cache_item key, *item;
     struct rbnode *node;      struct rbnode *node;
       debug_decl(sudo_getgrgid, SUDO_DEBUG_NSS)
   
     key.k.gid = gid;      key.k.gid = gid;
     if ((node = rbfind(grcache_bygid, &key)) != NULL) {      if ((node = rbfind(grcache_bygid, &key)) != NULL) {
Line 616  sudo_getgrgid(gid_t gid) Line 375  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)        item = ecalloc(1, sizeof(*item));
            errorx(1, _("unable to cache gid %u (%s), already exists"), 
                (unsigned int) gid, key.d.gr->gr_name); 
    } else { 
        item = emalloc(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(_("unable to cache gid %u, already exists"),
               (unsigned int) gid);
 done:  done:
     item->refcnt++;      item->refcnt++;
    return item->d.gr;    debug_return_ptr(item->d.gr);
 }  }
   
 /*  /*
Line 644  sudo_getgrnam(const char *name) Line 399  sudo_getgrnam(const char *name)
     struct cache_item key, *item;      struct cache_item key, *item;
     struct rbnode *node;      struct rbnode *node;
     size_t len;      size_t len;
       debug_decl(sudo_getgrnam, SUDO_DEBUG_NSS)
   
     key.k.name = (char *) name;      key.k.name = (char *) name;
     if ((node = rbfind(grcache_byname, &key)) != NULL) {      if ((node = rbfind(grcache_byname, &key)) != NULL) {
Line 653  sudo_getgrnam(const char *name) Line 409  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 = emalloc(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(_("unable to cache group %s, already exists"), name);
 done:  done:
     item->refcnt++;      item->refcnt++;
    return item->d.gr;    debug_return_ptr(item->d.gr);
 }  }
   
 /*  /*
Line 678  done: Line 431  done:
 struct group *  struct group *
 sudo_fakegrnam(const char *group)  sudo_fakegrnam(const char *group)
 {  {
    struct cache_item *item;    struct cache_item_gr *gritem;
     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)
   
    namelen = strlen(group);    name_len = strlen(group);
    len = sizeof(*item) + sizeof(*gr) + namelen + 1;    len = sizeof(*gritem) + name_len + 1;
   
     for (i = 0; i < 2; i++) {      for (i = 0; i < 2; i++) {
        item = emalloc(len);        gritem = ecalloc(1, len);
        zero_bytes(item, sizeof(*item) + sizeof(*gr));        gr = &gritem->gr;
        gr = (struct group *) ((char *)item + sizeof(*item)); 
         gr->gr_gid = (gid_t) atoi(group + 1);          gr->gr_gid = (gid_t) atoi(group + 1);
        gr->gr_name = (char *)gr + sizeof(struct group);        gr->gr_name = (char *)(gritem + 1);
        memcpy(gr->gr_name, group, namelen + 1);        memcpy(gr->gr_name, group, name_len + 1);
   
        item->refcnt = 1;        gritem->cache.refcnt = 1;
        item->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. */
            item->k.gid = gr->gr_gid;            gritem->cache.k.gid = gr->gr_gid;
            if ((node = rbinsert(grcache_bygid, item)) != NULL) {            if ((node = rbinsert(grcache_bygid, &gritem->cache)) != NULL) {
                gr_delref_item(node->data);                /* Already exists, free the item we created. */
                node->data = item;                efree(gritem);
                 gritem = (struct cache_item_gr *) node->data;
             }              }
         } else {          } else {
             /* Store by name, overwriting cached version. */              /* Store by name, overwriting cached version. */
            item->k.name = gr->gr_name;            gritem->cache.k.name = gr->gr_name;
            if ((node = rbinsert(grcache_byname, item)) != NULL) {            if ((node = rbinsert(grcache_byname, &gritem->cache)) != NULL) {
                gr_delref_item(node->data);                /* Already exists, free the item we created. */
                node->data = item;                efree(gritem);
                 gritem = (struct cache_item_gr *) node->data;
             }              }
         }          }
     }      }
    item->refcnt++;    gritem->cache.refcnt++;
    return gr;    debug_return_ptr(&gritem->gr);
 }  }
   
 void  void
grlist_addref(struct group_list *grlist)sudo_grlist_addref(struct group_list *grlist)
 {  {
       debug_decl(sudo_gr_addref, SUDO_DEBUG_NSS)
     ptr_to_item(grlist)->refcnt++;      ptr_to_item(grlist)->refcnt++;
       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(sudo_gr_delref_item, SUDO_DEBUG_NSS)
   
     if (--item->refcnt == 0)      if (--item->refcnt == 0)
         efree(item);          efree(item);
   
       debug_return;
 }  }
   
 void  void
grlist_delref(struct group_list *grlist)sudo_grlist_delref(struct group_list *grlist)
 {  {
    grlist_delref_item(ptr_to_item(grlist));    debug_decl(sudo_gr_delref, SUDO_DEBUG_NSS)
     sudo_grlist_delref_item(ptr_to_item(grlist));
     debug_return;
 }  }
   
 void  void
 sudo_setgrent(void)  sudo_setgrent(void)
 {  {
       debug_decl(sudo_setgrent, SUDO_DEBUG_NSS)
   
     setgrent();      setgrent();
     if (grcache_bygid == NULL)      if (grcache_bygid == NULL)
         grcache_bygid = rbcreate(cmp_grgid);          grcache_bygid = rbcreate(cmp_grgid);
Line 748  sudo_setgrent(void) Line 512  sudo_setgrent(void)
         grcache_byname = rbcreate(cmp_grnam);          grcache_byname = rbcreate(cmp_grnam);
     if (grlist_cache == NULL)      if (grlist_cache == NULL)
         grlist_cache = rbcreate(cmp_grnam);          grlist_cache = rbcreate(cmp_grnam);
   
       debug_return;
 }  }
   
 void  void
 sudo_freegrcache(void)  sudo_freegrcache(void)
 {  {
       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;
     }      }
   
       debug_return;
 }  }
   
 void  void
 sudo_endgrent(void)  sudo_endgrent(void)
 {  {
       debug_decl(sudo_endgrent, SUDO_DEBUG_NSS)
   
     endgrent();      endgrent();
     sudo_freegrcache();      sudo_freegrcache();
   
       debug_return;
 }  }
   
 struct group_list *  struct group_list *
get_group_list(struct passwd *pw)sudo_get_grlist(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; 
   
     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 791  get_group_list(struct passwd *pw) Line 564  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); 
            return 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 = emalloc(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, 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(_("unable to cache group list for %s, already exists"),
               pw->pw_name);
 done:  done:
     item->refcnt++;      item->refcnt++;
    return 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(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(_("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(_("unable to cache group list for %s, already exists"),
                user);                pw->pw_name);
     }      }
       debug_return;
 }  }
   
intbool
 user_in_group(struct passwd *pw, const char *group)  user_in_group(struct passwd *pw, const char *group)
 {  {
     struct group_list *grlist;      struct group_list *grlist;
     struct group *grp = NULL;      struct group *grp = NULL;
    int i, matched = FALSE;    int i;
     bool matched = false;
     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 = atoi(group + 1);
             if (gid == pw->pw_gid) {              if (gid == pw->pw_gid) {
                matched = TRUE;                matched = true;
                 goto done;                  goto done;
             }              }
             for (i = 0; i < grlist->ngids; i++) {              for (i = 0; i < grlist->ngids; i++) {
                 if (gid == grlist->gids[i]) {                  if (gid == grlist->gids[i]) {
                    matched = TRUE;                    matched = true;
                     goto done;                      goto done;
                 }                  }
             }              }
Line 879  user_in_group(struct passwd *pw, const char *group) Line 636  user_in_group(struct passwd *pw, const char *group)
          */           */
         for (i = 0; i < grlist->ngroups; i++) {          for (i = 0; i < grlist->ngroups; i++) {
             if (strcasecmp(group, grlist->groups[i]) == 0) {              if (strcasecmp(group, grlist->groups[i]) == 0) {
                matched = TRUE;                matched = true;
                 goto done;                  goto done;
             }              }
         }          }
Line 887  user_in_group(struct passwd *pw, const char *group) Line 644  user_in_group(struct passwd *pw, const char *group)
         /* Finally check against user's primary (passwd file) group. */          /* Finally check against user's primary (passwd file) group. */
         if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {          if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
             if (strcasecmp(group, grp->gr_name) == 0) {              if (strcasecmp(group, grp->gr_name) == 0) {
                matched = TRUE;                matched = true;
                 goto done;                  goto done;
             }              }
         }          }
 done:  done:
         if (grp != NULL)          if (grp != NULL)
            gr_delref(grp);            sudo_gr_delref(grp);
        grlist_delref(grlist);        sudo_grlist_delref(grlist);
     }      }
    return matched;    debug_return_bool(matched);
 }  }

Removed from v.1.1  
changed lines
  Added in v.1.1.1.4


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