THRIFT-5391 Named pipes transport hardening

Client: netstd
Patch: Jens Geyer

This closes #2367
This commit is contained in:
Jens Geyer 2021-04-02 12:18:15 +02:00
parent 20a86d68e9
commit ef0cb01abe
3 changed files with 40 additions and 13 deletions

View File

@ -17,6 +17,7 @@
using System;
using System.IO.Pipes;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
@ -39,7 +40,7 @@ namespace Thrift.Transport.Client
var serverName = string.IsNullOrWhiteSpace(server) ? server : ".";
ConnectTimeout = (timeout > 0) ? timeout : Timeout.Infinite;
PipeStream = new NamedPipeClientStream(serverName, pipe, PipeDirection.InOut, PipeOptions.None);
PipeStream = new NamedPipeClientStream(serverName, pipe, PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.Anonymous);
}
public override bool IsOpen => PipeStream != null && PipeStream.IsConnected;

View File

@ -27,6 +27,12 @@ using System.Security.Principal;
namespace Thrift.Transport.Server
{
[Flags]
public enum NamedPipeClientFlags {
None = 0x00,
OnlyLocalClients = 0x01
};
// ReSharper disable once InconsistentNaming
public class TNamedPipeServerTransport : TServerTransport
{
@ -37,11 +43,21 @@ namespace Thrift.Transport.Server
private bool _asyncMode = true;
private volatile bool _isPending = true;
private NamedPipeServerStream _stream = null;
private readonly bool _onlyLocalClients = false; // compatibility default
public TNamedPipeServerTransport(string pipeAddress, TConfiguration config, NamedPipeClientFlags flags)
: base(config)
{
_pipeAddress = pipeAddress;
_onlyLocalClients = flags.HasFlag(NamedPipeClientFlags.OnlyLocalClients);
}
[Obsolete("This CTOR is deprecated, please use the other one instead.")]
public TNamedPipeServerTransport(string pipeAddress, TConfiguration config)
: base(config)
{
_pipeAddress = pipeAddress;
_onlyLocalClients = false;
}
public override bool IsOpen() {
@ -96,7 +112,7 @@ namespace Thrift.Transport.Server
try
{
var handle = CreatePipeNative(_pipeAddress, inbuf, outbuf);
var handle = CreatePipeNative(_pipeAddress, inbuf, outbuf, _onlyLocalClients);
if ((handle != null) && (!handle.IsInvalid))
{
_stream = new NamedPipeServerStream(PipeDirection.InOut, _asyncMode, false, handle);
@ -149,14 +165,14 @@ namespace Thrift.Transport.Server
// Workaround: create the pipe via API call
// we have to do it this way, since NamedPipeServerStream() for netstd still lacks a few CTORs
// we have to do it this way, since NamedPipeServerStream() for netstd still lacks a few CTORs and/or arguments
// and _stream.SetAccessControl(pipesec); only keeps throwing ACCESS_DENIED errors at us
// References:
// - https://github.com/dotnet/corefx/issues/30170 (closed, continued in 31190)
// - https://github.com/dotnet/corefx/issues/31190 System.IO.Pipes.AccessControl package does not work
// - https://github.com/dotnet/corefx/issues/24040 NamedPipeServerStream: Provide support for WRITE_DAC
// - https://github.com/dotnet/corefx/issues/34400 Have a mechanism for lower privileged user to connect to a privileged user's pipe
private static SafePipeHandle CreatePipeNative(string name, int inbuf, int outbuf)
private static SafePipeHandle CreatePipeNative(string name, int inbuf, int outbuf, bool OnlyLocalClients)
{
if (! RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return null; // Windows only
@ -187,18 +203,25 @@ namespace Thrift.Transport.Server
}
// a bunch of constants we will need shortly
const int PIPE_ACCESS_DUPLEX = 0x00000003;
const int FILE_FLAG_OVERLAPPED = 0x40000000;
const int WRITE_DAC = 0x00040000;
const int PIPE_TYPE_BYTE = 0x00000000;
const int PIPE_READMODE_BYTE = 0x00000000;
const int PIPE_UNLIMITED_INSTANCES = 255;
const uint PIPE_ACCESS_DUPLEX = 0x00000003;
const uint FILE_FLAG_OVERLAPPED = 0x40000000;
const uint WRITE_DAC = 0x00040000;
const uint PIPE_TYPE_BYTE = 0x00000000;
const uint PIPE_READMODE_BYTE = 0x00000000;
const uint PIPE_UNLIMITED_INSTANCES = 255;
const uint PIPE_ACCEPT_REMOTE_CLIENTS = 0x00000000; // Connections from remote clients can be accepted and checked against the security descriptor for the pipe.
const uint PIPE_REJECT_REMOTE_CLIENTS = 0x00000008; // Connections from remote clients are automatically rejected.
// any extra flags we want to add
uint dwPipeModeXtra
= (OnlyLocalClients ? PIPE_REJECT_REMOTE_CLIENTS : PIPE_ACCEPT_REMOTE_CLIENTS)
;
// create the pipe via API call
var rawHandle = CreateNamedPipe(
@"\\.\pipe\" + name,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | dwPipeModeXtra,
PIPE_UNLIMITED_INSTANCES, (uint)inbuf, (uint)outbuf,
5 * 1000,
secAttrs

View File

@ -149,7 +149,10 @@ namespace ThriftTest
public class TestServer
{
public static int _clientID = -1;
#pragma warning disable CA2211
public static int _clientID = -1; // use with Interlocked only!
#pragma warning restore CA2211
private static readonly TConfiguration Configuration = null; // or new TConfiguration() if needed
public delegate void TestLogDelegate(string msg, params object[] values);
@ -556,7 +559,7 @@ namespace ThriftTest
{
case TransportChoice.NamedPipe:
Debug.Assert(param.pipe != null);
trans = new TNamedPipeServerTransport(param.pipe, Configuration);
trans = new TNamedPipeServerTransport(param.pipe, Configuration, NamedPipeClientFlags.OnlyLocalClients);
break;