Beispiel #1
0
    def __str__(self):
        """Render config output from this term object."""
        # Verify platform specific terms. Skip whole term if platform does not
        # match.
        if self.term.platform:
            if self._PLATFORM not in self.term.platform:
                return ''
        if self.term.platform_exclude:
            if self._PLATFORM in self.term.platform_exclude:
                return ''

        conditions = []

        # if terms does not specify action, use filter default action
        if not self.term.action:
            self.term.action[0].value = self.default_action
        if str(self.term.action[0]) not in self._ACTION_TABLE:
            raise aclgenerator.UnsupportedFilterError(
                '%s %s %s %s' % ('\n', self.term.name, self.term.action[0],
                                 'action not currently supported.'))

        # source address
        term_saddrs = self._CheckAddressAf(self.term.source_address)
        if not term_saddrs:
            logging.debug(
                self.NO_AF_LOG_ADDR.substitute(term=self.term.name,
                                               direction='source',
                                               af=self.af))
            return ''

        conditions.append(
            self._GenerateAddrStatement(term_saddrs,
                                        self.term.source_address_exclude))

        # destination address
        term_daddrs = self._CheckAddressAf(self.term.destination_address)
        if not term_daddrs:
            logging.debug(
                self.NO_AF_LOG_ADDR.substitute(term=self.term.name,
                                               direction='destination',
                                               af=self.af))
            return ''

        conditions.append(
            self._GenerateAddrStatement(term_daddrs,
                                        self.term.destination_address_exclude))

        # protocol
        if self.term.protocol_except:
            raise aclgenerator.UnsupportedFilterError(
                '%s %s %s' %
                ('\n', self.term.name,
                 'protocol_except logic not currently supported.'))
        conditions.append(self._GenerateProtoStatement(self.term.protocol))

        conditions.append(
            self._GeneratePortStatement(self.term.source_port, 'src'))
        conditions.append(
            self._GeneratePortStatement(self.term.destination_port, 'dst'))

        # icmp-type
        icmp_types = ['']
        if self.term.icmp_type:
            if self.term.protocol == ['icmp']:
                af = 'inet'
            elif self.term.protocol == ['icmpv6']:
                af = 'inet6'
            else:
                raise aclgenerator.UnsupportedFilterError(
                    '%s %s %s' %
                    ('\n', self.term.name,
                     'icmp protocol is not defined or not supported.'))
            icmp_types = self.NormalizeIcmpTypes(self.term.icmp_type,
                                                 self.term.protocol, af)

            if 'icmp' in self.term.protocol:
                conditions.append(
                    self._GenerateIcmpType(icmp_types, self.term.icmp_code))

        # tcp options
        if 'tcp' in self.term.protocol:
            conditions.append(self._GenerateTcpOptions(self.term.option))

        cond = Term.JoinConditionals(conditions, 'and')

        # Note that directionally-based pcap filter requires post-processing to
        # replace 'localhost' with whatever the IP(s) of the local machine happen
        # to be.  This bit of logic ensure there's a placeholder with the
        # appropriate booleans around it.  We also have to check that there exists
        # some form of condition already, else we'll end up with something overly
        # broad like 'dst net localhost' (e.g., 'default-deny').
        if cond and self.direction == 'in':
            cond = Term.JoinConditionals(['dst net localhost', cond], 'and')
        elif cond and self.direction == 'out':
            cond = Term.JoinConditionals(['src net localhost', cond], 'and')

        return cond + '\n'
