]> granicus.if.org Git - sudo/commitdiff
o Move userspecs, defaults and aliases into a new struct sudoers_parse_tree.
authorTodd C. Miller <Todd.Miller@sudo.ws>
Thu, 26 Jul 2018 21:12:33 +0000 (15:12 -0600)
committerTodd C. Miller <Todd.Miller@sudo.ws>
Thu, 26 Jul 2018 21:12:33 +0000 (15:12 -0600)
o The parse tree is now passed to the alias, match and defaults functions.
o The nss API has been changed so that the nss parse() function returns
  a pointer to a struct sudoers_parse_tree which will be filled in
  by the getdefs() and query() functions.

19 files changed:
plugins/sudoers/alias.c
plugins/sudoers/cvtsudoers.c
plugins/sudoers/cvtsudoers_json.c
plugins/sudoers/cvtsudoers_ldif.c
plugins/sudoers/defaults.c
plugins/sudoers/defaults.h
plugins/sudoers/file.c
plugins/sudoers/fmtsudoers.c
plugins/sudoers/gram.c
plugins/sudoers/gram.y
plugins/sudoers/ldap.c
plugins/sudoers/match.c
plugins/sudoers/parse.c
plugins/sudoers/parse.h
plugins/sudoers/sssd.c
plugins/sudoers/sudo_nss.h
plugins/sudoers/sudoers.c
plugins/sudoers/testsudoers.c
plugins/sudoers/visudo.c

index b15150d72a17cc8e830925e66c591b4560465907..c66241159aa76a30c7dec5b8c52c8d39208613a6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2005, 2007-2016
+ * Copyright (c) 2004-2005, 2007-2018
  *     Todd C. Miller <Todd.Miller@sudo.ws>
  *
  * Permission to use, copy, modify, and distribute this software for any
 #include "redblack.h"
 #include <gram.h>
 
-/*
- * Globals
- */
-static struct rbtree *aliases;
-
 /*
  * Comparison function for the red-black tree.
  * Aliases are sorted by name with the type used as a tie-breaker.
  */
