From: Todd C. Miller Date: Sun, 2 Jan 2005 00:31:08 +0000 (+0000) Subject: Print Defaults info in "sudo -l" output and wrap lines based on the X-Git-Tag: SUDO_1_7_0~759 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ed4df3a94834926b1c23eebabc68f1e0c9f33f26;p=sudo Print Defaults info in "sudo -l" output and wrap lines based on the terminal width. --- diff --git a/parse.c b/parse.c index c0ff6334e..9a70cd6f2 100644 --- a/parse.c +++ b/parse.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 1998-2004 Todd C. Miller + * Copyright (c) 2004, 2005 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,16 +14,13 @@ * 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. - * - * Sponsored in part by the Defense Advanced Research Projects - * Agency (DARPA) and Air Force Research Laboratory, Air Force - * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ #include #include #include +#include #include #ifdef STDC_HEADERS # include @@ -58,11 +55,25 @@ static const char rcsid[] = "$Sudo$"; * Parsed sudoers info. */ extern struct userspec *userspecs; +extern struct defaults *defaults; /* * Local prototypes. */ static void print_member __P((char *, int, int, int)); +static void display_defaults __P((struct passwd *)); +static void display_bound_defaults __P((int)); +static int get_ttycols __P((void)); +static void print_wrap __P((int, int, int, ...)); + +#define print_def(a) print_wrap(4, 0, 1, a); +#define print_def2(a, b) print_wrap(4, 0, 2, a, b); +#define print_def3(a, b, c) print_wrap(4, 0, 3, a, b, c); +#define print_def4(a, b, c, d) print_wrap(4, 0, 4, a, b, c, d); +#define print_priv(a) print_wrap(8, '\\', 1, a); +#define print_priv2(a, b) print_wrap(8, '\\', 2, a, b); +#define print_priv3(a, b, c) print_wrap(8, '\\', 3, a, b, c); +#define print_priv4(a, b, c, d) print_wrap(8, '\\', 4, a, b, c, d); /* * Parse the specified sudoers file. @@ -197,12 +208,15 @@ display_privs(pw) struct passwd *pw; { struct cmndspec *cs; - struct member *m, *runas; + struct member *m; struct privilege *priv; struct userspec *us; + struct cmndtag tags; + + display_defaults(pw); - printf("User %s may run the following commands on this host:\n", - pw->pw_name); + print_priv4("\n", "User ", pw->pw_name, + " may run the following commands on this host:\n"); for (us = userspecs; us != NULL; us = us->next) { if (user_matches(pw, us->user) != TRUE || @@ -210,32 +224,149 @@ display_privs(pw) continue; for (priv = us->privileges; priv != NULL; priv = priv->next) { - runas = NULL; + tags.monitor = def_monitor; + tags.noexec = def_noexec; + tags.nopasswd = !def_authenticate; for (cs = priv->cmndlist; cs != NULL; cs = cs->next) { - if (cs->runaslist != NULL) - runas = cs->runaslist; - fputs(" ", stdout); - if (runas != NULL) { - fputs("(", stdout); - for (m = runas; m != NULL; m = m->next) { - if (m != runas) - fputs(", ", stdout); + if (cs != priv->cmndlist) + print_priv(", "); + if (cs->runaslist != NULL) { + print_priv(" ("); + for (m = cs->runaslist; m != NULL; m = m->next) { + if (m != cs->runaslist) + print_priv(", "); print_member(m->name, m->type, m->negated, RUNASALIAS); } - fputs(") ", stdout); + print_priv(") "); + } + if (cs->tags.monitor != UNSPEC && cs->tags.monitor != tags.monitor) { + print_priv(cs->tags.monitor ? "MONITOR: " : "NOMONITOR: "); + tags.monitor = cs->tags.monitor; + } + if (cs->tags.noexec != UNSPEC && cs->tags.noexec != tags.noexec) { + print_priv(cs->tags.monitor ? "EXEC: " : "NOEXEC: "); + tags.noexec = cs->tags.noexec; + } + if (cs->tags.nopasswd != UNSPEC && cs->tags.nopasswd != tags.nopasswd) { + print_priv(cs->tags.monitor ? "PASSWD: " : "NOPASSWD: "); + tags.nopasswd = cs->tags.nopasswd; } - if (cs->tags.monitor != UNSPEC && cs->tags.monitor != def_monitor) - printf("%sMONITOR: ", cs->tags.monitor ? "" : "NO"); - if (cs->tags.noexec != UNSPEC && cs->tags.noexec != def_noexec) - printf("%sEXEC: ", cs->tags.noexec ? "NO" : ""); - if (cs->tags.nopasswd != UNSPEC && cs->tags.nopasswd != !def_authenticate) - printf("%sPASSWD: ", cs->tags.nopasswd ? "NO" : ""); m = cs->cmnd; print_member(m->name, m->type, m->negated, CMNDALIAS); - putchar('\n'); } + print_priv("\n"); + } + } +} + +/* + * Display matching Defaults entries for the given user on this host. + */ +static void +display_defaults(pw) + struct passwd *pw; +{ + struct defaults *d; + char opstr[2], *prefix; + int per_runas = 0, per_cmnd = 0; + + opstr[1] = '\0'; + print_def3("Matching Defaults entries for ", pw->pw_name, " on this host:\n"); + print_def(" "); + for (d = defaults, prefix = NULL; d != NULL; d = d->next) { + switch (d->type) { + case DEFAULTS_HOST: + if (host_matches(d->binding) != TRUE) + continue; + break; + case DEFAULTS_USER: + if (user_matches(pw, d->binding) != TRUE) + continue; + break; + case DEFAULTS_RUNAS: + per_runas = 1; + continue; + case DEFAULTS_CMND: + per_cmnd = 1; + continue; } + if (prefix) + print_def(prefix); + if (d->val != NULL) { + opstr[0] = d->op == TRUE ? '=' : d->op; + print_def4(d->op == FALSE ? "!" : "", d->var, opstr, d->val); + } else + print_def2(d->op == FALSE ? "!" : "", d->var); + prefix = ", "; } + print_priv("\n"); + + if (per_runas) + display_bound_defaults(DEFAULTS_RUNAS); + if (per_cmnd) + display_bound_defaults(DEFAULTS_CMND); +} + +/* + * Display Defaults entries of the given type. + */ +static void +display_bound_defaults(dtype) + int dtype; +{ + struct defaults *d; + struct member *m, *binding; + char *dname, *dsep, opstr[2]; + int atype; + + opstr[1] = '\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; + } + print_def4("\n", "Per-", dname, "Defaults entries:"); + for (d = defaults, binding = NULL; d != NULL; d = d->next) { + if (d->type != dtype) + continue; + + if (d->binding != binding) { + binding = d->binding; + print_def3("\n", " Defaults", dsep); + for (m = binding; m != NULL; m = m->next) { + if (m != binding) + print_def(","); + print_member(m->name, m->type, m->negated, atype); + print_def(" "); + } + } else + print_def(", "); + if (d->val != NULL) { + opstr[0] = d->op == TRUE ? '=' : d->op; + print_def4(d->op == FALSE ? "!" : "", d->var, opstr, d->val); + } else + print_def2(d->op == FALSE ? "!" : "", d->var); + } + print_priv("\n"); } /* @@ -288,18 +419,18 @@ print_member(name, type, negated, alias_type) switch (type) { case ALL: - printf("%sALL", negated ? "!" : ""); + print_priv(negated ? "!ALL" : "ALL"); break; case COMMAND: c = (struct sudo_command *) name; - printf("%s%s%s%s", negated ? "!" : "", c->cmnd, c->args ? " " : "", + print_priv4(negated ? "!" : "", c->cmnd, c->args ? " " : "", c->args ? c->args : ""); break; case ALIAS: if ((a = find_alias(name, alias_type)) != NULL) { for (m = a->first_member; m != NULL; m = m->next) { if (m != a->first_member) - fputs(", ", stdout); + print_priv(", "); print_member(m->name, m->type, negated ? !m->negated : m->negated, alias_type); } @@ -307,7 +438,79 @@ print_member(name, type, negated, alias_type) } /* FALLTHROUGH */ default: - printf("%s%s", negated ? "!" : "", name); + print_priv2(negated ? "!" : "", name); break; } } + +#if !defined(TIOCGSIZE) && defined(TIOCGWINSZ) +# define TIOCGSIZE TIOCGWINSZ +# define ttysize winsize +# define ts_cols ws_col +#endif + +static int +get_ttycols() +{ + char *p; + int cols; +#ifdef TIOCGSIZE + struct ttysize win; + + if (ioctl(STDERR_FILENO, TIOCGSIZE, &win) == 0 && win.ts_cols != 0) + return((int)win.ts_cols); +#endif + + /* Fall back on $COLUMNS. */ + if ((p = getenv("COLUMNS")) == NULL || (cols = atoi(p)) <= 0) + cols = 80; + return(cols); +} + +/* + * Simplistic print function with line wrap. + * XXX - does not expand tabs, etc and only checks for newlines + * at the end of an arg. + */ +static void +print_wrap(int indent, int lc, int nargs, ...) +{ + static int left, cols = -1; + int i, n, len; + va_list ap; + char *s = NULL; + + if (cols == -1) + left = cols = get_ttycols(); + + va_start(ap, nargs); + for (len = 0, i = 1; i <= nargs; i++) { + s = va_arg(ap, char *); + if ((n = strlen(s)) > 0) + len += s[n - 1] == '\n' ? n - 1 : n; + } + va_end(ap); + + if (len > left && cols > indent && len < cols - indent) { + if (lc) + putchar(lc); + putchar('\n'); + for (i = 0; i < indent; i++) + putchar(' '); + left = cols - indent; + } + va_start(ap, nargs); + for (i = 1; i <= nargs; i++) { + s = va_arg(ap, char *); + if ((len = strlen(s)) > 0) { + fwrite(s, len, 1, stdout); + if (s[len - 1] == '\n') + left = cols; + else if (len > left) + left = 0; + else + left -= len; + } + } + va_end(ap); +}