Project

General

Profile

Download (15.8 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
	// Refer to firewall_rules_edit.php separators updating code.
149
	// Using same code, variables, and techniques here.
150
	$after = -1;	// Place rule at top and move all separators.
151
	array_splice($a_filter, $after+1, 0, array($filterent));
152

    
153
	$tmpif = $int;
154

    
155
	// Update the separators
156
	$a_separators = &$config['filter']['separator'][strtolower($tmpif)];
157
	$ridx = ifridx($tmpif, $after);	// get rule index within interface
158
	$mvnrows = +1;
159
	move_separators($a_separators, $ridx, $mvnrows);
160

    
161
	return true;
162
}
163

    
164
function easyrule_block_alias_getid($int = 'wan') {
165
	global $blockaliasname, $config;
166
	if (!is_array($config['aliases'])) {
167
		return false;
168
	}
169

    
170
	/* Hunt down an alias with the name we want, return its id */
171
	foreach ($config['aliases']['alias'] as $aliasid => $alias) {
172
		if ($alias['name'] == $blockaliasname . strtoupper($int)) {
173
			return $aliasid;
174
		}
175
	}
176

    
177
	return false;
178
}
179

    
180
function easyrule_block_alias_add($host, $int = 'wan') {
181
	global $blockaliasname, $config;
182
	/* If the host isn't a valid IP address, bail */
183
	$host = trim($host, "[]");
184
	if (!is_ipaddr($host) && !is_subnet($host)) {
185
		return false;
186
	}
187

    
188
	/* If there are no aliases, start an array */
189
	if (!is_array($config['aliases']['alias'])) {
190
		$config['aliases']['alias'] = array();
191
	}
192

    
193
	$a_aliases = &$config['aliases']['alias'];
194

    
195
	/* Try to get the ID if the alias already exists */
196
	$id = easyrule_block_alias_getid($int);
197
	if ($id === false) {
198
	  unset($id);
199
	}
200

    
201
	$alias = array();
202

    
203
	if (is_subnet($host)) {
204
		list($host, $mask) = explode("/", $host);
205
	} elseif (is_specialnet($host)) {
206
		$mask = 0;
207
	} elseif (is_ipaddrv6($host)) {
208
		$mask = 128;
209
	} else {
210
		$mask = 32;
211
	}
212

    
213
	if (isset($id) && $a_aliases[$id]) {
214

    
215
		// Catch case when the list is empty
216
		if (empty($a_aliases[$id]['address'])) {
217
			$a_address = array();
218
			$a_detail = array();
219
		} else {
220
			$a_address = explode(" ", $a_aliases[$id]['address']);
221

    
222
			/* Make sure this IP isn't already in the list. */
223
			if (in_array($host.'/'.$mask, $a_address)) {
224
				return true;
225
			}
226
			$a_detail = explode("||", $a_aliases[$id]['detail']);
227
		}
228

    
229
		/* Since the alias already exists, just add to it. */
230
		$alias['name']    = $a_aliases[$id]['name'];
231
		$alias['type']    = $a_aliases[$id]['type'];
232
		$alias['descr']   = $a_aliases[$id]['descr'];
233

    
234
		$a_address[] = $host.'/'.$mask;
235
		$a_detail[] = gettext('Entry added') . ' ' . date('r');
236

    
237
		$alias['address'] = join(" ", $a_address);
238
		$alias['detail']  = join("||", $a_detail);
239

    
240
	} else {
241
		/* Create a new alias with all the proper information */
242
		$alias['name']    = $blockaliasname . strtoupper($int);
243
		$alias['type']    = 'network';
244
		$alias['descr']   = gettext("Hosts blocked from Firewall Log view");
245

    
246
		$alias['address'] = $host . '/' . $mask;
247
		$alias['detail']  = gettext('Entry added') . ' ' . date('r') . '||';
248
	}
249

    
250
	/* Replace the old alias if needed, otherwise tack it on the end */
251
	if (isset($id) && $a_aliases[$id]) {
252
		$a_aliases[$id] = $alias;
253
	} else {
254
		$a_aliases[] = $alias;
255
	}
256

    
257
	// Sort list
258
	$a_aliases = msort($a_aliases, "name");
259

    
260
	return true;
261
}
262

    
263
function easyrule_block_host_add($host, $int = 'wan', $ipproto = "inet") {
264
	global $retval;
265
	/* Bail if the supplied host is not a valid IP address */
266
	$host = trim($host, "[]");
267
	if (!is_ipaddr($host) && !is_subnet($host)) {
268
		return false;
269
	}
270

    
271
	/* Flag whether or not we need to reload the filter */
272
	$dirty = false;
273

    
274
	/* Attempt to add this host to the alias */
275
	if (easyrule_block_alias_add($host, $int)) {
276
		$dirty = true;
277
	} else {
278
		/* Couldn't add the alias, or adding the host failed. */
279
		return false;
280
	}
281

    
282
	/* Attempt to add the firewall rule if it doesn't exist.
283
	 * Failing to add the rule isn't necessarily an error, it may
284
	 * have been modified by the user in some way. Adding to the
285
	 * Alias is what's important.
286
	 */
287
	if (!easyrule_block_rule_exists($int, $ipproto)) {
288
		if (easyrule_block_rule_create($int, $ipproto)) {
289
			$dirty = true;
290
		} else {
291
			return false;
292
		}
293
	}
294

    
295
	/* If needed, write the config and reload the filter */
296
	if ($dirty) {
297
		write_config();
298
		$retval = filter_configure();
299
		if (!empty($_SERVER['DOCUMENT_ROOT'])) {
300
			header("Location: firewall_aliases.php");
301
			exit;
302
		} else {
303
			return true;
304
		}
305
	} else {
306
		return false;
307
	}
308
}
309

    
310
function easyrule_pass_rule_add($int, $proto, $srchost, $dsthost, $dstport, $ipproto) {
311
	global $config;
312

    
313
	/* No rules, start a new array */
314
	if (!is_array($config['filter']['rule'])) {
315
		$config['filter']['rule'] = array();
316
	}
317

    
318
	filter_rules_sort();
319
	$a_filter = &$config['filter']['rule'];
320

    
321
	/* Make up a new rule */
322
	$filterent = array();
323
	$filterent['type'] = 'pass';
324
	$filterent['interface'] = $int;
325
	$filterent['ipprotocol'] = $ipproto;
326
	$filterent['descr'] = gettext("Easy Rule: Passed from Firewall Log View");
327

    
328
	if ($proto != "any") {
329
		$filterent['protocol'] = $proto;
330
	} else {
331
		unset($filterent['protocol']);
332
	}
333

    
334
	/* Default to only allow echo requests, since that's what most people want and
335
	 *  it should be a safe choice. */
336
	if ($proto == "icmp") {
337
		$filterent['icmptype'] = 'echoreq';
338
	}
339

    
340
	if ((strtolower($proto) == "icmp6") || (strtolower($proto) == "icmpv6")) {
341
		$filterent['protocol'] = "icmp";
342
	}
343

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

    
354
	if (is_subnet($dsthost)) {
355
		list($dsthost, $dstmask) = explode("/", $dsthost);
356
	} elseif (is_specialnet($dsthost)) {
357
		$dstmask = 0;
358
	} elseif (is_ipaddrv6($dsthost)) {
359
		$dstmask = 128;
360
	} else {
361
		$dstmask = 32;
362
	}
363

    
364
	pconfig_to_address($filterent['source'], $srchost, $srcmask);
365
	pconfig_to_address($filterent['destination'], $dsthost, $dstmask, '', $dstport, $dstport);
366

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

    
371
	write_config($filterent['descr']);
372
	$retval = filter_configure();
373
	if (!empty($_SERVER['DOCUMENT_ROOT'])) {
374
		header("Location: firewall_rules.php?if={$int}");
375
		exit;
376
	} else {
377
		return true;
378
	}
379
}
380

    
381
function easyrule_parse_block($int, $src, $ipproto = "inet") {
382
	if (!empty($src) && !empty($int)) {
383
		$src = trim($src, "[]");
384
		if (!is_ipaddr($src) && !is_subnet($src)) {
385
			return gettext("Tried to block invalid IP:") . ' ' . htmlspecialchars($src);
386
		}
387
		$int = easyrule_find_rule_interface($int);
388
		if ($int === false) {
389
			return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int);
390
		}
391
		if (easyrule_block_host_add($src, $int, $ipproto)) {
392
			return gettext("Host added successfully");
393
		} else {
394
			return gettext("Failed to create block rule, alias, or add host.");
395
		}
396
	} else {
397
		return gettext("Tried to block but had no host IP or interface");
398
	}
