installed pty
[VSoRC/.git] / node_modules / node-pty / deps / winpty / src / shared / WindowsSecurity.cc
diff --git a/node_modules/node-pty/deps/winpty/src/shared/WindowsSecurity.cc b/node_modules/node-pty/deps/winpty/src/shared/WindowsSecurity.cc
new file mode 100644 (file)
index 0000000..711a863
--- /dev/null
@@ -0,0 +1,460 @@
+// 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);
+}