RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal. If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here. Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST" are being migrated throughout September 2023. Bugs of Red Hat partners with an assigned Engineering Partner Manager (EPM) are migrated in late September as per pre-agreed dates. Bugs against components "kernel", "kernel-rt", and "kpatch" are only migrated if still in "NEW" or "ASSIGNED". If you cannot log in to RH Jira, please consult article #7032570. That failing, please send an e-mail to the RH Jira admins at rh-issues@redhat.com to troubleshoot your issue as a user management inquiry. The email creates a ServiceNow ticket with Red Hat. Individual Bugzilla bugs that are migrated will be moved to status "CLOSED", resolution "MIGRATED", and set with "MigratedToJIRA" in "Keywords". The link to the successor Jira issue will be found under "Links", have a little "two-footprint" icon next to it, and direct you to the "RHEL project" in Red Hat Jira (issue links are of type "https://issues.redhat.com/browse/RHEL-XXXX", where "X" is a digit). This same link will be available in a blue banner at the top of the page informing you that that bug has been migrated.
Bug 1329691 - glibc: faccessat emulation gives incorrect results for AT_EACCESS, AT_SYMLINK_NOFOLLOW
Summary: glibc: faccessat emulation gives incorrect results for AT_EACCESS, AT_SYMLINK...
Keywords:
Status: CLOSED WONTFIX
Alias: None
Product: Red Hat Enterprise Linux 8
Classification: Red Hat
Component: glibc
Version: 8.2
Hardware: All
OS: Linux
medium
medium
Target Milestone: rc
: 8.0
Assignee: glibc team
QA Contact: qe-baseos-tools-bugs
URL:
Whiteboard:
Depends On: 1333764
Blocks: 1218420
TreeView+ depends on / blocked
 
Reported: 2016-04-22 15:21 UTC by Jiri Jaburek
Modified: 2023-07-18 14:30 UTC (History)
8 users (show)

Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Clone Of:
Environment:
Last Closed: 2022-11-11 14:27:42 UTC
Type: Bug
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Sourceware 18683 0 P2 RESOLVED Linux faccessat implementation can incorrectly ignore AT_EACCESS 2022-11-08 18:52:32 UTC

Description Jiri Jaburek 2016-04-22 15:21:19 UTC
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.

Comment 1 Kamil Dudka 2016-04-22 15:34:50 UTC
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

Comment 2 Jiri Jaburek 2016-04-22 15:35:26 UTC
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.

Comment 4 Jiri Jaburek 2016-04-22 15:38:13 UTC
(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

Comment 5 Kamil Dudka 2016-04-22 15:43:51 UTC
(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!

Comment 6 Kamil Dudka 2016-04-22 15:45:29 UTC
(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...

Comment 7 Siteshwar Vashisht 2016-05-03 19:45:20 UTC
bash uses faccessat() instead of access() to check permissions which might cause some differences :

faccessat (AT_FDCWD, path, mode, AT_EACCESS)

Comment 11 Siteshwar Vashisht 2016-05-05 13:57:10 UTC
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.

Comment 12 Jiri Jaburek 2016-05-05 14:35:38 UTC
A reproducer machine has been provided. I'm taking this bug on the QE side, we'll verify the solution during MLS testing.

Comment 13 Siteshwar Vashisht 2016-05-05 20:09:04 UTC
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.

Comment 14 Siteshwar Vashisht 2016-05-06 09:38:41 UTC
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.

Comment 16 Siteshwar Vashisht 2016-05-06 10:44:21 UTC
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.

Comment 18 Florian Weimer 2016-05-06 11:23:08 UTC
We need proper kernel interfaces to fix this.  There is no other way.  I filed bug 1333764 to cover the kernel side.

Comment 22 Florian Weimer 2020-04-16 12:56:17 UTC
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).

Comment 23 Carlos O'Donell 2022-11-08 18:59:24 UTC
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.

Comment 26 Florian Weimer 2022-11-11 14:27:42 UTC
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.


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