//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1999. // // File: AccessCk.cpp // // Contents: Functions imported and modified from ntos\se\accessck.c // // //---------------------------------------------------------------------------- #include "stdafx.h" #include "AccessCk.h" #include "adutils.h" typedef enum { UpdateRemaining, UpdateCurrentGranted, UpdateCurrentDenied } ACCESS_MASK_FIELD_TO_UPDATE; // // Prototypes // BOOLEAN SepSidInSIDList ( IN list& psidList, IN PSID PrincipalSelfSid, IN PSID Sid); HRESULT SepAddAccessTypeList ( IN PIOBJECT_TYPE_LIST ObjectTypeList, IN size_t ObjectTypeListLength, IN ULONG StartIndex, IN ACCESS_MASK AccessMask, IN ACCESS_MASK_FIELD_TO_UPDATE FieldToUpdate, IN PSID grantingSid ); BOOLEAN SepObjectInTypeList ( IN GUID *ObjectType, IN PIOBJECT_TYPE_LIST ObjectTypeList, IN size_t ObjectTypeListLength, OUT PULONG ReturnedIndex ); HRESULT SepUpdateParentTypeList ( IN PIOBJECT_TYPE_LIST ObjectTypeList, IN size_t ObjectTypeListLength, IN ULONG StartIndex, IN PSID grantingSid ); HRESULT SetGrantingSid ( IOBJECT_TYPE_LIST& ObjectTypeItem, ACCESS_MASK_FIELD_TO_UPDATE FieldToUpdate, ACCESS_MASK oldAccessBits, ACCESS_MASK newAccessBits, PSID grantingSid); /////////////////////////////////////////////////////////////////////////////// PSID SePrincipalSelfSid = 0; static SID_IDENTIFIER_AUTHORITY SepNtAuthority = SECURITY_NT_AUTHORITY; HRESULT SepInit () { HRESULT hr = S_OK; ULONG SidWithOneSubAuthority = RtlLengthRequiredSid (1); SePrincipalSelfSid = (PSID) CoTaskMemAlloc (SidWithOneSubAuthority); if ( SePrincipalSelfSid ) { SID_IDENTIFIER_AUTHORITY SeNtAuthority = SepNtAuthority; RtlInitializeSid (SePrincipalSelfSid, &SeNtAuthority, 1); *(RtlSubAuthoritySid (SePrincipalSelfSid, 0)) = SECURITY_PRINCIPAL_SELF_RID; } else hr = E_OUTOFMEMORY; return hr; } VOID SepCleanup () { if ( SePrincipalSelfSid ) { CoTaskMemFree (SePrincipalSelfSid); SePrincipalSelfSid = 0; } } /////////////////////////////////////////////////////////////////////////////// HRESULT SepMaximumAccessCheck( list& psidList, IN PACL Dacl, IN PSID PrincipalSelfSid, IN size_t LocalTypeListLength, IN PIOBJECT_TYPE_LIST LocalTypeList, IN size_t ObjectTypeListLength ) /*++ Routine Description: Does an access check for maximum allowed or with a result list. The current granted access is stored in the Remaining access and then another access check is run. Arguments: psidList - list of object sid to check, plus sids of all groups that the object belongs to Dacl - ACL to check PrincipalSelfSid - Sid to use in replacing the well-known self sid LocalTypeListLength - Length of list of types. LocalTypeList - List of types. ObjectTypeList - Length of caller-supplied list of object types. Return Value: none --*/ { if ( !LocalTypeList || ! Dacl ) return E_POINTER; if ( PrincipalSelfSid && !IsValidSid (PrincipalSelfSid) ) return E_INVALIDARG; _TRACE (1, L"Entering SepMaximumAccessCheck\n"); PVOID Ace = 0; ULONG AceCount = Dacl->AceCount; ULONG Index = 0; HRESULT hr = S_OK; // // granted == NUL // denied == NUL // // for each ACE // // if grant // for each SID // if SID match, then add all that is not denied to grant mask // // if deny // for each SID // if SID match, then add all that is not granted to deny mask // ULONG i = 0; for (Ace = FirstAce (Dacl); i < AceCount; i++, Ace = NextAce (Ace)) { if ( !(((PACE_HEADER)Ace)->AceFlags & INHERIT_ONLY_ACE)) { switch (((PACE_HEADER)Ace)->AceType) { case ACCESS_ALLOWED_ACE_TYPE: if (SepSidInSIDList(psidList, PrincipalSelfSid, &((PACCESS_ALLOWED_ACE)Ace)->SidStart)) { // // Only grant access types from this mask that have // not already been denied // // Optimize 'normal' case if ( LocalTypeListLength == 1 ) { // TODO: do granting SID LocalTypeList->CurrentGranted |= (((PACCESS_ALLOWED_ACE)Ace)->Mask & ~LocalTypeList->CurrentDenied); } else { // // The zeroeth object type represents the object itself. // hr = SepAddAccessTypeList( LocalTypeList, // List to modify LocalTypeListLength, // Length of list 0, // Element to update ((PACCESS_ALLOWED_ACE)Ace)->Mask, // Access Granted UpdateCurrentGranted, &((PACCESS_ALLOWED_ACE)Ace)->SidStart); } } break; // // Handle an object specific Access Allowed ACE // case ACCESS_ALLOWED_OBJECT_ACE_TYPE: { // // If no object type is in the ACE, // treat this as an ACCESS_ALLOWED_ACE. // GUID* ObjectTypeInAce = RtlObjectAceObjectType(Ace); if ( ObjectTypeInAce == NULL ) { if ( SepSidInSIDList(psidList, PrincipalSelfSid, RtlObjectAceSid(Ace)) ) { // Optimize 'normal' case if ( LocalTypeListLength == 1 ) { // TODO: do granting SID LocalTypeList->CurrentGranted |= (((PACCESS_ALLOWED_OBJECT_ACE)Ace)->Mask & ~LocalTypeList->CurrentDenied); } else { hr = SepAddAccessTypeList( LocalTypeList, // List to modify LocalTypeListLength, // Length of list 0, // Element to update ((PACCESS_ALLOWED_OBJECT_ACE)Ace)->Mask, // Access Granted UpdateCurrentGranted, RtlObjectAceSid(Ace)); } } // // If no object type list was passed, // don't grant access to anyone. // } else if ( ObjectTypeListLength == 0 ) { // Drop through // // If an object type is in the ACE, // Find it in the LocalTypeList before using the ACE. // } else { if ( SepSidInSIDList(psidList, PrincipalSelfSid, RtlObjectAceSid(Ace)) ) { if ( SepObjectInTypeList( ObjectTypeInAce, LocalTypeList, LocalTypeListLength, &Index ) ) { hr = SepAddAccessTypeList( LocalTypeList, // List to modify LocalTypeListLength, // Length of list Index, // Element already updated ((PACCESS_ALLOWED_OBJECT_ACE)Ace)->Mask, // Access Granted UpdateCurrentGranted, RtlObjectAceSid(Ace)); } } } } break; case ACCESS_ALLOWED_COMPOUND_ACE_TYPE: // // If we're impersonating, EToken is set to the Client, and if we're not, // EToken is set to the Primary. According to the DSA architecture, if // we're asked to evaluate a compound ACE and we're not impersonating, // pretend we are impersonating ourselves. So we can just use the EToken // for the client token, since it's already set to the right thing. // if ( SepSidInSIDList(psidList, PrincipalSelfSid, RtlCompoundAceClientSid( Ace )) && SepSidInSIDList(psidList, NULL, RtlCompoundAceServerSid( Ace )) ) { // // Only grant access types from this mask that have // not already been denied // // Optimize 'normal' case if ( LocalTypeListLength == 1 ) { // TODO: do granting SID LocalTypeList->CurrentGranted |= (((PCOMPOUND_ACCESS_ALLOWED_ACE)Ace)->Mask & ~LocalTypeList->CurrentDenied); } else { // // The zeroeth object type represents the object itself. // hr = SepAddAccessTypeList( LocalTypeList, // List to modify LocalTypeListLength, // Length of list 0, // Element to update ((PCOMPOUND_ACCESS_ALLOWED_ACE)Ace)->Mask, // Access Granted UpdateCurrentGranted, RtlCompoundAceClientSid (Ace)); } } break; case ACCESS_DENIED_ACE_TYPE: if ( SepSidInSIDList(psidList, PrincipalSelfSid, &((PACCESS_DENIED_ACE)Ace)->SidStart)) { // // Only deny access types from this mask that have // not already been granted // // Optimize 'normal' case if ( LocalTypeListLength == 1 ) { // TODO: do granting SID LocalTypeList->CurrentDenied |= (((PACCESS_DENIED_ACE)Ace)->Mask & ~LocalTypeList->CurrentGranted); } else { // // The zeroeth object type represents the object itself. // hr = SepAddAccessTypeList( LocalTypeList, // List to modify LocalTypeListLength, // Length of list 0, // Element to update ((PACCESS_DENIED_ACE)Ace)->Mask, // Access denied UpdateCurrentDenied, &((PACCESS_DENIED_ACE)Ace)->SidStart); } } break; // // Handle an object specific Access Denied ACE // case ACCESS_DENIED_OBJECT_ACE_TYPE: { PSID psid = RtlObjectAceSid(Ace); ASSERT (IsValidSid (psid)); if ( IsValidSid (psid) && SepSidInSIDList(psidList, PrincipalSelfSid, psid) ) { // // If there is no object type in the ACE, // or if the caller didn't specify an object type list, // apply this deny ACE to the entire object. // GUID* ObjectTypeInAce = RtlObjectAceObjectType(Ace); if ( ObjectTypeInAce == NULL || ObjectTypeListLength == 0 ) { // TODO: do granting SID LocalTypeList->CurrentDenied |= (((PACCESS_DENIED_OBJECT_ACE)Ace)->Mask & ~LocalTypeList->CurrentGranted); // // Otherwise apply the deny ACE to the object specified // in the ACE. // } else if ( SepObjectInTypeList( ObjectTypeInAce, LocalTypeList, LocalTypeListLength, &Index ) ) { hr = SepAddAccessTypeList( LocalTypeList, // List to modify LocalTypeListLength, // Length of list Index, // Element to update ((PACCESS_DENIED_OBJECT_ACE)Ace)->Mask, // Access denied UpdateCurrentDenied, psid); } } } break; default: break; } } } _TRACE (-1, L"Leaving SepMaximumAccessCheck\n"); return hr; } NTSTATUS SeCaptureObjectTypeList ( IN POBJECT_TYPE_LIST ObjectTypeList OPTIONAL, IN size_t ObjectTypeListLength, OUT PIOBJECT_TYPE_LIST *CapturedObjectTypeList ) /*++ Routine Description: This routine probes and captures a copy of any object type list that might have been provided via the ObjectTypeList argument. The object type list is converted to the internal form that explicitly specifies the hierarchical relationship between the entries. The object typs list is validated to ensure a valid hierarchical relationship is represented. Arguments: ObjectTypeList - The object type list from which the type list information is to be retrieved. ObjectTypeListLength - Number of elements in ObjectTypeList CapturedObjectTypeList - Receives the captured type list which must be freed using SeFreeCapturedObjectTypeList(). Return Value: STATUS_SUCCESS indicates no exceptions were encountered. Any access violations encountered will be returned. --*/ { _TRACE (1, L"Entering SeCaptureObjectTypeList\n"); NTSTATUS Status = STATUS_SUCCESS; PIOBJECT_TYPE_LIST LocalTypeList = NULL; ULONG Levels[ACCESS_MAX_LEVEL+1]; // // Set default return // *CapturedObjectTypeList = NULL; if ( ObjectTypeListLength == 0 ) { // Drop through } else if ( !ARGUMENT_PRESENT(ObjectTypeList) ) { Status = STATUS_INVALID_PARAMETER; } else { // // Allocate a buffer to copy into. // LocalTypeList = new IOBJECT_TYPE_LIST[ObjectTypeListLength]; if ( !LocalTypeList ) { Status = STATUS_INSUFFICIENT_RESOURCES; // // Copy the callers structure to the local structure. // } else { GUID * CapturedObjectType = 0; for (ULONG i=0; i < ObjectTypeListLength; i++ ) { // // Limit ourselves // USHORT CurrentLevel = ObjectTypeList[i].Level; if ( CurrentLevel > ACCESS_MAX_LEVEL ) { Status = STATUS_INVALID_PARAMETER; break; } // // Copy data the caller passed in // LocalTypeList[i].Level = CurrentLevel; LocalTypeList[i].Flags = 0; CapturedObjectType = ObjectTypeList[i].ObjectType; LocalTypeList[i].ObjectType = *CapturedObjectType; LocalTypeList[i].Remaining = 0; LocalTypeList[i].CurrentGranted = 0; LocalTypeList[i].CurrentDenied = 0; // // Ensure that the level number is consistent with the // level number of the previous entry. // if ( i == 0 ) { if ( CurrentLevel != 0 ) { Status = STATUS_INVALID_PARAMETER; break; } } else { // // The previous entry is either: // my immediate parent, // my sibling, or // the child (or grandchild, etc.) of my sibling. // if ( CurrentLevel > LocalTypeList[i-1].Level + 1 ) { Status = STATUS_INVALID_PARAMETER; break; } // // Don't support two roots. // if ( CurrentLevel == 0 ) { Status = STATUS_INVALID_PARAMETER; break; } } // // If the above rules are maintained, // then my parent object is the last object seen that // has a level one less than mine. // if ( CurrentLevel == 0 ) { LocalTypeList[i].ParentIndex = -1; } else { LocalTypeList[i].ParentIndex = Levels[CurrentLevel-1]; } // // Save this obect as the last object seen at this level. // Levels[CurrentLevel] = i; } } } // end_if *CapturedObjectTypeList = LocalTypeList; _TRACE (-1, L"Leaving SeCaptureObjectTypeList: Status = 0x%x\n", Status); return Status; } BOOLEAN SepSidInSIDList ( IN list& psidList, IN PSID PrincipalSelfSid, IN PSID Sid) /*++ Routine Description: Checks to see if a given restricted SID is in the given sid list. N.B. The code to compute the length of a SID and test for equality is duplicated from the security runtime since this is such a frequently used routine. Arguments: psidList - the list of sids to be examined PrincipalSelfSid - If the object being access checked is an object which represents a principal (e.g., a user object), this parameter should be the SID of the object. Any ACE containing the constant PRINCIPAL_SELF_SID is replaced by this SID. The parameter should be NULL if the object does not represent a principal. Sid - Pointer to the SID of interest DenyAce - The ACE being evaluated is a DENY or ACCESS DENIED ace Restricted - The access check being performed uses the restricted sids. Return Value: A value of TRUE indicates that the SID is in the token, FALSE otherwise. --*/ { _TRACE (1, L"Entering SeSidInSIDList\n"); BOOLEAN bRVal = FALSE; PISID MatchSid = 0; ASSERT (IsValidSid (Sid)); if ( IsValidSid (Sid) ) { // // If Sid is the constant PrincipalSelfSid, // replace it with the passed in PrincipalSelfSid. // if ( PrincipalSelfSid != NULL && EqualSid (SePrincipalSelfSid, Sid) ) { Sid = PrincipalSelfSid; } // // Get address of user/group array and number of user/groups. // // // Scan through the user/groups and attempt to find a match with the // specified SID. // ULONG i = 0; for (list::iterator itr = psidList.begin (); itr != psidList.end (); itr++, i++) { ASSERT (IsValidSid (*itr)); MatchSid = (PISID)*itr; if ( ::EqualSid (Sid, *itr) ) { bRVal = true; break; } } } _TRACE (-1, L"Leaving SeSidInSIDList: %s\n", bRVal ? L"TRUE" : L"FALSE"); return bRVal; } HRESULT SepAddAccessTypeList ( IN PIOBJECT_TYPE_LIST ObjectTypeList, IN size_t ObjectTypeListLength, IN ULONG StartIndex, IN ACCESS_MASK AccessMask, IN ACCESS_MASK_FIELD_TO_UPDATE FieldToUpdate, IN PSID grantingSid ) /*++ Routine Description: This routine grants the specified AccessMask to all of the objects that are descendents of the object specified by StartIndex. The Access fields of the parent objects are also recomputed as needed. For example, if an ACE granting access to a Property Set is found, that access is granted to all the Properties in the Property Set. Arguments: ObjectTypeList - The object type list to update. ObjectTypeListLength - Number of elements in ObjectTypeList StartIndex - Index to the target element to update. AccessMask - Mask of access to grant to the target element and all of its decendents FieldToUpdate - Indicate which fields to update in object type list Return Value: None. --*/ { if ( !ObjectTypeList ) return E_POINTER; if ( !IsValidSid (grantingSid) ) return E_INVALIDARG; _TRACE (1, L"Entering SepAddAccessTypeList\n"); ACCESS_MASK OldRemaining = 0; ACCESS_MASK OldCurrentGranted = 0; ACCESS_MASK OldCurrentDenied = 0; BOOLEAN AvoidParent = FALSE; HRESULT hr = S_OK; // PAGED_CODE(); // // Update the requested field. // // Always handle the target entry. // // If we've not actually changed the bits, // early out. // switch (FieldToUpdate ) { case UpdateRemaining: OldRemaining = ObjectTypeList[StartIndex].Remaining; ObjectTypeList[StartIndex].Remaining = OldRemaining & ~AccessMask; if ( OldRemaining == ObjectTypeList[StartIndex].Remaining ) { return hr; } else { hr = SetGrantingSid ( ObjectTypeList[StartIndex], FieldToUpdate, OldRemaining, AccessMask & ~ObjectTypeList[StartIndex].Remaining, grantingSid); } break; case UpdateCurrentGranted: OldCurrentGranted = ObjectTypeList[StartIndex].CurrentGranted; ObjectTypeList[StartIndex].CurrentGranted |= AccessMask & ~ObjectTypeList[StartIndex].CurrentDenied; if ( OldCurrentGranted == ObjectTypeList[StartIndex].CurrentGranted ) { // // We can't simply return here. // We have to visit our children. Consider the case where there // was a previous deny ACE on a child. That deny would have // propagated up the tree to this entry. However, this allow ACE // needs to be added all of the children that haven't been // explictly denied. // AvoidParent = TRUE; } else { hr = SetGrantingSid ( ObjectTypeList[StartIndex], FieldToUpdate, OldCurrentGranted, AccessMask & ~ObjectTypeList[StartIndex].CurrentDenied, grantingSid); } break; case UpdateCurrentDenied: OldCurrentDenied = ObjectTypeList[StartIndex].CurrentDenied; ObjectTypeList[StartIndex].CurrentDenied |= AccessMask & ~ObjectTypeList[StartIndex].CurrentGranted; if ( OldCurrentDenied == ObjectTypeList[StartIndex].CurrentDenied ) { return hr; } else { hr = SetGrantingSid ( ObjectTypeList[StartIndex], FieldToUpdate, OldCurrentDenied, AccessMask & ~ObjectTypeList[StartIndex].CurrentGranted, grantingSid); } break; default: return hr; } // // Go update parent of the target. // if ( !AvoidParent ) { hr = SepUpdateParentTypeList( ObjectTypeList, ObjectTypeListLength, StartIndex, grantingSid); } // // Loop handling all children of the target. // for (ULONG Index = StartIndex + 1; Index < ObjectTypeListLength; Index++) { // // By definition, the children of an object are all those entries // immediately following the target. The list of children (or // grandchildren) stops as soon as we reach an entry the has the // same level as the target (a sibling) or lower than the target // (an uncle). // if ( ObjectTypeList[Index].Level <= ObjectTypeList[StartIndex].Level ) { break; } // // Grant access to the children // switch (FieldToUpdate) { case UpdateRemaining: ObjectTypeList[Index].Remaining &= ~AccessMask; hr = SetGrantingSid ( ObjectTypeList[Index], FieldToUpdate, OldRemaining, ~AccessMask, grantingSid); break; case UpdateCurrentGranted: ObjectTypeList[Index].CurrentGranted |= AccessMask & ~ObjectTypeList[Index].CurrentDenied; hr = SetGrantingSid ( ObjectTypeList[Index], FieldToUpdate, OldCurrentGranted, AccessMask & ~ObjectTypeList[Index].CurrentDenied, grantingSid); break; case UpdateCurrentDenied: ObjectTypeList[Index].CurrentDenied |= AccessMask & ~ObjectTypeList[Index].CurrentGranted; hr = SetGrantingSid ( ObjectTypeList[Index], FieldToUpdate, OldCurrentDenied, AccessMask & ~ObjectTypeList[Index].CurrentGranted, grantingSid); break; default: return hr; } } _TRACE (-1, L"Leaving SepAddAccessTypeList\n"); return hr; } BOOLEAN SepObjectInTypeList ( IN GUID *ObjectType, IN PIOBJECT_TYPE_LIST ObjectTypeList, IN size_t ObjectTypeListLength, OUT PULONG ReturnedIndex ) /*++ Routine Description: This routine searches an ObjectTypeList to determine if the specified object type is in the list. Arguments: ObjectType - Object Type to search for. ObjectTypeList - The object type list to search. ObjectTypeListLength - Number of elements in ObjectTypeList ReturnedIndex - Index to the element ObjectType was found in Return Value: TRUE: ObjectType was found in list. FALSE: ObjectType was not found in list. --*/ { if ( !ObjectType || !ObjectTypeList ) return FALSE; _TRACE (1, L"Entering SepObjectInTypeList\n"); BOOLEAN bRVal = FALSE; GUID* LocalObjectType = 0; #if DBG HRESULT hr = S_OK; GUID_TYPE* pType = 0; wstring strClassName1; hr = _Module.GetClassFromGUID (*ObjectType, strClassName1, pType); ASSERT (SUCCEEDED (hr)); #endif #pragma warning (disable : 4127) ASSERT( sizeof(GUID) == sizeof(ULONG) * 4 ); #pragma warning (default : 4127) for (ULONG Index = 0; Index < ObjectTypeListLength; Index++) { LocalObjectType = &ObjectTypeList[Index].ObjectType; #if DBG wstring strClassName2; hr = _Module.GetClassFromGUID (*LocalObjectType, strClassName2, pType); ASSERT (SUCCEEDED (hr)); _TRACE (0, L"\tComparing %s to %s\n", strClassName1.c_str (), strClassName2.c_str ()); #endif if ( RtlpIsEqualGuid( ObjectType, LocalObjectType ) ) { *ReturnedIndex = Index; bRVal = TRUE; break; } } _TRACE (-1, L"Leaving SepObjectInTypeList: %s\n", bRVal ? L"TRUE" : L"FALSE"); return bRVal; } HRESULT SepUpdateParentTypeList ( IN PIOBJECT_TYPE_LIST ObjectTypeList, IN size_t ObjectTypeListLength, IN ULONG StartIndex, PSID grantingSid ) /*++ Routine Description: Update the Access fields of the parent object of the specified object. The "remaining" field of a parent object is the logical OR of the remaining field of all of its children. The CurrentGranted field of the parent is the collection of bits granted to every one of its children.. The CurrentDenied fields of the parent is the logical OR of the bits denied to any of its children. This routine takes an index to one of the children and updates the remaining field of the parent (and grandparents recursively). Arguments: ObjectTypeList - The object type list to update. ObjectTypeListLength - Number of elements in ObjectTypeList StartIndex - Index to the "child" element whose parents are to be updated. Return Value: None. --*/ { if ( !ObjectTypeList ) return E_POINTER; if ( !IsValidSid (grantingSid) ) return E_INVALIDARG; _TRACE (1, L"Entering SepUpdateParentTypeList\n"); ACCESS_MASK NewRemaining = 0; ACCESS_MASK NewCurrentGranted = 0xFFFFFFFF; ACCESS_MASK NewCurrentDenied = 0; HRESULT hr = S_OK; // // If the target node is at the root, // we're all done. // if ( ObjectTypeList[StartIndex].ParentIndex == -1 ) { return hr; } // // Get the index to the parent that needs updating and the level of // the siblings. // ULONG ParentIndex = ObjectTypeList[StartIndex].ParentIndex; ULONG Level = ObjectTypeList[StartIndex].Level; // // Loop through all the children. // for (UINT Index=ParentIndex+1; Index