Date: | June 4, 2004 / year-entry #221 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20040604-00/?p=39023 |
Comments: | 21 |
Summary: | Sometimes you might want to determine whether you can do something without actually doing it. For example, you might want to know whether you have a particular permission in a directory, say permission to delete files from it. One way is to retrieve the ACL and then check whether the current user has the desired... |
Sometimes you might want to determine whether you can do something without actually doing it. For example, you might want to know whether you have a particular permission in a directory, say permission to delete files from it. One way is to retrieve the ACL and then check whether the current user has the desired permission. The AccessCheck function does most of the heavy lifting there. Or you can realize, "Hey, wait a second, there is an entire security infrastructure whose job it is to decide who can access which files. Why not use it?" For example, here's how you can check whether the user has permission to delete files from a directory: BOOL CanDeleteFilesFromDirectory(LPCTSTR pszPath) { HANDLE h = CreateFile(pszPath, FILE_DELETE_CHILD, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h != INVALID_HANDLE_VALUE) { CloseHandle(h); } return h != INVALID_HANDLE_VALUE; } What we did was open the directory (which requires backup semantics) and ask for FILE_DELETE_CHILD access. If it succeeded, then we have permission to delete files from it. [Corrected 7:52am] Note, of course, that this information is purely advisory. You shouldn't be making security decisions based on this, because the permissions might change between the time you check and the time you try. |
Comments (21)
Comments are closed. |
It’s a nit, but you’re not opening the directory and asking for delete access – you’re opening the directory and asking for DELETE_CHILD access.
The two permissions are quite different – DELETE says "delete this object", and is one of the global permissions (valid for all objects). DELETE_CHILD is specific to filesystems and says "I want to delete any child in the subdirectory".
And this permission is actually kinda tricky – you may still not be able to delete a particular file though, since that file might have an ACL on it that prevents deletion.
I’m not sure if the FILE_DELETE_CHILD access right on a directory trumps the DELETE access right on an individual file though. It might only trump the DELETE access right on files that have only inherited ACLs.
True, but if parent grants DELETE_CHILD then you can delete. This is clearly what you want -think about the "public writable directory" scenario.
"What we did was open the directory (which requires backup semantics) and ask for DELETE access."
That’s the nit I was commenting on.
So DELETE_CHILD is a trump on DELETE access to the files in the directory.
But if you might have rights to delete the files in the directory WITHOUT the DELETE_CHILD access to the directory itself. So checking for DELETE_CHILD access to the directory isn’t sufficient to determine if you have rights to delete a particular file. For that, you need to open the individual file for DELETE access.
The other thing you need to keep in mind is that this has the potential of generating false audits log entries.
On a related note… Would it be possible to add a feature to Windows, where an Administrator could enable a mode when details about permissions failures are recorded in the event log. Say "Operation failed because user {0} does not have permission {1} on resource {2}".
Some might see that as a security hole, but it would certainly fill more holes than it opens, as the normal reaction to a permission failure is to give more & more groups, more & more rights, until the right combination is hit upon by accident.
Larry: Oops, typo. Good catch.
And yes a specific file may grant you DELETE even though the directory may not grant FILE_DELETE_CHILD. (E.g. you can delete your picture from the user pictures directory but you can’t delete anybody else’s.) I perhaps-too-subtly phrased it as checking whether you can delete files from a directory, not whether you can delete a specific file.
James: It’s already there. Set the SACL on the resource to log the failures you’re interested in and enable failure logging.
James, Raymond’s exactly right – and it’s been in NT since NT 3.1.
One of the requirements of C2 certification is that all failures need to be auditable.
Now, there’s an interesting denial of service attack that can result from enabling auditing if you’re not careful – If you overflow the security event log (and the event log’s set to "do not overwrite events"), then the system will shut down when the event log is filled.
This is actually by design, since filling the event log with garbage is one way of erasing the audit trail after an attack on a system.
Larry:
Speaking of that, you know what would be handy? A message box on the next reboot that would inform you of this whne you logged in as an administrator and the details on how to fix it.
We had an attack on our exchange server and our IT guy literally spent 4 hours being transferred to and from Microsoft Support in India and Seattle before they told him to change one registry key.
We’re running Win2k, so if 2003 has it, then just let me know.
Regarding the following,
"Note, of course, that this information is purely advisory. You shouldn’t be making security decisions based on this, because the permissions might change between the time you check and the time you try.".
So is the advice to just go ahead and try, and have mechanism to deal with the failures?
I haven’t tested this, but I would have expected that the FILE_FLAG_BACKUP_SEMANTICS would require that the user have SE_BACKUP_NAME and/or SE_RESTORE_NAME permissions (and have them enabled).
Am I wrong?
Right, I’m opening the directory and asking if I have permission to delete files from it (DELETE_CHILD); isn’t that what I wrote? It’s what I meant.
DELETE_CHILD and DELETE on a child are cumulative. You can delete a child if (a) the child grants DELETE *or* if (b) parent grants DELETE_CHILD.
But the child might specifically *deny* DELETE, right?
This doesn’t check only permissions. There could be other reasons for CreateFile to fail. File not found is one example.
Even if this function returns TRUE and the permissons don’t change, it doesn’t guarantee that you’ll really be able to delete a file. What if someone else has a handle open and didn’t pass SHARE_DELETE?
I don’t see the point trying to check for delete access before attempting to delete. If you want to see if you can delete a file, just try to delete it. If DeleteFile fails GetLastError will tell you why. Checking first then attempting the delete will be slower, require more code, and be less robust.
True. This sort of check is suitable for something like "Don’t offer to delete all files in the directory if the user doesn’t have permission anyway."
Ah – sneaky stuff. I should have suspected something like that from a shell dev. ;-)
I still say this is evil, though. As a tester, it means more test cases to write. Worse, many of the bugs found would probably be "bogus test bugs" aka "won’t fix". Any time someone actually needs to use a function like that I would argue that there is probably an underlying design flaw.
Hm, on the subject of logging, is it possible to get the process name in the audit logs rather than the PID? It’s kinda hard to use PID after the process has ended.
True. I was thinking of a more ephemeral scenario like a wizard step. This issue originally arose when implementing the desktop cleanup wizard. If you don’t have permission to delete files from the desktop, then there’s no point in running the wizard.
josh: Audit Process Tracking will do this.
aha! Thanks, I’ll give that a shot.
Ok, that’s still pretty painful to use. You still only get a PID on each event, then you get to do a manual linear search to find out what the program was. At least it’s *possible* now…