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. */
99 init_parser(NULL, false);
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, false);
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 struct member *matching_user;
162 debug_decl(sudo_file_lookup, SUDO_DEBUG_NSS)
164 if (nss->handle == NULL)
165 debug_return_int(validated);
168 * Only check the actual command if pwflag is not set.
169 * It is set for the "validate", "list" and "kill" pseudo-commands.
170 * Always check the host and user.
174 enum def_tuple pwcheck;
176 pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
177 nopass = (pwcheck == all) ? true : false;
180 SET(validated, FLAG_NO_CHECK);
181 CLR(validated, FLAG_NO_USER);
182 CLR(validated, FLAG_NO_HOST);
184 tq_foreach_fwd(&userspecs, us) {
185 if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
187 tq_foreach_fwd(&us->privileges, priv) {
188 if (hostlist_matches(&priv->hostlist) != ALLOW)
190 tq_foreach_fwd(&priv->cmndlist, cs) {
191 /* Only check the command when listing another user. */
192 if (user_uid == 0 || list_pw == NULL ||
193 user_uid == list_pw->pw_uid ||
194 cmnd_matches(cs->cmnd) == ALLOW)
196 if ((pwcheck == any && cs->tags.nopasswd == true) ||
197 (pwcheck == all && cs->tags.nopasswd != true))
198 nopass = cs->tags.nopasswd;
202 if (match == ALLOW || user_uid == 0) {
203 /* User has an entry for this host. */
204 SET(validated, VALIDATE_OK);
205 } else if (match == DENY)
206 SET(validated, VALIDATE_NOT_OK);
207 if (pwcheck == always && def_authenticate)
208 SET(validated, FLAG_CHECK_USER);
209 else if (pwcheck == never || nopass == true)
210 def_authenticate = false;
211 debug_return_int(validated);
214 /* Need to be runas user while stat'ing things. */
215 set_perms(PERM_RUNAS);
218 tq_foreach_rev(&userspecs, us) {
219 if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
221 CLR(validated, FLAG_NO_USER);
222 tq_foreach_rev(&us->privileges, priv) {
223 host_match = hostlist_matches(&priv->hostlist);
224 if (host_match == ALLOW)
225 CLR(validated, FLAG_NO_HOST);
228 tq_foreach_rev(&priv->cmndlist, cs) {
229 matching_user = NULL;
230 runas_match = runaslist_matches(&cs->runasuserlist,
231 &cs->runasgrouplist, &matching_user, NULL);
232 if (runas_match == ALLOW) {
233 cmnd_match = cmnd_matches(cs->cmnd);
234 if (cmnd_match != UNSPEC) {
238 /* Set role and type if not specified on command line. */
239 if (user_role == NULL)
240 user_role = cs->role ? estrdup(cs->role) : def_role;
241 if (user_type == NULL)
242 user_type = cs->type ? estrdup(cs->type) : def_type;
243 #endif /* HAVE_SELINUX */
245 /* Set Solaris privilege sets */
246 if (runas_privs == NULL)
247 runas_privs = cs->privs ? estrdup(cs->privs) : def_privs;
248 if (runas_limitprivs == NULL)
249 runas_limitprivs = cs->limitprivs ? estrdup(cs->limitprivs) : def_limitprivs;
250 #endif /* HAVE_PRIV_SET */
252 * If user is running command as himself,
253 * set runas_pw = sudo_user.pw.
254 * XXX - hack, want more general solution
256 if (matching_user && matching_user->type == MYSELF) {
257 sudo_pw_delref(runas_pw);
258 sudo_pw_addref(sudo_user.pw);
259 runas_pw = sudo_user.pw;
268 if (match == ALLOW) {
269 SET(validated, VALIDATE_OK);
270 CLR(validated, VALIDATE_NOT_OK);
272 if (tags->nopasswd != UNSPEC)
273 def_authenticate = !tags->nopasswd;
274 if (tags->noexec != UNSPEC)
275 def_noexec = tags->noexec;
276 if (tags->setenv != UNSPEC)
277 def_setenv = tags->setenv;
278 if (tags->log_input != UNSPEC)
279 def_log_input = tags->log_input;
280 if (tags->log_output != UNSPEC)
281 def_log_output = tags->log_output;
283 } else if (match == DENY) {
284 SET(validated, VALIDATE_NOT_OK);
285 CLR(validated, VALIDATE_OK);
286 if (tags != NULL && tags->nopasswd != UNSPEC)
287 def_authenticate = !tags->nopasswd;
290 debug_return_int(validated);
293 #define TAG_CHANGED(t) \
294 (cs->tags.t != UNSPEC && cs->tags.t != IMPLIED && cs->tags.t != tags->t)
297 sudo_file_append_cmnd(struct cmndspec *cs, struct cmndtag *tags,
301 debug_decl(sudo_file_append_cmnd, SUDO_DEBUG_NSS)
305 lbuf_append(lbuf, "PRIVS=\"%s\" ", cs->privs);
307 lbuf_append(lbuf, "LIMITPRIVS=\"%s\" ", cs->limitprivs);
308 #endif /* HAVE_PRIV_SET */
311 lbuf_append(lbuf, "ROLE=%s ", cs->role);
313 lbuf_append(lbuf, "TYPE=%s ", cs->type);
314 #endif /* HAVE_SELINUX */
315 if (TAG_CHANGED(setenv)) {
316 lbuf_append(lbuf, cs->tags.setenv ? "SETENV: " : "NOSETENV: ");
317 tags->setenv = cs->tags.setenv;
319 if (TAG_CHANGED(noexec)) {
320 lbuf_append(lbuf, cs->tags.noexec ? "NOEXEC: " : "EXEC: ");
321 tags->noexec = cs->tags.noexec;
323 if (TAG_CHANGED(nopasswd)) {
324 lbuf_append(lbuf, cs->tags.nopasswd ? "NOPASSWD: " : "PASSWD: ");
325 tags->nopasswd = cs->tags.nopasswd;
327 if (TAG_CHANGED(log_input)) {
328 lbuf_append(lbuf, cs->tags.log_input ? "LOG_INPUT: " : "NOLOG_INPUT: ");
329 tags->log_input = cs->tags.log_input;
331 if (TAG_CHANGED(log_output)) {
332 lbuf_append(lbuf, cs->tags.log_output ? "LOG_OUTPUT: " : "NOLOG_OUTPUT: ");
333 tags->log_output = cs->tags.log_output;
336 print_member(lbuf, m->name, m->type, m->negated,
342 sudo_file_display_priv_short(struct passwd *pw, struct userspec *us,
347 struct privilege *priv;
350 debug_decl(sudo_file_display_priv_short, SUDO_DEBUG_NSS)
352 tq_foreach_fwd(&us->privileges, priv) {
353 if (hostlist_matches(&priv->hostlist) != ALLOW)
355 tags.noexec = UNSPEC;
356 tags.setenv = UNSPEC;
357 tags.nopasswd = UNSPEC;
358 tags.log_input = UNSPEC;
359 tags.log_output = UNSPEC;
360 lbuf_append(lbuf, " ");
361 tq_foreach_fwd(&priv->cmndlist, cs) {
362 if (cs != tq_first(&priv->cmndlist))
363 lbuf_append(lbuf, ", ");
364 lbuf_append(lbuf, "(");
365 if (!tq_empty(&cs->runasuserlist)) {
366 tq_foreach_fwd(&cs->runasuserlist, m) {
367 if (m != tq_first(&cs->runasuserlist))
368 lbuf_append(lbuf, ", ");
369 print_member(lbuf, m->name, m->type, m->negated,
372 } else if (tq_empty(&cs->runasgrouplist)) {
373 lbuf_append(lbuf, "%s", def_runas_default);
375 lbuf_append(lbuf, "%s", pw->pw_name);
377 if (!tq_empty(&cs->runasgrouplist)) {
378 lbuf_append(lbuf, " : ");
379 tq_foreach_fwd(&cs->runasgrouplist, m) {
380 if (m != tq_first(&cs->runasgrouplist))
381 lbuf_append(lbuf, ", ");
382 print_member(lbuf, m->name, m->type, m->negated,
386 lbuf_append(lbuf, ") ");
387 sudo_file_append_cmnd(cs, &tags, lbuf);
390 lbuf_append(lbuf, "\n");
392 debug_return_int(nfound);
396 sudo_file_display_priv_long(struct passwd *pw, struct userspec *us,
401 struct privilege *priv;
404 debug_decl(sudo_file_display_priv_long, SUDO_DEBUG_NSS)
406 tq_foreach_fwd(&us->privileges, priv) {
407 if (hostlist_matches(&priv->hostlist) != ALLOW)
409 tags.noexec = UNSPEC;
410 tags.setenv = UNSPEC;
411 tags.nopasswd = UNSPEC;
412 tags.log_input = UNSPEC;
413 tags.log_output = UNSPEC;
414 lbuf_append(lbuf, _("\nSudoers entry:\n"));
415 tq_foreach_fwd(&priv->cmndlist, cs) {
416 lbuf_append(lbuf, _(" RunAsUsers: "));
417 if (!tq_empty(&cs->runasuserlist)) {
418 tq_foreach_fwd(&cs->runasuserlist, m) {
419 if (m != tq_first(&cs->runasuserlist))
420 lbuf_append(lbuf, ", ");
421 print_member(lbuf, m->name, m->type, m->negated,
424 } else if (tq_empty(&cs->runasgrouplist)) {
425 lbuf_append(lbuf, "%s", def_runas_default);
427 lbuf_append(lbuf, "%s", pw->pw_name);
429 lbuf_append(lbuf, "\n");
430 if (!tq_empty(&cs->runasgrouplist)) {
431 lbuf_append(lbuf, _(" RunAsGroups: "));
432 tq_foreach_fwd(&cs->runasgrouplist, m) {
433 if (m != tq_first(&cs->runasgrouplist))
434 lbuf_append(lbuf, ", ");
435 print_member(lbuf, m->name, m->type, m->negated,
438 lbuf_append(lbuf, "\n");
440 lbuf_append(lbuf, _(" Commands:\n\t"));
441 sudo_file_append_cmnd(cs, &tags, lbuf);
442 lbuf_append(lbuf, "\n");
446 debug_return_int(nfound);
450 sudo_file_display_privs(struct sudo_nss *nss, struct passwd *pw,
455 debug_decl(sudo_file_display_priv, SUDO_DEBUG_NSS)
457 if (nss->handle == NULL)
460 tq_foreach_fwd(&userspecs, us) {
461 if (userlist_matches(pw, &us->users) != ALLOW)
465 nfound += sudo_file_display_priv_long(pw, us, lbuf);
467 nfound += sudo_file_display_priv_short(pw, us, lbuf);
470 debug_return_int(nfound);
474 * Display matching Defaults entries for the given user on this host.
477 sudo_file_display_defaults(struct sudo_nss *nss, struct passwd *pw,
483 debug_decl(sudo_file_display_defaults, SUDO_DEBUG_NSS)
485 if (nss->handle == NULL)
488 if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
493 tq_foreach_fwd(&defaults, d) {
496 if (hostlist_matches(&d->binding) != ALLOW)
500 if (userlist_matches(pw, &d->binding) != ALLOW)
507 if (d->val != NULL) {
508 lbuf_append(lbuf, "%s%s%s", prefix, d->var,
509 d->op == '+' ? "+=" : d->op == '-' ? "-=" : "=");
510 if (strpbrk(d->val, " \t") != NULL) {
511 lbuf_append(lbuf, "\"");
512 lbuf_append_quoted(lbuf, "\"", "%s", d->val);
513 lbuf_append(lbuf, "\"");
515 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", d->val);
517 lbuf_append(lbuf, "%s%s%s", prefix,
518 d->op == false ? "!" : "", d->var);
523 debug_return_int(nfound);
527 * Display Defaults entries that are per-runas or per-command
530 sudo_file_display_bound_defaults(struct sudo_nss *nss, struct passwd *pw,
534 debug_decl(sudo_file_display_bound_defaults, SUDO_DEBUG_NSS)
536 /* XXX - should only print ones that match what the user can do. */
537 nfound += display_bound_defaults(DEFAULTS_RUNAS, lbuf);
538 nfound += display_bound_defaults(DEFAULTS_CMND, lbuf);
540 debug_return_int(nfound);
544 * Display Defaults entries of the given type.
547 display_bound_defaults(int dtype, struct lbuf *lbuf)
550 struct member *m, *binding = NULL;
552 int atype, nfound = 0;
553 debug_decl(display_bound_defaults, SUDO_DEBUG_NSS)
573 debug_return_int(-1);
575 tq_foreach_fwd(&defaults, d) {
576 if (d->type != dtype)
580 if (binding != tq_first(&d->binding)) {
581 binding = tq_first(&d->binding);
583 lbuf_append(lbuf, "\n");
584 lbuf_append(lbuf, " Defaults%s", dsep);
585 for (m = binding; m != NULL; m = m->next) {
587 lbuf_append(lbuf, ",");
588 print_member(lbuf, m->name, m->type, m->negated, atype);
589 lbuf_append(lbuf, " ");
592 lbuf_append(lbuf, ", ");
593 if (d->val != NULL) {
594 lbuf_append(lbuf, "%s%s%s", d->var, d->op == '+' ? "+=" :
595 d->op == '-' ? "-=" : "=", d->val);
597 lbuf_append(lbuf, "%s%s", d->op == false ? "!" : "", d->var);
600 debug_return_int(nfound);
604 sudo_file_display_cmnd(struct sudo_nss *nss, struct passwd *pw)
607 struct member *match;
608 struct privilege *priv;
611 int host_match, runas_match, cmnd_match;
612 debug_decl(sudo_file_display_cmnd, SUDO_DEBUG_NSS)
614 if (nss->handle == NULL)
618 tq_foreach_rev(&userspecs, us) {
619 if (userlist_matches(pw, &us->users) != ALLOW)
622 tq_foreach_rev(&us->privileges, priv) {
623 host_match = hostlist_matches(&priv->hostlist);
624 if (host_match != ALLOW)
626 tq_foreach_rev(&priv->cmndlist, cs) {
627 runas_match = runaslist_matches(&cs->runasuserlist,
628 &cs->runasgrouplist, NULL, NULL);
629 if (runas_match == ALLOW) {
630 cmnd_match = cmnd_matches(cs->cmnd);
631 if (cmnd_match != UNSPEC) {
632 match = host_match && runas_match ? cs->cmnd : NULL;
640 if (match != NULL && !match->negated) {
641 sudo_printf(SUDO_CONV_INFO_MSG, "%s%s%s\n",
642 safe_cmnd, user_args ? " " : "", user_args ? user_args : "");
646 debug_return_int(rval);
650 * Print the contents of a struct member to stdout
653 _print_member(struct lbuf *lbuf, char *name, int type, int negated,
658 struct sudo_command *c;
659 debug_decl(_print_member, SUDO_DEBUG_NSS)
663 lbuf_append(lbuf, "%sALL", negated ? "!" : "");
666 lbuf_append(lbuf, "%s%s", negated ? "!" : "", user_name);
669 c = (struct sudo_command *) name;
671 lbuf_append(lbuf, "!");
672 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", c->cmnd);
674 lbuf_append(lbuf, " ");
675 lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", c->args);
679 if ((a = alias_find(name, alias_type)) != NULL) {
680 tq_foreach_fwd(&a->members, m) {
681 if (m != tq_first(&a->members))
682 lbuf_append(lbuf, ", ");
683 _print_member(lbuf, m->name, m->type,
684 negated ? !m->negated : m->negated, alias_type);
690 lbuf_append(lbuf, "%s%s", negated ? "!" : "", name);
697 print_member(struct lbuf *lbuf, char *name, int type, int negated,
701 _print_member(lbuf, name, type, negated, alias_type);