1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // This file contains unit tests for the sid class.
6
7 #include "base/win/sid.h"
8
9 #include <windows.h>
10
11 #include <sddl.h>
12
13 #include <optional>
14
15 #include "base/ranges/algorithm.h"
16 #include "base/win/atl.h"
17 #include "base/win/scoped_handle.h"
18 #include "base/win/scoped_localalloc.h"
19 #include "base/win/win_util.h"
20 #include "build/branding_buildflags.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 namespace base::win {
24
25 namespace {
26
EqualSid(const std::optional<Sid> & sid,const ATL::CSid & compare_sid)27 bool EqualSid(const std::optional<Sid>& sid, const ATL::CSid& compare_sid) {
28 if (!sid)
29 return false;
30 return sid->Equal(const_cast<SID*>(compare_sid.GetPSID()));
31 }
32
EqualSid(const Sid & sid,const std::wstring & sddl_sid)33 bool EqualSid(const Sid& sid, const std::wstring& sddl_sid) {
34 PSID compare_sid;
35 if (!::ConvertStringSidToSid(sddl_sid.c_str(), &compare_sid)) {
36 return false;
37 }
38 auto sid_ptr = TakeLocalAlloc(compare_sid);
39 return sid.Equal(sid_ptr.get());
40 }
41
EqualSid(const std::optional<Sid> & sid,WELL_KNOWN_SID_TYPE known_sid)42 bool EqualSid(const std::optional<Sid>& sid, WELL_KNOWN_SID_TYPE known_sid) {
43 if (!sid)
44 return false;
45 char known_sid_buffer[SECURITY_MAX_SID_SIZE] = {};
46 DWORD size = SECURITY_MAX_SID_SIZE;
47 if (!::CreateWellKnownSid(known_sid, nullptr, known_sid_buffer, &size))
48 return false;
49
50 return sid->Equal(known_sid_buffer);
51 }
52
TestSidVector(std::optional<std::vector<Sid>> sids,const std::vector<std::wstring> & sddl)53 bool TestSidVector(std::optional<std::vector<Sid>> sids,
54 const std::vector<std::wstring>& sddl) {
55 return sids && ranges::equal(*sids, sddl,
56 [](const Sid& sid, const std::wstring& sddl) {
57 return EqualSid(sid, sddl);
58 });
59 }
60
TestFromSddlStringVector(const std::vector<std::wstring> sddl)61 bool TestFromSddlStringVector(const std::vector<std::wstring> sddl) {
62 return TestSidVector(Sid::FromSddlStringVector(sddl), sddl);
63 }
64
65 typedef decltype(::DeriveCapabilitySidsFromName)*
66 DeriveCapabilitySidsFromNameFunc;
67
68 // Get the DeriveCapabilitySidsFromName API dynamically. Versions of Windows 10
69 // older than 1809 do not implement this method. By loading dynamically we can
70 // skip tests when running on these older versions. Online documentation for
71 // this API claims it's supported back to Windows 2003, however this is entirely
72 // incorrect.
GetDeriveCapabilitySidsFromName()73 DeriveCapabilitySidsFromNameFunc GetDeriveCapabilitySidsFromName() {
74 static const DeriveCapabilitySidsFromNameFunc derive_capability_sids =
75 []() -> DeriveCapabilitySidsFromNameFunc {
76 HMODULE module = GetModuleHandle(L"api-ms-win-security-base-l1-2-2.dll");
77 if (!module) {
78 return nullptr;
79 }
80 return reinterpret_cast<DeriveCapabilitySidsFromNameFunc>(
81 ::GetProcAddress(module, "DeriveCapabilitySidsFromName"));
82 }();
83
84 return derive_capability_sids;
85 }
86
EqualNamedCapSid(const Sid & sid,const std::wstring & capability_name)87 bool EqualNamedCapSid(const Sid& sid, const std::wstring& capability_name) {
88 DeriveCapabilitySidsFromNameFunc derive_capability_sids =
89 GetDeriveCapabilitySidsFromName();
90 CHECK(derive_capability_sids);
91
92 // Pre-reserve some space for SID deleters.
93 std::vector<base::win::ScopedLocalAlloc> deleter_list;
94 deleter_list.reserve(16);
95
96 PSID* capability_groups = nullptr;
97 DWORD capability_group_count = 0;
98 PSID* capability_sids = nullptr;
99 DWORD capability_sid_count = 0;
100
101 CHECK(derive_capability_sids(capability_name.c_str(), &capability_groups,
102 &capability_group_count, &capability_sids,
103 &capability_sid_count));
104 deleter_list.emplace_back(capability_groups);
105 deleter_list.emplace_back(capability_sids);
106
107 for (DWORD i = 0; i < capability_group_count; ++i) {
108 deleter_list.emplace_back(capability_groups[i]);
109 }
110 for (DWORD i = 0; i < capability_sid_count; ++i) {
111 deleter_list.emplace_back(capability_sids[i]);
112 }
113
114 CHECK_GE(capability_sid_count, 1U);
115 return sid.Equal(capability_sids[0]);
116 }
117
118 struct KnownCapabilityTestEntry {
119 WellKnownCapability capability;
120 const wchar_t* sddl_sid;
121 };
122
123 struct KnownSidTestEntry {
124 WellKnownSid sid;
125 WELL_KNOWN_SID_TYPE well_known_sid;
126 };
127
128 } // namespace
129
130 // Tests the creation of a Sid.
TEST(SidTest,Initializers)131 TEST(SidTest, Initializers) {
132 ATL::CSid sid_world = ATL::Sids::World();
133 PSID sid_world_pointer = const_cast<SID*>(sid_world.GetPSID());
134
135 // Check the PSID constructor.
136 std::optional<Sid> sid_sid_star = Sid::FromPSID(sid_world_pointer);
137 ASSERT_TRUE(EqualSid(sid_sid_star, sid_world));
138
139 char invalid_sid[16] = {};
140 ASSERT_FALSE(Sid::FromPSID(invalid_sid));
141
142 std::optional<Sid> sid_sddl = Sid::FromSddlString(L"S-1-1-0");
143 ASSERT_TRUE(sid_sddl);
144 ASSERT_TRUE(EqualSid(sid_sddl, sid_world));
145 }
146
TEST(SidTest,KnownCapability)147 TEST(SidTest, KnownCapability) {
148 const KnownCapabilityTestEntry capabilities[] = {
149 {WellKnownCapability::kInternetClient, L"S-1-15-3-1"},
150 {WellKnownCapability::kInternetClientServer, L"S-1-15-3-2"},
151 {WellKnownCapability::kPrivateNetworkClientServer, L"S-1-15-3-3"},
152 {WellKnownCapability::kPicturesLibrary, L"S-1-15-3-4"},
153 {WellKnownCapability::kVideosLibrary, L"S-1-15-3-5"},
154 {WellKnownCapability::kMusicLibrary, L"S-1-15-3-6"},
155 {WellKnownCapability::kDocumentsLibrary, L"S-1-15-3-7"},
156 {WellKnownCapability::kEnterpriseAuthentication, L"S-1-15-3-8"},
157 {WellKnownCapability::kSharedUserCertificates, L"S-1-15-3-9"},
158 {WellKnownCapability::kRemovableStorage, L"S-1-15-3-10"},
159 {WellKnownCapability::kAppointments, L"S-1-15-3-11"},
160 {WellKnownCapability::kContacts, L"S-1-15-3-12"},
161 };
162
163 for (auto capability : capabilities) {
164 EXPECT_TRUE(EqualSid(Sid::FromKnownCapability(capability.capability),
165 capability.sddl_sid))
166 << "Known Capability: " << capability.sddl_sid;
167 EXPECT_TRUE(EqualSid(Sid(capability.capability), capability.sddl_sid))
168 << "Known Capability: " << capability.sddl_sid;
169 }
170 }
171
TEST(SidTest,NamedCapability)172 TEST(SidTest, NamedCapability) {
173 if (!GetDeriveCapabilitySidsFromName()) {
174 GTEST_SKIP()
175 << "Platform doesn't support DeriveCapabilitySidsFromName function.";
176 }
177 const std::wstring capabilities[] = {L"",
178 L"InternetClient",
179 L"InternetClientServer",
180 L"PrivateNetworkClientServer",
181 L"PicturesLibrary",
182 L"VideosLibrary",
183 L"MusicLibrary",
184 L"DocumentsLibrary",
185 L"EnterpriseAuthentication",
186 L"SharedUserCertificates",
187 L"RemovableStorage",
188 L"Appointments",
189 L"Contacts",
190 L"registryRead",
191 L"lpacCryptoServices"};
192
193 for (const std::wstring& capability : capabilities) {
194 EXPECT_TRUE(
195 EqualNamedCapSid(Sid::FromNamedCapability(capability), capability))
196 << "Named Capability: " << capability;
197 }
198 }
199
TEST(SidTest,KnownSids)200 TEST(SidTest, KnownSids) {
201 const KnownSidTestEntry known_sids[] = {
202 {WellKnownSid::kNull, ::WinNullSid},
203 {WellKnownSid::kWorld, ::WinWorldSid},
204 {WellKnownSid::kCreatorOwner, ::WinCreatorOwnerSid},
205 {WellKnownSid::kNetwork, ::WinNetworkSid},
206 {WellKnownSid::kBatch, ::WinBatchSid},
207 {WellKnownSid::kInteractive, ::WinInteractiveSid},
208 {WellKnownSid::kService, ::WinServiceSid},
209 {WellKnownSid::kAnonymous, ::WinAnonymousSid},
210 {WellKnownSid::kSelf, ::WinSelfSid},
211 {WellKnownSid::kAuthenticatedUser, ::WinAuthenticatedUserSid},
212 {WellKnownSid::kRestricted, ::WinRestrictedCodeSid},
213 {WellKnownSid::kLocalSystem, ::WinLocalSystemSid},
214 {WellKnownSid::kLocalService, ::WinLocalServiceSid},
215 {WellKnownSid::kNetworkService, ::WinNetworkServiceSid},
216 {WellKnownSid::kBuiltinAdministrators, ::WinBuiltinAdministratorsSid},
217 {WellKnownSid::kBuiltinUsers, ::WinBuiltinUsersSid},
218 {WellKnownSid::kBuiltinGuests, ::WinBuiltinGuestsSid},
219 {WellKnownSid::kUntrustedLabel, ::WinUntrustedLabelSid},
220 {WellKnownSid::kLowLabel, ::WinLowLabelSid},
221 {WellKnownSid::kMediumLabel, ::WinMediumLabelSid},
222 {WellKnownSid::kHighLabel, ::WinHighLabelSid},
223 {WellKnownSid::kSystemLabel, ::WinSystemLabelSid},
224 {WellKnownSid::kWriteRestricted, ::WinWriteRestrictedCodeSid},
225 {WellKnownSid::kCreatorOwnerRights, ::WinCreatorOwnerRightsSid},
226 {WellKnownSid::kAllApplicationPackages, ::WinBuiltinAnyPackageSid}};
227
228 for (auto known_sid : known_sids) {
229 EXPECT_TRUE(
230 EqualSid(Sid::FromKnownSid(known_sid.sid), known_sid.well_known_sid))
231 << "Known Sid: " << static_cast<int>(known_sid.sid);
232 EXPECT_TRUE(EqualSid(Sid(known_sid.sid), known_sid.well_known_sid))
233 << "Known Sid: " << static_cast<int>(known_sid.sid);
234 }
235
236 EXPECT_TRUE(EqualSid(
237 Sid::FromKnownSid(WellKnownSid::kAllRestrictedApplicationPackages),
238 L"S-1-15-2-2"));
239 }
240
TEST(SidTest,SddlString)241 TEST(SidTest, SddlString) {
242 std::optional<Sid> sid_sddl = Sid::FromSddlString(L"S-1-1-0");
243 ASSERT_TRUE(sid_sddl);
244 std::optional<std::wstring> sddl_str = sid_sddl->ToSddlString();
245 ASSERT_TRUE(sddl_str);
246 ASSERT_EQ(L"S-1-1-0", *sddl_str);
247 ASSERT_FALSE(Sid::FromSddlString(L"X-1-1-0"));
248 ASSERT_FALSE(Sid::FromSddlString(L""));
249 }
250
TEST(SidTest,RandomSid)251 TEST(SidTest, RandomSid) {
252 Sid sid1 = Sid::GenerateRandomSid();
253 Sid sid2 = Sid::GenerateRandomSid();
254 EXPECT_NE(sid1, sid2);
255 }
256
TEST(SidTest,FromIntegrityLevel)257 TEST(SidTest, FromIntegrityLevel) {
258 ASSERT_TRUE(EqualSid(
259 Sid::FromIntegrityLevel(SECURITY_MANDATORY_UNTRUSTED_RID), L"S-1-16-0"));
260 ASSERT_TRUE(EqualSid(Sid::FromIntegrityLevel(SECURITY_MANDATORY_LOW_RID),
261 L"S-1-16-4096"));
262 ASSERT_TRUE(EqualSid(Sid::FromIntegrityLevel(SECURITY_MANDATORY_MEDIUM_RID),
263 L"S-1-16-8192"));
264 ASSERT_TRUE(
265 EqualSid(Sid::FromIntegrityLevel(SECURITY_MANDATORY_MEDIUM_PLUS_RID),
266 L"S-1-16-8448"));
267 ASSERT_TRUE(EqualSid(Sid::FromIntegrityLevel(SECURITY_MANDATORY_HIGH_RID),
268 L"S-1-16-12288"));
269 ASSERT_TRUE(EqualSid(Sid::FromIntegrityLevel(SECURITY_MANDATORY_SYSTEM_RID),
270 L"S-1-16-16384"));
271 ASSERT_TRUE(EqualSid(Sid::FromIntegrityLevel(1234), L"S-1-16-1234"));
272 }
273
TEST(SidTest,FromSddlStringVector)274 TEST(SidTest, FromSddlStringVector) {
275 ASSERT_TRUE(
276 TestFromSddlStringVector({L"S-1-1-0", L"S-1-15-2-2", L"S-1-15-3-2"}));
277 ASSERT_FALSE(
278 TestFromSddlStringVector({L"S-1-1-0", L"X-1-15-2-2", L"S-1-15-3-2"}));
279 ASSERT_FALSE(TestFromSddlStringVector({L""}));
280 ASSERT_TRUE(TestFromSddlStringVector({}));
281 }
282
TEST(SidTest,FromNamedCapabilityVector)283 TEST(SidTest, FromNamedCapabilityVector) {
284 if (!GetDeriveCapabilitySidsFromName()) {
285 GTEST_SKIP()
286 << "Platform doesn't support DeriveCapabilitySidsFromName function.";
287 }
288 std::vector<std::wstring> capabilities = {L"",
289 L"InternetClient",
290 L"InternetClientServer",
291 L"PrivateNetworkClientServer",
292 L"PicturesLibrary",
293 L"VideosLibrary",
294 L"MusicLibrary",
295 L"DocumentsLibrary",
296 L"EnterpriseAuthentication",
297 L"SharedUserCertificates",
298 L"RemovableStorage",
299 L"Appointments",
300 L"Contacts",
301 L"registryRead",
302 L"lpacCryptoServices"};
303
304 ASSERT_TRUE(ranges::equal(Sid::FromNamedCapabilityVector(capabilities),
305 capabilities, EqualNamedCapSid));
306 EXPECT_EQ(Sid::FromNamedCapabilityVector({}).size(), 0U);
307 }
308
TEST(SidTest,FromKnownCapabilityVector)309 TEST(SidTest, FromKnownCapabilityVector) {
310 ASSERT_TRUE(TestSidVector(
311 Sid::FromKnownCapabilityVector(
312 {WellKnownCapability::kInternetClient,
313 WellKnownCapability::kInternetClientServer,
314 WellKnownCapability::kPrivateNetworkClientServer,
315 WellKnownCapability::kPicturesLibrary,
316 WellKnownCapability::kVideosLibrary,
317 WellKnownCapability::kMusicLibrary,
318 WellKnownCapability::kDocumentsLibrary,
319 WellKnownCapability::kEnterpriseAuthentication,
320 WellKnownCapability::kSharedUserCertificates,
321 WellKnownCapability::kRemovableStorage,
322 WellKnownCapability::kAppointments, WellKnownCapability::kContacts}),
323 {L"S-1-15-3-1", L"S-1-15-3-2", L"S-1-15-3-3", L"S-1-15-3-4",
324 L"S-1-15-3-5", L"S-1-15-3-6", L"S-1-15-3-7", L"S-1-15-3-8",
325 L"S-1-15-3-9", L"S-1-15-3-10", L"S-1-15-3-11", L"S-1-15-3-12"}));
326
327 ASSERT_FALSE(TestSidVector(
328 Sid::FromKnownCapabilityVector({WellKnownCapability::kInternetClient}),
329 {L"S-1-1-0"}));
330 }
331
TEST(SidTest,FromKnownSidVector)332 TEST(SidTest, FromKnownSidVector) {
333 ASSERT_TRUE(TestSidVector(
334 Sid::FromKnownSidVector({WellKnownSid::kNull, WellKnownSid::kWorld}),
335 {L"S-1-0-0", L"S-1-1-0"}));
336
337 ASSERT_FALSE(TestSidVector(Sid::FromKnownSidVector({WellKnownSid::kNull}),
338 {L"S-1-1-0"}));
339 }
340
TEST(SidTest,Equal)341 TEST(SidTest, Equal) {
342 Sid world_sid = Sid::FromKnownSid(WellKnownSid::kWorld);
343 EXPECT_EQ(world_sid, world_sid);
344 auto world_sid_sddl = Sid::FromSddlString(L"S-1-1-0");
345 ASSERT_TRUE(world_sid_sddl);
346 EXPECT_EQ(world_sid, world_sid_sddl);
347 EXPECT_EQ(world_sid_sddl, world_sid);
348 EXPECT_TRUE(world_sid.Equal(world_sid_sddl->GetPSID()));
349 EXPECT_TRUE(world_sid_sddl->Equal(world_sid.GetPSID()));
350 Sid null_sid = Sid::FromKnownSid(WellKnownSid::kNull);
351 EXPECT_NE(world_sid, null_sid);
352 EXPECT_NE(null_sid, world_sid);
353 EXPECT_FALSE(world_sid.Equal(null_sid.GetPSID()));
354 EXPECT_FALSE(null_sid.Equal(world_sid.GetPSID()));
355 }
356
TEST(SidTest,Clone)357 TEST(SidTest, Clone) {
358 Sid world_sid = Sid::FromKnownSid(WellKnownSid::kWorld);
359 auto world_sid_clone = world_sid.Clone();
360 EXPECT_NE(world_sid.GetPSID(), world_sid_clone.GetPSID());
361 EXPECT_EQ(world_sid, world_sid_clone);
362 }
363
364 } // namespace base::win
365