Example #1
0
    def compare(self, rule_d):
        """
        Compare two rules
        :param rule_d: Rule to be compared
        :return: 0 -> If the rule is not equal
                 1 -> If the rule (syntactically) is the same
                 2 -> If even the name of the rule is the same
        """
        if self.DEBUG:
            tools_debug(self.DEBUG, 'rule.compare', 'entering')
        data_rule = rule_d.get_rule()
        result = 0
        if self.source_address == data_rule[1] and \
           self.destination_address == data_rule[2] and \
           self.destination_port == data_rule[3] and \
           self.source_port == data_rule[4] and \
           self.protocol == data_rule[5] and \
           self.permit == data_rule[6] and \
           self.wildcard == data_rule[7]:
            result = 1
            if self.name == data_rule[0]:
                result = 2
        if self.DEBUG:
            tools_debug(self.DEBUG, 'rule.compare', 'result', result)

        return result
Example #2
0
    def link(self,
             sIP,
             dIP,
             dPort,
             sPort,
             proto,
             rules_exclude=[],
             show_deny=False,
             hide_allow_all=True,
             showallmatches=False,
             ignore_dsmo=False,
             anyport=False,
             strict_search=False,
             is_0_any=True):
        """
        Check a flow against the policy

        :param sIP: Source IP
        :param dIP: Destination IP
        :param dPort: Destination Port
        :param sPort: Source Port
        :param proto: Protocol
        :param rules_exclude: Allow to exclude rules from being checked
        :param show_deny: Will show if a DENY is matched
        :param hide_allow_all: HIDE rules PERMIT ANY ANY
        :param showallmatches: will show all rules in the policy that match the flow (not just the first one)
        :param ignore_dsmo: Will ignore a match if the rule has a non-contiguous wildcard
        :param anyport: anyport matched will match the rule (usually with port ranges)
        :param strict_search: It makes a the search of the rules STRICT, so when we look for ANY in Source/Dest (0.0.0.0/0),
                              only ANY will match (we are not looking for any value but for the 0.0.0.0/0.0.0.0 value). Also
                              it applies to protocols: if we are looking for IP protocol, only IP protocol will match, but
                              if we are looking for TCP, TCP and IP will match.
        if Source IP or Destination IP is 0.0.0.0 and we want to match them with rules with ANY (or equivalent) in the rule
        :param is_0_any: FALSE when sIP/dIP is 0.0.0.0 but is the HOST 0.0.0.0/255.255.255.255 (or 0.0.0.0/0.0.0.0 with wildcards)
        :return: list of rules found
        """
        rulef = 0
        rules_found = []
        for rule in self.rules:
            if self.DEBUG:
                if self.rules[0] == '<empty>':
                    print '<empty>'
            if not self.rules[0] == '<empty>':
                rulef = rule.check_ip_acl(sIP, dIP, dPort, sPort, proto,
                                          show_deny, hide_allow_all, anyport,
                                          strict_search, is_0_any)
                if self.DEBUG:
                    tools_debug(self.DEBUG, 'link', 'Result check_ip_acl:',
                                rulef)

            if rulef > 0:
                if rules_exclude and rulef in rules_exclude:
                    continue
                if rule.get_contiguous() > 0 and ignore_dsmo:
                    continue
                rules_found.append(rulef)
                if not showallmatches:
                    break
        return rules_found
