full diff: https://github.com/opencontainers/selinux/compare/v1.6.0...v1.7.0
- Implement get_default_context_with_level() from libselinux
- Wrap some syscalls (lgetattr, lsetattr, fstatfs, statfs) to retry on EINTR.
- Improve code quality by turning fixing many problems found by linters
- Use bufio.Scanner for parsing labels and policy confilabelg
- Cache the value for SELinux policy directory
- test on ppc64le and go 1.15
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| ... | ... |
@@ -176,7 +176,7 @@ github.com/morikuni/aec 39771216ff4c63d11f5e604076f9 |
| 176 | 176 |
# metrics |
| 177 | 177 |
github.com/docker/go-metrics b619b3592b65de4f087d9f16863a7e6ff905973c # v0.0.1 |
| 178 | 178 |
|
| 179 |
-github.com/opencontainers/selinux 25504e34a9826d481f6e2903963ecaa881749124 # v1.6.0 |
|
| 179 |
+github.com/opencontainers/selinux 63ad55b76fd78d4c76c2f5491f68516e60c9d523 # v1.7.0 |
|
| 180 | 180 |
github.com/willf/bitset 559910e8471e48d76d9e5a1ba15842dee77ad45d # v1.1.11 |
| 181 | 181 |
|
| 182 | 182 |
|
| ... | ... |
@@ -18,5 +18,5 @@ Participation in the OpenContainers community is governed by [OpenContainer's Co |
| 18 | 18 |
|
| 19 | 19 |
If you find an issue, please follow the [security][security] protocol to report it. |
| 20 | 20 |
|
| 21 |
-[security]: https://github.com/opencontainers/org/blob/master/security |
|
| 21 |
+[security]: https://github.com/opencontainers/org/blob/master/SECURITY.md |
|
| 22 | 22 |
[code-of-conduct]: https://github.com/opencontainers/org/blob/master/CODE_OF_CONDUCT.md |
| ... | ... |
@@ -27,14 +27,14 @@ var ErrIncompatibleLabel = errors.New("Bad SELinux option z and Z can not be use
|
| 27 | 27 |
// the container. A list of options can be passed into this function to alter |
| 28 | 28 |
// the labels. The labels returned will include a random MCS String, that is |
| 29 | 29 |
// guaranteed to be unique. |
| 30 |
-func InitLabels(options []string) (plabel string, mlabel string, Err error) {
|
|
| 30 |
+func InitLabels(options []string) (plabel string, mlabel string, retErr error) {
|
|
| 31 | 31 |
if !selinux.GetEnabled() {
|
| 32 | 32 |
return "", "", nil |
| 33 | 33 |
} |
| 34 | 34 |
processLabel, mountLabel := selinux.ContainerLabels() |
| 35 | 35 |
if processLabel != "" {
|
| 36 | 36 |
defer func() {
|
| 37 |
- if Err != nil {
|
|
| 37 |
+ if retErr != nil {
|
|
| 38 | 38 |
selinux.ReleaseLabel(mountLabel) |
| 39 | 39 |
} |
| 40 | 40 |
}() |
| ... | ... |
@@ -57,7 +57,6 @@ func InitLabels(options []string) (plabel string, mlabel string, Err error) {
|
| 57 | 57 |
con := strings.SplitN(opt, ":", 2) |
| 58 | 58 |
if !validOptions[con[0]] {
|
| 59 | 59 |
return "", "", errors.Errorf("Bad label option %q, valid options 'disable, user, role, level, type, filetype'", con[0])
|
| 60 |
- |
|
| 61 | 60 |
} |
| 62 | 61 |
if con[0] == "filetype" {
|
| 63 | 62 |
mcon["type"] = con[1] |
| ... | ... |
@@ -30,6 +30,11 @@ var ( |
| 30 | 30 |
// ErrLevelSyntax is returned when a sensitivity or category do not have correct syntax in a level |
| 31 | 31 |
ErrLevelSyntax = errors.New("invalid level syntax")
|
| 32 | 32 |
|
| 33 |
+ // ErrContextMissing is returned if a requested context is not found in a file. |
|
| 34 |
+ ErrContextMissing = errors.New("context does not have a match")
|
|
| 35 |
+ // ErrVerifierNil is returned when a context verifier function is nil. |
|
| 36 |
+ ErrVerifierNil = errors.New("verifier function is nil")
|
|
| 37 |
+ |
|
| 33 | 38 |
// CategoryRange allows the upper bound on the category range to be adjusted |
| 34 | 39 |
CategoryRange = DefaultCategoryRange |
| 35 | 40 |
) |
| ... | ... |
@@ -63,8 +68,12 @@ func FileLabel(fpath string) (string, error) {
|
| 63 | 63 |
return fileLabel(fpath) |
| 64 | 64 |
} |
| 65 | 65 |
|
| 66 |
-// SetFSCreateLabel tells kernel the label to create all file system objects |
|
| 67 |
-// created by this task. Setting label="" to return to default. |
|
| 66 |
+// SetFSCreateLabel tells the kernel what label to use for all file system objects |
|
| 67 |
+// created by this task. |
|
| 68 |
+// Set the label to an empty string to return to the default label. Calls to SetFSCreateLabel |
|
| 69 |
+// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until file system |
|
| 70 |
+// objects created by this task are finished to guarantee another goroutine does not migrate |
|
| 71 |
+// to the current thread before execution is complete. |
|
| 68 | 72 |
func SetFSCreateLabel(label string) error {
|
| 69 | 73 |
return setFSCreateLabel(label) |
| 70 | 74 |
} |
| ... | ... |
@@ -113,19 +122,27 @@ func CalculateGlbLub(sourceRange, targetRange string) (string, error) {
|
| 113 | 113 |
} |
| 114 | 114 |
|
| 115 | 115 |
// SetExecLabel sets the SELinux label that the kernel will use for any programs |
| 116 |
-// that are executed by the current process thread, or an error. |
|
| 116 |
+// that are executed by the current process thread, or an error. Calls to SetExecLabel |
|
| 117 |
+// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until execution |
|
| 118 |
+// of the program is finished to guarantee another goroutine does not migrate to the current |
|
| 119 |
+// thread before execution is complete. |
|
| 117 | 120 |
func SetExecLabel(label string) error {
|
| 118 | 121 |
return setExecLabel(label) |
| 119 | 122 |
} |
| 120 | 123 |
|
| 121 | 124 |
// SetTaskLabel sets the SELinux label for the current thread, or an error. |
| 122 |
-// This requires the dyntransition permission. |
|
| 125 |
+// This requires the dyntransition permission. Calls to SetTaskLabel should |
|
| 126 |
+// be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() to guarantee |
|
| 127 |
+// the current thread does not run in a new mislabeled thread. |
|
| 123 | 128 |
func SetTaskLabel(label string) error {
|
| 124 | 129 |
return setTaskLabel(label) |
| 125 | 130 |
} |
| 126 | 131 |
|
| 127 | 132 |
// SetSocketLabel takes a process label and tells the kernel to assign the |
| 128 |
-// label to the next socket that gets created |
|
| 133 |
+// label to the next socket that gets created. Calls to SetSocketLabel |
|
| 134 |
+// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until |
|
| 135 |
+// the the socket is created to guarantee another goroutine does not migrate |
|
| 136 |
+// to the current thread before execution is complete. |
|
| 129 | 137 |
func SetSocketLabel(label string) error {
|
| 130 | 138 |
return setSocketLabel(label) |
| 131 | 139 |
} |
| ... | ... |
@@ -141,7 +158,10 @@ func PeerLabel(fd uintptr) (string, error) {
|
| 141 | 141 |
} |
| 142 | 142 |
|
| 143 | 143 |
// SetKeyLabel takes a process label and tells the kernel to assign the |
| 144 |
-// label to the next kernel keyring that gets created |
|
| 144 |
+// label to the next kernel keyring that gets created. Calls to SetKeyLabel |
|
| 145 |
+// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until |
|
| 146 |
+// the kernel keyring is created to guarantee another goroutine does not migrate |
|
| 147 |
+// to the current thread before execution is complete. |
|
| 145 | 148 |
func SetKeyLabel(label string) error {
|
| 146 | 149 |
return setKeyLabel(label) |
| 147 | 150 |
} |
| ... | ... |
@@ -247,3 +267,12 @@ func DupSecOpt(src string) ([]string, error) {
|
| 247 | 247 |
func DisableSecOpt() []string {
|
| 248 | 248 |
return disableSecOpt() |
| 249 | 249 |
} |
| 250 |
+ |
|
| 251 |
+// GetDefaultContextWithLevel gets a single context for the specified SELinux user |
|
| 252 |
+// identity that is reachable from the specified scon context. The context is based |
|
| 253 |
+// on the per-user /etc/selinux/{SELINUXTYPE}/contexts/users/<username> if it exists,
|
|
| 254 |
+// and falls back to the global /etc/selinux/{SELINUXTYPE}/contexts/default_contexts
|
|
| 255 |
+// file. |
|
| 256 |
+func GetDefaultContextWithLevel(user, level, scon string) (string, error) {
|
|
| 257 |
+ return getDefaultContextWithLevel(user, level, scon) |
|
| 258 |
+} |
| ... | ... |
@@ -28,6 +28,8 @@ const ( |
| 28 | 28 |
minSensLen = 2 |
| 29 | 29 |
contextFile = "/usr/share/containers/selinux/contexts" |
| 30 | 30 |
selinuxDir = "/etc/selinux/" |
| 31 |
+ selinuxUsersDir = "contexts/users" |
|
| 32 |
+ defaultContexts = "contexts/default_contexts" |
|
| 31 | 33 |
selinuxConfig = selinuxDir + "config" |
| 32 | 34 |
selinuxfsMount = "/sys/fs/selinux" |
| 33 | 35 |
selinuxTypeTag = "SELINUXTYPE" |
| ... | ... |
@@ -35,6 +37,8 @@ const ( |
| 35 | 35 |
xattrNameSelinux = "security.selinux" |
| 36 | 36 |
) |
| 37 | 37 |
|
| 38 |
+var policyRoot = filepath.Join(selinuxDir, readConfig(selinuxTypeTag)) |
|
| 39 |
+ |
|
| 38 | 40 |
type selinuxState struct {
|
| 39 | 41 |
enabledSet bool |
| 40 | 42 |
enabled bool |
| ... | ... |
@@ -54,6 +58,13 @@ type mlsRange struct {
|
| 54 | 54 |
high *level |
| 55 | 55 |
} |
| 56 | 56 |
|
| 57 |
+type defaultSECtx struct {
|
|
| 58 |
+ user, level, scon string |
|
| 59 |
+ userRdr, defaultRdr io.Reader |
|
| 60 |
+ |
|
| 61 |
+ verifier func(string) error |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 57 | 64 |
type levelItem byte |
| 58 | 65 |
|
| 59 | 66 |
const ( |
| ... | ... |
@@ -111,7 +122,7 @@ func verifySELinuxfsMount(mnt string) bool {
|
| 111 | 111 |
if err == nil {
|
| 112 | 112 |
break |
| 113 | 113 |
} |
| 114 |
- if err == unix.EAGAIN {
|
|
| 114 |
+ if err == unix.EAGAIN || err == unix.EINTR {
|
|
| 115 | 115 |
continue |
| 116 | 116 |
} |
| 117 | 117 |
return false |
| ... | ... |
@@ -205,28 +216,16 @@ func getEnabled() bool {
|
| 205 | 205 |
} |
| 206 | 206 |
|
| 207 | 207 |
func readConfig(target string) string {
|
| 208 |
- var ( |
|
| 209 |
- val, key string |
|
| 210 |
- bufin *bufio.Reader |
|
| 211 |
- ) |
|
| 212 |
- |
|
| 213 | 208 |
in, err := os.Open(selinuxConfig) |
| 214 | 209 |
if err != nil {
|
| 215 | 210 |
return "" |
| 216 | 211 |
} |
| 217 | 212 |
defer in.Close() |
| 218 | 213 |
|
| 219 |
- bufin = bufio.NewReader(in) |
|
| 214 |
+ scanner := bufio.NewScanner(in) |
|
| 220 | 215 |
|
| 221 |
- for done := false; !done; {
|
|
| 222 |
- var line string |
|
| 223 |
- if line, err = bufin.ReadString('\n'); err != nil {
|
|
| 224 |
- if err != io.EOF {
|
|
| 225 |
- return "" |
|
| 226 |
- } |
|
| 227 |
- done = true |
|
| 228 |
- } |
|
| 229 |
- line = strings.TrimSpace(line) |
|
| 216 |
+ for scanner.Scan() {
|
|
| 217 |
+ line := strings.TrimSpace(scanner.Text()) |
|
| 230 | 218 |
if len(line) == 0 {
|
| 231 | 219 |
// Skip blank lines |
| 232 | 220 |
continue |
| ... | ... |
@@ -236,7 +235,7 @@ func readConfig(target string) string {
|
| 236 | 236 |
continue |
| 237 | 237 |
} |
| 238 | 238 |
if groups := assignRegex.FindStringSubmatch(line); groups != nil {
|
| 239 |
- key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) |
|
| 239 |
+ key, val := strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) |
|
| 240 | 240 |
if key == target {
|
| 241 | 241 |
return strings.Trim(val, "\"") |
| 242 | 242 |
} |
| ... | ... |
@@ -245,15 +244,17 @@ func readConfig(target string) string {
|
| 245 | 245 |
return "" |
| 246 | 246 |
} |
| 247 | 247 |
|
| 248 |
-func getSELinuxPolicyRoot() string {
|
|
| 249 |
- return filepath.Join(selinuxDir, readConfig(selinuxTypeTag)) |
|
| 250 |
-} |
|
| 251 |
- |
|
| 252 | 248 |
func isProcHandle(fh *os.File) error {
|
| 253 | 249 |
var buf unix.Statfs_t |
| 254 |
- err := unix.Fstatfs(int(fh.Fd()), &buf) |
|
| 255 |
- if err != nil {
|
|
| 256 |
- return errors.Wrapf(err, "statfs(%q) failed", fh.Name()) |
|
| 250 |
+ |
|
| 251 |
+ for {
|
|
| 252 |
+ err := unix.Fstatfs(int(fh.Fd()), &buf) |
|
| 253 |
+ if err == nil {
|
|
| 254 |
+ break |
|
| 255 |
+ } |
|
| 256 |
+ if err != unix.EINTR {
|
|
| 257 |
+ return errors.Wrapf(err, "statfs(%q) failed", fh.Name()) |
|
| 258 |
+ } |
|
| 257 | 259 |
} |
| 258 | 260 |
if buf.Type != unix.PROC_SUPER_MAGIC {
|
| 259 | 261 |
return errors.Errorf("file %q is not on procfs", fh.Name())
|
| ... | ... |
@@ -307,9 +308,16 @@ func setFileLabel(fpath string, label string) error {
|
| 307 | 307 |
if fpath == "" {
|
| 308 | 308 |
return ErrEmptyPath |
| 309 | 309 |
} |
| 310 |
- if err := unix.Lsetxattr(fpath, xattrNameSelinux, []byte(label), 0); err != nil {
|
|
| 311 |
- return errors.Wrapf(err, "failed to set file label on %s", fpath) |
|
| 310 |
+ for {
|
|
| 311 |
+ err := unix.Lsetxattr(fpath, xattrNameSelinux, []byte(label), 0) |
|
| 312 |
+ if err == nil {
|
|
| 313 |
+ break |
|
| 314 |
+ } |
|
| 315 |
+ if err != unix.EINTR {
|
|
| 316 |
+ return errors.Wrapf(err, "failed to set file label on %s", fpath) |
|
| 317 |
+ } |
|
| 312 | 318 |
} |
| 319 |
+ |
|
| 313 | 320 |
return nil |
| 314 | 321 |
} |
| 315 | 322 |
|
| ... | ... |
@@ -751,7 +759,7 @@ func reserveLabel(label string) {
|
| 751 | 751 |
if len(label) != 0 {
|
| 752 | 752 |
con := strings.SplitN(label, ":", 4) |
| 753 | 753 |
if len(con) > 3 {
|
| 754 |
- mcsAdd(con[3]) |
|
| 754 |
+ _ = mcsAdd(con[3]) |
|
| 755 | 755 |
} |
| 756 | 756 |
} |
| 757 | 757 |
} |
| ... | ... |
@@ -828,11 +836,11 @@ func intToMcs(id int, catRange uint32) string {
|
| 828 | 828 |
} |
| 829 | 829 |
|
| 830 | 830 |
for ORD > TIER {
|
| 831 |
- ORD = ORD - TIER |
|
| 831 |
+ ORD -= TIER |
|
| 832 | 832 |
TIER-- |
| 833 | 833 |
} |
| 834 | 834 |
TIER = SETSIZE - TIER |
| 835 |
- ORD = ORD + TIER |
|
| 835 |
+ ORD += TIER |
|
| 836 | 836 |
return fmt.Sprintf("s0:c%d,c%d", TIER, ORD)
|
| 837 | 837 |
} |
| 838 | 838 |
|
| ... | ... |
@@ -844,16 +852,14 @@ func uniqMcs(catRange uint32) string {
|
| 844 | 844 |
) |
| 845 | 845 |
|
| 846 | 846 |
for {
|
| 847 |
- binary.Read(rand.Reader, binary.LittleEndian, &n) |
|
| 847 |
+ _ = binary.Read(rand.Reader, binary.LittleEndian, &n) |
|
| 848 | 848 |
c1 = n % catRange |
| 849 |
- binary.Read(rand.Reader, binary.LittleEndian, &n) |
|
| 849 |
+ _ = binary.Read(rand.Reader, binary.LittleEndian, &n) |
|
| 850 | 850 |
c2 = n % catRange |
| 851 | 851 |
if c1 == c2 {
|
| 852 | 852 |
continue |
| 853 |
- } else {
|
|
| 854 |
- if c1 > c2 {
|
|
| 855 |
- c1, c2 = c2, c1 |
|
| 856 |
- } |
|
| 853 |
+ } else if c1 > c2 {
|
|
| 854 |
+ c1, c2 = c2, c1 |
|
| 857 | 855 |
} |
| 858 | 856 |
mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2)
|
| 859 | 857 |
if err := mcsAdd(mcs); err != nil {
|
| ... | ... |
@@ -884,18 +890,13 @@ func openContextFile() (*os.File, error) {
|
| 884 | 884 |
if f, err := os.Open(contextFile); err == nil {
|
| 885 | 885 |
return f, nil |
| 886 | 886 |
} |
| 887 |
- lxcPath := filepath.Join(getSELinuxPolicyRoot(), "/contexts/lxc_contexts") |
|
| 887 |
+ lxcPath := filepath.Join(policyRoot, "/contexts/lxc_contexts") |
|
| 888 | 888 |
return os.Open(lxcPath) |
| 889 | 889 |
} |
| 890 | 890 |
|
| 891 | 891 |
var labels = loadLabels() |
| 892 | 892 |
|
| 893 | 893 |
func loadLabels() map[string]string {
|
| 894 |
- var ( |
|
| 895 |
- val, key string |
|
| 896 |
- bufin *bufio.Reader |
|
| 897 |
- ) |
|
| 898 |
- |
|
| 899 | 894 |
labels := make(map[string]string) |
| 900 | 895 |
in, err := openContextFile() |
| 901 | 896 |
if err != nil {
|
| ... | ... |
@@ -903,18 +904,10 @@ func loadLabels() map[string]string {
|
| 903 | 903 |
} |
| 904 | 904 |
defer in.Close() |
| 905 | 905 |
|
| 906 |
- bufin = bufio.NewReader(in) |
|
| 906 |
+ scanner := bufio.NewScanner(in) |
|
| 907 | 907 |
|
| 908 |
- for done := false; !done; {
|
|
| 909 |
- var line string |
|
| 910 |
- if line, err = bufin.ReadString('\n'); err != nil {
|
|
| 911 |
- if err == io.EOF {
|
|
| 912 |
- done = true |
|
| 913 |
- } else {
|
|
| 914 |
- break |
|
| 915 |
- } |
|
| 916 |
- } |
|
| 917 |
- line = strings.TrimSpace(line) |
|
| 908 |
+ for scanner.Scan() {
|
|
| 909 |
+ line := strings.TrimSpace(scanner.Text()) |
|
| 918 | 910 |
if len(line) == 0 {
|
| 919 | 911 |
// Skip blank lines |
| 920 | 912 |
continue |
| ... | ... |
@@ -924,7 +917,7 @@ func loadLabels() map[string]string {
|
| 924 | 924 |
continue |
| 925 | 925 |
} |
| 926 | 926 |
if groups := assignRegex.FindStringSubmatch(line); groups != nil {
|
| 927 |
- key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) |
|
| 927 |
+ key, val := strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) |
|
| 928 | 928 |
labels[key] = strings.Trim(val, "\"") |
| 929 | 929 |
} |
| 930 | 930 |
} |
| ... | ... |
@@ -1015,7 +1008,7 @@ func copyLevel(src, dest string) (string, error) {
|
| 1015 | 1015 |
return "", err |
| 1016 | 1016 |
} |
| 1017 | 1017 |
mcsDelete(tcon["level"]) |
| 1018 |
- mcsAdd(scon["level"]) |
|
| 1018 |
+ _ = mcsAdd(scon["level"]) |
|
| 1019 | 1019 |
tcon["level"] = scon["level"] |
| 1020 | 1020 |
return tcon.Get(), nil |
| 1021 | 1021 |
} |
| ... | ... |
@@ -1095,3 +1088,124 @@ func dupSecOpt(src string) ([]string, error) {
|
| 1095 | 1095 |
func disableSecOpt() []string {
|
| 1096 | 1096 |
return []string{"disable"}
|
| 1097 | 1097 |
} |
| 1098 |
+ |
|
| 1099 |
+// findUserInContext scans the reader for a valid SELinux context |
|
| 1100 |
+// match that is verified with the verifier. Invalid contexts are |
|
| 1101 |
+// skipped. It returns a matched context or an empty string if no |
|
| 1102 |
+// match is found. If a scanner error occurs, it is returned. |
|
| 1103 |
+func findUserInContext(context Context, r io.Reader, verifier func(string) error) (string, error) {
|
|
| 1104 |
+ fromRole := context["role"] |
|
| 1105 |
+ fromType := context["type"] |
|
| 1106 |
+ scanner := bufio.NewScanner(r) |
|
| 1107 |
+ |
|
| 1108 |
+ for scanner.Scan() {
|
|
| 1109 |
+ fromConns := strings.Fields(scanner.Text()) |
|
| 1110 |
+ if len(fromConns) == 0 {
|
|
| 1111 |
+ // Skip blank lines |
|
| 1112 |
+ continue |
|
| 1113 |
+ } |
|
| 1114 |
+ |
|
| 1115 |
+ line := fromConns[0] |
|
| 1116 |
+ |
|
| 1117 |
+ if line[0] == ';' || line[0] == '#' {
|
|
| 1118 |
+ // Skip comments |
|
| 1119 |
+ continue |
|
| 1120 |
+ } |
|
| 1121 |
+ |
|
| 1122 |
+ // user context files contexts are formatted as |
|
| 1123 |
+ // role_r:type_t:s0 where the user is missing. |
|
| 1124 |
+ lineArr := strings.SplitN(line, ":", 4) |
|
| 1125 |
+ // skip context with typo, or role and type do not match |
|
| 1126 |
+ if len(lineArr) != 3 || |
|
| 1127 |
+ lineArr[0] != fromRole || |
|
| 1128 |
+ lineArr[1] != fromType {
|
|
| 1129 |
+ continue |
|
| 1130 |
+ } |
|
| 1131 |
+ |
|
| 1132 |
+ for _, cc := range fromConns[1:] {
|
|
| 1133 |
+ toConns := strings.SplitN(cc, ":", 4) |
|
| 1134 |
+ if len(toConns) != 3 {
|
|
| 1135 |
+ continue |
|
| 1136 |
+ } |
|
| 1137 |
+ |
|
| 1138 |
+ context["role"] = toConns[0] |
|
| 1139 |
+ context["type"] = toConns[1] |
|
| 1140 |
+ |
|
| 1141 |
+ outConn := context.get() |
|
| 1142 |
+ if err := verifier(outConn); err != nil {
|
|
| 1143 |
+ continue |
|
| 1144 |
+ } |
|
| 1145 |
+ |
|
| 1146 |
+ return outConn, nil |
|
| 1147 |
+ } |
|
| 1148 |
+ } |
|
| 1149 |
+ |
|
| 1150 |
+ if err := scanner.Err(); err != nil {
|
|
| 1151 |
+ return "", errors.Wrap(err, "failed to scan for context") |
|
| 1152 |
+ } |
|
| 1153 |
+ |
|
| 1154 |
+ return "", nil |
|
| 1155 |
+} |
|
| 1156 |
+ |
|
| 1157 |
+func getDefaultContextFromReaders(c *defaultSECtx) (string, error) {
|
|
| 1158 |
+ if c.verifier == nil {
|
|
| 1159 |
+ return "", ErrVerifierNil |
|
| 1160 |
+ } |
|
| 1161 |
+ |
|
| 1162 |
+ context, err := newContext(c.scon) |
|
| 1163 |
+ if err != nil {
|
|
| 1164 |
+ return "", errors.Wrapf(err, "failed to create label for %s", c.scon) |
|
| 1165 |
+ } |
|
| 1166 |
+ |
|
| 1167 |
+ // set so the verifier validates the matched context with the provided user and level. |
|
| 1168 |
+ context["user"] = c.user |
|
| 1169 |
+ context["level"] = c.level |
|
| 1170 |
+ |
|
| 1171 |
+ conn, err := findUserInContext(context, c.userRdr, c.verifier) |
|
| 1172 |
+ if err != nil {
|
|
| 1173 |
+ return "", err |
|
| 1174 |
+ } |
|
| 1175 |
+ |
|
| 1176 |
+ if conn != "" {
|
|
| 1177 |
+ return conn, nil |
|
| 1178 |
+ } |
|
| 1179 |
+ |
|
| 1180 |
+ conn, err = findUserInContext(context, c.defaultRdr, c.verifier) |
|
| 1181 |
+ if err != nil {
|
|
| 1182 |
+ return "", err |
|
| 1183 |
+ } |
|
| 1184 |
+ |
|
| 1185 |
+ if conn != "" {
|
|
| 1186 |
+ return conn, nil |
|
| 1187 |
+ } |
|
| 1188 |
+ |
|
| 1189 |
+ return "", errors.Wrapf(ErrContextMissing, "context not found: %q", c.scon) |
|
| 1190 |
+} |
|
| 1191 |
+ |
|
| 1192 |
+func getDefaultContextWithLevel(user, level, scon string) (string, error) {
|
|
| 1193 |
+ userPath := filepath.Join(policyRoot, selinuxUsersDir, user) |
|
| 1194 |
+ defaultPath := filepath.Join(policyRoot, defaultContexts) |
|
| 1195 |
+ |
|
| 1196 |
+ fu, err := os.Open(userPath) |
|
| 1197 |
+ if err != nil {
|
|
| 1198 |
+ return "", err |
|
| 1199 |
+ } |
|
| 1200 |
+ defer fu.Close() |
|
| 1201 |
+ |
|
| 1202 |
+ fd, err := os.Open(defaultPath) |
|
| 1203 |
+ if err != nil {
|
|
| 1204 |
+ return "", err |
|
| 1205 |
+ } |
|
| 1206 |
+ defer fd.Close() |
|
| 1207 |
+ |
|
| 1208 |
+ c := defaultSECtx{
|
|
| 1209 |
+ user: user, |
|
| 1210 |
+ level: level, |
|
| 1211 |
+ scon: scon, |
|
| 1212 |
+ userRdr: fu, |
|
| 1213 |
+ defaultRdr: fd, |
|
| 1214 |
+ verifier: securityCheckContext, |
|
| 1215 |
+ } |
|
| 1216 |
+ |
|
| 1217 |
+ return getDefaultContextFromReaders(&c) |
|
| 1218 |
+} |
| ... | ... |
@@ -6,21 +6,21 @@ import ( |
| 6 | 6 |
"golang.org/x/sys/unix" |
| 7 | 7 |
) |
| 8 | 8 |
|
| 9 |
-// Returns a []byte slice if the xattr is set and nil otherwise |
|
| 10 |
-// Requires path and its attribute as arguments |
|
| 11 |
-func lgetxattr(path string, attr string) ([]byte, error) {
|
|
| 9 |
+// lgetxattr returns a []byte slice containing the value of |
|
| 10 |
+// an extended attribute attr set for path. |
|
| 11 |
+func lgetxattr(path, attr string) ([]byte, error) {
|
|
| 12 | 12 |
// Start with a 128 length byte array |
| 13 | 13 |
dest := make([]byte, 128) |
| 14 |
- sz, errno := unix.Lgetxattr(path, attr, dest) |
|
| 14 |
+ sz, errno := doLgetxattr(path, attr, dest) |
|
| 15 | 15 |
for errno == unix.ERANGE {
|
| 16 | 16 |
// Buffer too small, use zero-sized buffer to get the actual size |
| 17 |
- sz, errno = unix.Lgetxattr(path, attr, []byte{})
|
|
| 17 |
+ sz, errno = doLgetxattr(path, attr, []byte{})
|
|
| 18 | 18 |
if errno != nil {
|
| 19 | 19 |
return nil, errno |
| 20 | 20 |
} |
| 21 | 21 |
|
| 22 | 22 |
dest = make([]byte, sz) |
| 23 |
- sz, errno = unix.Lgetxattr(path, attr, dest) |
|
| 23 |
+ sz, errno = doLgetxattr(path, attr, dest) |
|
| 24 | 24 |
} |
| 25 | 25 |
if errno != nil {
|
| 26 | 26 |
return nil, errno |
| ... | ... |
@@ -28,3 +28,13 @@ func lgetxattr(path string, attr string) ([]byte, error) {
|
| 28 | 28 |
|
| 29 | 29 |
return dest[:sz], nil |
| 30 | 30 |
} |
| 31 |
+ |
|
| 32 |
+// doLgetxattr is a wrapper that retries on EINTR |
|
| 33 |
+func doLgetxattr(path, attr string, dest []byte) (int, error) {
|
|
| 34 |
+ for {
|
|
| 35 |
+ sz, err := unix.Lgetxattr(path, attr, dest) |
|
| 36 |
+ if err != unix.EINTR {
|
|
| 37 |
+ return sz, err |
|
| 38 |
+ } |
|
| 39 |
+ } |
|
| 40 |
+} |
| ... | ... |
@@ -20,17 +20,16 @@ type WalkFunc = filepath.WalkFunc |
| 20 | 20 |
// |
| 21 | 21 |
// Note that this implementation only supports primitive error handling: |
| 22 | 22 |
// |
| 23 |
-// * no errors are ever passed to WalkFn |
|
| 23 |
+// - no errors are ever passed to WalkFn; |
|
| 24 | 24 |
// |
| 25 |
-// * once a walkFn returns any error, all further processing stops |
|
| 26 |
-// and the error is returned to the caller of Walk; |
|
| 25 |
+// - once a walkFn returns any error, all further processing stops |
|
| 26 |
+// and the error is returned to the caller of Walk; |
|
| 27 | 27 |
// |
| 28 |
-// * filepath.SkipDir is not supported; |
|
| 29 |
-// |
|
| 30 |
-// * if more than one walkFn instance will return an error, only one |
|
| 31 |
-// of such errors will be propagated and returned by Walk, others |
|
| 32 |
-// will be silently discarded. |
|
| 28 |
+// - filepath.SkipDir is not supported; |
|
| 33 | 29 |
// |
| 30 |
+// - if more than one walkFn instance will return an error, only one |
|
| 31 |
+// of such errors will be propagated and returned by Walk, others |
|
| 32 |
+// will be silently discarded. |
|
| 34 | 33 |
func Walk(root string, walkFn WalkFunc) error {
|
| 35 | 34 |
return WalkN(root, walkFn, runtime.NumCPU()*2) |
| 36 | 35 |
} |
| ... | ... |
@@ -38,6 +37,8 @@ func Walk(root string, walkFn WalkFunc) error {
|
| 38 | 38 |
// WalkN is a wrapper for filepath.Walk which can call multiple walkFn |
| 39 | 39 |
// in parallel, allowing to handle each item concurrently. A maximum of |
| 40 | 40 |
// num walkFn will be called at any one time. |
| 41 |
+// |
|
| 42 |
+// Please see Walk documentation for caveats of using this function. |
|
| 41 | 43 |
func WalkN(root string, walkFn WalkFunc, num int) error {
|
| 42 | 44 |
// make sure limit is sensible |
| 43 | 45 |
if num < 1 {
|