9b9287787e2cf375f1b094387edd298eb5904bb2
3 * $Id: bcquery.c,v 1.5 2003/11/29 23:39:16 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.5 2003/11/29 23:39:16 mdw
35 * Revision 1.4 2003/10/12 00:14:55 mdw
36 * Major overhaul. Now uses DSA signatures rather than the bogus symmetric
37 * encrypt-and-hope thing. Integrated with mLib and Catacomb.
39 * Revision 1.3 1999/05/04 16:17:11 mdw
40 * Change to header file name for parser. See log for `parse.h' for
43 * Revision 1.2 1998/06/26 10:32:31 mdw
44 * Cosmetic change: use sizeof(destination) in memcpy.
46 * Revision 1.1 1998/04/23 13:20:20 mdw
47 * Added new program to verify and query Become configuration files.
51 /*----- Header files ------------------------------------------------------*/
53 /* --- ANSI headers --- */
63 /* --- Unix headers --- */
65 #include <sys/types.h>
67 #include <sys/socket.h>
68 #include <sys/utsname.h>
70 #include <netinet/in.h>
72 #include <arpa/inet.h>
80 /* --- mLib headers --- */
82 #include <mLib/alloc.h>
83 #include <mLib/mdwopt.h>
84 #include <mLib/quis.h>
85 #include <mLib/report.h>
88 /* --- Local headers --- */
101 /*----- Type definitions --------------------------------------------------*/
113 typedef struct qnode
{
127 #define q_right q.q.r
131 /*----- Static variables --------------------------------------------------*/
148 static unsigned flags
;
149 static const char *cf
= file_RULES
;
150 static unsigned outmask
= cat_where
| cat_from
| cat_to
| cat_what
;
152 /*----- Low-level options handling ----------------------------------------*/
154 /* --- @optname@ --- *
158 * Returns: Pointer to a string describing the current option.
160 * Use: Creates a textual description of an option for use in
164 static const char *optname(void)
168 case 'H': return ("-host");
169 case 'F': return ("-from");
170 case 'T': return ("-to");
171 case 'C': return ("-command");
172 case 0: return (optarg
);
173 case '(': case ')': case '&': case '|': case '!':
177 case EOF
: return ("<eof>");
178 default: return ("<unknown>");
182 /* --- @nextopt@ --- *
186 * Returns: Next option id, or @EOF@.
188 * Use: Reads the next option. Does a lot of the messy work of
192 static int nextopt(void)
194 const static struct option opts
[] = {
195 { "help", 0, 0, 'h' },
197 { "file", gFlag_argReq
, 0, 'f' },
198 { "dump", 0, 0, 'd' },
199 { "check", 0, 0, 'k' },
201 { "output", gFlag_argReq
, 0, 'o' },
202 { "columns", 0, 0, '|' },
203 { "rows", 0, 0, '-' },
204 { "nohead", 0, 0, 'n' },
206 { "host", gFlag_argReq
, 0, 'H' },
207 { "from", gFlag_argReq
, 0, 'F' },
208 { "to", gFlag_argReq
, 0, 'T' },
209 { "command", gFlag_argReq
, 0, 'C' },
211 { "and", 0, 0, '&' },
213 { "not", 0, 0, '!' },
219 opt
= mdwopt(ac
, av
, "-", opts
, 0, 0, gFlag_noShorts
);
224 "Usage: %s [-help]\n"
225 " %s [-output COLS] [-dump] [-file FILE] [EXPR | -check]\n"
227 "Reads the `become' configuration file FILE (or " file_RULES
" by\n"
228 "default) and writes the rules which match the EXPR.\n"
230 "EXPR may make use of the following operators: `-host HOST', `-from USER',\n"
231 "`-to USER', and `-command CMD'. You may join them together with `-and',\n"
232 "`-or' and `-not' operators (which may be spelled `&', `|' and `!' if you\n"
233 "prefer), and group subexpressions with parentheses `(' and `)'.\n",
256 enum { m_replace
, m_add
, m_remove
} mode
= m_replace
;
280 die(1, "unknown column specifier `%c'", *p
);
283 if (mode
== m_replace
) {
289 else if (mode
== m_remove
)
292 die(1, "bad mode while setting output mask: %u", mode
);
300 die(1, "type `%s --help' for usage information", quis());
302 if (optarg
[0] && optarg
[1] == 0) switch (optarg
[0]) {
304 case '&': case '|': case '!':
309 die(1, "unexpected text `%s' found", optarg
);
316 /*----- Recursive descent query parser ------------------------------------*/
318 /* --- @qparse@ --- *
322 * Returns: A pointer to the finished tree.
324 * Use: Scans the command line arguments and makes them into a tree.
327 static qnode
*qparse_expr(void);
329 static qnode
*qparse_atom(void)
337 die(1, "syntax error: expected `)', found `%s'", optname());
343 qnode
*q
= xmalloc(sizeof(*q
));
344 h
= gethostbyname(optarg
);
346 die(1, "unknown host `%s'", optarg
);
347 q
->q_cat
= cat_where
;
348 memcpy(&q
->q_in
, h
->h_addr
, sizeof(q
->q_in
));
352 case 'F': case 'T': {
353 qnode
*q
= xmalloc(sizeof(*q
));
354 q
->q_cat
= (opt
== 'F' ? cat_from
: cat_to
);
355 if (isdigit((unsigned char)optarg
[0]))
356 q
->q_uid
= atoi(optarg
);
359 if (!(flags
& f_userdb
)) {
365 pw
= userdb_userByName(optarg
);
367 die(1, "unknown user `%s'", optarg
);
368 q
->q_uid
= pw
->pw_uid
;
374 qnode
*q
= xmalloc(sizeof(*q
));
381 die(1, "unexpected token: `%s'", optname());
386 static qnode
*qparse_factor(void)
389 qnode
*q
= xmalloc(sizeof(*q
));
392 q
->q_arg
= qparse_atom();
395 return (qparse_atom());
398 static qnode
*qparse_term(void)
400 qnode
*top
, *q
, **qq
;
408 case 'H': case 'F': case 'T': case 'C': case '!': case '(':
409 *qq
= xmalloc(sizeof(*q
));
410 (*qq
)->q_cat
= cat_and
;
412 qq
= &(*qq
)->q_right
;
421 static qnode
*qparse_expr(void)
423 qnode
*top
, *q
, **qq
;
431 *qq
= xmalloc(sizeof(*q
));
432 (*qq
)->q_cat
= cat_or
;
434 qq
= &(*qq
)->q_right
;
443 static qnode
*qparse(void)
451 die(1, "syntax error: `%s' unexpected", optname());
455 /* --- @dumptree@ --- *
457 * Arguments: @qnode *q@ = pointer to tree to dump
458 * @int indent@ = indentation for this subtree
462 * Use: Dumps a tree to stdout for debugging purposes.
465 static void dumptree(qnode
*q
, int indent
)
468 printf("<empty> -- magic query which matches everything\n");
473 printf("%*s", indent
* 2, "");
477 printf("host = %s\n", inet_ntoa(q
->q_in
));
480 printf("from = %u\n", (unsigned)q
->q_uid
);
483 printf("to = %u\n", (unsigned)q
->q_uid
);
486 printf("command = `%s'\n", q
->q_cmd
);
494 unsigned cat
= q
->q_cat
;
495 printf(cat
== cat_and ?
"and\n" : "or\n");
496 while (q
->q_cat
== cat
) {
497 dumptree(q
->q_left
, indent
);
503 printf("unknown type %u\n", q
->q_cat
);
507 /*----- Recursive query matching ------------------------------------------*/
509 /* --- @checkrule@ --- *
511 * Arguments: @rule *r@ = pointer to a rule
512 * @qnode *q@ = pointer to a query tree
514 * Returns: Nonzero if the query matches the rule.
516 * Use: Matches rules and queries.
519 static int checkrule(rule
*r
, qnode
*q
)
524 /* --- Handle the compound query types --- */
527 return (!checkrule(r
, q
->q_arg
));
530 if (!checkrule(r
, q
->q_left
))
536 if (checkrule(r
, q
->q_left
))
541 /* --- And now the simple query types --- */
544 return (class_matchHost(r
->host
, q
->q_in
));
546 return (class_matchUser(r
->from
, q
->q_uid
));
548 return (class_matchUser(r
->to
, q
->q_uid
));
550 return (class_matchCommand(r
->cmd
, q
->q_cmd
));
553 /* --- Anything else is bogus (and a bug) --- */
555 die(1, "unexpected cat code %u in checkrule", q
->q_cat
);
559 /*----- Rule output -------------------------------------------------------*/
561 /* --- @showrule@ --- *
563 * Arguments: @rule *r@ = pointer to a rule block
567 * Use: Writes a rule block to the output in a pleasant way.
570 static const char *xltuser(uid_t u
)
573 struct passwd
*pw
= userdb_userById(u
);
575 return (pw
->pw_name
);
576 sprintf(buf
, "%u", (unsigned)u
);
580 static void classfirstrow(class_node
*c
, const char *fmt
, sym_iter
*i
,
581 unsigned bit
, unsigned *imask
)
583 switch (c
->type
& clNode_mask
) {
585 printf(fmt
, (c
== class_all ?
"ALL" :
586 c
== class_none ?
"NONE" :
590 printf(fmt
, (c
->type
& clType_user
) ?
xltuser(c
->v
.u
) : c
->v
.s
);
594 sym_mkiter(i
, &c
->v
.t
);
599 } else if (c
->type
& clType_user
)
600 printf(fmt
, xltuser(*(uid_t
*)b
->name
));
602 printf(fmt
, b
->name
);
606 printf(fmt
, "<complex>");
611 static void showclass(class_node
*c
,
612 void (*sc
)(class_node
*c
),
613 void (*sh
)(sym_base
*b
))
618 switch (c
->type
& clNode_mask
) {
620 fputs(c
== class_all ?
"ALL" :
621 c
== class_none ?
"NONE" : "<buggy>",
630 sym_mkiter(&i
, &c
->v
.t
);
632 if ((b
= sym_next(&i
)) != 0) {
634 while ((b
= sym_next(&i
)) != 0) {
651 fputs("<unknown node type>", stdout
);
654 type
= c
->type
& clNode_mask
;
657 showclass(c
->v
.c
.l
, sc
, sh
);
660 } while ((c
->type
& clNode_mask
) == type
);
661 showclass(c
, sc
, sh
);
667 static void showuseri(class_node
*c
) { fputs(xltuser(c
->v
.u
), stdout
); }
669 static void showuserh(sym_base
*b
)
671 fputs(xltuser(*(uid_t
*)b
->name
), stdout
);
674 static void showstringi(class_node
*c
) { fputs(c
->v
.s
, stdout
); }
676 static void showstringh(sym_base
*b
) { fputs(b
->name
, stdout
); }
678 static void showrule(rule
*r
)
680 /* --- First up: display of simple classes in columns --- */
682 if (flags
& f_simple
) {
684 sym_base
*w
= 0, *x
= 0, *y
= 0, *z
= 0;
687 /* --- Print the header line if necessary --- */
689 if (!(flags
& f_header
)) {
690 if (!(flags
& f_nohead
)) {
691 if (outmask
& cat_from
) printf("%-15s ", "FROM");
692 if (outmask
& cat_to
) printf("%-15s ", "TO");
693 if (outmask
& cat_where
) printf("%-24s ", "HOST");
694 if (outmask
& cat_what
) printf("%s", "COMMAND");
702 /* --- Print out the first row --- */
704 if (outmask
& cat_from
)
705 classfirstrow(r
->from
, "%-15.15s ", &a
, cat_from
, &imask
);
706 if (outmask
& cat_to
)
707 classfirstrow(r
->to
, "%-15.15s ", &b
, cat_to
, &imask
);
708 if (outmask
& cat_where
)
709 classfirstrow(r
->host
, "%-24.24s ", &c
, cat_where
, &imask
);
710 if (outmask
& cat_what
)
711 classfirstrow(r
->cmd
, "%s", &d
, cat_what
, &imask
);
714 /* --- And now for the rest --- */
717 if ((imask
& cat_from
) && (w
= sym_next(&a
)) == 0)
719 if ((imask
& cat_to
) && (x
= sym_next(&b
)) == 0)
721 if ((imask
& cat_where
) && (y
= sym_next(&c
)) == 0)
723 if ((imask
& cat_what
) && (z
= sym_next(&d
)) == 0)
729 if (outmask
& cat_from
) {
731 !(imask
& cat_from
) ?
"" : xltuser(*(uid_t
*)w
->name
));
734 if (outmask
& cat_to
) {
736 !(imask
& cat_to
) ?
"" : xltuser(*(uid_t
*)x
->name
));
739 if (outmask
& cat_where
)
740 printf("%-24.24s ", !(imask
& cat_where
) ?
"" : y
->name
);
742 if (outmask
& cat_what
)
743 printf("%s", !(imask
& cat_what
) ?
"" : z
->name
);
749 /* --- Otherwise deal with complex cases --- */
752 if (flags
& f_header
)
756 if (outmask
& cat_from
) {
757 fputs(" From: ", stdout
);
758 showclass(r
->from
, showuseri
, showuserh
);
761 if (outmask
& cat_to
) {
762 fputs(" To: ", stdout
);
763 showclass(r
->to
, showuseri
, showuserh
);
766 if (outmask
& cat_where
) {
767 fputs(" Hosts: ", stdout
);
768 showclass(r
->host
, showstringi
, showstringh
);
771 if (outmask
& cat_what
) {
772 fputs("Commands: ", stdout
);
773 showclass(r
->cmd
, showstringi
, showstringh
);
779 /*----- Dummy functions ---------------------------------------------------*/
781 void daemon_usePort(int p
) { ; }
782 void daemon_readKey(const char *f
) { ; }
784 /*----- Main code ---------------------------------------------------------*/
788 * Arguments: @int argc@ = number of command line arguments
789 * @char *argv[]@ = pointer to command line arguments
791 * Returns: Zero if all went OK.
793 * Use: Verifies and queries the `become' configuration file.
796 int main(int argc
, char *argv
[])
800 /* --- Initialise things --- */
803 ac
= argc
; av
= argv
;
805 /* --- Read the query tree --- */
809 /* --- Dump the tree if so requested --- */
811 if (flags
& f_dump
) {
816 /* --- Check columns requested --- */
818 if (outmask
== (outmask
& (outmask
- 1)))
821 /* --- Read the ruleset --- */
823 if (!(flags
& f_userdb
)) {
834 FILE *fp
= fopen(cf
, "r");
838 die(1, "couldn't open configuration file `%s': %s", cf
, strerror(errno
));
845 /* --- Now scan the query --- */
848 rule
*rl
= rule_list(), *r
;
850 /* --- Decide on output format if not already chosen --- */
852 if (!(flags
& f_force
)) {
856 if ((!qtree
|| checkrule(r
, qtree
)) &&
857 ((r
->host
->type
& clNode_mask
) >= clNode_binop
||
858 (r
->from
->type
& clNode_mask
) >= clNode_binop
||
859 (r
->to
->type
& clNode_mask
) >= clNode_binop
||
860 (r
->cmd
->type
& clNode_mask
) >= clNode_binop
)) {
868 /* --- Now just dump the matching items --- */
872 if (!qtree
|| checkrule(r
, qtree
)) {
882 if (!(flags
& f_match
))
887 /*----- That's all, folks -------------------------------------------------*/