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