[apparmor] [patch] [22/38] Add get_perms_for_path() and get_rules_for_path() to FileRuleset

Christian Boltz apparmor at cboltz.de
Fri Aug 12 21:00:11 UTC 2016


Hello,

this patch adds
- get_rules_for_path() returns all rules matching the given path
  (both exact matches and AARE matches)
- get_perms_for_path() returns the merged permissions for the given
  path and a list of paths used in the matching rules

Also add tests for these two functions.


[ 22-add-get-perms-for-path.diff ]

=== modified file ./utils/apparmor/rule/file.py
--- utils/apparmor/rule/file.py	2016-05-09 22:59:25.467301579 +0200
+++ utils/apparmor/rule/file.py	2016-05-09 23:00:26.726961243 +0200
@@ -380,7 +380,72 @@
 class FileRuleset(BaseRuleset):
     '''Class to handle and store a collection of file rules'''
 
-    pass
+    def get_rules_for_path(self, path, audit=False, deny=False):
+        '''Get all rules matching the given path
+           path can be str or AARE
+           If audit is True, only return rules with the audit flag set.
+           If deny is True, only return matching deny rules'''
+
+        matching_rules = FileRuleset()
+        for rule in self.rules:
+            if (rule.all_paths or rule.path.match(path)) and ((not 
deny) or rule.deny) and ((not audit) or rule.audit):
+                matching_rules.add(rule)
+
+        return matching_rules
+
+    def get_perms_for_path(self, path, audit=False, deny=False):
+        '''Get the summarized permissions of all rules matching the 
given path, and the list of paths involved in the calculation
+           path can be str or AARE
+           If audit is True, only analyze rules with the audit flag set.
+           If deny is True, only analyze matching deny rules
+           Returns {'allow': {'owner': set_of_perms, 'all': 
set_of_perms},
+                    'deny':  {'owner': set_of_perms, 'all': 
set_of_perms},
+                    'path':  involved_paths}
+           Note: exec rules and exec/link target are not honored!
+           '''
+           # XXX do we need to honor the link target?
+
+        perms = {
+            'allow':    {'owner': set(), 'all': set() },
+            'deny':     {'owner': set(), 'all': set() },
+        }
+        all_perms = {
+            'allow':    {'owner': False, 'all': False },
+            'deny':     {'owner': False, 'all': False },
+        }
+        paths       = set()
+
+        matching_rules = self.get_rules_for_path(path, audit, deny)
+
+        for rule in matching_rules.rules:
+            allow_or_deny = 'allow'
+            if rule.deny:
+                allow_or_deny = 'deny'
+
+            owner_or_all = 'all'
+            if rule.owner:
+                owner_or_all = 'owner'
+
+            if rule.all_perms:
+                all_perms[allow_or_deny][owner_or_all] = True
+            elif rule.perms:
+                perms[allow_or_deny][owner_or_all] = 
perms[allow_or_deny][owner_or_all].union(rule.perms)
+                paths.add(rule.path.regex)
+
+        allow = {}
+        deny = {}
+        for who in ['all', 'owner']:
+            if all_perms['allow'][who]:
+                allow[who] = FileRule.ALL
+            else:
+                allow[who] = perms['allow'][who]
+
+            if all_perms['deny'][who]:
+                deny[who] = FileRule.ALL
+            else:
+                deny[who] = perms['deny'][who]
+
+        return {'allow': allow, 'deny': deny, 'paths': paths}
 
 
 def split_perms(perm_string, deny):
=== modified file ./utils/test/test-file.py
--- utils/test/test-file.py	2016-05-09 22:59:25.467301579 +0200
+++ utils/test/test-file.py	2016-05-09 23:02:07.526401510 +0200
@@ -871,6 +871,109 @@
 #class FileDeleteTest(AATest):
 #    pass
 
