Hello,
Am Samstag, 15. November 2014 schrieb Christian Boltz:
> this patch changes aa.py and cleanprof.py to use the new capabiliy
> rule class.
The updated patch (v2) is attached.
Only change: The patch got updated to match the changed class and
function names of the rule class.
This also means all notes from the first mail still apply.
Updated line count statistics:
1-add-base-and-capability-rule-class.diff - 365 lines added, 0 removed
2-add-capability-rule-test.diff - 747 lines added, 0 removed
3-use-capability-rule-class.diff - 62 lines added, 107 removed
Regards,
Christian Boltz
--
You took a sledge hammer to a meeting with the school principle and
found him carring a 9mm. :^) [Patrick Shanahan in opensuse-factory]
=== modified file 'utils/apparmor/aa.py'
--- utils/apparmor/aa.py 2014-11-15 11:51:24 +0000
+++ utils/apparmor/aa.py 2014-11-21 20:37:58 +0000
@@ -52,6 +52,8 @@
import apparmor.rules as aarules
+from apparmor.rule.capability import CapabilityRuleset, CapabilityRule
+
from apparmor.yasti import SendDataToYast, GetDataFromYast, shutdown_yast
# setup module translations
@@ -89,7 +91,7 @@
# To store the globs entered by users so they can be provided again
user_globs = []
-# The key for representing bare rules such as "capability," or "file,"
+# The key for representing bare "file," rules
ALL = '\0ALL'
## Variables used under logprof
@@ -1628,16 +1630,16 @@
if deleted:
aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
- aa[profile][hat]['allow']['capability'][capability]['set'] = True
- aa[profile][hat]['allow']['capability'][capability]['audit'] = audit_toggle
+ else:
+ aa[profile][hat]['capability'].add_raw('%s capability %s,'% (audit, capability)) # XXX yes, ugly ;-)
+ aaui.UI_Info(_('Adding capability %s to profile.') % capability)
changed[profile] = True
- aaui.UI_Info(_('Adding capability %s to profile.') % capability)
done = True
elif ans == 'CMD_DENY':
- aa[profile][hat]['deny']['capability'][capability]['set'] = True
+ aa[profile][hat]['capability'].add_raw('%s deny capability %s,'% (audit, capability)) # XXX yes, ugly ;-)
changed[profile] = True
aaui.UI_Info(_('Denying capability %s to profile.') % capability)
@@ -2121,20 +2123,6 @@
deleted += 1
return deleted
-def delete_cap_duplicates(profilecaps, inccaps):
- deleted = []
- if profilecaps and inccaps:
- for capname in profilecaps.keys():
- # XXX The presence of a bare capability rule ("capability,") should
- # cause more specific capability rules
- # ("capability audit_control,") to be deleted
- if inccaps[capname].get('set', False) == 1:
- deleted.append(capname)
- for capname in deleted:
- profilecaps.pop(capname)
-
- return len(deleted)
-
def delete_path_duplicates(profile, incname, allow):
deleted = []
for entry in profile[allow]['path'].keys():
@@ -2161,9 +2149,7 @@
deleted += delete_net_duplicates(profile['deny']['netdomain'], include[incname][incname]['deny']['netdomain'])
- deleted += delete_cap_duplicates(profile['allow']['capability'], include[incname][incname]['allow']['capability'])
-
- deleted += delete_cap_duplicates(profile['deny']['capability'], include[incname][incname]['deny']['capability'])
+ deleted += profile['capability'].delete_duplicates(include[incname][incname]['capability'])
deleted += delete_path_duplicates(profile, incname, 'allow')
deleted += delete_path_duplicates(profile, incname, 'deny')
@@ -2173,9 +2159,7 @@
deleted += delete_net_duplicates(profile['deny']['netdomain'], filelist[incname][incname]['deny']['netdomain'])
- deleted += delete_cap_duplicates(profile['allow']['capability'], filelist[incname][incname]['allow']['capability'])
-
- deleted += delete_cap_duplicates(profile['deny']['capability'], filelist[incname][incname]['deny']['capability'])
+ deleted += profile['capability'].delete_duplicates(filelist[incname][incname]['capability'])
deleted += delete_path_duplicates(profile, incname, 'allow')
deleted += delete_path_duplicates(profile, incname, 'deny')
@@ -2206,7 +2190,9 @@
def match_cap_includes(profile, cap):
newincludes = []
for incname in include.keys():
- if valid_include(profile, incname) and include[incname][incname]['allow']['capability'][cap].get('set', False) == 1:
+ # XXX type check should go away once we init all profiles correctly
+ # XXX using a CapabilityRule object instead of is_raw_covered() might be nice/faster
+ if valid_include(profile, incname) and type(include[incname][incname]['capability']) == CapabilityRuleset and include[incname][incname]['capability'].is_raw_covered("capability %s," % cap):
newincludes.append(incname)
return newincludes
@@ -2516,7 +2502,8 @@
for capability in prelog[aamode][profile][hat]['capability'].keys():
# If capability not already in profile
- if not aa[profile][hat]['allow']['capability'][capability].get('set', False):
+ # XXX remove first check when we have proper profile initialisation
+ if aa[profile][hat].get('capability', False) and not aa[profile][hat]['capability'].is_raw_covered("capability %s," % capability):
log_dict[aamode][profile][hat]['capability'][capability] = True
nd = prelog[aamode][profile][hat]['netdomain']
@@ -2704,6 +2691,10 @@
profile_data[profile][profile]['repo']['url'] = repo_data['url']
profile_data[profile][profile]['repo']['user'] = repo_data['user']
+ # init rule classes (if not done yet)
+ if not profile_data[profile][hat].get('capability', False):
+ profile_data[profile][hat]['capability'] = CapabilityRuleset()
+
elif RE_PROFILE_END.search(line):
# If profile ends and we're not in one
if not profile:
@@ -2719,21 +2710,14 @@
initial_comment = ''
elif RE_PROFILE_CAP.search(line):
- matches = RE_PROFILE_CAP.search(line)
-
if not profile:
raise AppArmorException(_('Syntax Error: Unexpected capability entry found in file: %(file)s line: %(line)s') % { 'file': file, 'line': lineno + 1 })
- audit, allow, allow_keyword, comment = parse_audit_allow(matches)
- # TODO: honor allow_keyword and comment
-
- capability = ALL
- if matches.group('capability'):
- capability = matches.group('capability').strip()
- # TODO: can contain more than one capability- split it?
-
- profile_data[profile][hat][allow]['capability'][capability]['set'] = True
- profile_data[profile][hat][allow]['capability'][capability]['audit'] = audit
+ # init rule class (if not done yet)
+ if not profile_data[profile][hat].get('capability', False):
+ profile_data[profile][hat]['capability'] = CapabilityRuleset()
+
+ profile_data[profile][hat]['capability'].add_raw(line)
elif RE_PROFILE_LINK.search(line):
matches = RE_PROFILE_LINK.search(line).groups()
@@ -3373,29 +3357,8 @@
def write_list_vars(prof_data, depth):
return write_pair(prof_data, depth, '', 'lvar', '', ' = ', '', var_transform)
-def write_cap_rules(prof_data, depth, allow):
- pre = ' ' * depth
- data = []
- allowstr = set_allow_str(allow)
-
- if prof_data[allow].get('capability', False):
- for cap in sorted(prof_data[allow]['capability'].keys()):
- audit = ''
- if prof_data[allow]['capability'][cap].get('audit', False):
- audit = 'audit '
- if prof_data[allow]['capability'][cap].get('set', False):
- if cap == ALL:
- data.append('%s%s%scapability,' % (pre, audit, allowstr))
- else:
- data.append('%s%s%scapability %s,' % (pre, audit, allowstr, cap))
- data.append('')
-
- return data
-
def write_capabilities(prof_data, depth):
- #data = write_single(prof_data, depth, '', 'set_capability', 'set capability ', ',')
- data = write_cap_rules(prof_data, depth, 'deny')
- data += write_cap_rules(prof_data, depth, 'allow')
+ data = prof_data['capability'].get_clean(depth)
return data
def write_net_rules(prof_data, depth, allow):
@@ -3886,6 +3849,8 @@
write_prof_data[name]['allow'].pop(segs)
if write_prof_data[name]['deny'].get(segs, False):
write_prof_data[name]['deny'].pop(segs)
+ if write_prof_data[name].get(segs, False):
+ write_prof_data[name][segs].delete_all_rules()
# then write everything else
for segs in default_write_order:
@@ -3927,25 +3892,7 @@
profile = None
elif RE_PROFILE_CAP.search(line):
- matches = RE_PROFILE_CAP.search(line).groups()
- audit = False
- if matches[0]:
- audit = matches[0]
-
- allow = 'allow'
- if matches[1] and matches[1].strip() == 'deny':
- allow = 'deny'
-
- capability = ALL
- if matches[2]:
- capability = matches[2].strip()
-
- if not write_prof_data[hat][allow]['capability'][capability].get('set', False):
- correct = False
- if not write_prof_data[hat][allow]['capability'][capability].get(audit, False) == audit:
- correct = False
-
- if correct:
+ if write_prof_data[hat]['capability'].is_raw_covered(line, True, True):
if not segments['capability'] and True in segments.values():
for segs in list(filter(lambda x: segments[x], segments.keys())):
depth = len(line) - len(line.lstrip())
@@ -3955,13 +3902,11 @@
write_prof_data[name]['allow'].pop(segs)
if write_prof_data[name]['deny'].get(segs, False):
write_prof_data[name]['deny'].pop(segs)
+ if write_prof_data[name].get(segs, False):
+ write_prof_data[name][segs].delete_all_rules()
segments['capability'] = True
- write_prof_data[hat][allow]['capability'].pop(capability)
+ write_prof_data[hat]['capability'].delete_raw(line)
data.append(line)
-
- #write_prof_data[hat][allow]['capability'][capability].pop(audit)
-
- #Remove this line
else:
# To-Do
pass
@@ -3996,6 +3941,8 @@
write_prof_data[name]['allow'].pop(segs)
if write_prof_data[name]['deny'].get(segs, False):
write_prof_data[name]['deny'].pop(segs)
+ if write_prof_data[name].get(segs, False):
+ write_prof_data[name][segs].delete_all_rules()
segments['link'] = True
write_prof_data[hat][allow]['link'].pop(link)
data.append(line)
@@ -4020,6 +3967,8 @@
write_prof_data[name]['allow'].pop(segs)
if write_prof_data[name]['deny'].get(segs, False):
write_prof_data[name]['deny'].pop(segs)
+ if write_prof_data[name].get(segs, False):
+ write_prof_data[name][segs].delete_all_rules()
segments['change_profile'] = True
write_prof_data[hat]['change_profile'].pop(cp)
data.append(line)
@@ -4050,6 +3999,8 @@
write_prof_data[name]['allow'].pop(segs)
if write_prof_data[name]['deny'].get(segs, False):
write_prof_data[name]['deny'].pop(segs)
+ if write_prof_data[name].get(segs, False):
+ write_prof_data[name][segs].delete_all_rules()
segments['alias'] = True
if profile:
write_prof_data[hat]['alias'].pop(from_name)
@@ -4079,6 +4030,8 @@
write_prof_data[name]['allow'].pop(segs)
if write_prof_data[name]['deny'].get(segs, False):
write_prof_data[name]['deny'].pop(segs)
+ if write_prof_data[name].get(segs, False):
+ write_prof_data[name][segs].delete_all_rules()
segments['rlimit'] = True
write_prof_data[hat]['rlimit'].pop(from_name)
data.append(line)
@@ -4104,6 +4057,8 @@
write_prof_data[name]['allow'].pop(segs)
if write_prof_data[name]['deny'].get(segs, False):
write_prof_data[name]['deny'].pop(segs)
+ if write_prof_data[name].get(segs, False):
+ write_prof_data[name][segs].delete_all_rules()
segments['lvar'] = True
write_prof_data[hat]['lvar'].pop(bool_var)
data.append(line)
@@ -4135,6 +4090,8 @@
write_prof_data[name]['allow'].pop(segs)
if write_prof_data[name]['deny'].get(segs, False):
write_prof_data[name]['deny'].pop(segs)
+ if write_prof_data[name].get(segs, False):
+ write_prof_data[name][segs].delete_all_rules()
segments['lvar'] = True
if profile:
write_prof_data[hat]['lvar'].pop(list_var)
@@ -4173,6 +4130,8 @@
write_prof_data[name]['allow'].pop(segs)
if write_prof_data[name]['deny'].get(segs, False):
write_prof_data[name]['deny'].pop(segs)
+ if write_prof_data[name].get(segs, False):
+ write_prof_data[name][segs].delete_all_rules()
segments['path'] = True
write_prof_data[hat][allow]['path'].pop(ALL)
data.append(line)
@@ -4221,6 +4180,8 @@
write_prof_data[name]['allow'].pop(segs)
if write_prof_data[name]['deny'].get(segs, False):
write_prof_data[name]['deny'].pop(segs)
+ if write_prof_data[name].get(segs, False):
+ write_prof_data[name][segs].delete_all_rules()
segments['path'] = True
write_prof_data[hat][allow]['path'].pop(path)
data.append(line)
@@ -4241,6 +4202,8 @@
write_prof_data[name]['allow'].pop(segs)
if write_prof_data[name]['deny'].get(segs, False):
write_prof_data[name]['deny'].pop(segs)
+ if write_prof_data[name].get(segs, False):
+ write_prof_data[name][segs].delete_all_rules()
segments['include'] = True
write_prof_data[hat]['include'].pop(include_name)
data.append(line)
@@ -4294,6 +4257,8 @@
write_prof_data[name]['allow'].pop(segs)
if write_prof_data[name]['deny'].get(segs, False):
write_prof_data[name]['deny'].pop(segs)
+ if write_prof_data[name].get(segs, False):
+ write_prof_data[name][segs].delete_all_rules()
segments['netdomain'] = True
elif RE_PROFILE_CHANGE_HAT.search(line):
@@ -4408,19 +4373,20 @@
return 0
def profile_known_capability(profile, capname):
- if profile['deny']['capability'][capname].get('set', False):
- return -1
+ rule = CapabilityRule()
+ rule.set_raw('capability %s,' % capname) # having a CapabilityRule object as parameter would be even better ;-)
- if profile['allow']['capability'][capname].get('set', False):
- return 1
+ # XXX get rid of type checks after we have a proper function to initialize a profile
+ if type(profile['capability']) == CapabilityRuleset:
+ if profile['capability'].is_obj_covered(rule, False):
+ return True
for incname in profile['include'].keys():
- if include[incname][incname]['deny']['capability'][capname].get('set', False):
- return -1
- if include[incname][incname]['allow']['capability'][capname].get('set', False):
- return 1
+ if type(include[incname][incname]['capability']) == CapabilityRuleset:
+ if include[incname][incname]['capability'].is_obj_covered(rule, False):
+ return True
- return 0
+ return False
def profile_known_network(profile, family, sock_type):
if netrules_access_check(profile['deny']['netdomain'], family, sock_type):
=== modified file 'utils/apparmor/cleanprofile.py'
--- utils/apparmor/cleanprofile.py 2014-09-05 21:21:00 +0000
+++ utils/apparmor/cleanprofile.py 2014-11-21 19:08:06 +0000
@@ -65,8 +65,8 @@
deleted += apparmor.aa.delete_duplicates(self.other.aa[program][hat], inc)
#Clean the duplicates of caps in other profile
- deleted += delete_cap_duplicates(self.profile.aa[program][hat]['allow']['capability'], self.other.aa[program][hat]['allow']['capability'], self.same_file)
- deleted += delete_cap_duplicates(self.profile.aa[program][hat]['deny']['capability'], self.other.aa[program][hat]['deny']['capability'], self.same_file)
+ if self.same_file:
+ deleted += self.other.aa[program][hat]['capability'].delete_duplicates(self.profile.aa[program][hat]['capability'])
#Clean the duplicates of path in other profile
deleted += delete_path_duplicates(self.profile.aa[program][hat], self.other.aa[program][hat], 'allow', self.same_file)
@@ -108,17 +108,6 @@
return len(deleted)
-def delete_cap_duplicates(profilecaps, profilecaps_other, same_profile=True):
- deleted = []
- if profilecaps and profilecaps_other and not same_profile:
- for capname in profilecaps.keys():
- if profilecaps_other[capname].get('set', False):
- deleted.append(capname)
- for capname in deleted:
- profilecaps_other.pop(capname)
-
- return len(deleted)
-
def delete_net_duplicates(netrules, netrules_other, same_profile=True):
deleted = 0
hasher_obj = apparmor.aa.hasher()
--
AppArmor mailing list
[email protected]
Modify settings or unsubscribe at:
https://lists.ubuntu.com/mailman/listinfo/apparmor