|
@@ -11,6 +11,7 @@ import (
|
|
|
"io"
|
|
|
"io/ioutil"
|
|
|
"os"
|
|
|
+ "path"
|
|
|
"path/filepath"
|
|
|
"regexp"
|
|
|
"strconv"
|
|
@@ -37,15 +38,14 @@ const (
|
|
|
selinuxTag = "SELINUX"
|
|
|
xattrNameSelinux = "security.selinux"
|
|
|
stRdOnly = 0x01
|
|
|
- selinuxfsMagic = 0xf97cff8c
|
|
|
)
|
|
|
|
|
|
type selinuxState struct {
|
|
|
- enabledSet bool
|
|
|
- enabled bool
|
|
|
- selinuxfsSet bool
|
|
|
- selinuxfs string
|
|
|
- mcsList map[string]bool
|
|
|
+ enabledSet bool
|
|
|
+ enabled bool
|
|
|
+ selinuxfsOnce sync.Once
|
|
|
+ selinuxfs string
|
|
|
+ mcsList map[string]bool
|
|
|
sync.Mutex
|
|
|
}
|
|
|
|
|
@@ -62,6 +62,10 @@ var (
|
|
|
state = selinuxState{
|
|
|
mcsList: make(map[string]bool),
|
|
|
}
|
|
|
+
|
|
|
+ // for attrPath()
|
|
|
+ attrPathOnce sync.Once
|
|
|
+ haveThreadSelf bool
|
|
|
)
|
|
|
|
|
|
// Context is a representation of the SELinux label broken into 4 parts
|
|
@@ -98,14 +102,6 @@ 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 {
|
|
@@ -118,7 +114,8 @@ func verifySELinuxfsMount(mnt string) bool {
|
|
|
}
|
|
|
return false
|
|
|
}
|
|
|
- if uint32(buf.Type) != uint32(selinuxfsMagic) {
|
|
|
+
|
|
|
+ if uint32(buf.Type) != uint32(unix.SELINUX_MAGIC) {
|
|
|
return false
|
|
|
}
|
|
|
if (buf.Flags & stRdOnly) != 0 {
|
|
@@ -166,33 +163,29 @@ func findSELinuxfs() string {
|
|
|
// 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()
|
|
|
+ txt := s.Bytes()
|
|
|
// The first field after - is fs type.
|
|
|
// Safe as spaces in mountpoints are encoded as \040
|
|
|
- if !strings.Contains(txt, " - selinuxfs ") {
|
|
|
+ if !bytes.Contains(txt, []byte(" - selinuxfs ")) {
|
|
|
continue
|
|
|
}
|
|
|
const mPos = 5 // mount point is 5th field
|
|
|
- fields := strings.SplitN(txt, " ", mPos+1)
|
|
|
+ fields := bytes.SplitN(txt, []byte(" "), mPos+1)
|
|
|
if len(fields) < mPos+1 {
|
|
|
continue
|
|
|
}
|
|
|
- return fields[mPos-1]
|
|
|
+ return string(fields[mPos-1])
|
|
|
}
|
|
|
|
|
|
return ""
|
|
|
}
|
|
|
|
|
|
func (s *selinuxState) getSELinuxfs() string {
|
|
|
- s.Lock()
|
|
|
- selinuxfs := s.selinuxfs
|
|
|
- selinuxfsSet := s.selinuxfsSet
|
|
|
- s.Unlock()
|
|
|
- if selinuxfsSet {
|
|
|
- return selinuxfs
|
|
|
- }
|
|
|
+ s.selinuxfsOnce.Do(func() {
|
|
|
+ s.selinuxfs = findSELinuxfs()
|
|
|
+ })
|
|
|
|
|
|
- return s.setSELinuxfs(findSELinuxfs())
|
|
|
+ return s.selinuxfs
|
|
|
}
|
|
|
|
|
|
// getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs
|
|
@@ -254,10 +247,17 @@ func getSELinuxPolicyRoot() string {
|
|
|
return filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
|
|
|
}
|
|
|
|
|
|
-func isProcHandle(fh *os.File) (bool, error) {
|
|
|
+func isProcHandle(fh *os.File) error {
|
|
|
var buf unix.Statfs_t
|
|
|
err := unix.Fstatfs(int(fh.Fd()), &buf)
|
|
|
- return buf.Type == unix.PROC_SUPER_MAGIC, err
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("statfs(%q) failed: %v", fh.Name(), err)
|
|
|
+ }
|
|
|
+ if buf.Type != unix.PROC_SUPER_MAGIC {
|
|
|
+ return fmt.Errorf("file %q is not on procfs", fh.Name())
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
func readCon(fpath string) (string, error) {
|
|
@@ -271,10 +271,8 @@ func readCon(fpath string) (string, error) {
|
|
|
}
|
|
|
defer in.Close()
|
|
|
|
|
|
- if ok, err := isProcHandle(in); err != nil {
|
|
|
+ if err := isProcHandle(in); err != nil {
|
|
|
return "", err
|
|
|
- } else if !ok {
|
|
|
- return "", fmt.Errorf("%s not on procfs", fpath)
|
|
|
}
|
|
|
|
|
|
var retval string
|
|
@@ -317,7 +315,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 writeAttr("fscreate", label)
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -325,12 +323,12 @@ 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 readAttr("fscreate")
|
|
|
}
|
|
|
|
|
|
// 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 readAttr("current")
|
|
|
}
|
|
|
|
|
|
// PidLabel returns the SELinux label of the given pid, or an error.
|
|
@@ -343,10 +341,10 @@ 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()))
|
|
|
+ return readAttr("exec")
|
|
|
}
|
|
|
|
|
|
-func writeCon(fpath string, val string) error {
|
|
|
+func writeCon(fpath, val string) error {
|
|
|
if fpath == "" {
|
|
|
return ErrEmptyPath
|
|
|
}
|
|
@@ -362,10 +360,8 @@ func writeCon(fpath string, val string) error {
|
|
|
}
|
|
|
defer out.Close()
|
|
|
|
|
|
- if ok, err := isProcHandle(out); err != nil {
|
|
|
+ if err := isProcHandle(out); err != nil {
|
|
|
return err
|
|
|
- } else if !ok {
|
|
|
- return fmt.Errorf("%s not on procfs", fpath)
|
|
|
}
|
|
|
|
|
|
if val != "" {
|
|
@@ -379,6 +375,32 @@ func writeCon(fpath string, val string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
+func attrPath(attr string) string {
|
|
|
+ // Linux >= 3.17 provides this
|
|
|
+ const threadSelfPrefix = "/proc/thread-self/attr"
|
|
|
+
|
|
|
+ attrPathOnce.Do(func() {
|
|
|
+ st, err := os.Stat(threadSelfPrefix)
|
|
|
+ if err == nil && st.Mode().IsDir() {
|
|
|
+ haveThreadSelf = true
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ if haveThreadSelf {
|
|
|
+ return path.Join(threadSelfPrefix, attr)
|
|
|
+ }
|
|
|
+
|
|
|
+ return path.Join("/proc/self/task/", strconv.Itoa(syscall.Gettid()), "/attr/", attr)
|
|
|
+}
|
|
|
+
|
|
|
+func readAttr(attr string) (string, error) {
|
|
|
+ return readCon(attrPath(attr))
|
|
|
+}
|
|
|
+
|
|
|
+func writeAttr(attr, val string) error {
|
|
|
+ return writeCon(attrPath(attr), val)
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
CanonicalizeContext takes a context string and writes it to the kernel
|
|
|
the function then returns the context that the kernel will use. This function
|
|
@@ -415,7 +437,7 @@ 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 writeAttr("exec", label)
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -423,18 +445,18 @@ SetTaskLabel sets the SELinux label for the current thread, or an error.
|
|
|
This requires the dyntransition permission.
|
|
|
*/
|
|
|
func SetTaskLabel(label string) error {
|
|
|
- return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid()), label)
|
|
|
+ return writeAttr("current", label)
|
|
|
}
|
|
|
|
|
|
// SetSocketLabel takes a process label and tells the kernel to assign the
|
|
|
// label to the next socket that gets created
|
|
|
func SetSocketLabel(label string) error {
|
|
|
- return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/sockcreate", syscall.Gettid()), label)
|
|
|
+ return writeAttr("sockcreate", label)
|
|
|
}
|
|
|
|
|
|
// SocketLabel retrieves the current socket label setting
|
|
|
func SocketLabel() (string, error) {
|
|
|
- return readCon(fmt.Sprintf("/proc/self/task/%d/attr/sockcreate", syscall.Gettid()))
|
|
|
+ return readAttr("sockcreate")
|
|
|
}
|
|
|
|
|
|
// PeerLabel retrieves the label of the client on the other side of a socket
|
|
@@ -449,7 +471,7 @@ func SetKeyLabel(label string) error {
|
|
|
if os.IsNotExist(err) {
|
|
|
return nil
|
|
|
}
|
|
|
- if label == "" && os.IsPermission(err) && !GetEnabled() {
|
|
|
+ if label == "" && os.IsPermission(err) {
|
|
|
return nil
|
|
|
}
|
|
|
return err
|
|
@@ -505,19 +527,18 @@ func ReserveLabel(label string) {
|
|
|
}
|
|
|
|
|
|
func selinuxEnforcePath() string {
|
|
|
- return fmt.Sprintf("%s/enforce", getSelinuxMountPoint())
|
|
|
+ return path.Join(getSelinuxMountPoint(), "enforce")
|
|
|
}
|
|
|
|
|
|
// EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
|
|
|
func EnforceMode() int {
|
|
|
var enforce int
|
|
|
|
|
|
- enforceS, err := readCon(selinuxEnforcePath())
|
|
|
+ enforceB, err := ioutil.ReadFile(selinuxEnforcePath())
|
|
|
if err != nil {
|
|
|
return -1
|
|
|
}
|
|
|
-
|
|
|
- enforce, err = strconv.Atoi(string(enforceS))
|
|
|
+ enforce, err = strconv.Atoi(string(enforceB))
|
|
|
if err != nil {
|
|
|
return -1
|
|
|
}
|
|
@@ -529,7 +550,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 ioutil.WriteFile(selinuxEnforcePath(), []byte(strconv.Itoa(mode)), 0644)
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -711,7 +732,7 @@ exit:
|
|
|
|
|
|
// 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 ioutil.WriteFile(path.Join(getSelinuxMountPoint(), "context"), []byte(val), 0644)
|
|
|
}
|
|
|
|
|
|
/*
|