Holy cow, I wrote a book!
The GetSecurityInfo function returns a copy of the security descriptor for a kernel object, along with pointers to specific portions you request. More than once, a customer has been confused by the guidelines for how to manage the memory returned by the function.
GetSecurityInfo
Let's look at what the function says:
ppsidOwner [out, optional] A pointer to a variable that receives a pointer to the owner SID in the security descriptor returned in ppSecurityDescriptor. The returned pointer is valid only if you set the OWNER_SECURITY_INFORMATION flag. This parameter can be NULL if you do not need the owner SID.
OWNER_SECURITY_INFORMATION
Similar verbiage can be found for the other subcomponent parameters. The final parameter is described as
ppSecurityDescriptor [out, optional] A pointer to a variable that receives a pointer to the security descriptor of the object. When you have finished using the pointer, free the returned buffer by calling the LocalFree function.
Okay, so it's clear that you need to free the security descriptor with LocalFree. But how do you free the owner, group, DACL, and SACL?
LocalFree
Read the documentation again. I've underlined the important part.
In case that wasn't clear, the point is reiterated in the remarks.
If the ppsidOwner, ppsidGroup, ppDacl, and ppSacl parameters are non-NULL, and the SecurityInfo parameter specifies that they be retrieved from the object, those parameters will point to the corresponding parameters in the security descriptor returned in ppSecurityDescriptor.
In other words, you are getting a pointer into the security descriptor. No separate memory allocation is made. The memory for the owner SID is freed when you free the security descriptor. It's like the last parameter to GetFullPathName, which receives a pointer to the file part of the full path. There is no separate memory allocation for that pointer; it's just a pointer back into the main buffer.
GetFullPathName
You can think of the ppsidOwner parameter as a convenience parameter. The GetSecurityInfo function offers to do the work of calling GetSecurityDescriptorOwner for you. You can think of the function as operating like this:
ppsidOwner
GetSecurityDescriptorOwner
DWORD WINAPI GetSecurityInfo(...) { ... blah blah get the security info ... // Just out of courtesy: // Fetch the owner if the caller requested it if (ppsidOwner != NULL && (SecurityInfo & OWNER_SECURITY_INFO)) { BOOL fDefaulted; GetSecurityDescriptorOwner(pSecurityDescriptor, ppsidOwner, &fDefaulted); } ... }
That's why the documentation says that you need to pass a non-null ppSecurityDescriptor if you request any of the pieces of the security descriptor: If you don't, then you won't be able to free the memory for it.
ppSecurityDescriptor
Bonus chatter: If the ppSecurityDescriptor is so important, why is it marked "optional"?
It really should be a mandatory parameter, but older versions of Windows didn't enforce the rule, so the parameter is grandfathered in as optional, even though no self-respecting program should ever pass in NULL. If you pass NULL for the ppSecurityDescriptor, the function happily allocates the security descriptor and then, "Oh wait, the caller didn't give me a way to receive the pointer to the security descriptor, so I guess I won't give it to him."
NULL
DWORD WINAPI GetSecurityInfo(...) { ... blah blah get the security info ... if (ppSecurityDescriptor != NULL) { *ppSecurityDescriptor = pSecurityDescriptor; } ... }
Result: Memory leak.
You might say that the last parameter was designed by somebody wearing kernel-colored glasses.