Anybody can make up a generic mapping


Each component that uses ACLs to control access has its own idea of what GENERIC_READ, GENERIC_WRITE, and GENERIC_EXECUTE mean. It's not like there's a master list that somebody can make that lists them all, because I can make up a new one right here. Watch me:

#define GIZMO_QUERY_STATUS   0x0001
#define GIZMO_QUERY_MEMBERS  0x0002
#define GIZMO_START          0x0004
#define GIZMO_STOP           0x0008
#define GIZMO_ADD_CLIENT     0x0010
#define GIZMO_REMOVE_CLIENT  0x0020

#define GIZMO_GENERIC_READ  (STANDARD_RIGHTS_READ | \
                             GIZMO_QUERY_STATUS | \
                             GIZMO_QUERY_MEMBERS)
#define GIZMO_GENERIC_READ  (STANDARD_RIGHTS_READ | GIZMO_QUERY_STATUS)
#define GIZMO_GENERIC_WRITE (STANDARD_RIGHTS_WRITE | \
                             GIZMO_ADD_CLIENT | \
                             GIZMO_REMOVE_CLIENT)
#define GIZMO_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE | \
                             GIZMO_START | \
                             GIZMO_STOP)
#define GIZMO_ALL_ACCESS    (STANDARD_RIGHTS_REQUIRED | \
                             GIZMO_QUERY_STATUS | \
                             GIZMO_QUERY_MEMBERS | \
                             GIZMO_START | \
                             GIZMO_STOP | \
                             GIZMO_ADD_CLIENT   | \
                             GIZMO_REMOVE_CLIENT)

GENERIC_MAPPING GizmoGenericMapping = {
    GIZMO_GENERIC_READ,
    GIZMO_GENERIC_WRITE,
    GIZMO_GENERIC_EXECUTE,
    GIZMO_ALL_ACCESS,
};

It's not just kernel objects that use ACLs. Anybody who wants to set up permissions can use ACLs to control access. For example, the file server service uses ACLs to control which users can create new file shares, which users can view printer properties, which users can connect to administrative shares, all that stuff. There is no kernel object that these access masks apply to; they merely control who can do what with the service.

In that example above, a "gizmo" might be some sort of chat room with a member list. Some users may have permission to add and remove other members from the chat room; others have permission to open the chat room or shut it down. When a client wants to perform an operation on the chat room, the program obtains the security descriptor for the chat room and calls AccessCheck to see whether the caller has permission to perform the operation.

This is a totally artificial example. My point is that anybody can make up access bits and use them to control access to some sort of shared resource. That shared resource might be something you think of as a "real object" like a file or a process, but it could be some sort of purely virtual construction like a chat room or a file share. Even if some sort of "complete list" were developed, anybody working in a basement can add a new one, and then your complete list is incomplete.

Bonus chatter: One of my colleagues points out that the mandatory integrity mechanism does have implications for generic mappings. I don't even understand that sentence, but there it is for you to ponder.

[Raymond is currently away; this message was pre-recorded.]

