Skip to content

Commit 4087223

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

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
@@ -30,6 +30,7 @@ const (
3030
selinuxDir = "/etc/selinux/"
3131
selinuxUsersDir = "contexts/users"
3232
defaultContexts = "contexts/default_contexts"
33+
failsafeContext = "contexts/failsafe_context"
3334
selinuxConfig = selinuxDir + "config"
3435
selinuxfsMount = "/sys/fs/selinux"
3536
selinuxTypeTag = "SELINUXTYPE"
@@ -60,6 +61,7 @@ type defaultSECtx struct {
6061
userRdr io.Reader
6162
verifier func(string) error
6263
defaultRdr io.Reader
64+
failsafeRdr io.Reader
6365
user, level, scon string
6466
}
6567

@@ -1449,6 +1451,33 @@ func findUserInContext(context Context, r io.Reader, verifier func(string) error
14491451
return "", nil
14501452
}
14511453

1454+
// getFailsafeContext returns the context in the failsafe_context file:
1455+
// https://www.man7.org/linux/man-pages/man5/failsafe_context.5.html
1456+
func getFailsafeContext(context Context, r io.Reader, verifier func(string) error) (string, error) {
1457+
conn := make([]byte, 256)
1458+
limReader := io.LimitReader(r, int64(len(conn)))
1459+
_, err := limReader.Read(conn)
1460+
if err != nil {
1461+
return "", fmt.Errorf("failed to read failsafe context: %w", err)
1462+
}
1463+
1464+
conn = bytes.TrimSpace(conn)
1465+
toConns := strings.SplitN(string(conn), ":", 4)
1466+
if len(toConns) != 3 {
1467+
return "", nil
1468+
}
1469+
1470+
context["role"] = toConns[0]
1471+
context["type"] = toConns[1]
1472+
1473+
outConn := context.get()
1474+
if err := verifier(outConn); err != nil {
1475+
return "", nil
1476+
}
1477+
1478+
return outConn, nil
1479+
}
1480+
14521481
func getDefaultContextFromReaders(c *defaultSECtx) (string, error) {
14531482
if c.verifier == nil {
14541483
return "", ErrVerifierNil
@@ -1465,7 +1494,7 @@ func getDefaultContextFromReaders(c *defaultSECtx) (string, error) {
14651494

14661495
conn, err := findUserInContext(context, c.userRdr, c.verifier)
14671496
if err != nil {
1468-
return "", err
1497+
return "", fmt.Errorf("failed to read %q's user context file: %w", c.user, err)
14691498
}
14701499

14711500
if conn != "" {
@@ -1474,7 +1503,16 @@ func getDefaultContextFromReaders(c *defaultSECtx) (string, error) {
14741503

14751504
conn, err = findUserInContext(context, c.defaultRdr, c.verifier)
14761505
if err != nil {
1477-
return "", err
1506+
return "", fmt.Errorf("failed to read default user context file: %w", err)
1507+
}
1508+
1509+
if conn != "" {
1510+
return conn, nil
1511+
}
1512+
1513+
conn, err = getFailsafeContext(context, c.failsafeRdr, c.verifier)
1514+
if err != nil {
1515+
return "", fmt.Errorf("failed to read failsafe_context: %w", err)
14781516
}
14791517

14801518
if conn != "" {
@@ -1488,24 +1526,32 @@ func getDefaultContextWithLevel(user, level, scon string) (string, error) {
14881526
userPath := filepath.Join(policyRoot(), selinuxUsersDir, user)
14891527
fu, err := os.Open(userPath)
14901528
if err != nil {
1491-
return "", err
1529+
return "", fmt.Errorf("failed to open %q's user context file: %w", user, err)
14921530
}
14931531
defer fu.Close()
14941532

14951533
defaultPath := filepath.Join(policyRoot(), defaultContexts)
14961534
fd, err := os.Open(defaultPath)
14971535
if err != nil {
1498-
return "", err
1536+
return "", fmt.Errorf("failed to open default user context file: %w", err)
14991537
}
15001538
defer fd.Close()
15011539

1540+
failsafePath := filepath.Join(policyRoot(), failsafeContext)
1541+
fs, err := os.Open(failsafePath)
1542+
if err != nil {
1543+
return "", fmt.Errorf("failed to open failsafe user context file: %w", err)
1544+
}
1545+
defer fs.Close()
1546+
15021547
c := defaultSECtx{
1503-
user: user,
1504-
level: level,
1505-
scon: scon,
1506-
userRdr: fu,
1507-
defaultRdr: fd,
1508-
verifier: securityCheckContext,
1548+
user: user,
1549+
level: level,
1550+
scon: scon,
1551+
userRdr: fu,
1552+
defaultRdr: fd,
1553+
failsafeRdr: fs,
1554+
verifier: securityCheckContext,
15091555
}
15101556

15111557
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)