如果我们正常打开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 服务?
我对 DBus 知之甚少,但这个问题让我很好奇。
\nTL;DR:dbus.bus.BusConnection与目标用户的套接字地址一起使用seteuid获得访问权限。
第一个问题: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\nRun Code Online (Sandbox Code Playgroud)\n也许它依赖于环境变量?明白了!
\n$ env|grep /run/user/1000/bus\nDBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus\nRun Code Online (Sandbox Code Playgroud)\n从 root 帐户追踪行为,它似乎不知道要连接的地址。谷歌搜索变量名称让我找到了D-Bus 规范“众所周知的消息总线实例”部分。
\n第二个问题:我们可以直接连接到套接字而不让 D-Bus 库猜测正确的地址吗?dbus-python教程指出:
\n\n\n出于特殊目的,您可以使用非默认总线,或者根本不是总线的连接,使用 dbus-python 0.81.0 中添加的一些新 API。
\n
查看变更日志,这似乎指的是这些:
\n\n\nBus 有一个超类 dbus.bus.BusConnection (与总线守护进程的连接,但没有共享连接语义或任何已弃用的 API),以方便那些想要子类化总线守护进程连接的人
\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\nRun Code Online (Sandbox Code Playgroud)\nroot访问权限怎么样?
\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\nRun Code Online (Sandbox Code Playgroud)\n所以这回答了问题:使用BusConnection代替SessionBus并显式指定地址,结合seteuid来获得访问权限。
我仍然想知道是否可以以 root 用户身份直接访问总线,而无需求助于seteuid. 经过几次搜索查询后,我找到了一张 systemd 票证,上面有这样的评论:
\n\ndbus-daemon 是强制访问的组件...(但您可以将 xml 策略文件放入其中,以实现此目的)。
\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\nRun 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\'))\nRun Code Online (Sandbox Code Playgroud)\n在全局安装时,这还应该允许访问系统上的所有session-local.conf会话总线:
# 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\nRun Code Online (Sandbox Code Playgroud)\n它可以工作了 - 现在 root 可以连接到任何会话总线,而无需求助于seteuid. 别忘了
# rm /etc/dbus-1/session-local.conf \nRun Code Online (Sandbox Code Playgroud)\n如果您的 root 用户不需要此权限。
\n