github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/runc/libcontainer/selinux/selinux.go (about) 1 // +build linux 2 3 package selinux 4 5 import ( 6 "bufio" 7 "crypto/rand" 8 "encoding/binary" 9 "fmt" 10 "io" 11 "os" 12 "path/filepath" 13 "regexp" 14 "strconv" 15 "strings" 16 "sync" 17 "syscall" 18 19 "github.com/opencontainers/runc/libcontainer/system" 20 ) 21 22 const ( 23 Enforcing = 1 24 Permissive = 0 25 Disabled = -1 26 selinuxDir = "/etc/selinux/" 27 selinuxConfig = selinuxDir + "config" 28 selinuxTypeTag = "SELINUXTYPE" 29 selinuxTag = "SELINUX" 30 selinuxPath = "/sys/fs/selinux" 31 xattrNameSelinux = "security.selinux" 32 stRdOnly = 0x01 33 ) 34 35 var ( 36 assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`) 37 mcsList = make(map[string]bool) 38 mcsLock sync.Mutex 39 selinuxfs = "unknown" 40 selinuxEnabled = false // Stores whether selinux is currently enabled 41 selinuxEnabledChecked = false // Stores whether selinux enablement has been checked or established yet 42 ) 43 44 type SELinuxContext map[string]string 45 46 // SetDisabled disables selinux support for the package 47 func SetDisabled() { 48 selinuxEnabled, selinuxEnabledChecked = false, true 49 } 50 51 // getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs 52 // filesystem or an empty string if no mountpoint is found. Selinuxfs is 53 // a proc-like pseudo-filesystem that exposes the selinux policy API to 54 // processes. The existence of an selinuxfs mount is used to determine 55 // whether selinux is currently enabled or not. 56 func getSelinuxMountPoint() string { 57 if selinuxfs != "unknown" { 58 return selinuxfs 59 } 60 selinuxfs = "" 61 62 f, err := os.Open("/proc/self/mountinfo") 63 if err != nil { 64 return selinuxfs 65 } 66 defer f.Close() 67 68 scanner := bufio.NewScanner(f) 69 for scanner.Scan() { 70 txt := scanner.Text() 71 // Safe as mountinfo encodes mountpoints with spaces as \040. 72 sepIdx := strings.Index(txt, " - ") 73 if sepIdx == -1 { 74 continue 75 } 76 if !strings.Contains(txt[sepIdx:], "selinuxfs") { 77 continue 78 } 79 fields := strings.Split(txt, " ") 80 if len(fields) < 5 { 81 continue 82 } 83 selinuxfs = fields[4] 84 break 85 } 86 87 if selinuxfs != "" { 88 var buf syscall.Statfs_t 89 syscall.Statfs(selinuxfs, &buf) 90 if (buf.Flags & stRdOnly) == 1 { 91 selinuxfs = "" 92 } 93 } 94 return selinuxfs 95 } 96 97 // SelinuxEnabled returns whether selinux is currently enabled. 98 func SelinuxEnabled() bool { 99 if selinuxEnabledChecked { 100 return selinuxEnabled 101 } 102 selinuxEnabledChecked = true 103 if fs := getSelinuxMountPoint(); fs != "" { 104 if con, _ := Getcon(); con != "kernel" { 105 selinuxEnabled = true 106 } 107 } 108 return selinuxEnabled 109 } 110 111 func readConfig(target string) (value string) { 112 var ( 113 val, key string 114 bufin *bufio.Reader 115 ) 116 117 in, err := os.Open(selinuxConfig) 118 if err != nil { 119 return "" 120 } 121 defer in.Close() 122 123 bufin = bufio.NewReader(in) 124 125 for done := false; !done; { 126 var line string 127 if line, err = bufin.ReadString('\n'); err != nil { 128 if err != io.EOF { 129 return "" 130 } 131 done = true 132 } 133 line = strings.TrimSpace(line) 134 if len(line) == 0 { 135 // Skip blank lines 136 continue 137 } 138 if line[0] == ';' || line[0] == '#' { 139 // Skip comments 140 continue 141 } 142 if groups := assignRegex.FindStringSubmatch(line); groups != nil { 143 key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) 144 if key == target { 145 return strings.Trim(val, "\"") 146 } 147 } 148 } 149 return "" 150 } 151 152 func getSELinuxPolicyRoot() string { 153 return selinuxDir + readConfig(selinuxTypeTag) 154 } 155 156 func readCon(name string) (string, error) { 157 var val string 158 159 in, err := os.Open(name) 160 if err != nil { 161 return "", err 162 } 163 defer in.Close() 164 165 _, err = fmt.Fscanf(in, "%s", &val) 166 return val, err 167 } 168 169 // Setfilecon sets the SELinux label for this path or returns an error. 170 func Setfilecon(path string, scon string) error { 171 return system.Lsetxattr(path, xattrNameSelinux, []byte(scon), 0) 172 } 173 174 // Getfilecon returns the SELinux label for this path or returns an error. 175 func Getfilecon(path string) (string, error) { 176 con, err := system.Lgetxattr(path, xattrNameSelinux) 177 if err != nil { 178 return "", err 179 } 180 // Trim the NUL byte at the end of the byte buffer, if present. 181 if len(con) > 0 && con[len(con)-1] == '\x00' { 182 con = con[:len(con)-1] 183 } 184 return string(con), nil 185 } 186 187 func Setfscreatecon(scon string) error { 188 return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()), scon) 189 } 190 191 func Getfscreatecon() (string, error) { 192 return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid())) 193 } 194 195 // Getcon returns the SELinux label of the current process thread, or an error. 196 func Getcon() (string, error) { 197 return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid())) 198 } 199 200 // Getpidcon returns the SELinux label of the given pid, or an error. 201 func Getpidcon(pid int) (string, error) { 202 return readCon(fmt.Sprintf("/proc/%d/attr/current", pid)) 203 } 204 205 func Getexeccon() (string, error) { 206 return readCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid())) 207 } 208 209 func writeCon(name string, val string) error { 210 out, err := os.OpenFile(name, os.O_WRONLY, 0) 211 if err != nil { 212 return err 213 } 214 defer out.Close() 215 216 if val != "" { 217 _, err = out.Write([]byte(val)) 218 } else { 219 _, err = out.Write(nil) 220 } 221 return err 222 } 223 224 func Setexeccon(scon string) error { 225 return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), scon) 226 } 227 228 func (c SELinuxContext) Get() string { 229 return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"]) 230 } 231 232 func NewContext(scon string) SELinuxContext { 233 c := make(SELinuxContext) 234 235 if len(scon) != 0 { 236 con := strings.SplitN(scon, ":", 4) 237 c["user"] = con[0] 238 c["role"] = con[1] 239 c["type"] = con[2] 240 c["level"] = con[3] 241 } 242 return c 243 } 244 245 func ReserveLabel(scon string) { 246 if len(scon) != 0 { 247 con := strings.SplitN(scon, ":", 4) 248 mcsAdd(con[3]) 249 } 250 } 251 252 func selinuxEnforcePath() string { 253 return fmt.Sprintf("%s/enforce", selinuxPath) 254 } 255 256 func SelinuxGetEnforce() int { 257 var enforce int 258 259 enforceS, err := readCon(selinuxEnforcePath()) 260 if err != nil { 261 return -1 262 } 263 264 enforce, err = strconv.Atoi(string(enforceS)) 265 if err != nil { 266 return -1 267 } 268 return enforce 269 } 270 271 func SelinuxSetEnforce(mode int) error { 272 return writeCon(selinuxEnforcePath(), fmt.Sprintf("%d", mode)) 273 } 274 275 func SelinuxGetEnforceMode() int { 276 switch readConfig(selinuxTag) { 277 case "enforcing": 278 return Enforcing 279 case "permissive": 280 return Permissive 281 } 282 return Disabled 283 } 284 285 func mcsAdd(mcs string) error { 286 mcsLock.Lock() 287 defer mcsLock.Unlock() 288 if mcsList[mcs] { 289 return fmt.Errorf("MCS Label already exists") 290 } 291 mcsList[mcs] = true 292 return nil 293 } 294 295 func mcsDelete(mcs string) { 296 mcsLock.Lock() 297 mcsList[mcs] = false 298 mcsLock.Unlock() 299 } 300 301 func IntToMcs(id int, catRange uint32) string { 302 var ( 303 SETSIZE = int(catRange) 304 TIER = SETSIZE 305 ORD = id 306 ) 307 308 if id < 1 || id > 523776 { 309 return "" 310 } 311 312 for ORD > TIER { 313 ORD = ORD - TIER 314 TIER-- 315 } 316 TIER = SETSIZE - TIER 317 ORD = ORD + TIER 318 return fmt.Sprintf("s0:c%d,c%d", TIER, ORD) 319 } 320 321 func uniqMcs(catRange uint32) string { 322 var ( 323 n uint32 324 c1, c2 uint32 325 mcs string 326 ) 327 328 for { 329 binary.Read(rand.Reader, binary.LittleEndian, &n) 330 c1 = n % catRange 331 binary.Read(rand.Reader, binary.LittleEndian, &n) 332 c2 = n % catRange 333 if c1 == c2 { 334 continue 335 } else { 336 if c1 > c2 { 337 t := c1 338 c1 = c2 339 c2 = t 340 } 341 } 342 mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2) 343 if err := mcsAdd(mcs); err != nil { 344 continue 345 } 346 break 347 } 348 return mcs 349 } 350 351 func FreeLxcContexts(scon string) { 352 if len(scon) != 0 { 353 con := strings.SplitN(scon, ":", 4) 354 mcsDelete(con[3]) 355 } 356 } 357 358 var roFileLabel string 359 360 func GetROFileLabel() (fileLabel string) { 361 return roFileLabel 362 } 363 364 func GetLxcContexts() (processLabel string, fileLabel string) { 365 var ( 366 val, key string 367 bufin *bufio.Reader 368 ) 369 370 if !SelinuxEnabled() { 371 return "", "" 372 } 373 lxcPath := fmt.Sprintf("%s/contexts/lxc_contexts", getSELinuxPolicyRoot()) 374 in, err := os.Open(lxcPath) 375 if err != nil { 376 return "", "" 377 } 378 defer in.Close() 379 380 bufin = bufio.NewReader(in) 381 382 for done := false; !done; { 383 var line string 384 if line, err = bufin.ReadString('\n'); err != nil { 385 if err == io.EOF { 386 done = true 387 } else { 388 goto exit 389 } 390 } 391 line = strings.TrimSpace(line) 392 if len(line) == 0 { 393 // Skip blank lines 394 continue 395 } 396 if line[0] == ';' || line[0] == '#' { 397 // Skip comments 398 continue 399 } 400 if groups := assignRegex.FindStringSubmatch(line); groups != nil { 401 key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) 402 if key == "process" { 403 processLabel = strings.Trim(val, "\"") 404 } 405 if key == "file" { 406 fileLabel = strings.Trim(val, "\"") 407 } 408 if key == "ro_file" { 409 roFileLabel = strings.Trim(val, "\"") 410 } 411 } 412 } 413 414 if processLabel == "" || fileLabel == "" { 415 return "", "" 416 } 417 418 if roFileLabel == "" { 419 roFileLabel = fileLabel 420 } 421 exit: 422 // mcs := IntToMcs(os.Getpid(), 1024) 423 mcs := uniqMcs(1024) 424 scon := NewContext(processLabel) 425 scon["level"] = mcs 426 processLabel = scon.Get() 427 scon = NewContext(fileLabel) 428 scon["level"] = mcs 429 fileLabel = scon.Get() 430 return processLabel, fileLabel 431 } 432 433 func SecurityCheckContext(val string) error { 434 return writeCon(fmt.Sprintf("%s.context", selinuxPath), val) 435 } 436 437 func CopyLevel(src, dest string) (string, error) { 438 if src == "" { 439 return "", nil 440 } 441 if err := SecurityCheckContext(src); err != nil { 442 return "", err 443 } 444 if err := SecurityCheckContext(dest); err != nil { 445 return "", err 446 } 447 scon := NewContext(src) 448 tcon := NewContext(dest) 449 mcsDelete(tcon["level"]) 450 mcsAdd(scon["level"]) 451 tcon["level"] = scon["level"] 452 return tcon.Get(), nil 453 } 454 455 // Prevent users from relabing system files 456 func badPrefix(fpath string) error { 457 var badprefixes = []string{"/usr"} 458 459 for _, prefix := range badprefixes { 460 if fpath == prefix || strings.HasPrefix(fpath, fmt.Sprintf("%s/", prefix)) { 461 return fmt.Errorf("Relabeling content in %s is not allowed.", prefix) 462 } 463 } 464 return nil 465 } 466 467 // Chcon changes the fpath file object to the SELinux label scon. 468 // If the fpath is a directory and recurse is true Chcon will walk the 469 // directory tree setting the label 470 func Chcon(fpath string, scon string, recurse bool) error { 471 if scon == "" { 472 return nil 473 } 474 if err := badPrefix(fpath); err != nil { 475 return err 476 } 477 callback := func(p string, info os.FileInfo, err error) error { 478 return Setfilecon(p, scon) 479 } 480 481 if recurse { 482 return filepath.Walk(fpath, callback) 483 } 484 485 return Setfilecon(fpath, scon) 486 } 487 488 // DupSecOpt takes an SELinux process label and returns security options that 489 // can will set the SELinux Type and Level for future container processes 490 func DupSecOpt(src string) []string { 491 if src == "" { 492 return nil 493 } 494 con := NewContext(src) 495 if con["user"] == "" || 496 con["role"] == "" || 497 con["type"] == "" || 498 con["level"] == "" { 499 return nil 500 } 501 return []string{"label=user:" + con["user"], 502 "label=role:" + con["role"], 503 "label=type:" + con["type"], 504 "label=level:" + con["level"]} 505 } 506 507 // DisableSecOpt returns a security opt that can be used to disabling SELinux 508 // labeling support for future container processes 509 func DisableSecOpt() []string { 510 return []string{"label=disable"} 511 }