+++ /dev/null
-/*
- * Copyright (c) 2004-2005, 2007-2010 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <config.h>
-
-#include <sys/types.h>
-#include <sys/param.h>
-#include <stdio.h>
-#ifdef STDC_HEADERS
-# include <stdlib.h>
-# include <stddef.h>
-#else
-# ifdef HAVE_STDLIB_H
-# include <stdlib.h>
-# endif
-#endif /* STDC_HEADERS */
-#ifdef HAVE_STRING_H
-# include <string.h>
-#endif /* HAVE_STRING_H */
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif /* HAVE_STRINGS_H */
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-#include <ctype.h>
-#include <pwd.h>
-#include <grp.h>
-
-#include "sudo.h"
-#include "parse.h"
-#include "lbuf.h"
-#include <gram.h>
-
-/* Characters that must be quoted in sudoers */
-#define SUDOERS_QUOTED ":\\,=#\""
-
-/* sudoers nsswitch routines */
-struct sudo_nss sudo_nss_file = {
- &sudo_nss_file,
- NULL,
- sudo_file_open,
- sudo_file_close,
- sudo_file_parse,
- sudo_file_setdefs,
- sudo_file_lookup,
- sudo_file_display_cmnd,
- sudo_file_display_defaults,
- sudo_file_display_bound_defaults,
- sudo_file_display_privs
-};
-
-/*
- * Parser externs.
- */
-extern FILE *yyin;
-extern char *errorfile;
-extern int errorlineno, parse_error;
-
-/*
- * Local prototypes.
- */
-static void print_member __P((struct lbuf *, char *, int, int, int));
-static int display_bound_defaults __P((int, struct lbuf *));
-
-int
-sudo_file_open(nss)
- struct sudo_nss *nss;
-{
- if (def_ignore_local_sudoers)
- return(-1);
- nss->handle = open_sudoers(_PATH_SUDOERS, FALSE, NULL);
- return(nss->handle ? 0 : -1);
-}
-
-int
-sudo_file_close(nss)
- struct sudo_nss *nss;
-{
- /* Free parser data structures and close sudoers file. */
- init_parser(NULL, 0);
- if (nss->handle != NULL) {
- fclose(nss->handle);
- nss->handle = NULL;
- yyin = NULL;
- }
- return(0);
-}
-
-/*
- * Parse the specified sudoers file.
- */
-int
-sudo_file_parse(nss)
- struct sudo_nss *nss;
-{
- if (nss->handle == NULL)
- return(-1);
-
- init_parser(_PATH_SUDOERS, 0);
- yyin = nss->handle;
- if (yyparse() != 0 || parse_error) {
- log_error(NO_EXIT, "parse error in %s near line %d",
- errorfile, errorlineno);
- return(-1);
- }
- return(0);
-}
-
-/*
- * Wrapper around update_defaults() for nsswitch code.
- */
-int
-sudo_file_setdefs(nss)
- struct sudo_nss *nss;
-{
- if (nss->handle == NULL)
- return(-1);
-
- if (!update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER))
- return(-1);
- return(0);
-}
-
-/*
- * Look up the user in the parsed sudoers file and check to see if they are
- * allowed to run the specified command on this host as the target user.
- */
-int
-sudo_file_lookup(nss, validated, pwflag)
- struct sudo_nss *nss;
- int validated;
- int pwflag;
-{
- int match, host_match, runas_match, cmnd_match;
- struct cmndspec *cs;
- struct cmndtag *tags = NULL;
- struct privilege *priv;
- struct userspec *us;
-
- if (nss->handle == NULL)
- return(validated);
-
- /*
- * Only check the actual command if pwflag is not set.
- * It is set for the "validate", "list" and "kill" pseudo-commands.
- * Always check the host and user.
- */
- if (pwflag) {
- int nopass;
- enum def_tupple pwcheck;
-
- pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
- nopass = (pwcheck == all) ? TRUE : FALSE;
-
- if (list_pw == NULL)
- SET(validated, FLAG_NO_CHECK);
- CLR(validated, FLAG_NO_USER);
- CLR(validated, FLAG_NO_HOST);
- match = DENY;
- tq_foreach_fwd(&userspecs, us) {
- if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
- continue;
- tq_foreach_fwd(&us->privileges, priv) {
- if (hostlist_matches(&priv->hostlist) != ALLOW)
- continue;
- tq_foreach_fwd(&priv->cmndlist, cs) {
- /* Only check the command when listing another user. */
- if (user_uid == 0 || list_pw == NULL ||
- user_uid == list_pw->pw_uid ||
- cmnd_matches(cs->cmnd) == ALLOW)
- match = ALLOW;
- if ((pwcheck == any && cs->tags.nopasswd == TRUE) ||
- (pwcheck == all && cs->tags.nopasswd != TRUE))
- nopass = cs->tags.nopasswd;
- }
- }
- }
- if (match == ALLOW || user_uid == 0) {
- /* User has an entry for this host. */
- SET(validated, VALIDATE_OK);
- } else if (match == DENY)
- SET(validated, VALIDATE_NOT_OK);
- if (pwcheck == always && def_authenticate)
- SET(validated, FLAG_CHECK_USER);
- else if (pwcheck == never || nopass == TRUE)
- def_authenticate = FALSE;
- return(validated);
- }
-
- /* Need to be runas user while stat'ing things. */
- set_perms(PERM_RUNAS);
-
- match = UNSPEC;
- tq_foreach_rev(&userspecs, us) {
- if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
- continue;
- CLR(validated, FLAG_NO_USER);
- tq_foreach_rev(&us->privileges, priv) {
- host_match = hostlist_matches(&priv->hostlist);
- if (host_match == ALLOW)
- CLR(validated, FLAG_NO_HOST);
- else
- continue;
- tq_foreach_rev(&priv->cmndlist, cs) {
- runas_match = runaslist_matches(&cs->runasuserlist,
- &cs->runasgrouplist);
- if (runas_match == ALLOW) {
- cmnd_match = cmnd_matches(cs->cmnd);
- if (cmnd_match != UNSPEC) {
- match = cmnd_match;
- tags = &cs->tags;
-#ifdef HAVE_SELINUX
- /* Set role and type if not specified on command line. */
- if (user_role == NULL)
- user_role = cs->role ? estrdup(cs->role) : def_role;
- if (user_type == NULL)
- user_type = cs->type ? estrdup(cs->type) : def_type;
-#endif /* HAVE_SELINUX */
- goto matched2;
- }
- }
- }
- }
- }
- matched2:
- if (match == ALLOW) {
- SET(validated, VALIDATE_OK);
- CLR(validated, VALIDATE_NOT_OK);
- if (tags != NULL) {
- if (tags->nopasswd != UNSPEC)
- def_authenticate = !tags->nopasswd;
- if (tags->noexec != UNSPEC)
- def_noexec = tags->noexec;
- if (tags->setenv != UNSPEC)
- def_setenv = tags->setenv;
- if (tags->log_input != UNSPEC)
- def_log_input = tags->log_input;
- if (tags->log_output != UNSPEC)
- def_log_output = tags->log_output;
- }
- } else if (match == DENY) {
- SET(validated, VALIDATE_NOT_OK);
- CLR(validated, VALIDATE_OK);
- }
- set_perms(PERM_ROOT);
- return(validated);
-}
-
-#define TAG_CHANGED(t) \
- (cs->tags.t != UNSPEC && cs->tags.t != IMPLIED && cs->tags.t != tags->t)
-
-static void
-sudo_file_append_cmnd(cs, tags, lbuf)
- struct cmndspec *cs;
- struct cmndtag *tags;
- struct lbuf *lbuf;
-{
- struct member *m;
-
-#ifdef HAVE_SELINUX
- if (cs->role)
- lbuf_append(lbuf, "ROLE=", cs->role, " ", NULL);
- if (cs->type)
- lbuf_append(lbuf, "TYPE=", cs->type, " ", NULL);
-#endif /* HAVE_SELINUX */
- if (TAG_CHANGED(setenv)) {
- lbuf_append(lbuf, cs->tags.setenv ? "SETENV: " :
- "NOSETENV: ", NULL);
- tags->setenv = cs->tags.setenv;
- }
- if (TAG_CHANGED(noexec)) {
- lbuf_append(lbuf, cs->tags.noexec ? "NOEXEC: " :
- "EXEC: ", NULL);
- tags->noexec = cs->tags.noexec;
- }
- if (TAG_CHANGED(nopasswd)) {
- lbuf_append(lbuf, cs->tags.nopasswd ? "NOPASSWD: " :
- "PASSWD: ", NULL);
- tags->nopasswd = cs->tags.nopasswd;
- }
- if (TAG_CHANGED(log_input)) {
- lbuf_append(lbuf, cs->tags.log_input ? "LOG_INPUT: " :
- "NOLOG_INPUT: ", NULL);
- tags->log_input = cs->tags.log_input;
- }
- if (TAG_CHANGED(log_output)) {
- lbuf_append(lbuf, cs->tags.log_output ? "LOG_OUTPUT: " :
- "NOLOG_OUTPUT: ", NULL);
- tags->log_output = cs->tags.log_output;
- }
- m = cs->cmnd;
- print_member(lbuf, m->name, m->type, m->negated,
- CMNDALIAS);
-}
-
-static int
-sudo_file_display_priv_short(pw, us, lbuf)
- struct passwd *pw;
- struct userspec *us;
- struct lbuf *lbuf;
-{
- struct cmndspec *cs;
- struct member *m;
- struct privilege *priv;
- struct cmndtag tags;
- int nfound = 0;
-
- tq_foreach_fwd(&us->privileges, priv) {
- if (hostlist_matches(&priv->hostlist) != ALLOW)
- continue;
- tags.noexec = UNSPEC;
- tags.setenv = UNSPEC;
- tags.nopasswd = UNSPEC;
- tags.log_input = UNSPEC;
- tags.log_output = UNSPEC;
- lbuf_append(lbuf, " ", NULL);
- tq_foreach_fwd(&priv->cmndlist, cs) {
- if (cs != tq_first(&priv->cmndlist))
- lbuf_append(lbuf, ", ", NULL);
- lbuf_append(lbuf, "(", NULL);
- if (!tq_empty(&cs->runasuserlist)) {
- tq_foreach_fwd(&cs->runasuserlist, m) {
- if (m != tq_first(&cs->runasuserlist))
- lbuf_append(lbuf, ", ", NULL);
- print_member(lbuf, m->name, m->type, m->negated,
- RUNASALIAS);
- }
- } else if (tq_empty(&cs->runasgrouplist)) {
- lbuf_append(lbuf, def_runas_default, NULL);
- } else {
- lbuf_append(lbuf, pw->pw_name, NULL);
- }
- if (!tq_empty(&cs->runasgrouplist)) {
- lbuf_append(lbuf, " : ", NULL);
- tq_foreach_fwd(&cs->runasgrouplist, m) {
- if (m != tq_first(&cs->runasgrouplist))
- lbuf_append(lbuf, ", ", NULL);
- print_member(lbuf, m->name, m->type, m->negated,
- RUNASALIAS);
- }
- }
- lbuf_append(lbuf, ") ", NULL);
- sudo_file_append_cmnd(cs, &tags, lbuf);
- nfound++;
- }
- lbuf_append(lbuf, "\n", NULL);
- }
- return(nfound);
-}
-
-static int
-sudo_file_display_priv_long(pw, us, lbuf)
- struct passwd *pw;
- struct userspec *us;
- struct lbuf *lbuf;
-{
- struct cmndspec *cs;
- struct member *m;
- struct privilege *priv;
- struct cmndtag tags;
- int nfound = 0;
-
- tq_foreach_fwd(&us->privileges, priv) {
- if (hostlist_matches(&priv->hostlist) != ALLOW)
- continue;
- tags.noexec = UNSPEC;
- tags.setenv = UNSPEC;
- tags.nopasswd = UNSPEC;
- tags.log_input = UNSPEC;
- tags.log_output = UNSPEC;
- lbuf_append(lbuf, "\nSudoers entry:\n", NULL);
- tq_foreach_fwd(&priv->cmndlist, cs) {
- lbuf_append(lbuf, " RunAsUsers: ", NULL);
- if (!tq_empty(&cs->runasuserlist)) {
- tq_foreach_fwd(&cs->runasuserlist, m) {
- if (m != tq_first(&cs->runasuserlist))
- lbuf_append(lbuf, ", ", NULL);
- print_member(lbuf, m->name, m->type, m->negated,
- RUNASALIAS);
- }
- } else if (tq_empty(&cs->runasgrouplist)) {
- lbuf_append(lbuf, def_runas_default, NULL);
- } else {
- lbuf_append(lbuf, pw->pw_name, NULL);
- }
- lbuf_append(lbuf, "\n", NULL);
- if (!tq_empty(&cs->runasgrouplist)) {
- lbuf_append(lbuf, " RunAsGroups: ", NULL);
- tq_foreach_fwd(&cs->runasgrouplist, m) {
- if (m != tq_first(&cs->runasgrouplist))
- lbuf_append(lbuf, ", ", NULL);
- print_member(lbuf, m->name, m->type, m->negated,
- RUNASALIAS);
- }
- lbuf_append(lbuf, "\n", NULL);
- }
- lbuf_append(lbuf, " Commands:\n\t", NULL);
- sudo_file_append_cmnd(cs, &tags, lbuf);
- lbuf_append(lbuf, "\n", NULL);
- nfound++;
- }
- }
- return(nfound);
-}
-
-int
-sudo_file_display_privs(nss, pw, lbuf)
- struct sudo_nss *nss;
- struct passwd *pw;
- struct lbuf *lbuf;
-{
- struct userspec *us;
- int nfound = 0;
-
- if (nss->handle == NULL)
- goto done;
-
- tq_foreach_fwd(&userspecs, us) {
- if (userlist_matches(pw, &us->users) != ALLOW)
- continue;
-
- if (long_list)
- nfound += sudo_file_display_priv_long(pw, us, lbuf);
- else
- nfound += sudo_file_display_priv_short(pw, us, lbuf);
- }
-done:
- return(nfound);
-}
-
-/*
- * Display matching Defaults entries for the given user on this host.
- */
-int
-sudo_file_display_defaults(nss, pw, lbuf)
- struct sudo_nss *nss;
- struct passwd *pw;
- struct lbuf *lbuf;
-{
- struct defaults *d;
- char *prefix;
- int nfound = 0;
-
- if (nss->handle == NULL)
- goto done;
-
- if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
- prefix = " ";
- else
- prefix = ", ";
-
- tq_foreach_fwd(&defaults, d) {
- switch (d->type) {
- case DEFAULTS_HOST:
- if (hostlist_matches(&d->binding) != ALLOW)
- continue;
- break;
- case DEFAULTS_USER:
- if (userlist_matches(pw, &d->binding) != ALLOW)
- continue;
- break;
- case DEFAULTS_RUNAS:
- case DEFAULTS_CMND:
- continue;
- }
- lbuf_append(lbuf, prefix, NULL);
- if (d->val != NULL) {
- lbuf_append(lbuf, d->var, d->op == '+' ? "+=" :
- d->op == '-' ? "-=" : "=", NULL);
- if (strpbrk(d->val, " \t") != NULL) {
- lbuf_append(lbuf, "\"", NULL);
- lbuf_append_quoted(lbuf, "\"", d->val, NULL);
- lbuf_append(lbuf, "\"", NULL);
- } else
- lbuf_append_quoted(lbuf, SUDOERS_QUOTED, d->val, NULL);
- } else
- lbuf_append(lbuf, d->op == FALSE ? "!" : "", d->var, NULL);
- prefix = ", ";
- nfound++;
- }
-done:
- return(nfound);
-}
-
-/*
- * Display Defaults entries that are per-runas or per-command
- */
-int
-sudo_file_display_bound_defaults(nss, pw, lbuf)
- struct sudo_nss *nss;
- struct passwd *pw;
- struct lbuf *lbuf;
-{
- int nfound = 0;
-
- /* XXX - should only print ones that match what the user can do. */
- nfound += display_bound_defaults(DEFAULTS_RUNAS, lbuf);
- nfound += display_bound_defaults(DEFAULTS_CMND, lbuf);
-
- return(nfound);
-}
-
-/*
- * Display Defaults entries of the given type.
- */
-static int
-display_bound_defaults(dtype, lbuf)
- int dtype;
- struct lbuf *lbuf;
-{
- struct defaults *d;
- struct member *m, *binding = NULL;
- char *dname, *dsep;
- int atype, nfound = 0;
-
- switch (dtype) {
- case DEFAULTS_HOST:
- atype = HOSTALIAS;
- dname = "host";
- dsep = "@";
- break;
- case DEFAULTS_USER:
- atype = USERALIAS;
- dname = "user";
- dsep = ":";
- break;
- case DEFAULTS_RUNAS:
- atype = RUNASALIAS;
- dname = "runas";
- dsep = ">";
- break;
- case DEFAULTS_CMND:
- atype = CMNDALIAS;
- dname = "cmnd";
- dsep = "!";
- break;
- default:
- return(-1);
- }
- /* printf("Per-%s Defaults entries:\n", dname); */
- tq_foreach_fwd(&defaults, d) {
- if (d->type != dtype)
- continue;
-
- nfound++;
- if (binding != tq_first(&d->binding)) {
- binding = tq_first(&d->binding);
- if (nfound != 1)
- lbuf_append(lbuf, "\n", NULL);
- lbuf_append(lbuf, " Defaults", dsep, NULL);
- for (m = binding; m != NULL; m = m->next) {
- if (m != binding)
- lbuf_append(lbuf, ",", NULL);
- print_member(lbuf, m->name, m->type, m->negated, atype);
- lbuf_append(lbuf, " ", NULL);
- }
- } else
- lbuf_append(lbuf, ", ", NULL);
- if (d->val != NULL) {
- lbuf_append(lbuf, d->var, d->op == '+' ? "+=" :
- d->op == '-' ? "-=" : "=", d->val, NULL);
- } else
- lbuf_append(lbuf, d->op == FALSE ? "!" : "", d->var, NULL);
- }
-
- return(nfound);
-}
-
-int
-sudo_file_display_cmnd(nss, pw)
- struct sudo_nss *nss;
- struct passwd *pw;
-{
- struct cmndspec *cs;
- struct member *match;
- struct privilege *priv;
- struct userspec *us;
- int rval = 1;
- int host_match, runas_match, cmnd_match;
-
- if (nss->handle == NULL)
- goto done;
-
- match = NULL;
- tq_foreach_rev(&userspecs, us) {
- if (userlist_matches(pw, &us->users) != ALLOW)
- continue;
-
- tq_foreach_rev(&us->privileges, priv) {
- host_match = hostlist_matches(&priv->hostlist);
- if (host_match != ALLOW)
- continue;
- tq_foreach_rev(&priv->cmndlist, cs) {
- runas_match = runaslist_matches(&cs->runasuserlist,
- &cs->runasgrouplist);
- if (runas_match == ALLOW) {
- cmnd_match = cmnd_matches(cs->cmnd);
- if (cmnd_match != UNSPEC) {
- match = host_match && runas_match ?
- cs->cmnd : NULL;
- goto matched;
- }
- }
- }
- }
- }
- matched:
- if (match != NULL && !match->negated) {
- printf("%s%s%s\n", safe_cmnd, user_args ? " " : "",
- user_args ? user_args : "");
- rval = 0;
- }
-done:
- return(rval);
-}
-
-/*
- * Print the contents of a struct member to stdout
- */
-static void
-_print_member(lbuf, name, type, negated, alias_type)
- struct lbuf *lbuf;
- char *name;
- int type, negated, alias_type;
-{
- struct alias *a;
- struct member *m;
- struct sudo_command *c;
-
- switch (type) {
- case ALL:
- lbuf_append(lbuf, negated ? "!ALL" : "ALL", NULL);
- break;
- case COMMAND:
- c = (struct sudo_command *) name;
- if (negated)
- lbuf_append(lbuf, "!", NULL);
- lbuf_append_quoted(lbuf, SUDOERS_QUOTED, c->cmnd, NULL);
- if (c->args) {
- lbuf_append(lbuf, " ", NULL);
- lbuf_append_quoted(lbuf, SUDOERS_QUOTED, c->args, NULL);
- }
- break;
- case ALIAS:
- if ((a = alias_find(name, alias_type)) != NULL) {
- tq_foreach_fwd(&a->members, m) {
- if (m != tq_first(&a->members))
- lbuf_append(lbuf, ", ", NULL);
- _print_member(lbuf, m->name, m->type,
- negated ? !m->negated : m->negated, alias_type);
- }
- break;
- }
- /* FALLTHROUGH */
- default:
- lbuf_append(lbuf, negated ? "!" : "", name, NULL);
- break;
- }
-}
-
-static void
-print_member(lbuf, name, type, negated, alias_type)
- struct lbuf *lbuf;
- char *name;
- int type, negated, alias_type;
-{
- alias_seqno++;
- _print_member(lbuf, name, type, negated, alias_type);
-}