Beispiel #2
0
    def _TranslatePolicy(self, pol, exp_info):
        self.pcap_policies = []
        current_date = datetime.datetime.utcnow().date()
        exp_info_date = current_date + datetime.timedelta(weeks=exp_info)

        good_afs = ['inet', 'inet6', 'mixed']
        good_options = ['in', 'out']
        direction = ''

        for header, terms in pol.filters:
            filter_type = None
            if self._PLATFORM not in header.platforms:
                continue

            filter_options = header.FilterOptions(self._PLATFORM)[1:]
            filter_name = header.FilterName(self._PLATFORM)

            # ensure all options after the filter name are expected
            for opt in filter_options:
                if opt not in good_afs + good_options:
                    raise UnsupportedTargetOptionError(
                        '%s %s %s %s' %
                        ('\nUnsupported option found in', self._PLATFORM,
                         'target definition:', opt))

            if 'in' in filter_options:
                direction = 'in'
            elif 'out' in filter_options:
                direction = 'out'

            # Check for matching af
            for address_family in good_afs:
                if address_family in filter_options:
                    # should not specify more than one AF in options
                    if filter_type is not None:
                        raise aclgenerator.UnsupportedFilterError(
                            '%s %s %s %s' %
                            ('\nMay only specify one of', good_afs,
                             'in filter options:', filter_options))
                    filter_type = address_family
            if filter_type is None:
                filter_type = 'mixed'

            # add the terms
            accept_terms = []
            deny_terms = []
            term_names = set()
            for term in terms:
                if term.name in term_names:
                    raise aclgenerator.DuplicateTermError(
                        'You have a duplicate term: %s' % term.name)

                if term.expiration:
                    if term.expiration <= exp_info_date:
                        logging.info(
                            'INFO: Term %s in policy %s expires '
                            'in less than two weeks.', term.name, filter_name)
                    if term.expiration <= current_date:
                        logging.warning(
                            'WARNING: Term %s in policy %s is expired and '
                            'will not be rendered.', term.name, filter_name)
                        continue

                if not term:
                    continue

                if term.action[0] == 'accept':
                    accept_terms.append(
                        self._TERM(term, filter_name, filter_type, direction))
                elif term.action[0] == 'deny' or term.action[0] == 'reject':
                    deny_terms.append(
                        self._TERM(term, filter_name, filter_type, direction))

            self.pcap_policies.append(
                (header, filter_name, filter_type, accept_terms, deny_terms))
Beispiel #3
0
 def _HandleIcmpTypes(self, icmp_types, protocols):
     if icmp_types:
         raise aclgenerator.UnsupportedFilterError(
             '\n%s %s %s %s' % ('icmp types unsupported by', self._PLATFORM,
                                '\nError in term:', self.term.name))
     return ([''], protocols)
Beispiel #4
0
    def _TranslatePolicy(self, pol, exp_info):
        self.pf_policies = []
        self.address_book = {}
        self.def_short_to_long = {}
        current_date = datetime.datetime.utcnow().date()
        exp_info_date = current_date + datetime.timedelta(weeks=exp_info)

        good_afs = ['inet', 'inet6', 'mixed']
        good_options = ['in', 'out', 'nostate']
        all_protocols_stateful = True

        for header, terms in pol.filters:
            filter_type = None
            if self._PLATFORM not in header.platforms:
                continue

            filter_options = header.FilterOptions(self._PLATFORM)[1:]
            filter_name = header.FilterName(self._PLATFORM)
            direction = ''

            # ensure all options after the filter name are expected
            for opt in filter_options:
                if opt not in good_afs + good_options:
                    raise aclgenerator.UnsupportedTargetOption(
                        '%s %s %s %s' %
                        ('\nUnsupported option found in', self._PLATFORM,
                         'target definition:', opt))

            # pf will automatically add 'keep state flags S/SA' to all TCP connections
            # by default.
            if 'nostate' in filter_options:
                all_protocols_stateful = False

            if 'in' in filter_options:
                direction = 'in'
            elif 'out' in filter_options:
                direction = 'out'

            # Check for matching af
            for address_family in good_afs:
                if address_family in filter_options:
                    # should not specify more than one AF in options
                    if filter_type is not None:
                        raise aclgenerator.UnsupportedFilterError(
                            '%s %s %s %s' %
                            ('\nMay only specify one of', good_afs,
                             'in filter options:', filter_options))
                    filter_type = address_family
            if filter_type is None:
                filter_type = 'inet'

            # add the terms
            new_terms = []
            term_names = set()

            for term in terms:
                term.name = self.FixTermLength(term.name)
                if term.name in term_names:
                    raise DuplicateTermError('You have a duplicate term: %s' %
                                             term.name)
                term_names.add(term.name)

                for source_addr in term.source_address:
                    src_token = source_addr.parent_token[:self._DEF_MAX_LENGTH]

                    if (src_token in self.def_short_to_long
                            and self.def_short_to_long[src_token] !=
                            source_addr.parent_token):
                        raise DuplicateShortenedTableName(
                            'There is a shortened name conflict between names %s and %s '
                            '(different named objects would conflict when shortened to %s)'
                            % (self.def_short_to_long[src_token],
                               source_addr.parent_token, src_token))
                    else:
                        self.def_short_to_long[
                            src_token] = source_addr.parent_token

                    if src_token not in self.address_book:
                        self.address_book[src_token] = set([source_addr])
                    else:
                        self.address_book[src_token].add(source_addr)

                for dest_addr in term.destination_address:
                    dst_token = dest_addr.parent_token[:self._DEF_MAX_LENGTH]

                    if (dst_token in self.def_short_to_long
                            and self.def_short_to_long[dst_token] !=
                            dest_addr.parent_token):
                        raise DuplicateShortenedTableName(
                            'There is a shortened name conflict between names %s and %s '
                            '(different named objects would conflict when shortened to %s)'
                            % (self.def_short_to_long[dst_token],
                               dest_addr.parent_token, dst_token))
                    else:
                        self.def_short_to_long[
                            dst_token] = dest_addr.parent_token

                    if dst_token not in self.address_book:
                        self.address_book[dst_token] = set([dest_addr])
                    else:
                        self.address_book[dst_token].add(dest_addr)

                if not term:
                    continue

                if term.expiration:
                    if term.expiration <= exp_info_date:
                        logging.info(
                            'INFO: Term %s in policy %s expires '
                            'in less than two weeks.', term.name, filter_name)
                    if term.expiration <= current_date:
                        logging.warn(
                            'WARNING: Term %s in policy %s is expired and '
                            'will not be rendered.', term.name, filter_name)
                        continue

                new_terms.append(
                    self._TERM(term, filter_name, all_protocols_stateful,
                               filter_type, direction))

            self.pf_policies.append(
                (header, filter_name, filter_type, new_terms))
