|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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 })); |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
static OwnedHandle openSecurityTokenForQuery() { |
|
HANDLE token = nullptr; |
|
|
|
|
|
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, |
|
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); |
|
} |
|
|
|
|
|
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, |
|
DWORD subAuthority1) { |
|
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() { |
|
|
|
SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY }; |
|
return wellKnownSid(L"BUILTIN\\Administrators group", |
|
authority, 2, |
|
SECURITY_BUILTIN_DOMAIN_RID, |
|
DOMAIN_ALIAS_RID_ADMINS); |
|
} |
|
|
|
Sid localSystemSid() { |
|
|
|
SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY }; |
|
return wellKnownSid(L"LocalSystem account", |
|
authority, 1, |
|
SECURITY_LOCAL_SYSTEM_RID); |
|
} |
|
|
|
Sid everyoneSid() { |
|
|
|
SID_IDENTIFIER_AUTHORITY authority = { SECURITY_WORLD_SID_AUTHORITY }; |
|
return wellKnownSid(L"Everyone account", |
|
authority, 1, |
|
SECURITY_WORLD_RID); |
|
} |
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
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()); |
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
DWORD rejectRemoteClientsPipeFlag() { |
|
if (isAtLeastWindowsVista()) { |
|
|
|
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); |
|
} |
|
|