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 #include "base/win/security_util.h"
6
7 // clang-format off
8 #include <windows.h> // Must be in front of other Windows header files.
9 // clang-format on
10
11 #include <aclapi.h>
12 #include <sddl.h>
13
14 #include <utility>
15
16 #include "base/files/file_util.h"
17 #include "base/files/scoped_temp_dir.h"
18 #include "base/strings/string_number_conversions_win.h"
19 #include "base/win/scoped_handle.h"
20 #include "base/win/scoped_localalloc.h"
21 #include "base/win/sid.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23
24 namespace base {
25 namespace win {
26
27 namespace {
28
29 constexpr wchar_t kBaseDacl[] = L"D:P(A;;FA;;;WD)";
30 constexpr wchar_t kTest1Dacl[] = L"D:PAI(A;;FR;;;AU)(A;;FA;;;WD)";
31 constexpr wchar_t kTest2Dacl[] = L"D:PAI(A;;FA;;;BA)(A;;FA;;;AU)(A;;FA;;;WD)";
32 constexpr wchar_t kTest1DenyDacl[] = L"D:PAI(D;;FR;;;LG)(A;;FA;;;WD)";
33 constexpr wchar_t kTest1DaclNoInherit[] = L"D:P(A;;FR;;;AU)(A;;FA;;;WD)";
34 constexpr wchar_t kTest2DaclNoInherit[] =
35 L"D:P(A;;FA;;;BA)(A;;FA;;;AU)(A;;FA;;;WD)";
36
37 constexpr wchar_t kBaseDirDacl[] = L"D:P(A;OICI;FA;;;WD)";
38 constexpr wchar_t kTest1InheritedDacl[] = L"D:(A;ID;FA;;;WD)";
39 constexpr wchar_t kBaseDir2Dacl[] = L"D:PAI(A;OICI;FR;;;AU)(A;OICI;FA;;;WD)";
40 constexpr wchar_t kTest2InheritedDacl[] = L"D:AI(A;ID;FR;;;AU)(A;ID;FA;;;WD)";
41 constexpr wchar_t kBaseDir2DaclNoInherit[] =
42 L"D:P(A;OICI;FR;;;AU)(A;OICI;FA;;;WD)";
43 constexpr wchar_t kTest2InheritedDaclNoInherit[] = L"D:P(A;;FA;;;WD)";
44 constexpr wchar_t kTest3InheritedDacl[] = L"D:(A;ID;FR;;;AU)(A;ID;FA;;;WD)";
45
46 constexpr wchar_t kNoWriteDacDacl[] = L"D:(D;;WD;;;OW)(A;;FRSD;;;WD)";
47
48 constexpr wchar_t kAuthenticatedUsersSid[] = L"AU";
49 constexpr wchar_t kLocalGuestSid[] = L"LG";
50
GetFileDacl(const FilePath & path)51 std::wstring GetFileDacl(const FilePath& path) {
52 PSECURITY_DESCRIPTOR sd;
53 if (::GetNamedSecurityInfo(path.value().c_str(), SE_FILE_OBJECT,
54 DACL_SECURITY_INFORMATION, nullptr, nullptr,
55 nullptr, nullptr, &sd) != ERROR_SUCCESS) {
56 return std::wstring();
57 }
58 auto sd_ptr = TakeLocalAlloc(sd);
59 LPWSTR sddl;
60 if (!::ConvertSecurityDescriptorToStringSecurityDescriptor(
61 sd_ptr.get(), SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &sddl,
62 nullptr)) {
63 return std::wstring();
64 }
65 return TakeLocalAlloc(sddl).get();
66 }
67
CreateWithDacl(const FilePath & path,const wchar_t * sddl,bool directory)68 bool CreateWithDacl(const FilePath& path, const wchar_t* sddl, bool directory) {
69 PSECURITY_DESCRIPTOR sd;
70 if (!::ConvertStringSecurityDescriptorToSecurityDescriptor(
71 sddl, SDDL_REVISION_1, &sd, nullptr)) {
72 return false;
73 }
74 auto sd_ptr = TakeLocalAlloc(sd);
75 SECURITY_ATTRIBUTES security_attr = {};
76 security_attr.nLength = sizeof(security_attr);
77 security_attr.lpSecurityDescriptor = sd_ptr.get();
78 if (directory)
79 return !!::CreateDirectory(path.value().c_str(), &security_attr);
80
81 return ScopedHandle(::CreateFile(path.value().c_str(), GENERIC_ALL, 0,
82 &security_attr, CREATE_ALWAYS, 0, nullptr))
83 .is_valid();
84 }
85
86 } // namespace
87
TEST(SecurityUtilTest,GrantAccessToPathErrorCase)88 TEST(SecurityUtilTest, GrantAccessToPathErrorCase) {
89 ScopedTempDir temp_dir;
90 auto sids = Sid::FromSddlStringVector({kAuthenticatedUsersSid});
91 ASSERT_TRUE(sids);
92 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
93 FilePath path = temp_dir.GetPath().Append(L"test");
94 EXPECT_FALSE(
95 GrantAccessToPath(path, *sids, FILE_GENERIC_READ, NO_INHERITANCE, true));
96 EXPECT_FALSE(
97 GrantAccessToPath(path, *sids, FILE_GENERIC_READ, NO_INHERITANCE, false));
98 ASSERT_TRUE(CreateWithDacl(path, kBaseDacl, false));
99 EXPECT_TRUE(
100 GrantAccessToPath(path, *sids, FILE_GENERIC_READ, NO_INHERITANCE, true));
101 EXPECT_TRUE(
102 GrantAccessToPath(path, *sids, FILE_GENERIC_READ, NO_INHERITANCE, false));
103 std::vector<Sid> large_sid_list;
104 while (large_sid_list.size() < 0x10000) {
105 auto sid = Sid::FromSddlString(L"S-1-5-1234-" +
106 NumberToWString(large_sid_list.size()));
107 ASSERT_TRUE(sid);
108 large_sid_list.emplace_back(std::move(*sid));
109 }
110 EXPECT_FALSE(GrantAccessToPath(path, large_sid_list, FILE_GENERIC_READ,
111 NO_INHERITANCE, false));
112 path = temp_dir.GetPath().Append(L"test_nowritedac");
113 ASSERT_TRUE(CreateWithDacl(path, kNoWriteDacDacl, false));
114 EXPECT_FALSE(
115 GrantAccessToPath(path, *sids, FILE_GENERIC_READ, NO_INHERITANCE, true));
116 EXPECT_FALSE(
117 GrantAccessToPath(path, *sids, FILE_GENERIC_READ, NO_INHERITANCE, false));
118 }
119
TEST(SecurityUtilTest,GrantAccessToPathFile)120 TEST(SecurityUtilTest, GrantAccessToPathFile) {
121 ScopedTempDir temp_dir;
122 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
123 FilePath path = temp_dir.GetPath().Append(L"test");
124 ASSERT_TRUE(CreateWithDacl(path, kBaseDacl, false));
125 EXPECT_EQ(kBaseDacl, GetFileDacl(path));
126 auto sids = Sid::FromSddlStringVector({kAuthenticatedUsersSid});
127 ASSERT_TRUE(sids);
128 EXPECT_TRUE(
129 GrantAccessToPath(path, *sids, FILE_GENERIC_READ, NO_INHERITANCE, true));
130 EXPECT_EQ(kTest1Dacl, GetFileDacl(path));
131 auto sids2 = Sid::FromSddlStringVector({L"S-1-5-11", L"BA"});
132 ASSERT_TRUE(sids2);
133 EXPECT_TRUE(
134 GrantAccessToPath(path, *sids2, GENERIC_ALL, NO_INHERITANCE, true));
135 EXPECT_EQ(kTest2Dacl, GetFileDacl(path));
136 }
137
TEST(SecurityUtilTest,GrantAccessToPathFileNoInherit)138 TEST(SecurityUtilTest, GrantAccessToPathFileNoInherit) {
139 ScopedTempDir temp_dir;
140 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
141 FilePath path = temp_dir.GetPath().Append(L"test");
142 ASSERT_TRUE(CreateWithDacl(path, kBaseDacl, false));
143 EXPECT_EQ(kBaseDacl, GetFileDacl(path));
144 EXPECT_TRUE(
145 GrantAccessToPath(path, {}, FILE_GENERIC_READ, NO_INHERITANCE, false));
146 EXPECT_EQ(kBaseDacl, GetFileDacl(path));
147 auto sids = Sid::FromSddlStringVector({kAuthenticatedUsersSid});
148 ASSERT_TRUE(sids);
149 EXPECT_TRUE(
150 GrantAccessToPath(path, *sids, FILE_GENERIC_READ, NO_INHERITANCE, false));
151 EXPECT_EQ(kTest1DaclNoInherit, GetFileDacl(path));
152 auto sids2 = Sid::FromSddlStringVector({L"S-1-5-11", L"BA"});
153 ASSERT_TRUE(sids2);
154 EXPECT_TRUE(
155 GrantAccessToPath(path, *sids2, GENERIC_ALL, NO_INHERITANCE, false));
156 EXPECT_EQ(kTest2DaclNoInherit, GetFileDacl(path));
157 }
158
TEST(SecurityUtilTest,DenyAccessToPathFile)159 TEST(SecurityUtilTest, DenyAccessToPathFile) {
160 ScopedTempDir temp_dir;
161 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
162 FilePath path = temp_dir.GetPath().Append(L"test");
163 ASSERT_TRUE(CreateWithDacl(path, kBaseDacl, false));
164 EXPECT_EQ(kBaseDacl, GetFileDacl(path));
165 EXPECT_TRUE(
166 DenyAccessToPath(path, {}, FILE_GENERIC_READ, NO_INHERITANCE, true));
167 EXPECT_EQ(kBaseDacl, GetFileDacl(path));
168 auto sids = Sid::FromSddlStringVector({kLocalGuestSid});
169 ASSERT_TRUE(sids);
170 EXPECT_TRUE(
171 DenyAccessToPath(path, *sids, FILE_GENERIC_READ, NO_INHERITANCE, true));
172 EXPECT_EQ(kTest1DenyDacl, GetFileDacl(path));
173 }
174
TEST(SecurityUtilTest,DenyAccessToPathFileMultiple)175 TEST(SecurityUtilTest, DenyAccessToPathFileMultiple) {
176 ScopedTempDir temp_dir;
177 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
178 FilePath path = temp_dir.GetPath().Append(L"test");
179 ASSERT_TRUE(CreateWithDacl(path, kBaseDacl, false));
180 EXPECT_EQ(kBaseDacl, GetFileDacl(path));
181 auto sids = Sid::FromSddlStringVector({kLocalGuestSid});
182 ASSERT_TRUE(sids);
183 EXPECT_TRUE(
184 DenyAccessToPath(path, *sids, FILE_GENERIC_READ, NO_INHERITANCE, true));
185 // Verify setting same ACE on same file does not change the ACL.
186 EXPECT_TRUE(
187 DenyAccessToPath(path, *sids, FILE_GENERIC_READ, NO_INHERITANCE, true));
188 EXPECT_TRUE(
189 DenyAccessToPath(path, *sids, FILE_GENERIC_READ, NO_INHERITANCE, true));
190 EXPECT_EQ(kTest1DenyDacl, GetFileDacl(path));
191 }
192
TEST(SecurityUtilTest,GrantAccessToPathDirectory)193 TEST(SecurityUtilTest, GrantAccessToPathDirectory) {
194 ScopedTempDir temp_dir;
195 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
196 FilePath path = temp_dir.GetPath().Append(L"testdir");
197 ASSERT_TRUE(CreateWithDacl(path, kBaseDirDacl, true));
198 EXPECT_EQ(kBaseDirDacl, GetFileDacl(path));
199 FilePath file_path = path.Append(L"test");
200 File file(file_path, File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE);
201 ASSERT_TRUE(file.IsValid());
202 file.Close();
203 EXPECT_EQ(kTest1InheritedDacl, GetFileDacl(file_path));
204 auto sids = Sid::FromSddlStringVector({kAuthenticatedUsersSid});
205 ASSERT_TRUE(sids);
206 EXPECT_TRUE(GrantAccessToPath(path, *sids, FILE_GENERIC_READ,
207 OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE,
208 true));
209 EXPECT_EQ(kBaseDir2Dacl, GetFileDacl(path));
210 EXPECT_EQ(kTest2InheritedDacl, GetFileDacl(file_path));
211 }
212
TEST(SecurityUtilTest,GrantAccessToPathDirectoryNoInherit)213 TEST(SecurityUtilTest, GrantAccessToPathDirectoryNoInherit) {
214 ScopedTempDir temp_dir;
215 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
216 FilePath path = temp_dir.GetPath().Append(L"testdir");
217 ASSERT_TRUE(CreateWithDacl(path, kBaseDirDacl, true));
218 EXPECT_EQ(kBaseDirDacl, GetFileDacl(path));
219 FilePath file_path = path.Append(L"test");
220 File file(file_path, File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE);
221 ASSERT_TRUE(file.IsValid());
222 file.Close();
223 EXPECT_EQ(kTest1InheritedDacl, GetFileDacl(file_path));
224 auto sids = Sid::FromSddlStringVector({kAuthenticatedUsersSid});
225 ASSERT_TRUE(sids);
226 EXPECT_TRUE(GrantAccessToPath(path, *sids, FILE_GENERIC_READ,
227 OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE,
228 false));
229 EXPECT_EQ(kBaseDir2DaclNoInherit, GetFileDacl(path));
230 EXPECT_EQ(kTest2InheritedDaclNoInherit, GetFileDacl(file_path));
231
232 FilePath file_path2 = path.Append(L"test2");
233 File file2(file_path2, File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE);
234 ASSERT_TRUE(file2.IsValid());
235 file2.Close();
236 EXPECT_EQ(kTest3InheritedDacl, GetFileDacl(file_path2));
237 }
238
TEST(SecurityUtilTest,CloneSidVector)239 TEST(SecurityUtilTest, CloneSidVector) {
240 std::vector<Sid> sids =
241 Sid::FromKnownSidVector({WellKnownSid::kNull, WellKnownSid::kWorld});
242 std::vector<Sid> clone = CloneSidVector(sids);
243 ASSERT_EQ(sids.size(), clone.size());
244 for (size_t index = 0; index < sids.size(); ++index) {
245 ASSERT_EQ(sids[index], clone[index]);
246 ASSERT_NE(sids[index].GetPSID(), clone[index].GetPSID());
247 }
248 ASSERT_EQ(CloneSidVector(std::vector<Sid>()).size(), 0U);
249 }
250
TEST(SecurityUtilTest,AppendSidVector)251 TEST(SecurityUtilTest, AppendSidVector) {
252 std::vector<Sid> sids =
253 Sid::FromKnownSidVector({WellKnownSid::kNull, WellKnownSid::kWorld});
254
255 std::vector<Sid> total_sids;
256 AppendSidVector(total_sids, sids);
257 EXPECT_EQ(total_sids.size(), sids.size());
258
259 std::vector<Sid> sids2 = Sid::FromKnownSidVector(
260 {WellKnownSid::kCreatorOwner, WellKnownSid::kNetwork});
261 AppendSidVector(total_sids, sids2);
262 EXPECT_EQ(total_sids.size(), sids.size() + sids2.size());
263
264 auto sid_interator = total_sids.cbegin();
265 for (size_t index = 0; index < sids.size(); ++index) {
266 ASSERT_EQ(*sid_interator, sids[index]);
267 ASSERT_NE(sid_interator->GetPSID(), sids[index].GetPSID());
268 sid_interator++;
269 }
270 for (size_t index = 0; index < sids2.size(); ++index) {
271 ASSERT_EQ(*sid_interator, sids2[index]);
272 ASSERT_NE(sid_interator->GetPSID(), sids2[index].GetPSID());
273 sid_interator++;
274 }
275 }
276
TEST(SecurityUtilTest,GetGrantedAccess)277 TEST(SecurityUtilTest, GetGrantedAccess) {
278 EXPECT_FALSE(GetGrantedAccess(nullptr));
279 ScopedHandle handle(::CreateMutexEx(nullptr, nullptr, 0, MUTEX_MODIFY_STATE));
280 EXPECT_EQ(GetGrantedAccess(handle.get()), DWORD{MUTEX_MODIFY_STATE});
281 handle.Set(::CreateMutexEx(nullptr, nullptr, 0, READ_CONTROL));
282 EXPECT_EQ(GetGrantedAccess(handle.get()), DWORD{READ_CONTROL});
283 handle.Set(::CreateMutexEx(nullptr, nullptr, 0, GENERIC_ALL));
284 EXPECT_EQ(GetGrantedAccess(handle.get()), DWORD{MUTEX_ALL_ACCESS});
285 }
286
287 } // namespace win
288 } // namespace base
289