OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
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 "sandbox/win/src/restricted_token.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <vector> | |
10 | |
11 #include "base/logging.h" | |
12 #include "base/memory/scoped_ptr.h" | |
13 #include "sandbox/win/src/acl.h" | |
14 #include "sandbox/win/src/win_utils.h" | |
15 | |
16 namespace { | |
17 | |
18 // Calls GetTokenInformation with the desired |info_class| and returns a buffer | |
19 // with the result. | |
20 scoped_ptr<BYTE[]> GetTokenInfo(const base::win::ScopedHandle& token, | |
21 TOKEN_INFORMATION_CLASS info_class, | |
22 DWORD* error) { | |
23 // Get the required buffer size. | |
24 DWORD size = 0; | |
25 ::GetTokenInformation(token.Get(), info_class, NULL, 0, &size); | |
26 if (!size) { | |
27 *error = ::GetLastError(); | |
28 return nullptr; | |
29 } | |
30 | |
31 scoped_ptr<BYTE[]> buffer(new BYTE[size]); | |
32 if (!::GetTokenInformation(token.Get(), info_class, buffer.get(), size, | |
33 &size)) { | |
34 *error = ::GetLastError(); | |
35 return nullptr; | |
36 } | |
37 | |
38 *error = ERROR_SUCCESS; | |
39 return buffer; | |
40 } | |
41 | |
42 } // namespace | |
43 | |
44 namespace sandbox { | |
45 | |
46 RestrictedToken::RestrictedToken() | |
47 : integrity_level_(INTEGRITY_LEVEL_LAST), | |
48 init_(false), | |
49 lockdown_default_dacl_(false) {} | |
50 | |
51 RestrictedToken::~RestrictedToken() { | |
52 } | |
53 | |
54 DWORD RestrictedToken::Init(const HANDLE effective_token) { | |
55 if (init_) | |
56 return ERROR_ALREADY_INITIALIZED; | |
57 | |
58 HANDLE temp_token; | |
59 if (effective_token) { | |
60 // We duplicate the handle to be able to use it even if the original handle | |
61 // is closed. | |
62 if (!::DuplicateHandle(::GetCurrentProcess(), effective_token, | |
63 ::GetCurrentProcess(), &temp_token, | |
64 0, FALSE, DUPLICATE_SAME_ACCESS)) { | |
65 return ::GetLastError(); | |
66 } | |
67 } else { | |
68 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, | |
69 &temp_token)) { | |
70 return ::GetLastError(); | |
71 } | |
72 } | |
73 effective_token_.Set(temp_token); | |
74 | |
75 init_ = true; | |
76 return ERROR_SUCCESS; | |
77 } | |
78 | |
79 DWORD RestrictedToken::GetRestrictedToken( | |
80 base::win::ScopedHandle* token) const { | |
81 DCHECK(init_); | |
82 if (!init_) | |
83 return ERROR_NO_TOKEN; | |
84 | |
85 size_t deny_size = sids_for_deny_only_.size(); | |
86 size_t restrict_size = sids_to_restrict_.size(); | |
87 size_t privileges_size = privileges_to_disable_.size(); | |
88 | |
89 SID_AND_ATTRIBUTES *deny_only_array = NULL; | |
90 if (deny_size) { | |
91 deny_only_array = new SID_AND_ATTRIBUTES[deny_size]; | |
92 | |
93 for (unsigned int i = 0; i < sids_for_deny_only_.size() ; ++i) { | |
94 deny_only_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY; | |
95 deny_only_array[i].Sid = | |
96 const_cast<SID*>(sids_for_deny_only_[i].GetPSID()); | |
97 } | |
98 } | |
99 | |
100 SID_AND_ATTRIBUTES *sids_to_restrict_array = NULL; | |
101 if (restrict_size) { | |
102 sids_to_restrict_array = new SID_AND_ATTRIBUTES[restrict_size]; | |
103 | |
104 for (unsigned int i = 0; i < restrict_size; ++i) { | |
105 sids_to_restrict_array[i].Attributes = 0; | |
106 sids_to_restrict_array[i].Sid = | |
107 const_cast<SID*>(sids_to_restrict_[i].GetPSID()); | |
108 } | |
109 } | |
110 | |
111 LUID_AND_ATTRIBUTES *privileges_to_disable_array = NULL; | |
112 if (privileges_size) { | |
113 privileges_to_disable_array = new LUID_AND_ATTRIBUTES[privileges_size]; | |
114 | |
115 for (unsigned int i = 0; i < privileges_size; ++i) { | |
116 privileges_to_disable_array[i].Attributes = 0; | |
117 privileges_to_disable_array[i].Luid = privileges_to_disable_[i]; | |
118 } | |
119 } | |
120 | |
121 BOOL result = TRUE; | |
122 HANDLE new_token_handle = NULL; | |
123 // The SANDBOX_INERT flag did nothing in XP and it was just a way to tell | |
124 // if a token has ben restricted given the limiations of IsTokenRestricted() | |
125 // but it appears that in Windows 7 it hints the AppLocker subsystem to | |
126 // leave us alone. | |
127 if (deny_size || restrict_size || privileges_size) { | |
128 result = ::CreateRestrictedToken(effective_token_.Get(), | |
129 SANDBOX_INERT, | |
130 static_cast<DWORD>(deny_size), | |
131 deny_only_array, | |
132 static_cast<DWORD>(privileges_size), | |
133 privileges_to_disable_array, | |
134 static_cast<DWORD>(restrict_size), | |
135 sids_to_restrict_array, | |
136 &new_token_handle); | |
137 } else { | |
138 // Duplicate the token even if it's not modified at this point | |
139 // because any subsequent changes to this token would also affect the | |
140 // current process. | |
141 result = ::DuplicateTokenEx(effective_token_.Get(), TOKEN_ALL_ACCESS, NULL, | |
142 SecurityIdentification, TokenPrimary, | |
143 &new_token_handle); | |
144 } | |
145 auto last_error = ::GetLastError(); | |
146 | |
147 if (deny_only_array) | |
148 delete[] deny_only_array; | |
149 | |
150 if (sids_to_restrict_array) | |
151 delete[] sids_to_restrict_array; | |
152 | |
153 if (privileges_to_disable_array) | |
154 delete[] privileges_to_disable_array; | |
155 | |
156 if (!result) | |
157 return last_error; | |
158 | |
159 base::win::ScopedHandle new_token(new_token_handle); | |
160 | |
161 if (lockdown_default_dacl_) { | |
162 // Don't add Restricted sid and also remove logon sid access. | |
163 if (!RevokeLogonSidFromDefaultDacl(new_token.Get())) | |
164 return ::GetLastError(); | |
165 } else { | |
166 // Modify the default dacl on the token to contain Restricted. | |
167 if (!AddSidToDefaultDacl(new_token.Get(), WinRestrictedCodeSid, | |
168 GRANT_ACCESS, GENERIC_ALL)) { | |
169 return ::GetLastError(); | |
170 } | |
171 } | |
172 | |
173 // Add user to default dacl. | |
174 if (!AddUserSidToDefaultDacl(new_token.Get(), GENERIC_ALL)) | |
175 return ::GetLastError(); | |
176 | |
177 DWORD error = SetTokenIntegrityLevel(new_token.Get(), integrity_level_); | |
178 if (ERROR_SUCCESS != error) | |
179 return error; | |
180 | |
181 HANDLE token_handle; | |
182 if (!::DuplicateHandle(::GetCurrentProcess(), new_token.Get(), | |
183 ::GetCurrentProcess(), &token_handle, | |
184 TOKEN_ALL_ACCESS, FALSE, // Don't inherit. | |
185 0)) { | |
186 return ::GetLastError(); | |
187 } | |
188 | |
189 token->Set(token_handle); | |
190 return ERROR_SUCCESS; | |
191 } | |
192 | |
193 DWORD RestrictedToken::GetRestrictedTokenForImpersonation( | |
194 base::win::ScopedHandle* token) const { | |
195 DCHECK(init_); | |
196 if (!init_) | |
197 return ERROR_NO_TOKEN; | |
198 | |
199 base::win::ScopedHandle restricted_token; | |
200 DWORD err_code = GetRestrictedToken(&restricted_token); | |
201 if (ERROR_SUCCESS != err_code) | |
202 return err_code; | |
203 | |
204 HANDLE impersonation_token_handle; | |
205 if (!::DuplicateToken(restricted_token.Get(), | |
206 SecurityImpersonation, | |
207 &impersonation_token_handle)) { | |
208 return ::GetLastError(); | |
209 } | |
210 base::win::ScopedHandle impersonation_token(impersonation_token_handle); | |
211 | |
212 HANDLE token_handle; | |
213 if (!::DuplicateHandle(::GetCurrentProcess(), impersonation_token.Get(), | |
214 ::GetCurrentProcess(), &token_handle, | |
215 TOKEN_ALL_ACCESS, FALSE, // Don't inherit. | |
216 0)) { | |
217 return ::GetLastError(); | |
218 } | |
219 | |
220 token->Set(token_handle); | |
221 return ERROR_SUCCESS; | |
222 } | |
223 | |
224 DWORD RestrictedToken::AddAllSidsForDenyOnly(std::vector<Sid> *exceptions) { | |
225 DCHECK(init_); | |
226 if (!init_) | |
227 return ERROR_NO_TOKEN; | |
228 | |
229 DWORD error; | |
230 scoped_ptr<BYTE[]> buffer = | |
231 GetTokenInfo(effective_token_, TokenGroups, &error); | |
232 | |
233 if (!buffer) | |
234 return error; | |
235 | |
236 TOKEN_GROUPS* token_groups = reinterpret_cast<TOKEN_GROUPS*>(buffer.get()); | |
237 | |
238 // Build the list of the deny only group SIDs | |
239 for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { | |
240 if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0 && | |
241 (token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 0) { | |
242 bool should_ignore = false; | |
243 if (exceptions) { | |
244 for (unsigned int j = 0; j < exceptions->size(); ++j) { | |
245 if (::EqualSid(const_cast<SID*>((*exceptions)[j].GetPSID()), | |
246 token_groups->Groups[i].Sid)) { | |
247 should_ignore = true; | |
248 break; | |
249 } | |
250 } | |
251 } | |
252 if (!should_ignore) { | |
253 sids_for_deny_only_.push_back( | |
254 reinterpret_cast<SID*>(token_groups->Groups[i].Sid)); | |
255 } | |
256 } | |
257 } | |
258 | |
259 return ERROR_SUCCESS; | |
260 } | |
261 | |
262 DWORD RestrictedToken::AddSidForDenyOnly(const Sid &sid) { | |
263 DCHECK(init_); | |
264 if (!init_) | |
265 return ERROR_NO_TOKEN; | |
266 | |
267 sids_for_deny_only_.push_back(sid); | |
268 return ERROR_SUCCESS; | |
269 } | |
270 | |
271 DWORD RestrictedToken::AddUserSidForDenyOnly() { | |
272 DCHECK(init_); | |
273 if (!init_) | |
274 return ERROR_NO_TOKEN; | |
275 | |
276 DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE; | |
277 scoped_ptr<BYTE[]> buffer(new BYTE[size]); | |
278 TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(buffer.get()); | |
279 | |
280 BOOL result = ::GetTokenInformation(effective_token_.Get(), TokenUser, | |
281 token_user, size, &size); | |
282 | |
283 if (!result) | |
284 return ::GetLastError(); | |
285 | |
286 Sid user = reinterpret_cast<SID*>(token_user->User.Sid); | |
287 sids_for_deny_only_.push_back(user); | |
288 | |
289 return ERROR_SUCCESS; | |
290 } | |
291 | |
292 DWORD RestrictedToken::DeleteAllPrivileges( | |
293 const std::vector<base::string16> *exceptions) { | |
294 DCHECK(init_); | |
295 if (!init_) | |
296 return ERROR_NO_TOKEN; | |
297 | |
298 DWORD error; | |
299 scoped_ptr<BYTE[]> buffer = | |
300 GetTokenInfo(effective_token_, TokenPrivileges, &error); | |
301 | |
302 if (!buffer) | |
303 return error; | |
304 | |
305 TOKEN_PRIVILEGES* token_privileges = | |
306 reinterpret_cast<TOKEN_PRIVILEGES*>(buffer.get()); | |
307 | |
308 // Build the list of privileges to disable | |
309 for (unsigned int i = 0; i < token_privileges->PrivilegeCount; ++i) { | |
310 bool should_ignore = false; | |
311 if (exceptions) { | |
312 for (unsigned int j = 0; j < exceptions->size(); ++j) { | |
313 LUID luid = {0}; | |
314 ::LookupPrivilegeValue(NULL, (*exceptions)[j].c_str(), &luid); | |
315 if (token_privileges->Privileges[i].Luid.HighPart == luid.HighPart && | |
316 token_privileges->Privileges[i].Luid.LowPart == luid.LowPart) { | |
317 should_ignore = true; | |
318 break; | |
319 } | |
320 } | |
321 } | |
322 if (!should_ignore) { | |
323 privileges_to_disable_.push_back(token_privileges->Privileges[i].Luid); | |
324 } | |
325 } | |
326 | |
327 return ERROR_SUCCESS; | |
328 } | |
329 | |
330 DWORD RestrictedToken::DeletePrivilege(const wchar_t *privilege) { | |
331 DCHECK(init_); | |
332 if (!init_) | |
333 return ERROR_NO_TOKEN; | |
334 | |
335 LUID luid = {0}; | |
336 if (LookupPrivilegeValue(NULL, privilege, &luid)) | |
337 privileges_to_disable_.push_back(luid); | |
338 else | |
339 return ::GetLastError(); | |
340 | |
341 return ERROR_SUCCESS; | |
342 } | |
343 | |
344 DWORD RestrictedToken::AddRestrictingSid(const Sid &sid) { | |
345 DCHECK(init_); | |
346 if (!init_) | |
347 return ERROR_NO_TOKEN; | |
348 | |
349 sids_to_restrict_.push_back(sid); // No attributes | |
350 return ERROR_SUCCESS; | |
351 } | |
352 | |
353 DWORD RestrictedToken::AddRestrictingSidLogonSession() { | |
354 DCHECK(init_); | |
355 if (!init_) | |
356 return ERROR_NO_TOKEN; | |
357 | |
358 DWORD error; | |
359 scoped_ptr<BYTE[]> buffer = | |
360 GetTokenInfo(effective_token_, TokenGroups, &error); | |
361 | |
362 if (!buffer) | |
363 return error; | |
364 | |
365 TOKEN_GROUPS* token_groups = reinterpret_cast<TOKEN_GROUPS*>(buffer.get()); | |
366 | |
367 SID *logon_sid = NULL; | |
368 for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { | |
369 if ((token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) { | |
370 logon_sid = static_cast<SID*>(token_groups->Groups[i].Sid); | |
371 break; | |
372 } | |
373 } | |
374 | |
375 if (logon_sid) | |
376 sids_to_restrict_.push_back(logon_sid); | |
377 | |
378 return ERROR_SUCCESS; | |
379 } | |
380 | |
381 DWORD RestrictedToken::AddRestrictingSidCurrentUser() { | |
382 DCHECK(init_); | |
383 if (!init_) | |
384 return ERROR_NO_TOKEN; | |
385 | |
386 DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE; | |
387 scoped_ptr<BYTE[]> buffer(new BYTE[size]); | |
388 TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(buffer.get()); | |
389 | |
390 BOOL result = ::GetTokenInformation(effective_token_.Get(), TokenUser, | |
391 token_user, size, &size); | |
392 | |
393 if (!result) | |
394 return ::GetLastError(); | |
395 | |
396 Sid user = reinterpret_cast<SID*>(token_user->User.Sid); | |
397 sids_to_restrict_.push_back(user); | |
398 | |
399 return ERROR_SUCCESS; | |
400 } | |
401 | |
402 DWORD RestrictedToken::AddRestrictingSidAllSids() { | |
403 DCHECK(init_); | |
404 if (!init_) | |
405 return ERROR_NO_TOKEN; | |
406 | |
407 // Add the current user to the list. | |
408 DWORD error = AddRestrictingSidCurrentUser(); | |
409 if (ERROR_SUCCESS != error) | |
410 return error; | |
411 | |
412 scoped_ptr<BYTE[]> buffer = | |
413 GetTokenInfo(effective_token_, TokenGroups, &error); | |
414 | |
415 if (!buffer) | |
416 return error; | |
417 | |
418 TOKEN_GROUPS* token_groups = reinterpret_cast<TOKEN_GROUPS*>(buffer.get()); | |
419 | |
420 // Build the list of restricting sids from all groups. | |
421 for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { | |
422 if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0) | |
423 AddRestrictingSid(reinterpret_cast<SID*>(token_groups->Groups[i].Sid)); | |
424 } | |
425 | |
426 return ERROR_SUCCESS; | |
427 } | |
428 | |
429 DWORD RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) { | |
430 integrity_level_ = integrity_level; | |
431 return ERROR_SUCCESS; | |
432 } | |
433 | |
434 void RestrictedToken::SetLockdownDefaultDacl() { | |
435 lockdown_default_dacl_ = true; | |
436 } | |
437 | |
438 } // namespace sandbox | |
OLD | NEW |