Project

General

Profile

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

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

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

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

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

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

    
50
	if (isset($iflist[$int])) {
51
		return $int;
52
	}
53

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

    
60
	if (substr($int, 0, 4) == "ovpn") {
61
		return "openvpn";
62
	}
63
	if (substr($int, 0, 5) == "ipsec") {
64
		return "ipsec";
65
	}
66

    
67
	return false;
68
}
69

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

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

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

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

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

    
107
	/* Make up a new rule */
108
	$filterent = array();
109
	$filterent['type'] = 'block';
110
	$filterent['interface'] = $int;
111
	$filterent['ipprotocol'] = $ipproto;
112
	$filterent['source']['address'] = $blockaliasname . strtoupper($int);
113
	$filterent['destination']['any'] = '';
114
	$filterent['descr'] = gettext("Easy Rule: Blocked from Firewall Log View");
115
	$filterent['created'] = make_config_revision_entry(null, "Easy Rule");
116
	$filterent['tracker'] = (int)microtime(true);
117

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

    
123
	$tmpif = $int;
124

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

    
132
	return true;
133
}
134

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

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

    
148
	return false;
149
}
150

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

    
159
	init_config_arr(array('aliases', 'alias'));
160
	$a_aliases = &$config['aliases']['alias'];
161

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

    
168
	$alias = array();
169

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

    
180
	if (isset($id) && $a_aliases[$id]) {
181

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

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

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

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

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

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

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

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

    
224
	// Sort list
225
	$a_aliases = msort($a_aliases, "name");
226

    
227
	return true;
228
}
229

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

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

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

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

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

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

    
280
	init_config_arr(array('filter', 'rule'));
281
	filter_rules_sort();
282
	$a_filter = &$config['filter']['rule'];
283

    
284
	/* Make up a new rule */
285
	$filterent = array();
286
	$filterent['type'] = 'pass';
287
	$filterent['interface'] = $int;
288
	$filterent['ipprotocol'] = $ipproto;
289
	$filterent['descr'] = gettext("Easy Rule: Passed from Firewall Log View");
290

    
291
	if ($proto != "any") {
292
		$filterent['protocol'] = $proto;
293
	} else {
294
		unset($filterent['protocol']);
295
	}
296

    
297
	/* Default to only allow echo requests, since that's what most people want and
298
	 *  it should be a safe choice. */
299
	if ($proto == "icmp") {
300
		$filterent['icmptype'] = 'echoreq';
301
	}
302

    
303
	if ((strtolower($proto) == "icmp6") || (strtolower($proto) == "icmpv6")) {
304
		$filterent['protocol'] = "icmp";
305
	}
306

    
307
	if (is_subnet($srchost)) {
308
		list($srchost, $srcmask) = explode("/", $srchost);
309
	} elseif (is_specialnet($srchost)) {
310
		$srcmask = 0;
311
	} elseif (is_ipaddrv6($srchost)) {
312
		$srcmask = 128;
313
	} else {
314
		$srcmask = 32;
315
	}
316

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

    
327
	pconfig_to_address($filterent['source'], $srchost, $srcmask);
328
	pconfig_to_address($filterent['destination'], $dsthost, $dstmask, '', $dstport, $dstport);
329

    
330
	$filterent['created'] = make_config_revision_entry(null, "Easy Rule");
331
	$filterent['tracker'] = (int)microtime(true);
332
	$a_filter[] = $filterent;
333

    
334
	write_config($filterent['descr']);
335
	$retval = filter_configure();
336
	if (!empty($_SERVER['DOCUMENT_ROOT'])) {
337
		header("Location: firewall_rules.php?if={$int}");
338
		exit;
339
	} else {
340
		return true;
341
	}
342
}
343

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

    
365
function easyrule_parse_unblock($int, $host, $ipproto = "inet") {
366
	global $blockaliasname, $config;
367

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

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

    
384
		init_config_arr(array('aliases', 'alias', $id));
385
		$alias = &$config['aliases']['alias'][$id];
386

    
387
		if (is_subnet($host)) {
388
			list($host, $mask) = explode("/", $host);
389
		} elseif (is_specialnet($host)) {
390
			$mask = 0;
391
		} elseif (is_ipaddrv6($host)) {
392
			$mask = 128;
393
		} else {
394
			$mask = 32;
395
		}
396

    
397
		// Create the expected string representation
398
		$unblock = $host.'/'.$mask;
399

    
400
		$a_address = explode(" ", $config['aliases']['alias'][$id]['address']);
401
		$a_detail = explode("||", $config['aliases']['alias'][$id]['detail']);
402

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

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

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

    
426
}
427

    
428
function easyrule_parse_getblock($int = 'wan', $sep = "\n") {
429
	global $blockaliasname, $config;
430

    
431
	$real_int = easyrule_find_rule_interface($int);
432
	if ($real_int === false) {
433
		return gettext("Invalid interface for block rule:") . ' ' . htmlspecialchars($int);
434
	}
435

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

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

    
444
}
445

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

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

    
488
?>
(17-17/62)