582 lines
14 KiB
C
582 lines
14 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1990 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
notify.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This file contains services which load notification packages and call
|
|||
|
them when passwords are changed using the SamChangePasswordUser2 API.
|
|||
|
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Mike Swift (MikeSw) 30-December-1994
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
User Mode - Win32
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Includes //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
#include <samsrvp.h>
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Private prototypes //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampConfigurePackage(
|
|||
|
IN PWSTR ValueName,
|
|||
|
IN ULONG ValueType,
|
|||
|
IN PVOID ValueData,
|
|||
|
IN ULONG ValueLength,
|
|||
|
IN PVOID Context,
|
|||
|
IN PVOID EntryContext
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// private service data and types //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
|
|||
|
|
|||
|
typedef struct _SAMP_NOTIFICATION_PACKAGE {
|
|||
|
struct _SAMP_NOTIFICATION_PACKAGE * Next;
|
|||
|
UNICODE_STRING PackageName;
|
|||
|
PSAM_PASSWORD_NOTIFICATION_ROUTINE PasswordNotificationRoutine;
|
|||
|
PSAM_DELTA_NOTIFICATION_ROUTINE DeltaNotificationRoutine;
|
|||
|
PSAM_PASSWORD_FILTER_ROUTINE PasswordFilterRoutine;
|
|||
|
} SAMP_NOTIFICATION_PACKAGE, *PSAMP_NOTIFICATION_PACKAGE;
|
|||
|
|
|||
|
PSAMP_NOTIFICATION_PACKAGE SampNotificationPackages = NULL;
|
|||
|
|
|||
|
RTL_QUERY_REGISTRY_TABLE SampRegistryConfigTable [] = {
|
|||
|
{SampConfigurePackage, 0, L"Notification Packages",
|
|||
|
NULL, REG_NONE, NULL, 0},
|
|||
|
{NULL, 0, NULL,
|
|||
|
NULL, REG_NONE, NULL, 0}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Routines //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampConfigurePackage(
|
|||
|
IN PWSTR ValueName,
|
|||
|
IN ULONG ValueType,
|
|||
|
IN PVOID ValueData,
|
|||
|
IN ULONG ValueLength,
|
|||
|
IN PVOID Context,
|
|||
|
IN PVOID EntryContext
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine loads a notification package by loading its DLL and getting
|
|||
|
the address of the notification routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
ValueName - Contains the name of the registry value, ignored.
|
|||
|
ValueType - Contains type of Value, must be REG_SZ.
|
|||
|
ValueData - Contains the package name null-terminated string.
|
|||
|
ValueLength - Length of package name and null terminator, in bytes.
|
|||
|
Context - Passed from caller of RtlQueryRegistryValues, ignored
|
|||
|
EntryContext - Ignored
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UNICODE_STRING PackageName;
|
|||
|
STRING NotificationRoutineName;
|
|||
|
PSAMP_NOTIFICATION_PACKAGE NewPackage = NULL;
|
|||
|
PVOID ModuleHandle = NULL;
|
|||
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|||
|
ULONG PackageSize;
|
|||
|
PSAM_INIT_NOTIFICATION_ROUTINE InitNotificationRoutine = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Make sure we got a string.
|
|||
|
//
|
|||
|
|
|||
|
if (ValueType != REG_SZ) {
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Build the package name from the value data.
|
|||
|
//
|
|||
|
|
|||
|
PackageName.Buffer = (LPWSTR) ValueData;
|
|||
|
PackageName.Length = (USHORT) (ValueLength - sizeof( UNICODE_NULL ));
|
|||
|
PackageName.MaximumLength = (USHORT) ValueLength;
|
|||
|
|
|||
|
//
|
|||
|
// Build the package structure.
|
|||
|
//
|
|||
|
|
|||
|
PackageSize = sizeof(SAMP_NOTIFICATION_PACKAGE) + ValueLength;
|
|||
|
NewPackage = (PSAMP_NOTIFICATION_PACKAGE) RtlAllocateHeap(
|
|||
|
RtlProcessHeap(),
|
|||
|
0,
|
|||
|
PackageSize
|
|||
|
);
|
|||
|
if (NewPackage == NULL) {
|
|||
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory(
|
|||
|
NewPackage,
|
|||
|
PackageSize
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Copy in the package name.
|
|||
|
//
|
|||
|
|
|||
|
NewPackage->PackageName = PackageName;
|
|||
|
|
|||
|
NewPackage->PackageName.Buffer = (LPWSTR) (NewPackage + 1);
|
|||
|
|
|||
|
|
|||
|
RtlCopyUnicodeString(
|
|||
|
&NewPackage->PackageName,
|
|||
|
&PackageName
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Load the notification library.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = LdrLoadDll(
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&PackageName,
|
|||
|
&ModuleHandle
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
RtlInitString(
|
|||
|
&NotificationRoutineName,
|
|||
|
SAM_INIT_NOTIFICATION_ROUTINE
|
|||
|
);
|
|||
|
|
|||
|
NtStatus = LdrGetProcedureAddress(
|
|||
|
ModuleHandle,
|
|||
|
&NotificationRoutineName,
|
|||
|
0,
|
|||
|
(PVOID *) &InitNotificationRoutine
|
|||
|
);
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
ASSERT(InitNotificationRoutine != NULL);
|
|||
|
|
|||
|
//
|
|||
|
// Call the init routine. If it returns false, unload this
|
|||
|
// DLL and continue on.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
if (!InitNotificationRoutine()) {
|
|||
|
NtStatus = STATUS_INTERNAL_ERROR;
|
|||
|
}
|
|||
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
KdPrint(("Exception thrown in Password Notification Routine: 0x%x (%d)\n",
|
|||
|
GetExceptionCode(),GetExceptionCode() ));
|
|||
|
NtStatus = STATUS_ACCESS_VIOLATION;
|
|||
|
}
|
|||
|
} else {
|
|||
|
//
|
|||
|
// This call isn't required, so reset the status to
|
|||
|
// STATUS_SUCCESS.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
RtlInitString(
|
|||
|
&NotificationRoutineName,
|
|||
|
SAM_PASSWORD_CHANGE_NOTIFY_ROUTINE
|
|||
|
);
|
|||
|
|
|||
|
(void) LdrGetProcedureAddress(
|
|||
|
ModuleHandle,
|
|||
|
&NotificationRoutineName,
|
|||
|
0,
|
|||
|
(PVOID *) &NewPackage->PasswordNotificationRoutine
|
|||
|
);
|
|||
|
|
|||
|
RtlInitString(
|
|||
|
&NotificationRoutineName,
|
|||
|
SAM_DELTA_NOTIFY_ROUTINE
|
|||
|
);
|
|||
|
|
|||
|
(void) LdrGetProcedureAddress(
|
|||
|
ModuleHandle,
|
|||
|
&NotificationRoutineName,
|
|||
|
0,
|
|||
|
(PVOID *) &NewPackage->DeltaNotificationRoutine
|
|||
|
);
|
|||
|
|
|||
|
RtlInitString(
|
|||
|
&NotificationRoutineName,
|
|||
|
SAM_PASSWORD_FILTER_ROUTINE
|
|||
|
);
|
|||
|
|
|||
|
(void) LdrGetProcedureAddress(
|
|||
|
ModuleHandle,
|
|||
|
&NotificationRoutineName,
|
|||
|
0,
|
|||
|
(PVOID *) &NewPackage->PasswordFilterRoutine
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// At least one of the two functions must be present
|
|||
|
//
|
|||
|
|
|||
|
if ((NewPackage->PasswordNotificationRoutine == NULL) &&
|
|||
|
(NewPackage->DeltaNotificationRoutine == NULL) &&
|
|||
|
(NewPackage->PasswordFilterRoutine == NULL)) {
|
|||
|
|
|||
|
NtStatus = STATUS_INTERNAL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If all this succeeded, add the routine to the global list.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
|
|||
|
NewPackage->Next = SampNotificationPackages;
|
|||
|
SampNotificationPackages = NewPackage;
|
|||
|
|
|||
|
//
|
|||
|
// Notify the auditing code to record this event.
|
|||
|
//
|
|||
|
|
|||
|
LsaIAuditNotifyPackageLoad(
|
|||
|
&PackageName
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Otherwise delete the entry.
|
|||
|
//
|
|||
|
|
|||
|
RtlFreeHeap(
|
|||
|
RtlProcessHeap(),
|
|||
|
0,
|
|||
|
NewPackage
|
|||
|
);
|
|||
|
|
|||
|
if (ModuleHandle != NULL) {
|
|||
|
(VOID) LdrUnloadDll( ModuleHandle );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampLoadNotificationPackages(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine retrieves the list of packages to be notified during
|
|||
|
password change.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
none
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
NtStatus = RtlQueryRegistryValues(
|
|||
|
RTL_REGISTRY_CONTROL,
|
|||
|
L"Lsa",
|
|||
|
SampRegistryConfigTable,
|
|||
|
NULL, // no context
|
|||
|
NULL // no enviroment
|
|||
|
);
|
|||
|
//
|
|||
|
// Always return STATUS_SUCCESS so we don't block the system from
|
|||
|
// booting.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampPasswordChangeNotify(
|
|||
|
IN PUNICODE_STRING UserName,
|
|||
|
IN ULONG RelativeId,
|
|||
|
IN PUNICODE_STRING NewPassword
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine notifies packages of a password change. It requires that
|
|||
|
the user no longer be locked so that other packages can write to the
|
|||
|
user parameters field.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
UserName - Name of user whose password changed
|
|||
|
|
|||
|
RelativeId - RID of the user whose password changed
|
|||
|
|
|||
|
NewPassword - Cleartext new password for the user
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS only - errors from packages are ignored.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PSAMP_NOTIFICATION_PACKAGE Package;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
Package = SampNotificationPackages;
|
|||
|
|
|||
|
while (Package != NULL) {
|
|||
|
if ( Package->PasswordNotificationRoutine != NULL ) {
|
|||
|
try {
|
|||
|
NtStatus = Package->PasswordNotificationRoutine(
|
|||
|
UserName,
|
|||
|
RelativeId,
|
|||
|
NewPassword
|
|||
|
);
|
|||
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
KdPrint(("Exception thrown in Password Notification Routine: 0x%x (%d)\n",
|
|||
|
GetExceptionCode(),GetExceptionCode() ));
|
|||
|
NtStatus = STATUS_ACCESS_VIOLATION;
|
|||
|
}
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
KdPrint(("Package %wZ failed to accept password change for user %wZ\n",
|
|||
|
&Package->PackageName, UserName ));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Package = Package->Next;
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampPasswordChangeFilter(
|
|||
|
IN PUNICODE_STRING UserName,
|
|||
|
IN PUNICODE_STRING FullName,
|
|||
|
IN PUNICODE_STRING NewPassword,
|
|||
|
IN BOOLEAN SetOperation
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine notifies packages of a password change. It requires that
|
|||
|
the user no longer be locked so that other packages can write to the
|
|||
|
user parameters field.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
UserName - Name of user whose password changed
|
|||
|
|
|||
|
FullName - Full name of the user whose password changed
|
|||
|
|
|||
|
NewPassword - Cleartext new password for the user
|
|||
|
|
|||
|
SetOperation - TRUE if the password was SET rather than CHANGED
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status codes from the notification packages.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PSAMP_NOTIFICATION_PACKAGE Package;
|
|||
|
BOOLEAN Result;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
Package = SampNotificationPackages;
|
|||
|
|
|||
|
while (Package != NULL) {
|
|||
|
if ( Package->PasswordFilterRoutine != NULL ) {
|
|||
|
try {
|
|||
|
Result = Package->PasswordFilterRoutine(
|
|||
|
UserName,
|
|||
|
FullName,
|
|||
|
NewPassword,
|
|||
|
SetOperation
|
|||
|
);
|
|||
|
if (!Result)
|
|||
|
{
|
|||
|
Status = STATUS_PASSWORD_RESTRICTION;
|
|||
|
}
|
|||
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
KdPrint(("Exception thrown in Password Notification Routine: 0x%x (%d)\n",
|
|||
|
GetExceptionCode(),GetExceptionCode() ));
|
|||
|
|
|||
|
//
|
|||
|
// Set result to FALSE so the change fails.
|
|||
|
//
|
|||
|
|
|||
|
Status = STATUS_ACCESS_VIOLATION;
|
|||
|
Result = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!Result) {
|
|||
|
KdPrint(("Package %wZ failed to accept password change for user %wZ: 0x%x\n",
|
|||
|
&Package->PackageName, UserName, Status));
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
Package = Package->Next;
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampDeltaChangeNotify(
|
|||
|
IN PSID DomainSid,
|
|||
|
IN SECURITY_DB_DELTA_TYPE DeltaType,
|
|||
|
IN SECURITY_DB_OBJECT_TYPE ObjectType,
|
|||
|
IN ULONG ObjectRid,
|
|||
|
IN PUNICODE_STRING ObjectName,
|
|||
|
IN PLARGE_INTEGER ModifiedCount,
|
|||
|
IN PSAM_DELTA_DATA DeltaData OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine notifies packages of a change to the SAM database. The
|
|||
|
database is still locked for write access so it requires that nothing
|
|||
|
it calls try to lock the database.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DomainSid - SID of domain for delta
|
|||
|
|
|||
|
DeltaType - Type of delta (change, add ,delete)
|
|||
|
|
|||
|
ObjectType - Type of object changed (user, alias, group ...)
|
|||
|
|
|||
|
ObjectRid - ID of object changed
|
|||
|
|
|||
|
ObjectName - Name of object changed
|
|||
|
|
|||
|
ModifiedCount - Serial number of database after this last change
|
|||
|
|
|||
|
DeltaData - Data describing the exact modification.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS only - errors from packages are ignored.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PSAMP_NOTIFICATION_PACKAGE Package;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
Package = SampNotificationPackages;
|
|||
|
|
|||
|
while (Package != NULL) {
|
|||
|
|
|||
|
if (Package->DeltaNotificationRoutine != NULL) {
|
|||
|
|
|||
|
try {
|
|||
|
NtStatus = Package->DeltaNotificationRoutine(
|
|||
|
DomainSid,
|
|||
|
DeltaType,
|
|||
|
ObjectType,
|
|||
|
ObjectRid,
|
|||
|
ObjectName,
|
|||
|
ModifiedCount,
|
|||
|
DeltaData
|
|||
|
);
|
|||
|
|
|||
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
KdPrint(("Exception thrown in Password Notification Routine: 0x%x (%d)\n",
|
|||
|
GetExceptionCode(),GetExceptionCode() ));
|
|||
|
NtStatus = STATUS_ACCESS_VIOLATION;
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
KdPrint(("Package %wZ failed to accept deltachange for object %wZ\n",
|
|||
|
&Package->PackageName, ObjectName ));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Package = Package->Next;
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|