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); | |
| } | |