]> granicus.if.org Git - sudo/commitdiff
Cache passwd db entries in 2 reb-black trees; one indexed by uid,
authorTodd C. Miller <Todd.Miller@courtesan.com>
Mon, 15 Nov 2004 14:53:05 +0000 (14:53 +0000)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Mon, 15 Nov 2004 14:53:05 +0000 (14:53 +0000)
the other by user name.  The data returned from the cache should
be considered read-only and is destroyed by sudo_endpwent().

getspwuid.c
mon_systrace.c
mon_systrace.h
sudo.c

index 1bfed0ff33280c3def128d4ce9414689fec8a2b9..b6e1d01f01e8f22389f41e2ba9883090ebd3bb3f 100644 (file)
@@ -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;
 }
index f7b2384d149445a7aeeabfce2b691bc7194a67db..c7f727df9e254304ce6d9b878c8480ebe619acf1 100644 (file)
@@ -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);
     }
 }
 
index 24206ecf6993655778a4a03354ae9f5dd554e9a2..8441cf4469777605e394e06afa8fa61e5af882fc 100644 (file)
@@ -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 d9b8357600a052cf636c5804c89112b08ef9a7a6..b4d6f8d211a328ba311df997f2a0cee80782ff0d 100644 (file)
--- 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) {