1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 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
|