Fix use of FILE_FLAG_BACKUP_SEMANTICS in win32unix#8797
Draft
dra27 wants to merge 7 commits intoocaml:trunkfrom
Draft
Fix use of FILE_FLAG_BACKUP_SEMANTICS in win32unix#8797dra27 wants to merge 7 commits intoocaml:trunkfrom
dra27 wants to merge 7 commits intoocaml:trunkfrom
Conversation
Use FILE_FLAG_BACKUP_SEMANTICS to get a handle to a directory, paying particular care not to utilise SeRestorePrivilege in doing so.
ENOENT was assumed for all errors from CreateFile when EACCES is occasionally the more appropriate response.
stedolan
reviewed
Jul 17, 2019
Contributor
stedolan
left a comment
There was a problem hiding this comment.
This is frankly terrifying. Great work.
| privilege++; | ||
| } | ||
|
|
||
| if (i < privs->PrivilegeCount) { |
Contributor
There was a problem hiding this comment.
I think that if we have a token and the token does not mention SeRestorePrivilege, then this condition is false, restore->PrivilegeCount will never be initialised, the function returns TRUE, and a later unix_restore_privilege reads the uninitialised restore->PrivilegeCount. I think?
Contributor
|
Would be nice to have this for 4.10 along with #8621 to improve the Windows platform support. |
Member
|
@dra27 , this feels a bit too complex for mergin in the 4.10 beta. Should we try to target 4.11 ? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This one needs some 🍿...
Windows, ah, Windows
So, on Windows, directories and files are somewhat more distinct than they are on Unix. The Windows
CreateFileAPI (confusingly, this function is also responsible for opening existing files) will return handles to directories, but only if the strangely namedFILE_FLAG_BACKUP_SEMANTICSis passed to it.This is used in
Unix.statandUnix.readlinkand #8796 noted that we should be doing it inUnix.utimesfor directories. It all looks good, and it's at least what Python and Go do, if not other major cross-platform OS libraries.But they are all wrong.
It's all in the semantics
Everyone gets duped by the sentence "To open a directory using CreateFile, specify the FILE_FLAG_BACKUP_SEMANTICS flag as part of dwFlagsAndAttributes." but fail to spot the following sentence: "Appropriate security checks still apply when this flag is used without SE_BACKUP_NAME and SE_RESTORE_NAME privileges."
So far, so good - but what happens if we're running as a user who has
SE_BACKUP_NAMEorSE_RESTORE_NAME? Well, unfortunately that means that our standard library function merrily bypasses all security checks on the files it's reading. It's not a privilege escalation, because the user has the capability, but it's somewhat akin to having a call tosudoembedded in a function for seemingly no reason.The cure? If we have
SeBackupPrivilege(forUnix.statandUnix.readlink) orSeRestorePrivilege(forUnix.utimes), then we need to drop that privilege when we open the directory, in order to ensure that we don't invoke those powers and access a directory which you would not expect a general purpose function to access.Some background for why I think this subtlety matters:
SeBackupPrivilegeandSeRestorePrivilegeare intended, as the name implies, for backup programs but the privilege is by default available for the Administrators group. However, there are intentionally various steps you have to go through in your program to invoke the privilege:FILE_FLAG_BACKUP_SEMANTICSto theCreateFilecallNow, with a triple-lock like that in "the design", I don't think general purpose functions should be accidentally taking advantage of those privileges, however tricky it is to enable them.
The implementation
First of all, the implementation only uses
FILE_FLAG_BACKUP_SEMANTICSfor directories. If a directory is encountered, then the three functions now analyse the privileges of the security token associated with the thread making the call.The first commit in this chain attempted to solve this at the process level, but it requires some enormously subtle interlocking to avoid a race condition between multiple calls. I didn't like it.
The second solution, which I dislike less, is to ensure that the thread making the call to
CreateFiledrops the privilege if necessary. This is far more complicated than a Unix programmer might expect, but about par for the course for the Windows API. Essentially:I have also, whilst here, changed the behaviour of
Unix.statandUnix.readlinkto returnEACCESinstead ofENOENTin some cases (I think this is an error from my 2015 code - you don't expectENOENTon a file to which you don't have access). That change possibly wants moving to a separate PR.