aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/app/seal.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/app/seal.go')
-rw-r--r--internal/app/seal.go154
1 files changed, 154 insertions, 0 deletions
diff --git a/internal/app/seal.go b/internal/app/seal.go
new file mode 100644
index 00000000..162faf40
--- /dev/null
+++ b/internal/app/seal.go
@@ -0,0 +1,154 @@
+package app
+
+import (
+ "errors"
+ "os"
+ "os/exec"
+ "os/user"
+ "strconv"
+
+ "git.ophivana.moe/cat/fortify/dbus"
+ "git.ophivana.moe/cat/fortify/internal"
+ "git.ophivana.moe/cat/fortify/internal/state"
+ "git.ophivana.moe/cat/fortify/internal/verbose"
+)
+
+const (
+ LaunchMethodSudo uint8 = iota
+ LaunchMethodBwrap
+ LaunchMethodMachineCtl
+)
+
+var (
+ ErrConfig = errors.New("no configuration to seal")
+ ErrUser = errors.New("unknown user")
+ ErrLaunch = errors.New("invalid launch method")
+
+ ErrSudo = errors.New("sudo not available")
+ ErrBwrap = errors.New("bwrap not available")
+ ErrSystemd = errors.New("systemd not available")
+ ErrMachineCtl = errors.New("machinectl not available")
+)
+
+type (
+ SealConfigError BaseError
+ LauncherLookupError BaseError
+ SecurityError BaseError
+)
+
+// Seal seals the app launch context
+func (a *app) Seal(config *Config) error {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+
+ if a.seal != nil {
+ panic("app sealed twice")
+ }
+
+ if config == nil {
+ return (*SealConfigError)(wrapError(ErrConfig, "attempted to seal app with nil config"))
+ }
+
+ // create seal
+ seal := new(appSeal)
+
+ // generate application ID
+ if id, err := newAppID(); err != nil {
+ return (*SecurityError)(wrapError(err, "cannot generate application ID:", err))
+ } else {
+ seal.id = id
+ }
+
+ // fetch system constants
+ seal.SystemConstants = internal.GetSC()
+
+ // pass through config values
+ seal.fid = config.ID
+ seal.command = config.Command
+
+ // parses launch method text and looks up tool path
+ switch config.Method {
+ case "sudo":
+ seal.launchOption = LaunchMethodSudo
+ if sudoPath, err := exec.LookPath("sudo"); err != nil {
+ return (*LauncherLookupError)(wrapError(ErrSudo, "sudo not found"))
+ } else {
+ seal.toolPath = sudoPath
+ }
+ case "bubblewrap":
+ seal.launchOption = LaunchMethodBwrap
+ if bwrapPath, err := exec.LookPath("bwrap"); err != nil {
+ return (*LauncherLookupError)(wrapError(ErrBwrap, "bwrap not found"))
+ } else {
+ seal.toolPath = bwrapPath
+ }
+ case "systemd":
+ seal.launchOption = LaunchMethodMachineCtl
+ if !internal.SdBootedV {
+ return (*LauncherLookupError)(wrapError(ErrSystemd,
+ "system has not been booted with systemd as init system"))
+ }
+
+ if machineCtlPath, err := exec.LookPath("machinectl"); err != nil {
+ return (*LauncherLookupError)(wrapError(ErrMachineCtl, "machinectl not found"))
+ } else {
+ seal.toolPath = machineCtlPath
+ }
+ default:
+ return (*SealConfigError)(wrapError(ErrLaunch, "invalid launch method"))
+ }
+
+ // create seal system component
+ seal.sys = new(appSealTx)
+
+ // look up fortify executable path
+ if p, err := os.Executable(); err != nil {
+ return (*LauncherLookupError)(wrapError(err, "cannot look up fortify executable path:", err))
+ } else {
+ seal.sys.executable = p
+ }
+
+ // look up user from system
+ if u, err := user.Lookup(config.User); err != nil {
+ if errors.As(err, new(user.UnknownUserError)) {
+ return (*SealConfigError)(wrapError(ErrUser, "unknown user", config.User))
+ } else {
+ // unreachable
+ panic(err)
+ }
+ } else {
+ seal.sys.User = u
+ }
+
+ // open process state store
+ // the simple store only starts holding an open file after first action
+ // store activity begins after Start is called and must end before Wait
+ seal.store = state.NewSimple(seal.SystemConstants.RunDirPath, seal.sys.Uid)
+
+ // parse string UID
+ if u, err := strconv.Atoi(seal.sys.Uid); err != nil {
+ // unreachable unless kernel bug
+ panic("uid parse")
+ } else {
+ seal.sys.uid = u
+ }
+
+ // pass through enablements
+ seal.et = config.Confinement.Enablements
+
+ // this method calls all share methods in sequence
+ if err := seal.shareAll([2]*dbus.Config{config.Confinement.SessionBus, config.Confinement.SystemBus}); err != nil {
+ return err
+ }
+
+ // verbose log seal information
+ verbose.Println("created application seal as user",
+ seal.sys.Username, "("+seal.sys.Uid+"),",
+ "method:", config.Method+",",
+ "launcher:", seal.toolPath+",",
+ "command:", config.Command)
+
+ // seal app and release lock
+ a.seal = seal
+ return nil
+}