diff options
Diffstat (limited to 'third_party/content_analysis_sdk/common')
-rw-r--r-- | third_party/content_analysis_sdk/common/utils_win.cc | 174 | ||||
-rw-r--r-- | third_party/content_analysis_sdk/common/utils_win.h | 76 |
2 files changed, 250 insertions, 0 deletions
diff --git a/third_party/content_analysis_sdk/common/utils_win.cc b/third_party/content_analysis_sdk/common/utils_win.cc new file mode 100644 index 0000000000..a87c2d9e02 --- /dev/null +++ b/third_party/content_analysis_sdk/common/utils_win.cc @@ -0,0 +1,174 @@ +// 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 <vector> + +#include <windows.h> +#include <sddl.h> + +#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<char> 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<TOKEN_USER*>(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 diff --git a/third_party/content_analysis_sdk/common/utils_win.h b/third_party/content_analysis_sdk/common/utils_win.h new file mode 100644 index 0000000000..93b9b379f2 --- /dev/null +++ b/third_party/content_analysis_sdk/common/utils_win.h @@ -0,0 +1,76 @@ +// 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. + +// Utility and helper functions common to both the agent and browser code. +// This code is not publicly exposed from the SDK. + +#ifndef CONTENT_ANALYSIS_COMMON_UTILS_WIN_H_ +#define CONTENT_ANALYSIS_COMMON_UTILS_WIN_H_ + +#include <string> + +namespace content_analysis { +namespace sdk { +namespace internal { + +// The default size of the buffer used to hold messages received from +// Google Chrome. +const DWORD kBufferSize = 4096; + +// Named pipe prefixes used for agent and client side of pipe. +constexpr char kPipePrefixForAgent[] = R"(\\.\\pipe\)"; +constexpr char kPipePrefixForClient[] = R"(\Device\NamedPipe\)"; + +// Returns the user SID of the thread or process that calls thie function. +// Returns an empty string on error. +std::string GetUserSID(); + +// Returns the name of the pipe that should be used to communicate between +// the agent and Google Chrome. If `sid` is non-empty, make the pip name +// specific to that user. +// +// GetPipeNameForAgent() is meant to be used in the agent. The returned +// path can be used with CreatePipe() below. GetPipeNameForClient() is meant +// to be used in the client. The returned path can only be used with +// NtCreateFile() and not CreateFile(). +std::string GetPipeNameForAgent(const std::string& base, bool user_specific); +std::string GetPipeNameForClient(const std::string& base, bool user_specific); + +// Creates a named pipe with the give name. If `is_first_pipe` is true, +// fail if this is not the first pipe using this name. +// +// This function create a pipe whose DACL allow full control to the creator +// owner and administrators. If `user_specific` the DACL only allows the +// logged on user to read from and write to the pipe. Otherwise anyone logged +// in can read from and write to the pipe. +// +// A handle to the pipe is retuned in `handle`. +DWORD CreatePipe(const std::string& name, + bool user_specific, + bool is_first_pipe, + HANDLE* handle); + +// Returns the full path to the main binary file of the process with the given +// process ID. +bool GetProcessPath(unsigned long pid, std::string* binary_path); + +// A class that scopes the creation and destruction of an OVERLAPPED structure +// used for async IO. +class ScopedOverlapped { + public: + ScopedOverlapped(); + ~ScopedOverlapped(); + + bool is_valid() { return overlapped_.hEvent != nullptr; } + operator OVERLAPPED*() { return &overlapped_; } + + private: + OVERLAPPED overlapped_; +}; + +} // internal +} // namespace sdk +} // namespace content_analysis + +#endif // CONTENT_ANALYSIS_COMMON_UTILS_WIN_H_
\ No newline at end of file |