--- embedaddon/sudo/plugins/sudoers/pwutil.c 2012/02/21 16:23:02 1.1 +++ embedaddon/sudo/plugins/sudoers/pwutil.c 2014/06/15 16:12:54 1.1.1.5 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 1998-2005, 2007-2011 + * Copyright (c) 1996, 1998-2005, 2007-2013 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -22,8 +22,6 @@ #include #include -#include -#include #include #ifdef STDC_HEADERS # include @@ -48,17 +46,12 @@ #ifdef HAVE_SETAUTHDB # include #endif /* HAVE_SETAUTHDB */ -#ifdef HAVE_UTMPX_H -# include -#else -# include -#endif /* HAVE_UTMPX_H */ -#include #include #include #include "sudoers.h" #include "redblack.h" +#include "pwutil.h" /* * The passwd and group caches. @@ -73,24 +66,6 @@ static int cmp_grgid(const void *, const void *); #define cmp_grnam cmp_pwnam -#define ptr_to_item(p) ((struct cache_item *)((char *)(p) - sizeof(struct cache_item))) - -struct cache_item { - unsigned int refcnt; - /* key */ - union { - uid_t uid; - gid_t gid; - char *name; - } k; - /* datum */ - union { - struct passwd *pw; - struct group *gr; - struct group_list *grlist; - } d; -}; - /* * Compare by uid. */ @@ -113,126 +88,43 @@ cmp_pwnam(const void *v1, const void *v2) 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 -pw_addref(struct passwd *pw) +sudo_pw_addref(struct passwd *pw) { + debug_decl(sudo_pw_addref, SUDO_DEBUG_NSS) ptr_to_item(pw)->refcnt++; + debug_return; } static void -pw_delref_item(void *v) +sudo_pw_delref_item(void *v) { struct cache_item *item = v; + debug_decl(sudo_pw_delref_item, SUDO_DEBUG_NSS) if (--item->refcnt == 0) efree(item); + + debug_return; } 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. - * Fills in pw_passwd from shadow file if necessary. */ struct passwd * sudo_getpwuid(uid_t uid) { struct cache_item key, *item; struct rbnode *node; + debug_decl(sudo_getpwuid, SUDO_DEBUG_NSS) key.k.uid = uid; if ((node = rbfind(pwcache_byuid, &key)) != NULL) { @@ -245,31 +137,26 @@ sudo_getpwuid(uid_t uid) #ifdef HAVE_SETAUTHDB aix_setauthdb(IDtouser(uid)); #endif - if ((key.d.pw = getpwuid(uid)) != NULL) { - item = make_pwitem(key.d.pw, 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 = emalloc(sizeof(*item)); + item = sudo_make_pwitem(uid, NULL); + if (item == NULL) { + item = ecalloc(1, sizeof(*item)); item->refcnt = 1; item->k.uid = uid; - item->d.pw = NULL; - if (rbinsert(pwcache_byuid, item) != NULL) - errorx(1, _("unable to cache uid %u, already exists"), - (unsigned int) uid); + /* item->d.pw = NULL; */ } + if (rbinsert(pwcache_byuid, item) != NULL) + fatalx(U_("unable to cache uid %u, already exists"), + (unsigned int) uid); #ifdef HAVE_SETAUTHDB aix_restoreauthdb(); #endif done: item->refcnt++; - return item->d.pw; + debug_return_ptr(item->d.pw); } /* * 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) @@ -277,6 +164,7 @@ sudo_getpwnam(const char *name) struct cache_item key, *item; struct rbnode *node; size_t len; + debug_decl(sudo_getpwnam, SUDO_DEBUG_NSS) key.k.name = (char *) name; if ((node = rbfind(pwcache_byname, &key)) != NULL) { @@ -289,82 +177,91 @@ sudo_getpwnam(const char *name) #ifdef HAVE_SETAUTHDB aix_setauthdb((char *) name); #endif - if ((key.d.pw = getpwnam(name)) != NULL) { - item = make_pwitem(key.d.pw, name); - if (rbinsert(pwcache_byname, item) != NULL) - errorx(1, _("unable to cache user %s, already exists"), name); - } else { + item = sudo_make_pwitem((uid_t)-1, name); + if (item == NULL) { 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; - if (rbinsert(pwcache_byname, item) != NULL) - errorx(1, _("unable to cache user %s, already exists"), name); + /* item->d.pw = NULL; */ } + if (rbinsert(pwcache_byname, item) != NULL) + fatalx(U_("unable to cache user %s, already exists"), name); #ifdef HAVE_SETAUTHDB aix_restoreauthdb(); #endif done: 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 * -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 rbnode *node; - size_t len, namelen; + size_t len, name_len, home_len, shell_len; int i; + debug_decl(sudo_mkpwent, SUDO_DEBUG_NSS) - namelen = strlen(user); - len = sizeof(*item) + sizeof(*pw) + namelen + 1 /* pw_name */ + + /* Optional arguments. */ + 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_dir */ + sizeof(_PATH_BSHELL); + home_len + 1 /* pw_dir */ + shell_len + 1 /* pw_shell */; 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); - memcpy(pw->pw_name, user, namelen + 1); - pw->pw_passwd = pw->pw_name + namelen + 1; + pw->pw_name = (char *)(pwitem + 1); + memcpy(pw->pw_name, user, name_len + 1); + pw->pw_passwd = pw->pw_name + name_len + 1; memcpy(pw->pw_passwd, "*", 2); pw->pw_gecos = pw->pw_passwd + 2; pw->pw_gecos[0] = '\0'; pw->pw_dir = pw->pw_gecos + 1; - memcpy(pw->pw_dir, "/", 2); - pw->pw_shell = pw->pw_dir + 2; - memcpy(pw->pw_shell, _PATH_BSHELL, sizeof(_PATH_BSHELL)); + memcpy(pw->pw_dir, home, home_len + 1); + pw->pw_shell = pw->pw_dir + home_len + 1; + memcpy(pw->pw_shell, shell, shell_len + 1); - 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) { - pw_delref_item(node->data); - node->data = item; + /* Store by uid if it doesn't already exist. */ + pwitem->cache.k.uid = pw->pw_uid; + if ((node = rbinsert(pwcache_byuid, &pwitem->cache)) != NULL) { + /* Already exists, free the item we created. */ + efree(pwitem); + pwitem = (struct cache_item_pw *) node->data; } } else { - /* Store by name, overwriting cached version. */ - item->k.name = pw->pw_name; - if ((node = rbinsert(pwcache_byname, item)) != NULL) { - pw_delref_item(node->data); - node->data = item; + /* Store by name if it doesn't already exist. */ + pwitem->cache.k.name = pw->pw_name; + if ((node = rbinsert(pwcache_byname, &pwitem->cache)) != NULL) { + /* Already exists, free the item we created. */ + efree(pwitem); + pwitem = (struct cache_item_pw *) node->data; } } } - item->refcnt++; - return pw; + pwitem->cache.refcnt++; + debug_return_ptr(&pwitem->pw); } /* @@ -373,40 +270,59 @@ sudo_fakepwnamid(const char *user, uid_t uid, gid_t gi struct passwd * sudo_fakepwnam(const char *user, gid_t gid) { + const char *errstr; uid_t uid; + debug_decl(sudo_fakepwnam, SUDO_DEBUG_NSS) - uid = (uid_t) atoi(user + 1); - return sudo_fakepwnamid(user, uid, gid); + uid = (uid_t) atoid(user + 1, NULL, NULL, &errstr); + 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 sudo_setpwent(void) { + debug_decl(sudo_setpwent, SUDO_DEBUG_NSS) + setpwent(); if (pwcache_byuid == NULL) pwcache_byuid = rbcreate(cmp_pwuid); if (pwcache_byname == NULL) pwcache_byname = rbcreate(cmp_pwnam); + + debug_return; } void sudo_freepwcache(void) { + debug_decl(sudo_freepwcache, SUDO_DEBUG_NSS) + if (pwcache_byuid != NULL) { - rbdestroy(pwcache_byuid, pw_delref_item); + rbdestroy(pwcache_byuid, sudo_pw_delref_item); pwcache_byuid = NULL; } if (pwcache_byname != NULL) { - rbdestroy(pwcache_byname, pw_delref_item); + rbdestroy(pwcache_byname, sudo_pw_delref_item); pwcache_byname = NULL; } + + debug_return; } void sudo_endpwent(void) { + debug_decl(sudo_endpwent, SUDO_DEBUG_NSS) + endpwent(); sudo_freepwcache(); + + debug_return; } /* @@ -420,183 +336,32 @@ cmp_grgid(const void *v1, const void *v2) 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 -gr_addref(struct group *gr) +sudo_gr_addref(struct group *gr) { + debug_decl(sudo_gr_addref, SUDO_DEBUG_NSS) ptr_to_item(gr)->refcnt++; + debug_return; } static void -gr_delref_item(void *v) +sudo_gr_delref_item(void *v) { struct cache_item *item = v; + debug_decl(sudo_gr_delref_item, SUDO_DEBUG_NSS) if (--item->refcnt == 0) efree(item); + + debug_return; } 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; } /* @@ -607,6 +372,7 @@ sudo_getgrgid(gid_t gid) { struct cache_item key, *item; struct rbnode *node; + debug_decl(sudo_getgrgid, SUDO_DEBUG_NSS) key.k.gid = gid; if ((node = rbfind(grcache_bygid, &key)) != NULL) { @@ -616,23 +382,19 @@ sudo_getgrgid(gid_t gid) /* * Cache group db entry if it exists or a negative response if not. */ - if ((key.d.gr = getgrgid(gid)) != NULL) { - item = make_gritem(key.d.gr, 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 = emalloc(sizeof(*item)); + item = sudo_make_gritem(gid, NULL); + if (item == NULL) { + item = ecalloc(1, sizeof(*item)); item->refcnt = 1; item->k.gid = gid; - item->d.gr = NULL; - if (rbinsert(grcache_bygid, item) != NULL) - errorx(1, _("unable to cache gid %u, already exists"), - (unsigned int) gid); + /* item->d.gr = NULL; */ } + if (rbinsert(grcache_bygid, item) != NULL) + fatalx(U_("unable to cache gid %u, already exists"), + (unsigned int) gid); done: item->refcnt++; - return item->d.gr; + debug_return_ptr(item->d.gr); } /* @@ -644,6 +406,7 @@ sudo_getgrnam(const char *name) struct cache_item key, *item; struct rbnode *node; size_t len; + debug_decl(sudo_getgrnam, SUDO_DEBUG_NSS) key.k.name = (char *) name; if ((node = rbfind(grcache_byname, &key)) != NULL) { @@ -653,23 +416,20 @@ sudo_getgrnam(const char *name) /* * Cache group db entry if it exists or a negative response if not. */ - if ((key.d.gr = getgrnam(name)) != NULL) { - item = make_gritem(key.d.gr, name); - if (rbinsert(grcache_byname, item) != NULL) - errorx(1, _("unable to cache group %s, already exists"), name); - } else { + item = sudo_make_gritem((gid_t)-1, name); + if (item == NULL) { 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; - if (rbinsert(grcache_byname, item) != NULL) - errorx(1, _("unable to cache group %s, already exists"), name); + /* item->d.gr = NULL; */ } + if (rbinsert(grcache_byname, item) != NULL) + fatalx(U_("unable to cache group %s, already exists"), name); done: item->refcnt++; - return item->d.gr; + debug_return_ptr(item->d.gr); } /* @@ -678,69 +438,87 @@ done: struct group * sudo_fakegrnam(const char *group) { - struct cache_item *item; + struct cache_item_gr *gritem; + const char *errstr; struct group *gr; struct rbnode *node; - size_t len, namelen; + size_t len, name_len; int i; + debug_decl(sudo_fakegrnam, SUDO_DEBUG_NSS) - namelen = strlen(group); - len = sizeof(*item) + sizeof(*gr) + namelen + 1; + name_len = strlen(group); + len = sizeof(*gritem) + name_len + 1; for (i = 0; i < 2; i++) { - item = emalloc(len); - zero_bytes(item, sizeof(*item) + sizeof(*gr)); - gr = (struct group *) ((char *)item + sizeof(*item)); - gr->gr_gid = (gid_t) atoi(group + 1); - gr->gr_name = (char *)gr + sizeof(struct group); - memcpy(gr->gr_name, group, namelen + 1); + gritem = ecalloc(1, len); + gr = &gritem->gr; + gr->gr_gid = (gid_t) atoid(group + 1, NULL, NULL, &errstr); + gr->gr_name = (char *)(gritem + 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); + } - 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) { - gr_delref_item(node->data); - node->data = item; + /* Store by gid if it doesn't already exist. */ + gritem->cache.k.gid = gr->gr_gid; + if ((node = rbinsert(grcache_bygid, &gritem->cache)) != NULL) { + /* Already exists, free the item we created. */ + efree(gritem); + gritem = (struct cache_item_gr *) node->data; } } else { /* Store by name, overwriting cached version. */ - item->k.name = gr->gr_name; - if ((node = rbinsert(grcache_byname, item)) != NULL) { - gr_delref_item(node->data); - node->data = item; + gritem->cache.k.name = gr->gr_name; + if ((node = rbinsert(grcache_byname, &gritem->cache)) != NULL) { + /* Already exists, free the item we created. */ + efree(gritem); + gritem = (struct cache_item_gr *) node->data; } } } - item->refcnt++; - return gr; + gritem->cache.refcnt++; + debug_return_ptr(&gritem->gr); } 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++; + debug_return; } static void -grlist_delref_item(void *v) +sudo_grlist_delref_item(void *v) { struct cache_item *item = v; + debug_decl(sudo_gr_delref_item, SUDO_DEBUG_NSS) if (--item->refcnt == 0) efree(item); + + debug_return; } 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 sudo_setgrent(void) { + debug_decl(sudo_setgrent, SUDO_DEBUG_NSS) + setgrent(); if (grcache_bygid == NULL) grcache_bygid = rbcreate(cmp_grgid); @@ -748,40 +526,49 @@ sudo_setgrent(void) grcache_byname = rbcreate(cmp_grnam); if (grlist_cache == NULL) grlist_cache = rbcreate(cmp_grnam); + + debug_return; } void sudo_freegrcache(void) { + debug_decl(sudo_freegrcache, SUDO_DEBUG_NSS) + if (grcache_bygid != NULL) { - rbdestroy(grcache_bygid, gr_delref_item); + rbdestroy(grcache_bygid, sudo_gr_delref_item); grcache_bygid = NULL; } if (grcache_byname != NULL) { - rbdestroy(grcache_byname, gr_delref_item); + rbdestroy(grcache_byname, sudo_gr_delref_item); grcache_byname = NULL; } if (grlist_cache != NULL) { - rbdestroy(grlist_cache, grlist_delref_item); + rbdestroy(grlist_cache, sudo_grlist_delref_item); grlist_cache = NULL; } + + debug_return; } void sudo_endgrent(void) { + debug_decl(sudo_endgrent, SUDO_DEBUG_NSS) + endgrent(); sudo_freegrcache(); + + debug_return; } struct group_list * -get_group_list(struct passwd *pw) +sudo_get_grlist(const struct passwd *pw) { struct cache_item key, *item; struct rbnode *node; size_t len; - GETGROUPS_T *gids; - int ngids; + debug_decl(sudo_get_grlist, SUDO_DEBUG_NSS) key.k.name = pw->pw_name; if ((node = rbfind(grlist_cache, &key)) != NULL) { @@ -791,85 +578,75 @@ get_group_list(struct passwd *pw) /* * Cache group db entry if it exists or a negative response if not. */ -#if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX) - ngids = (int)sysconf(_SC_NGROUPS_MAX) * 2; - 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 { + item = sudo_make_grlist_item(pw, NULL, NULL); + if (item == NULL) { /* 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; - if (rbinsert(grlist_cache, item) != NULL) - errorx(1, "unable to cache group list for %s, already exists", - pw->pw_name); + /* item->d.grlist = NULL; */ } + if (rbinsert(grlist_cache, item) != NULL) + fatalx(U_("unable to cache group list for %s, already exists"), + pw->pw_name); done: item->refcnt++; - return item->d.grlist; + debug_return_ptr(item->d.grlist); } 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 rbnode *node; + debug_decl(sudo_set_grlist, SUDO_DEBUG_NSS) /* * 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 ((item = make_grlist_item(user, gids, ngids)) == NULL) - errorx(1, "unable to parse group list for %s", user); + if ((item = sudo_make_grlist_item(pw, groups, gids)) == NULL) + fatalx(U_("unable to parse groups for %s"), pw->pw_name); if (rbinsert(grlist_cache, item) != NULL) - errorx(1, "unable to cache group list for %s, already exists", - user); + fatalx(U_("unable to cache group list for %s, already exists"), + pw->pw_name); } + debug_return; } -int -user_in_group(struct passwd *pw, const char *group) +bool +user_in_group(const struct passwd *pw, const char *group) { struct group_list *grlist; struct group *grp = NULL; - int i, matched = FALSE; + const char *errstr; + 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 (group[0] == '#') { - gid_t gid = atoi(group + 1); - if (gid == pw->pw_gid) { - matched = TRUE; - goto done; - } - for (i = 0; i < grlist->ngids; i++) { - if (gid == grlist->gids[i]) { - matched = TRUE; + gid_t gid = (gid_t) atoid(group + 1, NULL, NULL, &errstr); + if (errstr != NULL) { + sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_DIAG, + "gid %s %s", group, errstr); + } else { + if (gid == pw->pw_gid) { + matched = true; goto done; } + for (i = 0; i < grlist->ngids; i++) { + if (gid == grlist->gids[i]) { + matched = true; + goto done; + } + } } } @@ -879,7 +656,7 @@ user_in_group(struct passwd *pw, const char *group) */ for (i = 0; i < grlist->ngroups; i++) { if (strcasecmp(group, grlist->groups[i]) == 0) { - matched = TRUE; + matched = true; goto done; } } @@ -887,14 +664,14 @@ user_in_group(struct passwd *pw, const char *group) /* Finally check against user's primary (passwd file) group. */ if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) { if (strcasecmp(group, grp->gr_name) == 0) { - matched = TRUE; + matched = true; goto done; } } done: if (grp != NULL) - gr_delref(grp); - grlist_delref(grlist); + sudo_gr_delref(grp); + sudo_grlist_delref(grlist); } - return matched; + debug_return_bool(matched); }