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 
     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