Skip to content

Commit 5e62a0f

Browse files
Allow clients to specify a gateway group in firewall rules, forced gateway IP protocol to match rule IP protocol to create firewall rules with gateway, fixed bug that prevented clients from updating the firewall rule log field, broke down firewall rule validation
1 parent 1ba1e80 commit 5e62a0f

4 files changed

Lines changed: 165 additions & 42 deletions

File tree

pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,12 @@ function get($id, $data=[], $all=false) {
15441544
"return" => $id,
15451545
"message" => "Firewall alias cannot be deleted while in use"
15461546
],
1547+
4109 => [
1548+
"status" => "bad request",
1549+
"code" => 400,
1550+
"return" => $id,
1551+
"message" => "Firewall rule gateway must match IP protocol"
1552+
],
15471553

15481554
//5000-5999 reserved for /users API calls
15491555
5000 => [

pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -521,22 +521,28 @@ function sort_firewall_rules($mode=null, $data=null) {
521521
$config["filter"]["rule"] = $master_arr;
522522
}
523523

524-
// Checks if inputted routing gateway exists
525-
function is_gateway($gw) {
526-
// Local variables
527-
$gw_config = return_gateways_array(true, true);
528-
$gw_exists = false;
529-
// Check that we have gateways configured
524+
# Checks if routing gateway exists
525+
function is_gateway($gw, $ret_protocol=false) {
526+
# Local variables
527+
$gw_config = return_gateways_array();
528+
$gw_group_config = return_gateway_groups_array();
529+
530+
# Loop through each gateway/gateway group and check if our gw matches the name
530531
if (is_array($gw_config)) {
531-
// Loop through each gateway and see if name matches
532+
# Loop through each gateway and see if name matches
532533
foreach ($gw_config as $gw_name => $gw_item) {
533534
if ($gw === $gw_name) {
534-
$gw_exists = true;
535-
break;
535+
return ($ret_protocol) ? $gw_item["ipprotocol"] : true;
536+
}
537+
}
538+
# Loop through each gateway and see if name matches
539+
foreach ($gw_group_config as $gwg_name => $gwg_item) {
540+
if ($gw === $gwg_name) {
541+
return ($ret_protocol) ? $gwg_item["ipprotocol"] : true;
536542
}
537543
}
538544
}
539-
return $gw_exists;
545+
return false;
540546
}
541547

542548
// Duplicate function from /firewall_aliases.php: accept input and check if alias exists

pfSense-pkg-API/files/etc/inc/api/models/APIFirewallRuleCreate.inc

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@ class APIFirewallRuleCreate extends APIModel {
3333
return APIResponse\get(0, $this->validated_data);
3434
}
3535

36-
public function validate_payload() {
37-
# Include static rule ID
38-
$this->validated_data["id"] = "";
39-
36+
private function __validate_type() {
4037
# Check for our required 'type' payload value
4138
if (isset($this->initial_data["type"])) {
4239
$type_options = array("pass", "block", "reject");
@@ -49,7 +46,9 @@ class APIFirewallRuleCreate extends APIModel {
4946
} else {
5047
$this->errors[] = APIResponse\get(4033);
5148
}
49+
}
5250

51+
private function __validate_interface() {
5352
# Check for our required 'interface' payload value
5453
if (isset($this->initial_data['interface'])) {
5554
$this->initial_data['interface'] = APITools\get_pfsense_if_id($this->initial_data['interface']);
@@ -62,7 +61,9 @@ class APIFirewallRuleCreate extends APIModel {
6261
} else {
6362
$this->errors[] = APIResponse\get(4034);
6463
}
64+
}
6565

66+
private function __validate_ipprotocol() {
6667
# Check for our required 'ipprotocol' payload value
6768
if (isset($this->initial_data['ipprotocol'])) {
6869
$ipprotocol_options = array("inet", "inet6", "inet46");
@@ -75,7 +76,9 @@ class APIFirewallRuleCreate extends APIModel {
7576
} else {
7677
$this->errors[] = APIResponse\get(4035);
7778
}
79+
}
7880

81+
private function __validate_protocol() {
7982
# Check for our required 'protocol' payload value
8083
if (isset($this->initial_data['protocol'])) {
8184
$protocol_options = [
@@ -91,11 +94,12 @@ class APIFirewallRuleCreate extends APIModel {
9194
} else {
9295
$this->errors[] = APIResponse\get(4042);
9396
}
94-
$protocol = $this->initial_data['protocol'];
9597
} else {
9698
$this->errors[] = APIResponse\get(4036);
9799
}
100+
}
98101

102+
private function __validate_icmptype() {
99103
# Check for our optional 'icmpsubtype' payload value when our protocol is set to ICMP
100104
if (isset($this->initial_data["icmptype"]) and $this->validated_data["protocol"] === "icmp") {
101105
$icmptype_options = [
@@ -118,7 +122,9 @@ class APIFirewallRuleCreate extends APIModel {
118122
$this->validated_data["icmptype"] = implode(",", $this->initial_data["icmptype"]);
119123
}
120124
}
125+
}
121126

127+
private function __validate_src() {
122128
# Check for our required 'src' payload value
123129
if (isset($this->initial_data['src'])) {
124130
// Check if our source and destination values are valid
@@ -131,7 +137,9 @@ class APIFirewallRuleCreate extends APIModel {
131137
} else {
132138
$this->errors[] = APIResponse\get(4037);
133139
}
140+
}
134141

142+
private function __validate_dst() {
135143
# Check for our required 'src' payload value
136144
if (isset($this->initial_data['dst'])) {
137145
// Check if our source and destination values are valid
@@ -144,8 +152,10 @@ class APIFirewallRuleCreate extends APIModel {
144152
} else {
145153
$this->errors[] = APIResponse\get(4038);
146154
}
155+
}
147156

148-
# Check for our required 'srcport' and 'dstport' payload values if protocol is TCP, UDP or TCP/UDP
157+
private function __validate_srcport() {
158+
# Check for our required 'srcport' payload values if protocol is TCP, UDP or TCP/UDP
149159
if (in_array($this->validated_data["protocol"], ["tcp", "udp", "tcp/udp"])) {
150160
if (isset($this->initial_data['srcport'])) {
151161
$val = str_replace("-", ":", $this->initial_data['srcport']);
@@ -157,7 +167,12 @@ class APIFirewallRuleCreate extends APIModel {
157167
} else {
158168
$this->errors[] = APIResponse\get(4047);
159169
}
170+
}
171+
}
160172

173+
private function __validate_dstport() {
174+
# Check for our required 'dstport' payload values if protocol is TCP, UDP or TCP/UDP
175+
if (in_array($this->validated_data["protocol"], ["tcp", "udp", "tcp/udp"])) {
161176
if (isset($this->initial_data['dstport'])) {
162177
$val = str_replace("-", ":", $this->initial_data['dstport']);
163178
if (!is_port_or_range_or_alias($val) and $val !== "any") {
@@ -169,37 +184,70 @@ class APIFirewallRuleCreate extends APIModel {
169184
$this->errors[] = APIResponse\get(4047);
170185
}
171186
}
187+
}
172188

189+
private function __validate_gateway() {
173190
# Check for our optional 'gateway' payload value
174191
if (isset($this->initial_data['gateway'])) {
175-
# Check that the specified gateway exists
176-
if (APITools\is_gateway($this->initial_data["gateway"])) {
177-
$this->validated_data["gateway"] = $this->initial_data["gateway"];
178-
} else {
192+
$gw_protocol = APITools\is_gateway($this->initial_data["gateway"], true);
193+
# Check if we were able to locate the gateway
194+
if ($gw_protocol === false) {
179195
$this->errors[] = APIResponse\get(4043);
180196
}
197+
# Check if the gateway IP protocol matches our rule's IP protocol
198+
elseif ($gw_protocol !== "inet46" and $gw_protocol !== $this->validated_data["ipprotocol"]) {
199+
$this->errors[] = APIResponse\get(4109);
200+
}
201+
# Otherwise, the gateway is valid
202+
else {
203+
$this->validated_data["gateway"] = $this->initial_data["gateway"];
204+
}
181205
}
206+
}
182207

183-
208+
private function __validate_disabled() {
184209
# Check for our optional 'disabled' payload value
185210
if ($this->initial_data['disabled'] === true) {
186211
$this->validated_data["disabled"] = "";
187212
}
213+
}
188214

215+
private function __validate_descr() {
189216
# Check for our optional 'descr' payload value
190217
if (isset($this->initial_data['descr'])) {
191218
$this->validated_data["descr"] = strval($this->initial_data['descr']);
192219
}
220+
}
193221

222+
private function __validate_log() {
194223
# Check for our optional 'log' payload value
195224
if ($this->initial_data['log'] === true) {
196225
$this->validated_data["log"] = "";
197226
}
227+
}
198228

229+
private function __validate_top() {
199230
# Check for our optional 'top' payload value
200231
if ($this->initial_data['top'] === true) {
201232
$this->initial_data['top'] = "top";
202233
}
234+
}
235+
236+
public function validate_payload() {
237+
$this->__validate_type();
238+
$this->__validate_interface();
239+
$this->__validate_ipprotocol();
240+
$this->__validate_protocol();
241+
$this->__validate_icmptype();
242+
$this->__validate_src();
243+
$this->__validate_dst();
244+
$this->__validate_srcport();
245+
$this->__validate_dstport();
246+
$this->__validate_gateway();
247+
$this->__validate_disabled();
248+
$this->__validate_descr();
249+
$this->__validate_log();
250+
$this->__validate_top();
203251

204252
# Add our static 'tracker', 'created' and 'updated' values
205253
$this->validated_data["tracker"] = (int)microtime(true);

0 commit comments

Comments
 (0)