From: Todd C. Miller Date: Wed, 5 Jan 2005 01:07:33 +0000 (+0000) Subject: Split off pw/gr cache and dup code into its own file. X-Git-Tag: SUDO_1_7_0~758 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=282c0bf361fb2af4130dcb11353b09373f9041a6;p=sudo Split off pw/gr cache and dup code into its own file. This allows visudo and testsudoers to use the pw/gr cache too. --- diff --git a/getspwuid.c b/getspwuid.c index 488ca70c4..9c4fb5544 100644 --- a/getspwuid.c +++ b/getspwuid.c @@ -69,7 +69,6 @@ #endif /* HAVE_GETAUTHUID */ #include "sudo.h" -#include "redblack.h" #ifndef lint static const char rcsid[] = "$Sudo$"; @@ -81,40 +80,11 @@ static const char rcsid[] = "$Sudo$"; #if defined(HAVE_GETPRPWNAM) && defined(__alpha) int crypt_type = INT_MAX; #endif /* HAVE_GETPRPWNAM && __alpha */ -static struct rbtree *pwcache_byuid, *pwcache_byname; -static struct rbtree *grcache_bygid, *grcache_byname; -static int cmp_pwuid __P((const VOID *, const VOID *)); -static int cmp_pwnam __P((const VOID *, const VOID *)); -static void pw_free __P((VOID *)); -static int cmp_grgid __P((const VOID *, const VOID *)); -static int cmp_grnam __P((const VOID *, const VOID *)); - -/* - * Compare by uid. - */ -static int -cmp_pwuid(v1, v2) - const VOID *v1; - const VOID *v2; -{ - const struct passwd *pw1 = (const struct passwd *) v1; - const struct passwd *pw2 = (const struct passwd *) v2; - return(pw1->pw_uid - pw2->pw_uid); -} - -/* - * Compare by user name. - */ -static int -cmp_pwnam(v1, v2) - const VOID *v1; - const VOID *v2; -{ - const struct passwd *pw1 = (const struct passwd *) v1; - const struct passwd *pw2 = (const struct passwd *) v2; - return(strcmp(pw1->pw_name, pw2->pw_name)); -} +extern VOID *pwcache_get __P((enum cmptype, VOID *)); +extern int pwcache_put __P((enum cmptype, VOID *)); +extern struct passwd *sudo_pwdup __P((const struct passwd *)); +extern struct group *sudo_grdup __P((const struct group *)); /* * Return a copy of the encrypted password for the user described by pw. @@ -196,101 +166,6 @@ sudo_getepw(pw) return(estrdup(pw->pw_passwd)); } -/* - * Dynamically allocate space for a struct password and the constituent parts - * that we care about. Fills in pw_passwd from shadow file. - */ -static struct passwd * -sudo_pwdup(pw) - const struct passwd *pw; -{ - char *cp; - const char *pw_passwd, *pw_shell; - size_t nsize, psize, csize, gsize, dsize, ssize, total; - struct passwd *newpw; - - /* Get shadow password if available. */ - pw_passwd = sudo_getepw(pw); - - /* 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 passwd); - if (pw->pw_name) { - nsize = strlen(pw->pw_name) + 1; - total += nsize; - } - if (pw_passwd) { - psize = strlen(pw_passwd) + 1; - total += psize; - } -#ifdef HAVE_LOGIN_CAP_H - if (pw->pw_class) { - csize = strlen(pw->pw_class) + 1; - total += csize; - } -#endif - if (pw->pw_gecos) { - gsize = strlen(pw->pw_gecos) + 1; - total += gsize; - } - if (pw->pw_dir) { - dsize = strlen(pw->pw_dir) + 1; - total += dsize; - } - if (pw_shell) { - ssize = strlen(pw_shell) + 1; - total += ssize; - } - if ((cp = malloc(total)) == NULL) - return (NULL); - newpw = (struct passwd *)cp; - - /* - * Copy in passwd contents and make strings relative to space - * at the end of the buffer. - */ - (void)memcpy(newpw, pw, sizeof(struct passwd)); - cp += sizeof(struct passwd); - if (nsize) { - (void)memcpy(cp, pw->pw_name, nsize); - newpw->pw_name = cp; - cp += nsize; - } - if (psize) { - (void)memcpy(cp, pw_passwd, psize); - newpw->pw_passwd = cp; - cp += psize; - } -#ifdef HAVE_LOGIN_CAP_H - if (csize) { - (void)memcpy(cp, pw->pw_class, csize); - newpw->pw_class = cp; - cp += csize; - } -#endif - if (gsize) { - (void)memcpy(cp, pw->pw_gecos, gsize); - newpw->pw_gecos = cp; - cp += gsize; - } - if (dsize) { - (void)memcpy(cp, pw->pw_dir, dsize); - newpw->pw_dir = cp; - cp += dsize; - } - if (ssize) { - (void)memcpy(cp, pw_shell, ssize); - newpw->pw_shell = cp; - cp += ssize; - } - - return (newpw); -} - /* * Get a password entry by uid and allocate space for it. * Fills in pw_passwd from shadow file if necessary. @@ -300,28 +175,25 @@ sudo_getpwuid(uid) uid_t uid; { struct passwd key, *pw; - struct rbnode *node; key.pw_uid = uid; - if ((node = rbfind(pwcache_byuid, &key)) != NULL) { - pw = (struct passwd *) node->data; - return(pw->pw_name != NULL ? pw : NULL); - } + if ((pw = pwcache_get(byuid, &key)) != NULL) + return(pw); /* * Cache passwd db entry if it exists or a negative response if not. */ if ((pw = getpwuid(uid)) != NULL) { pw = sudo_pwdup(pw); - if (rbinsert(pwcache_byname, (VOID *) pw) != NULL) + if (!pwcache_put(bypwnam, (VOID *) pw)) errorx(1, "unable to cache user name, already exists"); - if (rbinsert(pwcache_byuid, (VOID *) pw) != NULL) + if (!pwcache_put(byuid, (VOID *) pw)) errorx(1, "unable to cache uid, already exists"); return(pw); } else { pw = emalloc(sizeof(*pw)); memset(pw, 0, sizeof(*pw)); pw->pw_uid = uid; - if (rbinsert(pwcache_byuid, (VOID *) pw) != NULL) + if (!pwcache_put(byuid, (VOID *) pw)) errorx(1, "unable to cache uid, already exists"); return(NULL); } @@ -336,23 +208,20 @@ sudo_getpwnam(name) const char *name; { struct passwd key, *pw; - struct rbnode *node; size_t len; char *cp; key.pw_name = (char *) name; - if ((node = rbfind(pwcache_byname, &key)) != NULL) { - pw = (struct passwd *) node->data; - return(pw->pw_uid != (uid_t) -1 ? pw : NULL); - } + if ((pw = pwcache_get(bypwnam, &key)) != NULL) + return(pw); /* * Cache passwd db entry if it exists or a negative response if not. */ if ((pw = getpwnam(name)) != NULL) { pw = sudo_pwdup(pw); - if (rbinsert(pwcache_byname, (VOID *) pw) != NULL) + if (!pwcache_put(bypwnam, (VOID *) pw)) errorx(1, "unable to cache user name, already exists"); - if (rbinsert(pwcache_byuid, (VOID *) pw) != NULL) + if (!pwcache_put(byuid, (VOID *) pw)) errorx(1, "unable to cache uid, already exists"); return(pw); } else { @@ -364,71 +233,12 @@ sudo_getpwnam(name) memcpy(cp, name, len); pw->pw_name = cp; pw->pw_uid = (uid_t) -1; - if (rbinsert(pwcache_byname, (VOID *) pw) != NULL) + if (!pwcache_put(bypwnam, (VOID *) pw)) errorx(1, "unable to cache user name, already exists"); return(NULL); } } -/* - * Take a uid and return a faked up passwd struct. - */ -struct passwd * -sudo_fakepwuid(uid) - uid_t uid; -{ - struct passwd *pw; - struct rbnode *node; - - pw = emalloc(sizeof(struct passwd) + MAX_UID_T_LEN + 1); - memset(pw, 0, sizeof(struct passwd)); - pw->pw_uid = uid; - pw->pw_name = (char *)pw + sizeof(struct passwd); - (void) snprintf(pw->pw_name, MAX_UID_T_LEN + 1, "#%lu", - (unsigned long) uid); - - /* Store by uid and by name, overwriting cached version. */ - if ((node = rbinsert(pwcache_byuid, pw)) != NULL) { - free(node->data); - node->data = (VOID *) pw; - } - if ((node = rbinsert(pwcache_byname, pw)) != NULL) { - free(node->data); - node->data = (VOID *) pw; - } - return(pw); -} - -/* - * Take a uid in string form "#123" and return a faked up passwd struct. - */ -struct passwd * -sudo_fakepwnam(user) - const char *user; -{ - struct passwd *pw; - struct rbnode *node; - size_t len; - - len = strlen(user); - pw = emalloc(sizeof(struct passwd) + len + 1); - memset(pw, 0, sizeof(struct passwd)); - pw->pw_uid = (uid_t) atoi(user + 1); - pw->pw_name = (char *)pw + sizeof(struct passwd); - strlcpy(pw->pw_name, user, len + 1); - - /* Store by uid and by name, overwriting cached version. */ - if ((node = rbinsert(pwcache_byuid, pw)) != NULL) { - free(node->data); - node->data = (VOID *) pw; - } - if ((node = rbinsert(pwcache_byname, pw)) != NULL) { - free(node->data); - node->data = (VOID *) pw; - } - return(pw); -} - void sudo_setpwent() { @@ -448,8 +258,6 @@ sudo_setpwent() #ifdef HAVE_GETAUTHUID setauthent(); #endif - pwcache_byuid = rbcreate(cmp_pwuid); - pwcache_byname = rbcreate(cmp_pwnam); } void @@ -471,125 +279,18 @@ sudo_endpwent() #ifdef HAVE_GETAUTHUID endauthent(); #endif - rbdestroy(pwcache_byuid, pw_free); - pwcache_byuid = NULL; - rbdestroy(pwcache_byname, NULL); - pwcache_byname = NULL; -} - -static void -pw_free(v) - VOID *v; -{ - struct passwd *pw = (struct passwd *) v; - - if (pw->pw_passwd != NULL) - zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd)); - free(pw); -} - -/* - * Compare by gid. - */ -static int -cmp_grgid(v1, v2) - const VOID *v1; - const VOID *v2; -{ - const struct group *grp1 = (const struct group *) v1; - const struct group *grp2 = (const struct group *) v2; - return(grp1->gr_gid - grp2->gr_gid); -} - -/* - * Compare by group name. - */ -static int -cmp_grnam(v1, v2) - const VOID *v1; - const VOID *v2; -{ - const struct group *grp1 = (const struct group *) v1; - const struct group *grp2 = (const struct group *) v2; - return(strcmp(grp1->gr_name, grp2->gr_name)); } void sudo_setgrent() { setgrent(); - grcache_bygid = rbcreate(cmp_grgid); - grcache_byname = rbcreate(cmp_grnam); } void sudo_endgrent() { endgrent(); - rbdestroy(grcache_bygid, free); - grcache_bygid = NULL; - rbdestroy(grcache_byname, NULL); - grcache_byname = NULL; -} - -static struct group * -sudo_grdup(gr) - const struct group *gr; -{ - char *cp; - size_t nsize, psize, csize, num, total, len; - struct group *newgr; - - /* Allocate in one big chunk for easy freeing. */ - nsize = psize = csize = num = 0; - total = sizeof(struct group); - if (gr->gr_name) { - nsize = strlen(gr->gr_name) + 1; - total += nsize; - } - if (gr->gr_passwd) { - psize = strlen(gr->gr_passwd) + 1; - total += psize; - } - if (gr->gr_mem) { - for (num = 0; gr->gr_mem[num] != NULL; num++) - total += strlen(gr->gr_mem[num]) + 1; - num++; - total += sizeof(char *) * num; - } - if ((cp = malloc(total)) == NULL) - return (NULL); - newgr = (struct group *)cp; - - /* - * Copy in group contents and make strings relative to space - * at the end of the buffer. - */ - (void)memcpy(newgr, gr, sizeof(struct group)); - cp += sizeof(struct group); - if (nsize) { - (void)memcpy(cp, gr->gr_name, nsize); - newgr->gr_name = cp; - cp += nsize; - } - if (psize) { - (void)memcpy(cp, gr->gr_passwd, psize); - newgr->gr_passwd = cp; - cp += psize; - } - if (gr->gr_mem) { - newgr->gr_mem = (char **)cp; - cp += sizeof(char *) * num; - for (num = 0; gr->gr_mem[num] != NULL; num++) { - len = strlen(gr->gr_mem[num]) + 1; - memcpy(cp, gr->gr_mem[num], len); - newgr->gr_mem[num] = cp; - cp += len; - } - newgr->gr_mem[num] = NULL; - } - - return (newgr); } /* @@ -600,28 +301,25 @@ sudo_getgrgid(gid) gid_t gid; { struct group key, *gr; - struct rbnode *node; key.gr_gid = gid; - if ((node = rbfind(grcache_bygid, &key)) != NULL) { - gr = (struct group *) node->data; - return(gr->gr_name != NULL ? gr : NULL); - } + if ((gr = pwcache_get(bygid, &key)) != NULL) + return(gr); /* * Cache group db entry if it exists or a negative response if not. */ if ((gr = getgrgid(gid)) != NULL) { gr = sudo_grdup(gr); - if (rbinsert(grcache_byname, (VOID *) gr) != NULL) + if (!pwcache_put(bygrnam, (VOID *) gr)) errorx(1, "unable to cache group name, already exists"); - if (rbinsert(grcache_bygid, (VOID *) gr) != NULL) + if (!pwcache_put(bygid, (VOID *) gr)) errorx(1, "unable to cache gid, already exists"); return(gr); } else { gr = emalloc(sizeof(*gr)); memset(gr, 0, sizeof(*gr)); gr->gr_gid = gid; - if (rbinsert(grcache_bygid, (VOID *) gr) != NULL) + if (!pwcache_put(bygid, (VOID *) gr)) errorx(1, "unable to cache gid, already exists"); return(NULL); } @@ -635,23 +333,20 @@ sudo_getgrnam(name) const char *name; { struct group key, *gr; - struct rbnode *node; size_t len; char *cp; key.gr_name = (char *) name; - if ((node = rbfind(grcache_byname, &key)) != NULL) { - gr = (struct group *) node->data; - return(gr->gr_gid != (gid_t) -1 ? gr : NULL); - } + if ((gr = pwcache_get(bygrnam, &key)) != NULL) + return(gr); /* * Cache group db entry if it exists or a negative response if not. */ if ((gr = getgrnam(name)) != NULL) { gr = sudo_grdup(gr); - if (rbinsert(grcache_byname, (VOID *) gr) != NULL) + if (!pwcache_put(bygrnam, (VOID *) gr)) errorx(1, "unable to cache group name, already exists"); - if (rbinsert(grcache_bygid, (VOID *) gr) != NULL) + if (!pwcache_put(bygid, (VOID *) gr)) errorx(1, "unable to cache gid, already exists"); return(gr); } else { @@ -663,7 +358,7 @@ sudo_getgrnam(name) memcpy(cp, name, len); gr->gr_name = cp; gr->gr_gid = (gid_t) -1; - if (rbinsert(grcache_byname, (VOID *) gr) != NULL) + if (!pwcache_put(bygrnam, (VOID *) gr)) errorx(1, "unable to cache group name, already exists"); return(NULL); } diff --git a/pwutil.c b/pwutil.c new file mode 100644 index 000000000..e6fdacd41 --- /dev/null +++ b/pwutil.c @@ -0,0 +1,444 @@ +/* + * Copyright (c) 1996, 1998-2005 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 +#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 +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif /* HAVE_STRING_H */ +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ +#include +#include + +#include "sudo.h" +#include "redblack.h" + +#ifndef lint +static const char rcsid[] = "$Sudo$"; +#endif /* lint */ + +/* + * The passwd and group caches. + */ +static struct rbtree *pwcache_byuid, *pwcache_byname; +static struct rbtree *grcache_bygid, *grcache_byname; + +static int cmp_pwuid __P((const VOID *, const VOID *)); +static int cmp_pwnam __P((const VOID *, const VOID *)); +static int cmp_grgid __P((const VOID *, const VOID *)); +static int cmp_grnam __P((const VOID *, const VOID *)); +static void pw_free __P((VOID *)); + VOID *pwcache_get __P((enum cmptype, VOID *)); + int pwcache_put __P((enum cmptype, VOID *)); + +void +pwcache_init() +{ + pwcache_byuid = rbcreate(cmp_pwuid); + pwcache_byname = rbcreate(cmp_pwnam); + grcache_bygid = rbcreate(cmp_grgid); + grcache_byname = rbcreate(cmp_grnam); +} + +void +pwcache_destroy() +{ + if (pwcache_byuid) { + rbdestroy(pwcache_byuid, pw_free); + pwcache_byuid = NULL; + } + if (pwcache_byname) { + rbdestroy(pwcache_byname, NULL); + pwcache_byname = NULL; + } + if (grcache_bygid) { + rbdestroy(grcache_bygid, free); + grcache_bygid = NULL; + } + if (grcache_byname) { + rbdestroy(grcache_byname, NULL); + grcache_byname = NULL; + } +} + +/* + * Get an entry in the passwd/group cache. + */ +VOID * +pwcache_get(how, key) + enum cmptype how; + VOID *key; +{ + struct rbnode *node; + struct passwd *pw; + struct group *gr; + + switch (how) { + case bypwnam: + if ((node = rbfind(pwcache_byname, key)) != NULL) { + pw = (struct passwd *) node->data; + return(pw->pw_uid != (uid_t) -1 ? pw : NULL); + } + break; + case byuid: + if ((node = rbfind(pwcache_byuid, key)) != NULL) { + pw = (struct passwd *) node->data; + return(pw->pw_name != NULL ? pw : NULL); + } + break; + case bygrnam: + if ((node = rbfind(grcache_bygid, key)) != NULL) { + gr = (struct group *) node->data; + return(gr->gr_gid != (gid_t) -1 ? gr : NULL); + } + break; + case bygid: + if ((node = rbfind(grcache_bygid, key)) != NULL) { + gr = (struct group *) node->data; + return(gr->gr_name != NULL ? gr : NULL); + } + break; + } + return(NULL); +} + +/* + * Store an entry in the passwd/group cache. + * Returns TRUE on success and FALSE if the entry already exists. + */ +int +pwcache_put(how, data) + enum cmptype how; + VOID *data; +{ + switch (how) { + case bypwnam: + return(rbinsert(pwcache_byname, data) == NULL); + break; + case byuid: + return(rbinsert(pwcache_byuid, data) == NULL); + break; + case bygrnam: + return(rbinsert(grcache_byname, data) == NULL); + break; + case bygid: + return(rbinsert(grcache_bygid, data) == NULL); + break; + } + return(FALSE); +} + +/* + * Compare by uid. + */ +static int +cmp_pwuid(v1, v2) + const VOID *v1; + const VOID *v2; +{ + const struct passwd *pw1 = (const struct passwd *) v1; + const struct passwd *pw2 = (const struct passwd *) v2; + return(pw1->pw_uid - pw2->pw_uid); +} + +/* + * Compare by user name. + */ +static int +cmp_pwnam(v1, v2) + const VOID *v1; + const VOID *v2; +{ + const struct passwd *pw1 = (const struct passwd *) v1; + const struct passwd *pw2 = (const struct passwd *) v2; + return(strcmp(pw1->pw_name, pw2->pw_name)); +} + +/* + * Dynamically allocate space for a struct password and the constituent parts + * that we care about. Fills in pw_passwd from shadow file. + */ +struct passwd * +sudo_pwdup(pw) + const struct passwd *pw; +{ + char *cp; + const char *pw_passwd, *pw_shell; + size_t nsize, psize, csize, gsize, dsize, ssize, total; + struct passwd *newpw; + + /* Get shadow password if available. */ + pw_passwd = sudo_getepw(pw); + + /* 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 passwd); + if (pw->pw_name) { + nsize = strlen(pw->pw_name) + 1; + total += nsize; + } + if (pw_passwd) { + psize = strlen(pw_passwd) + 1; + total += psize; + } +#ifdef HAVE_LOGIN_CAP_H + if (pw->pw_class) { + csize = strlen(pw->pw_class) + 1; + total += csize; + } +#endif + if (pw->pw_gecos) { + gsize = strlen(pw->pw_gecos) + 1; + total += gsize; + } + if (pw->pw_dir) { + dsize = strlen(pw->pw_dir) + 1; + total += dsize; + } + if (pw_shell) { + ssize = strlen(pw_shell) + 1; + total += ssize; + } + if ((cp = malloc(total)) == NULL) + return(NULL); + newpw = (struct passwd *)cp; + + /* + * Copy in passwd contents and make strings relative to space + * at the end of the buffer. + */ + (void)memcpy(newpw, pw, sizeof(struct passwd)); + cp += sizeof(struct passwd); + if (nsize) { + (void)memcpy(cp, pw->pw_name, nsize); + newpw->pw_name = cp; + cp += nsize; + } + if (psize) { + (void)memcpy(cp, pw_passwd, psize); + newpw->pw_passwd = cp; + cp += psize; + } +#ifdef HAVE_LOGIN_CAP_H + if (csize) { + (void)memcpy(cp, pw->pw_class, csize); + newpw->pw_class = cp; + cp += csize; + } +#endif + if (gsize) { + (void)memcpy(cp, pw->pw_gecos, gsize); + newpw->pw_gecos = cp; + cp += gsize; + } + if (dsize) { + (void)memcpy(cp, pw->pw_dir, dsize); + newpw->pw_dir = cp; + cp += dsize; + } + if (ssize) { + (void)memcpy(cp, pw_shell, ssize); + newpw->pw_shell = cp; + cp += ssize; + } + + return(newpw); +} + +/* + * Take a uid and return a faked up passwd struct. + */ +struct passwd * +sudo_fakepwuid(uid) + uid_t uid; +{ + struct passwd *pw; + struct rbnode *node; + + pw = emalloc(sizeof(struct passwd) + MAX_UID_T_LEN + 1); + memset(pw, 0, sizeof(struct passwd)); + pw->pw_uid = uid; + pw->pw_name = (char *)pw + sizeof(struct passwd); + (void) snprintf(pw->pw_name, MAX_UID_T_LEN + 1, "#%lu", + (unsigned long) uid); + + /* Store by uid and by name, overwriting cached version. */ + if ((node = rbinsert(pwcache_byuid, pw)) != NULL) { + free(node->data); + node->data = (VOID *) pw; + } + if ((node = rbinsert(pwcache_byname, pw)) != NULL) { + free(node->data); + node->data = (VOID *) pw; + } + return(pw); +} + +/* + * Take a uid in string form "#123" and return a faked up passwd struct. + */ +struct passwd * +sudo_fakepwnam(user) + const char *user; +{ + struct passwd *pw; + struct rbnode *node; + size_t len; + + len = strlen(user); + pw = emalloc(sizeof(struct passwd) + len + 1); + memset(pw, 0, sizeof(struct passwd)); + pw->pw_uid = (uid_t) atoi(user + 1); + pw->pw_name = (char *)pw + sizeof(struct passwd); + strlcpy(pw->pw_name, user, len + 1); + + /* Store by uid and by name, overwriting cached version. */ + if ((node = rbinsert(pwcache_byuid, pw)) != NULL) { + free(node->data); + node->data = (VOID *) pw; + } + if ((node = rbinsert(pwcache_byname, pw)) != NULL) { + free(node->data); + node->data = (VOID *) pw; + } + return(pw); +} + +static void +pw_free(v) + VOID *v; +{ + struct passwd *pw = (struct passwd *) v; + + if (pw->pw_passwd != NULL) + zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd)); + free(pw); +} + +/* + * Compare by gid. + */ +static int +cmp_grgid(v1, v2) + const VOID *v1; + const VOID *v2; +{ + const struct group *grp1 = (const struct group *) v1; + const struct group *grp2 = (const struct group *) v2; + return(grp1->gr_gid - grp2->gr_gid); +} + +/* + * Compare by group name. + */ +static int +cmp_grnam(v1, v2) + const VOID *v1; + const VOID *v2; +{ + const struct group *grp1 = (const struct group *) v1; + const struct group *grp2 = (const struct group *) v2; + return(strcmp(grp1->gr_name, grp2->gr_name)); +} + +struct group * +sudo_grdup(gr) + const struct group *gr; +{ + char *cp; + size_t nsize, psize, csize, num, total, len; + struct group *newgr; + + /* Allocate in one big chunk for easy freeing. */ + nsize = psize = csize = num = 0; + total = sizeof(struct group); + if (gr->gr_name) { + nsize = strlen(gr->gr_name) + 1; + total += nsize; + } + if (gr->gr_passwd) { + psize = strlen(gr->gr_passwd) + 1; + total += psize; + } + if (gr->gr_mem) { + for (num = 0; gr->gr_mem[num] != NULL; num++) + total += strlen(gr->gr_mem[num]) + 1; + num++; + total += sizeof(char *) * num; + } + if ((cp = malloc(total)) == NULL) + return(NULL); + newgr = (struct group *)cp; + + /* + * Copy in group contents and make strings relative to space + * at the end of the buffer. + */ + (void)memcpy(newgr, gr, sizeof(struct group)); + cp += sizeof(struct group); + if (nsize) { + (void)memcpy(cp, gr->gr_name, nsize); + newgr->gr_name = cp; + cp += nsize; + } + if (psize) { + (void)memcpy(cp, gr->gr_passwd, psize); + newgr->gr_passwd = cp; + cp += psize; + } + if (gr->gr_mem) { + newgr->gr_mem = (char **)cp; + cp += sizeof(char *) * num; + for (num = 0; gr->gr_mem[num] != NULL; num++) { + len = strlen(gr->gr_mem[num]) + 1; + memcpy(cp, gr->gr_mem[num], len); + newgr->gr_mem[num] = cp; + cp += len; + } + newgr->gr_mem[num] = NULL; + } + + return(newgr); +}