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 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// digestFileCap unpacks a file capability and returns it in a *Set
69// form.
70func digestFileCap(d []byte, sz int, err error) (*Set, error) {
71	if err != nil {
72		return nil, err
73	}
74	var raw1 vfsCaps1
75	var raw2 vfsCaps2
76	var raw3 vfsCaps3
77	if sz < binary.Size(raw1) || sz > binary.Size(raw3) {
78		return nil, ErrBadSize
79	}
80	b := bytes.NewReader(d[:sz])
81	var magicEtc uint32
82	if err = binary.Read(b, binary.LittleEndian, &magicEtc); err != nil {
83		return nil, err
84	}
85
86	c := NewSet()
87	b.Seek(0, io.SeekStart)
88	switch magicEtc & vfsCapRevisionMask {
89	case vfsCapRevision1:
90		if err = binary.Read(b, binary.LittleEndian, &raw1); err != nil {
91			return nil, err
92		}
93		data := raw1.Data[0]
94		c.flat[0][Permitted] = data.Permitted
95		c.flat[0][Inheritable] = data.Inheritable
96		if raw1.MagicEtc&vfsCapFlagsMask == vfsCapFlagsEffective {
97			c.flat[0][Effective] = data.Inheritable | data.Permitted
98		}
99	case vfsCapRevision2:
100		if err = binary.Read(b, binary.LittleEndian, &raw2); err != nil {
101			return nil, err
102		}
103		for i, data := range raw2.Data {
104			c.flat[i][Permitted] = data.Permitted
105			c.flat[i][Inheritable] = data.Inheritable
106			if raw2.MagicEtc&vfsCapFlagsMask == vfsCapFlagsEffective {
107				c.flat[i][Effective] = data.Inheritable | data.Permitted
108			}
109		}
110	case vfsCapRevision3:
111		if err = binary.Read(b, binary.LittleEndian, &raw3); err != nil {
112			return nil, err
113		}
114		for i, data := range raw3.Data {
115			c.flat[i][Permitted] = data.Permitted
116			c.flat[i][Inheritable] = data.Inheritable
117			if raw3.MagicEtc&vfsCapFlagsMask == vfsCapFlagsEffective {
118				c.flat[i][Effective] = data.Inheritable | data.Permitted
119			}
120		}
121		c.nsRoot = int(raw3.RootID)
122	default:
123		return nil, ErrBadMagic
124	}
125	return c, nil
126}
127
128//go:uintptrescapes
129
130// GetFd returns the file capabilities of an open (*os.File).Fd().
131func GetFd(file *os.File) (*Set, error) {
132	var raw3 vfsCaps3
133	d := make([]byte, binary.Size(raw3))
134	sz, _, oErr := multisc.r6(syscall.SYS_FGETXATTR, uintptr(file.Fd()), uintptr(unsafe.Pointer(xattrNameCaps)), uintptr(unsafe.Pointer(&d[0])), uintptr(len(d)), 0, 0)
135	var err error
136	if oErr != 0 {
137		err = oErr
138	}
139	return digestFileCap(d, int(sz), err)
140}
141
142//go:uintptrescapes
143
144// GetFile returns the file capabilities of a named file.
145func GetFile(path string) (*Set, error) {
146	p, err := syscall.BytePtrFromString(path)
147	if err != nil {
148		return nil, err
149	}
150	var raw3 vfsCaps3
151	d := make([]byte, binary.Size(raw3))
152	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)
153	if oErr != 0 {
154		err = oErr
155	}
156	return digestFileCap(d, int(sz), err)
157}
158
159// GetNSOwner returns the namespace owner UID of the capability Set.
160func (c *Set) GetNSOwner() (int, error) {
161	if magic < kv3 {
162		return 0, ErrBadMagic
163	}
164	return c.nsRoot, nil
165}
166
167// SetNSOwner adds an explicit namespace owner UID to the capability
168// Set. This is only honored when generating file capabilities, and is
169// generally for use by a setup process when installing binaries that
170// use file capabilities to become capable inside a namespace to be
171// administered by that UID. If capability aware code within that
172// namespace writes file capabilities without explicitly setting such
173// a UID, the kernel will fix-up the capabilities to be specific to
174// that owner. In this way, the kernel prevents filesystem
175// capabilities from leaking out of that restricted namespace.
176func (c *Set) SetNSOwner(uid int) {
177	c.mu.Lock()
178	defer c.mu.Unlock()
179	c.nsRoot = uid
180}
181
182// packFileCap transforms a system capability into a VFS form. Because
183// of the way Linux stores capabilities in the file extended
184// attributes, the process is a little lossy with respect to effective
185// bits.
186func (c *Set) packFileCap() ([]byte, error) {
187	var magic uint32
188	switch words {
189	case 1:
190		if c.nsRoot != 0 {
191			return nil, ErrBadSet // nsRoot not supported for single DWORD caps.
192		}
193		magic = vfsCapRevision1
194	case 2:
195		if c.nsRoot == 0 {
196			magic = vfsCapRevision2
197			break
198		}
199		magic = vfsCapRevision3
200	}
201	if magic == 0 {
202		return nil, ErrBadSize
203	}
204	eff := uint32(0)
205	for _, f := range c.flat {
206		eff |= (f[Permitted] | f[Inheritable]) & f[Effective]
207	}
208	if eff != 0 {
209		magic |= vfsCapFlagsEffective
210	}
211	b := new(bytes.Buffer)
212	binary.Write(b, binary.LittleEndian, magic)
213	for _, f := range c.flat {
214		binary.Write(b, binary.LittleEndian, f[Permitted])
215		binary.Write(b, binary.LittleEndian, f[Inheritable])
216	}
217	if c.nsRoot != 0 {
218		binary.Write(b, binary.LittleEndian, c.nsRoot)
219	}
220	return b.Bytes(), nil
221}
222
223//go:uintptrescapes
224
225// SetFd attempts to set the file capabilities of an open
226// (*os.File).Fd(). This function can also be used to delete a file's
227// capabilities, by calling with c = nil.
228//
229// Note, Linux does not store the full Effective Value Flag in the
230// metadata for the file. Only a single Effective bit is stored in
231// this metadata. This single bit is non-zero if the Effective vector
232// has any overlapping bits with the Permitted or Inheritable vector
233// of c. This may appear suboptimal, but the reasoning behind it is
234// sound. Namely, the purpose of the Effective bit it to support
235// capabability unaware binaries that will only work if they magically
236// launch with the needed bits already raised (this bit is sometimes
237// referred to simply as the 'legacy' bit). Without *full* support for
238// capability manipulation, as it is provided in this "../libcap/cap"
239// package, this was the only way for Go programs to make use of
240// file capabilities.
241//
242// The preferred way a binary will actually manipulate its
243// file-acquired capabilities is to carefully and deliberately use
244// this package (or libcap, assisted by libpsx, for threaded C/C++
245// family code).
246func (c *Set) SetFd(file *os.File) error {
247	if c == nil {
248		if _, _, err := multisc.r6(syscall.SYS_FREMOVEXATTR, uintptr(file.Fd()), uintptr(unsafe.Pointer(xattrNameCaps)), 0, 0, 0, 0); err != 0 {
249			return err
250		}
251		return nil
252	}
253	c.mu.Lock()
254	defer c.mu.Unlock()
255	d, err := c.packFileCap()
256	if err != nil {
257		return err
258	}
259	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 {
260		return err
261	}
262	return nil
263}
264
265//go:uintptrescapes
266
267// SetFile attempts to set the file capabilities of the specfied
268// filename. This function can also be used to delete a file's
269// capabilities, by calling with c = nil.
270//
271// Note, see the comment for SetFd() for some non-obvious behavior of
272// Linux for the Effective Value vector on the modified file.
273func (c *Set) SetFile(path string) error {
274	fi, err := os.Stat(path)
275	if err != nil {
276		return err
277	}
278	mode := fi.Mode()
279	if mode&os.ModeType != 0 {
280		return ErrBadPath
281	}
282	if mode&os.FileMode(0111) == 0 {
283		return ErrBadPath
284	}
285	p, err := syscall.BytePtrFromString(path)
286	if err != nil {
287		return err
288	}
289	if c == nil {
290		if _, _, err := multisc.r6(syscall.SYS_REMOVEXATTR, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(xattrNameCaps)), 0, 0, 0, 0); err != 0 {
291			return err
292		}
293		return nil
294	}
295	c.mu.Lock()
296	defer c.mu.Unlock()
297	d, err := c.packFileCap()
298	if err != nil {
299		return err
300	}
301	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 {
302		return err
303	}
304	return nil
305}
306
307// ExtMagic is the 32-bit (little endian) magic for an external
308// capability set. It can be used to transmit capabilities in binary
309// format in a Linux portable way. The format is:
310// <ExtMagic><byte:length><length-bytes*3-of-cap-data>.
311const ExtMagic = uint32(0x5101c290)
312
313// Import imports a Set from a byte array where it has been stored in
314// a portable (lossless) way.
315func Import(d []byte) (*Set, error) {
316	b := bytes.NewBuffer(d)
317	var m uint32
318	if err := binary.Read(b, binary.LittleEndian, &m); err != nil {
319		return nil, ErrBadSize
320	} else if m != ExtMagic {
321		return nil, ErrBadMagic
322	}
323	var n byte
324	if err := binary.Read(b, binary.LittleEndian, &n); err != nil {
325		return nil, ErrBadSize
326	}
327	c := NewSet()
328	if int(n) > 4*words {
329		return nil, ErrBadSize
330	}
331	f := make([]byte, 3)
332	for i := 0; i < words; i++ {
333		for j := uint(0); n > 0 && j < 4; j++ {
334			n--
335			if x, err := b.Read(f); err != nil || x != 3 {
336				return nil, ErrBadSize
337			}
338			sh := 8 * j
339			c.flat[i][Effective] |= uint32(f[0]) << sh
340			c.flat[i][Permitted] |= uint32(f[1]) << sh
341			c.flat[i][Inheritable] |= uint32(f[2]) << sh
342		}
343	}
344	return c, nil
345}
346
347// Export exports a Set into a lossless byte array format where it is
348// stored in a portable way. Note, any namespace owner in the Set
349// content is not exported by this function.
350func (c *Set) Export() ([]byte, error) {
351	if c == nil {
352		return nil, ErrBadSet
353	}
354	b := new(bytes.Buffer)
355	binary.Write(b, binary.LittleEndian, ExtMagic)
356	c.mu.Lock()
357	defer c.mu.Unlock()
358	var n = byte(0)
359	for i, f := range c.flat {
360		if u := f[Effective] | f[Permitted] | f[Inheritable]; u != 0 {
361			n = 4 * byte(i)
362			for ; u != 0; u >>= 8 {
363				n++
364			}
365		}
366	}
367	b.Write([]byte{n})
368	for _, f := range c.flat {
369		if n == 0 {
370			break
371		}
372		eff, per, inh := f[Effective], f[Permitted], f[Inheritable]
373		for i := 0; n > 0 && i < 4; i++ {
374			n--
375			b.Write([]byte{
376				byte(eff & 0xff),
377				byte(per & 0xff),
378				byte(inh & 0xff),
379			})
380			eff >>= 8
381			per >>= 8
382			inh >>= 8
383		}
384	}
385	return b.Bytes(), nil
386}
387