From: Todd C. Miller Date: Fri, 21 Sep 2012 20:25:01 +0000 (-0400) Subject: Split out implementation-specific back end code out of pwutil.c X-Git-Tag: SUDO_1_8_7~1^2~385 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=883e0ec3ccfc2dc82faaec15bfeac21a96cd15fe;p=sudo Split out implementation-specific back end code out of pwutil.c into pwutil_impl.c. This will allow the main pwutil code to be used for lookup methods other than getpw* and getgr*. --- diff --git a/MANIFEST b/MANIFEST index a55370d30..2b2b5498a 100644 --- a/MANIFEST +++ b/MANIFEST @@ -223,6 +223,8 @@ plugins/sudoers/po/vi.po plugins/sudoers/po/zh_CN.mo plugins/sudoers/po/zh_CN.po plugins/sudoers/pwutil.c +plugins/sudoers/pwutil.h +plugins/sudoers/pwutil_impl.c plugins/sudoers/redblack.c plugins/sudoers/redblack.h plugins/sudoers/regress/check_symbols/check_symbols.c diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index 227331504..a7c90237b 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -124,8 +124,8 @@ TEST_PROGS = check_iolog_path check_fill check_wrap check_addr check_symbols AUTH_OBJS = sudo_auth.lo @AUTH_OBJS@ LIBPARSESUDOERS_OBJS = alias.lo audit.lo defaults.lo gram.lo match.lo \ - match_addr.lo pwutil.lo timestr.lo toke.lo \ - toke_util.lo redblack.lo + match_addr.lo pwutil.lo pwutil_impl.lo timestr.lo \ + toke.lo toke_util.lo redblack.lo SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo env.lo goodpath.lo \ group_plugin.lo find_path.lo interfaces.lo logging.lo \ @@ -144,7 +144,7 @@ CHECK_ADDR_OBJS = check_addr.o match_addr.o interfaces.o error.o CHECK_FILL_OBJS = check_fill.o toke_util.o error.o CHECK_IOLOG_PATH_OBJS = check_iolog_path.o error.o iolog_path.o pwutil.o \ - redblack.o + pwutil_impl.o redblack.o CHECK_SYMBOLS_OBJS = check_symbols.o error.o @@ -676,9 +676,19 @@ pwutil.lo: $(srcdir)/pwutil.c $(top_builddir)/config.h $(srcdir)/sudoers.h \ $(incdir)/list.h $(incdir)/fileops.h $(srcdir)/defaults.h \ $(devdir)/def_data.h $(srcdir)/logging.h $(srcdir)/sudo_nss.h \ $(incdir)/sudo_plugin.h $(incdir)/sudo_debug.h $(incdir)/gettext.h \ - $(srcdir)/redblack.h + $(srcdir)/redblack.h $(srcdir)/pwutil.h $(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(DEFS) $(srcdir)/pwutil.c pwutil.o: pwutil.lo +pwutil_impl.lo: $(srcdir)/pwutil_impl.c $(top_builddir)/config.h \ + $(srcdir)/sudoers.h $(top_srcdir)/compat/stdbool.h \ + $(top_builddir)/pathnames.h $(incdir)/missing.h \ + $(incdir)/error.h $(incdir)/alloc.h $(incdir)/list.h \ + $(incdir)/fileops.h $(srcdir)/defaults.h $(devdir)/def_data.h \ + $(srcdir)/logging.h $(srcdir)/sudo_nss.h \ + $(incdir)/sudo_plugin.h $(incdir)/sudo_debug.h \ + $(incdir)/gettext.h $(srcdir)/pwutil.h + $(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(DEFS) $(srcdir)/pwutil_impl.c +pwutil_impl.o: pwutil_impl.lo redblack.lo: $(srcdir)/redblack.c $(top_builddir)/config.h $(incdir)/missing.h \ $(incdir)/alloc.h $(incdir)/sudo_debug.h $(srcdir)/redblack.h $(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(DEFS) $(srcdir)/redblack.c diff --git a/plugins/sudoers/pwutil.c b/plugins/sudoers/pwutil.c index 3e876d889..24be29f5d 100644 --- a/plugins/sudoers/pwutil.c +++ b/plugins/sudoers/pwutil.c @@ -22,7 +22,6 @@ #include #include -#include #include #include #ifdef STDC_HEADERS @@ -48,17 +47,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,47 +67,6 @@ static int cmp_grgid(const void *, const void *); #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. */ @@ -153,76 +106,6 @@ do { \ } \ } 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 sudo_pw_addref(struct passwd *pw) { @@ -272,20 +155,16 @@ 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 = 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); } + if (rbinsert(pwcache_byuid, item) != NULL) + errorx(1, _("unable to cache uid %u, already exists"), + (unsigned int) uid); #ifdef HAVE_SETAUTHDB aix_restoreauthdb(); #endif @@ -316,20 +195,17 @@ 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 = 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); } + if (rbinsert(pwcache_byname, item) != NULL) + errorx(1, _("unable to cache user %s, already exists"), name); #ifdef HAVE_SETAUTHDB aix_restoreauthdb(); #endif @@ -459,163 +335,6 @@ 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_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); - sudo_gr_delref(grp); - goto again; - } - memcpy(cp, grp->gr_name, len); - grlist->groups[ngroups++] = cp; - cp += len; - sudo_gr_delref(grp); - } - } - grlist->ngroups = ngroups; - -#ifdef HAVE_SETAUTHDB - aix_restoreauthdb(); -#endif - - debug_return_ptr(&grlitem->cache); -} - void sudo_gr_addref(struct group *gr) { @@ -662,20 +381,16 @@ 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 = 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); } + if (rbinsert(grcache_bygid, item) != NULL) + errorx(1, _("unable to cache gid %u, already exists"), + (unsigned int) gid); done: item->refcnt++; debug_return_ptr(item->d.gr); @@ -700,20 +415,17 @@ 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 = 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); } + if (rbinsert(grcache_byname, item) != NULL) + errorx(1, _("unable to cache group %s, already exists"), name); done: item->refcnt++; debug_return_ptr(item->d.gr); @@ -846,8 +558,6 @@ sudo_get_grlist(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; @@ -857,37 +567,9 @@ sudo_get_grlist(struct passwd *pw) } /* * Cache group db entry if it exists or a negative response if not. - * Use gids list from front-end if possible, otherwise getgrouplist(). */ - if (pw == sudo_user.pw && sudo_user.gids != NULL) { - gids = user_gids; - ngids = user_ngids; - user_gids = NULL; - user_ngids = 0; - } else { -#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); - 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 { + item = sudo_make_grlist_item(pw); + if (item == NULL) { /* Should not happen. */ len = strlen(pw->pw_name) + 1; item = ecalloc(1, sizeof(*item) + len); @@ -895,10 +577,10 @@ sudo_get_grlist(struct passwd *pw) 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); } + if (rbinsert(grlist_cache, item) != NULL) + errorx(1, "unable to cache group list for %s, already exists", + pw->pw_name); done: item->refcnt++; debug_return_ptr(item->d.grlist); diff --git a/plugins/sudoers/pwutil.h b/plugins/sudoers/pwutil.h new file mode 100644 index 000000000..32d9d04a3 --- /dev/null +++ b/plugins/sudoers/pwutil.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2010-2012 + * Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _PWUTIL_H +#define _PWUTIL_H + +#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 */ +}; + +struct cache_item *sudo_make_gritem(gid_t gid, const char *group); +struct cache_item *sudo_make_grlist_item(struct passwd *pw); +struct cache_item *sudo_make_pwitem(uid_t uid, const char *user); + +#endif /* _PWUTIL_H */ diff --git a/plugins/sudoers/pwutil_impl.c b/plugins/sudoers/pwutil_impl.c new file mode 100644 index 000000000..c87b3830c --- /dev/null +++ b/plugins/sudoers/pwutil_impl.c @@ -0,0 +1,338 @@ +/* + * Copyright (c) 1996, 1998-2005, 2007-2012 + * Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +#include + +#include +#include +#include +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif /* STDC_HEADERS */ +#ifdef HAVE_STRING_H +# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS) +# include +# endif +# include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_UTMPX_H +# include +#else +# include +#endif /* HAVE_UTMPX_H */ +#include +#include +#include + +#include "sudoers.h" +#include "pwutil.h" + +#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. + */ +struct cache_item * +sudo_make_pwitem(uid_t uid, 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 *pw, *newpw; + debug_decl(make_pwitem, SUDO_DEBUG_NSS) + + /* Look up by name or uid. */ + pw = name ? getpwnam(name) : getpwuid(uid); + if (pw == NULL) + debug_return_ptr(NULL); + + /* 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); +} + +/* + * 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. + */ +struct cache_item * +sudo_make_gritem(gid_t gid, const char *name) +{ + char *cp; + size_t nsize, psize, nmem, total, len; + struct cache_item_gr *gritem; + struct group *gr, *newgr; + debug_decl(make_gritem, SUDO_DEBUG_NSS) + + /* Look up by name or gid. */ + gr = name ? getgrnam(name) : getgrgid(gid); + if (gr == NULL) + debug_return_ptr(NULL); + + /* 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 gids arrays or from getgrouplist(3). + * Consumes (frees) the gids array on success. + */ +struct cache_item * +sudo_make_grlist_item(struct passwd *pw) +{ + char *cp; + size_t i, nsize, ngroups, total, len; + struct cache_item_grlist *grlitem; + struct group_list *grlist; + GETGROUPS_T *gids; + struct group *grp; + int ngids; + debug_decl(make_grlist_item, SUDO_DEBUG_NSS) + + if (pw == sudo_user.pw && sudo_user.gids != NULL) { + gids = user_gids; + ngids = user_ngids; + user_gids = NULL; + user_ngids = 0; + } else { +#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); + debug_return_ptr(NULL); + } + } + } + if (ngids <= 0) + debug_return_ptr(NULL); + +#ifdef HAVE_SETAUTHDB + aix_setauthdb((char *) pw->pw_name); +#endif + + /* Allocate in one big chunk for easy freeing. */ + nsize = strlen(pw->pw_name) + 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, pw->pw_name, 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); + sudo_gr_delref(grp); + goto again; + } + memcpy(cp, grp->gr_name, len); + grlist->groups[ngroups++] = cp; + cp += len; + sudo_gr_delref(grp); + } + } + grlist->ngroups = ngroups; + +#ifdef HAVE_SETAUTHDB + aix_restoreauthdb(); +#endif + + debug_return_ptr(&grlitem->cache); +}