/*--------------------------------------------------------------------------- File: SD.cpp Comments: class for manipulating security descriptors This class provides a thin wrapper around the security descriptor APIs, and also helps us with some of our processing heuristics. The NT APIs to read and write security descriptors for files, etc. generally return a self-relative SD when reading, but expect an absolute SD when writing Thus, we keep the original data of the SD as read, in self-relative form in m_relSD, and store any changes in absolute form in m_absSD. This allows us to easily track which parts of the SD have been modified, and to compare the before and after versions of the SD. As an optimization in our ACL translation, we can compare each SD to the initial state of the last SD we processed. If it is the same, we can simply write the result we have already calculated, instead of calculating it again. (c) Copyright 1995-1998, Mission Critical Software, Inc., All Rights Reserved Proprietary and confidential to Mission Critical Software, Inc. REVISION LOG ENTRY Revision By: Christy Boles Revised on 01-Oct-98 15:51:41 --------------------------------------------------------------------------- */ #include #include "McsDebug.h" #include "McsDbgU.h" #include "SD.hpp" TSD::TSD( SECURITY_DESCRIPTOR * pSD, // in - pointer to self-relative security descriptor SecuredObjectType objectType, // in - type of object this security descriptor secures BOOL bResponsibleForDelete // in - if TRUE, this class will delete the memory for pSD ) { m_absSD = MakeAbsSD(pSD); m_bOwnerChanged = FALSE; m_bGroupChanged = FALSE; m_bDACLChanged = FALSE; m_bSACLChanged = FALSE; m_bNeedToFreeSD = TRUE; m_bNeedToFreeOwner = TRUE; m_bNeedToFreeGroup = TRUE; m_bNeedToFreeDacl = TRUE; m_bNeedToFreeSacl = TRUE; m_ObjectType = objectType; if ( bResponsibleForDelete ) { free(pSD); } } TSD::TSD( TSD * pSD // in - another security descriptor ) { MCSVERIFY(pSD); if ( pSD ) { m_absSD = pSD->MakeAbsSD(); m_bOwnerChanged = FALSE; m_bGroupChanged = FALSE; m_bDACLChanged = FALSE; m_bSACLChanged = FALSE; m_bNeedToFreeSD = TRUE; m_bNeedToFreeOwner = TRUE; m_bNeedToFreeGroup = TRUE; m_bNeedToFreeDacl = TRUE; m_bNeedToFreeSacl = TRUE; m_ObjectType = pSD->GetType(); } } TSD::~TSD() { MCSVERIFY(m_absSD); if ( m_bNeedToFreeSD ) { if (m_absSD) FreeAbsSD(m_absSD,FALSE); m_absSD = NULL; } } void TSD::FreeAbsSD( SECURITY_DESCRIPTOR * pSD, // in - pointer to security descriptor to free BOOL bAll // in - flag whether to free all parts of the SD, or only those // allocated by this class ) { PSID sid = NULL; PACL acl = NULL; BOOL defaulted; BOOL present; GetSecurityDescriptorOwner(pSD,&sid,&defaulted); if ( sid && ( m_bNeedToFreeOwner || bAll ) ) { free(sid); sid = NULL; } GetSecurityDescriptorGroup(pSD,&sid,&defaulted); if ( sid && ( m_bNeedToFreeGroup || bAll ) ) { free(sid); sid = NULL; } GetSecurityDescriptorDacl(pSD,&present,&acl,&defaulted); if ( acl && (m_bNeedToFreeDacl || bAll ) ) { free(acl); acl = NULL; } GetSecurityDescriptorSacl(pSD,&present,&acl,&defaulted); if ( acl && ( m_bNeedToFreeSacl || bAll ) ) { free(acl); acl = NULL; } free(pSD); } SECURITY_DESCRIPTOR * // ret- copy of the SD, in Absolute format TSD::MakeAbsSD( SECURITY_DESCRIPTOR * pSD // in - security descriptor to copy ) const { DWORD sd_size = (sizeof SECURITY_DESCRIPTOR); DWORD dacl_size = 0; DWORD sacl_size = 0; DWORD owner_size = 0; DWORD group_size = 0; // Allocate space for the SD and its parts SECURITY_DESCRIPTOR * absSD = (SECURITY_DESCRIPTOR *) malloc(sd_size); if (!absSD || !pSD) { if (absSD) { free(absSD); absSD = NULL; } return NULL; } PACL absDacl = NULL; PACL absSacl = NULL; PSID absOwner = NULL; PSID absGroup = NULL; if ( ! MakeAbsoluteSD(pSD,absSD,&sd_size,absDacl,&dacl_size,absSacl,&sacl_size ,absOwner,&owner_size,absGroup,&group_size) ) { // DWORD rc = GetLastError(); // didn't work: increase sizes and try again if ( sd_size > (sizeof SECURITY_DESCRIPTOR) ) { free(absSD); absSD = (SECURITY_DESCRIPTOR *) malloc(sd_size); if (!absSD) return NULL; } if ( dacl_size ) { absDacl = (PACL)malloc(dacl_size); if (!absDacl) { free(absSD); absSD = NULL; return NULL; } } if ( sacl_size ) { absSacl = (PACL)malloc(sacl_size); if (!absSacl) { free(absSD); free(absDacl); absSD = NULL; absDacl = NULL; return NULL; } } if ( owner_size ) { absOwner = (PSID)malloc(owner_size); if (!absOwner) { free(absSD); free(absDacl); free(absSacl); absSD = NULL; absDacl = NULL; absSacl = NULL; return NULL; } } if ( group_size ) { absGroup = (PSID)malloc(group_size); if (!absGroup) { free(absSD); free(absDacl); free(absSacl); free(absOwner); absSD = NULL; absDacl = NULL; absSacl = NULL; absOwner = NULL; return NULL; } } // try again with bigger buffers if ( ! MakeAbsoluteSD(pSD,absSD,&sd_size,absDacl,&dacl_size,absSacl,&sacl_size ,absOwner,&owner_size,absGroup,&group_size) ) { free(absSD); free(absDacl); free(absSacl); free(absOwner); free(absGroup); absSD = NULL; absDacl = NULL; absSacl = NULL; absOwner = NULL; absGroup = NULL; } } return absSD; } SECURITY_DESCRIPTOR * // ret- copy of the SD, in Absolute format TSD::MakeAbsSD() const { SECURITY_DESCRIPTOR * absSD = NULL; SECURITY_DESCRIPTOR * relSD = MakeRelSD(); if (relSD) { absSD = MakeAbsSD(relSD); free(relSD); } return absSD; } SECURITY_DESCRIPTOR * // ret- copy of the SD, in self-relative form TSD::MakeRelSD() const { DWORD nBytes; SECURITY_DESCRIPTOR * relSD = NULL; nBytes = GetSecurityDescriptorLength(m_absSD); relSD = (SECURITY_DESCRIPTOR *)malloc(nBytes); if (!relSD) return NULL; if (! MakeSelfRelativeSD(m_absSD,relSD,&nBytes) ) { free(relSD); relSD = NULL; } return relSD; } PSID const // ret- sid for security descriptor owner field TSD::GetOwner() const { PSID ownersid = NULL; BOOL ownerDefaulted; GetSecurityDescriptorOwner(m_absSD,&ownersid,&ownerDefaulted); return ownersid; } void TSD::SetOwner( PSID pNewOwner // in - new value for owner field ) { MCSVERIFY(IsValidSecurityDescriptor(m_absSD)); if ( IsValidSid(pNewOwner) ) { if ( m_bNeedToFreeOwner ) { PSID old = GetOwner(); free(old); } SetSecurityDescriptorOwner(m_absSD,pNewOwner,FALSE); m_bOwnerChanged = TRUE; m_bNeedToFreeOwner = TRUE; } else { MCSVERIFY(FALSE); } } PSID const // ret- sid for security descriptor owner field TSD::GetGroup() const { PSID grpsid = NULL; BOOL grpDefaulted; MCSVERIFY(IsValidSecurityDescriptor(m_absSD)); GetSecurityDescriptorGroup(m_absSD,&grpsid,&grpDefaulted); return grpsid; } void TSD::SetGroup( PSID pNewGroup // in - new value for primary group field. ) { MCSVERIFY(IsValidSecurityDescriptor(m_absSD)); if ( IsValidSid(pNewGroup) ) { if ( m_bNeedToFreeGroup ) { PSID old = GetGroup(); free(old); } SetSecurityDescriptorGroup(m_absSD,pNewGroup,FALSE); m_bGroupChanged = TRUE; m_bNeedToFreeGroup = TRUE; } else { MCSVERIFY(FALSE); } } PACL const // ret- pointer to DACL TSD::GetDacl() const { PACL acl = NULL; BOOL defaulted; BOOL present; GetSecurityDescriptorDacl(m_absSD,&present,&acl,&defaulted); return acl; } void TSD::SetDacl( PACL pNewAcl, // in - new DACL BOOL present // in - flag, TRUE means DACL is present. ) { BOOL defaulted = FALSE; if ( IsValidAcl(pNewAcl) ) { if ( m_bNeedToFreeDacl ) { PACL old = GetDacl(); if ( old != pNewAcl ) { free(old); } } if (! SetSecurityDescriptorDacl(m_absSD,present,pNewAcl,defaulted) ) { // DWORD rc = GetLastError(); } m_bDACLChanged = TRUE; m_bNeedToFreeDacl = TRUE; } else { MCSVERIFY(FALSE); } } PACL const // ret- pointer to SACL TSD::GetSacl() const { PACL acl = NULL; BOOL defaulted; BOOL present; GetSecurityDescriptorSacl(m_absSD,&present,&acl,&defaulted); return acl; } void TSD::SetSacl( PACL pNewAcl, // in - new SACL BOOL present // in - flag, TRUE means SACL is present ) { BOOL defaulted = FALSE; if ( IsValidAcl(pNewAcl) ) { if ( m_bNeedToFreeSacl ) { PACL old = GetSacl(); if ( old != pNewAcl ) { free(old); } } SetSecurityDescriptorSacl(m_absSD,present,pNewAcl,defaulted); m_bSACLChanged = TRUE; m_bNeedToFreeSacl = TRUE; } else { MCSVERIFY(FALSE); } } BOOL TSD::IsOwnerDefaulted() const { PSID ownersid = NULL; BOOL ownerDefaulted = FALSE; GetSecurityDescriptorOwner(m_absSD,&ownersid,&ownerDefaulted); return ownerDefaulted; } BOOL TSD::IsGroupDefaulted() const { PSID groupsid = NULL; BOOL groupDefaulted = FALSE; GetSecurityDescriptorGroup(m_absSD,&groupsid,&groupDefaulted); return groupDefaulted; } BOOL TSD::IsDaclDefaulted() const { PACL acl = NULL; BOOL defaulted = FALSE; BOOL present; GetSecurityDescriptorDacl(m_absSD,&present,&acl,&defaulted); return defaulted; } BOOL TSD::IsDaclPresent() const { PACL acl = NULL; BOOL defaulted; BOOL present = FALSE; GetSecurityDescriptorDacl(m_absSD,&present,&acl,&defaulted); return present; } BOOL TSD::IsSaclDefaulted() const { PACL acl = NULL; BOOL defaulted = FALSE; BOOL present; GetSecurityDescriptorSacl(m_absSD,&present,&acl,&defaulted); return defaulted; } BOOL TSD::IsSaclPresent() const { PACL acl = NULL; BOOL defaulted; BOOL present = FALSE; GetSecurityDescriptorSacl(m_absSD,&present,&acl,&defaulted); return present; } BOOL TSD::operator == (TSD & otherSD) { return EqualSD(&otherSD); } // Compares the relative SDs (relSD) for 2 TSecurityDescriptor's. // true -> SDs are equal, false -> SDs are not equal BOOL // ret -true iff *(this->relSD) == *(otherSD->relSD) TSD::EqualSD( TSD * otherSD // in -TSecurityDescriptor to compare this SD to ) { PSID ps1; PSID ps2; PACL acl1; PACL acl2; BOOL diffound = FALSE; // both must be valid SD's ps1 = NULL; ps2 = NULL; // if GetSecurityDescriptor* fails, EqualSid, or ACLCompare will fail // Compare owners ps1 = GetOwner(); ps2 = otherSD->GetOwner(); if ((ps1) && (ps2) && (! EqualSid(ps1,ps2))) { diffound = TRUE; } ps1 = NULL; ps2 = NULL; // Compare Groups if ( ! diffound ) { ps1 = GetGroup(); ps2 = otherSD->GetGroup(); if ((ps1) && (ps2) && (! EqualSid(ps1,ps2))) { diffound = TRUE; } acl1 = NULL; acl2 = NULL; } // Compare DACLs if ( ! diffound ) { acl1 = GetDacl(); acl2 = otherSD->GetDacl(); if ((acl1) && (acl2) && (!ACLCompare(acl1,IsDaclPresent(),acl2,otherSD->IsDaclPresent()))) { diffound = TRUE; } } // Compare SACLs if ( ! diffound ) { acl1 = GetSacl(); acl2 = otherSD->GetSacl(); if ((acl1) && (acl2)) diffound = (! ACLCompare(acl1,IsSaclPresent(),acl2,otherSD->IsSaclPresent()) ); } return ! diffound; } BOOL // ret- TRUE if both ACLs are the same TSD::ACLCompare( PACL acl1, // in -ptr to first acl to compare BOOL present1, // in -present flag for acl1 PACL acl2, // in -ptr to second acl to compare BOOL present2 // in -present flag for acl2 ) { DWORD size1; DWORD size2; bool same = true; if ( present1 && present2 ) { if ( ( !acl1 && acl2 ) || ( acl1 && ! acl2 ) ) { same = false; } if ( acl1 && acl2) { size1 = acl1->AclSize; size2 = acl2->AclSize; if ( size1 != size2 ) { same = false; } else { if ( memcmp(acl1,acl2,size1) ) { same = false; } } } } else { if ( present1 || present2 ) { same = false; } } return same; } int // ret- number of aces in the ACL TSD::ACLGetNumAces( PACL acl // in - DACL or SACL ) { int nAces = 0; ACL_SIZE_INFORMATION info; if ( acl ) { if ( GetAclInformation(acl,&info,(sizeof info),AclSizeInformation) ) { nAces = info.AceCount; } else { // DWORD rc=GetLastError(); } } return nAces; } DWORD // ret- number of free bytes in the ACL TSD::ACLGetFreeBytes( PACL acl // in - DACL or SACL ) { int nFree = 0; ACL_SIZE_INFORMATION info; if ( acl ) { if ( GetAclInformation(acl,&info,(sizeof info),AclSizeInformation) ) { nFree = info.AclBytesFree; } } return nFree; } DWORD // ret- number of used bytes in the ACL TSD::ACLGetBytesInUse( PACL acl // in - DACL or SACL ) { int nBytes = 0; ACL_SIZE_INFORMATION info; if ( acl ) { if ( GetAclInformation(acl,&info,(sizeof info),AclSizeInformation) ) { nBytes = info.AclBytesInUse; } } return nBytes; } void * // ret- pointer to ace TSD::ACLGetAce( PACL acl, // in - DACL or SACL int ndx // in - index of ace to retrieve ) { void * ace = NULL; if ( ndx < ACLGetNumAces(acl) ) { if ( ! GetAce(acl,ndx,(void**)&ace) ) { ace = NULL; } } else { MCSASSERT(FALSE); // you specified a non-existant index } return ace; } void TSD::ACLDeleteAce( PACL acl, // in - DACL or SACL int ndx // in - index of ace to delete ) { int nAces = ACLGetNumAces(acl); if ( ndx < nAces ) { DeleteAce(acl,ndx); } else { MCSASSERT(FALSE); // you specified an invalid index } } // Access allowed aces are added at the beginning of the list, access denied aces are added at the end of the list void TSD::ACLAddAce( PACL * ppAcl, // i/o- DACL or SACL (this function may reallocate if the acl doesn't have room TACE * pAce, // in - ACE to add int pos // in - position ) { DWORD ndx = (DWORD)pos; DWORD rc; PACL acl = (*ppAcl); PACL newAcl; DWORD numaces = ACLGetNumAces(acl); DWORD freebytes = ACLGetFreeBytes(acl); if (!pAce->GetBuffer()) return; // allocate a new ACL if it doesn't already exist if ( ! acl ) { acl = (PACL) malloc(SD_DEFAULT_ACL_SIZE); if (!acl) return; InitializeAcl(acl,SD_DEFAULT_ACL_SIZE,ACL_REVISION); numaces = ACLGetNumAces(acl); freebytes = ACLGetFreeBytes(acl); } if ( pos == -1 ) { if ( pAce->IsAccessAllowedAce() ) { ndx = 0; } else { ndx = MAXDWORD; // insert at end of the list } } WORD currAceSize = pAce->GetSize(); if ( freebytes < currAceSize ) // we must allocate more space for the ace { //make a bigger acl newAcl = (ACL*)malloc(ACLGetBytesInUse(acl) + freebytes + currAceSize); if (!newAcl) { free(acl); acl = NULL; return; } memcpy(newAcl,acl,ACLGetBytesInUse(acl) + freebytes); newAcl->AclSize +=currAceSize; free(acl); acl = newAcl; } if (! AddAce(acl,ACL_REVISION,ndx,pAce->GetBuffer(),currAceSize) ) { rc = GetLastError(); } (*ppAcl) = acl; } // creates a new ace with the specified properties TACE::TACE( BYTE type, // in - type of ace (ACCESS_ALLOWED_ACE_TYPE, etc.) BYTE flags, // in - ace flags (controls inheritance, etc. use 0 for files) DWORD mask, // in - access control mask (see constants in sd.hpp) PSID sid // in - pointer to sid for this ace ) { MCSVERIFY(sid); // allocate memory for the new ace DWORD size = (sizeof ACCESS_ALLOWED_ACE) + GetLengthSid(sid) - (sizeof DWORD); m_pAce = (ACCESS_ALLOWED_ACE *)malloc(size); // Initialize the ACE if (m_pAce) { m_bNeedToFree = TRUE; m_pAce->Header.AceType = type; m_pAce->Header.AceFlags = flags; m_pAce->Header.AceSize = (WORD) size; m_pAce->Mask = mask; memcpy(&m_pAce->SidStart,sid,GetLengthSid(sid)); } } BYTE // ret- ace type (ACCESS_ALLOWED_ACE_TYPE, etc.) TACE::GetType() { MCSVERIFY(m_pAce); BYTE type = ACCESS_ALLOWED_ACE_TYPE; if (m_pAce) type = m_pAce->Header.AceType; return type; } BYTE // ret- ace flags (OBJECT_INHERIT_ACE, etc.) TACE::GetFlags() { MCSVERIFY(m_pAce); BYTE flags = OBJECT_INHERIT_ACE; if (m_pAce) flags = m_pAce->Header.AceFlags; return flags; } DWORD // ret- access control mask TACE::GetMask() { MCSVERIFY(m_pAce); DWORD mask = 0; if (m_pAce) mask = m_pAce->Mask; return mask; } PSID // ret- sid for this ace TACE::GetSid() { MCSVERIFY(m_pAce); PSID pSid = NULL; if (m_pAce) pSid = &m_pAce->SidStart; return pSid; } WORD // ret- size of the ace, in bytes TACE::GetSize() { MCSVERIFY(m_pAce); WORD size = 0; if (m_pAce) size = m_pAce->Header.AceSize; return size; } BOOL TACE::SetType( BYTE newType // in -new type for ace ) { MCSVERIFY(m_pAce); if (!m_pAce) return FALSE; MCSASSERT( newType=ACCESS_ALLOWED_ACE_TYPE || newType==ACCESS_DENIED_ACE_TYPE || newType==SYSTEM_AUDIT_ACE_TYPE ); m_pAce->Header.AceType = newType; return TRUE; } BOOL TACE::SetFlags( BYTE newFlags // in - new flags for ace ) { MCSVERIFY(m_pAce); if (!m_pAce) return FALSE; m_pAce->Header.AceFlags = newFlags; return TRUE; } BOOL TACE::SetMask( DWORD newMask // in - new access control mask ) { MCSVERIFY(m_pAce); if (!m_pAce) return FALSE; m_pAce->Mask = newMask; return TRUE; } BOOL TACE::SetSid( PSID sid // in - new SID for this ace ) { BOOL result = FALSE; MCSVERIFY( m_pAce ); MCSASSERT( IsValidSid(sid) ); MCSASSERT( GetLengthSid(sid) == GetLengthSid(GetSid()) ); if (!m_pAce) return FALSE; if ( GetLengthSid(sid) <= GetLengthSid(GetSid()) ) { memcpy(&m_pAce->SidStart,sid,GetLengthSid(sid)); result = TRUE; } return result; } BOOL TACE::IsAccessAllowedAce() { MCSVERIFY(m_pAce); return ( GetType() == ACCESS_ALLOWED_ACE_TYPE ); }