2

I've been doing some shell writing and came across a recommendation for access() to check whether a file exists, can be read, etc. Seems super easy to implement and faster than stat(). When I started looking at the man page for it, I noticed that it is NOT recommended for use because it can lead to a security hole. The man page says this:

Using access() to check if a user is authorized to e.g. open a file before actually doing so using open(2) creates a security hole, because the user might exploit the short time interval between checking and opening the file to manipulate it.

Does anyone know how this can be exploited or if it pertains only to using open() after checking a file? I know that a lot of people say to use stat() instead, but access() is so easy to implement, especially for the shell that I used it for.

halfer
  • 19,824
  • 17
  • 99
  • 186
Trevor Arjeski
  • 2,108
  • 1
  • 24
  • 40
  • 2
    Needless to say, stat() suffers from the same kind of potential security hole described in the quote above for access(). – janneb Oct 28 '11 at 05:56

3 Answers3

5

That is a TOCTOU race (Time of Check to Time of Update). A malicious user could substitute a file he has access to for a symlink to something he doesn't have access to between the access() and the open() calls. Use faccessat() or fstat(). In general, open a file once, and use f*() functions on it (e.g: fchown(), ...).

ninjalj
  • 42,493
  • 9
  • 106
  • 148
  • faccessat() is brilliant. Thanks for pointing this out to me. – Trevor Arjeski Oct 28 '11 at 14:24
  • faccessat's fd argument is for a directory that you have open, not for the file itself. faccessat(2) doesn't help you avoid the exact TOCTOU attack you just described, esp. if you use AT_FDCWD to make it behave exactly like access(2). If you use a dir fd, then an attacker with write permission on that directory can still attack just fine. (And you'd have to use openat, not open, with a relative path, to avoid an attacker shuffling symlinks earlier in the path) – Peter Cordes Dec 03 '14 at 14:43
  • Substituting a file he has access to for a symlink to something he doesn't have access to won't help malicious user in any way since access permissions will be checked during `open` call anyway. And the act of substitution probably requires access permissions on its own. – user7860670 Aug 31 '21 at 12:49
0

One thing I can think of, although it seems weak - access() uses the real rather than effective uid and gid. This supposedly allows a setuid program (one which a regular user executes but which gains permissions of the owner) to check whether the invoking user can read the file, to prevent inadvertently giving that user access to a file that they should be unable to read, perhaps by using some symbolic link or hard link trickery. I can't find any evidence that this is possible, or that this isn't possible with stat(), but imagine this scenario:

user executes program
program is setuid, immediately gets all privs of root
program checks file1 to ensure that user has access
file1 is a hardlink to file2, which user has access to
user changes file1 to hardlink to file3 (/etc/shadow or something like that)
program reads file1 and does something to it (print, convert, whatever)
user now has access to a file they shouldn't
Dan
  • 10,531
  • 2
  • 36
  • 55
  • 1
    This is probably the exact scenario the quoted portion of the man page is envisioning. `stat` is vulnerable to the exact same thing, that the underlying file changes between the check and the actual access. – John Flatness Oct 28 '11 at 04:50
  • 1
    Looks like it would have to be a hardlink, as softlinks have their own permissions. Can you hardlink to a file you don't have access to? – Dan Oct 28 '11 at 04:51
  • Symlinks don't have their own permissions... well, fine, they do, but `access` checks the target's permissions, not the symlink's. – John Flatness Oct 28 '11 at 04:53
  • 1
    @JohnFlatness: Whenever you see `stat` recommended instead of `access` think `fstat`. Open the file **then** `fstat` the returned handle. – Zan Lynx Oct 28 '11 at 08:48
  • For user to be able to alter file1 it must already have access to it. – user7860670 Aug 31 '21 at 12:51
0

The pattern seems to be calling access() or stat() to determine whether you can open a file, and then opening it if you have permission.

Instead, it's usually better just to go ahead and try to open it, and then check whether the attempt succeeded (and if not, why). This avoids the time interval between the check and the attempt to open the file.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • Actually, it will succeed. `access()` is for allowing a server running as a provileged user to impersonate a non-privileged user (e.g.: a NFS server, ...) without having to fork a new process for each user. – ninjalj Oct 28 '11 at 18:34