+class FileGetRulesForPath(AATest):
+    tests = [
+        #  path                                 audit   deny    
expected
+        (('/etc/foo/dovecot.conf',              False,  False), ['/etc/
foo/* r,', '/etc/foo/dovecot.conf rw,',                                  
'']),
+        (('/etc/foo/foo.conf',                  False,  False), ['/etc/
foo/* r,',                                                               
'']),
+        (('/etc/foo/dovecot-database.conf.ext', False,  False), ['/etc/
foo/* r,', '/etc/foo/dovecot-database.conf.ext w,',                      
'']),
+        (('/etc/foo/auth.d/authfoo.conf',       False,  False), ['/etc/
foo/{auth,conf}.d/*.conf r,','/etc/foo/{auth,conf}.d/authfoo.conf w,',   
'']),
+        (('/etc/foo/dovecot-deny.conf',         False,  False), ['deny 
/etc/foo/dovecot-deny.conf r,', '', '/etc/foo/* r,',                     
'']),
+        (('/foo/bar',                           False,  True ), [                                                                                 
]),
+        (('/etc/foo/dovecot-deny.conf',         False,  True ), ['deny 
/etc/foo/dovecot-deny.conf r,',                                          
'']),
+        (('/etc/foo/foo.conf',                  False,  True ), [                                                                                 
]),
+        (('/etc/foo/owner.conf',                False,  False), ['/etc/
foo/* r,', 'owner /etc/foo/owner.conf w,',                               
'']),
+    ]
+
+    def _run_test(self, params, expected):
+        rules = [
+            '/etc/foo/* r,',
+            '/etc/foo/dovecot.conf rw,',
+            '/etc/foo/{auth,conf}.d/*.conf r,',
+            '/etc/foo/{auth,conf}.d/authfoo.conf w,',
+            '/etc/foo/dovecot-database.conf.ext w,',
+            'owner /etc/foo/owner.conf w,',
+            'deny /etc/foo/dovecot-deny.conf r,',
+        ]
+
+        ruleset = FileRuleset()
+        for rule in rules:
+            ruleset.add(FileRule.parse(rule))
+
+        matching = ruleset.get_rules_for_path(params[0], params[1], 
params[2])
+        self. assertEqual(matching.get_clean(), expected)
+
+
+class FileGetPermsForPath_1(AATest):
+    tests = [
+        #  path                                 audit   deny    
expected
+        (('/etc/foo/dovecot.conf',              False,  False), 
{'allow': {'all': {'r', 'w'},    'owner': set()  },  'deny': {'all': 
set(),          'owner': set()   }, 'paths': {'/etc/foo/*', '/etc/foo/
dovecot.conf'                                    }   }),
+        (('/etc/foo/foo.conf',                  False,  False), 
{'allow': {'all': {'r'     },    'owner': set()  },  'deny': {'all': 
set(),          'owner': set()   }, 'paths': {'/etc/foo/*'                                                             
}   }),
+        (('/etc/foo/dovecot-database.conf.ext', False,  False), 
{'allow': {'all': {'r', 'w'},    'owner': set()  },  'deny': {'all': 
set(),          'owner': set()   }, 'paths': {'/etc/foo/*', '/etc/foo/
dovecot-database.conf.ext'                       }   }),
+        (('/etc/foo/auth.d/authfoo.conf',       False,  False), 
{'allow': {'all': {'r', 'w'},    'owner': set()  },  'deny': {'all': 
set(),          'owner': set()   }, 'paths': {'/etc/foo/{auth,conf}.d/
*.conf', '/etc/foo/{auth,conf}.d/authfoo.conf'   }   }),
+        (('/etc/foo/dovecot-deny.conf',         False,  False), 
{'allow': {'all': {'r'     },    'owner': set()  },  'deny': {'all': 
{'r'     },     'owner': set()   }, 'paths': {'/etc/foo/*', '/etc/foo/
dovecot-deny.conf'                               }   }),
+        (('/foo/bar',                           False,  True ), 
{'allow': {'all': set(),         'owner': set()  },  'deny': {'all': 
set(),          'owner': set()   }, 'paths': set()                                                                         
}),
+        (('/etc/foo/dovecot-deny.conf',         False,  True ), 
{'allow': {'all': set(),         'owner': set()  },  'deny': {'all': 
{'r'     },     'owner': set()   }, 'paths': {'/etc/foo/dovecot-
deny.conf'                                             }   }),
+        (('/etc/foo/foo.conf',                  False,  True ), 
{'allow': {'all': set(),         'owner': set()  },  'deny': {'all': 
set(),          'owner': set()   }, 'paths': set()                                                                         
}),
+    ]
+
+    def _run_test(self, params, expected):
+        rules = [
+            '/etc/foo/* r,',
+            '/etc/foo/dovecot.conf rw,',
+            '/etc/foo/{auth,conf}.d/*.conf r,',
+            '/etc/foo/{auth,conf}.d/authfoo.conf w,',
+            '/etc/foo/dovecot-database.conf.ext w,',
+            'deny /etc/foo/dovecot-deny.conf r,',
+            '/usr/lib/dovecot/config ix,',
+        ]
+
+        ruleset = FileRuleset()
+        for rule in rules:
+            ruleset.add(FileRule.parse(rule))
+
+        perms = ruleset.get_perms_for_path(params[0], params[1], 
params[2])
+        self. assertEqual(perms, expected)
+
+class FileGetPermsForPath_2(AATest):
+    tests = [
+        #  path                                 audit   deny    
expected
+        (('/etc/foo/dovecot.conf',              False,  False), 
{'allow': {'all': FileRule.ALL, 'owner': set()  },  'deny': {'all': 
FileRule.ALL,   'owner': set()  }, 'paths': {'/etc/foo/*', '/etc/foo/
dovecot.conf'                                     }    }),
+        (('/etc/foo/dovecot.conf',              True,   False), 
{'allow': {'all': {'r', 'w'},   'owner': set()  },  'deny': {'all': 
set(),          'owner': set()  }, 'paths': {'/etc/foo/dovecot.conf'                                                   
}    }),
+        (('/etc/foo/foo.conf',                  False,  False), 
{'allow': {'all': FileRule.ALL, 'owner': set()  },  'deny': {'all': 
FileRule.ALL,   'owner': set()  }, 'paths': {'/etc/foo/*'                                                              
}    }),
+        (('/etc/foo/dovecot-database.conf.ext', False,  False), 
{'allow': {'all': FileRule.ALL, 'owner': set()  },  'deny': {'all': 
FileRule.ALL,   'owner': set()  }, 'paths': {'/etc/foo/*', '/etc/foo/
dovecot-database.conf.ext'                        }    }),
+        (('/etc/foo/auth.d/authfoo.conf',       False,  False), 
{'allow': {'all': FileRule.ALL, 'owner': set()  },  'deny': {'all': 
FileRule.ALL,   'owner': set()  }, 'paths': {'/etc/foo/{auth,conf}.d/
*.conf', '/etc/foo/{auth,conf}.d/authfoo.conf'    }    }),
+        (('/etc/foo/auth.d/authfoo.conf',       True,   False), 
{'allow': {'all': {'w'     },   'owner': set()  },  'deny': {'all': 
set(),          'owner': set()  }, 'paths': {'/etc/foo/{auth,conf}.d/
authfoo.conf'                                     }    }),
+        (('/etc/foo/dovecot-deny.conf',         False,  False), 
{'allow': {'all': FileRule.ALL, 'owner': set()  },  'deny': {'all': 
FileRule.ALL,   'owner': set()  }, 'paths': {'/etc/foo/*', '/etc/foo/
dovecot-deny.conf'                                }    }),
+        (('/foo/bar',                           False,  True ), 
{'allow': {'all': set(),        'owner': set()  },  'deny': {'all': 
FileRule.ALL,   'owner': set()  }, 'paths': set()                                                                           
}),
+        (('/etc/foo/dovecot-deny.conf',         False,  True ), 
{'allow': {'all': set(),        'owner': set()  },  'deny': {'all': 
FileRule.ALL,   'owner': set()  }, 'paths': {'/etc/foo/dovecot-
deny.conf'                                              }    }),
+        (('/etc/foo/foo.conf',                  False,  True ), 
{'allow': {'all': set(),        'owner': set()  },  'deny': {'all': 
FileRule.ALL,   'owner': set()  }, 'paths': set()                                                                           
}),
+    #   (('/etc/foo/owner.conf',                False,  True ), 
{'allow': {'all': set(),        'owner': {'w'}  },  'deny': {'all': 
FileRule.ALL,   'owner': set()  }, 'paths': {'/etc/foo/owner.conf'                                                     
}    }), # XXX doen't work yet
+    ]
+
+    def _run_test(self, params, expected):
+        rules = [
+            '/etc/foo/* r,',
+            'audit /etc/foo/dovecot.conf rw,',
+            '/etc/foo/{auth,conf}.d/*.conf r,',
+            'audit /etc/foo/{auth,conf}.d/authfoo.conf w,',
+            '/etc/foo/dovecot-database.conf.ext w,',
+            'deny /etc/foo/dovecot-deny.conf r,',
+            'file,',
+            'owner /etc/foo/owner.conf w,',
+            'deny file,',
+        ]
+
+        ruleset = FileRuleset()
+        for rule in rules:
+            ruleset.add(FileRule.parse(rule))
+
+        perms = ruleset.get_perms_for_path(params[0], params[1], 
params[2])
+        self. assertEqual(perms, expected)
+
+
+
+
 setup_all_loops(__name__)
 if __name__ == '__main__':
     unittest.main(verbosity=2)



Regards,

Christian Boltz
-- 
Don't spend too much time with numbers and statistics as they both
are like a bikini; the most important part is always hidden.
[Togan Muftuoglu in opensuse-factory]
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part.
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20160812/33f3e706/attachment-0001.pgp>


More information about the AppArmor mailing list