security/sandbox/win/src/broker_services.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/sandbox/win/src/broker_services.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,510 @@
     1.4 +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +#include "sandbox/win/src/broker_services.h"
     1.9 +
    1.10 +#include "base/logging.h"
    1.11 +#include "base/memory/scoped_ptr.h"
    1.12 +#include "base/threading/platform_thread.h"
    1.13 +#include "base/win/scoped_handle.h"
    1.14 +#include "base/win/scoped_process_information.h"
    1.15 +#include "base/win/startup_information.h"
    1.16 +#include "base/win/windows_version.h"
    1.17 +#include "sandbox/win/src/app_container.h"
    1.18 +#include "sandbox/win/src/process_mitigations.h"
    1.19 +#include "sandbox/win/src/sandbox_policy_base.h"
    1.20 +#include "sandbox/win/src/sandbox.h"
    1.21 +#include "sandbox/win/src/target_process.h"
    1.22 +#include "sandbox/win/src/win2k_threadpool.h"
    1.23 +#include "sandbox/win/src/win_utils.h"
    1.24 +
    1.25 +namespace {
    1.26 +
    1.27 +// Utility function to associate a completion port to a job object.
    1.28 +bool AssociateCompletionPort(HANDLE job, HANDLE port, void* key) {
    1.29 +  JOBOBJECT_ASSOCIATE_COMPLETION_PORT job_acp = { key, port };
    1.30 +  return ::SetInformationJobObject(job,
    1.31 +                                   JobObjectAssociateCompletionPortInformation,
    1.32 +                                   &job_acp, sizeof(job_acp))? true : false;
    1.33 +}
    1.34 +
    1.35 +// Utility function to do the cleanup necessary when something goes wrong
    1.36 +// while in SpawnTarget and we must terminate the target process.
    1.37 +sandbox::ResultCode SpawnCleanup(sandbox::TargetProcess* target, DWORD error) {
    1.38 +  if (0 == error)
    1.39 +    error = ::GetLastError();
    1.40 +
    1.41 +  target->Terminate();
    1.42 +  delete target;
    1.43 +  ::SetLastError(error);
    1.44 +  return sandbox::SBOX_ERROR_GENERIC;
    1.45 +}
    1.46 +
    1.47 +// the different commands that you can send to the worker thread that
    1.48 +// executes TargetEventsThread().
    1.49 +enum {
    1.50 +  THREAD_CTRL_NONE,
    1.51 +  THREAD_CTRL_REMOVE_PEER,
    1.52 +  THREAD_CTRL_QUIT,
    1.53 +  THREAD_CTRL_LAST,
    1.54 +};
    1.55 +
    1.56 +// Helper structure that allows the Broker to associate a job notification
    1.57 +// with a job object and with a policy.
    1.58 +struct JobTracker {
    1.59 +  HANDLE job;
    1.60 +  sandbox::PolicyBase* policy;
    1.61 +  JobTracker(HANDLE cjob, sandbox::PolicyBase* cpolicy)
    1.62 +      : job(cjob), policy(cpolicy) {
    1.63 +  }
    1.64 +};
    1.65 +
    1.66 +// Helper structure that allows the broker to track peer processes
    1.67 +struct PeerTracker {
    1.68 +  HANDLE wait_object;
    1.69 +  base::win::ScopedHandle process;
    1.70 +  DWORD id;
    1.71 +  HANDLE job_port;
    1.72 +  PeerTracker(DWORD process_id, HANDLE broker_job_port)
    1.73 +      : wait_object(NULL), id(process_id), job_port(broker_job_port) {
    1.74 +  }
    1.75 +};
    1.76 +
    1.77 +void DeregisterPeerTracker(PeerTracker* peer) {
    1.78 +  // Deregistration shouldn't fail, but we leak rather than crash if it does.
    1.79 +  if (::UnregisterWaitEx(peer->wait_object, INVALID_HANDLE_VALUE)) {
    1.80 +    delete peer;
    1.81 +  } else {
    1.82 +    NOTREACHED();
    1.83 +  }
    1.84 +}
    1.85 +
    1.86 +}  // namespace
    1.87 +
    1.88 +namespace sandbox {
    1.89 +
    1.90 +BrokerServicesBase::BrokerServicesBase()
    1.91 +    : thread_pool_(NULL), job_port_(NULL), no_targets_(NULL),
    1.92 +      job_thread_(NULL) {
    1.93 +}
    1.94 +
    1.95 +// The broker uses a dedicated worker thread that services the job completion
    1.96 +// port to perform policy notifications and associated cleanup tasks.
    1.97 +ResultCode BrokerServicesBase::Init() {
    1.98 +  if ((NULL != job_port_) || (NULL != thread_pool_))
    1.99 +    return SBOX_ERROR_UNEXPECTED_CALL;
   1.100 +
   1.101 +  ::InitializeCriticalSection(&lock_);
   1.102 +
   1.103 +  job_port_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
   1.104 +  if (NULL == job_port_)
   1.105 +    return SBOX_ERROR_GENERIC;
   1.106 +
   1.107 +  no_targets_ = ::CreateEventW(NULL, TRUE, FALSE, NULL);
   1.108 +
   1.109 +  job_thread_ = ::CreateThread(NULL, 0,  // Default security and stack.
   1.110 +                               TargetEventsThread, this, NULL, NULL);
   1.111 +  if (NULL == job_thread_)
   1.112 +    return SBOX_ERROR_GENERIC;
   1.113 +
   1.114 +  return SBOX_ALL_OK;
   1.115 +}
   1.116 +
   1.117 +// The destructor should only be called when the Broker process is terminating.
   1.118 +// Since BrokerServicesBase is a singleton, this is called from the CRT
   1.119 +// termination handlers, if this code lives on a DLL it is called during
   1.120 +// DLL_PROCESS_DETACH in other words, holding the loader lock, so we cannot
   1.121 +// wait for threads here.
   1.122 +BrokerServicesBase::~BrokerServicesBase() {
   1.123 +  // If there is no port Init() was never called successfully.
   1.124 +  if (!job_port_)
   1.125 +    return;
   1.126 +
   1.127 +  // Closing the port causes, that no more Job notifications are delivered to
   1.128 +  // the worker thread and also causes the thread to exit. This is what we
   1.129 +  // want to do since we are going to close all outstanding Jobs and notifying
   1.130 +  // the policy objects ourselves.
   1.131 +  ::PostQueuedCompletionStatus(job_port_, 0, THREAD_CTRL_QUIT, FALSE);
   1.132 +  ::CloseHandle(job_port_);
   1.133 +
   1.134 +  if (WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_, 1000)) {
   1.135 +    // Cannot clean broker services.
   1.136 +    NOTREACHED();
   1.137 +    return;
   1.138 +  }
   1.139 +
   1.140 +  JobTrackerList::iterator it;
   1.141 +  for (it = tracker_list_.begin(); it != tracker_list_.end(); ++it) {
   1.142 +    JobTracker* tracker = (*it);
   1.143 +    FreeResources(tracker);
   1.144 +    delete tracker;
   1.145 +  }
   1.146 +  ::CloseHandle(job_thread_);
   1.147 +  delete thread_pool_;
   1.148 +  ::CloseHandle(no_targets_);
   1.149 +
   1.150 +  // Cancel the wait events and delete remaining peer trackers.
   1.151 +  for (PeerTrackerMap::iterator it = peer_map_.begin();
   1.152 +       it != peer_map_.end(); ++it) {
   1.153 +    DeregisterPeerTracker(it->second);
   1.154 +  }
   1.155 +
   1.156 +  // If job_port_ isn't NULL, assumes that the lock has been initialized.
   1.157 +  if (job_port_)
   1.158 +    ::DeleteCriticalSection(&lock_);
   1.159 +}
   1.160 +
   1.161 +TargetPolicy* BrokerServicesBase::CreatePolicy() {
   1.162 +  // If you change the type of the object being created here you must also
   1.163 +  // change the downcast to it in SpawnTarget().
   1.164 +  return new PolicyBase;
   1.165 +}
   1.166 +
   1.167 +void BrokerServicesBase::FreeResources(JobTracker* tracker) {
   1.168 +  if (NULL != tracker->policy) {
   1.169 +    BOOL res = ::TerminateJobObject(tracker->job, SBOX_ALL_OK);
   1.170 +    DCHECK(res);
   1.171 +    // Closing the job causes the target process to be destroyed so this
   1.172 +    // needs to happen before calling OnJobEmpty().
   1.173 +    res = ::CloseHandle(tracker->job);
   1.174 +    DCHECK(res);
   1.175 +    // In OnJobEmpty() we don't actually use the job handle directly.
   1.176 +    tracker->policy->OnJobEmpty(tracker->job);
   1.177 +    tracker->policy->Release();
   1.178 +    tracker->policy = NULL;
   1.179 +  }
   1.180 +}
   1.181 +
   1.182 +// The worker thread stays in a loop waiting for asynchronous notifications
   1.183 +// from the job objects. Right now we only care about knowing when the last
   1.184 +// process on a job terminates, but in general this is the place to tell
   1.185 +// the policy about events.
   1.186 +DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) {
   1.187 +  if (NULL == param)
   1.188 +    return 1;
   1.189 +
   1.190 +  base::PlatformThread::SetName("BrokerEvent");
   1.191 +
   1.192 +  BrokerServicesBase* broker = reinterpret_cast<BrokerServicesBase*>(param);
   1.193 +  HANDLE port = broker->job_port_;
   1.194 +  HANDLE no_targets = broker->no_targets_;
   1.195 +
   1.196 +  int target_counter = 0;
   1.197 +  ::ResetEvent(no_targets);
   1.198 +
   1.199 +  while (true) {
   1.200 +    DWORD events = 0;
   1.201 +    ULONG_PTR key = 0;
   1.202 +    LPOVERLAPPED ovl = NULL;
   1.203 +
   1.204 +    if (!::GetQueuedCompletionStatus(port, &events, &key, &ovl, INFINITE))
   1.205 +      // this call fails if the port has been closed before we have a
   1.206 +      // chance to service the last packet which is 'exit' anyway so
   1.207 +      // this is not an error.
   1.208 +      return 1;
   1.209 +
   1.210 +    if (key > THREAD_CTRL_LAST) {
   1.211 +      // The notification comes from a job object. There are nine notifications
   1.212 +      // that jobs can send and some of them depend on the job attributes set.
   1.213 +      JobTracker* tracker = reinterpret_cast<JobTracker*>(key);
   1.214 +
   1.215 +      switch (events) {
   1.216 +        case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: {
   1.217 +          // The job object has signaled that the last process associated
   1.218 +          // with it has terminated. Assuming there is no way for a process
   1.219 +          // to appear out of thin air in this job, it safe to assume that
   1.220 +          // we can tell the policy to destroy the target object, and for
   1.221 +          // us to release our reference to the policy object.
   1.222 +          FreeResources(tracker);
   1.223 +          break;
   1.224 +        }
   1.225 +
   1.226 +        case JOB_OBJECT_MSG_NEW_PROCESS: {
   1.227 +          ++target_counter;
   1.228 +          if (1 == target_counter) {
   1.229 +            ::ResetEvent(no_targets);
   1.230 +          }
   1.231 +          break;
   1.232 +        }
   1.233 +
   1.234 +        case JOB_OBJECT_MSG_EXIT_PROCESS:
   1.235 +        case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: {
   1.236 +          {
   1.237 +            AutoLock lock(&broker->lock_);
   1.238 +            broker->child_process_ids_.erase(reinterpret_cast<DWORD>(ovl));
   1.239 +          }
   1.240 +          --target_counter;
   1.241 +          if (0 == target_counter)
   1.242 +            ::SetEvent(no_targets);
   1.243 +
   1.244 +          DCHECK(target_counter >= 0);
   1.245 +          break;
   1.246 +        }
   1.247 +
   1.248 +        case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: {
   1.249 +          break;
   1.250 +        }
   1.251 +
   1.252 +        default: {
   1.253 +          NOTREACHED();
   1.254 +          break;
   1.255 +        }
   1.256 +      }
   1.257 +    } else if (THREAD_CTRL_REMOVE_PEER == key) {
   1.258 +      // Remove a process from our list of peers.
   1.259 +      AutoLock lock(&broker->lock_);
   1.260 +      PeerTrackerMap::iterator it =
   1.261 +          broker->peer_map_.find(reinterpret_cast<DWORD>(ovl));
   1.262 +      DeregisterPeerTracker(it->second);
   1.263 +      broker->peer_map_.erase(it);
   1.264 +    } else if (THREAD_CTRL_QUIT == key) {
   1.265 +      // The broker object is being destroyed so the thread needs to exit.
   1.266 +      return 0;
   1.267 +    } else {
   1.268 +      // We have not implemented more commands.
   1.269 +      NOTREACHED();
   1.270 +    }
   1.271 +  }
   1.272 +
   1.273 +  NOTREACHED();
   1.274 +  return 0;
   1.275 +}
   1.276 +
   1.277 +// SpawnTarget does all the interesting sandbox setup and creates the target
   1.278 +// process inside the sandbox.
   1.279 +ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
   1.280 +                                           const wchar_t* command_line,
   1.281 +                                           TargetPolicy* policy,
   1.282 +                                           PROCESS_INFORMATION* target_info) {
   1.283 +  if (!exe_path)
   1.284 +    return SBOX_ERROR_BAD_PARAMS;
   1.285 +
   1.286 +  if (!policy)
   1.287 +    return SBOX_ERROR_BAD_PARAMS;
   1.288 +
   1.289 +  // Even though the resources touched by SpawnTarget can be accessed in
   1.290 +  // multiple threads, the method itself cannot be called from more than
   1.291 +  // 1 thread. This is to protect the global variables used while setting up
   1.292 +  // the child process.
   1.293 +  static DWORD thread_id = ::GetCurrentThreadId();
   1.294 +  DCHECK(thread_id == ::GetCurrentThreadId());
   1.295 +
   1.296 +  AutoLock lock(&lock_);
   1.297 +
   1.298 +  // This downcast is safe as long as we control CreatePolicy()
   1.299 +  PolicyBase* policy_base = static_cast<PolicyBase*>(policy);
   1.300 +
   1.301 +  // Construct the tokens and the job object that we are going to associate
   1.302 +  // with the soon to be created target process.
   1.303 +  HANDLE initial_token_temp;
   1.304 +  HANDLE lockdown_token_temp;
   1.305 +  ResultCode result = policy_base->MakeTokens(&initial_token_temp,
   1.306 +                                              &lockdown_token_temp);
   1.307 +  if (SBOX_ALL_OK != result)
   1.308 +    return result;
   1.309 +
   1.310 +  base::win::ScopedHandle initial_token(initial_token_temp);
   1.311 +  base::win::ScopedHandle lockdown_token(lockdown_token_temp);
   1.312 +
   1.313 +  HANDLE job_temp;
   1.314 +  result = policy_base->MakeJobObject(&job_temp);
   1.315 +  if (SBOX_ALL_OK != result)
   1.316 +    return result;
   1.317 +
   1.318 +  base::win::ScopedHandle job(job_temp);
   1.319 +
   1.320 +  // Initialize the startup information from the policy.
   1.321 +  base::win::StartupInformation startup_info;
   1.322 +  string16 desktop = policy_base->GetAlternateDesktop();
   1.323 +  if (!desktop.empty()) {
   1.324 +    startup_info.startup_info()->lpDesktop =
   1.325 +        const_cast<wchar_t*>(desktop.c_str());
   1.326 +  }
   1.327 +
   1.328 +  bool inherit_handles = false;
   1.329 +  if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
   1.330 +    int attribute_count = 0;
   1.331 +    const AppContainerAttributes* app_container =
   1.332 +        policy_base->GetAppContainer();
   1.333 +    if (app_container)
   1.334 +      ++attribute_count;
   1.335 +
   1.336 +    DWORD64 mitigations;
   1.337 +    size_t mitigations_size;
   1.338 +    ConvertProcessMitigationsToPolicy(policy->GetProcessMitigations(),
   1.339 +                                      &mitigations, &mitigations_size);
   1.340 +    if (mitigations)
   1.341 +      ++attribute_count;
   1.342 +
   1.343 +    HANDLE stdout_handle = policy_base->GetStdoutHandle();
   1.344 +    HANDLE stderr_handle = policy_base->GetStderrHandle();
   1.345 +    HANDLE inherit_handle_list[2];
   1.346 +    int inherit_handle_count = 0;
   1.347 +    if (stdout_handle != INVALID_HANDLE_VALUE)
   1.348 +      inherit_handle_list[inherit_handle_count++] = stdout_handle;
   1.349 +    // Handles in the list must be unique.
   1.350 +    if (stderr_handle != stdout_handle && stderr_handle != INVALID_HANDLE_VALUE)
   1.351 +      inherit_handle_list[inherit_handle_count++] = stderr_handle;
   1.352 +    if (inherit_handle_count)
   1.353 +      ++attribute_count;
   1.354 +
   1.355 +    if (!startup_info.InitializeProcThreadAttributeList(attribute_count))
   1.356 +      return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
   1.357 +
   1.358 +    if (app_container) {
   1.359 +      result = app_container->ShareForStartup(&startup_info);
   1.360 +      if (SBOX_ALL_OK != result)
   1.361 +        return result;
   1.362 +    }
   1.363 +
   1.364 +    if (mitigations) {
   1.365 +      if (!startup_info.UpdateProcThreadAttribute(
   1.366 +               PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &mitigations,
   1.367 +               mitigations_size)) {
   1.368 +        return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
   1.369 +      }
   1.370 +    }
   1.371 +
   1.372 +    if (inherit_handle_count) {
   1.373 +      if (!startup_info.UpdateProcThreadAttribute(
   1.374 +              PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
   1.375 +              inherit_handle_list,
   1.376 +              sizeof(inherit_handle_list[0]) * inherit_handle_count)) {
   1.377 +        return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
   1.378 +      }
   1.379 +      startup_info.startup_info()->dwFlags |= STARTF_USESTDHANDLES;
   1.380 +      startup_info.startup_info()->hStdInput = INVALID_HANDLE_VALUE;
   1.381 +      startup_info.startup_info()->hStdOutput = stdout_handle;
   1.382 +      startup_info.startup_info()->hStdError = stderr_handle;
   1.383 +      // Allowing inheritance of handles is only secure now that we
   1.384 +      // have limited which handles will be inherited.
   1.385 +      inherit_handles = true;
   1.386 +    }
   1.387 +  }
   1.388 +
   1.389 +  // Construct the thread pool here in case it is expensive.
   1.390 +  // The thread pool is shared by all the targets
   1.391 +  if (NULL == thread_pool_)
   1.392 +    thread_pool_ = new Win2kThreadPool();
   1.393 +
   1.394 +  // Create the TargetProces object and spawn the target suspended. Note that
   1.395 +  // Brokerservices does not own the target object. It is owned by the Policy.
   1.396 +  base::win::ScopedProcessInformation process_info;
   1.397 +  TargetProcess* target = new TargetProcess(initial_token.Take(),
   1.398 +                                            lockdown_token.Take(),
   1.399 +                                            job,
   1.400 +                                            thread_pool_);
   1.401 +
   1.402 +  DWORD win_result = target->Create(exe_path, command_line, inherit_handles,
   1.403 +                                    startup_info, &process_info);
   1.404 +  if (ERROR_SUCCESS != win_result)
   1.405 +    return SpawnCleanup(target, win_result);
   1.406 +
   1.407 +  // Now the policy is the owner of the target.
   1.408 +  if (!policy_base->AddTarget(target)) {
   1.409 +    return SpawnCleanup(target, 0);
   1.410 +  }
   1.411 +
   1.412 +  // We are going to keep a pointer to the policy because we'll call it when
   1.413 +  // the job object generates notifications using the completion port.
   1.414 +  policy_base->AddRef();
   1.415 +  if (job.IsValid()) {
   1.416 +    scoped_ptr<JobTracker> tracker(new JobTracker(job.Take(), policy_base));
   1.417 +    if (!AssociateCompletionPort(tracker->job, job_port_, tracker.get()))
   1.418 +      return SpawnCleanup(target, 0);
   1.419 +    // Save the tracker because in cleanup we might need to force closing
   1.420 +    // the Jobs.
   1.421 +    tracker_list_.push_back(tracker.release());
   1.422 +    child_process_ids_.insert(process_info.process_id());
   1.423 +  } else {
   1.424 +    // We have to signal the event once here because the completion port will
   1.425 +    // never get a message that this target is being terminated thus we should
   1.426 +    // not block WaitForAllTargets until we have at least one target with job.
   1.427 +    if (child_process_ids_.empty())
   1.428 +      ::SetEvent(no_targets_);
   1.429 +    // We can not track the life time of such processes and it is responsibility
   1.430 +    // of the host application to make sure that spawned targets without jobs
   1.431 +    // are terminated when the main application don't need them anymore.
   1.432 +  }
   1.433 +
   1.434 +  *target_info = process_info.Take();
   1.435 +  return SBOX_ALL_OK;
   1.436 +}
   1.437 +
   1.438 +
   1.439 +ResultCode BrokerServicesBase::WaitForAllTargets() {
   1.440 +  ::WaitForSingleObject(no_targets_, INFINITE);
   1.441 +  return SBOX_ALL_OK;
   1.442 +}
   1.443 +
   1.444 +bool BrokerServicesBase::IsActiveTarget(DWORD process_id) {
   1.445 +  AutoLock lock(&lock_);
   1.446 +  return child_process_ids_.find(process_id) != child_process_ids_.end() ||
   1.447 +         peer_map_.find(process_id) != peer_map_.end();
   1.448 +}
   1.449 +
   1.450 +VOID CALLBACK BrokerServicesBase::RemovePeer(PVOID parameter, BOOLEAN timeout) {
   1.451 +  PeerTracker* peer = reinterpret_cast<PeerTracker*>(parameter);
   1.452 +  // Don't check the return code because we this may fail (safely) at shutdown.
   1.453 +  ::PostQueuedCompletionStatus(peer->job_port, 0, THREAD_CTRL_REMOVE_PEER,
   1.454 +                               reinterpret_cast<LPOVERLAPPED>(peer->id));
   1.455 +}
   1.456 +
   1.457 +ResultCode BrokerServicesBase::AddTargetPeer(HANDLE peer_process) {
   1.458 +  scoped_ptr<PeerTracker> peer(new PeerTracker(::GetProcessId(peer_process),
   1.459 +                                               job_port_));
   1.460 +  if (!peer->id)
   1.461 +    return SBOX_ERROR_GENERIC;
   1.462 +
   1.463 +  HANDLE process_handle;
   1.464 +  if (!::DuplicateHandle(::GetCurrentProcess(), peer_process,
   1.465 +                         ::GetCurrentProcess(), &process_handle,
   1.466 +                         SYNCHRONIZE, FALSE, 0)) {
   1.467 +    return SBOX_ERROR_GENERIC;
   1.468 +  }
   1.469 +  peer->process.Set(process_handle);
   1.470 +
   1.471 +  AutoLock lock(&lock_);
   1.472 +  if (!peer_map_.insert(std::make_pair(peer->id, peer.get())).second)
   1.473 +    return SBOX_ERROR_BAD_PARAMS;
   1.474 +
   1.475 +  if (!::RegisterWaitForSingleObject(
   1.476 +          &peer->wait_object, peer->process, RemovePeer, peer.get(), INFINITE,
   1.477 +          WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD)) {
   1.478 +    peer_map_.erase(peer->id);
   1.479 +    return SBOX_ERROR_GENERIC;
   1.480 +  }
   1.481 +
   1.482 +  // Release the pointer since it will be cleaned up by the callback.
   1.483 +  peer.release();
   1.484 +  return SBOX_ALL_OK;
   1.485 +}
   1.486 +
   1.487 +ResultCode BrokerServicesBase::InstallAppContainer(const wchar_t* sid,
   1.488 +                                                   const wchar_t* name) {
   1.489 +  if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
   1.490 +    return SBOX_ERROR_UNSUPPORTED;
   1.491 +
   1.492 +  string16 old_name = LookupAppContainer(sid);
   1.493 +  if (old_name.empty())
   1.494 +    return CreateAppContainer(sid, name);
   1.495 +
   1.496 +  if (old_name != name)
   1.497 +    return SBOX_ERROR_INVALID_APP_CONTAINER;
   1.498 +
   1.499 +  return SBOX_ALL_OK;
   1.500 +}
   1.501 +
   1.502 +ResultCode BrokerServicesBase::UninstallAppContainer(const wchar_t* sid) {
   1.503 +  if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
   1.504 +    return SBOX_ERROR_UNSUPPORTED;
   1.505 +
   1.506 +  string16 name =  LookupAppContainer(sid);
   1.507 +  if (name.empty())
   1.508 +    return SBOX_ERROR_INVALID_APP_CONTAINER;
   1.509 +
   1.510 +  return DeleteAppContainer(sid);
   1.511 +}
   1.512 +
   1.513 +}  // namespace sandbox

mercurial