Example #3
0
    def _checkPort(self, DP, Port, anyport, strict_search):
        """
        Check if "Port" is matched or not for a Source or Destination port of the rule (DP switch)
        :param DP: TRUE -> Destination Port, FALSE -> Source Port
        :param Port: Port to check
        :param anyport: switch to know if with any port matched is enough (usually with a port range)
        :return: TRUE/FALSE
        """
        lport = False

        if DP:
            list_port = self.destination_port
        else:
            list_port = self.source_port

        if self.DEBUG:
            tools_debug(self.DEBUG, '_checkPort', DP, Port, list_port, anyport,
                        strict_search)

        # Port with value 70000 means ANY
        if not strict_search and (Port == '0' or list_port == '0'):
            lport = True
        elif strict_search and (Port == '0' and list_port == '0'):
            lport = True
        else:
            for tport in list_port.split(','):
                if '-' in Port:
                    Port1 = Port.split('-')[0]
                    Port2 = Port.split('-')[1]
                    # We are checking a port range
                    if '-' not in tport:
                        if anyport:
                            lport = int(Port1) <= int(tport) <= int(Port2)
                            # If not anyport is checked, this won't match, so no "else".
                    else:
                        if anyport:
                            # dPort is bigger or dPort1 is inside the rule range or dPort2 is inside the rule range
                            lport = int(Port1) <= int(tport.split('-')[0]) and int(tport.split('-')[1]) <= int(Port2) or \
                                    int(tport.split('-')[0]) <= int(Port1) <= int(tport.split('-')[1]) or \
                                    int(tport.split('-')[0]) <= int(Port2) <= int(tport.split('-')[1])
                        else:
                            lport = int(tport.split('-')[0]) <= int(
                                Port1) and int(Port2) <= int(
                                    tport.split('-')[1])

                else:
                    if '-' in tport:
                        lport = int(tport.split('-')[0]) <= int(Port) <= int(
                            tport.split('-')[1])
                    else:
                        lport = int(tport) == int(Port)
                if lport:
                    break
        return lport
Example #4
0
    def remove_rule(self, rule_number):
        if self.DEBUG:
            tools_debug(self.DEBUG, 'find_rule', 'Entering remove_rule', rule_number)
        if self.rules[0] == '<empty>':
            return False
        for rule in self.rules:
            if rule.get_rule_number() == rule_number:
                self.rules.remove(rule)
                break
        if len(self.rules) == 0:
            self.rules = ['<empty>']

        return True
Example #5
0
    def last_deny(self):
        """
        Check if the last rule is DENY ANY ANY
        :return: TRUE/FALSE
        """
        if self.DEBUG:
            tools_debug(self.DEBUG, 'last_deny', 'Entering')
        if self.rules[0] == '<empty>':
            return False
        last = self.rules[-1].get_rule()

        if not last[6]:  # DENY
            if last[7]:  # Wildcard
                return last[1] == '0.0.0.0/255.255.255.255' and last[2] == '0.0.0.0/255.255.255.255'
            else:
                return last[1] == '0.0.0.0/0.0.0.0' and last[2] == '0.0.0.0/0.0.0.0'
        return False
Example #6
0
    def remove_shadowed_rules(self):
        """
        Check for a "basic" shadowed. It's only going to remove those rules that are fully shadowed.

        This method requires that the rules were split before
        :return: Number of removed rules (None in case of any error)
        """

        num_rules_removed = 0
        list_rules_removed = {}
        if self.DEBUG:
            tools_debug(self.DEBUG, 'remove_shadowed_rules', 'Entering"')
        if self.rules[0] == '<empty>':
            return list_rules_removed
        num_rule = len(self.rules)
        while num_rule > 0:
            rule = self.rules[num_rule-1].get_rule()
            # This method requires that the rules were split before
            if ',' in rule[1] or ',' in rule[2]:
                return None
            if rule[6]: # Only for Permit rules
                if rule[7]: # If it's a wildcard we need to change it
                    # Source
                    ip = rule[1].split('/')[0]
                    wild = rule[1].split('/')[1]
                    mask = mask_to_wild(wild) # This function is "bidirectional"
                    rule[1] = ip + '/' + mask
                    # Destination
                    ip = rule[2].split('/')[0]
                    wild = rule[2].split('/')[1]
                    mask = mask_to_wild(wild) # This function is "bidirectional"
                    rule[2] = ip + '/' + mask

                check = self.link(rule[1], rule[2], rule[3], rule[4], rule[5], rules_exclude=[num_rule], show_deny=True, hide_allow_all=False, strict_search=True, is_0_any=True, anyport=True)
                if len(check) > 0:
                    t_rule = self.rules[check[0]-1].get_rule()
                    if t_rule[6]:
                        if self.DEBUG:
                            tools_debug(self.DEBUG, 'remove_shadowed_rules', 'Removing rule', num_rule)
                        list_rules_removed[rule[0]] = self.rules[check[0]-1].get_rule()[0]
                        self.remove_rule(num_rule)
                        self.renum_policy()
                        num_rules_removed += 1
                    else:
                        if self.DEBUG:
                            tools_debug(self.DEBUG, 'remove_shadowed_rules', 'Matched with DENY. NOT removing', num_rule)
            num_rule -= 1

        return list_rules_removed
