Exemple #1
0
    def __str__(self):
        """Render config output from this term object."""
        ret_str = []

        # Create a new term
        ret_str.append('\n# term %s' % self.term.name)
        # append comments to output
        for line in self.term.comment:
            if not line:
                continue
            ret_str.append('# %s' % str(line))

        # 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.'))

        # protocol
        if self.term.protocol:
            protocol = self.term.protocol
        else:
            protocol = []
        if self.term.protocol_except:
            raise aclgenerator.UnsupportedFilterError(
                '%s %s %s' %
                ('\n', self.term.name,
                 'protocol_except logic not currently supported.'))

        # source address
        term_saddrs = self._CheckAddressAf(self.term.source_address)
        if not term_saddrs:
            logging.warn(
                self.NO_AF_LOG_FORMAT.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.warn(
                self.NO_AF_LOG_FORMAT.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 == ['icmp6']:
                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
        opts = [str(x) for x in self.term.option]
        tcp_flags = []
        for next_opt in opts:
            # Iterate through flags table, and create list of tcp-flags to append
            for next_flag in self._TCP_FLAGS_TABLE:
                if next_opt.find(next_flag) == 0:
                    tcp_flags.append(self._TCP_FLAGS_TABLE.get(next_flag))

        ret_str.extend(
            self._FormatPart(
                self._ACTION_TABLE.get(str(self.term.action[0])),
                self.term.logging,
                self.af,
                protocol,
                term_saddr,
                source_port,
                term_daddr,
                destination_port,
                tcp_flags,
                icmp_types,
                self.options,
            ))

        return '\n'.join(str(v) for v in ret_str if v is not '')
Exemple #2
0
  def _TranslatePolicy(self, pol, exp_info):
    self.pf_policies = []
    self.address_book = {}
    current_date = datetime.date.today()
    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 aclgenerator.DuplicateTermError(
              'You have a duplicate term: %s' % term.name)
        for source_addr in term.source_address:
          if source_addr.parent_token not in self.address_book:
            self.address_book[source_addr.parent_token] = set([source_addr])
          else:
            self.address_book[source_addr.parent_token].add(source_addr)
        for dest_addr in term.destination_address:
          if dest_addr.parent_token not in self.address_book:
            self.address_book[dest_addr.parent_token] = set([dest_addr])
          else:
            self.address_book[dest_addr.parent_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))
      shortened_deduped_list = {}

      for key in self.address_book:
        if len(key) > 31:
          name = key[:31]
        else:
          name = key
        if name in shortened_deduped_list.keys():
          raise DuplicateShortenedTableName(
              'The shortened name %s has a collision.', name)
        else:
          shortened_deduped_list[name] = self.address_book[key]
      self.address_book = shortened_deduped_list
      self.pf_policies.append((header, filter_name, filter_type, new_terms))
Exemple #3
0
    def _TranslatePolicy(self, pol, exp_info):
        self.pf_policies = []
        current_date = datetime.date.today()
        exp_info_date = current_date + datetime.timedelta(weeks=exp_info)

        good_afs = ['inet', 'inet6', 'mixed']
        good_options = []
        filter_type = None

        for header, terms in pol.filters:
            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 UnsupportedTargetOption(
                        '%s %s %s %s' %
                        ('\nUnsupported option found in', self._PLATFORM,
                         'target definition:', opt))

            # 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
            new_terms = []
            term_names = set()
            for term in terms:
                term.name = self.FixTermLength(term.name)
                if term.name in term_names:
                    raise aclgenerator.DuplicateTermError(
                        'You have a duplicate term: %s' % term.name)
                term_names.add(term.name)

                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, filter_type))

            self.pf_policies.append(
                (header, filter_name, filter_type, new_terms))
Exemple #4
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 = []
    if self.term.protocol_except:
      raise aclgenerator.UnsupportedFilterError('%s %s %s' % (
          '\n', self.term.name,
          'protocol_except logic 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 ''
    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 '')