Project

General

Profile

Download (14.2 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	easyrule.inc
4

    
5
	Copyright (C) 2009-2010 Jim Pingle (jpingle@gmail.com)
6
	Originally Sponsored By Anathematic @ pfSense Forums
7
	All rights reserved.
8

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

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

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

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

    
35
$blockaliasname = 'EasyRuleBlockHosts';
36
$protocols_with_ports = array('tcp', 'udp');
37
require_once("functions.inc");
38
require_once("util.inc");
39
require_once("ipsec.inc");
40
require_once("config.inc");
41

    
42
function easyrule_find_rule_interface($int) {
43
	global $config;
44
	/* Borrowed from firewall_rules.php */
45
	$iflist = get_configured_interface_with_descr(false, true);
46

    
47
	if ($config['pppoe']['mode'] == "server") {
48
		$iflist['pppoe'] = "PPPoE Server";
49
	}
50

    
51
	if ($config['l2tp']['mode'] == "server") {
52
		$iflist['l2tp'] = "L2TP VPN";
53
	}
54

    
55
	/* add ipsec interfaces */
56
	if (ipsec_enabled()) {
57
		$iflist["enc0"] = "IPSEC";
58
	}
59

    
60
	if (isset($iflist[$int])) {
61
		return $int;
62
	}
63

    
64
	foreach ($iflist as $if => $ifd) {
65
		if (strtolower($int) == strtolower($ifd)) {
66
			return $if;
67
		}
68
	}
69

    
70
	if (substr($int, 0, 4) == "ovpn") {
71
		return "openvpn";
72
	}
73

    
74
	return false;
75
}
76

    
77
function easyrule_block_rule_exists($int = 'wan', $ipproto = "inet") {
78
	global $blockaliasname, $config;
79
	/* No rules, we we know it doesn't exist */
80
	if (!is_array($config['filter']['rule'])) {
81
		return false;
82
	}
83

    
84
	/* Search through the rules for one referencing our alias */
85
	foreach ($config['filter']['rule'] as $rule) {
86
		if (!is_array($rule) || !is_array($rule['source'])) {
87
			continue;
88
		}
89
		$checkproto = isset($rule['ipprotocol']) ? $rule['ipprotocol'] : "inet";
90
		if ($rule['source']['address'] == $blockaliasname . strtoupper($int) && ($rule['interface'] == $int) && ($checkproto == $ipproto)) {
91
			return true;
92
		}
93
	}
94
	return false;
95
}
96

    
97
function easyrule_block_rule_create($int = 'wan', $ipproto = "inet") {
98
	global $blockaliasname, $config;
99
	/* If the alias doesn't exist, exit.
100
	 * Can't create an empty alias, and we don't know a host */
101
	if (easyrule_block_alias_getid($int) === false) {
102
		return false;
103
	}
104

    
105
	/* If the rule already exists, no need to do it again */
106
	if (easyrule_block_rule_exists($int, $ipproto)) {
107
		return true;
108
	}
109

    
110
	/* No rules, start a new array */
111
	if (!is_array($config['filter']['rule'])) {
112
		$config['filter']['rule'] = array();
113
	}
114

    
115
	filter_rules_sort();
116
	$a_filter = &$config['filter']['rule'];
117

    
118
	/* Make up a new rule */
119
	$filterent = array();
120
	$filterent['type'] = 'block';
121
	$filterent['interface'] = $int;
122
	$filterent['ipprotocol'] = $ipproto;
123
	$filterent['source']['address'] = $blockaliasname . strtoupper($int);
124
	$filterent['destination']['any'] = '';
125
	$filterent['descr'] = gettext("Easy Rule: Blocked from Firewall Log View");
126
	$filterent['created'] = make_config_revision_entry(null, gettext("Easy Rule"));
127

    
128
	array_splice($a_filter, 0, 0, array($filterent));
129

    
130
	return true;
131
}
132

    
133
function easyrule_block_alias_getid($int = 'wan') {
134
	global $blockaliasname, $config;
135
	if (!is_array($config['aliases'])) {
136
		return false;
137
	}
138

    
139
	/* Hunt down an alias with the name we want, return its id */
140
	foreach ($config['aliases']['alias'] as $aliasid => $alias) {
141
		if ($alias['name'] == $blockaliasname . strtoupper($int)) {
142
			return $aliasid;
143
		}
144
	}
145

    
146
	return false;
147
}
148

    
149
function easyrule_block_alias_add($host, $int = 'wan') {
150
	global $blockaliasname, $config;
151
	/* If the host isn't a valid IP address, bail */
152
	$host = trim($host, "[]");
153
	if (!is_ipaddr($host) && !is_subnet($host)) {
154
		return false;
155
	}
156

    
157
	/* If there are no aliases, start an array */
158
	if (!is_array($config['aliases']['alias'])) {
159
		$config['aliases']['alias'] = array();
160
	}
161

    
162
	$a_aliases = &$config['aliases']['alias'];
163

    
164
	/* Try to get the ID if the alias already exists */
165
	$id = easyrule_block_alias_getid($int);
166
	if ($id === false) {
167
	  unset($id);
168
	}
169

    
170
	$alias = array();
171

    
172
	if (is_subnet($host)) {
173
		list($host, $mask) = explode("/", $host);
174
	} elseif (is_specialnet($host)) {
175
		$mask = 0;
176
	} elseif (is_ipaddrv6($host)) {
177
		$mask = 128;
178
	} else {
179
		$mask = 32;
180
	}
181

    
182
	if (isset($id) && $a_aliases[$id]) {
183

    
184
		// Catch case when the list is empty
185
		if (empty($a_aliases[$id]['address'])) {
186
			$a_address = array();
187
			$a_detail = array();
188
		} else {
189
			$a_address = explode(" ", $a_aliases[$id]['address']);
190

    
191
			/* Make sure this IP isn't already in the list. */
192
			if (in_array($host.'/'.$mask, $a_address)) {
193
				return true;
194
			}
195
			$a_detail = explode("||", $a_aliases[$id]['detail']);
196
		}
197

    
198
		/* Since the alias already exists, just add to it. */
199
		$alias['name']    = $a_aliases[$id]['name'];
200
		$alias['type']    = $a_aliases[$id]['type'];
201
		$alias['descr']   = $a_aliases[$id]['descr'];
202

    
203
		$a_address[] = $host.'/'.$mask;
204
		$a_detail[] = gettext('Entry added') . ' ' . date('r');
205

    
206
		$alias['address'] = join(" ", $a_address);
207
		$alias['detail']  = join("||", $a_detail);
208

    
209
	} else {
210
		/* Create a new alias with all the proper information */
211
		$alias['name']    = $blockaliasname . strtoupper($int);
212
		$alias['type']    = 'network';
213
		$alias['descr']   = gettext("Hosts blocked from Firewall Log view");
214

    
215
		$alias['address'] = $host . '/' . $mask;
216
		$alias['detail']  = gettext('Entry added') . ' ' . date('r') . '||';
217
	}
218

    
219
	/* Replace the old alias if needed, otherwise tack it on the end */
220
	if (isset($id) && $a_aliases[$id]) {
221
		$a_aliases[$id] = $alias;
222
	} else {
223
		$a_aliases[] = $alias;
224
	}
225

    
226
	// Sort list
227
	$a_aliases = msort($a_aliases, "name");
228

    
229
	return true;
230
}
231

    
232
function easyrule_block_host_add($host, $int = 'wan', $ipproto = "inet") {
233
	global $retval;
234
	/* Bail if the supplied host is not a valid IP address */
235
	$host = trim($host, "[]");
236
	if (!is_ipaddr($host) && !is_subnet($host)) {
237
		return false;
238
	}
239

    
240
	/* Flag whether or not we need to reload the filter */
241
	$dirty = false;
242

    
243
	/* Attempt to add this host to the alias */
244
	if (easyrule_block_alias_add($host, $int)) {
245
		$dirty = true;
246
	} else {
247
		/* Couldn't add the alias, or adding the host failed. */
248
		return false;
249
	}
250

    
251
	/* Attempt to add the firewall rule if it doesn't exist.
252
	 * Failing to add the rule isn't necessarily an error, it may
253
	 * have been modified by the user in some way. Adding to the
254
	 * Alias is what's important.
255
	 */
256
	if (!easyrule_block_rule_exists($int, $ipproto)) {
257
		if (easyrule_block_rule_create($int, $ipproto)) {
258
			$dirty = true;
259
		} else {
260
			return false;
261
		}
262
	}
263

    
264
	/* If needed, write the config and reload the filter */
265
	if ($dirty) {
266
		write_config();
267
		$retval = filter_configure();
268
		if (!empty($_SERVER['DOCUMENT_ROOT'])) {
269
			header("Location: firewall_aliases.php");
270
			exit;
271
		} else {
272
			return true;
273
		}
274
	} else {
275
		return false;
276
	}
277
}
278

    
279
function easyrule_pass_rule_add($int, $proto, $srchost, $dsthost, $dstport, $ipproto) {
280
	global $config;
281

    
282
	/* No rules, start a new array */
283
	if (!is_array($config['filter']['rule'])) {
284
		$config['filter']['rule'] = array();
285
	}
286

    
287
	filter_rules_sort();
288
	$a_filter = &$config['filter']['rule'];
289

    
290
	/* Make up a new rule */
291
	$filterent = array();
292
	$filterent['type'] = 'pass';
293
	$filterent['interface'] = $int;
294
	$filterent['ipprotocol'] = $ipproto;
295
	$filterent['descr'] = gettext("Easy Rule: Passed from Firewall Log View");
296

    
297
	if ($proto != "any") {
298
		$filterent['protocol'] = $proto;
299
	} else {
300
		unset($filterent['protocol']);
301
	}
302

    
303
	/* Default to only allow echo requests, since that's what most people want and
304
	 *  it should be a safe choice. */
305
	if ($proto == "icmp") {
306
		$filterent['icmptype'] = 'echoreq';
307
	}
308

    
309
	if ((strtolower($proto) == "icmp6") || (strtolower($proto) == "icmpv6")) {
310
		$filterent['protocol'] = "icmp";
311
	}
312

    
313
	if (is_subnet($srchost)) {
314
		list($srchost, $srcmask) = explode("/", $srchost);
315
	} elseif (is_specialnet($srchost)) {
316
		$srcmask = 0;
317
	} elseif (is_ipaddrv6($srchost)) {
318
		$srcmask = 128;
319
	} else {
320
		$srcmask = 32;
321
	}
322

    
323
	if (is_subnet($dsthost)) {
324
		list($dsthost, $dstmask) = explode("/", $dsthost);
325
	} elseif (is_specialnet($dsthost)) {
326
		$dstmask = 0;
327
	} elseif (is_ipaddrv6($dsthost)) {
328
		$dstmask = 128;
329
	} else {
330
		$dstmask = 32;
331
	}
332

    
333
	pconfig_to_address($filterent['source'], $srchost, $srcmask);
334
	pconfig_to_address($filterent['destination'], $dsthost, $dstmask, '', $dstport, $dstport);
335

    
336
	$filterent['created'] = make_config_revision_entry(null, gettext("Easy Rule"));
337
	$a_filter[] = $filterent;
338

    
339
	write_config($filterent['descr']);
340
	$retval = filter_configure();
341
	if (!empty($_SERVER['DOCUMENT_ROOT'])) {
342
		header("Location: firewall_rules.php?if={$int}");
343
		exit;
344
	} else {
345
		return true;
346
	}
347
}
348

    
349
function easyrule_parse_block($int, $src, $ipproto = "inet") {
350
	if (!empty($src) && !empty($int)) {
351
		$src = trim($src, "[]");
352
		if (!is_ipaddr($src) && !is_subnet($src)) {
353
			return gettext("Tried to block invalid IP:") . ' ' . htmlspecialchars($src);
354
		}
355
		$int = easyrule_find_rule_interface($int);
356
		if ($int === false) {
357
			return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int);
358
		}
359
		if (easyrule_block_host_add($src, $int, $ipproto)) {
360
			return gettext("Host added successfully");
361
		} else {
362
			return gettext("Failed to create block rule, alias, or add host.");
363
		}
364
	} else {
365
		return gettext("Tried to block but had no host IP or interface");
366
	}
367
	return gettext("Unknown block error.");
368
}
369

    
370
function easyrule_parse_unblock($int, $host, $ipproto = "inet") {
371
	global $blockaliasname, $config;
372

    
373
	if (!empty($host) && !empty($int)) {
374
		$host = trim($host, "[]");
375
		if (!is_ipaddr($host) && !is_subnet($host)) {
376
			return gettext("Tried to unblock invalid IP:") . ' ' . htmlspecialchars($host);
377
		}
378
		$real_int = easyrule_find_rule_interface($int);
379
		if ($real_int === false) {
380
			return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int);
381
		}
382

    
383
		/* Try to get the ID - will fail if there are no rules/alias on this interface */
384
		$id = easyrule_block_alias_getid($real_int);
385
		if ($id === false || !$config['aliases']['alias'][$id]) {
386
			return gettext("No block rules set on interface:") . ' ' . htmlspecialchars($int);
387
		}
388

    
389
		$alias = &$config['aliases']['alias'][$id];
390

    
391
		if (is_subnet($host)) {
392
			list($host, $mask) = explode("/", $host);
393
		} elseif (is_specialnet($host)) {
394
			$mask = 0;
395
		} elseif (is_ipaddrv6($host)) {
396
			$mask = 128;
397
		} else {
398
			$mask = 32;
399
		}
400

    
401
		// Create the expected string representation
402
		$unblock = $host.'/'.$mask;
403

    
404
		$a_address = explode(" ", $config['aliases']['alias'][$id]['address']);
405
		$a_detail = explode("||", $config['aliases']['alias'][$id]['detail']);
406

    
407
		if (($key = array_search($unblock, $a_address)) !== false) {
408
			unset($a_address[$key]);
409
			unset($a_detail[$key]);
410
			// Write back the result to the config array
411
			$config['aliases']['alias'][$id]['address'] = join(" ", $a_address);
412
			$config['aliases']['alias'][$id]['detail'] = join("||", $a_detail);
413

    
414
			// Update config
415
			write_config();
416
			$retval = filter_configure();
417
			if (!empty($_SERVER['DOCUMENT_ROOT'])) {
418
				header("Location: firewall_aliases.php");
419
				exit;
420
			} else {
421
				return gettext("Host unblocked successfully");
422
			}
423
		} else {
424
			return gettext("Host ist not on block list: " . $host);
425
		}
426
	}
