1package cap 2 3import ( 4 "fmt" 5 "io/ioutil" 6 "strconv" 7 "strings" 8 "sync" 9) 10 11// omask returns the offset and mask for a specific capability. 12func omask(c Value) (uint, uint32) { 13 u := uint(c) 14 return u >> 5, uint32(1) << (u & 31) 15} 16 17// IAB holds a summary of all of the inheritable capability vectors: 18// Inh, Amb and Bound. The Bound vector is the logical inverse (two's 19// complement) of the process' Bounding set. That is, raising a Value 20// in the Bound (think blocked) vector is equivalent to dropping that 21// Value from the process' Bounding set. This convention is used to 22// support the empty IAB as being mostly harmless. 23type IAB struct { 24 mu sync.RWMutex 25 a, i, nb []uint32 26} 27 28// Vector enumerates which of the inheritable IAB capability vectors 29// is being manipulated. 30type Vector uint 31 32// Inh, Amb, Bound enumerate the IAB vector components. (Vector) Inh 33// is equivalent to (Flag) Inheritable. They are named differently for 34// syntax/type checking reasons. 35const ( 36 Inh Vector = iota 37 Amb 38 Bound 39) 40 41// IABDiff holds the non-error result of an (*IAB).Cf() 42// function call. It can be interpreted with the function 43// (IABDiff).Has(). 44type IABDiff uint 45 46// iBits, iBits and bBits track the (semi-)independent parts of an 47// IABDiff. 48const ( 49 iBits IABDiff = 1 << Inh 50 aBits IABDiff = 1 << Amb 51 bBits IABDiff = 1 << Bound 52) 53 54// Has determines if an IAB comparison differs in a specific vector. 55func (d IABDiff) Has(v Vector) bool { 56 return d&(1<<v) != 0 57} 58 59// String identifies a Vector value by its conventional I A or B 60// string abbreviation. 61func (v Vector) String() string { 62 switch v { 63 case Inh: 64 return "I" 65 case Amb: 66 return "A" 67 case Bound: 68 return "B" 69 default: 70 return "<Error>" 71 } 72} 73 74// NewIAB returns an empty IAB. 75func NewIAB() *IAB { 76 startUp.Do(multisc.cInit) 77 return &IAB{ 78 i: make([]uint32, words), 79 a: make([]uint32, words), 80 nb: make([]uint32, words), 81 } 82} 83 84// good confirms the iab looks to be initialized. 85func (iab *IAB) good() error { 86 if iab == nil || len(iab.i) == 0 || len(iab.i) != words || len(iab.a) != words || len(iab.nb) != words { 87 return ErrBadValue 88 } 89 return nil 90} 91 92// Dup returns a duplicate copy of the IAB. 93func (iab *IAB) Dup() (*IAB, error) { 94 if err := iab.good(); err != nil { 95 return nil, err 96 } 97 v := NewIAB() 98 iab.mu.RLock() 99 defer iab.mu.RUnlock() 100 copy(v.i, iab.i) 101 copy(v.a, iab.a) 102 copy(v.nb, iab.nb) 103 return v, nil 104} 105 106// IABInit allocates a new IAB tuple. 107// 108// Deprecated: Replace with NewIAB. 109// 110// Example, replace this: 111// 112// iab := IABInit() 113// 114// with this: 115// 116// iab := NewIAB() 117func IABInit() *IAB { 118 return NewIAB() 119} 120 121// IABGetProc summarizes the Inh, Amb and Bound capability vectors of 122// the current process. 123func IABGetProc() *IAB { 124 iab := NewIAB() 125 current := GetProc() 126 iab.Fill(Inh, current, Inheritable) 127 for c := MaxBits(); c > 0; { 128 c-- 129 offset, mask := omask(c) 130 if a, _ := GetAmbient(c); a { 131 iab.a[offset] |= mask 132 } 133 if b, err := GetBound(c); err == nil && !b { 134 iab.nb[offset] |= mask 135 } 136 } 137 return iab 138} 139 140// IABFromText parses a string representing an IAB, as generated 141// by IAB.String(), to generate an IAB. 142func IABFromText(text string) (*IAB, error) { 143 iab := NewIAB() 144 if len(text) == 0 { 145 return iab, nil 146 } 147 for _, f := range strings.Split(text, ",") { 148 var i, a, nb bool 149 var j int 150 for j = 0; j < len(f); j++ { 151 switch f[j : j+1] { 152 case "!": 153 nb = true 154 case "^": 155 i = true 156 a = true 157 case "%": 158 i = true 159 default: 160 goto done 161 } 162 } 163 done: 164 c, err := FromName(f[j:]) 165 if err != nil { 166 return nil, err 167 } 168 offset, mask := omask(c) 169 if i || !nb { 170 iab.i[offset] |= mask 171 } 172 if a { 173 iab.a[offset] |= mask 174 } 175 if nb { 176 iab.nb[offset] |= mask 177 } 178 } 179 return iab, nil 180} 181 182// String serializes an IAB to a string format. 183func (iab *IAB) String() string { 184 if err := iab.good(); err != nil { 185 return "<invalid>" 186 } 187 var vs []string 188 iab.mu.RLock() 189 defer iab.mu.RUnlock() 190 for c := Value(0); c < Value(maxValues); c++ { 191 offset, mask := omask(c) 192 i := (iab.i[offset] & mask) != 0 193 a := (iab.a[offset] & mask) != 0 194 nb := (iab.nb[offset] & mask) != 0 195 var cs []string 196 if nb { 197 cs = append(cs, "!") 198 } 199 if a { 200 cs = append(cs, "^") 201 } else if nb && i { 202 cs = append(cs, "%") 203 } 204 if nb || a || i { 205 vs = append(vs, strings.Join(cs, "")+c.String()) 206 } 207 } 208 return strings.Join(vs, ",") 209} 210 211// iabSetProc uses a syscaller to apply an IAB tuple to the process. 212// The iab is known to be locked by the caller. 213func (sc *syscaller) iabSetProc(iab *IAB) (err error) { 214 temp := GetProc() 215 var raising uint32 216 for i := 0; i < words; i++ { 217 newI := iab.i[i] 218 oldIP := temp.flat[i][Inheritable] | temp.flat[i][Permitted] 219 raising |= (newI & ^oldIP) | iab.a[i] | iab.nb[i] 220 temp.flat[i][Inheritable] = newI 221 } 222 working, err2 := temp.Dup() 223 if err2 != nil { 224 err = err2 225 return 226 } 227 if raising != 0 { 228 if err = working.SetFlag(Effective, true, SETPCAP); err != nil { 229 return 230 } 231 if err = sc.setProc(working); err != nil { 232 return 233 } 234 } 235 defer func() { 236 if err2 := sc.setProc(temp); err == nil { 237 err = err2 238 } 239 }() 240 if err = sc.resetAmbient(); err != nil { 241 return 242 } 243 for c := Value(maxValues); c > 0; { 244 c-- 245 offset, mask := omask(c) 246 if iab.a[offset]&mask != 0 { 247 err = sc.setAmbient(true, c) 248 } 249 if err == nil && iab.nb[offset]&mask != 0 { 250 err = sc.dropBound(c) 251 } 252 if err != nil { 253 return 254 } 255 } 256 return 257} 258 259// SetProc attempts to change the Inheritable, Ambient and Bounding 260// capability vectors of the current process using the content, 261// iab. The Bounding vector strongly affects the potential for setting 262// other bits, so this function carefully performs the combined 263// operation in the most flexible manner. 264func (iab *IAB) SetProc() error { 265 if err := iab.good(); err != nil { 266 return err 267 } 268 state, sc := scwStateSC() 269 defer scwSetState(launchBlocked, state, -1) 270 iab.mu.RLock() 271 defer iab.mu.RUnlock() 272 return sc.iabSetProc(iab) 273} 274 275// GetVector returns the raised state of the specific capability bit 276// of the indicated vector. 277func (iab *IAB) GetVector(vec Vector, val Value) (bool, error) { 278 if err := iab.good(); err != nil { 279 return false, err 280 } 281 if val >= MaxBits() { 282 return false, ErrBadValue 283 } 284 iab.mu.RLock() 285 defer iab.mu.RUnlock() 286 offset, mask := omask(val) 287 switch vec { 288 case Inh: 289 return (iab.i[offset] & mask) != 0, nil 290 case Amb: 291 return (iab.a[offset] & mask) != 0, nil 292 case Bound: 293 return (iab.nb[offset] & mask) != 0, nil 294 default: 295 return false, ErrBadValue 296 } 297} 298 299// SetVector sets all of the vals in the specified vector to the 300// raised value. Note, the Ambient vector cannot contain values not raised 301// in the Inh vector, so setting values directly in one vector may have 302// the side effect of mirroring the value in the other vector to 303// maintain this constraint. Note, raising a Bound vector bit is 304// equivalent to lowering the Bounding vector of the process (when 305// successfully applied with (*IAB).SetProc()). 306func (iab *IAB) SetVector(vec Vector, raised bool, vals ...Value) error { 307 if err := iab.good(); err != nil { 308 return err 309 } 310 iab.mu.Lock() 311 defer iab.mu.Unlock() 312 for _, val := range vals { 313 if val >= Value(maxValues) { 314 return ErrBadValue 315 } 316 offset, mask := omask(val) 317 switch vec { 318 case Inh: 319 if raised { 320 iab.i[offset] |= mask 321 } else { 322 iab.i[offset] &= ^mask 323 iab.a[offset] &= ^mask 324 } 325 case Amb: 326 if raised { 327 iab.a[offset] |= mask 328 iab.i[offset] |= mask 329 } else { 330 iab.a[offset] &= ^mask 331 } 332 case Bound: 333 if raised { 334 iab.nb[offset] |= mask 335 } else { 336 iab.nb[offset] &= ^mask 337 } 338 default: 339 return ErrBadValue 340 } 341 } 342 return nil 343} 344 345// Fill fills one of the Inh, Amb and Bound capability vectors from 346// one of the flag vectors of a Set. Note, filling the Inh vector 347// will mask the Amb vector, and filling the Amb vector may raise 348// entries in the Inh vector. Further, when filling the Bound vector, 349// the bits are inverted from what you might expect - that is lowered 350// bits from the Set will be raised in the Bound vector. 351func (iab *IAB) Fill(vec Vector, c *Set, flag Flag) error { 352 if err := iab.good(); err != nil { 353 return err 354 } 355 // work with a copy to avoid potential deadlock. 356 s, err := c.Dup() 357 if err != nil { 358 return err 359 } 360 iab.mu.Lock() 361 defer iab.mu.Unlock() 362 for i := 0; i < words; i++ { 363 flat := s.flat[i][flag] 364 switch vec { 365 case Inh: 366 iab.i[i] = flat 367 iab.a[i] &= flat 368 case Amb: 369 iab.a[i] = flat 370 iab.i[i] |= flat 371 case Bound: 372 iab.nb[i] = ^flat 373 default: 374 return ErrBadSet 375 } 376 } 377 return nil 378} 379 380// Cf compares two IAB values. Its return value is 0 if the compared 381// tuples are considered identical. The macroscopic differences can be 382// investigated with (IABDiff).Has(). 383func (iab *IAB) Cf(alt *IAB) (IABDiff, error) { 384 if err := iab.good(); err != nil { 385 return 0, err 386 } 387 if iab == alt { 388 return 0, nil 389 } 390 // Avoid holding two locks at once. 391 ref, err := alt.Dup() 392 if err != nil { 393 return 0, err 394 } 395 iab.mu.RLock() 396 defer iab.mu.RUnlock() 397 398 var cf IABDiff 399 for i := 0; i < words; i++ { 400 if iab.i[i] != ref.i[i] { 401 cf |= iBits 402 } 403 if iab.a[i] != ref.a[i] { 404 cf |= aBits 405 } 406 if iab.nb[i] != ref.nb[i] { 407 cf |= bBits 408 } 409 } 410 return cf, nil 411} 412 413// parseHex converts the /proc/*/status string into an array of 414// uint32s suitable for storage in an IAB structure. 415func parseHex(hex string, invert bool) []uint32 { 416 if len(hex) != 8*words { 417 // Invalid string 418 return nil 419 } 420 var result []uint32 421 for i := 0; i < words; i++ { 422 upper := 8 * (words - i) 423 raw, err := strconv.ParseUint(hex[upper-8:upper], 16, 32) 424 if err != nil { 425 return nil 426 } 427 if invert { 428 raw = ^raw 429 } 430 bits := allMask(uint(i)) & uint32(raw) 431 result = append(result, bits) 432 } 433 return result 434} 435 436var procRoot = "/proc" 437 438// ProcRoot sets the local mount point for the Linux /proc filesystem. 439// It defaults to "/proc", but might be mounted elsewhere on any given 440// system. The function returns the previous value of the local mount 441// point. If the user attempts to set it to "", the value is left 442// unchanged. 443func ProcRoot(path string) string { 444 was := procRoot 445 if path != "" { 446 procRoot = path 447 } 448 return was 449} 450 451// IABGetPID returns the IAB tuple of a specified process. The kernel 452// ABI does not support this query via system calls, so the function 453// works by parsing the /proc/<pid>/status file content. 454func IABGetPID(pid int) (*IAB, error) { 455 tf := fmt.Sprintf("%s/%d/status", procRoot, pid) 456 d, err := ioutil.ReadFile(tf) 457 if err != nil { 458 return nil, err 459 } 460 iab := &IAB{} 461 for _, line := range strings.Split(string(d), "\n") { 462 if !strings.HasPrefix(line, "Cap") { 463 continue 464 } 465 flavor := line[3:] 466 if strings.HasPrefix(flavor, "Inh:\t") { 467 iab.i = parseHex(line[8:], false) 468 continue 469 } 470 if strings.HasPrefix(flavor, "Bnd:\t") { 471 iab.nb = parseHex(line[8:], true) 472 continue 473 } 474 if strings.HasPrefix(flavor, "Amb:\t") { 475 iab.a = parseHex(line[8:], false) 476 continue 477 } 478 } 479 if len(iab.i) != words || len(iab.a) != words || len(iab.nb) != words { 480 return nil, ErrBadValue 481 } 482 return iab, nil 483} 484