/src/sudo/plugins/sudoers/display.c
Line | Count | Source |
1 | | /* |
2 | | * SPDX-License-Identifier: ISC |
3 | | * |
4 | | * Copyright (c) 2004-2005, 2007-2024 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 | | #include <config.h> |
20 | | |
21 | | #include <sys/stat.h> |
22 | | #include <stdio.h> |
23 | | #include <stdlib.h> |
24 | | #include <string.h> |
25 | | #include <unistd.h> |
26 | | #include <ctype.h> |
27 | | #include <pwd.h> |
28 | | |
29 | | #include <sudoers.h> |
30 | | #include <sudo_lbuf.h> |
31 | | #include <gram.h> |
32 | | |
33 | | static int |
34 | | display_priv_short(const struct sudoers_parse_tree *parse_tree, |
35 | | const struct passwd *pw, const struct userspec *us, struct sudo_lbuf *lbuf) |
36 | 1.15k | { |
37 | 1.15k | struct privilege *priv; |
38 | 1.15k | int nfound = 0; |
39 | 1.15k | debug_decl(display_priv_short, SUDOERS_DEBUG_PARSER); |
40 | | |
41 | 1.17k | TAILQ_FOREACH(priv, &us->privileges, entries) { |
42 | 1.17k | struct cmndspec *cs; |
43 | 1.17k | struct cmndtag tags; |
44 | | |
45 | 1.17k | if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW) |
46 | 1.05k | continue; |
47 | | |
48 | 127 | sudoers_defaults_list_to_tags(&priv->defaults, &tags); |
49 | 156 | TAILQ_FOREACH(cs, &priv->cmndlist, entries) { |
50 | 156 | struct cmndspec *prev_cs = TAILQ_PREV(cs, cmndspec_list, entries); |
51 | | |
52 | 156 | if (prev_cs == NULL || RUNAS_CHANGED(cs, prev_cs)) { |
53 | 127 | struct member *m; |
54 | | |
55 | | /* Start new line, first entry or RunAs changed. */ |
56 | 127 | if (prev_cs != NULL) |
57 | 0 | sudo_lbuf_append(lbuf, "\n"); |
58 | 127 | sudo_lbuf_append(lbuf, " ("); |
59 | 127 | if (cs->runasuserlist != NULL) { |
60 | 979 | TAILQ_FOREACH(m, cs->runasuserlist, entries) { |
61 | 979 | if (m != TAILQ_FIRST(cs->runasuserlist)) |
62 | 909 | sudo_lbuf_append(lbuf, ", "); |
63 | 979 | sudoers_format_member(lbuf, parse_tree, m, ", ", |
64 | 979 | RUNASALIAS); |
65 | 979 | } |
66 | 70 | } else if (cs->runasgrouplist == NULL) { |
67 | 57 | sudo_lbuf_append(lbuf, "%s", def_runas_default); |
68 | 57 | } else { |
69 | 0 | sudo_lbuf_append(lbuf, "%s", pw->pw_name); |
70 | 0 | } |
71 | 127 | if (cs->runasgrouplist != NULL) { |
72 | 0 | sudo_lbuf_append(lbuf, " : "); |
73 | 0 | TAILQ_FOREACH(m, cs->runasgrouplist, entries) { |
74 | 0 | if (m != TAILQ_FIRST(cs->runasgrouplist)) |
75 | 0 | sudo_lbuf_append(lbuf, ", "); |
76 | 0 | sudoers_format_member(lbuf, parse_tree, m, ", ", |
77 | 0 | RUNASALIAS); |
78 | 0 | } |
79 | 0 | } |
80 | 127 | sudo_lbuf_append(lbuf, ") "); |
81 | 127 | sudoers_format_cmndspec(lbuf, parse_tree, cs, NULL, |
82 | 127 | tags, true); |
83 | 127 | } else { |
84 | | /* Continue existing line. */ |
85 | 29 | sudo_lbuf_append(lbuf, ", "); |
86 | 29 | sudoers_format_cmndspec(lbuf, parse_tree, cs, prev_cs, |
87 | 29 | tags, true); |
88 | 29 | } |
89 | 156 | nfound++; |
90 | 156 | } |
91 | 127 | sudo_lbuf_append(lbuf, "\n"); |
92 | 127 | } |
93 | 1.15k | debug_return_int(nfound); |
94 | 1.15k | } |
95 | | |
96 | | /* |
97 | | * Compare the current cmndspec with the previous one to determine |
98 | | * whether we need to start a new long entry for "sudo -ll". |
99 | | * Returns true if we should start a new long entry, else false. |
100 | | */ |
101 | | static bool |
102 | | new_long_entry(const struct cmndspec *cs, const struct cmndspec *prev_cs) |
103 | 156 | { |
104 | 156 | debug_decl(new_long_entry, SUDOERS_DEBUG_PARSER); |
105 | | |
106 | 156 | if (prev_cs == NULL) |
107 | 127 | debug_return_bool(true); |
108 | 29 | if (RUNAS_CHANGED(cs, prev_cs) || TAGS_CHANGED(prev_cs->tags, cs->tags)) |
109 | 9 | debug_return_bool(true); |
110 | 20 | if (cs->privs && (!prev_cs->privs || strcmp(cs->privs, prev_cs->privs) != 0)) |
111 | 0 | debug_return_bool(true); |
112 | 20 | if (cs->limitprivs && (!prev_cs->limitprivs || strcmp(cs->limitprivs, prev_cs->limitprivs) != 0)) |
113 | 0 | debug_return_bool(true); |
114 | 20 | if (cs->role && (!prev_cs->role || strcmp(cs->role, prev_cs->role) != 0)) |
115 | 0 | debug_return_bool(true); |
116 | 20 | if (cs->type && (!prev_cs->type || strcmp(cs->type, prev_cs->type) != 0)) |
117 | 0 | debug_return_bool(true); |
118 | 20 | if (cs->apparmor_profile && (!prev_cs->apparmor_profile || strcmp(cs->apparmor_profile, prev_cs->apparmor_profile) != 0)) |
119 | 0 | debug_return_bool(true); |
120 | 20 | if (cs->runchroot && (!prev_cs->runchroot || strcmp(cs->runchroot, prev_cs->runchroot) != 0)) |
121 | 0 | debug_return_bool(true); |
122 | 20 | if (cs->runcwd && (!prev_cs->runcwd || strcmp(cs->runcwd, prev_cs->runcwd) != 0)) |
123 | 0 | debug_return_bool(true); |
124 | 20 | if (cs->timeout != prev_cs->timeout) |
125 | 0 | debug_return_bool(true); |
126 | 20 | if (cs->notbefore != prev_cs->notbefore) |
127 | 11 | debug_return_bool(true); |
128 | 9 | if (cs->notafter != prev_cs->notafter) |
129 | 0 | debug_return_bool(true); |
130 | 9 | debug_return_bool(false); |
131 | 9 | } |
132 | | |
133 | | static void |
134 | | display_cmndspec_long(const struct sudoers_parse_tree *parse_tree, |
135 | | const struct passwd *pw, const struct userspec *us, |
136 | | const struct privilege *priv, const struct cmndspec *cs, |
137 | | const struct cmndspec *prev_cs, struct sudo_lbuf *lbuf) |
138 | 156 | { |
139 | 156 | const struct defaults *d; |
140 | 156 | const struct member *m; |
141 | 156 | debug_decl(display_cmndspec_long, SUDOERS_DEBUG_PARSER); |
142 | | |
143 | 156 | if (new_long_entry(cs, prev_cs)) { |
144 | 147 | unsigned int olen; |
145 | | |
146 | 147 | if (prev_cs != NULL) |
147 | 20 | sudo_lbuf_append(lbuf, "\n"); |
148 | 147 | if (priv->ldap_role != NULL) { |
149 | 0 | sudo_lbuf_append(lbuf, _("LDAP Role: %s\n"), |
150 | 0 | priv->ldap_role); |
151 | 147 | } else { |
152 | 147 | sudo_lbuf_append(lbuf, _("Sudoers entry: %s\n"), |
153 | 147 | us->file); |
154 | 147 | } |
155 | 147 | sudo_lbuf_append(lbuf, "%s", _(" RunAsUsers: ")); |
156 | 147 | if (cs->runasuserlist != NULL) { |
157 | 3.34k | TAILQ_FOREACH(m, cs->runasuserlist, entries) { |
158 | 3.34k | if (m != TAILQ_FIRST(cs->runasuserlist)) |
159 | 3.25k | sudo_lbuf_append(lbuf, ", "); |
160 | 3.34k | sudoers_format_member(lbuf, parse_tree, m, ", ", |
161 | 3.34k | RUNASALIAS); |
162 | 3.34k | } |
163 | 90 | } else if (cs->runasgrouplist == NULL) { |
164 | 57 | sudo_lbuf_append(lbuf, "%s", def_runas_default); |
165 | 57 | } else { |
166 | 0 | sudo_lbuf_append(lbuf, "%s", pw->pw_name); |
167 | 0 | } |
168 | 147 | sudo_lbuf_append(lbuf, "\n"); |
169 | 147 | if (cs->runasgrouplist != NULL) { |
170 | 0 | sudo_lbuf_append(lbuf, "%s", _(" RunAsGroups: ")); |
171 | 0 | TAILQ_FOREACH(m, cs->runasgrouplist, entries) { |
172 | 0 | if (m != TAILQ_FIRST(cs->runasgrouplist)) |
173 | 0 | sudo_lbuf_append(lbuf, ", "); |
174 | 0 | sudoers_format_member(lbuf, parse_tree, m, ", ", |
175 | 0 | RUNASALIAS); |
176 | 0 | } |
177 | 0 | sudo_lbuf_append(lbuf, "\n"); |
178 | 0 | } |
179 | 147 | olen = lbuf->len; |
180 | 147 | sudo_lbuf_append(lbuf, "%s", _(" Options: ")); |
181 | 147 | TAILQ_FOREACH(d, &priv->defaults, entries) { |
182 | 0 | sudoers_format_default(lbuf, d); |
183 | 0 | sudo_lbuf_append(lbuf, ", "); |
184 | 0 | } |
185 | 147 | if (TAG_SET(cs->tags.setenv)) |
186 | 0 | sudo_lbuf_append(lbuf, "%ssetenv, ", cs->tags.setenv ? "" : "!"); |
187 | 147 | if (TAG_SET(cs->tags.noexec)) |
188 | 0 | sudo_lbuf_append(lbuf, "%snoexec, ", cs->tags.noexec ? "" : "!"); |
189 | 147 | if (TAG_SET(cs->tags.intercept)) |
190 | 0 | sudo_lbuf_append(lbuf, "%sintercept, ", cs->tags.intercept ? "" : "!"); |
191 | 147 | if (TAG_SET(cs->tags.nopasswd)) |
192 | 20 | sudo_lbuf_append(lbuf, "%sauthenticate, ", cs->tags.nopasswd ? "!" : ""); |
193 | 147 | if (TAG_SET(cs->tags.log_input)) |
194 | 0 | sudo_lbuf_append(lbuf, "%slog_input, ", cs->tags.log_input ? "" : "!"); |
195 | 147 | if (TAG_SET(cs->tags.log_output)) |
196 | 0 | sudo_lbuf_append(lbuf, "%slog_output, ", cs->tags.log_output ? "" : "!"); |
197 | 147 | if (lbuf->buf[lbuf->len - 2] == ',') { |
198 | 20 | lbuf->len -= 2; /* remove trailing ", " */ |
199 | 20 | sudo_lbuf_append(lbuf, "\n"); |
200 | 127 | } else { |
201 | 127 | lbuf->len = olen; /* no options */ |
202 | 127 | } |
203 | 147 | if (cs->apparmor_profile != NULL) { |
204 | 0 | sudo_lbuf_append(lbuf, " ApparmorProfile: %s\n", |
205 | 0 | cs->apparmor_profile); |
206 | 0 | } |
207 | 147 | if (cs->privs != NULL) |
208 | 0 | sudo_lbuf_append(lbuf, " Privs: %s\n", cs->privs); |
209 | 147 | if (cs->limitprivs != NULL) |
210 | 0 | sudo_lbuf_append(lbuf, " Limitprivs: %s\n", cs->limitprivs); |
211 | 147 | if (cs->role != NULL) |
212 | 0 | sudo_lbuf_append(lbuf, " Role: %s\n", cs->role); |
213 | 147 | if (cs->type != NULL) |
214 | 0 | sudo_lbuf_append(lbuf, " Type: %s\n", cs->type); |
215 | 147 | if (cs->runchroot != NULL) |
216 | 0 | sudo_lbuf_append(lbuf, " Chroot: %s\n", cs->runchroot); |
217 | 147 | if (cs->runcwd != NULL) |
218 | 2 | sudo_lbuf_append(lbuf, " Cwd: %s\n", cs->runcwd); |
219 | 147 | if (cs->timeout > 0) { |
220 | 0 | char numbuf[STRLEN_MAX_SIGNED(int) + 1]; |
221 | 0 | (void)snprintf(numbuf, sizeof(numbuf), "%d", cs->timeout); |
222 | 0 | sudo_lbuf_append(lbuf, " Timeout: %s\n", numbuf); |
223 | 0 | } |
224 | 147 | if (cs->notbefore != UNSPEC) { |
225 | 11 | char buf[sizeof("CCYYMMDDHHMMSSZ")] = ""; |
226 | 11 | struct tm gmt; |
227 | 11 | size_t len; |
228 | 11 | if (gmtime_r(&cs->notbefore, &gmt) != NULL) { |
229 | 11 | len = strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &gmt); |
230 | 11 | if (len != 0 && buf[sizeof(buf) - 1] == '\0') |
231 | 11 | sudo_lbuf_append(lbuf, " NotBefore: %s\n", buf); |
232 | 11 | } |
233 | 11 | } |
234 | 147 | if (cs->notafter != UNSPEC) { |
235 | 0 | char buf[sizeof("CCYYMMDDHHMMSSZ")] = ""; |
236 | 0 | struct tm gmt; |
237 | 0 | size_t len; |
238 | 0 | if (gmtime_r(&cs->notafter, &gmt) != NULL) { |
239 | 0 | len = strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &gmt); |
240 | 0 | if (len != 0 && buf[sizeof(buf) - 1] == '\0') |
241 | 0 | sudo_lbuf_append(lbuf, " NotAfter: %s\n", buf); |
242 | 0 | } |
243 | 0 | } |
244 | 147 | sudo_lbuf_append(lbuf, "%s", _(" Commands:\n")); |
245 | 147 | } |
246 | 156 | sudo_lbuf_append(lbuf, "\t"); |
247 | 156 | sudoers_format_member(lbuf, parse_tree, cs->cmnd, "\n\t", |
248 | 156 | CMNDALIAS); |
249 | 156 | sudo_lbuf_append(lbuf, "\n"); |
250 | | |
251 | 156 | debug_return; |
252 | 156 | } |
253 | | |
254 | | static int |
255 | | display_priv_long(const struct sudoers_parse_tree *parse_tree, |
256 | | const struct passwd *pw, const struct userspec *us, struct sudo_lbuf *lbuf) |
257 | 1.15k | { |
258 | 1.15k | const struct privilege *priv; |
259 | 1.15k | int nfound = 0; |
260 | 1.15k | debug_decl(display_priv_long, SUDOERS_DEBUG_PARSER); |
261 | | |
262 | 1.17k | TAILQ_FOREACH(priv, &us->privileges, entries) { |
263 | 1.17k | const struct cmndspec *cs, *prev_cs; |
264 | | |
265 | 1.17k | if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW) |
266 | 1.05k | continue; |
267 | 127 | prev_cs = NULL; |
268 | 127 | sudo_lbuf_append(lbuf, "\n"); |
269 | 156 | TAILQ_FOREACH(cs, &priv->cmndlist, entries) { |
270 | 156 | display_cmndspec_long(parse_tree, pw, us, priv, cs, prev_cs, |
271 | 156 | lbuf); |
272 | 156 | prev_cs = cs; |
273 | 156 | nfound++; |
274 | 156 | } |
275 | 127 | } |
276 | 1.15k | debug_return_int(nfound); |
277 | 1.15k | } |
278 | | |
279 | | static int |
280 | | sudo_display_userspecs(struct sudoers_parse_tree *parse_tree, |
281 | | const struct passwd *pw, struct sudo_lbuf *lbuf, bool verbose) |
282 | 24 | { |
283 | 24 | const struct userspec *us; |
284 | 24 | int nfound = 0; |
285 | 24 | debug_decl(sudo_display_userspecs, SUDOERS_DEBUG_PARSER); |
286 | | |
287 | 6.78k | TAILQ_FOREACH(us, &parse_tree->userspecs, entries) { |
288 | 6.78k | if (userlist_matches(parse_tree, pw, &us->users) != ALLOW) |
289 | 4.47k | continue; |
290 | | |
291 | 2.31k | if (verbose) |
292 | 1.15k | nfound += display_priv_long(parse_tree, pw, us, lbuf); |
293 | 1.15k | else |
294 | 1.15k | nfound += display_priv_short(parse_tree, pw, us, lbuf); |
295 | 2.31k | } |
296 | 24 | if (sudo_lbuf_error(lbuf)) |
297 | 0 | debug_return_int(-1); |
298 | 24 | debug_return_int(nfound); |
299 | 24 | } |
300 | | |
301 | | /* |
302 | | * Display matching Defaults entries for the given user on this host. |
303 | | */ |
304 | | static int |
305 | | display_defaults(const struct sudoers_parse_tree *parse_tree, |
306 | | const struct passwd *pw, struct sudo_lbuf *lbuf) |
307 | 24 | { |
308 | 24 | const struct defaults *d; |
309 | 24 | const char *prefix; |
310 | 24 | int nfound = 0; |
311 | 24 | debug_decl(display_defaults, SUDOERS_DEBUG_PARSER); |
312 | | |
313 | 24 | if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1])) |
314 | 24 | prefix = " "; |
315 | 0 | else |
316 | 0 | prefix = ", "; |
317 | | |
318 | 27.0k | TAILQ_FOREACH(d, &parse_tree->defaults, entries) { |
319 | 27.0k | switch (d->type) { |
320 | 0 | case DEFAULTS_HOST: |
321 | 0 | if (hostlist_matches(parse_tree, pw, &d->binding->members) != ALLOW) |
322 | 0 | continue; |
323 | 0 | break; |
324 | 648 | case DEFAULTS_USER: |
325 | 648 | if (userlist_matches(parse_tree, pw, &d->binding->members) != ALLOW) |
326 | 486 | continue; |
327 | 162 | break; |
328 | 576 | case DEFAULTS_RUNAS: |
329 | 1.05k | case DEFAULTS_CMND: |
330 | 1.05k | continue; |
331 | 27.0k | } |
332 | 25.4k | sudo_lbuf_append(lbuf, "%s", prefix); |
333 | 25.4k | sudoers_format_default(lbuf, d); |
334 | 25.4k | prefix = ", "; |
335 | 25.4k | nfound++; |
336 | 25.4k | } |
337 | 24 | if (sudo_lbuf_error(lbuf)) |
338 | 0 | debug_return_int(-1); |
339 | 24 | debug_return_int(nfound); |
340 | 24 | } |
341 | | |
342 | | /* |
343 | | * Display Defaults entries of the given type. |
344 | | */ |
345 | | static int |
346 | | display_bound_defaults_by_type(const struct sudoers_parse_tree *parse_tree, |
347 | | int deftype, struct sudo_lbuf *lbuf) |
348 | 48 | { |
349 | 48 | const struct defaults *d; |
350 | 48 | const struct defaults_binding *binding = NULL; |
351 | 48 | const struct member *m; |
352 | 48 | const char *dsep; |
353 | 48 | short atype; |
354 | 48 | int nfound = 0; |
355 | 48 | debug_decl(display_bound_defaults_by_type, SUDOERS_DEBUG_PARSER); |
356 | | |
357 | 48 | switch (deftype) { |
358 | 0 | case DEFAULTS_HOST: |
359 | 0 | atype = HOSTALIAS; |
360 | 0 | dsep = "@"; |
361 | 0 | break; |
362 | 0 | case DEFAULTS_USER: |
363 | 0 | atype = USERALIAS; |
364 | 0 | dsep = ":"; |
365 | 0 | break; |
366 | 24 | case DEFAULTS_RUNAS: |
367 | 24 | atype = RUNASALIAS; |
368 | 24 | dsep = ">"; |
369 | 24 | break; |
370 | 24 | case DEFAULTS_CMND: |
371 | 24 | atype = CMNDALIAS; |
372 | 24 | dsep = "!"; |
373 | 24 | break; |
374 | 0 | default: |
375 | 0 | debug_return_int(-1); |
376 | 48 | } |
377 | 54.0k | TAILQ_FOREACH(d, &parse_tree->defaults, entries) { |
378 | 54.0k | if (d->type != deftype) |
379 | 53.0k | continue; |
380 | | |
381 | 1.05k | nfound++; |
382 | 1.05k | if (binding != d->binding) { |
383 | 528 | binding = d->binding; |
384 | 528 | if (nfound != 1) |
385 | 512 | sudo_lbuf_append(lbuf, "\n"); |
386 | 528 | sudo_lbuf_append(lbuf, " Defaults%s", dsep); |
387 | 760 | TAILQ_FOREACH(m, &binding->members, entries) { |
388 | 760 | if (m != TAILQ_FIRST(&binding->members)) |
389 | 232 | sudo_lbuf_append(lbuf, ", "); |
390 | 760 | sudoers_format_member(lbuf, parse_tree, m, ", ", atype); |
391 | 760 | } |
392 | 528 | sudo_lbuf_append(lbuf, " "); |
393 | 528 | } else |
394 | 528 | sudo_lbuf_append(lbuf, ", "); |
395 | 1.05k | sudoers_format_default(lbuf, d); |
396 | 1.05k | } |
397 | | |
398 | 48 | if (sudo_lbuf_error(lbuf)) |
399 | 0 | debug_return_int(-1); |
400 | 48 | debug_return_int(nfound); |
401 | 48 | } |
402 | | |
403 | | /* |
404 | | * Display Defaults entries that are per-runas or per-command |
405 | | */ |
406 | | static int |
407 | | display_bound_defaults(const struct sudoers_parse_tree *parse_tree, |
408 | | const struct passwd *pw, struct sudo_lbuf *lbuf) |
409 | 24 | { |
410 | 24 | int nfound = 0; |
411 | 24 | debug_decl(display_bound_defaults, SUDOERS_DEBUG_PARSER); |
412 | | |
413 | | /* XXX - should only print ones that match what the user can do. */ |
414 | 24 | nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_RUNAS, |
415 | 24 | lbuf); |
416 | 24 | nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_CMND, |
417 | 24 | lbuf); |
418 | | |
419 | 24 | if (sudo_lbuf_error(lbuf)) |
420 | 0 | debug_return_int(-1); |
421 | 24 | debug_return_int(nfound); |
422 | 24 | } |
423 | | |
424 | | static int |
425 | | output(const char *buf) |
426 | 32 | { |
427 | 32 | struct sudo_conv_message msg; |
428 | 32 | struct sudo_conv_reply repl; |
429 | 32 | debug_decl(output, SUDOERS_DEBUG_NSS); |
430 | | |
431 | | /* Call conversation function */ |
432 | 32 | memset(&msg, 0, sizeof(msg)); |
433 | 32 | msg.msg_type = SUDO_CONV_INFO_MSG; |
434 | 32 | msg.msg = buf; |
435 | 32 | memset(&repl, 0, sizeof(repl)); |
436 | 32 | if (sudo_conv(1, &msg, &repl, NULL) == -1) |
437 | 0 | debug_return_int(0); |
438 | 32 | debug_return_int((int)strlen(buf)); |
439 | 32 | } |
440 | | |
441 | | /* |
442 | | * Print out privileges for the specified user. |
443 | | * Returns true on success or -1 on error. |
444 | | */ |
445 | | int |
446 | | display_privs(struct sudoers_context *ctx, const struct sudo_nss_list *snl, |
447 | | struct passwd *pw, int verbose) |
448 | 24 | { |
449 | 24 | const struct sudo_nss *nss; |
450 | 24 | struct sudo_lbuf def_buf, priv_buf; |
451 | 24 | int cols, count, n; |
452 | 24 | unsigned int olen; |
453 | 24 | struct stat sb; |
454 | 24 | debug_decl(display_privs, SUDOERS_DEBUG_PARSER); |
455 | | |
456 | 24 | if (verbose < 0) { |
457 | | /* Nothing to display. */ |
458 | 0 | debug_return_int(true); |
459 | 0 | } |
460 | | |
461 | 24 | cols = ctx->user.cols; |
462 | 24 | if (fstat(STDOUT_FILENO, &sb) == 0 && S_ISFIFO(sb.st_mode)) |
463 | 0 | cols = 0; |
464 | 24 | sudo_lbuf_init(&def_buf, output, 4, NULL, cols); |
465 | 24 | sudo_lbuf_init(&priv_buf, output, 8, NULL, cols); |
466 | | |
467 | 24 | sudo_lbuf_append(&def_buf, _("Matching Defaults entries for %s on %s:\n"), |
468 | 24 | pw->pw_name, ctx->runas.shost); |
469 | 24 | count = 0; |
470 | 24 | TAILQ_FOREACH(nss, snl, entries) { |
471 | 24 | n = display_defaults(nss->parse_tree, pw, &def_buf); |
472 | 24 | if (n == -1) |
473 | 0 | goto bad; |
474 | 24 | count += n; |
475 | 24 | } |
476 | 24 | if (count != 0) { |
477 | 8 | sudo_lbuf_append(&def_buf, "\n\n"); |
478 | 16 | } else { |
479 | | /* Undo Defaults header. */ |
480 | 16 | def_buf.len = 0; |
481 | 16 | } |
482 | | |
483 | | /* Display Runas and Cmnd-specific defaults. */ |
484 | 24 | olen = def_buf.len; |
485 | 24 | sudo_lbuf_append(&def_buf, _("Runas and Command-specific defaults for %s:\n"), |
486 | 24 | pw->pw_name); |
487 | 24 | count = 0; |
488 | 24 | TAILQ_FOREACH(nss, snl, entries) { |
489 | 24 | n = display_bound_defaults(nss->parse_tree, pw, &def_buf); |
490 | 24 | if (n == -1) |
491 | 0 | goto bad; |
492 | 24 | count += n; |
493 | 24 | } |
494 | 24 | if (count != 0) { |
495 | 8 | sudo_lbuf_append(&def_buf, "\n\n"); |
496 | 16 | } else { |
497 | | /* Undo Defaults header. */ |
498 | 16 | def_buf.len = olen; |
499 | 16 | } |
500 | | |
501 | | /* Display privileges from all sources. */ |
502 | 24 | sudo_lbuf_append(&priv_buf, |
503 | 24 | _("User %s may run the following commands on %s:\n"), |
504 | 24 | pw->pw_name, ctx->runas.shost); |
505 | 24 | count = 0; |
506 | 24 | TAILQ_FOREACH(nss, snl, entries) { |
507 | 24 | if (nss->query(ctx, nss, pw) != -1) { |
508 | 24 | n = sudo_display_userspecs(nss->parse_tree, pw, &priv_buf, |
509 | 24 | verbose); |
510 | 24 | if (n == -1) |
511 | 0 | goto bad; |
512 | 24 | count += n; |
513 | 24 | } |
514 | 24 | } |
515 | 24 | if (count == 0) { |
516 | 16 | def_buf.len = 0; |
517 | 16 | priv_buf.len = 0; |
518 | 16 | sudo_lbuf_append(&priv_buf, |
519 | 16 | _("User %s is not allowed to run sudo on %s.\n"), |
520 | 16 | pw->pw_name, ctx->runas.shost); |
521 | 16 | } |
522 | 24 | if (sudo_lbuf_error(&def_buf) || sudo_lbuf_error(&priv_buf)) |
523 | 0 | goto bad; |
524 | | |
525 | 24 | sudo_lbuf_print(&def_buf); |
526 | 24 | sudo_lbuf_print(&priv_buf); |
527 | | |
528 | 24 | sudo_lbuf_destroy(&def_buf); |
529 | 24 | sudo_lbuf_destroy(&priv_buf); |
530 | | |
531 | 24 | debug_return_int(true); |
532 | 0 | bad: |
533 | 0 | sudo_lbuf_destroy(&def_buf); |
534 | 0 | sudo_lbuf_destroy(&priv_buf); |
535 | |
|
536 | 0 | debug_return_int(-1); |
537 | 0 | } |
538 | | |
539 | | static int |
540 | | display_cmnd_check(struct sudoers_context *ctx, |
541 | | const struct sudoers_parse_tree *parse_tree, const struct passwd *pw, |
542 | | time_t now, struct sudoers_match_info *match_info) |
543 | 0 | { |
544 | 0 | int host_match, runas_match, cmnd_match = UNSPEC; |
545 | 0 | char *saved_user_cmnd, *saved_user_base; |
546 | 0 | const struct privilege *priv; |
547 | 0 | const struct userspec *us; |
548 | 0 | const struct cmndspec *cs; |
549 | 0 | debug_decl(display_cmnd_check, SUDOERS_DEBUG_PARSER); |
550 | | |
551 | | /* |
552 | | * For "sudo -l command", ctx->user.cmnd is "list" and the actual |
553 | | * command we are checking is in ctx->user.cmnd_list. |
554 | | */ |
555 | 0 | saved_user_cmnd = ctx->user.cmnd; |
556 | 0 | saved_user_base = ctx->user.cmnd_base; |
557 | 0 | ctx->user.cmnd = ctx->user.cmnd_list; |
558 | 0 | ctx->user.cmnd_base = sudo_basename(ctx->user.cmnd); |
559 | |
|
560 | 0 | TAILQ_FOREACH_REVERSE(us, &parse_tree->userspecs, userspec_list, entries) { |
561 | 0 | if (userlist_matches(parse_tree, pw, &us->users) != ALLOW) |
562 | 0 | continue; |
563 | 0 | TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) { |
564 | 0 | host_match = hostlist_matches(parse_tree, pw, &priv->hostlist); |
565 | 0 | if (host_match != ALLOW) |
566 | 0 | continue; |
567 | 0 | TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) { |
568 | 0 | if (cs->notbefore != UNSPEC) { |
569 | 0 | if (now < cs->notbefore) |
570 | 0 | continue; |
571 | 0 | } |
572 | 0 | if (cs->notafter != UNSPEC) { |
573 | 0 | if (now > cs->notafter) |
574 | 0 | continue; |
575 | 0 | } |
576 | 0 | runas_match = runaslist_matches(parse_tree, cs->runasuserlist, |
577 | 0 | cs->runasgrouplist); |
578 | 0 | if (runas_match == ALLOW) { |
579 | 0 | cmnd_match = cmnd_matches(parse_tree, cs->cmnd, |
580 | 0 | cs->runchroot, NULL); |
581 | 0 | if (cmnd_match != UNSPEC) { |
582 | 0 | match_info->parse_tree = parse_tree; |
583 | 0 | match_info->us = us; |
584 | 0 | match_info->priv = priv; |
585 | 0 | match_info->cs = cs; |
586 | 0 | goto done; |
587 | 0 | } |
588 | 0 | } |
589 | 0 | } |
590 | 0 | } |
591 | 0 | } |
592 | 0 | done: |
593 | 0 | ctx->user.cmnd = saved_user_cmnd; |
594 | 0 | ctx->user.cmnd_base = saved_user_base; |
595 | 0 | debug_return_int(cmnd_match); |
596 | 0 | } |
597 | | |
598 | | /* |
599 | | * Check ctx->user.cmnd against sudoers and print the matching entry if the |
600 | | * command is allowed. |
601 | | * Returns true if the command is allowed, false if not or -1 on error. |
602 | | */ |
603 | | int |
604 | | display_cmnd(struct sudoers_context *ctx, const struct sudo_nss_list *snl, |
605 | | struct passwd *pw, int verbose) |
606 | 0 | { |
607 | 0 | struct sudoers_match_info match_info = { NULL }; |
608 | 0 | struct sudo_lbuf lbuf; |
609 | 0 | struct sudo_nss *nss; |
610 | 0 | int m, match = UNSPEC; |
611 | 0 | int ret = false; |
612 | 0 | time_t now; |
613 | 0 | debug_decl(display_cmnd, SUDOERS_DEBUG_PARSER); |
614 | | |
615 | | /* Iterate over each source, checking for the command. */ |
616 | 0 | time(&now); |
617 | 0 | sudo_lbuf_init(&lbuf, output, 0, NULL, 0); |
618 | 0 | TAILQ_FOREACH(nss, snl, entries) { |
619 | 0 | if (nss->query(ctx, nss, pw) == -1) { |
620 | | /* The query function should have printed an error message. */ |
621 | 0 | debug_return_int(-1); |
622 | 0 | } |
623 | | |
624 | 0 | m = display_cmnd_check(ctx, nss->parse_tree, pw, now, &match_info); |
625 | 0 | if (m != UNSPEC) |
626 | 0 | match = m; |
627 | |
|
628 | 0 | if (!sudo_nss_can_continue(nss, m)) |
629 | 0 | break; |
630 | 0 | } |
631 | 0 | if (match == ALLOW) { |
632 | 0 | if (verbose < 0) { |
633 | | /* Nothing to display. */ |
634 | 0 | debug_return_int(true); |
635 | 0 | } |
636 | 0 | if (verbose) { |
637 | | /* Append matching sudoers rule (long form). */ |
638 | 0 | display_cmndspec_long(match_info.parse_tree, pw, match_info.us, |
639 | 0 | match_info.priv, match_info.cs, NULL, &lbuf); |
640 | 0 | sudo_lbuf_append(&lbuf, " Matched: "); |
641 | 0 | } |
642 | 0 | sudo_lbuf_append(&lbuf, "%s%s%s\n", ctx->user.cmnd_list, |
643 | 0 | ctx->user.cmnd_args ? " " : "", |
644 | 0 | ctx->user.cmnd_args ? ctx->user.cmnd_args : ""); |
645 | 0 | sudo_lbuf_print(&lbuf); |
646 | 0 | ret = sudo_lbuf_error(&lbuf) ? -1 : true; |
647 | 0 | sudo_lbuf_destroy(&lbuf); |
648 | 0 | } |
649 | 0 | debug_return_int(ret); |
650 | 0 | } |