On Tue, 2012-02-07 at 08:50 -0600, Jamie Strandboge wrote: > This patchset is to accomplish the code portion of the > security-p-app-isolation blueprint. aa-easyprof is a standalone CLI > application which can also be imported into developer SDKs. From the man > page: ... > * 0001-aa-easyprof.patch: the application, library, documentation and > installation script
This patch implements aa-easyprof. It consists of: - utils/aa-easyprof - utils/aa-easyprof.pod - utils/easyprof/easyprof.conf - utils/easyprof/README - utils/apparmor/easyprof.py - utils/python-tools-setup.py aa-easyprof takes the first steps towards a python implementation for the userspace tools by creating the first python library outside of libapparmor. For now, all functionality needed by aa-easyprof is in utils/apparmor/easyprof.py. We will presumably want to break some of this out into utils/apparmor/common.py, utils/apparmor/util.py or similar at some point. utils/python-tools-setup.py is a standard python setup.py-style distutils script to be used by the Makefile. -- Jamie Strandboge | http://www.canonical.com
diff -Naurp -x .bzr -x common apparmor-trunk/utils/aa-easyprof apparmor-trunk-easyprof/utils/aa-easyprof
--- apparmor-trunk/utils/aa-easyprof 1969-12-31 18:00:00.000000000 -0600
+++ apparmor-trunk-easyprof/utils/aa-easyprof 2012-02-07 07:52:31.000000000 -0600
@@ -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,
+
diff -Naurp -x .bzr -x common apparmor-trunk/utils/aa-easyprof.pod apparmor-trunk-easyprof/utils/aa-easyprof.pod
--- apparmor-trunk/utils/aa-easyprof.pod 1969-12-31 18:00:00.000000000 -0600
+++ apparmor-trunk-easyprof/utils/aa-easyprof.pod 2012-02-07 08:02:30.000000000 -0600
@@ -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
diff -Naurp -x .bzr -x common apparmor-trunk/utils/apparmor/easyprof.py apparmor-trunk-easyprof/utils/apparmor/easyprof.py
--- apparmor-trunk/utils/apparmor/easyprof.py 1969-12-31 18:00:00.000000000 -0600
+++ apparmor-trunk-easyprof/utils/apparmor/easyprof.py 2012-02-07 07:55:17.000000000 -0600
@@ -0,0 +1,568 @@
+#! /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 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
+
diff -Naurp -x .bzr -x common apparmor-trunk/utils/easyprof/easyprof.conf apparmor-trunk-easyprof/utils/easyprof/easyprof.conf
--- apparmor-trunk/utils/easyprof/easyprof.conf 1969-12-31 18:00:00.000000000 -0600
+++ apparmor-trunk-easyprof/utils/easyprof/easyprof.conf 2012-02-06 16:36:49.000000000 -0600
@@ -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"
diff -Naurp -x .bzr -x common apparmor-trunk/utils/easyprof/README apparmor-trunk-easyprof/utils/easyprof/README
--- apparmor-trunk/utils/easyprof/README 1969-12-31 18:00:00.000000000 -0600
+++ apparmor-trunk-easyprof/utils/easyprof/README 2012-02-07 08:08:43.000000000 -0600
@@ -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).
diff -Naurp -x .bzr -x common apparmor-trunk/utils/python-tools-setup.py apparmor-trunk-easyprof/utils/python-tools-setup.py
--- apparmor-trunk/utils/python-tools-setup.py 1969-12-31 18:00:00.000000000 -0600
+++ apparmor-trunk-easyprof/utils/python-tools-setup.py 2012-02-06 21:04:04.000000000 -0600
@@ -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='[email protected]',
+ url='https://launchpad.net/apparmor',
+ license='GPL-2',
+ cmdclass={'install': Install},
+ package_dir={'apparmor': 'staging'},
+ py_modules=['apparmor.easyprof']
+)
+
+shutil.rmtree('staging')
signature.asc
Description: This is a digitally signed message part
-- AppArmor mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
