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
IntToPlatformFile(int32_t handle)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
ReadMessage(PlatformFile file,size_t message_len,char * message)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
WriteMessage(PlatformFile file,size_t message_len,const char * message)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
VerifyMessage(PlatformFile file,size_t message_len,const char * message)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
ClosePlatformFile(PlatformFile file)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
VerifyIsUnsandboxed()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.
OnInstanceConnected(PP_Instance instance,int32_t handle)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
PPP_InitializeBroker(PP_ConnectInstance_Func * connect_instance_func)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
PPP_ShutdownBroker()211 PP_EXPORT void PPP_ShutdownBroker() {}
212
TestBroker(TestingInstance * instance)213 TestBroker::TestBroker(TestingInstance* instance)
214 : TestCase(instance),
215 broker_interface_(NULL) {
216 }
217
Init()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
RunTests(const std::string & filter)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
TestCreate()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.
TestConnectFailure()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
TestGetHandleFailure()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
TestConnectAndPipe()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
TestConnectPermissionDenied()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
TestConnectPermissionGranted()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
TestIsAllowedPermissionDenied()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
TestIsAllowedPermissionGranted()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