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.
5 #include "sandbox/win/src/sandbox_policy_base.h"
7 #include "base/basictypes.h"
8 #include "base/callback.h"
9 #include "base/logging.h"
10 #include "base/win/windows_version.h"
11 #include "sandbox/win/src/app_container.h"
12 #include "sandbox/win/src/filesystem_dispatcher.h"
13 #include "sandbox/win/src/filesystem_policy.h"
14 #include "sandbox/win/src/handle_dispatcher.h"
15 #include "sandbox/win/src/handle_policy.h"
16 #include "sandbox/win/src/job.h"
17 #include "sandbox/win/src/interception.h"
18 #include "sandbox/win/src/process_mitigations.h"
19 #include "sandbox/win/src/named_pipe_dispatcher.h"
20 #include "sandbox/win/src/named_pipe_policy.h"
21 #include "sandbox/win/src/policy_broker.h"
22 #include "sandbox/win/src/policy_engine_processor.h"
23 #include "sandbox/win/src/policy_low_level.h"
24 #include "sandbox/win/src/process_thread_dispatcher.h"
25 #include "sandbox/win/src/process_thread_policy.h"
26 #include "sandbox/win/src/registry_dispatcher.h"
27 #include "sandbox/win/src/registry_policy.h"
28 #include "sandbox/win/src/restricted_token_utils.h"
29 #include "sandbox/win/src/sandbox_policy.h"
30 #include "sandbox/win/src/sync_dispatcher.h"
31 #include "sandbox/win/src/sync_policy.h"
32 #include "sandbox/win/src/target_process.h"
33 #include "sandbox/win/src/window.h"
37 // The standard windows size for one memory page.
38 const size_t kOneMemPage
= 4096;
39 // The IPC and Policy shared memory sizes.
40 const size_t kIPCMemSize
= kOneMemPage
* 2;
41 const size_t kPolMemSize
= kOneMemPage
* 14;
43 // Helper function to allocate space (on the heap) for policy.
44 sandbox::PolicyGlobal
* MakeBrokerPolicyMemory() {
45 const size_t kTotalPolicySz
= kPolMemSize
;
46 sandbox::PolicyGlobal
* policy
= static_cast<sandbox::PolicyGlobal
*>
47 (::operator new(kTotalPolicySz
));
49 memset(policy
, 0, kTotalPolicySz
);
50 policy
->data_size
= kTotalPolicySz
- sizeof(sandbox::PolicyGlobal
);
54 bool IsInheritableHandle(HANDLE handle
) {
57 if (handle
== INVALID_HANDLE_VALUE
)
59 // File handles (FILE_TYPE_DISK) and pipe handles are known to be
60 // inheritable. Console handles (FILE_TYPE_CHAR) are not
61 // inheritable via PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
62 DWORD handle_type
= GetFileType(handle
);
63 return handle_type
== FILE_TYPE_DISK
|| handle_type
== FILE_TYPE_PIPE
;
70 SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level
;
71 SANDBOX_INTERCEPT MitigationFlags g_shared_delayed_mitigations
;
73 // Initializes static members.
74 HWINSTA
PolicyBase::alternate_winstation_handle_
= NULL
;
75 HDESK
PolicyBase::alternate_desktop_handle_
= NULL
;
77 PolicyBase::PolicyBase()
79 lockdown_level_(USER_LOCKDOWN
),
80 initial_level_(USER_LOCKDOWN
),
81 job_level_(JOB_LOCKDOWN
),
83 use_alternate_desktop_(false),
84 use_alternate_winstation_(false),
85 file_system_init_(false),
86 relaxed_interceptions_(true),
87 stdout_handle_(INVALID_HANDLE_VALUE
),
88 stderr_handle_(INVALID_HANDLE_VALUE
),
89 integrity_level_(INTEGRITY_LEVEL_LAST
),
90 delayed_integrity_level_(INTEGRITY_LEVEL_LAST
),
92 delayed_mitigations_(0),
95 ::InitializeCriticalSection(&lock_
);
96 // Initialize the IPC dispatcher array.
97 memset(&ipc_targets_
, NULL
, sizeof(ipc_targets_
));
98 Dispatcher
* dispatcher
= NULL
;
100 dispatcher
= new FilesystemDispatcher(this);
101 ipc_targets_
[IPC_NTCREATEFILE_TAG
] = dispatcher
;
102 ipc_targets_
[IPC_NTOPENFILE_TAG
] = dispatcher
;
103 ipc_targets_
[IPC_NTSETINFO_RENAME_TAG
] = dispatcher
;
104 ipc_targets_
[IPC_NTQUERYATTRIBUTESFILE_TAG
] = dispatcher
;
105 ipc_targets_
[IPC_NTQUERYFULLATTRIBUTESFILE_TAG
] = dispatcher
;
107 dispatcher
= new NamedPipeDispatcher(this);
108 ipc_targets_
[IPC_CREATENAMEDPIPEW_TAG
] = dispatcher
;
110 dispatcher
= new ThreadProcessDispatcher(this);
111 ipc_targets_
[IPC_NTOPENTHREAD_TAG
] = dispatcher
;
112 ipc_targets_
[IPC_NTOPENPROCESS_TAG
] = dispatcher
;
113 ipc_targets_
[IPC_CREATEPROCESSW_TAG
] = dispatcher
;
114 ipc_targets_
[IPC_NTOPENPROCESSTOKEN_TAG
] = dispatcher
;
115 ipc_targets_
[IPC_NTOPENPROCESSTOKENEX_TAG
] = dispatcher
;
117 dispatcher
= new SyncDispatcher(this);
118 ipc_targets_
[IPC_CREATEEVENT_TAG
] = dispatcher
;
119 ipc_targets_
[IPC_OPENEVENT_TAG
] = dispatcher
;
121 dispatcher
= new RegistryDispatcher(this);
122 ipc_targets_
[IPC_NTCREATEKEY_TAG
] = dispatcher
;
123 ipc_targets_
[IPC_NTOPENKEY_TAG
] = dispatcher
;
125 dispatcher
= new HandleDispatcher(this);
126 ipc_targets_
[IPC_DUPLICATEHANDLEPROXY_TAG
] = dispatcher
;
129 PolicyBase::~PolicyBase() {
130 TargetSet::iterator it
;
131 for (it
= targets_
.begin(); it
!= targets_
.end(); ++it
) {
132 TargetProcess
* target
= (*it
);
135 delete ipc_targets_
[IPC_NTCREATEFILE_TAG
];
136 delete ipc_targets_
[IPC_CREATENAMEDPIPEW_TAG
];
137 delete ipc_targets_
[IPC_NTOPENTHREAD_TAG
];
138 delete ipc_targets_
[IPC_CREATEEVENT_TAG
];
139 delete ipc_targets_
[IPC_NTCREATEKEY_TAG
];
140 delete ipc_targets_
[IPC_DUPLICATEHANDLEPROXY_TAG
];
141 delete policy_maker_
;
143 ::DeleteCriticalSection(&lock_
);
146 void PolicyBase::AddRef() {
147 ::InterlockedIncrement(&ref_count
);
150 void PolicyBase::Release() {
151 if (0 == ::InterlockedDecrement(&ref_count
))
155 ResultCode
PolicyBase::SetTokenLevel(TokenLevel initial
, TokenLevel lockdown
) {
156 if (initial
< lockdown
) {
157 return SBOX_ERROR_BAD_PARAMS
;
159 initial_level_
= initial
;
160 lockdown_level_
= lockdown
;
164 ResultCode
PolicyBase::SetJobLevel(JobLevel job_level
, uint32 ui_exceptions
) {
165 job_level_
= job_level
;
166 ui_exceptions_
= ui_exceptions
;
170 ResultCode
PolicyBase::SetAlternateDesktop(bool alternate_winstation
) {
171 use_alternate_desktop_
= true;
172 use_alternate_winstation_
= alternate_winstation
;
173 return CreateAlternateDesktop(alternate_winstation
);
176 string16
PolicyBase::GetAlternateDesktop() const {
177 // No alternate desktop or winstation. Return an empty string.
178 if (!use_alternate_desktop_
&& !use_alternate_winstation_
) {
182 // The desktop and winstation should have been created by now.
183 // If we hit this scenario, it means that the user ignored the failure
184 // during SetAlternateDesktop, so we ignore it here too.
185 if (use_alternate_desktop_
&& !alternate_desktop_handle_
) {
188 if (use_alternate_winstation_
&& (!alternate_desktop_handle_
||
189 !alternate_winstation_handle_
)) {
193 return GetFullDesktopName(alternate_winstation_handle_
,
194 alternate_desktop_handle_
);
197 ResultCode
PolicyBase::CreateAlternateDesktop(bool alternate_winstation
) {
198 if (alternate_winstation
) {
199 // Previously called with alternate_winstation = false?
200 if (!alternate_winstation_handle_
&& alternate_desktop_handle_
)
201 return SBOX_ERROR_UNSUPPORTED
;
203 // Check if it's already created.
204 if (alternate_winstation_handle_
&& alternate_desktop_handle_
)
207 DCHECK(!alternate_winstation_handle_
);
208 // Create the window station.
209 ResultCode result
= CreateAltWindowStation(&alternate_winstation_handle_
);
210 if (SBOX_ALL_OK
!= result
)
213 // Verify that everything is fine.
214 if (!alternate_winstation_handle_
||
215 GetWindowObjectName(alternate_winstation_handle_
).empty())
216 return SBOX_ERROR_CANNOT_CREATE_DESKTOP
;
218 // Create the destkop.
219 result
= CreateAltDesktop(alternate_winstation_handle_
,
220 &alternate_desktop_handle_
);
221 if (SBOX_ALL_OK
!= result
)
224 // Verify that everything is fine.
225 if (!alternate_desktop_handle_
||
226 GetWindowObjectName(alternate_desktop_handle_
).empty())
227 return SBOX_ERROR_CANNOT_CREATE_DESKTOP
;
229 // Previously called with alternate_winstation = true?
230 if (alternate_winstation_handle_
)
231 return SBOX_ERROR_UNSUPPORTED
;
233 // Check if it already exists.
234 if (alternate_desktop_handle_
)
237 // Create the destkop.
238 ResultCode result
= CreateAltDesktop(NULL
, &alternate_desktop_handle_
);
239 if (SBOX_ALL_OK
!= result
)
242 // Verify that everything is fine.
243 if (!alternate_desktop_handle_
||
244 GetWindowObjectName(alternate_desktop_handle_
).empty())
245 return SBOX_ERROR_CANNOT_CREATE_DESKTOP
;
251 void PolicyBase::DestroyAlternateDesktop() {
252 if (alternate_desktop_handle_
) {
253 ::CloseDesktop(alternate_desktop_handle_
);
254 alternate_desktop_handle_
= NULL
;
257 if (alternate_winstation_handle_
) {
258 ::CloseWindowStation(alternate_winstation_handle_
);
259 alternate_winstation_handle_
= NULL
;
263 ResultCode
PolicyBase::SetIntegrityLevel(IntegrityLevel integrity_level
) {
264 integrity_level_
= integrity_level
;
268 ResultCode
PolicyBase::SetDelayedIntegrityLevel(
269 IntegrityLevel integrity_level
) {
270 delayed_integrity_level_
= integrity_level
;
274 ResultCode
PolicyBase::SetAppContainer(const wchar_t* sid
) {
275 if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8
)
278 // Windows refuses to work with an impersonation token for a process inside
279 // an AppContainer. If the caller wants to use a more privileged initial
280 // token, or if the lockdown level will prevent the process from starting,
281 // we have to fail the operation.
282 if (lockdown_level_
< USER_LIMITED
|| lockdown_level_
!= initial_level_
)
283 return SBOX_ERROR_CANNOT_INIT_APPCONTAINER
;
285 DCHECK(!appcontainer_list_
.get());
286 appcontainer_list_
.reset(new AppContainerAttributes
);
287 ResultCode rv
= appcontainer_list_
->SetAppContainer(sid
, capabilities_
);
288 if (rv
!= SBOX_ALL_OK
)
294 ResultCode
PolicyBase::SetCapability(const wchar_t* sid
) {
295 capabilities_
.push_back(sid
);
299 ResultCode
PolicyBase::SetProcessMitigations(
300 MitigationFlags flags
) {
301 if (!CanSetProcessMitigationsPreStartup(flags
))
302 return SBOX_ERROR_BAD_PARAMS
;
303 mitigations_
= flags
;
307 MitigationFlags
PolicyBase::GetProcessMitigations() {
311 ResultCode
PolicyBase::SetDelayedProcessMitigations(
312 MitigationFlags flags
) {
313 if (!CanSetProcessMitigationsPostStartup(flags
))
314 return SBOX_ERROR_BAD_PARAMS
;
315 delayed_mitigations_
= flags
;
319 MitigationFlags
PolicyBase::GetDelayedProcessMitigations() {
320 return delayed_mitigations_
;
323 void PolicyBase::SetStrictInterceptions() {
324 relaxed_interceptions_
= false;
327 ResultCode
PolicyBase::SetStdoutHandle(HANDLE handle
) {
328 if (!IsInheritableHandle(handle
))
329 return SBOX_ERROR_BAD_PARAMS
;
330 stdout_handle_
= handle
;
334 ResultCode
PolicyBase::SetStderrHandle(HANDLE handle
) {
335 if (!IsInheritableHandle(handle
))
336 return SBOX_ERROR_BAD_PARAMS
;
337 stderr_handle_
= handle
;
341 ResultCode
PolicyBase::AddRule(SubSystem subsystem
, Semantics semantics
,
342 const wchar_t* pattern
) {
343 if (NULL
== policy_
) {
344 policy_
= MakeBrokerPolicyMemory();
346 policy_maker_
= new LowLevelPolicy(policy_
);
347 DCHECK(policy_maker_
);
352 if (!file_system_init_
) {
353 if (!FileSystemPolicy::SetInitialRules(policy_maker_
))
354 return SBOX_ERROR_BAD_PARAMS
;
355 file_system_init_
= true;
357 if (!FileSystemPolicy::GenerateRules(pattern
, semantics
, policy_maker_
)) {
359 return SBOX_ERROR_BAD_PARAMS
;
364 if (!SyncPolicy::GenerateRules(pattern
, semantics
, policy_maker_
)) {
366 return SBOX_ERROR_BAD_PARAMS
;
370 case SUBSYS_PROCESS
: {
371 if (lockdown_level_
< USER_INTERACTIVE
&&
372 TargetPolicy::PROCESS_ALL_EXEC
== semantics
) {
373 // This is unsupported. This is a huge security risk to give full access
374 // to a process handle.
375 return SBOX_ERROR_UNSUPPORTED
;
377 if (!ProcessPolicy::GenerateRules(pattern
, semantics
, policy_maker_
)) {
379 return SBOX_ERROR_BAD_PARAMS
;
383 case SUBSYS_NAMED_PIPES
: {
384 if (!NamedPipePolicy::GenerateRules(pattern
, semantics
, policy_maker_
)) {
386 return SBOX_ERROR_BAD_PARAMS
;
390 case SUBSYS_REGISTRY
: {
391 if (!RegistryPolicy::GenerateRules(pattern
, semantics
, policy_maker_
)) {
393 return SBOX_ERROR_BAD_PARAMS
;
397 case SUBSYS_HANDLES
: {
398 if (!HandlePolicy::GenerateRules(pattern
, semantics
, policy_maker_
)) {
400 return SBOX_ERROR_BAD_PARAMS
;
405 return SBOX_ERROR_UNSUPPORTED
;
412 ResultCode
PolicyBase::AddDllToUnload(const wchar_t* dll_name
) {
413 blacklisted_dlls_
.push_back(dll_name
);
417 ResultCode
PolicyBase::AddKernelObjectToClose(const char16
* handle_type
,
418 const char16
* handle_name
) {
419 return handle_closer_
.AddHandle(handle_type
, handle_name
);
422 // When an IPC is ready in any of the targets we get called. We manage an array
423 // of IPC dispatchers which are keyed on the IPC tag so we normally delegate
424 // to the appropriate dispatcher unless we can handle the IPC call ourselves.
425 Dispatcher
* PolicyBase::OnMessageReady(IPCParams
* ipc
,
426 CallbackGeneric
* callback
) {
428 static const IPCParams ping1
= {IPC_PING1_TAG
, ULONG_TYPE
};
429 static const IPCParams ping2
= {IPC_PING2_TAG
, INOUTPTR_TYPE
};
431 if (ping1
.Matches(ipc
) || ping2
.Matches(ipc
)) {
432 *callback
= reinterpret_cast<CallbackGeneric
>(
433 static_cast<Callback1
>(&PolicyBase::Ping
));
437 Dispatcher
* dispatch
= GetDispatcher(ipc
->ipc_tag
);
442 return dispatch
->OnMessageReady(ipc
, callback
);
445 // Delegate to the appropriate dispatcher.
446 bool PolicyBase::SetupService(InterceptionManager
* manager
, int service
) {
447 if (IPC_PING1_TAG
== service
|| IPC_PING2_TAG
== service
)
450 Dispatcher
* dispatch
= GetDispatcher(service
);
455 return dispatch
->SetupService(manager
, service
);
458 ResultCode
PolicyBase::MakeJobObject(HANDLE
* job
) {
459 if (job_level_
!= JOB_NONE
) {
460 // Create the windows job object.
462 DWORD result
= job_obj
.Init(job_level_
, NULL
, ui_exceptions_
);
463 if (ERROR_SUCCESS
!= result
) {
464 return SBOX_ERROR_GENERIC
;
466 *job
= job_obj
.Detach();
473 ResultCode
PolicyBase::MakeTokens(HANDLE
* initial
, HANDLE
* lockdown
) {
474 // Create the 'naked' token. This will be the permanent token associated
475 // with the process and therefore with any thread that is not impersonating.
476 DWORD result
= CreateRestrictedToken(lockdown
, lockdown_level_
,
477 integrity_level_
, PRIMARY
);
478 if (ERROR_SUCCESS
!= result
) {
479 return SBOX_ERROR_GENERIC
;
482 if (appcontainer_list_
.get() && appcontainer_list_
->HasAppContainer()) {
483 // Windows refuses to work with an impersonation token. See SetAppContainer
484 // implementation for more details.
485 if (lockdown_level_
< USER_LIMITED
|| lockdown_level_
!= initial_level_
)
486 return SBOX_ERROR_CANNOT_INIT_APPCONTAINER
;
488 *initial
= INVALID_HANDLE_VALUE
;
492 // Create the 'better' token. We use this token as the one that the main
493 // thread uses when booting up the process. It should contain most of
494 // what we need (before reaching main( ))
495 result
= CreateRestrictedToken(initial
, initial_level_
,
496 integrity_level_
, IMPERSONATION
);
497 if (ERROR_SUCCESS
!= result
) {
498 ::CloseHandle(*lockdown
);
499 return SBOX_ERROR_GENERIC
;
504 const AppContainerAttributes
* PolicyBase::GetAppContainer() {
505 if (!appcontainer_list_
.get() || !appcontainer_list_
->HasAppContainer())
508 return appcontainer_list_
.get();
511 bool PolicyBase::AddTarget(TargetProcess
* target
) {
513 policy_maker_
->Done();
515 if (!ApplyProcessMitigationsToSuspendedProcess(target
->Process(),
520 if (!SetupAllInterceptions(target
))
523 if (!SetupHandleCloser(target
))
526 // Initialize the sandbox infrastructure for the target.
527 if (ERROR_SUCCESS
!= target
->Init(this, policy_
, kIPCMemSize
, kPolMemSize
))
530 g_shared_delayed_integrity_level
= delayed_integrity_level_
;
531 ResultCode ret
= target
->TransferVariable(
532 "g_shared_delayed_integrity_level",
533 &g_shared_delayed_integrity_level
,
534 sizeof(g_shared_delayed_integrity_level
));
535 g_shared_delayed_integrity_level
= INTEGRITY_LEVEL_LAST
;
536 if (SBOX_ALL_OK
!= ret
)
539 // Add in delayed mitigations and pseudo-mitigations enforced at startup.
540 g_shared_delayed_mitigations
= delayed_mitigations_
|
541 FilterPostStartupProcessMitigations(mitigations_
);
542 if (!CanSetProcessMitigationsPostStartup(g_shared_delayed_mitigations
))
545 ret
= target
->TransferVariable("g_shared_delayed_mitigations",
546 &g_shared_delayed_mitigations
,
547 sizeof(g_shared_delayed_mitigations
));
548 g_shared_delayed_mitigations
= 0;
549 if (SBOX_ALL_OK
!= ret
)
552 AutoLock
lock(&lock_
);
553 targets_
.push_back(target
);
557 bool PolicyBase::OnJobEmpty(HANDLE job
) {
558 AutoLock
lock(&lock_
);
559 TargetSet::iterator it
;
560 for (it
= targets_
.begin(); it
!= targets_
.end(); ++it
) {
561 if ((*it
)->Job() == job
)
564 if (it
== targets_
.end()) {
567 TargetProcess
* target
= *it
;
573 EvalResult
PolicyBase::EvalPolicy(int service
,
574 CountedParameterSetBase
* params
) {
575 if (NULL
!= policy_
) {
576 if (NULL
== policy_
->entry
[service
]) {
577 // There is no policy for this particular service. This is not a big
581 for (int i
= 0; i
< params
->count
; i
++) {
582 if (!params
->parameters
[i
].IsValid()) {
587 PolicyProcessor
pol_evaluator(policy_
->entry
[service
]);
588 PolicyResult result
= pol_evaluator
.Evaluate(kShortEval
,
591 if (POLICY_MATCH
== result
) {
592 return pol_evaluator
.GetAction();
594 DCHECK(POLICY_ERROR
!= result
);
600 HANDLE
PolicyBase::GetStdoutHandle() {
601 return stdout_handle_
;
604 HANDLE
PolicyBase::GetStderrHandle() {
605 return stderr_handle_
;
608 // We service IPC_PING_TAG message which is a way to test a round trip of the
609 // IPC subsystem. We receive a integer cookie and we are expected to return the
610 // cookie times two (or three) and the current tick count.
611 bool PolicyBase::Ping(IPCInfo
* ipc
, void* arg1
) {
612 switch (ipc
->ipc_tag
) {
613 case IPC_PING1_TAG
: {
614 IPCInt
ipc_int(arg1
);
615 uint32 cookie
= ipc_int
.As32Bit();
616 ipc
->return_info
.extended_count
= 2;
617 ipc
->return_info
.extended
[0].unsigned_int
= ::GetTickCount();
618 ipc
->return_info
.extended
[1].unsigned_int
= 2 * cookie
;
621 case IPC_PING2_TAG
: {
622 CountedBuffer
* io_buffer
= reinterpret_cast<CountedBuffer
*>(arg1
);
623 if (sizeof(uint32
) != io_buffer
->Size())
626 uint32
* cookie
= reinterpret_cast<uint32
*>(io_buffer
->Buffer());
627 *cookie
= (*cookie
) * 3;
630 default: return false;
634 Dispatcher
* PolicyBase::GetDispatcher(int ipc_tag
) {
635 if (ipc_tag
>= IPC_LAST_TAG
|| ipc_tag
<= IPC_UNUSED_TAG
)
638 return ipc_targets_
[ipc_tag
];
641 bool PolicyBase::SetupAllInterceptions(TargetProcess
* target
) {
642 InterceptionManager
manager(target
, relaxed_interceptions_
);
645 for (int i
= 0; i
< IPC_LAST_TAG
; i
++) {
646 if (policy_
->entry
[i
] && !ipc_targets_
[i
]->SetupService(&manager
, i
))
651 if (!blacklisted_dlls_
.empty()) {
652 std::vector
<string16
>::iterator it
= blacklisted_dlls_
.begin();
653 for (; it
!= blacklisted_dlls_
.end(); ++it
) {
654 manager
.AddToUnloadModules(it
->c_str());
658 if (!handle_closer_
.SetupHandleInterceptions(&manager
))
661 if (!SetupBasicInterceptions(&manager
))
664 if (!manager
.InitializeInterceptions())
667 // Finally, setup imports on the target so the interceptions can work.
668 return SetupNtdllImports(target
);
671 bool PolicyBase::SetupHandleCloser(TargetProcess
* target
) {
672 return handle_closer_
.InitializeTargetHandles(target
);
675 } // namespace sandbox