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