The CreatePrivateObjectSecurity
function is part of a family of functions intended for programs that implement security descriptors for their own custom objects. Normally, you would let the kernel object manager deal with security descriptors, but if your object isn’t a kernel object, then you have to do your own security management. These functions let you give your objects a security model that matches those of kernel objects.
The CreatePrivateObjectSecurity
function is one of the functions for assigning security descriptors to sub-objects. It understands the rules for “container inherit” and “inherit only” as well as the magic SIDs like “creator owner”. This is what your custom CreateSubObject
function uses to generate the security descriptor for a new sub-object.
But we can use it here to calculate the security descriptor that would be applied to a subdirectory. We pretend that we are the file system, managing our custom “directory” object.
GENERIC_MAPPING fileGenericMapping = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_GENERIC_ALL, }; PSECURITY_DESCRIPTOR childSd; CreatePrivateObjectSecurity( parentSd, // ParentDescriptor nullptr, // CreatorDescriptor &childSd, // NewDescriptor TRUE // IsDirectoryObject nullptr, // Token &fileGenericMapping); // GenericMapping ... DestroyPrivateObjectSecurity(childSd);
The CreatePrivateObjectSecurity
gives you the security descriptor which results from the information you pass in:
- The security descriptor of the parent object. In our case, it’s the security descriptor of the parent directory.
- An optional custom security descriptor for the child object. In our case, we pass
NULL
to indicate that we want to inherit from the parent. - Whether the sub-object is itself a container. The function uses this to decide whether “container inherit” ACEs apply to the new object.
- An optional token representing the user doing the creating. This is used to set the owner and group on the resulting security descriptor, as well as knowing what “creator owner” ACEs should be converted to. We pass
NULL
to say that the function should use the current default token. - A
GENERIC_MAPPING
structure that specifies how generic access bits should be converted. Fortunately, the generic mapping for file system objects is documented, and it even has convenient names that we can use.
The theory is that this could be used to avoid the race condition when creating a folder that inherits its parent’s ACL, and then overriding part of it. That race condition exists in the period of time after the subdirectory is created with default attributes and before the program can apply the new seurity attributes. During that time, somebody might gain access to the directory in a form that would have been disallowed by your override.
Using this technique, you can precalculate what the default attributes would be, then apply your custom override, and then pass those security attributes when you call CreateDirectory
. This removes the race window where the subdirectory briefly has the wrong security attributes.
Unfortunately, you have other problems.
For one thing, you opened a different race condition: If the security attributes of the parent directory change, then you will apply those stale attributes to the subdirectory.
But the worse thing is that I glossed over the hard part: getting the security descriptor of the parent directory. The CreatePrivateObjectSecurity
function is intended to be used by the code that is managing security of its custom objects, so it has full access to all the security descriptors. But in this case, we are an outside operator, and getting access to that security descriptor may not be possible. We may not have READ_CONTROL
access to the DACL. We may not have ACCESS_SYSTEM_SECURITY
rights to read the SACL.
So this idea sounded good on paper, but runs into problems in practice. Still, I mentioned it because it gives me an excuse to write about the CreatePrivateObjectSecurity
function.
0 comments