in cmd/hotdog-hotpatch/main.go [211:280]
func commandDroppedPrivs(name string, arg []string, uid, gid, targetPID int) ([]byte, error) {
cmd := cap.NewLauncher(name, append([]string{name}, arg...), nil)
if uid != 0 {
cmd.SetUID(uid)
cmd.SetMode(cap.ModeNoPriv)
logger.Printf("dropping all capabilities and switching UID to %d for %s", uid, name)
}
if gid != 0 {
cmd.SetGroups(gid, nil)
}
pr, pw, err := os.Pipe()
defer pw.Close()
if err != nil {
return nil, fmt.Errorf("failed to allocate pipe for %q: %w", name, err)
}
buf := &bytes.Buffer{}
go func() {
io.Copy(buf, pr)
pr.Close()
}()
cmd.Callback(func(attr *syscall.ProcAttr, i interface{}) error {
// Capture stdout/stderr, since we can't rely on the exec package to
//do it for us here.
attr.Files[1] = pw.Fd()
attr.Files[2] = pw.Fd()
// cap.* functions called within the callback only affect the locked OS
// thread used to launch the program
if targetPID != 0 && uid == 0 {
set, err := cap.GetPID(targetPID)
if err != nil {
return fmt.Errorf("failed to load caps for %d: %w", targetPID, err)
}
if err := set.ClearFlag(cap.Inheritable); err != nil {
return fmt.Errorf("failed to clear inheritable set: %w", err)
}
iabSet, err := cap.IABGetPID(targetPID)
if err != nil {
return fmt.Errorf("failed to load IAB for %d: %w", targetPID, err)
}
if err := iabSet.SetProc(); err != nil {
return fmt.Errorf("failed to set IAB: %w", err)
}
if err := cap.ResetAmbient(); err != nil {
return fmt.Errorf("failed to reset ambient: %w", err)
}
if err := set.SetProc(); err != nil {
return fmt.Errorf("failed to set caps: %w", err)
}
logger.Printf("setting caps to %q for %q", set.String(), name)
logger.Printf("setting IAB to %q for %q", iabSet.String(), name)
}
return nil
})
pid, err := cmd.Launch(nil)
if err != nil {
return nil, fmt.Errorf("failed to launch %q: %w", name, err)
}
proc := os.Process{Pid: pid}
state, err := proc.Wait()
if err != nil {
return nil, fmt.Errorf("failed to wait on %q: %w", name, err)
}
if state.ExitCode() != 0 {
err = &exec.ExitError{ProcessState: state}
}
versionOut := buf.Bytes()
return versionOut, err
}