sepolicy: Make manpage and transition faster

SETools4 and Python 3 versions of map() and filter() uses iterators to
generates query results and these iterators can't be imply re-used. It
makes manpage and transitions operations really slow as they do lot of queries.

This patch changes it in the way that it caches results in lists for all
types, allow rules and transitions first and use cached results to
filter them using Python's filter() function.

Before:
$ time sepolicy manpage -d httpd_t sshd_t init_t
real    0m53.486s
user    0m53.171s
sys     0m0.054s

After:
$ time sepolicy manpage -d httpd_t sshd_t init_t
real    0m10.532s
user    0m10.368s
sys     0m0.114s

Signed-off-by: Petr Lautrbach <plautrba@redhat.com>
This commit is contained in:
Petr Lautrbach 2017-07-27 15:10:52 +02:00 committed by Stephen Smalley
parent c624c4abaa
commit 960b943b2f
3 changed files with 73 additions and 26 deletions

View file

@ -99,6 +99,7 @@ local_files = None
fcdict = None fcdict = None
methods = [] methods = []
all_types = None all_types = None
all_types_info = None
user_types = None user_types = None
role_allows = None role_allows = None
portrecs = None portrecs = None
@ -113,6 +114,8 @@ bools = None
all_attributes = None all_attributes = None
booleans = None booleans = None
booleans_dict = None booleans_dict = None
all_allow_rules = None
all_transitions = None
def get_installed_policy(root="/"): def get_installed_policy(root="/"):
@ -168,10 +171,10 @@ def info(setype, name=None):
q.name = name q.name = name
return ({ return ({
'aliases': map(str, x.aliases()), 'aliases': list(map(str, x.aliases())),
'name': str(x), 'name': str(x),
'permissive': bool(x.ispermissive), 'permissive': bool(x.ispermissive),
'attributes': map(str, x.attributes()) 'attributes': list(map(str, x.attributes()))
} for x in q.results()) } for x in q.results())
elif setype == ROLE: elif setype == ROLE:
@ -181,8 +184,8 @@ def info(setype, name=None):
return ({ return ({
'name': str(x), 'name': str(x),
'roles': map(str, x.expand()), 'roles': list(map(str, x.expand())),
'types': map(str, x.types()), 'types': list(map(str, x.types())),
} for x in q.results()) } for x in q.results())
elif setype == ATTRIBUTE: elif setype == ATTRIBUTE:
@ -192,7 +195,7 @@ def info(setype, name=None):
return ({ return ({
'name': str(x), 'name': str(x),
'types': map(str, x.expand()), 'types': list(map(str, x.expand())),
} for x in q.results()) } for x in q.results())
elif setype == PORT: elif setype == PORT:
@ -220,7 +223,7 @@ def info(setype, name=None):
return ({ return ({
'range': str(x.mls_range), 'range': str(x.mls_range),
'name': str(x), 'name': str(x),
'roles': map(str, x.roles), 'roles': list(map(str, x.roles)),
'level': str(x.mls_level), 'level': str(x.mls_level),
} for x in q.results()) } for x in q.results())
@ -362,17 +365,26 @@ def search(types, seinfo=None):
def get_conditionals(src, dest, tclass, perm): def get_conditionals(src, dest, tclass, perm):
tdict = {} tdict = {}
tlist = [] tlist = []
if dest.endswith("_t"): src_list = [src]
allows = search([ALLOW], {SOURCE: src, TARGET: dest, CLASS: tclass, PERMS: perm}) dest_list = [dest]
else: # add assigned attributes
# to include attribute
allows = search([ALLOW], {SOURCE: src, CLASS: tclass, PERMS: perm})
for i in allows:
if i['target'] == dest:
allows = []
allows.append(i)
try: try:
for i in map(lambda y: (y), filter(lambda x: set(perm).issubset(x[PERMS]) and x['boolean'], allows)): src_list += list(filter(lambda x: x['name'] == src, get_all_types_info()))[0]['attributes']
except:
pass
try:
dest_list += list(filter(lambda x: x['name'] == dest, get_all_types_info()))[0]['attributes']
except:
pass
allows = map(lambda y: y, filter(lambda x:
x['source'] in src_list and
x['target'] in dest_list and
set(perm).issubset(x[PERMS]) and
'boolean' in x,
get_all_allow_rules()))
try:
for i in allows:
tdict.update({'source': i['source'], 'boolean': i['boolean']}) tdict.update({'source': i['source'], 'boolean': i['boolean']})
if tdict not in tlist: if tdict not in tlist:
tlist.append(tdict) tlist.append(tdict)
@ -734,6 +746,11 @@ def get_all_types():
all_types = [x['name'] for x in info(TYPE)] all_types = [x['name'] for x in info(TYPE)]
return all_types return all_types
def get_all_types_info():
global all_types_info
if all_types_info is None:
all_types_info = list(info(TYPE))
return all_types_info
def get_user_types(): def get_user_types():
global user_types global user_types
@ -1018,12 +1035,23 @@ def gen_short_name(setype):
short_name = domainname + "_" short_name = domainname + "_"
return (domainname, short_name) return (domainname, short_name)
def get_all_allow_rules():
global all_allow_rules
if not all_allow_rules:
all_allow_rules = search([ALLOW])
return all_allow_rules
def get_all_transitions():
global all_transitions
if not all_transitions:
all_transitions = list(search([TRANSITION]))
return all_transitions
def get_bools(setype): def get_bools(setype):
bools = [] bools = []
domainbools = [] domainbools = []
domainname, short_name = gen_short_name(setype) domainname, short_name = gen_short_name(setype)
for i in map(lambda x: x['boolean'], filter(lambda x: 'boolean' in x, search([ALLOW], {'source': setype}))): for i in map(lambda x: x['boolean'], filter(lambda x: 'boolean' in x and x['source'] == setype, get_all_allow_rules())):
for b in i: for b in i:
if not isinstance(b, tuple): if not isinstance(b, tuple):
continue continue

