Project

General

Profile

Download (14.4 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * easyrule.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2009-2018 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(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
	if (substr($int, 0, 5) == "ipsec") {
62
		return "ipsec";
63
	}
64

    
65
	return false;
66
}
67

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

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

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

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

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

    
106
	filter_rules_sort();
107
	$a_filter = &$config['filter']['rule'];
108

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

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

    
126
	$tmpif = $int;
127

    
128
	// Update the separators
129
	$a_separators = &$config['filter']['separator'][strtolower($tmpif)];
130
	$ridx = ifridx($tmpif, $after);	// get rule index within interface
131
	$mvnrows = +1;
132
	move_separators($a_separators, $ridx, $mvnrows);
133

    
134
	return true;
135
}
136

    
137
function easyrule_block_alias_getid($int = 'wan') {
138
	global $blockaliasname, $config;
139
	if (!is_array($config['aliases'])) {
140
		return false;
141
	}
142

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

    
150
	return false;
151
}
152

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

    
161
	/* If there are no aliases, start an array */
162
	if (!is_array($config['aliases']['alias'])) {
163
		$config['aliases']['alias'] = array();
164
	}
165

    
166
	$a_aliases = &$config['aliases']['alias'];
167

    
168
	/* Try to get the ID if the alias already exists */
169
	$id = easyrule_block_alias_getid($int);
170
	if ($id === false) {
171
	  unset($id);
172
	}
173

    
174
	$alias = array();
175

    
176
	if (is_subnet($host)) {
177
		list($host, $mask) = explode("/", $host);
178
	} elseif (is_specialnet($host)) {
179
		$mask = 0;
180
	} elseif (is_ipaddrv6($host)) {
181
		$mask = 128;
182
	} else {
183
		$mask = 32;
184
	}
185

    
186
	if (isset($id) && $a_aliases[$id]) {
187

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

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

    
202
		/* Since the alias already exists, just add to it. */
203
		$alias['name']    = $a_aliases[$id]['name'];
204
		$alias['type']    = $a_aliases[$id]['type'];
205
		$alias['descr']   = $a_aliases[$id]['descr'];
206

    
207
		$a_address[] = $host.'/'.$mask;
208
		$a_detail[] = gettext('Entry added') . ' ' . date('r');
209

    
210
		$alias['address'] = join(" ", $a_address);
211
		$alias['detail']  = join("||", $a_detail);
212

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

    
219
		$alias['address'] = $host . '/' . $mask;
220
		$alias['detail']  = gettext('Entry added') . ' ' . date('r') . '||';
221
	}
222

    
223
	/* Replace the old alias if needed, otherwise tack it on the end */
224
	if (isset($id) && $a_aliases[$id]) {
225
		$a_aliases[$id] = $alias;
226
	} else {
227
		$a_aliases[] = $alias;
228
	}
229

    
230
	// Sort list
231
	$a_aliases = msort($a_aliases, "name");
232

    
233
	return true;
234
}
235

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

    
244
	/* Flag whether or not we need to reload the filter */
245
	$dirty = false;
246

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

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

    
268
	/* If needed, write the config and reload the filter */
269
	if ($dirty) {
270
		write_config(sprintf(gettext("Blocked host %s via easy rule"), $host));
271
		$retval = filter_configure();
272
		if (!empty($_SERVER['DOCUMENT_ROOT'])) {
273
			header("Location: firewall_aliases.php");
274
			exit;
275
		} else {
276
			return true;
277
		}
278
	} else {
279
		return false;
280
	}
