diff --git a/README.md b/README.md index 6df15354..a07aba71 100644 --- a/README.md +++ b/README.md @@ -73,4 +73,4 @@ Read or write a user setting - Why `gsudo` instead of just `sudo`? -When I created `gsudo`, there were other `sudo` packages on most Windows popular package managers such as `Chocolatey` and `Scoop`, all of them doing the elevation on a new console. In my opinion, that context switch is improductive, and also makes such tools less usefull for scripting. I could name the app `sudo` and the package as `gsudo`, but I fear people will not remember the package name for further installations. I will add the option to bind `sudo` command to the `gsudo` app in future versions of the installer. +When I created `gsudo`, there were other `sudo` packages on most Windows popular package managers such as `Chocolatey` and `Scoop`. I could name the app `sudo` and the package as `gsudo`, but I fear people will not remember the package name for further installations. I will add the option to bind `sudo` command to the `gsudo` app in future versions of the installer. diff --git a/backlog.md b/backlog.md index 9f2ef1b9..551105d9 100644 --- a/backlog.md +++ b/backlog.md @@ -1,20 +1,22 @@ # gsudo Backlog +- Check for grandparents on Authorization() +- raw mode shouls merge StdErr and StdOut in one file, to fix random ordering of stderr/stdout. - Chocolatey package / Scoop package / Release on github. - gsudo --nocache (service will quit immediately after process ends and will not elevate other commands with cached credentials) (find better --syntax) - Allow to specify other username. (RunAsUser verb) +- console resize on vt mode. ## Other not so likely ideas +- Third consecutive Ctrl-C should ask if the child process must be kept running or killed. - Remote sudo. Run process on another machine / as in PSExec. (security?) -- Make gsudo chocolatey package link sudo to gsudo (conflict with chocolatey sudo package) - Spend 500 USD in a code-signing certificate so I can sign the builds. I need to setup an https web site for gsudo or myself first as a prerequisit to get the certificate. - gsudo Chocolatey Package to genereate a self-signed cert, install, and sign the exe on install, then delete the cert. (better uac prompt without $$ buying a certificate) - Low level console access (https://docs.microsoft.com/en-us/windows/console/console-functions) ## Completed -- Third consecutive Ctrl-C or client disconnect kills the elevated process. - VT console extended keys (F1-F12, CTRL+?, HOME,PAGEUP) - WinPty/VT100 support: When in VT mode, processes are spawn using a PseudoConsole. Rendering could be done using Windows Console ENABLE_VIRTUAL_TERMINAL processing flag but it is pretty [unstable](https://github.com/microsoft/terminal/issues/3765). So it is disabled by default unless you are running inside ConEmu/Cmder which are VT100 ready terminals. VT Mode is enabled automatically if you run inside a ConEmu/Cmder or if you use `--vt` flag. diff --git a/src/gsudo/Commands/RunCommand.cs b/src/gsudo/Commands/RunCommand.cs index 6e25cb80..6dfb84a1 100644 --- a/src/gsudo/Commands/RunCommand.cs +++ b/src/gsudo/Commands/RunCommand.cs @@ -41,17 +41,11 @@ public async Task Execute() if (elevationRequest.Mode == ElevationRequest.ConsoleMode.VT) { - elevationRequest.ConsoleWidth = Console.WindowWidth; + elevationRequest.ConsoleWidth = Console.WindowWidth; elevationRequest.ConsoleHeight = Console.WindowHeight; if (TerminalHelper.IsConEmu()) elevationRequest.ConsoleWidth--; // weird ConEmu/Cmder fix - - Environment.SetEnvironmentVariable("PROMPT", GlobalSettings.VTPrompt.Value); - } - else - { - Environment.SetEnvironmentVariable("PROMPT", GlobalSettings.Prompt.Value); } if (ProcessExtensions.IsAdministrator() && !GlobalSettings.NewWindow) @@ -66,6 +60,15 @@ public async Task Execute() // No need to escalate. Run in-process + if (elevationRequest.Mode == ElevationRequest.ConsoleMode.Raw) + { + Environment.SetEnvironmentVariable("PROMPT", GlobalSettings.Prompt.Value); + } + else + { + Environment.SetEnvironmentVariable("PROMPT", GlobalSettings.VTPrompt.Value); + } + if (GlobalSettings.NewWindow) { using (Process process = ProcessFactory.StartDetached(exeName, GetArguments(), Environment.CurrentDirectory, false)) @@ -137,10 +140,7 @@ public async Task Execute() Logger.Instance.Log("Unable to connect to the elevated service.", LogLevel.Error); return Constants.GSUDO_ERROR_EXITCODE; } - - new BinaryFormatter() -// { TypeFormat = System.Runtime.Serialization.Formatters.FormatterTypeStyle.TypesAlways, Binder = new MySerializationBinder() } - .Serialize(connection.ControlStream, elevationRequest); + WriteElevationRequest(elevationRequest, connection); await connection.ControlStream.FlushAsync().ConfigureAwait(false); ConnectionKeepAliveThread.Start(connection); @@ -157,6 +157,21 @@ public async Task Execute() } + private async Task WriteElevationRequest(ElevationRequest elevationRequest, Connection connection) + { + var ms = new System.IO.MemoryStream(); + new BinaryFormatter() + { TypeFormat = System.Runtime.Serialization.Formatters.FormatterTypeStyle.TypesAlways, Binder = new MySerializationBinder() } + .Serialize(ms, elevationRequest); + ms.Seek(0, System.IO.SeekOrigin.Begin); + + byte[] lengthArray = BitConverter.GetBytes(ms.Length); + Logger.Instance.Log($"ElevationRequest length {ms.Length}", LogLevel.Debug); + + await connection.ControlStream.WriteAsync(lengthArray, 0, sizeof(int)).ConfigureAwait(false); + await connection.ControlStream.WriteAsync(ms.ToArray(), 0, (int)ms.Length).ConfigureAwait(false); + } + /// /// Decide wheter we will use raw piped I/O screen communication, /// or enhanced, colorfull VT mode with nice TAB auto-complete. diff --git a/src/gsudo/Commands/ServiceCommand.cs b/src/gsudo/Commands/ServiceCommand.cs index 3b606c94..51e00db6 100644 --- a/src/gsudo/Commands/ServiceCommand.cs +++ b/src/gsudo/Commands/ServiceCommand.cs @@ -83,9 +83,20 @@ private IRpcServer CreateServer() private static async Task ReadElevationRequest(Stream dataPipe) { + byte[] dataSize = new byte[sizeof(int)]; + dataPipe.Read(dataSize, 0, sizeof(int)); + int dataSizeInt = BitConverter.ToInt32(dataSize, 0); + byte[] inBuffer = new byte[dataSizeInt]; + + var bytesRemaining = dataSizeInt; + while (bytesRemaining > 0 ) + bytesRemaining -= dataPipe.Read(inBuffer, 0, bytesRemaining); + + Logger.Instance.Log($"ElevationRequest length {dataSizeInt}", LogLevel.Debug); + return (ElevationRequest) new BinaryFormatter() // { TypeFormat = System.Runtime.Serialization.Formatters.FormatterTypeStyle.TypesAlways, Binder = new MySerializationBinder() } - .Deserialize(dataPipe); + .Deserialize(new MemoryStream(inBuffer)); } } }