From 42e0b168e3d75389dfb968aebf9f5629d64782f9 Mon Sep 17 00:00:00 2001 From: Ophestra Umiker Date: Mon, 21 Oct 2024 20:47:02 +0900 Subject: [PATCH] fmsg: produce all output through fmsg The behaviour of print functions from package fmt is not thread safe. Functions provided by fmsg wrap around Logger methods. This makes prefix much cleaner and makes it easy to deal with future changes to logging. Signed-off-by: Ophestra Umiker --- config.go | 11 ++--- error.go | 18 +++------ internal/app/launch.machinectl.go | 4 +- internal/app/launch.sudo.go | 4 +- internal/app/seal.go | 5 +-- internal/app/start.go | 18 ++++----- internal/early.go | 5 ++- internal/environ.go | 9 ++--- internal/fmsg/fmsg.go | 39 ++++++++++++++++++ internal/fmsg/verbose.go | 21 ++++++++++ internal/init/main.go | 38 ++++++++---------- internal/shim/main.go | 49 +++++++++------------- internal/shim/parent.go | 25 ++++++------ internal/state/print.go | 20 ++++----- internal/system/acl.go | 9 +++-- internal/system/dbus.go | 32 +++++++-------- internal/system/mkdir.go | 7 ++-- internal/system/op.go | 5 ++- internal/system/tmpfiles.go | 11 +++-- internal/system/xhost.go | 7 ++-- internal/verbose/print.go | 19 --------- internal/verbose/print_test.go | 67 ------------------------------- internal/verbose/state.go | 13 ------ internal/verbose/state_test.go | 20 --------- main.go | 15 ++++--- state.go | 3 +- 26 files changed, 194 insertions(+), 280 deletions(-) create mode 100644 internal/fmsg/verbose.go delete mode 100644 internal/verbose/print.go delete mode 100644 internal/verbose/print_test.go delete mode 100644 internal/verbose/state.go delete mode 100644 internal/verbose/state_test.go 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")