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 "ppapi/tests/test_broker.h" 6 7 #if defined(_MSC_VER) 8 #define OS_WIN 1 9 #include <windows.h> 10 #else 11 #define OS_POSIX 1 12 #include <errno.h> 13 #include <unistd.h> 14 #endif 15 16 #include <cstdio> 17 #include <cstring> 18 #include <fstream> 19 #include <limits> 20 21 #include "ppapi/c/pp_errors.h" 22 #include "ppapi/c/trusted/ppp_broker.h" 23 #include "ppapi/c/trusted/ppb_broker_trusted.h" 24 #include "ppapi/tests/test_utils.h" 25 #include "ppapi/tests/testing_instance.h" 26 27 REGISTER_TEST_CASE(Broker); 28 29 namespace { 30 31 const char kHelloMessage[] = "Hello Plugin! This is Broker!"; 32 // Message sent from broker to plugin if the broker is unsandboxed. 33 const char kBrokerUnsandboxed[] = "Broker is Unsandboxed!"; 34 // Message sent from broker to plugin if the broker is sandboxed. This message 35 // needs to be longer than |kBrokerUnsandboxed| because the plugin is expecting 36 // |kBrokerUnsandboxed|. If it's shorter and the broker doesn't close its handle 37 // properly the plugin will hang waiting for all data of |kBrokerUnsandboxed| to 38 // be read. 39 const char kBrokerSandboxed[] = "Broker is Sandboxed! Verification failed!"; 40 41 #if defined(OS_WIN) 42 typedef HANDLE PlatformFile; 43 const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE; 44 const int32_t kInvalidHandle = static_cast<int32_t>( 45 reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE)); 46 #elif defined(OS_POSIX) 47 typedef int PlatformFile; 48 const PlatformFile kInvalidPlatformFileValue = -1; 49 const int32_t kInvalidHandle = -1; 50 #endif 51 52 PlatformFile IntToPlatformFile(int32_t handle) { 53 #if defined(OS_WIN) 54 return reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle)); 55 #elif defined(OS_POSIX) 56 return handle; 57 #endif 58 } 59 60 #if defined(OS_POSIX) 61 62 #define HANDLE_EINTR(x) ({ \ 63 typeof(x) eintr_wrapper_result; \ 64 do { \ 65 eintr_wrapper_result = (x); \ 66 } while (eintr_wrapper_result == -1 && errno == EINTR); \ 67 eintr_wrapper_result; \ 68 }) 69 70 #define IGNORE_EINTR(x) ({ \ 71 typeof(x) eintr_wrapper_result; \ 72 do { \ 73 eintr_wrapper_result = (x); \ 74 if (eintr_wrapper_result == -1 && errno == EINTR) { \ 75 eintr_wrapper_result = 0; \ 76 } \ 77 } while (0); \ 78 eintr_wrapper_result; \ 79 }) 80 81 #endif 82 83 bool ReadMessage(PlatformFile file, size_t message_len, char* message) { 84 #if defined(OS_WIN) 85 assert(message_len < std::numeric_limits<DWORD>::max()); 86 DWORD read = 0; 87 const DWORD size = static_cast<DWORD>(message_len); 88 return ::ReadFile(file, message, size, &read, NULL) && read == size; 89 #elif defined(OS_POSIX) 90 assert(message_len < 91 static_cast<size_t>(std::numeric_limits<ssize_t>::max())); 92 size_t total_read = 0; 93 while (total_read < message_len) { 94 ssize_t read = HANDLE_EINTR(::read(file, message + total_read, 95 message_len - total_read)); 96 if (read <= 0) 97 break; 98 total_read += read; 99 } 100 return total_read == message_len; 101 #endif 102 } 103 104 bool WriteMessage(PlatformFile file, size_t message_len, const char* message) { 105 #if defined(OS_WIN) 106 assert(message_len < std::numeric_limits<DWORD>::max()); 107 DWORD written = 0; 108 const DWORD size = static_cast<DWORD>(message_len); 109 return ::WriteFile(file, message, size, &written, NULL) && written == size; 110 #elif defined(OS_POSIX) 111 assert(message_len < 112 static_cast<size_t>(std::numeric_limits<ssize_t>::max())); 113 size_t total_written = 0; 114 while (total_written < message_len) { 115 ssize_t written = HANDLE_EINTR(::write(file, message + total_written, 116 message_len - total_written)); 117 if (written <= 0) 118 break; 119 total_written += written; 120 } 121 return total_written == message_len; 122 #endif 123 } 124 125 bool VerifyMessage(PlatformFile file, size_t message_len, const char* message) { 126 char* message_received = new char[message_len]; 127 bool success = ReadMessage(file, message_len, message_received) && 128 !::strcmp(message_received, message); 129 delete [] message_received; 130 return success; 131 } 132 133 bool ClosePlatformFile(PlatformFile file) { 134 #if defined(OS_WIN) 135 return !!::CloseHandle(file); 136 #elif defined(OS_POSIX) 137 return !IGNORE_EINTR(::close(file)); 138 #endif 139 } 140 141 bool VerifyIsUnsandboxed() { 142 #if defined(OS_WIN) 143 FILE* file = NULL; 144 wchar_t temp_path[MAX_PATH] = {'\0'}; 145 wchar_t file_name[MAX_PATH] = {'\0'}; 146 if (!::GetTempPath(MAX_PATH, temp_path) || 147 !::GetTempFileName(temp_path, L"test_pepper_broker", 0, file_name) || 148 ::_wfopen_s(&file, file_name, L"w")) 149 return false; 150 151 if (::fclose(file)) { 152 ::DeleteFile(file_name); 153 return false; 154 } 155 156 return !!::DeleteFile(file_name); 157 #elif defined(OS_POSIX) 158 char file_name[] = "/tmp/test_pepper_broker_XXXXXX"; 159 int fd = ::mkstemp(file_name); 160 if (-1 == fd) 161 return false; 162 163 if (IGNORE_EINTR(::close(fd))) { 164 ::remove(file_name); 165 return false; 166 } 167 168 return !::remove(file_name); 169 #endif 170 } 171 172 // Callback in the broker when a new broker connection occurs. 173 int32_t OnInstanceConnected(PP_Instance instance, int32_t handle) { 174 PlatformFile file = IntToPlatformFile(handle); 175 if (file == kInvalidPlatformFileValue) 176 return PP_ERROR_FAILED; 177 178 // Send hello message. 179 if (!WriteMessage(file, sizeof(kHelloMessage), kHelloMessage)) { 180 ClosePlatformFile(file); 181 return PP_ERROR_FAILED; 182 } 183 184 // Verify broker is not sandboxed and send result to plugin over the pipe. 185 if (VerifyIsUnsandboxed()) { 186 if (!WriteMessage(file, sizeof(kBrokerUnsandboxed), kBrokerUnsandboxed)) { 187 ClosePlatformFile(file); 188 return PP_ERROR_FAILED; 189 } 190 } else { 191 if (!WriteMessage(file, sizeof(kBrokerSandboxed), kBrokerSandboxed)) { 192 ClosePlatformFile(file); 193 return PP_ERROR_FAILED; 194 } 195 } 196 197 if (!ClosePlatformFile(file)) 198 return PP_ERROR_FAILED; 199 200 return PP_OK; 201 } 202 203 } // namespace 204 205 PP_EXPORT int32_t PPP_InitializeBroker( 206 PP_ConnectInstance_Func* connect_instance_func) { 207 *connect_instance_func = &OnInstanceConnected; 208 return PP_OK; 209 } 210 211 PP_EXPORT void PPP_ShutdownBroker() {} 212 213 TestBroker::TestBroker(TestingInstance* instance) 214 : TestCase(instance), 215 broker_interface_(NULL) { 216 } 217 218 bool TestBroker::Init() { 219 broker_interface_ = static_cast<const PPB_BrokerTrusted*>( 220 pp::Module::Get()->GetBrowserInterface(PPB_BROKER_TRUSTED_INTERFACE)); 221 return !!broker_interface_; 222 } 223 224 void TestBroker::RunTests(const std::string& filter) { 225 RUN_TEST(Create, filter); 226 RUN_TEST(Create, filter); 227 RUN_TEST(GetHandleFailure, filter); 228 RUN_TEST_FORCEASYNC_AND_NOT(ConnectFailure, filter); 229 RUN_TEST_FORCEASYNC_AND_NOT(ConnectAndPipe, filter); 230 231 // The following tests require special setup, so only run them if they're 232 // explicitly specified by the filter. 233 if (!ShouldRunAllTests(filter)) { 234 RUN_TEST(ConnectPermissionDenied, filter); 235 RUN_TEST(ConnectPermissionGranted, filter); 236 RUN_TEST(IsAllowedPermissionDenied, filter); 237 RUN_TEST(IsAllowedPermissionGranted, filter); 238 } 239 } 240 241 std::string TestBroker::TestCreate() { 242 // Very simplistic test to make sure we can create a broker interface. 243 // TODO(raymes): All of the resources created in this file are leaked. Write 244 // a C++ wrapper for PPB_Broker_Trusted to avoid these leaks. 245 PP_Resource broker = broker_interface_->CreateTrusted( 246 instance_->pp_instance()); 247 ASSERT_TRUE(broker); 248 249 ASSERT_FALSE(broker_interface_->IsBrokerTrusted(0)); 250 ASSERT_TRUE(broker_interface_->IsBrokerTrusted(broker)); 251 252 PASS(); 253 } 254 255 // Test connection on invalid resource. 256 std::string TestBroker::TestConnectFailure() { 257 TestCompletionCallback callback(instance_->pp_instance(), callback_type()); 258 callback.WaitForResult(broker_interface_->Connect(0, 259 callback.GetCallback().pp_completion_callback())); 260 CHECK_CALLBACK_BEHAVIOR(callback); 261 ASSERT_EQ(PP_ERROR_BADRESOURCE, callback.result()); 262 263 PASS(); 264 } 265 266 std::string TestBroker::TestGetHandleFailure() { 267 int32_t handle = kInvalidHandle; 268 269 // Test getting the handle for an invalid resource. 270 ASSERT_EQ(PP_ERROR_BADRESOURCE, broker_interface_->GetHandle(0, &handle)); 271 272 // Connect hasn't been called so this should fail. 273 PP_Resource broker = broker_interface_->CreateTrusted( 274 instance_->pp_instance()); 275 ASSERT_TRUE(broker); 276 ASSERT_EQ(PP_ERROR_FAILED, broker_interface_->GetHandle(broker, &handle)); 277 278 PASS(); 279 } 280 281 std::string TestBroker::TestConnectAndPipe() { 282 PP_Resource broker = broker_interface_->CreateTrusted( 283 instance_->pp_instance()); 284 ASSERT_TRUE(broker); 285 286 TestCompletionCallback callback(instance_->pp_instance(), callback_type()); 287 callback.WaitForResult(broker_interface_->Connect(broker, 288 callback.GetCallback().pp_completion_callback())); 289 CHECK_CALLBACK_BEHAVIOR(callback); 290 ASSERT_EQ(PP_OK, callback.result()); 291 292 int32_t handle = kInvalidHandle; 293 ASSERT_EQ(PP_OK, broker_interface_->GetHandle(broker, &handle)); 294 ASSERT_NE(kInvalidHandle, handle); 295 296 PlatformFile file = IntToPlatformFile(handle); 297 ASSERT_TRUE(VerifyMessage(file, sizeof(kHelloMessage), kHelloMessage)); 298 ASSERT_TRUE(VerifyMessage(file, sizeof(kBrokerUnsandboxed), 299 kBrokerUnsandboxed)); 300 301 ASSERT_TRUE(ClosePlatformFile(file)); 302 303 PASS(); 304 } 305 306 std::string TestBroker::TestConnectPermissionDenied() { 307 // This assumes that the browser side is set up to deny access to the broker. 308 PP_Resource broker = broker_interface_->CreateTrusted( 309 instance_->pp_instance()); 310 ASSERT_TRUE(broker); 311 312 TestCompletionCallback callback(instance_->pp_instance(), callback_type()); 313 callback.WaitForResult(broker_interface_->Connect(broker, 314 callback.GetCallback().pp_completion_callback())); 315 CHECK_CALLBACK_BEHAVIOR(callback); 316 ASSERT_EQ(PP_ERROR_NOACCESS, callback.result()); 317 318 PASS(); 319 } 320 321 std::string TestBroker::TestConnectPermissionGranted() { 322 // This assumes that the browser side is set up to allow access to the broker. 323 PP_Resource broker = broker_interface_->CreateTrusted( 324 instance_->pp_instance()); 325 ASSERT_TRUE(broker); 326 327 TestCompletionCallback callback(instance_->pp_instance(), callback_type()); 328 callback.WaitForResult(broker_interface_->Connect(broker, 329 callback.GetCallback().pp_completion_callback())); 330 CHECK_CALLBACK_BEHAVIOR(callback); 331 ASSERT_EQ(PP_OK, callback.result()); 332 333 PASS(); 334 } 335 336 std::string TestBroker::TestIsAllowedPermissionDenied() { 337 PP_Resource broker = broker_interface_->CreateTrusted( 338 instance_->pp_instance()); 339 ASSERT_TRUE(broker); 340 ASSERT_EQ(PP_FALSE, broker_interface_->IsAllowed(broker)); 341 342 PASS(); 343 } 344 345 std::string TestBroker::TestIsAllowedPermissionGranted() { 346 PP_Resource broker = broker_interface_->CreateTrusted( 347 instance_->pp_instance()); 348 ASSERT_TRUE(broker); 349 ASSERT_EQ(PP_TRUE, broker_interface_->IsAllowed(broker)); 350 351 PASS(); 352 } 353