2 * Copyright (c) 2004-2005, 2007-2010 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 */
34 #endif /* HAVE_STRING_H */
37 #endif /* HAVE_STRINGS_H */
40 #endif /* HAVE_UNISTD_H */
50 /* Characters that must be quoted in sudoers */
51 #define SUDOERS_QUOTED ":\\,=#\""
53 /* sudoers nsswitch routines */
54 struct sudo_nss sudo_nss_file = {
62 sudo_file_display_cmnd,
63 sudo_file_display_defaults,
64 sudo_file_display_bound_defaults,
65 sudo_file_display_privs
72 extern char *errorfile;
73 extern int errorlineno, parse_error;
78 static void print_member __P((struct lbuf *, char *, int, int, int));
79 static int display_bound_defaults __P((int, struct lbuf *));
85 if (def_ignore_local_sudoers)
87 nss->handle = open_sudoers(_PATH_SUDOERS, FALSE, NULL);
88 return(nss->handle ? 0 : -1);
95 /* Free parser data structures and close sudoers file. */
97 if (nss->handle != NULL) {
106 * Parse the specified sudoers file.
110 struct sudo_nss *nss;
112 if (nss->handle == NULL)
115 init_parser(_PATH_SUDOERS, 0);
117 if (yyparse() != 0 || parse_error) {
118 log_error(NO_EXIT, "parse error in %s near line %d",
119 errorfile, errorlineno);
126 * Wrapper around update_defaults() for nsswitch code.
129 sudo_file_setdefs(nss)
130 struct sudo_nss *nss;
132 if (nss->handle == NULL)
135 if (!update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER))
141 * Look up the user in the parsed sudoers file and check to see if they are
142 * allowed to run the specified command on this host as the target user.
145 sudo_file_lookup(nss, validated, pwflag)
146 struct sudo_nss *nss;
150 int match, host_match, runas_match, cmnd_match;
152 struct cmndtag *tags = NULL;
153 struct privilege *priv;
156 if (nss->handle == NULL)
160 * Only check the actual command if pwflag is not set.
161 * It is set for the "validate", "list" and "kill" pseudo-commands.
162 * Always check the host and user.
166 enum def_tupple pwcheck;
168 pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
169 nopass = (pwcheck == all) ? TRUE : FALSE;
172 SET(validated, FLAG_NO_CHECK);
173 CLR(validated, FLAG_NO_USER);
174 CLR(validated, FLAG_NO_HOST);
176 tq_foreach_fwd(&userspecs, us) {
177 if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
179 tq_foreach_fwd(&us->privileges, priv) {
180 if (hostlist_matches(&priv->hostlist) != ALLOW)
182 tq_foreach_fwd(&priv->cmndlist, cs) {
183 /* Only check the command when listing another user. */
184 if (user_uid == 0 || list_pw == NULL ||
185 user_uid == list_pw->pw_uid ||
186 cmnd_matches(cs->cmnd) == ALLOW)
188 if ((pwcheck == any && cs->tags.nopasswd == TRUE) ||
189 (pwcheck == all && cs->tags.nopasswd != TRUE))
190 nopass = cs->tags.nopasswd;
194 if (match == ALLOW || user_uid == 0) {
195 /* User has an entry for this host. */
196 SET(validated, VALIDATE_OK);
197 } else if (match == DENY)
198 SET(validated, VALIDATE_NOT_OK);
199 if (pwcheck == always && def_authenticate)
200 SET(validated, FLAG_CHECK_USER);
201 else if (pwcheck == never || nopass == TRUE)
202 def_authenticate = FALSE;
206 /* Need to be runas user while stat'ing things. */
207 set_perms(PERM_RUNAS);
210 tq_foreach_rev(&userspecs, us) {
211 if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
213 CLR(validated, FLAG_NO_USER);
214 tq_foreach_rev(&us->privileges, priv) {
215 host_match = hostlist_matches(&priv->hostlist);
216 if (host_match == ALLOW)
217 CLR(validated, FLAG_NO_HOST);
220 tq_foreach_rev(&priv->cmndlist, cs) {
221 runas_match = runaslist_matches(&cs->runasuserlist,
222 &cs->runasgrouplist);
223 if (runas_match == ALLOW) {
224 cmnd_match = cmnd_matches(cs->cmnd);
225 if (cmnd_match != UNSPEC) {
229 /* Set role and type if not specified on command line. */
230 if (user_role == NULL)
231 user_role = cs->role ? estrdup(cs->role) : def_role;
232 if (user_type == NULL)
233 user_type = cs->type ? estrdup(cs->type) : def_type;
234 #endif /* HAVE_SELINUX */
242 if (match == ALLOW) {
243 SET(validated, VALIDATE_OK);
244 CLR(validated, VALIDATE_NOT_OK);
246 if (tags->nopasswd != UNSPEC)
247 def_authenticate = !tags->nopasswd;
248 if (tags->noexec != UNSPEC)
249 def_noexec = tags->noexec;
250 if (tags->setenv != UNSPEC)
251 def_setenv = tags->setenv;
252 if (tags->log_input != UNSPEC)
253 def_log_input = tags->log_input;
254 if (tags->log_output != UNSPEC)
255 def_log_output = tags->log_output;
257 } else if (match == DENY) {
258 SET(validated, VALIDATE_NOT_OK);
259 CLR(validated, VALIDATE_OK);
261 set_perms(PERM_ROOT);
265 #define TAG_CHANGED(t) \
266 (cs->tags.t != UNSPEC && cs->tags.t != IMPLIED && cs->tags.t != tags->t)
269 sudo_file_append_cmnd(cs, tags, lbuf)
271 struct cmndtag *tags;
278 lbuf_append(lbuf, "ROLE=", cs->role, " ", NULL);
280 lbuf_append(lbuf, "TYPE=", cs->type, " ", NULL);
281 #endif /* HAVE_SELINUX */
282 if (TAG_CHANGED(setenv)) {
283 lbuf_append(lbuf, cs->tags.setenv ? "SETENV: " :
285 tags->setenv = cs->tags.setenv;
287 if (TAG_CHANGED(noexec)) {
288 lbuf_append(lbuf, cs->tags.noexec ? "NOEXEC: " :
290 tags->noexec = cs->tags.noexec;
292 if (TAG_CHANGED(nopasswd)) {
293 lbuf_append(lbuf, cs->tags.nopasswd ? "NOPASSWD: " :
295 tags->nopasswd = cs->tags.nopasswd;
297 if (TAG_CHANGED(log_input)) {
298 lbuf_append(lbuf, cs->tags.log_input ? "LOG_INPUT: " :
299 "NOLOG_INPUT: ", NULL);
300 tags->log_input = cs->tags.log_input;
302 if (TAG_CHANGED(log_output)) {
303 lbuf_append(lbuf, cs->tags.log_output ? "LOG_OUTPUT: " :
304 "NOLOG_OUTPUT: ", NULL);
305 tags->log_output = cs->tags.log_output;
308 print_member(lbuf, m->name, m->type, m->negated,
313 sudo_file_display_priv_short(pw, us, lbuf)
320 struct privilege *priv;
324 tq_foreach_fwd(&us->privileges, priv) {
325 if (hostlist_matches(&priv->hostlist) != ALLOW)
327 tags.noexec = UNSPEC;
328 tags.setenv = UNSPEC;
329 tags.nopasswd = UNSPEC;
330 tags.log_input = UNSPEC;
331 tags.log_output = UNSPEC;
332 lbuf_append(lbuf, " ", NULL);
333 tq_foreach_fwd(&priv->cmndlist, cs) {
334 if (cs != tq_first(&priv->cmndlist))
335 lbuf_append(lbuf, ", ", NULL);
336 lbuf_append(lbuf, "(", NULL);
337 if (!tq_empty(&cs->runasuserlist)) {
338 tq_foreach_fwd(&cs->runasuserlist, m) {
339 if (m != tq_first(&cs->runasuserlist))
340 lbuf_append(lbuf, ", ", NULL);
341 print_member(lbuf, m->name, m->type, m->negated,
344 } else if (tq_empty(&cs->runasgrouplist)) {
345 lbuf_append(lbuf, def_runas_default, NULL);
347 lbuf_append(lbuf, pw->pw_name, NULL);
349 if (!tq_empty(&cs->runasgrouplist)) {
350 lbuf_append(lbuf, " : ", NULL);
351 tq_foreach_fwd(&cs->runasgrouplist, m) {
352 if (m != tq_first(&cs->runasgrouplist))
353 lbuf_append(lbuf, ", ", NULL);
354 print_member(lbuf, m->name, m->type, m->negated,
358 lbuf_append(lbuf, ") ", NULL);
359 sudo_file_append_cmnd(cs, &tags, lbuf);
362 lbuf_append(lbuf, "\n", NULL);
368 sudo_file_display_priv_long(pw, us, lbuf)
375 struct privilege *priv;
379 tq_foreach_fwd(&us->privileges, priv) {
380 if (hostlist_matches(&priv->hostlist) != ALLOW)
382 tags.noexec = UNSPEC;
383 tags.setenv = UNSPEC;
384 tags.nopasswd = UNSPEC;
385 tags.log_input = UNSPEC;
386 tags.log_output = UNSPEC;
387 lbuf_append(lbuf, "\nSudoers entry:\n", NULL);
388 tq_foreach_fwd(&priv->cmndlist, cs) {
389 lbuf_append(lbuf, " RunAsUsers: ", NULL);
390 if (!tq_empty(&cs->runasuserlist)) {
391 tq_foreach_fwd(&cs->runasuserlist, m) {
392 if (m != tq_first(&cs->runasuserlist))
393 lbuf_append(lbuf, ", ", NULL);
394 print_member(lbuf, m->name, m->type, m->negated,
397 } else if (tq_empty(&cs->runasgrouplist)) {
398 lbuf_append(lbuf, def_runas_default, NULL);
400 lbuf_append(lbuf, pw->pw_name, NULL);
402 lbuf_append(lbuf, "\n", NULL);
403 if (!tq_empty(&cs->runasgrouplist)) {
404 lbuf_append(lbuf, " RunAsGroups: ", NULL);
405 tq_foreach_fwd(&cs->runasgrouplist, m) {
406 if (m != tq_first(&cs->runasgrouplist))
407 lbuf_append(lbuf, ", ", NULL);
408 print_member(lbuf, m->name, m->type, m->negated,
411 lbuf_append(lbuf, "\n", NULL);
413 lbuf_append(lbuf, " Commands:\n\t", NULL);
414 sudo_file_append_cmnd(cs, &tags, lbuf);
415 lbuf_append(lbuf, "\n", NULL);
423 sudo_file_display_privs(nss, pw, lbuf)
424 struct sudo_nss *nss;
431 if (nss->handle == NULL)
434 tq_foreach_fwd(&userspecs, us) {
435 if (userlist_matches(pw, &us->users) != ALLOW)
439 nfound += sudo_file_display_priv_long(pw, us, lbuf);
441 nfound += sudo_file_display_priv_short(pw, us, lbuf);
448 * Display matching Defaults entries for the given user on this host.
451 sudo_file_display_defaults(nss, pw, lbuf)
452 struct sudo_nss *nss;
460 if (nss->handle == NULL)
463 if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
468 tq_foreach_fwd(&defaults, d) {
471 if (hostlist_matches(&d->binding) != ALLOW)
475 if (userlist_matches(pw, &d->binding) != ALLOW)
482 lbuf_append(lbuf, prefix, NULL);
483 if (d->val != NULL) {
484 lbuf_append(lbuf, d->var, d->op == '+' ? "+=" :
485 d->op == '-' ? "-=" : "=", NULL);
486 if (strpbrk(d->val, " \t") != NULL) {
487 lbuf_append(lbuf, "\"", NULL);
488 lbuf_append_quoted(lbuf, "\"", d->val, NULL);
489 lbuf_append(lbuf, "\"", NULL);
491 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, d->val, NULL);
493 lbuf_append(lbuf, d->op == FALSE ? "!" : "", d->var, NULL);
502 * Display Defaults entries that are per-runas or per-command
505 sudo_file_display_bound_defaults(nss, pw, lbuf)
506 struct sudo_nss *nss;
512 /* XXX - should only print ones that match what the user can do. */
513 nfound += display_bound_defaults(DEFAULTS_RUNAS, lbuf);
514 nfound += display_bound_defaults(DEFAULTS_CMND, lbuf);
520 * Display Defaults entries of the given type.
523 display_bound_defaults(dtype, lbuf)
528 struct member *m, *binding = NULL;
530 int atype, nfound = 0;
556 /* printf("Per-%s Defaults entries:\n", dname); */
557 tq_foreach_fwd(&defaults, d) {
558 if (d->type != dtype)
562 if (binding != tq_first(&d->binding)) {
563 binding = tq_first(&d->binding);
565 lbuf_append(lbuf, "\n", NULL);
566 lbuf_append(lbuf, " Defaults", dsep, NULL);
567 for (m = binding; m != NULL; m = m->next) {
569 lbuf_append(lbuf, ",", NULL);
570 print_member(lbuf, m->name, m->type, m->negated, atype);
571 lbuf_append(lbuf, " ", NULL);
574 lbuf_append(lbuf, ", ", NULL);
575 if (d->val != NULL) {
576 lbuf_append(lbuf, d->var, d->op == '+' ? "+=" :
577 d->op == '-' ? "-=" : "=", d->val, NULL);
579 lbuf_append(lbuf, d->op == FALSE ? "!" : "", d->var, NULL);
586 sudo_file_display_cmnd(nss, pw)
587 struct sudo_nss *nss;
591 struct member *match;
592 struct privilege *priv;
595 int host_match, runas_match, cmnd_match;
597 if (nss->handle == NULL)
601 tq_foreach_rev(&userspecs, us) {
602 if (userlist_matches(pw, &us->users) != ALLOW)
605 tq_foreach_rev(&us->privileges, priv) {
606 host_match = hostlist_matches(&priv->hostlist);
607 if (host_match != ALLOW)
609 tq_foreach_rev(&priv->cmndlist, cs) {
610 runas_match = runaslist_matches(&cs->runasuserlist,
611 &cs->runasgrouplist);
612 if (runas_match == ALLOW) {
613 cmnd_match = cmnd_matches(cs->cmnd);
614 if (cmnd_match != UNSPEC) {
615 match = host_match && runas_match ?
624 if (match != NULL && !match->negated) {
625 printf("%s%s%s\n", safe_cmnd, user_args ? " " : "",
626 user_args ? user_args : "");
634 * Print the contents of a struct member to stdout
637 _print_member(lbuf, name, type, negated, alias_type)
640 int type, negated, alias_type;
644 struct sudo_command *c;
648 lbuf_append(lbuf, negated ? "!ALL" : "ALL", NULL);
651 c = (struct sudo_command *) name;
653 lbuf_append(lbuf, "!", NULL);
654 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, c->cmnd, NULL);
656 lbuf_append(lbuf, " ", NULL);
657 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, c->args, NULL);
661 if ((a = alias_find(name, alias_type)) != NULL) {
662 tq_foreach_fwd(&a->members, m) {
663 if (m != tq_first(&a->members))
664 lbuf_append(lbuf, ", ", NULL);
665 _print_member(lbuf, m->name, m->type,
666 negated ? !m->negated : m->negated, alias_type);
672 lbuf_append(lbuf, negated ? "!" : "", name, NULL);
678 print_member(lbuf, name, type, negated, alias_type)
681 int type, negated, alias_type;
684 _print_member(lbuf, name, type, negated, alias_type);