def toBDD(self, index): """Construct the ROBDD Parameters ---------- index : int. Used for variable index in ROBDD. Return ------ Return the computed ROBDD """ if self.operator == 'LT': if isinstance(self.v1, Protocol): return Protocol.range2bdd(0, self.v1.get_value(), index) elif isinstance(self.v1, Ip): return Ip.range2bdd(0, self.v1.ip | ~self.v1.mask & 0xFFFFFFFF, index) elif isinstance(self.v1, Port): return Port.range2bdd(0, self.v1.get_value(), index) else: return self.v1.toBDD(index) elif self.operator == 'GT': if isinstance(self.v1, Protocol): return Protocol.range2bdd(self.v1.get_value(), 2**8 - 1, index) elif isinstance(self.v1, Ip): return Ip.range2bdd(self.v1.ip & self.v1.mask, 2**32 - 1, index) elif isinstance(self.v1, Port): return Port.range2bdd(self.v1.get_value(), 2**16 - 1, index) else: return self.v1.toBDD(index) elif self.operator == 'EQ': return self.v1.toBDD(index) elif self.operator == 'NEQ': return negate_bdd(self.v1.toBDD(index)) elif self.operator == 'RANGE': if isinstance(self.v1, Protocol): return Protocol.range2bdd(self.v1.get_value(), self.v2.get_value(), index) elif isinstance(self.v1, Ip): return Ip.range2bdd(self.v1.ip & self.v1.mask, self.v2.ip | ~self.v2.mask & 0xFFFFFFFF, index) elif isinstance(self.v1, Port): return Port.range2bdd(self.v1.get_value(), self.v2.get_value(), index) else: return self.v1.toBDD(index) else: return self.v1.toBDD(index)
def _detect_anomaly(acl, stack, parsed_rule, visited, remain, accept, deny, result_queue, processed_id_rules, deep_search): """Recursive algorithm for ACL graph traversal. This recursive algorithm construct the list of all possible list of rule for the ACL chained model. Then it launch the _classify_anomaly function on each rule and try to detect anomaly if any. Parameters ---------- acl : ACL. The acl to inspect. stack : list of Rule list. A stack of rule list. parsed_rule : Rule list. A list of parse rule. visisted : ACL list. List of visited ACL. remain : ROBDD. Remain ROBDD. accept : ROBDD. Accept ROBDD. deny : ROBDD. Deny ROBDD. result_queue : list. List of error. processed_id_rules : list. List of processed rules. deep_search : Bool. If true find blamed rules. """ if acl not in visited: visited.append(acl) stack.append(list(acl.rules)) while stack: rule_list = stack[-1] while rule_list: rule = rule_list.pop(0) processed_id_rules.put(rule.identifier) _classify_anomaly(rule, parsed_rule, remain, accept, deny, result_queue, deep_search) parsed_rule.append([rule, rule.action.chain, rule.toBDD()]) if rule.action.is_chained() or rule.action.is_return(): # a = accept & True -> copy of accept a = synthesize(accept, Bdd.AND, Robdd.true()) # d = D ∪ (R ∩ ¬P) d = synthesize(deny, Bdd.OR, synthesize(remain, Bdd.AND, negate_bdd(rule.toBDD()))) # r = I ∩ ¬(a ∪ d) r = synthesize(Robdd.true(), Bdd.AND, negate_bdd(synthesize(a, Bdd.OR, d))) # copy stack of rules stack_copy = [list(i) for i in stack] visited_copy = list(visited) parsed_rule_copy = list(parsed_rule) parsed_rule_copy[-1] = [parsed_rule_copy[-1][0], False, negate_bdd(parsed_rule_copy[-1][0].toBDD())] if rule.action.is_chained(): _detect_anomaly(rule.action.chain, stack_copy, parsed_rule_copy, visited_copy, r, a, d, result_queue, processed_id_rules, deep_search) else: # remove current stack rule stack_copy.pop() visited_copy.pop() # special case RETURN in first ACL if not visited_copy: stack_copy = [[acl.rules[-1]]] visited_copy.append(acl) _detect_anomaly(visited_copy[-1], stack_copy, parsed_rule_copy, visited_copy, r, a, d, result_queue, processed_id_rules, deep_search) else: # rule is Permit or Deny if rule.action.chain: # accept = A ∪ (R ∩ P) accept = synthesize(accept, Bdd.OR, synthesize(remain, Bdd.AND, rule.toBDD())) else: # d = D ∪ (R ∩ P) deny = synthesize(deny, Bdd.OR, synthesize(remain, Bdd.AND, rule.toBDD())) # remain = I ∩ ¬(a ∪ d) remain = synthesize(Robdd.true(), Bdd.AND, negate_bdd(synthesize(accept, Bdd.OR, deny))) stack.pop() visited.pop()
def _distributed_detection(self, acl_list, remain, tree_path): """Detection method for a given acl with a given remain ROBDD. This algorithm is derived from the algorithm of Fireman. For more informations read : - Firewall Policy Advisor for Anomaly Detection and rules analysis, http://www.arc.uncc.edu/pubs/im03-cr.pdf - FIREMAN : A Toolkit for FIREwall Modeling and ANalysis, http://www.cs.ucdavis.edu/~su/publications/fireman.pdf Parameters ---------- acl : Rule list. The rule list to test remain : ROBDD. The remaining ROBDD Return ------ Return a tuple of remaining rules and the list error found in this context """ accept_robdd_list = [] error_list = deque() error_list_append = error_list.append for acl in acl_list: for rule_path in acl.get_rules_path(): accept = Robdd.false() deny = Robdd.false() if self.cancel: break for rule, action in rule_path: if self.cancel: break Gtk.Gtk_Main.Gtk_Main().update_progress_bar(1) Gtk.Gtk_Main.Gtk_Main().update_interface() error_rules = [] rule_action = rule.action.chain if isinstance( rule.action.chain, bool) else action if rule_action: # P ⊆ I if compare_bdd(rule.toBDD(), Bdd.IMPL, remain): pass # P ⊆ ¬I elif compare_bdd(rule.toBDD(), Bdd.IMPL, negate_bdd(remain)): if self.deep_search: # ∀ ACLx < ACLj, ∀ x ∈ ACLx, ∃ <Px, deny> such that Px ∩ Pj != ∅ error_rules = self.search_rules( rule, Bdd.AND, False, tree_path) error_list_append( AnomalyError.error_message( ErrorType.DIST_SHADOW, ErrorType.ERROR, rule, error_rules)) # P ∩ I != ∅ else: if self.deep_search: # ∀ ACLx < ACLj, ∀ x ∈ ACLx, ∃ Px such that Px ∩ Pj != ∅ error_rules = self.search_rules( rule, Bdd.AND, None, tree_path) error_list_append( AnomalyError.error_message( ErrorType.DIST_CORRELATE, ErrorType.WARNING, rule, error_rules)) else: # P ⊆ I if compare_bdd(rule.toBDD(), Bdd.IMPL, remain): if self.deep_search: # ∀ ACLx < ACLj, ∀ x ∈ ACLx, ∃ <Px, accept> such that Px ∩ Pj != ∅ error_rules = self.search_rules( rule, Bdd.AND, True, tree_path) error_list_append( AnomalyError.error_message( ErrorType.DIST_RAISED, ErrorType.WARNING, rule, error_rules)) # P ⊆ ¬I elif compare_bdd(rule.toBDD(), Bdd.IMPL, negate_bdd(remain)): if self.deep_search: # ∀ ACLx < ACLj, ∀ x ∈ ACLx, ∃ <Px, deny> such that Px ∩ Pj != ∅ error_rules = self.search_rules( rule, Bdd.AND, False, tree_path) error_list_append( AnomalyError.error_message( ErrorType.DIST_REDUNDANT, ErrorType.WARNING, rule, error_rules)) # P ∩ I != ∅ else: if self.deep_search: # ∀ ACLx < ACLj, ∀ x ∈ ACLx such that Px ∩ Pj != ∅ error_rules = self.search_rules( rule, Bdd.AND, None, tree_path) error_list_append( AnomalyError.error_message( ErrorType.DIST_CORRELATE, ErrorType.WARNING, rule, error_rules)) # update value if rule.action.is_chained() or rule.action.is_return(): if action: # D = D ∪ ¬(A ∪ P) = D ∪ (¬A ∩ ¬P) deny = synthesize( deny, Bdd.OR, negate_bdd( synthesize(accept, Bdd.OR, rule.toBDD()))) else: # D = D ∪ (¬A ∩ P) deny = synthesize( deny, Bdd.OR, synthesize(negate_bdd(accept), Bdd.AND, rule.toBDD())) else: if rule.action.chain: # A = A ∪ (¬D ∩ P) accept = synthesize( accept, Bdd.OR, synthesize(negate_bdd(deny), Bdd.AND, rule.toBDD())) else: # D = D ∪ (¬A ∩ P) deny = synthesize( deny, Bdd.OR, synthesize(negate_bdd(accept), Bdd.AND, rule.toBDD())) accept_robdd_list.append(accept) res_accept = Robdd.false() for a in accept_robdd_list: res_accept = synthesize(res_accept, Bdd.OR, a) return res_accept, error_list
def _distributed_detection(self, acl_list, remain, tree_path): """Detection method for a given acl with a given remain ROBDD. This algorithm is derived from the algorithm of Fireman. For more informations read : - Firewall Policy Advisor for Anomaly Detection and rules analysis, http://www.arc.uncc.edu/pubs/im03-cr.pdf - FIREMAN : A Toolkit for FIREwall Modeling and ANalysis, http://www.cs.ucdavis.edu/~su/publications/fireman.pdf Parameters ---------- acl : Rule list. The rule list to test remain : ROBDD. The remaining ROBDD Return ------ Return a tuple of remaining rules and the list error found in this context """ accept_robdd_list = [] error_list = deque() error_list_append = error_list.append for acl in acl_list: for rule_path in acl.get_rules_path(): accept = Robdd.false() deny = Robdd.false() if self.cancel: break for rule, action in rule_path: if self.cancel: break Gtk.Gtk_Main.Gtk_Main().update_progress_bar(1) Gtk.Gtk_Main.Gtk_Main().update_interface() error_rules = [] rule_action = rule.action.chain if isinstance(rule.action.chain, bool) else action if rule_action: # P ⊆ I if compare_bdd(rule.toBDD(), Bdd.IMPL, remain): pass # P ⊆ ¬I elif compare_bdd(rule.toBDD(), Bdd.IMPL, negate_bdd(remain)): if self.deep_search: # ∀ ACLx < ACLj, ∀ x ∈ ACLx, ∃ <Px, deny> such that Px ∩ Pj != ∅ error_rules = self.search_rules(rule, Bdd.AND, False, tree_path) error_list_append( AnomalyError.error_message(ErrorType.DIST_SHADOW, ErrorType.ERROR, rule, error_rules)) # P ∩ I != ∅ else: if self.deep_search: # ∀ ACLx < ACLj, ∀ x ∈ ACLx, ∃ Px such that Px ∩ Pj != ∅ error_rules = self.search_rules(rule, Bdd.AND, None, tree_path) error_list_append( AnomalyError.error_message(ErrorType.DIST_CORRELATE, ErrorType.WARNING, rule, error_rules)) else: # P ⊆ I if compare_bdd(rule.toBDD(), Bdd.IMPL, remain): if self.deep_search: # ∀ ACLx < ACLj, ∀ x ∈ ACLx, ∃ <Px, accept> such that Px ∩ Pj != ∅ error_rules = self.search_rules(rule, Bdd.AND, True, tree_path) error_list_append( AnomalyError.error_message(ErrorType.DIST_RAISED, ErrorType.WARNING, rule, error_rules)) # P ⊆ ¬I elif compare_bdd(rule.toBDD(), Bdd.IMPL, negate_bdd(remain)): if self.deep_search: # ∀ ACLx < ACLj, ∀ x ∈ ACLx, ∃ <Px, deny> such that Px ∩ Pj != ∅ error_rules = self.search_rules(rule, Bdd.AND, False, tree_path) error_list_append( AnomalyError.error_message(ErrorType.DIST_REDUNDANT, ErrorType.WARNING, rule, error_rules)) # P ∩ I != ∅ else: if self.deep_search: # ∀ ACLx < ACLj, ∀ x ∈ ACLx such that Px ∩ Pj != ∅ error_rules = self.search_rules(rule, Bdd.AND, None, tree_path) error_list_append( AnomalyError.error_message(ErrorType.DIST_CORRELATE, ErrorType.WARNING, rule, error_rules)) # update value if rule.action.is_chained() or rule.action.is_return(): if action: # D = D ∪ ¬(A ∪ P) = D ∪ (¬A ∩ ¬P) deny = synthesize(deny, Bdd.OR, negate_bdd(synthesize(accept, Bdd.OR, rule.toBDD()))) else: # D = D ∪ (¬A ∩ P) deny = synthesize(deny, Bdd.OR, synthesize(negate_bdd(accept), Bdd.AND, rule.toBDD())) else: if rule.action.chain: # A = A ∪ (¬D ∩ P) accept = synthesize(accept, Bdd.OR, synthesize(negate_bdd(deny), Bdd.AND, rule.toBDD())) else: # D = D ∪ (¬A ∩ P) deny = synthesize(deny, Bdd.OR, synthesize(negate_bdd(accept), Bdd.AND, rule.toBDD())) accept_robdd_list.append(accept) res_accept = Robdd.false() for a in accept_robdd_list: res_accept = synthesize(res_accept, Bdd.OR, a) return res_accept, error_list
def _detect_anomaly( acl, stack, parsed_rule, visited, remain, accept, deny, result_queue, processed_id_rules, deep_search ): """Recursive algorithm for ACL graph traversal. This recursive algorithm construct the list of all possible list of rule for the ACL chained model. Then it launch the _classify_anomaly function on each rule and try to detect anomaly if any. Parameters ---------- acl : ACL. The acl to inspect. stack : list of Rule list. A stack of rule list. parsed_rule : Rule list. A list of parse rule. visisted : ACL list. List of visited ACL. remain : ROBDD. Remain ROBDD. accept : ROBDD. Accept ROBDD. deny : ROBDD. Deny ROBDD. result_queue : list. List of error. processed_id_rules : list. List of processed rules. deep_search : Bool. If true find blamed rules. """ if acl not in visited: visited.append(acl) stack.append(list(acl.rules)) while stack: rule_list = stack[-1] while rule_list: rule = rule_list.pop(0) processed_id_rules.put(rule.identifier) _classify_anomaly(rule, parsed_rule, remain, accept, deny, result_queue, deep_search) parsed_rule.append([rule, rule.action.chain, rule.toBDD()]) if rule.action.is_chained() or rule.action.is_return(): # a = accept & True -> copy of accept a = synthesize(accept, Bdd.AND, Robdd.true()) # d = D ∪ (R ∩ ¬P) d = synthesize(deny, Bdd.OR, synthesize(remain, Bdd.AND, negate_bdd(rule.toBDD()))) # r = I ∩ ¬(a ∪ d) r = synthesize(Robdd.true(), Bdd.AND, negate_bdd(synthesize(a, Bdd.OR, d))) # copy stack of rules stack_copy = [list(i) for i in stack] visited_copy = list(visited) parsed_rule_copy = list(parsed_rule) parsed_rule_copy[-1] = [parsed_rule_copy[-1][0], False, negate_bdd(parsed_rule_copy[-1][0].toBDD())] if rule.action.is_chained(): _detect_anomaly( rule.action.chain, stack_copy, parsed_rule_copy, visited_copy, r, a, d, result_queue, processed_id_rules, deep_search, ) else: # remove current stack rule stack_copy.pop() visited_copy.pop() # special case RETURN in first ACL if not visited_copy: stack_copy = [[acl.rules[-1]]] visited_copy.append(acl) _detect_anomaly( visited_copy[-1], stack_copy, parsed_rule_copy, visited_copy, r, a, d, result_queue, processed_id_rules, deep_search, ) else: # rule is Permit or Deny if rule.action.chain: # accept = A ∪ (R ∩ P) accept = synthesize(accept, Bdd.OR, synthesize(remain, Bdd.AND, rule.toBDD())) else: # d = D ∪ (R ∩ P) deny = synthesize(deny, Bdd.OR, synthesize(remain, Bdd.AND, rule.toBDD())) # remain = I ∩ ¬(a ∪ d) remain = synthesize(Robdd.true(), Bdd.AND, negate_bdd(synthesize(accept, Bdd.OR, deny))) stack.pop() visited.pop()
def _detect_anomaly(rules, result_queue, processed_id_rules, deep_search): """Detect anomaly of the given rule set. This algorithm is derived from the algorithm of Fireman. It uses ROBDD to compare each rules. The complexity of this algorithm is in O(n). For more informations read : - Firewall Policy Advisor for Anomaly Detection and rules analysis, http://www.arc.uncc.edu/pubs/im03-cr.pdf - FIREMAN : A Toolkit for FIREwall Modeling and ANalysis, http://www.cs.ucdavis.edu/~su/publications/fireman.pdf Parameters ---------- rules : Rule list. The list of rule to inspect result_queue : multiprocessing Queue. A queue to put errors """ remain = Robdd.true() accept = Robdd.false() deny = Robdd.false() error_list = deque() error_list_append = error_list.append for rule in rules: processed_id_rules.put(rule.identifier) error_rules = deque() # Pj ⊆ Rj if compare_bdd(rule.toBDD(), Bdd.IMPL, remain): pass else: # Pj ∩ Rj = ∅ if not compare_bdd(rule.toBDD(), Bdd.AND, remain): # Pj ⊆ Dj if compare_bdd(rule.toBDD(), Bdd.IMPL, (deny if rule.action else accept)): if deep_search: for r in rules: if r == rule: break # ∀ x < j, ∃ <Px, deny> such that Px ∩ Pj != ∅ if r.action != rule.action and compare_bdd( r.toBDD(), Bdd.AND, rule.toBDD()): error_rules.append(r) error_list_append( AnomalyError.error_message(ErrorType.INT_MASK_SHADOW, ErrorType.ERROR, rule, error_rules)) # Pj ∩ Dj = ∅ elif not compare_bdd(rule.toBDD(), Bdd.AND, (deny if rule.action else accept)): if deep_search: for r in rules: if r == rule: break # ∀ x < j, ∃ <Px, accept> such that Px ∩ Pj != ∅ if r.action == rule.action and compare_bdd( r.toBDD(), Bdd.AND, rule.toBDD()): error_rules.append(r) error_list_append( AnomalyError.error_message( ErrorType.INT_MASK_REDUNDANT, ErrorType.ERROR, rule, error_rules)) else: if deep_search: for r in rules: if r == rule: break # ∀ x < j, ∃ Px such that Px ∩ Pj != ∅ if compare_bdd(r.toBDD(), Bdd.AND, rule.toBDD()): error_rules.append(r) error_list_append( AnomalyError.error_message( ErrorType.INT_MASK_REDUNDANT_CORRELATION, ErrorType.ERROR, rule, error_rules)) else: error_redudant = deque() error_generalization = deque() if deep_search: # if deep search try to distinguish overlap of generalization or redundancy for r in rules: if r == rule: break # ∀ x < j, ∃ <Px, deny> such that Px ⊆ Pj if r.action != rule.action and compare_bdd( r.toBDD(), Bdd.IMPL, rule.toBDD()): error_generalization.append(r) # ∀ x < j, ∃ <Px, accept> such that Px ⊆ Pj elif r.action == rule.action and compare_bdd( r.toBDD(), Bdd.IMPL, rule.toBDD()): error_redudant.append(r) elif compare_bdd(r.toBDD(), Bdd.AND, rule.toBDD()): error_rules.append(r) if error_redudant: error_list_append( AnomalyError.error_message( ErrorType.INT_PART_REDUNDANT, ErrorType.ERROR, rule, error_redudant)) if error_generalization: error_list_append( AnomalyError.error_message( ErrorType.INT_PART_GENERALIZATION, ErrorType.WARNING, rule, error_generalization)) if error_rules: error_list_append( AnomalyError.error_message( ErrorType.INT_PART_CORRELATION, ErrorType.WARNING, rule, error_rules)) else: error_list_append( AnomalyError.error_message( ErrorType.INT_PART_CORRELATION, ErrorType.WARNING, rule, error_rules)) if rule.action: accept = synthesize(accept, Bdd.OR, synthesize(remain, Bdd.AND, rule.toBDD())) else: deny = synthesize(deny, Bdd.OR, synthesize(remain, Bdd.AND, rule.toBDD())) remain = synthesize(Robdd.true(), Bdd.AND, negate_bdd(synthesize(accept, Bdd.OR, deny))) result_queue.put(error_list)
def _detect_anomaly(rules, result_queue, processed_id_rules, deep_search): """Detect anomaly of the given rule set. This algorithm is derived from the algorithm of Fireman. It uses ROBDD to compare each rules. The complexity of this algorithm is in O(n). For more informations read : - Firewall Policy Advisor for Anomaly Detection and rules analysis, http://www.arc.uncc.edu/pubs/im03-cr.pdf - FIREMAN : A Toolkit for FIREwall Modeling and ANalysis, http://www.cs.ucdavis.edu/~su/publications/fireman.pdf Parameters ---------- rules : Rule list. The list of rule to inspect result_queue : multiprocessing Queue. A queue to put errors """ remain = Robdd.true() accept = Robdd.false() deny = Robdd.false() error_list = deque() error_list_append = error_list.append for rule in rules: processed_id_rules.put(rule.identifier) error_rules = deque() # Pj ⊆ Rj if compare_bdd(rule.toBDD(), Bdd.IMPL, remain): pass else: # Pj ∩ Rj = ∅ if not compare_bdd(rule.toBDD(), Bdd.AND, remain): # Pj ⊆ Dj if compare_bdd(rule.toBDD(), Bdd.IMPL, (deny if rule.action else accept)): if deep_search: for r in rules: if r == rule: break # ∀ x < j, ∃ <Px, deny> such that Px ∩ Pj != ∅ if r.action != rule.action and compare_bdd(r.toBDD(), Bdd.AND, rule.toBDD()): error_rules.append(r) error_list_append(AnomalyError.error_message(ErrorType.INT_MASK_SHADOW, ErrorType.ERROR, rule, error_rules)) # Pj ∩ Dj = ∅ elif not compare_bdd(rule.toBDD(), Bdd.AND, (deny if rule.action else accept)): if deep_search: for r in rules: if r == rule: break # ∀ x < j, ∃ <Px, accept> such that Px ∩ Pj != ∅ if r.action == rule.action and compare_bdd(r.toBDD(), Bdd.AND, rule.toBDD()): error_rules.append(r) error_list_append(AnomalyError.error_message(ErrorType.INT_MASK_REDUNDANT, ErrorType.ERROR, rule, error_rules)) else: if deep_search: for r in rules: if r == rule: break # ∀ x < j, ∃ Px such that Px ∩ Pj != ∅ if compare_bdd(r.toBDD(), Bdd.AND, rule.toBDD()): error_rules.append(r) error_list_append(AnomalyError.error_message(ErrorType.INT_MASK_REDUNDANT_CORRELATION, ErrorType.ERROR, rule, error_rules)) else: error_redudant = deque() error_generalization = deque() if deep_search: # if deep search try to distinguish overlap of generalization or redundancy for r in rules: if r == rule: break # ∀ x < j, ∃ <Px, deny> such that Px ⊆ Pj if r.action != rule.action and compare_bdd(r.toBDD(), Bdd.IMPL, rule.toBDD()): error_generalization.append(r) # ∀ x < j, ∃ <Px, accept> such that Px ⊆ Pj elif r.action == rule.action and compare_bdd(r.toBDD(), Bdd.IMPL, rule.toBDD()): error_redudant.append(r) elif compare_bdd(r.toBDD(), Bdd.AND, rule.toBDD()): error_rules.append(r) if error_redudant: error_list_append(AnomalyError.error_message(ErrorType.INT_PART_REDUNDANT, ErrorType.ERROR, rule, error_redudant)) if error_generalization: error_list_append(AnomalyError.error_message(ErrorType.INT_PART_GENERALIZATION, ErrorType.WARNING, rule, error_generalization)) if error_rules: error_list_append(AnomalyError.error_message(ErrorType.INT_PART_CORRELATION, ErrorType.WARNING, rule, error_rules)) else: error_list_append(AnomalyError.error_message(ErrorType.INT_PART_CORRELATION, ErrorType.WARNING, rule, error_rules)) if rule.action: accept = synthesize(accept, Bdd.OR, synthesize(remain, Bdd.AND, rule.toBDD())) else: deny = synthesize(deny, Bdd.OR, synthesize(remain, Bdd.AND, rule.toBDD())) remain = synthesize(Robdd.true(), Bdd.AND, negate_bdd(synthesize(accept, Bdd.OR, deny))) result_queue.put(error_list)