Project

General

Profile

Download (14.3 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * easyrule.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2009-2016 Rubicon Communications, LLC (Netgate)
7
 * Originally Sponsored By Anathematic @ pfSense Forums
8
 * All rights reserved.
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 * http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22

    
23
$blockaliasname = 'EasyRuleBlockHosts';
24
$protocols_with_ports = array('tcp', 'udp');
25
require_once("functions.inc");
26
require_once("util.inc");
27
require_once("ipsec.inc");
28
require_once("config.inc");
29

    
30
function easyrule_find_rule_interface($int) {
31
	global $config;
32
	/* Borrowed from firewall_rules.php */
33
	$iflist = get_configured_interface_with_descr(false, true);
34

    
35
	if ($config['pppoe']['mode'] == "server") {
36
		$iflist['pppoe'] = "PPPoE Server";
37
	}
38

    
39
	if ($config['l2tp']['mode'] == "server") {
40
		$iflist['l2tp'] = "L2TP VPN";
41
	}
42

    
43
	/* add ipsec interfaces */
44
	if (ipsec_enabled()) {
45
		$iflist["enc0"] = "IPSEC";
46
	}
47

    
48
	if (isset($iflist[$int])) {
49
		return $int;
50
	}
51

    
52
	foreach ($iflist as $if => $ifd) {
53
		if (strtolower($int) == strtolower($ifd)) {
54
			return $if;
55
		}
56
	}
57

    
58
	if (substr($int, 0, 4) == "ovpn") {
59
		return "openvpn";
60
	}
61

    
62
	return false;
63
}
64

    
65
function easyrule_block_rule_exists($int = 'wan', $ipproto = "inet") {
66
	global $blockaliasname, $config;
67
	/* No rules, we we know it doesn't exist */
68
	if (!is_array($config['filter']['rule'])) {
69
		return false;
70
	}
71

    
72
	/* Search through the rules for one referencing our alias */
73
	foreach ($config['filter']['rule'] as $rule) {
74
		if (!is_array($rule) || !is_array($rule['source'])) {
75
			continue;
76
		}
77
		$checkproto = isset($rule['ipprotocol']) ? $rule['ipprotocol'] : "inet";
78
		if ($rule['source']['address'] == $blockaliasname . strtoupper($int) && ($rule['interface'] == $int) && ($checkproto == $ipproto)) {
79
			return true;
80
		}
81
	}
82
	return false;
83
}
84

    
85
function easyrule_block_rule_create($int = 'wan', $ipproto = "inet") {
86
	global $blockaliasname, $config;
87
	/* If the alias doesn't exist, exit.
88
	 * Can't create an empty alias, and we don't know a host */
89
	if (easyrule_block_alias_getid($int) === false) {
90
		return false;
91
	}
92

    
93
	/* If the rule already exists, no need to do it again */
94
	if (easyrule_block_rule_exists($int, $ipproto)) {
95
		return true;
96
	}
97

    
98
	/* No rules, start a new array */
99
	if (!is_array($config['filter']['rule'])) {
100
		$config['filter']['rule'] = array();
101
	}
102

    
103
	filter_rules_sort();
104
	$a_filter = &$config['filter']['rule'];
105

    
106
	/* Make up a new rule */
107
	$filterent = array();
108
	$filterent['type'] = 'block';
109
	$filterent['interface'] = $int;
110
	$filterent['ipprotocol'] = $ipproto;
111
	$filterent['source']['address'] = $blockaliasname . strtoupper($int);
112
	$filterent['destination']['any'] = '';
113
	$filterent['descr'] = gettext("Easy Rule: Blocked from Firewall Log View");
114
	/* Do not translate this, it's considered a username which cannot contain international characters */
115
	$filterent['created'] = make_config_revision_entry(null, "Easy Rule");
116

    
117
	// Refer to firewall_rules_edit.php separators updating code.
118
	// Using same code, variables, and techniques here.
119
	$after = -1;	// Place rule at top and move all separators.
120
	array_splice($a_filter, $after+1, 0, array($filterent));
121

    
122
	$tmpif = $int;
123

    
124
	// Update the separators
125
	$a_separators = &$config['filter']['separator'][strtolower($tmpif)];
126
	$ridx = ifridx($tmpif, $after);	// get rule index within interface
127
	$mvnrows = +1;
128
	move_separators($a_separators, $ridx, $mvnrows);
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(sprintf(gettext("Blocked host %s via easy rule"), $host));
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
	/* Do not translate this, it's considered a username which cannot contain international characters */
337
	$filterent['created'] = make_config_revision_entry(null, "Easy Rule");
338
	$a_filter[] = $filterent;
339

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

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

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

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

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

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

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

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

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

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

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

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

    
431
}
432

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

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

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

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

    
449
}
450

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

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

    
493
?>
(11-11/51)