Skip to content

Commit

Permalink
WIP: Use polkit for some D-Bus methods
Browse files Browse the repository at this point in the history
* Allow to call "register" and "unregister" D-Bus methods for
  non-root user, when the user is authorized using polkit
* This change will require to do some changes on selinux-policy.
  To test this with enforcing SELinux you can create local
  policy using following file (local_rhsmcertd_policykit.cil)
  containing following rules:

    (allow rhsmcertd_t policykit_t (dbus (send_msg)))
    (allow policykit_t rhsmcertd_t (dbus (send_msg)))

  Then you can run: `semodule -i local_rhsmcertd_policykit.cil`
* TODO: we should use more polkit action IDs. Not only one
  default
  • Loading branch information
jirihnidek committed Oct 30, 2023
1 parent 42065df commit cb628df
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 3 deletions.
10 changes: 7 additions & 3 deletions etc-conf/dbus/polkit/com.redhat.RHSM1.policy
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
<vendor>Red Hat Subscription Management</vendor>
<vendor_url>http://redhat.com</vendor_url>

<!--
TODO: Do not use one "id" for everything, but create
one ID for "register" and another ID for "unregister"
-->
<action id="com.redhat.RHSM1.default">
<description>RHSM default</description>
<message>System policy prevents access to com.redhat.RHSM1</message>
<defaults>
<allow_any>yes</allow_any>
<allow_inactive>yes</allow_inactive>
<allow_active>yes</allow_active>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
</policyconfig>
15 changes: 15 additions & 0 deletions etc-conf/dbus/system.d/com.redhat.RHSM1.conf
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,21 @@
send_interface="com.redhat.RHSM1.Consumer"
send_member="GetUuid"/>

<!--
Following methods are restricted using Polkit
-->
<allow send_destination="com.redhat.RHSM1"
send_interface="com.redhat.RHSM1.RegisterServer"
send_member="Start"/>

<allow send_destination="com.redhat.RHSM1"
send_interface="com.redhat.RHSM1.RegisterServer"
send_member="Stop"/>

<allow send_destination="com.redhat.RHSM1"
send_interface="com.redhat.RHSM1.Unregister"
send_member="Unregister"/>

<!-- Basic D-Bus API stuff -->
<allow send_destination="com.redhat.RHSM1"
send_interface="org.freedesktop.DBus.Introspectable"/>
Expand Down
6 changes: 6 additions & 0 deletions src/rhsmlib/client_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ class DBusSender:
@rhsm.utils.call_once
def __init__(self):
self._cmd_line = None
self._sender = None

@property
def sender(self):
return self._sender

@property
def cmd_line(self):
Expand Down Expand Up @@ -70,6 +75,7 @@ def set_cmd_line(self, sender, cmd_line=None, bus=None):
"""
if cmd_line is None:
self.cmd_line = self.get_cmd_line(sender, bus)
self._sender = sender
else:
self.cmd_line = cmd_line
log.debug("D-Bus sender: %s (cmd-line: %s)" % (sender, self.cmd_line))
Expand Down
4 changes: 4 additions & 0 deletions src/rhsmlib/dbus/objects/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,10 @@ def __init__(self, conn=None, object_path=None, bus_name=None):
out_signature="s",
)
@util.dbus_handle_sender
@util.dbus_admin_auth_policy
@util.dbus_handle_exceptions
def Start(self, locale, sender=None):
log.debug(f"D-Bus method: Start({locale}, {sender})")
locale = dbus_utils.dbus_to_python(locale, expected_type=str)
Locale.set(locale)

Expand All @@ -108,8 +110,10 @@ def Start(self, locale, sender=None):
out_signature="b",
)
@util.dbus_handle_sender
@util.dbus_admin_auth_policy
@util.dbus_handle_exceptions
def Stop(self, locale, sender=None):
log.debug(f"D-Bus method: Stop({locale}, {sender})")
locale = dbus_utils.dbus_to_python(locale, expected_type=str)
Locale.set(locale)

Expand Down
1 change: 1 addition & 0 deletions src/rhsmlib/dbus/objects/unregister.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def __init__(self, conn=None, object_path=None, bus_name=None):
out_signature="",
)
@util.dbus_handle_sender
@util.dbus_admin_auth_policy
@util.dbus_handle_exceptions
def Unregister(self, proxy_options, locale, sender=None):
"""
Expand Down
81 changes: 81 additions & 0 deletions src/rhsmlib/dbus/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,104 @@
import logging
import sys
import decorator
import dbus
import dbus.service
import json
import re

