selinux_linux.go 18 KB

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