selinux_linux.go 28 KB

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