3 * $Id: bcquery.c,v 1.4 2003/10/12 00:14:55 mdw Exp $
5 * Query and dump Become's configuration file
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of `become'
14 * `Become' is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * `Become' is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with `become'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.4 2003/10/12 00:14:55 mdw
33 * Major overhaul. Now uses DSA signatures rather than the bogus symmetric
34 * encrypt-and-hope thing. Integrated with mLib and Catacomb.
36 * Revision 1.3 1999/05/04 16:17:11 mdw
37 * Change to header file name for parser. See log for `parse.h' for
40 * Revision 1.2 1998/06/26 10:32:31 mdw
41 * Cosmetic change: use sizeof(destination) in memcpy.
43 * Revision 1.1 1998/04/23 13:20:20 mdw
44 * Added new program to verify and query Become configuration files.
48 /*----- Header files ------------------------------------------------------*/
50 /* --- ANSI headers --- */
60 /* --- Unix headers --- */
62 #include <sys/types.h>
64 #include <sys/socket.h>
65 #include <sys/utsname.h>
67 #include <netinet/in.h>
69 #include <arpa/inet.h>
77 /* --- mLib headers --- */
79 #include <mLib/alloc.h>
80 #include <mLib/mdwopt.h>
81 #include <mLib/quis.h>
82 #include <mLib/report.h>
85 /* --- Local headers --- */
98 /*----- Type definitions --------------------------------------------------*/
110 typedef struct qnode {
124 #define q_right q.q.r
128 /*----- Static variables --------------------------------------------------*/
145 static unsigned flags;
146 static const char *cf = file_RULES;
147 static unsigned outmask = cat_where | cat_from | cat_to | cat_what;
149 /*----- Low-level options handling ----------------------------------------*/
151 /* --- @optname@ --- *
155 * Returns: Pointer to a string describing the current option.
157 * Use: Creates a textual description of an option for use in
161 static const char *optname(void)
165 case 'H': return ("-host");
166 case 'F': return ("-from");
167 case 'T': return ("-to");
168 case 'C': return ("-command");
169 case 0: return (optarg);
170 case '(': case ')': case '&': case '|': case '!':
174 case EOF: return ("<eof>");
175 default: return ("<unknown>");
179 /* --- @nextopt@ --- *
183 * Returns: Next option id, or @EOF@.
185 * Use: Reads the next option. Does a lot of the messy work of
189 static int nextopt(void)
191 const static struct option opts[] = {
192 { "help", 0, 0, 'h' },
194 { "file", gFlag_argReq, 0, 'f' },
195 { "dump", 0, 0, 'd' },
196 { "check", 0, 0, 'k' },
198 { "output", gFlag_argReq, 0, 'o' },
199 { "columns", 0, 0, '|' },
200 { "rows", 0, 0, '-' },
201 { "nohead", 0, 0, 'n' },
203 { "host", gFlag_argReq, 0, 'H' },
204 { "from", gFlag_argReq, 0, 'F' },
205 { "to", gFlag_argReq, 0, 'T' },
206 { "command", gFlag_argReq, 0, 'C' },
208 { "and", 0, 0, '&' },
210 { "not", 0, 0, '!' },
216 opt = mdwopt(ac, av, "-", opts, 0, 0, gFlag_noShorts);
221 "Usage: %s [-help]\n"
222 " %s [-output COLS] [-dump] [-file FILE] [EXPR | -check]\n"
224 "Reads the `become' configuration file FILE (or " file_RULES " by\n"
225 "default) and writes the rules which match the EXPR.\n"
227 "EXPR may make use of the following operators: `-host HOST', `-from USER',\n"
228 "`-to USER', and `-command CMD'. You may join them together with `-and',\n"
229 "`-or' and `-not' operators (which may be spelled `&', `|' and `!' if you\n"
230 "prefer), and group subexpressions with parentheses `(' and `)'.\n",
253 enum { m_replace, m_add, m_remove } mode = m_replace;
277 die(1, "unknown column specifier `%c'", *p);
280 if (mode == m_replace) {
286 else if (mode == m_remove)
289 die(1, "bad mode while setting output mask: %u", mode);
297 die(1, "type `%s --help' for usage information", quis());
299 if (optarg[0] && optarg[1] == 0) switch (optarg[0]) {
301 case '&': case '|': case '!':
306 die(1, "unexpected text `%s' found", optarg);
313 /*----- Recursive descent query parser ------------------------------------*/
315 /* --- @qparse@ --- *
319 * Returns: A pointer to the finished tree.
321 * Use: Scans the command line arguments and makes them into a tree.
324 static qnode *qparse_expr(void);
326 static qnode *qparse_atom(void)
334 die(1, "syntax error: expected `)', found `%s'", optname());
340 qnode *q = xmalloc(sizeof(*q));
341 h = gethostbyname(optarg);
343 die(1, "unknown host `%s'", optarg);
344 q->q_cat = cat_where;
345 memcpy(&q->q_in, h->h_addr, sizeof(q->q_in));
349 case 'F': case 'T': {
350 qnode *q = xmalloc(sizeof(*q));
351 q->q_cat = (opt == 'F' ? cat_from : cat_to);
352 if (isdigit((unsigned char)optarg[0]))
353 q->q_uid = atoi(optarg);
356 if (!(flags & f_userdb)) {
362 pw = userdb_userByName(optarg);
364 die(1, "unknown user `%s'", optarg);
365 q->q_uid = pw->pw_uid;
371 qnode *q = xmalloc(sizeof(*q));
378 die(1, "unexpected token: `%s'", optname());
383 static qnode *qparse_factor(void)
386 qnode *q = xmalloc(sizeof(*q));
389 q->q_arg = qparse_atom();
392 return (qparse_atom());
395 static qnode *qparse_term(void)
397 qnode *top, *q, **qq;
405 case 'H': case 'F': case 'T': case 'C': case '!': case '(':
406 *qq = xmalloc(sizeof(*q));
407 (*qq)->q_cat = cat_and;
409 qq = &(*qq)->q_right;
418 static qnode *qparse_expr(void)
420 qnode *top, *q, **qq;
428 *qq = xmalloc(sizeof(*q));
429 (*qq)->q_cat = cat_or;
431 qq = &(*qq)->q_right;
440 static qnode *qparse(void)
448 die(1, "syntax error: `%s' unexpected", optname());
452 /* --- @dumptree@ --- *
454 * Arguments: @qnode *q@ = pointer to tree to dump
455 * @int indent@ = indentation for this subtree
459 * Use: Dumps a tree to stdout for debugging purposes.
462 static void dumptree(qnode *q, int indent)
465 printf("<empty> -- magic query which matches everything\n");
468 printf("%*s", indent * 2, "");
472 printf("host = %s\n", inet_ntoa(q->q_in));
475 printf("from = %u\n", (unsigned)q->q_uid);
478 printf("to = %u\n", (unsigned)q->q_uid);
481 printf("command = `%s'\n", q->q_cmd);
489 unsigned cat = q->q_cat;
490 printf(cat == cat_and ? "and\n" : "or\n");
491 while (q->q_cat == cat) {
492 dumptree(q->q_left, indent);
498 printf("unknown type %u\n", q->q_cat);
502 /*----- Recursive query matching ------------------------------------------*/
504 /* --- @checkrule@ --- *
506 * Arguments: @rule *r@ = pointer to a rule
507 * @qnode *q@ = pointer to a query tree
509 * Returns: Nonzero if the query matches the rule.
511 * Use: Matches rules and queries.
514 static int checkrule(rule *r, qnode *q)
519 /* --- Handle the compound query types --- */
522 return (!checkrule(r, q->q_arg));
525 if (!checkrule(r, q->q_left))
531 if (checkrule(r, q->q_left))
536 /* --- And now the simple query types --- */
539 return (class_matchHost(r->host, q->q_in));
541 return (class_matchUser(r->from, q->q_uid));
543 return (class_matchUser(r->to, q->q_uid));
545 return (class_matchCommand(r->cmd, q->q_cmd));
548 /* --- Anything else is bogus (and a bug) --- */
550 die(1, "unexpected cat code %u in checkrule", q->q_cat);
554 /*----- Rule output -------------------------------------------------------*/
556 /* --- @showrule@ --- *
558 * Arguments: @rule *r@ = pointer to a rule block
562 * Use: Writes a rule block to the output in a pleasant way.
565 static const char *xltuser(uid_t u)
568 struct passwd *pw = userdb_userById(u);
570 return (pw->pw_name);
571 sprintf(buf, "%u", (unsigned)u);
575 static void classfirstrow(class_node *c, const char *fmt, sym_iter *i,
576 unsigned bit, unsigned *imask)
578 switch (c->type & clNode_mask) {
580 printf(fmt, (c == class_all ? "ALL" :
581 c == class_none ? "NONE" :
585 printf(fmt, (c->type & clType_user) ? xltuser(c->v.u) : c->v.s);
589 sym_mkiter(i, &c->v.t);
594 } else if (c->type & clType_user)
595 printf(fmt, xltuser(*(uid_t *)b->name));
597 printf(fmt, b->name);
601 printf(fmt, "<complex>");
606 static void showclass(class_node *c,
607 void (*sc)(class_node *c),
608 void (*sh)(sym_base *b))
613 switch (c->type & clNode_mask) {
615 fputs(c == class_all ? "ALL" :
616 c == class_none ? "NONE" : "<buggy>",
625 sym_mkiter(&i, &c->v.t);
627 if ((b = sym_next(&i)) != 0) {
629 while ((b = sym_next(&i)) != 0) {
646 fputs("<unknown node type>", stdout);
649 type = c->type & clNode_mask;
652 showclass(c->v.c.l, sc, sh);
655 } while ((c->type & clNode_mask) == type);
656 showclass(c, sc, sh);
662 static void showuseri(class_node *c) { fputs(xltuser(c->v.u), stdout); }
664 static void showuserh(sym_base *b)
666 fputs(xltuser(*(uid_t *)b->name), stdout);
669 static void showstringi(class_node *c) { fputs(c->v.s, stdout); }
671 static void showstringh(sym_base *b) { fputs(b->name, stdout); }
673 static void showrule(rule *r)
675 /* --- First up: display of simple classes in columns --- */
677 if (flags & f_simple) {
679 sym_base *w = 0, *x = 0, *y = 0, *z = 0;
682 /* --- Print the header line if necessary --- */
684 if (!(flags & f_header)) {
685 if (!(flags & f_nohead)) {
686 if (outmask & cat_from) printf("%-15s ", "FROM");
687 if (outmask & cat_to) printf("%-15s ", "TO");
688 if (outmask & cat_where) printf("%-24s ", "HOST");
689 if (outmask & cat_what) printf("%s", "COMMAND");
697 /* --- Print out the first row --- */
699 if (outmask & cat_from)
700 classfirstrow(r->from, "%-15.15s ", &a, cat_from, &imask);
701 if (outmask & cat_to)
702 classfirstrow(r->to, "%-15.15s ", &b, cat_to, &imask);
703 if (outmask & cat_where)
704 classfirstrow(r->host, "%-24.24s ", &c, cat_where, &imask);
705 if (outmask & cat_what)
706 classfirstrow(r->cmd, "%s", &d, cat_what, &imask);
709 /* --- And now for the rest --- */
712 if ((imask & cat_from) && (w = sym_next(&a)) == 0)
714 if ((imask & cat_to) && (x = sym_next(&b)) == 0)
716 if ((imask & cat_where) && (y = sym_next(&c)) == 0)
718 if ((imask & cat_what) && (z = sym_next(&d)) == 0)
724 if (outmask & cat_from) {
726 !(imask & cat_from) ? "" : xltuser(*(uid_t *)w->name));
729 if (outmask & cat_to) {
731 !(imask & cat_to) ? "" : xltuser(*(uid_t *)x->name));
734 if (outmask & cat_where)
735 printf("%-24.24s ", !(imask & cat_where) ? "" : y->name);
737 if (outmask & cat_what)
738 printf("%s", !(imask & cat_what) ? "" : z->name);
744 /* --- Otherwise deal with complex cases --- */
747 if (flags & f_header)
751 if (outmask & cat_from) {
752 fputs(" From: ", stdout);
753 showclass(r->from, showuseri, showuserh);
756 if (outmask & cat_to) {
757 fputs(" To: ", stdout);
758 showclass(r->to, showuseri, showuserh);
761 if (outmask & cat_where) {
762 fputs(" Hosts: ", stdout);
763 showclass(r->host, showstringi, showstringh);
766 if (outmask & cat_what) {
767 fputs("Commands: ", stdout);
768 showclass(r->cmd, showstringi, showstringh);
774 /*----- Dummy functions ---------------------------------------------------*/
776 void daemon_usePort(int p) { ; }
777 void daemon_readKey(const char *f) { ; }
779 /*----- Main code ---------------------------------------------------------*/
783 * Arguments: @int argc@ = number of command line arguments
784 * @char *argv[]@ = pointer to command line arguments
786 * Returns: Zero if all went OK.
788 * Use: Verifies and queries the `become' configuration file.
791 int main(int argc, char *argv[])
795 /* --- Initialise things --- */
798 ac = argc; av = argv;
800 /* --- Read the query tree --- */
804 /* --- Dump the tree if so requested --- */
806 if (flags & f_dump) {
811 /* --- Check columns requested --- */
813 if (outmask == (outmask & (outmask - 1)))
816 /* --- Read the ruleset --- */
818 if (!(flags & f_userdb)) {
829 FILE *fp = fopen(cf, "r");
833 die(1, "couldn't open configuration file `%s': %s", cf, strerror(errno));
840 /* --- Now scan the query --- */
843 rule *rl = rule_list(), *r;
845 /* --- Decide on output format if not already chosen --- */
847 if (!(flags & f_force)) {
851 if ((!qtree || checkrule(r, qtree)) &&
852 ((r->host->type & clNode_mask) >= clNode_binop ||
853 (r->from->type & clNode_mask) >= clNode_binop ||
854 (r->to->type & clNode_mask) >= clNode_binop ||
855 (r->cmd->type & clNode_mask) >= clNode_binop)) {
863 /* --- Now just dump the matching items --- */
867 if (!qtree || checkrule(r, qtree)) {
877 if (!(flags & f_match))
882 /*----- That's all, folks -------------------------------------------------*/