Beispiel #5
0
    def __str__(self):
        """Render config output from this term object."""

        # Verify platform specific terms. Skip whole term if platform does not
        # match.
        if self.term.platform:
            if self._PLATFORM not in self.term.platform:
                return ''
        if self.term.platform_exclude:
            if self._PLATFORM in self.term.platform_exclude:
                return ''

        ret_str = []
        self._SetDefaultAction()

        # Create a new term
        ret_str.append('\n# term %s' % self.term.name)

        comments = aclgenerator.WrapWords(self.term.comment, 80)
        # append comments to output
        if comments and comments[0]:
            for line in comments:
                ret_str.append('# %s' % str(line))

        if str(self.term.action[0]) not in self._ACTION_TABLE:
            raise aclgenerator.UnsupportedFilterError(
                '%s %s %s %s' % ('\n', self.term.name, self.term.action[0],
                                 'action not currently supported.'))

        if self.direction and str(self.direction) not in self._DIRECTION_TABLE:
            raise aclgenerator.UnsupportedFilterError(
                '%s %s %s %s' % ('\n', self.term.name, self.term.direction,
                                 'direction not currently supported.'))
        # protocol
        if self.term.protocol:
            protocol = self.term.protocol
        else:
            protocol = []

        # source address
        term_saddrs = self._CheckAddressAf(self.term.source_address)
        if not term_saddrs:
            logging.debug(
                self.NO_AF_LOG_ADDR.substitute(term=self.term.name,
                                               direction='source',
                                               af=self.af))
            return ''
        term_saddr = self._GenerateAddrStatement(
            term_saddrs, self.term.source_address_exclude)

        # destination address
        term_daddrs = self._CheckAddressAf(self.term.destination_address)
        if not term_daddrs:
            logging.debug(
                self.NO_AF_LOG_ADDR.substitute(term=self.term.name,
                                               direction='destination',
                                               af=self.af))
            return ''
        term_daddr = self._GenerateAddrStatement(
            term_daddrs, self.term.destination_address_exclude)

        # ports
        source_port = []
        destination_port = []
        if self.term.source_port:
            source_port = self._GeneratePortStatement(self.term.source_port)
        if self.term.destination_port:
            destination_port = self._GeneratePortStatement(
                self.term.destination_port)

        # icmp-type
        icmp_types = ['']
        if self.term.icmp_type:
            if self.af != 'mixed':
                af = self.af
            elif protocol == ['icmp']:
                af = 'inet'
            elif protocol == ['icmpv6']:
                af = 'inet6'
            else:
                raise aclgenerator.UnsupportedFilterError(
                    '%s %s %s' %
                    ('\n', self.term.name,
                     'icmp protocol is not defined or not supported.'))
            icmp_types = self.NormalizeIcmpTypes(self.term.icmp_type, protocol,
                                                 af)

        # options
        tcp_flags_set = []
        tcp_flags_check = []
        for next_opt in [str(x) for x in self.term.option]:
            for next_flag in self._TCP_FLAGS_TABLE:
                if next_opt.find(next_flag) == 0:
                    if protocol != ['tcp']:
                        raise aclgenerator.UnsupportedFilterError(
                            '%s %s %s' %
                            ('\n', self.term.name,
                             'tcp flags may only be specified with tcp protocol.'
                             ))
                    tcp_flags_set.append(self._TCP_FLAGS_TABLE.get(next_flag))
                    tcp_flags_check.append(
                        self._TCP_FLAGS_TABLE.get(next_flag))

        # If tcp-established is set, override any of the flags above with the
        # S/SA flags.  Issue an error if flags are specified with 'established'.
        for opt in [str(x) for x in self.term.option]:
            if opt == 'established' or opt == 'tcp-established':
                if tcp_flags_set or tcp_flags_check:
                    raise aclgenerator.UnsupportedFilterError(
                        '%s %s %s' %
                        ('\n', self.term.name,
                         'tcp flags may not be specified with tcp-established.'
                         ))
                # We need to set 'flags A/A' for established regardless of whether or
                # not we're stateful:
                # - if we stateful, the default is 'flags S/SA' which prevent writing
                # rules for reply packets.
                # - if we're stateless, this is the only way to do it.
                if not protocol or 'tcp' in protocol:
                    tcp_flags_set.append(self._TCP_FLAGS_TABLE.get('ack'))
                    tcp_flags_check.append(self._TCP_FLAGS_TABLE.get('ack'))

        # The default behavior of pf is 'keep state flags S/SA'.  If we're not
        # stateless, and if flags have not been specified explicitly via options,
        # append that here.  Note that pf allows appending flags for udp and icmp;
        # they are just ignored, as long as TCP is in the proto.  This lets you
        # doing things like 'proto { tcp udp icmp } flags S/SA' and have the flags
        # only applied to the tcp bits that match.  However, the policy description
        # language prohibits setting flags on non-TCP, since it doesn't make sense
        # on all platforms.
        if ((not protocol or protocol == ['tcp']) and self.stateful
                and not tcp_flags_set and not tcp_flags_check):
            tcp_flags_set.append(self._TCP_FLAGS_TABLE.get('syn'))
            tcp_flags_check.append(self._TCP_FLAGS_TABLE.get('syn'))
            tcp_flags_check.append(self._TCP_FLAGS_TABLE.get('ack'))

        ret_str.extend(
            self._FormatPart(
                self.term.action[0],
                self.direction,
                self.term.logging,
                self.af,
                protocol,
                term_saddr,
                source_port,
                term_daddr,
                destination_port,
                tcp_flags_set,
                tcp_flags_check,
                icmp_types,
                self.options,
                self.stateful,
            ))

        return '\n'.join(str(v) for v in ret_str if v is not '')
