我对DBus的知识很少,但那个问题引起了我的好奇心。
TL;DR: 使用目标用户的套接字地址和seteuid
来获取访问权限,请使用dbus.bus.BusConnection
。
第一个问题:DBus连接到哪个套接字以获得会话总线?
$ cat list_bus.py
import dbus
print(dbus.SessionBus().list_names())
$ strace -o list_bus.trace python3 list_bus.py
$ grep ^connect list_bus.trace
connect(3, {sa_family=AF_UNIX, sun_path="/run/user/1000/bus"}, 20) = 0
也许它依赖于环境变量?明白了!
$ env|grep /run/user/1000/bus
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
从root账户跟踪行为可以看出它不知道连接的地址。通过搜索变量名称,我找到了D-Bus规范,其中的"Well-known Message Bus Instances"章节。
第二个问题:我们是否可以直接连接到套接字而不需要D-Bus库猜测正确的地址?dbus-python教程中指出:
对于特殊目的,您可能会使用非默认的总线,或者使用dbus-python 0.81.0中添加的一些新API来连接不是总线的连接。
查看更改日志,似乎是指这些内容:
Bus拥有一个超类dbus.bus.BusConnection(与总线守护进程的连接,但没有共享连接语义或任何已弃用的API),以便那些想要子类化总线守护进程连接的人受益。
让我们试一下:
$ python3
Python 3.9.2 (default, Feb 28 2021, 17:03:44)
>>> from dbus.bus import BusConnection
>>> len(BusConnection("unix:path=/run/user/1000/bus").list_names())
145
如何获取 Root 权限?
>>> from dbus.bus import BusConnection
>>> len(BusConnection("unix:path=/run/user/1000/bus").list_names())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3/dist-packages/dbus/bus.py", line 124, in __new__
bus = cls._new_for_bus(address_or_type, mainloop=mainloop)
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NoReply: Did not
receive a reply. Possible causes include: the remote application did not send
a reply, the message bus security policy blocked the reply, the reply timeout
expired, or the network connection was broken.
>>> import os
>>> os.seteuid(1000)
>>> len(BusConnection("unix:path=/run/user/1000/bus").list_names())
143
所以这个问题的答案是: 使用BusConnection
代替SessionBus
并显式指定地址,再结合使用seteuid
来获得访问权限。
奖励: 不使用seteuid以root身份连接
我还想知道是否可能直接作为root用户访问总线,而不需要使用seteuid
。经过一些搜索,我找到了一个systemd ticket,其中有如下评论:
dbus-daemon是强制访问的组件...(但你可以放置一个xml策略文件来实现这一点)。
这让我想起了一个askubuntu问题,讨论如何修改站点本地会话总线策略。
只是为了玩玩,我在一个终端中运行了以下命令:
$ cp /usr/share/dbus-1/session.conf session.conf
$ (edit session.conf to modify the include for local customization)
$ diff /usr/share/dbus-1/session.conf session.conf
50c50
< <include ignore_missing="yes">/etc/dbus-1/session-local.conf</include>
---
> <include ignore_missing="yes">session-local.conf</include>
$ cat > session-local.conf
<busconfig>
<policy context="mandatory">
<allow user="root"/>
</policy>
</busconfig>
$ dbus-daemon --config-file session.conf --print-address
unix:abstract=/tmp/dbus-j0r67hLIuh,guid=d100052e45d06f248242109262325b98
$ dbus-daemon --config-file session.conf --print-address
unix:abstract=/tmp/dbus-j0r67hLIuh,guid=d100052e45d06f248242109262325b98
在另一个终端中,我无法以root用户身份连接到此总线:
Python 3.9.2 (default, Feb 28 2021, 17:03:44)
>>> from dbus.bus import BusConnection
>>> address = "unix:abstract=/tmp/dbus-j0r67hLIuh,guid=d100052e45d06f248242109262325b98"
>>> BusConnection(address).list_names()
dbus.Array([dbus.String('org.freedesktop.DBus'), dbus.String(':1.0')], signature=dbus.Signature('s'))
这还应该使得在全局安装 session-local.conf
时能够访问系统上的 所有 会话总线:
>>> from dbus.bus import BusConnection
>>> len(BusConnection("unix:path=/run/user/1000/bus").list_names())
143
现在它可以工作了 - 现在root用户可以连接到任何会话总线,而不需要使用seteuid
。别忘了
# rm /etc/dbus-1/session-local.conf
如果您的root用户不需要这种权限。