| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "mojo/shell/shell.h" | 5 #include "mojo/shell/shell.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 | 8 |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 if (source) { | 118 if (source) { |
| 119 interfaces = GetAllowedInterfaces(source->filter_, identity_); | 119 interfaces = GetAllowedInterfaces(source->filter_, identity_); |
| 120 source_id = source->id(); | 120 source_id = source->id(); |
| 121 } | 121 } |
| 122 shell_client_->AcceptConnection( | 122 shell_client_->AcceptConnection( |
| 123 mojom::Identity::From(params->source()), source_id, | 123 mojom::Identity::From(params->source()), source_id, |
| 124 params->TakeRemoteInterfaces(), params->TakeLocalInterfaces(), | 124 params->TakeRemoteInterfaces(), params->TakeLocalInterfaces(), |
| 125 Array<String>::From(interfaces), params->target().name()); | 125 Array<String>::From(interfaces), params->target().name()); |
| 126 } | 126 } |
| 127 | 127 |
| 128 scoped_ptr<NativeRunner> StartWithFileURL(const GURL& file_url, | 128 void StartWithClientProcessConnection( |
| 129 mojom::ShellClientRequest request, | 129 mojom::ShellClientRequest request, |
| 130 bool start_sandboxed, | 130 mojom::ClientProcessConnectionPtr client_process_connection) { |
| 131 NativeRunnerFactory* factory) { | 131 factory_.Bind(mojom::ShellClientFactoryPtrInfo( |
| 132 base::FilePath path = util::UrlToFilePath(file_url); | 132 std::move(client_process_connection->shell_client_factory), 0u)); |
| 133 scoped_ptr<NativeRunner> runner = factory->Create(path); | 133 pid_receiver_binding_.Bind( |
| 134 runner_ = runner.get(); | 134 std::move(client_process_connection->pid_receiver_request)); |
| 135 runner_->Start(path, identity_, start_sandboxed, std::move(request), | 135 factory_->CreateShellClient(std::move(request), identity_.name()); |
| 136 base::Bind(&Instance::PIDAvailable, | 136 } |
| 137 weak_factory_.GetWeakPtr()), | 137 |
| 138 base::Bind(&mojo::shell::Shell::CleanupRunner, | 138 void StartWithFilePath(mojom::ShellClientRequest request, |
| 139 shell_->weak_ptr_factory_.GetWeakPtr(), | 139 const base::FilePath& path) { |
| 140 runner_)); | 140 scoped_ptr<NativeRunner> runner = |
| 141 return runner; | 141 shell_->native_runner_factory_->Create(path); |
| 142 bool start_sandboxed = false; |
| 143 runner->Start(path, identity_, start_sandboxed, std::move(request), |
| 144 base::Bind(&Instance::PIDAvailable, |
| 145 weak_factory_.GetWeakPtr()), |
| 146 base::Bind(&mojo::shell::Shell::CleanupRunner, |
| 147 shell_->weak_ptr_factory_.GetWeakPtr(), |
| 148 runner.get())); |
| 149 shell_->native_runners_.push_back(std::move(runner)); |
| 142 } | 150 } |
| 143 | 151 |
| 144 mojom::InstanceInfoPtr CreateInstanceInfo() const { | 152 mojom::InstanceInfoPtr CreateInstanceInfo() const { |
| 145 mojom::InstanceInfoPtr info(mojom::InstanceInfo::New()); | 153 mojom::InstanceInfoPtr info(mojom::InstanceInfo::New()); |
| 146 info->id = id_; | 154 info->id = id_; |
| 147 info->identity = mojom::Identity::From(identity_); | 155 info->identity = mojom::Identity::From(identity_); |
| 148 info->pid = pid_; | 156 info->pid = pid_; |
| 149 return info; | 157 return info; |
| 150 } | 158 } |
| 151 | 159 |
| 152 const Identity& identity() const { return identity_; } | 160 const Identity& identity() const { return identity_; } |
| 153 uint32_t id() const { return id_; } | 161 uint32_t id() const { return id_; } |
| 154 | 162 |
| 155 // ShellClient: | 163 // ShellClient: |
| 156 bool AcceptConnection(Connection* connection) override { | 164 bool AcceptConnection(Connection* connection) override { |
| 157 connection->AddInterface<mojom::Shell>(this); | 165 connection->AddInterface<mojom::Shell>(this); |
| 158 return true; | 166 return true; |
| 159 } | 167 } |
| 160 | 168 |
| 161 private: | 169 private: |
| 162 // mojom::Connector implementation: | 170 // mojom::Connector implementation: |
| 163 void Connect(mojom::IdentityPtr target, | 171 void Connect(mojom::IdentityPtr target_ptr, |
| 164 mojom::InterfaceProviderRequest remote_interfaces, | 172 mojom::InterfaceProviderRequest remote_interfaces, |
| 165 mojom::InterfaceProviderPtr local_interfaces, | 173 mojom::InterfaceProviderPtr local_interfaces, |
| 174 mojom::ClientProcessConnectionPtr client_process_connection, |
| 166 const ConnectCallback& callback) override { | 175 const ConnectCallback& callback) override { |
| 167 if (!IsValidName(target->name)) { | 176 Identity target = target_ptr.To<Identity>(); |
| 168 LOG(ERROR) << "Error: invalid Name: " << target->name; | 177 if (!ValidateIdentity(target, callback)) |
| 169 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, | 178 return; |
| 170 mojom::kInheritUserID, mojom::kInvalidInstanceID); | 179 if (!ValidateClientProcessConnection(&client_process_connection, target, |
| 180 callback)) { |
| 171 return; | 181 return; |
| 172 } | 182 } |
| 173 if (!base::IsValidGUID(target->user_id)) { | 183 // TODO(beng): Need to do the following additional policy validation of |
| 174 LOG(ERROR) << "Error: invalid user_id: " << target->user_id; | 184 // whether this instance is allowed to connect using: |
| 175 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, | 185 // - a user id other than its own, kInheritUserID or kRootUserID. |
| 176 mojom::kInheritUserID, mojom::kInvalidInstanceID); | 186 // - a non-empty instance name. |
| 187 // - a non-null client_process_connection. |
| 188 if (!ValidateCapabilityFilter(target, callback)) |
| 177 return; | 189 return; |
| 178 } | 190 |
| 179 // TODO(beng): perform checking on policy of whether this instance is | 191 scoped_ptr<ConnectParams> params(new ConnectParams); |
| 180 // allowed to pass different user_ids. | 192 params->set_source(identity_); |
| 181 // TODO(beng): perform checking on policy of whether this instance is | 193 params->set_target(target); |
| 182 // allowed to pass non-empty instance identifiers. | 194 params->set_remote_interfaces(std::move(remote_interfaces)); |
| 183 if (allow_any_application_ || filter_.find(target->name) != filter_.end()) { | 195 params->set_local_interfaces(std::move(local_interfaces)); |
| 184 scoped_ptr<ConnectParams> params(new ConnectParams); | 196 params->set_client_process_connection(std::move(client_process_connection)); |
| 185 params->set_source(identity_); | 197 params->set_connect_callback(callback); |
| 186 params->set_target(target.To<Identity>()); | 198 shell_->Connect(std::move(params)); |
| 187 params->set_remote_interfaces(std::move(remote_interfaces)); | |
| 188 params->set_local_interfaces(std::move(local_interfaces)); | |
| 189 params->set_connect_callback(callback); | |
| 190 shell_->Connect(std::move(params)); | |
| 191 } else { | |
| 192 LOG(WARNING) << "CapabilityFilter prevented connection from: " << | |
| 193 identity_.name() << " to: " << target->name; | |
| 194 callback.Run(mojom::ConnectResult::ACCESS_DENIED, | |
| 195 mojom::kInheritUserID, mojom::kInvalidInstanceID); | |
| 196 } | |
| 197 } | 199 } |
| 198 void Clone(mojom::ConnectorRequest request) override { | 200 void Clone(mojom::ConnectorRequest request) override { |
| 199 connectors_.AddBinding(this, std::move(request)); | 201 connectors_.AddBinding(this, std::move(request)); |
| 200 } | 202 } |
| 201 | 203 |
| 202 // mojom::PIDReceiver: | 204 // mojom::PIDReceiver: |
| 203 void SetPID(uint32_t pid) override { | 205 void SetPID(uint32_t pid) override { |
| 204 PIDAvailable(pid); | 206 PIDAvailable(pid); |
| 205 } | 207 } |
| 206 | 208 |
| 207 // InterfaceFactory<mojom::Shell>: | 209 // InterfaceFactory<mojom::Shell>: |
| 208 void Create(Connection* connection, | 210 void Create(Connection* connection, |
| 209 mojom::ShellRequest request) override { | 211 mojom::ShellRequest request) override { |
| 210 shell_bindings_.AddBinding(this, std::move(request)); | 212 shell_bindings_.AddBinding(this, std::move(request)); |
| 211 } | 213 } |
| 212 | 214 |
| 213 // mojom::Shell implementation: | 215 // mojom::Shell implementation: |
| 214 void CreateInstance(mojom::ShellClientFactoryPtr factory, | |
| 215 mojom::IdentityPtr target, | |
| 216 mojom::PIDReceiverRequest pid_receiver, | |
| 217 const CreateInstanceCallback& callback) override { | |
| 218 // We need to bounce through the package manager to load the | |
| 219 // CapabilityFilter. | |
| 220 std::string name = target->name; | |
| 221 shell_->shell_resolver_->ResolveMojoName(name, base::Bind( | |
| 222 &mojo::shell::Shell::Instance::OnResolvedNameForCreateInstance, | |
| 223 weak_factory_.GetWeakPtr(), base::Passed(std::move(factory)), | |
| 224 base::Passed(std::move(target)), base::Passed(std::move(pid_receiver)), | |
| 225 callback)); | |
| 226 } | |
| 227 void AddInstanceListener(mojom::InstanceListenerPtr listener) override { | 216 void AddInstanceListener(mojom::InstanceListenerPtr listener) override { |
| 228 // TODO(beng): this should only track the instances matching this user, and | 217 // TODO(beng): this should only track the instances matching this user, and |
| 229 // root. | 218 // root. |
| 230 shell_->AddInstanceListener(std::move(listener)); | 219 shell_->AddInstanceListener(std::move(listener)); |
| 231 } | 220 } |
| 232 | 221 |
| 233 void OnResolvedNameForCreateInstance(mojom::ShellClientFactoryPtr factory, | 222 bool ValidateIdentity(const Identity& identity, |
| 234 mojom::IdentityPtr target, | 223 const ConnectCallback& callback) { |
| 235 mojom::PIDReceiverRequest pid_receiver, | 224 if (!IsValidName(identity.name())) { |
| 236 const CreateInstanceCallback& callback, | 225 LOG(ERROR) << "Error: invalid Name: " << identity.name(); |
| 237 const String& resolved_name, | 226 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, |
| 238 const String& resolved_instance, | 227 mojom::kInheritUserID, mojom::kInvalidInstanceID); |
| 239 mojom::CapabilityFilterPtr filter, | 228 return false; |
| 240 const String& file_url) { | 229 } |
| 241 if (!base::IsValidGUID(target->user_id)) | 230 if (!base::IsValidGUID(identity.user_id())) { |
| 242 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT); | 231 LOG(ERROR) << "Error: invalid user_id: " << identity.user_id(); |
| 232 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, |
| 233 mojom::kInheritUserID, mojom::kInvalidInstanceID); |
| 234 return false; |
| 235 } |
| 236 return true; |
| 237 } |
| 243 | 238 |
| 244 Identity target_id = target.To<Identity>(); | 239 bool ValidateClientProcessConnection( |
| 240 mojom::ClientProcessConnectionPtr* client_process_connection, |
| 241 const Identity& identity, |
| 242 const ConnectCallback& callback) { |
| 243 if (!client_process_connection->is_null()) { |
| 244 if (!(*client_process_connection)->shell_client_factory.is_valid() || |
| 245 !(*client_process_connection)->pid_receiver_request.is_valid()) { |
| 246 LOG(ERROR) << "Error: must supply both shell_client_factory AND " |
| 247 << "pid_receiver_request when sending " |
| 248 << "client_process_connection."; |
| 249 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, |
| 250 mojom::kInheritUserID, mojom::kInvalidInstanceID); |
| 251 return false; |
| 252 } |
| 253 if (shell_->GetExistingOrRootInstance(identity)) { |
| 254 LOG(ERROR) << "Error: Cannot client process matching existing identity:" |
| 255 << "Name: " << identity.name() << " User: " |
| 256 << identity.user_id() << " Instance: " |
| 257 << identity.instance(); |
| 258 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, |
| 259 mojom::kInheritUserID, mojom::kInvalidInstanceID); |
| 260 return false; |
| 261 } |
| 262 } |
| 263 return true; |
| 264 } |
| 245 | 265 |
| 246 // TODO(beng): perform checking on policy of whether this instance is | 266 bool ValidateCapabilityFilter(const Identity& target, |
| 247 // allowed to pass different user_ids. | 267 const ConnectCallback& callback) { |
| 248 if (target_id.user_id() == mojom::kInheritUserID) | 268 if (allow_any_application_ || |
| 249 target_id.set_user_id(identity_.user_id()); | 269 filter_.find(target.name()) != filter_.end()) { |
| 250 | 270 return true; |
| 251 mojom::ShellClientRequest request; | 271 } |
| 252 Instance* instance = shell_->CreateInstance( | 272 LOG(ERROR) << "CapabilityFilter prevented connection from: " << |
| 253 target_id, filter->filter.To<CapabilityFilter>(), &request); | 273 identity_.name() << " to: " << target.name(); |
| 254 instance->pid_receiver_binding_.Bind(std::move(pid_receiver)); | 274 callback.Run(mojom::ConnectResult::ACCESS_DENIED, |
| 255 instance->factory_ = std::move(factory); | 275 mojom::kInheritUserID, mojom::kInvalidInstanceID); |
| 256 instance->factory_->CreateShellClient(std::move(request), target->name); | 276 return false; |
| 257 callback.Run(mojom::ConnectResult::SUCCEEDED); | |
| 258 // We don't call ConnectToClient() here since the instance was created | |
| 259 // manually by other code, not in response to a Connect() request. The newly | |
| 260 // created instance is identified by |name| and may be subsequently reached | |
| 261 // by client code using this identity. | |
| 262 } | 277 } |
| 263 | 278 |
| 264 uint32_t GenerateUniqueID() const { | 279 uint32_t GenerateUniqueID() const { |
| 265 static uint32_t id = mojom::kInvalidInstanceID; | 280 static uint32_t id = mojom::kInvalidInstanceID; |
| 266 ++id; | 281 ++id; |
| 267 CHECK_NE(mojom::kInvalidInstanceID, id); | 282 CHECK_NE(mojom::kInvalidInstanceID, id); |
| 268 return id; | 283 return id; |
| 269 } | 284 } |
| 270 | 285 |
| 271 void PIDAvailable(base::ProcessId pid) { | 286 void PIDAvailable(base::ProcessId pid) { |
| (...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 449 }); | 464 }); |
| 450 if (!instance_quit_callback_.is_null()) | 465 if (!instance_quit_callback_.is_null()) |
| 451 instance_quit_callback_.Run(identity); | 466 instance_quit_callback_.Run(identity); |
| 452 } | 467 } |
| 453 | 468 |
| 454 Shell::Instance* Shell::GetExistingInstance(const Identity& identity) const { | 469 Shell::Instance* Shell::GetExistingInstance(const Identity& identity) const { |
| 455 const auto& it = identity_to_instance_.find(identity); | 470 const auto& it = identity_to_instance_.find(identity); |
| 456 return it != identity_to_instance_.end() ? it->second : nullptr; | 471 return it != identity_to_instance_.end() ? it->second : nullptr; |
| 457 } | 472 } |
| 458 | 473 |
| 474 Shell::Instance* Shell::GetExistingOrRootInstance( |
| 475 const Identity& identity) const { |
| 476 Instance* instance = GetExistingInstance(identity); |
| 477 if (!instance) { |
| 478 Identity root_identity = identity; |
| 479 root_identity.set_user_id(mojom::kRootUserID); |
| 480 instance = GetExistingInstance(root_identity); |
| 481 } |
| 482 return instance; |
| 483 } |
| 484 |
| 459 void Shell::NotifyPIDAvailable(uint32_t id, base::ProcessId pid) { | 485 void Shell::NotifyPIDAvailable(uint32_t id, base::ProcessId pid) { |
| 460 instance_listeners_.ForAllPtrs([id, pid](mojom::InstanceListener* listener) { | 486 instance_listeners_.ForAllPtrs([id, pid](mojom::InstanceListener* listener) { |
| 461 listener->InstancePIDAvailable(id, pid); | 487 listener->InstancePIDAvailable(id, pid); |
| 462 }); | 488 }); |
| 463 } | 489 } |
| 464 | 490 |
| 465 bool Shell::ConnectToExistingInstance(scoped_ptr<ConnectParams>* params) { | 491 bool Shell::ConnectToExistingInstance(scoped_ptr<ConnectParams>* params) { |
| 466 Instance* instance = GetExistingInstance((*params)->target()); | 492 Instance* instance = GetExistingOrRootInstance((*params)->target()); |
| 467 if (!instance) { | 493 if (instance) |
| 468 Identity root_identity = (*params)->target(); | 494 instance->ConnectToClient(std::move(*params)); |
| 469 root_identity.set_user_id(mojom::kRootUserID); | 495 return !!instance; |
| 470 instance = GetExistingInstance(root_identity); | |
| 471 if (!instance) return false; | |
| 472 } | |
| 473 instance->ConnectToClient(std::move(*params)); | |
| 474 return true; | |
| 475 } | 496 } |
| 476 | 497 |
| 477 Shell::Instance* Shell::CreateInstance(const Identity& target_id, | 498 Shell::Instance* Shell::CreateInstance(const Identity& target_id, |
| 478 const CapabilityFilter& filter, | 499 const CapabilityFilter& filter, |
| 479 mojom::ShellClientRequest* request) { | 500 mojom::ShellClientRequest* request) { |
| 480 CHECK(target_id.user_id() != mojom::kInheritUserID); | 501 CHECK(target_id.user_id() != mojom::kInheritUserID); |
| 481 mojom::ShellClientPtr shell_client; | 502 mojom::ShellClientPtr shell_client; |
| 482 *request = GetProxy(&shell_client); | 503 *request = GetProxy(&shell_client); |
| 483 Instance* instance = | 504 Instance* instance = |
| 484 new Instance(std::move(shell_client), this, target_id, filter); | 505 new Instance(std::move(shell_client), this, target_id, filter); |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 560 if (ConnectToExistingInstance(¶ms)) | 581 if (ConnectToExistingInstance(¶ms)) |
| 561 return; | 582 return; |
| 562 | 583 |
| 563 Identity source = params->source(); | 584 Identity source = params->source(); |
| 564 // |base_filter| can be null when there is no manifest, e.g. for URL types | 585 // |base_filter| can be null when there is no manifest, e.g. for URL types |
| 565 // not resolvable by the resolver. | 586 // not resolvable by the resolver. |
| 566 CapabilityFilter filter = GetPermissiveCapabilityFilter(); | 587 CapabilityFilter filter = GetPermissiveCapabilityFilter(); |
| 567 if (!base_filter.is_null()) | 588 if (!base_filter.is_null()) |
| 568 filter = base_filter->filter.To<CapabilityFilter>(); | 589 filter = base_filter->filter.To<CapabilityFilter>(); |
| 569 | 590 |
| 591 mojom::ClientProcessConnectionPtr client_process_connection = |
| 592 params->TakeClientProcessConnection(); |
| 570 mojom::ShellClientRequest request; | 593 mojom::ShellClientRequest request; |
| 571 Instance* instance = CreateInstance(target, filter, &request); | 594 Instance* instance = CreateInstance(target, filter, &request); |
| 572 instance->ConnectToClient(std::move(params)); | 595 instance->ConnectToClient(std::move(params)); |
| 573 | 596 |
| 574 if (LoadWithLoader(target, &request)) | 597 if (LoadWithLoader(target, &request)) |
| 575 return; | 598 return; |
| 576 | 599 |
| 577 CHECK(!file_url.is_null() && !base_filter.is_null()); | 600 CHECK(!file_url.is_null() && !base_filter.is_null()); |
| 578 | 601 |
| 579 if (target.name() != resolved_name) { | 602 if (target.name() != resolved_name) { |
| 580 // In cases where a package alias is resolved, we have to use the instance | 603 // In cases where a package alias is resolved, we have to use the instance |
| 581 // from the original request rather than for the package itself, which will | 604 // from the original request rather than for the package itself, which will |
| 582 // always be the same. | 605 // always be the same. |
| 583 CreateShellClient( | 606 CreateShellClient( |
| 584 source, Identity(resolved_name, target.user_id(), instance_name), | 607 source, Identity(resolved_name, target.user_id(), instance_name), |
| 585 target.name(), std::move(request)); | 608 target.name(), std::move(request)); |
| 586 } else { | 609 } else { |
| 587 bool start_sandboxed = false; | 610 if (!client_process_connection.is_null()) { |
| 588 native_runners_.push_back( | 611 // The client already started a process for this instance, use it. |
| 589 instance->StartWithFileURL(file_url.To<GURL>(), std::move(request), | 612 instance->StartWithClientProcessConnection( |
| 590 start_sandboxed, | 613 std::move(request), std::move(client_process_connection)); |
| 591 native_runner_factory_.get())); | 614 } else { |
| 615 // Otherwise we make our own process. |
| 616 instance->StartWithFilePath(std::move(request), |
| 617 util::UrlToFilePath(file_url.To<GURL>())); |
| 618 } |
| 592 } | 619 } |
| 593 } | 620 } |
| 594 | 621 |
| 595 bool Shell::LoadWithLoader(const Identity& target, | 622 bool Shell::LoadWithLoader(const Identity& target, |
| 596 mojom::ShellClientRequest* request) { | 623 mojom::ShellClientRequest* request) { |
| 597 Loader* loader = GetLoaderForName(target.name()); | 624 Loader* loader = GetLoaderForName(target.name()); |
| 598 if (!loader) | 625 if (!loader) |
| 599 return false; | 626 return false; |
| 600 loader->Load(target.name(), std::move(*request)); | 627 loader->Load(target.name(), std::move(*request)); |
| 601 return true; | 628 return true; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 612 for (auto it = native_runners_.begin(); it != native_runners_.end(); ++it) { | 639 for (auto it = native_runners_.begin(); it != native_runners_.end(); ++it) { |
| 613 if (it->get() == runner) { | 640 if (it->get() == runner) { |
| 614 native_runners_.erase(it); | 641 native_runners_.erase(it); |
| 615 return; | 642 return; |
| 616 } | 643 } |
| 617 } | 644 } |
| 618 } | 645 } |
| 619 | 646 |
| 620 } // namespace shell | 647 } // namespace shell |
| 621 } // namespace mojo | 648 } // namespace mojo |
| OLD | NEW |