Beispiel #6
0
    def __str__(self):
        # Verify platform specific terms. Skip whole term if platform does not
        # match.
        if self.term.platform:
            if self._PLATFORM not in self.term.platform:
                return ''
        if self.term.platform_exclude:
            if self._PLATFORM in self.term.platform_exclude:
                return ''

        ret_str = []

        # Don't render icmpv6 protocol terms under inet, or icmp under inet6
        if ((self.af == 'inet6' and 'icmp' in self.term.protocol)
                or (self.af == 'inet' and 'icmpv6' in self.term.protocol)):
            logging.debug(
                self.NO_AF_LOG_PROTO.substitute(term=self.term.name,
                                                proto=self.term.protocol,
                                                af=self.af))
            return ''

        # append comments to output
        ret_str.append(
            self._COMMENT_FORMAT.substitute(filter=self.filter,
                                            term=self.term_name,
                                            comment=self.term.comment))

        # if terms does not specify action, use filter default action
        if not self.term.action:
            self.term.action[0].value = self.default_action

        if self.term.action[0] == 'next':
            return ''

        if len(self.term.action) > 1:
            raise aclgenerator.UnsupportedFilterError(
                '\n%s %s %s %s' %
                ('Multiple actions unsupported by', self._PLATFORM,
                 '\nError in term:', self.term.name))

        # protocol
        if self.term.protocol:
            protocols = self.term.protocol
        else:
            protocols = ['any']

        # addresses
        src_addr = self.term.source_address
        if not src_addr:
            src_addr = [self._all_ips]

        dst_addr = self.term.destination_address
        if not dst_addr:
            dst_addr = [self._all_ips]

        if (self.term.source_address_exclude
                or self.term.destination_address_exclude):
            raise aclgenerator.UnsupportedFilterError(
                '\n%s %s %s %s' %
                ('address exclusions unsupported by', self._PLATFORM,
                 '\nError in term:', self.term.name))

        # ports = Map the ports in a straight list since multiports aren't supported
        (src_ports, dst_ports) = self._HandlePorts(self.term.source_port,
                                                   self.term.destination_port)

        # The windows ipsec driver requires either 'tcp' or 'udp' to be specified
        # if a srcport or dstport is specified.  Fail if src or dst ports are
        # specified and of the protocols are not exactly one or both of 'tcp'
        # or 'udp'.
        if ((not set(protocols).issubset(set(['tcp', 'udp'])))
                and (len(src_ports) > 1 or len(dst_ports) > 1)):
            raise aclgenerator.UnsupportedFilterError('%s %s %s' % (
                '\n', self.term.name,
                'src or dst ports may only be specified with "tcp" and/or "udp".'
            ))

        # icmp-types
        (icmp_types,
         protocols) = self._HandleIcmpTypes(self.term.icmp_type, protocols)

        ret_str = []
        self._HandlePreRule(ret_str)
        self._CartesianProduct(src_addr, dst_addr, protocols, icmp_types,
                               src_ports, dst_ports, ret_str)
        self._HandlePreRule(ret_str)

        return '\n'.join(str(v) for v in ret_str if v)