399
	return gettext("Unknown block error.");
400
}
401

    
402
function easyrule_parse_unblock($int, $host, $ipproto = "inet") {
403
	global $blockaliasname, $config;
404

    
405
	if (!empty($host) && !empty($int)) {
406
		$host = trim($host, "[]");
407
		if (!is_ipaddr($host) && !is_subnet($host)) {
408
			return gettext("Tried to unblock invalid IP:") . ' ' . htmlspecialchars($host);
409
		}
410
		$real_int = easyrule_find_rule_interface($int);
411
		if ($real_int === false) {
412
			return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int);
413
		}
414

    
415
		/* Try to get the ID - will fail if there are no rules/alias on this interface */
416
		$id = easyrule_block_alias_getid($real_int);
417
		if ($id === false || !$config['aliases']['alias'][$id]) {
418
			return gettext("No block rules set on interface:") . ' ' . htmlspecialchars($int);
419
		}
420

    
421
		$alias = &$config['aliases']['alias'][$id];
422

    
423
		if (is_subnet($host)) {
424
			list($host, $mask) = explode("/", $host);
425
		} elseif (is_specialnet($host)) {
426
			$mask = 0;
427
		} elseif (is_ipaddrv6($host)) {
428
			$mask = 128;
429
		} else {
430
			$mask = 32;
431
		}
432

    
433
		// Create the expected string representation
434
		$unblock = $host.'/'.$mask;
435

    
436
		$a_address = explode(" ", $config['aliases']['alias'][$id]['address']);
437
		$a_detail = explode("||", $config['aliases']['alias'][$id]['detail']);
438

    
439
		if (($key = array_search($unblock, $a_address)) !== false) {
440
			unset($a_address[$key]);
441
			unset($a_detail[$key]);
442
			// Write back the result to the config array
443
			$config['aliases']['alias'][$id]['address'] = join(" ", $a_address);
444
			$config['aliases']['alias'][$id]['detail'] = join("||", $a_detail);
445

    
446
			// Update config
447
			write_config();
448
			$retval = filter_configure();
449
			if (!empty($_SERVER['DOCUMENT_ROOT'])) {
450
				header("Location: firewall_aliases.php");
451
				exit;
452
			} else {
453
				return gettext("Host unblocked successfully");
454
			}
455
		} else {
456
			return gettext("Host is not on block list: " . $host);
457
		}
458
	}
459

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

    
462
}
463

    
464
function easyrule_parse_getblock($int = 'wan', $sep = "\n") {
465
	global $blockaliasname, $config;
466

    
467
	$real_int = easyrule_find_rule_interface($int);
468
	if ($real_int === false) {
469
		return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int);
470
	}
471

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

    
475
	if ($id === false || !$config['aliases']['alias'][$id] || empty($config['aliases']['alias'][$id]['address'])) {
476
		return gettext("No block rules set on interface:") . ' ' . htmlspecialchars($int);
477
	}
478
	return join($sep, explode(" ", $config['aliases']['alias'][$id]['address']));
479

    
480
}
481

    
482
function easyrule_parse_pass($int, $proto, $src, $dst, $dstport = 0, $ipproto = "inet") {
483
	/* Check for valid int, srchost, dsthost, dstport, and proto */
484
	global $protocols_with_ports;
485
	$src = trim($src, "[]");
486
	$dst = trim($dst, "[]");
487

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

    
524
?>
(17-17/65)