Skip to content

Commit

Permalink
Added support for scoop shims: Auth checking now checking shims/junti…
Browse files Browse the repository at this point in the history
…ons/symlinks, and comparing with allowed PIDs with parent and grandparent processes.

Version bumped to v0.4.1
Added scoop package draft
  • Loading branch information
Gerardo Grignoli committed Dec 18, 2019
1 parent 3d1daff commit e2d4f98
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 23 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ Read or write a user setting

## Known issues

- Please report issues in the [Issues](https://github.com/gerardog/gsudo/issues) section.
- Feel free to contact me at gerardog @at@ gmail.com
- This project is a work in progress. Many improvements in the [backlog](backlog.md).
- You can use either `gsudo` or `sudo` alias, the one you like the most. But if you mix them you will get an 'authorization' error because gsudo forbids protects other proceses.

## FAQ

Expand Down
4 changes: 2 additions & 2 deletions src/gsudo/Commands/HelpCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ internal static void ShowHelp()
Console.WriteLine(" --debug Enable debug mode. (makes gsudo service window visible)");
Console.WriteLine(" -n | --new Starts the command in a new console with elevated rights and returns immediately.");
Console.WriteLine(" -w | --wait Force wait for the process to end.");
Console.WriteLine(" --raw Force use of a reduced terminal. Less features, More stable. (default=auto) ");
Console.WriteLine(" --vt Force use of full VT100 terminal emulator. (default=auto)");
Console.WriteLine(" --raw Force use of a reduced terminal.");
Console.WriteLine(" --vt Force use of full VT100 terminal emulator (experimental).");

return;
}
Expand Down
22 changes: 17 additions & 5 deletions src/gsudo/Commands/RunCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ public async Task<int> Execute()
else // IsAdministrator() == false, or build in Debug Mode
{
Logger.Instance.Log($"Using Console mode {elevationRequest.Mode}", LogLevel.Debug);
Logger.Instance.Log($"Caller ProcessId is {currentProcess.ParentProcessId()}", LogLevel.Debug);
var callingPid = GetCallingPid(currentProcess);
Logger.Instance.Log($"Caller ProcessId is {callingPid}", LogLevel.Debug);

var cmd = CommandToRun.FirstOrDefault();

Expand All @@ -111,7 +112,7 @@ public async Task<int> Execute()
{
try
{
connection = await rpcClient.Connect(elevationRequest, 300).ConfigureAwait(false);
connection = await rpcClient.Connect(elevationRequest, null, 300).ConfigureAwait(false);
}
catch (System.IO.IOException) { }
catch (TimeoutException) { }
Expand All @@ -124,23 +125,23 @@ public async Task<int> Execute()
{
// Start elevated service instance
Logger.Instance.Log("Elevating process...", LogLevel.Debug);
var callingPid = currentProcess.ParentProcessId();

var dbg = GlobalSettings.Debug ? "--debug " : string.Empty;
using (var process = ProcessFactory.StartElevatedDetached(currentProcess.MainModule.FileName, $"{dbg}gsudoservice {callingPid} {GlobalSettings.LogLevel}", !GlobalSettings.Debug))
{
Logger.Instance.Log("Elevated instance started.", LogLevel.Debug);
}

connection = await rpcClient.Connect(elevationRequest, 5000).ConfigureAwait(false);
connection = await rpcClient.Connect(elevationRequest, callingPid, 5000).ConfigureAwait(false);
}

if (connection == null) // service is not running or listening.
{
Logger.Instance.Log("Unable to connect to the elevated service.", LogLevel.Error);
return Constants.GSUDO_ERROR_EXITCODE;
}
WriteElevationRequest(elevationRequest, connection);

await WriteElevationRequest(elevationRequest, connection).ConfigureAwait(false);

await connection.ControlStream.FlushAsync().ConfigureAwait(false);
ConnectionKeepAliveThread.Start(connection);
Expand All @@ -157,6 +158,17 @@ public async Task<int> Execute()

}

private static int GetCallingPid(Process currentProcess)
{
var parent = currentProcess.ParentProcess();
while (parent.MainModule.FileName.In("sudo.exe", "gsudo.exe")) // naive shim detection
{
parent = parent.ParentProcess();
}

return parent.Id;
}

private async Task WriteElevationRequest(ElevationRequest elevationRequest, Connection connection)
{
var ms = new System.IO.MemoryStream();
Expand Down
15 changes: 13 additions & 2 deletions src/gsudo/Helpers/ProcessExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ public static Process ParentProcess(this Process process)
{
try
{
return Process.GetProcessById(process.ParentProcessId());
var parentPid = process.ParentProcessId();
if (parentPid == 0)
return null;
return Process.GetProcessById(parentPid);
}
catch
{
Expand All @@ -43,7 +46,15 @@ public static Process ParentProcess(this Process process)
public static int ParentProcessId(this Process process) // ExcludingShim
{
var parentId = ParentProcessId(process.Id);
var parent = Process.GetProcessById(parentId);
Process parent;
try
{
parent = Process.GetProcessById(parentId);
}
catch
{
return 0;
}

// workaround for chocolatey shim.
if (Path.GetFileName(parent.MainModule.FileName).In("gsudo.exe", "sudo.exe"))
Expand Down
2 changes: 1 addition & 1 deletion src/gsudo/Rpc/IRpcClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace gsudo.Rpc
{
internal interface IRpcClient
{
Task<Connection> Connect(ElevationRequest elevationRequest, int timeoutMilliseconds = 300);
Task<Connection> Connect(ElevationRequest elevationRequest, int? clientPid, int timeoutMilliseconds = 300);
}

class Connection : IDisposable
Expand Down
38 changes: 30 additions & 8 deletions src/gsudo/Rpc/NamedPipeClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using gsudo.Helpers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Linq;
Expand All @@ -10,24 +12,44 @@ namespace gsudo.Rpc
{
class NamedPipeClient : IRpcClient
{
public async Task<Connection> Connect(ElevationRequest elevationRequest, int timeoutMilliseconds)
public async Task<Connection> Connect(ElevationRequest elevationRequest, int? clientPid, int timeoutMilliseconds)
{
var pipeName = NamedPipeServer.GetPipeName();

var localServer = true;
var server = ".";

string pipeName = null;
string user = System.Security.Principal.WindowsIdentity.GetCurrent().User.Value;
NamedPipeClientStream dataPipe = null;
NamedPipeClientStream controlPipe = null;

try
{
// Does the pipe exists?
if (server == "." && !System.IO.Directory.EnumerateFiles(@"\\.\pipe\", pipeName).Any() && timeoutMilliseconds <= 300)
if (clientPid.HasValue)
{
pipeName = NamedPipeServer.GetPipeName(user, clientPid.Value);
if (!System.IO.Directory.EnumerateFiles(@"\\.\pipe\", pipeName).Any() && timeoutMilliseconds <= 300)
{
// fail fast without timeout.
return null;
}
}
else if (localServer)
{
// fail fast without timeout.
return null;
var callerProcess = Process.GetCurrentProcess().ParentProcess();
while (callerProcess != null)
{
pipeName = NamedPipeServer.GetPipeName(user, callerProcess.Id);
// Does the pipe exists?
if (!System.IO.Directory.EnumerateFiles(@"\\.\pipe\", pipeName).Any() && timeoutMilliseconds <= 300)
{
// try grandfather.
callerProcess = callerProcess.ParentProcess();
}
}
}

if (pipeName == null) return null;

dataPipe = new NamedPipeClientStream(server, pipeName, PipeDirection.InOut, PipeOptions.Asynchronous, System.Security.Principal.TokenImpersonationLevel.Impersonation, HandleInheritability.None);
await dataPipe.ConnectAsync(timeoutMilliseconds).ConfigureAwait(false);

Expand Down
9 changes: 6 additions & 3 deletions src/gsudo/Rpc/NamedPipeServer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using gsudo.Helpers;
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Security.AccessControl;
using System.Security.Principal;
Expand Down Expand Up @@ -79,7 +80,11 @@ public async Task Listen()
controlPipe.Disconnect();

// kill the server. I could also "break;" and keep listening, but better be on the safe side
#if DEBUG
continue;
#else
return;
#endif
}

ConnectionAccepted?.Invoke(this, connection);
Expand All @@ -96,12 +101,10 @@ public async Task Listen()
}
}



private bool IsAuthorized(int clientPid, int allowedPid)
{
var callingExe = SymbolicLinkSupport.ResolveSymbolicLink(Process.GetProcessById(clientPid).MainModule.FileName);
var allowedExe = Process.GetCurrentProcess().MainModule.FileName;
var allowedExe = SymbolicLinkSupport.ResolveSymbolicLink(Process.GetCurrentProcess().MainModule.FileName);
//
if (callingExe != allowedExe)
{
Expand Down
2 changes: 1 addition & 1 deletion src/gsudo/gsudo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ It is a sudo equivalent for Windows, with a similar user-experience as the origi
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<Version>0.4</Version>
<Version>0.4.1</Version>
<Copyright>2019 Gerardo Grignoli</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/gerardog/gsudo</PackageProjectUrl>
Expand Down
6 changes: 6 additions & 0 deletions src/scoop-package/gsudo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"version": "0.4",
"url": "https://github.com/gerardog/gsudo/releases/download/v0.4/gsudo.v0.4.zip",
"hash":"8D2BDA6B57FD30D8723ACC7207F270ED86F45178044F80546436C5022B0517C2",
"bin": [ "gsudo.exe",["gsudo.exe", "sudo"] ]
}

0 comments on commit e2d4f98

Please sign in to comment.