2 * Copyright (c) 2004-2005, 2007-2012 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;
74 extern bool parse_error;
79 static void print_member(struct lbuf *, char *, int, int, int);
80 static int display_bound_defaults(int, struct lbuf *);
83 sudo_file_open(struct sudo_nss *nss)
85 debug_decl(sudo_file_open, SUDO_DEBUG_NSS)
87 if (def_ignore_local_sudoers)
89 nss->handle = open_sudoers(sudoers_file, false, NULL);
90 debug_return_int(nss->handle ? 0 : -1);
94 sudo_file_close(struct sudo_nss *nss)
96 debug_decl(sudo_file_close, SUDO_DEBUG_NSS)
98 /* Free parser data structures and close sudoers file. */
100 if (nss->handle != NULL) {
109 * Parse the specified sudoers file.
112 sudo_file_parse(struct sudo_nss *nss)
114 debug_decl(sudo_file_close, SUDO_DEBUG_NSS)
116 if (nss->handle == NULL)
117 debug_return_int(-1);
119 init_parser(sudoers_file, 0);
121 if (yyparse() != 0 || parse_error) {
122 if (errorlineno != -1) {
123 log_error(0, _("parse error in %s near line %d"),
124 errorfile, errorlineno);
126 log_error(0, _("parse error in %s"), errorfile);
128 debug_return_int(-1);
134 * Wrapper around update_defaults() for nsswitch code.
137 sudo_file_setdefs(struct sudo_nss *nss)
139 debug_decl(sudo_file_setdefs, SUDO_DEBUG_NSS)
141 if (nss->handle == NULL)
142 debug_return_int(-1);
144 if (!update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER))
145 debug_return_int(-1);
150 * Look up the user in the parsed sudoers file and check to see if they are
151 * allowed to run the specified command on this host as the target user.
154 sudo_file_lookup(struct sudo_nss *nss, int validated, int pwflag)
156 int match, host_match, runas_match, cmnd_match;
158 struct cmndtag *tags = NULL;
159 struct privilege *priv;
161 debug_decl(sudo_file_lookup, SUDO_DEBUG_NSS)
163 if (nss->handle == NULL)
164 debug_return_int(validated);
167 * Only check the actual command if pwflag is not set.
168 * It is set for the "validate", "list" and "kill" pseudo-commands.
169 * Always check the host and user.
173 enum def_tuple pwcheck;
175 pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
176 nopass = (pwcheck == all) ? true : false;
179 SET(validated, FLAG_NO_CHECK);
180 CLR(validated, FLAG_NO_USER);
181 CLR(validated, FLAG_NO_HOST);
183 tq_foreach_fwd(&userspecs, us) {
184 if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
186 tq_foreach_fwd(&us->privileges, priv) {
187 if (hostlist_matches(&priv->hostlist) != ALLOW)
189 tq_foreach_fwd(&priv->cmndlist, cs) {
190 /* Only check the command when listing another user. */
191 if (user_uid == 0 || list_pw == NULL ||
192 user_uid == list_pw->pw_uid ||
193 cmnd_matches(cs->cmnd) == ALLOW)
195 if ((pwcheck == any && cs->tags.nopasswd == true) ||
196 (pwcheck == all && cs->tags.nopasswd != true))
197 nopass = cs->tags.nopasswd;
201 if (match == ALLOW || user_uid == 0) {
202 /* User has an entry for this host. */
203 SET(validated, VALIDATE_OK);
204 } else if (match == DENY)
205 SET(validated, VALIDATE_NOT_OK);
206 if (pwcheck == always && def_authenticate)
207 SET(validated, FLAG_CHECK_USER);
208 else if (pwcheck == never || nopass == true)
209 def_authenticate = false;
210 debug_return_int(validated);
213 /* Need to be runas user while stat'ing things. */
214 set_perms(PERM_RUNAS);
217 tq_foreach_rev(&userspecs, us) {
218 if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
220 CLR(validated, FLAG_NO_USER);
221 tq_foreach_rev(&us->privileges, priv) {
222 host_match = hostlist_matches(&priv->hostlist);
223 if (host_match == ALLOW)
224 CLR(validated, FLAG_NO_HOST);
227 tq_foreach_rev(&priv->cmndlist, cs) {
228 runas_match = runaslist_matches(&cs->runasuserlist,
229 &cs->runasgrouplist);
230 if (runas_match == ALLOW) {
231 cmnd_match = cmnd_matches(cs->cmnd);
232 if (cmnd_match != UNSPEC) {
236 /* Set role and type if not specified on command line. */
237 if (user_role == NULL)
238 user_role = cs->role ? estrdup(cs->role) : def_role;
239 if (user_type == NULL)
240 user_type = cs->type ? estrdup(cs->type) : def_type;
241 #endif /* HAVE_SELINUX */
249 if (match == ALLOW) {
250 SET(validated, VALIDATE_OK);
251 CLR(validated, VALIDATE_NOT_OK);
253 if (tags->nopasswd != UNSPEC)
254 def_authenticate = !tags->nopasswd;
255 if (tags->noexec != UNSPEC)
256 def_noexec = tags->noexec;
257 if (tags->setenv != UNSPEC)
258 def_setenv = tags->setenv;
259 if (tags->log_input != UNSPEC)
260 def_log_input = tags->log_input;
261 if (tags->log_output != UNSPEC)
262 def_log_output = tags->log_output;
264 } else if (match == DENY) {
265 SET(validated, VALIDATE_NOT_OK);
266 CLR(validated, VALIDATE_OK);
267 if (tags != NULL && tags->nopasswd != UNSPEC)
268 def_authenticate = !tags->nopasswd;
271 debug_return_int(validated);
274 #define TAG_CHANGED(t) \
275 (cs->tags.t != UNSPEC && cs->tags.t != IMPLIED && cs->tags.t != tags->t)
278 sudo_file_append_cmnd(struct cmndspec *cs, struct cmndtag *tags,
282 debug_decl(sudo_file_append_cmnd, SUDO_DEBUG_NSS)
286 lbuf_append(lbuf, "ROLE=%s ", cs->role);
288 lbuf_append(lbuf, "TYPE=%s ", cs->type);
289 #endif /* HAVE_SELINUX */
290 if (TAG_CHANGED(setenv)) {
291 lbuf_append(lbuf, cs->tags.setenv ? "SETENV: " : "NOSETENV: ");
292 tags->setenv = cs->tags.setenv;
294 if (TAG_CHANGED(noexec)) {
295 lbuf_append(lbuf, cs->tags.noexec ? "NOEXEC: " : "EXEC: ");
296 tags->noexec = cs->tags.noexec;
298 if (TAG_CHANGED(nopasswd)) {
299 lbuf_append(lbuf, cs->tags.nopasswd ? "NOPASSWD: " : "PASSWD: ");
300 tags->nopasswd = cs->tags.nopasswd;
302 if (TAG_CHANGED(log_input)) {
303 lbuf_append(lbuf, cs->tags.log_input ? "LOG_INPUT: " : "NOLOG_INPUT: ");
304 tags->log_input = cs->tags.log_input;
306 if (TAG_CHANGED(log_output)) {
307 lbuf_append(lbuf, cs->tags.log_output ? "LOG_OUTPUT: " : "NOLOG_OUTPUT: ");
308 tags->log_output = cs->tags.log_output;
311 print_member(lbuf, m->name, m->type, m->negated,
317 sudo_file_display_priv_short(struct passwd *pw, struct userspec *us,
322 struct privilege *priv;
325 debug_decl(sudo_file_display_priv_short, SUDO_DEBUG_NSS)
327 tq_foreach_fwd(&us->privileges, priv) {
328 if (hostlist_matches(&priv->hostlist) != ALLOW)
330 tags.noexec = UNSPEC;
331 tags.setenv = UNSPEC;
332 tags.nopasswd = UNSPEC;
333 tags.log_input = UNSPEC;
334 tags.log_output = UNSPEC;
335 lbuf_append(lbuf, " ");
336 tq_foreach_fwd(&priv->cmndlist, cs) {
337 if (cs != tq_first(&priv->cmndlist))
338 lbuf_append(lbuf, ", ");
339 lbuf_append(lbuf, "(");
340 if (!tq_empty(&cs->runasuserlist)) {
341 tq_foreach_fwd(&cs->runasuserlist, m) {
342 if (m != tq_first(&cs->runasuserlist))
343 lbuf_append(lbuf, ", ");
344 print_member(lbuf, m->name, m->type, m->negated,
347 } else if (tq_empty(&cs->runasgrouplist)) {
348 lbuf_append(lbuf, "%s", def_runas_default);
350 lbuf_append(lbuf, "%s", pw->pw_name);
352 if (!tq_empty(&cs->runasgrouplist)) {
353 lbuf_append(lbuf, " : ");
354 tq_foreach_fwd(&cs->runasgrouplist, m) {
355 if (m != tq_first(&cs->runasgrouplist))
356 lbuf_append(lbuf, ", ");
357 print_member(lbuf, m->name, m->type, m->negated,
361 lbuf_append(lbuf, ") ");
362 sudo_file_append_cmnd(cs, &tags, lbuf);
365 lbuf_append(lbuf, "\n");
367 debug_return_int(nfound);
371 sudo_file_display_priv_long(struct passwd *pw, struct userspec *us,
376 struct privilege *priv;
379 debug_decl(sudo_file_display_priv_long, SUDO_DEBUG_NSS)
381 tq_foreach_fwd(&us->privileges, priv) {
382 if (hostlist_matches(&priv->hostlist) != ALLOW)
384 tags.noexec = UNSPEC;
385 tags.setenv = UNSPEC;
386 tags.nopasswd = UNSPEC;
387 tags.log_input = UNSPEC;
388 tags.log_output = UNSPEC;
389 lbuf_append(lbuf, _("\nSudoers entry:\n"));
390 tq_foreach_fwd(&priv->cmndlist, cs) {
391 lbuf_append(lbuf, _(" RunAsUsers: "));
392 if (!tq_empty(&cs->runasuserlist)) {
393 tq_foreach_fwd(&cs->runasuserlist, m) {
394 if (m != tq_first(&cs->runasuserlist))
395 lbuf_append(lbuf, ", ");
396 print_member(lbuf, m->name, m->type, m->negated,
399 } else if (tq_empty(&cs->runasgrouplist)) {
400 lbuf_append(lbuf, "%s", def_runas_default);
402 lbuf_append(lbuf, "%s", pw->pw_name);
404 lbuf_append(lbuf, "\n");
405 if (!tq_empty(&cs->runasgrouplist)) {
406 lbuf_append(lbuf, _(" RunAsGroups: "));
407 tq_foreach_fwd(&cs->runasgrouplist, m) {
408 if (m != tq_first(&cs->runasgrouplist))
409 lbuf_append(lbuf, ", ");
410 print_member(lbuf, m->name, m->type, m->negated,
413 lbuf_append(lbuf, "\n");
415 lbuf_append(lbuf, _(" Commands:\n\t"));
416 sudo_file_append_cmnd(cs, &tags, lbuf);
417 lbuf_append(lbuf, "\n");
421 debug_return_int(nfound);
425 sudo_file_display_privs(struct sudo_nss *nss, struct passwd *pw,
430 debug_decl(sudo_file_display_priv, SUDO_DEBUG_NSS)
432 if (nss->handle == NULL)
435 tq_foreach_fwd(&userspecs, us) {
436 if (userlist_matches(pw, &us->users) != ALLOW)
440 nfound += sudo_file_display_priv_long(pw, us, lbuf);
442 nfound += sudo_file_display_priv_short(pw, us, lbuf);
445 debug_return_int(nfound);
449 * Display matching Defaults entries for the given user on this host.
452 sudo_file_display_defaults(struct sudo_nss *nss, struct passwd *pw,
458 debug_decl(sudo_file_display_defaults, SUDO_DEBUG_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 if (d->val != NULL) {
483 lbuf_append(lbuf, "%s%s%s", prefix, d->var,
484 d->op == '+' ? "+=" : d->op == '-' ? "-=" : "=");
485 if (strpbrk(d->val, " \t") != NULL) {
486 lbuf_append(lbuf, "\"");
487 lbuf_append_quoted(lbuf, "\"", "%s", d->val);
488 lbuf_append(lbuf, "\"");
490 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", d->val);
492 lbuf_append(lbuf, "%s%s%s", prefix,
493 d->op == false ? "!" : "", d->var);
498 debug_return_int(nfound);
502 * Display Defaults entries that are per-runas or per-command
505 sudo_file_display_bound_defaults(struct sudo_nss *nss, struct passwd *pw,
509 debug_decl(sudo_file_display_bound_defaults, SUDO_DEBUG_NSS)
511 /* XXX - should only print ones that match what the user can do. */
512 nfound += display_bound_defaults(DEFAULTS_RUNAS, lbuf);
513 nfound += display_bound_defaults(DEFAULTS_CMND, lbuf);
515 debug_return_int(nfound);
519 * Display Defaults entries of the given type.
522 display_bound_defaults(int dtype, struct lbuf *lbuf)
525 struct member *m, *binding = NULL;
527 int atype, nfound = 0;
528 debug_decl(display_bound_defaults, SUDO_DEBUG_NSS)
548 debug_return_int(-1);
550 tq_foreach_fwd(&defaults, d) {
551 if (d->type != dtype)
555 if (binding != tq_first(&d->binding)) {
556 binding = tq_first(&d->binding);
558 lbuf_append(lbuf, "\n");
559 lbuf_append(lbuf, " Defaults%s", dsep);
560 for (m = binding; m != NULL; m = m->next) {
562 lbuf_append(lbuf, ",");
563 print_member(lbuf, m->name, m->type, m->negated, atype);
564 lbuf_append(lbuf, " ");
567 lbuf_append(lbuf, ", ");
568 if (d->val != NULL) {
569 lbuf_append(lbuf, "%s%s%s", d->var, d->op == '+' ? "+=" :
570 d->op == '-' ? "-=" : "=", d->val);
572 lbuf_append(lbuf, "%s%s", d->op == false ? "!" : "", d->var);
575 debug_return_int(nfound);
579 sudo_file_display_cmnd(struct sudo_nss *nss, struct passwd *pw)
582 struct member *match;
583 struct privilege *priv;
586 int host_match, runas_match, cmnd_match;
587 debug_decl(sudo_file_display_cmnd, SUDO_DEBUG_NSS)
589 if (nss->handle == NULL)
593 tq_foreach_rev(&userspecs, us) {
594 if (userlist_matches(pw, &us->users) != ALLOW)
597 tq_foreach_rev(&us->privileges, priv) {
598 host_match = hostlist_matches(&priv->hostlist);
599 if (host_match != ALLOW)
601 tq_foreach_rev(&priv->cmndlist, cs) {
602 runas_match = runaslist_matches(&cs->runasuserlist,
603 &cs->runasgrouplist);
604 if (runas_match == ALLOW) {
605 cmnd_match = cmnd_matches(cs->cmnd);
606 if (cmnd_match != UNSPEC) {
607 match = host_match && runas_match ? cs->cmnd : NULL;
615 if (match != NULL && !match->negated) {
616 sudo_printf(SUDO_CONV_INFO_MSG, "%s%s%s\n",
617 safe_cmnd, user_args ? " " : "", user_args ? user_args : "");
621 debug_return_int(rval);
625 * Print the contents of a struct member to stdout
628 _print_member(struct lbuf *lbuf, char *name, int type, int negated,
633 struct sudo_command *c;
634 debug_decl(_print_member, SUDO_DEBUG_NSS)
638 lbuf_append(lbuf, "%sALL", negated ? "!" : "");
641 c = (struct sudo_command *) name;
643 lbuf_append(lbuf, "!");
644 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", c->cmnd);
646 lbuf_append(lbuf, " ");
647 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", c->args);
651 if ((a = alias_find(name, alias_type)) != NULL) {
652 tq_foreach_fwd(&a->members, m) {
653 if (m != tq_first(&a->members))
654 lbuf_append(lbuf, ", ");
655 _print_member(lbuf, m->name, m->type,
656 negated ? !m->negated : m->negated, alias_type);
662 lbuf_append(lbuf, "%s%s", negated ? "!" : "", name);
669 print_member(struct lbuf *lbuf, char *name, int type, int negated,
673 _print_member(lbuf, name, type, negated, alias_type);