Merge branch 'release/2.1' into dev

This commit is contained in:
Ajay Bhargav Baaskaran 2018-04-04 10:20:25 -07:00
commit 8e1475c5bc
6 changed files with 68 additions and 25 deletions

View File

@ -72,6 +72,7 @@
ToolAssembly="$(_RazorToolAssembly)"
UseServer="$(UseRazorBuildServer)"
ForceServer="$(_RazorForceBuildServer)"
SuppressCurrentUserOnlyPipeOptions="$(_RazorSuppressCurrentUserOnlyPipeOptions)"
PipeName="$(_RazorBuildServerPipeName)"
Version="$(RazorLangVersion)"
Configuration="@(ResolvedRazorConfiguration)"
@ -123,6 +124,7 @@
ToolAssembly="$(_RazorToolAssembly)"
UseServer="$(UseRazorBuildServer)"
ForceServer="$(_RazorForceBuildServer)"
SuppressCurrentUserOnlyPipeOptions="$(_RazorSuppressCurrentUserOnlyPipeOptions)"
PipeName="$(_RazorBuildServerPipeName)"
Version="$(RazorLangVersion)"
Configuration="@(ResolvedRazorConfiguration)"

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using Microsoft.AspNetCore.Razor.Tools;
using Microsoft.Build.Framework;
@ -15,6 +16,10 @@ namespace Microsoft.AspNetCore.Razor.Tasks
{
public abstract class DotNetToolTask : ToolTask
{
// From https://github.com/dotnet/corefx/blob/29cd6a0b0ac2993cee23ebaf36ca3d4bce6dd75f/src/System.IO.Pipes/ref/System.IO.Pipes.cs#L93.
// Using the enum value directly as this option is not available in netstandard.
private const PipeOptions PipeOptionCurrentUserOnly = (PipeOptions)536870912;
private CancellationTokenSource _razorServerCts;
public bool Debug { get; set; }
@ -29,6 +34,10 @@ namespace Microsoft.AspNetCore.Razor.Tasks
// Specifies whether we should fallback to in-process execution if server execution fails.
public bool ForceServer { get; set; }
// Specifies whether server execution is allowed when PipeOptions.CurrentUserOnly is not available.
// For testing purposes only.
public bool SuppressCurrentUserOnlyPipeOptions { get; set; }
public string PipeName { get; set; }
protected override string ToolName => "dotnet";
@ -115,6 +124,15 @@ namespace Microsoft.AspNetCore.Razor.Tasks
string commandLineCommands,
out int result)
{
if (!SuppressCurrentUserOnlyPipeOptions && !Enum.IsDefined(typeof(PipeOptions), PipeOptionCurrentUserOnly))
{
// For security reasons, we don't want to spin up a server that doesn't
// restrict requests only to the current user.
result = -1;
return false;
}
Log.LogMessage(StandardOutputLoggingImportance, "Server execution started.");
using (_razorServerCts = new CancellationTokenSource())
{

View File

@ -13,6 +13,12 @@ namespace Microsoft.AspNetCore.Razor.Tools
{
private static int counter;
// From https://github.com/dotnet/corefx/blob/29cd6a0b0ac2993cee23ebaf36ca3d4bce6dd75f/src/System.IO.Pipes/ref/System.IO.Pipes.cs#L93.
// Using the enum value directly as this option is not available in netstandard.
private const PipeOptions PipeOptionCurrentUserOnly = (PipeOptions)536870912;
private static readonly PipeOptions _pipeOptions = GetPipeOptions();
public abstract Stream Stream { get; }
public abstract string Identifier { get; }
@ -40,7 +46,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
// The NamedPipeClientStream class handles the "\\.\pipe\" part for us.
ServerLogger.Log("Attempt to open named pipe '{0}'", pipeName);
var stream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
var stream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, _pipeOptions);
cancellationToken.ThrowIfCancellationRequested();
ServerLogger.Log("Attempt to connect named pipe '{0}'", pipeName);
@ -74,6 +80,18 @@ namespace Microsoft.AspNetCore.Razor.Tools
}
}
private static PipeOptions GetPipeOptions()
{
var options = PipeOptions.Asynchronous;
if (Enum.IsDefined(typeof(PipeOptions), PipeOptionCurrentUserOnly))
{
return options | PipeOptionCurrentUserOnly;
}
return options;
}
private static string GetNextIdentifier()
{
var id = Interlocked.Increment(ref counter);

View File

@ -13,9 +13,6 @@ namespace Microsoft.AspNetCore.Razor.Tools
// https://github.com/dotnet/roslyn/blob/14aed138a01c448143b9acf0fe77a662e3dfe2f4/src/Compilers/Server/VBCSCompiler/NamedPipeClientConnection.cs#L17
internal abstract class ConnectionHost
{
// Size of the buffers to use: 64K
private const int PipeBufferSize = 0x10000;
private static int counter;
public abstract Task<Connection> WaitForConnectionAsync(CancellationToken cancellationToken);
@ -33,6 +30,15 @@ namespace Microsoft.AspNetCore.Razor.Tools
private class NamedPipeConnectionHost : ConnectionHost
{
// Size of the buffers to use: 64K
private const int PipeBufferSize = 0x10000;
// From https://github.com/dotnet/corefx/blob/29cd6a0b0ac2993cee23ebaf36ca3d4bce6dd75f/src/System.IO.Pipes/ref/System.IO.Pipes.cs#L93.
// Using the enum value directly as this option is not available in netstandard.
private const PipeOptions PipeOptionCurrentUserOnly = (PipeOptions)536870912;
private static readonly PipeOptions _pipeOptions = GetPipeOptions();
public NamedPipeConnectionHost(string pipeName)
{
PipeName = pipeName;
@ -45,15 +51,12 @@ namespace Microsoft.AspNetCore.Razor.Tools
// Create the pipe and begin waiting for a connection. This doesn't block, but could fail
// in certain circumstances, such as the OS refusing to create the pipe for some reason
// or the pipe was disconnected before we starting listening.
//
// Also, note that we're waiting on CoreFx to implement some security features for us.
// https://github.com/dotnet/corefx/issues/24040
var pipeStream = new NamedPipeServerStream(
PipeName,
PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances, // Maximum connections.
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous | PipeOptions.WriteThrough,
_pipeOptions,
PipeBufferSize, // Default input buffer
PipeBufferSize);// Default output buffer
@ -70,6 +73,18 @@ namespace Microsoft.AspNetCore.Razor.Tools
pipeStream.Close();
throw new Exception("Insufficient resources to process new connection.");
}
private static PipeOptions GetPipeOptions()
{
var options = PipeOptions.Asynchronous | PipeOptions.WriteThrough;
if (Enum.IsDefined(typeof(PipeOptions), PipeOptionCurrentUserOnly))
{
return options | PipeOptionCurrentUserOnly;
}
return options;
}
}
private class NamedPipeConnection : Connection

View File

@ -3,7 +3,6 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
@ -13,14 +12,10 @@ namespace Microsoft.AspNetCore.Razor.Tools
{
// We want each pipe to unique and predictable based on the inputs of:
// - user (security)
// - elevation status (security)
// - path of tool on disk (version)
//
// This allows us to meet the security and version compat requirements just by selecting a pipe name.
//
// https://github.com/dotnet/corefx/issues/25427 will actually enforce the security, but we still
// want these guarantees when we try to connect so we can expect it to succeed.
//
// This is similar to (and based on) the code used by Roslyn in VBCSCompiler:
// https://github.com/dotnet/roslyn/blob/c273b6a9f19570a344c274ae89185b3a2b64d93d/src/Compilers/Shared/BuildServerConnection.cs#L528
public static string ComputeDefault(string toolDirectory = null)
@ -36,24 +31,14 @@ namespace Microsoft.AspNetCore.Razor.Tools
// That would be a pretty wacky bug to try and unravel.
var baseName = ComputeBaseName("Razor:" + toolDirectory);
// Prefix with username and elevation
var isAdmin = false;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
#if WINDOWS_HACK_LOL
var currentIdentity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(currentIdentity);
isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
#endif
}
// Prefix with username
var userName = Environment.UserName;
if (userName == null)
{
return null;
}
return $"{userName}.{(isAdmin ? 'T' : 'F')}.{baseName}";
return $"{userName}.{baseName}";
}
private static string ComputeBaseName(string baseDirectory)

View File

@ -72,6 +72,11 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
if (!suppressBuildServer)
{
buildArgumentList.Add($"/p:_RazorBuildServerPipeName={buildServerPipeName ?? BuildServer.PipeName}");
// The build server will not be used in netcoreapp2.0 because PipeOptions.CurrentUserOnly is not available.
// But we still want to make sure to run the tests on the server. So suppress that check.
// This can be removed once https://github.com/aspnet/Razor/issues/2237 is done.
buildArgumentList.Add($"/p:_RazorSuppressCurrentUserOnlyPipeOptions=true");
}
buildArgumentList.Add($"/t:{target} /p:Configuration={Configuration} {args}");