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
27REGISTER_TEST_CASE(Broker);
28
29namespace {
30
31const char kHelloMessage[] = "Hello Plugin! This is Broker!";
32// Message sent from broker to plugin if the broker is unsandboxed.
33const 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.
39const char kBrokerSandboxed[] = "Broker is Sandboxed! Verification failed!";
40
41#if defined(OS_WIN)
42typedef HANDLE PlatformFile;
43const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE;
44const int32_t kInvalidHandle = static_cast<int32_t>(
45    reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE));
46#elif defined(OS_POSIX)
47typedef int PlatformFile;
48const PlatformFile kInvalidPlatformFileValue = -1;
49const int32_t kInvalidHandle = -1;
50#endif
51
52PlatformFile 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
70bool 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
91bool 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
112bool 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
120bool 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
128bool 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.
160int32_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
192PP_EXPORT int32_t PPP_InitializeBroker(
193    PP_ConnectInstance_Func* connect_instance_func) {
194  *connect_instance_func = &OnInstanceConnected;
195  return PP_OK;
196}
197
198PP_EXPORT void PPP_ShutdownBroker() {}
199
200TestBroker::TestBroker(TestingInstance* instance)
201    : TestCase(instance),
202      broker_interface_(NULL) {
203}
204
205bool 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
211void 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
228std::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.
243std::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
253std::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
268std::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
293std::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
308std::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
323std::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
332std::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