OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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 <windows.h> | |
6 #include <sddl.h> | |
7 | |
8 #include "base/command_line.h" | |
9 #include "base/memory/scoped_ptr.h" | |
10 #include "base/memory/shared_memory.h" | |
11 #include "base/process/process.h" | |
12 #include "base/rand_util.h" | |
13 #include "base/strings/stringprintf.h" | |
14 #include "base/strings/sys_string_conversions.h" | |
15 #include "base/test/multiprocess_test.h" | |
16 #include "base/test/test_timeouts.h" | |
17 #include "base/win/scoped_handle.h" | |
18 #include "testing/multiprocess_func_list.h" | |
19 | |
20 namespace base { | |
21 namespace { | |
22 const char* kHandleSwitchName = "shared_memory_win_test_switch"; | |
23 | |
24 // Creates a process token with a low integrity SID. | |
25 win::ScopedHandle CreateLowIntegritySID() { | |
26 HANDLE process_token_raw = nullptr; | |
27 BOOL success = ::OpenProcessToken(GetCurrentProcess(), | |
28 TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | | |
29 TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY, | |
30 &process_token_raw); | |
31 if (!success) | |
32 return base::win::ScopedHandle(); | |
33 win::ScopedHandle process_token(process_token_raw); | |
34 | |
35 HANDLE lowered_process_token_raw = nullptr; | |
36 success = | |
37 ::DuplicateTokenEx(process_token.Get(), 0, NULL, SecurityImpersonation, | |
38 TokenPrimary, &lowered_process_token_raw); | |
39 if (!success) | |
40 return base::win::ScopedHandle(); | |
41 win::ScopedHandle lowered_process_token(lowered_process_token_raw); | |
42 | |
43 // Low integrity SID | |
44 WCHAR integrity_sid_string[20] = L"S-1-16-4096"; | |
45 PSID integrity_sid = nullptr; | |
46 success = ::ConvertStringSidToSid(integrity_sid_string, &integrity_sid); | |
47 if (!success) | |
48 return base::win::ScopedHandle(); | |
49 | |
50 TOKEN_MANDATORY_LABEL TIL = {0}; | |
Nico
2016/02/19 03:33:01
FAILED: ninja -t msvc -e environment.x86 -- C:\b\b
| |
51 TIL.Label.Attributes = SE_GROUP_INTEGRITY; | |
52 TIL.Label.Sid = integrity_sid; | |
53 success = ::SetTokenInformation( | |
54 lowered_process_token.Get(), TokenIntegrityLevel, &TIL, | |
55 sizeof(TOKEN_MANDATORY_LABEL) + GetLengthSid(integrity_sid)); | |
56 if (!success) | |
57 return base::win::ScopedHandle(); | |
58 return lowered_process_token; | |
59 } | |
60 | |
61 // Reads a HANDLE from the pipe as a raw int, least significant digit first. | |
62 win::ScopedHandle ReadHandleFromPipe(HANDLE pipe) { | |
63 // Read from parent pipe. | |
64 const size_t buf_size = 1000; | |
65 char buffer[buf_size]; | |
66 memset(buffer, 0, buf_size); | |
67 DWORD bytes_read; | |
68 BOOL success = ReadFile(pipe, buffer, buf_size, &bytes_read, NULL); | |
69 | |
70 if (!success || bytes_read == 0) { | |
71 LOG(ERROR) << "Failed to read handle from pipe."; | |
72 return win::ScopedHandle(); | |
73 } | |
74 | |
75 int handle_as_int = 0; | |
76 int power_of_ten = 1; | |
77 for (unsigned int i = 0; i < bytes_read; ++i) { | |
78 handle_as_int += buffer[i] * power_of_ten; | |
79 power_of_ten *= 10; | |
80 } | |
81 | |
82 return win::ScopedHandle(reinterpret_cast<HANDLE>(handle_as_int)); | |
83 } | |
84 | |
85 // Writes a HANDLE to a pipe as a raw int, least significant digit first. | |
86 void WriteHandleToPipe(HANDLE pipe, HANDLE handle) { | |
87 uint32_t handle_as_int = reinterpret_cast<uint32_t>(handle); | |
brucedawson
2016/02/20 00:49:52
VC++ 2015 prefers double casts for the truncation-
| |
88 | |
89 scoped_ptr<char, base::FreeDeleter> buffer(static_cast<char*>(malloc(1000))); | |
90 size_t index = 0; | |
91 while (handle_as_int > 0) { | |
92 buffer.get()[index] = handle_as_int % 10; | |
93 handle_as_int /= 10; | |
94 ++index; | |
95 } | |
96 | |
97 ::ConnectNamedPipe(pipe, nullptr); | |
98 DWORD written; | |
99 ASSERT_TRUE(::WriteFile(pipe, buffer.get(), index, &written, NULL)); | |
100 } | |
101 | |
102 // Creates a communication pipe with the given name. | |
103 win::ScopedHandle CreateCommunicationPipe(const std::wstring& name) { | |
104 return win::ScopedHandle(CreateNamedPipe(name.c_str(), // pipe name | |
105 PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, | |
106 1000, 1000, 0, NULL)); | |
107 } | |
108 | |
109 // Generates a random name for a communication pipe. | |
110 std::wstring CreateCommunicationPipeName() { | |
111 uint64_t rand_values[4]; | |
112 RandBytes(&rand_values, sizeof(rand_values)); | |
113 std::wstring child_pipe_name = StringPrintf( | |
114 L"\\\\.\\pipe\\SharedMemoryWinTest_%016llx%016llx%016llx%016llx", | |
115 rand_values[0], rand_values[1], rand_values[2], rand_values[3]); | |
116 return child_pipe_name; | |
117 } | |
118 | |
119 class SharedMemoryWinTest : public base::MultiProcessTest { | |
120 protected: | |
121 CommandLine MakeCmdLine(const std::string& procname) override { | |
122 CommandLine line = base::MultiProcessTest::MakeCmdLine(procname); | |
123 line.AppendSwitchASCII(kHandleSwitchName, communication_pipe_name_); | |
124 return line; | |
125 } | |
126 | |
127 std::string communication_pipe_name_; | |
128 }; | |
129 | |
130 MULTIPROCESS_TEST_MAIN(LowerPermissions) { | |
131 std::string handle_name = | |
132 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kHandleSwitchName); | |
133 std::wstring handle_name16 = SysUTF8ToWide(handle_name); | |
134 win::ScopedHandle parent_pipe( | |
135 ::CreateFile(handle_name16.c_str(), // pipe name | |
136 GENERIC_READ, | |
137 0, // no sharing | |
138 NULL, // default security attributes | |
139 OPEN_EXISTING, // opens existing pipe | |
140 0, // default attributes | |
141 NULL)); // no template file | |
142 if (parent_pipe.Get() == INVALID_HANDLE_VALUE) { | |
143 LOG(ERROR) << "Failed to open communication pipe."; | |
144 return 1; | |
145 } | |
146 | |
147 win::ScopedHandle received_handle = ReadHandleFromPipe(parent_pipe.Get()); | |
148 if (!received_handle.Get()) { | |
149 LOG(ERROR) << "Failed to read handle from pipe."; | |
150 return 1; | |
151 } | |
152 | |
153 // Attempting to add the WRITE_DAC permission should fail. | |
154 HANDLE duped_handle; | |
155 BOOL success = ::DuplicateHandle(GetCurrentProcess(), received_handle.Get(), | |
156 GetCurrentProcess(), &duped_handle, | |
157 FILE_MAP_READ | WRITE_DAC, FALSE, 0); | |
158 if (success) { | |
159 LOG(ERROR) << "Should not have been able to add WRITE_DAC permission."; | |
160 return 1; | |
161 } | |
162 | |
163 // Attempting to add the FILE_MAP_WRITE permission should fail. | |
164 success = ::DuplicateHandle(GetCurrentProcess(), received_handle.Get(), | |
165 GetCurrentProcess(), &duped_handle, | |
166 FILE_MAP_READ | FILE_MAP_WRITE, FALSE, 0); | |
167 if (success) { | |
168 LOG(ERROR) << "Should not have been able to add FILE_MAP_WRITE permission."; | |
169 return 1; | |
170 } | |
171 | |
172 // Attempting to duplicate the HANDLE with the same permissions should | |
173 // succeed. | |
174 success = ::DuplicateHandle(GetCurrentProcess(), received_handle.Get(), | |
175 GetCurrentProcess(), &duped_handle, FILE_MAP_READ, | |
176 FALSE, 0); | |
177 if (!success) { | |
178 LOG(ERROR) << "Failed to duplicate handle."; | |
179 return 4; | |
180 } | |
181 ::CloseHandle(duped_handle); | |
182 return 0; | |
183 } | |
184 | |
185 TEST_F(SharedMemoryWinTest, LowerPermissions) { | |
186 std::wstring communication_pipe_name = CreateCommunicationPipeName(); | |
187 communication_pipe_name_ = SysWideToUTF8(communication_pipe_name); | |
188 | |
189 win::ScopedHandle communication_pipe = | |
190 CreateCommunicationPipe(communication_pipe_name); | |
191 ASSERT_TRUE(communication_pipe.Get()); | |
192 | |
193 win::ScopedHandle lowered_process_token = CreateLowIntegritySID(); | |
194 ASSERT_TRUE(lowered_process_token.Get()); | |
195 | |
196 base::LaunchOptions options; | |
197 options.as_user = lowered_process_token.Get(); | |
198 base::Process process = SpawnChildWithOptions("LowerPermissions", options); | |
199 ASSERT_TRUE(process.IsValid()); | |
200 | |
201 SharedMemory memory; | |
202 memory.CreateAndMapAnonymous(1001); | |
203 | |
204 // Duplicate into child process, giving only FILE_MAP_READ permissions. | |
205 HANDLE raw_handle = nullptr; | |
206 ::DuplicateHandle(::GetCurrentProcess(), memory.handle().GetHandle(), | |
207 process.Handle(), &raw_handle, | |
208 FILE_MAP_READ | SECTION_QUERY, FALSE, 0); | |
209 ASSERT_TRUE(raw_handle); | |
210 | |
211 WriteHandleToPipe(communication_pipe.Get(), raw_handle); | |
212 | |
213 int exit_code; | |
214 EXPECT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(), | |
215 &exit_code)); | |
216 EXPECT_EQ(0, exit_code); | |
217 } | |
218 | |
219 } // namespace | |
220 } // namespace base | |
OLD | NEW |