1/*
2 * Copyright (C) 1993-2005  by Darren Reed.
3 * See the IPFILTER.LICENCE file for details on licencing.
4 */
5
6%{
7#include "ipf.h"
8#include <syslog.h>
9#undef	OPT_NAT
10#undef	OPT_VERBOSE
11#include "ipmon_l.h"
12#include "ipmon.h"
13
14#define	YYDEBUG	1
15
16extern	void	yyerror __P((char *));
17extern	int	yyparse __P((void));
18extern	int	yylex __P((void));
19extern	int	yydebug;
20extern	FILE	*yyin;
21extern	int	yylineNum;
22
23typedef	struct	opt	{
24	struct	opt	*o_next;
25	int		o_line;
26	int		o_type;
27	int		o_num;
28	char		*o_str;
29	struct in_addr	o_ip;
30} opt_t;
31
32static	void	build_action __P((struct opt *));
33static	opt_t	*new_opt __P((int));
34static	void	free_action __P((ipmon_action_t *));
35
36static	ipmon_action_t	*alist = NULL;
37%}
38
39%union	{
40	char	*str;
41	u_32_t	num;
42	struct in_addr	addr;
43	struct opt	*opt;
44	union	i6addr	ip6;
45}
46
47%token	<num>	YY_NUMBER YY_HEX
48%token	<str>	YY_STR
49%token	<ip6>	YY_IPV6
50%token	YY_COMMENT
51%token	YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT
52%token	YY_RANGE_OUT YY_RANGE_IN
53
54%token	IPM_MATCH IPM_BODY IPM_COMMENT IPM_DIRECTION IPM_DSTIP IPM_DSTPORT
55%token	IPM_EVERY IPM_EXECUTE IPM_GROUP IPM_INTERFACE IPM_IN IPM_NO IPM_OUT
56%token	IPM_PACKET IPM_PACKETS IPM_POOL IPM_PROTOCOL IPM_RESULT IPM_RULE
57%token	IPM_SECOND IPM_SECONDS IPM_SRCIP IPM_SRCPORT IPM_LOGTAG IPM_WITH
58%token	IPM_DO IPM_SAVE IPM_SYSLOG IPM_NOTHING IPM_RAW IPM_TYPE IPM_NAT
59%token	IPM_STATE IPM_NATTAG IPM_IPF
60%type	<addr> ipv4
61%type	<opt> direction dstip dstport every execute group interface
62%type	<opt> protocol result rule srcip srcport logtag matching
63%type	<opt> matchopt nattag type doopt doing save syslog nothing
64%type	<num> saveopts saveopt typeopt
65
66%%
67file:	line
68	| assign
69	| file line
70	| file assign
71	;
72
73line:	IPM_MATCH '{' matching '}' IPM_DO '{' doing '}' ';'
74					{ build_action($3); resetlexer(); }
75	| IPM_COMMENT
76	| YY_COMMENT
77	;
78
79assign:	YY_STR assigning YY_STR ';'		{ set_variable($1, $3);
80						  resetlexer();
81						  free($1);
82						  free($3);
83						}
84	;
85
86assigning:
87	'='					{ yyvarnext = 1; }
88	;
89
90matching:
91	matchopt				{ $$ = $1; }
92	| matchopt ',' matching			{ $1->o_next = $3; $$ = $1; }
93	;
94
95matchopt:
96	direction				{ $$ = $1; }
97	| dstip					{ $$ = $1; }
98	| dstport				{ $$ = $1; }
99	| every					{ $$ = $1; }
100	| group					{ $$ = $1; }
101	| interface				{ $$ = $1; }
102	| protocol				{ $$ = $1; }
103	| result				{ $$ = $1; }
104	| rule					{ $$ = $1; }
105	| srcip					{ $$ = $1; }
106	| srcport				{ $$ = $1; }
107	| logtag				{ $$ = $1; }
108	| nattag				{ $$ = $1; }
109	| type					{ $$ = $1; }
110	;
111
112doing:
113	doopt					{ $$ = $1; }
114	| doopt ',' doing			{ $1->o_next = $3; $$ = $1; }
115	;
116
117doopt:
118	execute					{ $$ = $1; }
119	| save					{ $$ = $1; }
120	| syslog				{ $$ = $1; }
121	| nothing				{ $$ = $1; }
122	;
123
124direction:
125	IPM_DIRECTION '=' IPM_IN		{ $$ = new_opt(IPM_DIRECTION);
126						  $$->o_num = IPM_IN; }
127	| IPM_DIRECTION '=' IPM_OUT		{ $$ = new_opt(IPM_DIRECTION);
128						  $$->o_num = IPM_OUT; }
129	;
130
131dstip:	IPM_DSTIP '=' ipv4 '/' YY_NUMBER	{ $$ = new_opt(IPM_DSTIP);
132						  $$->o_ip = $3;
133						  $$->o_num = $5; }
134	;
135
136dstport:
137	IPM_DSTPORT '=' YY_NUMBER		{ $$ = new_opt(IPM_DSTPORT);
138						  $$->o_num = $3; }
139	| IPM_DSTPORT '=' YY_STR		{ $$ = new_opt(IPM_DSTPORT);
140						  $$->o_str = $3; }
141	;
142
143every:	IPM_EVERY IPM_SECOND			{ $$ = new_opt(IPM_SECOND);
144						  $$->o_num = 1; }
145	| IPM_EVERY YY_NUMBER IPM_SECONDS	{ $$ = new_opt(IPM_SECOND);
146						  $$->o_num = $2; }
147	| IPM_EVERY IPM_PACKET			{ $$ = new_opt(IPM_PACKET);
148						  $$->o_num = 1; }
149	| IPM_EVERY YY_NUMBER IPM_PACKETS	{ $$ = new_opt(IPM_PACKET);
150						  $$->o_num = $2; }
151	;
152
153group:	IPM_GROUP '=' YY_NUMBER			{ $$ = new_opt(IPM_GROUP);
154						  $$->o_num = $3; }
155	| IPM_GROUP '=' YY_STR			{ $$ = new_opt(IPM_GROUP);
156						  $$->o_str = $3; }
157	;
158
159interface:
160	IPM_INTERFACE '=' YY_STR		{ $$ = new_opt(IPM_INTERFACE);
161						  $$->o_str = $3; }
162	;
163
164logtag:	IPM_LOGTAG '=' YY_NUMBER		{ $$ = new_opt(IPM_LOGTAG);
165						  $$->o_num = $3; }
166	;
167
168nattag:	IPM_NATTAG '=' YY_STR			{ $$ = new_opt(IPM_NATTAG);
169						  $$->o_str = $3; }
170	;
171
172protocol:
173	IPM_PROTOCOL '=' YY_NUMBER		{ $$ = new_opt(IPM_PROTOCOL);
174						  $$->o_num = $3; }
175	| IPM_PROTOCOL '=' YY_STR		{ $$ = new_opt(IPM_PROTOCOL);
176						  $$->o_num = getproto($3);
177						  free($3);
178						}
179	;
180
181result:	IPM_RESULT '=' YY_STR			{ $$ = new_opt(IPM_RESULT);
182						  $$->o_str = $3; }
183	;
184
185rule:	IPM_RULE '=' YY_NUMBER			{ $$ = new_opt(IPM_RULE);
186						  $$->o_num = YY_NUMBER; }
187	;
188
189srcip:	IPM_SRCIP '=' ipv4 '/' YY_NUMBER	{ $$ = new_opt(IPM_SRCIP);
190						  $$->o_ip = $3;
191						  $$->o_num = $5; }
192	;
193
194srcport:
195	IPM_SRCPORT '=' YY_NUMBER		{ $$ = new_opt(IPM_SRCPORT);
196						  $$->o_num = $3; }
197	| IPM_SRCPORT '=' YY_STR		{ $$ = new_opt(IPM_SRCPORT);
198						  $$->o_str = $3; }
199	;
200
201type:	IPM_TYPE '=' typeopt			{ $$ = new_opt(IPM_TYPE);
202						  $$->o_num = $3; }
203	;
204
205typeopt:
206	IPM_IPF					{ $$ = IPL_MAGIC; }
207	| IPM_NAT				{ $$ = IPL_MAGIC_NAT; }
208	| IPM_STATE				{ $$ = IPL_MAGIC_STATE; }
209	;
210
211execute:
212	IPM_EXECUTE YY_STR			{ $$ = new_opt(IPM_EXECUTE);
213						  $$->o_str = $2; }
214	;
215
216save:	IPM_SAVE saveopts YY_STR		{ $$ = new_opt(IPM_SAVE);
217						  $$->o_num = $2;
218						  $$->o_str = $3; }
219	;
220
221saveopts:					{ $$ = 0; }
222	| saveopt				{ $$ = $1; }
223	| saveopt ',' saveopts			{ $$ = $1 | $3; }
224	;
225
226saveopt:
227	IPM_RAW					{ $$ = IPMDO_SAVERAW; }
228	;
229
230syslog:	IPM_SYSLOG				{ $$ = new_opt(IPM_SYSLOG); }
231	;
232
233nothing:
234	IPM_NOTHING				{ $$ = 0; }
235	;
236
237ipv4:   YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER
238		{ if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) {
239			yyerror("Invalid octet string for IP address");
240			return 0;
241		  }
242		  $$.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7;
243		  $$.s_addr = htonl($$.s_addr);
244		}
245%%
246static	struct	wordtab	yywords[] = {
247	{ "body",	IPM_BODY },
248	{ "direction",	IPM_DIRECTION },
249	{ "do",		IPM_DO },
250	{ "dstip",	IPM_DSTIP },
251	{ "dstport",	IPM_DSTPORT },
252	{ "every",	IPM_EVERY },
253	{ "execute",	IPM_EXECUTE },
254	{ "group",	IPM_GROUP },
255	{ "in",		IPM_IN },
256	{ "interface",	IPM_INTERFACE },
257	{ "ipf",	IPM_IPF },
258	{ "logtag",	IPM_LOGTAG },
259	{ "match",	IPM_MATCH },
260	{ "nat",	IPM_NAT },
261	{ "nattag",	IPM_NATTAG },
262	{ "no",		IPM_NO },
263	{ "nothing",	IPM_NOTHING },
264	{ "out",	IPM_OUT },
265	{ "packet",	IPM_PACKET },
266	{ "packets",	IPM_PACKETS },
267	{ "protocol",	IPM_PROTOCOL },
268	{ "result",	IPM_RESULT },
269	{ "rule",	IPM_RULE },
270	{ "save",	IPM_SAVE },
271	{ "raw",	IPM_RAW },
272	{ "second",	IPM_SECOND },
273	{ "seconds",	IPM_SECONDS },
274	{ "srcip",	IPM_SRCIP },
275	{ "srcport",	IPM_SRCPORT },
276	{ "state",	IPM_STATE },
277	{ "syslog",	IPM_SYSLOG },
278	{ "with",	IPM_WITH },
279	{ NULL,		0 }
280};
281
282static int macflags[17][2] = {
283	{ IPM_DIRECTION,	IPMAC_DIRECTION	},
284	{ IPM_DSTIP,		IPMAC_DSTIP	},
285	{ IPM_DSTPORT,		IPMAC_DSTPORT	},
286	{ IPM_GROUP,		IPMAC_GROUP	},
287	{ IPM_INTERFACE,	IPMAC_INTERFACE	},
288	{ IPM_LOGTAG,		IPMAC_LOGTAG 	},
289	{ IPM_NATTAG,		IPMAC_NATTAG 	},
290	{ IPM_PACKET,		IPMAC_EVERY	},
291	{ IPM_PROTOCOL,		IPMAC_PROTOCOL	},
292	{ IPM_RESULT,		IPMAC_RESULT	},
293	{ IPM_RULE,		IPMAC_RULE	},
294	{ IPM_SECOND,		IPMAC_EVERY	},
295	{ IPM_SRCIP,		IPMAC_SRCIP	},
296	{ IPM_SRCPORT,		IPMAC_SRCPORT	},
297	{ IPM_TYPE,		IPMAC_TYPE 	},
298	{ IPM_WITH,		IPMAC_WITH 	},
299	{ 0, 0 }
300};
301
302static opt_t *new_opt(type)
303int type;
304{
305	opt_t *o;
306
307	o = (opt_t *)malloc(sizeof(*o));
308	if (o == NULL)
309		yyerror("sorry, out of memory");
310	o->o_type = type;
311	o->o_line = yylineNum;
312	o->o_num = 0;
313	o->o_str = (char *)0;
314	o->o_next = NULL;
315	return o;
316}
317
318static void build_action(olist)
319opt_t *olist;
320{
321	ipmon_action_t *a;
322	opt_t *o;
323	char c;
324	int i;
325
326	a = (ipmon_action_t *)calloc(1, sizeof(*a));
327	if (a == NULL)
328		return;
329	while ((o = olist) != NULL) {
330		/*
331		 * Check to see if the same comparator is being used more than
332		 * once per matching statement.
333		 */
334		for (i = 0; macflags[i][0]; i++)
335			if (macflags[i][0] == o->o_type)
336				break;
337		if (macflags[i][1] & a->ac_mflag) {
338			fprintf(stderr, "%s redfined on line %d\n",
339				yykeytostr(o->o_type), yylineNum);
340			if (o->o_str != NULL)
341				free(o->o_str);
342			olist = o->o_next;
343			free(o);
344			continue;
345		}
346
347		a->ac_mflag |= macflags[i][1];
348
349		switch (o->o_type)
350		{
351		case IPM_DIRECTION :
352			a->ac_direction = o->o_num;
353			break;
354		case IPM_DSTIP :
355			a->ac_dip = o->o_ip.s_addr;
356			a->ac_dmsk = htonl(0xffffffff << (32 - o->o_num));
357			break;
358		case IPM_DSTPORT :
359			a->ac_dport = htons(o->o_num);
360			break;
361		case IPM_EXECUTE :
362			a->ac_exec = o->o_str;
363			c = *o->o_str;
364			if (c== '"'|| c == '\'') {
365				if (o->o_str[strlen(o->o_str) - 1] == c) {
366					a->ac_run = strdup(o->o_str + 1);
367					a->ac_run[strlen(a->ac_run) - 1] ='\0';
368				} else
369					a->ac_run = o->o_str;
370			} else
371				a->ac_run = o->o_str;
372			o->o_str = NULL;
373			break;
374		case IPM_INTERFACE :
375			a->ac_iface = o->o_str;
376			o->o_str = NULL;
377			break;
378		case IPM_GROUP :
379			if (o->o_str != NULL)
380				strncpy(a->ac_group, o->o_str, FR_GROUPLEN);
381			else
382				sprintf(a->ac_group, "%d", o->o_num);
383			break;
384		case IPM_LOGTAG :
385			a->ac_logtag = o->o_num;
386			break;
387		case IPM_NATTAG :
388			strncpy(a->ac_nattag, o->o_str, sizeof(a->ac_nattag));
389			break;
390		case IPM_PACKET :
391			a->ac_packet = o->o_num;
392			break;
393		case IPM_PROTOCOL :
394			a->ac_proto = o->o_num;
395			break;
396		case IPM_RULE :
397			a->ac_rule = o->o_num;
398			break;
399		case IPM_RESULT :
400			if (!strcasecmp(o->o_str, "pass"))
401				a->ac_result = IPMR_PASS;
402			else if (!strcasecmp(o->o_str, "block"))
403				a->ac_result = IPMR_BLOCK;
404			else if (!strcasecmp(o->o_str, "nomatch"))
405				a->ac_result = IPMR_NOMATCH;
406			else if (!strcasecmp(o->o_str, "log"))
407				a->ac_result = IPMR_LOG;
408			break;
409		case IPM_SECOND :
410			a->ac_second = o->o_num;
411			break;
412		case IPM_SRCIP :
413			a->ac_sip = o->o_ip.s_addr;
414			a->ac_smsk = htonl(0xffffffff << (32 - o->o_num));
415			break;
416		case IPM_SRCPORT :
417			a->ac_sport = htons(o->o_num);
418			break;
419		case IPM_SAVE :
420			if (a->ac_savefile != NULL) {
421				fprintf(stderr, "%s redfined on line %d\n",
422					yykeytostr(o->o_type), yylineNum);
423				break;
424			}
425			a->ac_savefile = strdup(o->o_str);
426			a->ac_savefp = fopen(o->o_str, "a");
427			a->ac_dflag |= o->o_num & IPMDO_SAVERAW;
428			break;
429		case IPM_SYSLOG :
430			if (a->ac_syslog != 0) {
431				fprintf(stderr, "%s redfined on line %d\n",
432					yykeytostr(o->o_type), yylineNum);
433				break;
434			}
435			a->ac_syslog = 1;
436			break;
437		case IPM_TYPE :
438			a->ac_type = o->o_num;
439			break;
440		case IPM_WITH :
441			break;
442		default :
443			break;
444		}
445
446		olist = o->o_next;
447		if (o->o_str != NULL)
448			free(o->o_str);
449		free(o);
450	}
451	a->ac_next = alist;
452	alist = a;
453}
454
455
456int check_action(buf, log, opts, lvl)
457char *buf, *log;
458int opts, lvl;
459{
460	ipmon_action_t *a;
461	struct timeval tv;
462	ipflog_t *ipf;
463	tcphdr_t *tcp;
464	iplog_t *ipl;
465	int matched;
466	u_long t1;
467	ip_t *ip;
468
469	matched = 0;
470	ipl = (iplog_t *)buf;
471	ipf = (ipflog_t *)(ipl +1);
472	ip = (ip_t *)(ipf + 1);
473	tcp = (tcphdr_t *)((char *)ip + (IP_HL(ip) << 2));
474
475	for (a = alist; a != NULL; a = a->ac_next) {
476		if ((a->ac_mflag & IPMAC_DIRECTION) != 0) {
477			if (a->ac_direction == IPM_IN) {
478				if ((ipf->fl_flags & FR_INQUE) == 0)
479					continue;
480			} else if (a->ac_direction == IPM_OUT) {
481				if ((ipf->fl_flags & FR_OUTQUE) == 0)
482					continue;
483			}
484		}
485
486		if ((a->ac_type != 0) && (a->ac_type != ipl->ipl_magic))
487			continue;
488
489		if ((a->ac_mflag & IPMAC_EVERY) != 0) {
490			gettimeofday(&tv, NULL);
491			t1 = tv.tv_sec - a->ac_lastsec;
492			if (tv.tv_usec <= a->ac_lastusec)
493				t1--;
494			if (a->ac_second != 0) {
495				if (t1 < a->ac_second)
496					continue;
497				a->ac_lastsec = tv.tv_sec;
498				a->ac_lastusec = tv.tv_usec;
499			}
500
501			if (a->ac_packet != 0) {
502				if (a->ac_pktcnt == 0)
503					a->ac_pktcnt++;
504				else if (a->ac_pktcnt == a->ac_packet) {
505					a->ac_pktcnt = 0;
506					continue;
507				} else {
508					a->ac_pktcnt++;
509					continue;
510				}
511			}
512		}
513
514		if ((a->ac_mflag & IPMAC_DSTIP) != 0) {
515			if ((ip->ip_dst.s_addr & a->ac_dmsk) != a->ac_dip)
516				continue;
517		}
518
519		if ((a->ac_mflag & IPMAC_DSTPORT) != 0) {
520			if (ip->ip_p != IPPROTO_UDP && ip->ip_p != IPPROTO_TCP)
521				continue;
522			if (tcp->th_dport != a->ac_dport)
523				continue;
524		}
525
526		if ((a->ac_mflag & IPMAC_GROUP) != 0) {
527			if (strncmp(a->ac_group, ipf->fl_group,
528				    FR_GROUPLEN) != 0)
529				continue;
530		}
531
532		if ((a->ac_mflag & IPMAC_INTERFACE) != 0) {
533			if (strcmp(a->ac_iface, ipf->fl_ifname))
534				continue;
535		}
536
537		if ((a->ac_mflag & IPMAC_PROTOCOL) != 0) {
538			if (a->ac_proto != ip->ip_p)
539				continue;
540		}
541
542		if ((a->ac_mflag & IPMAC_RESULT) != 0) {
543			if ((ipf->fl_flags & FF_LOGNOMATCH) != 0) {
544				if (a->ac_result != IPMR_NOMATCH)
545					continue;
546			} else if (FR_ISPASS(ipf->fl_flags)) {
547				if (a->ac_result != IPMR_PASS)
548					continue;
549			} else if (FR_ISBLOCK(ipf->fl_flags)) {
550				if (a->ac_result != IPMR_BLOCK)
551					continue;
552			} else {	/* Log only */
553				if (a->ac_result != IPMR_LOG)
554					continue;
555			}
556		}
557
558		if ((a->ac_mflag & IPMAC_RULE) != 0) {
559			if (a->ac_rule != ipf->fl_rule)
560				continue;
561		}
562
563		if ((a->ac_mflag & IPMAC_SRCIP) != 0) {
564			if ((ip->ip_src.s_addr & a->ac_smsk) != a->ac_sip)
565				continue;
566		}
567
568		if ((a->ac_mflag & IPMAC_SRCPORT) != 0) {
569			if (ip->ip_p != IPPROTO_UDP && ip->ip_p != IPPROTO_TCP)
570				continue;
571			if (tcp->th_sport != a->ac_sport)
572				continue;
573		}
574
575		if ((a->ac_mflag & IPMAC_LOGTAG) != 0) {
576			if (a->ac_logtag != ipf->fl_logtag)
577				continue;
578		}
579
580		if ((a->ac_mflag & IPMAC_NATTAG) != 0) {
581			if (strncmp(a->ac_nattag, ipf->fl_nattag.ipt_tag,
582				    IPFTAG_LEN) != 0)
583				continue;
584		}
585
586		matched = 1;
587
588		/*
589		 * It matched so now execute the command
590		 */
591		if (a->ac_syslog != 0) {
592			syslog(lvl, "%s", log);
593		}
594
595		if (a->ac_savefp != NULL) {
596			if (a->ac_dflag & IPMDO_SAVERAW)
597				fwrite(ipl, 1, ipl->ipl_dsize, a->ac_savefp);
598			else
599				fputs(log, a->ac_savefp);
600		}
601
602		if (a->ac_exec != NULL) {
603			switch (fork())
604			{
605			case 0 :
606			{
607				FILE *pi;
608
609				pi = popen(a->ac_run, "w");
610				if (pi != NULL) {
611					fprintf(pi, "%s\n", log);
612					if ((opts & OPT_HEXHDR) != 0) {
613						dumphex(pi, 0, buf,
614							sizeof(*ipl) +
615							sizeof(*ipf));
616					}
617					if ((opts & OPT_HEXBODY) != 0) {
618						dumphex(pi, 0, (char *)ip,
619							ipf->fl_hlen +
620							ipf->fl_plen);
621					}
622					pclose(pi);
623				}
624				exit(1);
625			}
626			case -1 :
627				break;
628			default :
629				break;
630			}
631		}
632	}
633
634	return matched;
635}
636
637
638static void free_action(a)
639ipmon_action_t *a;
640{
641	if (a->ac_savefile != NULL) {
642		free(a->ac_savefile);
643		a->ac_savefile = NULL;
644	}
645	if (a->ac_savefp != NULL) {
646		fclose(a->ac_savefp);
647		a->ac_savefp = NULL;
648	}
649	if (a->ac_exec != NULL) {
650		free(a->ac_exec);
651		if (a->ac_run == a->ac_exec)
652			a->ac_run = NULL;
653		a->ac_exec = NULL;
654	}
655	if (a->ac_run != NULL) {
656		free(a->ac_run);
657		a->ac_run = NULL;
658	}
659	if (a->ac_iface != NULL) {
660		free(a->ac_iface);
661		a->ac_iface = NULL;
662	}
663	a->ac_next = NULL;
664	free(a);
665}
666
667
668int load_config(file)
669char *file;
670{
671	ipmon_action_t *a;
672	FILE *fp;
673	char *s;
674
675	s = getenv("YYDEBUG");
676	if (s != NULL)
677		yydebug = atoi(s);
678	else
679		yydebug = 0;
680
681	while ((a = alist) != NULL) {
682		alist = a->ac_next;
683		free_action(a);
684	}
685
686	yylineNum = 1;
687
688	(void) yysettab(yywords);
689
690	fp = fopen(file, "r");
691	if (!fp) {
692		perror("load_config:fopen:");
693		return -1;
694	}
695	yyin = fp;
696	while (!feof(fp))
697		yyparse();
698	fclose(fp);
699	return 0;
700}
701