xref: /aosp_15_r20/external/libcap/go/try-launching.go (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
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