Chromium Code Reviews| 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, | |
|
Mark Mentovai
2016/02/17 19:03:14
Windows Chrome style is to have a leading :: on wi
erikchen
2016/02/17 19:22:27
I went through the file and added many instances o
| |
| 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}; | |
| 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); | |
| 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 |