From c1501f2ec9061474c1019fc9646d29373795a9be Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Thu, 3 Mar 2011 22:42:10 +0100 Subject: [PATCH] Implemented new, simplified firewall rules editor. - Select firewall default action to accept or deny - Add host exceptions to above rules - New "Add address" window validates inputs and allow only correct - Use service names instead port numbers - Working "Edit" button --- qubesmanager/firewall.py | 185 ++++++++++++++++++++++++++++----------- 1 file changed, 134 insertions(+), 51 deletions(-) diff --git a/qubesmanager/firewall.py b/qubesmanager/firewall.py index acd63c6..2ac6ed1 100644 --- a/qubesmanager/firewall.py +++ b/qubesmanager/firewall.py @@ -21,6 +21,7 @@ import sys import os +import re import xml.etree.ElementTree from PyQt4.QtCore import * @@ -28,10 +29,7 @@ from PyQt4.QtGui import * from qubes.qubes import QubesVmCollection from qubes.qubes import QubesException -from qubes.qubes import qubes_store_filename -from qubes.qubes import QubesVmLabels from qubes.qubes import dry_run -from qubes.qubes import qubes_guid_path import ui_editfwrulesdlg import ui_newfwruledlg @@ -41,70 +39,130 @@ class EditFwRulesDlg (QDialog, ui_editfwrulesdlg.Ui_EditFwRulesDlg): super (EditFwRulesDlg, self).__init__(parent) self.setupUi(self) self.newRuleButton.clicked.connect(self.new_rule_button_pressed) + self.editRuleButton.clicked.connect(self.edit_rule_button_pressed) self.deleteRuleButton.clicked.connect(self.delete_rule_button_pressed) + self.policyAllowRadioButton.toggled.connect(self.policy_radio_toggled) + self.dnsCheckBox.toggled.connect(self.dns_checkbox_toggled) def set_model(self, model): self.__model = model self.rulesTreeView.setModel(model) self.rulesTreeView.header().setResizeMode(QHeaderView.ResizeToContents) self.rulesTreeView.header().setResizeMode(0, QHeaderView.Stretch) + self.set_allow(model.allow) + self.dnsCheckBox.setChecked(model.allowDns) + self.setWindowTitle(model.get_vm_name() + " firewall") + + def set_allow(self, allow): + self.policyAllowRadioButton.setChecked(allow) + self.policyDenyRadioButton.setChecked(not allow) + + def policy_radio_toggled(self, on): + self.__model.allow = self.policyAllowRadioButton.isChecked() + + def dns_checkbox_toggled(self, on): + self.__model.allowDns = on def new_rule_button_pressed(self): dialog = NewFwRuleDlg() + self.run_rule_dialog(dialog) + def edit_rule_button_pressed(self): + dialog = NewFwRuleDlg() + row = self.rulesTreeView.selectedIndexes().pop().row() + item = self.__model.children[row] + dialog.addressEdit.setText(item.address) + service = self.__model.get_service_name(item.portBegin) + dialog.serviceComboBox.insertItem(0, service) + dialog.serviceComboBox.setCurrentIndex(0) + self.run_rule_dialog(dialog, row) + + def run_rule_dialog(self, dialog, row = None): if dialog.exec_(): - name = dialog.nameEdit.text() - allow = dialog.allowCheckBox.isChecked() address = dialog.addressEdit.text() - netmask = dialog.netmasks[dialog.netmaskComboBox.currentIndex()] - portBegin = dialog.portBeginSpinBox.value() - portEnd = dialog.portEndSpinBox.value() - if portEnd <= portBegin: - portEnd = None + service = dialog.serviceComboBox.currentText() + port = None - if portBegin == 0 and portEnd is None: - return + try: + port = int(service) + except (TypeError, ValueError) as ex: + port = self.__model.get_service_port(service) - if name == "": - QMessageBox.warning(None, "Incorrect name", "You need to name the rule.") - return - - if address == "": - QMessageBox.warning(None, "Incorrect address", "Pleas give an address for the rule.") - return - - self.__model.appendChild(QubesFirewallRuleItem(name, allow, address, netmask, portBegin, portEnd)) + if port is not None: + item = QubesFirewallRuleItem(address, 32, port, None) + if row is not None: + self.__model.setChild(row, item) + else: + self.__model.appendChild(item) + else: + QMessageBox.warning(None, "Invalid service name", "Service '{0} is unknown.".format(service)) def delete_rule_button_pressed(self): for i in set([index.row() for index in self.rulesTreeView.selectedIndexes()]): self.__model.removeChild(i) +class QIPAddressValidator(QValidator): + def __init__(self, parent = None): + super (QIPAddressValidator, self).__init__(parent) + + def validate(self, input, pos): + hostname = input + + if len(hostname) > 255 or len(hostname) == 0: + return (QValidator.Intermediate, pos) + + if hostname[-1:] == ".": + hostname = hostname[:-1] + + if hostname[-1:] == "-": + return (QValidator.Intermediate, pos) + + allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?[a-z][a-z0-9-]+)\s+(?P[0-9]+)/(?P[a-z]+)", re.IGNORECASE) + f = open('/etc/services', 'r') + for line in f: + match = pattern.match(line) + if match is not None: + service = match.groupdict() + self.__services.append( (service["name"], int(service["port"]), service["protocol"]) ) + f.close() + + def get_service_name(self, port): + for service in self.__services: + if service[1] == port: + return service[0] + return str(port) + + def get_service_port(self, name): + for service in self.__services: + if service[0] == name: + return service[1] + return None + def set_vm(self, vm): self.__vm = vm self.clearChildren() conf = vm.get_firewall_conf() + + self.allow = conf["allow"] + self.allowDns = conf["allowDns"] + for rule in conf["rules"]: self.appendChild(QubesFirewallRuleItem( - rule["name"], rule["allow"], rule["address"], - rule["netmask"], rule["portBegin"], rule["portEnd"] + rule["address"], rule["netmask"], rule["portBegin"], rule["portEnd"] )) + def get_vm_name(self): + return self.__vm.name + def apply_rules(self): assert self.__vm is not None - conf = { "allow": True, "rules": list() } + conf = { "allow": self.allow, "allowDns": self.allowDns, "rules": list() } for rule in self.children: conf["rules"].append( { - "allow": rule.allow, - "name": rule.name, "address": rule.address, "netmask": rule.netmask, "portBegin": rule.portBegin, @@ -241,6 +319,11 @@ class QubesFirewallRulesModel(QAbstractItemModel): index = self.createIndex(i, 0) self.dataChanged.emit(index, index) + def setChild(self, i, child): + self.children[i] = child + index = self.createIndex(i, 0, child) + self.dataChanged.emit(index, index) + def clearChildren(self): self.__children = list()