OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 "base/at_exit.h" |
| 6 #include "base/bind.h" |
| 7 #include "base/macros.h" |
| 8 #include "base/message_loop/message_loop.h" |
| 9 #include "base/strings/stringprintf.h" |
| 10 #include "mojo/application/public/cpp/application_connection.h" |
| 11 #include "mojo/application/public/cpp/application_delegate.h" |
| 12 #include "mojo/application/public/cpp/application_impl.h" |
| 13 #include "mojo/application/public/cpp/connect.h" |
| 14 #include "mojo/application/public/cpp/interface_factory.h" |
| 15 #include "mojo/common/weak_binding_set.h" |
| 16 #include "mojo/public/cpp/bindings/strong_binding.h" |
| 17 #include "mojo/shell/application_loader.h" |
| 18 #include "mojo/shell/application_manager.h" |
| 19 #include "mojo/shell/capability_filter_unittest.mojom.h" |
| 20 #include "testing/gtest/include/gtest/gtest.h" |
| 21 |
| 22 namespace mojo { |
| 23 namespace shell { |
| 24 namespace { |
| 25 |
| 26 // Listens for services exposed/blocked and for application connections being |
| 27 // closed. Quits |loop| when all expectations are met. |
| 28 class ConnectionValidator { |
| 29 public: |
| 30 ConnectionValidator(const std::set<std::string>& expectations, |
| 31 base::MessageLoop* loop) |
| 32 : expectations_(expectations), |
| 33 loop_(loop) {} |
| 34 ~ConnectionValidator() {} |
| 35 |
| 36 void AddServiceCalled(const std::string& app_url, |
| 37 const std::string& service_url, |
| 38 const std::string& name, |
| 39 bool blocked) { |
| 40 Validate(base::StringPrintf("%s %s %s %s", |
| 41 blocked ? "B" : "E", app_url.c_str(), service_url.c_str(), |
| 42 name.c_str())); |
| 43 } |
| 44 |
| 45 void ConnectionClosed(const std::string& app_url, |
| 46 const std::string& service_url) { |
| 47 Validate(base::StringPrintf("C %s %s", app_url.c_str(), |
| 48 service_url.c_str())); |
| 49 } |
| 50 |
| 51 bool expectations_met() { return expectations_.empty(); } |
| 52 |
| 53 private: |
| 54 void Validate(const std::string& result) { |
| 55 DVLOG(1) << "Validate: " << result; |
| 56 auto i = expectations_.find(result); |
| 57 if (i != expectations_.end()) { |
| 58 expectations_.erase(i); |
| 59 if (expectations_.empty()) |
| 60 loop_->Quit(); |
| 61 } else { |
| 62 DVLOG(1) << "Unexpected result."; |
| 63 loop_->Quit(); |
| 64 } |
| 65 } |
| 66 |
| 67 std::set<std::string> expectations_; |
| 68 base::MessageLoop* loop_; |
| 69 |
| 70 DISALLOW_COPY_AND_ASSIGN(ConnectionValidator); |
| 71 }; |
| 72 |
| 73 // This class models an application who will use the shell to interact with a |
| 74 // system service. The shell may limit this application's visibility of the full |
| 75 // set of interfaces exposed by that service. |
| 76 class TestApplication : public ApplicationDelegate, |
| 77 public ApplicationLoader, |
| 78 public InterfaceFactory<Client>, |
| 79 public Client { |
| 80 public: |
| 81 TestApplication(ConnectionValidator* validator, |
| 82 bool connect_to_test_service_2) |
| 83 : validator_(validator), |
| 84 connect_to_test_service_2_(connect_to_test_service_2) {} |
| 85 ~TestApplication() override {} |
| 86 |
| 87 private: |
| 88 // Overridden from ApplicationDelegate: |
| 89 bool ConfigureIncomingConnection(ApplicationConnection*) override { |
| 90 URLRequestPtr request(URLRequest::New()); |
| 91 request->url = String::From("test:service"); |
| 92 ApplicationConnection* connection = |
| 93 app_->ConnectToApplication(request.Pass()); |
| 94 connection->SetRemoteServiceProviderConnectionErrorHandler( |
| 95 base::Bind(&TestApplication::Connection1Closed, |
| 96 base::Unretained(this))); |
| 97 |
| 98 if (connect_to_test_service_2_) { |
| 99 URLRequestPtr request2(URLRequest::New()); |
| 100 request2->url = String::From("test:service2"); |
| 101 ApplicationConnection* connection2 = |
| 102 app_->ConnectToApplication(request2.Pass()); |
| 103 connection2->SetRemoteServiceProviderConnectionErrorHandler( |
| 104 base::Bind(&TestApplication::Connection2Closed, |
| 105 base::Unretained(this))); |
| 106 } |
| 107 return true; |
| 108 } |
| 109 bool ConfigureOutgoingConnection(ApplicationConnection* connection) override { |
| 110 connection->AddService<Client>(this); |
| 111 return true; |
| 112 } |
| 113 |
| 114 // Overridden from ApplicationLoader: |
| 115 void Load(const GURL& url, InterfaceRequest<Application> request) override { |
| 116 app_.reset(new ApplicationImpl(this, request.Pass())); |
| 117 } |
| 118 |
| 119 // Overridden from InterfaceFactory<Client>: |
| 120 void Create(ApplicationConnection* connection, |
| 121 InterfaceRequest<Client> request) override { |
| 122 client_bindings_.AddBinding(this, request.Pass()); |
| 123 } |
| 124 |
| 125 // Overridden from Client: |
| 126 void AddServiceCalled(const String& app_url, |
| 127 const String& service_url, |
| 128 const String& name, |
| 129 bool blocked) override { |
| 130 validator_->AddServiceCalled(app_url, service_url, name, blocked); |
| 131 } |
| 132 |
| 133 void Connection1Closed() { |
| 134 validator_->ConnectionClosed(app_->url(), "test:service"); |
| 135 } |
| 136 |
| 137 void Connection2Closed() { |
| 138 validator_->ConnectionClosed(app_->url(), "test:service2"); |
| 139 } |
| 140 |
| 141 ConnectionValidator* validator_; |
| 142 bool connect_to_test_service_2_; |
| 143 scoped_ptr<ApplicationImpl> app_; |
| 144 WeakBindingSet<Client> client_bindings_; |
| 145 |
| 146 DISALLOW_COPY_AND_ASSIGN(TestApplication); |
| 147 }; |
| 148 |
| 149 // This class models a system service that exposes two interfaces, Safe and |
| 150 // Unsafe. The interface Unsafe is not to be exposed to untrusted applications. |
| 151 class ServiceApplication : public ApplicationDelegate, |
| 152 public ApplicationLoader, |
| 153 public InterfaceFactory<Safe>, |
| 154 public InterfaceFactory<Unsafe>, |
| 155 public Safe, |
| 156 public Unsafe { |
| 157 public: |
| 158 ServiceApplication() {} |
| 159 ~ServiceApplication() override {} |
| 160 |
| 161 private: |
| 162 // Overridden from ApplicationDelegate: |
| 163 bool ConfigureIncomingConnection(ApplicationConnection* connection) override { |
| 164 connection->ConnectToService(&client_); |
| 165 AddService<Safe>(connection); |
| 166 AddService<Unsafe>(connection); |
| 167 return true; |
| 168 } |
| 169 |
| 170 // Overridden from ApplicationLoader: |
| 171 void Load(const GURL& url, |
| 172 InterfaceRequest<Application> application_request) override { |
| 173 app_.reset(new ApplicationImpl(this, application_request.Pass())); |
| 174 } |
| 175 |
| 176 // Overridden from InterfaceFactory<Safe>: |
| 177 void Create(ApplicationConnection* connection, |
| 178 InterfaceRequest<Safe> request) override { |
| 179 safe_bindings_.AddBinding(this, request.Pass()); |
| 180 } |
| 181 |
| 182 // Overridden from InterfaceFactory<Unsafe>: |
| 183 void Create(ApplicationConnection* connection, |
| 184 InterfaceRequest<Unsafe> request) override { |
| 185 unsafe_bindings_.AddBinding(this, request.Pass()); |
| 186 } |
| 187 |
| 188 template <typename Interface> |
| 189 void AddService(ApplicationConnection* connection) { |
| 190 client_->AddServiceCalled(connection->GetRemoteApplicationURL(), |
| 191 connection->GetConnectionURL(), |
| 192 Interface::Name_, |
| 193 !connection->AddService<Interface>(this)); |
| 194 } |
| 195 |
| 196 scoped_ptr<ApplicationImpl> app_; |
| 197 ClientPtr client_; |
| 198 WeakBindingSet<Safe> safe_bindings_; |
| 199 WeakBindingSet<Unsafe> unsafe_bindings_; |
| 200 |
| 201 DISALLOW_COPY_AND_ASSIGN(ServiceApplication); |
| 202 }; |
| 203 |
| 204 class TestApplicationManagerDelegate : public ApplicationManager::Delegate { |
| 205 public: |
| 206 TestApplicationManagerDelegate() {} |
| 207 ~TestApplicationManagerDelegate() override {} |
| 208 |
| 209 private: |
| 210 // Overridden from ApplicationManager::Delegate: |
| 211 GURL ResolveMappings(const GURL& url) override { |
| 212 return url; |
| 213 } |
| 214 GURL ResolveMojoURL(const GURL& url) override { |
| 215 return url; |
| 216 } |
| 217 bool CreateFetcher(const GURL& url, |
| 218 const Fetcher::FetchCallback& loader_callback) override { |
| 219 return false; |
| 220 } |
| 221 |
| 222 DISALLOW_COPY_AND_ASSIGN(TestApplicationManagerDelegate); |
| 223 }; |
| 224 |
| 225 class CapabilityFilterTest : public testing::Test { |
| 226 public: |
| 227 CapabilityFilterTest() {} |
| 228 ~CapabilityFilterTest() override {} |
| 229 |
| 230 protected: |
| 231 void RunApplication(const std::string& url, CapabilityFilterPtr filter) { |
| 232 ServiceProviderPtr services; |
| 233 URLRequestPtr request(URLRequest::New()); |
| 234 request->url = String::From(url); |
| 235 application_manager_->ConnectToApplication( |
| 236 nullptr, request.Pass(), std::string(), GURL(), GetProxy(&services), |
| 237 nullptr, filter.Pass(), base::MessageLoop::QuitWhenIdleClosure()); |
| 238 } |
| 239 |
| 240 base::MessageLoop* loop() { return &loop_; } |
| 241 ApplicationManager* application_manager() { |
| 242 return application_manager_.get(); |
| 243 } |
| 244 |
| 245 private: |
| 246 // Overridden from testing::Test: |
| 247 void SetUp() override { |
| 248 application_manager_.reset(new ApplicationManager(&test_delegate_)); |
| 249 } |
| 250 void TearDown() override { |
| 251 application_manager_.reset(); |
| 252 } |
| 253 |
| 254 base::ShadowingAtExitManager at_exit_; |
| 255 TestApplicationManagerDelegate test_delegate_; |
| 256 base::MessageLoop loop_; |
| 257 scoped_ptr<ApplicationManager> application_manager_; |
| 258 |
| 259 DISALLOW_COPY_AND_ASSIGN(CapabilityFilterTest); |
| 260 }; |
| 261 |
| 262 TEST_F(CapabilityFilterTest, Blocking) { |
| 263 std::set<std::string> expectations; |
| 264 expectations.insert("E test:trusted test:service mojo::shell::Safe"); |
| 265 expectations.insert("E test:trusted test:service mojo::shell::Unsafe"); |
| 266 expectations.insert("E test:trusted test:service2 mojo::shell::Safe"); |
| 267 expectations.insert("E test:trusted test:service2 mojo::shell::Unsafe"); |
| 268 expectations.insert("E test:untrusted test:service mojo::shell::Safe"); |
| 269 expectations.insert("B test:untrusted test:service mojo::shell::Unsafe"); |
| 270 expectations.insert("C test:untrusted test:service2"); |
| 271 |
| 272 ConnectionValidator validator(expectations, loop()); |
| 273 application_manager()->SetLoaderForURL( |
| 274 make_scoped_ptr(new TestApplication(&validator, true)), |
| 275 GURL("test:trusted")); |
| 276 application_manager()->SetLoaderForURL( |
| 277 make_scoped_ptr(new TestApplication(&validator, true)), |
| 278 GURL("test:untrusted")); |
| 279 application_manager()->SetLoaderForURL( |
| 280 make_scoped_ptr(new ServiceApplication), GURL("test:service")); |
| 281 application_manager()->SetLoaderForURL( |
| 282 make_scoped_ptr(new ServiceApplication), GURL("test:service2")); |
| 283 |
| 284 Array<String> interfaces(Array<String>::New(1)); |
| 285 interfaces[0] = String::From(std::string(Safe::Name_)); |
| 286 CapabilityFilterPtr filter(CapabilityFilter::New()); |
| 287 filter->filter.insert("test:service", interfaces.Pass()); |
| 288 |
| 289 // This first application can only connect to test:service. Connections to |
| 290 // test:service2 will be blocked. It also will only be able to see the "Safe" |
| 291 // interface exposed by test:service. It will be blocked from seeing "Unsafe". |
| 292 RunApplication("test:untrusted", filter.Pass()); |
| 293 |
| 294 // This second application can connect to both test:service and test:service2. |
| 295 // It can connect to both "Safe" and "Unsafe" interfaces. |
| 296 RunApplication("test:trusted", nullptr); |
| 297 |
| 298 loop()->Run(); |
| 299 |
| 300 EXPECT_TRUE(validator.expectations_met()); |
| 301 } |
| 302 |
| 303 TEST_F(CapabilityFilterTest, Wildcards) { |
| 304 std::set<std::string> expectations; |
| 305 expectations.insert("E test:wildcard test:service mojo::shell::Safe"); |
| 306 expectations.insert("E test:wildcard test:service mojo::shell::Unsafe"); |
| 307 expectations.insert("C test:blocked test:service"); |
| 308 expectations.insert("B test:wildcard2 test:service mojo::shell::Safe"); |
| 309 expectations.insert("B test:wildcard2 test:service mojo::shell::Unsafe"); |
| 310 expectations.insert("B test:wildcard2 test:service2 mojo::shell::Safe"); |
| 311 expectations.insert("B test:wildcard2 test:service2 mojo::shell::Unsafe"); |
| 312 expectations.insert("E test:wildcard3 test:service mojo::shell::Safe"); |
| 313 expectations.insert("E test:wildcard3 test:service mojo::shell::Unsafe"); |
| 314 expectations.insert("E test:wildcard3 test:service2 mojo::shell::Safe"); |
| 315 expectations.insert("B test:wildcard3 test:service2 mojo::shell::Unsafe"); |
| 316 |
| 317 ConnectionValidator validator(expectations, loop()); |
| 318 application_manager()->SetLoaderForURL( |
| 319 make_scoped_ptr(new TestApplication(&validator, false)), |
| 320 GURL("test:wildcard")); |
| 321 application_manager()->SetLoaderForURL( |
| 322 make_scoped_ptr(new TestApplication(&validator, false)), |
| 323 GURL("test:blocked")); |
| 324 application_manager()->SetLoaderForURL( |
| 325 make_scoped_ptr(new TestApplication(&validator, true)), |
| 326 GURL("test:wildcard2")); |
| 327 application_manager()->SetLoaderForURL( |
| 328 make_scoped_ptr(new TestApplication(&validator, true)), |
| 329 GURL("test:wildcard3")); |
| 330 application_manager()->SetLoaderForURL( |
| 331 make_scoped_ptr(new ServiceApplication), GURL("test:service")); |
| 332 application_manager()->SetLoaderForURL( |
| 333 make_scoped_ptr(new ServiceApplication), GURL("test:service2")); |
| 334 |
| 335 // This application is allowed to connect to any application because of a |
| 336 // wildcard rule, and any interface exposed because of a wildcard rule in |
| 337 // the interface array. |
| 338 CapabilityFilterPtr filter1(CapabilityFilter::New()); |
| 339 Array<String> interfaces(Array<String>::New(1)); |
| 340 interfaces[0] = "*"; |
| 341 filter1->filter.insert("*", interfaces.Pass()); |
| 342 RunApplication("test:wildcard", filter1.Pass()); |
| 343 |
| 344 // This application is allowed to connect to no other applications because of |
| 345 // an empty capability filter. |
| 346 RunApplication("test:blocked", CapabilityFilter::New()); |
| 347 |
| 348 // This application is allowed to connect to any application because of a |
| 349 // wildcard rule but may not connect to any interfaces because of an empty |
| 350 // interface array. |
| 351 CapabilityFilterPtr filter2(CapabilityFilter::New()); |
| 352 filter2->filter.insert("*", Array<String>::New(0)); |
| 353 RunApplication("test:wildcard2", filter2.Pass()); |
| 354 |
| 355 // This application is allowed to connect to both test:service and |
| 356 // test:service2, and may see any interface exposed by test:service but only |
| 357 // the Safe interface exposed by test:service2. |
| 358 CapabilityFilterPtr filter3(CapabilityFilter::New()); |
| 359 Array<String> interfaces1(Array<String>::New(1)); |
| 360 interfaces1[0] = "*"; |
| 361 filter3->filter.insert("test:service", interfaces1.Pass()); |
| 362 Array<String> interfaces2(Array<String>::New(1)); |
| 363 interfaces2[0] = String::From(std::string(Safe::Name_)); |
| 364 filter3->filter.insert("test:service2", interfaces2.Pass()); |
| 365 RunApplication("test:wildcard3", filter3.Pass()); |
| 366 |
| 367 loop()->Run(); |
| 368 |
| 369 EXPECT_TRUE(validator.expectations_met()); |
| 370 } |
| 371 |
| 372 } // namespace |
| 373 } // namespace shell |
| 374 } // namespace mojo |
OLD | NEW |