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/src/sandbox_policy_base.h" | |
6 | |
7 #include "base/basictypes.h" | |
8 #include "base/callback.h" | |
9 #include "base/logging.h" | |
10 #include "sandbox/src/filesystem_dispatcher.h" | |
11 #include "sandbox/src/filesystem_policy.h" | |
12 #include "sandbox/src/handle_dispatcher.h" | |
13 #include "sandbox/src/handle_policy.h" | |
14 #include "sandbox/src/job.h" | |
15 #include "sandbox/src/interception.h" | |
16 #include "sandbox/src/named_pipe_dispatcher.h" | |
17 #include "sandbox/src/named_pipe_policy.h" | |
18 #include "sandbox/src/policy_broker.h" | |
19 #include "sandbox/src/policy_engine_processor.h" | |
20 #include "sandbox/src/policy_low_level.h" | |
21 #include "sandbox/src/process_thread_dispatcher.h" | |
22 #include "sandbox/src/process_thread_policy.h" | |
23 #include "sandbox/src/registry_dispatcher.h" | |
24 #include "sandbox/src/registry_policy.h" | |
25 #include "sandbox/src/restricted_token_utils.h" | |
26 #include "sandbox/src/sandbox_policy.h" | |
27 #include "sandbox/src/sync_dispatcher.h" | |
28 #include "sandbox/src/sync_policy.h" | |
29 #include "sandbox/src/target_process.h" | |
30 #include "sandbox/src/window.h" | |
31 | |
32 namespace { | |
33 // The standard windows size for one memory page. | |
34 const size_t kOneMemPage = 4096; | |
35 // The IPC and Policy shared memory sizes. | |
36 const size_t kIPCMemSize = kOneMemPage * 2; | |
37 const size_t kPolMemSize = kOneMemPage * 14; | |
38 | |
39 // Helper function to allocate space (on the heap) for policy. | |
40 sandbox::PolicyGlobal* MakeBrokerPolicyMemory() { | |
41 const size_t kTotalPolicySz = kPolMemSize; | |
42 char* mem = new char[kTotalPolicySz]; | |
43 DCHECK(mem); | |
44 memset(mem, 0, kTotalPolicySz); | |
45 sandbox::PolicyGlobal* policy = reinterpret_cast<sandbox::PolicyGlobal*>(mem); | |
46 policy->data_size = kTotalPolicySz - sizeof(sandbox::PolicyGlobal); | |
47 return policy; | |
48 } | |
49 } | |
50 | |
51 namespace sandbox { | |
52 | |
53 SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level; | |
54 | |
55 // Initializes static members. | |
56 HWINSTA PolicyBase::alternate_winstation_handle_ = NULL; | |
57 HDESK PolicyBase::alternate_desktop_handle_ = NULL; | |
58 | |
59 PolicyBase::PolicyBase() | |
60 : ref_count(1), | |
61 lockdown_level_(USER_LOCKDOWN), | |
62 initial_level_(USER_LOCKDOWN), | |
63 job_level_(JOB_LOCKDOWN), | |
64 ui_exceptions_(0), | |
65 use_alternate_desktop_(false), | |
66 use_alternate_winstation_(false), | |
67 file_system_init_(false), | |
68 relaxed_interceptions_(true), | |
69 integrity_level_(INTEGRITY_LEVEL_LAST), | |
70 delayed_integrity_level_(INTEGRITY_LEVEL_LAST), | |
71 policy_maker_(NULL), | |
72 policy_(NULL) { | |
73 ::InitializeCriticalSection(&lock_); | |
74 // Initialize the IPC dispatcher array. | |
75 memset(&ipc_targets_, NULL, sizeof(ipc_targets_)); | |
76 Dispatcher* dispatcher = NULL; | |
77 | |
78 dispatcher = new FilesystemDispatcher(this); | |
79 ipc_targets_[IPC_NTCREATEFILE_TAG] = dispatcher; | |
80 ipc_targets_[IPC_NTOPENFILE_TAG] = dispatcher; | |
81 ipc_targets_[IPC_NTSETINFO_RENAME_TAG] = dispatcher; | |
82 ipc_targets_[IPC_NTQUERYATTRIBUTESFILE_TAG] = dispatcher; | |
83 ipc_targets_[IPC_NTQUERYFULLATTRIBUTESFILE_TAG] = dispatcher; | |
84 | |
85 dispatcher = new NamedPipeDispatcher(this); | |
86 ipc_targets_[IPC_CREATENAMEDPIPEW_TAG] = dispatcher; | |
87 | |
88 dispatcher = new ThreadProcessDispatcher(this); | |
89 ipc_targets_[IPC_NTOPENTHREAD_TAG] = dispatcher; | |
90 ipc_targets_[IPC_NTOPENPROCESS_TAG] = dispatcher; | |
91 ipc_targets_[IPC_CREATEPROCESSW_TAG] = dispatcher; | |
92 ipc_targets_[IPC_NTOPENPROCESSTOKEN_TAG] = dispatcher; | |
93 ipc_targets_[IPC_NTOPENPROCESSTOKENEX_TAG] = dispatcher; | |
94 | |
95 dispatcher = new SyncDispatcher(this); | |
96 ipc_targets_[IPC_CREATEEVENT_TAG] = dispatcher; | |
97 ipc_targets_[IPC_OPENEVENT_TAG] = dispatcher; | |
98 | |
99 dispatcher = new RegistryDispatcher(this); | |
100 ipc_targets_[IPC_NTCREATEKEY_TAG] = dispatcher; | |
101 ipc_targets_[IPC_NTOPENKEY_TAG] = dispatcher; | |
102 | |
103 dispatcher = new HandleDispatcher(this); | |
104 ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG] = dispatcher; | |
105 } | |
106 | |
107 PolicyBase::~PolicyBase() { | |
108 TargetSet::iterator it; | |
109 for (it = targets_.begin(); it != targets_.end(); ++it) { | |
110 TargetProcess* target = (*it); | |
111 delete target; | |
112 } | |
113 delete ipc_targets_[IPC_NTCREATEFILE_TAG]; | |
114 delete ipc_targets_[IPC_CREATENAMEDPIPEW_TAG]; | |
115 delete ipc_targets_[IPC_NTOPENTHREAD_TAG]; | |
116 delete ipc_targets_[IPC_CREATEEVENT_TAG]; | |
117 delete ipc_targets_[IPC_NTCREATEKEY_TAG]; | |
118 delete ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG]; | |
119 delete policy_maker_; | |
120 delete policy_; | |
121 ::DeleteCriticalSection(&lock_); | |
122 } | |
123 | |
124 void PolicyBase::AddRef() { | |
125 ::InterlockedIncrement(&ref_count); | |
126 } | |
127 | |
128 void PolicyBase::Release() { | |
129 if (0 == ::InterlockedDecrement(&ref_count)) | |
130 delete this; | |
131 } | |
132 | |
133 ResultCode PolicyBase::SetTokenLevel(TokenLevel initial, TokenLevel lockdown) { | |
134 if (initial < lockdown) { | |
135 return SBOX_ERROR_BAD_PARAMS; | |
136 } | |
137 initial_level_ = initial; | |
138 lockdown_level_ = lockdown; | |
139 return SBOX_ALL_OK; | |
140 } | |
141 | |
142 ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32 ui_exceptions) { | |
143 job_level_ = job_level; | |
144 ui_exceptions_ = ui_exceptions; | |
145 return SBOX_ALL_OK; | |
146 } | |
147 | |
148 ResultCode PolicyBase::SetAlternateDesktop(bool alternate_winstation) { | |
149 use_alternate_desktop_ = true; | |
150 use_alternate_winstation_ = alternate_winstation; | |
151 return CreateAlternateDesktop(alternate_winstation); | |
152 } | |
153 | |
154 std::wstring PolicyBase::GetAlternateDesktop() const { | |
155 // No alternate desktop or winstation. Return an empty string. | |
156 if (!use_alternate_desktop_ && !use_alternate_winstation_) { | |
157 return std::wstring(); | |
158 } | |
159 | |
160 // The desktop and winstation should have been created by now. | |
161 // If we hit this scenario, it means that the user ignored the failure | |
162 // during SetAlternateDesktop, so we ignore it here too. | |
163 if (use_alternate_desktop_ && !alternate_desktop_handle_) { | |
164 return std::wstring(); | |
165 } | |
166 if (use_alternate_winstation_ && (!alternate_desktop_handle_ || | |
167 !alternate_winstation_handle_)) { | |
168 return std::wstring(); | |
169 } | |
170 | |
171 return GetFullDesktopName(alternate_winstation_handle_, | |
172 alternate_desktop_handle_); | |
173 } | |
174 | |
175 ResultCode PolicyBase::CreateAlternateDesktop(bool alternate_winstation) { | |
176 if (alternate_winstation) { | |
177 // Previously called with alternate_winstation = false? | |
178 if (!alternate_winstation_handle_ && alternate_desktop_handle_) | |
179 return SBOX_ERROR_UNSUPPORTED; | |
180 | |
181 // Check if it's already created. | |
182 if (alternate_winstation_handle_ && alternate_desktop_handle_) | |
183 return SBOX_ALL_OK; | |
184 | |
185 DCHECK(!alternate_winstation_handle_); | |
186 // Create the window station. | |
187 ResultCode result = CreateAltWindowStation(&alternate_winstation_handle_); | |
188 if (SBOX_ALL_OK != result) | |
189 return result; | |
190 | |
191 // Verify that everything is fine. | |
192 if (!alternate_winstation_handle_ || | |
193 GetWindowObjectName(alternate_winstation_handle_).empty()) | |
194 return SBOX_ERROR_CANNOT_CREATE_DESKTOP; | |
195 | |
196 // Create the destkop. | |
197 result = CreateAltDesktop(alternate_winstation_handle_, | |
198 &alternate_desktop_handle_); | |
199 if (SBOX_ALL_OK != result) | |
200 return result; | |
201 | |
202 // Verify that everything is fine. | |
203 if (!alternate_desktop_handle_ || | |
204 GetWindowObjectName(alternate_desktop_handle_).empty()) | |
205 return SBOX_ERROR_CANNOT_CREATE_DESKTOP; | |
206 } else { | |
207 // Previously called with alternate_winstation = true? | |
208 if (alternate_winstation_handle_) | |
209 return SBOX_ERROR_UNSUPPORTED; | |
210 | |
211 // Check if it already exists. | |
212 if (alternate_desktop_handle_) | |
213 return SBOX_ALL_OK; | |
214 | |
215 // Create the destkop. | |
216 ResultCode result = CreateAltDesktop(NULL, &alternate_desktop_handle_); | |
217 if (SBOX_ALL_OK != result) | |
218 return result; | |
219 | |
220 // Verify that everything is fine. | |
221 if (!alternate_desktop_handle_ || | |
222 GetWindowObjectName(alternate_desktop_handle_).empty()) | |
223 return SBOX_ERROR_CANNOT_CREATE_DESKTOP; | |
224 } | |
225 | |
226 return SBOX_ALL_OK; | |
227 } | |
228 | |
229 void PolicyBase::DestroyAlternateDesktop() { | |
230 if (alternate_desktop_handle_) { | |
231 ::CloseDesktop(alternate_desktop_handle_); | |
232 alternate_desktop_handle_ = NULL; | |
233 } | |
234 | |
235 if (alternate_winstation_handle_) { | |
236 ::CloseWindowStation(alternate_winstation_handle_); | |
237 alternate_winstation_handle_ = NULL; | |
238 } | |
239 } | |
240 | |
241 ResultCode PolicyBase::SetIntegrityLevel(IntegrityLevel integrity_level) { | |
242 integrity_level_ = integrity_level; | |
243 return SBOX_ALL_OK; | |
244 } | |
245 | |
246 ResultCode PolicyBase::SetDelayedIntegrityLevel( | |
247 IntegrityLevel integrity_level) { | |
248 delayed_integrity_level_ = integrity_level; | |
249 return SBOX_ALL_OK; | |
250 } | |
251 | |
252 void PolicyBase::SetStrictInterceptions() { | |
253 relaxed_interceptions_ = false; | |
254 } | |
255 | |
256 ResultCode PolicyBase::AddRule(SubSystem subsystem, Semantics semantics, | |
257 const wchar_t* pattern) { | |
258 if (NULL == policy_) { | |
259 policy_ = MakeBrokerPolicyMemory(); | |
260 DCHECK(policy_); | |
261 policy_maker_ = new LowLevelPolicy(policy_); | |
262 DCHECK(policy_maker_); | |
263 } | |
264 | |
265 switch (subsystem) { | |
266 case SUBSYS_FILES: { | |
267 if (!file_system_init_) { | |
268 if (!FileSystemPolicy::SetInitialRules(policy_maker_)) | |
269 return SBOX_ERROR_BAD_PARAMS; | |
270 file_system_init_ = true; | |
271 } | |
272 if (!FileSystemPolicy::GenerateRules(pattern, semantics, policy_maker_)) { | |
273 NOTREACHED(); | |
274 return SBOX_ERROR_BAD_PARAMS; | |
275 } | |
276 break; | |
277 } | |
278 case SUBSYS_SYNC: { | |
279 if (!SyncPolicy::GenerateRules(pattern, semantics, policy_maker_)) { | |
280 NOTREACHED(); | |
281 return SBOX_ERROR_BAD_PARAMS; | |
282 } | |
283 break; | |
284 } | |
285 case SUBSYS_PROCESS: { | |
286 if (lockdown_level_ < USER_INTERACTIVE && | |
287 TargetPolicy::PROCESS_ALL_EXEC == semantics) { | |
288 // This is unsupported. This is a huge security risk to give full access | |
289 // to a process handle. | |
290 return SBOX_ERROR_UNSUPPORTED; | |
291 } | |
292 if (!ProcessPolicy::GenerateRules(pattern, semantics, policy_maker_)) { | |
293 NOTREACHED(); | |
294 return SBOX_ERROR_BAD_PARAMS; | |
295 } | |
296 break; | |
297 } | |
298 case SUBSYS_NAMED_PIPES: { | |
299 if (!NamedPipePolicy::GenerateRules(pattern, semantics, policy_maker_)) { | |
300 NOTREACHED(); | |
301 return SBOX_ERROR_BAD_PARAMS; | |
302 } | |
303 break; | |
304 } | |
305 case SUBSYS_REGISTRY: { | |
306 if (!RegistryPolicy::GenerateRules(pattern, semantics, policy_maker_)) { | |
307 NOTREACHED(); | |
308 return SBOX_ERROR_BAD_PARAMS; | |
309 } | |
310 break; | |
311 } | |
312 case SUBSYS_HANDLES: { | |
313 if (!HandlePolicy::GenerateRules(pattern, semantics, policy_maker_)) { | |
314 NOTREACHED(); | |
315 return SBOX_ERROR_BAD_PARAMS; | |
316 } | |
317 break; | |
318 } | |
319 default: { | |
320 return SBOX_ERROR_UNSUPPORTED; | |
321 } | |
322 } | |
323 | |
324 return SBOX_ALL_OK; | |
325 } | |
326 | |
327 ResultCode PolicyBase::AddDllToUnload(const wchar_t* dll_name) { | |
328 blacklisted_dlls_.push_back(std::wstring(dll_name)); | |
329 return SBOX_ALL_OK; | |
330 } | |
331 | |
332 ResultCode PolicyBase::AddKernelObjectToClose(const char16* handle_type, | |
333 const char16* handle_name) { | |
334 return handle_closer_.AddHandle(handle_type, handle_name); | |
335 } | |
336 | |
337 // When an IPC is ready in any of the targets we get called. We manage an array | |
338 // of IPC dispatchers which are keyed on the IPC tag so we normally delegate | |
339 // to the appropriate dispatcher unless we can handle the IPC call ourselves. | |
340 Dispatcher* PolicyBase::OnMessageReady(IPCParams* ipc, | |
341 CallbackGeneric* callback) { | |
342 DCHECK(callback); | |
343 static const IPCParams ping1 = {IPC_PING1_TAG, ULONG_TYPE}; | |
344 static const IPCParams ping2 = {IPC_PING2_TAG, INOUTPTR_TYPE}; | |
345 | |
346 if (ping1.Matches(ipc) || ping2.Matches(ipc)) { | |
347 *callback = reinterpret_cast<CallbackGeneric>( | |
348 static_cast<Callback1>(&PolicyBase::Ping)); | |
349 return this; | |
350 } | |
351 | |
352 Dispatcher* dispatch = GetDispatcher(ipc->ipc_tag); | |
353 if (!dispatch) { | |
354 NOTREACHED(); | |
355 return NULL; | |
356 } | |
357 return dispatch->OnMessageReady(ipc, callback); | |
358 } | |
359 | |
360 // Delegate to the appropriate dispatcher. | |
361 bool PolicyBase::SetupService(InterceptionManager* manager, int service) { | |
362 if (IPC_PING1_TAG == service || IPC_PING2_TAG == service) | |
363 return true; | |
364 | |
365 Dispatcher* dispatch = GetDispatcher(service); | |
366 if (!dispatch) { | |
367 NOTREACHED(); | |
368 return false; | |
369 } | |
370 return dispatch->SetupService(manager, service); | |
371 } | |
372 | |
373 DWORD PolicyBase::MakeJobObject(HANDLE* job) { | |
374 // Create the windows job object. | |
375 Job job_obj; | |
376 DWORD result = job_obj.Init(job_level_, NULL, ui_exceptions_); | |
377 if (ERROR_SUCCESS != result) { | |
378 return result; | |
379 } | |
380 *job = job_obj.Detach(); | |
381 return ERROR_SUCCESS; | |
382 } | |
383 | |
384 DWORD PolicyBase::MakeTokens(HANDLE* initial, HANDLE* lockdown) { | |
385 // Create the 'naked' token. This will be the permanent token associated | |
386 // with the process and therefore with any thread that is not impersonating. | |
387 DWORD result = CreateRestrictedToken(lockdown, lockdown_level_, | |
388 integrity_level_, PRIMARY); | |
389 if (ERROR_SUCCESS != result) { | |
390 return result; | |
391 } | |
392 // Create the 'better' token. We use this token as the one that the main | |
393 // thread uses when booting up the process. It should contain most of | |
394 // what we need (before reaching main( )) | |
395 result = CreateRestrictedToken(initial, initial_level_, | |
396 integrity_level_, IMPERSONATION); | |
397 if (ERROR_SUCCESS != result) { | |
398 ::CloseHandle(*lockdown); | |
399 return result; | |
400 } | |
401 return SBOX_ALL_OK; | |
402 } | |
403 | |
404 bool PolicyBase::AddTarget(TargetProcess* target) { | |
405 if (NULL != policy_) | |
406 policy_maker_->Done(); | |
407 | |
408 if (!SetupAllInterceptions(target)) | |
409 return false; | |
410 | |
411 if (!SetupHandleCloser(target)) | |
412 return false; | |
413 | |
414 // Initialize the sandbox infrastructure for the target. | |
415 if (ERROR_SUCCESS != target->Init(this, policy_, kIPCMemSize, kPolMemSize)) | |
416 return false; | |
417 | |
418 g_shared_delayed_integrity_level = delayed_integrity_level_; | |
419 ResultCode ret = target->TransferVariable( | |
420 "g_shared_delayed_integrity_level", | |
421 &g_shared_delayed_integrity_level, | |
422 sizeof(g_shared_delayed_integrity_level)); | |
423 g_shared_delayed_integrity_level = INTEGRITY_LEVEL_LAST; | |
424 if (SBOX_ALL_OK != ret) | |
425 return false; | |
426 | |
427 AutoLock lock(&lock_); | |
428 targets_.push_back(target); | |
429 return true; | |
430 } | |
431 | |
432 bool PolicyBase::OnJobEmpty(HANDLE job) { | |
433 AutoLock lock(&lock_); | |
434 TargetSet::iterator it; | |
435 for (it = targets_.begin(); it != targets_.end(); ++it) { | |
436 if ((*it)->Job() == job) | |
437 break; | |
438 } | |
439 if (it == targets_.end()) { | |
440 return false; | |
441 } | |
442 TargetProcess* target = *it; | |
443 targets_.erase(it); | |
444 delete target; | |
445 return true; | |
446 } | |
447 | |
448 EvalResult PolicyBase::EvalPolicy(int service, | |
449 CountedParameterSetBase* params) { | |
450 if (NULL != policy_) { | |
451 if (NULL == policy_->entry[service]) { | |
452 // There is no policy for this particular service. This is not a big | |
453 // deal. | |
454 return DENY_ACCESS; | |
455 } | |
456 for (int i = 0; i < params->count; i++) { | |
457 if (!params->parameters[i].IsValid()) { | |
458 NOTREACHED(); | |
459 return SIGNAL_ALARM; | |
460 } | |
461 } | |
462 PolicyProcessor pol_evaluator(policy_->entry[service]); | |
463 PolicyResult result = pol_evaluator.Evaluate(kShortEval, | |
464 params->parameters, | |
465 params->count); | |
466 if (POLICY_MATCH == result) { | |
467 return pol_evaluator.GetAction(); | |
468 } | |
469 DCHECK(POLICY_ERROR != result); | |
470 } | |
471 | |
472 return DENY_ACCESS; | |
473 } | |
474 | |
475 // We service IPC_PING_TAG message which is a way to test a round trip of the | |
476 // IPC subsystem. We receive a integer cookie and we are expected to return the | |
477 // cookie times two (or three) and the current tick count. | |
478 bool PolicyBase::Ping(IPCInfo* ipc, void* arg1) { | |
479 switch (ipc->ipc_tag) { | |
480 case IPC_PING1_TAG: { | |
481 IPCInt ipc_int(arg1); | |
482 uint32 cookie = ipc_int.As32Bit(); | |
483 ipc->return_info.extended_count = 2; | |
484 ipc->return_info.extended[0].unsigned_int = ::GetTickCount(); | |
485 ipc->return_info.extended[1].unsigned_int = 2 * cookie; | |
486 return true; | |
487 } | |
488 case IPC_PING2_TAG: { | |
489 CountedBuffer* io_buffer = reinterpret_cast<CountedBuffer*>(arg1); | |
490 if (sizeof(uint32) != io_buffer->Size()) | |
491 return false; | |
492 | |
493 uint32* cookie = reinterpret_cast<uint32*>(io_buffer->Buffer()); | |
494 *cookie = (*cookie) * 3; | |
495 return true; | |
496 } | |
497 default: return false; | |
498 } | |
499 } | |
500 | |
501 Dispatcher* PolicyBase::GetDispatcher(int ipc_tag) { | |
502 if (ipc_tag >= IPC_LAST_TAG || ipc_tag <= IPC_UNUSED_TAG) | |
503 return NULL; | |
504 | |
505 return ipc_targets_[ipc_tag]; | |
506 } | |
507 | |
508 bool PolicyBase::SetupAllInterceptions(TargetProcess* target) { | |
509 InterceptionManager manager(target, relaxed_interceptions_); | |
510 | |
511 if (policy_) { | |
512 for (int i = 0; i < IPC_LAST_TAG; i++) { | |
513 if (policy_->entry[i] && !ipc_targets_[i]->SetupService(&manager, i)) | |
514 return false; | |
515 } | |
516 } | |
517 | |
518 if (!blacklisted_dlls_.empty()) { | |
519 std::vector<std::wstring>::iterator it = blacklisted_dlls_.begin(); | |
520 for (; it != blacklisted_dlls_.end(); ++it) { | |
521 manager.AddToUnloadModules(it->c_str()); | |
522 } | |
523 } | |
524 | |
525 if (!handle_closer_.SetupHandleInterceptions(&manager)) | |
526 return false; | |
527 | |
528 if (!SetupBasicInterceptions(&manager)) | |
529 return false; | |
530 | |
531 if (!manager.InitializeInterceptions()) | |
532 return false; | |
533 | |
534 // Finally, setup imports on the target so the interceptions can work. | |
535 return SetupNtdllImports(target); | |
536 } | |
537 | |
538 bool PolicyBase::SetupHandleCloser(TargetProcess* target) { | |
539 return handle_closer_.InitializeTargetHandles(target); | |
540 } | |
541 | |
542 } // namespace sandbox | |
OLD | NEW |