Пример #1
0
    def check_entry(entry, options, ipset_type):
        family = "ipv4"
        if "family" in options:
            if options["family"] == "inet6":
                family = "ipv6"

        if ipset_type == "hash:ip":
            if "-" in entry:
                splits = entry.split("-")
                if len(splits) != 2:
                    raise FirewallError(
                        INVALID_ENTRY, "entry '%s' does not match ipset type '%s'" % (entry, ipset_type)
                    )
                for split in splits:
                    if (family == "ipv4" and not checkIP(entry)) or (family == "ipv6" and not checkIP6(entry)):
                        raise FirewallError(
                            INVALID_ENTRY, "entry '%s' does not match ipset type '%s'" % (entry, ipset_type)
                        )
            else:
                if (family == "ipv4" and not checkIPnMask(entry)) or (family == "ipv6" and not checkIP6nMask(entry)):
                    raise FirewallError(
                        INVALID_ENTRY, "entry '%s' does not match ipset type '%s'" % (entry, ipset_type)
                    )
        elif ipset_type == "hash:net":
            if (family == "ipv4" and not checkIPnMask(entry)) or (family == "ipv6" and not checkIP6nMask(entry)):
                raise FirewallError(INVALID_ENTRY, "entry '%s' does not match ipset type '%s'" % (entry, ipset_type))
        elif ipset_type == "hash:mac":
            # ipset does not allow to add 00:00:00:00:00:00
            if not check_mac(entry) or entry == "00:00:00:00:00:00":
                raise FirewallError(INVALID_ENTRY, "entry '%s' does not match ipset type '%s'" % (entry, ipset_type))
        else:
            raise FirewallError(INVALID_IPSET, "ipset type '%s' not usable" % ipset_type)
Пример #2
0
 def parse_source(self, value):
     if not checkIPnMask(value) and not checkIP6nMask(value) \
        and not check_mac(value) and not \
        (value.startswith("ipset:") and len(value) > 6):
         raise FirewallError(errors.INVALID_ADDR,
                             "'%s' is no valid IPv4, IPv6 or MAC address, nor an ipset" % value)
     return value
Пример #3
0
    def _check_config(self, config, item, all_config):
        common_check_config(self, config, item, all_config)

        if item == "target":
            if config not in ZONE_TARGETS:
                raise FirewallError(errors.INVALID_TARGET, config)
        elif item == "interfaces":
            for interface in config:
                if not checkInterface(interface):
                    raise FirewallError(errors.INVALID_INTERFACE, interface)
                if self.fw_config:
                    for zone in self.fw_config.get_zones():
                        if zone == self.name:
                            continue
                        if interface in self.fw_config.get_zone(
                                zone).interfaces:
                            raise FirewallError(
                                errors.INVALID_INTERFACE,
                                "interface '{}' already bound to zone '{}'".
                                format(interface, zone))
        elif item == "sources":
            for source in config:
                if not checkIPnMask(source) and not checkIP6nMask(source) and \
                   not check_mac(source) and not source.startswith("ipset:"):
                    raise FirewallError(errors.INVALID_ADDR, source)
                if self.fw_config:
                    for zone in self.fw_config.get_zones():
                        if zone == self.name:
                            continue
                        if source in self.fw_config.get_zone(zone).sources:
                            raise FirewallError(
                                errors.INVALID_ADDR,
                                "source '{}' already bound to zone '{}'".
                                format(source, zone))
Пример #4
0
    def startElement(self, name, attrs):
        self.item.parser_check_element_attrs(name, attrs)

        if name == "service":
            if "name" in attrs:
                log.warning("Ignoring deprecated attribute name='%s'" % 
                            attrs["name"])
            if "version" in attrs:
                self.item.version = str(attrs["version"])
        elif name == "short":
            pass
        elif name == "description":
            pass
        elif name == "port":
            self.item.ports.append((str(attrs["port"]),
                                       str(attrs["protocol"])))
        elif name == "destination":
            for x in [ "ipv4", "ipv6" ]:
                if x in attrs:
                    s = str(attrs[x])
                    if x == "ipv4" and not functions.checkIPnMask(s):
                        raise FirewallError(INVALID_DESTINATION, s)
                    if x == "ipv6" and not functions.checkIP6nMask(s):
                        raise FirewallError(INVALID_DESTINATION, s)
                    self.item.destination[x] = str(attrs[x])
        elif name == "module":
            self.item.modules.append(str(attrs["name"]))
Пример #5
0
    def startElement(self, name, attrs):
        self.item.parser_check_element_attrs(name, attrs)

        if name == "service":
            if "name" in attrs:
                log.warning("Ignoring deprecated attribute name='%s'" %
                            attrs["name"])
            if "version" in attrs:
                self.item.version = str(attrs["version"])
        elif name == "short":
            pass
        elif name == "description":
            pass
        elif name == "port":
            self.item.ports.append(
                (str(attrs["port"]), str(attrs["protocol"])))
        elif name == "destination":
            for x in ["ipv4", "ipv6"]:
                if x in attrs:
                    s = str(attrs[x])
                    if x == "ipv4" and not functions.checkIPnMask(s):
                        raise FirewallError(INVALID_DESTINATION, s)
                    if x == "ipv6" and not functions.checkIP6nMask(s):
                        raise FirewallError(INVALID_DESTINATION, s)
                    self.item.destination[x] = str(attrs[x])
        elif name == "module":
            self.item.modules.append(str(attrs["name"]))
Пример #6
0
    def check_entry(entry, options, ipset_type):
        family = "ipv4"
        if "family" in options:
            if options["family"] == "inet6":
                family = "ipv6"

        if ipset_type == "hash:ip":
            if "-" in entry:
                splits = entry.split("-")
                if len(splits) != 2:
                    raise FirewallError(
                        errors.INVALID_ENTRY,
                        "entry '%s' does not match ipset type '%s'" % \
                        (entry, ipset_type))
                for split in splits:
                    if (family == "ipv4" and not checkIP(split)) or \
                       (family == "ipv6" and not checkIP6(split)):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "entry '%s' does not match ipset type '%s'" % \
                            (entry, ipset_type))
            else:
                if (family == "ipv4" and not checkIPnMask(entry)) or \
                   (family == "ipv6" and not checkIP6nMask(entry)):
                    raise FirewallError(
                        errors.INVALID_ENTRY,
                        "entry '%s' does not match ipset type '%s'" % \
                        (entry, ipset_type))
        elif ipset_type == "hash:net":
            if (family == "ipv4" and not checkIPnMask(entry)) or \
               (family == "ipv6" and not checkIP6nMask(entry)):
                raise FirewallError(
                    errors.INVALID_ENTRY,
                    "entry '%s' does not match ipset type '%s'" % \
                    (entry, ipset_type))
        elif ipset_type == "hash:mac":
            # ipset does not allow to add 00:00:00:00:00:00
            if not check_mac(entry) or entry == "00:00:00:00:00:00":
                raise FirewallError(
                    errors.INVALID_ENTRY,
                    "entry '%s' does not match ipset type '%s'" % \
                    (entry, ipset_type))
        else:
            raise FirewallError(errors.INVALID_IPSET,
                                "ipset type '%s' not usable" % ipset_type)
