diff --git a/src/gsudo/Commands/ServiceCommand.cs b/src/gsudo/Commands/ServiceCommand.cs index 94df98cc..da6c0d4b 100644 --- a/src/gsudo/Commands/ServiceCommand.cs +++ b/src/gsudo/Commands/ServiceCommand.cs @@ -5,6 +5,7 @@ using gsudo.Rpc; using gsudo.ProcessHosts; using System.Runtime.Serialization.Formatters.Binary; +using gsudo.Helpers; #if NETCOREAPP using System.Text.Json; #endif @@ -75,6 +76,11 @@ private async Task AcceptConnection(Connection connection) IProcessHost applicationHost = CreateProcessHost(request); + if (!applicationHost.SupportsSimultaneousElevations && Settings.CacheMode.Value==Enums.CacheMode.Auto) + { + ServiceHelper.StartElevatedService(AllowedPid, CacheDuration, AllowedSid); + } + if (!string.IsNullOrEmpty(request.Prompt)) Environment.SetEnvironmentVariable("PROMPT", Environment.ExpandEnvironmentVariables(request.Prompt)); diff --git a/src/gsudo/Helpers/ArgumentsHelper.cs b/src/gsudo/Helpers/ArgumentsHelper.cs index eb6cf551..3e1ac30e 100644 --- a/src/gsudo/Helpers/ArgumentsHelper.cs +++ b/src/gsudo/Helpers/ArgumentsHelper.cs @@ -11,7 +11,7 @@ namespace gsudo.Helpers { public static class ArgumentsHelper { - static readonly HashSet CmdCommands = new HashSet(StringComparer.OrdinalIgnoreCase) { "ASSOC", "ATTRIB", "BREAK", "BCDEDIT", "CACLS", "CALL", "CD", "CHCP", "CHDIR", "CHKDSK", "CHKNTFS", "CLS", /*"CMD",*/ "COLOR", "COMP", "COMPACT", "CONVERT", "COPY", "DATE", "DEL", "DIR", "DISKPART", "DOSKEY", "DRIVERQUERY", "ECHO", "ENDLOCAL", "ERASE", "EXIT", "FC", "FIND", "FINDSTR", "FOR", "FORMAT", "FSUTIL", "FTYPE", "GOTO", "GPRESULT", "GRAFTABL", "HELP", "ICACLS", "IF", "LABEL", "MD", "MKDIR", "MKLINK", "MODE", "MORE", "MOVE", "OPENFILES", "PATH", "PAUSE", "POPD", "PRINT", "PROMPT", "PUSHD", "RD", "RECOVER", "REM", "REN", "RENAME", "REPLACE", "RMDIR", "ROBOCOPY", "SET", "SETLOCAL", "SC", "SCHTASKS", "SHIFT", "SHUTDOWN", "SORT", "START", "SUBST", "SYSTEMINFO", "TASKLIST", "TASKKILL", "TIME", "TITLE", "TREE", "TYPE", "VER", "VERIFY", "VOL", "XCOPY", "WMIC" }; + static readonly HashSet CmdCommands = new HashSet(StringComparer.OrdinalIgnoreCase) { "ASSOC", "BREAK", "CALL", "CD", "CHDIR", "CLS", "COLOR", "COPY", "DATE", "DEL", "DIR", "ECHO", "ENDLOCAL", "ERASE", "EXIT", "FOR", "FTYPE", "GOTO", "IF", "MD", "MKDIR", "MKLINK", "MOVE", "PATH", "PAUSE", "POPD", "PROMPT", "PUSHD", "RD", "REM", "REN", "RENAME", "RMDIR", "SET", "SETLOCAL", "SHIFT", "START", "TIME", "TITLE", "TYPE", "VER", "VERIFY", "VOL" }; static readonly HashSet CreateProcessSupportedExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { ".CMD", ".EXE", ".BAT", ".COM" }; internal static string[] AugmentCommand(string[] args) @@ -151,7 +151,7 @@ Running ./gsudo {command} should elevate the powershell command. } else { - if (CmdCommands.Contains(args[0])) + if (CmdCommands.Contains(args[0])) // We want cmd commands to be run with CMD /c, not search for .EXE return new string[] { currentShellExeName, "/c" } .Concat(args).ToArray(); diff --git a/src/gsudo/Helpers/ServiceHelper.cs b/src/gsudo/Helpers/ServiceHelper.cs index 8398bf51..1ce9606e 100644 --- a/src/gsudo/Helpers/ServiceHelper.cs +++ b/src/gsudo/Helpers/ServiceHelper.cs @@ -54,9 +54,9 @@ internal static async Task ConnectStartElevatedService() return connection; } - internal static bool StartElevatedService(int? allowedPid, TimeSpan? cacheDuration = null) + internal static bool StartElevatedService(int? allowedPid, TimeSpan? cacheDuration = null, string allowedSid=null) { - var callingSid = System.Security.Principal.WindowsIdentity.GetCurrent().User.Value; + var callingSid = allowedSid ?? System.Security.Principal.WindowsIdentity.GetCurrent().User.Value; var callingPid = allowedPid ?? Process.GetCurrentProcess().GetCacheableRootProcessId(); string verb; @@ -112,50 +112,5 @@ internal static bool StartElevatedService(int? allowedPid, TimeSpan? cacheDurati Logger.Instance.Log("Elevated instance started.", LogLevel.Debug); return true; } - - internal static bool StartSingleUseElevatedService(int callingPid) - { - var @params = string.Empty; - - if (InputArguments.Debug) @params = "--debug "; - if (InputArguments.IntegrityLevel.HasValue) @params += $"-i {InputArguments.IntegrityLevel.Value} "; - if (InputArguments.RunAsSystem) @params += "-s "; - - bool isAdmin = ProcessHelper.IsHighIntegrity(); - string ownExe = ProcessHelper.GetOwnExeName(); - - string commandLine; - commandLine = $"{@params}gsudoelevate --pid {callingPid}"; - - Process p; - - try - { - p = ProcessFactory.StartElevatedDetached(ownExe, commandLine, !InputArguments.Debug); - } - catch (System.ComponentModel.Win32Exception ex) - { - Logger.Instance.Log(ex.Message, LogLevel.Error); - return false; - } - - if (p == null) - { - Logger.Instance.Log("Failed to start elevated instance.", LogLevel.Error); - return false; - } - - Logger.Instance.Log("Elevated instance started.", LogLevel.Debug); - - p.WaitForExit(); - - if (p.ExitCode == 0) - { - return true; - } - - return false; - } - } } diff --git a/src/gsudo/ProcessHosts/AttachedConsoleHost.cs b/src/gsudo/ProcessHosts/AttachedConsoleHost.cs index 99d67bd5..0524fb62 100644 --- a/src/gsudo/ProcessHosts/AttachedConsoleHost.cs +++ b/src/gsudo/ProcessHosts/AttachedConsoleHost.cs @@ -14,6 +14,8 @@ namespace gsudo.ProcessHosts // This mode is not enabled unless you use --attached. class AttachedConsoleHost : IProcessHost { + public bool SupportsSimultaneousElevations { get; } = false; + public async Task Start(Connection connection, ElevationRequest elevationRequest) { var exitCode = 0; diff --git a/src/gsudo/ProcessHosts/IProcessHost.cs b/src/gsudo/ProcessHosts/IProcessHost.cs index 51b2a740..707c126f 100644 --- a/src/gsudo/ProcessHosts/IProcessHost.cs +++ b/src/gsudo/ProcessHosts/IProcessHost.cs @@ -14,5 +14,7 @@ namespace gsudo.ProcessHosts interface IProcessHost { Task Start(Connection connection, ElevationRequest elevationRequest); + + bool SupportsSimultaneousElevations { get; } } } diff --git a/src/gsudo/ProcessHosts/NewWindowProcessHost.cs b/src/gsudo/ProcessHosts/NewWindowProcessHost.cs index 621282a1..c5360f8c 100644 --- a/src/gsudo/ProcessHosts/NewWindowProcessHost.cs +++ b/src/gsudo/ProcessHosts/NewWindowProcessHost.cs @@ -14,6 +14,8 @@ class NewWindowProcessHost : IProcessHost { private Process process; + public bool SupportsSimultaneousElevations { get; } = true; + public async Task Start(Connection connection, ElevationRequest request) { try diff --git a/src/gsudo/ProcessHosts/PipedProcessHost.cs b/src/gsudo/ProcessHosts/PipedProcessHost.cs index 590636a5..818b98b4 100644 --- a/src/gsudo/ProcessHosts/PipedProcessHost.cs +++ b/src/gsudo/ProcessHosts/PipedProcessHost.cs @@ -23,6 +23,8 @@ class PipedProcessHost : IProcessHost private ElevationRequest _request; private bool _errorStreamActive; + public bool SupportsSimultaneousElevations { get; } = false; + public async Task Start(Connection connection, ElevationRequest request) { Native.ConsoleApi.SetConsoleCtrlHandler(ConsoleHelper.IgnoreConsoleCancelKeyPress, true); diff --git a/src/gsudo/ProcessHosts/TokenSwitchHost.cs b/src/gsudo/ProcessHosts/TokenSwitchHost.cs index 97fb2e83..c1bdc0c6 100644 --- a/src/gsudo/ProcessHosts/TokenSwitchHost.cs +++ b/src/gsudo/ProcessHosts/TokenSwitchHost.cs @@ -12,6 +12,8 @@ namespace gsudo.ProcessHosts /// class TokenSwitchHost : IProcessHost { + public bool SupportsSimultaneousElevations { get; } = true; + public async Task Start(Connection connection, ElevationRequest elevationRequest) { if (Settings.SecurityEnforceUacIsolation && !elevationRequest.NewWindow) @@ -22,7 +24,6 @@ public async Task Start(Connection connection, ElevationRequest elevationRequest TokenSwitcher.ReplaceProcessToken(elevationRequest); await connection.ControlStream.WriteAsync(Constants.TOKEN_SUCCESS).ConfigureAwait(false); - connection.DisconnectedWaitHandle.WaitOne(); // Wait until client receives the response before closing. } catch (Exception ex) { diff --git a/src/gsudo/ProcessHosts/VTProcessHost.cs b/src/gsudo/ProcessHosts/VTProcessHost.cs index 77e29569..70e4f371 100644 --- a/src/gsudo/ProcessHosts/VTProcessHost.cs +++ b/src/gsudo/ProcessHosts/VTProcessHost.cs @@ -23,6 +23,8 @@ class VTProcessHost : IProcessHost private Connection _connection; private static Encoding PseudoConsoleEncoding = new System.Text.UTF8Encoding(false); + public bool SupportsSimultaneousElevations { get; } = false; + public async Task Start(Connection connection, ElevationRequest request) { if (Settings.SecurityEnforceUacIsolation) diff --git a/src/gsudo/ProcessRenderers/VTClientRenderer.cs b/src/gsudo/ProcessRenderers/VTClientRenderer.cs index 85bb55b5..13739f57 100644 --- a/src/gsudo/ProcessRenderers/VTClientRenderer.cs +++ b/src/gsudo/ProcessRenderers/VTClientRenderer.cs @@ -157,7 +157,7 @@ private async Task WriteToConsole(string s) int left, top; ConsoleHelper.GetConsoleInfo(out _, out _, out left, out top); - await _connection.DataStream.WriteAsync($"\x001B[{top};{left}R").ConfigureAwait(false); + await _connection.DataStream.WriteAsync($"\x001B[{top+1};{left}R").ConfigureAwait(false); return; } diff --git a/src/gsudo/Rpc/NamedPipeClient.cs b/src/gsudo/Rpc/NamedPipeClient.cs index e9bb2c42..44c1cc30 100644 --- a/src/gsudo/Rpc/NamedPipeClient.cs +++ b/src/gsudo/Rpc/NamedPipeClient.cs @@ -32,7 +32,8 @@ public async Task Connect(int? clientPid, bool failFast) else { var callerProcessId = Process.GetCurrentProcess().Id; - while (callerProcessId > 0) + int maxRecursion = 20; + while (callerProcessId > 0 && maxRecursion-- > 0) { callerProcessId = ProcessHelper.GetParentProcessId(callerProcessId); pipeName = NamedPipeNameFactory.GetPipeName(user, callerProcessId); @@ -79,7 +80,8 @@ public static bool IsServiceAvailable(int? pid = null, string sid = null) pid = pid ?? ProcessHelper.GetParentProcessId(Process.GetCurrentProcess().Id); sid = sid ?? System.Security.Principal.WindowsIdentity.GetCurrent().User.Value; - while (pid.Value > 0) + int maxRecursion = 20; + while (pid.Value > 0 && maxRecursion-- > 0) { pipeName = NamedPipeNameFactory.GetPipeName(sid, pid.Value); // Does the pipe exists? diff --git a/src/gsudo/Rpc/NamedPipeServer.cs b/src/gsudo/Rpc/NamedPipeServer.cs index 22e617ff..9cb49461 100644 --- a/src/gsudo/Rpc/NamedPipeServer.cs +++ b/src/gsudo/Rpc/NamedPipeServer.cs @@ -57,11 +57,21 @@ public async Task Listen() { var ps = new PipeSecurity(); + // _allowedSid is the input argument saying who invoked this elevated instance. + // Needs access to connect to this pipe. ps.AddAccessRule(new PipeAccessRule( new SecurityIdentifier(_allowedSid), + PipeAccessRights.ReadWrite, + AccessControlType.Allow)); + + // WindowsIdentity.GetCurrent().User is our current elevated user. + // For UAC in admin-approval mode, it is the same as _allowedSid + // But when entering credentials (on the UAC Popup), it is not. + ps.AddAccessRule(new PipeAccessRule( + WindowsIdentity.GetCurrent().User, PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow)); - + var networkSid = new SecurityIdentifier("S-1-5-2"); // deny remote connections. ps.AddAccessRule(new PipeAccessRule(