/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  | }  |