• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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// 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	return c.nsRoot, nil
168}
169
170// SetNSOwner adds an explicit namespace owner UID to the capability
171// Set. This is only honored when generating file capabilities, and is
172// generally for use by a setup process when installing binaries that
173// use file capabilities to become capable inside a namespace to be
174// administered by that UID. If capability aware code within that
175// namespace writes file capabilities without explicitly setting such
176// a UID, the kernel will fix-up the capabilities to be specific to
177// that owner. In this way, the kernel prevents filesystem
178// capabilities from leaking out of that restricted namespace.
179func (c *Set) SetNSOwner(uid int) {
180	c.mu.Lock()
181	defer c.mu.Unlock()
182	c.nsRoot = uid
183}
184
185// packFileCap transforms a system capability into a VFS form. Because
186// of the way Linux stores capabilities in the file extended
187// attributes, the process is a little lossy with respect to effective
188// bits.
189func (c *Set) packFileCap() ([]byte, error) {
190	var magic uint32
191	switch words {
192	case 1:
193		if c.nsRoot != 0 {
194			return nil, ErrBadSet // nsRoot not supported for single DWORD caps.
195		}
196		magic = vfsCapRevision1
197	case 2:
198		if c.nsRoot == 0 {
199			magic = vfsCapRevision2
200			break
201		}
202		magic = vfsCapRevision3
203	}
204	if magic == 0 {
205		return nil, ErrBadSize
206	}
207	eff := uint32(0)
208	for _, f := range c.flat {
209		eff |= (f[Permitted] | f[Inheritable]) & f[Effective]
210	}
211	if eff != 0 {
212		magic |= vfsCapFlagsEffective
213	}
214	b := new(bytes.Buffer)
215	binary.Write(b, binary.LittleEndian, magic)
216	for _, f := range c.flat {
217		binary.Write(b, binary.LittleEndian, f[Permitted])
218		binary.Write(b, binary.LittleEndian, f[Inheritable])
219	}
220	if c.nsRoot != 0 {
221		binary.Write(b, binary.LittleEndian, c.nsRoot)
222	}
223	return b.Bytes(), nil
224}
225
226//go:uintptrescapes
227
228// SetFd attempts to set the file capabilities of an open
229// (*os.File).Fd(). This function can also be used to delete a file's
230// capabilities, by calling with c = nil.
231//
232// Note, Linux does not store the full Effective Value Flag in the
233// metadata for the file. Only a single Effective bit is stored in
234// this metadata. This single bit is non-zero if the Effective vector
235// has any overlapping bits with the Permitted or Inheritable vector
236// of c. This may appear suboptimal, but the reasoning behind it is
237// sound. Namely, the purpose of the Effective bit it to support
238// capabability unaware binaries that will only work if they magically
239// launch with the needed bits already raised (this bit is sometimes
240// referred to simply as the 'legacy' bit). Without *full* support for
241// capability manipulation, as it is provided in this "../libcap/cap"
242// package, this was the only way for Go programs to make use of
243// file capabilities.
244//
245// The preferred way a binary will actually manipulate its
246// file-acquired capabilities is to carefully and deliberately use
247// this package (or libcap, assisted by libpsx, for threaded C/C++
248// family code).
249func (c *Set) SetFd(file *os.File) error {
250	if c == nil {
251		if _, _, err := multisc.r6(syscall.SYS_FREMOVEXATTR, uintptr(file.Fd()), uintptr(unsafe.Pointer(xattrNameCaps)), 0, 0, 0, 0); err != 0 {
252			return err
253		}
254		return nil
255	}
256	c.mu.Lock()
257	defer c.mu.Unlock()
258	d, err := c.packFileCap()
259	if err != nil {
260		return err
261	}
262	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 {
263		return err
264	}
265	return nil
266}
267
268//go:uintptrescapes
269
270// SetFile attempts to set the file capabilities of the specified
271// filename. This function can also be used to delete a file's
272// capabilities, by calling with c = nil.
273//
274// Note, see the comment for SetFd() for some non-obvious behavior of
275// Linux for the Effective Value vector on the modified file.
276func (c *Set) SetFile(path string) error {
277	fi, err := os.Stat(path)
278	if err != nil {
279		return err
280	}
281	mode := fi.Mode()
282	if mode&os.ModeType != 0 {
283		return ErrBadPath
284	}
285	if mode&os.FileMode(0111) == 0 {
286		return ErrBadPath
287	}
288	p, err := syscall.BytePtrFromString(path)
289	if err != nil {
290		return err
291	}
292	if c == nil {
293		if _, _, err := multisc.r6(syscall.SYS_REMOVEXATTR, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(xattrNameCaps)), 0, 0, 0, 0); err != 0 {
294			return err
295		}
296		return nil
297	}
298	c.mu.Lock()
299	defer c.mu.Unlock()
300	d, err := c.packFileCap()
301	if err != nil {
302		return err
303	}
304	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 {
305		return err
306	}
307	return nil
308}
309
310// ExtMagic is the 32-bit (little endian) magic for an external
311// capability set. It can be used to transmit capabilities in binary
312// format in a Linux portable way. The format is:
313// <ExtMagic><byte:length><length-bytes*3-of-cap-data>.
314const ExtMagic = uint32(0x5101c290)
315
316// Import imports a Set from a byte array where it has been stored in
317// a portable (lossless) way. That is values exported by
318// libcap.cap_copy_ext() and Export().
319func Import(d []byte) (*Set, error) {
320	b := bytes.NewBuffer(d)
321	var m uint32
322	if err := binary.Read(b, binary.LittleEndian, &m); err != nil {
323		return nil, ErrBadSize
324	} else if m != ExtMagic {
325		return nil, ErrBadMagic
326	}
327	var n byte
328	if err := binary.Read(b, binary.LittleEndian, &n); err != nil {
329		return nil, ErrBadSize
330	}
331	c := NewSet()
332	if int(n) > 4*words {
333		return nil, ErrBadSize
334	}
335	f := make([]byte, 3)
336	for i := 0; i < words; i++ {
337		for j := uint(0); n > 0 && j < 4; j++ {
338			n--
339			if x, err := b.Read(f); err != nil || x != 3 {
340				return nil, ErrBadSize
341			}
342			sh := 8 * j
343			c.flat[i][Effective] |= uint32(f[0]) << sh
344			c.flat[i][Permitted] |= uint32(f[1]) << sh
345			c.flat[i][Inheritable] |= uint32(f[2]) << sh
346		}
347	}
348	return c, nil
349}
350
351// To strictly match libcap, this value defaults to 8. Setting it to
352// zero can generate smaller external representations. Such smaller
353// representations can be imported by libcap and the Go package just
354// fine, we just default to the default libcap representation for
355// legacy reasons.
356var MinExtFlagSize = uint(8)
357
358// Export exports a Set into a lossless byte array format where it is
359// stored in a portable way. Note, any namespace owner in the Set
360// content is not exported by this function.
361//
362// Note, Export() generates exported byte streams that are importable
363// by libcap.cap_copy_int() as well as Import().
364func (c *Set) Export() ([]byte, error) {
365	if c == nil {
366		return nil, ErrBadSet
367	}
368	if MinExtFlagSize > 255 {
369		return nil, ErrOutOfRange
370	}
371	b := new(bytes.Buffer)
372	binary.Write(b, binary.LittleEndian, ExtMagic)
373	c.mu.Lock()
374	defer c.mu.Unlock()
375	var n = uint(0)
376	for i, f := range c.flat {
377		if nn := 4 * uint(i); nn+4 > n {
378			if u := f[Effective] | f[Permitted] | f[Inheritable]; u != 0 {
379				n = nn
380				for ; u != 0; u >>= 8 {
381					n++
382				}
383			}
384		}
385	}
386	if n < MinExtFlagSize {
387		n = MinExtFlagSize
388	}
389	b.Write([]byte{byte(n)})
390	for _, f := range c.flat {
391		if n == 0 {
392			break
393		}
394		eff, per, inh := f[Effective], f[Permitted], f[Inheritable]
395		for i := 0; n > 0 && i < 4; i++ {
396			n--
397			b.Write([]byte{
398				byte(eff & 0xff),
399				byte(per & 0xff),
400				byte(inh & 0xff),
401			})
402			eff >>= 8
403			per >>= 8
404			inh >>= 8
405		}
406	}
407	for n > 0 {
408		n--
409		b.Write([]byte{0, 0, 0})
410	}
411	return b.Bytes(), nil
412}
413