1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 //*****************************************************************************
5 // File: SecurityWrapper.cpp
9 // Wrapper around Win32 Security functions
11 //*****************************************************************************
15 #include "securitywrapper.h"
20 // For GetSidFromProcess*
25 //-----------------------------------------------------------------------------
26 // Constructor for Sid wrapper class.
27 // pSid - OS sid to wrap
28 //-----------------------------------------------------------------------------
31 _ASSERTE(pSid != NULL);
35 //-----------------------------------------------------------------------------
36 // Aesthetic wrapper for Sid equality
37 //-----------------------------------------------------------------------------
38 bool Sid::Equals(PSID a, PSID b)
40 return EqualSid(a, b) != 0;
43 //-----------------------------------------------------------------------------
44 // Ctor for SidBuffer class
45 //-----------------------------------------------------------------------------
46 SidBuffer::SidBuffer()
51 //-----------------------------------------------------------------------------
52 // Dtor for SidBuffer class.
53 //-----------------------------------------------------------------------------
54 SidBuffer::~SidBuffer()
59 //-----------------------------------------------------------------------------
60 // Get the underlying sid
61 // Caller assumes SidBuffer has been initialized.
62 //-----------------------------------------------------------------------------
63 Sid SidBuffer::GetSid()
65 _ASSERTE(m_pBuffer != NULL);
66 Sid s((PSID) m_pBuffer);
70 // ----------------------------------------------------------------------------
71 // Used by GetSidFromProcessWorker to determine which SID from the
72 // process token to use when initializing the SID
75 // Use TokenOwner: the default owner SID used for newly created objects
78 // Use TokenUser: the user account from the token
82 // ----------------------------------------------------------------------------
83 // GetSidFromProcessWorker
86 // Internal helper. Gets the SID for the given process and given sid type
89 // * dwProcessId - [in] Process to get SID from
90 // * sidType - [in] Type of sid to get (owner or user)
91 // * ppSid - [out] SID found. Caller responsible for deleting this memory.
94 // HRESULT indicating success / failure.
97 // * Caller owns deleting (*ppSid) when done with the SID
100 HRESULT GetSidFromProcessWorker(DWORD dwProcessId, SidType sidType, PSID *ppSid)
110 TOKEN_USER *pTokUser = NULL;
111 HANDLE hProc = INVALID_HANDLE_VALUE;
112 HANDLE hToken = INVALID_HANDLE_VALUE;
114 LPVOID pvTokenInfo = NULL;
115 TOKEN_INFORMATION_CLASS tokenInfoClass;
116 PSID pSidFromTokenInfo = NULL;
120 LOG((LF_CORDB, LL_INFO10000,
121 "SecurityUtil::GetSidFromProcess: 0x%08x\n",
127 _ASSERTE((sidType == kOwnerSid) || (sidType == kUserSid));
128 tokenInfoClass = (sidType == kOwnerSid) ? TokenOwner : TokenUser;
130 hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
134 hr = HRESULT_FROM_GetLastError();
137 if (!OpenProcessToken(hProc, TOKEN_QUERY, &hToken))
139 hr = HRESULT_FROM_GetLastError();
143 // figure out the length
144 GetTokenInformation(hToken, tokenInfoClass, NULL, 0, &dwRetLength);
145 _ASSERTE(dwRetLength);
147 pvTokenInfo = new (nothrow) BYTE[dwRetLength];
148 if (pvTokenInfo == NULL)
154 if (!GetTokenInformation(hToken, tokenInfoClass, pvTokenInfo, dwRetLength, &dwRetLength))
156 hr = HRESULT_FROM_GetLastError();
162 (sidType == kOwnerSid) ?
163 ((TOKEN_OWNER *) pvTokenInfo)->Owner :
164 ((TOKEN_USER *) pvTokenInfo)->User.Sid;
165 cbSid = GetLengthSid(pSidFromTokenInfo);
166 pSid = new (nothrow) BYTE[cbSid];
173 if (!CopySid(cbSid, pSid, pSidFromTokenInfo))
175 hr = HRESULT_FROM_GetLastError();
184 if (hToken != INVALID_HANDLE_VALUE)
188 if (hProc != INVALID_HANDLE_VALUE)
195 delete [] (reinterpret_cast<BYTE*>(pvTokenInfo));
200 delete [] (reinterpret_cast<BYTE*>(pSid));
203 LOG((LF_CORDB, LL_INFO10000,
204 "SecurityUtil::GetSidFromProcess return hr : 0x%08x\n",
210 #ifndef FEATURE_CORESYSTEM
211 //-----------------------------------------------------------------------------
212 // get the sid of a given process id using WTSEnumerateProcesses
213 // @todo: Make this function fail when WTSEnumerateProcesses is not available
214 // Or is it always available on all of our platform?
216 // Caller remember to call delete on *ppSid
217 //-----------------------------------------------------------------------------
218 HRESULT GetSidFromProcessEXWorker(DWORD dwProcessId, PSID *ppSid)
224 PRECONDITION(CheckPointer(ppSid));
229 PWTS_PROCESS_INFOW rgProcessInfo = NULL;
230 DWORD dwNumProcesses;
235 LOG((LF_CORDB, LL_INFO10000,
236 "SecurityUtil::GetSidFromProcessEx: 0x%08x\n",
241 if (!WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, // use local server
242 0, // Reserved must be zero
243 1, // version must be 1
244 &rgProcessInfo, // Receives pointer to process list
247 hr = HRESULT_FROM_GetLastError();
251 for (iProc = 0; iProc < dwNumProcesses; iProc++)
254 if (rgProcessInfo[iProc].ProcessId == dwProcessId)
256 if (rgProcessInfo[iProc].pUserSid == NULL)
258 LOG((LF_CORDB, LL_INFO10000,
259 "SecurityUtil::GetSidFromProcessEx is not able to retreive SID\n"));
261 // if there is no Sid for the user, don't call GetLengthSid.
262 // It will crash! It is ok to return E_FAIL as caller will ignore it.
266 cbSid = GetLengthSid(rgProcessInfo[iProc].pUserSid);
267 pSid = new (nothrow) BYTE[cbSid];
274 if (!CopySid(cbSid, pSid, rgProcessInfo[iProc].pUserSid))
276 hr = HRESULT_FROM_GetLastError();
280 // We are done. Go to exit
285 // we already find a match. Even if we fail from memory allocation of CopySid, still
291 // Walk the whole list and cannot find the matching PID
292 // Find a better error code.
299 WTSFreeMemory(rgProcessInfo);
302 if (FAILED(hr) && pSid)
304 delete [] (reinterpret_cast<BYTE*>(pSid));
312 LOG((LF_CORDB, LL_INFO10000,
313 "SecurityUtil::GetSidFromProcessEx return hr : 0x%08x\n",
319 #endif // !FEATURE_CORESYSTEM
321 //-----------------------------------------------------------------------------
322 // The functions below initialize this SidBuffer instance with a Sid from
323 // the token of the specified process. The first pair use the OWNER sid from
324 // the process token if possible; else use the term serv API to find the
325 // USER sid from the process token. This seems a little inconsistent, but
326 // remains this way for backward compatibility. The second pair consistently
327 // use the USER sid (never the OWNER).
329 // While the USER and OWNER sid are often the same, they are not always the
330 // same. For example, running a process on win2k3 server as a member of the
331 // local admin group causes the USER sid to be the logged-on user, and the
332 // OWNER sid to be the local admins group. At least, that's how it was on
333 // Monday. Expect this to change randomly at unexpected times, as most
334 // security-related behavior does.
335 //-----------------------------------------------------------------------------
338 // ----------------------------------------------------------------------------
339 // SidBuffer::InitFromProcessNoThrow
342 // Initialize this SidBuffer instance with a Sid from the token of the specified
343 // process. Use the OWNER sid from the process token if possible; else use the term
344 // serv API to find the USER sid from the process token. This seems a little
345 // inconsistent, but remains this way for backward compatibility.
348 // * pid - Process ID from which to grab the SID
351 // HRESULT indicating success / failure
354 HRESULT SidBuffer::InitFromProcessNoThrow(DWORD pid)
363 _ASSERTE(m_pBuffer == NULL);
364 HRESULT hr = GetSidFromProcessWorker(pid, kOwnerSid, (PSID *) &m_pBuffer);
365 #ifndef FEATURE_CORESYSTEM
368 hr = GetSidFromProcessEXWorker(pid, (PSID *) &m_pBuffer);
370 #endif // !FEATURE_CORESYSTEM
376 _ASSERTE(m_pBuffer != NULL);
380 // See code:SidBuffer::InitFromProcessNoThrow. Throws if there's an error.
381 void SidBuffer::InitFromProcess(DWORD pid)
390 HRESULT hr = InitFromProcessNoThrow(pid);
397 // ----------------------------------------------------------------------------
398 // SidBuffer::InitFromProcessAppContainerSidNoThrow
401 // Initialize this SidBuffer instance with the TokenAppContainerSid from
405 // * pid - Process ID from which to grab the SID
408 // HRESULT indicating success / failure
409 // S_FALSE indicates the process isn't in an AppContainer
411 HRESULT SidBuffer::InitFromProcessAppContainerSidNoThrow(DWORD pid)
414 HANDLE hToken = NULL;
415 BOOL fIsLowBox = FALSE;
417 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
418 if (hProcess == NULL)
420 hr = HRESULT_FROM_GetLastError();
423 if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
425 hr = HRESULT_FROM_GetLastError();
429 // Define new TOKEN_INFORMATION_CLASS/ TOKEN_APPCONTAINER_INFORMATION members for Win8 since they are not in the DevDiv copy of WinSDK yet
430 typedef enum _TOKEN_INFORMATION_CLASS_WIN8 {
431 TokenIsAppContainer = TokenLogonSid + 1,
434 } TOKEN_INFORMATION_CLASS_WIN8;
436 typedef struct _TOKEN_APPCONTAINER_INFORMATION
439 } TOKEN_APPCONTAINER_INFORMATION, *PTOKEN_APPCONTAINER_INFORMATION;
442 if (!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIsAppContainer, &fIsLowBox, sizeof(fIsLowBox), &size))
444 DWORD gle = GetLastError();
445 if (gle == ERROR_INVALID_PARAMETER || gle == ERROR_INVALID_FUNCTION)
447 hr = S_FALSE; // We are on an OS which doesn't understand LowBox
451 hr = HRESULT_FROM_WIN32(gle);
462 UCHAR PackSid[SECURITY_MAX_SID_SIZE + sizeof(TOKEN_APPCONTAINER_INFORMATION)];
463 if (!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenAppContainerSid, &PackSid, sizeof(PackSid), &size))
465 hr = HRESULT_FROM_GetLastError();
470 PTOKEN_APPCONTAINER_INFORMATION pTokPack = (PTOKEN_APPCONTAINER_INFORMATION)&PackSid;
471 PSID pLowBoxPackage = pTokPack->TokenPackage;
472 DWORD dwSidLen = GetLengthSid(pLowBoxPackage);
473 m_pBuffer = new (nothrow) BYTE[dwSidLen];
474 if (m_pBuffer == NULL)
481 if (!CopySid(dwSidLen, m_pBuffer, pLowBoxPackage))
483 hr = HRESULT_FROM_GetLastError();
492 if (hProcess != NULL)
494 CloseHandle(hProcess);
504 // ----------------------------------------------------------------------------
505 // SidBuffer::InitFromProcessUserNoThrow
508 // Initialize this SidBuffer instance with a Sid from the token of the specified
509 // process. Use the USER sid from the process token if possible; else use the term
510 // serv API to find the USER sid from the process token.
513 // * pid - Process ID from which to grab the SID
516 // HRESULT indicating success / failure
519 HRESULT SidBuffer::InitFromProcessUserNoThrow(DWORD pid)
528 _ASSERTE(m_pBuffer == NULL);
529 HRESULT hr = GetSidFromProcessWorker(pid, kUserSid, (PSID *) &m_pBuffer);
530 #ifndef FEATURE_CORESYSTEM
533 hr = GetSidFromProcessEXWorker(pid, (PSID *) &m_pBuffer);
535 #endif // !FEATURE_CORESYSTEM
541 _ASSERTE(m_pBuffer != NULL);
545 // See code:SidBuffer::InitFromProcessUserNoThrow. Throws if there's an error.
546 void SidBuffer::InitFromProcessUser(DWORD pid)
555 HRESULT hr = InitFromProcessUserNoThrow(pid);
562 //-----------------------------------------------------------------------------
563 // Ctor for Dacl class. Wraps a win32 dacl.
564 //-----------------------------------------------------------------------------
565 Dacl::Dacl(PACL pAcl)
570 //-----------------------------------------------------------------------------
571 // Get number of ACE (Access Control Entries) in this DACL.
572 //-----------------------------------------------------------------------------
573 SIZE_T Dacl::GetAceCount()
575 return (SIZE_T) m_acl->AceCount;
578 //-----------------------------------------------------------------------------
579 // Get Raw a ACE at the given index.
580 // Caller assumes index is valid (0 <= dwAceIndex < GetAceCount())
581 // Throws on error (which should only be if the index is out of bounds).
582 //-----------------------------------------------------------------------------
583 ACE_HEADER * Dacl::GetAce(SIZE_T dwAceIndex)
590 ACE_HEADER * pAce = NULL;
591 BOOL fOk = ::GetAce(m_acl, (DWORD) dwAceIndex, (LPVOID*) &pAce);
592 _ASSERTE(fOk == (pAce != NULL));
602 //-----------------------------------------------------------------------------
603 // Ctor for SecurityDescriptor
604 //-----------------------------------------------------------------------------
605 Win32SecurityDescriptor::Win32SecurityDescriptor()
610 //-----------------------------------------------------------------------------
611 // Dtor for security Descriptor.
612 //-----------------------------------------------------------------------------
613 Win32SecurityDescriptor::~Win32SecurityDescriptor()
615 delete [] ((BYTE*) m_pDesc);
620 //-----------------------------------------------------------------------------
621 // Get the dacl for this security descriptor.
622 //-----------------------------------------------------------------------------
623 Dacl Win32SecurityDescriptor::GetDacl()
630 _ASSERTE(m_pDesc != NULL);
636 if (GetSecurityDescriptorDacl(m_pDesc, &bPresent, &acl, &bDaclDefaulted) == 0)
642 // No dacl. We consider this an error because all of the objects we expect
643 // to see should be dacled. If it's not dacled, then it's a malicious user spoofing it.
644 ThrowHR(E_INVALIDARG);
651 //-----------------------------------------------------------------------------
652 // Get the owner from the security descriptor.
653 //-----------------------------------------------------------------------------
654 HRESULT Win32SecurityDescriptor::GetOwnerNoThrow( PSID* ppSid)
661 _ASSERTE(m_pDesc != NULL);
662 BOOL bOwnerDefaulted;
669 if (GetSecurityDescriptorOwner(m_pDesc, ppSid, &bOwnerDefaulted) == 0)
671 DWORD err = GetLastError();
672 return HRESULT_FROM_WIN32(err);
677 Sid Win32SecurityDescriptor::GetOwner()
685 HRESULT hr = GetOwnerNoThrow( &pSid );
695 //-----------------------------------------------------------------------------
696 // Initialize this instance of a SecurityDescriptor with the SD for the handle.
697 // The handle must have READ_CONTROL permissions to do this.
699 //-----------------------------------------------------------------------------
700 HRESULT Win32SecurityDescriptor::InitFromHandleNoThrow(HANDLE h)
707 _ASSERTE(m_pDesc == NULL); // only init once.
711 DWORD flags = OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
713 // Now get the creator's SID. First get the size of the array needed.
714 BOOL fOk = GetKernelObjectSecurity(h, flags, NULL, 0, &cbNeeded);
715 DWORD err = GetLastError();
717 // Caller should give us a handle for which this succeeds. First call will
718 // fail w/ InsufficientBuffer.
719 CONSISTENCY_CHECK_MSGF(fOk || (err == ERROR_INSUFFICIENT_BUFFER), ("Failed to get KernelSecurity for object handle=%p.Err=%d\n", h, err));
721 PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR) new(nothrow) BYTE[cbNeeded];
724 return E_OUTOFMEMORY;
727 if (GetKernelObjectSecurity(h, flags, pSD, cbNeeded, &cbNeeded) == 0)
729 // get last error and fail out.
730 err = GetLastError();
731 delete [] ((BYTE*) pSD);
732 return HRESULT_FROM_WIN32(err);
738 void Win32SecurityDescriptor::InitFromHandle(HANDLE h)
745 HRESULT hr = InitFromHandleNoThrow(h);