PY-18154: Add sudo-like tool for windows to deal with UAC

When admin user launches Intellij, Windows revokes many
user privileges to improve safety for admin users (that is how UAC works).

One can't access "Program Files": any attempt to write something there
leads on ACCESS_DENIED(5) error even if NTFS reports file is writable

The only way to elevate privileges is to launch process as elevated.
"Elevator.sln" is Win32API app that launches command
as elevated. See its sources for more info.
This commit is contained in:
Ilya.Kazakevich
2017-05-18 01:26:28 +03:00
parent 8a84c3bad8
commit 29c484ca42
22 changed files with 1063 additions and 6 deletions
Binary file not shown.
Binary file not shown.
+53
View File
@@ -0,0 +1,53 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "launcher", "launcher\launcher.vcxproj", "{6631215A-50A4-49BE-A0B5-BDAC4C75B955}"
ProjectSection(ProjectDependencies) = postProject
{9D71A73C-2570-488A-958B-A126F4EEECB7} = {9D71A73C-2570-488A-958B-A126F4EEECB7}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elevator", "elevator\elevator.vcxproj", "{9D71A73C-2570-488A-958B-A126F4EEECB7}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elevShared", "elevShared\elevShared.vcxitems", "{45D41ACC-2C3C-43D2-BC10-02AA73FFC7C7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0AFC8DF7-8237-4909-8993-9CF44995AA42}"
ProjectSection(SolutionItems) = preProject
README.txt = README.txt
EndProjectSection
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
elevShared\elevShared.vcxitems*{45d41acc-2c3c-43d2-bc10-02aa73ffc7c7}*SharedItemsImports = 9
elevShared\elevShared.vcxitems*{6631215a-50a4-49be-a0b5-bdac4c75b955}*SharedItemsImports = 4
elevShared\elevShared.vcxitems*{9d71a73c-2570-488a-958b-a126f4eeecb7}*SharedItemsImports = 4
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Debug|x64.ActiveCfg = Debug|x64
{6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Debug|x64.Build.0 = Debug|x64
{6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Debug|x86.ActiveCfg = Debug|Win32
{6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Debug|x86.Build.0 = Debug|Win32
{6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Release|x64.ActiveCfg = Release|x64
{6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Release|x64.Build.0 = Release|x64
{6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Release|x86.ActiveCfg = Release|Win32
{6631215A-50A4-49BE-A0B5-BDAC4C75B955}.Release|x86.Build.0 = Release|Win32
{9D71A73C-2570-488A-958B-A126F4EEECB7}.Debug|x64.ActiveCfg = Debug|x64
{9D71A73C-2570-488A-958B-A126F4EEECB7}.Debug|x64.Build.0 = Debug|x64
{9D71A73C-2570-488A-958B-A126F4EEECB7}.Debug|x86.ActiveCfg = Debug|Win32
{9D71A73C-2570-488A-958B-A126F4EEECB7}.Debug|x86.Build.0 = Debug|Win32
{9D71A73C-2570-488A-958B-A126F4EEECB7}.Release|x64.ActiveCfg = Release|x64
{9D71A73C-2570-488A-958B-A126F4EEECB7}.Release|x64.Build.0 = Release|x64
{9D71A73C-2570-488A-958B-A126F4EEECB7}.Release|x86.ActiveCfg = Release|Win32
{9D71A73C-2570-488A-958B-A126F4EEECB7}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
+37
View File
@@ -0,0 +1,37 @@
With UAC enabled even administrator has limited access token and can't modify some folders like "Program Files".
The only official way to elevate token for process is to launch app as elevated using shell api so user will have chance to accept it.
Since it may lead to security risks it is not recomended to run whole app as elevated, but run only certain tools instead.
This app consists of 2 apps:
* launcher: accepts command line to run as elevated and launches "elevator" using shell api
* elevator: has "UAC execution level" set "administrator" in its manifest, so UAC is displayed. It then runs provided command line.
Since elevator is launched with elevated priviliges, it has separate console in conhost (technically it is not child of launcher but of AppInfo instead),
so there is some machinery to connect elevated process to console and its pipes.
Launcher provides its pid to elevator and elevator attaches to its console.
But if std(out|err|in) are redirected to files or pipes, attaching to console is not enough.
In this case launcher creates named pipes, elevator connects to them and provides their handlers as handlers for newly created process.
Launcher then creates threads to read/write them to console.
-------
How to build.
You may open .sln from Visual Studio or use msbuild from VS command prompt:
msbuild Elevator.sln /p:Configuration=release
-------
How to test.
From unelevated command.com run commands and click "yes"
Testing console
> launcher.exe %ComSpec%
You should be taken to admin console (i.e. has write access to c:\windows\)
Testing output
> echo spam | launcher.exe python.exe -c "import sys; sys.stderr.write('err'); print(sys.stdin.read()); " > out.txt 2> err.txt
Check you got "spam" in out.txt and "err" in "err.txt"
Ensure permissions
> launcher.exe python.exe -c "print(open('c:\\windows\\eggs.txt', 'w'))"
Check no error
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<HasSharedItems>true</HasSharedItems>
<ItemsProjectGuid>{45d41acc-2c3c-43d2-bc10-02aa73ffc7c7}</ItemsProjectGuid>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectCapability Include="SourceItemsFromImports" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(MSBuildThisFileDirectory)elevTools.h" />
</ItemGroup>
</Project>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClInclude Include="$(MSBuildThisFileDirectory)elevTools.h" />
</ItemGroup>
</Project>
+42
View File
@@ -0,0 +1,42 @@
#pragma once
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Shared file for elevator and launcher
// Author: Ilya Kazakevich
// So called "descriptors". Used as arguments in many macros and can also be used as binary flags
#define ELEV_DESCR_STDOUT 1
#define ELEV_DESCR_STDERR 2
#define ELEV_DESCR_STDIN 4
// Rules to generate pipe name
#define ELEV_GEN_PIPE_NAME(sDest, nPid, nDescriptor) wsprintf(sDest, L"\\\\.\\pipe\\_jetbrains%ld_%d", nPid, nDescriptor)
#define ELEV_BUF_SIZE 1024 //Buf to read/write between processes
// Convert descriptor to Win32API handler
#define ELEV_DESCR_GET_HANDLE(nDescriptor) (nDescriptor == ELEV_DESCR_STDOUT ? STD_OUTPUT_HANDLE : \
(nDescriptor == ELEV_DESCR_STDERR ? STD_ERROR_HANDLE : STD_INPUT_HANDLE))
// Pipe name
typedef wchar_t ELEV_PIPE_NAME[32];
// Separates arguments provided to elevator and user command line
#define ELEV_COMMAND_LINE_SEPARATOR L"--::--"
Binary file not shown.
+193
View File
@@ -0,0 +1,193 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <Windows.h>
#include <elevTools.h>
#include<stdio.h>
#include<io.h>
#include <fcntl.h>
// UAC-enabled (in manifset) tool to launch ptocesses.
// Connects to pipes and console, and then launches new process using CreateProcess
// Author: Ilya Kazakevich
// Connects and waits for pipe if required by descriptor flags
// nDescriptor ELEV_DESC_*
// nDescriptorFlags flags passed from launcher to check if descriptor should be connected
// Returns 0 if ok, error otherwise. Could be windows error, EBADF or EMFILE for dup2
static DWORD _ConnectIfNeededPipe(DWORD nParentPid, DWORD nDescriptor, FILE* stream, int nDescriptorFlags, _Out_ PHANDLE pRemoteProcessHandle)
{
if (!(nDescriptorFlags & nDescriptor))
{
*pRemoteProcessHandle = GetStdHandle(ELEV_DESCR_GET_HANDLE(nDescriptor));
return 0; // Not needed to connect pipe, use real descriptor
}
ELEV_PIPE_NAME sPipeName;
ELEV_GEN_PIPE_NAME(sPipeName, nParentPid, nDescriptor);
WaitNamedPipe(sPipeName, INFINITE);
BOOL bStdIn = nDescriptor == ELEV_DESCR_STDIN;
unsigned long access = (bStdIn ? GENERIC_READ : GENERIC_WRITE);
HANDLE hPipe = CreateFile(sPipeName, access, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hPipe == INVALID_HANDLE_VALUE || hPipe == NULL)
{
return GetLastError();
}
// Make inheritable by remote process
if (!SetHandleInformation(hPipe, HANDLE_FLAG_INHERIT, TRUE))
{
return GetLastError();
}
// Fix CRT
if (_dup2(_open_osfhandle((intptr_t)hPipe, _O_WTEXT | _O_TEXT), _fileno(stream)) != 0)
{
return errno;
}
// Fix Win32API
DWORD hStdHandleToChange = ELEV_DESCR_GET_HANDLE(nDescriptor);
if (!SetStdHandle(hStdHandleToChange, hPipe))
{
return GetLastError();
}
*pRemoteProcessHandle = hPipe;
return 0;
}
// PID Directory DescriptorFlags ProgramToRun Arguments
#define _ARG_PID 1
#define _ARG_DIR 2
#define _ARG_DESCRIPTORS 3
int wmain(int argc, wchar_t* argv[], wchar_t* envp[])
{
if (argc <= _ARG_DESCRIPTORS)
{
fwprintf(stderr, L"Bad command line");
return -1;
}
if (!SetCurrentDirectory(argv[_ARG_DIR]))
{
fwprintf(stderr, L"Failed to set directory to %s : %ld", argv[_ARG_DIR], GetLastError());
return -1;
}
DWORD nParentPid = _wtol(argv[_ARG_PID]);
if (!nParentPid)
{
fwprintf(stderr, L"Failed to get parent pid from %s", argv[_ARG_PID]);
return -1;
}
wchar_t* sDescriptorsStr = argv[_ARG_DESCRIPTORS];
size_t nDescriptorsLen = wcslen(sDescriptorsStr);
if (!nDescriptorsLen)
{
fwprintf(stderr, L"Failed to get descriptors from %s", sDescriptorsStr);
return -1;
}
for(int i = 0; i < nDescriptorsLen; i++)
{
if (! iswdigit(sDescriptorsStr[i]))
{
fwprintf(stderr, L"Bad descriptor %s", sDescriptorsStr);
return -1;
}
}
int nDescriptorFlags = _wtoi(sDescriptorsStr);
wchar_t* sFromSeparator = wcsstr(GetCommandLine(), ELEV_COMMAND_LINE_SEPARATOR);
if (! sFromSeparator)
{
fwprintf(stderr, L"Failed to find %s in %s", ELEV_COMMAND_LINE_SEPARATOR, GetCommandLine());
return -1;
}
// Add rest commandline
WCHAR* sCommandLine = sFromSeparator + wcslen(ELEV_COMMAND_LINE_SEPARATOR);
// Fix console
FreeConsole();
if (!AttachConsole(nParentPid))
{
fwprintf(stderr, L"Failed to attach console: %d", GetLastError());
return 1;
}
STARTUPINFO startupInfo;
ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
startupInfo.dwFlags = STARTF_USESTDHANDLES; // To pass handles to remote process
DWORD nError; // No place to output errors yet. Event log is overkill here, so we use exit code.
nError = _ConnectIfNeededPipe(nParentPid, ELEV_DESCR_STDIN, stdin, nDescriptorFlags, &startupInfo.hStdInput);
if (nError != 0)
{
exit(nError);
}
nError = _ConnectIfNeededPipe(nParentPid, ELEV_DESCR_STDOUT, stdout, nDescriptorFlags, &startupInfo.hStdOutput);
if (nError != 0)
{
exit(nError);
}
nError = _ConnectIfNeededPipe(nParentPid, ELEV_DESCR_STDERR, stderr, nDescriptorFlags, &startupInfo.hStdError);
if (nError != 0)
{
exit(nError);
}
HANDLE parentProcess = OpenProcess(SYNCHRONIZE, FALSE, nParentPid);
if (!parentProcess)
{
exit(GetLastError()); // If parent process can't be opened it probably dead
}
PROCESS_INFORMATION processInfo;
if (!CreateProcess(NULL, sCommandLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startupInfo, &processInfo))
{
fwprintf(stderr, L"Error launching process. Exit code %ld, command was %ls", GetLastError(), sCommandLine);
return 1;
}
HANDLE processesToWait[] = { parentProcess, processInfo.hProcess };
DWORD nWaitResult = WaitForMultipleObjects(2, processesToWait, FALSE, INFINITE);
if (WAIT_FAILED == nWaitResult)
{
fwprintf(stderr, L"Error waiting processes: %ld", GetLastError());
return -1;
}
if (nWaitResult - WAIT_OBJECT_0 == 0)
{
fwprintf(stderr, L"Parent process (launcher) died?");
TerminateProcess(processInfo.hProcess, -1);
return -1;
}
DWORD nExitCode = 0;
GetExitCodeProcess(processInfo.hProcess, &nExitCode);
return nExitCode;
}
@@ -0,0 +1,176 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{9D71A73C-2570-488A-958B-A126F4EEECB7}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>elevator</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
<Import Project="..\elevShared\elevShared.vcxitems" Label="Shared" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(IncludePath);</IncludePath>
<SourcePath>$(VC_SourcePath);</SourcePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(IncludePath);</IncludePath>
<SourcePath>$(VC_SourcePath);</SourcePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(IncludePath);</IncludePath>
<SourcePath>$(VC_SourcePath);</SourcePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(IncludePath);</IncludePath>
<SourcePath>$(VC_SourcePath);</SourcePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="elevator.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Resource.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="jb.ico" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="elevator.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Resource.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="jb.ico" />
</ItemGroup>
</Project>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.
Binary file not shown.
+275
View File
@@ -0,0 +1,275 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <Windows.h>
#include <elevTools.h>
#include <stdio.h>
// Elevation "frontend". Launched by user it starts elevator, connects to it and reads data from it
// Author: Ilya Kazakevich
// Pipe that should be connected to remote process
typedef struct
{
DWORD nRemoteProcessPid;
DWORD nDescriptor; //One of ELEV_* descriptors
BOOL bFromExternalProcess; // True if pipe is for READING from EXTERNAL process. Otherwise to write to it
} _PIPE_CONNECTION_INFO;
// Pipes to remote process. Accessed from another threads so they are global
static _PIPE_CONNECTION_INFO g_stdOutPipe, g_stdErrPipe, g_stdInPipe;
#define _CONFIGURE_PIPE_INFO(pipeInfo, nDescriptorToSet, nPid, bFromExternalProcessDirection) { \
pipeInfo.nDescriptor = nDescriptorToSet; \
pipeInfo.nRemoteProcessPid = nPid; \
pipeInfo.bFromExternalProcess = bFromExternalProcessDirection; \
}
// Returns full command line excluding program itself
static WCHAR* _GetCommandLineNoProgram()
{
WCHAR* sCommandLine = GetCommandLine();
int nNumberOfArgs;
WCHAR** args = CommandLineToArgvW(sCommandLine, &nNumberOfArgs);
WCHAR* sProgram = args[0];
size_t nProgramLengthChars = wcslen(sProgram);
LocalFree(args);
if (sCommandLine[0] == L'"')
{
nProgramLengthChars += 2; //Program name is in quotes
}
WCHAR * sCommandLineAfterProgram = sCommandLine + nProgramLengthChars;
for (; sCommandLineAfterProgram[0] == L' '; sCommandLineAfterProgram++) {} // Remove spaces after program
return sCommandLineAfterProgram;
}
// Adds argument to command line.
// pchCurrentBufferSize should be *psCommandLine size. Incremeted automatically.
// psCommandLine to append to
// sStringToAdd arugment to add
// Escaping is not supported, so string can't have quotes
static void _AddStringToCommandLine(_Inout_ size_t* pchCurrentBufferChars, _Inout_ WCHAR** psCommandLine, _In_ WCHAR* sStringToAdd, _In_ BOOL bAddQuotes)
{
// TODO: Doc suboptimal. Use line length instead of wcslen(*psCommandLine) ("shlemiel the painter algorithm")
size_t nCurrentLineLengthChars = wcslen(*psCommandLine);
size_t nStringToAddLengthChars = wcslen(sStringToAdd);
size_t nSpaceLeftInBufferChars = (*pchCurrentBufferChars) - nCurrentLineLengthChars;
// "\"new_string_goes_here\" "
size_t nRequiredSizeChars = (*pchCurrentBufferChars) + nStringToAddLengthChars;
if (bAddQuotes)
{
nRequiredSizeChars += wcslen(L"\"\" ");
}
if (nSpaceLeftInBufferChars < nRequiredSizeChars)
{
// Not enough space, add more space
(*pchCurrentBufferChars) = nRequiredSizeChars;
*psCommandLine = realloc(*psCommandLine, sizeof(WCHAR) * (*pchCurrentBufferChars));
}
WCHAR* endOfCommandLine = (*psCommandLine) + nCurrentLineLengthChars;
if (bAddQuotes) {
wsprintf(endOfCommandLine, L"\"%ls\" ", sStringToAdd);
}
else
{
wcscat_s((*psCommandLine), (*pchCurrentBufferChars), sStringToAdd);
}
}
// ThreadProc to connect pipe to remote process
static DWORD _CreateConnectPipe(_PIPE_CONNECTION_INFO* pPipeInfo)
{
ELEV_PIPE_NAME sPipeName;
ELEV_GEN_PIPE_NAME(sPipeName, pPipeInfo->nRemoteProcessPid, pPipeInfo->nDescriptor);
int access = (pPipeInfo->bFromExternalProcess ? PIPE_ACCESS_INBOUND : PIPE_ACCESS_OUTBOUND);
HANDLE hExternalPipe = CreateNamedPipe(
sPipeName,
access,
PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS | PIPE_TYPE_BYTE,
1,
ELEV_BUF_SIZE,
ELEV_BUF_SIZE,
0,
NULL);
if (hExternalPipe == NULL || hExternalPipe == INVALID_HANDLE_VALUE)
{
fwprintf(stderr, L"Failed to create in pipe: %ld", GetLastError());
exit(-1);
}
if (!ConnectNamedPipe(hExternalPipe, NULL))
{
fwprintf(stderr, L"Failed to wait for in pipe: %ld", GetLastError());
exit(-1);
}
char buffer[ELEV_BUF_SIZE];
DWORD nBytesRead;
DWORD nBytesWritten;
HANDLE hInternal = GetStdHandle(ELEV_DESCR_GET_HANDLE(pPipeInfo->nDescriptor));
HANDLE hToRead = (pPipeInfo->bFromExternalProcess ? hExternalPipe : hInternal);
HANDLE hToWrite = ((! pPipeInfo->bFromExternalProcess) ? hExternalPipe : hInternal);
while (1)
{
if (!ReadFile(hToRead, buffer, ELEV_BUF_SIZE, &nBytesRead, NULL))
{
DWORD nError = GetLastError();
if (nError == ERROR_BROKEN_PIPE)
{
break;
}
fwprintf(stderr, L"Failed to read from %ld: %ld", pPipeInfo->nDescriptor, GetLastError());
exit(-1);
}
if (!WriteFile(hToWrite, buffer, nBytesRead, &nBytesWritten, NULL))
{
DWORD nError = GetLastError();
if ((!pPipeInfo->bFromExternalProcess) && nError == ERROR_BROKEN_PIPE)
{
break;
}
fwprintf(stderr, L"Failed to write: %ld", GetLastError());
exit(-1);
}
FlushFileBuffers(hToWrite);
}
CloseHandle(hToWrite);
CloseHandle(hToRead);
return 0;
}
// If pipe is not console this function connects it, sets thread handler and configures descriptor flags to mark this descriptor is connected
static void _LaunchPipeThread(_PIPE_CONNECTION_INFO* pPipeInfo, _Out_opt_ PHANDLE pThreadHandle, _Inout_ int* pDescriptorFlags)
{
HANDLE hHandle = GetStdHandle(ELEV_DESCR_GET_HANDLE(pPipeInfo->nDescriptor));
// Console apps may act differently if its stream is not connected to console, so we only connect when file or pipe is used
if (GetFileType(hHandle) == FILE_TYPE_CHAR ) // Console (same as *nix istty()), do not touch
{
if (pThreadHandle) {
*pThreadHandle = NULL;
}
return;
}
HANDLE hThread = CreateThread(NULL, 0, _CreateConnectPipe, pPipeInfo, 0, NULL);
if (pThreadHandle)
{
*pThreadHandle = hThread;
}
*pDescriptorFlags |= pPipeInfo->nDescriptor;
}
int wmain(int argc, wchar_t* argv[], wchar_t* envp[])
{
DWORD nExitCode = 0;
// Get pids
DWORD nPid = GetCurrentProcessId();
WCHAR sPid[20];
_ltow_s(nPid, sPid, 20, 10);
_CONFIGURE_PIPE_INFO(g_stdOutPipe, ELEV_DESCR_STDOUT, nPid, TRUE);
_CONFIGURE_PIPE_INFO(g_stdErrPipe, ELEV_DESCR_STDERR, nPid, TRUE);
_CONFIGURE_PIPE_INFO(g_stdInPipe, ELEV_DESCR_STDIN, nPid, FALSE);
_mm_mfence(); // To make sure threads has access to g_
HANDLE arHandlesToWait[] = {NULL, NULL};
int nDescriptorFlags = 0;
_LaunchPipeThread(&g_stdInPipe, NULL, &nDescriptorFlags); // No need to wait stdin thread so we do not need its handle
_LaunchPipeThread(&g_stdOutPipe, &arHandlesToWait[0], &nDescriptorFlags);
_LaunchPipeThread(&g_stdErrPipe, &arHandlesToWait[1], &nDescriptorFlags);
//Get current dir
WCHAR sCurrentDirectory[MAX_PATH + 1];
GetCurrentDirectory(MAX_PATH, sCurrentDirectory);
//Build commandline
size_t chCurrentSize = 1;
WCHAR* sNewCommandLine = calloc(1, sizeof(WCHAR));
_AddStringToCommandLine(&chCurrentSize, &sNewCommandLine, sPid, TRUE);
_AddStringToCommandLine(&chCurrentSize, &sNewCommandLine, sCurrentDirectory, TRUE);
WCHAR sDescriptorFlags[3];
_itow_s(nDescriptorFlags, sDescriptorFlags, 2, 10);
_AddStringToCommandLine(&chCurrentSize, &sNewCommandLine, sDescriptorFlags, TRUE);
_AddStringToCommandLine(&chCurrentSize, &sNewCommandLine, ELEV_COMMAND_LINE_SEPARATOR, FALSE);
// Add arguments provided by user to the tail of command line
// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
WCHAR * sOriginalCommandLineNoProgram = _GetCommandLineNoProgram();
size_t nNewBufferSizeChars = wcslen(sOriginalCommandLineNoProgram) + wcslen(sNewCommandLine) + 1;
sNewCommandLine = realloc(sNewCommandLine, nNewBufferSizeChars * sizeof(WCHAR));
wcscat_s(sNewCommandLine, nNewBufferSizeChars, sOriginalCommandLineNoProgram);
// Get full path to elevator
WCHAR sPath[MAX_PATH + 1];
if(!GetModuleFileName(NULL, sPath, MAX_PATH))
{
fprintf(stderr, "Failed to get directory: %ld", GetLastError());
return 1;
}
WCHAR sDrive[_MAX_DRIVE], sDir[_MAX_DIR], sFile[_MAX_FNAME], sExt[_MAX_EXT];
_wsplitpath_s(sPath, sDrive, _MAX_DRIVE, sDir, _MAX_DIR, sFile, _MAX_FNAME, sExt, _MAX_EXT);
swprintf_s(sPath, MAX_PATH, L"%ls%lselevator.exe", sDrive, sDir);
//Execute elevator
SHELLEXECUTEINFO execInfo;
ZeroMemory(&execInfo, sizeof(SHELLEXECUTEINFO));
execInfo.cbSize = sizeof(SHELLEXECUTEINFO);
execInfo.lpParameters = sNewCommandLine;
execInfo.lpFile = sPath;
execInfo.lpDirectory = sCurrentDirectory;
execInfo.fMask = SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE;
if (!ShellExecuteEx(&execInfo))
{
fprintf(stderr, "Failed to launch process: %ld", GetLastError());
return 1;
}
// Wait for all threads
for(int i = 0; i < 2; i++)
{
if (arHandlesToWait[i])
{
WaitForSingleObject(arHandlesToWait[i], INFINITE);
}
}
// Process should be ended here, lets wait
WaitForSingleObject(execInfo.hProcess, INFINITE);
GetExitCodeProcess(execInfo.hProcess, &nExitCode);
return nExitCode;
}
@@ -0,0 +1,173 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{6631215A-50A4-49BE-A0B5-BDAC4C75B955}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>launcher</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
<Import Project="..\elevShared\elevShared.vcxitems" Label="Shared" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(IncludePath);</IncludePath>
<SourcePath>$(VC_SourcePath);</SourcePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(IncludePath);</IncludePath>
<SourcePath>$(VC_SourcePath);</SourcePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(IncludePath);</IncludePath>
<SourcePath>$(VC_SourcePath);</SourcePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(IncludePath);</IncludePath>
<SourcePath>$(VC_SourcePath);</SourcePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="launcher.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Resource.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="launcher.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Resource.rc" />
</ItemGroup>
</Project>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>
Binary file not shown.
@@ -144,8 +144,17 @@ public class ExecUtil {
command.add(commandLine.getExePath());
command.addAll(commandLine.getParametersList().getList());
GeneralCommandLine sudoCommandLine;
if (SystemInfo.isMac) {
final GeneralCommandLine sudoCommandLine;
if (SystemInfo.isWinVistaOrNewer) {
// launcher.exe process with elevated permissions on UAC.
final File launcherExe = PathManager.findBinFileWithException("launcher.exe");
sudoCommandLine = new GeneralCommandLine(launcherExe.getPath());
sudoCommandLine.setWorkDirectory(commandLine.getWorkDirectory());
sudoCommandLine.addParameter(commandLine.getExePath());
sudoCommandLine.addParameters(commandLine.getParametersList().getParameters());
sudoCommandLine.getEnvironment().putAll(commandLine.getEffectiveEnvironment());
}
else if (SystemInfo.isMac) {
String escapedCommandLine = StringUtil.join(command, ExecUtil::escapeAppleScriptArgument, " & \" \" & ");
String escapedScript = "tell current application\n" +
" activate\n" +
@@ -118,7 +118,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
private boolean refreshAndCheckForSetuptools() throws ExecutionException {
try {
final List<PyPackage> packages = refreshAndGetPackages(false);
return PyPackageUtil.findPackage(packages, PyPackageUtil.SETUPTOOLS) != null ||
return PyPackageUtil.findPackage(packages, PyPackageUtil.SETUPTOOLS) != null ||
PyPackageUtil.findPackage(packages, PyPackageUtil.DISTRIBUTE) != null;
}
catch (PyExecutionException e) {
@@ -233,7 +233,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
if (canModify) {
final String location = pkg.getLocation();
if (location != null) {
canModify = FileUtil.ensureCanCreateFile(new File(location));
canModify = ensureCanCreateFile(new File(location));
}
}
args.add(pkg.getName());
@@ -249,6 +249,29 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
}
// TODO: Move to FileUtil.ensureCanCreateFile ?
/**
* When file it protected with UAC on Windows, you can't relay on {@link File#canWrite()}.
*
* @param file file to check if writable (works in UAC too)
*/
private static boolean ensureCanCreateFile(@NotNull final File file) {
if (SystemInfo.isWinVistaOrNewer) {
try {
final File folder = (file.isFile() ? file.getParentFile() : file);
final File tmpFile = File.createTempFile("pycharm", null, folder);
tmpFile.deleteOnExit();
tmpFile.delete();
}
catch (final IOException ignored) {
return false;
}
return true;
}
return FileUtil.ensureCanCreateFile(file);
}
@Nullable
@Override
@@ -439,8 +462,8 @@ public class PyPackageManagerImpl extends PyPackageManager {
cmdline.addAll(args);
LOG.info("Running packaging tool: " + StringUtil.join(cmdline, " "));
final boolean canCreate = FileUtil.ensureCanCreateFile(new File(homePath));
final boolean useSudo = !canCreate && !SystemInfo.isWindows && askForSudo;
final boolean canCreate = ensureCanCreateFile(new File(homePath));
final boolean useSudo = !canCreate && askForSudo;
try {
final GeneralCommandLine commandLine = new GeneralCommandLine(cmdline).withWorkDirectory(workingDir);