281
}
282

    
283
function easyrule_pass_rule_add($int, $proto, $srchost, $dsthost, $dstport, $ipproto) {
284
	global $config;
285

    
286
	/* No rules, start a new array */
287
	if (!is_array($config['filter']['rule'])) {
288
		$config['filter']['rule'] = array();
289
	}
290

    
291
	filter_rules_sort();
292
	$a_filter = &$config['filter']['rule'];
293

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

    
301
	if ($proto != "any") {
302
		$filterent['protocol'] = $proto;
303
	} else {
304
		unset($filterent['protocol']);
305
	}
306

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

    
313
	if ((strtolower($proto) == "icmp6") || (strtolower($proto) == "icmpv6")) {
314
		$filterent['protocol'] = "icmp";
315
	}
316

    
317
	if (is_subnet($srchost)) {
318
		list($srchost, $srcmask) = explode("/", $srchost);
319
	} elseif (is_specialnet($srchost)) {
320
		$srcmask = 0;
321
	} elseif (is_ipaddrv6($srchost)) {
322
		$srcmask = 128;
323
	} else {
324
		$srcmask = 32;
325
	}
326

    
327
	if (is_subnet($dsthost)) {
328
		list($dsthost, $dstmask) = explode("/", $dsthost);
329
	} elseif (is_specialnet($dsthost)) {
330
		$dstmask = 0;
331
	} elseif (is_ipaddrv6($dsthost)) {
332
		$dstmask = 128;
333
	} else {
334
		$dstmask = 32;
335
	}
336

    
337
	pconfig_to_address($filterent['source'], $srchost, $srcmask);
338
	pconfig_to_address($filterent['destination'], $dsthost, $dstmask, '', $dstport, $dstport);
339

    
340
	/* Do not translate this, it's considered a username which cannot contain international characters */
341
	$filterent['created'] = make_config_revision_entry(null, "Easy Rule");
342
	$filterent['tracker'] = (int)microtime(true);
343
	$a_filter[] = $filterent;
344

    
345
	write_config($filterent['descr']);
346
	$retval = filter_configure();
347
	if (!empty($_SERVER['DOCUMENT_ROOT'])) {
348
		header("Location: firewall_rules.php?if={$int}");
349
		exit;
350
	} else {
351
		return true;
352
	}
353
}
354

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

    
376
function easyrule_parse_unblock($int, $host, $ipproto = "inet") {
377
	global $blockaliasname, $config;
378

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

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

    
395
		$alias = &$config['aliases']['alias'][$id];
396

    
397
		if (is_subnet($host)) {
398
			list($host, $mask) = explode("/", $host);
399
		} elseif (is_specialnet($host)) {
400
			$mask = 0;
401
		} elseif (is_ipaddrv6($host)) {
402
			$mask = 128;
403
		} else {
404
			$mask = 32;
405
		}
406

    
407
		// Create the expected string representation
408
		$unblock = $host.'/'.$mask;
409

    
410
		$a_address = explode(" ", $config['aliases']['alias'][$id]['address']);
411
		$a_detail = explode("||", $config['aliases']['alias'][$id]['detail']);
412

    
413
		if (($key = array_search($unblock, $a_address)) !== false) {
414
			unset($a_address[$key]);
415
			unset($a_detail[$key]);
416
			// Write back the result to the config array
417
			$config['aliases']['alias'][$id]['address'] = join(" ", $a_address);
418
			$config['aliases']['alias'][$id]['detail'] = join("||", $a_detail);
419

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

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

    
436
}
437

    
438
function easyrule_parse_getblock($int = 'wan', $sep = "\n") {
439
	global $blockaliasname, $config;
440

    
441
	$real_int = easyrule_find_rule_interface($int);
442
	if ($real_int === false) {
443
		return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int);
444
	}
445

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

    
449
	if ($id === false || !$config['aliases']['alias'][$id] || empty($config['aliases']['alias'][$id]['address'])) {
450
		return gettext("No block rules set on interface:") . ' ' . htmlspecialchars($int);
451
	}
452
	return join($sep, explode(" ", $config['aliases']['alias'][$id]['address']));
453

    
454
}
455

    
456
function easyrule_parse_pass($int, $proto, $src, $dst, $dstport = 0, $ipproto = "inet") {
457
	/* Check for valid int, srchost, dsthost, dstport, and proto */
458
	global $protocols_with_ports;
459
	$src = trim($src, "[]");
460
	$dst = trim($dst, "[]");
461

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

    
498
?>
(16-16/60)