from rhsmlib.dbus import exceptions
from rhsmlib.client_info import DBusSender
from rhsmlib.dbus.dbus_utils import pid_of_sender, dbus_to_python

log = logging.getLogger(__name__)

__all__ = [
"dbus_admin_auth_policy",
"dbus_domain_admin_auth_policy",
"dbus_handle_exceptions",
"dbus_handle_sender",
"dbus_service_method",
"dbus_service_signal",
]


def _check_polkit_policy(sender, func, *args, **kwargs):
"""
Check if given sender is authorized to call given function
"""
bus = dbus.SystemBus()
try:
pid = pid_of_sender(bus, sender)
except Exception as err:
raise exceptions.RHSM1DBusException(f"Unable to get PID of sender: {sender}: {err}")
dbus_obj = bus.get_object("org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority")
dbus_iface = dbus.Interface(dbus_obj, "org.freedesktop.PolicyKit1.Authority")
subject = (
"unix-process",
{"pid": dbus.UInt32(pid), "start-time": dbus.UInt64(0)}
)
# TODO: Modify code to be able to use at least two IDs (one for "register" and another
# for "unregister")
action_id = "com.redhat.RHSM1.default"
details = {}
flags = 1
cancellation_id = ""

try:
is_authorized, is_challenge, details = dbus_iface.CheckAuthorization(
subject, action_id, details, flags, cancellation_id)
except Exception as err:
raise exceptions.RHSM1DBusException(f"Unable to check authorization of {sender}: {err}")
else:
is_authorized = dbus_to_python(is_authorized, expected_type=bool)
if is_authorized is True:
return func(*args, **kwargs)
else:
details = dbus_to_python(details, expected_type=dict)
raise exceptions.RHSM1DBusException(f"{sender} is not authorized to call {func}: {details}")


@decorator.decorator
def dbus_admin_auth_policy(func, *args, **kwargs):
"""
When this decorator is used, then it is required that sender process
is admin authenticated. This is workaround for some applications using
our D-Bus API.
"""

sender = None
# Get sender from arguments
if "sender" in kwargs:
sender = kwargs["sender"]
elif len(args) > 0:
sender = args[-1]

if sender is not None:
return _check_polkit_policy(sender, func, *args, **kwargs)
else:
raise exceptions.RHSM1DBusException(f"No sender specified, unable to check authorization for calling {func}")


@decorator.decorator
def dbus_domain_admin_auth_policy(func, *args, **kwargs):
"""
This modified version of decorator dbus_admin_auth_policy(), but this could be
used in the case, when unix socket is used for registration
"""
# TODO: This does not work as expected and it is not used ATM.
# The pid_of_sender() in _check_polkit_policy() is not able to
# find process for some reason, but process communicating over
# unix socket should be authorized by polkit.
with DBusSender() as dbus_sender:
sender = dbus_sender.sender
if sender is not None:
return _check_polkit_policy(sender, func, *args, **kwargs)
else:
raise exceptions.RHSM1DBusException(
f"No sender specified, unable to check authorization for calling {func}"
)


@decorator.decorator
def dbus_handle_sender(func, *args, **kwargs):
"""
Expand Down

0 comments on commit cb628df

Please sign in to comment.