#!/usr/bin/python3 import dbus import dbus.service from dbus.mainloop.glib import DBusGMainLoop from gi.repository import GObject from gi.repository import GLib import os import selinux from subprocess import Popen, PIPE, STDOUT class selinux_server(dbus.service.Object): default_polkit_auth_required = "org.selinux.semanage" def __init__(self, *p, **k): super(selinux_server, self).__init__(*p, **k) def is_authorized(self, sender, action_id): bus = dbus.SystemBus() proxy = bus.get_object('org.freedesktop.PolicyKit1', '/org/freedesktop/PolicyKit1/Authority') authority = dbus.Interface(proxy, dbus_interface='org.freedesktop.PolicyKit1.Authority') subject = ('system-bus-name', {'name': sender}) result = authority.CheckAuthorization(subject, action_id, {}, 1, '') return result[0] # # The semanage method runs a transaction on a series of semanage commands, # these commands can take the output of customized # @dbus.service.method("org.selinux", in_signature='s', sender_keyword="sender") def semanage(self, buf, sender): if not self.is_authorized(sender, "org.selinux.semanage"): raise dbus.exceptions.DBusException("Not authorized") p = Popen(["/usr/sbin/semanage", "import"], stdout=PIPE, stderr=PIPE, stdin=PIPE, universal_newlines=True) p.stdin.write(buf) output = p.communicate() if p.returncode and p.returncode != 0: raise dbus.exceptions.DBusException(output[1]) # # The customized method will return all of the custommizations for policy # on the server. This output can be used with the semanage method on # another server to make the two systems have duplicate policy. # @dbus.service.method("org.selinux", in_signature='', out_signature='s', sender_keyword="sender") def customized(self, sender): if not self.is_authorized(sender, "org.selinux.customized"): raise dbus.exceptions.DBusException("Not authorized") p = Popen(["/usr/sbin/semanage", "export"], stdout=PIPE, stderr=PIPE, universal_newlines=True) buf = p.stdout.read() output = p.communicate() if p.returncode and p.returncode != 0: raise OSError("Failed to read SELinux configuration: %s", output) return buf # # The semodule_list method will return the output of semodule --list=full, using the customized polkit, # since this is a readonly behaviour # @dbus.service.method("org.selinux", in_signature='', out_signature='s', sender_keyword="sender") def semodule_list(self, sender): if not self.is_authorized(sender, "org.selinux.semodule_list"): raise dbus.exceptions.DBusException("Not authorized") p = Popen(["/usr/sbin/semodule", "--list=full"], stdout=PIPE, stderr=PIPE, universal_newlines=True) buf = p.stdout.read() output = p.communicate() if p.returncode and p.returncode != 0: raise OSError("Failed to list SELinux modules: %s", output) return buf # # The restorecon method modifies any file path to the default system label # @dbus.service.method("org.selinux", in_signature='s', sender_keyword="sender") def restorecon(self, path, sender): if not self.is_authorized(sender, "org.selinux.restorecon"): raise dbus.exceptions.DBusException("Not authorized") selinux.restorecon(str(path), recursive=1) # # The setenforce method turns off the current enforcement of SELinux # @dbus.service.method("org.selinux", in_signature='i', sender_keyword="sender") def setenforce(self, value, sender): if not self.is_authorized(sender, "org.selinux.setenforce"): raise dbus.exceptions.DBusException("Not authorized") selinux.security_setenforce(value) # # The setenforce method turns off the current enforcement of SELinux # @dbus.service.method("org.selinux", in_signature='i', sender_keyword="sender") def relabel_on_boot(self, value, sender): if not self.is_authorized(sender, "org.selinux.relabel_on_boot"): raise dbus.exceptions.DBusException("Not authorized") if value == 1: fd = open("/.autorelabel", "w") fd.close() else: try: os.unlink("/.autorelabel") except FileNotFoundError: pass def write_selinux_config(self, enforcing=None, policy=None): path = selinux.selinux_path() + "config" backup_path = path + ".bck" fd = open(path) lines = fd.readlines() fd.close() fd = open(backup_path, "w") for l in lines: if enforcing and l.startswith("SELINUX="): fd.write("SELINUX=%s\n" % enforcing) continue if policy and l.startswith("SELINUXTYPE="): fd.write("SELINUXTYPE=%s\n" % policy) continue fd.write(l) fd.close() os.rename(backup_path, path) # # The change_default_enforcement modifies the current enforcement mode # @dbus.service.method("org.selinux", in_signature='s', sender_keyword="sender") def change_default_mode(self, value, sender): if not self.is_authorized(sender, "org.selinux.change_default_mode"): raise dbus.exceptions.DBusException("Not authorized") values = ["enforcing", "permissive", "disabled"] if value not in values: raise ValueError("Enforcement mode must be %s" % ", ".join(values)) self.write_selinux_config(enforcing=value) # # The change_default_policy method modifies the policy type # @dbus.service.method("org.selinux", in_signature='s', sender_keyword="sender") def change_default_policy(self, value, sender): if not self.is_authorized(sender, "org.selinux.change_default_policy"): raise dbus.exceptions.DBusException("Not authorized") path = selinux.selinux_path() + value if os.path.isdir(path): return self.write_selinux_config(policy=value) raise ValueError("%s does not exist" % path) if __name__ == "__main__": DBusGMainLoop(set_as_default=True) mainloop = GLib.MainLoop() system_bus = dbus.SystemBus() name = dbus.service.BusName("org.selinux", system_bus) server = selinux_server(system_bus, "/org/selinux/object") mainloop.run()