Imported Upstream version 1.8.5
[debian/sudo] / plugins / sudoers / pwutil.c
index c1a80dc344f864ab3255592e808363cf7306ff4a..e18c6b0f777ac81713a874134910aeb50e435c78 100644 (file)
@@ -73,8 +73,11 @@ 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)))
+#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 */
@@ -91,6 +94,26 @@ struct cache_item {
     } 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.
  */
@@ -134,9 +157,6 @@ do {                                                        \
  * 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)
@@ -144,7 +164,7 @@ 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 cache_item_pw *pwitem;
     struct passwd *newpw;
     debug_decl(make_pwitem, SUDO_DEBUG_NSS)
 
@@ -154,7 +174,7 @@ make_pwitem(const struct passwd *pw, const char *name)
 
     /* Allocate in one big chunk for easy freeing. */
     nsize = psize = csize = gsize = dsize = ssize = 0;
-    total = sizeof(struct cache_item) + sizeof(struct passwd);
+    total = sizeof(*pwitem);
     FIELD_SIZE(pw, pw_name, nsize);
     FIELD_SIZE(pw, pw_passwd, psize);
 #ifdef HAVE_LOGIN_CAP_H
@@ -169,16 +189,15 @@ make_pwitem(const struct passwd *pw, const char *name)
        total += strlen(name) + 1;
 
     /* Allocate space for struct item, struct passwd and the strings. */
-    item = emalloc(total);
-    cp = (char *) item + sizeof(struct cache_item);
+    pwitem = ecalloc(1, total);
+    newpw = &pwitem->pw;
 
     /*
      * Copy in passwd contents and make strings relative to space
-     * at the end of the buffer.
+     * at the end of the struct.
      */
-    newpw = (struct passwd *) cp;
-    memcpy(newpw, pw, sizeof(struct passwd));
-    cp += sizeof(struct passwd);
+    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
@@ -194,14 +213,14 @@ make_pwitem(const struct passwd *pw, const char *name)
     /* Set key and datum. */
     if (name != NULL) {
        memcpy(cp, name, strlen(name) + 1);
-       item->k.name = cp;
+       pwitem->cache.k.name = cp;
     } else {
-       item->k.uid = pw->pw_uid;
+       pwitem->cache.k.uid = pw->pw_uid;
     }
-    item->d.pw = newpw;
-    item->refcnt = 1;
+    pwitem->cache.d.pw = newpw;
+    pwitem->cache.refcnt = 1;
 
-    debug_return_ptr(item);
+    debug_return_ptr(&pwitem->cache);
 }
 
 void
@@ -234,7 +253,6 @@ pw_delref(struct passwd *pw)
 
 /*
  * Get a password entry by uid and allocate space for it.
- * Fills in pw_passwd from shadow file if necessary.
  */
 struct passwd *
 sudo_getpwuid(uid_t uid)
@@ -260,10 +278,10 @@ sudo_getpwuid(uid_t uid)
            errorx(1, _("unable to cache uid %u (%s), already exists"),
                (unsigned int) uid, item->d.pw->pw_name);
     } else {
-       item = emalloc(sizeof(*item));
+       item = ecalloc(1, sizeof(*item));
        item->refcnt = 1;
        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);
@@ -278,7 +296,6 @@ done:
 
 /*
  * Get a password entry by name and allocate space for it.
- * Fills in pw_passwd from shadow file if necessary.
  */
 struct passwd *
 sudo_getpwnam(const char *name)
@@ -305,11 +322,11 @@ sudo_getpwnam(const char *name)
            errorx(1, _("unable to cache user %s, already exists"), name);
     } else {
        len = strlen(name) + 1;
-       item = emalloc(sizeof(*item) + len);
+       item = ecalloc(1, sizeof(*item) + len);
        item->refcnt = 1;
        item->k.name = (char *) item + sizeof(*item);
        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);
     }
