|
|
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
dbnotify.c
Abstract:
Implemntation of the LSA routines for notifying in processes callers when data changes
Author:
Mac McLain (MacM) May 22, 1997
Environment:
User Mode
Revision History:
--*/ #include <lsapch2.h>
#include <dbp.h>
//
// Global notification list
//
LSAP_POLICY_NOTIFICATION_LIST LsaPolicyChangeNotificationList[ PolicyNotifyMachineAccountPasswordInformation + 1 ]; SAFE_RESOURCE LsaPolicyChangeNotificationLock;
#define LSAP_NOTIFY_MAXIMUM_PER_CLASS 1000
//
// Local prototypes
//
DWORD WINAPI LsapNotifyChangeNotificationThread( LPVOID Parameter );
NTSTATUS LsapInitializeNotifiyList( VOID ) /*++
Routine Description:
Intializes the list of policy notification lists
Arguments:
VOID
Return Value:
VOID
--*/ { ULONG i; NTSTATUS Status ;
for ( i = 0; i < sizeof( LsaPolicyChangeNotificationList ) / sizeof( LSAP_POLICY_NOTIFICATION_LIST ); i++ ) {
InitializeListHead( &( LsaPolicyChangeNotificationList[ i ].List ) ); LsaPolicyChangeNotificationList[ i ].Callbacks = 0; }
try { SafeInitializeResource( &LsaPolicyChangeNotificationLock, ( DWORD )POLICY_CHANGE_NOTIFICATION_LOCK_ENUM ); Status = STATUS_SUCCESS ; } except ( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); }
return Status ; }
NTSTATUS LsapNotifyAddCallbackToList( IN PLSAP_POLICY_NOTIFICATION_LIST List, IN OPTIONAL pfLsaPolicyChangeNotificationCallback Callback, IN OPTIONAL HANDLE NotificationEvent, IN OPTIONAL ULONG OwnerProcess, IN OPTIONAL HANDLE OwnerEvent ) /*++
Routine Description:
This function inserts a new callback node into the existing list.
Arguments:
List -- Existing list
Callback -- Callback function pointer. Can be NULL if NotificationEvent is provided
NotificationEvent - Handle to an event to be signalled for notification. Can be NULL if Callback is provided.
Return Value:
STATUS_SUCCESS -- Success
STATUS_INSUFFICIENT_RESOURCES -- A memory allocation failed.
--*/ { NTSTATUS Status = STATUS_SUCCESS; PLSAP_POLICY_NOTIFICATION_ENTRY NewEntry = NULL;
NewEntry = LsapAllocateLsaHeap( sizeof( LSAP_POLICY_NOTIFICATION_ENTRY ) );
if ( !NewEntry ) {
return STATUS_INSUFFICIENT_RESOURCES; }
if ( !SafeAcquireResourceExclusive( &LsaPolicyChangeNotificationLock, TRUE ) ) {
LsapFreeLsaHeap( NewEntry ); return( STATUS_UNSUCCESSFUL ); }
if ( List->Callbacks < LSAP_NOTIFY_MAXIMUM_PER_CLASS ) {
InsertTailList( &List->List, &NewEntry->List );
NewEntry->NotificationCallback = Callback; NewEntry->NotificationEvent = NotificationEvent; NewEntry->HandleInvalid = FALSE; NewEntry->OwnerProcess = OwnerProcess; NewEntry->OwnerEvent = OwnerEvent;
List->Callbacks++; }
SafeReleaseResource( &LsaPolicyChangeNotificationLock );
return( Status ); }
NTSTATUS LsapNotifyRemoveCallbackFromList( IN PLSAP_POLICY_NOTIFICATION_LIST List, IN OPTIONAL pfLsaPolicyChangeNotificationCallback Callback, IN OPTIONAL ULONG OwnerProcess, IN OPTIONAL HANDLE OwnerEvent ) /*++
Routine Description:
This function inserts a new callback node into the existing list.
Arguments:
List -- Existing list
Callback -- Callback function pointer. Can be NULL if a notification event is provided
NotificationEvent -- Notification event handle to be revomed. Can be NULL if a callback is provided
Return Value:
STATUS_SUCCESS -- Success
STATUS_NOT_FOUND -- The supplied callback was not found in the specified list
--*/ { NTSTATUS Status = STATUS_NOT_FOUND; ULONG i; PLSAP_POLICY_NOTIFICATION_ENTRY Entry;
if ( !SafeAcquireResourceExclusive( &LsaPolicyChangeNotificationLock, TRUE ) ) {
return( STATUS_UNSUCCESSFUL ); }
Entry = (PLSAP_POLICY_NOTIFICATION_ENTRY)List->List.Flink;
for ( i = 0; i < List->Callbacks; i++ ) {
if ( Entry->NotificationCallback == Callback && Entry->OwnerProcess == OwnerProcess && Entry->OwnerEvent == OwnerEvent ) {
List->Callbacks--; RemoveEntryList( &Entry->List );
if ( Entry->NotificationEvent != NULL && Entry->NotificationEvent != INVALID_HANDLE_VALUE ) {
NtClose( Entry->NotificationEvent ); }
LsapFreeLsaHeap( Entry ); Status = STATUS_SUCCESS; break; }
Entry = (PLSAP_POLICY_NOTIFICATION_ENTRY)Entry->List.Flink; }
SafeReleaseResource( &LsaPolicyChangeNotificationLock );
return( Status ); }
NTSTATUS LsaINotifyChangeNotification( IN POLICY_NOTIFICATION_INFORMATION_CLASS InfoClass ) /*++
Routine Description:
This function processes a notification list by making the appropriate callback calls when a policy object has changed
Arguments:
InfoClass -- Policy information that has changed
Return Value:
STATUS_SUCCESS -- Success
STATUS_UNSUCCESSFUL -- Failed to lock the list for access
--*/ { NTSTATUS Status = STATUS_SUCCESS;
ASSERT( InfoClass <= sizeof( LsaPolicyChangeNotificationList ) / sizeof( LSAP_POLICY_NOTIFICATION_LIST ) );
if ( LsaIRegisterNotification( LsapNotifyChangeNotificationThread, ( PVOID ) InfoClass, NOTIFIER_TYPE_IMMEDIATE, 0, NOTIFIER_FLAG_ONE_SHOT, 0, 0 ) == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES; }
return( Status ); }
DWORD WINAPI LsapNotifyChangeNotificationThread( LPVOID Parameter ) /*++
Routine Description:
This function processes a notification list by making the appropriate callback calls when a policy object has changed
Arguments:
Parameter -- Policy information that has changed
Return Value:
STATUS_SUCCESS -- Success
STATUS_UNSUCCESSFUL -- Failed to lock the list for access
--*/ { NTSTATUS Status = STATUS_SUCCESS;
ULONG i; POLICY_NOTIFICATION_INFORMATION_CLASS InfoClass = ( POLICY_NOTIFICATION_INFORMATION_CLASS ) ( ( ULONG_PTR ) Parameter ); PLSAP_POLICY_NOTIFICATION_ENTRY Entry;
ASSERT( InfoClass <= sizeof( LsaPolicyChangeNotificationList ) / sizeof( LSAP_POLICY_NOTIFICATION_LIST ) );
if ( !SafeAcquireResourceShared( &LsaPolicyChangeNotificationLock, TRUE ) ) {
return STATUS_UNSUCCESSFUL; }
Entry = (PLSAP_POLICY_NOTIFICATION_ENTRY)LsaPolicyChangeNotificationList[ InfoClass ].List.Flink;
for ( i = 0; i < LsaPolicyChangeNotificationList[ InfoClass ].Callbacks; i++ ) {
ASSERT( Entry->NotificationCallback || Entry->NotificationEvent );
if ( Entry->NotificationCallback ) {
(*Entry->NotificationCallback)( InfoClass );
} else if ( Entry->NotificationEvent ) {
if ( !Entry->HandleInvalid ) {
Status = NtSetEvent( Entry->NotificationEvent, NULL );
if ( Status == STATUS_INVALID_HANDLE ) {
Entry->HandleInvalid = TRUE; } }
} else {
LsapDsDebugOut(( DEB_ERROR, "NULL callback found for info level %lu\n", InfoClass )); }
Entry = (PLSAP_POLICY_NOTIFICATION_ENTRY)Entry->List.Flink; }
SafeReleaseResource( &LsaPolicyChangeNotificationLock );
return( Status ); }
NTSTATUS LsaIRegisterPolicyChangeNotificationCallback( IN pfLsaPolicyChangeNotificationCallback Callback, IN POLICY_NOTIFICATION_INFORMATION_CLASS MonitorInfoClass ) /*++
Routine Description:
This function registers a callback with the Lsa server such that a change to the specified policy items results in the callback being called. These callbacks are informational only, such that a client must return instantly, not doing an Lsa calls in their callback.
Multiple callbacks can be specified for the same policy information.
Arguments:
Callback -- Callback function pointer.
MonitorInfoClass -- Policy information to watch for
Return Value:
STATUS_SUCCESS -- Success
STATUS_INVALID_PARAMETER -- A bad callback pointer was specified
STATUS_UNSUCCESSFUL -- Failed to lock the list for access
--*/ { NTSTATUS Status = STATUS_SUCCESS;
if ( !Callback ) {
return STATUS_INVALID_PARAMETER; }
if ( !SafeAcquireResourceExclusive( &LsaPolicyChangeNotificationLock, TRUE ) ) {
return STATUS_UNSUCCESSFUL; }
ASSERT( MonitorInfoClass <= sizeof( LsaPolicyChangeNotificationList ) / sizeof( LSAP_POLICY_NOTIFICATION_LIST ) );
Status = LsapNotifyAddCallbackToList( &LsaPolicyChangeNotificationList[ MonitorInfoClass ], Callback, NULL, 0, NULL );
LsapDsDebugOut(( DEB_NOTIFY, "Insertion of callback 0x%lx for %lu returned 0x%lx\n", Callback, MonitorInfoClass, Status ));
SafeReleaseResource( &LsaPolicyChangeNotificationLock );
return( Status ); }
NTSTATUS LsaIUnregisterPolicyChangeNotificationCallback( IN pfLsaPolicyChangeNotificationCallback Callback, IN POLICY_NOTIFICATION_INFORMATION_CLASS MonitorInfoClass ) /*++
Routine Description:
This function unregisters a callback from the Lsa server such that a change to the specified policy items do not result in a call to the client callback function.
Arguments:
Callback -- Callback function pointer to remove.
MonitorInfoClass -- Policy information to remove the callback for
Return Value:
STATUS_SUCCESS -- Success
STATUS_INVALID_PARAMETER -- A bad callback pointer was specified
STATUS_UNSUCCESSFUL -- Failed to lock the list for access
--*/ { NTSTATUS Status = STATUS_SUCCESS;
if ( !Callback ) {
return STATUS_INVALID_PARAMETER; }
if ( !SafeAcquireResourceExclusive( &LsaPolicyChangeNotificationLock, TRUE ) ) {
return STATUS_UNSUCCESSFUL; }
Status = LsapNotifyRemoveCallbackFromList( &LsaPolicyChangeNotificationList[ MonitorInfoClass ], Callback, 0, NULL );
LsapDsDebugOut(( DEB_NOTIFY, "Removal of callback 0x%lx for %lu returned 0x%lx\n", Callback, MonitorInfoClass, Status ));
SafeReleaseResource( &LsaPolicyChangeNotificationLock );
return( Status ); }
NTSTATUS LsaIUnregisterAllPolicyChangeNotificationCallback( IN pfLsaPolicyChangeNotificationCallback Callback ) /*++
Routine Description:
This function unregisters the specified callback function from all associated policy. This function is the equivalent of calling LsaIUnregisterPolicyChangeNotificationCallback for each InfoClass that was being watched.
Arguments:
Callback -- Callback function pointer to remove.
Return Value:
STATUS_SUCCESS -- Success
STATUS_INVALID_PARAMETER -- A bad callback pointer was specified
STATUS_UNSUCCESSFUL -- Failed to lock the list for access
STATUS_NOT_FOUND -- No matching entries were found
--*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG i, Removed = 0;
if ( !Callback ) {
return STATUS_INVALID_PARAMETER; }
if ( !SafeAcquireResourceExclusive( &LsaPolicyChangeNotificationLock, TRUE ) ) {
return STATUS_UNSUCCESSFUL; }
Removed = 0;
for ( i = 0; i < sizeof( LsaPolicyChangeNotificationList ) / sizeof( LSAP_POLICY_NOTIFICATION_LIST ) && NT_SUCCESS( Status ); i++ ) {
Status = LsapNotifyRemoveCallbackFromList( &LsaPolicyChangeNotificationList[ i ], Callback, 0, NULL );
LsapDsDebugOut(( DEB_NOTIFY, "Removal of callback 0x%lx for %lu returned 0x%lx\n", Callback, i, Status ));
if ( Status == STATUS_NOT_FOUND ) {
Status = STATUS_SUCCESS;
} else if ( Status == STATUS_SUCCESS ) {
Removed++; } }
SafeReleaseResource( &LsaPolicyChangeNotificationLock );
//
// Make sure we removed at least one
//
if ( NT_SUCCESS( Status ) ) {
if ( Removed == 0 ) {
Status = STATUS_NOT_FOUND; } }
return( Status ); }
NTSTATUS LsapNotifyProcessNotificationEvent( IN POLICY_NOTIFICATION_INFORMATION_CLASS InformationClass, IN HANDLE NotificationEvent, IN ULONG OwnerProcess, IN HANDLE OwnerEventHandle, IN BOOLEAN Register ) /*++
Routine Description:
This function registers / unregisters the specified Notification event handle for the specified information class
Arguments:
InformationClass -- Information class to add/remove the notification for
NotificationEvent -- Event handle to register/deregister
Register -- If TRUE, the event is being registered. If FALSE, it is unregistered
Return Value:
STATUS_SUCCESS -- Success
STATUS_INVALID_HANDLE -- A bad event handle was specified
STATUS_ACCESS_DENIED -- The opened policy handle does not have the requried permissions
STATUS_INSUFFICIENT_RESOURCES -- A memory allocation failed
STATUS_INVALID_INFO_CLASS -- An invalid information class was provided.
--*/ { NTSTATUS Status = STATUS_SUCCESS; LSAP_DB_OBJECT_INFORMATION ObjectInformation; POBJECT_TYPE_INFORMATION ObjTypeInfo = NULL; ULONG Length = 0, ReturnLength = 0; UNICODE_STRING EventString; LSAPR_HANDLE PolicyHandle = NULL; OBJECT_ATTRIBUTES ObjectAttributes;
//
// Make sure we are given a valid info class
//
if ( InformationClass < PolicyNotifyAuditEventsInformation || InformationClass > PolicyNotifyMachineAccountPasswordInformation ) {
return( STATUS_INVALID_INFO_CLASS ); }
//
// Make sure the caller has the proper privileges.
//
// We're already impersonating our caller so LsapDbOpenPolicy doesn't need to.
//
InitializeObjectAttributes( &ObjectAttributes, NULL, 0L, NULL, NULL );
Status = LsapDbOpenPolicy( NULL, (PLSAPR_OBJECT_ATTRIBUTES) &ObjectAttributes, POLICY_NOTIFICATION, LSAP_DB_USE_LPC_IMPERSONATE, &PolicyHandle, FALSE ); // Not a trusted client
if ( NT_SUCCESS( Status ) && Register ) {
if ( NotificationEvent == NULL || NotificationEvent == INVALID_HANDLE_VALUE ) {
Status = STATUS_INVALID_HANDLE; } }
if ( NT_SUCCESS( Status ) && Register ) {
//
// Verify that the handle is one for an Event
//
Status = NtQueryObject( NotificationEvent, ObjectTypeInformation, ObjTypeInfo, Length, &ReturnLength );
if ( Status == STATUS_INFO_LENGTH_MISMATCH ) {
ObjTypeInfo = LsapAllocateLsaHeap( ReturnLength );
if ( ObjTypeInfo == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
Length = ReturnLength; Status = NtQueryObject( NotificationEvent, ObjectTypeInformation, ObjTypeInfo, Length, &ReturnLength );
if ( NT_SUCCESS( Status ) ) {
//
// See if it's actually an event
//
RtlInitUnicodeString( &EventString, L"Event" ); if ( !RtlEqualUnicodeString( &EventString, &ObjTypeInfo->TypeName, FALSE ) ) {
Status = STATUS_INVALID_HANDLE; }
}
LsapFreeLsaHeap( ObjTypeInfo ); }
} else if ( Status == STATUS_SUCCESS ) {
LsapDsDebugOut(( DEB_ERROR, "NtQueryObject returned success on a NULL buffer\n" )); Status = STATUS_UNSUCCESSFUL; } }
//
// Now, add or remove the information from the list
//
if ( NT_SUCCESS( Status ) ) {
if ( Register ) {
Status = LsapNotifyAddCallbackToList( &LsaPolicyChangeNotificationList[ InformationClass ], NULL, NotificationEvent, OwnerProcess, OwnerEventHandle );
} else {
Status = LsapNotifyRemoveCallbackFromList( &LsaPolicyChangeNotificationList[ InformationClass ], NULL, OwnerProcess, OwnerEventHandle );
} }
if ( PolicyHandle != NULL ) {
LsapCloseHandle( &PolicyHandle, Status ); }
return( Status ); }
|