2015-06-02 121 views
5

我一直在处理一个问题,现在需要更新20年的代码,这些代码需要独立于系统(可以在Linux和Windows上运行)。它涉及检查时间(Time-of-Check),使用时间(TOCTOU)问题。我做了一个线程here,但它并没有走得太远,在反复思考了一段时间并深入研究这个问题之后,我想我理解我的问题会好一点。也许我可以问得更好一点...stat(),fstat(),lstat()和fopen();如何编写TOCTOU受保护的系统独立代码

从我读过的代码需要检查文件是否存在,是否可访问,打开文件,做一些操作,最后关闭文件。看起来最好的方法是致电lstat(),致电fopen(),致电fstat()(以排除TOCTOU),然后执行操作并关闭文件。

不过,我已经带领相信lstat()fstat()被POSIX定义, C标准定义,排除了他们对系统无关的程序使用,多以同样的方式open()不应该被用于交叉兼容性。你将如何实现这一点?

如果你看at my first post,你可以看到从20年前开发人员使用C预处理程序来削减代码为跨平台兼容性的部分,但即使我这样做,我不知道是什么,以取代lstat()fstat() (他们的窗口对应)。

编辑:添加abreviated代码到这个职位;如果有什么不清楚please go to the original post

#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); 
} 
+0

我认为有特定的Windows API功能,可以帮助你分开编写Windows部分。 –

+2

您可以为每个操作系统使用包装模块,并使用条件构建。 – Olaf

+0

@iharob任何想法他们可能被称为?我刚刚离开学校,我们从来没有使用过任何windows api,完全是gnu;也许是开始学习的好资源? – Makenbaccon

回答

5

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

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

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

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

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

所有这些都适用于Unix端。

细节将有所不同,但在Windows端,您仍然最好尝试打开文件并对错误进行适当的反应。

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

+0

我不得不仔细看看使用'open()'和控件。我希望不要使用POSIX电话,但你说得对,可能是要走的路。谢谢你为我拼写的东西。 = d – Makenbaccon