Skip to content

Commit abd8a4f

Browse files
committed
Add ChangeLabelType
All (open source) users of KVMContainerLabel[s] and InitContainerLabel[s] (containerd, podman, and cri-o) are immediately releasing the acquired MCS. They only need the type field, so they can change the existing label to that type. Everything else, including the just-generated unique MCS label, is not used. Introduce ChangeLabelType which does just what all those users need. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
1 parent fb9b5b2 commit abd8a4f

5 files changed

Lines changed: 133 additions & 0 deletions

File tree

go-selinux/selinux.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,22 @@ var (
4848
privContainerMountLabel string
4949
)
5050

51+
// LabelType is a type to use for [ChangeLabelType]
52+
type LabelType int
53+
54+
const (
55+
TypeProcess LabelType = iota + 1
56+
TypeInitProcess
57+
TypeKVMProcess
58+
)
59+
60+
// ChangeLabelType modifies the cLabel, changing its type component
61+
// to the one corresponding to the lType. Other cLabel components
62+
// are kept intact.
63+
func ChangeLabelType(cLabel string, lType LabelType) (string, error) {
64+
return changeLabelType(cLabel, lType)
65+
}
66+
5167
// Context is a representation of the SELinux label broken into 4 parts
5268
type Context map[string]string
5369

@@ -292,6 +308,8 @@ func KVMContainerLabels() (string, string) {
292308

293309
// KVMContainerLabel returns the default process label to be used
294310
// for KVM containers by the calling process.
311+
//
312+
// If you only need to change a type of existing label, use [ChangeLabelType] instead.
295313
func KVMContainerLabel() (string, error) {
296314
return kvmContainerLabel()
297315
}
@@ -306,6 +324,8 @@ func InitContainerLabels() (string, string) {
306324

307325
// InitContainerLabel returns the default process label to be used
308326
// for containers running an init system like systemd by the calling process.
327+
//
328+
// If you only need to change a type of existing label, use [ChangeLabelType] instead.
309329
func InitContainerLabel() (string, error) {
310330
return initContainerLabel()
311331
}

go-selinux/selinux_linux.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,3 +1513,46 @@ func getDefaultContextWithLevel(user, level, scon string) (string, error) {
15131513

15141514
return getDefaultContextFromReaders(&c)
15151515
}
1516+
1517+
func (kind LabelType) keys() (primary, fallback string, ok bool) {
1518+
switch kind {
1519+
case TypeProcess:
1520+
return "process", "", true
1521+
case TypeInitProcess:
1522+
return "init_process", "process", true
1523+
case TypeKVMProcess:
1524+
return "kvm_process", "process", true
1525+
}
1526+
return "", "", false
1527+
}
1528+
1529+
func changeLabelType(cLabel string, lt LabelType) (string, error) {
1530+
if cLabel == "" {
1531+
return "", nil
1532+
}
1533+
primary, fallback, ok := lt.keys()
1534+
if !ok {
1535+
return "", fmt.Errorf("selinux.ChangeLabelType: invalid LabelType %d", lt)
1536+
}
1537+
1538+
src := label(primary)
1539+
if src == "" && fallback != "" {
1540+
src = label(fallback)
1541+
}
1542+
if src == "" {
1543+
return cLabel, nil
1544+
}
1545+
1546+
// Replace cLabel type with one from src.
1547+
srcCtx, err := newContext(src)
1548+
if err != nil {
1549+
return "", fmt.Errorf("selinux.ChangeLabelType: invalid %s label %s: %w", primary, src, err)
1550+
}
1551+
dstCtx, err := newContext(cLabel)
1552+
if err != nil {
1553+
return "", fmt.Errorf("selinux.ChangeLabelType: invalid label %s: %w", cLabel, err)
1554+
}
1555+
1556+
dstCtx["type"] = srcCtx["type"]
1557+
return dstCtx.get(), nil
1558+
}

go-selinux/selinux_linux_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,69 @@ func TestSetFileLabel(t *testing.T) {
8484
}
8585
}
8686

87+
func TestChangeLabelType(t *testing.T) {
88+
// These cases do not depend on SELinux being enabled.
89+
t.Run("empty label", func(t *testing.T) {
90+
out, err := ChangeLabelType("", TypeProcess)
91+
if err != nil {
92+
t.Fatalf("unexpected error: %v", err)
93+
}
94+
if out != "" {
95+
t.Errorf("got %q, want empty string", out)
96+
}
97+
})
98+
99+
t.Run("invalid LabelType", func(t *testing.T) {
100+
if _, err := ChangeLabelType("system_u:system_r:container_t:s0", LabelType(0)); err == nil {
101+
t.Error("expected error for LabelType(0), got nil")
102+
}
103+
if _, err := ChangeLabelType("system_u:system_r:container_t:s0", LabelType(42)); err == nil {
104+
t.Error("expected error for LabelType(42), got nil")
105+
}
106+
})
107+
108+
if !GetEnabled() {
109+
t.Skip("SELinux not enabled, skipping policy-dependent cases")
110+
}
111+
112+
t.Run("invalid input label", func(t *testing.T) {
113+
if _, err := ChangeLabelType("not-a-valid-label", TypeProcess); err == nil {
114+
t.Error("expected error for malformed label, got nil")
115+
}
116+
})
117+
118+
t.Run("TypeKVMProcess", func(t *testing.T) {
119+
// Pick a base label we can mutate. Use the process label from policy as a
120+
// donor for user/role/level, but swap its type with something distinct so we
121+
// can observe ChangeLabelType replacing it.
122+
base := label("process")
123+
if base == "" {
124+
t.Skip("no process label in policy, skipping.")
125+
}
126+
baseCtx, err := NewContext(base)
127+
if err != nil {
128+
t.Fatalf("NewContext(%q): %v", base, err)
129+
}
130+
baseCtx["type"] = "foo_bar_baz_t"
131+
if baseCtx["level"] == "" {
132+
baseCtx["level"] = "s0"
133+
}
134+
input := baseCtx.Get()
135+
res, err := ChangeLabelType(input, TypeKVMProcess)
136+
t.Logf("ChangeLabelType(%q): %q", input, res)
137+
if err != nil {
138+
t.Fatalf("expected no error, got %v", err)
139+
}
140+
if _, err := CanonicalizeContext(res); err != nil {
141+
t.Error(err)
142+
}
143+
// Check the type has changed.
144+
if strings.Contains(res, baseCtx["type"]) {
145+
t.Errorf("want %q to not contain %q", res, baseCtx["type"])
146+
}
147+
})
148+
}
149+
87150
func TestKVMContainerLabel(t *testing.T) {
88151
if !GetEnabled() {
89152
t.Skip("SELinux not enabled, skipping.")

go-selinux/selinux_stub.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,7 @@ func getDefaultContextWithLevel(string, string, string) (string, error) {
157157
func label(_ string) string {
158158
return ""
159159
}
160+
161+
func changeLabelType(string, LabelType) (string, error) {
162+
return "", nil
163+
}

go-selinux/selinux_stub_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,7 @@ func TestSELinuxStubs(t *testing.T) {
120120
if _, err = CopyLevel("foo", "bar"); err != nil {
121121
t.Error(err)
122122
}
123+
if _, err = ChangeLabelType("", TypeProcess); err != nil {
124+
t.Error(err)
125+
}
123126
}

0 commit comments

Comments
 (0)