]> granicus.if.org Git - sudo/commitdiff
Print Defaults info in "sudo -l" output and wrap lines based on the
authorTodd C. Miller <Todd.Miller@courtesan.com>
Sun, 2 Jan 2005 00:31:08 +0000 (00:31 +0000)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Sun, 2 Jan 2005 00:31:08 +0000 (00:31 +0000)
terminal width.

parse.c

diff --git a/parse.c b/parse.c
index c0ff6334ec38f334327a94746766f29b7fdd6842..9a70cd6f267c2c67b1271393d40603856e143aeb 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 1998-2004 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2004, 2005 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
  * 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 <config.h>
 
 #include <sys/types.h>
 #include <sys/param.h>
+#include <sys/ioctl.h>
 #include <stdio.h>
 #ifdef STDC_HEADERS
 # include <stdlib.h>
@@ -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);
+}