bpo-31512: Add non-elevated symlink support for Windows (GH-3652)

This commit is contained in:
Vidar Tonaas Fauske 2019-04-09 20:19:46 +02:00 committed by Steve Dower
parent 8709490f48
commit 0e10766574
4 changed files with 53 additions and 79 deletions

View file

@ -284,10 +284,7 @@ extern char *ctermid_r(char *);
#include <windows.h>
#include <shellapi.h> /* for ShellExecute() */
#include <lmcons.h> /* for UNLEN */
#ifdef SE_CREATE_SYMBOLIC_LINK_NAME /* Available starting with Vista */
#define HAVE_SYMLINK
static int win32_can_symlink = 0;
#endif
#endif /* _MSC_VER */
#ifndef MAXPATHLEN
@ -7755,26 +7752,6 @@ os_readlink_impl(PyObject *module, path_t *path, int dir_fd)
#if defined(MS_WINDOWS)
/* Grab CreateSymbolicLinkW dynamically from kernel32 */
static BOOLEAN (CALLBACK *Py_CreateSymbolicLinkW)(LPCWSTR, LPCWSTR, DWORD) = NULL;
static int
check_CreateSymbolicLink(void)
{
HINSTANCE hKernel32;
/* only recheck */
if (Py_CreateSymbolicLinkW)
return 1;
Py_BEGIN_ALLOW_THREADS
hKernel32 = GetModuleHandleW(L"KERNEL32");
*(FARPROC*)&Py_CreateSymbolicLinkW = GetProcAddress(hKernel32,
"CreateSymbolicLinkW");
Py_END_ALLOW_THREADS
return Py_CreateSymbolicLinkW != NULL;
}
/* Remove the last portion of the path - return 0 on success */
static int
_dirnameW(WCHAR *path)
@ -7878,33 +7855,57 @@ os_symlink_impl(PyObject *module, path_t *src, path_t *dst,
{
#ifdef MS_WINDOWS
DWORD result;
DWORD flags = 0;
/* Assumed true, set to false if detected to not be available. */
static int windows_has_symlink_unprivileged_flag = TRUE;
#else
int result;
#endif
#ifdef MS_WINDOWS
if (!check_CreateSymbolicLink()) {
PyErr_SetString(PyExc_NotImplementedError,
"CreateSymbolicLink functions not found");
return NULL;
}
if (!win32_can_symlink) {
PyErr_SetString(PyExc_OSError, "symbolic link privilege not held");
return NULL;
}
#endif
#ifdef MS_WINDOWS
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
/* if src is a directory, ensure target_is_directory==1 */
target_is_directory |= _check_dirW(src->wide, dst->wide);
result = Py_CreateSymbolicLinkW(dst->wide, src->wide,
target_is_directory);
/* if src is a directory, ensure flags==1 (target_is_directory bit) */
if (target_is_directory || _check_dirW(src->wide, dst->wide)) {
flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
}
result = CreateSymbolicLinkW(dst->wide, src->wide, flags);
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
if (windows_has_symlink_unprivileged_flag && !result &&
ERROR_INVALID_PARAMETER == GetLastError()) {
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
/* This error might be caused by
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE not being supported.
Try again, and update windows_has_symlink_unprivileged_flag if we
are successful this time.
NOTE: There is a risk of a race condition here if there are other
conditions than the flag causing ERROR_INVALID_PARAMETER, and
another process (or thread) changes that condition in between our
calls to CreateSymbolicLink.
*/
flags &= ~(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE);
result = CreateSymbolicLinkW(dst->wide, src->wide, flags);
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
if (result || ERROR_INVALID_PARAMETER != GetLastError()) {
windows_has_symlink_unprivileged_flag = FALSE;
}
}
if (!result)
return path_error2(src, dst);
@ -13469,35 +13470,6 @@ static PyMethodDef posix_methods[] = {
{NULL, NULL} /* Sentinel */
};
#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
static int
enable_symlink()
{
HANDLE tok;
TOKEN_PRIVILEGES tok_priv;
LUID luid;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &tok))
return 0;
if (!LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid))
return 0;
tok_priv.PrivilegeCount = 1;
tok_priv.Privileges[0].Luid = luid;
tok_priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(tok, FALSE, &tok_priv,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL))
return 0;
/* ERROR_NOT_ALL_ASSIGNED returned when the privilege can't be assigned. */
return GetLastError() == ERROR_NOT_ALL_ASSIGNED ? 0 : 1;
}
#endif /* defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */
static int
all_ins(PyObject *m)
{
@ -14105,10 +14077,6 @@ INITFUNC(void)
PyObject *list;
const char * const *trace;
#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
win32_can_symlink = enable_symlink();
#endif
m = PyModule_Create(&posixmodule);
if (m == NULL)
return NULL;