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