/****************************** 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 #include #include #include // // 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); }