mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-31 06:16:03 +00:00
- set filetype, instead of syntax, in vim modelines - replace filetype of subdomain with apparmor - move modelines in the first or last five lines of each file so that vim can recognize them
2666 lines
93 KiB
Python
Executable File
2666 lines
93 KiB
Python
Executable File
#! /usr/bin/python3
|
|
# ------------------------------------------------------------------
|
|
#
|
|
# Copyright (C) 2011-2015 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 json
|
|
import optparse
|
|
import os
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
import apparmor.easyprof as easyprof
|
|
from apparmor.common import AppArmorException
|
|
|
|
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 not contents_only:
|
|
os.rmdir(dirPath)
|
|
|
|
|
|
# From Lib/test/test_optparse.py from python 2.7.4
|
|
class InterceptedError(Exception):
|
|
def __init__(self,
|
|
error_message=None,
|
|
exit_status=None,
|
|
exit_message=None):
|
|
self.error_message = error_message
|
|
self.exit_status = exit_status
|
|
self.exit_message = exit_message
|
|
|
|
def __str__(self):
|
|
return self.error_message or self.exit_message or "intercepted error"
|
|
|
|
|
|
class InterceptingOptionParser(optparse.OptionParser):
|
|
def exit(self, status=0, msg=None):
|
|
raise InterceptedError(exit_status=status, exit_message=msg)
|
|
|
|
def error(self, msg):
|
|
raise InterceptedError(error_message=msg)
|
|
|
|
|
|
class Manifest:
|
|
def __init__(self, profile_name):
|
|
self.security = dict()
|
|
self.security['profiles'] = dict()
|
|
self.profile_name = profile_name
|
|
self.security['profiles'][self.profile_name] = dict()
|
|
|
|
def add_policygroups(self, policy_list):
|
|
self.security['profiles'][self.profile_name]['policy_groups'] = policy_list.split(",")
|
|
|
|
def add_author(self, author):
|
|
self.security['profiles'][self.profile_name]['author'] = author
|
|
|
|
def add_copyright(self, copyright):
|
|
self.security['profiles'][self.profile_name]['copyright'] = copyright
|
|
|
|
def add_comment(self, comment):
|
|
self.security['profiles'][self.profile_name]['comment'] = comment
|
|
|
|
def add_binary(self, binary):
|
|
self.security['profiles'][self.profile_name]['binary'] = binary
|
|
|
|
def add_template(self, template):
|
|
self.security['profiles'][self.profile_name]['template'] = template
|
|
|
|
def add_template_variable(self, name, value):
|
|
if 'template_variables' not in self.security['profiles'][self.profile_name]:
|
|
self.security['profiles'][self.profile_name]['template_variables'] = dict()
|
|
|
|
self.security['profiles'][self.profile_name]['template_variables'][name] = value
|
|
|
|
def emit_json(self, use_security_prefix=True):
|
|
manifest = dict()
|
|
manifest['security'] = self.security
|
|
if use_security_prefix:
|
|
dumpee = manifest
|
|
else:
|
|
dumpee = self.security
|
|
|
|
return json.dumps(dumpee, indent=2)
|
|
|
|
|
|
#
|
|
# Our test class
|
|
#
|
|
class T(unittest.TestCase):
|
|
|
|
# work around UsrMove
|
|
ls = os.path.realpath('/bin/ls')
|
|
|
|
def setUp(self):
|
|
"""Setup for tests"""
|
|
global topdir
|
|
|
|
self.tmpdir = os.path.realpath(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:ft=apparmor
|
|
# %s
|
|
# AppArmor policy for ###NAME###
|
|
# ###AUTHOR###
|
|
# ###COPYRIGHT###
|
|
# ###COMMENT###
|
|
|
|
#include <tunables/global>
|
|
|
|
###VAR###
|
|
|
|
###PROFILEATTACH### {
|
|
#include <abstractions/base>
|
|
|
|
###ABSTRACTIONS###
|
|
|
|
###POLICYGROUPS###
|
|
|
|
###READS###
|
|
|
|
###WRITES###
|
|
}
|
|
|
|
''' % (self.test_template,)
|
|
with open(os.path.join(self.tmpdir, 'templates', self.test_template), 'w') as f:
|
|
f.write(contents)
|
|
|
|
# Create a test policygroup
|
|
self.test_policygroup = "test-policygroup"
|
|
contents = '''
|
|
# {}
|
|
#include <abstractions/gnome>
|
|
#include <abstractions/nameservice>
|
|
'''.format(self.test_policygroup)
|
|
with open(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), 'w') as f:
|
|
f.write(contents)
|
|
|
|
# setup our conffile
|
|
self.conffile = os.path.join(self.tmpdir, 'easyprof.conf')
|
|
contents = '''
|
|
POLICYGROUPS_DIR="{}/policygroups"
|
|
TEMPLATES_DIR="{}/templates"
|
|
'''.format(self.tmpdir, self.tmpdir)
|
|
with open(self.conffile, 'w') as f:
|
|
f.write(contents)
|
|
|
|
self.binary = "/opt/bin/foo"
|
|
self.full_args = ['-c', self.conffile, self.binary]
|
|
|
|
# Check __AA_BASEDIR, which may be set by the Makefile, to see if
|
|
# we should use a non-default base directory path to find
|
|
# abstraction files
|
|
#
|
|
# NOTE: Individual tests can append another --base path to the
|
|
# args list and override a base path set here
|
|
base = os.getenv('__AA_BASEDIR')
|
|
if base:
|
|
self.full_args.append('--base=' + base)
|
|
|
|
# Check __AA_PARSER, which may be set by the Makefile, to see if
|
|
# we should use a non-default apparmor_parser path to verify
|
|
# policy
|
|
parser = os.getenv('__AA_PARSER')
|
|
if parser:
|
|
self.full_args.append('--parser=' + parser)
|
|
|
|
if debugging:
|
|
self.full_args.append('-d')
|
|
|
|
(self.options, self.args) = easyprof.parse_args(self.full_args + [self.binary])
|
|
|
|
# Now create some differently prefixed files in the include-dir
|
|
self.test_include_dir = os.path.join(self.tmpdir, 'include-dir')
|
|
os.mkdir(self.test_include_dir)
|
|
os.mkdir(os.path.join(self.test_include_dir, "templates"))
|
|
os.mkdir(os.path.join(self.test_include_dir, "policygroups"))
|
|
for d in ('policygroups', 'templates'):
|
|
for f in easyprof.get_directory_contents(os.path.join(
|
|
self.tmpdir, d)):
|
|
shutil.copy(f, os.path.join(self.test_include_dir, d,
|
|
"inc_" + os.path.basename(f)))
|
|
|
|
def tearDown(self):
|
|
"""Teardown for tests"""
|
|
if os.path.exists(self.tmpdir):
|
|
if debugging:
|
|
sys.stdout.write(self.tmpdir + "\n")
|
|
else:
|
|
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="{}/templates"
|
|
'''.format(self.tmpdir)
|
|
|
|
with open(self.conffile, 'w') as f:
|
|
f.write(contents)
|
|
try:
|
|
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
|
except AppArmorException:
|
|
return
|
|
|
|
raise Exception("File should have been invalid")
|
|
|
|
def test_configuration_file_p_empty(self):
|
|
"""Test config parsing (empty POLICYGROUPS_DIR)"""
|
|
contents = '''
|
|
POLICYGROUPS_DIR=""
|
|
TEMPLATES_DIR="{}/templates"
|
|
'''.format(self.tmpdir)
|
|
|
|
with open(self.conffile, 'w') as f:
|
|
f.write(contents)
|
|
try:
|
|
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
|
except AppArmorException:
|
|
return
|
|
|
|
raise Exception("File should have been invalid")
|
|
|
|
def test_configuration_file_p_nonexistent(self):
|
|
"""Test config parsing (nonexistent POLICYGROUPS_DIR)"""
|
|
contents = '''
|
|
POLICYGROUPS_DIR="/nonexistent/policygroups"
|
|
TEMPLATES_DIR="{}/templates"
|
|
'''.format(self.tmpdir)
|
|
|
|
with open(self.conffile, 'w') as f:
|
|
f.write(contents)
|
|
try:
|
|
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
|
except AppArmorException:
|
|
return
|
|
|
|
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=' + 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\n"
|
|
"Specified dir: {}\nActual dir: {}".format(rel, easyp.dirs['policygroups']))
|
|
self.assertFalse(easyp.get_policy_groups() is 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=' + 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() is not 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=' + 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() is None, "Could not find policy-groups")
|
|
|
|
def test_policygroups_dir_valid_with_vendor(self):
|
|
"""Test --policy-groups-dir (valid DIR with vendor)"""
|
|
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))
|
|
|
|
vendor = "ubuntu"
|
|
version = "1.0"
|
|
valid_distro = os.path.join(valid, vendor, version)
|
|
os.mkdir(os.path.join(valid, vendor))
|
|
os.mkdir(valid_distro)
|
|
shutil.copy(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), valid_distro)
|
|
|
|
args = self.full_args
|
|
args += ['--policy-groups-dir', valid, '--show-policy-group', '--policy-groups=' + self.test_policygroup]
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
easyp = easyprof.AppArmorEasyProfile(self.binary, self.options)
|
|
|
|
self.assertTrue(easyp.dirs['policygroups'] == valid, "Not using specified --policy-groups-dir")
|
|
self.assertFalse(easyp.get_policy_groups() is None, "Could not find policy-groups")
|
|
for f in easyp.get_policy_groups():
|
|
self.assertFalse(os.path.basename(f) == vendor, "Found '{}' in {}".format(vendor, f))
|
|
|
|
def test_configuration_file_t_invalid(self):
|
|
"""Test config parsing (invalid TEMPLATES_DIR)"""
|
|
contents = '''
|
|
TEMPLATES_DIR=
|
|
POLICYGROUPS_DIR="{}/templates"
|
|
'''.format(self.tmpdir)
|
|
|
|
with open(self.conffile, 'w') as f:
|
|
f.write(contents)
|
|
try:
|
|
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
|
except AppArmorException:
|
|
return
|
|
|
|
raise Exception("File should have been invalid")
|
|
|
|
def test_configuration_file_t_empty(self):
|
|
"""Test config parsing (empty TEMPLATES_DIR)"""
|
|
contents = '''
|
|
TEMPLATES_DIR=""
|
|
POLICYGROUPS_DIR="{}/templates"
|
|
'''.format(self.tmpdir)
|
|
|
|
with open(self.conffile, 'w') as f:
|
|
f.write(contents)
|
|
try:
|
|
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
|
except AppArmorException:
|
|
return
|
|
|
|
raise Exception("File should have been invalid")
|
|
|
|
def test_configuration_file_t_nonexistent(self):
|
|
"""Test config parsing (nonexistent TEMPLATES_DIR)"""
|
|
contents = '''
|
|
TEMPLATES_DIR="/nonexistent/policygroups"
|
|
POLICYGROUPS_DIR="{}/templates"
|
|
'''.format(self.tmpdir)
|
|
|
|
with open(self.conffile, 'w') as f:
|
|
f.write(contents)
|
|
try:
|
|
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
|
except AppArmorException:
|
|
return
|
|
|
|
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=' + 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\n"
|
|
"Specified dir: {}\nActual dir: {}".format(rel, easyp.dirs['templates']))
|
|
self.assertFalse(easyp.get_templates() is 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=' + 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() is not 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=' + 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() is None, "Could not find templates")
|
|
|
|
def test_templates_dir_valid_with_vendor(self):
|
|
"""Test --templates-dir (valid DIR with vendor)"""
|
|
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))
|
|
|
|
vendor = "ubuntu"
|
|
version = "1.0"
|
|
valid_distro = os.path.join(valid, vendor, version)
|
|
os.mkdir(os.path.join(valid, vendor))
|
|
os.mkdir(valid_distro)
|
|
shutil.copy(os.path.join(self.tmpdir, 'templates', self.test_template), valid_distro)
|
|
|
|
args = self.full_args
|
|
args += ['--templates-dir', valid, '--show-template', '--template=' + self.test_template]
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
easyp = easyprof.AppArmorEasyProfile(self.binary, self.options)
|
|
|
|
self.assertTrue(easyp.dirs['templates'] == valid, "Not using specified --template-dir")
|
|
self.assertFalse(easyp.get_templates() is None, "Could not find templates")
|
|
for f in easyp.get_templates():
|
|
self.assertFalse(os.path.basename(f) == vendor, "Found '{}' in {}".format(vendor, f))
|
|
|
|
#
|
|
# Binary file tests
|
|
#
|
|
def test_binary_without_profile_name(self):
|
|
"""Test binary (<binary> { })"""
|
|
easyprof.AppArmorEasyProfile(self.ls, self.options)
|
|
|
|
def test_binary_with_profile_name(self):
|
|
"""Test binary (profile <name> <binary> { })"""
|
|
args = self.full_args
|
|
args += ['--profile-name=some-profile-name']
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
easyprof.AppArmorEasyProfile(self.ls, self.options)
|
|
|
|
def test_binary_omitted_with_profile_name(self):
|
|
"""Test binary (profile <name> { })"""
|
|
args = self.full_args
|
|
args += ['--profile-name=some-profile-name']
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
easyprof.AppArmorEasyProfile(None, 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 AppArmorException:
|
|
return
|
|
raise Exception("Binary should have been invalid")
|
|
|
|
def test_binary_symlink(self):
|
|
"""Test binary (symlink)"""
|
|
exe = os.path.join(self.tmpdir, 'exe')
|
|
open(exe, 'a').close()
|
|
symlink = exe + ".lnk"
|
|
os.symlink(exe, symlink)
|
|
|
|
try:
|
|
easyprof.AppArmorEasyProfile(symlink, self.options)
|
|
except AppArmorException:
|
|
return
|
|
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 '{}'".format(i))
|
|
|
|
def test_templates_show(self):
|
|
"""Test templates (show)"""
|
|
files = glob.glob(self.tmpdir + "/templates/*")
|
|
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 '{}'".format(path))
|
|
with open(path) as fd:
|
|
fd.read()
|
|
|
|
def test_templates_list_include(self):
|
|
"""Test templates (list with --include-templates-dir)"""
|
|
args = self.full_args
|
|
args.append('--list-templates')
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
|
|
easyp = easyprof.AppArmorEasyProfile(None, self.options)
|
|
orig_templates = easyp.get_templates()
|
|
|
|
args = self.full_args
|
|
args.append('--list-templates')
|
|
args.append('--include-templates-dir='
|
|
+ os.path.join(self.test_include_dir, 'templates'))
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
|
|
easyp = easyprof.AppArmorEasyProfile(None, self.options)
|
|
inc_templates = easyp.get_templates()
|
|
self.assertTrue(len(inc_templates) == len(orig_templates) * 2,
|
|
"templates missing: {}".format(inc_templates))
|
|
|
|
for i in inc_templates:
|
|
self.assertTrue(os.path.exists(i), "Could not find '{}'".format(i))
|
|
|
|
def test_templates_show_include(self):
|
|
"""Test templates (show with --include-templates-dir)"""
|
|
files = glob.glob(self.test_include_dir + "/templates/*")
|
|
for f in files:
|
|
args = self.full_args
|
|
args += ['--show-template',
|
|
'--template', f,
|
|
'--include-templates-dir='
|
|
+ os.path.join(self.test_include_dir, 'templates')]
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
easyp = easyprof.AppArmorEasyProfile(None, self.options)
|
|
|
|
path = os.path.join(easyp.dirs['templates_include'], f)
|
|
self.assertTrue(os.path.exists(path), "Could not find '{}'".format(path))
|
|
with open(path) as fd:
|
|
fd.read()
|
|
|
|
bn = os.path.basename(f)
|
|
# setup() copies everything in the include prefixed with inc_
|
|
self.assertTrue(bn.startswith('inc_'),
|
|
"'{}' does not start with 'inc_'".format(bn))
|
|
|
|
#
|
|
# 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_policy_groups():
|
|
self.assertTrue(os.path.exists(i), "Could not find '{}'".format(i))
|
|
|
|
def test_policygroups_show(self):
|
|
"""Test policygroups (show)"""
|
|
files = glob.glob(self.tmpdir + "/policygroups/*")
|
|
for f in files:
|
|
args = self.full_args
|
|
args += ['--show-policy-group',
|
|
'--policy-groups', os.path.basename(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 '{}'".format(path))
|
|
with open(path) as fd:
|
|
fd.read()
|
|
|
|
def test_policygroups_list_include(self):
|
|
"""Test policygroups (list with --include-policy-groups-dir)"""
|
|
args = self.full_args
|
|
args.append('--list-policy-groups')
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
|
|
easyp = easyprof.AppArmorEasyProfile(None, self.options)
|
|
orig_policy_groups = easyp.get_policy_groups()
|
|
|
|
args = self.full_args
|
|
args.append('--list-policy-groups')
|
|
args.append('--include-policy-groups-dir='
|
|
+ os.path.join(self.test_include_dir, 'policygroups'))
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
|
|
easyp = easyprof.AppArmorEasyProfile(None, self.options)
|
|
inc_policy_groups = easyp.get_policy_groups()
|
|
self.assertTrue(len(inc_policy_groups) == len(orig_policy_groups) * 2,
|
|
"policy_groups missing: {}".format(inc_policy_groups))
|
|
|
|
for i in inc_policy_groups:
|
|
self.assertTrue(os.path.exists(i), "Could not find '{}'".format(i))
|
|
|
|
def test_policygroups_show_include(self):
|
|
"""Test policygroups (show with --include-policy-groups-dir)"""
|
|
files = glob.glob(self.test_include_dir + "/policygroups/*")
|
|
for f in files:
|
|
args = self.full_args
|
|
args += ['--show-policy-group',
|
|
'--policy-groups', os.path.basename(f),
|
|
'--include-policy-groups-dir='
|
|
+ os.path.join(self.test_include_dir, 'policygroups')]
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
easyp = easyprof.AppArmorEasyProfile(None, self.options)
|
|
|
|
path = os.path.join(easyp.dirs['policygroups_include'], f)
|
|
self.assertTrue(os.path.exists(path), "Could not find '{}'".format(path))
|
|
with open(path) as fd:
|
|
fd.read()
|
|
|
|
bn = os.path.basename(f)
|
|
# setup() copies everything in the include prefixed with inc_
|
|
self.assertTrue(bn.startswith('inc_'),
|
|
"'{}' does not start with 'inc_'".format(bn))
|
|
|
|
#
|
|
# Manifest file argument tests
|
|
#
|
|
def test_manifest_argument(self):
|
|
"""Test manifest argument"""
|
|
|
|
# setup our manifest
|
|
self.manifest = os.path.join(self.tmpdir, 'manifest.json')
|
|
contents = '''
|
|
{"security": {"domain.reverse.appname": {"name": "simple-app"}}}
|
|
'''
|
|
with open(self.manifest, 'w') as f:
|
|
f.write(contents)
|
|
|
|
args = self.full_args
|
|
args.extend(('--manifest', self.manifest))
|
|
easyprof.parse_args(args)
|
|
|
|
def _manifest_conflicts(self, opt, value):
|
|
"""Helper for conflicts tests"""
|
|
# setup our manifest
|
|
self.manifest = os.path.join(self.tmpdir, 'manifest.json')
|
|
contents = '''
|
|
{"security": {"domain.reverse.appname": {"binary": /nonexistent"}}}
|
|
'''
|
|
with open(self.manifest, 'w') as f:
|
|
f.write(contents)
|
|
|
|
# opt first
|
|
args = self.full_args
|
|
args.extend((opt, value, '--manifest', self.manifest))
|
|
raised = False
|
|
try:
|
|
easyprof.parse_args(args, InterceptingOptionParser())
|
|
except InterceptedError:
|
|
raised = True
|
|
|
|
self.assertTrue(raised, msg=opt + " and manifest arguments did not "
|
|
"raise a parse error")
|
|
|
|
# manifest first
|
|
args = self.full_args
|
|
args.extend(('--manifest', self.manifest, opt, value))
|
|
raised = False
|
|
try:
|
|
easyprof.parse_args(args, InterceptingOptionParser())
|
|
except InterceptedError:
|
|
raised = True
|
|
|
|
self.assertTrue(raised, msg=opt + " and manifest arguments did not "
|
|
"raise a parse error")
|
|
|
|
def test_manifest_conflicts_profilename(self):
|
|
"""Test manifest arg conflicts with profile_name arg"""
|
|
self._manifest_conflicts("--profile-name", "simple-app")
|
|
|
|
def test_manifest_conflicts_copyright(self):
|
|
"""Test manifest arg conflicts with copyright arg"""
|
|
self._manifest_conflicts("--copyright", "2013-01-01")
|
|
|
|
def test_manifest_conflicts_author(self):
|
|
"""Test manifest arg conflicts with author arg"""
|
|
self._manifest_conflicts("--author", "Foo Bar")
|
|
|
|
def test_manifest_conflicts_comment(self):
|
|
"""Test manifest arg conflicts with comment arg"""
|
|
self._manifest_conflicts("--comment", "some comment")
|
|
|
|
def test_manifest_conflicts_abstractions(self):
|
|
"""Test manifest arg conflicts with abstractions arg"""
|
|
self._manifest_conflicts("--abstractions", "base")
|
|
|
|
def test_manifest_conflicts_read_path(self):
|
|
"""Test manifest arg conflicts with read-path arg"""
|
|
self._manifest_conflicts("--read-path", "/etc/passwd")
|
|
|
|
def test_manifest_conflicts_write_path(self):
|
|
"""Test manifest arg conflicts with write-path arg"""
|
|
self._manifest_conflicts("--write-path", "/tmp/foo")
|
|
|
|
def test_manifest_conflicts_policy_groups(self):
|
|
"""Test manifest arg conflicts with policy-groups arg"""
|
|
self._manifest_conflicts("--policy-groups", "opt-application")
|
|
|
|
def test_manifest_conflicts_name(self):
|
|
"""Test manifest arg conflicts with name arg"""
|
|
self._manifest_conflicts("--name", "foo")
|
|
|
|
def test_manifest_conflicts_template_var(self):
|
|
"""Test manifest arg conflicts with template-var arg"""
|
|
self._manifest_conflicts("--template-var", "foo")
|
|
|
|
def test_manifest_conflicts_policy_version(self):
|
|
"""Test manifest arg conflicts with policy-version arg"""
|
|
self._manifest_conflicts("--policy-version", "1.0")
|
|
|
|
def test_manifest_conflicts_policy_vendor(self):
|
|
"""Test manifest arg conflicts with policy-vendor arg"""
|
|
self._manifest_conflicts("--policy-vendor", "somevendor")
|
|
|
|
#
|
|
# Test genpolicy
|
|
#
|
|
def _gen_policy(self, name=None, template=None, extra_args=None):
|
|
"""Generate a policy"""
|
|
# Build up our args
|
|
args = self.full_args
|
|
|
|
if template is None:
|
|
args.append('--template=' + self.test_template)
|
|
else:
|
|
args.append('--template=' + template)
|
|
|
|
if name is not None:
|
|
args.append('--name=' + name)
|
|
|
|
if extra_args:
|
|
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 is not None:
|
|
search_terms.append(name)
|
|
|
|
if template is None:
|
|
search_terms.append(self.test_template)
|
|
|
|
for s in search_terms:
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
|
|
# ###NAME### should be replaced with self.binary or 'name'. Check for that
|
|
inv_s = '###NAME###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
if debugging:
|
|
sys.stdout.write(p + "\n")
|
|
|
|
return p
|
|
|
|
def _gen_manifest_policy(self, manifest, use_security_prefix=True):
|
|
# Build up our args
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(binary, self.options) = easyprof.parse_manifest(manifest.emit_json(use_security_prefix), self.options)[0]
|
|
easyp = easyprof.AppArmorEasyProfile(binary, self.options)
|
|
params = easyprof.gen_policy_params(binary, self.options)
|
|
p = easyp.gen_policy(**params)
|
|
|
|
# ###NAME### should be replaced with self.binary or 'name'. Check for that
|
|
inv_s = '###NAME###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
if debugging:
|
|
sys.stdout.write(p + "\n")
|
|
|
|
return p
|
|
|
|
def test__is_safe(self):
|
|
"""Test _is_safe()"""
|
|
bad = (
|
|
"/../../../../etc/passwd",
|
|
"abstraction with spaces",
|
|
"semicolon;bad",
|
|
"bad\x00baz",
|
|
"foo/bar",
|
|
"foo'bar",
|
|
'foo"bar',
|
|
)
|
|
for s in bad:
|
|
self.assertFalse(easyprof._is_safe(s), "'{}' should be bad".format(s))
|
|
|
|
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)
|
|
with open(template) as f:
|
|
contents = f.read()
|
|
test_string = "#teststring"
|
|
with open(template, 'w') as f:
|
|
f.write(contents + "\n{}\n".format(test_string))
|
|
|
|
p = self._gen_policy(template=template)
|
|
|
|
for s in (self.test_template, test_string):
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(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 AppArmorException:
|
|
return
|
|
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])
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###COMMENT###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_genpolicy_author(self):
|
|
"""Test genpolicy (author)"""
|
|
s = "Archibald Poindexter"
|
|
p = self._gen_policy(extra_args=['--author=' + s])
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###AUTHOR###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_genpolicy_copyright(self):
|
|
"""Test genpolicy (copyright)"""
|
|
s = "2112/01/01"
|
|
p = self._gen_policy(extra_args=['--copyright=' + s])
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###COPYRIGHT###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_genpolicy_abstractions(self):
|
|
"""Test genpolicy (single abstraction)"""
|
|
s = "nameservice"
|
|
p = self._gen_policy(extra_args=['--abstractions=' + s])
|
|
search = "#include <abstractions/{}>".format(s)
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###ABSTRACTIONS###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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=' + abstractions])
|
|
for s in abstractions.split(','):
|
|
search = "#include <abstractions/{}>".format(s)
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###ABSTRACTIONS###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_genpolicy_abstractions_bad(self):
|
|
"""Test genpolicy (abstractions - bad values)"""
|
|
bad = (
|
|
"nonexistent",
|
|
"/../../../../etc/passwd",
|
|
"abstraction with spaces",
|
|
)
|
|
for s in bad:
|
|
try:
|
|
self._gen_policy(extra_args=['--abstractions=' + s])
|
|
except AppArmorException:
|
|
continue
|
|
raise Exception("abstraction '{}' should be invalid".format(s))
|
|
|
|
def _create_tmp_base_dir(self, prefix='', abstractions=(), tunables=()):
|
|
"""Create a temporary base dir layout"""
|
|
base_name = 'apparmor.d'
|
|
if prefix:
|
|
base_name = '{}-{}'.format(prefix, base_name)
|
|
base_dir = os.path.join(self.tmpdir, base_name)
|
|
abstractions_dir = os.path.join(base_dir, 'abstractions')
|
|
tunables_dir = os.path.join(base_dir, 'tunables')
|
|
|
|
os.mkdir(base_dir)
|
|
os.mkdir(abstractions_dir)
|
|
os.mkdir(tunables_dir)
|
|
|
|
for f in abstractions:
|
|
contents = '''
|
|
# Abstraction file for testing
|
|
/{} r,
|
|
'''.format(f)
|
|
with open(os.path.join(abstractions_dir, f), 'w') as fd:
|
|
fd.write(contents)
|
|
|
|
for f in tunables:
|
|
contents = '''
|
|
# Tunable file for testing
|
|
@{AA_TEST_%s}=foo
|
|
''' % (f,)
|
|
with open(os.path.join(tunables_dir, f), 'w') as fd:
|
|
fd.write(contents)
|
|
|
|
return base_dir
|
|
|
|
def test_genpolicy_abstractions_custom_base(self):
|
|
"""Test genpolicy (custom base dir)"""
|
|
abstraction = "custom-base-dir-test-abstraction"
|
|
# The default template #includes the base abstraction and global
|
|
# tunable so we need to create placeholders
|
|
base = self._create_tmp_base_dir(abstractions=['base', abstraction], tunables=['global'])
|
|
args = ['--abstractions=' + abstraction, '--base=' + base]
|
|
|
|
p = self._gen_policy(extra_args=args)
|
|
search = "#include <abstractions/{}>".format(abstraction)
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###ABSTRACTIONS###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_genpolicy_abstractions_custom_base_bad(self):
|
|
"""Test genpolicy (custom base dir - bad base dirs)"""
|
|
abstraction = "custom-base-dir-test-abstraction"
|
|
bad = [None, '/etc/apparmor.d', '/']
|
|
for base in bad:
|
|
try:
|
|
args = ['--abstractions=' + abstraction]
|
|
if base:
|
|
args.append('--base={}'.format(base))
|
|
self._gen_policy(extra_args=args)
|
|
except AppArmorException:
|
|
continue
|
|
raise Exception("abstraction '{}' should be invalid".format(abstraction))
|
|
|
|
def test_genpolicy_abstractions_custom_include(self):
|
|
"""Test genpolicy (custom include dir)"""
|
|
abstraction = "custom-include-dir-test-abstraction"
|
|
# No need to create placeholders for the base abstraction or global
|
|
# tunable since we're not adjusting the base directory
|
|
include = self._create_tmp_base_dir(abstractions=[abstraction])
|
|
args = ['--abstractions=' + abstraction, '--Include=' + include]
|
|
p = self._gen_policy(extra_args=args)
|
|
search = "#include <abstractions/{}>".format(abstraction)
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###ABSTRACTIONS###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_genpolicy_abstractions_custom_include_bad(self):
|
|
"""Test genpolicy (custom include dir - bad include dirs)"""
|
|
abstraction = "custom-include-dir-test-abstraction"
|
|
bad = [None, '/etc/apparmor.d', '/']
|
|
for include in bad:
|
|
try:
|
|
args = ['--abstractions=' + abstraction]
|
|
if include:
|
|
args.append('--Include={}'.format(include))
|
|
self._gen_policy(extra_args=args)
|
|
except AppArmorException:
|
|
continue
|
|
raise Exception("abstraction '{}' should be invalid".format(abstraction))
|
|
|
|
def test_genpolicy_profile_name_bad(self):
|
|
"""Test genpolicy (profile name - bad values)"""
|
|
bad = [
|
|
"/../../../../etc/passwd",
|
|
"../../../../etc/passwd",
|
|
"profile name with spaces",
|
|
]
|
|
for s in bad:
|
|
try:
|
|
self._gen_policy(extra_args=['--profile-name=' + s])
|
|
except AppArmorException:
|
|
continue
|
|
raise Exception("profile_name '{}' should be invalid".format(s))
|
|
|
|
def test_genpolicy_policy_group_bad(self):
|
|
"""Test genpolicy (policy group - bad values)"""
|
|
bad = [
|
|
"/../../../../etc/passwd",
|
|
"../../../../etc/passwd",
|
|
"profile name with spaces",
|
|
]
|
|
for s in bad:
|
|
try:
|
|
self._gen_policy(extra_args=['--policy-groups=' + s])
|
|
except AppArmorException:
|
|
continue
|
|
raise Exception("policy group '{}' should be invalid".format(s))
|
|
|
|
def test_genpolicy_policygroups(self):
|
|
"""Test genpolicy (single policygroup)"""
|
|
groups = self.test_policygroup
|
|
p = self._gen_policy(extra_args=['--policy-groups=' + groups])
|
|
|
|
for s in ('#include <abstractions/nameservice>', '#include <abstractions/gnome>'):
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###POLICYGROUPS###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_genpolicy_policygroups_multiple(self):
|
|
"""Test genpolicy (multiple policygroups)"""
|
|
test_policygroup2 = "test-policygroup2"
|
|
contents = '''
|
|
# {}
|
|
#include <abstractions/kde>
|
|
#include <abstractions/openssl>
|
|
'''.format(self.test_policygroup)
|
|
with open(os.path.join(self.tmpdir, 'policygroups', test_policygroup2), 'w') as f:
|
|
f.write(contents)
|
|
|
|
groups = "{},{}".format(self.test_policygroup, test_policygroup2)
|
|
p = self._gen_policy(extra_args=['--policy-groups=' + groups])
|
|
|
|
for s in ('#include <abstractions/nameservice>',
|
|
'#include <abstractions/gnome>',
|
|
'#include <abstractions/kde>',
|
|
'#include <abstractions/openssl>'):
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###POLICYGROUPS###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_genpolicy_policygroups_nonexistent(self):
|
|
"""Test genpolicy (nonexistent policygroup)"""
|
|
try:
|
|
self._gen_policy(extra_args=['--policy-groups=nonexistent'])
|
|
except AppArmorException:
|
|
return
|
|
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])
|
|
search = "{} rk,".format(s)
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
search = "owner {} rk,".format(s)
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
search = "owner {} rk,".format(s)
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
search = "owner {} rk,".format(s)
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
search_terms = ["{} rk,".format(s), "{}** rk,".format(s)]
|
|
for search in search_terms:
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
search_terms = ["{} rk,".format(os.path.dirname(s)), "{} rk,".format(s)]
|
|
for search in search_terms:
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
search_terms = ["{} rk,".format(os.path.dirname(s)), "{} rk,".format(s)]
|
|
for search in search_terms:
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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)
|
|
# This mimics easyprof.gen_path_rule()
|
|
owner = ""
|
|
if s.startswith('/home/') or s.startswith("@{HOME"):
|
|
owner = "owner "
|
|
if s.endswith('/'):
|
|
search_terms.append("{} rk,".format(s))
|
|
search_terms.append("{}{}** rk,".format(owner, s))
|
|
elif s.endswith('/**') or s.endswith('/*'):
|
|
search_terms.append("{} rk,".format(os.path.dirname(s)))
|
|
search_terms.append("{}{} rk,".format(owner, s))
|
|
else:
|
|
search_terms.append("{}{} rk,".format(owner, s))
|
|
|
|
p = self._gen_policy(extra_args=args)
|
|
for search in search_terms:
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
except AppArmorException:
|
|
return
|
|
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])
|
|
search = "{} rwk,".format(s)
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
search = "owner {} rwk,".format(s)
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
search = "owner {} rwk,".format(s)
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
search = "owner {} rwk,".format(s)
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
search_terms = ["{} rwk,".format(s), "{}** rwk,".format(s)]
|
|
for search in search_terms:
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
search_terms = ["{} rwk,".format(os.path.dirname(s)), "{} rwk,".format(s)]
|
|
for search in search_terms:
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
search_terms = ["{} rwk,".format(os.path.dirname(s)), "{} rwk,".format(s)]
|
|
for search in search_terms:
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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)
|
|
# This mimics easyprof.gen_path_rule()
|
|
owner = ""
|
|
if s.startswith('/home/') or s.startswith("@{HOME"):
|
|
owner = "owner "
|
|
if s.endswith('/'):
|
|
search_terms.append("{} rwk,".format(s))
|
|
search_terms.append("{}{}** rwk,".format(owner, s))
|
|
elif s.endswith('/**') or s.endswith('/*'):
|
|
search_terms.append("{} rwk,".format(os.path.dirname(s)))
|
|
search_terms.append("{}{} rwk,".format(owner, s))
|
|
else:
|
|
search_terms.append("{}{} rwk,".format(owner, s))
|
|
|
|
p = self._gen_policy(extra_args=args)
|
|
for search in search_terms:
|
|
self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p))
|
|
inv_s = '###READPATH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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])
|
|
except AppArmorException:
|
|
return
|
|
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])
|
|
k, v = s.split('=')
|
|
s = '{}="{}"'.format(k, v)
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###TEMPLATEVAR###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(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)
|
|
|
|
p = self._gen_policy(extra_args=args)
|
|
for s in variables:
|
|
k, v = s.split('=')
|
|
s = '{}="{}"'.format(k, v)
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###TEMPLATEVAR###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_genpolicy_templatevar_bad(self):
|
|
"""Test genpolicy (template-var - bad values)"""
|
|
bad = [
|
|
"{FOO}=bar",
|
|
"@FOO}=bar",
|
|
"@{FOO=bar",
|
|
"FOO=bar",
|
|
"@FOO=bar",
|
|
"@{FOO}=/../../../etc/passwd",
|
|
"@{FOO}=bar=foo",
|
|
"@{FOO;BAZ}=bar",
|
|
'@{FOO}=bar"baz',
|
|
]
|
|
for s in bad:
|
|
try:
|
|
self._gen_policy(extra_args=['--template-var=' + s])
|
|
except AppArmorException:
|
|
continue
|
|
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)
|
|
with open(template) as f:
|
|
contents = f.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"
|
|
with open(template, 'w') as f:
|
|
f.write(bad_pol)
|
|
try:
|
|
self._gen_policy(template=template)
|
|
except AppArmorException:
|
|
return
|
|
raise Exception("policy should be invalid")
|
|
|
|
def test_genpolicy_no_binary_without_profile_name(self):
|
|
"""Test genpolicy (no binary with no profile name)"""
|
|
try:
|
|
easyprof.gen_policy_params(None, self.options)
|
|
except AppArmorException:
|
|
return
|
|
raise Exception("No binary or profile name should have been invalid")
|
|
|
|
def test_genpolicy_with_binary_with_profile_name(self):
|
|
"""Test genpolicy (binary with profile name)"""
|
|
profile_name = "some-profile-name"
|
|
p = self._gen_policy(extra_args=['--profile-name=' + profile_name])
|
|
s = 'profile "%s" "%s" {' % (profile_name, self.binary)
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###PROFILEATTACH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_genpolicy_with_binary_without_profile_name(self):
|
|
"""Test genpolicy (binary without profile name)"""
|
|
p = self._gen_policy()
|
|
s = '"%s" {' % (self.binary,)
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###PROFILEATTACH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_genpolicy_without_binary_with_profile_name(self):
|
|
"""Test genpolicy (no binary with profile name)"""
|
|
profile_name = "some-profile-name"
|
|
args = self.full_args
|
|
args.append('--profile-name=' + profile_name)
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
easyp = easyprof.AppArmorEasyProfile(None, self.options)
|
|
params = easyprof.gen_policy_params(None, self.options)
|
|
p = easyp.gen_policy(**params)
|
|
s = 'profile "%s" {' % (profile_name,)
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###PROFILEATTACH###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
# manifest tests
|
|
|
|
def test_gen_manifest_policy_with_binary_with_profile_name(self):
|
|
"""Test gen_manifest_policy (binary with profile name)"""
|
|
m = Manifest("test_gen_manifest_policy")
|
|
m.add_binary(self.ls)
|
|
self._gen_manifest_policy(m)
|
|
|
|
def test_gen_manifest_policy_without_binary_with_profile_name(self):
|
|
"""Test gen_manifest_policy (no binary with profile name)"""
|
|
m = Manifest("test_gen_manifest_policy")
|
|
self._gen_manifest_policy(m)
|
|
|
|
def test_gen_manifest_policy_templates_system(self):
|
|
"""Test gen_manifest_policy (system template)"""
|
|
m = Manifest("test_gen_manifest_policy")
|
|
m.add_template(self.test_template)
|
|
self._gen_manifest_policy(m)
|
|
|
|
def test_gen_manifest_policy_templates_system_noprefix(self):
|
|
"""Test gen_manifest_policy (system template, no security prefix)"""
|
|
m = Manifest("test_gen_manifest_policy")
|
|
m.add_template(self.test_template)
|
|
self._gen_manifest_policy(m, use_security_prefix=False)
|
|
|
|
def test_gen_manifest_abs_path_template(self):
|
|
"""Test gen_manifest_policy (abs path template)"""
|
|
m = Manifest("test_gen_manifest_policy")
|
|
m.add_template("/etc/shadow")
|
|
try:
|
|
self._gen_manifest_policy(m)
|
|
except AppArmorException:
|
|
return
|
|
raise Exception("abs path template name should be invalid")
|
|
|
|
def test_gen_manifest_escape_path_templates(self):
|
|
"""Test gen_manifest_policy (esc path template)"""
|
|
m = Manifest("test_gen_manifest_policy")
|
|
m.add_template("../../../../../../../../etc/shadow")
|
|
try:
|
|
self._gen_manifest_policy(m)
|
|
except AppArmorException:
|
|
return
|
|
raise Exception("../ template name should be invalid")
|
|
|
|
def test_gen_manifest_policy_templates_nonexistent(self):
|
|
"""Test gen manifest policy (nonexistent template)"""
|
|
m = Manifest("test_gen_manifest_policy")
|
|
m.add_template("nonexistent")
|
|
try:
|
|
self._gen_manifest_policy(m)
|
|
except AppArmorException:
|
|
return
|
|
raise Exception("template should be invalid")
|
|
|
|
def test_gen_manifest_policy_comment(self):
|
|
"""Test gen manifest policy (comment)"""
|
|
s = "test comment"
|
|
m = Manifest("test_gen_manifest_policy")
|
|
m.add_comment(s)
|
|
p = self._gen_manifest_policy(m)
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###COMMENT###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_gen_manifest_policy_author(self):
|
|
"""Test gen manifest policy (author)"""
|
|
s = "Archibald Poindexter"
|
|
m = Manifest("test_gen_manifest_policy")
|
|
m.add_author(s)
|
|
p = self._gen_manifest_policy(m)
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###AUTHOR###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_gen_manifest_policy_copyright(self):
|
|
"""Test genpolicy (copyright)"""
|
|
s = "2112/01/01"
|
|
m = Manifest("test_gen_manifest_policy")
|
|
m.add_copyright(s)
|
|
p = self._gen_manifest_policy(m)
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###COPYRIGHT###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_gen_manifest_policy_policygroups(self):
|
|
"""Test gen manifest policy (single policygroup)"""
|
|
groups = self.test_policygroup
|
|
m = Manifest("test_gen_manifest_policy")
|
|
m.add_policygroups(groups)
|
|
p = self._gen_manifest_policy(m)
|
|
|
|
for s in ('#include <abstractions/nameservice>', '#include <abstractions/gnome>'):
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###POLICYGROUPS###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_gen_manifest_policy_policygroups_multiple(self):
|
|
"""Test genpolicy (multiple policygroups)"""
|
|
test_policygroup2 = "test-policygroup2"
|
|
contents = '''
|
|
# {}
|
|
#include <abstractions/kde>
|
|
#include <abstractions/openssl>
|
|
'''.format(self.test_policygroup)
|
|
with open(os.path.join(self.tmpdir, 'policygroups', test_policygroup2), 'w') as f:
|
|
f.write(contents)
|
|
|
|
groups = "{},{}".format(self.test_policygroup, test_policygroup2)
|
|
m = Manifest("test_gen_manifest_policy")
|
|
m.add_policygroups(groups)
|
|
p = self._gen_manifest_policy(m)
|
|
|
|
for s in ('#include <abstractions/nameservice>',
|
|
'#include <abstractions/gnome>',
|
|
'#include <abstractions/kde>',
|
|
'#include <abstractions/openssl>'):
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###POLICYGROUPS###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_gen_manifest_policy_policygroups_nonexistent(self):
|
|
"""Test gen manifest policy (nonexistent policygroup)"""
|
|
groups = "nonexistent"
|
|
m = Manifest("test_gen_manifest_policy")
|
|
m.add_policygroups(groups)
|
|
try:
|
|
self._gen_manifest_policy(m)
|
|
except AppArmorException:
|
|
return
|
|
raise Exception("policygroup should be invalid")
|
|
|
|
def test_gen_manifest_policy_templatevar(self):
|
|
"""Test gen manifest policy (template-var single)"""
|
|
m = Manifest("test_gen_manifest_policy")
|
|
m.add_template_variable("FOO", "bar")
|
|
p = self._gen_manifest_policy(m)
|
|
s = '@{FOO}="bar"'
|
|
self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p))
|
|
inv_s = '###TEMPLATEVAR###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_gen_manifest_policy_templatevar_multiple(self):
|
|
"""Test gen manifest policy (template-var multiple)"""
|
|
variables = [["FOO", "bar"], ["BAR", "baz"]]
|
|
m = Manifest("test_gen_manifest_policy")
|
|
for s in variables:
|
|
m.add_template_variable(s[0], s[1])
|
|
|
|
p = self._gen_manifest_policy(m)
|
|
for s in variables:
|
|
str_s = '@{%s}="%s"' % (s[0], s[1])
|
|
self.assertTrue(str_s in p, "Could not find '{}' in:\n{}".format(str_s, p))
|
|
inv_s = '###TEMPLATEVAR###'
|
|
self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p))
|
|
|
|
def test_gen_manifest_policy_invalid_keys(self):
|
|
"""Test gen manifest policy (invalid keys)"""
|
|
keys = [
|
|
'config_file',
|
|
'debug',
|
|
'help',
|
|
'list-templates',
|
|
'list_templates',
|
|
'show-template',
|
|
'show_template',
|
|
'list-policy-groups',
|
|
'list_policy_groups',
|
|
'show-policy-group',
|
|
'show_policy_group',
|
|
'templates-dir',
|
|
'templates_dir',
|
|
'policy-groups-dir',
|
|
'policy_groups_dir',
|
|
'nonexistent',
|
|
'no_verify',
|
|
]
|
|
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
for k in keys:
|
|
security = dict()
|
|
security["profile_name"] = "test-app"
|
|
security[k] = "bad"
|
|
j = json.dumps(security, indent=2)
|
|
try:
|
|
easyprof.parse_manifest(j, self.options)
|
|
except AppArmorException:
|
|
continue
|
|
raise Exception("'{}' should be invalid".format(k))
|
|
|
|
def test_gen_manifest(self):
|
|
"""Test gen_manifest"""
|
|
# this should come from manpage
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.example.foo": {
|
|
"abstractions": [
|
|
"audio",
|
|
"gnome"
|
|
],
|
|
"author": "Your Name",
|
|
"binary": "/opt/foo/**",
|
|
"comment": "Unstructured single-line comment",
|
|
"copyright": "Unstructured single-line copyright statement",
|
|
"name": "My Foo App",
|
|
"policy_groups": [
|
|
"opt-application",
|
|
"user-application"
|
|
],
|
|
"policy_vendor": "somevendor",
|
|
"policy_version": 1.0,
|
|
"read_path": [
|
|
"/tmp/foo_r",
|
|
"/tmp/bar_r/"
|
|
],
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
"APPNAME": "foo",
|
|
"VAR1": "bar",
|
|
"VAR2": "baz"
|
|
},
|
|
"write_path": [
|
|
"/tmp/foo_w",
|
|
"/tmp/bar_w/"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
|
|
for d in ('policygroups', 'templates'):
|
|
shutil.copytree(os.path.join(self.tmpdir, d),
|
|
os.path.join(self.tmpdir, d, "somevendor/1.0"))
|
|
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(binary, self.options) = easyprof.parse_manifest(m, self.options)[0]
|
|
easyp = easyprof.AppArmorEasyProfile(binary, self.options)
|
|
params = easyprof.gen_policy_params(binary, self.options)
|
|
|
|
# verify we get the same manifest back
|
|
man_new = easyp.gen_manifest(params)
|
|
self.assertEqual(m, man_new)
|
|
|
|
def test_gen_manifest_ubuntu(self):
|
|
"""Test gen_manifest (ubuntu)"""
|
|
# this should be based on the manpage (but use existing policy_groups
|
|
# and template
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.ubuntu.developer.myusername.MyCoolApp": {
|
|
"name": "MyCoolApp",
|
|
"policy_groups": [
|
|
"opt-application",
|
|
"user-application"
|
|
],
|
|
"policy_vendor": "ubuntu",
|
|
"policy_version": 1.0,
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
"APPNAME": "MyCoolApp",
|
|
"APPVERSION": "0.1.2"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
|
|
for d in ('policygroups', 'templates'):
|
|
shutil.copytree(os.path.join(self.tmpdir, d),
|
|
os.path.join(self.tmpdir, d, "ubuntu/1.0"))
|
|
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(binary, self.options) = easyprof.parse_manifest(m, self.options)[0]
|
|
easyp = easyprof.AppArmorEasyProfile(binary, self.options)
|
|
params = easyprof.gen_policy_params(binary, self.options)
|
|
|
|
# verify we get the same manifest back
|
|
man_new = easyp.gen_manifest(params)
|
|
self.assertEqual(m, man_new)
|
|
|
|
def test_parse_manifest_no_version(self):
|
|
"""Test parse_manifest (vendor with no version)"""
|
|
# this should come from manpage
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.ubuntu.developer.myusername.MyCoolApp": {
|
|
"policy_groups": [
|
|
"opt-application",
|
|
"user-application"
|
|
],
|
|
"policy_vendor": "ubuntu",
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
"APPNAME": "MyCoolApp",
|
|
"APPVERSION": "0.1.2"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(binary, self.options) = easyprof.parse_manifest(m, self.options)[0]
|
|
try:
|
|
easyprof.AppArmorEasyProfile(binary, self.options)
|
|
except AppArmorException:
|
|
return
|
|
raise Exception("Should have failed on missing version")
|
|
|
|
def test_parse_manifest_no_vendor(self):
|
|
"""Test parse_manifest (version with no vendor)"""
|
|
# this should come from manpage
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.ubuntu.developer.myusername.MyCoolApp": {
|
|
"policy_groups": [
|
|
"opt-application",
|
|
"user-application"
|
|
],
|
|
"policy_version": 1.0,
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
"APPNAME": "MyCoolApp",
|
|
"APPVERSION": "0.1.2"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(binary, self.options) = easyprof.parse_manifest(m, self.options)[0]
|
|
try:
|
|
easyprof.AppArmorEasyProfile(binary, self.options)
|
|
except AppArmorException:
|
|
return
|
|
raise Exception("Should have failed on missing vendor")
|
|
|
|
def test_parse_manifest_multiple(self):
|
|
"""Test parse_manifest_multiple"""
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.example.foo": {
|
|
"abstractions": [
|
|
"audio",
|
|
"gnome"
|
|
],
|
|
"author": "Your Name",
|
|
"binary": "/opt/foo/**",
|
|
"comment": "Unstructured single-line comment",
|
|
"copyright": "Unstructured single-line copyright statement",
|
|
"name": "My Foo App",
|
|
"policy_groups": [
|
|
"opt-application",
|
|
"user-application"
|
|
],
|
|
"read_path": [
|
|
"/tmp/foo_r",
|
|
"/tmp/bar_r/"
|
|
],
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
"APPNAME": "foo",
|
|
"VAR1": "bar",
|
|
"VAR2": "baz"
|
|
},
|
|
"write_path": [
|
|
"/tmp/foo_w",
|
|
"/tmp/bar_w/"
|
|
]
|
|
},
|
|
"com.ubuntu.developer.myusername.MyCoolApp": {
|
|
"policy_groups": [
|
|
"opt-application"
|
|
],
|
|
"policy_vendor": "ubuntu",
|
|
"policy_version": 1.0,
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
"APPNAME": "MyCoolApp",
|
|
"APPVERSION": "0.1.2"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
|
|
for d in ('policygroups', 'templates'):
|
|
shutil.copytree(os.path.join(self.tmpdir, d),
|
|
os.path.join(self.tmpdir, d, "ubuntu/1.0"))
|
|
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
profiles = easyprof.parse_manifest(m, self.options)
|
|
for (binary, options) in profiles:
|
|
easyp = easyprof.AppArmorEasyProfile(binary, options)
|
|
params = easyprof.gen_policy_params(binary, options)
|
|
easyp.gen_manifest(params)
|
|
easyp.gen_policy(**params)
|
|
|
|
# verify manifest tests
|
|
def _verify_manifest(self, m, expected, invalid=False):
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
try:
|
|
(binary, options) = easyprof.parse_manifest(m, self.options)[0]
|
|
except AppArmorException:
|
|
if invalid:
|
|
return
|
|
raise
|
|
params = easyprof.gen_policy_params(binary, options)
|
|
if expected:
|
|
self.assertTrue(easyprof.verify_manifest(params, args), "params={}\nmanifest={}".format(params, m))
|
|
else:
|
|
self.assertFalse(easyprof.verify_manifest(params, args), "params={}\nmanifest={}".format(params, m))
|
|
|
|
def test_verify_manifest_full(self):
|
|
"""Test verify_manifest (full)"""
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.example.foo": {
|
|
"abstractions": [
|
|
"base"
|
|
],
|
|
"author": "Your Name",
|
|
"binary": "/opt/com.example/foo/**",
|
|
"comment": "some free-form single-line comment",
|
|
"copyright": "Unstructured single-line copyright statement",
|
|
"name": "foo",
|
|
"policy_groups": [
|
|
"user-application",
|
|
"opt-application"
|
|
],
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
"OK1": "foo",
|
|
"OK2": "com.example.foo"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
self._verify_manifest(m, expected=True)
|
|
|
|
def test_verify_manifest_full_bad(self):
|
|
"""Test verify_manifest (full bad)"""
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"/com.example.foo": {
|
|
"abstractions": [
|
|
"audio",
|
|
"gnome"
|
|
],
|
|
"author": "Your Name",
|
|
"binary": "/usr/foo/**",
|
|
"comment": "some free-form single-line comment",
|
|
"copyright": "Unstructured single-line copyright statement",
|
|
"name": "foo",
|
|
"policy_groups": [
|
|
"user-application",
|
|
"opt-application"
|
|
],
|
|
"read_path": [
|
|
"/tmp/foo_r",
|
|
"/tmp/bar_r/"
|
|
],
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
"VAR1": "f*o",
|
|
"VAR2": "*foo",
|
|
"VAR3": "fo*",
|
|
"VAR4": "b{ar",
|
|
"VAR5": "b{a,r}",
|
|
"VAR6": "b}ar",
|
|
"VAR7": "bar[0-9]",
|
|
"VAR8": "b{ar",
|
|
"VAR9": "/tmp/../etc/passwd"
|
|
},
|
|
"write_path": [
|
|
"/tmp/foo_w",
|
|
"/tmp/bar_w/"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
|
|
self._verify_manifest(m, expected=False, invalid=True)
|
|
|
|
def test_verify_manifest_binary(self):
|
|
"""Test verify_manifest (binary in /usr)"""
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.example.foo": {
|
|
"binary": "/usr/foo/**",
|
|
"template": "user-application"
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
self._verify_manifest(m, expected=True)
|
|
|
|
def test_verify_manifest_profile_profile_name_bad(self):
|
|
"""Test verify_manifest (bad profile_name)"""
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"/foo": {
|
|
"binary": "/opt/com.example/foo/**",
|
|
"template": "user-application"
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
self._verify_manifest(m, expected=False, invalid=True)
|
|
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"bin/*": {
|
|
"binary": "/opt/com.example/foo/**",
|
|
"template": "user-application"
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
self._verify_manifest(m, expected=False)
|
|
|
|
def test_verify_manifest_profile_profile_name(self):
|
|
"""Test verify_manifest (profile_name)"""
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.example.foo": {
|
|
"binary": "/opt/com.example/foo/**",
|
|
"template": "user-application"
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
self._verify_manifest(m, expected=True)
|
|
|
|
def test_verify_manifest_profile_abstractions(self):
|
|
"""Test verify_manifest (abstractions)"""
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.example.foo": {
|
|
"binary": "/opt/com.example/foo/**",
|
|
"template": "user-application",
|
|
"abstractions": [
|
|
"base"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
self._verify_manifest(m, expected=True)
|
|
|
|
def test_verify_manifest_profile_abstractions_bad(self):
|
|
"""Test verify_manifest (bad abstractions)"""
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.example.foo": {
|
|
"binary": "/opt/com.example/foo/**",
|
|
"template": "user-application",
|
|
"abstractions": [
|
|
"user-tmp"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
self._verify_manifest(m, expected=False)
|
|
|
|
def test_verify_manifest_profile_template_var(self):
|
|
"""Test verify_manifest (good template_var)"""
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.example.foo": {
|
|
"binary": "/opt/com.example/something with spaces/**",
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
"OK1": "foo",
|
|
"OK2": "com.example.foo",
|
|
"OK3": "something with spaces"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
self._verify_manifest(m, expected=True)
|
|
|
|
def test_verify_manifest_profile_template_var_bad(self):
|
|
"""Test verify_manifest (bad template_var)"""
|
|
for v in ('"VAR1": "f*o"',
|
|
'"VAR2": "*foo"',
|
|
'"VAR3": "fo*"',
|
|
'"VAR4": "b{ar"',
|
|
'"VAR5": "b{a,r}"',
|
|
'"VAR6": "b}ar"',
|
|
'"VAR7": "bar[0-9]"',
|
|
'"VAR8": "b{ar"',
|
|
'"VAR9": "foo/bar"' # this is valid, but potentially unsafe
|
|
):
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.example.foo": {
|
|
"binary": "/opt/com.example/foo/**",
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
%s
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}''' % (v,)
|
|
self._verify_manifest(m, expected=False)
|
|
|
|
def test_manifest_invalid(self):
|
|
"""Test invalid manifest (parse error)"""
|
|
m = '''{
|
|
"security": {
|
|
"com.example.foo": {
|
|
"binary": "/opt/com.example/foo/**",
|
|
"template": "user-application",
|
|
"abstractions": [
|
|
"base"
|
|
]
|
|
}'''
|
|
self._verify_manifest(m, expected=False, invalid=True)
|
|
|
|
def test_manifest_invalid2(self):
|
|
"""Test invalid manifest (profile_name is not key)"""
|
|
m = '''{
|
|
"security": {
|
|
"binary": "/opt/com.example/foo/**",
|
|
"template": "user-application",
|
|
"abstractions": [
|
|
"base"
|
|
]
|
|
}
|
|
}'''
|
|
self._verify_manifest(m, expected=False, invalid=True)
|
|
|
|
def test_manifest_invalid3(self):
|
|
"""Test invalid manifest (profile_name in dict)"""
|
|
m = '''{
|
|
"security": {
|
|
"binary": "/opt/com.example/foo/**",
|
|
"template": "user-application",
|
|
"abstractions": [
|
|
"base"
|
|
],
|
|
"profile_name": "com.example.foo"
|
|
}
|
|
}'''
|
|
self._verify_manifest(m, expected=False, invalid=True)
|
|
|
|
def test_manifest_invalid4(self):
|
|
"""Test invalid manifest (bad path in template var)"""
|
|
for v in ('"VAR1": "/tmp/../etc/passwd"',
|
|
'"VAR2": "./"',
|
|
'"VAR3": "foo\"bar"',
|
|
'"VAR4": "foo//bar"',
|
|
):
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.example.foo": {
|
|
"binary": "/opt/com.example/foo/**",
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
%s
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}''' % (v,)
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(binary, options) = easyprof.parse_manifest(m, self.options)[0]
|
|
params = easyprof.gen_policy_params(binary, options)
|
|
try:
|
|
easyprof.verify_manifest(params)
|
|
except AppArmorException:
|
|
return
|
|
|
|
raise Exception("Should have failed with invalid variable declaration")
|
|
|
|
# policy version tests
|
|
def test_policy_vendor_manifest_nonexistent(self):
|
|
"""Test policy vendor via manifest (nonexistent)"""
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.example.foo": {
|
|
"policy_vendor": "nonexistent",
|
|
"policy_version": 1.0,
|
|
"binary": "/opt/com.example/foo/**",
|
|
"template": "user-application"
|
|
}
|
|
}
|
|
}
|
|
}'''
|
|
# Build up our args
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(binary, self.options) = easyprof.parse_manifest(m, self.options)[0]
|
|
try:
|
|
easyprof.AppArmorEasyProfile(binary, self.options)
|
|
except AppArmorException:
|
|
return
|
|
|
|
raise Exception("Should have failed with non-existent directory")
|
|
|
|
def test_policy_version_manifest(self):
|
|
"""Test policy version via manifest (good)"""
|
|
policy_vendor = "somevendor"
|
|
policy_version = "1.0"
|
|
policy_subdir = "{}/{}".format(policy_vendor, policy_version)
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"com.example.foo": {
|
|
"policy_vendor": "%s",
|
|
"policy_version": %s,
|
|
"binary": "/opt/com.example/foo/**",
|
|
"template": "user-application"
|
|
}
|
|
}
|
|
}
|
|
}''' % (policy_vendor, policy_version)
|
|
for d in ('policygroups', 'templates'):
|
|
shutil.copytree(os.path.join(self.tmpdir, d),
|
|
os.path.join(self.tmpdir, d, policy_subdir))
|
|
|
|
# Build up our args
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(binary, self.options) = easyprof.parse_manifest(m, self.options)[0]
|
|
easyp = easyprof.AppArmorEasyProfile(binary, self.options)
|
|
|
|
tdir = os.path.join(self.tmpdir, 'templates', policy_subdir)
|
|
for t in easyp.get_templates():
|
|
self.assertTrue(t.startswith(tdir))
|
|
|
|
pdir = os.path.join(self.tmpdir, 'policygroups', policy_subdir)
|
|
for p in easyp.get_policy_groups():
|
|
self.assertTrue(p.startswith(pdir))
|
|
|
|
params = easyprof.gen_policy_params(binary, self.options)
|
|
easyp.gen_policy(**params)
|
|
|
|
def test_policy_vendor_version_args(self):
|
|
"""Test policy vendor and version via command line args (good)"""
|
|
policy_version = "1.0"
|
|
policy_vendor = "somevendor"
|
|
policy_subdir = "{}/{}".format(policy_vendor, policy_version)
|
|
|
|
# Create the directories
|
|
for d in ('policygroups', 'templates'):
|
|
shutil.copytree(os.path.join(self.tmpdir, d),
|
|
os.path.join(self.tmpdir, d, policy_subdir))
|
|
|
|
# Build up our args
|
|
args = self.full_args
|
|
args.append("--policy-version=" + policy_version)
|
|
args.append("--policy-vendor=" + policy_vendor)
|
|
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(self.options, self.args) = easyprof.parse_args(self.full_args + [self.binary])
|
|
easyp = easyprof.AppArmorEasyProfile(self.binary, self.options)
|
|
|
|
tdir = os.path.join(self.tmpdir, 'templates', policy_subdir)
|
|
for t in easyp.get_templates():
|
|
self.assertTrue(t.startswith(tdir),
|
|
"'{}' does not start with '{}'".format(t, tdir))
|
|
|
|
pdir = os.path.join(self.tmpdir, 'policygroups', policy_subdir)
|
|
for p in easyp.get_policy_groups():
|
|
self.assertTrue(p.startswith(pdir),
|
|
"'{}' does not start with '{}'".format(p, pdir))
|
|
|
|
params = easyprof.gen_policy_params(self.binary, self.options)
|
|
easyp.gen_policy(**params)
|
|
|
|
def test_policy_vendor_args_nonexistent(self):
|
|
"""Test policy vendor via command line args (nonexistent)"""
|
|
policy_vendor = "nonexistent"
|
|
policy_version = "1.0"
|
|
args = self.full_args
|
|
args.append("--policy-version=" + policy_version)
|
|
args.append("--policy-vendor=" + policy_vendor)
|
|
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(self.options, self.args) = easyprof.parse_args(self.full_args + [self.binary])
|
|
try:
|
|
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
|
except AppArmorException:
|
|
return
|
|
|
|
raise Exception("Should have failed with non-existent directory")
|
|
|
|
def test_policy_version_args_bad(self):
|
|
"""Test policy version via command line args (bad)"""
|
|
bad = [
|
|
"../../../../../../etc",
|
|
"notanumber",
|
|
"v1.0a",
|
|
"-1",
|
|
]
|
|
for policy_version in bad:
|
|
args = self.full_args
|
|
args.append("--policy-version=" + policy_version)
|
|
args.append("--policy-vendor=somevendor")
|
|
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(self.options, self.args) = easyprof.parse_args(self.full_args + [self.binary])
|
|
try:
|
|
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
|
except AppArmorException:
|
|
continue
|
|
|
|
raise Exception("Should have failed with bad version")
|
|
|
|
def test_policy_vendor_args_bad(self):
|
|
"""Test policy vendor via command line args (bad)"""
|
|
bad = [
|
|
"../../../../../../etc",
|
|
"vendor with space",
|
|
"semicolon;isbad",
|
|
]
|
|
for policy_vendor in bad:
|
|
args = self.full_args
|
|
args.append("--policy-vendor=" + policy_vendor)
|
|
args.append("--policy-version=1.0")
|
|
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(self.options, self.args) = easyprof.parse_args(self.full_args + [self.binary])
|
|
try:
|
|
easyprof.AppArmorEasyProfile(self.binary, self.options)
|
|
except AppArmorException:
|
|
continue
|
|
|
|
raise Exception("Should have failed with bad vendor")
|
|
|
|
# output_directory tests
|
|
def test_output_directory_multiple(self):
|
|
"""Test output_directory (multiple)"""
|
|
files = dict()
|
|
files["com.example.foo"] = "com.example.foo"
|
|
files["com.ubuntu.developer.myusername.MyCoolApp"] = "com.ubuntu.developer.myusername.MyCoolApp"
|
|
files["usr.bin.baz"] = "/usr/bin/baz"
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"%s": {
|
|
"abstractions": [
|
|
"audio",
|
|
"gnome"
|
|
],
|
|
"author": "Your Name",
|
|
"binary": "/opt/foo/**",
|
|
"comment": "Unstructured single-line comment",
|
|
"copyright": "Unstructured single-line copyright statement",
|
|
"name": "My Foo App",
|
|
"policy_groups": [
|
|
"opt-application",
|
|
"user-application"
|
|
],
|
|
"read_path": [
|
|
"/tmp/foo_r",
|
|
"/tmp/bar_r/"
|
|
],
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
"APPNAME": "foo",
|
|
"VAR1": "bar",
|
|
"VAR2": "baz"
|
|
},
|
|
"write_path": [
|
|
"/tmp/foo_w",
|
|
"/tmp/bar_w/"
|
|
]
|
|
},
|
|
"%s": {
|
|
"policy_groups": [
|
|
"opt-application",
|
|
"user-application"
|
|
],
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
"APPNAME": "MyCoolApp",
|
|
"APPVERSION": "0.1.2"
|
|
}
|
|
},
|
|
"%s": {
|
|
"abstractions": [
|
|
"gnome"
|
|
],
|
|
"policy_groups": [
|
|
"user-application"
|
|
],
|
|
"template_variables": {
|
|
"APPNAME": "baz"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}''' % (
|
|
files["com.example.foo"],
|
|
files["com.ubuntu.developer.myusername.MyCoolApp"],
|
|
files["usr.bin.baz"]
|
|
)
|
|
|
|
out_dir = os.path.join(self.tmpdir, "output")
|
|
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
profiles = easyprof.parse_manifest(m, self.options)
|
|
for (binary, options) in profiles:
|
|
easyp = easyprof.AppArmorEasyProfile(binary, options)
|
|
params = easyprof.gen_policy_params(binary, options)
|
|
easyp.output_policy(params, dir=out_dir)
|
|
|
|
for fn in files:
|
|
f = os.path.join(out_dir, fn)
|
|
self.assertTrue(os.path.exists(f), "Could not find '{}'".format(f))
|
|
|
|
def test_output_directory_single(self):
|
|
"""Test output_directory (single)"""
|
|
files = dict()
|
|
files["com.example.foo"] = "com.example.foo"
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"%s": {
|
|
"abstractions": [
|
|
"audio",
|
|
"gnome"
|
|
],
|
|
"author": "Your Name",
|
|
"binary": "/opt/foo/**",
|
|
"comment": "Unstructured single-line comment",
|
|
"copyright": "Unstructured single-line copyright statement",
|
|
"name": "My Foo App",
|
|
"policy_groups": [
|
|
"opt-application",
|
|
"user-application"
|
|
],
|
|
"read_path": [
|
|
"/tmp/foo_r",
|
|
"/tmp/bar_r/"
|
|
],
|
|
"template": "user-application",
|
|
"template_variables": {
|
|
"APPNAME": "foo",
|
|
"VAR1": "bar",
|
|
"VAR2": "baz"
|
|
},
|
|
"write_path": [
|
|
"/tmp/foo_w",
|
|
"/tmp/bar_w/"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}''' % (files["com.example.foo"],)
|
|
|
|
out_dir = os.path.join(self.tmpdir, "output")
|
|
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
profiles = easyprof.parse_manifest(m, self.options)
|
|
for (binary, options) in profiles:
|
|
easyp = easyprof.AppArmorEasyProfile(binary, options)
|
|
params = easyprof.gen_policy_params(binary, options)
|
|
easyp.output_policy(params, dir=out_dir)
|
|
|
|
for fn in files:
|
|
f = os.path.join(out_dir, fn)
|
|
self.assertTrue(os.path.exists(f), "Could not find '{}'".format(f))
|
|
|
|
def test_output_directory_invalid(self):
|
|
"""Test output_directory (output directory exists as file)"""
|
|
files = dict()
|
|
files["usr.bin.baz"] = "/usr/bin/baz"
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"%s": {
|
|
"abstractions": [
|
|
"gnome"
|
|
],
|
|
"policy_groups": [
|
|
"user-application"
|
|
],
|
|
"template_variables": {
|
|
"APPNAME": "baz"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}''' % (files["usr.bin.baz"],)
|
|
|
|
out_dir = os.path.join(self.tmpdir, "output")
|
|
open(out_dir, 'w').close()
|
|
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(binary, options) = easyprof.parse_manifest(m, self.options)[0]
|
|
easyp = easyprof.AppArmorEasyProfile(binary, options)
|
|
params = easyprof.gen_policy_params(binary, options)
|
|
try:
|
|
easyp.output_policy(params, dir=out_dir)
|
|
except AppArmorException:
|
|
return
|
|
raise Exception("Should have failed with 'is not a directory'")
|
|
|
|
def test_output_directory_invalid_params(self):
|
|
"""Test output_directory (no binary or profile_name)"""
|
|
files = dict()
|
|
files["usr.bin.baz"] = "/usr/bin/baz"
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"%s": {
|
|
"abstractions": [
|
|
"gnome"
|
|
],
|
|
"policy_groups": [
|
|
"user-application"
|
|
],
|
|
"template_variables": {
|
|
"APPNAME": "baz"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}''' % (files["usr.bin.baz"],)
|
|
|
|
out_dir = os.path.join(self.tmpdir, "output")
|
|
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(binary, options) = easyprof.parse_manifest(m, self.options)[0]
|
|
easyp = easyprof.AppArmorEasyProfile(binary, options)
|
|
params = easyprof.gen_policy_params(binary, options)
|
|
del params['binary']
|
|
try:
|
|
easyp.output_policy(params, dir=out_dir)
|
|
except AppArmorException:
|
|
return
|
|
raise Exception("Should have failed with 'Must specify binary and/or profile name'")
|
|
|
|
def test_output_directory_invalid2(self):
|
|
"""Test output_directory (profile exists)"""
|
|
files = dict()
|
|
files["usr.bin.baz"] = "/usr/bin/baz"
|
|
m = '''{
|
|
"security": {
|
|
"profiles": {
|
|
"%s": {
|
|
"abstractions": [
|
|
"gnome"
|
|
],
|
|
"policy_groups": [
|
|
"user-application"
|
|
],
|
|
"template_variables": {
|
|
"APPNAME": "baz"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}''' % (files["usr.bin.baz"],)
|
|
|
|
out_dir = os.path.join(self.tmpdir, "output")
|
|
os.mkdir(out_dir)
|
|
open(os.path.join(out_dir, "usr.bin.baz"), 'w').close()
|
|
|
|
args = self.full_args
|
|
args.append("--manifest=/dev/null")
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
(binary, options) = easyprof.parse_manifest(m, self.options)[0]
|
|
easyp = easyprof.AppArmorEasyProfile(binary, options)
|
|
params = easyprof.gen_policy_params(binary, options)
|
|
try:
|
|
easyp.output_policy(params, dir=out_dir)
|
|
except AppArmorException:
|
|
return
|
|
raise Exception("Should have failed with 'already exists'")
|
|
|
|
def test_output_directory_args(self):
|
|
"""Test output_directory (args)"""
|
|
files = dict()
|
|
files["usr.bin.baz"] = "/usr/bin/baz"
|
|
|
|
# Build up our args
|
|
args = self.full_args
|
|
args.append('--template=' + self.test_template)
|
|
args.append('--name=foo')
|
|
args.append(files["usr.bin.baz"])
|
|
|
|
out_dir = os.path.join(self.tmpdir, "output")
|
|
|
|
# Now parse our args
|
|
(self.options, self.args) = easyprof.parse_args(args)
|
|
easyp = easyprof.AppArmorEasyProfile(files["usr.bin.baz"], self.options)
|
|
params = easyprof.gen_policy_params(files["usr.bin.baz"], self.options)
|
|
easyp.output_policy(params, dir=out_dir)
|
|
|
|
for fn in files:
|
|
f = os.path.join(out_dir, fn)
|
|
self.assertTrue(os.path.exists(f), "Could not find '{}'".format(f))
|
|
|
|
#
|
|
# utility classes
|
|
#
|
|
def test_valid_profile_name(self):
|
|
"""Test valid_profile_name"""
|
|
names = [
|
|
'foo',
|
|
'com.example.foo',
|
|
'/usr/bin/foo',
|
|
'com.example.app_myapp_1:2.3+ab12~foo',
|
|
]
|
|
for n in names:
|
|
self.assertTrue(easyprof.valid_profile_name(n), "'{}' should be valid".format(n))
|
|
|
|
def test_valid_profile_name_invalid(self):
|
|
"""Test valid_profile_name (invalid)"""
|
|
names = [
|
|
'fo/o',
|
|
'/../../etc/passwd',
|
|
'../../etc/passwd',
|
|
'./../etc/passwd',
|
|
'./etc/passwd',
|
|
'/usr/bin//foo',
|
|
'/usr/bin/./foo',
|
|
'foo`',
|
|
'foo!',
|
|
'foo@',
|
|
'foo$',
|
|
'foo#',
|
|
'foo%',
|
|
'foo^',
|
|
'foo&',
|
|
'foo*',
|
|
'foo(',
|
|
'foo)',
|
|
'foo=',
|
|
'foo{',
|
|
'foo}',
|
|
'foo[',
|
|
'foo]',
|
|
'foo|',
|
|
'foo/',
|
|
'foo\\',
|
|
'foo;',
|
|
"foo'",
|
|
'foo"',
|
|
'foo<',
|
|
'foo>',
|
|
'foo?',
|
|
r'foo\/',
|
|
'foo,',
|
|
'_foo',
|
|
]
|
|
for n in names:
|
|
self.assertFalse(easyprof.valid_profile_name(n), "'{}' should be invalid".format(n))
|
|
|
|
def test_valid_path(self):
|
|
"""Test valid_path"""
|
|
names = [
|
|
'/bin/bar',
|
|
'/etc/apparmor.d/com.example.app_myapp_1:2.3+ab12~foo',
|
|
]
|
|
names_rel = [
|
|
'bin/bar',
|
|
'apparmor.d/com.example.app_myapp_1:2.3+ab12~foo',
|
|
'com.example.app_myapp_1:2.3+ab12~foo',
|
|
]
|
|
for n in names:
|
|
self.assertTrue(easyprof.valid_path(n), "'{}' should be valid".format(n))
|
|
for n in names_rel:
|
|
self.assertTrue(easyprof.valid_path(n, relative_ok=True), "'{}' should be valid".format(n))
|
|
|
|
def test_zz_valid_path_invalid(self):
|
|
"""Test valid_path (invalid)"""
|
|
names = [
|
|
'/bin//bar',
|
|
'bin/bar',
|
|
'/../etc/passwd',
|
|
'./bin/bar',
|
|
'./',
|
|
]
|
|
names_rel = [
|
|
'bin/../bar',
|
|
'apparmor.d/../passwd',
|
|
'com.example.app_"myapp_1:2.3+ab12~foo',
|
|
]
|
|
for n in names:
|
|
self.assertFalse(easyprof.valid_path(n, relative_ok=False), "'{}' should be invalid".format(n))
|
|
for n in names_rel:
|
|
self.assertFalse(easyprof.valid_path(n, relative_ok=True), "'{}' should be invalid".format(n))
|
|
|
|
|
|
#
|
|
# End test class
|
|
#
|
|
|
|
#
|
|
# Main
|
|
#
|
|
if __name__ == '__main__':
|
|
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
|
|
|
|
# run the tests
|
|
suite = unittest.TestSuite()
|
|
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(T))
|
|
rc = unittest.TextTestRunner(verbosity=1).run(suite)
|
|
|
|
if not rc.wasSuccessful():
|
|
sys.exit(1)
|