/****************************** Module Header ******************************\ * Module Name: userinit.c * * Copyright (c) 1991, Microsoft Corporation * * Userinit main module * * Userinit is an app executed by winlogon at user logon. * It executes in the security context of the user and on the user desktop. * Its purpose is to complete any user initialization that may take an * indeterminate time. e.g. code that interacts with the user. * This process may be terminated at any time if a shutdown is initiated * or if the user logs off by some other means. * * History: * 20-Aug-92 Davidc Created. \***************************************************************************/ #include "userinit.h" #include "winuserp.h" #include #include #include #include #include "msgalias.h" #include "stringid.h" #include "strings.h" #include #include #include #include #include #include #include #include #include "helpmsg.h" // for HelpMessageBox /**************************************************************************** IsTSAppCompatOn() Purpose: Checks if TS application compatibility is enabled. returns TRUE if enabled, FALSE - if not enabled or on case of error. Comments: This function goes to the registry only once. All other times it just returnes the value. ****************************************************************************/ BOOL IsTSAppCompatOn(); // // Define this to enable verbose output for this module // // #define DEBUG_USERINIT #ifdef DEBUG_USERINIT #define VerbosePrint(s) UIPrint(s) #else #define VerbosePrint(s) #endif // // Define this to enable timing of userinit // //#define LOGGING #ifdef LOGGING void _WriteLog(LPCTSTR LogString); #define WriteLog(s) _WriteLog(s) #else #define WriteLog(s) #endif // // Define the environment variable names used to pass the logon // server and script name from winlogon // #define LOGON_SERVER_VARIABLE TEXT("UserInitLogonServer") #define LOGON_SCRIPT_VARIABLE TEXT("UserInitLogonScript") #define MPR_LOGON_SCRIPT_VARIABLE TEXT("UserInitMprLogonScript") #define GPO_SCRIPT_TYPE_VARIABLE TEXT("UserInitGPOScriptType") #define OPTIMIZED_LOGON_VARIABLE TEXT("UserInitOptimizedLogon") #define EVENT_SOURCE_NAME TEXT("UserInit") #define USERDOMAIN_VARIABLE TEXT("USERDOMAIN") #define UNC_LOGON_SERVER_VARIABLE TEXT("LOGONSERVER") #define AUTOENROLL_VARIABLE TEXT("UserInitAutoEnroll") #define AUTOENROLL_NONEXCLUSIVE TEXT("1") #define AUTOENROLL_EXCLUSIVE TEXT("2") #define AUTOENROLLMODE_VARIABLE TEXT("UserInitAutoEnrollMode") #define AUTOENROLL_STARTUP TEXT("1") #define AUTOENROLL_WAKEUP TEXT("2") #define SCRIPT_ZONE_CHECK_VARIABLE TEXT("SEE_MASK_NOZONECHECKS") #define SCRIPT_ZONE_CHECK_DISABLE TEXT("1") // // Define path separator // #define PATH_SEPARATOR TEXT("\\") // // Define filename extension separator // #define EXTENSION_SEPARATOR_CHAR TEXT('.') // // Define server name prefix // #define SERVER_PREFIX TEXT("\\\\") // // Define Logon script paths. // #define SERVER_SCRIPT_PATH TEXT("\\NETLOGON") #define LOCAL_SCRIPT_PATH TEXT("\\repl\\import\\scripts") #define WINLOGON_KEY TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon") #define WINLOGON_POLICY_KEY TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System") #define GPO_SCRIPTS_KEY TEXT("Software\\Policies\\Microsoft\\Windows\\System\\Scripts") #define SYNC_LOGON_SCRIPT TEXT("RunLogonScriptSync") #define SYNC_STARTUP_SCRIPT TEXT("RunStartupScriptSync") #define GRPCONV_REG_VALUE_NAME TEXT("RunGrpConv") // // We cache user preference to run logon scripts synchronously // in the machine hive so it can be checked to determine if we // can do cached logon without having to load the user's hive. // #define PROFILE_LIST_PATH L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList" TCHAR g_szGrpConvExe[] = TEXT("grpconv.exe -p"); // // Define extensions that should be added to scripts without extensions // when we go search for them. Basically this list includes those extensions // that CreateProcess handles when they are present in the executable file // name but must be provided by the caller (us) // We search for a script file with these extensions in this order and // execute the first one we find. // static LPTSTR ScriptExtensions[] = { TEXT(".bat"), TEXT(".cmd") }; // // Name of registry key and value to check for temp page file. // TCHAR szMemMan[] = TEXT("System\\CurrentControlSet\\Control\\Session Manager\\Memory Management"); TCHAR szNoPageFile[] = TEXT("TempPageFile"); // // Handle to a thread that may be created to deal with autoenrollment goo. // If this is non-null, we will wait on this thread to complete before // terminating the process. // HANDLE AutoEnrollThread ; // // Timeout in miliseconds to wait for AddMessageAlias to complete // #define TIMEOUT_VALUE (5L * 60L * 1000L) #define MAX_STRING_BYTES 512 BOOL SetupHotKeyForKeyboardLayout (); LPTSTR AllocAndGetEnvironmentVariable( LPTSTR lpName ); BOOL RunScriptHidden(HKEY hKeyRoot, LPTSTR lpValue, BOOL bDefault); BOOL RunLogonScriptSync(VOID); BOOL RunStartupScriptSync(VOID); BOOL UpdateUserEnvironment(VOID); LPWSTR GetSidString(HANDLE UserToken); VOID DeleteSidString(LPWSTR SidString); VOID UpdateUserSyncLogonScriptsCache(BOOL bSync); VOID NewLogonNotify(VOID); BOOL RunGPOScripts( LPTSTR lpGPOScriptType ); void PathUnquoteSpaces(LPTSTR lpsz); BOOL PrependToPath( IN LPTSTR lpLogonPath, OUT LPTSTR *lpOldPath ); typedef BOOL (*PFNSHELLEXECUTEEX)(LPSHELLEXECUTEINFO lpExecInfo); PFNSHELLEXECUTEEX g_pfnShellExecuteEx=NULL; // If a path is contained in quotes then remove them. void PathUnquoteSpaces(LPTSTR lpsz) { int cch; cch = lstrlen(lpsz); // Are the first and last chars quotes? if (lpsz[0] == TEXT('"') && lpsz[cch-1] == TEXT('"')) { // Yep, remove them. lpsz[cch-1] = 0; MoveMemory(lpsz, lpsz+1, (cch-1) * sizeof(TCHAR)); } } // Following function determines if the machine is a Pro or Personal machine BOOL IsPerOrProTerminalServer() { OSVERSIONINFOEX osVersion = {0}; osVersion.dwOSVersionInfoSize = sizeof(osVersion); return(GetVersionEx((OSVERSIONINFO*)&osVersion) && (osVersion.wProductType == VER_NT_WORKSTATION) && (osVersion.wSuiteMask & VER_SUITE_SINGLEUSERTS)); } // // The 3 functions below are duplicated in gptext as well // for running GPO scripts // /***************************************************************************\ * AllocAndGetEnvironmentVariable * * Version of GetEnvironmentVariable that allocates the return buffer. * * Returns pointer to environment variable or NULL on failure * * 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 <%S> not found, error = %d", lpName, GetLastError())); return(NULL); } // // Allocate a buffer to hold the variable // BytesRequired = LengthRequired * sizeof(TCHAR); Buffer = (LPTSTR) Alloc(BytesRequired); if (Buffer == NULL) { VerbosePrint(("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); if (LengthUsed == 0) { VerbosePrint(("Environment variable <%S> not found (should have found it), error = %d", lpName, GetLastError())); Free(Buffer); return(NULL); } if (LengthUsed != LengthRequired - 1) { VerbosePrint(("Unexpected result from GetEnvironmentVariable. Length passed = %d, length used = %d (expected %d)", LengthRequired, LengthUsed, LengthRequired - 1)); Free(Buffer); return(NULL); } return(Buffer); } // // Directory separator in environment strings // #define DIRECTORY_SEPARATOR TEXT(";") BOOL PrependToPath( IN LPTSTR lpLogonPath, OUT LPTSTR *lpOldPath ) { DWORD BytesRequired; LPTSTR lpNewPath; // // Prepend the address of the logon script to the path, so it can // reference other files. // *lpOldPath = AllocAndGetEnvironmentVariable( PATH ); if (*lpOldPath == NULL) { return(FALSE); } BytesRequired = ( lstrlen(lpLogonPath) + lstrlen(*lpOldPath) + 2 // one for terminator, one for ';' ) * sizeof(TCHAR); lpNewPath = (LPTSTR)Alloc(BytesRequired); if (lpNewPath == NULL) { VerbosePrint(("PrependToPath: Failed to allocate %d bytes for modified path variable", BytesRequired)); return(FALSE); } lstrcpy(lpNewPath, lpLogonPath); lstrcat(lpNewPath, DIRECTORY_SEPARATOR); lstrcat(lpNewPath, *lpOldPath); // Free( *lpOldPath ); ASSERT(((lstrlen(lpNewPath) + 1) * sizeof(TCHAR)) == BytesRequired); SetEnvironmentVariable(PATH, lpNewPath); Free(lpNewPath); return(TRUE); } // // Volatile Environment // #define VOLATILE_ENVIRONMENT TEXT("Volatile Environment") FILETIME g_LastWrite = {0,0}; typedef BOOL (WINAPI *PFNREGENERATEUSERENVIRONMENT) ( PVOID pPrevEnv, BOOL bSetCurrentEnv); // // This function checks if a volatile environment section // exists in the registry, and if so does the environment // need to be updated. // BOOL UpdateUserEnvironment (void) { PVOID pEnv; HKEY hKey; DWORD dwDisp, dwType, dwSize; BOOL bRebuildEnv = FALSE; TCHAR szClass[MAX_PATH]; DWORD cchClass, dwSubKeys, dwMaxSubKey, dwMaxClass,dwValues; DWORD dwMaxValueName, dwMaxValueData, dwSecurityDescriptor; FILETIME LastWrite; // // Attempt to open the Volatile Environment key // if (RegOpenKeyEx (HKEY_CURRENT_USER, VOLATILE_ENVIRONMENT, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { // // Query the key information for the LastWrite time. // This way we can update the environment only when // we really need to. // cchClass = MAX_PATH; if (RegQueryInfoKey(hKey, szClass, &cchClass, NULL, &dwSubKeys, &dwMaxSubKey, &dwMaxClass, &dwValues, &dwMaxValueName, &dwMaxValueData, &dwSecurityDescriptor, &LastWrite) == ERROR_SUCCESS) { // // If we haven't checked this key before, // then just store the values for next time. // if (g_LastWrite.dwLowDateTime == 0) { g_LastWrite.dwLowDateTime = LastWrite.dwLowDateTime; g_LastWrite.dwHighDateTime = LastWrite.dwHighDateTime; bRebuildEnv = TRUE; } else { // // Compare the last write times. // if (CompareFileTime (&LastWrite, &g_LastWrite) == 1) { g_LastWrite.dwLowDateTime = LastWrite.dwLowDateTime; g_LastWrite.dwHighDateTime = LastWrite.dwHighDateTime; bRebuildEnv = TRUE; } } } RegCloseKey (hKey); } // // Check if we need to rebuild the environment // if (bRebuildEnv) { HINSTANCE hInst; PFNREGENERATEUSERENVIRONMENT pRegUserEnv; hInst = LoadLibrary (TEXT("shell32.dll")); if (hInst) { pRegUserEnv = (PFNREGENERATEUSERENVIRONMENT) GetProcAddress(hInst, "RegenerateUserEnvironment"); if (pRegUserEnv) { (*pRegUserEnv) (&pEnv, TRUE); } FreeLibrary (hInst); } } return TRUE; } // returns a pointer to the arguments in a cmd type path or pointer to // NULL if no args exist // // foo.exe bar.txt -> bar.txt // foo.exe -> "" // // Spaces in filenames must be quoted. // "A long name.txt" bar.txt -> bar.txt LPTSTR GetArgs(LPCTSTR pszPath) { BOOL fInQuotes = FALSE; if (!pszPath) return NULL; while (*pszPath) { if (*pszPath == TEXT('"')) fInQuotes = !fInQuotes; else if (!fInQuotes && *pszPath == TEXT(' ')) return (LPTSTR)pszPath; pszPath = CharNext(pszPath); } return (LPTSTR)pszPath; } /***************************************************************************\ * ExecApplication * * Execs an application * * Returns TRUE on success, FALSE on failure. * * 21-Aug-92 Davidc Created. \***************************************************************************/ BOOL ExecApplication( LPTSTR pch, BOOL bFileNameOnly, BOOL bSyncApp, BOOL bShellExec, USHORT ShowState ) { BOOL Result; WCHAR Localpch[ MAX_PATH+1 ]; BOOL IsProcessExplorer = FALSE; if ( (_wcsicmp( pch, L"explorer" ) == 0) || (_wcsicmp( pch, L"explorer.exe" ) == 0 ) ) { // // Explorer.exe might not be in the right spot on the path. Let's wire // it to the right spot. // IsProcessExplorer = TRUE ; if ( ExpandEnvironmentStrings( L"%SystemRoot%\\Explorer.EXE", Localpch, MAX_PATH ) ) { pch = Localpch ; } WriteLog( TEXT("Changed explorer.exe to") ); WriteLog( pch ); } else { if ( ExpandEnvironmentStrings( pch, Localpch, MAX_PATH ) ) { pch = Localpch; } } // // Applications can be launched via ShellExecuteEx or CreateProcess // if (bShellExec) { SHELLEXECUTEINFO ExecInfo; LPTSTR lpArgs = NULL; LPTSTR lpTemp; HINSTANCE hShell32; if (!g_pfnShellExecuteEx) { Result = FALSE; hShell32 = LoadLibrary(TEXT("shell32.dll")); // this handle is not closed.. if (hShell32) { #ifdef UNICODE g_pfnShellExecuteEx = (PFNSHELLEXECUTEEX)GetProcAddress(hShell32, "ShellExecuteExW"); #else g_pfnShellExecuteEx = (PFNSHELLEXECUTEEX)GetProcAddress(hShell32, "ShellExecuteExA"); #endif if (g_pfnShellExecuteEx) { Result = TRUE; } } } else { Result = TRUE; } if (Result) { lpTemp = LocalAlloc (LPTR, (lstrlen(pch) + 1) * sizeof(TCHAR)); if (!lpTemp) { return FALSE; } lstrcpy (lpTemp, pch); if (!bFileNameOnly) { lpArgs = GetArgs (lpTemp); if (lpArgs) { if (*lpArgs) { *lpArgs = TEXT('\0'); lpArgs++; } else { lpArgs = NULL; } } } PathUnquoteSpaces(lpTemp); ZeroMemory(&ExecInfo, sizeof(ExecInfo)); ExecInfo.cbSize = sizeof(ExecInfo); ExecInfo.fMask = SEE_MASK_DOENVSUBST | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; ExecInfo.lpFile = lpTemp; ExecInfo.lpParameters = lpArgs; ExecInfo.nShow = ShowState; ExecInfo.lpVerb = TEXT("open"); Result = g_pfnShellExecuteEx (&ExecInfo); if (Result) { // // If we are running this app synchronously, wait // for it to terminate. // if (bSyncApp) { WaitForSingleObject(ExecInfo.hProcess, INFINITE); } // // Close our handles to the process and thread // CloseHandle(ExecInfo.hProcess); } LocalFree (lpTemp); } } else { STARTUPINFO si; PROCESS_INFORMATION ProcessInformation; // // Initialize process startup info // si.cb = sizeof(STARTUPINFO); si.lpReserved = pch; // This tells progman it's the shell! si.lpTitle = pch; si.lpDesktop = NULL; // Not used si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0L; si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = ShowState; si.lpReserved2 = NULL; si.cbReserved2 = 0; // // Start the app // Result = CreateProcess( bFileNameOnly ? pch : NULL, // Image name bFileNameOnly ? NULL : pch, // Command line NULL, // Default process protection NULL, // Default thread protection FALSE, // Don't inherit handles NORMAL_PRIORITY_CLASS, NULL, // Inherit environment NULL, // Inherit current directory &si, &ProcessInformation ); if (!Result) { VerbosePrint(("Failed to execute <%S>, error = %d", pch, GetLastError())); // TS : For non console sessions, a app restriting process like AppSec or SAFER might not allow explorer.exe for remote session // In this case we cannot leave a Blue screen hanging around - so we should log-off in this case // Also we want this only for Server or Advanced Server where this scenario is relevant if ( IsPerOrProTerminalServer() == FALSE) { if ((NtCurrentPeb()->SessionId != 0) && (IsProcessExplorer == TRUE)) { TCHAR Title[MAX_STRING_BYTES]; TCHAR Message[MAX_STRING_BYTES]; #if DBG DbgPrint("Userinit : TS : Failed to launch explorer.exe for a Remote Session. Doing ExitWindowsEx to logoff. \n"); #endif // Display a MessageBox saying why we log off LoadString( NULL, IDS_LOGON_FAILED, Title, MAX_STRING_BYTES ); LoadString(NULL, IDS_ERROR_SHELL_FAILED, Message, MAX_STRING_BYTES ); MessageBox(NULL, Message, Title, MB_OK); ExitWindowsEx(EWX_LOGOFF, 0); } } } else { // // If we are running this app synchronously, wait // for it to terminate. // if (bSyncApp) { WaitForSingleObject(ProcessInformation.hProcess, INFINITE); } // // Close our handles to the process and thread // CloseHandle(ProcessInformation.hProcess); CloseHandle(ProcessInformation.hThread); } } return(Result); } /***************************************************************************\ * ExecProcesses * * Read the registry for a list of system processes and start them up. * * Returns number of processes successfully started. * * 3-Mar-97 Eric Flo Rewrote \***************************************************************************/ DWORD ExecProcesses( LPTSTR pszKeyName, LPTSTR pszDefault, BOOL bMachine, BOOL bSync, // Should we wait until the process finish? BOOL bMinimize // Should we use the SW_SHOWMINNOACTIVE flag ) { LPTSTR pchData, pchCmdLine, pchT; DWORD cbCopied; DWORD dwExecuted = 0 ; HKEY hKey; DWORD dwType, dwSize = (MAX_PATH * sizeof(TCHAR)); USHORT showstate = (UINT) (bMinimize ? SW_SHOWMINNOACTIVE : SW_SHOWNORMAL); // // Alloc a buffer to work with // pchData = LocalAlloc (LPTR, dwSize); if (!pchData) { return 0; } // // Set the default value // if (pszDefault) { lstrcpy (pchData, pszDefault); } // // Check for the requested value in the registry. // if (RegOpenKeyEx ((bMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER), WINLOGON_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { RegQueryValueEx (hKey, pszKeyName, NULL, &dwType, (LPBYTE) pchData, &dwSize); RegCloseKey (hKey); } // // Check for policy override if this is a user action // if (!bMachine) { if (RegOpenKeyEx (HKEY_CURRENT_USER, WINLOGON_POLICY_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { RegQueryValueEx (hKey, pszKeyName, NULL, &dwType, (LPBYTE) pchData, &dwSize); RegCloseKey (hKey); } } // // If the command line(s) is still null, exit now. // if (*pchData == TEXT('\0')) { LocalFree(pchData); return 0; } // // Walk through the command line(s) executing the app(s) // pchCmdLine = pchT = pchData; while (*pchT) { while (*pchT && *pchT != TEXT(',')) { pchT++; } if (*pchT == ',') { *pchT = TEXT('\0'); pchT++; } // // Skip any leading spaces. // while (*pchCmdLine == TEXT(' ')) { pchCmdLine++; } // // We have something... exec this application. // if (ExecApplication(pchCmdLine, FALSE, bSync, FALSE, showstate)) { dwExecuted++; } pchCmdLine = pchT; } LocalFree(pchData); return dwExecuted ; } /***************************************************************************\ * SearchAndAllocPath * * Version of SearchPath that allocates the return string. * * Returns pointer to full path of file or NULL if not found. * * The returned buffer should be free using Free() * * History: * 09-Dec-92 Davidc Created * \***************************************************************************/ LPTSTR SearchAndAllocPath( LPTSTR lpPath, LPTSTR lpFileName, LPTSTR lpExtension, LPTSTR *lpFilePart ) { LPTSTR Buffer; DWORD LengthRequired; DWORD LengthUsed; DWORD BytesRequired; // // Allocate a buffer to hold the full filename // LengthRequired = MAX_PATH; BytesRequired = (LengthRequired * sizeof(TCHAR)); Buffer = Alloc(BytesRequired); if (Buffer == NULL) { UIPrint(("SearchAndAllocPath: Failed to allocate %d bytes for file name", BytesRequired)); return(NULL); } // // Go search for the file // LengthUsed = SearchPath( lpPath, lpFileName, lpExtension, LengthRequired, Buffer, lpFilePart); if (LengthUsed == 0) { VerbosePrint(("SearchAndAllocPath: Path <%S>, file <%S>, extension <%S> not found, error = %d", lpPath, lpFileName, lpExtension, GetLastError())); Free(Buffer); return(NULL); } if (LengthUsed > LengthRequired - 1) { UIPrint(("SearchAndAllocPath: Unexpected result from SearchPath. Length passed = %d, length used = %d (expected %d)", LengthRequired, LengthUsed, LengthRequired - 1)); Free(Buffer); return(NULL); } return(Buffer); } BOOL DisableScriptZoneSecurityCheck() { BOOL bSucceeded; // // To make the shell skip the zone security check for launching scripts, we use // a special environment variable honored by the shell for this purpose and // set it to a specific value // bSucceeded = SetEnvironmentVariable(SCRIPT_ZONE_CHECK_VARIABLE, SCRIPT_ZONE_CHECK_DISABLE); return bSucceeded; } BOOL EnableScriptZoneSecurityCheck() { BOOL bSucceeded; // // Clear the environment variable that disables the security check // bSucceeded = SetEnvironmentVariable(SCRIPT_ZONE_CHECK_VARIABLE, NULL); if ( ! bSucceeded ) { // // If we failed to clear it, it may be that this is because the // environment variable wasn't set in the first place, in which // case we can ignore the error since we are in the desired state // LONG Status = GetLastError(); if ( ERROR_ENVVAR_NOT_FOUND == Status ) { bSucceeded = TRUE; } } return bSucceeded; } /***************************************************************************\ * ExecScript * * Attempts to run the command script or exe lpScript in the directory lpPath. * If path is not specified then the default windows search path is used. * * This routine is basically a wrapper for CreateProcess. CreateProcess always * assumes a .exe extension for files without extensions. It will run .cmd * and .bat files but it keys off the .cmd and .bat extension. So we must go * search for the file first and add the extension before calling CreateProcess. * * Returns TRUE if the script began executing successfully. * Returns FALSE if we can't find the script in the path specified * or something fails. * * History: * 09-Dec-92 Davidc Created * \***************************************************************************/ BOOL ExecScript( LPTSTR lpPath OPTIONAL, LPTSTR lpScript, BOOL bSyncApp, BOOL bShellExec ) { BOOL Result; DWORD i; USHORT uFlags; LPTSTR lpFullName; DWORD BytesRequired; // // First try and execute the raw script file name // if (lpPath != NULL) { BytesRequired = (lstrlen(lpPath) + lstrlen(PATH_SEPARATOR) + lstrlen(lpScript) + 1) * sizeof(TCHAR); lpFullName = Alloc(BytesRequired); if (lpFullName == NULL) { UIPrint(("ExecScript failed to allocate %d bytes for full script name", BytesRequired)); return(FALSE); } lstrcpy(lpFullName, lpPath); lstrcat(lpFullName, PATH_SEPARATOR); lstrcat(lpFullName, lpScript); ASSERT(((lstrlen(lpFullName) + 1) * sizeof(TCHAR)) == BytesRequired); } else { lpFullName = lpScript; } uFlags = SW_SHOWNORMAL; if (!bSyncApp) { uFlags |= SW_SHOWMINNOACTIVE; } if (RunScriptHidden(HKEY_CURRENT_USER, TEXT("HideLegacyLogonScripts"), FALSE)) { uFlags = SW_HIDE; } // // Let CreateProcess have a hack at the raw script path and name. // Result = ExecApplication(lpFullName, FALSE, bSyncApp, bShellExec, uFlags); // // Free up the full name buffer // if (lpFullName != lpScript) { Free(lpFullName); } if (!Result) { // // Create process couldn't find it so add each script extension in // turn and try and execute the full script name. // // Only bother with this procedure if the script name doesn't // already contain an extension // BOOL ExtensionPresent = FALSE; LPTSTR p = lpScript; while (*p) { if (*p == EXTENSION_SEPARATOR_CHAR) { ExtensionPresent = TRUE; break; } p = CharNext(p); } if (ExtensionPresent) { VerbosePrint(("ExecScript: Skipping search path because script name contains extension")); } else { for (i = 0; i < sizeof(ScriptExtensions)/sizeof(ScriptExtensions[0]); i++) { lpFullName = SearchAndAllocPath( lpPath, lpScript, ScriptExtensions[i], NULL); if (lpFullName != NULL) { // // We found the file, go execute it // Result = ExecApplication(lpFullName, FALSE, bSyncApp, bShellExec, uFlags); // // Free the full path buffer // Free(lpFullName); return(Result); } } } } return(Result); } BOOL RunScriptHidden(HKEY hKeyRoot, LPTSTR lpValue, BOOL bDefault) { BOOL bResult; HKEY hKey; DWORD dwType, dwSize; // // Set the default // bResult = bDefault; // // Check for a preference // if (RegOpenKeyEx (hKeyRoot, WINLOGON_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { dwSize = sizeof(bResult); RegQueryValueEx (hKey, lpValue, NULL, &dwType, (LPBYTE) &bResult, &dwSize); RegCloseKey (hKey); } // // Check for a policy // if (RegOpenKeyEx (hKeyRoot, WINLOGON_POLICY_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { dwSize = sizeof(bResult); RegQueryValueEx (hKey, lpValue, NULL, &dwType, (LPBYTE) &bResult, &dwSize); RegCloseKey (hKey); } return bResult; } /***************************************************************************\ * RunLogonScript * * Starts the logon script * * Returns TRUE on success, FALSE on failure * * History: * 21-Aug-92 Davidc Created * \***************************************************************************/ BOOL RunLogonScript( LPTSTR lpLogonServer OPTIONAL, LPTSTR lpLogonScript, BOOL bSyncApp, BOOL bShellExec ) { LPTSTR lpLogonPath; LPTSTR lpOldPath; DWORD BytesRequired; BOOL Result; WIN32_FILE_ATTRIBUTE_DATA fad; if (!lpLogonScript) { return TRUE; } // // if the logon server exists, look for the logon scripts on // \\\NETLOGON\ // if ((lpLogonServer != NULL) && (lpLogonServer[0] != 0)) { BytesRequired = ( lstrlen(SERVER_PREFIX) + lstrlen(lpLogonServer) + lstrlen(SERVER_SCRIPT_PATH) + 1 ) * sizeof(TCHAR); lpLogonPath = (LPTSTR)Alloc(BytesRequired); if (lpLogonPath == NULL) { UIPrint(("RunLogonScript: Failed to allocate %d bytes for remote logon script path", BytesRequired)); return(FALSE); } lstrcpy(lpLogonPath, SERVER_PREFIX); lstrcat(lpLogonPath, lpLogonServer); lstrcat(lpLogonPath, SERVER_SCRIPT_PATH); if (GetFileAttributesEx (lpLogonPath, GetFileExInfoStandard, &fad)) { Result = PrependToPath( lpLogonPath, &lpOldPath ); if (Result) { VerbosePrint(("Successfully prepended <%S> to path", lpLogonPath)); } else { VerbosePrint(("Cannot prepend <%S> path.",lpLogonPath)); } // // Try and execute the app/script specified by lpLogonScript // in the directory specified by lpLogonPath // Result = ExecScript(lpLogonPath, lpLogonScript, bSyncApp, bShellExec); if (Result) { VerbosePrint(("Successfully executed logon script <%S> in directory <%S>", lpLogonScript, lpLogonPath)); } else { VerbosePrint(("Cannot start logon script <%S> on LogonServer <%S>. Trying local path.", lpLogonScript, lpLogonServer)); } // // Put the path back the way it was // SetEnvironmentVariable(PATH, lpOldPath); Free(lpOldPath); } else { Result = FALSE; } // // Free up the buffer // Free(lpLogonPath); // // If the script started successfully we're done, otherwise // drop through and try to find the script locally // if (Result) { if (bSyncApp) { // // Check that the volatile environment hasn't changed. // UpdateUserEnvironment(); } return(TRUE); } } // // Try to find the scripts on \repl\import\scripts\ // BytesRequired = GetSystemDirectory(NULL, 0) * sizeof(TCHAR); if (BytesRequired == 0) { UIPrint(("RunLogonScript: GetSystemDirectory failed, error = %d", GetLastError())); return(FALSE); } BytesRequired += ( lstrlen(LOCAL_SCRIPT_PATH) // BytesRequired already includes space for terminator ) * sizeof(TCHAR); lpLogonPath = (LPTSTR)Alloc(BytesRequired); if (lpLogonPath == NULL) { UIPrint(("RunLogonScript failed to allocate %d bytes for logon script path", BytesRequired)); return(FALSE); } Result = FALSE; if (GetSystemDirectory(lpLogonPath, BytesRequired)) { lstrcat(lpLogonPath, LOCAL_SCRIPT_PATH); ASSERT(((lstrlen(lpLogonPath) + 1) * sizeof(TCHAR)) == BytesRequired); Result = PrependToPath( lpLogonPath, &lpOldPath ); if (Result) { VerbosePrint(("Successfully prepended <%S> to path", lpLogonPath)); } else { VerbosePrint(("Cannot prepend <%S> path.",lpLogonPath)); } // // Try and execute the app/script specified by lpLogonScript // in the directory specified by lpLogonPath // Result = ExecScript(lpLogonPath, lpLogonScript, bSyncApp, bShellExec); if (Result) { VerbosePrint(("Successfully executed logon script <%S> in directory <%S>", lpLogonScript, lpLogonPath)); } else { VerbosePrint(("Cannot start logon script <%S> on local path <%S>.", lpLogonScript, lpLogonPath)); } // // Put the path back the way it was // SetEnvironmentVariable(PATH, lpOldPath); Free(lpOldPath); } else { UIPrint(("RunLogonScript: GetSystemDirectory failed, error = %d", GetLastError())); } // // Free up the buffer // Free(lpLogonPath); // // Check that the volatile environment hasn't changed. // if (Result && bSyncApp) { UpdateUserEnvironment(); } return(Result); } #define SCR_STARTUP L"Startup" #define SCR_SHUTDOWN L"Shutdown" #define SCR_LOGON L"Logon" #define SCR_LOGOFF L"Logoff" DWORD ScrExecGPOListFromReg( LPWSTR szType, BOOL bMachine, BOOL bSync, BOOL bHidden, BOOL bRunMin, HANDLE hEventLog ); BOOL RunGPOScripts( LPTSTR lpGPOScriptType ) { HKEY hKeyScripts; HKEY hKeyRoot; BOOL bSync = TRUE; BOOL bRunMin = TRUE; BOOL bHide; HANDLE hEventLog = NULL; BOOL bMachine; BOOL bResult; DWORD dwError; // // Ensure that the shell's checks for ie zones are disabled // since this script is trusted by an administrator to execute // bResult = DisableScriptZoneSecurityCheck(); if ( ! bResult ) { goto RunGPOScripts_exit; } // // Register with Event Log // hEventLog = RegisterEventSource( 0, EVENT_SOURCE_NAME ); // // Preliminary work to see if the scripts should be // run sync or async and to decide what the appropriate // root key is // if (!lstrcmpi (lpGPOScriptType, SCR_LOGON )) { hKeyRoot = HKEY_CURRENT_USER; bHide = RunScriptHidden(hKeyRoot, TEXT("HideLogonScripts"), TRUE); bSync = RunLogonScriptSync(); bMachine = FALSE; if (bSync && !bHide) { bRunMin = FALSE; } } else if (!lstrcmpi (lpGPOScriptType, SCR_LOGOFF )) { hKeyRoot = HKEY_CURRENT_USER; bHide = RunScriptHidden(hKeyRoot, TEXT("HideLogoffScripts"), TRUE); bMachine = FALSE; if (!bHide) { bRunMin = FALSE; } } else if (!lstrcmpi (lpGPOScriptType, SCR_STARTUP )) { hKeyRoot = HKEY_LOCAL_MACHINE; bHide = RunScriptHidden(hKeyRoot, TEXT("HideStartupScripts"), TRUE); bSync = RunStartupScriptSync(); bMachine = TRUE; if (bSync && !bHide) { bRunMin = FALSE; } } else if (!lstrcmpi (lpGPOScriptType, SCR_SHUTDOWN )) { hKeyRoot = HKEY_LOCAL_MACHINE; bHide = RunScriptHidden(hKeyRoot, TEXT("HideShutdownScripts"), TRUE); bMachine = TRUE; if (!bHide) { bRunMin = FALSE; } } else { return FALSE; } dwError = ScrExecGPOListFromReg(lpGPOScriptType, bMachine, bSync, bHide, bRunMin, hEventLog ); bResult = ( dwError == ERROR_SUCCESS ); RunGPOScripts_exit: if (hEventLog) { DeregisterEventSource(hEventLog); } return bResult; } /***************************************************************************\ * RunMprLogonScripts * * Starts the network provider logon scripts * The passed string is a multi-sz - we exec each script in turn. * * Returns TRUE on success, FALSE on failure * * History: * 21-Aug-92 Davidc Created * \***************************************************************************/ BOOL RunMprLogonScripts( LPTSTR lpLogonScripts, BOOL bSyncApp ) { BOOL Result; if (lpLogonScripts != NULL) { DWORD Length; do { Length = lstrlen(lpLogonScripts); if (Length != 0) { Result = ExecScript(NULL, lpLogonScripts, bSyncApp, FALSE); if (Result) { VerbosePrint(("Successfully executed mpr logon script <%S>", lpLogonScripts)); if (bSyncApp) { // // Check that the volatile environment hasn't changed. // UpdateUserEnvironment(); } } else { VerbosePrint(("Cannot start mpr logon script <%S>", lpLogonScripts)); } } lpLogonScripts += (Length + 1); } while (Length != 0); } return(TRUE); } /***************************************************************************\ * AllocAndGetEnvironmentMultiSz * * Gets an environment variable's value that's assumed to be an * encoded multi-sz and decodes it into an allocated return buffer. * Variable should have been written with SetEnvironmentMultiSz() (winlogon) * * Returns pointer to environment variable or NULL on failure * * The returned buffer should be free using Free() * * History: * 01-15-93 Davidc Created * \***************************************************************************/ #define TERMINATOR_REPLACEMENT TEXT(',') LPTSTR AllocAndGetEnvironmentMultiSz( LPTSTR lpName ) { LPTSTR Buffer; LPTSTR p, q; Buffer = AllocAndGetEnvironmentVariable(lpName); if (Buffer == NULL) { return(NULL); } // // Now decode the string - we can do this in place since the string // will always get smaller // p = Buffer; q = Buffer; while (*p) { if (*p == TERMINATOR_REPLACEMENT) { p ++; if (*p != TERMINATOR_REPLACEMENT) { p --; *p = 0; } } if (p != q) { *q = *p; } p ++; q ++; } ASSERT(q <= p); // // Copy terminator // if (q != p) { *q = 0; } return(Buffer); } /***************************************************************************\ * CheckVideoSelection * * History: * 15-Mar-93 Andreva Created. \***************************************************************************/ VOID CheckVideoSelection( HINSTANCE hInstance ) { // // First check if we are in a detection mode. // If we are, spawn the applet and let the user pick the mode. // // Otherwise, check to see if the display was initialized properly. // We may want to move this to a more appropriate place at a later date. // // Andreva // NTSTATUS Status; HANDLE HkRegistry; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UnicodeString; TCHAR achDispMode[512]; TCHAR achDisp[512]; TCHAR achExec[MAX_PATH]; DWORD Mesg = 0; LPTSTR psz = NULL; DWORD cb, dwType; DWORD data; if ( NtCurrentPeb()->SessionId != 0 ) { // Only do this for Console return; } // // Check for a new driver installation // RtlInitUnicodeString(&UnicodeString, L"\\Registry\\Machine\\System\\CurrentControlSet" L"\\Control\\GraphicsDrivers\\DetectDisplay"); InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&HkRegistry, GENERIC_READ | GENERIC_WRITE | DELETE, &ObjectAttributes); if (!NT_SUCCESS(Status)) { // // Check for a new driver installation // RtlInitUnicodeString(&UnicodeString, L"\\Registry\\Machine\\System\\CurrentControlSet" L"\\Control\\GraphicsDrivers\\NewDisplay"); InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&HkRegistry, GENERIC_READ | GENERIC_WRITE | DELETE, &ObjectAttributes); if (!NT_SUCCESS(Status)) { // // Check for an invalid driver (like a 3.51 driver) or a badly // configured driver. // RtlInitUnicodeString(&UnicodeString, L"\\Registry\\Machine\\System\\CurrentControlSet" L"\\Control\\GraphicsDrivers\\InvalidDisplay"); InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&HkRegistry, GENERIC_READ | GENERIC_WRITE | DELETE, &ObjectAttributes); } } // // If any of the the error keys were opened successfully, then close the // key and spawn the applet (we only delete the invalid display key, not // the DetectDisplay key !) // if (NT_SUCCESS(Status)) { NtClose(HkRegistry); LoadString(hInstance, IDS_DISPLAYAPPLET, achExec, sizeof(achExec) / sizeof( TCHAR )); ExecApplication(achExec, FALSE, TRUE, FALSE, SW_SHOWNORMAL); } } /***************************************************************************\ * InitializeMisc * * History: * 14-Jul-95 EricFlo Created. \***************************************************************************/ void InitializeMisc (HINSTANCE hInstance) { HKEY hkeyMM; DWORD dwTempFile, cbTempFile, dwType; TCHAR achExec[MAX_PATH]; // // check the page file. If there is not one, then spawn the vm applet // if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szMemMan, 0, KEY_READ, &hkeyMM) == ERROR_SUCCESS) { cbTempFile = sizeof(dwTempFile); if (RegQueryValueEx (hkeyMM, szNoPageFile, NULL, &dwType, (LPBYTE) &dwTempFile, &cbTempFile) != ERROR_SUCCESS || dwType != REG_DWORD || cbTempFile != sizeof(dwTempFile)) { dwTempFile = 0; } RegCloseKey(hkeyMM); } else dwTempFile = 0; if (dwTempFile == 1) { LoadString(hInstance, IDS_VMAPPLET, achExec, sizeof(achExec) / sizeof( TCHAR )); ExecProcesses(TEXT("vmapplet"), achExec, TRUE, FALSE, TRUE); } // // Tell the user if he has an invalid video selection. // CheckVideoSelection(hInstance); // // Notify other system components that a new // user has logged into the workstation. // NewLogonNotify(); } #ifdef LOGGING #define DATEFORMAT TEXT("%d-%d %.2d:%.2d:%.2d:%.3d ") /***************************************************************************\ * _WriteLog * * History: * 22-Mar-93 Robertre Created. \***************************************************************************/ void _WriteLog( LPCTSTR LogString ) { TCHAR Buffer[MAX_PATH]; SYSTEMTIME st; TCHAR FormatString[MAX_PATH]; lstrcpy( FormatString, DATEFORMAT ); lstrcat( FormatString, LogString ); lstrcat( FormatString, TEXT("\r\n") ); GetLocalTime( &st ); // // Construct the message // wsprintf( Buffer, FormatString, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds ); OutputDebugString (Buffer); } #endif DWORD WINAPI AddToMessageAlias( PVOID params ) /***************************************************************************\ * AddToMessageAlias * * History: * 10-Apr-93 Robertre Created. \***************************************************************************/ { HANDLE hShellReadyEvent; WCHAR UserName[MAX_PATH + 1]; DWORD UserNameLength = sizeof(UserName) / sizeof(*UserName); DWORD dwCount; BOOL standardShellWasStarted = *(BOOL *)params; // // Add the user's msg alias. // WriteLog(TEXT("Userinit: Adding MsgAlias")); if (GetUserNameW(UserName, &UserNameLength)) { AddMsgAlias(UserName); } else { UIPrint(("GetUserName failed, error = %d",GetLastError())); } WriteLog( TEXT("Userinit: Finished adding MsgAlias")); if (standardShellWasStarted ) { dwCount = 0; while (TRUE) { hShellReadyEvent = OpenEvent(EVENT_ALL_ACCESS,FALSE,L"ShellReadyEvent"); if (hShellReadyEvent == NULL) { if (GetLastError() == ERROR_FILE_NOT_FOUND) { if (dwCount < 5) { Sleep (3000); dwCount++; } else { break; } } else { break; } } else { WaitForSingleObject(hShellReadyEvent, INFINITE); Sleep(20000); CloseHandle(hShellReadyEvent); break; } } } SpoolerInit(); return( NO_ERROR ); } BOOL StartTheShell( void ) /***************************************************************************\ * StartTheShell * * Starts the shell, either explorer, the shell value specified in * the registry for winlogon, or the alternate shell that is specified * by the safeboot procedure. * * retrun * TRUE if the standard shell was executed * FALSE if a non-standard shell was executed. * * 14-Jan-98 WesW Created. \***************************************************************************/ { HKEY hKey; DWORD dwSize, dwType; WCHAR ShellCmdLine[MAX_PATH]; DWORD UseAlternateShell = 0; // // get the safeboot mode // if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("system\\currentcontrolset\\control\\safeboot\\option"), 0, KEY_READ, &hKey ) == ERROR_SUCCESS) { dwSize = sizeof(DWORD); RegQueryValueEx ( hKey, TEXT("UseAlternateShell"), NULL, &dwType, (LPBYTE) &UseAlternateShell, &dwSize ); RegCloseKey( hKey ); if (UseAlternateShell) { if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("system\\currentcontrolset\\control\\safeboot"), 0, KEY_READ, &hKey ) == ERROR_SUCCESS) { dwSize = sizeof(ShellCmdLine); if (RegQueryValueEx ( hKey, TEXT("AlternateShell"), NULL, &dwType, (LPBYTE) ShellCmdLine, &dwSize ) != ERROR_SUCCESS || ShellCmdLine[0] == 0) { UseAlternateShell = 0; } RegCloseKey( hKey ); } else { UseAlternateShell = 0; } } } // // Before we start the shell, we must re-enable the shell's script // zone security checks -- if we can't do this, it is not safe // to start the shell since it may allow the user to run // unsafe code without notification. // if ( ! EnableScriptZoneSecurityCheck() ) { // // We have to exit, and return TRUE which means that we failed to start // the standard shell. We do this even if an alternate shell was desired since // whenever the alternate shell fails to launch for some other reason, // we try to launch explorer.exe and would return TRUE in that case. // return TRUE; } if (IsTSAppCompatOn()) { if ( !ExecProcesses(TEXT("AppSetup"), NULL, FALSE, TRUE, TRUE ) ) { ExecProcesses(TEXT("AppSetup"), NULL, TRUE, TRUE, TRUE); } } if (UseAlternateShell) { if (ExecApplication(ShellCmdLine, FALSE, FALSE, FALSE, SW_MAXIMIZE)) { return FALSE; // an alt-shell was executed } } else if (NtCurrentPeb()->SessionId != 0) { // // Terminal Server: For remote sessions query the Terminal Server service // to see if this session has specified a initial program other than // explorer.exe. // BOOLEAN bExecOk = TRUE; BOOLEAN IsWorkingDirWrong = FALSE; UINT ErrorStringId; LPTSTR psz = NULL; LPTSTR pszerr; ULONG Length; BOOLEAN Result; HANDLE dllHandle; // // Load winsta.dll // dllHandle = LoadLibraryW(L"winsta.dll"); if (dllHandle) { WINSTATIONCONFIG *pConfigData = LocalAlloc(LPTR, sizeof(WINSTATIONCONFIG)); if (pConfigData) { PWINSTATION_QUERY_INFORMATION pfnWinstationQueryInformation; pfnWinstationQueryInformation = (PWINSTATION_QUERY_INFORMATION) GetProcAddress( dllHandle, "WinStationQueryInformationW" ); if (pfnWinstationQueryInformation) { Result = pfnWinstationQueryInformation( SERVERNAME_CURRENT, LOGONID_CURRENT, WinStationConfiguration, pConfigData, sizeof(WINSTATIONCONFIG), &Length ); if (Result && pConfigData->User.InitialProgram[0] ) { //BUGID - 342176 if( !ExpandEnvironmentStrings( pConfigData->User.InitialProgram, ShellCmdLine, MAX_PATH ) ) { wcscpy( ShellCmdLine, pConfigData->User.InitialProgram ); } // // If a working directory is specified, // then attempt to change the current directory to it. // if ( pConfigData->User.WorkDirectory[0] ) { WCHAR WorkDirectory[ DIRECTORY_LENGTH + 1 ]; if ( !ExpandEnvironmentStrings( pConfigData->User.WorkDirectory, WorkDirectory, DIRECTORY_LENGTH + 1 ) ) { wcscpy( WorkDirectory, pConfigData->User.WorkDirectory ); } bExecOk = (BYTE) SetCurrentDirectory( WorkDirectory ); } pszerr = pConfigData->User.WorkDirectory; if ( !bExecOk ) { DbgPrint( "USERINIT: Failed to set working directory %ws for SessionId %u\n", pConfigData->User.WorkDirectory, NtCurrentPeb()->SessionId ); IsWorkingDirWrong = TRUE; goto badexec; } bExecOk = (BYTE)ExecApplication( ShellCmdLine, FALSE, FALSE, FALSE,(USHORT)(pConfigData->User.fMaximize ? SW_SHOWMAXIMIZED : SW_SHOW) ); pszerr = ShellCmdLine; badexec: if ( !bExecOk ) { DWORD rc; BOOL bGotString; #define PSZ_MAX 256 WCHAR pszTemplate[PSZ_MAX]; LPTSTR errbuf = NULL; rc = GetLastError(); FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, rc, 0, (LPTSTR) (&psz), 1, NULL); if (psz) { if (IsWorkingDirWrong == TRUE) { ErrorStringId = IDS_FAILING_WORKINGDIR; } else { ErrorStringId = IDS_FAILING_SHELLCOMMAND; } bGotString = LoadString(NULL,ErrorStringId,pszTemplate,PSZ_MAX); if (bGotString) { errbuf = LocalAlloc(LPTR, 512 * sizeof(TCHAR)); if (errbuf) { wsprintf( errbuf, pszTemplate, psz, pszerr ); } } LocalFree(psz); } else { if (IsWorkingDirWrong == TRUE) { ErrorStringId = IDS_ERROR_WORKINGDIR; } else { ErrorStringId = IDS_ERROR_SHELLCOMMAND; } bGotString = LoadString(NULL,ErrorStringId,pszTemplate,PSZ_MAX); if (bGotString) { errbuf = LocalAlloc(LPTR, 512 * sizeof(WCHAR)); if (errbuf) { wsprintf( errbuf, pszTemplate, rc, pszerr ); } } } if (bGotString && errbuf) { HelpMessageBox(NULL, NULL, errbuf, NULL, MB_OK | MB_ICONSTOP | MB_HELP, TEXT("MS-ITS:rdesktop.chm::/rdesktop_troubleshoot.htm")); LocalFree(errbuf); } } LocalFree(pConfigData); FreeLibrary(dllHandle); // an alt shell/program was executed return FALSE ; } } LocalFree(pConfigData); } // if pConfigData FreeLibrary(dllHandle); } } if (!ExecProcesses(TEXT("shell"), NULL, FALSE, FALSE, FALSE)) { ExecProcesses(TEXT("shell"), TEXT("explorer"), TRUE, FALSE, FALSE); } return TRUE; // standard shell/explorer was executed } VOID DoAutoEnrollment( LPTSTR Param ) { if (0==wcscmp(Param, AUTOENROLL_STARTUP)) { AutoEnrollThread = CertAutoEnrollment( GetDesktopWindow(), CERT_AUTO_ENROLLMENT_START_UP ); } else { AutoEnrollThread = CertAutoEnrollment( GetDesktopWindow(), CERT_AUTO_ENROLLMENT_WAKE_UP ); } } /***************************************************************************\ * WinMain * * History: * 20-Aug-92 Davidc Created. \***************************************************************************/ typedef BOOL (WINAPI * PFNIMMDISABLEIME)( DWORD ); int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow ) { LPTSTR lpLogonServer; LPTSTR lpOriginalUNCLogonServer; LPTSTR lpLogonScript; LPTSTR lpMprLogonScripts; LPTSTR lpGPOScriptType; LPTSTR lpAutoEnroll; LPTSTR lpAutoEnrollMode; DWORD ThreadId; DWORD WaitResult; HANDLE ThreadHandle; BOOL bRunLogonScriptsSync; BOOL bRunGrpConv = FALSE; HKEY hKey; DWORD dwType, dwSize, dwTemp; TCHAR szCmdLine[50]; BOOL standardShellWasStarted; HANDLE hImm = 0; PFNIMMDISABLEIME pfnImmDisableIME = 0; BOOL OptimizedLogon; LPTSTR OptimizedLogonStatus; WriteLog(TEXT("Userinit: Starting")); if ( GetSystemMetrics( SM_IMMENABLED ) ) { hImm = LoadLibrary( L"imm32.dll"); if ( hImm ) { pfnImmDisableIME = (PFNIMMDISABLEIME) GetProcAddress( hImm, "ImmDisableIME" ); if ( pfnImmDisableIME ) { pfnImmDisableIME( -1 ); } } } // // Determine if we did an optimized logon. By default assume we did not. // OptimizedLogon = FALSE; OptimizedLogonStatus = AllocAndGetEnvironmentVariable(OPTIMIZED_LOGON_VARIABLE); if (OptimizedLogonStatus) { if (lstrcmp(OptimizedLogonStatus, TEXT("1")) == 0) { OptimizedLogon = TRUE; } Free(OptimizedLogonStatus); } SetEnvironmentVariable(OPTIMIZED_LOGON_VARIABLE, NULL); // // Check if userinit is being started to just run GPO scripts // lpGPOScriptType = AllocAndGetEnvironmentVariable(GPO_SCRIPT_TYPE_VARIABLE); // // Check if userinit.exe is being run just for auto enrollment // lpAutoEnroll = AllocAndGetEnvironmentVariable( AUTOENROLL_VARIABLE ); lpAutoEnrollMode = AllocAndGetEnvironmentVariable( AUTOENROLLMODE_VARIABLE ); SetEnvironmentVariable(AUTOENROLL_VARIABLE, NULL); if (lpGPOScriptType) { // // Userinit was started to execute GPO scripts only // // Clean up the environment block // SetEnvironmentVariable(GPO_SCRIPT_TYPE_VARIABLE, NULL); // // Execute the scripts and clean up // RunGPOScripts (lpGPOScriptType); Free(lpGPOScriptType); // // We're finished. Exit now. // if ( lpAutoEnroll == NULL ) { goto Exit ; } } if ( lpAutoEnroll ) { if ( ( wcscmp( lpAutoEnroll, AUTOENROLL_NONEXCLUSIVE ) == 0 ) || ( wcscmp( lpAutoEnroll, AUTOENROLL_EXCLUSIVE ) == 0 ) ) { DoAutoEnrollment( lpAutoEnrollMode ); if ( wcscmp( lpAutoEnroll, AUTOENROLL_EXCLUSIVE ) == 0 ) { goto Exit; } } } // // Check if grpconv.exe needs to be run // if (RegOpenKeyEx (HKEY_CURRENT_USER, WINLOGON_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { // // Check for the sync flag. // dwSize = sizeof(bRunGrpConv); RegQueryValueEx (hKey, GRPCONV_REG_VALUE_NAME, NULL, &dwType, (LPBYTE) &bRunGrpConv, &dwSize); RegCloseKey (hKey); } // // Run grpconv.exe if requested // if (bRunGrpConv) { WriteLog(TEXT("Userinit: Running grpconv.exe")); ExecApplication(g_szGrpConvExe, FALSE, TRUE, FALSE, SW_SHOWNORMAL); } // // Get the logon script environment variables // lpLogonServer = AllocAndGetEnvironmentVariable(LOGON_SERVER_VARIABLE); lpLogonScript = AllocAndGetEnvironmentVariable(LOGON_SCRIPT_VARIABLE); lpMprLogonScripts = AllocAndGetEnvironmentMultiSz(MPR_LOGON_SCRIPT_VARIABLE); // // Delete the logon script environment variables // SetEnvironmentVariable(LOGON_SERVER_VARIABLE, NULL); SetEnvironmentVariable(LOGON_SCRIPT_VARIABLE, NULL); SetEnvironmentVariable(MPR_LOGON_SCRIPT_VARIABLE, NULL); // // See if logon scripts are to be run sync or async // bRunLogonScriptsSync = RunLogonScriptSync(); SetupHotKeyForKeyboardLayout(); // // For application server see if we hve any .ini file/registry sync'ing to do //We should do it before we start running logon scripts! // //First Check if Application compatibility is on // if (IsTSAppCompatOn()) { HANDLE dllHandle; if (lpMprLogonScripts) { //Force to run logon script sync when a provider logon script exists when the system //is a terminal server. This is because of the global flag on the registry // doesn't work when two interactive users logon at the same time. bRunLogonScriptsSync = TRUE; } // // Load tsappcmp.dll // dllHandle = LoadLibrary (TEXT("tsappcmp.dll")); if (dllHandle) { PTERMSRCHECKNEWINIFILES pfnTermsrvCheckNewIniFiles; pfnTermsrvCheckNewIniFiles = (PTERMSRCHECKNEWINIFILES) GetProcAddress( dllHandle, "TermsrvCheckNewIniFiles" ); if (pfnTermsrvCheckNewIniFiles) { pfnTermsrvCheckNewIniFiles(); } FreeLibrary(dllHandle); } } // // If logon scripts can be run async then start the shell first. // if (bRunLogonScriptsSync) { // // Disable the shell's ie zone checking for the processes we // are starting along with all their child processes // (void) DisableScriptZoneSecurityCheck(); RunLogonScript(lpLogonServer, lpLogonScript, bRunLogonScriptsSync, TRUE); RunMprLogonScripts(lpMprLogonScripts, bRunLogonScriptsSync); standardShellWasStarted = StartTheShell(); } else { WriteLog(TEXT("Userinit: Starting the shell")); standardShellWasStarted = StartTheShell(); (void) DisableScriptZoneSecurityCheck(); RunLogonScript(lpLogonServer, lpLogonScript, bRunLogonScriptsSync, TRUE); RunMprLogonScripts(lpMprLogonScripts, bRunLogonScriptsSync); } UpdateUserSyncLogonScriptsCache(bRunLogonScriptsSync); // // Free up the buffers // Free(lpLogonServer); Free(lpLogonScript); Free(lpMprLogonScripts); // // Lower our priority so the shell can start faster // SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_LOWEST); // // Load remote fonts // LoadRemoteFonts(); // // Initialize misc stuff // InitializeMisc (hInstance); ThreadHandle = CreateThread( NULL, 0, AddToMessageAlias, &standardShellWasStarted, 0, &ThreadId ); WaitResult = WaitForSingleObject( ThreadHandle, TIMEOUT_VALUE ); if ( WaitResult == WAIT_TIMEOUT ) { // // This may never come back, so kill it. // UIPrint(("UserInit: AddToMessageAlias timeout, terminating thread\n")); } CloseHandle( ThreadHandle ); // // If appropriate, start proquota.exe // if (RegOpenKeyEx (HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"), 0, KEY_READ, &hKey) == ERROR_SUCCESS) { dwTemp = 0; dwSize = sizeof(dwTemp); RegQueryValueEx (hKey, TEXT("EnableProfileQuota"), NULL, &dwType, (LPBYTE) &dwTemp, &dwSize); if (dwTemp) { lstrcpy (szCmdLine, TEXT("proquota.exe")); ExecApplication(szCmdLine, FALSE, FALSE, FALSE, SW_SHOWNORMAL); } RegCloseKey (hKey); } Exit: if ( AutoEnrollThread ) { WaitResult = WaitForSingleObject( AutoEnrollThread, INFINITE ); CloseHandle( AutoEnrollThread ); AutoEnrollThread = NULL ; } Free(lpAutoEnroll); Free(lpAutoEnrollMode); if ( hImm ) { FreeLibrary( hImm ); } return(0); } // // Determines if logon scripts should be executed sync or async // BOOL RunLogonScriptSync() { BOOL bSync = FALSE; HKEY hKey; DWORD dwType, dwSize; // // Check for a user preference // if (RegOpenKeyEx (HKEY_CURRENT_USER, WINLOGON_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { // // Check for the sync flag. // dwSize = sizeof(bSync); RegQueryValueEx (hKey, SYNC_LOGON_SCRIPT, NULL, &dwType, (LPBYTE) &bSync, &dwSize); RegCloseKey (hKey); } // // Check for a machine preference // if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, WINLOGON_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { // // Check for the sync flag. // dwSize = sizeof(bSync); RegQueryValueEx (hKey, SYNC_LOGON_SCRIPT, NULL, &dwType, (LPBYTE) &bSync, &dwSize); RegCloseKey (hKey); } // // Check for a user policy // if (RegOpenKeyEx (HKEY_CURRENT_USER, WINLOGON_POLICY_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { // // Check for the sync flag. // dwSize = sizeof(bSync); RegQueryValueEx (hKey, SYNC_LOGON_SCRIPT, NULL, &dwType, (LPBYTE) &bSync, &dwSize); RegCloseKey (hKey); } // // Check for a machine policy // if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, WINLOGON_POLICY_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { // // Check for the sync flag. // dwSize = sizeof(bSync); RegQueryValueEx (hKey, SYNC_LOGON_SCRIPT, NULL, &dwType, (LPBYTE) &bSync, &dwSize); RegCloseKey (hKey); } return bSync; } // // Determines if startup scripts should be executed sync or async // BOOL RunStartupScriptSync() { BOOL bSync = TRUE; HKEY hKey; DWORD dwType, dwSize; // // Check for a machine preference // if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, WINLOGON_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { // // Check for the sync flag. // dwSize = sizeof(bSync); RegQueryValueEx (hKey, SYNC_STARTUP_SCRIPT, NULL, &dwType, (LPBYTE) &bSync, &dwSize); RegCloseKey (hKey); } // // Check for a machine policy // if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, WINLOGON_POLICY_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { // // Check for the sync flag. // dwSize = sizeof(bSync); RegQueryValueEx (hKey, SYNC_STARTUP_SCRIPT, NULL, &dwType, (LPBYTE) &bSync, &dwSize); RegCloseKey (hKey); } return bSync; } // // Notify various components that a new user // has logged into the workstation. // VOID NewLogonNotify( VOID ) { FARPROC lpProc; HMODULE hLib; HANDLE hEvent; // // Load the client-side user-mode PnP manager DLL // hLib = LoadLibrary(TEXT("setupapi.dll")); if (hLib) { lpProc = GetProcAddress(hLib, "CMP_Report_LogOn"); if (lpProc) { // // Ping the user-mode pnp manager - // pass the private id as a parameter // (lpProc)(0x07020420, GetCurrentProcessId()); } FreeLibrary(hLib); } // // Notify DPAPI that a new user has just logged in. DPAPI will take // this opportunity to re-synchronize its master keys if necessary. // { BYTE BufferIn[8] = {0}; DATA_BLOB DataIn; DATA_BLOB DataOut; DataIn.pbData = BufferIn; DataIn.cbData = sizeof(BufferIn); CryptProtectData(&DataIn, NULL, NULL, NULL, NULL, CRYPTPROTECT_CRED_SYNC, &DataOut); } // // Only do this for Console session // if ( NtCurrentPeb()->SessionId != 0 ) { return; } // // Notify RAS Autodial service that a new // user has logged in. // hEvent = OpenEvent(SYNCHRONIZE|EVENT_MODIFY_STATE, FALSE, L"RasAutodialNewLogonUser"); if (hEvent) { SetEvent(hEvent); CloseHandle(hEvent); } } BOOL SetupHotKeyForKeyboardLayout () { if (!GetSystemMetrics(SM_REMOTESESSION)) { // // we dont care about local sessions. // return TRUE; } if (GetUserDefaultLangID() != LOWORD(GetKeyboardLayout(0))) { // // we are in a remote session, and we have different keyboard layouts for client and this users settings. // the user should be allowed to switch the keyboard layout even if there is only 1 kbd layout available in his settings. // since the current kbd layout is different that the one in his profile. // WCHAR szCtfmon[] = L"ctfmon.exe"; WCHAR szCtfmonCmd[] = L"ctfmon.exe /n"; HKEY hRunOnce; DWORD dw; // // Lets put this in RunOnce. // if (RegCreateKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Runonce", 0, REG_NONE, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hRunOnce, &dw) == ERROR_SUCCESS) { WCHAR *szHotKeyReg = L"Keyboard Layout\\Toggle"; HKEY hHotKey; WCHAR szHotKeylAltShft[] = L"1"; WCHAR szNoHotKey[] = L"3"; RegSetValueEx(hRunOnce, szCtfmon, 0, REG_SZ, (PBYTE)szCtfmonCmd, sizeof(szCtfmonCmd)); RegCloseKey(hRunOnce); if (RegCreateKeyEx(HKEY_CURRENT_USER, szHotKeyReg, 0, REG_NONE, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hHotKey, &dw) == ERROR_SUCCESS) { DWORD dwType; WCHAR szHotKey[3]; DWORD dwLen = sizeof(szHotKey); BOOL bResetHotkey = FALSE; if (RegQueryValueEx(hHotKey, L"Hotkey", NULL, &dwType, (PBYTE)szHotKey, &dwLen) != ERROR_SUCCESS) { bResetHotkey = TRUE; } if (bResetHotkey || !wcscmp(szHotKey, szNoHotKey)) { // // setup the registry for Hotkey. // if (RegSetValueEx(hHotKey, L"Hotkey", 0, REG_SZ, (const BYTE *)szHotKeylAltShft, sizeof(szHotKeylAltShft)) == ERROR_SUCCESS) { // // now make call to read this registry and set the hotkey appropriately. // SystemParametersInfo( SPI_SETLANGTOGGLE, 0, NULL, 0); } } RegCloseKey(hHotKey); } } } return TRUE; } /**************************************************************************** IsTSAppCompatOn() Purpose: Checks if TS application compatibility is enabled. returns TRUE if enabled, FALSE - if not enabled or on case of error. Comments: This function goes to the registry only once. All other times it just returnes the value. ****************************************************************************/ BOOL IsTSAppCompatOn() { static BOOL bAppCompatOn = FALSE; static BOOL bFirst = TRUE; if(bFirst) { HKEY hKey; if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CONTROL_TSERVER, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS ) { DWORD dwValue = 0; DWORD dwType; DWORD dwSize = sizeof(dwValue); if( RegQueryValueEx(hKey, REG_TERMSRV_APPCOMPAT, NULL, &dwType, (LPBYTE) &dwValue, &dwSize) == ERROR_SUCCESS ) { bAppCompatOn = (dwValue != 0); } RegCloseKey(hKey); } bFirst = FALSE; } return bAppCompatOn; } /**************************************************************************** UpdateUserSyncLogonScriptsCache() Purpose: Update user's sync-logon-scripts setting cache in profile list. ****************************************************************************/ VOID UpdateUserSyncLogonScriptsCache(BOOL bSync) { HANDLE UserToken; HKEY UserKey; PWCHAR UserSidString; PWCHAR KeyPath; ULONG Length; // // Update user's sync-logon-scripts setting cache in profile list. // if (OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &UserToken)) { UserSidString = GetSidString(UserToken); if (UserSidString) { Length = 0; Length += wcslen(PROFILE_LIST_PATH); Length += wcslen(L"\\"); Length += wcslen(UserSidString); KeyPath = Alloc((Length + 1) * sizeof(WCHAR)); if (KeyPath) { wcscpy(KeyPath, PROFILE_LIST_PATH); wcscat(KeyPath, L"\\"); wcscat(KeyPath, UserSidString); if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, KeyPath, 0, KEY_SET_VALUE, &UserKey) == ERROR_SUCCESS) { RegSetValueEx(UserKey, SYNC_LOGON_SCRIPT, 0, REG_DWORD, (BYTE *) &bSync, sizeof(bSync)); RegCloseKey(UserKey); } Free(KeyPath); } DeleteSidString(UserSidString); } CloseHandle(UserToken); } return; }