You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4959 lines
138 KiB
4959 lines
138 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
persist.cxx
|
|
|
|
Abstract:
|
|
|
|
Routines implementing the common logic for persisting the authz policy.
|
|
|
|
This file contains routine called by the core logic to submit changes.
|
|
It also contains routines that are called by the particular providers to
|
|
find out information about the changed objects.
|
|
|
|
Author:
|
|
|
|
Cliff Van Dyke (cliffv) 9-May-2001
|
|
|
|
--*/
|
|
|
|
#include "pch.hxx"
|
|
|
|
DWORD
|
|
WINAPI
|
|
AzpeCreateObject(
|
|
IN AZPE_OBJECT_HANDLE AzpeParentHandle,
|
|
IN ULONG ChildObjectType,
|
|
IN LPCWSTR ChildObjectNameString,
|
|
IN GUID *ChildObjectGuid OPTIONAL,
|
|
IN ULONG lPersistFlags,
|
|
OUT AZPE_OBJECT_HANDLE *AzpeChildHandle
|
|
);
|
|
|
|
VOID
|
|
WINAPI
|
|
AzpeObjectFinished(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN DWORD WinStatus
|
|
);
|
|
|
|
DWORD
|
|
WINAPI
|
|
AzpeGetProperty(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG PropertyId,
|
|
OUT PVOID *PropertyValue
|
|
);
|
|
|
|
DWORD
|
|
WINAPI
|
|
AzpeGetDeltaArray(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG PropertyId,
|
|
OUT PULONG DeltaArrayCount,
|
|
OUT PAZP_DELTA_ENTRY **DeltaArray
|
|
);
|
|
|
|
DWORD
|
|
WINAPI
|
|
AzpeGetSecurityDescriptorFromCache(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags,
|
|
IN PAZP_POLICY_USER_RIGHTS *ppPolicyAdminRights OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS *ppPolicyReaderRights OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS *ppDelegatedPolicyUsersRights OPTIONAL,
|
|
IN GUID *pDelegatedObjectGuid OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS pDelegatedUsersAttributeRights OPTIONAL,
|
|
IN GUID *pAttributeGuid OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS pSaclRights OPTIONAL,
|
|
IN PSECURITY_DESCRIPTOR OldSd OPTIONAL,
|
|
OUT PSECURITY_DESCRIPTOR *NewSd
|
|
);
|
|
|
|
//
|
|
// Routines to return a single field of an object
|
|
//
|
|
|
|
DWORD
|
|
WINAPI
|
|
AzpeObjectType(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
);
|
|
|
|
DWORD
|
|
WINAPI
|
|
AzpeDirtyBits(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
);
|
|
|
|
GUID *
|
|
WINAPI
|
|
AzpePersistenceGuid(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
);
|
|
|
|
AZPE_OBJECT_HANDLE
|
|
WINAPI
|
|
AzpeParentOfChild(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
);
|
|
|
|
BOOLEAN
|
|
WINAPI
|
|
AzpeIsParentWritable(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
);
|
|
|
|
BOOLEAN
|
|
WINAPI
|
|
AzpeUpdateChildren(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
);
|
|
|
|
BOOLEAN
|
|
WINAPI
|
|
AzpeCanCreateChildren(
|
|
IN AZPE_OBJECT_HANDLE AzpeCanCreateChildren
|
|
);
|
|
|
|
//
|
|
// Routines to change an object
|
|
//
|
|
|
|
DWORD
|
|
WINAPI
|
|
AzpeSetProperty(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG PropertyId,
|
|
IN PVOID PropertyValue
|
|
);
|
|
|
|
DWORD
|
|
WINAPI
|
|
AzpeSetObjectOptions(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG ObjectOptions
|
|
);
|
|
|
|
DWORD
|
|
WINAPI
|
|
AzpeAddPropertyItemGuid(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG PropertyId,
|
|
IN GUID *ObjectGuid
|
|
);
|
|
|
|
DWORD
|
|
WINAPI
|
|
AzpeAddPropertyItemGuidString(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG PropertyId,
|
|
IN WCHAR *ObjectGuidString
|
|
);
|
|
|
|
VOID
|
|
WINAPI
|
|
AzpeSetProviderData(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN PVOID ProviderData
|
|
);
|
|
|
|
PVOID
|
|
WINAPI
|
|
AzpeGetProviderData(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
);
|
|
|
|
DWORD
|
|
WINAPI
|
|
AzpeSetSecurityDescriptorIntoCache(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN PSECURITY_DESCRIPTOR pSD,
|
|
IN ULONG lPersistFlags,
|
|
IN PAZP_POLICY_USER_RIGHTS pAdminRights,
|
|
IN PAZP_POLICY_USER_RIGHTS pReadersRights,
|
|
IN PAZP_POLICY_USER_RIGHTS pDelegatedUserRights OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS pSaclRights OPTIONAL
|
|
);
|
|
|
|
PVOID
|
|
WINAPI
|
|
AzpeAllocateMemory(
|
|
IN SIZE_T Size
|
|
);
|
|
|
|
VOID
|
|
WINAPI
|
|
AzpeFreeMemory (
|
|
IN PVOID Buffer
|
|
);
|
|
|
|
BOOLEAN
|
|
WINAPI
|
|
AzpeIsParentWritable(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
);
|
|
|
|
BOOLEAN
|
|
WINAPI
|
|
AzpeUpdateChildren(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
);
|
|
|
|
BOOLEAN
|
|
WINAPI
|
|
AzpeCanCreateChildren(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
);
|
|
|
|
BOOL
|
|
WINAPI
|
|
AzpeAzStoreIsBatchUpdateMode(
|
|
IN AZPE_OBJECT_HANDLE hObject
|
|
);
|
|
|
|
AZPE_OBJECT_HANDLE
|
|
WINAPI
|
|
AzpeGetAuthorizationStore(
|
|
IN AZPE_OBJECT_HANDLE hObject
|
|
);
|
|
|
|
//
|
|
// Define the Azrole info structure to pass to the providers
|
|
//
|
|
|
|
AZPE_AZROLES_INFO AzGlAzrolesInfo = {
|
|
AZPE_AZROLES_INFO_VERSION_2,
|
|
|
|
//
|
|
// Routines exported by azroles to the provider
|
|
//
|
|
|
|
AzpeCreateObject,
|
|
AzpeObjectFinished,
|
|
AzpeGetProperty,
|
|
AzpeGetDeltaArray,
|
|
AzpeGetSecurityDescriptorFromCache,
|
|
AzpeObjectType,
|
|
AzpeDirtyBits,
|
|
AzpePersistenceGuid,
|
|
AzpeParentOfChild,
|
|
AzpeSetProperty,
|
|
AzpeSetObjectOptions,
|
|
AzpeAddPropertyItemSid,
|
|
AzpeAddPropertyItemGuid,
|
|
AzpeAddPropertyItemGuidString,
|
|
AzpeSetProviderData,
|
|
AzpeGetProviderData,
|
|
AzpeSetSecurityDescriptorIntoCache,
|
|
AzpeAllocateMemory,
|
|
AzpeFreeMemory,
|
|
//
|
|
// These routines available for Version 2 and higher only
|
|
//
|
|
|
|
AzpeIsParentWritable,
|
|
AzpeUpdateChildren,
|
|
AzpeCanCreateChildren,
|
|
AzpeAzStoreIsBatchUpdateMode,
|
|
AzpeGetAuthorizationStore
|
|
|
|
};
|
|
|
|
|
|
//
|
|
// The enumeration context describes the current state of an enumeration
|
|
// through the list of all the objects in the authz policy database
|
|
//
|
|
|
|
typedef struct _AZP_PERSIST_ENUM_CONTEXT {
|
|
|
|
//
|
|
// Stack Index
|
|
// The enumeration walks the tree of objects. While enumerating child objects,
|
|
// the context of the parent object enumeration is kept on the stack of contexts.
|
|
//
|
|
|
|
LONG StackIndex;
|
|
#define AZ_PERSIST_MAX_INDEX 4
|
|
|
|
//
|
|
// Object to return on the first call to AzpPersistEnumNext
|
|
//
|
|
PGENERIC_OBJECT GenericObject;
|
|
|
|
|
|
//
|
|
// Pointer to the current Generic Child Head being enumerated
|
|
//
|
|
PGENERIC_OBJECT_HEAD GenericChildHead[AZ_PERSIST_MAX_INDEX];
|
|
ULONG EnumerationContext[AZ_PERSIST_MAX_INDEX];
|
|
|
|
//
|
|
// Variables for remembering the last object that was returned
|
|
// These are maintained to allow us to detect if that object was deleted.
|
|
//
|
|
|
|
PGENERIC_OBJECT PreviousObject;
|
|
PGENERIC_OBJECT_HEAD PreviousGenericChildHead;
|
|
ULONG PreviousEnumContext;
|
|
|
|
} AZP_PERSIST_ENUM_CONTEXT, *PAZP_PERSIST_ENUM_CONTEXT;
|
|
|
|
DWORD
|
|
AzpPersistEnumOpen(
|
|
IN PGENERIC_OBJECT GenericObject,
|
|
OUT PVOID *PersistEnumContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine begins an enumeration of the objects in the authz policy database from
|
|
GenericObject
|
|
|
|
On entry, AzGlResource must be locked exclusive.
|
|
|
|
Arguments:
|
|
|
|
GenericObject - Specifies the object that is being queried
|
|
|
|
PersistEnumContext - Returns a context that can be passed to AzpPersistEnumNext.
|
|
This context must be closed by calling AzPersistClose.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
PAZP_PERSIST_ENUM_CONTEXT Context = NULL;
|
|
|
|
//
|
|
// Allocate memory for the context
|
|
//
|
|
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
|
|
Context = (PAZP_PERSIST_ENUM_CONTEXT) AzpAllocateHeap( sizeof(*Context), "PEENUMCX" );
|
|
|
|
if ( Context == NULL ) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
RtlZeroMemory( Context, sizeof(*Context) );
|
|
|
|
//
|
|
// Initialize it
|
|
//
|
|
|
|
Context->GenericObject = GenericObject;
|
|
Context->StackIndex = -1;
|
|
|
|
//
|
|
// Return the context to the caller
|
|
//
|
|
|
|
*PersistEnumContext = Context;
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
DWORD
|
|
AzpPersistEnumNext(
|
|
IN PVOID PersistEnumContext,
|
|
OUT PGENERIC_OBJECT *GenericObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the next object in the list of all objects in the authz policy database.
|
|
|
|
The caller may feel free to delete the returned object and its children. However,
|
|
the caller should not delete any other objects.
|
|
|
|
On entry, AzGlResource must be locked exclusive.
|
|
|
|
Arguments:
|
|
|
|
PersistEnumContext - A context describing the current state of the enumeration
|
|
|
|
GenericObject - Returns a pointer to an next object.
|
|
There is no reference on this object
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful (a GenericObject was returned)
|
|
ERROR_NO_MORE_ITEMS - No more items were available for enumeration
|
|
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
PGENERIC_OBJECT PreviousObject = NULL;
|
|
|
|
PAZP_PERSIST_ENUM_CONTEXT Context = (PAZP_PERSIST_ENUM_CONTEXT) PersistEnumContext;
|
|
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
|
|
//
|
|
// If this is the first call,
|
|
// return the AzAuthorizationStore itself.
|
|
//
|
|
|
|
if ( Context->PreviousObject == NULL ) {
|
|
*GenericObject = Context->GenericObject;
|
|
Context->PreviousObject = *GenericObject;
|
|
Context->PreviousGenericChildHead = NULL;
|
|
Context->PreviousEnumContext = NULL;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// Detect if the previously returned object was deleted by the caller
|
|
//
|
|
|
|
if ( Context->PreviousGenericChildHead == NULL ) {
|
|
PreviousObject = Context->GenericObject;
|
|
} else {
|
|
|
|
WinStatus = ObEnumObjects( Context->PreviousGenericChildHead,
|
|
TRUE, // return deleted objects
|
|
FALSE, // Don't refresh the cache
|
|
&Context->PreviousEnumContext,
|
|
&PreviousObject );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
if ( WinStatus != ERROR_NO_MORE_ITEMS ) {
|
|
return WinStatus;
|
|
}
|
|
PreviousObject = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the previously returned object wasn't deleted by the caller,
|
|
// process its children.
|
|
//
|
|
|
|
if ( Context->PreviousObject == PreviousObject ) {
|
|
|
|
//
|
|
// Only push onto the stack if the current object can have children
|
|
//
|
|
|
|
if ( Context->PreviousObject->ChildGenericObjectHead != NULL ) {
|
|
|
|
if ( Context->StackIndex+1 >= AZ_PERSIST_MAX_INDEX ) {
|
|
ASSERT(FALSE);
|
|
return ERROR_INTERNAL_ERROR;
|
|
}
|
|
|
|
Context->StackIndex++;
|
|
Context->GenericChildHead[Context->StackIndex] = Context->PreviousObject->ChildGenericObjectHead;
|
|
Context->EnumerationContext[Context->StackIndex] = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Loop until we find another object to return
|
|
//
|
|
|
|
for (;;) {
|
|
|
|
//
|
|
// Don't return pseudo objects to the caller.
|
|
//
|
|
|
|
if ( Context->GenericChildHead[Context->StackIndex]->ObjectType != OBJECT_TYPE_SID ) {
|
|
ULONG PreviousEnumContext;
|
|
|
|
//
|
|
// Get the next object from the current list
|
|
//
|
|
|
|
PreviousEnumContext = Context->EnumerationContext[Context->StackIndex];
|
|
|
|
WinStatus = ObEnumObjects( Context->GenericChildHead[Context->StackIndex],
|
|
TRUE, // return deleted objects
|
|
FALSE, // Don't refresh the cache
|
|
&Context->EnumerationContext[Context->StackIndex],
|
|
GenericObject );
|
|
|
|
//
|
|
// If that worked,
|
|
// remember the object that was found and return it to the caller
|
|
//
|
|
if ( WinStatus == NO_ERROR ) {
|
|
Context->PreviousObject = *GenericObject;
|
|
Context->PreviousGenericChildHead = Context->GenericChildHead[Context->StackIndex];
|
|
Context->PreviousEnumContext = PreviousEnumContext;
|
|
return NO_ERROR;
|
|
}
|
|
if ( WinStatus != ERROR_NO_MORE_ITEMS ) {
|
|
return WinStatus;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Move on to the next set of sibling object types.
|
|
//
|
|
|
|
Context->EnumerationContext[Context->StackIndex] = 0;
|
|
if ( Context->GenericChildHead[Context->StackIndex]->SiblingGenericObjectHead != NULL ) {
|
|
Context->GenericChildHead[Context->StackIndex] = Context->GenericChildHead[Context->StackIndex]->SiblingGenericObjectHead;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// There are no more sibling object types for the same parent.
|
|
// Continue the enumeration of the parent objects
|
|
//
|
|
|
|
if ( Context->StackIndex == 0 ) {
|
|
return ERROR_NO_MORE_ITEMS;
|
|
}
|
|
|
|
Context->StackIndex--;
|
|
|
|
}
|
|
}
|
|
|
|
VOID
|
|
AzpPersistEnumClose(
|
|
IN PVOID PersistEnumContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns free any resources consumed by the PersistEnumContext.
|
|
|
|
On entry, AzGlResource must be locked exclusive.
|
|
|
|
Arguments:
|
|
|
|
PersistEnumContext - A context describing the current state of the enumeration
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
AzpFreeHeap( PersistEnumContext );
|
|
}
|
|
|
|
//
|
|
// Operation being reconciled
|
|
//
|
|
|
|
typedef enum _RECONCILE_TYPE {
|
|
IsOpen, // AzPersistOpen
|
|
PreUpdateCache, // AzPersistUpdateCache (prior to calling provider), AzPersistUpdateChildrenCache
|
|
IsUpdateCache, // AzPersistUpdateCache
|
|
IsUpdateChildCache, // AzPersisUpdateChildrenCache
|
|
IsRefresh // AzPersistRefresh
|
|
} RECONCILE_TYPE, *PRECONCILE_TYPE;
|
|
|
|
DWORD
|
|
AzpPersistReconcileDeltaArray(
|
|
IN PGENERIC_OBJECT GenericObject,
|
|
IN PGENERIC_OBJECT_LIST GenericObjectList,
|
|
IN RECONCILE_TYPE ReconType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Implements AzpPersistReconcile for a single generic object list
|
|
|
|
On entry, AzGlResource must be locked exclusive.
|
|
PersistCritSect must be locked. (unless this is a refresh)
|
|
|
|
Arguments:
|
|
|
|
GenericObject - Specifies the object in the cache that is to be reconciled
|
|
|
|
GenericObjectList - Specifies the delta array to reconcile.
|
|
|
|
ReconType - Specifies the routine that called the persistence provider
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
DWORD SavedWinStatus = NO_ERROR;
|
|
DWORD WinStatus;
|
|
|
|
ULONG Index;
|
|
ULONG DeltaIndex;
|
|
PAZP_DELTA_ENTRY DeltaEntry;
|
|
|
|
|
|
//
|
|
// Skip back links
|
|
//
|
|
|
|
if ( GenericObjectList->IsBackLink ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Ensure each entry in the delta array has an entry in the object list
|
|
//
|
|
|
|
for ( DeltaIndex=0; DeltaIndex<GenericObjectList->DeltaArray.UsedCount; DeltaIndex++ ) {
|
|
|
|
//
|
|
// If the entry corresponds to an "add",
|
|
// do an "add" to the object list.
|
|
//
|
|
|
|
DeltaEntry = (PAZP_DELTA_ENTRY)GenericObjectList->DeltaArray.Array[DeltaIndex];
|
|
|
|
if ( DeltaEntry->DeltaFlags & AZP_DELTA_ADD ) {
|
|
PAZP_STRING StringToAdd;
|
|
AZP_STRING SidString;
|
|
ULONG Flags = AZP_FLAGS_RECONCILE;
|
|
|
|
//
|
|
// Compute the parameters to ObAddPropertyItem
|
|
//
|
|
if ( DeltaEntry->DeltaFlags & AZP_DELTA_SID ) {
|
|
AzpInitSid( &SidString, DeltaEntry->Sid );
|
|
StringToAdd = &SidString;
|
|
} else {
|
|
AzPrint(( AZD_REF, "AzpPersistReconcileOne (by guid): " ));
|
|
AzpDumpGuid( AZD_REF, &DeltaEntry->Guid );
|
|
AzPrint(( AZD_REF, "\n" ));
|
|
Flags |= AZP_FLAGS_BY_GUID;
|
|
StringToAdd = (PAZP_STRING) &DeltaEntry->Guid;
|
|
}
|
|
|
|
|
|
//
|
|
// Add the property item
|
|
//
|
|
|
|
WinStatus = ObAddPropertyItem(
|
|
GenericObject,
|
|
GenericObjectList,
|
|
Flags,
|
|
StringToAdd );
|
|
|
|
//
|
|
// We ignore links that have already been updated.
|
|
// During the update child cache routine, we want to ignore objects that exist in the store
|
|
// but not yet in the cache since the user has not called an update on the entire policy cache
|
|
//
|
|
|
|
if ( (WinStatus != NO_ERROR && WinStatus != ERROR_ALREADY_EXISTS) &&
|
|
((ReconType != IsUpdateChildCache) ||
|
|
(ReconType == IsUpdateChildCache && WinStatus != ERROR_NOT_FOUND)) ) {
|
|
|
|
AzPrint(( AZD_REF,
|
|
"AzpPersistReconcileOne: ObAddPropertyItem failed %ld\n",
|
|
WinStatus
|
|
));
|
|
SavedWinStatus = WinStatus;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove entries from the object list that should no longer exist
|
|
//
|
|
|
|
for ( Index=0; Index<GenericObjectList->GenericObjects.UsedCount; ) {
|
|
|
|
PGENERIC_OBJECT LinkedToGenericObject;
|
|
DWORD DeltaFlags;
|
|
GUID *Guid;
|
|
|
|
//
|
|
// Determine if the object is in the DeltaArray
|
|
//
|
|
|
|
LinkedToGenericObject = (PGENERIC_OBJECT)(GenericObjectList->GenericObjects.Array[Index]);
|
|
|
|
if ( LinkedToGenericObject->ObjectType == OBJECT_TYPE_SID ) {
|
|
DeltaFlags = AZP_DELTA_SID;
|
|
Guid = (GUID *)&LinkedToGenericObject->ObjectName->ObjectName;
|
|
} else {
|
|
DeltaFlags = 0;
|
|
Guid = &LinkedToGenericObject->PersistenceGuid;
|
|
}
|
|
|
|
if ( ObLookupDelta( DeltaFlags,
|
|
Guid,
|
|
&GenericObjectList->DeltaArray,
|
|
&DeltaIndex ) ) {
|
|
|
|
//
|
|
// Found it.
|
|
//
|
|
// If the entry is an "add" entry,
|
|
// this is one we added above,
|
|
// just move to the next entry.
|
|
//
|
|
|
|
DeltaEntry = (PAZP_DELTA_ENTRY)GenericObjectList->DeltaArray.Array[DeltaIndex];
|
|
|
|
if ( DeltaEntry->DeltaFlags & AZP_DELTA_ADD ) {
|
|
Index++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Link should be removed
|
|
//
|
|
// Either there was no entry in the DeltaArray (or the entry is a "remove" entry
|
|
//
|
|
// Remove the links in both directions
|
|
//
|
|
|
|
ObRemoveObjectListLink( GenericObject,
|
|
GenericObjectList,
|
|
Index );
|
|
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// Clear the bit that got us here
|
|
//
|
|
|
|
GenericObject->PersistDirtyBits &= ~GenericObjectList->DirtyBit;
|
|
|
|
//
|
|
// Free entries from the delta arrays that were added by the persist provider.
|
|
//
|
|
|
|
ObFreeDeltaArray( &GenericObjectList->DeltaArray, FALSE );
|
|
|
|
return SavedWinStatus;
|
|
|
|
}
|
|
|
|
DWORD
|
|
AzpPersistReconcileOne(
|
|
IN PGENERIC_OBJECT GenericObject,
|
|
IN RECONCILE_TYPE ReconType,
|
|
IN BOOLEAN OpenFailed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Implements AzpPersistReconcile for a single object.
|
|
|
|
On entry, AzGlResource must be locked exclusive.
|
|
PersistCritSect must be locked. (unless this is a refresh)
|
|
|
|
Arguments:
|
|
|
|
GenericObject - Specifies the object in the cache that is to be reconciled
|
|
|
|
ReconType - Specifies the routine that called the persistence provider
|
|
|
|
OpenFailed - TRUE if the open call to the persistence provider failed.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
|
|
DWORD SavedWinStatus = NO_ERROR;
|
|
|
|
ULONG DirtyBits;
|
|
PGENERIC_OBJECT_LIST GenericObjectList;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
ASSERT( ReconType == IsRefresh || ReconType == PreUpdateCache || AzpIsCritsectLocked( &GenericObject->AzStoreObject->PersistCritSect ) );
|
|
|
|
AzPrint(( AZD_PERSIST,
|
|
"AzpPersistReconcileOne: %ws %ld 0x%lx 0x%lx\n",
|
|
GenericObject->ObjectName == NULL ? NULL : GenericObject->ObjectName->ObjectName.String,
|
|
GenericObject->ObjectType,
|
|
GenericObject->PersistDirtyBits,
|
|
GenericObject->DirtyBits ));
|
|
|
|
if ( ReconType == IsOpen ) {
|
|
ASSERT( GenericObject->DirtyBits == 0 || GenericObject->ObjectType == OBJECT_TYPE_AZAUTHSTORE);
|
|
}
|
|
|
|
//
|
|
// If the provider didn't create this object, then the object doesn't exist in the store.
|
|
//
|
|
|
|
if ( (GenericObject->PersistDirtyBits & AZ_DIRTY_CREATE) == 0 ) {
|
|
|
|
//
|
|
// Never delete cached objects if the persistence provider failed.
|
|
// The provider may have bailed before processing the object.
|
|
// If an object is abosultely clean, then we should just let it stay there.
|
|
//
|
|
|
|
if ( OpenFailed ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( ReconType == IsOpen ) {
|
|
ASSERT( GenericObject->PersistDirtyBits & AZ_DIRTY_CREATE );
|
|
}
|
|
|
|
//
|
|
// If the app didn't create this object either,
|
|
// then the object should be deleted from the cache,
|
|
// Unless it already has been marked for deletion.
|
|
// This is also not marked for deletion if the children of AzApplication/AzScope
|
|
// are being loaded into cache
|
|
//
|
|
// NOTE: other attributes might be dirty. This simply reflects the fact
|
|
// that during reconciliation a delete overides a modification.
|
|
//
|
|
|
|
if ( (GenericObject->DirtyBits & AZ_DIRTY_CREATE) == 0 ) {
|
|
|
|
//
|
|
// Mark the entry (and its child objects) as deleted
|
|
// We do this since other threads may have references to the objects.
|
|
// We want to ensure those threads know the objects are deleted.
|
|
//
|
|
|
|
ObMarkObjectDeleted( GenericObject );
|
|
|
|
GenericObject = NULL; // Don't reference this again
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// If the provider created this object,
|
|
// and successfully set all of the attributes it knows about,
|
|
// clean up the attributes.
|
|
//
|
|
|
|
} else if ((GenericObject->Flags & GENOBJ_FLAGS_PERSIST_OK) != 0 ) {
|
|
|
|
//
|
|
// Never bother updating objects if the persistence provider failed and this
|
|
// is AzInitialize. We'll delete the attributes soon anyway.
|
|
//
|
|
|
|
if ( OpenFailed ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Compute the properties that were neither written by
|
|
// the provider nor by the application.
|
|
|
|
DirtyBits = 0xFFFFFFFF & (~GenericObject->DirtyBits) & (~GenericObject->PersistDirtyBits);
|
|
|
|
//
|
|
// All such properties should be set to their default value
|
|
//
|
|
|
|
WinStatus = ObSetPropertyToDefault( GenericObject, DirtyBits );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
|
|
SavedWinStatus = WinStatus;
|
|
AzPrint(( AZD_CRITICAL, "AzpPersistReconcile: Cannot refresh object: %ws %ld\n", GenericObject->ObjectName->ObjectName.String, WinStatus ));
|
|
// Continue processing
|
|
}
|
|
|
|
//
|
|
// Fix up all of the links from this object
|
|
//
|
|
// Walk all of the GenericObjectLists rooted on by this object
|
|
//
|
|
|
|
for ( GenericObjectList = GenericObject->GenericObjectLists;
|
|
GenericObjectList != NULL;
|
|
GenericObjectList = GenericObjectList->NextGenericObjectList ) {
|
|
|
|
|
|
//
|
|
// Reconcile this individual object list entry.
|
|
//
|
|
|
|
WinStatus = AzpPersistReconcileDeltaArray( GenericObject, GenericObjectList, ReconType );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
|
|
SavedWinStatus = WinStatus;
|
|
// ASSERT(FALSE);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
//
|
|
// If the object wasn't deleted,
|
|
// clean it up.
|
|
//
|
|
|
|
if ( GenericObject != NULL ) {
|
|
|
|
//
|
|
// Clear any bits the provider left lying around
|
|
//
|
|
|
|
GenericObject->PersistDirtyBits = 0;
|
|
GenericObject->Flags &= !GENOBJ_FLAGS_PERSIST_OK;
|
|
|
|
//
|
|
// Indicate whether the cache entry needs to be refreshed.
|
|
//
|
|
// If the provider didn't successfully update the cache,
|
|
// leave the refresh bit alone.
|
|
//
|
|
// If the caller IsOpen,
|
|
// we don't care since we'll be deleting the cache shortly.
|
|
// If the caller IsUpdateCache,
|
|
// we haven't changed the state one way or another.
|
|
// The cache is guaranteed to not be worse than it was before. But that's
|
|
// all we can say.
|
|
// If the caller IsRefresh,
|
|
// that bit was set when we were called so we leave it set.
|
|
//
|
|
|
|
if ( OpenFailed ) {
|
|
/* Drop through */
|
|
|
|
//
|
|
// If we detected an error,
|
|
// mark the item as needing refreshed since the current state of the cache is bogus.
|
|
//
|
|
|
|
} else if ( SavedWinStatus != NO_ERROR ) {
|
|
|
|
GenericObject->Flags |= GENOBJ_FLAGS_REFRESH_ME;
|
|
|
|
//
|
|
// Otherwise, the cache entry is known to match the underlying store
|
|
//
|
|
|
|
} else {
|
|
|
|
GenericObject->Flags &= ~GENOBJ_FLAGS_REFRESH_ME;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Free entries from the delta arrays that were added by the persist provider.
|
|
//
|
|
|
|
for ( GenericObjectList = GenericObject->GenericObjectLists;
|
|
GenericObjectList != NULL;
|
|
GenericObjectList = GenericObjectList->NextGenericObjectList ) {
|
|
|
|
ObFreeDeltaArray( &GenericObjectList->DeltaArray, FALSE );
|
|
|
|
}
|
|
}
|
|
|
|
return SavedWinStatus;
|
|
}
|
|
|
|
DWORD
|
|
AzpPersistReconcile(
|
|
IN PGENERIC_OBJECT GenericObject,
|
|
IN RECONCILE_TYPE ReconType,
|
|
IN BOOLEAN OpenFailed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reconciles any inconsistencies in the cache that were left around
|
|
by the persistence provider during the initial population of the cache or
|
|
during a periodic cache update.
|
|
|
|
Examples, are given in the code below. However, the philosophy is the persistence
|
|
providers shouldn't have to worry about ordering issues and naming issues. For instance,
|
|
they should be able to link to objects that are not yet in the cache. They should
|
|
be able to create an object with the same name as a deleted object.
|
|
|
|
On entry, AzGlResource must be locked exclusive.
|
|
|
|
Arguments:
|
|
|
|
GenericObject - Specifies the object that is being queried
|
|
|
|
ReconType - Specifies the routine that called the persistence provider
|
|
|
|
OpenFailed - TRUE if the open call to the persistence provider failed.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
DWORD SavedWinStatus = NO_ERROR;
|
|
|
|
PVOID EnumContext = NULL;
|
|
PGENERIC_OBJECT NextGenericObject;
|
|
BOOL FixMoreNames = TRUE;
|
|
PNEW_OBJECT_NAME NewObjectName;
|
|
PLIST_ENTRY ListEntry;
|
|
PAZP_AZSTORE AzAuthorizationStore = GenericObject->AzStoreObject;
|
|
|
|
//
|
|
// Prepare to enumerate all of the objects in the cache
|
|
//
|
|
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
|
|
WinStatus = AzpPersistEnumOpen( GenericObject, &EnumContext );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
SavedWinStatus = WinStatus;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If this call is from UpdateChildrenCache, we donot want to call AzpPersistReconcileOne
|
|
// on the parent object, but only on its children
|
|
//
|
|
|
|
if ( ReconType == IsUpdateChildCache ) {
|
|
|
|
WinStatus = AzpPersistEnumNext( EnumContext, &NextGenericObject );
|
|
|
|
if ( WinStatus != NO_ERROR) {
|
|
if ( WinStatus != ERROR_NO_MORE_ITEMS ) {
|
|
SavedWinStatus = WinStatus;
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT( NextGenericObject == GenericObject );
|
|
|
|
}
|
|
|
|
//
|
|
// Loop through each object
|
|
//
|
|
|
|
for (;;) {
|
|
|
|
//
|
|
// Get the next object
|
|
//
|
|
|
|
WinStatus = AzpPersistEnumNext( EnumContext, &NextGenericObject );
|
|
|
|
if ( WinStatus != NO_ERROR) {
|
|
if ( WinStatus != ERROR_NO_MORE_ITEMS ) {
|
|
SavedWinStatus = WinStatus;
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Reconcile that one object
|
|
//
|
|
|
|
WinStatus = AzpPersistReconcileOne( NextGenericObject, ReconType, OpenFailed );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
SavedWinStatus = WinStatus;
|
|
AzPrint(( AZD_CRITICAL,
|
|
"AzpPersistReconcile: Cannot reconcile object: %ws %ld %ld\n",
|
|
NextGenericObject->ObjectName == NULL ? NULL : NextGenericObject->ObjectName->ObjectName.String,
|
|
NextGenericObject->ObjectType,
|
|
WinStatus ));
|
|
// Continue processing
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Loop attempting to fix up any name conflicts.
|
|
// Keep looping as long as a pass fixed at least one name
|
|
//
|
|
// Don't fix name comflicts if we're just cleaning up.
|
|
//
|
|
|
|
while ( ReconType != PreUpdateCache && FixMoreNames ) {
|
|
|
|
FixMoreNames = FALSE;
|
|
|
|
//
|
|
// Loop handling each conflicting name
|
|
//
|
|
|
|
for ( ListEntry = AzAuthorizationStore->NewNames.Flink;
|
|
ListEntry != &AzAuthorizationStore->NewNames;
|
|
) {
|
|
|
|
NewObjectName = CONTAINING_RECORD( ListEntry,
|
|
NEW_OBJECT_NAME,
|
|
Next );
|
|
// Entry might be deleted below
|
|
ListEntry = ListEntry->Flink;
|
|
|
|
//
|
|
// Try to set the name
|
|
//
|
|
|
|
WinStatus = ObSetProperty(
|
|
NewObjectName->GenericObject,
|
|
AZP_FLAGS_RECONCILE,
|
|
AZ_PROP_NAME,
|
|
&NewObjectName->ObjectName );
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
ObFreeNewName( NewObjectName );
|
|
FixMoreNames = TRUE;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If we couldn't fix all of the collided names,
|
|
// return the problem to the caller.
|
|
//
|
|
|
|
if ( SavedWinStatus == NO_ERROR &&
|
|
!IsListEmpty( &AzAuthorizationStore->NewNames ) ) {
|
|
|
|
SavedWinStatus = ERROR_ALREADY_EXISTS;
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
if ( EnumContext != NULL ) {
|
|
AzpPersistEnumClose( EnumContext );
|
|
}
|
|
|
|
//
|
|
// Free the list of conflicting names
|
|
//
|
|
|
|
while ( !IsListEmpty( &AzAuthorizationStore->NewNames ) ) {
|
|
|
|
ListEntry = RemoveHeadList( &AzAuthorizationStore->NewNames );
|
|
|
|
NewObjectName = CONTAINING_RECORD( ListEntry,
|
|
NEW_OBJECT_NAME,
|
|
Next );
|
|
|
|
ObFreeNewName( NewObjectName );
|
|
|
|
}
|
|
return SavedWinStatus;
|
|
}
|
|
|
|
|
|
//
|
|
// List of providers that are built into azroles.dll
|
|
//
|
|
|
|
struct {
|
|
LPWSTR PolicyUrlPrefix;
|
|
AZ_PERSIST_PROVIDER_INITIALIZE ProviderInitRoutine;
|
|
} AzGlInternalProviders[] = {
|
|
#ifdef USE_INTERNAL_MSXML
|
|
{ AZ_XML_PROVIDER_NAME, XmlProviderInitialize },
|
|
#endif // USE_INTERNAL_MSXML
|
|
{ AZ_AD_PROVIDER_NAME, AdProviderInitialize },
|
|
};
|
|
#define INTERNAL_PROVIDER_COUNT (sizeof(AzGlInternalProviders)/sizeof(AzGlInternalProviders[0]))
|
|
|
|
DWORD
|
|
AzpPersistDetermineProvider(
|
|
IN PAZP_AZSTORE AzAuthorizationStore
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines which provider to use for a particular Policy URL.
|
|
|
|
Arguments:
|
|
|
|
AzAuthorizationStore - A pointer to the authorization store object the provider is needed for
|
|
The PolicyUrl should already have been set.
|
|
On successfull return, the ProviderInfo and ProviderDll will be properly set.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_INVALID_PARAMETER - The policy URL cannot identify the provider
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
|
|
ULONG Index;
|
|
ULONG UrlPrefixLength;
|
|
AZ_PERSIST_PROVIDER_INITIALIZE ProviderInitRoutine = NULL;
|
|
PAZPE_PROVIDER_INFO ProviderInfo;
|
|
LPWSTR PolicyUrl = AzAuthorizationStore->PolicyUrl.String;
|
|
LPWSTR KeyName = NULL;
|
|
HKEY KeyHandle = NULL;
|
|
HMODULE DllHandle = NULL;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
ASSERT( AzAuthorizationStore->PolicyUrl.String != NULL );
|
|
ASSERT( AzAuthorizationStore->ProviderInfo == NULL );
|
|
ASSERT( AzAuthorizationStore->ProviderDll == NULL );
|
|
|
|
|
|
//
|
|
// Loop through the internal providers finding the one specified
|
|
//
|
|
|
|
for ( Index=0; Index<INTERNAL_PROVIDER_COUNT; Index++ ) {
|
|
|
|
UrlPrefixLength = (ULONG) wcslen(AzGlInternalProviders[Index].PolicyUrlPrefix);
|
|
|
|
|
|
//
|
|
// Check if the prefix of the provider matches the passed in URL
|
|
//
|
|
if (_wcsnicmp( AzGlInternalProviders[Index].PolicyUrlPrefix, PolicyUrl, UrlPrefixLength) == 0) {
|
|
|
|
//
|
|
// Ensure the next character is a :
|
|
//
|
|
|
|
if ( UrlPrefixLength == 0 || PolicyUrl[UrlPrefixLength] == L':' ) {
|
|
ProviderInitRoutine = AzGlInternalProviders[Index].ProviderInitRoutine;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If we didn't find an internal provider,
|
|
// load one
|
|
//
|
|
|
|
if ( ProviderInitRoutine == NULL ) {
|
|
LPWSTR ColonPtr;
|
|
ULONG KeyNameLen;
|
|
DWORD ValueType;
|
|
WCHAR ProviderDll[MAX_PATH+1];
|
|
DWORD ProviderDllSize;
|
|
|
|
//
|
|
// Determin the length of the name of the registry key
|
|
//
|
|
|
|
ColonPtr = wcschr( PolicyUrl, L':' );
|
|
|
|
if ( ColonPtr == NULL ) {
|
|
WinStatus = ERROR_BAD_PROVIDER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
UrlPrefixLength = (ULONG)(ColonPtr - PolicyUrl);
|
|
KeyNameLen = AZ_REGISTRY_PROVIDER_KEY_NAME_LEN +
|
|
1 +
|
|
UrlPrefixLength +
|
|
1;
|
|
|
|
//
|
|
// Build the name of the registry key
|
|
//
|
|
|
|
SafeAllocaAllocate( KeyName, KeyNameLen * sizeof(WCHAR) );
|
|
|
|
if ( KeyName == NULL ) {
|
|
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory( KeyName, AZ_REGISTRY_PROVIDER_KEY_NAME, AZ_REGISTRY_PROVIDER_KEY_NAME_LEN * sizeof(WCHAR) );
|
|
KeyName[AZ_REGISTRY_PROVIDER_KEY_NAME_LEN] = '\\';
|
|
RtlCopyMemory( &KeyName[AZ_REGISTRY_PROVIDER_KEY_NAME_LEN+1],
|
|
PolicyUrl,
|
|
UrlPrefixLength*sizeof(WCHAR) );
|
|
KeyName[AZ_REGISTRY_PROVIDER_KEY_NAME_LEN+1+UrlPrefixLength] = '\0';
|
|
|
|
AzPrint(( AZD_PERSIST, "AzpPersistDetermineProvider: Open Provider reg key at 'HKLM\\%ws'\n", KeyName ));
|
|
|
|
//
|
|
// Open the registry key
|
|
//
|
|
|
|
WinStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
KeyName,
|
|
0,
|
|
KEY_READ,
|
|
&KeyHandle );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
AzPrint(( AZD_CRITICAL, "AzpPersistDetermineProvider: Cannot open Provider reg key at 'HKLM\\%ws' %ld\n", KeyName, WinStatus ));
|
|
if ( WinStatus == ERROR_FILE_NOT_FOUND ) {
|
|
WinStatus = ERROR_BAD_PROVIDER;
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Read the name of the provider dll
|
|
//
|
|
|
|
ProviderDllSize = sizeof(ProviderDll);
|
|
|
|
WinStatus = RegQueryValueEx( KeyHandle,
|
|
AZ_REGISTRY_PROVIDER_DLL_VALUE_NAME,
|
|
NULL,
|
|
&ValueType,
|
|
(LPBYTE)ProviderDll,
|
|
&ProviderDllSize );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
|
|
AzPrint(( AZD_CRITICAL,
|
|
"AzpPersistDetermineProvider: Cannot open Provider reg value at 'HKLM\\%ws\\%ws' %ld\n",
|
|
KeyName,
|
|
AZ_REGISTRY_PROVIDER_DLL_VALUE_NAME,
|
|
WinStatus ));
|
|
|
|
if ( WinStatus == ERROR_FILE_NOT_FOUND ) {
|
|
WinStatus = ERROR_BAD_PROVIDER;
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Load the library
|
|
//
|
|
|
|
DllHandle = LoadLibrary( ProviderDll );
|
|
|
|
if ( DllHandle == NULL ) {
|
|
|
|
WinStatus = GetLastError();
|
|
|
|
AzPrint(( AZD_CRITICAL,
|
|
"AzpPersistDetermineProvider: Cannot load libary '%ws' %ld\n",
|
|
ProviderDll,
|
|
WinStatus ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the address of the provider init routine
|
|
//
|
|
|
|
ProviderInitRoutine = (AZ_PERSIST_PROVIDER_INITIALIZE)
|
|
GetProcAddress( DllHandle, AZ_PERSIST_PROVIDER_INITIALIZE_NAME );
|
|
|
|
if ( ProviderInitRoutine == NULL ) {
|
|
|
|
WinStatus = GetLastError();
|
|
|
|
AzPrint(( AZD_CRITICAL,
|
|
"AzpPersistDetermineProvider: libary '%ws' does not export '%s': %ld\n",
|
|
ProviderDll,
|
|
AZ_PERSIST_PROVIDER_INITIALIZE_NAME,
|
|
WinStatus ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// A provider has been found
|
|
// Ask the provider for the provider info
|
|
//
|
|
|
|
WinStatus = ProviderInitRoutine( &AzGlAzrolesInfo, &ProviderInfo );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Ensure the provider info is a version we understand
|
|
//
|
|
|
|
if ( ProviderInfo->ProviderInfoVersion < AZPE_PROVIDER_INFO_VERSION_1 ) {
|
|
WinStatus = ERROR_UNKNOWN_REVISION;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Return the information to the caller
|
|
//
|
|
|
|
AzAuthorizationStore->ProviderInfo = ProviderInfo;
|
|
AzAuthorizationStore->ProviderDll = DllHandle;
|
|
DllHandle = NULL;
|
|
WinStatus = NO_ERROR;
|
|
|
|
//
|
|
// Free locally used resources
|
|
//
|
|
Cleanup:
|
|
SafeAllocaFree( KeyName );
|
|
if ( KeyHandle != NULL ) {
|
|
RegCloseKey( KeyHandle );
|
|
}
|
|
if ( DllHandle != NULL ) {
|
|
FreeLibrary( DllHandle );
|
|
}
|
|
return WinStatus;
|
|
|
|
}
|
|
|
|
DWORD
|
|
AzPersistOpen(
|
|
IN PAZP_AZSTORE AzAuthorizationStore,
|
|
IN BOOL CreatePolicy
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine open the authz policy database.
|
|
This routine also reads the policy database into cache.
|
|
|
|
On Success, the caller should call AzPersistClose to free any resources
|
|
consumed by the open.
|
|
|
|
This routine routes the request to the correct provider.
|
|
|
|
On entry, AzGlResource must be locked exclusive.
|
|
This routine temporarily drops the AzGlResource while waiting for updates to complete.
|
|
|
|
Arguments:
|
|
|
|
AzAuthorizationStore - Specifies the policy database that is to be read.
|
|
|
|
CreatePolicy - TRUE if the policy database is to be created.
|
|
FALSE if the policy database already exists
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
ERROR_ALREADY_EXISTS - CreatePolicy is TRUE and the policy already exists
|
|
ERROR_FILE_NOT_FOUND - CreatePolicy is FALSE and the policy does not already exist
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
DWORD TempWinStatus;
|
|
DWORD lPersistFlags;
|
|
BOOLEAN CritSectLocked = FALSE;
|
|
BOOLEAN ResourceLocked = TRUE;
|
|
LPWSTR pwszTargetMachine=NULL;
|
|
|
|
//
|
|
// Compute the flags
|
|
//
|
|
|
|
lPersistFlags = AZPE_FLAGS_PERSIST_OPEN;
|
|
|
|
//
|
|
// Drop the global crit sect across the call to the provider to ensure on
|
|
// UpdateCache that it doesn't lock out AccessCheck calls while it hits the wire
|
|
//
|
|
// Grab the Persistence crit sect maintaining locking order
|
|
//
|
|
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
AzpUnlockResource( &AzGlResource );
|
|
ResourceLocked = FALSE;
|
|
SafeEnterCriticalSection( &AzAuthorizationStore->PersistCritSect );
|
|
CritSectLocked = TRUE;
|
|
|
|
//
|
|
// Determine which provider to use
|
|
//
|
|
|
|
WinStatus = AzpPersistDetermineProvider( AzAuthorizationStore );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Call the appropriate provider
|
|
//
|
|
|
|
WinStatus = AzAuthorizationStore->ProviderInfo->AzPersistOpen(
|
|
AzAuthorizationStore->PolicyUrl.String,
|
|
(AZPE_OBJECT_HANDLE)AzAuthorizationStore,
|
|
lPersistFlags,
|
|
CreatePolicy,
|
|
&AzAuthorizationStore->PersistContext,
|
|
&pwszTargetMachine );
|
|
|
|
if ( WinStatus == NO_ERROR &&
|
|
pwszTargetMachine ) {
|
|
|
|
//
|
|
// target machine name is returned
|
|
// add it to the authorization store object, which will be freed when
|
|
// authorization store object is freed
|
|
//
|
|
AzpInitString(&AzAuthorizationStore->TargetMachine, pwszTargetMachine);
|
|
}
|
|
|
|
//
|
|
// Restore the lock now that we're out of the provider
|
|
//
|
|
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
ResourceLocked = TRUE;
|
|
|
|
//
|
|
// Ensure the provider set the version
|
|
//
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
ASSERT( (((PGENERIC_OBJECT)AzAuthorizationStore)->PersistDirtyBits & (AZ_DIRTY_AZSTORE_MAJOR_VERSION | AZ_DIRTY_AZSTORE_MINOR_VERSION)) == (AZ_DIRTY_AZSTORE_MAJOR_VERSION | AZ_DIRTY_AZSTORE_MINOR_VERSION) );
|
|
}
|
|
|
|
//
|
|
// If we didn't create the underlying store,
|
|
// Turn off the dirty bits.
|
|
// (AZ_DIRTY_CREATE was set when the object was created. But the object really isn't dirty.)
|
|
//
|
|
|
|
if ( !CreatePolicy ) {
|
|
AzAuthorizationStore->GenericObject.DirtyBits = 0;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Major and minor versions are not to be set externally. But they always need
|
|
// to be persisted in the authorization store object. We thus set these bits dirty.
|
|
// This will make sure that these two properties are persisted upon submit.
|
|
//
|
|
|
|
((PGENERIC_OBJECT)AzAuthorizationStore)->DirtyBits |=
|
|
(AZ_DIRTY_AZSTORE_MAJOR_VERSION | AZ_DIRTY_AZSTORE_MINOR_VERSION);
|
|
}
|
|
|
|
//
|
|
// Now that the entire cache has been updated from the store
|
|
// fix up any issues in the cache.
|
|
//
|
|
|
|
TempWinStatus = AzpPersistReconcile( (PGENERIC_OBJECT)AzAuthorizationStore, IsOpen, WinStatus != NO_ERROR );
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
WinStatus = TempWinStatus;
|
|
}
|
|
|
|
//
|
|
// If we created the store,
|
|
// set the default AZ_PROP_APPLY_STORE_SACL based on whether the caller has the privilege
|
|
//
|
|
|
|
if ( CreatePolicy ) {
|
|
AzAuthorizationStore->GenericObject.ApplySacl = AzAuthorizationStore->HasSecurityPrivilege;
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
if ( !ResourceLocked ) {
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
}
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
if ( CritSectLocked ) {
|
|
SafeLeaveCriticalSection( &AzAuthorizationStore->PersistCritSect );
|
|
}
|
|
return WinStatus;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
AzPersistUpdateCache(
|
|
IN PAZP_AZSTORE AzAuthorizationStore
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates the cache to reflect the current authz policy database.
|
|
|
|
This routine routes the request to the correct provider.
|
|
|
|
On entry, AzGlResource must be locked exclusive.
|
|
This routine temporarily drops the AzGlResource while waiting for other updates to complete.
|
|
|
|
Arguments:
|
|
|
|
AzAuthorizationStore - Specifies the policy database that is to be read.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
ERROR_ALREADY_EXISTS - CreatePolicy is TRUE and the policy already exists
|
|
ERROR_FILE_NOT_FOUND - CreatePolicy is FALSE and the policy does not already exist
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
DWORD TempWinStatus = NO_ERROR;
|
|
BOOLEAN CritSectLocked = FALSE;
|
|
|
|
//
|
|
// Provider decides what to update. It passes back that information
|
|
// via the flag.
|
|
//
|
|
|
|
ULONG ulEffectiveUpdateFlag = 0;
|
|
|
|
//
|
|
// Walk the cache cleaning up from any previous failed attempt.
|
|
//
|
|
|
|
WinStatus = AzpPersistReconcile( (PGENERIC_OBJECT)AzAuthorizationStore, PreUpdateCache, TRUE );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Drop the global crit sect across the call to the provider to ensure on
|
|
// UpdateCache that it doesn't lock out AccessCheck calls while it hits the wire
|
|
//
|
|
// Grab the Persistence crit sect maintaining locking order
|
|
//
|
|
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
AzpUnlockResource( &AzGlResource );
|
|
SafeEnterCriticalSection( &AzAuthorizationStore->PersistCritSect );
|
|
CritSectLocked = TRUE;
|
|
|
|
|
|
//
|
|
// Call the appropriate provider
|
|
// The peristence provider will drop
|
|
//
|
|
|
|
WinStatus = AzAuthorizationStore->ProviderInfo->AzPersistUpdateCache(
|
|
AzAuthorizationStore->PersistContext,
|
|
AZPE_FLAGS_PERSIST_UPDATE_CACHE,
|
|
&ulEffectiveUpdateFlag);
|
|
|
|
//
|
|
// Restore the lock now that we're out of the provider
|
|
//
|
|
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
|
|
|
|
//
|
|
// Now that the entire cache has been updated from the store
|
|
// If the store has been deleted, ERROR_FILE_NOT_FOUND is returned. In this case,
|
|
// the cache needs to cleaned up completely.
|
|
// fix up any issues in the cache.
|
|
//
|
|
|
|
if ( (AZPE_FLAG_CACHE_UPDATE_STORE_LEVEL & ulEffectiveUpdateFlag) || (WinStatus != NO_ERROR) )
|
|
{
|
|
TempWinStatus = AzpPersistReconcile( (PGENERIC_OBJECT)AzAuthorizationStore, IsUpdateCache,
|
|
(WinStatus != NO_ERROR &&
|
|
WinStatus != ERROR_FILE_NOT_FOUND) );
|
|
}
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
WinStatus = TempWinStatus;
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
if ( CritSectLocked ) {
|
|
SafeLeaveCriticalSection( &AzAuthorizationStore->PersistCritSect );
|
|
}
|
|
return WinStatus;
|
|
}
|
|
|
|
DWORD
|
|
AzPersistUpdateChildrenCache(
|
|
IN OUT PGENERIC_OBJECT GenericObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates the child objects (upto one level) under the object specified.
|
|
|
|
The request is routed to the relevant provider.
|
|
|
|
On entry, the global resource must be locked exclusive
|
|
|
|
Arguments:
|
|
|
|
GenericObject - Object whose children need to be updated (one-level only)
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus = 0;
|
|
DWORD TempWinStatus = 0;
|
|
BOOLEAN bCritSectLocked = FALSE;
|
|
|
|
PAZP_AZSTORE AzAuthorizationStore = (PAZP_AZSTORE) GenericObject->AzStoreObject;
|
|
|
|
//
|
|
// Validate that the provider supports lazy load
|
|
//
|
|
|
|
ASSERT( AzAuthorizationStore->ProviderInfo->ProviderInfoVersion >= AZPE_PROVIDER_INFO_VERSION_2 );
|
|
|
|
//
|
|
// If the parent object has not been submitted as yet, then no children exist for it.
|
|
// Simply return NO_ERROR
|
|
//
|
|
|
|
if ( (GenericObject->DirtyBits & AZ_DIRTY_CREATE) != 0 ) {
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
WinStatus = AzpPersistReconcile( GenericObject, PreUpdateCache, TRUE );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
AzpUnlockResource( &AzGlResource );
|
|
|
|
//
|
|
// Grab the Persistence crit sect maintaining locking order
|
|
//
|
|
|
|
SafeEnterCriticalSection( &AzAuthorizationStore->PersistCritSect );
|
|
bCritSectLocked = TRUE;
|
|
|
|
//
|
|
// If the children have already been loaded by another thread, then
|
|
// skip this
|
|
//
|
|
|
|
if ( GenericObject->AreChildrenLoaded ) {
|
|
|
|
//
|
|
// Grab the lock back
|
|
//
|
|
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Call the appropriate provider
|
|
//
|
|
|
|
WinStatus = AzAuthorizationStore->ProviderInfo->AzPersistUpdateChildrenCache(
|
|
AzAuthorizationStore->PersistContext,
|
|
(AZPE_OBJECT_HANDLE) GenericObject,
|
|
AZPE_FLAGS_PERSIST_UPDATE_CHILDREN_CACHE );
|
|
|
|
//
|
|
// Restore the lock, now that we are out of the provider
|
|
//
|
|
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
|
|
//
|
|
// Set the flag on the generic object that its children have been loaded
|
|
// and this object is no longer closed
|
|
//
|
|
|
|
GenericObject->AreChildrenLoaded = TRUE;
|
|
GenericObject->ObjectClosed = FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// Now that the cache has been updated from the store
|
|
// fix up any issues in the cache.
|
|
//
|
|
|
|
TempWinStatus = AzpPersistReconcile( GenericObject, IsUpdateChildCache, WinStatus != NO_ERROR );
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
WinStatus = TempWinStatus;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ( bCritSectLocked ) {
|
|
SafeLeaveCriticalSection( &AzAuthorizationStore->PersistCritSect );
|
|
}
|
|
|
|
return WinStatus;
|
|
}
|
|
|
|
VOID
|
|
AzPersistClose(
|
|
IN PAZP_AZSTORE AzAuthorizationStore
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine closes the authz policy database storage handles.
|
|
This routine routes the request to the correct provider.
|
|
|
|
On entry, AzGlResource must be locked exclusive.
|
|
|
|
Arguments:
|
|
|
|
AzAuthorizationStore - Specifies the policy database that is to be read.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// If the store wasn't successfully opened,
|
|
// we're done.
|
|
|
|
if ( AzAuthorizationStore->PersistContext == NULL ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Grab the persist engine crit sect maintaining locking order
|
|
//
|
|
// The provider uses PersistCritSect to serialize access to many of its structures.
|
|
//
|
|
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
AzpUnlockResource( &AzGlResource );
|
|
SafeEnterCriticalSection( &AzAuthorizationStore->PersistCritSect );
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
|
|
//
|
|
// Call the appropriate provider
|
|
//
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
|
|
AzAuthorizationStore->ProviderInfo->AzPersistClose( AzAuthorizationStore->PersistContext );
|
|
|
|
AzAuthorizationStore->PersistContext = NULL;
|
|
SafeLeaveCriticalSection( &AzAuthorizationStore->PersistCritSect );
|
|
}
|
|
|
|
DWORD
|
|
AzPersistSubmit(
|
|
IN PGENERIC_OBJECT GenericObject,
|
|
IN BOOLEAN DeleteMe
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine submits changes made to the authz policy database.
|
|
This routine routes the request to the correct provider.
|
|
|
|
If the object is being created, the GenericObject->PersistenceGuid field will be
|
|
zero on input. Upon successful creation, this routine will set PersistenceGuid to
|
|
non-zero. Upon failed creation, this routine will leave PersistenceGuid as zero.
|
|
|
|
On entry, AzGlResource must be locked exclusive.
|
|
This routine temporarily drops the AzGlResource while waiting for updates to complete.
|
|
|
|
Arguments:
|
|
|
|
GenericObject - Specifies the object in the database that is to be updated
|
|
in the underlying store.
|
|
|
|
DeleteMe - TRUE if the object and all of its children are to be deleted.
|
|
FALSE if the object is to be updated.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus = NO_ERROR;
|
|
|
|
PGENERIC_OBJECT_LIST GenericObjectList;
|
|
|
|
|
|
//
|
|
// Grab the persist engine crit sect maintaining locking order
|
|
//
|
|
// The PersistCritSect must be locked since we want to ensure that AzpPersistReconcile
|
|
// doesn't have to deal with a changing database.
|
|
//
|
|
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
AzpUnlockResource( &AzGlResource );
|
|
SafeEnterCriticalSection( &GenericObject->AzStoreObject->PersistCritSect );
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
|
|
//
|
|
// Only persist dirty objects
|
|
//
|
|
|
|
if ( GenericObject->DirtyBits != 0 ||
|
|
DeleteMe ) {
|
|
|
|
//
|
|
// Call the appropriate provider
|
|
//
|
|
|
|
WinStatus = GenericObject->AzStoreObject->ProviderInfo->AzPersistSubmit(
|
|
GenericObject->AzStoreObject->PersistContext,
|
|
(AZPE_OBJECT_HANDLE)GenericObject,
|
|
AZPE_FLAGS_PERSIST_SUBMIT,
|
|
DeleteMe );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
|
|
//
|
|
// Submit somehow fails, we need to remember that we are not in good
|
|
// shape with the store. So any access should refresh it.
|
|
//
|
|
|
|
GenericObject->Flags |= GENOBJ_FLAGS_REFRESH_ME;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Ensure the provider created a GUID
|
|
//
|
|
|
|
if ( (GenericObject->DirtyBits & AZ_DIRTY_CREATE) != 0 &&
|
|
GenericObject->ObjectType != OBJECT_TYPE_AZAUTHSTORE &&
|
|
!DeleteMe ) {
|
|
ASSERT( !IsEqualGUID( GenericObject->PersistenceGuid, AzGlZeroGuid ) );
|
|
}
|
|
|
|
//
|
|
// Ensure the provider didn't change the cache.
|
|
//
|
|
|
|
ASSERT( GenericObject->PersistDirtyBits == 0 );
|
|
|
|
|
|
//
|
|
// Clean up the list of deltas
|
|
//
|
|
// Walk all of the GenericObjectLists rooted on by this object
|
|
//
|
|
|
|
for ( GenericObjectList = GenericObject->GenericObjectLists;
|
|
GenericObjectList != NULL;
|
|
GenericObjectList = GenericObjectList->NextGenericObjectList ) {
|
|
|
|
|
|
//
|
|
// If there is a list of deltas,
|
|
// delete it
|
|
//
|
|
|
|
if ( GenericObjectList->DeltaArray.UsedCount != 0 ) {
|
|
ASSERT( !GenericObjectList->IsBackLink );
|
|
ASSERT( (GenericObject->DirtyBits & GenericObjectList->DirtyBit) != 0 );
|
|
ObFreeDeltaArray( &GenericObjectList->DeltaArray, TRUE );
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Turn off the dirty bits
|
|
//
|
|
|
|
GenericObject->DirtyBits = 0;
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
SafeLeaveCriticalSection( &GenericObject->AzStoreObject->PersistCritSect );
|
|
return WinStatus;
|
|
|
|
}
|
|
|
|
VOID
|
|
AzPersistAbort(
|
|
IN PGENERIC_OBJECT GenericObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine aborts changes made to the authz policy database.
|
|
This routine routes the request to the correct provider.
|
|
|
|
On entry, AzGlResource must be locked exclusive.
|
|
|
|
Arguments:
|
|
|
|
GenericObject - Specifies the object in the database that is to be updated
|
|
in the underlying store.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
PGENERIC_OBJECT_LIST GenericObjectList;
|
|
BOOLEAN WasCreated;
|
|
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
|
|
|
|
//
|
|
// Clean up the list of deltas
|
|
// ... The changes where aborted.
|
|
//
|
|
// Walk all of the GenericObjectLists rooted on by this object
|
|
//
|
|
|
|
for ( GenericObjectList = GenericObject->GenericObjectLists;
|
|
GenericObjectList != NULL;
|
|
GenericObjectList = GenericObjectList->NextGenericObjectList ) {
|
|
|
|
|
|
//
|
|
// If there is a list of deltas,
|
|
// delete it
|
|
//
|
|
|
|
if ( GenericObjectList->DeltaArray.UsedCount != 0 ) {
|
|
ASSERT( !GenericObjectList->IsBackLink );
|
|
ASSERT( (GenericObject->DirtyBits & GenericObjectList->DirtyBit) != 0 );
|
|
ObFreeDeltaArray( &GenericObjectList->DeltaArray, TRUE );
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Turn off the dirty bits
|
|
// ... The changes where aborted.
|
|
//
|
|
|
|
WasCreated = (( GenericObject->DirtyBits & AZ_DIRTY_CREATE ) != 0);
|
|
GenericObject->DirtyBits = 0;
|
|
|
|
//
|
|
// Update the cache to match the real object
|
|
//
|
|
// If we were trying to persist a creation of the object,
|
|
// delete the object from the cache.
|
|
//
|
|
|
|
if ( WasCreated ) {
|
|
|
|
//
|
|
// Mark the entry (and its child objects) as deleted
|
|
// We do this since other threads may have references to the objects.
|
|
// We want to ensure those threads know the objects are deleted.
|
|
//
|
|
|
|
ObMarkObjectDeleted( GenericObject );
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// Refresh the cache
|
|
// Ignore the status code
|
|
//
|
|
|
|
(VOID) AzPersistRefresh( GenericObject );
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
AzPersistRefresh(
|
|
IN PGENERIC_OBJECT GenericObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates the attributes of the object from the policy database.
|
|
|
|
This routine routes the request to the correct provider.
|
|
|
|
On entry, AzGlResource must be locked exclusive.
|
|
|
|
Arguments:
|
|
|
|
GenericObject - Specifies the object in the database whose cache entry is to be
|
|
updated
|
|
The GenericObject->PersistenceGuid field should be non-zero on input.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
DWORD TempWinStatus;
|
|
|
|
//
|
|
// Grab the persist engine crit sect maintaining locking order
|
|
//
|
|
// The PersistCritSect must be locked since we want to ensure that AzpPersistReconcile
|
|
// doesn't have to deal with a changing database.
|
|
//
|
|
|
|
ASSERT( AzpIsLockedExclusive( &AzGlResource ) );
|
|
AzpUnlockResource( &AzGlResource );
|
|
SafeEnterCriticalSection( &GenericObject->AzStoreObject->PersistCritSect );
|
|
|
|
//
|
|
// Mark the object that it needs to be refreshed
|
|
// so that it'll be refreshed later if we're not successful this time
|
|
//
|
|
|
|
GenericObject->Flags |= GENOBJ_FLAGS_REFRESH_ME;
|
|
|
|
//
|
|
// Clear any bits the provider left lying around
|
|
//
|
|
|
|
GenericObject->PersistDirtyBits = 0;
|
|
GenericObject->Flags &= !GENOBJ_FLAGS_PERSIST_OK;
|
|
|
|
|
|
//
|
|
// Call the appropriate provider
|
|
//
|
|
|
|
WinStatus = GenericObject->AzStoreObject->ProviderInfo->AzPersistRefresh(
|
|
GenericObject->AzStoreObject->PersistContext,
|
|
(AZPE_OBJECT_HANDLE)GenericObject,
|
|
AZPE_FLAGS_PERSIST_REFRESH );
|
|
|
|
// Provider should return WinStatus
|
|
// ASSERT( SUCCEEDED(WinStatus) );
|
|
|
|
//
|
|
// Restore the lock now that we're out of the provider
|
|
//
|
|
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
|
|
//
|
|
// If the provider couldn't find the object,
|
|
// that's OK. Reconcile will delete the cache entry.
|
|
//
|
|
|
|
if ( WinStatus == ERROR_NOT_FOUND ) {
|
|
WinStatus = NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// Reconcile this one object
|
|
//
|
|
|
|
TempWinStatus = AzpPersistReconcileOne(
|
|
GenericObject,
|
|
IsRefresh,
|
|
WinStatus != NO_ERROR ); // Operation status
|
|
|
|
if ( TempWinStatus != NO_ERROR ) {
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
WinStatus = TempWinStatus;
|
|
}
|
|
|
|
AzPrint(( AZD_CRITICAL, "AzpPersistReconcile: Cannot reconcile object: %ws %ld\n", GenericObject->ObjectName->ObjectName.String, TempWinStatus ));
|
|
// Continue processing
|
|
}
|
|
|
|
SafeLeaveCriticalSection( &GenericObject->AzStoreObject->PersistCritSect );
|
|
return WinStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
AzSubmit(
|
|
IN AZ_HANDLE AzHandle,
|
|
IN DWORD Flags,
|
|
IN DWORD Reserved
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Submit the changes made to the object via the *Create, *SetProperty, or *SetPropertyItem
|
|
APIs.
|
|
|
|
On failure, any changes made to the object are undone.
|
|
|
|
Arguments:
|
|
|
|
AzHandle - Passes in the handle to be updated.
|
|
|
|
Flags - Specifies flags that control the behavior of AzInitialize
|
|
AZ_SUBMIT_FLAG_ABORT: Abort the operation instead of commiting it
|
|
|
|
Reserved - Reserved. Must by zero.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful.
|
|
|
|
ERROR_INVALID_HANDLE - The passed in handle was invalid
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
|
|
PGENERIC_OBJECT ReferencedGenericObject = NULL;
|
|
PGENERIC_OBJECT GenericObject = (PGENERIC_OBJECT) AzHandle;
|
|
DWORD ObjectType;
|
|
|
|
//
|
|
// Grab the global lock
|
|
// Only for the authorization store case do we modify anything.
|
|
//
|
|
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
|
|
//
|
|
// Validate the input parameters
|
|
//
|
|
if ( Reserved != 0 ) {
|
|
AzPrint(( AZD_INVPARM, "AzCloseHandle: Reserved != 0\n" ));
|
|
WinStatus = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Determine the type of the object
|
|
//
|
|
|
|
WinStatus = ObGetHandleType( GenericObject,
|
|
FALSE, // Don't allow deleted objects
|
|
&ObjectType );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the passed in handle
|
|
//
|
|
|
|
WinStatus = ObReferenceObjectByHandle( GenericObject,
|
|
FALSE, // Don't allow deleted objects
|
|
TRUE, // Refresh the cache
|
|
ObjectType );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
ReferencedGenericObject = GenericObject;
|
|
|
|
//
|
|
// If we've been asked to abort the changes,
|
|
// do so
|
|
//
|
|
|
|
if ( Flags & AZ_SUBMIT_FLAG_ABORT ) {
|
|
|
|
//
|
|
// Call the worker routine to abort the operation
|
|
//
|
|
|
|
AzPersistAbort( GenericObject );
|
|
|
|
|
|
//
|
|
// Submit the changes
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Submit the change
|
|
// On failure, leave the object dirty
|
|
//
|
|
|
|
WinStatus = AzPersistSubmit( GenericObject, FALSE );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
WinStatus = NO_ERROR;
|
|
|
|
//
|
|
// Free locally used resources
|
|
//
|
|
Cleanup:
|
|
|
|
if ( ReferencedGenericObject != NULL ) {
|
|
ObDereferenceObject( ReferencedGenericObject );
|
|
}
|
|
|
|
//
|
|
// Drop the global lock
|
|
//
|
|
|
|
AzpUnlockResource( &AzGlResource );
|
|
|
|
return WinStatus;
|
|
|
|
}
|
|
|
|
//
|
|
// Various functions that are callable by the provider.
|
|
// These Azpe* routines are the only routines callable by the provider.
|
|
// They are typically thin wrappers around other routines in the core.
|
|
//
|
|
|
|
DWORD
|
|
AzpeCreateObject(
|
|
IN AZPE_OBJECT_HANDLE AzpeParentHandle,
|
|
IN ULONG ChildObjectType,
|
|
IN LPCWSTR ChildObjectNameString,
|
|
IN GUID *ChildObjectGuid,
|
|
IN ULONG lPersistFlags,
|
|
OUT AZPE_OBJECT_HANDLE *AzpeChildHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The provider should call AzpeCreateObject to create an object in the AzRoles object cache.
|
|
It should only be called from a thread processing a call to AzPersistOpen,
|
|
AzPersistUpdateCache, AzPersistUpdateChildrenCache or AzPersistRefresh.
|
|
|
|
Arguments:
|
|
|
|
AzpeParentHandle - Specifies a handle to the object that is the parent of the object
|
|
to create.
|
|
|
|
ChildObjectType - Specifies the type of the object to create. This should be one
|
|
of the OBJECT_TYPE_* defines. See the section entitled AzpeObjectType for a
|
|
list of valid object types. OBJECT_TYPE_AZAUTHSTORE is not valid.
|
|
|
|
ChildObjectNameString - Specifies the name of the object to create.
|
|
|
|
ChildObjectGuid - Specifies the Persistence Guid of the object to create.
|
|
|
|
lPersistFlags - Specifies a bit mask describing the operation. The provider should
|
|
pass the same flags that were passed to it by AzRoles when AzRoles called the AzPersist*.
|
|
|
|
AzpeChildHandle - On success, returns a handle to the newly created object.
|
|
The provider must call AzpeObjectFinished to close this handle.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
ERROR_INVALID_FLAGS - lPersistFlags is invalid.
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
PGENERIC_OBJECT ParentGenericObject = (PGENERIC_OBJECT)AzpeParentHandle;
|
|
PGENERIC_OBJECT_HEAD GenericChildHead;
|
|
|
|
AZP_STRING ChildObjectName;
|
|
|
|
//
|
|
// Ensure the provider didn't pass bogus parameters
|
|
//
|
|
|
|
if ( (lPersistFlags & ~AZPE_FLAGS_PERSIST_OPEN_MASK) != 0) {
|
|
ASSERT( FALSE );
|
|
return ERROR_INVALID_FLAGS;
|
|
}
|
|
|
|
if ( ChildObjectType >= OBJECT_TYPE_COUNT ) {
|
|
ASSERT( ChildObjectType < OBJECT_TYPE_COUNT );
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
AzpInitString( &ChildObjectName, ChildObjectNameString );
|
|
|
|
//
|
|
// Find child object head data structure from parent object
|
|
//
|
|
|
|
for ( GenericChildHead = ParentGenericObject->ChildGenericObjectHead;
|
|
GenericChildHead != NULL;
|
|
GenericChildHead = GenericChildHead->SiblingGenericObjectHead ) {
|
|
|
|
if ( GenericChildHead->ObjectType == ChildObjectType ) {
|
|
|
|
//
|
|
// Found object type head
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( GenericChildHead == NULL ) {
|
|
|
|
WinStatus = ERROR_INVALID_PARAMETER;
|
|
|
|
AzPrint(( AZD_INVPARM,
|
|
"AzpeCreateObject: Cannot find Object Head: %ld: %ld\n",
|
|
ParentGenericObject->ObjectType,
|
|
ChildObjectType ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Actually create the object
|
|
//
|
|
|
|
WinStatus = ObCreateObject(
|
|
ParentGenericObject,
|
|
GenericChildHead,
|
|
ChildObjectType,
|
|
&ChildObjectName,
|
|
ChildObjectGuid,
|
|
lPersistFlags | AZP_FLAGS_BY_GUID,
|
|
(PGENERIC_OBJECT *)AzpeChildHandle );
|
|
|
|
Cleanup:
|
|
AzpUnlockResource( &AzGlResource );
|
|
|
|
return WinStatus;
|
|
}
|
|
|
|
DWORD
|
|
AzpeSetProperty(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG PropertyId,
|
|
IN PVOID PropertyValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The provider should call AzpeSetProperty to set the value of a scalar property
|
|
in the AzRoles object cache. It should only be called from a thread processing
|
|
a call to AzPersistOpen, AzPersistUpdateCache, AzPersistUpdateChildrenCache or
|
|
AzPersistRefresh.
|
|
|
|
This operation is silently ignored if the application has already modified this
|
|
property and has not yet submitted the change.
|
|
|
|
The provider should not call AzpeSetProperty for values that it hasn't actually stored.
|
|
That is, the provider should make no assumption about the default values of the properties.
|
|
|
|
AzpeSetProperty does not bounds check, length check, or value check the PropertyValue.
|
|
The provider maintains the definitive copy of the authorization policy. AzRoles
|
|
simply maintains a cache of that authorization policy.
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object.
|
|
|
|
lPersistFlags - Specifies a bit mask describing the operation. The provider should
|
|
pass the same flags that were passed to it by AzRoles when AzRoles called the AzPersist*.
|
|
|
|
PropertyId - Specifies the property ID of the property to set. This should be one of
|
|
the AZ_PROP_* defines. See the section entitled PropertyId parameter for a list
|
|
of valid values.
|
|
|
|
PropertyValue - Specifies a pointer to the buffer containing the property.
|
|
For properties that are strings, the pointer is of type LPWSTR.
|
|
For properties that are LONGs, the pointer is of type PULONG.
|
|
For properties that are Booleans, the pointer is of type PBOOL.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_INVALID_FLAGS - lPersistFlags is invalid.
|
|
ERROR_INVALID_PARAMETER - Property ID is invalid
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
|
|
PGENERIC_OBJECT_LIST GenericObjectList;
|
|
ULONG ObjectType;
|
|
|
|
//
|
|
// Ensure the provider didn't pass bogus flags
|
|
//
|
|
|
|
if ( (lPersistFlags & ~AZPE_FLAGS_PERSIST_OPEN_MASK) != 0) {
|
|
ASSERT( FALSE );
|
|
return ERROR_INVALID_FLAGS;
|
|
}
|
|
|
|
//
|
|
// Ensure the provider is only getting a scalar property
|
|
//
|
|
|
|
WinStatus = ObMapPropIdToObjectList(
|
|
(PGENERIC_OBJECT)AzpeObjectHandle,
|
|
PropertyId,
|
|
&GenericObjectList,
|
|
&ObjectType );
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
AzPrint(( AZD_INVPARM, "AzpeSetProperty: Property ID for non-scalar: %ld\n", PropertyId ));
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Actually set the property
|
|
//
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
WinStatus = ObSetProperty(
|
|
(PGENERIC_OBJECT)AzpeObjectHandle,
|
|
lPersistFlags,
|
|
PropertyId,
|
|
PropertyValue);
|
|
|
|
AzpUnlockResource( &AzGlResource );
|
|
|
|
return WinStatus;
|
|
}
|
|
|
|
DWORD
|
|
AzpeSetObjectOptions(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG ObjectOptions
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The provider should call AzpeSetObjectOptions to tell AzRoles about optional
|
|
characteristics of the object. It should be called from a thread processing
|
|
a call to AzPersistOpen, AzPersistUpdateCache, or AzPersistUpdateChildrenCache,
|
|
AzPersistRefresh for each object read from the authorization database. It should
|
|
be called from a thread processing a call to AzPersistSubmit for each newly created object.
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object.
|
|
|
|
lPersistFlags - Specifies a bit mask describing the operation. The provider should
|
|
pass the same flags that were passed to it by AzRoles when AzRoles called the AzPersist*.
|
|
|
|
ObjectOptions - Specifies a bit mask containing one or more of the following bits:
|
|
|
|
AZPE_OPTIONS_WRITABLE (0x1) - If this bit is set, then the current user can write
|
|
this object. This corresponds to the Writable method on the various objects.
|
|
|
|
AZPE_OPTIONS_SUPPORTS_DACL (0x2) - If this bit is set, then the provider supports
|
|
setting AZ_PROP_POLICY_READERS and AZ_PROP_POLICY_ADMINS on this object.
|
|
This bit should only be specified if the object type is OBJECT_TYPE_AZAUTHSTORE,
|
|
OBJECT_TYPE_APPLICATION or OBJECT_TYPE_SCOPE.
|
|
|
|
AZPE_OPTIONS_SUPPORTS_DELEGATION (0x4) If this bit is set, then the provider
|
|
supports setting AZ_PROP_DELEGATED_POLICY_USERS on this object. This bit
|
|
should only be specified if the object type is OBJECT_TYPE_AZAUTHSTORE
|
|
or OBJECT_TYPE_APPLICATION.
|
|
|
|
AZPE_OPTIONS_SUPPORTS_SACL (0x8) - If this bit is set, then the provider
|
|
supports setting AZ_PROP_APPLY_STORE_SACL on this object. This bit should
|
|
only be specified if the object type is OBJECT_TYPE_AZAUTHSTORE,
|
|
OBJECT_TYPE_APPLICATION or OBJECT_TYPE_SCOPE.
|
|
|
|
AZPE_OPTIONS_HAS_SECURITY_PRIVILEGE (0x10) - If this bit is set, then the current
|
|
user has SE_SECURITY_PRIVILEGE on the machine containing the store.
|
|
|
|
AZPE_OPTIONS_SUUPORTS_LAZY_LOAD (0x20) - If this bit is set, then the provider supports
|
|
lazy load for children
|
|
|
|
AZPE_OPTIONS_CREATE_CHILDREN - If this bit is set, then the current user can create children
|
|
for this object. Currently, user only for AzScope objects.
|
|
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_INVALID_FLAGS - lPersistFlags is invalid.
|
|
|
|
--*/
|
|
{
|
|
PGENERIC_OBJECT GenericObject = (PGENERIC_OBJECT)AzpeObjectHandle;
|
|
|
|
//
|
|
// Ensure the provider didn't pass bogus flags
|
|
//
|
|
|
|
if ( (lPersistFlags & ~AZPE_FLAGS_PERSIST_MASK) != 0) {
|
|
ASSERT( FALSE );
|
|
return ERROR_INVALID_FLAGS;
|
|
}
|
|
|
|
if ( (ObjectOptions & ~AZPE_OPTIONS_VALID_MASK) != 0) {
|
|
AzPrint(( AZD_INVPARM, "AzpeSetObjectOptions: bad options mask 0x%lx\n", ObjectOptions ));
|
|
ASSERT( FALSE );
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
|
|
//
|
|
// Mark the object as writable
|
|
//
|
|
|
|
GenericObject->IsWritable = (( ObjectOptions & AZPE_OPTIONS_WRITABLE ) != 0);
|
|
|
|
//
|
|
// Mark whether the object support a DACL
|
|
//
|
|
|
|
if ( IsContainerObject( GenericObject->ObjectType )) {
|
|
GenericObject->IsAclSupported = (( ObjectOptions & AZPE_OPTIONS_SUPPORTS_DACL ) != 0);
|
|
} else {
|
|
GenericObject->IsAclSupported = FALSE;
|
|
}
|
|
|
|
//
|
|
// Mark whether the object support a SACL
|
|
//
|
|
|
|
if ( IsContainerObject( GenericObject->ObjectType )) {
|
|
GenericObject->IsSACLSupported = (( ObjectOptions & AZPE_OPTIONS_SUPPORTS_SACL ) != 0);
|
|
} else {
|
|
GenericObject->IsSACLSupported = FALSE;
|
|
}
|
|
|
|
//
|
|
// Load delegation support flag
|
|
//
|
|
|
|
if ( IsDelegatorObject( GenericObject->ObjectType ) ) {
|
|
GenericObject->IsDelegationSupported = (( ObjectOptions & AZPE_OPTIONS_SUPPORTS_DELEGATION ) != 0);
|
|
} else {
|
|
GenericObject->IsDelegationSupported = FALSE;
|
|
}
|
|
|
|
//
|
|
// Mark whether the current user has SE_SECURITY_PRIVILEGE on the store server
|
|
//
|
|
|
|
if ( GenericObject->ObjectType == OBJECT_TYPE_AZAUTHSTORE ) {
|
|
((PAZP_AZSTORE)GenericObject)->HasSecurityPrivilege = (( ObjectOptions & AZPE_OPTIONS_HAS_SECURITY_PRIVILEGE ) != 0);
|
|
|
|
//
|
|
// If the provider supports lazy load for children
|
|
//
|
|
|
|
((PAZP_AZSTORE)GenericObject)->ChildLazyLoadSupported = ((ObjectOptions & AZPE_OPTIONS_SUPPORTS_LAZY_LOAD) != 0);
|
|
}
|
|
|
|
//
|
|
// Mark if the AzScope object children can be created
|
|
//
|
|
|
|
if ( IsContainerObject(GenericObject->ObjectType) ) {
|
|
GenericObject->CanCreateChildren = ((ObjectOptions & AZPE_OPTIONS_CREATE_CHILDREN) != 0);
|
|
}
|
|
|
|
AzpUnlockResource( &AzGlResource );
|
|
return NO_ERROR;
|
|
}
|
|
|
|
VOID
|
|
AzpeObjectFinished(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN DWORD WinStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The provider should call AzpeObjectFinished to indicate that it is finished
|
|
updating the cache for a particular object. It should only be called from a
|
|
thread processing a call to AzPersistOpen, AzPersistUpdateCache, AzPersistUpdateChildrenCache,
|
|
or AzPersistRefresh.
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object.
|
|
|
|
WinStatus - Specifies whether the provider successfully set all attributes
|
|
of the object. If all attributes were set, specify NO_ERROR. If not all attributes
|
|
were set, specify an appropriate status code.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PGENERIC_OBJECT GenericObject = (PGENERIC_OBJECT)AzpeObjectHandle;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
|
|
// Should no longer ASSERT on this condition.
|
|
//ASSERT( WinStatus == NO_ERROR );
|
|
|
|
ASSERT( (GenericObject->Flags & GENOBJ_FLAGS_PERSIST_OK) == 0 );
|
|
|
|
//
|
|
// Indicate that the provider has blessed the object
|
|
//
|
|
if ( WinStatus == NO_ERROR ) {
|
|
GenericObject->Flags |= GENOBJ_FLAGS_PERSIST_OK;
|
|
|
|
// Providers don't call AzpeCreateObject for the authorization store object.
|
|
// So ensure the dirty bit gets set.
|
|
//ASSERT( (GenericObject->PersistDirtyBits & AZ_DIRTY_CREATE) != 0 || GenericObject->ObjectType == OBJECT_TYPE_AZAUTHSTORE );
|
|
GenericObject->PersistDirtyBits |= AZ_DIRTY_CREATE;
|
|
}
|
|
|
|
//
|
|
// If this isn't the authorization store object,
|
|
// the provider got this handle from AzpeCreateObject,
|
|
// so dereference it.
|
|
//
|
|
if ( GenericObject->ObjectType != OBJECT_TYPE_AZAUTHSTORE ) {
|
|
ObDereferenceObject( GenericObject );
|
|
}
|
|
|
|
AzpUnlockResource( &AzGlResource );
|
|
}
|
|
|
|
DWORD
|
|
AzpeGetProperty(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG PropertyId,
|
|
OUT PVOID *PropertyValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The provider should call AzpeGetProperty to determine the value of scalar properties
|
|
for a particular object. It should only be called from a thread processing a call to
|
|
AzPersistSubmit.
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object.
|
|
|
|
lPersistFlags - Specifies a bit mask describing the operation. The provider should
|
|
pass the same flags that were passed to it by AzRoles when AzRoles called the AzPersist*.
|
|
|
|
PropertyId - Specifies the property ID of the property to get. This should be one
|
|
of the AZ_PROP_* defines. See the section entitled PropertyId parameter for a
|
|
list of valid values.
|
|
|
|
PropertyValue - On success, returns a pointer to the buffer containing the property.
|
|
The provider should free this buffer using AzpeFreeMemory.
|
|
For properties that are strings, the returned pointer is of type LPWSTR.
|
|
For properties that are LONGs, the returned pointer is of type PULONG.
|
|
For properties that are Booleans, the returned pointer is of type PBOOL.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
ERROR_INVALID_FLAGS - lPersistFlags is invalid.
|
|
ERROR_INVALID_PARAMETER - PropertyId is invalid.
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
PGENERIC_OBJECT_LIST GenericObjectList;
|
|
ULONG ObjectType;
|
|
|
|
//
|
|
// Ensure the provider didn't pass bogus flags
|
|
//
|
|
|
|
if ( (lPersistFlags & ~AZPE_FLAGS_PERSIST_SUBMIT) != 0) {
|
|
ASSERT( FALSE );
|
|
return ERROR_INVALID_FLAGS;
|
|
}
|
|
|
|
//
|
|
// Ensure the provider is only getting a scalar property
|
|
//
|
|
|
|
WinStatus = ObMapPropIdToObjectList(
|
|
(PGENERIC_OBJECT)AzpeObjectHandle,
|
|
PropertyId,
|
|
&GenericObjectList,
|
|
&ObjectType );
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
AzPrint(( AZD_INVPARM, "AzpeGetProperty: Property ID for non-scalar: %ld\n", PropertyId ));
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Go get the property value
|
|
//
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
|
|
WinStatus = ObGetProperty(
|
|
(PGENERIC_OBJECT)AzpeObjectHandle,
|
|
lPersistFlags |
|
|
AZP_FLAGS_BY_GUID, // use guid from persist store if apply
|
|
PropertyId,
|
|
PropertyValue);
|
|
|
|
AzpUnlockResource( &AzGlResource );
|
|
|
|
return WinStatus;
|
|
}
|
|
|
|
DWORD
|
|
AzpeGetDeltaArray(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG PropertyId,
|
|
OUT PULONG DeltaArrayCount,
|
|
OUT PAZP_DELTA_ENTRY **DeltaArray
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The provider should call AzpeObjectFinished to determine the value of non-scalar
|
|
properties for a particular object. It should only be called from a thread
|
|
processing a call to AzPersistSubmit.
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object.
|
|
|
|
lPersistFlags - Specifies a bit mask describing the operation. The provider should
|
|
pass the same flags that were passed to it by AzRoles when AzRoles called the
|
|
AzPersist*.
|
|
|
|
PropertyId - Specifies the property ID of the property to get. This should be one
|
|
of the AZ_PROP_* defines. See the section entitled PropertyId parameter for
|
|
a list of valid values.
|
|
|
|
DeltaArrayCount - On success, returns the number of elements in DeltaArray.
|
|
The returned value may be zero if no changes have been made to the property.
|
|
|
|
DeltaArray - On success, returns a pointers to the delta array. This pointer need
|
|
not be freed. The pointer is only valid until the persistence provider returns
|
|
from the AzPersistSubmit call. The returned value may be NULL if no changes
|
|
have been made to the property.
|
|
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_INVALID_PARAMETER - PropertyId is invalid.
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
|
|
PGENERIC_OBJECT_LIST GenericObjectList;
|
|
ULONG ObjectType;
|
|
|
|
//
|
|
// Map to the generic object list
|
|
//
|
|
|
|
WinStatus = ObMapPropIdToObjectList(
|
|
(PGENERIC_OBJECT)AzpeObjectHandle,
|
|
PropertyId,
|
|
&GenericObjectList,
|
|
&ObjectType );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
AzPrint(( AZD_INVPARM, "AzpeGetDeltaArray: invalid prop id %ld\n", PropertyId ));
|
|
return WinStatus;
|
|
}
|
|
|
|
//
|
|
// Return the delta array from the object list
|
|
//
|
|
|
|
*DeltaArrayCount = GenericObjectList->DeltaArray.UsedCount;
|
|
*DeltaArray = (PAZP_DELTA_ENTRY *)GenericObjectList->DeltaArray.Array;
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
DWORD
|
|
AzpeObjectType(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine returns the object type for a particular object
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the objec
|
|
|
|
Return Value:
|
|
Returns the object type of the object. This will be one of the OBJECT_TYPE_* defines.
|
|
|
|
--*/
|
|
{
|
|
return ((PGENERIC_OBJECT)AzpeObjectHandle)->ObjectType;
|
|
}
|
|
|
|
DWORD
|
|
AzpeDirtyBits(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a bit mask of the dirty bits for a particular object.
|
|
Each bit corresponds to a property that has been modified by the application.
|
|
It should only be called from a thread processing a call to AzPersistSubmit.
|
|
|
|
The dirty bits are object type specific. The provider should determine
|
|
the object type of the object then should map each dirty bit to the corresponding
|
|
property ID. If the property ID corresponds to a scalar, the provider should call
|
|
AzpeGetProperty to get the value of the property. If the property ID corresponds
|
|
to a list, the provider should call AzpeGetDeltaArray to get a description of what
|
|
values where added to and removed from the property. See the section entitled
|
|
PropertyId Parameter for details.
|
|
|
|
Each PropertyId maps to a single AZ_DIRTY_* dirty bit as returned from AzpeDirtyBits.
|
|
The #define names exist on a one-to-one basis. That is AZ_PROP_NAME is the property
|
|
ID for AZ_DIRTY_NAME.
|
|
|
|
There is one special dirty bit, AZ_DIRTY_CREATE. That bit is set if the object
|
|
was created by the application.
|
|
|
|
If AzpeDirtyBits returns a bit the provider doesn't understand, the provider
|
|
should fail the AzPersistSubmit call.
|
|
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object.
|
|
|
|
|
|
Return Value:
|
|
|
|
Returns a bit mask specifying which properties where changed on the object.
|
|
These bits are the AZ_DIRTY_* defines.
|
|
|
|
--*/
|
|
{
|
|
return ((PGENERIC_OBJECT)AzpeObjectHandle)->DirtyBits;
|
|
}
|
|
|
|
GUID *
|
|
AzpePersistenceGuid(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a pointer to the location of the persistence GUID in the
|
|
AzRoles cache. The provider may read the returned location as long as the
|
|
AzpeObjectHandle is valid. The provider may only modify the returned location
|
|
while processing an AzPersistSubmit call and then only if the AZ_DIRTY_CREATE
|
|
bit was set.
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to the location of the persistence GUID in the AzRoles cache.
|
|
|
|
--*/
|
|
{
|
|
return &((PGENERIC_OBJECT)AzpeObjectHandle)->PersistenceGuid;
|
|
}
|
|
|
|
BOOLEAN
|
|
AzpeIsParentWritable(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This routine returns the IsWritable property of the object's parent
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN value indicating the IsWritable property of the parent
|
|
|
|
--*/
|
|
{
|
|
return (BOOLEAN)(ParentOfChild((PGENERIC_OBJECT)AzpeObjectHandle)->IsWritable);
|
|
}
|
|
|
|
BOOLEAN
|
|
AzpeCanCreateChildren(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This routine returns the CanCreateChildren property of the object's parent
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN value indicating the IsWritable property of the parent
|
|
|
|
--*/
|
|
{
|
|
return (BOOLEAN)(ParentOfChild((PGENERIC_OBJECT)AzpeObjectHandle)->CanCreateChildren);
|
|
}
|
|
|
|
BOOLEAN
|
|
AzpeUpdateChildren(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns TRUE if the children of the object need to be updated into cache
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN value indicating if the children of the object need to be updated into the cache
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN retVal = FALSE;
|
|
|
|
if ( ((PGENERIC_OBJECT)AzpeObjectHandle)->ObjectType == OBJECT_TYPE_SCOPE ||
|
|
((PGENERIC_OBJECT)AzpeObjectHandle)->ObjectType == OBJECT_TYPE_APPLICATION ) {
|
|
|
|
retVal = ((PGENERIC_OBJECT)AzpeObjectHandle)->AreChildrenLoaded;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
AZPE_OBJECT_HANDLE
|
|
AzpeParentOfChild(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a handle to the parent of a particular object.
|
|
The provider may use this parent handle as long as AzpeObjectHandle is valid.
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object.
|
|
|
|
Return Value:
|
|
|
|
Returns a handle to the parent of a particular object.
|
|
If AzpeObjectHandle is for an authorization store object, NULL is returned.
|
|
|
|
--*/
|
|
{
|
|
|
|
return (AZPE_OBJECT_HANDLE)ParentOfChild((PGENERIC_OBJECT)AzpeObjectHandle);
|
|
}
|
|
|
|
DWORD
|
|
AzpeAddPropertyItemSid(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG PropertyId,
|
|
IN PSID Sid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The provider should call AzpeAddPropertyItemSid to set the value of a SID list
|
|
property in the AzRoles object cache. It should be called once from each SID
|
|
in the SID list. It should only be called from a thread processing a call
|
|
to AzPersistOpen, AzPersistUpdateCache, AzPersistUpdateChildrenCache or AzPersistRefresh.
|
|
|
|
This routine may also be called from a thread processing AzPersistSubmit if the
|
|
submitted AZ_PROP_POLICY_ADMINS list is empty. In that case, the provider should default
|
|
the AZ_PROP_POLICY_ADMINS list to the owner of the submitted file and should tell AzRoles
|
|
who that own is by either calling AzpeSetSecurityDescriptorIntoCache or AzpeAddPropertyItemSid.
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object.
|
|
|
|
lPersistFlags - Specifies a bit mask describing the operation. The provider should
|
|
pass the same flags that were passed to it by AzRoles when AzRoles called the AzPersist*.
|
|
|
|
PropertyId - Specifies the property ID of the property to set. This should be one
|
|
of the AZ_PROP_* defines. See the section entitled PropertyId parameter for a
|
|
list of valid values.
|
|
|
|
Sid - Specifies a pointer to the SID to add to the Sid list
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_INVALID_FLAGS - lPersistFlags is invalid.
|
|
ERROR_INVALID_PARAMETER - Property ID is invalid or the Sid is syntactically invalid.
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
|
|
PGENERIC_OBJECT GenericObject = (PGENERIC_OBJECT)AzpeObjectHandle;
|
|
PGENERIC_OBJECT_LIST GenericObjectList;
|
|
ULONG ObjectType;
|
|
AZP_STRING SidString;
|
|
|
|
//
|
|
// Ensure the provider didn't pass bogus flags
|
|
//
|
|
|
|
if ( (lPersistFlags & ~AZPE_FLAGS_PERSIST_MASK) != 0) {
|
|
ASSERT( FALSE );
|
|
return ERROR_INVALID_FLAGS;
|
|
}
|
|
|
|
//
|
|
// Handle calls from submit.
|
|
//
|
|
|
|
if ( lPersistFlags & AZPE_FLAGS_PERSIST_SUBMIT ) {
|
|
|
|
//
|
|
// Submit is only allowed for Policy admins
|
|
// .. on the authorization store object
|
|
// .. if there is no an empty policy admins list
|
|
//
|
|
if ( PropertyId != AZ_PROP_POLICY_ADMINS ||
|
|
GenericObject->ObjectType != OBJECT_TYPE_AZAUTHSTORE &&
|
|
GenericObject->PolicyAdmins.GenericObjects.UsedCount == 0 ) {
|
|
|
|
AzPrint(( AZD_INVPARM, "AzpeAddPropertyItemSid: called from submit: %ld %ld %ld\n",
|
|
PropertyId,
|
|
GenericObject->ObjectType,
|
|
GenericObject->PolicyAdmins.GenericObjects.UsedCount ));
|
|
|
|
ASSERT( FALSE );
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Treat this as though it came from reconcile.
|
|
// That ensure the cache really gets updated but the dirty bits don't
|
|
//
|
|
|
|
lPersistFlags |= AZP_FLAGS_RECONCILE;
|
|
lPersistFlags &= ~AZPE_FLAGS_PERSIST_SUBMIT;
|
|
|
|
}
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
|
|
|
|
//
|
|
// Validate the Property ID
|
|
//
|
|
|
|
WinStatus = ObMapPropIdToObjectList(
|
|
GenericObject,
|
|
PropertyId,
|
|
&GenericObjectList,
|
|
&ObjectType );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
AzPrint(( AZD_INVPARM, "AzpeAddPropertyItemSid: invalid prop id %ld\n", PropertyId ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Validate a passed in SID
|
|
//
|
|
|
|
if ( !RtlValidSid( Sid ) ) {
|
|
AzPrint(( AZD_INVPARM, "AzpeAddPropertyItemSid: SID not valid\n" ));
|
|
WinStatus = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Add the property item
|
|
//
|
|
|
|
AzpInitSid( &SidString, Sid );
|
|
WinStatus = ObAddPropertyItem(
|
|
GenericObject,
|
|
GenericObjectList,
|
|
lPersistFlags,
|
|
&SidString );
|
|
|
|
Cleanup:
|
|
AzpUnlockResource( &AzGlResource );
|
|
|
|
return WinStatus;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AzpeAddPropertyItemGuid(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG PropertyId,
|
|
IN GUID *ObjectGuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The provider should call AzpeAddPropertyItemGuid to set the value of a property
|
|
that is a list of other object in the AzRoles object cache. It should be called
|
|
once from each object in the list. It should only be called from a thread
|
|
processing a call to AzPersistOpen, AzPersistUpdateCache, AzPersistUpdateChildrenCache
|
|
or AzPersistRefresh.
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object.
|
|
|
|
lPersistFlags - Specifies a bit mask describing the operation. The provider should
|
|
pass the same flags that were passed to it by AzRoles when AzRoles called the AzPersist*.
|
|
|
|
PropertyId - Specifies the property ID of the property to set. This should be one of
|
|
the AZ_PROP_* defines. See the section entitled PropertyId parameter for a list of
|
|
valid values.
|
|
|
|
ObjectGuid - Specifies the persistence GUID of the object in the list.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_INVALID_FLAGS - lPersistFlags is invalid.
|
|
ERROR_INVALID_PARAMETER - Property ID is invalid
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
|
|
PGENERIC_OBJECT_LIST GenericObjectList;
|
|
ULONG ObjectType;
|
|
|
|
//
|
|
// Ensure the provider didn't pass bogus flags
|
|
//
|
|
|
|
if ( (lPersistFlags & ~AZPE_FLAGS_PERSIST_OPEN_MASK) != 0) {
|
|
ASSERT( FALSE );
|
|
return ERROR_INVALID_FLAGS;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the Property ID
|
|
//
|
|
|
|
WinStatus = ObMapPropIdToObjectList(
|
|
(PGENERIC_OBJECT)AzpeObjectHandle,
|
|
PropertyId,
|
|
&GenericObjectList,
|
|
&ObjectType );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
AzPrint(( AZD_INVPARM, "AzpeAddPropertyItemGuid: invalid prop id %ld\n", PropertyId ));
|
|
return WinStatus;
|
|
}
|
|
|
|
AzpLockResourceExclusive( &AzGlResource );
|
|
|
|
WinStatus = ObAddPropertyItem(
|
|
(PGENERIC_OBJECT)AzpeObjectHandle,
|
|
GenericObjectList,
|
|
lPersistFlags |
|
|
AZP_FLAGS_BY_GUID,
|
|
(PAZP_STRING)ObjectGuid);
|
|
|
|
AzpUnlockResource( &AzGlResource );
|
|
|
|
return WinStatus;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AzpeAddPropertyItemGuidString(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG PropertyId,
|
|
IN WCHAR *ObjectGuidString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
AzpeAddPropertyItemGuidString is identical to AzpeAddPropertyItemGuid except it
|
|
takes the GUID as an LPWSTR. The provider may call whichever function is more convenient.
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object.
|
|
|
|
lPersistFlags - Specifies a bit mask describing the operation. The provider should
|
|
pass the same flags that were passed to it by AzRoles when AzRoles called the AzPersist*.
|
|
|
|
PropertyId - Specifies the property ID of the property to set. This should be one
|
|
of the AZ_PROP_* defines. See the section entitled PropertyId parameter for a list
|
|
of valid values.
|
|
|
|
ObjectGuidString - Specifies the persistence GUID of the object in the list.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_INVALID_FLAGS - lPersistFlags is invalid.
|
|
ERROR_INVALID_PARAMETER - Property ID is invalid
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr;
|
|
GUID ObjectGuid;
|
|
|
|
//
|
|
// Convert the string to a GUID
|
|
//
|
|
ASSERT(NULL != ObjectGuidString);
|
|
|
|
dwErr = UuidFromString(ObjectGuidString, &ObjectGuid);
|
|
if (S_OK != dwErr)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Use the routine that takes a binary GUID
|
|
//
|
|
|
|
dwErr = AzpeAddPropertyItemGuid( AzpeObjectHandle,
|
|
lPersistFlags,
|
|
PropertyId,
|
|
&ObjectGuid );
|
|
|
|
Cleanup:
|
|
return dwErr;
|
|
}
|
|
|
|
PVOID
|
|
AzpeGetProviderData(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
AzpeGetProviderData returns a pointer to buffer previously stored by AzpeSetProviderData.
|
|
The provider is responsible for providing any synchronization for the data.
|
|
The provider should only access the data from a thread processing a call to one of
|
|
the AzPersist* routines.
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the data the provider previously passed into AzpeSetProviderData
|
|
|
|
--*/
|
|
{
|
|
return ((PGENERIC_OBJECT)AzpeObjectHandle)->ProviderData;
|
|
}
|
|
|
|
VOID
|
|
AzpeSetProviderData(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN PVOID ProviderData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
It may be convenient for the provider to store some provider specific data on
|
|
each object. AzpeSetProviderData stores a pointer to that data in the AzRoles cache.
|
|
Azoles will automatically delete the pointed to data when it deletes the cache entry.
|
|
|
|
The provider may clear a previously stored pointer by specifying NULL as
|
|
ProviderData.
|
|
|
|
The provider is responsible for providing any synchronization for the data.
|
|
The provider should only access the data from a thread processing a call to one
|
|
of the AzPersist* routines.
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Specifies a handle to the object.
|
|
|
|
ProviderData - Specifies a pointer to data to be stored by the provider.
|
|
The buffer should have been allocated using AzpeAllocateMemory. AzRoles
|
|
will delete this data when the cache entry is deleted by using AzpeFreeMemory.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
((PGENERIC_OBJECT)AzpeObjectHandle)->ProviderData = ProviderData;
|
|
}
|
|
|
|
VOID
|
|
AzpeFreeMemory(
|
|
IN PVOID Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
The provider must use AzpeFreeMemory to free memory allocated by AzpeAllocateMemory,
|
|
AzpeGetProperty, and AzpeGetSecurityDescriptorFromCache.
|
|
|
|
Arguments
|
|
|
|
Buffer - Specifies a pointer to the buffer to be free.
|
|
If NULL, the call is silently ignored.
|
|
|
|
|
|
Return Value
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if ( Buffer != NULL ) {
|
|
AzpFreeHeap( Buffer );
|
|
}
|
|
}
|
|
|
|
|
|
PVOID
|
|
AzpeAllocateMemory(
|
|
IN SIZE_T Size
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The provider may call AzpeAllocateMemory to allocate memory. The provider
|
|
must use AzpeAllocateMemory to allocate memory passed to AzpeSetProviderData
|
|
and returned in the pwszTargetMachine parameter to AzPersistOpen. The provider
|
|
may use AzpeAllocateMemory for other memory allocation. The provider must
|
|
not have any memory allocated after it returns from AzPersistClose.
|
|
|
|
Arguments:
|
|
|
|
Size - Size in bytes of the memory to allocate.
|
|
|
|
Return Values:
|
|
|
|
Returns a pointer to the allocated memory.
|
|
NULL - Not enough memory
|
|
|
|
--*/
|
|
{
|
|
|
|
return AzpAllocateHeap( Size, "AZPEALOC" );
|
|
}
|
|
|
|
|
|
DWORD
|
|
AzpSdToPolicy(
|
|
IN PSECURITY_DESCRIPTOR pSD,
|
|
IN PAZP_POLICY_USER_RIGHTS pAdminRights OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS pReaderRights OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS pDelegatedUserRights OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS pSaclRights OPTIONAL,
|
|
IN DWORD (*CallbackRoutine) (
|
|
IN PVOID Context,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG PropertyId,
|
|
IN PSID Sid ),
|
|
IN PVOID Context,
|
|
IN ULONG lPersistFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
AzpSdToPolicy parses a security descriptor and determines which ACEs correspond
|
|
to AZ_PROP_POLICY_ADMINS, AZ_PROP_POLICY_READERS, and AZ_PROP_DELEGATED_POLICY_USERS properties.
|
|
For each such Sid, the routine calls a callback routine to tell the caller about the sid.
|
|
|
|
It also determines whether AZ_PORP_APPLY_STORE_SACL is TRUE or FALSE by inspecting the SACL.
|
|
After determining the value, the routine calls a callback routine to tell the caller the value.
|
|
|
|
Arguments:
|
|
|
|
pSD - The current security descriptor for the object on the object in the store
|
|
|
|
pAdminRights - Rights for admins (Mask and Flags)
|
|
|
|
pReaderRights - Rights for readers (Mask and Flags). Specify NULL if the readers list
|
|
need not be added. (For instance, for AzPersistSubmit as described above.)
|
|
|
|
pDelegatedUserRights - An optional parameter specifying the delegated user's rights (Mask and Flags)
|
|
|
|
pSaclRights - Specifies the rights to put on the SACL
|
|
|
|
CallbackRoutine - Address of the routine to call for each policy sid.
|
|
|
|
Context - Context to pass to the callback routine.
|
|
|
|
lPersistFlags - Internal flags
|
|
These flags are passed to the callback routine but are otherwise unused.
|
|
|
|
Return Values:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other values returned from CallbackRoutine
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD WinStatus = 0;
|
|
|
|
PACL pDacl;
|
|
BOOL bDaclPresent;
|
|
BOOL bDaclDefaulted;
|
|
|
|
PACL pSacl;
|
|
BOOL bSaclPresent;
|
|
BOOL bSaclDefaulted;
|
|
|
|
ACL_SIZE_INFORMATION AclInfo;
|
|
|
|
PACE_HEADER pAceHeader;
|
|
PACCESS_ALLOWED_ACE pAce;
|
|
PSID pSid;
|
|
|
|
ULONG lUserType;
|
|
ULONG i;
|
|
|
|
BOOL bPolicyAdmin;
|
|
BOOL bPolicyReader;
|
|
BOOL bDelegatedPolicyUser;
|
|
|
|
BOOL ApplySacl;
|
|
|
|
//
|
|
// Validation
|
|
//
|
|
|
|
ASSERT( pSD != NULL );
|
|
|
|
//
|
|
// If the caller is interested, do the DACL
|
|
//
|
|
|
|
if ( pAdminRights || pReaderRights || pDelegatedUserRights) {
|
|
|
|
//
|
|
// Retrive the DACL information. From this, the SIDs will
|
|
// extracted for each ACE, and loaded into the policy readers/
|
|
// admins and delegators list property accordingly.
|
|
// All inherited ACEs will be discarded
|
|
//
|
|
|
|
if ( !GetSecurityDescriptorDacl(pSD,
|
|
&bDaclPresent,
|
|
&pDacl,
|
|
&bDaclDefaulted
|
|
) ) {
|
|
WinStatus = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
if( !bDaclPresent ||
|
|
(pDacl == NULL)
|
|
) {
|
|
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// We now need the ACL information from the DACL
|
|
//
|
|
|
|
if ( !GetAclInformation(
|
|
pDacl,
|
|
&AclInfo,
|
|
sizeof( ACL_SIZE_INFORMATION ),
|
|
AclSizeInformation
|
|
) ) {
|
|
|
|
WinStatus = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Loop through all the SIDs in the ACEs adding each one to the appropriate
|
|
// user type. Ignore all inherited ACEs
|
|
//
|
|
|
|
for ( i = 0; i < AclInfo.AceCount; i++ ) {
|
|
|
|
if ( GetAce( pDacl, i, (PVOID *)&pAce ) ) {
|
|
|
|
ASSERT( pAce != NULL );
|
|
|
|
pAceHeader = (PACE_HEADER) pAce;
|
|
|
|
//
|
|
// Allowed ACEs should only be added
|
|
//
|
|
|
|
if ( pAceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE ||
|
|
pAceHeader->AceType == ACCESS_ALLOWED_OBJECT_ACE_TYPE ) {
|
|
|
|
//
|
|
// Pick up the AzRoles policy aces. Ignore inherited ACEs
|
|
//
|
|
|
|
if ( !(pAceHeader->AceFlags & INHERITED_ACE) ) {
|
|
|
|
bDelegatedPolicyUser = pDelegatedUserRights != NULL &&
|
|
!(pAce->Mask ^ pDelegatedUserRights->lUserRightsMask) &&
|
|
(pAceHeader->AceFlags == pDelegatedUserRights->lUserRightsFlags);
|
|
|
|
bPolicyAdmin = pAdminRights != NULL &&
|
|
!(pAce->Mask ^ pAdminRights->lUserRightsMask) &&
|
|
(pAceHeader->AceFlags & INHERIT_ONLY_ACE) == 0;
|
|
|
|
bPolicyReader = pReaderRights != NULL &&
|
|
!(pAce->Mask ^ pReaderRights->lUserRightsMask) &&
|
|
(pAceHeader->AceFlags & pReaderRights->lUserRightsFlags) == pReaderRights->
|
|
lUserRightsFlags &&
|
|
(pAceHeader->AceFlags & INHERIT_ONLY_ACE) == 0;
|
|
|
|
if ( bPolicyAdmin ||
|
|
bPolicyReader ||
|
|
bDelegatedPolicyUser
|
|
) {
|
|
|
|
pSid = (PSID)&pAce->SidStart;
|
|
|
|
if ( bDelegatedPolicyUser ) {
|
|
|
|
lUserType = AZ_PROP_DELEGATED_POLICY_USERS;
|
|
|
|
pSid = RtlObjectAceSid( pAce );
|
|
|
|
} else if ( bPolicyAdmin ) {
|
|
|
|
lUserType = AZ_PROP_POLICY_ADMINS;
|
|
|
|
} else {
|
|
|
|
lUserType = AZ_PROP_POLICY_READERS;
|
|
|
|
}
|
|
|
|
if ( IsValidSid( pSid ) ) {
|
|
|
|
WinStatus = (*CallbackRoutine)(
|
|
Context,
|
|
lPersistFlags,
|
|
lUserType,
|
|
pSid );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
} // if ( IsValidSid( pSid )
|
|
|
|
} // if AzRoles policy ace
|
|
|
|
} // if not inherited
|
|
|
|
} // if ACCESS_ALLOWED_ACE_TYPE
|
|
|
|
} // if GetAce
|
|
|
|
} // for ( i=0; i<AceCount; i++ )
|
|
}
|
|
|
|
//
|
|
// If the caller is interested, do the SACL
|
|
//
|
|
|
|
if ( pSaclRights ) {
|
|
|
|
//
|
|
// Retrive the SACL information.
|
|
//
|
|
// Use it to determine AZ_PORP_APPLY_STORE_SACL
|
|
//
|
|
|
|
if ( !GetSecurityDescriptorSacl(pSD,
|
|
&bSaclPresent,
|
|
&pSacl,
|
|
&bSaclDefaulted
|
|
) ) {
|
|
WinStatus = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// If there is a SACL,
|
|
// determine if we have put explicit ACEs in the SACL.
|
|
// If so, set ApplySacl TRUE
|
|
//
|
|
|
|
ApplySacl = FALSE;
|
|
|
|
if( bSaclPresent && pSacl != NULL ) {
|
|
|
|
//
|
|
// We now need the ACL information from the SACL
|
|
//
|
|
|
|
if ( !GetAclInformation(
|
|
pSacl,
|
|
&AclInfo,
|
|
sizeof( ACL_SIZE_INFORMATION ),
|
|
AclSizeInformation
|
|
) ) {
|
|
|
|
WinStatus = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Loop through all ACEs
|
|
//
|
|
|
|
for ( i = 0; i < AclInfo.AceCount; i++ ) {
|
|
|
|
pAce = NULL;
|
|
|
|
if ( GetAce( pSacl, i, (PVOID *)&pAce ) ) {
|
|
|
|
ASSERT( pAce != NULL );
|
|
|
|
pAceHeader = (PACE_HEADER) pAce;
|
|
|
|
//
|
|
// Ignore non-audit ACEs and
|
|
// Ignore inherited ACEs
|
|
//
|
|
|
|
if ( pAceHeader->AceType != SYSTEM_AUDIT_ACE_TYPE ||
|
|
(pAceHeader->AceFlags & INHERITED_ACE) != 0 ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If this is the explicit ACE we inserted,
|
|
// the consider that the ApplySacl property is true for this object.
|
|
//
|
|
|
|
pSid = (PSID)&pAce->SidStart;
|
|
|
|
if ( RtlEqualSid( pSid, AzGlWorldSid ) &&
|
|
pAce->Mask == pSaclRights->lUserRightsMask ) {
|
|
|
|
ApplySacl = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Tell the caller the whether an explicit SACL was applied
|
|
//
|
|
|
|
WinStatus = (*CallbackRoutine)(
|
|
Context,
|
|
lPersistFlags,
|
|
AZ_PROP_APPLY_STORE_SACL,
|
|
(PSID)&ApplySacl );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
WinStatus = NO_ERROR;
|
|
|
|
//
|
|
// Free local resources
|
|
//
|
|
Cleanup:
|
|
|
|
return WinStatus;
|
|
}
|
|
|
|
//
|
|
// Context for AzpeGetSecurityDescriptorFromCache
|
|
//
|
|
typedef struct _AZP_GET_ACL_CONTEXT_ENTRY {
|
|
|
|
ULONG PropertyId; // Property Id for this policy
|
|
|
|
//
|
|
// Policy Rights as specified by the caller.
|
|
// NULL implies the caller isn't interested
|
|
|
|
PAZP_POLICY_USER_RIGHTS *ppPolicyRights;
|
|
|
|
//
|
|
// Delta array containing the merged data from the existing Security Descriptor
|
|
// plus the deltas added by the application
|
|
//
|
|
|
|
AZP_PTR_ARRAY ResultDeltaArray;
|
|
|
|
} AZP_GET_ACL_CONTEXT_ENTRY, *PAZP_GET_ACL_CONTEXT_ENTRY;
|
|
|
|
typedef struct _AZP_GET_ACL_CONTEXT {
|
|
|
|
//
|
|
// One entry for admins, readers, and delegators
|
|
//
|
|
#define AZP_GET_ACL_CONTEXT_ADMINS 0
|
|
#define AZP_GET_ACL_CONTEXT_READERS 1
|
|
#define AZP_GET_ACL_CONTEXT_DELEGATORS 2
|
|
#define AZP_GET_ACL_CONTEXT_COUNT 3
|
|
AZP_GET_ACL_CONTEXT_ENTRY ContextEntry[AZP_GET_ACL_CONTEXT_COUNT];
|
|
|
|
} AZP_GET_ACL_CONTEXT, *PAZP_GET_ACL_CONTEXT;
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
AzpAddSidsToAcl(
|
|
IN PAZP_PTR_ARRAY DeltaArray,
|
|
IN DWORD AceFlags,
|
|
IN ACCESS_MASK Mask,
|
|
IN OUT PACL pDacl,
|
|
IN GUID *pDelegatedObjectGuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds the SIDs in the passed SID array into the ACL
|
|
with the given mask.
|
|
|
|
Arguments:
|
|
|
|
pSids - The array of SIDs that need to be added to the ACL
|
|
|
|
Mask - The mask used in each ace
|
|
|
|
pDacl - Pointer to the ACL
|
|
|
|
pDelegatedObjectGuid - GUID of the object this ACL will be placed on
|
|
|
|
Return Values:
|
|
|
|
NO_ERROR - The SIDs were added successfully
|
|
|
|
Other Status codes
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD WinStatus = 0;
|
|
ULONG i = 0;
|
|
BOOL bResult = FALSE;
|
|
|
|
//
|
|
// Validation
|
|
//
|
|
|
|
AZASSERT( DeltaArray != NULL );
|
|
AZASSERT( pDacl != NULL );
|
|
|
|
for ( i = 0; i < DeltaArray->UsedCount; i++ ) {
|
|
|
|
PAZP_DELTA_ENTRY DeltaEntry = (PAZP_DELTA_ENTRY)DeltaArray->Array[i];
|
|
ASSERT( DeltaEntry->DeltaFlags & AZP_DELTA_SID );
|
|
ASSERT( DeltaEntry->DeltaFlags & AZP_DELTA_ADD );
|
|
|
|
if ( pDelegatedObjectGuid == NULL ) {
|
|
|
|
bResult = AddAccessAllowedAceEx(
|
|
pDacl,
|
|
ACL_REVISION,
|
|
AceFlags,
|
|
Mask,
|
|
DeltaEntry->Sid
|
|
);
|
|
} else {
|
|
|
|
bResult = AddAccessAllowedObjectAce(
|
|
pDacl,
|
|
ACL_REVISION_DS,
|
|
AceFlags,
|
|
Mask,
|
|
pDelegatedObjectGuid,
|
|
NULL,
|
|
DeltaEntry->Sid
|
|
);
|
|
}
|
|
|
|
if ( !bResult ) {
|
|
|
|
WinStatus = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
WinStatus = NO_ERROR;
|
|
|
|
Cleanup:
|
|
|
|
return WinStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
AzpGetSdWorker(
|
|
IN PVOID Context,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG PropertyId,
|
|
IN PSID Sid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Worker routine for AzpeGetSecurityDescriptorFromCache. This is a callback routine for AzpSdToPolicy.
|
|
It is called for each policy SID and simply remembers the list of sids
|
|
|
|
Arguments:
|
|
|
|
Context - Context from AzpeSetSecurityDescriptorIntoCache. In this case, the context
|
|
is a pointer to a AZP_GET_ACL_CONTEXT structure.
|
|
|
|
lPersistFlags - Internal flags
|
|
|
|
PropertyId - AZ_PROP_* identifying the right granted to the Sid.
|
|
|
|
Sid - Specifies the Sid the right is granted to.
|
|
|
|
Return Values:
|
|
|
|
NO_ERROR - The operation was successful
|
|
Other errors from AzpeAddPropertyItemSid.
|
|
|
|
--*/
|
|
{
|
|
ULONG PolicyIndex;
|
|
PAZP_GET_ACL_CONTEXT RealContext = (PAZP_GET_ACL_CONTEXT)Context;
|
|
PAZP_GET_ACL_CONTEXT_ENTRY ContextEntry = NULL;
|
|
AZP_STRING SidString;
|
|
|
|
//
|
|
// AZ_PROP_APPLY_STORE_SACL is a boolean.
|
|
// There is nothing to merge.
|
|
//
|
|
|
|
ASSERT( PropertyId != AZ_PROP_APPLY_STORE_SACL );
|
|
|
|
//
|
|
// Find the context entry for this policy
|
|
//
|
|
|
|
for ( PolicyIndex=0; PolicyIndex<AZP_GET_ACL_CONTEXT_COUNT; PolicyIndex++ ) {
|
|
|
|
|
|
//
|
|
// If the property ID matches,
|
|
// use it.
|
|
//
|
|
|
|
ContextEntry = &RealContext->ContextEntry[PolicyIndex];
|
|
|
|
if ( ContextEntry->PropertyId == PropertyId ) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if ( PolicyIndex == AZP_GET_ACL_CONTEXT_COUNT ) {
|
|
ASSERT( PolicyIndex != AZP_GET_ACL_CONTEXT_COUNT );
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If the caller isn't interested in this right,
|
|
// we're done.
|
|
//
|
|
|
|
if ( ContextEntry->ppPolicyRights == NULL ) {
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// Add this sid to the list of Sids for this policy
|
|
//
|
|
|
|
AzpInitSid( &SidString, Sid );
|
|
|
|
return ObAddDeltaToArray(
|
|
AZP_DELTA_SID | AZP_DELTA_ADD,
|
|
(GUID *)&SidString,
|
|
&ContextEntry->ResultDeltaArray,
|
|
TRUE ); // Discard entries that are deletions
|
|
|
|
UNREFERENCED_PARAMETER( lPersistFlags );
|
|
|
|
|
|
}
|
|
|
|
DWORD
|
|
AzpeGetSecurityDescriptorFromCache(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags,
|
|
IN PAZP_POLICY_USER_RIGHTS *ppPolicyAdminRights OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS *ppPolicyReaderRights OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS *ppDelegatedPolicyUsersRights OPTIONAL,
|
|
IN GUID *pDelegatedObjectGuid OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS pDelegatedUsersAttributeRights OPTIONAL,
|
|
IN GUID *pAttributeGuid OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS pSaclRights OPTIONAL,
|
|
IN PSECURITY_DESCRIPTOR OldSd OPTIONAL,
|
|
OUT PSECURITY_DESCRIPTOR *NewSd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
AzpeGetSecurityDescriptorFromCache is a support routine for the AZ_PROP_POLICY_ADMINS,
|
|
AZ_PROP_POLICY_READERS, AZ_PROP_DELEGATED_POLICY_USERS, and AZ_PROP_APPLY_STORE_SACL properties.
|
|
The routine reads the deltas to those properties from the object cache and
|
|
builds a DACL and SACL corresponding to that policy.
|
|
|
|
The provider may choose to call AzpeGetDeltaArray for each of those properties
|
|
if the provider's security model does not match that provided by this routine.
|
|
|
|
This routine should only be called from a thread processing a call to AzPersistSubmit.
|
|
|
|
|
|
The caller should only call this routine if the security descriptor should be written.
|
|
Specifically, if the object was just created (the AZ_DIRTY_CREATE dirty bits is set
|
|
for the object) or if the dirty bit for the properties mentioned above are set.
|
|
AzpeGetSecurityDescriptorFromCache process the DACL and SACL separately.
|
|
The caller should also. The caller should only specify the DACL specific parameters
|
|
to this routine if the DACL is to be written. The caller should only specify the SACL
|
|
specific parameter to this routine if the SACL is to be written.
|
|
|
|
If the AZ_DIRTY_CREATE bit is not set, then the previous security descriptor from
|
|
the object needs to be passed to AzpeGetSecurityDescriptorFromCache. Again, the
|
|
DACL should only be read if the DACL changed (as indicated by the dirty bits).
|
|
The SACL should only be read if the SACL changed (as indicated by the dirty bits).
|
|
Otherwise, the security descriptor read may fail because the caller has no access.
|
|
|
|
The returned security descriptor has the SE_DACL_PROTECTED if an only if the DACL
|
|
is to be marked as protected.
|
|
|
|
The caller should not pass the generic access bit GENERIC_READ, GENERIC_WRITE,
|
|
or GENERIC_EXECUTE to this routine. This routine needs to do bit mask comparisons
|
|
with the access mask on the ACLs on the security descriptor. It cannot interpret
|
|
the generic bits. Instead, pass the object specific mask such as FILE_GENERIC_READ.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Handle to object whose ACLs will be read
|
|
|
|
lPersistFlags - Internal flags
|
|
|
|
ppPolicyAdminRights - Rights for policy admins.
|
|
Specifies a NULL terminated array of pointers to AZP_POLICY_USER_RIGHTS structures.
|
|
Each structure specifies an ACE to add to the DACL for each policy admin.
|
|
The first element of the array must be the one passed to AzpeSetSecurityDescriptorIntoCache.
|
|
|
|
ppPolicyReaderRights - Rights for policy readers
|
|
Specifies a NULL terminated array of pointers to AZP_POLICY_USER_RIGHTS structures.
|
|
Each structure specifies an ACE to add to the DACL for each policy reader.
|
|
The first element of the array must be the one passed to AzpeSetSecurityDescriptorIntoCache.
|
|
|
|
ppDelegatedPolicyUsersRights - Rights for delegated users
|
|
Specifies a NULL terminated array of pointers to AZP_POLICY_USER_RIGHTS structures.
|
|
Each structure specifies an ACE to add to the DACL for each delegated policy user.
|
|
The first element of the array must be the one passed to AzpeSetSecurityDescriptorIntoCache.
|
|
|
|
pDelegatedObjectGuid - GUID for an object on which the delegated users
|
|
will have read access on
|
|
|
|
|
|
pDelegatedUsersAttributeRights - the rights that needs to be granted to the delegated users
|
|
for the attribute whoese guid is given by the next parameter.
|
|
|
|
pAttributeGuid - the attribute that will grant special access to the delegated attribute users,
|
|
|
|
pSaclRights - Specifies the rights to put on the SACL
|
|
|
|
OldSd - Specifies the existing security descriptor for the object
|
|
Specify NULL for a newly created object.
|
|
|
|
NewSd - Returns the new self relative security descriptor for the object.
|
|
The returned buffer should be freed using AzpeFreeHeap.
|
|
|
|
Return Values:
|
|
|
|
NO_ERROR - The ACLs were got successfully
|
|
ERROR_EMPTY - The PolicyAdmins list was empty. AzpeGetSecurityDescriptorFromCache added the
|
|
CreatorOwner Sid to the DACL a admin. The provider should apply that
|
|
DACL then re-read the DACL to determine the actual PolicyAdmin. The
|
|
provider should tell AzRole about the actual AZ_PROP_POLICY_ADMINS by
|
|
calling AzpeSetSecurityDescriptorIntoCache or by calling AzpeAddPropertyItemSid for the actual
|
|
creator/owner returned in the read DACL
|
|
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
|
|
ULONG PolicyIndex;
|
|
ULONG DeltaIndex;
|
|
ULONG RightsCountIndex;
|
|
|
|
AZP_GET_ACL_CONTEXT Context;
|
|
PAZP_GET_ACL_CONTEXT_ENTRY ContextEntry;
|
|
|
|
AZP_STRING SidString;
|
|
ULONG SidSize;
|
|
|
|
BOOLEAN UpdateDacl = (ppPolicyAdminRights != NULL || ppPolicyReaderRights != NULL || ppDelegatedPolicyUsersRights != NULL);
|
|
DWORD DaclSize;
|
|
PACL TempDacl = NULL;
|
|
|
|
DWORD SaclSize;
|
|
PACL TempSacl = NULL;
|
|
|
|
SECURITY_DESCRIPTOR TempSd;
|
|
ULONG ObjectType = AzpeObjectType(AzpeObjectHandle);
|
|
|
|
BOOL SetCreatorOwner = FALSE;
|
|
|
|
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
// Build a table since each of the three policies are handled the same way
|
|
//
|
|
|
|
ASSERT( AzpeObjectHandle != NULL );
|
|
RtlZeroMemory( &Context, sizeof(Context) );
|
|
Context.ContextEntry[AZP_GET_ACL_CONTEXT_ADMINS].PropertyId = AZ_PROP_POLICY_ADMINS;
|
|
Context.ContextEntry[AZP_GET_ACL_CONTEXT_ADMINS].ppPolicyRights = ppPolicyAdminRights;
|
|
Context.ContextEntry[AZP_GET_ACL_CONTEXT_READERS].PropertyId = AZ_PROP_POLICY_READERS;
|
|
Context.ContextEntry[AZP_GET_ACL_CONTEXT_READERS].ppPolicyRights = ppPolicyReaderRights;
|
|
Context.ContextEntry[AZP_GET_ACL_CONTEXT_DELEGATORS].PropertyId = AZ_PROP_DELEGATED_POLICY_USERS;
|
|
Context.ContextEntry[AZP_GET_ACL_CONTEXT_DELEGATORS].ppPolicyRights = ppDelegatedPolicyUsersRights;
|
|
|
|
//
|
|
// Ensure the provider didn't pass bogus parameters
|
|
//
|
|
|
|
if ( (lPersistFlags & ~AZPE_FLAGS_PERSIST_SUBMIT) != 0) {
|
|
ASSERT( FALSE );
|
|
WinStatus = ERROR_INVALID_FLAGS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize the local security descriptor
|
|
//
|
|
|
|
if ( !InitializeSecurityDescriptor( &TempSd, SECURITY_DESCRIPTOR_REVISION) ) {
|
|
WinStatus = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the Sids from the existing security descriptor on the object
|
|
//
|
|
// Call AzpGetSdWorker for each sid in the Security Descriptor
|
|
//
|
|
|
|
if ( OldSd != NULL ) {
|
|
WinStatus = AzpSdToPolicy(
|
|
OldSd,
|
|
ppPolicyAdminRights ? ppPolicyAdminRights[0] : NULL,
|
|
ppPolicyReaderRights ? ppPolicyReaderRights[0] : NULL,
|
|
ppDelegatedPolicyUsersRights ? ppDelegatedPolicyUsersRights[0] : NULL,
|
|
NULL, // No need to determine previous state of boolean
|
|
AzpGetSdWorker,
|
|
&Context, // Context
|
|
lPersistFlags );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the caller asked for one,
|
|
// Build the DACL
|
|
//
|
|
|
|
if ( UpdateDacl ) {
|
|
|
|
//
|
|
// Get the Sids that the application submitted
|
|
//
|
|
// Do it for each policy
|
|
//
|
|
|
|
for ( PolicyIndex=0; PolicyIndex<AZP_GET_ACL_CONTEXT_COUNT; PolicyIndex++ ) {
|
|
ULONG SubmittedDeltaArrayCount;
|
|
PAZP_DELTA_ENTRY *SubmittedDeltaArray;
|
|
|
|
|
|
//
|
|
// If the caller isn't asking for this policy,
|
|
// move to the next one
|
|
|
|
ContextEntry = &Context.ContextEntry[PolicyIndex];
|
|
if ( ContextEntry->ppPolicyRights == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Get the submitted delta array from the object
|
|
//
|
|
|
|
WinStatus = AzpeGetDeltaArray(
|
|
AzpeObjectHandle,
|
|
ContextEntry->PropertyId,
|
|
&SubmittedDeltaArrayCount,
|
|
&SubmittedDeltaArray );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
ASSERT( FALSE );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Add each Submitted delta entry to passed in delta array
|
|
//
|
|
|
|
for ( DeltaIndex=0; DeltaIndex<SubmittedDeltaArrayCount; DeltaIndex++ ) {
|
|
|
|
ASSERT( SubmittedDeltaArray[DeltaIndex]->DeltaFlags & AZP_DELTA_SID );
|
|
ASSERT( (SubmittedDeltaArray[DeltaIndex]->DeltaFlags & AZP_DELTA_PERSIST_PROVIDER) == 0 );
|
|
|
|
AzpInitSid( &SidString, SubmittedDeltaArray[DeltaIndex]->Sid );
|
|
|
|
WinStatus = ObAddDeltaToArray(
|
|
SubmittedDeltaArray[DeltaIndex]->DeltaFlags,
|
|
(GUID *)&SidString,
|
|
&ContextEntry->ResultDeltaArray,
|
|
TRUE ); // Discard entries that are deletions
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If the caller wants the list of admins,
|
|
// and the AdminSids list is empty,
|
|
// and this is the authorization store object (and thus doesn't inherit PolicyAdmins),
|
|
// create one with just the creator/owner in it
|
|
//
|
|
|
|
if ( ppPolicyAdminRights != NULL &&
|
|
Context.ContextEntry[AZP_GET_ACL_CONTEXT_ADMINS].ResultDeltaArray.UsedCount == 0 &&
|
|
ObjectType == OBJECT_TYPE_AZAUTHSTORE ) {
|
|
|
|
AzpInitSid( &SidString, AzGlCreatorOwnerSid );
|
|
|
|
WinStatus = ObAddDeltaToArray(
|
|
AZP_DELTA_SID | AZP_DELTA_ADD,
|
|
(GUID *)&SidString,
|
|
&Context.ContextEntry[AZP_GET_ACL_CONTEXT_ADMINS].ResultDeltaArray,
|
|
TRUE ); // Discard entries that are deletions
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
SetCreatorOwner = TRUE;
|
|
}
|
|
|
|
//
|
|
// Compute the size of the DACL
|
|
//
|
|
// Do it for each policy
|
|
//
|
|
|
|
DaclSize = sizeof( ACL );
|
|
|
|
for ( PolicyIndex=0; PolicyIndex<AZP_GET_ACL_CONTEXT_COUNT; PolicyIndex++ ) {
|
|
ULONG i;
|
|
|
|
//
|
|
// If the caller isn't asking for this policy,
|
|
// move to the next one
|
|
|
|
ContextEntry = &Context.ContextEntry[PolicyIndex];
|
|
if ( ContextEntry->ppPolicyRights == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// Determine the size of the ACLS after adding these ACEs
|
|
//
|
|
|
|
for ( i = 0; i < ContextEntry->ResultDeltaArray.UsedCount; i++ ) {
|
|
PAZP_DELTA_ENTRY DeltaEntry = (PAZP_DELTA_ENTRY)ContextEntry->ResultDeltaArray.Array[i];
|
|
|
|
ASSERT( DeltaEntry->DeltaFlags & AZP_DELTA_SID );
|
|
ASSERT( DeltaEntry->DeltaFlags & AZP_DELTA_ADD );
|
|
|
|
SidSize = GetLengthSid( DeltaEntry->Sid );
|
|
|
|
//
|
|
// We have one right count for delegate user's attribute specific ACE
|
|
//
|
|
|
|
if ( PolicyIndex == AZP_GET_ACL_CONTEXT_DELEGATORS &&
|
|
pDelegatedUsersAttributeRights != NULL && pAttributeGuid != NULL ) {
|
|
|
|
DaclSize += sizeof( ACE_HEADER ) + sizeof ( ACCESS_MASK ) + SidSize +
|
|
+ sizeof (ULONG) // For object ACE flags
|
|
+ sizeof( GUID ); // For attribute GUID
|
|
}
|
|
|
|
for ( RightsCountIndex = 0;
|
|
ContextEntry->ppPolicyRights[RightsCountIndex] != NULL;
|
|
RightsCountIndex++ ) {
|
|
|
|
DaclSize += sizeof( ACE_HEADER ) + sizeof ( ACCESS_MASK ) + SidSize;
|
|
|
|
if ( ContextEntry->ppPolicyRights[RightsCountIndex]->lUserRightsFlags &
|
|
INHERIT_ONLY_ACE ) {
|
|
|
|
DaclSize += sizeof (ULONG) // For object ACE flags
|
|
+ sizeof( GUID ); // For object GUID
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Allocate a temporary buffer for the DACL
|
|
//
|
|
|
|
SafeAllocaAllocate( TempDacl, DaclSize )
|
|
|
|
if ( TempDacl == NULL ) {
|
|
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Fill in the DACL
|
|
//
|
|
|
|
if ( !InitializeAcl( TempDacl, DaclSize, ACL_REVISION ) ) {
|
|
WinStatus = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
for ( PolicyIndex=0; PolicyIndex<AZP_GET_ACL_CONTEXT_COUNT; PolicyIndex++ ) {
|
|
|
|
//
|
|
// If the caller isn't asking for this policy,
|
|
// move to the next one
|
|
|
|
ContextEntry = &Context.ContextEntry[PolicyIndex];
|
|
if ( ContextEntry->ppPolicyRights == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If there are no Sids to add,
|
|
// move on to the next one
|
|
//
|
|
|
|
if ( ContextEntry->ResultDeltaArray.UsedCount == 0 ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Loop for each right to add
|
|
//
|
|
|
|
for ( RightsCountIndex = 0;
|
|
ContextEntry->ppPolicyRights[RightsCountIndex] != NULL;
|
|
RightsCountIndex++ ) {
|
|
|
|
PAZP_POLICY_USER_RIGHTS pPolicyRights = ContextEntry->ppPolicyRights[RightsCountIndex];
|
|
|
|
WinStatus = AzpAddSidsToAcl(
|
|
&ContextEntry->ResultDeltaArray,
|
|
pPolicyRights->lUserRightsFlags,
|
|
pPolicyRights->lUserRightsMask,
|
|
TempDacl,
|
|
(pPolicyRights->lUserRightsFlags & INHERIT_ONLY_ACE) ? pDelegatedObjectGuid:NULL );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If we are processing delegators and the caller
|
|
// asks us to put delegated users' attribute rights
|
|
//
|
|
|
|
if ( PolicyIndex == AZP_GET_ACL_CONTEXT_DELEGATORS &&
|
|
pDelegatedUsersAttributeRights != NULL && pAttributeGuid != NULL ) {
|
|
|
|
WinStatus = AzpAddSidsToAcl(
|
|
&ContextEntry->ResultDeltaArray,
|
|
pDelegatedUsersAttributeRights->lUserRightsFlags,
|
|
pDelegatedUsersAttributeRights->lUserRightsMask,
|
|
TempDacl,
|
|
pAttributeGuid
|
|
);
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Add the DACL to the security descriptor
|
|
//
|
|
|
|
if ( !SetSecurityDescriptorDacl(
|
|
&TempSd,
|
|
TRUE,
|
|
TempDacl,
|
|
FALSE ) ) {
|
|
|
|
WinStatus = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If this is the authorization store object, and
|
|
// the caller isn't asking for the SD of the "container" object for delegators,
|
|
// mark the SD as protected so azroles has absolute control of the DACL.
|
|
//
|
|
|
|
if ( ObjectType == OBJECT_TYPE_AZAUTHSTORE &&
|
|
(ppPolicyAdminRights != NULL || ppPolicyReaderRights != NULL) ) {
|
|
|
|
TempSd.Control |= SE_DACL_PROTECTED;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If the caller asked for one, Build the SACL
|
|
//
|
|
// Only container objects can have a SACL
|
|
//
|
|
|
|
if ( pSaclRights && IsContainerObject( ObjectType ) ) {
|
|
|
|
//
|
|
// Only apply a SACL if one is configured
|
|
//
|
|
|
|
if ( ((PGENERIC_OBJECT)AzpeObjectHandle)->ApplySacl ) {
|
|
|
|
//
|
|
// Determine the size of the SACL
|
|
//
|
|
|
|
SaclSize = sizeof(ACL) +
|
|
sizeof(ACE_HEADER) +
|
|
sizeof(ACCESS_MASK) +
|
|
AzGlWorldSidSize;
|
|
|
|
|
|
//
|
|
// Allocate a buffer for the SACL
|
|
//
|
|
|
|
SafeAllocaAllocate( TempSacl, SaclSize );
|
|
|
|
if ( TempSacl == NULL ) {
|
|
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize the SACL
|
|
//
|
|
|
|
if ( !InitializeAcl( TempSacl, SaclSize, ACL_REVISION ) ) {
|
|
WinStatus = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !AddAuditAccessAceEx( TempSacl,
|
|
ACL_REVISION,
|
|
pSaclRights->lUserRightsFlags,
|
|
pSaclRights->lUserRightsMask,
|
|
AzGlWorldSid,
|
|
TRUE, // Audit Success
|
|
TRUE )) { // Audit Failure
|
|
|
|
WinStatus = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Add the SACL to the security descriptor
|
|
//
|
|
// The SACL is never protected
|
|
//
|
|
|
|
if ( !SetSecurityDescriptorSacl(
|
|
&TempSd,
|
|
TRUE,
|
|
TempSacl,
|
|
FALSE ) ) {
|
|
|
|
WinStatus = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Return a self relative SD back to the caller.
|
|
//
|
|
|
|
WinStatus = AzpConvertAbsoluteSDToSelfRelative(
|
|
&TempSd,
|
|
NewSd );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Tell the caller that he needs to re-read the security descriptor
|
|
//
|
|
if ( SetCreatorOwner ) {
|
|
WinStatus = ERROR_EMPTY;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// Free the temporary resultant delta array
|
|
//
|
|
|
|
for ( PolicyIndex=0; PolicyIndex<AZP_GET_ACL_CONTEXT_COUNT; PolicyIndex++ ) {
|
|
|
|
//
|
|
// If the caller isn't asking for this policy,
|
|
// move to the next one
|
|
|
|
ContextEntry = &Context.ContextEntry[PolicyIndex];
|
|
|
|
ASSERT( ContextEntry->ResultDeltaArray.UsedCount == 0 ||
|
|
ContextEntry->ppPolicyRights != NULL );
|
|
|
|
ObFreeDeltaArray( &ContextEntry->ResultDeltaArray,
|
|
TRUE ); // FreeAllEntries
|
|
|
|
}
|
|
|
|
SafeAllocaFree( TempDacl )
|
|
SafeAllocaFree( TempSacl )
|
|
|
|
return WinStatus;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AzpSetSdWorker(
|
|
IN PVOID Context,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG PropertyId,
|
|
IN PSID Sid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Worker routine for AzpeSetSecurityDescriptorIntoCache. It is a callback routine for AzpSdToPolicy.
|
|
It is called for each policy SID and simply call AzpeAddPropertyItemSid for the SID.
|
|
|
|
Arguments:
|
|
|
|
Context - Context from AzpeSetSecurityDescriptorIntoCache In this case, the context is the
|
|
AzpeObjectHandle.
|
|
|
|
lPersistFlags - Internal flags
|
|
|
|
PropertyId - AZ_PROP_* identifying the right granted to the Sid.
|
|
|
|
Sid - Specifies the Sid the right is granted to.
|
|
|
|
Return Values:
|
|
|
|
NO_ERROR - The operation was successful
|
|
Other errors from AzpeAddPropertyItemSid.
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// For AZ_PROP_APPLY_STORE_SACL, call AzpeSetProperty.
|
|
//
|
|
|
|
if ( PropertyId == AZ_PROP_APPLY_STORE_SACL ) {
|
|
|
|
return AzpeSetProperty(
|
|
(AZPE_OBJECT_HANDLE)Context,
|
|
lPersistFlags,
|
|
PropertyId,
|
|
Sid );
|
|
|
|
|
|
//
|
|
// For all other property IDs, simply add the sid to the cache
|
|
//
|
|
|
|
} else {
|
|
|
|
return AzpeAddPropertyItemSid(
|
|
(AZPE_OBJECT_HANDLE)Context,
|
|
lPersistFlags,
|
|
PropertyId,
|
|
Sid );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
AzpeSetSecurityDescriptorIntoCache(
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN PSECURITY_DESCRIPTOR pSD,
|
|
IN ULONG lPersistFlags,
|
|
IN PAZP_POLICY_USER_RIGHTS pAdminRights,
|
|
IN PAZP_POLICY_USER_RIGHTS pReaderRights OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS pDelegatedUserRights OPTIONAL,
|
|
IN PAZP_POLICY_USER_RIGHTS pSaclRights OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
AzpeSetSecurityDescriptorIntoCache is a support routine for the AZ_PROP_POLICY_ADMINS,
|
|
AZ_PROP_POLICY_READERS, AZ_PROP_DELEGATED_POLICY_USERS, and AZ_PROP_APPLY_STORE_SACL properties.
|
|
It inspects the security descriptor for the object and set each of the above
|
|
properties based on the ACEs found.
|
|
|
|
The routine walks the DACL of the security descriptor for an object then
|
|
calls AzpeAddPropertyItemSid for each non-inherited ACE. The provider
|
|
may choose to call AzpeAddPropertyItemSid itself if the provider's security model
|
|
does not match that provided by this routine.
|
|
|
|
The routine also walks the SACL of the security descriptor and calls AzpeSetProperty
|
|
setting AZ_PROP_APPLY_STORE_SACL to TRUE or FALSE depending on whether there is a
|
|
non-inherited ACE specifying the rights passed in pSaclRights.
|
|
|
|
This routine should only be called from a thread processing a call to AzPersistOpen,
|
|
AzPersistUpdateCache, AzPersistUpdateChildrenCache or AzPersistRefresh.
|
|
|
|
This routine may also be called from a thread processing AzPersistSubmit if the
|
|
submitted AZ_PROP_POLICY_ADMINS list is empty. In that case, the provider should default
|
|
the AZ_PROP_POLICY_ADMINS list to the owner of the submitted file and should tell AzRoles
|
|
who that own is by either calling AzpeSetSecurityDescriptorIntoCache or AzpeAddPropertyItemSid.
|
|
|
|
Arguments:
|
|
|
|
AzpeObjectHandle - Pointer to object whose policy admins and readers needs to be loaded.
|
|
|
|
pSD - The current security descriptor for the object on the object in the store
|
|
|
|
lPersistFlags - Internal flags
|
|
|
|
pAdminRights - Rights for admins (Mask and Flags)
|
|
|
|
pReaderRights - Rights for readers (Mask and Flags). Specify NULL if the readers list
|
|
need not be added. (For instance, for AzPersistSubmit as described above.)
|
|
|
|
pDelegatedUserRights - An optional parameter specifying the delegated user's rights (Mask and Flags)
|
|
|
|
pSaclRights - Specifies the rights for the SACL. By convention the provider
|
|
should specify a mask consisting of all the access bits indicating modification
|
|
to the policy store. For instance, DELETE|WRITE_DAC|WRITE_OWNER|FILE_GENERIC_WRITE,
|
|
would be appropriate for a file based store.
|
|
|
|
The parameter should be NULL if AZ_PROP_APPLY_STORE_SACL isn't being set
|
|
into the cache. For instance, when caller doesn't have SE_SECURITY_PRIVILEGE
|
|
or during AzPersistSubmit if the submitted AZ_PROP_POLICY_ADMINS list is empty.
|
|
|
|
|
|
Return Values:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_INVALID_FLAGS - lPersistFlags is invalid.
|
|
ERROR_INVALID_PARAMETER - Property ID is invalid
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Validation
|
|
//
|
|
|
|
ASSERT( AzpeObjectHandle != NULL );
|
|
|
|
//
|
|
// Ensure the provider didn't pass bogus flags
|
|
//
|
|
|
|
if ( (lPersistFlags & ~AZPE_FLAGS_PERSIST_MASK) != 0) {
|
|
ASSERT( FALSE );
|
|
return ERROR_INVALID_FLAGS;
|
|
}
|
|
|
|
//
|
|
// Call AzpSetSdWorker for each sid in the Security Descriptor
|
|
//
|
|
|
|
return AzpSdToPolicy(
|
|
pSD,
|
|
pAdminRights,
|
|
pReaderRights,
|
|
pDelegatedUserRights,
|
|
pSaclRights,
|
|
AzpSetSdWorker,
|
|
AzpeObjectHandle, // Context
|
|
lPersistFlags );
|
|
|
|
}
|
|
|
|
BOOL
|
|
AzpeAzStoreIsBatchUpdateMode(
|
|
IN AZPE_OBJECT_HANDLE hObject
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Determines if the az store is initialized with a batch update flag.
|
|
This flag is set if the store is created to update massive amount of objects.
|
|
|
|
Arguments:
|
|
|
|
hObject - the object handle
|
|
|
|
Return Values:
|
|
|
|
True if the initialization flag has that bit set.
|
|
|
|
--*/
|
|
{
|
|
PAZP_AZSTORE pAzStore = (PAZP_AZSTORE)( ((PGENERIC_OBJECT)hObject)->AzStoreObject );
|
|
return (pAzStore->InitializeFlag & AZ_AZSTORE_FLAG_BATCH_UPDATE);
|
|
}
|
|
|
|
AZPE_OBJECT_HANDLE
|
|
AzpeGetAuthorizationStore(
|
|
IN AZPE_OBJECT_HANDLE hObject
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Get the store handle from any object
|
|
|
|
Arguments:
|
|
|
|
hObject - the object handle
|
|
|
|
Return Values:
|
|
|
|
Returns the object's store handle
|
|
|
|
--*/
|
|
{
|
|
return (AZPE_OBJECT_HANDLE)( ((PGENERIC_OBJECT)hObject)->AzStoreObject );
|
|
}
|