作为 this one 的后续问题,我想到了另一种方法,该方法基于@caf的答案,针对我想要附加到文件name
并创建它(如果它不存在)的情况。
这是我的想法:
- 在与文件
name
相同的文件系统上的系统临时目录中创建模式为0700的临时目录。 以只读方式打开文件name
和O_CREAT
。如果是符号链接(symbolic link),操作系统可能会遵循name
。使用mkstemp
在临时目录并尝试将mkstemp
创建的临时文件重命名
为文件name
。
以只读方式打开文件name
和O_CREAT | O_EXCL
。- 反复尝试在临时目录中的临时名称处建立到
name
的硬链接(hard link)。如果有link
由于“链接目标存在”(errnoEEXIST
) 之外的错误,调用失败,然后退出。 (也许有人已经删除了name
处的文件,谁知道呢?) - 使用
lstat
在temp_name
(硬链接(hard link))上。如果S_ISLNK(lst.st_mode)
,则退出。 -
open
temp_name
用于写入和附加 (O_WRONLY | O_APPEND
)。 - 写下所有内容。关闭文件描述符。
-
unlink
硬链接(hard link)。 - 删除临时目录。
(顺便说一下,所有这些都是针对我正在开发的 open source project 的。您可以查看我实现此方法的源代码 here 。)
此过程可以安全地抵御符号链接(symbolic link)攻击吗?例如,恶意进程是否有可能确保 name
的 inode 代表 lstat
持续时间内的常规文件。检查,然后使 inode 成为符号链接(symbolic link),其中 temp_name
硬链接(hard link)现在指向新的符号链接(symbolic link)?
我假设恶意进程无法影响 temp_name
。
编辑: link
不会覆盖目标,因此创建“占位符”临时文件不是我想要做的。我已经更新了我的代码并更新了上述步骤。
编辑2:我现在正在使用第2步的替代过程来创建文件name
(如果它不存在),我认为这不会受到 this problem 的影响。 .
EDIT3: 甚至比将临时的、空的常规文件重命名为 name
更好,这也具有取消链接 name
的效果,< em>然后重命名,我可以打开文件O_RDONLY | O_CREAT | O_EXCL
。
open
的 POSIX 标准规定:
If
O_EXCL
andO_CREAT
are set, andpath
names a symbolic link,open()
shall fail and seterrno
toEEXIST
, regardless of the contents of the symbolic link.
最佳答案
好吧,第 2 步有一个问题(“打开文件 name
仅供读取和 O_CREAT
。操作系统可能会遵循 name
,如果它是一个符号链接(symbolic link)。”)如果完全可利用,它可能允许非特权进程实质上触摸
文件系统中的任何路径。这样做的后果包括在下次重新启动时强制进行磁盘检查(通过触摸 /forcefsck
),以及其他更具破坏性的事情。例如,在从 Debian Lenny 升级到 Squeeze 时,dbus 和内核镜像都必须同时升级,因为每个相应的软件包都依赖于另一个软件包(新的 dbus 不能与旧内核一起工作,而旧的内核镜像则不能使用)。 dbus 不适用于新内核)。管理员解决此循环依赖关系的方法是通过touch
特定路径,该路径通知新的 dbus 包内核镜像将在下次重新启动之前升级。但是,如果恶意进程在首次升级之前触及
该路径,并在从 Lenny 升级到 Squeeze 期间重新启动,则系统可能无法启动。如果我没记错的话,第一次升级会安装新的 dbus,但是您必须再次显式升级才能安装新的内核镜像。 一次升级后重新启动可能会导致系统瘫痪。
浏览 source of GNU Coreutils' touch
,看起来他们只设置文件时间戳,但当 touch
传递 --no-dereference
选项时,如果文件不存在,则不会创建文件。他们使用实用函数 lutimens
来做到这一点。来自 gnulib,它包装了 Linux 的 utimensat
,允许符号链接(symbolic link)文件本身的时间戳(如果是这种情况)在 Linux >= 2.6.22 时更新。
关于c - 模拟O_NOFOLLOW (2) : Is this other approach safe?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2938563/