/src/sudo/plugins/sudoers/parse.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * SPDX-License-Identifier: ISC |
3 | | * |
4 | | * Copyright (c) 2004-2005, 2007-2023 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 | | runas_matches_pw(struct sudoers_parse_tree *parse_tree, |
40 | | const struct cmndspec *cs, const struct passwd *pw) |
41 | 0 | { |
42 | 0 | debug_decl(runas_matches_pw, SUDOERS_DEBUG_PARSER); |
43 | |
|
44 | 0 | if (cs->runasuserlist != NULL) |
45 | 0 | debug_return_int(userlist_matches(parse_tree, pw, cs->runasuserlist)); |
46 | | |
47 | 0 | if (cs->runasgrouplist == NULL) { |
48 | | /* No explicit runas user or group, use default. */ |
49 | 0 | if (userpw_matches(def_runas_default, pw->pw_name, pw)) |
50 | 0 | debug_return_int(ALLOW); |
51 | 0 | } |
52 | 0 | debug_return_int(UNSPEC); |
53 | 0 | } |
54 | | |
55 | | /* |
56 | | * Look up the user in the sudoers parse tree for pseudo-commands like |
57 | | * list, verify and kill. |
58 | | */ |
59 | | static int |
60 | | sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw, int pwflag) |
61 | 16 | { |
62 | 16 | char *saved_runchroot; |
63 | 16 | struct passwd *root_pw = NULL; |
64 | 16 | struct sudo_nss *nss; |
65 | 16 | struct cmndspec *cs; |
66 | 16 | struct privilege *priv; |
67 | 16 | struct userspec *us; |
68 | 16 | struct defaults *def; |
69 | 16 | int cmnd_match, nopass, match = DENY; |
70 | 16 | int validated = 0; |
71 | 16 | enum def_tuple pwcheck; |
72 | 16 | debug_decl(sudoers_lookup_pseudo, SUDOERS_DEBUG_PARSER); |
73 | | |
74 | 16 | pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple; |
75 | 16 | nopass = (pwcheck == never || pwcheck == all) ? true : false; |
76 | | |
77 | 16 | if (list_pw != NULL) { |
78 | 0 | root_pw = sudo_getpwuid(ROOT_UID); |
79 | 0 | if (root_pw == NULL) |
80 | 0 | log_warningx(SLOG_SEND_MAIL, N_("unknown uid %u"), ROOT_UID); |
81 | 16 | } else { |
82 | 16 | SET(validated, FLAG_NO_CHECK); |
83 | 16 | } |
84 | | |
85 | | /* Don't use chroot setting for pseudo-commands. */ |
86 | 16 | saved_runchroot = def_runchroot; |
87 | 16 | def_runchroot = NULL; |
88 | | |
89 | 16 | TAILQ_FOREACH(nss, snl, entries) { |
90 | 16 | if (nss->query(nss, pw) == -1) { |
91 | | /* The query function should have printed an error message. */ |
92 | 0 | SET(validated, VALIDATE_ERROR); |
93 | 0 | break; |
94 | 0 | } |
95 | 16 | TAILQ_FOREACH(us, &nss->parse_tree->userspecs, entries) { |
96 | 8 | if (userlist_matches(nss->parse_tree, pw, &us->users) != ALLOW) |
97 | 6 | continue; |
98 | 2 | TAILQ_FOREACH(priv, &us->privileges, entries) { |
99 | 2 | int priv_nopass = UNSPEC; |
100 | | |
101 | 2 | if (hostlist_matches(nss->parse_tree, pw, &priv->hostlist) != ALLOW) |
102 | 0 | continue; |
103 | 2 | TAILQ_FOREACH(def, &priv->defaults, entries) { |
104 | 0 | if (strcmp(def->var, "authenticate") == 0) |
105 | 0 | priv_nopass = !def->op; |
106 | 0 | } |
107 | 4 | TAILQ_FOREACH(cs, &priv->cmndlist, entries) { |
108 | 4 | if (pwcheck == any) { |
109 | 0 | if (cs->tags.nopasswd == true || priv_nopass == true) |
110 | 0 | nopass = true; |
111 | 4 | } else if (pwcheck == all) { |
112 | 0 | if (cs->tags.nopasswd != true && priv_nopass != true) |
113 | 0 | nopass = false; |
114 | 0 | } |
115 | 4 | if (match == ALLOW) |
116 | 2 | continue; |
117 | | |
118 | | /* |
119 | | * Root can list any user's privileges. |
120 | | * A user may always list their own privileges. |
121 | | */ |
122 | 2 | if (user_uid == 0 || list_pw == NULL || |
123 | 2 | user_uid == list_pw->pw_uid) { |
124 | 2 | match = ALLOW; |
125 | 2 | continue; |
126 | 2 | } |
127 | | |
128 | | /* |
129 | | * To list another user's prilileges, the runas |
130 | | * user must match the list user or root. |
131 | | */ |
132 | 0 | switch (runas_matches_pw(nss->parse_tree, cs, list_pw)) { |
133 | 0 | case DENY: |
134 | 0 | break; |
135 | 0 | case ALLOW: |
136 | | /* |
137 | | * RunAs user matches list user. |
138 | | * Match on command "list" or ALL. |
139 | | */ |
140 | 0 | cmnd_match = cmnd_matches(nss->parse_tree, |
141 | 0 | cs->cmnd, cs->runchroot, NULL); |
142 | 0 | if (cmnd_match != UNSPEC) { |
143 | 0 | match = cmnd_match; |
144 | 0 | goto done; |
145 | 0 | } |
146 | 0 | break; |
147 | 0 | default: |
148 | | /* |
149 | | * RunAs user doesn't match list user. Only allow |
150 | | * listing if the user has "sudo ALL" for root. |
151 | | */ |
152 | 0 | if (root_pw != NULL && runas_matches_pw(nss->parse_tree, |
153 | 0 | cs, root_pw) == ALLOW) { |
154 | 0 | cmnd_match = cmnd_matches_all(nss->parse_tree, |
155 | 0 | cs->cmnd, cs->runchroot, NULL); |
156 | 0 | if (cmnd_match != UNSPEC) { |
157 | 0 | match = cmnd_match; |
158 | 0 | goto done; |
159 | 0 | } |
160 | 0 | } |
161 | 0 | break; |
162 | 0 | } |
163 | 0 | } |
164 | 2 | } |
165 | 2 | } |
166 | 16 | } |
167 | 16 | done: |
168 | 16 | if (root_pw != NULL) |
169 | 0 | sudo_pw_delref(root_pw); |
170 | 16 | if (match == ALLOW || user_uid == 0) { |
171 | | /* User has an entry for this host. */ |
172 | 16 | SET(validated, VALIDATE_SUCCESS); |
173 | 16 | } else if (match == DENY) |
174 | 0 | SET(validated, VALIDATE_FAILURE); |
175 | 16 | if (pwcheck == always && def_authenticate) |
176 | 0 | SET(validated, FLAG_CHECK_USER); |
177 | 16 | else if (nopass == true) |
178 | 0 | def_authenticate = false; |
179 | | |
180 | | /* Restore original def_runchroot. */ |
181 | 16 | def_runchroot = saved_runchroot; |
182 | | |
183 | 16 | debug_return_int(validated); |
184 | 16 | } |
185 | | |
186 | | static void |
187 | | init_cmnd_info(struct cmnd_info *info) |
188 | 17 | { |
189 | 17 | memset(info, 0, sizeof(*info)); |
190 | 17 | if (def_intercept || ISSET(sudo_mode, MODE_POLICY_INTERCEPTED)) |
191 | 0 | info->intercepted = true; |
192 | 17 | } |
193 | | |
194 | | static int |
195 | | sudoers_lookup_check(struct sudo_nss *nss, struct passwd *pw, |
196 | | int *validated, struct cmnd_info *info, struct cmndspec **matching_cs, |
197 | | struct defaults_list **defs, time_t now) |
198 | 16 | { |
199 | 16 | int host_match, runas_match, cmnd_match; |
200 | 16 | struct cmndspec *cs; |
201 | 16 | struct privilege *priv; |
202 | 16 | struct userspec *us; |
203 | 16 | struct member *matching_user; |
204 | 16 | debug_decl(sudoers_lookup_check, SUDOERS_DEBUG_PARSER); |
205 | | |
206 | 16 | init_cmnd_info(info); |
207 | | |
208 | 16 | TAILQ_FOREACH_REVERSE(us, &nss->parse_tree->userspecs, userspec_list, entries) { |
209 | 8 | if (userlist_matches(nss->parse_tree, pw, &us->users) != ALLOW) |
210 | 6 | continue; |
211 | 2 | CLR(*validated, FLAG_NO_USER); |
212 | 2 | TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) { |
213 | 2 | host_match = hostlist_matches(nss->parse_tree, pw, &priv->hostlist); |
214 | 2 | if (host_match == ALLOW) |
215 | 2 | CLR(*validated, FLAG_NO_HOST); |
216 | 0 | else |
217 | 0 | continue; |
218 | 2 | TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) { |
219 | 2 | if (cs->notbefore != UNSPEC) { |
220 | 0 | if (now < cs->notbefore) |
221 | 0 | continue; |
222 | 0 | } |
223 | 2 | if (cs->notafter != UNSPEC) { |
224 | 0 | if (now > cs->notafter) |
225 | 0 | continue; |
226 | 0 | } |
227 | 2 | matching_user = NULL; |
228 | 2 | runas_match = runaslist_matches(nss->parse_tree, |
229 | 2 | cs->runasuserlist, cs->runasgrouplist, &matching_user, |
230 | 2 | NULL); |
231 | 2 | if (runas_match == ALLOW) { |
232 | 2 | cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd, |
233 | 2 | cs->runchroot, info); |
234 | 2 | if (cmnd_match != UNSPEC) { |
235 | | /* |
236 | | * If user is running command as himself, |
237 | | * set runas_pw = sudo_user.pw. |
238 | | * XXX - hack, want more general solution |
239 | | */ |
240 | 1 | if (matching_user && matching_user->type == MYSELF) { |
241 | 0 | sudo_pw_delref(runas_pw); |
242 | 0 | sudo_pw_addref(sudo_user.pw); |
243 | 0 | runas_pw = sudo_user.pw; |
244 | 0 | } |
245 | 1 | *matching_cs = cs; |
246 | 1 | *defs = &priv->defaults; |
247 | 1 | sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, |
248 | 1 | "userspec matched @ %s:%d:%d: %s", |
249 | 1 | us->file ? us->file : "???", us->line, us->column, |
250 | 1 | cmnd_match ? "allowed" : "denied"); |
251 | 1 | debug_return_int(cmnd_match); |
252 | 1 | } |
253 | 1 | free(info->cmnd_path); |
254 | 1 | init_cmnd_info(info); |
255 | 1 | } |
256 | 2 | } |
257 | 2 | } |
258 | 2 | } |
259 | 15 | debug_return_int(UNSPEC); |
260 | 15 | } |
261 | | |
262 | | /* |
263 | | * Apply cmndspec-specific settings including SELinux role/type, |
264 | | * Solaris privs, and command tags. |
265 | | */ |
266 | | static bool |
267 | | apply_cmndspec(struct cmndspec *cs) |
268 | 1 | { |
269 | 1 | debug_decl(apply_cmndspec, SUDOERS_DEBUG_PARSER); |
270 | | |
271 | 1 | if (cs != NULL) { |
272 | | #ifdef HAVE_SELINUX |
273 | | /* Set role and type if not specified on command line. */ |
274 | | if (user_role == NULL) { |
275 | | if (cs->role != NULL) { |
276 | | user_role = strdup(cs->role); |
277 | | if (user_role == NULL) { |
278 | | sudo_warnx(U_("%s: %s"), __func__, |
279 | | U_("unable to allocate memory")); |
280 | | debug_return_bool(false); |
281 | | } |
282 | | } else { |
283 | | user_role = def_role; |
284 | | def_role = NULL; |
285 | | } |
286 | | if (user_role != NULL) { |
287 | | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
288 | | "user_role -> %s", user_role); |
289 | | } |
290 | | } |
291 | | if (user_type == NULL) { |
292 | | if (cs->type != NULL) { |
293 | | user_type = strdup(cs->type); |
294 | | if (user_type == NULL) { |
295 | | sudo_warnx(U_("%s: %s"), __func__, |
296 | | U_("unable to allocate memory")); |
297 | | debug_return_bool(false); |
298 | | } |
299 | | } else { |
300 | | user_type = def_type; |
301 | | def_type = NULL; |
302 | | } |
303 | | if (user_type != NULL) { |
304 | | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
305 | | "user_type -> %s", user_type); |
306 | | } |
307 | | } |
308 | | #endif /* HAVE_SELINUX */ |
309 | | #ifdef HAVE_APPARMOR |
310 | | /* Set AppArmor profile, if specified */ |
311 | | if (cs->apparmor_profile != NULL) { |
312 | | user_apparmor_profile = strdup(cs->apparmor_profile); |
313 | | if (user_apparmor_profile == NULL) { |
314 | | sudo_warnx(U_("%s: %s"), __func__, |
315 | | U_("unable to allocate memory")); |
316 | | debug_return_bool(false); |
317 | | } |
318 | | } else { |
319 | | user_apparmor_profile = def_apparmor_profile; |
320 | | def_apparmor_profile = NULL; |
321 | | } |
322 | | if (user_apparmor_profile != NULL) { |
323 | | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
324 | | "user_apparmor_profile -> %s", user_apparmor_profile); |
325 | | } |
326 | | #endif |
327 | | #ifdef HAVE_PRIV_SET |
328 | | /* Set Solaris privilege sets */ |
329 | | if (runas_privs == NULL) { |
330 | | if (cs->privs != NULL) { |
331 | | runas_privs = strdup(cs->privs); |
332 | | if (runas_privs == NULL) { |
333 | | sudo_warnx(U_("%s: %s"), __func__, |
334 | | U_("unable to allocate memory")); |
335 | | debug_return_bool(false); |
336 | | } |
337 | | } else { |
338 | | runas_privs = def_privs; |
339 | | def_privs = NULL; |
340 | | } |
341 | | if (runas_privs != NULL) { |
342 | | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
343 | | "runas_privs -> %s", runas_privs); |
344 | | } |
345 | | } |
346 | | if (runas_limitprivs == NULL) { |
347 | | if (cs->limitprivs != NULL) { |
348 | | runas_limitprivs = strdup(cs->limitprivs); |
349 | | if (runas_limitprivs == NULL) { |
350 | | sudo_warnx(U_("%s: %s"), __func__, |
351 | | U_("unable to allocate memory")); |
352 | | debug_return_bool(false); |
353 | | } |
354 | | } else { |
355 | | runas_limitprivs = def_limitprivs; |
356 | | def_limitprivs = NULL; |
357 | | } |
358 | | if (runas_limitprivs != NULL) { |
359 | | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
360 | | "runas_limitprivs -> %s", runas_limitprivs); |
361 | | } |
362 | | } |
363 | | #endif /* HAVE_PRIV_SET */ |
364 | 1 | if (cs->timeout > 0) { |
365 | 0 | def_command_timeout = cs->timeout; |
366 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
367 | 0 | "def_command_timeout -> %d", def_command_timeout); |
368 | 0 | } |
369 | 1 | if (cs->runcwd != NULL) { |
370 | 0 | free(def_runcwd); |
371 | 0 | def_runcwd = strdup(cs->runcwd); |
372 | 0 | if (def_runcwd == NULL) { |
373 | 0 | sudo_warnx(U_("%s: %s"), __func__, |
374 | 0 | U_("unable to allocate memory")); |
375 | 0 | debug_return_bool(false); |
376 | 0 | } |
377 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
378 | 0 | "def_runcwd -> %s", def_runcwd); |
379 | 0 | } |
380 | 1 | if (cs->runchroot != NULL) { |
381 | 0 | free(def_runchroot); |
382 | 0 | def_runchroot = strdup(cs->runchroot); |
383 | 0 | if (def_runchroot == NULL) { |
384 | 0 | sudo_warnx(U_("%s: %s"), __func__, |
385 | 0 | U_("unable to allocate memory")); |
386 | 0 | debug_return_bool(false); |
387 | 0 | } |
388 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
389 | 0 | "def_runchroot -> %s", def_runchroot); |
390 | 0 | } |
391 | 1 | if (cs->tags.nopasswd != UNSPEC) { |
392 | 0 | def_authenticate = !cs->tags.nopasswd; |
393 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
394 | 0 | "def_authenticate -> %s", def_authenticate ? "true" : "false"); |
395 | 0 | } |
396 | 1 | if (cs->tags.noexec != UNSPEC) { |
397 | 0 | def_noexec = cs->tags.noexec; |
398 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
399 | 0 | "def_noexec -> %s", def_noexec ? "true" : "false"); |
400 | 0 | } |
401 | 1 | if (cs->tags.intercept != UNSPEC) { |
402 | 0 | def_intercept = cs->tags.intercept; |
403 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
404 | 0 | "def_intercept -> %s", def_intercept ? "true" : "false"); |
405 | 0 | } |
406 | 1 | if (cs->tags.setenv != UNSPEC) { |
407 | 0 | def_setenv = cs->tags.setenv; |
408 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
409 | 0 | "def_setenv -> %s", def_setenv ? "true" : "false"); |
410 | 0 | } |
411 | 1 | if (cs->tags.log_input != UNSPEC) { |
412 | 0 | def_log_input = cs->tags.log_input; |
413 | 0 | cb_log_input(NULL, 0, 0, NULL, cs->tags.log_input); |
414 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
415 | 0 | "def_log_input -> %s", def_log_input ? "true" : "false"); |
416 | 0 | } |
417 | 1 | if (cs->tags.log_output != UNSPEC) { |
418 | 0 | def_log_output = cs->tags.log_output; |
419 | 0 | cb_log_output(NULL, 0, 0, NULL, cs->tags.log_output); |
420 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
421 | 0 | "def_log_output -> %s", def_log_output ? "true" : "false"); |
422 | 0 | } |
423 | 1 | if (cs->tags.send_mail != UNSPEC) { |
424 | 0 | if (cs->tags.send_mail) { |
425 | 0 | def_mail_all_cmnds = true; |
426 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
427 | 0 | "def_mail_all_cmnds -> true"); |
428 | 0 | } else { |
429 | 0 | def_mail_all_cmnds = false; |
430 | 0 | def_mail_always = false; |
431 | 0 | def_mail_no_perms = false; |
432 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
433 | 0 | "def_mail_all_cmnds -> false, def_mail_always -> false, " |
434 | 0 | "def_mail_no_perms -> false"); |
435 | 0 | } |
436 | 0 | } |
437 | 1 | if (cs->tags.follow != UNSPEC) { |
438 | 0 | def_sudoedit_follow = cs->tags.follow; |
439 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
440 | 0 | "def_sudoedit_follow -> %s", def_sudoedit_follow ? "true" : "false"); |
441 | 0 | } |
442 | 1 | } |
443 | | |
444 | 1 | debug_return_bool(true); |
445 | 1 | } |
446 | | |
447 | | /* |
448 | | * Look up the user in the sudoers parse tree and check to see if they are |
449 | | * allowed to run the specified command on this host as the target user. |
450 | | */ |
451 | | int |
452 | | sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int *cmnd_status, |
453 | | int pwflag) |
454 | 32 | { |
455 | 32 | struct defaults_list *defs = NULL; |
456 | 32 | struct sudoers_parse_tree *parse_tree = NULL; |
457 | 32 | struct cmndspec *cs = NULL; |
458 | 32 | struct sudo_nss *nss; |
459 | 32 | struct cmnd_info info; |
460 | 32 | int validated = FLAG_NO_USER | FLAG_NO_HOST; |
461 | 32 | int m, match = UNSPEC; |
462 | 32 | time_t now; |
463 | 32 | debug_decl(sudoers_lookup, SUDOERS_DEBUG_PARSER); |
464 | | |
465 | | /* |
466 | | * Special case checking the "validate", "list" and "kill" pseudo-commands. |
467 | | */ |
468 | 32 | if (pwflag) |
469 | 16 | debug_return_int(sudoers_lookup_pseudo(snl, pw, pwflag)); |
470 | | |
471 | | /* Need to be runas user while stat'ing things. */ |
472 | 16 | if (!set_perms(PERM_RUNAS)) |
473 | 0 | debug_return_int(validated); |
474 | | |
475 | | /* Query each sudoers source and check the user. */ |
476 | 16 | time(&now); |
477 | 16 | TAILQ_FOREACH(nss, snl, entries) { |
478 | 16 | if (nss->query(nss, pw) == -1) { |
479 | | /* The query function should have printed an error message. */ |
480 | 0 | SET(validated, VALIDATE_ERROR); |
481 | 0 | break; |
482 | 0 | } |
483 | | |
484 | 16 | m = sudoers_lookup_check(nss, pw, &validated, &info, &cs, &defs, now); |
485 | 16 | if (m != UNSPEC) { |
486 | 1 | match = m; |
487 | 1 | parse_tree = nss->parse_tree; |
488 | 1 | } |
489 | | |
490 | 16 | if (!sudo_nss_can_continue(nss, m)) |
491 | 0 | break; |
492 | 16 | } |
493 | 16 | if (match != UNSPEC) { |
494 | 1 | if (info.cmnd_path != NULL) { |
495 | | /* Update user_cmnd, user_stat, cmnd_status from matching entry. */ |
496 | 0 | free(user_cmnd); |
497 | 0 | user_cmnd = info.cmnd_path; |
498 | 0 | if (user_stat != NULL) |
499 | 0 | *user_stat = info.cmnd_stat; |
500 | 0 | *cmnd_status = info.status; |
501 | 0 | } |
502 | 1 | if (defs != NULL) |
503 | 1 | (void)update_defaults(parse_tree, defs, SETDEF_GENERIC, false); |
504 | 1 | if (!apply_cmndspec(cs)) |
505 | 0 | SET(validated, VALIDATE_ERROR); |
506 | 1 | else if (match == ALLOW) |
507 | 0 | SET(validated, VALIDATE_SUCCESS); |
508 | 1 | else |
509 | 1 | SET(validated, VALIDATE_FAILURE); |
510 | 1 | } |
511 | 16 | if (!restore_perms()) |
512 | 0 | SET(validated, VALIDATE_ERROR); |
513 | 16 | debug_return_int(validated); |
514 | 16 | } |
515 | | |
516 | | static int |
517 | | display_priv_short(struct sudoers_parse_tree *parse_tree, struct passwd *pw, |
518 | | struct userspec *us, struct sudo_lbuf *lbuf) |
519 | 2 | { |
520 | 2 | struct privilege *priv; |
521 | 2 | int nfound = 0; |
522 | 2 | debug_decl(display_priv_short, SUDOERS_DEBUG_PARSER); |
523 | | |
524 | 2 | TAILQ_FOREACH(priv, &us->privileges, entries) { |
525 | 2 | struct cmndspec *cs; |
526 | 2 | struct cmndtag tags; |
527 | | |
528 | 2 | if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW) |
529 | 0 | continue; |
530 | | |
531 | 2 | sudoers_defaults_list_to_tags(&priv->defaults, &tags); |
532 | 4 | TAILQ_FOREACH(cs, &priv->cmndlist, entries) { |
533 | 4 | struct cmndspec *prev_cs = TAILQ_PREV(cs, cmndspec_list, entries); |
534 | | |
535 | 4 | if (prev_cs == NULL || RUNAS_CHANGED(cs, prev_cs)) { |
536 | 2 | struct member *m; |
537 | | |
538 | | /* Start new line, first entry or RunAs changed. */ |
539 | 2 | if (prev_cs != NULL) |
540 | 0 | sudo_lbuf_append(lbuf, "\n"); |
541 | 2 | sudo_lbuf_append(lbuf, " ("); |
542 | 2 | if (cs->runasuserlist != NULL) { |
543 | 0 | TAILQ_FOREACH(m, cs->runasuserlist, entries) { |
544 | 0 | if (m != TAILQ_FIRST(cs->runasuserlist)) |
545 | 0 | sudo_lbuf_append(lbuf, ", "); |
546 | 0 | sudoers_format_member(lbuf, parse_tree, m, ", ", |
547 | 0 | RUNASALIAS); |
548 | 0 | } |
549 | 2 | } else if (cs->runasgrouplist == NULL) { |
550 | 2 | sudo_lbuf_append(lbuf, "%s", def_runas_default); |
551 | 2 | } else { |
552 | 0 | sudo_lbuf_append(lbuf, "%s", pw->pw_name); |
553 | 0 | } |
554 | 2 | if (cs->runasgrouplist != NULL) { |
555 | 0 | sudo_lbuf_append(lbuf, " : "); |
556 | 0 | TAILQ_FOREACH(m, cs->runasgrouplist, entries) { |
557 | 0 | if (m != TAILQ_FIRST(cs->runasgrouplist)) |
558 | 0 | sudo_lbuf_append(lbuf, ", "); |
559 | 0 | sudoers_format_member(lbuf, parse_tree, m, ", ", |
560 | 0 | RUNASALIAS); |
561 | 0 | } |
562 | 0 | } |
563 | 2 | sudo_lbuf_append(lbuf, ") "); |
564 | 2 | sudoers_format_cmndspec(lbuf, parse_tree, cs, NULL, tags, true); |
565 | 2 | } else { |
566 | | /* Continue existing line. */ |
567 | 2 | sudo_lbuf_append(lbuf, ", "); |
568 | 2 | sudoers_format_cmndspec(lbuf, parse_tree, cs, prev_cs, tags, |
569 | 2 | true); |
570 | 2 | } |
571 | 4 | nfound++; |
572 | 4 | } |
573 | 2 | sudo_lbuf_append(lbuf, "\n"); |
574 | 2 | } |
575 | 2 | debug_return_int(nfound); |
576 | 2 | } |
577 | | |
578 | | /* |
579 | | * Compare the current cmndspec with the previous one to determine |
580 | | * whether we need to start a new long entry for "sudo -ll". |
581 | | * Returns true if we should start a new long entry, else false. |
582 | | */ |
583 | | static bool |
584 | | new_long_entry(struct cmndspec *cs, struct cmndspec *prev_cs) |
585 | 4 | { |
586 | 4 | debug_decl(new_long_entry, SUDOERS_DEBUG_PARSER); |
587 | | |
588 | 4 | if (prev_cs == NULL) |
589 | 2 | debug_return_bool(true); |
590 | 2 | if (RUNAS_CHANGED(cs, prev_cs) || TAGS_CHANGED(prev_cs->tags, cs->tags)) |
591 | 0 | debug_return_bool(true); |
592 | | #ifdef HAVE_PRIV_SET |
593 | | if (cs->privs && (!prev_cs->privs || strcmp(cs->privs, prev_cs->privs) != 0)) |
594 | | debug_return_bool(true); |
595 | | if (cs->limitprivs && (!prev_cs->limitprivs || strcmp(cs->limitprivs, prev_cs->limitprivs) != 0)) |
596 | | debug_return_bool(true); |
597 | | #endif /* HAVE_PRIV_SET */ |
598 | | #ifdef HAVE_SELINUX |
599 | | if (cs->role && (!prev_cs->role || strcmp(cs->role, prev_cs->role) != 0)) |
600 | | debug_return_bool(true); |
601 | | if (cs->type && (!prev_cs->type || strcmp(cs->type, prev_cs->type) != 0)) |
602 | | debug_return_bool(true); |
603 | | #endif /* HAVE_SELINUX */ |
604 | | #ifdef HAVE_APPARMOR |
605 | | if (cs->apparmor_profile && (!prev_cs->apparmor_profile || strcmp(cs->apparmor_profile, prev_cs->apparmor_profile) != 0)) |
606 | | debug_return_bool(true); |
607 | | #endif /* HAVE_APPARMOR */ |
608 | 2 | if (cs->runchroot && (!prev_cs->runchroot || strcmp(cs->runchroot, prev_cs->runchroot) != 0)) |
609 | 0 | debug_return_bool(true); |
610 | 2 | if (cs->runcwd && (!prev_cs->runcwd || strcmp(cs->runcwd, prev_cs->runcwd) != 0)) |
611 | 0 | debug_return_bool(true); |
612 | 2 | if (cs->timeout != prev_cs->timeout) |
613 | 0 | debug_return_bool(true); |
614 | 2 | if (cs->notbefore != prev_cs->notbefore) |
615 | 0 | debug_return_bool(true); |
616 | 2 | if (cs->notafter != prev_cs->notafter) |
617 | 0 | debug_return_bool(true); |
618 | 2 | debug_return_bool(false); |
619 | 2 | } |
620 | | |
621 | | static int |
622 | | display_priv_long(struct sudoers_parse_tree *parse_tree, struct passwd *pw, |
623 | | struct userspec *us, struct sudo_lbuf *lbuf) |
624 | 2 | { |
625 | 2 | struct privilege *priv; |
626 | 2 | int nfound = 0; |
627 | 2 | debug_decl(display_priv_long, SUDOERS_DEBUG_PARSER); |
628 | | |
629 | 2 | TAILQ_FOREACH(priv, &us->privileges, entries) { |
630 | 2 | struct cmndspec *cs, *prev_cs; |
631 | | |
632 | 2 | if (hostlist_matches(parse_tree, pw, &priv->hostlist) != ALLOW) |
633 | 0 | continue; |
634 | 2 | prev_cs = NULL; |
635 | 4 | TAILQ_FOREACH(cs, &priv->cmndlist, entries) { |
636 | 4 | struct defaults *d; |
637 | 4 | struct member *m; |
638 | | |
639 | 4 | if (new_long_entry(cs, prev_cs)) { |
640 | 2 | int olen; |
641 | | |
642 | 2 | if (priv->ldap_role != NULL) { |
643 | 0 | sudo_lbuf_append(lbuf, _("\nLDAP Role: %s\n"), |
644 | 0 | priv->ldap_role); |
645 | 2 | } else { |
646 | 2 | sudo_lbuf_append(lbuf, "%s", _("\nSudoers entry:\n")); |
647 | 2 | } |
648 | 2 | sudo_lbuf_append(lbuf, "%s", _(" RunAsUsers: ")); |
649 | 2 | if (cs->runasuserlist != NULL) { |
650 | 0 | TAILQ_FOREACH(m, cs->runasuserlist, entries) { |
651 | 0 | if (m != TAILQ_FIRST(cs->runasuserlist)) |
652 | 0 | sudo_lbuf_append(lbuf, ", "); |
653 | 0 | sudoers_format_member(lbuf, parse_tree, m, ", ", |
654 | 0 | RUNASALIAS); |
655 | 0 | } |
656 | 2 | } else if (cs->runasgrouplist == NULL) { |
657 | 2 | sudo_lbuf_append(lbuf, "%s", def_runas_default); |
658 | 2 | } else { |
659 | 0 | sudo_lbuf_append(lbuf, "%s", pw->pw_name); |
660 | 0 | } |
661 | 2 | sudo_lbuf_append(lbuf, "\n"); |
662 | 2 | if (cs->runasgrouplist != NULL) { |
663 | 0 | sudo_lbuf_append(lbuf, "%s", _(" RunAsGroups: ")); |
664 | 0 | TAILQ_FOREACH(m, cs->runasgrouplist, entries) { |
665 | 0 | if (m != TAILQ_FIRST(cs->runasgrouplist)) |
666 | 0 | sudo_lbuf_append(lbuf, ", "); |
667 | 0 | sudoers_format_member(lbuf, parse_tree, m, ", ", |
668 | 0 | RUNASALIAS); |
669 | 0 | } |
670 | 0 | sudo_lbuf_append(lbuf, "\n"); |
671 | 0 | } |
672 | 2 | olen = lbuf->len; |
673 | 2 | sudo_lbuf_append(lbuf, "%s", _(" Options: ")); |
674 | 2 | TAILQ_FOREACH(d, &priv->defaults, entries) { |
675 | 0 | sudoers_format_default(lbuf, d); |
676 | 0 | sudo_lbuf_append(lbuf, ", "); |
677 | 0 | } |
678 | 2 | if (TAG_SET(cs->tags.setenv)) |
679 | 0 | sudo_lbuf_append(lbuf, "%ssetenv, ", cs->tags.setenv ? "" : "!"); |
680 | 2 | if (TAG_SET(cs->tags.noexec)) |
681 | 0 | sudo_lbuf_append(lbuf, "%snoexec, ", cs->tags.noexec ? "" : "!"); |
682 | 2 | if (TAG_SET(cs->tags.intercept)) |
683 | 0 | sudo_lbuf_append(lbuf, "%sintercept, ", cs->tags.intercept ? "" : "!"); |
684 | 2 | if (TAG_SET(cs->tags.nopasswd)) |
685 | 0 | sudo_lbuf_append(lbuf, "%sauthenticate, ", cs->tags.nopasswd ? "!" : ""); |
686 | 2 | if (TAG_SET(cs->tags.log_input)) |
687 | 0 | sudo_lbuf_append(lbuf, "%slog_input, ", cs->tags.log_input ? "" : "!"); |
688 | 2 | if (TAG_SET(cs->tags.log_output)) |
689 | 0 | sudo_lbuf_append(lbuf, "%slog_output, ", cs->tags.log_output ? "" : "!"); |
690 | 2 | if (lbuf->buf[lbuf->len - 2] == ',') { |
691 | 0 | lbuf->len -= 2; /* remove trailing ", " */ |
692 | 0 | sudo_lbuf_append(lbuf, "\n"); |
693 | 2 | } else { |
694 | 2 | lbuf->len = olen; /* no options */ |
695 | 2 | } |
696 | | #ifdef HAVE_PRIV_SET |
697 | | if (cs->privs) |
698 | | sudo_lbuf_append(lbuf, " Privs: %s\n", cs->privs); |
699 | | if (cs->limitprivs) |
700 | | sudo_lbuf_append(lbuf, " Limitprivs: %s\n", cs->limitprivs); |
701 | | #endif /* HAVE_PRIV_SET */ |
702 | | #ifdef HAVE_SELINUX |
703 | | if (cs->role) |
704 | | sudo_lbuf_append(lbuf, " Role: %s\n", cs->role); |
705 | | if (cs->type) |
706 | | sudo_lbuf_append(lbuf, " Type: %s\n", cs->type); |
707 | | #endif /* HAVE_SELINUX */ |
708 | 2 | if (cs->runchroot != NULL) |
709 | 0 | sudo_lbuf_append(lbuf, " Chroot: %s\n", cs->runchroot); |
710 | 2 | if (cs->runcwd != NULL) |
711 | 0 | sudo_lbuf_append(lbuf, " Cwd: %s\n", cs->runcwd); |
712 | 2 | if (cs->timeout > 0) { |
713 | 0 | char numbuf[(((sizeof(int) * 8) + 2) / 3) + 2]; |
714 | 0 | (void)snprintf(numbuf, sizeof(numbuf), "%d", cs->timeout); |
715 | 0 | sudo_lbuf_append(lbuf, " Timeout: %s\n", numbuf); |
716 | 0 | } |
717 | 2 | if (cs->notbefore != UNSPEC) { |
718 | 0 | char buf[sizeof("CCYYMMDDHHMMSSZ")] = ""; |
719 | 0 | struct tm gmt; |
720 | 0 | int len; |
721 | 0 | if (gmtime_r(&cs->notbefore, &gmt) != NULL) { |
722 | 0 | len = strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &gmt); |
723 | 0 | if (len != 0 && buf[sizeof(buf) - 1] == '\0') |
724 | 0 | sudo_lbuf_append(lbuf, " NotBefore: %s\n", buf); |
725 | 0 | } |
726 | 0 | } |
727 | 2 | if (cs->notafter != UNSPEC) { |
728 | 0 | char buf[sizeof("CCYYMMDDHHMMSSZ")] = ""; |
729 | 0 | struct tm gmt; |
730 | 0 | int len; |
731 | 0 | if (gmtime_r(&cs->notafter, &gmt) != NULL) { |
732 | 0 | len = strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &gmt); |
733 | 0 | if (len != 0 && buf[sizeof(buf) - 1] == '\0') |
734 | 0 | sudo_lbuf_append(lbuf, " NotAfter: %s\n", buf); |
735 | 0 | } |
736 | 0 | } |
737 | 2 | sudo_lbuf_append(lbuf, "%s", _(" Commands:\n")); |
738 | 2 | } |
739 | 4 | sudo_lbuf_append(lbuf, "\t"); |
740 | 4 | sudoers_format_member(lbuf, parse_tree, cs->cmnd, "\n\t", |
741 | 4 | CMNDALIAS); |
742 | 4 | sudo_lbuf_append(lbuf, "\n"); |
743 | 4 | prev_cs = cs; |
744 | 4 | nfound++; |
745 | 4 | } |
746 | 2 | } |
747 | 2 | debug_return_int(nfound); |
748 | 2 | } |
749 | | |
750 | | static int |
751 | | sudo_display_userspecs(struct sudoers_parse_tree *parse_tree, struct passwd *pw, |
752 | | struct sudo_lbuf *lbuf, bool verbose) |
753 | 32 | { |
754 | 32 | struct userspec *us; |
755 | 32 | int nfound = 0; |
756 | 32 | debug_decl(sudo_display_userspecs, SUDOERS_DEBUG_PARSER); |
757 | | |
758 | 32 | TAILQ_FOREACH(us, &parse_tree->userspecs, entries) { |
759 | 16 | if (userlist_matches(parse_tree, pw, &us->users) != ALLOW) |
760 | 12 | continue; |
761 | | |
762 | 4 | if (verbose) |
763 | 2 | nfound += display_priv_long(parse_tree, pw, us, lbuf); |
764 | 2 | else |
765 | 2 | nfound += display_priv_short(parse_tree, pw, us, lbuf); |
766 | 4 | } |
767 | 32 | if (sudo_lbuf_error(lbuf)) |
768 | 0 | debug_return_int(-1); |
769 | 32 | debug_return_int(nfound); |
770 | 32 | } |
771 | | |
772 | | /* |
773 | | * Display matching Defaults entries for the given user on this host. |
774 | | */ |
775 | | static int |
776 | | display_defaults(struct sudoers_parse_tree *parse_tree, struct passwd *pw, |
777 | | struct sudo_lbuf *lbuf) |
778 | 32 | { |
779 | 32 | struct defaults *d; |
780 | 32 | const char *prefix; |
781 | 32 | int nfound = 0; |
782 | 32 | debug_decl(display_defaults, SUDOERS_DEBUG_PARSER); |
783 | | |
784 | 32 | if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1])) |
785 | 32 | prefix = " "; |
786 | 0 | else |
787 | 0 | prefix = ", "; |
788 | | |
789 | 32 | TAILQ_FOREACH(d, &parse_tree->defaults, entries) { |
790 | 8 | switch (d->type) { |
791 | 0 | case DEFAULTS_HOST: |
792 | 0 | if (hostlist_matches(parse_tree, pw, &d->binding->members) != ALLOW) |
793 | 0 | continue; |
794 | 0 | break; |
795 | 0 | case DEFAULTS_USER: |
796 | 0 | if (userlist_matches(parse_tree, pw, &d->binding->members) != ALLOW) |
797 | 0 | continue; |
798 | 0 | break; |
799 | 0 | case DEFAULTS_RUNAS: |
800 | 0 | case DEFAULTS_CMND: |
801 | 0 | continue; |
802 | 8 | } |
803 | 8 | sudo_lbuf_append(lbuf, "%s", prefix); |
804 | 8 | sudoers_format_default(lbuf, d); |
805 | 8 | prefix = ", "; |
806 | 8 | nfound++; |
807 | 8 | } |
808 | 32 | if (sudo_lbuf_error(lbuf)) |
809 | 0 | debug_return_int(-1); |
810 | 32 | debug_return_int(nfound); |
811 | 32 | } |
812 | | |
813 | | /* |
814 | | * Display Defaults entries of the given type. |
815 | | */ |
816 | | static int |
817 | | display_bound_defaults_by_type(struct sudoers_parse_tree *parse_tree, |
818 | | int deftype, struct sudo_lbuf *lbuf) |
819 | 64 | { |
820 | 64 | struct defaults *d; |
821 | 64 | struct defaults_binding *binding = NULL; |
822 | 64 | struct member *m; |
823 | 64 | const char *dsep; |
824 | 64 | int atype, nfound = 0; |
825 | 64 | debug_decl(display_bound_defaults_by_type, SUDOERS_DEBUG_PARSER); |
826 | | |
827 | 64 | switch (deftype) { |
828 | 0 | case DEFAULTS_HOST: |
829 | 0 | atype = HOSTALIAS; |
830 | 0 | dsep = "@"; |
831 | 0 | break; |
832 | 0 | case DEFAULTS_USER: |
833 | 0 | atype = USERALIAS; |
834 | 0 | dsep = ":"; |
835 | 0 | break; |
836 | 32 | case DEFAULTS_RUNAS: |
837 | 32 | atype = RUNASALIAS; |
838 | 32 | dsep = ">"; |
839 | 32 | break; |
840 | 32 | case DEFAULTS_CMND: |
841 | 32 | atype = CMNDALIAS; |
842 | 32 | dsep = "!"; |
843 | 32 | break; |
844 | 0 | default: |
845 | 0 | debug_return_int(-1); |
846 | 64 | } |
847 | 64 | TAILQ_FOREACH(d, &parse_tree->defaults, entries) { |
848 | 16 | if (d->type != deftype) |
849 | 16 | continue; |
850 | | |
851 | 0 | nfound++; |
852 | 0 | if (binding != d->binding) { |
853 | 0 | binding = d->binding; |
854 | 0 | if (nfound != 1) |
855 | 0 | sudo_lbuf_append(lbuf, "\n"); |
856 | 0 | sudo_lbuf_append(lbuf, " Defaults%s", dsep); |
857 | 0 | TAILQ_FOREACH(m, &binding->members, entries) { |
858 | 0 | if (m != TAILQ_FIRST(&binding->members)) |
859 | 0 | sudo_lbuf_append(lbuf, ", "); |
860 | 0 | sudoers_format_member(lbuf, parse_tree, m, ", ", atype); |
861 | 0 | } |
862 | 0 | sudo_lbuf_append(lbuf, " "); |
863 | 0 | } else |
864 | 0 | sudo_lbuf_append(lbuf, ", "); |
865 | 0 | sudoers_format_default(lbuf, d); |
866 | 0 | } |
867 | | |
868 | 64 | if (sudo_lbuf_error(lbuf)) |
869 | 0 | debug_return_int(-1); |
870 | 64 | debug_return_int(nfound); |
871 | 64 | } |
872 | | |
873 | | /* |
874 | | * Display Defaults entries that are per-runas or per-command |
875 | | */ |
876 | | static int |
877 | | display_bound_defaults(struct sudoers_parse_tree *parse_tree, |
878 | | struct passwd *pw, struct sudo_lbuf *lbuf) |
879 | 32 | { |
880 | 32 | int nfound = 0; |
881 | 32 | debug_decl(display_bound_defaults, SUDOERS_DEBUG_PARSER); |
882 | | |
883 | | /* XXX - should only print ones that match what the user can do. */ |
884 | 32 | nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_RUNAS, lbuf); |
885 | 32 | nfound += display_bound_defaults_by_type(parse_tree, DEFAULTS_CMND, lbuf); |
886 | | |
887 | 32 | if (sudo_lbuf_error(lbuf)) |
888 | 0 | debug_return_int(-1); |
889 | 32 | debug_return_int(nfound); |
890 | 32 | } |
891 | | |
892 | | static int |
893 | | output(const char *buf) |
894 | 32 | { |
895 | 32 | struct sudo_conv_message msg; |
896 | 32 | struct sudo_conv_reply repl; |
897 | 32 | debug_decl(output, SUDOERS_DEBUG_NSS); |
898 | | |
899 | | /* Call conversation function */ |
900 | 32 | memset(&msg, 0, sizeof(msg)); |
901 | 32 | msg.msg_type = SUDO_CONV_INFO_MSG; |
902 | 32 | msg.msg = buf; |
903 | 32 | memset(&repl, 0, sizeof(repl)); |
904 | 32 | if (sudo_conv(1, &msg, &repl, NULL) == -1) |
905 | 0 | debug_return_int(0); |
906 | 32 | debug_return_int(strlen(buf)); |
907 | 32 | } |
908 | | |
909 | | /* |
910 | | * Print out privileges for the specified user. |
911 | | * Returns true on success or -1 on error. |
912 | | */ |
913 | | int |
914 | | display_privs(struct sudo_nss_list *snl, struct passwd *pw, bool verbose) |
915 | 32 | { |
916 | 32 | struct sudo_nss *nss; |
917 | 32 | struct sudo_lbuf def_buf, priv_buf; |
918 | 32 | struct stat sb; |
919 | 32 | int cols, count, olen, n; |
920 | 32 | debug_decl(display_privs, SUDOERS_DEBUG_PARSER); |
921 | | |
922 | 32 | cols = sudo_user.cols; |
923 | 32 | if (fstat(STDOUT_FILENO, &sb) == 0 && S_ISFIFO(sb.st_mode)) |
924 | 0 | cols = 0; |
925 | 32 | sudo_lbuf_init(&def_buf, output, 4, NULL, cols); |
926 | 32 | sudo_lbuf_init(&priv_buf, output, 8, NULL, cols); |
927 | | |
928 | 32 | sudo_lbuf_append(&def_buf, _("Matching Defaults entries for %s on %s:\n"), |
929 | 32 | pw->pw_name, user_srunhost); |
930 | 32 | count = 0; |
931 | 32 | TAILQ_FOREACH(nss, snl, entries) { |
932 | 32 | n = display_defaults(nss->parse_tree, pw, &def_buf); |
933 | 32 | if (n == -1) |
934 | 0 | goto bad; |
935 | 32 | count += n; |
936 | 32 | } |
937 | 32 | if (count != 0) { |
938 | 8 | sudo_lbuf_append(&def_buf, "\n\n"); |
939 | 24 | } else { |
940 | | /* Undo Defaults header. */ |
941 | 24 | def_buf.len = 0; |
942 | 24 | } |
943 | | |
944 | | /* Display Runas and Cmnd-specific defaults. */ |
945 | 32 | olen = def_buf.len; |
946 | 32 | sudo_lbuf_append(&def_buf, _("Runas and Command-specific defaults for %s:\n"), |
947 | 32 | pw->pw_name); |
948 | 32 | count = 0; |
949 | 32 | TAILQ_FOREACH(nss, snl, entries) { |
950 | 32 | n = display_bound_defaults(nss->parse_tree, pw, &def_buf); |
951 | 32 | if (n == -1) |
952 | 0 | goto bad; |
953 | 32 | count += n; |
954 | 32 | } |
955 | 32 | if (count != 0) { |
956 | 0 | sudo_lbuf_append(&def_buf, "\n\n"); |
957 | 32 | } else { |
958 | | /* Undo Defaults header. */ |
959 | 32 | def_buf.len = olen; |
960 | 32 | } |
961 | | |
962 | | /* Display privileges from all sources. */ |
963 | 32 | sudo_lbuf_append(&priv_buf, |
964 | 32 | _("User %s may run the following commands on %s:\n"), |
965 | 32 | pw->pw_name, user_srunhost); |
966 | 32 | count = 0; |
967 | 32 | TAILQ_FOREACH(nss, snl, entries) { |
968 | 32 | if (nss->query(nss, pw) != -1) { |
969 | 32 | n = sudo_display_userspecs(nss->parse_tree, pw, &priv_buf, verbose); |
970 | 32 | if (n == -1) |
971 | 0 | goto bad; |
972 | 32 | count += n; |
973 | 32 | } |
974 | 32 | } |
975 | 32 | if (count == 0) { |
976 | 28 | def_buf.len = 0; |
977 | 28 | priv_buf.len = 0; |
978 | 28 | sudo_lbuf_append(&priv_buf, |
979 | 28 | _("User %s is not allowed to run sudo on %s.\n"), |
980 | 28 | pw->pw_name, user_srunhost); |
981 | 28 | } |
982 | 32 | if (sudo_lbuf_error(&def_buf) || sudo_lbuf_error(&priv_buf)) |
983 | 0 | goto bad; |
984 | | |
985 | 32 | sudo_lbuf_print(&def_buf); |
986 | 32 | sudo_lbuf_print(&priv_buf); |
987 | | |
988 | 32 | sudo_lbuf_destroy(&def_buf); |
989 | 32 | sudo_lbuf_destroy(&priv_buf); |
990 | | |
991 | 32 | debug_return_int(true); |
992 | 0 | bad: |
993 | 0 | sudo_lbuf_destroy(&def_buf); |
994 | 0 | sudo_lbuf_destroy(&priv_buf); |
995 | |
|
996 | 0 | debug_return_int(-1); |
997 | 0 | } |
998 | | |
999 | | static int |
1000 | | display_cmnd_check(struct sudoers_parse_tree *parse_tree, struct passwd *pw, |
1001 | | time_t now) |
1002 | 0 | { |
1003 | 0 | int host_match, runas_match, cmnd_match = UNSPEC; |
1004 | 0 | char *saved_user_cmnd, *saved_user_base; |
1005 | 0 | struct cmndspec *cs; |
1006 | 0 | struct privilege *priv; |
1007 | 0 | struct userspec *us; |
1008 | 0 | debug_decl(display_cmnd_check, SUDOERS_DEBUG_PARSER); |
1009 | | |
1010 | | /* |
1011 | | * For "sudo -l command", user_cmnd is "list" and the actual |
1012 | | * command we are checking is in list_cmnd. |
1013 | | */ |
1014 | 0 | saved_user_cmnd = user_cmnd; |
1015 | 0 | saved_user_base = user_base; |
1016 | 0 | user_cmnd = list_cmnd; |
1017 | 0 | user_base = sudo_basename(user_cmnd); |
1018 | |
|
1019 | 0 | TAILQ_FOREACH_REVERSE(us, &parse_tree->userspecs, userspec_list, entries) { |
1020 | 0 | if (userlist_matches(parse_tree, pw, &us->users) != ALLOW) |
1021 | 0 | continue; |
1022 | 0 | TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) { |
1023 | 0 | host_match = hostlist_matches(parse_tree, pw, &priv->hostlist); |
1024 | 0 | if (host_match != ALLOW) |
1025 | 0 | continue; |
1026 | 0 | TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) { |
1027 | 0 | if (cs->notbefore != UNSPEC) { |
1028 | 0 | if (now < cs->notbefore) |
1029 | 0 | continue; |
1030 | 0 | } |
1031 | 0 | if (cs->notafter != UNSPEC) { |
1032 | 0 | if (now > cs->notafter) |
1033 | 0 | continue; |
1034 | 0 | } |
1035 | 0 | runas_match = runaslist_matches(parse_tree, cs->runasuserlist, |
1036 | 0 | cs->runasgrouplist, NULL, NULL); |
1037 | 0 | if (runas_match == ALLOW) { |
1038 | 0 | cmnd_match = cmnd_matches(parse_tree, cs->cmnd, |
1039 | 0 | cs->runchroot, NULL); |
1040 | 0 | if (cmnd_match != UNSPEC) |
1041 | 0 | goto done; |
1042 | 0 | } |
1043 | 0 | } |
1044 | 0 | } |
1045 | 0 | } |
1046 | 0 | done: |
1047 | 0 | user_cmnd = saved_user_cmnd; |
1048 | 0 | user_base = saved_user_base; |
1049 | 0 | debug_return_int(cmnd_match); |
1050 | 0 | } |
1051 | | |
1052 | | /* |
1053 | | * Check user_cmnd against sudoers and print the matching entry if the |
1054 | | * command is allowed. |
1055 | | * Returns true if the command is allowed, false if not or -1 on error. |
1056 | | */ |
1057 | | int |
1058 | | display_cmnd(struct sudo_nss_list *snl, struct passwd *pw) |
1059 | 0 | { |
1060 | 0 | struct sudo_nss *nss; |
1061 | 0 | int m, match = UNSPEC; |
1062 | 0 | int ret = false; |
1063 | 0 | time_t now; |
1064 | 0 | debug_decl(display_cmnd, SUDOERS_DEBUG_PARSER); |
1065 | | |
1066 | | /* Iterate over each source, checking for the command. */ |
1067 | 0 | time(&now); |
1068 | 0 | TAILQ_FOREACH(nss, snl, entries) { |
1069 | 0 | if (nss->query(nss, pw) == -1) { |
1070 | | /* The query function should have printed an error message. */ |
1071 | 0 | debug_return_int(-1); |
1072 | 0 | } |
1073 | | |
1074 | 0 | m = display_cmnd_check(nss->parse_tree, pw, now); |
1075 | 0 | if (m != UNSPEC) |
1076 | 0 | match = m; |
1077 | |
|
1078 | 0 | if (!sudo_nss_can_continue(nss, m)) |
1079 | 0 | break; |
1080 | 0 | } |
1081 | 0 | if (match == ALLOW) { |
1082 | 0 | const int len = sudo_printf(SUDO_CONV_INFO_MSG, "%s%s%s\n", |
1083 | 0 | list_cmnd, user_args ? " " : "", user_args ? user_args : ""); |
1084 | 0 | ret = len < 0 ? -1 : true; |
1085 | 0 | } |
1086 | 0 | debug_return_int(ret); |
1087 | 0 | } |