// Copyright 2022 The Chromium Authors. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include "utils_win.h" namespace content_analysis { namespace sdk { namespace internal { std::string GetUserSID() { std::string sid; HANDLE handle; if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &handle) && !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &handle)) { return std::string(); } DWORD length = 0; std::vector buffer; if (!GetTokenInformation(handle, TokenUser, nullptr, 0, &length)) { DWORD err = GetLastError(); if (err == ERROR_INSUFFICIENT_BUFFER) { buffer.resize(length); } } if (GetTokenInformation(handle, TokenUser, buffer.data(), buffer.size(), &length)) { TOKEN_USER* info = reinterpret_cast(buffer.data()); char* sid_string; if (ConvertSidToStringSidA(info->User.Sid, &sid_string)) { sid = sid_string; LocalFree(sid_string); } } CloseHandle(handle); return sid; } std::string BuildPipeName(const char* prefix, const std::string& base, bool user_specific) { std::string pipename = prefix; // If the agent is not user-specific, the assumption is that it runs with // administrator privileges. Create the pipe in a location only available // to administrators. if (!user_specific) pipename += "ProtectedPrefix\\Administrators\\"; pipename += base; if (user_specific) { std::string sid = GetUserSID(); if (sid.empty()) return std::string(); pipename += "." + sid; } return pipename; } std::string GetPipeNameForAgent(const std::string& base, bool user_specific) { return BuildPipeName(kPipePrefixForAgent, base, user_specific); } std::string GetPipeNameForClient(const std::string& base, bool user_specific) { return BuildPipeName(kPipePrefixForClient, base, user_specific); } DWORD CreatePipe( const std::string& name, bool user_specific, bool is_first_pipe, HANDLE* handle) { DWORD err = ERROR_SUCCESS; DWORD mode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED; // Create DACL for pipe to allow users on the local system to read and write // to the pipe. The creator and owner as well as administrator always get // full control of the pipe. // // If `user_specific` is true, a different agent instance is used for each // OS user, so only allow the interactive logged on user to reads and write // messages to the pipe. Otherwise only one agent instance used used for all // OS users and all authenticated logged on users can reads and write // messages. // // See https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-definition-language // for a description of this string format. constexpr char kDaclEveryone[] = "D:" "(A;OICI;GA;;;CO)" // Allow full control to creator owner. "(A;OICI;GA;;;BA)" // Allow full control to admins. "(A;OICI;GRGW;;;WD)"; // Allow read and write to everyone. constexpr char kDaclUserSpecific[] = "D:" "(A;OICI;GA;;;CO)" // Allow full control to creator owner. "(A;OICI;GA;;;BA)" // Allow full control to admins. "(A;OICI;GRGW;;;IU)"; // Allow read and write to interactive user. SECURITY_ATTRIBUTES sa; memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); sa.bInheritHandle = FALSE; if (!ConvertStringSecurityDescriptorToSecurityDescriptorA( user_specific ? kDaclUserSpecific : kDaclEveryone, SDDL_REVISION_1, &sa.lpSecurityDescriptor, /*outSdSize=*/nullptr)) { err = GetLastError(); return err; } // When `is_first_pipe` is true, the agent expects there is no process that // is currently listening on this pipe. If there is, CreateNamedPipeA() // returns with ERROR_ACCESS_DENIED. This is used to detect if another // process is listening for connections when there shouldn't be. if (is_first_pipe) { mode |= FILE_FLAG_FIRST_PIPE_INSTANCE; } *handle = CreateNamedPipeA(name.c_str(), mode, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, PIPE_UNLIMITED_INSTANCES, kBufferSize, kBufferSize, 0, &sa); if (*handle == INVALID_HANDLE_VALUE) { err = GetLastError(); } // Free the security descriptor as it is no longer needed once // CreateNamedPipeA() returns. LocalFree(sa.lpSecurityDescriptor); return err; } bool GetProcessPath(unsigned long pid, std::string* binary_path) { HANDLE hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); if (hProc == nullptr) { return false; } char path[MAX_PATH]; DWORD size = sizeof(path); DWORD length = QueryFullProcessImageNameA(hProc, /*flags=*/0, path, &size); CloseHandle(hProc); if (length == 0) { return false; } *binary_path = path; return true; } ScopedOverlapped::ScopedOverlapped() { memset(&overlapped_, 0, sizeof(overlapped_)); overlapped_.hEvent = CreateEvent(/*securityAttr=*/nullptr, /*manualReset=*/TRUE, /*initialState=*/FALSE, /*name=*/nullptr); } ScopedOverlapped::~ScopedOverlapped() { if (overlapped_.hEvent != nullptr) { CloseHandle(overlapped_.hEvent); } } } // internal } // namespace sdk } // namespace content_analysis