-int
+static int
 alias_compare(const void *v1, const void *v2)
 {
     const struct alias *a1 = (const struct alias *)v1;
@@ -68,16 +63,19 @@ alias_compare(const void *v1, const void *v2)
  * alias to mark it as unused.
  */
 struct alias *
-alias_get(const char *name, int type)
+alias_get(struct sudoers_parse_tree *parse_tree, const char *name, int type)
 {
     struct alias key;
     struct rbnode *node;
     struct alias *a = NULL;
     debug_decl(alias_get, SUDOERS_DEBUG_ALIAS)
 
+    if (parse_tree->aliases == NULL)
+       debug_return_ptr(NULL);
+
     key.name = (char *)name;
     key.type = type;
-    if ((node = rbfind(aliases, &key)) != NULL) {
+    if ((node = rbfind(parse_tree->aliases, &key)) != NULL) {
        /*
         * Check whether this alias is already in use.
         * If so, we've detected a loop.  If not, set the flag,
@@ -112,12 +110,20 @@ alias_put(struct alias *a)
  * Returns NULL on success and an error string on failure.
  */
 const char *
-alias_add(char *name, int type, char *file, int lineno, struct member *members)
+alias_add(struct sudoers_parse_tree *parse_tree, char *name, int type,
+    char *file, int lineno, struct member *members)
 {
     static char errbuf[512];
     struct alias *a;
     debug_decl(alias_add, SUDOERS_DEBUG_ALIAS)
 
+    if (parse_tree->aliases == NULL) {
+       if ((parse_tree->aliases = alloc_aliases()) == NULL) {
+           strlcpy(errbuf, N_("unable to allocate memory"), sizeof(errbuf));
+           debug_return_str(errbuf);
+       }
+    }
+
     a = calloc(1, sizeof(*a));
     if (a == NULL) {
        strlcpy(errbuf, N_("unable to allocate memory"), sizeof(errbuf));
@@ -129,9 +135,10 @@ alias_add(char *name, int type, char *file, int lineno, struct member *members)
     a->file = rcstr_addref(file);
     a->lineno = lineno;
     HLTQ_TO_TAILQ(&a->members, members, entries);
-    switch (rbinsert(aliases, a, NULL)) {
+    switch (rbinsert(parse_tree->aliases, a, NULL)) {
     case 1:
-       snprintf(errbuf, sizeof(errbuf), N_("Alias \"%s\" already defined"), name);
+       snprintf(errbuf, sizeof(errbuf), N_("Alias \"%s\" already defined"),
+           name);
        alias_free(a);
        debug_return_str(errbuf);
     case -1:
@@ -146,37 +153,26 @@ alias_add(char *name, int type, char *file, int lineno, struct member *members)
  * Apply a function to each alias entry and pass in a cookie.
  */
 void
-alias_apply(int (*func)(void *, void *), void *cookie)
+alias_apply(struct sudoers_parse_tree *parse_tree, int (*func)(void *, void *),
+    void *cookie)
 {
     debug_decl(alias_apply, SUDOERS_DEBUG_ALIAS)
 
-    rbapply(aliases, func, cookie, inorder);
+    if (parse_tree->aliases != NULL)
+       rbapply(parse_tree->aliases, func, cookie, inorder);
 
     debug_return;
 }
 
 /*
- * Returns true if there are no aliases, else false.
+ * Returns true if there are no aliases in the parse_tree, else false.
  */
 bool
-no_aliases(void)
+no_aliases(struct sudoers_parse_tree *parse_tree)
 {
     debug_decl(no_aliases, SUDOERS_DEBUG_ALIAS)
-    debug_return_bool(rbisempty(aliases));
-}
-
-/*
- * Replace the aliases tree with a new one, returns the old.
- */
-struct rbtree *
-replace_aliases(struct rbtree *new_aliases)
-{
-    struct rbtree *old_aliases = aliases;
-    debug_decl(replace_aliases, SUDOERS_DEBUG_ALIAS)
-
-    aliases = new_aliases;
-
-    debug_return_ptr(old_aliases);
+    debug_return_bool(parse_tree->aliases == NULL ||
+       rbisempty(parse_tree->aliases));
 }
 
 /*
@@ -202,31 +198,37 @@ alias_free(void *v)
  * Find the named alias, remove it from the tree and return it.
  */
 struct alias *
-alias_remove(char *name, int type)
+alias_remove(struct sudoers_parse_tree *parse_tree, char *name, int type)
 {
     struct rbnode *node;
     struct alias key;
     debug_decl(alias_remove, SUDOERS_DEBUG_ALIAS)
 
-    key.name = name;
-    key.type = type;
-    if ((node = rbfind(aliases, &key)) == NULL) {
-       errno = ENOENT;
-       return NULL;
+    if (parse_tree->aliases != NULL) {
+       key.name = name;
+       key.type = type;
+       if ((node = rbfind(parse_tree->aliases, &key)) != NULL)
+           debug_return_ptr(rbdelete(parse_tree->aliases, node));
     }
-    debug_return_ptr(rbdelete(aliases, node));
+    errno = ENOENT;
+    debug_return_ptr(NULL);
 }
 
-bool
-init_aliases(void)
+struct rbtree *
+alloc_aliases(void)
+{
+    debug_decl(alloc_aliases, SUDOERS_DEBUG_ALIAS)
+
+    debug_return_ptr(rbcreate(alias_compare));
+}
+
+void
+free_aliases(struct rbtree *aliases)
 {
-    debug_decl(init_aliases, SUDOERS_DEBUG_ALIAS)
+    debug_decl(free_aliases, SUDOERS_DEBUG_ALIAS)
 
     if (aliases != NULL)
        rbdestroy(aliases, alias_free);
-    aliases = rbcreate(alias_compare);
-
-    debug_return_bool(aliases != NULL);
 }
 
 const char *
@@ -244,17 +246,18 @@ alias_type_to_string(int alias_type)
  * referenced by that alias.  Stores removed aliases in a freelist.
  */
 static bool
-alias_remove_recursive(char *name, int type, struct rbtree *freelist)
+alias_remove_recursive(struct sudoers_parse_tree *parse_tree, char *name,
+    int type, struct rbtree *freelist)
 {
     struct member *m;
     struct alias *a;
     bool ret = true;
     debug_decl(alias_remove_recursive, SUDOERS_DEBUG_ALIAS)
 
-    if ((a = alias_remove(name, type)) != NULL) {
+    if ((a = alias_remove(parse_tree, name, type)) != NULL) {
        TAILQ_FOREACH(m, &a->members, entries) {
            if (m->type == ALIAS) {
-               if (!alias_remove_recursive(m->name, type, freelist))
+               if (!alias_remove_recursive(parse_tree, m->name, type, freelist))
                    ret = false;
            }
        }
@@ -264,81 +267,80 @@ alias_remove_recursive(char *name, int type, struct rbtree *freelist)
     debug_return_bool(ret);
 }
 
+static int
+alias_find_used_members(struct sudoers_parse_tree *parse_tree,
+    struct member_list *members, int atype, struct rbtree *used_aliases)
+{
+    struct member *m;
+    int errors = 0;
+    debug_decl(alias_find_used_members, SUDOERS_DEBUG_ALIAS)
+
+    if (members != NULL) {
+       TAILQ_FOREACH(m, members, entries) {
+           if (m->type != ALIAS)
+               continue;
+           if (!alias_remove_recursive(parse_tree, m->name, atype, used_aliases))
+               errors++;
+       }
+    }
+
+    debug_return_int(errors);
+}
+
 /*
  * Move all aliases referenced by userspecs to used_aliases.
  */
 bool
-alias_find_used(struct rbtree *used_aliases)
+alias_find_used(struct sudoers_parse_tree *parse_tree, struct rbtree *used_aliases)
 {
     struct privilege *priv;
     struct userspec *us;
     struct cmndspec *cs;
     struct defaults *d;
     struct member *m;
-    int atype, errors = 0;
+    int errors = 0;
     debug_decl(alias_find_used, SUDOERS_DEBUG_ALIAS)
 
     /* Move referenced aliases to used_aliases. */
-    TAILQ_FOREACH(us, &userspecs, entries) {
-       TAILQ_FOREACH(m, &us->users, entries) {
-           if (m->type == ALIAS) {
-               if (!alias_remove_recursive(m->name, USERALIAS, used_aliases))
-                   errors++;
-           }
-       }
+    TAILQ_FOREACH(us, &parse_tree->userspecs, entries) {
+       errors += alias_find_used_members(parse_tree, &us->users,
+           USERALIAS, used_aliases);
        TAILQ_FOREACH(priv, &us->privileges, entries) {
-           TAILQ_FOREACH(m, &priv->hostlist, entries) {
-               if (m->type == ALIAS) {
-                   if (!alias_remove_recursive(m->name, HOSTALIAS, used_aliases))
-                       errors++;
-               }
-           }
+           errors += alias_find_used_members(parse_tree, &priv->hostlist,
+               HOSTALIAS, used_aliases);
            TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
-               if (cs->runasuserlist != NULL) {
-                   TAILQ_FOREACH(m, cs->runasuserlist, entries) {
-                       if (m->type == ALIAS) {
-                           if (!alias_remove_recursive(m->name, RUNASALIAS, used_aliases))
-                               errors++;
-                       }
-                   }
-               }
-               if (cs->runasgrouplist != NULL) {
-                   TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
-                       if (m->type == ALIAS) {
-                           if (!alias_remove_recursive(m->name, RUNASALIAS, used_aliases))
-                               errors++;
-                       }
-                   }
-               }
+               errors += alias_find_used_members(parse_tree, cs->runasuserlist,
+                   RUNASALIAS, used_aliases);
+               errors += alias_find_used_members(parse_tree, cs->runasgrouplist,
+                   RUNASALIAS, used_aliases);
                if ((m = cs->cmnd)->type == ALIAS) {
-                   if (!alias_remove_recursive(m->name, CMNDALIAS, used_aliases))
+                   if (!alias_remove_recursive(parse_tree, m->name, CMNDALIAS,
+                       used_aliases))
                        errors++;
                }
            }
        }
     }
-    TAILQ_FOREACH(d, &defaults, entries) {
+    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
        switch (d->type) {
            case DEFAULTS_HOST:
-               atype = HOSTALIAS;
+               errors += alias_find_used_members(parse_tree, d->binding,
+                   HOSTALIAS, used_aliases);
                break;
            case DEFAULTS_USER:
-               atype = USERALIAS;
+               errors += alias_find_used_members(parse_tree, d->binding,
+                   USERALIAS, used_aliases);
                break;
            case DEFAULTS_RUNAS:
-               atype = RUNASALIAS;
+               errors += alias_find_used_members(parse_tree, d->binding,
+                   RUNASALIAS, used_aliases);
                break;
            case DEFAULTS_CMND:
-               atype = CMNDALIAS;
+               errors += alias_find_used_members(parse_tree, d->binding,
+                   CMNDALIAS, used_aliases);
                break;
            default:
-               continue; /* not an alias */
-       }
-       TAILQ_FOREACH(m, d->binding, entries) {
-           if (m->type == ALIAS) {
-               if (!alias_remove_recursive(m->name, atype, used_aliases))
-                   errors++;
-           }
+               break;
        }
     }
 
index f84e4a2aa694cc515e72514cd9de9edd4685c787..e086d7debc01ae937325bf9d1ef81b94498cab84 100644 (file)
@@ -703,7 +703,7 @@ userlist_matches_filter(struct member_list *users, struct cvtsudoers_config *con
            pw.pw_uid = (uid_t)-1;
            pw.pw_gid = (gid_t)-1;
 
-           if (user_matches(&pw, m) == true)
+           if (user_matches(&parsed_policy, &pw, m) == true)
                matched = true;
        } else {
            STAILQ_FOREACH(s, &filters->users, entries) {
@@ -729,7 +729,7 @@ userlist_matches_filter(struct member_list *users, struct cvtsudoers_config *con
                if (pw == NULL)
                    continue;
 
-               if (user_matches(pw, m) == true)
+               if (user_matches(&parsed_policy, pw, m) == true)
                    matched = true;
                sudo_pw_delref(pw);
 
@@ -804,7 +804,7 @@ hostlist_matches_filter(struct member_list *hostlist, struct cvtsudoers_config *
 
            /* Only need one host in the filter to match. */
            /* XXX - can't use netgroup_tuple with NULL pw */
-           if (host_matches(NULL, lhost, shost, m) == true) {
+           if (host_matches(&parsed_policy, NULL, lhost, shost, m) == true) {
                matched = true;
                break;
            }
@@ -840,8 +840,10 @@ print_defaults_sudoers(struct sudo_lbuf *lbuf, bool expand_aliases)
     struct defaults *def, *next;
     debug_decl(print_defaults_sudoers, SUDOERS_DEBUG_UTIL)
 
-    TAILQ_FOREACH_SAFE(def, &defaults, entries, next)
-       sudoers_format_default_line(lbuf, def, &next, expand_aliases);
+    TAILQ_FOREACH_SAFE(def, &parsed_policy.defaults, entries, next) {
+       sudoers_format_default_line(lbuf, &parsed_policy, def, &next,
+           expand_aliases);
+    }
 
     debug_return_bool(!sudo_lbuf_error(lbuf));
 }
@@ -859,7 +861,7 @@ print_alias_sudoers(void *v1, void *v2)
     TAILQ_FOREACH(m, &a->members, entries) {
        if (m != TAILQ_FIRST(&a->members))
            sudo_lbuf_append(lbuf, ", ");
-       sudoers_format_member(lbuf, m, NULL, UNSPEC);
+       sudoers_format_member(lbuf, &parsed_policy, m, NULL, UNSPEC);
     }
     sudo_lbuf_append(lbuf, "\n");
 
@@ -874,7 +876,7 @@ print_aliases_sudoers(struct sudo_lbuf *lbuf)
 {
     debug_decl(print_aliases_sudoers, SUDOERS_DEBUG_UTIL)
 
-    alias_apply(print_alias_sudoers, lbuf);
+    alias_apply(&parsed_policy, print_alias_sudoers, lbuf);
 
     debug_return_bool(!sudo_lbuf_error(lbuf));
 }
@@ -905,9 +907,9 @@ filter_userspecs(struct cvtsudoers_config *conf)
      * host lists.  It acts more like a grep than a true filter.
      * In the future, we may want to add a prune option.
      */
-    TAILQ_FOREACH_SAFE(us, &userspecs, entries, next_us) {
+    TAILQ_FOREACH_SAFE(us, &parsed_policy.userspecs, entries, next_us) {
        if (!userlist_matches_filter(&us->users, conf)) {
-           TAILQ_REMOVE(&userspecs, us, entries);
+           TAILQ_REMOVE(&parsed_policy.userspecs, us, entries);
            free_userspec(us);
            continue;
        }
@@ -918,7 +920,7 @@ filter_userspecs(struct cvtsudoers_config *conf)
            }
        }
        if (TAILQ_EMPTY(&us->privileges)) {
-           TAILQ_REMOVE(&userspecs, us, entries);
+           TAILQ_REMOVE(&parsed_policy.userspecs, us, entries);
            free_userspec(us);
            continue;
        }
@@ -942,7 +944,8 @@ alias_matches(const char *name, const char *alias_name, int alias_type)
     if (strcmp(name, alias_name) == 0)
        debug_return_bool(true);
 
-    if ((a = alias_get(alias_name, alias_type)) != NULL) {
+    a = alias_get(&parsed_policy, alias_name, alias_type);
+    if (a != NULL) {
        TAILQ_FOREACH(m, &a->members, entries) {
            if (m->type != ALIAS)
                continue;
@@ -975,7 +978,7 @@ alias_used_by_userspecs(struct member_list *user_aliases,
     debug_decl(alias_used_by_userspecs, SUDOERS_DEBUG_ALIAS)
 
     /* Iterate over the policy, checking for aliases. */
-    TAILQ_FOREACH_SAFE(us, &userspecs, entries, us_next) {
+    TAILQ_FOREACH_SAFE(us, &parsed_policy.userspecs, entries, us_next) {
        TAILQ_FOREACH_SAFE(m, &us->users, entries, m_next) {
            if (m->type == ALIAS) {
                /* If alias is used, remove from user_aliases and free. */
@@ -1055,13 +1058,14 @@ filter_defaults(struct cvtsudoers_config *conf)
     struct member_list *prev_binding = NULL;
     struct defaults *def, *def_next;
     struct member *m, *m_next;
+    struct alias *a;
     int alias_type;
     debug_decl(filter_defaults, SUDOERS_DEBUG_DEFAULTS)
 
     if (filters == NULL && conf->defaults == CVT_DEFAULTS_ALL)
        debug_return;
 
-    TAILQ_FOREACH_SAFE(def, &defaults, entries, def_next) {
+    TAILQ_FOREACH_SAFE(def, &parsed_policy.defaults, entries, def_next) {
        bool keep = true;
 
        switch (def->type) {
@@ -1118,20 +1122,21 @@ filter_defaults(struct cvtsudoers_config *conf)
                            TAILQ_INSERT_TAIL(&cmnd_aliases, m, entries);
                            break;
                        default:
-                           sudo_fatalx_nodebug("unexpected alias type %d", alias_type);
+                           sudo_fatalx_nodebug("unexpected alias type %d",
+                               alias_type);
                            break;
                        }
                    }
                }
            }
-           TAILQ_REMOVE(&defaults, def, entries);
+           TAILQ_REMOVE(&parsed_policy.defaults, def, entries);
            free_default(def, &prev_binding);
            if (prev_binding != NULL) {
                /* Remove and free Defaults that share the same binding. */
                while (def_next != NULL && def_next->binding == prev_binding) {
                    def = def_next;
                    def_next = TAILQ_NEXT(def, entries);
-                   TAILQ_REMOVE(&defaults, def, entries);
+                   TAILQ_REMOVE(&parsed_policy.defaults, def, entries);
                    free_default(def, &prev_binding);
                }
            }
@@ -1144,22 +1149,22 @@ filter_defaults(struct cvtsudoers_config *conf)
     alias_used_by_userspecs(&user_aliases, &runas_aliases, &host_aliases,
        &cmnd_aliases);
     TAILQ_FOREACH_SAFE(m, &user_aliases, entries, m_next) {
-       struct alias *a = alias_remove(m->name, USERALIAS);
+       a = alias_remove(&parsed_policy, m->name, USERALIAS);
        alias_free(a);
        free_member(m);
     }
     TAILQ_FOREACH_SAFE(m, &runas_aliases, entries, m_next) {
-       struct alias *a = alias_remove(m->name, RUNASALIAS);
+       a = alias_remove(&parsed_policy, m->name, RUNASALIAS);
        alias_free(a);
        free_member(m);
     }
     TAILQ_FOREACH_SAFE(m, &host_aliases, entries, m_next) {
-       struct alias *a = alias_remove(m->name, HOSTALIAS);
+       a = alias_remove(&parsed_policy, m->name, HOSTALIAS);
        alias_free(a);
        free_member(m);
     }
     TAILQ_FOREACH_SAFE(m, &cmnd_aliases, entries, m_next) {
-       struct alias *a = alias_remove(m->name, CMNDALIAS);
+       a = alias_remove(&parsed_policy, m->name, CMNDALIAS);
        alias_free(a);
        free_member(m);
     }
@@ -1174,20 +1179,19 @@ static void
 alias_remove_unused(void)
 {
     struct rbtree *used_aliases;
-    struct rbtree *unused_aliases;
     debug_decl(alias_remove_unused, SUDOERS_DEBUG_ALIAS)
 
-    used_aliases = rbcreate(alias_compare);
+    used_aliases = alloc_aliases();
     if (used_aliases == NULL)
        sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
 
     /* Move all referenced aliases to used_aliases. */
-    if (!alias_find_used(used_aliases))
+    if (!alias_find_used(&parsed_policy, used_aliases))
        sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
 
     /* Only unreferenced aliases are left, swap and free the unused ones. */
-    unused_aliases = replace_aliases(used_aliases);
-    rbdestroy(unused_aliases, alias_free);
+    free_aliases(parsed_policy.aliases);
+    parsed_policy.aliases = used_aliases;
 
     debug_return;
 }
@@ -1224,7 +1228,7 @@ alias_prune(struct cvtsudoers_config *conf)
 {
     debug_decl(alias_prune, SUDOERS_DEBUG_ALIAS)
 
-    alias_apply(alias_prune_helper, conf);
+    alias_apply(&parsed_policy, alias_prune_helper, conf);
 
     debug_return;
 }
@@ -1271,7 +1275,7 @@ convert_sudoers_sudoers(const char *output_file, struct cvtsudoers_config *conf)
 
     /* Print User_Specs, separated by blank lines. */
     if (!ISSET(conf->suppress, SUPPRESS_PRIVS)) {
-       if (!sudoers_format_userspecs(&lbuf, &userspecs, "\n",
+       if (!sudoers_format_userspecs(&lbuf, &parsed_policy, "\n",
            conf->expand_aliases, true)) {
            goto done;
        }
index 556160e9013ecdc08949c5a60e034ea5fea247d5..7ca59850e6cced97a8976a5cde284957ac641b44 100644 (file)
@@ -475,7 +475,7 @@ print_member_json_int(FILE *fp, char *name, int type, bool negated,
        struct alias *a;
        struct member *m;
 
-       if ((a = alias_get(value.u.string, alias_type)) != NULL) {
+       if ((a = alias_get(&parsed_policy, value.u.string, alias_type)) != NULL) {
            TAILQ_FOREACH(m, &a->members, entries) {
                print_member_json_int(fp, m->name, m->type,
                    negated ? !m->negated : m->negated,
@@ -661,13 +661,13 @@ print_defaults_json(FILE *fp, int indent, bool expand_aliases, bool need_comma)
     int type;
     debug_decl(print_defaults_json, SUDOERS_DEBUG_UTIL)
 
-    if (TAILQ_EMPTY(&defaults))
+    if (TAILQ_EMPTY(&parsed_policy.defaults))
        debug_return_bool(need_comma);
 
     fprintf(fp, "%s\n%*s\"Defaults\": [\n", need_comma ? "," : "", indent, "");
     indent += 4;
 
-    TAILQ_FOREACH_SAFE(def, &defaults, entries, next) {
+    TAILQ_FOREACH_SAFE(def, &parsed_policy.defaults, entries, next) {
        type = get_defaults_type(def);
        if (type == -1) {
            sudo_warnx(U_("unknown defaults entry \"%s\""), def->var);
@@ -745,7 +745,7 @@ print_aliases_by_type_json(FILE *fp, int alias_type, const char *title,
     closure.alias_type = alias_type;
     closure.title = title;
     closure.need_comma = need_comma;
-    alias_apply(print_alias_json, &closure);
+    alias_apply(&parsed_policy, print_alias_json, &closure);
     if (closure.count != 0) {
        print_indent(fp, closure.indent);
        fputs("]\n", fp);
@@ -1083,12 +1083,12 @@ print_userspecs_json(FILE *fp, int indent, bool expand_aliases, bool need_comma)
     struct userspec *us;
     debug_decl(print_userspecs_json, SUDOERS_DEBUG_UTIL)
 
-    if (TAILQ_EMPTY(&userspecs))
+    if (TAILQ_EMPTY(&parsed_policy.userspecs))
        debug_return_bool(need_comma);
 
     fprintf(fp, "%s\n%*s\"User_Specs\": [\n", need_comma ? "," : "", indent, "");
     indent += 4;
-    TAILQ_FOREACH(us, &userspecs, entries) {
+    TAILQ_FOREACH(us, &parsed_policy.userspecs, entries) {
        print_userspec_json(fp, us, indent, expand_aliases);
     }
     indent -= 4;
index 1fbda1f5597ee8f4027bfc1abd7321f3eb8682a6..4e4473daddceacb0ca5bfdd325404c82ce293962 100644 (file)
@@ -167,14 +167,14 @@ print_global_defaults_ldif(FILE *fp, const char *base)
 
     sudo_lbuf_init(&lbuf, NULL, 0, NULL, 80);
 
-    TAILQ_FOREACH(opt, &defaults, entries) {
+    TAILQ_FOREACH(opt, &parsed_policy.defaults, entries) {
        /* Skip bound Defaults (unsupported). */
        if (opt->type == DEFAULTS) {
            count++;
        } else {
            lbuf.len = 0;
            sudo_lbuf_append(&lbuf, "# ");
-           sudoers_format_default_line(&lbuf, opt, false, true);
+           sudoers_format_default_line(&lbuf, &parsed_policy, opt, false, true);
            fprintf(fp, "# Unable to translate %s:%d\n%s\n",
                opt->file, opt->lineno, lbuf.buf);
        }
@@ -195,7 +195,7 @@ print_global_defaults_ldif(FILE *fp, const char *base)
     print_attribute_ldif(fp, "cn", "defaults");
     print_attribute_ldif(fp, "description", "Default sudoOption's go here");
 
-    print_options_ldif(fp, &defaults);
+    print_options_ldif(fp, &parsed_policy.defaults);
     putc('\n', fp);
 
     debug_return_bool(!ferror(fp));
@@ -239,7 +239,7 @@ print_member_ldif(FILE *fp, char *name, int type, bool negated,
        free(attr_val);
        break;
     case ALIAS:
-       if ((a = alias_get(name, alias_type)) != NULL) {
+       if ((a = alias_get(&parsed_policy, name, alias_type)) != NULL) {
            TAILQ_FOREACH(m, &a->members, entries) {
                print_member_ldif(fp, m->name, m->type,
                    negated ? !m->negated : m->negated, alias_type, attr_name);
@@ -601,7 +601,7 @@ print_userspecs_ldif(FILE *fp, struct cvtsudoers_config *conf)
     struct userspec *us;
     debug_decl(print_userspecs_ldif, SUDOERS_DEBUG_UTIL)
  
-    TAILQ_FOREACH(us, &userspecs, entries) {
+    TAILQ_FOREACH(us, &parsed_policy.userspecs, entries) {
        if (!print_userspec_ldif(fp, us, conf))
            debug_return_bool(false);
     }
@@ -860,7 +860,7 @@ ldif_store_options(struct cvtsudoers_str_list *options)
                    U_("unable to allocate memory"));
            }
        }
-       TAILQ_INSERT_TAIL(&defaults, d, entries);
+       TAILQ_INSERT_TAIL(&parsed_policy.defaults, d, entries);
     }
     debug_return;
 }
@@ -928,7 +928,7 @@ role_to_sudoers(struct sudo_role *role, bool store_options,
 
     if (reuse_userspec) {
        /* Re-use the previous userspec */
-       us = TAILQ_LAST(&userspecs, userspec_list);
+       us = TAILQ_LAST(&parsed_policy.userspecs, userspec_list);
     } else {
        /* Allocate a new userspec and fill in the user list. */
        if ((us = calloc(1, sizeof(*us))) == NULL) {
@@ -1039,7 +1039,7 @@ role_to_sudoers(struct sudo_role *role, bool store_options,
 
     /* Add finished userspec to the list if new. */
     if (!reuse_userspec)
-       TAILQ_INSERT_TAIL(&userspecs, us, entries);
+       TAILQ_INSERT_TAIL(&parsed_policy.userspecs, us, entries);
 
     debug_return;
 }
index d4715f72fc47f08d0b4342c1e3dc77b3b1c0162b..8611d5906585b44644103437ca55894460da15b8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999-2005, 2007-2017
+ * Copyright (c) 1999-2005, 2007-2018
  *     Todd C. Miller <Todd.Miller@sudo.ws>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -696,7 +696,8 @@ default_type_matches(struct defaults *d, int what)
  * Returns true if it matches, else false.
  */
 static bool
-default_binding_matches(struct defaults *d, int what)
+default_binding_matches(struct sudoers_parse_tree *parse_tree,
+    struct defaults *d, int what)
 {
     debug_decl(default_binding_matches, SUDOERS_DEBUG_DEFAULTS)
 
@@ -705,19 +706,19 @@ default_binding_matches(struct defaults *d, int what)
        debug_return_bool(true);
        break;
     case DEFAULTS_USER:
-       if (userlist_matches(sudo_user.pw, d->binding) == ALLOW)
+       if (userlist_matches(parse_tree, sudo_user.pw, d->binding) == ALLOW)
            debug_return_bool(true);
        break;
     case DEFAULTS_RUNAS:
-       if (runaslist_matches(d->binding, NULL, NULL, NULL) == ALLOW)
+       if (runaslist_matches(parse_tree, d->binding, NULL, NULL, NULL) == ALLOW)
            debug_return_bool(true);
        break;
     case DEFAULTS_HOST:
-       if (hostlist_matches(sudo_user.pw, d->binding) == ALLOW)
+       if (hostlist_matches(parse_tree, sudo_user.pw, d->binding) == ALLOW)
            debug_return_bool(true);
        break;
     case DEFAULTS_CMND:
-       if (cmndlist_matches(d->binding) == ALLOW)
+       if (cmndlist_matches(parse_tree, d->binding) == ALLOW)
            debug_return_bool(true);
        break;
     }
@@ -729,7 +730,7 @@ default_binding_matches(struct defaults *d, int what)
  * Pass in an OR'd list of which default types to update.
  */
 bool
-update_defaults(struct defaults_list *defs, int what, bool quiet)
+update_defaults(struct sudoers_parse_tree *parse_tree, int what, bool quiet)
 {
     struct defaults *d;
     bool ret = true;
@@ -741,14 +742,14 @@ update_defaults(struct defaults_list *defs, int what, bool quiet)
     /*
      * First apply Defaults values marked as early.
      */
-    TAILQ_FOREACH(d, defs, entries) {
+    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
        struct early_default *early = is_early_default(d->var);
        if (early == NULL)
            continue;
 
        /* Defaults type and binding must match. */
        if (!default_type_matches(d, what) ||
-           !default_binding_matches(d, what))
+           !default_binding_matches(parse_tree, d, what))
            continue;
 
        /* Copy the value to sudo_defs_table and mark as early. */
@@ -763,14 +764,14 @@ update_defaults(struct defaults_list *defs, int what, bool quiet)
     /*
      * Then set the rest of the defaults.
      */
-    TAILQ_FOREACH(d, defs, entries) {
+    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
        /* Skip Defaults marked as early, we already did them. */
        if (is_early_default(d->var))
            continue;
 
        /* Defaults type and binding must match. */
        if (!default_type_matches(d, what) ||
-           !default_binding_matches(d, what))
+           !default_binding_matches(parse_tree, d, what))
            continue;
 
        /* Copy the value to sudo_defs_table and run callback (if any) */
@@ -784,14 +785,14 @@ update_defaults(struct defaults_list *defs, int what, bool quiet)
  * Check all defaults entries without actually setting them.
  */
 bool
-check_defaults(bool quiet)
+check_defaults(struct sudoers_parse_tree *parse_tree, bool quiet)
 {
     struct defaults *d;
     bool ret = true;
     int idx;
     debug_decl(check_defaults, SUDOERS_DEBUG_DEFAULTS)
 
-    TAILQ_FOREACH(d, &defaults, entries) {
+    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
        idx = find_default(d->var, d->file, d->lineno, quiet);
        if (idx != -1) {
            struct sudo_defs_types *def = &sudo_defs_table[idx];
index 41582accec08897697d979f480476c056921abdf..3d360b8e05958760625822d998f47283f73d68b5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999-2005, 2008-2016
+ * Copyright (c) 1999-2005, 2008-2018
  *     Todd C. Miller <Todd.Miller@sudo.ws>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -122,15 +122,15 @@ struct early_default {
 /*
  * Prototypes
  */
-struct defaults_list;
+struct sudoers_parse_tree;
 void dump_default(void);
 bool init_defaults(void);
 struct early_default *is_early_default(const char *name);
 bool run_early_defaults(void);
 bool set_early_default(const char *var, const char *val, int op, const char *file, int lineno, bool quiet, struct early_default *early);
 bool set_default(const char *var, const char *val, int op, const char *file, int lineno, bool quiet);
-bool update_defaults(struct defaults_list *defs, int what, bool quiet);
-bool check_defaults(bool quiet);
+bool update_defaults(struct sudoers_parse_tree *parse_tree, int what, bool quiet);
+bool check_defaults(struct sudoers_parse_tree *parse_tree, bool quiet);
 
 extern struct sudo_defs_types sudo_defs_table[];
 
index dc6713f1cd777abb11ed1ff51a44e4c79f756ceb..62a6eade762d7b9c097a77b3a0d760150739e614 100644 (file)
@@ -40,8 +40,7 @@
 
 struct sudo_file_handle {
     FILE *fp;
-    struct defaults_list defaults;
-    struct userspec_list userspecs;
+    struct sudoers_parse_tree parse_tree;
 };
 
 static int
@@ -54,9 +53,7 @@ sudo_file_close(struct sudo_nss *nss)
        fclose(handle->fp);
        sudoersin = NULL;
 
-       free_userspecs(&handle->userspecs);
-       free_defaults(&handle->defaults);
-
+       free_parse_tree(&handle->parse_tree);
        free(handle);
        nss->handle = NULL;
     }
@@ -83,8 +80,7 @@ sudo_file_open(struct sudo_nss *nss)
     if (handle != NULL) {
        handle->fp = open_sudoers(sudoers_file, false, NULL);
        if (handle->fp != NULL) {
-           TAILQ_INIT(&handle->userspecs);
-           TAILQ_INIT(&handle->defaults);
+           init_parse_tree(&handle->parse_tree);
        } else {
            free(handle);
            handle = NULL;
@@ -95,9 +91,9 @@ sudo_file_open(struct sudo_nss *nss)
 }
 
 /*
- * Parse the specified sudoers file.
+ * Parse and return the specified sudoers file.
  */
-static int
+static struct sudoers_parse_tree *
 sudo_file_parse(struct sudo_nss *nss)
 {
     debug_decl(sudo_file_close, SUDOERS_DEBUG_NSS)
@@ -106,7 +102,7 @@ sudo_file_parse(struct sudo_nss *nss)
     if (handle == NULL || handle->fp == NULL) {
        sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: called with NULL %s",
            __func__, handle ? "file pointer" : "handle");
-       debug_return_int(-1);
+       debug_return_ptr(NULL);
     }
 
     sudoersin = handle->fp;
@@ -117,49 +113,33 @@ sudo_file_parse(struct sudo_nss *nss)
        } else {
            log_warningx(SLOG_SEND_MAIL, N_("parse error in %s"), errorfile);
        }
-       debug_return_int(-1);
+       debug_return_ptr(NULL);
     }
 
-    /* Move parsed userspecs and defaults to nss structure. */
-    TAILQ_CONCAT(&handle->userspecs, &userspecs, entries);
-    TAILQ_CONCAT(&handle->defaults, &defaults, entries);
+    /* Move parsed sudoers policy to nss handle. */
+    reparent_parse_tree(&handle->parse_tree);
 
-    debug_return_int(0);
+    debug_return_ptr(&handle->parse_tree);
 }
 
 /*
- * We return all cached userspecs, the parse functions will
- * perform matching against pw for us.
+ * No need for explicit sudoers queries, the parse function handled it.
  */
-static struct userspec_list *
+static int
 sudo_file_query(struct sudo_nss *nss, struct passwd *pw)
 {
-    struct sudo_file_handle *handle = nss->handle;
     debug_decl(sudo_file_query, SUDOERS_DEBUG_NSS)
-
-    if (handle == NULL) {
-       sudo_debug_printf(SUDO_DEBUG_ERROR,
-           "%s: called with NULL handle", __func__);
-       debug_return_ptr(NULL);
-    }
-    debug_return_ptr(&handle->userspecs);
+    debug_return_int(0);
 }
 
 /*
- * Return cached defaults entries.
+ * No need to get defaults for sudoers file, the parse function handled it.
  */
-static struct defaults_list *
+static int
 sudo_file_getdefs(struct sudo_nss *nss)
 {
     debug_decl(sudo_file_getdefs, SUDOERS_DEBUG_NSS)
-    struct sudo_file_handle *handle = nss->handle;
-
-    if (handle == NULL) {
-       sudo_debug_printf(SUDO_DEBUG_ERROR,
-           "%s: called with NULL handle", __func__);
-       debug_return_ptr(NULL);
-    }
-    debug_return_ptr(&handle->defaults);
+    debug_return_int(0);
 }
 
 /* sudo_nss implementation */
index bd1a930fa4f0bf3e3c7fb8b9d69dd5c3286f18b4..69e3019ade8c54d9f48c39d83d7d2e07d4bcf870 100644 (file)
@@ -37,8 +37,9 @@
  * the specified separator (which must not be NULL in the UNSPEC case).
  */
 static bool
-sudoers_format_member_int(struct sudo_lbuf *lbuf, char *name, int type,
-    bool negated, const char *separator, int alias_type)
+sudoers_format_member_int(struct sudo_lbuf *lbuf,
+    struct sudoers_parse_tree *parse_tree, char *name, int type, bool negated,
+    const char *separator, int alias_type)
 {
     struct alias *a;
     struct member *m;
@@ -81,13 +82,13 @@ sudoers_format_member_int(struct sudo_lbuf *lbuf, char *name, int type,
            goto print_word;
        case ALIAS:
            if (alias_type != UNSPEC) {
-               if ((a = alias_get(name, alias_type)) != NULL) {
+               if ((a = alias_get(parse_tree, name, alias_type)) != NULL) {
                    TAILQ_FOREACH(m, &a->members, entries) {
                        if (m != TAILQ_FIRST(&a->members))
                            sudo_lbuf_append(lbuf, "%s", separator);
-                       sudoers_format_member_int(lbuf, m->name, m->type,
-                           negated ? !m->negated : m->negated, separator,
-                           alias_type);
+                       sudoers_format_member_int(lbuf, parse_tree, m->name,
+                           m->type, negated ? !m->negated : m->negated,
+                           separator, alias_type);
                    }
                    alias_put(a);
                    break;
@@ -116,11 +117,12 @@ sudoers_format_member_int(struct sudo_lbuf *lbuf, char *name, int type,
 }
 
 bool
-sudoers_format_member(struct sudo_lbuf *lbuf, struct member *m,
+sudoers_format_member(struct sudo_lbuf *lbuf,
+    struct sudoers_parse_tree *parse_tree, struct member *m,
     const char *separator, int alias_type)
 {
-    return sudoers_format_member_int(lbuf, m->name, m->type, m->negated,
-       separator, alias_type);
+    return sudoers_format_member_int(lbuf, parse_tree, m->name, m->type,
+       m->negated, separator, alias_type);
 }
 
 #define        FIELD_CHANGED(ocs, ncs, fld) \
@@ -133,7 +135,8 @@ sudoers_format_member(struct sudo_lbuf *lbuf, struct member *m,
  * Write a cmndspec to lbuf in sudoers format.
  */
 bool
-sudoers_format_cmndspec(struct sudo_lbuf *lbuf, struct cmndspec *cs,
+sudoers_format_cmndspec(struct sudo_lbuf *lbuf,
+    struct sudoers_parse_tree *parse_tree, struct cmndspec *cs,
     struct cmndspec *prev_cs, bool expand_aliases)
 {
     debug_decl(sudoers_format_cmndspec, SUDOERS_DEBUG_UTIL)
@@ -185,7 +188,7 @@ sudoers_format_cmndspec(struct sudo_lbuf *lbuf, struct cmndspec *cs,
        sudo_lbuf_append(lbuf, cs->tags.send_mail ? "MAIL: " : "NOMAIL: ");
     if (TAG_CHANGED(prev_cs, cs, follow))
        sudo_lbuf_append(lbuf, cs->tags.follow ? "FOLLOW: " : "NOFOLLOW: ");
-    sudoers_format_member(lbuf, cs->cmnd, ", ",
+    sudoers_format_member(lbuf, parse_tree, cs->cmnd, ", ",
        expand_aliases ? CMNDALIAS : UNSPEC);
     debug_return_bool(!sudo_lbuf_error(lbuf));
 }
@@ -194,7 +197,8 @@ sudoers_format_cmndspec(struct sudo_lbuf *lbuf, struct cmndspec *cs,
  * Write a privilege to lbuf in sudoers format.
  */
 bool
-sudoers_format_privilege(struct sudo_lbuf *lbuf, struct privilege *priv,
+sudoers_format_privilege(struct sudo_lbuf *lbuf,
+    struct sudoers_parse_tree *parse_tree, struct privilege *priv,
     bool expand_aliases)
 {
     struct cmndspec *cs, *prev_cs;
@@ -205,7 +209,7 @@ sudoers_format_privilege(struct sudo_lbuf *lbuf, struct privilege *priv,
     TAILQ_FOREACH(m, &priv->hostlist, entries) {
        if (m != TAILQ_FIRST(&priv->hostlist))
            sudo_lbuf_append(lbuf, ", ");
-       sudoers_format_member(lbuf, m, ", ",
+       sudoers_format_member(lbuf, parse_tree, m, ", ",
            expand_aliases ? HOSTALIAS : UNSPEC);
     }
 
@@ -222,7 +226,7 @@ sudoers_format_privilege(struct sudo_lbuf *lbuf, struct privilege *priv,
                TAILQ_FOREACH(m, cs->runasuserlist, entries) {
                    if (m != TAILQ_FIRST(cs->runasuserlist))
                        sudo_lbuf_append(lbuf, ", ");
-                   sudoers_format_member(lbuf, m, ", ",
+                   sudoers_format_member(lbuf, parse_tree, m, ", ",
                        expand_aliases ? RUNASALIAS : UNSPEC);
                }
            }
@@ -231,7 +235,7 @@ sudoers_format_privilege(struct sudo_lbuf *lbuf, struct privilege *priv,
                TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
                    if (m != TAILQ_FIRST(cs->runasgrouplist))
                        sudo_lbuf_append(lbuf, ", ");
-                   sudoers_format_member(lbuf, m, ", ",
+                   sudoers_format_member(lbuf, parse_tree, m, ", ",
                        expand_aliases ? RUNASALIAS : UNSPEC);
                }
            }
@@ -240,7 +244,7 @@ sudoers_format_privilege(struct sudo_lbuf *lbuf, struct privilege *priv,
        } else if (cs != TAILQ_FIRST(&priv->cmndlist)) {
            sudo_lbuf_append(lbuf, ", ");
        }
-       sudoers_format_cmndspec(lbuf, cs, prev_cs, expand_aliases);
+       sudoers_format_cmndspec(lbuf, parse_tree, cs, prev_cs, expand_aliases);
        prev_cs = cs;
     }
 
@@ -251,8 +255,9 @@ sudoers_format_privilege(struct sudo_lbuf *lbuf, struct privilege *priv,
  * Write a userspec to lbuf in sudoers format.
  */
 bool
-sudoers_format_userspec(struct sudo_lbuf *lbuf, struct userspec *us,
-    bool expand_aliases)
+sudoers_format_userspec(struct sudo_lbuf *lbuf,
+    struct sudoers_parse_tree *parse_tree,
+    struct userspec *us, bool expand_aliases)
 {
     struct privilege *priv;
     struct sudoers_comment *comment;
@@ -268,7 +273,7 @@ sudoers_format_userspec(struct sudo_lbuf *lbuf, struct userspec *us,
     TAILQ_FOREACH(m, &us->users, entries) {
        if (m != TAILQ_FIRST(&us->users))
            sudo_lbuf_append(lbuf, ", ");
-       sudoers_format_member(lbuf, m, ", ",
+       sudoers_format_member(lbuf, parse_tree, m, ", ",
            expand_aliases ? USERALIAS : UNSPEC);
     }
 
@@ -277,7 +282,7 @@ sudoers_format_userspec(struct sudo_lbuf *lbuf, struct userspec *us,
            sudo_lbuf_append(lbuf, " : ");
        else
            sudo_lbuf_append(lbuf, " ");
-       if (!sudoers_format_privilege(lbuf, priv, expand_aliases))
+       if (!sudoers_format_privilege(lbuf, parse_tree, priv, expand_aliases))
            break;
     }
     sudo_lbuf_append(lbuf, "\n");
@@ -289,16 +294,17 @@ sudoers_format_userspec(struct sudo_lbuf *lbuf, struct userspec *us,
  * Write a userspec_list to lbuf in sudoers format.
  */
 bool
-sudoers_format_userspecs(struct sudo_lbuf *lbuf, struct userspec_list *usl,
-    const char *separator, bool expand_aliases, bool flush)
+sudoers_format_userspecs(struct sudo_lbuf *lbuf,
+    struct sudoers_parse_tree *parse_tree, const char *separator,
+    bool expand_aliases, bool flush)
 {
     struct userspec *us;
     debug_decl(sudoers_format_userspecs, SUDOERS_DEBUG_UTIL)
 
-    TAILQ_FOREACH(us, usl, entries) {
-       if (separator != NULL && us != TAILQ_FIRST(usl))
+    TAILQ_FOREACH(us, &parse_tree->userspecs, entries) {
+       if (separator != NULL && us != TAILQ_FIRST(&parse_tree->userspecs))
            sudo_lbuf_append(lbuf, "%s", separator);
-       if (!sudoers_format_userspec(lbuf, us, expand_aliases))
+       if (!sudoers_format_userspec(lbuf, parse_tree, us, expand_aliases))
            break;
        sudo_lbuf_print(lbuf);
     }
@@ -336,7 +342,8 @@ sudoers_format_default(struct sudo_lbuf *lbuf, struct defaults *d)
  * entries with the same binding on a single line.
  */
 bool
-sudoers_format_default_line(struct sudo_lbuf *lbuf, struct defaults *d,
+sudoers_format_default_line( struct sudo_lbuf *lbuf,
+    struct sudoers_parse_tree *parse_tree, struct defaults *d,
     struct defaults **next, bool expand_aliases)
 {
     struct member *m;
@@ -369,7 +376,7 @@ sudoers_format_default_line(struct sudo_lbuf *lbuf, struct defaults *d,
     TAILQ_FOREACH(m, d->binding, entries) {
        if (m != TAILQ_FIRST(d->binding))
            sudo_lbuf_append(lbuf, ", ");
-       sudoers_format_member(lbuf, m, ", ", alias_type);
+       sudoers_format_member(lbuf, parse_tree, m, ", ", alias_type);
     }
 
     sudo_lbuf_append(lbuf, " ");
index 52df29a758963b8025ac1dee409f23be040119c3..85a8b8a1d1ab12a01ce89bc10f9927f139b05aa1 100644 (file)
@@ -38,7 +38,7 @@
 #define YYPREFIX "sudoers"
 #line 2 "gram.y"
 /*
- * Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2017
+ * Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2018
  *     Todd C. Miller <Todd.Miller@sudo.ws>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -98,8 +98,11 @@ bool parse_error = false;
 int errorlineno = -1;
 char *errorfile = NULL;
 
-struct defaults_list defaults = TAILQ_HEAD_INITIALIZER(defaults);
-struct userspec_list userspecs = TAILQ_HEAD_INITIALIZER(userspecs);
+struct sudoers_parse_tree parsed_policy = {
+    TAILQ_HEAD_INITIALIZER(parsed_policy.userspecs),
+    TAILQ_HEAD_INITIALIZER(parsed_policy.defaults),
+    NULL /* aliases */
+};
 
 /*
  * Local protoypes
@@ -110,7 +113,7 @@ static bool add_userspec(struct member *, struct privilege *);
 static struct defaults *new_default(char *, char *, short);
 static struct member *new_member(char *, int);
 static struct command_digest *new_digest(int, char *);
-#line 77 "gram.y"
+#line 80 "gram.y"
 #ifndef YYSTYPE_DEFINED
 #define YYSTYPE_DEFINED
 typedef union {
@@ -127,7 +130,7 @@ typedef union {
     int tok;
 } YYSTYPE;
 #endif /* YYSTYPE_DEFINED */
-#line 130 "gram.c"
+#line 133 "gram.c"
 #define COMMAND 257
 #define ALIAS 258
 #define DEFVAR 259
@@ -667,7 +670,7 @@ short *yysslim;
 YYSTYPE *yyvs;
 unsigned int yystacksize;
 int yyparse(void);
-#line 899 "gram.y"
+#line 906 "gram.y"
 void
 sudoerserror(const char *s)
 {
@@ -799,7 +802,7 @@ add_defaults(int type, struct member *bmem, struct defaults *defs)
        HLTQ_FOREACH_SAFE(d, defs, entries, next) {
            d->type = type;
            d->binding = binding;
-           TAILQ_INSERT_TAIL(&defaults, d, entries);
+           TAILQ_INSERT_TAIL(&parsed_policy.defaults, d, entries);
        }
     }
 
@@ -826,7 +829,7 @@ add_userspec(struct member *members, struct privilege *privs)
     HLTQ_TO_TAILQ(&u->users, members, entries);
     HLTQ_TO_TAILQ(&u->privileges, privs, entries);
     STAILQ_INIT(&u->comments);
-    TAILQ_INSERT_TAIL(&userspecs, u, entries);
+    TAILQ_INSERT_TAIL(&parsed_policy.userspecs, u, entries);
 
     debug_return_bool(true);
 }
@@ -1007,6 +1010,41 @@ free_userspec(struct userspec *us)
     debug_return;
 }
 
+/*
+ * Initialized a sudoers parse tree.
+ */
+void
+init_parse_tree(struct sudoers_parse_tree *parse_tree)
+{
+    TAILQ_INIT(&parse_tree->userspecs);
+    TAILQ_INIT(&parse_tree->defaults);
+    parse_tree->aliases = NULL;
+}
+
+/*
+ * Move the contents of parsed_policy to new_tree.
+ */
+void
+reparent_parse_tree(struct sudoers_parse_tree *new_tree)
+{
+    TAILQ_CONCAT(&new_tree->userspecs, &parsed_policy.userspecs, entries);
+    TAILQ_CONCAT(&new_tree->defaults, &parsed_policy.defaults, entries);
+    new_tree->aliases = parsed_policy.aliases;
+    parsed_policy.aliases = NULL;
+}
+
+/*
+ * Free the contents of a sudoers parse tree and initialize it.
+ */
+void
+free_parse_tree(struct sudoers_parse_tree *parse_tree)
+{
+    free_userspecs(&parse_tree->userspecs);
+    free_defaults(&parse_tree->defaults);
+    free_aliases(parse_tree->aliases);
+    parse_tree->aliases = NULL;
+}
+
 /*
  * Free up space used by data structures from a previous parser run and sets
  * the current sudoers file to path.
@@ -1017,15 +1055,9 @@ init_parser(const char *path, bool quiet)
     bool ret = true;
     debug_decl(init_parser, SUDOERS_DEBUG_PARSER)
 
-    free_userspecs(&userspecs);
-    free_defaults(&defaults);
+    free_parse_tree(&parsed_policy);
     init_lexer();
 
-    if (!init_aliases()) {
-       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-       ret = false;
-    }
-
     rcstr_delref(sudoers);
     if (path != NULL) {
        if ((sudoers = rcstr_dup(path)) == NULL) {
@@ -1063,7 +1095,7 @@ init_options(struct command_options *opts)
     opts->limitprivs = NULL;
 #endif
 }
-#line 1014 "gram.c"
+#line 1046 "gram.c"
 /* allocate initial stack or double stack size, up to YYMAXDEPTH */
 #if defined(__cplusplus) || defined(__STDC__)
 static int yygrowstack(void)
@@ -1272,23 +1304,23 @@ yyreduce:
     switch (yyn)
     {
 case 1:
-#line 175 "gram.y"
+#line 178 "gram.y"
 { ; }
 break;
 case 5:
-#line 183 "gram.y"
+#line 186 "gram.y"
 {
                            ;
                        }
 break;
 case 6:
-#line 186 "gram.y"
+#line 189 "gram.y"
 {
                            yyerrok;
                        }
 break;
 case 7:
-#line 189 "gram.y"
+#line 192 "gram.y"
 {
                            if (!add_userspec(yyvsp[-1].member, yyvsp[0].privilege)) {
                                sudoerserror(N_("unable to allocate memory"));
@@ -1297,73 +1329,73 @@ case 7:
                        }
 break;
 case 8:
-#line 195 "gram.y"
+#line 198 "gram.y"
 {
                            ;
                        }
 break;
 case 9:
-#line 198 "gram.y"
+#line 201 "gram.y"
 {
                            ;
                        }
 break;
 case 10:
-#line 201 "gram.y"
+#line 204 "gram.y"
 {
                            ;
                        }
 break;
 case 11:
-#line 204 "gram.y"
+#line 207 "gram.y"
 {
                            ;
                        }
 break;
 case 12:
-#line 207 "gram.y"
+#line 210 "gram.y"
 {
                            if (!add_defaults(DEFAULTS, NULL, yyvsp[0].defaults))
                                YYERROR;
                        }
 break;
 case 13:
-#line 211 "gram.y"
+#line 214 "gram.y"
 {
                            if (!add_defaults(DEFAULTS_USER, yyvsp[-1].member, yyvsp[0].defaults))
                                YYERROR;
                        }
 break;
 case 14:
-#line 215 "gram.y"
+#line 218 "gram.y"
 {
                            if (!add_defaults(DEFAULTS_RUNAS, yyvsp[-1].member, yyvsp[0].defaults))
                                YYERROR;
                        }
 break;
 case 15:
-#line 219 "gram.y"
+#line 222 "gram.y"
 {
                            if (!add_defaults(DEFAULTS_HOST, yyvsp[-1].member, yyvsp[0].defaults))
                                YYERROR;
                        }
 break;
 case 16:
-#line 223 "gram.y"
+#line 226 "gram.y"
 {
                            if (!add_defaults(DEFAULTS_CMND, yyvsp[-1].member, yyvsp[0].defaults))
                                YYERROR;
                        }
 break;
 case 18:
-#line 230 "gram.y"
+#line 233 "gram.y"
 {
                            HLTQ_CONCAT(yyvsp[-2].defaults, yyvsp[0].defaults, entries);
                            yyval.defaults = yyvsp[-2].defaults;
                        }
 break;
 case 19:
-#line 236 "gram.y"
+#line 239 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[0].string, NULL, true);
                            if (yyval.defaults == NULL) {
@@ -1373,7 +1405,7 @@ case 19:
                        }
 break;
 case 20:
-#line 243 "gram.y"
+#line 246 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[0].string, NULL, false);
                            if (yyval.defaults == NULL) {
@@ -1383,7 +1415,7 @@ case 20:
                        }
 break;
 case 21:
-#line 250 "gram.y"
+#line 253 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, true);
                            if (yyval.defaults == NULL) {
@@ -1393,7 +1425,7 @@ case 21:
                        }
 break;
 case 22:
-#line 257 "gram.y"
+#line 260 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, '+');
                            if (yyval.defaults == NULL) {
@@ -1403,7 +1435,7 @@ case 22:
                        }
 break;
 case 23:
-#line 264 "gram.y"
+#line 267 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, '-');
                            if (yyval.defaults == NULL) {
@@ -1413,14 +1445,14 @@ case 23:
                        }
 break;
 case 25:
-#line 274 "gram.y"
+#line 277 "gram.y"
 {
                            HLTQ_CONCAT(yyvsp[-2].privilege, yyvsp[0].privilege, entries);
                            yyval.privilege = yyvsp[-2].privilege;
                        }
 break;
 case 26:
-#line 280 "gram.y"
+#line 283 "gram.y"
 {
                            struct privilege *p = calloc(1, sizeof(*p));
                            if (p == NULL) {
@@ -1435,21 +1467,21 @@ case 26:
                        }
 break;
 case 27:
-#line 294 "gram.y"
+#line 297 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = false;
                        }
 break;
 case 28:
-#line 298 "gram.y"
+#line 301 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = true;
                        }
 break;
 case 29:
-#line 304 "gram.y"
+#line 307 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, ALIAS);
                            if (yyval.member == NULL) {
@@ -1459,7 +1491,7 @@ case 29:
                        }
 break;
 case 30:
-#line 311 "gram.y"
+#line 314 "gram.y"
 {
                            yyval.member = new_member(NULL, ALL);
                            if (yyval.member == NULL) {
@@ -1469,7 +1501,7 @@ case 30:
                        }
 break;
 case 31:
-#line 318 "gram.y"
+#line 321 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, NETGROUP);
                            if (yyval.member == NULL) {
@@ -1479,7 +1511,7 @@ case 31:
                        }
 break;
 case 32:
-#line 325 "gram.y"
+#line 328 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, NTWKADDR);
                            if (yyval.member == NULL) {
@@ -1489,7 +1521,7 @@ case 32:
                        }
 break;
 case 33:
-#line 332 "gram.y"
+#line 335 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, WORD);
                            if (yyval.member == NULL) {
@@ -1499,7 +1531,7 @@ case 33:
                        }
 break;
 case 35:
-#line 342 "gram.y"
+#line 345 "gram.y"
 {
                            struct cmndspec *prev;
                            prev = HLTQ_LAST(yyvsp[-2].cmndspec, cmndspec, entries);
@@ -1553,7 +1585,7 @@ case 35:
                        }
 break;
 case 36:
-#line 395 "gram.y"
+#line 398 "gram.y"
 {
                            struct cmndspec *cs = calloc(1, sizeof(*cs));
                            if (cs == NULL) {
@@ -1605,7 +1637,7 @@ case 36:
                        }
 break;
 case 37:
-#line 446 "gram.y"
+#line 449 "gram.y"
 {
                            yyval.digest = new_digest(SUDO_DIGEST_SHA224, yyvsp[0].string);
                            if (yyval.digest == NULL) {
@@ -1615,7 +1647,7 @@ case 37:
                        }
 break;
 case 38:
-#line 453 "gram.y"
+#line 456 "gram.y"
 {
                            yyval.digest = new_digest(SUDO_DIGEST_SHA256, yyvsp[0].string);
                            if (yyval.digest == NULL) {
@@ -1625,7 +1657,7 @@ case 38:
                        }
 break;
 case 39:
-#line 460 "gram.y"
+#line 463 "gram.y"
 {
                            yyval.digest = new_digest(SUDO_DIGEST_SHA384, yyvsp[0].string);
                            if (yyval.digest == NULL) {
@@ -1635,7 +1667,7 @@ case 39:
                        }
 break;
 case 40:
-#line 467 "gram.y"
+#line 470 "gram.y"
 {
                            yyval.digest = new_digest(SUDO_DIGEST_SHA512, yyvsp[0].string);
                            if (yyval.digest == NULL) {
@@ -1645,13 +1677,13 @@ case 40:
                        }
 break;
 case 41:
-#line 476 "gram.y"
+#line 479 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                        }
 break;
 case 42:
-#line 479 "gram.y"
+#line 482 "gram.y"
 {
                            if (yyvsp[0].member->type != COMMAND) {
                                sudoerserror(N_("a digest requires a path name"));
@@ -1663,75 +1695,75 @@ case 42:
                        }
 break;
 case 43:
-#line 490 "gram.y"
+#line 493 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = false;
                        }
 break;
 case 44:
-#line 494 "gram.y"
+#line 497 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = true;
                        }
 break;
 case 45:
-#line 500 "gram.y"
+#line 503 "gram.y"
 {
                            yyval.string = yyvsp[0].string;
                        }
 break;
 case 46:
-#line 505 "gram.y"
+#line 508 "gram.y"
 {
                            yyval.string = yyvsp[0].string;
                        }
 break;
 case 47:
-#line 509 "gram.y"
+#line 512 "gram.y"
 {
                            yyval.string = yyvsp[0].string;
                        }
 break;
 case 48:
-#line 514 "gram.y"
+#line 517 "gram.y"
 {
                            yyval.string = yyvsp[0].string;
                        }
 break;
 case 49:
-#line 519 "gram.y"
+#line 522 "gram.y"
 {
                            yyval.string = yyvsp[0].string;
                        }
 break;
 case 50:
-#line 524 "gram.y"
+#line 527 "gram.y"
 {
                            yyval.string = yyvsp[0].string;
                        }
 break;
 case 51:
-#line 528 "gram.y"
+#line 531 "gram.y"
 {
                            yyval.string = yyvsp[0].string;
                        }
 break;
 case 52:
-#line 533 "gram.y"
+#line 536 "gram.y"
 {
                            yyval.runas = NULL;
                        }
 break;
 case 53:
-#line 536 "gram.y"
+#line 539 "gram.y"
 {
                            yyval.runas = yyvsp[-1].runas;
                        }
 break;
 case 54:
-#line 541 "gram.y"
+#line 544 "gram.y"
 {
                            yyval.runas = calloc(1, sizeof(struct runascontainer));
                            if (yyval.runas != NULL) {
@@ -1749,7 +1781,7 @@ case 54:
                        }
 break;
 case 55:
-#line 556 "gram.y"
+#line 559 "gram.y"
 {
                            yyval.runas = calloc(1, sizeof(struct runascontainer));
                            if (yyval.runas == NULL) {
@@ -1761,7 +1793,7 @@ case 55:
                        }
 break;
 case 56:
-#line 565 "gram.y"
+#line 568 "gram.y"
 {
                            yyval.runas = calloc(1, sizeof(struct runascontainer));
                            if (yyval.runas == NULL) {
@@ -1773,7 +1805,7 @@ case 56:
                        }
 break;
 case 57:
-#line 574 "gram.y"
+#line 577 "gram.y"
 {
                            yyval.runas = calloc(1, sizeof(struct runascontainer));
                            if (yyval.runas == NULL) {
@@ -1785,7 +1817,7 @@ case 57:
                        }
 break;
 case 58:
-#line 583 "gram.y"
+#line 586 "gram.y"
 {
                            yyval.runas = calloc(1, sizeof(struct runascontainer));
                            if (yyval.runas != NULL) {
@@ -1803,13 +1835,13 @@ case 58:
                        }
 break;
 case 59:
-#line 600 "gram.y"
+#line 603 "gram.y"
 {
                            init_options(&yyval.options);
                        }
 break;
 case 60:
-#line 603 "gram.y"
+#line 606 "gram.y"
 {
                            yyval.options.notbefore = parse_gentime(yyvsp[0].string);
                            free(yyvsp[0].string);
@@ -1820,7 +1852,7 @@ case 60:
                        }
 break;
 case 61:
-#line 611 "gram.y"
+#line 614 "gram.y"
 {
                            yyval.options.notafter = parse_gentime(yyvsp[0].string);
                            free(yyvsp[0].string);
@@ -1831,7 +1863,7 @@ case 61:
                        }
 break;
 case 62:
-#line 619 "gram.y"
+#line 622 "gram.y"
 {
                            yyval.options.timeout = parse_timeout(yyvsp[0].string);
                            free(yyvsp[0].string);
@@ -1845,7 +1877,7 @@ case 62:
                        }
 break;
 case 63:
-#line 630 "gram.y"
+#line 633 "gram.y"
 {
 #ifdef HAVE_SELINUX
                            free(yyval.options.role);
@@ -1854,7 +1886,7 @@ case 63:
                        }
 break;
 case 64:
-#line 636 "gram.y"
+#line 639 "gram.y"
 {
 #ifdef HAVE_SELINUX
                            free(yyval.options.type);
@@ -1863,7 +1895,7 @@ case 64:
                        }
 break;
 case 65:
-#line 642 "gram.y"
+#line 645 "gram.y"
 {
 #ifdef HAVE_PRIV_SET
                            free(yyval.options.privs);
@@ -1872,7 +1904,7 @@ case 65:
                        }
 break;
 case 66:
-#line 648 "gram.y"
+#line 651 "gram.y"
 {
 #ifdef HAVE_PRIV_SET
                            free(yyval.options.limitprivs);
@@ -1881,97 +1913,97 @@ case 66:
                        }
 break;
 case 67:
-#line 656 "gram.y"
+#line 659 "gram.y"
 {
                            TAGS_INIT(yyval.tag);
                        }
 break;
 case 68:
-#line 659 "gram.y"
+#line 662 "gram.y"
 {
                            yyval.tag.nopasswd = true;
                        }
 break;
 case 69:
-#line 662 "gram.y"
+#line 665 "gram.y"
 {
                            yyval.tag.nopasswd = false;
                        }
 break;
 case 70:
-#line 665 "gram.y"
+#line 668 "gram.y"
 {
                            yyval.tag.noexec = true;
                        }
 break;
 case 71:
-#line 668 "gram.y"
+#line 671 "gram.y"
 {
                            yyval.tag.noexec = false;
                        }
 break;
 case 72:
-#line 671 "gram.y"
+#line 674 "gram.y"
 {
                            yyval.tag.setenv = true;
                        }
 break;
 case 73:
-#line 674 "gram.y"
+#line 677 "gram.y"
 {
                            yyval.tag.setenv = false;
                        }
 break;
 case 74:
-#line 677 "gram.y"
+#line 680 "gram.y"
 {
                            yyval.tag.log_input = true;
                        }
 break;
 case 75:
-#line 680 "gram.y"
+#line 683 "gram.y"
 {
                            yyval.tag.log_input = false;
                        }
 break;
 case 76:
-#line 683 "gram.y"
+#line 686 "gram.y"
 {
                            yyval.tag.log_output = true;
                        }
 break;
 case 77:
-#line 686 "gram.y"
+#line 689 "gram.y"
 {
                            yyval.tag.log_output = false;
                        }
 break;
 case 78:
-#line 689 "gram.y"
+#line 692 "gram.y"
 {
                            yyval.tag.follow = true;
                        }
 break;
 case 79:
-#line 692 "gram.y"
+#line 695 "gram.y"
 {
                            yyval.tag.follow = false;
                        }
 break;
 case 80:
-#line 695 "gram.y"
+#line 698 "gram.y"
 {
                            yyval.tag.send_mail = true;
                        }
 break;
 case 81:
-#line 698 "gram.y"
+#line 701 "gram.y"
 {
                            yyval.tag.send_mail = false;
                        }
 break;
 case 82:
-#line 703 "gram.y"
+#line 706 "gram.y"
 {
                            yyval.member = new_member(NULL, ALL);
                            if (yyval.member == NULL) {
@@ -1981,7 +2013,7 @@ case 82:
                        }
 break;
 case 83:
-#line 710 "gram.y"
+#line 713 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, ALIAS);
                            if (yyval.member == NULL) {
@@ -1991,7 +2023,7 @@ case 83:
                        }
 break;
 case 84:
-#line 717 "gram.y"
+#line 720 "gram.y"
 {
                            struct sudo_command *c = calloc(1, sizeof(*c));
                            if (c == NULL) {
@@ -2009,10 +2041,11 @@ case 84:
                        }
 break;
 case 87:
-#line 738 "gram.y"
+#line 741 "gram.y"
 {
                            const char *s;
-                           s = alias_add(yyvsp[-2].string, HOSTALIAS, sudoers, this_lineno, yyvsp[0].member);
+                           s = alias_add(&parsed_policy, yyvsp[-2].string, HOSTALIAS,
+                               sudoers, this_lineno, yyvsp[0].member);
                            if (s != NULL) {
                                sudoerserror(s);
                                YYERROR;
@@ -2020,17 +2053,18 @@ case 87:
                        }
 break;
 case 89:
-#line 749 "gram.y"
+#line 753 "gram.y"
 {
                            HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries);
                            yyval.member = yyvsp[-2].member;
                        }
 break;
 case 92:
-#line 759 "gram.y"
+#line 763 "gram.y"
 {
                            const char *s;
-                           s = alias_add(yyvsp[-2].string, CMNDALIAS, sudoers, this_lineno, yyvsp[0].member);
+                           s = alias_add(&parsed_policy, yyvsp[-2].string, CMNDALIAS,
+                               sudoers, this_lineno, yyvsp[0].member);
                            if (s != NULL) {
                                sudoerserror(s);
                                YYERROR;
@@ -2038,17 +2072,18 @@ case 92:
                        }
 break;
 case 94:
-#line 770 "gram.y"
+#line 775 "gram.y"
 {
                            HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries);
                            yyval.member = yyvsp[-2].member;
                        }
 break;
 case 97:
-#line 780 "gram.y"
+#line 785 "gram.y"
 {
                            const char *s;
-                           s = alias_add(yyvsp[-2].string, RUNASALIAS, sudoers, this_lineno, yyvsp[0].member);
+                           s = alias_add(&parsed_policy, yyvsp[-2].string, RUNASALIAS,
+                               sudoers, this_lineno, yyvsp[0].member);
                            if (s != NULL) {
                                sudoerserror(s);
                                YYERROR;
@@ -2056,10 +2091,11 @@ case 97:
                        }
 break;
 case 100:
-#line 794 "gram.y"
+#line 800 "gram.y"
 {
                            const char *s;
-                           s = alias_add(yyvsp[-2].string, USERALIAS, sudoers, this_lineno, yyvsp[0].member);
+                           s = alias_add(&parsed_policy, yyvsp[-2].string, USERALIAS,
+                               sudoers, this_lineno, yyvsp[0].member);
                            if (s != NULL) {
                                sudoerserror(s);
                                YYERROR;
@@ -2067,28 +2103,28 @@ case 100:
                        }
 break;
 case 102:
-#line 805 "gram.y"
+#line 812 "gram.y"
 {
                            HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries);
                            yyval.member = yyvsp[-2].member;
                        }
 break;
 case 103:
-#line 811 "gram.y"
+#line 818 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = false;
                        }
 break;
 case 104:
-#line 815 "gram.y"
+#line 822 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = true;
                        }
 break;
 case 105:
-#line 821 "gram.y"
+#line 828 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, ALIAS);
                            if (yyval.member == NULL) {
@@ -2098,7 +2134,7 @@ case 105:
                        }
 break;
 case 106:
-#line 828 "gram.y"
+#line 835 "gram.y"
 {
                            yyval.member = new_member(NULL, ALL);
                            if (yyval.member == NULL) {
@@ -2108,7 +2144,7 @@ case 106:
                        }
 break;
 case 107:
-#line 835 "gram.y"
+#line 842 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, NETGROUP);
                            if (yyval.member == NULL) {
@@ -2118,7 +2154,7 @@ case 107:
                        }
 break;
 case 108:
-#line 842 "gram.y"
+#line 849 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, USERGROUP);
                            if (yyval.member == NULL) {
@@ -2128,7 +2164,7 @@ case 108:
                        }
 break;
 case 109:
-#line 849 "gram.y"
+#line 856 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, WORD);
                            if (yyval.member == NULL) {
@@ -2138,28 +2174,28 @@ case 109:
                        }
 break;
 case 111:
-#line 859 "gram.y"
+#line 866 "gram.y"
 {
                            HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries);
                            yyval.member = yyvsp[-2].member;
                        }
 break;
 case 112:
-#line 865 "gram.y"
+#line 872 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = false;
                        }
 break;
 case 113:
-#line 869 "gram.y"
+#line 876 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = true;
                        }
 break;
 case 114:
-#line 875 "gram.y"
+#line 882 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, ALIAS);
                            if (yyval.member == NULL) {
@@ -2169,7 +2205,7 @@ case 114:
                        }
 break;
 case 115:
-#line 882 "gram.y"
+#line 889 "gram.y"
 {
                            yyval.member = new_member(NULL, ALL);
                            if (yyval.member == NULL) {
@@ -2179,7 +2215,7 @@ case 115:
                        }
 break;
 case 116:
-#line 889 "gram.y"
+#line 896 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, WORD);
                            if (yyval.member == NULL) {
@@ -2188,7 +2224,7 @@ case 116:
                            }
                        }
 break;
-#line 2139 "gram.c"
+#line 2175 "gram.c"
     }
     yyssp -= yym;
     yystate = *yyssp;
index 7e3a1ef2abaac93fb02ba91c0c45260ff61a25bb..b8b583305cbf9ba058a37cfd5eddbd26d8929a7b 100644 (file)
@@ -1,6 +1,6 @@
 %{
 /*
- * Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2017
+ * Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2018
  *     Todd C. Miller <Todd.Miller@sudo.ws>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -60,8 +60,11 @@ bool parse_error = false;
 int errorlineno = -1;
 char *errorfile = NULL;
 
-struct defaults_list defaults = TAILQ_HEAD_INITIALIZER(defaults);
-struct userspec_list userspecs = TAILQ_HEAD_INITIALIZER(userspecs);
+struct sudoers_parse_tree parsed_policy = {
+    TAILQ_HEAD_INITIALIZER(parsed_policy.userspecs),
+    TAILQ_HEAD_INITIALIZER(parsed_policy.defaults),
+    NULL /* aliases */
+};
 
 /*
  * Local protoypes
@@ -737,7 +740,8 @@ hostaliases :       hostalias
 
 hostalias      :       ALIAS '=' hostlist {
                            const char *s;
-                           s = alias_add($1, HOSTALIAS, sudoers, this_lineno, $3);
+                           s = alias_add(&parsed_policy, $1, HOSTALIAS,
+                               sudoers, this_lineno, $3);
                            if (s != NULL) {
                                sudoerserror(s);
                                YYERROR;
@@ -758,7 +762,8 @@ cmndaliases :       cmndalias
 
 cmndalias      :       ALIAS '=' cmndlist {
                            const char *s;
-                           s = alias_add($1, CMNDALIAS, sudoers, this_lineno, $3);
+                           s = alias_add(&parsed_policy, $1, CMNDALIAS,
+                               sudoers, this_lineno, $3);
                            if (s != NULL) {
                                sudoerserror(s);
                                YYERROR;
@@ -779,7 +784,8 @@ runasaliases        :       runasalias
 
 runasalias     :       ALIAS '=' userlist {
                            const char *s;
-                           s = alias_add($1, RUNASALIAS, sudoers, this_lineno, $3);
+                           s = alias_add(&parsed_policy, $1, RUNASALIAS,
+                               sudoers, this_lineno, $3);
                            if (s != NULL) {
                                sudoerserror(s);
                                YYERROR;
@@ -793,7 +799,8 @@ useraliases :       useralias
 
 useralias      :       ALIAS '=' userlist {
                            const char *s;
-                           s = alias_add($1, USERALIAS, sudoers, this_lineno, $3);
+                           s = alias_add(&parsed_policy, $1, USERALIAS,
+                               sudoers, this_lineno, $3);
                            if (s != NULL) {
                                sudoerserror(s);
                                YYERROR;
@@ -1027,7 +1034,7 @@ add_defaults(int type, struct member *bmem, struct defaults *defs)
        HLTQ_FOREACH_SAFE(d, defs, entries, next) {
            d->type = type;
            d->binding = binding;
-           TAILQ_INSERT_TAIL(&defaults, d, entries);
+           TAILQ_INSERT_TAIL(&parsed_policy.defaults, d, entries);
        }
     }
 
@@ -1054,7 +1061,7 @@ add_userspec(struct member *members, struct privilege *privs)
     HLTQ_TO_TAILQ(&u->users, members, entries);
     HLTQ_TO_TAILQ(&u->privileges, privs, entries);
     STAILQ_INIT(&u->comments);
-    TAILQ_INSERT_TAIL(&userspecs, u, entries);
+    TAILQ_INSERT_TAIL(&parsed_policy.userspecs, u, entries);
 
     debug_return_bool(true);
 }
@@ -1235,6 +1242,41 @@ free_userspec(struct userspec *us)
     debug_return;
 }
 
+/*
+ * Initialized a sudoers parse tree.
+ */
+void
+init_parse_tree(struct sudoers_parse_tree *parse_tree)
+{
+    TAILQ_INIT(&parse_tree->userspecs);
+    TAILQ_INIT(&parse_tree->defaults);
+    parse_tree->aliases = NULL;
+}
+
+/*
+ * Move the contents of parsed_policy to new_tree.
+ */
+void
+reparent_parse_tree(struct sudoers_parse_tree *new_tree)
+{
+    TAILQ_CONCAT(&new_tree->userspecs, &parsed_policy.userspecs, entries);
+    TAILQ_CONCAT(&new_tree->defaults, &parsed_policy.defaults, entries);
+    new_tree->aliases = parsed_policy.aliases;
+    parsed_policy.aliases = NULL;
+}
+
+/*
+ * Free the contents of a sudoers parse tree and initialize it.
+ */
+void
+free_parse_tree(struct sudoers_parse_tree *parse_tree)
+{
+    free_userspecs(&parse_tree->userspecs);
+    free_defaults(&parse_tree->defaults);
+    free_aliases(parse_tree->aliases);
+    parse_tree->aliases = NULL;
+}
+
 /*
  * Free up space used by data structures from a previous parser run and sets
  * the current sudoers file to path.
@@ -1245,15 +1287,9 @@ init_parser(const char *path, bool quiet)
     bool ret = true;
     debug_decl(init_parser, SUDOERS_DEBUG_PARSER)
 
-    free_userspecs(&userspecs);
-    free_defaults(&defaults);
+    free_parse_tree(&parsed_policy);
     init_lexer();
 
-    if (!init_aliases()) {
-       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-       ret = false;
-    }
-
     rcstr_delref(sudoers);
     if (path != NULL) {
        if ((sudoers = rcstr_dup(path)) == NULL) {
index 0c5047d42bf8ed769f635454bf9ef24ce1b211a9..59512b9a17d8b4990794b8b3059bbe0d26c054ae 100644 (file)
@@ -152,9 +152,7 @@ STAILQ_HEAD(ldap_netgroup_list, ldap_netgroup);
 struct sudo_ldap_handle {
     LDAP *ld;
     struct passwd *pw;
-    struct userspec_list userspecs;
-    struct defaults_list defaults;
-    bool cached_defaults;
+    struct sudoers_parse_tree parse_tree;
 };
 
 #ifdef HAVE_LDAP_INITIALIZE
@@ -1553,8 +1551,7 @@ sudo_ldap_close(struct sudo_nss *nss)
        /* Free the handle container. */
        if (handle->pw != NULL)
            sudo_pw_delref(handle->pw);
-       free_userspecs(&handle->userspecs);
-       free_defaults(&handle->defaults);
+       free_parse_tree(&handle->parse_tree);
        free(handle);
        nss->handle = NULL;
     }
@@ -1660,40 +1657,39 @@ sudo_ldap_open(struct sudo_nss *nss)
     }
     handle->ld = ld;
     /* handle->pw = NULL; */
-    TAILQ_INIT(&handle->userspecs);
-    TAILQ_INIT(&handle->defaults);
+    init_parse_tree(&handle->parse_tree);
     nss->handle = handle;
 
 done:
     debug_return_int(rc == LDAP_SUCCESS ? 0 : -1);
 }
 
-static struct defaults_list *
+static int
 sudo_ldap_getdefs(struct sudo_nss *nss)
 {
     struct sudo_ldap_handle *handle = nss->handle;
-    struct defaults_list *ret = &handle->defaults;
     struct timeval tv, *tvp = NULL;
     struct ldap_config_str *base;
     LDAPMessage *entry, *result = NULL;
     char *filt = NULL;
-    int rc;
+    int rc, ret = -1;
+    static bool cached;
     debug_decl(sudo_ldap_getdefs, SUDOERS_DEBUG_LDAP)
 
     if (handle == NULL) {
        sudo_debug_printf(SUDO_DEBUG_ERROR,
            "%s: called with NULL handle", __func__);
-       debug_return_ptr(NULL);
+       debug_return_int(-1);
     }
 
     /* Use cached result if present. */
-    if (handle->cached_defaults)
-       goto done;
+    if (cached)
+       debug_return_int(0);
 
     filt = sudo_ldap_build_default_filter();
     if (filt == NULL) {
        sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-       debug_return_ptr(NULL);
+       debug_return_int(-1);
     }
     DPRINTF1("Looking for cn=defaults: %s", filt);
 
@@ -1711,21 +1707,20 @@ sudo_ldap_getdefs(struct sudo_nss *nss)
            filt, NULL, 0, NULL, NULL, tvp, 0, &result);
        if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) {
            DPRINTF1("found:%s", ldap_get_dn(ld, entry));
-           if (!sudo_ldap_parse_options(ld, entry, &handle->defaults)) {
-               ret = NULL;
+           if (!sudo_ldap_parse_options(ld, entry, &handle->parse_tree.defaults))
                goto done;
-           }
        } else {
            DPRINTF1("no default options found in %s", base->val);
        }
     }
-    handle->cached_defaults = true;
+    cached = true;
+    ret = 0;
 
 done:
     ldap_msgfree(result);
     free(filt);
 
-    debug_return_ptr(ret);
+    debug_return_int(ret);
 }
 
 /*
@@ -1918,67 +1913,73 @@ oom:
  * Perform LDAP query for user and host and convert to sudoers
  * parse tree.
  */
-static struct userspec_list *
+static int
 sudo_ldap_query(struct sudo_nss *nss, struct passwd *pw)
 {
     struct sudo_ldap_handle *handle = nss->handle;
     struct ldap_result *lres = NULL;
-    struct userspec_list *ret = &handle->userspecs;
+    int ret = -1;
     debug_decl(sudo_ldap_query, SUDOERS_DEBUG_LDAP)
 
     if (handle == NULL) {
        sudo_debug_printf(SUDO_DEBUG_ERROR,
            "%s: called with NULL handle", __func__);
-       debug_return_ptr(NULL);
+       debug_return_int(-1);
     }
 
     /* Use cached result if it matches pw. */
     if (handle->pw != NULL) {
-       if (pw == handle->pw)
+       if (pw == handle->pw) {
+           ret = 0;
            goto done;
+       }
        sudo_pw_delref(handle->pw);
        handle->pw = NULL;
     }
 
     /* Free old userspecs, if any. */
-    free_userspecs(&handle->userspecs);
+    free_userspecs(&handle->parse_tree.userspecs);
 
     DPRINTF1("%s: ldap search user %s, host %s", __func__, pw->pw_name,
        user_runhost);
-    if ((lres = sudo_ldap_result_get(nss, pw)) == NULL) {
-       ret = NULL;
+    if ((lres = sudo_ldap_result_get(nss, pw)) == NULL)
        goto done;
-    }
 
     /* Convert to sudoers parse tree. */
-    if (!ldap_to_sudoers(handle->ld, lres, &handle->userspecs)) {
-       ret = NULL;
+    if (!ldap_to_sudoers(handle->ld, lres, &handle->parse_tree.userspecs))
        goto done;
-    }
 
     /* Stash a ref to the passwd struct in the handle. */
     sudo_pw_addref(pw);
     handle->pw = pw;
 
-    ret = &handle->userspecs;
+    ret = 0;
 
 done:
     /* Cleanup. */
     sudo_ldap_result_free(lres);
-    if (ret == NULL) {
-       free_userspecs(&handle->userspecs);
-       debug_return_ptr(NULL);
-    }
-    debug_return_ptr(ret);
+    if (ret == -1)
+       free_userspecs(&handle->parse_tree.userspecs);
+    debug_return_int(ret);
 }
 
 /*
- * STUB
+ * Return the initialized (but empty) sudoers parse tree.
+ * The contents will be populated by the getdefs() and query() functions.
  */
-static int
+static struct sudoers_parse_tree *
 sudo_ldap_parse(struct sudo_nss *nss)
 {
-    return 0;
+    struct sudo_ldap_handle *handle = nss->handle;
+    debug_decl(sudo_ldap_parse, SUDOERS_DEBUG_LDAP)
+
+    if (handle == NULL) {
+       sudo_debug_printf(SUDO_DEBUG_ERROR,
+           "%s: called with NULL handle", __func__);
+       debug_return_ptr(NULL);
+    }
+
+    debug_return_ptr(&handle->parse_tree);
 }
 
 #if 0
index f0a8e9c40309b1777c6281f5303716857bb0d8cf..71d67fdc9e1be1210acb3794c8808bd1cc346e8b 100644 (file)
@@ -93,7 +93,8 @@ static bool digest_matches(int fd, const char *file, const struct command_digest
  * Returns ALLOW, DENY or UNSPEC.
  */
 int
-user_matches(const struct passwd *pw, const struct member *m)
+user_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw,
+    const struct member *m)
 {
     struct alias *a;
     int matched = UNSPEC;
@@ -114,9 +115,9 @@ user_matches(const struct passwd *pw, const struct member *m)
                matched = !m->negated;
            break;
        case ALIAS:
-           if ((a = alias_get(m->name, USERALIAS)) != NULL) {
+           if ((a = alias_get(parse_tree, m->name, USERALIAS)) != NULL) {
                /* XXX */
-               int rc = userlist_matches(pw, &a->members);
+               int rc = userlist_matches(parse_tree, pw, &a->members);
                if (rc != UNSPEC)
                    matched = m->negated ? !rc : rc;
                alias_put(a);
@@ -136,14 +137,15 @@ user_matches(const struct passwd *pw, const struct member *m)
  * Returns ALLOW, DENY or UNSPEC.
  */
 int
-userlist_matches(const struct passwd *pw, const struct member_list *list)
+userlist_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw,
+    const struct member_list *list)
 {
     struct member *m;
     int matched = UNSPEC;
     debug_decl(userlist_matches, SUDOERS_DEBUG_MATCH)
 
     TAILQ_FOREACH_REVERSE(m, list, member_list, entries) {
-       if ((matched = user_matches(pw, m)) != UNSPEC)
+       if ((matched = user_matches(parse_tree, pw, m)) != UNSPEC)
            break;
     }
     debug_return_int(matched);
@@ -155,9 +157,9 @@ userlist_matches(const struct passwd *pw, const struct member_list *list)
  * Returns ALLOW, DENY or UNSPEC.
  */
 int
-runaslist_matches(const struct member_list *user_list,
-    const struct member_list *group_list, struct member **matching_user,
-    struct member **matching_group)
+runaslist_matches(struct sudoers_parse_tree *parse_tree,
+    const struct member_list *user_list, const struct member_list *group_list,
+    struct member **matching_user, struct member **matching_group)
 {
     struct member *m;
     struct alias *a;
@@ -191,9 +193,10 @@ runaslist_matches(const struct member_list *user_list,
                            user_matched = !m->negated;
                        break;
                    case ALIAS:
-                       if ((a = alias_get(m->name, RUNASALIAS)) != NULL) {
-                           rc = runaslist_matches(&a->members, &empty,
-                               matching_user, NULL);
+                       a = alias_get(parse_tree, m->name, RUNASALIAS);
+                       if (a != NULL) {
+                           rc = runaslist_matches(parse_tree, &a->members,
+                               &empty, matching_user, NULL);
                            if (rc != UNSPEC)
                                user_matched = m->negated ? !rc : rc;
                            alias_put(a);
@@ -234,9 +237,10 @@ runaslist_matches(const struct member_list *user_list,
                        group_matched = !m->negated;
                        break;
                    case ALIAS:
-                       if ((a = alias_get(m->name, RUNASALIAS)) != NULL) {
-                           rc = runaslist_matches(&empty, &a->members,
-                               NULL, matching_group);
+                       a = alias_get(parse_tree, m->name, RUNASALIAS);
+                       if (a != NULL) {
+                           rc = runaslist_matches(parse_tree, &empty,
+                               &a->members, NULL, matching_group);
                            if (rc != UNSPEC)
                                group_matched = m->negated ? !rc : rc;
                            alias_put(a);
@@ -273,15 +277,16 @@ runaslist_matches(const struct member_list *user_list,
  * Returns ALLOW, DENY or UNSPEC.
  */
 static int
-hostlist_matches_int(const struct passwd *pw, const char *lhost,
-    const char *shost, const struct member_list *list)
+hostlist_matches_int(struct sudoers_parse_tree *parse_tree,
+    const struct passwd *pw, const char *lhost, const char *shost,
+    const struct member_list *list)
 {
     struct member *m;
     int matched = UNSPEC;
     debug_decl(hostlist_matches, SUDOERS_DEBUG_MATCH)
 
     TAILQ_FOREACH_REVERSE(m, list, member_list, entries) {
-       matched = host_matches(pw, lhost, shost, m);
+       matched = host_matches(parse_tree, pw, lhost, shost, m);
        if (matched != UNSPEC)
            break;
     }
@@ -293,9 +298,10 @@ hostlist_matches_int(const struct passwd *pw, const char *lhost,
  * Returns ALLOW, DENY or UNSPEC.
  */
 int
-hostlist_matches(const struct passwd *pw, const struct member_list *list)
+hostlist_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw,
+    const struct member_list *list)
 {
-    return hostlist_matches_int(pw, user_runhost, user_srunhost, list);
+    return hostlist_matches_int(parse_tree, pw, user_runhost, user_srunhost, list);
 }
 
 /*
@@ -303,8 +309,8 @@ hostlist_matches(const struct passwd *pw, const struct member_list *list)
  * Returns ALLOW, DENY or UNSPEC.
  */
 int
-host_matches(const struct passwd *pw, const char *lhost, const char *shost,
-    const struct member *m)
+host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw,
+    const char *lhost, const char *shost, const struct member *m)
 {
     struct alias *a;
     int matched = UNSPEC;
@@ -324,9 +330,11 @@ host_matches(const struct passwd *pw, const char *lhost, const char *shost,
                matched = !m->negated;
            break;
        case ALIAS:
-           if ((a = alias_get(m->name, HOSTALIAS)) != NULL) {
+           a = alias_get(parse_tree, m->name, HOSTALIAS);
+           if (a != NULL) {
                /* XXX */
-               int rc = hostlist_matches_int(pw, lhost, shost, &a->members);
+               int rc = hostlist_matches_int(parse_tree, pw, lhost, shost,
+                   &a->members);
                if (rc != UNSPEC)
                    matched = m->negated ? !rc : rc;
                alias_put(a);
@@ -346,14 +354,15 @@ host_matches(const struct passwd *pw, const char *lhost, const char *shost,
  * Returns ALLOW, DENY or UNSPEC.
  */
 int
-cmndlist_matches(const struct member_list *list)
+cmndlist_matches(struct sudoers_parse_tree *parse_tree,
+    const struct member_list *list)
 {
     struct member *m;
     int matched = UNSPEC;
     debug_decl(cmndlist_matches, SUDOERS_DEBUG_MATCH)
 
     TAILQ_FOREACH_REVERSE(m, list, member_list, entries) {
-       matched = cmnd_matches(m);
+       matched = cmnd_matches(parse_tree, m);
        if (matched != UNSPEC)
            break;
     }
@@ -365,7 +374,7 @@ cmndlist_matches(const struct member_list *list)
  * Returns ALLOW, DENY or UNSPEC.
  */
 int
-cmnd_matches(const struct member *m)
+cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m)
 {
     struct alias *a;
     struct sudo_command *c;
@@ -377,8 +386,9 @@ cmnd_matches(const struct member *m)
            matched = !m->negated;
            break;
        case ALIAS:
-           if ((a = alias_get(m->name, CMNDALIAS)) != NULL) {
-               rc = cmndlist_matches(&a->members);
+           a = alias_get(parse_tree, m->name, CMNDALIAS);
+           if (a != NULL) {
+               rc = cmndlist_matches(parse_tree, &a->members);
                if (rc != UNSPEC)
                    matched = m->negated ? !rc : rc;
                alias_put(a);
index d7f3e7e21ff956f444d5d568bbd7ac969550eaab..f61518636c99fb4f3f2da0fad2086ff3b87c48aa 100644 (file)
@@ -65,19 +65,18 @@ sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw,
     CLR(validated, FLAG_NO_HOST);
     match = DENY;
     TAILQ_FOREACH(nss, snl, entries) {
-       struct userspec_list *usl = nss->query(nss, pw);
-       if (usl == NULL) {
+       if (nss->query(nss, pw) == -1) {
            /* The query function should have printed an error message. */
            SET(validated, VALIDATE_ERROR);
            break;
        }
-       TAILQ_FOREACH(us, usl, entries) {
-           if (userlist_matches(pw, &us->users) != ALLOW)
+       TAILQ_FOREACH(us, &nss->parse_tree->userspecs, entries) {
+           if (userlist_matches(nss->parse_tree, pw, &us->users) != ALLOW)
                continue;
            TAILQ_FOREACH(priv, &us->privileges, entries) {
                int priv_nopass = UNSPEC;
 
-               if (hostlist_matches(pw, &priv->hostlist) != ALLOW)
+               if (hostlist_matches(nss->parse_tree, pw, &priv->hostlist) != ALLOW)
                    continue;
                TAILQ_FOREACH(def, &priv->defaults, entries) {
                    if (strcmp(def->var, "authenticate") == 0)
@@ -96,7 +95,7 @@ sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw,
                    /* Only check the command when listing another user. */
                    if (user_uid == 0 || list_pw == NULL ||
                        user_uid == list_pw->pw_uid ||
-                       cmnd_matches(cs->cmnd) == ALLOW)
+                       cmnd_matches(nss->parse_tree, cs->cmnd) == ALLOW)
                            match = ALLOW;
                }
            }
@@ -115,7 +114,7 @@ sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw,
 }
 
 static int
-sudoers_lookup_check(struct userspec_list *usl, struct passwd *pw,
+sudoers_lookup_check(struct sudo_nss *nss, struct passwd *pw,
     int *validated, struct cmndspec **matching_cs,
     struct defaults_list **defs, time_t now)
 {
@@ -126,12 +125,12 @@ sudoers_lookup_check(struct userspec_list *usl, struct passwd *pw,
     struct member *matching_user;
     debug_decl(sudoers_lookup_check, SUDOERS_DEBUG_PARSER)
 
-    TAILQ_FOREACH_REVERSE(us, usl, userspec_list, entries) {
-       if (userlist_matches(pw, &us->users) != ALLOW)
+    TAILQ_FOREACH_REVERSE(us, &nss->parse_tree->userspecs, userspec_list, entries) {
+       if (userlist_matches(nss->parse_tree, pw, &us->users) != ALLOW)
            continue;
        CLR(*validated, FLAG_NO_USER);
        TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) {
-           host_match = hostlist_matches(pw, &priv->hostlist);
+           host_match = hostlist_matches(nss->parse_tree, pw, &priv->hostlist);
            if (host_match == ALLOW)
                CLR(*validated, FLAG_NO_HOST);
            else
@@ -146,10 +145,11 @@ sudoers_lookup_check(struct userspec_list *usl, struct passwd *pw,
                        continue;
                }
                matching_user = NULL;
-               runas_match = runaslist_matches(cs->runasuserlist,
-                   cs->runasgrouplist, &matching_user, NULL);
+               runas_match = runaslist_matches(nss->parse_tree,
+                   cs->runasuserlist, cs->runasgrouplist, &matching_user,
+                   NULL);
                if (runas_match == ALLOW) {
-                   cmnd_match = cmnd_matches(cs->cmnd);
+                   cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd);
                    if (cmnd_match != UNSPEC) {
                        /*
                         * If user is running command as himself,
@@ -273,6 +273,7 @@ sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int validated,
     int pwflag)
 {
     struct defaults_list *defs = NULL;
+    struct sudoers_parse_tree *parse_tree = NULL;
     struct cmndspec *cs = NULL;
     struct sudo_nss *nss;
     int m, match = UNSPEC;
@@ -292,23 +293,24 @@ sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int validated,
     /* Query each sudoers source and check the user. */
     time(&now);
     TAILQ_FOREACH(nss, snl, entries) {
-       struct userspec_list *usl = nss->query(nss, pw);
-       if (usl == NULL) {
+       if (nss->query(nss, pw) == -1) {
            /* The query function should have printed an error message. */
            SET(validated, VALIDATE_ERROR);
            break;
        }
 
-       m = sudoers_lookup_check(usl, pw, &validated, &cs, &defs, now);
-       if (m != UNSPEC)
+       m = sudoers_lookup_check(nss, pw, &validated, &cs, &defs, now);
+       if (m != UNSPEC) {
            match = m;
+           parse_tree = nss->parse_tree;
+       }
 
        if (!sudo_nss_can_continue(nss, m))
            break;
     }
-    if (defs != NULL)
-       update_defaults(defs, SETDEF_GENERIC, false);
     if (match != UNSPEC) {
+       if (defs != NULL)
+           update_defaults(parse_tree, SETDEF_GENERIC, false);
        if (!apply_cmndspec(cs))
            SET(validated, VALIDATE_ERROR);
        else if (match == ALLOW)
@@ -322,8 +324,8 @@ sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int validated,
 }
 
 static int
-display_priv_short(struct passwd *pw, struct userspec *us,
-    struct sudo_lbuf *lbuf)
+display_priv_short(struct sudoers_parse_tree *parse_tree, struct passwd *pw,
+    struct userspec *us, struct sudo_lbuf *lbuf)
 {
     struct cmndspec *cs, *prev_cs;
     struct member *m;
@@ -332,7 +334,7 @@ display_priv_short(struct passwd *pw, struct userspec *us,
     debug_decl(display_priv_short, SUDOERS_DEBUG_PARSER)
 
     TAILQ_FOREACH(priv, &us->privileges, entries) {
-       if (hostlist_matches(pw, &priv->hostlist) != ALLOW)
+       if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW)
            continue;
        prev_cs = NULL;
        TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
@@ -345,7 +347,8 @@ display_priv_short(struct passwd *pw, struct userspec *us,
                    TAILQ_FOREACH(m, cs->runasuserlist, entries) {
                        if (m != TAILQ_FIRST(cs->runasuserlist))
                            sudo_lbuf_append(lbuf, ", ");
-                       sudoers_format_member(lbuf, m, ", ", RUNASALIAS);
+                       sudoers_format_member(lbuf, parse_tree, m, ", ",
+                           RUNASALIAS);
                    }
                } else if (cs->runasgrouplist == NULL) {
                    sudo_lbuf_append(lbuf, "%s", def_runas_default);
@@ -357,14 +360,15 @@ display_priv_short(struct passwd *pw, struct userspec *us,
                    TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
                        if (m != TAILQ_FIRST(cs->runasgrouplist))
                            sudo_lbuf_append(lbuf, ", ");
-                       sudoers_format_member(lbuf, m, ", ", RUNASALIAS);
+                       sudoers_format_member(lbuf, parse_tree, m, ", ",
+                           RUNASALIAS);
                    }
                }
                sudo_lbuf_append(lbuf, ") ");
            } else if (cs != TAILQ_FIRST(&priv->cmndlist)) {
                sudo_lbuf_append(lbuf, ", ");
            }
-           sudoers_format_cmndspec(lbuf, cs, prev_cs, true);
+           sudoers_format_cmndspec(lbuf, parse_tree, cs, prev_cs, true);
            prev_cs = cs;
            nfound++;
        }
@@ -409,8 +413,8 @@ new_long_entry(struct cmndspec *cs, struct cmndspec *prev_cs)
 }
 
 static int
-display_priv_long(struct passwd *pw, struct userspec *us,
-    struct sudo_lbuf *lbuf)
+display_priv_long(struct sudoers_parse_tree *parse_tree, struct passwd *pw,
+    struct userspec *us, struct sudo_lbuf *lbuf)
 {
     struct cmndspec *cs, *prev_cs;
     struct privilege *priv;
@@ -418,7 +422,7 @@ display_priv_long(struct passwd *pw, struct userspec *us,
     debug_decl(display_priv_long, SUDOERS_DEBUG_PARSER)
 
     TAILQ_FOREACH(priv, &us->privileges, entries) {
-       if (hostlist_matches(pw, &priv->hostlist) != ALLOW)
+       if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW)
            continue;
        prev_cs = NULL;
        TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
@@ -437,7 +441,8 @@ display_priv_long(struct passwd *pw, struct userspec *us,
                    TAILQ_FOREACH(m, cs->runasuserlist, entries) {
                        if (m != TAILQ_FIRST(cs->runasuserlist))
                            sudo_lbuf_append(lbuf, ", ");
-                       sudoers_format_member(lbuf, m, ", ", RUNASALIAS);
+                       sudoers_format_member(lbuf, parse_tree, m, ", ",
+                           RUNASALIAS);
                    }
                } else if (cs->runasgrouplist == NULL) {
                    sudo_lbuf_append(lbuf, "%s", def_runas_default);
@@ -450,7 +455,8 @@ display_priv_long(struct passwd *pw, struct userspec *us,
                    TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
                        if (m != TAILQ_FIRST(cs->runasgrouplist))
                            sudo_lbuf_append(lbuf, ", ");
-                       sudoers_format_member(lbuf, m, ", ", RUNASALIAS);
+                       sudoers_format_member(lbuf, parse_tree, m, ", ",
+                           RUNASALIAS);
                    }
                    sudo_lbuf_append(lbuf, "\n");
                }
@@ -512,7 +518,8 @@ display_priv_long(struct passwd *pw, struct userspec *us,
                sudo_lbuf_append(lbuf, _("    Commands:\n"));
            }
            sudo_lbuf_append(lbuf, "\t");
-           sudoers_format_member(lbuf, cs->cmnd, "\n\t", CMNDALIAS);
+           sudoers_format_member(lbuf, parse_tree, cs->cmnd, "\n\t",
+               CMNDALIAS);
            sudo_lbuf_append(lbuf, "\n");
            prev_cs = cs;
            nfound++;
@@ -522,21 +529,21 @@ display_priv_long(struct passwd *pw, struct userspec *us,
 }
 
 static int
-sudo_display_userspecs(struct userspec_list *usl, struct passwd *pw,
+sudo_display_userspecs(struct sudoers_parse_tree *parse_tree, struct passwd *pw,
     struct sudo_lbuf *lbuf)
 {
     struct userspec *us;
     int nfound = 0;
     debug_decl(sudo_display_userspecs, SUDOERS_DEBUG_PARSER)
 
-    TAILQ_FOREACH(us, usl, entries) {
-       if (userlist_matches(pw, &us->users) != ALLOW)
+    TAILQ_FOREACH(us, &parse_tree->userspecs, entries) {
+       if (userlist_matches(parse_tree, pw, &us->users) != ALLOW)
            continue;
 
        if (long_list)
-           nfound += display_priv_long(pw, us, lbuf);
+           nfound += display_priv_long(parse_tree, pw, us, lbuf);
        else
-           nfound += display_priv_short(pw, us, lbuf);
+           nfound += display_priv_short(parse_tree, pw, us, lbuf);
     }
     if (sudo_lbuf_error(lbuf))
        debug_return_int(-1);
@@ -547,7 +554,7 @@ sudo_display_userspecs(struct userspec_list *usl, struct passwd *pw,
  * Display matching Defaults entries for the given user on this host.
  */
 static int
-display_defaults(struct defaults_list *defs, struct passwd *pw,
+display_defaults(struct sudoers_parse_tree *parse_tree, struct passwd *pw,
     struct sudo_lbuf *lbuf)
 {
     struct defaults *d;
@@ -560,14 +567,14 @@ display_defaults(struct defaults_list *defs, struct passwd *pw,
     else
        prefix = ", ";
 
-    TAILQ_FOREACH(d, defs, entries) {
+    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
        switch (d->type) {
            case DEFAULTS_HOST:
-               if (hostlist_matches(pw, d->binding) != ALLOW)
+               if (hostlist_matches(parse_tree, pw, d->binding) != ALLOW)
                    continue;
                break;
            case DEFAULTS_USER:
-               if (userlist_matches(pw, d->binding) != ALLOW)
+               if (userlist_matches(parse_tree, pw, d->binding) != ALLOW)
                    continue;
                break;
            case DEFAULTS_RUNAS:
@@ -588,8 +595,8 @@ display_defaults(struct defaults_list *defs, struct passwd *pw,
  * Display Defaults entries of the given type.
  */
 static int
-display_bound_defaults_by_type(struct defaults_list *defs, int deftype,
-    struct sudo_lbuf *lbuf)
+display_bound_defaults_by_type(struct sudoers_parse_tree *parse_tree,
+    int deftype, struct sudo_lbuf *lbuf)
 {
     struct defaults *d;
     struct member_list *binding = NULL;
@@ -618,7 +625,7 @@ display_bound_defaults_by_type(struct defaults_list *defs, int deftype,
        default:
            debug_return_int(-1);
     }
-    TAILQ_FOREACH(d, defs, entries) {
+    TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
        if (d->type != deftype)
            continue;
 
@@ -631,7 +638,7 @@ display_bound_defaults_by_type(struct defaults_list *defs, int deftype,
            TAILQ_FOREACH(m, binding, entries) {
                if (m != TAILQ_FIRST(binding))
                    sudo_lbuf_append(lbuf, ",");
-               sudoers_format_member(lbuf, m, ", ", atype);
+               sudoers_format_member(lbuf, parse_tree, m, ", ", atype);
                sudo_lbuf_append(lbuf, " ");
            }
        } else
@@ -648,15 +655,15 @@ display_bound_defaults_by_type(struct defaults_list *defs, int deftype,
  * Display Defaults entries that are per-runas or per-command
  */
 static int
-display_bound_defaults(struct defaults_list *defs, struct passwd *pw,
-    struct sudo_lbuf *lbuf)
+display_bound_defaults(struct sudoers_parse_tree *parse_tree,
+    struct passwd *pw, struct sudo_lbuf *lbuf)
 {
     int nfound = 0;
     debug_decl(display_bound_defaults, SUDOERS_DEBUG_PARSER)
 
     /* XXX - should only print ones that match what the user can do. */
-    nfound += display_bound_defaults_by_type(defs, DEFAULTS_RUNAS, lbuf);
-    nfound += display_bound_defaults_by_type(defs, DEFAULTS_CMND, lbuf);
+    nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_RUNAS, lbuf);
+    nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_CMND, lbuf);
 
     if (sudo_lbuf_error(lbuf))
        debug_return_int(-1);
@@ -703,13 +710,10 @@ display_privs(struct sudo_nss_list *snl, struct passwd *pw)
        pw->pw_name, user_srunhost);
     count = 0;
     TAILQ_FOREACH(nss, snl, entries) {
-       struct defaults_list *defs = nss->getdefs(nss);
-       if (defs != NULL) {
-           n = display_defaults(defs, pw, &def_buf);
-           if (n == -1)
-               goto bad;
-           count += n;
-       }
+       n = display_defaults(nss->parse_tree, pw, &def_buf);
+       if (n == -1)
+           goto bad;
+       count += n;
     }
     if (count != 0) {
        sudo_lbuf_append(&def_buf, "\n\n");
@@ -724,13 +728,10 @@ display_privs(struct sudo_nss_list *snl, struct passwd *pw)
        pw->pw_name);
     count = 0;
     TAILQ_FOREACH(nss, snl, entries) {
-       struct defaults_list *defs = nss->getdefs(nss);
-       if (defs != NULL) {
-           n = display_bound_defaults(defs, pw, &def_buf);
-           if (n == -1)
-               goto bad;
-           count += n;
-       }
+       n = display_bound_defaults(nss->parse_tree, pw, &def_buf);
+       if (n == -1)
+           goto bad;
+       count += n;
     }
     if (count != 0) {
        sudo_lbuf_append(&def_buf, "\n\n");
@@ -745,9 +746,8 @@ display_privs(struct sudo_nss_list *snl, struct passwd *pw)
        pw->pw_name, user_srunhost);
     count = 0;
     TAILQ_FOREACH(nss, snl, entries) {
-       struct userspec_list *usl = nss->query(nss, pw);
-       if (usl != NULL) {
-           n = sudo_display_userspecs(usl, pw, &priv_buf);
+       if (nss->query(nss, pw) != -1) {
+           n = sudo_display_userspecs(nss->parse_tree, pw, &priv_buf);
            if (n == -1)
                goto bad;
            count += n;
@@ -778,7 +778,8 @@ bad:
 }
 
 static int
-display_cmnd_check(struct userspec_list *usl, struct passwd *pw, time_t now)
+display_cmnd_check(struct sudoers_parse_tree *parse_tree, struct passwd *pw,
+    time_t now)
 {
     int host_match, runas_match, cmnd_match;
     struct cmndspec *cs;
@@ -786,11 +787,11 @@ display_cmnd_check(struct userspec_list *usl, struct passwd *pw, time_t now)
     struct userspec *us;
     debug_decl(display_cmnd_check, SUDOERS_DEBUG_PARSER)
 
-    TAILQ_FOREACH_REVERSE(us, usl, userspec_list, entries) {
-       if (userlist_matches(pw, &us->users) != ALLOW)
+    TAILQ_FOREACH_REVERSE(us, &parse_tree->userspecs, userspec_list, entries) {
+       if (userlist_matches(parse_tree, pw, &us->users) != ALLOW)
            continue;
        TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) {
-           host_match = hostlist_matches(pw, &priv->hostlist);
+           host_match = hostlist_matches(parse_tree, pw, &priv->hostlist);
            if (host_match != ALLOW)
                continue;
            TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) {
@@ -802,10 +803,10 @@ display_cmnd_check(struct userspec_list *usl, struct passwd *pw, time_t now)
                    if (now > cs->notafter)
                        continue;
                }
-               runas_match = runaslist_matches(cs->runasuserlist,
+               runas_match = runaslist_matches(parse_tree, cs->runasuserlist,
                    cs->runasgrouplist, NULL, NULL);
                if (runas_match == ALLOW) {
-                   cmnd_match = cmnd_matches(cs->cmnd);
+                   cmnd_match = cmnd_matches(parse_tree, cs->cmnd);
                    if (cmnd_match != UNSPEC)
                        debug_return_int(cmnd_match);
                }
@@ -832,13 +833,12 @@ display_cmnd(struct sudo_nss_list *snl, struct passwd *pw)
     /* Iterate over each source, checking for the command. */
     time(&now);
     TAILQ_FOREACH(nss, snl, entries) {
-       struct userspec_list *usl = nss->query(nss, pw);
-       if (usl == NULL) {
+       if (nss->query(nss, pw) == -1) {
            /* The query function should have printed an error message. */
            debug_return_int(-1);
        }
 
-       m = display_cmnd_check(usl, pw, now);
+       m = display_cmnd_check(nss->parse_tree, pw, now);
        if (m != UNSPEC)
            match = m;
 
index 1216f28235e0d97be007df80c8d29fd322630c5d..4d93069e7379f6912f24846c395327d15cfc9301 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 1998-2000, 2004, 2007-2016
+ * Copyright (c) 1996, 1998-2000, 2004, 2007-2018
  *     Todd C. Miller <Todd.Miller@sudo.ws>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -239,26 +239,29 @@ struct defaults {
 };
 
 /*
- * Parsed sudoers info.
+ * Parsed sudoers policy.
  */
-extern struct userspec_list userspecs;
-extern struct defaults_list defaults;
+struct sudoers_parse_tree {
+    struct userspec_list userspecs;
+    struct defaults_list defaults;
+    struct rbtree *aliases;
+};
 
 /* alias.c */
-bool no_aliases(void);
-struct rbtree *replace_aliases(struct rbtree *new_aliases);
-const char *alias_add(char *name, int type, char *file, int lineno, struct member *members);
+struct rbtree *alloc_aliases(void);
+void free_aliases(struct rbtree *aliases);
+bool no_aliases(struct sudoers_parse_tree *parse_tree);
+const char *alias_add(struct sudoers_parse_tree *parse_tree, char *name, int type, char *file, int lineno, struct member *members);
 const char *alias_type_to_string(int alias_type);
-int alias_compare(const void *a1, const void *a2);
-struct alias *alias_get(const char *name, int type);
-struct alias *alias_remove(char *name, int type);
-bool alias_find_used(struct rbtree *used_aliases);
-void alias_apply(int (*func)(void *, void *), void *cookie);
+struct alias *alias_get(struct sudoers_parse_tree *parse_tree, const char *name, int type);
+struct alias *alias_remove(struct sudoers_parse_tree *parse_tree, char *name, int type);
+bool alias_find_used(struct sudoers_parse_tree *parse_tree, struct rbtree *used_aliases);
+void alias_apply(struct sudoers_parse_tree *parse_tree, int (*func)(void *, void *), void *cookie);
 void alias_free(void *a);
 void alias_put(struct alias *a);
-bool init_aliases(void);
 
 /* gram.c */
+extern struct sudoers_parse_tree parsed_policy;
 bool init_parser(const char *path, bool quiet);
 void free_member(struct member *m);
 void free_members(struct member_list *members);
@@ -267,6 +270,9 @@ void free_userspec(struct userspec *us);
 void free_userspecs(struct userspec_list *usl);
 void free_default(struct defaults *def, struct member_list **binding);
 void free_defaults(struct defaults_list *defs);
+void init_parse_tree(struct sudoers_parse_tree *parse_tree);
+void free_parse_tree(struct sudoers_parse_tree *parse_tree);
+void reparent_parse_tree(struct sudoers_parse_tree *new_tree);
 
 /* match_addr.c */
 bool addr_matches(char *n);
@@ -280,13 +286,13 @@ bool hostname_matches(const char *shost, const char *lhost, const char *pattern)
 bool netgr_matches(const char *netgr, const char *lhost, const char *shost, const char *user);
 bool usergr_matches(const char *group, const char *user, const struct passwd *pw);
 bool userpw_matches(const char *sudoers_user, const char *user, const struct passwd *pw);
-int cmnd_matches(const struct member *m);
-int cmndlist_matches(const struct member_list *list);
-int host_matches(const struct passwd *pw, const char *host, const char *shost, const struct member *m);
-int hostlist_matches(const struct passwd *pw, const struct member_list *list);
-int runaslist_matches(const struct member_list *user_list, const struct member_list *group_list, struct member **matching_user, struct member **matching_group);
-int user_matches(const struct passwd *pw, const struct member *m);
-int userlist_matches(const struct passwd *pw, const struct member_list *list);
+int cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m);
+int cmndlist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *list);
+int host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const char *host, const char *shost, const struct member *m);
+int hostlist_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const struct member_list *list);
+int runaslist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *user_list, const struct member_list *group_list, struct member **matching_user, struct member **matching_group);
+int user_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const struct member *m);
+int userlist_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const struct member_list *list);
 const char *sudo_getdomainname(void);
 
 /* toke.c */
@@ -322,12 +328,12 @@ int display_cmnd(struct sudo_nss_list *snl, struct passwd *pw);
 
 /* fmtsudoers.c */
 struct sudo_lbuf;
-bool sudoers_format_cmndspec(struct sudo_lbuf *lbuf, struct cmndspec *cs, struct cmndspec *prev_cs, bool expand_aliases);
+bool sudoers_format_cmndspec(struct sudo_lbuf *lbuf, struct sudoers_parse_tree *parse_tree, struct cmndspec *cs, struct cmndspec *prev_cs, bool expand_aliases);
 bool sudoers_format_default(struct sudo_lbuf *lbuf, struct defaults *d);
-bool sudoers_format_default_line(struct sudo_lbuf *lbuf, struct defaults *d, struct defaults **next, bool expand_aliases);
-bool sudoers_format_member(struct sudo_lbuf *lbuf, struct member *m, const char *separator, int alias_type);
-bool sudoers_format_privilege(struct sudo_lbuf *lbuf, struct privilege *priv, bool expand_aliases);
-bool sudoers_format_userspec(struct sudo_lbuf *lbuf, struct userspec *us, bool expand_aliases);
-bool sudoers_format_userspecs(struct sudo_lbuf *lbuf, struct userspec_list *usl, const char *separator, bool expand_aliases, bool flush);
+bool sudoers_format_default_line(struct sudo_lbuf *lbuf, struct sudoers_parse_tree *parse_tree, struct defaults *d, struct defaults **next, bool expand_aliases);
+bool sudoers_format_member(struct sudo_lbuf *lbuf, struct sudoers_parse_tree *parse_tree, struct member *m, const char *separator, int alias_type);
+bool sudoers_format_privilege(struct sudo_lbuf *lbuf, struct sudoers_parse_tree *parse_tree, struct privilege *priv, bool expand_aliases);
+bool sudoers_format_userspec(struct sudo_lbuf *lbuf, struct sudoers_parse_tree *parse_tree, struct userspec *us, bool expand_aliases);
+bool sudoers_format_userspecs(struct sudo_lbuf *lbuf, struct sudoers_parse_tree *parse_tree, const char *separator, bool expand_aliases, bool flush);
 
 #endif /* SUDOERS_PARSE_H */
index 1b5df7fe63f64bc1aaeee71bcb62bc3cf4857b5b..0c66a4b1ab641fe94abf2cc8208c6867927f8df7 100644 (file)
@@ -83,9 +83,7 @@ struct sudo_sss_handle {
     char *ipa_shost;
     struct passwd *pw;
     void *ssslib;
-    struct userspec_list userspecs;
-    struct defaults_list defaults;
-    bool cached_defaults;
+    struct sudoers_parse_tree parse_tree;
     sss_sudo_send_recv_t fn_send_recv;
     sss_sudo_send_recv_defaults_t fn_send_recv_defaults;
     sss_sudo_free_result_t fn_free_result;
@@ -237,7 +235,7 @@ val_array_iter(void **vp)
 
 static bool
 sss_to_sudoers(struct sudo_sss_handle *handle,
-    struct sss_sudo_result *sss_result, struct userspec_list *sss_userspecs)
+    struct sss_sudo_result *sss_result)
 {
     struct userspec *us;
     struct member *m;
@@ -250,7 +248,7 @@ sss_to_sudoers(struct sudo_sss_handle *handle,
     TAILQ_INIT(&us->users);
     TAILQ_INIT(&us->privileges);
     STAILQ_INIT(&us->comments);
-    TAILQ_INSERT_TAIL(sss_userspecs, us, entries);
+    TAILQ_INSERT_TAIL(&handle->parse_tree.userspecs, us, entries);
 
     /* We only include rules where the user matches. */
     if ((m = calloc(1, sizeof(*m))) == NULL)
@@ -387,7 +385,7 @@ sss_to_sudoers(struct sudo_sss_handle *handle,
 
 oom:
     sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-    free_userspecs(sss_userspecs);
+    free_userspecs(&handle->parse_tree.userspecs);
     debug_return_bool(false);
 }
 
@@ -517,8 +515,7 @@ sudo_sss_close(struct sudo_nss *nss)
        free(handle->ipa_host);
        if (handle->ipa_host != handle->ipa_shost)
            free(handle->ipa_shost);
-       free_userspecs(&handle->userspecs);
-       free_defaults(&handle->defaults);
+       free_parse_tree(&handle->parse_tree);
        free(handle);
        nss->handle = NULL;
     }
@@ -544,9 +541,7 @@ sudo_sss_open(struct sudo_nss *nss)
        sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
        debug_return_int(ENOMEM);
     }
-
-    TAILQ_INIT(&handle->userspecs);
-    TAILQ_INIT(&handle->defaults);
+    init_parse_tree(&handle->parse_tree);
 
     /* Load symbols */
     handle->ssslib = sudo_dso_load(path, SUDO_DSO_LAZY);
@@ -625,18 +620,18 @@ sudo_sss_open(struct sudo_nss *nss)
 /*
  * Perform query for user and host and convert to sudoers parse tree.
  */
-static struct userspec_list *
+static int
 sudo_sss_query(struct sudo_nss *nss, struct passwd *pw)
 {
     struct sudo_sss_handle *handle = nss->handle;
     struct sss_sudo_result *sss_result = NULL;
-    struct userspec_list *ret = &handle->userspecs;
+    int ret = 0;
     debug_decl(sudo_sss_query, SUDOERS_DEBUG_SSSD);
 
     if (handle == NULL) {
        sudo_debug_printf(SUDO_DEBUG_ERROR,
            "%s: called with NULL handle", __func__);
-       debug_return_ptr(NULL);
+       debug_return_int(-1);
     }
 
     /* Use cached result if it matches pw. */
@@ -648,7 +643,7 @@ sudo_sss_query(struct sudo_nss *nss, struct passwd *pw)
     }
 
     /* Free old userspecs, if any. */
-    free_userspecs(&handle->userspecs);
+    free_userspecs(&handle->parse_tree.userspecs);
 
     /* Fetch list of sudoRole entries that match user and host. */
     sss_result = sudo_sss_result_get(nss, pw);
@@ -657,45 +652,59 @@ sudo_sss_query(struct sudo_nss *nss, struct passwd *pw)
        "searching SSSD/LDAP for sudoers entries for user %s, host %s",
         pw->pw_name, user_runhost);
 
-    if (sss_result == NULL)
-       goto done;
-
     /* Stash a ref to the passwd struct in the handle. */
     sudo_pw_addref(pw);
     handle->pw = pw;
 
-    /* Convert to sudoers parse tree. */
-    if (!sss_to_sudoers(handle, sss_result, &handle->userspecs)) {
-       ret = NULL;
-       goto done;
+    /* Convert to sudoers parse tree if the user was found. */
+    if (sss_result != NULL) {
+       if (!sss_to_sudoers(handle, sss_result)) {
+           ret = -1;
+           goto done;
+       }
     }
 
 done:
     /* Cleanup */
     handle->fn_free_result(sss_result);
-    if (ret == NULL) {
-       free_userspecs(&handle->userspecs);
-       sudo_pw_delref(handle->pw);
-       handle->pw = NULL;
+    if (ret == -1) {
+       free_userspecs(&handle->parse_tree.userspecs);
+       if (handle->pw != NULL) {
+           sudo_pw_delref(handle->pw);
+           handle->pw = NULL;
+       }
     }
 
     sudo_debug_printf(SUDO_DEBUG_DIAG, "Done with LDAP searches");
 
-    debug_return_ptr(ret);
+    debug_return_int(ret);
 }
 
-static int
+/*
+ * Return the initialized (but empty) sudoers parse tree.
+ * The contents will be populated by the getdefs() and query() functions.
+ */
+static struct sudoers_parse_tree *
 sudo_sss_parse(struct sudo_nss *nss)
 {
+    struct sudo_sss_handle *handle = nss->handle;
     debug_decl(sudo_sss_parse, SUDOERS_DEBUG_SSSD);
-    debug_return_int(0);
+
+    if (handle == NULL) {
+       sudo_debug_printf(SUDO_DEBUG_ERROR,
+           "%s: called with NULL handle", __func__);
+       debug_return_ptr(NULL);
+    }
+
+    debug_return_ptr(&handle->parse_tree);
 }
 
-static struct defaults_list *
+static int
 sudo_sss_getdefs(struct sudo_nss *nss)
 {
     struct sudo_sss_handle *handle = nss->handle;
     struct sss_sudo_result *sss_result = NULL;
+    static bool cached;
     uint32_t sss_error;
     unsigned int i;
     int rc;
@@ -704,12 +713,12 @@ sudo_sss_getdefs(struct sudo_nss *nss)
     if (handle == NULL) {
        sudo_debug_printf(SUDO_DEBUG_ERROR,
            "%s: called with NULL handle", __func__);
-       debug_return_ptr(NULL);
+       debug_return_int(-1);
     }
 
     /* Use cached result if present. */
-    if (handle->cached_defaults)
-       debug_return_ptr(&handle->defaults);
+    if (cached)
+       debug_return_int(0);
 
     sudo_debug_printf(SUDO_DEBUG_DIAG, "Looking for cn=defaults");
 
@@ -725,7 +734,7 @@ sudo_sss_getdefs(struct sudo_nss *nss)
     default:
        sudo_debug_printf(SUDO_DEBUG_ERROR,
            "handle->fn_send_recv_defaults: rc=%d, sss_error=%u", rc, sss_error);
-       debug_return_ptr(NULL);
+       debug_return_int(-1);
     }
 
     switch (sss_error) {
@@ -735,7 +744,8 @@ sudo_sss_getdefs(struct sudo_nss *nss)
            struct sss_sudo_rule *sss_rule = sss_result->rules + i;
            sudo_debug_printf(SUDO_DEBUG_DIAG,
                "Parsing cn=defaults, %d/%d", i, sss_result->num_rules);
-           if (!sudo_sss_parse_options(handle, sss_rule, &handle->defaults))
+           if (!sudo_sss_parse_options(handle, sss_rule,
+               &handle->parse_tree.defaults))
                goto bad;
        }
        break;
@@ -747,13 +757,13 @@ sudo_sss_getdefs(struct sudo_nss *nss)
        sudo_debug_printf(SUDO_DEBUG_ERROR, "sss_error=%u\n", sss_error);
        goto bad;
     }
-    handle->cached_defaults = true;
     handle->fn_free_result(sss_result);
-    debug_return_ptr(&handle->defaults);
+    cached = true;
+    debug_return_int(0);
 
 bad:
     handle->fn_free_result(sss_result);
-    debug_return_ptr(NULL);
+    debug_return_int(-1);
 }
 
 /* sudo_nss implementation */
index d6d3aa3c0533992bb4d5da3db7222a4781909617..e4566f8ff22c9d9e8ef40b78e14d922bd951896a 100644 (file)
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2007-2011, 2013-2015 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 2007-2011, 2013-2015, 2017-2018
+ *     Todd C. Miller <Todd.Miller@sudo.ws>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -21,14 +22,16 @@ struct passwd;
 struct userspec_list;
 struct defaults_list;
 
+/* XXX - parse_tree, ret_if_found and ret_if_notfound should be private */
 struct sudo_nss {
     TAILQ_ENTRY(sudo_nss) entries;
     int (*open)(struct sudo_nss *nss);
     int (*close)(struct sudo_nss *nss);
-    int (*parse)(struct sudo_nss *nss);
-    struct userspec_list *(*query)(struct sudo_nss *nss, struct passwd *pw);
-    struct defaults_list *(*getdefs)(struct sudo_nss *nss);
+    struct sudoers_parse_tree *(*parse)(struct sudo_nss *nss);
+    int (*query)(struct sudo_nss *nss, struct passwd *pw);
+    int (*getdefs)(struct sudo_nss *nss);
     void *handle;
+    struct sudoers_parse_tree *parse_tree;
     bool ret_if_found;
     bool ret_if_notfound;
 };
index 47fea3400a7be765ed3c26915f77e50bddf2c7d0..28c6ada56991807a18c84156a410a355b61390a2 100644 (file)
@@ -192,17 +192,17 @@ sudoers_policy_init(void *info, char * const envp[])
     sudo_warn_set_locale_func(sudoers_warn_setlocale);
     init_parser(sudoers_file, false);
     TAILQ_FOREACH_SAFE(nss, snl, entries, nss_next) {
-        if (nss->open(nss) == 0 && nss->parse(nss) == 0) {
-           struct defaults_list *defs = nss->getdefs(nss);
-            sources++;
-           if (defs == NULL || !update_defaults(defs,
-               SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) {
-               log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
-                   N_("problem with defaults entries"));
-           }
-        } else {
+       if (nss->open(nss) == -1 || (nss->parse_tree = nss->parse(nss)) == NULL) {
            TAILQ_REMOVE(snl, nss, entries);
-        }
+           continue;
+       }
+
+       sources++;
+       if (nss->getdefs(nss) == -1 || !update_defaults(nss->parse_tree,
+           SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) {
+           log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
+               N_("problem with defaults entries"));
+       }
     }
     if (sources == 0) {
        sudo_warnx(U_("no valid sudoers sources found, quitting"));
@@ -243,12 +243,12 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
     /* Is root even allowed to run sudo? */
     if (user_uid == 0 && !def_root_sudo) {
        /* Not an audit event. */
-        sudo_warnx(U_("sudoers specifies that root is not allowed to sudo"));
-        goto bad;
+       sudo_warnx(U_("sudoers specifies that root is not allowed to sudo"));
+       goto bad;
     }    
 
     if (!set_perms(PERM_INITIAL))
-        goto bad;
+       goto bad;
 
     /* Environment variables specified on the command line. */
     if (env_add != NULL && env_add[0] != NULL)
@@ -854,8 +854,7 @@ set_cmnd(void)
        user_base = user_cmnd;
 
     TAILQ_FOREACH(nss, snl, entries) {
-       struct defaults_list *defs = nss->getdefs(nss);
-       if (defs == NULL || !update_defaults(defs, SETDEF_CMND, false)) {
+       if (!update_defaults(nss->parse_tree, SETDEF_CMND, false)) {
            log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
                N_("problem with defaults entries"));
        }
index 556f4ae69693c1626332705dc11e69583768e855..fde8b743e85a9a0a35a52b1838293e8b767e8ef9 100644 (file)
@@ -285,7 +285,7 @@ main(int argc, char *argv[])
        (void) fputs("Parses OK", stdout);
     }
 
-    if (!update_defaults(&defaults, SETDEF_ALL, false))
+    if (!update_defaults(&parsed_policy, SETDEF_ALL, false))
        (void) fputs(" (problem with defaults entries)", stdout);
     puts(".");
 
@@ -301,22 +301,23 @@ main(int argc, char *argv[])
     /* This loop must match the one in sudo_file_lookup() */
     printf("\nEntries for user %s:\n", user_name);
     match = UNSPEC;
-    TAILQ_FOREACH_REVERSE(us, &userspecs, userspec_list, entries) {
-       if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
+    TAILQ_FOREACH_REVERSE(us, &parsed_policy.userspecs, userspec_list, entries) {
+       if (userlist_matches(&parsed_policy, sudo_user.pw, &us->users) != ALLOW)
            continue;
        TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) {
            sudo_lbuf_append(&lbuf, "\n");
-           sudoers_format_privilege(&lbuf, priv, false);
+           sudoers_format_privilege(&lbuf, &parsed_policy, priv, false);
            sudo_lbuf_print(&lbuf);
-           host_match = hostlist_matches(sudo_user.pw, &priv->hostlist);
+           host_match = hostlist_matches(&parsed_policy, sudo_user.pw,
+               &priv->hostlist);
            if (host_match == ALLOW) {
                puts("\thost  matched");
                TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) {
-                   runas_match = runaslist_matches(cs->runasuserlist,
-                       cs->runasgrouplist, NULL, NULL);
+                   runas_match = runaslist_matches(&parsed_policy,
+                       cs->runasuserlist, cs->runasgrouplist, NULL, NULL);
                    if (runas_match == ALLOW) {
                        puts("\trunas matched");
-                       cmnd_match = cmnd_matches(cs->cmnd);
+                       cmnd_match = cmnd_matches(&parsed_policy, cs->cmnd);
                        if (cmnd_match != UNSPEC)
                            match = cmnd_match;
                        printf("\tcmnd  %s\n", match == ALLOW ? "allowed" :
@@ -483,8 +484,8 @@ print_defaults(struct sudo_lbuf *lbuf)
     struct defaults *def, *next;
     debug_decl(print_defaults, SUDOERS_DEBUG_UTIL)
 
-    TAILQ_FOREACH_SAFE(def, &defaults, entries, next)
-       sudoers_format_default_line(lbuf, def, &next, false);
+    TAILQ_FOREACH_SAFE(def, &parsed_policy.defaults, entries, next)
+       sudoers_format_default_line(lbuf, &parsed_policy, def, &next, false);
 
     debug_return_bool(!sudo_lbuf_error(lbuf));
 }
@@ -502,7 +503,7 @@ print_alias(void *v1, void *v2)
     TAILQ_FOREACH(m, &a->members, entries) {
        if (m != TAILQ_FIRST(&a->members))
            sudo_lbuf_append(lbuf, ", ");
-       sudoers_format_member(lbuf, m, NULL, UNSPEC);
+       sudoers_format_member(lbuf, &parsed_policy, m, NULL, UNSPEC);
     }
     sudo_lbuf_append(lbuf, "\n");
 
@@ -514,7 +515,7 @@ print_aliases(struct sudo_lbuf *lbuf)
 {
     debug_decl(print_aliases, SUDOERS_DEBUG_UTIL)
 
-    alias_apply(print_alias, lbuf);
+    alias_apply(&parsed_policy, print_alias, lbuf);
 
     debug_return_bool(!sudo_lbuf_error(lbuf));
 }
@@ -541,7 +542,7 @@ dump_sudoers(struct sudo_lbuf *lbuf)
     }
 
     /* Print User_Specs */
-    if (!sudoers_format_userspecs(lbuf, &userspecs, NULL, false, true))
+    if (!sudoers_format_userspecs(lbuf, &parsed_policy, NULL, false, true))
        goto done;
     if (lbuf->len > 1) {
        sudo_lbuf_print(lbuf);
index dd9c03d670b0c821b338e4d7d501f59e748780fa..340dbb5c5030d2025f2a49c0d80797719d2862b6 100644 (file)
@@ -246,8 +246,8 @@ main(int argc, char *argv[])
     init_parser(sudoers_file, quiet);
     sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
     (void) sudoersparse();
-    (void) update_defaults(&defaults, SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER,
-       quiet);
+    (void) update_defaults(&parsed_policy,
+       SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER, quiet);
     sudoers_setlocale(oldlocale, NULL);
 
     editor = get_editor(&editor_argc, &editor_argv);
@@ -538,13 +538,13 @@ check_defaults_and_aliases(bool strict, bool quiet)
 {
     debug_decl(check_defaults_and_aliases, SUDOERS_DEBUG_UTIL)
 
-    if (!check_defaults(quiet)) {
+    if (!check_defaults(&parsed_policy, quiet)) {
        struct defaults *d;
        rcstr_delref(errorfile);
        errorfile = NULL;
        errorlineno = -1;
        /* XXX - should edit all files with errors */
-       TAILQ_FOREACH(d, &defaults, entries) {
+       TAILQ_FOREACH(d, &parsed_policy.defaults, entries) {
            if (d->error) {
                /* Defaults parse error, set errorfile/errorlineno. */
                errorfile = rcstr_addref(d->file);
@@ -602,7 +602,7 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv,
        }
        fclose(sudoersin);
        if (!parse_error) {
-           (void) update_defaults(&defaults,
+           (void) update_defaults(&parsed_policy,
                SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER, true);
            check_defaults_and_aliases(strict, quiet);
        }
@@ -920,7 +920,7 @@ check_syntax(const char *sudoers_file, bool quiet, bool strict, bool oldperms)
            sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
     }
     if (!parse_error) {
-       (void) update_defaults(&defaults,
+       (void) update_defaults(&parsed_policy,
            SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER, true);
        check_defaults_and_aliases(strict, quiet);
     }
@@ -1023,7 +1023,7 @@ check_alias(char *name, int type, char *file, int lineno, bool strict, bool quie
     int errors = 0;
     debug_decl(check_alias, SUDOERS_DEBUG_ALIAS)
 
-    if ((a = alias_get(name, type)) != NULL) {
+    if ((a = alias_get(&parsed_policy, name, type)) != NULL) {
        /* check alias contents */
        TAILQ_FOREACH(m, &a->members, entries) {
            if (m->type != ALIAS)
@@ -1071,14 +1071,14 @@ check_aliases(bool strict, bool quiet)
     int errors = 0;
     debug_decl(check_aliases, SUDOERS_DEBUG_ALIAS)
 
-    used_aliases = rbcreate(alias_compare);
+    used_aliases = alloc_aliases();
     if (used_aliases == NULL) {
        sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
        debug_return_int(-1);
     }
 
     /* Forward check. */
-    TAILQ_FOREACH(us, &userspecs, entries) {
+    TAILQ_FOREACH(us, &parsed_policy.userspecs, entries) {
        TAILQ_FOREACH(m, &us->users, entries) {
            if (m->type == ALIAS) {
                errors += check_alias(m->name, USERALIAS,
@@ -1118,13 +1118,13 @@ check_aliases(bool strict, bool quiet)
     }
 
     /* Reverse check (destructive) */
-    if (!alias_find_used(used_aliases))
+    if (!alias_find_used(&parsed_policy, used_aliases))
        errors++;
-    rbdestroy(used_aliases, alias_free);
+    free_aliases(used_aliases);
 
     /* If all aliases were referenced we will have an empty tree. */
-    if (!no_aliases() && !quiet)
-       alias_apply(print_unused, NULL);
+    if (!no_aliases(&parsed_policy) && !quiet)
+       alias_apply(&parsed_policy, print_unused, NULL);
 
     debug_return_int(strict ? errors : 0);
 }