mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-22 01:57:43 +00:00
utils: Basic support for file prefix in path rules
Bug: https://bugs.launchpad.net/bugs/1295346 Add the ability to read and write path rules containing the file prefix. This also includes bare "file," rules. The ALL global is updated to include a preceding NUL char to eliminate possibilities of a real file path colliding with the ALL global. Signed-off-by: Tyler Hicks <tyhicks@canonical.com> Acked-by: Steve Beattie <steve@nxnw.org>
This commit is contained in:
parent
a26b7a5a79
commit
54a24c2b6a
@ -79,7 +79,7 @@ seen_events = 0 # was our
|
||||
user_globs = []
|
||||
|
||||
# The key for representing bare rules such as "capability," or "file,"
|
||||
ALL = '_ALL'
|
||||
ALL = '\0ALL'
|
||||
|
||||
## Variables used under logprof
|
||||
### Were our
|
||||
@ -2615,6 +2615,7 @@ RE_PROFILE_VARIABLE = re.compile('^\s*(@\{?\w+\}?)\s*(\+?=)\s*(@*.+?)\s*,?\s*(#.
|
||||
RE_PROFILE_CONDITIONAL = re.compile('^\s*if\s+(not\s+)?(\$\{?\w*\}?)\s*\{\s*(#.*)?$')
|
||||
RE_PROFILE_CONDITIONAL_VARIABLE = re.compile('^\s*if\s+(not\s+)?defined\s+(@\{?\w+\}?)\s*\{\s*(#.*)?$')
|
||||
RE_PROFILE_CONDITIONAL_BOOLEAN = re.compile('^\s*if\s+(not\s+)?defined\s+(\$\{?\w+\}?)\s*\{\s*(#.*)?$')
|
||||
RE_PROFILE_FILE_ENTRY = re.compile('^\s*(audit\s+)?(allow\s+|deny\s+)?(owner\s+)?file(?:\s+([\"@/].*?)\s+(\S+)(\s+->\s*(.*?))?)?\s*,\s*(#.*)?$')
|
||||
RE_PROFILE_PATH_ENTRY = re.compile('^\s*(audit\s+)?(allow\s+|deny\s+)?(owner\s+)?([\"@/].*?)\s+(\S+)(\s+->\s*(.*?))?\s*,\s*(#.*)?$')
|
||||
RE_PROFILE_NETWORK = re.compile('^\s*(audit\s+)?(allow\s+|deny\s+)?network(.*)\s*(#.*)?$')
|
||||
RE_PROFILE_CHANGE_HAT = re.compile('^\s*\^(\"??.+?\"??)\s*,\s*(#.*)?$')
|
||||
@ -2896,6 +2897,69 @@ def parse_profile_data(data, file, do_include):
|
||||
else:
|
||||
profile_data[profile][hat][allow]['path'][path]['audit'] = set()
|
||||
|
||||
elif RE_PROFILE_FILE_ENTRY.search(line):
|
||||
matches = RE_PROFILE_FILE_ENTRY.search(line).groups()
|
||||
|
||||
if not profile:
|
||||
raise AppArmorException(_('Syntax Error: Unexpected file entry found in file: %s line: %s') % (file, lineno + 1))
|
||||
|
||||
audit = False
|
||||
if matches[0]:
|
||||
audit = True
|
||||
|
||||
allow = 'allow'
|
||||
if matches[1] and matches[1].strip() == 'deny':
|
||||
allow = 'deny'
|
||||
|
||||
user = False
|
||||
if matches[2]:
|
||||
user = True
|
||||
|
||||
path = None
|
||||
if matches[3]:
|
||||
path = matches[3].strip()
|
||||
|
||||
mode = None
|
||||
if matches[4]:
|
||||
mode = matches[4]
|
||||
|
||||
nt_name = None
|
||||
if matches[6]:
|
||||
nt_name = matches[6].strip()
|
||||
|
||||
if not path and not mode and not nt_name:
|
||||
path = ALL
|
||||
elif (path and not mode) or (nt_name and (not path or not mode)):
|
||||
raise AppArmorException(_('Syntax Error: Invalid file entry found in file: %s line: %s') % (file, lineno + 1))
|
||||
|
||||
p_re = convert_regexp(path)
|
||||
try:
|
||||
re.compile(p_re)
|
||||
except:
|
||||
raise AppArmorException(_('Syntax Error: Invalid Regex %s in file: %s line: %s') % (path, file, lineno + 1))
|
||||
|
||||
tmpmode = set()
|
||||
if mode:
|
||||
if not validate_profile_mode(mode, allow, nt_name):
|
||||
raise AppArmorException(_('Invalid mode %s in file: %s line: %s') % (mode, file, lineno + 1))
|
||||
|
||||
if user:
|
||||
tmpmode = str_to_mode('%s::' % mode)
|
||||
else:
|
||||
tmpmode = str_to_mode(mode)
|
||||
|
||||
profile_data[profile][hat][allow]['path'][path]['mode'] = profile_data[profile][hat][allow]['path'][path].get('mode', set()) | tmpmode
|
||||
|
||||
profile_data[profile][hat][allow]['path'][path]['file_prefix'] = True
|
||||
|
||||
if nt_name:
|
||||
profile_data[profile][hat][allow]['path'][path]['to'] = nt_name
|
||||
|
||||
if audit:
|
||||
profile_data[profile][hat][allow]['path'][path]['audit'] = profile_data[profile][hat][allow]['path'][path].get('audit', set()) | tmpmode
|
||||
else:
|
||||
profile_data[profile][hat][allow]['path'][path]['audit'] = set()
|
||||
|
||||
elif re_match_include(line):
|
||||
# Include files
|
||||
include_name = re_match_include(line)
|
||||
@ -3359,13 +3423,19 @@ def write_path_rules(prof_data, depth, allow):
|
||||
|
||||
if prof_data[allow].get('path', False):
|
||||
for path in sorted(prof_data[allow]['path'].keys()):
|
||||
filestr = ''
|
||||
if prof_data[allow]['path'][path].get('file_prefix', False):
|
||||
filestr = 'file '
|
||||
mode = prof_data[allow]['path'][path]['mode']
|
||||
audit = prof_data[allow]['path'][path]['audit']
|
||||
tail = ''
|
||||
if prof_data[allow]['path'][path].get('to', False):
|
||||
tail = ' -> %s' % prof_data[allow]['path'][path]['to']
|
||||
user, other = split_mode(mode)
|
||||
user_audit, other_audit = split_mode(audit)
|
||||
user = None
|
||||
other = None
|
||||
if mode or audit:
|
||||
user, other = split_mode(mode)
|
||||
user_audit, other_audit = split_mode(audit)
|
||||
|
||||
while user or other:
|
||||
ownerstr = ''
|
||||
@ -3393,13 +3463,19 @@ def write_path_rules(prof_data, depth, allow):
|
||||
if tmpmode & tmpaudit:
|
||||
modestr = mode_to_str(tmpmode & tmpaudit)
|
||||
path = quote_if_needed(path)
|
||||
data.append('%saudit %s%s%s %s%s,' % (pre, allowstr, ownerstr, path, modestr, tail))
|
||||
data.append('%saudit %s%s%s%s %s%s,' % (pre, allowstr, ownerstr, filestr, path, modestr, tail))
|
||||
tmpmode = tmpmode - tmpaudit
|
||||
|
||||
if tmpmode:
|
||||
modestr = mode_to_str(tmpmode)
|
||||
path = quote_if_needed(path)
|
||||
data.append('%s%s%s%s %s%s,' % (pre, allowstr, ownerstr, path, modestr, tail))
|
||||
data.append('%s%s%s%s%s %s%s,' % (pre, allowstr, ownerstr, filestr, path, modestr, tail))
|
||||
|
||||
if filestr and path == ALL:
|
||||
auditstr = ''
|
||||
if audit == 0:
|
||||
auditstr = 'audit '
|
||||
data.append('%s%s%s%s%s,' % (pre, auditstr, allowstr, filestr, tail))
|
||||
|
||||
data.append('')
|
||||
return data
|
||||
@ -3969,6 +4045,71 @@ def serialize_profile_from_old_profile(profile_data, name, options):
|
||||
#To-Do
|
||||
pass
|
||||
|
||||
elif RE_PROFILE_FILE_ENTRY.search(line):
|
||||
matches = RE_PROFILE_FILE_ENTRY.search(line).groups()
|
||||
audit = False
|
||||
if matches[0]:
|
||||
audit = True
|
||||
allow = 'allow'
|
||||
if matches[1] and matches[1].split() == 'deny':
|
||||
allow = 'deny'
|
||||
|
||||
user = False
|
||||
if matches[2]:
|
||||
user = True
|
||||
|
||||
path = None
|
||||
if matches[3]:
|
||||
path = matches[3].strip()
|
||||
|
||||
mode = None
|
||||
if matches[4]:
|
||||
mode = matches[4].strip()
|
||||
|
||||
nt_name = None
|
||||
if matches[6]:
|
||||
nt_name = matches[6].strip()
|
||||
|
||||
if not path and not mode and not nt_name:
|
||||
path = ALL
|
||||
elif (path and not mode) or (nt_name and (not path or not mode)):
|
||||
correct = False
|
||||
|
||||
tmpmode = set()
|
||||
if mode:
|
||||
if user:
|
||||
tmpmode = str_to_mode('%s::' % mode)
|
||||
else:
|
||||
tmpmode = str_to_mode(mode)
|
||||
|
||||
if not write_prof_data[hat][allow]['path'][path].get('mode', set()) & tmpmode:
|
||||
if path != ALL:
|
||||
correct = False
|
||||
|
||||
if nt_name and not write_prof_data[hat][allow]['path'][path].get('to', False) == nt_name:
|
||||
correct = False
|
||||
|
||||
if audit and not write_prof_data[hat][allow]['path'][path].get('audit', set()) & tmpmode:
|
||||
if path != ALL:
|
||||
correct = False
|
||||
|
||||
if correct:
|
||||
if not segments['path'] and True in segments.values():
|
||||
for segs in list(filter(lambda x: segments[x], segments.keys())):
|
||||
depth = len(line) - len(line.lstrip())
|
||||
data += write_methods[segs](write_prof_data[name], int(depth / 2))
|
||||
segments[segs] = False
|
||||
if write_prof_data[name]['allow'].get(segs, False):
|
||||
write_prof_data[name]['allow'].pop(segs)
|
||||
if write_prof_data[name]['deny'].get(segs, False):
|
||||
write_prof_data[name]['deny'].pop(segs)
|
||||
segments['path'] = True
|
||||
write_prof_data[hat][allow]['path'].pop(path)
|
||||
data.append(line)
|
||||
else:
|
||||
#To-Do
|
||||
pass
|
||||
|
||||
elif re_match_include(line):
|
||||
include_name = re_match_include(line)
|
||||
if profile:
|
||||
|
@ -96,6 +96,9 @@ regex_split_comment_testcases = [
|
||||
('dbus send member=no_comment, ', False),
|
||||
('audit "/tmp/foo, # bar" rw', False),
|
||||
('audit "/tmp/foo, # bar" rw # comment', ('audit "/tmp/foo, # bar" rw ', '# comment')),
|
||||
('file,', False),
|
||||
('file, # bare', ('file, ', '# bare')),
|
||||
('file /tmp/foo rw, # read-write', ('file /tmp/foo rw, ', '# read-write')),
|
||||
]
|
||||
|
||||
def setup_split_comment_testcases():
|
||||
@ -154,6 +157,125 @@ class AARegexCapability(unittest.TestCase):
|
||||
result = aa.RE_PROFILE_CAP.search(line)
|
||||
self.assertFalse(result, 'Found unexpected capability rule in "%s"' % line)
|
||||
|
||||
class AARegexPath(unittest.TestCase):
|
||||
'''Tests for RE_PROFILE_PATH_ENTRY'''
|
||||
|
||||
def test_simple_path_01(self):
|
||||
'''test ' /tmp/foo r,' '''
|
||||
|
||||
line = ' /tmp/foo r,'
|
||||
result = aa.RE_PROFILE_PATH_ENTRY.search(line)
|
||||
self.assertTrue(result, 'Couldn\'t find file rule in "%s"' % line)
|
||||
mode = result.groups()[4].strip()
|
||||
self.assertEqual(mode, 'r', 'Expected mode "r", got "%s"' % (mode))
|
||||
|
||||
def test_simple_path_02(self):
|
||||
'''test ' audit /tmp/foo rw,' '''
|
||||
|
||||
line = ' audit /tmp/foo rw,'
|
||||
result = aa.RE_PROFILE_PATH_ENTRY.search(line)
|
||||
self.assertTrue(result, 'Couldn\'t find file rule in "%s"' % line)
|
||||
audit = result.groups()[0].strip()
|
||||
self.assertEqual(audit, 'audit', 'Couldn\t find audit modifier')
|
||||
mode = result.groups()[4].strip()
|
||||
self.assertEqual(mode, 'rw', 'Expected mode "rw", got "%s"' % (mode))
|
||||
|
||||
def test_simple_path_03(self):
|
||||
'''test ' audit deny /tmp/foo rw,' '''
|
||||
|
||||
line = ' audit deny /tmp/foo rw,'
|
||||
result = aa.RE_PROFILE_PATH_ENTRY.search(line)
|
||||
self.assertTrue(result, 'Couldn\'t find file rule in "%s"' % line)
|
||||
audit = result.groups()[0].strip()
|
||||
self.assertEqual(audit, 'audit', 'Couldn\t find audit modifier')
|
||||
deny = result.groups()[1].strip()
|
||||
self.assertEqual(deny, 'deny', 'Couldn\t find deny modifier')
|
||||
mode = result.groups()[4].strip()
|
||||
self.assertEqual(mode, 'rw', 'Expected mode "rw", got "%s"' % (mode))
|
||||
|
||||
def test_simple_bad_path_01(self):
|
||||
'''test ' file,' '''
|
||||
|
||||
line = ' file,'
|
||||
result = aa.RE_PROFILE_PATH_ENTRY.search(line)
|
||||
self.assertFalse(result, 'RE_PROFILE_PATH_ENTRY unexpectedly matched "%s"' % line)
|
||||
|
||||
def test_simple_bad_path_02(self):
|
||||
'''test ' file /tmp/foo rw,' '''
|
||||
|
||||
line = ' file /tmp/foo rw,'
|
||||
result = aa.RE_PROFILE_PATH_ENTRY.search(line)
|
||||
self.assertFalse(result, 'RE_PROFILE_PATH_ENTRY unexpectedly matched "%s"' % line)
|
||||
|
||||
class AARegexFile(unittest.TestCase):
|
||||
'''Tests for RE_PROFILE_FILE_ENTRY'''
|
||||
|
||||
def _assertEqualStrings(self, str1, str2):
|
||||
self.assertEqual(str1, str2, 'Expected %s, got "%s"' % (str1, str2))
|
||||
|
||||
def test_simple_file_01(self):
|
||||
'''test ' file /tmp/foo rw,' '''
|
||||
|
||||
path = '/tmp/foo'
|
||||
mode = 'rw'
|
||||
line = ' file %s %s,' % (path, mode)
|
||||
result = aa.RE_PROFILE_FILE_ENTRY.search(line)
|
||||
self.assertTrue(result, 'Couldn\'t find file rule in "%s"' % line)
|
||||
self._assertEqualStrings(path, result.groups()[3].strip())
|
||||
self._assertEqualStrings(mode, result.groups()[4].strip())
|
||||
|
||||
def test_simple_file_02(self):
|
||||
'''test ' file,' '''
|
||||
|
||||
line = ' file,'
|
||||
result = aa.RE_PROFILE_FILE_ENTRY.search(line)
|
||||
self.assertTrue(result, 'Couldn\'t find file rule in "%s"' % line)
|
||||
path = result.groups()[3]
|
||||
self.assertEqual(path, None, 'Unexpected path, got "%s"' % path)
|
||||
mode = result.groups()[4]
|
||||
self.assertEqual(mode, None, 'Unexpected mode, got "%s"' % (mode))
|
||||
|
||||
def test_simple_file_03(self):
|
||||
'''test ' audit file,' '''
|
||||
|
||||
line = ' audit file,'
|
||||
result = aa.RE_PROFILE_FILE_ENTRY.search(line)
|
||||
self.assertTrue(result, 'Couldn\'t find file rule in "%s"' % line)
|
||||
audit = result.groups()[0].strip()
|
||||
self.assertEqual(audit, 'audit', 'Couldn\t find audit modifier')
|
||||
path = result.groups()[3]
|
||||
self.assertEqual(path, None, 'Unexpected path, got "%s"' % path)
|
||||
mode = result.groups()[4]
|
||||
self.assertEqual(mode, None, 'Unexpected mode, got "%s"' % (mode))
|
||||
|
||||
def test_simple_bad_file_01(self):
|
||||
'''test ' dbus,' '''
|
||||
|
||||
line = ' dbus,'
|
||||
result = aa.RE_PROFILE_FILE_ENTRY.search(line)
|
||||
self.assertFalse(result, 'RE_PROFILE_FILE_ENTRY unexpectedly matched "%s"' % line)
|
||||
|
||||
def test_simple_bad_file_02(self):
|
||||
'''test ' /tmp/foo rw,' '''
|
||||
|
||||
line = ' /tmp/foo rw,'
|
||||
result = aa.RE_PROFILE_FILE_ENTRY.search(line)
|
||||
self.assertFalse(result, 'RE_PROFILE_FILE_ENTRY unexpectedly matched "%s"' % line)
|
||||
|
||||
def test_simple_bad_file_03(self):
|
||||
'''test ' file /tmp/foo,' '''
|
||||
|
||||
line = ' file /tmp/foo,'
|
||||
result = aa.RE_PROFILE_FILE_ENTRY.search(line)
|
||||
self.assertFalse(result, 'RE_PROFILE_FILE_ENTRY unexpectedly matched "%s"' % line)
|
||||
|
||||
def test_simple_bad_file_04(self):
|
||||
'''test ' file r,' '''
|
||||
|
||||
line = ' file r,'
|
||||
result = aa.RE_PROFILE_FILE_ENTRY.search(line)
|
||||
self.assertFalse(result, 'RE_PROFILE_FILE_ENTRY unexpectedly matched "%s"' % line)
|
||||
|
||||
if __name__ == '__main__':
|
||||
verbosity = 2
|
||||
|
||||
@ -164,6 +286,8 @@ if __name__ == '__main__':
|
||||
test_suite.addTest(unittest.TestLoader().loadTestsFromTestCase(AARegexHasComma))
|
||||
test_suite.addTest(unittest.TestLoader().loadTestsFromTestCase(AARegexSplitComment))
|
||||
test_suite.addTest(unittest.TestLoader().loadTestsFromTestCase(AARegexCapability))
|
||||
test_suite.addTest(unittest.TestLoader().loadTestsFromTestCase(AARegexPath))
|
||||
test_suite.addTest(unittest.TestLoader().loadTestsFromTestCase(AARegexFile))
|
||||
result = unittest.TextTestRunner(verbosity=verbosity).run(test_suite)
|
||||
if not result.wasSuccessful():
|
||||
exit(1)
|
||||
|
Loading…
x
Reference in New Issue
Block a user