780 lines
15 KiB
C++
780 lines
15 KiB
C++
/*++
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
|