summaryrefslogtreecommitdiffstats
path: root/third_party/content_analysis_sdk/common
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/content_analysis_sdk/common')
-rw-r--r--third_party/content_analysis_sdk/common/utils_win.cc174
-rw-r--r--third_party/content_analysis_sdk/common/utils_win.h76
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