Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to set permissions to the listening named pipe #15

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ The `address1` can accept `STDIO`, `TCP-LISTEN`, `TCP`, `NPIPE`, `NPIPE-LISTEN`,

The `address2` can accept `STDIO`, `TCP`, `NPIPE`, `EXEC`, `WSL`, `UNIX`, `HVSOCK`, `SP` socket types.

`NPIPE-LISTEN` supports the `ACL=AllowCurrentUser` parameter, in this case, no other user that is connected to the machine can read / write from / to the pipe. The default is `ACL=AllowEveryone`.

## Examples

* It can bridge standard input/output and tcp connection to address **127.0.0.1** on port **80**.
Expand Down
38 changes: 30 additions & 8 deletions Tests/NamedPipeListenPiperInfoTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace APPTest;
public class NamedPipeListenPiperInfoTest
{
[TestCase("NPIPE-LISTEN:fooPipe")]
[TestCase("NPIPE-LISTEN:fooPipe,ACL=AllowEveryone")]
[TestCase("NPIPE-LISTEN:fooPipe,ACL=AllowCurrentUser")]
public void VaildInputParseTest(string input)
{
var element = AddressElement.TryParse(input);
Expand All @@ -18,22 +20,42 @@ public void CaseInsensitiveValidInputParseTest(string input)
var element = AddressElement.TryParse(input);
Assert.NotNull(Firejox.App.WinSocat.NamedPipeListenPiperInfo.TryParse(element));
}

[TestCase("STDIO")]
[TestCase("TCP:127.0.0.1:80")]
[TestCase("TCP-LISTEN:127.0.0.1:80")]
[TestCase("NPIPE:fooServer:barPipe")]
[TestCase("NPIPE:fooServer:barPipe")]
[TestCase("NPIPE:fooServer:barPipe")]
[TestCase(@"EXEC:'C:\Foo.exe bar'")]
public void InvalidInputParseTest(string input)
{
var element = AddressElement.TryParse(input);
Assert.Null(Firejox.App.WinSocat.NamedPipeListenPiperInfo.TryParse(element));
}

[TestCase("NPIPE-LISTEN:fooPipe", ExpectedResult = "fooPipe")]
public string PipePatternMatchTest(string input)
}

