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