简体   繁体   English

root 无法打开 NamedTemporaryFile 创建的文件

[英]root can't open file created by NamedTemporaryFile

I've got a Python script running on Linux that does something like我有一个在 Linux 上运行的 Python 脚本,它执行类似的操作

with tempfile.NamedTemporaryFile() as output:
    permissions = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH
    os.chmod(output.name, permissions)
    subprocess.run(f'sudo ./some_executable -f {output.name}', shell=True)

where some_executable is a C program that contains其中some_executable是一个 C 程序,其中包含

fd = open(output_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if ( fd < 0 ) {
    perror("open");
    // bail
}

some_executable prints some_executable打印

open: Permission denied

It works if I either don't run as root or don't add O_CREAT to open .如果我不以root身份运行或不将O_CREAT添加到open ,它就可以工作。

Am I missing something obvious?我错过了一些明显的东西吗?

The error has nothing to do with "The C program is trying to open() a file that has already been opened by another process".该错误与“C 程序正在尝试打开()已被另一个进程打开的文件”无关。 The problem is caused entirely by the default use of /tmp for temporary files, the special semantics of that directory, and some protections built into Linux (which I'll explain at the end of this answer).该问题完全是由/tmp对临时文件的默认使用、该目录的特殊语义以及 Linux 中内置的一些保护措施引起的(我将在此答案的末尾解释)。

You can verify that by modifying your code to create a temporary file in the local directory instead:您可以通过修改代码以在本地目录中创建一个临时文件来验证这一点:

with tempfile.NamedTemporaryFile(dir=".") as output:
    permissions = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH
    os.chmod(output.name, permissions)
    subprocess.run(f'sudo ./some_executable -f {output.name}', shell=True)

This code will run without errors.此代码将无错误地运行。


The error you're seeing is caused by a combination of three things:您看到的错误是由三件事共同引起的:

  1. You're creating a file in /tmp , which is world writable and has the "sticky" bit set.您正在/tmp中创建一个文件,该文件是世界可写的,并且设置了“粘性”位。

  2. You're opening the file with O_CREAT您正在使用O_CREAT打开文件

  3. Since kernel 4.19, Linux has the protected_regular sysctl:由于 kernel 4.19,Linux 具有protected_regular sysctl:

    This protection is similar to protected_fifos, but it avoids writes to an attacker-controlled regular file, where a program expected to create one.这种保护类似于protected_fifos,但它避免了写入攻击者控制的常规文件,程序预计会在其中创建一个文件。

    When set to "0", writing to regular files is unrestricted.当设置为“0”时,写入常规文件不受限制。

    When set to "1" don't allow O_CREAT open on regular files that we don't own in world writable sticky directories, unless they are owned by the owner of the directory.当设置为“1”时,不允许O_CREAT在世界可写粘性目录中我们不拥有的常规文件上打开,除非它们归目录所有者所有。

    When set to "2" it also applies to group writable sticky directories.当设置为“2”时,它也适用于组可写粘性目录。

Reading the above documentation, you're hitting "don't allow O_CREAT open on regular files that we don't own in world writable sticky directories".阅读上述文档时,您会遇到“不允许 O_CREAT 打开我们在世界可写粘性目录中不拥有的常规文件”。 I'm sure if you check the value of the fs.protected_regular sysctl, you'll find that it's either 1 or 2 .我敢肯定,如果您检查fs.protected_regular sysctl 的值,您会发现它是12

The solution is either:解决方案是:

  • Create temporary files somewhere other than /tmp ./tmp以外的地方创建临时文件。
  • Set the fs.protected_regular sysctl to 0fs.protected_regular sysctl 设置为0

I'd go with the first solution as demonstrated above.我会用上面演示的第一个解决方案 go 。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM