diff --git a/tuned/exports/dbus_exporter.py b/tuned/exports/dbus_exporter.py index 0c9e8be3..75a49a4d 100644 --- a/tuned/exports/dbus_exporter.py +++ b/tuned/exports/dbus_exporter.py @@ -122,7 +122,28 @@ def _prepare_for_dbus(self, method, wrapper): func = FunctionType(code.co_consts[0], locals(), method.__name__) return func - def export(self, method, in_signature, out_signature): + def _polkit_auth(self, action_name, *args): + action_id = self._namespace + "." + action_name + caller = args[-1] + log.debug("checking authorization for action '%s' requested by caller '%s'" % (action_id, caller)) + ret = self._polkit.check_authorization(caller, action_id) + args_copy = args + if ret == 1: + log.debug("action '%s' requested by caller '%s' was successfully authorized by polkit" % (action_id, caller)) + elif ret == 2: + log.warning("polkit error, but action '%s' requested by caller '%s' was successfully authorized by fallback method" % (action_id, caller)) + elif ret == 0: + log.info("action '%s' requested by caller '%s' wasn't authorized, ignoring the request" % (action_id, caller)) + args_copy = list(args[:-1]) + [""] + elif ret == -1: + log.warning("polkit error and action '%s' requested by caller '%s' wasn't authorized by fallback method, ignoring the request" % (action_id, caller)) + args_copy = list(args[:-1]) + [""] + else: + log.error("polkit error and unable to use fallback method to authorize action '%s' requested by caller '%s', ignoring the request" % (action_id, caller)) + args_copy = list(args[:-1]) + [""] + return args_copy + + def export(self, method, in_signature, out_signature, action_name=None): if not ismethod(method): raise Exception("Only bound methods can be exported.") @@ -130,28 +151,11 @@ def export(self, method, in_signature, out_signature): if method_name in self._dbus_methods: raise Exception("Method with this name is already exported.") - def wrapper(owner, *args, **kwargs): - action_id = self._namespace + "." + method.__name__ - caller = args[-1] - log.debug("checking authorization for action '%s' requested by caller '%s'" % (action_id, caller)) - ret = self._polkit.check_authorization(caller, action_id) - args_copy = args - if ret == 1: - log.debug("action '%s' requested by caller '%s' was successfully authorized by polkit" % (action_id, caller)) - elif ret == 2: - log.warning("polkit error, but action '%s' requested by caller '%s' was successfully authorized by fallback method" % (action_id, caller)) - elif ret == 0: - log.info("action '%s' requested by caller '%s' wasn't authorized, ignoring the request" % (action_id, caller)) - args_copy = list(args[:-1]) + [""] - elif ret == -1: - log.warning("polkit error and action '%s' requested by caller '%s' wasn't authorized by fallback method, ignoring the request" % (action_id, caller)) - args_copy = list(args[:-1]) + [""] - else: - log.error("polkit error and unable to use fallback method to authorize action '%s' requested by caller '%s', ignoring the request" % (action_id, caller)) - args_copy = list(args[:-1]) + [""] - return method(*args_copy, **kwargs) + action_name = action_name or method_name + def auth_wrapper(owner, *args, **kwargs): + return method(*self._polkit_auth(action_name, *args), **kwargs) - wrapper = self._prepare_for_dbus(method, wrapper) + wrapper = self._prepare_for_dbus(method, auth_wrapper) wrapper = dbus.service.method(self._interface_name, in_signature, out_signature, sender_keyword = "caller")(wrapper) self._dbus_methods[method_name] = wrapper diff --git a/tuned/exports/dbus_exporter_with_properties.py b/tuned/exports/dbus_exporter_with_properties.py index 64219e24..8b327b98 100644 --- a/tuned/exports/dbus_exporter_with_properties.py +++ b/tuned/exports/dbus_exporter_with_properties.py @@ -11,50 +11,62 @@ def __init__(self, bus_name, interface_name, object_name, namespace): self._property_setters = {} self._property_getters = {} - def Get(_, interface_name, property_name): + def Get(_, interface_name, property_name, caller): if interface_name != self._interface_name: raise DBusException("Unknown interface: %s" % interface_name) if property_name not in self._property_getters: raise DBusException("No such property: %s" % property_name) getter = self._property_getters[property_name] - return getter() + return getter(caller) - def Set(_, interface_name, property_name, value): + def Set(_, interface_name, property_name, value, caller): if interface_name != self._interface_name: raise DBusException("Unknown interface: %s" % interface_name) if property_name not in self._property_setters: raise DBusException("No such property: %s" % property_name) setter = self._property_setters[property_name] - setter(value) + setter(value, caller) - def GetAll(_, interface_name): + def GetAll(_, interface_name, caller): if interface_name != self._interface_name: raise DBusException("Unknown interface: %s" % interface_name) - return {name: getter() for name, getter in self._property_getters.items()} + return {name: getter(caller) for name, getter in self._property_getters.items()} def PropertiesChanged(_, interface_name, changed_properties, invalidated_properties): if interface_name != self._interface_name: raise DBusException("Unknown interface: %s" % interface_name) - self._dbus_methods["Get"] = method(PROPERTIES_IFACE, in_signature="ss", out_signature="v")(Get) - self._dbus_methods["Set"] = method(PROPERTIES_IFACE, in_signature="ssv")(Set) - self._dbus_methods["GetAll"] = method(PROPERTIES_IFACE, in_signature="s", out_signature="a{sv}")(GetAll) + self._dbus_methods["Get"] = method(PROPERTIES_IFACE, in_signature="ss", out_signature="v", sender_keyword="caller")(Get) + self._dbus_methods["Set"] = method(PROPERTIES_IFACE, in_signature="ssv", sender_keyword="caller")(Set) + self._dbus_methods["GetAll"] = method(PROPERTIES_IFACE, in_signature="s", out_signature="a{sv}", sender_keyword="caller")(GetAll) self._dbus_methods["PropertiesChanged"] = signal(PROPERTIES_IFACE, signature="sa{sv}as")(PropertiesChanged) self._signals.add("PropertiesChanged") + def _auth_wrapper(self, method, action_name): + def wrapper(*args, **kwargs): + new_args = self._polkit_auth(action_name, *args) + if new_args[-1] == "": + raise DBusException("Unauthorized") + return method(*new_args, **kwargs) + return wrapper + def property_changed(self, property_name, value): self.send_signal("PropertiesChanged", self._interface_name, {property_name: value}, {}) - def property_getter(self, method, property_name): + def property_getter(self, method, property_name, action_name=None): if not ismethod(method): raise Exception("Only bound methods can be exported.") if property_name in self._property_getters: raise Exception("A getter for this property is already registered.") + if action_name is not None: + method = self._auth_wrapper(method, action_name) self._property_getters[property_name] = method - def property_setter(self, method, property_name): + def property_setter(self, method, property_name, action_name=None): if not ismethod(method): raise Exception("Only bound methods can be exported.") if property_name in self._property_setters: raise Exception("A setter for this property is already registered.") + if action_name is not None: + method = self._auth_wrapper(method, action_name) self._property_setters[property_name] = method diff --git a/tuned/ppd/controller.py b/tuned/ppd/controller.py index 4376c1e9..b7fb00dd 100644 --- a/tuned/ppd/controller.py +++ b/tuned/ppd/controller.py @@ -172,7 +172,7 @@ def active_profile(self): tuned_profile = self._tuned_interface.active_profile() return self._config.tuned_to_ppd.get(tuned_profile, UNKNOWN_PROFILE) - @exports.export("sss", "u") + @exports.export("sss", "u", "hold-profile") def HoldProfile(self, profile, reason, app_id, caller): if profile != PPD_POWER_SAVER and profile != PPD_PERFORMANCE: raise dbus.exceptions.DBusException( @@ -180,7 +180,7 @@ def HoldProfile(self, profile, reason, app_id, caller): ) return self._profile_holds.add(profile, reason, app_id, caller) - @exports.export("u", "") + @exports.export("u", "", "release-profile") def ReleaseProfile(self, cookie, caller): if not self._profile_holds.has(cookie): raise dbus.exceptions.DBusException("No active hold for cookie '%s'" % cookie) @@ -190,8 +190,8 @@ def ReleaseProfile(self, cookie, caller): def ProfileReleased(self, cookie): pass - @exports.property_setter("ActiveProfile") - def set_active_profile(self, profile): + @exports.property_setter("ActiveProfile", "switch-profile") + def set_active_profile(self, profile, caller): if profile not in self._config.ppd_to_tuned: raise dbus.exceptions.DBusException("Invalid profile '%s'" % profile) log.debug("Setting base profile to %s" % profile) @@ -200,24 +200,24 @@ def set_active_profile(self, profile): self.switch_profile(profile) @exports.property_getter("ActiveProfile") - def get_active_profile(self): + def get_active_profile(self, caller): return self.active_profile() @exports.property_getter("Profiles") - def get_profiles(self): + def get_profiles(self, caller): return dbus.Array( [{"Profile": profile, "Driver": DRIVER} for profile in self._config.ppd_to_tuned.keys()], signature="a{sv}", ) @exports.property_getter("Actions") - def get_actions(self): + def get_actions(self, caller): return dbus.Array([], signature="s") @exports.property_getter("PerformanceDegraded") - def get_performance_degraded(self): + def get_performance_degraded(self, caller): return self._performance_degraded @exports.property_getter("ActiveProfileHolds") - def get_active_profile_holds(self): + def get_active_profile_holds(self, caller): return self._profile_holds.as_dbus_array() diff --git a/tuned/ppd/tuned-ppd.policy b/tuned/ppd/tuned-ppd.policy index 8106808d..f4dea8c3 100644 --- a/tuned/ppd/tuned-ppd.policy +++ b/tuned/ppd/tuned-ppd.policy @@ -6,7 +6,17 @@ TuneD https://tuned-project.org/ - + + Switch power profile + Authentication is required to switch power profiles. + + no + no + yes + + + + Hold power profile Authentication is required to hold power profiles. @@ -16,7 +26,7 @@ - + Release power profile Authentication is required to release power profiles.