- add sources.
[platform/framework/web/crosswalk.git] / src / sandbox / win / src / sandbox_policy_base.cc
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 "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"
34
35 namespace {
36
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;
42
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));
48   DCHECK(policy);
49   memset(policy, 0, kTotalPolicySz);
50   policy->data_size = kTotalPolicySz - sizeof(sandbox::PolicyGlobal);
51   return policy;
52 }
53
54 bool IsInheritableHandle(HANDLE handle) {
55   if (!handle)
56     return false;
57   if (handle == INVALID_HANDLE_VALUE)
58     return false;
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;
64 }
65
66 }
67
68 namespace sandbox {
69
70 SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level;
71 SANDBOX_INTERCEPT MitigationFlags g_shared_delayed_mitigations;
72
73 // Initializes static members.
74 HWINSTA PolicyBase::alternate_winstation_handle_ = NULL;
75 HDESK PolicyBase::alternate_desktop_handle_ = NULL;
76
77 PolicyBase::PolicyBase()
78     : ref_count(1),
79       lockdown_level_(USER_LOCKDOWN),
80       initial_level_(USER_LOCKDOWN),
81       job_level_(JOB_LOCKDOWN),
82       ui_exceptions_(0),
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),
91       mitigations_(0),
92       delayed_mitigations_(0),
93       policy_maker_(NULL),
94       policy_(NULL) {
95   ::InitializeCriticalSection(&lock_);
96   // Initialize the IPC dispatcher array.
97   memset(&ipc_targets_, NULL, sizeof(ipc_targets_));
98   Dispatcher* dispatcher = NULL;
99
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;
106
107   dispatcher = new NamedPipeDispatcher(this);
108   ipc_targets_[IPC_CREATENAMEDPIPEW_TAG] = dispatcher;
109
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;
116
117   dispatcher = new SyncDispatcher(this);
118   ipc_targets_[IPC_CREATEEVENT_TAG] = dispatcher;
119   ipc_targets_[IPC_OPENEVENT_TAG] = dispatcher;
120
121   dispatcher = new RegistryDispatcher(this);
122   ipc_targets_[IPC_NTCREATEKEY_TAG] = dispatcher;
123   ipc_targets_[IPC_NTOPENKEY_TAG] = dispatcher;
124
125   dispatcher = new HandleDispatcher(this);
126   ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG] = dispatcher;
127 }
128
129 PolicyBase::~PolicyBase() {
130   TargetSet::iterator it;
131   for (it = targets_.begin(); it != targets_.end(); ++it) {
132     TargetProcess* target = (*it);
133     delete target;
134   }
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_;
142   delete policy_;
143   ::DeleteCriticalSection(&lock_);
144 }
145
146 void PolicyBase::AddRef() {
147   ::InterlockedIncrement(&ref_count);
148 }
149
150 void PolicyBase::Release() {
151   if (0 == ::InterlockedDecrement(&ref_count))
152     delete this;
153 }
154
155 ResultCode PolicyBase::SetTokenLevel(TokenLevel initial, TokenLevel lockdown) {
156   if (initial < lockdown) {
157     return SBOX_ERROR_BAD_PARAMS;
158   }
159   initial_level_ = initial;
160   lockdown_level_ = lockdown;
161   return SBOX_ALL_OK;
162 }
163
164 ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32 ui_exceptions) {
165   job_level_ = job_level;
166   ui_exceptions_ = ui_exceptions;
167   return SBOX_ALL_OK;
168 }
169
170 ResultCode PolicyBase::SetAlternateDesktop(bool alternate_winstation) {
171   use_alternate_desktop_ = true;
172   use_alternate_winstation_ = alternate_winstation;
173   return CreateAlternateDesktop(alternate_winstation);
174 }
175
176 string16 PolicyBase::GetAlternateDesktop() const {
177   // No alternate desktop or winstation. Return an empty string.
178   if (!use_alternate_desktop_ && !use_alternate_winstation_) {
179     return string16();
180   }
181
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_) {
186     return string16();
187   }
188   if (use_alternate_winstation_ && (!alternate_desktop_handle_ ||
189                                     !alternate_winstation_handle_)) {
190     return string16();
191   }
192
193   return GetFullDesktopName(alternate_winstation_handle_,
194                             alternate_desktop_handle_);
195 }
196
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;
202
203     // Check if it's already created.
204     if (alternate_winstation_handle_ && alternate_desktop_handle_)
205       return SBOX_ALL_OK;
206
207     DCHECK(!alternate_winstation_handle_);
208     // Create the window station.
209     ResultCode result = CreateAltWindowStation(&alternate_winstation_handle_);
210     if (SBOX_ALL_OK != result)
211       return result;
212
213     // Verify that everything is fine.
214     if (!alternate_winstation_handle_ ||
215         GetWindowObjectName(alternate_winstation_handle_).empty())
216       return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
217
218     // Create the destkop.
219     result = CreateAltDesktop(alternate_winstation_handle_,
220                               &alternate_desktop_handle_);
221     if (SBOX_ALL_OK != result)
222       return result;
223
224     // Verify that everything is fine.
225     if (!alternate_desktop_handle_ ||
226         GetWindowObjectName(alternate_desktop_handle_).empty())
227       return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
228   } else {
229     // Previously called with alternate_winstation = true?
230     if (alternate_winstation_handle_)
231       return SBOX_ERROR_UNSUPPORTED;
232
233     // Check if it already exists.
234     if (alternate_desktop_handle_)
235       return SBOX_ALL_OK;
236
237     // Create the destkop.
238     ResultCode result = CreateAltDesktop(NULL, &alternate_desktop_handle_);
239     if (SBOX_ALL_OK != result)
240       return result;
241
242     // Verify that everything is fine.
243     if (!alternate_desktop_handle_ ||
244         GetWindowObjectName(alternate_desktop_handle_).empty())
245       return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
246   }
247
248   return SBOX_ALL_OK;
249 }
250
251 void PolicyBase::DestroyAlternateDesktop() {
252   if (alternate_desktop_handle_) {
253     ::CloseDesktop(alternate_desktop_handle_);
254     alternate_desktop_handle_ = NULL;
255   }
256
257   if (alternate_winstation_handle_) {
258     ::CloseWindowStation(alternate_winstation_handle_);
259     alternate_winstation_handle_ = NULL;
260   }
261 }
262
263 ResultCode PolicyBase::SetIntegrityLevel(IntegrityLevel integrity_level) {
264   integrity_level_ = integrity_level;
265   return SBOX_ALL_OK;
266 }
267
268 ResultCode PolicyBase::SetDelayedIntegrityLevel(
269     IntegrityLevel integrity_level) {
270   delayed_integrity_level_ = integrity_level;
271   return SBOX_ALL_OK;
272 }
273
274 ResultCode PolicyBase::SetAppContainer(const wchar_t* sid) {
275   if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
276     return SBOX_ALL_OK;
277
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;
284
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)
289     return rv;
290
291   return SBOX_ALL_OK;
292 }
293
294 ResultCode PolicyBase::SetCapability(const wchar_t* sid) {
295   capabilities_.push_back(sid);
296   return SBOX_ALL_OK;
297 }
298
299 ResultCode PolicyBase::SetProcessMitigations(
300     MitigationFlags flags) {
301   if (!CanSetProcessMitigationsPreStartup(flags))
302     return SBOX_ERROR_BAD_PARAMS;
303   mitigations_ = flags;
304   return SBOX_ALL_OK;
305 }
306
307 MitigationFlags PolicyBase::GetProcessMitigations() {
308   return mitigations_;
309 }
310
311 ResultCode PolicyBase::SetDelayedProcessMitigations(
312     MitigationFlags flags) {
313   if (!CanSetProcessMitigationsPostStartup(flags))
314     return SBOX_ERROR_BAD_PARAMS;
315   delayed_mitigations_ = flags;
316   return SBOX_ALL_OK;
317 }
318
319 MitigationFlags PolicyBase::GetDelayedProcessMitigations() {
320   return delayed_mitigations_;
321 }
322
323 void PolicyBase::SetStrictInterceptions() {
324   relaxed_interceptions_ = false;
325 }
326
327 ResultCode PolicyBase::SetStdoutHandle(HANDLE handle) {
328   if (!IsInheritableHandle(handle))
329     return SBOX_ERROR_BAD_PARAMS;
330   stdout_handle_ = handle;
331   return SBOX_ALL_OK;
332 }
333
334 ResultCode PolicyBase::SetStderrHandle(HANDLE handle) {
335   if (!IsInheritableHandle(handle))
336     return SBOX_ERROR_BAD_PARAMS;
337   stderr_handle_ = handle;
338   return SBOX_ALL_OK;
339 }
340
341 ResultCode PolicyBase::AddRule(SubSystem subsystem, Semantics semantics,
342                                const wchar_t* pattern) {
343   if (NULL == policy_) {
344     policy_ = MakeBrokerPolicyMemory();
345     DCHECK(policy_);
346     policy_maker_ = new LowLevelPolicy(policy_);
347     DCHECK(policy_maker_);
348   }
349
350   switch (subsystem) {
351     case SUBSYS_FILES: {
352       if (!file_system_init_) {
353         if (!FileSystemPolicy::SetInitialRules(policy_maker_))
354           return SBOX_ERROR_BAD_PARAMS;
355         file_system_init_ = true;
356       }
357       if (!FileSystemPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
358         NOTREACHED();
359         return SBOX_ERROR_BAD_PARAMS;
360       }
361       break;
362     }
363     case SUBSYS_SYNC: {
364       if (!SyncPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
365         NOTREACHED();
366         return SBOX_ERROR_BAD_PARAMS;
367       }
368       break;
369     }
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;
376       }
377       if (!ProcessPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
378         NOTREACHED();
379         return SBOX_ERROR_BAD_PARAMS;
380       }
381       break;
382     }
383     case SUBSYS_NAMED_PIPES: {
384       if (!NamedPipePolicy::GenerateRules(pattern, semantics, policy_maker_)) {
385         NOTREACHED();
386         return SBOX_ERROR_BAD_PARAMS;
387       }
388       break;
389     }
390     case SUBSYS_REGISTRY: {
391       if (!RegistryPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
392         NOTREACHED();
393         return SBOX_ERROR_BAD_PARAMS;
394       }
395       break;
396     }
397     case SUBSYS_HANDLES: {
398       if (!HandlePolicy::GenerateRules(pattern, semantics, policy_maker_)) {
399         NOTREACHED();
400         return SBOX_ERROR_BAD_PARAMS;
401       }
402       break;
403     }
404     default: {
405       return SBOX_ERROR_UNSUPPORTED;
406     }
407   }
408
409   return SBOX_ALL_OK;
410 }
411
412 ResultCode PolicyBase::AddDllToUnload(const wchar_t* dll_name) {
413   blacklisted_dlls_.push_back(dll_name);
414   return SBOX_ALL_OK;
415 }
416
417 ResultCode PolicyBase::AddKernelObjectToClose(const char16* handle_type,
418                                               const char16* handle_name) {
419   return handle_closer_.AddHandle(handle_type, handle_name);
420 }
421
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) {
427   DCHECK(callback);
428   static const IPCParams ping1 = {IPC_PING1_TAG, ULONG_TYPE};
429   static const IPCParams ping2 = {IPC_PING2_TAG, INOUTPTR_TYPE};
430
431   if (ping1.Matches(ipc) || ping2.Matches(ipc)) {
432     *callback = reinterpret_cast<CallbackGeneric>(
433                     static_cast<Callback1>(&PolicyBase::Ping));
434     return this;
435   }
436
437   Dispatcher* dispatch = GetDispatcher(ipc->ipc_tag);
438   if (!dispatch) {
439     NOTREACHED();
440     return NULL;
441   }
442   return dispatch->OnMessageReady(ipc, callback);
443 }
444
445 // Delegate to the appropriate dispatcher.
446 bool PolicyBase::SetupService(InterceptionManager* manager, int service) {
447   if (IPC_PING1_TAG == service || IPC_PING2_TAG == service)
448     return true;
449
450   Dispatcher* dispatch = GetDispatcher(service);
451   if (!dispatch) {
452     NOTREACHED();
453     return false;
454   }
455   return dispatch->SetupService(manager, service);
456 }
457
458 ResultCode PolicyBase::MakeJobObject(HANDLE* job) {
459   if (job_level_ != JOB_NONE) {
460     // Create the windows job object.
461     Job job_obj;
462     DWORD result = job_obj.Init(job_level_, NULL, ui_exceptions_);
463     if (ERROR_SUCCESS != result) {
464       return SBOX_ERROR_GENERIC;
465     }
466     *job = job_obj.Detach();
467   } else {
468     *job = NULL;
469   }
470   return SBOX_ALL_OK;
471 }
472
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;
480   }
481
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;
487
488     *initial = INVALID_HANDLE_VALUE;
489     return SBOX_ALL_OK;
490   }
491
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;
500   }
501   return SBOX_ALL_OK;
502 }
503
504 const AppContainerAttributes* PolicyBase::GetAppContainer() {
505   if (!appcontainer_list_.get() || !appcontainer_list_->HasAppContainer())
506     return NULL;
507
508   return appcontainer_list_.get();
509 }
510
511 bool PolicyBase::AddTarget(TargetProcess* target) {
512   if (NULL != policy_)
513     policy_maker_->Done();
514
515   if (!ApplyProcessMitigationsToSuspendedProcess(target->Process(),
516                                                  mitigations_)) {
517     return false;
518   }
519
520   if (!SetupAllInterceptions(target))
521     return false;
522
523   if (!SetupHandleCloser(target))
524     return false;
525
526   // Initialize the sandbox infrastructure for the target.
527   if (ERROR_SUCCESS != target->Init(this, policy_, kIPCMemSize, kPolMemSize))
528     return false;
529
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)
537     return false;
538
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))
543     return false;
544
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)
550     return false;
551
552   AutoLock lock(&lock_);
553   targets_.push_back(target);
554   return true;
555 }
556
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)
562       break;
563   }
564   if (it == targets_.end()) {
565     return false;
566   }
567   TargetProcess* target = *it;
568   targets_.erase(it);
569   delete target;
570   return true;
571 }
572
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
578       // deal.
579       return DENY_ACCESS;
580     }
581     for (int i = 0; i < params->count; i++) {
582       if (!params->parameters[i].IsValid()) {
583         NOTREACHED();
584         return SIGNAL_ALARM;
585       }
586     }
587     PolicyProcessor pol_evaluator(policy_->entry[service]);
588     PolicyResult result =  pol_evaluator.Evaluate(kShortEval,
589                                                   params->parameters,
590                                                   params->count);
591     if (POLICY_MATCH == result) {
592       return pol_evaluator.GetAction();
593     }
594     DCHECK(POLICY_ERROR != result);
595   }
596
597   return DENY_ACCESS;
598 }
599
600 HANDLE PolicyBase::GetStdoutHandle() {
601   return stdout_handle_;
602 }
603
604 HANDLE PolicyBase::GetStderrHandle() {
605   return stderr_handle_;
606 }
607
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;
619       return true;
620     }
621     case IPC_PING2_TAG: {
622       CountedBuffer* io_buffer = reinterpret_cast<CountedBuffer*>(arg1);
623       if (sizeof(uint32) != io_buffer->Size())
624         return false;
625
626       uint32* cookie = reinterpret_cast<uint32*>(io_buffer->Buffer());
627       *cookie = (*cookie) * 3;
628       return true;
629     }
630     default: return false;
631   }
632 }
633
634 Dispatcher* PolicyBase::GetDispatcher(int ipc_tag) {
635   if (ipc_tag >= IPC_LAST_TAG || ipc_tag <= IPC_UNUSED_TAG)
636     return NULL;
637
638   return ipc_targets_[ipc_tag];
639 }
640
641 bool PolicyBase::SetupAllInterceptions(TargetProcess* target) {
642   InterceptionManager manager(target, relaxed_interceptions_);
643
644   if (policy_) {
645     for (int i = 0; i < IPC_LAST_TAG; i++) {
646       if (policy_->entry[i] && !ipc_targets_[i]->SetupService(&manager, i))
647           return false;
648     }
649   }
650
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());
655     }
656   }
657
658   if (!handle_closer_.SetupHandleInterceptions(&manager))
659     return false;
660
661   if (!SetupBasicInterceptions(&manager))
662     return false;
663
664   if (!manager.InitializeInterceptions())
665     return false;
666
667   // Finally, setup imports on the target so the interceptions can work.
668   return SetupNtdllImports(target);
669 }
670
671 bool PolicyBase::SetupHandleCloser(TargetProcess* target) {
672   return handle_closer_.InitializeTargetHandles(target);
673 }
674
675 }  // namespace sandbox