Home | History | Annotate | Download | only in tests
      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