diff options
Diffstat (limited to 'internal/app/seal.go')
| -rw-r--r-- | internal/app/seal.go | 154 |
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 +} |
