| 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 |