aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--cmd/fpkg/main.go47
-rw-r--r--cmd/fpkg/proc.go57
-rw-r--r--cmd/fpkg/with.go17
-rwxr-xr-xdist/release.sh8
-rw-r--r--internal/app/shim/manager.go10
-rw-r--r--internal/comp.go13
-rw-r--r--internal/path.go21
-rw-r--r--internal/sys/std.go46
-rw-r--r--main.go6
-rw-r--r--package.nix9
10 files changed, 120 insertions, 114 deletions
diff --git a/cmd/fpkg/main.go b/cmd/fpkg/main.go
index ba0228f8..8075ba16 100644
--- a/cmd/fpkg/main.go
+++ b/cmd/fpkg/main.go
@@ -1,23 +1,32 @@
package main
import (
+ "context"
"encoding/json"
"errors"
"log"
"os"
+ "os/signal"
"path"
"syscall"
"git.gensokyo.uk/security/fortify/command"
"git.gensokyo.uk/security/fortify/fst"
"git.gensokyo.uk/security/fortify/helper/bwrap"
+ "git.gensokyo.uk/security/fortify/helper/seccomp"
+ "git.gensokyo.uk/security/fortify/internal"
+ init0 "git.gensokyo.uk/security/fortify/internal/app/init"
+ "git.gensokyo.uk/security/fortify/internal/app/shim"
"git.gensokyo.uk/security/fortify/internal/fmsg"
+ "git.gensokyo.uk/security/fortify/internal/sys"
)
const shellPath = "/run/current-system/sw/bin/bash"
var (
errSuccess = errors.New("success")
+
+ std sys.State = new(sys.Std)
)
func init() {
@@ -28,14 +37,40 @@ func init() {
}
func main() {
+ // early init argv0 check, skips root check and duplicate PR_SET_DUMPABLE
+ init0.TryArgv0()
+
+ if err := internal.PR_SET_DUMPABLE__SUID_DUMP_DISABLE(); err != nil {
+ log.Printf("cannot set SUID_DUMP_DISABLE: %s", err)
+ // not fatal: this program runs as the privileged user
+ }
+
+ if os.Geteuid() == 0 {
+ log.Fatal("this program must not run as root")
+ }
+
+ ctx, stop := signal.NotifyContext(context.Background(),
+ syscall.SIGINT, syscall.SIGTERM)
+ defer stop() // unreachable
+
var (
flagVerbose bool
flagDropShell bool
)
- c := command.New(os.Stderr, log.Printf, "fpkg", func([]string) error { fmsg.Store(flagVerbose); return nil }).
+ c := command.New(os.Stderr, log.Printf, "fpkg", func([]string) error {
+ fmsg.Store(flagVerbose)
+ if flagVerbose {
+ seccomp.CPrintln = log.Println
+ }
+ return nil
+ }).
Flag(&flagVerbose, "v", command.BoolFlag(false), "Print debug messages to the console").
Flag(&flagDropShell, "s", command.BoolFlag(false), "Drop to a shell in place of next fortify action")
+ // internal commands
+ c.Command("shim", command.UsageInternal, func([]string) error { shim.Main(); return errSuccess })
+ c.Command("init", command.UsageInternal, func([]string) error { init0.Main(); return errSuccess })
+
{
var (
flagDropShellActivate bool
@@ -149,7 +184,7 @@ func main() {
Setup steps for files owned by the target user.
*/
- withCacheDir("install", []string{
+ withCacheDir(ctx, "install", []string{
// export inner bundle path in the environment
"export BUNDLE=" + fst.Tmp + "/bundle",
// replace inner /etc
@@ -171,7 +206,7 @@ func main() {
}, workDir, bundle, pathSet, flagDropShell, cleanup)
if bundle.GPU {
- withCacheDir("mesa-wrappers", []string{
+ withCacheDir(ctx, "mesa-wrappers", []string{
// link nixGL mesa wrappers
"mkdir -p nix/.nixGL",
"ln -s " + bundle.Mesa + "/bin/nixGLIntel nix/.nixGL/nixGL",
@@ -183,7 +218,7 @@ func main() {
Activate home-manager generation.
*/
- withNixDaemon("activate", []string{
+ withNixDaemon(ctx, "activate", []string{
// clean up broken links
"mkdir -p .local/state/{nix,home-manager}",
"chmod -R +w .local/state/{nix,home-manager}",
@@ -251,7 +286,7 @@ func main() {
*/
if app.GPU && flagAutoDrivers {
- withNixDaemon("nix-gl", []string{
+ withNixDaemon(ctx, "nix-gl", []string{
"mkdir -p /nix/.nixGL/auto",
"rm -rf /nix/.nixGL/auto",
"export NIXPKGS_ALLOW_UNFREE=1",
@@ -349,7 +384,7 @@ func main() {
Spawn app.
*/
- fortifyApp(config, func() {})
+ mustRunApp(ctx, config, func() {})
return errSuccess
}).
Flag(&flagDropShellNixGL, "s", command.BoolFlag(false), "Drop to a shell on nixGL build").
diff --git a/cmd/fpkg/proc.go b/cmd/fpkg/proc.go
index 538c5ed4..ea106adf 100644
--- a/cmd/fpkg/proc.go
+++ b/cmd/fpkg/proc.go
@@ -1,59 +1,28 @@
package main
import (
- "encoding/json"
- "errors"
- "io"
- "log"
+ "context"
"os"
- "os/exec"
"git.gensokyo.uk/security/fortify/fst"
- "git.gensokyo.uk/security/fortify/internal"
+ "git.gensokyo.uk/security/fortify/internal/app"
"git.gensokyo.uk/security/fortify/internal/fmsg"
)
-func fortifyApp(config *fst.Config, beforeFail func()) {
- var (
- cmd *exec.Cmd
- st io.WriteCloser
- )
- if p, ok := internal.Path(internal.Fortify); !ok {
- beforeFail()
- log.Fatal("invalid fortify path, this copy of fpkg is not compiled correctly")
- } else if r, w, err := os.Pipe(); err != nil {
- beforeFail()
- log.Fatalf("cannot pipe: %v", err)
+func mustRunApp(ctx context.Context, config *fst.Config, beforeFail func()) {
+ rs := new(fst.RunState)
+ a := app.MustNew(std)
+
+ if sa, err := a.Seal(config); err != nil {
+ fmsg.PrintBaseError(err, "cannot seal app:")
+ rs.ExitCode = 1
} else {
- if fmsg.Load() {
- cmd = exec.Command(p, "-v", "app", "3")
- } else {
- cmd = exec.Command(p, "app", "3")
- }
- cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
- cmd.ExtraFiles = []*os.File{r}
- st = w
+ // this updates ExitCode
+ app.PrintRunStateErr(rs, sa.Run(ctx, rs))
}
- go func() {
- if err := json.NewEncoder(st).Encode(config); err != nil {
- beforeFail()
- log.Fatalf("cannot send configuration: %v", err)
- }
- }()
-
- if err := cmd.Start(); err != nil {
+ if rs.ExitCode != 0 {
beforeFail()
- log.Fatalf("cannot start fortify: %v", err)
- }
- if err := cmd.Wait(); err != nil {
- var exitError *exec.ExitError
- if errors.As(err, &exitError) {
- beforeFail()
- internal.Exit(exitError.ExitCode())
- } else {
- beforeFail()
- log.Fatalf("cannot wait: %v", err)
- }
+ os.Exit(rs.ExitCode)
}
}
diff --git a/cmd/fpkg/with.go b/cmd/fpkg/with.go
index b6928bd1..d7b0e968 100644
--- a/cmd/fpkg/with.go
+++ b/cmd/fpkg/with.go
@@ -1,6 +1,7 @@
package main
import (
+ "context"
"path"
"strings"
@@ -10,10 +11,11 @@ import (
)
func withNixDaemon(
+ ctx context.Context,
action string, command []string, net bool, updateConfig func(config *fst.Config) *fst.Config,
app *bundleInfo, pathSet *appPathSet, dropShell bool, beforeFail func(),
) {
- fortifyAppDropShell(updateConfig(&fst.Config{
+ mustRunAppDropShell(ctx, updateConfig(&fst.Config{
ID: app.ID,
Command: []string{shellPath, "-lc", "rm -f /nix/var/nix/daemon-socket/socket && " +
// start nix-daemon
@@ -56,8 +58,11 @@ func withNixDaemon(
}), dropShell, beforeFail)
}
-func withCacheDir(action string, command []string, workDir string, app *bundleInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) {
- fortifyAppDropShell(&fst.Config{
+func withCacheDir(
+ ctx context.Context,
+ action string, command []string, workDir string,
+ app *bundleInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) {
+ mustRunAppDropShell(ctx, &fst.Config{
ID: app.ID,
Command: []string{shellPath, "-lc", strings.Join(command, " && ")},
Confinement: fst.ConfinementConfig{
@@ -90,12 +95,12 @@ func withCacheDir(action string, command []string, workDir string, app *bundleIn
}, dropShell, beforeFail)
}
-func fortifyAppDropShell(config *fst.Config, dropShell bool, beforeFail func()) {
+func mustRunAppDropShell(ctx context.Context, config *fst.Config, dropShell bool, beforeFail func()) {
if dropShell {
config.Command = []string{shellPath, "-l"}
- fortifyApp(config, beforeFail)
+ mustRunApp(ctx, config, beforeFail)
beforeFail()
internal.Exit(0)
}
- fortifyApp(config, beforeFail)
+ mustRunApp(ctx, config, beforeFail)
}
diff --git a/dist/release.sh b/dist/release.sh
index 88171860..02f15411 100755
--- a/dist/release.sh
+++ b/dist/release.sh
@@ -10,10 +10,10 @@ cp -rv "comp" "${out}"
go generate ./...
go build -trimpath -v -o "${out}/bin/" -ldflags "-s -w -buildid= -extldflags '-static'
- -X git.gensokyo.uk/security/fortify/internal.Version=${VERSION}
- -X git.gensokyo.uk/security/fortify/internal.Fsu=/usr/bin/fsu
- -X git.gensokyo.uk/security/fortify/internal.Fortify=/usr/bin/fortify
- -X main.Fmain=/usr/bin/fortify" ./...
+ -X git.gensokyo.uk/security/fortify/internal.version=${VERSION}
+ -X git.gensokyo.uk/security/fortify/internal.fsu=/usr/bin/fsu
+ -X main.fmain=/usr/bin/fortify
+ -X main.fpkg=/usr/bin/fpkg" ./...
rm -f "./${out}.tar.gz" && tar -C dist -czf "${out}.tar.gz" "${pname}"
rm -rf "./${out}"
diff --git a/internal/app/shim/manager.go b/internal/app/shim/manager.go
index e5ddaadb..bb3948fd 100644
--- a/internal/app/shim/manager.go
+++ b/internal/app/shim/manager.go
@@ -52,14 +52,8 @@ func (s *Shim) Start(
syncFd *os.File,
) (*time.Time, error) {
// prepare user switcher invocation
- var fsu string
- if p, ok := internal.Path(internal.Fsu); !ok {
- return nil, fmsg.WrapError(errors.New("bad fsu path"),
- "invalid fsu path, this copy of fortify is not compiled correctly")
- } else {
- fsu = p
- }
- s.cmd = exec.Command(fsu)
+ fsuPath := internal.MustFsuPath()
+ s.cmd = exec.Command(fsuPath)
// pass shim setup pipe
if fd, e, err := proc.Setup(&s.cmd.ExtraFiles); err != nil {
diff --git a/internal/comp.go b/internal/comp.go
index e7064db0..89dc0f85 100644
--- a/internal/comp.go
+++ b/internal/comp.go
@@ -3,10 +3,15 @@ package internal
const compPoison = "INVALIDINVALIDINVALIDINVALIDINVALID"
var (
- Version = compPoison
+ version = compPoison
)
-// Check validates string value set at compile time.
-func Check(s string) (string, bool) {
- return s, s != compPoison && s != ""
+// check validates string value set at compile time.
+func check(s string) (string, bool) { return s, s != compPoison && s != "" }
+
+func Version() string {
+ if v, ok := check(version); ok {
+ return v
+ }
+ return "impure"
}
diff --git a/internal/path.go b/internal/path.go
index 97b0754c..8211478b 100644
--- a/internal/path.go
+++ b/internal/path.go
@@ -1,12 +1,23 @@
package internal
-import "path"
+import (
+ "log"
+ "path"
+
+ "git.gensokyo.uk/security/fortify/internal/fmsg"
+)
var (
- Fsu = compPoison
- Fortify = compPoison
+ fsu = compPoison
)
-func Path(p string) (string, bool) {
- return p, p != compPoison && p != "" && path.IsAbs(p)
+func MustFsuPath() string {
+ if name, ok := checkPath(fsu); ok {
+ return name
+ }
+ fmsg.BeforeExit()
+ log.Fatal("invalid fsu path, this program is compiled incorrectly")
+ return compPoison
}
+
+func checkPath(p string) (string, bool) { return p, p != compPoison && p != "" && path.IsAbs(p) }
diff --git a/internal/sys/std.go b/internal/sys/std.go
index bab2b923..d447f586 100644
--- a/internal/sys/std.go
+++ b/internal/sys/std.go
@@ -4,7 +4,6 @@ import (
"errors"
"fmt"
"io/fs"
- "log"
"os"
"os/exec"
"os/user"
@@ -79,32 +78,27 @@ func (s *Std) Uid(aid int) (int, error) {
defer func() { s.uidCopy[aid] = u }()
u.uid = -1
- if fsu, ok := internal.Check(internal.Fsu); !ok {
- fmsg.BeforeExit()
- log.Fatal("invalid fsu path, this copy of fortify is not compiled correctly")
- // unreachable
- return 0, syscall.EBADE
- } else {
- cmd := exec.Command(fsu)
- cmd.Path = fsu
- cmd.Stderr = os.Stderr // pass through fatal messages
- cmd.Env = []string{"FORTIFY_APP_ID=" + strconv.Itoa(aid)}
- cmd.Dir = "/"
- var (
- p []byte
- exitError *exec.ExitError
- )
+ fsuPath := internal.MustFsuPath()
- if p, u.err = cmd.Output(); u.err == nil {
- u.uid, u.err = strconv.Atoi(string(p))
- if u.err != nil {
- u.err = fmsg.WrapErrorSuffix(u.err, "cannot parse uid from fsu:")
- }
- } else if errors.As(u.err, &exitError) && exitError != nil && exitError.ExitCode() == 1 {
- u.err = fmsg.WrapError(syscall.EACCES, "") // fsu prints to stderr in this case
- } else if os.IsNotExist(u.err) {
- u.err = fmsg.WrapError(os.ErrNotExist, fmt.Sprintf("the setuid helper is missing: %s", fsu))
+ cmd := exec.Command(fsuPath)
+ cmd.Path = fsuPath
+ cmd.Stderr = os.Stderr // pass through fatal messages
+ cmd.Env = []string{"FORTIFY_APP_ID=" + strconv.Itoa(aid)}
+ cmd.Dir = "/"
+ var (
+ p []byte
+ exitError *exec.ExitError
+ )
+
+ if p, u.err = cmd.Output(); u.err == nil {
+ u.uid, u.err = strconv.Atoi(string(p))
+ if u.err != nil {
+ u.err = fmsg.WrapErrorSuffix(u.err, "cannot parse uid from fsu:")
}
- return u.uid, u.err
+ } else if errors.As(u.err, &exitError) && exitError != nil && exitError.ExitCode() == 1 {
+ u.err = fmsg.WrapError(syscall.EACCES, "") // fsu prints to stderr in this case
+ } else if os.IsNotExist(u.err) {
+ u.err = fmsg.WrapError(os.ErrNotExist, fmt.Sprintf("the setuid helper is missing: %s", fsuPath))
}
+ return u.uid, u.err
}
diff --git a/main.go b/main.go
index c3590f35..9a47b2fe 100644
--- a/main.go
+++ b/main.go
@@ -259,11 +259,7 @@ func buildCommand(out io.Writer) command.Command {
}).Flag(&psFlagShort, "short", command.BoolFlag(false), "Print instance id")
c.Command("version", "Show fortify version", func(args []string) error {
- if v, ok := internal.Check(internal.Version); ok {
- fmt.Println(v)
- } else {
- fmt.Println("impure")
- }
+ fmt.Println(internal.Version())
return errSuccess
})
diff --git a/package.nix b/package.nix
index 595fa2a7..056a9c53 100644
--- a/package.nix
+++ b/package.nix
@@ -44,18 +44,15 @@ buildGoModule rec {
ldflags ++ [ "-X git.gensokyo.uk/security/fortify/internal.${name}=${value}" ]
)
(
- [
- "-s -w"
- ]
+ [ "-s -w" ]
++ lib.optionals withStatic [
"-linkmode external"
"-extldflags \"-static\""
]
)
{
- Version = "v${version}";
- Fsu = "/run/wrappers/bin/fsu";
- Fortify = "${placeholder "out"}/libexec/fortify";
+ version = "v${version}";
+ fsu = "/run/wrappers/bin/fsu";
};
# nix build environment does not allow acls