427

    
428
	return gettext("Tried to unblock but had no host IP or interface");
429

    
430
}
431

    
432
function easyrule_parse_getblock($int = 'wan', $sep = "\n") {
433
	global $blockaliasname, $config;
434

    
435
	$real_int = easyrule_find_rule_interface($int);
436
	if ($real_int === false) {
437
		return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int);
438
	}
439

    
440
	/* Try to get the ID - will fail if there are no rules/alias on this interface */
441
	$id = easyrule_block_alias_getid($real_int);
442

    
443
	if ($id === false || !$config['aliases']['alias'][$id] || empty($config['aliases']['alias'][$id]['address'])) {
444
		return gettext("No block rules set on interface:") . ' ' . htmlspecialchars($int);
445
	}
446
	return join($sep, explode(" ", $config['aliases']['alias'][$id]['address']));
447

    
448
}
449

    
450
function easyrule_parse_pass($int, $proto, $src, $dst, $dstport = 0, $ipproto = "inet") {
451
	/* Check for valid int, srchost, dsthost, dstport, and proto */
452
	global $protocols_with_ports;
453
	$src = trim($src, "[]");
454
	$dst = trim($dst, "[]");
455

    
456
	if (!empty($int) && !empty($proto) && !empty($src) && !empty($dst)) {
457
		$int = easyrule_find_rule_interface($int);
458
		if ($int === false) {
459
			return gettext("Invalid interface for pass rule:") . ' ' . htmlspecialchars($int);
460
		}
461
		if (getprotobyname($proto) == -1) {
462
			return gettext("Invalid protocol for pass rule:") . ' ' . htmlspecialchars($proto);
463
		}
464
		if (!is_ipaddr($src) && !is_subnet($src) && !is_ipaddroralias($src) && !is_specialnet($src)) {
465
			return gettext("Tried to pass invalid source IP:") . ' ' . htmlspecialchars($src);
466
		}
467
		if (!is_ipaddr($dst) && !is_subnet($dst) && !is_ipaddroralias($dst) && !is_specialnet($dst)) {
468
			return gettext("Tried to pass invalid destination IP:") . ' ' . htmlspecialchars($dst);
469
		}
470
		if (in_array($proto, $protocols_with_ports)) {
471
			if (empty($dstport)) {
472
				return gettext("Missing destination port:") . ' ' . htmlspecialchars($dstport);
473
			}
474
			if (!is_port($dstport) && ($dstport != "any")) {
475
				return gettext("Tried to pass invalid destination port:") . ' ' . htmlspecialchars($dstport);
476
			}
477
		} else {
478
			$dstport = 0;
479
		}
480
		/* Should have valid input... */
481
		if (easyrule_pass_rule_add($int, $proto, $src, $dst, $dstport, $ipproto)) {
482
			return gettext("Successfully added pass rule!");
483
		} else {
484
			return gettext("Failed to add pass rule.");
485
		}
486
	} else {
487
		return gettext("Missing parameters for pass rule.");
488
	}
489
	return gettext("Unknown pass error.");
490
}
491

    
492
?>
(17-17/65)