Пример #7
0
 def _check_config(self, config, item):
     if item == "services" and self.fw_config:
         existing_services = self.fw_config.get_services()
         for service in config:
             if service not in existing_services:
                 raise FirewallError(errors.INVALID_SERVICE,
                                     "'%s' not among existing services" % \
                                     service)
     elif item == "ports":
         for port in config:
             check_port(port[0])
             check_tcpudp(port[1])
     elif item == "protocols":
         for proto in config:
             check_protocol(proto)
     elif item == "icmp_blocks" and self.fw_config:
         existing_icmptypes = self.fw_config.get_icmptypes()
         for icmptype in config:
             if icmptype not in existing_icmptypes:
                 raise FirewallError(errors.INVALID_ICMPTYPE,
                                     "'%s' not among existing icmp types" % \
                                     icmptype)
     elif item == "forward_ports":
         for fwd_port in config:
             check_port(fwd_port[0])
             check_tcpudp(fwd_port[1])
             if not fwd_port[2] and not fwd_port[3]:
                 raise FirewallError(
                     errors.INVALID_FORWARD,
                     "'%s' is missing to-port AND to-addr " % fwd_port)
             if fwd_port[2]:
                 check_port(fwd_port[2])
             if fwd_port[3]:
                 if not checkIP(fwd_port[3]) and not checkIP6(fwd_port[3]):
                     raise FirewallError(
                         errors.INVALID_ADDR,
                         "to-addr '%s' is not a valid address" %
                         fwd_port[3])
     elif item == "source_ports":
         for port in config:
             check_port(port[0])
             check_tcpudp(port[1])
     elif item == "target":
         if config not in ZONE_TARGETS:
             raise FirewallError(errors.INVALID_TARGET, config)
     elif item == "interfaces":
         for interface in config:
             if not checkInterface(interface):
                 raise FirewallError(errors.INVALID_INTERFACE, interface)
     elif item == "sources":
         for source in config:
             if not checkIPnMask(source) and not checkIP6nMask(source) and \
                not check_mac(source) and not source.startswith("ipset:"):
                 raise FirewallError(errors.INVALID_ADDR, source)
     elif item == "rules_str":
         for rule in config:
             rich.Rich_Rule(rule_str=rule)
Пример #8
0
 def check_address(self, ipv, source):
     if ipv == "ipv4":
         if not functions.checkIPnMask(source):
             raise FirewallError(INVALID_ADDR, source)
     elif ipv == "ipv6":
         if not functions.checkIP6nMask(source):
             raise FirewallError(INVALID_ADDR, source)
     else:
         raise FirewallError(INVALID_IPV)
Пример #9
0
 def check_address(self, ipv, source):
     if ipv == "ipv4":
         if not functions.checkIPnMask(source):
             raise FirewallError(INVALID_ADDR, source)
     elif ipv == "ipv6":
         if not functions.checkIP6nMask(source):
             raise FirewallError(INVALID_ADDR, source)
     else:
         raise FirewallError(INVALID_IPV)
Пример #10
0
 def _check_config(self, config, item):
     if item == "services" and self.fw_config:
         existing_services = self.fw_config.get_services()
         for service in config:
             if not service in existing_services:
                 raise FirewallError(errors.INVALID_SERVICE,
                                     "'%s' not among existing services" % \
                                     service)
     elif item == "ports":
         for port in config:
             check_port(port[0])
             check_tcpudp(port[1])
     elif item == "protocols":
         for proto in config:
             check_protocol(proto)
     elif item == "icmp_blocks" and self.fw_config:
         existing_icmptypes = self.fw_config.get_icmptypes()
         for icmptype in config:
             if not icmptype in existing_icmptypes:
                 raise FirewallError(errors.INVALID_ICMPTYPE,
                                     "'%s' not among existing icmp types" % \
                                     icmptype)
     elif item == "forward_ports":
         for fwd_port in config:
             check_port(fwd_port[0])
             check_tcpudp(fwd_port[1])
             if not fwd_port[2] and not fwd_port[3]:
                 raise FirewallError(
                     errors.INVALID_FORWARD,
                     "'%s' is missing to-port AND to-addr " % fwd_port)
             if fwd_port[2]:
                 check_port(fwd_port[2])
             if fwd_port[3]:
                 if not checkIP(fwd_port[3]):
                     raise FirewallError(
                         errors.INVALID_ADDR,
                         "to-addr '%s' is not a valid address" % fwd_port[3])
     elif item == "source_ports":
         for port in config:
             check_port(port[0])
             check_tcpudp(port[1])
     elif item == "target":
         if config not in ZONE_TARGETS:
             raise FirewallError(errors.INVALID_TARGET, config)
     elif item == "interfaces":
         for interface in config:
             if not checkInterface(interface):
                 raise FirewallError(errors.INVALID_INTERFACE, interface)
     elif item == "sources":
         for source in config:
             if not checkIPnMask(source) and not checkIP6nMask(source) and \
                not check_mac(source) and not source.startswith("ipset:"):
                 raise FirewallError(errors.INVALID_ADDR, source)
     elif item == "rules_str":
         for rule in config:
             Rich_Rule(rule_str=rule)
Пример #11
0
 def check_address(self, ipv, source):
     if ipv == "ipv4":
         if not functions.checkIPnMask(source):
             raise FirewallError(errors.INVALID_ADDR, source)
     elif ipv == "ipv6":
         if not functions.checkIP6nMask(source):
             raise FirewallError(errors.INVALID_ADDR, source)
     else:
         raise FirewallError(errors.INVALID_IPV,
                             "'%s' not in {'ipv4'|'ipv6'}")
