According to this web site about SMB access masks,
I'd like to know what are common use cases for using these. Specifically, if I wanted to be maximally permissive, shouldn't I use GENERIC_ALL? And though it says in its description that it doesn't include MAXIMUM_ALLOWED, because it includes the previously mentioned access flags wouldn't GENERIC_ALL therefore also subsume MAXIMUM_ALLOWED? I have a feeling I'm missing some important concept.
Okay, so technically this is a Windows question, not sure why that tag is missing. The meaning of those two values differs, but you also should be aware of another related concept: GENERIC_MAPPING.
The generic mapping allows to turn generic access masks (for read, write and all) into object-type-specific access masks. As the name implies, these are specific to the object type, i.e. file, pipe, memory mapped file ("Section"), mutex ("Mutant") etc. You can use WinObj from Sysinternals to get a sense of the object types available in Windows. However, you just need to be aware that this exists and there is no need to know all the details. This becomes relevant only when you are using the ACL editor and need to do that mapping yourself, for example. Every object type basically comes with a mapping like that.
After obtaining a handle to an object, you can query OBJECT_BASIC_INFORMATION
(public prototype here; alternative) to see what level of object-type-specific access was granted.
Obtaining the handle is the part where the access check happens. This is why passing around or inheriting handles can be an issue, sometimes. So if the SD/DACL says you don't have the requested level of access or outright denies it, you won't even get a handle and instead will get to see STATUS_ACCESS_DENIED
.
Now that we have this out of the way let's discuss the difference between GENERIC_ALL
and MAXIMUM_ALLOWED
.
If you paid attention above, you may already see why they are semantically different.
MAXIMUM_ALLOWED
-- as the name suggests -- requests whatever access you have available and OBJECT_BASIC_INFORMATION::GrantedAccess
can tell you what that is. If you don't get a handle and instead see STATUS_ACCESS_DENIED
, you simply don't have any sort of access to said object.
GENERIC_ALL
instead requests the actual maximum access based on the generic mapping. And if you don't have all of those access types requested it will fail all the same.
So in conclusion:
MAXIMUM_ALLOWED
will try to get you a handle with any access level that you can get (it could be the actual maximum, too)GENERIC_ALL
will try to get you a handle specifically with all access types subsumed under this generic mapping ... if any of those "all" fails, you won't get a handle and instead see STATUS_ACCESS_DENIED
.If you wanted to be maximally permissive (the way I understand it) you'd want to use: MAXIMUM_ALLOWED
, because the (still generic but more specific) GENERIC_ALL
will fail to even get you a handle in many cases where MAXIMUM_ALLOWED
yields one.
In the best case -- given your intent -- you will be granted an access that amounts to GENERIC_ALL
without running the risk that you don't get a handle at all.
Alternatively you could request access to the object at your desired access level (e.g. GENERIC_ALL
) and have a staggered fallback to GENERIC_WRITE
etc. and as last ditch effort try MAXIMUM_ALLOWED
. The advantage is that with the concrete desired access masks -- if and when you get a handle back! -- you know the access level. With MAXIMUM_ALLOWED
you would still have to query OBJECT_BASIC_INFORMATION::GrantedAccess
in order to find that out (well, or attempt an action and get STATUS_ACCESS_DENIED
or ERROR_ACCESS_DENIED
).