xref: /aosp_15_r20/external/libcap/goapps/gowns/gowns.go (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1*2810ac1bSKiyoung Kim// Program gowns is a small program to explore and demonstrate using
2*2810ac1bSKiyoung Kim// Go to Wrap a child in a NameSpace under Linux.
3*2810ac1bSKiyoung Kim//
4*2810ac1bSKiyoung Kim// Note, this program is under active development and should not be
5*2810ac1bSKiyoung Kim// considered stable. That is, it is more a worked example and may
6*2810ac1bSKiyoung Kim// change command line arguments and behavior from release to release.
7*2810ac1bSKiyoung Kim// Should it become stable, I'll remove this comment.
8*2810ac1bSKiyoung Kimpackage main
9*2810ac1bSKiyoung Kim
10*2810ac1bSKiyoung Kimimport (
11*2810ac1bSKiyoung Kim	"errors"
12*2810ac1bSKiyoung Kim	"flag"
13*2810ac1bSKiyoung Kim	"fmt"
14*2810ac1bSKiyoung Kim	"log"
15*2810ac1bSKiyoung Kim	"os"
16*2810ac1bSKiyoung Kim	"strings"
17*2810ac1bSKiyoung Kim	"syscall"
18*2810ac1bSKiyoung Kim
19*2810ac1bSKiyoung Kim	"kernel.org/pub/linux/libs/security/libcap/cap"
20*2810ac1bSKiyoung Kim)
21*2810ac1bSKiyoung Kim
22*2810ac1bSKiyoung Kim// nsDetail is how we summarize the type of namespace we want to
23*2810ac1bSKiyoung Kim// enter.
24*2810ac1bSKiyoung Kimtype nsDetail struct {
25*2810ac1bSKiyoung Kim	// uid holds the uid for the base user in this namespace (defaults to getuid).
26*2810ac1bSKiyoung Kim	uid int
27*2810ac1bSKiyoung Kim
28*2810ac1bSKiyoung Kim	// uidMap holds the namespace mapping of uid values.
29*2810ac1bSKiyoung Kim	uidMap []syscall.SysProcIDMap
30*2810ac1bSKiyoung Kim
31*2810ac1bSKiyoung Kim	// gid holds the gid for the base user in this namespace (defaults to getgid).
32*2810ac1bSKiyoung Kim	gid int
33*2810ac1bSKiyoung Kim
34*2810ac1bSKiyoung Kim	// uidMap holds the namespace mapping of gid values.
35*2810ac1bSKiyoung Kim	gidMap []syscall.SysProcIDMap
36*2810ac1bSKiyoung Kim}
37*2810ac1bSKiyoung Kim
38*2810ac1bSKiyoung Kimvar (
39*2810ac1bSKiyoung Kim	baseID = flag.Int("base", -1, "base id for uids and gids (-1 = invoker's uid)")
40*2810ac1bSKiyoung Kim	uid    = flag.Int("uid", -1, "uid of the hosting user")
41*2810ac1bSKiyoung Kim	gid    = flag.Int("gid", -1, "gid of the hosting user")
42*2810ac1bSKiyoung Kim	iab    = flag.String("iab", "", "IAB string for inheritable capabilities")
43*2810ac1bSKiyoung Kim	mode   = flag.String("mode", "", "force a libcap mode (capsh --modes for list)")
44*2810ac1bSKiyoung Kim
45*2810ac1bSKiyoung Kim	ns   = flag.Bool("ns", false, "enable user namespace features")
46*2810ac1bSKiyoung Kim	uids = flag.String("uids", "", "comma separated UID ranges to map contiguously (req. CAP_SETUID)")
47*2810ac1bSKiyoung Kim	gids = flag.String("gids", "", "comma separated GID ranges to map contiguously (req. CAP_SETGID)")
48*2810ac1bSKiyoung Kim
49*2810ac1bSKiyoung Kim	shell = flag.String("shell", "/bin/bash", "shell to be launched")
50*2810ac1bSKiyoung Kim	debug = flag.Bool("verbose", false, "more verbose output")
51*2810ac1bSKiyoung Kim)
52*2810ac1bSKiyoung Kim
53*2810ac1bSKiyoung Kim// r holds a base and count for a contiguous range.
54*2810ac1bSKiyoung Kimtype r struct {
55*2810ac1bSKiyoung Kim	base, count int
56*2810ac1bSKiyoung Kim}
57*2810ac1bSKiyoung Kim
58*2810ac1bSKiyoung Kim// ranges unpacks numerical ranges.
59*2810ac1bSKiyoung Kimfunc ranges(s string) []r {
60*2810ac1bSKiyoung Kim	if s == "" {
61*2810ac1bSKiyoung Kim		return nil
62*2810ac1bSKiyoung Kim	}
63*2810ac1bSKiyoung Kim	var rs []r
64*2810ac1bSKiyoung Kim	for _, n := range strings.Split(s, ",") {
65*2810ac1bSKiyoung Kim		var base, upper int
66*2810ac1bSKiyoung Kim		if _, err := fmt.Sscanf(n, "%d-%d", &base, &upper); err == nil {
67*2810ac1bSKiyoung Kim			if upper < base {
68*2810ac1bSKiyoung Kim				log.Fatalf("invalid range: [%d-%d]", base, upper)
69*2810ac1bSKiyoung Kim			}
70*2810ac1bSKiyoung Kim			rs = append(rs, r{
71*2810ac1bSKiyoung Kim				base:  base,
72*2810ac1bSKiyoung Kim				count: 1 + upper - base,
73*2810ac1bSKiyoung Kim			})
74*2810ac1bSKiyoung Kim		} else if _, err := fmt.Sscanf(n, "%d", &base); err == nil {
75*2810ac1bSKiyoung Kim			rs = append(rs, r{
76*2810ac1bSKiyoung Kim				base:  base,
77*2810ac1bSKiyoung Kim				count: 1,
78*2810ac1bSKiyoung Kim			})
79*2810ac1bSKiyoung Kim		} else {
80*2810ac1bSKiyoung Kim			log.Fatalf("unable to parse range [%s]", n)
81*2810ac1bSKiyoung Kim		}
82*2810ac1bSKiyoung Kim	}
83*2810ac1bSKiyoung Kim	return rs
84*2810ac1bSKiyoung Kim}
85*2810ac1bSKiyoung Kim
86*2810ac1bSKiyoung Kim// restart launches the program again with the remaining arguments.
87*2810ac1bSKiyoung Kimfunc restart() {
88*2810ac1bSKiyoung Kim	log.Fatalf("failed to restart: flags: %q %q", os.Args[0], flag.Args()[1:])
89*2810ac1bSKiyoung Kim}
90*2810ac1bSKiyoung Kim
91*2810ac1bSKiyoung Kim// errUnableToSetup is how nsSetup fails.
92*2810ac1bSKiyoung Kimvar errUnableToSetup = errors.New("data was not in supported format")
93*2810ac1bSKiyoung Kim
94*2810ac1bSKiyoung Kim// nsSetup is the callback used to enter the namespace for the user
95*2810ac1bSKiyoung Kim// via callback in the cap.Launcher mechanism.
96*2810ac1bSKiyoung Kimfunc nsSetup(pa *syscall.ProcAttr, data interface{}) error {
97*2810ac1bSKiyoung Kim	nsD, ok := data.(nsDetail)
98*2810ac1bSKiyoung Kim	if !ok {
99*2810ac1bSKiyoung Kim		return errUnableToSetup
100*2810ac1bSKiyoung Kim	}
101*2810ac1bSKiyoung Kim
102*2810ac1bSKiyoung Kim	if pa.Sys == nil {
103*2810ac1bSKiyoung Kim		pa.Sys = &syscall.SysProcAttr{}
104*2810ac1bSKiyoung Kim	}
105*2810ac1bSKiyoung Kim	pa.Sys.Cloneflags |= syscall.CLONE_NEWUSER
106*2810ac1bSKiyoung Kim	pa.Sys.UidMappings = nsD.uidMap
107*2810ac1bSKiyoung Kim	pa.Sys.GidMappings = nsD.gidMap
108*2810ac1bSKiyoung Kim	return nil
109*2810ac1bSKiyoung Kim}
110*2810ac1bSKiyoung Kim
111*2810ac1bSKiyoung Kimfunc parseRanges(detail *nsDetail, ids string, id int) []syscall.SysProcIDMap {
112*2810ac1bSKiyoung Kim	base := *baseID
113*2810ac1bSKiyoung Kim	if base < 0 {
114*2810ac1bSKiyoung Kim		base = detail.uid
115*2810ac1bSKiyoung Kim	}
116*2810ac1bSKiyoung Kim
117*2810ac1bSKiyoung Kim	list := []syscall.SysProcIDMap{
118*2810ac1bSKiyoung Kim		syscall.SysProcIDMap{
119*2810ac1bSKiyoung Kim			ContainerID: base,
120*2810ac1bSKiyoung Kim			HostID:      id,
121*2810ac1bSKiyoung Kim			Size:        1,
122*2810ac1bSKiyoung Kim		},
123*2810ac1bSKiyoung Kim	}
124*2810ac1bSKiyoung Kim
125*2810ac1bSKiyoung Kim	base++
126*2810ac1bSKiyoung Kim	for _, next := range ranges(ids) {
127*2810ac1bSKiyoung Kim		list = append(list,
128*2810ac1bSKiyoung Kim			syscall.SysProcIDMap{
129*2810ac1bSKiyoung Kim				ContainerID: base,
130*2810ac1bSKiyoung Kim				HostID:      next.base,
131*2810ac1bSKiyoung Kim				Size:        next.count,
132*2810ac1bSKiyoung Kim			})
133*2810ac1bSKiyoung Kim		base += next.count
134*2810ac1bSKiyoung Kim	}
135*2810ac1bSKiyoung Kim	return list
136*2810ac1bSKiyoung Kim}
137*2810ac1bSKiyoung Kim
138*2810ac1bSKiyoung Kimfunc main() {
139*2810ac1bSKiyoung Kim	flag.Parse()
140*2810ac1bSKiyoung Kim
141*2810ac1bSKiyoung Kim	detail := nsDetail{
142*2810ac1bSKiyoung Kim		gid: syscall.Getgid(),
143*2810ac1bSKiyoung Kim	}
144*2810ac1bSKiyoung Kim
145*2810ac1bSKiyoung Kim	thisUID := syscall.Getuid()
146*2810ac1bSKiyoung Kim	switch *uid {
147*2810ac1bSKiyoung Kim	case -1:
148*2810ac1bSKiyoung Kim		detail.uid = thisUID
149*2810ac1bSKiyoung Kim	default:
150*2810ac1bSKiyoung Kim		detail.uid = *uid
151*2810ac1bSKiyoung Kim	}
152*2810ac1bSKiyoung Kim	detail.uidMap = parseRanges(&detail, *uids, detail.uid)
153*2810ac1bSKiyoung Kim
154*2810ac1bSKiyoung Kim	thisGID := syscall.Getgid()
155*2810ac1bSKiyoung Kim	switch *gid {
156*2810ac1bSKiyoung Kim	case -1:
157*2810ac1bSKiyoung Kim		detail.gid = thisGID
158*2810ac1bSKiyoung Kim	default:
159*2810ac1bSKiyoung Kim		detail.gid = *gid
160*2810ac1bSKiyoung Kim	}
161*2810ac1bSKiyoung Kim	detail.gidMap = parseRanges(&detail, *gids, detail.gid)
162*2810ac1bSKiyoung Kim
163*2810ac1bSKiyoung Kim	unparsed := flag.Args()
164*2810ac1bSKiyoung Kim
165*2810ac1bSKiyoung Kim	arg0 := *shell
166*2810ac1bSKiyoung Kim	skip := 0
167*2810ac1bSKiyoung Kim	var w *cap.Launcher
168*2810ac1bSKiyoung Kim	if len(unparsed) > 0 {
169*2810ac1bSKiyoung Kim		switch unparsed[0] {
170*2810ac1bSKiyoung Kim		case "==":
171*2810ac1bSKiyoung Kim			arg0 = os.Args[0]
172*2810ac1bSKiyoung Kim			skip++
173*2810ac1bSKiyoung Kim		}
174*2810ac1bSKiyoung Kim	}
175*2810ac1bSKiyoung Kim
176*2810ac1bSKiyoung Kim	w = cap.NewLauncher(arg0, append([]string{arg0}, unparsed[skip:]...), nil)
177*2810ac1bSKiyoung Kim	if *ns {
178*2810ac1bSKiyoung Kim		// Include the namespace setup callback with the launcher.
179*2810ac1bSKiyoung Kim		w.Callback(nsSetup)
180*2810ac1bSKiyoung Kim	}
181*2810ac1bSKiyoung Kim
182*2810ac1bSKiyoung Kim	if thisUID != detail.uid {
183*2810ac1bSKiyoung Kim		w.SetUID(detail.uid)
184*2810ac1bSKiyoung Kim	}
185*2810ac1bSKiyoung Kim
186*2810ac1bSKiyoung Kim	if thisGID != detail.gid {
187*2810ac1bSKiyoung Kim		w.SetGroups(detail.gid, nil)
188*2810ac1bSKiyoung Kim	}
189*2810ac1bSKiyoung Kim
190*2810ac1bSKiyoung Kim	if *iab != "" {
191*2810ac1bSKiyoung Kim		ins, err := cap.IABFromText(*iab)
192*2810ac1bSKiyoung Kim		if err != nil {
193*2810ac1bSKiyoung Kim			log.Fatalf("--iab=%q parsing issue: %v", err)
194*2810ac1bSKiyoung Kim		}
195*2810ac1bSKiyoung Kim		w.SetIAB(ins)
196*2810ac1bSKiyoung Kim	}
197*2810ac1bSKiyoung Kim
198*2810ac1bSKiyoung Kim	if *mode != "" {
199*2810ac1bSKiyoung Kim		for m := cap.Mode(1); ; m++ {
200*2810ac1bSKiyoung Kim			if s := m.String(); s == "UNKNOWN" {
201*2810ac1bSKiyoung Kim				log.Fatalf("mode %q is unknown", *mode)
202*2810ac1bSKiyoung Kim			} else if s == *mode {
203*2810ac1bSKiyoung Kim				w.SetMode(m)
204*2810ac1bSKiyoung Kim				break
205*2810ac1bSKiyoung Kim			}
206*2810ac1bSKiyoung Kim		}
207*2810ac1bSKiyoung Kim	}
208*2810ac1bSKiyoung Kim
209*2810ac1bSKiyoung Kim	// The launcher can enable more functionality if involked with
210*2810ac1bSKiyoung Kim	// effective capabilities.
211*2810ac1bSKiyoung Kim	have := cap.GetProc()
212*2810ac1bSKiyoung Kim	for _, c := range []cap.Value{cap.SETUID, cap.SETGID} {
213*2810ac1bSKiyoung Kim		if canDo, err := have.GetFlag(cap.Permitted, c); err != nil {
214*2810ac1bSKiyoung Kim			log.Fatalf("failed to explore process capabilities, %q for %q", have, c)
215*2810ac1bSKiyoung Kim		} else if canDo {
216*2810ac1bSKiyoung Kim			if err := have.SetFlag(cap.Effective, true, c); err != nil {
217*2810ac1bSKiyoung Kim				log.Fatalf("failed to raise effective capability: \"%v e+%v\"", have, c)
218*2810ac1bSKiyoung Kim			}
219*2810ac1bSKiyoung Kim		}
220*2810ac1bSKiyoung Kim	}
221*2810ac1bSKiyoung Kim	if err := have.SetProc(); err != nil {
222*2810ac1bSKiyoung Kim		log.Fatalf("privilege assertion %q failed: %v", have, err)
223*2810ac1bSKiyoung Kim	}
224*2810ac1bSKiyoung Kim
225*2810ac1bSKiyoung Kim	if *debug {
226*2810ac1bSKiyoung Kim		if *ns {
227*2810ac1bSKiyoung Kim			fmt.Println("launching namespace")
228*2810ac1bSKiyoung Kim		} else {
229*2810ac1bSKiyoung Kim			fmt.Println("launching without namespace")
230*2810ac1bSKiyoung Kim		}
231*2810ac1bSKiyoung Kim	}
232*2810ac1bSKiyoung Kim
233*2810ac1bSKiyoung Kim	pid, err := w.Launch(detail)
234*2810ac1bSKiyoung Kim	if err != nil {
235*2810ac1bSKiyoung Kim		log.Fatalf("launch failed: %v", err)
236*2810ac1bSKiyoung Kim	}
237*2810ac1bSKiyoung Kim	if err := cap.NewSet().SetProc(); err != nil {
238*2810ac1bSKiyoung Kim		log.Fatalf("gowns could not drop privilege: %v", err)
239*2810ac1bSKiyoung Kim	}
240*2810ac1bSKiyoung Kim
241*2810ac1bSKiyoung Kim	p, err := os.FindProcess(pid)
242*2810ac1bSKiyoung Kim	if err != nil {
243*2810ac1bSKiyoung Kim		log.Fatalf("cannot find process: %v", err)
244*2810ac1bSKiyoung Kim	}
245*2810ac1bSKiyoung Kim	state, err := p.Wait()
246*2810ac1bSKiyoung Kim	if err != nil {
247*2810ac1bSKiyoung Kim		log.Fatalf("waiting failed: %v", err)
248*2810ac1bSKiyoung Kim	}
249*2810ac1bSKiyoung Kim
250*2810ac1bSKiyoung Kim	if *debug {
251*2810ac1bSKiyoung Kim		fmt.Println("process exited:", state)
252*2810ac1bSKiyoung Kim	}
253*2810ac1bSKiyoung Kim}
254