diff --git a/MANIFEST b/MANIFEST index d31022d2c..29dc74fd3 100644 --- a/MANIFEST +++ b/MANIFEST @@ -554,6 +554,7 @@ plugins/sudoers/cvtsudoers.h plugins/sudoers/cvtsudoers_csv.c plugins/sudoers/cvtsudoers_json.c plugins/sudoers/cvtsudoers_ldif.c +plugins/sudoers/cvtsudoers_merge.c plugins/sudoers/cvtsudoers_pwutil.c plugins/sudoers/def_data.c plugins/sudoers/def_data.h diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index 046a5928c..2e18eae87 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -197,12 +197,13 @@ VISUDO_OBJS = check_aliases.o editor.lo find_path.lo gc.lo goodpath.lo \ VISUDO_IOBJS = sudo_printf.i visudo.i CVTSUDOERS_OBJS = b64_encode.o cvtsudoers.o cvtsudoers_json.o cvtsudoers_csv.o \ - cvtsudoers_ldif.o cvtsudoers_pwutil.o fmtsudoers.lo \ - fmtsudoers_cvt.lo locale.lo parse_ldif.o stubs.o \ - sudo_printf.o ldap_util.lo testsudoers_pwutil.o tsgetgrpw.o + cvtsudoers_ldif.o cvtsudoers_merge.o cvtsudoers_pwutil.o \ + fmtsudoers.lo fmtsudoers_cvt.lo locale.lo parse_ldif.o \ + stubs.o sudo_printf.o ldap_util.lo testsudoers_pwutil.o \ + tsgetgrpw.o -CVTSUDOERS_IOBJS = cvtsudoers.i cvtsudoers_json.i cvtsudoers_ldif.i \ - cvtsudoers_pwutil.i +CVTSUDOERS_IOBJS = cvtsudoers.i cvtsudoers_csv.i cvtsudoers_json.i \ + cvtsudoers_ldif.i cvtsudoers_merge.i cvtsudoers_pwutil.i REPLAY_OBJS = getdate.o sudoreplay.o @@ -1424,6 +1425,34 @@ cvtsudoers_ldif.i: $(srcdir)/cvtsudoers_ldif.c $(devdir)/def_data.h \ $(CC) -E -o $@ $(CPPFLAGS) $< cvtsudoers_ldif.plog: cvtsudoers_ldif.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/cvtsudoers_ldif.c --i-file $< --output-file $@ +cvtsudoers_merge.o: $(srcdir)/cvtsudoers_merge.c $(devdir)/def_data.h \ + $(devdir)/gram.h $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ + $(incdir)/sudo_debug.h $(incdir)/sudo_eventlog.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(srcdir)/cvtsudoers.h \ + $(srcdir)/defaults.h $(srcdir)/logging.h $(srcdir)/parse.h \ + $(srcdir)/redblack.h $(srcdir)/strlist.h \ + $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ + $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ + $(top_builddir)/pathnames.h + $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/cvtsudoers_merge.c +cvtsudoers_merge.i: $(srcdir)/cvtsudoers_merge.c $(devdir)/def_data.h \ + $(devdir)/gram.h $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ + $(incdir)/sudo_debug.h $(incdir)/sudo_eventlog.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(srcdir)/cvtsudoers.h \ + $(srcdir)/defaults.h $(srcdir)/logging.h $(srcdir)/parse.h \ + $(srcdir)/redblack.h $(srcdir)/strlist.h \ + $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ + $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ + $(top_builddir)/pathnames.h + $(CC) -E -o $@ $(CPPFLAGS) $< +cvtsudoers_merge.plog: cvtsudoers_merge.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/cvtsudoers_merge.c --i-file $< --output-file $@ cvtsudoers_pwutil.o: $(srcdir)/cvtsudoers_pwutil.c $(devdir)/def_data.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ diff --git a/plugins/sudoers/alias.c b/plugins/sudoers/alias.c index 2b9376311..3adfda8a3 100644 --- a/plugins/sudoers/alias.c +++ b/plugins/sudoers/alias.c @@ -227,14 +227,14 @@ alias_free(void *v) * Find the named alias, remove it from the tree and return it. */ struct alias * -alias_remove(struct sudoers_parse_tree *parse_tree, char *name, int type) +alias_remove(struct sudoers_parse_tree *parse_tree, const char *name, int type) { struct rbnode *node; struct alias key; debug_decl(alias_remove, SUDOERS_DEBUG_ALIAS); if (parse_tree->aliases != NULL) { - key.name = name; + key.name = (char *)name; key.type = type; if ((node = rbfind(parse_tree->aliases, &key)) != NULL) debug_return_ptr(rbdelete(parse_tree->aliases, node)); diff --git a/plugins/sudoers/cvtsudoers.c b/plugins/sudoers/cvtsudoers.c index e2dfa5676..63a2acd59 100644 --- a/plugins/sudoers/cvtsudoers.c +++ b/plugins/sudoers/cvtsudoers.c @@ -104,7 +104,7 @@ int main(int argc, char *argv[]) { struct sudoers_parse_tree_list parse_trees = TAILQ_HEAD_INITIALIZER(parse_trees); - struct sudoers_parse_tree *parse_tree; + struct sudoers_parse_tree *parse_tree, merged_tree; struct cvtsudoers_config *conf = NULL; enum sudoers_formats output_format = format_ldif; enum sudoers_formats input_format = format_sudoers; @@ -415,24 +415,30 @@ main(int argc, char *argv[]) argv++; } while (argc > 0); - /* TODO: merge parse tree into a single one and output that. */ - TAILQ_FOREACH(parse_tree, &parse_trees, entries) { - switch (output_format) { - case format_csv: - exitcode = !convert_sudoers_csv(parse_tree, output_file, conf); - break; - case format_json: - exitcode = !convert_sudoers_json(parse_tree, output_file, conf); - break; - case format_ldif: - exitcode = !convert_sudoers_ldif(parse_tree, output_file, conf); - break; - case format_sudoers: - exitcode = !convert_sudoers_sudoers(parse_tree, output_file, conf); - break; - default: - sudo_fatalx("error: unhandled output format %d", output_format); - } + parse_tree = TAILQ_FIRST(&parse_trees); + if (parse_tree == TAILQ_LAST(&parse_trees, sudoers_parse_tree_list)) { + /* No merging required. */ + goto output; + } + + parse_tree = merge_sudoers(&parse_trees, &merged_tree); + +output: + switch (output_format) { + case format_csv: + exitcode = !convert_sudoers_csv(parse_tree, output_file, conf); + break; + case format_json: + exitcode = !convert_sudoers_json(parse_tree, output_file, conf); + break; + case format_ldif: + exitcode = !convert_sudoers_ldif(parse_tree, output_file, conf); + break; + case format_sudoers: + exitcode = !convert_sudoers_sudoers(parse_tree, output_file, conf); + break; + default: + sudo_fatalx("error: unhandled output format %d", output_format); } done: diff --git a/plugins/sudoers/cvtsudoers.h b/plugins/sudoers/cvtsudoers.h index cdee6d9f2..e759c21c7 100644 --- a/plugins/sudoers/cvtsudoers.h +++ b/plugins/sudoers/cvtsudoers.h @@ -93,6 +93,9 @@ bool convert_sudoers_json(struct sudoers_parse_tree *parse_tree, const char *out /* cvtsudoers_ldif.c */ bool convert_sudoers_ldif(struct sudoers_parse_tree *parse_tree, const char *output_file, struct cvtsudoers_config *conf); +/* cvtsudoers_merge.c */ +struct sudoers_parse_tree *merge_sudoers(struct sudoers_parse_tree_list *parse_trees, struct sudoers_parse_tree *merged_tree); + /* cvtsudoers_pwutil.c */ struct cache_item *cvtsudoers_make_pwitem(uid_t uid, const char *name); struct cache_item *cvtsudoers_make_gritem(gid_t gid, const char *name); diff --git a/plugins/sudoers/cvtsudoers_merge.c b/plugins/sudoers/cvtsudoers_merge.c new file mode 100644 index 000000000..ccb94264b --- /dev/null +++ b/plugins/sudoers/cvtsudoers_merge.c @@ -0,0 +1,534 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2021 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This is an open source non-commercial project. Dear PVS-Studio, please check it. + * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "sudoers.h" +#include "redblack.h" +#include "cvtsudoers.h" +#include + +/* + * Compare two digest lists. + * Returns true if they are the same, else false. + * XXX - should not care about order + */ +static bool +digest_list_equivalent(struct command_digest_list *cdl1, + struct command_digest_list *cdl2) +{ + struct command_digest *cd1 = TAILQ_FIRST(cdl1); + struct command_digest *cd2 = TAILQ_FIRST(cdl2); + debug_decl(digest_list_equivalent, SUDOERS_DEBUG_PARSER); + + while (cd1 != NULL && cd2 != NULL) { + if (cd1->digest_type != cd2->digest_type) + debug_return_bool(false); + if (strcmp(cd1->digest_str, cd2->digest_str) != 0) + debug_return_bool(false); + cd1 = TAILQ_NEXT(cd1, entries); + cd2 = TAILQ_NEXT(cd2, entries); + } + + if (cd1 != NULL || cd2 != NULL) + debug_return_bool(false); + debug_return_bool(true); +} + +/* + * Compare two members. + * Returns true if they are the same, else false. + */ +static bool +member_equivalent(struct member *m1, struct member *m2) +{ + debug_decl(member_equivalent, SUDOERS_DEBUG_PARSER); + + if (m1->type != m2->type || m1->negated != m2->negated) + debug_return_bool(false); + + if (m1->type == COMMAND) { + struct sudo_command *c1 = (struct sudo_command *)m1->name; + struct sudo_command *c2 = (struct sudo_command *)m2->name; + if (c1->cmnd != NULL && c2->cmnd != NULL) { + if (strcmp(c1->cmnd, c2->cmnd) != 0) + debug_return_bool(false); + } else if (c1->cmnd != c2->cmnd) { + debug_return_bool(false); + } + + if (c1->args != NULL && c2->args != NULL) { + if (strcmp(c1->args, c2->args) != 0) + debug_return_bool(false); + } else if (c1->args != c2->args) { + debug_return_bool(false); + } + + if (!digest_list_equivalent(&c1->digests, &c2->digests)) { + debug_return_bool(false); + } + } else { + if (m1->name != NULL && m2->name != NULL) { + if (strcmp(m1->name, m2->name) != 0) + debug_return_bool(false); + } else if (m1->name != m2->name) { + debug_return_bool(false); + } + } + + debug_return_bool(true); +} + +/* + * Compare two member lists. + * Returns true if they are the same, else false. + * XXX - should not care about order if things are not negated. + */ +static bool +member_list_equivalent(struct member_list *ml1, struct member_list *ml2) +{ + struct member *m1 = TAILQ_FIRST(ml1); + struct member *m2 = TAILQ_FIRST(ml2); + debug_decl(member_list_equivalent, SUDOERS_DEBUG_PARSER); + + while (m1 != NULL && m2 != NULL) { + if (!member_equivalent(m1, m2)) + debug_return_bool(false); + m1 = TAILQ_NEXT(m1, entries); + m2 = TAILQ_NEXT(m2, entries); + } + + if (m1 != NULL || m2 != NULL) + debug_return_bool(false); + debug_return_bool(true); +} + +/* + * Generate a unique name from old_name that is not used in parse_tree, + * subsequent parse_trees or merged_tree. + */ +static char * +alias_make_unique(const char *old_name, int type, + struct sudoers_parse_tree *parse_tree0, + struct sudoers_parse_tree *merged_tree) +{ + struct sudoers_parse_tree *parse_tree = parse_tree0; + char *cp, *new_name = NULL; + struct alias *a; + long long suffix; + size_t namelen; + debug_decl(alias_make_unique, SUDOERS_DEBUG_ALIAS); + + /* If old_name already has a suffix, increment it, else start with "_1". */ + suffix = 0; + namelen = strlen(old_name); + cp = strrchr(old_name, '_'); + if (cp != NULL && isdigit((unsigned char)cp[1])) { + suffix = sudo_strtonum(cp + 1, 0, LLONG_MAX, NULL); + if (suffix != 0) { + namelen = (size_t)(cp - old_name); + } + } + + for (;;) { + suffix++; + free(new_name); + if (asprintf(&new_name, "%.*s_%lld", (int)namelen, old_name, suffix) == -1) + sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + /* Make sure new_name is not already in use. */ + a = alias_get(merged_tree, new_name, type); + if (a != NULL) { + alias_put(a); + continue; + } + parse_tree = parse_tree0; + while ((parse_tree = TAILQ_NEXT(parse_tree, entries)) != NULL) { + a = alias_get(parse_tree, new_name, type); + if (a != NULL) { + alias_put(a); + break; + } + } + if (a == NULL) { + /* Must be unique. */ + break; + } + } + + debug_return_ptr(new_name); +} + +struct alias_rename_closure { + const char *old_name; + const char *new_name; + int type; +}; + +static int +alias_rename_members(struct sudoers_parse_tree *parse_tree, struct alias *a, + void *v) +{ + struct alias_rename_closure *closure = v; + struct member *m; + debug_decl(alias_rename_members, SUDOERS_DEBUG_ALIAS); + + if (a->type != closure->type) + debug_return_int(0); + + /* Replace old_name in member list, if present. */ + TAILQ_FOREACH(m, &a->members, entries) { + if (m->type == ALIAS && strcmp(m->name, closure->old_name) == 0) { + char *copy = strdup(closure->new_name); + if (copy == NULL) + sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + free(m->name); + m->name = copy; + } + } + + debug_return_int(0); +} + +static void +alias_rename_defaults(const char *old_name, const char *new_name, + int alias_type, struct defaults_list *defaults) +{ + struct defaults *def; + struct member_list *prev_binding = NULL; + struct member *m; + debug_decl(alias_rename_defaults, SUDOERS_DEBUG_ALIAS); + + TAILQ_FOREACH(def, defaults, entries) { + if (def->binding == prev_binding) + continue; + switch (def->type) { + case DEFAULTS_USER: + if (alias_type != USERALIAS) + goto wrong_type; + break; + case DEFAULTS_RUNAS: + if (alias_type != RUNASALIAS) + goto wrong_type; + break; + case DEFAULTS_HOST: + if (alias_type != HOSTALIAS) + goto wrong_type; + break; + default: + wrong_type: + prev_binding = NULL; + continue; + } + if (def->binding != NULL) { + TAILQ_FOREACH(m, def->binding, entries) { + if (m->type != ALIAS) + continue; + if (strcmp(m->name, old_name) == 0) { + char *copy = strdup(new_name); + if (copy == NULL) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + free(m->name); + m->name = copy; + } + } + } + prev_binding = def->binding; + } + + debug_return; +} + +static void +alias_rename_member(const char *old_name, const char *new_name, + struct member *m) +{ + debug_decl(alias_rename_member, SUDOERS_DEBUG_ALIAS); + + if (m->type == ALIAS && strcmp(m->name, old_name) == 0) { + char *copy = strdup(new_name); + if (copy == NULL) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + free(m->name); + m->name = copy; + } + + debug_return; +} + +static void +alias_rename_member_list(const char *old_name, const char *new_name, + struct member_list *members) +{ + struct member *m; + debug_decl(alias_rename_member_list, SUDOERS_DEBUG_ALIAS); + + TAILQ_FOREACH(m, members, entries) { + alias_rename_member(old_name, new_name, m); + } + + debug_return; +} + +static bool +alias_rename_userspecs(const char *old_name, const char *new_name, + int alias_type, struct userspec_list *userspecs) +{ + struct privilege *priv; + struct cmndspec *cs; + struct userspec *us; + bool ret = true; + debug_decl(alias_rename_userspecs, SUDOERS_DEBUG_ALIAS); + + TAILQ_FOREACH(us, userspecs, entries) { + if (alias_type == USERALIAS) { + alias_rename_member_list(old_name, new_name, &us->users); + } + TAILQ_FOREACH(priv, &us->privileges, entries) { + alias_rename_defaults(old_name, new_name, alias_type, &priv->defaults); + if (alias_type == HOSTALIAS) { + alias_rename_member_list(old_name, new_name, &priv->hostlist); + continue; + } + TAILQ_FOREACH(cs, &priv->cmndlist, entries) { + if (alias_type == CMNDALIAS) { + alias_rename_member(old_name, new_name, cs->cmnd); + continue; + } + if (alias_type == RUNASALIAS) { + if (cs->runasuserlist != NULL) { + alias_rename_member_list(old_name, new_name, cs->runasuserlist); + } + if (cs->runasgrouplist != NULL) { + alias_rename_member_list(old_name, new_name, cs->runasgrouplist); + } + } + } + } + } + + debug_return_bool(ret); +} + +/* + * Rename an alias in parse_tree and all the places where it is used. + */ +static bool +alias_rename(const char *old_name, const char *new_name, int alias_type, + struct sudoers_parse_tree *parse_tree) +{ + struct alias_rename_closure closure = { old_name, new_name, alias_type }; + struct alias *a; + debug_decl(alias_rename, SUDOERS_DEBUG_ALIAS); + + /* Remove under old name and add via new to maintain tree properties. */ + a = alias_remove(parse_tree, old_name, alias_type); + if (a == NULL) { + /* Should not happen. */ + sudo_warnx(U_("unable to find alias %s"), old_name); + debug_return_bool(false); + } + free(a->name); + a->name = strdup(new_name); + if (a->name == NULL) + sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + switch (rbinsert(parse_tree->aliases, a, NULL)) { + case 0: + /* success */ + break; + case 1: + /* Already present, should not happen. */ + errno = EEXIST; + sudo_warn(U_("%s: %s"), __func__, a->name); + break; + default: + sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + } + + /* Rename it in the aliases tree itself (aliases can be nested). */ + alias_apply(parse_tree, alias_rename_members, &closure); + + /* Rename it in the Defaults list. */ + alias_rename_defaults(old_name, new_name, alias_type, &parse_tree->defaults); + + /* Rename it in the userspecs list. */ + alias_rename_userspecs(old_name, new_name, alias_type, &parse_tree->userspecs); + + debug_return_bool(true); +} + +static int +alias_resolve_conflicts(struct sudoers_parse_tree *parse_tree0, struct alias *a, + void *v) +{ + struct sudoers_parse_tree *parse_tree = parse_tree0; + struct sudoers_parse_tree *merged_tree = v; + char *new_name; + int ret; + debug_decl(alias_resolve_conflicts, SUDOERS_DEBUG_ALIAS); + + /* + * Check for conflicting alias names in the subsequent sudoers files. + * Duplicates are removed and conflicting aliases are renamed. + * We cannot modify the alias tree that we are traversing. + */ + while ((parse_tree = TAILQ_NEXT(parse_tree, entries)) != NULL) { + struct alias *b = alias_get(parse_tree, a->name, a->type); + if (b == NULL) + continue; + + /* If alias 'b' is equivalent, remove it. */ + alias_put(b); + if (member_list_equivalent(&a->members, &b->members)) { + sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, + "removing duplicate alias %s from %p", a->name, parse_tree); + b = alias_remove(parse_tree, a->name, a->type); + alias_free(b); + continue; + } + + /* Rename alias 'b' to avoid a naming conflict. */ + new_name = alias_make_unique(a->name, a->type, parse_tree, merged_tree); + alias_rename(a->name, new_name, a->type, parse_tree); + free(new_name); + } + + /* + * The alias will exist in both the original and merged trees. + * This is not a problem as the caller will delete the old trees + * (without freeing the data). + */ + ret = rbinsert(merged_tree->aliases, a, NULL); + switch (ret) { + case 0: + /* success */ + break; + case 1: + /* already present, should not happen. */ + errno = EEXIST; + sudo_warn(U_("%s: %s"), __func__, a->name); + break; + default: + sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + } + + debug_return_int(0); +} + +static bool +merge_aliases(struct sudoers_parse_tree_list *parse_trees, + struct sudoers_parse_tree *merged_tree) +{ + struct sudoers_parse_tree *parse_tree; + debug_decl(merge_aliases, SUDOERS_DEBUG_ALIAS); + + /* + * For each parse_tree, check for collisions with alias names + * in subsequent parse trees. On collision, add a numbered + * suffix (e.g. ALIAS_1) to make the name unique and rename + * any uses of that alias in the affected parse_tree. + */ + TAILQ_FOREACH(parse_tree, parse_trees, entries) { + if (parse_tree->aliases == NULL) + continue; + + /* + * Resolve any conflicts in alias names, renaming aliases as + * needed and eliminating duplicates. + */ + alias_apply(parse_tree, alias_resolve_conflicts, merged_tree); + + /* + * Destroy the old alias tree without freeing the alias data + * which has been copied to merged_tree. + */ + rbdestroy(parse_tree->aliases, NULL); + parse_tree->aliases = NULL; + } + + debug_return_bool(true); +} + +static bool +merge_defaults(struct sudoers_parse_tree_list *parse_trees, + struct sudoers_parse_tree *merged_tree) +{ + struct sudoers_parse_tree *parse_tree; + debug_decl(merge_defaults, SUDOERS_DEBUG_DEFAULTS); + + /* XXX - implement */ + TAILQ_FOREACH(parse_tree, parse_trees, entries) { + TAILQ_CONCAT(&merged_tree->defaults, &parse_tree->defaults, entries); + } + + debug_return_bool(true); +} + +static bool +merge_userspecs(struct sudoers_parse_tree_list *parse_trees, + struct sudoers_parse_tree *merged_tree) +{ + struct sudoers_parse_tree *parse_tree; + debug_decl(merge_userspecs, SUDOERS_DEBUG_PARSER); + + /* XXX - implement */ + TAILQ_FOREACH(parse_tree, parse_trees, entries) { + TAILQ_CONCAT(&merged_tree->userspecs, &parse_tree->userspecs, entries); + } + + debug_return_bool(true); +} + +struct sudoers_parse_tree * +merge_sudoers(struct sudoers_parse_tree_list *parse_trees, + struct sudoers_parse_tree *merged_tree) +{ + debug_decl(merge_sudoers, SUDOERS_DEBUG_UTIL); + + memset(merged_tree, 0, sizeof(*merged_tree)); + TAILQ_INIT(&merged_tree->userspecs); + TAILQ_INIT(&merged_tree->defaults); + if ((merged_tree->aliases = alloc_aliases()) == NULL) + sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + + if (!merge_aliases(parse_trees, merged_tree)) + debug_return_ptr(NULL); + + if (!merge_defaults(parse_trees, merged_tree)) + debug_return_ptr(NULL); + + if (!merge_userspecs(parse_trees, merged_tree)) + debug_return_ptr(NULL); + + debug_return_ptr(merged_tree); +} diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h index 4537b90ad..2984c5833 100644 --- a/plugins/sudoers/parse.h +++ b/plugins/sudoers/parse.h @@ -345,7 +345,7 @@ bool no_aliases(struct sudoers_parse_tree *parse_tree); bool alias_add(struct sudoers_parse_tree *parse_tree, char *name, int type, char *file, int line, int column, struct member *members); const char *alias_type_to_string(int alias_type); 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); +struct alias *alias_remove(struct sudoers_parse_tree *parse_tree, const 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)(struct sudoers_parse_tree *, struct alias *, void *), void *cookie); void alias_free(void *a);