嘿,我在尝试从python调用ioctl linux系统调用时遇到了问题。
在C应用程序中运行以下行我设法获取给定linux命名空间文件描述符的父文件描述符。
from fcntl import ioctl
NS_GET_PARENT = (0x7b << (4*2)) | 2
f = open('/proc/1337/ns/user')
fd = ioctl(f.fileno(),NS_GET_PARENT)
但是在python中运行相同的以下脚本会给我“设备不合适的ioctl”
open("/proc/1337/ns/user", O_RDONLY) = 3
ioctl(3, _IOC(0, 0xb7, 0x02, 0x00), 0)
通过在两个脚本上运行strace,我发现它们都发出相同的系统调用
-1 ENOTTY (Inappropriate ioctl for device)
区别在于,当python代码返回时,C代码实际上将文件描述符返回到父名称空间
document.getElementById('play').addEventListener('click', function (e) {
e.preventDefault();
document.getElementById('audio').play();
});
该问题在2台不同的计算机上复制(Linux Kernel 4.13.0-37),并且这两个脚本都在同一个用户上运行。 任何人都知道可能导致这个问题的原因吗?
答案 0 :(得分:1)
注意:现在有一个可用的Python 3库,该库涵盖了Linux命名空间ioctl:PyPi:linuxns-rel,GitHub:thediveo/linuxns_rel。
这是一个棘手的问题。为什么您的示例不起作用有两个(三个)方面,我花了一些时间弄清楚它们并了解了很多有关Linux名称空间的信息。
首先,它是NSIO=0xb7
(而不是不是 NSIO=0x7b
),如您的Python示例中那样,NS_GET_PARENT
的正确值是(我完全错过了该错误起初,只有在我从头开始完成工作PoC后,才注意到Linux头文件宏定义非常紧密):
NS_GET_PARENT = (0xb7 << (4*2)) | 2
第二,基于我在Python 3的实验中看到的内容,关于Python ioctl()
引发的异常的信息似乎有第二个错误,不正确的ioctl是ENOTTY
(25) 。 ENOTTY
将与错误的NSIO
的{{1}}而不是0x7b
相适应。实际上,当我逐字尝试您的示例时,我得到了0xb7
。那时我还没有意识到这表明请求代码无效。
使用正确的ENOTTY
请求代码,错误NS_GET_PARENT
(1)会适合Linux内核由于权限限制而不允许您查看父名称空间的情况(即使您是超级用户,则您可能只在本地另一个用户用户空间中,等等。
因此,此经过纠正的代码有效,但受访问限制(抛出EPERM
OSError
s的限制):
EPERM
由于您没有提供有关系统用户名称空间设置的详细信息,因此我怀疑您也陷入了同样的陷阱:向“ root”用户名称空间中的进程查询其父用户工作空间:会产生误导性的from fcntl import ioctl
NS_GET_PARENT = (0xb7 << (4*2)) | 2
with open('/proc/self/ns/user') as f:
fd = ioctl(f.fileno(), NS_GET_PARENT)
,但是对于ioctl_ns(2)所说的话,这是正确的。
验证以上代码是否有效的一个好技巧是首先启动Firefox,然后启动EPERM
。这将显示一组在其自己的用户,ipc和网络名称空间中运行的Firefox子进程。采取以下Firefox子进程之一的PID,然后运行上面的示例:它现在应该成功,并且如果您sudo lsns -t user
返回的文件描述符,它应该引用root用户名称空间。尝试获取根用户名称空间的父级应该再次失败,并显示fstat()
。