Project

General

Profile

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

    
5
	part of pfSense (https://www.pfsense.org)
6
	Originally Sponsored By Anathematic @ pfSense Forums
7
	Copyright (c) 2009-2010 Electric Sheep Fencing, LLC. 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
17
	   the documentation and/or other materials provided with the
18
	   distribution.
19

    
20
	3. All advertising materials mentioning features or use of this software
21
	   must display the following acknowledgment:
22
	   "This product includes software developed by the pfSense Project
23
	   for use in the pfSense® software distribution. (http://www.pfsense.org/).
24

    
25
	4. The names "pfSense" and "pfSense Project" must not be used to
26
	   endorse or promote products derived from this software without
27
	   prior written permission. For written permission, please contact
28
	   coreteam@pfsense.org.
29

    
30
	5. Products derived from this software may not be called "pfSense"
31
	   nor may "pfSense" appear in their names without prior written
32
	   permission of the Electric Sheep Fencing, LLC.
33

    
34
	6. Redistributions of any form whatsoever must retain the following
35
	   acknowledgment:
36

    
37
	"This product includes software developed by the pfSense Project
38
	for use in the pfSense software distribution (http://www.pfsense.org/).
39

    
40
	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
41
	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43
	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
44
	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45
	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46
	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47
	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48
	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49
	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51
	OF THE POSSIBILITY OF SUCH DAMAGE.
52
*/
53

    
54
$blockaliasname = 'EasyRuleBlockHosts';
55
$protocols_with_ports = array('tcp', 'udp');
56
require_once("functions.inc");
57
require_once("util.inc");
58
require_once("ipsec.inc");
59
require_once("config.inc");
60

    
61
function easyrule_find_rule_interface($int) {
62
	global $config;
63
	/* Borrowed from firewall_rules.php */
64
	$iflist = get_configured_interface_with_descr(false, true);
65

    
66
	if ($config['pppoe']['mode'] == "server") {
67
		$iflist['pppoe'] = "PPPoE Server";
68
	}
69

    
70
	if ($config['l2tp']['mode'] == "server") {
71
		$iflist['l2tp'] = "L2TP VPN";
72
	}
73

    
74
	/* add ipsec interfaces */
75
	if (ipsec_enabled()) {
76
		$iflist["enc0"] = "IPSEC";
77
	}
78

    
79
	if (isset($iflist[$int])) {
80
		return $int;
81
	}
82

    
83
	foreach ($iflist as $if => $ifd) {
84
		if (strtolower($int) == strtolower($ifd)) {
85
			return $if;
86
		}
87
	}
88

    
89
	if (substr($int, 0, 4) == "ovpn") {
90
		return "openvpn";
91
	}
92

    
93
	return false;
94
}
95

    
96
function easyrule_block_rule_exists($int = 'wan', $ipproto = "inet") {
97
	global $blockaliasname, $config;
98
	/* No rules, we we know it doesn't exist */
99
	if (!is_array($config['filter']['rule'])) {
100
		return false;
101
	}
102

    
103
	/* Search through the rules for one referencing our alias */
104
	foreach ($config['filter']['rule'] as $rule) {
105
		if (!is_array($rule) || !is_array($rule['source'])) {
106
			continue;
107
		}
108
		$checkproto = isset($rule['ipprotocol']) ? $rule['ipprotocol'] : "inet";
109
		if ($rule['source']['address'] == $blockaliasname . strtoupper($int) && ($rule['interface'] == $int) && ($checkproto == $ipproto)) {
110
			return true;
111
		}
112
	}
113
	return false;
114
}
115

    
116
function easyrule_block_rule_create($int = 'wan', $ipproto = "inet") {
117
	global $blockaliasname, $config;
118
	/* If the alias doesn't exist, exit.
119
	 * Can't create an empty alias, and we don't know a host */
120
	if (easyrule_block_alias_getid($int) === false) {
121
		return false;
122
	}
123

    
124
	/* If the rule already exists, no need to do it again */
125
	if (easyrule_block_rule_exists($int, $ipproto)) {
126
		return true;
127
	}
128

    
129
	/* No rules, start a new array */
130
	if (!is_array($config['filter']['rule'])) {
131
		$config['filter']['rule'] = array();
132
	}
133

    
134
	filter_rules_sort();
135
	$a_filter = &$config['filter']['rule'];
136

    
137
	/* Make up a new rule */
138
	$filterent = array();
139
	$filterent['type'] = 'block';
140
	$filterent['interface'] = $int;
141
	$filterent['ipprotocol'] = $ipproto;
142
	$filterent['source']['address'] = $blockaliasname . strtoupper($int);
143
	$filterent['destination']['any'] = '';
144
	$filterent['descr'] = gettext("Easy Rule: Blocked from Firewall Log View");
145
	/* Do not translate this, it's considered a username which cannot contain international characters */
146
	$filterent['created'] = make_config_revision_entry(null, "Easy Rule");
147

    
148
	array_splice($a_filter, 0, 0, array($filterent));
149

    
150
	return true;
151
}
152

    
153
function easyrule_block_alias_getid($int = 'wan') {
154
	global $blockaliasname, $config;
155
	if (!is_array($config['aliases'])) {
156
		return false;
157
	}
158

    
159
	/* Hunt down an alias with the name we want, return its id */
160
	foreach ($config['aliases']['alias'] as $aliasid => $alias) {
161
		if ($alias['name'] == $blockaliasname . strtoupper($int)) {
162
			return $aliasid;
163
		}
164
	}
165

    
166
	return false;
167
}
168

    
169
function easyrule_block_alias_add($host, $int = 'wan') {
170
	global $blockaliasname, $config;
171
	/* If the host isn't a valid IP address, bail */
172
	$host = trim($host, "[]");
173
	if (!is_ipaddr($host) && !is_subnet($host)) {
174
		return false;
175
	}
176

    
177
	/* If there are no aliases, start an array */
178
	if (!is_array($config['aliases']['alias'])) {
179
		$config['aliases']['alias'] = array();
180
	}
181

    
182
	$a_aliases = &$config['aliases']['alias'];
183

    
184
	/* Try to get the ID if the alias already exists */
185
	$id = easyrule_block_alias_getid($int);
186
	if ($id === false) {
187
	  unset($id);
188
	}
189

    
190
	$alias = array();
191

    
192
	if (is_subnet($host)) {
193
		list($host, $mask) = explode("/", $host);
194
	} elseif (is_specialnet($host)) {
195
		$mask = 0;
196
	} elseif (is_ipaddrv6($host)) {
197
		$mask = 128;
198
	} else {
199
		$mask = 32;
200
	}
201

    
202
	if (isset($id) && $a_aliases[$id]) {
203

    
204
		// Catch case when the list is empty
205
		if (empty($a_aliases[$id]['address'])) {
206
			$a_address = array();
207
			$a_detail = array();
208
		} else {
209
			$a_address = explode(" ", $a_aliases[$id]['address']);
210

    
211
			/* Make sure this IP isn't already in the list. */
212
			if (in_array($host.'/'.$mask, $a_address)) {
213
				return true;
214
			}
215
			$a_detail = explode("||", $a_aliases[$id]['detail']);
216
		}
217

    
218
		/* Since the alias already exists, just add to it. */
219
		$alias['name']    = $a_aliases[$id]['name'];
220
		$alias['type']    = $a_aliases[$id]['type'];
221
		$alias['descr']   = $a_aliases[$id]['descr'];
222

    
223
		$a_address[] = $host.'/'.$mask;
224
		$a_detail[] = gettext('Entry added') . ' ' . date('r');
225

    
226
		$alias['address'] = join(" ", $a_address);
227
		$alias['detail']  = join("||", $a_detail);
228

    
229
	} else {
230
		/* Create a new alias with all the proper information */
231
		$alias['name']    = $blockaliasname . strtoupper($int);
232
		$alias['type']    = 'network';
233
		$alias['descr']   = gettext("Hosts blocked from Firewall Log view");
234

    
235
		$alias['address'] = $host . '/' . $mask;
236
		$alias['detail']  = gettext('Entry added') . ' ' . date('r') . '||';
237
	}
238

    
239
	/* Replace the old alias if needed, otherwise tack it on the end */
240
	if (isset($id) && $a_aliases[$id]) {
241
		$a_aliases[$id] = $alias;
242
	} else {
243
		$a_aliases[] = $alias;
244
	}
245

    
246
	// Sort list
247
	$a_aliases = msort($a_aliases, "name");
248

    
249
	return true;
250
}
251

    
252
function easyrule_block_host_add($host, $int = 'wan', $ipproto = "inet") {
253
	global $retval;
254
	/* Bail if the supplied host is not a valid IP address */
255
	$host = trim($host, "[]");
256
	if (!is_ipaddr($host) && !is_subnet($host)) {
257
		return false;
258
	}
259

    
260
	/* Flag whether or not we need to reload the filter */
261
	$dirty = false;
262

    
263
	/* Attempt to add this host to the alias */
264
	if (easyrule_block_alias_add($host, $int)) {
265
		$dirty = true;
266
	} else {
267
		/* Couldn't add the alias, or adding the host failed. */
268
		return false;
269
	}
270

    
271
	/* Attempt to add the firewall rule if it doesn't exist.
272
	 * Failing to add the rule isn't necessarily an error, it may
273
	 * have been modified by the user in some way. Adding to the
274
	 * Alias is what's important.
275
	 */
276
	if (!easyrule_block_rule_exists($int, $ipproto)) {
277
		if (easyrule_block_rule_create($int, $ipproto)) {
278
			$dirty = true;
279
		} else {
280
			return false;
281
		}
282
	}
283

    
284
	/* If needed, write the config and reload the filter */
285
	if ($dirty) {
286
		write_config();
287
		$retval = filter_configure();
288
		if (!empty($_SERVER['DOCUMENT_ROOT'])) {
289
			header("Location: firewall_aliases.php");
290
			exit;
291
		} else {
292
			return true;
293
		}
294
	} else {
295
		return false;
296
	}
297
}
298

    
299
function easyrule_pass_rule_add($int, $proto, $srchost, $dsthost, $dstport, $ipproto) {
300
	global $config;
301

    
302
	/* No rules, start a new array */
303
	if (!is_array($config['filter']['rule'])) {
304
		$config['filter']['rule'] = array();
305
	}
306

    
307
	filter_rules_sort();
308
	$a_filter = &$config['filter']['rule'];
309

    
310
	/* Make up a new rule */
311
	$filterent = array();
312
	$filterent['type'] = 'pass';
313
	$filterent['interface'] = $int;
314
	$filterent['ipprotocol'] = $ipproto;
315
	$filterent['descr'] = gettext("Easy Rule: Passed from Firewall Log View");
316

    
317
	if ($proto != "any") {
318
		$filterent['protocol'] = $proto;
319
	} else {
320
		unset($filterent['protocol']);
321
	}
322

    
323
	/* Default to only allow echo requests, since that's what most people want and
324
	 *  it should be a safe choice. */
325
	if ($proto == "icmp") {
326
		$filterent['icmptype'] = 'echoreq';
327
	}
328

    
329
	if ((strtolower($proto) == "icmp6") || (strtolower($proto) == "icmpv6")) {
330
		$filterent['protocol'] = "icmp";
331
	}
332

    
333
	if (is_subnet($srchost)) {
334
		list($srchost, $srcmask) = explode("/", $srchost);
335
	} elseif (is_specialnet($srchost)) {
336
		$srcmask = 0;
337
	} elseif (is_ipaddrv6($srchost)) {
338
		$srcmask = 128;
339
	} else {
340
		$srcmask = 32;
341
	}
342

    
343
	if (is_subnet($dsthost)) {
344
		list($dsthost, $dstmask) = explode("/", $dsthost);
345
	} elseif (is_specialnet($dsthost)) {
346
		$dstmask = 0;
347
	} elseif (is_ipaddrv6($dsthost)) {
348
		$dstmask = 128;
349
	} else {
350
		$dstmask = 32;
351
	}
352

    
353
	pconfig_to_address($filterent['source'], $srchost, $srcmask);
354
	pconfig_to_address($filterent['destination'], $dsthost, $dstmask, '', $dstport, $dstport);
355

    
356
	/* Do not translate this, it's considered a username which cannot contain international characters */
357
	$filterent['created'] = make_config_revision_entry(null, "Easy Rule");
358
	$a_filter[] = $filterent;
359

    
360
	write_config($filterent['descr']);
361
	$retval = filter_configure();
362
	if (!empty($_SERVER['DOCUMENT_ROOT'])) {
363
		header("Location: firewall_rules.php?if={$int}");
364
		exit;
365
	} else {
366
		return true;
367
	}
368
}
369

    
370
function easyrule_parse_block($int, $src, $ipproto = "inet") {
371
	if (!empty($src) && !empty($int)) {
372
		$src = trim($src, "[]");
373
		if (!is_ipaddr($src) && !is_subnet($src)) {
374
			return gettext("Tried to block invalid IP:") . ' ' . htmlspecialchars($src);
375
		}
376
		$int = easyrule_find_rule_interface($int);
377
		if ($int === false) {
378
			return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int);
379
		}
380
		if (easyrule_block_host_add($src, $int, $ipproto)) {
381
			return gettext("Host added successfully");
382
		} else {
383
			return gettext("Failed to create block rule, alias, or add host.");
384
		}
385
	} else {
386
		return gettext("Tried to block but had no host IP or interface");
387
	}
388
	return gettext("Unknown block error.");
389
}
390

    
391
function easyrule_parse_unblock($int, $host, $ipproto = "inet") {
392
	global $blockaliasname, $config;
393

    
394
	if (!empty($host) && !empty($int)) {
395
		$host = trim($host, "[]");
396
		if (!is_ipaddr($host) && !is_subnet($host)) {
397
			return gettext("Tried to unblock invalid IP:") . ' ' . htmlspecialchars($host);
398
		}
399
		$real_int = easyrule_find_rule_interface($int);
400
		if ($real_int === false) {
401
			return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int);
402
		}
403

    
404
		/* Try to get the ID - will fail if there are no rules/alias on this interface */
405
		$id = easyrule_block_alias_getid($real_int);
406
		if ($id === false || !$config['aliases']['alias'][$id]) {
407
			return gettext("No block rules set on interface:") . ' ' . htmlspecialchars($int);
408
		}
409

    
410
		$alias = &$config['aliases']['alias'][$id];
411

    
412
		if (is_subnet($host)) {
413
			list($host, $mask) = explode("/", $host);
414
		} elseif (is_specialnet($host)) {
415
			$mask = 0;
416
		} elseif (is_ipaddrv6($host)) {
417
			$mask = 128;
418
		} else {
419
			$mask = 32;
420
		}
421

    
422
		// Create the expected string representation
423
		$unblock = $host.'/'.$mask;
424

    
425
		$a_address = explode(" ", $config['aliases']['alias'][$id]['address']);
426
		$a_detail = explode("||", $config['aliases']['alias'][$id]['detail']);
427

    
428
		if (($key = array_search($unblock, $a_address)) !== false) {
429
			unset($a_address[$key]);
430
			unset($a_detail[$key]);
431
			// Write back the result to the config array
432
			$config['aliases']['alias'][$id]['address'] = join(" ", $a_address);
433
			$config['aliases']['alias'][$id]['detail'] = join("||", $a_detail);
434

    
435
			// Update config
436
			write_config();
437
			$retval = filter_configure();
438
			if (!empty($_SERVER['DOCUMENT_ROOT'])) {
439
				header("Location: firewall_aliases.php");
440
				exit;
441
			} else {
442
				return gettext("Host unblocked successfully");
443
			}
444
		} else {
445
			return gettext("Host is not on block list: " . $host);
446
		}
447
	}
448

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

    
451
}
452

    
453
function easyrule_parse_getblock($int = 'wan', $sep = "\n") {
454
	global $blockaliasname, $config;
455

    
456
	$real_int = easyrule_find_rule_interface($int);
457
	if ($real_int === false) {
458
		return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int);
459
	}
460

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

    
464
	if ($id === false || !$config['aliases']['alias'][$id] || empty($config['aliases']['alias'][$id]['address'])) {
465
		return gettext("No block rules set on interface:") . ' ' . htmlspecialchars($int);
466
	}
467
	return join($sep, explode(" ", $config['aliases']['alias'][$id]['address']));
468

    
469
}
470

    
471
function easyrule_parse_pass($int, $proto, $src, $dst, $dstport = 0, $ipproto = "inet") {
472
	/* Check for valid int, srchost, dsthost, dstport, and proto */
473
	global $protocols_with_ports;
474
	$src = trim($src, "[]");
475
	$dst = trim($dst, "[]");
476

    
477
	if (!empty($int) && !empty($proto) && !empty($src) && !empty($dst)) {
478
		$int = easyrule_find_rule_interface($int);
479
		if ($int === false) {
480
			return gettext("Invalid interface for pass rule:") . ' ' . htmlspecialchars($int);
481
		}
482
		if (getprotobyname($proto) == -1) {
483
			return gettext("Invalid protocol for pass rule:") . ' ' . htmlspecialchars($proto);
484
		}
485
		if (!is_ipaddr($src) && !is_subnet($src) && !is_ipaddroralias($src) && !is_specialnet($src)) {
486
			return gettext("Tried to pass invalid source IP:") . ' ' . htmlspecialchars($src);
487
		}
488
		if (!is_ipaddr($dst) && !is_subnet($dst) && !is_ipaddroralias($dst) && !is_specialnet($dst)) {
489
			return gettext("Tried to pass invalid destination IP:") . ' ' . htmlspecialchars($dst);
490
		}
491
		if (in_array($proto, $protocols_with_ports)) {
492
			if (empty($dstport)) {
493
				return gettext("Missing destination port:") . ' ' . htmlspecialchars($dstport);
494
			}
495
			if (!is_port($dstport) && ($dstport != "any")) {
496
				return gettext("Tried to pass invalid destination port:") . ' ' . htmlspecialchars($dstport);
497
			}
498
		} else {
499
			$dstport = 0;
500
		}
501
		/* Should have valid input... */
502
		if (easyrule_pass_rule_add($int, $proto, $src, $dst, $dstport, $ipproto)) {
503
			return gettext("Successfully added pass rule!");
504
		} else {
505
			return gettext("Failed to add pass rule.");
506
		}
507
	} else {
508
		return gettext("Missing parameters for pass rule.");
509
	}
510
	return gettext("Unknown pass error.");
511
}
512

    
513
?>
(17-17/65)