package ipcauth import "testing" // The self rule is the one place privilege is granted to something other than the // platform administrator, so its two guards matter: it must apply only when the // daemon is itself unprivileged, and only to a caller with the daemon's identity. func TestIsPrivilegedCaller_SelfRule(t *testing.T) { tests := []struct { name string // self stands in for the process the daemon runs as. self Identity caller Identity want bool }{ { name: "root is privileged whatever the daemon runs as", self: Identity{known: true, UID: 1000}, caller: Identity{known: true, UID: 0}, want: true, }, { name: "an unprivileged daemon delegates to its own user (rootless container)", self: Identity{known: true, UID: 1000}, caller: Identity{known: true, UID: 1000}, want: true, }, { name: "an unprivileged daemon delegates to nobody else", self: Identity{known: true, UID: 1000}, caller: Identity{known: true, UID: 1001}, want: false, }, { // The daemon is root on a normal install, so sharing its identity is // already covered by being root; nothing else may match. name: "a root daemon delegates to nobody", self: Identity{known: true, UID: 0}, caller: Identity{known: true, UID: 1000}, want: false, }, { // Windows netstack mode: the daemon needs no administrator rights. name: "an unprivileged windows daemon delegates to its own SID", self: Identity{known: true, SID: "S-1-5-21-1-2-3-1001"}, caller: Identity{known: true, SID: "S-1-5-21-1-2-3-1001"}, want: true, }, { name: "an unprivileged windows daemon delegates to no other SID", self: Identity{known: true, SID: "S-1-5-21-1-2-3-1001"}, caller: Identity{known: true, SID: "S-1-5-21-1-2-3-1002"}, want: false, }, { // The UAC boundary: a filtered and a full token of the same account // carry the same SID but not the same power, so an elevated daemon must // never delegate to its own SID. name: "an elevated windows daemon does not delegate to its own SID", self: Identity{known: true, SID: "S-1-5-21-1-2-3-500", Elevated: true}, caller: Identity{known: true, SID: "S-1-5-21-1-2-3-500"}, want: false, }, { name: "LocalSystem is privileged on its own merits, not by delegation", self: Identity{known: true, SID: sidLocalSystem}, caller: Identity{known: true, SID: sidLocalSystem}, want: true, // LocalSystem is privileged on its own merits }, { name: "identities of different kinds never match", self: Identity{known: true, UID: 1000}, caller: Identity{known: true, SID: "S-1-5-21-1-2-3-1001"}, want: false, }, { name: "an unknown self identity delegates to nobody", self: Identity{}, caller: Identity{known: true, UID: 1000}, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { prevID, prevDelegate := selfIdentity, selfMayDelegate t.Cleanup(func() { selfIdentity, selfMayDelegate = prevID, prevDelegate }) selfIdentity = tt.self selfMayDelegate = tt.self.Known() && !tt.self.IsPrivileged() if got := IsPrivilegedCaller(tt.caller); got != tt.want { t.Fatalf("IsPrivilegedCaller(%v) with daemon %v = %t, want %t", tt.caller, tt.self, got, tt.want) } }) } } // The real process must never accidentally delegate: a test binary running as a // normal user is unprivileged, so it may match itself, but nothing else. func TestIsPrivilegedCaller_ThisProcess(t *testing.T) { id, err := CurrentProcessIdentity() if err != nil { t.Skipf("cannot read this process's identity: %v", err) } // This process is always allowed to act as itself: either it is privileged, or // it is unprivileged and therefore delegates to its own identity. if !IsPrivilegedCaller(id) { t.Errorf("this process %v was refused its own identity", id) } // A caller that is neither root nor this process must be refused, whatever // this process happens to be. other := Identity{known: true, UID: id.UID + 1} if id.IsWindows() { other = Identity{known: true, SID: id.SID + "9"} } if IsPrivilegedCaller(other) { t.Errorf("an unrelated identity %v was treated as privileged", other) } }