Example #7
0
    def split_ips(self):
        """
        Some ACLs (like Juniper) allow to have multiple IPs in Source and Destination. This function split all these rules
        in rules with only one source and only one destination, adding some extra information in the name to identify them

        The name of the rules will be changed to:
            - <original rule name>{<original rule number>{<counter>{[<source_ip>,<destination_ip>]

        :return: True
        """
        if self.DEBUG:
            tools_debug(self.DEBUG, 'split_ips', 'Entering split_ips')
        cont_rules = 0
        for rule in self.rules:
            cont_rules += 1
            if self.DEBUG:
                if self.rules[0] == '<empty>':
                    print '<empty>'
                else:
                    rule.print_rule()
            if not self.rules[0] == '<empty>':
                rule_data = rule.get_rule()
                if rule_data[0].startswith(
                        '^'
                ):  # Special rule, usually empty, we don't need to check it
                    continue
                if ',' not in rule_data[1] and ',' not in rule_data[2]:
                    continue
                if ',' in rule_data[1]:
                    ips_list = rule_data[1].split(',')
                else:
                    ips_list = [rule_data[1]]

                if ',' in rule_data[2]:
                    ipd_list = rule_data[2].split(',')
                else:
                    ipd_list = [rule_data[2]]
                rule_number = cont_rules
                num_split = 0
                for ips in ips_list:
                    for ipd in ipd_list:
                        num_split += 1
                        rule_name = rule_data[0] + '{' + str(
                            cont_rules) + '{' + str(num_split) + '{' + str(
                                [ips, ipd])

                        if rule_number == cont_rules:
                            '''
                            We need to split the current rule into multiple rules, so instead of remove the current one
                            and add all the split, we change the current one with the data of the first "new rule"
                            '''
                            rule.set_source(ips)
                            rule.set_destination(ipd)
                            rule.set_name(rule_name)
                        else:
                            newrule = FWRule()
                            newrule.set_rule_data(sAddress=ips,
                                                  dAddress=ipd,
                                                  dPort=rule_data[3],
                                                  sPort=rule_data[4],
                                                  protocol=rule_data[5],
                                                  ACCEPT=rule_data[6],
                                                  wildcard=False,
                                                  name=rule_name,
                                                  rulenumber=rule_number)
                            if self.DEBUG:
                                newrule.set_debug()
                            self.rules.insert(rule_number - 1, newrule)
                        rule_number += 1

                self.renum_policy()

        return True
