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.
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.