| 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/sandbox_policy_base.h" | |
| 6 | |
| 7 #include <sddl.h> | |
| 8 #include <stddef.h> | |
| 9 #include <stdint.h> | |
| 10 | |
| 11 #include "base/callback.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/macros.h" | |
| 14 #include "base/stl_util.h" | |
| 15 #include "base/strings/stringprintf.h" | |
| 16 #include "base/win/windows_version.h" | |
| 17 #include "sandbox/win/src/app_container.h" | |
| 18 #include "sandbox/win/src/filesystem_policy.h" | |
| 19 #include "sandbox/win/src/handle_policy.h" | |
| 20 #include "sandbox/win/src/interception.h" | |
| 21 #include "sandbox/win/src/job.h" | |
| 22 #include "sandbox/win/src/named_pipe_policy.h" | |
| 23 #include "sandbox/win/src/policy_broker.h" | |
| 24 #include "sandbox/win/src/policy_engine_processor.h" | |
| 25 #include "sandbox/win/src/policy_low_level.h" | |
| 26 #include "sandbox/win/src/process_mitigations.h" | |
| 27 #include "sandbox/win/src/process_mitigations_win32k_policy.h" | |
| 28 #include "sandbox/win/src/process_thread_policy.h" | |
| 29 #include "sandbox/win/src/registry_policy.h" | |
| 30 #include "sandbox/win/src/restricted_token_utils.h" | |
| 31 #include "sandbox/win/src/sandbox_policy.h" | |
| 32 #include "sandbox/win/src/sandbox_utils.h" | |
| 33 #include "sandbox/win/src/sync_policy.h" | |
| 34 #include "sandbox/win/src/target_process.h" | |
| 35 #include "sandbox/win/src/top_level_dispatcher.h" | |
| 36 #include "sandbox/win/src/window.h" | |
| 37 | |
| 38 namespace { | |
| 39 | |
| 40 // The standard windows size for one memory page. | |
| 41 const size_t kOneMemPage = 4096; | |
| 42 // The IPC and Policy shared memory sizes. | |
| 43 const size_t kIPCMemSize = kOneMemPage * 2; | |
| 44 const size_t kPolMemSize = kOneMemPage * 14; | |
| 45 | |
| 46 // Helper function to allocate space (on the heap) for policy. | |
| 47 sandbox::PolicyGlobal* MakeBrokerPolicyMemory() { | |
| 48 const size_t kTotalPolicySz = kPolMemSize; | |
| 49 sandbox::PolicyGlobal* policy = static_cast<sandbox::PolicyGlobal*> | |
| 50 (::operator new(kTotalPolicySz)); | |
| 51 DCHECK(policy); | |
| 52 memset(policy, 0, kTotalPolicySz); | |
| 53 policy->data_size = kTotalPolicySz - sizeof(sandbox::PolicyGlobal); | |
| 54 return policy; | |
| 55 } | |
| 56 | |
| 57 bool IsInheritableHandle(HANDLE handle) { | |
| 58 if (!handle) | |
| 59 return false; | |
| 60 if (handle == INVALID_HANDLE_VALUE) | |
| 61 return false; | |
| 62 // File handles (FILE_TYPE_DISK) and pipe handles are known to be | |
| 63 // inheritable. Console handles (FILE_TYPE_CHAR) are not | |
| 64 // inheritable via PROC_THREAD_ATTRIBUTE_HANDLE_LIST. | |
| 65 DWORD handle_type = GetFileType(handle); | |
| 66 return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE; | |
| 67 } | |
| 68 | |
| 69 HANDLE CreateLowBoxObjectDirectory(PSID lowbox_sid) { | |
| 70 DWORD session_id = 0; | |
| 71 if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id)) | |
| 72 return NULL; | |
| 73 | |
| 74 LPWSTR sid_string = NULL; | |
| 75 if (!::ConvertSidToStringSid(lowbox_sid, &sid_string)) | |
| 76 return NULL; | |
| 77 | |
| 78 base::string16 directory_path = base::StringPrintf( | |
| 79 L"\\Sessions\\%d\\AppContainerNamedObjects\\%ls", | |
| 80 session_id, sid_string).c_str(); | |
| 81 ::LocalFree(sid_string); | |
| 82 | |
| 83 NtCreateDirectoryObjectFunction CreateObjectDirectory = NULL; | |
| 84 ResolveNTFunctionPtr("NtCreateDirectoryObject", &CreateObjectDirectory); | |
| 85 | |
| 86 OBJECT_ATTRIBUTES obj_attr; | |
| 87 UNICODE_STRING obj_name; | |
| 88 sandbox::InitObjectAttribs(directory_path, | |
| 89 OBJ_CASE_INSENSITIVE | OBJ_OPENIF, | |
| 90 NULL, | |
| 91 &obj_attr, | |
| 92 &obj_name, | |
| 93 NULL); | |
| 94 | |
| 95 HANDLE handle = NULL; | |
| 96 NTSTATUS status = CreateObjectDirectory(&handle, | |
| 97 DIRECTORY_ALL_ACCESS, | |
| 98 &obj_attr); | |
| 99 | |
| 100 if (!NT_SUCCESS(status)) | |
| 101 return NULL; | |
| 102 | |
| 103 return handle; | |
| 104 } | |
| 105 | |
| 106 } // namespace | |
| 107 | |
| 108 namespace sandbox { | |
| 109 | |
| 110 SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level; | |
| 111 SANDBOX_INTERCEPT MitigationFlags g_shared_delayed_mitigations; | |
| 112 | |
| 113 // Initializes static members. | |
| 114 HWINSTA PolicyBase::alternate_winstation_handle_ = NULL; | |
| 115 HDESK PolicyBase::alternate_desktop_handle_ = NULL; | |
| 116 IntegrityLevel PolicyBase::alternate_desktop_integrity_level_label_ = | |
| 117 INTEGRITY_LEVEL_SYSTEM; | |
| 118 | |
| 119 PolicyBase::PolicyBase() | |
| 120 : ref_count(1), | |
| 121 lockdown_level_(USER_LOCKDOWN), | |
| 122 initial_level_(USER_LOCKDOWN), | |
| 123 job_level_(JOB_LOCKDOWN), | |
| 124 ui_exceptions_(0), | |
| 125 memory_limit_(0), | |
| 126 use_alternate_desktop_(false), | |
| 127 use_alternate_winstation_(false), | |
| 128 file_system_init_(false), | |
| 129 relaxed_interceptions_(true), | |
| 130 stdout_handle_(INVALID_HANDLE_VALUE), | |
| 131 stderr_handle_(INVALID_HANDLE_VALUE), | |
| 132 integrity_level_(INTEGRITY_LEVEL_LAST), | |
| 133 delayed_integrity_level_(INTEGRITY_LEVEL_LAST), | |
| 134 mitigations_(0), | |
| 135 delayed_mitigations_(0), | |
| 136 is_csrss_connected_(true), | |
| 137 policy_maker_(NULL), | |
| 138 policy_(NULL), | |
| 139 lowbox_sid_(NULL), | |
| 140 lockdown_default_dacl_(false) { | |
| 141 ::InitializeCriticalSection(&lock_); | |
| 142 dispatcher_.reset(new TopLevelDispatcher(this)); | |
| 143 } | |
| 144 | |
| 145 PolicyBase::~PolicyBase() { | |
| 146 TargetSet::iterator it; | |
| 147 for (it = targets_.begin(); it != targets_.end(); ++it) { | |
| 148 TargetProcess* target = (*it); | |
| 149 delete target; | |
| 150 } | |
| 151 delete policy_maker_; | |
| 152 delete policy_; | |
| 153 | |
| 154 if (lowbox_sid_) | |
| 155 ::LocalFree(lowbox_sid_); | |
| 156 | |
| 157 ::DeleteCriticalSection(&lock_); | |
| 158 } | |
| 159 | |
| 160 void PolicyBase::AddRef() { | |
| 161 ::InterlockedIncrement(&ref_count); | |
| 162 } | |
| 163 | |
| 164 void PolicyBase::Release() { | |
| 165 if (0 == ::InterlockedDecrement(&ref_count)) | |
| 166 delete this; | |
| 167 } | |
| 168 | |
| 169 ResultCode PolicyBase::SetTokenLevel(TokenLevel initial, TokenLevel lockdown) { | |
| 170 if (initial < lockdown) { | |
| 171 return SBOX_ERROR_BAD_PARAMS; | |
| 172 } | |
| 173 initial_level_ = initial; | |
| 174 lockdown_level_ = lockdown; | |
| 175 return SBOX_ALL_OK; | |
| 176 } | |
| 177 | |
| 178 TokenLevel PolicyBase::GetInitialTokenLevel() const { | |
| 179 return initial_level_; | |
| 180 } | |
| 181 | |
| 182 TokenLevel PolicyBase::GetLockdownTokenLevel() const { | |
| 183 return lockdown_level_; | |
| 184 } | |
| 185 | |
| 186 ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) { | |
| 187 if (memory_limit_ && job_level == JOB_NONE) { | |
| 188 return SBOX_ERROR_BAD_PARAMS; | |
| 189 } | |
| 190 job_level_ = job_level; | |
| 191 ui_exceptions_ = ui_exceptions; | |
| 192 return SBOX_ALL_OK; | |
| 193 } | |
| 194 | |
| 195 JobLevel PolicyBase::GetJobLevel() const { | |
| 196 return job_level_; | |
| 197 } | |
| 198 | |
| 199 ResultCode PolicyBase::SetJobMemoryLimit(size_t memory_limit) { | |
| 200 if (memory_limit && job_level_ == JOB_NONE) { | |
| 201 return SBOX_ERROR_BAD_PARAMS; | |
| 202 } | |
| 203 memory_limit_ = memory_limit; | |
| 204 return SBOX_ALL_OK; | |
| 205 } | |
| 206 | |
| 207 ResultCode PolicyBase::SetAlternateDesktop(bool alternate_winstation) { | |
| 208 use_alternate_desktop_ = true; | |
| 209 use_alternate_winstation_ = alternate_winstation; | |
| 210 return CreateAlternateDesktop(alternate_winstation); | |
| 211 } | |
| 212 | |
| 213 base::string16 PolicyBase::GetAlternateDesktop() const { | |
| 214 // No alternate desktop or winstation. Return an empty string. | |
| 215 if (!use_alternate_desktop_ && !use_alternate_winstation_) { | |
| 216 return base::string16(); | |
| 217 } | |
| 218 | |
| 219 // The desktop and winstation should have been created by now. | |
| 220 // If we hit this scenario, it means that the user ignored the failure | |
| 221 // during SetAlternateDesktop, so we ignore it here too. | |
| 222 if (use_alternate_desktop_ && !alternate_desktop_handle_) { | |
| 223 return base::string16(); | |
| 224 } | |
| 225 if (use_alternate_winstation_ && (!alternate_desktop_handle_ || | |
| 226 !alternate_winstation_handle_)) { | |
| 227 return base::string16(); | |
| 228 } | |
| 229 | |
| 230 return GetFullDesktopName(alternate_winstation_handle_, | |
| 231 alternate_desktop_handle_); | |
| 232 } | |
| 233 | |
| 234 ResultCode PolicyBase::CreateAlternateDesktop(bool alternate_winstation) { | |
| 235 if (alternate_winstation) { | |
| 236 // Previously called with alternate_winstation = false? | |
| 237 if (!alternate_winstation_handle_ && alternate_desktop_handle_) | |
| 238 return SBOX_ERROR_UNSUPPORTED; | |
| 239 | |
| 240 // Check if it's already created. | |
| 241 if (alternate_winstation_handle_ && alternate_desktop_handle_) | |
| 242 return SBOX_ALL_OK; | |
| 243 | |
| 244 DCHECK(!alternate_winstation_handle_); | |
| 245 // Create the window station. | |
| 246 ResultCode result = CreateAltWindowStation(&alternate_winstation_handle_); | |
| 247 if (SBOX_ALL_OK != result) | |
| 248 return result; | |
| 249 | |
| 250 // Verify that everything is fine. | |
| 251 if (!alternate_winstation_handle_ || | |
| 252 GetWindowObjectName(alternate_winstation_handle_).empty()) | |
| 253 return SBOX_ERROR_CANNOT_CREATE_DESKTOP; | |
| 254 | |
| 255 // Create the destkop. | |
| 256 result = CreateAltDesktop(alternate_winstation_handle_, | |
| 257 &alternate_desktop_handle_); | |
| 258 if (SBOX_ALL_OK != result) | |
| 259 return result; | |
| 260 | |
| 261 // Verify that everything is fine. | |
| 262 if (!alternate_desktop_handle_ || | |
| 263 GetWindowObjectName(alternate_desktop_handle_).empty()) | |
| 264 return SBOX_ERROR_CANNOT_CREATE_DESKTOP; | |
| 265 } else { | |
| 266 // Previously called with alternate_winstation = true? | |
| 267 if (alternate_winstation_handle_) | |
| 268 return SBOX_ERROR_UNSUPPORTED; | |
| 269 | |
| 270 // Check if it already exists. | |
| 271 if (alternate_desktop_handle_) | |
| 272 return SBOX_ALL_OK; | |
| 273 | |
| 274 // Create the destkop. | |
| 275 ResultCode result = CreateAltDesktop(NULL, &alternate_desktop_handle_); | |
| 276 if (SBOX_ALL_OK != result) | |
| 277 return result; | |
| 278 | |
| 279 // Verify that everything is fine. | |
| 280 if (!alternate_desktop_handle_ || | |
| 281 GetWindowObjectName(alternate_desktop_handle_).empty()) | |
| 282 return SBOX_ERROR_CANNOT_CREATE_DESKTOP; | |
| 283 } | |
| 284 | |
| 285 return SBOX_ALL_OK; | |
| 286 } | |
| 287 | |
| 288 void PolicyBase::DestroyAlternateDesktop() { | |
| 289 if (alternate_desktop_handle_) { | |
| 290 ::CloseDesktop(alternate_desktop_handle_); | |
| 291 alternate_desktop_handle_ = NULL; | |
| 292 } | |
| 293 | |
| 294 if (alternate_winstation_handle_) { | |
| 295 ::CloseWindowStation(alternate_winstation_handle_); | |
| 296 alternate_winstation_handle_ = NULL; | |
| 297 } | |
| 298 } | |
| 299 | |
| 300 ResultCode PolicyBase::SetIntegrityLevel(IntegrityLevel integrity_level) { | |
| 301 integrity_level_ = integrity_level; | |
| 302 return SBOX_ALL_OK; | |
| 303 } | |
| 304 | |
| 305 IntegrityLevel PolicyBase::GetIntegrityLevel() const { | |
| 306 return integrity_level_; | |
| 307 } | |
| 308 | |
| 309 ResultCode PolicyBase::SetDelayedIntegrityLevel( | |
| 310 IntegrityLevel integrity_level) { | |
| 311 delayed_integrity_level_ = integrity_level; | |
| 312 return SBOX_ALL_OK; | |
| 313 } | |
| 314 | |
| 315 ResultCode PolicyBase::SetAppContainer(const wchar_t* sid) { | |
| 316 if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8) | |
| 317 return SBOX_ALL_OK; | |
| 318 | |
| 319 // SetLowBox and SetAppContainer are mutually exclusive. | |
| 320 if (lowbox_sid_) | |
| 321 return SBOX_ERROR_UNSUPPORTED; | |
| 322 | |
| 323 // Windows refuses to work with an impersonation token for a process inside | |
| 324 // an AppContainer. If the caller wants to use a more privileged initial | |
| 325 // token, or if the lockdown level will prevent the process from starting, | |
| 326 // we have to fail the operation. | |
| 327 if (lockdown_level_ < USER_LIMITED || lockdown_level_ != initial_level_) | |
| 328 return SBOX_ERROR_CANNOT_INIT_APPCONTAINER; | |
| 329 | |
| 330 DCHECK(!appcontainer_list_.get()); | |
| 331 appcontainer_list_.reset(new AppContainerAttributes); | |
| 332 ResultCode rv = appcontainer_list_->SetAppContainer(sid, capabilities_); | |
| 333 if (rv != SBOX_ALL_OK) | |
| 334 return rv; | |
| 335 | |
| 336 return SBOX_ALL_OK; | |
| 337 } | |
| 338 | |
| 339 ResultCode PolicyBase::SetCapability(const wchar_t* sid) { | |
| 340 capabilities_.push_back(sid); | |
| 341 return SBOX_ALL_OK; | |
| 342 } | |
| 343 | |
| 344 ResultCode PolicyBase::SetLowBox(const wchar_t* sid) { | |
| 345 if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8) | |
| 346 return SBOX_ERROR_UNSUPPORTED; | |
| 347 | |
| 348 // SetLowBox and SetAppContainer are mutually exclusive. | |
| 349 if (appcontainer_list_.get()) | |
| 350 return SBOX_ERROR_UNSUPPORTED; | |
| 351 | |
| 352 DCHECK(sid); | |
| 353 | |
| 354 if (lowbox_sid_) | |
| 355 return SBOX_ERROR_BAD_PARAMS; | |
| 356 | |
| 357 if (!ConvertStringSidToSid(sid, &lowbox_sid_)) | |
| 358 return SBOX_ERROR_GENERIC; | |
| 359 | |
| 360 return SBOX_ALL_OK; | |
| 361 } | |
| 362 | |
| 363 ResultCode PolicyBase::SetProcessMitigations( | |
| 364 MitigationFlags flags) { | |
| 365 if (!CanSetProcessMitigationsPreStartup(flags)) | |
| 366 return SBOX_ERROR_BAD_PARAMS; | |
| 367 mitigations_ = flags; | |
| 368 return SBOX_ALL_OK; | |
| 369 } | |
| 370 | |
| 371 MitigationFlags PolicyBase::GetProcessMitigations() { | |
| 372 return mitigations_; | |
| 373 } | |
| 374 | |
| 375 ResultCode PolicyBase::SetDelayedProcessMitigations( | |
| 376 MitigationFlags flags) { | |
| 377 if (!CanSetProcessMitigationsPostStartup(flags)) | |
| 378 return SBOX_ERROR_BAD_PARAMS; | |
| 379 delayed_mitigations_ = flags; | |
| 380 return SBOX_ALL_OK; | |
| 381 } | |
| 382 | |
| 383 MitigationFlags PolicyBase::GetDelayedProcessMitigations() const { | |
| 384 return delayed_mitigations_; | |
| 385 } | |
| 386 | |
| 387 void PolicyBase::SetStrictInterceptions() { | |
| 388 relaxed_interceptions_ = false; | |
| 389 } | |
| 390 | |
| 391 ResultCode PolicyBase::SetStdoutHandle(HANDLE handle) { | |
| 392 if (!IsInheritableHandle(handle)) | |
| 393 return SBOX_ERROR_BAD_PARAMS; | |
| 394 stdout_handle_ = handle; | |
| 395 return SBOX_ALL_OK; | |
| 396 } | |
| 397 | |
| 398 ResultCode PolicyBase::SetStderrHandle(HANDLE handle) { | |
| 399 if (!IsInheritableHandle(handle)) | |
| 400 return SBOX_ERROR_BAD_PARAMS; | |
| 401 stderr_handle_ = handle; | |
| 402 return SBOX_ALL_OK; | |
| 403 } | |
| 404 | |
| 405 ResultCode PolicyBase::AddRule(SubSystem subsystem, | |
| 406 Semantics semantics, | |
| 407 const wchar_t* pattern) { | |
| 408 ResultCode result = AddRuleInternal(subsystem, semantics, pattern); | |
| 409 LOG_IF(ERROR, result != SBOX_ALL_OK) << "Failed to add sandbox rule." | |
| 410 << " error = " << result | |
| 411 << ", subsystem = " << subsystem | |
| 412 << ", semantics = " << semantics | |
| 413 << ", pattern = '" << pattern << "'"; | |
| 414 return result; | |
| 415 } | |
| 416 | |
| 417 ResultCode PolicyBase::AddDllToUnload(const wchar_t* dll_name) { | |
| 418 blacklisted_dlls_.push_back(dll_name); | |
| 419 return SBOX_ALL_OK; | |
| 420 } | |
| 421 | |
| 422 ResultCode PolicyBase::AddKernelObjectToClose(const base::char16* handle_type, | |
| 423 const base::char16* handle_name) { | |
| 424 return handle_closer_.AddHandle(handle_type, handle_name); | |
| 425 } | |
| 426 | |
| 427 void PolicyBase::AddHandleToShare(HANDLE handle) { | |
| 428 CHECK(handle && handle != INVALID_HANDLE_VALUE); | |
| 429 | |
| 430 // Ensure the handle can be inherited. | |
| 431 BOOL result = SetHandleInformation(handle, HANDLE_FLAG_INHERIT, | |
| 432 HANDLE_FLAG_INHERIT); | |
| 433 PCHECK(result); | |
| 434 | |
| 435 handles_to_share_.push_back(handle); | |
| 436 } | |
| 437 | |
| 438 void PolicyBase::SetLockdownDefaultDacl() { | |
| 439 lockdown_default_dacl_ = true; | |
| 440 } | |
| 441 | |
| 442 const base::HandlesToInheritVector& PolicyBase::GetHandlesBeingShared() { | |
| 443 return handles_to_share_; | |
| 444 } | |
| 445 | |
| 446 ResultCode PolicyBase::MakeJobObject(base::win::ScopedHandle* job) { | |
| 447 if (job_level_ != JOB_NONE) { | |
| 448 // Create the windows job object. | |
| 449 Job job_obj; | |
| 450 DWORD result = job_obj.Init(job_level_, NULL, ui_exceptions_, | |
| 451 memory_limit_); | |
| 452 if (ERROR_SUCCESS != result) | |
| 453 return SBOX_ERROR_GENERIC; | |
| 454 | |
| 455 *job = job_obj.Take(); | |
| 456 } else { | |
| 457 *job = base::win::ScopedHandle(); | |
| 458 } | |
| 459 return SBOX_ALL_OK; | |
| 460 } | |
| 461 | |
| 462 ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial, | |
| 463 base::win::ScopedHandle* lockdown, | |
| 464 base::win::ScopedHandle* lowbox) { | |
| 465 if (appcontainer_list_.get() && appcontainer_list_->HasAppContainer() && | |
| 466 lowbox_sid_) { | |
| 467 return SBOX_ERROR_BAD_PARAMS; | |
| 468 } | |
| 469 | |
| 470 // Create the 'naked' token. This will be the permanent token associated | |
| 471 // with the process and therefore with any thread that is not impersonating. | |
| 472 DWORD result = | |
| 473 CreateRestrictedToken(lockdown_level_, integrity_level_, PRIMARY, | |
| 474 lockdown_default_dacl_, lockdown); | |
| 475 if (ERROR_SUCCESS != result) | |
| 476 return SBOX_ERROR_GENERIC; | |
| 477 | |
| 478 // If we're launching on the alternate desktop we need to make sure the | |
| 479 // integrity label on the object is no higher than the sandboxed process's | |
| 480 // integrity level. So, we lower the label on the desktop process if it's | |
| 481 // not already low enough for our process. | |
| 482 if (alternate_desktop_handle_ && use_alternate_desktop_ && | |
| 483 integrity_level_ != INTEGRITY_LEVEL_LAST && | |
| 484 alternate_desktop_integrity_level_label_ < integrity_level_) { | |
| 485 // Integrity label enum is reversed (higher level is a lower value). | |
| 486 static_assert(INTEGRITY_LEVEL_SYSTEM < INTEGRITY_LEVEL_UNTRUSTED, | |
| 487 "Integrity level ordering reversed."); | |
| 488 result = SetObjectIntegrityLabel(alternate_desktop_handle_, | |
| 489 SE_WINDOW_OBJECT, | |
| 490 L"", | |
| 491 GetIntegrityLevelString(integrity_level_)); | |
| 492 if (ERROR_SUCCESS != result) | |
| 493 return SBOX_ERROR_GENERIC; | |
| 494 | |
| 495 alternate_desktop_integrity_level_label_ = integrity_level_; | |
| 496 } | |
| 497 | |
| 498 // We are maintaining two mutually exclusive approaches. One is to start an | |
| 499 // AppContainer process through StartupInfoEx and other is replacing | |
| 500 // existing token with LowBox token after process creation. | |
| 501 if (appcontainer_list_.get() && appcontainer_list_->HasAppContainer()) { | |
| 502 // Windows refuses to work with an impersonation token. See SetAppContainer | |
| 503 // implementation for more details. | |
| 504 if (lockdown_level_ < USER_LIMITED || lockdown_level_ != initial_level_) | |
| 505 return SBOX_ERROR_CANNOT_INIT_APPCONTAINER; | |
| 506 | |
| 507 *initial = base::win::ScopedHandle(); | |
| 508 return SBOX_ALL_OK; | |
| 509 } | |
| 510 | |
| 511 if (lowbox_sid_) { | |
| 512 NtCreateLowBoxToken CreateLowBoxToken = NULL; | |
| 513 ResolveNTFunctionPtr("NtCreateLowBoxToken", &CreateLowBoxToken); | |
| 514 OBJECT_ATTRIBUTES obj_attr; | |
| 515 InitializeObjectAttributes(&obj_attr, NULL, 0, NULL, NULL); | |
| 516 HANDLE token_lowbox = NULL; | |
| 517 | |
| 518 if (!lowbox_directory_.IsValid()) | |
| 519 lowbox_directory_.Set(CreateLowBoxObjectDirectory(lowbox_sid_)); | |
| 520 DCHECK(lowbox_directory_.IsValid()); | |
| 521 | |
| 522 // The order of handles isn't important in the CreateLowBoxToken call. | |
| 523 // The kernel will maintain a reference to the object directory handle. | |
| 524 HANDLE saved_handles[1] = {lowbox_directory_.Get()}; | |
| 525 DWORD saved_handles_count = lowbox_directory_.IsValid() ? 1 : 0; | |
| 526 | |
| 527 NTSTATUS status = CreateLowBoxToken(&token_lowbox, lockdown->Get(), | |
| 528 TOKEN_ALL_ACCESS, &obj_attr, | |
| 529 lowbox_sid_, 0, NULL, | |
| 530 saved_handles_count, saved_handles); | |
| 531 if (!NT_SUCCESS(status)) | |
| 532 return SBOX_ERROR_GENERIC; | |
| 533 | |
| 534 DCHECK(token_lowbox); | |
| 535 lowbox->Set(token_lowbox); | |
| 536 } | |
| 537 | |
| 538 // Create the 'better' token. We use this token as the one that the main | |
| 539 // thread uses when booting up the process. It should contain most of | |
| 540 // what we need (before reaching main( )) | |
| 541 result = | |
| 542 CreateRestrictedToken(initial_level_, integrity_level_, IMPERSONATION, | |
| 543 lockdown_default_dacl_, initial); | |
| 544 if (ERROR_SUCCESS != result) | |
| 545 return SBOX_ERROR_GENERIC; | |
| 546 | |
| 547 return SBOX_ALL_OK; | |
| 548 } | |
| 549 | |
| 550 const AppContainerAttributes* PolicyBase::GetAppContainer() const { | |
| 551 if (!appcontainer_list_.get() || !appcontainer_list_->HasAppContainer()) | |
| 552 return NULL; | |
| 553 | |
| 554 return appcontainer_list_.get(); | |
| 555 } | |
| 556 | |
| 557 PSID PolicyBase::GetLowBoxSid() const { | |
| 558 return lowbox_sid_; | |
| 559 } | |
| 560 | |
| 561 bool PolicyBase::AddTarget(TargetProcess* target) { | |
| 562 if (NULL != policy_) | |
| 563 policy_maker_->Done(); | |
| 564 | |
| 565 if (!ApplyProcessMitigationsToSuspendedProcess(target->Process(), | |
| 566 mitigations_)) { | |
| 567 return false; | |
| 568 } | |
| 569 | |
| 570 if (!SetupAllInterceptions(target)) | |
| 571 return false; | |
| 572 | |
| 573 if (!SetupHandleCloser(target)) | |
| 574 return false; | |
| 575 | |
| 576 // Initialize the sandbox infrastructure for the target. | |
| 577 if (ERROR_SUCCESS != | |
| 578 target->Init(dispatcher_.get(), policy_, kIPCMemSize, kPolMemSize)) | |
| 579 return false; | |
| 580 | |
| 581 g_shared_delayed_integrity_level = delayed_integrity_level_; | |
| 582 ResultCode ret = target->TransferVariable( | |
| 583 "g_shared_delayed_integrity_level", | |
| 584 &g_shared_delayed_integrity_level, | |
| 585 sizeof(g_shared_delayed_integrity_level)); | |
| 586 g_shared_delayed_integrity_level = INTEGRITY_LEVEL_LAST; | |
| 587 if (SBOX_ALL_OK != ret) | |
| 588 return false; | |
| 589 | |
| 590 // Add in delayed mitigations and pseudo-mitigations enforced at startup. | |
| 591 g_shared_delayed_mitigations = delayed_mitigations_ | | |
| 592 FilterPostStartupProcessMitigations(mitigations_); | |
| 593 if (!CanSetProcessMitigationsPostStartup(g_shared_delayed_mitigations)) | |
| 594 return false; | |
| 595 | |
| 596 ret = target->TransferVariable("g_shared_delayed_mitigations", | |
| 597 &g_shared_delayed_mitigations, | |
| 598 sizeof(g_shared_delayed_mitigations)); | |
| 599 g_shared_delayed_mitigations = 0; | |
| 600 if (SBOX_ALL_OK != ret) | |
| 601 return false; | |
| 602 | |
| 603 AutoLock lock(&lock_); | |
| 604 targets_.push_back(target); | |
| 605 return true; | |
| 606 } | |
| 607 | |
| 608 bool PolicyBase::OnJobEmpty(HANDLE job) { | |
| 609 AutoLock lock(&lock_); | |
| 610 TargetSet::iterator it; | |
| 611 for (it = targets_.begin(); it != targets_.end(); ++it) { | |
| 612 if ((*it)->Job() == job) | |
| 613 break; | |
| 614 } | |
| 615 if (it == targets_.end()) { | |
| 616 return false; | |
| 617 } | |
| 618 TargetProcess* target = *it; | |
| 619 targets_.erase(it); | |
| 620 delete target; | |
| 621 return true; | |
| 622 } | |
| 623 | |
| 624 void PolicyBase::SetDisconnectCsrss() { | |
| 625 if (base::win::GetVersion() >= base::win::VERSION_WIN8) { | |
| 626 is_csrss_connected_ = false; | |
| 627 AddKernelObjectToClose(L"ALPC Port", NULL); | |
| 628 } | |
| 629 } | |
| 630 | |
| 631 EvalResult PolicyBase::EvalPolicy(int service, | |
| 632 CountedParameterSetBase* params) { | |
| 633 if (NULL != policy_) { | |
| 634 if (NULL == policy_->entry[service]) { | |
| 635 // There is no policy for this particular service. This is not a big | |
| 636 // deal. | |
| 637 return DENY_ACCESS; | |
| 638 } | |
| 639 for (int i = 0; i < params->count; i++) { | |
| 640 if (!params->parameters[i].IsValid()) { | |
| 641 NOTREACHED(); | |
| 642 return SIGNAL_ALARM; | |
| 643 } | |
| 644 } | |
| 645 PolicyProcessor pol_evaluator(policy_->entry[service]); | |
| 646 PolicyResult result = pol_evaluator.Evaluate(kShortEval, | |
| 647 params->parameters, | |
| 648 params->count); | |
| 649 if (POLICY_MATCH == result) { | |
| 650 return pol_evaluator.GetAction(); | |
| 651 } | |
| 652 DCHECK(POLICY_ERROR != result); | |
| 653 } | |
| 654 | |
| 655 return DENY_ACCESS; | |
| 656 } | |
| 657 | |
| 658 HANDLE PolicyBase::GetStdoutHandle() { | |
| 659 return stdout_handle_; | |
| 660 } | |
| 661 | |
| 662 HANDLE PolicyBase::GetStderrHandle() { | |
| 663 return stderr_handle_; | |
| 664 } | |
| 665 | |
| 666 bool PolicyBase::SetupAllInterceptions(TargetProcess* target) { | |
| 667 InterceptionManager manager(target, relaxed_interceptions_); | |
| 668 | |
| 669 if (policy_) { | |
| 670 for (int i = 0; i < IPC_LAST_TAG; i++) { | |
| 671 if (policy_->entry[i] && !dispatcher_->SetupService(&manager, i)) | |
| 672 return false; | |
| 673 } | |
| 674 } | |
| 675 | |
| 676 if (!blacklisted_dlls_.empty()) { | |
| 677 std::vector<base::string16>::iterator it = blacklisted_dlls_.begin(); | |
| 678 for (; it != blacklisted_dlls_.end(); ++it) { | |
| 679 manager.AddToUnloadModules(it->c_str()); | |
| 680 } | |
| 681 } | |
| 682 | |
| 683 if (!SetupBasicInterceptions(&manager, is_csrss_connected_)) | |
| 684 return false; | |
| 685 | |
| 686 if (!manager.InitializeInterceptions()) | |
| 687 return false; | |
| 688 | |
| 689 // Finally, setup imports on the target so the interceptions can work. | |
| 690 return SetupNtdllImports(target); | |
| 691 } | |
| 692 | |
| 693 bool PolicyBase::SetupHandleCloser(TargetProcess* target) { | |
| 694 return handle_closer_.InitializeTargetHandles(target); | |
| 695 } | |
| 696 | |
| 697 ResultCode PolicyBase::AddRuleInternal(SubSystem subsystem, | |
| 698 Semantics semantics, | |
| 699 const wchar_t* pattern) { | |
| 700 if (NULL == policy_) { | |
| 701 policy_ = MakeBrokerPolicyMemory(); | |
| 702 DCHECK(policy_); | |
| 703 policy_maker_ = new LowLevelPolicy(policy_); | |
| 704 DCHECK(policy_maker_); | |
| 705 } | |
| 706 | |
| 707 switch (subsystem) { | |
| 708 case SUBSYS_FILES: { | |
| 709 if (!file_system_init_) { | |
| 710 if (!FileSystemPolicy::SetInitialRules(policy_maker_)) | |
| 711 return SBOX_ERROR_BAD_PARAMS; | |
| 712 file_system_init_ = true; | |
| 713 } | |
| 714 if (!FileSystemPolicy::GenerateRules(pattern, semantics, policy_maker_)) { | |
| 715 NOTREACHED(); | |
| 716 return SBOX_ERROR_BAD_PARAMS; | |
| 717 } | |
| 718 break; | |
| 719 } | |
| 720 case SUBSYS_SYNC: { | |
| 721 if (!SyncPolicy::GenerateRules(pattern, semantics, policy_maker_)) { | |
| 722 NOTREACHED(); | |
| 723 return SBOX_ERROR_BAD_PARAMS; | |
| 724 } | |
| 725 break; | |
| 726 } | |
| 727 case SUBSYS_PROCESS: { | |
| 728 if (lockdown_level_ < USER_INTERACTIVE && | |
| 729 TargetPolicy::PROCESS_ALL_EXEC == semantics) { | |
| 730 // This is unsupported. This is a huge security risk to give full access | |
| 731 // to a process handle. | |
| 732 return SBOX_ERROR_UNSUPPORTED; | |
| 733 } | |
| 734 if (!ProcessPolicy::GenerateRules(pattern, semantics, policy_maker_)) { | |
| 735 NOTREACHED(); | |
| 736 return SBOX_ERROR_BAD_PARAMS; | |
| 737 } | |
| 738 break; | |
| 739 } | |
| 740 case SUBSYS_NAMED_PIPES: { | |
| 741 if (!NamedPipePolicy::GenerateRules(pattern, semantics, policy_maker_)) { | |
| 742 NOTREACHED(); | |
| 743 return SBOX_ERROR_BAD_PARAMS; | |
| 744 } | |
| 745 break; | |
| 746 } | |
| 747 case SUBSYS_REGISTRY: { | |
| 748 if (!RegistryPolicy::GenerateRules(pattern, semantics, policy_maker_)) { | |
| 749 NOTREACHED(); | |
| 750 return SBOX_ERROR_BAD_PARAMS; | |
| 751 } | |
| 752 break; | |
| 753 } | |
| 754 case SUBSYS_HANDLES: { | |
| 755 if (!HandlePolicy::GenerateRules(pattern, semantics, policy_maker_)) { | |
| 756 NOTREACHED(); | |
| 757 return SBOX_ERROR_BAD_PARAMS; | |
| 758 } | |
| 759 break; | |
| 760 } | |
| 761 | |
| 762 case SUBSYS_WIN32K_LOCKDOWN: { | |
| 763 if (!ProcessMitigationsWin32KLockdownPolicy::GenerateRules( | |
| 764 pattern, semantics, policy_maker_)) { | |
| 765 NOTREACHED(); | |
| 766 return SBOX_ERROR_BAD_PARAMS; | |
| 767 } | |
| 768 break; | |
| 769 } | |
| 770 | |
| 771 default: { return SBOX_ERROR_UNSUPPORTED; } | |
| 772 } | |
| 773 | |
| 774 return SBOX_ALL_OK; | |
| 775 } | |
| 776 | |
| 777 } // namespace sandbox | |
| OLD | NEW |