Beispiel #7
0
    def _TranslatePolicy(self, pol, exp_info):
        """Translate a policy from objects into strings."""
        self.windows_policies = []
        current_date = datetime.datetime.utcnow().date()
        exp_info_date = current_date + datetime.timedelta(weeks=exp_info)

        default_action = None
        good_default_actions = ['permit', 'block']
        good_options = []

        for header, terms in pol.filters:
            filter_type = None
            if self._PLATFORM not in header.platforms:
                continue

            filter_options = header.FilterOptions(self._PLATFORM)[1:]
            filter_name = header.FilterName(self._PLATFORM)

            # ensure all options after the filter name are expected
            for opt in filter_options:
                if opt not in good_default_actions + self._GOOD_AFS + good_options:
                    raise aclgenerator.UnsupportedTargetOptionError(
                        '%s %s %s %s' %
                        ('\nUnsupported option found in', self._PLATFORM,
                         'target definition:', opt))

            # Check for matching af
            for address_family in self._GOOD_AFS:
                if address_family in filter_options:
                    # should not specify more than one AF in options
                    if filter_type is not None:
                        raise aclgenerator.UnsupportedFilterError(
                            '%s %s %s %s' %
                            ('\nMay only specify one of', self._GOOD_AFS,
                             'in filter options:', filter_options))
                    filter_type = address_family
            if filter_type is None:
                filter_type = 'inet'

            # does this policy override the default filter actions?
            for next_target in header.target:
                if next_target.platform == self._PLATFORM:
                    if len(next_target.options) > 1:
                        for arg in next_target.options:
                            if arg in good_default_actions:
                                default_action = arg
            if default_action and default_action not in good_default_actions:
                raise aclgenerator.UnsupportedTargetOptionError(
                    '%s %s %s %s %s' %
                    ('\nOnly', ', '.join(good_default_actions),
                     'default filter action allowed;', default_action,
                     'used.'))

            # add the terms
            new_terms = []
            term_names = set()
            for term in terms:
                if term.name in term_names:
                    raise aclgenerator.DuplicateTermError(
                        'You have a duplicate term: %s' % term.name)
                term_names.add(term.name)

                if term.expiration:
                    if term.expiration <= exp_info_date:
                        logging.info(
                            'INFO: Term %s in policy %s expires '
                            'in less than two weeks.', term.name, filter_name)
                    if term.expiration <= current_date:
                        logging.warning(
                            'WARNING: Term %s in policy %s is expired and '
                            'will not be rendered.', term.name, filter_name)
                        continue
                if 'established' in term.option or 'tcp-established' in term.option:
                    continue
                new_terms.append(
                    self._TERM(term, filter_name, default_action, filter_type))

            self.windows_policies.append(
                (header, filter_name, filter_type, default_action, new_terms))