Пример #12
0
 def check_address(self, ipv, source):
     if ipv == "ipv4":
         if not functions.checkIPnMask(source):
             raise FirewallError(errors.INVALID_ADDR, source)
     elif ipv == "ipv6":
         if not functions.checkIP6nMask(source):
             raise FirewallError(errors.INVALID_ADDR, source)
     else:
         raise FirewallError(errors.INVALID_IPV,
                             "'%s' not in {'ipv4'|'ipv6'}")
Пример #13
0
 def check_source(self, source, applied=False):
     if checkIPnMask(source):
         return "ipv4"
     elif checkIP6nMask(source):
         return "ipv6"
     elif check_mac(source):
         return ""
     elif source.startswith("ipset:"):
         self._check_ipset_type_for_source(source[6:])
         if applied:
             self._check_ipset_applied(source[6:])
         return self._ipset_family(source[6:])
     else:
         raise FirewallError(errors.INVALID_ADDR, source)
Пример #14
0
    def _check_config(self, config, item, all_config):
        common_check_config(self, config, item, all_config)

        if item == "target":
            if config not in ZONE_TARGETS:
                raise FirewallError(errors.INVALID_TARGET, config)
        elif item == "interfaces":
            for interface in config:
                if not checkInterface(interface):
                    raise FirewallError(errors.INVALID_INTERFACE, interface)
        elif item == "sources":
            for source in config:
                if not checkIPnMask(source) and not checkIP6nMask(source) and \
                   not check_mac(source) and not source.startswith("ipset:"):
                    raise FirewallError(errors.INVALID_ADDR, source)
Пример #15
0
    def _check_config(self, config, item, all_config, all_io_objects):
        common_check_config(self, config, item, all_config, all_io_objects)

        if self.name in all_io_objects["policies"]:
            raise FirewallError(
                errors.NAME_CONFLICT,
                "Zone '{}': Can't have the same name as a policy.".format(
                    self.name))

        if item == "target":
            if config not in ZONE_TARGETS:
                raise FirewallError(
                    errors.INVALID_TARGET,
                    "Zone '{}': invalid target '{}'".format(self.name, config))
        elif item == "interfaces":
            for interface in config:
                if not checkInterface(interface):
                    raise FirewallError(
                        errors.INVALID_INTERFACE,
                        "Zone '{}': invalid interface '{}'".format(
                            self.name, interface))
                for zone in all_io_objects["zones"]:
                    if zone == self.name:
                        continue
                    if interface in all_io_objects["zones"][zone].interfaces:
                        raise FirewallError(
                            errors.INVALID_INTERFACE,
                            "Zone '{}': interface '{}' already bound to zone '{}'"
                            .format(self.name, interface, zone))
        elif item == "sources":
            for source in config:
                if not checkIPnMask(source) and not checkIP6nMask(source) and \
                   not check_mac(source) and not source.startswith("ipset:"):
                    raise FirewallError(
                        errors.INVALID_ADDR,
                        "Zone '{}': invalid source '{}'".format(
                            self.name, source))
                for zone in all_io_objects["zones"]:
                    if zone == self.name:
                        continue
                    if source in all_io_objects["zones"][zone].sources:
                        raise FirewallError(
                            errors.INVALID_ADDR,
                            "Zone '{}': source '{}' already bound to zone '{}'"
                            .format(self.name, source, zone))
Пример #16
0
    def check_entry(entry, options, ipset_type):
        family = "ipv4"
        if "family" in options:
            if options["family"] == "inet6":
                family = "ipv6"

        if not ipset_type.startswith("hash:"):
            raise FirewallError(errors.INVALID_IPSET,
                                "ipset type '%s' not usable" % ipset_type)
        flags = ipset_type[5:].split(",")
        items = entry.split(",")

        if len(flags) != len(items) or len(flags) < 1:
            raise FirewallError(
                errors.INVALID_ENTRY,
                "entry '%s' does not match ipset type '%s'" % \
                (entry, ipset_type))

        for i in range(len(flags)):
            flag = flags[i]
            item = items[i]

            if flag == "ip":
                if "-" in item and family == "ipv4":
                    # IP ranges only with plain IPs, no masks
                    if i > 1:
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address '%s' in '%s'[%d]" % \
                            (item, entry, i))
                    splits = item.split("-")
                    if len(splits) != 2:
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address range '%s' in '%s' for %s (%s)" % \
                            (item, entry, ipset_type, family))
                    for _split in splits:
                        if (family == "ipv4" and not checkIP(_split)) or \
                           (family == "ipv6" and not checkIP6(_split)):
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid address '%s' in '%s' for %s (%s)" % \
                                (_split, entry, ipset_type, family))
                else:
                    # IPs with mask only allowed in the first
                    # position of the type
                    if family == "ipv4":
                        if item == "0.0.0.0":
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid address '%s' in '%s' for %s (%s)" % \
                                (item, entry, ipset_type, family))
                        if i == 0:
                            ip_check = checkIPnMask
                        else:
                            ip_check = checkIP
                    else:
                        ip_check = checkIP6
                    if not ip_check(item):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address '%s' in '%s' for %s (%s)" % \
                            (item, entry, ipset_type, family))
            elif flag == "net":
                if "-" in item:
                    # IP ranges only with plain IPs, no masks
                    splits = item.split("-")
                    if len(splits) != 2:
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address range '%s' in '%s' for %s (%s)" % \
                            (item, entry, ipset_type, family))
                    # First part can only be a plain IP
                    if (family == "ipv4" and not checkIP(splits[0])) or \
                       (family == "ipv6" and not checkIP6(splits[0])):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address '%s' in '%s' for %s (%s)" % \
                            (splits[0], entry, ipset_type, family))
                    # Second part can also have a mask
                    if (family == "ipv4" and not checkIPnMask(splits[1])) or \
                       (family == "ipv6" and not checkIP6nMask(splits[1])):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address '%s' in '%s' for %s (%s)" % \
                            (splits[1], entry, ipset_type, family))
                else:
                    # IPs with mask allowed in all positions, but no /0
                    if item.endswith("/0"):
                        if not (family == "ipv6" and i == 0 and
                                ipset_type == "hash:net,iface"):
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid address '%s' in '%s' for %s (%s)" % \
                                (item, entry, ipset_type, family))
                    if (family == "ipv4" and not checkIPnMask(item)) or \
                       (family == "ipv6" and not checkIP6nMask(item)):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address '%s' in '%s' for %s (%s)" % \
                            (item, entry, ipset_type, family))
            elif flag == "mac":
                # ipset does not allow to add 00:00:00:00:00:00
                if not check_mac(item) or item == "00:00:00:00:00:00":
                    raise FirewallError(
                        errors.INVALID_ENTRY,
                        "invalid mac address '%s' in '%s'" % (item, entry))
            elif flag == "port":
                if ":" in item:
                    splits = item.split(":")
                    if len(splits) != 2:
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid port '%s'" % (item))
                    if splits[0] == "icmp":
                        if family != "ipv4":
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid protocol for family '%s' in '%s'" % \
                                (family, entry))
                        if not check_icmp_name(splits[1]) and not \
                           check_icmp_type(splits[1]):
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid icmp type '%s' in '%s'" % \
                                (splits[1], entry))
                    elif splits[0] in [ "icmpv6", "ipv6-icmp" ]:
                        if family != "ipv6":
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid protocol for family '%s' in '%s'" % \
                                (family, entry))
                        if not check_icmpv6_name(splits[1]) and not \
                           check_icmpv6_type(splits[1]):
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid icmpv6 type '%s' in '%s'" % \
                                (splits[1], entry))
                    elif splits[0] not in [ "tcp", "sctp", "udp", "udplite" ] \
                       and not checkProtocol(splits[0]):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid protocol '%s' in '%s'" % (splits[0],
                                                               entry))
                    elif not check_port(splits[1]):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid port '%s'in '%s'" % (splits[1], entry))
                else:
                    if not check_port(item):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid port '%s' in '%s'" % (item, entry))
            elif flag == "mark":
                if item.startswith("0x"):
                    try:
                        int_val = int(item, 16)
                    except ValueError:
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid mark '%s' in '%s'" % (item, entry))
                else:
                    try:
                        int_val = int(item)
                    except ValueError:
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid mark '%s' in '%s'" % (item, entry))
                if int_val < 0 or int_val > 4294967295:
                    raise FirewallError(
                        errors.INVALID_ENTRY,
                        "invalid mark '%s' in '%s'" % (item, entry))
            elif flag == "iface":
                if not checkInterface(item) or len(item) > 15:
                    raise FirewallError(
                        errors.INVALID_ENTRY,
                        "invalid interface '%s' in '%s'" % (item, entry))
            else:
                raise FirewallError(errors.INVALID_IPSET,
                                    "ipset type '%s' not usable" % ipset_type)
