This removes the requirement to call fmsg.Exit on every exit path, and enables direct use of the "log" package. However, fmsg.BeforeExit is still encouraged when possible to catch exit on suspended output. Signed-off-by: Ophestra <cat@gensokyo.uk>
140 lines
3.1 KiB
Go
140 lines
3.1 KiB
Go
package shim
|
|
|
|
import (
|
|
"context"
|
|
"encoding/gob"
|
|
"errors"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.gensokyo.uk/security/fortify/helper/proc"
|
|
"git.gensokyo.uk/security/fortify/internal"
|
|
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
|
)
|
|
|
|
// used by the parent process
|
|
|
|
type Shim struct {
|
|
// user switcher process
|
|
cmd *exec.Cmd
|
|
// fallback exit notifier with error returned killing the process
|
|
killFallback chan error
|
|
// monitor to shim encoder
|
|
encoder *gob.Encoder
|
|
// bwrap --sync-fd value
|
|
sync *uintptr
|
|
}
|
|
|
|
func (s *Shim) String() string {
|
|
if s.cmd == nil {
|
|
return "(unused shim manager)"
|
|
}
|
|
return s.cmd.String()
|
|
}
|
|
|
|
func (s *Shim) Unwrap() *exec.Cmd {
|
|
return s.cmd
|
|
}
|
|
|
|
func (s *Shim) WaitFallback() chan error {
|
|
return s.killFallback
|
|
}
|
|
|
|
func (s *Shim) Start(
|
|
// string representation of application id
|
|
aid string,
|
|
// string representation of supplementary group ids
|
|
supp []string,
|
|
// bwrap --sync-fd
|
|
syncFd *os.File,
|
|
) (*time.Time, error) {
|
|
// prepare user switcher invocation
|
|
var fsu string
|
|
if p, ok := internal.Path(internal.Fsu); !ok {
|
|
log.Fatal("invalid fsu path, this copy of fortify is not compiled correctly")
|
|
} else {
|
|
fsu = p
|
|
}
|
|
s.cmd = exec.Command(fsu)
|
|
|
|
// pass shim setup pipe
|
|
if fd, e, err := proc.Setup(&s.cmd.ExtraFiles); err != nil {
|
|
return nil, fmsg.WrapErrorSuffix(err,
|
|
"cannot create shim setup pipe:")
|
|
} else {
|
|
s.encoder = e
|
|
s.cmd.Env = []string{
|
|
Env + "=" + strconv.Itoa(fd),
|
|
"FORTIFY_APP_ID=" + aid,
|
|
}
|
|
}
|
|
|
|
// format fsu supplementary groups
|
|
if len(supp) > 0 {
|
|
fmsg.Verbosef("attaching supplementary group ids %s", supp)
|
|
s.cmd.Env = append(s.cmd.Env, "FORTIFY_GROUPS="+strings.Join(supp, " "))
|
|
}
|
|
s.cmd.Stdin, s.cmd.Stdout, s.cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
|
s.cmd.Dir = "/"
|
|
|
|
// pass sync fd if set
|
|
if syncFd != nil {
|
|
fd := proc.ExtraFile(s.cmd, syncFd)
|
|
s.sync = &fd
|
|
}
|
|
|
|
fmsg.Verbose("starting shim via fsu:", s.cmd)
|
|
// withhold messages to stderr
|
|
fmsg.Suspend()
|
|
if err := s.cmd.Start(); err != nil {
|
|
return nil, fmsg.WrapErrorSuffix(err,
|
|
"cannot start fsu:")
|
|
}
|
|
startTime := time.Now().UTC()
|
|
return &startTime, nil
|
|
}
|
|
|
|
func (s *Shim) Serve(ctx context.Context, payload *Payload) error {
|
|
// kill shim if something goes wrong and an error is returned
|
|
s.killFallback = make(chan error, 1)
|
|
killShim := func() {
|
|
if err := s.cmd.Process.Signal(os.Interrupt); err != nil {
|
|
s.killFallback <- err
|
|
}
|
|
}
|
|
defer func() { killShim() }()
|
|
|
|
payload.Sync = s.sync
|
|
encodeErr := make(chan error)
|
|
go func() { encodeErr <- s.encoder.Encode(payload) }()
|
|
|
|
select {
|
|
// encode return indicates setup completion
|
|
case err := <-encodeErr:
|
|
if err != nil {
|
|
return fmsg.WrapErrorSuffix(err,
|
|
"cannot transmit shim config:")
|
|
}
|
|
killShim = func() {}
|
|
return nil
|
|
|
|
// setup canceled before payload was accepted
|
|
case <-ctx.Done():
|
|
err := ctx.Err()
|
|
if errors.Is(err, context.Canceled) {
|
|
return fmsg.WrapError(errors.New("shim setup canceled"),
|
|
"shim setup canceled")
|
|
}
|
|
if errors.Is(err, context.DeadlineExceeded) {
|
|
return fmsg.WrapError(errors.New("deadline exceeded waiting for shim"),
|
|
"deadline exceeded waiting for shim")
|
|
}
|
|
// unreachable
|
|
return err
|
|
}
|
|
}
|