def _read_rules(self): """Read in rules that were added by ufw.""" rfns = [self.files["rules"]] if self.use_ipv6(): rfns.append(self.files["rules6"]) for f in rfns: try: orig = ufw.util.open_file_read(f) except Exception: err_msg = _("Couldn't open '%s' for reading") % (f) raise UFWError(err_msg) pat_tuple = re.compile(r"^### tuple ###\s*") for line in orig: if pat_tuple.match(line): tupl = pat_tuple.sub("", line) tmp = re.split(r"\s+", tupl.strip()) if len(tmp) < 6 or len(tmp) > 9: warn_msg = _("Skipping malformed tuple (bad length): %s") % (tupl) warn(warn_msg) continue else: # set direction to "in" to support upgrades # from old format, which only had 6 or 8 fields type = "in" interface = "" if len(tmp) == 7 or len(tmp) == 9: if "_" in tmp[-1]: (type, interface) = tmp[-1].split("_") else: type = tmp[-1] try: if len(tmp) < 8: rule = UFWRule(tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], type) else: rule = UFWRule(tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], type) # Removed leading [sd]app_ and unescape spaces pat_space = re.compile("%20") if tmp[6] != "-": rule.dapp = pat_space.sub(" ", tmp[6]) if tmp[7] != "-": rule.sapp = pat_space.sub(" ", tmp[7]) if interface != "": rule.set_interface(type, interface) except UFWError: warn_msg = _("Skipping malformed tuple: %s") % (tupl) warn(warn_msg) continue if f == self.files["rules6"]: rule.set_v6(True) self.rules6.append(rule) else: rule.set_v6(False) self.rules.append(rule) orig.close()
def find_other_position(self, position, v6): '''Return the absolute position in the other list of the rule with the user position of the given list. For example, find_other_position(4, True) will return the absolute position of the rule in the ipv4 list matching the user specified '4' rule in the ipv6 list. ''' # Invalid search (v6 rule with too low position) if v6 and position > len(self.rules6): raise ValueError() # Invalid search (v4 rule with too high position) if not v6 and position > len(self.rules): raise ValueError() if position < 1: raise ValueError() rules = [] if v6: rules = self.rules6 else: rules = self.rules # self.rules[6] is a list of tuples. Some application rules have # multiple tuples but the user specifies by ufw rule, not application # tuple, so we need to find how many tuples there are leading up to # the specified position, which we can then use as an offset for # getting the proper match_rule. app_rules = {} tuple_offset = 0 for i, r in enumerate(rules): if i >= position: break tupl = "" if r.dapp != "" or r.sapp != "": tupl = r.get_app_tuple() if app_rules.has_key(tupl): tuple_offset += 1 else: app_rules[tupl] = True rules = [] if v6: rules = self.rules match_rule = self.rules6[position - 1 + tuple_offset].dup_rule() match_rule.set_v6(False) else: rules = self.rules6 match_rule = self.rules[position - 1 + tuple_offset].dup_rule() match_rule.set_v6(True) count = 1 for r in rules: if UFWRule.match(r, match_rule) == 0: return count count += 1 return 0
def find_other_position(self, position, v6): '''Return the absolute position in the other list of the rule with the user position of the given list. For example, find_other_position(4, True) will return the absolute position of the rule in the ipv4 list matching the user specified '4' rule in the ipv6 list. ''' # Invalid search (v6 rule with too low position) if v6 and position > len(self.rules6): raise ValueError() # Invalid search (v4 rule with too high position) if not v6 and position > len(self.rules): raise ValueError() if position < 1: raise ValueError() rules = [] if v6: rules = self.rules6 else: rules = self.rules # self.rules[6] is a list of tuples. Some application rules have # multiple tuples but the user specifies by ufw rule, not application # tuple, so we need to find how many tuples there are leading up to # the specified position, which we can then use as an offset for # getting the proper match_rule. app_rules = {} tuple_offset = 0 for i, r in enumerate(rules): if i >= position: break tupl = "" if r.dapp != "" or r.sapp != "": tupl = r.get_app_tuple() if tupl in app_rules: tuple_offset += 1 else: app_rules[tupl] = True rules = [] if v6: rules = self.rules match_rule = self.rules6[position - 1 + tuple_offset].dup_rule() match_rule.set_v6(False) else: rules = self.rules6 match_rule = self.rules[position - 1 + tuple_offset].dup_rule() match_rule.set_v6(True) count = 1 for r in rules: if UFWRule.match(r, match_rule) == 0: return count count += 1 return 0
def _get_rule_from_dialog(self): action = self._get_combobox_value('action_cbox').lower() if self.ui.protocol_cbox.get_sensitive(): protocol = self._get_combobox_value('protocol_cbox').lower() else: protocol = 'any' rule = UFWRule(action, protocol) # position pos = self.ui.position_adjustment.get_value() rule.set_position(pos) # direction direction = ('in' if self.ui.in_rbutton.get_active() else 'out') rule.set_direction(direction) # logtype log_map = {'Off': '', 'New Connections': 'log', 'Packets': 'log-all'} logtype = log_map[self._get_combobox_value('rule_logging_cbox')] rule.set_logtype(logtype) # src if self.ui.src_addr_custom_rbutton.get_active(): addr = self.ui.src_addr_custom_entry.get_text() rule.set_src(addr) # src port port = gfw.util.ANY_PORT if self.ui.src_port_custom_rbutton.get_active(): port = self.ui.src_port_custom_entry.get_text() elif self.ui.src_app_rbutton.get_active(): port = self._get_combobox_value('src_app_cbox') rule.sapp = port rule.set_port(port, 'src') # dst if self.ui.dst_addr_custom_rbutton.get_active(): addr = self.ui.dst_addr_custom_entry.get_text() rule.set_dst(addr) # dst port port = gfw.util.ANY_PORT if self.ui.dst_port_custom_rbutton.get_active(): port = self.ui.dst_port_custom_entry.get_text() elif self.ui.dst_app_rbutton.get_active(): port = self._get_combobox_value('dst_app_cbox') rule.dapp = port rule.set_port(port, 'dst') return rule
def set_rule(self, rule, allow_reload=True): """Updates firewall with rule by: * appending the rule to the chain if new rule and firewall enabled * deleting the rule from the chain if found and firewall enabled * inserting the rule if possible and firewall enabled * updating user rules file * reloading the user rules file if rule is modified """ rstr = "" if rule.v6: if not self.use_ipv6(): err_msg = _("Adding IPv6 rule failed: IPv6 not enabled") raise UFWError(err_msg) if rule.action == "limit": # Netfilter doesn't have ip6t_recent yet, so skip return _("Skipping unsupported IPv6 '%s' rule") % (rule.action) if rule.multi and rule.protocol != "udp" and rule.protocol != "tcp": err_msg = _("Must specify 'tcp' or 'udp' with multiple ports") raise UFWError(err_msg) newrules = [] found = False modified = False delete = False rules = self.rules position = rule.position if rule.v6: if self.iptables_version < "1.4" and (rule.dapp != "" or rule.sapp != ""): return _("Skipping IPv6 application rule. Need at least iptables 1.4") rules = self.rules6 # bail if we have a bad position if position < 0 or position > len(rules): err_msg = _("Invalid position '%d'") % (position) raise UFWError(err_msg) if position > 0 and rule.remove: err_msg = _("Cannot specify insert and delete") raise UFWError(err_msg) if position > len(rules): err_msg = _("Cannot insert rule at position '%d'") % position raise UFWError(err_msg) # First construct the new rules list try: rule.normalize() except Exception: raise count = 1 inserted = False matches = 0 last = ("", "", "", "") for r in rules: try: r.normalize() except Exception: raise current = (r.dst, r.src, r.dapp, r.sapp) if count == position: # insert the rule if: # 1. the last rule was not an application rule # 2. the current rule is not an application rule # 3. the last application rule is different than the current # while the new rule is different than the current one if ( (last[2] == "" and last[3] == "" and count > 1) or (current[2] == "" and current[3] == "") or last != current ): inserted = True newrules.append(rule.dup_rule()) last = ("", "", "", "") else: position += 1 last = current count += 1 ret = UFWRule.match(r, rule) if ret < 1: matches += 1 if ret == 0 and not found and not inserted: # If find the rule, add it if it's not to be removed, otherwise # skip it. found = True if not rule.remove: newrules.append(rule.dup_rule()) elif ret < 0 and not rule.remove and not inserted: # If only the action is different, replace the rule if it's not # to be removed. found = True modified = True newrules.append(rule.dup_rule()) else: newrules.append(r) if inserted: if matches > 0: rstr = _("Skipping inserting existing rule") if rule.v6: rstr += " (v6)" return rstr else: # Add rule to the end if it was not already added. if not found and not rule.remove: newrules.append(rule.dup_rule()) # Don't process non-existing or unchanged pre-exisiting rules if not found and rule.remove and not self.dryrun: rstr = _("Could not delete non-existent rule") if rule.v6: rstr += " (v6)" return rstr elif found and not rule.remove and not modified: rstr = _("Skipping adding existing rule") if rule.v6: rstr += " (v6)" return rstr if rule.v6: self.rules6 = newrules else: self.rules = newrules # Update the user rules file try: self._write_rules(rule.v6) except UFWError: raise except Exception: err_msg = _("Couldn't update rules file") UFWError(err_msg) # We wrote out the rules, so set reasonable string. We will change # this below when operating on the live firewall. rstr = _("Rules updated") if rule.v6: rstr = _("Rules updated (v6)") # Operate on the chains if self._is_enabled() and not self.dryrun: flag = "" if modified or self._need_reload(rule.v6) or inserted: rstr = "" if inserted: rstr += _("Rule inserted") else: rstr += _("Rule updated") if rule.v6: rstr += " (v6)" if allow_reload: # Reload the chain try: self._reload_user_rules() except Exception: raise else: rstr += _(" (skipped reloading firewall)") elif found and rule.remove: flag = "-D" rstr = _("Rule deleted") elif not found and not modified and not rule.remove: flag = "-A" rstr = _("Rule added") if flag != "": exe = self.iptables chain_prefix = "ufw" if rule.v6: exe = self.ip6tables chain_prefix = "ufw6" rstr += " (v6)" chain_suffix = "input" if rule.direction == "out": chain_suffix = "output" chain = "%s-user-%s" % (chain_prefix, chain_suffix) # Is the firewall running? err_msg = _("Could not update running firewall") (rc, out) = cmd([exe, "-L", chain, "-n"]) if rc != 0: raise UFWError(err_msg) rule_str = "%s %s %s" % (flag, chain, rule.format_rule()) pat_log = re.compile(r"(-A +)(ufw6?-user-[a-z\-]+)(.*)") for s in self._get_lists_from_formatted(rule_str, chain_prefix, chain_suffix): (rc, out) = cmd([exe] + s) if rc != 0: msg(out, sys.stderr) UFWError(err_msg) # delete any lingering RETURN rules (needed for upgrades) if flag == "-A" and pat_log.search(" ".join(s)): c = pat_log.sub(r"\2", " ".join(s)) (rc, out) = cmd([exe, "-D", c, "-j", "RETURN"]) if rc != 0: debug("FAILOK: -D %s -j RETURN" % (c)) return rstr
def set_rule(self, rule, allow_reload=True): '''Updates firewall with rule by: * appending the rule to the chain if new rule and firewall enabled * deleting the rule from the chain if found and firewall enabled * inserting the rule if possible and firewall enabled * updating user rules file * reloading the user rules file if rule is modified ''' rstr = "" if rule.v6: if not self.use_ipv6(): err_msg = _("Adding IPv6 rule failed: IPv6 not enabled") raise UFWError(err_msg) if rule.action == 'limit': # Netfilter doesn't have ip6t_recent yet, so skip return _("Skipping unsupported IPv6 '%s' rule") % (rule.action) if rule.multi and rule.protocol != "udp" and rule.protocol != "tcp": err_msg = _("Must specify 'tcp' or 'udp' with multiple ports") raise UFWError(err_msg) newrules = [] found = False modified = False rules = self.rules position = rule.position if rule.v6: if self.iptables_version < "1.4" and (rule.dapp != "" or \ rule.sapp != ""): return _( "Skipping IPv6 application rule. Need at least iptables 1.4" ) rules = self.rules6 # bail if we have a bad position if position < 0 or position > len(rules): err_msg = _("Invalid position '%d'") % (position) raise UFWError(err_msg) if position > 0 and rule.remove: err_msg = _("Cannot specify insert and delete") raise UFWError(err_msg) if position > len(rules): err_msg = _("Cannot insert rule at position '%d'") % position raise UFWError(err_msg) # First construct the new rules list try: rule.normalize() except Exception: raise count = 1 inserted = False matches = 0 last = ('', '', '', '') for r in rules: try: r.normalize() except Exception: raise current = (r.dst, r.src, r.dapp, r.sapp) if count == position: # insert the rule if: # 1. the last rule was not an application rule # 2. the current rule is not an application rule # 3. the last application rule is different than the current # while the new rule is different than the current one if (last[2] == '' and last[3] == '' and count > 1) or \ (current[2] == '' and current[3] == '') or \ last != current: inserted = True newrules.append(rule.dup_rule()) last = ('', '', '', '') else: position += 1 last = current count += 1 ret = UFWRule.match(r, rule) if ret < 1: matches += 1 if ret == 0 and not found and not inserted: # If find the rule, add it if it's not to be removed, otherwise # skip it. found = True if not rule.remove: newrules.append(rule.dup_rule()) elif ret < 0 and not rule.remove and not inserted: # If only the action is different, replace the rule if it's not # to be removed. found = True modified = True newrules.append(rule.dup_rule()) else: newrules.append(r) if inserted: if matches > 0: rstr = _("Skipping inserting existing rule") if rule.v6: rstr += " (v6)" return rstr else: # Add rule to the end if it was not already added. if not found and not rule.remove: newrules.append(rule.dup_rule()) # Don't process non-existing or unchanged pre-exisiting rules if not found and rule.remove and not self.dryrun: rstr = _("Could not delete non-existent rule") if rule.v6: rstr += " (v6)" return rstr elif found and not rule.remove and not modified: rstr = _("Skipping adding existing rule") if rule.v6: rstr += " (v6)" return rstr if rule.v6: self.rules6 = newrules else: self.rules = newrules # Update the user rules file try: self._write_rules(rule.v6) except UFWError: raise except Exception: err_msg = _("Couldn't update rules file") UFWError(err_msg) # We wrote out the rules, so set reasonable string. We will change # this below when operating on the live firewall. rstr = _("Rules updated") if rule.v6: rstr = _("Rules updated (v6)") # Operate on the chains if self.is_enabled() and not self.dryrun: flag = "" if modified or self._need_reload(rule.v6) or inserted: rstr = "" if inserted: rstr += _("Rule inserted") else: rstr += _("Rule updated") if rule.v6: rstr += " (v6)" if allow_reload: # Reload the chain try: self._reload_user_rules() except Exception: raise else: rstr += _(" (skipped reloading firewall)") elif found and rule.remove: flag = '-D' rstr = _("Rule deleted") elif not found and not modified and not rule.remove: flag = '-A' rstr = _("Rule added") if flag != "": exe = self.iptables chain_prefix = "ufw" if rule.v6: exe = self.ip6tables chain_prefix = "ufw6" rstr += " (v6)" chain_suffix = "input" if rule.direction == "out": chain_suffix = "output" chain = "%s-user-%s" % (chain_prefix, chain_suffix) # Is the firewall running? err_msg = _("Could not update running firewall") (rc, out) = cmd([exe, '-L', chain, '-n']) if rc != 0: raise UFWError(err_msg) rule_str = "%s %s %s" % (flag, chain, rule.format_rule()) pat_log = re.compile(r'(-A +)(ufw6?-user-[a-z\-]+)(.*)') for s in self._get_lists_from_formatted(rule_str, \ chain_prefix, \ chain_suffix): (rc, out) = cmd([exe] + s) if rc != 0: msg(out, sys.stderr) UFWError(err_msg) # delete any lingering RETURN rules (needed for upgrades) if flag == "-A" and pat_log.search(" ".join(s)): c = pat_log.sub(r'\2', " ".join(s)) (rc, out) = cmd([exe, '-D', c, '-j', 'RETURN']) if rc != 0: debug("FAILOK: -D %s -j RETURN" % (c)) return rstr
def _read_rules(self): '''Read in rules that were added by ufw''' rfns = [self.files['rules']] if self.use_ipv6(): rfns.append(self.files['rules6']) for f in rfns: try: orig = ufw.util.open_file_read(f) except Exception: err_msg = _("Couldn't open '%s' for reading") % (f) raise UFWError(err_msg) pat_tuple = re.compile(r'^### tuple ###\s*') for line in orig: if pat_tuple.match(line): tupl = pat_tuple.sub('', line) tmp = re.split(r'\s+', tupl.strip()) if len(tmp) < 6 or len(tmp) > 9: wmsg = _("Skipping malformed tuple (bad length): %s") \ % (tupl) warn(wmsg) continue else: # set direction to "in" to support upgrades # from old format, which only had 6 or 8 fields dtype = "in" interface = "" if len(tmp) == 7 or len(tmp) == 9: if '_' in tmp[-1]: (dtype, interface) = tmp[-1].split('_') else: dtype = tmp[-1] try: if len(tmp) < 8: rule = UFWRule(tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], dtype) else: rule = UFWRule(tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], dtype) # Removed leading [sd]app_ and unescape spaces pat_space = re.compile('%20') if tmp[6] != "-": rule.dapp = pat_space.sub(' ', tmp[6]) if tmp[7] != "-": rule.sapp = pat_space.sub(' ', tmp[7]) if interface != "": rule.set_interface(dtype, interface) except UFWError: warn_msg = _("Skipping malformed tuple: %s") % \ (tupl) warn(warn_msg) continue if f == self.files['rules6']: rule.set_v6(True) self.rules6.append(rule) else: rule.set_v6(False) self.rules.append(rule) orig.close()
def fromXml(str): elem = etree.XML(str) if elem.tag != 'rule': error("ERROR: Invalid XML, expected \'rule\' element", ERROR_INVALID_XML_NO_RULE) action=elem.get('action', '').lower() if action == '': error("ERROR: Invalid XML, no action specified", ERROR_INVALID_XML_NO_ACTION_XML) protocol=elem.get('protocol', ANY_PROTOCOL).lower() rule = UFWRule(action, protocol) rule.position=int(elem.get('position', 0)) rule.direction=elem.get('direction', 'in').lower() rule.dapp=elem.get('dapp', '') rule.sapp=elem.get('sapp', '') rule.dport=elem.get('dport', ANY_PORT) rule.sport=elem.get('sport', ANY_PORT) rule.dst=elem.get('dst', ANY_ADDR) rule.src=elem.get('src', ANY_ADDR) rule.interface_in=elem.get('interface_in', '') rule.interface_out=elem.get('interface_out', '') rule.logtype=elem.get('logtype', '').lower() rule.v6=elem.get('v6', 'False').lower() == "true" return rule