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