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