|
|
/****************************** Module Header ******************************\
* Module Name: mpnotify.c * * Copyright (c) 1991, Microsoft Corporation * * MpNotify main module * * Mpnotify is an app executed by winlogon to notify network providers * of authentication events. Currently this means logon and password change. * This functionality is in a separate process to avoid network providers * having to handle the asynchronous events that winlogon receives. * * Winlogon initializes environment variables to describe the event * and then executes this process in system context on the winlogon * desktop. While this process executes, winlogon handls all screen-saver * and logoff notifications. Winlogon will terminate this process if required * to respond to events (e.g. remote shutdown). * * On completion this process signals winlogon by sending a buffer of * data to it in a WM_COPYDATA message and then quits. * * History: * 01-12-93 Davidc Created. \***************************************************************************/
#include "mpnotify.h"
#include <ntmsv1_0.h>
#include <mpr.h>
#include <npapi.h>
#include <stdio.h>
//
// Define to enable verbose output for this module
//
// #define DEBUG_MPNOTIFY
#ifdef DEBUG_MPNOTIFY
#define VerbosePrint(s) MPPrint(s)
#else
#define VerbosePrint(s)
#endif
//
// Define the environment variable names used to pass the
// notification event data
//
#define MPR_STATION_NAME_VARIABLE TEXT("WlMprNotifyStationName")
#define MPR_STATION_HANDLE_VARIABLE TEXT("WlMprNotifyStationHandle")
#define MPR_WINLOGON_WINDOW_VARIABLE TEXT("WlMprNotifyWinlogonWindow")
#define MPR_LOGON_FLAG_VARIABLE TEXT("WlMprNotifyLogonFlag")
#define MPR_USERNAME_VARIABLE TEXT("WlMprNotifyUserName")
#define MPR_DOMAIN_VARIABLE TEXT("WlMprNotifyDomain")
#define MPR_PASSWORD_VARIABLE TEXT("WlMprNotifyPassword")
#define MPR_OLD_PASSWORD_VARIABLE TEXT("WlMprNotifyOldPassword")
#define MPR_OLD_PASSWORD_VALID_VARIABLE TEXT("WlMprNotifyOldPasswordValid")
#define MPR_LOGONID_VARIABLE TEXT("WlMprNotifyLogonId")
#define MPR_CHANGE_INFO_VARIABLE TEXT("WlMprNotifyChangeInfo")
#define MPR_PASSTHROUGH_VARIABLE TEXT("WlMprNotifyPassThrough")
#define MPR_PROVIDER_VARIABLE TEXT("WlMprNotifyProvider")
#define MPR_DESKTOP_VARIABLE TEXT("WlMprNotifyDesktop")
#define WINLOGON_DESKTOP_NAME TEXT("Winlogon")
//
// Define the authentication info type that we use
// This lets the provider know that we're passing
// an MSV1_0_INTERACTIVE_LOGON structure.
//
#define AUTHENTICATION_INFO_TYPE TEXT("MSV1_0:Interactive")
//
// Define the primary authenticator
//
#define PRIMARY_AUTHENTICATOR TEXT("Microsoft Windows Network")
/***************************************************************************\
* ScrubString * * Wipes out the content of the string. \***************************************************************************/ void ScrubString( LPTSTR lpName ) { while(*lpName) { *lpName++ = TEXT(' '); } }
/***************************************************************************\
* AllocAndGetEnvironmentVariable * * Version of GetEnvironmentVariable that allocates the return buffer. * * Returns pointer to environment variable or NULL on failure. This routine * will also return NULL if the environment variable is a 0 length string. * * The returned buffer should be free using Free() * * History: * 09-Dec-92 Davidc Created * \***************************************************************************/ LPTSTR AllocAndGetEnvironmentVariable( LPTSTR lpName ) { LPTSTR Buffer; DWORD LengthRequired; DWORD LengthUsed; DWORD BytesRequired;
//
// Go search for the variable and find its length
//
LengthRequired = GetEnvironmentVariable(lpName, NULL, 0);
if (LengthRequired == 0) { VerbosePrint(("Environment variable <%ws> not found, error = %d", lpName, GetLastError())); return(NULL); }
//
// Allocate a buffer to hold the variable
//
BytesRequired = LengthRequired * sizeof(TCHAR);
Buffer = Alloc(BytesRequired); if (Buffer == NULL) { MPPrint(("Failed to allocate %d bytes for environment variable", BytesRequired)); return(NULL); }
//
// Go get the variable and pass a buffer this time
//
LengthUsed = GetEnvironmentVariable(lpName, Buffer, LengthRequired);
// Now is a good time to cleanup the env variable
if (LengthRequired > 1) { // There is a non empty password
if (wcsstr(lpName, TEXT("Password"))) { // Matches WlMprNotifyOldPassword & WlMprNotifyPassword
LPTSTR Stars, Cursor;
Stars = Alloc(BytesRequired); if (Stars != NULL) { // Alloc inits to 0 and we can't have an empty value.
// Get Cursor to point to the last char
Cursor = Stars + LengthRequired - 1; // We need to wipe out from Cursor to Stars
do { *(--Cursor) = TEXT('*'); } while(Stars != Cursor);
// That'll wipe out our env var...
SetEnvironmentVariable(lpName, Stars); } } }
if (LengthUsed != LengthRequired - 1) { MPPrint(("Unexpected result from GetEnvironmentVariable. Length passed = %d, length used = %d (expected %d)", LengthRequired, LengthUsed, LengthRequired - 1)); Free(Buffer); return(NULL); }
return(Buffer); }
/***************************************************************************\
* FUNCTION: GetEnvironmentULong * * PURPOSE: Gets the value of an environment variable and converts it back * to its normal form. The variable should have been written * using SetEnvironmentULong. (See winlogon) * * RETURNS: TRUE on success, FALSE on failure * * HISTORY: * * 01-12-93 Davidc Created. * \***************************************************************************/
BOOL GetEnvironmentULong( LPTSTR Variable, PULONG Value ) { LPTSTR String; UNICODE_STRING UnicodeString; ANSI_STRING AnsiString; NTSTATUS Status;
String = AllocAndGetEnvironmentVariable(Variable); if (String == NULL) { return(FALSE); }
//
// Convert to ansi
//
RtlInitUnicodeString(&UnicodeString, String); Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE);
Free(String);
if (!NT_SUCCESS(Status)) { return(FALSE); }
//
// Convert to numeric value
//
if (1 != sscanf(AnsiString.Buffer, "%x", Value)) { Value = 0; }
RtlFreeAnsiString(&AnsiString);
return(TRUE); }
/***************************************************************************\
* FUNCTION: GetEnvironmentLargeInt * * PURPOSE: Gets the value of an environment variable and converts it back * to its normal form. The variable should have been written * using SetEnvironmentLargeInt. (See winlogon) * * RETURNS: TRUE on success, FALSE on failure * * HISTORY: * * 01-12-93 Davidc Created. * \***************************************************************************/
BOOL GetEnvironmentLargeInt( LPTSTR Variable, PLARGE_INTEGER Value ) { LPTSTR String; UNICODE_STRING UnicodeString; ANSI_STRING AnsiString; NTSTATUS Status;
String = AllocAndGetEnvironmentVariable(Variable); if (String == NULL) { return(FALSE); }
//
// Convert to ansi
//
RtlInitUnicodeString(&UnicodeString, String); Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE);
Free(String);
if (!NT_SUCCESS(Status)) { return(FALSE); }
//
// Convert to numeric value
//
if (2 != sscanf(AnsiString.Buffer, "%x:%x", &Value->HighPart, &Value->LowPart)) { Value->LowPart = 0; Value->HighPart = 0; }
RtlFreeAnsiString(&AnsiString);
return(TRUE); }
/***************************************************************************\
* FUNCTION: GetCommonNotifyVariables * * PURPOSE: Gets environment variables describing values common to all * notification events * * On successful return, all values should be free using Free() * * RETURNS: TRUE on success, FALSE on failure * * HISTORY: * * 01-12-93 Davidc Created. * \***************************************************************************/
BOOL GetCommonNotifyVariables( PULONG LogonFlag, PHWND hwndWinlogon, PLPTSTR StationName, PHWND StationHandle, PLPTSTR Name, PLPTSTR Domain, PLPTSTR Password, PLPTSTR OldPassword ) { BOOL Result = TRUE; ULONG OldPasswordValid;
//
// Prepare for failure
//
*hwndWinlogon = NULL; *StationName = NULL; *StationHandle = NULL; *Name = NULL; *Domain = NULL; *Password = NULL; *OldPassword = NULL;
Result = GetEnvironmentULong(MPR_WINLOGON_WINDOW_VARIABLE, (PULONG)hwndWinlogon);
if (Result) { *StationName = AllocAndGetEnvironmentVariable(MPR_STATION_NAME_VARIABLE); Result = (*StationName != NULL); } if (Result) { Result = GetEnvironmentULong(MPR_STATION_HANDLE_VARIABLE, (PULONG)StationHandle); }
if (Result) { *Name = AllocAndGetEnvironmentVariable(MPR_USERNAME_VARIABLE); // Result = (*Name != NULL);
} if (Result) { *Domain = AllocAndGetEnvironmentVariable(MPR_DOMAIN_VARIABLE); // Result = (*Domain != NULL);
} if (Result) { *Password = AllocAndGetEnvironmentVariable(MPR_PASSWORD_VARIABLE); // If the password is NULL that's ok
} if (Result) { Result = GetEnvironmentULong(MPR_OLD_PASSWORD_VALID_VARIABLE, &OldPasswordValid); } if (Result && OldPasswordValid) { *OldPassword = AllocAndGetEnvironmentVariable(MPR_OLD_PASSWORD_VARIABLE); // If the old password is NULL that's ok
} if (Result) { Result = GetEnvironmentULong(MPR_LOGON_FLAG_VARIABLE, LogonFlag); }
if (!Result) { MPPrint(("GetCommonNotifyVariables: Failed to get a variable, error = %d", GetLastError()));
//
// Free up any memory we allocated
//
if (*StationName != NULL) { Free(*StationName); } if (*Name != NULL) { Free(*Name); } if (*Domain != NULL) { Free(*Domain); } if (*Password != NULL) { ScrubString(*Password); Free(*Password); } if (*OldPassword != NULL) { ScrubString(*OldPassword); Free(*OldPassword); } }
return(Result); }
/***************************************************************************\
* FUNCTION: GetLogonNotifyVariables * * PURPOSE: Get logon specific notify data * * RETURNS: TRUE on success, FALSE on failure * * HISTORY: * * 01-12-93 Davidc Created. * \***************************************************************************/
BOOL GetLogonNotifyVariables( PLUID LogonId ) { BOOL Result;
Result = GetEnvironmentLargeInt(MPR_LOGONID_VARIABLE, (PLARGE_INTEGER) LogonId);
if (!Result) { MPPrint(("GetLogonNotifyVariables: Failed to get variable, error = %d", GetLastError())); }
return(Result); }
/***************************************************************************\
* FUNCTION: GetChangePasswordNotifyVariables * * PURPOSE: Gets change-password specific notify data * * RETURNS: TRUE on success, FALSE on failure * * HISTORY: * * 01-12-93 Davidc Created. * \***************************************************************************/
BOOL GetChangePasswordNotifyVariables( PDWORD ChangeInfo, PBOOL PassThrough, PWSTR * ProviderName ) { BOOL Result;
Result = GetEnvironmentULong(MPR_CHANGE_INFO_VARIABLE, ChangeInfo); if (Result) { Result = GetEnvironmentULong(MPR_PASSTHROUGH_VARIABLE, PassThrough); } if (Result) { *ProviderName = AllocAndGetEnvironmentVariable( MPR_PROVIDER_VARIABLE ); }
if (!Result) { MPPrint(("GetChangePasswordNotifyVariables: Failed to get variable, error = %d", GetLastError())); }
return(Result); }
/***************************************************************************\
* FUNCTION: NotifyWinlogon * * PURPOSE: Informs winlogon that credential provider notification * has completed and passes the logon scripts buffer back. * * RETURNS: TRUE on success, FALSE on failure * * HISTORY: * * 01-12-93 Davidc Created. * \***************************************************************************/
BOOL NotifyWinlogon( HWND hwndWinlogon, DWORD Error, LPTSTR MultiSz OPTIONAL ) { DWORD MultiSzSize = 0; COPYDATASTRUCT CopyData;
if (MultiSz != NULL) {
LPTSTR StringPointer = MultiSz; DWORD Length;
VerbosePrint(("NotifyWinlogon : logon scripts strings start"));
do { Length = lstrlen(StringPointer); if (Length != 0) { VerbosePrint(("<%ws>", StringPointer)); }
MultiSzSize += ((Length + 1) * sizeof(TCHAR)); StringPointer += Length + 1;
} while (Length != 0);
VerbosePrint(("NotifyWinlogon : logon scripts strings end"));
}
CopyData.dwData = Error; CopyData.cbData = MultiSzSize; CopyData.lpData = MultiSz;
SendMessage(hwndWinlogon, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&CopyData);
return(TRUE); }
DWORD NotifySpecificProvider( PWSTR Provider, LPCWSTR lpAuthentInfoType, LPVOID lpAuthentInfo, LPCWSTR lpPreviousAuthentInfoType, LPVOID lpPreviousAuthentInfo, LPWSTR lpStationName, LPVOID StationHandle, DWORD dwChangeInfo ) { HMODULE hDll; HKEY hKey; WCHAR szText[MAX_PATH]; WCHAR szPath[128]; PWSTR pszPath; DWORD dwType; DWORD dwLen; int err; PF_NPPasswordChangeNotify pFunc;
wcscpy(szText, TEXT("System\\CurrentControlSet\\Services\\") ); wcscat(szText, Provider ); wcscat(szText, TEXT("\\networkprovider") );
err = RegOpenKey( HKEY_LOCAL_MACHINE, szText, &hKey );
if ( err ) { return( err ); }
dwLen = sizeof( szPath ); pszPath = szPath;
err = RegQueryValueEx( hKey, TEXT("ProviderPath"), NULL, &dwType, (PUCHAR) pszPath, &dwLen );
if ( err ) { if ( err == ERROR_BUFFER_OVERFLOW ) {
pszPath = LocalAlloc( LMEM_FIXED, dwLen ); if (pszPath) { err = RegQueryValueEx( hKey, TEXT("ProviderPath"), NULL, &dwType, (PUCHAR) pszPath, &dwLen ); }
}
if ( err ) { RegCloseKey( hKey );
if ( pszPath != szPath ) { LocalFree( pszPath ); }
return( err ); } }
RegCloseKey( hKey );
if ( dwType == REG_EXPAND_SZ ) { ExpandEnvironmentStrings( pszPath, szText, MAX_PATH ); } else if (dwType == REG_SZ ) { wcscpy( szText, pszPath ); } else { if (pszPath != szPath) { LocalFree( pszPath ); }
return( err ); }
//
// Ok, now we have expanded the DLL where the NP code lives, and it
// is in szText. Load it, call it.
//
if ( pszPath != szPath ) { LocalFree( pszPath ); pszPath = NULL; }
hDll = LoadLibrary( szText );
if ( hDll ) { pFunc = (PF_NPPasswordChangeNotify) GetProcAddress( hDll, "NPPasswordChangeNotify" ); if ( pFunc ) { err = pFunc(lpAuthentInfoType, lpAuthentInfo, lpPreviousAuthentInfoType, lpPreviousAuthentInfo, lpStationName, StationHandle, dwChangeInfo);
}
FreeLibrary( hDll ); }
return( err );
}
/***************************************************************************\
* WinMain * * History: * 01-12-93 Davidc Created. \***************************************************************************/
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow ) { DWORD Error; BOOL Result;
ULONG LogonFlag; HWND hwndWinlogon; LPTSTR StationName; HWND StationHandle; LPTSTR Name; LPTSTR Domain; LPTSTR Password; LPTSTR OldPassword; LPTSTR LogonScripts; LPTSTR Desktop; PWSTR Provider;
LUID LogonId; DWORD ChangeInfo;
MSV1_0_INTERACTIVE_LOGON AuthenticationInfo; MSV1_0_INTERACTIVE_LOGON OldAuthenticationInfo; HDESK hDesk = NULL ; HDESK hWinlogon ;
BOOL PassThrough = FALSE;
//
// Get information describing event from environment variables
//
Result = GetCommonNotifyVariables( &LogonFlag, &hwndWinlogon, &StationName, &StationHandle, &Name, &Domain, &Password, &OldPassword); if (!Result) { MPPrint(("Failed to get common notify variables")); return(0); }
//
// Debug info
//
VerbosePrint(("LogonFlag = 0x%x", LogonFlag)); VerbosePrint(("hwndWinlogon = 0x%x", hwndWinlogon)); VerbosePrint(("Station Name = <%ws>", StationName)); VerbosePrint(("Station Handle = 0x%x", StationHandle)); VerbosePrint(("Name = <%ws>", Name)); VerbosePrint(("Domain = <%ws>", Domain)); VerbosePrint(("Password = <%ws>", Password)); VerbosePrint(("Old Password = <%ws>", OldPassword));
//
// Get the notify type specific data
//
if (LogonFlag != 0) { Result = GetLogonNotifyVariables(&LogonId); } else { Result = GetChangePasswordNotifyVariables(&ChangeInfo, &PassThrough, &Provider); }
if (!Result) { MPPrint(("Failed to get notify event type-specific variables")); return(0); }
//
// Debug info
//
if (LogonFlag != 0) { VerbosePrint(("LogonId = 0x%x:%x", LogonId.HighPart, LogonId.LowPart)); } else { VerbosePrint(("ChangeInfo = 0x%x", ChangeInfo)); VerbosePrint(("PassThrough = 0x%x", PassThrough)); }
Desktop = AllocAndGetEnvironmentVariable( MPR_DESKTOP_VARIABLE );
if ( wcscmp( Desktop, WINLOGON_DESKTOP_NAME ) ) { //
// Not supposed to use winlogon desktop. Switch ourselves to the
// current one:
//
hWinlogon = GetThreadDesktop( GetCurrentThreadId() );
if ( hWinlogon ) { hDesk = OpenInputDesktop( 0, FALSE, MAXIMUM_ALLOWED );
if ( hDesk ) { SetThreadDesktop( hDesk ); } }
}
//
// Fill in the authentication info structures
//
RtlInitUnicodeString(&AuthenticationInfo.UserName, Name); RtlInitUnicodeString(&AuthenticationInfo.LogonDomainName, Domain); RtlInitUnicodeString(&AuthenticationInfo.Password, Password);
RtlInitUnicodeString(&OldAuthenticationInfo.UserName, Name); RtlInitUnicodeString(&OldAuthenticationInfo.LogonDomainName, Domain); RtlInitUnicodeString(&OldAuthenticationInfo.Password, OldPassword);
//
// Call the appropriate notify api
//
if (LogonFlag != 0) {
Error = WNetLogonNotify( PRIMARY_AUTHENTICATOR, &LogonId, AUTHENTICATION_INFO_TYPE, &AuthenticationInfo, (OldPassword != NULL) ? AUTHENTICATION_INFO_TYPE : NULL, (OldPassword != NULL) ? &OldAuthenticationInfo : NULL, StationName, StationHandle, &LogonScripts ); if (Error != ERROR_SUCCESS) { LogonScripts = NULL; }
} else {
if (!PassThrough) { ChangeInfo |= WN_NT_PASSWORD_CHANGED; }
if (Provider) { Error = NotifySpecificProvider( Provider, AUTHENTICATION_INFO_TYPE, &AuthenticationInfo, AUTHENTICATION_INFO_TYPE, &OldAuthenticationInfo, StationName, StationHandle, ChangeInfo );
} else {
Error = WNetPasswordChangeNotify( PRIMARY_AUTHENTICATOR, AUTHENTICATION_INFO_TYPE, &AuthenticationInfo, AUTHENTICATION_INFO_TYPE, &OldAuthenticationInfo, StationName, StationHandle, ChangeInfo ); }
LogonScripts = NULL; }
if (Error != ERROR_SUCCESS) { MPPrint(("WNet%sNotify failed, error = %d", LogonFlag ? "Logon" : "PasswordChange", Error)); }
//
// Switch back if necessary
//
if ( hDesk ) { SetThreadDesktop( hWinlogon ); CloseDesktop( hWinlogon ); CloseDesktop( hDesk ); }
//
// Scrub the passwords before calling NotifyWinlogon
// because we can be killed before the cleanup below
if (Password != NULL) { ScrubString(Password); } if (OldPassword != NULL) { ScrubString(OldPassword); }
//
// Notify winlogon we completed and pass the logon script data
//
NotifyWinlogon(hwndWinlogon, Error, LogonScripts);
//
// Free up allocated data
//
if (LogonScripts != NULL) { LocalFree(LogonScripts); }
if (StationName != NULL) { Free(StationName); } if (Name != NULL) { Free(Name); } if (Domain != NULL) { Free(Domain); } if (Password != NULL) { Free(Password); } if (OldPassword != NULL) { Free(OldPassword); }
//
// We're finished
//
return(0); }
|