• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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