Index: base/memory/shared_memory_win_unittest.cc |
diff --git a/base/memory/shared_memory_win_unittest.cc b/base/memory/shared_memory_win_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6e19083f73586cebed0fa9f42093ae023b776f38 |
--- /dev/null |
+++ b/base/memory/shared_memory_win_unittest.cc |
@@ -0,0 +1,220 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include <windows.h> |
+#include <sddl.h> |
+ |
+#include "base/command_line.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/memory/shared_memory.h" |
+#include "base/process/process.h" |
+#include "base/rand_util.h" |
+#include "base/strings/stringprintf.h" |
+#include "base/strings/sys_string_conversions.h" |
+#include "base/test/multiprocess_test.h" |
+#include "base/test/test_timeouts.h" |
+#include "base/win/scoped_handle.h" |
+#include "testing/multiprocess_func_list.h" |
+ |
+namespace base { |
+namespace { |
+const char* kHandleSwitchName = "shared_memory_win_test_switch"; |
+ |
+// Creates a process token with a low integrity SID. |
+win::ScopedHandle CreateLowIntegritySID() { |
+ HANDLE process_token_raw = nullptr; |
+ BOOL success = ::OpenProcessToken(GetCurrentProcess(), |
+ TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | |
+ TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY, |
+ &process_token_raw); |
+ if (!success) |
+ return base::win::ScopedHandle(); |
+ win::ScopedHandle process_token(process_token_raw); |
+ |
+ HANDLE lowered_process_token_raw = nullptr; |
+ success = |
+ ::DuplicateTokenEx(process_token.Get(), 0, NULL, SecurityImpersonation, |
+ TokenPrimary, &lowered_process_token_raw); |
+ if (!success) |
+ return base::win::ScopedHandle(); |
+ win::ScopedHandle lowered_process_token(lowered_process_token_raw); |
+ |
+ // Low integrity SID |
+ WCHAR integrity_sid_string[20] = L"S-1-16-4096"; |
+ PSID integrity_sid = nullptr; |
+ success = ::ConvertStringSidToSid(integrity_sid_string, &integrity_sid); |
+ if (!success) |
+ return base::win::ScopedHandle(); |
+ |
+ TOKEN_MANDATORY_LABEL TIL = {0}; |
Nico
2016/02/19 03:33:01
FAILED: ninja -t msvc -e environment.x86 -- C:\b\b
|
+ TIL.Label.Attributes = SE_GROUP_INTEGRITY; |
+ TIL.Label.Sid = integrity_sid; |
+ success = ::SetTokenInformation( |
+ lowered_process_token.Get(), TokenIntegrityLevel, &TIL, |
+ sizeof(TOKEN_MANDATORY_LABEL) + GetLengthSid(integrity_sid)); |
+ if (!success) |
+ return base::win::ScopedHandle(); |
+ return lowered_process_token; |
+} |
+ |
+// Reads a HANDLE from the pipe as a raw int, least significant digit first. |
+win::ScopedHandle ReadHandleFromPipe(HANDLE pipe) { |
+ // Read from parent pipe. |
+ const size_t buf_size = 1000; |
+ char buffer[buf_size]; |
+ memset(buffer, 0, buf_size); |
+ DWORD bytes_read; |
+ BOOL success = ReadFile(pipe, buffer, buf_size, &bytes_read, NULL); |
+ |
+ if (!success || bytes_read == 0) { |
+ LOG(ERROR) << "Failed to read handle from pipe."; |
+ return win::ScopedHandle(); |
+ } |
+ |
+ int handle_as_int = 0; |
+ int power_of_ten = 1; |
+ for (unsigned int i = 0; i < bytes_read; ++i) { |
+ handle_as_int += buffer[i] * power_of_ten; |
+ power_of_ten *= 10; |
+ } |
+ |
+ return win::ScopedHandle(reinterpret_cast<HANDLE>(handle_as_int)); |
+} |
+ |
+// Writes a HANDLE to a pipe as a raw int, least significant digit first. |
+void WriteHandleToPipe(HANDLE pipe, HANDLE handle) { |
+ 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-
|
+ |
+ scoped_ptr<char, base::FreeDeleter> buffer(static_cast<char*>(malloc(1000))); |
+ size_t index = 0; |
+ while (handle_as_int > 0) { |
+ buffer.get()[index] = handle_as_int % 10; |
+ handle_as_int /= 10; |
+ ++index; |
+ } |
+ |
+ ::ConnectNamedPipe(pipe, nullptr); |
+ DWORD written; |
+ ASSERT_TRUE(::WriteFile(pipe, buffer.get(), index, &written, NULL)); |
+} |
+ |
+// Creates a communication pipe with the given name. |
+win::ScopedHandle CreateCommunicationPipe(const std::wstring& name) { |
+ return win::ScopedHandle(CreateNamedPipe(name.c_str(), // pipe name |
+ PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, |
+ 1000, 1000, 0, NULL)); |
+} |
+ |
+// Generates a random name for a communication pipe. |
+std::wstring CreateCommunicationPipeName() { |
+ uint64_t rand_values[4]; |
+ RandBytes(&rand_values, sizeof(rand_values)); |
+ std::wstring child_pipe_name = StringPrintf( |
+ L"\\\\.\\pipe\\SharedMemoryWinTest_%016llx%016llx%016llx%016llx", |
+ rand_values[0], rand_values[1], rand_values[2], rand_values[3]); |
+ return child_pipe_name; |
+} |
+ |
+class SharedMemoryWinTest : public base::MultiProcessTest { |
+ protected: |
+ CommandLine MakeCmdLine(const std::string& procname) override { |
+ CommandLine line = base::MultiProcessTest::MakeCmdLine(procname); |
+ line.AppendSwitchASCII(kHandleSwitchName, communication_pipe_name_); |
+ return line; |
+ } |
+ |
+ std::string communication_pipe_name_; |
+}; |
+ |
+MULTIPROCESS_TEST_MAIN(LowerPermissions) { |
+ std::string handle_name = |
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kHandleSwitchName); |
+ std::wstring handle_name16 = SysUTF8ToWide(handle_name); |
+ win::ScopedHandle parent_pipe( |
+ ::CreateFile(handle_name16.c_str(), // pipe name |
+ GENERIC_READ, |
+ 0, // no sharing |
+ NULL, // default security attributes |
+ OPEN_EXISTING, // opens existing pipe |
+ 0, // default attributes |
+ NULL)); // no template file |
+ if (parent_pipe.Get() == INVALID_HANDLE_VALUE) { |
+ LOG(ERROR) << "Failed to open communication pipe."; |
+ return 1; |
+ } |
+ |
+ win::ScopedHandle received_handle = ReadHandleFromPipe(parent_pipe.Get()); |
+ if (!received_handle.Get()) { |
+ LOG(ERROR) << "Failed to read handle from pipe."; |
+ return 1; |
+ } |
+ |
+ // Attempting to add the WRITE_DAC permission should fail. |
+ HANDLE duped_handle; |
+ BOOL success = ::DuplicateHandle(GetCurrentProcess(), received_handle.Get(), |
+ GetCurrentProcess(), &duped_handle, |
+ FILE_MAP_READ | WRITE_DAC, FALSE, 0); |
+ if (success) { |
+ LOG(ERROR) << "Should not have been able to add WRITE_DAC permission."; |
+ return 1; |
+ } |
+ |
+ // Attempting to add the FILE_MAP_WRITE permission should fail. |
+ success = ::DuplicateHandle(GetCurrentProcess(), received_handle.Get(), |
+ GetCurrentProcess(), &duped_handle, |
+ FILE_MAP_READ | FILE_MAP_WRITE, FALSE, 0); |
+ if (success) { |
+ LOG(ERROR) << "Should not have been able to add FILE_MAP_WRITE permission."; |
+ return 1; |
+ } |
+ |
+ // Attempting to duplicate the HANDLE with the same permissions should |
+ // succeed. |
+ success = ::DuplicateHandle(GetCurrentProcess(), received_handle.Get(), |
+ GetCurrentProcess(), &duped_handle, FILE_MAP_READ, |
+ FALSE, 0); |
+ if (!success) { |
+ LOG(ERROR) << "Failed to duplicate handle."; |
+ return 4; |
+ } |
+ ::CloseHandle(duped_handle); |
+ return 0; |
+} |
+ |
+TEST_F(SharedMemoryWinTest, LowerPermissions) { |
+ std::wstring communication_pipe_name = CreateCommunicationPipeName(); |
+ communication_pipe_name_ = SysWideToUTF8(communication_pipe_name); |
+ |
+ win::ScopedHandle communication_pipe = |
+ CreateCommunicationPipe(communication_pipe_name); |
+ ASSERT_TRUE(communication_pipe.Get()); |
+ |
+ win::ScopedHandle lowered_process_token = CreateLowIntegritySID(); |
+ ASSERT_TRUE(lowered_process_token.Get()); |
+ |
+ base::LaunchOptions options; |
+ options.as_user = lowered_process_token.Get(); |
+ base::Process process = SpawnChildWithOptions("LowerPermissions", options); |
+ ASSERT_TRUE(process.IsValid()); |
+ |
+ SharedMemory memory; |
+ memory.CreateAndMapAnonymous(1001); |
+ |
+ // Duplicate into child process, giving only FILE_MAP_READ permissions. |
+ HANDLE raw_handle = nullptr; |
+ ::DuplicateHandle(::GetCurrentProcess(), memory.handle().GetHandle(), |
+ process.Handle(), &raw_handle, |
+ FILE_MAP_READ | SECTION_QUERY, FALSE, 0); |
+ ASSERT_TRUE(raw_handle); |
+ |
+ WriteHandleToPipe(communication_pipe.Get(), raw_handle); |
+ |
+ int exit_code; |
+ EXPECT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(), |
+ &exit_code)); |
+ EXPECT_EQ(0, exit_code); |
+} |
+ |
+} // namespace |
+} // namespace base |