selinux.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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("/proc/self/attr/fscreate", scon)
  132. }
  133. func Getfscreatecon() (string, error) {
  134. return readCon("/proc/self/attr/fscreate")
  135. }
  136. func getcon() (string, error) {
  137. return readCon("/proc/self/attr/current")
  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 SelinuxGetEnforce() int {
  179. var enforce int
  180. enforceS, err := readCon(fmt.Sprintf("%s/enforce", selinuxPath))
  181. if err != nil {
  182. return -1
  183. }
  184. enforce, err = strconv.Atoi(string(enforceS))
  185. if err != nil {
  186. return -1
  187. }
  188. return enforce
  189. }
  190. func SelinuxGetEnforceMode() int {
  191. switch readConfig(selinuxTag) {
  192. case "enforcing":
  193. return Enforcing
  194. case "permissive":
  195. return Permissive
  196. }
  197. return Disabled
  198. }
  199. func mcsAdd(mcs string) {
  200. mcsList[mcs] = true
  201. }
  202. func mcsDelete(mcs string) {
  203. mcsList[mcs] = false
  204. }
  205. func mcsExists(mcs string) bool {
  206. return mcsList[mcs]
  207. }
  208. func IntToMcs(id int, catRange uint32) string {
  209. var (
  210. SETSIZE = int(catRange)
  211. TIER = SETSIZE
  212. ORD = id
  213. )
  214. if id < 1 || id > 523776 {
  215. return ""
  216. }
  217. for ORD > TIER {
  218. ORD = ORD - TIER
  219. TIER -= 1
  220. }
  221. TIER = SETSIZE - TIER
  222. ORD = ORD + TIER
  223. return fmt.Sprintf("s0:c%d,c%d", TIER, ORD)
  224. }
  225. func uniqMcs(catRange uint32) string {
  226. var (
  227. n uint32
  228. c1, c2 uint32
  229. mcs string
  230. )
  231. for {
  232. binary.Read(rand.Reader, binary.LittleEndian, &n)
  233. c1 = n % catRange
  234. binary.Read(rand.Reader, binary.LittleEndian, &n)
  235. c2 = n % catRange
  236. if c1 == c2 {
  237. continue
  238. } else {
  239. if c1 > c2 {
  240. t := c1
  241. c1 = c2
  242. c2 = t
  243. }
  244. }
  245. mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2)
  246. if mcsExists(mcs) {
  247. continue
  248. }
  249. mcsAdd(mcs)
  250. break
  251. }
  252. return mcs
  253. }
  254. func GetLxcContexts() (processLabel string, fileLabel string) {
  255. var (
  256. val, key string
  257. bufin *bufio.Reader
  258. )
  259. if !SelinuxEnabled() {
  260. return "", ""
  261. }
  262. lxcPath := fmt.Sprintf("%s/contexts/lxc_contexts", getSELinuxPolicyRoot())
  263. in, err := os.Open(lxcPath)
  264. if err != nil {
  265. return "", ""
  266. }
  267. defer in.Close()
  268. bufin = bufio.NewReader(in)
  269. for done := false; !done; {
  270. var line string
  271. if line, err = bufin.ReadString('\n'); err != nil {
  272. if err == io.EOF {
  273. done = true
  274. } else {
  275. goto exit
  276. }
  277. }
  278. line = strings.TrimSpace(line)
  279. if len(line) == 0 {
  280. // Skip blank lines
  281. continue
  282. }
  283. if line[0] == ';' || line[0] == '#' {
  284. // Skip comments
  285. continue
  286. }
  287. if groups := assignRegex.FindStringSubmatch(line); groups != nil {
  288. key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
  289. if key == "process" {
  290. processLabel = strings.Trim(val, "\"")
  291. }
  292. if key == "file" {
  293. fileLabel = strings.Trim(val, "\"")
  294. }
  295. }
  296. }
  297. if processLabel == "" || fileLabel == "" {
  298. return "", ""
  299. }
  300. exit:
  301. mcs := IntToMcs(os.Getpid(), 1024)
  302. scon := NewContext(processLabel)
  303. scon["level"] = mcs
  304. processLabel = scon.Get()
  305. scon = NewContext(fileLabel)
  306. scon["level"] = mcs
  307. fileLabel = scon.Get()
  308. return processLabel, fileLabel
  309. }
  310. func SecurityCheckContext(val string) error {
  311. return writeCon(fmt.Sprintf("%s.context", selinuxPath), val)
  312. }
  313. func CopyLevel(src, dest string) (string, error) {
  314. if !SelinuxEnabled() {
  315. return "", nil
  316. }
  317. if src == "" {
  318. return "", nil
  319. }
  320. if err := SecurityCheckContext(src); err != nil {
  321. return "", err
  322. }
  323. if err := SecurityCheckContext(dest); err != nil {
  324. return "", err
  325. }
  326. scon := NewContext(src)
  327. tcon := NewContext(dest)
  328. tcon["level"] = scon["level"]
  329. return tcon.Get(), nil
  330. }