Пример #17
0
    def startElement(self, name, attrs):
        IO_Object_ContentHandler.startElement(self, name, attrs)
        if self._rule_error:
            return

        self.item.parser_check_element_attrs(name, attrs)

        if common_startElement(self, name, attrs):
            return

        elif name == "zone":
            if "name" in attrs:
                log.warning("Ignoring deprecated attribute name='%s'",
                            attrs["name"])
            if "version" in attrs:
                self.item.version = attrs["version"]
            if "immutable" in attrs:
                log.warning("Ignoring deprecated attribute immutable='%s'",
                            attrs["immutable"])
            if "target" in attrs:
                target = attrs["target"]
                if target not in ZONE_TARGETS:
                    raise FirewallError(errors.INVALID_TARGET, target)
                if target != "" and target != DEFAULT_ZONE_TARGET:
                    self.item.target = target

        elif name == "forward":
            if self.item.forward:
                log.warning("Forward already set, ignoring.")
            else:
                self.item.forward = True

        elif name == "interface":
            if self._rule:
                log.warning('Invalid rule: interface use in rule.')
                self._rule_error = True
                return
            # zone bound to interface
            if "name" not in attrs:
                log.warning('Invalid interface: Name missing.')
                self._rule_error = True
                return
            if attrs["name"] not in self.item.interfaces:
                self.item.interfaces.append(attrs["name"])
            else:
                log.warning("Interface '%s' already set, ignoring.",
                            attrs["name"])

        elif name == "source":
            if self._rule:
                if self._rule.source:
                    log.warning(
                        "Invalid rule: More than one source in rule '%s', ignoring.",
                        str(self._rule))
                    self._rule_error = True
                    return
                invert = False
                if "invert" in attrs and \
                        attrs["invert"].lower() in [ "yes", "true" ]:
                    invert = True
                addr = mac = ipset = None
                if "address" in attrs:
                    addr = attrs["address"]
                if "mac" in attrs:
                    mac = attrs["mac"]
                if "ipset" in attrs:
                    ipset = attrs["ipset"]
                self._rule.source = rich.Rich_Source(addr,
                                                     mac,
                                                     ipset,
                                                     invert=invert)
                return
            # zone bound to source
            if "address" not in attrs and "ipset" not in attrs:
                log.warning('Invalid source: No address no ipset.')
                return
            if "address" in attrs and "ipset" in attrs:
                log.warning('Invalid source: Address and ipset.')
                return
            if "family" in attrs:
                log.warning("Ignoring deprecated attribute family='%s'",
                            attrs["family"])
            if "invert" in attrs:
                log.warning('Invalid source: Invertion not allowed here.')
                return
            if "address" in attrs:
                if not checkIPnMask(attrs["address"]) and \
                   not checkIP6nMask(attrs["address"]) and \
                   not check_mac(attrs["address"]):
                    raise FirewallError(errors.INVALID_ADDR, attrs["address"])
            if "ipset" in attrs:
                entry = "ipset:%s" % attrs["ipset"]
                if entry not in self.item.sources:
                    self.item.sources.append(entry)
                else:
                    log.warning("Source '%s' already set, ignoring.",
                                attrs["address"])
            if "address" in attrs:
                entry = attrs["address"]
                if entry not in self.item.sources:
                    self.item.sources.append(entry)
                else:
                    log.warning("Source '%s' already set, ignoring.",
                                attrs["address"])

        elif name == "icmp-block-inversion":
            if self.item.icmp_block_inversion:
                log.warning("Icmp-Block-Inversion already set, ignoring.")
            else:
                self.item.icmp_block_inversion = True

        else:
            log.warning("Unknown XML element '%s'", name)
            return
