以 root 身份连接到用户 dbus

day*_*day 4 python linux dbus

如果我们正常打开python解释器并输入以下内容:

import dbus
bus = dbus.SessionBus()
bus.list_names()
Run Code Online (Sandbox Code Playgroud)

我们看到用户会话 dbus 上的所有服务。现在假设我们想在同一个脚本中执行一些仅限 root 的操作来确定要通过 dbus 传递的信息,因此我们运行解释器并sudo python运行相同的操作,我们只在 root 用户的会话 dbus 上看到一个简短的项目列表,并尝试连接到用户 dbus 上的任何内容都会get_object相应地产生未找到错误。

到目前为止我已经尝试插入

import os

os.seteuid(int(os.environ['SUDO_UID']))
Run Code Online (Sandbox Code Playgroud)

但这只能给出SessionBus()一个org.freedesktop.DBus.Error.NoReply所以这可能是无稽之谈。有没有办法使用 python dbus 绑定以超级用户身份连接到用户的 dbus 服务?

Blu*_*orn 6

我对 DBus 知之甚少,但这个问题让我很好奇。

\n

TL;DR:dbus.bus.BusConnection与目标用户的套接字地址一起使用seteuid获得访问权限。

\n

第一个问题:DBus 连接到会话总线的哪个套接字?

\n
$ cat list_bus.py \nimport dbus\nprint(dbus.SessionBus().list_names())\n$ strace -o list_bus.trace python3 list_bus.py\n$ grep ^connect list_bus.trace \nconnect(3, {sa_family=AF_UNIX, sun_path="/run/user/1000/bus"}, 20) = 0\n
Run Code Online (Sandbox Code Playgroud)\n

也许它依赖于环境变量?明白了!

\n
$ env|grep /run/user/1000/bus\nDBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus\n
Run Code Online (Sandbox Code Playgroud)\n

从 root 帐户追踪行为,它似乎不知道要连接的地址。谷歌搜索变量名称让我找到了D-Bus 规范“众所周知的消息总线实例”部分。

\n

第二个问题:我们可以直接连接到套接字而不让 D-Bus 库猜测正确的地址吗?dbus-python教程指出:

\n
\n

出于特殊目的,您可以使用非默认总线,或者根本不是总线的连接,使用 dbus-python 0.81.0 中添加的一些新 API。

\n
\n

查看变更日志,这似乎指的是这些:

\n
\n

Bus 有一个超类 dbus.bus.BusConnection (与总线守护进程的连接,但没有共享连接语义或任何已弃用的 API),以方便那些想要子类化总线守护进程连接的人

\n
\n

让我们试试这个:

\n
$ python3\nPython 3.9.2 (default, Feb 28 2021, 17:03:44) \n>>> from dbus.bus import BusConnection\n>>> len(BusConnection("unix:path=/run/user/1000/bus").list_names())\n145\n
Run Code Online (Sandbox Code Playgroud)\n

root访问权限怎么样?

\n
# python3\n>>> from dbus.bus import BusConnection\n>>> len(BusConnection("unix:path=/run/user/1000/bus").list_names())\nTraceback (most recent call last):\n  File "<stdin>", line 1, in <module>\n  File "/usr/lib/python3/dist-packages/dbus/bus.py", line 124, in __new__\n    bus = cls._new_for_bus(address_or_type, mainloop=mainloop)\ndbus.exceptions.DBusException: org.freedesktop.DBus.Error.NoReply: Did not\n  receive a reply. Possible causes include: the remote application did not send\n  a reply, the message bus security policy blocked the reply, the reply timeout\n  expired, or the network connection was broken.\n>>> import os\n>>> os.seteuid(1000)\n>>> len(BusConnection("unix:path=/run/user/1000/bus").list_names())\n143\n
Run Code Online (Sandbox Code Playgroud)\n

所以这回答了问题:使用BusConnection代替SessionBus并显式指定地址,结合seteuid来获得访问权限。

\n

奖励:以 root 身份连接,无需 seteuid

\n

我仍然想知道是否可以以 root 用户身份直接访问总线,而无需求助于seteuid. 经过几次搜索查询后,我找到了一张 systemd 票证,上面有这样的评论:

\n
\n

dbus-daemon 是强制访问的组件...(但您可以将 xml 策略文件放入其中,以实现此目的)。

\n
\n

这导致我提出了一个Askubuntu 问题,讨论如何修改站点本地会话总线策略。

\n

只是为了玩一玩,我在一个终端中运行了这个:

\n
$ python3\nPython 3.9.2 (default, Feb 28 2021, 17:03:44) \n>>> from dbus.bus import BusConnection\n>>> len(BusConnection("unix:path=/run/user/1000/bus").list_names())\n145\n
Run Code Online (Sandbox Code Playgroud)\n

在另一个终端中,我无法以 root 用户身份连接到该总线:

\n
# python3\nPython 3.9.2 (default, Feb 28 2021, 17:03:44) \n>>> from dbus.bus import BusConnection\n>>> address = "unix:abstract=/tmp/dbus-j0r67hLIuh,guid=d100052e45d06f248242109262325b98"\n>>> BusConnection(address).list_names()\ndbus.Array([dbus.String(\'org.freedesktop.DBus\'), dbus.String(\':1.0\')], signature=dbus.Signature(\'s\'))\n
Run Code Online (Sandbox Code Playgroud)\n

在全局安装时,这还应该允许访问系统上的所有session-local.conf会话总线:

\n
# cp session-local.conf /etc/dbus-1/session-local.conf\n# kill -HUP 1865   # reload config of my users session dbus-daemon\n# python3\n>>> from dbus.bus import BusConnection\n>>> len(BusConnection("unix:path=/run/user/1000/bus").list_names())\n143\n
Run Code Online (Sandbox Code Playgroud)\n

它可以工作了 - 现在 root 可以连接到任何会话总线,而无需求助于seteuid. 别忘了

\n
# rm /etc/dbus-1/session-local.conf \n
Run Code Online (Sandbox Code Playgroud)\n

如果您的 root 用户不需要此权限。

\n