1
# ------------------------------------------------------------------
3
# Copyright (C) 2011-2013 Canonical Ltd.
5
# This program is free software; you can redistribute it and/or
6
# modify it under the terms of version 2 of the GNU General Public
7
# License published by the Free Software Foundation.
9
# ------------------------------------------------------------------
11
from __future__ import with_statement
26
# TODO: move this out to the common library
28
#from apparmor import AppArmorException
29
class AppArmorException(Exception):
30
'''This class represents AppArmor exceptions'''
31
def __init__(self, value):
35
return repr(self.value)
43
# TODO: move this out to a utilities library
45
def error(out, exit_code=1, do_exit=True):
46
'''Print error message and exit'''
48
sys.stderr.write("ERROR: %s\n" % (out))
57
'''Print warning message'''
59
sys.stderr.write("WARN: %s\n" % (out))
64
def msg(out, output=sys.stdout):
67
sys.stdout.write("%s\n" % (out))
73
'''Try to execute the given command.'''
76
sp = subprocess.Popen(command, stdout=subprocess.PIPE,
77
stderr=subprocess.STDOUT)
81
out = sp.communicate()[0]
82
return [sp.returncode, out]
85
def cmd_pipe(command1, command2):
86
'''Try to pipe command1 into command2.'''
88
sp1 = subprocess.Popen(command1, stdout=subprocess.PIPE)
89
sp2 = subprocess.Popen(command2, stdin=sp1.stdout)
93
out = sp2.communicate()[0]
94
return [sp2.returncode, out]
98
'''Print debug message'''
101
sys.stderr.write("DEBUG: %s\n" % (out))
106
def valid_binary_path(path):
109
a_path = os.path.abspath(path)
111
debug("Could not find absolute path for binary")
115
debug("Binary should use a normalized absolute path")
118
if not os.path.exists(a_path):
121
r_path = os.path.realpath(path)
123
debug("Binary should not be a symlink")
129
def valid_variable(v):
130
'''Validate variable name'''
131
debug("Checking '%s'" % v)
133
(key, value) = v.split('=')
137
if not re.search(r'^@\{[a-zA-Z0-9_]+\}$', key):
142
if not value.startswith('/'):
144
if not valid_path(value, relative_ok=rel_ok):
150
# If we made it here, we are safe
154
def valid_path(path, relative_ok=False):
156
m = "Invalid path: %s" % (path)
157
if not relative_ok and not path.startswith('/'):
158
debug("%s (relative)" % (m))
161
if '"' in path: # We double quote elsewhere
162
debug("%s (quote)" % (m))
166
debug("%s (../ path escape)" % (m))
170
p = os.path.normpath(path)
172
debug("%s (could not normalize)" % (m))
176
debug("%s (normalized path != path (%s != %s))" % (m, p, path))
179
# If we made it here, we are safe
184
'''Known safe regex'''
185
if re.search(r'^[a-zA-Z_0-9\-\.]+$', s):
190
def valid_policy_vendor(s):
191
'''Verify the policy vendor'''
195
def valid_policy_version(v):
196
'''Verify the policy version'''
206
def valid_template_name(s, strict=False):
207
'''Verify the template name'''
208
if not strict and s.startswith('/'):
209
if not valid_path(s):
215
def valid_abstraction_name(s):
216
'''Verify the template name'''
220
def valid_profile_name(s):
221
'''Verify the profile name'''
222
# profile name specifies path
223
if s.startswith('/'):
224
if not valid_path(s):
228
# profile name does not specify path
229
# alpha-numeric and Debian version, plus '_'
230
if re.search(r'^[a-zA-Z0-9][a-zA-Z0-9_\+\-\.:~]+$', s):
235
def valid_policy_group_name(s):
236
'''Verify policy group name'''
240
def get_directory_contents(path):
241
'''Find contents of the given directory'''
242
if not valid_path(path):
246
for f in glob.glob(path + "/*"):
252
def open_file_read(path):
253
'''Open specified file read-only'''
255
orig = codecs.open(path, 'r', "UTF-8")
262
def verify_policy(policy):
263
'''Verify policy compiles'''
264
exe = "/sbin/apparmor_parser"
265
if not os.path.exists(exe):
266
rc, exe = cmd(['which', 'apparmor_parser'])
268
warn("Could not find apparmor_parser. Skipping verify")
272
# if policy starts with '/' and is one line, assume it is a path
273
if len(policy.splitlines()) == 1 and valid_path(policy):
276
f, fn = tempfile.mkstemp(prefix='aa-easyprof')
277
if not isinstance(policy, bytes):
278
policy = policy.encode('utf-8')
282
rc, out = cmd([exe, '-QTK', fn])
289
# End utility functions
293
class AppArmorEasyProfile:
294
'''Easy profile class'''
295
def __init__(self, binary, opt):
297
opt.ensure_value("conffile", "/etc/apparmor/easyprof.conf")
298
self.conffile = os.path.abspath(opt.conffile)
299
debug("Examining confile=%s" % (self.conffile))
302
if os.path.isfile(self.conffile):
305
if opt.templates_dir and os.path.isdir(opt.templates_dir):
306
self.dirs['templates'] = os.path.abspath(opt.templates_dir)
307
elif not opt.templates_dir and \
309
os.path.isfile(opt.template) and \
310
valid_path(opt.template):
311
# If we specified the template and it is an absolute path, just set
312
# the templates directory to the parent of the template so we don't
313
# have to require --template-dir with absolute paths.
314
self.dirs['templates'] = os.path.abspath(os.path.dirname(opt.template))
315
if opt.policy_groups_dir and os.path.isdir(opt.policy_groups_dir):
316
self.dirs['policygroups'] = os.path.abspath(opt.policy_groups_dir)
319
self.policy_version = None
320
self.policy_vendor = None
321
if (opt.policy_version and not opt.policy_vendor) or \
322
(opt.policy_vendor and not opt.policy_version):
323
raise AppArmorException("Must specify both policy version and vendor")
324
if opt.policy_version and opt.policy_vendor:
325
self.policy_vendor = opt.policy_vendor
326
self.policy_version = str(opt.policy_version)
328
for i in ['templates', 'policygroups']:
329
d = os.path.join(self.dirs[i], \
330
self.policy_vendor, \
332
if not os.path.isdir(d):
333
raise AppArmorException(
334
"Could not find %s directory '%s'" % (i, d))
337
if not 'templates' in self.dirs:
338
raise AppArmorException("Could not find templates directory")
339
if not 'policygroups' in self.dirs:
340
raise AppArmorException("Could not find policygroups directory")
342
self.aa_topdir = "/etc/apparmor.d"
346
if not valid_binary_path(binary):
347
raise AppArmorException("Invalid path for binary: '%s'" % binary)
350
self.set_template(opt.template, allow_abs_path=False)
352
self.set_template(opt.template)
354
self.set_policygroup(opt.policy_groups)
356
self.set_name(opt.name)
357
elif self.binary != None:
358
self.set_name(self.binary)
361
for f in get_directory_contents(self.dirs['templates']):
362
if os.path.isfile(f):
363
self.templates.append(f)
364
self.policy_groups = []
365
for f in get_directory_contents(self.dirs['policygroups']):
366
if os.path.isfile(f):
367
self.policy_groups.append(f)
369
def _get_defaults(self):
370
'''Read in defaults from configuration'''
371
if not os.path.exists(self.conffile):
372
raise AppArmorException("Could not find '%s'" % self.conffile)
374
# Read in the configuration
375
f = open_file_read(self.conffile)
377
pat = re.compile(r'^\w+=".*"?')
379
if not pat.search(line):
381
if line.startswith("POLICYGROUPS_DIR="):
382
d = re.split(r'=', line.strip())[1].strip('["\']')
383
self.dirs['policygroups'] = d
384
elif line.startswith("TEMPLATES_DIR="):
385
d = re.split(r'=', line.strip())[1].strip('["\']')
386
self.dirs['templates'] = d
389
keys = self.dirs.keys()
390
if 'templates' not in keys:
391
raise AppArmorException("Could not find TEMPLATES_DIR in '%s'" % self.conffile)
392
if 'policygroups' not in keys:
393
raise AppArmorException("Could not find POLICYGROUPS_DIR in '%s'" % self.conffile)
395
for k in self.dirs.keys():
396
if not os.path.isdir(self.dirs[k]):
397
raise AppArmorException("Could not find '%s'" % self.dirs[k])
399
def set_name(self, name):
400
'''Set name of policy'''
403
def get_template(self):
404
'''Get contents of current template'''
405
return open(self.template).read()
407
def set_template(self, template, allow_abs_path=True):
408
'''Set current template'''
409
if "../" in template:
410
raise AppArmorException('template "%s" contains "../" escape path' % (template))
411
elif template.startswith('/') and not allow_abs_path:
412
raise AppArmorException("Cannot use an absolute path template '%s'" % template)
414
if template.startswith('/'):
415
self.template = template
417
self.template = os.path.join(self.dirs['templates'], template)
419
if not os.path.exists(self.template):
420
raise AppArmorException('%s does not exist' % (self.template))
422
def get_templates(self):
423
'''Get list of all available templates by filename'''
424
return self.templates
426
def get_policygroup(self, policygroup):
427
'''Get contents of specific policygroup'''
429
if not p.startswith('/'):
430
p = os.path.join(self.dirs['policygroups'], p)
431
if self.policy_groups == None or not p in self.policy_groups:
432
raise AppArmorException("Policy group '%s' does not exist" % p)
433
return open(p).read()
435
def set_policygroup(self, policygroups):
436
'''Set policygroups'''
437
self.policy_groups = []
438
if policygroups != None:
439
for p in policygroups.split(','):
440
if not p.startswith('/'):
441
p = os.path.join(self.dirs['policygroups'], p)
442
if not os.path.exists(p):
443
raise AppArmorException('%s does not exist' % (p))
444
self.policy_groups.append(p)
446
def get_policy_groups(self):
447
'''Get list of all policy groups by filename'''
448
return self.policy_groups
450
def gen_abstraction_rule(self, abstraction):
451
'''Generate an abstraction rule'''
452
p = os.path.join(self.aa_topdir, "abstractions", abstraction)
453
if not os.path.exists(p):
454
raise AppArmorException("%s does not exist" % p)
455
return "#include <abstractions/%s>" % abstraction
457
def gen_variable_declaration(self, dec):
458
'''Generate a variable declaration'''
459
if not valid_variable(dec):
460
raise AppArmorException("Invalid variable declaration '%s'" % dec)
461
# Make sure we always quote
462
k, v = dec.split('=')
463
return '%s="%s"' % (k, v)
465
def gen_path_rule(self, path, access):
467
if not path.startswith('/') and not path.startswith('@'):
468
raise AppArmorException("'%s' should not be relative path" % path)
471
if path.startswith('/home/') or path.startswith("@{HOME"):
474
if path.endswith('/'):
475
rule.append("%s %s," % (path, access))
476
rule.append("%s%s** %s," % (owner, path, access))
477
elif path.endswith('/**') or path.endswith('/*'):
478
rule.append("%s %s," % (os.path.dirname(path), access))
479
rule.append("%s%s %s," % (owner, path, access))
481
rule.append("%s%s %s," % (owner, path, access))
486
def gen_policy(self, name,
498
def find_prefix(t, s):
499
'''Calculate whitespace prefix based on occurrence of s in t'''
500
pat = re.compile(r'^ *%s' % s)
502
for line in t.splitlines():
504
p = " " * (len(line) - len(line.lstrip()))
508
policy = self.get_template()
509
if '###ENDUSAGE###' in policy:
512
for line in policy.splitlines():
514
if line.startswith('###ENDUSAGE###'):
522
if not valid_binary_path(binary):
523
raise AppArmorException("Invalid path for binary: '%s'" % \
526
attachment = 'profile "%s" "%s"' % (profile_name, binary)
528
attachment = '"%s"' % binary
530
attachment = 'profile "%s"' % profile_name
532
raise AppArmorException("Must specify binary and/or profile name")
533
policy = re.sub(r'###PROFILEATTACH###', attachment, policy)
535
policy = re.sub(r'###NAME###', name, policy)
537
# Fill-in various comment fields
539
policy = re.sub(r'###COMMENT###', "Comment: %s" % comment, policy)
542
policy = re.sub(r'###AUTHOR###', "Author: %s" % author, policy)
544
if copyright != None:
545
policy = re.sub(r'###COPYRIGHT###', "Copyright: %s" % copyright, policy)
547
# Fill-in rules and variables with proper indenting
548
search = '###ABSTRACTIONS###'
549
prefix = find_prefix(policy, search)
550
s = "%s# No abstractions specified" % prefix
551
if abstractions != None:
552
s = "%s# Specified abstractions" % (prefix)
553
t = abstractions.split(',')
556
s += "\n%s%s" % (prefix, self.gen_abstraction_rule(i))
557
policy = re.sub(r' *%s' % search, s, policy)
559
search = '###POLICYGROUPS###'
560
prefix = find_prefix(policy, search)
561
s = "%s# No policy groups specified" % prefix
562
if policy_groups != None:
563
s = "%s# Rules specified via policy groups" % (prefix)
564
t = policy_groups.split(',')
567
for line in self.get_policygroup(i).splitlines():
568
s += "\n%s%s" % (prefix, line)
569
if i != policy_groups.split(',')[-1]:
571
policy = re.sub(r' *%s' % search, s, policy)
574
prefix = find_prefix(policy, search)
575
s = "%s# No template variables specified" % prefix
576
if len(template_var) > 0:
577
s = "%s# Specified profile variables" % (prefix)
579
for i in template_var:
580
s += "\n%s%s" % (prefix, self.gen_variable_declaration(i))
581
policy = re.sub(r' *%s' % search, s, policy)
583
search = '###READS###'
584
prefix = find_prefix(policy, search)
585
s = "%s# No read paths specified" % prefix
586
if len(read_path) > 0:
587
s = "%s# Specified read permissions" % (prefix)
590
for r in self.gen_path_rule(i, 'rk'):
591
s += "\n%s%s" % (prefix, r)
592
policy = re.sub(r' *%s' % search, s, policy)
594
search = '###WRITES###'
595
prefix = find_prefix(policy, search)
596
s = "%s# No write paths specified" % prefix
597
if len(write_path) > 0:
598
s = "%s# Specified write permissions" % (prefix)
601
for r in self.gen_path_rule(i, 'rwk'):
602
s += "\n%s%s" % (prefix, r)
603
policy = re.sub(r' *%s' % search, s, policy)
606
debug("Skipping policy verification")
607
elif not verify_policy(policy):
609
raise AppArmorException("Invalid policy")
613
def output_policy(self, params, count=0, dir=None):
615
policy = self.gen_policy(**params)
618
sys.stdout.write('### aa-easyprof profile #%d ###\n' % count)
619
sys.stdout.write('%s\n' % policy)
622
if 'profile_name' in params:
623
out_fn = params['profile_name']
624
elif 'binary' in params:
625
out_fn = params['binary']
626
else: # should not ever reach this
627
raise AppArmorException("Could not determine output filename")
629
# Generate an absolute path, convertng any path delimiters to '.'
630
out_fn = os.path.join(dir, re.sub(r'/', '.', out_fn.lstrip('/')))
631
if os.path.exists(out_fn):
632
raise AppArmorException("'%s' already exists" % out_fn)
634
if not os.path.exists(dir):
637
if not os.path.isdir(dir):
638
raise AppArmorException("'%s' is not a directory" % dir)
640
f, fn = tempfile.mkstemp(prefix='aa-easyprof')
641
if not isinstance(policy, bytes):
642
policy = policy.encode('utf-8')
646
shutil.move(fn, out_fn)
648
def gen_manifest(self, params):
649
'''Take params list and output a JSON file'''
651
d['security'] = dict()
652
d['security']['profiles'] = dict()
655
if 'profile_name' in params:
656
pkey = params['profile_name']
657
elif 'binary' in params:
658
# when profile_name is not specified, the binary (path attachment)
659
# also functions as the profile name
660
pkey = params['binary']
662
raise AppArmorException("Must supply binary or profile name")
664
d['security']['profiles'][pkey] = dict()
666
# Add the template since it isn't part of 'params'
667
template = os.path.basename(self.template)
668
if template != 'default':
669
d['security']['profiles'][pkey]['template'] = template
671
# Add the policy_version since it isn't part of 'params'
672
if self.policy_version:
673
d['security']['profiles'][pkey]['policy_version'] = float(self.policy_version)
674
if self.policy_vendor:
675
d['security']['profiles'][pkey]['policy_vendor'] = self.policy_vendor
678
if key == 'profile_name' or \
679
(key == 'binary' and not 'profile_name' in params):
680
continue # don't re-add the pkey
681
elif key == 'binary' and not params[key]:
682
continue # binary can by None when specifying --profile-name
683
elif key == 'template_var':
684
d['security']['profiles'][pkey]['template_variables'] = dict()
685
for tvar in params[key]:
686
if not self.gen_variable_declaration(tvar):
687
raise AppArmorException("Malformed template_var '%s'" % tvar)
688
(k, v) = tvar.split('=')
689
k = k.lstrip('@').lstrip('{').rstrip('}')
690
d['security']['profiles'][pkey]['template_variables'][k] = v
691
elif key == 'abstractions' or key == 'policy_groups':
692
d['security']['profiles'][pkey][key] = params[key].split(",")
693
d['security']['profiles'][pkey][key].sort()
695
d['security']['profiles'][pkey][key] = params[key]
696
json_str = json.dumps(d,
699
separators=(',', ': ')
703
def print_basefilenames(files):
705
sys.stdout.write("%s\n" % (os.path.basename(i)))
707
def print_files(files):
710
sys.stdout.write(f.read()+"\n")
712
def check_manifest_conflict_args(option, opt_str, value, parser):
713
'''Check for -m/--manifest with conflicting args'''
714
conflict_args = ['abstractions',
717
# template always get set to 'default', can't conflict
728
for conflict in conflict_args:
729
if getattr(parser.values, conflict, False):
730
raise optparse.OptionValueError("can't use --%s with --manifest " \
731
"argument" % conflict)
732
setattr(parser.values, option.dest, value)
734
def check_for_manifest_arg(option, opt_str, value, parser):
735
'''Check for -m/--manifest with conflicting args'''
736
if parser.values.manifest:
737
raise optparse.OptionValueError("can't use --%s with --manifest " \
738
"argument" % opt_str.lstrip('-'))
739
setattr(parser.values, option.dest, value)
741
def check_for_manifest_arg_append(option, opt_str, value, parser):
742
'''Check for -m/--manifest with conflicting args (with append)'''
743
if parser.values.manifest:
744
raise optparse.OptionValueError("can't use --%s with --manifest " \
745
"argument" % opt_str.lstrip('-'))
746
parser.values.ensure_value(option.dest, []).append(value)
748
def add_parser_policy_args(parser):
749
'''Add parser arguments'''
750
parser.add_option("-a", "--abstractions",
752
callback=check_for_manifest_arg,
755
help="Comma-separated list of abstractions",
756
metavar="ABSTRACTIONS")
757
parser.add_option("--read-path",
759
callback=check_for_manifest_arg_append,
762
help="Path allowing owner reads",
764
parser.add_option("--write-path",
766
callback=check_for_manifest_arg_append,
769
help="Path allowing owner writes",
771
parser.add_option("-t", "--template",
773
help="Use non-default policy template",
776
parser.add_option("--templates-dir",
777
dest="templates_dir",
778
help="Use non-default templates directory",
780
parser.add_option("-p", "--policy-groups",
782
callback=check_for_manifest_arg,
784
help="Comma-separated list of policy groups",
785
metavar="POLICYGROUPS")
786
parser.add_option("--policy-groups-dir",
787
dest="policy_groups_dir",
788
help="Use non-default policy-groups directory",
790
parser.add_option("--policy-version",
792
callback=check_for_manifest_arg,
794
dest="policy_version",
795
help="Specify version for templates and policy groups",
797
parser.add_option("--policy-vendor",
799
callback=check_for_manifest_arg,
801
dest="policy_vendor",
802
help="Specify vendor for templates and policy groups",
804
parser.add_option("--profile-name",
806
callback=check_for_manifest_arg,
809
help="AppArmor profile name",
810
metavar="PROFILENAME")
812
def parse_args(args=None, parser=None):
813
'''Parse arguments'''
817
parser = optparse.OptionParser()
819
parser.add_option("-c", "--config-file",
821
help="Use alternate configuration file",
823
parser.add_option("-d", "--debug",
824
help="Show debugging output",
827
parser.add_option("--no-verify",
828
help="Don't verify policy using 'apparmor_parser -p'",
831
parser.add_option("--list-templates",
832
help="List available templates",
835
parser.add_option("--show-template",
836
help="Show specified template",
839
parser.add_option("--list-policy-groups",
840
help="List available policy groups",
843
parser.add_option("--show-policy-group",
844
help="Show specified policy groups",
847
parser.add_option("-n", "--name",
849
callback=check_for_manifest_arg,
852
help="Name of policy (not AppArmor profile name)",
854
parser.add_option("--comment",
856
callback=check_for_manifest_arg,
859
help="Comment for policy",
861
parser.add_option("--author",
863
callback=check_for_manifest_arg,
866
help="Author of policy",
868
parser.add_option("--copyright",
870
callback=check_for_manifest_arg,
873
help="Copyright for policy",
875
parser.add_option("--template-var",
877
callback=check_for_manifest_arg_append,
880
help="Declare AppArmor variable",
881
metavar="@{VARIABLE}=VALUE")
882
parser.add_option("--output-format",
884
dest="output_format",
885
help="Specify output format as text (default) or json",
888
parser.add_option("--output-directory",
890
dest="output_directory",
891
help="Output policy to this directory",
893
# This option conflicts with any of the value arguments, e.g. name,
894
# author, template-var, etc.
895
parser.add_option("-m", "--manifest",
897
callback=check_manifest_conflict_args,
900
help="JSON manifest file",
902
parser.add_option("--verify-manifest",
905
dest="verify_manifest",
906
help="Verify JSON manifest file")
909
# add policy args now
910
add_parser_policy_args(parser)
912
(my_opt, my_args) = parser.parse_args(args)
916
return (my_opt, my_args)
918
def gen_policy_params(binary, opt):
919
'''Generate parameters for gen_policy'''
920
params = dict(binary=binary)
922
if not binary and not opt.profile_name:
923
raise AppArmorException("Must specify binary and/or profile name")
926
params['profile_name'] = opt.profile_name
929
params['name'] = opt.name
932
params['name'] = opt.profile_name
934
params['name'] = os.path.basename(binary)
936
if opt.template_var: # What about specified multiple times?
937
params['template_var'] = opt.template_var
939
params['abstractions'] = opt.abstractions
940
if opt.policy_groups:
941
params['policy_groups'] = opt.policy_groups
943
params['read_path'] = opt.read_path
945
params['write_path'] = opt.write_path
947
params['comment'] = opt.comment
949
params['author'] = opt.author
951
params['copyright'] = opt.copyright
952
if opt.policy_version and opt.output_format == "json":
953
params['policy_version'] = opt.policy_version
954
if opt.policy_vendor and opt.output_format == "json":
955
params['policy_vendor'] = opt.policy_vendor
959
def parse_manifest(manifest, opt_orig):
960
'''Take a JSON manifest as a string and updates options, returning an
961
updated binary. Note that a JSON file may contain multiple profiles.'''
964
m = json.loads(manifest)
966
raise AppArmorException("Could not parse manifest")
969
top_table = m['security']
973
if 'profiles' not in top_table:
974
raise AppArmorException("Could not parse manifest (could not find 'profiles')")
975
table = top_table['profiles']
977
# generally mirrors what is settable in gen_policy_params()
978
valid_keys = ['abstractions',
990
'template_variables',
996
for profile_name in table:
997
if not isinstance(table[profile_name], dict):
998
raise AppArmorException("Wrong JSON structure")
999
opt = copy.deepcopy(opt_orig)
1001
# The JSON structure is:
1007
# but because binary can be the profile name, we need to handle
1008
# 'profile_name' and 'binary' special. If a profile_name starts with
1009
# '/', then it is considered the binary. Otherwise, set the
1010
# profile_name and set the binary if it is in the JSON.
1012
if profile_name.startswith('/'):
1013
if 'binary' in table[profile_name]:
1014
raise AppArmorException("Profile name should not specify path with binary")
1015
binary = profile_name
1017
setattr(opt, 'profile_name', profile_name)
1018
if 'binary' in table[profile_name]:
1019
binary = table[profile_name]['binary']
1020
setattr(opt, 'binary', binary)
1022
for key in table[profile_name]:
1023
if key not in valid_keys:
1024
raise AppArmorException("Invalid key '%s'" % key)
1027
continue # handled above
1028
elif key == 'abstractions' or key == 'policy_groups':
1029
setattr(opt, key, ",".join(table[profile_name][key]))
1030
elif key == "template_variables":
1031
t = table[profile_name]['template_variables']
1034
vlist.append("@{%s}=%s" % (v, t[v]))
1035
setattr(opt, 'template_var', vlist)
1037
if hasattr(opt, key):
1038
setattr(opt, key, table[profile_name][key])
1040
profiles.append( (binary, opt) )
1045
def verify_options(opt, strict=False):
1046
'''Make sure our options are valid'''
1047
if hasattr(opt, 'binary') and opt.binary and not valid_path(opt.binary):
1048
raise AppArmorException("Invalid binary '%s'" % opt.binary)
1049
if hasattr(opt, 'profile_name') and opt.profile_name != None and \
1050
not valid_profile_name(opt.profile_name):
1051
raise AppArmorException("Invalid profile name '%s'" % opt.profile_name)
1052
if hasattr(opt, 'binary') and opt.binary and \
1053
hasattr(opt, 'profile_name') and opt.profile_name != None and \
1054
opt.profile_name.startswith('/'):
1055
raise AppArmorException("Profile name should not specify path with binary")
1056
if hasattr(opt, 'policy_vendor') and opt.policy_vendor and \
1057
not valid_policy_vendor(opt.policy_vendor):
1058
raise AppArmorException("Invalid policy vendor '%s'" % \
1060
if hasattr(opt, 'policy_version') and opt.policy_version and \
1061
not valid_policy_version(opt.policy_version):
1062
raise AppArmorException("Invalid policy version '%s'" % \
1064
if hasattr(opt, 'template') and opt.template and \
1065
not valid_template_name(opt.template, strict):
1066
raise AppArmorException("Invalid template '%s'" % opt.template)
1067
if hasattr(opt, 'template_var') and opt.template_var:
1068
for i in opt.template_var:
1069
if not valid_variable(i):
1070
raise AppArmorException("Invalid variable '%s'" % i)
1071
if hasattr(opt, 'policy_groups') and opt.policy_groups:
1072
for i in opt.policy_groups.split(','):
1073
if not valid_policy_group_name(i):
1074
raise AppArmorException("Invalid policy group '%s'" % i)
1075
if hasattr(opt, 'abstractions') and opt.abstractions:
1076
for i in opt.abstractions.split(','):
1077
if not valid_abstraction_name(i):
1078
raise AppArmorException("Invalid abstraction '%s'" % i)
1079
if hasattr(opt, 'read_paths') and opt.read_paths:
1080
for i in opt.read_paths:
1081
if not valid_path(i):
1082
raise AppArmorException("Invalid read path '%s'" % i)
1083
if hasattr(opt, 'write_paths') and opt.write_paths:
1084
for i in opt.write_paths:
1085
if not valid_path(i):
1086
raise AppArmorException("Invalid write path '%s'" % i)
1089
def verify_manifest(params, args=None):
1090
'''Verify manifest for safe and unsafe options'''
1092
(opt, args) = parse_args(args)
1093
fake_easyp = AppArmorEasyProfile(None, opt)
1095
unsafe_keys = ['read_path', 'write_path']
1096
safe_abstractions = ['base']
1098
debug("Examining %s=%s" % (k, params[k]))
1099
if k in unsafe_keys:
1100
err_str += "\nfound %s key" % k
1101
elif k == 'profile_name':
1102
if params['profile_name'].startswith('/') or \
1103
'*' in params['profile_name']:
1104
err_str += "\nprofile_name '%s'" % params['profile_name']
1105
elif k == 'abstractions':
1106
for a in params['abstractions'].split(','):
1107
if not a in safe_abstractions:
1108
err_str += "\nfound '%s' abstraction" % a
1109
elif k == "template_var":
1110
pat = re.compile(r'[*/\{\}\[\]]')
1111
for tv in params['template_var']:
1112
if not fake_easyp.gen_variable_declaration(tv):
1113
err_str += "\n%s" % tv
1115
tv_val = tv.split('=')[1]
1116
debug("Examining %s" % tv_val)
1117
if '..' in tv_val or pat.search(tv_val):
1118
err_str += "\n%s" % tv
1121
warn("Manifest definition is potentially unsafe%s" % err_str)