Skip to content

Commit

Permalink
refactor: Reimplement elevated command runner to allow specifying a t…
Browse files Browse the repository at this point in the history
…itle, body and escaped command

Signed-off-by: Felicitas Pojtinger <felicitas@pojtinger.com>
  • Loading branch information
pojntfx committed Sep 6, 2024
1 parent e07e4b1 commit 805df22
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 29 deletions.
11 changes: 2 additions & 9 deletions pkg/backend/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ var (
)

const (
flatpakSpawnCmd = "flatpak-spawn"

TraceCommandEnv = "CONNMAPPER_TRACE"

traceCommandHandshakeLen = 2
Expand Down Expand Up @@ -412,16 +410,11 @@ restartTraceCommand:

switch runtime.GOOS {
case "linux":
cmd := fmt.Sprintf("setcap cap_net_raw,cap_net_admin=eip %v", bin)
if _, err := exec.LookPath(flatpakSpawnCmd); err == nil {
cmd = flatpakSpawnCmd + " --host " + cmd
}

if err := uutils.RunElevatedCommand(ctx, cmd); err != nil {
if err := uutils.RunElevatedCommand(ctx, "Authentication Required", "Authentication is needed to capture packets.", fmt.Sprintf(`setcap cap_net_raw,cap_net_admin=eip '%v'`, bin)); err != nil {
return err
}
default:
if err := uutils.RunElevatedCommand(ctx, fmt.Sprintf("%v %v", bin, strings.Join(os.Args, " "))); err != nil {
if err := uutils.RunElevatedCommand(ctx, "Authentication Required", "Authentication is needed to capture packets.", fmt.Sprintf("%v %v", bin, strings.Join(os.Args, " "))); err != nil {
return err
}
}
Expand Down
78 changes: 58 additions & 20 deletions pkg/utils/elevated_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,86 @@ package utils

import (
"context"
"errors"
"fmt"
"os/exec"
"runtime"
)

func RunElevatedCommand(ctx context.Context, command string) error {
var (
ErrCouldNotExecuteCommand = errors.New("could not execute command")
ErrNoEscalationMethodFound = errors.New("no escalation method could be found")
ErrNoTerminalFound = errors.New("no terminal could be found")
)

const (
flatpakSpawnCmd = "flatpak-spawn"
)

// RunElevatedCommand runs a command with elevated (e.g. administrator or root) permissions
// Note that `title` and `body` will be passed into the shell without validation or escapes, so make sure
// to not enter any user-provided strings
func RunElevatedCommand(ctx context.Context, title, body, command string) error {
switch runtime.GOOS {
case "windows":
if output, err := exec.CommandContext(ctx, "cmd.exe", "/C", "runas", "/user:Administrator", command).CombinedOutput(); err != nil {
return fmt.Errorf("could run command with output: %s: %w", output, err)
powerShellBinary, err := exec.LookPath("pwsh.exe")
if err != nil {
powerShellBinary = "powershell.exe"
}

if output, err := exec.Command(powerShellBinary, `-Command`, fmt.Sprintf(`Start-Process '%v' -Verb RunAs -Wait -ArgumentList "%v"`, powerShellBinary, command)).CombinedOutput(); err != nil {
return errors.Join(ErrCouldNotExecuteCommand, fmt.Errorf("could not execute command with output: %s", output), err)
}

case "darwin":
if output, err := exec.CommandContext(ctx, "osascript", "-e", fmt.Sprintf(`do shell script "%v" with administrator privileges`, command)).CombinedOutput(); err != nil {
return fmt.Errorf("could run command with output: %s: %w", output, err)
if output, err := exec.Command(
"osascript",
"-e",
fmt.Sprintf(`do shell script "%v" with administrator privileges with prompt "%v: %v"`, command, title, body),
).CombinedOutput(); err != nil {
return errors.Join(ErrCouldNotExecuteCommand, fmt.Errorf("could not execute command with output: %s", output), err)
}

default:
var (
binaryName string
prefix = []string{}
)
if _, err := exec.LookPath(flatpakSpawnCmd); err == nil {
binaryName = flatpakSpawnCmd
prefix = append(prefix, "--host")
} else {
binaryName = "sh"
prefix = append(prefix, "-c")
}

// Escalate using Polkit
if pkexec, err := exec.LookPath("pkexec"); err == nil {
if output, err := exec.CommandContext(ctx, pkexec, "sh", "-c", command).CombinedOutput(); err != nil {
return fmt.Errorf("could run command with output: %s: %w", output, err)
}
command = pkexec + " " + command
} else {
// Escalate using using terminal emulator
xterm, err := exec.LookPath("xterm")
// Escalate manually using using terminal emulator as a fallback
// This doesn't work inside Flatpak - for Flatpak systems we asume that `pkexec` is available, so this code would never be reached
terminal, err := exec.LookPath("xterm")
if err != nil {
return err
return errors.Join(ErrNoTerminalFound, err)
}

suid, err := exec.LookPath("sudo")
suid, err := exec.LookPath("run0")
if err != nil {
suid, err = exec.LookPath("doas")
suid, err = exec.LookPath("sudo")
if err != nil {
return err
suid, err = exec.LookPath("doas")
if err != nil {
return errors.Join(ErrNoEscalationMethodFound, err)
}
}
}

if output, err := exec.CommandContext(
ctx,
xterm, "-T", "Authentication Required", "-e", fmt.Sprintf(`echo 'Authentication is needed.' && %v %v`, suid, command),
).CombinedOutput(); err != nil {
return fmt.Errorf("could run command with output: %s: %w", output, err)
}
command = terminal + " -T '" + title + `' -e "echo '` + body + `' && ` + suid + " " + command + `"`
}

if output, err := exec.Command(binaryName, append(prefix, []string{command}...)...).CombinedOutput(); err != nil {
return errors.Join(ErrCouldNotExecuteCommand, fmt.Errorf("could not execute command with output: %s", output), err)
}
}

Expand Down

0 comments on commit 805df22

Please sign in to comment.