mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01:00
This patchset is broken into 4 parts:
* the application, library, documentation and installation script * the initial templates and policy groups. This will undoubtedly need refinement as we get feedback from users. Initial policy is based on Ubuntu's Application Review Board (ARB) requirements[2]. * tests for the library * Makefile integration Templates are stored in /usr/share/apparmor/easyprof/templates and policy groups in /usr/share/apparmor/easyprof/policygroups. This can be adjusted via /etc/apparmor/easyprof.conf. The aa-easyprof.pod has complete documentation on usage with some additional information in utils/easyprof/README (mostly duplicated here). Testing can be performed in a number of ways: $ cd utils ; make check # runs unit tests and pyflakes Unit tests manually: $ ./test/test-aa-easyprof.py In source manual testing: $ ./aa-easyprof --templates-dir=./easyprof/templates \ --policy-groups-dir=./easyprof/policygroups \ ... \ /opt/foo/bin/foo Post-install manual testing: $ make DESTDIR=/tmp/test PERLDIR=/tmp/test/usr/share/perl5/Immunix install $ cd /tmp/test $ PYTHONPATH=/tmp/test/usr/local/.../dist-packages ./usr/bin/aa-easyprof \ --templates-dir=/tmp/test/usr/share/apparmor/easyprof/templates \ --policy-groups-dir=/tmp/test/usr/share/apparmor/easyprof/policygroups \ /opt/bin/foo (you may also adjust /tmp/test/etc/apparmor/easyprof.conf to avoid specifying --templates-dir and --policy-groups-dir). Committing this now based on conversation with John and Steve. Acked-By: Jamie Strandboge <jamie@canonical.com>
This commit is contained in:
parent
279b5945cb
commit
1db463f4de
15 changed files with 1889 additions and 3 deletions
|
@ -32,8 +32,10 @@ PERLTOOLS = aa-genprof aa-logprof aa-autodep aa-audit aa-complain aa-enforce \
|
|||
TOOLS = ${PERLTOOLS} aa-decode aa-status
|
||||
MODULES = ${MODDIR}/AppArmor.pm ${MODDIR}/Repository.pm \
|
||||
${MODDIR}/Config.pm ${MODDIR}/Severity.pm
|
||||
PYTOOLS = aa-easyprof
|
||||
PYSETUP = python-tools-setup.py
|
||||
|
||||
MANPAGES = ${TOOLS:=.8} logprof.conf.5
|
||||
MANPAGES = ${TOOLS:=.8} logprof.conf.5 ${PYTOOLS:=.8}
|
||||
|
||||
all: ${MANPAGES} ${HTMLMANPAGES}
|
||||
$(MAKE) -C po all
|
||||
|
@ -45,9 +47,10 @@ BINDIR=${DESTDIR}/usr/sbin
|
|||
CONFDIR=${DESTDIR}/etc/apparmor
|
||||
VENDOR_PERL=$(shell perl -e 'use Config; print $$Config{"vendorlib"};')
|
||||
PERLDIR=${DESTDIR}${VENDOR_PERL}/${MODDIR}
|
||||
PYPREFIX=/usr
|
||||
|
||||
po/${NAME}.pot: ${TOOLS}
|
||||
$(MAKE) -C po ${NAME}.pot NAME=${NAME} SOURCES="${TOOLS} ${MODULES}"
|
||||
po/${NAME}.pot: ${TOOLS} ${PYTOOLS}
|
||||
$(MAKE) -C po ${NAME}.pot NAME=${NAME} SOURCES="${TOOLS} ${MODULES} ${PYTOOLS}"
|
||||
|
||||
.PHONY: install
|
||||
install: ${MANPAGES} ${HTMLMANPAGES}
|
||||
|
@ -62,6 +65,7 @@ install: ${MANPAGES} ${HTMLMANPAGES}
|
|||
$(MAKE) install_manpages DESTDIR=${DESTDIR}
|
||||
$(MAKE) -C vim install DESTDIR=${DESTDIR}
|
||||
ln -sf aa-status.8 ${DESTDIR}/${MANDIR}/man8/apparmor_status.8
|
||||
python ${PYSETUP} install --prefix=${PYPREFIX} --root=${DESTDIR} --version=${VERSION}
|
||||
|
||||
.PHONY: clean
|
||||
ifndef VERBOSE
|
||||
|
@ -72,6 +76,8 @@ clean: _clean
|
|||
rm -f Make.rules
|
||||
$(MAKE) -C po clean
|
||||
$(MAKE) -C vim clean
|
||||
rm -rf staging/ build/
|
||||
rm -f apparmor/*.pyc
|
||||
|
||||
# ${CAPABILITIES} is defined in common/Make.rules
|
||||
.PHONY: check_severity_db
|
||||
|
@ -92,3 +98,13 @@ check: check_severity_db
|
|||
for i in ${MODULES} ${PERLTOOLS} ; do \
|
||||
perl -c $$i || exit 1; \
|
||||
done
|
||||
tmpfile=$$(mktemp --tmpdir aa-pyflakes-XXXXXX); \
|
||||
for i in ${PYTOOLS} apparmor aa-status test/*.py; do \
|
||||
echo Checking $$i; \
|
||||
pyflakes $$i 2>&1 | grep -v "undefined name '_'" > $$tmpfile; \
|
||||
test -s $$tmpfile && cat $$tmpfile && rm -f $$tmpfile && exit 1; \
|
||||
done || true; \
|
||||
rm -f $$tmpfile
|
||||
for i in test/* ; do \
|
||||
python $$i || exit 1; \
|
||||
done
|
||||
|
|
65
utils/aa-easyprof
Normal file
65
utils/aa-easyprof
Normal file
|
@ -0,0 +1,65 @@
|
|||
#! /usr/bin/env python
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2011-2012 Canonical Ltd.
|
||||
#
|
||||
# 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 apparmor.easyprof
|
||||
from apparmor.easyprof import AppArmorException, error
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
def usage():
|
||||
'''Return usage information'''
|
||||
return 'USAGE: %s [options] <path to binary>' % \
|
||||
os.path.basename(sys.argv[0])
|
||||
|
||||
(opt, args) = apparmor.easyprof.parse_args()
|
||||
binary = None
|
||||
|
||||
m = usage()
|
||||
if opt.show_policy_group and not opt.policy_groups:
|
||||
error("Must specify -p with --show-policy-group")
|
||||
elif not opt.template and not opt.policy_groups and len(args) < 1:
|
||||
error("Must specify full path to binary\n%s" % m)
|
||||
|
||||
binary = None
|
||||
if len(args) >= 1:
|
||||
binary = args[0]
|
||||
|
||||
try:
|
||||
easyp = apparmor.easyprof.AppArmorEasyProfile(binary, opt)
|
||||
except AppArmorException, e:
|
||||
error(e.value)
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
if opt.list_templates:
|
||||
apparmor.easyprof.print_basefilenames(easyp.get_templates())
|
||||
sys.exit(0)
|
||||
elif opt.template and opt.show_template:
|
||||
files = [os.path.join(easyp.dirs['templates'], opt.template)]
|
||||
apparmor.easyprof.print_files(files)
|
||||
sys.exit(0)
|
||||
elif opt.list_policy_groups:
|
||||
apparmor.easyprof.print_basefilenames(easyp.get_policy_groups())
|
||||
sys.exit(0)
|
||||
elif opt.policy_groups and opt.show_policy_group:
|
||||
for g in opt.policy_groups.split(','):
|
||||
files = [os.path.join(easyp.dirs['policygroups'], g)]
|
||||
apparmor.easyprof.print_files(files)
|
||||
sys.exit(0)
|
||||
elif binary == None:
|
||||
error("Must specify full path to binary\n%s" % m)
|
||||
|
||||
# if we made it here, generate a profile
|
||||
params = apparmor.easyprof.gen_policy_params(binary, opt)
|
||||
p = easyp.gen_policy(**params)
|
||||
print p,
|
||||
|
146
utils/aa-easyprof.pod
Normal file
146
utils/aa-easyprof.pod
Normal file
|
@ -0,0 +1,146 @@
|
|||
# This publication is intellectual property of Canonical Ltd. Its contents
|
||||
# can be duplicated, either in part or in whole, provided that a copyright
|
||||
# label is visibly located on each copy.
|
||||
#
|
||||
# All information found in this book has been compiled with utmost
|
||||
# attention to detail. However, this does not guarantee complete accuracy.
|
||||
# Neither Canonical Ltd, the authors, nor the translators shall be held
|
||||
# liable for possible errors or the consequences thereof.
|
||||
#
|
||||
# Many of the software and hardware descriptions cited in this book
|
||||
# are registered trademarks. All trade names are subject to copyright
|
||||
# restrictions and may be registered trade marks. Canonical Ltd
|
||||
# essentially adheres to the manufacturer's spelling.
|
||||
#
|
||||
# Names of products and trademarks appearing in this book (with or without
|
||||
# specific notation) are likewise subject to trademark and trade protection
|
||||
# laws and may thus fall under copyright restrictions.
|
||||
#
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
aa-easyprof - AppArmor profile generation made easy.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<aa-easyprof> [option] <path to binary>
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
B<aa-easyprof> provides an easy to use interface for AppArmor policy
|
||||
generation. B<aa-easyprof> supports the use of templates and policy groups to
|
||||
quickly profile an application. Please note that while this tool can help
|
||||
with policy generation, its utility is dependent on the quality of the
|
||||
templates, policy groups and abstractions used. Also, this tool may create
|
||||
policy which is less restricted than creating policy by hand or with
|
||||
B<aa-genprof> and B<aa-logprof>.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
B<aa-easyprof> accepts the following arguments:
|
||||
|
||||
=over 4
|
||||
|
||||
=item -t TEMPLATE, --template=TEMPLATE
|
||||
|
||||
Specify which template to use. May specify either a system template from
|
||||
/usr/share/apparmor/easyprof/templates or a filename for the template to
|
||||
use. If not specified, use /usr/share/apparmor/easyprof/templates/default.
|
||||
|
||||
=item -p POLICYGROUPS, --policy-groups=POLICYGROUPS
|
||||
|
||||
Specify POLICY as a comma-separated list of policy groups. See --list-templates
|
||||
for supported policy groups. The available policy groups are in
|
||||
/usr/share/apparmor/easyprof/policy. Policy groups are simply groupings of
|
||||
AppArmor rules or policies. They are similar to AppArmor abstractions, but
|
||||
usually encompass more policy rules.
|
||||
|
||||
=item -a ABSTRACTIONS, --abstractions=ABSTRACTIONS
|
||||
|
||||
Specify ABSTRACTIONS as a comma-separated list of AppArmor abstractions. It is
|
||||
usually recommended you use policy groups instead, but this is provided as a
|
||||
convenience. AppArmor abstractions are located in /etc/apparmor.d/abstractions.
|
||||
See apparmor.d(5) for details.
|
||||
|
||||
=item -r PATH, --read-path=PATH
|
||||
|
||||
Specify a PATH to allow owner reads. May be specified multiple times. If the
|
||||
PATH ends in a '/', then PATH is treated as a directory and reads are allowed
|
||||
to all files under this directory. Can optionally use '/*' at the end of the
|
||||
PATH to only allow reads to files directly in PATH.
|
||||
|
||||
=item -w PATH, --write-dir=PATH
|
||||
|
||||
Like --read-path but also allow owner writes in additions to reads.
|
||||
|
||||
=item -n NAME, --name=NAME
|
||||
|
||||
Specify NAME of policy. If not specified, NAME is set to the name of the
|
||||
binary. The NAME of the policy is often used as part of the path in the
|
||||
various templates.
|
||||
|
||||
=item --template-var="@{VAR}=VALUE"
|
||||
|
||||
Set VAR to VALUE in the resulting policy. This typically only makes sense if
|
||||
the specified template uses this value. May be specified multiple times.
|
||||
|
||||
=item --list-templates
|
||||
|
||||
List available templates.
|
||||
|
||||
=item --show-template=TEMPLATE
|
||||
|
||||
Display template specified with --template.
|
||||
|
||||
=item --templates-dir=PATH
|
||||
|
||||
Use PATH instead of system templates directory.
|
||||
|
||||
=item --list-policy-groups
|
||||
|
||||
List available policy groups.
|
||||
|
||||
=item --show-policy-group
|
||||
|
||||
Display policy groups specified with --policy.
|
||||
|
||||
=item --policy-groups-dir=PATH
|
||||
|
||||
Use PATH instead of system policy-groups directory.
|
||||
|
||||
=item --author
|
||||
|
||||
Specify author of the policy.
|
||||
|
||||
=item --copyright
|
||||
|
||||
Specify copyright of the policy.
|
||||
|
||||
=item --comment
|
||||
|
||||
Specify comment for the policy.
|
||||
|
||||
=back
|
||||
|
||||
=head1 EXAMPLE
|
||||
|
||||
Example usage for a program named 'foo' which is installed in /opt/foo:
|
||||
|
||||
=over
|
||||
|
||||
$ aa-easyprof --template=user-application --template-var="@{APPNAME}=foo" --policy-groups=opt-application,user-application /opt/foo/bin/FooApp
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
If you find any additional bugs, please report them to Launchpad at
|
||||
L<https://bugs.launchpad.net/apparmor/+filebug>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
apparmor(7) apparmor.d(5)
|
||||
|
||||
=cut
|
9
utils/apparmor/__init__.py
Normal file
9
utils/apparmor/__init__.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2011-2012 Canonical Ltd.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# ------------------------------------------------------------------
|
567
utils/apparmor/easyprof.py
Normal file
567
utils/apparmor/easyprof.py
Normal file
|
@ -0,0 +1,567 @@
|
|||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2011-2012 Canonical Ltd.
|
||||
#
|
||||
# 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 codecs
|
||||
import glob
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
#
|
||||
# TODO: move this out to the common library
|
||||
#
|
||||
#from apparmor import AppArmorException
|
||||
class AppArmorException(Exception):
|
||||
'''This class represents AppArmor exceptions'''
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
#
|
||||
# End common
|
||||
#
|
||||
|
||||
DEBUGGING = False
|
||||
|
||||
#
|
||||
# TODO: move this out to a utilities library
|
||||
#
|
||||
def error(out, exit_code=1, do_exit=True):
|
||||
'''Print error message and exit'''
|
||||
try:
|
||||
print >> sys.stderr, "ERROR: %s" % (out)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
if do_exit:
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
def warn(out):
|
||||
'''Print warning message'''
|
||||
try:
|
||||
print >> sys.stderr, "WARN: %s" % (out)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
|
||||
def msg(out, output=sys.stdout):
|
||||
'''Print message'''
|
||||
try:
|
||||
print >> output, "%s" % (out)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
|
||||
def cmd(command):
|
||||
'''Try to execute the given command.'''
|
||||
debug(command)
|
||||
try:
|
||||
sp = subprocess.Popen(command, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
except OSError, ex:
|
||||
return [127, str(ex)]
|
||||
|
||||
out = sp.communicate()[0]
|
||||
return [sp.returncode, out]
|
||||
|
||||
|
||||
def cmd_pipe(command1, command2):
|
||||
'''Try to pipe command1 into command2.'''
|
||||
try:
|
||||
sp1 = subprocess.Popen(command1, stdout=subprocess.PIPE)
|
||||
sp2 = subprocess.Popen(command2, stdin=sp1.stdout)
|
||||
except OSError, ex:
|
||||
return [127, str(ex)]
|
||||
|
||||
out = sp2.communicate()[0]
|
||||
return [sp2.returncode, out]
|
||||
|
||||
|
||||
def debug(out):
|
||||
'''Print debug message'''
|
||||
if DEBUGGING:
|
||||
try:
|
||||
print >> sys.stderr, "DEBUG: %s" % (out)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
|
||||
def valid_binary_path(path):
|
||||
'''Validate name'''
|
||||
try:
|
||||
a_path = os.path.abspath(path)
|
||||
except Exception:
|
||||
debug("Could not find absolute path for binary")
|
||||
return False
|
||||
|
||||
if path != a_path:
|
||||
debug("Binary should use a normalized absolute path")
|
||||
return False
|
||||
|
||||
if not os.path.exists(a_path):
|
||||
return True
|
||||
|
||||
r_path = os.path.realpath(path)
|
||||
if r_path != a_path:
|
||||
debug("Binary should not be a symlink")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def valid_variable_name(var):
|
||||
'''Validate variable name'''
|
||||
if re.search(r'[a-zA-Z0-9_]+$', var):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def valid_path(path):
|
||||
'''Valid path'''
|
||||
# No relative paths
|
||||
m = "Invalid path: %s" % (path)
|
||||
if not path.startswith('/'):
|
||||
debug("%s (relative)" % (m))
|
||||
return False
|
||||
|
||||
try:
|
||||
os.path.normpath(path)
|
||||
except Exception:
|
||||
debug("%s (could not normalize)" % (m))
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_directory_contents(path):
|
||||
'''Find contents of the given directory'''
|
||||
if not valid_path(path):
|
||||
return None
|
||||
|
||||
files = []
|
||||
for f in glob.glob(path + "/*"):
|
||||
files.append(f)
|
||||
|
||||
files.sort()
|
||||
return files
|
||||
|
||||
def open_file_read(path):
|
||||
'''Open specified file read-only'''
|
||||
try:
|
||||
orig = codecs.open(path, 'r', "UTF-8")
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
return orig
|
||||
|
||||
|
||||
def verify_policy(policy):
|
||||
'''Verify policy compiles'''
|
||||
exe = "/sbin/apparmor_parser"
|
||||
if not os.path.exists(exe):
|
||||
rc, exe = cmd(['which', 'apparmor_parser'])
|
||||
if rc != 0:
|
||||
warn("Could not find apparmor_parser. Skipping verify")
|
||||
return True
|
||||
|
||||
fn = ""
|
||||
# if policy starts with '/' and is one line, assume it is a path
|
||||
if len(policy.splitlines()) == 1 and valid_path(policy):
|
||||
fn = policy
|
||||
else:
|
||||
f, fn = tempfile.mkstemp(prefix='aa-easyprof')
|
||||
os.write(f, policy)
|
||||
os.close(f)
|
||||
|
||||
rc, out = cmd([exe, '-p', fn])
|
||||
os.unlink(fn)
|
||||
if rc == 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
#
|
||||
# End utility functions
|
||||
#
|
||||
|
||||
|
||||
class AppArmorEasyProfile:
|
||||
'''Easy profile class'''
|
||||
def __init__(self, binary, opt):
|
||||
self.conffile = "/etc/apparmor/easyprof.conf"
|
||||
if opt.conffile:
|
||||
self.conffile = os.path.abspath(opt.conffile)
|
||||
|
||||
self.dirs = dict()
|
||||
if os.path.isfile(self.conffile):
|
||||
self._get_defaults()
|
||||
|
||||
if opt.templates_dir and os.path.isdir(opt.templates_dir):
|
||||
self.dirs['templates'] = os.path.abspath(opt.templates_dir)
|
||||
elif not opt.templates_dir and \
|
||||
opt.template and \
|
||||
os.path.isfile(opt.template) and \
|
||||
valid_path(opt.template):
|
||||
# If we specified the template and it is an absolute path, just set
|
||||
# the templates directory to the parent of the template so we don't
|
||||
# have to require --template-dir with absolute paths.
|
||||
self.dirs['templates'] = os.path.abspath(os.path.dirname(opt.template))
|
||||
if opt.policy_groups_dir and os.path.isdir(opt.policy_groups_dir):
|
||||
self.dirs['policygroups'] = os.path.abspath(opt.policy_groups_dir)
|
||||
|
||||
if not self.dirs.has_key('templates'):
|
||||
raise AppArmorException("Could not find templates directory")
|
||||
if not self.dirs.has_key('policygroups'):
|
||||
raise AppArmorException("Could not find policygroups directory")
|
||||
|
||||
self.aa_topdir = "/etc/apparmor.d"
|
||||
|
||||
self.binary = binary
|
||||
if binary != None:
|
||||
if not valid_binary_path(binary):
|
||||
raise AppArmorException("Invalid path for binary: '%s'" % binary)
|
||||
|
||||
self.set_template(opt.template)
|
||||
self.set_policygroup(opt.policy_groups)
|
||||
if opt.name:
|
||||
self.set_name(opt.name)
|
||||
elif self.binary != None:
|
||||
self.set_name(self.binary)
|
||||
|
||||
self.templates = get_directory_contents(self.dirs['templates'])
|
||||
self.policy_groups = get_directory_contents(self.dirs['policygroups'])
|
||||
|
||||
def _get_defaults(self):
|
||||
'''Read in defaults from configuration'''
|
||||
if not os.path.exists(self.conffile):
|
||||
raise AppArmorException("Could not find '%s'" % self.conffile)
|
||||
|
||||
# Read in the configuration
|
||||
f = open_file_read(self.conffile)
|
||||
|
||||
pat = re.compile(r'^\w+=".*"?')
|
||||
for line in f:
|
||||
if not pat.search(line):
|
||||
continue
|
||||
if line.startswith("POLICYGROUPS_DIR="):
|
||||
d = re.split(r'=', line.strip())[1].strip('["\']')
|
||||
self.dirs['policygroups'] = d
|
||||
elif line.startswith("TEMPLATES_DIR="):
|
||||
d = re.split(r'=', line.strip())[1].strip('["\']')
|
||||
self.dirs['templates'] = d
|
||||
f.close()
|
||||
|
||||
keys = self.dirs.keys()
|
||||
if 'templates' not in keys:
|
||||
raise AppArmorException("Could not find TEMPLATES_DIR in '%s'" % self.conffile)
|
||||
if 'policygroups' not in keys:
|
||||
raise AppArmorException("Could not find POLICYGROUPS_DIR in '%s'" % self.conffile)
|
||||
|
||||
for k in self.dirs.keys():
|
||||
if not os.path.isdir(self.dirs[k]):
|
||||
raise AppArmorException("Could not find '%s'" % self.dirs[k])
|
||||
|
||||
def set_name(self, name):
|
||||
'''Set name of policy'''
|
||||
self.name = name
|
||||
|
||||
def get_template(self):
|
||||
'''Get contents of current template'''
|
||||
return open(self.template).read()
|
||||
|
||||
def set_template(self, template):
|
||||
'''Set current template'''
|
||||
self.template = template
|
||||
if not template.startswith('/'):
|
||||
self.template = os.path.join(self.dirs['templates'], template)
|
||||
if not os.path.exists(self.template):
|
||||
raise AppArmorException('%s does not exist' % (self.template))
|
||||
|
||||
def get_templates(self):
|
||||
'''Get list of all available templates by filename'''
|
||||
return self.templates
|
||||
|
||||
def get_policygroup(self, policygroup):
|
||||
'''Get contents of specific policygroup'''
|
||||
p = policygroup
|
||||
if not p.startswith('/'):
|
||||
p = os.path.join(self.dirs['policygroups'], p)
|
||||
if self.policy_groups == None or not p in self.policy_groups:
|
||||
raise AppArmorException("Policy group '%s' does not exist" % p)
|
||||
return open(p).read()
|
||||
|
||||
def set_policygroup(self, policygroups):
|
||||
'''Set policygroups'''
|
||||
self.policy_groups = []
|
||||
if policygroups != None:
|
||||
for p in policygroups.split(','):
|
||||
if not p.startswith('/'):
|
||||
p = os.path.join(self.dirs['policygroups'], p)
|
||||
if not os.path.exists(p):
|
||||
raise AppArmorException('%s does not exist' % (p))
|
||||
self.policy_groups.append(p)
|
||||
|
||||
def get_policy_groups(self):
|
||||
'''Get list of all policy groups by filename'''
|
||||
return self.policy_groups
|
||||
|
||||
def gen_abstraction_rule(self, abstraction):
|
||||
'''Generate an abstraction rule'''
|
||||
p = os.path.join(self.aa_topdir, "abstractions", abstraction)
|
||||
if not os.path.exists(p):
|
||||
raise AppArmorException("%s does not exist" % p)
|
||||
return "#include <abstractions/%s>" % abstraction
|
||||
|
||||
def gen_variable_declaration(self, dec):
|
||||
'''Generate a variable declaration'''
|
||||
if not re.search(r'^@\{[a-zA-Z_]+\}=.+', dec):
|
||||
raise AppArmorException("Invalid variable declaration '%s'" % dec)
|
||||
return dec
|
||||
|
||||
def gen_path_rule(self, path, access):
|
||||
rule = []
|
||||
if not path.startswith('/') and not path.startswith('@'):
|
||||
raise AppArmorException("'%s' should not be relative path" % path)
|
||||
|
||||
owner = ""
|
||||
if path.startswith('/home/') or path.startswith("@{HOME"):
|
||||
owner = "owner "
|
||||
|
||||
if path.endswith('/'):
|
||||
rule.append("%s %s," % (path, access))
|
||||
rule.append("%s%s** %s," % (owner, path, access))
|
||||
elif path.endswith('/**') or path.endswith('/*'):
|
||||
rule.append("%s %s," % (os.path.dirname(path), access))
|
||||
rule.append("%s%s %s," % (owner, path, access))
|
||||
else:
|
||||
rule.append("%s%s %s," % (owner, path, access))
|
||||
|
||||
return rule
|
||||
|
||||
|
||||
def gen_policy(self, name, binary, template_var=[], abstractions=None, policy_groups=None, read_path=[], write_path=[], author=None, comment=None, copyright=None):
|
||||
def find_prefix(t, s):
|
||||
'''Calculate whitespace prefix based on occurrence of s in t'''
|
||||
pat = re.compile(r'^ *%s' % s)
|
||||
p = ""
|
||||
for line in t.splitlines():
|
||||
if pat.match(line):
|
||||
p = " " * (len(line) - len(line.lstrip()))
|
||||
break
|
||||
return p
|
||||
|
||||
policy = self.get_template()
|
||||
if '###ENDUSAGE###' in policy:
|
||||
found = False
|
||||
tmp = ""
|
||||
for line in policy.splitlines():
|
||||
if not found:
|
||||
if line.startswith('###ENDUSAGE###'):
|
||||
found = True
|
||||
continue
|
||||
tmp += line + "\n"
|
||||
policy = tmp
|
||||
|
||||
# Fill-in profile name and binary
|
||||
policy = re.sub(r'###NAME###', name, policy)
|
||||
policy = re.sub(r'###BINARY###', binary, policy)
|
||||
|
||||
# Fill-in various comment fields
|
||||
if comment != None:
|
||||
policy = re.sub(r'###COMMENT###', "Comment: %s" % comment, policy)
|
||||
|
||||
if author != None:
|
||||
policy = re.sub(r'###AUTHOR###', "Author: %s" % author, policy)
|
||||
|
||||
if copyright != None:
|
||||
policy = re.sub(r'###COPYRIGHT###', "Copyright: %s" % copyright, policy)
|
||||
|
||||
# Fill-in rules and variables with proper indenting
|
||||
search = '###ABSTRACTIONS###'
|
||||
prefix = find_prefix(policy, search)
|
||||
s = "%s# No abstractions specified" % prefix
|
||||
if abstractions != None:
|
||||
s = "%s# Specified abstractions" % (prefix)
|
||||
for i in abstractions.split(','):
|
||||
s += "\n%s%s" % (prefix, self.gen_abstraction_rule(i))
|
||||
policy = re.sub(r' *%s' % search, s, policy)
|
||||
|
||||
search = '###POLICYGROUPS###'
|
||||
prefix = find_prefix(policy, search)
|
||||
s = "%s# No policy groups specified" % prefix
|
||||
if policy_groups != None:
|
||||
s = "%s# Rules specified via policy groups" % (prefix)
|
||||
for i in policy_groups.split(','):
|
||||
for line in self.get_policygroup(i).splitlines():
|
||||
s += "\n%s%s" % (prefix, line)
|
||||
if i != policy_groups.split(',')[-1]:
|
||||
s += "\n"
|
||||
policy = re.sub(r' *%s' % search, s, policy)
|
||||
|
||||
search = '###VAR###'
|
||||
prefix = find_prefix(policy, search)
|
||||
s = "%s# No template variables specified" % prefix
|
||||
if len(template_var) > 0:
|
||||
s = "%s# Specified profile variables" % (prefix)
|
||||
for i in template_var:
|
||||
s += "\n%s%s" % (prefix, self.gen_variable_declaration(i))
|
||||
policy = re.sub(r' *%s' % search, s, policy)
|
||||
|
||||
search = '###READS###'
|
||||
prefix = find_prefix(policy, search)
|
||||
s = "%s# No read paths specified" % prefix
|
||||
if len(read_path) > 0:
|
||||
s = "%s# Specified read permissions" % (prefix)
|
||||
for i in read_path:
|
||||
for r in self.gen_path_rule(i, 'r'):
|
||||
s += "\n%s%s" % (prefix, r)
|
||||
policy = re.sub(r' *%s' % search, s, policy)
|
||||
|
||||
search = '###WRITES###'
|
||||
prefix = find_prefix(policy, search)
|
||||
s = "%s# No write paths specified" % prefix
|
||||
if len(write_path) > 0:
|
||||
s = "%s# Specified write permissions" % (prefix)
|
||||
for i in write_path:
|
||||
for r in self.gen_path_rule(i, 'rwk'):
|
||||
s += "\n%s%s" % (prefix, r)
|
||||
policy = re.sub(r' *%s' % search, s, policy)
|
||||
|
||||
if not verify_policy(policy):
|
||||
debug("\n" + policy)
|
||||
raise AppArmorException("Invalid policy")
|
||||
|
||||
return policy
|
||||
|
||||
def print_basefilenames(files):
|
||||
for i in files:
|
||||
print "%s" % (os.path.basename(i))
|
||||
|
||||
def print_files(files):
|
||||
for i in files:
|
||||
print open(i).read()
|
||||
|
||||
def parse_args(args=None):
|
||||
'''Parse arguments'''
|
||||
global DEBUGGING
|
||||
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option("-c", "--config-file",
|
||||
dest="conffile",
|
||||
help="Use alternate configuration file",
|
||||
metavar="FILE")
|
||||
parser.add_option("-d", "--debug",
|
||||
help="Show debugging output",
|
||||
action='store_true',
|
||||
default=False)
|
||||
parser.add_option("-t", "--template",
|
||||
dest="template",
|
||||
help="Use non-default policy template",
|
||||
metavar="TEMPLATE",
|
||||
default='default')
|
||||
parser.add_option("--list-templates",
|
||||
help="List available templates",
|
||||
action='store_true',
|
||||
default=False)
|
||||
parser.add_option("--templates-dir",
|
||||
dest="templates_dir",
|
||||
help="Use non-default templates directory",
|
||||
metavar="DIR")
|
||||
parser.add_option("--show-template",
|
||||
help="Show specified template",
|
||||
action='store_true',
|
||||
default=False)
|
||||
parser.add_option("-p", "--policy-groups",
|
||||
help="Comma-separated list of policy groups",
|
||||
metavar="POLICYGROUPS")
|
||||
parser.add_option("--list-policy-groups",
|
||||
help="List available policy groups",
|
||||
action='store_true',
|
||||
default=False)
|
||||
parser.add_option("--policy-groups-dir",
|
||||
dest="policy_groups_dir",
|
||||
help="Use non-default policy-groups directory",
|
||||
metavar="DIR")
|
||||
parser.add_option("--show-policy-group",
|
||||
help="Show specified policy groups",
|
||||
action='store_true',
|
||||
default=False)
|
||||
parser.add_option("-a", "--abstractions",
|
||||
dest="abstractions",
|
||||
help="Comma-separated list of abstractions",
|
||||
metavar="ABSTRACTIONS")
|
||||
parser.add_option("--read-path",
|
||||
dest="read_path",
|
||||
help="Path allowing owner reads",
|
||||
metavar="PATH",
|
||||
action="append")
|
||||
parser.add_option("--write-path",
|
||||
dest="write_path",
|
||||
help="Path allowing owner writes",
|
||||
metavar="PATH",
|
||||
action="append")
|
||||
parser.add_option("-n", "--name",
|
||||
dest="name",
|
||||
help="Name of policy",
|
||||
metavar="NAME")
|
||||
parser.add_option("--comment",
|
||||
dest="comment",
|
||||
help="Comment for policy",
|
||||
metavar="COMMENT")
|
||||
parser.add_option("--author",
|
||||
dest="author",
|
||||
help="Author of policy",
|
||||
metavar="COMMENT")
|
||||
parser.add_option("--copyright",
|
||||
dest="copyright",
|
||||
help="Copyright for policy",
|
||||
metavar="COMMENT")
|
||||
parser.add_option("--template-var",
|
||||
dest="template_var",
|
||||
help="Declare AppArmor variable",
|
||||
metavar="@{VARIABLE}=VALUE",
|
||||
action="append")
|
||||
|
||||
(my_opt, my_args) = parser.parse_args(args)
|
||||
if my_opt.debug:
|
||||
DEBUGGING = True
|
||||
return (my_opt, my_args)
|
||||
|
||||
def gen_policy_params(binary, opt):
|
||||
'''Generate parameters for gen_policy'''
|
||||
params = dict(binary=binary)
|
||||
if opt.name:
|
||||
params['name'] = opt.name
|
||||
else:
|
||||
params['name'] = os.path.basename(binary)
|
||||
if opt.template_var: # What about specified multiple times?
|
||||
params['template_var'] = opt.template_var
|
||||
if opt.abstractions:
|
||||
params['abstractions'] = opt.abstractions
|
||||
if opt.policy_groups:
|
||||
params['policy_groups'] = opt.policy_groups
|
||||
if opt.read_path:
|
||||
params['read_path'] = opt.read_path
|
||||
if opt.write_path:
|
||||
params['write_path'] = opt.write_path
|
||||
if opt.abstractions:
|
||||
params['abstractions'] = opt.abstractions
|
||||
if opt.comment:
|
||||
params['comment'] = opt.comment
|
||||
if opt.author:
|
||||
params['author'] = opt.author
|
||||
if opt.copyright:
|
||||
params['copyright'] = opt.copyright
|
||||
|
||||
return params
|
||||
|
44
utils/easyprof/README
Normal file
44
utils/easyprof/README
Normal file
|
@ -0,0 +1,44 @@
|
|||
AppArmor Easy Profiler
|
||||
----------------------
|
||||
aa-easyprof is a standalone CLI application which can also be imported into
|
||||
developer SDKs. See test/test-aa-easyprof.py for an example of how to import
|
||||
this into your SDK.
|
||||
|
||||
|
||||
Templates
|
||||
---------
|
||||
Any number of templates can be used. The user may specify one on the command
|
||||
line or use a system-wide template from /usr/share/apparmor/easyprof/templates.
|
||||
|
||||
Currently the combination of the user-application and the opt-application and
|
||||
user-application policygroups should achieve a working policy for Ubuntu's
|
||||
Application Review Board:
|
||||
- http://developer.ubuntu.com/publish/my-apps-packages/
|
||||
|
||||
Eg:
|
||||
$ aa-easyprof --template=user-application \
|
||||
--template-var="@{APPNAME}=foo" \
|
||||
--policy-groups=opt-application,user-application \
|
||||
/opt/foo/bin/foo
|
||||
|
||||
Testing
|
||||
-------
|
||||
Unit tests:
|
||||
$ ./test/test-aa-easyprof.py
|
||||
|
||||
In source manual testing:
|
||||
$ ./aa-easyprof --templates-dir=./easyprof/templates \
|
||||
--policy-groups-dir=./easyprof/policygroups \
|
||||
... \
|
||||
/opt/foo/bin/foo
|
||||
|
||||
Post-install manual testing:
|
||||
$ make DESTDIR=/tmp/test PERLDIR=/tmp/test/usr/share/perl5/Immunix install
|
||||
$ cd /tmp/test
|
||||
$ PYTHONPATH=/tmp/test/usr/local/.../dist-packages ./usr/bin/aa-easyprof \
|
||||
--templates-dir=/tmp/test/usr/share/apparmor/easyprof/templates \
|
||||
--policy-groups-dir=/tmp/test/usr/share/apparmor/easyprof/policygroups \
|
||||
/opt/bin/foo
|
||||
|
||||
(you may also adjust /tmp/test/etc/apparmor/easyprof.conf to avoid specifying
|
||||
--templates-dir and --policy-groups-dir).
|
5
utils/easyprof/easyprof.conf
Normal file
5
utils/easyprof/easyprof.conf
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Location of system policygroups
|
||||
POLICYGROUPS_DIR="/usr/share/apparmor/easyprof/policygroups"
|
||||
|
||||
# Location of system templates
|
||||
TEMPLATES_DIR="/usr/share/apparmor/easyprof/templates"
|
2
utils/easyprof/policygroups/networking
Normal file
2
utils/easyprof/policygroups/networking
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Policygroup to allow networking
|
||||
#include <abstractions/nameservice>
|
3
utils/easyprof/policygroups/opt-application
Normal file
3
utils/easyprof/policygroups/opt-application
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Policy group for applications installed in /opt
|
||||
/opt/@{APPNAME}/ r,
|
||||
/opt/@{APPNAME}/** mrk,
|
8
utils/easyprof/policygroups/user-application
Normal file
8
utils/easyprof/policygroups/user-application
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Policy group allowing various writes to standard directories in @{HOMEDIRS}
|
||||
#include <abstractions/xdg-desktop>
|
||||
owner @{HOMEDIRS}/.cache/@{APPNAME}/ rw,
|
||||
owner @{HOMEDIRS}/.cache/@{APPNAME}/** rwkl,
|
||||
owner @{HOMEDIRS}/.config/@{APPNAME}/ rw,
|
||||
owner @{HOMEDIRS}/.config/@{APPNAME}/** rwkl,
|
||||
owner @{HOMEDIRS}/.local/share/@{APPNAME}/ rw,
|
||||
owner @{HOMEDIRS}/.local/share/@{APPNAME}/** rwkl,
|
26
utils/easyprof/templates/default
Normal file
26
utils/easyprof/templates/default
Normal file
|
@ -0,0 +1,26 @@
|
|||
#
|
||||
# Example usage:
|
||||
# $ aa-easyprof --policy-groups=user-application /usr/bin/foo
|
||||
#
|
||||
###ENDUSAGE###
|
||||
# vim:syntax=apparmor
|
||||
# AppArmor policy for ###NAME###
|
||||
# ###AUTHOR###
|
||||
# ###COPYRIGHT###
|
||||
# ###COMMENT###
|
||||
|
||||
#include <tunables/global>
|
||||
|
||||
###VAR###
|
||||
|
||||
###BINARY### {
|
||||
#include <abstractions/base>
|
||||
|
||||
###ABSTRACTIONS###
|
||||
|
||||
###POLICYGROUPS###
|
||||
|
||||
###READS###
|
||||
|
||||
###WRITES###
|
||||
}
|
29
utils/easyprof/templates/user-application
Normal file
29
utils/easyprof/templates/user-application
Normal file
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# Example usage for a program named 'foo' which is installed in /opt/foo
|
||||
# $ aa-easyprof --template=user-application \
|
||||
# --template-var="@{APPNAME}=foo" \
|
||||
# --policy-groups=opt-application,user-application \
|
||||
# /opt/foo/bin/foo
|
||||
#
|
||||
###ENDUSAGE###
|
||||
# vim:syntax=apparmor
|
||||
# AppArmor policy for ###NAME###
|
||||
# ###AUTHOR###
|
||||
# ###COPYRIGHT###
|
||||
# ###COMMENT###
|
||||
|
||||
#include <tunables/global>
|
||||
|
||||
###VAR###
|
||||
|
||||
###BINARY### {
|
||||
#include <abstractions/base>
|
||||
|
||||
###ABSTRACTIONS###
|
||||
|
||||
###POLICYGROUPS###
|
||||
|
||||
###READS###
|
||||
|
||||
###WRITES###
|
||||
}
|
79
utils/python-tools-setup.py
Normal file
79
utils/python-tools-setup.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
# ----------------------------------------------------------------------
|
||||
# Copyright (c) 2012 Canonical Ltd.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, contact Canonical, Ltd.
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# Usage:
|
||||
# $ python ./python-tools-setup.py install --root=... --version=...
|
||||
#
|
||||
# Note: --version=... must be the last argument to this script
|
||||
#
|
||||
|
||||
from distutils.command.install import install as _install
|
||||
from distutils.core import setup
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
class Install(_install, object):
|
||||
'''Override distutils to install the files where we want them.'''
|
||||
def run(self):
|
||||
# Now byte-compile everything
|
||||
super(Install, self).run()
|
||||
|
||||
prefix = self.prefix
|
||||
if self.root != None:
|
||||
prefix = self.root
|
||||
|
||||
# Install scripts, configuration files and data
|
||||
scripts = ['/usr/bin/aa-easyprof']
|
||||
self.mkpath(prefix + os.path.dirname(scripts[0]))
|
||||
for s in scripts:
|
||||
self.copy_file(os.path.basename(s), prefix + s)
|
||||
|
||||
configs = ['easyprof/easyprof.conf']
|
||||
self.mkpath(prefix + "/etc/apparmor")
|
||||
for c in configs:
|
||||
self.copy_file(c, os.path.join(prefix + "/etc/apparmor", os.path.basename(c)))
|
||||
|
||||
data = ['easyprof/templates', 'easyprof/policygroups']
|
||||
self.mkpath(prefix + "/usr/share/apparmor/easyprof")
|
||||
for d in data:
|
||||
self.copy_tree(d, os.path.join(prefix + "/usr/share/apparmor/easyprof", os.path.basename(d)))
|
||||
|
||||
|
||||
if os.path.exists('staging'):
|
||||
shutil.rmtree('staging')
|
||||
shutil.copytree('apparmor', 'staging')
|
||||
|
||||
# Support the --version=... since this will be part of a Makefile
|
||||
version = "unknown-version"
|
||||
if "--version=" in sys.argv[-1]:
|
||||
version=sys.argv[-1].split('=')[1]
|
||||
sys.argv = sys.argv[0:-1]
|
||||
|
||||
setup (name='apparmor',
|
||||
version=version,
|
||||
description='Python libraries for AppArmor utilities',
|
||||
long_description='Python libraries for AppArmor utilities',
|
||||
author='AppArmor Developers',
|
||||
author_email='apparmor@lists.ubuntu.com',
|
||||
url='https://launchpad.net/apparmor',
|
||||
license='GPL-2',
|
||||
cmdclass={'install': Install},
|
||||
package_dir={'apparmor': 'staging'},
|
||||
py_modules=['apparmor.easyprof']
|
||||
)
|
||||
|
||||
shutil.rmtree('staging')
|
5
utils/test/easyprof.conf
Normal file
5
utils/test/easyprof.conf
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Location of system policygroups
|
||||
POLICYGROUPS_DIR="./policygroups"
|
||||
|
||||
# Location of system templates
|
||||
TEMPLATES_DIR="./templates"
|
882
utils/test/test-aa-easyprof.py
Normal file
882
utils/test/test-aa-easyprof.py
Normal file
|
@ -0,0 +1,882 @@
|
|||
#! /usr/bin/env python
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2011-2012 Canonical Ltd.
|
||||
#
|
||||
# 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 glob
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
topdir = None
|
||||
debugging = False
|
||||
|
||||
def recursive_rm(dirPath, contents_only=False):
|
||||
'''recursively remove directory'''
|
||||
names = os.listdir(dirPath)
|
||||
for name in names:
|
||||
path = os.path.join(dirPath, name)
|
||||
if os.path.islink(path) or not os.path.isdir(path):
|
||||
os.unlink(path)
|
||||
else:
|
||||
recursive_rm(path)
|
||||
if contents_only == False:
|
||||
os.rmdir(dirPath)
|
||||
|
||||
#
|
||||
# Our test class
|
||||
#
|
||||
class T(unittest.TestCase):
|
||||
def setUp(self):
|
||||
'''Setup for tests'''
|
||||
global topdir
|
||||
|
||||
self.tmpdir = tempfile.mkdtemp(prefix='test-aa-easyprof')
|
||||
|
||||
# Copy everything into place
|
||||
for d in ['easyprof/policygroups', 'easyprof/templates']:
|
||||
shutil.copytree(os.path.join(topdir, d), os.path.join(self.tmpdir, os.path.basename(d)))
|
||||
|
||||
# Create a test template
|
||||
self.test_template = "test-template"
|
||||
contents = '''# vim:syntax=apparmor
|
||||
# %s
|
||||
# AppArmor policy for ###NAME###
|
||||
# ###AUTHOR###
|
||||
# ###COPYRIGHT###
|
||||
# ###COMMENT###
|
||||
|
||||
#include <tunables/global>
|
||||
|
||||
###VAR###
|
||||
|
||||
###BINARY### {
|
||||
#include <abstractions/base>
|
||||
|
||||
###ABSTRACTIONS###
|
||||
|
||||
###POLICYGROUPS###
|
||||
|
||||
###READS###
|
||||
|
||||
###WRITES###
|
||||
}
|
||||
|
||||
''' % (self.test_template)
|
||||
open(os.path.join(self.tmpdir, 'templates', self.test_template), 'w').write(contents)
|
||||
|
||||
# Create a test policygroup
|
||||
self.test_policygroup = "test-policygroup"
|
||||
contents = '''
|
||||
# %s
|
||||
#include <abstractions/gnome>
|
||||
#include <abstractions/nameservice>
|
||||
''' % (self.test_policygroup)
|
||||
open(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), 'w').write(contents)
|
||||
|
||||
# setup our conffile
|
||||
self.conffile = os.path.join(self.tmpdir, 'easyprof.conf')
|
||||
contents = '''
|
||||
POLICYGROUPS_DIR="%s/policygroups"
|
||||
TEMPLATES_DIR="%s/templates"
|
||||
''' % (self.tmpdir, self.tmpdir)
|
||||
open(self.conffile, 'w').write(contents)
|
||||
|
||||
self.binary = "/opt/bin/foo"
|
||||
self.full_args = ['-c', self.conffile, self.binary]
|
||||
|
||||
if debugging:
|
||||
self.full_args.append('-d')
|
||||
|
||||
(self.options, self.args) = easyprof.parse_args(self.full_args + [self.binary])
|
||||
|
||||
def tearDown(self):
|
||||
'''Teardown for tests'''
|
||||
if os.path.exists(self.tmpdir):
|
||||
recursive_rm(self.tmpdir)
|
||||
|
||||
#
|
||||
# config file tests
|
||||
#
|
||||
def test_configuration_file_p_invalid(self):
|
||||
'''Test config parsing (invalid POLICYGROUPS_DIR)'''
|
||||
contents = '''
|
||||
POLICYGROUPS_DIR=
|
||||
TEMPLATES_DIR="%s/templates"
|
||||
''' % (self.tmpdir)
|
||||
|
||||
open(self.conffile, 'w').write(contents)
|
||||
try:
|
||||
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
raise Exception ("File should have been invalid")
|
||||
|
||||
def test_configuration_file_p_empty(self):
|
||||
'''Test config parsing (empty POLICYGROUPS_DIR)'''
|
||||
contents = '''
|
||||
POLICYGROUPS_DIR="%s"
|
||||
TEMPLATES_DIR="%s/templates"
|
||||
''' % ('', self.tmpdir)
|
||||
|
||||
open(self.conffile, 'w').write(contents)
|
||||
try:
|
||||
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
raise Exception ("File should have been invalid")
|
||||
|
||||
def test_configuration_file_p_nonexistent(self):
|
||||
'''Test config parsing (nonexistent POLICYGROUPS_DIR)'''
|
||||
contents = '''
|
||||
POLICYGROUPS_DIR="%s/policygroups"
|
||||
TEMPLATES_DIR="%s/templates"
|
||||
''' % ('/nonexistent', self.tmpdir)
|
||||
|
||||
open(self.conffile, 'w').write(contents)
|
||||
try:
|
||||
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
raise Exception ("File should have been invalid")
|
||||
|
||||
def test_policygroups_dir_relative(self):
|
||||
'''Test --policy-groups-dir (relative DIR)'''
|
||||
os.chdir(self.tmpdir)
|
||||
rel = os.path.join(self.tmpdir, 'relative')
|
||||
os.mkdir(rel)
|
||||
shutil.copy(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), os.path.join(rel, self.test_policygroup))
|
||||
|
||||
args = self.full_args
|
||||
args += ['--policy-groups-dir', './relative', '--show-policy-group', '--policy-groups=%s' % self.test_policygroup]
|
||||
(self.options, self.args) = easyprof.parse_args(args)
|
||||
easyp = easyprof.AppArmorEasyProfile(self.binary, self.options)
|
||||
|
||||
# no fallback
|
||||
self.assertTrue(easyp.dirs['policygroups'] == rel, "Not using specified --policy-groups-dir")
|
||||
self.assertFalse(easyp.get_policy_groups() == None, "Could not find policy-groups")
|
||||
|
||||
def test_policygroups_dir_nonexistent(self):
|
||||
'''Test --policy-groups-dir (nonexistent DIR)'''
|
||||
os.chdir(self.tmpdir)
|
||||
rel = os.path.join(self.tmpdir, 'nonexistent')
|
||||
|
||||
args = self.full_args
|
||||
args += ['--policy-groups-dir', rel, '--show-policy-group', '--policy-groups=%s' % self.test_policygroup]
|
||||
(self.options, self.args) = easyprof.parse_args(args)
|
||||
easyp = easyprof.AppArmorEasyProfile(self.binary, self.options)
|
||||
|
||||
# test if using fallback
|
||||
self.assertFalse(easyp.dirs['policygroups'] == rel, "Using nonexistent --policy-groups-dir")
|
||||
|
||||
# test fallback
|
||||
self.assertTrue(easyp.get_policy_groups() != None, "Found policy-groups when shouldn't have")
|
||||
|
||||
def test_policygroups_dir_valid(self):
|
||||
'''Test --policy-groups-dir (valid DIR)'''
|
||||
os.chdir(self.tmpdir)
|
||||
valid = os.path.join(self.tmpdir, 'valid')
|
||||
os.mkdir(valid)
|
||||
shutil.copy(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), os.path.join(valid, self.test_policygroup))
|
||||
|
||||
args = self.full_args
|
||||
args += ['--policy-groups-dir', valid, '--show-policy-group', '--policy-groups=%s' % self.test_policygroup]
|
||||
(self.options, self.args) = easyprof.parse_args(args)
|
||||
easyp = easyprof.AppArmorEasyProfile(self.binary, self.options)
|
||||
|
||||
# no fallback
|
||||
self.assertTrue(easyp.dirs['policygroups'] == valid, "Not using specified --policy-groups-dir")
|
||||
self.assertFalse(easyp.get_policy_groups() == None, "Could not find policy-groups")
|
||||
|
||||
def test_configuration_file_t_invalid(self):
|
||||
'''Test config parsing (invalid TEMPLATES_DIR)'''
|
||||
contents = '''
|
||||
TEMPLATES_DIR=
|
||||
POLICYGROUPS_DIR="%s/templates"
|
||||
''' % (self.tmpdir)
|
||||
|
||||
open(self.conffile, 'w').write(contents)
|
||||
try:
|
||||
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
raise Exception ("File should have been invalid")
|
||||
|
||||
def test_configuration_file_t_empty(self):
|
||||
'''Test config parsing (empty TEMPLATES_DIR)'''
|
||||
contents = '''
|
||||
TEMPLATES_DIR="%s"
|
||||
POLICYGROUPS_DIR="%s/templates"
|
||||
''' % ('', self.tmpdir)
|
||||
|
||||
open(self.conffile, 'w').write(contents)
|
||||
try:
|
||||
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
raise Exception ("File should have been invalid")
|
||||
|
||||
def test_configuration_file_t_nonexistent(self):
|
||||
'''Test config parsing (nonexistent TEMPLATES_DIR)'''
|
||||
contents = '''
|
||||
TEMPLATES_DIR="%s/policygroups"
|
||||
POLICYGROUPS_DIR="%s/templates"
|
||||
''' % ('/nonexistent', self.tmpdir)
|
||||
|
||||
open(self.conffile, 'w').write(contents)
|
||||
try:
|
||||
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
raise Exception ("File should have been invalid")
|
||||
|
||||
def test_templates_dir_relative(self):
|
||||
'''Test --templates-dir (relative DIR)'''
|
||||
os.chdir(self.tmpdir)
|
||||
rel = os.path.join(self.tmpdir, 'relative')
|
||||
os.mkdir(rel)
|
||||
shutil.copy(os.path.join(self.tmpdir, 'templates', self.test_template), os.path.join(rel, self.test_template))
|
||||
|
||||
args = self.full_args
|
||||
args += ['--templates-dir', './relative', '--show-template', '--template=%s' % self.test_template]
|
||||
(self.options, self.args) = easyprof.parse_args(args)
|
||||
easyp = easyprof.AppArmorEasyProfile(self.binary, self.options)
|
||||
|
||||
# no fallback
|
||||
self.assertTrue(easyp.dirs['templates'] == rel, "Not using specified --template-dir")
|
||||
self.assertFalse(easyp.get_templates() == None, "Could not find templates")
|
||||
|
||||
def test_templates_dir_nonexistent(self):
|
||||
'''Test --templates-dir (nonexistent DIR)'''
|
||||
os.chdir(self.tmpdir)
|
||||
rel = os.path.join(self.tmpdir, 'nonexistent')
|
||||
|
||||
args = self.full_args
|
||||
args += ['--templates-dir', rel, '--show-template', '--template=%s' % self.test_template]
|
||||
(self.options, self.args) = easyprof.parse_args(args)
|
||||
easyp = easyprof.AppArmorEasyProfile(self.binary, self.options)
|
||||
|
||||
# test if using fallback
|
||||
self.assertFalse(easyp.dirs['templates'] == rel, "Using nonexistent --template-dir")
|
||||
|
||||
# test fallback
|
||||
self.assertTrue(easyp.get_templates() != None, "Found templates when shouldn't have")
|
||||
|
||||
def test_templates_dir_valid(self):
|
||||
'''Test --templates-dir (valid DIR)'''
|
||||
os.chdir(self.tmpdir)
|
||||
valid = os.path.join(self.tmpdir, 'valid')
|
||||
os.mkdir(valid)
|
||||
shutil.copy(os.path.join(self.tmpdir, 'templates', self.test_template), os.path.join(valid, self.test_template))
|
||||
|
||||
args = self.full_args
|
||||
args += ['--templates-dir', valid, '--show-template', '--template=%s' % self.test_template]
|
||||
(self.options, self.args) = easyprof.parse_args(args)
|
||||
easyp = easyprof.AppArmorEasyProfile(self.binary, self.options)
|
||||
|
||||
# no fallback
|
||||
self.assertTrue(easyp.dirs['templates'] == valid, "Not using specified --template-dir")
|
||||
self.assertFalse(easyp.get_templates() == None, "Could not find templates")
|
||||
|
||||
#
|
||||
# Binary file tests
|
||||
#
|
||||
def test_binary(self):
|
||||
'''Test binary'''
|
||||
easyprof.AppArmorEasyProfile('/bin/ls', self.options)
|
||||
|
||||
def test_binary_nonexistent(self):
|
||||
'''Test binary (nonexistent)'''
|
||||
easyprof.AppArmorEasyProfile(os.path.join(self.tmpdir, 'nonexistent'), self.options)
|
||||
|
||||
def test_binary_relative(self):
|
||||
'''Test binary (relative)'''
|
||||
try:
|
||||
easyprof.AppArmorEasyProfile('./foo', self.options)
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
raise Exception ("Binary should have been invalid")
|
||||
|
||||
def test_binary_symlink(self):
|
||||
'''Test binary (symlink)'''
|
||||
exe = os.path.join(self.tmpdir, 'exe')
|
||||
open(exe, 'wa').close()
|
||||
symlink = exe + ".lnk"
|
||||
os.symlink(exe, symlink)
|
||||
|
||||
try:
|
||||
easyprof.AppArmorEasyProfile(symlink, self.options)
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
raise Exception ("Binary should have been invalid")
|
||||
|
||||
#
|
||||
# Templates tests
|
||||
#
|
||||
def test_templates_list(self):
|
||||
'''Test templates (list)'''
|
||||
args = self.full_args
|
||||
args.append('--list-templates')
|
||||
(self.options, self.args) = easyprof.parse_args(args)
|
||||
|
||||
easyp = easyprof.AppArmorEasyProfile(None, self.options)
|
||||
for i in easyp.get_templates():
|
||||
self.assertTrue(os.path.exists(i), "Could not find '%s'" % i)
|
||||
|
||||
def test_templates_show(self):
|
||||
'''Test templates (show)'''
|
||||
files = []
|
||||
for f in glob.glob("%s/templates/*" % self.tmpdir):
|
||||
files.append(f)
|
||||
|
||||
for f in files:
|
||||
args = self.full_args
|
||||
args += ['--show-template', '--template', f]
|
||||
(self.options, self.args) = easyprof.parse_args(args)
|
||||
easyp = easyprof.AppArmorEasyProfile(None, self.options)
|
||||
|
||||
path = os.path.join(easyp.dirs['templates'], f)
|
||||
self.assertTrue(os.path.exists(path), "Could not find '%s'" % path)
|
||||
open(path).read()
|
||||
|
||||
#
|
||||
# Policygroups tests
|
||||
#
|
||||
def test_policygroups_list(self):
|
||||
'''Test policygroups (list)'''
|
||||
args = self.full_args
|
||||
args.append('--list-policy-groups')
|
||||
(self.options, self.args) = easyprof.parse_args(args)
|
||||
|
||||
easyp = easyprof.AppArmorEasyProfile(None, self.options)
|
||||
for i in easyp.get_templates():
|
||||
self.assertTrue(os.path.exists(i), "Could not find '%s'" % i)
|
||||
|
||||
def test_policygroups_show(self):
|
||||
'''Test policygroups (show)'''
|
||||
files = []
|
||||
for f in glob.glob("%s/policygroups/*" % self.tmpdir):
|
||||
files.append(f)
|
||||
|
||||
for f in files:
|
||||
args = self.full_args
|
||||
args += ['--show-template', '--template', f]
|
||||
(self.options, self.args) = easyprof.parse_args(args)
|
||||
easyp = easyprof.AppArmorEasyProfile(None, self.options)
|
||||
|
||||
path = os.path.join(easyp.dirs['policygroups'], f)
|
||||
self.assertTrue(os.path.exists(path), "Could not find '%s'" % path)
|
||||
open(path).read()
|
||||
|
||||
#
|
||||
# Test genpolicy
|
||||
#
|
||||
def _gen_policy(self, name=None, template=None, extra_args=[]):
|
||||
'''Generate a policy'''
|
||||
# Build up our args
|
||||
args = self.full_args
|
||||
|
||||
if template == None:
|
||||
args.append('--template=%s' % self.test_template)
|
||||
else:
|
||||
args.append('--template=%s' % template)
|
||||
|
||||
if name != None:
|
||||
args.append('--name=%s' % name)
|
||||
|
||||
if len(extra_args) > 0:
|
||||
args += extra_args
|
||||
|
||||
args.append(self.binary)
|
||||
|
||||
# Now parse our args
|
||||
(self.options, self.args) = easyprof.parse_args(args)
|
||||
easyp = easyprof.AppArmorEasyProfile(self.binary, self.options)
|
||||
params = easyprof.gen_policy_params(self.binary, self.options)
|
||||
p = easyp.gen_policy(**params)
|
||||
|
||||
# We always need to check for these
|
||||
search_terms = [self.binary]
|
||||
if name != None:
|
||||
search_terms.append(name)
|
||||
|
||||
if template == None:
|
||||
search_terms.append(self.test_template)
|
||||
|
||||
for s in search_terms:
|
||||
self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
|
||||
|
||||
# ###NAME### should be replaced with self.binary or 'name'. Check for that
|
||||
inv_s = '###NAME###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
if debugging:
|
||||
print p
|
||||
|
||||
return p
|
||||
|
||||
def test_genpolicy_templates_abspath(self):
|
||||
'''Test genpolicy (abspath to template)'''
|
||||
# create a new template
|
||||
template = os.path.join(self.tmpdir, "test-abspath-template")
|
||||
shutil.copy(os.path.join(self.tmpdir, 'templates', self.test_template), template)
|
||||
contents = open(template).read()
|
||||
test_string = "#teststring"
|
||||
open(template, 'w').write(contents + "\n%s\n" % test_string)
|
||||
|
||||
p = self._gen_policy(template=template)
|
||||
|
||||
for s in [self.test_template, test_string]:
|
||||
self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
|
||||
|
||||
def test_genpolicy_templates_system(self):
|
||||
'''Test genpolicy (system template)'''
|
||||
self._gen_policy()
|
||||
|
||||
def test_genpolicy_templates_nonexistent(self):
|
||||
'''Test genpolicy (nonexistent template)'''
|
||||
try:
|
||||
self._gen_policy(template=os.path.join(self.tmpdir, "/nonexistent"))
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
raise Exception ("template should be invalid")
|
||||
|
||||
def test_genpolicy_name(self):
|
||||
'''Test genpolicy (name)'''
|
||||
self._gen_policy(name='test-foo')
|
||||
|
||||
def test_genpolicy_comment(self):
|
||||
'''Test genpolicy (comment)'''
|
||||
s = "test comment"
|
||||
p = self._gen_policy(extra_args=['--comment=%s' % s])
|
||||
self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
|
||||
inv_s = '###COMMENT###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_author(self):
|
||||
'''Test genpolicy (author)'''
|
||||
s = "Archibald Poindexter"
|
||||
p = self._gen_policy(extra_args=['--author=%s' % s])
|
||||
self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
|
||||
inv_s = '###AUTHOR###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_copyright(self):
|
||||
'''Test genpolicy (copyright)'''
|
||||
s = "2112/01/01"
|
||||
p = self._gen_policy(extra_args=['--copyright=%s' % s])
|
||||
self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
|
||||
inv_s = '###COPYRIGHT###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_abstractions(self):
|
||||
'''Test genpolicy (single abstraction)'''
|
||||
s = "nameservice"
|
||||
p = self._gen_policy(extra_args=['--abstractions=%s' % s])
|
||||
search = "#include <abstractions/%s>" % s
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###ABSTRACTIONS###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_abstractions_multiple(self):
|
||||
'''Test genpolicy (multiple abstractions)'''
|
||||
abstractions = "authentication,X,user-tmp"
|
||||
p = self._gen_policy(extra_args=['--abstractions=%s' % abstractions])
|
||||
for s in abstractions.split(','):
|
||||
search = "#include <abstractions/%s>" % s
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###ABSTRACTIONS###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_policygroups(self):
|
||||
'''Test genpolicy (single policygroup)'''
|
||||
groups = self.test_policygroup
|
||||
p = self._gen_policy(extra_args=['--policy-groups=%s' % groups])
|
||||
|
||||
for s in ['#include <abstractions/nameservice>', '#include <abstractions/gnome>']:
|
||||
self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
|
||||
inv_s = '###POLICYGROUPS###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_policygroups_multiple(self):
|
||||
'''Test genpolicy (multiple policygroups)'''
|
||||
test_policygroup2 = "test-policygroup2"
|
||||
contents = '''
|
||||
# %s
|
||||
#include <abstractions/kde>
|
||||
#include <abstractions/openssl>
|
||||
''' % (self.test_policygroup)
|
||||
open(os.path.join(self.tmpdir, 'policygroups', test_policygroup2), 'w').write(contents)
|
||||
|
||||
groups = "%s,%s" % (self.test_policygroup, test_policygroup2)
|
||||
p = self._gen_policy(extra_args=['--policy-groups=%s' % groups])
|
||||
|
||||
for s in ['#include <abstractions/nameservice>',
|
||||
'#include <abstractions/gnome>',
|
||||
'#include <abstractions/kde>',
|
||||
'#include <abstractions/openssl>']:
|
||||
self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
|
||||
inv_s = '###POLICYGROUPS###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_policygroups_nonexistent(self):
|
||||
'''Test genpolicy (nonexistent policygroup)'''
|
||||
try:
|
||||
self._gen_policy(extra_args=['--policy-groups=nonexistent'])
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
raise Exception ("policygroup should be invalid")
|
||||
|
||||
def test_genpolicy_readpath_file(self):
|
||||
'''Test genpolicy (read-path file)'''
|
||||
s = "/opt/test-foo"
|
||||
p = self._gen_policy(extra_args=['--read-path=%s' % s])
|
||||
search = "%s r," % s
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_readpath_home_file(self):
|
||||
'''Test genpolicy (read-path file in /home)'''
|
||||
s = "/home/*/test-foo"
|
||||
p = self._gen_policy(extra_args=['--read-path=%s' % s])
|
||||
search = "owner %s r," % s
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_readpath_homevar_file(self):
|
||||
'''Test genpolicy (read-path file in @{HOME})'''
|
||||
s = "@{HOME}/test-foo"
|
||||
p = self._gen_policy(extra_args=['--read-path=%s' % s])
|
||||
search = "owner %s r," % s
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_readpath_homedirs_file(self):
|
||||
'''Test genpolicy (read-path file in @{HOMEDIRS})'''
|
||||
s = "@{HOMEDIRS}/test-foo"
|
||||
p = self._gen_policy(extra_args=['--read-path=%s' % s])
|
||||
search = "owner %s r," % s
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_readpath_dir(self):
|
||||
'''Test genpolicy (read-path directory/)'''
|
||||
s = "/opt/test-foo-dir/"
|
||||
p = self._gen_policy(extra_args=['--read-path=%s' % s])
|
||||
search_terms = ["%s r," % s, "%s** r," % s]
|
||||
for search in search_terms:
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_readpath_dir_glob(self):
|
||||
'''Test genpolicy (read-path directory/*)'''
|
||||
s = "/opt/test-foo-dir/*"
|
||||
p = self._gen_policy(extra_args=['--read-path=%s' % s])
|
||||
search_terms = ["%s r," % os.path.dirname(s), "%s r," % s]
|
||||
for search in search_terms:
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_readpath_dir_glob_all(self):
|
||||
'''Test genpolicy (read-path directory/**)'''
|
||||
s = "/opt/test-foo-dir/**"
|
||||
p = self._gen_policy(extra_args=['--read-path=%s' % s])
|
||||
search_terms = ["%s r," % os.path.dirname(s), "%s r," % s]
|
||||
for search in search_terms:
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_readpath_multiple(self):
|
||||
'''Test genpolicy (read-path multiple)'''
|
||||
paths = ["/opt/test-foo",
|
||||
"/home/*/test-foo",
|
||||
"@{HOME}/test-foo",
|
||||
"@{HOMEDIRS}/test-foo",
|
||||
"/opt/test-foo-dir/",
|
||||
"/opt/test-foo-dir/*",
|
||||
"/opt/test-foo-dir/**"]
|
||||
args = []
|
||||
search_terms = []
|
||||
for s in paths:
|
||||
args.append('--read-path=%s' % s)
|
||||
# This mimics easyprof.gen_path_rule()
|
||||
owner = ""
|
||||
if s.startswith('/home/') or s.startswith("@{HOME"):
|
||||
owner = "owner "
|
||||
if s.endswith('/'):
|
||||
search_terms.append("%s r," % (s))
|
||||
search_terms.append("%s%s** r," % (owner, s))
|
||||
elif s.endswith('/**') or s.endswith('/*'):
|
||||
search_terms.append("%s r," % (os.path.dirname(s)))
|
||||
search_terms.append("%s%s r," % (owner, s))
|
||||
else:
|
||||
search_terms.append("%s%s r," % (owner, s))
|
||||
|
||||
p = self._gen_policy(extra_args=args)
|
||||
for search in search_terms:
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_readpath_bad(self):
|
||||
'''Test genpolicy (read-path bad)'''
|
||||
s = "bar"
|
||||
try:
|
||||
self._gen_policy(extra_args=['--read-path=%s' % s])
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
raise Exception ("read-path should be invalid")
|
||||
|
||||
def test_genpolicy_writepath_file(self):
|
||||
'''Test genpolicy (write-path file)'''
|
||||
s = "/opt/test-foo"
|
||||
p = self._gen_policy(extra_args=['--write-path=%s' % s])
|
||||
search = "%s rwk," % s
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_writepath_home_file(self):
|
||||
'''Test genpolicy (write-path file in /home)'''
|
||||
s = "/home/*/test-foo"
|
||||
p = self._gen_policy(extra_args=['--write-path=%s' % s])
|
||||
search = "owner %s rwk," % s
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_writepath_homevar_file(self):
|
||||
'''Test genpolicy (write-path file in @{HOME})'''
|
||||
s = "@{HOME}/test-foo"
|
||||
p = self._gen_policy(extra_args=['--write-path=%s' % s])
|
||||
search = "owner %s rwk," % s
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_writepath_homedirs_file(self):
|
||||
'''Test genpolicy (write-path file in @{HOMEDIRS})'''
|
||||
s = "@{HOMEDIRS}/test-foo"
|
||||
p = self._gen_policy(extra_args=['--write-path=%s' % s])
|
||||
search = "owner %s rwk," % s
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_writepath_dir(self):
|
||||
'''Test genpolicy (write-path directory/)'''
|
||||
s = "/opt/test-foo-dir/"
|
||||
p = self._gen_policy(extra_args=['--write-path=%s' % s])
|
||||
search_terms = ["%s rwk," % s, "%s** rwk," % s]
|
||||
for search in search_terms:
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_writepath_dir_glob(self):
|
||||
'''Test genpolicy (write-path directory/*)'''
|
||||
s = "/opt/test-foo-dir/*"
|
||||
p = self._gen_policy(extra_args=['--write-path=%s' % s])
|
||||
search_terms = ["%s rwk," % os.path.dirname(s), "%s rwk," % s]
|
||||
for search in search_terms:
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_writepath_dir_glob_all(self):
|
||||
'''Test genpolicy (write-path directory/**)'''
|
||||
s = "/opt/test-foo-dir/**"
|
||||
p = self._gen_policy(extra_args=['--write-path=%s' % s])
|
||||
search_terms = ["%s rwk," % os.path.dirname(s), "%s rwk," % s]
|
||||
for search in search_terms:
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_writepath_multiple(self):
|
||||
'''Test genpolicy (write-path multiple)'''
|
||||
paths = ["/opt/test-foo",
|
||||
"/home/*/test-foo",
|
||||
"@{HOME}/test-foo",
|
||||
"@{HOMEDIRS}/test-foo",
|
||||
"/opt/test-foo-dir/",
|
||||
"/opt/test-foo-dir/*",
|
||||
"/opt/test-foo-dir/**"]
|
||||
args = []
|
||||
search_terms = []
|
||||
for s in paths:
|
||||
args.append('--write-path=%s' % s)
|
||||
# This mimics easyprof.gen_path_rule()
|
||||
owner = ""
|
||||
if s.startswith('/home/') or s.startswith("@{HOME"):
|
||||
owner = "owner "
|
||||
if s.endswith('/'):
|
||||
search_terms.append("%s rwk," % (s))
|
||||
search_terms.append("%s%s** rwk," % (owner, s))
|
||||
elif s.endswith('/**') or s.endswith('/*'):
|
||||
search_terms.append("%s rwk," % (os.path.dirname(s)))
|
||||
search_terms.append("%s%s rwk," % (owner, s))
|
||||
else:
|
||||
search_terms.append("%s%s rwk," % (owner, s))
|
||||
|
||||
p = self._gen_policy(extra_args=args)
|
||||
for search in search_terms:
|
||||
self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
|
||||
inv_s = '###READPATH###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_writepath_bad(self):
|
||||
'''Test genpolicy (write-path bad)'''
|
||||
s = "bar"
|
||||
try:
|
||||
self._gen_policy(extra_args=['--write-path=%s' % s])
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
raise Exception ("write-path should be invalid")
|
||||
|
||||
def test_genpolicy_templatevar(self):
|
||||
'''Test genpolicy (template-var single)'''
|
||||
s = "@{FOO}=bar"
|
||||
p = self._gen_policy(extra_args=['--template-var=%s' % s])
|
||||
self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
|
||||
inv_s = '###TEMPLATEVAR###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_templatevar_multiple(self):
|
||||
'''Test genpolicy (template-var multiple)'''
|
||||
variables = ["@{FOO}=bar", "@{BAR}=baz"]
|
||||
args = []
|
||||
for s in variables:
|
||||
args.append('--template-var=%s' % s)
|
||||
|
||||
p = self._gen_policy(extra_args=args)
|
||||
for s in variables:
|
||||
self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
|
||||
inv_s = '###TEMPLATEVAR###'
|
||||
self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))
|
||||
|
||||
def test_genpolicy_templatevar_bad(self):
|
||||
'''Test genpolicy (template-var bad)'''
|
||||
s = "{FOO}=bar"
|
||||
try:
|
||||
self._gen_policy(extra_args=['--template-var=%s' % s])
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
raise Exception ("template-var should be invalid")
|
||||
|
||||
def test_genpolicy_invalid_template_policy(self):
|
||||
'''Test genpolicy (invalid template policy)'''
|
||||
# create a new template
|
||||
template = os.path.join(self.tmpdir, "test-invalid-template")
|
||||
shutil.copy(os.path.join(self.tmpdir, 'templates', self.test_template), template)
|
||||
contents = open(template).read()
|
||||
bad_pol = ""
|
||||
bad_string = "bzzzt"
|
||||
for line in contents.splitlines():
|
||||
if '}' in line:
|
||||
bad_pol += bad_string
|
||||
else:
|
||||
bad_pol += line
|
||||
bad_pol += "\n"
|
||||
open(template, 'w').write(bad_pol)
|
||||
try:
|
||||
self._gen_policy(template=template)
|
||||
except easyprof.AppArmorException:
|
||||
return
|
||||
except Exception:
|
||||
raise
|
||||
raise Exception ("policy should be invalid")
|
||||
|
||||
|
||||
#
|
||||
# End test class
|
||||
#
|
||||
|
||||
#
|
||||
# Main
|
||||
#
|
||||
if __name__ == '__main__':
|
||||
def cleanup(files):
|
||||
for f in files:
|
||||
if os.path.exists(f):
|
||||
os.unlink(f)
|
||||
|
||||
absfn = os.path.abspath(sys.argv[0])
|
||||
topdir = os.path.dirname(os.path.dirname(absfn))
|
||||
|
||||
if len(sys.argv) > 1 and (sys.argv[1] == '-d' or sys.argv[1] == '--debug'):
|
||||
debugging = True
|
||||
|
||||
created = []
|
||||
|
||||
# Create the necessary files to import aa-easyprof
|
||||
init = os.path.join(os.path.dirname(absfn), '__init__.py')
|
||||
if not os.path.exists(init):
|
||||
open(init, 'wa').close()
|
||||
created.append(init)
|
||||
|
||||
symlink = os.path.join(os.path.dirname(absfn), 'easyprof.py')
|
||||
if not os.path.exists(symlink):
|
||||
os.symlink(os.path.join(topdir, 'apparmor', 'easyprof.py'), symlink)
|
||||
created.append(symlink)
|
||||
created.append(symlink + 'c')
|
||||
|
||||
# Now that we have everything we need, import aa-easyprof
|
||||
import easyprof
|
||||
|
||||
# run the tests
|
||||
suite = unittest.TestSuite()
|
||||
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(T))
|
||||
rc = unittest.TextTestRunner(verbosity=2).run(suite)
|
||||
|
||||
cleanup(created)
|
||||
|
||||
if not rc.wasSuccessful():
|
||||
sys.exit(1)
|
Loading…
Add table
Reference in a new issue