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.
546 lines
16 KiB
546 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 1987-1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
notify.c
|
|
|
|
Abstract:
|
|
|
|
Sample SubAuthentication Package.
|
|
|
|
Author:
|
|
|
|
Yi-Hsin Sung (yihsins) 27-Feb-1995
|
|
|
|
Revisions:
|
|
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
Contains NT-specific code.
|
|
Requires ANSI C extensions: slash-slash comments, long external names.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <ntsam.h>
|
|
#include <ntlsa.h>
|
|
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
#include <winuser.h>
|
|
|
|
#include <lmaccess.h>
|
|
#include <lmapibuf.h>
|
|
|
|
#include <nwsutil.h>
|
|
#include <fpnwcomm.h>
|
|
#include <usrprop.h>
|
|
#include <fpnwapi.h>
|
|
#include <nwsutil.h>
|
|
|
|
#define SW_DLL_NAME L"swclnt.dll"
|
|
#define PASSWORD_PROC_NAME "SwPasswordChangeNotify"
|
|
#define NOTIFY_PROC_NAME "SwDeltaChangeNotify"
|
|
#define NO_GRACE_LOGIN_LIMIT 0xFF
|
|
|
|
typedef DWORD (*PWPROC)( LPWSTR pUserName,
|
|
ULONG RelativeId,
|
|
LPWSTR pPassword );
|
|
|
|
DWORD
|
|
GetNCPLSASecret(
|
|
VOID
|
|
);
|
|
|
|
BOOL fTriedToGetSW = FALSE;
|
|
BOOL fTriedToGetNCP = FALSE;
|
|
HINSTANCE hinstSW = NULL;
|
|
PWPROC ProcPasswordChange = NULL;
|
|
PSAM_DELTA_NOTIFICATION_ROUTINE ProcDeltaChange = NULL;
|
|
BOOL fGotSecret = FALSE;
|
|
char szNWSecretValue[NCP_LSA_SECRET_LENGTH] = "";
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PasswordChangeNotify(
|
|
PUNICODE_STRING UserName,
|
|
ULONG RelativeId,
|
|
PUNICODE_STRING Password
|
|
)
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
PUSER_INFO_2 pUserInfo2 = NULL;
|
|
LPWSTR pszUser = NULL;
|
|
LPWSTR pszPassword = NULL;
|
|
|
|
//
|
|
// If password is NULL, we can't get the cleartext password. Hence,
|
|
// ignore this notification. Same for UserName.
|
|
//
|
|
if ( (Password == NULL) || (Password->Buffer == NULL) )
|
|
return STATUS_SUCCESS;
|
|
|
|
if ( (UserName == NULL) || (UserName->Buffer == NULL) )
|
|
return STATUS_SUCCESS;
|
|
|
|
//
|
|
// if neither DSMN nor FPNW are installed, blow out of here as there's
|
|
// nothing to do.
|
|
//
|
|
|
|
if ( ( fTriedToGetSW && hinstSW == NULL ) &&
|
|
( fTriedToGetNCP && fGotSecret == FALSE) )
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Make sure user name and password are null terminated
|
|
//
|
|
pszUser = LocalAlloc( LMEM_ZEROINIT, UserName->Length + sizeof(WCHAR));
|
|
|
|
if ( pszUser == NULL )
|
|
return STATUS_NO_MEMORY;
|
|
|
|
pszPassword = LocalAlloc( LMEM_ZEROINIT, Password->Length + sizeof(WCHAR));
|
|
|
|
if ( pszPassword == NULL )
|
|
{
|
|
LocalFree( pszUser );
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
memcpy( pszUser, UserName->Buffer, UserName->Length );
|
|
memcpy( pszPassword, Password->Buffer, Password->Length );
|
|
CharUpper( pszPassword );
|
|
|
|
//
|
|
// First, try to change the small world password if it is installed.
|
|
//
|
|
if ( !fTriedToGetSW )
|
|
{
|
|
hinstSW = LoadLibrary( SW_DLL_NAME );
|
|
fTriedToGetSW = TRUE;
|
|
}
|
|
|
|
if (( hinstSW != NULL ) && ( ProcPasswordChange == NULL ))
|
|
{
|
|
ProcPasswordChange = (PWPROC) GetProcAddress( hinstSW,
|
|
PASSWORD_PROC_NAME );
|
|
}
|
|
|
|
if ( ProcPasswordChange != NULL )
|
|
{
|
|
err = (ProcPasswordChange)( pszUser, RelativeId, pszPassword );
|
|
}
|
|
|
|
#if DBG
|
|
if ( err )
|
|
{
|
|
KdPrint(("[FPNWCLNT] SwPasswordChangeNotify of user %ws changing returns %d.\n", pszUser, err ));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// we require that the PDC be rebooted after either DSMN or FPNW is
|
|
// installed anywhere in the domain for the first server... if we
|
|
// decide we shouldn't require a reboot, change the code such that
|
|
// it looks for the LSA secret everytime, not just first time through.
|
|
//
|
|
|
|
if ( !fTriedToGetNCP ) {
|
|
|
|
fTriedToGetNCP = TRUE;
|
|
|
|
//
|
|
// Get the LSA secret used to encrypt the password
|
|
//
|
|
err = GetNCPLSASecret();
|
|
}
|
|
|
|
if ( !fGotSecret ) {
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Next, change the netware password residue in the user parms field
|
|
//
|
|
err = NetUserGetInfo( NULL,
|
|
pszUser,
|
|
2,
|
|
(LPBYTE *) &pUserInfo2 );
|
|
|
|
if ( !err )
|
|
{
|
|
WCHAR PropertyFlag;
|
|
UNICODE_STRING PropertyValue;
|
|
|
|
err = RtlNtStatusToDosError(
|
|
NetpParmsQueryUserProperty( pUserInfo2->usri2_parms,
|
|
NWPASSWORD,
|
|
&PropertyFlag,
|
|
&PropertyValue ));
|
|
|
|
|
|
if ( !err && PropertyValue.Length != 0 )
|
|
{
|
|
//
|
|
// This is a netware-enabled user, we need to store
|
|
// the new password residue into the user parms
|
|
//
|
|
|
|
NT_PRODUCT_TYPE ProductType;
|
|
WCHAR szEncryptedNWPassword[NWENCRYPTEDPASSWORDLENGTH];
|
|
DWORD dwUserId;
|
|
WORD wGraceLoginRemaining;
|
|
WORD wGraceLoginAllowed;
|
|
|
|
LocalFree( PropertyValue.Buffer );
|
|
|
|
//
|
|
// Get the grace login allowed and remaining value
|
|
//
|
|
err = RtlNtStatusToDosError(
|
|
NetpParmsQueryUserProperty( pUserInfo2->usri2_parms,
|
|
GRACELOGINREMAINING,
|
|
&PropertyFlag,
|
|
&PropertyValue ));
|
|
|
|
if ( !err && ( PropertyValue.Length != 0 ))
|
|
{
|
|
wGraceLoginRemaining = (WORD) *(PropertyValue.Buffer);
|
|
LocalFree( PropertyValue.Buffer );
|
|
|
|
if ( wGraceLoginRemaining != NO_GRACE_LOGIN_LIMIT )
|
|
{
|
|
// If the grace login remaining is not unlimited,
|
|
// then we need to reset grace login remaining to
|
|
// the value in grace login allowed. Hence, read the
|
|
// grace login allowed value.
|
|
|
|
err = RtlNtStatusToDosError(
|
|
NetpParmsQueryUserProperty( pUserInfo2->usri2_parms,
|
|
GRACELOGINALLOWED,
|
|
&PropertyFlag,
|
|
&PropertyValue ));
|
|
|
|
if ( !err && ( PropertyValue.Length != 0 ))
|
|
{
|
|
wGraceLoginAllowed = (WORD) *(PropertyValue.Buffer);
|
|
LocalFree( PropertyValue.Buffer );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
if ( !err )
|
|
{
|
|
RtlGetNtProductType( &ProductType );
|
|
|
|
|
|
dwUserId = MapRidToObjectId(
|
|
RelativeId,
|
|
pszUser,
|
|
ProductType == NtProductLanManNt,
|
|
FALSE );
|
|
|
|
err = RtlNtStatusToDosError(
|
|
ReturnNetwareForm(
|
|
szNWSecretValue,
|
|
dwUserId,
|
|
pszPassword,
|
|
(UCHAR *) szEncryptedNWPassword ));
|
|
}
|
|
|
|
if ( !err )
|
|
{
|
|
LPWSTR pNewUserParms = NULL;
|
|
BOOL fUpdate;
|
|
UNICODE_STRING uPropertyValue;
|
|
|
|
uPropertyValue.Buffer = szEncryptedNWPassword;
|
|
uPropertyValue.Length = uPropertyValue.MaximumLength
|
|
= sizeof( szEncryptedNWPassword );
|
|
|
|
err = RtlNtStatusToDosError(
|
|
NetpParmsSetUserProperty(
|
|
pUserInfo2->usri2_parms,
|
|
NWPASSWORD,
|
|
uPropertyValue,
|
|
PropertyFlag,
|
|
&pNewUserParms,
|
|
&fUpdate ));
|
|
|
|
if ( !err && fUpdate )
|
|
{
|
|
USER_INFO_1013 userInfo1013;
|
|
LPWSTR pNewUserParms2 = NULL;
|
|
LPWSTR pNewUserParms3 = NULL;
|
|
LARGE_INTEGER currentTime;
|
|
|
|
//
|
|
// Since we're resetting the user's password, let's
|
|
// also clear the flag saying the password has
|
|
// expired. We do this by putting the current
|
|
// time into the NWPasswordSet.
|
|
//
|
|
|
|
NtQuerySystemTime (¤tTime);
|
|
|
|
uPropertyValue.Buffer = (PWCHAR) ¤tTime;
|
|
uPropertyValue.Length = sizeof (LARGE_INTEGER);
|
|
uPropertyValue.MaximumLength = sizeof (LARGE_INTEGER);
|
|
|
|
NetpParmsSetUserProperty( pNewUserParms,
|
|
NWTIMEPASSWORDSET,
|
|
uPropertyValue,
|
|
(SHORT) 0, // not a set
|
|
&pNewUserParms2,
|
|
&fUpdate );
|
|
|
|
if (pNewUserParms2 != NULL) {
|
|
userInfo1013.usri1013_parms = pNewUserParms2;
|
|
} else {
|
|
userInfo1013.usri1013_parms = pNewUserParms;
|
|
}
|
|
|
|
if ( wGraceLoginRemaining != NO_GRACE_LOGIN_LIMIT )
|
|
{
|
|
// If the grace login remaining is not unlimited,
|
|
// then we need to reset grace login remaining to
|
|
// the value in grace login allowed.
|
|
|
|
uPropertyValue.Buffer = (PWCHAR) &wGraceLoginAllowed;
|
|
uPropertyValue.Length = uPropertyValue.MaximumLength
|
|
= sizeof(wGraceLoginAllowed);
|
|
|
|
NetpParmsSetUserProperty( userInfo1013.usri1013_parms,
|
|
GRACELOGINREMAINING,
|
|
uPropertyValue,
|
|
(SHORT) 0, // not a set
|
|
&pNewUserParms3,
|
|
&fUpdate );
|
|
|
|
if (pNewUserParms3 != NULL)
|
|
userInfo1013.usri1013_parms = pNewUserParms3;
|
|
}
|
|
|
|
err = NetUserSetInfo( NULL,
|
|
pszUser,
|
|
USER_PARMS_INFOLEVEL,
|
|
(LPBYTE) &userInfo1013,
|
|
NULL );
|
|
|
|
if (pNewUserParms2 != NULL)
|
|
NetpParmsUserPropertyFree( pNewUserParms2 );
|
|
|
|
if (pNewUserParms3 != NULL)
|
|
NetpParmsUserPropertyFree( pNewUserParms3 );
|
|
}
|
|
|
|
if ( pNewUserParms != NULL )
|
|
NetpParmsUserPropertyFree( pNewUserParms );
|
|
}
|
|
}
|
|
|
|
NetApiBufferFree( pUserInfo2 );
|
|
}
|
|
|
|
#if DBG
|
|
if ( err )
|
|
{
|
|
KdPrint(("[FPNWCLNT] Password of user %ws changing returns %d.\n",
|
|
pszUser, err ));
|
|
}
|
|
#endif
|
|
|
|
CleanUp:
|
|
|
|
LocalFree( pszUser );
|
|
|
|
// Need to clear all memory that contains password
|
|
memset( pszPassword, 0, Password->Length + sizeof( WCHAR ));
|
|
LocalFree( pszPassword );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
InitializeChangeNotify (
|
|
VOID
|
|
)
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
|
|
//
|
|
// First, check to see if small world is installed.
|
|
//
|
|
if ( !fTriedToGetSW )
|
|
{
|
|
hinstSW = LoadLibrary( SW_DLL_NAME );
|
|
fTriedToGetSW = TRUE;
|
|
}
|
|
|
|
if (( hinstSW != NULL )) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if ( !fTriedToGetNCP ) {
|
|
|
|
fTriedToGetNCP = TRUE;
|
|
|
|
//
|
|
// Get the LSA secret used to encrypt the password
|
|
//
|
|
err = GetNCPLSASecret();
|
|
}
|
|
|
|
return (fGotSecret != 0);
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
GetNCPLSASecret(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD err;
|
|
LSA_HANDLE hlsaPolicy;
|
|
OBJECT_ATTRIBUTES oa;
|
|
SECURITY_QUALITY_OF_SERVICE sqos;
|
|
LSA_HANDLE hlsaSecret;
|
|
UNICODE_STRING uSecretName;
|
|
UNICODE_STRING *puSecretValue;
|
|
LARGE_INTEGER lintCurrentSetTime, lintOldSetTime;
|
|
|
|
sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|
sqos.ImpersonationLevel = SecurityImpersonation;
|
|
sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
sqos.EffectiveOnly = FALSE;
|
|
|
|
//
|
|
// Set up the object attributes prior to opening the LSA.
|
|
//
|
|
|
|
InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
|
|
|
|
//
|
|
// The InitializeObjectAttributes macro presently store NULL for
|
|
// the psqos field, so we must manually copy that
|
|
// structure for now.
|
|
//
|
|
|
|
oa.SecurityQualityOfService = &sqos;
|
|
|
|
|
|
err = RtlNtStatusToDosError( LsaOpenPolicy( NULL,
|
|
&oa,
|
|
GENERIC_EXECUTE,
|
|
&hlsaPolicy ));
|
|
|
|
if ( !err )
|
|
{
|
|
RtlInitUnicodeString( &uSecretName, NCP_LSA_SECRET_KEY );
|
|
err = RtlNtStatusToDosError( LsaOpenSecret( hlsaPolicy,
|
|
&uSecretName,
|
|
SECRET_QUERY_VALUE,
|
|
&hlsaSecret ));
|
|
|
|
if ( !err )
|
|
{
|
|
err = RtlNtStatusToDosError(
|
|
LsaQuerySecret( hlsaSecret,
|
|
&puSecretValue,
|
|
&lintCurrentSetTime,
|
|
NULL,
|
|
&lintOldSetTime ));
|
|
|
|
if ( !err )
|
|
{
|
|
memcpy( szNWSecretValue,
|
|
puSecretValue->Buffer,
|
|
NCP_LSA_SECRET_LENGTH );
|
|
|
|
fGotSecret = TRUE;
|
|
|
|
(VOID) LsaFreeMemory( puSecretValue );
|
|
}
|
|
|
|
(VOID) LsaClose( hlsaSecret );
|
|
|
|
}
|
|
|
|
(VOID) LsaClose( hlsaPolicy );
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
DeltaNotify(
|
|
IN PSID DomainSid,
|
|
IN SECURITY_DB_DELTA_TYPE DeltaType,
|
|
IN SECURITY_DB_OBJECT_TYPE ObjectType,
|
|
IN ULONG ObjectRid,
|
|
IN PUNICODE_STRING ObjectName OPTIONAL,
|
|
IN PLARGE_INTEGER ModifiedCount,
|
|
IN PSAM_DELTA_DATA DeltaData OPTIONAL
|
|
)
|
|
{
|
|
NTSTATUS err = NO_ERROR;
|
|
|
|
//
|
|
// Try to notify small world of SAM changes if it is installed.
|
|
//
|
|
|
|
if ( !fTriedToGetSW )
|
|
{
|
|
hinstSW = LoadLibrary( SW_DLL_NAME );
|
|
fTriedToGetSW = TRUE;
|
|
}
|
|
|
|
if ( ( hinstSW != NULL ) && ( ProcDeltaChange == NULL ))
|
|
{
|
|
ProcDeltaChange = (PSAM_DELTA_NOTIFICATION_ROUTINE)
|
|
GetProcAddress( hinstSW, NOTIFY_PROC_NAME );
|
|
}
|
|
|
|
if ( ProcDeltaChange != NULL )
|
|
{
|
|
err = (ProcDeltaChange)( DomainSid,
|
|
DeltaType,
|
|
ObjectType,
|
|
ObjectRid,
|
|
ObjectName,
|
|
ModifiedCount,
|
|
DeltaData );
|
|
}
|
|
|
|
#if DBG
|
|
if ( err )
|
|
{
|
|
KdPrint(("[FPNWCLNT] SwDeltaChangeNotify of type %d on rid 0x%x returns %d.\n", DeltaType, ObjectRid, err ));
|
|
}
|
|
#endif
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|