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 hasSysAdmin, _ := cap.GetBound(cap.SYS_ADMIN) 24 25 vs := []struct { 26 args []string 27 fail bool 28 callbackFn func(*syscall.ProcAttr, interface{}) error 29 chroot string 30 iab string 31 uid int 32 gid int 33 mode cap.Mode 34 groups []int 35 }{ 36 {args: []string{root + "/go/ok"}}, 37 { 38 args: []string{root + "/progs/tcapsh-static", "--dropped=cap_chown", "--is-uid=123", "--is-gid=456", "--has-a=cap_setuid"}, 39 iab: "!cap_chown,^cap_setuid,cap_sys_admin", 40 uid: 123, 41 gid: 456, 42 groups: []int{1, 2, 3}, 43 fail: syscall.Getuid() != 0 || !hasSysAdmin, 44 }, 45 { 46 args: []string{"/ok"}, 47 chroot: root + "/go", 48 fail: syscall.Getuid() != 0, 49 }, 50 { 51 args: []string{root + "/progs/tcapsh-static", "--inmode=NOPRIV", "--has-no-new-privs"}, 52 mode: cap.ModeNoPriv, 53 fail: syscall.Getuid() != 0, 54 }, 55 } 56 57 ps := make([]int, len(vs)) 58 ws := make([]syscall.WaitStatus, len(vs)) 59 60 for i, v := range vs { 61 e := cap.NewLauncher(v.args[0], v.args, nil) 62 e.Callback(v.callbackFn) 63 if v.chroot != "" { 64 e.SetChroot(v.chroot) 65 } 66 if v.uid != 0 { 67 e.SetUID(v.uid) 68 } 69 if v.gid != 0 { 70 e.SetGroups(v.gid, v.groups) 71 } 72 if v.mode != 0 { 73 e.SetMode(v.mode) 74 } 75 if v.iab != "" { 76 if iab, err := cap.IABFromText(v.iab); err != nil { 77 log.Fatalf("failed to parse iab=%q: %v", v.iab, err) 78 } else { 79 e.SetIAB(iab) 80 } 81 } 82 log.Printf("[%d] trying: %q\n", i, v.args) 83 if ps[i], err = e.Launch(nil); err != nil { 84 if v.fail { 85 continue 86 } 87 log.Fatalf("[%d] launch %q failed: %v", i, v.args, err) 88 } 89 } 90 91 for i, p := range ps { 92 if p == -1 { 93 continue 94 } 95 if pr, err := syscall.Wait4(p, &ws[i], 0, nil); err != nil { 96 log.Fatalf("wait4 <%d> failed: %v", p, err) 97 } else if p != pr { 98 log.Fatalf("wait4 <%d> returned <%d> instead", p, pr) 99 } else if ws[i] != 0 { 100 if vs[i].fail { 101 continue 102 } 103 log.Fatalf("wait4 <%d> status was %d", p, ws[i]) 104 } 105 } 106} 107 108func main() { 109 if cap.LaunchSupported { 110 // The Go runtime had some OS threading bugs that 111 // prevented Launch from working. Specifically, the 112 // launch OS thread would get reused. 113 tryLaunching() 114 } 115 fmt.Println("PASSED") 116} 117