1 // Copyright (c) 2016 Ryan Prichard
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to
5 // deal in the Software without restriction, including without limitation the
6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 // sell copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 #include "WindowsSecurity.h"
25 #include "DebugClient.h"
27 #include "OwnedHandle.h"
28 #include "StringBuilder.h"
29 #include "WindowsVersion.h"
30 #include "WinptyAssert.h"
31 #include "WinptyException.h"
36 void operator()(void *ptr) {
38 LocalFree(reinterpret_cast<HLOCAL>(ptr));
43 typedef std::unique_ptr<void, LocalFreer> PointerLocal;
46 SecurityItem<T> localItem(typename T::type v) {
47 typedef typename T::type P;
48 struct Impl : SecurityItem<T>::Impl {
52 LocalFree(reinterpret_cast<HLOCAL>(m_v));
55 return SecurityItem<T>(v, std::unique_ptr<Impl>(new Impl { v }));
58 Sid allocatedSid(PSID v) {
59 struct Impl : Sid::Impl {
61 Impl(PSID v) : m_v(v) {}
68 return Sid(v, std::unique_ptr<Impl>(new Impl { v }));
71 } // anonymous namespace
73 // Returns a handle to the thread's effective security token. If the thread
74 // is impersonating another user, its token is returned, and otherwise, the
75 // process' security token is opened. The handle is opened with TOKEN_QUERY.
76 static OwnedHandle openSecurityTokenForQuery() {
77 HANDLE token = nullptr;
78 // It is unclear to me whether OpenAsSelf matters for winpty, or what the
79 // most appropriate value is.
80 if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY,
81 /*OpenAsSelf=*/FALSE, &token)) {
82 if (GetLastError() != ERROR_NO_TOKEN) {
83 throwWindowsError(L"OpenThreadToken failed");
85 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
86 throwWindowsError(L"OpenProcessToken failed");
89 ASSERT(token != nullptr &&
90 "OpenThreadToken/OpenProcessToken token is NULL");
91 return OwnedHandle(token);
94 // Returns the TokenOwner of the thread's effective security token.
96 struct Impl : Sid::Impl {
97 std::unique_ptr<char[]> buffer;
100 OwnedHandle token = openSecurityTokenForQuery();
103 success = GetTokenInformation(token.get(), TokenOwner,
104 nullptr, 0, &actual);
106 throwWinptyException(L"getOwnerSid: GetTokenInformation: "
107 L"expected ERROR_INSUFFICIENT_BUFFER");
108 } else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
109 throwWindowsError(L"getOwnerSid: GetTokenInformation: "
110 L"expected ERROR_INSUFFICIENT_BUFFER");
112 std::unique_ptr<Impl> impl(new Impl);
113 impl->buffer = std::unique_ptr<char[]>(new char[actual]);
114 success = GetTokenInformation(token.get(), TokenOwner,
115 impl->buffer.get(), actual, &actual);
117 throwWindowsError(L"getOwnerSid: GetTokenInformation");
120 ASSERT(actual >= sizeof(tmp));
123 impl->buffer.get() + sizeof(tmp),
124 reinterpret_cast<char*>(&tmp));
125 return Sid(tmp.Owner, std::move(impl));
129 const wchar_t *debuggingName,
130 SID_IDENTIFIER_AUTHORITY authority,
132 DWORD subAuthority0/*=0*/,
133 DWORD subAuthority1/*=0*/) {
135 if (!AllocateAndInitializeSid(&authority, authorityCount,
140 const auto err = GetLastError();
142 std::wstring(L"wellKnownSid: error getting ") +
143 debuggingName + L" SID";
144 throwWindowsError(msg.c_str(), err);
146 return allocatedSid(psid);
149 Sid builtinAdminsSid() {
151 SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY };
152 return wellKnownSid(L"BUILTIN\\Administrators group",
154 SECURITY_BUILTIN_DOMAIN_RID, // 32
155 DOMAIN_ALIAS_RID_ADMINS); // 544
158 Sid localSystemSid() {
160 SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY };
161 return wellKnownSid(L"LocalSystem account",
163 SECURITY_LOCAL_SYSTEM_RID); // 18
168 SID_IDENTIFIER_AUTHORITY authority = { SECURITY_WORLD_SID_AUTHORITY };
169 return wellKnownSid(L"Everyone account",
171 SECURITY_WORLD_RID); // 0
174 static SecurityDescriptor finishSecurityDescriptor(
175 size_t daclEntryCount,
176 EXPLICIT_ACCESSW *daclEntries,
179 PACL aclRaw = nullptr;
181 SetEntriesInAclW(daclEntryCount,
184 if (aclError != ERROR_SUCCESS) {
185 WStringBuilder sb(64);
186 sb << L"finishSecurityDescriptor: "
187 << L"SetEntriesInAcl failed: " << aclError;
188 throwWinptyException(sb.c_str());
190 outAcl = localItem<AclTag>(aclRaw);
193 const PSECURITY_DESCRIPTOR sdRaw =
194 reinterpret_cast<PSECURITY_DESCRIPTOR>(
195 LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH));
196 if (sdRaw == nullptr) {
197 throwWinptyException(L"finishSecurityDescriptor: LocalAlloc failed");
199 SecurityDescriptor sd = localItem<SecurityDescriptorTag>(sdRaw);
200 if (!InitializeSecurityDescriptor(sdRaw, SECURITY_DESCRIPTOR_REVISION)) {
202 L"finishSecurityDescriptor: InitializeSecurityDescriptor");
204 if (!SetSecurityDescriptorDacl(sdRaw, TRUE, outAcl.get(), FALSE)) {
206 L"finishSecurityDescriptor: SetSecurityDescriptorDacl");
209 return std::move(sd);
212 // Create a security descriptor that grants full control to the local system
213 // account, built-in administrators, and the owner.
215 createPipeSecurityDescriptorOwnerFullControl() {
217 struct Impl : SecurityDescriptor::Impl {
221 std::array<EXPLICIT_ACCESSW, 3> daclEntries = {};
223 SecurityDescriptor value;
226 std::unique_ptr<Impl> impl(new Impl);
227 impl->localSystem = localSystemSid();
228 impl->builtinAdmins = builtinAdminsSid();
229 impl->owner = getOwnerSid();
231 for (auto &ea : impl->daclEntries) {
232 ea.grfAccessPermissions = GENERIC_ALL;
233 ea.grfAccessMode = SET_ACCESS;
234 ea.grfInheritance = NO_INHERITANCE;
235 ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
237 impl->daclEntries[0].Trustee.ptstrName =
238 reinterpret_cast<LPWSTR>(impl->localSystem.get());
239 impl->daclEntries[1].Trustee.ptstrName =
240 reinterpret_cast<LPWSTR>(impl->builtinAdmins.get());
241 impl->daclEntries[2].Trustee.ptstrName =
242 reinterpret_cast<LPWSTR>(impl->owner.get());
244 impl->value = finishSecurityDescriptor(
245 impl->daclEntries.size(),
246 impl->daclEntries.data(),
249 const auto retValue = impl->value.get();
250 return SecurityDescriptor(retValue, std::move(impl));
254 createPipeSecurityDescriptorOwnerFullControlEveryoneWrite() {
256 struct Impl : SecurityDescriptor::Impl {
261 std::array<EXPLICIT_ACCESSW, 4> daclEntries = {};
263 SecurityDescriptor value;
266 std::unique_ptr<Impl> impl(new Impl);
267 impl->localSystem = localSystemSid();
268 impl->builtinAdmins = builtinAdminsSid();
269 impl->owner = getOwnerSid();
270 impl->everyone = everyoneSid();
272 for (auto &ea : impl->daclEntries) {
273 ea.grfAccessPermissions = GENERIC_ALL;
274 ea.grfAccessMode = SET_ACCESS;
275 ea.grfInheritance = NO_INHERITANCE;
276 ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
278 impl->daclEntries[0].Trustee.ptstrName =
279 reinterpret_cast<LPWSTR>(impl->localSystem.get());
280 impl->daclEntries[1].Trustee.ptstrName =
281 reinterpret_cast<LPWSTR>(impl->builtinAdmins.get());
282 impl->daclEntries[2].Trustee.ptstrName =
283 reinterpret_cast<LPWSTR>(impl->owner.get());
284 impl->daclEntries[3].Trustee.ptstrName =
285 reinterpret_cast<LPWSTR>(impl->everyone.get());
286 // Avoid using FILE_GENERIC_WRITE because it includes FILE_APPEND_DATA,
287 // which is equal to FILE_CREATE_PIPE_INSTANCE. Instead, include all the
288 // flags that comprise FILE_GENERIC_WRITE, except for the one.
289 impl->daclEntries[3].grfAccessPermissions =
291 FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | FILE_WRITE_EA |
292 STANDARD_RIGHTS_WRITE | SYNCHRONIZE;
294 impl->value = finishSecurityDescriptor(
295 impl->daclEntries.size(),
296 impl->daclEntries.data(),
299 const auto retValue = impl->value.get();
300 return SecurityDescriptor(retValue, std::move(impl));
303 SecurityDescriptor getObjectSecurityDescriptor(HANDLE handle) {
305 PSECURITY_DESCRIPTOR sd = nullptr;
306 const DWORD errCode = GetSecurityInfo(handle, SE_KERNEL_OBJECT,
307 OWNER_SECURITY_INFORMATION |
308 GROUP_SECURITY_INFORMATION |
309 DACL_SECURITY_INFORMATION,
310 nullptr, nullptr, &dacl, nullptr, &sd);
311 if (errCode != ERROR_SUCCESS) {
312 throwWindowsError(L"GetSecurityInfo failed");
314 return localItem<SecurityDescriptorTag>(sd);
317 // The (SID/SD)<->string conversion APIs are useful for testing/debugging, so
318 // create convenient accessor functions for them. They're too slow for
319 // ordinary use. The APIs exist in XP and up, but the MinGW headers only
320 // declare the SID<->string APIs, not the SD APIs. MinGW also gets the
321 // prototype wrong for ConvertStringSidToSidW (LPWSTR instead of LPCWSTR) and
322 // requires WINVER to be defined. MSVC and MinGW-w64 get everything right, but
323 // for consistency, use LoadLibrary/GetProcAddress for all four APIs.
325 typedef BOOL WINAPI ConvertStringSidToSidW_t(
329 typedef BOOL WINAPI ConvertSidToStringSidW_t(
333 typedef BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW_t(
334 LPCWSTR StringSecurityDescriptor,
335 DWORD StringSDRevision,
336 PSECURITY_DESCRIPTOR *SecurityDescriptor,
337 PULONG SecurityDescriptorSize);
339 typedef BOOL WINAPI ConvertSecurityDescriptorToStringSecurityDescriptorW_t(
340 PSECURITY_DESCRIPTOR SecurityDescriptor,
341 DWORD RequestedStringSDRevision,
342 SECURITY_INFORMATION SecurityInformation,
343 LPWSTR *StringSecurityDescriptor,
344 PULONG StringSecurityDescriptorLen);
346 #define GET_MODULE_PROC(mod, funcName) \
347 const auto p##funcName = \
348 reinterpret_cast<funcName##_t*>( \
349 mod.proc(#funcName)); \
350 if (p##funcName == nullptr) { \
351 throwWinptyException( \
352 L"" L ## #funcName L" API is missing from ADVAPI32.DLL"); \
355 const DWORD kSDDL_REVISION_1 = 1;
357 std::wstring sidToString(PSID sid) {
358 OsModule advapi32(L"advapi32.dll");
359 GET_MODULE_PROC(advapi32, ConvertSidToStringSidW);
360 wchar_t *sidString = NULL;
361 BOOL success = pConvertSidToStringSidW(sid, &sidString);
363 throwWindowsError(L"ConvertSidToStringSidW failed");
365 PointerLocal freer(sidString);
366 return std::wstring(sidString);
369 Sid stringToSid(const std::wstring &str) {
370 // Cast the string from const wchar_t* to LPWSTR because the function is
371 // incorrectly prototyped in the MinGW sddl.h header. The API does not
372 // modify the string -- it is correctly prototyped as taking LPCWSTR in
373 // MinGW-w64, MSVC, and MSDN.
374 OsModule advapi32(L"advapi32.dll");
375 GET_MODULE_PROC(advapi32, ConvertStringSidToSidW);
377 BOOL success = pConvertStringSidToSidW(const_cast<LPWSTR>(str.c_str()),
380 const auto err = GetLastError();
382 (std::wstring(L"ConvertStringSidToSidW failed on \"") +
386 return localItem<SidTag>(psid);
389 SecurityDescriptor stringToSd(const std::wstring &str) {
390 OsModule advapi32(L"advapi32.dll");
391 GET_MODULE_PROC(advapi32, ConvertStringSecurityDescriptorToSecurityDescriptorW);
392 PSECURITY_DESCRIPTOR desc = nullptr;
393 if (!pConvertStringSecurityDescriptorToSecurityDescriptorW(
394 str.c_str(), kSDDL_REVISION_1, &desc, nullptr)) {
395 const auto err = GetLastError();
397 (std::wstring(L"ConvertStringSecurityDescriptorToSecurityDescriptorW failed on \"") +
401 return localItem<SecurityDescriptorTag>(desc);
404 std::wstring sdToString(PSECURITY_DESCRIPTOR sd) {
405 OsModule advapi32(L"advapi32.dll");
406 GET_MODULE_PROC(advapi32, ConvertSecurityDescriptorToStringSecurityDescriptorW);
407 wchar_t *sdString = nullptr;
408 if (!pConvertSecurityDescriptorToStringSecurityDescriptorW(
411 OWNER_SECURITY_INFORMATION |
412 GROUP_SECURITY_INFORMATION |
413 DACL_SECURITY_INFORMATION,
417 L"ConvertSecurityDescriptorToStringSecurityDescriptor failed");
419 PointerLocal freer(sdString);
420 return std::wstring(sdString);
423 // Vista added a useful flag to CreateNamedPipe, PIPE_REJECT_REMOTE_CLIENTS,
424 // that rejects remote connections. Return this flag on Vista, or return 0
426 DWORD rejectRemoteClientsPipeFlag() {
427 if (isAtLeastWindowsVista()) {
428 // MinGW lacks this flag; MinGW-w64 has it.
429 const DWORD kPIPE_REJECT_REMOTE_CLIENTS = 8;
430 return kPIPE_REJECT_REMOTE_CLIENTS;
432 trace("Omitting PIPE_REJECT_REMOTE_CLIENTS on pre-Vista OS");
437 typedef BOOL WINAPI GetNamedPipeClientProcessId_t(
439 PULONG ClientProcessId);
441 std::tuple<GetNamedPipeClientProcessId_Result, DWORD, DWORD>
442 getNamedPipeClientProcessId(HANDLE serverPipe) {
443 OsModule kernel32(L"kernel32.dll");
444 const auto pGetNamedPipeClientProcessId =
445 reinterpret_cast<GetNamedPipeClientProcessId_t*>(
446 kernel32.proc("GetNamedPipeClientProcessId"));
447 if (pGetNamedPipeClientProcessId == nullptr) {
448 return std::make_tuple(
449 GetNamedPipeClientProcessId_Result::UnsupportedOs, 0, 0);
452 if (!pGetNamedPipeClientProcessId(serverPipe, &pid)) {
453 return std::make_tuple(
454 GetNamedPipeClientProcessId_Result::Failure, 0, GetLastError());
456 return std::make_tuple(
457 GetNamedPipeClientProcessId_Result::Success,
458 static_cast<DWORD>(pid),