diff --git a/internal/app/seal.go b/internal/app/seal.go index fb3b3ce..4cd3129 100644 --- a/internal/app/seal.go +++ b/internal/app/seal.go @@ -252,8 +252,12 @@ func (a *app) Seal(config *fst.Config) error { // store activity begins after Start is called and must end before Wait seal.store = state.NewMulti(seal.RunDirPath) - // initialise system interface with full uid + // initialise system interface with os uid seal.sys.I = system.New(seal.sys.user.uid) + seal.sys.I.IsVerbose = fmsg.Load + seal.sys.I.Verbose = fmsg.Verbose + seal.sys.I.Verbosef = fmsg.Verbosef + seal.sys.I.WrapErr = fmsg.WrapError // pass through enablements seal.et = config.Confinement.Enablements diff --git a/internal/system/acl.go b/internal/system/acl.go index e6e6e37..840c397 100644 --- a/internal/system/acl.go +++ b/internal/system/acl.go @@ -5,7 +5,6 @@ import ( "slices" "git.gensokyo.uk/security/fortify/acl" - "git.gensokyo.uk/security/fortify/internal/fmsg" ) // UpdatePerm appends an ephemeral acl update Op. @@ -31,23 +30,21 @@ type ACL struct { perms acl.Perms } -func (a *ACL) Type() Enablement { - return a.et -} +func (a *ACL) Type() Enablement { return a.et } func (a *ACL) apply(sys *I) error { - fmsg.Verbose("applying ACL", a) - return fmsg.WrapErrorSuffix(acl.UpdatePerm(a.path, sys.uid, a.perms...), + sys.println("applying ACL", a) + return sys.wrapErrSuffix(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) { - fmsg.Verbose("stripping ACL", a) - return fmsg.WrapErrorSuffix(acl.UpdatePerm(a.path, sys.uid), + sys.println("stripping ACL", a) + return sys.wrapErrSuffix(acl.UpdatePerm(a.path, sys.uid), fmt.Sprintf("cannot strip ACL entry from %q:", a.path)) } else { - fmsg.Verbose("skipping ACL", a) + sys.println("skipping ACL", a) return nil } } @@ -60,9 +57,7 @@ func (a *ACL) Is(o Op) bool { slices.Equal(a.perms, a0.perms) } -func (a *ACL) Path() string { - return a.path -} +func (a *ACL) Path() string { return a.path } func (a *ACL) String() string { return fmt.Sprintf("%s type: %s path: %q", diff --git a/internal/system/dbus.go b/internal/system/dbus.go index f52adf1..cbb127e 100644 --- a/internal/system/dbus.go +++ b/internal/system/dbus.go @@ -8,7 +8,6 @@ import ( "sync" "git.gensokyo.uk/security/fortify/dbus" - "git.gensokyo.uk/security/fortify/internal/fmsg" ) var ( @@ -28,7 +27,7 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath st // session bus is mandatory if session == nil { - return nil, fmsg.WrapError(ErrDBusConfig, + return nil, sys.wrapErr(ErrDBusConfig, "attempted to seal message bus proxy without session bus config") } @@ -48,12 +47,12 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath st d.proxy = dbus.New(sessionBus, systemBus) defer func() { - if fmsg.Load() && d.proxy.Sealed() { - fmsg.Verbose("sealed session proxy", session.Args(sessionBus)) + if sys.IsVerbose() && d.proxy.Sealed() { + sys.println("sealed session proxy", session.Args(sessionBus)) if system != nil { - fmsg.Verbose("sealed system proxy", system.Args(systemBus)) + sys.println("sealed system proxy", system.Args(systemBus)) } - fmsg.Verbose("message bus proxy final args:", d.proxy) + sys.println("message bus proxy final args:", d.proxy) } }() @@ -62,7 +61,7 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath st // seal dbus proxy d.out = &scanToFmsg{msg: new(strings.Builder)} - return d.out.Dump, fmsg.WrapErrorSuffix(d.proxy.Seal(session, system), + return d.out.Dump, sys.wrapErrSuffix(d.proxy.Seal(session, system), "cannot seal message bus proxy:") } @@ -74,32 +73,30 @@ type DBus struct { system bool } -func (d *DBus) Type() Enablement { - return Process -} +func (d *DBus) Type() Enablement { return Process } func (d *DBus) apply(sys *I) error { - fmsg.Verbosef("session bus proxy on %q for upstream %q", d.proxy.Session()[1], d.proxy.Session()[0]) + sys.printf("session bus proxy on %q for upstream %q", d.proxy.Session()[1], d.proxy.Session()[0]) if d.system { - fmsg.Verbosef("system bus proxy on %q for upstream %q", d.proxy.System()[1], d.proxy.System()[0]) + sys.printf("system bus proxy on %q for upstream %q", d.proxy.System()[1], d.proxy.System()[0]) } // this starts the process and blocks until ready if err := d.proxy.Start(sys.ctx, d.out, true); err != nil { d.out.Dump() - return fmsg.WrapErrorSuffix(err, + return sys.wrapErrSuffix(err, "cannot start message bus proxy:") } - fmsg.Verbose("starting message bus proxy:", d.proxy) + sys.println("starting message bus proxy:", d.proxy) return nil } -func (d *DBus) revert(_ *I, _ *Criteria) error { +func (d *DBus) revert(sys *I, _ *Criteria) error { // criteria ignored here since dbus is always process-scoped - fmsg.Verbose("terminating message bus proxy") + sys.println("terminating message bus proxy") d.proxy.Close() - defer fmsg.Verbose("message bus proxy exit") - return fmsg.WrapErrorSuffix(d.proxy.Wait(), "message bus proxy error:") + defer sys.println("message bus proxy exit") + return sys.wrapErrSuffix(d.proxy.Wait(), "message bus proxy error:") } func (d *DBus) Is(o Op) bool { diff --git a/internal/system/link.go b/internal/system/link.go index 9689e25..4182d56 100644 --- a/internal/system/link.go +++ b/internal/system/link.go @@ -3,8 +3,6 @@ package system import ( "fmt" "os" - - "git.gensokyo.uk/security/fortify/internal/fmsg" ) // Link registers an Op that links dst to src. @@ -27,27 +25,23 @@ type Hardlink struct { func (l *Hardlink) Type() Enablement { return l.et } -func (l *Hardlink) apply(_ *I) error { - fmsg.Verbose("linking", l) - return fmsg.WrapErrorSuffix(os.Link(l.src, l.dst), +func (l *Hardlink) apply(sys *I) error { + sys.println("linking", l) + return sys.wrapErrSuffix(os.Link(l.src, l.dst), fmt.Sprintf("cannot link %q:", l.dst)) } -func (l *Hardlink) revert(_ *I, ec *Criteria) error { +func (l *Hardlink) revert(sys *I, ec *Criteria) error { if ec.hasType(l) { - fmsg.Verbosef("removing hard link %q", l.dst) - return fmsg.WrapErrorSuffix(os.Remove(l.dst), + sys.printf("removing hard link %q", l.dst) + return sys.wrapErrSuffix(os.Remove(l.dst), fmt.Sprintf("cannot remove hard link %q:", l.dst)) } else { - fmsg.Verbosef("skipping hard link %q", l.dst) + sys.printf("skipping hard link %q", l.dst) return nil } } -func (l *Hardlink) Is(o Op) bool { - l0, ok := o.(*Hardlink) - return ok && l0 != nil && *l == *l0 -} - +func (l *Hardlink) Is(o Op) bool { l0, ok := o.(*Hardlink); return ok && l0 != nil && *l == *l0 } func (l *Hardlink) Path() string { return l.src } func (l *Hardlink) String() string { return fmt.Sprintf("%q from %q", l.dst, l.src) } diff --git a/internal/system/mkdir.go b/internal/system/mkdir.go index 855b339..524289e 100644 --- a/internal/system/mkdir.go +++ b/internal/system/mkdir.go @@ -4,8 +4,6 @@ import ( "errors" "fmt" "os" - - "git.gensokyo.uk/security/fortify/internal/fmsg" ) // Ensure the existence and mode of a directory. @@ -39,33 +37,33 @@ func (m *Mkdir) Type() Enablement { return m.et } -func (m *Mkdir) apply(_ *I) error { - fmsg.Verbose("ensuring directory", m) +func (m *Mkdir) apply(sys *I) error { + sys.println("ensuring directory", m) // create directory err := os.Mkdir(m.path, m.perm) if !errors.Is(err, os.ErrExist) { - return fmsg.WrapErrorSuffix(err, + return sys.wrapErrSuffix(err, fmt.Sprintf("cannot create directory %q:", m.path)) } // directory exists, ensure mode - return fmsg.WrapErrorSuffix(os.Chmod(m.path, m.perm), + return sys.wrapErrSuffix(os.Chmod(m.path, m.perm), fmt.Sprintf("cannot change mode of %q to %s:", m.path, m.perm)) } -func (m *Mkdir) revert(_ *I, ec *Criteria) error { +func (m *Mkdir) revert(sys *I, ec *Criteria) error { if !m.ephemeral { // skip non-ephemeral dir and do not log anything return nil } if ec.hasType(m) { - fmsg.Verbose("destroying ephemeral directory", m) - return fmsg.WrapErrorSuffix(os.Remove(m.path), + sys.println("destroying ephemeral directory", m) + return sys.wrapErrSuffix(os.Remove(m.path), fmt.Sprintf("cannot remove ephemeral directory %q:", m.path)) } else { - fmsg.Verbose("skipping ephemeral directory", m) + sys.println("skipping ephemeral directory", m) return nil } } diff --git a/internal/system/op.go b/internal/system/op.go index 42f93d4..0e421ae 100644 --- a/internal/system/op.go +++ b/internal/system/op.go @@ -5,8 +5,6 @@ import ( "errors" "log" "sync" - - "git.gensokyo.uk/security/fortify/internal/fmsg" ) const ( @@ -55,19 +53,42 @@ func TypeString(e Enablement) string { } } +// New initialises sys with no-op verbose functions. +func New(uid int) (sys *I) { + sys = new(I) + sys.uid = uid + sys.IsVerbose = func() bool { return false } + sys.Verbose = func(...any) {} + sys.Verbosef = func(string, ...any) {} + sys.WrapErr = func(err error, _ ...any) error { return err } + return +} + type I struct { uid int ops []Op ctx context.Context + IsVerbose func() bool + Verbose func(v ...any) + Verbosef func(format string, v ...any) + WrapErr func(err error, a ...any) error + // whether sys has been reverted state bool lock sync.Mutex } -func (sys *I) UID() int { - return sys.uid +func (sys *I) UID() int { return sys.uid } +func (sys *I) println(v ...any) { sys.Verbose(v...) } +func (sys *I) printf(format string, v ...any) { sys.Verbosef(format, v...) } +func (sys *I) wrapErr(err error, a ...any) error { return sys.WrapErr(err, a...) } +func (sys *I) wrapErrSuffix(err error, a ...any) error { + if err == nil { + return nil + } + return sys.wrapErr(err, append(a, err)...) } func (sys *I) Equal(v *I) bool { @@ -99,7 +120,7 @@ func (sys *I) Commit(ctx context.Context) error { // sp is set to nil when all ops are applied if sp != nil { // rollback partial commit - fmsg.Verbosef("commit faulted after %d ops, rolling back partial commit", len(sp.ops)) + sys.printf("commit faulted after %d ops, rolling back partial commit", len(sp.ops)) if err := sp.Revert(&Criteria{nil}); err != nil { log.Println("errors returned reverting partial commit:", err) } @@ -139,7 +160,3 @@ func (sys *I) Revert(ec *Criteria) error { // errors.Join filters nils return errors.Join(errs...) } - -func New(uid int) *I { - return &I{uid: uid} -} diff --git a/internal/system/tmpfiles.go b/internal/system/tmpfiles.go index 5130f2e..636943b 100644 --- a/internal/system/tmpfiles.go +++ b/internal/system/tmpfiles.go @@ -7,8 +7,6 @@ import ( "io" "os" "syscall" - - "git.gensokyo.uk/security/fortify/internal/fmsg" ) // CopyFile registers an Op that copies from src. @@ -33,8 +31,8 @@ type Tmpfile struct { } func (t *Tmpfile) Type() Enablement { return Process } -func (t *Tmpfile) apply(_ *I) error { - fmsg.Verbose("copying", t) +func (t *Tmpfile) apply(sys *I) error { + sys.println("copying", t) if t.payload == nil { // this is a misuse of the API; do not return an error message @@ -42,25 +40,25 @@ func (t *Tmpfile) apply(_ *I) error { } if b, err := os.Stat(t.src); err != nil { - return fmsg.WrapErrorSuffix(err, + return sys.wrapErrSuffix(err, fmt.Sprintf("cannot stat %q:", t.src)) } else { if b.IsDir() { - return fmsg.WrapErrorSuffix(syscall.EISDIR, + return sys.wrapErrSuffix(syscall.EISDIR, fmt.Sprintf("%q is a directory", t.src)) } if s := b.Size(); s > t.n { - return fmsg.WrapErrorSuffix(syscall.ENOMEM, + return sys.wrapErrSuffix(syscall.ENOMEM, fmt.Sprintf("file %q is too long: %d > %d", t.src, s, t.n)) } } if f, err := os.Open(t.src); err != nil { - return fmsg.WrapErrorSuffix(err, + return sys.wrapErrSuffix(err, fmt.Sprintf("cannot open %q:", t.src)) } else if _, err = io.CopyN(t.buf, f, t.n); err != nil { - return fmsg.WrapErrorSuffix(err, + return sys.wrapErrSuffix(err, fmt.Sprintf("cannot read from %q:", t.src)) } diff --git a/internal/system/wayland.go b/internal/system/wayland.go index f381bd2..11f54d8 100644 --- a/internal/system/wayland.go +++ b/internal/system/wayland.go @@ -6,7 +6,6 @@ import ( "os" "git.gensokyo.uk/security/fortify/acl" - "git.gensokyo.uk/security/fortify/internal/fmsg" "git.gensokyo.uk/security/fortify/wl" ) @@ -47,35 +46,35 @@ func (w *Wayland) apply(sys *I) error { if errors.Is(err, os.ErrNotExist) { err = os.ErrNotExist } - return fmsg.WrapErrorSuffix(err, + return sys.wrapErrSuffix(err, fmt.Sprintf("cannot attach to wayland on %q:", w.src)) } else { - fmsg.Verbosef("wayland attached on %q", w.src) + sys.printf("wayland attached on %q", w.src) } if sp, err := w.conn.Bind(w.dst, w.appID, w.instanceID); err != nil { - return fmsg.WrapErrorSuffix(err, + return sys.wrapErrSuffix(err, fmt.Sprintf("cannot bind to socket on %q:", w.dst)) } else { *w.sync = sp - fmsg.Verbosef("wayland listening on %q", w.dst) - return fmsg.WrapErrorSuffix(errors.Join(os.Chmod(w.dst, 0), acl.UpdatePerm(w.dst, sys.uid, acl.Read, acl.Write, acl.Execute)), + sys.printf("wayland listening on %q", w.dst) + return sys.wrapErrSuffix(errors.Join(os.Chmod(w.dst, 0), acl.UpdatePerm(w.dst, sys.uid, acl.Read, acl.Write, acl.Execute)), fmt.Sprintf("cannot chmod socket on %q:", w.dst)) } } -func (w *Wayland) revert(_ *I, ec *Criteria) error { +func (w *Wayland) revert(sys *I, ec *Criteria) error { if ec.hasType(w) { - fmsg.Verbosef("removing wayland socket on %q", w.dst) + sys.printf("removing wayland socket on %q", w.dst) if err := os.Remove(w.dst); err != nil && !errors.Is(err, os.ErrNotExist) { return err } - fmsg.Verbosef("detaching from wayland on %q", w.src) - return fmsg.WrapErrorSuffix(w.conn.Close(), + sys.printf("detaching from wayland on %q", w.src) + return sys.wrapErrSuffix(w.conn.Close(), fmt.Sprintf("cannot detach from wayland on %q:", w.src)) } else { - fmsg.Verbosef("skipping wayland cleanup on %q", w.dst) + sys.printf("skipping wayland cleanup on %q", w.dst) return nil } } diff --git a/internal/system/xhost.go b/internal/system/xhost.go index 89e63db..bbc0a91 100644 --- a/internal/system/xhost.go +++ b/internal/system/xhost.go @@ -3,7 +3,6 @@ package system import ( "fmt" - "git.gensokyo.uk/security/fortify/internal/fmsg" "git.gensokyo.uk/security/fortify/xcb" ) @@ -23,19 +22,19 @@ func (x XHost) Type() Enablement { return EX11 } -func (x XHost) apply(_ *I) error { - fmsg.Verbosef("inserting entry %s to X11", x) - return fmsg.WrapErrorSuffix(xcb.ChangeHosts(xcb.HostModeInsert, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)), +func (x XHost) apply(sys *I) error { + sys.printf("inserting entry %s to X11", x) + return sys.wrapErrSuffix(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 { +func (x XHost) revert(sys *I, ec *Criteria) error { if ec.hasType(x) { - fmsg.Verbosef("deleting entry %s from X11", x) - return fmsg.WrapErrorSuffix(xcb.ChangeHosts(xcb.HostModeDelete, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)), + sys.printf("deleting entry %s from X11", x) + return sys.wrapErrSuffix(xcb.ChangeHosts(xcb.HostModeDelete, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)), fmt.Sprintf("cannot delete entry %s from X11:", x)) } else { - fmsg.Verbosef("skipping entry %s in X11", x) + sys.printf("skipping entry %s in X11", x) return nil } }