You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
115 lines
2.6 KiB
115 lines
2.6 KiB
// Program try-launching validates the cap.Launch feature.
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"kernel.org/pub/linux/libs/security/libcap/cap"
|
|
)
|
|
|
|
// tryLaunching attempts to launch a bunch of programs in parallel. It
|
|
// first tries some unprivileged launches, and then (if privileged)
|
|
// tries some more ambitious ones.
|
|
func tryLaunching() {
|
|
cwd, err := syscall.Getwd()
|
|
if err != nil {
|
|
log.Fatalf("no working directory: %v", err)
|
|
}
|
|
root := cwd[:strings.LastIndex(cwd, "/")]
|
|
|
|
vs := []struct {
|
|
args []string
|
|
fail bool
|
|
callbackFn func(*syscall.ProcAttr, interface{}) error
|
|
chroot string
|
|
iab string
|
|
uid int
|
|
gid int
|
|
mode cap.Mode
|
|
groups []int
|
|
}{
|
|
{args: []string{root + "/go/ok"}},
|
|
{
|
|
args: []string{root + "/progs/tcapsh-static", "--dropped=cap_chown", "--is-uid=123", "--is-gid=456", "--has-a=cap_setuid"},
|
|
iab: "!cap_chown,^cap_setuid,cap_sys_admin",
|
|
uid: 123,
|
|
gid: 456,
|
|
groups: []int{1, 2, 3},
|
|
fail: syscall.Getuid() != 0,
|
|
},
|
|
{
|
|
args: []string{"/ok"},
|
|
chroot: root + "/go",
|
|
fail: syscall.Getuid() != 0,
|
|
},
|
|
{
|
|
args: []string{root + "/progs/tcapsh-static", "--inmode=NOPRIV", "--has-no-new-privs"},
|
|
mode: cap.ModeNoPriv,
|
|
fail: syscall.Getuid() != 0,
|
|
},
|
|
}
|
|
|
|
ps := make([]int, len(vs))
|
|
ws := make([]syscall.WaitStatus, len(vs))
|
|
|
|
for i, v := range vs {
|
|
e := cap.NewLauncher(v.args[0], v.args, nil)
|
|
e.Callback(v.callbackFn)
|
|
if v.chroot != "" {
|
|
e.SetChroot(v.chroot)
|
|
}
|
|
if v.uid != 0 {
|
|
e.SetUID(v.uid)
|
|
}
|
|
if v.gid != 0 {
|
|
e.SetGroups(v.gid, v.groups)
|
|
}
|
|
if v.mode != 0 {
|
|
e.SetMode(v.mode)
|
|
}
|
|
if v.iab != "" {
|
|
if iab, err := cap.IABFromText(v.iab); err != nil {
|
|
log.Fatalf("failed to parse iab=%q: %v", v.iab, err)
|
|
} else {
|
|
e.SetIAB(iab)
|
|
}
|
|
}
|
|
log.Printf("[%d] trying: %q\n", i, v.args)
|
|
if ps[i], err = e.Launch(nil); err != nil {
|
|
if v.fail {
|
|
continue
|
|
}
|
|
log.Fatalf("[%d] launch %q failed: %v", i, v.args, err)
|
|
}
|
|
}
|
|
|
|
for i, p := range ps {
|
|
if p == -1 {
|
|
continue
|
|
}
|
|
if pr, err := syscall.Wait4(p, &ws[i], 0, nil); err != nil {
|
|
log.Fatalf("wait4 <%d> failed: %v", p, err)
|
|
} else if p != pr {
|
|
log.Fatalf("wait4 <%d> returned <%d> instead", p, pr)
|
|
} else if ws[i] != 0 {
|
|
if vs[i].fail {
|
|
continue
|
|
}
|
|
log.Fatalf("wait4 <%d> status was %d", p, ws[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
if cap.LaunchSupported {
|
|
// The Go runtime had some OS threading bugs that
|
|
// prevented Launch from working. Specifically, the
|
|
// launch OS thread would get reused.
|
|
tryLaunching()
|
|
}
|
|
fmt.Println("PASSED")
|
|
}
|