diff --git a/config.go b/config.go index 228d3fe..26510e4 100644 --- a/config.go +++ b/config.go @@ -9,6 +9,7 @@ import ( "git.ophivana.moe/security/fortify/dbus" "git.ophivana.moe/security/fortify/internal" "git.ophivana.moe/security/fortify/internal/app" + "git.ophivana.moe/security/fortify/internal/fmsg" "git.ophivana.moe/security/fortify/internal/system" ) @@ -60,7 +61,7 @@ func init() { func tryTemplate() { if printTemplate { if s, err := json.MarshalIndent(app.Template(), "", " "); err != nil { - fatalf("cannot generate template: %v", err) + fmsg.Fatalf("cannot generate template: %v", err) panic("unreachable") } else { fmt.Println(string(s)) @@ -77,10 +78,10 @@ func loadConfig() *app.Config { // config from file c := new(app.Config) if f, err := os.Open(confPath); err != nil { - fatalf("cannot access config file '%s': %s\n", confPath, err) + fmsg.Fatalf("cannot access config file %q: %s", confPath, err) panic("unreachable") } else if err = json.NewDecoder(f).Decode(&c); err != nil { - fatalf("cannot parse config file '%s': %s\n", confPath, err) + fmsg.Fatalf("cannot parse config file %q: %s", confPath, err) panic("unreachable") } else { return c @@ -110,7 +111,7 @@ func configFromFlags() (config *app.Config) { config.Confinement.SessionBus = dbus.NewConfig(dbusID, true, mpris) } else { if c, err := dbus.NewConfigFromFile(dbusConfigSession); err != nil { - fatalf("cannot load session bus proxy config from %q: %s\n", dbusConfigSession, err) + fmsg.Fatalf("cannot load session bus proxy config from %q: %s", dbusConfigSession, err) } else { config.Confinement.SessionBus = c } @@ -119,7 +120,7 @@ func configFromFlags() (config *app.Config) { // system bus proxy is optional if dbusConfigSystem != "nil" { if c, err := dbus.NewConfigFromFile(dbusConfigSystem); err != nil { - fatalf("cannot load system bus proxy config from %q: %s\n", dbusConfigSystem, err) + fmsg.Fatalf("cannot load system bus proxy config from %q: %s", dbusConfigSystem, err) } else { config.Confinement.SystemBus = c } diff --git a/error.go b/error.go index 922a93b..112427a 100644 --- a/error.go +++ b/error.go @@ -3,7 +3,6 @@ package main import ( "errors" "fmt" - "os" "git.ophivana.moe/security/fortify/internal/app" "git.ophivana.moe/security/fortify/internal/fmsg" @@ -12,13 +11,13 @@ import ( func logWaitError(err error) { var e *fmsg.BaseError if !fmsg.AsBaseError(err, &e) { - fmt.Println("fortify: wait failed:", err) + fmsg.Println("wait failed:", err) } else { // Wait only returns either *app.ProcessError or *app.StateStoreError wrapped in a *app.BaseError var se *app.StateStoreError if !errors.As(err, &se) { // does not need special handling - fmt.Print("fortify: " + e.Message()) + fmsg.Print(e.Message()) } else { // inner error are either unwrapped store errors // or joined errors returned by *appSealTx revert @@ -26,7 +25,7 @@ func logWaitError(err error) { var ej app.RevertCompoundError if !errors.As(se.InnerErr, &ej) { // does not require special handling - fmt.Print("fortify: " + e.Message()) + fmsg.Print(e.Message()) } else { errs := ej.Unwrap() @@ -35,10 +34,10 @@ func logWaitError(err error) { var eb *fmsg.BaseError if !errors.As(ei, &eb) { // unreachable - fmt.Println("fortify: invalid error type returned by revert:", ei) + fmsg.Println("invalid error type returned by revert:", ei) } else { // print inner *app.BaseError message - fmt.Print("fortify: " + eb.Message()) + fmsg.Print(eb.Message()) } } } @@ -50,13 +49,8 @@ func logBaseError(err error, message string) { var e *fmsg.BaseError if fmsg.AsBaseError(err, &e) { - fmt.Print("fortify: " + e.Message()) + fmsg.Print(e.Message()) } else { fmt.Println(message, err) } } - -func fatalf(format string, a ...any) { - fmt.Printf("fortify: "+format, a...) - os.Exit(1) -} diff --git a/internal/app/launch.machinectl.go b/internal/app/launch.machinectl.go index c06e8a6..32f0674 100644 --- a/internal/app/launch.machinectl.go +++ b/internal/app/launch.machinectl.go @@ -4,7 +4,7 @@ import ( "os/exec" "strings" - "git.ophivana.moe/security/fortify/internal/verbose" + "git.ophivana.moe/security/fortify/internal/fmsg" ) func (a *app) commandBuilderMachineCtl(shimEnv string) (args []string) { @@ -14,7 +14,7 @@ func (a *app) commandBuilderMachineCtl(shimEnv string) (args []string) { args = append(args, "shell", "--uid="+a.seal.sys.user.Username) // --quiet - if !verbose.Get() { + if !fmsg.Verbose() { args = append(args, "--quiet") } diff --git a/internal/app/launch.sudo.go b/internal/app/launch.sudo.go index 129ef32..2df7be0 100644 --- a/internal/app/launch.sudo.go +++ b/internal/app/launch.sudo.go @@ -3,7 +3,7 @@ package app import ( "os" - "git.ophivana.moe/security/fortify/internal/verbose" + "git.ophivana.moe/security/fortify/internal/fmsg" ) const ( @@ -18,7 +18,7 @@ func (a *app) commandBuilderSudo(shimEnv string) (args []string) { // -A? if _, ok := os.LookupEnv(sudoAskPass); ok { - verbose.Printf("%s set, adding askpass flag\n", sudoAskPass) + fmsg.VPrintln(sudoAskPass, "set, adding askpass flag") args = append(args, "-A") } diff --git a/internal/app/seal.go b/internal/app/seal.go index 7de6aec..f0fe558 100644 --- a/internal/app/seal.go +++ b/internal/app/seal.go @@ -14,7 +14,6 @@ import ( "git.ophivana.moe/security/fortify/internal/shim" "git.ophivana.moe/security/fortify/internal/state" "git.ophivana.moe/security/fortify/internal/system" - "git.ophivana.moe/security/fortify/internal/verbose" ) const ( @@ -152,7 +151,7 @@ func (a *app) Seal(config *Config) error { // map sandbox config to bwrap if config.Confinement.Sandbox == nil { - verbose.Println("sandbox configuration not supplied, PROCEED WITH CAUTION") + fmsg.VPrintln("sandbox configuration not supplied, PROCEED WITH CAUTION") // permissive defaults conf := &SandboxConfig{ @@ -242,7 +241,7 @@ func (a *app) Seal(config *Config) error { } // verbose log seal information - verbose.Println("created application seal as user", + fmsg.VPrintln("created application seal as user", seal.sys.user.Username, "("+seal.sys.user.Uid+"),", "method:", config.Method+",", "launcher:", seal.toolPath+",", diff --git a/internal/app/start.go b/internal/app/start.go index 3106a1e..b6c9404 100644 --- a/internal/app/start.go +++ b/internal/app/start.go @@ -7,7 +7,6 @@ import ( "os/exec" "path" "path/filepath" - "strconv" "strings" "time" @@ -16,7 +15,6 @@ import ( "git.ophivana.moe/security/fortify/internal/shim" "git.ophivana.moe/security/fortify/internal/state" "git.ophivana.moe/security/fortify/internal/system" - "git.ophivana.moe/security/fortify/internal/verbose" ) // Start starts the fortified child @@ -71,14 +69,14 @@ func (a *app) Start() error { Bwrap: a.seal.sys.bwrap, WL: a.seal.wl != nil, - Verbose: verbose.Get(), + Verbose: fmsg.Verbose(), }, a.seal.wl); err != nil { return fmsg.WrapErrorSuffix(err, "cannot serve shim setup:") } // start shim - verbose.Println("starting shim as target user:", a.cmd) + fmsg.VPrintln("starting shim as target user:", a.cmd) if err := a.cmd.Start(); err != nil { return fmsg.WrapErrorSuffix(err, "cannot start process:") @@ -178,12 +176,12 @@ func (a *app) Wait() (int, error) { r = a.cmd.ProcessState.ExitCode() } - verbose.Println("process", strconv.Itoa(a.cmd.Process.Pid), "exited with exit code", r) + fmsg.VPrintf("process %d exited with exit code %d", a.cmd.Process.Pid, r) // close wayland connection if a.seal.wl != nil { if err := a.seal.wl.Close(); err != nil { - fmt.Println("fortify: cannot close wayland connection:", err) + fmsg.Println("cannot close wayland connection:", err) } } @@ -205,10 +203,10 @@ func (a *app) Wait() (int, error) { } else { if l := len(states); l == 0 { // cleanup globals as the final launcher - verbose.Println("no other launchers active, will clean up globals") + fmsg.VPrintln("no other launchers active, will clean up globals") ec.Set(system.User) } else { - verbose.Printf("found %d active launchers, cleaning up without globals\n", l) + fmsg.VPrintf("found %d active launchers, cleaning up without globals", l) } // accumulate capabilities of other launchers @@ -222,7 +220,7 @@ func (a *app) Wait() (int, error) { ec.Set(i) } } - if verbose.Get() { + if fmsg.Verbose() { labels := make([]string, 0, system.ELen+1) for i := system.Enablement(0); i < system.Enablement(system.ELen+2); i++ { if ec.Has(i) { @@ -230,7 +228,7 @@ func (a *app) Wait() (int, error) { } } if len(labels) > 0 { - verbose.Println("reverting operations labelled", strings.Join(labels, ", ")) + fmsg.VPrintln("reverting operations labelled", strings.Join(labels, ", ")) } } diff --git a/internal/early.go b/internal/early.go index 218105f..81ca49c 100644 --- a/internal/early.go +++ b/internal/early.go @@ -2,9 +2,10 @@ package internal import ( "errors" - "fmt" "io/fs" "os" + + "git.ophivana.moe/security/fortify/internal/fmsg" ) const ( @@ -13,7 +14,7 @@ const ( var SdBootedV = func() bool { if v, err := SdBooted(); err != nil { - fmt.Println("warn: read systemd marker:", err) + fmsg.Println("cannot read systemd marker:", err) return false } else { return v diff --git a/internal/environ.go b/internal/environ.go index 441c74a..8f03cb3 100644 --- a/internal/environ.go +++ b/internal/environ.go @@ -1,13 +1,12 @@ package internal import ( - "fmt" "os" "path" "strconv" "sync" - "git.ophivana.moe/security/fortify/internal/verbose" + "git.ophivana.moe/security/fortify/internal/fmsg" ) // state that remain constant for the lifetime of the process @@ -37,16 +36,16 @@ func copySC() { SharePath: path.Join(os.TempDir(), "fortify."+strconv.Itoa(os.Geteuid())), } - verbose.Println("process share directory at", sc.SharePath) + fmsg.VPrintf("process share directory at %q", sc.SharePath) // runtimePath, runDirPath if r, ok := os.LookupEnv(xdgRuntimeDir); !ok { - fmt.Println("Env variable", xdgRuntimeDir, "unset") + fmsg.Println("variable", xdgRuntimeDir, "unset") os.Exit(1) } else { sc.RuntimePath = r sc.RunDirPath = path.Join(sc.RuntimePath, "fortify") - verbose.Println("XDG runtime directory at", sc.RunDirPath) + fmsg.VPrintf("XDG runtime directory at %q", sc.RunDirPath) } scVal = sc diff --git a/internal/fmsg/fmsg.go b/internal/fmsg/fmsg.go index 40d3410..c72913f 100644 --- a/internal/fmsg/fmsg.go +++ b/internal/fmsg/fmsg.go @@ -1,2 +1,41 @@ // Package fmsg provides various functions for output messages. package fmsg + +import ( + "log" + "os" + "sync/atomic" +) + +var ( + std = log.New(os.Stdout, "fortify: ", 0) + warn = log.New(os.Stderr, "fortify: ", 0) + + verbose = new(atomic.Bool) +) + +func SetPrefix(prefix string) { + prefix += ": " + std.SetPrefix(prefix) + warn.SetPrefix(prefix) +} + +func Print(v ...any) { + warn.Print(v...) +} + +func Printf(format string, v ...any) { + warn.Printf(format, v...) +} + +func Println(v ...any) { + warn.Println(v...) +} + +func Fatal(v ...any) { + warn.Fatal(v...) +} + +func Fatalf(format string, v ...any) { + warn.Fatalf(format, v...) +} diff --git a/internal/fmsg/verbose.go b/internal/fmsg/verbose.go new file mode 100644 index 0000000..36faade --- /dev/null +++ b/internal/fmsg/verbose.go @@ -0,0 +1,21 @@ +package fmsg + +func Verbose() bool { + return verbose.Load() +} + +func SetVerbose(v bool) { + verbose.Store(v) +} + +func VPrintf(format string, v ...any) { + if verbose.Load() { + std.Printf(format, v...) + } +} + +func VPrintln(v ...any) { + if verbose.Load() { + std.Println(v...) + } +} diff --git a/internal/init/main.go b/internal/init/main.go index d0d8105..7a340a8 100644 --- a/internal/init/main.go +++ b/internal/init/main.go @@ -4,7 +4,6 @@ import ( "encoding/gob" "errors" "flag" - "fmt" "os" "os/exec" "os/signal" @@ -13,7 +12,7 @@ import ( "syscall" "time" - "git.ophivana.moe/security/fortify/internal/verbose" + "git.ophivana.moe/security/fortify/internal/fmsg" ) const ( @@ -25,49 +24,46 @@ const ( // proceed with caution! func doInit(fd uintptr) { + fmsg.SetPrefix("init") + // re-exec if len(os.Args) > 0 && os.Args[0] != "fortify" && path.IsAbs(os.Args[0]) { if err := syscall.Exec(os.Args[0], []string{"fortify", "init"}, os.Environ()); err != nil { - fmt.Println("fortify-init: cannot re-exec self:", err) + fmsg.Println("cannot re-exec self:", err) // continue anyway } } - verbose.Prefix = "fortify-init:" - var payload Payload p := os.NewFile(fd, "config-stream") if p == nil { - fmt.Println("fortify-init: invalid config descriptor") - os.Exit(1) + fmsg.Fatal("invalid config descriptor") } if err := gob.NewDecoder(p).Decode(&payload); err != nil { - fmt.Println("fortify-init: cannot decode init payload:", err) - os.Exit(1) + fmsg.Fatal("cannot decode init payload:", err) } else { // sharing stdout with parent // USE WITH CAUTION - verbose.Set(payload.Verbose) + fmsg.SetVerbose(payload.Verbose) // child does not need to see this if err = os.Unsetenv(EnvInit); err != nil { - fmt.Println("fortify-init: cannot unset", EnvInit+":", err) + fmsg.Println("cannot unset", EnvInit+":", err) // not fatal } else { - verbose.Println("received configuration") + fmsg.VPrintln("received configuration") } } // close config fd if err := p.Close(); err != nil { - fmt.Println("fortify-init: cannot close config fd:", err) + fmsg.Println("cannot close config fd:", err) // not fatal } // die with parent if _, _, errno := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGKILL), 0); errno != 0 { - fmt.Println("fortify-init: prctl(PR_SET_PDEATHSIG, SIGKILL):", errno.Error()) - os.Exit(1) + fmsg.Fatal("prctl(PR_SET_PDEATHSIG, SIGKILL):", errno.Error()) } cmd := exec.Command(payload.Argv0) @@ -84,8 +80,7 @@ func doInit(fd uintptr) { } if err := cmd.Start(); err != nil { - fmt.Printf("fortify-init: cannot start %q: %v", payload.Argv0, err) - os.Exit(1) + fmsg.Fatalf("cannot start %q: %v", payload.Argv0, err) } sig := make(chan os.Signal, 2) @@ -121,7 +116,7 @@ func doInit(fd uintptr) { } } if !errors.Is(err, syscall.ECHILD) { - fmt.Println("fortify-init: unexpected wait4 response:", err) + fmsg.Println("unexpected wait4 response:", err) } close(done) @@ -133,7 +128,7 @@ func doInit(fd uintptr) { for { select { case s := <-sig: - verbose.Println("received", s.String()) + fmsg.VPrintln("received", s.String()) os.Exit(0) case w := <-info: if w.wpid == cmd.Process.Pid { @@ -154,7 +149,7 @@ func doInit(fd uintptr) { case <-done: os.Exit(r) case <-timeout: - fmt.Println("fortify-init: timeout exceeded waiting for lingering processes") + fmsg.Println("timeout exceeded waiting for lingering processes") os.Exit(r) } } @@ -169,8 +164,7 @@ func Try() { if args := flag.Args(); len(args) == 1 && args[0] == "init" { if s, ok := os.LookupEnv(EnvInit); ok { if fd, err := strconv.Atoi(s); err != nil { - fmt.Printf("fortify-init: cannot parse %q: %v", s, err) - os.Exit(1) + fmsg.Fatalf("cannot parse %q: %v", s, err) } else { doInit(uintptr(fd)) } diff --git a/internal/shim/main.go b/internal/shim/main.go index 051d945..f53e8fb 100644 --- a/internal/shim/main.go +++ b/internal/shim/main.go @@ -4,7 +4,6 @@ import ( "encoding/gob" "errors" "flag" - "fmt" "net" "os" "path" @@ -12,29 +11,29 @@ import ( "syscall" "git.ophivana.moe/security/fortify/helper" + "git.ophivana.moe/security/fortify/internal/fmsg" init0 "git.ophivana.moe/security/fortify/internal/init" - "git.ophivana.moe/security/fortify/internal/verbose" ) // everything beyond this point runs as target user // proceed with caution! func doShim(socket string) { + fmsg.SetPrefix("shim") + // re-exec if len(os.Args) > 0 && os.Args[0] != "fortify" && path.IsAbs(os.Args[0]) { if err := syscall.Exec(os.Args[0], []string{"fortify", "shim"}, os.Environ()); err != nil { - fmt.Println("fortify-shim: cannot re-exec self:", err) + fmsg.Println("cannot re-exec self:", err) // continue anyway } } - verbose.Prefix = "fortify-shim:" - // dial setup socket var conn *net.UnixConn if c, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: socket, Net: "unix"}); err != nil { - fmt.Println("fortify-shim: cannot dial setup socket:", err) - os.Exit(1) + fmsg.Fatal("cannot dial setup socket:", err) + panic("unreachable") } else { conn = c } @@ -42,25 +41,22 @@ func doShim(socket string) { // decode payload gob stream var payload Payload if err := gob.NewDecoder(conn).Decode(&payload); err != nil { - fmt.Println("fortify-shim: cannot decode shim payload:", err) - os.Exit(1) + fmsg.Fatal("cannot decode shim payload:", err) } else { // sharing stdout with parent // USE WITH CAUTION - verbose.Set(payload.Verbose) + fmsg.SetVerbose(payload.Verbose) } if payload.Bwrap == nil { - fmt.Println("fortify-shim: bwrap config not supplied") - os.Exit(1) + fmsg.Fatal("bwrap config not supplied") } // receive wayland fd over socket wfd := -1 if payload.WL { if fd, err := receiveWLfd(conn); err != nil { - fmt.Println("fortify-shim: cannot receive wayland fd:", err) - os.Exit(1) + fmsg.Fatal("cannot receive wayland fd:", err) } else { wfd = fd } @@ -68,7 +64,7 @@ func doShim(socket string) { // close setup socket if err := conn.Close(); err != nil { - fmt.Println("fortify-shim: cannot close setup socket:", err) + fmsg.Println("cannot close setup socket:", err) // not fatal } @@ -83,8 +79,7 @@ func doShim(socket string) { // no argv, look up shell instead var ok bool if ic.Argv0, ok = os.LookupEnv("SHELL"); !ok { - fmt.Println("fortify-shim: no command was specified and $SHELL was unset") - os.Exit(1) + fmsg.Fatal("no command was specified and $SHELL was unset") } ic.Argv = []string{ic.Argv0} @@ -106,41 +101,37 @@ func doShim(socket string) { // share config pipe if r, w, err := os.Pipe(); err != nil { - fmt.Println("fortify-shim: cannot pipe:", err) - os.Exit(1) + fmsg.Fatal("cannot pipe:", err) } else { conf.SetEnv[init0.EnvInit] = strconv.Itoa(3 + len(extraFiles)) extraFiles = append(extraFiles, r) - verbose.Println("transmitting config to init") + fmsg.VPrintln("transmitting config to init") go func() { // stream config to pipe if err = gob.NewEncoder(w).Encode(&ic); err != nil { - fmt.Println("fortify-shim: cannot transmit init config:", err) - os.Exit(1) + fmsg.Fatal("cannot transmit init config:", err) } }() } helper.BubblewrapName = payload.Exec[1] // resolved bwrap path by parent if b, err := helper.NewBwrap(conf, nil, payload.Exec[0], func(int, int) []string { return []string{"init"} }); err != nil { - fmt.Println("fortify-shim: malformed sandbox config:", err) - os.Exit(1) + fmsg.Fatal("malformed sandbox config:", err) } else { cmd := b.Unwrap() cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr cmd.ExtraFiles = extraFiles - if verbose.Get() { - verbose.Println("bwrap args:", conf.Args()) + if fmsg.Verbose() { + fmsg.VPrintln("bwrap args:", conf.Args()) } // run and pass through exit code if err = b.Start(); err != nil { - fmt.Println("fortify-shim: cannot start target process:", err) - os.Exit(1) + fmsg.Fatal("cannot start target process:", err) } else if err = b.Wait(); err != nil { - verbose.Println("wait:", err) + fmsg.VPrintln("wait:", err) } if b.Unwrap().ProcessState != nil { os.Exit(b.Unwrap().ProcessState.ExitCode()) diff --git a/internal/shim/parent.go b/internal/shim/parent.go index 0e90383..6c393c5 100644 --- a/internal/shim/parent.go +++ b/internal/shim/parent.go @@ -3,13 +3,12 @@ package shim import ( "encoding/gob" "errors" - "fmt" "net" "os" "syscall" "git.ophivana.moe/security/fortify/acl" - "git.ophivana.moe/security/fortify/internal/verbose" + "git.ophivana.moe/security/fortify/internal/fmsg" ) // called in the parent process @@ -19,7 +18,7 @@ func ServeConfig(socket string, uid int, payload *Payload, wl *Wayland) error { if f, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: wl.Path, Net: "unix"}); err != nil { return err } else { - verbose.Println("connected to wayland at", wl) + fmsg.VPrintf("connected to wayland at %q", wl.Path) wl.UnixConn = f } } @@ -27,18 +26,18 @@ func ServeConfig(socket string, uid int, payload *Payload, wl *Wayland) error { if c, err := net.ListenUnix("unix", &net.UnixAddr{Name: socket, Net: "unix"}); err != nil { return err } else { - verbose.Println("configuring shim on socket", socket) + fmsg.VPrintf("configuring shim on socket %q", socket) if err = acl.UpdatePerm(socket, uid, acl.Read, acl.Write, acl.Execute); err != nil { - fmt.Println("fortify: cannot change permissions of shim setup socket:", err) + fmsg.Println("cannot change permissions of shim setup socket:", err) } go func() { var conn *net.UnixConn if conn, err = c.AcceptUnix(); err != nil { - fmt.Println("fortify: cannot accept connection from shim:", err) + fmsg.Println("cannot accept connection from shim:", err) } else { if err = gob.NewEncoder(conn).Encode(*payload); err != nil { - fmt.Println("fortify: cannot stream shim payload:", err) + fmsg.Println("cannot stream shim payload:", err) _ = os.Remove(socket) return } @@ -47,23 +46,23 @@ func ServeConfig(socket string, uid int, payload *Payload, wl *Wayland) error { // get raw connection var rc syscall.RawConn if rc, err = wl.SyscallConn(); err != nil { - fmt.Println("fortify: cannot obtain raw wayland connection:", err) + fmsg.Println("cannot obtain raw wayland connection:", err) return } else { go func() { // pass wayland socket fd if err = rc.Control(func(fd uintptr) { if _, _, err = conn.WriteMsgUnix(nil, syscall.UnixRights(int(fd)), nil); err != nil { - fmt.Println("fortify: cannot pass wayland connection to shim:", err) + fmsg.Println("cannot pass wayland connection to shim:", err) return } _ = conn.Close() // block until shim exits <-wl.done - verbose.Println("releasing wayland connection") + fmsg.VPrintln("releasing wayland connection") }); err != nil { - fmt.Println("fortify: cannot obtain wayland connection fd:", err) + fmsg.Println("cannot obtain wayland connection fd:", err) } }() } @@ -72,10 +71,10 @@ func ServeConfig(socket string, uid int, payload *Payload, wl *Wayland) error { } } if err = c.Close(); err != nil { - fmt.Println("fortify: cannot close shim socket:", err) + fmsg.Println("cannot close shim socket:", err) } if err = os.Remove(socket); err != nil && !errors.Is(err, os.ErrNotExist) { - fmt.Println("fortify: cannot remove dangling shim socket:", err) + fmsg.Println("cannot remove dangling shim socket:", err) } }() return nil diff --git a/internal/state/print.go b/internal/state/print.go index e04a690..d8f08fa 100644 --- a/internal/state/print.go +++ b/internal/state/print.go @@ -10,8 +10,8 @@ import ( "text/tabwriter" "time" + "git.ophivana.moe/security/fortify/internal/fmsg" "git.ophivana.moe/security/fortify/internal/system" - "git.ophivana.moe/security/fortify/internal/verbose" ) // MustPrintLauncherStateSimpleGlobal prints active launcher states of all simple stores @@ -21,19 +21,19 @@ func MustPrintLauncherStateSimpleGlobal(w **tabwriter.Writer, runDir string) { // read runtime directory to get all UIDs if dirs, err := os.ReadDir(path.Join(runDir, "state")); err != nil && !errors.Is(err, os.ErrNotExist) { - fmt.Println("cannot read runtime directory:", err) + fmsg.Println("cannot read runtime directory:", err) os.Exit(1) } else { for _, e := range dirs { // skip non-directories if !e.IsDir() { - verbose.Println("skipped non-directory entry", e.Name()) + fmsg.VPrintf("skipped non-directory entry %q", e.Name()) continue } // skip non-numerical names if _, err = strconv.Atoi(e.Name()); err != nil { - verbose.Println("skipped non-uid entry", e.Name()) + fmsg.VPrintf("skipped non-uid entry %q", e.Name()) continue } @@ -45,7 +45,7 @@ func MustPrintLauncherStateSimpleGlobal(w **tabwriter.Writer, runDir string) { // mustPrintLauncherState causes store activity so store needs to be closed if err = s.Close(); err != nil { - fmt.Printf("warn: error closing store for user %s: %s\n", e.Name(), err) + fmsg.Printf("cannot close store for user %q: %s", e.Name(), err) } } } @@ -67,7 +67,7 @@ func (s *simpleStore) mustPrintLauncherState(w **tabwriter.Writer, now time.Time *w = tabwriter.NewWriter(os.Stdout, 0, 1, 4, ' ', 0) // write header when initialising - if !verbose.Get() { + if !fmsg.Verbose() { _, _ = fmt.Fprintln(*w, "\tUID\tPID\tUptime\tEnablements\tMethod\tCommand") } else { // argv is emitted in body when verbose @@ -96,7 +96,7 @@ func (s *simpleStore) mustPrintLauncherState(w **tabwriter.Writer, now time.Time ets.WriteString("(No enablements)") } - if !verbose.Get() { + if !fmsg.Verbose() { _, _ = fmt.Fprintf(*w, "\t%s\t%d\t%s\t%s\t%s\t%s\n", s.path[len(s.path)-1], state.PID, now.Sub(state.Time).Round(time.Second).String(), strings.TrimPrefix(ets.String(), ", "), state.Method, state.Command) @@ -110,15 +110,15 @@ func (s *simpleStore) mustPrintLauncherState(w **tabwriter.Writer, now time.Time return nil }() }); err != nil { - fmt.Printf("cannot perform action on store '%s': %s\n", path.Join(s.path...), err) + fmsg.Printf("cannot perform action on store %q: %s", path.Join(s.path...), err) if !ok { - fmt.Println("warn: store faulted before printing") + fmsg.Println("store faulted before printing") os.Exit(1) } } if innerErr != nil { - fmt.Printf("cannot print launcher state for store '%s': %s\n", path.Join(s.path...), innerErr) + fmsg.Printf("cannot print launcher state for store %q: %s", path.Join(s.path...), innerErr) os.Exit(1) } } diff --git a/internal/system/acl.go b/internal/system/acl.go index e4bc935..f8f1dc6 100644 --- a/internal/system/acl.go +++ b/internal/system/acl.go @@ -6,7 +6,6 @@ import ( "git.ophivana.moe/security/fortify/acl" "git.ophivana.moe/security/fortify/internal/fmsg" - "git.ophivana.moe/security/fortify/internal/verbose" ) // UpdatePerm appends an ephemeral acl update Op. @@ -33,18 +32,20 @@ func (a *ACL) Type() Enablement { } func (a *ACL) apply(sys *I) error { - verbose.Println("applying ACL", a, "uid:", sys.uid, "type:", TypeString(a.et), "path:", a.path) + fmsg.VPrintf("applying ACL %s uid: %d type: %s path: %q", + a, sys.uid, TypeString(a.et), a.path) return fmsg.WrapErrorSuffix(acl.UpdatePerm(a.path, sys.uid, a.perms...), fmt.Sprintf("cannot apply ACL entry to %q:", a.path)) } func (a *ACL) revert(sys *I, ec *Criteria) error { if ec.hasType(a) { - verbose.Println("stripping ACL", a, "uid:", sys.uid, "type:", TypeString(a.et), "path:", a.path) + fmsg.VPrintf("stripping ACL %s uid: %d type: %s path: %q", + a, sys.uid, TypeString(a.et), a.path) return fmsg.WrapErrorSuffix(acl.UpdatePerm(a.path, sys.uid), fmt.Sprintf("cannot strip ACL entry from %q:", a.path)) } else { - verbose.Println("skipping ACL", a, "uid:", sys.uid, "tag:", TypeString(a.et), "path:", a.path) + fmsg.VPrintln("skipping ACL", a, "uid:", sys.uid, "tag:", TypeString(a.et), "path:", a.path) return nil } } diff --git a/internal/system/dbus.go b/internal/system/dbus.go index 8fe7d51..7c003e4 100644 --- a/internal/system/dbus.go +++ b/internal/system/dbus.go @@ -2,12 +2,10 @@ package system import ( "errors" - "fmt" "os" "git.ophivana.moe/security/fortify/dbus" "git.ophivana.moe/security/fortify/internal/fmsg" - "git.ophivana.moe/security/fortify/internal/verbose" ) var ( @@ -42,12 +40,12 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath st d.proxy = dbus.New(sessionBus, systemBus) defer func() { - if verbose.Get() && d.proxy.Sealed() { - verbose.Println("sealed session proxy", session.Args(sessionBus)) + if fmsg.Verbose() && d.proxy.Sealed() { + fmsg.VPrintln("sealed session proxy", session.Args(sessionBus)) if system != nil { - verbose.Println("sealed system proxy", system.Args(systemBus)) + fmsg.VPrintln("sealed system proxy", system.Args(systemBus)) } - verbose.Println("message bus proxy final args:", d.proxy) + fmsg.VPrintln("message bus proxy final args:", d.proxy) } }() @@ -73,9 +71,9 @@ func (d *DBus) Type() Enablement { } func (d *DBus) apply(_ *I) error { - verbose.Printf("session bus proxy on %q for upstream %q\n", d.proxy.Session()[1], d.proxy.Session()[0]) + fmsg.VPrintf("session bus proxy on %q for upstream %q", d.proxy.Session()[1], d.proxy.Session()[0]) if d.system { - verbose.Printf("system bus proxy on %q for upstream %q\n", d.proxy.System()[1], d.proxy.System()[0]) + fmsg.VPrintf("system bus proxy on %q for upstream %q", d.proxy.System()[1], d.proxy.System()[0]) } // ready channel passed to dbus package @@ -86,27 +84,27 @@ func (d *DBus) apply(_ *I) error { return fmsg.WrapErrorSuffix(err, "cannot start message bus proxy:") } - verbose.Println("starting message bus proxy:", d.proxy) - if verbose.Get() { // save the extra bwrap arg build when verbose logging is off - verbose.Println("message bus proxy bwrap args:", d.proxy.Bwrap()) + fmsg.VPrintln("starting message bus proxy:", d.proxy) + if fmsg.Verbose() { // save the extra bwrap arg build when verbose logging is off + fmsg.VPrintln("message bus proxy bwrap args:", d.proxy.Bwrap()) } // background wait for proxy instance and notify completion go func() { if err := d.proxy.Wait(); err != nil { - fmt.Println("fortify: message bus proxy exited with error:", err) + fmsg.Println("message bus proxy exited with error:", err) go func() { ready <- err }() } else { - verbose.Println("message bus proxy exit") + fmsg.VPrintln("message bus proxy exit") } // ensure socket removal so ephemeral directory is empty at revert if err := os.Remove(d.proxy.Session()[1]); err != nil && !errors.Is(err, os.ErrNotExist) { - fmt.Println("fortify: cannot remove dangling session bus socket:", err) + fmsg.Println("cannot remove dangling session bus socket:", err) } if d.system { if err := os.Remove(d.proxy.System()[1]); err != nil && !errors.Is(err, os.ErrNotExist) { - fmt.Println("fortify: cannot remove dangling system bus socket:", err) + fmsg.Println("cannot remove dangling system bus socket:", err) } } @@ -120,14 +118,14 @@ func (d *DBus) apply(_ *I) error { return fmsg.WrapErrorSuffix(err, "message bus proxy fault after start:") } - verbose.Println("message bus proxy ready") + fmsg.VPrintln("message bus proxy ready") return nil } func (d *DBus) revert(_ *I, _ *Criteria) error { // criteria ignored here since dbus is always process-scoped - verbose.Println("terminating message bus proxy") + fmsg.VPrintln("terminating message bus proxy") if err := d.proxy.Close(); err != nil { if errors.Is(err, os.ErrClosed) { diff --git a/internal/system/mkdir.go b/internal/system/mkdir.go index 89dcd48..0f3787f 100644 --- a/internal/system/mkdir.go +++ b/internal/system/mkdir.go @@ -6,7 +6,6 @@ import ( "os" "git.ophivana.moe/security/fortify/internal/fmsg" - "git.ophivana.moe/security/fortify/internal/verbose" ) // Ensure the existence and mode of a directory. @@ -37,7 +36,7 @@ func (m *Mkdir) Type() Enablement { } func (m *Mkdir) apply(_ *I) error { - verbose.Println("ensuring directory", m) + fmsg.VPrintln("ensuring directory", m) // create directory err := os.Mkdir(m.path, m.perm) @@ -58,11 +57,11 @@ func (m *Mkdir) revert(_ *I, ec *Criteria) error { } if ec.hasType(m) { - verbose.Println("destroying ephemeral directory", m) + fmsg.VPrintln("destroying ephemeral directory", m) return fmsg.WrapErrorSuffix(os.Remove(m.path), fmt.Sprintf("cannot remove ephemeral directory %q:", m.path)) } else { - verbose.Println("skipping ephemeral directory", m) + fmsg.VPrintln("skipping ephemeral directory", m) return nil } } diff --git a/internal/system/op.go b/internal/system/op.go index a904018..67a637b 100644 --- a/internal/system/op.go +++ b/internal/system/op.go @@ -2,8 +2,9 @@ package system import ( "errors" - "fmt" "sync" + + "git.ophivana.moe/security/fortify/internal/fmsg" ) const ( @@ -80,7 +81,7 @@ func (sys *I) Commit() error { if sp != nil { // rollback partial commit if err := sp.Revert(&Criteria{nil}); err != nil { - fmt.Println("fortify: errors returned reverting partial commit:", err) + fmsg.Println("errors returned reverting partial commit:", err) } } }() diff --git a/internal/system/tmpfiles.go b/internal/system/tmpfiles.go index 7b23e0e..9215bd3 100644 --- a/internal/system/tmpfiles.go +++ b/internal/system/tmpfiles.go @@ -9,7 +9,6 @@ import ( "git.ophivana.moe/security/fortify/acl" "git.ophivana.moe/security/fortify/internal/fmsg" - "git.ophivana.moe/security/fortify/internal/verbose" ) // CopyFile registers an Op that copies path dst from src. @@ -72,15 +71,15 @@ func (t *Tmpfile) Type() Enablement { func (t *Tmpfile) apply(_ *I) error { switch t.method { case tmpfileCopy: - verbose.Printf("publishing tmpfile %s\n", t) + fmsg.VPrintln("publishing tmpfile", t) return fmsg.WrapErrorSuffix(copyFile(t.dst, t.src), fmt.Sprintf("cannot copy tmpfile %q:", t.dst)) case tmpfileLink: - verbose.Printf("linking tmpfile %s\n", t) + fmsg.VPrintln("linking tmpfile", t) return fmsg.WrapErrorSuffix(os.Link(t.src, t.dst), fmt.Sprintf("cannot link tmpfile %q:", t.dst)) case tmpfileWrite: - verbose.Printf("writing %s\n", t) + fmsg.VPrintln("writing", t) return fmsg.WrapErrorSuffix(os.WriteFile(t.dst, []byte(t.src), 0600), fmt.Sprintf("cannot write tmpfile %q:", t.dst)) default: @@ -90,11 +89,11 @@ func (t *Tmpfile) apply(_ *I) error { func (t *Tmpfile) revert(_ *I, ec *Criteria) error { if ec.hasType(t) { - verbose.Printf("removing tmpfile %q\n", t.dst) + fmsg.VPrintf("removing tmpfile %q", t.dst) return fmsg.WrapErrorSuffix(os.Remove(t.dst), fmt.Sprintf("cannot remove tmpfile %q:", t.dst)) } else { - verbose.Printf("skipping tmpfile %q\n", t.dst) + fmsg.VPrintf("skipping tmpfile %q", t.dst) return nil } } diff --git a/internal/system/xhost.go b/internal/system/xhost.go index 4ed3f8b..51c8d24 100644 --- a/internal/system/xhost.go +++ b/internal/system/xhost.go @@ -4,7 +4,6 @@ import ( "fmt" "git.ophivana.moe/security/fortify/internal/fmsg" - "git.ophivana.moe/security/fortify/internal/verbose" "git.ophivana.moe/security/fortify/xcb" ) @@ -23,18 +22,18 @@ func (x XHost) Type() Enablement { } func (x XHost) apply(_ *I) error { - verbose.Printf("inserting entry %s to X11\n", x) + fmsg.VPrintf("inserting entry %s to X11", x) return fmsg.WrapErrorSuffix(xcb.ChangeHosts(xcb.HostModeInsert, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)), fmt.Sprintf("cannot insert entry %s to X11:", x)) } func (x XHost) revert(_ *I, ec *Criteria) error { if ec.hasType(x) { - verbose.Printf("deleting entry %s from X11\n", x) + fmsg.VPrintf("deleting entry %s from X11", x) return fmsg.WrapErrorSuffix(xcb.ChangeHosts(xcb.HostModeDelete, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)), fmt.Sprintf("cannot delete entry %s from X11:", x)) } else { - verbose.Printf("skipping entry %s in X11\n", x) + fmsg.VPrintf("skipping entry %s in X11", x) return nil } } diff --git a/internal/verbose/print.go b/internal/verbose/print.go deleted file mode 100644 index 3d0686b..0000000 --- a/internal/verbose/print.go +++ /dev/null @@ -1,19 +0,0 @@ -package verbose - -import ( - "fmt" -) - -var Prefix = "fortify:" - -func Println(a ...any) { - if verbose.Load() { - fmt.Println(append([]any{Prefix}, a...)...) - } -} - -func Printf(format string, a ...any) { - if verbose.Load() { - fmt.Printf(Prefix+" "+format, a...) - } -} diff --git a/internal/verbose/print_test.go b/internal/verbose/print_test.go deleted file mode 100644 index d74b70e..0000000 --- a/internal/verbose/print_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package verbose_test - -import ( - "os" - "os/exec" - "strconv" - "strings" - "testing" - - "git.ophivana.moe/security/fortify/internal/verbose" -) - -const ( - testVerbose = "GO_TEST_VERBOSE" - wantStdout = "fortify: println\nfortify: printf" -) - -func TestPrinter(t *testing.T) { - switch os.Getenv(testVerbose) { - case "0": - verbose.Set(false) - case "1": - verbose.Set(true) - default: - return - } - - verbose.Println("println") - verbose.Printf("%s", "printf") -} - -func TestPrintf_Println(t *testing.T) { - testPrintfPrintln(t, false) - testPrintfPrintln(t, true) - - // make -cover happy - stdout := os.Stdout - t.Cleanup(func() { - os.Stdout = stdout - }) - os.Stdout = nil - verbose.Set(true) - verbose.Printf("") - verbose.Println() -} - -func testPrintfPrintln(t *testing.T, v bool) { - t.Run("start verbose printer with verbose "+strconv.FormatBool(v), func(t *testing.T) { - stdout, stderr := new(strings.Builder), new(strings.Builder) - stdout.Grow(len(wantStdout)) - cmd := exec.Command(os.Args[0], "-test.run=TestPrinter") - cmd.Stdout, cmd.Stderr = stdout, stderr - if v { - cmd.Env = append(cmd.Env, testVerbose+"=1") - } else { - cmd.Env = append(cmd.Env, testVerbose+"=0") - } - if err := cmd.Run(); err != nil { - panic("cannot run printer process: " + err.Error() + " stderr: " + stderr.String()) - } - - if got := stdout.String(); strings.Contains(got, wantStdout) != v { - t.Errorf("Print: got %v; want %t", - got, v) - } - }) -} diff --git a/internal/verbose/state.go b/internal/verbose/state.go deleted file mode 100644 index e63e7a1..0000000 --- a/internal/verbose/state.go +++ /dev/null @@ -1,13 +0,0 @@ -package verbose - -import "sync/atomic" - -var verbose = new(atomic.Bool) - -func Get() bool { - return verbose.Load() -} - -func Set(v bool) { - verbose.Store(v) -} diff --git a/internal/verbose/state_test.go b/internal/verbose/state_test.go deleted file mode 100644 index f8179ee..0000000 --- a/internal/verbose/state_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package verbose_test - -import ( - "testing" - - "git.ophivana.moe/security/fortify/internal/verbose" -) - -func TestGetSet(t *testing.T) { - verbose.Set(false) - if verbose.Get() { - t.Errorf("Get() = true, want false") - } - - verbose.Set(true) - if !verbose.Get() { - t.Errorf("Get() = false, want true") - } - -} diff --git a/main.go b/main.go index da84f96..9a390af 100644 --- a/main.go +++ b/main.go @@ -2,15 +2,14 @@ package main import ( "flag" - "fmt" "os" "syscall" "git.ophivana.moe/security/fortify/internal" "git.ophivana.moe/security/fortify/internal/app" + "git.ophivana.moe/security/fortify/internal/fmsg" init0 "git.ophivana.moe/security/fortify/internal/init" "git.ophivana.moe/security/fortify/internal/shim" - "git.ophivana.moe/security/fortify/internal/verbose" ) var ( @@ -24,14 +23,14 @@ func init() { func main() { // linux/sched/coredump.h if _, _, errno := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_DUMPABLE, 0, 0); errno != 0 { - fmt.Printf("fortify: cannot set SUID_DUMP_DISABLE: %s", errno.Error()) + fmsg.Printf("fortify: cannot set SUID_DUMP_DISABLE: %s", errno.Error()) } flag.Parse() - verbose.Set(flagVerbose) + fmsg.SetVerbose(flagVerbose) if internal.SdBootedV { - verbose.Println("system booted with systemd as init system") + fmsg.VPrintln("system booted with systemd as init system") } // shim/init early exit @@ -40,7 +39,7 @@ func main() { // root check if os.Getuid() == 0 { - fmt.Println("fortify: this program must not run as root") + fmsg.Println("this program must not run as root") os.Exit(1) } @@ -56,7 +55,7 @@ func main() { r := 1 a, err := app.New() if err != nil { - fatalf("cannot create app: %s\n", err) + fmsg.Fatalf("cannot create app: %s\n", err) } else if err = a.Seal(loadConfig()); err != nil { logBaseError(err, "fortify: cannot seal app:") } else if err = a.Start(); err != nil { @@ -68,7 +67,7 @@ func main() { logWaitError(err) } if err = a.WaitErr(); err != nil { - fmt.Println("fortify: inner wait failed:", err) + fmsg.Println("inner wait failed:", err) } os.Exit(r) } diff --git a/state.go b/state.go index 0435526..c4551ac 100644 --- a/state.go +++ b/state.go @@ -7,6 +7,7 @@ import ( "text/tabwriter" "git.ophivana.moe/security/fortify/internal" + "git.ophivana.moe/security/fortify/internal/fmsg" "git.ophivana.moe/security/fortify/internal/state" ) @@ -25,7 +26,7 @@ func tryState() { state.MustPrintLauncherStateSimpleGlobal(&w, internal.GetSC().RunDirPath) if w != nil { if err := w.Flush(); err != nil { - fmt.Println("warn: error formatting output:", err) + fmsg.Println("cannot format output:", err) } } else { fmt.Println("No information available")