Bug 18683 - Linux faccessat implementation can incorrectly ignore AT_EACCESS
Summary: Linux faccessat implementation can incorrectly ignore AT_EACCESS
Status: RESOLVED FIXED
Alias: None
Product: glibc
Classification: Unclassified
Component: libc (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: 2.33
Assignee: Florian Weimer
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-07-15 21:11 UTC by Jed Davis
Modified: 2020-08-07 20:08 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jed Davis 2015-07-15 21:11:11 UTC
The Linux implementation of faccessat() assumes that if __libc_enable_secure is 0 then the process's real and effective capabilities are equal and it can ignore AT_EACCESS in order to use the faccessat syscall (instead of, as documented, imperfectly emulating it with fstatat).

But this isn't quite correct: if the process has CAP_SETUID and/or CAP_SETGID (e.g., if it's run as root) it can start with equal real/effective capabilities and change them later.

For example, in a program run as uid 0, assuming "/etc/passwd" is a regular file with mode 0644 owned by uid 0:

seteuid(-2) => 0
faccessat(AT_FDCWD, "/etc/passwd", W_OK, AT_EACCESS) => 0
open("/etc/passwd", O_WRONLY) => -1

I'm not sure how important this bug is (the usual warnings about time-of-check/time-of-use issues apply to most use cases for faccessat, and it's currently impossible to implement faccessat correctly on Linux), but I thought it should at least be on file for if/when someone else runs into it.
Comment 1 Florian Weimer 2015-10-13 12:17:01 UTC
Interesting.  I suspect faccessat should return ENOSYS if AT_EACCESS is specified.  glibc does not know what kind of security modules the kernel has loaded, so any emulation will be misleading.
Comment 2 Florian Weimer 2015-10-13 12:20:52 UTC
EINVAL would probably be the better error code.
Comment 3 Florian Weimer 2020-04-16 12:51:03 UTC
This is similar to the problem with fchmodat—the system call does not take a flags argument:

  if ((flag == 0 || ((flag & ~AT_EACCESS) == 0 && ! __libc_enable_secure)))
    return INLINE_SYSCALL (faccessat, 3, fd, file, mode);

A fix depends will need kernel support.
Comment 4 Florian Weimer 2020-08-07 16:21:16 UTC
Linux 5.8 has the required kernel support.
Comment 5 Florian Weimer 2020-08-07 20:08:17 UTC
Fixed in glibc 2.33.

I'm marking this as security- until there is demonstrated application impact.