Skip to content

Commit 3a28444

Browse files
committed
fallback to failsafe context in GetDefaultContextWithLevel
Signed-off-by: Andrew LeFevre <[email protected]>
1 parent 4635962 commit 3a28444

File tree

3 files changed

+82
-24
lines changed

3 files changed

+82
-24
lines changed

go-selinux/selinux.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,8 @@ func GetSeUserByName(username string) (seUser string, level string, err error) {
316316
// identity that is reachable from the specified scon context. The context is based
317317
// on the per-user /etc/selinux/{SELINUXTYPE}/contexts/users/<username> if it exists,
318318
// and falls back to the global /etc/selinux/{SELINUXTYPE}/contexts/default_contexts
319-
// file.
319+
// file and finally the global /etc/selinux/{SELINUXTYPE}/contexts/failsafe_context
320+
// file if no match can be found anywhere else.
320321
func GetDefaultContextWithLevel(user, level, scon string) (string, error) {
321322
return getDefaultContextWithLevel(user, level, scon)
322323
}

go-selinux/selinux_linux.go

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const (
2727
selinuxDir = "/etc/selinux/"
2828
selinuxUsersDir = "contexts/users"
2929
defaultContexts = "contexts/default_contexts"
30+
failsafeContext = "contexts/failsafe_context"
3031
selinuxConfig = selinuxDir + "config"
3132
selinuxfsMount = "/sys/fs/selinux"
3233
selinuxTypeTag = "SELINUXTYPE"
@@ -57,6 +58,7 @@ type defaultSECtx struct {
5758
userRdr io.Reader
5859
verifier func(string) error
5960
defaultRdr io.Reader
61+
failsafeRdr io.Reader
6062
user, level, scon string
6163
}
6264

@@ -1349,6 +1351,33 @@ func findUserInContext(context Context, r io.Reader, verifier func(string) error
13491351
return "", nil
13501352
}
13511353

1354+
// getFailsafeContext returns the context in the failsafe_context file:
1355+
// https://www.man7.org/linux/man-pages/man5/failsafe_context.5.html
1356+
func getFailsafeContext(context Context, r io.Reader, verifier func(string) error) (string, error) {
1357+
conn := make([]byte, 256)
1358+
limReader := io.LimitReader(r, int64(len(conn)))
1359+
_, err := limReader.Read(conn)
1360+
if err != nil {
1361+
return "", fmt.Errorf("failed to read failsafe context: %w", err)
1362+
}
1363+
1364+
conn = bytes.TrimSpace(conn)
1365+
toConns := strings.SplitN(string(conn), ":", 4)
1366+
if len(toConns) != 3 {
1367+
return "", nil
1368+
}
1369+
1370+
context["role"] = toConns[0]
1371+
context["type"] = toConns[1]
1372+
1373+
outConn := context.get()
1374+
if err := verifier(outConn); err != nil {
1375+
return "", nil
1376+
}
1377+
1378+
return outConn, nil
1379+
}
1380+
13521381
func getDefaultContextFromReaders(c *defaultSECtx) (string, error) {
13531382
if c.verifier == nil {
13541383
return "", ErrVerifierNil
@@ -1365,7 +1394,7 @@ func getDefaultContextFromReaders(c *defaultSECtx) (string, error) {
13651394

13661395
conn, err := findUserInContext(context, c.userRdr, c.verifier)
13671396
if err != nil {
1368-
return "", err
1397+
return "", fmt.Errorf("failed to read %q's user context file: %w", c.user, err)
13691398
}
13701399

13711400
if conn != "" {
@@ -1374,7 +1403,16 @@ func getDefaultContextFromReaders(c *defaultSECtx) (string, error) {
13741403

13751404
conn, err = findUserInContext(context, c.defaultRdr, c.verifier)
13761405
if err != nil {
1377-
return "", err
1406+
return "", fmt.Errorf("failed to read default user context file: %w", err)
1407+
}
1408+
1409+
if conn != "" {
1410+
return conn, nil
1411+
}
1412+
1413+
conn, err = getFailsafeContext(context, c.failsafeRdr, c.verifier)
1414+
if err != nil {
1415+
return "", fmt.Errorf("failed to read failsafe_context: %w", err)
13781416
}
13791417

13801418
if conn != "" {
@@ -1388,24 +1426,32 @@ func getDefaultContextWithLevel(user, level, scon string) (string, error) {
13881426
userPath := filepath.Join(policyRoot(), selinuxUsersDir, user)
13891427
fu, err := os.Open(userPath)
13901428
if err != nil {
1391-
return "", err
1429+
return "", fmt.Errorf("failed to open %q's user context file: %w", user, err)
13921430
}
13931431
defer fu.Close()
13941432

13951433
defaultPath := filepath.Join(policyRoot(), defaultContexts)
13961434
fd, err := os.Open(defaultPath)
13971435
if err != nil {
1398-
return "", err
1436+
return "", fmt.Errorf("failed to open default user context file: %w", err)
13991437
}
14001438
defer fd.Close()
14011439

1440+
failsafePath := filepath.Join(policyRoot(), failsafeContext)
1441+
fs, err := os.Open(failsafePath)
1442+
if err != nil {
1443+
return "", fmt.Errorf("failed to open failsafe user context file: %w", err)
1444+
}
1445+
defer fs.Close()
1446+
14021447
c := defaultSECtx{
1403-
user: user,
1404-
level: level,
1405-
scon: scon,
1406-
userRdr: fu,
1407-
defaultRdr: fd,
1408-
verifier: securityCheckContext,
1448+
user: user,
1449+
level: level,
1450+
scon: scon,
1451+
userRdr: fu,
1452+
defaultRdr: fd,
1453+
failsafeRdr: fs,
1454+
verifier: securityCheckContext,
14091455
}
14101456

14111457
return getDefaultContextFromReaders(&c)

go-selinux/selinux_linux_test.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"os"
9+
"os/user"
910
"path/filepath"
1011
"runtime"
1112
"strconv"
@@ -694,7 +695,6 @@ bob:staff_u:s0-s15:c0.c255`,
694695

695696
for _, tt := range tests {
696697
t.Run(tt.name, func(t *testing.T) {
697-
698698
r := bytes.NewBufferString(tt.seUserBuf)
699699
seUser, level, err := getSeUserFromReader(tt.username, tt.gids, r, lookupGroup)
700700
if tt.expectedErr != "" {
@@ -724,6 +724,7 @@ func TestContextWithLevel(t *testing.T) {
724724
foo_r:foo_t:s0 sysadm_r:sysadm_t:s0
725725
staff_r:staff_t:s0 baz_r:baz_t:s0 sysadm_r:sysadm_t:s0
726726
`
727+
goodFailsafeBuff := "unconfined_r:unconfined_t:s0"
727728

728729
verifier := func(con string) error {
729730
if con != want {
@@ -734,7 +735,7 @@ staff_r:staff_t:s0 baz_r:baz_t:s0 sysadm_r:sysadm_t:s0
734735
}
735736

736737
tests := []struct {
737-
name, userBuff, defaultBuff string
738+
name, userBuff, defaultBuff, failsafeBuff string
738739
}{
739740
{
740741
name: "match exists in user context file",
@@ -743,15 +744,17 @@ foo_r:foo_t:s0 sysadm_r:sysadm_t:s0
743744
744745
staff_r:staff_t:s0 baz_r:baz_t:s0 sysadm_r:sysadm_t:s0
745746
`,
746-
defaultBuff: goodDefaultBuff,
747+
defaultBuff: goodDefaultBuff,
748+
failsafeBuff: goodFailsafeBuff,
747749
},
748750
{
749751
name: "match exists in default context file, but not in user file",
750752
userBuff: `# COMMENT
751753
foo_r:foo_t:s0 sysadm_r:sysadm_t:s0
752754
fake_r:fake_t:s0 baz_r:baz_t:s0 sysadm_r:sysadm_t:s0
753755
`,
754-
defaultBuff: goodDefaultBuff,
756+
defaultBuff: goodDefaultBuff,
757+
failsafeBuff: goodFailsafeBuff,
755758
},
756759
}
757760

@@ -785,17 +788,25 @@ fake_r:fake_t:s0 baz_r:baz_t:s0 sysadm_r:sysadm_t:s0
785788
dne_r:dne_t:s0 baz_r:baz_t:s0 sysadm_r:sysadm_t:s0
786789
`
787790
c := defaultSECtx{
788-
user: "bob",
789-
level: "SystemLow-SystemHigh",
790-
scon: "system_u:staff_r:staff_t:s0",
791-
userRdr: bytes.NewBufferString(badUserBuff),
792-
defaultRdr: bytes.NewBufferString(badDefaultBuff),
793-
verifier: verifier,
791+
user: "bob",
792+
level: "SystemLow-SystemHigh",
793+
scon: "system_u:staff_r:staff_t:s0",
794+
userRdr: bytes.NewBufferString(badUserBuff),
795+
defaultRdr: bytes.NewBufferString(badDefaultBuff),
796+
failsafeRdr: bytes.NewBufferString(goodFailsafeBuff),
797+
verifier: func(s string) error {
798+
return nil
799+
},
800+
}
801+
802+
got, err := getDefaultContextFromReaders(&c)
803+
if err != nil {
804+
t.Fatalf("err should not exist but is: %v", err)
794805
}
795806

796-
_, err := getDefaultContextFromReaders(&c)
797-
if err == nil {
798-
t.Fatalf("err was expected")
807+
const want string = "bob:unconfined_r:unconfined_t:SystemLow-SystemHigh"
808+
if got != want {
809+
t.Fatalf("got context: %q but expected %q", got, want)
799810
}
800811
})
801812
}

0 commit comments

Comments
 (0)