123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- // +build linux
- package selinux
- import (
- "bufio"
- "crypto/rand"
- "encoding/binary"
- "fmt"
- "io"
- "os"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
- "sync"
- "syscall"
- "github.com/docker/docker/pkg/mount"
- "github.com/opencontainers/runc/libcontainer/system"
- )
- const (
- Enforcing = 1
- Permissive = 0
- Disabled = -1
- selinuxDir = "/etc/selinux/"
- selinuxConfig = selinuxDir + "config"
- selinuxTypeTag = "SELINUXTYPE"
- selinuxTag = "SELINUX"
- selinuxPath = "/sys/fs/selinux"
- xattrNameSelinux = "security.selinux"
- stRdOnly = 0x01
- )
- var (
- assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`)
- mcsList = make(map[string]bool)
- mcsLock sync.Mutex
- selinuxfs = "unknown"
- selinuxEnabled = false // Stores whether selinux is currently enabled
- selinuxEnabledChecked = false // Stores whether selinux enablement has been checked or established yet
- )
- type SELinuxContext map[string]string
- // SetDisabled disables selinux support for the package
- func SetDisabled() {
- selinuxEnabled, selinuxEnabledChecked = false, true
- }
- // getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs
- // filesystem or an empty string if no mountpoint is found. Selinuxfs is
- // a proc-like pseudo-filesystem that exposes the selinux policy API to
- // processes. The existence of an selinuxfs mount is used to determine
- // whether selinux is currently enabled or not.
- func getSelinuxMountPoint() string {
- if selinuxfs != "unknown" {
- return selinuxfs
- }
- selinuxfs = ""
- mounts, err := mount.GetMounts()
- if err != nil {
- return selinuxfs
- }
- for _, mount := range mounts {
- if mount.Fstype == "selinuxfs" {
- selinuxfs = mount.Mountpoint
- break
- }
- }
- if selinuxfs != "" {
- var buf syscall.Statfs_t
- syscall.Statfs(selinuxfs, &buf)
- if (buf.Flags & stRdOnly) == 1 {
- selinuxfs = ""
- }
- }
- return selinuxfs
- }
- // SelinuxEnabled returns whether selinux is currently enabled.
- func SelinuxEnabled() bool {
- if selinuxEnabledChecked {
- return selinuxEnabled
- }
- selinuxEnabledChecked = true
- if fs := getSelinuxMountPoint(); fs != "" {
- if con, _ := Getcon(); con != "kernel" {
- selinuxEnabled = true
- }
- }
- return selinuxEnabled
- }
- func readConfig(target string) (value string) {
- var (
- val, key string
- bufin *bufio.Reader
- )
- in, err := os.Open(selinuxConfig)
- if err != nil {
- return ""
- }
- defer in.Close()
- bufin = bufio.NewReader(in)
- for done := false; !done; {
- var line string
- if line, err = bufin.ReadString('\n'); err != nil {
- if err != io.EOF {
- return ""
- }
- done = true
- }
- line = strings.TrimSpace(line)
- if len(line) == 0 {
- // Skip blank lines
- continue
- }
- if line[0] == ';' || line[0] == '#' {
- // Skip comments
- continue
- }
- if groups := assignRegex.FindStringSubmatch(line); groups != nil {
- key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
- if key == target {
- return strings.Trim(val, "\"")
- }
- }
- }
- return ""
- }
- func getSELinuxPolicyRoot() string {
- return selinuxDir + readConfig(selinuxTypeTag)
- }
- func readCon(name string) (string, error) {
- var val string
- in, err := os.Open(name)
- if err != nil {
- return "", err
- }
- defer in.Close()
- _, err = fmt.Fscanf(in, "%s", &val)
- return val, err
- }
- // Setfilecon sets the SELinux label for this path or returns an error.
- func Setfilecon(path string, scon string) error {
- return system.Lsetxattr(path, xattrNameSelinux, []byte(scon), 0)
- }
- // Getfilecon returns the SELinux label for this path or returns an error.
- func Getfilecon(path string) (string, error) {
- con, err := system.Lgetxattr(path, xattrNameSelinux)
- if err != nil {
- return "", err
- }
- // Trim the NUL byte at the end of the byte buffer, if present.
- if len(con) > 0 && con[len(con)-1] == '\x00' {
- con = con[:len(con)-1]
- }
- return string(con), nil
- }
- func Setfscreatecon(scon string) error {
- return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()), scon)
- }
- func Getfscreatecon() (string, error) {
- return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()))
- }
- // Getcon returns the SELinux label of the current process thread, or an error.
- func Getcon() (string, error) {
- return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid()))
- }
- // Getpidcon returns the SELinux label of the given pid, or an error.
- func Getpidcon(pid int) (string, error) {
- return readCon(fmt.Sprintf("/proc/%d/attr/current", pid))
- }
- func Getexeccon() (string, error) {
- return readCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()))
- }
- func writeCon(name string, val string) error {
- out, err := os.OpenFile(name, os.O_WRONLY, 0)
- if err != nil {
- return err
- }
- defer out.Close()
- if val != "" {
- _, err = out.Write([]byte(val))
- } else {
- _, err = out.Write(nil)
- }
- return err
- }
- func Setexeccon(scon string) error {
- return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), scon)
- }
- func (c SELinuxContext) Get() string {
- return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"])
- }
- func NewContext(scon string) SELinuxContext {
- c := make(SELinuxContext)
- if len(scon) != 0 {
- con := strings.SplitN(scon, ":", 4)
- c["user"] = con[0]
- c["role"] = con[1]
- c["type"] = con[2]
- c["level"] = con[3]
- }
- return c
- }
- func ReserveLabel(scon string) {
- if len(scon) != 0 {
- con := strings.SplitN(scon, ":", 4)
- mcsAdd(con[3])
- }
- }
- func selinuxEnforcePath() string {
- return fmt.Sprintf("%s/enforce", selinuxPath)
- }
- func SelinuxGetEnforce() int {
- var enforce int
- enforceS, err := readCon(selinuxEnforcePath())
- if err != nil {
- return -1
- }
- enforce, err = strconv.Atoi(string(enforceS))
- if err != nil {
- return -1
- }
- return enforce
- }
- func SelinuxSetEnforce(mode int) error {
- return writeCon(selinuxEnforcePath(), fmt.Sprintf("%d", mode))
- }
- func SelinuxGetEnforceMode() int {
- switch readConfig(selinuxTag) {
- case "enforcing":
- return Enforcing
- case "permissive":
- return Permissive
- }
- return Disabled
- }
- func mcsAdd(mcs string) error {
- mcsLock.Lock()
- defer mcsLock.Unlock()
- if mcsList[mcs] {
- return fmt.Errorf("MCS Label already exists")
- }
- mcsList[mcs] = true
- return nil
- }
- func mcsDelete(mcs string) {
- mcsLock.Lock()
- mcsList[mcs] = false
- mcsLock.Unlock()
- }
- func IntToMcs(id int, catRange uint32) string {
- var (
- SETSIZE = int(catRange)
- TIER = SETSIZE
- ORD = id
- )
- if id < 1 || id > 523776 {
- return ""
- }
- for ORD > TIER {
- ORD = ORD - TIER
- TIER -= 1
- }
- TIER = SETSIZE - TIER
- ORD = ORD + TIER
- return fmt.Sprintf("s0:c%d,c%d", TIER, ORD)
- }
- func uniqMcs(catRange uint32) string {
- var (
- n uint32
- c1, c2 uint32
- mcs string
- )
- for {
- binary.Read(rand.Reader, binary.LittleEndian, &n)
- c1 = n % catRange
- binary.Read(rand.Reader, binary.LittleEndian, &n)
- c2 = n % catRange
- if c1 == c2 {
- continue
- } else {
- if c1 > c2 {
- t := c1
- c1 = c2
- c2 = t
- }
- }
- mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2)
- if err := mcsAdd(mcs); err != nil {
- continue
- }
- break
- }
- return mcs
- }
- func FreeLxcContexts(scon string) {
- if len(scon) != 0 {
- con := strings.SplitN(scon, ":", 4)
- mcsDelete(con[3])
- }
- }
- func GetLxcContexts() (processLabel string, fileLabel string) {
- var (
- val, key string
- bufin *bufio.Reader
- )
- if !SelinuxEnabled() {
- return "", ""
- }
- lxcPath := fmt.Sprintf("%s/contexts/lxc_contexts", getSELinuxPolicyRoot())
- in, err := os.Open(lxcPath)
- if err != nil {
- return "", ""
- }
- defer in.Close()
- bufin = bufio.NewReader(in)
- for done := false; !done; {
- var line string
- if line, err = bufin.ReadString('\n'); err != nil {
- if err == io.EOF {
- done = true
- } else {
- goto exit
- }
- }
- line = strings.TrimSpace(line)
- if len(line) == 0 {
- // Skip blank lines
- continue
- }
- if line[0] == ';' || line[0] == '#' {
- // Skip comments
- continue
- }
- if groups := assignRegex.FindStringSubmatch(line); groups != nil {
- key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
- if key == "process" {
- processLabel = strings.Trim(val, "\"")
- }
- if key == "file" {
- fileLabel = strings.Trim(val, "\"")
- }
- }
- }
- if processLabel == "" || fileLabel == "" {
- return "", ""
- }
- exit:
- // mcs := IntToMcs(os.Getpid(), 1024)
- mcs := uniqMcs(1024)
- scon := NewContext(processLabel)
- scon["level"] = mcs
- processLabel = scon.Get()
- scon = NewContext(fileLabel)
- scon["level"] = mcs
- fileLabel = scon.Get()
- return processLabel, fileLabel
- }
- func SecurityCheckContext(val string) error {
- return writeCon(fmt.Sprintf("%s.context", selinuxPath), val)
- }
- func CopyLevel(src, dest string) (string, error) {
- if src == "" {
- return "", nil
- }
- if err := SecurityCheckContext(src); err != nil {
- return "", err
- }
- if err := SecurityCheckContext(dest); err != nil {
- return "", err
- }
- scon := NewContext(src)
- tcon := NewContext(dest)
- mcsDelete(tcon["level"])
- mcsAdd(scon["level"])
- tcon["level"] = scon["level"]
- return tcon.Get(), nil
- }
- // Prevent users from relabing system files
- func badPrefix(fpath string) error {
- var badprefixes = []string{"/usr"}
- for _, prefix := range badprefixes {
- if fpath == prefix || strings.HasPrefix(fpath, fmt.Sprintf("%s/", prefix)) {
- return fmt.Errorf("Relabeling content in %s is not allowed.", prefix)
- }
- }
- return nil
- }
- // Change the fpath file object to the SELinux label scon.
- // If the fpath is a directory and recurse is true Chcon will walk the
- // directory tree setting the label
- func Chcon(fpath string, scon string, recurse bool) error {
- if scon == "" {
- return nil
- }
- if err := badPrefix(fpath); err != nil {
- return err
- }
- callback := func(p string, info os.FileInfo, err error) error {
- return Setfilecon(p, scon)
- }
- if recurse {
- return filepath.Walk(fpath, callback)
- }
- return Setfilecon(fpath, scon)
- }
- // DupSecOpt takes an SELinux process label and returns security options that
- // can will set the SELinux Type and Level for future container processes
- func DupSecOpt(src string) []string {
- if src == "" {
- return nil
- }
- con := NewContext(src)
- if con["user"] == "" ||
- con["role"] == "" ||
- con["type"] == "" ||
- con["level"] == "" {
- return nil
- }
- return []string{"label:user:" + con["user"],
- "label:role:" + con["role"],
- "label:type:" + con["type"],
- "label:level:" + con["level"]}
- }
- // DisableSecOpt returns a security opt that can be used to disabling SELinux
- // labeling support for future container processes
- func DisableSecOpt() []string {
- return []string{"label:disable"}
- }
|