/*--------------------------------------------------------------------------- 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(); } } // // This constructor allows for construction of an empty security descriptor. This is useful // if one wishes to create a security descriptor for an object that does not currently have // one such as a newly created share with default permissions. // TSD::TSD( SecuredObjectType objectType // in - type of object this security descriptor secures ) { m_absSD = (SECURITY_DESCRIPTOR*) malloc(sizeof(SECURITY_DESCRIPTOR)); if (m_absSD) { InitializeSecurityDescriptor(m_absSD, SECURITY_DESCRIPTOR_REVISION); } m_bOwnerChanged = FALSE; m_bGroupChanged = FALSE; m_bDACLChanged = FALSE; m_bSACLChanged = FALSE; m_bNeedToFreeSD = TRUE; m_bNeedToFreeOwner = FALSE; m_bNeedToFreeGroup = FALSE; m_bNeedToFreeDacl = FALSE; m_bNeedToFreeSacl = FALSE; m_ObjectType = objectType; } 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; } BOOL TSD::SetDacl( PACL pNewAcl, // in - new DACL BOOL present // in - flag, TRUE means DACL is present. ) { BOOL defaulted = FALSE; BOOL success = TRUE; if ( IsValidAcl(pNewAcl) ) { if ( m_bNeedToFreeDacl ) { PACL old = GetDacl(); if ( old != pNewAcl ) { free(old); } } if (! SetSecurityDescriptorDacl(m_absSD,present,pNewAcl,defaulted) ) { // DWORD rc = GetLastError(); success = FALSE; } m_bDACLChanged = TRUE; m_bNeedToFreeDacl = TRUE; } else { MCSVERIFY(FALSE); success = FALSE; } return success; } 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; } 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 // Note: ppAcl and pAce should be of the same revision level, otherwise AddAce will fail // Most usage of ACLAddAce includes two situations: // 1) pAce is from ppAcl or a copy of ace from ppAcl // 2) ppAcl is NULL and pAce is ACCESS_ALLOWED_ACE_TYPE 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 = NULL; BOOL bSuccess = TRUE; // indicates whether the ACE has been added into the ACL BOOL bOwnAcl = FALSE; // indicates whether the passed ACL is NULL so that we have to create it DWORD numaces = ACLGetNumAces(acl); DWORD freebytes = ACLGetFreeBytes(acl); // allocate a new ACL if it doesn't already exist if ( ! acl ) { bOwnAcl = TRUE; 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 { // if acl is created by us, we need to free it first if (bOwnAcl) { free(acl); acl = NULL; } // make a bigger acl newAcl = (ACL*)malloc(ACLGetBytesInUse(acl) + freebytes + currAceSize); if (!newAcl) { bSuccess = FALSE; } else { memcpy(newAcl,acl,ACLGetBytesInUse(acl) + freebytes); newAcl->AclSize +=currAceSize; acl = newAcl; } } if (bSuccess) { if (!pAce->GetBuffer()) { bSuccess = FALSE; } } if (bSuccess) { if (! AddAce(acl,acl->AclRevision,ndx,pAce->GetBuffer(),currAceSize) ) { bSuccess = FALSE; } } if (!bSuccess) { if (newAcl) free(newAcl); else if (bOwnAcl) // if acl is created by us, we need to free it // note: we only need to do this when newAcl is NULL free(acl); acl = NULL; } (*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; } DWORD TACE::SetSid( PSID sid // in - new SID for this ace ) { DWORD result = SET_SID_NOTLARGEENOUGH; MCSVERIFY( m_pAce ); MCSASSERT( IsValidSid(sid) ); if (!m_pAce) return SET_SID_FAILED; if ( GetLengthSid(sid) <= GetLengthSid(GetSid()) ) { memcpy(&m_pAce->SidStart,sid,GetLengthSid(sid)); result = SET_SID_SUCCEEDED; } return result; } BOOL TACE::IsAccessAllowedAce() { MCSVERIFY(m_pAce); return ( GetType() == ACCESS_ALLOWED_ACE_TYPE ); }