From 908a5930383a9939b4847e2cba559f86871d5ffc Mon Sep 17 00:00:00 2001 Message-Id: <908a5930383a9939b4847e2cba559f86871d5ffc.1714490180.git.mdw@distorted.org.uk> From: Mark Wooding Date: Thu, 23 Apr 1998 13:20:20 +0000 Subject: [PATCH] Added new program to verify and query Become configuration files. Organization: Straylight/Edgeware From: mdw --- src/bcquery.c | 866 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 866 insertions(+) create mode 100644 src/bcquery.c diff --git a/src/bcquery.c b/src/bcquery.c new file mode 100644 index 0000000..e43d075 --- /dev/null +++ b/src/bcquery.c @@ -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 +#include +#include +#include +#include +#include +#include + +/* --- Unix headers --- */ + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +/* --- 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 (""); + default: return (""); + } +} + +/* --- @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 = ⊤ + +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 = ⊤ + +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(" -- 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" : + "")); + 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, ""); + 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" : "", + 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("", 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 -------------------------------------------------*/ -- [mdw]