File: powershell_windows.go

package info (click to toggle)
golang-github-crc-org-crc 2.34.0%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,548 kB
  • sloc: sh: 398; makefile: 326; javascript: 40
file content (107 lines) | stat: -rw-r--r-- 3,119 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package powershell

import (
	"bytes"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/crc-org/crc/v2/pkg/crc/logging"
)

var isAdminCmds = []string{
	"$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())",
	"$currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)",
}

func IsAdmin() bool {
	cmd := strings.Join(isAdminCmds, ";")
	stdOut, _, err := Execute(cmd)
	if err != nil {
		return false
	}
	if strings.TrimSpace(stdOut) == "False" {
		return false
	}

	return true
}

func Execute(args ...string) (string, string, error) {
	logging.Debugf("Running '%s'", strings.Join(args, " "))

	powershell, err := exec.LookPath("powershell.exe")
	if err != nil {
		return "", "", err
	}
	args = append([]string{"-NoProfile", "-NonInteractive", "-ExecutionPolicy", "RemoteSigned", "-Command", "$ProgressPreference = 'SilentlyContinue';"}, args...)
	cmd := exec.Command(powershell, args...) // #nosec G204
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}

	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	err = cmd.Run()
	if err != nil {
		logging.Debugf("Command failed: %v", err)
		logging.Debugf("stdout: %s", stdout.String())
		logging.Debugf("stderr: %s", stderr.String())
	}
	return stdout.String(), stderr.String(), err
}

func ExecuteAsAdmin(reason, cmd string) (string, string, error) {
	powershell, err := exec.LookPath("powershell.exe")
	if err != nil {
		return "", "", err
	}
	scriptContent := strings.Join(append(runAsCmds(powershell), cmd), "\n")

	tempDir, err := os.MkdirTemp("", "crcScripts")
	if err != nil {
		return "", "", err
	}
	defer os.RemoveAll(tempDir)

	// Write a temporary script
	/* Add UTF-8 BOM at the beginning of the script so that Windows
	 * correctly detects the file encoding
	 */
	filename := filepath.Join(tempDir, "runAsAdmin.ps1")

	// #nosec G306
	if err := os.WriteFile(filename, append([]byte{0xef, 0xbb, 0xbf}, []byte(scriptContent)...), 0666); err != nil {
		return "", "", err
	}

	logging.Infof("Will run as admin: %s", reason)

	return Execute(filename)
}

func runAsCmds(powershell string) []string {
	return []string{
		`$myWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent();`,
		`$myWindowsPrincipal = New-Object System.Security.Principal.WindowsPrincipal($myWindowsID);`,
		`$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator;`,
		`if (-Not ($myWindowsPrincipal.IsInRole($adminRole))) {`,
		`  $procInfo = New-Object System.Diagnostics.ProcessStartInfo;`,
		`  $procInfo.FileName = "` + powershell + `"`,
		`  $procInfo.WindowStyle = [Diagnostics.ProcessWindowStyle]::Hidden`,
		`  $procInfo.Arguments = "-ExecutionPolicy RemoteSigned & '" + $script:MyInvocation.MyCommand.Path + "'"`,
		`  $procInfo.Verb = "runas";`,
		`  $p = New-Object System.Diagnostics.Process`,
		`  $p.StartInfo = $procInfo`,
		`  $p.Start() | Out-Null`,
		`  $p.WaitForExit()`,
		`  if ($p.ExitCode -ne 0) {`,
		`    throw "Unexpected failure";`,
		`  }`,
		`  Exit;`,
		`}`,
	}
}