Пример #1
0
    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()
Пример #2
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 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
Пример #3
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
Пример #4
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
Пример #5
0
    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
Пример #6
0
    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
Пример #7
0
    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()
Пример #8
0
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