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

[Feature Request] Add Sudo Support on Windows #3541

Open
lunatic-gh opened this issue Nov 21, 2024 · 15 comments
Open

[Feature Request] Add Sudo Support on Windows #3541

lunatic-gh opened this issue Nov 21, 2024 · 15 comments

Comments

@lunatic-gh
Copy link

Description of the problem or steps to reproduce

Since Sudo wasn't really a thing on Windows, micro has no support for this, and will happily tell this to the user when trying to use:
image

However, since 24H2, Windows added sudo as an option. It behaves pretty similar, eg by running sudo <my-command> it would show a UAC prompt, and on success run my-command in an administrator instance of that commandline.

Additionally, even if 24H2 is not being used, GSudo is basically the exact same.

It would be a great addition to support sudo when either 24H2's Sudo or Gsudo is present.

Specifications

Commit hash: /
OS: Windows 11
Terminal: /

@niten94
Copy link
Contributor

niten94 commented Nov 21, 2024

I am not available to look much into adding support, but I tried checking what had to be specifically done to add support months ago. Micro runs dd when saving files using sudo but dd is not preinstalled. I wrote about it in a comment in a different issue: #2660 (comment)

Edit: I was thinking about running a PowerShell command instead of dd sometime after I posted the comment, but I have not checked anything about it.

@lunatic-gh
Copy link
Author

What exactly is micro internally using on windows to save files? Couldn't it just do the exact same it is already doing, but instead with this sudo/gsudo command as a prefix? Sadly i don't know anything about the go language or windows internals itself, otherwise i'd gladly help however i can.

@niten94
Copy link
Contributor

niten94 commented Nov 21, 2024

Micro writes to files using Go standard library functions in all platforms when saving without sudo, so the same thing cannot be done as a command using sudo. The actual command run with sudo in all platforms except Windows is like echo "new file content" | sudo dd bs=4k of=file.txt, but micro uses functions to write input that sudo passes to dd instead of echo "new file content" |.

dd is a program used like a command that is usually preinstalled in Linux distributions. It's not available by default in Windows so it may be inconvenient if micro uses dd in Windows. Micro may just use a different command instead of dd ... in Windows to add support, but I don't know what command can be used.

@JoeKar
Copy link
Collaborator

JoeKar commented Nov 21, 2024

As @niten94 already wrote, it's unfortunately not done by allowing/enabling sudo on Windows, because it would require additionally dd (coreutils) for Windows. The latter one isn't impossible, because they already have been ported (e.g. gnuwin32 coreutils, rust coreutils), but need to be installed too.
Or it requires a fully different approach for storing files with extended privileges like ShellExecute (see here: https://blog.hadenes.io/post/how-to-request-admin-permissions-in-windows-through-uac-with-golang/).

The easiest way for us would be option one, since the mechanism doesn't need to be rewritten or extended and we just need to enable the sudo support and give hints that additional tools are required.

@lunatic-gh
Copy link
Author

Micro may just use a different command instead of dd ... in Windows to add support, but I don't know what command can be used.

If all that is needed is an alternative native command, can't we just use powershell for that?
I googled a bit, and tested a bit of powershell script in a freshly installed windows-10 vm (only gsudo installed), and could observe the following:
image

Maybe this could be used on windows?

@JoeKar
Copy link
Collaborator

JoeKar commented Nov 21, 2024

Maybe this could be used on windows?

That's like running micro itself with super user rights and using Go's standard functionalities to open and write, but this isn't what we want...at least not in this form. The extended privileges shall only be used for the file save process, not for the rest of micro.
This is the current approach to store the file:

cmd = exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "bs=4k", "of="+name)

Currently I don't know if there is a different alternative other than dd under Windows to use the stdin as input and write to a file.

BTW:

