ObReferenceObjectByHandle documentation tell to not use any generic access rights.
You should use not GENERIC_READ, but FILE_GENERIC_READ instead.
How very odd. It DOES say exactly what Mr. Grig indicated:
Do not use any generic access rights.
I wonder why you wouldn’t want to do this?
Peter
OSR
@OSRDrivers
Here is a clarification:
GENERIC_READ/GENERIC_WRITE *are* generic access rights. These are single-bit masks that are translated to object-specific mask. These should NOT be used in ObReferenceObjectByHandle, because the function will validate the mask against actual access mask in the handle before even it resolves the handle to the object. Translation of generic access right to object-specific mask requires the object type which is not known at the time.
FILE_GENERIC_READ/FILE_GENERIC_WRITE are FILE_OBJECT-specific access rights that correspond to GENERIC_READ/GENERIC_WRITE. These are actual access masks of the file handle that was opened with GENERIC_READ/GENERIC_WRITE.
Thank you, Mr. Grig, for the clarification.
Isn’t the object type know, given that it is supplied in the call to ObReferenceObjectByHandle?
It is *very* dangerous indeed to reference an object, *especially* one coming from user-mode, without specifying the object type.
I’m not arguing, I’m hoping to understand the prescription…
Peter
OSR
@OSRDrivers
>object type which is not known at the time.
I think it’s more likely not that because the object type is not known, but because ObReferenceObjectByHandle is more often used in the fast path upon getting a ReadFile/WriteFile/DeviceIoControl syscall, where the generic access masks are not supplied, so it just doesn’t bother checking for them and converting to the object-specific mask.
Yes Alex, the 0 ACCESS_MASK can be dangerous. For file objects, the 0 ACCESS_MASK translates to FILE_ANY_ACCESS (unrestricted access) and this ACCESS_MASK should be used carefully, especially if the file object represents a device (which is not the case here):
http://msdn.microsoft.com/en-us/library/windows/hardware/dn614603(v=vs.85).aspx
Using user mode handles in kernel mode is discussed in the following article:
http://msdn.microsoft.com/en-us/library/windows/hardware/dn614606(v=vs.85).aspx
Sorry, I still don’t understand why MSDN says not to use GENERIC_READ when calling ObReferenceObjectByHandle.
No disrespect, Mr. Grig… But I think MSDN is likely to be mistaken here.
Here’s what I know: In general you *always* want to use the generic access types when requesting access to an object. This keeps you from making dumb mistakes, and leaving out some weird but required specific access type (like Synchronize for file objects).
For a file object, GENERIC_READ maps to FILE_GENERIC_READ which maps to “all the right stuff to do a read” for whatever object (STANDARD_RIGHTS_READ or something… This shit always confuses me… It confuses most people, I bet… Which is why it’s best practice to use the generics!).
I’ll try to check tomorrow to see if there’s anything special about ObReferenceObjectByHandle that makes using GENERIC_READ a bad idea… But I’ve got to tell you: In the absence of other info, I would rather trust Mr. Lebedinski more than a random sentence in MSDN.
More tomorrow,
Peter
OSR
@OSRDrivers
The issue here is that GENERIC_READ is not a real access mask, but a generic alias. Translation of the generic right to the obj-type-specific is not done by all functions. ObOpenObjectByPointer has to do that, because it creates a handle. When a handle is created, it has to put the actual access mask to the table, not a generic one. ObReferenceObjectByHandle may not do that, and because of that it doesn’t take GENERIC_READ.
>For a file object, GENERIC_READ maps to FILE_GENERIC_READ which maps to “all the right stuff to do a read” for whatever object (STANDARD_RIGHTS_READ or something… This shit always confuses me… It confuses most people, I bet… Which is why it’s best practice to use the generics!).
That’s why Visual Studio is a good IDE and not just a toy for kids: just right-click on a symbol and choose “Go to declaration” or “Go to definition” and you should get the exact meaning of the symbol. It’s a good way to avoid confusion.
Generics make the highest nibble of the ACCESS_MASK while type specific rights make the lower 16 bits.That’s why GENERIC_READ cannot map to FILE_GENERIC_READ. Using generics is probably the easy way to go or probably the way to go if the object type is not known but it should not be considered a best practice.
The best practice, regarding the invocation of ObReferenceObjectByHandle, is to precise the object type and use the exact same type specific access rights that would be passed to the equivalent ZwXxx routine that opens/creates a handle of that same type (ZwOpenFile/ZwCreateFile,ZwOpenEvent/ZwCreateEvent…). But ZwQueryObject is documented and the object type can be obtained, so using generics is just not necessary.
<wdm.h wdk=“8.1”>
typedef ULONG ACCESS_MASK;
typedef ACCESS_MASK *PACCESS_MASK;
//
// The following are masks for the predefined standard access types
//
#define DELETE (0x00010000L)
#define READ_CONTROL (0x00020000L)
#define WRITE_DAC (0x00040000L)
#define WRITE_OWNER (0x00080000L)
#define SYNCHRONIZE (0x00100000L)
#define STANDARD_RIGHTS_REQUIRED (0x000F0000L)
#define STANDARD_RIGHTS_READ (READ_CONTROL)
#define STANDARD_RIGHTS_WRITE (READ_CONTROL)
#define STANDARD_RIGHTS_EXECUTE (READ_CONTROL)
#define STANDARD_RIGHTS_ALL (0x001F0000L)
#define SPECIFIC_RIGHTS_ALL (0x0000FFFFL)
.
.
.
//
// These are the generic rights.
//
#define GENERIC_READ (0x80000000L)
#define GENERIC_WRITE (0x40000000L)
#define GENERIC_EXECUTE (0x20000000L)
#define GENERIC_ALL (0x10000000L)
.
.
.
//
// Define access rights to files and directories
//
//
// The FILE_READ_DATA and FILE_WRITE_DATA constants are also defined in
// devioctl.h as FILE_READ_ACCESS and FILE_WRITE_ACCESS. The values for these
// constants MUST always be in sync.
// The values are redefined in devioctl.h because they must be available to
// both DOS and NT.
//
#define FILE_READ_DATA ( 0x0001 ) // file & pipe
#define FILE_LIST_DIRECTORY ( 0x0001 ) // directory
#define FILE_WRITE_DATA ( 0x0002 ) // file & pipe
#define FILE_ADD_FILE ( 0x0002 ) // directory
#define FILE_APPEND_DATA ( 0x0004 ) // file
#define FILE_ADD_SUBDIRECTORY ( 0x0004 ) // directory
#define FILE_CREATE_PIPE_INSTANCE ( 0x0004 ) // named pipe
#define FILE_READ_EA ( 0x0008 ) // file & directory
#define FILE_WRITE_EA ( 0x0010 ) // file & directory
#define FILE_EXECUTE ( 0x0020 ) // file
#define FILE_TRAVERSE ( 0x0020 ) // directory
#define FILE_DELETE_CHILD ( 0x0040 ) // directory
#define FILE_READ_ATTRIBUTES ( 0x0080 ) // all
#define FILE_WRITE_ATTRIBUTES ( 0x0100 ) // all
#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ |<br> FILE_READ_DATA |<br> FILE_READ_ATTRIBUTES |<br> FILE_READ_EA |<br> SYNCHRONIZE)
#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE |<br> FILE_WRITE_DATA |<br> FILE_WRITE_ATTRIBUTES |<br> FILE_WRITE_EA |<br> FILE_APPEND_DATA |<br> SYNCHRONIZE)
#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE |<br> FILE_READ_ATTRIBUTES |<br> FILE_EXECUTE |<br> SYNCHRONIZE)
</wdm.h>
I would personally trust MSDN in that case
I don’t actually know what ObReferenceObjectByHandle will do if you feed it a generic mask. But you probably don’t want to do that anyway… Calling CreateFile with something like GENERIC_READ is OK - you’re basically saying “I need to do some read-only stuff, but I’m too lazy to figure out what exact rights I need”. Not perfect, but acceptable most of the time.
On the other hand, with ObReferenceObjectByHandle you are validating that the handle passed to you has sufficient rights to perform some operation on the object. So you should pick whatever specific right(s) best describe that operation, and check for that. For example, if the operation involves modifying data in the file, then you should check for FILE_WRITE_DATA. There’s no reason to require the handle to contain FILE_WRITE_ATTRIBUTES and all the other “generic write” rights.
-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@osr.com
Sent: Wednesday, October 22, 2014 7:45 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Accessing usermode file handle from kernel
Sorry, I still don’t understand why MSDN says not to use GENERIC_READ when calling ObReferenceObjectByHandle.
No disrespect, Mr. Grig… But I think MSDN is likely to be mistaken here.
Here’s what I know: In general you *always* want to use the generic access types when requesting access to an object. This keeps you from making dumb mistakes, and leaving out some weird but required specific access type (like Synchronize for file objects).
For a file object, GENERIC_READ maps to FILE_GENERIC_READ which maps to “all the right stuff to do a read” for whatever object (STANDARD_RIGHTS_READ or something… This shit always confuses me… It confuses most people, I bet… Which is why it’s best practice to use the generics!).
I’ll try to check tomorrow to see if there’s anything special about ObReferenceObjectByHandle that makes using GENERIC_READ a bad idea… But I’ve got to tell you: In the absence of other info, I would rather trust Mr. Lebedinski more than a random sentence in MSDN.
> Here’s what I know: In general you *always* want to use the generic access types when requesting
access to an object.
I disagree.
One should request as little access as it is actually necessary. Probably FILE_READ_DATA is enough.
And yes, at this moment you know the object type you want here, and it is validated in the same call.
GENERIC_READ is good in ACEs, but not when requesting access.
–
Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com
Mr. Shatskih, Mr. Grig, and Mr. Lebedinsky… thanks very much.
This has been – for me at least – a very interesting discussion.
Thanks to NTDEV GEEK, as well, for documenting for the archives where the GENERIC_xxx bits live. Now people who come along and find this discussion after us, who may know about the where the generic bits reside, will be informed.
Mr. Shatskih, Mr. Grig, and Mr. Lebedinsky articulated, quite rightly, the basics of implementing the principal of least privilege. That is in this particular case, “only ask for the access you need, very specifically.” This is very true, and very obviously best practice. Your point here is well taken.
My fears, based on several annoying experiences, are that it’s not intuitively obvious precisely what specific access you need for what you want to do. I cited SYNCHRONIZE earlier… without this access, you may be able to read or write a file, but you won’t be able to wait on the file handle you’re using to do that read/write. Arrrgh. Gets me every time.
Our discussion caused me to look more deeply into which functions actually interpret the GENERIC_xxx bits, and which ones ignore them. I found the results quite surprising: In short, the GENERIC_xxx mappings are in fact not frequently interpreted. You almost ALWAYS have to use the *actual* bits.
Functions such as ZwCreateFile will map GENERIC_xxxx access bits (as noted by Mr. Lebedinsky), as will anything that creates an access state structure (such as ObOpenObjectByPointer mentioned by Mr. Grig) and various security descriptor functions (as mentioned by Mr. Shatskih).
But it seems, at least from a casual check, that MOST functions in fact DO NOT map the generic bits to specific bits. For example, my reading of ObReferenceObjectByHandle seems to indicate that this function in fact DOES NOT map any GENERIC_xxxx bits the provided AccessMask to (non-generic) effective bits. So unless my reading is wrong, using GENERIC_xxxx for this purpose will in fact not work.
Bottom line: Using the GENERIC_xxxx mappings is usually only a good idea when doing something like CreateFile, or building an ACE in an SD… just as Mr. Shatskih, Mr. Lebedinsky, and Mr. Grig said.
When referencing a handle, you should use the specific types relevant to the object you?re attempting to access. At the very least, this makes the access check clear in the code, and as a bonus results in using the ?least privilege? necessary to access the object.
Thanks again, guys? I learned something interesting today.
Peter
OSR
@OSRDrivers
> My fears, based on several annoying experiences, are that it’s not intuitively obvious precisely what
specific access you need for what you want to do.
At least in this case the code will fail ASAP and you will add the missing flag ASAP.
No further surprises (like on customer sites )
won’t be able to wait on the file handle you’re using to do that read/write.
Rather useless a feature.
If the file is not overlapped, then any IOs will wait internally in the kernel without checking for this flag.
If the file is overlapped, then this wait is useless - wait for individual IOs instead.
Functions such as ZwCreateFile will map GENERIC_xxxx access bits (as noted by Mr.
Lebedinsky),
Note that this function creates a file handle and initializes the handle’s GrantedAccess mask.
It is not ObRefXxx which checks the bits in GrantedAccess.
(such as ObOpenObjectByPointer mentioned by Mr. Grig)
Again it creates a handle.
and various security descriptor functions (as mentioned by Mr. Shatskih).
Yes, for ACLs, you can just use only generic masks unless you want to really do something fine grained.
like CreateFile, or building an ACE in an SD… just as Mr. Shatskih, Mr. Lebedinsky, and Mr. Grig
Note that the standard Windows ACL editor UI separately shows GENERIC_xxx and the bit flags.
IIRC GENERIC_xxx are the enum values and not the ORable bit flags masks.
–
Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com
>IIRC GENERIC_xxx are the enum values and not the ORable bit flags masks.
You can combine GENERIC_READ and GENERIC_WRITE.
I am making an assertion here, can anybody tell me if it’s correct or wrong ?
“A file object is always a named object and the name of a file object is it’s full NT path name.”
Assuming the previous assertion is correct and because we are also talking about best practices, I would propose the following rule: an application should not pass a user-mode handle unless the underlying object is unamed. And consequently, an application should not pass a user-mode file handle but it’s full NT path name instead.
File Objects are actually unnamed objects from the perspective of the Object
Manager, it’s the File System that owns the name that we think of as being
associated with the File Object.
Weeelll it’s not that easy to assert this as being a rule, just passing a
name is not a panacea for the woes of validating input from user mode.
One complication is that if you pass the name, the driver is now responsible
for enforcing proper access checking on opening the object. For example, if
you pass my driver a HANDLE and tell me to write to it I can make sure you
have write access as part of my call to ObReferenceObjectByHandle. If you
pass me a file name, I need to make sure that you have write access to the
underlying file when I open it (e.g. by adding the IO_FORCE_ACCESS_CHECK
option to my open). Not onerous or overly difficult, but whether you specify
a HANDLE or a name there is the chance of getting it wrong.
-scott
OSR
@OSRDrivers
wrote in message news:xxxxx@ntdev…
I am making an assertion here, can anybody tell me if it’s correct or wrong
?
“A file object is always a named object and the name of a file object is
it’s full NT path name.”
Assuming the previous assertion is correct and because we are also talking
about best practices, I would propose the following rule: an application
should not pass a user-mode handle unless the underlying object is unamed.
And consequently, an application should not pass a user-mode file handle but
it’s full NT path name instead.
>If you?pass me a file name, I need to make sure that you have write access to the?underlying file when I open it (e.g. by adding the IO_FORCE_ACCESS_CHECK?option to my open). Not onerous or overly difficult, but whether you specify?a HANDLE or a name there is the chance of getting it wrong.
In fact context evaluation is not that easy. The problem is about a driver who is an intermediate driver. Even though the driver starts its operations as a highest level driver, another driver could attach to the (top of the) stack at anytime and, depending on how the upper driver handles IRPs, the lower driver has no guaranty that’s it is actually running in the context of the issuer. So using the I/O system to mimic “system services” is not that easy and applications should, I think, use the WIN32 API to perform file operations.
Thank you for pointing out the role of filesystem drivers. I’ve learned that the ‘GENERIC_XXX paradigm mimics the UNIX “rwx” access bits’, though it may look obvious. The mapping from generic rights to standard and specific rights is done by the I/O manager (IoGetFileObjectGenericMapping).
Access Mask
http://msdn.microsoft.com/en-us/library/windows/hardware/ff538834(v=vs.85).aspx
If a driver is written under the guarantee that it is called in the context
of the requesting application, a filter driver must not break this by
sending requests along from an arbitrary context. You could break all kinds
of things in spectacular fashion by injecting a filter that does this.
If you have a branch where the highest driver may pass the request along to
lower drivers for further processing, all process context dependent
validation must be performed by the highest driver.
The mapping of generic rights to specific rights is maintained on a
per-Object Type basis and is set up by the creator of the Object Type. This
allows the Object Manager to, for lack of a better word, “generically” map
generic rights to specific rights by simply referring to the Object Type.
You are correct though that the I/O Manager is the one that dictates what
the mapping is for File Objects as the I/O Manager is the one that creates
the File Object Type.
You can see this in the debugger by looking at the Type Objects’
GenericMapping field. For example, here is the Event Object Type mapping:
14: kd> !object \ObjectTypes\Event
Object: ffffe00115b36f20 Type: (ffffe00115a244d0) Type
…
14: kd> dt nt!_object_type ffffe00115b36f20 TypeInfo.GenericMapping.
+0x040 TypeInfo :
+0x00c GenericMapping :
+0x000 GenericRead : 0x20001
+0x004 GenericWrite : 0x20002
+0x008 GenericExecute : 0x120000
+0x00c GenericAll : 0x1f0003
Versus the File Object Type mapping:
14: kd> !object \ObjectTypes\File
Object: ffffe00115b5d080 Type: (ffffe00115a244d0) Type
…
14: kd> dt nt!_object_type ffffe00115b5d080 TypeInfo.GenericMapping.
+0x040 TypeInfo :
+0x00c GenericMapping :
+0x000 GenericRead : 0x120089
+0x004 GenericWrite : 0x120116
+0x008 GenericExecute : 0x1200a0
+0x00c GenericAll : 0x1f01ff
-scott
OSR
@OSRDrivers
wrote in message news:xxxxx@ntdev…
If you pass me a file name, I need to make sure that you have write access
to the underlying file when I open it (e.g. by adding the
IO_FORCE_ACCESS_CHECK option to my open). Not onerous or overly difficult,
but whether you specify a HANDLE or a name there is the chance of getting
it wrong.
In fact context evaluation is not that easy. The problem is about a driver
who is an intermediate driver. Even though the driver starts its operations
as a highest level driver, another driver could attach to the (top of the)
stack at anytime and, depending on how the upper driver handles IRPs, the
lower driver has no guaranty that’s it is actually running in the context of
the issuer. So using the I/O system to mimic “system services” is not that
easy and applications should, I think, use the WIN32 API to perform file
operations.
Thank you for pointing out the role of filesystem drivers. I’ve learned that
the ‘GENERIC_XXX paradigm mimics the UNIX “rwx” access bits’, though it may
look obvious. The mapping from generic rights to standard and specific
rights is done by the I/O manager (IoGetFileObjectGenericMapping).
Access Mask
http://msdn.microsoft.com/en-us/library/windows/hardware/ff538834(v=vs.85).aspx
Thank you for that detail, that’s what I needed to know.
Regarding generic rights mapping, the term mapping is clear to me now. This a set theory word. The legacy Unix set of permissions is a subset of the windows set of permissions. I have many math books about set theory but I missed that. The RBAC ACM is defined using set theory. Thank you very much.