windows-nt/Source/XPSP1/NT/ds/security/services/ca/certlib/officer.cpp

711 lines
18 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+--------------------------------------------------------------------------
// File: officer.cpp
// Contents: officer rights implementation
//---------------------------------------------------------------------------
#include <pch.cpp>
#include <certsd.h>
#include <certacl.h>
#include <sid.h>
using namespace CertSrv;
HRESULT
COfficerRightsSD::Merge(
PSECURITY_DESCRIPTOR pOfficerSD,
PSECURITY_DESCRIPTOR pCASD)
{
HRESULT hr;
PACL pCAAcl; // no free
PACL pOfficerAcl; // no free
PACL pNewOfficerAcl = NULL;
ACL_SIZE_INFORMATION CAAclInfo, OfficerAclInfo;
PBOOL pCAFound = NULL, pOfficerFound = NULL;
DWORD cCAAce, cOfficerAce;
PACCESS_ALLOWED_ACE pCAAce;
PACCESS_ALLOWED_CALLBACK_ACE pOfficerAce;
PACCESS_ALLOWED_CALLBACK_ACE pNewAce = NULL;
DWORD dwNewAclSize = sizeof(ACL);
PSID pSidEveryone = NULL, pSidBuiltinAdministrators = NULL;
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_WORLD_SID_AUTHORITY;
DWORD dwSidEveryoneSize, dwAceSize, dwSidSize;
PSID_LIST pSidList;
PSECURITY_DESCRIPTOR pNewOfficerSD = NULL;
ACL EmptyAcl;
SECURITY_DESCRIPTOR EmptySD;
CSASSERT(NULL==pOfficerSD || IsValidSecurityDescriptor(pOfficerSD));
CSASSERT(IsValidSecurityDescriptor(pCASD));
// allow NULL officer SD, in that case build an empty SD and use it
if(NULL==pOfficerSD)
{
if(!InitializeAcl(&EmptyAcl, sizeof(ACL), ACL_REVISION))
{
hr = myHLastError();
_JumpError(hr, error, "InitializeAcl");
}
if (!InitializeSecurityDescriptor(&EmptySD, SECURITY_DESCRIPTOR_REVISION))
{
hr = myHLastError();
_JumpError(hr, error, "InitializeSecurityDescriptor");
}
if(!SetSecurityDescriptorDacl(
&EmptySD,
TRUE, // DACL present
&EmptyAcl,
FALSE)) // DACL defaulted
{
hr = myHLastError();
_JumpError(hr, error, "SetSecurityDescriptorControl");
}
pOfficerSD = &EmptySD;
}
hr = myGetSecurityDescriptorDacl(pCASD, &pCAAcl);
_JumpIfError(hr, error, "myGetSecurityDescriptorDacl");
hr = myGetSecurityDescriptorDacl(pOfficerSD, &pOfficerAcl);
_JumpIfError(hr, error, "myGetSecurityDescriptorDacl");
// allocate a bool array for each DACL
if(!GetAclInformation(pCAAcl,
&CAAclInfo,
sizeof(CAAclInfo),
AclSizeInformation))
{
hr = myHLastError();
_JumpError(hr, error, "GetAclInformation");
}
if(!GetAclInformation(pOfficerAcl,
&OfficerAclInfo,
sizeof(OfficerAclInfo),
AclSizeInformation))
{
hr = myHLastError();
_JumpError(hr, error, "GetAclInformation");
}
pCAFound = (PBOOL)LocalAlloc(LMEM_FIXED, sizeof(BOOL)*CAAclInfo.AceCount);
if(!pCAFound)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
ZeroMemory(pCAFound, sizeof(BOOL)*CAAclInfo.AceCount);
pOfficerFound = (PBOOL)LocalAlloc(LMEM_FIXED, sizeof(BOOL)*OfficerAclInfo.AceCount);
if(!pOfficerFound)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
ZeroMemory(pOfficerFound, sizeof(BOOL)*OfficerAclInfo.AceCount);
hr = GetEveryoneSID(&pSidEveryone);
_JumpIfError(hr, error, "GetEveryoneSID");
dwSidEveryoneSize = GetLengthSid(pSidEveryone);
// mark in the bool arrays each ace whose SID is found in both DACLs;
// also mark CA ACEs we are not interested in (denied ACEs and
// non-officer ACEs)
for(cCAAce=0; cCAAce<CAAclInfo.AceCount; cCAAce++)
{
if(!GetAce(pCAAcl, cCAAce, (PVOID*)&pCAAce))
{
hr = myHLastError();
_JumpError(hr, error, "GetAce");
}
// process only officer allow aces
if(0==(pCAAce->Mask & CA_ACCESS_OFFICER))
{
pCAFound[cCAAce] = TRUE;
continue;
}
// compare SIDs in each officer ace with current CA ace and mark
// corresponding bool in arrays if equal
for(cOfficerAce=0; cOfficerAce<OfficerAclInfo.AceCount; cOfficerAce++)
{
if(!GetAce(pOfficerAcl, cOfficerAce, (PVOID*)&pOfficerAce))
{
hr = myHLastError();
_JumpError(hr, error, "GetAce");
}
if(EqualSid((PSID)&pOfficerAce->SidStart,
(PSID)&pCAAce->SidStart))
{
pCAFound[cCAAce] = TRUE;
pOfficerFound[cOfficerAce] = TRUE;
}
}
// if the officer is found in the CA ACL but not in the officer ACL,
// we will add a new ACE allowing him to manage certs for Everyone
if(!pCAFound[cCAAce])
{
dwNewAclSize += sizeof(ACCESS_ALLOWED_CALLBACK_ACE)+
GetLengthSid((PSID)&pCAAce->SidStart)+
dwSidEveryoneSize;
}
}
// Calculate the size of the new officer ACL; we already added in the header
// size and the size of the new ACEs to be added. Now we add the ACEs to be
// copied over from the old ACL
for(cOfficerAce=0; cOfficerAce<OfficerAclInfo.AceCount; cOfficerAce++)
{
if(pOfficerFound[cOfficerAce])
{
if(!GetAce(pOfficerAcl, cOfficerAce, (PVOID*)&pOfficerAce))
{
hr = myHLastError();
_JumpError(hr, error, "GetAce");
}
dwNewAclSize += pOfficerAce->Header.AceSize;
}
}
// allocate a new DACL
pNewOfficerAcl = (PACL)LocalAlloc(LMEM_FIXED, dwNewAclSize);
if(!pNewOfficerAcl)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
#ifdef _DEBUG
ZeroMemory(pNewOfficerAcl, dwNewAclSize);
#endif
if(!InitializeAcl(pNewOfficerAcl, dwNewAclSize, ACL_REVISION))
{
hr = myHLastError();
_JumpError(hr, error, "InitializeAcl");
}
// build the new DACL
// traverse officer DACL and add only marked ACEs (whose SID was found
// in the CA DACL, ie principal is an officer)
for(cOfficerAce=0; cOfficerAce<OfficerAclInfo.AceCount; cOfficerAce++)
{
if(pOfficerFound[cOfficerAce])
{
if(!GetAce(pOfficerAcl, cOfficerAce, (PVOID*)&pOfficerAce))
{
hr = myHLastError();
_JumpError(hr, error, "GetAce");
}
if(!AddAce(
pNewOfficerAcl,
ACL_REVISION,
MAXDWORD,
pOfficerAce,
pOfficerAce->Header.AceSize))
{
hr = myHLastError();
_JumpError(hr, error, "GetAce");
}
}
}
CSASSERT(IsValidAcl(pNewOfficerAcl));
hr = GetBuiltinAdministratorsSID(&pSidBuiltinAdministrators);
_JumpIfError(hr, error, "GetBuiltinAdministratorsSID");
// traverse the CA DACL and add a new officer to list, allowed to manage
// Everyone
for(cCAAce=0; cCAAce<CAAclInfo.AceCount; cCAAce++)
{
if(pCAFound[cCAAce])
continue;
if(!GetAce(pCAAcl, cCAAce, (PVOID*)&pCAAce))
{
hr = myHLastError();
_JumpError(hr, error, "GetAce");
}
// create a new ACE
dwSidSize = GetLengthSid((PSID)&pCAAce->SidStart);
dwAceSize = sizeof(ACCESS_ALLOWED_CALLBACK_ACE)+
dwSidEveryoneSize+dwSidSize;
pNewAce = (ACCESS_ALLOWED_CALLBACK_ACE*) LocalAlloc(LMEM_FIXED, dwAceSize);
if(!pNewAce)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
#ifdef _DEBUG
ZeroMemory(pNewAce, dwAceSize);
#endif
pNewAce->Header.AceType = ACCESS_ALLOWED_CALLBACK_ACE_TYPE;
pNewAce->Header.AceFlags= 0;
pNewAce->Header.AceSize = (USHORT)dwAceSize;
pNewAce->Mask = DELETE;
CopySid(dwSidSize,
(PSID)&pNewAce->SidStart,
(PSID)&pCAAce->SidStart);
pSidList = (PSID_LIST)(((BYTE*)&pNewAce->SidStart)+dwSidSize);
pSidList->dwSidCount = 1;
CopySid(dwSidEveryoneSize,
(PSID)&pSidList->SidListStart,
pSidEveryone);
CSASSERT(IsValidSid((PSID)&pNewAce->SidStart));
if(!AddAce(
pNewOfficerAcl,
ACL_REVISION,
MAXDWORD,
pNewAce,
dwAceSize))
{
hr = myHLastError();
_JumpError(hr, error, "AddAce");
}
LocalFree(pNewAce);
pNewAce = NULL;
}
CSASSERT(IsValidAcl(pNewOfficerAcl));
// setup the new security descriptor
pNewOfficerSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if (pNewOfficerSD == NULL)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
#ifdef _DEBUG
ZeroMemory(pNewOfficerSD, SECURITY_DESCRIPTOR_MIN_LENGTH);
#endif
if (!InitializeSecurityDescriptor(pNewOfficerSD, SECURITY_DESCRIPTOR_REVISION))
{
hr = myHLastError();
_JumpError(hr, error, "InitializeSecurityDescriptor");
}
if(!SetSecurityDescriptorOwner(
pNewOfficerSD,
pSidBuiltinAdministrators,
FALSE))
{
hr = myHLastError();
_JumpError(hr, error, "SetSecurityDescriptorControl");
}
if(!SetSecurityDescriptorDacl(
pNewOfficerSD,
TRUE, // DACL present
pNewOfficerAcl,
FALSE)) // DACL defaulted
{
hr = myHLastError();
_JumpError(hr, error, "SetSecurityDescriptorDacl");
}
CSASSERT(IsValidSecurityDescriptor(pNewOfficerSD));
hr = Set(pNewOfficerSD);
_JumpIfError(hr, error, "CProtectedSecurityDescriptor::Set");
error:
if(pNewAce)
{
LocalFree(pNewAce);
}
if(pSidEveryone)
{
FreeSid(pSidEveryone);
}
if(pSidBuiltinAdministrators)
{
FreeSid(pSidBuiltinAdministrators);
}
if(pNewOfficerAcl)
{
LocalFree(pNewOfficerAcl);
}
if(pNewOfficerSD)
{
LocalFree(pNewOfficerSD);
}
return hr;
}
HRESULT
COfficerRightsSD::Adjust(PSECURITY_DESCRIPTOR pCASD)
{
return Merge(Get(), pCASD);
}
HRESULT
COfficerRightsSD::InitializeEmpty()
{
HRESULT hr = S_OK;
ACL Acl;
SECURITY_DESCRIPTOR SD;
hr = Init(NULL);
_JumpIfError(hr, error, "CProtectedSecurityDescriptor::Init");
if(!InitializeAcl(&Acl, sizeof(ACL), ACL_REVISION))
{
hr = myHLastError();
_JumpError(hr, error, "InitializeAcl");
}
if (!InitializeSecurityDescriptor(&SD, SECURITY_DESCRIPTOR_REVISION))
{
hr = myHLastError();
_JumpError(hr, error, "InitializeSecurityDescriptor");
}
if(!SetSecurityDescriptorDacl(
&SD,
TRUE, // DACL present
&Acl,
FALSE)) // DACL defaulted
{
hr = myHLastError();
_JumpError(hr, error, "SetSecurityDescriptorControl");
}
m_fInitialized = true;
hr = Set(&SD);
_JumpIfError(hr, error, "CProtectedSecurityDescriptor::Set");
error:
return hr;
}
HRESULT COfficerRightsSD::Save()
{
HRESULT hr = S_OK;
if(IsEnabled())
{
hr = CProtectedSecurityDescriptor::Save();
_JumpIfError(hr, error, "CProtectedSecurityDescriptor::Save");
}
else
{
hr = CProtectedSecurityDescriptor::Delete();
_JumpIfError(hr, error, "CProtectedSecurityDescriptor::Delete");
}
error:
return hr;
}
HRESULT COfficerRightsSD::Load()
{
HRESULT hr;
hr = CProtectedSecurityDescriptor::Load();
if(S_OK==hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
{
SetEnable(S_OK==hr);
hr = S_OK;
}
_JumpIfError(hr, error, "CProtectedSecurityDescriptor::Save");
error:
return hr;
}
HRESULT COfficerRightsSD::Initialize(LPCWSTR pwszSanitizedName)
{
HRESULT hr;
hr = CProtectedSecurityDescriptor::Initialize(pwszSanitizedName);
_JumpIfError(hr, error, "CProtectedSecurityDescriptor::Save");
SetEnable(NULL!=m_pSD);
error:
return hr;
}
HRESULT COfficerRightsSD::ConvertToString(
IN PSECURITY_DESCRIPTOR pSD,
OUT LPWSTR& rpwszSD)
{
HRESULT hr = S_OK;
LPCWSTR pcwszHeader = L"\n"; // start with a new line
DWORD dwBufSize = sizeof(WCHAR)*(wcslen(pcwszHeader)+1);
ACL_SIZE_INFORMATION AclInfo;
DWORD dwIndex;
PACCESS_ALLOWED_CALLBACK_ACE pAce; // no free
PACL pDacl; // no free
LPWSTR pwszAce; // no free
CSASSERT(IsValidSecurityDescriptor(pSD));
rpwszSD = NULL;
// get acl
hr = myGetSecurityDescriptorDacl(
pSD,
&pDacl);
_JumpIfError(hr, error, "myGetDaclFromInfoSecurityDescriptor");
if(!GetAclInformation(pDacl,
&AclInfo,
sizeof(ACL_SIZE_INFORMATION),
AclSizeInformation))
{
hr = myHLastError();
_JumpError(hr, error, "GetAclInformation");
}
// calculate text size
for(dwIndex = 0; dwIndex < AclInfo.AceCount; dwIndex++)
{
DWORD dwAceSize;
if(!GetAce(pDacl, dwIndex, (LPVOID*)&pAce))
{
hr = myHLastError();
_JumpError(hr, error, "GetAce");
}
hr = ConvertAceToString(
pAce,
&dwAceSize,
NULL);
_JumpIfError(hr, error, "ConvertAceToString");
dwBufSize += dwAceSize;
}
rpwszSD = (LPWSTR)LocalAlloc(LMEM_FIXED, dwBufSize);
_JumpIfAllocFailed(rpwszSD, error);
// build the output string
wcscpy(rpwszSD, pcwszHeader);
pwszAce = rpwszSD + wcslen(pcwszHeader);
for(dwIndex = 0; dwIndex < AclInfo.AceCount; dwIndex++)
{
DWORD dwAceSize;
if(!GetAce(pDacl, dwIndex, (LPVOID*)&pAce))
{
hr = myHLastError();
_JumpError(hr, error, "GetAce");
}
hr = ConvertAceToString(
pAce,
&dwAceSize,
pwszAce);
_JumpIfError(hr, error, "ConvertAceToString");
pwszAce += dwAceSize/sizeof(WCHAR);
}
error:
return hr;
}
// Returned string has the following format:
//
// [Allow|Deny]\t[OfficerName|SID]\n
// \t[Client1Name|SID]\n
// \t[Client2Name|SID]\n
// ...
//
// Example:
//
// Allow OfficerGroup1
// ClientGroup1
// ClientGroup2
//
// If SID cannot be converted to friendly name it is displayed
// as a string SID
//
HRESULT COfficerRightsSD::ConvertAceToString(
IN PACCESS_ALLOWED_CALLBACK_ACE pAce,
OUT OPTIONAL PDWORD pdwSize,
IN OUT OPTIONAL LPWSTR pwszSD)
{
HRESULT hr = S_OK;
DWORD dwSize = 1; // trailing '\0'
CSid sid((PSID)(&pAce->SidStart));
PSID_LIST pSidList = (PSID_LIST) (((BYTE*)&pAce->SidStart)+
GetLengthSid(&pAce->SidStart));
PSID pSid;
DWORD cSids;
LPCWSTR pcwszAllow = m_pcwszResources[0];
LPCWSTR pcwszDeny = m_pcwszResources[1];
LPCWSTR pcwszPermissionType =
(ACCESS_ALLOWED_CALLBACK_ACE_TYPE==pAce->Header.AceType)?
pcwszAllow:pcwszDeny;
LPCWSTR pcwszSid; // no free
// asked for size and/or ace string
CSASSERT(pdwSize || pwszSD);
if(pAce->Header.AceType != ACCESS_ALLOWED_CALLBACK_ACE_TYPE &&
pAce->Header.AceType != ACCESS_DENIED_CALLBACK_ACE_TYPE)
{
return E_INVALIDARG;
}
pcwszSid = sid.GetName();
if(!pcwszSid)
{
return E_OUTOFMEMORY;
}
dwSize = wcslen(pcwszSid);
dwSize += wcslen(pcwszPermissionType);
dwSize += 2; // '\t' between sid an permission and a '\n' after
if(pwszSD)
{
wcscat(pwszSD, pcwszPermissionType);
wcscat(pwszSD, L"\t");
wcscat(pwszSD, pcwszSid);
wcscat(pwszSD, L"\n");
}
for(pSid=(PSID)&pSidList->SidListStart, cSids=0; cSids<pSidList->dwSidCount;
cSids++, pSid = (PSID)(((BYTE*)pSid)+GetLengthSid(pSid)))
{
CSASSERT(IsValidSid(pSid));
CSid sidClient(pSid);
LPCWSTR pcwszSidClient;
pcwszSidClient = sidClient.GetName();
if(!pcwszSidClient)
{
return E_OUTOFMEMORY;
}
dwSize += wcslen(pcwszSidClient) + 2; // \tClientNameOrSid\n
if(pwszSD)
{
wcscat(pwszSD, L"\t");
wcscat(pwszSD, pcwszSidClient);
wcscat(pwszSD, L"\n");
}
}
dwSize *= sizeof(WCHAR);
if(pdwSize)
{
*pdwSize = dwSize;
}
return hr;
}
HRESULT
CertSrv::GetWellKnownSID(
PSID *ppSid,
SID_IDENTIFIER_AUTHORITY *pAuth,
BYTE SubauthorityCount,
DWORD SubAuthority1,
DWORD SubAuthority2,
DWORD SubAuthority3,
DWORD SubAuthority4,
DWORD SubAuthority5,
DWORD SubAuthority6,
DWORD SubAuthority7,
DWORD SubAuthority8)
{
HRESULT hr = S_OK;
// build Everyone SID
if(!AllocateAndInitializeSid(
pAuth,
SubauthorityCount,
SubAuthority1,
SubAuthority2,
SubAuthority3,
SubAuthority4,
SubAuthority5,
SubAuthority6,
SubAuthority7,
SubAuthority8,
ppSid))
{
hr = myHLastError();
_JumpError(hr, error, "AllocateAndInitializeSid");
}
error:
return hr;
}
// caller is responsible for LocalFree'ing PSID
HRESULT CertSrv::GetEveryoneSID(PSID *ppSid)
{
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_WORLD_SID_AUTHORITY;
return GetWellKnownSID(
ppSid,
&SIDAuth,
1,
SECURITY_WORLD_RID);
return S_OK;
}
// caller is responsible for LocalFree'ing PSID
HRESULT CertSrv::GetLocalSystemSID(PSID *ppSid)
{
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
return GetWellKnownSID(
ppSid,
&SIDAuth,
1,
SECURITY_LOCAL_SYSTEM_RID);
}
// caller is responsible for LocalFree'ing PSID
HRESULT CertSrv::GetBuiltinAdministratorsSID(PSID *ppSid)
{
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
return GetWellKnownSID(
ppSid,
&SIDAuth,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS);
}