Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions go-selinux/selinux.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ var (
privContainerMountLabel string
)

// ProcessKind selects which process domain [SetProcessKind] applies to a label.
type ProcessKind int

const (
ProcessKindRegular ProcessKind = 1
ProcessKindInit ProcessKind = 2
ProcessKindKVM ProcessKind = 3
)
Comment thread
thaJeztah marked this conversation as resolved.

// SetProcessKind returns label with its type component replaced by the one
// corresponding to kind. Other label components are kept intact.
func SetProcessKind(label string, kind ProcessKind) (string, error) {
return setProcessKind(label, kind)
}

// Context is a representation of the SELinux label broken into 4 parts
type Context map[string]string

Expand Down Expand Up @@ -292,6 +307,8 @@ func KVMContainerLabels() (string, string) {

// KVMContainerLabel returns the default process label to be used
// for KVM containers by the calling process.
//
// If you only need to change a type of existing label, use [SetProcessKind] instead.
func KVMContainerLabel() (string, error) {
return kvmContainerLabel()
}
Expand All @@ -306,6 +323,8 @@ func InitContainerLabels() (string, string) {

// InitContainerLabel returns the default process label to be used
// for containers running an init system like systemd by the calling process.
//
// If you only need to change a type of existing label, use [SetProcessKind] instead.
func InitContainerLabel() (string, error) {
return initContainerLabel()
}
Expand Down
43 changes: 43 additions & 0 deletions go-selinux/selinux_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1513,3 +1513,46 @@ func getDefaultContextWithLevel(user, level, scon string) (string, error) {

return getDefaultContextFromReaders(&c)
}

func (k ProcessKind) keys() (primary, fallback string, ok bool) {
switch k {
case ProcessKindRegular:
return "process", "", true
case ProcessKindInit:
return "init_process", "process", true
case ProcessKindKVM:
return "kvm_process", "process", true
}
return "", "", false
}

func setProcessKind(cLabel string, k ProcessKind) (string, error) {
if cLabel == "" {
return "", nil
}
primary, fallback, ok := k.keys()
if !ok {
return "", fmt.Errorf("selinux.SetProcessKind: invalid ProcessKind %d", k)
}

src := label(primary)
if src == "" && fallback != "" {
src = label(fallback)
}
if src == "" {
return cLabel, nil
}

// Replace cLabel type with one from src.
srcCtx, err := newContext(src)
if err != nil {
return "", fmt.Errorf("selinux.SetProcessKind: invalid %s label %s: %w", primary, src, err)
}
dstCtx, err := newContext(cLabel)
if err != nil {
return "", fmt.Errorf("selinux.SetProcessKind: invalid label %s: %w", cLabel, err)
}

dstCtx["type"] = srcCtx["type"]
return dstCtx.get(), nil
}
63 changes: 63 additions & 0 deletions go-selinux/selinux_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,69 @@ func TestSetFileLabel(t *testing.T) {
}
}

func TestSetProcessKind(t *testing.T) {
// These cases do not depend on SELinux being enabled.
t.Run("empty label", func(t *testing.T) {
out, err := SetProcessKind("", ProcessKindRegular)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if out != "" {
t.Errorf("got %q, want empty string", out)
}
})

t.Run("invalid ProcessKind", func(t *testing.T) {
if _, err := SetProcessKind("system_u:system_r:container_t:s0", ProcessKind(0)); err == nil {
t.Error("expected error for ProcessKind(0), got nil")
}
if _, err := SetProcessKind("system_u:system_r:container_t:s0", ProcessKind(42)); err == nil {
t.Error("expected error for ProcessKind(42), got nil")
}
})

if !GetEnabled() {
t.Skip("SELinux not enabled, skipping policy-dependent cases")
}

t.Run("invalid input label", func(t *testing.T) {
if _, err := SetProcessKind("not-a-valid-label", ProcessKindRegular); err == nil {
t.Error("expected error for malformed label, got nil")
}
})

t.Run("ProcessKindKVM", func(t *testing.T) {
// Pick a base label we can mutate. Use the process label from policy as a
// donor for user/role/level, but swap its type with something distinct so we
// can observe SetProcessKind replacing it.
base := label("process")
if base == "" {
t.Skip("no process label in policy, skipping.")
}
baseCtx, err := NewContext(base)
if err != nil {
t.Fatalf("NewContext(%q): %v", base, err)
}
baseCtx["type"] = "foo_bar_baz_t"
if baseCtx["level"] == "" {
baseCtx["level"] = "s0"
}
input := baseCtx.Get()
res, err := SetProcessKind(input, ProcessKindKVM)
t.Logf("SetProcessKind(%q): %q", input, res)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if _, err := CanonicalizeContext(res); err != nil {
t.Error(err)
}
// Check the type has changed.
if strings.Contains(res, baseCtx["type"]) {
t.Errorf("want %q to not contain %q", res, baseCtx["type"])
}
})
}

func TestKVMContainerLabel(t *testing.T) {
if !GetEnabled() {
t.Skip("SELinux not enabled, skipping.")
Expand Down
4 changes: 4 additions & 0 deletions go-selinux/selinux_stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,7 @@ func getDefaultContextWithLevel(string, string, string) (string, error) {
func label(_ string) string {
return ""
}

func setProcessKind(string, ProcessKind) (string, error) {
return "", nil
}
3 changes: 3 additions & 0 deletions go-selinux/selinux_stub_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,7 @@ func TestSELinuxStubs(t *testing.T) {
if _, err = CopyLevel("foo", "bar"); err != nil {
t.Error(err)
}
if _, err = SetProcessKind("", ProcessKindRegular); err != nil {
t.Error(err)
}
}
Loading