selinux_linux.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  1. // +build selinux,linux
  2. package selinux
  3. import (
  4. "bufio"
  5. "bytes"
  6. "crypto/rand"
  7. "encoding/binary"
  8. "fmt"
  9. "io"
  10. "io/ioutil"
  11. "os"
  12. "path"
  13. "path/filepath"
  14. "regexp"
  15. "strconv"
  16. "strings"
  17. "sync"
  18. "github.com/opencontainers/selinux/pkg/pwalk"
  19. "github.com/pkg/errors"
  20. "golang.org/x/sys/unix"
  21. )
  22. const (
  23. // Enforcing constant indicate SELinux is in enforcing mode
  24. Enforcing = 1
  25. // Permissive constant to indicate SELinux is in permissive mode
  26. Permissive = 0
  27. // Disabled constant to indicate SELinux is disabled
  28. Disabled = -1
  29. contextFile = "/usr/share/containers/selinux/contexts"
  30. selinuxDir = "/etc/selinux/"
  31. selinuxConfig = selinuxDir + "config"
  32. selinuxfsMount = "/sys/fs/selinux"
  33. selinuxTypeTag = "SELINUXTYPE"
  34. selinuxTag = "SELINUX"
  35. xattrNameSelinux = "security.selinux"
  36. )
  37. type selinuxState struct {
  38. enabledSet bool
  39. enabled bool
  40. selinuxfsOnce sync.Once
  41. selinuxfs string
  42. mcsList map[string]bool
  43. sync.Mutex
  44. }
  45. var (
  46. // ErrMCSAlreadyExists is returned when trying to allocate a duplicate MCS.
  47. ErrMCSAlreadyExists = errors.New("MCS label already exists")
  48. // ErrEmptyPath is returned when an empty path has been specified.
  49. ErrEmptyPath = errors.New("empty path")
  50. // InvalidLabel is returned when an invalid label is specified.
  51. InvalidLabel = errors.New("Invalid Label")
  52. assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`)
  53. roFileLabel string
  54. state = selinuxState{
  55. mcsList: make(map[string]bool),
  56. }
  57. // for attrPath()
  58. attrPathOnce sync.Once
  59. haveThreadSelf bool
  60. )
  61. // Context is a representation of the SELinux label broken into 4 parts
  62. type Context map[string]string
  63. func (s *selinuxState) setEnable(enabled bool) bool {
  64. s.Lock()
  65. defer s.Unlock()
  66. s.enabledSet = true
  67. s.enabled = enabled
  68. return s.enabled
  69. }
  70. func (s *selinuxState) getEnabled() bool {
  71. s.Lock()
  72. enabled := s.enabled
  73. enabledSet := s.enabledSet
  74. s.Unlock()
  75. if enabledSet {
  76. return enabled
  77. }
  78. enabled = false
  79. if fs := getSelinuxMountPoint(); fs != "" {
  80. if con, _ := CurrentLabel(); con != "kernel" {
  81. enabled = true
  82. }
  83. }
  84. return s.setEnable(enabled)
  85. }
  86. // SetDisabled disables selinux support for the package
  87. func SetDisabled() {
  88. state.setEnable(false)
  89. }
  90. func verifySELinuxfsMount(mnt string) bool {
  91. var buf unix.Statfs_t
  92. for {
  93. err := unix.Statfs(mnt, &buf)
  94. if err == nil {
  95. break
  96. }
  97. if err == unix.EAGAIN {
  98. continue
  99. }
  100. return false
  101. }
  102. if uint32(buf.Type) != uint32(unix.SELINUX_MAGIC) {
  103. return false
  104. }
  105. if (buf.Flags & unix.ST_RDONLY) != 0 {
  106. return false
  107. }
  108. return true
  109. }
  110. func findSELinuxfs() string {
  111. // fast path: check the default mount first
  112. if verifySELinuxfsMount(selinuxfsMount) {
  113. return selinuxfsMount
  114. }
  115. // check if selinuxfs is available before going the slow path
  116. fs, err := ioutil.ReadFile("/proc/filesystems")
  117. if err != nil {
  118. return ""
  119. }
  120. if !bytes.Contains(fs, []byte("\tselinuxfs\n")) {
  121. return ""
  122. }
  123. // slow path: try to find among the mounts
  124. f, err := os.Open("/proc/self/mountinfo")
  125. if err != nil {
  126. return ""
  127. }
  128. defer f.Close()
  129. scanner := bufio.NewScanner(f)
  130. for {
  131. mnt := findSELinuxfsMount(scanner)
  132. if mnt == "" { // error or not found
  133. return ""
  134. }
  135. if verifySELinuxfsMount(mnt) {
  136. return mnt
  137. }
  138. }
  139. }
  140. // findSELinuxfsMount returns a next selinuxfs mount point found,
  141. // if there is one, or an empty string in case of EOF or error.
  142. func findSELinuxfsMount(s *bufio.Scanner) string {
  143. for s.Scan() {
  144. txt := s.Bytes()
  145. // The first field after - is fs type.
  146. // Safe as spaces in mountpoints are encoded as \040
  147. if !bytes.Contains(txt, []byte(" - selinuxfs ")) {
  148. continue
  149. }
  150. const mPos = 5 // mount point is 5th field
  151. fields := bytes.SplitN(txt, []byte(" "), mPos+1)
  152. if len(fields) < mPos+1 {
  153. continue
  154. }
  155. return string(fields[mPos-1])
  156. }
  157. return ""
  158. }
  159. func (s *selinuxState) getSELinuxfs() string {
  160. s.selinuxfsOnce.Do(func() {
  161. s.selinuxfs = findSELinuxfs()
  162. })
  163. return s.selinuxfs
  164. }
  165. // getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs
  166. // filesystem or an empty string if no mountpoint is found. Selinuxfs is
  167. // a proc-like pseudo-filesystem that exposes the selinux policy API to
  168. // processes. The existence of an selinuxfs mount is used to determine
  169. // whether selinux is currently enabled or not.
  170. func getSelinuxMountPoint() string {
  171. return state.getSELinuxfs()
  172. }
  173. // GetEnabled returns whether selinux is currently enabled.
  174. func GetEnabled() bool {
  175. return state.getEnabled()
  176. }
  177. func readConfig(target string) string {
  178. var (
  179. val, key string
  180. bufin *bufio.Reader
  181. )
  182. in, err := os.Open(selinuxConfig)
  183. if err != nil {
  184. return ""
  185. }
  186. defer in.Close()
  187. bufin = bufio.NewReader(in)
  188. for done := false; !done; {
  189. var line string
  190. if line, err = bufin.ReadString('\n'); err != nil {
  191. if err != io.EOF {
  192. return ""
  193. }
  194. done = true
  195. }
  196. line = strings.TrimSpace(line)
  197. if len(line) == 0 {
  198. // Skip blank lines
  199. continue
  200. }
  201. if line[0] == ';' || line[0] == '#' {
  202. // Skip comments
  203. continue
  204. }
  205. if groups := assignRegex.FindStringSubmatch(line); groups != nil {
  206. key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
  207. if key == target {
  208. return strings.Trim(val, "\"")
  209. }
  210. }
  211. }
  212. return ""
  213. }
  214. func getSELinuxPolicyRoot() string {
  215. return filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
  216. }
  217. func isProcHandle(fh *os.File) error {
  218. var buf unix.Statfs_t
  219. err := unix.Fstatfs(int(fh.Fd()), &buf)
  220. if err != nil {
  221. return errors.Wrapf(err, "statfs(%q) failed", fh.Name())
  222. }
  223. if buf.Type != unix.PROC_SUPER_MAGIC {
  224. return errors.Errorf("file %q is not on procfs", fh.Name())
  225. }
  226. return nil
  227. }
  228. func readCon(fpath string) (string, error) {
  229. if fpath == "" {
  230. return "", ErrEmptyPath
  231. }
  232. in, err := os.Open(fpath)
  233. if err != nil {
  234. return "", err
  235. }
  236. defer in.Close()
  237. if err := isProcHandle(in); err != nil {
  238. return "", err
  239. }
  240. var retval string
  241. if _, err := fmt.Fscanf(in, "%s", &retval); err != nil {
  242. return "", err
  243. }
  244. return strings.Trim(retval, "\x00"), nil
  245. }
  246. // ClassIndex returns the int index for an object class in the loaded policy, or -1 and an error
  247. func ClassIndex(class string) (int, error) {
  248. permpath := fmt.Sprintf("class/%s/index", class)
  249. indexpath := filepath.Join(getSelinuxMountPoint(), permpath)
  250. indexB, err := ioutil.ReadFile(indexpath)
  251. if err != nil {
  252. return -1, err
  253. }
  254. index, err := strconv.Atoi(string(indexB))
  255. if err != nil {
  256. return -1, err
  257. }
  258. return index, nil
  259. }
  260. // SetFileLabel sets the SELinux label for this path or returns an error.
  261. func SetFileLabel(fpath string, label string) error {
  262. if fpath == "" {
  263. return ErrEmptyPath
  264. }
  265. if err := unix.Lsetxattr(fpath, xattrNameSelinux, []byte(label), 0); err != nil {
  266. return errors.Wrapf(err, "failed to set file label on %s", fpath)
  267. }
  268. return nil
  269. }
  270. // FileLabel returns the SELinux label for this path or returns an error.
  271. func FileLabel(fpath string) (string, error) {
  272. if fpath == "" {
  273. return "", ErrEmptyPath
  274. }
  275. label, err := lgetxattr(fpath, xattrNameSelinux)
  276. if err != nil {
  277. return "", err
  278. }
  279. // Trim the NUL byte at the end of the byte buffer, if present.
  280. if len(label) > 0 && label[len(label)-1] == '\x00' {
  281. label = label[:len(label)-1]
  282. }
  283. return string(label), nil
  284. }
  285. /*
  286. SetFSCreateLabel tells kernel the label to create all file system objects
  287. created by this task. Setting label="" to return to default.
  288. */
  289. func SetFSCreateLabel(label string) error {
  290. return writeAttr("fscreate", label)
  291. }
  292. /*
  293. FSCreateLabel returns the default label the kernel which the kernel is using
  294. for file system objects created by this task. "" indicates default.
  295. */
  296. func FSCreateLabel() (string, error) {
  297. return readAttr("fscreate")
  298. }
  299. // CurrentLabel returns the SELinux label of the current process thread, or an error.
  300. func CurrentLabel() (string, error) {
  301. return readAttr("current")
  302. }
  303. // PidLabel returns the SELinux label of the given pid, or an error.
  304. func PidLabel(pid int) (string, error) {
  305. return readCon(fmt.Sprintf("/proc/%d/attr/current", pid))
  306. }
  307. /*
  308. ExecLabel returns the SELinux label that the kernel will use for any programs
  309. that are executed by the current process thread, or an error.
  310. */
  311. func ExecLabel() (string, error) {
  312. return readAttr("exec")
  313. }
  314. func writeCon(fpath, val string) error {
  315. if fpath == "" {
  316. return ErrEmptyPath
  317. }
  318. if val == "" {
  319. if !GetEnabled() {
  320. return nil
  321. }
  322. }
  323. out, err := os.OpenFile(fpath, os.O_WRONLY, 0)
  324. if err != nil {
  325. return err
  326. }
  327. defer out.Close()
  328. if err := isProcHandle(out); err != nil {
  329. return err
  330. }
  331. if val != "" {
  332. _, err = out.Write([]byte(val))
  333. } else {
  334. _, err = out.Write(nil)
  335. }
  336. if err != nil {
  337. return errors.Wrapf(err, "failed to set %s on procfs", fpath)
  338. }
  339. return nil
  340. }
  341. func attrPath(attr string) string {
  342. // Linux >= 3.17 provides this
  343. const threadSelfPrefix = "/proc/thread-self/attr"
  344. attrPathOnce.Do(func() {
  345. st, err := os.Stat(threadSelfPrefix)
  346. if err == nil && st.Mode().IsDir() {
  347. haveThreadSelf = true
  348. }
  349. })
  350. if haveThreadSelf {
  351. return path.Join(threadSelfPrefix, attr)
  352. }
  353. return path.Join("/proc/self/task/", strconv.Itoa(unix.Gettid()), "/attr/", attr)
  354. }
  355. func readAttr(attr string) (string, error) {
  356. return readCon(attrPath(attr))
  357. }
  358. func writeAttr(attr, val string) error {
  359. return writeCon(attrPath(attr), val)
  360. }
  361. /*
  362. CanonicalizeContext takes a context string and writes it to the kernel
  363. the function then returns the context that the kernel will use. This function
  364. can be used to see if two contexts are equivalent
  365. */
  366. func CanonicalizeContext(val string) (string, error) {
  367. return readWriteCon(filepath.Join(getSelinuxMountPoint(), "context"), val)
  368. }
  369. /*
  370. ComputeCreateContext requests the type transition from source to target for class from the kernel.
  371. */
  372. func ComputeCreateContext(source string, target string, class string) (string, error) {
  373. classidx, err := ClassIndex(class)
  374. if err != nil {
  375. return "", err
  376. }
  377. return readWriteCon(filepath.Join(getSelinuxMountPoint(), "create"), fmt.Sprintf("%s %s %d", source, target, classidx))
  378. }
  379. func readWriteCon(fpath string, val string) (string, error) {
  380. if fpath == "" {
  381. return "", ErrEmptyPath
  382. }
  383. f, err := os.OpenFile(fpath, os.O_RDWR, 0)
  384. if err != nil {
  385. return "", err
  386. }
  387. defer f.Close()
  388. _, err = f.Write([]byte(val))
  389. if err != nil {
  390. return "", err
  391. }
  392. var retval string
  393. if _, err := fmt.Fscanf(f, "%s", &retval); err != nil {
  394. return "", err
  395. }
  396. return strings.Trim(retval, "\x00"), nil
  397. }
  398. /*
  399. SetExecLabel sets the SELinux label that the kernel will use for any programs
  400. that are executed by the current process thread, or an error.
  401. */
  402. func SetExecLabel(label string) error {
  403. return writeAttr("exec", label)
  404. }
  405. /*
  406. SetTaskLabel sets the SELinux label for the current thread, or an error.
  407. This requires the dyntransition permission.
  408. */
  409. func SetTaskLabel(label string) error {
  410. return writeAttr("current", label)
  411. }
  412. // SetSocketLabel takes a process label and tells the kernel to assign the
  413. // label to the next socket that gets created
  414. func SetSocketLabel(label string) error {
  415. return writeAttr("sockcreate", label)
  416. }
  417. // SocketLabel retrieves the current socket label setting
  418. func SocketLabel() (string, error) {
  419. return readAttr("sockcreate")
  420. }
  421. // PeerLabel retrieves the label of the client on the other side of a socket
  422. func PeerLabel(fd uintptr) (string, error) {
  423. return unix.GetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_PEERSEC)
  424. }
  425. // SetKeyLabel takes a process label and tells the kernel to assign the
  426. // label to the next kernel keyring that gets created
  427. func SetKeyLabel(label string) error {
  428. err := writeCon("/proc/self/attr/keycreate", label)
  429. if os.IsNotExist(errors.Cause(err)) {
  430. return nil
  431. }
  432. if label == "" && os.IsPermission(errors.Cause(err)) {
  433. return nil
  434. }
  435. return err
  436. }
  437. // KeyLabel retrieves the current kernel keyring label setting
  438. func KeyLabel() (string, error) {
  439. return readCon("/proc/self/attr/keycreate")
  440. }
  441. // Get returns the Context as a string
  442. func (c Context) Get() string {
  443. if c["level"] != "" {
  444. return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"])
  445. }
  446. return fmt.Sprintf("%s:%s:%s", c["user"], c["role"], c["type"])
  447. }
  448. // NewContext creates a new Context struct from the specified label
  449. func NewContext(label string) (Context, error) {
  450. c := make(Context)
  451. if len(label) != 0 {
  452. con := strings.SplitN(label, ":", 4)
  453. if len(con) < 3 {
  454. return c, InvalidLabel
  455. }
  456. c["user"] = con[0]
  457. c["role"] = con[1]
  458. c["type"] = con[2]
  459. if len(con) > 3 {
  460. c["level"] = con[3]
  461. }
  462. }
  463. return c, nil
  464. }
  465. // ClearLabels clears all reserved labels
  466. func ClearLabels() {
  467. state.Lock()
  468. state.mcsList = make(map[string]bool)
  469. state.Unlock()
  470. }
  471. // ReserveLabel reserves the MLS/MCS level component of the specified label
  472. func ReserveLabel(label string) {
  473. if len(label) != 0 {
  474. con := strings.SplitN(label, ":", 4)
  475. if len(con) > 3 {
  476. mcsAdd(con[3])
  477. }
  478. }
  479. }
  480. func selinuxEnforcePath() string {
  481. return path.Join(getSelinuxMountPoint(), "enforce")
  482. }
  483. // EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
  484. func EnforceMode() int {
  485. var enforce int
  486. enforceB, err := ioutil.ReadFile(selinuxEnforcePath())
  487. if err != nil {
  488. return -1
  489. }
  490. enforce, err = strconv.Atoi(string(enforceB))
  491. if err != nil {
  492. return -1
  493. }
  494. return enforce
  495. }
  496. /*
  497. SetEnforceMode sets the current SELinux mode Enforcing, Permissive.
  498. Disabled is not valid, since this needs to be set at boot time.
  499. */
  500. func SetEnforceMode(mode int) error {
  501. return ioutil.WriteFile(selinuxEnforcePath(), []byte(strconv.Itoa(mode)), 0644)
  502. }
  503. /*
  504. DefaultEnforceMode returns the systems default SELinux mode Enforcing,
  505. Permissive or Disabled. Note this is is just the default at boot time.
  506. EnforceMode tells you the systems current mode.
  507. */
  508. func DefaultEnforceMode() int {
  509. switch readConfig(selinuxTag) {
  510. case "enforcing":
  511. return Enforcing
  512. case "permissive":
  513. return Permissive
  514. }
  515. return Disabled
  516. }
  517. func mcsAdd(mcs string) error {
  518. if mcs == "" {
  519. return nil
  520. }
  521. state.Lock()
  522. defer state.Unlock()
  523. if state.mcsList[mcs] {
  524. return ErrMCSAlreadyExists
  525. }
  526. state.mcsList[mcs] = true
  527. return nil
  528. }
  529. func mcsDelete(mcs string) {
  530. if mcs == "" {
  531. return
  532. }
  533. state.Lock()
  534. defer state.Unlock()
  535. state.mcsList[mcs] = false
  536. }
  537. func intToMcs(id int, catRange uint32) string {
  538. var (
  539. SETSIZE = int(catRange)
  540. TIER = SETSIZE
  541. ORD = id
  542. )
  543. if id < 1 || id > 523776 {
  544. return ""
  545. }
  546. for ORD > TIER {
  547. ORD = ORD - TIER
  548. TIER--
  549. }
  550. TIER = SETSIZE - TIER
  551. ORD = ORD + TIER
  552. return fmt.Sprintf("s0:c%d,c%d", TIER, ORD)
  553. }
  554. func uniqMcs(catRange uint32) string {
  555. var (
  556. n uint32
  557. c1, c2 uint32
  558. mcs string
  559. )
  560. for {
  561. binary.Read(rand.Reader, binary.LittleEndian, &n)
  562. c1 = n % catRange
  563. binary.Read(rand.Reader, binary.LittleEndian, &n)
  564. c2 = n % catRange
  565. if c1 == c2 {
  566. continue
  567. } else {
  568. if c1 > c2 {
  569. c1, c2 = c2, c1
  570. }
  571. }
  572. mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2)
  573. if err := mcsAdd(mcs); err != nil {
  574. continue
  575. }
  576. break
  577. }
  578. return mcs
  579. }
  580. /*
  581. ReleaseLabel will unreserve the MLS/MCS Level field of the specified label.
  582. Allowing it to be used by another process.
  583. */
  584. func ReleaseLabel(label string) {
  585. if len(label) != 0 {
  586. con := strings.SplitN(label, ":", 4)
  587. if len(con) > 3 {
  588. mcsDelete(con[3])
  589. }
  590. }
  591. }
  592. // ROFileLabel returns the specified SELinux readonly file label
  593. func ROFileLabel() string {
  594. return roFileLabel
  595. }
  596. func openContextFile() (*os.File, error) {
  597. if f, err := os.Open(contextFile); err == nil {
  598. return f, nil
  599. }
  600. lxcPath := filepath.Join(getSELinuxPolicyRoot(), "/contexts/lxc_contexts")
  601. return os.Open(lxcPath)
  602. }
  603. var labels = loadLabels()
  604. func loadLabels() map[string]string {
  605. var (
  606. val, key string
  607. bufin *bufio.Reader
  608. )
  609. labels := make(map[string]string)
  610. in, err := openContextFile()
  611. if err != nil {
  612. return labels
  613. }
  614. defer in.Close()
  615. bufin = bufio.NewReader(in)
  616. for done := false; !done; {
  617. var line string
  618. if line, err = bufin.ReadString('\n'); err != nil {
  619. if err == io.EOF {
  620. done = true
  621. } else {
  622. break
  623. }
  624. }
  625. line = strings.TrimSpace(line)
  626. if len(line) == 0 {
  627. // Skip blank lines
  628. continue
  629. }
  630. if line[0] == ';' || line[0] == '#' {
  631. // Skip comments
  632. continue
  633. }
  634. if groups := assignRegex.FindStringSubmatch(line); groups != nil {
  635. key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
  636. labels[key] = strings.Trim(val, "\"")
  637. }
  638. }
  639. return labels
  640. }
  641. /*
  642. KVMContainerLabels returns the default processLabel and mountLabel to be used
  643. for kvm containers by the calling process.
  644. */
  645. func KVMContainerLabels() (string, string) {
  646. processLabel := labels["kvm_process"]
  647. if processLabel == "" {
  648. processLabel = labels["process"]
  649. }
  650. return addMcs(processLabel, labels["file"])
  651. }
  652. /*
  653. InitContainerLabels returns the default processLabel and file labels to be
  654. used for containers running an init system like systemd by the calling process.
  655. */
  656. func InitContainerLabels() (string, string) {
  657. processLabel := labels["init_process"]
  658. if processLabel == "" {
  659. processLabel = labels["process"]
  660. }
  661. return addMcs(processLabel, labels["file"])
  662. }
  663. /*
  664. ContainerLabels returns an allocated processLabel and fileLabel to be used for
  665. container labeling by the calling process.
  666. */
  667. func ContainerLabels() (processLabel string, fileLabel string) {
  668. if !GetEnabled() {
  669. return "", ""
  670. }
  671. processLabel = labels["process"]
  672. fileLabel = labels["file"]
  673. roFileLabel = labels["ro_file"]
  674. if processLabel == "" || fileLabel == "" {
  675. return "", fileLabel
  676. }
  677. if roFileLabel == "" {
  678. roFileLabel = fileLabel
  679. }
  680. return addMcs(processLabel, fileLabel)
  681. }
  682. func addMcs(processLabel, fileLabel string) (string, string) {
  683. scon, _ := NewContext(processLabel)
  684. if scon["level"] != "" {
  685. mcs := uniqMcs(1024)
  686. scon["level"] = mcs
  687. processLabel = scon.Get()
  688. scon, _ = NewContext(fileLabel)
  689. scon["level"] = mcs
  690. fileLabel = scon.Get()
  691. }
  692. return processLabel, fileLabel
  693. }
  694. // SecurityCheckContext validates that the SELinux label is understood by the kernel
  695. func SecurityCheckContext(val string) error {
  696. return ioutil.WriteFile(path.Join(getSelinuxMountPoint(), "context"), []byte(val), 0644)
  697. }
  698. /*
  699. CopyLevel returns a label with the MLS/MCS level from src label replaced on
  700. the dest label.
  701. */
  702. func CopyLevel(src, dest string) (string, error) {
  703. if src == "" {
  704. return "", nil
  705. }
  706. if err := SecurityCheckContext(src); err != nil {
  707. return "", err
  708. }
  709. if err := SecurityCheckContext(dest); err != nil {
  710. return "", err
  711. }
  712. scon, err := NewContext(src)
  713. if err != nil {
  714. return "", err
  715. }
  716. tcon, err := NewContext(dest)
  717. if err != nil {
  718. return "", err
  719. }
  720. mcsDelete(tcon["level"])
  721. mcsAdd(scon["level"])
  722. tcon["level"] = scon["level"]
  723. return tcon.Get(), nil
  724. }
  725. // Prevent users from relabing system files
  726. func badPrefix(fpath string) error {
  727. if fpath == "" {
  728. return ErrEmptyPath
  729. }
  730. badPrefixes := []string{"/usr"}
  731. for _, prefix := range badPrefixes {
  732. if strings.HasPrefix(fpath, prefix) {
  733. return errors.Errorf("relabeling content in %s is not allowed", prefix)
  734. }
  735. }
  736. return nil
  737. }
  738. // Chcon changes the fpath file object to the SELinux label label.
  739. // If fpath is a directory and recurse is true, Chcon will walk the
  740. // directory tree setting the label.
  741. func Chcon(fpath string, label string, recurse bool) error {
  742. if fpath == "" {
  743. return ErrEmptyPath
  744. }
  745. if label == "" {
  746. return nil
  747. }
  748. if err := badPrefix(fpath); err != nil {
  749. return err
  750. }
  751. if !recurse {
  752. return SetFileLabel(fpath, label)
  753. }
  754. return pwalk.Walk(fpath, func(p string, info os.FileInfo, err error) error {
  755. e := SetFileLabel(p, label)
  756. // Walk a file tree can race with removal, so ignore ENOENT
  757. if os.IsNotExist(errors.Cause(e)) {
  758. return nil
  759. }
  760. return e
  761. })
  762. }
  763. // DupSecOpt takes an SELinux process label and returns security options that
  764. // can be used to set the SELinux Type and Level for future container processes.
  765. func DupSecOpt(src string) ([]string, error) {
  766. if src == "" {
  767. return nil, nil
  768. }
  769. con, err := NewContext(src)
  770. if err != nil {
  771. return nil, err
  772. }
  773. if con["user"] == "" ||
  774. con["role"] == "" ||
  775. con["type"] == "" {
  776. return nil, nil
  777. }
  778. dup := []string{"user:" + con["user"],
  779. "role:" + con["role"],
  780. "type:" + con["type"],
  781. }
  782. if con["level"] != "" {
  783. dup = append(dup, "level:"+con["level"])
  784. }
  785. return dup, nil
  786. }
  787. // DisableSecOpt returns a security opt that can be used to disable SELinux
  788. // labeling support for future container processes.
  789. func DisableSecOpt() []string {
  790. return []string{"disable"}
  791. }