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:
You're creating a file in /tmp
, which is world writable and has the "sticky" bit set.
You're opening the file with O_CREAT
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:
/tmp
.fs.protected_regular
sysctl to 0
I'd go with the first solution as demonstrated above.