/*++ Copyright (c) 1989 Microsoft Corporation Module Name: shadowse.c Abstract: This module implements all security related functions for disconnected operation of Client Side Caching Revision History: Balan Sethu Raman [SethuR] 6-October-1997 Notes: In NT ACL(Access Control lists) provide the necessary mechanism for denying/granting permissions to users. Each user is associated with a SID. The ACLs are specified in terms of SIDs associated with groups of users as well as individual users. Each user is associated with a token at runtime which details the various groups a user is a member of and this token is used to evaluate the ACLs. This is complicated by the fact that there are local groups associated with various machines. Therefore the token/context associated with an user varies from machine. In connected mode the credentials associated with a given user are shipped to the server where they are validated and the appropriate token created. This token is used subsequently in the evaluation of ACLs. This presents us with two options in implementing security for disconnected operation -- lazy evaluation and eager evaluation. In lazy evaluation the evaluation of ACLs is done on demand but the preparatory work for this evaluation is done in connected mode. On the other hand in eager evaluation the ACLs are evaluated and the maximal rights are stored as part of the CSC database. These rights are used to determine the appropriate access. The advantage of lazy evaluation is that the database is no longer constrained by previous access requirements while in eager evaluation we require that the user have accessed the file in connected mode in order to correctly determine the rights in disconnected mode. The flip side is that Lazy Evaluation is tougher to implement ( requires modifications in security/DS ) while the eager evaluation implementation is very easy. The current implementation corresponds to a simplified form of eager evaluation strategy. Appropriate encapsulation has been provided to allow us to swicth over to a lazy evaluation mode easily. There are three facets of the implementation 1) Storing/Retreiving Access information 2) Denying/Granting access based upon the stored access information. 3) Persisting the SID/index mapping Currently associated with each file/directory in the CSC database there is a security blob. This blob is an in --*/ #include "precomp.h" #pragma hdrstop #pragma code_seg("PAGE") DWORD CscUpdateCachedSecurityInformation( PCACHED_SECURITY_INFORMATION pCachedSecurityInformation, ULONG CachedSecurityInformationLength, ULONG NumberOfSids, PCSC_SID_ACCESS_RIGHTS pSidAccessRights) /*++ Routine Description: This routine updates the access rights for a given number of sids in the given cached security information structure. This routine centralizes the update process required for share level security as well as the object level security into a single routine since the on disk format for both these cases are the same. However, different APIs are required to update the in memory data structures. Arguments: pCachedSecurityInformation - the cached security information instance CachedSecurityInformationLength - the length of the cached information NumberOfSids - the number of sids for which the access rights needs to be updated pSidAccessRights - an array of the sids and the corresponding access rights. Return Value: ERROR_SUCCESS if successful, otherwise appropriate error Notes: The current implementation of this routine is based upon the assumption that the number of Sid mappings stored on a per file basis is very small ( 8 at most). If this assumption is changed this routine needs to be reworked. --*/ { DWORD Status = ERROR_SUCCESS; ULONG i,j,cntNewRights=0; CACHED_SECURITY_INFORMATION NewSecurityInformation; ASSERT(CachedSecurityInformationLength == sizeof(CACHED_SECURITY_INFORMATION)); if (NumberOfSids > CSC_MAXIMUM_NUMBER_OF_CACHED_SID_INDEXES) { return ERROR_BUFFER_OVERFLOW; } else if (NumberOfSids == 0) { return ERROR_SUCCESS; } // NB assumption, CSC_INVALID_INDEX is 0 memset(&NewSecurityInformation, 0, sizeof(NewSecurityInformation)); // from the array of new rights for (i = 0; i < NumberOfSids; i++) { CSC_SID_INDEX SidIndex; // map the sid to a sid index SidIndex = CscMapSidToIndex( pSidAccessRights[i].pSid, pSidAccessRights[i].SidLength); if (SidIndex == CSC_INVALID_SID_INDEX) { // Map the new sid Status = CscAddSidToDatabase( pSidAccessRights[i].pSid, pSidAccessRights[i].SidLength, &SidIndex); if (Status != STATUS_SUCCESS) { return Status; } } NewSecurityInformation.AccessRights[i].SidIndex = SidIndex; NewSecurityInformation.AccessRights[i].MaximalRights = (USHORT)pSidAccessRights[i].MaximalAccessRights; cntNewRights++; } // now copy the cached rights from old array for those sids which are not already // there in the new array, till all slots in the new array are full // this ensures a round robin scheme ASSERT(cntNewRights && (cntNewRights <= CSC_MAXIMUM_NUMBER_OF_CACHED_SID_INDEXES)); for (i=0; iAccessRights[i].SidIndex != CSC_INVALID_SID_INDEX) { BOOLEAN fFound; fFound = FALSE; // check if it is already there in the new array. for (j=0; j< cntNewRights; ++j) { if (NewSecurityInformation.AccessRights[j].SidIndex == pCachedSecurityInformation->AccessRights[i].SidIndex) { fFound = TRUE; break; } } // if it isn't in the new array, then we need to copy it if (!fFound) { NewSecurityInformation.AccessRights[cntNewRights] = pCachedSecurityInformation->AccessRights[i]; ++cntNewRights; // the new array has } } } // update the cached security info and pass it back *pCachedSecurityInformation = NewSecurityInformation; return Status; } DWORD CscAddMaximalAccessRightsForSids( HSHADOW hParent, HSHADOW hFile, ULONG NumberOfSids, PCSC_SID_ACCESS_RIGHTS pSidAccessRights) /*++ Routine Description: This routine updates the access rights for a given number of sids on the given file Arguments: hParent - the parent directory shadow handle hFile - the shadow handle NumberOfSids - the number of sids for which the access rights needs to be updated pSidAccessRights - an array of the sids and the corresponding access rights. Return Value: ERROR_SUCCESS if successful, otherwise appropriate error Notes: The current implementation of this routine is based upon the assumption that the number of Sid mappings stored on a per file basis is very small ( 8 at most). If this assumption is changed this routine needs to be reworked. --*/ { DWORD Status = ERROR_SUCCESS; CACHED_SECURITY_INFORMATION CachedSecurityInformation; ULONG BytesReturned, BytesWritten; BytesReturned = sizeof(CachedSecurityInformation); Status = GetShadowInfoEx( hParent, hFile, NULL, NULL, NULL, &CachedSecurityInformation, &BytesReturned); if ((Status == ERROR_SUCCESS) && ((BytesReturned == 0) || (BytesReturned == sizeof(CachedSecurityInformation)))) { Status = CscUpdateCachedSecurityInformation( &CachedSecurityInformation, BytesReturned, NumberOfSids, pSidAccessRights); if (Status == ERROR_SUCCESS) { BytesWritten = sizeof(CachedSecurityInformation); Status = SetShadowInfoEx( hParent, hFile, NULL, 0, SHADOW_FLAGS_OR, NULL, &CachedSecurityInformation, &BytesWritten); } } return Status; } DWORD CscAddMaximalAccessRightsForShare( HSERVER hShare, ULONG NumberOfSids, PCSC_SID_ACCESS_RIGHTS pSidAccessRights) /*++ Routine Description: This routine updates the access rights for a given number of sids on the given share Arguments: hShare - the parent directory shadow handle NumberOfSids - the number of sids for which the access rights needs to be updated pSidAccessRights - an array of the sids and the corresponding access rights. Return Value: ERROR_SUCCESS if successful, otherwise appropriate error Notes: The current implementation of this routine is based upon the assumption that the number of Sid mappings stored on a per file basis is very small ( 8 at most). If this assumption is changed this routine needs to be reworked. --*/ { DWORD Status = ERROR_SUCCESS; CACHED_SECURITY_INFORMATION CachedSecurityInformation; ULONG BytesReturned, BytesWritten; BytesReturned = sizeof(CachedSecurityInformation); Status = GetShareInfoEx( hShare, NULL, NULL, &CachedSecurityInformation, &BytesReturned); if ((Status == ERROR_SUCCESS) && ((BytesReturned == 0) || (BytesReturned == sizeof(CachedSecurityInformation)))) { Status = CscUpdateCachedSecurityInformation( &CachedSecurityInformation, BytesReturned, NumberOfSids, pSidAccessRights); if (Status == ERROR_SUCCESS) { BytesWritten = sizeof(CachedSecurityInformation); if (SetShareStatusEx( hShare, 0, SHADOW_FLAGS_OR, &CachedSecurityInformation, &BytesWritten) >= 0) { Status = STATUS_SUCCESS; } { Status = STATUS_UNSUCCESSFUL; } } } return Status; } DWORD CscRemoveMaximalAccessRightsForSid( HSHADOW hParent, HSHADOW hFile, PVOID pSid, ULONG SidLength) /*++ Routine Description: This routine removes the cached access rights for a given number of sids on the given file Arguments: hParent - the parent directory shadow handle hFile - the shadow handle pSid - the sid for which the cached access rights are revoked. SidLength - the length of the sid. Return Value: ERROR_SUCCESS if successful, otherwise appropriate error --*/ { DWORD Status = ERROR_SUCCESS; USHORT SidIndex; ULONG BytesRead,BytesWritten,i; CACHED_SECURITY_INFORMATION CachedSecurityInformation; SidIndex = CscMapSidToIndex( pSid, SidLength); if (SidIndex != CSC_INVALID_SID_INDEX) { BytesRead = sizeof(CachedSecurityInformation); Status = GetShadowInfoEx( hParent, hFile, NULL, NULL, NULL, &CachedSecurityInformation, &BytesRead); if ((Status == ERROR_SUCCESS) && (BytesRead == sizeof(CachedSecurityInformation))) { for (i = 0; i < CSC_MAXIMUM_NUMBER_OF_CACHED_SID_INDEXES; i++) { if (CachedSecurityInformation.AccessRights[i].SidIndex == SidIndex) { CachedSecurityInformation.AccessRights[i].SidIndex = CSC_INVALID_SID_INDEX; BytesWritten = sizeof(CachedSecurityInformation); Status = SetShadowInfoEx( hParent, hFile, NULL, 0, SHADOW_FLAGS_OR, NULL, &CachedSecurityInformation, &BytesWritten); break; } } } } return Status; }