Ejemplo n.º 1
0
 def testBlockableKey_NoParent(self):
     change = bit9.RuleChangeSet(
         rule_keys=[],
         change_type=constants.RULE_POLICY.WHITELIST,
         parent=None)
     with self.assertRaises(ValueError):
         change.put()
Ejemplo n.º 2
0
 def testBlockableKey_NotABlockableKey(self):
     host = test_utils.CreateBit9Host()
     change = bit9.RuleChangeSet(
         rule_keys=[],
         change_type=constants.RULE_POLICY.WHITELIST,
         parent=host.key)
     with self.assertRaises(ValueError):
         change.put()
Ejemplo n.º 3
0
    def testBlockableKey(self):
        change = bit9.RuleChangeSet(
            rule_keys=[],
            change_type=constants.RULE_POLICY.WHITELIST,
            parent=self.bit9_binary.key)
        change.put()

        self.assertEqual(self.bit9_binary.key, change.blockable_key)
Ejemplo n.º 4
0
def CreateRuleChangeSet(blockable_key, **kwargs):
    defaults = {
        'change_type': constants.RULE_POLICY.BLACKLIST,
        'rule_keys': []
    }
    defaults.update(kwargs.copy())
    change = bit9.RuleChangeSet(parent=blockable_key, **defaults)
    change.put()
    return change
Ejemplo n.º 5
0
def _CopyLocalRules(user_key, dest_host_id):
    """Copy over a user's local rules to a newly-associated host.

  NOTE: Because of the implementation of local whitelisting on Bit9, many of
  these new copied local rules will likely be initially unfulfilled, that is,
  held in Upvote and not saved to Bit9.

  Args:
    user_key: str, The user for whom the rules will be copied.
    dest_host_id: str, The ID of the host for which the new rules will be
        created.
  """
    logging.info('Copying rules for %s to host %s', user_key.id(),
                 dest_host_id)

    username = user_map.EmailToUsername(user_key.id())
    host_query = bit9.Bit9Host.query(bit9.Bit9Host.users == username)
    src_host = yield host_query.get_async()
    if src_host is None:
        raise ndb.Return()
    assert src_host.key.id() != dest_host_id, (
        'User already associated with target host')

    # Get all local rules from that host.
    rules_query = bit9.Bit9Rule.query(
        bit9.Bit9Rule.host_id == src_host.key.id(),
        bit9.Bit9Rule.in_effect == True)  # pylint: disable=g-explicit-bool-comparison

    # Get a rough idea of how many rules we're in for. Since this is a
    # non-critical query, we limit the max number to a fairly low bound.
    rule_count = yield rules_query.count_async(limit=250)
    logging.info('Retrieved %s%s rules to copy',
                 '>' if rule_count == 250 else '', rule_count)

    # Copy the local rules to the new host.
    new_rules = []
    for batch in query_utils.Paginate(rules_query):
        for rule in batch:
            new_rule = model_utils.CopyEntity(rule,
                                              new_parent=rule.key.parent(),
                                              host_id=dest_host_id,
                                              user_key=user_key)
            new_rules.append(new_rule)
    logging.info('Copying %s rules to new host', len(new_rules))
    yield ndb.put_multi_async(new_rules)

    # Create the change sets necessary to submit the new rules to Bit9.
    changes = []
    for new_rule in new_rules:
        change = bit9.RuleChangeSet(rule_keys=[new_rule.key],
                                    change_type=new_rule.policy,
                                    parent=new_rule.key.parent())
        changes.append(change)
    logging.info('Creating %s RuleChangeSet', len(changes))
    yield ndb.put_multi_async(changes)
