Fix an alignment problem on NetBSD systems with a 64-bit time_t and

strict alignment.  Based on a patch from Martin Husemann.
This commit is contained in:
Todd C. Miller
2012-05-10 11:06:46 -04:00
parent 4e0ffceacb
commit 0d69580776

View 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 = ecalloc(1, 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)
@@ -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)
@@ -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,16 +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 = ecalloc(1, len);
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);
@@ -355,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);
}
@@ -452,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) {
@@ -470,17 +487,16 @@ make_gritem(const struct group *gr, const char *name)
if (name != NULL)
total += strlen(name) + 1;
item = ecalloc(1, 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;
@@ -498,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
@@ -527,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)
@@ -538,22 +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 = ecalloc(1, 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;
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;
@@ -561,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;
/*
@@ -580,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;
}
@@ -598,7 +613,7 @@ again:
aix_restoreauthdb();
#endif
debug_return_ptr(item);
debug_return_ptr(&grlitem->cache);
}
void
@@ -710,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;
@@ -718,34 +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 = ecalloc(1, len);
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);
}