Commit 273e45a4 authored by Christian Boltz's avatar Christian Boltz

Merge branch 'cboltz-profile-list' into 'master'

Replace "existing_profiles" & fix minitools for named profiles

See merge request !249Acked-by: 's avatarJohn Johansen <john.johansen@canonical.com>
parents 2809060b 4d722f18
Pipeline #36183223 passed with stages
in 11 minutes and 43 seconds
......@@ -107,7 +107,7 @@ apparmor.check_qualifiers(program)
apparmor.loadincludes()
profile_filename = apparmor.get_profile_filename(program)
profile_filename = apparmor.get_profile_filename_from_attachment(program, True)
if os.path.exists(profile_filename):
apparmor.helpers[program] = apparmor.get_profile_flags(profile_filename, program)
else:
......
#! /usr/bin/python3
# ----------------------------------------------------------------------
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
# Copyright (C) 2014-2017 Christian Boltz <apparmor@cboltz.de>
# Copyright (C) 2014-2018 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
......@@ -57,7 +57,7 @@ def reset_aa():
apparmor.aa.aa = apparmor.aa.hasher()
apparmor.aa.filelist = apparmor.aa.hasher()
apparmor.aa.include = dict()
apparmor.aa.existing_profiles = apparmor.aa.hasher()
apparmor.aa.active_profiles = apparmor.aa.ProfileList()
apparmor.aa.original_aa = apparmor.aa.hasher()
def find_profiles_from_files(files):
......@@ -75,7 +75,7 @@ def find_files_from_profiles(profiles):
apparmor.aa.read_profiles()
for profile_name in profiles:
profile_to_filename[profile_name] = apparmor.aa.get_profile_filename(profile_name)
profile_to_filename[profile_name] = apparmor.aa.get_profile_filename_from_profile_name(profile_name, True)
reset_aa()
......
This diff is collapsed.
# ----------------------------------------------------------------------
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
# Copyright (C) 2015-2016 Christian Boltz <apparmor@cboltz.de>
# Copyright (C) 2015-2018 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
......@@ -44,11 +44,11 @@ class ReadLog:
# used to pre-filter log lines so that we hand over only relevant lines to LibAppArmor parsing
RE_LOG_ALL = re.compile('(' + '|'.join(RE_log_parts) + ')')
def __init__(self, pid, filename, existing_profiles, profile_dir):
def __init__(self, pid, filename, active_profiles, profile_dir):
self.filename = filename
self.profile_dir = profile_dir
self.pid = pid
self.existing_profiles = existing_profiles
self.active_profiles = active_profiles
self.log = []
self.debug_logger = DebugLogger('ReadLog')
self.LOG = None
......@@ -447,15 +447,16 @@ class ReadLog:
def profile_exists(self, program):
"""Returns True if profile exists, False otherwise"""
# Check cache of profiles
if self.existing_profiles.get(program, False):
if self.active_profiles.filename_from_profile_name(program):
return True
# Check the disk for profile
prof_path = self.get_profile_filename(program)
#print(prof_path)
if os.path.isfile(prof_path):
# Add to cache of profile
self.existing_profiles[program] = prof_path
return True
raise AppArmorBug('This should never happen, please open a bugreport!')
# self.active_profiles[program] = prof_path
# return True
return False
def get_profile_filename(self, profile):
......
# ----------------------------------------------------------------------
# Copyright (C) 2018 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# ----------------------------------------------------------------------
from apparmor.aare import AARE
from apparmor.common import AppArmorBug, AppArmorException
# setup module translations
from apparmor.translations import init_translation
_ = init_translation()
class ProfileList:
''' Stores the list of profiles (both name and attachment) and in which files they live '''
def __init__(self):
self.profile_names = {} # profile name -> filename
self.attachments = {} # attachment -> filename
self.attachments_AARE = {} # AARE(attachment) -> filename
def add(self, filename, profile_name, attachment):
''' Add the given profile and attachment to the list '''
if not filename:
raise AppArmorBug('Empty filename given to ProfileList')
if not profile_name and not attachment:
raise AppArmorBug('Neither profile name or attachment given')
if profile_name in self.profile_names:
raise AppArmorException(_('Profile %(profile_name)s exists in %(filename)s and %(filename2)s' % {'profile_name': profile_name, 'filename': filename, 'filename2': self.profile_names[profile_name]}))
if attachment in self.attachments:
raise AppArmorException(_('Profile for %(profile_name)s exists in %(filename)s and %(filename2)s' % {'profile_name': attachment, 'filename': filename, 'filename2': self.attachments[attachment]}))
if profile_name:
self.profile_names[profile_name] = filename
if attachment:
self.attachments[attachment] = filename
self.attachments_AARE[attachment] = AARE(attachment, True)
def filename_from_profile_name(self, name):
''' Return profile filename for the given profile name, or None '''
return self.profile_names.get(name, None)
def filename_from_attachment(self, attachment):
''' Return profile filename for the given attachment/executable path, or None '''
if not attachment.startswith( ('/', '@', '{') ):
raise AppArmorBug('Called filename_from_attachment with non-path attachment: %s' % attachment)
# plain path
if self.attachments.get(attachment):
return self.attachments[attachment]
# try AARE matches to cover profile names with alternations and wildcards
for path in self.attachments.keys():
if self.attachments_AARE[path].match(attachment):
return self.attachments[path] # XXX this returns the first match, not necessarily the best one
return None # nothing found
# ----------------------------------------------------------------------
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
# Copyright (C) 2015-2018 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
......@@ -66,12 +67,12 @@ class aa_tools:
profile = fq_path
else:
program = fq_path
profile = apparmor.get_profile_filename(fq_path)
profile = apparmor.get_profile_filename_from_attachment(fq_path, True)
else:
which = apparmor.which(p)
if which is not None:
program = apparmor.get_full_path(which)
profile = apparmor.get_profile_filename(program)
profile = apparmor.get_profile_filename_from_attachment(program, True)
elif os.path.exists(os.path.join(apparmor.profile_dir, p)):
program = None
profile = apparmor.get_full_path(os.path.join(apparmor.profile_dir, p)).strip()
......@@ -190,7 +191,7 @@ class aa_tools:
apparmor.check_qualifiers(program)
if os.path.exists(apparmor.get_profile_filename(program)) and not self.force:
if os.path.exists(apparmor.get_profile_filename_from_attachment(program, True)) and not self.force:
aaui.UI_Info(_('Profile for %s already exists - skipping.') % program)
else:
apparmor.autodep(program)
......@@ -198,7 +199,7 @@ class aa_tools:
apparmor.reload(program)
def clean_profile(self, program):
filename = apparmor.get_profile_filename(program)
filename = apparmor.get_profile_filename_from_attachment(program, True)
import apparmor.cleanprofile as cleanprofile
prof = cleanprofile.Prof(filename)
cleanprof = cleanprofile.CleanProf(True, prof, prof)
......@@ -220,14 +221,14 @@ class aa_tools:
while ans != 'CMD_SAVE_CHANGES':
ans, arg = q.promptUser()
if ans == 'CMD_SAVE_CHANGES':
apparmor.write_profile_ui_feedback(program)
apparmor.write_profile_ui_feedback(program, True)
self.reload_profile(filename)
elif ans == 'CMD_VIEW_CHANGES':
#oldprofile = apparmor.serialize_profile(apparmor.original_aa[program], program, {})
newprofile = apparmor.serialize_profile(apparmor.aa[program], program, {})
newprofile = apparmor.serialize_profile(apparmor.aa[program], program, {'is_attachment': True})
aaui.UI_Changes(filename, newprofile, comments=True)
else:
apparmor.write_profile_ui_feedback(program)
apparmor.write_profile_ui_feedback(program, True)
self.reload_profile(filename)
else:
raise apparmor.AppArmorException(_('The profile for %s does not exists. Nothing to clean.') % program)
......
#! /usr/bin/python3
# ------------------------------------------------------------------
#
# Copyright (C) 2015 Christian Boltz <apparmor@cboltz.de>
# Copyright (C) 2015-2018 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
......@@ -18,6 +18,7 @@ from apparmor.common import open_file_read
import apparmor.aa
from apparmor.logparser import ReadLog
from apparmor.profile_list import ProfileList
class TestLibapparmorTestMulti(AATest):
'''Parse all libraries/libapparmor/testsuite/test_multi tests and compare the result with the *.out files'''
......@@ -249,9 +250,15 @@ def logfile_to_profile(logfile):
if '//' in profile:
profile, hat = profile.split('//')
apparmor.aa.existing_profiles = {profile: profile_dummy_file}
apparmor.aa.active_profiles = ProfileList()
log_reader = ReadLog(dict(), logfile, apparmor.aa.existing_profiles, '')
# optional for now, might be needed one day
# if profile.startswith('/'):
# apparmor.aa.active_profiles.add(profile_dummy_file, profile, profile)
# else:
apparmor.aa.active_profiles.add(profile_dummy_file, profile, '')
log_reader = ReadLog(dict(), logfile, apparmor.aa.active_profiles, '')
log = log_reader.read_log('')
for root in log:
......
#! /usr/bin/python3
# ------------------------------------------------------------------
#
# Copyright (C) 2018 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License published by the Free Software Foundation.
#
# ------------------------------------------------------------------
import unittest
from common_test import AATest, setup_all_loops
from apparmor.common import AppArmorBug, AppArmorException
from apparmor.profile_list import ProfileList
class TestAdd(AATest):
def AASetup(self):
self.pl = ProfileList()
def testEmpty(self):
self.assertEqual(self.pl.profile_names, {})
self.assertEqual(self.pl.attachments, {})
def testAdd_1(self):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
self.assertEqual(self.pl.profile_names, {'foo': '/etc/apparmor.d/bin.foo'})
self.assertEqual(self.pl.attachments, {'/bin/foo': '/etc/apparmor.d/bin.foo'})
def testAdd_2(self):
self.pl.add('/etc/apparmor.d/bin.foo', None, '/bin/foo')
self.assertEqual(self.pl.profile_names, {})
self.assertEqual(self.pl.attachments, {'/bin/foo': '/etc/apparmor.d/bin.foo'})
def testAdd_3(self):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', None)
self.assertEqual(self.pl.profile_names, {'foo': '/etc/apparmor.d/bin.foo'})
self.assertEqual(self.pl.attachments, {})
def testAddError_1(self):
with self.assertRaises(AppArmorBug):
self.pl.add('', 'foo', '/bin/foo') # no filename
def testAddError_2(self):
with self.assertRaises(AppArmorBug):
self.pl.add('/etc/apparmor.d/bin.foo', None, None) # neither attachment or profile name
def testAddError_twice_1(self):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
with self.assertRaises(AppArmorException):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
def testAddError_twice_2(self):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
with self.assertRaises(AppArmorException):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', None)
def testAddError_twice_3(self):
self.pl.add('/etc/apparmor.d/bin.foo', None, '/bin/foo')
with self.assertRaises(AppArmorException):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
def testAddError_twice_4(self):
self.pl.add('/etc/apparmor.d/bin.foo', None, '/bin/foo')
with self.assertRaises(AppArmorException):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
def testAddError_twice_5(self):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', None)
with self.assertRaises(AppArmorException):
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
class TestFilename_from_profile_name(AATest):
tests = [
('foo', '/etc/apparmor.d/bin.foo'),
('/bin/foo', None),
('bar', None),
]
def AASetup(self):
self.pl = ProfileList()
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
def _run_test(self, params, expected):
self.assertEqual(self.pl.filename_from_profile_name(params), expected)
class TestFilename_from_attachment(AATest):
tests = [
('/bin/foo', '/etc/apparmor.d/bin.foo'),
('/bin/baz', '/etc/apparmor.d/bin.baz'),
('/bin/foobar', '/etc/apparmor.d/bin.foobar'),
('@{foo}', None), # XXX variables not supported yet (and @{foo} isn't defined in this test)
('/bin/404', None),
]
def AASetup(self):
self.pl = ProfileList()
self.pl.add('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
self.pl.add('/etc/apparmor.d/bin.baz', 'baz', '/bin/ba*')
self.pl.add('/etc/apparmor.d/bin.foobar', 'foobar', '/bin/foo{bar,baz}')
def _run_test(self, params, expected):
self.assertEqual(self.pl.filename_from_attachment(params), expected)
def test_non_path_attachment(self):
with self.assertRaises(AppArmorBug):
self.pl.filename_from_attachment('foo')
setup_all_loops(__name__)
if __name__ == '__main__':
unittest.main(verbosity=1)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment