summaryrefslogtreecommitdiff
path: root/src/utilcode/securityutil.cpp
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
committerdotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
commitef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch)
treedee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/utilcode/securityutil.cpp
downloadcoreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/utilcode/securityutil.cpp')
-rw-r--r--src/utilcode/securityutil.cpp509
1 files changed, 509 insertions, 0 deletions
diff --git a/src/utilcode/securityutil.cpp b/src/utilcode/securityutil.cpp
new file mode 100644
index 0000000000..22b99735ba
--- /dev/null
+++ b/src/utilcode/securityutil.cpp
@@ -0,0 +1,509 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+
+#include "stdafx.h"
+
+#include "securityutil.h"
+#include "ex.h"
+
+#include "securitywrapper.h"
+
+// This is defined in ipcmanagerinterface.h, but including that is problematic at the moment...
+// These are the right that we will give to the global section and global events used
+// in communicating between debugger and debugee
+//
+// SECTION_ALL_ACCESS is needed for the IPC block. Unfortunately, we DACL our events and
+// IPC block identically. Or this particular right does not need to bleed into here.
+//
+#ifndef CLR_IPC_GENERIC_RIGHT
+#define CLR_IPC_GENERIC_RIGHT (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | STANDARD_RIGHTS_ALL | SECTION_ALL_ACCESS)
+#endif
+
+
+//*****************************************************************
+// static helper function
+//
+// helper to form ACL that contains AllowedACE of users of current
+// process and target process
+//
+// [IN] pid - target process id
+// [OUT] ppACL - ACL for the process
+//
+// Clean up -
+// Caller remember to call FreeACL() on *ppACL
+//*****************************************************************
+HRESULT SecurityUtil::GetACLOfPid(DWORD pid, PACL *ppACL)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ _ASSERTE(ppACL);
+ *ppACL = NULL;
+
+ PSID pCurrentProcessSid = NULL;
+ PSID pTargetProcessSid = NULL;
+ PSID pTargetProcessAppContainerSid = NULL;
+ DWORD cSid = 0;
+ DWORD dwAclSize = 0;
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetACLOfPid on pid : 0x%08x\n",
+ pid));
+
+
+ SidBuffer sidCurrentProcess;
+ SidBuffer sidTargetProcess;
+ SidBuffer sidTargetProcessAppContainer;
+
+ // Get sid for current process.
+ EX_TRY
+ {
+ sidCurrentProcess.InitFromProcess(GetCurrentProcessId()); // throw on error.
+ pCurrentProcessSid = sidCurrentProcess.GetSid().RawSid();
+ cSid++;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(RethrowTerminalExceptions);
+
+ // Get sid for target process.
+ EX_TRY
+ {
+ sidTargetProcess.InitFromProcess(pid); // throws on error.
+ pTargetProcessSid = sidTargetProcess.GetSid().RawSid();
+ cSid++;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(RethrowTerminalExceptions);
+
+ //FISHY: what is the scenario where only one of the above calls succeeds?
+ if (cSid == 0)
+ {
+ // failed to get any useful sid. Just return.
+ // need a better error.
+ //
+ hr = E_FAIL;
+ goto exit;
+ }
+
+ hr = sidTargetProcessAppContainer.InitFromProcessAppContainerSidNoThrow(pid);
+ if (FAILED(hr))
+ {
+ goto exit;
+ }
+ else if (hr == S_OK)
+ {
+ pTargetProcessAppContainerSid = sidTargetProcessAppContainer.GetSid().RawSid();
+ cSid++;
+ }
+ else if(hr == S_FALSE) //not an app container, no sid to add
+ {
+ hr = S_OK; // don't leak S_FALSE
+ }
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetACLOfPid number of sid : 0x%08x\n",
+ cSid));
+
+ // Now allocate space for ACL. First calculate the space is need to hold ACL
+ dwAclSize = sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) * cSid;
+ if (pCurrentProcessSid)
+ {
+ dwAclSize += GetLengthSid(pCurrentProcessSid);
+ }
+ if (pTargetProcessSid)
+ {
+ dwAclSize += GetLengthSid(pTargetProcessSid);
+ }
+ if (pTargetProcessAppContainerSid)
+ {
+ dwAclSize += GetLengthSid(pTargetProcessAppContainerSid);
+ }
+
+ *ppACL = (PACL) new (nothrow) char[dwAclSize];
+ if (*ppACL == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto exit;
+ }
+
+ // Initialize ACL
+ // add each sid to the allowed ace list
+ if (!InitializeAcl(*ppACL, dwAclSize, ACL_REVISION))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+
+ if (pCurrentProcessSid)
+ {
+ // add the current process's sid into ACL if we have it
+ if (!AddAccessAllowedAce(*ppACL, ACL_REVISION, CLR_IPC_GENERIC_RIGHT, pCurrentProcessSid))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ }
+
+ if (pTargetProcessSid)
+ {
+ // add the target process's sid into ACL if we have it
+ if (!AddAccessAllowedAce(*ppACL, ACL_REVISION, CLR_IPC_GENERIC_RIGHT, pTargetProcessSid))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ }
+
+ if (pTargetProcessAppContainerSid)
+ {
+ // add the target process's AppContainer's sid into ACL if we have it
+ if (!AddAccessAllowedAce(*ppACL, ACL_REVISION, CLR_IPC_GENERIC_RIGHT, pTargetProcessAppContainerSid))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ }
+
+ // we better to form a valid ACL to return
+ _ASSERTE(IsValidAcl(*ppACL));
+exit:
+ if (FAILED(hr) && *ppACL)
+ {
+ delete [] (reinterpret_cast<char*>(ppACL));
+ }
+ return hr;
+} // SecurityUtil::GetACLOfPid
+
+
+//*****************************************************************
+// static helper function
+//
+// free the ACL allocated by SecurityUtil::GetACLOfPid
+//
+// [IN] pACL - ACL to be freed
+//
+//*****************************************************************
+void SecurityUtil::FreeACL(PACL pACL)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+ if (pACL)
+ {
+ delete [] (reinterpret_cast<char*>(pACL));
+ }
+} // SecurityUtil::FreeACL
+
+
+//*****************************************************************
+// constructor
+//
+// [IN] pACL - ACL that this instance of SecurityUtil will held on to
+//
+//*****************************************************************
+SecurityUtil::SecurityUtil(PACL pACL)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+ m_pACL = pACL;
+ m_pSacl = NULL;
+ m_fInitialized = false;
+}
+
+//*****************************************************************
+// destructor
+//
+// free the ACL that this instance of SecurityUtil helds on to
+//
+//*****************************************************************
+SecurityUtil::~SecurityUtil()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+ FreeACL(m_pACL);
+ FreeACL(m_pSacl);
+}
+
+//*****************************************************************
+// Initialization function
+//
+// form the SecurityDescriptor that will represent the m_pACL
+//
+//*****************************************************************
+HRESULT SecurityUtil::Init()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ if (m_pACL)
+ {
+ if (!InitializeSecurityDescriptor(&m_SD, SECURITY_DESCRIPTOR_REVISION))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ return hr;
+ }
+ if (!SetSecurityDescriptorDacl(&m_SD, TRUE, m_pACL, FALSE))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ return hr;
+ }
+
+ m_SA.nLength = sizeof(SECURITY_ATTRIBUTES);
+ m_SA.lpSecurityDescriptor = &m_SD;
+ m_SA.bInheritHandle = FALSE;
+ m_fInitialized = true;
+ }
+ return S_OK;
+}
+
+// ***************************************************************************
+// Initialization functions which will call the normal Init and add a
+// mandatory label entry to the sacl
+//
+// Expects hProcess to be a valid handle to the process which has the desired
+// mandatory label
+// ***************************************************************************
+HRESULT SecurityUtil::Init(HANDLE hProcess)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = Init();
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ NewArrayHolder<BYTE> pLabel;
+
+ hr = GetMandatoryLabelFromProcess(hProcess, &pLabel);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ TOKEN_MANDATORY_LABEL * ptml = (TOKEN_MANDATORY_LABEL *) pLabel.GetValue();
+
+ hr = SetSecurityDescriptorMandatoryLabel(ptml->Label.Sid);
+
+ return hr;
+}
+
+
+// ***************************************************************************
+// Given a process, this will put the mandatory label into a buffer and point
+// ppbLabel at the buffer.
+//
+// Caller must free ppbLabel via the array "delete []" operator
+// ***************************************************************************
+HRESULT SecurityUtil::GetMandatoryLabelFromProcess(HANDLE hProcess, LPBYTE * ppbLabel)
+{
+ *ppbLabel = NULL;
+
+ DWORD dwSize = 0;
+ HandleHolder hToken;
+ DWORD err = 0;
+
+ if(!OpenProcessToken(hProcess, TOKEN_READ, &hToken))
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, NULL, 0, &dwSize))
+ {
+ err = GetLastError();
+ }
+
+ // We need to make sure that GetTokenInformation failed in a predictable manner so we know that
+ // dwSize has the correct buffer size in it.
+ if (err != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
+ {
+ return HRESULT_FROM_WIN32(err);
+ }
+
+ NewArrayHolder<BYTE> pLabel = new (nothrow) BYTE[dwSize];
+ if (pLabel == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, pLabel, dwSize, &dwSize))
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ // Our caller will be freeing the memory so use Extract
+ *ppbLabel = pLabel.Extract();
+
+ return S_OK;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Returns pointer inside the specified mandatory SID to the DWORD representing the
+// integrity level of the process. This DWORD will be one of the
+// SECURITY_MANDATORY_*_RID constants.
+//
+// Arguments:
+// psidIntegrityLevelLabel - [in] PSID in which to find the integrity level.
+//
+// Return Value:
+// Pointer to the RID stored in the specified SID. This RID represents the
+// integrity level of the process
+//
+
+// static
+DWORD * SecurityUtil::GetIntegrityLevelFromMandatorySID(PSID psidIntegrityLevelLabel)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ return GetSidSubAuthority(psidIntegrityLevelLabel, (*GetSidSubAuthorityCount(psidIntegrityLevelLabel) - 1));
+}
+
+// Creates a mandatory label ace and sets it to be the entry in the
+// security descriptor's sacl. This assumes there are no other entries
+// in the sacl
+HRESULT SecurityUtil::SetSecurityDescriptorMandatoryLabel(PSID psidIntegrityLevelLabel)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr;
+
+ DWORD cbSid = GetLengthSid(psidIntegrityLevelLabel);
+ DWORD cbAceStart = offsetof(SYSTEM_MANDATORY_LABEL_ACE, SidStart);
+ // We are about allocate memory for a ACL and an ACE so we need space for:
+ // 1) the ACL: sizeof(ACL)
+ // 2) the entry: the sid is of variable size, so the SYSTEM_MANDATORY_LABEL_ACE
+ // structure has only the first DWORD of the sid in its definition, to get the
+ // appropriate size we get size without SidStart and add on the actual size of the sid
+ DWORD cbSacl = sizeof(ACL) + cbAceStart + cbSid;
+
+ NewArrayHolder<BYTE> sacl = new (nothrow) BYTE[cbSacl];
+
+ m_pSacl = NULL;
+
+ if (sacl == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ ZeroMemory(sacl.GetValue(), cbSacl);
+ PACL pSacl = reinterpret_cast<ACL *>(sacl.GetValue());
+ SYSTEM_MANDATORY_LABEL_ACE * pLabelAce = reinterpret_cast<SYSTEM_MANDATORY_LABEL_ACE *>(sacl.GetValue() + sizeof(ACL));
+ PSID psid = reinterpret_cast<SID *>(&pLabelAce->SidStart);
+
+ // Our buffer looks like this now: (not drawn to scale)
+ // sacl pSacl pLabelAce psid
+ // - -
+ // | |
+ // | - -
+ // | |
+ // | | -
+ // | - |
+ // - -
+
+ DWORD dwIntegrityLevel = *(GetIntegrityLevelFromMandatorySID(psidIntegrityLevelLabel));
+
+ if (dwIntegrityLevel >= SECURITY_MANDATORY_MEDIUM_RID)
+ {
+ // No need to set the integrity level unless it's lower than medium
+ return S_OK;
+ }
+
+ if(!InitializeAcl(pSacl, cbSacl, ACL_REVISION))
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ pSacl->AceCount = 1;
+
+ pLabelAce->Header.AceType = SYSTEM_MANDATORY_LABEL_ACE_TYPE;
+ pLabelAce->Header.AceSize = WORD(cbAceStart + cbSid);
+ pLabelAce->Mask = SYSTEM_MANDATORY_LABEL_NO_WRITE_UP;
+
+ memcpy(psid, psidIntegrityLevelLabel, cbSid);
+
+ if(!SetSecurityDescriptorSacl(m_SA.lpSecurityDescriptor, TRUE, pSacl, FALSE))
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ // No need to delete the sacl buffer, it will be deleted in the
+ // destructor of this class
+ m_pSacl = (PACL)sacl.Extract();
+ return S_OK;
+}
+
+//*****************************************************************
+// Return SECURITY_ATTRIBUTES that we form in the Init function
+//
+// No clean up is needed after calling this function. The destructor of the
+// instance will do the right thing. Note that this is designed such that
+// we minimize memory allocation, ie the SECURITY_DESCRIPTOR and
+// SECURITY_ATTRIBUTES are embedded in the SecurityUtil instance.
+//
+// Caller should not modify the returned SECURITY_ATTRIBUTES!!!
+//*****************************************************************
+HRESULT SecurityUtil::GetSA(SECURITY_ATTRIBUTES **ppSA)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(ppSA);
+
+ if (m_fInitialized == false)
+ {
+ _ASSERTE(!"Bad code path!");
+ *ppSA = NULL;
+ return E_FAIL;
+ }
+
+ *ppSA = &m_SA;
+ return S_OK;
+}