cmd: shim and init into separate binaries
All checks were successful
test / test (push) Successful in 19s
All checks were successful
test / test (push) Successful in 19s
This change also fixes a deadlock when shim fails to connect and complete the setup. Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
This commit is contained in:
194
cmd/fshim/main.go
Normal file
194
cmd/fshim/main.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
init0 "git.ophivana.moe/security/fortify/cmd/finit/ipc"
|
||||
shim "git.ophivana.moe/security/fortify/cmd/fshim/ipc"
|
||||
"git.ophivana.moe/security/fortify/helper"
|
||||
"git.ophivana.moe/security/fortify/internal"
|
||||
"git.ophivana.moe/security/fortify/internal/fmsg"
|
||||
)
|
||||
|
||||
// everything beyond this point runs as unconstrained target user
|
||||
// proceed with caution!
|
||||
|
||||
func main() {
|
||||
// sharing stdout with fortify
|
||||
// USE WITH CAUTION
|
||||
fmsg.SetPrefix("shim")
|
||||
|
||||
// setting this prevents ptrace
|
||||
if err := internal.PR_SET_DUMPABLE__SUID_DUMP_DISABLE(); err != nil {
|
||||
fmsg.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// re-exec
|
||||
if len(os.Args) > 0 && (os.Args[0] != "fshim" || len(os.Args) != 1) && path.IsAbs(os.Args[0]) {
|
||||
if err := syscall.Exec(os.Args[0], []string{"fshim"}, os.Environ()); err != nil {
|
||||
fmsg.Println("cannot re-exec self:", err)
|
||||
// continue anyway
|
||||
}
|
||||
}
|
||||
|
||||
// lookup socket path from environment
|
||||
var socketPath string
|
||||
if s, ok := os.LookupEnv(shim.Env); !ok {
|
||||
fmsg.Fatal("FORTIFY_SHIM not set")
|
||||
panic("unreachable")
|
||||
} else {
|
||||
socketPath = s
|
||||
}
|
||||
|
||||
// check path to finit
|
||||
var finitPath string
|
||||
if p, ok := internal.Path(internal.Finit); !ok {
|
||||
fmsg.Fatal("invalid finit path, this copy of fshim is not compiled correctly")
|
||||
} else {
|
||||
finitPath = p
|
||||
}
|
||||
|
||||
// dial setup socket
|
||||
var conn *net.UnixConn
|
||||
if c, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: socketPath, Net: "unix"}); err != nil {
|
||||
fmsg.Fatal("cannot dial setup socket:", err)
|
||||
panic("unreachable")
|
||||
} else {
|
||||
conn = c
|
||||
}
|
||||
|
||||
// decode payload gob stream
|
||||
var payload shim.Payload
|
||||
if err := gob.NewDecoder(conn).Decode(&payload); err != nil {
|
||||
fmsg.Fatal("cannot decode shim payload:", err)
|
||||
} else {
|
||||
fmsg.SetVerbose(payload.Verbose)
|
||||
}
|
||||
|
||||
if payload.Bwrap == nil {
|
||||
fmsg.Fatal("bwrap config not supplied")
|
||||
}
|
||||
|
||||
// receive wayland fd over socket
|
||||
wfd := -1
|
||||
if payload.WL {
|
||||
if fd, err := receiveWLfd(conn); err != nil {
|
||||
fmsg.Fatal("cannot receive wayland fd:", err)
|
||||
} else {
|
||||
wfd = fd
|
||||
}
|
||||
}
|
||||
|
||||
// close setup socket
|
||||
if err := conn.Close(); err != nil {
|
||||
fmsg.Println("cannot close setup socket:", err)
|
||||
// not fatal
|
||||
}
|
||||
|
||||
var ic init0.Payload
|
||||
|
||||
// resolve argv0
|
||||
ic.Argv = payload.Argv
|
||||
if len(ic.Argv) > 0 {
|
||||
// looked up from $PATH by parent
|
||||
ic.Argv0 = payload.Exec[1]
|
||||
} else {
|
||||
// no argv, look up shell instead
|
||||
var ok bool
|
||||
if ic.Argv0, ok = os.LookupEnv("SHELL"); !ok {
|
||||
fmsg.Fatal("no command was specified and $SHELL was unset")
|
||||
}
|
||||
|
||||
ic.Argv = []string{ic.Argv0}
|
||||
}
|
||||
|
||||
conf := payload.Bwrap
|
||||
|
||||
var extraFiles []*os.File
|
||||
|
||||
// pass wayland fd
|
||||
if wfd != -1 {
|
||||
if f := os.NewFile(uintptr(wfd), "wayland"); f != nil {
|
||||
ic.WL = 3 + len(extraFiles)
|
||||
extraFiles = append(extraFiles, f)
|
||||
}
|
||||
} else {
|
||||
ic.WL = -1
|
||||
}
|
||||
|
||||
// share config pipe
|
||||
if r, w, err := os.Pipe(); err != nil {
|
||||
fmsg.Fatal("cannot pipe:", err)
|
||||
} else {
|
||||
conf.SetEnv[init0.Env] = strconv.Itoa(3 + len(extraFiles))
|
||||
extraFiles = append(extraFiles, r)
|
||||
|
||||
fmsg.VPrintln("transmitting config to init")
|
||||
go func() {
|
||||
// stream config to pipe
|
||||
if err = gob.NewEncoder(w).Encode(&ic); err != nil {
|
||||
fmsg.Fatal("cannot transmit init config:", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
helper.BubblewrapName = payload.Exec[0] // resolved bwrap path by parent
|
||||
if b, err := helper.NewBwrap(conf, nil, finitPath,
|
||||
func(int, int) []string { return make([]string, 0) }); err != nil {
|
||||
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 fmsg.Verbose() {
|
||||
fmsg.VPrintln("bwrap args:", conf.Args())
|
||||
}
|
||||
|
||||
// run and pass through exit code
|
||||
if err = b.Start(); err != nil {
|
||||
fmsg.Fatal("cannot start target process:", err)
|
||||
} else if err = b.Wait(); err != nil {
|
||||
fmsg.VPrintln("wait:", err)
|
||||
}
|
||||
if b.Unwrap().ProcessState != nil {
|
||||
fmsg.Exit(b.Unwrap().ProcessState.ExitCode())
|
||||
} else {
|
||||
fmsg.Exit(127)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func receiveWLfd(conn *net.UnixConn) (int, error) {
|
||||
oob := make([]byte, syscall.CmsgSpace(4)) // single fd
|
||||
|
||||
if _, oobn, _, _, err := conn.ReadMsgUnix(nil, oob); err != nil {
|
||||
return -1, err
|
||||
} else if len(oob) != oobn {
|
||||
return -1, errors.New("invalid message length")
|
||||
}
|
||||
|
||||
var msg syscall.SocketControlMessage
|
||||
if messages, err := syscall.ParseSocketControlMessage(oob); err != nil {
|
||||
return -1, err
|
||||
} else if len(messages) != 1 {
|
||||
return -1, errors.New("unexpected message count")
|
||||
} else {
|
||||
msg = messages[0]
|
||||
}
|
||||
|
||||
if fds, err := syscall.ParseUnixRights(&msg); err != nil {
|
||||
return -1, err
|
||||
} else if len(fds) != 1 {
|
||||
return -1, errors.New("unexpected fd count")
|
||||
} else {
|
||||
return fds[0], nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user