Ejemplo n.º 6
0
def _CopyLocalRules(user_key, dest_host_id):
    """Copy over a user's local rules to a newly-associated host.

  NOTE: Because of the implementation of local whitelisting on Bit9, many of
  these new copied local rules will likely be initially unfulfilled, that is,
  held in Upvote and not saved to Bit9.

  Args:
    user_key: str, The user for whom the rules will be copied.
    dest_host_id: str, The ID of the host for which the new rules will be
        created.
  """
    logging.info('Copying rules for user %s to host %s', user_key.id(),
                 dest_host_id)

    # Query for a host belonging to the user.
    username = user_utils.EmailToUsername(user_key.id())
    query = host_models.Bit9Host.query(host_models.Bit9Host.users == username)
    src_host = yield query.get_async()
    if src_host is None:
        logging.warning('User %s has no hosts to copy from', username)
        raise ndb.Return()
    src_host_id = src_host.key.id()

    # Query for all the Bit9Rules in effect for the given user on the chosen host.
    query = rule_models.Bit9Rule.query(
        rule_models.Bit9Rule.host_id == src_host_id,
        rule_models.Bit9Rule.user_key == user_key,
        rule_models.Bit9Rule.in_effect == True)  # pylint: disable=g-explicit-bool-comparison, singleton-comparison
    src_rules = yield query.fetch_async()
    logging.info('Found a total of %d rule(s) for user %s', len(src_rules),
                 user_key.id())

    # Copy the local rules to the new host.
    logging.info('Copying %d rule(s) to host %s', len(src_rules), dest_host_id)
    new_rules = []
    for src_rule in src_rules:
        new_rule = datastore_utils.CopyEntity(src_rule,
                                              new_parent=src_rule.key.parent(),
                                              host_id=dest_host_id,
                                              user_key=user_key)
        new_rules.append(new_rule)
        new_rule.InsertBigQueryRow()
    yield ndb.put_multi_async(new_rules)

    # Create the change sets necessary to submit the new rules to Bit9.
    changes = []
    for new_rule in new_rules:
        change = bit9.RuleChangeSet(rule_keys=[new_rule.key],
                                    change_type=new_rule.policy,
                                    parent=new_rule.key.parent())
        changes.append(change)
    logging.info('Creating %d RuleChangeSet(s)', len(changes))
    yield ndb.put_multi_async(changes)
Ejemplo n.º 7
0
  def _SetInstallerPolicy(self, blockable_id, new_policy):
    blockable = base_models.Blockable.get_by_id(blockable_id)

    # pylint: disable=g-explicit-bool-comparison, singleton-comparison
    installer_rule_query = rule_models.Bit9Rule.query(
        rule_models.Bit9Rule.in_effect == True,
        rule_models.Bit9Rule.policy.IN(constants.RULE_POLICY.SET_INSTALLER),
        ancestor=blockable.key)
    # pylint: enable=g-explicit-bool-comparison, singleton-comparison
    existing_rule = installer_rule_query.get()
    if existing_rule:
      if existing_rule.policy == new_policy:
        return blockable.is_installer
      else:
        existing_rule.in_effect = False
        existing_rule.put()

    # Create the Bit9Rule associated with the installer state and a change set
    # to commit it.
    new_rule = rule_models.Bit9Rule(
        rule_type=blockable.rule_type,
        in_effect=True,
        policy=new_policy,
        parent=blockable.key)
    new_rule.put()
    change = bit9_models.RuleChangeSet(
        rule_keys=[new_rule.key],
        change_type=new_rule.policy,
        parent=blockable.key)
    change.put()

    message = 'User %s changed installer state to %s' % (
        self.user.key.id(), new_policy)
    tables.BINARY.InsertRow(
        sha256=blockable.key.id(),
        timestamp=datetime.datetime.utcnow(),
        action=constants.BLOCK_ACTION.COMMENT,
        state=blockable.state,
        score=blockable.score,
        platform=constants.PLATFORM.WINDOWS,
        client=constants.CLIENT.BIT9,
        first_seen_file_name=blockable.first_seen_name,
        cert_fingerprint=blockable.cert_id,
        is_compiler=blockable.is_compiler,
        comment=message)

    change_set.DeferCommitBlockableChangeSet(blockable.key)

    # Update the blockable's is_installer property.
    blockable.is_installer = new_policy == constants.RULE_POLICY.FORCE_INSTALLER
    blockable.put()

    return blockable.is_installer
