From: Todd C. Miller Date: Mon, 15 Nov 2004 14:53:05 +0000 (+0000) Subject: Cache passwd db entries in 2 reb-black trees; one indexed by uid, X-Git-Tag: SUDO_1_7_0~833 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ae2e26fd2f58424e627f3a0cda6277b5cb4ec10d;p=sudo Cache passwd db entries in 2 reb-black trees; one indexed by uid, the other by user name. The data returned from the cache should be considered read-only and is destroyed by sudo_endpwent(). --- diff --git a/getspwuid.c b/getspwuid.c index 1bfed0ff3..b6e1d01f0 100644 --- a/getspwuid.c +++ b/getspwuid.c @@ -68,6 +68,7 @@ #endif /* HAVE_GETAUTHUID */ #include "sudo.h" +#include "redblack.h" #ifndef lint static const char rcsid[] = "$Sudo$"; @@ -79,6 +80,37 @@ static const char rcsid[] = "$Sudo$"; #if defined(HAVE_GETPRPWNAM) && defined(__alpha) int crypt_type = INT_MAX; #endif /* HAVE_GETPRPWNAM && __alpha */ +static struct rbtree *cache_byuid; +static struct rbtree *cache_byname; + +static int cmp_byuid __P((const VOID *, const VOID *)); +static int cmp_byname __P((const VOID *, const VOID *)); + +/* + * Compare by uid. + */ +static int +cmp_byuid(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_byname(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)); +} /* * Return a copy of the encrypted password for the user described by pw. @@ -162,12 +194,11 @@ sudo_getepw(pw) /* * Dynamically allocate space for a struct password and the constituent parts - * that we care about. Fills in pw_passwd from shadow file if necessary. + * that we care about. Fills in pw_passwd from shadow file. */ struct passwd * -sudo_pwdup(pw, checkshadow) +sudo_pwdup(pw) const struct passwd *pw; - int checkshadow; { char *cp; const char *pw_passwd, *pw_shell; @@ -175,7 +206,7 @@ sudo_pwdup(pw, checkshadow) struct passwd *newpw; /* Get shadow password if available. */ - pw_passwd = checkshadow ? sudo_getepw(pw) : pw->pw_passwd; + 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') @@ -264,12 +295,19 @@ struct passwd * sudo_getpwuid(uid) uid_t uid; { - struct passwd *pw; + struct passwd key, *pw; + struct rbnode *node; + key.pw_uid = uid; + if ((node = rbfind(cache_byuid, &key)) != NULL) + return((struct passwd *) node->data); if ((pw = getpwuid(uid)) == NULL) return(NULL); else - return(sudo_pwdup(pw, 1)); + pw = sudo_pwdup(pw); + rbinsert(cache_byname, (VOID *) pw); + rbinsert(cache_byuid, (VOID *) pw); + return(pw); } /* @@ -280,12 +318,60 @@ struct passwd * sudo_getpwnam(name) const char *name; { - struct passwd *pw; + struct passwd key, *pw; + struct rbnode *node; + key.pw_name = (char *) name; + if ((node = rbfind(cache_byname, &key)) != NULL) + return((struct passwd *) node->data); if ((pw = getpwnam(name)) == NULL) return(NULL); else - return(sudo_pwdup(pw, 1)); + pw = sudo_pwdup(pw); + rbinsert(cache_byname, (VOID *) pw); + rbinsert(cache_byuid, (VOID *) pw); + return(pw); +} + +/* + * Take a uid and return a faked up passwd struct. + */ +struct passwd * +sudo_fakepwuid(uid) + uid_t uid; +{ + struct passwd *pw; + + 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); + rbinsert(cache_byname, (VOID *) pw); + rbinsert(cache_byuid, (VOID *) pw); + return(pw); +} + +/* + * Take a uid in string form "#123" and return a faked up passwd struct. + */ +struct passwd * +sudo_fakepwnam(user) + char *user; +{ + struct passwd *pw; + 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); + rbinsert(cache_byname, (VOID *) pw); + rbinsert(cache_byuid, (VOID *) pw); + return(pw); } void @@ -307,6 +393,8 @@ sudo_setpwent() #ifdef HAVE_GETAUTHUID setauthent(); #endif + cache_byuid = rbcreate(cmp_byuid); + cache_byname = rbcreate(cmp_byname); } void @@ -328,4 +416,8 @@ sudo_endpwent() #ifdef HAVE_GETAUTHUID endauthent(); #endif + rbdestroy(cache_byuid, (void (*)__P((VOID *))) free); + cache_byuid = NULL; + rbdestroy(cache_byname, NULL); + cache_byname = NULL; } diff --git a/mon_systrace.c b/mon_systrace.c index f7b2384d1..c7f727df9 100644 --- a/mon_systrace.c +++ b/mon_systrace.c @@ -318,7 +318,7 @@ new_child(ppid, pid) } entry = (struct childinfo *) emalloc(sizeof(*entry)); entry->pid = pid; - entry->pw = sudo_pwdup(pw, 0); + entry->pw = pw; entry->action = action; entry->next = children.first; children.first = entry; @@ -357,7 +357,6 @@ rm_child(pid) prev->next = cur->next; else children.first = cur->next; - free(cur->pw); free(cur); break; } @@ -395,16 +394,9 @@ update_child(pid, uid) return; /* cannot happen */ if (child->pw->pw_uid != uid) { - free(child->pw); - /* lookup uid in passwd db, using a stub on failure */ - if ((child->pw = sudo_getpwuid(uid)) == NULL) { - child->pw = emalloc(sizeof(struct passwd) + MAX_UID_T_LEN + 1); - memset(child->pw, 0, sizeof(struct passwd)); - child->pw->pw_uid = uid; - child->pw->pw_name = (char *)child->pw + sizeof(struct passwd); - (void) snprintf(child->pw->pw_name, MAX_UID_T_LEN + 1, "%lu", - (unsigned long) uid); - } + /* look up uid in passwd db, using a stub on failure */ + if ((child->pw = sudo_getpwuid(uid)) == NULL) + child->pw = sudo_fakepwuid(uid); } } diff --git a/mon_systrace.h b/mon_systrace.h index 24206ecf6..8441cf446 100644 --- a/mon_systrace.h +++ b/mon_systrace.h @@ -22,8 +22,8 @@ typedef int (*schandler_t) struct childinfo; struct listhead; -extern struct passwd *sudo_pwdup __P((const struct passwd *, int)); extern struct passwd *sudo_getpwuid __P((uid_t)); +extern struct passwd *sudo_fakepwuid __P((uid_t)); static int check_execv __P((int, pid_t, u_int16_t, struct str_msg_ask *, int, int *, int *)); diff --git a/sudo.c b/sudo.c index d9b835760..b4d6f8d21 100644 --- a/sudo.c +++ b/sudo.c @@ -112,6 +112,7 @@ static struct passwd *get_authpw __P((void)); extern int sudo_edit __P((int, char **)); extern char **rebuild_env __P((char **, int, int)); extern char **zero_env __P((char **)); +extern struct passwd *sudo_fakepwnam __P((const char *)); extern struct passwd *sudo_getpwnam __P((const char *)); extern struct passwd *sudo_getpwuid __P((uid_t)); @@ -339,12 +340,10 @@ main(argc, argv, envp) /* XXX - causes confusion when root is not listed in sudoers */ if (sudo_mode & (MODE_RUN | MODE_EDIT) && prev_user != NULL) { if (user_uid == 0 && strcmp(prev_user, "root") != 0) { - struct passwd *pw; + struct passwd *pw; - if ((pw = sudo_getpwnam(prev_user)) != NULL) { - free(sudo_user.pw); - sudo_user.pw = pw; - } + if ((pw = sudo_getpwnam(prev_user)) != NULL) + sudo_user.pw = pw; } } @@ -568,7 +567,7 @@ init_vars(sudo_mode) log_error(USE_ERRNO|MSG_ONLY, "can't get hostname"); set_runaspw(*user_runas); /* may call log_error() */ - if (*user_runas[0] == '#' && runas_pw->pw_name && runas_pw->pw_name[0]) + if (*user_runas[0] == '#' && runas_pw->pw_name[0] != '#') *user_runas = estrdup(runas_pw->pw_name); /* @@ -1035,18 +1034,12 @@ set_runaspw(user) if (runas_pw != NULL) { if (user_runas != &def_runas_default) return(TRUE); /* don't override -u option */ - free(runas_pw); } if (*user == '#') { - runas_pw = sudo_getpwuid(atoi(user + 1)); - if (runas_pw == NULL) { - runas_pw = emalloc(sizeof(struct passwd)); - (void) memset((VOID *)runas_pw, 0, sizeof(struct passwd)); - runas_pw->pw_uid = atoi(user + 1); - } + if ((runas_pw = sudo_getpwuid(atoi(user + 1))) == NULL) + runas_pw = sudo_fakepwnam(user); } else { - runas_pw = sudo_getpwnam(user); - if (runas_pw == NULL) + if ((runas_pw = sudo_getpwnam(user)) == NULL) log_error(NO_MAIL|MSG_ONLY, "no passwd entry for %s!", user); } return(TRUE); @@ -1063,14 +1056,10 @@ get_authpw() struct passwd *pw; if (def_rootpw) { - if (runas_pw->pw_uid == 0) - pw = runas_pw; - else if ((pw = sudo_getpwuid(0)) == NULL) + if ((pw = sudo_getpwuid(0)) == NULL) log_error(0, "uid 0 does not exist in the passwd file!"); } else if (def_runaspw) { - if (strcmp(def_runas_default, *user_runas) == 0) - pw = runas_pw; - else if ((pw = sudo_getpwnam(def_runas_default)) == NULL) + if ((pw = sudo_getpwnam(def_runas_default)) == NULL) log_error(0, "user %s does not exist in the passwd file!", def_runas_default); } else if (def_targetpw) {