NTSTATUS PvfsAccessCheckFile( PACCESS_TOKEN pToken, PPVFS_FILE_NAME FileName, ACCESS_MASK Desired, ACCESS_MASK *pGranted ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; ACCESS_MASK AccessMask = 0; ACCESS_MASK GrantedAccess = 0; PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc = NULL; BYTE pRelativeSecDescBuffer[SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE]; ULONG ulRelativeSecDescLength = SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE; PSECURITY_DESCRIPTOR_ABSOLUTE pParentSecDesc = NULL; BYTE pParentRelSecDescBuffer[SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE]; ULONG ulParentRelSecDescLength = SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE; BOOLEAN bGranted = FALSE; SECURITY_INFORMATION SecInfo = (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION); PSID pOwner = NULL; BOOLEAN bOwnerDefaulted = FALSE; BOOLEAN bWantsDelete = FALSE; BOOLEAN bWantsMaximumAccess = FALSE; PPVFS_FILE_NAME parentDirectoryName = NULL; PPVFS_FILE_NAME relativeFileName = NULL; BAIL_ON_INVALID_PTR(pToken, ntError); BAIL_ON_INVALID_PTR(pGranted, ntError); // Check the file object itself ntError = PvfsGetSecurityDescriptorFilename( PvfsGetCStringBaseFileName(FileName), SecInfo, (PSECURITY_DESCRIPTOR_RELATIVE)((PVOID)pRelativeSecDescBuffer), &ulRelativeSecDescLength); BAIL_ON_NT_STATUS(ntError); ntError = PvfsSecurityAclSelfRelativeToAbsoluteSD( &pSecDesc, (PSECURITY_DESCRIPTOR_RELATIVE)((PVOID)pRelativeSecDescBuffer)); BAIL_ON_NT_STATUS(ntError); // Tests against NTFS/Win2003R2 show that the file/directory object // owner is always granted FILE_READ_ATTRIBUTE ntError = RtlGetOwnerSecurityDescriptor( pSecDesc, &pOwner, &bOwnerDefaulted); BAIL_ON_NT_STATUS(ntError); if (RtlIsSidMemberOfToken(pToken, pOwner)) { ClearFlag(Desired, FILE_READ_ATTRIBUTES); SetFlag(GrantedAccess, FILE_READ_ATTRIBUTES); } // Check for access rights. We'll deal with DELETE separately since that // could be granted by the parent directory security descriptor if (Desired & DELETE) { bWantsDelete = TRUE; ClearFlag(Desired, DELETE); } if (Desired & MAXIMUM_ALLOWED) { bWantsMaximumAccess = TRUE; } bGranted = RtlAccessCheck( pSecDesc, pToken, Desired, GrantedAccess, &gPvfsDriverState.GenericSecurityMap, &AccessMask, &ntError); if (!bGranted) { BAIL_ON_NT_STATUS(ntError); } GrantedAccess = AccessMask; // See if the file object security descriptor grants DELETE // Only continue when checking for MAXIMUM_ALLOWED if we haven't been // granted DELETE already if (bWantsDelete || (bWantsMaximumAccess && !(GrantedAccess & DELETE))) { AccessMask = 0; bGranted = RtlAccessCheck( pSecDesc, pToken, DELETE, GrantedAccess, &gPvfsDriverState.GenericSecurityMap, &AccessMask, &ntError); if (!bGranted) { ntError = PvfsSplitFileNamePath( &parentDirectoryName, &relativeFileName, FileName); BAIL_ON_NT_STATUS(ntError); ntError = PvfsGetSecurityDescriptorFilename( PvfsGetCStringBaseFileName(parentDirectoryName), SecInfo, (PSECURITY_DESCRIPTOR_RELATIVE)((PVOID)pParentRelSecDescBuffer), &ulParentRelSecDescLength); BAIL_ON_NT_STATUS(ntError); ntError = PvfsSecurityAclSelfRelativeToAbsoluteSD( &pParentSecDesc, (PSECURITY_DESCRIPTOR_RELATIVE)((PVOID)pParentRelSecDescBuffer)); BAIL_ON_NT_STATUS(ntError); AccessMask = 0; bGranted = RtlAccessCheck( pParentSecDesc, pToken, FILE_DELETE_CHILD, 0, &gPvfsDriverState.GenericSecurityMap, &AccessMask, &ntError); // This is a hard failure unless we are just trying to determine // what the maximum allowed access would be if (!bGranted && !bWantsMaximumAccess) { BAIL_ON_NT_STATUS(ntError); } AccessMask = DELETE; } // Combine directory and file object granted permissions AccessMask |= GrantedAccess; } *pGranted = AccessMask; ntError = STATUS_SUCCESS; error: if (parentDirectoryName) { PvfsFreeFileName(relativeFileName); } if (relativeFileName) { PvfsFreeFileName(parentDirectoryName); } if (pParentSecDesc) { PvfsFreeAbsoluteSecurityDescriptor(&pParentSecDesc); } if (pSecDesc) { PvfsFreeAbsoluteSecurityDescriptor(&pSecDesc); } return ntError; }
NTSTATUS PvfsAccessCheckFileEnumerate( PPVFS_CCB pCcb, PCSTR pszRelativeFilename ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PACCESS_TOKEN pToken = pCcb->pUserToken; PSTR pszFilename = NULL; ACCESS_MASK AccessMask = 0; ACCESS_MASK GrantedAccess = 0; PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc = NULL; BYTE pRelativeSecDescBuffer[SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE]; ULONG ulRelativeSecDescLength = SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE; BOOLEAN bGranted = FALSE; SECURITY_INFORMATION SecInfo = (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION); ACCESS_MASK Desired = (FILE_READ_ATTRIBUTES| FILE_READ_EA| FILE_READ_DATA| READ_CONTROL); PSID pOwner = NULL; BOOLEAN bOwnerDefaulted = FALSE; /* Create the absolute path */ ntError = LwRtlCStringAllocatePrintf( &pszFilename, "%s/%s", pCcb->pScb->pOwnerFcb->pszFilename, pszRelativeFilename); BAIL_ON_NT_STATUS(ntError); /* Check the file object itself */ ntError = PvfsGetSecurityDescriptorFilename( pszFilename, SecInfo, (PSECURITY_DESCRIPTOR_RELATIVE)((PVOID)pRelativeSecDescBuffer), &ulRelativeSecDescLength); BAIL_ON_NT_STATUS(ntError); ntError = PvfsSecurityAclSelfRelativeToAbsoluteSD( &pSecDesc, (PSECURITY_DESCRIPTOR_RELATIVE)((PVOID)pRelativeSecDescBuffer)); BAIL_ON_NT_STATUS(ntError); // Tests against NTFS/Win2003R2 show that the file/directory object // owner is always granted FILE_READ_ATTRIBUTE ntError = RtlGetOwnerSecurityDescriptor( pSecDesc, &pOwner, &bOwnerDefaulted); BAIL_ON_NT_STATUS(ntError); if (RtlIsSidMemberOfToken(pToken, pOwner)) { ClearFlag(Desired, FILE_READ_ATTRIBUTES); SetFlag(GrantedAccess, FILE_READ_ATTRIBUTES); } /* Now check access */ bGranted = RtlAccessCheck( pSecDesc, pToken, Desired, GrantedAccess, &gPvfsDriverState.GenericSecurityMap, &AccessMask, &ntError); if (!bGranted) { ntError = STATUS_ACCESS_DENIED; } BAIL_ON_NT_STATUS(ntError); cleanup: if (pszFilename) { LwRtlCStringFree(&pszFilename); } if (pSecDesc) { PvfsFreeAbsoluteSecurityDescriptor(&pSecDesc); } return ntError; error: goto cleanup; }
NTSTATUS PvfsSetSecurityDescriptorFile( IN PPVFS_CCB pCcb, IN SECURITY_INFORMATION SecInfo, IN PSECURITY_DESCRIPTOR_RELATIVE pSecDesc, IN ULONG SecDescLength ) { NTSTATUS ntError = STATUS_ACCESS_DENIED; PSECURITY_DESCRIPTOR_RELATIVE pFinalSecDesc = NULL; ULONG ulFinalSecDescLength = 0; SECURITY_INFORMATION SecInfoAll = (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION); BYTE pCurrentSecDescBuffer[SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE]; ULONG ulCurrentSecDescLength = SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE; BYTE pNewSecDescBuffer[SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE]; ULONG ulNewSecDescLength = SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE; PSECURITY_DESCRIPTOR_ABSOLUTE pIncAbsSecDesc = NULL; union { TOKEN_OWNER TokenOwnerInfo; BYTE Buffer[SID_MAX_SIZE]; } TokenOwnerBuffer; PTOKEN_OWNER pTokenOwnerInformation = (PTOKEN_OWNER)&TokenOwnerBuffer; ULONG ulTokenOwnerLength = 0; union { SID Sid; BYTE Buffer[SID_MAX_SIZE]; } LocalSystemSidBuffer; PSID pLocalSystemSid = (PSID)&LocalSystemSidBuffer; ULONG ulLocalSystemSidLength = sizeof(LocalSystemSidBuffer); memset(pCurrentSecDescBuffer, 0, SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE); memset(pNewSecDescBuffer, 0, SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE); memset(TokenOwnerBuffer.Buffer, 0, SID_MAX_SIZE); /* Sanity checks */ if (SecInfo == 0) { ntError = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntError); } /* If the new Security Descriptor contains owner or group SID information, berify that the user's ACCESS_TOKEN contains the SID as a member */ if (SecInfo & (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION)) { PSID pOwner = NULL; PSID pGroup = NULL; BOOLEAN bDefaulted = FALSE; ntError = PvfsSecurityAclSelfRelativeToAbsoluteSD( &pIncAbsSecDesc, pSecDesc); BAIL_ON_NT_STATUS(ntError); ntError = RtlQueryAccessTokenInformation( pCcb->pUserToken, TokenOwner, (PVOID)pTokenOwnerInformation, sizeof(TokenOwnerBuffer), &ulTokenOwnerLength); BAIL_ON_NT_STATUS(ntError); ntError = RtlCreateWellKnownSid( WinLocalSystemSid, NULL, pLocalSystemSid, &ulLocalSystemSidLength); BAIL_ON_NT_STATUS(ntError); if (SecInfo & OWNER_SECURITY_INFORMATION) { ntError = RtlGetOwnerSecurityDescriptor( pIncAbsSecDesc, &pOwner, &bDefaulted); BAIL_ON_NT_STATUS(ntError); if (!RtlIsSidMemberOfToken(pCcb->pUserToken, pOwner) && !RtlEqualSid(pLocalSystemSid, pTokenOwnerInformation->Owner)) { ntError = STATUS_ACCESS_DENIED; BAIL_ON_NT_STATUS(ntError); } } if (SecInfo & GROUP_SECURITY_INFORMATION) { ntError = RtlGetGroupSecurityDescriptor( pIncAbsSecDesc, &pGroup, &bDefaulted); BAIL_ON_NT_STATUS(ntError); if (!RtlIsSidMemberOfToken(pCcb->pUserToken, pGroup) && !RtlEqualSid(pLocalSystemSid, pTokenOwnerInformation->Owner)) { ntError = STATUS_ACCESS_DENIED; BAIL_ON_NT_STATUS(ntError); } } } if (SecInfo == SecInfoAll) { /* We already have a fully formed Security Descriptor */ pFinalSecDesc = pSecDesc; ulFinalSecDescLength = SecDescLength; } else { /* Retrieve the existing SD and merge with the incoming one */ ntError = PvfsGetSecurityDescriptorFile( pCcb, SecInfoAll, (PSECURITY_DESCRIPTOR_RELATIVE)pCurrentSecDescBuffer, &ulCurrentSecDescLength); BAIL_ON_NT_STATUS(ntError); /* Assume that the new SD is <= the combined size of the current SD and the incoming one */ ntError = RtlSetSecurityDescriptorInfo( SecInfo, pSecDesc, (PSECURITY_DESCRIPTOR_RELATIVE)pCurrentSecDescBuffer, (PSECURITY_DESCRIPTOR_RELATIVE)pNewSecDescBuffer, &ulNewSecDescLength, &gPvfsFileGenericMapping); BAIL_ON_NT_STATUS(ntError); pFinalSecDesc = (PSECURITY_DESCRIPTOR_RELATIVE)pNewSecDescBuffer; ulFinalSecDescLength = ulNewSecDescLength; } /* Save the combined SD */ #ifdef HAVE_EA_SUPPORT ntError = PvfsSetSecurityDescriptorFileXattr( pCcb, pFinalSecDesc, ulFinalSecDescLength); #else ntError = PvfsSetSecurityDescriptorPosix( pCcb, pFinalSecDesc, ulFinalSecDescLength); #endif BAIL_ON_NT_STATUS(ntError); PvfsNotifyScheduleFullReport( pCcb->pFcb, FILE_NOTIFY_CHANGE_SECURITY, FILE_ACTION_MODIFIED, pCcb->pszFilename); cleanup: if (pIncAbsSecDesc) { PvfsFreeAbsoluteSecurityDescriptor(&pIncAbsSecDesc); } return ntError; error: goto cleanup; }