selinux_linux.go 27 KB

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