Projet

Général

Profil

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

univnautes / etc / inc / filter_log.inc @ 481b97b0

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
	/* Always do a reverse tail, to be sure we're grabbing the 'end' of the log. */
51
	$logarr = "";
52

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

    
58
	$filterlog = array();
59
	$counter = 0;
60

    
61
	$filterinterface = strtoupper($filterinterface);
62
	foreach ($logarr as $logent) {
63
		if($counter >= $nentries)
64
			break;
65

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

    
80
function match_filter_line($flent, $filtertext = "") {
81
	if (!$filtertext)
82
		return true;
83
	$filtertext = str_replace(' ', '\s+', $filtertext);
84
	return preg_match("/{$filtertext}/i", implode(" ", array_values($flent)));
85
}
86

    
87
function match_filter_field($flent, $fields) {
88
	foreach ($fields as $field) {
89
		if ($fields[$field] == "All") continue;
90
		if ((strpos($fields[$field], '!') === 0)) {
91
			$fields[$field] = substr($fields[$field], 1);
92
			if (preg_match("/act/i", $field)) {
93
				if ( (in_arrayi($flent[$field], explode(",", str_replace(" ", ",", $fields[$field]))) ) ) return false;
94
			} else if ( (preg_match("/{$fields[$field]}/i", $flent[$field])) ) return false;
95
		}
96
		else {
97
			if (preg_match("/act/i", $field)) {
98
				if ( !(in_arrayi($flent[$field], explode(",", str_replace(" ", ",", $fields[$field]))) ) ) return false;
99
			} else if ( !(preg_match("/{$fields[$field]}/i", $flent[$field])) ) return false;
100
		}
101
	}	
102
	return true;
103
}
104

    
105
// Case Insensitive in_array function
106
function in_arrayi($needle, $haystack) {
107
	return in_array(strtolower($needle), array_map('strtolower', $haystack));
108
}
109

    
110
function parse_filter_line($line) {
111
	global $config, $g;
112

    
113
	$flent = array();
114
	$log_split = "";
115

    
116
	if (!preg_match("/(.*)\s(.*)\sfilterlog:\s(.*)$/", $line, $log_split))
117
		return "";
118

    
119
	list($all, $flent['time'], $host, $rule) = $log_split;
120

    
121
	$rule_data = explode(",", $rule);
122
	$field = 0;
123

    
124
	$flent['rulenum'] = $rule_data[$field++];
125
	$flent['subrulenum'] = $rule_data[$field++];
126
	$flent['anchor'] = $rule_data[$field++];
127
	$flent['tracker'] = $rule_data[$field++];
128
	$flent['realint'] = $rule_data[$field++];
129
	$flent['interface']  = convert_real_interface_to_friendly_descr($flent['realint']);
130
	$flent['reason'] = $rule_data[$field++];
131
	$flent['act'] = $rule_data[$field++];
132
	$flent['direction'] = $rule_data[$field++];
133
	$flent['version'] = $rule_data[$field++];
134

    
135
	if ($flent['version'] == '4' || $flent['version'] == '6') {
136
		if ($flent['version'] == '4') {
137
			$flent['tos'] = $rule_data[$field++];
138
			$flent['ecn'] = $rule_data[$field++];
139
			$flent['ttl'] = $rule_data[$field++];
140
			$flent['id'] = $rule_data[$field++];
141
			$flent['offset'] = $rule_data[$field++];
142
			$flent['flags'] = $rule_data[$field++];
143
			$flent['protoid'] = $rule_data[$field++];
144
			$flent['proto'] = strtoupper($rule_data[$field++]);
145
		} else {
146
			$flent['class'] = $rule_data[$field++];
147
			$flent['flowlabel'] = $rule_data[$field++];
148
			$flent['hlim'] = $rule_data[$field++];
149
			$flent['proto'] = $rule_data[$field++];
150
			$flent['protoid'] = $rule_data[$field++];
151
		}
152

    
153
		$flent['length'] = $rule_data[$field++];
154
		$flent['srcip'] = $rule_data[$field++];
155
		$flent['dstip'] = $rule_data[$field++];
156

    
157
		if ($flent['protoid'] == '6' || $flent['protoid'] == '17') { // TCP or UDP
158
			$flent['srcport'] = $rule_data[$field++];
159
			$flent['dstport'] = $rule_data[$field++];
160

    
161
			$flent['src'] = $flent['srcip'] . ':' . $flent['srcport'];
162
			$flent['dst'] = $flent['dstip'] . ':' . $flent['dstport'];
163

    
164
			$flent['datalen'] = $rule_data[$field++];
165
			if ($flent['protoid'] == '6') { // TCP
166
				$flent['tcpflags'] = $rule_data[$field++];
167
				$flent['seq'] = $rule_data[$field++];
168
				$flent['ack'] = $rule_data[$field++];
169
				$flent['window'] = $rule_data[$field++];
170
				$flent['urg'] = $rule_data[$field++];
171
				$flent['options'] = explode(";",$rule_data[$field++]);
172
			}
173
		} else if ($flent['protoid'] == '112') { // CARP
174
			$flent['type'] = $rule_data[$field++];
175
			$flent['ttl'] = $rule_data[$field++];
176
			$flent['vhid'] = $rule_data[$field++];
177
			$flent['version'] = $rule_data[$field++];
178
			$flent['advskew'] = $rule_data[$field++];
179
			$flent['advbase'] = $rule_data[$field++];
180
		}
181
	} else {
182
		if($g['debug'])
183
			log_error(sprintf(gettext("There was a error parsing rule number: %s.   Please report to mailing list or forum."), $flent['rulenum']));
184
		return "";
185
	}
186

    
187
	/* If there is a src, a dst, and a time, then the line should be usable/good */
188
	if (!((trim($flent['src']) == "") || (trim($flent['dst']) == "") || (trim($flent['time']) == ""))) {
189
		return $flent;
190
	} else {
191
		if($g['debug']) {
192
			log_error(sprintf(gettext("There was a error parsing rule: %s.   Please report to mailing list or forum."), $errline));
193
		}
194
		return "";
195
	}
196
}
197

    
198
function get_port_with_service($port, $proto) {
199
	if (!$port)
200
		return '';
201

    
202
	$service = getservbyport($port, $proto);
203
	$portstr = "";
204
	if ($service) {
205
		$portstr = sprintf('<span title="' . gettext('Service %1$s/%2$s: %3$s') . '">' . htmlspecialchars($port) . '</span>', $port, $proto, $service);
206
	} else {
207
		$portstr = htmlspecialchars($port);
208
	}
209
	return ':' . $portstr;
210
}
211

    
212
function find_rule_by_number($rulenum, $trackernum, $type="block") {
213
	global $g;
214

    
215
	/* Passing arbitrary input to grep could be a Very Bad Thing(tm) */
216
	if (!is_numeric($rulenum) || !is_numeric($trackernum) || !in_array($type, array('pass', 'block', 'match', 'rdr')))
217
		return;
218

    
219
	if ($trackernum == "0")
220
		$lookup_pattern = "^@{$rulenum}\([0-9]+\)[[:space:]]{$type}[[:space:]].*[[:space:]]log[[:space:]]";
221
	else
222
		$lookup_pattern = "^@[0-9]+\({$trackernum}\)[[:space:]]{$type}[[:space:]].*[[:space:]]log[[:space:]]";
223

    
224
	/* At the moment, miniupnpd is the only thing I know of that
225
	   generates logging rdr rules */
226
	unset($buffer);
227
	if ($type == "rdr")
228
		$_gb = exec("/sbin/pfctl -vvPsn -a \"miniupnpd\" | grep " . escapeshellarg("^@{$rulenum}"), $buffer);
229
	else {
230
		if (file_exists("{$g['tmp_path']}/rules.debug"))
231
			$_gb = exec("/sbin/pfctl -vvPnf {$g['tmp_path']}/rules.debug 2>/dev/null | /usr/bin/egrep " . escapeshellarg($lookup_pattern), $buffer);
232
		else
233
			$_gb = exec("/sbin/pfctl -vvPsr | grep " . escapeshellarg($lookup_pattern), $buffer);
234
	}
235
	if (is_array($buffer))
236
		return $buffer[0];
237

    
238
	return "";
239
}
240

    
241
function buffer_rules_load() {
242
	global $g, $buffer_rules_rdr, $buffer_rules_normal;
243

    
244
	unset($buffer, $buffer_rules_rdr, $buffer_rules_normal);
245
	$buffer_rules_rdr = array();
246
	$buffer_rules_normal = array();
247

    
248
	$_gb = exec("/sbin/pfctl -vvPsn -a \"miniupnpd\" | grep '^@'", $buffer);
249
	if (is_array($buffer)) {
250
		foreach ($buffer as $line) {
251
			list($key, $value) = explode (" ", $line, 2);
252
			$buffer_rules_rdr[$key] = $value;
253
		}
254
	}
255
	unset($buffer, $_gb);
256
	if (file_exists("{$g['tmp_path']}/rules.debug"))
257
		$_gb = exec("/sbin/pfctl -vvPnf {$g['tmp_path']}/rules.debug 2>/dev/null | /usr/bin/egrep '^@[0-9]+\([0-9]+\)[[:space:]].*[[:space:]]log[[:space:]]' | /usr/bin/egrep -v '^@[0-9]+\([0-9]+\)[[:space:]](nat|rdr|binat|no|scrub)'", $buffer);
258
	else
259
		$_gb = exec("/sbin/pfctl -vvPsr | /usr/bin/egrep '^@[0-9]+\([0-9]+\)[[:space:]].*[[:space:]]log[[:space:]]'", $buffer);
260

    
261
	if (is_array($buffer)) {
262
		foreach ($buffer as $line) {
263
			list($key, $value) = explode (" ", $line, 2);
264
			# pfctl rule number output with tracker number: @dd(dddddddddd)
265
			$matches = array();
266
			if (preg_match('/\@(?P<rulenum>\d+)\((?<trackernum>\d+)\)/', $key, $matches) == 1) {
267
				if ($matches['trackernum'] > 0)
268
					$key = $matches['trackernum'];
269
				else
270
					$key = "@{$matches['rulenum']}";
271
			}
272
			$buffer_rules_normal[$key] = $value;
273
		}
274
	}
275
	unset($_gb, $buffer);
276
}
277

    
278
function buffer_rules_clear() {
279
	unset($GLOBALS['buffer_rules_normal']);
280
	unset($GLOBALS['buffer_rules_rdr']);
281
}
282

    
283
function find_rule_by_number_buffer($rulenum, $trackernum, $type){
284
	global $g, $buffer_rules_rdr, $buffer_rules_normal;
285

    
286
	if ($trackernum == "0")
287
		$lookup_key = "@{$rulenum}";
288
	else
289
		$lookup_key = $trackernum;
290

    
291
	if ($type == "rdr")	{
292
		$ruleString = $buffer_rules_rdr[$lookup_key];
293
		//TODO: get the correct 'description' part of a RDR log line. currently just first 30 characters..
294
		$rulename = substr($ruleString,0,30);
295
	} else {
296
		$ruleString = $buffer_rules_normal[$lookup_key];
297
		list(,$rulename,) = explode("\"",$ruleString);
298
		$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"/> ',$rulename);
299
	}
300
	return "{$rulename} ({$lookup_key})";
301
}
302

    
303
function find_action_image($action) {
304
	global $g;
305
	if ((strstr(strtolower($action), "p")) || (strtolower($action) == "rdr"))
306
		return "/themes/{$g['theme']}/images/icons/icon_pass.gif";
307
	else if(strstr(strtolower($action), "r"))
308
		return "/themes/{$g['theme']}/images/icons/icon_reject.gif";
309
	else
310
		return "/themes/{$g['theme']}/images/icons/icon_block.gif";
311
}
312

    
313
/* AJAX specific handlers */
314
function handle_ajax($nentries, $tail = 50) {
315
	global $config;
316
	if($_GET['lastsawtime'] or $_POST['lastsawtime']) {
317
		global $filter_logfile,$filterent;
318
		if($_GET['lastsawtime'])
319
			$lastsawtime = $_GET['lastsawtime'];
320
		if($_POST['lastsawtime'])
321
			$lastsawtime = $_POST['lastsawtime'];
322
		/*  compare lastsawrule's time stamp to filter logs.
323
		 *  afterwards return the newer records so that client
324
		 *  can update AJAX interface screen.
325
		 */
326
		$new_rules = "";
327
		$filterlog = conv_log_filter($filter_logfile, $nentries, $tail);
328
		/* We need this to always be in forward order for the AJAX update to work properly */
329
		$filterlog = isset($config['syslog']['reverse']) ? array_reverse($filterlog) : $filterlog;
330
		foreach($filterlog as $log_row) {
331
			$row_time = strtotime($log_row['time']);
332
			$img = "<img border='0' src='" . find_action_image($log_row['act']) . "' alt={$log_row['act']} title={$log_row['act']} />";
333
			if($row_time > $lastsawtime) {
334
				if ($log_row['proto'] == "TCP")
335
					$log_row['proto'] .= ":{$log_row['tcpflags']}";
336

    
337
				$img = "<a href=\"#\" onClick=\"javascript:getURL('diag_logs_filter.php?getrulenum={$log_row['rulenum']},{$log_row['rulenum']}', outputrule);\">{$img}</a>";
338
				$new_rules .= "{$img}||{$log_row['time']}||{$log_row['interface']}||{$log_row['srcip']}||{$log_row['dst']}||{$log_row['proto']}||" . time() . "||\n";
339
			}
340
		}
341
		echo $new_rules;
342
		exit;
343
	}
344
}
345

    
346
?>
(20-20/67)