Comments (17)
  1. car insurance » Anybody can make up a generic mapping says:

    PingBack from http://hoursfunnywallpaper.cn/?p=8287

  2. John says:

    This is like Alfred Hitchcock’s "The Birds", only instead of birds it’s pingback spam.

  3. Will says:

    @John:

    I prefer to think of it as being like William F. Claxton’s "Night of the Lepus", only instead of giant mutant rabbits it’s pingback spam.

  4. Medinoc says:

    So actually, anything can have a security descriptor, and that’s why the AccessCheck() uses it rather than a kernel handle to the object ?

  5. Alexandre Grigoriev says:

    Medinoc,

    The point is that if you have your own set of permissions in your application (for example, your app is a source/versioning control system), you can use Win32 security API for access check. In this case you keep the ACLs in your application files. When you give your own meanings to the permission bits, you can also create your own set of generic mappings.

    For example, in a versioning control system, generic read permissions will include get the source, enumerate files, get labels, read security lists, etc.

  6. Mark says:

    One of my colleagues points out that the mandatory integrity mechanism does have implications for generic mappings.

    I think their suggestion is that integrity levels provide a snapshot of generic mappings.  If a user has a low trust token, it’s likely that’s GENERIC_READ, while high trust will usually be GENERIC_ALL_ACCESS.  But again, each component implements it as it wants.

  7. Mark says:

    … and although it would be nice to have a mechanism to expose what the generic mappings are, I’m afraid there’s no point forcing things to have such a mapping.

  8. Pavel Lebedinsky says:

    mandatory integrity mechanism does have implications for generic mappings

    One example of this is the fact that PROCESS_QUERY_LIMITED_INFORMATION is part of PROCESS_EXECUTE_ACCESS rather than PROCESS_READ_ACCESS (if it was part of read access, MIC wouldn’t allow a lower integrity process to query information about a higher integrity process).

  9. KJK::Hyperion says:

    I can answer the cryptic part about generic accesses and mandatory integrity – not to brag, but my BS thesis was the first ever implementation of a Bell-LaPadula policy for Windows (ReactOS actually, because my school didn’t have a WRK license, but close enough).

    The Biba integrity policy (the one implemented by Vista) is based on the concept of an integrity level. Every object (in Windows, a security descriptor) and subject (token object) is marked ("labeled" is the technical term) with a specific integrity level. The more critical a process, file, registry key, etc. the higher the integrity level. Conversely, the more untrusted a program, the lower the integrity level it will need to be run at. Levels as assigned to objects and subjects are immutable, for really complicated reasons (the short of it: Windows doesn’t support revoking access to objects). Obviously, you don’t want low-integrity processes to be able to alter high-integrity code and data; in MAC-speak, this is known as "no write up", and it’s one of the two security properties that make up the Biba policy.

    To detect whether an access control operation (CreateFile, OpenProcess, RegCreateKey, etc.) is going "up", you just compare the subject’s level with the object’s: if the object’s is larger, the direction is "up". To detect whether the operation is a "write", you have to take the intersection between the requested access and the GenericWrite mask for the object type: if the intersection isn’t empty, the operation is "write". An operation that is both "write" and "up" fails before the ACL is even checked.

    For an object type to properly support mandatory policy, it’s imperative that either its GenericWrite includes DELETE | WRITE_DAC | WRITE_OWNER (I believe the system handles ACCESS_SYSTEM_SECURITY automatically), or that the object type doesn’t support the delete/set owner/write DAC operations. Failure to do this results in a security hole (a write operation that isn’t detected as a "write").

    The full Biba policy has a second property, "no read down", that prevents high-integrity processes from writing low-integrity data into themselves by themselves, but Windows doesn’t implement it (I can see why, it’d be an usability and compatibility nightmare). On the other hand, and if I remember correctly, Windows implements "no execute up" and "no read up" too. This is where things get interesting. Detecting writes alone is easy; detecting reads as well is complicated by dubious design choices made a long time ago. The very short of it (this was of the longest parts of my thesis): the Windows kernel object model is leaky, unorthogonal and can’t support mandatory security policies completely. The theory of the object model is that data managed by the kernel on behalf of user-mode is split up into discrete subsets, that is objects, and each object maps to a security model object, that is a security descriptor.

    The reality is very different. A lot of data isn’t even in an object, for example (the process list, to name one). Some non-trivial object types result in several data subsets with different semantics sharing the same object, and by extension the same security descriptor (which therefore has to contain a mishmash of access bits, some covering one subset, some covering another, some covering all of them…). For example, file mapping objects, where the same security descriptor covers the object itself and the data it points to – by looking at the GENERIC_MAPPING alone, a security policy would think file mapping objects supported write-only access (they don’t, mapped memory is always at least readable. Well they do with COW, but this isn’t expressed in any way the security subsystem can understand). Or file objects, actually made up of two objects (the file object itself and the FCB it points to) sharing a single security descriptor, where SYNCHRONIZE access covers the file object itself (a volatile, in-memory structure) and the rest of accesses cover the FCB… but all bits end up serialized on disk in the FCB’s security descriptor (you can deny synchronous I/O access to a file… what’s the use of that?). In fact, almost any all operations on file objects would be detected as "read"/"write"/"execute" at the same time by looking at the GENERIC_MAPPING alone, since all generic accesses include SYNCHRONIZE… An extreme case are disk volumes, where a single security descriptor (the volume’s) bypasses hundreds or thousands others (the files’), an unique issue that MAC policies don’t address because UNIX doesn’t give any raw access to mounted volumes.

    And objects are hard enough, let’s not even get into the mess that subjects are: where TrustedBSD (my reference implementation) has a single label covering a process, its credentials and all its threads, Windows has a security descriptor for the process, a security descriptor for each thread in it, a process token, a security descriptor for the token itself, multiplied by the number of active impersonation tokens… I don’t know how Windows handles it yet, but I remember coming up with really byzantine formulas.

    A small PS: the definition of MAC policies uses the term "label" instead of the more specific "level", because labels are required to be composed of a non-hierarchical component (a set of horizontal "compartments" – the original Bell-LaPadula paper used "Crypto", "NATO" and "Nuclear") as well as the hierarchical component (the vertical "level" – e.g. "unclassified", "secret", "top secret", because Bell-LaPadula is specular to Biba and designed to prevent unauthorized leaks of information, rather than violations of integrity), but that’s another story for another day (basically, Windows implements a single compartment)

  10. KJK::Hyperion says:

    I can answer the cryptic part about generic accesses and mandatory integrity – not to brag, but my BS thesis was the first ever implementation of a Bell-LaPadula policy for Windows (ReactOS actually, because my school didn’t have a WRK license, but close enough).

    The Biba integrity policy (the one implemented by Vista) is based on the concept of an integrity level. Every object (in Windows, a security descriptor) and subject (token object) is marked ("labeled" is the technical term) with a specific integrity level. The more critical a process, file, registry key, etc. the higher the integrity level. Conversely, the more untrusted a program, the lower the integrity level it will need to be run at. Levels as assigned to objects and subjects are immutable, for really complicated reasons (the short of it: Windows doesn’t support revoking access to objects). Obviously, you don’t want low-integrity processes to be able to alter high-integrity code and data; in MAC-speak, this is known as "no write up", and it’s one of the two security properties that make up the Biba policy.

    To detect whether an access control operation (CreateFile, OpenProcess, RegCreateKey, etc.) is going "up", you just compare the subject’s level with the object’s: if the object’s is larger, the direction is "up". To detect whether the operation is a "write", you have to take the intersection between the requested access and the GenericWrite mask for the object type: if the intersection isn’t empty, the operation is "write". An operation that is both "write" and "up" fails before the ACL is even checked.

    For an object type to properly support mandatory policy, it’s imperative that either its GenericWrite includes DELETE | WRITE_DAC | WRITE_OWNER (I believe the system handles ACCESS_SYSTEM_SECURITY automatically), or that the object type doesn’t support the delete/set owner/write DAC operations. Failure to do this results in a security hole (a write operation that isn’t detected as a "write").

    The full Biba policy has a second property, "no read down", that prevents high-integrity processes from writing low-integrity data into themselves by themselves, but Windows doesn’t implement it (I can see why, it’d be an usability and compatibility nightmare). On the other hand, and if I remember correctly, Windows implements "no execute up" and "no read up" too. This is where things get interesting. Detecting writes alone is easy; detecting reads as well is complicated by dubious design choices made a long time ago. The very short of it (this was of the longest parts of my thesis): the Windows kernel object model is leaky, unorthogonal and can’t support mandatory security policies completely. The theory of the object model is that data managed by the kernel on behalf of user-mode is split up into discrete subsets, that is objects, and each object maps to a security model object, that is a security descriptor.

    The reality is very different. A lot of data isn’t even in an object, for example (the process list, to name one). Some non-trivial object types result in several data subsets with different semantics sharing the same object, and by extension the same security descriptor (which therefore has to contain a mishmash of access bits, some covering one subset, some covering another, some covering all of them…). For example, file mapping objects, where the same security descriptor covers the object itself and the data it points to – by looking at the GENERIC_MAPPING alone, a security policy would think file mapping objects supported write-only access (they don’t, mapped memory is always at least readable. Well they do with COW, but this isn’t expressed in any way the security subsystem can understand). Or file objects, actually made up of two objects (the file object itself and the FCB it points to) sharing a single security descriptor, where SYNCHRONIZE access covers the file object itself (a volatile, in-memory structure) and the rest of accesses cover the FCB… but all bits end up serialized on disk in the FCB’s security descriptor (you can deny synchronous I/O access to a file… what’s the use of that?). In fact, almost any all operations on file objects would be detected as "read"/"write"/"execute" at the same time by looking at the GENERIC_MAPPING alone, since all generic accesses include SYNCHRONIZE… An extreme case are disk volumes, where a single security descriptor (the volume’s) bypasses hundreds or thousands others (the files’), an unique issue that MAC policies don’t address because UNIX doesn’t give any raw access to mounted volumes.

    And objects are hard enough, let’s not even get into the mess that subjects are: where TrustedBSD (my reference implementation) has a single label covering a process, its credentials and all its threads, Windows has a security descriptor for the process, a security descriptor for each thread in it, a process token, a security descriptor for the token itself, multiplied by the number of active impersonation tokens… I don’t know how Windows handles it yet, but I remember coming up with really byzantine formulas.

    A small PS: the definition of MAC policies uses the term "label" instead of the more specific "level", because labels are required to be composed of a non-hierarchical component (a set of horizontal "compartments" – the original Bell-LaPadula paper used "Crypto", "NATO" and "Nuclear") as well as the hierarchical component (the vertical "level" – e.g. "unclassified", "secret", "top secret", because Bell-LaPadula is specular to Biba and designed to prevent unauthorized leaks of information, rather than violations of integrity), but that’s another story for another day (basically, Windows implements a single compartment)

  11. Seth says:

    Oh man, I had to read that last comment twice to get it… ;-)

  12. Name required says:

    He lost me at "BS thesis". Mainly because I thought that if the thesis was BS, I probably shouldn’t read what he said.

    (colon dash right-bracket)

  13. Poochner says:

    This is one of the reasons I read this blog.

    That [long post] reminds me of some mainframe systems, with access "rings."  Also, the "no read down" sounds like what perl aims for with data tainting.

  14. That comment was thesis-length. :)  Let me see whether I can condense it:

    MIC = Vista’s Mandatory Integrity Control.

    Every process (and every thread that is impersonating) has an access token.  The access token has a MIC level (typically Low, Medium, High or System).  Every object has an explicit or implicit MIC label, which consists of a level (e.g., Low, Medium, etc.) and a policy (no-write-up, no-read-up, no-execute-up).  If a process(thread) requests access to an object, and the token’s level is lower than that of the object label, then the policies come into play.  If the actor is requesting "write" permissions on the object, access is denied if the object’s policy includes "no-write-up".  If the actor is requesting "read" permissions, access is denied if the policy includes "no-read-up".  (And likewise with "execute" and "no-execute-up".)  So what constitutes "write" permissions?  That’s where the generic mappings, provided by the relevant resource manager (e.g., NTFS) come into play.  Each permission that GENERIC_WRITE maps to is a "write" permission; each permission that GENERIC_READ maps to is a "read" permission; etc.  (Or if the process/thread requests GENERIC_WRITE explicitly.)

    In Vista, most objects have explicit or implicit "no-write-up" policies only.  Processes and threads have both "no-write-up" and "no-read-up", so a lower integrity process cannot open the process and read its memory space.

    Hope this helps.

  15. For further reading on Vista’s Mandatory Integrity Control, here’s the whitepaper:

    http://msdn.microsoft.com/en-us/library/bb625964.aspx

  16. KJK::Hyperion says:

    Sorry for the rambling comment, but Raymond’s post gave me a Vietnam flashback

  17. KJK::Hyperion says:

    Poochner: data tainting schemes are, in fact, MAC policies. I wish more environments/languages supported them natively. The state of Internet security would be quite little different

Comments are closed.