xref: /aosp_15_r20/external/libcap/cap/file.go (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1package cap
2
3import (
4	"bytes"
5	"encoding/binary"
6	"errors"
7	"io"
8	"os"
9	"syscall"
10	"unsafe"
11)
12
13// uapi/linux/xattr.h defined.
14var (
15	xattrNameCaps, _ = syscall.BytePtrFromString("security.capability")
16)
17
18// uapi/linux/capability.h defined.
19const (
20	vfsCapRevisionMask   = uint32(0xff000000)
21	vfsCapFlagsMask      = ^vfsCapRevisionMask
22	vfsCapFlagsEffective = uint32(1)
23
24	vfsCapRevision1 = uint32(0x01000000)
25	vfsCapRevision2 = uint32(0x02000000)
26	vfsCapRevision3 = uint32(0x03000000)
27)
28
29// Data types stored in little-endian order.
30
31type vfsCaps1 struct {
32	MagicEtc uint32
33	Data     [1]struct {
34		Permitted, Inheritable uint32
35	}
36}
37
38type vfsCaps2 struct {
39	MagicEtc uint32
40	Data     [2]struct {
41		Permitted, Inheritable uint32
42	}
43}
44
45type vfsCaps3 struct {
46	MagicEtc uint32
47	Data     [2]struct {
48		Permitted, Inheritable uint32
49	}
50	RootID uint32
51}
52
53// ErrBadSize indicates the loaded file capability has
54// an invalid number of bytes in it.
55var ErrBadSize = errors.New("filecap bad size")
56
57// ErrBadMagic indicates that the kernel preferred magic number for
58// capability Set values is not supported by this package. This
59// generally implies you are using an exceptionally old
60// "../libcap/cap" package. An upgrade is needed, or failing that see
61// https://sites.google.com/site/fullycapable/ for how to file a bug.
62var ErrBadMagic = errors.New("unsupported magic")
63
64// ErrBadPath indicates a failed attempt to set a file capability on
65// an irregular (non-executable) file.
66var ErrBadPath = errors.New("file is not a regular executable")
67
68// ErrOutOfRange indicates an erroneous value for MinExtFlagSize.
69var ErrOutOfRange = errors.New("flag length invalid for export")
70
71// digestFileCap unpacks a file capability and returns it in a *Set
72// form.
73func digestFileCap(d []byte, sz int, err error) (*Set, error) {
74	if err != nil {
75		return nil, err
76	}
77	var raw1 vfsCaps1
78	var raw2 vfsCaps2
79	var raw3 vfsCaps3
80	if sz < binary.Size(raw1) || sz > binary.Size(raw3) {
81		return nil, ErrBadSize
82	}
83	b := bytes.NewReader(d[:sz])
84	var magicEtc uint32
85	if err = binary.Read(b, binary.LittleEndian, &magicEtc); err != nil {
86		return nil, err
87	}
88
89	c := NewSet()
90	b.Seek(0, io.SeekStart)
91	switch magicEtc & vfsCapRevisionMask {
92	case vfsCapRevision1:
93		if err = binary.Read(b, binary.LittleEndian, &raw1); err != nil {
94			return nil, err
95		}
96		data := raw1.Data[0]
97		c.flat[0][Permitted] = data.Permitted
98		c.flat[0][Inheritable] = data.Inheritable
99		if raw1.MagicEtc&vfsCapFlagsMask == vfsCapFlagsEffective {
100			c.flat[0][Effective] = data.Inheritable | data.Permitted
101		}
102	case vfsCapRevision2:
103		if err = binary.Read(b, binary.LittleEndian, &raw2); err != nil {
104			return nil, err
105		}
106		for i, data := range raw2.Data {
107			c.flat[i][Permitted] = data.Permitted
108			c.flat[i][Inheritable] = data.Inheritable
109			if raw2.MagicEtc&vfsCapFlagsMask == vfsCapFlagsEffective {
110				c.flat[i][Effective] = data.Inheritable | data.Permitted
111			}
112		}
113	case vfsCapRevision3:
114		if err = binary.Read(b, binary.LittleEndian, &raw3); err != nil {
115			return nil, err
116		}
117		for i, data := range raw3.Data {
118			c.flat[i][Permitted] = data.Permitted
119			c.flat[i][Inheritable] = data.Inheritable
120			if raw3.MagicEtc&vfsCapFlagsMask == vfsCapFlagsEffective {
121				c.flat[i][Effective] = data.Inheritable | data.Permitted
122			}
123		}
124		c.nsRoot = int(raw3.RootID)
125	default:
126		return nil, ErrBadMagic
127	}
128	return c, nil
129}
130
131//go:uintptrescapes
132
133// GetFd returns the file capabilities of an open (*os.File).Fd().
134func GetFd(file *os.File) (*Set, error) {
135	var raw3 vfsCaps3
136	d := make([]byte, binary.Size(raw3))
137	sz, _, oErr := multisc.r6(syscall.SYS_FGETXATTR, uintptr(file.Fd()), uintptr(unsafe.Pointer(xattrNameCaps)), uintptr(unsafe.Pointer(&d[0])), uintptr(len(d)), 0, 0)
138	var err error
139	if oErr != 0 {
140		err = oErr
141	}
142	return digestFileCap(d, int(sz), err)
143}
144
145//go:uintptrescapes
146
147// GetFile returns the file capabilities of a named file.
148func GetFile(path string) (*Set, error) {
149	p, err := syscall.BytePtrFromString(path)
150	if err != nil {
151		return nil, err
152	}
153	var raw3 vfsCaps3
154	d := make([]byte, binary.Size(raw3))
155	sz, _, oErr := multisc.r6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(xattrNameCaps)), uintptr(unsafe.Pointer(&d[0])), uintptr(len(d)), 0, 0)
156	if oErr != 0 {
157		err = oErr
158	}
159	return digestFileCap(d, int(sz), err)
160}
161
162// GetNSOwner returns the namespace owner UID of the capability Set.
163func (c *Set) GetNSOwner() (int, error) {
164	if magic < kv3 {
165		return 0, ErrBadMagic
166	}
167	c.mu.RLock()
168	defer c.mu.RUnlock()
169	return c.nsRoot, nil
170}
171
172// SetNSOwner adds an explicit namespace owner UID to the capability
173// Set. This is only honored when generating file capabilities, and is
174// generally for use by a setup process when installing binaries that
175// use file capabilities to become capable inside a namespace to be
176// administered by that UID. If capability aware code within that
177// namespace writes file capabilities without explicitly setting such
178// a UID, the kernel will fix-up the capabilities to be specific to
179// that owner. In this way, the kernel prevents filesystem
180// capabilities from leaking out of that restricted namespace.
181func (c *Set) SetNSOwner(uid int) {
182	c.mu.Lock()
183	defer c.mu.Unlock()
184	c.nsRoot = uid
185}
186
187// packFileCap transforms a system capability into a VFS form. Because
188// of the way Linux stores capabilities in the file extended
189// attributes, the process is a little lossy with respect to effective
190// bits.
191func (c *Set) packFileCap() ([]byte, error) {
192	c.mu.RLock()
193	defer c.mu.RUnlock()
194
195	var magic uint32
196	switch words {
197	case 1:
198		if c.nsRoot != 0 {
199			return nil, ErrBadSet // nsRoot not supported for single DWORD caps.
200		}
201		magic = vfsCapRevision1
202	case 2:
203		if c.nsRoot == 0 {
204			magic = vfsCapRevision2
205			break
206		}
207		magic = vfsCapRevision3
208	}
209	if magic == 0 {
210		return nil, ErrBadSize
211	}
212	eff := uint32(0)
213	for _, f := range c.flat {
214		eff |= (f[Permitted] | f[Inheritable]) & f[Effective]
215	}
216	if eff != 0 {
217		magic |= vfsCapFlagsEffective
218	}
219	b := new(bytes.Buffer)
220	binary.Write(b, binary.LittleEndian, magic)
221	for _, f := range c.flat {
222		binary.Write(b, binary.LittleEndian, f[Permitted])
223		binary.Write(b, binary.LittleEndian, f[Inheritable])
224	}
225	if c.nsRoot != 0 {
226		binary.Write(b, binary.LittleEndian, c.nsRoot)
227	}
228	return b.Bytes(), nil
229}
230
231//go:uintptrescapes
232
233// SetFd attempts to set the file capabilities of an open
234// (*os.File).Fd(). This function can also be used to delete a file's
235// capabilities, by calling with c = nil.
236//
237// Note, Linux does not store the full Effective Flag in the metadata
238// for the file. Only a single Effective bit is stored in this
239// metadata. This single bit is non-zero if the Effective Flag has any
240// overlapping bits with the Permitted or Inheritable Flags of c. This
241// may appear suboptimal, but the reasoning behind it is sound.
242// Namely, the purpose of the Effective bit it to support capabability
243// unaware binaries that will only work if they magically launch with
244// the needed Values already raised (this bit is sometimes referred to
245// simply as the 'legacy' bit).
246//
247// Historical note: without *full* support for runtime capability
248// manipulation, as it is provided in this "../libcap/cap" package,
249// this was previously the only way for Go programs to make use of
250// file capabilities.
251//
252// The preferred way that a binary will actually manipulate its
253// file-acquired capabilities is to carefully and deliberately use
254// this package (or libcap, assisted by libpsx, for threaded C/C++
255// family code).
256func (c *Set) SetFd(file *os.File) error {
257	if c == nil {
258		if _, _, err := multisc.r6(syscall.SYS_FREMOVEXATTR, uintptr(file.Fd()), uintptr(unsafe.Pointer(xattrNameCaps)), 0, 0, 0, 0); err != 0 {
259			return err
260		}
261		return nil
262	}
263	c.mu.RLock()
264	defer c.mu.RUnlock()
265	d, err := c.packFileCap()
266	if err != nil {
267		return err
268	}
269	if _, _, err := multisc.r6(syscall.SYS_FSETXATTR, uintptr(file.Fd()), uintptr(unsafe.Pointer(xattrNameCaps)), uintptr(unsafe.Pointer(&d[0])), uintptr(len(d)), 0, 0); err != 0 {
270		return err
271	}
272	return nil
273}
274
275//go:uintptrescapes
276
277// SetFile attempts to set the file capabilities of the specified
278// filename. This function can also be used to delete a file's
279// capabilities, by calling with c = nil.
280//
281// Note, see the comment for SetFd() for some non-obvious behavior of
282// Linux for the Effective Flag on the modified file.
283func (c *Set) SetFile(path string) error {
284	fi, err := os.Stat(path)
285	if err != nil {
286		return err
287	}
288	mode := fi.Mode()
289	if mode&os.ModeType != 0 {
290		return ErrBadPath
291	}
292	if mode&os.FileMode(0111) == 0 {
293		return ErrBadPath
294	}
295	p, err := syscall.BytePtrFromString(path)
296	if err != nil {
297		return err
298	}
299	if c == nil {
300		if _, _, err := multisc.r6(syscall.SYS_REMOVEXATTR, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(xattrNameCaps)), 0, 0, 0, 0); err != 0 {
301			return err
302		}
303		return nil
304	}
305	c.mu.RLock()
306	defer c.mu.RUnlock()
307	d, err := c.packFileCap()
308	if err != nil {
309		return err
310	}
311	if _, _, err := multisc.r6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(xattrNameCaps)), uintptr(unsafe.Pointer(&d[0])), uintptr(len(d)), 0, 0); err != 0 {
312		return err
313	}
314	return nil
315}
316
317// ExtMagic is the 32-bit (little endian) magic for an external
318// capability set. It can be used to transmit capabilities in binary
319// format in a Linux portable way. The format is:
320// <ExtMagic><byte:length><length-bytes*3-of-cap-data>.
321const ExtMagic = uint32(0x5101c290)
322
323// Import imports a Set from a byte array where it has been stored in
324// a portable (lossless) way. That is values exported by
325// libcap.cap_copy_ext() and Export().
326func Import(d []byte) (*Set, error) {
327	b := bytes.NewBuffer(d)
328	var m uint32
329	if err := binary.Read(b, binary.LittleEndian, &m); err != nil {
330		return nil, ErrBadSize
331	} else if m != ExtMagic {
332		return nil, ErrBadMagic
333	}
334	var n byte
335	if err := binary.Read(b, binary.LittleEndian, &n); err != nil {
336		return nil, ErrBadSize
337	}
338	c := NewSet()
339	if int(n) > 4*words {
340		return nil, ErrBadSize
341	}
342	f := make([]byte, 3)
343	for i := 0; i < words; i++ {
344		for j := uint(0); n > 0 && j < 4; j++ {
345			n--
346			if x, err := b.Read(f); err != nil || x != 3 {
347				return nil, ErrBadSize
348			}
349			sh := 8 * j
350			c.flat[i][Effective] |= uint32(f[0]) << sh
351			c.flat[i][Permitted] |= uint32(f[1]) << sh
352			c.flat[i][Inheritable] |= uint32(f[2]) << sh
353		}
354	}
355	return c, nil
356}
357
358// MinExtFlagSize defaults to 8 in order to be equivalent to libcap
359// defaults. Setting it to zero can generate smaller external
360// representations. Such smaller representations can be imported by
361// libcap and the Go package just fine, we just default to the default
362// libcap representation for legacy reasons.
363var MinExtFlagSize = uint(8)
364
365// Export exports a Set into a lossless byte array format where it is
366// stored in a portable way. Note, any namespace owner in the Set
367// content is not exported by this function.
368//
369// Note, Export() generates exported byte streams that are importable
370// by libcap.cap_copy_int() as well as Import().
371func (c *Set) Export() ([]byte, error) {
372	if err := c.good(); err != nil {
373		return nil, err
374	}
375	if MinExtFlagSize > 255 {
376		return nil, ErrOutOfRange
377	}
378	b := new(bytes.Buffer)
379	binary.Write(b, binary.LittleEndian, ExtMagic)
380	c.mu.RLock()
381	defer c.mu.RUnlock()
382	var n = uint(0)
383	for i, f := range c.flat {
384		if nn := 4 * uint(i); nn+4 > n {
385			if u := f[Effective] | f[Permitted] | f[Inheritable]; u != 0 {
386				n = nn
387				for ; u != 0; u >>= 8 {
388					n++
389				}
390			}
391		}
392	}
393	if n < MinExtFlagSize {
394		n = MinExtFlagSize
395	}
396	b.Write([]byte{byte(n)})
397	for _, f := range c.flat {
398		if n == 0 {
399			break
400		}
401		eff, per, inh := f[Effective], f[Permitted], f[Inheritable]
402		for i := 0; n > 0 && i < 4; i++ {
403			n--
404			b.Write([]byte{
405				byte(eff & 0xff),
406				byte(per & 0xff),
407				byte(inh & 0xff),
408			})
409			eff >>= 8
410			per >>= 8
411			inh >>= 8
412		}
413	}
414	for n > 0 {
415		n--
416		b.Write([]byte{0, 0, 0})
417	}
418	return b.Bytes(), nil
419}
420