Allow Windows users in developer mode to create symlinks without elevation (#8534)

This commit is contained in:
Steve Lee 2019-01-08 10:43:24 -08:00 committed by Aditya Patwardhan
parent c1920da410
commit d6000fdea8
2 changed files with 50 additions and 13 deletions

View file

@ -2434,9 +2434,16 @@ namespace Microsoft.PowerShell.Commands
private static bool WinCreateSymbolicLink(string path, string strTargetPath, bool isDirectory)
{
int created = NativeMethods.CreateSymbolicLink(path, strTargetPath, (isDirectory ? 1 : 0));
bool success = (created == 1) ? true : false;
return success;
// The new AllowUnprivilegedCreate is only available on Win10 build 14972 or newer
var flags = isDirectory ? NativeMethods.SymbolicLinkFlags.Directory : NativeMethods.SymbolicLinkFlags.File;
if (Environment.OSVersion.Version.Major == 10 && Environment.OSVersion.Version.Build >= 14972 ||
Environment.OSVersion.Version.Major >= 11)
{
flags |= NativeMethods.SymbolicLinkFlags.AllowUnprivilegedCreate;
}
int created = NativeMethods.CreateSymbolicLink(path, strTargetPath, flags);
return (created == 1) ? true : false;
}
private static bool WinCreateHardLink(string path, string strTargetPath)
@ -7014,10 +7021,32 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
/// <param name="name">Path of the symbolic link.</param>
/// <param name="destination">Path of the target of the symbolic link.</param>
/// <param name="destinationType">0 for destination as file and 1 for destination as directory.</param>
/// <param name="symbolicLinkFlags">Flag values from SymbolicLinkFlags enum.</param>
/// <returns>1 on successful creation.</returns>
[DllImport(PinvokeDllNames.CreateSymbolicLinkDllName, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int CreateSymbolicLink(string name, string destination, int destinationType);
internal static extern int CreateSymbolicLink(string name, string destination, SymbolicLinkFlags symbolicLinkFlags);
/// <summary>
/// Flags used when creating a symbolic link.
/// </summary>
[Flags]
internal enum SymbolicLinkFlags
{
/// <summary>
/// Symbolic link is a file.
/// </summary>
File = 0,
/// <summary>
/// Symbolic link is a directory.
/// </summary>
Directory = 1,
/// <summary>
/// Allow creation of symbolic link without elevation. Requires Developer mode.
/// </summary>
AllowUnprivilegedCreate = 2
}
/// <summary>
/// Creates a hard link using the native API.

View file

@ -256,19 +256,27 @@ Describe "New-Item with links" -Tags @('CI', 'RequireAdminOnWindows') {
}
}
Describe "New-Item with links fails for non elevated user." -Tags "CI" {
Describe "New-Item with links fails for non elevated user if developer mode not enabled on Windows." -Tags "CI" {
BeforeAll {
$tmpDirectory = $TestDrive
$testfile = "testfile.txt"
$testfolder = "newDirectory"
$testlink = "testlink"
$FullyQualifiedFile = Join-Path -Path $tmpDirectory -ChildPath $testfile
$FullyQualifiedFolder = Join-Path -Path $tmpDirectory -ChildPath $testfolder
$FullyQualifiedFile = Join-Path -Path $TestDrive -ChildPath $testfile
$TestFilePath = Join-Path -Path $TestDrive -ChildPath $testlink
$developerMode = (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock -ErrorAction SilentlyContinue).AllowDevelopmentWithoutDevLicense -eq 1
}
It "Should error correctly when failing to create a symbolic link" -Skip:(Test-IsRoot) {
# This test expects that /sbin exists but is not writable by the user
{ New-Item -ItemType SymbolicLink -Path "/sbin/powershell-test" -Target $FullyQualifiedFolder -ErrorAction Stop } |
AfterEach {
Remove-Item -Path $testFilePath -Force -ErrorAction SilentlyContinue
}
It "Should error correctly when failing to create a symbolic link and not in developer mode" -Skip:(!$IsWindows -or $developerMode -or (Test-IsElevated)) {
{ New-Item -ItemType SymbolicLink -Path $TestFilePath -Target $FullyQualifiedFile -ErrorAction Stop } |
Should -Throw -ErrorId "NewItemSymbolicLinkElevationRequired,Microsoft.PowerShell.Commands.NewItemCommand"
$TestFilePath | Should -Not -Exist
}
It "Should succeed to create a symbolic link without elevation and in developer mode" -Skip:(!$IsWindows -or !$developerMode -or (Test-IsElevated)) {
{ New-Item -ItemType SymbolicLink -Path $TestFilePath -Target $FullyQualifiedFile -ErrorAction Stop } | Should -Not -Throw
$TestFilePath | Should -Exist
}
}