2 * Copyright (c) 2004-2005, 2007-2009 Todd C. Miller <Todd.Miller@courtesan.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
16 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21 #include <sys/types.h>
22 #include <sys/param.h>
31 #endif /* STDC_HEADERS */
35 # ifdef HAVE_STRINGS_H
38 #endif /* HAVE_STRING_H */
41 #endif /* HAVE_UNISTD_H */
52 __unused static const char rcsid[] = "$Sudo: parse.c,v 1.242 2009/05/25 12:02:41 millert Exp $";
55 /* Characters that must be quoted in sudoers */
56 #define SUDOERS_QUOTED ":\\,=#\""
58 /* sudoers nsswitch routines */
59 struct sudo_nss sudo_nss_file = {
67 sudo_file_display_cmnd,
68 sudo_file_display_defaults,
69 sudo_file_display_bound_defaults,
70 sudo_file_display_privs
77 extern char *errorfile;
78 extern int errorlineno, parse_error;
83 static void print_member __P((struct lbuf *, char *, int, int, int));
84 static int display_bound_defaults __P((int, struct lbuf *));
90 if (def_ignore_local_sudoers)
92 nss->handle = open_sudoers(_PATH_SUDOERS, FALSE, NULL);
93 return(nss->handle ? 0 : -1);
100 /* Free parser data structures and close sudoers file. */
101 init_parser(NULL, 0);
102 if (nss->handle != NULL) {
111 * Parse the specified sudoers file.
115 struct sudo_nss *nss;
117 if (nss->handle == NULL)
120 init_parser(_PATH_SUDOERS, 0);
122 if (yyparse() != 0 || parse_error) {
123 log_error(NO_EXIT, "parse error in %s near line %d",
124 errorfile, errorlineno);
131 * Wrapper around update_defaults() for nsswitch code.
134 sudo_file_setdefs(nss)
135 struct sudo_nss *nss;
137 if (nss->handle == NULL)
140 if (!update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER))
146 * Look up the user in the parsed sudoers file and check to see if they are
147 * allowed to run the specified command on this host as the target user.
150 sudo_file_lookup(nss, validated, pwflag)
151 struct sudo_nss *nss;
155 int match, host_match, runas_match, cmnd_match;
157 struct cmndtag *tags = NULL;
158 struct privilege *priv;
161 if (nss->handle == NULL)
165 * Only check the actual command if pwflag is not set.
166 * It is set for the "validate", "list" and "kill" pseudo-commands.
167 * Always check the host and user.
171 enum def_tupple pwcheck;
173 pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
174 nopass = (pwcheck == all) ? TRUE : FALSE;
177 SET(validated, FLAG_NO_CHECK);
178 CLR(validated, FLAG_NO_USER);
179 CLR(validated, FLAG_NO_HOST);
181 tq_foreach_fwd(&userspecs, us) {
182 if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
184 tq_foreach_fwd(&us->privileges, priv) {
185 if (hostlist_matches(&priv->hostlist) != ALLOW)
187 tq_foreach_fwd(&priv->cmndlist, cs) {
188 /* Only check the command when listing another user. */
189 if (user_uid == 0 || list_pw == NULL ||
190 user_uid == list_pw->pw_uid ||
191 cmnd_matches(cs->cmnd) == ALLOW)
193 if ((pwcheck == any && cs->tags.nopasswd == TRUE) ||
194 (pwcheck == all && cs->tags.nopasswd != TRUE))
195 nopass = cs->tags.nopasswd;
199 if (match == ALLOW || user_uid == 0) {
200 /* User has an entry for this host. */
201 SET(validated, VALIDATE_OK);
202 } else if (match == DENY)
203 SET(validated, VALIDATE_NOT_OK);
204 if (pwcheck == always && def_authenticate)
205 SET(validated, FLAG_CHECK_USER);
206 else if (pwcheck == never || nopass == TRUE)
207 def_authenticate = FALSE;
211 /* Need to be runas user while stat'ing things. */
212 set_perms(PERM_RUNAS);
215 tq_foreach_rev(&userspecs, us) {
216 if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
218 CLR(validated, FLAG_NO_USER);
219 tq_foreach_rev(&us->privileges, priv) {
220 host_match = hostlist_matches(&priv->hostlist);
221 if (host_match == ALLOW)
222 CLR(validated, FLAG_NO_HOST);
225 tq_foreach_rev(&priv->cmndlist, cs) {
226 runas_match = runaslist_matches(&cs->runasuserlist,
227 &cs->runasgrouplist);
228 if (runas_match == ALLOW) {
229 cmnd_match = cmnd_matches(cs->cmnd);
230 if (cmnd_match != UNSPEC) {
234 /* Set role and type if not specified on command line. */
235 if (user_role == NULL)
236 user_role = cs->role ? estrdup(cs->role) : def_role;
237 if (user_type == NULL)
238 user_type = cs->type ? estrdup(cs->type) : def_type;
239 #endif /* HAVE_SELINUX */
247 if (match == ALLOW) {
248 SET(validated, VALIDATE_OK);
249 CLR(validated, VALIDATE_NOT_OK);
251 if (tags->nopasswd != UNSPEC)
252 def_authenticate = !tags->nopasswd;
253 if (tags->noexec != UNSPEC)
254 def_noexec = tags->noexec;
255 if (tags->setenv != UNSPEC)
256 def_setenv = tags->setenv;
258 } else if (match == DENY) {
259 SET(validated, VALIDATE_NOT_OK);
260 CLR(validated, VALIDATE_OK);
262 set_perms(PERM_ROOT);
266 #define TAG_CHANGED(t) \
267 (cs->tags.t != UNSPEC && cs->tags.t != IMPLIED && cs->tags.t != tags->t)
270 sudo_file_append_cmnd(cs, tags, lbuf)
272 struct cmndtag *tags;
279 lbuf_append(lbuf, "ROLE=", cs->role, " ", NULL);
281 lbuf_append(lbuf, "TYPE=", cs->type, " ", NULL);
282 #endif /* HAVE_SELINUX */
283 if (TAG_CHANGED(setenv)) {
284 lbuf_append(lbuf, cs->tags.setenv ? "SETENV: " :
286 tags->setenv = cs->tags.setenv;
288 if (TAG_CHANGED(noexec)) {
289 lbuf_append(lbuf, cs->tags.noexec ? "NOEXEC: " :
291 tags->noexec = cs->tags.noexec;
293 if (TAG_CHANGED(nopasswd)) {
294 lbuf_append(lbuf, cs->tags.nopasswd ? "NOPASSWD: " :
296 tags->nopasswd = cs->tags.nopasswd;
299 print_member(lbuf, m->name, m->type, m->negated,
304 sudo_file_display_priv_short(pw, us, lbuf)
311 struct privilege *priv;
315 tq_foreach_fwd(&us->privileges, priv) {
316 if (hostlist_matches(&priv->hostlist) != ALLOW)
318 tags.noexec = UNSPEC;
319 tags.setenv = UNSPEC;
320 tags.nopasswd = UNSPEC;
321 lbuf_append(lbuf, " ", NULL);
322 tq_foreach_fwd(&priv->cmndlist, cs) {
323 if (cs != tq_first(&priv->cmndlist))
324 lbuf_append(lbuf, ", ", NULL);
325 lbuf_append(lbuf, "(", NULL);
326 if (!tq_empty(&cs->runasuserlist)) {
327 tq_foreach_fwd(&cs->runasuserlist, m) {
328 if (m != tq_first(&cs->runasuserlist))
329 lbuf_append(lbuf, ", ", NULL);
330 print_member(lbuf, m->name, m->type, m->negated,
333 } else if (tq_empty(&cs->runasgrouplist)) {
334 lbuf_append(lbuf, def_runas_default, NULL);
336 lbuf_append(lbuf, pw->pw_name, NULL);
338 if (!tq_empty(&cs->runasgrouplist)) {
339 lbuf_append(lbuf, " : ", NULL);
340 tq_foreach_fwd(&cs->runasgrouplist, m) {
341 if (m != tq_first(&cs->runasgrouplist))
342 lbuf_append(lbuf, ", ", NULL);
343 print_member(lbuf, m->name, m->type, m->negated,
347 lbuf_append(lbuf, ") ", NULL);
348 sudo_file_append_cmnd(cs, &tags, lbuf);
351 lbuf_print(lbuf); /* forces a newline */
357 sudo_file_display_priv_long(pw, us, lbuf)
364 struct privilege *priv;
368 tq_foreach_fwd(&us->privileges, priv) {
369 if (hostlist_matches(&priv->hostlist) != ALLOW)
371 tags.noexec = UNSPEC;
372 tags.setenv = UNSPEC;
373 tags.nopasswd = UNSPEC;
374 lbuf_print(lbuf); /* force a newline */
375 lbuf_append(lbuf, "Sudoers entry:", NULL);
377 tq_foreach_fwd(&priv->cmndlist, cs) {
378 lbuf_append(lbuf, " RunAsUsers: ", NULL);
379 if (!tq_empty(&cs->runasuserlist)) {
380 tq_foreach_fwd(&cs->runasuserlist, m) {
381 if (m != tq_first(&cs->runasuserlist))
382 lbuf_append(lbuf, ", ", NULL);
383 print_member(lbuf, m->name, m->type, m->negated,
386 } else if (tq_empty(&cs->runasgrouplist)) {
387 lbuf_append(lbuf, def_runas_default, NULL);
389 lbuf_append(lbuf, pw->pw_name, NULL);
392 if (!tq_empty(&cs->runasgrouplist)) {
393 lbuf_append(lbuf, " RunAsGroups: ", NULL);
394 tq_foreach_fwd(&cs->runasgrouplist, m) {
395 if (m != tq_first(&cs->runasgrouplist))
396 lbuf_append(lbuf, ", ", NULL);
397 print_member(lbuf, m->name, m->type, m->negated,
402 lbuf_append(lbuf, " Commands: ", NULL);
404 lbuf_append(lbuf, "\t", NULL);
405 sudo_file_append_cmnd(cs, &tags, lbuf);
414 sudo_file_display_privs(nss, pw, lbuf)
415 struct sudo_nss *nss;
422 if (nss->handle == NULL)
425 tq_foreach_fwd(&userspecs, us) {
426 if (userlist_matches(pw, &us->users) != ALLOW)
430 nfound += sudo_file_display_priv_long(pw, us, lbuf);
432 nfound += sudo_file_display_priv_short(pw, us, lbuf);
438 * Display matching Defaults entries for the given user on this host.
441 sudo_file_display_defaults(nss, pw, lbuf)
442 struct sudo_nss *nss;
450 if (nss->handle == NULL)
458 tq_foreach_fwd(&defaults, d) {
461 if (hostlist_matches(&d->binding) != ALLOW)
465 if (userlist_matches(pw, &d->binding) != ALLOW)
472 lbuf_append(lbuf, prefix, NULL);
473 if (d->val != NULL) {
474 lbuf_append(lbuf, d->var, d->op == '+' ? "+=" :
475 d->op == '-' ? "-=" : "=", NULL);
476 if (strpbrk(d->val, " \t") != NULL) {
477 lbuf_append(lbuf, "\"", NULL);
478 lbuf_append_quoted(lbuf, "\"", d->val, NULL);
479 lbuf_append(lbuf, "\"", NULL);
481 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, d->val, NULL);
483 lbuf_append(lbuf, d->op == FALSE ? "!" : "", d->var, NULL);
492 * Display Defaults entries that are per-runas or per-command
495 sudo_file_display_bound_defaults(nss, pw, lbuf)
496 struct sudo_nss *nss;
502 /* XXX - should only print ones that match what the user can do. */
503 nfound += display_bound_defaults(DEFAULTS_RUNAS, lbuf);
504 nfound += display_bound_defaults(DEFAULTS_CMND, lbuf);
510 * Display Defaults entries of the given type.
513 display_bound_defaults(dtype, lbuf)
518 struct member *m, *binding = NULL;
520 int atype, nfound = 0;
546 /* printf("Per-%s Defaults entries:\n", dname); */
547 tq_foreach_fwd(&defaults, d) {
548 if (d->type != dtype)
552 if (binding != tq_first(&d->binding)) {
553 binding = tq_first(&d->binding);
554 lbuf_append(lbuf, " Defaults", dsep, NULL);
555 for (m = binding; m != NULL; m = m->next) {
557 lbuf_append(lbuf, ",", NULL);
558 print_member(lbuf, m->name, m->type, m->negated, atype);
559 lbuf_append(lbuf, " ", NULL);
562 lbuf_append(lbuf, ", ", NULL);
563 if (d->val != NULL) {
564 lbuf_append(lbuf, d->var, d->op == '+' ? "+=" :
565 d->op == '-' ? "-=" : "=", d->val, NULL);
567 lbuf_append(lbuf, d->op == FALSE ? "!" : "", d->var, NULL);
574 sudo_file_display_cmnd(nss, pw)
575 struct sudo_nss *nss;
579 struct member *match;
580 struct privilege *priv;
583 int host_match, runas_match, cmnd_match;
585 if (nss->handle == NULL)
589 tq_foreach_rev(&userspecs, us) {
590 if (userlist_matches(pw, &us->users) != ALLOW)
593 tq_foreach_rev(&us->privileges, priv) {
594 host_match = hostlist_matches(&priv->hostlist);
595 if (host_match != ALLOW)
597 tq_foreach_rev(&priv->cmndlist, cs) {
598 runas_match = runaslist_matches(&cs->runasuserlist,
599 &cs->runasgrouplist);
600 if (runas_match == ALLOW) {
601 cmnd_match = cmnd_matches(cs->cmnd);
602 if (cmnd_match != UNSPEC) {
603 match = host_match && runas_match ?
612 if (match != NULL && !match->negated) {
613 printf("%s%s%s\n", safe_cmnd, user_args ? " " : "",
614 user_args ? user_args : "");
621 * Print the contents of a struct member to stdout
624 _print_member(lbuf, name, type, negated, alias_type)
627 int type, negated, alias_type;
631 struct sudo_command *c;
635 lbuf_append(lbuf, negated ? "!ALL" : "ALL", NULL);
638 c = (struct sudo_command *) name;
640 lbuf_append(lbuf, "!", NULL);
641 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, c->cmnd, NULL);
643 lbuf_append(lbuf, " ", NULL);
644 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, c->args, NULL);
648 if ((a = alias_find(name, alias_type)) != NULL) {
649 tq_foreach_fwd(&a->members, m) {
650 if (m != tq_first(&a->members))
651 lbuf_append(lbuf, ", ", NULL);
652 _print_member(lbuf, m->name, m->type,
653 negated ? !m->negated : m->negated, alias_type);
659 lbuf_append(lbuf, negated ? "!" : "", name, NULL);
665 print_member(lbuf, name, type, negated, alias_type)
668 int type, negated, alias_type;
671 _print_member(lbuf, name, type, negated, alias_type);