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