1#!/usr/bin/python
2
3# Author: Dan Walsh <dwalsh@redhat.com>
4# Author: Ryan Hallisey <rhallise@redhat.com>
5# Author: Jason Zaman <perfinion@gentoo.org>
6
7import selinux
8import setools
9import glob
10import sepolgen.defaults as defaults
11import sepolgen.interfaces as interfaces
12import sys
13import os
14import re
15import gzip
16
17PROGNAME = "policycoreutils"
18try:
19    import gettext
20    kwargs = {}
21    if sys.version_info < (3,):
22        kwargs['unicode'] = True
23    gettext.install(PROGNAME,
24                    localedir="/usr/share/locale",
25                    codeset='utf-8',
26                    **kwargs)
27except:
28    try:
29        import builtins
30        builtins.__dict__['_'] = str
31    except ImportError:
32        import __builtin__
33        __builtin__.__dict__['_'] = unicode
34
35TYPE = 1
36ROLE = 2
37ATTRIBUTE = 3
38PORT = 4
39USER = 5
40BOOLEAN = 6
41TCLASS = 7
42
43ALLOW = 'allow'
44AUDITALLOW = 'auditallow'
45NEVERALLOW = 'neverallow'
46DONTAUDIT = 'dontaudit'
47SOURCE = 'source'
48TARGET = 'target'
49PERMS = 'permlist'
50CLASS = 'class'
51TRANSITION = 'transition'
52ROLE_ALLOW = 'role_allow'
53
54
55# Autofill for adding files *************************
56DEFAULT_DIRS = {}
57DEFAULT_DIRS["/etc"] = "etc_t"
58DEFAULT_DIRS["/tmp"] = "tmp_t"
59DEFAULT_DIRS["/usr/lib/systemd/system"] = "unit_file_t"
60DEFAULT_DIRS["/lib/systemd/system"] = "unit_file_t"
61DEFAULT_DIRS["/etc/systemd/system"] = "unit_file_t"
62DEFAULT_DIRS["/var/cache"] = "var_cache_t"
63DEFAULT_DIRS["/var/lib"] = "var_lib_t"
64DEFAULT_DIRS["/var/log"] = "log_t"
65DEFAULT_DIRS["/var/run"] = "var_run_t"
66DEFAULT_DIRS["/run"] = "var_run_t"
67DEFAULT_DIRS["/run/lock"] = "var_lock_t"
68DEFAULT_DIRS["/var/run/lock"] = "var_lock_t"
69DEFAULT_DIRS["/var/spool"] = "var_spool_t"
70DEFAULT_DIRS["/var/www"] = "content_t"
71
72file_type_str = {}
73file_type_str["a"] = _("all files")
74file_type_str["f"] = _("regular file")
75file_type_str["d"] = _("directory")
76file_type_str["c"] = _("character device")
77file_type_str["b"] = _("block device")
78file_type_str["s"] = _("socket file")
79file_type_str["l"] = _("symbolic link")
80file_type_str["p"] = _("named pipe")
81
82trans_file_type_str = {}
83trans_file_type_str[""] = "a"
84trans_file_type_str["--"] = "f"
85trans_file_type_str["-d"] = "d"
86trans_file_type_str["-c"] = "c"
87trans_file_type_str["-b"] = "b"
88trans_file_type_str["-s"] = "s"
89trans_file_type_str["-l"] = "l"
90trans_file_type_str["-p"] = "p"
91
92# the setools policy handle
93_pol = None
94
95# cache the lookup results
96file_equiv_modified = None
97file_equiv = None
98local_files = None
99fcdict = None
100methods = []
101all_types = None
102user_types = None
103role_allows = None
104portrecs = None
105portrecsbynum = None
106all_domains = None
107roles = None
108selinux_user_list = None
109login_mappings = None
110file_types = None
111port_types = None
112bools = None
113all_attributes = None
114booleans = None
115booleans_dict = None
116
117
118def get_installed_policy(root="/"):
119    try:
120        path = root + selinux.selinux_binary_policy_path()
121        policies = glob.glob("%s.*" % path)
122        policies.sort()
123        return policies[-1]
124    except:
125        pass
126    raise ValueError(_("No SELinux Policy installed"))
127
128
129def policy(policy_file):
130    global all_domains
131    global all_attributes
132    global bools
133    global all_types
134    global role_allows
135    global users
136    global roles
137    global file_types
138    global port_types
139    all_domains = None
140    all_attributes = None
141    bools = None
142    all_types = None
143    role_allows = None
144    users = None
145    roles = None
146    file_types = None
147    port_types = None
148    global _pol
149
150    try:
151        _pol = setools.SELinuxPolicy(policy_file)
152    except:
153        raise ValueError(_("Failed to read %s policy file") % policy_file)
154
155
156try:
157    policy_file = get_installed_policy()
158    policy(policy_file)
159except ValueError as e:
160    if selinux.is_selinux_enabled() == 1:
161        raise e
162
163
164def info(setype, name=None):
165    if setype == TYPE:
166        q = setools.TypeQuery(_pol)
167        if name:
168            q.name = name
169
170        return ({
171            'aliases': map(str, x.aliases()),
172            'name': str(x),
173            'permissive': bool(x.ispermissive),
174        } for x in q.results())
175
176    elif setype == ROLE:
177        q = setools.RoleQuery(_pol)
178        if name:
179            q.name = name
180
181        return ({
182            'name': str(x),
183            'roles': map(str, x.expand()),
184            'types': map(str, x.types()),
185        } for x in q.results())
186
187    elif setype == ATTRIBUTE:
188        q = setools.TypeAttributeQuery(_pol)
189        if name:
190            q.name = name
191
192        return ({
193            'name': str(x),
194            'types': map(str, x.expand()),
195        } for x in q.results())
196
197    elif setype == PORT:
198        q = setools.PortconQuery(_pol)
199        if name:
200            ports = [int(i) for i in name.split("-")]
201            if len(ports) == 2:
202                q.ports = ports
203            elif len(ports) == 1:
204                q.ports = (ports[0], ports[0])
205
206        return ({
207            'high': x.ports.high,
208            'protocol': str(x.protocol),
209            'range': str(x.context.range_),
210            'type': str(x.context.type_),
211            'low': x.ports.low,
212        } for x in q.results())
213
214    elif setype == USER:
215        q = setools.UserQuery(_pol)
216        if name:
217            q.name = name
218
219        return ({
220            'range': str(x.mls_range),
221            'name': str(x),
222            'roles': map(str, x.roles),
223            'level': str(x.mls_level),
224        } for x in q.results())
225
226    elif setype == BOOLEAN:
227        q = setools.BoolQuery(_pol)
228        if name:
229            q.name = name
230
231        return ({
232            'name': str(x),
233            'state': x.state,
234        } for x in q.results())
235
236    elif setype == TCLASS:
237        q = setools.ObjClassQuery(_pol)
238        if name:
239            q.name = name
240
241        return ({
242            'name': str(x),
243            'permlist': list(x.perms),
244        } for x in q.results())
245
246    else:
247        raise ValueError("Invalid type")
248
249
250def _setools_rule_to_dict(rule):
251    d = {
252        'type': str(rule.ruletype),
253        'source': str(rule.source),
254        'target': str(rule.target),
255        'class': str(rule.tclass),
256    }
257
258    try:
259        enabled = bool(rule.qpol_symbol.is_enabled(rule.policy))
260    except AttributeError:
261        enabled = True
262
263    if isinstance(rule, setools.policyrep.terule.AVRule):
264        d['enabled'] = enabled
265
266    try:
267        d['permlist'] = list(map(str, rule.perms))
268    except setools.policyrep.exception.RuleUseError:
269        pass
270
271    try:
272        d['transtype'] = str(rule.default)
273    except setools.policyrep.exception.RuleUseError:
274        pass
275
276    try:
277        d['boolean'] = [(str(rule.conditional), enabled)]
278    except (AttributeError, setools.policyrep.exception.RuleNotConditional):
279        pass
280
281    try:
282        d['filename'] = rule.filename
283    except (AttributeError,
284            setools.policyrep.exception.RuleNotConditional,
285            setools.policyrep.exception.TERuleNoFilename):
286        pass
287
288    return d
289
290
291def search(types, seinfo=None):
292    if not seinfo:
293        seinfo = {}
294    valid_types = set([ALLOW, AUDITALLOW, NEVERALLOW, DONTAUDIT, TRANSITION, ROLE_ALLOW])
295    for setype in types:
296        if setype not in valid_types:
297            raise ValueError("Type has to be in %s" % " ".join(valid_types))
298
299    source = None
300    if SOURCE in seinfo:
301        source = str(seinfo[SOURCE])
302
303    target = None
304    if TARGET in seinfo:
305        target = str(seinfo[TARGET])
306
307    tclass = None
308    if CLASS in seinfo:
309        tclass = str(seinfo[CLASS]).split(',')
310
311    toret = []
312
313    tertypes = []
314    if ALLOW in types:
315        tertypes.append(ALLOW)
316    if NEVERALLOW in types:
317        tertypes.append(NEVERALLOW)
318    if AUDITALLOW in types:
319        tertypes.append(AUDITALLOW)
320
321    if len(tertypes) > 0:
322        q = setools.TERuleQuery(_pol,
323                                ruletype=tertypes,
324                                source=source,
325                                target=target,
326                                tclass=tclass)
327
328        if PERMS in seinfo:
329            q.perms = seinfo[PERMS]
330
331        toret += [_setools_rule_to_dict(x) for x in q.results()]
332
333    if TRANSITION in types:
334        rtypes = ['type_transition', 'type_change', 'type_member']
335        q = setools.TERuleQuery(_pol,
336                                ruletype=rtypes,
337                                source=source,
338                                target=target,
339                                tclass=tclass)
340
341        if PERMS in seinfo:
342            q.perms = seinfo[PERMS]
343
344        toret += [_setools_rule_to_dict(x) for x in q.results()]
345
346    if ROLE_ALLOW in types:
347        ratypes = ['allow']
348        q = setools.RBACRuleQuery(_pol,
349                                  ruletype=ratypes,
350                                  source=source,
351                                  target=target,
352                                  tclass=tclass)
353
354        for r in q.results():
355            toret.append({'source': str(r.source),
356                          'target': str(r.target)})
357
358    return toret
359
360
361def get_conditionals(src, dest, tclass, perm):
362    tdict = {}
363    tlist = []
364    if dest.endswith("_t"):
365        allows = search([ALLOW], {SOURCE: src, TARGET: dest, CLASS: tclass, PERMS: perm})
366    else:
367        # to include attribute
368        allows = search([ALLOW], {SOURCE: src, CLASS: tclass, PERMS: perm})
369        for i in allows:
370            if i['target'] == dest:
371                allows = []
372                allows.append(i)
373    try:
374        for i in map(lambda y: (y), filter(lambda x: set(perm).issubset(x[PERMS]) and x['boolean'], allows)):
375            tdict.update({'source': i['source'], 'boolean': i['boolean']})
376            if tdict not in tlist:
377                tlist.append(tdict)
378                tdict = {}
379    except KeyError:
380        return(tlist)
381
382    return (tlist)
383
384
385def get_conditionals_format_text(cond):
386    enabled = len(filter(lambda x: x['boolean'][0][1], cond)) > 0
387    return _("-- Allowed %s [ %s ]") % (enabled, " || ".join(set(map(lambda x: "%s=%d" % (x['boolean'][0][0], x['boolean'][0][1]), cond))))
388
389
390def get_types_from_attribute(attribute):
391    return list(info(ATTRIBUTE, attribute))[0]["types"]
392
393
394def get_file_types(setype):
395    flist = []
396    mpaths = {}
397    for f in get_all_file_types():
398        if f.startswith(gen_short_name(setype)):
399            flist.append(f)
400    fcdict = get_fcdict()
401    for f in flist:
402        try:
403            mpaths[f] = (fcdict[f]["regex"], file_type_str[fcdict[f]["ftype"]])
404        except KeyError:
405            mpaths[f] = []
406    return mpaths
407
408
409def get_writable_files(setype):
410    file_types = get_all_file_types()
411    all_writes = []
412    mpaths = {}
413    permlist = search([ALLOW], {'source': setype, 'permlist': ['open', 'write'], 'class': 'file'})
414    if permlist is None or len(permlist) == 0:
415        return mpaths
416
417    fcdict = get_fcdict()
418
419    attributes = ["proc_type", "sysctl_type"]
420    for i in permlist:
421        if i['target'] in attributes:
422            continue
423        if "enabled" in i:
424            if not i["enabled"]:
425                continue
426        if i['target'].endswith("_t"):
427            if i['target'] not in file_types:
428                continue
429            if i['target'] not in all_writes:
430                if i['target'] != setype:
431                    all_writes.append(i['target'])
432        else:
433            for t in get_types_from_attribute(i['target']):
434                if t not in all_writes:
435                    all_writes.append(t)
436
437    for f in all_writes:
438        try:
439            mpaths[f] = (fcdict[f]["regex"], file_type_str[fcdict[f]["ftype"]])
440        except KeyError:
441            mpaths[f] = []  # {"regex":[],"paths":[]}
442    return mpaths
443
444
445def find_file(reg):
446    if os.path.exists(reg):
447        return [reg]
448    try:
449        pat = re.compile(r"%s$" % reg)
450    except:
451        print("bad reg:", reg)
452        return []
453    p = reg
454    if p.endswith("(/.*)?"):
455        p = p[:-6] + "/"
456
457    path = os.path.dirname(p)
458
459    try:                       # Bug fix: when "all files on system"
460        if path[-1] != "/":    # is pass in it breaks without try block
461            path += "/"
462    except IndexError:
463        print("try failed got an IndexError")
464        pass
465
466    try:
467        pat = re.compile(r"%s$" % reg)
468        return filter(pat.match, map(lambda x: path + x, os.listdir(path)))
469    except:
470        return []
471
472
473def find_all_files(domain, exclude_list=[]):
474    executable_files = get_entrypoints(domain)
475    for exe in executable_files.keys():
476        if exe.endswith("_exec_t") and exe not in exclude_list:
477            for path in executable_files[exe]:
478                for f in find_file(path):
479                    return f
480    return None
481
482
483def find_entrypoint_path(exe, exclude_list=[]):
484    fcdict = get_fcdict()
485    try:
486        if exe.endswith("_exec_t") and exe not in exclude_list:
487            for path in fcdict[exe]["regex"]:
488                for f in find_file(path):
489                    return f
490    except KeyError:
491        pass
492    return None
493
494
495def read_file_equiv(edict, fc_path, modify):
496    fd = open(fc_path, "r")
497    fc = fd.readlines()
498    fd.close()
499    for e in fc:
500        f = e.split()
501        edict[f[0]] = {"equiv": f[1], "modify": modify}
502    return edict
503
504
505def get_file_equiv_modified(fc_path=selinux.selinux_file_context_path()):
506    global file_equiv_modified
507    if file_equiv_modified:
508        return file_equiv_modified
509    file_equiv_modified = {}
510    file_equiv_modified = read_file_equiv(file_equiv_modified, fc_path + ".subs", modify=True)
511    return file_equiv_modified
512
513
514def get_file_equiv(fc_path=selinux.selinux_file_context_path()):
515    global file_equiv
516    if file_equiv:
517        return file_equiv
518    file_equiv = get_file_equiv_modified(fc_path)
519    file_equiv = read_file_equiv(file_equiv, fc_path + ".subs_dist", modify=False)
520    return file_equiv
521
522
523def get_local_file_paths(fc_path=selinux.selinux_file_context_path()):
524    global local_files
525    if local_files:
526        return local_files
527    local_files = []
528    fd = open(fc_path + ".local", "r")
529    fc = fd.readlines()
530    fd.close()
531    for i in fc:
532        rec = i.split()
533        if len(rec) == 0:
534            continue
535        try:
536            if len(rec) > 2:
537                ftype = trans_file_type_str[rec[1]]
538            else:
539                ftype = "a"
540
541            local_files.append((rec[0], ftype))
542        except KeyError:
543            pass
544    return local_files
545
546
547def get_fcdict(fc_path=selinux.selinux_file_context_path()):
548    global fcdict
549    if fcdict:
550        return fcdict
551    fd = open(fc_path, "r")
552    fc = fd.readlines()
553    fd.close()
554    fd = open(fc_path + ".homedirs", "r")
555    fc += fd.readlines()
556    fd.close()
557    fcdict = {}
558    fd = open(fc_path + ".local", "r")
559    fc += fd.readlines()
560    fd.close()
561
562    for i in fc:
563        rec = i.split()
564        try:
565            if len(rec) > 2:
566                ftype = trans_file_type_str[rec[1]]
567            else:
568                ftype = "a"
569
570            t = rec[-1].split(":")[2]
571            if t in fcdict:
572                fcdict[t]["regex"].append(rec[0])
573            else:
574                fcdict[t] = {"regex": [rec[0]], "ftype": ftype}
575        except:
576            pass
577
578    fcdict["logfile"] = {"regex": ["all log files"]}
579    fcdict["user_tmp_type"] = {"regex": ["all user tmp files"]}
580    fcdict["user_home_type"] = {"regex": ["all user home files"]}
581    fcdict["virt_image_type"] = {"regex": ["all virtual image files"]}
582    fcdict["noxattrfs"] = {"regex": ["all files on file systems which do not support extended attributes"]}
583    fcdict["sandbox_tmpfs_type"] = {"regex": ["all sandbox content in tmpfs file systems"]}
584    fcdict["user_tmpfs_type"] = {"regex": ["all user content in tmpfs file systems"]}
585    fcdict["file_type"] = {"regex": ["all files on the system"]}
586    fcdict["samba_share_t"] = {"regex": ["use this label for random content that will be shared using samba"]}
587    return fcdict
588
589
590def get_transitions_into(setype):
591    try:
592        return filter(lambda x: x["transtype"] == setype, search([TRANSITION], {'class': 'process'}))
593    except (TypeError, AttributeError):
594        pass
595    return None
596
597
598def get_transitions(setype):
599    try:
600        return search([TRANSITION], {'source': setype, 'class': 'process'})
601    except (TypeError, AttributeError):
602        pass
603    return None
604
605
606def get_file_transitions(setype):
607    try:
608        return filter(lambda x: x['class'] != "process", search([TRANSITION], {'source': setype}))
609    except (TypeError, AttributeError):
610        pass
611    return None
612
613
614def get_boolean_rules(setype, boolean):
615    boollist = []
616    permlist = search([ALLOW], {'source': setype})
617    for p in permlist:
618        if "boolean" in p:
619            try:
620                for b in p["boolean"]:
621                    if boolean in b:
622                        boollist.append(p)
623            except:
624                pass
625    return boollist
626
627
628def get_all_entrypoints():
629    return get_types_from_attribute("entry_type")
630
631
632def get_entrypoint_types(setype):
633    q = setools.TERuleQuery(_pol,
634                            ruletype=[ALLOW],
635                            source=setype,
636                            tclass=["file"],
637                            perms=["entrypoint"])
638    return [str(x.target) for x in q.results() if x.source == setype]
639
640
641def get_init_transtype(path):
642    entrypoint = selinux.getfilecon(path)[1].split(":")[2]
643    try:
644        entrypoints = list(filter(lambda x: x['target'] == entrypoint, search([TRANSITION], {'source': "init_t", 'class': 'process'})))
645        return entrypoints[0]["transtype"]
646    except (TypeError, AttributeError, IndexError):
647        pass
648    return None
649
650
651def get_init_entrypoint(transtype):
652    q = setools.TERuleQuery(_pol,
653                            ruletype=["type_transition"],
654                            source="init_t",
655                            tclass=["process"])
656    entrypoints = []
657    for i in q.results():
658        try:
659            if i.default == transtype:
660                entrypoints.append(i.target)
661        except AttributeError:
662            continue
663
664    return entrypoints
665
666def get_init_entrypoints_str():
667    q = setools.TERuleQuery(_pol,
668                            ruletype=["type_transition"],
669                            source="init_t",
670                            tclass=["process"])
671    entrypoints = {}
672    for i in q.results():
673        try:
674            transtype = str(i.default)
675            if transtype in entrypoints:
676                entrypoints[transtype].append(str(i.target))
677            else:
678                entrypoints[transtype] = [str(i.target)]
679        except AttributeError:
680            continue
681
682    return entrypoints
683
684def get_init_entrypoint_target(entrypoint):
685    try:
686        entrypoints = map(lambda x: x['transtype'], search([TRANSITION], {'source': "init_t", 'target': entrypoint, 'class': 'process'}))
687        return list(entrypoints)[0]
688    except (TypeError, IndexError):
689        pass
690    return None
691
692
693def get_entrypoints(setype):
694    fcdict = get_fcdict()
695    mpaths = {}
696    for f in get_entrypoint_types(setype):
697        try:
698            mpaths[f] = (fcdict[f]["regex"], file_type_str[fcdict[f]["ftype"]])
699        except KeyError:
700            mpaths[f] = []
701    return mpaths
702
703
704def get_methods():
705    global methods
706    if len(methods) > 0:
707        return methods
708    gen_interfaces()
709    fn = defaults.interface_info()
710    try:
711        fd = open(fn)
712    # List of per_role_template interfaces
713        ifs = interfaces.InterfaceSet()
714        ifs.from_file(fd)
715        methods = list(ifs.interfaces.keys())
716        fd.close()
717    except:
718        sys.stderr.write("could not open interface info [%s]\n" % fn)
719        sys.exit(1)
720
721    methods.sort()
722    return methods
723
724
725def get_all_types():
726    global all_types
727    if all_types is None:
728        all_types = [x['name'] for x in info(TYPE)]
729    return all_types
730
731
732def get_user_types():
733    global user_types
734    if user_types is None:
735        user_types = list(list(info(ATTRIBUTE, "userdomain"))[0]["types"])
736    return user_types
737
738
739def get_all_role_allows():
740    global role_allows
741    if role_allows:
742        return role_allows
743    role_allows = {}
744
745    q = setools.RBACRuleQuery(_pol, ruletype='allow')
746    for r in q.results():
747        src = str(r.source)
748        tgt = str(r.target)
749        if src == "system_r" or tgt == "system_r":
750            continue
751        if src in role_allows:
752            role_allows[src].append(tgt)
753        else:
754            role_allows[src] = [tgt]
755
756    return role_allows
757
758
759def get_all_entrypoint_domains():
760    import re
761    all_domains = []
762    types = sorted(get_all_types())
763    for i in types:
764        m = re.findall("(.*)%s" % "_exec_t$", i)
765        if len(m) > 0:
766            if len(re.findall("(.*)%s" % "_initrc$", m[0])) == 0 and m[0] not in all_domains:
767                all_domains.append(m[0])
768    return all_domains
769
770
771def gen_interfaces():
772    try:
773        from commands import getstatusoutput
774    except ImportError:
775        from subprocess import getstatusoutput
776    ifile = defaults.interface_info()
777    headers = defaults.headers()
778    try:
779        if os.stat(headers).st_mtime <= os.stat(ifile).st_mtime:
780            return
781    except OSError:
782        pass
783
784    if os.getuid() != 0:
785        raise ValueError(_("You must regenerate interface info by running /usr/bin/sepolgen-ifgen"))
786    print(getstatusoutput("/usr/bin/sepolgen-ifgen")[1])
787
788
789def gen_port_dict():
790    global portrecs
791    global portrecsbynum
792    if portrecs:
793        return (portrecs, portrecsbynum)
794    portrecsbynum = {}
795    portrecs = {}
796    for i in info(PORT):
797        if i['low'] == i['high']:
798            port = str(i['low'])
799        else:
800            port = "%s-%s" % (str(i['low']), str(i['high']))
801
802        if (i['type'], i['protocol']) in portrecs:
803            portrecs[(i['type'], i['protocol'])].append(port)
804        else:
805            portrecs[(i['type'], i['protocol'])] = [port]
806
807        if 'range' in i:
808            portrecsbynum[(i['low'], i['high'], i['protocol'])] = (i['type'], i['range'])
809        else:
810            portrecsbynum[(i['low'], i['high'], i['protocol'])] = (i['type'])
811
812    return (portrecs, portrecsbynum)
813
814
815def get_all_domains():
816    global all_domains
817    if not all_domains:
818        all_domains = list(list(info(ATTRIBUTE, "domain"))[0]["types"])
819    return all_domains
820
821
822def get_all_roles():
823    global roles
824    if roles:
825        return roles
826
827    q = setools.RoleQuery(_pol)
828    roles = [str(x) for x in q.results() if str(x) != "object_r"]
829    return roles
830
831
832def get_selinux_users():
833    global selinux_user_list
834    if not selinux_user_list:
835        selinux_user_list = list(info(USER))
836        for x in selinux_user_list:
837            x['range'] = "".join(x['range'].split(" "))
838    return selinux_user_list
839
840
841def get_login_mappings():
842    global login_mappings
843    if login_mappings:
844        return login_mappings
845
846    fd = open(selinux.selinux_usersconf_path(), "r")
847    buf = fd.read()
848    fd.close()
849    login_mappings = []
850    for b in buf.split("\n"):
851        b = b.strip()
852        if len(b) == 0 or b.startswith("#"):
853            continue
854        x = b.split(":")
855        login_mappings.append({"name": x[0], "seuser": x[1], "mls": ":".join(x[2:])})
856    return login_mappings
857
858
859def get_all_users():
860    return sorted(map(lambda x: x['name'], get_selinux_users()))
861
862
863def get_all_file_types():
864    global file_types
865    if file_types:
866        return file_types
867    file_types = list(sorted(info(ATTRIBUTE, "file_type"))[0]["types"])
868    return file_types
869
870
871def get_all_port_types():
872    global port_types
873    if port_types:
874        return port_types
875    port_types = list(sorted(info(ATTRIBUTE, "port_type"))[0]["types"])
876    return port_types
877
878
879def get_all_bools():
880    global bools
881    if not bools:
882        bools = list(info(BOOLEAN))
883    return bools
884
885
886def prettyprint(f, trim):
887    return " ".join(f[:-len(trim)].split("_"))
888
889
890def markup(f):
891    return f
892
893
894def get_description(f, markup=markup):
895
896    txt = "Set files with the %s type, if you want to " % markup(f)
897
898    if f.endswith("_var_run_t"):
899        return txt + "store the %s files under the /run or /var/run directory." % prettyprint(f, "_var_run_t")
900    if f.endswith("_pid_t"):
901        return txt + "store the %s files under the /run directory." % prettyprint(f, "_pid_t")
902    if f.endswith("_var_lib_t"):
903        return txt + "store the %s files under the /var/lib directory." % prettyprint(f, "_var_lib_t")
904    if f.endswith("_var_t"):
905        return txt + "store the %s files under the /var directory." % prettyprint(f, "_var_lib_t")
906    if f.endswith("_var_spool_t"):
907        return txt + "store the %s files under the /var/spool directory." % prettyprint(f, "_spool_t")
908    if f.endswith("_spool_t"):
909        return txt + "store the %s files under the /var/spool directory." % prettyprint(f, "_spool_t")
910    if f.endswith("_cache_t") or f.endswith("_var_cache_t"):
911        return txt + "store the files under the /var/cache directory."
912    if f.endswith("_keytab_t"):
913        return txt + "treat the files as kerberos keytab files."
914    if f.endswith("_lock_t"):
915        return txt + "treat the files as %s lock data, stored under the /var/lock directory" % prettyprint(f, "_lock_t")
916    if f.endswith("_log_t"):
917        return txt + "treat the data as %s log data, usually stored under the /var/log directory." % prettyprint(f, "_log_t")
918    if f.endswith("_config_t"):
919        return txt + "treat the files as %s configuration data, usually stored under the /etc directory." % prettyprint(f, "_config_t")
920    if f.endswith("_conf_t"):
921        return txt + "treat the files as %s configuration data, usually stored under the /etc directory." % prettyprint(f, "_conf_t")
922    if f.endswith("_exec_t"):
923        return txt + "transition an executable to the %s_t domain." % f[:-len("_exec_t")]
924    if f.endswith("_cgi_content_t"):
925        return txt + "treat the files as %s cgi content." % prettyprint(f, "_cgi_content_t")
926    if f.endswith("_rw_content_t"):
927        return txt + "treat the files as %s read/write content." % prettyprint(f, "_rw_content_t")
928    if f.endswith("_rw_t"):
929        return txt + "treat the files as %s read/write content." % prettyprint(f, "_rw_t")
930    if f.endswith("_write_t"):
931        return txt + "treat the files as %s read/write content." % prettyprint(f, "_write_t")
932    if f.endswith("_db_t"):
933        return txt + "treat the files as %s database content." % prettyprint(f, "_db_t")
934    if f.endswith("_ra_content_t"):
935        return txt + "treat the files as %s read/append content." % prettyprint(f, "_ra_conten_t")
936    if f.endswith("_cert_t"):
937        return txt + "treat the files as %s certificate data." % prettyprint(f, "_cert_t")
938    if f.endswith("_key_t"):
939        return txt + "treat the files as %s key data." % prettyprint(f, "_key_t")
940
941    if f.endswith("_secret_t"):
942        return txt + "treat the files as %s secret data." % prettyprint(f, "_key_t")
943
944    if f.endswith("_ra_t"):
945        return txt + "treat the files as %s read/append content." % prettyprint(f, "_ra_t")
946
947    if f.endswith("_ro_t"):
948        return txt + "treat the files as %s read/only content." % prettyprint(f, "_ro_t")
949
950    if f.endswith("_modules_t"):
951        return txt + "treat the files as %s modules." % prettyprint(f, "_modules_t")
952
953    if f.endswith("_content_t"):
954        return txt + "treat the files as %s content." % prettyprint(f, "_content_t")
955
956    if f.endswith("_state_t"):
957        return txt + "treat the files as %s state data." % prettyprint(f, "_state_t")
958
959    if f.endswith("_files_t"):
960        return txt + "treat the files as %s content." % prettyprint(f, "_files_t")
961
962    if f.endswith("_file_t"):
963        return txt + "treat the files as %s content." % prettyprint(f, "_file_t")
964
965    if f.endswith("_data_t"):
966        return txt + "treat the files as %s content." % prettyprint(f, "_data_t")
967
968    if f.endswith("_file_t"):
969        return txt + "treat the data as %s content." % prettyprint(f, "_file_t")
970
971    if f.endswith("_tmp_t"):
972        return txt + "store %s temporary files in the /tmp directories." % prettyprint(f, "_tmp_t")
973    if f.endswith("_etc_t"):
974        return txt + "store %s files in the /etc directories." % prettyprint(f, "_tmp_t")
975    if f.endswith("_home_t"):
976        return txt + "store %s files in the users home directory." % prettyprint(f, "_home_t")
977    if f.endswith("_tmpfs_t"):
978        return txt + "store %s files on a tmpfs file system." % prettyprint(f, "_tmpfs_t")
979    if f.endswith("_unit_file_t"):
980        return txt + "treat files as a systemd unit file."
981    if f.endswith("_htaccess_t"):
982        return txt + "treat the file as a %s access file." % prettyprint(f, "_htaccess_t")
983
984    return txt + "treat the files as %s data." % prettyprint(f, "_t")
985
986
987def get_all_attributes():
988    global all_attributes
989    if not all_attributes:
990        all_attributes = list(sorted(map(lambda x: x['name'], info(ATTRIBUTE))))
991    return all_attributes
992
993
994def _dict_has_perms(dict, perms):
995    for perm in perms:
996        if perm not in dict[PERMS]:
997            return False
998    return True
999
1000
1001def gen_short_name(setype):
1002    all_domains = get_all_domains()
1003    if setype.endswith("_t"):
1004        domainname = setype[:-2]
1005    else:
1006        domainname = setype
1007    if domainname + "_t" not in all_domains:
1008        raise ValueError("domain %s_t does not exist" % domainname)
1009    if domainname[-1] == 'd':
1010        short_name = domainname[:-1] + "_"
1011    else:
1012        short_name = domainname + "_"
1013    return (domainname, short_name)
1014
1015
1016def get_bools(setype):
1017    bools = []
1018    domainbools = []
1019    domainname, short_name = gen_short_name(setype)
1020    for i in map(lambda x: x['boolean'], filter(lambda x: 'boolean' in x, search([ALLOW], {'source': setype}))):
1021        for b in i:
1022            if not isinstance(b, tuple):
1023                continue
1024            try:
1025                enabled = selinux.security_get_boolean_active(b[0])
1026            except OSError:
1027                enabled = b[1]
1028            if b[0].startswith(short_name) or b[0].startswith(domainname):
1029                if (b[0], enabled) not in domainbools and (b[0], not enabled) not in domainbools:
1030                    domainbools.append((b[0], enabled))
1031            else:
1032                if (b[0], enabled) not in bools and (b[0], not enabled) not in bools:
1033                    bools.append((b[0], enabled))
1034    return (domainbools, bools)
1035
1036
1037def get_all_booleans():
1038    global booleans
1039    if not booleans:
1040        booleans = selinux.security_get_boolean_names()[1]
1041    return booleans
1042
1043
1044def policy_xml(path="/usr/share/selinux/devel/policy.xml"):
1045    try:
1046        fd = gzip.open(path)
1047        buf = fd.read()
1048        fd.close()
1049    except IOError:
1050        fd = open(path)
1051        buf = fd.read()
1052        fd.close()
1053    return buf
1054
1055
1056def gen_bool_dict(path="/usr/share/selinux/devel/policy.xml"):
1057    global booleans_dict
1058    if booleans_dict:
1059        return booleans_dict
1060    import xml.etree.ElementTree
1061    booleans_dict = {}
1062    try:
1063        tree = xml.etree.ElementTree.fromstring(policy_xml(path))
1064        for l in tree.findall("layer"):
1065            for m in l.findall("module"):
1066                for b in m.findall("tunable"):
1067                    desc = b.find("desc").find("p").text.strip("\n")
1068                    desc = re.sub("\n", " ", desc)
1069                    booleans_dict[b.get('name')] = (m.get("name"), b.get('dftval'), desc)
1070                for b in m.findall("bool"):
1071                    desc = b.find("desc").find("p").text.strip("\n")
1072                    desc = re.sub("\n", " ", desc)
1073                    booleans_dict[b.get('name')] = (m.get("name"), b.get('dftval'), desc)
1074            for i in tree.findall("bool"):
1075                desc = i.find("desc").find("p").text.strip("\n")
1076                desc = re.sub("\n", " ", desc)
1077                booleans_dict[i.get('name')] = ("global", i.get('dftval'), desc)
1078        for i in tree.findall("tunable"):
1079            desc = i.find("desc").find("p").text.strip("\n")
1080            desc = re.sub("\n", " ", desc)
1081            booleans_dict[i.get('name')] = ("global", i.get('dftval'), desc)
1082    except IOError:
1083        pass
1084    return booleans_dict
1085
1086
1087def boolean_category(boolean):
1088    booleans_dict = gen_bool_dict()
1089    if boolean in booleans_dict:
1090        return _(booleans_dict[boolean][0])
1091    else:
1092        return _("unknown")
1093
1094
1095def boolean_desc(boolean):
1096    booleans_dict = gen_bool_dict()
1097    if boolean in booleans_dict:
1098        return _(booleans_dict[boolean][2])
1099    else:
1100        desc = boolean.split("_")
1101        return "Allow %s to %s" % (desc[0], " ".join(desc[1:]))
1102
1103
1104def get_os_version():
1105    os_version = ""
1106    pkg_name = "selinux-policy"
1107    try:
1108        try:
1109            from commands import getstatusoutput
1110        except ImportError:
1111            from subprocess import getstatusoutput
1112        rc, output = getstatusoutput("rpm -q '%s'" % pkg_name)
1113        if rc == 0:
1114            os_version = output.split(".")[-2]
1115    except:
1116        os_version = ""
1117
1118    if os_version[0:2] == "fc":
1119        os_version = "Fedora" + os_version[2:]
1120    elif os_version[0:2] == "el":
1121        os_version = "RHEL" + os_version[2:]
1122    else:
1123        os_version = ""
1124
1125    return os_version
1126
1127
1128def reinit():
1129    global all_attributes
1130    global all_domains
1131    global all_types
1132    global booleans
1133    global booleans_dict
1134    global bools
1135    global fcdict
1136    global file_types
1137    global local_files
1138    global methods
1139    global methods
1140    global portrecs
1141    global portrecsbynum
1142    global port_types
1143    global role_allows
1144    global roles
1145    global login_mappings
1146    global selinux_user_list
1147    global user_types
1148    all_attributes = None
1149    all_domains = None
1150    all_types = None
1151    booleans = None
1152    booleans_dict = None
1153    bools = None
1154    fcdict = None
1155    file_types = None
1156    local_files = None
1157    methods = None
1158    methods = None
1159    portrecs = None
1160    portrecsbynum = None
1161    port_types = None
1162    role_allows = None
1163    roles = None
1164    user_types = None
1165    login_mappings = None
1166    selinux_user_list = None
1167