[TestCase("NPIPE-LISTEN:fooPipe")]
[TestCase("NPIPE-LISTEN:fooPipe,ACL=AllowEveryone")]
[TestCase("NPIPE-LISTEN:fooPipe,ACL=AllowCurrentUser")]
public void PipePatternMatchTest(string input)
{
var element = AddressElement.TryParse(input);
return Firejox.App.WinSocat.NamedPipeListenPiperInfo.TryParse(element).PipeName;
// Case 1 - Default ACL
var element = AddressElement.TryParse("NPIPE-LISTEN:fooPipe");
var parsed = Firejox.App.WinSocat.NamedPipeListenPiperInfo.TryParse(element);
Assert.That(parsed.PipeName, Is.EqualTo("fooPipe"));
Assert.That(parsed.ACL, Is.EqualTo("AllowEveryone"));

// Case 2 - AllowEveryone ACL
element = AddressElement.TryParse("NPIPE-LISTEN:fooPipe,ACL=AllowEveryone");
parsed = Firejox.App.WinSocat.NamedPipeListenPiperInfo.TryParse(element);
Assert.That(parsed.PipeName, Is.EqualTo("fooPipe"));
Assert.That(parsed.ACL, Is.EqualTo("AllowEveryone"));

// Case 3 - AllowCurrentUser ACL
element = AddressElement.TryParse("NPIPE-LISTEN:fooPipe,ACL=AllowCurrentUser");
parsed = Firejox.App.WinSocat.NamedPipeListenPiperInfo.TryParse(element);
Assert.That(parsed.PipeName, Is.EqualTo("fooPipe"));
Assert.That(parsed.ACL, Is.EqualTo("AllowCurrentUser"));

}
}
}
2 changes: 1 addition & 1 deletion Tests/NamedPipeStreamPiperInfoTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void CaseInsensitiveValidInputParseTest(string input)
[TestCase("STDIO")]
[TestCase("TCP:127.0.0.1:80")]
[TestCase("TCP-LISTEN:127.0.0.1:80")]
[TestCase("NPIPE-LISTEN:fooPipe")]
[TestCase("NPIPE-LISTEN:fooPipe")]
[TestCase(@"EXEC:'C:\Foo.exe bar'")]
public void InvalidInputParseTest(string input)
{
Expand Down
37 changes: 32 additions & 5 deletions winsocat/NamedPipe.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
using System.IO.Pipes;
using System.Security.Principal;

namespace Firejox.App.WinSocat;

public class NamedPipeStreamPiperInfo
{
private readonly string _serverName;
private readonly string _pipeName;
private readonly string _acl;

public string ServerName => _serverName;
public string PipeName => _pipeName;
public string ACL => _acl;

public NamedPipeStreamPiperInfo(string serverName, string pipeName)
public NamedPipeStreamPiperInfo(string serverName, string pipeName, string acl)
{
_serverName = serverName;
_pipeName = pipeName;
_acl = acl;
}
public static NamedPipeStreamPiperInfo TryParse(AddressElement element)
{
Expand All @@ -31,24 +35,28 @@ public static NamedPipeStreamPiperInfo TryParse(AddressElement element)

pipeName = element.Address.Substring(sepIndex + 1);

return new NamedPipeStreamPiperInfo(serverName, pipeName);
return new NamedPipeStreamPiperInfo(serverName, pipeName, element.Options.GetValueOrDefault("ACL", "AllowEveryone"));
}
}

public class NamedPipeListenPiperInfo
{
private readonly string _pipeName;
private readonly string _acl = "AllowEveryone";

public string PipeName => _pipeName;
public string ACL => _acl;

public NamedPipeListenPiperInfo(string pipeName)
public NamedPipeListenPiperInfo(string pipeName, string acl = "AllowEveryone")
{
_pipeName = pipeName;
_acl = acl;
}

public static NamedPipeListenPiperInfo TryParse(AddressElement element)
{
if (element.Tag.Equals("NPIPE-LISTEN", StringComparison.OrdinalIgnoreCase))
return new NamedPipeListenPiperInfo(element.Address);
return new NamedPipeListenPiperInfo(element.Address, element.Options.GetValueOrDefault("ACL", "AllowEveryone"));

return null!;
}
Expand Down Expand Up @@ -135,6 +143,24 @@ public NamedPipeListenPiper(NamedPipeListenPiperInfo info)
_closed = false;
}

public void SetPermissions(NamedPipeServerStream _serverStream)
{
if (OperatingSystem.IsWindows())
{
// Only allow current user
if (_info.ACL.Equals("AllowCurrentUser", StringComparison.OrdinalIgnoreCase))
{
var securityIdentifier = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
var pipeAcl = new PipeAccessRule(securityIdentifier,
PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance,
System.Security.AccessControl.AccessControlType.Allow);
var pipeSecurity = new PipeSecurity();
pipeSecurity.AddAccessRule(pipeAcl);
_serverStream.SetAccessControl(pipeSecurity);
}
}
}

public IPiper NewIncomingPiper()
{
_serverStream = new NamedPipeServerStream(
Expand All @@ -143,8 +169,8 @@ public IPiper NewIncomingPiper()
-1,
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous);
SetPermissions(_serverStream);
_serverStream.WaitForConnection();

var tmpServerStream = _serverStream;
_serverStream = null;
return new StreamPiper(tmpServerStream);
Expand All @@ -161,6 +187,7 @@ public async Task<IPiper> NewIncomingPiperAsync()
-1,
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous);
SetPermissions(_serverStream);
await _serverStream.WaitForConnectionAsync();

var tmpServerStream = _serverStream;
Expand Down