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; }
BOOLEAN RtlAccessCheckEx( IN PSECURITY_DESCRIPTOR_ABSOLUTE SecurityDescriptor, IN PACCESS_TOKEN AccessToken, IN ACCESS_MASK DesiredAccess, IN ACCESS_MASK PreviouslyGrantedAccess, IN PGENERIC_MAPPING GenericMapping, OUT PACCESS_MASK RemainingDesiredAccess, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus ) { NTSTATUS status = STATUS_ACCESS_DENIED; BOOLEAN isLocked = FALSE; ACCESS_MASK grantedAccess = PreviouslyGrantedAccess; ACCESS_MASK deniedAccess = 0; ACCESS_MASK desiredAccess = DesiredAccess; BOOLEAN wantMaxAllowed = FALSE; USHORT aclSizeUsed = 0; USHORT aceOffset = 0; PACE_HEADER aceHeader = NULL; union { SID Sid; BYTE Buffer[SID_MAX_SIZE]; } sidBuffer; ULONG ulSidSize = sizeof(sidBuffer); if (!SecurityDescriptor || !AccessToken || !GenericMapping) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } if (!LW_IS_VALID_FLAGS(DesiredAccess, VALID_DESIRED_ACCESS_MASK)) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } if (!LW_IS_VALID_FLAGS(PreviouslyGrantedAccess, VALID_GRANTED_ACCESS_MASK)) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } if ((SecurityDescriptor->Owner == NULL) || (SecurityDescriptor->Group == NULL)) { status = STATUS_INVALID_SECURITY_DESCR; GOTO_CLEANUP(); } wantMaxAllowed = IsSetFlag(desiredAccess, MAXIMUM_ALLOWED); ClearFlag(desiredAccess, MAXIMUM_ALLOWED); RtlMapGenericMask(&desiredAccess, GenericMapping); // // NT AUTHORITY\SYSTEM is always allowed an access // status = RtlCreateWellKnownSid(WinLocalSystemSid, NULL, &sidBuffer.Sid, &ulSidSize); GOTO_CLEANUP_ON_STATUS(status); SHARED_LOCK_RWLOCK(&AccessToken->RwLock, isLocked); if (RtlIsSidMemberOfToken(AccessToken, &sidBuffer.Sid)) { if (wantMaxAllowed) { SetFlag(desiredAccess, STANDARD_RIGHTS_ALL); SetFlag(desiredAccess, GENERIC_ALL); RtlMapGenericMask(&desiredAccess, GenericMapping); } SetFlag(grantedAccess, desiredAccess); desiredAccess = 0; status = STATUS_SUCCESS; GOTO_CLEANUP(); } if (wantMaxAllowed || IsSetFlag(desiredAccess, ACCESS_SYSTEM_SECURITY)) { // TODO-Handle ACCESS_SYSTEM_SECURITY by checking SE_SECURITY_NAME // privilege. For now, requesting ACCESS_SYSTEM_SECURITY is not // allowed. ulSidSize = sizeof(sidBuffer); status = RtlCreateWellKnownSid( WinBuiltinAdministratorsSid, NULL, &sidBuffer.Sid, &ulSidSize); GOTO_CLEANUP_ON_STATUS(status); if (RtlIsSidMemberOfToken(AccessToken, &sidBuffer.Sid)) { SetFlag(grantedAccess, ACCESS_SYSTEM_SECURITY); ClearFlag(desiredAccess, ACCESS_SYSTEM_SECURITY); } else if (IsSetFlag(desiredAccess, ACCESS_SYSTEM_SECURITY)) { status = STATUS_PRIVILEGE_NOT_HELD; GOTO_CLEANUP(); } } if (wantMaxAllowed || IsSetFlag(desiredAccess, WRITE_OWNER)) { // TODO-Allow WRITE_OWNER if have SE_TAKE_OWNERSHIP_NAME regardless // of DACL. // // BUILTIN\Administrators are always allowed WRITE_OWNER // ulSidSize = sizeof(sidBuffer); status = RtlCreateWellKnownSid( WinBuiltinAdministratorsSid, NULL, &sidBuffer.Sid, &ulSidSize); GOTO_CLEANUP_ON_STATUS(status); if (RtlIsSidMemberOfToken(AccessToken, &sidBuffer.Sid)) { SetFlag(grantedAccess, WRITE_OWNER); ClearFlag(desiredAccess, WRITE_OWNER); } } // // Owner can always read the SD and write the DACL. // if (wantMaxAllowed || IsSetFlag(desiredAccess, READ_CONTROL | WRITE_DAC)) { if (RtlIsSidMemberOfToken(AccessToken, SecurityDescriptor->Owner)) { if (wantMaxAllowed) { desiredAccess |= (READ_CONTROL | WRITE_DAC); } SetFlag(grantedAccess, (READ_CONTROL | WRITE_DAC) & desiredAccess); ClearFlag(desiredAccess, grantedAccess); } } // TODO-MAXIMUM_ALLOWED wrt privileges and WRITE_OWNER and // ACCESS_SYSTEM_SECURITY above. if (!SecurityDescriptor->Dacl) { // TODO-Interplay with special bits above? if (wantMaxAllowed) { SetFlag(desiredAccess, STANDARD_RIGHTS_ALL); SetFlag(desiredAccess, GENERIC_ALL); RtlMapGenericMask(&desiredAccess, GenericMapping); } SetFlag(grantedAccess, desiredAccess); desiredAccess = 0; status = STATUS_SUCCESS; GOTO_CLEANUP(); } if (!RtlValidAcl(SecurityDescriptor->Dacl, &aclSizeUsed)) { status = STATUS_INVALID_ACL; GOTO_CLEANUP(); } while (wantMaxAllowed || desiredAccess) { status = RtlIterateAce(SecurityDescriptor->Dacl, aclSizeUsed, &aceOffset, &aceHeader); if (STATUS_NO_MORE_ENTRIES == status) { break; } GOTO_CLEANUP_ON_STATUS(status); // Check ACE switch (aceHeader->AceType) { case ACCESS_ALLOWED_ACE_TYPE: { PACCESS_ALLOWED_ACE ace = (PACCESS_ALLOWED_ACE) aceHeader; ACCESS_MASK mask = ace->Mask; RtlMapGenericMask(&mask, GenericMapping); if (wantMaxAllowed || IsSetFlag(desiredAccess, mask)) { // SID in token => add bits to granted bits PSID sid = RtlpGetSidAccessAllowedAce(ace); if (RtlIsSidMemberOfToken(AccessToken, sid)) { if (wantMaxAllowed) { SetFlag(grantedAccess, mask & ~deniedAccess); } else { SetFlag(grantedAccess, mask & desiredAccess); } ClearFlag(desiredAccess, grantedAccess); } } break; } case ACCESS_DENIED_ACE_TYPE: { // Allowed and deny ACEs are isomorphic. PACCESS_ALLOWED_ACE ace = (PACCESS_ALLOWED_ACE) aceHeader; ACCESS_MASK mask = ace->Mask; RtlMapGenericMask(&mask, GenericMapping); if (wantMaxAllowed || IsSetFlag(desiredAccess, mask)) { // SID in token => exit with STATUS_ACCESS_DENIED PSID sid = RtlpGetSidAccessAllowedAce(ace); if (RtlIsSidMemberOfToken(AccessToken, sid)) { if (wantMaxAllowed) { SetFlag(deniedAccess, mask); } else { status = STATUS_ACCESS_DENIED; GOTO_CLEANUP(); } ClearFlag(desiredAccess, deniedAccess); } } break; } default: // ignore break; } } status = desiredAccess ? STATUS_ACCESS_DENIED : STATUS_SUCCESS; cleanup: UNLOCK_RWLOCK(&AccessToken->RwLock, isLocked); if (NT_SUCCESS(status) && !LW_IS_VALID_FLAGS(grantedAccess, VALID_GRANTED_ACCESS_MASK)) { status = STATUS_ASSERTION_FAILURE; } if (!NT_SUCCESS(status)) { grantedAccess = PreviouslyGrantedAccess; } if (RemainingDesiredAccess) { *RemainingDesiredAccess = desiredAccess; } *GrantedAccess = grantedAccess; *AccessStatus = status; return NT_SUCCESS(status) ? TRUE : FALSE; }
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 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; }