Why is /proc/self/fd/N forbidden after setuid?

835 Views Asked by At

Consider running the following Python code as root:

import os
f=os.open("/etc/shadow", os.O_RDONLY)
os.setuid(65535)
os.open(f"/proc/self/fd/{f}", os.O_RDONLY)

Here is a one-liner convenient for pasting:

python3 -c 'import os; f=os.open("/etc/shadow", os.O_RDONLY); os.setuid(65535); os.open(f"/proc/self/fd/{f}", os.O_RDONLY)'

Given the comment of proc_fd_permission, I would expect this code to succeed. However, I actually observe -EACCES. Why is this use of /proc/self/fd/N not permitted and what is the source code comment actually trying to convey?

Update: If the permission only applies to the symlink itself and not the target file, why can I open sockets and deleted files via /proc/self/fd/N? (e.g. exec 3>foo; echo hello >&3; rm foo; cat /proc/self/fd/3 prints hello)

1

There are 1 best solutions below

1
Tinkerer On

Inside your program, open files are represented as integer file descriptors. These are the filenames of the entries in the directory: /proc/self/fd. The kernel's /proc/ ABI helps you debug your system by displaying what those file descriptors refer to using the conventions of symlinks. But those symlinks are not equivalent to the open file descriptor.

$ ls -l /proc/self/fd
total 0
lrwx------. 1 tinkerer tinkerer 64 Mar 10 08:19 0 -> /dev/pts/0
lrwx------. 1 tinkerer tinkerer 64 Mar 10 08:19 1 -> /dev/pts/0
lrwx------. 1 tinkerer tinkerer 64 Mar 10 08:19 2 -> /dev/pts/0
lr-x------. 1 tinkerer tinkerer 64 Mar 10 08:19 3 -> /proc/230669/fd

If you close a file descriptor, there is no guarantee you can open it again. You have to satisfy the kernel's permission model to do that at the time you attempt to open the file. When you change UID, you should expect the permission model to view the open request differently.

If you want a second file descriptor to point to an already open file (descriptor), you should use the os.dup() method.