1#!/usr/bin/python3
2
3import dbus
4import dbus.service
5import dbus.mainloop.glib
6from gi.repository import GObject
7import slip.dbus.service
8from slip.dbus import polkit
9import os
10import selinux
11from subprocess import Popen, PIPE, STDOUT
12
13
14class selinux_server(slip.dbus.service.Object):
15    default_polkit_auth_required = "org.selinux.semanage"
16
17    def __init__(self, *p, **k):
18        super(selinux_server, self).__init__(*p, **k)
19
20    #
21    # The semanage method runs a transaction on a series of semanage commands,
22    # these commands can take the output of customized
23    #
24    @slip.dbus.polkit.require_auth("org.selinux.semanage")
25    @dbus.service.method("org.selinux", in_signature='s')
26    def semanage(self, buf):
27        p = Popen(["/usr/sbin/semanage", "import"], stdout=PIPE, stderr=PIPE, stdin=PIPE, universal_newlines=True)
28        p.stdin.write(buf)
29        output = p.communicate()
30        if p.returncode and p.returncode != 0:
31            raise dbus.exceptions.DBusException(output[1])
32
33    #
34    # The customized method will return all of the custommizations for policy
35    # on the server.  This output can be used with the semanage method on
36    # another server to make the two systems have duplicate policy.
37    #
38    @slip.dbus.polkit.require_auth("org.selinux.customized")
39    @dbus.service.method("org.selinux", in_signature='', out_signature='s')
40    def customized(self):
41        p = Popen(["/usr/sbin/semanage", "export"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
42        buf = p.stdout.read()
43        output = p.communicate()
44        if p.returncode and p.returncode != 0:
45            raise OSError("Failed to read SELinux configuration: %s", output)
46        return buf
47
48    #
49    # The semodule_list method will return the output of semodule --list=full, using the customized polkit,
50    # since this is a readonly behaviour
51    #
52    @slip.dbus.polkit.require_auth("org.selinux.semodule_list")
53    @dbus.service.method("org.selinux", in_signature='', out_signature='s')
54    def semodule_list(self):
55        p = Popen(["/usr/sbin/semodule", "--list=full"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
56        buf = p.stdout.read()
57        output = p.communicate()
58        if p.returncode and p.returncode != 0:
59            raise OSError("Failed to list SELinux modules: %s", output)
60        return buf
61
62    #
63    # The restorecon method modifies any file path to the default system label
64    #
65    @slip.dbus.polkit.require_auth("org.selinux.restorecon")
66    @dbus.service.method("org.selinux", in_signature='s')
67    def restorecon(self, path):
68        selinux.restorecon(str(path), recursive=1)
69
70    #
71    # The setenforce method turns off the current enforcement of SELinux
72    #
73    @slip.dbus.polkit.require_auth("org.selinux.setenforce")
74    @dbus.service.method("org.selinux", in_signature='i')
75    def setenforce(self, value):
76        selinux.security_setenforce(value)
77
78    #
79    # The setenforce method turns off the current enforcement of SELinux
80    #
81    @slip.dbus.polkit.require_auth("org.selinux.relabel_on_boot")
82    @dbus.service.method("org.selinux", in_signature='i')
83    def relabel_on_boot(self, value):
84        if value == 1:
85            fd = open("/.autorelabel", "w")
86            fd.close()
87        else:
88            try:
89                os.unlink("/.autorelabel")
90            except FileNotFoundError:
91                pass
92
93    def write_selinux_config(self, enforcing=None, policy=None):
94        path = selinux.selinux_path() + "config"
95        backup_path = path + ".bck"
96        fd = open(path)
97        lines = fd.readlines()
98        fd.close()
99        fd = open(backup_path, "w")
100        for l in lines:
101            if enforcing and l.startswith("SELINUX="):
102                fd.write("SELINUX=%s\n" % enforcing)
103                continue
104            if policy and l.startswith("SELINUXTYPE="):
105                fd.write("SELINUXTYPE=%s\n" % policy)
106                continue
107            fd.write(l)
108        fd.close()
109        os.rename(backup_path, path)
110
111    #
112    # The change_default_enforcement modifies the current enforcement mode
113    #
114    @slip.dbus.polkit.require_auth("org.selinux.change_default_mode")
115    @dbus.service.method("org.selinux", in_signature='s')
116    def change_default_mode(self, value):
117        values = ["enforcing", "permissive", "disabled"]
118        if value not in values:
119            raise ValueError("Enforcement mode must be %s" % ", ".join(values))
120        self.write_selinux_config(enforcing=value)
121
122    #
123    # The change_default_policy method modifies the policy type
124    #
125    @slip.dbus.polkit.require_auth("org.selinux.change_default_policy")
126    @dbus.service.method("org.selinux", in_signature='s')
127    def change_default_policy(self, value):
128        path = selinux.selinux_path() + value
129        if os.path.isdir(path):
130            return self.write_selinux_config(policy=value)
131        raise ValueError("%s does not exist" % path)
132
133if __name__ == "__main__":
134    mainloop = GObject.MainLoop()
135    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
136    system_bus = dbus.SystemBus()
137    name = dbus.service.BusName("org.selinux", system_bus)
138    object = selinux_server(system_bus, "/org/selinux/object")
139    slip.dbus.service.set_mainloop(mainloop)
140    mainloop.run()
141