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