chiark / gitweb /
Added new program to verify and query Become configuration files.
authormdw <mdw>
Thu, 23 Apr 1998 13:20:20 +0000 (13:20 +0000)
committermdw <mdw>
Thu, 23 Apr 1998 13:20:20 +0000 (13:20 +0000)
src/bcquery.c [new file with mode: 0644]

diff --git a/src/bcquery.c b/src/bcquery.c
new file mode 100644 (file)
index 0000000..e43d075
--- /dev/null
@@ -0,0 +1,866 @@
+/* -*-c-*-
+ *
+ * $Id: bcquery.c,v 1.1 1998/04/23 13:20:20 mdw Exp $
+ *
+ * Query and dump Become's configuration file
+ *
+ * (c) 1998 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * `Become' is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with `become'; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: bcquery.c,v $
+ * Revision 1.1  1998/04/23 13:20:20  mdw
+ * Added new program to verify and query Become configuration files.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+/* --- Unix headers --- */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <grp.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <unistd.h>
+
+/* --- Local headers --- */
+
+#include "become.h"
+#include "class.h"
+#include "config.h"
+#include "daemon.h"
+#include "lexer.h"
+#include "mdwopt.h"
+#include "name.h"
+#include "netg.h"
+#include "parser.h"
+#include "rule.h"
+#include "sym.h"
+#include "utils.h"
+#include "userdb.h"
+
+/*----- Type definitions --------------------------------------------------*/
+
+enum {
+  cat_where = 1u,
+  cat_from = 2u,
+  cat_to = 4u,
+  cat_what = 8u,
+  cat_and = 16u,
+  cat_or = 17u,
+  cat_not = 18u
+};
+
+typedef struct qnode {
+  unsigned q_cat;
+  union {
+    uid_t uid;
+    struct in_addr in;
+    const char *cmd;
+    struct {
+      struct qnode *l, *r;
+    } q;
+  } q;
+#define q_uid q.uid
+#define q_in q.in
+#define q_cmd q.cmd
+#define q_left q.q.l
+#define q_right q.q.r
+#define q_arg q_left
+} qnode;
+
+/*----- Static variables --------------------------------------------------*/
+
+enum {
+  f_dump = 1u,
+  f_userdb = 2u,
+  f_header = 4u,
+  f_match = 8u,
+  f_single = 16u,
+  f_simple = 32u,
+  f_force = 64u,
+  f_check = 128u,
+  f_nohead = 256u
+};
+
+static int ac;
+static char **av;
+static int opt;
+static unsigned flags;
+static const char *cf = file_RULES;
+static unsigned outmask = cat_where | cat_from | cat_to | cat_what;
+
+/*----- Low-level options handling ----------------------------------------*/
+
+/* --- @optname@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Pointer to a string describing the current option.
+ *
+ * Use:                Creates a textual description of an option for use in
+ *             error messages.
+ */
+
+static const char *optname(void)
+{
+  static char buf[2];
+  switch (opt) {
+    case 'H': return ("-host");
+    case 'F': return ("-from");
+    case 'T': return ("-to");
+    case 'C': return ("-command");
+    case 0: return (optarg);
+    case '(': case ')': case '&': case '|': case '!':
+      buf[0] = opt;
+      buf[1] = 0;
+      return (buf);
+    case EOF: return ("<eof>");
+    default: return ("<unknown>");
+  }
+}
+
+/* --- @nextopt@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Next option id, or @EOF@.
+ *
+ * Use:                Reads the next option.  Does a lot of the messy work of
+ *             options parsing.
+ */
+
+static int nextopt(void)
+{
+  const static struct option opts[] = {
+    { "help",          0,              0,      'h' },
+
+    { "file",          gFlag_argReq,   0,      'f' },
+    { "dump",          0,              0,      'd' },
+    { "check",         0,              0,      'k' },
+
+    { "output",                gFlag_argReq,   0,      'o' },
+    { "columns",       0,              0,      '|' },
+    { "rows",          0,              0,      '-' },
+    { "nohead",                0,              0,      'n' },
+
+    { "host",          gFlag_argReq,   0,      'H' },
+    { "from",          gFlag_argReq,   0,      'F' },
+    { "to",            gFlag_argReq,   0,      'T' },
+    { "command",       gFlag_argReq,   0,      'C' },
+
+    { "and",           0,              0,      '&' },
+    { "or",            0,              0,      '|' },
+    { "not",           0,              0,      '!' },
+
+    { 0,               0,              0,      0 }
+  };
+
+again:
+  opt = mdwopt(ac, av, "-", opts, 0, 0, gFlag_noShorts);
+
+  switch (opt) {
+    case 'h':
+      printf(""
+"Usage: %s [-help]\n"
+"       %s [-output COLS] [-dump] [-file FILE] [EXPR | -check]\n"
+"\n"
+"Reads the `become' configuration file FILE (or " file_RULES " by\n"
+"default) and writes the rules which match the EXPR.\n"
+"\n"
+"EXPR may make use of the following operators: `-host HOST', `-from USER',\n"
+"`-to USER', and `-command CMD'.  You may join them together with `-and',\n"
+"`-or' and `-not' operators (which may be spelled `&', `|' and `!' if you\n"
+"prefer), and group subexpressions with parentheses `(' and `)'.\n",
+            quis(), quis());
+      exit(0);
+    case 'd':
+      flags |= f_dump;
+      goto again;
+    case 'f':
+      cf = optarg;
+     goto again;
+    case '|':
+      flags |= f_simple;
+      /* Drop through */
+    case '-':
+      flags |= f_force;
+      goto again;
+    case 'k':
+      flags |= f_check;
+      goto again;
+    case 'n':
+      flags |= f_nohead;
+      goto again;
+    case 'o': {
+      char *p = optarg;
+      enum { m_replace, m_add, m_remove } mode = m_replace;
+      unsigned bit;
+
+      while (*p) {
+       switch (*p) {
+         case '+':
+           mode = m_add;
+           break;
+         case '-':
+           mode = m_remove;
+           break;
+         case 'h':
+           bit = cat_where;
+           goto setbits;
+         case 'f':
+           bit = cat_from;
+           goto setbits;
+         case 't':
+           bit = cat_to;
+           goto setbits;
+         case 'c':
+           bit = cat_what;
+           goto setbits;
+         default:
+           die("unknown column specifier `%c'", *p);
+           break;
+         setbits:
+           if (mode == m_replace) {
+             outmask = 0;
+             mode = m_add;
+           }
+           if (mode == m_add)
+             outmask |= bit;
+           else if (mode == m_remove)
+             outmask &= ~bit;
+           else
+             die("bad mode while setting output mask: %u", mode);
+           break;
+       }
+       p++;
+      }
+      goto again;
+    }
+    case '?':
+      die("type `%s --help' for usage information", quis());
+    case 0:
+      if (optarg[0] && optarg[1] == 0) switch (optarg[0]) {
+       case '(': case ')':
+       case '&': case '|': case '!':
+         opt = optarg[0];
+         break;
+      }
+      if (!opt)
+       die("unexpected text `%s' found", optarg);
+      break;
+  }
+
+  return (opt);            
+}
+
+/*----- Recursive descent query parser ------------------------------------*/
+
+/* --- @qparse@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    A pointer to the finished tree.
+ *
+ * Use:                Scans the command line arguments and makes them into a tree.
+ */
+
+static qnode *qparse_expr(void);
+
+static qnode *qparse_atom(void)
+{
+  switch (opt) {
+    case '(': {
+      qnode *q;
+      nextopt();
+      q = qparse_expr();
+      if (opt != ')')
+       die("syntax error: expected `)', found `%s'", optname());
+      nextopt();
+      return (q);
+    }
+    case 'H': {
+      struct hostent *h;
+      qnode *q = xmalloc(sizeof(*q));
+      h = gethostbyname(optarg);
+      if (!h)
+       die("unknown host `%s'", optarg);
+      q->q_cat = cat_where;
+      memcpy(&q->q_in, h->h_addr, sizeof(struct in_addr));
+      nextopt();
+      return (q);
+    }
+    case 'F': case 'T': {
+      qnode *q = xmalloc(sizeof(*q));
+      q->q_cat = (opt == 'F' ? cat_from : cat_to);
+      if (isdigit((unsigned char)optarg[0]))
+       q->q_uid = atoi(optarg);
+      else {
+       struct passwd *pw;
+       if (!(flags & f_userdb)) {
+         userdb_init();
+         userdb_local();
+         userdb_yp();
+         flags |= f_userdb;
+       }
+       pw = userdb_userByName(optarg);
+       if (!pw)
+         die("unknown user `%s'", optarg);
+       q->q_uid = pw->pw_uid;
+      }
+      nextopt();
+      return (q);
+    }
+    case 'C': {
+      qnode *q = xmalloc(sizeof(*q));
+      q->q_cat = cat_what;
+      q->q_cmd = optarg;
+      nextopt();
+      return (q);
+    }
+    default:
+      die("unexpected token: `%s'", optname());
+  }
+  return (0);
+}
+
+static qnode *qparse_factor(void)
+{
+  if (opt == '!') {
+    qnode *q = xmalloc(sizeof(*q));
+    nextopt();
+    q->q_cat = cat_not;
+    q->q_arg = qparse_atom();
+    return (q);
+  } else
+    return (qparse_atom());
+}
+
+static qnode *qparse_term(void)
+{
+  qnode *top, *q, **qq;
+  qq = &top;
+
+again:
+  q = qparse_factor();
+  switch (opt) {
+    case '&':
+      nextopt();
+    case 'H': case 'F': case 'T': case 'C': case '!': case '(':
+      *qq = xmalloc(sizeof(*q));
+      (*qq)->q_cat = cat_and;
+      (*qq)->q_left = q;
+      qq = &(*qq)->q_right;
+      goto again;
+    default:
+      *qq = q;
+      break;
+  }
+  return (top);
+}
+
+static qnode *qparse_expr(void)
+{
+  qnode *top, *q, **qq;
+  qq = &top;
+
+again:
+  q = qparse_term();
+  switch (opt) {
+    case '|':
+      nextopt();
+      *qq = xmalloc(sizeof(*q));
+      (*qq)->q_cat = cat_or;
+      (*qq)->q_left = q;
+      qq = &(*qq)->q_right;
+      goto again;
+    default:
+      *qq = q;
+      break;
+  }
+  return (top);
+}
+
+static qnode *qparse(void)
+{
+  qnode *q;
+  nextopt();
+  if (opt == EOF)
+    return (0);
+  q = qparse_expr();
+  if (opt != EOF)
+    die("syntax error: `%s' unexpected", optname());
+  return (q);
+}
+
+/* --- @dumptree@ --- *
+ *
+ * Arguments:  @qnode *q@ = pointer to tree to dump
+ *             @int indent@ = indentation for this subtree
+ *
+ * Returns:    ---
+ *
+ * Use:                Dumps a tree to stdout for debugging purposes.
+ */
+
+static void dumptree(qnode *q, int indent)
+{
+  if (!q)
+    printf("<empty> -- magic query which matches everything\n");
+
+again:
+  printf("%*s", indent * 2, "");
+  indent++;
+  switch (q->q_cat) {
+    case cat_where:
+      printf("host = %s\n", inet_ntoa(q->q_in));
+      break;
+    case cat_from:
+      printf("from = %u\n", (unsigned)q->q_uid);
+      break;
+    case cat_to:
+      printf("to = %u\n", (unsigned)q->q_uid);
+      break;
+    case cat_what:
+      printf("command = `%s'\n", q->q_cmd);
+      break;
+    case cat_not:
+      printf("not\n");
+      q = q->q_arg;
+      goto again;
+    case cat_and:
+    case cat_or: {
+      unsigned cat = q->q_cat;
+      printf(cat == cat_and ? "and\n" : "or\n");
+      while (q->q_cat == cat) {
+       dumptree(q->q_left, indent);
+       q = q->q_right;
+      }
+      goto again;
+    }
+    default:
+      printf("unknown type %u\n", q->q_cat);
+  }
+}
+
+/*----- Recursive query matching ------------------------------------------*/
+
+/* --- @checkrule@ --- *
+ *
+ * Arguments:  @rule *r@ = pointer to a rule
+ *             @qnode *q@ = pointer to a query tree
+ *
+ * Returns:    Nonzero if the query matches the rule.
+ *
+ * Use:                Matches rules and queries.
+ */
+
+static int checkrule(rule *r, qnode *q)
+{
+again:
+  switch (q->q_cat) {
+
+    /* --- Handle the compound query types --- */
+
+    case cat_not:
+      return (!checkrule(r, q->q_arg));
+
+    case cat_and:
+      if (!checkrule(r, q->q_left))
+       return (0);
+      q = q->q_right;
+      goto again;
+
+    case cat_or:
+      if (checkrule(r, q->q_left))
+       return (1);
+      q = q->q_right;
+      goto again;
+
+    /* --- And now the simple query types --- */
+
+    case cat_where:
+      return (class_matchHost(r->host, q->q_in));
+    case cat_from:
+      return (class_matchUser(r->from, q->q_uid));
+    case cat_to:
+      return (class_matchUser(r->to, q->q_uid));
+    case cat_what:
+      return (class_matchCommand(r->cmd, q->q_cmd));
+  }
+
+  /* --- Anything else is bogus (and a bug) --- */
+
+  die("unexpected cat code %u in checkrule", q->q_cat);
+  return (-1);
+}
+
+/*----- Rule output -------------------------------------------------------*/
+
+/* --- @showrule@ --- *
+ *
+ * Arguments:  @rule *r@ = pointer to a rule block
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a rule block to the output in a pleasant way.
+ */
+
+static const char *xltuser(uid_t u)
+{
+  static char buf[16];
+  struct passwd *pw = userdb_userById(u);
+  if (pw)
+    return (pw->pw_name);
+  sprintf(buf, "%u", (unsigned)u);
+  return (buf);
+}
+
+static void classfirstrow(class_node *c, const char *fmt, sym_iter *i,
+                         unsigned bit, unsigned *imask)
+{
+  switch (c->type & clNode_mask) {
+    case clNode_any:
+      printf(fmt, (c == class_all ? "ALL" :
+                  c == class_none ? "NONE" :
+                  "<bug>"));
+      break;
+    case clNode_immed:
+      printf(fmt, (c->type & clType_user) ? xltuser(c->v.u) : c->v.s);
+      break;
+    case clNode_hash: {
+      sym_base *b;
+      sym_createIter(i, &c->v.t);
+      b = sym_next(i);
+      if (!b) {
+       printf(fmt, "");
+       break;
+      } else if (c->type & clType_user)
+       printf(fmt, xltuser(*(uid_t *)b->name));
+      else
+       printf(fmt, b->name);
+      *imask |= bit;
+    } break;
+    default:
+      printf(fmt, "<complex>");
+      break;
+  }
+}
+
+static void showclass(class_node *c,
+                     void (*sc)(class_node *c),
+                     void (*sh)(sym_base *b))
+{
+  const char *op;
+  unsigned type;
+
+  switch (c->type & clNode_mask) {
+    case clNode_any:
+      fputs(c == class_all ? "ALL" :
+           c == class_none ? "NONE" : "<buggy>",
+           stdout);
+      break;
+    case clNode_immed:
+      sc(c);
+      break;
+    case clNode_hash: {
+      sym_iter i;
+      sym_base *b;
+      sym_createIter(&i, &c->v.t);
+      fputc('(', stdout);
+      if ((b = sym_next(&i)) != 0) {
+       sh(b);
+       while ((b = sym_next(&i)) != 0) {
+         fputs(", ", stdout);
+         sh(b);
+       }
+      }
+      fputc(')', stdout);
+    } break;
+    case clNode_union:
+      op = " | ";
+      goto binop;
+    case clNode_diff:
+      op = " - ";
+      goto binop;
+    case clNode_isect:
+      op = " & ";
+      goto binop;
+    default:
+      fputs("<unknown node type>", stdout);
+      break;
+    binop:
+      type = c->type & clNode_mask;
+      fputc('(', stdout);
+      do {
+       showclass(c->v.c.l, sc, sh);
+       fputs(op, stdout);
+       c = c->v.c.r;
+      } while ((c->type & clNode_mask) == type);
+      showclass(c, sc, sh);
+      fputc(')', stdout);
+      break;
+  }
+}
+
+static void showuseri(class_node *c) { fputs(xltuser(c->v.u), stdout); }
+
+static void showuserh(sym_base *b)
+{
+  fputs(xltuser(*(uid_t *)b->name), stdout);
+}
+
+static void showstringi(class_node *c) { fputs(c->v.s, stdout); }
+
+static void showstringh(sym_base *b) { fputs(b->name, stdout); }
+
+static void showrule(rule *r)
+{
+  /* --- First up: display of simple classes in columns --- */
+  
+  if (flags & f_simple) {
+    sym_iter a, b, c, d;
+    sym_base *w = 0, *x = 0, *y = 0, *z = 0;
+    unsigned imask = 0;
+
+    /* --- Print the header line if necessary --- */
+
+    if (!(flags & f_header)) {
+      if (!(flags & f_nohead)) {
+       if (outmask & cat_from) printf("%-15s ", "FROM");
+       if (outmask & cat_to) printf("%-15s ", "TO");
+       if (outmask & cat_where) printf("%-24s ", "HOST");
+       if (outmask & cat_what) printf("%s", "COMMAND");
+       fputc('\n', stdout);
+       fputc('\n', stdout);
+      }
+      flags |= f_header;
+    } else 
+      fputc('\n', stdout);
+
+    /* --- Print out the first row --- */
+
+    if (outmask & cat_from)
+      classfirstrow(r->from, "%-15.15s ", &a, cat_from, &imask);
+    if (outmask & cat_to)
+      classfirstrow(r->to, "%-15.15s ", &b, cat_to, &imask);
+    if (outmask & cat_where)
+      classfirstrow(r->host, "%-24.24s ", &c, cat_where, &imask);
+    if (outmask & cat_what)
+      classfirstrow(r->cmd, "%s", &d, cat_what, &imask);
+    fputc('\n', stdout);
+
+    /* --- And now for the rest --- */
+
+    for (;;) {
+      if ((imask & cat_from) && (w = sym_next(&a)) == 0)
+       imask &= ~cat_from;
+      if ((imask & cat_to) && (x = sym_next(&b)) == 0)
+       imask &= ~cat_to;
+      if ((imask & cat_where) && (y = sym_next(&c)) == 0)
+       imask &= ~cat_where;
+      if ((imask & cat_what) && (z = sym_next(&d)) == 0)
+       imask &= ~cat_what;
+
+      if (!imask)
+       break;
+
+      if (outmask & cat_from) {
+       printf("%-15.15s ",
+              !(imask & cat_from) ? "" : xltuser(*(uid_t *)w->name));
+      }
+
+      if (outmask & cat_to) {
+       printf("%-15.15s ",
+              !(imask & cat_to) ? "" : xltuser(*(uid_t *)x->name));
+      }
+
+      if (outmask & cat_where)
+       printf("%-24.24s ", !(imask & cat_where) ? "" : y->name);
+
+      if (outmask & cat_what)
+       printf("%s", !(imask & cat_what) ? "" : z->name);
+
+      fputc('\n', stdout);
+    }
+  }
+
+  /* --- Otherwise deal with complex cases --- */
+
+  else {
+    if (flags & f_header)
+      fputc('\n', stdout);
+    else
+      flags |= f_header;
+    if (outmask & cat_from) {
+      fputs("    From: ", stdout);
+      showclass(r->from, showuseri, showuserh);
+      fputc('\n', stdout);
+    }
+    if (outmask & cat_to) {
+      fputs("      To: ", stdout);
+      showclass(r->to, showuseri, showuserh);
+      fputc('\n', stdout);
+    }
+    if (outmask & cat_where) {
+      fputs("   Hosts: ", stdout);
+      showclass(r->host, showstringi, showstringh);
+      fputc('\n', stdout);
+    }
+    if (outmask & cat_what) {
+      fputs("Commands: ", stdout);
+      showclass(r->cmd, showstringi, showstringh);
+      fputc('\n', stdout);
+    }
+  }
+}
+
+/*----- Dummy functions ---------------------------------------------------*/
+
+void daemon_usePort(int p) { ; }
+void daemon_readKey(const char *f) { ; }
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @main@ --- *
+ *
+ * Arguments:  @int argc@ = number of command line arguments
+ *             @char *argv[]@ = pointer to command line arguments
+ *
+ * Returns:    Zero if all went OK.
+ *
+ * Use:                Verifies and queries the `become' configuration file.
+ */
+
+int main(int argc, char *argv[])
+{
+  qnode *qtree;
+
+  /* --- Initialise things --- */
+
+  ego(argv[0]);
+  ac = argc; av = argv;
+
+  /* --- Read the query tree --- */
+
+  qtree = qparse();
+
+  /* --- Dump the tree if so requested --- */
+
+  if (flags & f_dump) {
+    dumptree(qtree, 0);
+    return (0);
+  }
+
+  /* --- Check columns requested --- */
+
+  if (outmask == (outmask & (outmask - 1)))
+    flags |= f_single;
+
+  /* --- Read the ruleset --- */
+
+  if (!(flags & f_userdb)) {
+    userdb_init();
+    userdb_local();
+    userdb_yp();
+  }
+
+  netg_init();
+  name_init();
+  rule_init();
+
+  {
+    FILE *fp = fopen(cf, "r");
+    int ok;
+
+    if (!fp)
+      die("couldn't open configuration file `%s': %s", cf, strerror(errno));
+    lexer_scan(fp);
+    ok = parse();
+    if (flags & f_check)
+      exit(ok);
+  }
+
+  /* --- Now scan the query --- */
+
+  {
+    rule *rl = rule_list(), *r;
+
+    /* --- Decide on output format if not already chosen --- */
+
+    if (!(flags & f_force)) {
+      r = rl;
+      flags |= f_simple;
+      while (r) {
+       if ((!qtree || checkrule(r, qtree)) &&
+           ((r->host->type & clNode_mask) >= clNode_binop ||
+            (r->from->type & clNode_mask) >= clNode_binop ||
+            (r->to->type & clNode_mask) >= clNode_binop ||
+            (r->cmd->type & clNode_mask) >= clNode_binop)) {
+         flags &= ~f_simple;
+         break;
+       }
+       r = r->next;
+      }
+    }
+
+    /* --- Now just dump the matching items --- */
+
+    r = rl;
+    while (r) {
+      if (!qtree || checkrule(r, qtree)) {
+       flags |= f_match;
+       showrule(r);
+      }
+      r = r->next;
+    }
+  }
+
+  /* --- Done --- */
+
+  if (!(flags & f_match))
+    die("no match");
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/