Example #8
0
    def check_ip_acl(self, sIP, dIP, dPort, sPort, proto, show_deny_any,
                     hide_allow_all, anyport, strict_search, is_0_any):
        """
        Check a flow in a rule
        :param sIP: Source IP
        :param dIP: Destination IP
        :param dPort: Destination Port
        :param sPort: Source Port
        :param proto: Protocol
        :param show_deny_any: Switch to SHOW a match in a DENY ALL ALL
        :param hide_allow_all: Switch to HIDE any PERMIT ALL ALL
        :param anyport: switch to match any port (usually for port ranges)
        :param strict_search: True/False (check explanation in link)
        :param is_0_any: FALSE when sIP/dIP is 0.0.0.0 but is the HOST 0.0.0.0/255.255.255.255 (or 0.0.0.0/0.0.0.0 with wildcards)
        :return: Integer (rule number matched)
        """
        def _any_in_source():
            return ('any' in self.source_address) or \
                   (self.wildcard and self.source_address == '0.0.0.0/255.255.255.255') or \
                   (not self.wildcard and self.source_address == '0.0.0.0/0.0.0.0')

        def _any_in_dest():
            return ('any' in self.destination_address) or \
                   (self.wildcard and self.destination_address == '0.0.0.0/255.255.255.255') or \
                   (not self.wildcard and self.destination_address == '0.0.0.0/0.0.0.0')

        def _check_ip(origin_ip, ip_to_check, wildcard):
            match = False
            for ip in origin_ip.split(','):
                if ':' in ip:
                    # IPv6
                    pass
                else:
                    netT = ip.split('/')[0]
                    filtT = ip.split('/')[1]

                    if '/' in ip_to_check:
                        if '.' not in ip_to_check.split('/')[1]:
                            ip_to_check = ip_to_check.split(
                                '/')[0] + '/' + cidr_to_mask(
                                    ip_to_check.split('/')[1])
                        if wildcard:
                            ip_to_check = ip_to_check.split(
                                '/')[0] + '/' + mask_to_wild(
                                    ip_to_check.split('/')[1])
                            match = self._checkNetWild(netT, filtT,
                                                       ip_to_check)
                        else:
                            match = self._checkNetMask(netT, filtT,
                                                       ip_to_check)
                    else:
                        if wildcard:
                            match = self._checkIPWild(netT, filtT, ip_to_check)
                        else:
                            match = self._checkIPMask(netT, filtT, ip_to_check)
                    if match:
                        break
            return match

        if self.DEBUG:
            print
            tools_debug(self.DEBUG, 'check_ip_acl', 'sIP:', sIP, 'dIP:', dIP,
                        'dPort:', dPort, 'sPort:', sPort, 'proto:', proto,
                        'show_deny_any:', show_deny_any, 'hide_allow_all:',
                        hide_allow_all, 'anyport:', anyport, 'strict_search:',
                        strict_search, 'is_0_any:', is_0_any)
            self.print_rule()
        match = False
        checked = False

        if not show_deny_any and not self.permit:
            if _any_in_source() and _any_in_dest():
                return 0

        if hide_allow_all and self.permit:
            if _any_in_source() and _any_in_dest():
                return 0

        if self.source_address == '' and self.destination_address == '':
            return 0

        if self.protocol == 'ip' or self.protocol == proto or (
                proto == 'ip' and not strict_search):
            # Link doesn't allow to check from the command line
            # Source IP = 0.0.0.0 and Destination IP = 0.0.0.0
            # so if both are 0.0.0.0 it means that it's being used as module
            # In this case, the only possible matches are also any any in source and destination
            if sIP == '0.0.0.0' and dIP == '0.0.0.0':
                if _any_in_source() and _any_in_dest():
                    # If source/dest are any, then check ports
                    match = True
                else:
                    return 0
            else:
                # Verifying that if strict_search = TRUE then when we have an ANY we are searching for 0.0.0.0
                if strict_search and is_0_any:
                    if sIP == '0.0.0.0' and not _any_in_source():
                        return 0
                    if dIP == '0.0.0.0' and not _any_in_dest():
                        return 0

                if _any_in_source() and _any_in_dest():
                    match = True
                    checked = True

                if not match and _any_in_source():
                    checked = True
                    if dIP == '0.0.0.0' and is_0_any:
                        match = True
                    else:
                        match = _check_ip(self.destination_address, dIP,
                                          self.wildcard)

                if not checked and _any_in_dest():
                    checked = True
                    if sIP == '0.0.0.0' and is_0_any:
                        match = True
                    else:
                        match = _check_ip(self.source_address, sIP,
                                          self.wildcard)

                if not checked:
                    match = _check_ip(
                        self.source_address, sIP, self.wildcard) and _check_ip(
                            self.destination_address, dIP, self.wildcard)

        if match:
            if self.protocol == 'icmp' or self.protocol == 'ip':
                return self.rulenumber
            if self.protocol == 'vrrp' or self.protocol == '112':
                return self.rulenumber
            # If a dPort or sPort is specified in the command line, then it makes sense to check the ports when
            # the protocol is TCP/UDP
            if (dPort != '0' or sPort != '0') and (self.protocol != 'udp'
                                                   and self.protocol != 'tcp'):
                return 0
            result = True
            for tport in dPort.split(','):
                result = result and self._checkPort(True, tport, anyport,
                                                    strict_search)
            if result:  # If still TRUE
                for tport in sPort.split(','):
                    result = result and self._checkPort(
                        False, tport, anyport, strict_search)
            if result:
                return self.rulenumber
        return 0