Lorenzob's picture
Upload folder using huggingface_hub
19605ab verified
raw
history blame
16.5 kB
// Copyright (c) 2016 Ryan Prichard
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "WindowsSecurity.h"
#include <array>
#include "DebugClient.h"
#include "OsModule.h"
#include "OwnedHandle.h"
#include "StringBuilder.h"
#include "WindowsVersion.h"
#include "WinptyAssert.h"
#include "WinptyException.h"
namespace {
struct LocalFreer {
void operator()(void *ptr) {
if (ptr != nullptr) {
LocalFree(reinterpret_cast<HLOCAL>(ptr));
}
}
};
typedef std::unique_ptr<void, LocalFreer> PointerLocal;
template <typename T>
SecurityItem<T> localItem(typename T::type v) {
typedef typename T::type P;
struct Impl : SecurityItem<T>::Impl {
P m_v;
Impl(P v) : m_v(v) {}
virtual ~Impl() {
LocalFree(reinterpret_cast<HLOCAL>(m_v));
}
};
return SecurityItem<T>(v, std::unique_ptr<Impl>(new Impl { v }));
}
Sid allocatedSid(PSID v) {
struct Impl : Sid::Impl {
PSID m_v;
Impl(PSID v) : m_v(v) {}
virtual ~Impl() {
if (m_v != nullptr) {
FreeSid(m_v);
}
}
};
return Sid(v, std::unique_ptr<Impl>(new Impl { v }));
}
} // anonymous namespace
// Returns a handle to the thread's effective security token. If the thread
// is impersonating another user, its token is returned, and otherwise, the
// process' security token is opened. The handle is opened with TOKEN_QUERY.
static OwnedHandle openSecurityTokenForQuery() {
HANDLE token = nullptr;
// It is unclear to me whether OpenAsSelf matters for winpty, or what the
// most appropriate value is.
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY,
/*OpenAsSelf=*/FALSE, &token)) {
if (GetLastError() != ERROR_NO_TOKEN) {
throwWindowsError(L"OpenThreadToken failed");
}
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
throwWindowsError(L"OpenProcessToken failed");
}
}
ASSERT(token != nullptr &&
"OpenThreadToken/OpenProcessToken token is NULL");
return OwnedHandle(token);
}
// Returns the TokenOwner of the thread's effective security token.
Sid getOwnerSid() {
struct Impl : Sid::Impl {
std::unique_ptr<char[]> buffer;
};
OwnedHandle token = openSecurityTokenForQuery();
DWORD actual = 0;
BOOL success;
success = GetTokenInformation(token.get(), TokenOwner,
nullptr, 0, &actual);
if (success) {
throwWinptyException(L"getOwnerSid: GetTokenInformation: "
L"expected ERROR_INSUFFICIENT_BUFFER");
} else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
throwWindowsError(L"getOwnerSid: GetTokenInformation: "
L"expected ERROR_INSUFFICIENT_BUFFER");
}
std::unique_ptr<Impl> impl(new Impl);
impl->buffer = std::unique_ptr<char[]>(new char[actual]);
success = GetTokenInformation(token.get(), TokenOwner,
impl->buffer.get(), actual, &actual);
if (!success) {
throwWindowsError(L"getOwnerSid: GetTokenInformation");
}
TOKEN_OWNER tmp;
ASSERT(actual >= sizeof(tmp));
std::copy(
impl->buffer.get(),
impl->buffer.get() + sizeof(tmp),
reinterpret_cast<char*>(&tmp));
return Sid(tmp.Owner, std::move(impl));
}
Sid wellKnownSid(
const wchar_t *debuggingName,
SID_IDENTIFIER_AUTHORITY authority,
BYTE authorityCount,
DWORD subAuthority0/*=0*/,
DWORD subAuthority1/*=0*/) {
PSID psid = nullptr;
if (!AllocateAndInitializeSid(&authority, authorityCount,
subAuthority0,
subAuthority1,
0, 0, 0, 0, 0, 0,
&psid)) {
const auto err = GetLastError();
const auto msg =
std::wstring(L"wellKnownSid: error getting ") +
debuggingName + L" SID";
throwWindowsError(msg.c_str(), err);
}
return allocatedSid(psid);
}
Sid builtinAdminsSid() {
// S-1-5-32-544
SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY };
return wellKnownSid(L"BUILTIN\\Administrators group",
authority, 2,
SECURITY_BUILTIN_DOMAIN_RID, // 32
DOMAIN_ALIAS_RID_ADMINS); // 544
}
Sid localSystemSid() {
// S-1-5-18
SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY };
return wellKnownSid(L"LocalSystem account",
authority, 1,
SECURITY_LOCAL_SYSTEM_RID); // 18
}
Sid everyoneSid() {
// S-1-1-0
SID_IDENTIFIER_AUTHORITY authority = { SECURITY_WORLD_SID_AUTHORITY };
return wellKnownSid(L"Everyone account",
authority, 1,
SECURITY_WORLD_RID); // 0
}
static SecurityDescriptor finishSecurityDescriptor(
size_t daclEntryCount,
EXPLICIT_ACCESSW *daclEntries,
Acl &outAcl) {
{
PACL aclRaw = nullptr;
DWORD aclError =
SetEntriesInAclW(daclEntryCount,
daclEntries,
nullptr, &aclRaw);
if (aclError != ERROR_SUCCESS) {
WStringBuilder sb(64);
sb << L"finishSecurityDescriptor: "
<< L"SetEntriesInAcl failed: " << aclError;
throwWinptyException(sb.c_str());
}
outAcl = localItem<AclTag>(aclRaw);
}
const PSECURITY_DESCRIPTOR sdRaw =
reinterpret_cast<PSECURITY_DESCRIPTOR>(
LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH));
if (sdRaw == nullptr) {
throwWinptyException(L"finishSecurityDescriptor: LocalAlloc failed");
}
SecurityDescriptor sd = localItem<SecurityDescriptorTag>(sdRaw);
if (!InitializeSecurityDescriptor(sdRaw, SECURITY_DESCRIPTOR_REVISION)) {
throwWindowsError(
L"finishSecurityDescriptor: InitializeSecurityDescriptor");
}
if (!SetSecurityDescriptorDacl(sdRaw, TRUE, outAcl.get(), FALSE)) {
throwWindowsError(
L"finishSecurityDescriptor: SetSecurityDescriptorDacl");
}
return std::move(sd);
}
// Create a security descriptor that grants full control to the local system
// account, built-in administrators, and the owner.
SecurityDescriptor
createPipeSecurityDescriptorOwnerFullControl() {
struct Impl : SecurityDescriptor::Impl {
Sid localSystem;
Sid builtinAdmins;
Sid owner;
std::array<EXPLICIT_ACCESSW, 3> daclEntries = {};
Acl dacl;
SecurityDescriptor value;
};
std::unique_ptr<Impl> impl(new Impl);
impl->localSystem = localSystemSid();
impl->builtinAdmins = builtinAdminsSid();
impl->owner = getOwnerSid();
for (auto &ea : impl->daclEntries) {
ea.grfAccessPermissions = GENERIC_ALL;
ea.grfAccessMode = SET_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
}
impl->daclEntries[0].Trustee.ptstrName =
reinterpret_cast<LPWSTR>(impl->localSystem.get());
impl->daclEntries[1].Trustee.ptstrName =
reinterpret_cast<LPWSTR>(impl->builtinAdmins.get());
impl->daclEntries[2].Trustee.ptstrName =
reinterpret_cast<LPWSTR>(impl->owner.get());
impl->value = finishSecurityDescriptor(
impl->daclEntries.size(),
impl->daclEntries.data(),
impl->dacl);
const auto retValue = impl->value.get();
return SecurityDescriptor(retValue, std::move(impl));
}
SecurityDescriptor
createPipeSecurityDescriptorOwnerFullControlEveryoneWrite() {
struct Impl : SecurityDescriptor::Impl {
Sid localSystem;
Sid builtinAdmins;
Sid owner;
Sid everyone;
std::array<EXPLICIT_ACCESSW, 4> daclEntries = {};
Acl dacl;
SecurityDescriptor value;
};
std::unique_ptr<Impl> impl(new Impl);
impl->localSystem = localSystemSid();
impl->builtinAdmins = builtinAdminsSid();
impl->owner = getOwnerSid();
impl->everyone = everyoneSid();
for (auto &ea : impl->daclEntries) {
ea.grfAccessPermissions = GENERIC_ALL;
ea.grfAccessMode = SET_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
}
impl->daclEntries[0].Trustee.ptstrName =
reinterpret_cast<LPWSTR>(impl->localSystem.get());
impl->daclEntries[1].Trustee.ptstrName =
reinterpret_cast<LPWSTR>(impl->builtinAdmins.get());
impl->daclEntries[2].Trustee.ptstrName =
reinterpret_cast<LPWSTR>(impl->owner.get());
impl->daclEntries[3].Trustee.ptstrName =
reinterpret_cast<LPWSTR>(impl->everyone.get());
// Avoid using FILE_GENERIC_WRITE because it includes FILE_APPEND_DATA,
// which is equal to FILE_CREATE_PIPE_INSTANCE. Instead, include all the
// flags that comprise FILE_GENERIC_WRITE, except for the one.
impl->daclEntries[3].grfAccessPermissions =
FILE_GENERIC_READ |
FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | FILE_WRITE_EA |
STANDARD_RIGHTS_WRITE | SYNCHRONIZE;
impl->value = finishSecurityDescriptor(
impl->daclEntries.size(),
impl->daclEntries.data(),
impl->dacl);
const auto retValue = impl->value.get();
return SecurityDescriptor(retValue, std::move(impl));
}
SecurityDescriptor getObjectSecurityDescriptor(HANDLE handle) {
PACL dacl = nullptr;
PSECURITY_DESCRIPTOR sd = nullptr;
const DWORD errCode = GetSecurityInfo(handle, SE_KERNEL_OBJECT,
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION,
nullptr, nullptr, &dacl, nullptr, &sd);
if (errCode != ERROR_SUCCESS) {
throwWindowsError(L"GetSecurityInfo failed");
}
return localItem<SecurityDescriptorTag>(sd);
}
// The (SID/SD)<->string conversion APIs are useful for testing/debugging, so
// create convenient accessor functions for them. They're too slow for
// ordinary use. The APIs exist in XP and up, but the MinGW headers only
// declare the SID<->string APIs, not the SD APIs. MinGW also gets the
// prototype wrong for ConvertStringSidToSidW (LPWSTR instead of LPCWSTR) and
// requires WINVER to be defined. MSVC and MinGW-w64 get everything right, but
// for consistency, use LoadLibrary/GetProcAddress for all four APIs.
typedef BOOL WINAPI ConvertStringSidToSidW_t(
LPCWSTR StringSid,
PSID *Sid);
typedef BOOL WINAPI ConvertSidToStringSidW_t(
PSID Sid,
LPWSTR *StringSid);
typedef BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW_t(
LPCWSTR StringSecurityDescriptor,
DWORD StringSDRevision,
PSECURITY_DESCRIPTOR *SecurityDescriptor,
PULONG SecurityDescriptorSize);
typedef BOOL WINAPI ConvertSecurityDescriptorToStringSecurityDescriptorW_t(
PSECURITY_DESCRIPTOR SecurityDescriptor,
DWORD RequestedStringSDRevision,
SECURITY_INFORMATION SecurityInformation,
LPWSTR *StringSecurityDescriptor,
PULONG StringSecurityDescriptorLen);
#define GET_MODULE_PROC(mod, funcName) \
const auto p##funcName = \
reinterpret_cast<funcName##_t*>( \
mod.proc(#funcName)); \
if (p##funcName == nullptr) { \
throwWinptyException( \
L"" L ## #funcName L" API is missing from ADVAPI32.DLL"); \
}
const DWORD kSDDL_REVISION_1 = 1;
std::wstring sidToString(PSID sid) {
OsModule advapi32(L"advapi32.dll");
GET_MODULE_PROC(advapi32, ConvertSidToStringSidW);
wchar_t *sidString = NULL;
BOOL success = pConvertSidToStringSidW(sid, &sidString);
if (!success) {
throwWindowsError(L"ConvertSidToStringSidW failed");
}
PointerLocal freer(sidString);
return std::wstring(sidString);
}
Sid stringToSid(const std::wstring &str) {
// Cast the string from const wchar_t* to LPWSTR because the function is
// incorrectly prototyped in the MinGW sddl.h header. The API does not
// modify the string -- it is correctly prototyped as taking LPCWSTR in
// MinGW-w64, MSVC, and MSDN.
OsModule advapi32(L"advapi32.dll");
GET_MODULE_PROC(advapi32, ConvertStringSidToSidW);
PSID psid = nullptr;
BOOL success = pConvertStringSidToSidW(const_cast<LPWSTR>(str.c_str()),
&psid);
if (!success) {
const auto err = GetLastError();
throwWindowsError(
(std::wstring(L"ConvertStringSidToSidW failed on \"") +
str + L'"').c_str(),
err);
}
return localItem<SidTag>(psid);
}
SecurityDescriptor stringToSd(const std::wstring &str) {
OsModule advapi32(L"advapi32.dll");
GET_MODULE_PROC(advapi32, ConvertStringSecurityDescriptorToSecurityDescriptorW);
PSECURITY_DESCRIPTOR desc = nullptr;
if (!pConvertStringSecurityDescriptorToSecurityDescriptorW(
str.c_str(), kSDDL_REVISION_1, &desc, nullptr)) {
const auto err = GetLastError();
throwWindowsError(
(std::wstring(L"ConvertStringSecurityDescriptorToSecurityDescriptorW failed on \"") +
str + L'"').c_str(),
err);
}
return localItem<SecurityDescriptorTag>(desc);
}
std::wstring sdToString(PSECURITY_DESCRIPTOR sd) {
OsModule advapi32(L"advapi32.dll");
GET_MODULE_PROC(advapi32, ConvertSecurityDescriptorToStringSecurityDescriptorW);
wchar_t *sdString = nullptr;
if (!pConvertSecurityDescriptorToStringSecurityDescriptorW(
sd,
kSDDL_REVISION_1,
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION,
&sdString,
nullptr)) {
throwWindowsError(
L"ConvertSecurityDescriptorToStringSecurityDescriptor failed");
}
PointerLocal freer(sdString);
return std::wstring(sdString);
}
// Vista added a useful flag to CreateNamedPipe, PIPE_REJECT_REMOTE_CLIENTS,
// that rejects remote connections. Return this flag on Vista, or return 0
// otherwise.
DWORD rejectRemoteClientsPipeFlag() {
if (isAtLeastWindowsVista()) {
// MinGW lacks this flag; MinGW-w64 has it.
const DWORD kPIPE_REJECT_REMOTE_CLIENTS = 8;
return kPIPE_REJECT_REMOTE_CLIENTS;
} else {
trace("Omitting PIPE_REJECT_REMOTE_CLIENTS on pre-Vista OS");
return 0;
}
}
typedef BOOL WINAPI GetNamedPipeClientProcessId_t(
HANDLE Pipe,
PULONG ClientProcessId);
std::tuple<GetNamedPipeClientProcessId_Result, DWORD, DWORD>
getNamedPipeClientProcessId(HANDLE serverPipe) {
OsModule kernel32(L"kernel32.dll");
const auto pGetNamedPipeClientProcessId =
reinterpret_cast<GetNamedPipeClientProcessId_t*>(
kernel32.proc("GetNamedPipeClientProcessId"));
if (pGetNamedPipeClientProcessId == nullptr) {
return std::make_tuple(
GetNamedPipeClientProcessId_Result::UnsupportedOs, 0, 0);
}
ULONG pid = 0;
if (!pGetNamedPipeClientProcessId(serverPipe, &pid)) {
return std::make_tuple(
GetNamedPipeClientProcessId_Result::Failure, 0, GetLastError());
}
return std::make_tuple(
GetNamedPipeClientProcessId_Result::Success,
static_cast<DWORD>(pid),
0);
}