Files
runc/internal/third_party/selinux/go-selinux/label/label_linux.go
T
Aleksa Sarai 2c5356e73f selinux: use safe procfs API for labels
Due to the sensitive nature of these fixes, it was not possible to
submit these upstream and vendor the upstream library. Instead, this
patch uses a fork of github.com/opencontainers/selinux, branched at
commit opencontainers/selinux@879a755db5.

In order to permit downstreams to build with this patched version, a
snapshot of the forked version has been included in
internal/third_party/selinux. Note that since we use "go mod vendor",
the patched code is usable even without being "go get"-able. Once the
embargo for this issue is lifted we can submit the patches upstream and
switch back to a proper upstream go.mod entry.

Also, this requires us to temporarily disable the CI job we have that
disallows "replace" directives.

Fixes: GHSA-cgrx-mc8f-2prm CVE-2025-52881
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
2025-10-16 11:29:35 +11:00

137 lines
3.8 KiB
Go

package label
import (
"errors"
"fmt"
"strings"
"github.com/opencontainers/selinux/go-selinux"
)
// Valid Label Options
var validOptions = map[string]bool{
"disable": true,
"type": true,
"filetype": true,
"user": true,
"role": true,
"level": true,
}
var ErrIncompatibleLabel = errors.New("bad SELinux option: z and Z can not be used together")
// InitLabels returns the process label and file labels to be used within
// the container. A list of options can be passed into this function to alter
// the labels. The labels returned will include a random MCS String, that is
// guaranteed to be unique.
// If the disabled flag is passed in, the process label will not be set, but the mount label will be set
// to the container_file label with the maximum category. This label is not usable by any confined label.
func InitLabels(options []string) (plabel string, mlabel string, retErr error) {
if !selinux.GetEnabled() {
return "", "", nil
}
processLabel, mountLabel := selinux.ContainerLabels()
if processLabel != "" {
defer func() {
if retErr != nil {
selinux.ReleaseLabel(mountLabel)
}
}()
pcon, err := selinux.NewContext(processLabel)
if err != nil {
return "", "", err
}
mcsLevel := pcon["level"]
mcon, err := selinux.NewContext(mountLabel)
if err != nil {
return "", "", err
}
for _, opt := range options {
if opt == "disable" {
selinux.ReleaseLabel(mountLabel)
return "", selinux.PrivContainerMountLabel(), nil
}
if i := strings.Index(opt, ":"); i == -1 {
return "", "", fmt.Errorf("bad label option %q, valid options 'disable' or \n'user, role, level, type, filetype' followed by ':' and a value", opt)
}
con := strings.SplitN(opt, ":", 2)
if !validOptions[con[0]] {
return "", "", fmt.Errorf("bad label option %q, valid options 'disable, user, role, level, type, filetype'", con[0])
}
if con[0] == "filetype" {
mcon["type"] = con[1]
continue
}
pcon[con[0]] = con[1]
if con[0] == "level" || con[0] == "user" {
mcon[con[0]] = con[1]
}
}
if pcon.Get() != processLabel {
if pcon["level"] != mcsLevel {
selinux.ReleaseLabel(processLabel)
}
processLabel = pcon.Get()
selinux.ReserveLabel(processLabel)
}
mountLabel = mcon.Get()
}
return processLabel, mountLabel, nil
}
// SetFileLabel modifies the "path" label to the specified file label
func SetFileLabel(path string, fileLabel string) error {
if !selinux.GetEnabled() || fileLabel == "" {
return nil
}
return selinux.SetFileLabel(path, fileLabel)
}
// SetFileCreateLabel tells the kernel the label for all files to be created
func SetFileCreateLabel(fileLabel string) error {
if !selinux.GetEnabled() {
return nil
}
return selinux.SetFSCreateLabel(fileLabel)
}
// Relabel changes the label of path and all the entries beneath the path.
// It changes the MCS label to s0 if shared is true.
// This will allow all containers to share the content.
//
// The path itself is guaranteed to be relabeled last.
func Relabel(path string, fileLabel string, shared bool) error {
if !selinux.GetEnabled() || fileLabel == "" {
return nil
}
if shared {
c, err := selinux.NewContext(fileLabel)
if err != nil {
return err
}
c["level"] = "s0"
fileLabel = c.Get()
}
return selinux.Chcon(path, fileLabel, true)
}
// Validate checks that the label does not include unexpected options
func Validate(label string) error {
if strings.Contains(label, "z") && strings.Contains(label, "Z") {
return ErrIncompatibleLabel
}
return nil
}
// RelabelNeeded checks whether the user requested a relabel
func RelabelNeeded(label string) bool {
return strings.Contains(label, "z") || strings.Contains(label, "Z")
}
// IsShared checks that the label includes a "shared" mark
func IsShared(label string) bool {
return strings.Contains(label, "z")
}