windows-nt/Source/XPSP1/NT/inetsrv/iis/admin/common/accentry.cpp

780 lines
15 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1994-1999 Microsoft Corporation
Module Name :
accentry.cpp
Abstract:
CAccessEntry class implementation
Author:
Ronald Meijer (ronaldm)
Project:
Internet Services Manager
Revision History:
1/9/2000 sergeia Cleaned out from usrbrows.cpp
--*/
//
// Include Files
//
#include "stdafx.h"
#include <iiscnfgp.h>
#include "common.h"
#include "objpick.h"
#include "accentry.h"
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
#define new DEBUG_NEW
#define SZ_USER_CLASS _T("user")
#define SZ_GROUP_CLASS _T("group")
BOOL
CAccessEntry::LookupAccountSid(
OUT CString & strFullUserName,
OUT int & nPictureID,
IN PSID pSid,
IN LPCTSTR lpstrSystemName /* OPTIONAL */
)
/*++
Routine Description:
Get a full user name and picture ID from the SID
Arguments:
CString &strFullUserName : Returns the user name
int & nPictureID : Returns offset into the imagemap that
represents the account type.
PSID pSid : Input SID pointer
LPCTSTR lpstrSystemName : System name or NULL
Return Value:
TRUE for success, FALSE for failure.
--*/
{
DWORD cbUserName = PATHLEN * sizeof(TCHAR);
DWORD cbRefDomainName = PATHLEN * sizeof(TCHAR);
CString strUserName;
CString strRefDomainName;
SID_NAME_USE SidToNameUse;
LPTSTR lpUserName = strUserName.GetBuffer(PATHLEN);
LPTSTR lpRefDomainName = strRefDomainName.GetBuffer(PATHLEN);
BOOL fLookUpOK = ::LookupAccountSid(
lpstrSystemName,
pSid,
lpUserName,
&cbUserName,
lpRefDomainName,
&cbRefDomainName,
&SidToNameUse
);
strUserName.ReleaseBuffer();
strRefDomainName.ReleaseBuffer();
strFullUserName.Empty();
if (fLookUpOK)
{
if (!strRefDomainName.IsEmpty()
&& strRefDomainName.CompareNoCase(_T("BUILTIN")))
{
strFullUserName += strRefDomainName;
strFullUserName += "\\";
}
strFullUserName += strUserName;
nPictureID = SidToNameUse;
}
else
{
strFullUserName.LoadString(IDS_UNKNOWN_USER);
nPictureID = SidTypeUnknown;
}
//
// SID_NAME_USE is 1-based
//
--nPictureID ;
return fLookUpOK;
}
CAccessEntry::CAccessEntry(
IN LPVOID pAce,
IN BOOL fResolveSID
)
/*++
Routine Description:
Construct from an ACE
Arguments:
LPVOID pAce : Pointer to ACE object
BOOL fResolveSID : TRUE to resolve the SID immediately
Return Value:
N/A
--*/
: m_pSid(NULL),
m_fSIDResolved(FALSE),
m_fDeletable(TRUE),
m_fInvisible(FALSE),
m_nPictureID(SidTypeUnknown-1), // SID_NAME_USE is 1-based
m_lpstrSystemName(NULL),
m_accMask(0L),
m_fDeleted(FALSE),
m_strUserName()
{
MarkEntryAsClean();
PACE_HEADER ph = (PACE_HEADER)pAce;
PSID pSID = NULL;
switch(ph->AceType)
{
case ACCESS_ALLOWED_ACE_TYPE:
pSID = (PSID)&((PACCESS_ALLOWED_ACE)pAce)->SidStart;
m_accMask = ((PACCESS_ALLOWED_ACE)pAce)->Mask;
break;
case ACCESS_DENIED_ACE_TYPE:
case SYSTEM_AUDIT_ACE_TYPE:
case SYSTEM_ALARM_ACE_TYPE:
default:
//
// Not supported!
//
ASSERT_MSG("Unsupported ACE type");
break;
}
if (pSID == NULL)
{
return;
}
//
// Allocate a new copy of the sid.
//
DWORD cbSize = ::RtlLengthSid(pSID);
m_pSid = (PSID)AllocMem(cbSize);
if (m_pSid != NULL)
{
DWORD err = ::RtlCopySid(cbSize, m_pSid, pSID);
ASSERT(err == ERROR_SUCCESS);
}
//
// Only the non-deletable administrators have execute
// privileges
//
m_fDeletable = (m_accMask & FILE_EXECUTE) == 0L;
//
// Enum_keys is a special access right that literally "everyone"
// has, but it doesn't designate an operator, so it should not
// show up in the operator list if this is the only right it has.
//
m_fInvisible = (m_accMask == MD_ACR_ENUM_KEYS);
//SetAccessMask(lpAccessEntry);
if (fResolveSID)
{
ResolveSID();
}
}
CAccessEntry::CAccessEntry(
IN ACCESS_MASK accPermissions,
IN PSID pSid,
IN LPCTSTR lpstrSystemName, OPTIONAL
IN BOOL fResolveSID
)
/*++
Routine Description:
Constructor from access permissions and SID.
Arguments:
ACCESS_MASK accPermissions : Access mask
PSID pSid : Pointer to SID
LPCTSTR lpstrSystemName : Optional system name
BOOL fResolveSID : TRUE to resolve SID immediately
Return Value:
N/A
--*/
: m_pSid(NULL),
m_fSIDResolved(FALSE),
m_fDeletable(TRUE),
m_fInvisible(FALSE),
m_fDeleted(FALSE),
m_nPictureID(SidTypeUnknown-1), // SID_NAME_USE is 1-based
m_strUserName(),
m_lpstrSystemName(NULL),
m_accMask(accPermissions)
{
MarkEntryAsClean();
//
// Allocate a new copy of the sid.
//
DWORD cbSize = ::RtlLengthSid(pSid);
m_pSid = (PSID)AllocMem(cbSize);
if (m_pSid != NULL)
{
DWORD err = ::RtlCopySid(cbSize, m_pSid, pSid);
ASSERT(err == ERROR_SUCCESS);
}
if (lpstrSystemName != NULL)
{
m_lpstrSystemName = AllocTString(::lstrlen(lpstrSystemName) + 1);
if (m_lpstrSystemName != NULL)
{
::lstrcpy(m_lpstrSystemName, lpstrSystemName);
}
}
if (fResolveSID)
{
TRACEEOLID("Bogus SID");
ResolveSID();
}
}
CAccessEntry::CAccessEntry(
IN PSID pSid,
IN LPCTSTR pszUserName,
IN LPCTSTR pszClassName
)
/*++
Routine Description:
Constructor from access sid and user/class name.
Arguments:
PSID pSid, Pointer to SID
LPCTSTR pszUserName User name
LPCTSTR pszClassName User Class name
Return Value:
N/A
--*/
: m_pSid(NULL),
m_fSIDResolved(pszUserName != NULL),
m_fDeletable(TRUE),
m_fInvisible(FALSE),
m_fDeleted(FALSE),
m_nPictureID(SidTypeUnknown-1), // SID_NAME_USE is 1-based
m_strUserName(pszUserName),
m_lpstrSystemName(NULL),
m_accMask(0)
{
MarkEntryAsClean();
//
// Allocate a new copy of the sid.
//
DWORD cbSize = ::RtlLengthSid(pSid);
m_pSid = (PSID)AllocMem(cbSize);
if (m_pSid != NULL)
{
DWORD err = ::RtlCopySid(cbSize, m_pSid, pSid);
ASSERT(err == ERROR_SUCCESS);
}
if (lstrcmpi(SZ_USER_CLASS, pszClassName) == 0)
{
m_nPictureID = SidTypeUser - 1;
}
else if (lstrcmpi(SZ_GROUP_CLASS, pszClassName) == 0)
{
m_nPictureID = SidTypeGroup - 1;
}
}
CAccessEntry::CAccessEntry(
IN CAccessEntry & ae
)
/*++
Routine Description:
Copy constructor
Arguments:
CAccessEntry & ae : Source to copy from
Return Value:
N/A
--*/
: m_fSIDResolved(ae.m_fSIDResolved),
m_fDeletable(ae.m_fDeletable),
m_fInvisible(ae.m_fInvisible),
m_fDeleted(ae.m_fDeleted),
m_fDirty(ae.m_fDirty),
m_nPictureID(ae.m_nPictureID),
m_strUserName(ae.m_strUserName),
m_lpstrSystemName(ae.m_lpstrSystemName),
m_accMask(ae.m_accMask)
{
DWORD cbSize = ::RtlLengthSid(ae.m_pSid);
m_pSid = (PSID)AllocMem(cbSize);
if (m_pSid != NULL)
{
DWORD err = ::RtlCopySid(cbSize, m_pSid, ae.m_pSid);
ASSERT(err == ERROR_SUCCESS);
}
}
CAccessEntry::~CAccessEntry()
/*++
Routine Description:
Destructor
Arguments:
N/A
Return Value:
N/A
--*/
{
TRACEEOLID(_T("Destroying local copy of the SID"));
ASSERT_PTR(m_pSid);
FreeMem(m_pSid);
if (m_lpstrSystemName != NULL)
{
FreeMem(m_lpstrSystemName);
}
}
BOOL
CAccessEntry::ResolveSID()
/*++
Routine Description:
Look up the user name and type.
Arguments:
None
Return Value:
TRUE for success, FALSE for failure.
Notes:
This could take some time.
--*/
{
//
// Even if it fails, it will be considered
// resolved
//
m_fSIDResolved = TRUE;
return CAccessEntry::LookupAccountSid(
m_strUserName,
m_nPictureID,
m_pSid,
m_lpstrSystemName
);
}
void
CAccessEntry::AddPermissions(
IN ACCESS_MASK accNewPermissions
)
/*++
Routine Description:
Add permissions to this entry.
Arguments:
ACCESS_MASK accNewPermissions : New access permissions to be added
Return Value:
None.
--*/
{
m_accMask |= accNewPermissions;
m_fInvisible = (m_accMask == MD_ACR_ENUM_KEYS);
m_fDeletable = (m_accMask & FILE_EXECUTE) == 0L;
MarkEntryAsChanged();
}
void
CAccessEntry::RemovePermissions(
IN ACCESS_MASK accPermissions
)
/*++
Routine Description:
Remove permissions from this entry.
Arguments:
ACCESS_MASK accPermissions : Access permissions to be taken away
--*/
{
m_accMask &= ~accPermissions;
MarkEntryAsChanged();
}
//
// Helper Functions
//
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
PSID
GetOwnerSID()
/*++
Routine Description:
Return a pointer to the primary owner SID we're using.
Arguments:
None
Return Value:
Pointer to owner SID.
--*/
{
PSID pSID = NULL;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
if (!::AllocateAndInitializeSid(
&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pSID
))
{
TRACEEOLID("Unable to get primary SID " << ::GetLastError());
}
return pSID;
}
BOOL
BuildAclBlob(
IN CObListPlus & oblSID,
OUT CBlob & blob
)
/*++
Routine Description:
Build a security descriptor blob from the access entries in the oblist
Arguments:
CObListPlus & oblSID : Input list of access entries
CBlob & blob : Output blob
Return Value:
TRUE if the list is dirty. If the list had no entries marked
as dirty, the blob generated will be empty, and this function will
return FALSE.
Notes:
Entries marked as deleted will not be added to the list.
--*/
{
ASSERT(blob.IsEmpty());
BOOL fAclDirty = FALSE;
CAccessEntry * pEntry;
DWORD dwAclSize = sizeof(ACL) - sizeof(DWORD);
CObListIter obli(oblSID);
int cItems = 0;
while(NULL != (pEntry = (CAccessEntry *)obli.Next()))
{
if (!pEntry->IsDeleted())
{
dwAclSize += GetLengthSid(pEntry->GetSid());
dwAclSize += sizeof(ACCESS_ALLOWED_ACE);
++cItems;
}
if (pEntry->IsDirty())
{
fAclDirty = TRUE;
}
}
if (fAclDirty)
{
//
// Build the acl
//
PACL pacl = NULL;
if (cItems > 0 && dwAclSize > 0)
{
pacl = (PACL)AllocMem(dwAclSize);
if (pacl != NULL)
{
if (InitializeAcl(pacl, dwAclSize, ACL_REVISION))
{
obli.Reset();
while(NULL != (pEntry = (CAccessEntry *)obli.Next()))
{
if (!pEntry->IsDeleted())
{
VERIFY(AddAccessAllowedAce(
pacl,
ACL_REVISION,
pEntry->QueryAccessMask(),
pEntry->GetSid()
));
}
}
}
}
else
{
return FALSE;
}
}
//
// Build the security descriptor
//
PSECURITY_DESCRIPTOR pSD =
(PSECURITY_DESCRIPTOR)AllocMem(SECURITY_DESCRIPTOR_MIN_LENGTH);
if (pSD != NULL)
{
VERIFY(InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION));
VERIFY(SetSecurityDescriptorDacl(pSD, TRUE, pacl, FALSE));
}
else
{
return FALSE;
}
//
// Set owner and primary group
//
PSID pSID = GetOwnerSID();
ASSERT(pSID);
VERIFY(SetSecurityDescriptorOwner(pSD, pSID, TRUE));
VERIFY(SetSecurityDescriptorGroup(pSD, pSID, TRUE));
//
// Convert to self-relative
//
PSECURITY_DESCRIPTOR pSDSelfRelative = NULL;
DWORD dwSize = 0L;
MakeSelfRelativeSD(pSD, pSDSelfRelative, &dwSize);
pSDSelfRelative = AllocMem(dwSize);
if (pSDSelfRelative != NULL)
{
MakeSelfRelativeSD(pSD, pSDSelfRelative, &dwSize);
//
// Blob takes ownership
//
blob.SetValue(dwSize, (PBYTE)pSDSelfRelative, FALSE);
}
//
// Clean up
//
FreeMem(pSD);
FreeSid(pSID);
}
return fAclDirty;
}
DWORD
BuildAclOblistFromBlob(
IN CBlob & blob,
OUT CObListPlus & oblSID
)
/*++
Routine Description:
Build an oblist of access entries from a security descriptor blob
Arguments:
CBlob & blob : Input blob
CObListPlus & oblSID : Output oblist of access entries
Return Value:
Error return code
--*/
{
PSECURITY_DESCRIPTOR pSD = NULL;
if (!blob.IsEmpty())
{
pSD = (PSECURITY_DESCRIPTOR)blob.GetData();
}
if (pSD == NULL)
{
//
// Empty...
//
return ERROR_SUCCESS;
}
if (!IsValidSecurityDescriptor(pSD))
{
return ::GetLastError();
}
ASSERT(GetSecurityDescriptorLength(pSD) == blob.GetSize());
PACL pacl = NULL;
BOOL fDaclPresent = FALSE;
BOOL fDaclDef= FALSE;
VERIFY(GetSecurityDescriptorDacl(pSD, &fDaclPresent, &pacl, &fDaclDef));
if (!fDaclPresent || pacl == NULL)
{
return ERROR_SUCCESS;
}
if (!IsValidAcl(pacl))
{
return GetLastError();
}
CError err;
for (WORD w = 0; w < pacl->AceCount; ++w)
{
PVOID pAce;
if (GetAce(pacl, w, &pAce))
{
CAccessEntry * pEntry = new CAccessEntry(pAce, TRUE);
if (pEntry)
{
oblSID.AddTail(pEntry);
}
else
{
TRACEEOLID("BuildAclOblistFromBlob: OOM");
err = ERROR_NOT_ENOUGH_MEMORY;
break;
}
}
else
{
//
// Save last error, but continue
//
err.GetLastWinError();
}
}
//
// Return last error
//
return err;
}