Пример #18
0
    def check_entry(entry, options, ipset_type):
        family = "ipv4"
        if "family" in options:
            if options["family"] == "inet6":
                family = "ipv6"

        if not ipset_type.startswith("hash:"):
            raise FirewallError(errors.INVALID_IPSET,
                                "ipset type '%s' not usable" % ipset_type)
        flags = ipset_type[5:].split(",")
        items = entry.split(",")

        if len(flags) != len(items) or len(flags) < 1:
            raise FirewallError(
                errors.INVALID_ENTRY,
                "entry '%s' does not match ipset type '%s'" % \
                (entry, ipset_type))

        for i in range(len(flags)):
            flag = flags[i]
            item = items[i]

            if flag == "ip":
                if "-" in item and family == "ipv4":
                    # IP ranges only with plain IPs, no masks
                    if i > 1:
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address '%s' in '%s'[%d]" % \
                            (item, entry, i))
                    splits = item.split("-")
                    if len(splits) != 2:
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address range '%s' in '%s' for %s (%s)" % \
                            (item, entry, ipset_type, family))
                    for _split in splits:
                        if (family == "ipv4" and not checkIP(_split)) or \
                           (family == "ipv6" and not checkIP6(_split)):
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid address '%s' in '%s' for %s (%s)" % \
                                (_split, entry, ipset_type, family))
                else:
                    # IPs with mask only allowed in the first
                    # position of the type
                    if family == "ipv4":
                        if item == "0.0.0.0":
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid address '%s' in '%s' for %s (%s)" % \
                                (item, entry, ipset_type, family))
                        if i == 0:
                            ip_check = checkIPnMask
                        else:
                            ip_check = checkIP
                    else:
                        ip_check = checkIP6
                    if not ip_check(item):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address '%s' in '%s' for %s (%s)" % \
                            (item, entry, ipset_type, family))
            elif flag == "net":
                if "-" in item:
                    # IP ranges only with plain IPs, no masks
                    splits = item.split("-")
                    if len(splits) != 2:
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address range '%s' in '%s' for %s (%s)" % \
                            (item, entry, ipset_type, family))
                    # First part can only be a plain IP
                    if (family == "ipv4" and not checkIP(splits[0])) or \
                       (family == "ipv6" and not checkIP6(splits[0])):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address '%s' in '%s' for %s (%s)" % \
                            (splits[0], entry, ipset_type, family))
                    # Second part can also have a mask
                    if (family == "ipv4" and not checkIPnMask(splits[1])) or \
                       (family == "ipv6" and not checkIP6nMask(splits[1])):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address '%s' in '%s' for %s (%s)" % \
                            (splits[1], entry, ipset_type, family))
                else:
                    # IPs with mask allowed in all positions, but no /0
                    if item.endswith("/0"):
                        if not (family == "ipv6" and i == 0 and
                                ipset_type == "hash:net,iface"):
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid address '%s' in '%s' for %s (%s)" % \
                                (item, entry, ipset_type, family))
                    if (family == "ipv4" and not checkIPnMask(item)) or \
                       (family == "ipv6" and not checkIP6nMask(item)):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid address '%s' in '%s' for %s (%s)" % \
                            (item, entry, ipset_type, family))
            elif flag == "mac":
                # ipset does not allow to add 00:00:00:00:00:00
                if not check_mac(item) or item == "00:00:00:00:00:00":
                    raise FirewallError(
                        errors.INVALID_ENTRY,
                        "invalid mac address '%s' in '%s'" % (item, entry))
            elif flag == "port":
                if ":" in item:
                    splits = item.split(":")
                    if len(splits) != 2:
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid port '%s'" % (item))
                    if splits[0] == "icmp":
                        if family != "ipv4":
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid protocol for family '%s' in '%s'" % \
                                (family, entry))
                        if not check_icmp_name(splits[1]) and not \
                           check_icmp_type(splits[1]):
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid icmp type '%s' in '%s'" % \
                                (splits[1], entry))
                    elif splits[0] in [ "icmpv6", "ipv6-icmp" ]:
                        if family != "ipv6":
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid protocol for family '%s' in '%s'" % \
                                (family, entry))
                        if not check_icmpv6_name(splits[1]) and not \
                           check_icmpv6_type(splits[1]):
                            raise FirewallError(
                                errors.INVALID_ENTRY,
                                "invalid icmpv6 type '%s' in '%s'" % \
                                (splits[1], entry))
                    elif splits[0] not in [ "tcp", "sctp", "udp", "udplite" ] \
                       and not checkProtocol(splits[0]):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid protocol '%s' in '%s'" % (splits[0],
                                                               entry))
                    elif not check_port(splits[1]):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid port '%s'in '%s'" % (splits[1], entry))
                else:
                    if not check_port(item):
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid port '%s' in '%s'" % (item, entry))
            elif flag == "mark":
                if item.startswith("0x"):
                    try:
                        int_val = int(item, 16)
                    except ValueError:
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid mark '%s' in '%s'" % (item, entry))
                else:
                    try:
                        int_val = int(item)
                    except ValueError:
                        raise FirewallError(
                            errors.INVALID_ENTRY,
                            "invalid mark '%s' in '%s'" % (item, entry))
                if int_val < 0 or int_val > 4294967295:
                    raise FirewallError(
                        errors.INVALID_ENTRY,
                        "invalid mark '%s' in '%s'" % (item, entry))
            elif flag == "iface":
                if not checkInterface(item) or len(item) > 15:
                    raise FirewallError(
                        errors.INVALID_ENTRY,
                        "invalid interface '%s' in '%s'" % (item, entry))
            else:
                raise FirewallError(errors.INVALID_IPSET,
                                    "ipset type '%s' not usable" % ipset_type)