Ejemplo n.º 8
0
def _CheckAndResolveAnomalousBlock(blockable_key, host_id):
    """Checks whether an unfulfilled rule already existed for this blockable.

  If there are unfulfilled rules, triggers an attempt to commit them back to the
  database.

  Args:
    blockable_key: The key of the blockable that was blocked.
    host_id: The host on which the block occurred.

  Returns:
    Whether the block was anomalous (i.e. whether an unfulfilled rule existed
    for the blockable-host pair).
  """
    # Check and handle anomalous block events by detecting unfulfilled rules and,
    # if present, attempting to commit them.
    # pylint: disable=g-explicit-bool-comparison
    unfulfilled_rule_query = bit9.Bit9Rule.query(
        bit9.Bit9Rule.is_committed == True,
        bit9.Bit9Rule.is_fulfilled == False,
        bit9.Bit9Rule.host_id == host_id,
        ancestor=blockable_key).order(bit9.Bit9Rule.updated_dt)
    # pylint: enable=g-explicit-bool-comparison
    unfulfilled_rules = unfulfilled_rule_query.fetch()

    # Installer rules shouldn't be local (e.g. have host_id's) so they shouldn't
    # have been returned by the query. Still, the sanity check couldn't hurt.
    assert all(rule.policy in constants.RULE_POLICY.SET_EXECUTION
               for rule in unfulfilled_rules)
    if unfulfilled_rules:
        logging.info('Processing %s unfulfilled rules for %s',
                     len(unfulfilled_rules), blockable_key.id())

        # Mark all outstanding unfulfilled rules _except_ the most recent one as
        # fulfilled as we're going to ignore them.
        for rule in unfulfilled_rules[:-1]:
            rule.is_fulfilled = True

        # Mark the most recent unfulfilled rule as uncommitted as we're going to
        # commit it.
        unfulfilled_rules[-1].is_committed = False

        # Create and trigger a change set to commit the most recent rule.
        change = bit9.RuleChangeSet(rule_keys=[unfulfilled_rules[-1].key],
                                    change_type=unfulfilled_rules[-1].policy,
                                    parent=blockable_key)

        ndb.put_multi(unfulfilled_rules + [change])

        change_set.DeferCommitBlockableChangeSet(blockable_key)

    return bool(unfulfilled_rules)
Ejemplo n.º 9
0
  def _CreateRuleChangeSet(self, rules_future, new_policy):
    """Creates a RuleChangeSet and trigger an attempted commit."""
    rules = rules_future.get_result()
    # If there are no rules to be created (rare but possible), we can just drop
    # the change set entirely.
    if not rules:
      return

    keys = [rule.key for rule in rules]
    change = bit9.RuleChangeSet(
        rule_keys=keys, change_type=new_policy, parent=self.blockable.key)
    change.put()

    # Attempt to commit the change set in a deferred task.
    # NOTE: If we're in a transaction, we should only send out the
    # request to Bit9 once the RuleChangeSet has been successfully created.
    # If we're not in a transaction, this executes immediately.
    ndb.get_context().call_on_commit(self._TriggerCommit)
Ejemplo n.º 10
0
  def _SetInstallerPolicy(self, blockable_id, new_policy):
    blockable = base_db.Blockable.get_by_id(blockable_id)

    # pylint: disable=g-explicit-bool-comparison
    installer_rule_query = bit9_db.Bit9Rule.query(
        bit9_db.Bit9Rule.in_effect == True,
        bit9_db.Bit9Rule.policy.IN(constants.RULE_POLICY.SET_INSTALLER),
        ancestor=blockable.key)
    # pylint: enable=g-explicit-bool-comparison
    existing_rule = installer_rule_query.get()
    if existing_rule:
      if existing_rule.policy == new_policy:
        return blockable.is_installer
      else:
        existing_rule.in_effect = False
        existing_rule.put()

    # Create the Bit9Rule associated with the installer state and a change set
    # to commit it.
    new_rule = bit9_db.Bit9Rule(
        rule_type=blockable.rule_type,
        in_effect=True,
        policy=new_policy,
        parent=blockable.key)
    new_rule.put()
    change = bit9_db.RuleChangeSet(
        rule_keys=[new_rule.key],
        change_type=new_rule.policy,
        parent=blockable.key)
    change.put()
    base_db.AuditLog.Create(
        blockable,
        'User %s changing installer state to %s' % (
            self.user.key.id(), new_policy))

    change_set.DeferCommitBlockableChangeSet(blockable.key)

    # Update the blockable's is_installer property.
    blockable.is_installer = new_policy == constants.RULE_POLICY.FORCE_INSTALLER
    blockable.put()

    return blockable.is_installer