2 * Copyright (c) 2004-2005, 2007-2011 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(struct lbuf *, char *, int, int, int);
79 static int display_bound_defaults(int, struct lbuf *);
82 sudo_file_open(struct sudo_nss *nss)
84 if (def_ignore_local_sudoers)
86 nss->handle = open_sudoers(sudoers_file, FALSE, NULL);
87 return nss->handle ? 0 : -1;
91 sudo_file_close(struct sudo_nss *nss)
93 /* Free parser data structures and close sudoers file. */
95 if (nss->handle != NULL) {
104 * Parse the specified sudoers file.
107 sudo_file_parse(struct sudo_nss *nss)
109 if (nss->handle == NULL)
112 init_parser(sudoers_file, 0);
114 if (yyparse() != 0 || parse_error) {
115 log_error(NO_EXIT, _("parse error in %s near line %d"),
116 errorfile, errorlineno);
123 * Wrapper around update_defaults() for nsswitch code.
126 sudo_file_setdefs(struct sudo_nss *nss)
128 if (nss->handle == NULL)
131 if (!update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER))
137 * Look up the user in the parsed sudoers file and check to see if they are
138 * allowed to run the specified command on this host as the target user.
141 sudo_file_lookup(struct sudo_nss *nss, int validated, int pwflag)
143 int match, host_match, runas_match, cmnd_match;
145 struct cmndtag *tags = NULL;
146 struct privilege *priv;
149 if (nss->handle == NULL)
153 * Only check the actual command if pwflag is not set.
154 * It is set for the "validate", "list" and "kill" pseudo-commands.
155 * Always check the host and user.
159 enum def_tuple pwcheck;
161 pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
162 nopass = (pwcheck == all) ? TRUE : FALSE;
165 SET(validated, FLAG_NO_CHECK);
166 CLR(validated, FLAG_NO_USER);
167 CLR(validated, FLAG_NO_HOST);
169 tq_foreach_fwd(&userspecs, us) {
170 if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
172 tq_foreach_fwd(&us->privileges, priv) {
173 if (hostlist_matches(&priv->hostlist) != ALLOW)
175 tq_foreach_fwd(&priv->cmndlist, cs) {
176 /* Only check the command when listing another user. */
177 if (user_uid == 0 || list_pw == NULL ||
178 user_uid == list_pw->pw_uid ||
179 cmnd_matches(cs->cmnd) == ALLOW)
181 if ((pwcheck == any && cs->tags.nopasswd == TRUE) ||
182 (pwcheck == all && cs->tags.nopasswd != TRUE))
183 nopass = cs->tags.nopasswd;
187 if (match == ALLOW || user_uid == 0) {
188 /* User has an entry for this host. */
189 SET(validated, VALIDATE_OK);
190 } else if (match == DENY)
191 SET(validated, VALIDATE_NOT_OK);
192 if (pwcheck == always && def_authenticate)
193 SET(validated, FLAG_CHECK_USER);
194 else if (pwcheck == never || nopass == TRUE)
195 def_authenticate = FALSE;
199 /* Need to be runas user while stat'ing things. */
200 set_perms(PERM_RUNAS);
203 tq_foreach_rev(&userspecs, us) {
204 if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
206 CLR(validated, FLAG_NO_USER);
207 tq_foreach_rev(&us->privileges, priv) {
208 host_match = hostlist_matches(&priv->hostlist);
209 if (host_match == ALLOW)
210 CLR(validated, FLAG_NO_HOST);
213 tq_foreach_rev(&priv->cmndlist, cs) {
214 runas_match = runaslist_matches(&cs->runasuserlist,
215 &cs->runasgrouplist);
216 if (runas_match == ALLOW) {
217 cmnd_match = cmnd_matches(cs->cmnd);
218 if (cmnd_match != UNSPEC) {
222 /* Set role and type if not specified on command line. */
223 if (user_role == NULL)
224 user_role = cs->role ? estrdup(cs->role) : def_role;
225 if (user_type == NULL)
226 user_type = cs->type ? estrdup(cs->type) : def_type;
227 #endif /* HAVE_SELINUX */
235 if (match == ALLOW) {
236 SET(validated, VALIDATE_OK);
237 CLR(validated, VALIDATE_NOT_OK);
239 if (tags->nopasswd != UNSPEC)
240 def_authenticate = !tags->nopasswd;
241 if (tags->noexec != UNSPEC)
242 def_noexec = tags->noexec;
243 if (tags->setenv != UNSPEC)
244 def_setenv = tags->setenv;
245 if (tags->log_input != UNSPEC)
246 def_log_input = tags->log_input;
247 if (tags->log_output != UNSPEC)
248 def_log_output = tags->log_output;
250 } else if (match == DENY) {
251 SET(validated, VALIDATE_NOT_OK);
252 CLR(validated, VALIDATE_OK);
258 #define TAG_CHANGED(t) \
259 (cs->tags.t != UNSPEC && cs->tags.t != IMPLIED && cs->tags.t != tags->t)
262 sudo_file_append_cmnd(struct cmndspec *cs, struct cmndtag *tags,
269 lbuf_append(lbuf, "ROLE=%s ", cs->role);
271 lbuf_append(lbuf, "TYPE=%s ", cs->type);
272 #endif /* HAVE_SELINUX */
273 if (TAG_CHANGED(setenv)) {
274 lbuf_append(lbuf, cs->tags.setenv ? "SETENV: " : "NOSETENV: ");
275 tags->setenv = cs->tags.setenv;
277 if (TAG_CHANGED(noexec)) {
278 lbuf_append(lbuf, cs->tags.noexec ? "NOEXEC: " : "EXEC: ");
279 tags->noexec = cs->tags.noexec;
281 if (TAG_CHANGED(nopasswd)) {
282 lbuf_append(lbuf, cs->tags.nopasswd ? "NOPASSWD: " : "PASSWD: ");
283 tags->nopasswd = cs->tags.nopasswd;
285 if (TAG_CHANGED(log_input)) {
286 lbuf_append(lbuf, cs->tags.log_input ? "LOG_INPUT: " : "NOLOG_INPUT: ");
287 tags->log_input = cs->tags.log_input;
289 if (TAG_CHANGED(log_output)) {
290 lbuf_append(lbuf, cs->tags.log_output ? "LOG_OUTPUT: " : "NOLOG_OUTPUT: ");
291 tags->log_output = cs->tags.log_output;
294 print_member(lbuf, m->name, m->type, m->negated,
299 sudo_file_display_priv_short(struct passwd *pw, struct userspec *us,
304 struct privilege *priv;
308 tq_foreach_fwd(&us->privileges, priv) {
309 if (hostlist_matches(&priv->hostlist) != ALLOW)
311 tags.noexec = UNSPEC;
312 tags.setenv = UNSPEC;
313 tags.nopasswd = UNSPEC;
314 tags.log_input = UNSPEC;
315 tags.log_output = UNSPEC;
316 lbuf_append(lbuf, " ");
317 tq_foreach_fwd(&priv->cmndlist, cs) {
318 if (cs != tq_first(&priv->cmndlist))
319 lbuf_append(lbuf, ", ");
320 lbuf_append(lbuf, "(");
321 if (!tq_empty(&cs->runasuserlist)) {
322 tq_foreach_fwd(&cs->runasuserlist, m) {
323 if (m != tq_first(&cs->runasuserlist))
324 lbuf_append(lbuf, ", ");
325 print_member(lbuf, m->name, m->type, m->negated,
328 } else if (tq_empty(&cs->runasgrouplist)) {
329 lbuf_append(lbuf, "%s", def_runas_default);
331 lbuf_append(lbuf, "%s", pw->pw_name);
333 if (!tq_empty(&cs->runasgrouplist)) {
334 lbuf_append(lbuf, " : ");
335 tq_foreach_fwd(&cs->runasgrouplist, m) {
336 if (m != tq_first(&cs->runasgrouplist))
337 lbuf_append(lbuf, ", ");
338 print_member(lbuf, m->name, m->type, m->negated,
342 lbuf_append(lbuf, ") ");
343 sudo_file_append_cmnd(cs, &tags, lbuf);
346 lbuf_append(lbuf, "\n");
352 sudo_file_display_priv_long(struct passwd *pw, struct userspec *us,
357 struct privilege *priv;
361 tq_foreach_fwd(&us->privileges, priv) {
362 if (hostlist_matches(&priv->hostlist) != ALLOW)
364 tags.noexec = UNSPEC;
365 tags.setenv = UNSPEC;
366 tags.nopasswd = UNSPEC;
367 tags.log_input = UNSPEC;
368 tags.log_output = UNSPEC;
369 lbuf_append(lbuf, _("\nSudoers entry:\n"));
370 tq_foreach_fwd(&priv->cmndlist, cs) {
371 lbuf_append(lbuf, _(" RunAsUsers: "));
372 if (!tq_empty(&cs->runasuserlist)) {
373 tq_foreach_fwd(&cs->runasuserlist, m) {
374 if (m != tq_first(&cs->runasuserlist))
375 lbuf_append(lbuf, ", ");
376 print_member(lbuf, m->name, m->type, m->negated,
379 } else if (tq_empty(&cs->runasgrouplist)) {
380 lbuf_append(lbuf, "%s", def_runas_default);
382 lbuf_append(lbuf, "%s", pw->pw_name);
384 lbuf_append(lbuf, "\n");
385 if (!tq_empty(&cs->runasgrouplist)) {
386 lbuf_append(lbuf, _(" RunAsGroups: "));
387 tq_foreach_fwd(&cs->runasgrouplist, m) {
388 if (m != tq_first(&cs->runasgrouplist))
389 lbuf_append(lbuf, ", ");
390 print_member(lbuf, m->name, m->type, m->negated,
393 lbuf_append(lbuf, "\n");
395 lbuf_append(lbuf, _(" Commands:\n\t"));
396 sudo_file_append_cmnd(cs, &tags, lbuf);
397 lbuf_append(lbuf, "\n");
405 sudo_file_display_privs(struct sudo_nss *nss, struct passwd *pw,
411 if (nss->handle == NULL)
414 tq_foreach_fwd(&userspecs, us) {
415 if (userlist_matches(pw, &us->users) != ALLOW)
419 nfound += sudo_file_display_priv_long(pw, us, lbuf);
421 nfound += sudo_file_display_priv_short(pw, us, lbuf);
428 * Display matching Defaults entries for the given user on this host.
431 sudo_file_display_defaults(struct sudo_nss *nss, struct passwd *pw,
438 if (nss->handle == NULL)
441 if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
446 tq_foreach_fwd(&defaults, d) {
449 if (hostlist_matches(&d->binding) != ALLOW)
453 if (userlist_matches(pw, &d->binding) != ALLOW)
460 if (d->val != NULL) {
461 lbuf_append(lbuf, "%s%s%s", prefix, d->var,
462 d->op == '+' ? "+=" : d->op == '-' ? "-=" : "=");
463 if (strpbrk(d->val, " \t") != NULL) {
464 lbuf_append(lbuf, "\"");
465 lbuf_append_quoted(lbuf, "\"", "%s", d->val);
466 lbuf_append(lbuf, "\"");
468 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", d->val);
470 lbuf_append(lbuf, "%s%s%s", prefix,
471 d->op == FALSE ? "!" : "", d->var);
480 * Display Defaults entries that are per-runas or per-command
483 sudo_file_display_bound_defaults(struct sudo_nss *nss, struct passwd *pw,
488 /* XXX - should only print ones that match what the user can do. */
489 nfound += display_bound_defaults(DEFAULTS_RUNAS, lbuf);
490 nfound += display_bound_defaults(DEFAULTS_CMND, lbuf);
496 * Display Defaults entries of the given type.
499 display_bound_defaults(int dtype, struct lbuf *lbuf)
502 struct member *m, *binding = NULL;
504 int atype, nfound = 0;
526 tq_foreach_fwd(&defaults, d) {
527 if (d->type != dtype)
531 if (binding != tq_first(&d->binding)) {
532 binding = tq_first(&d->binding);
534 lbuf_append(lbuf, "\n");
535 lbuf_append(lbuf, " Defaults%s", dsep);
536 for (m = binding; m != NULL; m = m->next) {
538 lbuf_append(lbuf, ",");
539 print_member(lbuf, m->name, m->type, m->negated, atype);
540 lbuf_append(lbuf, " ");
543 lbuf_append(lbuf, ", ");
544 if (d->val != NULL) {
545 lbuf_append(lbuf, "%s%s%s", d->var, d->op == '+' ? "+=" :
546 d->op == '-' ? "-=" : "=", d->val);
548 lbuf_append(lbuf, "%s%s", d->op == FALSE ? "!" : "", d->var);
555 sudo_file_display_cmnd(struct sudo_nss *nss, struct passwd *pw)
558 struct member *match;
559 struct privilege *priv;
562 int host_match, runas_match, cmnd_match;
564 if (nss->handle == NULL)
568 tq_foreach_rev(&userspecs, us) {
569 if (userlist_matches(pw, &us->users) != ALLOW)
572 tq_foreach_rev(&us->privileges, priv) {
573 host_match = hostlist_matches(&priv->hostlist);
574 if (host_match != ALLOW)
576 tq_foreach_rev(&priv->cmndlist, cs) {
577 runas_match = runaslist_matches(&cs->runasuserlist,
578 &cs->runasgrouplist);
579 if (runas_match == ALLOW) {
580 cmnd_match = cmnd_matches(cs->cmnd);
581 if (cmnd_match != UNSPEC) {
582 match = host_match && runas_match ? cs->cmnd : NULL;
590 if (match != NULL && !match->negated) {
591 sudo_printf(SUDO_CONV_INFO_MSG, "%s%s%s\n",
592 safe_cmnd, user_args ? " " : "", user_args ? user_args : "");
600 * Print the contents of a struct member to stdout
603 _print_member(struct lbuf *lbuf, char *name, int type, int negated,
608 struct sudo_command *c;
612 lbuf_append(lbuf, "%sALL", negated ? "!" : "");
615 c = (struct sudo_command *) name;
617 lbuf_append(lbuf, "!");
618 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", c->cmnd);
620 lbuf_append(lbuf, " ");
621 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", c->args);
625 if ((a = alias_find(name, alias_type)) != NULL) {
626 tq_foreach_fwd(&a->members, m) {
627 if (m != tq_first(&a->members))
628 lbuf_append(lbuf, ", ");
629 _print_member(lbuf, m->name, m->type,
630 negated ? !m->negated : m->negated, alias_type);
636 lbuf_append(lbuf, "%s%s", negated ? "!" : "", name);
642 print_member(struct lbuf *lbuf, char *name, int type, int negated,
646 _print_member(lbuf, name, type, negated, alias_type);