简体   繁体   中英

root can't open file created by NamedTemporaryFile

I've got a Python script running on Linux that does something like

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

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

open: Permission denied

It works if I either don't run as root or don't add O_CREAT to 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". 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).

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.

  2. You're opening the file with O_CREAT

  3. Since kernel 4.19, Linux has the 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.

    When set to "0", writing to regular files is unrestricted.

    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.

    When set to "2" it also applies to group writable sticky directories.

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". I'm sure if you check the value of the fs.protected_regular sysctl, you'll find that it's either 1 or 2 .

The solution is either:

  • Create temporary files somewhere other than /tmp .
  • Set the fs.protected_regular sysctl to 0

I'd go with the first solution as demonstrated above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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