9

I have successfully used both stat() & access() separately to determine if a user has either read or read/write access to a directory.

My question is: - Is there a preferred method ? I see a lot of examples using stat, but for my purpose, access seems to be more lightweight and serves purpose.
- Are there any issues (e.g. - security) w/ one or the other ? - Any issues w/ my approach ?

Here is some pseudo code (re-creating from memory w/o compiling) :

       // Using access():
    bool readAccessPermission = false; 
    bool writeAccessPermission = false;

    if (mode == 'r'){
            if (access(directory, R_OK) == 0)
                    readAccessPermission = true;                        
    }
    else{
            if (access(directory, R_OK && W_OK) == 0)
                    readAccessPermission = true;
                    writeAccessPermission = true;
    }


    // vs. using stat function
    // assume I already called stat(directory) and have the object


    bool readAccessPermission = false; 
    bool writeAccessPermission = false;

    var retmode = ((stats.mode) & (0777));

    if (modeString == 'r'){ 
        if ((retmode) & (consts.S_IRUSR)){
            readAccessPermission = false; 
        }    
    } 
    else{ 
        if ((retmode) & (consts.S_IRUSR)){
            readAccessPermission = true; 

            if ((retmode) & consts.S_IWUSR)){               
                writeAccessPermission = true; 
            }
        }
    }
BartoszKP
  • 34,786
  • 15
  • 102
  • 130
Bamerza
  • 1,335
  • 1
  • 18
  • 34
  • 1
    Your code is slightly buggy... `access(directory, R_OK && W_OK)` should be `access(directory, R_OK|W_OK)`. Also there is no reason to compute `(stats.mode) & (0777)`; you can just use `stats.mode` directly. That said, there is no functional difference between these approaches. – Nemo Aug 17 '11 at 05:05
  • Another issue if you want portable code: under POSIX you need to verify if the current user has those rights specifically; your tests only check the permission on the target and not if it applies to the current user. – slashmais May 04 '18 at 05:58

3 Answers3

11

Either is equivalent for your needs. access() is a cleaner wrapper if you're not going to do anything with the stat structure that you populate.

Just be mindful that you are creating a race when doing this. The permissions can change between calling stat()/access() and when you actually try and use the directory. Hell, the directory could even be deleted and recreated in that time.

It's better to just try and open what you need and check for EPERM. Checking stat() or access() will not guarantee that a subsequent operation won't return EPERM.

Chinna
  • 3,930
  • 4
  • 25
  • 55
Adam Hawes
  • 5,439
  • 1
  • 23
  • 30
  • From the manpage: "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." – Adam Hawes Aug 17 '11 at 05:05
  • Or if you need the file's stat info, then `open` the file and use `fstat` on it. At least then you know that the stat info is for the same file you are holding a handle to. – Zan Lynx Aug 17 '11 at 05:08
  • @Zan: Except the point is to check for read and/or write permission... If the `open` succeeds, obviously you already have at least one of those :-). (Edit: OK, so I suppose you could open a directory with `O_SEARCH`. But then what are going to do with the results of the permissions check? Makes more sense to just take Adam's suggestion and simply attempt the desired operation itself.) – Nemo Aug 17 '11 at 05:13
  • @Nemo: Good point. I was thinking of other things like size or ownership, but for read/write access it doesn't matter. – Zan Lynx Aug 17 '11 at 08:42
5

In the simple case, both are functionally equivalent for our matter. Also, access() would not be much faster or so as the same data structure (the inode) much be fetched.

However, if access control lists (ACL) are used on the system, access will process these while you have no way to check ACLs with the stat data.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
dmeister
  • 34,704
  • 19
  • 73
  • 95
2

These two code snippets are not identical and will produce materially different results.

Firstly, your stat call only checks the owner's permission bits. If the user running this code does not own the file in question, this is not the right set of bits to check.

Secondly, even if we assume the calling user is the owner, we also need to figure out what we mean by "calling user." As far as open(2) and its friends are concerned, the calling user is the number returned by geteuid(2), that is, the effective user ID. This is what open uses to determine whether to allow opening a file. As far as access(2) is concerned, the calling user is the number returned by getuid(2), that is, the real user ID. This is what access uses to determine whether to report success. The real and effective UIDs can differ, particularly when running a binary with the setuid bit enabled or when the programmer is doing something particularly clever (and probably ill-advised IMHO).

(Meanwhile, stat() doesn't care about the calling user one way or the other, except insofar as it needs execute permission on the containing directory to do its job. For that purpose it checks the EUID).

Finally, as has been pointed out in multiple answers, both code snippets may be vulnerable to TOCTTOU bugs, depending on what you do with the results. All in all, the safest way to determine whether an action is permitted is to attempt that action and check whether it failed.

Kevin
  • 28,963
  • 9
  • 62
  • 81