xref: /aosp_15_r20/external/libcap/go/compare-cap.go (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1*2810ac1bSKiyoung Kim// Program compare-cap is a sanity check that Go's cap package is
2*2810ac1bSKiyoung Kim// inter-operable with the C libcap.
3*2810ac1bSKiyoung Kimpackage main
4*2810ac1bSKiyoung Kim
5*2810ac1bSKiyoung Kimimport (
6*2810ac1bSKiyoung Kim	"log"
7*2810ac1bSKiyoung Kim	"os"
8*2810ac1bSKiyoung Kim	"syscall"
9*2810ac1bSKiyoung Kim	"unsafe"
10*2810ac1bSKiyoung Kim
11*2810ac1bSKiyoung Kim	"kernel.org/pub/linux/libs/security/libcap/cap"
12*2810ac1bSKiyoung Kim)
13*2810ac1bSKiyoung Kim
14*2810ac1bSKiyoung Kim// #include <stdlib.h>
15*2810ac1bSKiyoung Kim// #include <sys/capability.h>
16*2810ac1bSKiyoung Kim// #cgo CFLAGS: -I../libcap/include
17*2810ac1bSKiyoung Kim// #cgo LDFLAGS: -L../libcap -lcap
18*2810ac1bSKiyoung Kimimport "C"
19*2810ac1bSKiyoung Kim
20*2810ac1bSKiyoung Kim// tryFileCaps attempts to use the cap package to manipulate file
21*2810ac1bSKiyoung Kim// capabilities. No reference to libcap in this function.
22*2810ac1bSKiyoung Kimfunc tryFileCaps() {
23*2810ac1bSKiyoung Kim	saved := cap.GetProc()
24*2810ac1bSKiyoung Kim
25*2810ac1bSKiyoung Kim	// Capabilities we will place on a file.
26*2810ac1bSKiyoung Kim	want := cap.NewSet()
27*2810ac1bSKiyoung Kim	if err := want.SetFlag(cap.Permitted, true, cap.SETFCAP, cap.DAC_OVERRIDE); err != nil {
28*2810ac1bSKiyoung Kim		log.Fatalf("failed to explore desired file capability: %v", err)
29*2810ac1bSKiyoung Kim	}
30*2810ac1bSKiyoung Kim	if err := want.SetFlag(cap.Effective, true, cap.SETFCAP, cap.DAC_OVERRIDE); err != nil {
31*2810ac1bSKiyoung Kim		log.Fatalf("failed to raise the effective bits: %v", err)
32*2810ac1bSKiyoung Kim	}
33*2810ac1bSKiyoung Kim
34*2810ac1bSKiyoung Kim	if perm, err := saved.GetFlag(cap.Permitted, cap.SETFCAP); err != nil {
35*2810ac1bSKiyoung Kim		log.Fatalf("failed to read capability: %v", err)
36*2810ac1bSKiyoung Kim	} else if !perm {
37*2810ac1bSKiyoung Kim		log.Printf("skipping file cap tests - insufficient privilege")
38*2810ac1bSKiyoung Kim		return
39*2810ac1bSKiyoung Kim	}
40*2810ac1bSKiyoung Kim
41*2810ac1bSKiyoung Kim	if err := saved.ClearFlag(cap.Effective); err != nil {
42*2810ac1bSKiyoung Kim		log.Fatalf("failed to drop effective: %v", err)
43*2810ac1bSKiyoung Kim	}
44*2810ac1bSKiyoung Kim	if err := saved.SetProc(); err != nil {
45*2810ac1bSKiyoung Kim		log.Fatalf("failed to limit capabilities: %v", err)
46*2810ac1bSKiyoung Kim	}
47*2810ac1bSKiyoung Kim
48*2810ac1bSKiyoung Kim	// Failing attempt to remove capabilities.
49*2810ac1bSKiyoung Kim	var empty *cap.Set
50*2810ac1bSKiyoung Kim	if err := empty.SetFile(os.Args[0]); err != syscall.EPERM {
51*2810ac1bSKiyoung Kim		log.Fatalf("failed to be blocked from removing filecaps: %v", err)
52*2810ac1bSKiyoung Kim	}
53*2810ac1bSKiyoung Kim
54*2810ac1bSKiyoung Kim	// The privilege we want (in the case we are root, we need the
55*2810ac1bSKiyoung Kim	// DAC_OVERRIDE too).
56*2810ac1bSKiyoung Kim	working, err := saved.Dup()
57*2810ac1bSKiyoung Kim	if err != nil {
58*2810ac1bSKiyoung Kim		log.Fatalf("failed to duplicate (%v): %v", saved, err)
59*2810ac1bSKiyoung Kim	}
60*2810ac1bSKiyoung Kim	if err := working.SetFlag(cap.Effective, true, cap.DAC_OVERRIDE, cap.SETFCAP); err != nil {
61*2810ac1bSKiyoung Kim		log.Fatalf("failed to raise effective: %v", err)
62*2810ac1bSKiyoung Kim	}
63*2810ac1bSKiyoung Kim
64*2810ac1bSKiyoung Kim	// Critical (privilege using) section:
65*2810ac1bSKiyoung Kim	if err := working.SetProc(); err != nil {
66*2810ac1bSKiyoung Kim		log.Fatalf("failed to enable first effective privilege: %v", err)
67*2810ac1bSKiyoung Kim	}
68*2810ac1bSKiyoung Kim	// Delete capability
69*2810ac1bSKiyoung Kim	if err := empty.SetFile(os.Args[0]); err != nil && err != syscall.ENODATA {
70*2810ac1bSKiyoung Kim		log.Fatalf("blocked from removing filecaps: %v", err)
71*2810ac1bSKiyoung Kim	}
72*2810ac1bSKiyoung Kim	if got, err := cap.GetFile(os.Args[0]); err == nil {
73*2810ac1bSKiyoung Kim		log.Fatalf("read deleted file caps: %v", got)
74*2810ac1bSKiyoung Kim	}
75*2810ac1bSKiyoung Kim	// Create file caps (this use employs the effective bit).
76*2810ac1bSKiyoung Kim	if err := want.SetFile(os.Args[0]); err != nil {
77*2810ac1bSKiyoung Kim		log.Fatalf("failed to set file capability: %v", err)
78*2810ac1bSKiyoung Kim	}
79*2810ac1bSKiyoung Kim	if err := saved.SetProc(); err != nil {
80*2810ac1bSKiyoung Kim		log.Fatalf("failed to lower effective capability: %v", err)
81*2810ac1bSKiyoung Kim	}
82*2810ac1bSKiyoung Kim	// End of critical section.
83*2810ac1bSKiyoung Kim
84*2810ac1bSKiyoung Kim	if got, err := cap.GetFile(os.Args[0]); err != nil {
85*2810ac1bSKiyoung Kim		log.Fatalf("failed to read caps: %v", err)
86*2810ac1bSKiyoung Kim	} else if is, was := got.String(), want.String(); is != was {
87*2810ac1bSKiyoung Kim		log.Fatalf("read file caps do not match desired: got=%q want=%q", is, was)
88*2810ac1bSKiyoung Kim	}
89*2810ac1bSKiyoung Kim
90*2810ac1bSKiyoung Kim	// Now, do it all again but this time on an open file.
91*2810ac1bSKiyoung Kim	f, err := os.Open(os.Args[0])
92*2810ac1bSKiyoung Kim	if err != nil {
93*2810ac1bSKiyoung Kim		log.Fatalf("failed to open %q: %v", os.Args[0], err)
94*2810ac1bSKiyoung Kim	}
95*2810ac1bSKiyoung Kim	defer f.Close()
96*2810ac1bSKiyoung Kim
97*2810ac1bSKiyoung Kim	// Failing attempt to remove capabilities.
98*2810ac1bSKiyoung Kim	if err := empty.SetFd(f); err != syscall.EPERM {
99*2810ac1bSKiyoung Kim		log.Fatalf("failed to be blocked from fremoving filecaps: %v", err)
100*2810ac1bSKiyoung Kim	}
101*2810ac1bSKiyoung Kim
102*2810ac1bSKiyoung Kim	// For the next section, we won't set the effective bit on the file.
103*2810ac1bSKiyoung Kim	want.ClearFlag(cap.Effective)
104*2810ac1bSKiyoung Kim
105*2810ac1bSKiyoung Kim	// Critical (privilege using) section:
106*2810ac1bSKiyoung Kim	if err := working.SetProc(); err != nil {
107*2810ac1bSKiyoung Kim		log.Fatalf("failed to enable effective privilege: %v", err)
108*2810ac1bSKiyoung Kim	}
109*2810ac1bSKiyoung Kim	if err := empty.SetFd(f); err != nil && err != syscall.ENODATA {
110*2810ac1bSKiyoung Kim		log.Fatalf("blocked from fremoving filecaps: %v", err)
111*2810ac1bSKiyoung Kim	}
112*2810ac1bSKiyoung Kim	if got, err := cap.GetFd(f); err == nil {
113*2810ac1bSKiyoung Kim		log.Fatalf("read fdeleted file caps: %v", got)
114*2810ac1bSKiyoung Kim	}
115*2810ac1bSKiyoung Kim	// This one does not set the effective bit.
116*2810ac1bSKiyoung Kim	if err := want.SetFd(f); err != nil {
117*2810ac1bSKiyoung Kim		log.Fatalf("failed to fset file capability: %v", err)
118*2810ac1bSKiyoung Kim	}
119*2810ac1bSKiyoung Kim	if got, err := cap.GetFd(f); err != nil {
120*2810ac1bSKiyoung Kim		log.Fatalf("failed to fread caps: %v", err)
121*2810ac1bSKiyoung Kim	} else if is, was := got.String(), want.String(); is != was {
122*2810ac1bSKiyoung Kim		log.Fatalf("fread file caps do not match desired: got=%q want=%q", is, was)
123*2810ac1bSKiyoung Kim	}
124*2810ac1bSKiyoung Kim	if err := empty.SetFd(f); err != nil && err != syscall.ENODATA {
125*2810ac1bSKiyoung Kim		log.Fatalf("blocked from cleanup fremoving filecaps: %v", err)
126*2810ac1bSKiyoung Kim	}
127*2810ac1bSKiyoung Kim	if err := saved.SetProc(); err != nil {
128*2810ac1bSKiyoung Kim		log.Fatalf("failed to lower effective capability: %v", err)
129*2810ac1bSKiyoung Kim	}
130*2810ac1bSKiyoung Kim	// End of critical section.
131*2810ac1bSKiyoung Kim}
132*2810ac1bSKiyoung Kim
133*2810ac1bSKiyoung Kim// tryProcCaps performs a set of convenience functions and compares
134*2810ac1bSKiyoung Kim// the results with those seen by libcap. At the end of this function,
135*2810ac1bSKiyoung Kim// the running process has no privileges at all. So exiting the
136*2810ac1bSKiyoung Kim// program is the only option.
137*2810ac1bSKiyoung Kimfunc tryProcCaps() {
138*2810ac1bSKiyoung Kim	c := cap.GetProc()
139*2810ac1bSKiyoung Kim	if v, err := c.GetFlag(cap.Permitted, cap.SETPCAP); err != nil {
140*2810ac1bSKiyoung Kim		log.Fatalf("failed to read permitted setpcap: %v", err)
141*2810ac1bSKiyoung Kim	} else if !v {
142*2810ac1bSKiyoung Kim		log.Printf("skipping proc cap tests - insufficient privilege")
143*2810ac1bSKiyoung Kim		return
144*2810ac1bSKiyoung Kim	}
145*2810ac1bSKiyoung Kim	if err := cap.SetUID(99); err != nil {
146*2810ac1bSKiyoung Kim		log.Fatalf("failed to set uid=99: %v", err)
147*2810ac1bSKiyoung Kim	}
148*2810ac1bSKiyoung Kim	if u := syscall.Getuid(); u != 99 {
149*2810ac1bSKiyoung Kim		log.Fatal("uid=99 did not take: got=%d", u)
150*2810ac1bSKiyoung Kim	}
151*2810ac1bSKiyoung Kim	if err := cap.SetGroups(98, 100, 101); err != nil {
152*2810ac1bSKiyoung Kim		log.Fatalf("failed to set groups=98 [100, 101]: %v", err)
153*2810ac1bSKiyoung Kim	}
154*2810ac1bSKiyoung Kim	if g := syscall.Getgid(); g != 98 {
155*2810ac1bSKiyoung Kim		log.Fatalf("gid=98 did not take: got=%d", g)
156*2810ac1bSKiyoung Kim	}
157*2810ac1bSKiyoung Kim	if gs, err := syscall.Getgroups(); err != nil {
158*2810ac1bSKiyoung Kim		log.Fatalf("error getting groups: %v", err)
159*2810ac1bSKiyoung Kim	} else if len(gs) != 2 || gs[0] != 100 || gs[1] != 101 {
160*2810ac1bSKiyoung Kim		log.Fatalf("wrong of groups: got=%v want=[100 l01]", gs)
161*2810ac1bSKiyoung Kim	}
162*2810ac1bSKiyoung Kim
163*2810ac1bSKiyoung Kim	if mode := cap.GetMode(); mode != cap.ModeHybrid {
164*2810ac1bSKiyoung Kim		log.Fatalf("initial mode should be 4 (HYBRID), got: %d (%v)", mode, mode)
165*2810ac1bSKiyoung Kim	}
166*2810ac1bSKiyoung Kim
167*2810ac1bSKiyoung Kim	// To distinguish PURE1E and PURE1E_INIT we need an inheritable capability set.
168*2810ac1bSKiyoung Kim	working := cap.GetProc()
169*2810ac1bSKiyoung Kim	if err := working.SetFlag(cap.Inheritable, true, cap.SETPCAP); err != nil {
170*2810ac1bSKiyoung Kim		log.Fatalf("unable to raise inheritable bit: %v", err)
171*2810ac1bSKiyoung Kim	}
172*2810ac1bSKiyoung Kim	if err := working.SetProc(); err != nil {
173*2810ac1bSKiyoung Kim		log.Fatalf("failed to add inheritable bit: %v", err)
174*2810ac1bSKiyoung Kim	}
175*2810ac1bSKiyoung Kim
176*2810ac1bSKiyoung Kim	for i, mode := range []cap.Mode{cap.ModePure1E, cap.ModePure1EInit, cap.ModeNoPriv} {
177*2810ac1bSKiyoung Kim		if err := mode.Set(); err != nil {
178*2810ac1bSKiyoung Kim			log.Fatalf("[%d] in mode=%v and failed to set mode to %d (%v): %v", i, cap.GetMode(), mode, mode, err)
179*2810ac1bSKiyoung Kim		}
180*2810ac1bSKiyoung Kim		if got := cap.GetMode(); got != mode {
181*2810ac1bSKiyoung Kim			log.Fatalf("[%d] unable to recognise mode %d (%v), got: %d (%v)", i, mode, mode, got, got)
182*2810ac1bSKiyoung Kim		}
183*2810ac1bSKiyoung Kim		cM := C.cap_get_mode()
184*2810ac1bSKiyoung Kim		if mode != cap.Mode(cM) {
185*2810ac1bSKiyoung Kim			log.Fatalf("[%d] C and Go disagree on mode: %d vs %d", cM, mode)
186*2810ac1bSKiyoung Kim		}
187*2810ac1bSKiyoung Kim	}
188*2810ac1bSKiyoung Kim
189*2810ac1bSKiyoung Kim	// The current process is now without any access to privilege.
190*2810ac1bSKiyoung Kim}
191*2810ac1bSKiyoung Kim
192*2810ac1bSKiyoung Kimfunc main() {
193*2810ac1bSKiyoung Kim	// Use the C libcap to obtain a non-trivial capability in text form (from init).
194*2810ac1bSKiyoung Kim	cC := C.cap_get_pid(1)
195*2810ac1bSKiyoung Kim	if cC == nil {
196*2810ac1bSKiyoung Kim		log.Fatal("basic c caps from init function failure")
197*2810ac1bSKiyoung Kim	}
198*2810ac1bSKiyoung Kim	defer C.cap_free(unsafe.Pointer(cC))
199*2810ac1bSKiyoung Kim	var tCLen C.ssize_t
200*2810ac1bSKiyoung Kim	tC := C.cap_to_text(cC, &tCLen)
201*2810ac1bSKiyoung Kim	if tC == nil {
202*2810ac1bSKiyoung Kim		log.Fatal("basic c init caps -> text failure")
203*2810ac1bSKiyoung Kim	}
204*2810ac1bSKiyoung Kim	defer C.cap_free(unsafe.Pointer(tC))
205*2810ac1bSKiyoung Kim
206*2810ac1bSKiyoung Kim	importT := C.GoString(tC)
207*2810ac1bSKiyoung Kim	if got, want := len(importT), int(tCLen); got != want {
208*2810ac1bSKiyoung Kim		log.Fatalf("C string import failed: got=%d [%q] want=%d", got, importT, want)
209*2810ac1bSKiyoung Kim	}
210*2810ac1bSKiyoung Kim
211*2810ac1bSKiyoung Kim	// Validate that it can be decoded in Go.
212*2810ac1bSKiyoung Kim	cGo, err := cap.FromText(importT)
213*2810ac1bSKiyoung Kim	if err != nil {
214*2810ac1bSKiyoung Kim		log.Fatalf("go parsing of c text import failed: %v", err)
215*2810ac1bSKiyoung Kim	}
216*2810ac1bSKiyoung Kim
217*2810ac1bSKiyoung Kim	// Validate that it matches the one directly loaded in Go.
218*2810ac1bSKiyoung Kim	c, err := cap.GetPID(1)
219*2810ac1bSKiyoung Kim	if err != nil {
220*2810ac1bSKiyoung Kim		log.Fatalf("...failed to read init's capabilities:", err)
221*2810ac1bSKiyoung Kim	}
222*2810ac1bSKiyoung Kim	tGo := c.String()
223*2810ac1bSKiyoung Kim	if got, want := tGo, cGo.String(); got != want {
224*2810ac1bSKiyoung Kim		log.Fatalf("go text rep does not match c: got=%q, want=%q", got, want)
225*2810ac1bSKiyoung Kim	}
226*2810ac1bSKiyoung Kim
227*2810ac1bSKiyoung Kim	// Export it in text form again from Go.
228*2810ac1bSKiyoung Kim	tForC := C.CString(tGo)
229*2810ac1bSKiyoung Kim	defer C.free(unsafe.Pointer(tForC))
230*2810ac1bSKiyoung Kim
231*2810ac1bSKiyoung Kim	// Validate it can be encoded in C.
232*2810ac1bSKiyoung Kim	cC2 := C.cap_from_text(tForC)
233*2810ac1bSKiyoung Kim	if cC2 == nil {
234*2810ac1bSKiyoung Kim		log.Fatal("go text rep not parsable by c")
235*2810ac1bSKiyoung Kim	}
236*2810ac1bSKiyoung Kim	defer C.cap_free(unsafe.Pointer(cC2))
237*2810ac1bSKiyoung Kim
238*2810ac1bSKiyoung Kim	// Validate that it can be exported in binary form in C
239*2810ac1bSKiyoung Kim	const enoughForAnyone = 1000
240*2810ac1bSKiyoung Kim	eC := make([]byte, enoughForAnyone)
241*2810ac1bSKiyoung Kim	eCLen := C.cap_copy_ext(unsafe.Pointer(&eC[0]), cC2, C.ssize_t(len(eC)))
242*2810ac1bSKiyoung Kim	if eCLen < 5 {
243*2810ac1bSKiyoung Kim		log.Fatalf("c export yielded bad length: %d", eCLen)
244*2810ac1bSKiyoung Kim	}
245*2810ac1bSKiyoung Kim
246*2810ac1bSKiyoung Kim	// Validate that it can be imported from binary in Go
247*2810ac1bSKiyoung Kim	iGo, err := cap.Import(eC[:eCLen])
248*2810ac1bSKiyoung Kim	if err != nil {
249*2810ac1bSKiyoung Kim		log.Fatalf("go import of c binary failed: %v", err)
250*2810ac1bSKiyoung Kim	}
251*2810ac1bSKiyoung Kim	if got, want := iGo.String(), importT; got != want {
252*2810ac1bSKiyoung Kim		log.Fatalf("go import of c binary miscompare: got=%q want=%q", got, want)
253*2810ac1bSKiyoung Kim	}
254*2810ac1bSKiyoung Kim
255*2810ac1bSKiyoung Kim	// Validate that it can be exported in binary in Go
256*2810ac1bSKiyoung Kim	iE, err := iGo.Export()
257*2810ac1bSKiyoung Kim	if err != nil {
258*2810ac1bSKiyoung Kim		log.Fatalf("go failed to export binary: %v", err)
259*2810ac1bSKiyoung Kim	}
260*2810ac1bSKiyoung Kim
261*2810ac1bSKiyoung Kim	// Validate that it can be imported in binary in C
262*2810ac1bSKiyoung Kim	iC := C.cap_copy_int_check(unsafe.Pointer(&iE[0]), C.ssize_t(len(iE)))
263*2810ac1bSKiyoung Kim	if iC == nil {
264*2810ac1bSKiyoung Kim		log.Fatal("c failed to import go binary")
265*2810ac1bSKiyoung Kim	}
266*2810ac1bSKiyoung Kim	defer C.cap_free(unsafe.Pointer(iC))
267*2810ac1bSKiyoung Kim	fC := C.cap_to_text(iC, &tCLen)
268*2810ac1bSKiyoung Kim	if fC == nil {
269*2810ac1bSKiyoung Kim		log.Fatal("basic c init caps -> text failure")
270*2810ac1bSKiyoung Kim	}
271*2810ac1bSKiyoung Kim	defer C.cap_free(unsafe.Pointer(fC))
272*2810ac1bSKiyoung Kim	if got, want := C.GoString(fC), importT; got != want {
273*2810ac1bSKiyoung Kim		log.Fatalf("c import from go yielded bad caps: got=%q want=%q", got, want)
274*2810ac1bSKiyoung Kim	}
275*2810ac1bSKiyoung Kim
276*2810ac1bSKiyoung Kim	// Validate that everyone agrees what all is:
277*2810ac1bSKiyoung Kim	want := "=ep"
278*2810ac1bSKiyoung Kim	all, err := cap.FromText("all=ep")
279*2810ac1bSKiyoung Kim	if err != nil {
280*2810ac1bSKiyoung Kim		log.Fatalf("unable to parse all=ep: %v", err)
281*2810ac1bSKiyoung Kim	}
282*2810ac1bSKiyoung Kim	if got := all.String(); got != want {
283*2810ac1bSKiyoung Kim		log.Fatalf("all decode failed in Go: got=%q, want=%q", got, want)
284*2810ac1bSKiyoung Kim	}
285*2810ac1bSKiyoung Kim
286*2810ac1bSKiyoung Kim	// Validate some random values stringify consistently between
287*2810ac1bSKiyoung Kim	// libcap.cap_to_text() and (*cap.Set).String().
288*2810ac1bSKiyoung Kim	mb := cap.MaxBits()
289*2810ac1bSKiyoung Kim	sample := cap.NewSet()
290*2810ac1bSKiyoung Kim	for c := cap.Value(0); c < 7*mb; c += 3 {
291*2810ac1bSKiyoung Kim		n := int(c)
292*2810ac1bSKiyoung Kim		raise, f := c%mb, cap.Flag(c/mb)%3
293*2810ac1bSKiyoung Kim		sample.SetFlag(f, true, raise)
294*2810ac1bSKiyoung Kim		if v, err := cap.FromText(sample.String()); err != nil {
295*2810ac1bSKiyoung Kim			log.Fatalf("[%d] cap to text for %q not reversible: %v", n, sample, err)
296*2810ac1bSKiyoung Kim		} else if cf, err := v.Compare(sample); err != nil {
297*2810ac1bSKiyoung Kim			log.Fatalf("[%d] FromText generated bad capability from %q: %v", n, sample, err)
298*2810ac1bSKiyoung Kim		} else if cf != 0 {
299*2810ac1bSKiyoung Kim			log.Fatalf("[%d] text import got=%q want=%q", n, v, sample)
300*2810ac1bSKiyoung Kim		}
301*2810ac1bSKiyoung Kim		e, err := sample.Export()
302*2810ac1bSKiyoung Kim		if err != nil {
303*2810ac1bSKiyoung Kim			log.Fatalf("[%d] failed to export %q: %v", n, sample, err)
304*2810ac1bSKiyoung Kim		}
305*2810ac1bSKiyoung Kim		i, err := cap.Import(e)
306*2810ac1bSKiyoung Kim		if err != nil {
307*2810ac1bSKiyoung Kim			log.Fatalf("[%d] failed to import %q: %v", n, sample, err)
308*2810ac1bSKiyoung Kim		}
309*2810ac1bSKiyoung Kim		if cf, err := i.Compare(sample); err != nil {
310*2810ac1bSKiyoung Kim			log.Fatalf("[%d] failed to compare %q vs original:%q", n, i, sample)
311*2810ac1bSKiyoung Kim		} else if cf != 0 {
312*2810ac1bSKiyoung Kim			log.Fatalf("[%d] import got=%q want=%q", n, i, sample)
313*2810ac1bSKiyoung Kim		}
314*2810ac1bSKiyoung Kim		// Confirm that importing this portable binary
315*2810ac1bSKiyoung Kim		// representation in libcap and converting to text,
316*2810ac1bSKiyoung Kim		// generates the same text as Go generates. This was
317*2810ac1bSKiyoung Kim		// broken prior to v0.2.41.
318*2810ac1bSKiyoung Kim		cCap := C.cap_copy_int(unsafe.Pointer(&e[0]))
319*2810ac1bSKiyoung Kim		if cCap == nil {
320*2810ac1bSKiyoung Kim			log.Fatalf("[%d] C import failed for %q export", n, sample)
321*2810ac1bSKiyoung Kim		}
322*2810ac1bSKiyoung Kim		var tCLen C.ssize_t
323*2810ac1bSKiyoung Kim		tC := C.cap_to_text(cCap, &tCLen)
324*2810ac1bSKiyoung Kim		if tC == nil {
325*2810ac1bSKiyoung Kim			log.Fatalf("[%d] basic c init caps -> text failure", n)
326*2810ac1bSKiyoung Kim		}
327*2810ac1bSKiyoung Kim		C.cap_free(unsafe.Pointer(cCap))
328*2810ac1bSKiyoung Kim		importT := C.GoString(tC)
329*2810ac1bSKiyoung Kim		C.cap_free(unsafe.Pointer(tC))
330*2810ac1bSKiyoung Kim		if got, want := len(importT), int(tCLen); got != want {
331*2810ac1bSKiyoung Kim			log.Fatalf("[%d] C text generated wrong length: Go=%d, C=%d", n, got, want)
332*2810ac1bSKiyoung Kim		}
333*2810ac1bSKiyoung Kim		if got, want := importT, sample.String(); got != want {
334*2810ac1bSKiyoung Kim			log.Fatalf("[%d] C and Go text rep disparity: C=%q Go=%q", n, got, want)
335*2810ac1bSKiyoung Kim		}
336*2810ac1bSKiyoung Kim	}
337*2810ac1bSKiyoung Kim
338*2810ac1bSKiyoung Kim	iab, err := cap.IABFromText("cap_chown,!cap_setuid,^cap_setgid")
339*2810ac1bSKiyoung Kim	if err != nil {
340*2810ac1bSKiyoung Kim		log.Fatalf("failed to initialize iab from text: %v", err)
341*2810ac1bSKiyoung Kim	}
342*2810ac1bSKiyoung Kim	cIAB := C.cap_iab_init()
343*2810ac1bSKiyoung Kim	defer C.cap_free(unsafe.Pointer(cIAB))
344*2810ac1bSKiyoung Kim	for c := cap.MaxBits(); c > 0; {
345*2810ac1bSKiyoung Kim		c--
346*2810ac1bSKiyoung Kim		if en, err := iab.GetVector(cap.Inh, c); err != nil {
347*2810ac1bSKiyoung Kim			log.Fatalf("failed to read iab.i[%v]", c)
348*2810ac1bSKiyoung Kim		} else if en {
349*2810ac1bSKiyoung Kim			if C.cap_iab_set_vector(cIAB, C.CAP_IAB_INH, C.cap_value_t(int(c)), C.CAP_SET) != 0 {
350*2810ac1bSKiyoung Kim				log.Fatalf("failed to set C's AIB.I %v: %v", c)
351*2810ac1bSKiyoung Kim			}
352*2810ac1bSKiyoung Kim		}
353*2810ac1bSKiyoung Kim		if en, err := iab.GetVector(cap.Amb, c); err != nil {
354*2810ac1bSKiyoung Kim			log.Fatalf("failed to read iab.a[%v]", c)
355*2810ac1bSKiyoung Kim		} else if en {
356*2810ac1bSKiyoung Kim			if C.cap_iab_set_vector(cIAB, C.CAP_IAB_AMB, C.cap_value_t(int(c)), C.CAP_SET) != 0 {
357*2810ac1bSKiyoung Kim				log.Fatalf("failed to set C's AIB.A %v: %v", c)
358*2810ac1bSKiyoung Kim			}
359*2810ac1bSKiyoung Kim		}
360*2810ac1bSKiyoung Kim		if en, err := iab.GetVector(cap.Bound, c); err != nil {
361*2810ac1bSKiyoung Kim			log.Fatalf("failed to read iab.b[%v]", c)
362*2810ac1bSKiyoung Kim		} else if en {
363*2810ac1bSKiyoung Kim			if C.cap_iab_set_vector(cIAB, C.CAP_IAB_BOUND, C.cap_value_t(int(c)), C.CAP_SET) != 0 {
364*2810ac1bSKiyoung Kim				log.Fatalf("failed to set C's AIB.B %v: %v", c)
365*2810ac1bSKiyoung Kim			}
366*2810ac1bSKiyoung Kim		}
367*2810ac1bSKiyoung Kim	}
368*2810ac1bSKiyoung Kim	iabC := C.cap_iab_to_text(cIAB)
369*2810ac1bSKiyoung Kim	if iabC == nil {
370*2810ac1bSKiyoung Kim		log.Fatalf("failed to get text from C for %q", iab)
371*2810ac1bSKiyoung Kim	}
372*2810ac1bSKiyoung Kim	defer C.cap_free(unsafe.Pointer(iabC))
373*2810ac1bSKiyoung Kim	if got, want := C.GoString(iabC), iab.String(); got != want {
374*2810ac1bSKiyoung Kim		log.Fatalf("IAB for Go and C differ: got=%q, want=%q", got, want)
375*2810ac1bSKiyoung Kim	}
376*2810ac1bSKiyoung Kim
377*2810ac1bSKiyoung Kim	// Next, we attempt to manipulate some file capabilities on
378*2810ac1bSKiyoung Kim	// the running program.  These are optional, based on whether
379*2810ac1bSKiyoung Kim	// the current program is capable enough and do not involve
380*2810ac1bSKiyoung Kim	// any cgo calls to libcap.
381*2810ac1bSKiyoung Kim	tryFileCaps()
382*2810ac1bSKiyoung Kim
383*2810ac1bSKiyoung Kim	// Nothing left to do but exit after this one.
384*2810ac1bSKiyoung Kim	tryProcCaps()
385*2810ac1bSKiyoung Kim	log.Printf("compare-cap success!")
386*2810ac1bSKiyoung Kim}
387