fmsg: produce all output through fmsg
test / test (push) Successful in 17s Details

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 <cat@ophivana.moe>
This commit is contained in:
Ophestra Umiker 2024-10-21 20:47:02 +09:00
parent 380d1f4585
commit 42e0b168e3
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
26 changed files with 194 additions and 280 deletions

View File

@ -9,6 +9,7 @@ import (
"git.ophivana.moe/security/fortify/dbus" "git.ophivana.moe/security/fortify/dbus"
"git.ophivana.moe/security/fortify/internal" "git.ophivana.moe/security/fortify/internal"
"git.ophivana.moe/security/fortify/internal/app" "git.ophivana.moe/security/fortify/internal/app"
"git.ophivana.moe/security/fortify/internal/fmsg"
"git.ophivana.moe/security/fortify/internal/system" "git.ophivana.moe/security/fortify/internal/system"
) )
@ -60,7 +61,7 @@ func init() {
func tryTemplate() { func tryTemplate() {
if printTemplate { if printTemplate {
if s, err := json.MarshalIndent(app.Template(), "", " "); err != nil { if s, err := json.MarshalIndent(app.Template(), "", " "); err != nil {
fatalf("cannot generate template: %v", err) fmsg.Fatalf("cannot generate template: %v", err)
panic("unreachable") panic("unreachable")
} else { } else {
fmt.Println(string(s)) fmt.Println(string(s))
@ -77,10 +78,10 @@ func loadConfig() *app.Config {
// config from file // config from file
c := new(app.Config) c := new(app.Config)
if f, err := os.Open(confPath); err != nil { 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") panic("unreachable")
} else if err = json.NewDecoder(f).Decode(&c); err != nil { } 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") panic("unreachable")
} else { } else {
return c return c
@ -110,7 +111,7 @@ func configFromFlags() (config *app.Config) {
config.Confinement.SessionBus = dbus.NewConfig(dbusID, true, mpris) config.Confinement.SessionBus = dbus.NewConfig(dbusID, true, mpris)
} else { } else {
if c, err := dbus.NewConfigFromFile(dbusConfigSession); err != nil { 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 { } else {
config.Confinement.SessionBus = c config.Confinement.SessionBus = c
} }
@ -119,7 +120,7 @@ func configFromFlags() (config *app.Config) {
// system bus proxy is optional // system bus proxy is optional
if dbusConfigSystem != "nil" { if dbusConfigSystem != "nil" {
if c, err := dbus.NewConfigFromFile(dbusConfigSystem); err != 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 { } else {
config.Confinement.SystemBus = c config.Confinement.SystemBus = c
} }

View File

@ -3,7 +3,6 @@ package main
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"git.ophivana.moe/security/fortify/internal/app" "git.ophivana.moe/security/fortify/internal/app"
"git.ophivana.moe/security/fortify/internal/fmsg" "git.ophivana.moe/security/fortify/internal/fmsg"
@ -12,13 +11,13 @@ import (
func logWaitError(err error) { func logWaitError(err error) {
var e *fmsg.BaseError var e *fmsg.BaseError
if !fmsg.AsBaseError(err, &e) { if !fmsg.AsBaseError(err, &e) {
fmt.Println("fortify: wait failed:", err) fmsg.Println("wait failed:", err)
} else { } else {
// Wait only returns either *app.ProcessError or *app.StateStoreError wrapped in a *app.BaseError // Wait only returns either *app.ProcessError or *app.StateStoreError wrapped in a *app.BaseError
var se *app.StateStoreError var se *app.StateStoreError
if !errors.As(err, &se) { if !errors.As(err, &se) {
// does not need special handling // does not need special handling
fmt.Print("fortify: " + e.Message()) fmsg.Print(e.Message())
} else { } else {
// inner error are either unwrapped store errors // inner error are either unwrapped store errors
// or joined errors returned by *appSealTx revert // or joined errors returned by *appSealTx revert
@ -26,7 +25,7 @@ func logWaitError(err error) {
var ej app.RevertCompoundError var ej app.RevertCompoundError
if !errors.As(se.InnerErr, &ej) { if !errors.As(se.InnerErr, &ej) {
// does not require special handling // does not require special handling
fmt.Print("fortify: " + e.Message()) fmsg.Print(e.Message())
} else { } else {
errs := ej.Unwrap() errs := ej.Unwrap()
@ -35,10 +34,10 @@ func logWaitError(err error) {
var eb *fmsg.BaseError var eb *fmsg.BaseError
if !errors.As(ei, &eb) { if !errors.As(ei, &eb) {
// unreachable // unreachable
fmt.Println("fortify: invalid error type returned by revert:", ei) fmsg.Println("invalid error type returned by revert:", ei)
} else { } else {
// print inner *app.BaseError message // 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 var e *fmsg.BaseError
if fmsg.AsBaseError(err, &e) { if fmsg.AsBaseError(err, &e) {
fmt.Print("fortify: " + e.Message()) fmsg.Print(e.Message())
} else { } else {
fmt.Println(message, err) fmt.Println(message, err)
} }
} }
func fatalf(format string, a ...any) {
fmt.Printf("fortify: "+format, a...)
os.Exit(1)
}

View File

@ -4,7 +4,7 @@ import (
"os/exec" "os/exec"
"strings" "strings"
"git.ophivana.moe/security/fortify/internal/verbose" "git.ophivana.moe/security/fortify/internal/fmsg"
) )
func (a *app) commandBuilderMachineCtl(shimEnv string) (args []string) { 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) args = append(args, "shell", "--uid="+a.seal.sys.user.Username)
// --quiet // --quiet
if !verbose.Get() { if !fmsg.Verbose() {
args = append(args, "--quiet") args = append(args, "--quiet")
} }

View File

@ -3,7 +3,7 @@ package app
import ( import (
"os" "os"
"git.ophivana.moe/security/fortify/internal/verbose" "git.ophivana.moe/security/fortify/internal/fmsg"
) )
const ( const (
@ -18,7 +18,7 @@ func (a *app) commandBuilderSudo(shimEnv string) (args []string) {
// -A? // -A?
if _, ok := os.LookupEnv(sudoAskPass); ok { 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") args = append(args, "-A")
} }

View File

@ -14,7 +14,6 @@ import (
"git.ophivana.moe/security/fortify/internal/shim" "git.ophivana.moe/security/fortify/internal/shim"
"git.ophivana.moe/security/fortify/internal/state" "git.ophivana.moe/security/fortify/internal/state"
"git.ophivana.moe/security/fortify/internal/system" "git.ophivana.moe/security/fortify/internal/system"
"git.ophivana.moe/security/fortify/internal/verbose"
) )
const ( const (
@ -152,7 +151,7 @@ func (a *app) Seal(config *Config) error {
// map sandbox config to bwrap // map sandbox config to bwrap
if config.Confinement.Sandbox == nil { 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 // permissive defaults
conf := &SandboxConfig{ conf := &SandboxConfig{
@ -242,7 +241,7 @@ func (a *app) Seal(config *Config) error {
} }
// verbose log seal information // 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+"),", seal.sys.user.Username, "("+seal.sys.user.Uid+"),",
"method:", config.Method+",", "method:", config.Method+",",
"launcher:", seal.toolPath+",", "launcher:", seal.toolPath+",",

View File

@ -7,7 +7,6 @@ import (
"os/exec" "os/exec"
"path" "path"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"time" "time"
@ -16,7 +15,6 @@ import (
"git.ophivana.moe/security/fortify/internal/shim" "git.ophivana.moe/security/fortify/internal/shim"
"git.ophivana.moe/security/fortify/internal/state" "git.ophivana.moe/security/fortify/internal/state"
"git.ophivana.moe/security/fortify/internal/system" "git.ophivana.moe/security/fortify/internal/system"
"git.ophivana.moe/security/fortify/internal/verbose"
) )
// Start starts the fortified child // Start starts the fortified child
@ -71,14 +69,14 @@ func (a *app) Start() error {
Bwrap: a.seal.sys.bwrap, Bwrap: a.seal.sys.bwrap,
WL: a.seal.wl != nil, WL: a.seal.wl != nil,
Verbose: verbose.Get(), Verbose: fmsg.Verbose(),
}, a.seal.wl); err != nil { }, a.seal.wl); err != nil {
return fmsg.WrapErrorSuffix(err, return fmsg.WrapErrorSuffix(err,
"cannot serve shim setup:") "cannot serve shim setup:")
} }
// start shim // 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 { if err := a.cmd.Start(); err != nil {
return fmsg.WrapErrorSuffix(err, return fmsg.WrapErrorSuffix(err,
"cannot start process:") "cannot start process:")
@ -178,12 +176,12 @@ func (a *app) Wait() (int, error) {
r = a.cmd.ProcessState.ExitCode() 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 // close wayland connection
if a.seal.wl != nil { if a.seal.wl != nil {
if err := a.seal.wl.Close(); err != 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 { } else {
if l := len(states); l == 0 { if l := len(states); l == 0 {
// cleanup globals as the final launcher // 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) ec.Set(system.User)
} else { } 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 // accumulate capabilities of other launchers
@ -222,7 +220,7 @@ func (a *app) Wait() (int, error) {
ec.Set(i) ec.Set(i)
} }
} }
if verbose.Get() { if fmsg.Verbose() {
labels := make([]string, 0, system.ELen+1) labels := make([]string, 0, system.ELen+1)
for i := system.Enablement(0); i < system.Enablement(system.ELen+2); i++ { for i := system.Enablement(0); i < system.Enablement(system.ELen+2); i++ {
if ec.Has(i) { if ec.Has(i) {
@ -230,7 +228,7 @@ func (a *app) Wait() (int, error) {
} }
} }
if len(labels) > 0 { if len(labels) > 0 {
verbose.Println("reverting operations labelled", strings.Join(labels, ", ")) fmsg.VPrintln("reverting operations labelled", strings.Join(labels, ", "))
} }
} }

View File

@ -2,9 +2,10 @@ package internal
import ( import (
"errors" "errors"
"fmt"
"io/fs" "io/fs"
"os" "os"
"git.ophivana.moe/security/fortify/internal/fmsg"
) )
const ( const (
@ -13,7 +14,7 @@ const (
var SdBootedV = func() bool { var SdBootedV = func() bool {
if v, err := SdBooted(); err != nil { if v, err := SdBooted(); err != nil {
fmt.Println("warn: read systemd marker:", err) fmsg.Println("cannot read systemd marker:", err)
return false return false
} else { } else {
return v return v

View File

@ -1,13 +1,12 @@
package internal package internal
import ( import (
"fmt"
"os" "os"
"path" "path"
"strconv" "strconv"
"sync" "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 // 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())), 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 // runtimePath, runDirPath
if r, ok := os.LookupEnv(xdgRuntimeDir); !ok { if r, ok := os.LookupEnv(xdgRuntimeDir); !ok {
fmt.Println("Env variable", xdgRuntimeDir, "unset") fmsg.Println("variable", xdgRuntimeDir, "unset")
os.Exit(1) os.Exit(1)
} else { } else {
sc.RuntimePath = r sc.RuntimePath = r
sc.RunDirPath = path.Join(sc.RuntimePath, "fortify") 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 scVal = sc

View File

@ -1,2 +1,41 @@
// Package fmsg provides various functions for output messages. // Package fmsg provides various functions for output messages.
package fmsg 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...)
}

21
internal/fmsg/verbose.go Normal file
View File

@ -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...)
}
}

View File

@ -4,7 +4,6 @@ import (
"encoding/gob" "encoding/gob"
"errors" "errors"
"flag" "flag"
"fmt"
"os" "os"
"os/exec" "os/exec"
"os/signal" "os/signal"
@ -13,7 +12,7 @@ import (
"syscall" "syscall"
"time" "time"
"git.ophivana.moe/security/fortify/internal/verbose" "git.ophivana.moe/security/fortify/internal/fmsg"
) )
const ( const (
@ -25,49 +24,46 @@ const (
// proceed with caution! // proceed with caution!
func doInit(fd uintptr) { func doInit(fd uintptr) {
fmsg.SetPrefix("init")
// re-exec // re-exec
if len(os.Args) > 0 && os.Args[0] != "fortify" && path.IsAbs(os.Args[0]) { 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 { 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 // continue anyway
} }
} }
verbose.Prefix = "fortify-init:"
var payload Payload var payload Payload
p := os.NewFile(fd, "config-stream") p := os.NewFile(fd, "config-stream")
if p == nil { if p == nil {
fmt.Println("fortify-init: invalid config descriptor") fmsg.Fatal("invalid config descriptor")
os.Exit(1)
} }
if err := gob.NewDecoder(p).Decode(&payload); err != nil { if err := gob.NewDecoder(p).Decode(&payload); err != nil {
fmt.Println("fortify-init: cannot decode init payload:", err) fmsg.Fatal("cannot decode init payload:", err)
os.Exit(1)
} else { } else {
// sharing stdout with parent // sharing stdout with parent
// USE WITH CAUTION // USE WITH CAUTION
verbose.Set(payload.Verbose) fmsg.SetVerbose(payload.Verbose)
// child does not need to see this // child does not need to see this
if err = os.Unsetenv(EnvInit); err != nil { if err = os.Unsetenv(EnvInit); err != nil {
fmt.Println("fortify-init: cannot unset", EnvInit+":", err) fmsg.Println("cannot unset", EnvInit+":", err)
// not fatal // not fatal
} else { } else {
verbose.Println("received configuration") fmsg.VPrintln("received configuration")
} }
} }
// close config fd // close config fd
if err := p.Close(); err != nil { if err := p.Close(); err != nil {
fmt.Println("fortify-init: cannot close config fd:", err) fmsg.Println("cannot close config fd:", err)
// not fatal // not fatal
} }
// die with parent // die with parent
if _, _, errno := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGKILL), 0); errno != 0 { 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()) fmsg.Fatal("prctl(PR_SET_PDEATHSIG, SIGKILL):", errno.Error())
os.Exit(1)
} }
cmd := exec.Command(payload.Argv0) cmd := exec.Command(payload.Argv0)
@ -84,8 +80,7 @@ func doInit(fd uintptr) {
} }
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
fmt.Printf("fortify-init: cannot start %q: %v", payload.Argv0, err) fmsg.Fatalf("cannot start %q: %v", payload.Argv0, err)
os.Exit(1)
} }
sig := make(chan os.Signal, 2) sig := make(chan os.Signal, 2)
@ -121,7 +116,7 @@ func doInit(fd uintptr) {
} }
} }
if !errors.Is(err, syscall.ECHILD) { if !errors.Is(err, syscall.ECHILD) {
fmt.Println("fortify-init: unexpected wait4 response:", err) fmsg.Println("unexpected wait4 response:", err)
} }
close(done) close(done)
@ -133,7 +128,7 @@ func doInit(fd uintptr) {
for { for {
select { select {
case s := <-sig: case s := <-sig:
verbose.Println("received", s.String()) fmsg.VPrintln("received", s.String())
os.Exit(0) os.Exit(0)
case w := <-info: case w := <-info:
if w.wpid == cmd.Process.Pid { if w.wpid == cmd.Process.Pid {
@ -154,7 +149,7 @@ func doInit(fd uintptr) {
case <-done: case <-done:
os.Exit(r) os.Exit(r)
case <-timeout: case <-timeout:
fmt.Println("fortify-init: timeout exceeded waiting for lingering processes") fmsg.Println("timeout exceeded waiting for lingering processes")
os.Exit(r) os.Exit(r)
} }
} }
@ -169,8 +164,7 @@ func Try() {
if args := flag.Args(); len(args) == 1 && args[0] == "init" { if args := flag.Args(); len(args) == 1 && args[0] == "init" {
if s, ok := os.LookupEnv(EnvInit); ok { if s, ok := os.LookupEnv(EnvInit); ok {
if fd, err := strconv.Atoi(s); err != nil { if fd, err := strconv.Atoi(s); err != nil {
fmt.Printf("fortify-init: cannot parse %q: %v", s, err) fmsg.Fatalf("cannot parse %q: %v", s, err)
os.Exit(1)
} else { } else {
doInit(uintptr(fd)) doInit(uintptr(fd))
} }

View File

@ -4,7 +4,6 @@ import (
"encoding/gob" "encoding/gob"
"errors" "errors"
"flag" "flag"
"fmt"
"net" "net"
"os" "os"
"path" "path"
@ -12,29 +11,29 @@ import (
"syscall" "syscall"
"git.ophivana.moe/security/fortify/helper" "git.ophivana.moe/security/fortify/helper"
"git.ophivana.moe/security/fortify/internal/fmsg"
init0 "git.ophivana.moe/security/fortify/internal/init" init0 "git.ophivana.moe/security/fortify/internal/init"
"git.ophivana.moe/security/fortify/internal/verbose"
) )
// everything beyond this point runs as target user // everything beyond this point runs as target user
// proceed with caution! // proceed with caution!
func doShim(socket string) { func doShim(socket string) {
fmsg.SetPrefix("shim")
// re-exec // re-exec
if len(os.Args) > 0 && os.Args[0] != "fortify" && path.IsAbs(os.Args[0]) { 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 { 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 // continue anyway
} }
} }
verbose.Prefix = "fortify-shim:"
// dial setup socket // dial setup socket
var conn *net.UnixConn var conn *net.UnixConn
if c, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: socket, Net: "unix"}); err != nil { if c, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: socket, Net: "unix"}); err != nil {
fmt.Println("fortify-shim: cannot dial setup socket:", err) fmsg.Fatal("cannot dial setup socket:", err)
os.Exit(1) panic("unreachable")
} else { } else {
conn = c conn = c
} }
@ -42,25 +41,22 @@ func doShim(socket string) {
// decode payload gob stream // decode payload gob stream
var payload Payload var payload Payload
if err := gob.NewDecoder(conn).Decode(&payload); err != nil { if err := gob.NewDecoder(conn).Decode(&payload); err != nil {
fmt.Println("fortify-shim: cannot decode shim payload:", err) fmsg.Fatal("cannot decode shim payload:", err)
os.Exit(1)
} else { } else {
// sharing stdout with parent // sharing stdout with parent
// USE WITH CAUTION // USE WITH CAUTION
verbose.Set(payload.Verbose) fmsg.SetVerbose(payload.Verbose)
} }
if payload.Bwrap == nil { if payload.Bwrap == nil {
fmt.Println("fortify-shim: bwrap config not supplied") fmsg.Fatal("bwrap config not supplied")
os.Exit(1)
} }
// receive wayland fd over socket // receive wayland fd over socket
wfd := -1 wfd := -1
if payload.WL { if payload.WL {
if fd, err := receiveWLfd(conn); err != nil { if fd, err := receiveWLfd(conn); err != nil {
fmt.Println("fortify-shim: cannot receive wayland fd:", err) fmsg.Fatal("cannot receive wayland fd:", err)
os.Exit(1)
} else { } else {
wfd = fd wfd = fd
} }
@ -68,7 +64,7 @@ func doShim(socket string) {
// close setup socket // close setup socket
if err := conn.Close(); err != nil { if err := conn.Close(); err != nil {
fmt.Println("fortify-shim: cannot close setup socket:", err) fmsg.Println("cannot close setup socket:", err)
// not fatal // not fatal
} }
@ -83,8 +79,7 @@ func doShim(socket string) {
// no argv, look up shell instead // no argv, look up shell instead
var ok bool var ok bool
if ic.Argv0, ok = os.LookupEnv("SHELL"); !ok { if ic.Argv0, ok = os.LookupEnv("SHELL"); !ok {
fmt.Println("fortify-shim: no command was specified and $SHELL was unset") fmsg.Fatal("no command was specified and $SHELL was unset")
os.Exit(1)
} }
ic.Argv = []string{ic.Argv0} ic.Argv = []string{ic.Argv0}
@ -106,41 +101,37 @@ func doShim(socket string) {
// share config pipe // share config pipe
if r, w, err := os.Pipe(); err != nil { if r, w, err := os.Pipe(); err != nil {
fmt.Println("fortify-shim: cannot pipe:", err) fmsg.Fatal("cannot pipe:", err)
os.Exit(1)
} else { } else {
conf.SetEnv[init0.EnvInit] = strconv.Itoa(3 + len(extraFiles)) conf.SetEnv[init0.EnvInit] = strconv.Itoa(3 + len(extraFiles))
extraFiles = append(extraFiles, r) extraFiles = append(extraFiles, r)
verbose.Println("transmitting config to init") fmsg.VPrintln("transmitting config to init")
go func() { go func() {
// stream config to pipe // stream config to pipe
if err = gob.NewEncoder(w).Encode(&ic); err != nil { if err = gob.NewEncoder(w).Encode(&ic); err != nil {
fmt.Println("fortify-shim: cannot transmit init config:", err) fmsg.Fatal("cannot transmit init config:", err)
os.Exit(1)
} }
}() }()
} }
helper.BubblewrapName = payload.Exec[1] // resolved bwrap path by parent 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 { 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) fmsg.Fatal("malformed sandbox config:", err)
os.Exit(1)
} else { } else {
cmd := b.Unwrap() cmd := b.Unwrap()
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
cmd.ExtraFiles = extraFiles cmd.ExtraFiles = extraFiles
if verbose.Get() { if fmsg.Verbose() {
verbose.Println("bwrap args:", conf.Args()) fmsg.VPrintln("bwrap args:", conf.Args())
} }
// run and pass through exit code // run and pass through exit code
if err = b.Start(); err != nil { if err = b.Start(); err != nil {
fmt.Println("fortify-shim: cannot start target process:", err) fmsg.Fatal("cannot start target process:", err)
os.Exit(1)
} else if err = b.Wait(); err != nil { } else if err = b.Wait(); err != nil {
verbose.Println("wait:", err) fmsg.VPrintln("wait:", err)
} }
if b.Unwrap().ProcessState != nil { if b.Unwrap().ProcessState != nil {
os.Exit(b.Unwrap().ProcessState.ExitCode()) os.Exit(b.Unwrap().ProcessState.ExitCode())

View File

@ -3,13 +3,12 @@ package shim
import ( import (
"encoding/gob" "encoding/gob"
"errors" "errors"
"fmt"
"net" "net"
"os" "os"
"syscall" "syscall"
"git.ophivana.moe/security/fortify/acl" "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 // 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 { if f, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: wl.Path, Net: "unix"}); err != nil {
return err return err
} else { } else {
verbose.Println("connected to wayland at", wl) fmsg.VPrintf("connected to wayland at %q", wl.Path)
wl.UnixConn = f 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 { if c, err := net.ListenUnix("unix", &net.UnixAddr{Name: socket, Net: "unix"}); err != nil {
return err return err
} else { } 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 { 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() { go func() {
var conn *net.UnixConn var conn *net.UnixConn
if conn, err = c.AcceptUnix(); err != nil { 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 { } else {
if err = gob.NewEncoder(conn).Encode(*payload); err != nil { 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) _ = os.Remove(socket)
return return
} }
@ -47,23 +46,23 @@ func ServeConfig(socket string, uid int, payload *Payload, wl *Wayland) error {
// get raw connection // get raw connection
var rc syscall.RawConn var rc syscall.RawConn
if rc, err = wl.SyscallConn(); err != nil { 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 return
} else { } else {
go func() { go func() {
// pass wayland socket fd // pass wayland socket fd
if err = rc.Control(func(fd uintptr) { if err = rc.Control(func(fd uintptr) {
if _, _, err = conn.WriteMsgUnix(nil, syscall.UnixRights(int(fd)), nil); err != nil { 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 return
} }
_ = conn.Close() _ = conn.Close()
// block until shim exits // block until shim exits
<-wl.done <-wl.done
verbose.Println("releasing wayland connection") fmsg.VPrintln("releasing wayland connection")
}); err != nil { }); 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 { 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) { 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 return nil

View File

@ -10,8 +10,8 @@ import (
"text/tabwriter" "text/tabwriter"
"time" "time"
"git.ophivana.moe/security/fortify/internal/fmsg"
"git.ophivana.moe/security/fortify/internal/system" "git.ophivana.moe/security/fortify/internal/system"
"git.ophivana.moe/security/fortify/internal/verbose"
) )
// MustPrintLauncherStateSimpleGlobal prints active launcher states of all simple stores // 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 // read runtime directory to get all UIDs
if dirs, err := os.ReadDir(path.Join(runDir, "state")); err != nil && !errors.Is(err, os.ErrNotExist) { 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) os.Exit(1)
} else { } else {
for _, e := range dirs { for _, e := range dirs {
// skip non-directories // skip non-directories
if !e.IsDir() { if !e.IsDir() {
verbose.Println("skipped non-directory entry", e.Name()) fmsg.VPrintf("skipped non-directory entry %q", e.Name())
continue continue
} }
// skip non-numerical names // skip non-numerical names
if _, err = strconv.Atoi(e.Name()); err != nil { 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 continue
} }
@ -45,7 +45,7 @@ func MustPrintLauncherStateSimpleGlobal(w **tabwriter.Writer, runDir string) {
// mustPrintLauncherState causes store activity so store needs to be closed // mustPrintLauncherState causes store activity so store needs to be closed
if err = s.Close(); err != nil { 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) *w = tabwriter.NewWriter(os.Stdout, 0, 1, 4, ' ', 0)
// write header when initialising // write header when initialising
if !verbose.Get() { if !fmsg.Verbose() {
_, _ = fmt.Fprintln(*w, "\tUID\tPID\tUptime\tEnablements\tMethod\tCommand") _, _ = fmt.Fprintln(*w, "\tUID\tPID\tUptime\tEnablements\tMethod\tCommand")
} else { } else {
// argv is emitted in body when verbose // 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)") 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", _, _ = 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, s.path[len(s.path)-1], state.PID, now.Sub(state.Time).Round(time.Second).String(), strings.TrimPrefix(ets.String(), ", "), state.Method,
state.Command) state.Command)
@ -110,15 +110,15 @@ func (s *simpleStore) mustPrintLauncherState(w **tabwriter.Writer, now time.Time
return nil return nil
}() }()
}); err != 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 { if !ok {
fmt.Println("warn: store faulted before printing") fmsg.Println("store faulted before printing")
os.Exit(1) os.Exit(1)
} }
} }
if innerErr != nil { 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) os.Exit(1)
} }
} }

View File

@ -6,7 +6,6 @@ import (
"git.ophivana.moe/security/fortify/acl" "git.ophivana.moe/security/fortify/acl"
"git.ophivana.moe/security/fortify/internal/fmsg" "git.ophivana.moe/security/fortify/internal/fmsg"
"git.ophivana.moe/security/fortify/internal/verbose"
) )
// UpdatePerm appends an ephemeral acl update Op. // UpdatePerm appends an ephemeral acl update Op.
@ -33,18 +32,20 @@ func (a *ACL) Type() Enablement {
} }
func (a *ACL) apply(sys *I) error { 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...), return fmsg.WrapErrorSuffix(acl.UpdatePerm(a.path, sys.uid, a.perms...),
fmt.Sprintf("cannot apply ACL entry to %q:", a.path)) fmt.Sprintf("cannot apply ACL entry to %q:", a.path))
} }
func (a *ACL) revert(sys *I, ec *Criteria) error { func (a *ACL) revert(sys *I, ec *Criteria) error {
if ec.hasType(a) { 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), return fmsg.WrapErrorSuffix(acl.UpdatePerm(a.path, sys.uid),
fmt.Sprintf("cannot strip ACL entry from %q:", a.path)) fmt.Sprintf("cannot strip ACL entry from %q:", a.path))
} else { } 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 return nil
} }
} }

View File

@ -2,12 +2,10 @@ package system
import ( import (
"errors" "errors"
"fmt"
"os" "os"
"git.ophivana.moe/security/fortify/dbus" "git.ophivana.moe/security/fortify/dbus"
"git.ophivana.moe/security/fortify/internal/fmsg" "git.ophivana.moe/security/fortify/internal/fmsg"
"git.ophivana.moe/security/fortify/internal/verbose"
) )
var ( var (
@ -42,12 +40,12 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath st
d.proxy = dbus.New(sessionBus, systemBus) d.proxy = dbus.New(sessionBus, systemBus)
defer func() { defer func() {
if verbose.Get() && d.proxy.Sealed() { if fmsg.Verbose() && d.proxy.Sealed() {
verbose.Println("sealed session proxy", session.Args(sessionBus)) fmsg.VPrintln("sealed session proxy", session.Args(sessionBus))
if system != nil { 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 { 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 { 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 // ready channel passed to dbus package
@ -86,27 +84,27 @@ func (d *DBus) apply(_ *I) error {
return fmsg.WrapErrorSuffix(err, return fmsg.WrapErrorSuffix(err,
"cannot start message bus proxy:") "cannot start message bus proxy:")
} }
verbose.Println("starting message bus proxy:", d.proxy) fmsg.VPrintln("starting message bus proxy:", d.proxy)
if verbose.Get() { // save the extra bwrap arg build when verbose logging is off if fmsg.Verbose() { // save the extra bwrap arg build when verbose logging is off
verbose.Println("message bus proxy bwrap args:", d.proxy.Bwrap()) fmsg.VPrintln("message bus proxy bwrap args:", d.proxy.Bwrap())
} }
// background wait for proxy instance and notify completion // background wait for proxy instance and notify completion
go func() { go func() {
if err := d.proxy.Wait(); err != nil { 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 }() go func() { ready <- err }()
} else { } else {
verbose.Println("message bus proxy exit") fmsg.VPrintln("message bus proxy exit")
} }
// ensure socket removal so ephemeral directory is empty at revert // 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) { 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 d.system {
if err := os.Remove(d.proxy.System()[1]); err != nil && !errors.Is(err, os.ErrNotExist) { 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, return fmsg.WrapErrorSuffix(err,
"message bus proxy fault after start:") "message bus proxy fault after start:")
} }
verbose.Println("message bus proxy ready") fmsg.VPrintln("message bus proxy ready")
return nil return nil
} }
func (d *DBus) revert(_ *I, _ *Criteria) error { func (d *DBus) revert(_ *I, _ *Criteria) error {
// criteria ignored here since dbus is always process-scoped // 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 err := d.proxy.Close(); err != nil {
if errors.Is(err, os.ErrClosed) { if errors.Is(err, os.ErrClosed) {

View File

@ -6,7 +6,6 @@ import (
"os" "os"
"git.ophivana.moe/security/fortify/internal/fmsg" "git.ophivana.moe/security/fortify/internal/fmsg"
"git.ophivana.moe/security/fortify/internal/verbose"
) )
// Ensure the existence and mode of a directory. // Ensure the existence and mode of a directory.
@ -37,7 +36,7 @@ func (m *Mkdir) Type() Enablement {
} }
func (m *Mkdir) apply(_ *I) error { func (m *Mkdir) apply(_ *I) error {
verbose.Println("ensuring directory", m) fmsg.VPrintln("ensuring directory", m)
// create directory // create directory
err := os.Mkdir(m.path, m.perm) err := os.Mkdir(m.path, m.perm)
@ -58,11 +57,11 @@ func (m *Mkdir) revert(_ *I, ec *Criteria) error {
} }
if ec.hasType(m) { if ec.hasType(m) {
verbose.Println("destroying ephemeral directory", m) fmsg.VPrintln("destroying ephemeral directory", m)
return fmsg.WrapErrorSuffix(os.Remove(m.path), return fmsg.WrapErrorSuffix(os.Remove(m.path),
fmt.Sprintf("cannot remove ephemeral directory %q:", m.path)) fmt.Sprintf("cannot remove ephemeral directory %q:", m.path))
} else { } else {
verbose.Println("skipping ephemeral directory", m) fmsg.VPrintln("skipping ephemeral directory", m)
return nil return nil
} }
} }

View File

@ -2,8 +2,9 @@ package system
import ( import (
"errors" "errors"
"fmt"
"sync" "sync"
"git.ophivana.moe/security/fortify/internal/fmsg"
) )
const ( const (
@ -80,7 +81,7 @@ func (sys *I) Commit() error {
if sp != nil { if sp != nil {
// rollback partial commit // rollback partial commit
if err := sp.Revert(&Criteria{nil}); err != nil { 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)
} }
} }
}() }()

View File

@ -9,7 +9,6 @@ import (
"git.ophivana.moe/security/fortify/acl" "git.ophivana.moe/security/fortify/acl"
"git.ophivana.moe/security/fortify/internal/fmsg" "git.ophivana.moe/security/fortify/internal/fmsg"
"git.ophivana.moe/security/fortify/internal/verbose"
) )
// CopyFile registers an Op that copies path dst from src. // 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 { func (t *Tmpfile) apply(_ *I) error {
switch t.method { switch t.method {
case tmpfileCopy: case tmpfileCopy:
verbose.Printf("publishing tmpfile %s\n", t) fmsg.VPrintln("publishing tmpfile", t)
return fmsg.WrapErrorSuffix(copyFile(t.dst, t.src), return fmsg.WrapErrorSuffix(copyFile(t.dst, t.src),
fmt.Sprintf("cannot copy tmpfile %q:", t.dst)) fmt.Sprintf("cannot copy tmpfile %q:", t.dst))
case tmpfileLink: case tmpfileLink:
verbose.Printf("linking tmpfile %s\n", t) fmsg.VPrintln("linking tmpfile", t)
return fmsg.WrapErrorSuffix(os.Link(t.src, t.dst), return fmsg.WrapErrorSuffix(os.Link(t.src, t.dst),
fmt.Sprintf("cannot link tmpfile %q:", t.dst)) fmt.Sprintf("cannot link tmpfile %q:", t.dst))
case tmpfileWrite: case tmpfileWrite:
verbose.Printf("writing %s\n", t) fmsg.VPrintln("writing", t)
return fmsg.WrapErrorSuffix(os.WriteFile(t.dst, []byte(t.src), 0600), return fmsg.WrapErrorSuffix(os.WriteFile(t.dst, []byte(t.src), 0600),
fmt.Sprintf("cannot write tmpfile %q:", t.dst)) fmt.Sprintf("cannot write tmpfile %q:", t.dst))
default: default:
@ -90,11 +89,11 @@ func (t *Tmpfile) apply(_ *I) error {
func (t *Tmpfile) revert(_ *I, ec *Criteria) error { func (t *Tmpfile) revert(_ *I, ec *Criteria) error {
if ec.hasType(t) { 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), return fmsg.WrapErrorSuffix(os.Remove(t.dst),
fmt.Sprintf("cannot remove tmpfile %q:", t.dst)) fmt.Sprintf("cannot remove tmpfile %q:", t.dst))
} else { } else {
verbose.Printf("skipping tmpfile %q\n", t.dst) fmsg.VPrintf("skipping tmpfile %q", t.dst)
return nil return nil
} }
} }

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"git.ophivana.moe/security/fortify/internal/fmsg" "git.ophivana.moe/security/fortify/internal/fmsg"
"git.ophivana.moe/security/fortify/internal/verbose"
"git.ophivana.moe/security/fortify/xcb" "git.ophivana.moe/security/fortify/xcb"
) )
@ -23,18 +22,18 @@ func (x XHost) Type() Enablement {
} }
func (x XHost) apply(_ *I) error { 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)), return fmsg.WrapErrorSuffix(xcb.ChangeHosts(xcb.HostModeInsert, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)),
fmt.Sprintf("cannot insert entry %s to X11:", x)) fmt.Sprintf("cannot insert entry %s to X11:", x))
} }
func (x XHost) revert(_ *I, ec *Criteria) error { func (x XHost) revert(_ *I, ec *Criteria) error {
if ec.hasType(x) { 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)), return fmsg.WrapErrorSuffix(xcb.ChangeHosts(xcb.HostModeDelete, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)),
fmt.Sprintf("cannot delete entry %s from X11:", x)) fmt.Sprintf("cannot delete entry %s from X11:", x))
} else { } else {
verbose.Printf("skipping entry %s in X11\n", x) fmsg.VPrintf("skipping entry %s in X11", x)
return nil return nil
} }
} }

View File

@ -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...)
}
}

View File

@ -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)
}
})
}

View File

@ -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)
}

View File

@ -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")
}
}

15
main.go
View File

@ -2,15 +2,14 @@ package main
import ( import (
"flag" "flag"
"fmt"
"os" "os"
"syscall" "syscall"
"git.ophivana.moe/security/fortify/internal" "git.ophivana.moe/security/fortify/internal"
"git.ophivana.moe/security/fortify/internal/app" "git.ophivana.moe/security/fortify/internal/app"
"git.ophivana.moe/security/fortify/internal/fmsg"
init0 "git.ophivana.moe/security/fortify/internal/init" init0 "git.ophivana.moe/security/fortify/internal/init"
"git.ophivana.moe/security/fortify/internal/shim" "git.ophivana.moe/security/fortify/internal/shim"
"git.ophivana.moe/security/fortify/internal/verbose"
) )
var ( var (
@ -24,14 +23,14 @@ func init() {
func main() { func main() {
// linux/sched/coredump.h // linux/sched/coredump.h
if _, _, errno := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_DUMPABLE, 0, 0); errno != 0 { 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() flag.Parse()
verbose.Set(flagVerbose) fmsg.SetVerbose(flagVerbose)
if internal.SdBootedV { 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 // shim/init early exit
@ -40,7 +39,7 @@ func main() {
// root check // root check
if os.Getuid() == 0 { 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) os.Exit(1)
} }
@ -56,7 +55,7 @@ func main() {
r := 1 r := 1
a, err := app.New() a, err := app.New()
if err != nil { 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 { } else if err = a.Seal(loadConfig()); err != nil {
logBaseError(err, "fortify: cannot seal app:") logBaseError(err, "fortify: cannot seal app:")
} else if err = a.Start(); err != nil { } else if err = a.Start(); err != nil {
@ -68,7 +67,7 @@ func main() {
logWaitError(err) logWaitError(err)
} }
if err = a.WaitErr(); err != nil { if err = a.WaitErr(); err != nil {
fmt.Println("fortify: inner wait failed:", err) fmsg.Println("inner wait failed:", err)
} }
os.Exit(r) os.Exit(r)
} }

View File

@ -7,6 +7,7 @@ import (
"text/tabwriter" "text/tabwriter"
"git.ophivana.moe/security/fortify/internal" "git.ophivana.moe/security/fortify/internal"
"git.ophivana.moe/security/fortify/internal/fmsg"
"git.ophivana.moe/security/fortify/internal/state" "git.ophivana.moe/security/fortify/internal/state"
) )
@ -25,7 +26,7 @@ func tryState() {
state.MustPrintLauncherStateSimpleGlobal(&w, internal.GetSC().RunDirPath) state.MustPrintLauncherStateSimpleGlobal(&w, internal.GetSC().RunDirPath)
if w != nil { if w != nil {
if err := w.Flush(); err != nil { if err := w.Flush(); err != nil {
fmt.Println("warn: error formatting output:", err) fmsg.Println("cannot format output:", err)
} }
} else { } else {
fmt.Println("No information available") fmt.Println("No information available")