Bug 1895864 - A user part of a 'monitoring' group cannot read another user's /proc/XXX/cwd
Summary: A user part of a 'monitoring' group cannot read another user's /proc/XXX/cwd
Keywords:
Status: CLOSED NOTABUG
Alias: None
Product: Red Hat Enterprise Linux 7
Classification: Red Hat
Component: kernel
Version: 7.9
Hardware: All
OS: Linux
medium
medium
Target Milestone: rc
: ---
Assignee: Oleg Nesterov
QA Contact: Kernel General QE
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2020-11-09 10:14 UTC by Renaud Métrich
Modified: 2020-11-10 08:46 UTC (History)
2 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2020-11-09 16:16:51 UTC
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)

Description Renaud Métrich 2020-11-09 10:14:10 UTC
Description of problem:

Some nodes are "protected" in /proc/XXX, in particular "cwd". Even though a user has the supplementary group of another user's it wants to monitor and "/proc/XXX/cwd"'s target shows group right is readable, then monitoring user cannot access "/proc/XXX/cwd".


Version-Release number of selected component (if applicable):

RHEL7 kernel


How reproducible:

ALWAYS


Steps to Reproduce:
1. Create a user being monitored

  # useradd monitored_user
  # chmod 750 /home/monitored_user

2. Create monitoring user

  # useradd -G monitored_user monitoring_user

3. Start a shell as monitored_user

  # su - monitored_user
  $ id
  uid=1007(monitored_user) gid=1007(monitored_user) groups=1007(monitored_user) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
  $ echo $$
  3208
  $ ls -ld /proc/$$/cwd
  lrwxrwxrwx. 1 monitored_user monitored_user 0 Nov  9 10:59 /proc/3208/cwd -> /home/monitored_user

4. Start a shell as monitoring_user and try accessing /proc/3208/cwd

  # su - monitoring_user
  $ id
  uid=1008(monitoring_user) gid=1008(monitoring_user) groups=1008(monitoring_user),1007(monitored_user) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
  $ ls -ld /proc/3208/cwd


Actual results:

  ls: cannot read symbolic link '/proc/3208/cwd': Permission denied
  lrwxrwxrwx. 1 monitored_user monitored_user 0 Nov  9 10:59 /proc/3208/cwd
  

Expected results:

  Can read the symlink

Additional info:

The "workaround" is to add "cap_sys_ptrace" capability to "/usr/bin/ls", but this has security implications:

  # setcap cap_sys_ptrace=ep /usr/bin/ls

This works because internally procfs code checks for PTRACE capability to authorize the monitoring user, as seen in the systemtap callgraph below (7137 is the PID of the "monitoring user" shell):

# stap -v -x 7137 ./para-callgraph.stp 'kernel.function("*@fs/proc*.c")' 'syscall.readlink'

-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
     0 ls(9992):->pid_revalidate dentry=0xffff9eb87b750540 flags=0x4050
     3 ls(9992):<-pid_revalidate return=0xfffffffffffffff6
     0 ls(9992):->pid_revalidate dentry=0xffff9eb87b750540 flags=0x4010
     1 ls(9992):<-pid_revalidate return=0x1
     0 ls(9992):->proc_pid_permission inode=0xffff9eb87b768590 mask=0x1
     1 ls(9992): ->has_pid_permissions task=0xffff9eb87617e300 hide_pid_min=0x1 pid=? pid=?
     2 ls(9992): <-has_pid_permissions return=0x1
     3 ls(9992):<-proc_pid_permission return=0x0
     0 ls(9992):->pid_revalidate dentry=0xffff9eb87b72f600 flags=0x4000
     1 ls(9992):<-pid_revalidate return=0x1
     0 ls(9992):->proc_pid_readlink dentry=0xffff9eb87b72f600 buffer=0xdb2330 buflen=0x1
     2 ls(9992): ->proc_fd_access_allowed inode=0xffff9eb87b763550
     4 ls(9992): <-proc_fd_access_allowed return=0x0
     5 ls(9992):<-proc_pid_readlink return=0xfffffffffffffff3
     0 ls(9992):->pid_delete_dentry dentry=0xffff9eb87b72f600
     0 ls(9992):<-pid_delete_dentry return=0x0
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

From above, we see that "proc_fd_access_allowed()" returned 0x0, preventing continuing readlink() code.

If I do the same test from a new terminal running as "bashuser", I can see this just works fine:
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
     0 ls(10981):->proc_pid_readlink dentry=0xffff9eb87b72f600 buffer=0x18e0330 buflen=0x1
     7 ls(10981): ->proc_fd_access_allowed inode=0xffff9eb87b763550
    13 ls(10981): <-proc_fd_access_allowed return=0x1
    19 ls(10981): ->proc_cwd_link dentry=0xffff9eb87b72f600 path=0xffff9eb86ac53e90
    25 ls(10981): <-proc_cwd_link return=0x0
    34 ls(10981):<-proc_pid_readlink return=0x1
    ...
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

The corresponding procfs code is shown below:

-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
1501 static int proc_pid_readlink(struct dentry * dentry, char __user * buffer, int buflen)
1502 {
1503 	int error = -EACCES;
1504 	struct inode *inode = dentry->d_inode;
1505 	struct path path;
1506 
1507 	/* Are we allowed to snoop on the tasks file descriptors? */
1508 	if (!proc_fd_access_allowed(inode))
1509 		goto out;
1510 
1511 	error = PROC_I(inode)->op.proc_get_link(dentry, &path);
1512 	if (error)
1513 		goto out;
1514 
1515 	error = do_proc_readlink(&path, buffer, buflen);
1516 	path_put(&path);
1517 out:
1518 	return error;
1519 }

550 static int proc_fd_access_allowed(struct inode *inode)
551 {
552 	struct task_struct *task;
553 	int allowed = 0;
554 	/* Allow access to a task's file descriptors if it is us or we
555 	 * may use ptrace attach to the process and find out that
556 	 * information.
557 	 */
558 	task = get_proc_task(inode);
559 	if (task) {
560 		allowed = ptrace_may_access(task, PTRACE_MODE_READ);
561 		put_task_struct(task);
562 	}
563 	return allowed;
564 }
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

On line 1508 above, we see a check, which is shown on lines 551+.
Here above the code checks for "ptrace" capability, which my user doesn't have, causing "allowed = 0" to be returned and the permission denied to happen.

Comment 2 Oleg Nesterov 2020-11-09 16:16:51 UTC
Sorry, I don't understand this report.

(In reply to Renaud Métrich from comment #0)
> 
> Some nodes are "protected" in /proc/XXX, in particular "cwd". Even though a
> user has the supplementary group of another user's it wants to monitor and
> "/proc/XXX/cwd"'s target shows group right is readable,
                                 ^^^^^^^^^^^^^^^^^^^^^^^
this is irrelevant,

> then monitoring user
> cannot access "/proc/XXX/cwd".

because readlink fails,

> Here above the code checks for "ptrace" capability, which my user doesn't
> have, causing "allowed = 0" to be returned and the permission denied to
> happen.

Yes, so as you can see this is by design.

Where is the bug?

Yes, on the normal filesystem readlink always works. But /proc is special,
readlink fails for the sake of security.


Note You need to log in before you can comment on or make changes to this bug.