indiaai-hackathon
/
datalab
/web
/node_modules
/node-pty
/deps
/winpty
/src
/shared
/WindowsSecurity.cc
// 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. | |
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); | |
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); | |
} | |