Projet

Général

Profil

Télécharger (12,4 ko) Statistiques
| Branche: | Tag: | Révision:

univnautes / etc / inc / filter_log.inc @ 9036e766

1
<?php
2
/* $Id$ */
3
/*
4
	filter_log.inc
5
	part of pfSesne by Scott Ullrich
6
	originally based on m0n0wall (http://m0n0.ch/wall)
7

    
8
	Copyright (C) 2009 Jim Pingle <myfirstname>@<mylastname>.org
9
	All rights reserved.
10

    
11
	Redistribution and use in source and binary forms, with or without
12
	modification, are permitted provided that the following conditions are met:
13

    
14
	1. Redistributions of source code must retain the above copyright notice,
15
	   this list of conditions and the following disclaimer.
16

    
17
	2. Redistributions in binary form must reproduce the above copyright
18
	   notice, this list of conditions and the following disclaimer in the
19
	   documentation and/or other materials provided with the distribution.
20

    
21
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
22
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
23
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
25
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
	POSSIBILITY OF SUCH DAMAGE.
31
*/
32
/*
33
	pfSense_BUILDER_BINARIES:	/usr/sbin/fifolog_reader	/usr/bin/tail	/usr/local/sbin/clog
34
	pfSense_MODULE:	filter
35
*/
36

    
37
require 'config.inc';
38

    
39
/* format filter logs */
40
function conv_log_filter($logfile, $nentries, $tail = 50, $filtertext = "", $filterinterface = null) {
41
	global $config, $g;
42

    
43
	/* Make sure this is a number before using it in a system call */
44
	if (!(is_numeric($tail)))
45
		return;
46

    
47
	if ($filtertext)
48
		$tail = 5000;
49

    
50
	/* FreeBSD 8 splits pf log lines into two lines, so we need to at least
51
	 * tail twice as many, plus some extra to account for unparseable lines */
52
	$tail = $tail * 2 + 50;
53

    
54
	/* Always do a reverse tail, to be sure we're grabbing the 'end' of the log. */
55
	$logarr = "";
56

    
57
	if(isset($config['system']['usefifolog']))
58
		exec("/usr/sbin/fifolog_reader " . escapeshellarg($logfile) . " | /usr/bin/tail -r -n {$tail}", $logarr);
59
	else
60
		exec("/usr/local/sbin/clog " . escapeshellarg($logfile) . " | grep -v \"CLOG\" | grep -v \"\033\" | /usr/bin/tail -r -n {$tail}", $logarr);
61

    
62
	$filterlog = array();
63
	$counter = 0;
64

    
65
	$logarr = array_reverse(collapse_filter_lines(array_reverse($logarr)));
66
	$filterinterface = strtoupper($filterinterface);
67
	foreach ($logarr as $logent) {
68
		if($counter >= $nentries)
69
			break;
70

    
71
		$flent = parse_filter_line($logent);
72
		if (!$filterinterface || ($filterinterface == $flent['interface']))
73
		{
74
			if ( ( ($flent != "") && (!is_array($filtertext)) && (match_filter_line ($flent, $filtertext))) || 
75
			     ( ($flent != "") && ( is_array($filtertext)) && (match_filter_field($flent, $filtertext)) ) ) {
76
				$counter++;
77
				$filterlog[] = $flent;
78
			}
79
		}
80
	}
81
	/* Since the lines are in reverse order, flip them around if needed based on the user's preference */
82
	return isset($config['syslog']['reverse']) ? $filterlog : array_reverse($filterlog);
83
}
84

    
85
function match_filter_line($flent, $filtertext = "") {
86
	if (!$filtertext)
87
		return true;
88
	$filtertext = str_replace(' ', '\s+', $filtertext);
89
	return preg_match("/{$filtertext}/i", implode(" ", array_values($flent)));
90
}
91

    
92
function match_filter_field($flent, $fields) {
93
	foreach ($fields as $key => $field) {
94
		if ($field == "All")
95
			continue;
96
		if ((strpos($field, '!') === 0)) {
97
			$field = substr($field, 1);
98
			if (strtolower($key) == 'act') {
99
				if (in_arrayi($flent[$key], explode(" ", $field)))
100
					return false;
101
			} else {
102
				if (@preg_match("/{$field}/i", $flent[$key]))
103
					return false;
104
			}
105
		} else {
106
			if (strtolower($key) == 'act') {
107
				if (!in_arrayi($flent[$key], explode(" ", $field)))
108
					return false;
109
			} else {
110
				if (!@preg_match("/{$field}/i", $flent[$key]))
111
					return false;
112
			}
113
		}
114
	}
115
	return true;
116
}
117

    
118
// Case Insensitive in_array function
119
function in_arrayi($needle, $haystack) {
120
    return in_array(strtolower($needle), array_map('strtolower', $haystack));
121
}
122

    
123
function collapse_filter_lines($logarr) {
124
	$lastline = "";
125
	$collapsed = array();
126
	/* Stick a blank entry at the end to be sure we always fully parse the last entry */
127
	$logarr[] = "";
128
	foreach ($logarr as $logent) {
129
		$line_split = "";
130
		preg_match("/.*\spf:\s(.*)/", $logent, $line_split);
131
		if (substr($line_split[1], 0, 4) != "    ") {
132
			if (($lastline != "") && (substr($lastline, 0, 1) != " ")) {
133
				$collapsed[] = $lastline;
134
			}
135
			$lastline = $logent;
136
		} else {
137
			$lastline .= substr($line_split[1], 3);
138
		}
139
	}
140
	//print_r($collapsed);
141
	return $collapsed;
142
}
143

    
144
function parse_filter_line($line) {
145
	global $config, $g;
146
	$log_split = "";
147
	preg_match("/(.*)\s(.*)\spf:\s.*\srule\s(.*)\(match\)\:\s(.*)\s(\w+)\son\s(\w+)\:\s\((.*)\)\s(.*)\s>\s(.*)\:\s(.*)/", $line, $log_split);
148

    
149
	list($all, $flent['time'], $host, $rule, $flent['act'], $flent['direction'], $flent['realint'], $details, $src, $dst, $leftovers) = $log_split;
150

    
151
	list($flent['srcip'], $flent['srcport']) = parse_ipport($src);
152
	list($flent['dstip'], $flent['dstport']) = parse_ipport($dst);
153

    
154
	$flent['src'] = $flent['srcip'];
155
	$flent['dst'] = $flent['dstip'];
156

    
157
	if ($flent['srcport'])
158
		$flent['src'] .= ':' . $flent['srcport'];
159
	if ($flent['dstport'])
160
		$flent['dst'] .= ':' . $flent['dstport'];
161

    
162
	$flent['interface']  = convert_real_interface_to_friendly_descr($flent['realint']);
163

    
164
	$tmp = explode("/", $rule);
165
	$flent['rulenum'] = $tmp[0];
166

    
167
	$proto = array(" ", "(?)");
168
	/* Attempt to determine the protocol, based on several possible patterns.
169
	 * The value returned by strpos() must be strictly checkeded against the
170
	 * boolean FALSE because it could return a valid answer of 0 upon success. */
171
	if (!(strpos($details, 'proto ') === FALSE)) {
172
		preg_match("/.*\sproto\s(.*)\s\(/", $details, $proto);
173
	} elseif (!(strpos($details, 'next-header ') === FALSE)) {
174
		preg_match("/.*\snext-header\s(.*)\s\(/", $details, $proto);
175
	} elseif (!(strpos($details, 'proto: ') === FALSE)) {
176
		preg_match("/.*\sproto\:(.*)\s\(/", $details, $proto);
177
	} elseif (!(strpos($leftovers, 'sum ok] ') === FALSE)) {
178
		preg_match("/.*\ssum ok]\s(.*)\,\s.*/", $leftovers, $proto);
179
	} elseif (!(strpos($line, 'sum ok] ') === FALSE)) {
180
		preg_match("/.*\ssum ok]\s(.*)\,\s.*/", $line, $proto);
181
	}
182
	$proto = explode(" ", trim($proto[1]));
183
	$flent['proto'] = rtrim($proto[0], ",");
184

    
185
	/* If we're dealing with TCP, try to determine the flags/control bits */
186
	$flent['tcpflags'] = "";
187
	if ($flent['proto'] == "TCP") {
188
		$flags = preg_split('/[, ]/', $leftovers);
189
		$flent['tcpflags'] = str_replace(".", "A", substr($flags[1], 1, -1));
190
	} elseif ($flent['proto'] == "Options") {
191
		/* Then there must be some info we missed */
192
		if (!(strpos($leftovers, 'sum ok] ') === FALSE)) {
193
			preg_match("/.*\ssum ok]\s(.*)\,\s.*/", $leftovers, $proto);
194
		} elseif (!(strpos($line, 'sum ok] ') === FALSE)) {
195
			preg_match("/.*\ssum ok]\s(.*)\,\s.*/", $line, $proto);
196
		}
197
		$proto = explode(" ", trim($proto[1]));
198
		$flent['proto'] = rtrim($proto[0], ",");
199
		/* If it's still 'Options', then just ignore it. */
200
		if ($flent['proto'] == "Options")
201
			$flent['proto'] = "none";
202
	} elseif (($flent['proto'] == "unknown") && (!(strpos($line, ':  pfsync') === FALSE))) {
203
		$flent['proto'] = "PFSYNC";
204
	}
205

    
206
	/* If there is a src, a dst, and a time, then the line should be usable/good */
207
	if (!((trim($flent['src']) == "") || (trim($flent['dst']) == "") || (trim($flent['time']) == ""))) {
208
		return $flent;
209
	} else {
210
		if($g['debug']) {
211
			log_error(sprintf(gettext("There was a error parsing rule: %s.   Please report to mailing list or forum."), $errline));
212
		}
213
		return "";
214
	}
215
}
216

    
217
function parse_ipport($addr) {
218
	$addr = trim(rtrim($addr, ":"));
219
	if (substr($addr, 0, 4) == "kip ")
220
		$addr = substr($addr, 4);
221
	$port = '';
222
	if (substr_count($addr, '.') > 1) {
223
		/* IPv4 */
224
		$addr_split = explode(".", $addr);
225
		$ip = "{$addr_split[0]}.{$addr_split[1]}.{$addr_split[2]}.{$addr_split[3]}";
226

    
227
		if ($ip == "...")
228
			return array($addr, '');
229

    
230
		if($addr_split[4] != "") {
231
			$port_split = explode(":", $addr_split[4]);
232
			$port = $port_split[0];
233
		}
234
	} else {
235
		/* IPv6 */
236
		$addr = explode(" ", $addr);
237
		$addr = rtrim($addr[0], ":");
238
		$addr_split = explode(".", $addr);
239
		if (count($addr_split) > 1) {
240
			$ip   = $addr_split[0];
241
			$port = $addr_split[1];
242
		} else {
243
			$ip   = $addr;
244
		}
245
	}
246

    
247
	return array($ip, $port);
248
}
249

    
250
function get_port_with_service($port, $proto) {
251
	if (!$port)
252
		return '';
253

    
254
	$service = getservbyport($port, $proto);
255
	$portstr = "";
256
	if ($service) {
257
		$portstr = sprintf('<span title="' . gettext('Service %1$s/%2$s: %3$s') . '">' . htmlspecialchars($port) . '</span>', $port, $proto, $service);
258
	} else {
259
		$portstr = htmlspecialchars($port);
260
	}
261
	return ':' . $portstr;
262
}
263

    
264
function find_rule_by_number($rulenum, $type="rules") {
265
	/* Passing arbitrary input to grep could be a Very Bad Thing(tm) */
266
	if (!(is_numeric($rulenum)))
267
		return;
268
	/* At the moment, miniupnpd is the only thing I know of that
269
	   generates logging rdr rules */
270
	if ($type == "rdr")
271
		return `pfctl -vvsn -a "miniupnpd" | grep '^@{$rulenum} '`;
272
	else
273
		return `pfctl -vvsr | grep '^@{$rulenum} '`;
274
}
275

    
276
function buffer_rules_load() {
277
    global $buffer_rules_rdr, $buffer_rules_normal;
278
	$buffer = explode("\n",`pfctl -vvsn -a "miniupnpd" | grep '^@'`);
279
	foreach ($buffer as $line) {
280
		list($key, $value) = explode (" ", $line, 2);
281
		$buffer_rules_rdr[$key] = $value;
282
	}	
283
	$buffer = explode("\n",`pfctl -vvsr | grep '^@'`);
284
	foreach ($buffer as $line) {
285
		list($key, $value) = explode (" ", $line, 2);
286
		$buffer_rules_normal[$key] = $value;
287
	}	
288
}
289

    
290
function buffer_rules_clear() {
291
	unset($GLOBALS['buffer_rules_normal']);
292
	unset($GLOBALS['buffer_rules_rdr']);
293
}
294

    
295
function find_rule_by_number_buffer($rulenum, $type){
296
    global $g, $buffer_rules_rdr, $buffer_rules_normal;
297
	
298
	if ($type == "rdr")	{
299
		$ruleString = $buffer_rules_rdr["@".$rulenum];
300
		//TODO: get the correct 'description' part of a RDR log line. currently just first 30 characters..
301
		$rulename = substr($ruleString,0,30);
302
	} else {
303
		$ruleString = $buffer_rules_normal["@".$rulenum];
304
		list(,$rulename,) = explode("\"",$ruleString);
305
		$rulename = str_replace("USER_RULE: ",'<img src="/themes/'.$g['theme'].'/images/icons/icon_frmfld_user.png" width="11" height="12" title="USER_RULE" alt="USER_RULE"/> ',htmlspecialchars($rulename));
306
	}
307
	return $rulename." (@".$rulenum.")";
308
}
309

    
310
function find_action_image($action) {
311
	global $g;
312
	if ((strstr(strtolower($action), "p")) || (strtolower($action) == "rdr"))
313
		return "/themes/{$g['theme']}/images/icons/icon_pass.gif";
314
	else if(strstr(strtolower($action), "r"))
315
		return "/themes/{$g['theme']}/images/icons/icon_reject.gif";
316
	else
317
		return "/themes/{$g['theme']}/images/icons/icon_block.gif";
318
}
319

    
320
function is_first_row($rownum, $totalrows) {
321
	global $config;
322
	if(isset($config['syslog']['reverse'])) {
323
		/* Honor reverse logging setting */
324
		if($rownum == 0)
325
			return " id=\"firstrow\"";
326
	} else {
327
		/* non-reverse logging */
328
		if($rownum == $totalrows - 1)
329
			return " id=\"firstrow\"";
330
	}
331
	return "";
332
}
333

    
334
/* AJAX specific handlers */
335
function handle_ajax($nentries, $tail = 50) {
336
	global $config;
337
	if($_GET['lastsawtime'] or $_POST['lastsawtime']) {
338
		global $filter_logfile,$filterent;
339
		if($_GET['lastsawtime'])
340
			$lastsawtime = $_GET['lastsawtime'];
341
		if($_POST['lastsawtime'])
342
			$lastsawtime = $_POST['lastsawtime'];
343
		/*  compare lastsawrule's time stamp to filter logs.
344
		 *  afterwards return the newer records so that client
345
                 *  can update AJAX interface screen.
346
		 */
347
		$new_rules = "";
348
		$filterlog = conv_log_filter($filter_logfile, $nentries, $tail);
349
		/* We need this to always be in forward order for the AJAX update to work properly */
350
		$filterlog = isset($config['syslog']['reverse']) ? array_reverse($filterlog) : $filterlog;
351
		foreach($filterlog as $log_row) {
352
			$row_time = strtotime($log_row['time']);
353
			$img = "<img border='0' src='" . find_action_image($log_row['act']) . "' alt={$log_row['act']} title={$log_row['act']} />";
354
			if($row_time > $lastsawtime) {
355
				if ($log_row['proto'] == "TCP")
356
					$log_row['proto'] .= ":{$log_row['tcpflags']}";
357

    
358
				$img = "<a href=\"#\" onClick=\"javascript:getURL('diag_logs_filter.php?getrulenum={$log_row['rulenum']},{$log_row['rulenum']}', outputrule);\">{$img}</a>";
359
				$new_rules .= "{$img}||{$log_row['time']}||{$log_row['interface']}||{$log_row['srcip']}||{$log_row['dst']}||{$log_row['proto']}||" . time() . "||\n";
360
			}
361
		}
362
		echo $new_rules;
363
		exit;
364
	}
365
}
366

    
367
?>
(20-20/66)