Vendor in latest go-selinux

This should fix the issue with iptables being denied execution on
container cleanup

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

Closes: #966
Approved by: mheon
This commit is contained in:
Daniel J Walsh
2018-06-19 10:41:49 -04:00
committed by Atomic Bot
parent 4ab054073d
commit aa1ccfb094
3 changed files with 208 additions and 83 deletions

View File

@ -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 b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd
github.com/opencontainers/selinux 6ccd0b50d53ae771fe5259ff7a4039110777aa2d
github.com/ostreedev/ostree-go master
github.com/pkg/errors v0.8.0
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2

View File

@ -87,9 +87,6 @@ func FormatMountLabel(src, mountLabel string) string {
// SetProcessLabel takes a process label and tells the kernel to assign the
// label to the next program executed by the current process.
func SetProcessLabel(processLabel string) error {
if processLabel == "" {
return nil
}
return selinux.SetExecLabel(processLabel)
}

View File

@ -4,10 +4,13 @@ package selinux
import (
"bufio"
"bytes"
"crypto/rand"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
@ -23,14 +26,16 @@ const (
// Permissive constant to indicate SELinux is in permissive mode
Permissive = 0
// Disabled constant to indicate SELinux is disabled
Disabled = -1
Disabled = -1
selinuxDir = "/etc/selinux/"
selinuxConfig = selinuxDir + "config"
selinuxfsMount = "/sys/fs/selinux"
selinuxTypeTag = "SELINUXTYPE"
selinuxTag = "SELINUX"
selinuxPath = "/sys/fs/selinux"
xattrNameSelinux = "security.selinux"
stRdOnly = 0x01
selinuxfsMagic = 0xf97cff8c
)
type selinuxState struct {
@ -43,7 +48,13 @@ type selinuxState struct {
}
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),
}
@ -91,6 +102,83 @@ func (s *selinuxState) setSELinuxfs(selinuxfs string) string {
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
@ -100,40 +188,7 @@ func (s *selinuxState) getSELinuxfs() string {
return selinuxfs
}
selinuxfs = ""
f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return selinuxfs
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
txt := scanner.Text()
// Safe as mountinfo encodes mountpoints with spaces as \040.
sepIdx := strings.Index(txt, " - ")
if sepIdx == -1 {
continue
}
if !strings.Contains(txt[sepIdx:], "selinuxfs") {
continue
}
fields := strings.Split(txt, " ")
if len(fields) < 5 {
continue
}
selinuxfs = fields[4]
break
}
if selinuxfs != "" {
var buf syscall.Statfs_t
syscall.Statfs(selinuxfs, &buf)
if (buf.Flags & stRdOnly) == 1 {
selinuxfs = ""
}
}
return s.setSELinuxfs(selinuxfs)
return s.setSELinuxfs(findSELinuxfs())
}
// getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs
@ -150,7 +205,7 @@ func GetEnabled() bool {
return state.getEnabled()
}
func readConfig(target string) (value string) {
func readConfig(target string) string {
var (
val, key string
bufin *bufio.Reader
@ -192,30 +247,42 @@ func readConfig(target string) (value string) {
}
func getSELinuxPolicyRoot() string {
return selinuxDir + readConfig(selinuxTypeTag)
return filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
}
func readCon(name string) (string, error) {
var val string
func readCon(fpath string) (string, error) {
if fpath == "" {
return "", ErrEmptyPath
}
in, err := os.Open(name)
in, err := os.Open(fpath)
if err != nil {
return "", err
}
defer in.Close()
_, err = fmt.Fscanf(in, "%s", &val)
return val, err
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(path string, label string) error {
return lsetxattr(path, xattrNameSelinux, []byte(label), 0)
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(path string) (string, error) {
label, err := lgetxattr(path, xattrNameSelinux)
func FileLabel(fpath string) (string, error) {
if fpath == "" {
return "", ErrEmptyPath
}
label, err := lgetxattr(fpath, xattrNameSelinux)
if err != nil {
return "", err
}
@ -260,8 +327,12 @@ func ExecLabel() (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)
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
}
@ -275,6 +346,37 @@ func writeCon(name string, val string) error {
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.
@ -285,7 +387,10 @@ func SetExecLabel(label string) error {
// Get returns the Context as a string
func (c Context) Get() string {
return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"])
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
@ -297,7 +402,9 @@ func NewContext(label string) Context {
c["user"] = con[0]
c["role"] = con[1]
c["type"] = con[2]
c["level"] = con[3]
if len(con) > 3 {
c["level"] = con[3]
}
}
return c
}
@ -306,12 +413,14 @@ func NewContext(label string) Context {
func ReserveLabel(label string) {
if len(label) != 0 {
con := strings.SplitN(label, ":", 4)
mcsAdd(con[3])
if len(con) > 3 {
mcsAdd(con[3])
}
}
}
func selinuxEnforcePath() string {
return fmt.Sprintf("%s/enforce", selinuxPath)
return fmt.Sprintf("%s/enforce", getSelinuxMountPoint())
}
// EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
@ -354,16 +463,22 @@ func DefaultEnforceMode() int {
}
func mcsAdd(mcs string) error {
if mcs == "" {
return nil
}
state.Lock()
defer state.Unlock()
if state.mcsList[mcs] {
return fmt.Errorf("MCS Label already exists")
return ErrMCSAlreadyExists
}
state.mcsList[mcs] = true
return nil
}
func mcsDelete(mcs string) {
if mcs == "" {
return
}
state.Lock()
defer state.Unlock()
state.mcsList[mcs] = false
@ -424,14 +539,14 @@ Allowing it to be used by another process.
func ReleaseLabel(label string) {
if len(label) != 0 {
con := strings.SplitN(label, ":", 4)
mcsDelete(con[3])
if len(con) > 3 {
mcsDelete(con[3])
}
}
}
var roFileLabel string
// ROFileLabel returns the specified SELinux readonly file label
func ROFileLabel() (fileLabel string) {
func ROFileLabel() string {
return roFileLabel
}
@ -497,23 +612,25 @@ func ContainerLabels() (processLabel string, fileLabel string) {
roFileLabel = fileLabel
}
exit:
mcs := uniqMcs(1024)
scon := NewContext(processLabel)
scon["level"] = mcs
processLabel = scon.Get()
scon = NewContext(fileLabel)
scon["level"] = mcs
fileLabel = scon.Get()
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", selinuxPath), val)
return writeCon(fmt.Sprintf("%s/context", getSelinuxMountPoint()), val)
}
/*
CopyLevel returns a label with the MLS/MCS level from src label replaces on
CopyLevel returns a label with the MLS/MCS level from src label replaced on
the dest label.
*/
func CopyLevel(src, dest string) (string, error) {
@ -536,20 +653,26 @@ func CopyLevel(src, dest string) (string, error) {
// Prevent users from relabing system files
func badPrefix(fpath string) error {
var badprefixes = []string{"/usr"}
if fpath == "" {
return ErrEmptyPath
}
for _, prefix := range badprefixes {
if fpath == prefix || strings.HasPrefix(fpath, fmt.Sprintf("%s/", prefix)) {
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 the fpath is a directory and recurse is true Chcon will walk the
// directory tree setting the label
// 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
}
@ -568,7 +691,7 @@ func Chcon(fpath string, label string, recurse bool) error {
}
// DupSecOpt takes an SELinux process label and returns security options that
// can will set the SELinux Type and Level for future container processes
// can be used to set the SELinux Type and Level for future container processes.
func DupSecOpt(src string) []string {
if src == "" {
return nil
@ -576,18 +699,23 @@ func DupSecOpt(src string) []string {
con := NewContext(src)
if con["user"] == "" ||
con["role"] == "" ||
con["type"] == "" ||
con["level"] == "" {
con["type"] == "" {
return nil
}
return []string{"user:" + con["user"],
dup := []string{"user:" + con["user"],
"role:" + con["role"],
"type:" + con["type"],
"level:" + con["level"]}
}
if con["level"] != "" {
dup = append(dup, "level:"+con["level"])
}
return dup
}
// DisableSecOpt returns a security opt that can be used to disabling SELinux
// labeling support for future container processes
// DisableSecOpt returns a security opt that can be used to disable SELinux
// labeling support for future container processes.
func DisableSecOpt() []string {
return []string{"disable"}
}