2024-09-04 01:20:12 +09:00
|
|
|
package app
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"os/user"
|
2024-09-16 20:31:15 +09:00
|
|
|
"path"
|
2024-09-04 01:20:12 +09:00
|
|
|
"strconv"
|
|
|
|
|
2024-09-17 13:48:42 +09:00
|
|
|
"git.ophivana.moe/cat/fortify/internal"
|
2024-09-12 20:53:33 +09:00
|
|
|
"git.ophivana.moe/cat/fortify/internal/util"
|
2024-09-12 21:07:05 +09:00
|
|
|
"git.ophivana.moe/cat/fortify/internal/verbose"
|
2024-09-04 01:20:12 +09:00
|
|
|
)
|
|
|
|
|
2024-09-16 20:31:15 +09:00
|
|
|
const (
|
|
|
|
xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
|
|
|
)
|
|
|
|
|
2024-09-04 01:20:12 +09:00
|
|
|
type App struct {
|
2024-09-16 20:31:15 +09:00
|
|
|
uid int // assigned
|
|
|
|
env []string // modified via AppendEnv
|
|
|
|
command []string // set on initialisation
|
2024-09-12 20:53:33 +09:00
|
|
|
|
2024-09-17 13:48:42 +09:00
|
|
|
exit *internal.ExitState // assigned
|
|
|
|
|
2024-09-16 20:31:15 +09:00
|
|
|
launchOptionText string // set on initialisation
|
|
|
|
launchOption uint8 // assigned
|
2024-09-04 01:20:12 +09:00
|
|
|
|
2024-09-16 20:31:15 +09:00
|
|
|
sharePath string // set on initialisation
|
|
|
|
runtimePath string // assigned
|
|
|
|
runDirPath string // assigned
|
|
|
|
toolPath string // assigned
|
2024-09-12 20:53:33 +09:00
|
|
|
|
2024-09-17 13:48:42 +09:00
|
|
|
enablements internal.Enablements // set via setEnablement
|
|
|
|
*user.User // assigned
|
2024-09-04 01:20:12 +09:00
|
|
|
|
2024-09-08 02:24:01 +09:00
|
|
|
// absolutely *no* method of this type is thread-safe
|
|
|
|
// so don't treat it as if it is
|
|
|
|
}
|
2024-09-04 01:20:12 +09:00
|
|
|
|
2024-09-12 20:53:33 +09:00
|
|
|
func (a *App) LaunchOption() uint8 {
|
|
|
|
return a.launchOption
|
|
|
|
}
|
|
|
|
|
2024-09-16 20:31:15 +09:00
|
|
|
func (a *App) RunDir() string {
|
|
|
|
return a.runDirPath
|
|
|
|
}
|
|
|
|
|
2024-09-17 13:48:42 +09:00
|
|
|
func (a *App) setEnablement(e internal.Enablement) {
|
2024-09-08 02:24:01 +09:00
|
|
|
if a.enablements.Has(e) {
|
|
|
|
panic("enablement " + e.String() + " set twice")
|
2024-09-04 01:20:12 +09:00
|
|
|
}
|
|
|
|
|
2024-09-08 02:24:01 +09:00
|
|
|
a.enablements |= e.Mask()
|
2024-09-04 01:20:12 +09:00
|
|
|
}
|
|
|
|
|
2024-09-17 13:48:42 +09:00
|
|
|
func (a *App) SealExit(exit *internal.ExitState) {
|
|
|
|
if a.exit != nil {
|
|
|
|
panic("application exit state sealed twice")
|
|
|
|
}
|
|
|
|
a.exit = exit
|
|
|
|
}
|
|
|
|
|
2024-09-12 20:53:33 +09:00
|
|
|
func New(userName string, args []string, launchOptionText string) *App {
|
2024-09-16 20:31:15 +09:00
|
|
|
a := &App{
|
|
|
|
command: args,
|
|
|
|
launchOptionText: launchOptionText,
|
|
|
|
sharePath: path.Join(os.TempDir(), "fortify."+strconv.Itoa(os.Geteuid())),
|
|
|
|
}
|
|
|
|
|
|
|
|
// runtimePath, runDirPath
|
|
|
|
if r, ok := os.LookupEnv(xdgRuntimeDir); !ok {
|
|
|
|
fmt.Println("Env variable", xdgRuntimeDir, "unset")
|
|
|
|
|
|
|
|
// too early for fatal
|
|
|
|
os.Exit(1)
|
|
|
|
} else {
|
|
|
|
a.runtimePath = r
|
|
|
|
a.runDirPath = path.Join(a.runtimePath, "fortify")
|
|
|
|
verbose.Println("Runtime directory at", a.runDirPath)
|
|
|
|
}
|
2024-09-04 01:20:12 +09:00
|
|
|
|
2024-09-16 20:31:15 +09:00
|
|
|
// *user.User
|
2024-09-04 01:20:12 +09:00
|
|
|
if u, err := user.Lookup(userName); err != nil {
|
|
|
|
if errors.As(err, new(user.UnknownUserError)) {
|
|
|
|
fmt.Println("unknown user", userName)
|
|
|
|
} else {
|
|
|
|
// unreachable
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// too early for fatal
|
|
|
|
os.Exit(1)
|
|
|
|
} else {
|
|
|
|
a.User = u
|
|
|
|
}
|
|
|
|
|
2024-09-16 20:31:15 +09:00
|
|
|
// uid
|
2024-09-04 01:20:12 +09:00
|
|
|
if u, err := strconv.Atoi(a.Uid); err != nil {
|
|
|
|
// usually unreachable
|
|
|
|
panic("uid parse")
|
|
|
|
} else {
|
|
|
|
a.uid = u
|
|
|
|
}
|
|
|
|
|
2024-09-12 21:07:05 +09:00
|
|
|
verbose.Println("Running as user", a.Username, "("+a.Uid+"),", "command:", a.command)
|
2024-09-17 13:48:42 +09:00
|
|
|
if internal.SdBootedV {
|
2024-09-12 21:07:05 +09:00
|
|
|
verbose.Println("System booted with systemd as init system (PID 1).")
|
2024-09-12 20:53:33 +09:00
|
|
|
}
|
|
|
|
|
2024-09-16 20:31:15 +09:00
|
|
|
// launchOption, toolPath
|
2024-09-12 20:53:33 +09:00
|
|
|
switch a.launchOptionText {
|
|
|
|
case "sudo":
|
|
|
|
a.launchOption = LaunchMethodSudo
|
|
|
|
if sudoPath, ok := util.Which("sudo"); !ok {
|
|
|
|
fmt.Println("Did not find 'sudo' in PATH")
|
|
|
|
os.Exit(1)
|
|
|
|
} else {
|
|
|
|
a.toolPath = sudoPath
|
|
|
|
}
|
|
|
|
case "bubblewrap":
|
|
|
|
a.launchOption = LaunchMethodBwrap
|
|
|
|
if bwrapPath, ok := util.Which("bwrap"); !ok {
|
|
|
|
fmt.Println("Did not find 'bwrap' in PATH")
|
|
|
|
os.Exit(1)
|
|
|
|
} else {
|
|
|
|
a.toolPath = bwrapPath
|
|
|
|
}
|
|
|
|
case "systemd":
|
|
|
|
a.launchOption = LaunchMethodMachineCtl
|
2024-09-17 13:48:42 +09:00
|
|
|
if !internal.SdBootedV {
|
2024-09-12 20:53:33 +09:00
|
|
|
fmt.Println("System has not been booted with systemd as init system (PID 1).")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
if machineCtlPath, ok := util.Which("machinectl"); !ok {
|
|
|
|
fmt.Println("Did not find 'machinectl' in PATH")
|
|
|
|
} else {
|
|
|
|
a.toolPath = machineCtlPath
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
fmt.Println("invalid launch method")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
2024-09-12 21:07:05 +09:00
|
|
|
verbose.Println("Determined launch method to be", a.launchOptionText, "with tool at", a.toolPath)
|
2024-09-04 01:20:12 +09:00
|
|
|
|
|
|
|
return a
|
|
|
|
}
|