selinux.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. package selinux
  2. import (
  3. "bufio"
  4. "crypto/rand"
  5. "encoding/binary"
  6. "fmt"
  7. "github.com/dotcloud/docker/pkg/mount"
  8. "github.com/dotcloud/docker/pkg/system"
  9. "io"
  10. "os"
  11. "regexp"
  12. "strconv"
  13. "strings"
  14. "syscall"
  15. )
  16. const (
  17. Enforcing = 1
  18. Permissive = 0
  19. Disabled = -1
  20. selinuxDir = "/etc/selinux/"
  21. selinuxConfig = selinuxDir + "config"
  22. selinuxTypeTag = "SELINUXTYPE"
  23. selinuxTag = "SELINUX"
  24. selinuxPath = "/sys/fs/selinux"
  25. xattrNameSelinux = "security.selinux"
  26. stRdOnly = 0x01
  27. )
  28. var (
  29. assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`)
  30. spaceRegex = regexp.MustCompile(`^([^=]+) (.*)$`)
  31. mcsList = make(map[string]bool)
  32. selinuxfs = "unknown"
  33. selinuxEnabled = false
  34. selinuxEnabledChecked = false
  35. )
  36. type SELinuxContext map[string]string
  37. // SetDisabled disables selinux support for the package
  38. func SetDisabled() {
  39. selinuxEnabled, selinuxEnabledChecked = false, true
  40. }
  41. func getSelinuxMountPoint() string {
  42. if selinuxfs != "unknown" {
  43. return selinuxfs
  44. }
  45. selinuxfs = ""
  46. mounts, err := mount.GetMounts()
  47. if err != nil {
  48. return selinuxfs
  49. }
  50. for _, mount := range mounts {
  51. if mount.Fstype == "selinuxfs" {
  52. selinuxfs = mount.Mountpoint
  53. break
  54. }
  55. }
  56. if selinuxfs != "" {
  57. var buf syscall.Statfs_t
  58. syscall.Statfs(selinuxfs, &buf)
  59. if (buf.Flags & stRdOnly) == 1 {
  60. selinuxfs = ""
  61. }
  62. }
  63. return selinuxfs
  64. }
  65. func SelinuxEnabled() bool {
  66. if selinuxEnabledChecked {
  67. return selinuxEnabled
  68. }
  69. selinuxEnabledChecked = true
  70. if fs := getSelinuxMountPoint(); fs != "" {
  71. if con, _ := getcon(); con != "kernel" {
  72. selinuxEnabled = true
  73. }
  74. }
  75. return selinuxEnabled
  76. }
  77. func readConfig(target string) (value string) {
  78. var (
  79. val, key string
  80. bufin *bufio.Reader
  81. )
  82. in, err := os.Open(selinuxConfig)
  83. if err != nil {
  84. return ""
  85. }
  86. defer in.Close()
  87. bufin = bufio.NewReader(in)
  88. for done := false; !done; {
  89. var line string
  90. if line, err = bufin.ReadString('\n'); err != nil {
  91. if err != io.EOF {
  92. return ""
  93. }
  94. done = true
  95. }
  96. line = strings.TrimSpace(line)
  97. if len(line) == 0 {
  98. // Skip blank lines
  99. continue
  100. }
  101. if line[0] == ';' || line[0] == '#' {
  102. // Skip comments
  103. continue
  104. }
  105. if groups := assignRegex.FindStringSubmatch(line); groups != nil {
  106. key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
  107. if key == target {
  108. return strings.Trim(val, "\"")
  109. }
  110. }
  111. }
  112. return ""
  113. }
  114. func getSELinuxPolicyRoot() string {
  115. return selinuxDir + readConfig(selinuxTypeTag)
  116. }
  117. func readCon(name string) (string, error) {
  118. var val string
  119. in, err := os.Open(name)
  120. if err != nil {
  121. return "", err
  122. }
  123. defer in.Close()
  124. _, err = fmt.Fscanf(in, "%s", &val)
  125. return val, err
  126. }
  127. func Setfilecon(path string, scon string) error {
  128. return system.Lsetxattr(path, xattrNameSelinux, []byte(scon), 0)
  129. }
  130. func Setfscreatecon(scon string) error {
  131. return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", system.Gettid()), scon)
  132. }
  133. func Getfscreatecon() (string, error) {
  134. return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", system.Gettid()))
  135. }
  136. func getcon() (string, error) {
  137. return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", system.Gettid()))
  138. }
  139. func Getpidcon(pid int) (string, error) {
  140. return readCon(fmt.Sprintf("/proc/%d/attr/current", pid))
  141. }
  142. func Getexeccon() (string, error) {
  143. return readCon("/proc/self/attr/exec")
  144. }
  145. func writeCon(name string, val string) error {
  146. if !SelinuxEnabled() {
  147. return nil
  148. }
  149. out, err := os.OpenFile(name, os.O_WRONLY, 0)
  150. if err != nil {
  151. return err
  152. }
  153. defer out.Close()
  154. if val != "" {
  155. _, err = out.Write([]byte(val))
  156. } else {
  157. _, err = out.Write(nil)
  158. }
  159. return err
  160. }
  161. func Setexeccon(scon string) error {
  162. return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", system.Gettid()), scon)
  163. }
  164. func (c SELinuxContext) Get() string {
  165. return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"])
  166. }
  167. func NewContext(scon string) SELinuxContext {
  168. c := make(SELinuxContext)
  169. if len(scon) != 0 {
  170. con := strings.SplitN(scon, ":", 4)
  171. c["user"] = con[0]
  172. c["role"] = con[1]
  173. c["type"] = con[2]
  174. c["level"] = con[3]
  175. }
  176. return c
  177. }
  178. func ReserveLabel(scon string) {
  179. if len(scon) != 0 {
  180. con := strings.SplitN(scon, ":", 4)
  181. mcsAdd(con[3])
  182. }
  183. }
  184. func SelinuxGetEnforce() int {
  185. var enforce int
  186. enforceS, err := readCon(fmt.Sprintf("%s/enforce", selinuxPath))
  187. if err != nil {
  188. return -1
  189. }
  190. enforce, err = strconv.Atoi(string(enforceS))
  191. if err != nil {
  192. return -1
  193. }
  194. return enforce
  195. }
  196. func SelinuxGetEnforceMode() int {
  197. switch readConfig(selinuxTag) {
  198. case "enforcing":
  199. return Enforcing
  200. case "permissive":
  201. return Permissive
  202. }
  203. return Disabled
  204. }
  205. func mcsAdd(mcs string) error {
  206. if mcsList[mcs] {
  207. return fmt.Errorf("MCS Label already exists")
  208. }
  209. mcsList[mcs] = true
  210. return nil
  211. }
  212. func mcsDelete(mcs string) {
  213. mcsList[mcs] = false
  214. }
  215. func mcsExists(mcs string) bool {
  216. return mcsList[mcs]
  217. }
  218. func IntToMcs(id int, catRange uint32) string {
  219. var (
  220. SETSIZE = int(catRange)
  221. TIER = SETSIZE
  222. ORD = id
  223. )
  224. if id < 1 || id > 523776 {
  225. return ""
  226. }
  227. for ORD > TIER {
  228. ORD = ORD - TIER
  229. TIER -= 1
  230. }
  231. TIER = SETSIZE - TIER
  232. ORD = ORD + TIER
  233. return fmt.Sprintf("s0:c%d,c%d", TIER, ORD)
  234. }
  235. func uniqMcs(catRange uint32) string {
  236. var (
  237. n uint32
  238. c1, c2 uint32
  239. mcs string
  240. )
  241. for {
  242. binary.Read(rand.Reader, binary.LittleEndian, &n)
  243. c1 = n % catRange
  244. binary.Read(rand.Reader, binary.LittleEndian, &n)
  245. c2 = n % catRange
  246. if c1 == c2 {
  247. continue
  248. } else {
  249. if c1 > c2 {
  250. t := c1
  251. c1 = c2
  252. c2 = t
  253. }
  254. }
  255. mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2)
  256. if err := mcsAdd(mcs); err != nil {
  257. continue
  258. }
  259. break
  260. }
  261. return mcs
  262. }
  263. func FreeLxcContexts(scon string) {
  264. if len(scon) != 0 {
  265. con := strings.SplitN(scon, ":", 4)
  266. mcsDelete(con[3])
  267. }
  268. }
  269. func GetLxcContexts() (processLabel string, fileLabel string) {
  270. var (
  271. val, key string
  272. bufin *bufio.Reader
  273. )
  274. if !SelinuxEnabled() {
  275. return "", ""
  276. }
  277. lxcPath := fmt.Sprintf("%s/contexts/lxc_contexts", getSELinuxPolicyRoot())
  278. in, err := os.Open(lxcPath)
  279. if err != nil {
  280. return "", ""
  281. }
  282. defer in.Close()
  283. bufin = bufio.NewReader(in)
  284. for done := false; !done; {
  285. var line string
  286. if line, err = bufin.ReadString('\n'); err != nil {
  287. if err == io.EOF {
  288. done = true
  289. } else {
  290. goto exit
  291. }
  292. }
  293. line = strings.TrimSpace(line)
  294. if len(line) == 0 {
  295. // Skip blank lines
  296. continue
  297. }
  298. if line[0] == ';' || line[0] == '#' {
  299. // Skip comments
  300. continue
  301. }
  302. if groups := assignRegex.FindStringSubmatch(line); groups != nil {
  303. key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
  304. if key == "process" {
  305. processLabel = strings.Trim(val, "\"")
  306. }
  307. if key == "file" {
  308. fileLabel = strings.Trim(val, "\"")
  309. }
  310. }
  311. }
  312. if processLabel == "" || fileLabel == "" {
  313. return "", ""
  314. }
  315. exit:
  316. // mcs := IntToMcs(os.Getpid(), 1024)
  317. mcs := uniqMcs(1024)
  318. scon := NewContext(processLabel)
  319. scon["level"] = mcs
  320. processLabel = scon.Get()
  321. scon = NewContext(fileLabel)
  322. scon["level"] = mcs
  323. fileLabel = scon.Get()
  324. return processLabel, fileLabel
  325. }
  326. func SecurityCheckContext(val string) error {
  327. return writeCon(fmt.Sprintf("%s.context", selinuxPath), val)
  328. }
  329. func CopyLevel(src, dest string) (string, error) {
  330. if !SelinuxEnabled() {
  331. return "", nil
  332. }
  333. if src == "" {
  334. return "", nil
  335. }
  336. if err := SecurityCheckContext(src); err != nil {
  337. return "", err
  338. }
  339. if err := SecurityCheckContext(dest); err != nil {
  340. return "", err
  341. }
  342. scon := NewContext(src)
  343. tcon := NewContext(dest)
  344. mcsDelete(tcon["level"])
  345. mcsAdd(scon["level"])
  346. tcon["level"] = scon["level"]
  347. return tcon.Get(), nil
  348. }