From c702957879e42f0d1067003b6043c80a6a3ea020 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Tue, 23 Feb 2021 19:07:12 -0700 Subject: [PATCH] Move alias checking code out of visudo.c and into check_aliases.c. --- plugins/sudoers/check_aliases.c | 149 ++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 plugins/sudoers/check_aliases.c diff --git a/plugins/sudoers/check_aliases.c b/plugins/sudoers/check_aliases.c new file mode 100644 index 000000000..1c29e0501 --- /dev/null +++ b/plugins/sudoers/check_aliases.c @@ -0,0 +1,149 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2004-2005, 2007-2018 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 "sudoers.h" +#include + +static int +check_alias(struct sudoers_parse_tree *parse_tree, char *name, int type, + char *file, int line, int column, bool strict, bool quiet) +{ + struct member *m; + struct alias *a; + int errors = 0; + debug_decl(check_alias, SUDOERS_DEBUG_ALIAS); + + if ((a = alias_get(parse_tree, name, type)) != NULL) { + /* check alias contents */ + TAILQ_FOREACH(m, &a->members, entries) { + if (m->type != ALIAS) + continue; + errors += check_alias(parse_tree, m->name, type, a->file, a->line, + a->column, strict, quiet); + } + alias_put(a); + } else { + if (!quiet) { + if (errno == ELOOP) { + fprintf(stderr, strict ? + U_("Error: %s:%d:%d: cycle in %s \"%s\"") : + U_("Warning: %s:%d:%d: cycle in %s \"%s\""), + file, line, column, alias_type_to_string(type), name); + } else { + fprintf(stderr, strict ? + U_("Error: %s:%d:%d: %s \"%s\" referenced but not defined") : + U_("Warning: %s:%d:%d: %s \"%s\" referenced but not defined"), + file, line, column, alias_type_to_string(type), name); + } + fputc('\n', stderr); + if (strict && errorfile == NULL) { + errorfile = rcstr_addref(file); + errorlineno = line; + } + } + errors++; + } + + debug_return_int(errors); +} + +/* + * Iterate through the sudoers datastructures looking for undefined + * aliases or unused aliases. + */ +int +check_aliases(struct sudoers_parse_tree *parse_tree, bool strict, bool quiet, + int (*cb_unused)(struct sudoers_parse_tree *, struct alias *, void *)) +{ + struct rbtree *used_aliases; + struct cmndspec *cs; + struct member *m; + struct privilege *priv; + struct userspec *us; + int errors = 0; + debug_decl(check_aliases, SUDOERS_DEBUG_ALIAS); + + 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, &parse_tree->userspecs, entries) { + TAILQ_FOREACH(m, &us->users, entries) { + if (m->type == ALIAS) { + errors += check_alias(parse_tree, m->name, USERALIAS, + us->file, us->line, us->column, strict, quiet); + } + } + TAILQ_FOREACH(priv, &us->privileges, entries) { + TAILQ_FOREACH(m, &priv->hostlist, entries) { + if (m->type == ALIAS) { + errors += check_alias(parse_tree, m->name, HOSTALIAS, + us->file, us->line, us->column, strict, quiet); + } + } + TAILQ_FOREACH(cs, &priv->cmndlist, entries) { + if (cs->runasuserlist != NULL) { + TAILQ_FOREACH(m, cs->runasuserlist, entries) { + if (m->type == ALIAS) { + errors += check_alias(parse_tree, m->name, RUNASALIAS, + us->file, us->line, us->column, strict, quiet); + } + } + } + if (cs->runasgrouplist != NULL) { + TAILQ_FOREACH(m, cs->runasgrouplist, entries) { + if (m->type == ALIAS) { + errors += check_alias(parse_tree, m->name, RUNASALIAS, + us->file, us->line, us->column, strict, quiet); + } + } + } + if ((m = cs->cmnd)->type == ALIAS) { + errors += check_alias(parse_tree, m->name, CMNDALIAS, + us->file, us->line, us->column, strict, quiet); + } + } + } + } + + /* Reverse check (destructive) */ + if (!alias_find_used(parse_tree, used_aliases)) + errors++; + free_aliases(used_aliases); + + /* If all aliases were referenced we will have an empty tree. */ + if (!no_aliases(parse_tree)) + alias_apply(parse_tree, cb_unused, &quiet); + + debug_return_int(strict ? errors : 0); +}