A login session is the account's own, of class user, read at accept (hq ADR 0272)
The cgroup names a scope; only logind's record of it says whose session it is. The engine now takes a session only when /run/systemd/sessions/<id> says it is this account's (UID), of class user (not a greeter's or the user manager's), and of this very scope. And it reads the session the moment the connection is accepted, with the namespaces, before the line, so the peer is judged as it was when it connected. Tests: session-9.scope with a user record is taken; x.scope, a different uid, another account's session, a greeter's, a record naming another scope, no record, and a path with dots are not; the session is read before the line is forwarded.
This commit is contained in:
@@ -249,7 +249,66 @@ func TestTheLoginSessionAskingIsReadFromTheKernel(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raw, _ := os.ReadFile("/proc/self/cgroup")
|
||||
if want := loginSessionIn(string(raw), uint32(os.Getuid())); got.Session != want {
|
||||
if want := userSessionIn(string(raw), uint32(os.Getuid())); got.Session != want {
|
||||
t.Fatalf("sent session %q, the kernel says %q", got.Session, want)
|
||||
}
|
||||
}
|
||||
|
||||
// **A login session is a person's: systemd's own record says so** (review of ADR 0272): the scope the cgroup names
|
||||
// must be a session logind keeps for this very account, of class `user` — not a greeter's, not the user manager's,
|
||||
// not one whose record names another account — and read at accept, with the namespaces, before anything else.
|
||||
func TestALoginSessionIsAUsersByItsOwnRecord(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
was := sessionsDir
|
||||
sessionsDir = dir
|
||||
t.Cleanup(func() { sessionsDir = was })
|
||||
write := func(id, body string) {
|
||||
if err := os.WriteFile(filepath.Join(dir, id), []byte(body), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
write("9", "UID=1000\nUSER=op\nCLASS=user\nSCOPE=session-9.scope\nACTIVE=1\n")
|
||||
write("3", "UID=1000\nCLASS=greeter\nSCOPE=session-3.scope\n")
|
||||
write("4", "UID=1001\nCLASS=user\nSCOPE=session-4.scope\n")
|
||||
write("5", "UID=1000\nCLASS=user\nSCOPE=session-6.scope\n")
|
||||
for _, c := range []struct {
|
||||
cgroup string
|
||||
uid uint32
|
||||
session string
|
||||
}{
|
||||
{"0::/user.slice/user-1000.slice/session-9.scope\n", 1000, "session-9.scope"},
|
||||
{"0::/user.slice/user-1000.slice/x.scope\n", 1000, ""},
|
||||
{"0::/user.slice/user-1000.slice/session-9.scope\n", 1001, ""}, // a different uid than the scope's slice
|
||||
{"0::/user.slice/user-1001.slice/session-4.scope\n", 1000, ""}, // another account's session
|
||||
{"0::/user.slice/user-1000.slice/session-3.scope\n", 1000, ""}, // a greeter's
|
||||
{"0::/user.slice/user-1000.slice/session-5.scope\n", 1000, ""}, // a record naming another scope
|
||||
{"0::/user.slice/user-1000.slice/session-7.scope\n", 1000, ""}, // no record at all
|
||||
{"0::/user.slice/user-1000.slice/session-../9.scope\n", 1000, ""},
|
||||
} {
|
||||
if got := userSessionIn(c.cgroup, c.uid); got != c.session {
|
||||
t.Errorf("%q as uid %d read as %q, want %q", c.cgroup, c.uid, got, c.session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The session is read at accept, with the namespaces, and before the line: a peer that execs or exits while its
|
||||
// line is read is judged as it was when it connected.
|
||||
func TestTheSessionIsReadBeforeTheLine(t *testing.T) {
|
||||
var order []string
|
||||
beforeNS, beforeSession := sameNamespaces, sessionOf
|
||||
sameNamespaces = func(int32) error { order = append(order, "namespaces"); return nil }
|
||||
sessionOf = func(int32, uint32) string { order = append(order, "session"); return "session-1.scope" }
|
||||
t.Cleanup(func() { sameNamespaces, sessionOf = beforeNS, beforeSession })
|
||||
var got Asked
|
||||
socket := serving(t, func(_ context.Context, asked Asked) cli.Answer {
|
||||
order = append(order, "forward")
|
||||
got = asked
|
||||
return cli.Answer{}
|
||||
})
|
||||
if _, err := cli.Ask(context.Background(), socket, []string{"status"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Join(order, ",") != "namespaces,session,forward" || got.Session != "session-1.scope" {
|
||||
t.Fatalf("read in the order %v, sent %q", order, got.Session)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user