Messages in this thread Patch in this message |  | | Date | Sat, 1 Jun 96 03:50 BST | From | Jamie Lokier <> | Subject | /proc/self/exe unreadable by suid programs |
| |
The particular example I have in mind, is a setuid root program that drops its root euid as soon as it can, and then later tries to read its own executable (which happens to have data stuck on the end). The dropping of the root euid is done by a library, prior to calling `main', so it's not so easy to open /proc/self/exe first.
The same problem prevents the program from opening /proc/self/mem.
It is unusual for a setuid root program to have less permission than the same program without setuid!
The problem is due to the following test in fs/proc/inode.c:
if (ino == PROC_PID_INO || p->dumpable) { inode->i_uid = p->euid; inode->i_gid = p->egid; }
When this condition is not satisfied, inode->i_uid and inode->i_gid are both set to zero.
I understand the need for this test even in the "exe" case: otherwise, a program that's switching its euid all the time (like a file server that doesn't know about fsuid) could inadvertantly give access to a file (the executable) which may not be reachable to ordinary users by any other means.
It seems to me that everything referenced through /proc/self should be accessible to the process, though the same information, fetched through /proc/<pid>, may be denied to other processes.
The following patch implements this:
--- linux/fs/proc/base.c.orig Sat Jun 1 04:15:22 1996 +++ linux/fs/proc/base.c Sat Jun 1 04:13:46 1996 @@ -57,7 +57,7 @@ for_each_task(p) { if (p->pid == pid) { - if (p->dumpable || ino == PROC_PID_INO) { + if (p->dumpable || p == current || ino == PROC_PID_INO) { inode->i_uid = p->euid; inode->i_gid = p->gid; } --- linux/fs/proc/inode.c.orig Sat Jun 1 01:52:25 1996 +++ linux/fs/proc/inode.c Sat Jun 1 04:14:16 1996 @@ -193,7 +193,7 @@ return; } ino &= 0x0000ffff; - if (ino == PROC_PID_INO || p->dumpable) { + if (ino == PROC_PID_INO || p->dumpable || p == current) { inode->i_uid = p->euid; inode->i_gid = p->egid; } BTW, there is some duplication between proc_read_inode, proc_pid_fill_inode and proc_get_inode for /proc/<pid>/*. The permissions, reference counts and operations are set by proc_read_inode, and then overwritten by proc_get_inode (which uses the proc_dir_entry structure). proc_pid_fill_inode then changes the user and group ids, ignoring the values chosen by proc_read_inode. (That had me confused for a while). I would be inclined to delete parts of proc_read_inode, leaving the settings in the proc_dir_entry structures.
There is some conflict: proc_read_inode sets the reference count for /proc/<pid> to 4, but then (somehow) the directory's reference count comes out as 3. The directory link counts are generally wrong, due to conflicting calculations. For example, /proc has a reference count of 5 (it should be much larger). /proc/net has a reference count of 2, despite proc_register and proc_register_dynamic going to all the effort of counting the links. There are others, too.
Enjoy, -- Jamie
|  |