rewrite firewall

This commit is contained in:
Wojtek Porczyk 2017-07-12 14:10:43 +02:00
parent 43f2f6b881
commit 37d501c42b

View File

@ -19,18 +19,24 @@
#
#
import sys
import datetime
import ipaddress
import os
import re
import sys
import xml.etree.ElementTree
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import datetime
import qubesadmin.firewall
from . import ui_newfwruledlg
class FirewallModifiedOutsideError(ValueError):
pass
class QIPAddressValidator(QValidator):
def __init__(self, parent = None):
super (QIPAddressValidator, self).__init__(parent)
@ -39,10 +45,10 @@ class QIPAddressValidator(QValidator):
hostname = str(input)
if len(hostname) > 255 or len(hostname) == 0:
return (QValidator.Intermediate, pos)
return (QValidator.Intermediate, input, pos)
if hostname == "*":
return (QValidator.Acceptable, pos)
return (QValidator.Acceptable, input, pos)
unmask = hostname.split("/", 1)
if len(unmask) == 2:
@ -50,25 +56,25 @@ class QIPAddressValidator(QValidator):
mask = unmask[1]
if mask.isdigit() or mask == "":
if re.match("^([0-9]{1,3}\.){3}[0-9]{1,3}$", hostname) is None:
return (QValidator.Invalid, pos)
return (QValidator.Invalid, input, pos)
if mask != "":
mask = int(unmask[1])
if mask < 0 or mask > 32:
return (QValidator.Invalid, pos)
return (QValidator.Invalid, input, pos)
else:
return (QValidator.Invalid, pos)
return (QValidator.Invalid, input, pos)
if hostname[-1:] == ".":
hostname = hostname[:-1]
if hostname[-1:] == "-":
return (QValidator.Intermediate, pos)
return (QValidator.Intermediate, input, pos)
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
if all(allowed.match(x) for x in hostname.split(".")):
return (QValidator.Acceptable, pos)
return (QValidator.Acceptable, input, pos)
return (QValidator.Invalid, pos)
return (QValidator.Invalid, input, pos)
class NewFwRuleDlg (QDialog, ui_newfwruledlg.Ui_NewFwRuleDlg):
def __init__(self, parent = None):
@ -194,12 +200,135 @@ class QubesFirewallRulesModel(QAbstractItemModel):
def get_column_string(self, col, row):
return self.__columnValues[col](row)
def rule_to_dict(self, rule):
if rule.dsthost is None:
raise FirewallModifiedOutsideError('no dsthost')
d = {}
if not rule.proto:
d['proto'] = 'any'
d['portBegin'] = 'any'
d['portEnd'] = None
else:
d['proto'] = rule.proto
if rule.dstports is None:
raise FirewallModifiedOutsideError('no dstport')
d['portBegin'] = rule.dstports.range[0]
d['portEnd'] = rule.dstports.range[1] \
if rule.dstports.range[0] != rule.dstports.range[1] \
else None
if rule.dsthost.type == 'dsthost':
d['address'] = str(rule.dsthost)
d['netmask'] = 32
elif rule.dsthost.type == 'dst4':
network = ipaddress.IPv4Network(rule.dsthost)
d['address'] = str(network.network_address)
d['netmask'] = int(network.prefixlen)
else:
raise FirewallModifiedOutsideError(
'cannot map dsthost.type={!s}'.format(rule.dsthost))
if rule.expire is not None:
d['expire'] = int(rule.expire)
return d
def get_firewall_conf(self, vm):
conf = {
'allow': None,
'allowDns': False,
'allowIcmp': False,
'allowYumProxy': False,
'rules': [],
}
common_action = None
tentative_action = None
reversed_rules = list(reversed(vm.firewall.rules))
while reversed_rules:
rule = reversed_rules[0]
if rule.dsthost is not None or rule.proto is not None:
break
tentative_action = reversed_rules.pop(0).action
if not reversed_rules:
conf['allow'] = tentative_action == 'accept'
return conf
for rule in reversed_rules:
if rule.specialtarget == 'dns':
conf['allowDns'] = (rule.action == 'accept')
continue
if rule.proto == 'icmp':
if rule.icmptype is not None:
raise FirewallModifiedOutsideError(
'cannot map icmptype != None')
conf['allowIcmp'] = (rule.action == 'accept')
continue
if common_action is None:
common_action = rule.action
elif common_action != rule.action:
raise FirewallModifiedOutsideError('incoherent action')
conf['rules'].insert(0, self.rule_to_dict(rule))
if common_action is None or common_action != tentative_action:
# we've got only specialtarget and/or icmp
conf['allow'] = tentative_action == 'accept'
return conf
raise FirewallModifiedOutsideError('it does not add up')
def write_firewall_conf(self, vm, conf):
common_action = qubesadmin.firewall.Action(
'drop' if conf['allow'] else 'accept')
rules = []
for rule in conf['rules']:
kwargs = {}
if rule['proto'] != 'any':
kwargs['proto'] = rule['proto']
if rule['portBegin'] != 'any':
kwargs['dstports'] = '-'.join(map(str, filter((lambda x: x),
(rule['portBegin'], rule['portEnd']))))
netmask = str(rule['netmask']) if rule['netmask'] != 32 else None
rules.append(qubesadmin.firewall.Rule(None,
action=common_action,
dsthost='/'.join(map(str, filter((lambda x: x),
(rule['address'], netmask)))),
**kwargs))
if conf['allowDns']:
rules.append(qubesadmin.firewall.Rule(None,
action='accept', specialtarget='dns'))
if conf['allowIcmp']:
rules.append(qubesadmin.firewall.Rule(None,
action='accept', proto='icmp'))
if common_action == 'drop':
rules.append(qubesadmin.firewall.Rule(None,
action='accept'))
vm.firewall.rules = rules
def set_vm(self, vm):
self.__vm = vm
self.clearChildren()
conf = vm.get_firewall_conf()
conf = self.get_firewall_conf(vm)
self.allow = conf["allow"]
self.allowDns = conf["allowDns"]
@ -252,7 +381,7 @@ class QubesFirewallRulesModel(QAbstractItemModel):
})
if self.fw_changed:
self.__vm.write_firewall_conf(conf)
self.write_firewall_conf(self.__vm, conf)
if self.__vm.is_running():
vm = self.__vm.netvm
@ -287,15 +416,11 @@ class QubesFirewallRulesModel(QAbstractItemModel):
if index.isValid() and role == Qt.DisplayRole:
return self.__columnValues[index.column()](index.row())
return QVariant()
def headerData(self, section, orientation, role=Qt.DisplayRole):
if section < len(self.__columnNames) \
and orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.__columnNames[section]
return QVariant()
@property
def children(self):
return self.__children