From 9b457b3c3d84a082f2267f794c384f1cad313365 Mon Sep 17 00:00:00 2001 From: Gerardo Grignoli Date: Fri, 25 Aug 2023 10:13:13 -0300 Subject: [PATCH 1/3] Fix connection error by proper null-checking --- src/gsudo/Rpc/NamedPipeClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gsudo/Rpc/NamedPipeClient.cs b/src/gsudo/Rpc/NamedPipeClient.cs index 3d69a515..ab9f9a1c 100644 --- a/src/gsudo/Rpc/NamedPipeClient.cs +++ b/src/gsudo/Rpc/NamedPipeClient.cs @@ -28,9 +28,9 @@ public async Task Connect(int? clientPid, SafeProcessHandle serviceP int retryLefts = 3; do { - if (ProcessApi.WaitForSingleObject(serviceProcessHandle.DangerousGetHandle(), 1) == 0) // original service process is dead, but may have started an elevated service that we don't have handle. + if (serviceProcessHandle!=null && ProcessApi.WaitForSingleObject(serviceProcessHandle.DangerousGetHandle(), 1) == 0) // original service process is dead, but may have started an elevated service that we don't have handle. retryLefts--; - + pipeName = FindService(user, clientPid.Value, out isHighIntegrity); if (pipeName == null) From 449f048047aadb15f5534ce63cee94ecc1950e11 Mon Sep 17 00:00:00 2001 From: Gerardo Grignoli Date: Sat, 26 Aug 2023 23:21:21 -0300 Subject: [PATCH 2/3] Changed service connection timeout handling for #291 --- src/gsudo/Commands/RunCommand.cs | 2 +- src/gsudo/Helpers/ServiceHelper.cs | 4 +- src/gsudo/Rpc/IRpcClient.cs | 2 +- src/gsudo/Rpc/NamedPipeClient.cs | 60 +++++++++++++++++++----------- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/gsudo/Commands/RunCommand.cs b/src/gsudo/Commands/RunCommand.cs index 83dd40d4..d920c8dd 100644 --- a/src/gsudo/Commands/RunCommand.cs +++ b/src/gsudo/Commands/RunCommand.cs @@ -106,7 +106,7 @@ private async Task RunUsingService(ElevationRequest elevationRequest) if (connection == null) // service is not running or listening. { var service = ServiceHelper.StartService(callingPid, singleUse: InputArguments.KillCache); - connection = await ServiceHelper.Connect(callingPid, service).ConfigureAwait(false); + connection = await ServiceHelper.Connect(callingPid).ConfigureAwait(false); if (connection == null) // still not listening. throw new ApplicationException("Unable to connect to the elevated service."); diff --git a/src/gsudo/Helpers/ServiceHelper.cs b/src/gsudo/Helpers/ServiceHelper.cs index be633754..b542d473 100644 --- a/src/gsudo/Helpers/ServiceHelper.cs +++ b/src/gsudo/Helpers/ServiceHelper.cs @@ -17,13 +17,13 @@ internal static IRpcClient GetRpcClient() return new NamedPipeClient(); } - internal static async Task Connect(int? callingPid = null, SafeProcessHandle serviceHandle = null) + internal static async Task Connect(int? callingPid = null) { IRpcClient rpcClient = GetRpcClient(); try { - return await rpcClient.Connect(callingPid, serviceHandle).ConfigureAwait(false); + return await rpcClient.Connect(callingPid).ConfigureAwait(false); } catch (System.IO.IOException) { } catch (TimeoutException) { } diff --git a/src/gsudo/Rpc/IRpcClient.cs b/src/gsudo/Rpc/IRpcClient.cs index 8d097cc8..0144026d 100644 --- a/src/gsudo/Rpc/IRpcClient.cs +++ b/src/gsudo/Rpc/IRpcClient.cs @@ -5,6 +5,6 @@ namespace gsudo.Rpc { internal interface IRpcClient { - Task Connect(int? clientPid = null, SafeProcessHandle serviceHandle = null); + Task Connect(int? clientPid = null); } } \ No newline at end of file diff --git a/src/gsudo/Rpc/NamedPipeClient.cs b/src/gsudo/Rpc/NamedPipeClient.cs index ab9f9a1c..373bd931 100644 --- a/src/gsudo/Rpc/NamedPipeClient.cs +++ b/src/gsudo/Rpc/NamedPipeClient.cs @@ -9,10 +9,15 @@ namespace gsudo.Rpc { class NamedPipeClient : IRpcClient - { - public async Task Connect(int? clientPid, SafeProcessHandle serviceProcessHandle) + { + /// + /// Establishes a connection to a named pipe server. + /// + /// Optional client process ID. + /// A object representing the connected named pipe, or null if connection fails. + public async Task Connect(int? clientPid) { - int timeoutMilliseconds; + int timeoutMilliseconds = 10000; var server = "."; string pipeName = null; @@ -25,25 +30,21 @@ public async Task Connect(int? clientPid, SafeProcessHandle serviceP { if (clientPid.HasValue) { - int retryLefts = 3; do - { - if (serviceProcessHandle!=null && ProcessApi.WaitForSingleObject(serviceProcessHandle.DangerousGetHandle(), 1) == 0) // original service process is dead, but may have started an elevated service that we don't have handle. - retryLefts--; - - pipeName = FindService(user, clientPid.Value, out isHighIntegrity); + { + pipeName = FindServicePipeName(user, clientPid.Value, out isHighIntegrity); if (pipeName == null) + { await Task.Delay(50).ConfigureAwait(false); + timeoutMilliseconds -= 50; + } } - while (pipeName == null && retryLefts>0); - - timeoutMilliseconds = 5000; // service just started. Larger Timeout - } + while (pipeName == null && timeoutMilliseconds > 0); + } else { isHighIntegrity = false; - timeoutMilliseconds = 300; var callerProcessId = Process.GetCurrentProcess().Id; int maxRecursion = 20; while (callerProcessId > 0 && maxRecursion-- > 0) @@ -51,9 +52,7 @@ public async Task Connect(int? clientPid, SafeProcessHandle serviceP callerProcessId = ProcessHelper.GetParentProcessId(callerProcessId); // Search for Credentials Cache - - //Try Admin - pipeName = FindService(user, callerProcessId, out isHighIntegrity); + pipeName = FindServicePipeName(user, callerProcessId, out isHighIntegrity); if (pipeName!=null) break; @@ -87,7 +86,15 @@ public async Task Connect(int? clientPid, SafeProcessHandle serviceP } } - public static string FindService(string allowedSid, int allowedPid, out bool isHighIntegrity, string targetUserSid = null) + /// + /// Finds the elevated service pipe based on the security identifier (SID) and process ID (PID). + /// + /// The SID of the requesting user. + /// The PID of the requesting process. + /// Output parameter indicating whether the pipe is high integrity. + /// Optional SID that the new process will impersonate. + /// The name of the pipe if found, otherwise null. + public static string FindServicePipeName(string allowedSid, int allowedPid, out bool isHighIntegrity, string targetUserSid = null) { targetUserSid = targetUserSid ?? InputArguments.UserSid; string pipeName; @@ -115,9 +122,16 @@ public static string FindService(string allowedSid, int allowedPid, out bool isH } isHighIntegrity = false; - return null; + return null; } + /// + /// Checks if a service is available for default elevation or the optional specified PID and SID. + /// + /// Optional requester PID that needs a service. + /// Optional requester SID that needs a service. + /// Optional SID that the new process will impersonate. + /// True if a cache service is available, otherwise false. public static bool IsServiceAvailable(int? allowedPid = null, string allowedSid = null, string targetSid = null) { string pipeName = null; @@ -126,13 +140,15 @@ public static bool IsServiceAvailable(int? allowedPid = null, string allowedSid allowedSid = allowedSid ?? System.Security.Principal.WindowsIdentity.GetCurrent().User.Value; targetSid = targetSid ?? InputArguments.UserSid; - if (NamedPipeClient.FindService(allowedSid, 0, out _, targetSid) != null) + // Try cache for any process + if (NamedPipeClient.FindServicePipeName(allowedSid, 0, out _, targetSid) != null) return true; - int maxIterations = 20; + // Loop to search for a cache for the current process or its ancestors + int maxIterations = 20; // To avoid potential PID tree loops where an ancestor process has the same PID. (gerardog/gsudo#155) while (allowedPid.Value > 0 && maxIterations-- > 0) { - pipeName = NamedPipeClient.FindService(allowedSid, allowedPid.Value, out _, targetSid); + pipeName = NamedPipeClient.FindServicePipeName(allowedSid, allowedPid.Value, out _, targetSid); if (pipeName != null) break; From ad187276ebcac359264b38d7873b43a85f0a3d9a Mon Sep 17 00:00:00 2001 From: Gerardo Grignoli Date: Sun, 27 Aug 2023 10:59:16 -0300 Subject: [PATCH 3/3] Refactored service connection handling. #291 --- src/gsudo/Commands/RunCommand.cs | 26 +- src/gsudo/Commands/ServiceCommand.cs | 2 +- src/gsudo/Helpers/ConsoleHelper.cs | 4 +- src/gsudo/Helpers/ServiceHelper.cs | 266 ++++++++++++------ .../ProcessRenderers/TokenSwitchRenderer.cs | 2 +- src/gsudo/Rpc/Connection.cs | 4 +- src/gsudo/Rpc/IRpcClient.cs | 5 +- src/gsudo/Rpc/NamedPipeClient.cs | 105 ++----- src/gsudo/Rpc/NamedPipeServer.cs | 2 +- 9 files changed, 225 insertions(+), 191 deletions(-) diff --git a/src/gsudo/Commands/RunCommand.cs b/src/gsudo/Commands/RunCommand.cs index d920c8dd..3669cfc5 100644 --- a/src/gsudo/Commands/RunCommand.cs +++ b/src/gsudo/Commands/RunCommand.cs @@ -98,18 +98,28 @@ private async Task RunUsingService(ElevationRequest elevationRequest) try { var callingPid = ProcessHelper.GetCallerPid(); - Logger.Instance.Log($"Caller PID: {callingPid}", LogLevel.Debug); - connection = await ServiceHelper.Connect().ConfigureAwait(false); + var serviceLocation = await ServiceHelper.FindAnyServiceFast().ConfigureAwait(false); + if (serviceLocation == null) + { + var serviceHandle = ServiceHelper.StartService(callingPid, singleUse: InputArguments.KillCache); + serviceLocation = await ServiceHelper.WaitForNewService(callingPid).ConfigureAwait(false); + } - if (connection == null) // service is not running or listening. + if (!InputArguments.IntegrityLevel.HasValue) { - var service = ServiceHelper.StartService(callingPid, singleUse: InputArguments.KillCache); - connection = await ServiceHelper.Connect(callingPid).ConfigureAwait(false); + // This is the edge case where user does `gsudo -u SomeOne` and we dont know if SomeOne can elevate or not. + elevationRequest.IntegrityLevel = serviceLocation.IsHighIntegrity ? IntegrityLevel.High : IntegrityLevel.Medium; + } + + if (serviceLocation==null) + throw new ApplicationException("Unable to connect to the elevated service."); - if (connection == null) // still not listening. - throw new ApplicationException("Unable to connect to the elevated service."); + connection = await ServiceHelper.Connect(serviceLocation).ConfigureAwait(false); + if (connection == null) // service is not running or listening. + { + throw new ApplicationException("Unable to connect to the elevated service."); } var renderer = GetRenderer(connection, elevationRequest); @@ -133,7 +143,7 @@ private static int RunWithoutService(ElevationRequest elevationRequest) // No need to escalate. Run in-process Native.ConsoleApi.SetConsoleCtrlHandler(ConsoleHelper.IgnoreConsoleCancelKeyPress, true); - ConsoleHelper.SetPrompt(elevationRequest, InputArguments.GetIntegrityLevel() >= IntegrityLevel.High); + ConsoleHelper.SetPrompt(elevationRequest); if (sameIntegrity) { diff --git a/src/gsudo/Commands/ServiceCommand.cs b/src/gsudo/Commands/ServiceCommand.cs index 553ce78b..ad7bdf63 100644 --- a/src/gsudo/Commands/ServiceCommand.cs +++ b/src/gsudo/Commands/ServiceCommand.cs @@ -116,7 +116,7 @@ private async Task AcceptConnection(Connection connection) ServiceHelper.StartService(AllowedPid, CacheDuration, AllowedSid, SingleUse); } - ConsoleHelper.SetPrompt(request, connection.IsHighIntegrity); + ConsoleHelper.SetPrompt(request); await applicationHost.Start(connection, request).ConfigureAwait(false); //if (replaceService) diff --git a/src/gsudo/Helpers/ConsoleHelper.cs b/src/gsudo/Helpers/ConsoleHelper.cs index 7bc6864b..a583c42b 100644 --- a/src/gsudo/Helpers/ConsoleHelper.cs +++ b/src/gsudo/Helpers/ConsoleHelper.cs @@ -122,11 +122,11 @@ internal static SecureString ReadConsolePassword(string userName) return pass; } - internal static void SetPrompt(ElevationRequest elevationRequest, bool isElevated) + internal static void SetPrompt(ElevationRequest elevationRequest) { if (!string.IsNullOrEmpty(elevationRequest.Prompt)) { - if (!isElevated) + if (elevationRequest.IntegrityLevel < IntegrityLevel.High) Environment.SetEnvironmentVariable("PROMPT", Environment.GetEnvironmentVariable("PROMPT", EnvironmentVariableTarget.User) ?? Environment.GetEnvironmentVariable("PROMPT", EnvironmentVariableTarget.Machine) ?? "$P$G"); else Environment.SetEnvironmentVariable("PROMPT", Environment.ExpandEnvironmentVariables(elevationRequest.Prompt)); diff --git a/src/gsudo/Helpers/ServiceHelper.cs b/src/gsudo/Helpers/ServiceHelper.cs index b542d473..c777e6f8 100644 --- a/src/gsudo/Helpers/ServiceHelper.cs +++ b/src/gsudo/Helpers/ServiceHelper.cs @@ -9,66 +9,147 @@ namespace gsudo.Helpers { + class ServiceLocation + { + public string PipeName { get; set; } + public bool IsHighIntegrity { get; set; } + } + internal static class ServiceHelper { internal static IRpcClient GetRpcClient() { // future Tcp implementations should be plugged here. return new NamedPipeClient(); - } - - internal static async Task Connect(int? callingPid = null) - { - IRpcClient rpcClient = GetRpcClient(); - - try - { - return await rpcClient.Connect(callingPid).ConfigureAwait(false); - } - catch (System.IO.IOException) { } - catch (TimeoutException) { } - catch (Exception ex) - { - if (callingPid.HasValue) - Logger.Instance.Log(ex.ToString(), LogLevel.Warning); - } - - return null; - } - - internal static SafeProcessHandle StartService(int? allowedPid, TimeSpan? cacheDuration = null, string allowedSid = null, bool singleUse = false) + } + + /// + /// Establishes a connection to a named pipe server. + /// + /// Optional client process ID. + /// A object representing the connected named pipe, or null if connection fails. + public static async Task WaitForNewService(int clientPid) { - var currentSid = WindowsIdentity.GetCurrent().User.Value; - - allowedPid = allowedPid ?? Process.GetCurrentProcess().GetCacheableRootProcessId(); - allowedSid = allowedSid ?? Process.GetProcessById(allowedPid.Value)?.GetProcessUser()?.User.Value ?? currentSid; - - string verb; - SafeProcessHandle ret; - - Logger.Instance.Log($"Caller SID: {allowedSid}", LogLevel.Debug); - + int timeoutMilliseconds = 10000; + ServiceLocation service; + + string user = WindowsIdentity.GetCurrent().User.Value; + do + { + service = FindServiceByIntegrity(clientPid, user); + if (service != null) + return service; + + // Retry until service has started. + await Task.Delay(50).ConfigureAwait(false); + timeoutMilliseconds -= 50; + } + while (service == null && timeoutMilliseconds > 0); + + return service; + } + + public static async Task FindAnyServiceFast() + { + string user = WindowsIdentity.GetCurrent().User.Value; + var callerProcessId = Process.GetCurrentProcess().Id; + // Loop to search for a cache for the current process or its ancestors + int maxIterations = 20; // To avoid potential PID tree loops where an ancestor process has the same PID. (gerardog/gsudo#155) + while (callerProcessId > 0 && maxIterations-- > 0) + { + callerProcessId = ProcessHelper.GetParentProcessId(callerProcessId); + + var service = FindServiceByIntegrity(callerProcessId, user); + if (service != null) + return service; + } + return null; + } + + private static ServiceLocation FindServiceByIntegrity(int? clientPid, string user) + { + var anyIntegrity = InputArguments.UserName != null; + var tryHighIntegrity = !InputArguments.IntegrityLevel.HasValue || InputArguments.IntegrityLevel.Value >= IntegrityLevel.High; + var tryLowIntegrity = !InputArguments.IntegrityLevel.HasValue || InputArguments.IntegrityLevel.Value < IntegrityLevel.High; + if (tryHighIntegrity) + { + var pipeName = NamedPipeClient.TryGetServicePipe(user, clientPid.Value, true); + if (pipeName != null) + { + return new ServiceLocation + { + PipeName = pipeName, + IsHighIntegrity = true + }; + } + } + + if (tryLowIntegrity) + { + var pipeName = NamedPipeClient.TryGetServicePipe(user, clientPid.Value, false); + if (pipeName != null) + { + return new ServiceLocation + { + PipeName = pipeName, + IsHighIntegrity = false + }; + } + } + return null; + } + + internal static async Task Connect(ServiceLocation service) + { + IRpcClient rpcClient = GetRpcClient(); + + try + { + return await rpcClient.Connect(service).ConfigureAwait(false); + } + catch (System.IO.IOException) { } + catch (TimeoutException) { } + catch (Exception ex) + { + Logger.Instance.Log(ex.ToString(), LogLevel.Warning); + } + + return null; + } + + internal static SafeProcessHandle StartService(int? allowedPid, TimeSpan? cacheDuration = null, string allowedSid = null, bool singleUse = false) + { + var currentSid = WindowsIdentity.GetCurrent().User.Value; + + allowedPid = allowedPid ?? Process.GetCurrentProcess().GetCacheableRootProcessId(); + allowedSid = allowedSid ?? Process.GetProcessById(allowedPid.Value)?.GetProcessUser()?.User.Value ?? currentSid; + + string verb; + SafeProcessHandle ret; + + Logger.Instance.Log($"Caller SID: {allowedSid}", LogLevel.Debug); + var @params = InputArguments.Debug ? "--debug " : string.Empty; - if (!InputArguments.RunAsSystem && InputArguments.IntegrityLevel.HasValue) @params += $"-i {InputArguments.IntegrityLevel.Value} "; - if (InputArguments.RunAsSystem) @params += "-s "; - if (InputArguments.TrustedInstaller) @params += "--ti "; - if (InputArguments.UserName != null) @params += $"-u {InputArguments.UserName} "; - - verb = "gsudoservice"; - - if (!cacheDuration.HasValue || singleUse) + if (!InputArguments.RunAsSystem && InputArguments.IntegrityLevel.HasValue) @params += $"-i {InputArguments.IntegrityLevel.Value} "; + if (InputArguments.RunAsSystem) @params += "-s "; + if (InputArguments.TrustedInstaller) @params += "--ti "; + if (InputArguments.UserName != null) @params += $"-u {InputArguments.UserName} "; + + verb = "gsudoservice"; + + if (!cacheDuration.HasValue || singleUse) { - if (!Settings.CacheMode.Value.In(CredentialsCache.CacheMode.Auto) || singleUse) - { - verb = "gsudoelevate"; - cacheDuration = TimeSpan.Zero; - } - else - cacheDuration = Settings.CacheDuration; - } - - bool isAdmin = SecurityHelper.IsHighIntegrity(); - + if (!Settings.CacheMode.Value.In(CredentialsCache.CacheMode.Auto) || singleUse) + { + verb = "gsudoelevate"; + cacheDuration = TimeSpan.Zero; + } + else + cacheDuration = Settings.CacheDuration; + } + + bool isAdmin = SecurityHelper.IsHighIntegrity(); + string commandLine = $"{@params}{verb} {allowedPid} {allowedSid} {Settings.LogLevel} {Settings.TimeSpanWithInfiniteToString(cacheDuration.Value)}"; string ownExe = ProcessHelper.GetOwnExeName(); @@ -98,45 +179,46 @@ internal static SafeProcessHandle StartService(int? allowedPid, TimeSpan? cacheD } else { - ret = ProcessFactory.StartElevatedDetached(ownExe, commandLine, !InputArguments.Debug).GetSafeProcessHandle(); - } - - Logger.Instance.Log("Service process started.", LogLevel.Debug); - return ret; - } - - private static void StartTrustedInstallerService(string commandLine, int pid) - { - string name = $"gsudo TI Cache for PID {pid}"; - - string args = $"/Create /ru \"NT SERVICE\\TrustedInstaller\" /TN \"{name}\" /TR \"\\\"{ProcessHelper.GetOwnExeName()}\\\" {commandLine}\" /sc ONCE /st 00:00 /f\""; - Logger.Instance.Log($"Running: schtasks {args}", LogLevel.Debug); - Process p; - - p = InputArguments.Debug - ? ProcessFactory.StartAttached("schtasks", args) - : ProcessFactory.StartRedirected("schtasks", args, null); - - p.WaitForExit(); - if (p.ExitCode != 0) throw new ApplicationException($"Error creating a scheduled task for TrustedInstaller: {p.ExitCode}"); - - try - { - args = $"/run /I /TN \"{name}\""; - p = InputArguments.Debug - ? ProcessFactory.StartAttached("schtasks", args) - : ProcessFactory.StartRedirected("schtasks", args, null); - p.WaitForExit(); - if (p.ExitCode != 0) throw new ApplicationException($"Error starting scheduled task for TrustedInstaller: {p.ExitCode}"); - } - finally - { - args = $"/delete /F /TN \"{name}\""; - p = InputArguments.Debug - ? ProcessFactory.StartAttached("schtasks", args) - : ProcessFactory.StartRedirected("schtasks", args, null); - p.WaitForExit(); - } - } - } + ret = ProcessFactory.StartElevatedDetached(ownExe, commandLine, !InputArguments.Debug).GetSafeProcessHandle(); + } + + Logger.Instance.Log("Service process started.", LogLevel.Debug); + return ret; + } + + private static void StartTrustedInstallerService(string commandLine, int pid) + { + string name = $"gsudo TI Cache for PID {pid}"; + + string args = $"/Create /ru \"NT SERVICE\\TrustedInstaller\" /TN \"{name}\" /TR \"\\\"{ProcessHelper.GetOwnExeName()}\\\" {commandLine}\" /sc ONCE /st 00:00 /f\""; + Logger.Instance.Log($"Running: schtasks {args}", LogLevel.Debug); + Process p; + + p = InputArguments.Debug + ? ProcessFactory.StartAttached("schtasks", args) + : ProcessFactory.StartRedirected("schtasks", args, null); + + p.WaitForExit(); + if (p.ExitCode != 0) throw new ApplicationException($"Error creating a scheduled task for TrustedInstaller: {p.ExitCode}"); + + try + { + args = $"/run /I /TN \"{name}\""; + p = InputArguments.Debug + ? ProcessFactory.StartAttached("schtasks", args) + : ProcessFactory.StartRedirected("schtasks", args, null); + p.WaitForExit(); + if (p.ExitCode != 0) throw new ApplicationException($"Error starting scheduled task for TrustedInstaller: {p.ExitCode}"); + } + finally + { + args = $"/delete /F /TN \"{name}\""; + p = InputArguments.Debug + ? ProcessFactory.StartAttached("schtasks", args) + : ProcessFactory.StartRedirected("schtasks", args, null); + p.WaitForExit(); + } + } + } } + diff --git a/src/gsudo/ProcessRenderers/TokenSwitchRenderer.cs b/src/gsudo/ProcessRenderers/TokenSwitchRenderer.cs index c96700a9..faad4a5c 100644 --- a/src/gsudo/ProcessRenderers/TokenSwitchRenderer.cs +++ b/src/gsudo/ProcessRenderers/TokenSwitchRenderer.cs @@ -30,7 +30,7 @@ internal TokenSwitchRenderer(Connection connection, ElevationRequest elevationRe _connection = connection; _elevationRequest = elevationRequest; - ConsoleHelper.SetPrompt(elevationRequest, connection.IsHighIntegrity); + ConsoleHelper.SetPrompt(elevationRequest); ProcessApi.CreateProcessFlags dwCreationFlags = ProcessApi.CreateProcessFlags.CREATE_SUSPENDED; diff --git a/src/gsudo/Rpc/Connection.cs b/src/gsudo/Rpc/Connection.cs index 9245db9d..47e202ac 100644 --- a/src/gsudo/Rpc/Connection.cs +++ b/src/gsudo/Rpc/Connection.cs @@ -14,12 +14,10 @@ class Connection : IDisposable { private PipeStream _dataStream; private PipeStream _controlStream; - public bool IsHighIntegrity { get; } - public Connection(PipeStream ControlStream, PipeStream DataStream, bool isHighIntegrity) + public Connection(PipeStream ControlStream, PipeStream DataStream) { _dataStream = DataStream; _controlStream = ControlStream; - IsHighIntegrity = isHighIntegrity; } public Stream DataStream => _dataStream; diff --git a/src/gsudo/Rpc/IRpcClient.cs b/src/gsudo/Rpc/IRpcClient.cs index 0144026d..89477187 100644 --- a/src/gsudo/Rpc/IRpcClient.cs +++ b/src/gsudo/Rpc/IRpcClient.cs @@ -1,10 +1,11 @@ -using Microsoft.Win32.SafeHandles; +using gsudo.Helpers; +using Microsoft.Win32.SafeHandles; using System.Threading.Tasks; namespace gsudo.Rpc { internal interface IRpcClient { - Task Connect(int? clientPid = null); + Task Connect(ServiceLocation service); } } \ No newline at end of file diff --git a/src/gsudo/Rpc/NamedPipeClient.cs b/src/gsudo/Rpc/NamedPipeClient.cs index 373bd931..104ffd31 100644 --- a/src/gsudo/Rpc/NamedPipeClient.cs +++ b/src/gsudo/Rpc/NamedPipeClient.cs @@ -10,66 +10,24 @@ namespace gsudo.Rpc { class NamedPipeClient : IRpcClient { - /// - /// Establishes a connection to a named pipe server. - /// - /// Optional client process ID. - /// A object representing the connected named pipe, or null if connection fails. - public async Task Connect(int? clientPid) - { - int timeoutMilliseconds = 10000; - var server = "."; - - string pipeName = null; - bool isHighIntegrity; - string user = System.Security.Principal.WindowsIdentity.GetCurrent().User.Value; + public async Task Connect(ServiceLocation service) + { + var server = "."; NamedPipeClientStream dataPipe = null; - NamedPipeClientStream controlPipe = null; - - try - { - if (clientPid.HasValue) - { - do - { - pipeName = FindServicePipeName(user, clientPid.Value, out isHighIntegrity); + NamedPipeClientStream controlPipe = null; + var timeoutMilliseconds = 10000; - if (pipeName == null) - { - await Task.Delay(50).ConfigureAwait(false); - timeoutMilliseconds -= 50; - } - } - while (pipeName == null && timeoutMilliseconds > 0); - } - else - { - isHighIntegrity = false; - var callerProcessId = Process.GetCurrentProcess().Id; - int maxRecursion = 20; - while (callerProcessId > 0 && maxRecursion-- > 0) - { - callerProcessId = ProcessHelper.GetParentProcessId(callerProcessId); - - // Search for Credentials Cache - pipeName = FindServicePipeName(user, callerProcessId, out isHighIntegrity); - - if (pipeName!=null) - break; - } - } - - if (pipeName == null) return null; - - dataPipe = new NamedPipeClientStream(server, pipeName, PipeDirection.InOut, PipeOptions.Asynchronous, System.Security.Principal.TokenImpersonationLevel.Identification, HandleInheritability.None); + try + { + dataPipe = new NamedPipeClientStream(server, service.PipeName, PipeDirection.InOut, PipeOptions.Asynchronous, System.Security.Principal.TokenImpersonationLevel.Identification, HandleInheritability.None); await dataPipe.ConnectAsync(timeoutMilliseconds).ConfigureAwait(false); - controlPipe = new NamedPipeClientStream(server, pipeName + "_control", PipeDirection.InOut, PipeOptions.Asynchronous, System.Security.Principal.TokenImpersonationLevel.Identification, HandleInheritability.None); + controlPipe = new NamedPipeClientStream(server, service.PipeName + "_control", PipeDirection.InOut, PipeOptions.Asynchronous, System.Security.Principal.TokenImpersonationLevel.Identification, HandleInheritability.None); await controlPipe.ConnectAsync(timeoutMilliseconds).ConfigureAwait(false); - Logger.Instance.Log($"Connected via Named Pipe {pipeName}.", LogLevel.Debug); + Logger.Instance.Log($"Connected via Named Pipe {service.PipeName}.", LogLevel.Debug); - var conn = new Connection(controlPipe, dataPipe, isHighIntegrity); + var conn = new Connection(controlPipe, dataPipe); return conn; } catch (System.TimeoutException) @@ -87,41 +45,23 @@ public async Task Connect(int? clientPid) } /// - /// Finds the elevated service pipe based on the security identifier (SID) and process ID (PID). + /// Checks if a service pipe exists based on the security identifier (SID) and process ID (PID). /// /// The SID of the requesting user. /// The PID of the requesting process. - /// Output parameter indicating whether the pipe is high integrity. + /// Indicating whether the pipe is high integrity. /// Optional SID that the new process will impersonate. /// The name of the pipe if found, otherwise null. - public static string FindServicePipeName(string allowedSid, int allowedPid, out bool isHighIntegrity, string targetUserSid = null) + public static string TryGetServicePipe(string allowedSid, int allowedPid, bool isHighIntegrity, string targetUserSid = null) { targetUserSid = targetUserSid ?? InputArguments.UserSid; string pipeName; - if (!InputArguments.IntegrityLevel.HasValue || InputArguments.IntegrityLevel.Value >= IntegrityLevel.High) + pipeName = NamedPipeNameFactory.GetPipeName(allowedSid, allowedPid, targetUserSid, isHighIntegrity); + if (NamedPipeUtils.ExistsNamedPipe(pipeName)) { - pipeName = NamedPipeNameFactory.GetPipeName(allowedSid, allowedPid, targetUserSid, true); - if (NamedPipeUtils.ExistsNamedPipe(pipeName)) - { - isHighIntegrity = true; - InputArguments.IntegrityLevel = InputArguments.IntegrityLevel ?? IntegrityLevel.High; - return pipeName; - } + return pipeName; } - - if (!InputArguments.IntegrityLevel.HasValue || InputArguments.IntegrityLevel.Value < IntegrityLevel.High) - { - pipeName = NamedPipeNameFactory.GetPipeName(allowedSid, allowedPid, targetUserSid, false); - if (NamedPipeUtils.ExistsNamedPipe(pipeName)) - { - isHighIntegrity = false; - InputArguments.IntegrityLevel = InputArguments.IntegrityLevel ?? IntegrityLevel.Low; - return pipeName; - } - } - - isHighIntegrity = false; return null; } @@ -141,16 +81,19 @@ public static bool IsServiceAvailable(int? allowedPid = null, string allowedSid targetSid = targetSid ?? InputArguments.UserSid; // Try cache for any process - if (NamedPipeClient.FindServicePipeName(allowedSid, 0, out _, targetSid) != null) + if (NamedPipeClient.TryGetServicePipe(allowedSid, 0, true, targetSid) != null) return true; + //if (NamedPipeClient.TryGetServicePipe(allowedSid, 0, false, targetSid) != null) + // return true; // Loop to search for a cache for the current process or its ancestors int maxIterations = 20; // To avoid potential PID tree loops where an ancestor process has the same PID. (gerardog/gsudo#155) while (allowedPid.Value > 0 && maxIterations-- > 0) { - pipeName = NamedPipeClient.FindServicePipeName(allowedSid, allowedPid.Value, out _, targetSid); - if (pipeName != null) - break; + if (NamedPipeClient.TryGetServicePipe(allowedSid, allowedPid.Value, true, targetSid) != null) + return true; + //if (NamedPipeClient.TryGetServicePipe(allowedSid, allowedPid.Value, false, targetSid) != null) + // return true; allowedPid = ProcessHelper.GetParentProcessId(allowedPid.Value); } diff --git a/src/gsudo/Rpc/NamedPipeServer.cs b/src/gsudo/Rpc/NamedPipeServer.cs index 3e2c4777..66f701d8 100644 --- a/src/gsudo/Rpc/NamedPipeServer.cs +++ b/src/gsudo/Rpc/NamedPipeServer.cs @@ -118,7 +118,7 @@ public async Task Listen() if (dataPipe.IsConnected && controlPipe.IsConnected && !_cancellationTokenSource.IsCancellationRequested) { - var connection = new Connection(controlPipe, dataPipe, isHighIntegrity); + var connection = new Connection(controlPipe, dataPipe); ConnectionKeepAliveThread.Start(connection);