aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/app/shim.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/app/shim.go')
-rw-r--r--internal/app/shim.go165
1 files changed, 64 insertions, 101 deletions
diff --git a/internal/app/shim.go b/internal/app/shim.go
index 0c12606e..4ebb2c32 100644
--- a/internal/app/shim.go
+++ b/internal/app/shim.go
@@ -2,14 +2,11 @@ package app
import (
"context"
- "encoding/gob"
"errors"
"log"
"os"
"os/exec"
"os/signal"
- "strconv"
- "strings"
"syscall"
"time"
@@ -18,9 +15,63 @@ import (
"git.gensokyo.uk/security/fortify/sandbox"
)
+/*
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+
+static pid_t f_shim_param_ppid = -1;
+
+// this cannot unblock fmsg since Go code is not async-signal-safe
+static void f_shim_sigaction(int sig, siginfo_t *si, void *ucontext) {
+ if (sig != SIGCONT || si == NULL) {
+ // unreachable
+ fprintf(stderr, "sigaction: sa_sigaction got invalid siginfo\n");
+ return;
+ }
+
+ // monitor requests shim exit
+ if (si->si_pid == f_shim_param_ppid)
+ exit(254);
+
+ fprintf(stderr, "sigaction: got SIGCONT from process %d\n", si->si_pid);
+
+ // shim orphaned before monitor delivers a signal
+ if (getppid() != f_shim_param_ppid)
+ exit(3);
+}
+
+void f_shim_setup_cont_signal(pid_t ppid) {
+ struct sigaction new_action = {0}, old_action = {0};
+ if (sigaction(SIGCONT, NULL, &old_action) != 0)
+ return;
+ if (old_action.sa_handler != SIG_DFL) {
+ errno = ENOTRECOVERABLE;
+ return;
+ }
+
+ new_action.sa_sigaction = f_shim_sigaction;
+ if (sigemptyset(&new_action.sa_mask) != 0)
+ return;
+ new_action.sa_flags = SA_ONSTACK | SA_SIGINFO;
+
+ if (sigaction(SIGCONT, &new_action, NULL) != 0)
+ return;
+
+ errno = 0;
+ f_shim_param_ppid = ppid;
+}
+*/
+import "C"
+
const shimEnv = "FORTIFY_SHIM"
type shimParams struct {
+ // monitor pid, checked against ppid in signal handler
+ Monitor int
+
// finalised container params
Container *sandbox.Params
// path to outer home directory
@@ -54,6 +105,16 @@ func ShimMain() {
} else {
internal.InstallFmsg(params.Verbose)
closeSetup = f
+
+ // the Go runtime does not expose siginfo_t so SIGCONT is handled in C to check si_pid
+ if _, err = C.f_shim_setup_cont_signal(C.pid_t(params.Monitor)); err != nil {
+ log.Fatalf("cannot install SIGCONT handler: %v", err)
+ }
+
+ // pdeath_signal delivery is checked as if the dying process called kill(2), see kernel/exit.c
+ if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGCONT), 0); errno != 0 {
+ log.Fatalf("cannot set parent-death signal: %v", errno)
+ }
}
if params.Container == nil || params.Container.Ops == nil {
@@ -112,101 +173,3 @@ func ShimMain() {
os.Exit(exitError.ExitCode())
}
}
-
-type shimProcess 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
-}
-
-func (s *shimProcess) Unwrap() *exec.Cmd { return s.cmd }
-func (s *shimProcess) Fallback() chan error { return s.killFallback }
-
-func (s *shimProcess) String() string {
- if s.cmd == nil {
- return "(unused shim manager)"
- }
- return s.cmd.String()
-}
-
-func (s *shimProcess) Start(
- aid string,
- supp []string,
-) (*time.Time, error) {
- // prepare user switcher invocation
- fsuPath := internal.MustFsuPath()
- s.cmd = exec.Command(fsuPath)
-
- // pass shim setup pipe
- if fd, e, err := sandbox.Setup(&s.cmd.ExtraFiles); err != nil {
- return nil, fmsg.WrapErrorSuffix(err,
- "cannot create shim setup pipe:")
- } else {
- s.encoder = e
- s.cmd.Env = []string{
- shimEnv + "=" + 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 = "/"
-
- 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 *shimProcess) Serve(ctx context.Context, params *shimParams) 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() }()
-
- encodeErr := make(chan error)
- go func() { encodeErr <- s.encoder.Encode(params) }()
-
- 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(syscall.ECANCELED,
- "shim setup canceled")
- }
- if errors.Is(err, context.DeadlineExceeded) {
- return fmsg.WrapError(syscall.ETIMEDOUT,
- "deadline exceeded waiting for shim")
- }
- // unreachable
- return err
- }
-}