@@ -327,7 +344,7 @@ done:
 struct passwd *
 sudo_fakepwnamid(const char *user, uid_t uid, gid_t gid)
 {
-    struct cache_item *item;
+    struct cache_item_pw *pwitem;
     struct passwd *pw;
     struct rbnode *node;
     size_t len, namelen;
@@ -335,17 +352,16 @@ sudo_fakepwnamid(const char *user, uid_t uid, gid_t gid)
     debug_decl(sudo_fakepwnam, SUDO_DEBUG_NSS)
 
     namelen = strlen(user);
-    len = sizeof(*item) + sizeof(*pw) + namelen + 1 /* pw_name */ +
+    len = sizeof(*pwitem) + namelen + 1 /* pw_name */ +
        sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
        sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL);
 
     for (i = 0; i < 2; i++) {
-       item = emalloc(len);
-       zero_bytes(item, sizeof(*item) + sizeof(*pw));
-       pw = (struct passwd *) ((char *)item + sizeof(*item));
+       pwitem = ecalloc(1, len);
+       pw = &pwitem->pw;
        pw->pw_uid = uid;
        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);
        pw->pw_passwd = pw->pw_name + namelen + 1;
        memcpy(pw->pw_passwd, "*", 2);
@@ -356,25 +372,25 @@ sudo_fakepwnamid(const char *user, uid_t uid, 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;
+       pwitem->cache.refcnt = 1;
+       pwitem->cache.d.pw = pw;
        if (i == 0) {
            /* Store by uid, overwriting cached version. */
-           item->k.uid = pw->pw_uid;
-           if ((node = rbinsert(pwcache_byuid, item)) != NULL) {
+           pwitem->cache.k.uid = pw->pw_uid;
+           if ((node = rbinsert(pwcache_byuid, &pwitem->cache)) != NULL) {
                pw_delref_item(node->data);
-               node->data = item;
+               node->data = &pwitem->cache;
            }
        } else {
            /* Store by name, overwriting cached version. */
-           item->k.name = pw->pw_name;
-           if ((node = rbinsert(pwcache_byname, item)) != NULL) {
+           pwitem->cache.k.name = pw->pw_name;
+           if ((node = rbinsert(pwcache_byname, &pwitem->cache)) != NULL) {
                pw_delref_item(node->data);
-               node->data = item;
+               node->data = &pwitem->cache;
            }
        }
     }
-    item->refcnt++;
+    pwitem->cache.refcnt++;
     debug_return_ptr(pw);
 }
 
@@ -453,13 +469,13 @@ make_gritem(const struct group *gr, const char *name)
 {
     char *cp;
     size_t nsize, psize, nmem, total, len;
-    struct cache_item *item;
+    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(struct cache_item) + sizeof(struct group);
+    total = sizeof(*gritem);
     FIELD_SIZE(gr, gr_name, nsize);
     FIELD_SIZE(gr, gr_passwd, psize);
     if (gr->gr_mem) {
@@ -471,17 +487,16 @@ make_gritem(const struct group *gr, const char *name)
     if (name != NULL)
        total += strlen(name) + 1;
 
-    item = emalloc(total);
-    cp = (char *) item + sizeof(struct cache_item);
+    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 = (struct group *)cp;
-    memcpy(newgr, gr, sizeof(struct group));
-    cp += sizeof(struct group);
+    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;
@@ -499,14 +514,14 @@ make_gritem(const struct group *gr, const char *name)
     /* Set key and datum. */
     if (name != NULL) {
        memcpy(cp, name, strlen(name) + 1);
-       item->k.name = cp;
+       gritem->cache.k.name = cp;
     } else {
-       item->k.gid = gr->gr_gid;
+       gritem->cache.k.gid = gr->gr_gid;
     }
-    item->d.gr = newgr;
-    item->refcnt = 1;
+    gritem->cache.d.gr = newgr;
+    gritem->cache.refcnt = 1;
 
-    debug_return_ptr(item);
+    debug_return_ptr(&gritem->cache);
 }
 
 #ifdef HAVE_UTMPX_H
@@ -528,7 +543,7 @@ 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 cache_item_grlist *grlitem;
     struct group_list *grlist;
     struct group *grp;
     debug_decl(make_grlist_item, SUDO_DEBUG_NSS)
@@ -539,23 +554,21 @@ make_grlist_item(const char *user, GETGROUPS_T *gids, int ngids)
 
     /* Allocate in one big chunk for easy freeing. */
     nsize = strlen(user) + 1;
-    total = sizeof(struct cache_item) + sizeof(struct group_list) + nsize;
+    total = sizeof(*grlitem) + 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);
+    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 = (struct group_list *)cp;
-    zero_bytes(grlist, sizeof(struct group_list));
-    cp += sizeof(struct group_list);
+    grlist = &grlitem->grlist;
+    cp = (char *)(grlitem + 1);
     grlist->groups = (char **)cp;
     cp += sizeof(char *) * ngids;
     grlist->gids = (gid_t *)cp;
@@ -563,9 +576,9 @@ again:
 
     /* Set key and datum. */
     memcpy(cp, user, nsize);
-    item->k.name = cp;
-    item->d.grlist = grlist;
-    item->refcnt = 1;
+    grlitem->cache.k.name = cp;
+    grlitem->cache.d.grlist = grlist;
+    grlitem->cache.refcnt = 1;
     cp += nsize;
 
     /*
@@ -582,9 +595,9 @@ again:
     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) {
+           if (cp - (char *)grlitem + len > total) {
                total += len + GROUPNAME_LEN;
-               efree(item);
+               efree(grlitem);
                gr_delref(grp);
                goto again;
            }
@@ -600,7 +613,7 @@ again:
     aix_restoreauthdb();
 #endif
 
-    debug_return_ptr(item);
+    debug_return_ptr(&grlitem->cache);
 }
 
 void
@@ -655,10 +668,10 @@ sudo_getgrgid(gid_t gid)
            errorx(1, _("unable to cache gid %u (%s), already exists"),
                (unsigned int) gid, key.d.gr->gr_name);
     } else {
-       item = emalloc(sizeof(*item));
+       item = ecalloc(1, sizeof(*item));
        item->refcnt = 1;
        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);
@@ -693,11 +706,11 @@ sudo_getgrnam(const char *name)
            errorx(1, _("unable to cache group %s, already exists"), name);
     } else {
        len = strlen(name) + 1;
-       item = emalloc(sizeof(*item) + len);
+       item = ecalloc(1, sizeof(*item) + len);
        item->refcnt = 1;
        item->k.name = (char *) item + sizeof(*item);
        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);
     }
@@ -712,7 +725,7 @@ done:
 struct group *
 sudo_fakegrnam(const char *group)
 {
-    struct cache_item *item;
+    struct cache_item_gr *gritem;
     struct group *gr;
     struct rbnode *node;
     size_t len, namelen;
@@ -720,35 +733,34 @@ sudo_fakegrnam(const char *group)
     debug_decl(sudo_fakegrnam, SUDO_DEBUG_NSS)
 
     namelen = strlen(group);
-    len = sizeof(*item) + sizeof(*gr) + namelen + 1;
+    len = sizeof(*gritem) + 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));
+       gritem = ecalloc(1, len);
+       gr = &gritem->gr;
        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);
 
-       item->refcnt = 1;
-       item->d.gr = gr;
+       gritem->cache.refcnt = 1;
+       gritem->cache.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) {
+           gritem->cache.k.gid = gr->gr_gid;
+           if ((node = rbinsert(grcache_bygid, &gritem->cache)) != NULL) {
                gr_delref_item(node->data);
-               node->data = item;
+               node->data = &gritem->cache;
            }
        } else {
            /* Store by name, overwriting cached version. */
-           item->k.name = gr->gr_name;
-           if ((node = rbinsert(grcache_byname, item)) != NULL) {
+           gritem->cache.k.name = gr->gr_name;
+           if ((node = rbinsert(grcache_byname, &gritem->cache)) != NULL) {
                gr_delref_item(node->data);
-               node->data = item;
+               node->data = &gritem->cache;
            }
        }
     }
-    item->refcnt++;
+    gritem->cache.refcnt++;
     debug_return_ptr(gr);
 }
 
@@ -870,11 +882,11 @@ get_group_list(struct passwd *pw)
     } else {
        /* Should not happen. */
        len = strlen(pw->pw_name) + 1;
-       item = emalloc(sizeof(*item) + len);
+       item = ecalloc(1, sizeof(*item) + len);
        item->refcnt = 1;
        item->k.name = (char *) item + sizeof(*item);
        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);