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