| // Copyright (c) 2012 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 "remoting/host/win/launch_process_with_token.h" |
| |
| #include <windows.h> |
| |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "base/win/scoped_handle.h" |
| #include "base/win/scoped_process_information.h" |
| #include "base/win/startup_information.h" |
| |
| using base::win::ScopedHandle; |
| |
| namespace { |
| |
| // Copies the process token making it a primary impersonation token. |
| // The returned handle will have |desired_access| rights. |
| bool CopyProcessToken(DWORD desired_access, ScopedHandle* token_out) { |
| HANDLE temp_handle; |
| if (!OpenProcessToken(GetCurrentProcess(), |
| TOKEN_DUPLICATE | desired_access, |
| &temp_handle)) { |
| PLOG(ERROR) << "Failed to open process token"; |
| return false; |
| } |
| ScopedHandle process_token(temp_handle); |
| |
| if (!DuplicateTokenEx(process_token.Get(), |
| desired_access, |
| nullptr, |
| SecurityImpersonation, |
| TokenPrimary, |
| &temp_handle)) { |
| PLOG(ERROR) << "Failed to duplicate the process token"; |
| return false; |
| } |
| |
| token_out->Set(temp_handle); |
| return true; |
| } |
| |
| // Creates a copy of the current process with SE_TCB_NAME privilege enabled. |
| bool CreatePrivilegedToken(ScopedHandle* token_out) { |
| ScopedHandle privileged_token; |
| DWORD desired_access = TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE | |
| TOKEN_DUPLICATE | TOKEN_QUERY; |
| if (!CopyProcessToken(desired_access, &privileged_token)) { |
| return false; |
| } |
| |
| // Get the LUID for the SE_TCB_NAME privilege. |
| TOKEN_PRIVILEGES state; |
| state.PrivilegeCount = 1; |
| state.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; |
| if (!LookupPrivilegeValue(nullptr, SE_TCB_NAME, &state.Privileges[0].Luid)) { |
| PLOG(ERROR) << "Failed to lookup the LUID for the SE_TCB_NAME privilege"; |
| return false; |
| } |
| |
| // Enable the SE_TCB_NAME privilege. |
| if (!AdjustTokenPrivileges(privileged_token.Get(), FALSE, &state, 0, nullptr, |
| 0)) { |
| PLOG(ERROR) << "Failed to enable SE_TCB_NAME privilege in a token"; |
| return false; |
| } |
| |
| *token_out = std::move(privileged_token); |
| return true; |
| } |
| |
| } // namespace |
| |
| namespace remoting { |
| |
| // Creates a copy of the current process token for the given |session_id| so |
| // it can be used to launch a process in that session. |
| bool CreateSessionToken(uint32_t session_id, ScopedHandle* token_out) { |
| ScopedHandle session_token; |
| DWORD desired_access = TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID | |
| TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY; |
| if (!CopyProcessToken(desired_access, &session_token)) { |
| return false; |
| } |
| |
| // Temporarily enable the SE_TCB_NAME privilege as it is required by |
| // SetTokenInformation(TokenSessionId). |
| ScopedHandle privileged_token; |
| if (!CreatePrivilegedToken(&privileged_token)) { |
| return false; |
| } |
| if (!ImpersonateLoggedOnUser(privileged_token.Get())) { |
| PLOG(ERROR) << "Failed to impersonate the privileged token"; |
| return false; |
| } |
| |
| // Change the session ID of the token. |
| DWORD new_session_id = session_id; |
| if (!SetTokenInformation(session_token.Get(), |
| TokenSessionId, |
| &new_session_id, |
| sizeof(new_session_id))) { |
| PLOG(ERROR) << "Failed to change session ID of a token"; |
| |
| // Revert to the default token. |
| CHECK(RevertToSelf()); |
| return false; |
| } |
| |
| // Revert to the default token. |
| CHECK(RevertToSelf()); |
| |
| *token_out = std::move(session_token); |
| return true; |
| } |
| |
| bool LaunchProcessWithToken( |
| const base::FilePath& binary, |
| const base::CommandLine::StringType& command_line, |
| HANDLE user_token, |
| SECURITY_ATTRIBUTES* process_attributes, |
| SECURITY_ATTRIBUTES* thread_attributes, |
| const base::HandlesToInheritVector& handles_to_inherit, |
| DWORD creation_flags, |
| const base::char16* desktop_name, |
| ScopedHandle* process_out, |
| ScopedHandle* thread_out) { |
| base::FilePath::StringType application_name = binary.value(); |
| |
| base::win::StartupInformation startup_info_wrapper; |
| STARTUPINFO* startup_info = startup_info_wrapper.startup_info(); |
| if (desktop_name) |
| startup_info->lpDesktop = const_cast<base::char16*>(desktop_name); |
| |
| bool inherit_handles = false; |
| if (!handles_to_inherit.empty()) { |
| if (handles_to_inherit.size() > |
| std::numeric_limits<DWORD>::max() / sizeof(HANDLE)) { |
| DLOG(ERROR) << "Too many handles to inherit."; |
| return false; |
| } |
| |
| // Ensure the handles can be inherited. |
| for (HANDLE handle : handles_to_inherit) { |
| BOOL result = SetHandleInformation(handle, HANDLE_FLAG_INHERIT, |
| HANDLE_FLAG_INHERIT); |
| PCHECK(result); |
| } |
| |
| if (!startup_info_wrapper.InitializeProcThreadAttributeList( |
| /* attribute_count= */ 1)) { |
| PLOG(ERROR) << "InitializeProcThreadAttributeList()"; |
| return false; |
| } |
| |
| if (!startup_info_wrapper.UpdateProcThreadAttribute( |
| PROC_THREAD_ATTRIBUTE_HANDLE_LIST, |
| const_cast<HANDLE*>(&handles_to_inherit.at(0)), |
| static_cast<DWORD>(handles_to_inherit.size() * sizeof(HANDLE)))) { |
| PLOG(ERROR) << "UpdateProcThreadAttribute()"; |
| return false; |
| } |
| |
| inherit_handles = true; |
| creation_flags |= EXTENDED_STARTUPINFO_PRESENT; |
| } |
| PROCESS_INFORMATION temp_process_info = {}; |
| BOOL result = CreateProcessAsUser(user_token, application_name.c_str(), |
| const_cast<LPWSTR>(command_line.c_str()), |
| process_attributes, thread_attributes, |
| inherit_handles, creation_flags, nullptr, |
| nullptr, startup_info, &temp_process_info); |
| |
| if (!result) { |
| PLOG(ERROR) << "Failed to launch a process with a user token"; |
| return false; |
| } |
| |
| base::win::ScopedProcessInformation process_info(temp_process_info); |
| |
| CHECK(process_info.IsValid()); |
| process_out->Set(process_info.TakeProcessHandle()); |
| thread_out->Set(process_info.TakeThreadHandle()); |
| return true; |
| } |
| |
| } // namespace remoting |