Or it requires a fully different approach for storing files with extended privileges like ShellExecute (see here: https://blog.hadenes.io/post/how-to-request-admin-permissions-in-windows-through-uac-with-golang/).

Currently realized, that we don't even need sudo, because Windows already offers runas, which can be used via the native exec.Command() without the need of ShellExecute. And the runas can be stored into sucmd.

@lunatic-gh
Copy link
Author

lunatic-gh commented Nov 21, 2024

in this case, my basic "idea" wasn't to run the entirety of micro as sudo, but rather something like

cmd = exec.Command(config.GlobalSettings["sucmd"].(string), "powershell -command", "& {...the-command...}")

(assuming that syntax is correct)
since the saving-process is done via a command anyway.

But if the runas method is more convenient that is probably better than this awfully long powershell-script.

@JoeKar
Copy link
Collaborator

JoeKar commented Nov 22, 2024

But the PowerShell isn't available by default either or am I wrong? micro can be executed under Windows without the PowerShell. So we end up in a situation where something additionally needs to be installed anyway. Then I would recommend to go with a coreutils alternative (mentioned here: #3541 (comment)) and the dd path should most probably be configurable.

@lunatic-gh
Copy link
Author

lunatic-gh commented Nov 22, 2024

both windows 10 and 11 come with powershell 5.1 pre-installed. it should therefore be available on every pc that isn't stuck 200 years ago. I think it might even be available out of the box since windows 7, but i am not 100% sure on that.

the screenshot i gave above was indeed done on a vm with windows 10, where i really only installed gsudo (well and updated the app installer through ms-store, so i can actually install gsudo)

@JoeKar
Copy link
Collaborator

JoeKar commented Nov 22, 2024

Ok, if this is the case then...

cmd = exec.Command(config.GlobalSettings["sucmd"].(string), "powershell", "-command", "Get-Content -Path '-' | Set-Content -Path "+name)

...could do the trick, right?

@lunatic-gh
Copy link
Author

lunatic-gh commented Nov 22, 2024

That command does not seem to work from what i can see. Dunno what the '-' means either.

Does the 4k block size matter? If not, the simplest way would probably be the command:

powershell -command "string-content" | Out-File -FilePath "path/to/file"

or in go:

cmd = exec.Command(config.GlobalSettings["sucmd"].(string), "powershell", "-command", content-string, "| Out-File -FilePath", name)

If i got the syntax right.

This would create the given file (if it does not exist), and afterwards write the given content to it.

@niten94
Copy link
Contributor

niten94 commented Nov 23, 2024

I think @JoeKar included Get-Content -Path - in the command to read the data written to PowerShell standard input. Each string passed except the first argument are passed as one argument to the program in the first argument (exec.Command: sudo, sudo: powershell), so arguments have to be passed like in the syntax that @JoeKar wrote. The file content is written to the standard input of cmd in line 39, 63 and 64 below, so it does not have to be included in arguments:

if writeCloser, err = cmd.StdinPipe(); err != nil {
return
}
c = make(chan os.Signal, 1)
signal.Reset(os.Interrupt)
signal.Notify(c, os.Interrupt)
screenb = screen.TempFini()
// need to start the process now, otherwise when we flush the file
// contents to its stdin it might hang because the kernel's pipe size
// is too small to handle the full file contents all at once
if err = cmd.Start(); err != nil {
screen.TempStart(screenb)
signal.Notify(util.Sigterm, os.Interrupt)
signal.Stop(c)
return
}
} else if writeCloser, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666); err != nil {
return
}
w := bufio.NewWriter(transform.NewWriter(writeCloser, enc.NewEncoder()))
err = fn(w)

I do not know much about .NET and PowerShell but I tried reading documentation and I do not think cmdlets can be used. The output encoding is handled in line 63 but encoding seems to be converted when using pipes and Out-File1 in PowerShell. Set-Content -AsByteStream cannot be used in PowerShell 5.1.2 I do not know if writing with 4k block size is needed.

I did not check anything with error handling but I think this can be done. The path would be passed as a different argument like -command "code" file.txt to avoid escaping. The code can also be passed in Base64 but I don't know if that would be worse.

using namespace System.IO
$f = [File]::Open($args[0],
    [FileMode]::OpenOrCreate -bor [FileMode]::Truncate, [FileAccess]::Write)
$i = [System.Console]::OpenStandardInput()
$i.CopyTo($f)
$i.Dispose()
$f.Dispose()

Footnotes

  1. https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/out-file?view=powershell-5.1#-encoding

  2. https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/set-content?view=powershell-7.4#-asbytestream

@lunatic-gh
Copy link
Author

lunatic-gh commented Nov 23, 2024

I am not sure if Go would do that different, but whenever i run this inside a powershell terminal, it just fails because it takes that '-' literally, but expects an actual filesystem path it seems:
image

The file content is written to the standard input of cmd in line 39, 63 and 64 below, so it does not have to be included in arguments:

Wouldn't that require both of those processes (A: Creating the file, and B: Writing to it) require admin permissions? I am pretty sure just running the creation process as sudo won't automatically give the process access to write to it. And since the 2nd Process (writing to the file) is not done via shell-commands, there's no easy way to give it those needed permissions

@JoeKar
Copy link
Collaborator

JoeKar commented Nov 23, 2024

I am not sure if Go would do that different, but whenever i run this inside a powershell terminal, it just fails because it takes that '-' literally, but expects an actual filesystem path it seems:

Yep, the AI fooled me, since "-" can't be used as placeholder for the stdin there. So the PS script must be more complex.

Maybe then something like this:
cmd = exec.Command(config.GlobalSettings["sucmd"].(string), "powershell", "-command", "$input = [System.Console]::In.ReadToEnd(); $input | Set-Content -Path", name)

But to be honest, I never used PS before.
If you find a nice way to create a short replacement for the dd-approach without dd itself but with Windows internals...why not.

@lunatic-gh
Copy link
Author

Honestly, i think i totally lack the options to test something like this. I have no idea what [System.Console]::In.ReadToEnd() does, or specifically i cannot test it, since in a console it just... freezes, probably since it does something entirely else in the Go Program context.

If someone else with go & ps knowledge and the ability to build micro could test this all, it would probably be way easier than me trying to fiddle my way into this without knowing anything about... anything really.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants