Cosmetic change: use sizeof(destination) in memcpy.
[become] / src / bcquery.c
CommitLineData
908a5930 1/* -*-c-*-
2 *
7df38e1e 3 * $Id: bcquery.c,v 1.2 1998/06/26 10:32:31 mdw Exp $
908a5930 4 *
5 * Query and dump Become's configuration file
6 *
7 * (c) 1998 EBI
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of `become'
13 *
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.
18 *
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.
23 *
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.
27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: bcquery.c,v $
7df38e1e 32 * Revision 1.2 1998/06/26 10:32:31 mdw
33 * Cosmetic change: use sizeof(destination) in memcpy.
34 *
908a5930 35 * Revision 1.1 1998/04/23 13:20:20 mdw
36 * Added new program to verify and query Become configuration files.
37 *
38 */
39
40/*----- Header files ------------------------------------------------------*/
41
42/* --- ANSI headers --- */
43
44#include <ctype.h>
45#include <errno.h>
46#include <limits.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <time.h>
51
52/* --- Unix headers --- */
53
54#include <sys/types.h>
55#include <sys/stat.h>
56#include <sys/socket.h>
57#include <sys/utsname.h>
58
59#include <netinet/in.h>
60
61#include <arpa/inet.h>
62
63#include <netdb.h>
64#include <grp.h>
65#include <pwd.h>
66#include <syslog.h>
67#include <unistd.h>
68
69/* --- Local headers --- */
70
71#include "become.h"
72#include "class.h"
73#include "config.h"
74#include "daemon.h"
75#include "lexer.h"
76#include "mdwopt.h"
77#include "name.h"
78#include "netg.h"
79#include "parser.h"
80#include "rule.h"
81#include "sym.h"
82#include "utils.h"
83#include "userdb.h"
84
85/*----- Type definitions --------------------------------------------------*/
86
87enum {
88 cat_where = 1u,
89 cat_from = 2u,
90 cat_to = 4u,
91 cat_what = 8u,
92 cat_and = 16u,
93 cat_or = 17u,
94 cat_not = 18u
95};
96
97typedef struct qnode {
98 unsigned q_cat;
99 union {
100 uid_t uid;
101 struct in_addr in;
102 const char *cmd;
103 struct {
104 struct qnode *l, *r;
105 } q;
106 } q;
107#define q_uid q.uid
108#define q_in q.in
109#define q_cmd q.cmd
110#define q_left q.q.l
111#define q_right q.q.r
112#define q_arg q_left
113} qnode;
114
115/*----- Static variables --------------------------------------------------*/
116
117enum {
118 f_dump = 1u,
119 f_userdb = 2u,
120 f_header = 4u,
121 f_match = 8u,
122 f_single = 16u,
123 f_simple = 32u,
124 f_force = 64u,
125 f_check = 128u,
126 f_nohead = 256u
127};
128
129static int ac;
130static char **av;
131static int opt;
132static unsigned flags;
133static const char *cf = file_RULES;
134static unsigned outmask = cat_where | cat_from | cat_to | cat_what;
135
136/*----- Low-level options handling ----------------------------------------*/
137
138/* --- @optname@ --- *
139 *
140 * Arguments: ---
141 *
142 * Returns: Pointer to a string describing the current option.
143 *
144 * Use: Creates a textual description of an option for use in
145 * error messages.
146 */
147
148static const char *optname(void)
149{
150 static char buf[2];
151 switch (opt) {
152 case 'H': return ("-host");
153 case 'F': return ("-from");
154 case 'T': return ("-to");
155 case 'C': return ("-command");
156 case 0: return (optarg);
157 case '(': case ')': case '&': case '|': case '!':
158 buf[0] = opt;
159 buf[1] = 0;
160 return (buf);
161 case EOF: return ("<eof>");
162 default: return ("<unknown>");
163 }
164}
165
166/* --- @nextopt@ --- *
167 *
168 * Arguments: ---
169 *
170 * Returns: Next option id, or @EOF@.
171 *
172 * Use: Reads the next option. Does a lot of the messy work of
173 * options parsing.
174 */
175
176static int nextopt(void)
177{
178 const static struct option opts[] = {
179 { "help", 0, 0, 'h' },
180
181 { "file", gFlag_argReq, 0, 'f' },
182 { "dump", 0, 0, 'd' },
183 { "check", 0, 0, 'k' },
184
185 { "output", gFlag_argReq, 0, 'o' },
186 { "columns", 0, 0, '|' },
187 { "rows", 0, 0, '-' },
188 { "nohead", 0, 0, 'n' },
189
190 { "host", gFlag_argReq, 0, 'H' },
191 { "from", gFlag_argReq, 0, 'F' },
192 { "to", gFlag_argReq, 0, 'T' },
193 { "command", gFlag_argReq, 0, 'C' },
194
195 { "and", 0, 0, '&' },
196 { "or", 0, 0, '|' },
197 { "not", 0, 0, '!' },
198
199 { 0, 0, 0, 0 }
200 };
201
202again:
203 opt = mdwopt(ac, av, "-", opts, 0, 0, gFlag_noShorts);
204
205 switch (opt) {
206 case 'h':
207 printf(""
208"Usage: %s [-help]\n"
209" %s [-output COLS] [-dump] [-file FILE] [EXPR | -check]\n"
210"\n"
211"Reads the `become' configuration file FILE (or " file_RULES " by\n"
212"default) and writes the rules which match the EXPR.\n"
213"\n"
214"EXPR may make use of the following operators: `-host HOST', `-from USER',\n"
215"`-to USER', and `-command CMD'. You may join them together with `-and',\n"
216"`-or' and `-not' operators (which may be spelled `&', `|' and `!' if you\n"
217"prefer), and group subexpressions with parentheses `(' and `)'.\n",
218 quis(), quis());
219 exit(0);
220 case 'd':
221 flags |= f_dump;
222 goto again;
223 case 'f':
224 cf = optarg;
225 goto again;
226 case '|':
227 flags |= f_simple;
228 /* Drop through */
229 case '-':
230 flags |= f_force;
231 goto again;
232 case 'k':
233 flags |= f_check;
234 goto again;
235 case 'n':
236 flags |= f_nohead;
237 goto again;
238 case 'o': {
239 char *p = optarg;
240 enum { m_replace, m_add, m_remove } mode = m_replace;
241 unsigned bit;
242
243 while (*p) {
244 switch (*p) {
245 case '+':
246 mode = m_add;
247 break;
248 case '-':
249 mode = m_remove;
250 break;
251 case 'h':
252 bit = cat_where;
253 goto setbits;
254 case 'f':
255 bit = cat_from;
256 goto setbits;
257 case 't':
258 bit = cat_to;
259 goto setbits;
260 case 'c':
261 bit = cat_what;
262 goto setbits;
263 default:
264 die("unknown column specifier `%c'", *p);
265 break;
266 setbits:
267 if (mode == m_replace) {
268 outmask = 0;
269 mode = m_add;
270 }
271 if (mode == m_add)
272 outmask |= bit;
273 else if (mode == m_remove)
274 outmask &= ~bit;
275 else
276 die("bad mode while setting output mask: %u", mode);
277 break;
278 }
279 p++;
280 }
281 goto again;
282 }
283 case '?':
284 die("type `%s --help' for usage information", quis());
285 case 0:
286 if (optarg[0] && optarg[1] == 0) switch (optarg[0]) {
287 case '(': case ')':
288 case '&': case '|': case '!':
289 opt = optarg[0];
290 break;
291 }
292 if (!opt)
293 die("unexpected text `%s' found", optarg);
294 break;
295 }
296
297 return (opt);
298}
299
300/*----- Recursive descent query parser ------------------------------------*/
301
302/* --- @qparse@ --- *
303 *
304 * Arguments: ---
305 *
306 * Returns: A pointer to the finished tree.
307 *
308 * Use: Scans the command line arguments and makes them into a tree.
309 */
310
311static qnode *qparse_expr(void);
312
313static qnode *qparse_atom(void)
314{
315 switch (opt) {
316 case '(': {
317 qnode *q;
318 nextopt();
319 q = qparse_expr();
320 if (opt != ')')
321 die("syntax error: expected `)', found `%s'", optname());
322 nextopt();
323 return (q);
324 }
325 case 'H': {
326 struct hostent *h;
327 qnode *q = xmalloc(sizeof(*q));
328 h = gethostbyname(optarg);
329 if (!h)
330 die("unknown host `%s'", optarg);
331 q->q_cat = cat_where;
7df38e1e 332 memcpy(&q->q_in, h->h_addr, sizeof(q->q_in));
908a5930 333 nextopt();
334 return (q);
335 }
336 case 'F': case 'T': {
337 qnode *q = xmalloc(sizeof(*q));
338 q->q_cat = (opt == 'F' ? cat_from : cat_to);
339 if (isdigit((unsigned char)optarg[0]))
340 q->q_uid = atoi(optarg);
341 else {
342 struct passwd *pw;
343 if (!(flags & f_userdb)) {
344 userdb_init();
345 userdb_local();
346 userdb_yp();
347 flags |= f_userdb;
348 }
349 pw = userdb_userByName(optarg);
350 if (!pw)
351 die("unknown user `%s'", optarg);
352 q->q_uid = pw->pw_uid;
353 }
354 nextopt();
355 return (q);
356 }
357 case 'C': {
358 qnode *q = xmalloc(sizeof(*q));
359 q->q_cat = cat_what;
360 q->q_cmd = optarg;
361 nextopt();
362 return (q);
363 }
364 default:
365 die("unexpected token: `%s'", optname());
366 }
367 return (0);
368}
369
370static qnode *qparse_factor(void)
371{
372 if (opt == '!') {
373 qnode *q = xmalloc(sizeof(*q));
374 nextopt();
375 q->q_cat = cat_not;
376 q->q_arg = qparse_atom();
377 return (q);
378 } else
379 return (qparse_atom());
380}
381
382static qnode *qparse_term(void)
383{
384 qnode *top, *q, **qq;
385 qq = &top;
386
387again:
388 q = qparse_factor();
389 switch (opt) {
390 case '&':
391 nextopt();
392 case 'H': case 'F': case 'T': case 'C': case '!': case '(':
393 *qq = xmalloc(sizeof(*q));
394 (*qq)->q_cat = cat_and;
395 (*qq)->q_left = q;
396 qq = &(*qq)->q_right;
397 goto again;
398 default:
399 *qq = q;
400 break;
401 }
402 return (top);
403}
404
405static qnode *qparse_expr(void)
406{
407 qnode *top, *q, **qq;
408 qq = &top;
409
410again:
411 q = qparse_term();
412 switch (opt) {
413 case '|':
414 nextopt();
415 *qq = xmalloc(sizeof(*q));
416 (*qq)->q_cat = cat_or;
417 (*qq)->q_left = q;
418 qq = &(*qq)->q_right;
419 goto again;
420 default:
421 *qq = q;
422 break;
423 }
424 return (top);
425}
426
427static qnode *qparse(void)
428{
429 qnode *q;
430 nextopt();
431 if (opt == EOF)
432 return (0);
433 q = qparse_expr();
434 if (opt != EOF)
435 die("syntax error: `%s' unexpected", optname());
436 return (q);
437}
438
439/* --- @dumptree@ --- *
440 *
441 * Arguments: @qnode *q@ = pointer to tree to dump
442 * @int indent@ = indentation for this subtree
443 *
444 * Returns: ---
445 *
446 * Use: Dumps a tree to stdout for debugging purposes.
447 */
448
449static void dumptree(qnode *q, int indent)
450{
451 if (!q)
452 printf("<empty> -- magic query which matches everything\n");
453
454again:
455 printf("%*s", indent * 2, "");
456 indent++;
457 switch (q->q_cat) {
458 case cat_where:
459 printf("host = %s\n", inet_ntoa(q->q_in));
460 break;
461 case cat_from:
462 printf("from = %u\n", (unsigned)q->q_uid);
463 break;
464 case cat_to:
465 printf("to = %u\n", (unsigned)q->q_uid);
466 break;
467 case cat_what:
468 printf("command = `%s'\n", q->q_cmd);
469 break;
470 case cat_not:
471 printf("not\n");
472 q = q->q_arg;
473 goto again;
474 case cat_and:
475 case cat_or: {
476 unsigned cat = q->q_cat;
477 printf(cat == cat_and ? "and\n" : "or\n");
478 while (q->q_cat == cat) {
479 dumptree(q->q_left, indent);
480 q = q->q_right;
481 }
482 goto again;
483 }
484 default:
485 printf("unknown type %u\n", q->q_cat);
486 }
487}
488
489/*----- Recursive query matching ------------------------------------------*/
490
491/* --- @checkrule@ --- *
492 *
493 * Arguments: @rule *r@ = pointer to a rule
494 * @qnode *q@ = pointer to a query tree
495 *
496 * Returns: Nonzero if the query matches the rule.
497 *
498 * Use: Matches rules and queries.
499 */
500
501static int checkrule(rule *r, qnode *q)
502{
503again:
504 switch (q->q_cat) {
505
506 /* --- Handle the compound query types --- */
507
508 case cat_not:
509 return (!checkrule(r, q->q_arg));
510
511 case cat_and:
512 if (!checkrule(r, q->q_left))
513 return (0);
514 q = q->q_right;
515 goto again;
516
517 case cat_or:
518 if (checkrule(r, q->q_left))
519 return (1);
520 q = q->q_right;
521 goto again;
522
523 /* --- And now the simple query types --- */
524
525 case cat_where:
526 return (class_matchHost(r->host, q->q_in));
527 case cat_from:
528 return (class_matchUser(r->from, q->q_uid));
529 case cat_to:
530 return (class_matchUser(r->to, q->q_uid));
531 case cat_what:
532 return (class_matchCommand(r->cmd, q->q_cmd));
533 }
534
535 /* --- Anything else is bogus (and a bug) --- */
536
537 die("unexpected cat code %u in checkrule", q->q_cat);
538 return (-1);
539}
540
541/*----- Rule output -------------------------------------------------------*/
542
543/* --- @showrule@ --- *
544 *
545 * Arguments: @rule *r@ = pointer to a rule block
546 *
547 * Returns: ---
548 *
549 * Use: Writes a rule block to the output in a pleasant way.
550 */
551
552static const char *xltuser(uid_t u)
553{
554 static char buf[16];
555 struct passwd *pw = userdb_userById(u);
556 if (pw)
557 return (pw->pw_name);
558 sprintf(buf, "%u", (unsigned)u);
559 return (buf);
560}
561
562static void classfirstrow(class_node *c, const char *fmt, sym_iter *i,
563 unsigned bit, unsigned *imask)
564{
565 switch (c->type & clNode_mask) {
566 case clNode_any:
567 printf(fmt, (c == class_all ? "ALL" :
568 c == class_none ? "NONE" :
569 "<bug>"));
570 break;
571 case clNode_immed:
572 printf(fmt, (c->type & clType_user) ? xltuser(c->v.u) : c->v.s);
573 break;
574 case clNode_hash: {
575 sym_base *b;
576 sym_createIter(i, &c->v.t);
577 b = sym_next(i);
578 if (!b) {
579 printf(fmt, "");
580 break;
581 } else if (c->type & clType_user)
582 printf(fmt, xltuser(*(uid_t *)b->name));
583 else
584 printf(fmt, b->name);
585 *imask |= bit;
586 } break;
587 default:
588 printf(fmt, "<complex>");
589 break;
590 }
591}
592
593static void showclass(class_node *c,
594 void (*sc)(class_node *c),
595 void (*sh)(sym_base *b))
596{
597 const char *op;
598 unsigned type;
599
600 switch (c->type & clNode_mask) {
601 case clNode_any:
602 fputs(c == class_all ? "ALL" :
603 c == class_none ? "NONE" : "<buggy>",
604 stdout);
605 break;
606 case clNode_immed:
607 sc(c);
608 break;
609 case clNode_hash: {
610 sym_iter i;
611 sym_base *b;
612 sym_createIter(&i, &c->v.t);
613 fputc('(', stdout);
614 if ((b = sym_next(&i)) != 0) {
615 sh(b);
616 while ((b = sym_next(&i)) != 0) {
617 fputs(", ", stdout);
618 sh(b);
619 }
620 }
621 fputc(')', stdout);
622 } break;
623 case clNode_union:
624 op = " | ";
625 goto binop;
626 case clNode_diff:
627 op = " - ";
628 goto binop;
629 case clNode_isect:
630 op = " & ";
631 goto binop;
632 default:
633 fputs("<unknown node type>", stdout);
634 break;
635 binop:
636 type = c->type & clNode_mask;
637 fputc('(', stdout);
638 do {
639 showclass(c->v.c.l, sc, sh);
640 fputs(op, stdout);
641 c = c->v.c.r;
642 } while ((c->type & clNode_mask) == type);
643 showclass(c, sc, sh);
644 fputc(')', stdout);
645 break;
646 }
647}
648
649static void showuseri(class_node *c) { fputs(xltuser(c->v.u), stdout); }
650
651static void showuserh(sym_base *b)
652{
653 fputs(xltuser(*(uid_t *)b->name), stdout);
654}
655
656static void showstringi(class_node *c) { fputs(c->v.s, stdout); }
657
658static void showstringh(sym_base *b) { fputs(b->name, stdout); }
659
660static void showrule(rule *r)
661{
662 /* --- First up: display of simple classes in columns --- */
663
664 if (flags & f_simple) {
665 sym_iter a, b, c, d;
666 sym_base *w = 0, *x = 0, *y = 0, *z = 0;
667 unsigned imask = 0;
668
669 /* --- Print the header line if necessary --- */
670
671 if (!(flags & f_header)) {
672 if (!(flags & f_nohead)) {
673 if (outmask & cat_from) printf("%-15s ", "FROM");
674 if (outmask & cat_to) printf("%-15s ", "TO");
675 if (outmask & cat_where) printf("%-24s ", "HOST");
676 if (outmask & cat_what) printf("%s", "COMMAND");
677 fputc('\n', stdout);
678 fputc('\n', stdout);
679 }
680 flags |= f_header;
681 } else
682 fputc('\n', stdout);
683
684 /* --- Print out the first row --- */
685
686 if (outmask & cat_from)
687 classfirstrow(r->from, "%-15.15s ", &a, cat_from, &imask);
688 if (outmask & cat_to)
689 classfirstrow(r->to, "%-15.15s ", &b, cat_to, &imask);
690 if (outmask & cat_where)
691 classfirstrow(r->host, "%-24.24s ", &c, cat_where, &imask);
692 if (outmask & cat_what)
693 classfirstrow(r->cmd, "%s", &d, cat_what, &imask);
694 fputc('\n', stdout);
695
696 /* --- And now for the rest --- */
697
698 for (;;) {
699 if ((imask & cat_from) && (w = sym_next(&a)) == 0)
700 imask &= ~cat_from;
701 if ((imask & cat_to) && (x = sym_next(&b)) == 0)
702 imask &= ~cat_to;
703 if ((imask & cat_where) && (y = sym_next(&c)) == 0)
704 imask &= ~cat_where;
705 if ((imask & cat_what) && (z = sym_next(&d)) == 0)
706 imask &= ~cat_what;
707
708 if (!imask)
709 break;
710
711 if (outmask & cat_from) {
712 printf("%-15.15s ",
713 !(imask & cat_from) ? "" : xltuser(*(uid_t *)w->name));
714 }
715
716 if (outmask & cat_to) {
717 printf("%-15.15s ",
718 !(imask & cat_to) ? "" : xltuser(*(uid_t *)x->name));
719 }
720
721 if (outmask & cat_where)
722 printf("%-24.24s ", !(imask & cat_where) ? "" : y->name);
723
724 if (outmask & cat_what)
725 printf("%s", !(imask & cat_what) ? "" : z->name);
726
727 fputc('\n', stdout);
728 }
729 }
730
731 /* --- Otherwise deal with complex cases --- */
732
733 else {
734 if (flags & f_header)
735 fputc('\n', stdout);
736 else
737 flags |= f_header;
738 if (outmask & cat_from) {
739 fputs(" From: ", stdout);
740 showclass(r->from, showuseri, showuserh);
741 fputc('\n', stdout);
742 }
743 if (outmask & cat_to) {
744 fputs(" To: ", stdout);
745 showclass(r->to, showuseri, showuserh);
746 fputc('\n', stdout);
747 }
748 if (outmask & cat_where) {
749 fputs(" Hosts: ", stdout);
750 showclass(r->host, showstringi, showstringh);
751 fputc('\n', stdout);
752 }
753 if (outmask & cat_what) {
754 fputs("Commands: ", stdout);
755 showclass(r->cmd, showstringi, showstringh);
756 fputc('\n', stdout);
757 }
758 }
759}
760
761/*----- Dummy functions ---------------------------------------------------*/
762
763void daemon_usePort(int p) { ; }
764void daemon_readKey(const char *f) { ; }
765
766/*----- Main code ---------------------------------------------------------*/
767
768/* --- @main@ --- *
769 *
770 * Arguments: @int argc@ = number of command line arguments
771 * @char *argv[]@ = pointer to command line arguments
772 *
773 * Returns: Zero if all went OK.
774 *
775 * Use: Verifies and queries the `become' configuration file.
776 */
777
778int main(int argc, char *argv[])
779{
780 qnode *qtree;
781
782 /* --- Initialise things --- */
783
784 ego(argv[0]);
785 ac = argc; av = argv;
786
787 /* --- Read the query tree --- */
788
789 qtree = qparse();
790
791 /* --- Dump the tree if so requested --- */
792
793 if (flags & f_dump) {
794 dumptree(qtree, 0);
795 return (0);
796 }
797
798 /* --- Check columns requested --- */
799
800 if (outmask == (outmask & (outmask - 1)))
801 flags |= f_single;
802
803 /* --- Read the ruleset --- */
804
805 if (!(flags & f_userdb)) {
806 userdb_init();
807 userdb_local();
808 userdb_yp();
809 }
810
811 netg_init();
812 name_init();
813 rule_init();
814
815 {
816 FILE *fp = fopen(cf, "r");
817 int ok;
818
819 if (!fp)
820 die("couldn't open configuration file `%s': %s", cf, strerror(errno));
821 lexer_scan(fp);
822 ok = parse();
823 if (flags & f_check)
824 exit(ok);
825 }
826
827 /* --- Now scan the query --- */
828
829 {
830 rule *rl = rule_list(), *r;
831
832 /* --- Decide on output format if not already chosen --- */
833
834 if (!(flags & f_force)) {
835 r = rl;
836 flags |= f_simple;
837 while (r) {
838 if ((!qtree || checkrule(r, qtree)) &&
839 ((r->host->type & clNode_mask) >= clNode_binop ||
840 (r->from->type & clNode_mask) >= clNode_binop ||
841 (r->to->type & clNode_mask) >= clNode_binop ||
842 (r->cmd->type & clNode_mask) >= clNode_binop)) {
843 flags &= ~f_simple;
844 break;
845 }
846 r = r->next;
847 }
848 }
849
850 /* --- Now just dump the matching items --- */
851
852 r = rl;
853 while (r) {
854 if (!qtree || checkrule(r, qtree)) {
855 flags |= f_match;
856 showrule(r);
857 }
858 r = r->next;
859 }
860 }
861
862 /* --- Done --- */
863
864 if (!(flags & f_match))
865 die("no match");
866 return (0);
867}
868
869/*----- That's all, folks -------------------------------------------------*/