Bug 1329691
| Summary: | glibc: faccessat emulation gives incorrect results for AT_EACCESS, AT_SYMLINK_NOFOLLOW | ||
|---|---|---|---|
| Product: | Red Hat Enterprise Linux 8 | Reporter: | Jiri Jaburek <jjaburek> |
| Component: | glibc | Assignee: | glibc team <glibc-bugzilla> |
| Status: | CLOSED WONTFIX | QA Contact: | qe-baseos-tools-bugs |
| Severity: | medium | Docs Contact: | |
| Priority: | medium | ||
| Version: | 8.2 | CC: | ashankar, codonell, dj, fweimer, kdudka, mnewsome, pfrankli, svashisht |
| Target Milestone: | rc | Keywords: | Patch, Reopened, Triaged |
| Target Release: | 8.0 | Flags: | pm-rhel:
mirror+
|
| Hardware: | All | ||
| OS: | Linux | ||
| Whiteboard: | |||
| Fixed In Version: | Doc Type: | Bug Fix | |
| Doc Text: | Story Points: | --- | |
| Clone Of: | Environment: | ||
| Last Closed: | 2022-11-11 14:27:42 UTC | Type: | Bug |
| Regression: | --- | Mount Type: | --- |
| Documentation: | --- | CRM: | |
| Verified Versions: | Category: | --- | |
| oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |
| Cloudforms Team: | --- | Target Upstream Version: | |
| Embargoed: | |||
| Bug Depends On: | 1333764 | ||
| Bug Blocks: | 1218420 | ||
This could be caused by the fact that all the commands you tried *except* the shell built-ins spawn a new process. Have you verified that the actual shell process running those built-ins is not permitted to access /etc/shadow for reading? Something like this could help: # read </etc/shadow I further checked (using gdb) that kernel indeed returns -1 to bash,
0x00007f4080ff7980 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81
81 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
Missing separate debuginfos, use: debuginfo-install ncurses-libs-5.9-13.20130511.el7.x86_64
(gdb) call access("/etc/shadow", 4)
$1 = -1
where 4 is
/usr/include/unistd.h:#define R_OK 4 /* Test for read permission. */
just in case there's something weird with execve(2) regarding contexts/transitions and the earlier "exec ./access" wouldn't spot it.
So this indeed seems like a bash issue.
(In reply to Kamil Dudka from comment #1) > This could be caused by the fact that all the commands you tried *except* > the shell built-ins spawn a new process. Have you verified that the actual > shell process running those built-ins is not permitted to access /etc/shadow > for reading? > > Something like this could help: > > # read </etc/shadow Please see the comment #0 thoroughly, I go into extra detail regarding that. # test -r /etc/shadow; echo $? 0 # read </etc/shadow bash: /etc/shadow: Permission denied Thanks, Jiri (In reply to Jiri Jaburek from comment #4) > Please see the comment #0 thoroughly, I go into extra detail regarding that. I did. There was no command attempting to read the command in the *same* process in which you run the built-ins in question. > # test -r /etc/shadow; echo $? > 0 > > # read </etc/shadow > bash: /etc/shadow: Permission denied OK. Thanks for confirmation! (In reply to Kamil Dudka from comment #5) > There was no command attempting to read the command in the same process... Sorry, I meant read the *file* in the same process... bash uses faccessat() instead of access() to check permissions which might cause some differences : faccessat (AT_FDCWD, path, mode, AT_EACCESS) I have tried reproducing this bug on a RHEL 7.2 server minimal install by following below steps : 1. Install RHEL 7.2 server minimal. 2. Enable MLS by following steps from https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/SELinux_Users_and_Administrators_Guide/mls.html 3. I was not able to login to tty after enabling MLS in enforcing mode, so I booted with 'enforce=0' parameter, logged in as root and did 'setenforce 1' to enable MLS. However this issue was not reproducible. A reproducer machine has been provided. I'm taking this bug on the QE side, we'll verify the solution during MLS testing. faccessat() function seems to behave differently with AT_EACCESS flag :
(gdb) p faccessat (-100, "/etc/shadow", 4, 512)
$1 = 0
where AT_EACCESS=512.
Otherwise it works correctly.
(gdb) p access("/etc/shadow", 4)
$3 = -1
(gdb) p faccessat (-100, "/etc/shadow", 4, 0)
$4 = -1
I am still checking how effective user id of the process is different from it's real user id.
The root cause of this issue is value of '__libc_enable_secure' variable. This variable is being checked in faccessat() function in glibc-2.17-c758a686/sysdeps/unix/sysv/linux/faccessat.c :
On ssh session it is set to 0 :
(gdb) p __libc_enable_secure
$1 = 0
so this condition will be true :
45 if ((flag == 0 || ((flag & ~AT_EACCESS) == 0 && ! __libc_enable_secure))
46 # ifndef __ASSUME_ATFCTS
47 && __have_atfcts >= 0
48 # endif
49 )
50 {
51 int result = INLINE_SYSCALL (faccessat, 3, fd, file, mode);
52 # ifndef __ASSUME_ATFCTS
53 if (result == -1 && errno == ENOSYS)
54 __have_atfcts = -1;
55 else
56 # endif
57 return result;
58 }
59 #endif
and actual 'faccessat()' system call is made to verify file permissions.
However on tty it is set to 1 :
(gdb) p __libc_enable_secure
$1 = 1
so this condition will be true :
131 if (uid == 0 && ((mode & X_OK) == 0
132 || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))))
133 return 0;
134
and since root is trying to acccess the file, permissions will be granted without further checking SELinux permissions.
bash has AT_SECURE flag enabled when it is started via tty :
(gdb) p getauxval(23)
$1 = 1
This would enable '__libc_enable_secure' variable in glibc-2.17-c758a686/elf/dl-sysdep.c :
147 case AT_SECURE:
148 #ifndef HAVE_AUX_SECURE
149 seen = -1;
150 #endif
151 INTUSE(__libc_enable_secure) = av->a_un.a_val;
152 break;
It seems MLS enables AT_SECURE flag while starting bash via tty.
We need proper kernel interfaces to fix this. There is no other way. I filed bug 1333764 to cover the kernel side. We are tracking this upstream. We need kernel support for this first, anyway. If upstream support becomes available, we can consider backporting the change (the glibc part will be easy enough). The upstream fix is here:
commit 3d3ab573a5f3071992cbc4f57d50d1d29d55bde2
Author: Florian Weimer <fweimer>
Date: Fri Aug 7 22:06:59 2020 +0200
Linux: Use faccessat2 to implement faccessat (bug 18683)
This provides correct AT_EACCESS handling and also takes
Linux security modules into account.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella>
This can work with a RHEL8/UBI8 container running on RHEL9 host (Kernel 5.14).
Reopening for consideration in RHEL 8.
Our experience with fixing this in Fedora and Red Hat Enterprise Linux 9 shows that such a change in the system call profile of glibc can be very disruptive. The faccessat function is used in key places in bash and systemd, so the new system call would be used immediately by containers. Most of the system call filtering issues should have been fixed by now. On the other hand, it is relatively late in the Red Hat Enterprise Linux 8 life-cycle, and this change is too disruptive at this point. |
Description of problem: Bash seems to, under some circumstances, ignore access(2) for the root user. # [ -r /etc/shadow ]; echo $? 0 # [[ -r /etc/shadow ]]; echo $? 0 # test -r /etc/shadow; echo $? 0 # /bin/[ -r /etc/shadow ]; echo $? 1 # /bin/test -r /etc/shadow; echo $? 1 # cat /etc/shadow cat: /etc/shadow: Permission denied # id -Z root:sysadm_r:sysadm_t:SystemLow-SystemHigh The denial happens because with SELinux MLS policy, sysadm_r cannot read /etc/shadow. The access(2) syscall is pretty clear about that: access("/etc/shadow", R_OK) = -1 EACCES (Permission denied) I'm not sure if this happens due to some form of caching as the issue is NOT present in further bash shells spawned from the login one, not even in 'bash -l' shells. # test -r /etc/shadow; echo $? 0 # bash -c 'test -r /etc/shadow; echo $?' 1 # bash -l # test -r /etc/shadow; echo $? 1 However fresh log-ins into the system exhibit the 'wrong' behavior, so I'm not sure about the caching hypothesis. The 'wrong' behavior happens when logging via ssh (normal user, then 'newrole -r sysadm_r', then '/bin/su -') as well as when just logging via TTY (through login(1) straight to bash) or hooking the process, enforcing screen(1) spawn through /etc/profile (like we do for Common Criteria), but I can't think of anything that would be /special/ to the first login shell. Another theory would be that the shell is simply spawned in a wrong context and any new commands in the correct one, explaining why builtins behave differently to new spawned bash instances, but that doesn't seem to be true, notice the shell and ps having the same context: # ps -Z LABEL PID TTY TIME CMD root:sysadm_r:sysadm_t:SystemLow-SystemHigh 18580 ttyS0 00:00:00 bash root:sysadm_r:sysadm_t:SystemLow-SystemHigh 18607 ttyS0 00:00:00 ps Furthermore, exec'ing an access(2) check seems to fail correctly, # cat access.c #include <stdio.h> #include <unistd.h> int main () { printf("%d\n", access("/etc/shadow", R_OK)); return 0; } # gcc access.c -o access # test -r /etc/shadow; echo $? 0 # ./access -1 # exec ./access -1 so it doesn't seem to be an external event granting the initial bash instance extra privileges. I've also tried to reproduce it on a SELinux targeted system (as the issue appears in MLS), but without success: # cat test.te policy_module(mypolicy,1.0) type completely_new_t; require { type unconfined_t; class file { ioctl getattr lock execute execute_no_trans open append write }; }; allow unconfined_t completely_new_t : file { ioctl getattr lock execute execute_no_trans open append write }; # ls -Z /testf -rwxrwxrwx. root root unconfined_u:object_r:completely_new_t:s0 /testf # cat /testf cat: /testf: Permission denied # test -r /testf; echo $? 1 # id -Z unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 however I might have missed some essential privileges in the policy module that would trigger the behavior, so I'm still unsure whether it's a bash issue or not (as bash might have checks for selinux and behave differently). Version-Release number of selected component (if applicable): bash-4.2.46-19.el7.x86_64 How reproducible: always Steps to Reproduce: 1. switch a system to SELinux MLS policy (and reboot) 2. login on TTY as root 3. test -r /etc/shadow; echo $? should print 0 4. /bin/test -r /etc/shadow; echo $? should print 1 Actual results: the test builtin returns 0 Expected results: the test builtin (and [ , [[ ) returns 1 Additional info: CCing mgrepl for the SELinux side of things, to help identify whether this is a SELinux issue or not.