selinux_linux.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212
  1. package selinux
  2. import (
  3. "bufio"
  4. "bytes"
  5. "crypto/rand"
  6. "encoding/binary"
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "os"
  11. "path"
  12. "path/filepath"
  13. "regexp"
  14. "strconv"
  15. "strings"
  16. "sync"
  17. "github.com/bits-and-blooms/bitset"
  18. "github.com/opencontainers/selinux/pkg/pwalk"
  19. "github.com/pkg/errors"
  20. "golang.org/x/sys/unix"
  21. )
  22. const (
  23. minSensLen = 2
  24. contextFile = "/usr/share/containers/selinux/contexts"
  25. selinuxDir = "/etc/selinux/"
  26. selinuxUsersDir = "contexts/users"
  27. defaultContexts = "contexts/default_contexts"
  28. selinuxConfig = selinuxDir + "config"
  29. selinuxfsMount = "/sys/fs/selinux"
  30. selinuxTypeTag = "SELINUXTYPE"
  31. selinuxTag = "SELINUX"
  32. xattrNameSelinux = "security.selinux"
  33. )
  34. var policyRoot = filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
  35. type selinuxState struct {
  36. enabledSet bool
  37. enabled bool
  38. selinuxfsOnce sync.Once
  39. selinuxfs string
  40. mcsList map[string]bool
  41. sync.Mutex
  42. }
  43. type level struct {
  44. sens uint
  45. cats *bitset.BitSet
  46. }
  47. type mlsRange struct {
  48. low *level
  49. high *level
  50. }
  51. type defaultSECtx struct {
  52. user, level, scon string
  53. userRdr, defaultRdr io.Reader
  54. verifier func(string) error
  55. }
  56. type levelItem byte
  57. const (
  58. sensitivity levelItem = 's'
  59. category levelItem = 'c'
  60. )
  61. var (
  62. assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`)
  63. readOnlyFileLabel string
  64. state = selinuxState{
  65. mcsList: make(map[string]bool),
  66. }
  67. // for attrPath()
  68. attrPathOnce sync.Once
  69. haveThreadSelf bool
  70. )
  71. func (s *selinuxState) setEnable(enabled bool) bool {
  72. s.Lock()
  73. defer s.Unlock()
  74. s.enabledSet = true
  75. s.enabled = enabled
  76. return s.enabled
  77. }
  78. func (s *selinuxState) getEnabled() bool {
  79. s.Lock()
  80. enabled := s.enabled
  81. enabledSet := s.enabledSet
  82. s.Unlock()
  83. if enabledSet {
  84. return enabled
  85. }
  86. enabled = false
  87. if fs := getSelinuxMountPoint(); fs != "" {
  88. if con, _ := CurrentLabel(); con != "kernel" {
  89. enabled = true
  90. }
  91. }
  92. return s.setEnable(enabled)
  93. }
  94. // setDisabled disables SELinux support for the package
  95. func setDisabled() {
  96. state.setEnable(false)
  97. }
  98. func verifySELinuxfsMount(mnt string) bool {
  99. var buf unix.Statfs_t
  100. for {
  101. err := unix.Statfs(mnt, &buf)
  102. if err == nil {
  103. break
  104. }
  105. if err == unix.EAGAIN || err == unix.EINTR {
  106. continue
  107. }
  108. return false
  109. }
  110. if uint32(buf.Type) != uint32(unix.SELINUX_MAGIC) {
  111. return false
  112. }
  113. if (buf.Flags & unix.ST_RDONLY) != 0 {
  114. return false
  115. }
  116. return true
  117. }
  118. func findSELinuxfs() string {
  119. // fast path: check the default mount first
  120. if verifySELinuxfsMount(selinuxfsMount) {
  121. return selinuxfsMount
  122. }
  123. // check if selinuxfs is available before going the slow path
  124. fs, err := ioutil.ReadFile("/proc/filesystems")
  125. if err != nil {
  126. return ""
  127. }
  128. if !bytes.Contains(fs, []byte("\tselinuxfs\n")) {
  129. return ""
  130. }
  131. // slow path: try to find among the mounts
  132. f, err := os.Open("/proc/self/mountinfo")
  133. if err != nil {
  134. return ""
  135. }
  136. defer f.Close()
  137. scanner := bufio.NewScanner(f)
  138. for {
  139. mnt := findSELinuxfsMount(scanner)
  140. if mnt == "" { // error or not found
  141. return ""
  142. }
  143. if verifySELinuxfsMount(mnt) {
  144. return mnt
  145. }
  146. }
  147. }
  148. // findSELinuxfsMount returns a next selinuxfs mount point found,
  149. // if there is one, or an empty string in case of EOF or error.
  150. func findSELinuxfsMount(s *bufio.Scanner) string {
  151. for s.Scan() {
  152. txt := s.Bytes()
  153. // The first field after - is fs type.
  154. // Safe as spaces in mountpoints are encoded as \040
  155. if !bytes.Contains(txt, []byte(" - selinuxfs ")) {
  156. continue
  157. }
  158. const mPos = 5 // mount point is 5th field
  159. fields := bytes.SplitN(txt, []byte(" "), mPos+1)
  160. if len(fields) < mPos+1 {
  161. continue
  162. }
  163. return string(fields[mPos-1])
  164. }
  165. return ""
  166. }
  167. func (s *selinuxState) getSELinuxfs() string {
  168. s.selinuxfsOnce.Do(func() {
  169. s.selinuxfs = findSELinuxfs()
  170. })
  171. return s.selinuxfs
  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. in, err := os.Open(selinuxConfig)
  187. if err != nil {
  188. return ""
  189. }
  190. defer in.Close()
  191. scanner := bufio.NewScanner(in)
  192. for scanner.Scan() {
  193. line := strings.TrimSpace(scanner.Text())
  194. if len(line) == 0 {
  195. // Skip blank lines
  196. continue
  197. }
  198. if line[0] == ';' || line[0] == '#' {
  199. // Skip comments
  200. continue
  201. }
  202. if groups := assignRegex.FindStringSubmatch(line); groups != nil {
  203. key, val := strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
  204. if key == target {
  205. return strings.Trim(val, "\"")
  206. }
  207. }
  208. }
  209. return ""
  210. }
  211. func isProcHandle(fh *os.File) error {
  212. var buf unix.Statfs_t
  213. for {
  214. err := unix.Fstatfs(int(fh.Fd()), &buf)
  215. if err == nil {
  216. break
  217. }
  218. if err != unix.EINTR {
  219. return errors.Wrapf(err, "statfs(%q) failed", fh.Name())
  220. }
  221. }
  222. if buf.Type != unix.PROC_SUPER_MAGIC {
  223. return errors.Errorf("file %q is not on procfs", fh.Name())
  224. }
  225. return nil
  226. }
  227. func readCon(fpath string) (string, error) {
  228. if fpath == "" {
  229. return "", ErrEmptyPath
  230. }
  231. in, err := os.Open(fpath)
  232. if err != nil {
  233. return "", err
  234. }
  235. defer in.Close()
  236. if err := isProcHandle(in); err != nil {
  237. return "", err
  238. }
  239. var retval string
  240. if _, err := fmt.Fscanf(in, "%s", &retval); err != nil {
  241. return "", err
  242. }
  243. return strings.Trim(retval, "\x00"), nil
  244. }
  245. // classIndex returns the int index for an object class in the loaded policy,
  246. // 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. for {
  266. err := unix.Lsetxattr(fpath, xattrNameSelinux, []byte(label), 0)
  267. if err == nil {
  268. break
  269. }
  270. if err != unix.EINTR {
  271. return errors.Wrapf(err, "failed to set file label on %s", fpath)
  272. }
  273. }
  274. return nil
  275. }
  276. // fileLabel returns the SELinux label for this path or returns an error.
  277. func fileLabel(fpath string) (string, error) {
  278. if fpath == "" {
  279. return "", ErrEmptyPath
  280. }
  281. label, err := lgetxattr(fpath, xattrNameSelinux)
  282. if err != nil {
  283. return "", err
  284. }
  285. // Trim the NUL byte at the end of the byte buffer, if present.
  286. if len(label) > 0 && label[len(label)-1] == '\x00' {
  287. label = label[:len(label)-1]
  288. }
  289. return string(label), nil
  290. }
  291. // setFSCreateLabel tells kernel the label to create all file system objects
  292. // created by this task. Setting label="" to return to default.
  293. func setFSCreateLabel(label string) error {
  294. return writeAttr("fscreate", label)
  295. }
  296. // fsCreateLabel returns the default label the kernel which the kernel is using
  297. // for file system objects created by this task. "" indicates default.
  298. func fsCreateLabel() (string, error) {
  299. return readAttr("fscreate")
  300. }
  301. // currentLabel returns the SELinux label of the current process thread, or an error.
  302. func currentLabel() (string, error) {
  303. return readAttr("current")
  304. }
  305. // pidLabel returns the SELinux label of the given pid, or an error.
  306. func pidLabel(pid int) (string, error) {
  307. return readCon(fmt.Sprintf("/proc/%d/attr/current", pid))
  308. }
  309. // ExecLabel returns the SELinux label that the kernel will use for any programs
  310. // that are executed by the current process thread, or an error.
  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. // canonicalizeContext takes a context string and writes it to the kernel
  362. // the function then returns the context that the kernel will use. Use this
  363. // function to check if two contexts are equivalent
  364. func canonicalizeContext(val string) (string, error) {
  365. return readWriteCon(filepath.Join(getSelinuxMountPoint(), "context"), val)
  366. }
  367. // computeCreateContext requests the type transition from source to target for
  368. // class from the kernel.
  369. func computeCreateContext(source string, target string, class string) (string, error) {
  370. classidx, err := classIndex(class)
  371. if err != nil {
  372. return "", err
  373. }
  374. return readWriteCon(filepath.Join(getSelinuxMountPoint(), "create"), fmt.Sprintf("%s %s %d", source, target, classidx))
  375. }
  376. // catsToBitset stores categories in a bitset.
  377. func catsToBitset(cats string) (*bitset.BitSet, error) {
  378. bitset := &bitset.BitSet{}
  379. catlist := strings.Split(cats, ",")
  380. for _, r := range catlist {
  381. ranges := strings.SplitN(r, ".", 2)
  382. if len(ranges) > 1 {
  383. catstart, err := parseLevelItem(ranges[0], category)
  384. if err != nil {
  385. return nil, err
  386. }
  387. catend, err := parseLevelItem(ranges[1], category)
  388. if err != nil {
  389. return nil, err
  390. }
  391. for i := catstart; i <= catend; i++ {
  392. bitset.Set(i)
  393. }
  394. } else {
  395. cat, err := parseLevelItem(ranges[0], category)
  396. if err != nil {
  397. return nil, err
  398. }
  399. bitset.Set(cat)
  400. }
  401. }
  402. return bitset, nil
  403. }
  404. // parseLevelItem parses and verifies that a sensitivity or category are valid
  405. func parseLevelItem(s string, sep levelItem) (uint, error) {
  406. if len(s) < minSensLen || levelItem(s[0]) != sep {
  407. return 0, ErrLevelSyntax
  408. }
  409. val, err := strconv.ParseUint(s[1:], 10, 32)
  410. if err != nil {
  411. return 0, err
  412. }
  413. return uint(val), nil
  414. }
  415. // parseLevel fills a level from a string that contains
  416. // a sensitivity and categories
  417. func (l *level) parseLevel(levelStr string) error {
  418. lvl := strings.SplitN(levelStr, ":", 2)
  419. sens, err := parseLevelItem(lvl[0], sensitivity)
  420. if err != nil {
  421. return errors.Wrap(err, "failed to parse sensitivity")
  422. }
  423. l.sens = sens
  424. if len(lvl) > 1 {
  425. cats, err := catsToBitset(lvl[1])
  426. if err != nil {
  427. return errors.Wrap(err, "failed to parse categories")
  428. }
  429. l.cats = cats
  430. }
  431. return nil
  432. }
  433. // rangeStrToMLSRange marshals a string representation of a range.
  434. func rangeStrToMLSRange(rangeStr string) (*mlsRange, error) {
  435. mlsRange := &mlsRange{}
  436. levelSlice := strings.SplitN(rangeStr, "-", 2)
  437. switch len(levelSlice) {
  438. // rangeStr that has a low and a high level, e.g. s4:c0.c1023-s6:c0.c1023
  439. case 2:
  440. mlsRange.high = &level{}
  441. if err := mlsRange.high.parseLevel(levelSlice[1]); err != nil {
  442. return nil, errors.Wrapf(err, "failed to parse high level %q", levelSlice[1])
  443. }
  444. fallthrough
  445. // rangeStr that is single level, e.g. s6:c0,c3,c5,c30.c1023
  446. case 1:
  447. mlsRange.low = &level{}
  448. if err := mlsRange.low.parseLevel(levelSlice[0]); err != nil {
  449. return nil, errors.Wrapf(err, "failed to parse low level %q", levelSlice[0])
  450. }
  451. }
  452. if mlsRange.high == nil {
  453. mlsRange.high = mlsRange.low
  454. }
  455. return mlsRange, nil
  456. }
  457. // bitsetToStr takes a category bitset and returns it in the
  458. // canonical selinux syntax
  459. func bitsetToStr(c *bitset.BitSet) string {
  460. var str string
  461. i, e := c.NextSet(0)
  462. len := 0
  463. for e {
  464. if len == 0 {
  465. if str != "" {
  466. str += ","
  467. }
  468. str += "c" + strconv.Itoa(int(i))
  469. }
  470. next, e := c.NextSet(i + 1)
  471. if e {
  472. // consecutive cats
  473. if next == i+1 {
  474. len++
  475. i = next
  476. continue
  477. }
  478. }
  479. if len == 1 {
  480. str += ",c" + strconv.Itoa(int(i))
  481. } else if len > 1 {
  482. str += ".c" + strconv.Itoa(int(i))
  483. }
  484. if !e {
  485. break
  486. }
  487. len = 0
  488. i = next
  489. }
  490. return str
  491. }
  492. func (l1 *level) equal(l2 *level) bool {
  493. if l2 == nil || l1 == nil {
  494. return l1 == l2
  495. }
  496. if l1.sens != l2.sens {
  497. return false
  498. }
  499. return l1.cats.Equal(l2.cats)
  500. }
  501. // String returns an mlsRange as a string.
  502. func (m mlsRange) String() string {
  503. low := "s" + strconv.Itoa(int(m.low.sens))
  504. if m.low.cats != nil && m.low.cats.Count() > 0 {
  505. low += ":" + bitsetToStr(m.low.cats)
  506. }
  507. if m.low.equal(m.high) {
  508. return low
  509. }
  510. high := "s" + strconv.Itoa(int(m.high.sens))
  511. if m.high.cats != nil && m.high.cats.Count() > 0 {
  512. high += ":" + bitsetToStr(m.high.cats)
  513. }
  514. return low + "-" + high
  515. }
  516. func max(a, b uint) uint {
  517. if a > b {
  518. return a
  519. }
  520. return b
  521. }
  522. func min(a, b uint) uint {
  523. if a < b {
  524. return a
  525. }
  526. return b
  527. }
  528. // calculateGlbLub computes the glb (greatest lower bound) and lub (least upper bound)
  529. // of a source and target range.
  530. // The glblub is calculated as the greater of the low sensitivities and
  531. // the lower of the high sensitivities and the and of each category bitset.
  532. func calculateGlbLub(sourceRange, targetRange string) (string, error) {
  533. s, err := rangeStrToMLSRange(sourceRange)
  534. if err != nil {
  535. return "", err
  536. }
  537. t, err := rangeStrToMLSRange(targetRange)
  538. if err != nil {
  539. return "", err
  540. }
  541. if s.high.sens < t.low.sens || t.high.sens < s.low.sens {
  542. /* these ranges have no common sensitivities */
  543. return "", ErrIncomparable
  544. }
  545. outrange := &mlsRange{low: &level{}, high: &level{}}
  546. /* take the greatest of the low */
  547. outrange.low.sens = max(s.low.sens, t.low.sens)
  548. /* take the least of the high */
  549. outrange.high.sens = min(s.high.sens, t.high.sens)
  550. /* find the intersecting categories */
  551. if s.low.cats != nil && t.low.cats != nil {
  552. outrange.low.cats = s.low.cats.Intersection(t.low.cats)
  553. }
  554. if s.high.cats != nil && t.high.cats != nil {
  555. outrange.high.cats = s.high.cats.Intersection(t.high.cats)
  556. }
  557. return outrange.String(), nil
  558. }
  559. func readWriteCon(fpath string, val string) (string, error) {
  560. if fpath == "" {
  561. return "", ErrEmptyPath
  562. }
  563. f, err := os.OpenFile(fpath, os.O_RDWR, 0)
  564. if err != nil {
  565. return "", err
  566. }
  567. defer f.Close()
  568. _, err = f.Write([]byte(val))
  569. if err != nil {
  570. return "", err
  571. }
  572. var retval string
  573. if _, err := fmt.Fscanf(f, "%s", &retval); err != nil {
  574. return "", err
  575. }
  576. return strings.Trim(retval, "\x00"), nil
  577. }
  578. // setExecLabel sets the SELinux label that the kernel will use for any programs
  579. // that are executed by the current process thread, or an error.
  580. func setExecLabel(label string) error {
  581. return writeAttr("exec", label)
  582. }
  583. // setTaskLabel sets the SELinux label for the current thread, or an error.
  584. // This requires the dyntransition permission.
  585. func setTaskLabel(label string) error {
  586. return writeAttr("current", label)
  587. }
  588. // setSocketLabel takes a process label and tells the kernel to assign the
  589. // label to the next socket that gets created
  590. func setSocketLabel(label string) error {
  591. return writeAttr("sockcreate", label)
  592. }
  593. // socketLabel retrieves the current socket label setting
  594. func socketLabel() (string, error) {
  595. return readAttr("sockcreate")
  596. }
  597. // peerLabel retrieves the label of the client on the other side of a socket
  598. func peerLabel(fd uintptr) (string, error) {
  599. return unix.GetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_PEERSEC)
  600. }
  601. // setKeyLabel takes a process label and tells the kernel to assign the
  602. // label to the next kernel keyring that gets created
  603. func setKeyLabel(label string) error {
  604. err := writeCon("/proc/self/attr/keycreate", label)
  605. if os.IsNotExist(errors.Cause(err)) {
  606. return nil
  607. }
  608. if label == "" && os.IsPermission(errors.Cause(err)) {
  609. return nil
  610. }
  611. return err
  612. }
  613. // keyLabel retrieves the current kernel keyring label setting
  614. func keyLabel() (string, error) {
  615. return readCon("/proc/self/attr/keycreate")
  616. }
  617. // get returns the Context as a string
  618. func (c Context) get() string {
  619. if c["level"] != "" {
  620. return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"])
  621. }
  622. return fmt.Sprintf("%s:%s:%s", c["user"], c["role"], c["type"])
  623. }
  624. // newContext creates a new Context struct from the specified label
  625. func newContext(label string) (Context, error) {
  626. c := make(Context)
  627. if len(label) != 0 {
  628. con := strings.SplitN(label, ":", 4)
  629. if len(con) < 3 {
  630. return c, InvalidLabel
  631. }
  632. c["user"] = con[0]
  633. c["role"] = con[1]
  634. c["type"] = con[2]
  635. if len(con) > 3 {
  636. c["level"] = con[3]
  637. }
  638. }
  639. return c, nil
  640. }
  641. // clearLabels clears all reserved labels
  642. func clearLabels() {
  643. state.Lock()
  644. state.mcsList = make(map[string]bool)
  645. state.Unlock()
  646. }
  647. // reserveLabel reserves the MLS/MCS level component of the specified label
  648. func reserveLabel(label string) {
  649. if len(label) != 0 {
  650. con := strings.SplitN(label, ":", 4)
  651. if len(con) > 3 {
  652. _ = mcsAdd(con[3])
  653. }
  654. }
  655. }
  656. func selinuxEnforcePath() string {
  657. return path.Join(getSelinuxMountPoint(), "enforce")
  658. }
  659. // enforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
  660. func enforceMode() int {
  661. var enforce int
  662. enforceB, err := ioutil.ReadFile(selinuxEnforcePath())
  663. if err != nil {
  664. return -1
  665. }
  666. enforce, err = strconv.Atoi(string(enforceB))
  667. if err != nil {
  668. return -1
  669. }
  670. return enforce
  671. }
  672. // setEnforceMode sets the current SELinux mode Enforcing, Permissive.
  673. // Disabled is not valid, since this needs to be set at boot time.
  674. func setEnforceMode(mode int) error {
  675. return ioutil.WriteFile(selinuxEnforcePath(), []byte(strconv.Itoa(mode)), 0644)
  676. }
  677. // defaultEnforceMode returns the systems default SELinux mode Enforcing,
  678. // Permissive or Disabled. Note this is is just the default at boot time.
  679. // EnforceMode tells you the systems current mode.
  680. func defaultEnforceMode() int {
  681. switch readConfig(selinuxTag) {
  682. case "enforcing":
  683. return Enforcing
  684. case "permissive":
  685. return Permissive
  686. }
  687. return Disabled
  688. }
  689. func mcsAdd(mcs string) error {
  690. if mcs == "" {
  691. return nil
  692. }
  693. state.Lock()
  694. defer state.Unlock()
  695. if state.mcsList[mcs] {
  696. return ErrMCSAlreadyExists
  697. }
  698. state.mcsList[mcs] = true
  699. return nil
  700. }
  701. func mcsDelete(mcs string) {
  702. if mcs == "" {
  703. return
  704. }
  705. state.Lock()
  706. defer state.Unlock()
  707. state.mcsList[mcs] = false
  708. }
  709. func intToMcs(id int, catRange uint32) string {
  710. var (
  711. SETSIZE = int(catRange)
  712. TIER = SETSIZE
  713. ORD = id
  714. )
  715. if id < 1 || id > 523776 {
  716. return ""
  717. }
  718. for ORD > TIER {
  719. ORD -= TIER
  720. TIER--
  721. }
  722. TIER = SETSIZE - TIER
  723. ORD += TIER
  724. return fmt.Sprintf("s0:c%d,c%d", TIER, ORD)
  725. }
  726. func uniqMcs(catRange uint32) string {
  727. var (
  728. n uint32
  729. c1, c2 uint32
  730. mcs string
  731. )
  732. for {
  733. _ = binary.Read(rand.Reader, binary.LittleEndian, &n)
  734. c1 = n % catRange
  735. _ = binary.Read(rand.Reader, binary.LittleEndian, &n)
  736. c2 = n % catRange
  737. if c1 == c2 {
  738. continue
  739. } else if c1 > c2 {
  740. c1, c2 = c2, c1
  741. }
  742. mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2)
  743. if err := mcsAdd(mcs); err != nil {
  744. continue
  745. }
  746. break
  747. }
  748. return mcs
  749. }
  750. // releaseLabel un-reserves the MLS/MCS Level field of the specified label,
  751. // allowing it to be used by another process.
  752. func releaseLabel(label string) {
  753. if len(label) != 0 {
  754. con := strings.SplitN(label, ":", 4)
  755. if len(con) > 3 {
  756. mcsDelete(con[3])
  757. }
  758. }
  759. }
  760. // roFileLabel returns the specified SELinux readonly file label
  761. func roFileLabel() string {
  762. return readOnlyFileLabel
  763. }
  764. func openContextFile() (*os.File, error) {
  765. if f, err := os.Open(contextFile); err == nil {
  766. return f, nil
  767. }
  768. lxcPath := filepath.Join(policyRoot, "/contexts/lxc_contexts")
  769. return os.Open(lxcPath)
  770. }
  771. var labels, privContainerMountLabel = loadLabels()
  772. func loadLabels() (map[string]string, string) {
  773. labels := make(map[string]string)
  774. in, err := openContextFile()
  775. if err != nil {
  776. return labels, ""
  777. }
  778. defer in.Close()
  779. scanner := bufio.NewScanner(in)
  780. for scanner.Scan() {
  781. line := strings.TrimSpace(scanner.Text())
  782. if len(line) == 0 {
  783. // Skip blank lines
  784. continue
  785. }
  786. if line[0] == ';' || line[0] == '#' {
  787. // Skip comments
  788. continue
  789. }
  790. if groups := assignRegex.FindStringSubmatch(line); groups != nil {
  791. key, val := strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
  792. labels[key] = strings.Trim(val, "\"")
  793. }
  794. }
  795. con, _ := NewContext(labels["file"])
  796. con["level"] = fmt.Sprintf("s0:c%d,c%d", maxCategory-2, maxCategory-1)
  797. reserveLabel(con.get())
  798. return labels, con.get()
  799. }
  800. // kvmContainerLabels returns the default processLabel and mountLabel to be used
  801. // for kvm containers by the calling process.
  802. func kvmContainerLabels() (string, string) {
  803. processLabel := labels["kvm_process"]
  804. if processLabel == "" {
  805. processLabel = labels["process"]
  806. }
  807. return addMcs(processLabel, labels["file"])
  808. }
  809. // initContainerLabels returns the default processLabel and file labels to be
  810. // used for containers running an init system like systemd by the calling process.
  811. func initContainerLabels() (string, string) {
  812. processLabel := labels["init_process"]
  813. if processLabel == "" {
  814. processLabel = labels["process"]
  815. }
  816. return addMcs(processLabel, labels["file"])
  817. }
  818. // containerLabels returns an allocated processLabel and fileLabel to be used for
  819. // container labeling by the calling process.
  820. func containerLabels() (processLabel string, fileLabel string) {
  821. if !getEnabled() {
  822. return "", ""
  823. }
  824. processLabel = labels["process"]
  825. fileLabel = labels["file"]
  826. readOnlyFileLabel = labels["ro_file"]
  827. if processLabel == "" || fileLabel == "" {
  828. return "", fileLabel
  829. }
  830. if readOnlyFileLabel == "" {
  831. readOnlyFileLabel = fileLabel
  832. }
  833. return addMcs(processLabel, fileLabel)
  834. }
  835. func addMcs(processLabel, fileLabel string) (string, string) {
  836. scon, _ := NewContext(processLabel)
  837. if scon["level"] != "" {
  838. mcs := uniqMcs(CategoryRange)
  839. scon["level"] = mcs
  840. processLabel = scon.Get()
  841. scon, _ = NewContext(fileLabel)
  842. scon["level"] = mcs
  843. fileLabel = scon.Get()
  844. }
  845. return processLabel, fileLabel
  846. }
  847. // securityCheckContext validates that the SELinux label is understood by the kernel
  848. func securityCheckContext(val string) error {
  849. return ioutil.WriteFile(path.Join(getSelinuxMountPoint(), "context"), []byte(val), 0644)
  850. }
  851. // copyLevel returns a label with the MLS/MCS level from src label replaced on
  852. // the dest label.
  853. func copyLevel(src, dest string) (string, error) {
  854. if src == "" {
  855. return "", nil
  856. }
  857. if err := SecurityCheckContext(src); err != nil {
  858. return "", err
  859. }
  860. if err := SecurityCheckContext(dest); err != nil {
  861. return "", err
  862. }
  863. scon, err := NewContext(src)
  864. if err != nil {
  865. return "", err
  866. }
  867. tcon, err := NewContext(dest)
  868. if err != nil {
  869. return "", err
  870. }
  871. mcsDelete(tcon["level"])
  872. _ = mcsAdd(scon["level"])
  873. tcon["level"] = scon["level"]
  874. return tcon.Get(), nil
  875. }
  876. // Prevent users from relabeling system files
  877. func badPrefix(fpath string) error {
  878. if fpath == "" {
  879. return ErrEmptyPath
  880. }
  881. badPrefixes := []string{"/usr"}
  882. for _, prefix := range badPrefixes {
  883. if strings.HasPrefix(fpath, prefix) {
  884. return errors.Errorf("relabeling content in %s is not allowed", prefix)
  885. }
  886. }
  887. return nil
  888. }
  889. // chcon changes the fpath file object to the SELinux label label.
  890. // If fpath is a directory and recurse is true, then chcon walks the
  891. // directory tree setting the label.
  892. func chcon(fpath string, label string, recurse bool) error {
  893. if fpath == "" {
  894. return ErrEmptyPath
  895. }
  896. if label == "" {
  897. return nil
  898. }
  899. if err := badPrefix(fpath); err != nil {
  900. return err
  901. }
  902. if !recurse {
  903. return SetFileLabel(fpath, label)
  904. }
  905. return pwalk.Walk(fpath, func(p string, info os.FileInfo, err error) error {
  906. e := SetFileLabel(p, label)
  907. // Walk a file tree can race with removal, so ignore ENOENT
  908. if os.IsNotExist(errors.Cause(e)) {
  909. return nil
  910. }
  911. return e
  912. })
  913. }
  914. // dupSecOpt takes an SELinux process label and returns security options that
  915. // can be used to set the SELinux Type and Level for future container processes.
  916. func dupSecOpt(src string) ([]string, error) {
  917. if src == "" {
  918. return nil, nil
  919. }
  920. con, err := NewContext(src)
  921. if err != nil {
  922. return nil, err
  923. }
  924. if con["user"] == "" ||
  925. con["role"] == "" ||
  926. con["type"] == "" {
  927. return nil, nil
  928. }
  929. dup := []string{"user:" + con["user"],
  930. "role:" + con["role"],
  931. "type:" + con["type"],
  932. }
  933. if con["level"] != "" {
  934. dup = append(dup, "level:"+con["level"])
  935. }
  936. return dup, nil
  937. }
  938. // disableSecOpt returns a security opt that can be used to disable SELinux
  939. // labeling support for future container processes.
  940. func disableSecOpt() []string {
  941. return []string{"disable"}
  942. }
  943. // findUserInContext scans the reader for a valid SELinux context
  944. // match that is verified with the verifier. Invalid contexts are
  945. // skipped. It returns a matched context or an empty string if no
  946. // match is found. If a scanner error occurs, it is returned.
  947. func findUserInContext(context Context, r io.Reader, verifier func(string) error) (string, error) {
  948. fromRole := context["role"]
  949. fromType := context["type"]
  950. scanner := bufio.NewScanner(r)
  951. for scanner.Scan() {
  952. fromConns := strings.Fields(scanner.Text())
  953. if len(fromConns) == 0 {
  954. // Skip blank lines
  955. continue
  956. }
  957. line := fromConns[0]
  958. if line[0] == ';' || line[0] == '#' {
  959. // Skip comments
  960. continue
  961. }
  962. // user context files contexts are formatted as
  963. // role_r:type_t:s0 where the user is missing.
  964. lineArr := strings.SplitN(line, ":", 4)
  965. // skip context with typo, or role and type do not match
  966. if len(lineArr) != 3 ||
  967. lineArr[0] != fromRole ||
  968. lineArr[1] != fromType {
  969. continue
  970. }
  971. for _, cc := range fromConns[1:] {
  972. toConns := strings.SplitN(cc, ":", 4)
  973. if len(toConns) != 3 {
  974. continue
  975. }
  976. context["role"] = toConns[0]
  977. context["type"] = toConns[1]
  978. outConn := context.get()
  979. if err := verifier(outConn); err != nil {
  980. continue
  981. }
  982. return outConn, nil
  983. }
  984. }
  985. if err := scanner.Err(); err != nil {
  986. return "", errors.Wrap(err, "failed to scan for context")
  987. }
  988. return "", nil
  989. }
  990. func getDefaultContextFromReaders(c *defaultSECtx) (string, error) {
  991. if c.verifier == nil {
  992. return "", ErrVerifierNil
  993. }
  994. context, err := newContext(c.scon)
  995. if err != nil {
  996. return "", errors.Wrapf(err, "failed to create label for %s", c.scon)
  997. }
  998. // set so the verifier validates the matched context with the provided user and level.
  999. context["user"] = c.user
  1000. context["level"] = c.level
  1001. conn, err := findUserInContext(context, c.userRdr, c.verifier)
  1002. if err != nil {
  1003. return "", err
  1004. }
  1005. if conn != "" {
  1006. return conn, nil
  1007. }
  1008. conn, err = findUserInContext(context, c.defaultRdr, c.verifier)
  1009. if err != nil {
  1010. return "", err
  1011. }
  1012. if conn != "" {
  1013. return conn, nil
  1014. }
  1015. return "", errors.Wrapf(ErrContextMissing, "context not found: %q", c.scon)
  1016. }
  1017. func getDefaultContextWithLevel(user, level, scon string) (string, error) {
  1018. userPath := filepath.Join(policyRoot, selinuxUsersDir, user)
  1019. defaultPath := filepath.Join(policyRoot, defaultContexts)
  1020. fu, err := os.Open(userPath)
  1021. if err != nil {
  1022. return "", err
  1023. }
  1024. defer fu.Close()
  1025. fd, err := os.Open(defaultPath)
  1026. if err != nil {
  1027. return "", err
  1028. }
  1029. defer fd.Close()
  1030. c := defaultSECtx{
  1031. user: user,
  1032. level: level,
  1033. scon: scon,
  1034. userRdr: fu,
  1035. defaultRdr: fd,
  1036. verifier: securityCheckContext,
  1037. }
  1038. return getDefaultContextFromReaders(&c)
  1039. }