From 56133f7263337e9b2c9cfb1cfce35adc2366f52b Mon Sep 17 00:00:00 2001
From: Daniel J Walsh <dwalsh@redhat.com>
Date: Tue, 26 Jun 2018 10:48:41 -0400
Subject: Update the vendoring of github.com/opencontainers/selinux

THis should make libpod easier to build on non linux platforms.

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>

Closes: #1000
Approved by: mheon
---
 vendor.conf                                        |   2 +-
 .../opencontainers/selinux/go-selinux/selinux.go   | 582 +----------------
 .../selinux/go-selinux/selinux_linux.go            | 721 +++++++++++++++++++++
 3 files changed, 744 insertions(+), 561 deletions(-)
 create mode 100644 vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go

diff --git a/vendor.conf b/vendor.conf
index a2eaffa10..d9766a4b4 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -49,7 +49,7 @@ github.com/opencontainers/image-spec v1.0.0
 github.com/opencontainers/runc 6e15bc3f92fd4c58b3285e8f27eaeb6b22d62920
 github.com/opencontainers/runtime-spec v1.0.0
 github.com/opencontainers/runtime-tools 625e2322645b151a7cbb93a8b42920933e72167f
-github.com/opencontainers/selinux 6ccd0b50d53ae771fe5259ff7a4039110777aa2d
+github.com/opencontainers/selinux 55cb7f16be130730b52cb3bdcc9351efa231130a
 github.com/ostreedev/ostree-go master
 github.com/pkg/errors v0.8.0
 github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go
