1// Program try-launching validates the cap.Launch feature. 2package main 3 4import ( 5 "fmt" 6 "log" 7 "strings" 8 "syscall" 9 10 "kernel.org/pub/linux/libs/security/libcap/cap" 11) 12 13// tryLaunching attempts to launch a bunch of programs in parallel. It 14// first tries some unprivileged launches, and then (if privileged) 15// tries some more ambitious ones. 16func tryLaunching() { 17 cwd, err := syscall.Getwd() 18 if err != nil { 19 log.Fatalf("no working directory: %v", err) 20 } 21 root := cwd[:strings.LastIndex(cwd, "/")] 22 23 vs := []struct { 24 args []string 25 fail bool 26 callbackFn func(*syscall.ProcAttr, interface{}) error 27 chroot string 28 iab string 29 uid int 30 gid int 31 mode cap.Mode 32 groups []int 33 }{ 34 {args: []string{root + "/go/ok"}}, 35 { 36 args: []string{root + "/progs/tcapsh-static", "--dropped=cap_chown", "--is-uid=123", "--is-gid=456", "--has-a=cap_setuid"}, 37 iab: "!cap_chown,^cap_setuid,cap_sys_admin", 38 uid: 123, 39 gid: 456, 40 groups: []int{1, 2, 3}, 41 fail: syscall.Getuid() != 0, 42 }, 43 { 44 args: []string{"/ok"}, 45 chroot: root + "/go", 46 fail: syscall.Getuid() != 0, 47 }, 48 { 49 args: []string{root + "/progs/tcapsh-static", "--inmode=NOPRIV", "--has-no-new-privs"}, 50 mode: cap.ModeNoPriv, 51 fail: syscall.Getuid() != 0, 52 }, 53 } 54 55 ps := make([]int, len(vs)) 56 ws := make([]syscall.WaitStatus, len(vs)) 57 58 for i, v := range vs { 59 e := cap.NewLauncher(v.args[0], v.args, nil) 60 e.Callback(v.callbackFn) 61 if v.chroot != "" { 62 e.SetChroot(v.chroot) 63 } 64 if v.uid != 0 { 65 e.SetUID(v.uid) 66 } 67 if v.gid != 0 { 68 e.SetGroups(v.gid, v.groups) 69 } 70 if v.mode != 0 { 71 e.SetMode(v.mode) 72 } 73 if v.iab != "" { 74 if iab, err := cap.IABFromText(v.iab); err != nil { 75 log.Fatalf("failed to parse iab=%q: %v", v.iab, err) 76 } else { 77 e.SetIAB(iab) 78 } 79 } 80 log.Printf("[%d] trying: %q\n", i, v.args) 81 if ps[i], err = e.Launch(nil); err != nil { 82 if v.fail { 83 continue 84 } 85 log.Fatalf("[%d] launch %q failed: %v", i, v.args, err) 86 } 87 } 88 89 for i, p := range ps { 90 if p == -1 { 91 continue 92 } 93 if pr, err := syscall.Wait4(p, &ws[i], 0, nil); err != nil { 94 log.Fatalf("wait4 <%d> failed: %v", p, err) 95 } else if p != pr { 96 log.Fatalf("wait4 <%d> returned <%d> instead", p, pr) 97 } else if ws[i] != 0 { 98 if vs[i].fail { 99 continue 100 } 101 log.Fatalf("wait4 <%d> status was %d", p, ws[i]) 102 } 103 } 104} 105 106func main() { 107 if cap.LaunchSupported { 108 // The Go runtime had some OS threading bugs that 109 // prevented Launch from working. Specifically, the 110 // launch OS thread would get reused. 111 tryLaunching() 112 } 113 fmt.Println("PASSED") 114} 115