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