utils_win.cc (5481B)
1 // Copyright 2022 The Chromium Authors. 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 <vector> 6 7 #include <windows.h> 8 #include <sddl.h> 9 10 #include "utils_win.h" 11 12 namespace content_analysis { 13 namespace sdk { 14 namespace internal { 15 16 std::string GetUserSID() { 17 std::string sid; 18 19 HANDLE handle; 20 if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &handle) && 21 !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &handle)) { 22 return std::string(); 23 } 24 25 DWORD length = 0; 26 std::vector<char> buffer; 27 if (!GetTokenInformation(handle, TokenUser, nullptr, 0, &length)) { 28 DWORD err = GetLastError(); 29 if (err == ERROR_INSUFFICIENT_BUFFER) { 30 buffer.resize(length); 31 } 32 } 33 if (GetTokenInformation(handle, TokenUser, buffer.data(), buffer.size(), 34 &length)) { 35 TOKEN_USER* info = reinterpret_cast<TOKEN_USER*>(buffer.data()); 36 char* sid_string; 37 if (ConvertSidToStringSidA(info->User.Sid, &sid_string)) { 38 sid = sid_string; 39 LocalFree(sid_string); 40 } 41 } 42 43 CloseHandle(handle); 44 return sid; 45 } 46 47 std::string BuildPipeName(const char* prefix, 48 const std::string& base, 49 bool user_specific) { 50 std::string pipename = prefix; 51 52 // If the agent is not user-specific, the assumption is that it runs with 53 // administrator privileges. Create the pipe in a location only available 54 // to administrators. 55 if (!user_specific) 56 pipename += "ProtectedPrefix\\Administrators\\"; 57 58 pipename += base; 59 60 if (user_specific) { 61 std::string sid = GetUserSID(); 62 if (sid.empty()) 63 return std::string(); 64 65 pipename += "." + sid; 66 } 67 68 return pipename; 69 } 70 71 std::string GetPipeNameForAgent(const std::string& base, bool user_specific) { 72 return BuildPipeName(kPipePrefixForAgent, base, user_specific); 73 } 74 75 std::string GetPipeNameForClient(const std::string& base, bool user_specific) { 76 return BuildPipeName(kPipePrefixForClient, base, user_specific); 77 } 78 79 DWORD CreatePipe( 80 const std::string& name, 81 bool user_specific, 82 bool is_first_pipe, 83 HANDLE* handle) { 84 DWORD err = ERROR_SUCCESS; 85 DWORD mode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED; 86 87 // Create DACL for pipe to allow users on the local system to read and write 88 // to the pipe. The creator and owner as well as administrator always get 89 // full control of the pipe. 90 // 91 // If `user_specific` is true, a different agent instance is used for each 92 // OS user, so only allow the interactive logged on user to reads and write 93 // messages to the pipe. Otherwise only one agent instance used used for all 94 // OS users and all authenticated logged on users can reads and write 95 // messages. 96 // 97 // See https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-definition-language 98 // for a description of this string format. 99 constexpr char kDaclEveryone[] = "D:" 100 "(A;OICI;GA;;;CO)" // Allow full control to creator owner. 101 "(A;OICI;GA;;;BA)" // Allow full control to admins. 102 "(A;OICI;GRGW;;;WD)"; // Allow read and write to everyone. 103 constexpr char kDaclUserSpecific[] = "D:" 104 "(A;OICI;GA;;;CO)" // Allow full control to creator owner. 105 "(A;OICI;GA;;;BA)" // Allow full control to admins. 106 "(A;OICI;GRGW;;;IU)"; // Allow read and write to interactive user. 107 SECURITY_ATTRIBUTES sa; 108 memset(&sa, 0, sizeof(sa)); 109 sa.nLength = sizeof(sa); 110 sa.bInheritHandle = FALSE; 111 if (!ConvertStringSecurityDescriptorToSecurityDescriptorA( 112 user_specific ? kDaclUserSpecific : kDaclEveryone, SDDL_REVISION_1, 113 &sa.lpSecurityDescriptor, /*outSdSize=*/nullptr)) { 114 err = GetLastError(); 115 return err; 116 } 117 118 // When `is_first_pipe` is true, the agent expects there is no process that 119 // is currently listening on this pipe. If there is, CreateNamedPipeA() 120 // returns with ERROR_ACCESS_DENIED. This is used to detect if another 121 // process is listening for connections when there shouldn't be. 122 if (is_first_pipe) { 123 mode |= FILE_FLAG_FIRST_PIPE_INSTANCE; 124 } 125 *handle = CreateNamedPipeA(name.c_str(), mode, 126 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | 127 PIPE_REJECT_REMOTE_CLIENTS, PIPE_UNLIMITED_INSTANCES, kBufferSize, 128 kBufferSize, 0, &sa); 129 if (*handle == INVALID_HANDLE_VALUE) { 130 err = GetLastError(); 131 } 132 133 // Free the security descriptor as it is no longer needed once 134 // CreateNamedPipeA() returns. 135 LocalFree(sa.lpSecurityDescriptor); 136 137 return err; 138 } 139 140 bool GetProcessPath(unsigned long pid, std::string* binary_path) { 141 HANDLE hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); 142 if (hProc == nullptr) { 143 return false; 144 } 145 146 char path[MAX_PATH]; 147 DWORD size = sizeof(path); 148 DWORD length = QueryFullProcessImageNameA(hProc, /*flags=*/0, path, &size); 149 CloseHandle(hProc); 150 if (length == 0) { 151 return false; 152 } 153 154 *binary_path = path; 155 return true; 156 } 157 158 ScopedOverlapped::ScopedOverlapped() { 159 memset(&overlapped_, 0, sizeof(overlapped_)); 160 overlapped_.hEvent = CreateEvent(/*securityAttr=*/nullptr, 161 /*manualReset=*/TRUE, 162 /*initialState=*/FALSE, 163 /*name=*/nullptr); 164 } 165 166 ScopedOverlapped::~ScopedOverlapped() { 167 if (overlapped_.hEvent != nullptr) { 168 CloseHandle(overlapped_.hEvent); 169 } 170 } 171 172 } // internal 173 } // namespace sdk 174 } // namespace content_analysis