index 6068e84a5..6993cb388 100644
--- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go
+++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go
@@ -1,23 +1,9 @@
-// +build linux
+// +build !linux
 
 package selinux
 
 import (
-	"bufio"
-	"bytes"
-	"crypto/rand"
-	"encoding/binary"
 	"errors"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"regexp"
-	"strconv"
-	"strings"
-	"sync"
-	"syscall"
 )
 
 const (
@@ -27,270 +13,31 @@ const (
 	Permissive = 0
 	// Disabled constant to indicate SELinux is disabled
 	Disabled = -1
-
-	selinuxDir       = "/etc/selinux/"
-	selinuxConfig    = selinuxDir + "config"
-	selinuxfsMount   = "/sys/fs/selinux"
-	selinuxTypeTag   = "SELINUXTYPE"
-	selinuxTag       = "SELINUX"
-	xattrNameSelinux = "security.selinux"
-	stRdOnly         = 0x01
-	selinuxfsMagic   = 0xf97cff8c
 )
 
-type selinuxState struct {
-	enabledSet   bool
-	enabled      bool
-	selinuxfsSet bool
-	selinuxfs    string
-	mcsList      map[string]bool
-	sync.Mutex
-}
-
 var (
 	// ErrMCSAlreadyExists is returned when trying to allocate a duplicate MCS.
 	ErrMCSAlreadyExists = errors.New("MCS label already exists")
 	// ErrEmptyPath is returned when an empty path has been specified.
 	ErrEmptyPath = errors.New("empty path")
-
-	assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`)
-	roFileLabel string
-	state       = selinuxState{
-		mcsList: make(map[string]bool),
-	}
 )
 
 // Context is a representation of the SELinux label broken into 4 parts
 type Context map[string]string
 
-func (s *selinuxState) setEnable(enabled bool) bool {
-	s.Lock()
-	defer s.Unlock()
-	s.enabledSet = true
-	s.enabled = enabled
-	return s.enabled
-}
-
-func (s *selinuxState) getEnabled() bool {
-	s.Lock()
-	enabled := s.enabled
-	enabledSet := s.enabledSet
-	s.Unlock()
-	if enabledSet {
-		return enabled
-	}
-
-	enabled = false
-	if fs := getSelinuxMountPoint(); fs != "" {
-		if con, _ := CurrentLabel(); con != "kernel" {
-			enabled = true
-		}
-	}
-	return s.setEnable(enabled)
-}
-
 // SetDisabled disables selinux support for the package
 func SetDisabled() {
-	state.setEnable(false)
-}
-
-func (s *selinuxState) setSELinuxfs(selinuxfs string) string {
-	s.Lock()
-	defer s.Unlock()
-	s.selinuxfsSet = true
-	s.selinuxfs = selinuxfs
-	return s.selinuxfs
-}
-
-func verifySELinuxfsMount(mnt string) bool {
-	var buf syscall.Statfs_t
-	for {
-		err := syscall.Statfs(mnt, &buf)
-		if err == nil {
-			break
-		}
-		if err == syscall.EAGAIN {
-			continue
-		}
-		return false
-	}
-	if uint32(buf.Type) != uint32(selinuxfsMagic) {
-		return false
-	}
-	if (buf.Flags & stRdOnly) != 0 {
-		return false
-	}
-
-	return true
-}
-
-func findSELinuxfs() string {
-	// fast path: check the default mount first
-	if verifySELinuxfsMount(selinuxfsMount) {
-		return selinuxfsMount
-	}
-
-	// check if selinuxfs is available before going the slow path
-	fs, err := ioutil.ReadFile("/proc/filesystems")
-	if err != nil {
-		return ""
-	}
-	if !bytes.Contains(fs, []byte("\tselinuxfs\n")) {
-		return ""
-	}
-
-	// slow path: try to find among the mounts
-	f, err := os.Open("/proc/self/mountinfo")
-	if err != nil {
-		return ""
-	}
-	defer f.Close()
-
-	scanner := bufio.NewScanner(f)
-	for {
-		mnt := findSELinuxfsMount(scanner)
-		if mnt == "" { // error or not found
-			return ""
-		}
-		if verifySELinuxfsMount(mnt) {
-			return mnt
-		}
-	}
-}
-
-// findSELinuxfsMount returns a next selinuxfs mount point found,
-// if there is one, or an empty string in case of EOF or error.
-func findSELinuxfsMount(s *bufio.Scanner) string {
-	for s.Scan() {
-		txt := s.Text()
-		// The first field after - is fs type.
-		// Safe as spaces in mountpoints are encoded as \040
-		if !strings.Contains(txt, " - selinuxfs ") {
-			continue
-		}
-		const mPos = 5 // mount point is 5th field
-		fields := strings.SplitN(txt, " ", mPos+1)
-		if len(fields) < mPos+1 {
-			continue
-		}
-		return fields[mPos-1]
-	}
-
-	return ""
-}
-
-func (s *selinuxState) getSELinuxfs() string {
-	s.Lock()
-	selinuxfs := s.selinuxfs
-	selinuxfsSet := s.selinuxfsSet
-	s.Unlock()
-	if selinuxfsSet {
-		return selinuxfs
-	}
-
-	return s.setSELinuxfs(findSELinuxfs())
-}
-
-// 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 {
-	return state.getSELinuxfs()
-}
-
-// GetEnabled returns whether selinux is currently enabled.
-func GetEnabled() bool {
-	return state.getEnabled()
-}
-
-func readConfig(target string) 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 filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
-}
-
-func readCon(fpath string) (string, error) {
-	if fpath == "" {
-		return "", ErrEmptyPath
-	}
-
-	in, err := os.Open(fpath)
-	if err != nil {
-		return "", err
-	}
-	defer in.Close()
-
-	var retval string
-	if _, err := fmt.Fscanf(in, "%s", &retval); err != nil {
-		return "", err
-	}
-	return strings.Trim(retval, "\x00"), nil
+	return
 }
 
 // SetFileLabel sets the SELinux label for this path or returns an error.
 func SetFileLabel(fpath string, label string) error {
-	if fpath == "" {
-		return ErrEmptyPath
-	}
-	return lsetxattr(fpath, xattrNameSelinux, []byte(label), 0)
+	return nil
 }
 
 // FileLabel returns the SELinux label for this path or returns an error.
 func FileLabel(fpath string) (string, error) {
-	if fpath == "" {
-		return "", ErrEmptyPath
-	}
-
-	label, err := lgetxattr(fpath, xattrNameSelinux)
-	if err != nil {
-		return "", err
-	}
-	// Trim the NUL byte at the end of the byte buffer, if present.
-	if len(label) > 0 && label[len(label)-1] == '\x00' {
-		label = label[:len(label)-1]
-	}
-	return string(label), nil
+	return "", nil
 }
 
 /*
@@ -298,7 +45,7 @@ SetFSCreateLabel tells kernel the label to create all file system objects
 created by this task. Setting label="" to return to default.
 */
 func SetFSCreateLabel(label string) error {
-	return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()), label)
+	return nil
 }
 
 /*
@@ -306,17 +53,17 @@ FSCreateLabel returns the default label the kernel which the kernel is using
 for file system objects created by this task. "" indicates default.
 */
 func FSCreateLabel() (string, error) {
-	return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()))
+	return nil
 }
 
 // CurrentLabel returns the SELinux label of the current process thread, or an error.
 func CurrentLabel() (string, error) {
-	return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid()))
+	return "", nil
 }
 
 // PidLabel returns the SELinux label of the given pid, or an error.
 func PidLabel(pid int) (string, error) {
-	return readCon(fmt.Sprintf("/proc/%d/attr/current", pid))
+	return "", nil
 }
 
 /*
@@ -324,26 +71,7 @@ ExecLabel returns the SELinux label that the kernel will use for any programs
 that are executed by the current process thread, or an error.
 */
 func ExecLabel() (string, error) {
-	return readCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()))
-}
-
-func writeCon(fpath string, val string) error {
-	if fpath == "" {
-		return ErrEmptyPath
-	}
-
-	out, err := os.OpenFile(fpath, 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
+	return "", nil
 }
 
 /*
@@ -352,29 +80,7 @@ the function then returns the context that the kernel will use.  This function
 can be used to see if two contexts are equivalent
 */
 func CanonicalizeContext(val string) (string, error) {
-	return readWriteCon(filepath.Join(getSelinuxMountPoint(), "context"), val)
-}
-
-func readWriteCon(fpath string, val string) (string, error) {
-	if fpath == "" {
-		return "", ErrEmptyPath
-	}
-	f, err := os.OpenFile(fpath, os.O_RDWR, 0)
-	if err != nil {
-		return "", err
-	}
-	defer f.Close()
-
-	_, err = f.Write([]byte(val))
-	if err != nil {
-		return "", err
-	}
-
-	var retval string
-	if _, err := fmt.Fscanf(f, "%s", &retval); err != nil {
-		return "", err
-	}
-	return strings.Trim(retval, "\x00"), nil
+	return "", nil
 }
 
 /*
@@ -382,61 +88,28 @@ SetExecLabel sets the SELinux label that the kernel will use for any programs
 that are executed by the current process thread, or an error.
 */
 func SetExecLabel(label string) error {
-	return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), label)
+	return nil
 }
 
 // Get returns the Context as a string
 func (c Context) Get() string {
-	if c["level"] != "" {
-		return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"])
-	}
-	return fmt.Sprintf("%s:%s:%s", c["user"], c["role"], c["type"])
+	return ""
 }
 
 // NewContext creates a new Context struct from the specified label
 func NewContext(label string) Context {
 	c := make(Context)
-
-	if len(label) != 0 {
-		con := strings.SplitN(label, ":", 4)
-		c["user"] = con[0]
-		c["role"] = con[1]
-		c["type"] = con[2]
-		if len(con) > 3 {
-			c["level"] = con[3]
-		}
-	}
 	return c
 }
 
 // ReserveLabel reserves the MLS/MCS level component of the specified label
 func ReserveLabel(label string) {
-	if len(label) != 0 {
-		con := strings.SplitN(label, ":", 4)
-		if len(con) > 3 {
-			mcsAdd(con[3])
-		}
-	}
-}
-
-func selinuxEnforcePath() string {
-	return fmt.Sprintf("%s/enforce", getSelinuxMountPoint())
+	return
 }
 
 // EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
 func EnforceMode() 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
+	return Disabled
 }
 
 /*
@@ -444,7 +117,7 @@ SetEnforceMode sets the current SELinux mode Enforcing, Permissive.
 Disabled is not valid, since this needs to be set at boot time.
 */
 func SetEnforceMode(mode int) error {
-	return writeCon(selinuxEnforcePath(), fmt.Sprintf("%d", mode))
+	return nil
 }
 
 /*
@@ -453,101 +126,20 @@ Permissive or Disabled. Note this is is just the default at boot time.
 EnforceMode tells you the systems current mode.
 */
 func DefaultEnforceMode() int {
-	switch readConfig(selinuxTag) {
-	case "enforcing":
-		return Enforcing
-	case "permissive":
-		return Permissive
-	}
 	return Disabled
 }
 
-func mcsAdd(mcs string) error {
-	if mcs == "" {
-		return nil
-	}
-	state.Lock()
-	defer state.Unlock()
-	if state.mcsList[mcs] {
-		return ErrMCSAlreadyExists
-	}
-	state.mcsList[mcs] = true
-	return nil
-}
-
-func mcsDelete(mcs string) {
-	if mcs == "" {
-		return
-	}
-	state.Lock()
-	defer state.Unlock()
-	state.mcsList[mcs] = false
-}
-
-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--
-	}
-	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 {
-				c1, c2 = c2, c1
-			}
-		}
-		mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2)
-		if err := mcsAdd(mcs); err != nil {
-			continue
-		}
-		break
-	}
-	return mcs
-}
-
 /*
 ReleaseLabel will unreserve the MLS/MCS Level field of the specified label.
 Allowing it to be used by another process.
 */
 func ReleaseLabel(label string) {
-	if len(label) != 0 {
-		con := strings.SplitN(label, ":", 4)
-		if len(con) > 3 {
-			mcsDelete(con[3])
-		}
-	}
+	return
 }
 
 // ROFileLabel returns the specified SELinux readonly file label
 func ROFileLabel() string {
-	return roFileLabel
+	return ""
 }
 
 /*
@@ -555,78 +147,12 @@ ContainerLabels returns an allocated processLabel and fileLabel to be used for
 container labeling by the calling process.
 */
 func ContainerLabels() (processLabel string, fileLabel string) {
-	var (
-		val, key string
-		bufin    *bufio.Reader
-	)
-
-	if !GetEnabled() {
-		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 key == "ro_file" {
-				roFileLabel = strings.Trim(val, "\"")
-			}
-		}
-	}
-
-	if processLabel == "" || fileLabel == "" {
-		return "", ""
-	}
-
-	if roFileLabel == "" {
-		roFileLabel = fileLabel
-	}
-exit:
-	scon := NewContext(processLabel)
-	if scon["level"] != "" {
-		mcs := uniqMcs(1024)
-		scon["level"] = mcs
-		processLabel = scon.Get()
-		scon = NewContext(fileLabel)
-		scon["level"] = mcs
-		fileLabel = scon.Get()
-	}
-	return processLabel, fileLabel
+	return "", ""
 }
 
 // SecurityCheckContext validates that the SELinux label is understood by the kernel
 func SecurityCheckContext(val string) error {
-	return writeCon(fmt.Sprintf("%s/context", getSelinuxMountPoint()), val)
+	return nil
 }
 
 /*
@@ -634,84 +160,20 @@ CopyLevel returns a label with the MLS/MCS level from src label replaced on
 the dest label.
 */
 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 {
-	if fpath == "" {
-		return ErrEmptyPath
-	}
-
-	badPrefixes := []string{"/usr"}
-	for _, prefix := range badPrefixes {
-		if strings.HasPrefix(fpath, prefix) {
-			return fmt.Errorf("relabeling content in %s is not allowed", prefix)
-		}
-	}
-	return nil
+	return "", nil
 }
 
 // Chcon changes the `fpath` file object to the SELinux label `label`.
 // If `fpath` is a directory and `recurse`` is true, Chcon will walk the
 // directory tree setting the label.
 func Chcon(fpath string, label string, recurse bool) error {
-	if fpath == "" {
-		return ErrEmptyPath
-	}
-	if label == "" {
-		return nil
-	}
-	if err := badPrefix(fpath); err != nil {
-		return err
-	}
-	callback := func(p string, info os.FileInfo, err error) error {
-		return SetFileLabel(p, label)
-	}
-
-	if recurse {
-		return filepath.Walk(fpath, callback)
-	}
-
-	return SetFileLabel(fpath, label)
+	return nil
 }
 
 // DupSecOpt takes an SELinux process label and returns security options that
 // can be used to 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"] == "" {
-		return nil
-	}
-	dup := []string{"user:" + con["user"],
-		"role:" + con["role"],
-		"type:" + con["type"],
-	}
-
-	if con["level"] != "" {
-		dup = append(dup, "level:"+con["level"])
-	}
-
-	return dup
+	return nil
 }
 
 // DisableSecOpt returns a security opt that can be used to disable SELinux
diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
new file mode 100644
index 000000000..6068e84a5
--- /dev/null
+++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
@@ -0,0 +1,721 @@
+// +build linux
+
+package selinux
+
+import (
+	"bufio"
+	"bytes"
+	"crypto/rand"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strconv"
+	"strings"
+	"sync"
+	"syscall"
+)
+
+const (
+	// Enforcing constant indicate SELinux is in enforcing mode
+	Enforcing = 1
+	// Permissive constant to indicate SELinux is in permissive mode
+	Permissive = 0
+	// Disabled constant to indicate SELinux is disabled
+	Disabled = -1
+
+	selinuxDir       = "/etc/selinux/"
+	selinuxConfig    = selinuxDir + "config"
+	selinuxfsMount   = "/sys/fs/selinux"
+	selinuxTypeTag   = "SELINUXTYPE"
+	selinuxTag       = "SELINUX"
+	xattrNameSelinux = "security.selinux"
+	stRdOnly         = 0x01
+	selinuxfsMagic   = 0xf97cff8c
+)
+
+type selinuxState struct {
+	enabledSet   bool
+	enabled      bool
+	selinuxfsSet bool
+	selinuxfs    string
+	mcsList      map[string]bool
+	sync.Mutex
+}
+
+var (
+	// ErrMCSAlreadyExists is returned when trying to allocate a duplicate MCS.
+	ErrMCSAlreadyExists = errors.New("MCS label already exists")
+	// ErrEmptyPath is returned when an empty path has been specified.
+	ErrEmptyPath = errors.New("empty path")
+
+	assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`)
+	roFileLabel string
+	state       = selinuxState{
+		mcsList: make(map[string]bool),
+	}
+)
+
+// Context is a representation of the SELinux label broken into 4 parts
+type Context map[string]string
+
+func (s *selinuxState) setEnable(enabled bool) bool {
+	s.Lock()
+	defer s.Unlock()
+	s.enabledSet = true
+	s.enabled = enabled
+	return s.enabled
+}
+
+func (s *selinuxState) getEnabled() bool {
+	s.Lock()
+	enabled := s.enabled
+	enabledSet := s.enabledSet
+	s.Unlock()
+	if enabledSet {
+		return enabled
+	}
+
+	enabled = false
+	if fs := getSelinuxMountPoint(); fs != "" {
+		if con, _ := CurrentLabel(); con != "kernel" {
+			enabled = true
+		}
+	}
+	return s.setEnable(enabled)
+}
+
+// SetDisabled disables selinux support for the package
+func SetDisabled() {
+	state.setEnable(false)
+}
+
+func (s *selinuxState) setSELinuxfs(selinuxfs string) string {
+	s.Lock()
+	defer s.Unlock()
+	s.selinuxfsSet = true
+	s.selinuxfs = selinuxfs
+	return s.selinuxfs
+}
+
+func verifySELinuxfsMount(mnt string) bool {
+	var buf syscall.Statfs_t
+	for {
+		err := syscall.Statfs(mnt, &buf)
+		if err == nil {
+			break
+		}
+		if err == syscall.EAGAIN {
+			continue
+		}
+		return false
+	}
+	if uint32(buf.Type) != uint32(selinuxfsMagic) {
+		return false
+	}
+	if (buf.Flags & stRdOnly) != 0 {
+		return false
+	}
+
+	return true
+}
+
+func findSELinuxfs() string {
+	// fast path: check the default mount first
+	if verifySELinuxfsMount(selinuxfsMount) {
+		return selinuxfsMount
+	}
+
+	// check if selinuxfs is available before going the slow path
+	fs, err := ioutil.ReadFile("/proc/filesystems")
+	if err != nil {
+		return ""
+	}
+	if !bytes.Contains(fs, []byte("\tselinuxfs\n")) {
+		return ""
+	}
+
+	// slow path: try to find among the mounts
+	f, err := os.Open("/proc/self/mountinfo")
+	if err != nil {
+		return ""
+	}
+	defer f.Close()
+
+	scanner := bufio.NewScanner(f)
+	for {
+		mnt := findSELinuxfsMount(scanner)
+		if mnt == "" { // error or not found
+			return ""
+		}
+		if verifySELinuxfsMount(mnt) {
+			return mnt
+		}
+	}
+}
+
+// findSELinuxfsMount returns a next selinuxfs mount point found,
+// if there is one, or an empty string in case of EOF or error.
+func findSELinuxfsMount(s *bufio.Scanner) string {
+	for s.Scan() {
+		txt := s.Text()
+		// The first field after - is fs type.
+		// Safe as spaces in mountpoints are encoded as \040
+		if !strings.Contains(txt, " - selinuxfs ") {
+			continue
+		}
+		const mPos = 5 // mount point is 5th field
+		fields := strings.SplitN(txt, " ", mPos+1)
+		if len(fields) < mPos+1 {
+			continue
+		}
+		return fields[mPos-1]
+	}
+
+	return ""
+}
+
+func (s *selinuxState) getSELinuxfs() string {
+	s.Lock()
+	selinuxfs := s.selinuxfs
+	selinuxfsSet := s.selinuxfsSet
+	s.Unlock()
+	if selinuxfsSet {
+		return selinuxfs
+	}
+
+	return s.setSELinuxfs(findSELinuxfs())
+}
+
+// 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 {
+	return state.getSELinuxfs()
+}
+
+// GetEnabled returns whether selinux is currently enabled.
+func GetEnabled() bool {
+	return state.getEnabled()
+}
+
+func readConfig(target string) 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 filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
+}
+
+func readCon(fpath string) (string, error) {
+	if fpath == "" {
+		return "", ErrEmptyPath
+	}
+
+	in, err := os.Open(fpath)
+	if err != nil {
+		return "", err
+	}
+	defer in.Close()
+
+	var retval string
+	if _, err := fmt.Fscanf(in, "%s", &retval); err != nil {
+		return "", err
+	}
+	return strings.Trim(retval, "\x00"), nil
+}
+
+// SetFileLabel sets the SELinux label for this path or returns an error.
+func SetFileLabel(fpath string, label string) error {
+	if fpath == "" {
+		return ErrEmptyPath
+	}
+	return lsetxattr(fpath, xattrNameSelinux, []byte(label), 0)
+}
+
+// FileLabel returns the SELinux label for this path or returns an error.
+func FileLabel(fpath string) (string, error) {
+	if fpath == "" {
+		return "", ErrEmptyPath
+	}
+
+	label, err := lgetxattr(fpath, xattrNameSelinux)
+	if err != nil {
+		return "", err
+	}
+	// Trim the NUL byte at the end of the byte buffer, if present.
+	if len(label) > 0 && label[len(label)-1] == '\x00' {
+		label = label[:len(label)-1]
+	}
+	return string(label), nil
+}
+
+/*
+SetFSCreateLabel tells kernel the label to create all file system objects
+created by this task. Setting label="" to return to default.
+*/
+func SetFSCreateLabel(label string) error {
+	return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()), label)
+}
+
+/*
+FSCreateLabel returns the default label the kernel which the kernel is using
+for file system objects created by this task. "" indicates default.
+*/
+func FSCreateLabel() (string, error) {
+	return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()))
+}
+
+// CurrentLabel returns the SELinux label of the current process thread, or an error.
+func CurrentLabel() (string, error) {
+	return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid()))
+}
+
+// PidLabel returns the SELinux label of the given pid, or an error.
+func PidLabel(pid int) (string, error) {
+	return readCon(fmt.Sprintf("/proc/%d/attr/current", pid))
+}
+
+/*
+ExecLabel returns the SELinux label that the kernel will use for any programs
+that are executed by the current process thread, or an error.
+*/
+func ExecLabel() (string, error) {
+	return readCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()))
+}
+
+func writeCon(fpath string, val string) error {
+	if fpath == "" {
+		return ErrEmptyPath
+	}
+
+	out, err := os.OpenFile(fpath, 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
+}
+
+/*
+CanonicalizeContext takes a context string and writes it to the kernel
+the function then returns the context that the kernel will use.  This function
+can be used to see if two contexts are equivalent
+*/
+func CanonicalizeContext(val string) (string, error) {
+	return readWriteCon(filepath.Join(getSelinuxMountPoint(), "context"), val)
+}
+
+func readWriteCon(fpath string, val string) (string, error) {
+	if fpath == "" {
+		return "", ErrEmptyPath
+	}
+	f, err := os.OpenFile(fpath, os.O_RDWR, 0)
+	if err != nil {
+		return "", err
+	}
+	defer f.Close()
+
+	_, err = f.Write([]byte(val))
+	if err != nil {
+		return "", err
+	}
+
+	var retval string
+	if _, err := fmt.Fscanf(f, "%s", &retval); err != nil {
+		return "", err
+	}
+	return strings.Trim(retval, "\x00"), nil
+}
+
+/*
+SetExecLabel sets the SELinux label that the kernel will use for any programs
+that are executed by the current process thread, or an error.
+*/
+func SetExecLabel(label string) error {
+	return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), label)
+}
+
+// Get returns the Context as a string
+func (c Context) Get() string {
+	if c["level"] != "" {
+		return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"])
+	}
+	return fmt.Sprintf("%s:%s:%s", c["user"], c["role"], c["type"])
+}
+
+// NewContext creates a new Context struct from the specified label
+func NewContext(label string) Context {
+	c := make(Context)
+
+	if len(label) != 0 {
+		con := strings.SplitN(label, ":", 4)
+		c["user"] = con[0]
+		c["role"] = con[1]
+		c["type"] = con[2]
+		if len(con) > 3 {
+			c["level"] = con[3]
+		}
+	}
+	return c
+}
+
+// ReserveLabel reserves the MLS/MCS level component of the specified label
+func ReserveLabel(label string) {
+	if len(label) != 0 {
+		con := strings.SplitN(label, ":", 4)
+		if len(con) > 3 {
+			mcsAdd(con[3])
+		}
+	}
+}
+
+func selinuxEnforcePath() string {
+	return fmt.Sprintf("%s/enforce", getSelinuxMountPoint())
+}
+
+// EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
+func EnforceMode() 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
+}
+
+/*
+SetEnforceMode sets the current SELinux mode Enforcing, Permissive.
+Disabled is not valid, since this needs to be set at boot time.
+*/
+func SetEnforceMode(mode int) error {
+	return writeCon(selinuxEnforcePath(), fmt.Sprintf("%d", mode))
+}
+
+/*
+DefaultEnforceMode returns the systems default SELinux mode Enforcing,
+Permissive or Disabled. Note this is is just the default at boot time.
+EnforceMode tells you the systems current mode.
+*/
+func DefaultEnforceMode() int {
+	switch readConfig(selinuxTag) {
+	case "enforcing":
+		return Enforcing
+	case "permissive":
+		return Permissive
+	}
+	return Disabled
+}
+
+func mcsAdd(mcs string) error {
+	if mcs == "" {
+		return nil
+	}
+	state.Lock()
+	defer state.Unlock()
+	if state.mcsList[mcs] {
+		return ErrMCSAlreadyExists
+	}
+	state.mcsList[mcs] = true
+	return nil
+}
+
+func mcsDelete(mcs string) {
+	if mcs == "" {
+		return
+	}
+	state.Lock()
+	defer state.Unlock()
+	state.mcsList[mcs] = false
+}
+
+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--
+	}
+	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 {
+				c1, c2 = c2, c1
+			}
+		}
+		mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2)
+		if err := mcsAdd(mcs); err != nil {
+			continue
+		}
+		break
+	}
+	return mcs
+}
+
+/*
+ReleaseLabel will unreserve the MLS/MCS Level field of the specified label.
+Allowing it to be used by another process.
+*/
+func ReleaseLabel(label string) {
+	if len(label) != 0 {
+		con := strings.SplitN(label, ":", 4)
+		if len(con) > 3 {
+			mcsDelete(con[3])
+		}
+	}
+}
+
+// ROFileLabel returns the specified SELinux readonly file label
+func ROFileLabel() string {
+	return roFileLabel
+}
+
+/*
+ContainerLabels returns an allocated processLabel and fileLabel to be used for
+container labeling by the calling process.
+*/
+func ContainerLabels() (processLabel string, fileLabel string) {
+	var (
+		val, key string
+		bufin    *bufio.Reader
+	)
+
+	if !GetEnabled() {
+		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 key == "ro_file" {
+				roFileLabel = strings.Trim(val, "\"")
+			}
+		}
+	}
+
+	if processLabel == "" || fileLabel == "" {
+		return "", ""
+	}
+
+	if roFileLabel == "" {
+		roFileLabel = fileLabel
+	}
+exit:
+	scon := NewContext(processLabel)
+	if scon["level"] != "" {
+		mcs := uniqMcs(1024)
+		scon["level"] = mcs
+		processLabel = scon.Get()
+		scon = NewContext(fileLabel)
+		scon["level"] = mcs
+		fileLabel = scon.Get()
+	}
+	return processLabel, fileLabel
+}
+
+// SecurityCheckContext validates that the SELinux label is understood by the kernel
+func SecurityCheckContext(val string) error {
+	return writeCon(fmt.Sprintf("%s/context", getSelinuxMountPoint()), val)
+}
+
+/*
+CopyLevel returns a label with the MLS/MCS level from src label replaced on
+the dest label.
+*/
+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 {
+	if fpath == "" {
+		return ErrEmptyPath
+	}
+
+	badPrefixes := []string{"/usr"}
+	for _, prefix := range badPrefixes {
+		if strings.HasPrefix(fpath, prefix) {
+			return fmt.Errorf("relabeling content in %s is not allowed", prefix)
+		}
+	}
+	return nil
+}
+
+// Chcon changes the `fpath` file object to the SELinux label `label`.
+// If `fpath` is a directory and `recurse`` is true, Chcon will walk the
+// directory tree setting the label.
+func Chcon(fpath string, label string, recurse bool) error {
+	if fpath == "" {
+		return ErrEmptyPath
+	}
+	if label == "" {
+		return nil
+	}
+	if err := badPrefix(fpath); err != nil {
+		return err
+	}
+	callback := func(p string, info os.FileInfo, err error) error {
+		return SetFileLabel(p, label)
+	}
+
+	if recurse {
+		return filepath.Walk(fpath, callback)
+	}
+
+	return SetFileLabel(fpath, label)
+}
+
+// DupSecOpt takes an SELinux process label and returns security options that
+// can be used to 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"] == "" {
+		return nil
+	}
+	dup := []string{"user:" + con["user"],
+		"role:" + con["role"],
+		"type:" + con["type"],
+	}
+
+	if con["level"] != "" {
+		dup = append(dup, "level:"+con["level"])
+	}
+
+	return dup
+}
+
+// DisableSecOpt returns a security opt that can be used to disable SELinux
+// labeling support for future container processes.
+func DisableSecOpt() []string {
+	return []string{"disable"}
+}
-- 
cgit v1.2.3-54-g00ecf