1 /*
2 * SPDX-License-Identifier: ISC
3 *
4 * Copyright (c) 2004-2005, 2007-2018 Todd C. Miller <Todd.Miller@sudo.ws>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /*
20 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
21 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
22 */
23
24 #include <config.h>
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30
31 #include "sudoers.h"
32 #include <gram.h>
33
34 static int
check_alias(struct sudoers_parse_tree * parse_tree,char * name,int type,char * file,int line,int column,bool strict,bool quiet)35 check_alias(struct sudoers_parse_tree *parse_tree, char *name, int type,
36 char *file, int line, int column, bool strict, bool quiet)
37 {
38 struct member *m;
39 struct alias *a;
40 int errors = 0;
41 debug_decl(check_alias, SUDOERS_DEBUG_ALIAS);
42
43 if ((a = alias_get(parse_tree, name, type)) != NULL) {
44 /* check alias contents */
45 TAILQ_FOREACH(m, &a->members, entries) {
46 if (m->type != ALIAS)
47 continue;
48 errors += check_alias(parse_tree, m->name, type, a->file, a->line,
49 a->column, strict, quiet);
50 }
51 alias_put(a);
52 } else {
53 if (!quiet) {
54 if (errno == ELOOP) {
55 fprintf(stderr, strict ?
56 U_("Error: %s:%d:%d: cycle in %s \"%s\"") :
57 U_("Warning: %s:%d:%d: cycle in %s \"%s\""),
58 file, line, column, alias_type_to_string(type), name);
59 } else {
60 fprintf(stderr, strict ?
61 U_("Error: %s:%d:%d: %s \"%s\" referenced but not defined") :
62 U_("Warning: %s:%d:%d: %s \"%s\" referenced but not defined"),
63 file, line, column, alias_type_to_string(type), name);
64 }
65 fputc('\n', stderr);
66 if (strict && errorfile == NULL) {
67 errorfile = sudo_rcstr_addref(file);
68 errorlineno = line;
69 }
70 }
71 errors++;
72 }
73
74 debug_return_int(errors);
75 }
76
77 /*
78 * Iterate through the sudoers datastructures looking for undefined
79 * aliases or unused aliases.
80 */
81 int
check_aliases(struct sudoers_parse_tree * parse_tree,bool strict,bool quiet,int (* cb_unused)(struct sudoers_parse_tree *,struct alias *,void *))82 check_aliases(struct sudoers_parse_tree *parse_tree, bool strict, bool quiet,
83 int (*cb_unused)(struct sudoers_parse_tree *, struct alias *, void *))
84 {
85 struct rbtree *used_aliases;
86 struct cmndspec *cs;
87 struct member *m;
88 struct privilege *priv;
89 struct userspec *us;
90 int errors = 0;
91 debug_decl(check_aliases, SUDOERS_DEBUG_ALIAS);
92
93 used_aliases = alloc_aliases();
94 if (used_aliases == NULL) {
95 sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
96 debug_return_int(-1);
97 }
98
99 /* Forward check. */
100 TAILQ_FOREACH(us, &parse_tree->userspecs, entries) {
101 TAILQ_FOREACH(m, &us->users, entries) {
102 if (m->type == ALIAS) {
103 errors += check_alias(parse_tree, m->name, USERALIAS,
104 us->file, us->line, us->column, strict, quiet);
105 }
106 }
107 TAILQ_FOREACH(priv, &us->privileges, entries) {
108 TAILQ_FOREACH(m, &priv->hostlist, entries) {
109 if (m->type == ALIAS) {
110 errors += check_alias(parse_tree, m->name, HOSTALIAS,
111 us->file, us->line, us->column, strict, quiet);
112 }
113 }
114 TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
115 if (cs->runasuserlist != NULL) {
116 TAILQ_FOREACH(m, cs->runasuserlist, entries) {
117 if (m->type == ALIAS) {
118 errors += check_alias(parse_tree, m->name, RUNASALIAS,
119 us->file, us->line, us->column, strict, quiet);
120 }
121 }
122 }
123 if (cs->runasgrouplist != NULL) {
124 TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
125 if (m->type == ALIAS) {
126 errors += check_alias(parse_tree, m->name, RUNASALIAS,
127 us->file, us->line, us->column, strict, quiet);
128 }
129 }
130 }
131 if ((m = cs->cmnd)->type == ALIAS) {
132 errors += check_alias(parse_tree, m->name, CMNDALIAS,
133 us->file, us->line, us->column, strict, quiet);
134 }
135 }
136 }
137 }
138
139 /* Reverse check (destructive) */
140 if (!alias_find_used(parse_tree, used_aliases))
141 errors++;
142 free_aliases(used_aliases);
143
144 /* If all aliases were referenced we will have an empty tree. */
145 if (!no_aliases(parse_tree))
146 alias_apply(parse_tree, cb_unused, &quiet);
147
148 debug_return_int(strict ? errors : 0);
149 }
150