/* * Copyright (c) Likewise Software. All rights Reserved. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the license, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. You should have received a copy * of the GNU Lesser General Public License along with this program. If * not, see . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Module Name: * * security-sd-inherit.c * * Abstract: * * Security Descriptor Inheritance (SD) Functions in * Security Module. * * Authors: Gerald Carter * */ #include "security-includes.h" static NTSTATUS RtlpObjectSetOwner( IN OUT PSECURITY_DESCRIPTOR_ABSOLUTE pSecurityDescriptor, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pParentSecDesc, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pCreatorSecDesc, IN ULONG AutoInheritFlags, IN OPTIONAL PACCESS_TOKEN pUserToken ); static NTSTATUS RtlpObjectSetGroup( IN OUT PSECURITY_DESCRIPTOR_ABSOLUTE pSecurityDescriptor, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pParentSecDesc, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pCreatorSecDesc, IN ULONG AutoInheritFlags, IN OPTIONAL PACCESS_TOKEN pUserToken ); static NTSTATUS RtlpObjectSetDacl( IN OUT PSECURITY_DESCRIPTOR_ABSOLUTE pSecurityDescriptor, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pParentSecDesc, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pCreatorSecDesc, IN BOOLEAN bIsContainerObject, IN ULONG AutoInheritFlags, IN OPTIONAL PACCESS_TOKEN pUserToken, IN PGENERIC_MAPPING pGenericMap ); static NTSTATUS RtlpObjectSetSacl( IN OUT PSECURITY_DESCRIPTOR_ABSOLUTE pSecurityDescriptor, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pParentSecDesc, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pCreatorSecDesc, IN BOOLEAN bIsContainerObject, IN ULONG AutoInheritFlags, IN OPTIONAL PACCESS_TOKEN pUserToken, IN PGENERIC_MAPPING pGenericMap ); NTSTATUS RtlCreatePrivateObjectSecurityEx( IN OPTIONAL PSECURITY_DESCRIPTOR_RELATIVE pParentSecDesc, IN OPTIONAL PSECURITY_DESCRIPTOR_RELATIVE pCreatorSecDesc, OUT PSECURITY_DESCRIPTOR_RELATIVE *ppNewSecDesc, OUT PULONG pNewSecDescLength, IN OPTIONAL PVOID pObjectType, // Unused IN BOOLEAN bIsContainerObject, IN ULONG AutoInheritFlags, IN OPTIONAL PACCESS_TOKEN pUserToken, IN PGENERIC_MAPPING pGenericMap ) { NTSTATUS status = STATUS_SUCCESS; PSECURITY_DESCRIPTOR_RELATIVE pNewSecDesc = NULL; ULONG ulNewSecDescLength = 0; PSECURITY_DESCRIPTOR_ABSOLUTE pAbsParentSecDesc = NULL; PSECURITY_DESCRIPTOR_ABSOLUTE pAbsCreatorSecDesc = NULL; PSECURITY_DESCRIPTOR_ABSOLUTE pAbsNewSecDesc = NULL; if (!ppNewSecDesc || !pNewSecDescLength || !pGenericMap) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP_ON_STATUS(status); } *ppNewSecDesc = NULL; *pNewSecDescLength = 0; // pUserToken can only be NULL if bother the following flags // Are set in the AutoInheritFlags // (SEF_AVOID_OWNER_CHECK|SEF_AVOID_PRIVILEGE_CHECK) if (!((AutoInheritFlags & (SEF_AVOID_OWNER_CHECK|SEF_AVOID_PRIVILEGE_CHECK)) != (SEF_AVOID_OWNER_CHECK|SEF_AVOID_PRIVILEGE_CHECK)) && (pUserToken == NULL)) { status = STATUS_NO_TOKEN; GOTO_CLEANUP_ON_STATUS(status); } if (pParentSecDesc) { status = RtlpCreateAbsSecDescFromRelative( &pAbsParentSecDesc, pParentSecDesc); GOTO_CLEANUP_ON_STATUS(status); } if (pCreatorSecDesc) { status = RtlpCreateAbsSecDescFromRelative( &pAbsCreatorSecDesc, pCreatorSecDesc); GOTO_CLEANUP_ON_STATUS(status); } // Create new Absolute SecDesc status= LW_RTL_ALLOCATE( &pAbsNewSecDesc, SECURITY_DESCRIPTOR_ABSOLUTE, SECURITY_DESCRIPTOR_ABSOLUTE_MIN_SIZE); GOTO_CLEANUP_ON_STATUS(status); status = RtlCreateSecurityDescriptorAbsolute( pAbsNewSecDesc, SECURITY_DESCRIPTOR_REVISION); GOTO_CLEANUP_ON_STATUS(status); // Set Owner status = RtlpObjectSetOwner( pAbsNewSecDesc, pAbsParentSecDesc, pAbsCreatorSecDesc, AutoInheritFlags, pUserToken); GOTO_CLEANUP_ON_STATUS(status); // Set Group status = RtlpObjectSetGroup( pAbsNewSecDesc, pAbsParentSecDesc, pAbsCreatorSecDesc, AutoInheritFlags, pUserToken); GOTO_CLEANUP_ON_STATUS(status); // Set DACL status = RtlpObjectSetDacl( pAbsNewSecDesc, pAbsParentSecDesc, pAbsCreatorSecDesc, bIsContainerObject, AutoInheritFlags, pUserToken, pGenericMap); GOTO_CLEANUP_ON_STATUS(status); // Set SACL status = RtlpObjectSetSacl( pAbsNewSecDesc, pAbsParentSecDesc, pAbsCreatorSecDesc, bIsContainerObject, AutoInheritFlags, pUserToken, pGenericMap); GOTO_CLEANUP_ON_STATUS(status); // All done - convert to Self-Relative form and return status = RtlAbsoluteToSelfRelativeSD( pAbsNewSecDesc, NULL, &ulNewSecDescLength); if (status == STATUS_BUFFER_TOO_SMALL) { status = LW_RTL_ALLOCATE( &pNewSecDesc, SECURITY_DESCRIPTOR_RELATIVE, ulNewSecDescLength); GOTO_CLEANUP_ON_STATUS(status); status = RtlAbsoluteToSelfRelativeSD( pAbsNewSecDesc, pNewSecDesc, &ulNewSecDescLength); } GOTO_CLEANUP_ON_STATUS(status); *ppNewSecDesc = pNewSecDesc; *pNewSecDescLength = ulNewSecDescLength; cleanup: // Normal cleanup if (pAbsParentSecDesc) { RtlpFreeAbsoluteSecurityDescriptor(&pAbsParentSecDesc); } if (pAbsCreatorSecDesc) { RtlpFreeAbsoluteSecurityDescriptor(&pAbsCreatorSecDesc); } if (pAbsNewSecDesc) { RtlpFreeAbsoluteSecurityDescriptor(&pAbsNewSecDesc); } // Failure cleanup if (!NT_SUCCESS(status)) { LW_RTL_FREE(&pNewSecDesc); } return status; } static NTSTATUS RtlpObjectSetOwner( IN OUT PSECURITY_DESCRIPTOR_ABSOLUTE pSecurityDescriptor, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pParentSecDesc, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pCreatorSecDesc, IN ULONG AutoInheritFlags, IN OPTIONAL PACCESS_TOKEN pUserToken ) { NTSTATUS status = STATUS_SUCCESS; PSID pCreatorSecDescOwner = NULL; PSID pParentSecDescOwner = NULL; BOOLEAN bDefaulted = FALSE; PSID pOwner = NULL; union { TOKEN_OWNER TokenOwnerInfo; BYTE Buffer[SID_MAX_SIZE]; } TokenOwnerBuffer; PTOKEN_OWNER pTokenOwnerInformation = (PTOKEN_OWNER)&TokenOwnerBuffer; ULONG ulTokenOwnerLength = 0; // Use the CreatorSecDesc Owner if present and is // a member of the user's token if (pCreatorSecDesc) { status = RtlGetOwnerSecurityDescriptor( pCreatorSecDesc, &pCreatorSecDescOwner, &bDefaulted); GOTO_CLEANUP_ON_STATUS(status); if (pCreatorSecDescOwner && RtlIsSidMemberOfToken(pUserToken, pCreatorSecDescOwner)) { status = RtlDuplicateSid(&pOwner, pCreatorSecDescOwner); GOTO_CLEANUP_ON_STATUS(status); status = RtlSetOwnerSecurityDescriptor( pSecurityDescriptor, pOwner, bDefaulted); GOTO_CLEANUP(); } } // Check for Defaulting the owner from the parent Sec Desc if (AutoInheritFlags & SEF_DEFAULT_OWNER_FROM_PARENT) { status = RtlGetOwnerSecurityDescriptor( pParentSecDesc, &pParentSecDescOwner, &bDefaulted); GOTO_CLEANUP_ON_STATUS(status); if (pParentSecDescOwner) { status = RtlDuplicateSid(&pOwner, pCreatorSecDescOwner); GOTO_CLEANUP_ON_STATUS(status); status = RtlSetOwnerSecurityDescriptor( pSecurityDescriptor, pOwner, TRUE); GOTO_CLEANUP(); } } // Copy the owner of the ACCESS_TOKEN status = RtlQueryAccessTokenInformation( pUserToken, TokenOwner, (PVOID)pTokenOwnerInformation, sizeof(TokenOwnerBuffer), &ulTokenOwnerLength); GOTO_CLEANUP_ON_STATUS(status); status = RtlDuplicateSid(&pOwner, pTokenOwnerInformation->Owner); GOTO_CLEANUP_ON_STATUS(status); status = RtlSetOwnerSecurityDescriptor( pSecurityDescriptor, pOwner, TRUE); GOTO_CLEANUP_ON_STATUS(status); cleanup: // Failure cleanup if (!NT_SUCCESS(status)) { LW_RTL_FREE(&pOwner); } return status; } static NTSTATUS RtlpObjectSetGroup( IN OUT PSECURITY_DESCRIPTOR_ABSOLUTE pSecurityDescriptor, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pParentSecDesc, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pCreatorSecDesc, IN ULONG AutoInheritFlags, IN OPTIONAL PACCESS_TOKEN pUserToken ) { NTSTATUS status = STATUS_SUCCESS; PSID pCreatorSecDescGroup = NULL; PSID pParentSecDescGroup = NULL; BOOLEAN bDefaulted = FALSE; PSID pGroup = NULL; struct { TOKEN_PRIMARY_GROUP TokenPrimaryGroupInfo; BYTE Buffer[SID_MAX_SIZE]; } TokenPrimaryGroupBuffer; PTOKEN_PRIMARY_GROUP pTokenPrimaryGroupInfo = (PTOKEN_PRIMARY_GROUP)&TokenPrimaryGroupBuffer; ULONG ulTokenPrimaryGroupLength = 0; // Use the CreatorSecDesc Group if present and if a // member of the user's token if (pCreatorSecDesc) { status = RtlGetGroupSecurityDescriptor( pCreatorSecDesc, &pCreatorSecDescGroup, &bDefaulted); GOTO_CLEANUP_ON_STATUS(status); if (pCreatorSecDescGroup && RtlIsSidMemberOfToken(pUserToken, pCreatorSecDescGroup)) { status = RtlDuplicateSid(&pGroup, pCreatorSecDescGroup); GOTO_CLEANUP_ON_STATUS(status); status = RtlSetGroupSecurityDescriptor( pSecurityDescriptor, pGroup, bDefaulted); GOTO_CLEANUP(); } } // Check for Defaulting the owner from the parent Sec Desc if (AutoInheritFlags & SEF_DEFAULT_GROUP_FROM_PARENT) { status = RtlGetGroupSecurityDescriptor( pParentSecDesc, &pParentSecDescGroup, &bDefaulted); GOTO_CLEANUP_ON_STATUS(status); if (pParentSecDescGroup) { status = RtlDuplicateSid(&pGroup, pCreatorSecDescGroup); GOTO_CLEANUP_ON_STATUS(status); status = RtlSetGroupSecurityDescriptor( pSecurityDescriptor, pGroup, TRUE); GOTO_CLEANUP(); } } // Copy the Group SID from the Token status = RtlQueryAccessTokenInformation( pUserToken, TokenPrimaryGroup, (PVOID)pTokenPrimaryGroupInfo, sizeof(TokenPrimaryGroupBuffer), &ulTokenPrimaryGroupLength); GOTO_CLEANUP_ON_STATUS(status); status = RtlDuplicateSid(&pGroup, pTokenPrimaryGroupInfo->PrimaryGroup); GOTO_CLEANUP_ON_STATUS(status); status = RtlSetGroupSecurityDescriptor( pSecurityDescriptor, pGroup, TRUE); GOTO_CLEANUP_ON_STATUS(status); cleanup: // Failure cleanup if (!NT_SUCCESS(status)) { LW_RTL_FREE(&pGroup); } return status; } static NTSTATUS RtlpDuplicateDacl( PACL *ppNewDacl, PACL pSrcDacl ); static NTSTATUS RtlpObjectInheritSecurity( OUT PACL *ppNewDacl, OUT PBOOLEAN pbIsNewDaclDefaulted, IN OPTIONAL PACL pParentDacl, IN BOOLEAN bParentIsDaclDefaulted, IN OPTIONAL PACL pCreatorDacl, IN BOOLEAN bCreatorIsDaclDefaulted, IN BOOLEAN bIsContainerObject, IN PACCESS_TOKEN pUserToken, IN PGENERIC_MAPPING pGenericMap ); static NTSTATUS RtlpObjectSetDacl( IN OUT PSECURITY_DESCRIPTOR_ABSOLUTE pSecurityDescriptor, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pParentSecDesc, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pCreatorSecDesc, IN BOOLEAN bIsContainerObject, IN ULONG AutoInheritFlags, IN OPTIONAL PACCESS_TOKEN pUserToken, IN PGENERIC_MAPPING pGenericMap ) { NTSTATUS status = STATUS_SUCCESS; PACL pParentDacl = NULL; PACL pCreatorDacl = NULL; PACL pFinalDacl = NULL; BOOLEAN bCreatorIsDaclPresent = FALSE; BOOLEAN bCreatorIsDaclDefaulted = FALSE; BOOLEAN bParentIsDaclPresent = FALSE; BOOLEAN bParentIsDaclDefaulted = FALSE; BOOLEAN bFinalIsDaclDefaulted = FALSE; SECURITY_DESCRIPTOR_CONTROL CreatorSecDescControl = 0; PBYTE pBuffer = NULL; PTOKEN_DEFAULT_DACL pTokenDefaultDaclInformation = NULL; ULONG ulTokenDefaultDaclLength = 0; ULONG ulTokenDefaultDaclSize = 0; // Pull the DACLs so we have something to work with if (pParentSecDesc) { status = RtlGetDaclSecurityDescriptor( pParentSecDesc, &bParentIsDaclPresent, &pParentDacl, &bParentIsDaclDefaulted); GOTO_CLEANUP_ON_STATUS(status); } if (pCreatorSecDesc) { status = RtlGetDaclSecurityDescriptor( pCreatorSecDesc, &bCreatorIsDaclPresent, &pCreatorDacl, &bCreatorIsDaclDefaulted); GOTO_CLEANUP_ON_STATUS(status); status = RtlGetSecurityDescriptorControl( pCreatorSecDesc, &CreatorSecDescControl, NULL); GOTO_CLEANUP_ON_STATUS(status); } // If the creator wants to block inheritance, we are done if (bCreatorIsDaclPresent && (CreatorSecDescControl & SE_DACL_PROTECTED)) { status = RtlpDuplicateDacl(&pFinalDacl, pCreatorDacl); GOTO_CLEANUP_ON_STATUS(status); bFinalIsDaclDefaulted = FALSE; } // Do the inheritance if we have at least one SecDesc to work with else if (bParentIsDaclPresent || bCreatorIsDaclPresent) { status = RtlpObjectInheritSecurity( &pFinalDacl, &bFinalIsDaclDefaulted, bParentIsDaclPresent ? pParentDacl : NULL, bParentIsDaclDefaulted, bCreatorIsDaclPresent ? pCreatorDacl : NULL, bCreatorIsDaclDefaulted, bIsContainerObject, pUserToken, pGenericMap); GOTO_CLEANUP_ON_STATUS(status); } // Use the Token Default Dacl IFF we don't have a valid DACL with // at least 1 ACE if (!pFinalDacl || (RtlGetAclAceCount(pFinalDacl) == 0)) { if (pFinalDacl) { LW_RTL_FREE(&pFinalDacl); } status = RtlQueryAccessTokenInformation( pUserToken, TokenDefaultDacl, NULL, 0, &ulTokenDefaultDaclLength); if (status == STATUS_BUFFER_TOO_SMALL) { ulTokenDefaultDaclSize = ulTokenDefaultDaclLength; status = LW_RTL_ALLOCATE(&pBuffer, VOID, ulTokenDefaultDaclSize); GOTO_CLEANUP_ON_STATUS(status); status = RtlQueryAccessTokenInformation( pUserToken, TokenDefaultDacl, pBuffer, ulTokenDefaultDaclLength, &ulTokenDefaultDaclLength); } GOTO_CLEANUP_ON_STATUS(status); pTokenDefaultDaclInformation = (PTOKEN_DEFAULT_DACL)pBuffer; // Call RtlpObjectInheritSecurity() again but with the Token // Default DACL as the creator Dacl so that it can handle // adding the ACEs and mapping to the object specific bits status = RtlpObjectInheritSecurity( &pFinalDacl, &bFinalIsDaclDefaulted, NULL, FALSE, pTokenDefaultDaclInformation->DefaultDacl, TRUE, bIsContainerObject, pUserToken, pGenericMap); GOTO_CLEANUP_ON_STATUS(status); bFinalIsDaclDefaulted = TRUE; } // Finally set the new DACL in the outgoing Seccurity Descriptor status = RtlSetDaclSecurityDescriptor( pSecurityDescriptor, pFinalDacl ? TRUE : FALSE, pFinalDacl, bFinalIsDaclDefaulted); GOTO_CLEANUP_ON_STATUS(status); cleanup: // Failure cleanup if (!NT_SUCCESS(status)) { LW_RTL_FREE(&pFinalDacl); } LW_RTL_FREE(&pBuffer); return status; } static NTSTATUS RtlpDuplicateDacl( PACL *ppNewDacl, PACL pSrcDacl ) { NTSTATUS status = STATUS_SUCCESS; PACL pDacl = NULL; USHORT usDaclSize = 0; usDaclSize = RtlGetAclSize(pSrcDacl); status = LW_RTL_ALLOCATE(&pDacl, VOID, usDaclSize); GOTO_CLEANUP_ON_STATUS(status); RtlCopyMemory(pDacl, pSrcDacl, usDaclSize); *ppNewDacl = pDacl; cleanup: return status; } static NTSTATUS RtlpObjectDaclAssignSecurity( OUT PACL *ppObjectDacl, IN PACCESS_TOKEN pUserToken, IN PACL pSrcDacl, IN PGENERIC_MAPPING pGenericMap ) { NTSTATUS status = STATUS_SUCCESS; PACL pDacl = NULL; ACCESS_MASK mask = 0; USHORT aclSize = 0; USHORT srcAclSizeUsed = 0; USHORT aceOffset = 0; PACE_HEADER aceHeader = NULL; PACCESS_ALLOWED_ACE aceAllow = NULL; PACCESS_DENIED_ACE aceDeny = NULL; PSID aceSid = NULL; union { SID sid; BYTE buffer[SID_MAX_SIZE]; } CreatorOwner; union { SID sid; BYTE buffer[SID_MAX_SIZE]; } CreatorGroup; ULONG CreatorOwnerSidSize = sizeof(CreatorOwner.buffer); ULONG CreatorGroupSidSize = sizeof(CreatorGroup.buffer); union { TOKEN_OWNER TokenOwnerInfo; BYTE Buffer[SID_MAX_SIZE]; } TokenOwnerBuffer; PTOKEN_OWNER pTokenOwnerInformation = (PTOKEN_OWNER)&TokenOwnerBuffer; ULONG ulTokenOwnerLength = 0; struct { TOKEN_PRIMARY_GROUP TokenPrimaryGroupInfo; BYTE Buffer[SID_MAX_SIZE]; } TokenPrimaryGroupBuffer; PTOKEN_PRIMARY_GROUP pTokenPrimaryGroupInfo = (PTOKEN_PRIMARY_GROUP)&TokenPrimaryGroupBuffer; ULONG ulTokenPrimaryGroupLength = 0; aclSize = ACL_HEADER_SIZE + (RtlGetAclAceCount(pSrcDacl) * (sizeof(ACCESS_ALLOWED_ACE) + SID_MAX_SIZE)); if (aclSize == 0) { status = STATUS_INVALID_SECURITY_DESCR; GOTO_CLEANUP_ON_STATUS(status); } // Build the "CREATOR OWNER" and "CREATOR GROUP" sids for comparison status = RtlCreateWellKnownSid( WinCreatorOwnerSid, NULL, &CreatorOwner.sid, &CreatorOwnerSidSize); GOTO_CLEANUP_ON_STATUS(status); status = RtlCreateWellKnownSid( WinCreatorGroupSid, NULL, &CreatorGroup.sid, &CreatorGroupSidSize); GOTO_CLEANUP_ON_STATUS(status); status = RtlQueryAccessTokenInformation( pUserToken, TokenOwner, (PVOID)pTokenOwnerInformation, sizeof(TokenOwnerBuffer), &ulTokenOwnerLength); GOTO_CLEANUP_ON_STATUS(status); status = RtlQueryAccessTokenInformation( pUserToken, TokenPrimaryGroup, (PVOID)pTokenPrimaryGroupInfo, sizeof(TokenPrimaryGroupBuffer), &ulTokenPrimaryGroupLength); GOTO_CLEANUP_ON_STATUS(status); status = LW_RTL_ALLOCATE(&pDacl, VOID, aclSize); GOTO_CLEANUP_ON_STATUS(status); status = RtlCreateAcl(pDacl, aclSize, ACL_REVISION); GOTO_CLEANUP_ON_STATUS(status); srcAclSizeUsed = RtlGetAclSizeUsed(pSrcDacl); while (TRUE) { status = RtlIterateAce( pSrcDacl, srcAclSizeUsed, &aceOffset, &aceHeader); if (status == STATUS_NO_MORE_ENTRIES) { break; } GOTO_CLEANUP_ON_STATUS(status); switch (aceHeader->AceType) { case ACCESS_ALLOWED_ACE_TYPE: { aceAllow = (PACCESS_ALLOWED_ACE)aceHeader; mask = aceAllow->Mask; aceSid = (PSID)&aceAllow->SidStart; RtlMapGenericMask(&mask, pGenericMap); if (RtlEqualSid(aceSid, &CreatorOwner.sid)) { aceSid = pTokenOwnerInformation->Owner; } else if (RtlEqualSid(aceSid, &CreatorGroup.sid)) { aceSid = pTokenPrimaryGroupInfo->PrimaryGroup; } status = RtlAddAccessAllowedAceEx( pDacl, ACL_REVISION, aceAllow->Header.AceFlags, mask, aceSid); GOTO_CLEANUP_ON_STATUS(status); } break; case ACCESS_DENIED_ACE_TYPE: { aceDeny = (PACCESS_DENIED_ACE)aceHeader; mask = aceDeny->Mask; aceSid = (PSID)&aceDeny->SidStart; RtlMapGenericMask(&mask, pGenericMap); if (RtlEqualSid(aceSid, &CreatorOwner.sid)) { aceSid = pTokenOwnerInformation->Owner; } else if (RtlEqualSid(aceSid, &CreatorGroup.sid)) { aceSid = pTokenPrimaryGroupInfo->PrimaryGroup; } status = RtlAddAccessDeniedAceEx( pDacl, ACL_REVISION, aceDeny->Header.AceFlags, mask, aceSid); GOTO_CLEANUP_ON_STATUS(status); } break; default: // ignore break; } } *ppObjectDacl = pDacl; status = STATUS_SUCCESS; cleanup: if (!NT_SUCCESS(status)) { LW_RTL_FREE(&pDacl); } return status; } static NTSTATUS RtlpObjectInheritSecurity( OUT PACL *ppNewDacl, OUT PBOOLEAN pbIsNewDaclDefaulted, IN OPTIONAL PACL pParentDacl, IN BOOLEAN bParentIsDaclDefaulted, IN OPTIONAL PACL pCreatorDacl, IN BOOLEAN bCreatorIsDaclDefaulted, IN BOOLEAN bIsContainerObject, IN PACCESS_TOKEN pUserToken, IN PGENERIC_MAPPING pGenericMap ) { NTSTATUS status = STATUS_SUCCESS; PACL pDacl = NULL; USHORT usDaclSize = 0; USHORT usCreatorNumAces = 0; USHORT usParentNumAces = 0; USHORT i = 0; PACE_HEADER pAceHeader = NULL; PACCESS_ALLOWED_ACE pAllowAce = NULL; PACCESS_DENIED_ACE pDenyAce = NULL; ACCESS_MASK mask = 0; union { SID sid; BYTE buffer[SID_MAX_SIZE]; } CreatorOwner; union { SID sid; BYTE buffer[SID_MAX_SIZE]; } CreatorGroup; ULONG CreatorOwnerSidSize = sizeof(CreatorOwner.buffer); ULONG CreatorGroupSidSize = sizeof(CreatorGroup.buffer); union { TOKEN_OWNER TokenOwnerInfo; BYTE Buffer[SID_MAX_SIZE]; } TokenOwnerBuffer; PTOKEN_OWNER pTokenOwnerInformation = (PTOKEN_OWNER)&TokenOwnerBuffer; ULONG ulTokenOwnerLength = 0; struct { TOKEN_PRIMARY_GROUP TokenPrimaryGroupInfo; BYTE Buffer[SID_MAX_SIZE]; } TokenPrimaryGroupBuffer; PTOKEN_PRIMARY_GROUP pTokenPrimaryGroupInfo = (PTOKEN_PRIMARY_GROUP)&TokenPrimaryGroupBuffer; ULONG ulTokenPrimaryGroupLength = 0; // Inheritance Algorithm: // // (a) If defaulted creator DACL and no parent, just use defaulted // creator DACL, // // else // // (b) Add in all creator DACL entries // (c) Add in inheritable ACEs from parent // i. Skip if is a container and is NOT a container inherit // ace, or if is NOT a container and is NOT a object // (non-container) inherit ace // ii. For containers, add propagatable inheritance entries // iii. Add inherited ace, applying generic map // // Case (a) if (!pParentDacl && bCreatorIsDaclDefaulted) { status = RtlpObjectDaclAssignSecurity( &pDacl, pUserToken, pCreatorDacl, pGenericMap); GOTO_CLEANUP_ON_STATUS(status); *pbIsNewDaclDefaulted = TRUE; *ppNewDacl = pDacl; GOTO_CLEANUP(); } if (pCreatorDacl) { usDaclSize = ACL_HEADER_SIZE + (RtlGetAclAceCount(pCreatorDacl) * (sizeof(ACCESS_ALLOWED_ACE) + SID_MAX_SIZE)); } if (pParentDacl) { usDaclSize += ACL_HEADER_SIZE + (RtlGetAclAceCount(pParentDacl) * (sizeof(ACCESS_ALLOWED_ACE) + SID_MAX_SIZE)); } if (usDaclSize <= 0) { status = STATUS_INVALID_SECURITY_DESCR; GOTO_CLEANUP_ON_STATUS(status); } // Build the "CREATOR OWNER" and "CREATOR GROUP" sids for comparison status = RtlCreateWellKnownSid( WinCreatorOwnerSid, NULL, &CreatorOwner.sid, &CreatorOwnerSidSize); GOTO_CLEANUP_ON_STATUS(status); status = RtlCreateWellKnownSid( WinCreatorGroupSid, NULL, &CreatorGroup.sid, &CreatorGroupSidSize); GOTO_CLEANUP_ON_STATUS(status); status = RtlQueryAccessTokenInformation( pUserToken, TokenOwner, (PVOID)pTokenOwnerInformation, sizeof(TokenOwnerBuffer), &ulTokenOwnerLength); GOTO_CLEANUP_ON_STATUS(status); status = RtlQueryAccessTokenInformation( pUserToken, TokenPrimaryGroup, (PVOID)pTokenPrimaryGroupInfo, sizeof(TokenPrimaryGroupBuffer), &ulTokenPrimaryGroupLength); GOTO_CLEANUP_ON_STATUS(status); status = LW_RTL_ALLOCATE(&pDacl, VOID, usDaclSize); GOTO_CLEANUP_ON_STATUS(status); status = RtlCreateAcl(pDacl, usDaclSize, ACL_REVISION); GOTO_CLEANUP_ON_STATUS(status); // Case (b) - Direct ACEs first if (pCreatorDacl) { usCreatorNumAces = RtlGetAclAceCount(pCreatorDacl); } for (i=0; iAceType) { case ACCESS_ALLOWED_ACE_TYPE: pAllowAce = (PACCESS_ALLOWED_ACE)pAceHeader; mask = pAllowAce->Mask; RtlMapGenericMask(&mask, pGenericMap); status = RtlAddAccessAllowedAceEx( pDacl, ACL_REVISION, pAllowAce->Header.AceFlags, mask, (PSID)&pAllowAce->SidStart); GOTO_CLEANUP_ON_STATUS(status); break; case ACCESS_DENIED_ACE_TYPE: pDenyAce = (PACCESS_DENIED_ACE)pAceHeader; mask = pDenyAce->Mask; RtlMapGenericMask(&mask, pGenericMap); status = RtlAddAccessDeniedAceEx( pDacl, ACL_REVISION, pDenyAce->Header.AceFlags, mask, (PSID)&pDenyAce->SidStart); GOTO_CLEANUP_ON_STATUS(status); break; default: // Skip all other types break; } } // Case (c) - Inheritable ACEs if (pParentDacl) { usParentNumAces = RtlGetAclAceCount(pParentDacl); } for (i=0; iAceFlags & CONTAINER_INHERIT_ACE)) || (!bIsContainerObject && !(pAceHeader->AceFlags & OBJECT_INHERIT_ACE))) { continue; } // See if the inherit ACE should continue to be propagated. // If so, then copy it switch(pAceHeader->AceType) { case ACCESS_ALLOWED_ACE_TYPE: { pAllowAce = (PACCESS_ALLOWED_ACE)pAceHeader; mask = pAllowAce->Mask; if (bIsContainerObject && !(pAceHeader->AceFlags & NO_PROPAGATE_INHERIT_ACE)) { status = RtlAddAccessAllowedAceEx( pDacl, ACL_REVISION, pAllowAce->Header.AceFlags, pAllowAce->Mask, (PSID)&pAllowAce->SidStart); GOTO_CLEANUP_ON_STATUS(status); } } break; case ACCESS_DENIED_ACE_TYPE: { pDenyAce = (PACCESS_DENIED_ACE)pAceHeader; mask = pDenyAce->Mask; if (bIsContainerObject && !(pAceHeader->AceFlags & NO_PROPAGATE_INHERIT_ACE)) { status = RtlAddAccessDeniedAceEx( pDacl, ACL_REVISION, pDenyAce->Header.AceFlags, pDenyAce->Mask, (PSID)&pDenyAce->SidStart); GOTO_CLEANUP_ON_STATUS(status); } } break; default: // Skip all other types continue; } // Map the generic bits to specific bits and remove inherit flags RtlMapGenericMask(&mask, pGenericMap); AceFlags = pAceHeader->AceFlags; AceFlags &= ~(CONTAINER_INHERIT_ACE| OBJECT_INHERIT_ACE| INHERIT_ONLY_ACE); AceFlags |= INHERITED_ACE; // Set the inherited direct ACE // Deal with CREATOR OWNER and CREATOR GROUP switch(pAceHeader->AceType) { case ACCESS_ALLOWED_ACE_TYPE: pAllowAce = (PACCESS_ALLOWED_ACE)pAceHeader; pAceSid = (PSID)&pAllowAce->SidStart; if (RtlEqualSid(pAceSid, &CreatorOwner.sid)) { pAceSid = pTokenOwnerInformation->Owner; } else if (RtlEqualSid(pAceSid, &CreatorGroup.sid)) { pAceSid = pTokenPrimaryGroupInfo->PrimaryGroup; } status = RtlAddAccessAllowedAceEx( pDacl, ACL_REVISION, AceFlags, mask, pAceSid); GOTO_CLEANUP_ON_STATUS(status); break; case ACCESS_DENIED_ACE_TYPE: pDenyAce = (PACCESS_DENIED_ACE)pAceHeader; pAceSid = (PSID)&pDenyAce->SidStart; if (RtlEqualSid(pAceSid, &CreatorOwner.sid)) { pAceSid = pTokenOwnerInformation->Owner; } else if (RtlEqualSid(pAceSid, &CreatorGroup.sid)) { pAceSid = pTokenPrimaryGroupInfo->PrimaryGroup; } status = RtlAddAccessDeniedAceEx( pDacl, ACL_REVISION, AceFlags, mask, pAceSid); GOTO_CLEANUP_ON_STATUS(status); break; default: // Skip all other types continue; } } *pbIsNewDaclDefaulted = bCreatorIsDaclDefaulted; *ppNewDacl = pDacl; status = STATUS_SUCCESS; cleanup: // Failure cleanup if (!NT_SUCCESS(status)) { LW_RTL_FREE(&pDacl); } return status; } static NTSTATUS RtlpObjectSetSacl( IN OUT PSECURITY_DESCRIPTOR_ABSOLUTE pSecurityDescriptor, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pParentSecDesc, IN OPTIONAL PSECURITY_DESCRIPTOR_ABSOLUTE pCreatorSecDesc, IN BOOLEAN bIsContainerObject, IN ULONG AutoInheritFlags, IN OPTIONAL PACCESS_TOKEN pUserToken, IN PGENERIC_MAPPING pGenericMap ) { // No SACL support currently return STATUS_SUCCESS; } /* local variables: mode: c c-basic-offset: 4 indent-tabs-mode: nil tab-width: 4 end: */