-
-
Notifications
You must be signed in to change notification settings - Fork 31.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bpo-31512: Add non-elevated symlink support for dev mode Windows 10 #3652
Conversation
I'm not particularly familiar with the details of what was added in Creators update, but wouldn't it be better to leave |
@pfmoore The
I'm not a good judge for the correctness of this, but lacking any counter arguments I've assumed this to be true. I'll be happy to undo this bit as needed. |
I'm happy to go with @eryksun's view on this - he has a much better understanding than I do of the lower-level details involved. Thanks for providing the link back to the original discussion. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, with the NEWS grammar tweaks.
Misc/NEWS.d/next/Windows/2017-10-04-12-40-45.bpo-31512.YQeBt2.rst
Outdated
Show resolved
Hide resolved
Misc/NEWS.d/next/Windows/2017-10-04-12-40-45.bpo-31512.YQeBt2.rst
Outdated
Show resolved
Hide resolved
And yeah, I believe Eryk is correct here. We shouldn't have to do anything extra to enable the privilege for the process, but the call will fail if the privilege cannot be added due to enforced restrictions. |
76d406a
to
3669415
Compare
I fixed the news grammar. I also did a rebase to put the news entry together with the first commit, and in order to rename the commit according to the dev guide (bpo-xxxx prefix). Other than that:
Yes, but currently the error message will change (slightly less informative). I do not know if that is a problem? Old: |
Since we're not backporting this (I assume), the changed error message doesn't matter. We should probably add an entry to
Also, no need to rebase PRs. We always do squash merges, so there will only be one commit at the end - rebasing PRs only breaks code reviews. |
Not sure how I should add this, or if you intended that someone else do this ('we' is rather imprecise here). However, I did take the opportunity to update the doc entry for |
It just occurred to me that it's potentially a breaking change if Python stops enabling SeCreateSymbolicLinkPrivilege in the process token. When a server impersonates a client, it uses a duplicate of the client's token that honors the client's requested security quality of service. If the client requests that the impersonation is only "effective", which is the default SQOS setting, then the groups and privileges that are currently disabled in the client token will be removed from the server's impersonation token. Note that removing a group or privilege from a token is permanent; it's not merely disabled. Here's an example that demonstrates the current behavior with a named pipe. It lists the privileges in the process and thread tokens after calling
Output:
SeCreateSymbolicLinkPrivilege is just one of many privileges that are disabled by default and normally removed in this impersonation context. I'm comfortable with changing this because the docs never explicitly stated that Python enables this privilege for the entire process, only that the privilege is required to create symbolic links. |
good |
Is there anything more that I need to do on this PR, or is this now simply awaiting a favorable time? I'm not familiar with the contribution process for these things. |
Is there some problem with Windows CI? But anyway, it wouldn't pass. Maybe a feature test could be done instead of old |
Note that this PR accidentally fixes an unrelated bug by removing code. The following declaration in
I've confirmed that this leads to interpreting I can file a separate issue with a trivial PR fixing the declaration (for backporting only). Alternatively, maybe this PR could be split in two (one that removes code and may be backported; another that adds the feature), but I don't know the workflow. Any advice? |
I'd rather not rely on the order of error checking invalid parameters (i.e. using an invalid path), and I'd also rather not check that the build number is at least 15063. How about using a global value |
Why not? Is this expensive, or is this considered less than ideal for some reason?
I could add that, but this does run the risk of misattributing a race condition as a lack of support for this flag, e.g. this scenario:
As the documentation for |
I'd rather handle the error, but checking the build number works. Ensure it comes from kernel32.dll, since this feature doesn't depend on manifested support for Windows 10. You could factor out |
What if the unprivileged symlinks feature were backported to an earlier Windows version? Does Windows 10 Mobile have the same build numbers as normal Windows? Explicit feature testing avoids such kind of questions, and this is what Python does to check availability of various POSIX features at runtime (like syscalls).
On the bright side, if the second Theorizing a bit further, if the second
So, either we can't set |
It doesn't have to use obviously invalid arguments, for example, it could be I think that if we worry about races, the feature test at module initialization approach is slightly better because the just-in-time error checking approach suffers from exactly the same problem with If we don't, error checking seems cleaner. |
Thanks for clearing that up, I see the motivation now. I'll add the on-use error handling then, but with a comment in the code noting the chance of a race depending on the behavior of
It might still fail, but with another error code than |
You have not removed Also, it'd be better to rename |
Sorry for the commit noise, I'm lacking proper dev env at my current location, so I was abusing CI. It should be OK for a fresh review now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the fix. I've left several comments.
result = CreateSymbolicLinkW(dst->wide, src->wide, flags); | ||
if (result) { | ||
windows_has_symlink_unprivileged_flag = FALSE; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A note: on systems without the new flag support, we'll do both calls on each os.symlink
invocation until the first time the symlink can be created. This is probably not important, but could be avoided if windows_has_symlink_unprivileged_flag
was set to FALSE
in the case of failure with any error other than ERROR_INVALID_PARAMETER
(suffering from the same race as the case of success).
Thanks for the reviews! It seems things went a little quick yesterday. I've tried to clean up the logic so that there is as few thread blocks as possible, while still enabling threads for all calls out. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, thank you!
As Windows XP is no longer supported, these symlink checks can be removed.
On Windows 10 (build > 14972), symlinks are available for unprivileged users when the machine is in developer mode.
Rebased to resolve merge conflicts. |
Is there anything more that I need to do on this PR, or is this now simply awaiting a favorable time? I'm not familiar with the contribution process for these things. |
Vidar has apparently responded to all comments. Steve, can you re-review and presumably merge? |
if (windows_has_symlink_unprivileged_flag) { | ||
/* Allow non-admin symlinks if system allows it. */ | ||
flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; | ||
} | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
_Py_BEGIN_SUPPRESS_IPH |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to suppress the CRT invalid-parameter handler in this code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to remove it, but it caused test failures (test_os.Win32SynLinkTests.test_buffer_overflow). See #5989, which introduced these macros here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. That makes sense. I forgot that _check_dirW
was rewritten using the safe string functions. IMO, it needs improvement. We have path_t
strings, so they're null terminated, and we know their length
. Also, the check shouldn't be limited to MAX_PATH
. But that's for another day.
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2
flag when callingCreateSymbolicLinkW
on Windows. It also defines this flag if it is not already defined.This is my first time submitting a PR to cpython, so any help with procedural errors are welcome.
https://bugs.python.org/issue31512