Пример #19
0
    def startElement(self, name, attrs):
        IO_Object_ContentHandler.startElement(self, name)
        if self._rule_error:
            return

        self.item.parser_check_element_attrs(name, attrs)

        if name == "zone":
            if "name" in attrs:
                log.warning("Ignoring deprecated attribute name='%s'",
                            attrs["name"])
            if "version" in attrs:
                self.item.version = attrs["version"]
            if "immutable" in attrs:
                log.warning("Ignoring deprecated attribute immutable='%s'",
                            attrs["immutable"])
            if "target" in attrs:
                target = attrs["target"]
                if target not in ZONE_TARGETS:
                    raise FirewallError(INVALID_TARGET, target)
                if target != "" and target != DEFAULT_ZONE_TARGET:
                    self.item.target = target

        elif name == "short":
            pass
        elif name == "description":
            pass
        elif name == "service":
            if self._rule:
                if self._rule.element:
                    log.warning("Invalid rule: More than one element in rule '%s', ignoring.",
                                str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = Rich_Service(attrs["name"])
                return
            if attrs["name"] not in self.item.services:
                self.item.services.append(attrs["name"])
            else:
                log.warning("Service '%s' already set, ignoring.",
                            attrs["name"])

        elif name == "port":
            if self._rule:
                if self._rule.element:
                    log.warning("Invalid rule: More than one element in rule '%s', ignoring.",
                                str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = Rich_Port(attrs["port"],
                                               attrs["protocol"])
                return
            check_port(attrs["port"])
            check_tcpudp(attrs["protocol"])
            entry = (portStr(attrs["port"], "-"), attrs["protocol"])
            if entry not in self.item.ports:
                self.item.ports.append(entry)
            else:
                log.warning("Port '%s/%s' already set, ignoring.",
                            attrs["port"], attrs["protocol"])

        elif name == "protocol":
            if self._rule:
                if self._rule.element:
                    log.warning("Invalid rule: More than one element in rule '%s', ignoring.",
                                str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = Rich_Protocol(attrs["value"])
            else:
                check_protocol(attrs["value"])
                if attrs["value"] not in self.item.protocols:
                    self.item.protocols.append(attrs["value"])
                else:
                    log.warning("Protocol '%s' already set, ignoring.",
                                attrs["value"])
        elif name == "icmp-block":
            if self._rule:
                if self._rule.element:
                    log.warning("Invalid rule: More than one element in rule '%s', ignoring.",
                                str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = Rich_IcmpBlock(attrs["name"])
                return
            if attrs["name"] not in self.item.icmp_blocks:
                self.item.icmp_blocks.append(attrs["name"])
            else:
                log.warning("icmp-block '%s' already set, ignoring.",
                            attrs["name"])

        elif name == "masquerade":
            if "enabled" in attrs:
                log.warning("Ignoring deprecated attribute enabled='%s'",
                            attrs["enabled"])
            if self._rule:
                if self._rule.element:
                    log.warning("Invalid rule: More than one element in rule '%s', ignoring.",
                                str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = Rich_Masquerade()
            else:
                if self.item.masquerade:
                    log.warning("Masquerade already set, ignoring.")
                else:
                    self.item.masquerade = True

        elif name == "forward-port":
            to_port = ""
            if "to-port" in attrs:
                to_port = attrs["to-port"]
            to_addr = ""
            if "to-addr" in attrs:
                to_addr = attrs["to-addr"]

            if self._rule:
                if self._rule.element:
                    log.warning("Invalid rule: More than one element in rule '%s', ignoring.",
                                str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = Rich_ForwardPort(attrs["port"],
                                                      attrs["protocol"],
                                                      to_port, to_addr)
                return

            check_port(attrs["port"])
            check_tcpudp(attrs["protocol"])
            if to_port:
                check_port(to_port)
            if to_addr:
                if not checkIP(to_addr):
                    raise FirewallError(INVALID_ADDR,
                                        "to-addr '%s' is not a valid address" \
                                        % to_addr)
            entry = (portStr(attrs["port"], "-"), attrs["protocol"],
                     portStr(to_port, "-"), str(to_addr))
            if entry not in self.item.forward_ports:
                self.item.forward_ports.append(entry)
            else:
                log.warning("Forward port %s/%s%s%s already set, ignoring.",
                            attrs["port"], attrs["protocol"],
                            " >%s" % to_port if to_port else "",
                            " @%s" % to_addr if to_addr else "")

        elif name == "interface":
            if self._rule:
                log.warning('Invalid rule: interface use in rule.')
                self._rule_error = True
                return
            # zone bound to interface
            if "name" not in attrs:
                log.warning('Invalid interface: Name missing.')
                self._rule_error = True
                return
            attrs["name"]
            if attrs["name"] not in self.item.interfaces:
                self.item.interfaces.append(attrs["name"])
            else:
                log.warning("Interface '%s' already set, ignoring.",
                            attrs["name"])

        elif name == "source":
            if self._rule:
                if self._rule.source:
                    log.warning("Invalid rule: More than one source in rule '%s', ignoring.",
                                str(self._rule))
                    self._rule_error = True
                    return
                invert = False
                if "invert" in attrs and \
                        attrs["invert"].lower() in [ "yes", "true" ]:
                    invert = True
                addr = mac = ipset = None
                if "address" in attrs:
                    addr = attrs["address"]
                if "mac" in attrs:
                    mac = attrs["mac"]
                if "ipset" in attrs:
                    ipset = attrs["ipset"]
                self._rule.source = Rich_Source(addr, mac, ipset, invert=invert)
                return
            # zone bound to source
            if "address" not in attrs and not "ipset" in attrs:
                log.warning('Invalid source: No address no ipset.')
                return
            if "address" in attrs and "ipset" in attrs:
                log.warning('Invalid source: Address and ipset.')
                return
            if "family" in attrs:
                log.warning("Ignoring deprecated attribute family='%s'",
                            attrs["family"])
            if "invert" in attrs:
                log.warning('Invalid source: Invertion not allowed here.')
                return
            if "address" in attrs:
                if not checkIPnMask(attrs["address"]) and \
                   not checkIP6nMask(attrs["address"]) and \
                   not check_mac(attrs["address"]):
                    raise FirewallError(INVALID_ADDR, source)
            if "ipset" in attrs:
                entry = "ipset:%s" % attrs["ipset"]
                if entry not in self.item.sources:
                    self.item.sources.append(entry)
                else:
                    log.warning("Source '%s' already set, ignoring.",
                                attrs["address"])
            if "address" in attrs:
                entry = attrs["address"]
                if entry not in self.item.sources:
                    self.item.sources.append(entry)
                else:
                    log.warning("Source '%s' already set, ignoring.",
                                attrs["address"])

        elif name == "destination":
            if not self._rule:
                log.warning('Invalid rule: Destination outside of rule')
                self._rule_error = True
                return
            if self._rule.destination:
                log.warning("Invalid rule: More than one destination in rule '%s', ignoring.",
                            str(self._rule))
                return
            invert = False
            if "invert" in attrs and \
                    attrs["invert"].lower() in [ "yes", "true" ]:
                invert = True
            self._rule.destination = Rich_Destination(attrs["address"],
                                                      invert)

        elif name in [ "accept", "reject", "drop", "mark" ]:
            if not self._rule:
                log.warning('Invalid rule: Action outside of rule')
                self._rule_error = True
                return
            if self._rule.action:
                log.warning('Invalid rule: More than one action')
                self._rule_error = True
                return
            if name == "accept":
                self._rule.action = Rich_Accept()
            elif name == "reject":
                _type = None
                if "type" in attrs:
                    _type = attrs["type"]
                self._rule.action = Rich_Reject(_type)
            elif name == "drop":
                self._rule.action = Rich_Drop()
            elif name == "mark":
                _set = attrs["set"]
                self._rule.action = Rich_Mark(_set)
            self._limit_ok = self._rule.action

        elif name == "log":
            if not self._rule:
                log.warning('Invalid rule: Log outside of rule')
                return
            if self._rule.log:
                log.warning('Invalid rule: More than one log')
                return
            level = None
            if "level" in attrs:
                level = attrs["level"]
                if level not in [ "emerg", "alert", "crit", "error",
                                  "warning", "notice", "info", "debug" ]:
                    log.warning('Invalid rule: Invalid log level')
                    self._rule_error = True
                    return
            prefix = attrs["prefix"] if "prefix" in attrs else None
            self._rule.log = Rich_Log(prefix, level)
            self._limit_ok = self._rule.log

        elif name == "audit":
            if not self._rule:
                log.warning('Invalid rule: Audit outside of rule')
                return
            if self._rule.audit:
                log.warning("Invalid rule: More than one audit in rule '%s', ignoring.",
                            str(self._rule))
                self._rule_error = True
                return            
            self._rule.audit = Rich_Audit()
            self._limit_ok = self._rule.audit

        elif name == "rule":
            family = None
            if "family" in attrs:
                family = attrs["family"]
                if family not in [ "ipv4", "ipv6" ]:
                    log.warning('Invalid rule: Rule family "%s" invalid',
                                attrs["family"])
                    self._rule_error = True
                    return
            self._rule = Rich_Rule(family)

        elif name == "limit":
            if not self._limit_ok:
                log.warning('Invalid rule: Limit outside of action, log and audit')
                self._rule_error = True
                return
            if self._limit_ok.limit:
                log.warning("Invalid rule: More than one limit in rule '%s', ignoring.",
                            str(self._rule))
                self._rule_error = True
                return
            value = attrs["value"]
            self._limit_ok.limit = Rich_Limit(value)

        else:
            log.warning("Unknown XML element '%s'", name)
            return
Пример #20
0
    def startElement(self, name, attrs):
        IO_Object_ContentHandler.startElement(self, name, attrs)
        if self._rule_error:
            return

        self.item.parser_check_element_attrs(name, attrs)

        if name == "zone":
            if "name" in attrs:
                log.warning("Ignoring deprecated attribute name='%s'",
                            attrs["name"])
            if "version" in attrs:
                self.item.version = attrs["version"]
            if "immutable" in attrs:
                log.warning("Ignoring deprecated attribute immutable='%s'",
                            attrs["immutable"])
            if "target" in attrs:
                target = attrs["target"]
                if target not in ZONE_TARGETS:
                    raise FirewallError(errors.INVALID_TARGET, target)
                if target != "" and target != DEFAULT_ZONE_TARGET:
                    self.item.target = target

        elif name == "short":
            pass
        elif name == "description":
            pass
        elif name == "service":
            if self._rule:
                if self._rule.element:
                    log.warning(
                        "Invalid rule: More than one element in rule '%s', ignoring.",
                        str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = rich.Rich_Service(attrs["name"])
                return
            if attrs["name"] not in self.item.services:
                self.item.services.append(attrs["name"])
            else:
                log.warning("Service '%s' already set, ignoring.",
                            attrs["name"])

        elif name == "port":
            if self._rule:
                if self._rule.element:
                    log.warning(
                        "Invalid rule: More than one element in rule '%s', ignoring.",
                        str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = rich.Rich_Port(attrs["port"],
                                                    attrs["protocol"])
                return
            check_port(attrs["port"])
            check_tcpudp(attrs["protocol"])
            entry = (portStr(attrs["port"], "-"), attrs["protocol"])
            if entry not in self.item.ports:
                self.item.ports.append(entry)
            else:
                log.warning("Port '%s/%s' already set, ignoring.",
                            attrs["port"], attrs["protocol"])

        elif name == "protocol":
            if self._rule:
                if self._rule.element:
                    log.warning(
                        "Invalid rule: More than one element in rule '%s', ignoring.",
                        str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = rich.Rich_Protocol(attrs["value"])
            else:
                check_protocol(attrs["value"])
                if attrs["value"] not in self.item.protocols:
                    self.item.protocols.append(attrs["value"])
                else:
                    log.warning("Protocol '%s' already set, ignoring.",
                                attrs["value"])
        elif name == "icmp-block":
            if self._rule:
                if self._rule.element:
                    log.warning(
                        "Invalid rule: More than one element in rule '%s', ignoring.",
                        str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = rich.Rich_IcmpBlock(attrs["name"])
                return
            if attrs["name"] not in self.item.icmp_blocks:
                self.item.icmp_blocks.append(attrs["name"])
            else:
                log.warning("icmp-block '%s' already set, ignoring.",
                            attrs["name"])

        elif name == "icmp-type":
            if self._rule:
                if self._rule.element:
                    log.warning(
                        "Invalid rule: More than one element in rule '%s', ignoring.",
                        str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = rich.Rich_IcmpType(attrs["name"])
                return
            else:
                log.warning("Invalid rule: icmp-block '%s' outside of rule",
                            attrs["name"])

        elif name == "masquerade":
            if "enabled" in attrs and \
               attrs["enabled"].lower() in [ "no", "false" ] :
                log.warning("Ignoring deprecated attribute enabled='%s'",
                            attrs["enabled"])
                return

            if self._rule:
                if self._rule.element:
                    log.warning(
                        "Invalid rule: More than one element in rule '%s', ignoring.",
                        str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = rich.Rich_Masquerade()
            else:
                if self.item.masquerade:
                    log.warning("Masquerade already set, ignoring.")
                else:
                    self.item.masquerade = True

        elif name == "forward-port":
            to_port = ""
            if "to-port" in attrs:
                to_port = attrs["to-port"]
            to_addr = ""
            if "to-addr" in attrs:
                to_addr = attrs["to-addr"]

            if self._rule:
                if self._rule.element:
                    log.warning(
                        "Invalid rule: More than one element in rule '%s', ignoring.",
                        str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = rich.Rich_ForwardPort(
                    attrs["port"], attrs["protocol"], to_port, to_addr)
                return

            check_port(attrs["port"])
            check_tcpudp(attrs["protocol"])
            if to_port:
                check_port(to_port)
            if to_addr:
                if not checkIP(to_addr) and not checkIP6(to_addr):
                    raise FirewallError(errors.INVALID_ADDR,
                                        "to-addr '%s' is not a valid address" \
                                        % to_addr)
            entry = (portStr(attrs["port"], "-"), attrs["protocol"],
                     portStr(to_port, "-"), str(to_addr))
            if entry not in self.item.forward_ports:
                self.item.forward_ports.append(entry)
            else:
                log.warning("Forward port %s/%s%s%s already set, ignoring.",
                            attrs["port"], attrs["protocol"],
                            " >%s" % to_port if to_port else "",
                            " @%s" % to_addr if to_addr else "")

        elif name == "source-port":
            if self._rule:
                if self._rule.element:
                    log.warning(
                        "Invalid rule: More than one element in rule '%s', ignoring.",
                        str(self._rule))
                    self._rule_error = True
                    return
                self._rule.element = rich.Rich_SourcePort(
                    attrs["port"], attrs["protocol"])
                return
            check_port(attrs["port"])
            check_tcpudp(attrs["protocol"])
            entry = (portStr(attrs["port"], "-"), attrs["protocol"])
            if entry not in self.item.source_ports:
                self.item.source_ports.append(entry)
            else:
                log.warning("Source port '%s/%s' already set, ignoring.",
                            attrs["port"], attrs["protocol"])

        elif name == "interface":
            if self._rule:
                log.warning('Invalid rule: interface use in rule.')
                self._rule_error = True
                return
            # zone bound to interface
            if "name" not in attrs:
                log.warning('Invalid interface: Name missing.')
                self._rule_error = True
                return
            if attrs["name"] not in self.item.interfaces:
                self.item.interfaces.append(attrs["name"])
            else:
                log.warning("Interface '%s' already set, ignoring.",
                            attrs["name"])

        elif name == "source":
            if self._rule:
                if self._rule.source:
                    log.warning(
                        "Invalid rule: More than one source in rule '%s', ignoring.",
                        str(self._rule))
                    self._rule_error = True
                    return
                invert = False
                if "invert" in attrs and \
                        attrs["invert"].lower() in [ "yes", "true" ]:
                    invert = True
                addr = mac = ipset = None
                if "address" in attrs:
                    addr = attrs["address"]
                if "mac" in attrs:
                    mac = attrs["mac"]
                if "ipset" in attrs:
                    ipset = attrs["ipset"]
                self._rule.source = rich.Rich_Source(addr,
                                                     mac,
                                                     ipset,
                                                     invert=invert)
                return
            # zone bound to source
            if "address" not in attrs and "ipset" not in attrs:
                log.warning('Invalid source: No address no ipset.')
                return
            if "address" in attrs and "ipset" in attrs:
                log.warning('Invalid source: Address and ipset.')
                return
            if "family" in attrs:
                log.warning("Ignoring deprecated attribute family='%s'",
                            attrs["family"])
            if "invert" in attrs:
                log.warning('Invalid source: Invertion not allowed here.')
                return
            if "address" in attrs:
                if not checkIPnMask(attrs["address"]) and \
                   not checkIP6nMask(attrs["address"]) and \
                   not check_mac(attrs["address"]):
                    raise FirewallError(errors.INVALID_ADDR, attrs["address"])
            if "ipset" in attrs:
                entry = "ipset:%s" % attrs["ipset"]
                if entry not in self.item.sources:
                    self.item.sources.append(entry)
                else:
                    log.warning("Source '%s' already set, ignoring.",
                                attrs["address"])
            if "address" in attrs:
                entry = attrs["address"]
                if entry not in self.item.sources:
                    self.item.sources.append(entry)
                else:
                    log.warning("Source '%s' already set, ignoring.",
                                attrs["address"])

        elif name == "destination":
            if not self._rule:
                log.warning('Invalid rule: Destination outside of rule')
                self._rule_error = True
                return
            if self._rule.destination:
                log.warning(
                    "Invalid rule: More than one destination in rule '%s', ignoring.",
                    str(self._rule))
                return
            invert = False
            if "invert" in attrs and \
                    attrs["invert"].lower() in [ "yes", "true" ]:
                invert = True
            self._rule.destination = rich.Rich_Destination(
                attrs["address"], invert)

        elif name in ["accept", "reject", "drop", "mark"]:
            if not self._rule:
                log.warning('Invalid rule: Action outside of rule')
                self._rule_error = True
                return
            if self._rule.action:
                log.warning('Invalid rule: More than one action')
                self._rule_error = True
                return
            if name == "accept":
                self._rule.action = rich.Rich_Accept()
            elif name == "reject":
                _type = None
                if "type" in attrs:
                    _type = attrs["type"]
                self._rule.action = rich.Rich_Reject(_type)
            elif name == "drop":
                self._rule.action = rich.Rich_Drop()
            elif name == "mark":
                _set = attrs["set"]
                self._rule.action = rich.Rich_Mark(_set)
            self._limit_ok = self._rule.action

        elif name == "log":
            if not self._rule:
                log.warning('Invalid rule: Log outside of rule')
                return
            if self._rule.log:
                log.warning('Invalid rule: More than one log')
                return
            level = None
            if "level" in attrs:
                level = attrs["level"]
                if level not in [
                        "emerg", "alert", "crit", "error", "warning", "notice",
                        "info", "debug"
                ]:
                    log.warning('Invalid rule: Invalid log level')
                    self._rule_error = True
                    return
            prefix = attrs["prefix"] if "prefix" in attrs else None
            self._rule.log = rich.Rich_Log(prefix, level)
            self._limit_ok = self._rule.log

        elif name == "audit":
            if not self._rule:
                log.warning('Invalid rule: Audit outside of rule')
                return
            if self._rule.audit:
                log.warning(
                    "Invalid rule: More than one audit in rule '%s', ignoring.",
                    str(self._rule))
                self._rule_error = True
                return
            self._rule.audit = rich.Rich_Audit()
            self._limit_ok = self._rule.audit

        elif name == "rule":
            family = None
            if "family" in attrs:
                family = attrs["family"]
                if family not in ["ipv4", "ipv6"]:
                    log.warning('Invalid rule: Rule family "%s" invalid',
                                attrs["family"])
                    self._rule_error = True
                    return
            self._rule = rich.Rich_Rule(family)

        elif name == "limit":
            if not self._limit_ok:
                log.warning(
                    'Invalid rule: Limit outside of action, log and audit')
                self._rule_error = True
                return
            if self._limit_ok.limit:
                log.warning(
                    "Invalid rule: More than one limit in rule '%s', ignoring.",
                    str(self._rule))
                self._rule_error = True
                return
            value = attrs["value"]
            self._limit_ok.limit = rich.Rich_Limit(value)

        elif name == "icmp-block-inversion":
            if self.item.icmp_block_inversion:
                log.warning("Icmp-Block-Inversion already set, ignoring.")
            else:
                self.item.icmp_block_inversion = True

        else:
            log.warning("Unknown XML element '%s'", name)
            return