根据openat()
提供了一种避免使用时间检查(TOCTTOU)错误的方法。
我对如何感到困惑。据我所知,openat
依赖于文件描述符,因此需要首先打开此文件描述符 - 这是否会引入TOCTTOU?
答案 0 :(得分:6)
openat()
的POSIX规范说:
int openat(int
fd
, const char *
path
, int
oflag
, ...);
[...常规
open()
上的长篇大论......除
openat()
指定相对路径的情况外,open()
函数应等效于path
函数。在这种情况下,要打开的文件是相对于与文件描述符fd
关联的目录而不是当前工作目录确定的。如果在没有O_SEARCH
的情况下打开文件描述符,则该函数将使用文件描述符下的目录的当前权限来检查是否允许目录搜索。如果使用O_SEARCH
打开文件描述符,则该函数不应执行检查。
oflag
参数和可选的第四个参数完全对应open()
的参数。如果
openat()
在AT_FDCWD
参数中传递了特殊值fd
,则应使用当前工作目录,其行为应与对open()
的调用相同
这意味着如果要在特定目录中或相对于特定目录放置文件,可以打开该目录的文件描述符(可能使用O_SEARCH
选项),然后指定相对于该目录的路径名openat()
系统调用中的该目录。
其他*at()
函数(例如fstatat()
)的工作方式类似。
首先,请注意文件描述符是目录的文件描述符。在打开目录(用于读取)时,它存在,并且进程有权访问目录及其中的文件。此外,因为此进程打开了目录,所以对该目录的最后引用不会消失,直到进程关闭目录文件描述符。如果它在已安装的文件系统上,则在程序终止之前不能卸载该文件系统(因为该进程打开了该目录)。如果目录被移动(在同一文件系统上),则将继续相对于该目录在文件系统中的当前位置创建文件。
事情从这里得到更多的推测 - 我还没有正式测试这些观察结果。
即使目录已删除,您似乎仍然可以创建相对于它的文件。如果名称是简单名称("new_file"
或"./new_file"
),那应该没问题。如果名称具有更多路径("subdir/new_file"
),则如果已删除目录,则创建或打开文件将失败,因为所有子目录也将被删除。
当然,有mkdirat()
来创建子目录。
据推测,文件系统必须在所有这些之后进行清理,这可能非常复杂。这意味着实际上您无法在您拥有打开文件描述符但名称已被删除的目录中创建文件。但是,您会知道这不再可能,而不是假设它是同一个目录。
无论哪种方式,攻击者都很难将您的程序混淆到在错误的目录中创建文件,只要您在使用正确的*at()
函数时一直保持谨慎和一致。
删除了一组TOCTOU攻击;那些依赖于重命名目录(或可能被删除)并且使用新名称(例如符号链接到某个其他位置)的攻击被挫败,因为文件继续相对于原始目录创建,而不是使用重命名或替换。目录
openat()
的POSIX规范的基本原理部分说:
openat()
函数的目的是在不暴露竞争条件的情况下启用当前工作目录以外的目录中的文件。文件路径的任何部分都可以与open()
的调用并行更改,从而导致未指定的行为。通过打开目标目录的文件描述符并使用openat()
函数,可以保证打开的文件相对于所需目录。某些实现也将openat()
函数用于其他目的。在某些情况下,如果oflag参数设置了O_XATTR
位,则返回的文件描述符提供对扩展属性的访问。此功能在此处未标准化。
答案 1 :(得分:1)
通过使用文件描述符,可以保证所有操作都在同一个文件中。如果您使用了文件名,那么文件名所引用的文件可能会因其他进程执行的操作而发生更改。