View file

@ -938,7 +938,11 @@ selinux(8), %s(8), semanage(8), restorecon(8), chcon(1), sepolicy(8)
return True return True
def _entrypoints(self): def _entrypoints(self):
entrypoints = [x['target'] for x in sepolicy.search([sepolicy.ALLOW], {'source': self.type, 'permlist': ['entrypoint'], 'class': 'file'})] entrypoints = [x['target'] for x in filter(lambda y:
y['source'] == self.type and y['class'] == 'file' and 'entrypoint' in y['permlist'],
sepolicy.get_all_allow_rules()
)]
if len(entrypoints) == 0: if len(entrypoints) == 0:
return return
@ -980,15 +984,23 @@ For example one process might be launched with %(type)s_t:s0:c1,c2, and another
""" % {'type': self.domainname}) """ % {'type': self.domainname})
def _writes(self): def _writes(self):
permlist = sepolicy.search([sepolicy.ALLOW], {'source': self.type, 'permlist': ['open', 'write'], 'class': 'file'}) # add assigned attributes
src_list = [self.type]
try:
src_list += list(filter(lambda x: x['name'] == self.type, sepolicy.get_all_types_info()))[0]['attributes']
except:
pass
permlist = list(filter(lambda x:
x['source'] in src_list and
set(['open', 'write']).issubset(x['permlist']) and
x['class'] == 'file',
sepolicy.get_all_allow_rules()))
if permlist is None or len(permlist) == 0: if permlist is None or len(permlist) == 0:
return return
all_writes = [] all_writes = []
attributes = ["proc_type", "sysctl_type"] attributes = ["proc_type", "sysctl_type"]
for i in permlist:
if not i['target'].endswith("_t"):
attributes.append(i['target'])
for i in permlist: for i in permlist:
if self._valid_write(i['target'], attributes): if self._valid_write(i['target'], attributes):
@ -1187,7 +1199,12 @@ The SELinux user %s_u is able to connect to the following tcp ports.
""" % ",".join(ports)) """ % ",".join(ports))
def _home_exec(self): def _home_exec(self):
permlist = sepolicy.search([sepolicy.ALLOW], {'source': self.type, 'target': 'user_home_type', 'class': 'file', 'permlist': ['ioctl', 'read', 'getattr', 'execute', 'execute_no_trans', 'open']}) permlist = list(filter(lambda x:
x['source'] == self.type and
x['target'] == 'user_home_type' and
x['class'] == 'file' and
set(['ioctl', 'read', 'getattr', 'execute', 'execute_no_trans', 'open']).issubset(set(x['permlist'])),
sepolicy.get_all_allow_rules()))
self.fd.write(""" self.fd.write("""
.SH HOME_EXEC .SH HOME_EXEC
""") """)

View file

@ -30,7 +30,9 @@ def _entrypoint(src):
def _get_trans(src): def _get_trans(src):
return sepolicy.search([sepolicy.TRANSITION], {sepolicy.SOURCE: src, sepolicy.CLASS: "process"}) src_list = [src] + list(filter(lambda x: x['name'] == src, sepolicy.get_all_types_info()))[0]['attributes']
trans_list = list(filter(lambda x: x['source'] in src_list and x['class'] == 'process', sepolicy.get_all_transitions()))
return trans_list
class setrans: class setrans:
@ -53,8 +55,8 @@ class setrans:
if not self.dest: if not self.dest:
self.sdict[source]["map"] = trans self.sdict[source]["map"] = trans
else: else:
self.sdict[source]["map"] = map(lambda y: y, filter(lambda x: x["transtype"] == self.dest, trans)) self.sdict[source]["map"] = list(map(lambda y: y, filter(lambda x: x["transtype"] == self.dest, trans)))
self.sdict[source]["child"] = map(lambda y: y["transtype"], filter(lambda x: x["transtype"] not in [self.dest, source], trans)) self.sdict[source]["child"] = list(map(lambda y: y["transtype"], filter(lambda x: x["transtype"] not in [self.dest, source], trans)))
for s in self.sdict[source]["child"]: for s in self.sdict[source]["child"]:
self._process(s) self._process(s)