Why do STANDARD_RIGHTS_READ, STANDARD_RIGHTS_WRITE, and STANDARD_RIGHTS_EXECUTE have the same values?

Raymond Chen

Windows defines values for the access rights STANDARD_RIGHTS_READ, STANDARD_RIGHTS_WRITE, and STANDARD_RIGHTS_EXECUTE. But if you look at their definitions, they are all defined to be the same thing, namely, READ_CONTROL. How can this possibly make sense? Certainly read, write, and execute rights should be different, shouldn’t they?

They should, but that’s not what the STANDARD_RIGHTS_* values are for.

What these values are trying to say is “Every securable object should include STANDARD_RIGHTS_READ in their generic read access mask, STANDARD_RIGHTS_WRITE in their generic write access mask, and STANDARD_RIGHTS_EXECUTE in their generic execute access mask.” Confusingly, STANDARD_RIGHTS_ALL is just a mask of all the standard rights, not the “mask that must be present in the generic all access mask.” It is STANDARD_RIGHTS_REQUIRED that you have to put in your generic all access mask.

For example, the file access rights are defined as

#define FILE_GENERIC_READ         (STANDARD_RIGHTS_READ     |\
                                   FILE_READ_DATA           |\
                                   FILE_READ_ATTRIBUTES     |\
                                   FILE_READ_EA             |\
                                   SYNCHRONIZE)


#define FILE_GENERIC_WRITE        (STANDARD_RIGHTS_WRITE    |\
                                   FILE_WRITE_DATA          |\
                                   FILE_WRITE_ATTRIBUTES    |\
                                   FILE_WRITE_EA            |\
                                   FILE_APPEND_DATA         |\
                                   SYNCHRONIZE)


#define FILE_GENERIC_EXECUTE      (STANDARD_RIGHTS_EXECUTE  |\
                                   FILE_READ_ATTRIBUTES     |\
                                   FILE_EXECUTE             |\
                                   SYNCHRONIZE)

#define FILE_ALL_ACCESS           (STANDARD_RIGHTS_REQUIRED |\
                                   SYNCHRONIZE              |\
                                   0x1FF)

const GENERIC_MAPPING FileGenericMapping =
{
    FILE_GENERIC_READ,
    FILE_GENERIC_WRITE,
    FILE_GENERIC_EXECUTE,
    FILE_ALL_ACCESS,
};

The “generic read” file access includes STANDARD_RIGHTS_READ, plus any other read rights specific to files. Similarly for “write”, “execute”, and “all” access.

Now, it so happens that the only mandatory access right for read, write, and execute is READ_CONTROL, so that’s why all three of the macros expand to the same underlying value.

But you weren’t supposed to care about that. Just include the corresponding standard rights in each of the four levels of access, and you’re all set.

3 comments

Discussion is closed. Login to edit/delete existing comments.

  • Joshua Hudson 0

    For a fun time, set a deny ACL for SYNCHRONIZE for some file and watch things explode.

    It’s very easy to do this by accident when trying to deny write permission to a file with a deny rule. Some of the builtin windows tools (either presently do or used to) do it by using the macros to construct the deny ACL and FILE_GENERIC_WRITE contains SYNCHRONIZE.

    If you don’t have SYNCHRONIZE, some programs work opening the file for read and some don’t and there’s no rhyme or reason to which ones.

    • Ian Boyd 0

      It probably fails for applications that try to wait on the file handle.

  • Ian Boyd 0

    I was recently adding access controls to an application, and i was wrining my hands about whether or not users should be allowed to see the DACL.

    “If they see the DACL; then they could see the names of other users! Oh nose!”

    Then i calmed down; because if it’s good enough for Windows NT, and it’s C2 security rating, then it’s good enough for a LoB application.

Feedback usabilla icon