8

几个星期以来我一直在处理一个问题,现在更新需要独立于系统的 20 年代码(在 Linux 和 Windows 上都可以工作)。它涉及检查时间、使用时间 (TOCTOU) 问题。我在这里做了一个线程,但它并没有走得很远,在思考了一段时间并深入研究了这个问题之后,我想我对我的问题有了更好的理解。也许我也可以问好一点...

根据我的阅读,代码需要检查文件是否存在,是否可以访问,打开文件,执行一些操作,最后关闭文件。似乎最好的方法是调用lstat(),调用fopen(),调用fstat()(以排除 TOCTOU),然后执行操作并关闭文件。

但是,我一直相信lstat()并且fstat()是 POSIX 定义的,而不是C 标准定义的,排除了它们用于与系统无关的程序,同样的方式open()不应该用于交叉兼容性。你将如何实现这一点?

如果你看我的第一篇文章,你可以看到 20 年前的开发人员使用 C 预处理器将代码切割成交叉兼容的部分,但即使我这样做了,我也不知道要替换什么lstat()fstat()用什么(他们的Windows 对应)。

编辑:在这篇文章中添加了缩写代码;如果有不清楚的地方,请转到原帖

#ifdef WIN32
    struct _stat buf;
#else
    struct stat buf;
#endif //WIN32

    FILE *fp;
    char data[2560];

    // Make sure file exists and is readable
#ifdef WIN32
    if (_access(file.c_str(), R_OK) == -1) {
#else
    if (access(file.c_str(), R_OK) == -1) {
#endif //WIN32

        char message[2560];
        sprintf(message, "File '%s' Not Found or Not Readable", file.c_str());
        throw message;
        }

    // Get the file status information
#ifdef WIN32
    if (_stat(file.c_str(), &buf) != 0) {
#else
    if (stat(file.c_str(), &buf) != 0) {
#endif //WIN32

        char message[2560];
        sprintf(message, "File '%s' No Status Available", file.c_str());
        throw message;
        }

    // Open the file for reading
    fp = fopen(file.c_str(), "r");
    if (fp == NULL) {
        char message[2560];
        sprintf(message, "File '%s' Cound Not be Opened", file.c_str());
        throw message;
    }

    // Read the file
    MvString s, ss;
    while (fgets(data, sizeof(data), fp) != (char *)0) {
        s = data;
        s.trimBoth();
        if (s.compare( 0, 5, "GROUP" ) == 0) {
            //size_t t = s.find_last_of( ":" );
            size_t t = s.find( ":" );
            if (t != string::npos) {
                ss = s.substr( t+1 ).c_str();
                ss.trimBoth();
                ss = ss.substr( 1, ss.length() - 3 ).c_str();
                group_list.push_back( ss );
            }
        }
    }
    // Close the file
    fclose(fp);
}
4

1 回答 1

9

检查文件是否存在并且可以打开的可靠方法是尝试打开它。如果打开,一切正常。如果没有打开,可以考虑花点时间分析一下出了什么问题。

access()函数正式提出了一个与您的想法不同的问题;它询问'真实用户ID或真实组ID是否可以访问文件',但程序将使用有效用户ID或有效组ID来访问文件。如果您的程序没有运行 SUID 或 SGID,并且不是从运行 SUID 或 SGID 的程序启动的——这是正常情况——那么就没有区别。但问题是不同的。

使用stat()or lstat()似乎没有帮助。特别是,lstat()仅告诉您是否从符号链接开始,但代码并不关心这一点。

access()和调用都stat()为您提供了 TOCTOU 漏洞窗口;该文件可以在他们报告它存在后删除,或者在他们报告它不存在后创建。

你应该简单地打电话fopen()看看它是否有效;代码将更简单,更能抵抗 TOCTOU 问题。您可能需要考虑是否使用open()它的所有额外控件(O_EXCL等),然后将文件描述符转换为文件指针(fdopen())。

所有这些都适用于 Unix 端。

细节会有所不同,但在 Windows 方面,您仍然最好尝试打开文件并对失败做出适当的反应。

在这两个系统中,确保提供给 open 函数的选项是合适的。

于 2015-06-02T18:32:22.787 回答