Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/plugins/sudoers/alias.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2004-2005, 2007-2021, 2023
5
* Todd C. Miller <[email protected]>
6
*
7
* Permission to use, copy, modify, and distribute this software for any
8
* purpose with or without fee is hereby granted, provided that the above
9
* copyright notice and this permission notice appear in all copies.
10
*
11
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
*/
19
20
#include <config.h>
21
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <errno.h>
26
27
#include <sudoers.h>
28
#include <redblack.h>
29
#include <gram.h>
30
31
/*
32
* Comparison function for the red-black tree.
33
* Aliases are sorted by name with the type used as a tie-breaker.
34
*/
35
static int
36
alias_compare(const void *v1, const void *v2)
37
{
38
const struct alias *a1 = (const struct alias *)v1;
39
const struct alias *a2 = (const struct alias *)v2;
40
int res;
41
debug_decl(alias_compare, SUDOERS_DEBUG_ALIAS);
42
43
if (a1 == NULL)
44
res = -1;
45
else if (a2 == NULL)
46
res = 1;
47
else if ((res = strcmp(a1->name, a2->name)) == 0)
48
res = a1->type - a2->type;
49
debug_return_int(res);
50
}
51
52
/*
53
* Search the tree for an alias with the specified name and type.
54
* Returns a pointer to the alias structure or NULL if not found.
55
* Caller is responsible for calling alias_put() on the returned
56
* alias to mark it as unused.
57
*/
58
struct alias *
59
alias_get(const struct sudoers_parse_tree *parse_tree, const char *name,
60
short type)
61
{
62
struct alias key;
63
struct rbnode *node;
64
struct alias *a = NULL;
65
debug_decl(alias_get, SUDOERS_DEBUG_ALIAS);
66
67
if (parse_tree->aliases == NULL)
68
debug_return_ptr(NULL);
69
70
key.name = (char *)name;
71
key.type = type;
72
if ((node = rbfind(parse_tree->aliases, &key)) != NULL) {
73
/*
74
* Check whether this alias is already in use.
75
* If so, we've detected a loop. If not, set the flag,
76
* which the caller should clear with a call to alias_put().
77
*/
78
a = node->data;
79
if (a->used) {
80
errno = ELOOP;
81
debug_return_ptr(NULL);
82
}
83
a->used = true;
84
} else {
85
errno = ENOENT;
86
}
87
debug_return_ptr(a);
88
}
89
90
/*
91
* Clear the "used" flag in an alias once the caller is done with it.
92
*/
93
void
94
alias_put(struct alias *a)
95
{
96
debug_decl(alias_put, SUDOERS_DEBUG_ALIAS);
97
a->used = false;
98
debug_return;
99
}
100
101
/*
102
* Add an alias to the aliases redblack tree.
103
* Note that "file" must be a reference-counted string.
104
* Returns true on success and false on failure, setting errno.
105
*/
106
bool
107
alias_add(struct sudoers_parse_tree *parse_tree, char *name,
108
short type, char *file, int line, int column,
109
struct member *members)
110
{
111
struct alias *a;
112
debug_decl(alias_add, SUDOERS_DEBUG_ALIAS);
113
114
if (parse_tree->aliases == NULL) {
115
if ((parse_tree->aliases = alloc_aliases()) == NULL)
116
debug_return_bool(false);
117
}
118
119
a = calloc(1, sizeof(*a));
120
if (a == NULL)
121
debug_return_bool(false);
122
123
/* Only set elements used by alias_compare() in case there is a dupe. */
124
a->name = name;
125
a->type = type;
126
switch (rbinsert(parse_tree->aliases, a, NULL)) {
127
case 1:
128
free(a);
129
errno = EEXIST;
130
debug_return_bool(false);
131
case -1:
132
free(a);
133
debug_return_bool(false);
134
}
135
136
/*
137
* It is now safe to fill in the rest of the alias. We do this last
138
* since it modifies "file" (adds a ref) and "members" (tailq conversion).
139
*/
140
/* a->used = false; */
141
a->file = sudo_rcstr_addref(file);
142
a->line = line;
143
a->column = column;
144
HLTQ_TO_TAILQ(&a->members, members, entries);
145
debug_return_bool(true);
146
}
147
148
/*
149
* Closure to adapt 2-arg rbapply() to 3-arg alias_apply().
150
*/
151
struct alias_apply_closure {
152
struct sudoers_parse_tree *parse_tree;
153
int (*func)(struct sudoers_parse_tree *, struct alias *, void *);
154
void *cookie;
155
};
156
157
/* Adapt rbapply() to alias_apply() calling convention. */
158
static int
159
alias_apply_func(void *v1, void *v2)
160
{
161
struct alias *a = v1;
162
struct alias_apply_closure *closure = v2;
163
164
return closure->func(closure->parse_tree, a, closure->cookie);
165
}
166
167
/*
168
* Apply a function to each alias entry and pass in a cookie.
169
*/
170
bool
171
alias_apply(struct sudoers_parse_tree *parse_tree,
172
int (*func)(struct sudoers_parse_tree *, struct alias *, void *),
173
void *cookie)
174
{
175
struct alias_apply_closure closure;
176
bool ret = true;
177
debug_decl(alias_apply, SUDOERS_DEBUG_ALIAS);
178
179
if (parse_tree->aliases != NULL) {
180
closure.parse_tree = parse_tree;
181
closure.func = func;
182
closure.cookie = cookie;
183
if (rbapply(parse_tree->aliases, alias_apply_func, &closure, inorder) != 0)
184
ret = false;
185
}
186
187
debug_return_bool(ret);
188
}
189
190
/*
191
* Returns true if there are no aliases in the parse_tree, else false.
192
*/
193
bool
194
no_aliases(const struct sudoers_parse_tree *parse_tree)
195
{
196
debug_decl(no_aliases, SUDOERS_DEBUG_ALIAS);
197
debug_return_bool(parse_tree->aliases == NULL ||
198
rbisempty(parse_tree->aliases));
199
}
200
201
/*
202
* Free memory used by an alias struct and its members.
203
*/
204
void
205
alias_free(void *v)
206
{
207
struct alias *a = (struct alias *)v;
208
debug_decl(alias_free, SUDOERS_DEBUG_ALIAS);
209
210
if (a != NULL) {
211
free(a->name);
212
sudo_rcstr_delref(a->file);
213
free_members(&a->members);
214
free(a);
215
}
216
217
debug_return;
218
}
219
220
/*
221
* Find the named alias, remove it from the tree and return it.
222
*/
223
struct alias *
224
alias_remove(struct sudoers_parse_tree *parse_tree, const char *name,
225
short type)
226
{
227
struct rbnode *node;
228
struct alias key;
229
debug_decl(alias_remove, SUDOERS_DEBUG_ALIAS);
230
231
if (parse_tree->aliases != NULL) {
232
key.name = (char *)name;
233
key.type = type;
234
if ((node = rbfind(parse_tree->aliases, &key)) != NULL)
235
debug_return_ptr(rbdelete(parse_tree->aliases, node));
236
}
237
errno = ENOENT;
238
debug_return_ptr(NULL);
239
}
240
241
struct rbtree *
242
alloc_aliases(void)
243
{
244
debug_decl(alloc_aliases, SUDOERS_DEBUG_ALIAS);
245
246
debug_return_ptr(rbcreate(alias_compare));
247
}
248
249
void
250
free_aliases(struct rbtree *aliases)
251
{
252
debug_decl(free_aliases, SUDOERS_DEBUG_ALIAS);
253
254
if (aliases != NULL)
255
rbdestroy(aliases, alias_free);
256
}
257
258
const char *
259
alias_type_to_string(short alias_type)
260
{
261
return alias_type == HOSTALIAS ? "Host_Alias" :
262
alias_type == CMNDALIAS ? "Cmnd_Alias" :
263
alias_type == USERALIAS ? "User_Alias" :
264
alias_type == RUNASALIAS ? "Runas_Alias" :
265
"Invalid_Alias";
266
}
267
268
/*
269
* Remove the alias of the specified type as well as any other aliases
270
* referenced by that alias. Stores removed aliases in a freelist.
271
*/
272
static bool
273
alias_remove_recursive(struct sudoers_parse_tree *parse_tree, char *name,
274
short type, struct rbtree *freelist)
275
{
276
struct member *m;
277
struct alias *a;
278
bool ret = true;
279
debug_decl(alias_remove_recursive, SUDOERS_DEBUG_ALIAS);
280
281
if ((a = alias_remove(parse_tree, name, type)) != NULL) {
282
TAILQ_FOREACH(m, &a->members, entries) {
283
if (m->type == ALIAS) {
284
if (!alias_remove_recursive(parse_tree, m->name, type, freelist))
285
ret = false;
286
}
287
}
288
if (rbinsert(freelist, a, NULL) != 0)
289
ret = false;
290
}
291
debug_return_bool(ret);
292
}
293
294
static int
295
alias_find_used_members(struct sudoers_parse_tree *parse_tree,
296
struct member_list *members, short atype, struct rbtree *used_aliases)
297
{
298
struct member *m;
299
int errors = 0;
300
debug_decl(alias_find_used_members, SUDOERS_DEBUG_ALIAS);
301
302
if (members != NULL) {
303
TAILQ_FOREACH(m, members, entries) {
304
if (m->type != ALIAS)
305
continue;
306
if (!alias_remove_recursive(parse_tree, m->name, atype, used_aliases))
307
errors++;
308
}
309
}
310
311
debug_return_int(errors);
312
}
313
314
/*
315
* Move all aliases referenced by userspecs to used_aliases.
316
*/
317
bool
318
alias_find_used(struct sudoers_parse_tree *parse_tree, struct rbtree *used_aliases)
319
{
320
struct privilege *priv;
321
struct userspec *us;
322
struct cmndspec *cs;
323
struct defaults *d;
324
struct member *m;
325
int errors = 0;
326
debug_decl(alias_find_used, SUDOERS_DEBUG_ALIAS);
327
328
/* Move referenced aliases to used_aliases. */
329
TAILQ_FOREACH(us, &parse_tree->userspecs, entries) {
330
errors += alias_find_used_members(parse_tree, &us->users,
331
USERALIAS, used_aliases);
332
TAILQ_FOREACH(priv, &us->privileges, entries) {
333
errors += alias_find_used_members(parse_tree, &priv->hostlist,
334
HOSTALIAS, used_aliases);
335
TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
336
errors += alias_find_used_members(parse_tree, cs->runasuserlist,
337
RUNASALIAS, used_aliases);
338
errors += alias_find_used_members(parse_tree, cs->runasgrouplist,
339
RUNASALIAS, used_aliases);
340
if ((m = cs->cmnd)->type == ALIAS) {
341
if (!alias_remove_recursive(parse_tree, m->name, CMNDALIAS,
342
used_aliases))
343
errors++;
344
}
345
}
346
}
347
}
348
TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
349
switch (d->type) {
350
case DEFAULTS_HOST:
351
errors += alias_find_used_members(parse_tree,
352
&d->binding->members, HOSTALIAS, used_aliases);
353
break;
354
case DEFAULTS_USER:
355
errors += alias_find_used_members(parse_tree,
356
&d->binding->members, USERALIAS, used_aliases);
357
break;
358
case DEFAULTS_RUNAS:
359
errors += alias_find_used_members(parse_tree,
360
&d->binding->members, RUNASALIAS, used_aliases);
361
break;
362
case DEFAULTS_CMND:
363
errors += alias_find_used_members(parse_tree,
364
&d->binding->members, CMNDALIAS, used_aliases);
365
break;
366
default:
367
break;
368
}
369
}
370
371
debug_return_bool(errors ? false : true);
372
}
373
374