// Copyright 2021 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This file contains unit tests for the sid class. #include "base/win/sid.h" #include #include #include #include "base/ranges/algorithm.h" #include "base/win/atl.h" #include "base/win/scoped_handle.h" #include "base/win/scoped_localalloc.h" #include "base/win/win_util.h" #include "build/branding_buildflags.h" #include "testing/gtest/include/gtest/gtest.h" namespace base::win { namespace { bool EqualSid(const std::optional& sid, const ATL::CSid& compare_sid) { if (!sid) return false; return sid->Equal(const_cast(compare_sid.GetPSID())); } bool EqualSid(const Sid& sid, const std::wstring& sddl_sid) { PSID compare_sid; if (!::ConvertStringSidToSid(sddl_sid.c_str(), &compare_sid)) { return false; } auto sid_ptr = TakeLocalAlloc(compare_sid); return sid.Equal(sid_ptr.get()); } bool EqualSid(const std::optional& sid, WELL_KNOWN_SID_TYPE known_sid) { if (!sid) return false; char known_sid_buffer[SECURITY_MAX_SID_SIZE] = {}; DWORD size = SECURITY_MAX_SID_SIZE; if (!::CreateWellKnownSid(known_sid, nullptr, known_sid_buffer, &size)) return false; return sid->Equal(known_sid_buffer); } bool TestSidVector(std::optional> sids, const std::vector& sddl) { return sids && ranges::equal(*sids, sddl, [](const Sid& sid, const std::wstring& sddl) { return EqualSid(sid, sddl); }); } bool TestFromSddlStringVector(const std::vector sddl) { return TestSidVector(Sid::FromSddlStringVector(sddl), sddl); } typedef decltype(::DeriveCapabilitySidsFromName)* DeriveCapabilitySidsFromNameFunc; // Get the DeriveCapabilitySidsFromName API dynamically. Versions of Windows 10 // older than 1809 do not implement this method. By loading dynamically we can // skip tests when running on these older versions. Online documentation for // this API claims it's supported back to Windows 2003, however this is entirely // incorrect. DeriveCapabilitySidsFromNameFunc GetDeriveCapabilitySidsFromName() { static const DeriveCapabilitySidsFromNameFunc derive_capability_sids = []() -> DeriveCapabilitySidsFromNameFunc { HMODULE module = GetModuleHandle(L"api-ms-win-security-base-l1-2-2.dll"); if (!module) { return nullptr; } return reinterpret_cast( ::GetProcAddress(module, "DeriveCapabilitySidsFromName")); }(); return derive_capability_sids; } bool EqualNamedCapSid(const Sid& sid, const std::wstring& capability_name) { DeriveCapabilitySidsFromNameFunc derive_capability_sids = GetDeriveCapabilitySidsFromName(); CHECK(derive_capability_sids); // Pre-reserve some space for SID deleters. std::vector deleter_list; deleter_list.reserve(16); PSID* capability_groups = nullptr; DWORD capability_group_count = 0; PSID* capability_sids = nullptr; DWORD capability_sid_count = 0; CHECK(derive_capability_sids(capability_name.c_str(), &capability_groups, &capability_group_count, &capability_sids, &capability_sid_count)); deleter_list.emplace_back(capability_groups); deleter_list.emplace_back(capability_sids); for (DWORD i = 0; i < capability_group_count; ++i) { deleter_list.emplace_back(capability_groups[i]); } for (DWORD i = 0; i < capability_sid_count; ++i) { deleter_list.emplace_back(capability_sids[i]); } CHECK_GE(capability_sid_count, 1U); return sid.Equal(capability_sids[0]); } struct KnownCapabilityTestEntry { WellKnownCapability capability; const wchar_t* sddl_sid; }; struct KnownSidTestEntry { WellKnownSid sid; WELL_KNOWN_SID_TYPE well_known_sid; }; } // namespace // Tests the creation of a Sid. TEST(SidTest, Initializers) { ATL::CSid sid_world = ATL::Sids::World(); PSID sid_world_pointer = const_cast(sid_world.GetPSID()); // Check the PSID constructor. std::optional sid_sid_star = Sid::FromPSID(sid_world_pointer); ASSERT_TRUE(EqualSid(sid_sid_star, sid_world)); char invalid_sid[16] = {}; ASSERT_FALSE(Sid::FromPSID(invalid_sid)); std::optional sid_sddl = Sid::FromSddlString(L"S-1-1-0"); ASSERT_TRUE(sid_sddl); ASSERT_TRUE(EqualSid(sid_sddl, sid_world)); } TEST(SidTest, KnownCapability) { const KnownCapabilityTestEntry capabilities[] = { {WellKnownCapability::kInternetClient, L"S-1-15-3-1"}, {WellKnownCapability::kInternetClientServer, L"S-1-15-3-2"}, {WellKnownCapability::kPrivateNetworkClientServer, L"S-1-15-3-3"}, {WellKnownCapability::kPicturesLibrary, L"S-1-15-3-4"}, {WellKnownCapability::kVideosLibrary, L"S-1-15-3-5"}, {WellKnownCapability::kMusicLibrary, L"S-1-15-3-6"}, {WellKnownCapability::kDocumentsLibrary, L"S-1-15-3-7"}, {WellKnownCapability::kEnterpriseAuthentication, L"S-1-15-3-8"}, {WellKnownCapability::kSharedUserCertificates, L"S-1-15-3-9"}, {WellKnownCapability::kRemovableStorage, L"S-1-15-3-10"}, {WellKnownCapability::kAppointments, L"S-1-15-3-11"}, {WellKnownCapability::kContacts, L"S-1-15-3-12"}, }; for (auto capability : capabilities) { EXPECT_TRUE(EqualSid(Sid::FromKnownCapability(capability.capability), capability.sddl_sid)) << "Known Capability: " << capability.sddl_sid; EXPECT_TRUE(EqualSid(Sid(capability.capability), capability.sddl_sid)) << "Known Capability: " << capability.sddl_sid; } } TEST(SidTest, NamedCapability) { if (!GetDeriveCapabilitySidsFromName()) { GTEST_SKIP() << "Platform doesn't support DeriveCapabilitySidsFromName function."; } const std::wstring capabilities[] = {L"", L"InternetClient", L"InternetClientServer", L"PrivateNetworkClientServer", L"PicturesLibrary", L"VideosLibrary", L"MusicLibrary", L"DocumentsLibrary", L"EnterpriseAuthentication", L"SharedUserCertificates", L"RemovableStorage", L"Appointments", L"Contacts", L"registryRead", L"lpacCryptoServices"}; for (const std::wstring& capability : capabilities) { EXPECT_TRUE( EqualNamedCapSid(Sid::FromNamedCapability(capability), capability)) << "Named Capability: " << capability; } } TEST(SidTest, KnownSids) { const KnownSidTestEntry known_sids[] = { {WellKnownSid::kNull, ::WinNullSid}, {WellKnownSid::kWorld, ::WinWorldSid}, {WellKnownSid::kCreatorOwner, ::WinCreatorOwnerSid}, {WellKnownSid::kNetwork, ::WinNetworkSid}, {WellKnownSid::kBatch, ::WinBatchSid}, {WellKnownSid::kInteractive, ::WinInteractiveSid}, {WellKnownSid::kService, ::WinServiceSid}, {WellKnownSid::kAnonymous, ::WinAnonymousSid}, {WellKnownSid::kSelf, ::WinSelfSid}, {WellKnownSid::kAuthenticatedUser, ::WinAuthenticatedUserSid}, {WellKnownSid::kRestricted, ::WinRestrictedCodeSid}, {WellKnownSid::kLocalSystem, ::WinLocalSystemSid}, {WellKnownSid::kLocalService, ::WinLocalServiceSid}, {WellKnownSid::kNetworkService, ::WinNetworkServiceSid}, {WellKnownSid::kBuiltinAdministrators, ::WinBuiltinAdministratorsSid}, {WellKnownSid::kBuiltinUsers, ::WinBuiltinUsersSid}, {WellKnownSid::kBuiltinGuests, ::WinBuiltinGuestsSid}, {WellKnownSid::kUntrustedLabel, ::WinUntrustedLabelSid}, {WellKnownSid::kLowLabel, ::WinLowLabelSid}, {WellKnownSid::kMediumLabel, ::WinMediumLabelSid}, {WellKnownSid::kHighLabel, ::WinHighLabelSid}, {WellKnownSid::kSystemLabel, ::WinSystemLabelSid}, {WellKnownSid::kWriteRestricted, ::WinWriteRestrictedCodeSid}, {WellKnownSid::kCreatorOwnerRights, ::WinCreatorOwnerRightsSid}, {WellKnownSid::kAllApplicationPackages, ::WinBuiltinAnyPackageSid}}; for (auto known_sid : known_sids) { EXPECT_TRUE( EqualSid(Sid::FromKnownSid(known_sid.sid), known_sid.well_known_sid)) << "Known Sid: " << static_cast(known_sid.sid); EXPECT_TRUE(EqualSid(Sid(known_sid.sid), known_sid.well_known_sid)) << "Known Sid: " << static_cast(known_sid.sid); } EXPECT_TRUE(EqualSid( Sid::FromKnownSid(WellKnownSid::kAllRestrictedApplicationPackages), L"S-1-15-2-2")); } TEST(SidTest, SddlString) { std::optional sid_sddl = Sid::FromSddlString(L"S-1-1-0"); ASSERT_TRUE(sid_sddl); std::optional sddl_str = sid_sddl->ToSddlString(); ASSERT_TRUE(sddl_str); ASSERT_EQ(L"S-1-1-0", *sddl_str); ASSERT_FALSE(Sid::FromSddlString(L"X-1-1-0")); ASSERT_FALSE(Sid::FromSddlString(L"")); } TEST(SidTest, RandomSid) { Sid sid1 = Sid::GenerateRandomSid(); Sid sid2 = Sid::GenerateRandomSid(); EXPECT_NE(sid1, sid2); } TEST(SidTest, FromIntegrityLevel) { ASSERT_TRUE(EqualSid( Sid::FromIntegrityLevel(SECURITY_MANDATORY_UNTRUSTED_RID), L"S-1-16-0")); ASSERT_TRUE(EqualSid(Sid::FromIntegrityLevel(SECURITY_MANDATORY_LOW_RID), L"S-1-16-4096")); ASSERT_TRUE(EqualSid(Sid::FromIntegrityLevel(SECURITY_MANDATORY_MEDIUM_RID), L"S-1-16-8192")); ASSERT_TRUE( EqualSid(Sid::FromIntegrityLevel(SECURITY_MANDATORY_MEDIUM_PLUS_RID), L"S-1-16-8448")); ASSERT_TRUE(EqualSid(Sid::FromIntegrityLevel(SECURITY_MANDATORY_HIGH_RID), L"S-1-16-12288")); ASSERT_TRUE(EqualSid(Sid::FromIntegrityLevel(SECURITY_MANDATORY_SYSTEM_RID), L"S-1-16-16384")); ASSERT_TRUE(EqualSid(Sid::FromIntegrityLevel(1234), L"S-1-16-1234")); } TEST(SidTest, FromSddlStringVector) { ASSERT_TRUE( TestFromSddlStringVector({L"S-1-1-0", L"S-1-15-2-2", L"S-1-15-3-2"})); ASSERT_FALSE( TestFromSddlStringVector({L"S-1-1-0", L"X-1-15-2-2", L"S-1-15-3-2"})); ASSERT_FALSE(TestFromSddlStringVector({L""})); ASSERT_TRUE(TestFromSddlStringVector({})); } TEST(SidTest, FromNamedCapabilityVector) { if (!GetDeriveCapabilitySidsFromName()) { GTEST_SKIP() << "Platform doesn't support DeriveCapabilitySidsFromName function."; } std::vector capabilities = {L"", L"InternetClient", L"InternetClientServer", L"PrivateNetworkClientServer", L"PicturesLibrary", L"VideosLibrary", L"MusicLibrary", L"DocumentsLibrary", L"EnterpriseAuthentication", L"SharedUserCertificates", L"RemovableStorage", L"Appointments", L"Contacts", L"registryRead", L"lpacCryptoServices"}; ASSERT_TRUE(ranges::equal(Sid::FromNamedCapabilityVector(capabilities), capabilities, EqualNamedCapSid)); EXPECT_EQ(Sid::FromNamedCapabilityVector({}).size(), 0U); } TEST(SidTest, FromKnownCapabilityVector) { ASSERT_TRUE(TestSidVector( Sid::FromKnownCapabilityVector( {WellKnownCapability::kInternetClient, WellKnownCapability::kInternetClientServer, WellKnownCapability::kPrivateNetworkClientServer, WellKnownCapability::kPicturesLibrary, WellKnownCapability::kVideosLibrary, WellKnownCapability::kMusicLibrary, WellKnownCapability::kDocumentsLibrary, WellKnownCapability::kEnterpriseAuthentication, WellKnownCapability::kSharedUserCertificates, WellKnownCapability::kRemovableStorage, WellKnownCapability::kAppointments, WellKnownCapability::kContacts}), {L"S-1-15-3-1", L"S-1-15-3-2", L"S-1-15-3-3", L"S-1-15-3-4", L"S-1-15-3-5", L"S-1-15-3-6", L"S-1-15-3-7", L"S-1-15-3-8", L"S-1-15-3-9", L"S-1-15-3-10", L"S-1-15-3-11", L"S-1-15-3-12"})); ASSERT_FALSE(TestSidVector( Sid::FromKnownCapabilityVector({WellKnownCapability::kInternetClient}), {L"S-1-1-0"})); } TEST(SidTest, FromKnownSidVector) { ASSERT_TRUE(TestSidVector( Sid::FromKnownSidVector({WellKnownSid::kNull, WellKnownSid::kWorld}), {L"S-1-0-0", L"S-1-1-0"})); ASSERT_FALSE(TestSidVector(Sid::FromKnownSidVector({WellKnownSid::kNull}), {L"S-1-1-0"})); } TEST(SidTest, Equal) { Sid world_sid = Sid::FromKnownSid(WellKnownSid::kWorld); EXPECT_EQ(world_sid, world_sid); auto world_sid_sddl = Sid::FromSddlString(L"S-1-1-0"); ASSERT_TRUE(world_sid_sddl); EXPECT_EQ(world_sid, world_sid_sddl); EXPECT_EQ(world_sid_sddl, world_sid); EXPECT_TRUE(world_sid.Equal(world_sid_sddl->GetPSID())); EXPECT_TRUE(world_sid_sddl->Equal(world_sid.GetPSID())); Sid null_sid = Sid::FromKnownSid(WellKnownSid::kNull); EXPECT_NE(world_sid, null_sid); EXPECT_NE(null_sid, world_sid); EXPECT_FALSE(world_sid.Equal(null_sid.GetPSID())); EXPECT_FALSE(null_sid.Equal(world_sid.GetPSID())); } TEST(SidTest, Clone) { Sid world_sid = Sid::FromKnownSid(WellKnownSid::kWorld); auto world_sid_clone = world_sid.Clone(); EXPECT_NE(world_sid.GetPSID(), world_sid_clone.GetPSID()); EXPECT_EQ(world_sid, world_sid_clone); } } // namespace base::win