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