diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/windows/gina/winlogon | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/windows/gina/winlogon')
74 files changed, 28595 insertions, 0 deletions
diff --git a/private/windows/gina/winlogon/debug.c b/private/windows/gina/winlogon/debug.c new file mode 100644 index 000000000..2e1f94e9e --- /dev/null +++ b/private/windows/gina/winlogon/debug.c @@ -0,0 +1,306 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1993. +// +// File: debug.c +// +// Contents: Debugging support functions +// +// Classes: +// +// Functions: +// +// Note: This file is not compiled for retail builds +// +// History: 4-29-93 RichardW Created +// +//---------------------------------------------------------------------------- + +#if DBG // NOTE: This file not compiled for retail builds + +#include "precomp.h" +#pragma hdrstop + +FILE * LogFile; +DWORD WinlogonInfoLevel = 3; + + +// Debugging support functions. + +// These two functions do not exist in Non-Debug builds. They are wrappers +// to the commnot functions (maybe I should get rid of that as well...) +// that echo the message to a log file. + +char * DebLevel[] = { "Winlogon-Error", + "Winlogon-Warn", + "Winlogon-Trace", + "Winlogon-Trace-Init", + "Winlogon-Trace-Timeout", + "Winlogon-Trace-SAS", + "Winlogon-Trace-State", + "Winlogon-Trace-MPR", + "Should-not-see", + "Winlogon-Trace-Profile", + "Should-not-see", + "Should-not-see", + "Should-not-see", + "Winlogon-Trace-Migrate", + "Should-not-see", + "Winlogon-Trace-Setup" + }; + +typedef struct _DebugKeys { + char * Name; + DWORD Value; +} DebugKeys, *PDebugKeys; + +DebugKeys DebugKeyNames[] = { + {"Error", DEB_ERROR}, + {"Warning", DEB_WARN}, + {"Trace", DEB_TRACE}, + {"Init", DEB_TRACE_INIT}, + {"Timeout", DEB_TRACE_TIMEOUT}, + {"Sas", DEB_TRACE_SAS}, + {"State", DEB_TRACE_STATE}, + {"MPR", DEB_TRACE_MPR}, + {"CoolSwitch", DEB_COOL_SWITCH}, + {"Profile", DEB_TRACE_PROFILE}, + {"DebugLsa", DEB_DEBUG_LSA}, + {"DebugSpm", DEB_DEBUG_LSA}, + {"DebugMpr", DEB_DEBUG_MPR}, + {"DebugGo", DEB_DEBUG_NOWAIT}, + {"Migrate", DEB_TRACE_MIGRATE}, + {"DebugServices", DEB_DEBUG_SERVICES}, + {"Setup", DEB_TRACE_SETUP}, + }; + +#define NUM_DEBUG_KEYS sizeof(DebugKeyNames) / sizeof(DebugKeys) +#define NUM_BREAK_KEYS sizeof(BreakKeyNames) / sizeof(DebugKeys) + +//+--------------------------------------------------------------------------- +// +// Function: LogEvent +// +// Synopsis: Logs an event to the console and, optionally, a file. +// +// Effects: +// +// Arguments: [Mask] -- +// [Format] -- +// [Format] -- +// +// Requires: +// +// Returns: +// +// Signals: +// +// Modifies: +// +// Algorithm: +// +// History: 4-29-93 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- + +void +LogEvent( long Mask, + const char * Format, + ...) +{ + va_list ArgList; + int Level = 0; + int PrefixSize = 0; + char szOutString[256]; + long OriWinlogonlMask = Mask; + + + if (Mask & WinlogonInfoLevel) + { + while (!(Mask & 1)) + { + Level++; + Mask >>= 1; + } + if (Level >= (sizeof(DebLevel) / sizeof(char *)) ) + { + Level = (sizeof(DebLevel) / sizeof(char *)) - 1; + } + + + // + // Make the prefix first: "Process.Thread> Winlogon-XXX" + // + + PrefixSize = sprintf(szOutString, "%d.%d> %s: ", + GetCurrentProcessId(), GetCurrentThreadId(), DebLevel[Level]); + + + va_start(ArgList, Format); + + if (_vsnprintf(&szOutString[PrefixSize], sizeof(szOutString) - PrefixSize, + Format, ArgList) < 0) + { + // + // Less than zero indicates that the string could not be + // fitted into the buffer. Output a special message indicating + // that: + // + + OutputDebugStringA("Winlogon!LogEvent: Could not pack string into 256 bytes\n"); + + } + else + { + OutputDebugStringA(szOutString); + } + + + if (LogFile) + { + SYSTEMTIME stTime; + FILETIME ftTime; + FILETIME localtime; + + NtQuerySystemTime((PLARGE_INTEGER) &ftTime); + FileTimeToLocalFileTime(&ftTime, &localtime); + FileTimeToSystemTime(&localtime, &stTime); + fprintf(LogFile, "%02d:%02d:%02d.%03d: %s\n", + stTime.wHour, stTime.wMinute, stTime.wSecond, + stTime.wMilliseconds, szOutString); + + fflush(LogFile); + } + + } + +} + +void +OpenLogFile(LPSTR pszLogFile) +{ + LogFile = fopen(pszLogFile, "a"); + if (!LogFile) + { + OutputDebugStringA("Winlogon: Could not open logfile for append"); + OutputDebugStringA(pszLogFile); + } + DebugLog((DEB_TRACE, "Log file '%s' begins\n", pszLogFile)); +} + + +DWORD +GetDebugKeyValue( + PDebugKeys KeyTable, + int cKeys, + LPSTR pszKey) +{ + int i; + + for (i = 0; i < cKeys ; i++ ) + { + if (_strcmpi(KeyTable[i].Name, pszKey) == 0) + { + return(KeyTable[i].Value); + } + } + return(0); +} + +//+--------------------------------------------------------------------------- +// +// Function: LoadDebugParameters +// +// Synopsis: Loads debug parameters from win.ini +// +// Effects: +// +// Arguments: (none) +// +// Requires: +// +// Returns: +// +// Signals: +// +// Modifies: +// +// Algorithm: +// +// History: 4-29-93 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- + + +void +LoadDebugParameters(char * szSection) +{ + char szVal[128]; + char * pszDebug; + int cbVal; + + cbVal = GetProfileStringA(szSection, "DebugFlags", "Error,Warning", szVal, sizeof(szVal)); + + pszDebug = strtok(szVal, ", \t"); + while (pszDebug) + { + WinlogonInfoLevel |= GetDebugKeyValue(DebugKeyNames, NUM_DEBUG_KEYS, pszDebug); + pszDebug = strtok(NULL, ", \t"); + } + + cbVal = GetProfileStringA(szSection, "LogFile", "", szVal, sizeof(szVal)); + if (cbVal) + { + OpenLogFile(szVal); + } + +} + +//+--------------------------------------------------------------------------- +// +// Function: InitDebugSupport +// +// Synopsis: Initializes debugging support for the Winlogon +// +// Effects: +// +// Arguments: (none) +// +// Requires: +// +// Returns: +// +// Signals: +// +// Modifies: +// +// Algorithm: +// +// History: 4-29-93 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- + + +void +InitDebugSupport(void) +{ + LoadDebugParameters("WinlogonDebug"); + LoadDebugParameters("Winlogon"); + +} + + + +#else // DBG + +#pragma warning(disable:4206) // Disable the empty transation unit + // warning/error + +#endif // NOTE: This file not compiled for retail builds diff --git a/private/windows/gina/winlogon/debug.h b/private/windows/gina/winlogon/debug.h new file mode 100644 index 000000000..7da351b04 --- /dev/null +++ b/private/windows/gina/winlogon/debug.h @@ -0,0 +1,62 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1993. +// +// File: debug.h +// +// Contents: +// +// Classes: +// +// Functions: +// +// History: 8-02-94 RichardW Created +// +//---------------------------------------------------------------------------- + + +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#if DBG + +extern DWORD WinlogonInfoLevel; +extern DWORD GinaBreakFlags; + +#define DebugLog(x) LogEvent x + + +void LogEvent(long, const char *, ...); +void InitDebugSupport(void); + +#define DEB_ERROR 0x00000001 +#define DEB_WARN 0x00000002 +#define DEB_TRACE 0x00000004 +#define DEB_TRACE_INIT 0x00000008 +#define DEB_TRACE_TIMEOUT 0x00000010 +#define DEB_TRACE_SAS 0x00000020 +#define DEB_TRACE_STATE 0x00000040 +#define DEB_TRACE_MPR 0x00000080 +#define DEB_COOL_SWITCH 0x00000100 +#define DEB_TRACE_PROFILE 0x00000200 +#define DEB_DEBUG_LSA 0x00000400 +#define DEB_DEBUG_MPR 0x00000800 +#define DEB_DEBUG_NOWAIT 0x00001000 +#define DEB_TRACE_MIGRATE 0x00002000 +#define DEB_DEBUG_SERVICES 0x00004000 +#define DEB_TRACE_SETUP 0x00008000 + + + +#else + +#define DebugLog(x) +#define InitDebugSupport() + + +#endif + + + +#endif // __DEBUG_H__ diff --git a/private/windows/gina/winlogon/dialogs.dlg b/private/windows/gina/winlogon/dialogs.dlg new file mode 100644 index 000000000..6359c4301 --- /dev/null +++ b/private/windows/gina/winlogon/dialogs.dlg @@ -0,0 +1,48 @@ +1 DLGINCLUDE "dialogs.h" + + +IDD_CONTROL DIALOG 70, 80, 144, 76 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Winlogon generic control dialog" +FONT 8, "MS Shell Dlg" +BEGIN +END + +IDD_SHUTDOWN DIALOG 45, 22, 164, 52 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Shutdown Computer" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "It is now safe to turn off your computer.", 302, 31, 13, + 132, 8 + ICON 4, IDD_ICON, 6, 7, 18, 20 + DEFPUSHBUTTON "&Restart", 305, 62, 32, 40, 14 +END + +IDD_SHUTDOWN_WAIT DIALOG 69, 73, 132, 42 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Shutdown in Progress" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Please wait while the system writes unsaved data to the disk.", + 101, 11, 12, 112, 18 +END + +IDD_WAIT_NET_DRIVES_DISCONNECT DIALOG 111, 47, 120, 37 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Logoff in Progress" +FONT 8, "MS Shell Dlg" +BEGIN + CTEXT " Please wait while the network connections are being closed.", 101, 4, 8, 113, + 18 +END + + +IDD_FORCED_LOGOFF_WAIT DIALOG 94, 47, 146, 42 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Workstation Locked" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Please wait while the current user is logged off.", 101, + 7, 12, 137, 19 +END diff --git a/private/windows/gina/winlogon/dialogs.h b/private/windows/gina/winlogon/dialogs.h new file mode 100644 index 000000000..b50a029be --- /dev/null +++ b/private/windows/gina/winlogon/dialogs.h @@ -0,0 +1,24 @@ +/****************************** Module Header ******************************\ +* Module Name: dialogs.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Define contants used by dialog edit when editting dialog.dlg +* +* NOTE - this file is maintained by dlgedit. Do not edit directly +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + + +#define IDD_SHUTDOWN 300 +#define IDD_FORCED_LOGOFF_WAIT 153 +#define IDD_SHUTDOWN_BUTTON 1011 +#define IDD_TEXT 1401 +#define IDD_SHUTDOWN_WAIT 600 +#define IDD_WAIT_DOMAIN_CACHE_VALID 800 +#define IDD_WAIT_NET_DRIVES_DISCONNECT 1600 +#define IDD_ICON 102 +#define IDD_CONTROL 1400 +#define IDD_FORCED_LOGOFF_WAIT 153 diff --git a/private/windows/gina/winlogon/doslog.c b/private/windows/gina/winlogon/doslog.c new file mode 100644 index 000000000..6ce99cf71 --- /dev/null +++ b/private/windows/gina/winlogon/doslog.c @@ -0,0 +1,59 @@ +/****************************** Module Header ******************************\ +* Module Name: DosLog.c +* +* Copyright (c) 1991, Microsoft Corporation +* +* Contains implementations of Dos boot/shutdown support functions +* +* History: +* 04-30-92 Sudeepb Created. +* +* 25-Nov-1992 Jonle, removed winlogon renaming, left BootDos\ShutdownDos +* as stubs for future use. +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + + +/***************************************************************************\ +* BootDOS +* +* Routine to be called at system boot for DOS support. +* +* Currently does nothing, left in place for future convenience +* +* Parameters - None +* +* Returns - Nothing +* +* History: +* 04-30-92 Sudeepb Created. +\***************************************************************************/ + +void BootDOS ( void ) +{ + return; +} + + +/***************************************************************************\ +* ShutdownDOS +* +* Routine to be called at system shutdown for DOS support. +* +* This routine saves away the config.sys and autoexec.bat of +* DOS subsystem and restores the original ones. +* +* Parameters - None +* +* Returns - Nothing +* +* History: +* 04-30-92 Sudeepb Created. +\***************************************************************************/ + +void ShutdownDOS ( void ) +{ + return; +} diff --git a/private/windows/gina/winlogon/doslog.h b/private/windows/gina/winlogon/doslog.h new file mode 100644 index 000000000..ab2fc755e --- /dev/null +++ b/private/windows/gina/winlogon/doslog.h @@ -0,0 +1,17 @@ +/****************************** Module Header ******************************\ +* Module Name: DosLog.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Contains definitions of Dos boot/shutdown support functions +* +* Currently NONE +* +* History: +* 04-30-92 Sudeepb Created. +* +* 25-Nov-1992 Jonle, removed winlogon renaming +\***************************************************************************/ + +void BootDOS ( void ) ; +void ShutdownDOS ( void ); diff --git a/private/windows/gina/winlogon/envvar.c b/private/windows/gina/winlogon/envvar.c new file mode 100644 index 000000000..6cc586fb7 --- /dev/null +++ b/private/windows/gina/winlogon/envvar.c @@ -0,0 +1,1458 @@ +/****************************** Module Header ******************************\ +* Module Name: envvar.c +* +* Copyright (c) 1992, Microsoft Corporation +* +* Sets environment variables. +* +* History: +* 2-25-92 JohanneC Created - +* +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +BOOL ProcessCommand(LPTSTR lpStart, PVOID *pEnv); +BOOL ProcessSetCommand(LPTSTR lpStart, PVOID *pEnv); +LPTSTR ProcessAutoexecPath(PVOID *pEnv, LPTSTR lpValue, DWORD cb); +BOOL AddNetworkConnection(PGLOBALS pGlobals, LPNETRESOURCE lpNetResource); +BOOL UpdateUserEnvironment( PVOID *pEnv ); + +#define KEY_NAME TEXT("System\\CurrentControlSet\\Control\\Session Manager\\Environment") + +// +// Max environment variable length +// + +#define MAX_VALUE_LEN 1024 + + +/***************************************************************************\ +* InitSystemParametersInfo +* +* Re-Initializes system parameters given the current user registry. +* +* History: +* +\***************************************************************************/ + +VOID +InitSystemParametersInfo( + PGLOBALS pGlobals, + BOOL bUserLoggedOn + ) +{ + HANDLE ImpersonationHandle; + BOOL Result; + + // + // Open the profile mapping for the logged on user + // + + Result = OpenIniFileUserMapping(pGlobals); + if (!Result) { + DebugLog((DEB_ERROR, "InitSystemParametersInfo: Failed to open ini file mapping for user")); + return; + } + // + // Impersonate the user so if the user server side has to reference + // any ini files etc, it will do it in the correct place. + // + + ImpersonationHandle = ImpersonateUser(&pGlobals->UserProcessData, NULL); + + if (ImpersonationHandle == NULL) { + DebugLog((DEB_ERROR, "InitSystemParametersInfo : Failed to impersonate user")); + CloseIniFileUserMapping(pGlobals); + return; + } + + UpdatePerUserSystemParameters(bUserLoggedOn); + + // + // Revert to being 'ourself' + // + + if (!StopImpersonating(ImpersonationHandle)) { + DebugLog((DEB_ERROR, "InitSystemParametersInfo : Failed to revert to self")); + } + + // + // Close the profile mapping + // + + CloseIniFileUserMapping(pGlobals); +} + + +/***************************************************************************\ +* CreateUserEnvironment +* +* +* History: +* 2-28-92 Johannec Created +* +\***************************************************************************/ +BOOL +CreateUserEnvironment( + PVOID *pEnv + ) +{ + NTSTATUS Status; + + Status = RtlCreateEnvironment((BOOLEAN)TRUE, pEnv); + if (NT_SUCCESS(Status)) { + + // + // We must examine the registry directly to suck out + // the system environment variables, because they + // may have changed since the system was booted. + // + + if ( UpdateUserEnvironment( pEnv )) { + + return( TRUE ); + } + + RtlDestroyEnvironment(*pEnv); + } + + return(FALSE); +} + + +/***************************************************************************\ +* SetUserEnvironmentVariable +* +* +* History: +* 2-28-92 Johannec Created +* +\***************************************************************************/ +BOOL +SetUserEnvironmentVariable( + PVOID *pEnv, + LPTSTR lpVariable, + LPTSTR lpValue, + BOOL bOverwrite + ) +{ + NTSTATUS Status; + UNICODE_STRING Name, Value; + DWORD cb; + DWORD cchValue = 1024; + + if (!*pEnv || !lpVariable || !*lpVariable) { + return(FALSE); + } + RtlInitUnicodeString(&Name, lpVariable); + cb = 1024; + Value.Buffer = Alloc(sizeof(TCHAR)*cb); + if (Value.Buffer) { + Value.Length = (USHORT)cb; + Value.MaximumLength = (USHORT)cb; + Status = RtlQueryEnvironmentVariable_U(*pEnv, &Name, &Value); + + Free(Value.Buffer); + + if ( NT_SUCCESS(Status) && !bOverwrite) { + return(TRUE); + } + } + if (lpValue && *lpValue) { + RtlInitUnicodeString(&Value, lpValue); + Status = RtlSetEnvironmentVariable(pEnv, &Name, &Value); + } + else { + Status = RtlSetEnvironmentVariable(pEnv, &Name, NULL); + } + if (NT_SUCCESS(Status)) { + return(TRUE); + } + return(FALSE); +} + + +/***************************************************************************\ +* ExpandUserEnvironmentStrings +* +* History: +* 2-28-92 Johannec Created +* +\***************************************************************************/ +DWORD +ExpandUserEnvironmentStrings( + PVOID pEnv, + LPTSTR lpSrc, + LPTSTR lpDst, + DWORD nSize + ) +{ + NTSTATUS Status; + UNICODE_STRING Source, Destination; + ULONG Length; + + RtlInitUnicodeString( &Source, lpSrc ); + Destination.Buffer = lpDst; + Destination.Length = 0; + Destination.MaximumLength = (USHORT)nSize; + Length = 0; + Status = RtlExpandEnvironmentStrings_U( pEnv, + (PUNICODE_STRING)&Source, + (PUNICODE_STRING)&Destination, + &Length + ); + if (NT_SUCCESS( Status ) || Status == STATUS_BUFFER_TOO_SMALL) { + return( Length ); + } + else { + return( 0 ); + } +} + + +/***************************************************************************\ +* BuildEnvironmentPath +* +* +* History: +* 2-28-92 Johannec Created +* +\***************************************************************************/ +BOOL BuildEnvironmentPath(PVOID *pEnv, + LPTSTR lpPathVariable, + LPTSTR lpPathValue) +{ + NTSTATUS Status; + UNICODE_STRING Name; + UNICODE_STRING Value; + TCHAR lpTemp[1024]; + DWORD cch; + + + if (!*pEnv) { + return(FALSE); + } + RtlInitUnicodeString(&Name, lpPathVariable); + cch = 1024; + Value.Buffer = Alloc(sizeof(TCHAR)*cch); + if (!Value.Buffer) { + return(FALSE); + } + Value.Length = (USHORT)cch; + Value.MaximumLength = (USHORT)cch; + Status = RtlQueryEnvironmentVariable_U(*pEnv, &Name, &Value); + if (!NT_SUCCESS(Status)) { + Free(Value.Buffer); + Value.Length = 0; + *lpTemp = 0; + } + if (Value.Length) { + lstrcpy(lpTemp, Value.Buffer); + if ( *( lpTemp + lstrlen(lpTemp) - 1) != TEXT(';') ) { + lstrcat(lpTemp, TEXT(";")); + } + Free(Value.Buffer); + } + if (lpPathValue && ((INT)(lstrlen(lpTemp) + lstrlen(lpPathValue) + 1) < (INT)cch)) { + lstrcat(lpTemp, lpPathValue); + + RtlInitUnicodeString(&Value, lpTemp); + + Status = RtlSetEnvironmentVariable(pEnv, &Name, &Value); + } + if (NT_SUCCESS(Status)) { + return(TRUE); + } + return(FALSE); +} + + +/***************************************************************************\ +* SetEnvironmentVariables +* +* Reads the user-defined environment variables from the user registry +* and adds them to the environment block at pEnv. +* +* History: +* 2-28-92 Johannec Created +* +\***************************************************************************/ +BOOL +SetEnvironmentVariables( + PGLOBALS pGlobals, + PVOID *pEnv + ) +{ + TCHAR lpValueName[MAX_PATH]; + PWCH lpDataBuffer; + DWORD cbDataBuffer; + PWCH lpData; + LPTSTR lpExpandedValue = NULL; + DWORD cbValueName = MAX_PATH; + DWORD cbData; + DWORD dwType; + DWORD dwIndex = 0; + HKEY hkey; + BOOL bResult; + NTSTATUS Status = 0; + TCHAR UserEnvVarSubkey[] = TEXT("Environment"); + + /* + * Open registry key to access USER environment variables. + */ + if (!OpenHKeyCurrentUser(pGlobals)) { + DebugLog((DEB_ERROR, "SetEnvironmentVariables: Failed to open HKeyCurrentUser")); + return(FALSE); + } + + if (RegOpenKeyEx(HKEY_CURRENT_USER, UserEnvVarSubkey, 0, KEY_READ, &hkey)) { + CloseHKeyCurrentUser(pGlobals); + return(FALSE); + } + + cbDataBuffer = 4096; + lpDataBuffer = Alloc(sizeof(TCHAR)*cbDataBuffer); + if (lpDataBuffer == NULL) { + DebugLog((DEB_ERROR, "SetEnvironmentVariables: Failed to allocate %d bytes", cbDataBuffer)); + CloseHKeyCurrentUser(pGlobals); + RegCloseKey(hkey); + return(FALSE); + } + lpData = lpDataBuffer; + cbData = sizeof(TCHAR)*cbDataBuffer; + bResult = TRUE; + while (!RegEnumValue(hkey, dwIndex, lpValueName, &cbValueName, 0, &dwType, + (LPBYTE)lpData, &cbData)) { + if (cbValueName) { + + // + // Limit environment variable length + // + + lpData[MAX_VALUE_LEN-1] = TEXT('\0'); + + + if (dwType == REG_SZ) { + + // + // The path variables PATH, LIBPATH and OS2LIBPATH must have + // their values apppended to the system path. + // + + if ( !lstrcmpi(lpValueName, PATH_VARIABLE) || + !lstrcmpi(lpValueName, LIBPATH_VARIABLE) || + !lstrcmpi(lpValueName, OS2LIBPATH_VARIABLE) ) { + + BuildEnvironmentPath(pEnv, lpValueName, lpData); + } + else { + + // + // the other environment variables are just set. + // + SetUserEnvironmentVariable(pEnv, lpValueName, lpData, TRUE); + } + } + } + dwIndex++; + cbData = cbDataBuffer; + cbValueName = MAX_PATH; + } + + dwIndex = 0; + cbData = cbDataBuffer; + cbValueName = MAX_PATH; + + + while (!RegEnumValue(hkey, dwIndex, lpValueName, &cbValueName, 0, &dwType, + (LPBYTE)lpData, &cbData)) { + if (cbValueName) { + + // + // Limit environment variable length + // + + lpData[MAX_VALUE_LEN-1] = TEXT('\0'); + + + if (dwType == REG_EXPAND_SZ) { + DWORD cb, cbNeeded; + + cb = 1024; + lpExpandedValue = Alloc(sizeof(TCHAR)*cb); + if (lpExpandedValue) { + cbNeeded = ExpandUserEnvironmentStrings(*pEnv, lpData, lpExpandedValue, cb); + if (cbNeeded > cb) { + Free(lpExpandedValue); + cb = cbNeeded; + lpExpandedValue = Alloc(sizeof(TCHAR)*cb); + if (lpExpandedValue) { + ExpandUserEnvironmentStrings(*pEnv, lpData, lpExpandedValue, cb); + } + } + } + + if (lpExpandedValue == NULL) { + bResult = FALSE; + break; + } + + + // + // The path variables PATH, LIBPATH and OS2LIBPATH must have + // their values apppended to the system path. + // + + if ( !lstrcmpi(lpValueName, PATH_VARIABLE) || + !lstrcmpi(lpValueName, LIBPATH_VARIABLE) || + !lstrcmpi(lpValueName, OS2LIBPATH_VARIABLE) ) { + + BuildEnvironmentPath(pEnv, lpValueName, lpExpandedValue); + } + else { + + // + // the other environment variables are just set. + // + SetUserEnvironmentVariable(pEnv, lpValueName, lpExpandedValue, TRUE); + } + + Free(lpExpandedValue); + } + } + dwIndex++; + cbData = cbDataBuffer; + cbValueName = MAX_PATH; + } + + + Free(lpDataBuffer); + RegCloseKey(hkey); + CloseHKeyCurrentUser(pGlobals); + + return(bResult); +} + +/***************************************************************************\ +* IsUNCPath +* +* History: +* 2-28-92 Johannec Created +* +\***************************************************************************/ +BOOL IsUNCPath(LPTSTR lpPath) +{ + if (lpPath[0] == BSLASH && lpPath[1] == BSLASH) { + return(TRUE); + } + return(FALSE); +} + +/***************************************************************************\ +* SetHomeDirectoryEnvVars +* +* History: +* 2-28-92 Johannec Created +* +\***************************************************************************/ +BOOL +SetHomeDirectoryEnvVars( + PVOID *pEnv, + LPTSTR lpHomeDirectory, + LPTSTR lpHomeDrive, + LPTSTR lpHomeShare, + LPTSTR lpHomePath + ) +{ + TCHAR cTmp; + LPTSTR lpHomeTmp; + BOOL bFoundFirstBSlash = FALSE; + + if (!*lpHomeDirectory) { + return(FALSE); + } + if (IsUNCPath(lpHomeDirectory)) { + lpHomeTmp = lpHomeDirectory + 2; + while (*lpHomeTmp) { + if (*lpHomeTmp == BSLASH) { + if (bFoundFirstBSlash) { + break; + } + bFoundFirstBSlash = TRUE; + } + lpHomeTmp++; + } + if (*lpHomeTmp) { + lstrcpy(lpHomePath, lpHomeTmp); + } + else { + *lpHomePath = BSLASH; + *(lpHomePath+1) = 0; + } + + cTmp = *lpHomeTmp; + *lpHomeTmp = (TCHAR)0; + lstrcpy(lpHomeShare, lpHomeDirectory); + *lpHomeTmp = cTmp; + + // + // If no home drive specified, than default to z: + // + if (!*lpHomeDrive) { + lstrcpy(lpHomeDrive, TEXT("Z:")); + } + + } + else { // local home directory + + *lpHomeShare = 0; // no home share + + cTmp = lpHomeDirectory[2]; + lpHomeDirectory[2] = (TCHAR)0; + lstrcpy(lpHomeDrive, lpHomeDirectory); + lpHomeDirectory[2] = cTmp; + + lstrcpy(lpHomePath, lpHomeDirectory + 2); + } + + SetUserEnvironmentVariable(pEnv, HOMEDRIVE_VARIABLE, lpHomeDrive, TRUE); + SetUserEnvironmentVariable(pEnv, HOMESHARE_VARIABLE, lpHomeShare, TRUE); + SetUserEnvironmentVariable(pEnv, HOMEPATH_VARIABLE, lpHomePath, TRUE); +} + +/***************************************************************************\ +* ChangeToHomeDirectory +* +* Sets the current directory to the user's home directory. If this fails +* tries to set to the directory in the following order: +* 1. home directory +* 2. c:\users\default +* 3. c:\users +* 4. \ (root) +* 5. leaves directory as is i.e. the present current directory +* +* History: +* 2-28-92 Johannec Created +* +\***************************************************************************/ +VOID +ChangeToHomeDirectory( + PGLOBALS pGlobals, + PVOID *pEnv, + LPTSTR lpHomeDir, + LPTSTR lpHomeDrive, + LPTSTR lpHomeShare, + LPTSTR lpHomePath + ) +{ + TCHAR lpOldDir[MAX_PATH]; + TCHAR lpCurDrive[4]; + BOOL bNoHomeDir = FALSE; + HANDLE ImpersonationHandle = NULL; + DWORD error = ERROR_SUCCESS; + + if (GetCurrentDirectory(MAX_PATH, lpOldDir)) { + lpCurDrive[0] = lpOldDir[0]; + lpCurDrive[1] = lpOldDir[1]; + lpCurDrive[2] = (TCHAR)0; + } + else + lpCurDrive[0] = (TCHAR)0; + + if (!*lpHomeDir) { + bNoHomeDir = TRUE; + +DefaultDirectory: + if (!bNoHomeDir) { + ReportWinlogonEvent(pGlobals, + EVENTLOG_ERROR_TYPE, + EVENT_SET_HOME_DIRECTORY_FAILED, + sizeof(error), + &error, + 1, + lpHomeDir); + + } + lstrcpy(lpHomeDir, lpCurDrive); + if (SetCurrentDirectory(USERS_DEFAULT_DIRECTORY)) { + lstrcat(lpHomeDir, USERS_DEFAULT_DIRECTORY); + } + else if (SetCurrentDirectory(USERS_DIRECTORY)) { + lstrcat(lpHomeDir, USERS_DIRECTORY); + } + else if (SetCurrentDirectory(ROOT_DIRECTORY)) { + lstrcat(lpHomeDir, ROOT_DIRECTORY); + } + else { + lstrcpy(lpHomeDir, NULL_STRING); + } + if (bNoHomeDir) { + SetUserEnvironmentVariable(pEnv, HOMEDRIVE_VARIABLE, lpCurDrive, TRUE); + *lpHomeShare = 0; // null string + SetUserEnvironmentVariable(pEnv, HOMESHARE_VARIABLE, lpHomeShare, TRUE); + if (*lpHomeDir) { + lpHomeDir += 2; + } + SetUserEnvironmentVariable(pEnv, HOMEPATH_VARIABLE, lpHomeDir, TRUE); + } + + if (*lpOldDir) { + SetCurrentDirectory(lpOldDir); + } + + return; + } + /* + * Test if homedir is a local directory.'?:\foo\bar' + */ + if (IsUNCPath(lpHomeDir)) { + NETRESOURCE NetResource; + + /* + * lpHomeDir is a UNC path, use lpHomedrive. + */ + + NetResource.lpLocalName = lpHomeDrive; + NetResource.lpRemoteName = lpHomeShare; + NetResource.lpProvider = NULL; + NetResource.dwType = RESOURCETYPE_DISK; + + if (!AddNetworkConnection(pGlobals, &NetResource)) { + error = GetLastError(); + goto DefaultDirectory; + } + + lstrcpy(lpHomeDir, lpHomeDrive); + if (lpHomePath && *lpHomePath != TEXT('\\')) { + lstrcat(lpHomeDir, TEXT("\\")); + } + lstrcat(lpHomeDir, lpHomePath); + + // + // Impersonate the user + // + + ImpersonationHandle = ImpersonateUser(&pGlobals->UserProcessData, NULL); + + if (ImpersonationHandle == NULL) { + DebugLog((DEB_ERROR, "ChangeToHomeDirectory : Failed to impersonate user")); + } + + if (!SetCurrentDirectory(lpHomeDir)) { + error = GetLastError(); + DebugLog((DEB_ERROR, "ChangeToHomeDirectory : Failed to SetCurrentDirectory '%ws', error = %d", + lpHomeDir, error)); + // + // Revert to being 'ourself' + // + + if (ImpersonationHandle && !StopImpersonating(ImpersonationHandle)) { + DebugLog((DEB_ERROR, "ChangeToHomeDirectory : Failed to revert to self")); + } + + goto DefaultDirectory; + } + } + else { + /* + * lpHomeDir is a local path or absolute local path. + */ + + // + // Impersonate the user + // + + ImpersonationHandle = ImpersonateUser(&pGlobals->UserProcessData, NULL); + + if (ImpersonationHandle == NULL) { + DebugLog((DEB_ERROR, "ChangeToHomeDirectory : Failed to impersonate user")); + } + + if (!SetCurrentDirectory(lpHomeDir)) { + error = GetLastError(); + DebugLog((DEB_ERROR, "ChangeToHomeDirectory : Failed to SetCurrentDirectory '%ws', error = %d", + lpHomeDir, error)); + // + // Revert to being 'ourself' + // + + if (ImpersonationHandle && !StopImpersonating(ImpersonationHandle)) { + DebugLog((DEB_ERROR, "ChangeToHomeDirectory : Failed to revert to self")); + } + + goto DefaultDirectory; + } + } + + // + // Revert to being 'ourself' + // + + if (ImpersonationHandle && !StopImpersonating(ImpersonationHandle)) { + DebugLog((DEB_ERROR, "ChangeToHomeDirectory : Failed to revert to self")); + } + + if (*lpOldDir) { + SetCurrentDirectory(lpOldDir); + } +} + +/***************************************************************************\ +* ProcessAutoexec +* +* History: +* 01-24-92 Johannec Created. +* +\***************************************************************************/ +BOOL +ProcessAutoexec( + PVOID *pEnv, + LPTSTR lpPathVariable + ) +{ + HANDLE fh; + DWORD dwFileSize; + DWORD dwBytesRead; + CHAR *lpBuffer = NULL; + CHAR *token; + CHAR Seps[] = "&\n\r"; // Seperators for tokenizing autoexec.bat + BOOL Status = FALSE; + TCHAR szAutoExecBat [] = TEXT("c:\\autoexec.bat"); + UINT uiErrMode; + + + // There is a case where the OS might not be booting from drive + // C, so we can not assume that the autoexec.bat file is on c:\. + // Set the error mode so the user doesn't see the critical error + // popup and attempt to open the file on c:\. + + uiErrMode = SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); + + fh = CreateFile (szAutoExecBat, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + SetErrorMode (uiErrMode); + + if (fh == INVALID_HANDLE_VALUE) { + return(FALSE); //could not open autoexec.bat file, we're done. + } + + dwFileSize = GetFileSize(fh, NULL); + if (dwFileSize == -1) { + goto Exit; // can't read the file size + } + + lpBuffer = Alloc(dwFileSize+1); + if (!lpBuffer) { + goto Exit; + } + + Status = ReadFile(fh, lpBuffer, dwFileSize, &dwBytesRead, NULL); + if (!Status) { + goto Exit; // error reading file + } + + // + // Zero terminate the buffer so we don't walk off the end + // + + ASSERT(dwBytesRead <= dwFileSize); + lpBuffer[dwBytesRead] = 0; + + // + // Search for SET and PATH commands + // + + token = strtok(lpBuffer, Seps); + while (token != NULL) { + for (;*token && *token == ' ';token++) //skip spaces + ; + if (*token == TEXT('@')) + token++; + for (;*token && *token == ' ';token++) //skip spaces + ; + if (!_strnicmp(token, "PATH", 4)) { + STRING String; + UNICODE_STRING UniString; + + RtlInitString(&String, (LPSTR)token); + RtlAnsiStringToUnicodeString(&UniString, &String, TRUE); + + if (UniString.Buffer) + { + ProcessCommand(UniString.Buffer, pEnv); + //ProcessCommand(token, pEnv); + + RtlFreeUnicodeString(&UniString); + } + } + if (!_strnicmp(token, "SET", 3)) { + STRING String; + UNICODE_STRING UniString; + + RtlInitString(&String, (LPSTR)token); + RtlAnsiStringToUnicodeString(&UniString, &String, TRUE); + + if (UniString.Buffer) + { + ProcessSetCommand(UniString.Buffer, pEnv); + //ProcessSetCommand(token, pEnv); + + RtlFreeUnicodeString(&UniString); + } + } + token = strtok(NULL, Seps); + } +Exit: + CloseHandle(fh); + if (lpBuffer) { + Free(lpBuffer); + } + if (!Status) { + DebugLog((DEB_ERROR, "Cannot process autoexec.bat.")); + } + return(Status); +} + +/***************************************************************************\ +* ProcessCommand +* +* History: +* 01-24-92 Johannec Created. +* +\***************************************************************************/ +BOOL ProcessCommand(LPTSTR lpStart, PVOID *pEnv) +{ + LPTSTR lpt, lptt; + LPTSTR lpVariable; + LPTSTR lpValue; + LPTSTR lpExpandedValue = NULL; + TCHAR c; + DWORD cb, cbNeeded; + + // + // Find environment variable. + // + for (lpt = lpStart; *lpt && *lpt == TEXT(' '); lpt++) //skip spaces + ; + + if (!*lpt) + return(FALSE); + + lptt = lpt; + for (; *lpt && *lpt != TEXT(' ') && *lpt != TEXT('='); lpt++) //find end of variable name + ; + + c = *lpt; + *lpt = 0; + lpVariable = Alloc(sizeof(TCHAR)*(lstrlen(lptt) + 1)); + if (!lpVariable) + return(FALSE); + lstrcpy(lpVariable, lptt); + *lpt = c; + + // + // Find environment variable value. + // + for (; *lpt && (*lpt == TEXT(' ') || *lpt == TEXT('=')); lpt++) + ; + + if (!*lpt) { + // if we have a blank path statement in the autoexec file, + // then we don't want to pass "PATH" as the environment + // variable because it trashes the system's PATH. Instead + // we want to change the variable AutoexecPath. This would have + // be handled below if a value had been assigned to the + // environment variable. + if (lstrcmpi(lpVariable, PATH_VARIABLE) == 0) + { + SetUserEnvironmentVariable(pEnv, AUTOEXECPATH_VARIABLE, TEXT(""), TRUE); + } + else + { + SetUserEnvironmentVariable(pEnv, lpVariable, TEXT(""), TRUE); + } + Free(lpVariable); + return(FALSE); + } + + lptt = lpt; + for (; *lpt; lpt++) //find end of varaible value + ; + + c = *lpt; + *lpt = 0; + lpValue = Alloc(sizeof(TCHAR)*(lstrlen(lptt) + 1)); + if (!lpValue) { + Free(lpVariable); + return(FALSE); + } + + lstrcpy(lpValue, lptt); + *lpt = c; + + cb = 1024; + lpExpandedValue = Alloc(sizeof(TCHAR)*cb); + if (lpExpandedValue) { + if (!lstrcmpi(lpVariable, PATH_VARIABLE)) { + lpValue = ProcessAutoexecPath(pEnv, lpValue, lstrlen(lpValue)+1); + } + cbNeeded = ExpandUserEnvironmentStrings(*pEnv, lpValue, lpExpandedValue, cb); + if (cbNeeded > cb) { + Free(lpExpandedValue); + cb = cbNeeded; + lpExpandedValue = Alloc(sizeof(TCHAR)*cb); + if (lpExpandedValue) { + ExpandUserEnvironmentStrings(*pEnv, lpValue, lpExpandedValue, cb); + } + } + } + + if (!lpExpandedValue) { + lpExpandedValue = lpValue; + } + if (lstrcmpi(lpVariable, PATH_VARIABLE)) { + SetUserEnvironmentVariable(pEnv, lpVariable, lpExpandedValue, FALSE); + } + else { + SetUserEnvironmentVariable(pEnv, AUTOEXECPATH_VARIABLE, lpExpandedValue, TRUE); + + } + + if (lpExpandedValue != lpValue) { + Free(lpExpandedValue); + } + Free(lpVariable); + Free(lpValue); + + return(TRUE); +} + +/***************************************************************************\ +* ProcessSetCommand +* +* History: +* 01-24-92 Johannec Created. +* +\***************************************************************************/ +BOOL ProcessSetCommand(LPTSTR lpStart, PVOID *pEnv) +{ + LPTSTR lpt; + + // + // Find environment variable. + // + for (lpt = lpStart; *lpt && *lpt != TEXT(' '); lpt++) + ; + + if (!*lpt || !_wcsnicmp(lpt,TEXT("COMSPEC"), 7)) + return(FALSE); + + return (ProcessCommand(lpt, pEnv)); + +} + +/***************************************************************************\ +* ProcessAutoexecPath +* +* Creates AutoexecPath environment variable using autoexec.bat +* LpValue may be freed by this routine. +* +* History: +* 06-02-92 Johannec Created. +* +\***************************************************************************/ +LPTSTR ProcessAutoexecPath(PVOID *pEnv, LPTSTR lpValue, DWORD cb) +{ + LPTSTR lpt; + LPTSTR lpStart; + LPTSTR lpPath; + DWORD cbt; + UNICODE_STRING Name; + UNICODE_STRING Value; + BOOL bPrevAutoexecPath; + WCHAR ch; + DWORD dwTemp, dwCount = 0; + + cbt = 1024; + lpt = Alloc(sizeof(TCHAR)*cbt); + if (!lpt) { + return(lpValue); + } + *lpt = 0; + lpStart = lpValue; + + RtlInitUnicodeString(&Name, AUTOEXECPATH_VARIABLE); + Value.Buffer = Alloc(sizeof(TCHAR)*cbt); + if (!Value.Buffer) { + goto Fail; + } + + while (lpPath = wcsstr (lpValue, TEXT("%"))) { + if (!_wcsnicmp(lpPath+1, TEXT("PATH%"), 5)) { + // + // check if we have an autoexecpath already set, if not just remove + // the %path% + // + Value.Length = (USHORT)cbt; + Value.MaximumLength = (USHORT)cbt; + bPrevAutoexecPath = (BOOL)!RtlQueryEnvironmentVariable_U(*pEnv, &Name, &Value); + + *lpPath = 0; + dwTemp = dwCount + lstrlen (lpValue); + if (dwTemp < cbt) { + lstrcat(lpt, lpValue); + dwCount += dwTemp; + } + if (bPrevAutoexecPath) { + dwTemp = dwCount + lstrlen (Value.Buffer); + if (dwTemp < cbt) { + lstrcat(lpt, Value.Buffer); + dwCount += dwTemp; + } + } + + *lpPath++ = TEXT('%'); + lpPath += 5; // go passed %path% + lpValue = lpPath; + } + else { + lpPath = wcsstr(lpPath+1, TEXT("%")); + if (!lpPath) { + lpStart = NULL; + goto Fail; + } + lpPath++; + ch = *lpPath; + *lpPath = 0; + dwTemp = dwCount + lstrlen (lpValue); + if (dwTemp < cbt) { + lstrcat(lpt, lpValue); + dwCount += dwTemp; + } + *lpPath = ch; + lpValue = lpPath; + } + } + + if (*lpValue) { + dwTemp = dwCount + lstrlen (lpValue); + if (dwTemp < cbt) { + lstrcat(lpt, lpValue); + dwCount += dwTemp; + } + } + + Free(Value.Buffer); + Free(lpStart); + + return(lpt); +Fail: + Free(lpt); + return(lpStart); +} + + +/***************************************************************************\ +* AppendNTPathWithAutoexecPath +* +* Gets the AutoexecPath created in ProcessAutoexec, and appends it to +* the NT path. +* +* History: +* 05-28-92 Johannec Created. +* +\***************************************************************************/ +BOOL +AppendNTPathWithAutoexecPath( + PVOID *pEnv, + LPTSTR lpPathVariable, + LPTSTR lpAutoexecPath + ) +{ + NTSTATUS Status; + UNICODE_STRING Name; + UNICODE_STRING Value; + TCHAR AutoexecPathValue[1024]; + DWORD cb; + BOOL Success; + + if (!*pEnv) { + return(FALSE); + } + + RtlInitUnicodeString(&Name, lpAutoexecPath); + cb = 1024; + Value.Buffer = Alloc(sizeof(TCHAR)*cb); + if (!Value.Buffer) { + return(FALSE); + } + + Value.Length = (USHORT)cb; + Value.MaximumLength = (USHORT)cb; + Status = RtlQueryEnvironmentVariable_U(*pEnv, &Name, &Value); + if (!NT_SUCCESS(Status)) { + Free(Value.Buffer); + return(FALSE); + } + + if (Value.Length) { + lstrcpy(AutoexecPathValue, Value.Buffer); + } + + Free(Value.Buffer); + + Success = BuildEnvironmentPath(pEnv, lpPathVariable, AutoexecPathValue); + RtlSetEnvironmentVariable(pEnv, &Name, NULL); + return(Success); +} + + +/***************************************************************************\ +* AddNetworkConnection +* +* calls WNetAddConnection in the user's context. +* +* History: +* 6-26-92 Johannec Created +* +\***************************************************************************/ +BOOL AddNetworkConnection(PGLOBALS pGlobals, LPNETRESOURCE lpNetResource) +{ + HANDLE ImpersonationHandle; + TCHAR szMprDll[] = TEXT("mpr.dll"); + CHAR szWNetAddConn[] = "WNetAddConnection2W"; + DWORD (APIENTRY *lpfnWNetAddConn)(LPNETRESOURCE, LPTSTR, LPTSTR, DWORD); + DWORD WNetResult; + static BOOL fFirstLogon = TRUE; + BOOL Result = FALSE; // Failure is default + + // + // Impersonate the user + // + + ImpersonationHandle = ImpersonateUser(&pGlobals->UserProcessData, NULL); + + if (ImpersonationHandle == NULL) { + DebugLog((DEB_ERROR, "AddNetworkConnection : Failed to impersonate user")); + return(FALSE); + } + + + // + // Call the add connection api in the users context + // + + if (!pGlobals->hMPR) { + // wasn't loaded, try to load it now. + pGlobals->hMPR = LoadLibrary(szMprDll); + } + + if (pGlobals->hMPR) { +#if 0 + if (lpfnWNetAddConn = (DWORD (APIENTRY *)(LPNETRESOURCE, LPTSTR, LPTSTR, DWORD)) + GetProcAddress(pGlobals->hMPR, (LPSTR)szWNetAddConn)) { + + RevealPassword(&pGlobals->PasswordString); + + if (fFirstLogon) { + WNetResult = ERROR_NO_NETWORK; + if (WaitForNetworkToStart(SERVICE_WORKSTATION)) + WNetResult = (*lpfnWNetAddConn)(lpNetResource, + pGlobals->Password, + pGlobals->UserName, + 0); + } + else { + WNetResult = (*lpfnWNetAddConn)(lpNetResource, + pGlobals->Password, + pGlobals->UserName, + 0); + } + + HidePassword( &pGlobals->Seed, &pGlobals->PasswordString); + + if (WNetResult != ERROR_SUCCESS) { + DebugLog((DEB_ERROR, "WNetAddConnection2W failed for home directory, error = %d", WNetResult)); + } + + Result = (WNetResult == ERROR_SUCCESS); + + } else { + DebugLog((DEB_ERROR, "Failed to get address of WNetAddConnection2W from mpr.dll")); + } +#endif + } else { + DebugLog((DEB_ERROR, "Winlogon failed to load mpr.dll for add connection")); + } + + // + // Make sure we don't try to start the network again. + // + + fFirstLogon = FALSE; + + // + // Unload mpr.dll. Keeping it open messes up Novell and Banyan. + // + + if ( pGlobals->hMPR ) { + + FreeLibrary(pGlobals->hMPR); + pGlobals->hMPR = NULL; + } + + // + // Revert to being 'ourself' + // + + if (!StopImpersonating(ImpersonationHandle)) { + DebugLog((DEB_ERROR, "AddNetworkConnection : Failed to revert to self")); + } + + return(Result); +} + + + +BOOL +UpdateUserEnvironment( + PVOID *pEnv + ) +{ + + HKEY KeyHandle; + DWORD Result; + DWORD ValueNameLength; + DWORD Type; + DWORD DataLength; + DWORD cValues; /* address of buffer for number of value identifiers */ + DWORD chMaxValueName; /* address of buffer for longest value name length */ + DWORD cbMaxValueData; /* address of buffer for longest value data length */ + DWORD junk; + FILETIME FileTime; + PTCHAR ValueName; + PTCHAR ValueData; + DWORD i; + BOOL Bool; + PTCHAR ExpandedValue; + BOOL rc = TRUE; + + DWORD ClassStringSize = MAX_PATH + 1; + TCHAR Class[MAX_PATH + 1]; + + Result = RegOpenKeyEx ( + HKEY_LOCAL_MACHINE, + KEY_NAME, + 0, + KEY_QUERY_VALUE, + &KeyHandle + ); + + if ( Result != ERROR_SUCCESS ) { + + DebugLog((DEB_ERROR, "RegOpenKeyEx failed, error = %d\n",Result)); + return( FALSE ); + } + + Result = RegQueryInfoKey( + KeyHandle, + Class, /* address of buffer for class string */ + &ClassStringSize, /* address of size of class string buffer */ + NULL, /* reserved */ + &junk, /* address of buffer for number of subkeys */ + &junk, /* address of buffer for longest subkey */ + &junk, /* address of buffer for longest class string length */ + &cValues, /* address of buffer for number of value identifiers */ + &chMaxValueName, /* address of buffer for longest value name length */ + &cbMaxValueData, /* address of buffer for longest value data length */ + &junk, /* address of buffer for descriptor length */ + &FileTime /* address of buffer for last write time */ + ); + + if ( Result != NO_ERROR && Result != ERROR_MORE_DATA ) { + RegCloseKey(KeyHandle); + return( FALSE ); + } + + // + // No need to adjust the datalength for TCHAR issues + // + + ValueData = Alloc( cbMaxValueData ); + + if ( ValueData == NULL ) { + RegCloseKey(KeyHandle); + return( FALSE ); + } + + // + // The maximum value name length comes back in characters, convert to bytes + // before allocating storage. Allow for trailing NULL also. + // + + ValueName = Alloc( (++chMaxValueName) * sizeof( TCHAR ) ); + + if ( ValueName == NULL ) { + + RegCloseKey(KeyHandle); + Free( ValueData ); + return( FALSE ); + } + + // + // To exit from here on, set rc and jump to Cleanup + // + + for (i=0; i<cValues ; i++) { + + ValueNameLength = chMaxValueName; + DataLength = cbMaxValueData; + + Result = RegEnumValue ( + KeyHandle, + i, + ValueName, + &ValueNameLength, // Size in TCHARs + NULL, + &Type, + (LPBYTE)ValueData, + &DataLength // Size in bytes + ); + + if ( Result != ERROR_SUCCESS ) { + + // + // Problem getting the value. We can either try + // the rest or punt completely. + // + + rc = FALSE; + goto Cleanup; + } + + // + // If the buffer size is greater than the max allowed, + // terminate the string at MAX_VALUE_LEN - 1. + // + + if (DataLength >= (MAX_VALUE_LEN * sizeof(TCHAR))) { + ValueData[MAX_VALUE_LEN-1] = TEXT('\0'); + } + + switch ( Type ) { + case REG_SZ: + { + + Bool = SetUserEnvironmentVariable( + pEnv, + ValueName, + ValueData, + TRUE + ); + + if ( !Bool ) { + + // + // Not much to do here. + // + + rc = FALSE; + goto Cleanup; + } + + break; + } + default: + { + continue; + } + } + } + + // + // To exit from here on, set rc and jump to Cleanup + // + + for (i=0; i<cValues ; i++) { + + ValueNameLength = chMaxValueName; + DataLength = cbMaxValueData; + + Result = RegEnumValue ( + KeyHandle, + i, + ValueName, + &ValueNameLength, // Size in TCHARs + NULL, + &Type, + (LPBYTE)ValueData, + &DataLength // Size in bytes + ); + + if ( Result != ERROR_SUCCESS ) { + + // + // Problem getting the value. We can either try + // the rest or punt completely. + // + + rc = FALSE; + goto Cleanup; + } + + // + // If the buffer size is greater than the max allowed, + // terminate the string at MAX_VALUE_LEN - 1. + // + + if (DataLength >= (MAX_VALUE_LEN * sizeof(TCHAR))) { + ValueData[MAX_VALUE_LEN-1] = TEXT('\0'); + } + + switch ( Type ) { + case REG_EXPAND_SZ: + { + + ExpandedValue = AllocAndExpandEnvironmentStrings( ValueData ); + + Bool = SetUserEnvironmentVariable( + pEnv, + ValueName, + ExpandedValue, + TRUE + ); + + Free( ExpandedValue ); + + if ( !Bool ) { + + // + // Not much to do here. + // + + rc = FALSE; + goto Cleanup; + } + + break; + } + default: + { + continue; + } + } + } + + +Cleanup: + + RegCloseKey(KeyHandle); + + Free( ValueName ); + Free( ValueData ); + + return( rc ); +} diff --git a/private/windows/gina/winlogon/envvar.h b/private/windows/gina/winlogon/envvar.h new file mode 100644 index 000000000..63c270f77 --- /dev/null +++ b/private/windows/gina/winlogon/envvar.h @@ -0,0 +1,73 @@ +/****************************** Module Header ******************************\ +* Module Name: envvar.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Define apis in envvar.c +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +// +// Prototypes +// + +BOOL +AppendNTPathWithAutoexecPath( + PVOID *pEnv, + LPTSTR lpPathVariable, + LPTSTR lpAutoexecPath + ); + +BOOL +CreateUserEnvironment( + PVOID *pEnv + ); + +BOOL +SetUserEnvironmentVariable( + PVOID *pEnv, + LPTSTR lpVariable, + LPTSTR lpValue, + BOOL bOverwrite + ); + +DWORD +ExpandUserEnvironmentStrings( + PVOID pEnv, + LPTSTR lpSrc, + LPTSTR lpDst, + DWORD nSize + ); + +BOOL +SetEnvironmentVariables( + PGLOBALS pGlobals, + PVOID *pEnv + ); + +BOOL +SetHomeDirectoryEnvVars( + PVOID *pEnv, + LPTSTR lpHomeDirectory, + LPTSTR lpHomeDrive, + LPTSTR lpHomeShare, + LPTSTR lpHomePath + ); + +BOOL +ProcessAutoexec( + PVOID *pEnv, + LPTSTR lpPathVariable + ); + +VOID +ChangeToHomeDirectory( + PGLOBALS pGlobals, + PVOID *pEnv, + LPTSTR lpHomeDir, + LPTSTR lpHomeDrive, + LPTSTR lpHomeShare, + LPTSTR lpHomePath + ); diff --git a/private/windows/gina/winlogon/ginamgr.c b/private/windows/gina/winlogon/ginamgr.c new file mode 100644 index 000000000..176e08b2e --- /dev/null +++ b/private/windows/gina/winlogon/ginamgr.c @@ -0,0 +1,220 @@ +/****************************** Module Header ******************************\ +* Module Name: ginamgr.c +* +* Copyright (c) 1991, Microsoft Corporation +* +* GINA Management code +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +#if DBG +DWORD GinaBreakFlags; +#endif + +PWLX_ISLOCKOK IsLockOkFn; + +BOOL +WINAPI +DummyWlxScreenSaverNotify( + PVOID pWlxContext, + BOOL * Secure) +{ + if ( *Secure && (IsLockOkFn != NULL ) ) + { + *Secure = IsLockOkFn( pWlxContext ); + } + + return( TRUE ); +} + +PGINASESSION +LoadGinaDll(PWSTR pszGinaDll) +{ + PGINASESSION pGinaSession; + HINSTANCE hDllInstance; + + hDllInstance = LoadLibrary(pszGinaDll); + + if (!hDllInstance) + { + DebugLog((DEB_ERROR, "Error %d loading Gina DLL %ws\n", GetLastError(), pszGinaDll)); + return(NULL); + } + + pGinaSession = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(GINASESSION)); + if (!pGinaSession) + { + return(NULL); + } + + pGinaSession->hInstance = hDllInstance; + + pGinaSession->pWlxNegotiate = (PWLX_NEGOTIATE) GetProcAddress(hDllInstance, WLX_NEGOTIATE_NAME); + if (!pGinaSession->pWlxNegotiate) + { + DebugLog((DEB_ERROR, "Could not find WlxNegotiate entry point\n")); + goto LoadGina_ErrorReturn; + } + + pGinaSession->pWlxInitialize = (PWLX_INITIALIZE) GetProcAddress(hDllInstance, WLX_INITIALIZE_NAME); + if (!pGinaSession->pWlxInitialize) + { + DebugLog((DEB_ERROR, "Could not find WlxInitialize entry point\n")); + goto LoadGina_ErrorReturn; + + } + + pGinaSession->pWlxDisplaySASNotice = (PWLX_DISPLAYSASNOTICE) GetProcAddress(hDllInstance, WLX_DISPLAYSASNOTICE_NAME); + if (!pGinaSession->pWlxDisplaySASNotice) + { + DebugLog((DEB_ERROR, "Could not find WlxDisplaySASNotice entry point\n")); + goto LoadGina_ErrorReturn; + } + + pGinaSession->pWlxLoggedOutSAS = (PWLX_LOGGEDOUTSAS) GetProcAddress(hDllInstance, WLX_LOGGEDOUTSAS_NAME); + if (!pGinaSession->pWlxLoggedOutSAS) + { + DebugLog((DEB_ERROR, "Could not find WlxLoggedOutSAS entry point\n")); + goto LoadGina_ErrorReturn; + } + + pGinaSession->pWlxActivateUserShell = (PWLX_ACTIVATEUSERSHELL) GetProcAddress(hDllInstance, WLX_ACTIVATEUSERSHELL_NAME); + if (!pGinaSession->pWlxActivateUserShell) + { + DebugLog((DEB_ERROR, "Could not find WlxActivateUserShell entry point\n")); + goto LoadGina_ErrorReturn; + } + + pGinaSession->pWlxLoggedOnSAS = (PWLX_LOGGEDONSAS) GetProcAddress(hDllInstance, WLX_LOGGEDONSAS_NAME); + if (!pGinaSession->pWlxLoggedOnSAS) + { + DebugLog((DEB_ERROR, "Could not find WlxLoggedOnSAS entry point\n")); + goto LoadGina_ErrorReturn; + } + + pGinaSession->pWlxDisplayLockedNotice = (PWLX_DISPLAYLOCKEDNOTICE) GetProcAddress(hDllInstance, WLX_DISPLAYLOCKED_NAME); + if (!pGinaSession->pWlxDisplayLockedNotice) + { + DebugLog((DEB_ERROR, "Could not find WlxDisplayLockedNotice\n")); + goto LoadGina_ErrorReturn; + } + + pGinaSession->pWlxWkstaLockedSAS = (PWLX_WKSTALOCKEDSAS) GetProcAddress(hDllInstance, WLX_WKSTALOCKEDSAS_NAME); + if (!pGinaSession->pWlxWkstaLockedSAS) + { + DebugLog((DEB_ERROR, "Could not find WlxWkstaLockedSAS entry point \n")); + goto LoadGina_ErrorReturn; + } + + pGinaSession->pWlxIsLockOk = (PWLX_ISLOCKOK) GetProcAddress(hDllInstance, WLX_ISLOCKOK_NAME); + if (!pGinaSession->pWlxIsLockOk) + { + DebugLog((DEB_ERROR, "Could not find WlxIsLockOk entry point")); + goto LoadGina_ErrorReturn; + } + IsLockOkFn = pGinaSession->pWlxIsLockOk; + + pGinaSession->pWlxIsLogoffOk = (PWLX_ISLOGOFFOK) GetProcAddress(hDllInstance, WLX_ISLOGOFFOK_NAME); + if (!pGinaSession->pWlxIsLogoffOk) + { + DebugLog((DEB_ERROR, "Could not find WlxIsLogoffOk entry point")); + goto LoadGina_ErrorReturn; + } + + pGinaSession->pWlxLogoff = (PWLX_LOGOFF) GetProcAddress(hDllInstance, WLX_LOGOFF_NAME); + if (!pGinaSession->pWlxLogoff) + { + DebugLog((DEB_ERROR, "Could not find WlxLogoff entry point\n")); + goto LoadGina_ErrorReturn; + } + + pGinaSession->pWlxShutdown = (PWLX_SHUTDOWN) GetProcAddress(hDllInstance, WLX_SHUTDOWN_NAME); + if (!pGinaSession->pWlxShutdown) + { + DebugLog((DEB_ERROR, "Could not find WlxShutdown entry point \n")); + goto LoadGina_ErrorReturn; + } + + + // + // New interfaces + // + + pGinaSession->pWlxStartApplication = (PWLX_STARTAPPLICATION) GetProcAddress(hDllInstance, WLX_STARTAPPLICATION_NAME); + if (!pGinaSession->pWlxStartApplication) + { + DebugLog((DEB_TRACE, "Could not find WlxStartApplication entry point \n")); + pGinaSession->pWlxStartApplication = WlxStartApplication; + } + pGinaSession->pWlxScreenSaverNotify = (PWLX_SSNOTIFY) GetProcAddress(hDllInstance, WLX_SSNOTIFY_NAME); + if (!pGinaSession->pWlxScreenSaverNotify) + { + pGinaSession->pWlxScreenSaverNotify = DummyWlxScreenSaverNotify; + } + + return(pGinaSession); + +LoadGina_ErrorReturn: + + LocalFree(pGinaSession); + return(NULL); +} + +void +GinaCatastrophicError(PGLOBALS pGlobals) +{ + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon); + MessageBox( NULL, + TEXT("The GINA dll has created an unrecoverable error\nand Windows NT will reboot after this"), + TEXT("Error!"), + MB_ICONSTOP | MB_OK | MB_SYSTEMMODAL); + RtlRaiseStatus(STATUS_LOGON_FAILURE); +} + +BOOL +DetermineUserInterface(PGLOBALS pGlobals) +{ + WCHAR szGinaDll[MAX_PATH]; + DWORD dwGinaLevel; + + GetProfileString(APPLICATION_NAME, GINA_KEY, TEXT("msgina.dll"), szGinaDll, MAX_PATH); + + pGlobals->pGina = LoadGinaDll(szGinaDll); + + if (!pGlobals->pGina) + { + ExitProcess( EXIT_GINA_ERROR ); + } + +#if DBG + if (TEST_FLAG(GinaBreakFlags, BREAK_NEGOTIATE)) + { + DebugBreak(); + } + try + { +#endif // Debug + + pGlobals->pGina->pWlxNegotiate(WLX_CURRENT_VERSION, &dwGinaLevel); +#if DBG + } + except(EXCEPTION_EXECUTE_HANDLER) + { + DebugLog((DEB_ERROR, "Exception in GINA dll\n")); + RtlRaiseStatus(GetExceptionCode()); + } +#endif + + if (dwGinaLevel > WLX_CURRENT_VERSION) + { + DebugLog((DEB_ERROR, "%ws is at version %d, can't support\n", szGinaDll, dwGinaLevel)); + ExitProcess( EXIT_GINA_ERROR ); + } + + return(TRUE); +} diff --git a/private/windows/gina/winlogon/ginamgr.h b/private/windows/gina/winlogon/ginamgr.h new file mode 100644 index 000000000..87cbf3aa3 --- /dev/null +++ b/private/windows/gina/winlogon/ginamgr.h @@ -0,0 +1,159 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1993. +// +// File: ginamgr.h +// +// Contents: +// +// Classes: +// +// Functions: +// +// History: 10-24-94 RichardW Created +// +//---------------------------------------------------------------------------- + + + +#ifdef TYPES_ONLY + +typedef +BOOL (WINAPI * PWLX_NEGOTIATE)( + DWORD, DWORD *); + +typedef +BOOL (WINAPI * PWLX_INITIALIZE)( + LPWSTR, HANDLE, PVOID, PVOID, PVOID *); + +typedef +VOID (WINAPI * PWLX_DISPLAYSASNOTICE)( + PVOID ); + +typedef +int (WINAPI * PWLX_LOGGEDOUTSAS)( + PVOID, DWORD, PLUID, PSID, PDWORD, PHANDLE, PWLX_MPR_NOTIFY_INFO, PVOID); + +typedef +BOOL (WINAPI * PWLX_ACTIVATEUSERSHELL)( + PVOID, PWSTR, PWSTR, PVOID); + +typedef +int (WINAPI * PWLX_LOGGEDONSAS)( + PVOID, DWORD, PVOID); + +typedef +VOID (WINAPI * PWLX_DISPLAYLOCKEDNOTICE)( + PVOID ); + +typedef +int (WINAPI * PWLX_WKSTALOCKEDSAS)( + PVOID, DWORD ); + +typedef +BOOL (WINAPI * PWLX_ISLOCKOK)( + PVOID ); + +typedef +BOOL (WINAPI * PWLX_ISLOGOFFOK)( + PVOID ); + +typedef +VOID (WINAPI * PWLX_LOGOFF)( + PVOID ); + +typedef +VOID (WINAPI * PWLX_SHUTDOWN)( + PVOID, DWORD ); + +typedef +BOOL (WINAPI * PWLX_STARTAPPLICATION)( + PVOID, PWSTR, PVOID, PWSTR); + +typedef +BOOL (WINAPI * PWLX_SSNOTIFY)( + PVOID, BOOL *); + +#define WLX_NEGOTIATE_NAME "WlxNegotiate" +#define WLX_INITIALIZE_NAME "WlxInitialize" +#define WLX_DISPLAYSASNOTICE_NAME "WlxDisplaySASNotice" +#define WLX_LOGGEDOUTSAS_NAME "WlxLoggedOutSAS" +#define WLX_ACTIVATEUSERSHELL_NAME "WlxActivateUserShell" +#define WLX_LOGGEDONSAS_NAME "WlxLoggedOnSAS" +#define WLX_DISPLAYLOCKED_NAME "WlxDisplayLockedNotice" +#define WLX_WKSTALOCKEDSAS_NAME "WlxWkstaLockedSAS" +#define WLX_ISLOCKOK_NAME "WlxIsLockOk" +#define WLX_ISLOGOFFOK_NAME "WlxIsLogoffOk" +#define WLX_LOGOFF_NAME "WlxLogoff" +#define WLX_SHUTDOWN_NAME "WlxShutdown" +#define WLX_STARTAPPLICATION_NAME "WlxStartApplication" +#define WLX_SSNOTIFY_NAME "WlxScreenSaverNotify" + + +typedef struct _GINASESSION { + struct _GINASESSION * pNext; + struct _GINASESSION * pPrev; + HANDLE hWlx; // Handle used by the DLL to call us + HANDLE hInstance; // Handle of the DLL + PVOID pGinaContext; // Pointer we store for them + DWORD cTimeout; // Current timeout, in sec. + PWLX_NEGOTIATE pWlxNegotiate; // WlxNegotiate function + PWLX_INITIALIZE pWlxInitialize; // WlxInitialize function + PWLX_DISPLAYSASNOTICE pWlxDisplaySASNotice; + PWLX_LOGGEDOUTSAS pWlxLoggedOutSAS; + PWLX_ACTIVATEUSERSHELL pWlxActivateUserShell; + PWLX_LOGGEDONSAS pWlxLoggedOnSAS; + PWLX_DISPLAYLOCKEDNOTICE pWlxDisplayLockedNotice; + PWLX_WKSTALOCKEDSAS pWlxWkstaLockedSAS; + PWLX_ISLOCKOK pWlxIsLockOk; + PWLX_ISLOGOFFOK pWlxIsLogoffOk; + PWLX_LOGOFF pWlxLogoff; + PWLX_SHUTDOWN pWlxShutdown; + PWLX_STARTAPPLICATION pWlxStartApplication; + PWLX_SSNOTIFY pWlxScreenSaverNotify; +} GINASESSION, * PGINASESSION; + + +#define BREAK_NEGOTIATE 0x00000001 +#define BREAK_INITIALIZE 0x00000002 +#define BREAK_DISPLAY 0x00000004 +#define BREAK_LOGGEDOUT 0x00000008 +#define BREAK_ACTIVATE 0x00000010 +#define BREAK_LOGGEDON 0x00000020 +#define BREAK_DISPLAYLOCKED 0x00000040 +#define BREAK_WKSTALOCKED 0x00000080 +#define BREAK_ISLOCKOK 0x00000100 +#define BREAK_ISLOGOFFOK 0x00000200 +#define BREAK_LOGOFF 0x00000400 +#define BREAK_SHUTDOWN 0x00000800 + +#define FLAG_ON(dw, f) dw |= (f) +#define FLAG_OFF(dw, f) dw &= (~(f)) +#define TEST_FLAG(dw, f) ((BOOL)(dw & (f))) + +#else // not TYPES_ONLY + +extern DWORD GinaBreakFlags; + +PGINASESSION +LoadGinaDll(PWSTR pszGinaDll); + + +BOOL +DetermineUserInterface(PGLOBALS pGlobals); + + +void +GinaCatastrophicError(PGLOBALS pGlobals); + +void +CADNotify(PGLOBALS pGlobals, DWORD SasType); + +#define IsShutdownReturn(Result) ((Result == WLX_SAS_ACTION_SHUTDOWN) || \ + (Result == WLX_SAS_ACTION_SHUTDOWN_REBOOT) || \ + (Result == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF) ) + + + +#endif diff --git a/private/windows/gina/winlogon/i386/os2ssmig.c b/private/windows/gina/winlogon/i386/os2ssmig.c new file mode 100644 index 000000000..6b6b2172b --- /dev/null +++ b/private/windows/gina/winlogon/i386/os2ssmig.c @@ -0,0 +1,2104 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + os2ssmig.c + +Abstract: + + This module contains the code necessary to initialize the + OS/2 SS entries in the registry. It migrates information + from os/2's config.sys file to the NT registry. + +Author: + + Ofer Porat (oferp) 30-Mar-1993 + +Environment: + + User Mode only + +Revision History: + + The code was originally in the os/2 subsystem, but was moved + to winlogon so the registry initialization can be done during + boot time. This way the registry info will be correct for the + first os/2 program to run. + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#define IF_OS2_DEBUG(x) // permanently enables KdPrint()s +static PVOID Os2Heap; // private heap used for allocs + +#define PATHLIST_MAX 1024 // max length of pathlists such as Os2LibPath (in characters) +#define MAX_CONSYS_SIZE 16384 // max size of config.sys buffers (in bytes) +#define DOS_DEV_LEN 12 // length of "\\DosDevices\\" + +// +// The following constants define the meaning of the Flags value in +// the config sys key entry: +// bit CONFSYSON_DISK -- 1 = os/2 config.sys on drive c, 0 = no os/2 config.sys on drive c +// bit DISABLEMIGTRATION -- if 1, migration from os/2 config.sys to NT registry is disabled. +// + +#define FLAGS_CONFIGSYSONDISK 0x1L +#define FLAGS_DISABLEMIGRATION 0x2L + +static WCHAR Os2SoftwareDirectory[] = L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft"; +static WCHAR Os2ProductDirectory[] = L"OS/2 Subsystem for NT"; +static WCHAR Os2VersionDirectory[] = L"1.0"; +static WCHAR Os2IniName[] = L"os2.ini"; +static WCHAR Os2ConfigSysName[] = L"config.sys"; +static WCHAR Os2ConfigFlagName[] = L"Flags"; +static WCHAR Os2ConfigStampName[] = L"FileTimeSizeStamp"; +static WCHAR Os2Class[] = L"OS2SS"; +static CHAR Os2ConfigSysDefaultValue[] = +// +// The '\a' in the following strings will be replaced by the SystemDirectory Value +// +"NTREM Here is a summary of what is allowed to appear in this registry entry:\0" +"NTREM Comments starting with REM will be visible to the user when s/he opens\0" +"NTREM c:\\config.sys.\0" +"NTREM Comments starting with NTREM are only visible by direct access to the\0" +"NTREM registry.\0" +"NTREM The following OS/2 configuration commands are significant:\0" +"NTREM COUNTRY=\0" +"NTREM CODEPAGE=\0" +"NTREM DEVINFO=KBD,\0" +"NTREM Any other commands apart from the exceptions listed below will be\0" +"NTREM visible to an OS/2 program that opens c:\\config.sys, however they are\0" +"NTREM not used internally by the NT OS/2 SubSystem.\0" +"NTREM Exceptions:\0" +"NTREM The following commands are completely ignored. Their true values\0" +"NTREM appear in the system environment and should be modified using the\0" +"NTREM Control Panel System applet. Note that LIBPATH is called Os2LibPath\0" +"NTREM in the NT system environment.\0" +"SET PATH=<ignored>\0" +"LIBPATH=<ignored>\0" +"NTREM In addition, any \"SET=\" commands (except COMSPEC) will be\0" +"NTREM completely ignored. You should set OS/2 environment variables just\0" +"NTREM like any other Windows NT environment variables by using the Control\0" +"NTREM Panel System applet.\0" +"NTREM If you have an OS/2 editor available, it is highly recommended that you\0" +"NTREM modify NT OS/2 config.sys configuration by editing c:\\config.sys with\0" +"NTREM this editor. This is the documented way to make such modification, and\0" +"NTREM is therefore less error-prone.\0" +"NTREM Now comes the actual text.\0" +"REM\0" +"REM This is a fake OS/2 config.sys file used by the NT OS/2 SubSystem.\0" +"REM The following information resides in the Registry and NOT in a disk file.\0" +"REM OS/2 Apps that access c:\\config.sys actually manipulate this information.\0" +"REM\0" +"PROTSHELL=c:\\os2\\pmshell.exe c:\\os2\\os2.ini c:\\os2\\os2sys.ini \a\\cmd.exe\0" +"SET COMSPEC=\a\\cmd.exe\0" +; + +static WCHAR Os2ConfigSysKeyName[] = +L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\OS/2 Subsystem for NT\\1.0\\config.sys"; +static WCHAR Os2EnvironmentDirectory[] = +L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"; +static HANDLE Os2EnvironmentKeyHandle = NULL; + +static WCHAR Os2OriginalCanonicalConfigSys[] = L"\\DosDevices\\C:\\CONFIG.SYS"; + +static BOOLEAN Os2LibPathFound = FALSE; +static WCHAR Os2LibPathValueName[] = L"Os2LibPath"; +static UNICODE_STRING Os2LibPathValueData_U; + +static PWSTR Os2PathString = NULL; + +static PWSTR pOs2ConfigSys = NULL; +static ULONG Os2SizeOfConfigSys = 0; +static PWSTR pOs2UpperCaseConfigSys = NULL; + +static WCHAR Os2SystemDirectory[MAX_PATH]; + + +VOID +Os2RegSetFlags( + IN HANDLE ConfigSysKeyHandle, + IN ULONG FlagsValue + ) + +/*++ + +Routine Description: + + Write the "Flags" value into the config.sys registry key. + +Arguments: + + ConfigSysKeyHandle -- supplies read/write handle to config.sys reg key + FlagsValue -- supplies the value to write + +Return Value: + + None. + +Note: + + Errors are ignored + +--*/ + +{ + UNICODE_STRING String_U; + NTSTATUS Status; + + RtlInitUnicodeString(&String_U, Os2ConfigFlagName); + + Status = NtSetValueKey( + ConfigSysKeyHandle, + &String_U, + 0L, + REG_DWORD, + &FlagsValue, + sizeof(DWORD) + ); + + if (!NT_SUCCESS(Status)) { +#if DBG + KdPrint(("Os2RegSetFlags: NetSetValueKey failed, Status = %lx\n", Status)); +#endif + } + + // ignore errors +} + + +VOID +Os2RegSetStamp( + IN HANDLE ConfigSysKeyHandle, + IN PLARGE_INTEGER pTimeStamp, + IN PLARGE_INTEGER pSizeStamp + ) + +/*++ + +Routine Description: + + Write the "FileTimeSizeStamp" value into the config.sys registry key. + +Arguments: + + ConfigSysKeyHandle -- supplies read/write handle to config.sys reg key + pTimeStamp, pSizeStamp -- supplies the value to write + +Return Value: + + None. + +Note: + + Errors are ignored + +--*/ + +{ + UNICODE_STRING String_U; + ULONG Buffer[4]; + NTSTATUS Status; + + Buffer[0] = pTimeStamp->LowPart; + Buffer[1] = (ULONG) pTimeStamp->HighPart; + Buffer[2] = pSizeStamp->LowPart; + Buffer[3] = (ULONG) pSizeStamp->HighPart; + + RtlInitUnicodeString(&String_U, Os2ConfigStampName); + + Status = NtSetValueKey( + ConfigSysKeyHandle, + &String_U, + 0L, + REG_BINARY, + (PVOID) Buffer, + sizeof(Buffer) + ); + + if (!NT_SUCCESS(Status)) { +#if DBG + KdPrint(("Os2RegSetFlags: NetSetValueKey failed, Status = %lx\n", Status)); +#endif + } + + // ignore errors +} + + +BOOLEAN +Os2InitOriginalConfigSysProcessing( + IN HANDLE ConfigSysKeyHandle, + IN ULONG FlagsValue, + IN HANDLE ConfigSysFileHandle, + IN PLARGE_INTEGER pTimeStamp, + IN PLARGE_INTEGER pSizeStamp + ) + +/*++ + +Routine Description: + + This function checks if there is an OS/2 config.sys configuration file on the + disk. If so, it opens it and reads it in. The file is converted to UNICODE, + and an upper case copy of it is made. + +Arguments: + + ConfigSysKeyHandle -- supplies a read/write handle to the config.sys reg key + this is used to update the flags/stamp values + FlagsValue -- The previously existing "Flags" value that was read in from the + registry. If this is -1, then no previous Flags value existed. + ConfigSysFileHandle -- An optional handle to an already open config.sys file + pTimeStamp, pSizeStamp -- info about the open config.sys file. + + Note: The parameters ConfigSysFileHandle + pTimeStamp + pSizeStamp are optional, + and come as a packet. If the config.sys file was previously opened, the handle + will be non-null, and all 3 params must be valid. If the config.sys file was + not previously opened (successfully), the handle should be NULL, and the other + 2 params need not be valid. + +Return Value: + + TRUE if there's an OS/2 config.sys, and all the operations needed to prepare + it have succeeded. FALSE otherwise. + +Notes: + On success Sets global variables as follows: + pOs2ConfigSys - a null-terminated UNICODE copy of the OS/2 config.sys. + pOs2UpperCaseConfigSys - an upper case copy of pOs2ConfigSys. + Os2SizeOfConfigSys - the number of characters in the above strings. + + ConfigSysFileHandle is closed before the function returns. + + The flags/stamp values in the config.sys reg key are updated if necessary. + +--*/ + +{ + UNICODE_STRING CanonicalConfigDotSys_U; + UNICODE_STRING Tmp_U; + ANSI_STRING Tmp_MB; + OBJECT_ATTRIBUTES Obja; + NTSTATUS Status; + IO_STATUS_BLOCK IoStatus; + PSZ pTempOs2ConfigSys; + LARGE_INTEGER TimeStamp, SizeStamp; + + if (FlagsValue != (ULONG)-1) { + + if (FlagsValue & FLAGS_DISABLEMIGRATION) { + + // + // ConfigSysFileHandle cannot be non-NULL at this point + // so there's no need to close it. Also, no need to + // update flags/stamp. + // + + return(FALSE); + } + + FlagsValue &= ~FLAGS_CONFIGSYSONDISK; + + } else { + FlagsValue = 0; + + // + // 1st time migration will always take place (after system setup) + // if you you want to disable post 1st-time migration, set FlagsValue + // to a default of FLAGS_DISABLEMIGRATION. If you don't any migration + // at all (not even 1st-time), then set the FlagsValue similarly, and + // also do a { Os2RegSetFlags(..), return FALSE } + // + } + + if (ConfigSysFileHandle == NULL) { + + // Try opening OS/2's config.sys file + + RtlInitUnicodeString(&CanonicalConfigDotSys_U, Os2OriginalCanonicalConfigSys); + + InitializeObjectAttributes(&Obja, + &CanonicalConfigDotSys_U, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenFile(&ConfigSysFileHandle, + FILE_GENERIC_READ, + &Obja, + &IoStatus, + FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (!NT_SUCCESS(Status)) { + + // + // Debug print removed, because this is a valid situation (no config.sys file) + // +#if 0 +#if DBG + KdPrint(("Os2InitOriginalConfigSysProcessing: FAILED - NtOpenFile-1 %lx\n", Status)); +#endif +#endif + Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); + return (FALSE); + } + + Status = Or2GetFileStamps( + ConfigSysFileHandle, + &TimeStamp, + &SizeStamp + ); + + if (!NT_SUCCESS(Status)) { +#if DBG + KdPrint(("Os2InitOriginalConfigSysProcessing: Can't get config.sys time/size stamp, Status = %lx\n", Status)); +#endif + + NtClose(ConfigSysFileHandle); + Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); + return(FALSE); + } + + pTimeStamp = &TimeStamp; + pSizeStamp = &SizeStamp; + } + + // Get the file length + + Os2SizeOfConfigSys = pSizeStamp->LowPart; + + // Allocate space for reading it in + + // the + 1 in following parameter is for inserting the NUL character + + pTempOs2ConfigSys = (PSZ) RtlAllocateHeap(Os2Heap, 0, Os2SizeOfConfigSys + 1); + + if (pTempOs2ConfigSys == NULL) { +#if DBG + KdPrint(("Os2InitOriginalConfigSysProcessing: FAILED - RtlAllocateHeap pTempOs2ConfigSys\n")); +#endif + Os2SizeOfConfigSys = 0; + NtClose(ConfigSysFileHandle); + Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); + return (FALSE); + } + + pOs2ConfigSys = (PWSTR) RtlAllocateHeap(Os2Heap, 0, (Os2SizeOfConfigSys + 1) * sizeof(WCHAR)); + + if (pOs2ConfigSys == NULL) { +#if DBG + KdPrint(("Os2InitOriginalConfigSysProcessing: FAILED - RtlAllocateHeap pOs2ConfigSys\n")); +#endif + Os2SizeOfConfigSys = 0; + RtlFreeHeap(Os2Heap, 0, pTempOs2ConfigSys); + NtClose(ConfigSysFileHandle); + Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); + return (FALSE); + } + + // Read it in + + Status = NtReadFile(ConfigSysFileHandle, + NULL, + NULL, + NULL, + &IoStatus, + (PVOID)pTempOs2ConfigSys, + Os2SizeOfConfigSys, + NULL, + NULL + ); + NtClose(ConfigSysFileHandle); + if (!NT_SUCCESS(Status)) { +#if DBG + KdPrint(("Os2InitOriginalConfigSysProcessing: FAILED - NtReadFile %lx\n", Status)); +#endif + Os2SizeOfConfigSys = 0; + RtlFreeHeap(Os2Heap, 0, pTempOs2ConfigSys); + RtlFreeHeap(Os2Heap, 0, pOs2ConfigSys); + pOs2ConfigSys = NULL; + Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); + return (FALSE); + } + + pTempOs2ConfigSys[Os2SizeOfConfigSys] = '\0'; + + // Convert to UNICODE + + RtlInitAnsiString(&Tmp_MB, pTempOs2ConfigSys); + + Tmp_U.Buffer = pOs2ConfigSys; + Tmp_U.MaximumLength = (USHORT) ((Os2SizeOfConfigSys + 1) * sizeof(WCHAR)); + + RtlOemStringToUnicodeString(&Tmp_U, (POEM_STRING)&Tmp_MB ,FALSE); + + Os2SizeOfConfigSys = Tmp_U.Length / sizeof(WCHAR); + + pOs2ConfigSys[Os2SizeOfConfigSys] = UNICODE_NULL; + + RtlFreeHeap(Os2Heap, 0, pTempOs2ConfigSys); + + // Prepare the upper case copy + + pOs2UpperCaseConfigSys = (PWSTR) RtlAllocateHeap(Os2Heap, 0, (Os2SizeOfConfigSys + 1) * sizeof(WCHAR)); + if (pOs2UpperCaseConfigSys == NULL) { +#if DBG + KdPrint(("Os2InitOriginalConfigSysProcessing: FAILED - RtlAllocateHeap pOs2UpperConfigSys\n")); +#endif + Os2SizeOfConfigSys = 0; + RtlFreeHeap(Os2Heap, 0, pOs2ConfigSys); + pOs2ConfigSys = NULL; + Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); + return (FALSE); + } + + wcscpy(pOs2UpperCaseConfigSys, pOs2ConfigSys); + Or2UnicodeStrupr(pOs2UpperCaseConfigSys); + + // + // Verify that the CONFIG.SYS file is really an OS/2 file and not a + // DOS file. + // Look for certain strings that MUST appear in an OS/2 CONFIG.SYS + // file and don't appear in DOS CONFIG.SYS files + // + + if ((wcsstr(pOs2UpperCaseConfigSys, L"LIBPATH") == NULL) || + (wcsstr(pOs2UpperCaseConfigSys, L"PROTSHELL") == NULL) || + (wcsstr(pOs2UpperCaseConfigSys, L"PROTECTONLY") == NULL) + ) { + Os2SizeOfConfigSys = 0; + RtlFreeHeap(Os2Heap, 0, pOs2ConfigSys); + RtlFreeHeap(Os2Heap, 0, pOs2UpperCaseConfigSys); + pOs2UpperCaseConfigSys = pOs2ConfigSys = NULL; + Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); + return (FALSE); + } + + FlagsValue |= FLAGS_CONFIGSYSONDISK; + + Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); + Os2RegSetStamp(ConfigSysKeyHandle, pTimeStamp, pSizeStamp); + + return (TRUE); +} + + +VOID +Os2SetDirectiveProcessingDispatchFunction( + IN ULONG DispatchTableIndex, + IN PVOID UserParameter, + IN PWSTR Name, + IN ULONG NameLen, + IN PWSTR Value, + IN ULONG ValueLen + ) + +/*++ + +Routine Description: + + This is a Dispatch Routine that is used to process SET directives in OS/2's config.sys + file. The directives are entered into the system environment. + +Arguments: + + Standard arguments passed to a Dispatch Function, see the description of + Or2IterateEnvironment in os2ssrtl.c + + UserParameter - points to a pointer which indicates the current position in the + config.sys registry entry we're building. + +Return Value: + + None. + +--*/ + +{ + + UNICODE_STRING VarName_U; // for setting up variable name + UNICODE_STRING VarValue_U; // for setting up variable value + PWSTR Dest = *(PWSTR *) UserParameter; + NTSTATUS Status; +// ULONG ResultLength; + WCHAR wch; +// KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; + + // Set up variable name + + VarName_U.Buffer = Value; + VarName_U.Length = 0; + + while ((ValueLen > 0) && (*Value != L'=')) { + Value++; + VarName_U.Length += sizeof(WCHAR); + ValueLen--; + } + + if (ValueLen == 0 || // End of line reached without finding '=' + VarName_U.Length == 0) { // Empty name + return; + } + + VarName_U.MaximumLength = VarName_U.Length; + + // Following SET directives are ignored + + if (Or2UnicodeEqualCI(VarName_U.Buffer, L"COMSPEC=", 8) || +// Or2UnicodeEqualCI(VarName_U.Buffer, L"PATH=", 5) || + Or2UnicodeEqualCI(VarName_U.Buffer, L"VIDEO_DEVICES=", 14) || + Or2UnicodeEqualCI(VarName_U.Buffer, L"VIO_IBMVGA=", 11) || + Or2UnicodeEqualCI(VarName_U.Buffer, L"VIO_VGA=", 8) || + Or2UnicodeEqualCI(VarName_U.Buffer, L"PROMPT=", 7) + ) { + return; + } + + // Here, we have a valid name, followed by '=' + Value++; // Skip the '=' + ValueLen--; + + // Set up variable value + + VarValue_U.Buffer = Value; + VarValue_U.Length = (USHORT) (ValueLen * sizeof(WCHAR)); // what's left of the line + VarValue_U.MaximumLength = VarValue_U.Length; + + // + // Update the information in the registry with the info + // in the file + // + +#if 0 + // not in effect anymore + // + // The KEYS variable is handled in a special way. + // It's put in the registry config.sys in order to prevent possible + // conflict. + // + + if (Or2UnicodeEqualCI(VarName_U.Buffer, L"KEYS=", 5)) { + RtlMoveMemory(Dest, L"SET ", 8); + Dest += 4; + RtlMoveMemory(Dest, VarName_U.Buffer, VarName_U.Length); + Dest += VarName_U.Length / sizeof(WCHAR); + *Dest++ = L'='; + RtlMoveMemory(Dest, VarValue_U.Buffer, VarValue_U.Length); + Dest += VarValue_U.Length / sizeof(WCHAR); + *Dest++ = UNICODE_NULL; + *(PWSTR *) UserParameter = Dest; + return; + } +#endif + + // + // If Os2EnvironmentKeyHandle is NULL, we don't have + // write access to the system environment, and we skip + // setting the variable. + // + + if (Os2EnvironmentKeyHandle == NULL) { + return; + } + + // + // Special handling for PATH -- retained in the global variable + // Os2PathString. Only the last occurence of PATH is retained. + // This is compatible with OS/2 which uses the last occurence. + // + + if (Or2UnicodeEqualCI(VarName_U.Buffer, L"PATH=", 5)) { + + if (Os2PathString != NULL) { // already found a path? + + RtlFreeHeap(Os2Heap, 0, Os2PathString); // get rid of it + Os2PathString = NULL; + } + + Os2PathString = (PWSTR) RtlAllocateHeap(Os2Heap, 0, VarValue_U.Length + sizeof(WCHAR)); + + if (Os2PathString == NULL) { +#if DBG + KdPrint(("Os2SetDirectiveProcessingDispatchFunction: Failed to allocate heap space for PATH\n")); +#endif + + return; // no space for storing the path -- skip it + } + + // switch to the upper case copy... + + VarValue_U.Buffer = pOs2UpperCaseConfigSys + (VarValue_U.Buffer - pOs2ConfigSys); + + RtlMoveMemory(Os2PathString, VarValue_U.Buffer, VarValue_U.Length); + Os2PathString[VarValue_U.Length/sizeof(WCHAR)] = UNICODE_NULL; + + return; + } + + // + // Otherwise, it's stored in the system environment + // + + // + // If a value key of the same name already exists, + // don't replace it. This is done in order to prevent + // the OS/2 SS from overriding possible NT definitions + // + + // + // !!! Modification 5/30/93 -- We decided to be a little more brave about this. + // On one hand there doesn't seem to be any conflict between any possible OS/2 + // definitions, and NT definitions. On the other hand, since we now do re-migration + // on every config.sys change, if we don't update existing variables, the user's + // important OS/2 variables (e.g. DPATH) will not get remigrated. Therefore we + // always update. -- oferp + // + +#if 0 + + Status = NtQueryValueKey(Os2EnvironmentKeyHandle, + &VarName_U, + KeyValuePartialInformation, + &KeyValueInfo, + sizeof(KeyValueInfo), + &ResultLength + ); +#else + + Status = STATUS_OBJECT_NAME_NOT_FOUND; + +#endif + + + if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { + + // + // Set the system wide variable to the value specified + // in the original OS/2 config.sys + // + + wch = Value[ValueLen]; + Value[ValueLen] = UNICODE_NULL; + + Status = NtSetValueKey(Os2EnvironmentKeyHandle, + &VarName_U, + (ULONG)0, + REG_EXPAND_SZ, + VarValue_U.Buffer, + VarValue_U.Length + sizeof(WCHAR) + ); + + Value[ValueLen] = wch; + + if (!NT_SUCCESS(Status)) + { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SetDirectiveProcessingDispatchFunction: Unable to NtSetValueKey() system env, rc = %X\n", + Status)); + } +#endif + return; + } + + } else { +#if DBG + if (Status != STATUS_BUFFER_OVERFLOW) { + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SetDirectiveProcessingDispatchFunction: Unable to NtQueryValueKey() system env, rc = %X\n", + Status)); + } + } +#endif + return; + } +} + + +VOID +Os2CommaProcessingDispatchFunction( + IN ULONG DispatchTableIndex, + IN PVOID UserParameter, + IN PWSTR Name, + IN ULONG NameLen, + IN PWSTR Value, + IN ULONG ValueLen + ) + +/*++ + +Routine Description: + + This is a Dispatch Routine that is used to process certain directives in OS/2's config.sys + file. The directives are copied into the config.sys registry entry we're building. + Some directives are truncated after a certain number of commas. + + !!! Modification 5/30/93 -- Migration of the three NLS related statements has been disabled. + Reason: The intended default behavior is this: The registry config.sys entry has no + NLS entries, and the OS/2 subsystem picks up these values from NT's default values. + Supporting the registry config.sys values is only meant to be used if the user actually + wants to have separate OS/2 and NT NLS parameters. This is almost never the case. The + reason for allowing dual-NLS mode is because some NT languages may not be supported on + OS/2, and so the user may want to specify a different (existing) NLS setting for the + OS/2 subsystem. The usual setting is therefore to have no NLS directives in the registry + config.sys. That's why we shouldn't migrate. -- oferp + +Arguments: + + Standard arguments passed to a Dispatch Function, see the description of + Or2IterateEnvironment in os2ssrtl.c + + UserParameter - points to a pointer which indicates the current position in the + config.sys registry entry we're building. + +Return Value: + + None. + +--*/ + +{ +// +// Lines passed to this dispatch function need to be copied to the output as are +// except they should be truncated after a certain number of commas. The +// following table lists the number of commas. 0 means no truncation. +// + +#if 0 + ULONG ItemTable[] = { 1, // COUNTRY + 0, // CODEPAGE + 2 // DEVINFO (only with KBD) + }; + PWSTR Dest = *(PWSTR *) UserParameter; + ULONG CommaCtr; + + if (DispatchTableIndex== 2 && !Or2UnicodeEqualCI(Value, L"KBD,", 4)) { + return; + } + + // First, copy the Name + + RtlMoveMemory(Dest, Name, NameLen * sizeof(WCHAR)); + Dest += NameLen; + *Dest++ = L'='; + + // Now, copy the value for the right number of commas + + CommaCtr = 0; + + while (ValueLen > 0) { + if (ItemTable[DispatchTableIndex] != 0 && *Value == L',') { + CommaCtr++; + if (CommaCtr == ItemTable[DispatchTableIndex]) { + break; + } + } + + *Dest++ = *Value++; + ValueLen--; + } + *Dest++ = UNICODE_NULL; + *(PWSTR *) UserParameter = Dest; + +#else + + return; // do nothing! + +#endif +} + + +VOID +Os2ProcessOriginalConfigSys( + IN OUT PWSTR *DestPtr + ) + +/*++ + +Routine Description: + + This function processes OS/2's config.sys file after it has been properly initialized + by Os2InitOriginalConfigSysProcessing. + +Arguments: + + DestPtr - points to a pointer which indicates the current position in the + config.sys registry entry we're building. + +Return Value: + + None. + +--*/ + +{ + static ENVIRONMENT_DISPATCH_TABLE_ENTRY DispatchTable[] = + { + { L"COUNTRY", L"=", Os2CommaProcessingDispatchFunction, NULL }, + { L"CODEPAGE", L"=", Os2CommaProcessingDispatchFunction, NULL }, + { L"DEVINFO", L"=", Os2CommaProcessingDispatchFunction, NULL }, + { L"LIBPATH", L"=", Or2FillInSearchRecordDispatchFunction, NULL }, + { L"SET", L" \t", Os2SetDirectiveProcessingDispatchFunction, NULL } + }; + ENVIRONMENT_SEARCH_RECORD LibPathRecord; + ULONG i; + PWSTR p; + WCHAR ch; + + // + // Most of the job is done by Or2IterateEnvironment which processes the file + // according to a dispatch table. + // + // LibPath needs to be handled a little differently. We need to process only + // the *last* occurence of LIBPATH= in the file (this is the same as OS/2). + // Therefore we only record the position of the LIBPATH= statments as we run + // into them. After we're finished we'll have the position of the last one, + // and we can process that line. + // + + for (i = 0; i < 5; i++) { + DispatchTable[i].UserParameter = (PVOID) DestPtr; + } + + DispatchTable[3].UserParameter = (PVOID)&LibPathRecord; + LibPathRecord.DispatchTableIndex = (ULONG)-1; + + Or2IterateEnvironment(pOs2ConfigSys, + DispatchTable, + 5, + CRLF_DELIM); + + + if (Os2LibPathFound && LibPathRecord.DispatchTableIndex != (ULONG)-1) { + + // handle LIBPATH if there was one + + // get a pointer to the upper case version, so we can append it + + p = pOs2UpperCaseConfigSys + (LibPathRecord.Value - pOs2ConfigSys); + + ch = p[LibPathRecord.ValueLen]; + p[LibPathRecord.ValueLen] = UNICODE_NULL; + + Or2AppendPathToPath(Os2Heap, + p, + &Os2LibPathValueData_U, + TRUE); + p[LibPathRecord.ValueLen] = ch; + } +} + + +VOID +Os2TerminateOriginalConfigSysProcessing( + VOID + ) + +/*++ + +Routine Description: + + Cleans up processing of OS/2's config.sys. Releases the storage used to store the + file. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + RtlFreeHeap(Os2Heap, 0, pOs2ConfigSys); + RtlFreeHeap(Os2Heap, 0, pOs2UpperCaseConfigSys); + pOs2UpperCaseConfigSys = pOs2ConfigSys = NULL; + Os2SizeOfConfigSys = 0; +} + + +NTSTATUS +Os2SbBuildSD( + PSECURITY_DESCRIPTOR SecurityDescriptor, + PACL *pDacl + ) + +/*++ + +Routine Description: + + Builds a security descriptor for creating our registry keys. + + Administrators -- all access + everyone -- read access + +Arguments: + + SecurityDescriptor -- supplies a pointer to a preallocated SD that will be built + pDacl -- returns a pointer to a dacl that should be released from Os2Heap after + we're finished using the security descriptor. + +Return Value: + + NT error code. + +--*/ + +{ + PACL Dacl; + PACE_HEADER Pace; + PSID AdminAliasSid; + ULONG DaclSize; + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY; + PSID WorldSid; + NTSTATUS Status; + + // + // Create the SIDs for local admin and World. + // + + Status = RtlAllocateAndInitializeSid( + &NtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &AdminAliasSid + ); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbBuildSD: RtlAllocateAndInitializeSid(Admin), Status = %lx\n", Status)); + } +#endif + return (Status); + } + + Status = RtlAllocateAndInitializeSid( + &WorldSidAuthority, + 1, + SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, + &WorldSid + ); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbBuildSD: RtlAllocateAndInitializeSid(World), Status = %lx\n", Status)); + } +#endif + RtlFreeSid( AdminAliasSid ); + return (Status); + } + + Status = RtlCreateSecurityDescriptor( SecurityDescriptor, + SECURITY_DESCRIPTOR_REVISION ); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbBuildSD: RtlCreateSecurityDescriptor, Status = %lx\n", Status)); + } +#endif + RtlFreeSid( AdminAliasSid ); + RtlFreeSid( WorldSid ); + return (Status); + } + + + // + // Compute the size of the buffer needed for the + // DACL. + // + + DaclSize = sizeof( ACL ) + + 2 * (sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG )) + + + RtlLengthSid( AdminAliasSid ) + + RtlLengthSid( WorldSid ); + + Dacl = (PACL) RtlAllocateHeap(Os2Heap, 0, DaclSize); + + if (Dacl == NULL) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbBuildSD: RtlAllocateHeap failed\n")); + } +#endif + RtlFreeSid( AdminAliasSid ); + RtlFreeSid( WorldSid ); + return(STATUS_NO_MEMORY); + } + + // + // Build the ACL + // + + Status = RtlCreateAcl ( Dacl, DaclSize, ACL_REVISION2 ); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbBuildSD: RtlCreateAcl, Status = %lx\n", Status)); + } +#endif + RtlFreeHeap(Os2Heap, 0, Dacl); + RtlFreeSid( AdminAliasSid ); + RtlFreeSid( WorldSid ); + return (Status); + } + + Status = RtlAddAccessAllowedAce ( + Dacl, + ACL_REVISION2, + GENERIC_ALL, + AdminAliasSid + ); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbBuildSD: RtlAddAccessAllowedAce(Admin), Status = %lx\n", Status)); + } +#endif + RtlFreeHeap(Os2Heap, 0, Dacl); + RtlFreeSid( AdminAliasSid ); + RtlFreeSid( WorldSid ); + return (Status); + } + + Status = RtlAddAccessAllowedAce ( + Dacl, + ACL_REVISION2, + GENERIC_READ, + WorldSid + ); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbBuildSD: RtlAddAccessAllowedAce(World), Status = %lx\n", Status)); + } +#endif + RtlFreeHeap(Os2Heap, 0, Dacl); + RtlFreeSid( AdminAliasSid ); + RtlFreeSid( WorldSid ); + return (Status); + } + + Status = RtlGetAce( + Dacl, + 0L, + &Pace + ); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbBuildSD: RtlGetAce(Admin), Status = %lx\n", Status)); + } +#endif + RtlFreeHeap(Os2Heap, 0, Dacl); + RtlFreeSid( AdminAliasSid ); + RtlFreeSid( WorldSid ); + return (Status); + } + + Pace->AceFlags |= CONTAINER_INHERIT_ACE; + + Status = RtlGetAce( + Dacl, + 1L, + &Pace + ); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbBuildSD: RtlGetAce(World), Status = %lx\n", Status)); + } +#endif + RtlFreeHeap(Os2Heap, 0, Dacl); + RtlFreeSid( AdminAliasSid ); + RtlFreeSid( WorldSid ); + return (Status); + } + + Pace->AceFlags |= CONTAINER_INHERIT_ACE; + + // + // Add the ACL to the security descriptor + // + + Status = RtlSetDaclSecurityDescriptor( + SecurityDescriptor, + TRUE, + Dacl, // put (PACL) NULL to allow everyone all access + FALSE ); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbBuildSD: RtlSetDaclSecurityDescriptor, Status = %lx\n", Status)); + } +#endif + RtlFreeHeap(Os2Heap, 0, Dacl); + RtlFreeSid( AdminAliasSid ); + RtlFreeSid( WorldSid ); + return (Status); + } + + // + // These have been copied into the security descriptor, so + // we can free them. + // + + RtlFreeSid( AdminAliasSid ); + + RtlFreeSid( WorldSid ); + + *pDacl = Dacl; + + return(STATUS_SUCCESS); +} + + +NTSTATUS +Os2SbInitializeRegistryKeys( + OUT PHANDLE phConfigSysKeyHandle, + OUT PHANDLE phEnvironmentKeyHandle + ) + +/*++ + +Routine Description: + + This routine creates the OS/2 subsystem key hierarchy in the registry. It also opens + the config.sys key, and opens the system environment key. + +Arguments: + + phConfigSysKeyHandle - Returns a READ/WRITE handle to the config.sys key in the registry. + + phEnvironmentKeyHandle - Returns a READ/WRITE handle to the system environment key. + If this key can't be opened due to access denied, NULL is returned and the + return value will be STATUS_SUCCESS. + + Note --- If the return value is not a success return value, none of the above return + variables can be considered to be valid. + +Return Value: + + The value is an NTSTATUS type that is returned when some failure occurs. It may + indicate any of several errors that occur during the APIs called in this function. + The return value should be tested with NT_SUCCESS(). + +--*/ + +{ + OBJECT_ATTRIBUTES Obja; + UNICODE_STRING Class_U; + UNICODE_STRING SoftwareDirectory_U; + UNICODE_STRING ProductDirectory_U; + UNICODE_STRING VersionDirectory_U; + UNICODE_STRING Os2IniName_U; + UNICODE_STRING ConfigSysName_U; + UNICODE_STRING EnvRegDir_U; + HANDLE SoftwareKeyHandle; + HANDLE ProductKeyHandle; + HANDLE VersionKeyHandle; + HANDLE Os2IniKeyHandle; + HANDLE ConfigSysKeyHandle; + HANDLE EnvironmentKeyHandle; + ULONG Disposition; + NTSTATUS Status; + SECURITY_DESCRIPTOR localSecurityDescriptor; + PSECURITY_DESCRIPTOR securityDescriptor; + PACL Dacl = NULL; + + + *phConfigSysKeyHandle = NULL; + *phEnvironmentKeyHandle = NULL; + + // We start off by creating/opening the registry key hierarchy for our subsystem + + securityDescriptor = &localSecurityDescriptor; + + Status = Os2SbBuildSD(securityDescriptor, + &Dacl); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbInitializeRegistryKeys: Os2SbBuildSD failed, Status = %lx\n", Status)); + } +#endif + return (Status); + } + + RtlInitUnicodeString(&Class_U, Os2Class); + + RtlInitUnicodeString(&SoftwareDirectory_U, Os2SoftwareDirectory); + InitializeObjectAttributes(&Obja, + &SoftwareDirectory_U, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenKey(&SoftwareKeyHandle, + KEY_CREATE_SUB_KEY, + &Obja + ); + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbInitializeRegistryKeys: Can't open software key, rc = %lx\n", Status)); + } +#endif + if (Dacl != NULL) { + RtlFreeHeap(Os2Heap, 0, Dacl); + } + return (Status); + } + + RtlInitUnicodeString(&ProductDirectory_U, Os2ProductDirectory); + InitializeObjectAttributes(&Obja, + &ProductDirectory_U, + OBJ_CASE_INSENSITIVE, + SoftwareKeyHandle, + securityDescriptor); + + Status = NtCreateKey(&ProductKeyHandle, + KEY_CREATE_SUB_KEY, + &Obja, + 0, + &Class_U, + REG_OPTION_NON_VOLATILE, + &Disposition + ); + NtClose(SoftwareKeyHandle); + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbInitializeRegistryKeys: Can't create product key, rc = %lx\n", Status)); + } +#endif + if (Dacl != NULL) { + RtlFreeHeap(Os2Heap, 0, Dacl); + } + return (Status); + } + + RtlInitUnicodeString(&VersionDirectory_U, Os2VersionDirectory); + InitializeObjectAttributes(&Obja, + &VersionDirectory_U, + OBJ_CASE_INSENSITIVE, + ProductKeyHandle, + securityDescriptor); + + Status = NtCreateKey(&VersionKeyHandle, + KEY_CREATE_SUB_KEY, + &Obja, + 0, + &Class_U, + REG_OPTION_NON_VOLATILE, + &Disposition + ); + NtClose(ProductKeyHandle); + if (!NT_SUCCESS(Status)) + { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbInitializeRegistryKeys: Can't create version key, rc = %lx\n", Status)); + } +#endif + if (Dacl != NULL) { + RtlFreeHeap(Os2Heap, 0, Dacl); + } + return (Status); + } + + RtlInitUnicodeString(&Os2IniName_U, Os2IniName); + InitializeObjectAttributes(&Obja, + &Os2IniName_U, + OBJ_CASE_INSENSITIVE, + VersionKeyHandle, + securityDescriptor); + + Status = NtCreateKey(&Os2IniKeyHandle, + KEY_CREATE_SUB_KEY, + &Obja, + 0, + &Class_U, + REG_OPTION_NON_VOLATILE, + &Disposition + ); + if (!NT_SUCCESS(Status)) + { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbInitializeRegistryKeys: Can't create os2ini key, rc = %lx\n", Status)); + } +#endif + NtClose(VersionKeyHandle); + if (Dacl != NULL) { + RtlFreeHeap(Os2Heap, 0, Dacl); + } + return (Status); + } + NtClose(Os2IniKeyHandle); + + RtlInitUnicodeString(&ConfigSysName_U, Os2ConfigSysName); + InitializeObjectAttributes(&Obja, + &ConfigSysName_U, + OBJ_CASE_INSENSITIVE, + VersionKeyHandle, + securityDescriptor); + + Status = NtCreateKey(&ConfigSysKeyHandle, + KEY_READ | KEY_WRITE, + &Obja, + 0, + &Class_U, + REG_OPTION_NON_VOLATILE, + &Disposition + ); + + if (Dacl != NULL) { + RtlFreeHeap(Os2Heap, 0, Dacl); + } + NtClose(VersionKeyHandle); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbInitializeRegistryKeys: Can't create/open config.sys key, rc = %lx\n", + Status)); + } +#endif + return(Status); + } + + // Open the environment. + + RtlInitUnicodeString(&EnvRegDir_U, Os2EnvironmentDirectory); + InitializeObjectAttributes(&Obja, + &EnvRegDir_U, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenKey(&EnvironmentKeyHandle, + KEY_READ | KEY_WRITE, + &Obja + ); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbInitializeRegistryKeys: Unable to NtOpenKey() the system environment, rc = %lx\n", + Status)); + } +#endif + if (Status != STATUS_ACCESS_DENIED) { + NtClose(ConfigSysKeyHandle); + return(Status); + } + + // + // on denied access, return with no error so we can at least create the + // config.sys value entry + // The caller will know this occured because the environment handle is null. + // + + } else { + *phEnvironmentKeyHandle = EnvironmentKeyHandle; + } + + *phConfigSysKeyHandle = ConfigSysKeyHandle; + return (STATUS_SUCCESS); +} + + +NTSTATUS +Os2SbInitializeRegistry( + IN ULONG FlagsValue, + IN HANDLE ConfigSysFileHandle, + IN PLARGE_INTEGER pTimeStamp, + IN PLARGE_INTEGER pSizeStamp + ) + +/*++ + +Routine Description: + + This function is responsible for initializing the entire Registry component of the + Subsystem. It generates the key hierarchy in the registry. + + In the generation of the key hierarchy, it also generates a config.sys entry. + The information in this entry is taken from the following sources: + + -- some default strings we put in (see Os2ConfigSysDefaultValue). + -- Information from OS/2's config.sys file is added as follows: + + > SET commands are put in the system environment. + Some SET commands are ignored (see Os2SetDirectiveProcessingDispatchFunction) + > LIBPATH commands are merged to the Os2LibPath variable in the + system environment. A terminating semicolon is added if there + is only one path in the path list. + > SET PATH= is ignored, and the system path remains the same. + +Arguments: + + FlagsValue -- The previously existing "Flags" value that was read in from the + registry. If this is -1, then no previous Flags value existed. + ConfigSysFileHandle -- An optional handle to an already open config.sys file + pTimeStamp, pSizeStamp -- info about the open config.sys file. + + Note: The parameters ConfigSysFileHandle + pTimeStamp + pSizeStamp are optional, + and come as a packet. If the config.sys file was previously opened, the handle + will be non-null, and all 3 params must be valid. If the config.sys file was + not previously opened (successfully), the handle should be NULL, and the other + 2 params need not be valid. + +Return Value: + + The value is an NTSTATUS type that is returned when some failure occurs. It may + indicate any of several errors that occur during the APIs called in this function. + The return value should be tested with NT_SUCCESS(). If an unsuccessful value + is returned, it means the registry component was not properly initialized. + +--*/ + +{ + UNICODE_STRING Os2LibPathValueName_U; + HANDLE ConfigSysKeyHandle; + NTSTATUS Status; + UNICODE_STRING ConfigSysName_U; + PWCHAR pInfo; + PUCHAR Src; + PWCHAR Src1; + PWCHAR Dest; + WCHAR ch, ch1; + + // Create the key hierarchy + + Status = Os2SbInitializeRegistryKeys(&ConfigSysKeyHandle, + &Os2EnvironmentKeyHandle); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbInitializeRegistry: failed Os2SbInitializeRegistryKeys, Status = %lx\n", Status)); + } +#endif + return(Status); + } + + // + // Create the config.sys entry + // and migrate information from os/2's config.sys file. + // + + Os2LibPathFound = FALSE; + Os2LibPathValueData_U.Buffer = NULL; + + if (Os2EnvironmentKeyHandle == NULL) { // we have no write access to the environment + goto Os2NoEnvAccess; // skip over the Os2LibPath stuff + } + + // Get Os2LibPath from sys env so we can update it. + + if (!Or2GetEnvPath(&Os2LibPathValueData_U, + Os2Heap, + PATHLIST_MAX, + Os2EnvironmentKeyHandle, + Os2LibPathValueName, + FALSE)) { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbInitializeRegistry: Unable to fetch Os2LibPath from sys env\n")); + } +#endif + } else { + + // make sure there's at lease one semicolon + + Or2CheckSemicolon(&Os2LibPathValueData_U); + + Os2LibPathFound = TRUE; + } + + if (Os2LibPathFound && Os2LibPathValueData_U.Length == 0) { // it's empty (or nonexistent) + // set up a default + UNICODE_STRING tmp; + + RtlInitUnicodeString(&tmp, Os2SystemDirectory); + + RtlCopyUnicodeString(&Os2LibPathValueData_U, &tmp); + + RtlAppendUnicodeToString(&Os2LibPathValueData_U, L"\\os2\\dll;"); + + Os2LibPathValueData_U.Buffer[Os2LibPathValueData_U.Length/sizeof(WCHAR)] = UNICODE_NULL; + } + +Os2NoEnvAccess: + + // Now set up the initial regisry config.sys entry. + + // Allocate a buffer to build the config.sys entry in. (it's a multi-string) + + pInfo = (PWCHAR) RtlAllocateHeap(Os2Heap, 0, MAX_CONSYS_SIZE); + if (pInfo == NULL) + { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbInitializeRegistry: Unable to RtlAllocateHeap() space for config.sys value buffer\n")); + } +#endif + if (Os2LibPathValueData_U.Buffer != NULL) { + RtlFreeHeap(Os2Heap, 0, Os2LibPathValueData_U.Buffer); + Os2LibPathValueData_U.Buffer = NULL; + Os2LibPathFound = FALSE; + } + if (Os2EnvironmentKeyHandle != NULL) { + NtClose(Os2EnvironmentKeyHandle); + Os2EnvironmentKeyHandle = NULL; + } + NtClose(ConfigSysKeyHandle); + + return (STATUS_NO_MEMORY); + } + + // Initially, copy our default value into the entry. + + Src = (PUCHAR) Os2ConfigSysDefaultValue; + Dest = pInfo; + do + { + ch = (WCHAR) *Src++; + if (ch == L'\a') + { + Src1 = Os2SystemDirectory; + ch1 = *Src1++; + while (ch1 != UNICODE_NULL) + { + *Dest++ = ch1; + ch1 = *Src1++; + } + } + else + { + *Dest++ = ch; + } + } while (!((ch == UNICODE_NULL) && (*Src == '\0'))); + + + // check if the an OS/2 CONFIG.SYS file already exists on + // the drive. Its name will be C:\CONFIG.SYS + // If it exists, process it for additional information + // The system env will also be updated + + if (Os2InitOriginalConfigSysProcessing( + ConfigSysKeyHandle, + FlagsValue, + ConfigSysFileHandle, + pTimeStamp, + pSizeStamp + )) { + + if (Os2EnvironmentKeyHandle == NULL) { // we have no write access to the environment + + // + // We couldn't open a write key to the sys env for some reason, so we + // won't be able to migrate SET variables to NT from config.sys. + // Perhaps a popup should be generated to notify the user of this at + // this point. + // Anyway, we go on and only write the config.sys entry, skipping + // variable migration. + // +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbInitializeRegistry: Initializing registry without writing system env\n")); + } +#endif + } + + Os2ProcessOriginalConfigSys(&Dest); + Os2TerminateOriginalConfigSysProcessing(); + } +#if 0 + + // + // The code below is disabled. We'll add COUNTRY= when creating a fictive + // config.sys on the fly. If the code is reenabled in the future -- it + // should be fixed. It doesn't make sure there are at least three characters + // (with leading 0s) in the country code. + // + + else { + + WCHAR CountryCode[7]; + ULONG CountryLength; + // + // add a default "COUNTRY=" line + // + + CountryLength = GetLocaleInfoW( + GetSystemDefaultLCID(), + LOCALE_ICOUNTRY, + CountryCode, + 6); + + if (CountryLength == 0) { +#if DBG + KdPrint(("Os2SbInitializeRegistry: Cannot get locale country info, LastError = %lx\n", + GetLastError())); +#endif + } else { + + RtlMoveMemory(Dest, L"COUNTRY=", 16); + Dest += 8; + + RtlMoveMemory(Dest, CountryCode, CountryLength * sizeof(WCHAR)); + Dest += CountryLength; + + *Dest++ = UNICODE_NULL; + } + } +#endif + + // Write the new Os2LibPath + + if (Os2LibPathFound) { + + RtlInitUnicodeString(&Os2LibPathValueName_U, Os2LibPathValueName); + Status = NtSetValueKey(Os2EnvironmentKeyHandle, + &Os2LibPathValueName_U, + (ULONG)0, + REG_EXPAND_SZ, + Os2LibPathValueData_U.Buffer, + Os2LibPathValueData_U.Length + sizeof(WCHAR) + ); + +#if DBG + if (!NT_SUCCESS(Status)) + { + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbInitializeRegistry: Unable to NtSetValueKey() system environment, rc = %X\n", + Status)); + } + } +#endif + + Os2LibPathFound = FALSE; + RtlFreeHeap(Os2Heap, 0, Os2LibPathValueData_U.Buffer); + Os2LibPathValueData_U.Buffer = NULL; + } + + // handle a possible PATH value setting + + if (Os2PathString != NULL) { + + UNICODE_STRING Os2PathValueName_U; + UNICODE_STRING Os2PathValueData_U; + + // + // Os2EnvironmentKeyHandle can't be NULL at this point because + // we wouldn't have set Os2PathString... + // + + if (!Or2GetEnvPath(&Os2PathValueData_U, + Os2Heap, + PATHLIST_MAX, + Os2EnvironmentKeyHandle, + L"Path", + FALSE)) { +#if DBG + KdPrint(("Os2SbInitializeRegistry: Unable to fetch Path from sys env\n")); +#endif + // + // Can't get value, skip processing + // + + } else { + + if (Os2PathValueData_U.Length == 0) { + + // + // If we couldn't get the value, or it's empty, don't process it + // + +#if DBG + KdPrint(("Os2SbInitializeRegistry: Path in sys env empty or not found\n")); +#endif + } else { + + // + // Append our string... + // + + Or2AppendPathToPath(Os2Heap, + Os2PathString, + &Os2PathValueData_U, + TRUE); + + // + // Put it back into the registry... + // + + RtlInitUnicodeString(&Os2PathValueName_U, L"Path"); + Status = NtSetValueKey(Os2EnvironmentKeyHandle, + &Os2PathValueName_U, + (ULONG)0, + REG_EXPAND_SZ, + Os2PathValueData_U.Buffer, + Os2PathValueData_U.Length + sizeof(WCHAR) + ); + +#if DBG + if (!NT_SUCCESS(Status)) { + KdPrint(("Os2SbInitializeRegistry: Unable to NtSetValueKey() system environment (2), rc = %X\n", + Status)); + } +#endif + // ignore error from setvaluekey + } + + RtlFreeHeap(Os2Heap, 0, Os2PathValueData_U.Buffer); + } + + RtlFreeHeap(Os2Heap, 0, Os2PathString); + Os2PathString = NULL; + } + + if (Os2EnvironmentKeyHandle != NULL) { + NtClose(Os2EnvironmentKeyHandle); + Os2EnvironmentKeyHandle = NULL; + } + + // set the REG_MULTI_SZ terminating NUL + + *Dest++ = UNICODE_NULL; + + // Finally, write the config.sys multi-string entry into the registry. + + RtlInitUnicodeString(&ConfigSysName_U, Os2ConfigSysName); + Status = NtSetValueKey(ConfigSysKeyHandle, + &ConfigSysName_U, + (ULONG)0, + REG_MULTI_SZ, + (PVOID)pInfo, + (PBYTE)Dest - (PBYTE)pInfo + ); + RtlFreeHeap(Os2Heap, 0, pInfo); + if (!NT_SUCCESS(Status)) + { +#if DBG + IF_OS2_DEBUG( INIT ) { + KdPrint(("Os2SbInitializeRegistry: Unable to NtSetValueKey() registry config.sys, rc = %X\n", + Status)); + } +#endif + NtClose(ConfigSysKeyHandle); + return (Status); + } + + NtClose(ConfigSysKeyHandle); + return (STATUS_SUCCESS); +} + + +VOID +Os2MigrationProcedure( + VOID + ) + +/*++ + +Routine Description: + + This routine oversees the os/2 config.sys migration procedure. It's called from winlogon during + boot. It checks to see if the os/2ss config.sys-related registy entries are intact. If they + aren't, it rebuilds them. If they are intact, it checks if config.sys migration is enabled, + and if the os/2 config.sys file exists and has changed (size/lastwritetime) since the last migration. + If so, it remigrates the config.sys info. Note that SET variables are only added, never + removed. The same goes for LIBPATH. PATH is not migrated from os/2 config.sys to NT. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + BYTE RegBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 4 * sizeof(ULONG)]; + OBJECT_ATTRIBUTES Obja; + IO_STATUS_BLOCK IoStatus; + UNICODE_STRING String_U; + LARGE_INTEGER TimeStamp; + LARGE_INTEGER SizeStamp; + PKEY_VALUE_PARTIAL_INFORMATION pInfo = (PKEY_VALUE_PARTIAL_INFORMATION) RegBuffer; + PULONG RegVal; + HANDLE ConfigSysKeyHandle; + HANDLE ConfigSysFileHandle = NULL; + ULONG FlagsValue = (ULONG)-1; + ULONG ResultLength; + NTSTATUS Status; + + RtlInitUnicodeString(&String_U, Os2ConfigSysKeyName); + InitializeObjectAttributes(&Obja, + &String_U, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenKey(&ConfigSysKeyHandle, + KEY_READ, + &Obja + ); + + if (!NT_SUCCESS(Status)) { + + if (Status != STATUS_OBJECT_PATH_NOT_FOUND && + Status != STATUS_OBJECT_NAME_NOT_FOUND && + Status != STATUS_OBJECT_PATH_INVALID) { +#if DBG + KdPrint(("Os2MigrationProcedure: Can't Open config.sys registry key, Status = %lx\n", Status)); +#endif + return; + } + + goto BuildRegistry; + } + + RtlInitUnicodeString(&String_U, Os2ConfigFlagName); + Status = NtQueryValueKey(ConfigSysKeyHandle, + &String_U, + KeyValuePartialInformation, + pInfo, + sizeof(RegBuffer), + &ResultLength + ); + + if (!NT_SUCCESS(Status)) { + + NtClose(ConfigSysKeyHandle); + + if (Status != STATUS_OBJECT_PATH_NOT_FOUND && + Status != STATUS_OBJECT_NAME_NOT_FOUND && + Status != STATUS_OBJECT_PATH_INVALID) { +#if DBG + KdPrint(("Os2MigrationProcedure: Can't Read config.sys flag value key, Status = %lx\n", Status)); +#endif + return; + } + + goto BuildRegistry; + } + + if (pInfo->Type != REG_DWORD || + pInfo->DataLength != sizeof(DWORD)) { + NtClose(ConfigSysKeyHandle); + goto BuildRegistry; + } + + FlagsValue = *(PULONG) (pInfo->Data); + + RtlInitUnicodeString(&String_U, Os2ConfigSysName); + Status = NtQueryValueKey(ConfigSysKeyHandle, + &String_U, + KeyValuePartialInformation, + NULL, + 0, + &ResultLength + ); + + // + // The 2 expected status results are: + // + // STATUS_OBJECT_NAME_NOT_FOUND - config.sys not yet defined + // STATUS_BUFFER_TOO_SMALL - config.sys already defined + // + + if (Status == STATUS_OBJECT_NAME_NOT_FOUND || + Status == STATUS_OBJECT_PATH_NOT_FOUND || + Status == STATUS_OBJECT_PATH_INVALID) { + NtClose(ConfigSysKeyHandle); + goto BuildRegistry; + } + + if (Status != STATUS_BUFFER_TOO_SMALL) { +#if DBG + KdPrint(("Os2MigrationProcedure: Can't Read config.sys registry value key, Status = %lx\n", Status)); +#endif + NtClose(ConfigSysKeyHandle); + return; + } + + if ((FlagsValue & FLAGS_DISABLEMIGRATION) || + !(FlagsValue & FLAGS_CONFIGSYSONDISK)) { + + // + // os/2 config.sys migration disabled + // or no os/2 config.sys on disk + // + + NtClose(ConfigSysKeyHandle); + return; + } + + RtlInitUnicodeString(&String_U, Os2ConfigStampName); + Status = NtQueryValueKey(ConfigSysKeyHandle, + &String_U, + KeyValuePartialInformation, + pInfo, + sizeof(RegBuffer), + &ResultLength + ); + + NtClose(ConfigSysKeyHandle); + + if (!NT_SUCCESS(Status)) { + + if (Status != STATUS_OBJECT_PATH_NOT_FOUND && + Status != STATUS_OBJECT_NAME_NOT_FOUND && + Status != STATUS_OBJECT_PATH_INVALID) { +#if DBG + KdPrint(("Os2MigrationProcedure: Can't Read config.sys stamp value key, Status = %lx\n", Status)); +#endif + return; + } + + goto BuildRegistry; + } + + if (pInfo->Type != REG_BINARY || + pInfo->DataLength != 4 * sizeof(DWORD)) { + goto BuildRegistry; + } + + RegVal = (PULONG) (pInfo->Data); + + RtlInitUnicodeString(&String_U, Os2OriginalCanonicalConfigSys); + + InitializeObjectAttributes(&Obja, + &String_U, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenFile(&ConfigSysFileHandle, + FILE_GENERIC_READ, + &Obja, + &IoStatus, + FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (!NT_SUCCESS(Status)) { + goto BuildRegistry; + } + + Status = Or2GetFileStamps( + ConfigSysFileHandle, + &TimeStamp, + &SizeStamp + ); + + if (!NT_SUCCESS(Status)) { +#if DBG + KdPrint(("Os2MigrationProcedure: Can't get config.sys time/size stamp, Status = %lx\n", Status)); +#endif + + NtClose(ConfigSysFileHandle); + ConfigSysFileHandle = NULL; + goto BuildRegistry; + } + + if (TimeStamp.LowPart == RegVal[0] && + (ULONG) TimeStamp.HighPart == RegVal[1] && + SizeStamp.LowPart == RegVal[2] && + (ULONG) SizeStamp.HighPart == RegVal[3]) { + + // + // os/2 config.sys hasn't changed, skip processing + // + + NtClose(ConfigSysFileHandle); + return; + } + + // + // We need to install the registry stuff + // + +BuildRegistry: + + if (GetSystemDirectoryW(Os2SystemDirectory, MAX_PATH) == 0) { +#if DBG + KdPrint(("Os2MigrationProcedure: Cannot obtain name of system directory, LastError = %lx\n", + GetLastError())); +#endif + if (ConfigSysFileHandle != NULL) { + NtClose(ConfigSysFileHandle); + } + return; + } + + // + // Create a heap to use for dynamic memory allocation. + // + + Os2Heap = RtlCreateHeap( HEAP_GROWABLE, + NULL, + 0x10000L, // Initial size of heap is 64K + 0x1000L, // Commit an initial page + NULL, + NULL // Reserved + ); + if (Os2Heap == NULL) { +#if DBG + KdPrint(("Os2MigrationProcedure: Error at RtlCreateHeap of Os2Heap\n")); +#endif + if (ConfigSysFileHandle != NULL) { + NtClose(ConfigSysFileHandle); + } + return; + } + + Status = Os2SbInitializeRegistry( + FlagsValue, + ConfigSysFileHandle, + &TimeStamp, + &SizeStamp + ); + + if (!NT_SUCCESS(Status)) { +#if DBG + KdPrint(("Os2MigrationProcedure: Os2SbInitializeRegistry() failed, Status = %lx\n", Status)); +#endif + } + + // + // ConfigSysFileHandle is closed by Os2SbInitializeRegistry + // + + RtlDestroyHeap(Os2Heap); +} diff --git a/private/windows/gina/winlogon/i386/os2ssmig.h b/private/windows/gina/winlogon/i386/os2ssmig.h new file mode 100644 index 000000000..56a10edc0 --- /dev/null +++ b/private/windows/gina/winlogon/i386/os2ssmig.h @@ -0,0 +1,24 @@ +/****************************** Module Header ******************************\ +* Module Name: os2ssmig.h +* +* Copyright (c) 1993, Microsoft Corporation +* +* Import file for the OS/2 Subsystem migration code. Only applicable to x86 builds. +* +* History: +* 03-30-93 OferP Created. +\***************************************************************************/ + +#ifndef __OS2SSMIG_H +#define __OS2SSMIG_H + +#ifdef _X86_ + +VOID +Os2MigrationProcedure( + VOID + ); + +#endif +#endif + diff --git a/private/windows/gina/winlogon/i386/os2ssrtl.c b/private/windows/gina/winlogon/i386/os2ssrtl.c new file mode 100644 index 000000000..eda1612de --- /dev/null +++ b/private/windows/gina/winlogon/i386/os2ssrtl.c @@ -0,0 +1,1037 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + os2ssrtl.c + +Abstract: + + This module contains CONFIG.SYS related parsing routines, and some other + routines needed to support os/2ss migration. + +Author: + + Ofer Porat (oferp) 8-Nov-1992 + +Environment: + + User Mode Only + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#define IF_OD2_DEBUG(x) // permanently enable debug prints + +#define WBLANK(ch) ((ch) == L' ' || (ch) == L'\t') + +// +// used for text processing in Or2ReplacePathByPath() +// +typedef struct _INDEX_T { + + UNICODE_STRING Src; + UNICODE_STRING Dest; + +} INDEX_T, *PINDEX_T; + + + +// +// This routine skips over white space in a unicode string +// either forward or backward. +// Str -- address of pointer to string. +// Direction -- either FWD or BWD. +// + +VOID +Or2SkipWWS( + IN OUT PWSTR *Str, + IN LONG Direction + ) +{ + PWSTR q = *Str; + + while (WBLANK(*q)) { + q += Direction; + } + *Str = q; +} + + +// +// This routine converts a null terminated unicode string to upper case. +// It is similar to wcsupr(). It exists because I'm not sure wcsupr() +// actually uses the Unicode convention. +// +// Str -- string to convert. +// + +VOID +Or2UnicodeStrupr( + IN OUT PWSTR Str + ) +{ + while (*Str != UNICODE_NULL) { + *Str = RtlUpcaseUnicodeChar(*Str); + Str++; + } +} + + +// +// This routine compares 2 null-terminated unicode strings. The comparison +// has a count limiting the number of chars compared, and the comparison is +// case insensitive. It is similar to wcsnicmp(). It exists because I'm not +// sure wcsnicmp() actually uses the Unicode convention. +// +// Str1, Str2 -- strings to compare. +// Count -- max # of chars to compare. +// return value -- TRUE if they're equal to within Count characters. FALSE otherwise. +// + +BOOLEAN +Or2UnicodeEqualCI( + IN PWSTR Str1, + IN PWSTR Str2, + IN ULONG Count + ) +{ + while (Count != 0) { + + if (*Str1 == UNICODE_NULL) { + + if (*Str2 == UNICODE_NULL) { + return(TRUE); + } + + return(FALSE); + } + + if (RtlUpcaseUnicodeChar(*Str1) != RtlUpcaseUnicodeChar(*Str2)) { + + return(FALSE); + } + + Str1++; Str2++; Count--; + } + return(TRUE); +} + + +// +// This routine appends a source path list to a destination path list. multiple occurences +// of the same path are removed. +// +// HeapHandle -- a handle to a heap for temporary allocations +// SrcPath -- a null terminated unicode string. It must be in uppercase for elimination of +// multiple paths to occur. This path list is appended to DestPath. +// DestPath -- an already existing counted unicode string containing the path list to be appended +// to. The case is unimportant. The result is stored back in this string. +// ExpandIt -- Should be TRUE in DestPath contains unexpanded %...% type strings. FALSE otherwise. +// return value -- TRUE on success, FALSE otherwise. +// +// Possible Errors Effect +// =============== ====== +// allocation failure Does nothing, return value FALSE +// can't expand the +// DestPath Does nothing, return value FALSE +// DestPath MaxLen +// exceeded during +// append Stop on the last path that fits, return value TRUE +// + +BOOLEAN +Or2AppendPathToPath( + IN PVOID HeapHandle, + IN PWSTR SrcPath, + IN OUT PUNICODE_STRING DestPath, + IN BOOLEAN ExpandIt + ) +{ + WCHAR wch; + WCHAR wch1; + WCHAR wch2; + PWSTR p; + PWSTR q; + PWSTR r; + PWSTR t; + USHORT l; + USHORT addsemi; + UNICODE_STRING Expanded; + UNICODE_STRING Tmp; + BOOLEAN Found; + NTSTATUS Status; + + Expanded.Buffer = (PWSTR) RtlAllocateHeap(HeapHandle, 0, DestPath->MaximumLength + sizeof(WCHAR)); + + if (Expanded.Buffer == NULL) { +#if DBG + IF_OD2_DEBUG( INIT ) { + KdPrint(("Or2AppendPathPath: can't allocate Expanded from heap\n")); + } +#endif + return(FALSE); + } + + Expanded.MaximumLength = DestPath->MaximumLength; + + if (ExpandIt) { + + Status = RtlExpandEnvironmentStrings_U(NULL, + DestPath, + &Expanded, + NULL); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OD2_DEBUG( INIT ) { + KdPrint(("Or2AppendPathToPath: can't expand environment strings, rc = %lx\n", Status)); + } +#endif + RtlFreeHeap(HeapHandle, 0, Expanded.Buffer); + return(FALSE); + } + + } else { + + RtlCopyUnicodeString(&Expanded, DestPath); + } + + Expanded.Buffer[Expanded.Length/sizeof(WCHAR)] = UNICODE_NULL; + + Or2UnicodeStrupr(Expanded.Buffer); + + addsemi = 2; + + if (Expanded.Length == 0 || + Expanded.Buffer[Expanded.Length/sizeof(WCHAR) - 1] == L';') { + + addsemi = 0; + } + + while (TRUE) { + + Or2SkipWWS(&SrcPath, FWD); + + wch = *SrcPath; + + if (wch == UNICODE_NULL) { + break; + } + + if (wch == L';') { + SrcPath++; + continue; + } + + p = wcschr(SrcPath, L';'); + + if (p == NULL) { + + p = SrcPath + wcslen(SrcPath); + q = p - 1; + + } else { + + q = p - 1; + p++; + } + + Or2SkipWWS(&q, BWD); + + l = q - SrcPath + 1; + + wch = *(q+1); + *(q+1) = UNICODE_NULL; + t = Expanded.Buffer; + Found = FALSE; + + while (TRUE) { + + r = wcsstr(t, SrcPath); + + if (r == NULL) { + break; + } + + if (r == Expanded.Buffer) { + wch1 = L';'; + } else { + wch1 = *(r-1); + } + + wch2 = r[l]; + + if ((wch1 == L';' || WBLANK(wch1)) && + (wch2 == L';' || WBLANK(wch2) || wch2 == UNICODE_NULL)) { + + Found = TRUE; + break; + } + + t = r + l; + } + + *(q+1) = wch; + + if (Found) { + SrcPath = p; + continue; + } + + Tmp.Buffer = SrcPath; + Tmp.Length = l * sizeof(WCHAR); + Tmp.MaximumLength = Tmp.Length; + + if (Expanded.Length + addsemi + Tmp.Length > Expanded.MaximumLength || + DestPath->Length + addsemi + Tmp.Length > DestPath->MaximumLength) { + +#if DBG + IF_OD2_DEBUG( INIT ) { + KdPrint(("Or2AppendPathToPath: Path buffer overflow\n")); + } +#endif + break; // out of string space, terminate processing. + } + + if (addsemi) { + + RtlAppendUnicodeToString(&Expanded, L";"); + RtlAppendUnicodeToString(DestPath, L";"); + + } else { + + addsemi = 2; + } + + RtlAppendUnicodeStringToString(&Expanded, &Tmp); + RtlAppendUnicodeStringToString(DestPath, &Tmp); + + Expanded.Buffer[Expanded.Length/sizeof(WCHAR)] = UNICODE_NULL; + + SrcPath = p; + } + + DestPath->Buffer[DestPath->Length / sizeof(WCHAR)] = UNICODE_NULL; + + RtlFreeHeap(HeapHandle, 0, Expanded.Buffer); + return(TRUE); +} + + +// +// This routine takes a counted unicode string containing a path-list with %...% type strings in it. +// It prepares a pool of characters containing the expansions of paths with %...% strings in them. +// Also prepared is an index table. Each entry in the index table contains a counted string pointing +// to the point in DestPath where the unexpanded path is, and a counted string pointing to the the pool +// where the expanded path is. +// +// SrcPath -- a counted unicode string containing the path-list to generate an index for. +// Pool -- a buffer containing space for SrcPath.MaximumLength wide characters. This is where +// the expanded strings will be stored. +// Ind -- a buffer containing enough space to allocate as many index entries as necessary to index +// the path list. One way to know how many may be necessary is to count the # of semicolons in +// SrcPath. +// IndSize -- will contain the number of entries placed in Ind on exit. +// +// Possible Errors Effect +// =============== ====== +// SrcPath.MaximumLength +// characters are not +// enough to store the +// pool of expanded env +// strings. Stops after the last expanded env string that fits in the pool. +// + +static +VOID +Or2IndexPath( + IN PUNICODE_STRING SrcPath, + OUT PWSTR Pool, + OUT PINDEX_T Ind, + OUT PULONG IndSize + ) +{ + PWSTR CurPool = Pool; + USHORT PoolQuota = SrcPath->MaximumLength; + ULONG IndEx = 0; + PWCHAR wp = SrcPath->Buffer; + PWCHAR wq, wr; + BOOLEAN HasPercent; + NTSTATUS Status; + + for (; ; ) { + + Or2SkipWWS(&wp, FWD); + + if (*wp == UNICODE_NULL) { + break; + } + + HasPercent = FALSE; + + wq = wp; + + while (*wq != L';' && *wq != UNICODE_NULL) { + if (*wq == L'%') { + HasPercent = TRUE; + } + wq++; + } + + if (*wq == L';') { + wr = wq + 1; + } else { + wr = wq; + } + + if (!HasPercent) { + wp = wr; + continue; + } + + wq--; + + Or2SkipWWS(&wq, BWD); + + Ind[IndEx].Dest.Buffer = wp; + Ind[IndEx].Dest.Length = (wq - wp + 1) * sizeof(WCHAR); + Ind[IndEx].Dest.MaximumLength = Ind[IndEx].Dest.Length; + + wp = wr; + + Ind[IndEx].Src.Buffer = CurPool; + Ind[IndEx].Src.MaximumLength = PoolQuota; + + Status = RtlExpandEnvironmentStrings_U(NULL, + &Ind[IndEx].Dest, + &Ind[IndEx].Src, + NULL); + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OD2_DEBUG( INIT ) { + KdPrint(("Or2IndexPath: can't expand environment strings, rc = %lx\n", Status)); + } +#endif + break; + } + + PoolQuota -= Ind[IndEx].Src.Length; + CurPool += Ind[IndEx].Src.Length / sizeof(WCHAR); + + Ind[IndEx].Src.MaximumLength = Ind[IndEx].Src.Length; + + IndEx++; + } + + *IndSize = IndEx; +} + + +// +// This routine takes a destination path-list and a source path list. The destination path-list +// is completely replaced with the source path list. The destination list may contain %...% strings. +// The source list is not expected to contain %...% strings. While the replacement is done, any +// paths in the source which already exist in the destination in a form containing %...% strings, are +// retained with their original %...% form. +// +// HeapHandle -- a handle to a heap for temporary allocations +// SrcPath -- a null terminated unicode string containing the replacing path-list. +// DestPath -- an already existing counted unicode string containing the path list to be replaced +// to. The result is stored back in this string. +// return value -- TRUE on success, FALSE otherwise. +// +// Possible Errors Effect +// =============== ====== +// allocation failure Does nothing, return value FALSE +// DestPath MaxLen +// exceeded during +// replace Stop on the last path that fits, return value TRUE +// + +BOOLEAN +Or2ReplacePathByPath( + IN PVOID HeapHandle, + IN PWSTR SrcPath, + IN OUT PUNICODE_STRING DestPath + ) +{ + PINDEX_T Ind; + PWSTR Pool; + PWSTR p; + PWSTR q; + WCHAR wch; + ULONG IndSize; + ULONG SemiCount; + ULONG i; + USHORT addsemi; + UNICODE_STRING Target; + UNICODE_STRING Tmp; + + Target.Buffer = (PWSTR) RtlAllocateHeap(HeapHandle, 0, DestPath->MaximumLength + sizeof(WCHAR)); + + if (Target.Buffer == NULL) { +#if DBG + IF_OD2_DEBUG( INIT ) { + KdPrint(("Or2ReplacePathByPath: can't allocate Target from heap\n")); + } +#endif + return(FALSE); + } + + Target.MaximumLength = DestPath->MaximumLength; + Target.Length = 0; + + Pool = (PWSTR) RtlAllocateHeap(HeapHandle, 0, (ULONG) DestPath->MaximumLength); + + if (Pool == NULL) { +#if DBG + IF_OD2_DEBUG( INIT ) { + KdPrint(("Or2ReplacePathByPath: can't allocate Pool from heap\n")); + } +#endif + RtlFreeHeap(HeapHandle, 0, Target.Buffer); + return(FALSE); + } + + SemiCount = 1; + + for (i = 0; i < DestPath->Length/sizeof(WCHAR); i++) { + if (DestPath->Buffer[i] == L';') { + SemiCount++; + } + } + + Ind = (PINDEX_T) RtlAllocateHeap(HeapHandle, 0, SemiCount * sizeof(INDEX_T)); + + if (Ind == NULL) { +#if DBG + IF_OD2_DEBUG( INIT ) { + KdPrint(("Or2ReplacePathByPath: can't allocate Ind from heap\n")); + } +#endif + RtlFreeHeap(HeapHandle, 0, Pool); + RtlFreeHeap(HeapHandle, 0, Target.Buffer); + return(FALSE); + } + + Or2IndexPath(DestPath, Pool, Ind, &IndSize); + + addsemi = 0; + + while (TRUE) { + + Or2SkipWWS(&SrcPath, FWD); + + wch = *SrcPath; + + if (wch == UNICODE_NULL) { + break; + } + + if (wch == L';') { + SrcPath++; + continue; + } + + p = wcschr(SrcPath, L';'); + + if (p == NULL) { + + p = SrcPath + wcslen(SrcPath); + q = p - 1; + + } else { + + q = p - 1; + p++; + } + + Or2SkipWWS(&q, BWD); + + Tmp.Buffer = SrcPath; + Tmp.Length = (q - SrcPath + 1) * sizeof(WCHAR); + Tmp.MaximumLength = Tmp.Length; + + for (i = 0; i < IndSize; i++) { + + if (RtlEqualUnicodeString(&Tmp, &Ind[i].Src, TRUE)) { + + Tmp = Ind[i].Dest; + break; + } + } + + if (Target.Length + addsemi + Tmp.Length > Target.MaximumLength) { + +#if DBG + IF_OD2_DEBUG( INIT ) { + KdPrint(("Or2ReplacePathByPath: Path buffer overflow\n")); + } +#endif + break; // out of string space, terminate processing. + } + + if (addsemi) { + + RtlAppendUnicodeToString(&Target, L";"); + + } else { + + addsemi = 2; + } + + RtlAppendUnicodeStringToString(&Target, &Tmp); + + SrcPath = p; + } + + RtlCopyUnicodeString(DestPath, &Target); + + DestPath->Buffer[DestPath->Length/sizeof(WCHAR)] = UNICODE_NULL; + + RtlFreeHeap(HeapHandle, 0, Ind); + RtlFreeHeap(HeapHandle, 0, Pool); + RtlFreeHeap(HeapHandle, 0, Target.Buffer); + return(TRUE); +} + + +// +// This routine appends a terminating semicolon to a path list, if that path list +// does not have a semicolon in it (in other words, if the path list contains only +// one path). +// +// Str - The string to do semicolon processing on. +// + +VOID +Or2CheckSemicolon( + IN OUT PUNICODE_STRING Str + ) +{ + USHORT i; + BOOLEAN flag; + + if (Str->Length == 0 || Str->Length >= Str->MaximumLength) { + return; + } + + flag = FALSE; + + for(i = 0; i < Str->Length/ (USHORT) sizeof(WCHAR); i++) { + if (Str->Buffer[i] == L';') { + flag = TRUE; + break; + } + } + + if (!flag) { + Str->Buffer[i++] = L';'; + Str->Buffer[i] = UNICODE_NULL; + Str->Length += sizeof(WCHAR); + } +} + + +// +// This routine fetches a path-list type environment variable from the registry. +// +// Data - an unallocated counted unicode string to hold the result. +// HeapHandle -- a handle to a heap where we allocate from. +// MaxSiz -- The MaximumLength Data should be given. +// EnvKey -- a handle to the registry key in which the environment to be searched is. +// ValueName -- a null-terminated unicode name of the registry value to fetch. +// ExpandIt -- If TRUE, the registry value is expanded for %..% type strings before returning it. +// return value -- TRUE on success, FALSE otherwise. +// +// Possible Errors Effect +// =============== ====== +// allocation failure Data.Buffer = NULL, return value FALSE +// requested env variable +// doesn't exist, or +// can't query its value Data is allocated and given 0 length, return value TRUE +// can't expand the +// env variable Data is allocated and given 0 length, return value TRUE +// + +BOOLEAN +Or2GetEnvPath( + OUT PUNICODE_STRING Data, + IN PVOID HeapHandle, + IN USHORT MaxSiz, + IN HANDLE EnvKey, + IN PWSTR ValueName, + IN BOOLEAN ExpandIt + ) +{ + PKEY_VALUE_PARTIAL_INFORMATION Buf; + UNICODE_STRING ExpBuf; + UNICODE_STRING ValueName_U; + UNICODE_STRING tmp; + ULONG ResultLength; + ULONG l1, l2; + NTSTATUS Status; + + Data->Buffer = NULL; + + l1 = ((ULONG) MaxSiz + 1) * sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION); + + Buf = (PKEY_VALUE_PARTIAL_INFORMATION) RtlAllocateHeap(HeapHandle, 0, l1); + + if (Buf == NULL) { +#if DBG + IF_OD2_DEBUG( INIT ) { + KdPrint(("Or2GetEnvPath: can't allocate Buf from heap\n")); + } +#endif + return(FALSE); + } + + if (ExpandIt) { + + l2 = ((ULONG) MaxSiz + 1) * sizeof(WCHAR); + + ExpBuf.Buffer = (PWSTR) RtlAllocateHeap(HeapHandle, 0, l2); + + if (ExpBuf.Buffer == NULL) { +#if DBG + IF_OD2_DEBUG( INIT ) { + KdPrint(("Or2GetEnvPath: can't allocate ExpBuf from heap\n")); + } +#endif + RtlFreeHeap(HeapHandle, 0, Buf); + return(FALSE); + } + + ExpBuf.MaximumLength = MaxSiz * sizeof(WCHAR); + } + + RtlInitUnicodeString(&ValueName_U, ValueName); + Status = NtQueryValueKey(EnvKey, + &ValueName_U, + KeyValuePartialInformation, + Buf, + l1, + &ResultLength + ); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OD2_DEBUG( INIT ) { + KdPrint(("OS2SSRTL(Or2GetEnvPath): FAILED - NtQueryValueKey %lx\n", Status)); + } +#endif + if (ExpandIt) { + RtlFreeHeap(HeapHandle, 0, Buf); + ExpBuf.Length = 0; + ExpBuf.Buffer[0] = UNICODE_NULL; + *Data = ExpBuf; + } else { + *((PWSTR) Buf) = UNICODE_NULL; + Data->Buffer = (PWSTR) Buf; + Data->Length = 0; + Data->MaximumLength = MaxSiz * sizeof(WCHAR); + } + + return(TRUE); + } + + if (ExpandIt) { + RtlInitUnicodeString(&tmp, (PWSTR) Buf->Data); + + Status = RtlExpandEnvironmentStrings_U(NULL, + &tmp, + &ExpBuf, + NULL); + + RtlFreeHeap(HeapHandle, 0, Buf); + + if (!NT_SUCCESS(Status)) { +#if DBG + IF_OD2_DEBUG( INIT ) { + KdPrint(("OS2SSRTL(Or2GetEnvPath): FAILED - RtlExpandEnvironmentStrings_U %lx\n", Status)); + } +#endif + ExpBuf.Buffer[0] = UNICODE_NULL; + ExpBuf.Length = 0; + *Data = ExpBuf; + return(TRUE); + } + + ExpBuf.Buffer[ExpBuf.Length/sizeof(WCHAR)] = UNICODE_NULL; + *Data = ExpBuf; + + } else { + + l2 = Buf->DataLength; + RtlMoveMemory(Buf, Buf->Data, l2); + Data->Buffer = (PWSTR) Buf; + Data->Length = (USHORT) (l2 - sizeof(WCHAR)); + Data->MaximumLength = MaxSiz * sizeof(WCHAR); + } + + return(TRUE); +} + + +// +// This is a general routine to parse an environment. It loops over the lines in the environment, and +// identifies the variable in each line. For each variable, it searches a user dispatch table, and if +// that dispatch table contains an entry for this variable, it calls the user supplied routine with the +// name and value of the variable. +// +// Environment -- The environment to process. This is either in multi-string format, or a +// (null|^Z)-terminated string consisting of lines separated by crlfs. +// DispatchTable -- A dispatch table used to process the environment. +// NumberOfDispatchItems -- Contains the number of entries in DispatchTable. +// DelimOption -- specifies whether Environment is a multi-string (NULL_DELIM) or a string of crlf +// separated lines (CRLF_DELIM). +// +// Description of the DispatchTable: +// This is an array of structures of type ENVIRONMENT_DISPATCH_TABLE_ENTRY. +// Each entry specifies a particular variable which should be handled. +// an entry contains: +// -- the name of the variable to operate on (this is case-insensitive). +// -- a string of possible characters that serve as delimiters for the variable name (e.g. =, space, tab). +// -- a DispatchFunction. This function is dispatched with 6 parameters: +// -- the index in the dispatch table which triggered this call. +// -- a user defined pointer, see below. +// -- a pointer to the variable name in the environment. +// -- length of the variable name (in chars). +// -- a pointer to the variable value in the environment. +// -- length of the variable value (in chars). +// -- a user defined pointer that is passed to the dispatch function. +// +// The dispatch function may do anything it likes with the information it gets, and it +// is not expected to return any value. +// +// A special value of "*" in the variable name field of an entry indicates that any variable name +// is to be processed by the DispatchFunction. The DispatchFunction can figure out the name of +// the variable it was called for by examining its parameters. If this option is used together +// with a Delimeters value of NULL, no delimiter will be searched for, the line will be passed +// as is with Name and Value both pointing to the beginning of the line (and ValueLen containing +// its length) +// + +VOID +Or2IterateEnvironment( + IN PWSTR Environment, + IN ENVIRONMENT_DISPATCH_TABLE DispatchTable, + IN ULONG NumberOfDispatchItems, + IN ULONG DelimOption + ) +{ + PWSTR Src; + PWSTR Tmp; + PWSTR Eol; + PWSTR NextLine; + ULONG i, m; + LONG l; + BOOLEAN Flag; + + if (NumberOfDispatchItems == 0 || Environment == NULL || DispatchTable == NULL) { + return; + } + + Src = Environment; + while (*Src != UNICODE_NULL) { + Or2SkipWWS(&Src, FWD); + + if (DelimOption == CRLF_DELIM && + *Src == L'\32') { // ^Z terminates file on CRLF delim texts + break; + } + + for (i = 0; i < NumberOfDispatchItems; i++) { + + if (DispatchTable[i].VarName[0] == L'*') { + + if (DispatchTable[i].Delimiters == NULL) { + + // + // No delimeter required. If the line is empty + // we don't process it. + // + + if (*Src == UNICODE_NULL) { + continue; + } + + if (DelimOption == CRLF_DELIM && + (*Src == L'\r' || *Src == L'\n')) { + continue; + } + + l = -1; // process the line from start + break; + } + + Tmp = Src; + l = 0; + while (TRUE) { + if (*Tmp == UNICODE_NULL || + (DelimOption == CRLF_DELIM && (*Tmp == L'\r' || *Tmp == L'\n' || *Tmp == L'\32'))) { + Flag = FALSE; + break; + } + if (wcschr(DispatchTable[i].Delimiters, RtlUpcaseUnicodeChar(*Tmp)) != NULL) { + Flag = TRUE; + break; + } + Tmp++; + l++; + } + + if (Flag) { + break; + } + continue; + } + + l = wcslen(DispatchTable[i].VarName); + + if (Or2UnicodeEqualCI(Src, DispatchTable[i].VarName, (ULONG)l) && + Src[l] != UNICODE_NULL && + wcschr(DispatchTable[i].Delimiters, RtlUpcaseUnicodeChar(Src[l])) != NULL) { + break; + } + } + + if (DelimOption == NULL_DELIM) { + Eol = Src + wcslen(Src); + NextLine = Eol + 1; + } else { + Eol = wcspbrk(Src, L"\r\n\32"); + if (Eol == NULL) { + NextLine = Eol = Src + wcslen(Src); + } else { + NextLine = Eol; + if (*Eol != L'\32') { + while (*NextLine == L'\r' || *NextLine == L'\n') { + NextLine++; + } + } + } + } + + if (i == NumberOfDispatchItems || DispatchTable[i].DispatchFunction == NULL) { + Src = NextLine; + continue; + } + + Tmp = Src + l + 1; + Or2SkipWWS(&Tmp, FWD); + if (Eol != Tmp) { + Eol--; + Or2SkipWWS(&Eol, BWD); + m = (Eol - Tmp) + 1; + } else { + m = 0; + } + + DispatchTable[i].DispatchFunction(i, DispatchTable[i].UserParameter, Src, l, Tmp, m); + + Src = NextLine; + } +} + + +// +// Following is useful Dispatch Function for use with Or2IterateEnvironment. +// It copies the parameters passed to it into a structure of type +// ENVIRONMENT_SEARCH_RECORD. A pointer to the structure to fill must be +// passed through the UserParameter in the Dispatch Table. +// + +VOID +Or2FillInSearchRecordDispatchFunction( + IN ULONG DispatchTableIndex, + IN PVOID UserParameter, + IN PWSTR Name, + IN ULONG NameLen, + IN PWSTR Value, + IN ULONG ValueLen + ) +{ + PENVIRONMENT_SEARCH_RECORD p = (PENVIRONMENT_SEARCH_RECORD) UserParameter; + + p->DispatchTableIndex = DispatchTableIndex; + p->Name = Name; + p->NameLen = NameLen; + p->Value = Value; + p->ValueLen = ValueLen; +} + + +NTSTATUS +Or2GetFileStamps( + IN HANDLE hFile, + OUT PLARGE_INTEGER pTimeStamp, + OUT PLARGE_INTEGER pSizeStamp + ) + +/*++ + +Routine Description: + + This function reads a file's size and time of last write. + +Arguments: + + hFile -- handle of file to read. + pTimeStamp -- returns file time of last write. + pSizeStamp -- returns file size. + +Return Value: + + NT error code. The return parameters are valid only if this is + success. + +--*/ + +{ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatus; + FILE_BASIC_INFORMATION BasicInfo; + FILE_STANDARD_INFORMATION StandardInfo; + + Status = NtQueryInformationFile(hFile, + &IoStatus, + &BasicInfo, + sizeof(BasicInfo), + FileBasicInformation); + + if (!NT_SUCCESS(Status)) { +#if DBG + KdPrint(("Or2GetFileStamps: Unable to query file time, rc = %lx\n", Status)); +#endif + return(Status); + } + + *pTimeStamp = BasicInfo.LastWriteTime; + + Status = NtQueryInformationFile(hFile, + &IoStatus, + &StandardInfo, + sizeof(StandardInfo), + FileStandardInformation); + + if (!NT_SUCCESS(Status)) { +#if DBG + KdPrint(("Or2GetFileStamps: Unable to query file size, rc = %lx\n", Status)); +#endif + return(Status); + } + + *pSizeStamp = StandardInfo.EndOfFile; + + return(STATUS_SUCCESS); +} + diff --git a/private/windows/gina/winlogon/i386/os2ssrtl.h b/private/windows/gina/winlogon/i386/os2ssrtl.h new file mode 100644 index 000000000..90d054fd4 --- /dev/null +++ b/private/windows/gina/winlogon/i386/os2ssrtl.h @@ -0,0 +1,121 @@ +/****************************** Module Header ******************************\ +* Module Name: os2ssrtl.h +* +* Copyright (c) 1993, Microsoft Corporation +* +* Import file for the OS/2 Subsystem migration code support module +* +* History: +* 03-30-93 OferP Created. +\***************************************************************************/ + +#ifndef __OS2SSRTL_H +#define __OS2SSRTL_H + +#define FWD +1L // these are used with +#define BWD -1L // Or2SkipWWS + +#define NULL_DELIM 0 // these are used with +#define CRLF_DELIM 1 // Or2IterateEnvironment + +typedef VOID (*PFN_ENVIRONMENT_PROCESSOR)( + IN ULONG DispatchTableIndex, + IN PVOID UserParameter, + IN PWSTR Name, + IN ULONG NameLen, + IN PWSTR Value, + IN ULONG ValueLen + ); + +typedef struct _ENVIRONMENT_DISPATCH_TABLE_ENTRY { + PWSTR VarName; + PWSTR Delimiters; + PFN_ENVIRONMENT_PROCESSOR DispatchFunction; + PVOID UserParameter; +} ENVIRONMENT_DISPATCH_TABLE_ENTRY, *PENVIRONMENT_DISPATCH_TABLE_ENTRY; + +typedef PENVIRONMENT_DISPATCH_TABLE_ENTRY ENVIRONMENT_DISPATCH_TABLE; + +typedef struct _ENVIRONMENT_SEARCH_RECORD { + ULONG DispatchTableIndex; + PWSTR Name; + ULONG NameLen; + PWSTR Value; + ULONG ValueLen; +} ENVIRONMENT_SEARCH_RECORD, *PENVIRONMENT_SEARCH_RECORD; + +VOID +Or2SkipWWS( + IN OUT PWSTR *Str, + IN LONG Direction + ); + +VOID +Or2UnicodeStrupr( + IN OUT PWSTR Str + ); + +BOOLEAN +Or2UnicodeEqualCI( + IN PWSTR Str1, + IN PWSTR Str2, + IN ULONG Count + ); + +BOOLEAN +Or2AppendPathToPath( + IN PVOID HeapHandle, + IN PWSTR SrcPath, + IN OUT PUNICODE_STRING DestPath, + IN BOOLEAN ExpandIt + ); + +BOOLEAN +Or2ReplacePathByPath( + IN PVOID HeapHandle, + IN PWSTR SrcPath, + IN OUT PUNICODE_STRING DestPath + ); + +VOID +Or2CheckSemicolon( + IN OUT PUNICODE_STRING Str + ); + +BOOLEAN +Or2GetEnvPath( + OUT PUNICODE_STRING Data, + IN PVOID HeapHandle, + IN USHORT MaxSiz, + IN HANDLE EnvKey, + IN PWSTR ValueName, + IN BOOLEAN ExpandIt + ); + +VOID +Or2IterateEnvironment( + IN PWSTR Environment, + IN ENVIRONMENT_DISPATCH_TABLE DispatchTable, + IN ULONG NumberOfDispatchItems, + IN ULONG DelimOption + ); + +VOID +Or2FillInSearchRecordDispatchFunction( + IN ULONG DispatchTableIndex, + IN PVOID UserParameter, + IN PWSTR Name, + IN ULONG NameLen, + IN PWSTR Value, + IN ULONG ValueLen + ); + +NTSTATUS +Or2GetFileStamps( + IN HANDLE hFile, + OUT PLARGE_INTEGER pTimeStamp, + OUT PLARGE_INTEGER pSizeStamp + ); + +#endif + diff --git a/private/windows/gina/winlogon/i386/sources b/private/windows/gina/winlogon/i386/sources new file mode 100644 index 000000000..b7e8c42b7 --- /dev/null +++ b/private/windows/gina/winlogon/i386/sources @@ -0,0 +1,2 @@ +I386_SOURCES=os2ssmig.c \ + os2ssrtl.c diff --git a/private/windows/gina/winlogon/logfull.h b/private/windows/gina/winlogon/logfull.h new file mode 100644 index 000000000..ff478cd08 --- /dev/null +++ b/private/windows/gina/winlogon/logfull.h @@ -0,0 +1,22 @@ +/****************************** Module Header ******************************\ +* Module Name: logfull.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Define apis used to implement audit log full action dialog +* +* History: +* 5-6-92 DaveHart Created. +\***************************************************************************/ + + +// +// Exported function prototypes +// + + +DLG_RETURN_TYPE +LogFullAction( + HWND hwnd, + PGLOBALS pGlobals, + BOOL * bAuditingDisabled); diff --git a/private/windows/gina/winlogon/loggedon.h b/private/windows/gina/winlogon/loggedon.h new file mode 100644 index 000000000..ea837165b --- /dev/null +++ b/private/windows/gina/winlogon/loggedon.h @@ -0,0 +1,23 @@ +/****************************** Module Header ******************************\ +* Module Name: loggedon.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Define apis used to process input while a user is logged on. +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + + +// +// Exported function prototypes +// + + +int +Loggedon( + PGLOBALS + ); + + diff --git a/private/windows/gina/winlogon/logging.h b/private/windows/gina/winlogon/logging.h new file mode 100644 index 000000000..acbeb7a1f --- /dev/null +++ b/private/windows/gina/winlogon/logging.h @@ -0,0 +1,31 @@ +/****************************** Module Header ******************************\ +* Module Name: sas.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Defines apis to perform event logging in winlogon. +* +* History: +* 03-19-93 Robertre Created. +\***************************************************************************/ + +#ifdef LOGGING + +BOOL +WriteLog( + HANDLE FileHandle, + LPWSTR LogString + ); + +BOOL +OpenLogFile( + PHANDLE LogFileHandle + ); + +BOOL SetLoggingFileVariables(PGLOBALS pGlobals); + +BOOL DeleteLoggingFileVariables(PGLOBALS pGlobals); + +extern HANDLE LogFileHandle; + +#endif diff --git a/private/windows/gina/winlogon/logoff.c b/private/windows/gina/winlogon/logoff.c new file mode 100644 index 000000000..995a49106 --- /dev/null +++ b/private/windows/gina/winlogon/logoff.c @@ -0,0 +1,1284 @@ +/****************************** Module Header ******************************\ +* Module Name: logoff.c +* +* Copyright (c) 1991, Microsoft Corporation +* +* Implements functions to allow a user to logoff the system. +* +* History: +* 12-05-91 Davidc Created. +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +#include <ras.h> +#include <raserror.h> + +// +// Private prototypes +// + +HANDLE +ExecLogoffThread( + PGLOBALS pGlobals, + DWORD Flags + ); + +DWORD +LogoffThreadProc( + LPVOID Parameter + ); + +BOOL WINAPI +ShutdownWaitDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ); + +BOOL WINAPI +ShutdownDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ); + +BOOL +DeleteNetworkConnections( + PGLOBALS pGlobals + ); + +BOOLEAN +ShutdownThread( + VOID + ); + +BOOL +DeleteRasConnections( + PGLOBALS pGlobals + ); + +BOOL WINAPI +DeleteNetConnectionsDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ); + + +BOOL ExitWindowsInProgress = FALSE ; +HMODULE hRasApi; + +// +// Bugbug: move to winlogon.h +// +#define IsShutdown(x) ((x == WLX_SAS_ACTION_SHUTDOWN) || \ + (x == WLX_SAS_ACTION_SHUTDOWN_REBOOT) || \ + (x == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF) ) + + +/***************************************************************************\ +* FUNCTION: InitiateLogOff +* +* PURPOSE: Starts the procedure of logging off the user. +* +* RETURNS: DLG_SUCCESS - logoff was initiated successfully. +* DLG_FAILURE - failed to initiate logoff. +* +* HISTORY: +* +* 12-09-91 Davidc Created. +* +\***************************************************************************/ + +int +InitiateLogoff( + PGLOBALS pGlobals, + LONG Flags + ) +{ + BOOL IgnoreResult; + HANDLE ThreadHandle; + HANDLE Handle; + PUSER_PROCESS_DATA UserProcessData; + DWORD Result; + + // + // If this is a shutdown operation, call ExitWindowsEx from + // another thread. + // + + if (Flags & (EWX_SHUTDOWN | EWX_REBOOT | EWX_POWEROFF)) { + + // + // Exec a user thread to call ExitWindows + // + + ThreadHandle = ExecLogoffThread(pGlobals, Flags); + + if (ThreadHandle == NULL) { + + DebugLog((DEB_ERROR, "Unable to create logoff thread")); + return(DLG_FAILURE); + + } else { + + // + // We don't need the thread handle + // + + IgnoreResult = CloseHandle(ThreadHandle); + ASSERT(IgnoreResult); + } + Result = DLG_SUCCESS; + + } else { + + // + // Switch the thread to user context. We don't want + // to start another thread to perform logoffs in + // case the system is out of memory and unable to + // create any more threads. + // + + UserProcessData = &pGlobals->UserProcessData; + Handle = ImpersonateUser(UserProcessData, GetCurrentThread()); + + if (Handle == NULL) { + + DebugLog((DEB_ERROR, "Failed to set user context on thread!")); + + } else { + + // + // Let the thread run + // + + if ((pGlobals->UserLoggedOn) && + (pGlobals->LastGinaRet != WLX_SAS_ACTION_FORCE_LOGOFF) ) + { + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Application); + } + Result = LogoffThreadProc((LPVOID)Flags); + + } + + RevertToSelf(); + + } + + // + // ExitWindowsEx will cause one or more desktop switches to occur, + // so we must invalidate our current desktop. + // + + if ( (Flags & EWX_WINLOGON_API_SHUTDOWN) == 0 ) + { + pGlobals->WindowStation.PreviousDesktop = pGlobals->WindowStation.ActiveDesktop; + pGlobals->WindowStation.ActiveDesktop = -1; + } + + // + // The reboot thread is off and running. We're finished. + // + + return (Result); +} + + +/***************************************************************************\ +* FUNCTION: ExecLogoffThread +* +* PURPOSE: Creates a user thread that calls ExitWindowsEx with the +* passed flags. +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 05-05-92 Davidc Created. +* +\***************************************************************************/ + +HANDLE +ExecLogoffThread( + PGLOBALS pGlobals, + DWORD Flags + ) +{ + HANDLE ThreadHandle; + DWORD ThreadId; + + ThreadHandle = ExecUserThread( + pGlobals, + LogoffThreadProc, + (LPVOID)Flags, + 0, // Thread creation flags + &ThreadId); + + if (ThreadHandle == NULL) { + DebugLog((DEB_ERROR, "Failed to exec a user logoff thread")); + } + + return (ThreadHandle); +} + + +/***************************************************************************\ +* FUNCTION: LogoffThreadProc +* +* PURPOSE: The logoff thread procedure. Calls ExitWindowsEx with passed flags. +* +* RETURNS: Thread termination code is result of ExitWindowsEx call. +* +* HISTORY: +* +* 05-05-92 Davidc Created. +* +\***************************************************************************/ + +DWORD +LogoffThreadProc( + LPVOID Parameter + ) +{ + DWORD LogoffFlags = (DWORD)Parameter; + BOOL Result = FALSE; + + + // + // If this logoff is a result of the InitiateSystemShutdown API, + // put up a dialog warning the user. + // + + if ( LogoffFlags & EWX_WINLOGON_API_SHUTDOWN ) { + Result = ShutdownThread(); + } else { + Result = TRUE; + } + + + if ( Result ) { + + // + // Enable shutdown privilege if we need it + // + + if (LogoffFlags & (EWX_SHUTDOWN | EWX_REBOOT | EWX_POWEROFF)) { + Result = EnablePrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE); + if (!Result) { + DebugLog((DEB_ERROR, "Logoff thread failed to enable shutdown privilege!\n")); + } + } + + // + // Call ExitWindowsEx with the passed flags + // + + if (Result) { + + DebugLog((DEB_TRACE, "Calling ExitWindowsEx(%#x, 0)\n", LogoffFlags)); + + // + // Set global flag indicating an ExitWindows is in progress. + // + + ExitWindowsInProgress = TRUE ; + + Result = ExitWindowsEx(LogoffFlags, 0); + + ExitWindowsInProgress = FALSE ; + + if (!Result) { + DebugLog((DEB_ERROR, "Logoff thread call to ExitWindowsEx failed, error = %d\n", GetLastError())); + } + } + } + + return(Result ? DLG_SUCCESS : DLG_FAILURE); +} + + +/***************************************************************************\ +* FUNCTION: RebootMachine +* +* PURPOSE: Calls NtShutdown(Reboot) in current user's context. +* +* RETURNS: Should never return +* +* HISTORY: +* +* 05-09-92 Davidc Created. +* +\***************************************************************************/ + +VOID +RebootMachine( + PGLOBALS pGlobals + ) +{ + NTSTATUS Status; + BOOL EnableResult, IgnoreResult; + HANDLE UserHandle; + + // + // Call windows to have it clear all data from video memory + // + + // GdiEraseMemory(); + + DebugLog(( DEB_TRACE, "Rebooting machine\n" )); + + // + // Impersonate the user for the shutdown call + // + + UserHandle = ImpersonateUser( &pGlobals->UserProcessData, NULL ); + ASSERT(UserHandle != NULL); + + // + // Enable the shutdown privilege + // This should always succeed - we are either system or a user who + // successfully passed the privilege check in ExitWindowsEx. + // + + EnableResult = EnablePrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE); + ASSERT(EnableResult); + + + // + // Do the final system shutdown pass (reboot) + // + + Status = NtShutdownSystem(ShutdownReboot); + + DebugLog((DEB_ERROR, "NtShutdownSystem failed, status = 0x%lx", Status)); + ASSERT(NT_SUCCESS(Status)); // Should never get here + + // + // We may get here if system is screwed up. + // Try and clean up so they can at least log on again. + // + + IgnoreResult = StopImpersonating(UserHandle); + ASSERT(IgnoreResult); +} + +/***************************************************************************\ +* FUNCTION: PowerdownMachine +* +* PURPOSE: Calls NtShutdownSystem(ShutdownPowerOff) in current user's context. +* +* RETURNS: Should never return +* +* HISTORY: +* +* 08-09-93 TakaoK Created. +* +\***************************************************************************/ + +VOID +PowerdownMachine( + PGLOBALS pGlobals + ) +{ + NTSTATUS Status; + BOOL EnableResult, IgnoreResult; + HANDLE UserHandle; + + DebugLog(( DEB_TRACE, "Powering down machine\n" )); + // + // Impersonate the user for the shutdown call + // + + UserHandle = ImpersonateUser( &pGlobals->UserProcessData, NULL ); + ASSERT(UserHandle != NULL); + + // + // Enable the shutdown privilege + // This should always succeed - we are either system or a user who + // successfully passed the privilege check in ExitWindowsEx. + // + + EnableResult = EnablePrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE); + ASSERT(EnableResult); + + // + // Do the final system shutdown and powerdown pass + // + + Status = NtShutdownSystem(ShutdownPowerOff); + + DebugLog((DEB_ERROR, "NtPowerdownSystem failed, status = 0x%lx", Status)); + ASSERT(NT_SUCCESS(Status)); // Should never get here + + // + // We may get here if system is screwed up. + // Try and clean up so they can at least log on again. + // + + IgnoreResult = StopImpersonating(UserHandle); + ASSERT(IgnoreResult); +} + + +/***************************************************************************\ +* FUNCTION: ShutdownMachine +* +* PURPOSE: Shutsdown and optionally reboots or powers off the machine. +* +* The shutdown is always done in the logged on user's context. +* If no user is logged on then the shutdown happens in system context. +* +* RETURNS: FALSE if something went wrong, otherwise it never returns. +* +* HISTORY: +* +* 05-09-92 Davidc Created. +* 10-04-93 Johannec Add poweroff option. +* +\***************************************************************************/ + +BOOL +ShutdownMachine( + PGLOBALS pGlobals, + int Flags + ) +{ + int Result; + HANDLE FoundDialogHandle; + HANDLE LoadedDialogHandle = NULL; + + + // + // Preload the shutdown dialog so we don't have to fetch it after + // the filesystem has been shutdown + // + + FoundDialogHandle = FindResource(NULL, + (LPTSTR) MAKEINTRESOURCE(IDD_SHUTDOWN), + (LPTSTR) MAKEINTRESOURCE(RT_DIALOG)); + if (FoundDialogHandle == NULL) { + DebugLog((DEB_ERROR, "Failed to find shutdown dialog resource")); + } else { + LoadedDialogHandle = LoadResource(NULL, FoundDialogHandle); + if (LoadedDialogHandle == NULL) { + DebugLog((DEB_ERROR, "Ffailed to load shutdown dialog resource")); + } + } + + // + // Notify the GINA of shutdown here. + // + + +#if DBG + if (TEST_FLAG(GinaBreakFlags, BREAK_SHUTDOWN)) + { + DebugLog((DEB_TRACE, "About to call WlxShutdown(%#x)\n", + pGlobals->pGina->pGinaContext)); + + DebugBreak(); + } +#endif + + WlxSetTimeout(pGlobals, 120); + + (void) pGlobals->pGina->pWlxShutdown(pGlobals->pGina->pGinaContext, Flags); + + // + // If we haven't shut down already (via the Remote shutdown path), then + // we start it here, and wait for it to complete. Otherwise, skip straight + // down to the cool stuff. + // + if (pGlobals->WinlogonState != Winsta_Shutdown) + { + // + // Call windows to do the windows part of shutdown + // We make this a force operation so it is guaranteed to work + // and can not be interrupted. + // + + DebugLog(( DEB_TRACE, "Starting shutdown\n" )); + + Result = InitiateLogoff(pGlobals, EWX_SHUTDOWN | EWX_FORCE | + ((Flags == WLX_SAS_ACTION_SHUTDOWN_REBOOT) ? EWX_REBOOT : 0) | + ((Flags == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF) ? EWX_POWEROFF : 0) ); + + ASSERT(Result == DLG_SUCCESS); + + + + // + // Put up a dialog box to wait for the shutdown notification + // from windows and make the first NtShutdownSystem call. + // + + WlxSetTimeout(pGlobals, TIMEOUT_NONE); + + Result = WlxDialogBoxParam( pGlobals, + pGlobals->hInstance, + (LPTSTR)IDD_SHUTDOWN_WAIT, + NULL, + ShutdownWaitDlgProc, + (LONG)pGlobals); + + } + else + { + // + // If we're here, it means that we were shut down from the remote path, + // so user has cleaned up, now we have to call NtShutdown to flush out + // mm, io, etc. + // + + DebugLog(( DEB_TRACE, "Shutting down kernel\n" )); + + EnablePrivilege( SE_SHUTDOWN_PRIVILEGE, TRUE ); + + NtShutdownSystem( ShutdownNoReboot ); + + EnablePrivilege( SE_SHUTDOWN_PRIVILEGE, FALSE ); + } + + + // + // if machine has powerdown capability and user want to turn it off, then + // we down the system power. + // + if ( Flags == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF) + { + PowerdownMachine(pGlobals); + + } + + + // + // If this is a shutdown request, then let the user know they can turn + // off the power. Otherwise drop straight through and reboot. + // + + if ( Flags != WLX_SAS_ACTION_SHUTDOWN_REBOOT) { + + DialogBoxIndirectParam( + pGlobals->hInstance, + (LPDLGTEMPLATE)LoadedDialogHandle, + NULL, + ShutdownDlgProc, + (LONG)pGlobals); + } + + + // + // If they got past that dialog it means they want to reboot + // + + RebootMachine(pGlobals); + + ASSERT(!"RebootMachine failed"); // Should never get here + + return(FALSE); +} + + +/***************************************************************************\ +* FUNCTION: ShutdownWaitDlgProc +* +* PURPOSE: Processes messages while we wait for windows to notify us of +* a successful shutdown. When notification is received, do any +* final processing and make the first call to NtShutdownSystem. +* +* RETURNS: +* DLG_FAILURE - the dialog could not be displayed +* DLG_SHUTDOWN() - the system has been shutdown, reboot wasn't requested +* +* HISTORY: +* +* 10-14-92 Davidc Created. +* 10-04-93 Johannec Added Power off option. +* +\***************************************************************************/ + +BOOL WINAPI +ShutdownWaitDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ) +{ + PGLOBALS pGlobals = (PGLOBALS)GetWindowLong(hDlg, GWL_USERDATA); + BOOL Success; + NTSTATUS Status; + HANDLE UserHandle; + + switch (message) { + + case WM_INITDIALOG: + SetWindowLong(hDlg, GWL_USERDATA, lParam); + CentreWindow(hDlg); + return(TRUE); + + + case WLX_WM_SAS: + if (wParam != WLX_SAS_TYPE_USER_LOGOFF) + { + return(TRUE); + } + + UpdateWindow(hDlg); + + // + // Look at the public shutdown/reboot flags to determine what windows + // has actually done. We may receive other logoff notifications here + // but they will be only logoffs - the only place that winlogon actually + // calls ExitWindowsEx to do a shutdown/reboot is right here. So wait + // for the real shutdown/reboot notification. + // + + if (pGlobals->LogoffFlags & (EWX_SHUTDOWN | EWX_REBOOT | EWX_POWEROFF)) { + + // + // It's the notification we were waiting for. + // Do any final processing required and make the first + // call to NtShutdownSystem. + // + + + // + // Do any dos-specific clean-up + // + + ShutdownDOS(); + + + // + // Impersonate the user for the shutdown call + // + + UserHandle = ImpersonateUser( &pGlobals->UserProcessData, NULL ); + ASSERT(UserHandle != NULL); + + // + // Enable the shutdown privilege + // This should always succeed - we are either system or a user who + // successfully passed the privilege check in ExitWindowsEx. + // + + Success = EnablePrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE); + ASSERT(Success); + + // + // Do the first pass at system shutdown (no reboot yet) + // + + WaitForSystemProcesses(pGlobals); + + Status = NtShutdownSystem(ShutdownNoReboot); + ASSERT(NT_SUCCESS(Status)); + + // + // Revert to ourself + // + + Success = StopImpersonating(UserHandle); + ASSERT(Success); + + // + // We've finished system shutdown, we're done + // + + EndDialog(hDlg, LogoffFlagsToWlxCode(pGlobals->LogoffFlags) ); + } + + return(TRUE); + } + + // We didn't process this message + return FALSE; +} + + +/***************************************************************************\ +* FUNCTION: ShutdownDlgProc +* +* PURPOSE: Processes messages for the shutdown dialog - the one that says +* it's safe to turn off the machine. +* +* RETURNS: DLG_SUCCESS if the user hits the restart button. +* +* HISTORY: +* +* 03-19-92 Davidc Created. +* +\***************************************************************************/ + +BOOL WINAPI +ShutdownDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ) +{ + PGLOBALS pGlobals = (PGLOBALS)GetWindowLong(hDlg, GWL_USERDATA); + + switch (message) { + + case WM_INITDIALOG: + SetWindowLong(hDlg, GWL_USERDATA, lParam); + SetupSystemMenu(hDlg); + CentreWindow(hDlg); + return(TRUE); + + case WM_COMMAND: + EndDialog(hDlg, DLG_SUCCESS); + return(TRUE); + } + + // We didn't process this message + return FALSE; +} + + +/***************************************************************************\ +* FUNCTION: LogOff +* +* PURPOSE: Handles the post-user-application part of user logoff. This +* routine is called after all the user apps have been closed down +* It saves the user's profile, deletes network connections +* and reboots/shutsdown the machine if that was requested. +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 12-09-91 Davidc Created. +* +\***************************************************************************/ + +BOOL +Logoff( + PGLOBALS pGlobals, + int LoggedOnResult + ) +{ + NTSTATUS Status; + LUID luidNone = { 0, 0 }; + + DebugLog((DEB_TRACE, "In Logoff()\n")); + + // + // We expect to be at the winlogon desktop in all cases + // + + // ASSERT(OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED) == pGlobals->hdeskWinlogon); + + + // + // Delete the user's network connections + // Make sure we do this before deleting the user's profile + // + + DeleteNetworkConnections(pGlobals); + + + + // + // Remove any Messages Aliases added by the user. + // + DeleteMsgAliases(); + + // + // Play the user's logoff sound + // + if (pGlobals->PlaySound || pGlobals->MigrateSoundEvents) { + HANDLE uh; + BOOL fBeep; + + // We AREN'T impersonating the user by default, so we MUST do so + // otherwise we end up playing the default rather than the user + // specified sound. + + if (OpenIniFileUserMapping(pGlobals)) + { + uh = ImpersonateUser(&pGlobals->UserProcessData, NULL); + + // + // Whenever a user logs out, have WINMM.DLL check if there + // were any sound events added to the [SOUNDS] section of + // CONTROL.INI by a non-regstry-aware app. If there are, + // migrate those schemes to their new home. If there aren't, + // this is very quick. + // + + if (pGlobals->MigrateSoundEvents) { + (*(pGlobals->MigrateSoundEvents))(); + } + + if (pGlobals->PlaySound) { + if (!SystemParametersInfo(SPI_GETBEEP, 0, &fBeep, FALSE)) { + // Failed to get hold of beep setting. Should we be + // noisy or quiet? We have to choose one value... + fBeep = TRUE; + } + + if (fBeep) { + + // + // Play synchronous + // + (*(pGlobals->PlaySound))((LPCSTR)SND_ALIAS_SYSTEMEXIT, NULL, SND_ALIAS_ID | SND_SYNC | SND_NODEFAULT); + } + } + + StopImpersonating(uh); + + CloseIniFileUserMapping(pGlobals); + } + + } + + // + // Call user to close the registry key for the NLS cache. + // + + SetWindowStationUser(pGlobals->WindowStation.hwinsta, &luidNone, NULL, 0); + + // + // Close the IniFileMapping that happened at logon time (LogonAttempt()). + // + + CloseIniFileUserMapping(pGlobals); + + // + // Save the user profile, this unloads the user's key in the registry + // + + SaveUserProfile(pGlobals); + + // + // Delete any remaining RAS connections. Make sure to do this after + // the user profile gets copied up to the + // + + DeleteRasConnections( pGlobals ); + + // + // If the user logged off themselves (rather than a system logoff) + // and wanted to reboot then do it now. + // + + if (IsShutdown(LoggedOnResult) && (!(pGlobals->LogoffFlags & EWX_WINLOGON_OLD_SYSTEM))) + { + ShutdownMachine(pGlobals, LoggedOnResult); + + ASSERT(!"ShutdownMachine failed"); // Should never return + } + + + // + // Set up security info for new user (system) - this clears out + // the stuff for the old user. + // + + SecurityChangeUser(pGlobals, NULL, NULL, pGlobals->WinlogonSid, FALSE); + + + + return(TRUE); +} + + +/***************************************************************************\ +* FUNCTION: DeleteNetworkConnections +* +* PURPOSE: Calls WNetNukeConnections in the client context to delete +* any connections they may have had. +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 04-15-92 Davidc Created. +* +\***************************************************************************/ + +BOOL +DeleteNetworkConnections( + PGLOBALS pGlobals + ) +{ + HANDLE ImpersonationHandle; + DWORD WNetResult; + BOOL Result = FALSE; // Default is failure + TCHAR szMprDll[] = TEXT("mpr.dll"); + CHAR szWNetNukeConn[] = "WNetClearConnections"; + CHAR szWNetOpenEnum[] = "WNetOpenEnumW"; + CHAR szWNetEnumResource[] = "WNetEnumResourceW"; + CHAR szWNetCloseEnum[] = "WNetCloseEnum"; + PWNETNUKECONN lpfnWNetNukeConn = NULL; + PWNETOPENENUM lpfnWNetOpenEnum = NULL; + PWNETENUMRESOURCE lpfnWNetEnumResource = NULL; + PWNETCLOSEENUM lpfnWNetCloseEnum = NULL; + HWND hNetDelDlg; + HANDLE hEnum; + BOOL bConnectionsExist = TRUE; + NETRESOURCE NetRes; + DWORD dwNumEntries = 1; + DWORD dwEntrySize = sizeof (NETRESOURCE); + + // + // Impersonate the user + // + + ImpersonationHandle = ImpersonateUser(&pGlobals->UserProcessData, NULL); + + if (ImpersonationHandle == NULL) { + DebugLog((DEB_ERROR, "DeleteNetworkConnections : Failed to impersonate user\n")); + return(FALSE); + } + + + // + // Load mpr if it wasn't already loaded. + // + + if (!pGlobals->hMPR){ + if (!(pGlobals->hMPR = LoadLibrary(szMprDll))) { + DebugLog((DEB_ERROR, "DeleteNetworkConnections : Failed to load mpr.dll\n")); + goto DNCExit; + } + } + + // + // Get the function pointers + // + + lpfnWNetOpenEnum = (PWNETOPENENUM) GetProcAddress(pGlobals->hMPR, + (LPSTR)szWNetOpenEnum); + lpfnWNetEnumResource = (PWNETENUMRESOURCE) GetProcAddress(pGlobals->hMPR, + (LPSTR)szWNetEnumResource); + lpfnWNetCloseEnum = (PWNETCLOSEENUM) GetProcAddress(pGlobals->hMPR, + (LPSTR)szWNetCloseEnum); + lpfnWNetNukeConn = (PWNETNUKECONN) GetProcAddress(pGlobals->hMPR, + (LPSTR)szWNetNukeConn); + + // + // Check for NULL return values + // + + if ( !lpfnWNetOpenEnum || !lpfnWNetEnumResource || + !lpfnWNetCloseEnum || !lpfnWNetNukeConn ) { + DebugLog((DEB_ERROR, "DeleteNetworkConnections : Received a NULL pointer from GetProcAddress\n")); + goto DNCExit; + } + + // + // Check for at least one network connection + // + + if ( (*lpfnWNetOpenEnum)(RESOURCE_CONNECTED, RESOURCETYPE_ANY, + 0, NULL, &hEnum) == NO_ERROR) { + + if ((*lpfnWNetEnumResource)(hEnum, &dwNumEntries, &NetRes, + &dwEntrySize) == ERROR_NO_MORE_ITEMS) { + bConnectionsExist = FALSE; + } + + (*lpfnWNetCloseEnum)(hEnum); + } + + // + // If we don't have any connections, then we can exit. + // + + if (!bConnectionsExist) { + goto DNCExit; + } + + + // + // Display the status dialog box to the user + // + + hNetDelDlg = CreateDialog (pGlobals->hInstance, + MAKEINTRESOURCE(IDD_WAIT_NET_DRIVES_DISCONNECT), + NULL, + DeleteNetConnectionsDlgProc); + + // + // Delete the network connections. + // + + WNetResult = 0; + + WNetResult = (*lpfnWNetNukeConn)(NULL); + + if (WNetResult != 0 && WNetResult != ERROR_CAN_NOT_COMPLETE) { + DebugLog((DEB_ERROR, "DeleteNetworkConnections : WNetNukeConnections failed, error = %d\n", WNetResult)); + } + + Result = (WNetResult == ERROR_SUCCESS); + + // + // Close the dialog box + // + + if (IsWindow (hNetDelDlg)) { + DestroyWindow (hNetDelDlg); + } + + +DNCExit: + + // + // Unload mpr.dll + // + + if ( pGlobals->hMPR ) { + FreeLibrary(pGlobals->hMPR); + pGlobals->hMPR = NULL; + } + + // + // Revert to being 'ourself' + // + + if (!StopImpersonating(ImpersonationHandle)) { + DebugLog((DEB_ERROR, "DeleteNetworkConnections : Failed to revert to self\n")); + } + + return(Result); +} + +/***************************************************************************\ +* FUNCTION: DeleteNetConnectionsDlgProc +* +* PURPOSE: Processes messages for the deleting net connections dialog +* +* RETURNS: Standard dialog box return values +* +* HISTORY: +* +* 04-26-92 EricFlo Created. +* +\***************************************************************************/ + +BOOL WINAPI +DeleteNetConnectionsDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ) +{ + + switch (message) { + + case WM_INITDIALOG: + CentreWindow(hDlg); + return(TRUE); + + } + + // We didn't process this message + return FALSE; +} + +//+--------------------------------------------------------------------------- +// +// Function: DeleteRasConnections +// +// Synopsis: Delete RAS connections during logoff. +// +// Arguments: (none) +// +// History: 5-10-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL +DeleteRasConnections( + PGLOBALS pGlobals + ) +{ + HANDLE ImpersonationHandle; + SC_HANDLE hServiceMgr, hService; + SERVICE_STATUS status; + RASCONN rasconn; + LPRASCONN lprasconn = &rasconn; + DWORD i, dwErr, dwcb, dwc; + BOOL bRet; + PRASENUMCONNECTIONSW pRasEnumConnectionsW; + PRASHANGUPW pRasHangUpW; + BOOL FreeThatRasConn = FALSE; + + + if ( GetProfileInt( WINLOGON, KEEP_RAS_AFTER_LOGOFF, 0 ) ) + { + return( TRUE ); + } + + // + // Determine whether the rasman service is running. + // + + hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + + if ( hServiceMgr == NULL ) + { + DebugLog((DEB_ERROR, "Unable to object service controller, %d\n", GetLastError())); + return( FALSE ); + } + + hService = OpenService( + hServiceMgr, + RASMAN_SERVICE_NAME, + SERVICE_QUERY_STATUS); + + if (hService == NULL) + { + CloseServiceHandle(hServiceMgr); + DebugLog((DEB_TRACE, "rasman not started, nothing to tear down\n")); + return( TRUE ); + } + + bRet = QueryServiceStatus( hService, &status ); + + CloseServiceHandle(hService); + + CloseServiceHandle(hServiceMgr); + + if (! bRet ) + { + // + // Service in bad state, get out of here... + // + + return( TRUE ); + } + + if (status.dwCurrentState != SERVICE_RUNNING) + { + // + // Service is not running + // + + return( TRUE ); + + } + + // + // Load the RASAPI DLL so we can make it do stuff + // + + if ( !hRasApi ) + { + hRasApi = LoadLibrary( RASAPI32 ); + if ( !hRasApi ) + { + return( FALSE ); + } + } + + pRasEnumConnectionsW = (PRASENUMCONNECTIONSW) GetProcAddress( + hRasApi, + "RasEnumConnectionsW" ); + + pRasHangUpW = (PRASHANGUPW) GetProcAddress( + hRasApi, + "RasHangUpW" ); + + if ( (!pRasEnumConnectionsW) || + (!pRasHangUpW) ) + { + return( FALSE ); + } + + + + // + // Impersonate the user + // + + ImpersonationHandle = ImpersonateUser(&pGlobals->UserProcessData, NULL); + + if (ImpersonationHandle == NULL) { + DebugLog((DEB_ERROR, "DeleteNetworkConnections : Failed to impersonate user\n")); + return(FALSE); + } + + // + // Enumerate the current RAS connections. + // + + + lprasconn->dwSize = sizeof (RASCONN); + + dwcb = sizeof (RASCONN); + + dwc = 1; + + dwErr = pRasEnumConnectionsW(lprasconn, &dwcb, &dwc); + + if (dwErr == ERROR_BUFFER_TOO_SMALL) + { + lprasconn = LocalAlloc(LPTR, dwcb); + + FreeThatRasConn = TRUE; + + if ( !lprasconn ) + { + return( FALSE ); + } + + dwErr = pRasEnumConnectionsW(lprasconn, &dwcb, &dwc); + if (dwErr) + { + if ( FreeThatRasConn ) + { + LocalFree( lprasconn ); + } + + return( FALSE ); + } + } + else if (dwErr) + { + return( FALSE ); + } + + // + // cycle through the connections, and kill them + // + + + for (i = 0; i < dwc; i++) + { + DebugLog((DEB_TRACE, "Hanging up connection to %ws\n", + lprasconn[i].szEntryName)); + + (VOID) pRasHangUpW( lprasconn[i].hrasconn ); + } + + if ( FreeThatRasConn ) + { + LocalFree( lprasconn ); + } + + // + // Revert to being 'ourself' + // + + if (!StopImpersonating(ImpersonationHandle)) { + DebugLog((DEB_ERROR, "DeleteNetworkConnections : Failed to revert to self\n")); + } + + return( TRUE ); +} diff --git a/private/windows/gina/winlogon/logoff.h b/private/windows/gina/winlogon/logoff.h new file mode 100644 index 000000000..52fdded4a --- /dev/null +++ b/private/windows/gina/winlogon/logoff.h @@ -0,0 +1,72 @@ +/****************************** Module Header ******************************\ +* Module Name: logoff.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Define apis user to implement logoff functionality of winlogon +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + + + +// Exported function prototypes +// + +int +InitiateLogoff( + PGLOBALS pGlobals, + LONG Flags + ); + +BOOL +Logoff( + PGLOBALS pGlobals, + int Result + ); + +BOOL +ShutdownMachine( + PGLOBALS pGlobals, + int Flags + ); + +VOID +RebootMachine( + PGLOBALS pGlobals + ); + +VOID +PowerdownMachine( + PGLOBALS pGlobals + ); + +typedef DWORD (*PWNETNUKECONN) ( + HWND + ); + +typedef DWORD (*PWNETOPENENUM) ( + DWORD, + DWORD, + DWORD, + LPNETRESOURCE, + LPHANDLE + ); + +typedef DWORD (*PWNETENUMRESOURCE) ( + HANDLE, + LPDWORD, + LPVOID, + LPDWORD + ); + +typedef DWORD (*PWNETCLOSEENUM) ( + HANDLE + ); + +typedef DWORD +(APIENTRY * PRASENUMCONNECTIONSW)( LPRASCONNW, LPDWORD, LPDWORD ); + +typedef DWORD +(APIENTRY * PRASHANGUPW) ( HRASCONN ); diff --git a/private/windows/gina/winlogon/logon.h b/private/windows/gina/winlogon/logon.h new file mode 100644 index 000000000..79badac00 --- /dev/null +++ b/private/windows/gina/winlogon/logon.h @@ -0,0 +1,23 @@ +/****************************** Module Header ******************************\ +* Module Name: logon.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Define apis user to implement logon functionality of winlogon +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + + +// +// Exported function prototypes +// + + +int +Logon( + PGLOBALS + ); + + diff --git a/private/windows/gina/winlogon/makefile b/private/windows/gina/winlogon/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/windows/gina/winlogon/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/windows/gina/winlogon/makefile.inc b/private/windows/gina/winlogon/makefile.inc new file mode 100644 index 000000000..bba8bc651 --- /dev/null +++ b/private/windows/gina/winlogon/makefile.inc @@ -0,0 +1,14 @@ +!if exist($(TARGET_DIRECTORY).inc) +!include $(TARGET_DIRECTORY).inc +!endif + +res.rc: dialogs.dlg strings.rc win31mig.dlg wlevents.rc + +precomp.h: wlevents.h +wlevents.rc: wlevents.h + +wlevents.h msg00001.bin: wlevents.mc + mc -v wlevents.mc + + + diff --git a/private/windows/gina/winlogon/misc.c b/private/windows/gina/winlogon/misc.c new file mode 100644 index 000000000..364ea4000 --- /dev/null +++ b/private/windows/gina/winlogon/misc.c @@ -0,0 +1,143 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1993. +// +// File: misc.c +// +// Contents: +// +// Classes: +// +// Functions: +// +// History: 8-25-94 RichardW Created +// +//---------------------------------------------------------------------------- + +#include "precomp.h" +#pragma hdrstop + + +/***************************************************************************\ +* ReportWinlogonEvent +* +* Reports winlogon event by calling ReportEvent. +* +* History: +* 10-Dec-93 JohanneC Created +* +\***************************************************************************/ +#define MAX_EVENT_STRINGS 8 + +DWORD +ReportWinlogonEvent( + IN PGLOBALS pGlobals, + IN WORD EventType, + IN DWORD EventId, + IN DWORD SizeOfRawData, + IN PVOID RawData, + IN DWORD NumberOfStrings, + ... + ) +{ + va_list arglist; + ULONG i; + PWSTR Strings[ MAX_EVENT_STRINGS ]; + DWORD rv; + + va_start( arglist, NumberOfStrings ); + + if (NumberOfStrings > MAX_EVENT_STRINGS) { + NumberOfStrings = MAX_EVENT_STRINGS; + } + + for (i=0; i<NumberOfStrings; i++) { + Strings[ i ] = va_arg( arglist, PWSTR ); + } + + if (pGlobals->hEventLog == NULL) { + return ERROR_INVALID_HANDLE; + } + + if (pGlobals->hEventLog != INVALID_HANDLE_VALUE) { + if (!ReportEvent( pGlobals->hEventLog, + EventType, + 0, // event category + EventId, + pGlobals->UserProcessData.UserSid, + (WORD)NumberOfStrings, + SizeOfRawData, + Strings, + RawData) ) { + rv = GetLastError(); + DebugLog((DEB_ERROR, "WINLOGON: ReportEvent( %u ) failed - %u\n", EventId, GetLastError() )); + } else { + rv = ERROR_SUCCESS; + } + } else { + rv = ERROR_INVALID_HANDLE; + } + return rv; +} + +/***************************************************************************\ +* ClearUserProfileData +* +* Resets fields in user profile data. Should be used at startup when structure +* contents are unknown. +* +* History: +* 26-Aug-92 Davidc Created +\***************************************************************************/ +VOID +ClearUserProfileData( + PUSER_PROFILE_INFO UserProfileData + ) +{ + UserProfileData->ProfilePath = NULL; + UserProfileData->ProfilePath = NULL; + UserProfileData->hProfile = NULL; + UserProfileData->PolicyPath = NULL; + UserProfileData->NetworkDefaultUserProfile = NULL; + UserProfileData->ServerName = NULL; + UserProfileData->Environment = NULL; + +} + + + + +/***************************************************************************\ +* TimeoutMessageBox +* +* Same as a normal message box, but times out if there is no user input +* for the specified number of seconds +* For convenience, this api takes string resource ids rather than string +* pointers as input. The resources are loaded from the .exe module +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +int +TimeoutMessageBox( + PGLOBALS pGlobals, + HWND hwnd, + UINT IdText, + UINT IdCaption, + UINT wType) +{ + TCHAR CaptionBuffer[MAX_STRING_BYTES]; + PTCHAR Caption = CaptionBuffer; + TCHAR Text[MAX_STRING_BYTES]; + + LoadString(NULL, IdText, Text, MAX_STRING_LENGTH); + + if (IdCaption != 0) { + LoadString(NULL, IdCaption, Caption, MAX_STRING_LENGTH); + } else { + Caption = NULL; + } + + return WlxMessageBox(pGlobals, hwnd, Text, Caption, wType); +} diff --git a/private/windows/gina/winlogon/misc.h b/private/windows/gina/winlogon/misc.h new file mode 100644 index 000000000..a2ae79f33 --- /dev/null +++ b/private/windows/gina/winlogon/misc.h @@ -0,0 +1,47 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1993. +// +// File: misc.h +// +// Contents: +// +// Classes: +// +// Functions: +// +// History: 8-25-94 RichardW Created +// +//---------------------------------------------------------------------------- + + +DWORD +ReportWinlogonEvent( + IN PGLOBALS pGlobals, + IN WORD EventType, + IN DWORD EventId, + IN DWORD SizeOfRawData, + IN PVOID RawData, + IN DWORD NumberOfStrings, + ... + ); + + +VOID +ClearUserProfileData( + PUSER_PROFILE_INFO UserProfileData + ); + + +int TimeoutMessageBox( + PGLOBALS pGlobals, + HWND hWnd, + UINT IdText, + UINT IdCaption, + UINT wType + ); + + +void +MainLoop(PGLOBALS pGlobals); diff --git a/private/windows/gina/winlogon/monitor.c b/private/windows/gina/winlogon/monitor.c new file mode 100644 index 000000000..a07cf43c0 --- /dev/null +++ b/private/windows/gina/winlogon/monitor.c @@ -0,0 +1,369 @@ +/****************************** Module Header ******************************\ +* Module Name: monitor.c +* +* Copyright (c) 1991, Microsoft Corporation +* +* Implements functions that handle waiting for an object to be signalled +* asynchronously. +* +* History: +* 01-11-93 Davidc Created. +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +// +// Define this to enable verbose output for this module +// + +// #define DEBUG_MONITOR + +#ifdef DEBUG_MONITOR +#define VerbosePrint(s) WLPrint(s) +#else +#define VerbosePrint(s) +#endif + +#define LockMonitor(Monitor) RtlEnterCriticalSection( &Monitor->CritSec ) +#define UnlockMonitor(Monitor) RtlLeaveCriticalSection( &Monitor->CritSec ) + + +//+--------------------------------------------------------------------------- +// +// Function: RefMonitor +// +// Synopsis: Safe Ref Count +// +// Arguments: [Monitor] -- +// +// History: 7-12-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +VOID +RefMonitor( + POBJECT_MONITOR Monitor) +{ + LockMonitor( Monitor ); + + Monitor->RefCount ++ ; + + UnlockMonitor( Monitor ); + +} + + +//+--------------------------------------------------------------------------- +// +// Function: DerefMonitor +// +// Synopsis: Deref the monitor, cleaning up if refcount goes to zero +// +// Arguments: [Monitor] -- +// +// History: 7-12-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +VOID +DerefMonitor( + POBJECT_MONITOR Monitor ) +{ + LockMonitor( Monitor ); + + DebugLog(( DEB_TRACE, "Deref of Monitor at %x\n", Monitor)); + + Monitor->RefCount -- ; + + if ( Monitor->RefCount == 0 ) + { + // + // Clean up time: + // + + DebugLog(( DEB_TRACE, "Cleaning up Monitor at %x\n", Monitor )); + + if ( Monitor->Thread ) + { + CloseHandle( Monitor->Thread ); + } + + if ( Monitor->Flags & MONITOR_CLOSEOBJ ) + { + if ( Monitor->Object != INVALID_HANDLE_VALUE ) + { + CloseHandle( Monitor->Object ); + } + } + + UnlockMonitor( Monitor ); + + RtlDeleteCriticalSection( &Monitor->CritSec ); + + Free( Monitor ); + + } + else + { + UnlockMonitor( Monitor ); + } + +} + +/***************************************************************************\ +* FUNCTION: MonitorThread +* +* PURPOSE: Entry point for object monitor thread +* +* RETURNS: Windows error value +* +* HISTORY: +* +* 01-11-93 Davidc Created. +* +\***************************************************************************/ + +DWORD MonitorThread( + LPVOID lpThreadParameter + ) +{ + POBJECT_MONITOR Monitor = (POBJECT_MONITOR)lpThreadParameter; + DWORD WaitResult; + NTSTATUS Status; + HANDLE Handle; + + // + // Wait forever for object to be signalled + // + + LockMonitor( Monitor ); + + Monitor->Flags |= MONITOR_ACTIVE ; + + Handle = Monitor->Object ; + + RefMonitor( Monitor ); + + UnlockMonitor( Monitor ); + + if ( Handle != INVALID_HANDLE_VALUE ) + { + + Status = NtWaitForSingleObject( Handle, TRUE, NULL); + + if (!NT_SUCCESS(Status) && (Status != STATUS_ALERTED)) + { + DebugLog((DEB_ERROR, "MonitorThread: NtWaitForSingleObject returned %x\n", Status)); + } + } + else + { + Status = STATUS_SUCCESS ; + } + + // + // Notify the appropriate window + // + if (Status != STATUS_ALERTED) + { + PostMessage( Monitor->hwndNotify, + WM_OBJECT_NOTIFY, + (WPARAM)Monitor, + (LPARAM)Monitor->CallerContext + ); + } + + LockMonitor( Monitor ); + + if ( Monitor->Flags & MONITOR_CLOSEOBJ ) + { + CloseHandle( Handle ); + } + + Monitor->Object = INVALID_HANDLE_VALUE ; + + Monitor->Flags = 0 ; + + UnlockMonitor( Monitor ); + + DerefMonitor( Monitor ); + + return(ERROR_SUCCESS); +} + + +/***************************************************************************\ +* FUNCTION: CreateObjectMonitor +* +* PURPOSE: Creates a monitor object that will wait on the specified object +* and post a message to the specifed window when the object is +* signalled. +* +* NOTES: The object must have been opened for SYNCHRONIZE access. +* The caller is responsible for closing the object handle +* after the monitor object has been deleted. +* +* RETURNS: Handle to the monitor instance or NULL on failure. +* +* HISTORY: +* +* 01-11-93 Davidc Created. +* +\***************************************************************************/ + +POBJECT_MONITOR +CreateObjectMonitor( + HANDLE Object, + HWND hwndNotify, + DWORD CallerContext + ) +{ + POBJECT_MONITOR Monitor; + DWORD ThreadId; + NTSTATUS Status; + + // + // Create monitor object + // + + Monitor = Alloc(sizeof(OBJECT_MONITOR)); + if (Monitor == NULL) { + return(NULL); + } + + // + // Initialize monitor fields + // + + Status = RtlInitializeCriticalSection( &Monitor->CritSec ); + if ( !NT_SUCCESS( Status ) ) + { + Free( Monitor ); + return( NULL ); + } + + LockMonitor( Monitor ); + + Monitor->hwndNotify = hwndNotify; + Monitor->Object = Object; + Monitor->CallerContext = CallerContext; + Monitor->RefCount = 1; + Monitor->Flags = 0 ; + + // + // Create the monitor thread + // + + Monitor->Thread = CreateThread( + NULL, // Use default ACL + 0, // Same stack size + MonitorThread, // Start address + (LPVOID)Monitor, // Parameter + 0, // Creation flags + &ThreadId // Get the id back here + ); + + if (Monitor->Thread == NULL) { + + DebugLog((DEB_ERROR, "Failed to create monitor thread, error = %d\n", GetLastError())); + + DerefMonitor( Monitor ); + + return(NULL); + } + + UnlockMonitor( Monitor ); + + return(Monitor); +} + + +/***************************************************************************\ +* FUNCTION: DeleteObjectMonitor +* +* PURPOSE: Deletes an instance of a monitor object +* +* RETURNS: Nothing +* +* HISTORY: +* +* 01-11-93 Davidc Created. +* +\***************************************************************************/ + +VOID +DeleteObjectMonitor( + POBJECT_MONITOR Monitor, + BOOLEAN fTerminate + ) +{ + BOOL Result; + + if ( fTerminate ) + { + CancelObjectMonitor( Monitor ); + } + + DerefMonitor( Monitor ); + +} + +//+--------------------------------------------------------------------------- +// +// Function: CancelObjectMonitor +// +// Synopsis: Interrupts a monitor thread safely. +// +// Arguments: [Monitor] -- +// +// History: 7-12-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +VOID +CancelObjectMonitor( + POBJECT_MONITOR Monitor ) +{ + LockMonitor( Monitor ); + + if ( Monitor->Flags & MONITOR_ACTIVE ) + { + NtAlertThread( Monitor->Thread ); + } + else + { + Monitor->Object = INVALID_HANDLE_VALUE ; + } + + UnlockMonitor( Monitor ); + +} + +//+--------------------------------------------------------------------------- +// +// Function: CloseObjectMonitorObject +// +// Synopsis: Tags the handle to be closed when the monitor goes away +// +// Arguments: [Monitor] -- +// +// History: 7-19-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +VOID +CloseObjectMonitorObject( + POBJECT_MONITOR Monitor + ) +{ + LockMonitor( Monitor ); + + Monitor->Flags |= MONITOR_CLOSEOBJ ; + + UnlockMonitor( Monitor ); +} diff --git a/private/windows/gina/winlogon/monitor.h b/private/windows/gina/winlogon/monitor.h new file mode 100644 index 000000000..85cbabb8e --- /dev/null +++ b/private/windows/gina/winlogon/monitor.h @@ -0,0 +1,66 @@ +/****************************** Module Header ******************************\ +* Module Name: monitor.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Define object monitor interface +* +* History: +* 01-23-91 Davidc Created. +\***************************************************************************/ + +// +// Define a monitor object +// + +typedef struct { + RTL_CRITICAL_SECTION CritSec; + HANDLE Object; + HWND hwndNotify; + DWORD CallerContext; + HANDLE Thread; + DWORD Flags; + DWORD RefCount; +} OBJECT_MONITOR; +typedef OBJECT_MONITOR *POBJECT_MONITOR; + +#define MONITOR_ACTIVE 0x00000001 +#define MONITOR_CLOSEOBJ 0x00000002 + +// +// Define notification message sent when object is signalled +// wParam = monitor object handle +// lParam = caller context +// + +#define WM_OBJECT_NOTIFY (WM_USER + 800) + + +// +// Exported function prototypes +// + +POBJECT_MONITOR +CreateObjectMonitor( + HANDLE Object, + HWND hwndNotify, + DWORD CallerContext + ); + +VOID +DeleteObjectMonitor( + POBJECT_MONITOR Monitor, + BOOLEAN fTerminate + ); + +VOID +CancelObjectMonitor( + POBJECT_MONITOR Monitor ); + +VOID +CloseObjectMonitorObject( + POBJECT_MONITOR Monitor + ); + +#define GetObjectMonitorCallerContext(Monitor) (Monitor->Context) +#define GetObjectMonitorObject(Monitor) (Monitor->Object) diff --git a/private/windows/gina/winlogon/msgalias.c b/private/windows/gina/winlogon/msgalias.c new file mode 100644 index 000000000..fa8b25e58 --- /dev/null +++ b/private/windows/gina/winlogon/msgalias.c @@ -0,0 +1,171 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + msgalias.c + +Abstract: + + This file contains routines for adding and deleting message aliases + when a user logs on/off. + +Author: + + Dan Lafferty (danl) 21-Aug-1992 + +Environment: + + User Mode -Win32 + +Revision History: + + 21-Aug-1992 danl + created + +--*/ +#include "precomp.h" +#pragma hdrstop + +#define LPTSTR LPWSTR + + + + +VOID +DeleteMsgAliases( + VOID + ) + +/*++ + +Routine Description: + + This function removes all message aliases except the ComputerName. + +Arguments: + + none + +Return Value: + + none + +--*/ +{ + DWORD status; + LPMSG_INFO_0 InfoStruct; + DWORD entriesRead; + DWORD totalEntries; + DWORD resumeHandle = 0; + DWORD i; + WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + LPWSTR NewServerName = NULL; + DWORD bufLen = MAX_COMPUTERNAME_LENGTH + 1; + HANDLE dllHandle; + PMSG_NAME_DEL NetMessageNameDel = NULL; + PMSG_NAME_ENUM NetMessageNameEnum = NULL; + + // + // Get the computer name + // + + if (!GetComputerNameW(ComputerName,&bufLen)) { + DebugLog((DEB_ERROR, "failed to obtain the computername")); + } + + // + // Get the address of the functions we need from netapi32.dll + // + dllHandle = LoadLibraryW(L"netapi32.dll"); + if (dllHandle == NULL) { + return; + } + + + NetMessageNameEnum = (PMSG_NAME_ENUM) GetProcAddress( + dllHandle, + "NetMessageNameEnum"); + + if (NetMessageNameEnum == NULL) { + FreeLibrary(dllHandle); + return; + } + + NetMessageNameDel = (PMSG_NAME_DEL) GetProcAddress( + dllHandle, + "NetMessageNameDel"); + + if (NetMessageNameDel == NULL) { + FreeLibrary(dllHandle); + return; + } + + // + // Enumerate all the Message Aliases + // + + status = NetMessageNameEnum ( + NULL, // ServerName - Local version + 0, // Level + (LPBYTE *)&InfoStruct, // return status buffer pointer + 0xffffffff, // preferred max length + &entriesRead, // entries read + &totalEntries, // total entries + &resumeHandle); // resume handle + + if (status != NERR_Success) { + // DebugLog((DEB_ERROR, "NetMessageNameEnum failed %d",status)); + FreeLibrary(dllHandle); + return; + } + + // + // Remove the aliases that are not the computername. + // + for (i=0; i<entriesRead ;i++) { + + if (_wcsicmp(InfoStruct->msgi0_name, ComputerName) != 0) { + + status = NetMessageNameDel( + NULL, + InfoStruct->msgi0_name); + if (status != NERR_Success) { + DebugLog((DEB_ERROR, "DeleteMsgAliases: Name - %ws - delete failed", + InfoStruct->msgi0_name)); + } + } + InfoStruct++; + } + + FreeLibrary(dllHandle); +} + + +VOID +TickleMessenger(VOID) +{ + HANDLE hDll; + PMSG_NAME_DEL NetMessageNameDel = NULL; + + // + // Get the address of the functions we need from netapi32.dll + // + hDll = LoadLibraryW(L"netapi32.dll"); + if (hDll == NULL) { + return; + } + + NetMessageNameDel = (PMSG_NAME_DEL) GetProcAddress( + hDll, + "NetMessageNameDel"); + + if (NetMessageNameDel) { + + NetMessageNameDel(NULL, TEXT("")); + } + + FreeLibrary(hDll); + +} diff --git a/private/windows/gina/winlogon/msgalias.h b/private/windows/gina/winlogon/msgalias.h new file mode 100644 index 000000000..07622676c --- /dev/null +++ b/private/windows/gina/winlogon/msgalias.h @@ -0,0 +1,67 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + msgalias.h + +Abstract: + + Prototypes for functions that add/delete message aliases. + +Author: + + Dan Lafferty (danl) 20-Mar-1991 + +Environment: + + User Mode -Win32 + +Notes: + + optional-notes + +Revision History: + + 20-Mar-1991 danl + created + . + . + least-recent-revision-date email-name + description + +--*/ + +// +// GetProcAddr Prototypes +// + +typedef DWORD (*PMSG_NAME_ENUM) ( + LPWSTR servername, + DWORD level, + LPBYTE *bufptr, + DWORD prefmaxlen, + LPDWORD entriesread, + LPDWORD totalentries, + LPDWORD resume_handle + ); + +typedef DWORD (*PMSG_NAME_DEL) ( + LPWSTR servername, + LPWSTR msgname + ); + + +// +// Function Prototypes +// + +VOID +DeleteMsgAliases( + VOID + ); + + +VOID +TickleMessenger(VOID); diff --git a/private/windows/gina/winlogon/precomp.h b/private/windows/gina/winlogon/precomp.h new file mode 100644 index 000000000..7615dd702 --- /dev/null +++ b/private/windows/gina/winlogon/precomp.h @@ -0,0 +1,41 @@ +#include "winlogon.h" +#include <string.h> +#include <stdio.h> +#include <npapi.h> +#include "doslog.h" +#include <sys\types.h> +#include <sys\stat.h> +#include <io.h> +#include <fcntl.h> +#include "mpr.h" +#include <stddef.h> +// #include "winp.h" +// #include "winnls32.h" +// #include "ime.h" +// #include "winnls3p.h" +#include <lmcons.h> +#include <lmerr.h> +#include <lmmsg.h> +#include <malloc.h> +#include <stdlib.h> +#include "sysshut.h" +#include <winsvc.h> +#include "crypt.h" +#include <ntsam.h> +#include <lmapibuf.h> +#include <lmaccess.h> +#include <wchar.h> + +#ifdef _X86_ +#include "os2ssrtl.h" +#endif + +#include <stdarg.h> +#include <errno.h> +#include <ctype.h> +#include "setup.h" +// #include "regrpc.h" +#include "ntrpcp.h" +#include <rpc.h> +#include <winreg.h> +#include <userenv.h> diff --git a/private/windows/gina/winlogon/provider.c b/private/windows/gina/winlogon/provider.c new file mode 100644 index 000000000..566a6a7c2 --- /dev/null +++ b/private/windows/gina/winlogon/provider.c @@ -0,0 +1,1045 @@ +/****************************** Module Header ******************************\ +* Module Name: provider.c +* +* Copyright (c) 1991, Microsoft Corporation +* +* Implements functions that support multiple network providers. +* Currently this involves notifying credential managers of logon and +* password change operations. +* +* History: +* 01-10-93 Davidc Created. +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +// +// Define this to enable verbose output for this module +// + +// #define DEBUG_PROVIDER + +#ifdef DEBUG_PROVIDER +#define VerbosePrint(s) WLPrint(s) +#else +#define VerbosePrint(s) +#endif + + +// +// Define the key in the winlogon section of win.ini that +// defines the the multiple provider notify app name. +// + +#define NOTIFY_KEY_NAME TEXT("mpnotify") + +// +// Define the default multiple provider notify app name. +// + +#define DEFAULT_NOTIFY_APP_NAME TEXT("mpnotify.exe") + + +// +// Define environment variables used to pass information to multiple +// provider notify process +// + +#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") + + +// Message we send to ourselves so we can hide. +#define WM_HIDEOURSELVES (WM_USER + 0) + + + +// +// Define the structure used to pass data into the notify control dialog +// + +typedef struct { + PGLOBALS pGlobals; + LPWSTR ReturnBuffer; // Returned from dialog + HANDLE hProcess; + POBJECT_MONITOR Monitor; + BOOL ProcessRunning; +} NOTIFY_DATA; +typedef NOTIFY_DATA *PNOTIFY_DATA; + + + + +// +// Private prototypes +// + +BOOL +MprNotifyDlgInit( + HWND hDlg + ); + +BOOL +StartNotifyProcessMonitor( + HWND hDlg + ); + +VOID +DeleteNotifyProcessMonitor( + HWND hDlg + ); + +BOOL +KillNotifyProcess( + PNOTIFY_DATA pNotifyData + ); + + +/***************************************************************************\ +* FUNCTION: DeleteNotifyVariables +* +* PURPOSE: Deletes all the notify data environment variables from the +* current process's environment. +* +* RETURNS: Nothing +* +* HISTORY: +* +* 01-12-93 Davidc Created. +* +\***************************************************************************/ + +VOID +DeleteNotifyVariables( + VOID + ) +{ + SetEnvironmentVariable(MPR_STATION_NAME_VARIABLE, NULL); + SetEnvironmentVariable(MPR_STATION_HANDLE_VARIABLE, NULL); + SetEnvironmentVariable(MPR_WINLOGON_WINDOW_VARIABLE, NULL); + + SetEnvironmentVariable(MPR_LOGON_FLAG_VARIABLE, NULL); + SetEnvironmentVariable(MPR_USERNAME_VARIABLE, NULL); + SetEnvironmentVariable(MPR_DOMAIN_VARIABLE, NULL); + SetEnvironmentVariable(MPR_PASSWORD_VARIABLE, NULL); + SetEnvironmentVariable(MPR_OLD_PASSWORD_VALID_VARIABLE, NULL); + SetEnvironmentVariable(MPR_OLD_PASSWORD_VARIABLE, NULL); + SetEnvironmentVariable(MPR_LOGONID_VARIABLE, NULL); + SetEnvironmentVariable(MPR_CHANGE_INFO_VARIABLE, NULL); + SetEnvironmentVariable(MPR_PASSTHROUGH_VARIABLE, NULL); + SetEnvironmentVariable(MPR_PROVIDER_VARIABLE, NULL); +} + + +/***************************************************************************\ +* FUNCTION: SetWinlogonWindowVariable +* +* PURPOSE: Sets winlogon window environment variable in current process's +* environment - this is inherited by notify process. +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 01-12-93 Davidc Created. +* +\***************************************************************************/ + +BOOL +SetWinlogonWindowVariable( + HWND hwnd + ) +{ + BOOL Result; + + Result = SetEnvironmentULong(MPR_WINLOGON_WINDOW_VARIABLE, (ULONG)hwnd); + + if (!Result) { + DebugLog((DEB_ERROR, "SetWinlogonWindowVariable: Failed to set variable, error = %d\n", GetLastError())); + } + + return(Result); +} + + +/***************************************************************************\ +* FUNCTION: SetCommonNotifyVariables +* +* PURPOSE: Sets environment variables to pass information to notify process +* for data that is common to all notifications. +* The variables are set in winlogon's environment - this is +* inherited by the notify process. +* +* RETURNS: TRUE on success, FALSE on failure +* +* On failure return, all notify variables have been deleted +* +* HISTORY: +* +* 01-12-93 Davidc Created. +* +\***************************************************************************/ + +BOOL +SetCommonNotifyVariables( + PGLOBALS pGlobals, + HWND hwndOwner, + LPTSTR Name OPTIONAL, + LPTSTR Domain OPTIONAL, + LPTSTR Password OPTIONAL, + LPTSTR OldPassword OPTIONAL + ) +{ + BOOL Result = TRUE; + + if (Result) { + Result = SetEnvironmentVariable(MPR_STATION_NAME_VARIABLE, WINDOW_STATION_NAME); + } + if (Result) { + Result = SetEnvironmentULong(MPR_STATION_HANDLE_VARIABLE, (ULONG)hwndOwner); + } + + if (Result && ARGUMENT_PRESENT( Name )) { + Result = SetEnvironmentVariable(MPR_USERNAME_VARIABLE, Name); + } + if (Result && ARGUMENT_PRESENT( Domain )) { + Result = SetEnvironmentVariable(MPR_DOMAIN_VARIABLE, Domain); + } + if (Result && ARGUMENT_PRESENT( Password )) { + Result = SetEnvironmentVariable(MPR_PASSWORD_VARIABLE, Password); + } + if (Result) { + Result = SetEnvironmentULong(MPR_OLD_PASSWORD_VALID_VARIABLE, + (OldPassword != NULL) ? 1 : 0); + } + if (Result) { + Result = SetEnvironmentVariable(MPR_OLD_PASSWORD_VARIABLE, OldPassword); + if (OldPassword == NULL) { + Result = TRUE; // Ignore failure since deleting a variable that + // doesn't exist returns failure. + } + } + + if (!Result) { + DebugLog((DEB_ERROR, "SetCommonNotifyVariables: Failed to set a variable, error = %d\n", GetLastError())); + DeleteNotifyVariables(); + } + + return(Result); +} + + +/***************************************************************************\ +* FUNCTION: SetLogonNotifyVariables +* +* PURPOSE: Sets environment variables to pass information to notify process +* for data that is specific to logon notifications. +* The variables are set in winlogon's environment - this is +* inherited by the notify process. +* +* RETURNS: TRUE on success, FALSE on failure +* +* On failure return, all notify variables have been deleted +* +* HISTORY: +* +* 01-12-93 Davidc Created. +* +\***************************************************************************/ + +BOOL +SetLogonNotifyVariables( + PLUID LogonId + ) +{ + BOOL Result; + LARGE_INTEGER LargeInt; + + LargeInt.LowPart = LogonId->LowPart; + LargeInt.HighPart = LogonId->HighPart; + Result = SetEnvironmentLargeInt(MPR_LOGONID_VARIABLE, LargeInt); + if (Result) { + Result = SetEnvironmentULong(MPR_LOGON_FLAG_VARIABLE, 1); + } + + if (!Result) { + DebugLog((DEB_ERROR, "SetLogonNotifyVariables: Failed to set variable, error = %d\n", GetLastError())); + DeleteNotifyVariables(); + } + + return(Result); +} + + +/***************************************************************************\ +* FUNCTION: SetChangePasswordNotifyVariables +* +* PURPOSE: Sets environment variables to pass information to notify process +* for data that is specific to change password notifications. +* The variables are set in winlogon's environment - this is +* inherited by the notify process. +* +* RETURNS: TRUE on success, FALSE on failure +* +* On failure return, all notify variables have been deleted +* +* HISTORY: +* +* 01-12-93 Davidc Created. +* +\***************************************************************************/ + +BOOL +SetChangePasswordNotifyVariables( + DWORD ChangeInfo, + BOOL PassThrough, + PWSTR Provider OPTIONAL + ) +{ + BOOL Result; + + Result = SetEnvironmentULong(MPR_CHANGE_INFO_VARIABLE, ChangeInfo); + if (Result) { + Result = SetEnvironmentULong(MPR_LOGON_FLAG_VARIABLE, 0); + } + + if (Result) { + Result = SetEnvironmentULong(MPR_PASSTHROUGH_VARIABLE, (PassThrough ? 1 : 0)); + } + + if (Result && ARGUMENT_PRESENT( Provider ) ) + { + Result = SetEnvironmentVariable( MPR_PROVIDER_VARIABLE, Provider ); + } + + if (!Result) { + DebugLog((DEB_ERROR, "SetChangePasswordNotifyVariables: Failed to set variable, error = %d\n", GetLastError())); + DeleteNotifyVariables(); + } + + return(Result); +} + + +/***************************************************************************\ +* FUNCTION: MprNotifyDlgProc +* +* PURPOSE: Processes messages for the Mpr Notify dialog +* +* RETURNS: DLG_SUCCESS - the notification went without a hitch +* - NotifyData->ReturnBuffer is valid. +* DLG_FAILURE - something failed or there is no buffer to return. +* - NotifyData->ReturnBuffer is invalid. +* +* DLG_INTERRUPTED() - a set defined in winlogon.h +* +* HISTORY: +* +* 01-11-93 Davidc Created. +* +\***************************************************************************/ + +BOOL WINAPI +MprNotifyDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ) +{ + PNOTIFY_DATA pNotifyData = (PNOTIFY_DATA)GetWindowLong(hDlg, GWL_USERDATA); + PCOPYDATASTRUCT CopyData; + + switch (message) { + + case WM_INITDIALOG: + SetWindowLong(hDlg, GWL_USERDATA, lParam); + + if (!MprNotifyDlgInit(hDlg)) { + EndDialog(hDlg, DLG_FAILURE); + return(TRUE); + } + + // + // Send ourselves a message so we can hide without the + // dialog code trying to force us to be visible + // + + PostMessage(hDlg, WM_HIDEOURSELVES, 0, 0); + return(TRUE); + + + case WM_HIDEOURSELVES: + ShowWindow(hDlg, SW_HIDE); + return(TRUE); + + + case WLX_WM_SAS: + + if (wParam == WLX_SAS_TYPE_USER_LOGOFF) + { + DebugLog((DEB_TRACE_MPR, "Got a logoff notification\n")); + } + // + // Interrupt the notify process + // This gives us a way to terminate the notify process if it hangs up. + // + + DebugLog((DEB_TRACE_MPR, "Got SAS message - interrupting notify process\n")); + EndDialog(hDlg, DLG_FAILURE); + return(TRUE); + + + case WM_COPYDATA: + + // + // The notify process completed and is passing us the result + // + + CopyData = (PCOPYDATASTRUCT)lParam; + + DebugLog((DEB_TRACE_MPR, "Got WM_COPYDATA message from notify process\n")); + DebugLog((DEB_TRACE_MPR, "/tdwData = %d", CopyData->dwData)); + DebugLog((DEB_TRACE_MPR, "/tcbData = %d", CopyData->cbData)); + + // + // End the screen-saver if it's running + // This assumes the screen-saver dialog terminates when it gets SAS. + // If it's not running this will come straight to us which is OK + // + + DebugLog((DEB_TRACE_MPR, "Forwarding SAS message to top window\n")); + //ForwardMessage(pNotifyData->pGlobals, WM_SAS, 0, 0); + + + // + // Copy the passed data and quit this dialog + // + + if (CopyData->dwData == 0) { + if (CopyData->cbData != 0) { + pNotifyData->ReturnBuffer = Alloc(CopyData->cbData); + if (pNotifyData->ReturnBuffer != NULL) { + CopyMemory(pNotifyData->ReturnBuffer, CopyData->lpData, CopyData->cbData); + } else { + DebugLog((DEB_ERROR, ("Failed to allocate memory for returned logon scripts"))); + } + } else { + pNotifyData->ReturnBuffer = NULL; + } + + } else { + DebugLog((DEB_TRACE_MPR, "Notify completed with an error: %d", CopyData->dwData)); + } + + EndDialog(hDlg, pNotifyData->ReturnBuffer ? DLG_SUCCESS : DLG_FAILURE); + + return(TRUE); // We processed this message + + + + case WM_OBJECT_NOTIFY: + + // + // The notify process terminated for some reason + // + + DebugLog((DEB_TRACE_MPR, "Notify process terminated - got monitor notification\n")); + EndDialog(hDlg, DLG_FAILURE); + return(TRUE); + + + + case WM_DESTROY: + + // + // Terminate the notify process and delete the monitor object. + // + + if (pNotifyData->ProcessRunning) { + + DebugLog((DEB_TRACE_MPR, "NotifyDlgProc: Deleting notify process and monitor\n")); + + DeleteNotifyProcessMonitor(hDlg); + KillNotifyProcess(pNotifyData); + } + + return(0); + } + + + // We didn't process the message + return(FALSE); +} + + +/***************************************************************************\ +* FUNCTION: MprNotifyDlgInit +* +* PURPOSE: Handles initialization of Mpr notify dialog +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 01-11-93 Davidc Created. +* +\***************************************************************************/ + +#if DEVL +BOOL bDebugMpNotify = FALSE; +#endif + +BOOL +MprNotifyDlgInit( + HWND hDlg + ) +{ + PNOTIFY_DATA pNotifyData = (PNOTIFY_DATA)GetWindowLong(hDlg, GWL_USERDATA); + PGLOBALS pGlobals = pNotifyData->pGlobals; + USER_PROCESS_DATA SystemProcessData; + BOOL Success; + LPTSTR NotifyApp; + PROCESS_INFORMATION ProcessInformation; + PWSTR pchCmdLine; +#if DEVL + WCHAR chDebugCmdLine[ MAX_PATH ]; +#endif + + // + // Initialize flag to show we haven't created the notify process yet + // + + pNotifyData->ProcessRunning = FALSE; + + // + // Set our size to zero so we we don't appear + // + + SetWindowPos(hDlg, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOREDRAW | SWP_NOZORDER); + + // + // Set the winlogon window variable so the process knows who we are + // + + SetWinlogonWindowVariable(hDlg); + + // + // Start the notify process in system context + // + + SystemProcessData.UserToken = NULL; + SystemProcessData.UserSid = pGlobals->WinlogonSid; + SystemProcessData.NewProcessSD = NULL; + SystemProcessData.NewProcessTokenSD = NULL; + SystemProcessData.NewThreadSD = NULL; + SystemProcessData.NewThreadTokenSD = NULL; + SystemProcessData.Quotas.PagedPoolLimit = 0; + SystemProcessData.CurrentDirectory = NULL; + SystemProcessData.pEnvironment = NULL; // Inherit our environment + + // + // Get the name of the notify app + // + + NotifyApp = AllocAndGetProfileString(WINLOGON, NOTIFY_KEY_NAME, DEFAULT_NOTIFY_APP_NAME); + if (NotifyApp == NULL) { + DebugLog((DEB_ERROR, "Failed to get name of provider notify app from registry\n")); + return(FALSE); + } + + pchCmdLine = NotifyApp; + + // + // Try and execute it + // +#if DEVL + if (bDebugMpNotify) { + wsprintf( chDebugCmdLine, TEXT("ntsd -d %s%s"), + bDebugMpNotify == 2 ? TEXT("-g -G ") : TEXT(""), + pchCmdLine + ); + pchCmdLine = chDebugCmdLine; + } +#endif + + + Success = StartSystemProcess(pchCmdLine, + WINLOGON_DESKTOP_PATH, + 0, + 0, + NULL, + FALSE, + &pNotifyData->hProcess, + NULL); + + Free(NotifyApp); + + if (!Success) { + DebugLog((DEB_ERROR, "Failed to start multiple provider notifier\n")); + return(FALSE); + } + + // + // Store the process id in our notify data for future reference + // + + + + // + // Start the thread that will wait for the notify process to finish + // + + if (!StartNotifyProcessMonitor(hDlg)) { + + DebugLog((DEB_ERROR, "Failed to start notify process monitor thread\n")); + KillNotifyProcess(pNotifyData); + return(FALSE); + } + + // + // Record the fact we started the notify process so we know + // to cleanup during WM_DESTROY + // + + pNotifyData->ProcessRunning = TRUE; + + // Success + return (TRUE); +} + + +/***************************************************************************\ +* FUNCTION: StartNotifyProcessMonitor +* +* PURPOSE: Creates a thread that waits for the notify process to terminate +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 01-11-93 Davidc Created. +* +\***************************************************************************/ + +BOOL +StartNotifyProcessMonitor( + HWND hDlg + ) +{ + PNOTIFY_DATA NotifyData = (PNOTIFY_DATA)GetWindowLong(hDlg, GWL_USERDATA); + + NotifyData->Monitor = CreateObjectMonitor(NotifyData->hProcess, hDlg, 0); + + if (NotifyData->Monitor == NULL) { + DebugLog((DEB_ERROR, "Failed to create notify process monitor object\n")); + return(FALSE); + } + + return TRUE; +} + + +/***************************************************************************\ +* FUNCTION: DeleteNotifyProcessMonitor +* +* PURPOSE: Cleans up resources used by notify process monitor +* +* RETURNS: Nothing +* +* HISTORY: +* +* 01-11-93 Davidc Created. +* +\***************************************************************************/ + +VOID +DeleteNotifyProcessMonitor( + HWND hDlg + ) +{ + PNOTIFY_DATA NotifyData = (PNOTIFY_DATA)GetWindowLong(hDlg, GWL_USERDATA); + POBJECT_MONITOR Monitor = NotifyData->Monitor; + HANDLE ProcessHandle = GetObjectMonitorObject(Monitor); + + // + // Delete the object monitor + // + + DeleteObjectMonitor(Monitor, TRUE); +} + + +/***************************************************************************\ +* FUNCTION: KillNotifyProcess +* +* PURPOSE: Terminates the notify process +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 01-11-93 Davidc Created. +* +\***************************************************************************/ + +BOOL +KillNotifyProcess( + PNOTIFY_DATA NotifyData + ) +{ + + if (!TerminateProcess(NotifyData->hProcess, STATUS_SUCCESS)) { + DebugLog((DEB_ERROR, "Failed to terminate notification process, error = %d\n", GetLastError())); + return(FALSE); + } + + CloseHandle(NotifyData->hProcess); + + return(TRUE); +} + + +/***************************************************************************\ +* FUNCTION: NoNeedToNotify +* +* PURPOSE: Determines if it is necessary to call the notify apis. +* It is not necessary if there is only one provider installed. +* +* We use this to save time in the common case where there is +* only one provider. We can avoid the overhead of creating +* the notify process in this case. +* +* RETURNS: TRUE if there is only one provider, otherwise FALSE +* +* HISTORY: +* +* 01-11-93 Davidc Created. +* +\***************************************************************************/ + +#define NET_PROVIDER_ORDER_KEY TEXT("system\\CurrentControlSet\\Control\\NetworkProvider\\Order") +#define NET_PROVIDER_ORDER_VALUE TEXT("ProviderOrder") +#define NET_ORDER_SEPARATOR TEXT(',') + +BOOL +NoNeedToNotify( + VOID + ) +{ + HKEY ProviderKey; + DWORD Error; + DWORD ValueType; + LPTSTR Value; + BOOL NeedToNotify = TRUE; + + Error = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, // hKey + NET_PROVIDER_ORDER_KEY, // lpSubKey + 0, // Must be 0 + KEY_QUERY_VALUE, // Desired access + &ProviderKey // Newly Opened Key Handle + ); + + if (Error != ERROR_SUCCESS) { + DebugLog((DEB_ERROR, "NoNeedToNotify - failed to open provider key, assuming notification is necessary\n")); + return(!NeedToNotify); + } + + Value = AllocAndRegQueryValueEx( + ProviderKey, // Key + NET_PROVIDER_ORDER_VALUE,// Value name + NULL, // Must be NULL + &ValueType // Type returned here + ); + + if (Value != NULL) { + if (ValueType == REG_SZ) { + + LPTSTR p = Value; + while (*p) { + if (*p == NET_ORDER_SEPARATOR) { + break; + } + p = CharNext(p); + } + + if (*p == 0) { + + // + // We got to the end without finding a separator + // Only one provider is installed. + // + + if (lstrcmpi(Value, SERVICE_WORKSTATION) == 0) { + + // + // it's Lanman, don't notify + // + + NeedToNotify = FALSE; + + + } else { + + // + // it isn't Lanman, notify + // + + NeedToNotify = TRUE; + } + } + + } else { + DebugLog((DEB_ERROR, "NoNeedToNotify - provider order key unexpected type: %d, assuming notification is necessary", ValueType)); + } + + Free(Value); + + } else { + DebugLog((DEB_ERROR, "NoNeedToNotify - failed to query provider order value, assuming notification is necessary\n")); + } + + Error = RegCloseKey(ProviderKey); + ASSERT(Error == ERROR_SUCCESS); + + return(!NeedToNotify); +} + + + +/***************************************************************************\ +* MprLogonNotify +* +* Purpose : Notifies credential managers of a logon. +* +* RETURNS: DLG_SUCCESS - the notification went without a hitch +* DLG_FAILURE - something failed. +* DLG_INTERRUPTED() - a set of interruptions defined in winlogon.h +* +* On DLG_SUCCESS return MprLogonScripts contains a pointer to a +* Multi-sz string or NULL if there is no data. i.e. multiple concatenated +* zero terminated strings with a final terminator. +* The memory should be freed by the caller (if pointer non-NULL) using Free(). +* +* History: +* 11-12-92 Davidc Created. +\***************************************************************************/ + +int +MprLogonNotify( + PGLOBALS pGlobals, + HWND hwndOwner, + LPTSTR Name, + LPTSTR Domain, + LPTSTR Password, + LPTSTR OldPassword OPTIONAL, + PLUID LogonId, + LPWSTR *MprLogonScripts + ) +{ + int Result; + NOTIFY_DATA NotifyData; + + // + // Check if we really need to bother with this + // + + if (NoNeedToNotify()) { + DebugLog((DEB_TRACE_MPR, "MprLogonNotify - skipping notification - only one provider\n")); + *MprLogonScripts = NULL; + return(DLG_SUCCESS); + } + + // + // Set up the environment variables that we will use to pass + // information to notify process + // + + if (!SetCommonNotifyVariables(pGlobals, + hwndOwner, + Name, + Domain, + Password, + OldPassword + )) { + return(DLG_FAILURE); + } + + if (!SetLogonNotifyVariables(LogonId)) { + return(DLG_FAILURE); + } + + + // + // Initialize our notify data structure + // + + NotifyData.pGlobals = pGlobals; + NotifyData.ReturnBuffer = NULL; + + // + // Update windowstation lock so mpnotify can start. + // + + UnlockWindowStation(pGlobals->WindowStation.hwinsta); + FastSetWinstaSecurity( &pGlobals->WindowStation, + FALSE ); + + + // + // Create the dialog that will initiate the notify and wait + // for it to complete + // + + Result = WlxDialogBoxParam( pGlobals, + pGlobals->hInstance, + (LPTSTR)IDD_CONTROL, + hwndOwner, + MprNotifyDlgProc, + (LONG)&NotifyData); + + if (Result == DLG_SUCCESS) { + DebugLog((DEB_TRACE_MPR, "Logon notification return buffer (first string only) = <%ws>\n", NotifyData.ReturnBuffer)); + *MprLogonScripts = NotifyData.ReturnBuffer; + } else { + DebugLog((DEB_TRACE_MPR, "Logon notification failed\n")); + } + + // + // Re-lock the windowstation. + // + + LockWindowStation(pGlobals->WindowStation.hwinsta); + + DeleteNotifyVariables(); + + return(Result); +} + + + +/***************************************************************************\ +* MprChangePasswordNotify +* +* Purpose : Notifies credential managers of a password change +* +* RETURNS: DLG_SUCCESS - the notification went without a hitch +* DLG_FAILURE - something failed. +* DLG_INTERRUPTED() - a set of interruptions defined in winlogon.h +* +* History: +* 01-12-93 Davidc Created. +\***************************************************************************/ + +int +MprChangePasswordNotify( + PGLOBALS pGlobals, + HWND hwndOwner, + PWSTR Provider, + LPTSTR Name, + LPTSTR Domain, + LPTSTR Password, + LPTSTR OldPassword, + DWORD ChangeInfo, + BOOL PassThrough + ) +{ + int Result; + NOTIFY_DATA NotifyData; + + // + // Check if we really need to bother with this + // + + if (NoNeedToNotify()) { + DebugLog((DEB_TRACE_MPR, "MprChangePasswordNotify - skipping notification - only one provider\n")); + return(DLG_SUCCESS); + } + + // + // Set up the environment variables that we will use to pass + // information to notify process + // + + if (!SetCommonNotifyVariables(pGlobals, + hwndOwner, + Name, + Domain, + Password, + OldPassword + )) { + return(DLG_FAILURE); + } + + if (!SetChangePasswordNotifyVariables(ChangeInfo, + PassThrough, + Provider ) ) + { + return(DLG_FAILURE); + } + + + // + // Initialize our notify data structure + // + + NotifyData.pGlobals = pGlobals; + NotifyData.ReturnBuffer = NULL; + + + // + // Update windowstation security so mpnotify can start. + // + + FastSetWinstaSecurity( &pGlobals->WindowStation, + FALSE ); + + // + // Create the dialog that will initiate the notify and wait + // for it to complete + // + + // + // Set timeout to 5 minutes, so the nwcs provider has time to run. + // + + WlxSetTimeout( pGlobals, 5 * 60 ); + + Result = WlxDialogBoxParam( pGlobals, + pGlobals->hInstance, + (LPTSTR)IDD_CONTROL, + hwndOwner, + MprNotifyDlgProc, + (LONG)&NotifyData); + // + // Reset the windowstation security. + // + + FastSetWinstaSecurity( &pGlobals->WindowStation, + TRUE ); + + if (Result == DLG_SUCCESS) { + Free(NotifyData.ReturnBuffer); + } else { + DebugLog((DEB_TRACE_MPR, "Change password notification failed\n")); + } + + DeleteNotifyVariables(); + + return(Result); +} diff --git a/private/windows/gina/winlogon/provider.h b/private/windows/gina/winlogon/provider.h new file mode 100644 index 000000000..0536d7b2f --- /dev/null +++ b/private/windows/gina/winlogon/provider.h @@ -0,0 +1,45 @@ +/****************************** Module Header ******************************\ +* Module Name: provider.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Define functions that support multiple network providers +* +* History: +* 01-13-93 Davidc Created. +\***************************************************************************/ + + +// +// Exported function prototypes +// + +int +MprLogonNotify( + PGLOBALS pGlobals, + HWND hwndOwner, + LPTSTR Name, + LPTSTR Domain, + LPTSTR Password, + LPTSTR OldPassword OPTIONAL, + PLUID LogonId, + LPWSTR *MprLogonScripts + ); + +int +MprChangePasswordNotify( + PGLOBALS pGlobals, + HWND hwndOwner, + PWSTR Provider, + LPTSTR Name, + LPTSTR Domain, + LPTSTR Password, + LPTSTR OldPassword, + DWORD ChangeInfo, + BOOL PassThrough + ); + +BOOL +NoNeedToNotify( + VOID + ); diff --git a/private/windows/gina/winlogon/regini.c b/private/windows/gina/winlogon/regini.c new file mode 100644 index 000000000..6c1c7183d --- /dev/null +++ b/private/windows/gina/winlogon/regini.c @@ -0,0 +1,2527 @@ +#include "precomp.h" +#pragma hdrstop + + +#if INIT_REGISTRY + +VOID +TmppSetUnsecureDefaultDacl( VOID ); + +PGLOBALS pLocalGlobals; + +BOOL RunNetDetect = 0; +BOOL ExtendedNetSetup = FALSE; +BOOL NetSetupGoingToRun = FALSE; +BOOL NetFound = FALSE; +BOOL KeepScript = FALSE; +char WinlogonSystemVariable[ 1024 ]; +PCHAR WinlogonShellVariable = NULL; + +char InputFileName[ MAX_PATH ]; +FILE *fh; +char LineBuffer[ 1024 ]; +int LineIndent; +int LineNumber; + +char MessageBuffer[ 512 ]; +char AnsiWinlogon[] = "Winlogon"; +WCHAR WideWinlogon[] = L"Winlogon"; + +BOOL +DeclareError( + char *Format, + ... + ); + +BOOL +DeclareError( + char *Format, + ... + ) +{ + char *s; + size_t cb; + va_list arglist; + + va_start(arglist, Format); + + cb = _snprintf( MessageBuffer, + sizeof( MessageBuffer ), + "Winlogon: %s(%u)", + InputFileName, + LineNumber + ); + s = MessageBuffer + cb; + *s++ = '\0'; + cb = sizeof( MessageBuffer ) - cb; + + _vsnprintf( s, cb, Format, arglist ); + +#if DBG + DebugLog((DEB_TRACE_SETUP, "%s\n", s )); +#endif + + if (MessageBoxA( NULL, s, MessageBuffer, MB_OKCANCEL | MB_SETFOREGROUND )) { + return TRUE; + } + else { + return FALSE; + } +} + +BOOL +GetLine( void ) +{ + char *s, *s1; + + while (TRUE) { + s = fgets( LineBuffer, sizeof( LineBuffer ), fh ); + if (s == NULL) { + return FALSE; + } + + LineNumber++; + if (s1 = strchr( s, '\r' )) { + *s1 = '\0'; + } + else + if (s1 = strchr( s, '\n' )) { + *s1 = '\0'; + } + else { + s1 = strchr( s, '\0' ); + } + while (s1 > s && *--s1 <= ' ') { + *s1 = '\0'; + } + + while (*s && (*s <= ' ')) { + s++; + } + + // + // If not a blank line or a comment line, then return to caller. + // + + if (*s && *s != ';' && strncmp( s, "//", 2 )) { + LineIndent = s - LineBuffer; + strcpy( LineBuffer, s ); +// DebugLog((DEB_TRACE_SETUP, " (%u)'%s'\n", LineIndent, LineBuffer )); + return TRUE; + } + } +} + + +typedef BOOL (*PREG_INI_ROUTINE)( + struct _REG_INI_TABLE *TableEntry + ); + +#define REG_INI_NONE 0 +#define REG_INI_VALUE 1 +#define REG_INI_MULTI_VALUE 2 +#define REG_INI_NAME_EQ_VALUE 3 +#define REG_INI_NAME_BOOLEAN 4 +#define REG_INI_DWORD 5 +#define REG_INI_NAME_EQ_MULTI 6 + +#define REG_INI_FLAG_SKIP_FOR_NETSETUP (USHORT)0x0001 +#define REG_INI_FLAG_IGNORE_NOT_FOUND (USHORT)0x0002 + +typedef struct _REG_INI_TABLE { + char *Name; + USHORT ValueType; + USHORT Flags; + PREG_INI_ROUTINE Routine; + char *RegistryPath; + char *RegistryValueName; +} REG_INI_TABLE, *PREG_INI_TABLE; + +BOOL +ProcessMachineType( + PREG_INI_TABLE TableEntry + ); + +BOOL +ProcessDisabledDrivers( + PREG_INI_TABLE TableEntry + ); + +BOOL +ProcessEnabledDrivers( + PREG_INI_TABLE TableEntry + ); + +BOOL +ProcessProgramGroups( + PREG_INI_TABLE TableEntry, + BOOL PersonalGroups + ); + +BOOL +ProcessPersonalProgramGroups( + PREG_INI_TABLE TableEntry + ); + +BOOL +ProcessCommonProgramGroups( + PREG_INI_TABLE TableEntry + ); + +BOOL +SaveUserName( + PREG_INI_TABLE TableEntry + ); + +BOOL +SaveMachineName( + PREG_INI_TABLE TableEntry + ); + +BOOL +SavePassword( + PREG_INI_TABLE TableEntry + ); + +BOOL +SaveTimeZone( + PREG_INI_TABLE TableEntry + ); + +BOOL +SaveDomainName( + PREG_INI_TABLE TableEntry + ); + +BOOL +SaveProductType( + PREG_INI_TABLE TableEntry + ); + +BOOL +SaveScriptCommands( + PREG_INI_TABLE TableEntry + ); + +REG_INI_TABLE RegistryIniTable[] = { + {"MachineType", REG_INI_NONE, 0, ProcessMachineType, + NULL, NULL + }, + + {"DisabledDrivers", REG_INI_NONE, 0, ProcessDisabledDrivers, + NULL, NULL + }, + + {"EnabledDrivers", REG_INI_NONE, 0, ProcessEnabledDrivers, + NULL, NULL + }, + + {"ProductType", REG_INI_VALUE, 0, SaveProductType, + "SYS:Control\\ProductOptions", "ProductType" + }, + + {"Domain", REG_INI_VALUE, REG_INI_FLAG_SKIP_FOR_NETSETUP, SaveDomainName, + "SYS:Services\\LanmanWorkstation\\Parameters", "Domain" + }, + + {"DomainId", REG_INI_VALUE, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\LanmanWorkstation\\Parameters", "DomainId" + }, + + {"AccountDomainId", REG_INI_VALUE, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\LanmanWorkstation\\Parameters", "AccountDomainId" + }, + + {"MachineName", REG_INI_VALUE, REG_INI_FLAG_SKIP_FOR_NETSETUP, SaveMachineName, + "SYS:Control\\ComputerName\\ComputerName", "ComputerName" + }, + + {"Password", REG_INI_VALUE, REG_INI_FLAG_SKIP_FOR_NETSETUP, SavePassword, + NULL, NULL + }, + + {"TimeZone", REG_INI_VALUE, 0, SaveTimeZone, + NULL, NULL + }, + + {"InsertScript", REG_INI_NONE, 0, SaveScriptCommands, + NULL, NULL + }, + + {"DosDevices", REG_INI_NAME_EQ_VALUE, 0, NULL, + "SYS:Control\\Session Manager\\DOS Devices", NULL + }, + + {"Serial0", REG_INI_NAME_EQ_VALUE, 0, NULL, + "SYS:Services\\Serial\\Parameters\\Serial0", NULL + }, + + {"Serial1", REG_INI_NAME_EQ_VALUE, 0, NULL, + "SYS:Services\\Serial\\Parameters\\Serial1", NULL + }, + + {"Serial2", REG_INI_NAME_EQ_VALUE, 0, NULL, + "SYS:Services\\Serial\\Parameters\\Serial2", NULL + }, + + {"Serial3", REG_INI_NAME_EQ_VALUE, 0, NULL, + "SYS:Services\\Serial\\Parameters\\Serial3", NULL + }, + + {"Serial4", REG_INI_NAME_EQ_VALUE, 0, NULL, + "SYS:Services\\Serial\\Parameters\\Serial4", NULL + }, + + {"Serial5", REG_INI_NAME_EQ_VALUE, 0, NULL, + "SYS:Services\\Serial\\Parameters\\Serial5", NULL + }, + + {"Serial6", REG_INI_NAME_EQ_VALUE, 0, NULL, + "SYS:Services\\Serial\\Parameters\\Serial6", NULL + }, + + {"Serial7", REG_INI_NAME_EQ_VALUE, 0, NULL, + "SYS:Services\\Serial\\Parameters\\Serial7", NULL + }, + + {"Serial8", REG_INI_NAME_EQ_VALUE, 0, NULL, + "SYS:Services\\Serial\\Parameters\\Serial8", NULL + }, + + {"Serial9", REG_INI_NAME_EQ_VALUE, 0, NULL, + "SYS:Services\\Serial\\Parameters\\Serial9", NULL + }, + + {"UserName", REG_INI_VALUE, 0, SaveUserName, + "SYS:Services", "CurrentUser" + }, + + {"DisablePasswordChange", REG_INI_DWORD, 0, NULL, + "SYS:Services\\NetLogon\\Parameters", "DisablePasswordChange" + }, + + {"GlobalFlags", REG_INI_DWORD, 0, NULL, + "SYS:Control\\Session Manager", "GlobalFlag" + }, + + {"ProtectionMode", REG_INI_DWORD, 0, NULL, + "SYS:Control\\Session Manager", "ProtectionMode" + }, + + {"CriticalSectionTimeout", REG_INI_DWORD, 0, NULL, + "SYS:Control\\Session Manager", "CriticalSectionTimeout" + }, + + {"ResourceTimeout", REG_INI_DWORD, 0, NULL, + "SYS:Control\\Session Manager", "ResourceTimeout" + }, + + {"WOW", REG_INI_NAME_EQ_VALUE, 0, NULL, + "SYS:Control\\WOW", NULL + }, + + {"PagingFiles", REG_INI_MULTI_VALUE, 0, NULL, + "SYS:Control\\Session Manager\\Memory Management", "PagingFiles" + }, + + {"InitialCommand", REG_INI_MULTI_VALUE, 0, NULL, + "SYS:Control\\Session Manager", "Execute" + }, + + {"SubSystems", REG_INI_MULTI_VALUE, 0, NULL, + "SYS:Control\\Session Manager\\SubSystems", "Optional" + }, + + {"ProgramGroups", REG_INI_NONE, 0, ProcessPersonalProgramGroups, + "USR:UNICODE Program Groups", NULL + }, + + {"CommonProgramGroups", REG_INI_NONE, 0, ProcessCommonProgramGroups, + "\\Registry\\Machine\\Software\\Program Groups", NULL + }, + + {"ProgramManager", REG_INI_NAME_BOOLEAN, 0, NULL, + "USR:Software\\Microsoft\\Windows NT\\CurrentVersion\\Program Manager\\Settings", NULL + }, + + {"Environment", REG_INI_NAME_EQ_VALUE, 0, NULL, + "USR:Environment", NULL + }, + + {"Server", REG_INI_NAME_EQ_VALUE, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\LanmanServer\\Parameters", NULL + }, + + {"ServerShares", REG_INI_NAME_EQ_MULTI, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\LanmanServer\\Shares", NULL + }, + + {"ServerLinkage", REG_INI_NAME_EQ_MULTI, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\LanmanServer\\Linkage", NULL + }, + + {"Nbf", REG_INI_NAME_EQ_VALUE, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\Nbf\\Parameters", NULL + }, + + {"NbfLinkage", REG_INI_NAME_EQ_MULTI, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\Nbf\\Linkage", NULL + }, + + {"NetBios", REG_INI_NAME_EQ_MULTI, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\NetBiosInformation\\Parameters", NULL + }, + + {"NetBiosLinkage", REG_INI_NAME_EQ_MULTI, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\NetBios\\Linkage", NULL + }, + + {"Elnkii01", REG_INI_NAME_EQ_VALUE, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\Elnkii01\\Parameters", NULL + }, + + {"ElnkMc01", REG_INI_NAME_EQ_VALUE, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\ElnkMc01\\Parameters", NULL + }, + + {"UBAdapter", REG_INI_NAME_EQ_VALUE, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\Xns\\Parameters", NULL + }, + + {"Lance01", REG_INI_NAME_EQ_VALUE, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\Lance01\\Parameters", NULL + }, + + {"NE320001", REG_INI_NAME_EQ_VALUE, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\NE320001\\Parameters", NULL + }, + + {"WorkstationLinkage", REG_INI_NAME_EQ_MULTI, REG_INI_FLAG_SKIP_FOR_NETSETUP, NULL, + "SYS:Services\\LanmanWorkstation\\Linkage", NULL + }, + + {"Shell", REG_INI_VALUE, 0, NULL, + "\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultShell" + }, + + {NULL, REG_INI_NAME_EQ_VALUE, 0, NULL, + NULL, NULL + } +}; + + +BOOL +ProcessNone( + PREG_INI_TABLE TableEntry + ); + +BOOL +ProcessValue( + PREG_INI_TABLE TableEntry + ); + +BOOL +ProcessMultiValue( + PREG_INI_TABLE TableEntry + ); + +BOOL +ProcessNameEqValue( + PREG_INI_TABLE TableEntry + ); + +BOOL +ProcessNameBoolean( + PREG_INI_TABLE TableEntry + ); + +BOOL +ProcessDWord( + PREG_INI_TABLE TableEntry + ); + +BOOL +ProcessNameEqMulti( + PREG_INI_TABLE TableEntry + ); + +PREG_INI_ROUTINE RegistryIniRoutines[] = { + ProcessNone, // REG_INI_NONE + ProcessValue, // REG_INI_VALUE + ProcessMultiValue, // REG_INI_MULTI_VALUE + ProcessNameEqValue, // REG_INI_NAME_EQ_VALUE + ProcessNameBoolean, // REG_INI_NAME_BOOLEAN + ProcessDWord, // REG_INI_DWORD + ProcessNameEqMulti // REG_INI_NAME_EQ_MULTI +}; + + +BOOL +OpenRegistryKey( + PREG_INI_TABLE TableEntry, + PHANDLE Handle + ); + +char CurrentIniFileName[ 256 ] = "win.ini"; + +PREG_INI_TABLE +FindTableEntry( + char *Name + ) +{ + PREG_INI_TABLE TableEntry; + + TableEntry = RegistryIniTable; + while (TableEntry->Name) { + if (!_stricmp( TableEntry->Name, Name )) { + break; + } + else { + TableEntry++; + } + } + + return TableEntry; +} + +BOOL +WriteWinIni( + char *Section, + char *Key, + char *Value, + BOOL AppendToValue + ) +{ + DWORD cbValue; + char *NewValue; + char *FileName; + BOOL Result; + + FileName = CurrentIniFileName; + if (!_strnicmp( Value, "REG_DWORD ", 10 )) { + Value += 10; + } + + if (AppendToValue) { + cbValue = 1024; + NewValue = (char *)LocalAlloc( LMEM_ZEROINIT, cbValue + strlen( Value ) + 1 ); + if (NewValue) { + cbValue = GetPrivateProfileStringA( Section, Key, NULL, NewValue, cbValue, FileName ); + if (cbValue) { + strcat( NewValue, Value ); + } + } + } + else { + NewValue = Value; + } + + Result = WritePrivateProfileStringA( Section, Key, NewValue, FileName ); + if (!Result) { + DeclareError( "WritePrivateProfileString( %s, %s, %s, %s ) - failed (rc == %u)\n", + Section, + Key, + NewValue, + FileName ? FileName : "win.ini", + GetLastError() + ); + + } + + if (NewValue != Value) { + LocalFree( NewValue ); + } + + return Result; +} + + +BOOL +OpenRegistryKey( + PREG_INI_TABLE TableEntry, + PHANDLE Handle + ) +{ + char *Path, FullPath[ 2 * MAX_PATH ]; + ANSI_STRING AnsiPath; + UNICODE_STRING UnicodePath; + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + + Path = TableEntry->RegistryPath; + *Handle = NULL; + if (Path == NULL) { + return TRUE; + } + else + if (NetSetupGoingToRun && (TableEntry->Flags & REG_INI_FLAG_SKIP_FOR_NETSETUP)) { + return TRUE; + } + + if (!_strnicmp( Path, "USR:", 4 )) { + strcpy( FullPath, "\\Registry\\User\\.Default\\" ); + Path += 4; + } + else + if (!_strnicmp( Path, "SYS:", 4 )) { + strcpy( FullPath, "\\Registry\\Machine\\System\\CurrentControlSet\\" ); + Path += 4; + } + else { + FullPath[ 0 ] = '\0'; + } + strcat( FullPath, Path ); + RtlInitAnsiString( &AnsiPath, FullPath ); + RtlAnsiStringToUnicodeString( &UnicodePath, &AnsiPath, TRUE ); + InitializeObjectAttributes( &ObjectAttributes, + &UnicodePath, + OBJ_CASE_INSENSITIVE | OBJ_OPENIF, + NULL, + NULL + ); + Status = NtOpenKey( Handle, + MAXIMUM_ALLOWED, + &ObjectAttributes + ); + + RtlFreeUnicodeString( &UnicodePath ); + + if (NT_SUCCESS( Status )) { + return TRUE; + } + else { + if (!(TableEntry->Flags & REG_INI_FLAG_IGNORE_NOT_FOUND)) { + DeclareError( "NtOpenKey( %s ) failed (Status == %lx)\n", + FullPath, + Status + ); + } + + return FALSE; + } +} + +BOOL +WriteRegistryKey( + HANDLE KeyHandle, + char *ValueName, + DWORD ValueType, + char *ValueData, + DWORD ValueLength + ) +{ + NTSTATUS Status; + ANSI_STRING AnsiString; + UNICODE_STRING UnicodeKeyName; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE KeyHandle1; + + RtlInitAnsiString( &AnsiString, ValueName ); + RtlAnsiStringToUnicodeString( &UnicodeKeyName, &AnsiString, TRUE ); + InitializeObjectAttributes( &ObjectAttributes, + &UnicodeKeyName, + OBJ_CASE_INSENSITIVE | OBJ_OPENIF, + KeyHandle, + NULL + ); + Status = NtCreateKey( &KeyHandle1, + MAXIMUM_ALLOWED, + &ObjectAttributes, + 0, + NULL, + 0, + NULL + ); + RtlFreeUnicodeString( &UnicodeKeyName ); + + if (NT_SUCCESS( Status )) { + Status = WriteRegistry( KeyHandle1, + NULL, + ValueType, + ValueData, + ValueLength + ); + (void) NtClose( KeyHandle1 ); + + return( Status ); + + } + else { + DeclareError( "NtCreateKey( %s ) failed (Status == %lx)\n", + ValueName, + Status + ); + return FALSE; + } +} + +BOOL +ProcessNone( + PREG_INI_TABLE TableEntry + ) +{ + TableEntry; + + return TRUE; +} + +BOOL +ProcessValue( + PREG_INI_TABLE TableEntry + ) +{ + HANDLE Handle; + BOOL Result; + + if (!GetLine()) { + return FALSE; + } + + if (!OpenRegistryKey( TableEntry, &Handle )) { + return FALSE; + } + + Result = TRUE; + if (Handle) { + Result &= WriteRegistry( Handle, + TableEntry->RegistryValueName, + REG_SZ, + LineBuffer, + 0 + ); + NtClose( Handle ); + } + + return Result; +} + + +BOOL +ProcessMultiValue( + PREG_INI_TABLE TableEntry + ) +{ + HANDLE Handle; + BOOL Result; + DWORD ValueLength, cb; + char *ValueData; + + if (!OpenRegistryKey( TableEntry, &Handle )) { + return FALSE; + } + + Result = TRUE; + ValueLength = 0; + ValueData = (char *)LocalAlloc( LMEM_ZEROINIT, ValueLength ); + if (!ValueData) + { + NtClose( Handle ); + return( FALSE ); + } + while (GetLine()) { + if (!LineIndent) { + break; + } + + if (Handle) { + cb = strlen( LineBuffer ) + 1; + ValueData = (char *)LocalReAlloc( ValueData, ValueLength + cb, LMEM_ZEROINIT | LMEM_MOVEABLE ); + if (!ValueData) + { + NtClose( Handle ); + return( FALSE ); + + } + strcpy( ValueData + ValueLength, LineBuffer ); + ValueLength += cb; + } + + } + + if (Handle) { + cb = sizeof( '\0' ); + ValueData = (char *)LocalReAlloc( ValueData, ValueLength + cb, LMEM_ZEROINIT | LMEM_MOVEABLE ); + if (!ValueData) + { + NtClose( Handle ); + return( FALSE ); + } + ValueLength += cb; + Result &= WriteRegistry( Handle, + TableEntry->RegistryValueName, + REG_MULTI_SZ, + ValueData, + ValueLength + ); + NtClose( Handle ); + } + + LocalFree( ValueData ); + + return Result; +} + + +BOOL +ProcessNameEqValue( + PREG_INI_TABLE TableEntry + ) +{ + char MessageBuffer[ 128 ]; + char SectionNameBuffer[ MAX_PATH ]; + char *SectionName; + char *Equal, *Value; + HANDLE Handle; + BOOL AppendToValue; + BOOL Result; + + if (!_strnicmp( LineBuffer, "-f ", 3)) { + Value = LineBuffer + 3; + while (*Value == ' ') { + Value++; + } + + strcpy( CurrentIniFileName, Value ); + return TRUE; + } + + if (!OpenRegistryKey( TableEntry, &Handle )) { + return FALSE; + } + + if (TableEntry->Name == NULL) { + SectionName = SectionNameBuffer; + strcpy( SectionName, LineBuffer ); + } + else { + SectionName = NULL; + } + strcpy( MessageBuffer, LineBuffer ); + + Result = TRUE; + while (GetLine()) { + if (!LineIndent) { + break; + } + + Equal = strchr( LineBuffer, '=' ); + if (Equal == NULL) { + strcat( MessageBuffer, " - Expecting NAME=VALUE" ); + DeclareError( MessageBuffer ); + Result = FALSE; + break; + } + *Equal = '\0'; + Value = Equal + 1; + if (Equal[ -1 ] == '+') { + AppendToValue = TRUE; + *--Equal = '\0'; + } + else { + AppendToValue = FALSE; + } + + while (Equal > LineBuffer && *--Equal <= ' ') { + *Equal = '\0'; + } + while (*Value && *Value <= ' ') { + Value++; + } + + if (Handle) { + Result &= WriteRegistry( Handle, + LineBuffer, + REG_SZ, + Value, + 0 + ); + } + + if (SectionName) { + Result &= WriteWinIni( SectionName, + LineBuffer, + Value, + AppendToValue + ); + } + } + + if (Handle) { + NtClose( Handle ); + } + + return Result; +} + +BOOL +ProcessNameBoolean( + PREG_INI_TABLE TableEntry + ) +{ + HANDLE Handle; + BOOL Result; + + if (!OpenRegistryKey( TableEntry, &Handle )) { + return FALSE; + } + + Result = TRUE; + while (GetLine()) { + if (!LineIndent) { + break; + } + + if (Handle) { + Result &= WriteRegistry( Handle, + LineBuffer, + REG_DWORD, + "1", + 0 + ); + } + } + + if (Handle) { + NtClose( Handle ); + } + + return Result; +} + +BOOL +ProcessDWord( + PREG_INI_TABLE TableEntry + ) +{ + HANDLE Handle; + BOOL Result; + + if (!GetLine()) { + return FALSE; + } + + Result = TRUE; + if (!OpenRegistryKey( TableEntry, &Handle )) { + Result = FALSE; + } + else + if (Handle != NULL) { + Result &= WriteRegistry( Handle, + TableEntry->RegistryValueName, + REG_DWORD, + LineBuffer, + 0 + ); + NtClose( Handle ); + } + + return Result; +} + +BOOL +ProcessNameEqMulti( + PREG_INI_TABLE TableEntry + ) +{ + char *Equal, *Value; + char *Src, *Dst; + HANDLE Handle; + BOOL Result; + + if (!TableEntry->RegistryPath) { + DeclareError( "RegistryPath must be specified for NAME_EQ_MULTI" ); + return FALSE; + } + if (TableEntry->RegistryValueName) { + DeclareError( "RegistryValueName must be NULL for NAME_EQ_MULTI" ); + return FALSE; + } + + if (!OpenRegistryKey( TableEntry, &Handle )) { + return FALSE; + } + + Result = TRUE; + while (GetLine()) { + if (!LineIndent) { + break; + } + + Equal = strchr( LineBuffer, '=' ); + if (Equal == NULL) { + DeclareError( "Expecting NAME=MULTI_SZ" ); + Result = FALSE; + break; + } + *Equal = '\0'; + Value = Equal + 1; + if (Equal[ -1 ] == '+') { + DeclareError( "+= not allowed for MULTI_SZ values" ); + Result = FALSE; + break; + } + + while (Equal > LineBuffer && *--Equal <= ' ') { + *Equal = '\0'; + } + while (*Value && *Value <= ' ') { + Value++; + } + + // + // Strip " from strings and put NULs in between. + // + + Src = Dst = Value; + while (*Src) { + if (*Src++ != '"') { + DeclareError( "Expected '\"' to start MULTI_SZ string" ); + Result = FALSE; + break; + } + while (*Src && (*Src != '"' || Src[1] == '"')) { + if ((*Dst++ = *Src++) == '"') { + Src++; + } + } + if (*Src++ != '"') { + DeclareError( "Missing '\"' at end of MULTI_SZ string" ); + Result = FALSE; + break; + } + *Dst++ = '\0'; + while (*Src && *Src <= ' ') { + Src++; + } + } + *Dst = '\0'; + + if (!Result) { + break; + } + + if (Handle) { + Result &= WriteRegistry( Handle, + LineBuffer, + REG_MULTI_SZ, + Value, + 0 + ); + } + } + + if (Handle) { + NtClose( Handle ); + } + + return Result; +} + + +BOOL +ProcessPersonalProgramGroups( + PREG_INI_TABLE TableEntry + ) +{ + return ProcessProgramGroups( TableEntry, TRUE ); +} + + +BOOL +ProcessCommonProgramGroups( + PREG_INI_TABLE TableEntry + ) +{ + return ProcessProgramGroups( TableEntry, FALSE ); +} + +BOOL +ProcessProgramGroups( + PREG_INI_TABLE TableEntry, + BOOL PersonalGroups + ) +{ + HANDLE ProgramGroupsHandle; + HANDLE GroupsHandle; + HANDLE SettingsHandle; + REG_INI_TABLE TempEntry; + char *Equal, *Value, *ExpandedValue; + BOOL Result; + int GroupNumber; + int GroupFileHandle; + char GroupNumberKey[ MAX_PATH ]; + char GroupOrderList[ MAX_PATH ] = " "; + char *GroupFileData; + struct _finddata_t GroupFileInfo; + long FindHandle; + DWORD cb; + + OpenRegistryKey( TableEntry, &ProgramGroupsHandle ); + if (ProgramGroupsHandle == NULL) { + return FALSE; + } + + if (PersonalGroups) { + TempEntry.Flags = 0; + TempEntry.RegistryPath = "USR:Software\\Microsoft\\Windows NT\\CurrentVersion\\Program Manager\\UNICODE Groups"; + OpenRegistryKey( &TempEntry, &GroupsHandle ); + if (GroupsHandle == NULL) { + NtClose( ProgramGroupsHandle ); + return FALSE; + } + + TempEntry.RegistryPath = "USR:Software\\Microsoft\\Windows NT\\CurrentVersion\\Program Manager\\Settings"; + OpenRegistryKey( &TempEntry, &SettingsHandle ); + if (SettingsHandle == NULL) { + NtClose( GroupsHandle ); + NtClose( ProgramGroupsHandle ); + return FALSE; + } + } + else { + GroupsHandle = NULL; + SettingsHandle = NULL; + } + + Result = TRUE; + GroupNumber = 0; + while (GetLine()) { + if (!LineIndent) { + break; + } + + Equal = strchr( LineBuffer, '=' ); + if (Equal == NULL) { + DeclareError( "Expecting NAME=VALUE" ); + Result = FALSE; + break; + } + *Equal = '\0'; + Value = Equal + 1; + while (Equal > LineBuffer && *--Equal <= ' ') { + *Equal = '\0'; + } + while (*Value && *Value <= ' ') { + Value++; + } + + if (strchr( Value, '%' )) { + cb = 4 * strlen( Value ); + ExpandedValue = (char *)LocalAlloc( 0, cb ); + ExpandEnvironmentStringsA( Value, ExpandedValue, cb ); + Value = ExpandedValue; + } + else { + ExpandedValue = NULL; + } + + cb = 0; + FindHandle = _findfirst( Value, &GroupFileInfo ); + if (FindHandle != -1) { + GroupFileData = (char *)LocalAlloc( 0, GroupFileInfo.size ); + _findclose( FindHandle ); + GroupFileHandle = _open( Value, _O_BINARY | _O_RDONLY, _A_NORMAL ); + if (GroupFileHandle != -1) { + cb = _read( GroupFileHandle, GroupFileData, GroupFileInfo.size ); + if (cb != GroupFileInfo.size) { + cb = 0; + } + _close( GroupFileHandle ); + } + } + + if (ExpandedValue) { + LocalFree( ExpandedValue ); + } + + if (cb == 0) { + DeclareError( "Unable to open or access group file - %s", Value ); + Result = FALSE; + } + else { + if (PersonalGroups) { + GroupNumber += 1; + sprintf( GroupNumberKey, "Group%u", GroupNumber ); + Result &= WriteRegistry( GroupsHandle, + GroupNumberKey, + REG_SZ, + LineBuffer, + 0 + ); + } + + Result &= WriteRegistryKey( ProgramGroupsHandle, + LineBuffer, + REG_BINARY, + GroupFileData, + cb + ); + + + LocalFree( GroupFileData ); + GroupFileData = NULL; + } + } + + if (Result && PersonalGroups) { + char *s; + int i; + + s = GroupOrderList; + for (i=0; i<GroupNumber; i++) { + if (i) { + *s++ = ' ' ; + } + + s += sprintf( s, "%d", i+1 ); + } + + Result &= WriteRegistry( SettingsHandle, + "UNICODE Order", + REG_SZ, + GroupOrderList, + 0 + ); + + + Result &= WriteRegistry( SettingsHandle, + "Startup", + REG_SZ, + "Startup", + 0 + ); + } + + if (PersonalGroups) { + NtClose( SettingsHandle ); + NtClose( GroupsHandle ); + } + + NtClose( ProgramGroupsHandle ); + return Result; +} + + +BOOL +EnableDisableDriver( + char *ModuleName, + BOOL EnableLoad + ); + +BOOL +EnableDisableDriver( + char *ModuleName, + BOOL EnableLoad + ) +{ + BOOL Result; + char RegistryPath[ MAX_PATH ]; + HANDLE KeyHandle; + char StartValue[ 16 ]; + REG_INI_TABLE TempEntry; + + + // + // Never ever disable VgaStart and VgaSave drivers. + // + + if ( (_strnicmp(ModuleName, "Vga", 3) == 0) && + !EnableLoad) { + return FALSE; + } + + _snprintf( RegistryPath, sizeof( RegistryPath ), "SYS:Services\\%s", ModuleName ); + TempEntry.RegistryPath = RegistryPath; + TempEntry.Flags = REG_INI_FLAG_IGNORE_NOT_FOUND; + OpenRegistryKey( &TempEntry, &KeyHandle ); + if (KeyHandle != NULL) { + _snprintf( StartValue, sizeof( StartValue ), "%u", + EnableLoad ? SERVICE_SYSTEM_START : SERVICE_DISABLED + ); + Result = WriteRegistry( KeyHandle, + "Start", + REG_DWORD, + StartValue, + 0 + ); + if (Result && EnableLoad) { + _snprintf( StartValue, sizeof( StartValue ), "%u", SERVICE_ERROR_IGNORE ); + Result = WriteRegistry( KeyHandle, + "ErrorControl", + REG_DWORD, + StartValue, + 0 + ); + } + + NtClose( KeyHandle ); + Result = TRUE; + } + else { + Result = FALSE; + } + + return Result; +} + + +BOOL +ProcessREGINI( + PREG_INI_TABLE TableEntry + ) +{ + TableEntry; + + while (GetLine()) { + if (!LineIndent) { + break; + } + + } + + return TRUE; +} + +BOOL +ProcessMachineType( + PREG_INI_TABLE TableEntry + ) +{ + TableEntry; + + if (GetLine()) { + } + + return TRUE; +} + +BOOL +ProcessDisabledDrivers( + PREG_INI_TABLE TableEntry + ) +{ + TableEntry; + + while (GetLine()) { + if (!LineIndent) { + break; + } + + if (EnableDisableDriver( LineBuffer, FALSE )) { + DebugLog((DEB_TRACE_SETUP, " Disabled load of %s driver.\n", LineBuffer )); + } + } + + return TRUE; +} + +BOOL +ProcessEnabledDrivers( + PREG_INI_TABLE TableEntry + ) +{ + TableEntry; + + while (GetLine()) { + if (!LineIndent) { + break; + } + + if (EnableDisableDriver( LineBuffer, TRUE )) { + DebugLog((DEB_TRACE_SETUP, " Enabled load of %s driver.\n", LineBuffer )); + } + } + + return TRUE; +} + +BOOL +DoFile( void ) +{ + PREG_INI_TABLE TableEntry; + BOOL Result; + + fh = fopen( InputFileName, "rb" ); + if (fh == NULL) { + DeclareError( "Unable to open input file (rc == %u)\n", GetLastError() ); + return FALSE; + } + + Result = TRUE; + while (GetLine()) { + while (!LineIndent) { + TableEntry = FindTableEntry( LineBuffer ); + LineIndent = 1; + if ((RegistryIniRoutines[ TableEntry->ValueType ])( TableEntry )) { + if (TableEntry->Routine) { + Result &= (TableEntry->Routine)( TableEntry ); + } + } + else { + Result = FALSE; + } + } + } + + return Result; +} + +#if 0 +BOOL +SaveDefaultProfile( void ) +{ + char DefaultPath[ 2 * MAX_PATH ]; + char ProfileKey[ 2 * MAX_PATH ]; + ANSI_STRING AnsiPath; + UNICODE_STRING UnicodePath; + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE KeyHandle; + HANDLE FileHandle; + IO_STATUS_BLOCK IoStatusBlock; + RTL_RELATIVE_NAME RelativeName; + UNICODE_STRING FileName; + PVOID FreeBuffer; + BOOLEAN ErrorFlag; + + strcpy( ProfileKey, "\\Registry\\User\\The_User" ); + RtlInitAnsiString( &AnsiPath, ProfileKey ); + RtlAnsiStringToUnicodeString( &UnicodePath, &AnsiPath, TRUE ); + InitializeObjectAttributes( &ObjectAttributes, + &UnicodePath, + OBJ_CASE_INSENSITIVE | OBJ_OPENIF, + NULL, + NULL + ); + Status = NtOpenKey( &KeyHandle, + MAXIMUM_ALLOWED, + &ObjectAttributes + ); + + RtlFreeUnicodeString( &UnicodePath ); + + if (!NT_SUCCESS( Status )) { + DebugLog((DEB_ERROR, " Could not open key 'THE_USER' Status = 0x%lx\n\r", Status)); + return FALSE; + } + + ExpandEnvironmentStringsA("%systemRoot%\\system32\\config\\DEFAULT", DefaultPath, sizeof(DefaultPath)); + RtlInitAnsiString( &AnsiPath, DefaultPath ); + RtlAnsiStringToUnicodeString( &UnicodePath, &AnsiPath, TRUE ); + + // + // Convert the DOS path name to a canonical Nt path name. + // + + ErrorFlag = RtlDosPathNameToNtPathName_U( + UnicodePath.Buffer, + &FileName, + NULL, + &RelativeName + ); + + RtlFreeUnicodeString( &UnicodePath ); + + // + // If the name was not succesfully converted assume it was invalid. + // + if( ! ErrorFlag ) { + DebugLog((DEB_ERROR, " Could not create default profile - RtlDosPathNameToNtPathName_U FAILED error = %d\n\r", ErrorFlag)); + return FALSE; + } + + // + // Remember the buffer allocatted by RtlDosPathNameToNtPathName_U. + // + FreeBuffer = FileName.Buffer; + + // + // If a relative name and directory handle will work, use those. + // + if( RelativeName.RelativeName.Length ) { + + // + // Replace the full path with the relative path. + // + FileName = *(PUNICODE_STRING)&RelativeName.RelativeName; + + } else { + + // + // Using the full path - no containing directory. + // + RelativeName.ContainingDirectory = NULL; + } + + // + // Initialize the Obja structure for the save file. + // + InitializeObjectAttributes( + &ObjectAttributes, + &FileName, + OBJ_CASE_INSENSITIVE, + RelativeName.ContainingDirectory, + NULL + ); + + // + // Create the file - fail if the file exists. + // + Status = NtCreateFile( + &FileHandle, + GENERIC_WRITE | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_CREATE, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0 + ); + + // + // Free the buffer allocatted by RtlDosPathNameToNtPathName_U. + // + RtlFreeHeap( RtlProcessHeap(), 0, FreeBuffer ); + + // + // Check the results of the NtCreateFile. + // + if( ! NT_SUCCESS( Status )) { + DebugLog((DEB_ERROR, " Could not create default profile - Status == 0x%lx\n\r", Status)); + return FALSE; + } + + EnablePrivilege(SE_BACKUP_PRIVILEGE, TRUE); + EnablePrivilege(SE_RESTORE_PRIVILEGE, TRUE); + + Status = NtSaveKey( KeyHandle, FileHandle ); + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, " Could not save default profile - Status == 0x%lx\n\r", Status)); + } + else { + DebugLog((DEB_ERROR, " Created the default profile DEFAULT\n\r")); + } + + EnablePrivilege(SE_BACKUP_PRIVILEGE, FALSE); + EnablePrivilege(SE_RESTORE_PRIVILEGE, FALSE); + + // + // Close the file. + // + NtClose( FileHandle ); + return(NT_SUCCESS(Status)); +} +#endif + +char SavedUserName[ MAX_COMPUTERNAME_LENGTH ]; +char SavedMachineName[ MAX_COMPUTERNAME_LENGTH ]; +char SavedDomainName[ MAX_COMPUTERNAME_LENGTH ]; +char SavedPassword[ 64 ]; +char SavedTimeZone[ 64 ]; +NT_PRODUCT_TYPE SavedProductType; +char *CommandsToInsertAtBegOfScript; +char *CommandsToInsertAtEndOfScript; + +BOOL +SaveUserName( + PREG_INI_TABLE TableEntry + ) +{ + TableEntry; + + strcpy( SavedUserName, LineBuffer ); + _strlwr( SavedUserName ); + return TRUE; +} + +BOOL +SaveMachineName( + PREG_INI_TABLE TableEntry + ) +{ + TableEntry; + + strcpy( SavedMachineName, LineBuffer ); + _strupr( SavedMachineName ); + return TRUE; +} + +BOOL +SavePassword( + PREG_INI_TABLE TableEntry + ) +{ + TableEntry; + + strcpy( SavedPassword, LineBuffer ); + return TRUE; +} + + +BOOL +SaveTimeZone( + PREG_INI_TABLE TableEntry + ) +{ + TableEntry; + + strcpy( SavedTimeZone, LineBuffer ); + return TRUE; +} + +BOOL +SaveDomainName( + PREG_INI_TABLE TableEntry + ) +{ + TableEntry; + + strcpy( SavedDomainName, LineBuffer ); + _strupr( SavedDomainName ); + return TRUE; +} + +BOOL +SaveProductType( + PREG_INI_TABLE TableEntry + ) +{ + TableEntry; + + if (_strcmpi(LineBuffer, "LanmanNt") == 0) { + SavedProductType = NtProductLanManNt; + } else if (_strcmpi(LineBuffer, "ServerNt") == 0) { + SavedProductType = NtProductServer; + } else { + SavedProductType = NtProductWinNt; + } + + return TRUE; +} + +BOOL +SaveScriptCommands( + PREG_INI_TABLE TableEntry + ) +{ + char *s, **pp, *Src; + DWORD cb; + + while (GetLine()) { + if (!LineIndent) { + break; + } + + if (!_stricmp( LineBuffer, "KeepScript" )) { + KeepScript = TRUE; + } + else + if (!_stricmp( LineBuffer, "ExtendedNetSetup" )) { + ExtendedNetSetup = TRUE; + if (NetSetupGoingToRun) { + SetSetupType( SETUPTYPE_NETSRW ); + } + } + else + if (!_stricmp( LineBuffer, "RunNetDetect" )) { + if (NetSetupGoingToRun) { + RunNetDetect = TRUE; + } + } + else { + Src = LineBuffer; + if (*Src == '!') { + pp = &CommandsToInsertAtEndOfScript; + Src++; + } + else { + pp = &CommandsToInsertAtBegOfScript; + } + + if (*pp != NULL) { + cb = strlen( *pp ); + } + else { + cb = 0; + } + cb += strlen( Src ) + 4; + s = RtlAllocateHeap( RtlProcessHeap(), 0, cb ); + if (s == NULL) { + return FALSE; + } + sprintf( s, "%s%s\r\n", *pp != NULL ? *pp : "", Src ); + if (*pp != NULL) { + RtlFreeHeap( RtlProcessHeap(), 0, *pp ); + } + *pp = s; + } + } + + return TRUE; +} + + +BOOL +GenerateInitialCommandScript( void ) +{ + FILE *fh; + char ScriptFileName[ MAX_PATH ]; + char SetupArguments[ 256]; + + GetEnvironmentVariableA( "SystemRoot", ScriptFileName, sizeof( ScriptFileName ) ); + strcat( ScriptFileName, "\\winlogon.cmd" ); + _unlink( ScriptFileName ); + + if (fh = fopen( ScriptFileName, "wb" )) { + fprintf( fh, "ini winlogon.Shell = \"progman.exe\"\r\n" ); + fprintf( fh, "@ech ;\r\n" ); + fprintf( fh, "@ech ;\r\n" ); + fprintf( fh, "@ech This command will finish the initialization of your accounts database ;\r\n" ); + fprintf( fh, "@ech and will not execute again the next time you boot. ;\r\n" ); + fprintf( fh, "@ech ;\r\n" ); + fprintf( fh, "@ech ;\r\n" ); + + if (CommandsToInsertAtBegOfScript != NULL) { + fprintf( fh, "%s", CommandsToInsertAtBegOfScript ); + } + + if (!NetSetupGoingToRun) { + if ( SavedTimeZone[0] ) { + fprintf( fh, "control main.cpl /INSTALL=%s\r\n", SavedTimeZone ); + } + else { + fprintf( fh, "control main.cpl /INSTALL=Pacific\r\n"); + } + } + + fprintf( fh, "ini winlogon.DefaultUserName = %s\r\n", SavedUserName ); + if ( NetFound ) { + if (SavedPassword[ 0 ]) { + fprintf( fh, "ini winlogon.AutoAdminLogon = 1\r\n" ); + fprintf( fh, "ini winlogon.DefaultPassword = %s\r\n", SavedPassword ); + } + + if (SavedProductType != NtProductLanManNt) { + fprintf( fh, "ini winlogon.DefaultDomainName = %s\r\n", SavedDomainName ); + if (!NetSetupGoingToRun) { + fprintf( fh, "netjoin\r\n" ); + } + else { + fprintf( fh, "erase %%SystemRoot%%\\system32\\config\\userdef.*\r\n" ); + } + fprintf( fh, "net localgroup Administrators %s\\%s /add\r\n", SavedDomainName, SavedUserName ); + if (NetSetupGoingToRun) { + if (ExtendedNetSetup) { + sprintf( SetupArguments, " /t STF_COMPUTERNAME = %s", SavedMachineName ); + AppendToSetupCommandLine( SetupArguments ); + } + + if (RunNetDetect) { + sprintf( SetupArguments, " /t STF_RUNNETDETECT = %u", RunNetDetect ); + AppendToSetupCommandLine( SetupArguments ); + } + } + } + } + else { + if (SavedPassword[ 0 ]) { + fprintf( fh, "ini winlogon.AutoAdminLogon = 1\r\n" ); + fprintf( fh, "ini winlogon.DefaultPassword = %s\r\n", SavedPassword ); + fprintf( fh, "adduser %s %s\r\n", SavedUserName, SavedPassword ); + } + else { + fprintf( fh, "adduser %s localuser\r\n", SavedUserName ); + } + } + + if (!SavedPassword[ 0 ]) { + fprintf( fh, "@echo ;\r\n" ); + fprintf( fh, "@echo Ready to reboot and logon as %s\r\n", SavedUserName ); + fprintf( fh, "@echo ;\r\n" ); + fprintf( fh, "@echo Pressing any key except Ctrl-C will reboot the machine.\r\n", SavedUserName ); + fprintf( fh, "@pause\r\n" ); + } + + if (CommandsToInsertAtEndOfScript != NULL) { + fprintf( fh, "%s", CommandsToInsertAtEndOfScript ); + } + + if (!KeepScript) { + fprintf( fh, "erase %s && ", ScriptFileName ); + } + fprintf( fh, "shutdown -r -f -t 0\r\n" ); + fclose( fh ); + WriteProfileStringA( AnsiWinlogon, + "Shell", + ScriptFileName + ); + WriteProfileStringA( AnsiWinlogon, "AutoAdminLogon", "1" ); + return TRUE; + } + else { + return FALSE; + } +} + + +BOOL +DisableFailedBuiltInDrivers( + PRTL_PROCESS_MODULES *ModuleList + ) +{ + NTSTATUS Status; + RTL_PROCESS_MODULES ModuleInfoBuffer; + PRTL_PROCESS_MODULES ModuleInfo; + PRTL_PROCESS_MODULE_INFORMATION ModuleInfo1; + ULONG RequiredLength, ModuleNumber; + + ModuleInfo = &ModuleInfoBuffer; + RequiredLength = sizeof( *ModuleInfo ); + while (TRUE) { + Status = NtQuerySystemInformation( SystemModuleInformation, + ModuleInfo, + RequiredLength, + &RequiredLength + ); + if (Status == STATUS_INFO_LENGTH_MISMATCH) { + if (ModuleInfo != &ModuleInfoBuffer) { + DebugLog((DEB_TRACE_SETUP, " QueryModuleInformation returned incorrect result.\n" )); + VirtualFree( ModuleInfo, 0, MEM_RELEASE ); + return FALSE; + } + + RequiredLength += 4096; + ModuleInfo = (PRTL_PROCESS_MODULES)VirtualAlloc( NULL, + RequiredLength, + MEM_COMMIT, + PAGE_READWRITE + ); + if (ModuleInfo == NULL) { + DebugLog((DEB_TRACE_SETUP, " No memory for QueryModuleInformation (%lx).\n", RequiredLength )); + return FALSE; + } + } + else + if (!NT_SUCCESS( Status )) { + if (ModuleInfo != &ModuleInfoBuffer) { + VirtualFree( ModuleInfo, 0, MEM_RELEASE ); + } + + DebugLog((DEB_TRACE_SETUP, " QueryModuleInformation failed - %lx.\n", Status )); + return FALSE; + } + else { + break; + } + } + + ModuleInfo1 = &ModuleInfo->Modules[ 0 ]; + for (ModuleNumber=0; ModuleNumber<ModuleInfo->NumberOfModules; ModuleNumber++) { + if ((ModuleInfo1->Flags & LDRP_FAILED_BUILTIN_LOAD) && + ModuleInfo1->LoadCount <= 1 + ) { + char *ModuleName, *s; + + ModuleName = &ModuleInfo1->FullPathName[ ModuleInfo1->OffsetToFileName ]; + if (s = strchr( ModuleName, '.' )) { + *s = '\0'; + } + + if (EnableDisableDriver( ModuleName, FALSE )) { + DebugLog((DEB_TRACE_SETUP, " Disabled load of %s builtin driver.\n", ModuleName )); + } + } + + ModuleInfo1++; + } + + *ModuleList = ModuleInfo; + return TRUE; +} + +BOOL +LookupModuleName( + CHAR *DriverName, + PRTL_PROCESS_MODULES ModuleInfo + ) +{ + PRTL_PROCESS_MODULE_INFORMATION moduleInfo1; + ULONG moduleNumber; + + moduleInfo1 = &ModuleInfo->Modules[ 0 ]; + for (moduleNumber=0; moduleNumber<ModuleInfo->NumberOfModules; moduleNumber++) { + if (!(moduleInfo1->Flags & LDRP_FAILED_BUILTIN_LOAD)) { + + CHAR *moduleName, *s; + + moduleName = &moduleInfo1->FullPathName[ moduleInfo1->OffsetToFileName ]; + if (s = strchr( moduleName, '.' )) { + *s = '\0'; + } + + if (!_stricmp( moduleName, DriverName )) { + return TRUE; + } + } + moduleInfo1++; + } + return FALSE; +} + +BOOL +DisableFailedSysInitDrivers( + PRTL_PROCESS_MODULES ModuleInfo + ) +{ + HANDLE keyHandle; + HANDLE subkeyHandle; + BOOL result; + CHAR registryPath[ MAX_PATH ]; + ULONG subkey; + CHAR subkeyName[ MAX_PATH ]; + ULONG subkeyNameLength; + FILETIME fileTime; + WCHAR startValue[ 16 ]; + ULONG startValueLength; + ULONG status; + REG_INI_TABLE TempEntry; + + // + // Get a handle to the Services entry in the registry to walk + // its tree. + // + + TempEntry.RegistryPath = "SYS:Services"; + TempEntry.Flags = 0; + OpenRegistryKey( &TempEntry, &keyHandle ); + if (keyHandle != NULL) { + result = TRUE; + status = 0; + subkey = 0; + while (status == 0) { + subkeyNameLength = sizeof( subkeyName ); + status = RegEnumKeyExA( keyHandle, + subkey, + subkeyName, + &subkeyNameLength, + NULL, + NULL, + NULL, + &fileTime ); + if (status == 0) { + if (!(strstr( subkeyName, "_Rec" ))) { + _snprintf( registryPath, sizeof( registryPath ), "SYS:Services\\%s", subkeyName ); + TempEntry.RegistryPath = registryPath; + OpenRegistryKey( &TempEntry, &subkeyHandle ); + if (subkeyHandle != NULL) { + startValueLength = sizeof( startValue ); + if (ReadRegistry( subkeyHandle, + L"Start", + REG_DWORD, + startValue, + &startValueLength )) { + NtClose( subkeyHandle ); + if (startValueLength == SERVICE_SYSTEM_START || + startValueLength == SERVICE_BOOT_START + ) { + + // + // Driver located that should have been loaded at + // initialization time. Ensure in loaded module + // list. + // + + if (!LookupModuleName( subkeyName, ModuleInfo )) { + if (EnableDisableDriver( subkeyName, FALSE )) { + DebugLog((DEB_TRACE_SETUP, " Disabled load of %s system init driver.\n", subkeyName )); + } + } + } + } + } + } + } + subkey++; + } + NtClose( keyHandle ); + } + else { + result = FALSE; + } + + VirtualFree( ModuleInfo, 0, MEM_RELEASE ); + return result; +} + + +BOOL bDebugCSRSS; + +BOOL +InitializeDefaultRegistry( + PGLOBALS pGlobals + ) +{ + DWORD cb; + CHAR cbuff[512]; + PRTL_PROCESS_MODULES moduleInfo; + + + // + // See if we have done first boot of triple boot sequence. + // + + + cb = GetProfileStringA( AnsiWinlogon, + "DefaultSystem", + NULL, + WinlogonSystemVariable, + sizeof( WinlogonSystemVariable ) + ); + + if (cb == 0) { + + // + // Yes, see if we are doing second boot of triple boot + // sequence. + // + + cbuff[ 0 ] = '\0'; + cb = GetProfileStringA( AnsiWinlogon, + "Shell", + NULL, + cbuff, + sizeof( cbuff ) + ); + + if (cb != 0 && !_stricmp( cbuff, "progman.exe" )) { + + // + // No must be third or greater boot. See if they + // want to disable debugging the server. + // + + cb = GetProfileStringA( AnsiWinlogon, + "DebugServerCommand", + "ntsd -p -1 -g -G", + cbuff, + sizeof( cbuff ) + ); + + if (cb != 0 && (_stricmp( cbuff, "no" ) || bDebugCSRSS)) { + // + // No, start the debugger + // + UNICODE_STRING UniString; + STRING String; + + if (bDebugCSRSS) { + strcpy( cbuff, "ntsd -p -1 -g -G" ); + } + + RtlInitAnsiString(&String,cbuff); + RtlAnsiStringToUnicodeString(&UniString,&String,TRUE); +#if 0 + ExecProcesses(L"DebugServerCommand", + UniString.Buffer, + APPLICATION_DESKTOP_NAME, + &pGlobals->UserProcessData, + HIGH_PRIORITY_CLASS, + STARTF_FORCEOFFFEEDBACK); +#endif + RtlFreeUnicodeString(&UniString); + } + } + else + if (cb != 0 && strstr( cbuff, "winlogon.cmd" )) { + WriteProfileStringA( AnsiWinlogon, "AutoAdminLogon", "1" ); + + WriteProfileStringA( AnsiWinlogon, + "DefaultUserName", + "Administrator" + ); + + WriteProfileStringA( AnsiWinlogon, + "DefaultPassword", + "" + ); + + cb = sizeof( cbuff ); + if (GetComputerNameA( cbuff, &cb )) { + WriteProfileStringA( AnsiWinlogon, + "DefaultDomainName", + cbuff + ); + } + } + + return FALSE; + } + + TmppSetUnsecureDefaultDacl(); + pLocalGlobals = pGlobals; + NetSetupGoingToRun = pGlobals->fExecuteSetup; + + // + // Make sure we can see user (.default) portion of win.ini + // + + if (!OpenProfileUserMapping()) { + DebugLog((DEB_TRACE_SETUP, " Unable to enable access to user specific portion of win.ini\n" )); + } + + cb = SearchPathA( + NULL, + "net", + ".exe", + 512, + cbuff, + NULL + ); + if ( cb && cb <= 512 ) { + NetFound = TRUE; + } + else { + NetFound = FALSE; + } + + WriteProfileStringA( AnsiWinlogon, + "DefaultSystem", + NULL + ); + WriteProfileStringA( AnsiWinlogon, + "System", + WinlogonSystemVariable + ); + + GetEnvironmentVariableA( "SystemRoot", InputFileName, sizeof( InputFileName ) ); + strcat( InputFileName, "\\registry.ini" ); + + DebugLog((DEB_TRACE_SETUP, " Updating \\Registry and win.ini\n" )); + DebugLog((DEB_TRACE_SETUP, " with information from %s\n", InputFileName )); + + if (!DisableFailedBuiltInDrivers( &moduleInfo )) { + DebugLog((DEB_TRACE_SETUP, " Failed to disable builtin drivers.\n" )); + } + else { + if (!DisableFailedSysInitDrivers( moduleInfo )) { + DebugLog((DEB_TRACE_SETUP, " Failed to disable system init drivers.\n" )); + } + } + + DoFile(); + + // + // Set up the default username and domain so we logon as admin on next boot + // + + WriteProfileStringA( AnsiWinlogon, + "DefaultUserName", + "Administrator" + ); + + WriteProfileStringA( AnsiWinlogon, + "DefaultDomainName", + SavedProductType == NtProductLanManNt ? + SavedDomainName : SavedMachineName + ); + + GenerateInitialCommandScript(); + + // + // All done accessing user (.default) portion of win.ini + // + + CloseProfileUserMapping(); + + DebugLog((DEB_TRACE_SETUP, "Done updating. Rebooting to get changes.\n" )); + QuickReboot( pGlobals, FALSE ); + return TRUE; +} + +void +QuickReboot( + PGLOBALS pGlobals, + BOOL RebootToAlternateOS + ) +{ +#ifdef i386 + TCHAR szBuffer[ 64 ]; + + // + // Debug reboot facility to reboot directly into DOS by editing + // the users c:\boot.ini and changing the default boot OS to be + // whatever is in the root of C:, which is most likely DOS for + // people that are using NTLDR to dual boot. Only works for x86 + // as MIPS uses ARCLOADER and it is not defined how to change its + // default operating system to load, other than through jzsetup.exe + // + + if (RebootToAlternateOS) { + if (GetPrivateProfileString(TEXT("boot loader"), + TEXT("default"), + NULL, + szBuffer, + 64, + TEXT("c:\\boot.ini") + ) + ) { + if (GetPrivateProfileString(WideWinlogon, + TEXT("DefaultAlternateOS"), + TEXT(""), + szBuffer, + 64, + NULL + ) + ) { + WritePrivateProfileString(TEXT("boot loader"), + TEXT("default"), + szBuffer, + TEXT("c:\\boot.ini") + ); + } else { +#if DBG + DbgPrint( "WINLOGON: GetPrivateProfileString( %ws, %ws ) failed (%u)\n", + WideWinlogon, + TEXT("DefaultAlternateOS"), + GetLastError() + ); +#endif + } + } + else { +#if DBG + DbgPrint( "WINLOGON: GetPrivateProfileString( %ws, %ws ) failed (%u)\n", + TEXT("boot loader"), + TEXT("default"), + GetLastError() + ); +#endif + } + } +#endif // i386 + + WriteProfileStringW( NULL, NULL, NULL ); + EnablePrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE); + NtShutdownSystem(TRUE); + ASSERT(FALSE); +} + +VOID +TmppSetUnsecureDefaultDacl( VOID ) + +{ + + NTSTATUS Status; + PSID WorldSid; + PACL Dacl; + HANDLE Token; + + TOKEN_DEFAULT_DACL DefaultDacl; + + SID_IDENTIFIER_AUTHORITY + WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY; + + + + + + Status = RtlAllocateAndInitializeSid( + &WorldSidAuthority, + 1, //Sub authority count + SECURITY_WORLD_RID, //Sub authorities (up to 8) + 0, 0, 0, 0, 0, 0, 0, + &WorldSid + ); + + + // + // Set our default protection so that regini won't protect + // registry keys it creates. + // + + Dacl = RtlAllocateHeap( RtlProcessHeap(), 0, 256 ); + ASSERT(Dacl != NULL); + + Status = RtlCreateAcl( Dacl, 256, ACL_REVISION2); + ASSERT(NT_SUCCESS(Status)); + + Status = RtlAddAccessAllowedAce( + Dacl, + ACL_REVISION2, + (GENERIC_ALL ), + WorldSid + ); + ASSERT(NT_SUCCESS(Status)); + + + DefaultDacl.DefaultDacl = Dacl; + + + Status = NtOpenProcessToken( + NtCurrentProcess(), + (TOKEN_ADJUST_DEFAULT), + &Token + ); + ASSERT(NT_SUCCESS(Status)); + + + Status = NtSetInformationToken( + Token, + TokenDefaultDacl, + &DefaultDacl, + (ULONG)sizeof(TOKEN_DEFAULT_DACL) + ); + ASSERT(NT_SUCCESS(Status)); + Status = NtClose( Token ); + + RtlFreeHeap( RtlProcessHeap(), 0, Dacl ); + RtlFreeSid( WorldSid ); + + + return; + +} + +#endif // INIT_REGISTRY + + +// +// Open Registry key use base API. Same as OpenRegistryKey(), +// but without error handling (since GUI may not be active). +// + +HANDLE +OpenNtRegKey( + WCHAR *UPath + ) +{ + char *Path; + char FullPath[ 2 * MAX_PATH ]; + ANSI_STRING AnsiPath; + UNICODE_STRING UnicodePath; + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE Handle; + UNICODE_STRING UString; + ANSI_STRING AString; + + RtlInitUnicodeString( &UString, UPath); + RtlUnicodeStringToAnsiString( &AString, &UString, TRUE); + Path = AString.Buffer; + if (!_strnicmp( Path, "USR:", 4 )) { + strcpy( FullPath, "\\Registry\\User\\.Default\\" ); + Path += 4; + } + else + if (!_strnicmp( Path, "SYS:", 4 )) { + strcpy( FullPath, "\\Registry\\Machine\\System\\CurrentControlSet\\" ); + Path += 4; + } + else { + FullPath[ 0 ] = '\0'; + } + strcat( FullPath, Path ); + RtlInitAnsiString( &AnsiPath, FullPath ); + RtlAnsiStringToUnicodeString( &UnicodePath, &AnsiPath, TRUE ); + InitializeObjectAttributes( &ObjectAttributes, + &UnicodePath, + OBJ_CASE_INSENSITIVE | OBJ_OPENIF, + NULL, + NULL + ); + Status = NtOpenKey( &Handle, + MAXIMUM_ALLOWED, + &ObjectAttributes + ); + + RtlFreeUnicodeString( &UnicodePath ); + RtlFreeAnsiString( &AString ); + + if (NT_SUCCESS( Status )) { + return Handle; + } else { + return NULL ; + } +} + + +BOOL +ReadRegistry( + HANDLE KeyHandle, // Registry handle + WCHAR *ValueName, // Value to query + DWORD ValueType, // Value type expected + WCHAR *ValueData, // Value data if (multi-)string + DWORD *ValueLength // Length if string or value if REG_DWORD + ) +{ + NTSTATUS Status; + UNICODE_STRING UnicodeString; + WCHAR ValueBuffer[ 512 ]; + PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation; + ULONG ResultLength; + + RtlInitUnicodeString( & UnicodeString, ValueName ); + KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION) ValueBuffer ; + Status = NtQueryValueKey( KeyHandle, + &UnicodeString, + KeyValuePartialInformation, + KeyValueInformation, + sizeof ValueBuffer, + &ResultLength + ); + if (!NT_SUCCESS( Status )) { + return FALSE; + } + + if (KeyValueInformation->Type != ValueType) { + return FALSE ; + } + + switch (KeyValueInformation->Type) { + case REG_MULTI_SZ: + case REG_SZ: + RtlMoveMemory( ValueData, + (PWSTR)KeyValueInformation->Data, + KeyValueInformation->DataLength + ); + ValueData[ (KeyValueInformation->DataLength / sizeof( WCHAR )) - 1 ] = UNICODE_NULL; + *ValueLength = KeyValueInformation->DataLength; + break; + + case REG_DWORD: + *ValueLength = *((DWORD *) KeyValueInformation->Data) ; + break; + + default: + // We can't handle any other type. + return FALSE; + } + + return TRUE; +} + + +BOOL +WriteRegistry( + HANDLE KeyHandle, + char *ValueName, + DWORD ValueType, + char *ValueData, + DWORD ValueLength + ) +{ + char *s; + ANSI_STRING AnsiString; + UNICODE_STRING UnicodeString; + UNICODE_STRING UnicodeValueName; + DWORD ValueDword; + NTSTATUS Status; + + if (ValueType == REG_SZ) { + if (!_strnicmp( ValueData, "REG_DWORD ", 10 )) { + Status = RtlCharToInteger( ValueData + 10, 0, &ValueDword ); + if (!NT_SUCCESS( Status )) { + DeclareError( "Invalid integer\n" ); + return FALSE; + } + + ValueLength = sizeof( ValueDword ); + ValueData = (char *)&ValueDword; + ValueType = REG_DWORD; + } + else { + if (strchr( ValueData, '%')) { + ValueType = REG_EXPAND_SZ; + } + + RtlInitAnsiString( &AnsiString, ValueData ); + ValueLength = 0; + } + } + else + if (ValueType == REG_MULTI_SZ) { + s = ValueData; + AnsiString.Buffer = ValueData; + while (*s) { + while (*s++) { + } + } + + AnsiString.Length = (USHORT)(s - ValueData); + AnsiString.MaximumLength = (USHORT)(AnsiString.Length + 1); + ValueLength = 0; + } + else + if (ValueType == REG_BINARY) { + if (ValueLength == 0) { + DeclareError( "Invalid binary data\n" ); + return FALSE; + } + } + else + if (ValueType == REG_DWORD) { + Status = RtlCharToInteger( ValueData, 0, &ValueDword ); + if (!NT_SUCCESS( Status )) { + DeclareError( "Invalid integer\n" ); + return FALSE; + } + + ValueLength = sizeof( ValueDword ); + ValueData = (char *)&ValueDword; + } + else { + DeclareError( "Invalid data type == %lx for %s\n", + ValueType, + ValueName ? ValueName : "(null)" + ); + + return FALSE; + } + + if (ValueLength == 0) { + RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, TRUE ); + ValueLength = UnicodeString.MaximumLength; + ValueData = (char *)UnicodeString.Buffer; + } + else { + UnicodeString.Buffer = NULL; + } + + if (ValueName == NULL) { + RtlInitUnicodeString( &UnicodeValueName, NULL ); + } + else { + RtlInitAnsiString( &AnsiString, ValueName ); + RtlAnsiStringToUnicodeString( &UnicodeValueName, &AnsiString, TRUE ); + } + Status = NtSetValueKey( KeyHandle, + &UnicodeValueName, + 0, + ValueType, + ValueData, + ValueLength + ); + + if (UnicodeValueName.Buffer != NULL) { + RtlFreeUnicodeString( &UnicodeValueName ); + } + + if (UnicodeString.Buffer != NULL) { + RtlFreeUnicodeString( &UnicodeString ); + } + + if (NT_SUCCESS( Status )) { + return TRUE; + } + else { + DeclareError( "NtSetValueKey( %wZ ) failed (Status == %lx)\n", + &UnicodeValueName, + Status + ); + + return FALSE; + } +} diff --git a/private/windows/gina/winlogon/regini.h b/private/windows/gina/winlogon/regini.h new file mode 100644 index 000000000..a603a1abc --- /dev/null +++ b/private/windows/gina/winlogon/regini.h @@ -0,0 +1,52 @@ +/****************************** Module Header ******************************\ +* Module Name: regini.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Define apis user to implement default registry initialization +* +* History: +* 05-01-92 Stevewo Created. +\***************************************************************************/ + +#if INIT_REGISTRY + +BOOL +InitializeDefaultRegistry( + PGLOBALS pGlobals + ); + +void +QuickReboot( + PGLOBALS pGlobals, + BOOL RebootToAlternateOS + ); + +#endif // INIT_REGISTRY + + // + // Open Registry key use base API. Same as OpenRegistryKey(), + // but without error handling (since GUI may not be active). + // +HANDLE +OpenNtRegKey( + WCHAR *Path + ); + +BOOL +WriteRegistry( + HANDLE KeyHandle, + char *ValueName, + DWORD ValueType, + char *ValueData, + DWORD ValueLength + ); + +BOOL +ReadRegistry( + HANDLE KeyHandle, // Registry handle + WCHAR *ValueName, // Value to query + DWORD ValueType, // Value type expected + WCHAR *ValueData, // Value data if (multi-)string + DWORD *ValueLength // Length if string or value if REG_DWORD + ); diff --git a/private/windows/gina/winlogon/removabl.c b/private/windows/gina/winlogon/removabl.c new file mode 100644 index 000000000..88450de45 --- /dev/null +++ b/private/windows/gina/winlogon/removabl.c @@ -0,0 +1,866 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + removabl.c + +Abstract: + + Removable Media Services Module - This module contains services + related to the protection of removable media (e.g., floppies + and CD Roms) related to logon and logoff. + + +Author: + + Jim Kelly (JimK) Oct-19-1994 + +Environment: + + Part of Winlogon. + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + + + +////////////////////////////////// Section //////////////////////////////// +// // +// Constants used in this module only. // +// // +///////////////////////////////////////////////////////////////////////////// + +#define RMVP_DRIVE_CD_ROM (1) +#define RMVP_DRIVE_FLOPPY (2) + +// +// Size of the buffer used to enumerate object directory +// contents +// + +#define RMVP_OBJDIR_ENUM_BUFFER_SIZE (1024) + + + + + + +////////////////////////////////// Section //////////////////////////////// +// // +// Variables Global to this module. // +// // +///////////////////////////////////////////////////////////////////////////// + + + +PSID + RmvpLocalSystemSid, + RmvpAdministratorsSid; + + +// +// flags indicating whether or not we are configured to allocate +// floppies and/or CDRoms. +// + +BOOLEAN + RmvpAllocateFloppies = FALSE, + RmvpAllocateCDRoms = FALSE; + + + + + +////////////////////////////////// Section //////////////////////////////// +// // +// Prototypes of modules private to this module // +// // +///////////////////////////////////////////////////////////////////////////// + +VOID +RmvpAllocateDevice( + IN HANDLE DeviceHandle, + IN PUNICODE_STRING DeviceName, + IN PSECURITY_DESCRIPTOR SecurityDescriptor, + IN PUNICODE_STRING FsName, + IN OUT PHANDLE FsHandle + ); + +BOOLEAN +RmvpOpenDevice( + IN HANDLE Root, + IN POBJECT_DIRECTORY_INFORMATION Object, + IN ULONG DeviceType, + OUT PHANDLE Device + ); + + + + + +////////////////////////////////// Section //////////////////////////////// +// // +// Services Exported By This Module // +// // +///////////////////////////////////////////////////////////////////////////// + + +VOID +RmvInitializeRemovableMediaSrvcs( + VOID + ) +/*++ +Routine Description: + + Initialize RemovableMediaServices Module + + + +Arguments: + + None. + +Return Value: + + None. + + +--*/ +{ + NTSTATUS + Status; + + SID_IDENTIFIER_AUTHORITY + NtAuthority = SECURITY_NT_AUTHORITY; + + + // + // See if we need to protect floppy and/or CDRom drives. + // + + if (GetProfileInt( TEXT("Winlogon"), TEXT("AllocateFloppies"), 0) == 1) { + RmvpAllocateFloppies = TRUE; + } + + if (GetProfileInt( TEXT("Winlogon"), TEXT("AllocateCDRoms"), 0) == 1) { + RmvpAllocateCDRoms = TRUE; + } + + + // + // Initialize needed well-known sids + // + + Status = RtlAllocateAndInitializeSid( &NtAuthority, + 1, + SECURITY_LOCAL_SYSTEM_RID, + 0, 0, 0, 0, 0, 0, 0, + &RmvpLocalSystemSid + ); + ASSERT(NT_SUCCESS(Status)); + + Status = RtlAllocateAndInitializeSid( &NtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &RmvpAdministratorsSid + ); + ASSERT(NT_SUCCESS(Status)); + + return; +} + + + +VOID +RmvAllocateRemovableMedia( + IN PSID AllocatorId + ) +/*++ +Routine Description: + + This routine must be called during the logon process. + Its purpose is to: + + See if floppy and / or CD Rom allocation is enabled. + If so, then allocate the devices by: + + Restricting TRAVERSE access to the devices, and + + Forcing all handles to volumes open on those devices + to be made invalid. + + Note that the work of finding and openning these devices must + be done at logon time rather than system initialization time + because of NT's ability to dynamically add devices while running. + + + +Arguments: + + AllocatorId - Pointer to the SID to which the devices are + to be allocated. + +Return Value: + + None. + + +--*/ +{ + NTSTATUS + Status, + IgnoreStatus; + + ACCESS_MASK + Access; + + MYACE + Ace[3]; + + ACEINDEX + AceCount = 0; + + PSECURITY_DESCRIPTOR + SecurityDescriptor; + + ULONG + DriveType, + ReturnedLength, + Context = 0; + + CHAR + Buffer[RMVP_OBJDIR_ENUM_BUFFER_SIZE]; + + HANDLE + DeviceHandle, + FatHandle = NULL, + CdfsHandle = NULL, + DirectoryHandle; + + + UNICODE_STRING + DirectoryName, + CdfsName, + FatfsName; + + OBJECT_ATTRIBUTES + Attributes; + + BOOLEAN + Opened; + + POBJECT_DIRECTORY_INFORMATION + DirInfo; + + + RtlInitUnicodeString( &CdfsName, L"\\cdfs" ); + RtlInitUnicodeString( &FatfsName, L"\\fat" ); + + + + + // + // If there is nothing to allocate, then return. + // + + if (!RmvpAllocateFloppies && !RmvpAllocateCDRoms) { + return; + } + + + + /////////////////////////////////////////////////////////////////////// + // + // Technical Note: + // + // If I understand the way things work correctly, then there is + // a race condition that exists that we have to work around. + // + // The problem is that until a volume is mounted in a file-system + // that file system might not be loaded (and a corresponding device + // object may not exist). Therefore, we must not try to open a + // filesystem until we have opened a device object. For example, + // don't open FAT until we have opened \device\floppy0 AND PROTECTED + // IT AGAINST FUTURE TRAVERSE ACCESS. + // + // So, if you are looking at this routine and wondering why I don't + // open the FAT and CDFS file systems right away, it is in case they + // aren't loaded yet, but someone manages to squeek in a volume mount + // after I try to open the filesystems. + // + // What I believe will work is to open any floppy or cdrom devices + // I find, then change who has traverse access to them, then open + // the corresponding filesystem object IF IT HASN'T ALREADY BEEN + // OPENED. Once a filesystem object is opened, there is no need + // to close it and re-open it. + // + /////////////////////////////////////////////////////////////////////// + + + + + // + // Set up the protection to be applied to the removable device objects + // + // System: FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | SYNCHRONIZE + // AllocationId: RWE + // + // We apply the restrictive System access to the object so that network shares + // don't gain access (by nature of using a handle to the volume root + // that LM server keeps). + // + + Access = FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE; + SetMyAce( &(Ace[AceCount]), AllocatorId, Access, 0 ); + AceCount++; + //Access &= ~FILE_TRAVERSE; + Access = (FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | SYNCHRONIZE); + SetMyAce( &(Ace[AceCount]), RmvpLocalSystemSid, Access, 0 ); + AceCount++; + +#ifdef TRMV + { + // + // This is needed for our test program (an interactive program + // run in user mode) so that we don't set protection on the device + // preventing us from changing the protection. + // + // THIS CODE IS NOT INCLUDED WHEN THIS MODULE IS PART OF WINLOGON. + // + + ACCESS_MASK + TestorAccess = (FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | SYNCHRONIZE); + + + SetMyAce( &(Ace[AceCount]), RmvpAdministratorsSid, TestorAccess, 0 ); + AceCount++; + } +#endif // TEST REMOVABLE + + + // Check we didn't goof + ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount); + + // + // Create the security descriptor + // + + SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount); + if (SecurityDescriptor != NULL) { + + + // + // We find floppies and or CD Roms by enumerating the objects in + // the "\Device" object container and selecting the ones that + // start with "floppy" followed by a number or "cdrom" followed + // by a number. Even after finding such an object, we want to be + // sure it is a Device object and then open it and query it to + // make sure it is really a floppy or cdrom. The reason I say this + // is because you will find names like "floppyControllerEvent" in + // the \device container. + // + + RtlInitUnicodeString( &DirectoryName, L"\\Device" ); + InitializeObjectAttributes( &Attributes, + &DirectoryName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL ); + + + Status = NtOpenDirectoryObject( &DirectoryHandle, + STANDARD_RIGHTS_READ | + DIRECTORY_QUERY | + DIRECTORY_TRAVERSE, + &Attributes ); +#if DBG + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "Failed to open \\Device object container. Status: %#x\n", Status )); + } +#endif //DBG + ASSERT(NT_SUCCESS(Status)); + + + // + // Loop through the directory querying device objects + // + + Status = NtQueryDirectoryObject( DirectoryHandle, + Buffer, + sizeof(Buffer), + TRUE, // one entry at a time for now + TRUE, + &Context, + &ReturnedLength ); + +#if DBG + if (!NT_SUCCESS( Status )) { + if (Status != STATUS_NO_MORE_ENTRIES) { + DebugLog((DEB_ERROR, "failed to query directory object, status: 0x%lx\n", Status)); + } + } +#endif //DBG + + while ( NT_SUCCESS(Status) ) { + + // + // Point to the first record in the buffer, we are guaranteed to have + // one otherwise Status would have been NO_MORE_ENTRIES + // + + DirInfo = (POBJECT_DIRECTORY_INFORMATION)Buffer; + while (DirInfo->Name.Length != 0) { + + // + // For every record in the buffer compare it's name with the ones we're + // looking for + // + + if (RmvpAllocateFloppies) { + Opened = RmvpOpenDevice( DirectoryHandle, + DirInfo, + RMVP_DRIVE_FLOPPY, + &DeviceHandle + ); + if (Opened) { + RmvpAllocateDevice( DeviceHandle, &DirInfo->Name, SecurityDescriptor, &FatfsName, &FatHandle ); + NtClose( DeviceHandle ); + } + } + + if (RmvpAllocateCDRoms) { + Opened = RmvpOpenDevice( DirectoryHandle, + DirInfo, + RMVP_DRIVE_CD_ROM, + &DeviceHandle + ); + if (Opened) { + RmvpAllocateDevice( DeviceHandle, &DirInfo->Name, SecurityDescriptor, &CdfsName, &CdfsHandle ); + NtClose( DeviceHandle ); + } + } + + // + // Advance DirInfo to the next entry + // + + DirInfo ++; + + } //end_while (more names in buffer) + + + // + // Get the next block of entries + // + + Status = NtQueryDirectoryObject( DirectoryHandle, + Buffer, + sizeof(Buffer), + // LATER FALSE, + TRUE, // one entry at a time for now + FALSE, + &Context, + &ReturnedLength ); + + } //end_while (enumeration succeeded) + + // + // We no longer need handles to FAT or CDFS (if they were opened). + // + + if (FatHandle != NULL) { + IgnoreStatus = NtClose( FatHandle ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + if (CdfsHandle != NULL) { + IgnoreStatus = NtClose( CdfsHandle ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + + + + + + // + // Free up the security descriptor + // + + DeleteSecurityDescriptor(SecurityDescriptor); + + } else { + DebugLog((DEB_ERROR, "failed to create removable media security descriptor")); + } + + + return; +} + + + + +////////////////////////////////// Section //////////////////////////////// +// // +// Services Private To This Module // +// // +///////////////////////////////////////////////////////////////////////////// + +VOID +RmvpAllocateDevice( + IN HANDLE DeviceHandle, + IN PUNICODE_STRING DeviceName, + IN PSECURITY_DESCRIPTOR SecurityDescriptor, + IN PUNICODE_STRING FsName, + IN OUT PHANDLE FsHandle + ) + +/*++ + +Routine Description: + + This routine attempts to allocate the device opened by DeviceHandle. + + Allocation entails changing the protection on the device (the new + protection is passed in, but is expected to restrict TRAVERSE access), + and notifying the filesystem to invalidate all handles to the + volumes on the device. + + + +Arguments: + + DeviceHandle - handle to the device being allocated. This must be opened + for WRITE_DAC access. + + DeviceName - The name of the device being allocated. + + SecurityDescriptor - New protection to assign to the device. Only the + Dacl of this security descriptor is used. + + FsName - The name of the file system that owns responsibility for the + device. This is expected to be either "\\fat" or + "\\cdfs". + + FsHandle - Pointer to a handle to the filesystem. If the handle + value is NULL, then it is assumed that the filesystem has not yet + been opened. In that case, this routine will attempt to open the + filesystem after assigning protection to the device. + + +Return Value: + + TRUE - indicates the device was successfully opened. + + FALSE - indicates the device was NOT successfully opened. + + +--*/ +{ + + NTSTATUS + Status; + + OBJECT_ATTRIBUTES + Attributes; + + IO_STATUS_BLOCK + IoStatusBlock; + + BOOLEAN + WasEnabled; + + // + // Change the protection on the device to restrict traverse access. + // + + Status = NtSetSecurityObject( DeviceHandle, DACL_SECURITY_INFORMATION, SecurityDescriptor); + ASSERT(NT_SUCCESS(Status)); + + + // + // If the File system isn't yet open, then do so now. + // + + if ((*FsHandle) == NULL) { + + // + // Turn on the TCB privilege for our open to the filesystem to work. + // + + Status = RtlAdjustPrivilege( SE_TCB_PRIVILEGE, + TRUE, // Enable + FALSE, // Client + &WasEnabled + ); + ASSERT(NT_SUCCESS(Status)); + + if (NT_SUCCESS(Status)) { + InitializeObjectAttributes( &Attributes, + FsName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL ); + + Status = NtOpenFile( FsHandle, + (SYNCHRONIZE | FILE_READ_ATTRIBUTES), + &Attributes, + &IoStatusBlock, + (FILE_SHARE_READ | FILE_SHARE_WRITE), + FILE_SYNCHRONOUS_IO_NONALERT + ); + + if (!NT_SUCCESS(Status)) { + + // + // If no volumes have ever been mounted in the file system, + // then the file system may not be loaded. So, this error + // can not be considered serious. + // + + (*FsHandle) = NULL; // In case garbage was put in our out parameter + + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + DebugLog((DEB_ERROR, "Failed to open <%wZ>. Status: %#x\n", FsName, Status)); + } + } + + + // + // Now restore the privilege (if necessary) + // + + if (!WasEnabled) { + Status = RtlAdjustPrivilege( SE_TCB_PRIVILEGE, + FALSE, // Disable + FALSE, // Client + &WasEnabled + ); + ASSERT(NT_SUCCESS(Status)); + } + } + } + + if ((*FsHandle) != NULL) { + + // + // Notify the file system that it needs to invalidate the + // passed in volume. + // + + Status = NtFsControlFile( (*FsHandle), + NULL, + NULL, + NULL, + &IoStatusBlock, + FSCTL_INVALIDATE_VOLUMES, + &DeviceHandle, //DavidGoe is going to define a real structure to pass here + sizeof(HANDLE), + NULL, + 0 ); +#if DBG + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "Failed to remove handles from:\n" + " device: <%wZ>\n" + " file system: <%wZ>\n" + " Status: 0x%lx\n\n", + DeviceName, FsName, Status)); + ASSERT(NT_SUCCESS(Status)); + } +#endif //DBG + + } + + return; +} + + + +BOOLEAN +RmvpOpenDevice( + IN HANDLE Root, + IN POBJECT_DIRECTORY_INFORMATION Object, + IN ULONG DeviceType, + OUT PHANDLE Device + ) + +/*++ + +Routine Description: + + This routine is called to open a handle to a device whose name starts + with the string specified in the Prefix parameter. + + +Arguments: + + Root - handle to an object directory to be used as the root + when openning the device object. That is, the name specified + in the Object parameter is relative to the object this handle + refers to. + + Object - Contains the name of the object and the name of the type + of the object. If the name of the type is not "device" and the + name of the object does not start with the string passed in + the Prefix parameter, then the open is not attempted. + + DeviceType - specifies the type of device the object is expected to + be. Valid values are: + + RMVP_DRIVE_CD_ROM + RMVP_DRIVE_FLOPPY + + Device - Receives the handle to the device object (upon success). + + +Return Value: + + TRUE - indicates the device was successfully opened. + + FALSE - indicates the device was NOT successfully opened. + + +--*/ +{ + NTSTATUS + IgnoreStatus, + Status; + + OBJECT_ATTRIBUTES + ObjectAttributes; + + IO_STATUS_BLOCK + IoStatusBlock; + + UNICODE_STRING + Prefix, + DeviceName; + + HANDLE + FileHandle; + + FILE_FS_DEVICE_INFORMATION + DeviceInfo; + + + + //RmvpPrint("Attempting to open Object<%wZ>, Type<%wZ>\n", &Object->Name, &Object->TypeName); + + + RtlInitUnicodeString( &DeviceName, L"Device" ); + + // + // If the type is not "Device" then we aren't interested in this object + // + + if (!RtlEqualUnicodeString(&Object->TypeName, &DeviceName, TRUE)) { + return(FALSE); + } + + + // + // If the object name doesn't have the right prefix, then we aren't + // interested in this object + // + + if (DeviceType == RMVP_DRIVE_CD_ROM) { + + RtlInitUnicodeString( &Prefix, L"cdrom" ); + if (!RtlPrefixUnicodeString( &Prefix, &Object->Name, TRUE)) { + return(FALSE); + } + } else { + + ASSERT( DeviceType == RMVP_DRIVE_FLOPPY); + RtlInitUnicodeString( &Prefix, L"floppy" ); + if (!RtlPrefixUnicodeString( &Prefix, &Object->Name, TRUE)) { + return(FALSE); + } + } + + + + // + // Looks promising. Open the Device object + // + + InitializeObjectAttributes( &ObjectAttributes, + &Object->Name, + OBJ_CASE_INSENSITIVE, + Root, + NULL + ); + + Status = NtOpenFile( &FileHandle, + (ACCESS_MASK)FILE_READ_ATTRIBUTES | + WRITE_DAC | + READ_CONTROL | + SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_NONALERT + ); +#if DBG + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "Can't open for allocate: Object<%wZ> Type<%wZ>\n" + " Status: 0x%lx\n", &Object->Name, &Object->TypeName, Status)); + } +#endif //DBG + + if (NT_SUCCESS( Status )) { + + Status = NtQueryVolumeInformationFile( FileHandle, + &IoStatusBlock, + &DeviceInfo, + sizeof( DeviceInfo ), + FileFsDeviceInformation + ); + + if (NT_SUCCESS( Status )) { + + // + // See if its the right kind of beast and has the right name prefix + // + + if (DeviceType == RMVP_DRIVE_CD_ROM) { + if (DeviceInfo.DeviceType == FILE_DEVICE_CD_ROM) { + (*Device) = FileHandle; + DebugLog((DEB_TRACE, "Winlogon: Allocating CD Rom:\n" + " object: <%wZ>\n" + " type: <%wZ>\n", + &Object->Name, &Object->TypeName )); + return(TRUE); + } + + } else { + ASSERT( DeviceType == RMVP_DRIVE_FLOPPY); + if ((DeviceInfo.Characteristics & FILE_FLOPPY_DISKETTE)) { + (*Device) = FileHandle; + DebugLog((DEB_TRACE, "Winlogon: Allocating floppy:\n" + " object: <%wZ>\n" + " type: <%wZ>\n", + &Object->Name, &Object->TypeName )); + return(TRUE); + } + } + } + + IgnoreStatus = NtClose(FileHandle); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + return(FALSE); + +} + diff --git a/private/windows/gina/winlogon/removabl.h b/private/windows/gina/winlogon/removabl.h new file mode 100644 index 000000000..5304d05a2 --- /dev/null +++ b/private/windows/gina/winlogon/removabl.h @@ -0,0 +1,43 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + removabl.h + +Abstract: + + This file contains prototype definitions of services exported + from removabl.c + + +Author: + + Jim Kelly (JimK) Oct-19-1994 + +Environment: + + Part of Winlogon. + +Revision History: + + +--*/ + +#ifndef _REMOVABL_ +#define _REMOVABL_ + +VOID +RmvInitializeRemovableMediaSrvcs( + VOID + ); + +VOID +RmvAllocateRemovableMedia( + IN PSID AllocatorId + ); + + + +#endif //_REMOVABL_ diff --git a/private/windows/gina/winlogon/res.rc b/private/windows/gina/winlogon/res.rc new file mode 100644 index 000000000..890be2fd3 --- /dev/null +++ b/private/windows/gina/winlogon/res.rc @@ -0,0 +1,35 @@ +/****************************** Module Header ******************************\ +* Module Name: res.rc +* +* Copyright (c) 1991, Microsoft Corporation +* +* Main winlogon resource file. +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + + +#include "winlogon.h" + +#include "strings.rc" + +#include "sysshut.dlg" + +#include "dialogs.dlg" + +#include "win31mig.dlg" + +#include "wlevents.rc" + +IDD_SHUTDOWN_BITMAP ICON "shutdown.ico" + +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Windows NT Logon Application" +#define VER_INTERNALNAME_STR "winlogon\0" +#define VER_ORIGINALFILENAME_STR "WINLOGON.EXE" + +#include "common.ver" diff --git a/private/windows/gina/winlogon/sas.c b/private/windows/gina/winlogon/sas.c new file mode 100644 index 000000000..f28bdd805 --- /dev/null +++ b/private/windows/gina/winlogon/sas.c @@ -0,0 +1,524 @@ +/****************************** Module Header ******************************\ +* Module Name: sas.c +* +* Copyright (c) 1991, Microsoft Corporation +* +* Support routines to implement processing of the secure attention sequence +* +* Users must always press the SAS key sequence before entering a password. +* This module catches the key press and forwards a SAS message to the +* correct winlogon window. +* +* History: +* 12-05-91 Davidc Created. +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +// Internal Prototypes +LONG SASWndProc( + HWND hwnd, + UINT message, + DWORD wParam, + LONG lParam); + +BOOL SASCreate( + HWND hwnd); + +BOOL SASDestroy( + HWND hwnd); + + +// Global used to hold the window handle of the SAS window. +static HWND hwndSAS = NULL; +// LATER this hwndSAS will have to go in instance data when we have multiple threads + +// Global for SAS window class name +static PWCHAR szSASClass = TEXT("SAS window class"); + + +#if DBG +#define DEFAULT_QUICK_REBOOT 1 +#else +#define DEFAULT_QUICK_REBOOT 0 +#endif + +#define SHELL_RESTART_TIMER_ID 100 + +/***************************************************************************\ +* SASInit +* +* Initialises this module. +* +* Creates a window to receive the SAS and registers the +* key sequence as a hot key. +* +* Returns TRUE on success, FALSE on failure. +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL SASInit( + PGLOBALS pGlobals) +{ + WNDCLASS wc; + + if (hwndSAS != NULL) { + DebugLog((DEB_ERROR, "SAS module already initialized !!")); + return(FALSE); + } + + // + // Register the notification window class + // + + wc.style = CS_SAVEBITS; + wc.lpfnWndProc = (WNDPROC)SASWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = pGlobals->hInstance; + wc.hIcon = NULL; + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = szSASClass; + + if (!RegisterClass(&wc)) + return FALSE; + + hwndSAS = CreateWindowEx(0L, szSASClass, TEXT("SAS window"), + WS_OVERLAPPEDWINDOW, + 0, 0, 0, 0, + NULL, NULL, pGlobals->hInstance, NULL); + + if (hwndSAS == NULL) + return FALSE; + + // + // Store our globals pointer in the window user data + // + + SetWindowLong(hwndSAS, GWL_USERDATA, (LONG)pGlobals); + + // + // Register this window with windows so we get notified for + // screen-saver startup and user log-off + // + if (!SetLogonNotifyWindow(pGlobals->WindowStation.hwinsta, hwndSAS)) { + DebugLog((DEB_ERROR, "Failed to set logon notify window")); + return(FALSE); + } + + return(TRUE); +} + + +/***************************************************************************\ +* SASTerminate +* +* Terminates this module. +* +* Unregisters the SAS and destroys the SAS windows +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +VOID SASTerminate(VOID) +{ + DestroyWindow(hwndSAS); + + // Reset our globals + hwndSAS = NULL; +} + + +/***************************************************************************\ +* SASWndProc +* +* Window procedure for the SAS window. +* +* This window registers the SAS hotkey sequence, and forwards any hotkey +* messages to the current winlogon window. It does this using a +* timeout module function. i.e. every window should register a timeout +* even if it's 0 if they want to get SAS messages. +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +LONG SASWndProc( + HWND hwnd, + UINT message, + DWORD wParam, + LONG lParam) +{ + PGLOBALS pGlobals = (PGLOBALS)GetWindowLong(hwnd, GWL_USERDATA); + + switch (message) + { + + case WM_CREATE: + if (!SASCreate(hwnd)) + { + return(TRUE); // Fail creation + } + return(FALSE); // Continue creating window + + case WM_DESTROY: + DebugLog(( DEB_TRACE, "SAS Window Shutting down?\n")); +#if DBG + DebugBreak(); +#endif + SASDestroy(hwnd); + return(0); + + case WM_HOTKEY: + if (wParam == 1) + { + QuickReboot(pGlobals, TRUE); + return(0); + } + +#if DBG + if (wParam == 2) + { + switch (pGlobals->WindowStation.ActiveDesktop) + { + case Desktop_Winlogon: + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Application); + break; + case Desktop_Application: + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon); + break; + } + return(0); + } + if (wParam == 3) + { + DebugBreak(); + return(0); + } +#endif + + if (wParam == 4) + { + PGINASESSION pGina = pGlobals->pGina; + WCHAR szTaskMgr[] = L"taskmgr.exe"; + + DebugLog((DEB_TRACE, "Starting taskmgr.exe.\n")); + + if (pGlobals->UserLoggedOn ) { + pGina->pWlxStartApplication(pGina->pGinaContext, + APPLICATION_DESKTOP_PATH, + pGlobals->UserProcessData.pEnvironment, + szTaskMgr); + } + return(0); + } + + CADNotify(pGlobals, WLX_SAS_TYPE_CTRL_ALT_DEL); + return(0); + + case WM_LOGONNOTIFY: // A private notification from Windows + + DebugLog((DEB_TRACE_SAS, "LOGONNOTIFY message %d\n", wParam )); + + switch (wParam) + { + + + case LOGON_LOGOFF: + +#if DBG + DebugLog((DEB_TRACE_SAS, "\tWINLOGON : %s\n", (lParam & EWX_WINLOGON_CALLER) ? "True" : "False")); + DebugLog((DEB_TRACE_SAS, "\tSYSTEM : %s\n", (lParam & EWX_SYSTEM_CALLER) ? "True" : "False")); + DebugLog((DEB_TRACE_SAS, "\tSHUTDOWN : %s\n", (lParam & EWX_SHUTDOWN) ? "True" : "False")); + DebugLog((DEB_TRACE_SAS, "\tREBOOT : %s\n", (lParam & EWX_REBOOT) ? "True" : "False")); + DebugLog((DEB_TRACE_SAS, "\tPOWEROFF : %s\n", (lParam & EWX_POWEROFF) ? "True" : "False")); + DebugLog((DEB_TRACE_SAS, "\tFORCE : %s\n", (lParam & EWX_FORCE) ? "True" : "False")); + DebugLog((DEB_TRACE_SAS, "\tOLD_SYSTEM : %s\n", (lParam & EWX_WINLOGON_OLD_SYSTEM) ? "True" : "False")); + DebugLog((DEB_TRACE_SAS, "\tOLD_SHUTDOWN : %s\n", (lParam & EWX_WINLOGON_OLD_SHUTDOWN) ? "True" : "False")); + DebugLog((DEB_TRACE_SAS, "\tOLD_REBOOT : %s\n", (lParam & EWX_WINLOGON_OLD_REBOOT) ? "True" : "False")); + DebugLog((DEB_TRACE_SAS, "\tOLD_POWEROFF : %s\n", (lParam & EWX_WINLOGON_OLD_POWEROFF) ? "True" : "False")); +#endif + + // + // If there is an exit windows in progress, reject this + // message if it is not our own call coming back. This + // prevents people from calling ExitWindowsEx repeatedly + // + + if ( ExitWindowsInProgress && + ( !( lParam & EWX_WINLOGON_CALLER ) ) ) + { + break; + + } + pGlobals->LogoffFlags = lParam; + CADNotify(pGlobals, WLX_SAS_TYPE_USER_LOGOFF); + break; + + case LOGON_INPUT_TIMEOUT: + // + // Notify the current window + // + // ForwardMessage(pGlobals, WM_SCREEN_SAVER_TIMEOUT, 0, 0); + CADNotify(pGlobals, WLX_SAS_TYPE_SCRNSVR_TIMEOUT); + break; + + case LOGON_RESTARTSHELL: + // + // Restart the shell after X seconds + // + // We don't restart the shell for the following conditions: + // + // 1) No one is logged on + // 2) We are in the process of logging off + // (logoffflags will be non-zero) + // 3) The shell exiting gracefully + // (Exit status is in lParam. 1 = graceful) + // 4) A new user has logged on after the request + // to restart the shell. + // (in the case of autoadminlogon, the new + // user could be logged on before the restart + // request comes through). + // + + if (!pGlobals->UserLoggedOn || + pGlobals->LogoffFlags || + (lParam == 1) || + (pGlobals->TickCount > (DWORD)GetMessageTime())) { + + break; + } + + SetTimer (hwnd, SHELL_RESTART_TIMER_ID, 2000, NULL); + break; + } + + return(0); + + case WM_TIMER: + { + PGINASESSION pGina; + LONG lResult; + HKEY hKey; + BOOL bRestart = TRUE; + DWORD dwType, dwSize; + + + // + // Restart the shell + // + + if (wParam != SHELL_RESTART_TIMER_ID) { + break; + } + + KillTimer (hwnd, SHELL_RESTART_TIMER_ID); + + + // + // Check if we should restart the shell + // + + lResult = RegOpenKeyEx (HKEY_LOCAL_MACHINE, + WINLOGON_KEY, + 0, + KEY_READ, + &hKey); + + if (lResult == ERROR_SUCCESS) { + + dwSize = sizeof(bRestart); + RegQueryValueEx (hKey, + TEXT("AutoRestartShell"), + NULL, + &dwType, + (LPBYTE) &bRestart, + &dwSize); + + RegCloseKey (hKey); + } + + + if (bRestart) { + PWCH pchData; + PWSTR pszTok; + + DebugLog((DEB_TRACE, "Restarting user's shell.\n")); + + + pGina = pGlobals->pGina; + + pchData = AllocAndGetPrivateProfileString(APPLICATION_NAME, + SHELL_KEY, + TEXT("explorer.exe"), + NULL); + + if (!pchData) { + break; + } + + pszTok = wcstok(pchData, TEXT(",")); + while (pszTok) + { + if (*pszTok == TEXT(' ')) + { + while (*pszTok++ == TEXT(' ')) + ; + } + + + if (pGina->pWlxStartApplication(pGina->pGinaContext, + APPLICATION_DESKTOP_PATH, + pGlobals->UserProcessData.pEnvironment, + pszTok)) { + + ReportWinlogonEvent(pGlobals, + EVENTLOG_INFORMATION_TYPE, + EVENT_SHELL_RESTARTED, + 0, + NULL, + 1, + pszTok); + } + + pszTok = wcstok(NULL, TEXT(",")); + } + + Free(pchData); + } + + } + break; + + default: + return DefWindowProc(hwnd, message, wParam, lParam); + + } + + return 0L; +} + +BOOL bRegisteredQuickReboot; +BOOL bRegisteredDesktopSwitching; +BOOL bRegisteredWinlogonBreakpoint; +BOOL bRegisteredTaskmgr; + + +/***************************************************************************\ +* SASCreate +* +* Does any processing required for WM_CREATE message. +* +* Returns TRUE on success, FALSE on failure +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +BOOL SASCreate( + HWND hwnd) +{ + // Register the SAS unless we are told not to. + + + if (GetProfileInt( APPNAME_WINLOGON, VARNAME_AUTOLOGON, 0 ) != 2) { + if (!RegisterHotKey(hwnd, 0, MOD_CONTROL | MOD_ALT, VK_DELETE)) { + DebugLog((DEB_ERROR, "failed to register SAS")); + return(FALSE); // Fail creation + } + } + + + // + // (Ctrl+Alt+Shift+Del) hotkey to reboot into DOS directly + // + + if (GetProfileInt( APPNAME_WINLOGON, VARNAME_ENABLEQUICKREBOOT, DEFAULT_QUICK_REBOOT) != 0) { + if (!RegisterHotKey(hwnd, 1, MOD_CONTROL | MOD_ALT | MOD_SHIFT, VK_DELETE)) { + DebugLog((DEB_ERROR, "failed to register quick reboot SAS")); + bRegisteredQuickReboot = FALSE; + } else { + bRegisteredQuickReboot = TRUE; + } + } + +#if DBG + // + // (Ctrl+Alt+Tab) will switch between desktops + // + if (GetProfileInt( APPNAME_WINLOGON, VARNAME_ENABLEDESKTOPSWITCHING, 0 ) != 0) { + if (!RegisterHotKey(hwnd, 2, MOD_CONTROL | MOD_ALT, VK_TAB)) { + DebugLog((DEB_ERROR, "failed to register desktop switch SAS")); + bRegisteredDesktopSwitching = FALSE; + } else { + bRegisteredDesktopSwitching = TRUE; + } + } + + + if (WinlogonInfoLevel & DEB_COOL_SWITCH) { + if (!RegisterHotKey(hwnd, 3, MOD_CONTROL | MOD_ALT | MOD_SHIFT, VK_TAB)) { + DebugLog((DEB_ERROR, "failed to register breakpoint SAS")); + bRegisteredWinlogonBreakpoint = FALSE; + } else { + bRegisteredWinlogonBreakpoint = TRUE; + } + } +#endif + + // + // (Ctrl+Shift+Esc) will start taskmgr + // + + if (!RegisterHotKey(hwnd, 4, MOD_CONTROL | MOD_SHIFT, VK_ESCAPE)) { + DebugLog((DEB_ERROR, "failed to register taskmgr hotkey")); + bRegisteredTaskmgr = FALSE; + } else { + bRegisteredTaskmgr = TRUE; + } + + return(TRUE); +} + + +/***************************************************************************\ +* SASDestroy +* +* Does any processing required for WM_DESTROY message. +* +* Returns TRUE on success, FALSE on failure +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +BOOL SASDestroy( + HWND hwnd) +{ + // Unregister the SAS + UnregisterHotKey(hwnd, 0); + + if (bRegisteredQuickReboot) { + UnregisterHotKey(hwnd, 1); + } + if (bRegisteredDesktopSwitching) { + UnregisterHotKey(hwnd, 2); + } + +#if DBG + if (bRegisteredWinlogonBreakpoint) { + UnregisterHotKey(hwnd, 3); + } +#endif + + if (bRegisteredTaskmgr) { + UnregisterHotKey(hwnd, 4); + } + + + return(TRUE); +} diff --git a/private/windows/gina/winlogon/sas.h b/private/windows/gina/winlogon/sas.h new file mode 100644 index 000000000..979b3d875 --- /dev/null +++ b/private/windows/gina/winlogon/sas.h @@ -0,0 +1,24 @@ +/****************************** Module Header ******************************\ +* Module Name: sas.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Defines apis to handle Secure Attention Key Sequence +* +* History: +* 12-05-91 Davidc Created. +\***************************************************************************/ + +// +// Exported function prototypes +// + + +BOOL SASInit( + PGLOBALS pGlobals + ); + +VOID SASTerminate( + VOID + ); + diff --git a/private/windows/gina/winlogon/scrnsave.c b/private/windows/gina/winlogon/scrnsave.c new file mode 100644 index 000000000..0ce16f5c9 --- /dev/null +++ b/private/windows/gina/winlogon/scrnsave.c @@ -0,0 +1,981 @@ +/****************************** Module Header ******************************\ +* Module Name: scrnsave.c +* +* Copyright (c) 1991, Microsoft Corporation +* +* Support routines to implement screen-saver-invokation +* +* History: +* 01-23-91 Davidc Created. +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +#define LPTSTR LPWSTR + +// +// Define the structure used to pass data into the screen-saver control dialog +// +typedef struct { + PGLOBALS pGlobals; + BOOL fSecure; + BOOL fEnabled; + LPTSTR ScreenSaverName; + DWORD ProcessId; // Screen-saver process id + DWORD SasInterrupt; // Sas interrupt, if any + BOOL WeKilledIt; + HANDLE hProcess; + POBJECT_MONITOR Monitor; + int ReturnValue; +} SCREEN_SAVER_DATA; +typedef SCREEN_SAVER_DATA *PSCREEN_SAVER_DATA; + +// Parameters added to screen saver command line +TCHAR Parameters[] = TEXT(" /s"); + +// +// Private prototypes +// + +BOOL WINAPI +ScreenSaverDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ); + +BOOL +ScreenSaverDlgInit( + HWND hDlg + ); + +BOOL +StartScreenSaver( + PSCREEN_SAVER_DATA ScreenSaverData + ); + +BOOL +KillScreenSaver( + PSCREEN_SAVER_DATA ScreenSaverData, + int ReturnValue + ); + +BOOL +StartScreenSaverMonitor( + HWND hDlg + ); + +VOID +DeleteScreenSaverMonitor( + HWND hDlg + ); + +DWORD ScreenSaverMonitorThread( + LPVOID lpThreadParameter + ); + +BOOL +GetScreenSaverInfo( + PSCREEN_SAVER_DATA ScreenSaverData + ); + +VOID +DeleteScreenSaverInfo( + PSCREEN_SAVER_DATA ScreenSaverData + ); + +// Message sent by the monitor thread to main thread window +#define WM_SCREEN_SAVER_ENDED (WM_USER + 10) + + +/***************************************************************************\ +* ScreenSaverEnabled +* +* Checks that a screen-saver is enabled for the current logged-on user. +* +* Returns : TRUE if the current user has an enabled screen-saver, otherwise FALSE +* +* 10-15-92 Davidc Created. +\***************************************************************************/ + +BOOL +ScreenSaverEnabled( + PGLOBALS pGlobals) +{ + SCREEN_SAVER_DATA ScreenSaverData; + BOOL Enabled; + + ScreenSaverData.pGlobals = pGlobals; + GetScreenSaverInfo(&ScreenSaverData); + + Enabled = ScreenSaverData.fEnabled; + + DeleteScreenSaverInfo(&ScreenSaverData); + + return(Enabled); +} + +/***************************************************************************\ +* ValidateScreenSaver +* +* Confirm that the screen saver executable exists and it enabled. +* +* Returns : +* TRUE - the screen-saver is ready. +* FALSE - the screen-saver does not exist or is not enabled. +* +* 01-23-91 ericflo Created. +\***************************************************************************/ +BOOL +ValidateScreenSaver( + PSCREEN_SAVER_DATA ssd) +{ + WIN32_FIND_DATA fd; + HANDLE hFile; + BOOL Enabled; + LPTSTR lpTempSS, lpEnd; + WCHAR szFake[2]; + LPTSTR lpFake; + + + // + // Check if the screen saver enabled + // + + Enabled = ssd->fEnabled; + + // + // If the screen saver is enabled, confirm that the executable exists. + // + + if (Enabled) { + + // + // The screen save executable name contains some parameters after + // it. We need to allocate a temporary buffer, remove the arguments + // and test if the executable exists. + // + + lpTempSS = (LPTSTR) GlobalAlloc (GPTR, + sizeof (TCHAR) * (lstrlen (ssd->ScreenSaverName) + 1)); + + if (!lpTempSS) { + return FALSE; + } + + // + // Copy the filename to the temp buffer. + // + + lstrcpy (lpTempSS, ssd->ScreenSaverName); + + + // + // Since we know how many arguments were added to the executable, + // we can get the string length, move that many characters in from + // the right and insert a NULL. + // + + lpEnd = lpTempSS + lstrlen (lpTempSS); + *(lpEnd - lstrlen (Parameters)) = TEXT('\0'); + + // + // Test to see if the executable exists. + // + + if (SearchPath(NULL, lpTempSS, NULL, 2, szFake, &lpFake) == 0) + { + Enabled = FALSE; + + DebugLog((DEB_TRACE, "Screen Saver <%S> does not exist. Error is %d", + lpTempSS, GetLastError())); + + } + + // + // Clean up. + // + + GlobalFree (lpTempSS); + } + + return (Enabled); +} + +/***************************************************************************\ +* RunScreenSaver +* +* Starts the appropriate screen-saver for the current user in the correct +* context and waits for it to complete. +* If the user presses the SAS, we kill the screen-saver and return. +* If a logoff notification comes in, we kill the screen-saver and return. +* +* Returns : +* DLG_SUCCESS - the screen-saver completed successfully. +* DLG_FAILURE - unable to run the screen-saver +* DLG_LOCK_WORKSTATION - the screen-saver completed successfully and +* was designated secure. +* DLG_LOGOFF - the screen-saver was interrupted by a user logoff notification +* +* Normally the original desktop is switched back to and the desktop lock +* returned to its original state on exit. +* If the return value is DLG_LOCK_WORKSTATION or DLG_LOGOFF - the winlogon +* desktop has been switched to and the desktop lock retained. +* +* 01-23-91 Davidc Created. +\***************************************************************************/ + +int +RunScreenSaver( + PGLOBALS pGlobals, + BOOL WindowStationLocked, + BOOL AllowFastUnlock) +{ + HDESK hdeskPrevious; + int Result; + SCREEN_SAVER_DATA ScreenSaverData; + BOOL Success; + WinstaState PriorState; + DWORD BeginTime; + DWORD EndTime; + + + + // + // If no one is logged on, make SYSTEM the user. + // + if (!pGlobals->UserLoggedOn) + { + + + // + // Toggle the locks, in case the OPENLOCK is still set from + // a prior logoff. + // + + UnlockWindowStation(pGlobals->WindowStation.hwinsta); + LockWindowStation(pGlobals->WindowStation.hwinsta); + } + // + // Create and open the app desktop. + // + if (!(pGlobals->WindowStation.hdeskScreenSaver = + CreateDesktop((LPTSTR)SCREENSAVER_DESKTOP_NAME, + NULL, NULL, 0, MAXIMUM_ALLOWED, NULL))) { + DebugLog((DEB_TRACE, "Failed to create screen saver desktop\n")); + return(DLG_FAILURE); + } + + // + // Fill in screen-saver data structure + // + ZeroMemory( &ScreenSaverData, sizeof( ScreenSaverData ) ); + + ScreenSaverData.pGlobals = pGlobals; + + if (GetScreenSaverInfo(&ScreenSaverData)) { + if (!ValidateScreenSaver(&ScreenSaverData)) { + + DeleteScreenSaverInfo(&ScreenSaverData); + + CloseDesktop(pGlobals->WindowStation.hdeskScreenSaver); + + return (DLG_FAILURE); + } + } + + // + // Update windowstation lock so screen saver can start + // + + if (!pGlobals->pGina->pWlxScreenSaverNotify( + pGlobals->pGina->pGinaContext, + &ScreenSaverData.fSecure )) + { + DebugLog((DEB_TRACE, "GINA DLL rejected screen saver\n")); + + DeleteScreenSaverInfo( &ScreenSaverData ); + CloseDesktop( pGlobals->WindowStation.hdeskScreenSaver ); + + // + // If no one is logged on, remove SYSTEM as the user. + // + return(DLG_FAILURE); + } + + + pGlobals->ScreenSaverActive = TRUE; + + if ( ScreenSaverData.fSecure ) + { + + PriorState = pGlobals->WinlogonState; + pGlobals->WinlogonState = Winsta_Locked; + + DebugLog((DEB_TRACE_STATE, "RunScreenSaver: Setting state to %s\n", + GetState(Winsta_Locked))); + } + + // + // Switch to screen-saver desktop + // + if (!SetActiveDesktop(&pGlobals->WindowStation, Desktop_ScreenSaver)) { + + DebugLog((DEB_TRACE, "Failed to switch to screen saver desktop\n")); + + pGlobals->ScreenSaverActive = FALSE; + + DeleteScreenSaverInfo(&ScreenSaverData); + CloseDesktop(pGlobals->WindowStation.hdeskScreenSaver); + + // + // If no one is logged on, remove SYSTEM as the user. + // + return(DLG_FAILURE); + } + + // + // Stash begin time + // + + BeginTime = GetTickCount(); + + // + // Start the screen-saver + // + if (!StartScreenSaver(&ScreenSaverData)) { + + DebugLog((DEB_TRACE, "Failed to start screen-saver\n")); + + if (ScreenSaverData.fSecure && pGlobals->UserLoggedOn) + { + Result = WLX_SAS_ACTION_LOCK_WKSTA; + } + else + { + Result = DLG_FAILURE; + } + DeleteScreenSaverInfo(&ScreenSaverData); + SetActiveDesktop(&pGlobals->WindowStation, pGlobals->WindowStation.PreviousDesktop); + pGlobals->ScreenSaverActive = FALSE; + CloseDesktop(pGlobals->WindowStation.hdeskScreenSaver); + + // + // If no one is logged on, remove SYSTEM as the user. + // + return(Result); + } + + // + // Initialize the sas type to report + // + + ScreenSaverData.SasInterrupt = WLX_SAS_TYPE_SCRNSVR_ACTIVITY; + + // + // Summon the dialog that monitors the screen-saver + // + Result = WlxSetTimeout( pGlobals, TIMEOUT_NONE); + + Result = WlxDialogBoxParam( pGlobals, + pGlobals->hInstance, + (LPTSTR)IDD_CONTROL, + NULL, ScreenSaverDlgProc, + (LONG)&ScreenSaverData); + + EndTime = GetTickCount(); + + + if (EndTime <= BeginTime) + { + // + // TickCount must have wrapped around: + // + + EndTime += (0xFFFFFFFF - BeginTime); + } + else + { + EndTime -= BeginTime; + } + + // + // If the screen saver ran for less than the default period, don't enforce + // the lock. + // + if (ScreenSaverData.fSecure && pGlobals->UserLoggedOn) + { + + if (AllowFastUnlock && + (EndTime < (GetProfileInt( APPLICATION_NAME, + LOCK_GRACE_PERIOD_KEY, + LOCK_DEFAULT_VALUE) * 1000))) + { + Result = WLX_SAS_ACTION_NONE; + pGlobals->WinlogonState = PriorState; + } + else + { + Result = WLX_SAS_ACTION_LOCK_WKSTA; + } + + } + + DebugLog((DEB_TRACE, "Screensaver completed, SasInterrupt == %d\n", + ScreenSaverData.SasInterrupt)); + + + + // + // Set up desktop and windowstation lock appropriately. If we got a logoff, + // or we're supposed to lock the workstation, switch back to the winlogon + // desktop. Otherwise, go back to the users current desktop. + // + + if ((ScreenSaverData.SasInterrupt == WLX_SAS_TYPE_USER_LOGOFF) || + (Result == WLX_SAS_ACTION_LOCK_WKSTA) ) { + + // + // Switch to the winlogon desktop and retain windowstation lock + // + Success = SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon); + + } else { + + // + // Switch to previous desktop and retore lock to previous state + // + SetActiveDesktop(&pGlobals->WindowStation, pGlobals->WindowStation.PreviousDesktop); + + + // + // Tickle the messenger so it will display any queue'd messages. + // (This call is a kind of NoOp). + // + TickleMessenger(); + } + + // + // We are no longer active (our knowledge of the desktop state is correct), + // let SASRouter know, so any future SAS's will be sent correctly. + // + + pGlobals->ScreenSaverActive = FALSE; + // + // If we killed it, it means we got a SAS and killed the screen saver. + // we need to repost the SAS so that whomever invoked the screen saver + // can catch it and pass it off to the gina. Note, we special case + // WLX_SAS_TYPE_USER_LOGOFF below, because WlxSasNotify filters it out, + // and we really need to return as the result code... + // + if ( ScreenSaverData.WeKilledIt && + (ScreenSaverData.SasInterrupt != WLX_SAS_TYPE_SCRNSVR_ACTIVITY) ) + { + if ( ScreenSaverData.SasInterrupt == WLX_SAS_TYPE_USER_LOGOFF ) + { + // + // So HandleLoggedOn() will know what to do: + // + + pGlobals->SasType = WLX_SAS_TYPE_USER_LOGOFF ; + Result = WLX_SAS_ACTION_LOGOFF ; + } + else + { + WlxSasNotify( pGlobals, ScreenSaverData.SasInterrupt ); + } + } + + DeleteScreenSaverInfo(&ScreenSaverData); + + if (!CloseDesktop(pGlobals->WindowStation.hdeskScreenSaver)) { + DebugLog((DEB_TRACE, "Failed to close screen saver desktop!\n\n")); + } else + pGlobals->WindowStation.hdeskScreenSaver = NULL; + + // + // Update windowstation locks to reflect correct state. + // + + + return(Result); +} + + +/***************************************************************************\ +* FUNCTION: ScreenSaverDlgProc +* +* PURPOSE: Processes messages for the screen-saver control dialog +* +* DIALOG RETURNS : DLG_FAILURE if dialog could not be created +* DLG_SUCCESS if the screen-saver ran correctly and +* has now completed. +* DLG_LOGOFF() if the screen-saver was interrupted by +* a logoff notification. +* HISTORY: +* +* 12-09-91 Davidc Created. +* +\***************************************************************************/ + +BOOL WINAPI +ScreenSaverDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ) +{ + PSCREEN_SAVER_DATA pScreenSaverData = (PSCREEN_SAVER_DATA)GetWindowLong(hDlg, GWL_USERDATA); + + switch (message) { + + case WM_INITDIALOG: + SetWindowLong(hDlg, GWL_USERDATA, lParam); + + if (!ScreenSaverDlgInit(hDlg)) { + EndDialog(hDlg, DLG_FAILURE); + + } + + // + // Tell the mapping layer that we're a winlogon window, so we + // can handle our own timeouts. + // + + SetMapperFlag(hDlg, MAPPERFLAG_WINLOGON); + + EnableSasMessages( hDlg ); + + return(TRUE); + + case WLX_WM_SAS: + // + // Just kill the screen-saver, the monitor thread will notice that + // the process has ended and send us a message + // + + // + // Actually, stash the Sas code away, so that who ever invoked us + // can use it. + + + pScreenSaverData->SasInterrupt = wParam; + pScreenSaverData->WeKilledIt = TRUE; + + KillScreenSaver(pScreenSaverData, DLG_SUCCESS); + + DisableSasMessages(); + + return(TRUE); + + + case WM_OBJECT_NOTIFY: + + DeleteScreenSaverMonitor(hDlg); + + EndDialog(hDlg, pScreenSaverData->ReturnValue); + return(TRUE); + } + + // We didn't process this message + return FALSE; +} + + +/***************************************************************************\ +* FUNCTION: ScreenSaverDlgInit +* +* PURPOSE: Handles initialization of screen-saver control dialog +* Actually starts the screen-saver and puts the id of the +* screen-saver process in the screen-saver data structure. +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 12-09-91 Davidc Created. +* +\***************************************************************************/ + +BOOL +ScreenSaverDlgInit( + HWND hDlg + ) +{ + PSCREEN_SAVER_DATA ScreenSaverData = (PSCREEN_SAVER_DATA)GetWindowLong(hDlg, GWL_USERDATA); + + // + // Initialize our return value + // + ScreenSaverData->ReturnValue = DLG_SUCCESS; + + + // + // Start the thread that will wait for the screen-saver to finish + // + if (!StartScreenSaverMonitor(hDlg)) { + + DebugLog((DEB_TRACE, "Failed to start screen-saver monitor thread\n")); + KillScreenSaver(ScreenSaverData, DLG_FAILURE); + return(FALSE); + } + + return(TRUE); +} + + +/***************************************************************************\ +* FUNCTION: StartScreenSaver +* +* PURPOSE: Creates the screen-saver process +* +* RETURNS: TRUE on success, FALSE on failure +* +* On successful return, the ProcessId field in our global data structure +* is set to the screen-saver process id +* +* HISTORY: +* +* 12-09-91 Davidc Created. +* +\***************************************************************************/ + +BOOL +StartScreenSaver( + PSCREEN_SAVER_DATA ScreenSaverData + ) +{ + HANDLE hThread; + HANDLE hProcess; + + + // + // Try and exec the screen-saver app + // + if (!StartSystemProcess(ScreenSaverData->ScreenSaverName, + SCREENSAVER_DESKTOP_PATH, + CREATE_SUSPENDED | CREATE_SEPARATE_WOW_VDM | + IDLE_PRIORITY_CLASS, + STARTF_SCREENSAVER, + ScreenSaverData->pGlobals->UserProcessData.pEnvironment, + FALSE, + &hProcess, + &hThread) ) + { + DebugLog((DEB_ERROR, "Failed to start screen saver %ws, %d\n", + ScreenSaverData->ScreenSaverName, GetLastError())); + return(FALSE); + } + + if (ScreenSaverData->pGlobals->UserLoggedOn) + { + WlxAssignShellProtection( + ScreenSaverData->pGlobals, + ScreenSaverData->pGlobals->UserProcessData.UserToken, + hProcess, + hThread); + + } + + ResumeThread(hThread); + CloseHandle(hThread); + + ScreenSaverData->hProcess = hProcess; + + return TRUE; +} + + +/***************************************************************************\ +* FUNCTION: KillScreenSaver +* +* PURPOSE: Terminates the screen-saver process +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 12-09-91 Davidc Created. +* +\***************************************************************************/ + +BOOL +KillScreenSaver( + PSCREEN_SAVER_DATA ScreenSaverData, + int ReturnValue + ) +{ + HANDLE hProcess; + BOOL bRet; + + // + // Store the return value to be used by the dlg proc when the notification + // arrives from the monitor thread that the SS has ended. + // + + ScreenSaverData->ReturnValue = ReturnValue; + + if (ScreenSaverData->hProcess != INVALID_HANDLE_VALUE) + { + bRet = TerminateProcess(ScreenSaverData->hProcess, STATUS_SUCCESS); + if (!bRet) + { + DebugLog((DEB_TRACE, "Failed to terminate screen-saver process, error = %d\n", GetLastError())); + } + + CloseObjectMonitorObject( ScreenSaverData->Monitor ); + + } + +// if (ScreenSaverData->Monitor) +// { +// DeleteObjectMonitor( ScreenSaverData->Monitor, FALSE ); +// } + + ScreenSaverData->hProcess = INVALID_HANDLE_VALUE; + + return(bRet); +} + + +/***************************************************************************\ +* FUNCTION: StartScreenSaverMonitor +* +* PURPOSE: Creates a thread that waits for the screen-saver to terminate +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 12-09-91 Davidc Created. +* +\***************************************************************************/ + +BOOL +StartScreenSaverMonitor( + HWND hDlg + ) +{ + PSCREEN_SAVER_DATA ScreenSaverData = (PSCREEN_SAVER_DATA)GetWindowLong(hDlg, GWL_USERDATA); + + // + // Create a monitor object to watch the screen-saver process + // + + ScreenSaverData->Monitor = CreateObjectMonitor(ScreenSaverData->hProcess, + hDlg, 0); + + if (ScreenSaverData->Monitor == NULL) { + DebugLog((DEB_TRACE, "Failed to create screen-saver monitor object\n")); + return(FALSE); + } + + return TRUE; +} + + +/***************************************************************************\ +* FUNCTION: DeleteScreenSaverMonitor +* +* PURPOSE: Cleans up resources used by screen-saver monitor +* +* RETURNS: Nothing +* +* HISTORY: +* +* 01-11-93 Davidc Created. +* +\***************************************************************************/ + +VOID +DeleteScreenSaverMonitor( + HWND hDlg + ) +{ + PSCREEN_SAVER_DATA ScreenSaverData = (PSCREEN_SAVER_DATA)GetWindowLong(hDlg, GWL_USERDATA); + POBJECT_MONITOR Monitor = ScreenSaverData->Monitor; + + // + // Delete the object monitor + // + + CloseObjectMonitorObject( Monitor ); + + DeleteObjectMonitor(Monitor, FALSE); + +} + + +/***************************************************************************\ +* FUNCTION: GetScreenSaverInfo +* +* PURPOSE: Gets the name of the screen-saver that should be run. Also whether +* the user wanted the screen-saver to be secure. These values are +* filled in in the ScreenSaver data structure on return. +* +* If there is no current user logged on or if we fail to get the +* user's preferred screen-saver info, we default to the system +* secure screen-saver. +* +* RETURNS: TRUE on success +* +* HISTORY: +* +* 12-09-91 Davidc Created. +* +\***************************************************************************/ + +BOOL +GetScreenSaverInfo( + PSCREEN_SAVER_DATA ScreenSaverData + ) +{ + BOOL Success = FALSE; + TCHAR SystemScreenSaverName[MAX_STRING_BYTES]; + WCHAR ExpandedName[MAX_PATH]; + DWORD ScreenSaverLength; + DWORD ExpandedSize; + + + // + // Get the default system screen-saver name + // + LoadString(NULL, IDS_SYSTEM_SCREEN_SAVER_NAME, SystemScreenSaverName, sizeof(SystemScreenSaverName)); + + + // + // Open the ini file mapping layer in the current user's context + // + + if (!OpenIniFileUserMapping(ScreenSaverData->pGlobals)) + { + DebugLog((DEB_ERROR, "Failed to open user profile mapping!\n")); + } + + // + // Try and get the user screen-saver program name + // + + ScreenSaverData->ScreenSaverName = AllocAndGetPrivateProfileString( + SCREEN_SAVER_INI_SECTION, + SCREEN_SAVER_FILENAME_KEY, + SystemScreenSaverName, // default + SCREEN_SAVER_INI_FILE); + + if (ScreenSaverData->ScreenSaverName == NULL) { + DebugLog((DEB_TRACE, "Failed to get screen-saver name\n")); + goto Exit; + } + + ScreenSaverLength = lstrlen(ScreenSaverData->ScreenSaverName); + + // + // Figure out how big based on the expanded environment variables, if any. + // subtract one, since Expand returns the # characters, plus the \0. + // + ExpandedSize = ExpandEnvironmentStrings(ScreenSaverData->ScreenSaverName, + ExpandedName, + MAX_PATH) - 1; + + if (ExpandedSize != ScreenSaverLength ) + { + // + // Well, we expanded to something other than what we had originally. + // So, alloc anew and do the parameter thing right now. + // + + Free(ScreenSaverData->ScreenSaverName); + ScreenSaverData->ScreenSaverName = Alloc( (ExpandedSize + + lstrlen(Parameters) + 1) * + sizeof(WCHAR) ); + + if (!ScreenSaverData->ScreenSaverName) + { + DebugLog((DEB_WARN, "No memory for screensaver\n")); + goto Exit; + } + + wcscpy( ScreenSaverData->ScreenSaverName, + ExpandedName); + wcscpy( &ScreenSaverData->ScreenSaverName[ExpandedSize], + Parameters); + + } + else + { + // + // Always add some fixed screen-saver parameters + // + + ScreenSaverData->ScreenSaverName = ReAlloc( + ScreenSaverData->ScreenSaverName, + (lstrlen(ScreenSaverData->ScreenSaverName) + + lstrlen(Parameters) + 1) + * sizeof(TCHAR)); + if (ScreenSaverData->ScreenSaverName == NULL) { + DebugLog((DEB_TRACE, "Realloc of screen-saver name failed\n")); + goto Exit; + } + + lstrcat(ScreenSaverData->ScreenSaverName, Parameters); + + } + + // + // Find out if the screen-saver should be secure + // + + ScreenSaverData->fSecure = (GetPrivateProfileInt( + SCREEN_SAVER_INI_SECTION, + SCREEN_SAVER_SECURE_KEY, + (DWORD)FALSE, // default to non-secure + SCREEN_SAVER_INI_FILE) != 0); + + // + // Find out if the screen-saver is enabled + // + ScreenSaverData->fEnabled = (GetProfileInt( + WINDOWS_INI_SECTION, + SCREEN_SAVER_ENABLED_KEY, + (DWORD)FALSE // default to not-enabled + ) != 0); + + Success = TRUE; + +Exit: + + // + // Close the ini file mapping - this closes the user registry key + // + + (VOID)CloseIniFileUserMapping(ScreenSaverData->pGlobals); + + return(Success); +} + + +/***************************************************************************\ +* FUNCTION: DeleteScreenSaverInfo +* +* PURPOSE: Frees up any space allocate by screen-saver data structure +* +* RETURNS: Nothing +* +* HISTORY: +* +* 11-17-92 Davidc Created. +* +\***************************************************************************/ + +VOID +DeleteScreenSaverInfo( + PSCREEN_SAVER_DATA ScreenSaverData + ) +{ + if (ScreenSaverData->ScreenSaverName != NULL) { + Free(ScreenSaverData->ScreenSaverName); + } +} diff --git a/private/windows/gina/winlogon/scrnsave.h b/private/windows/gina/winlogon/scrnsave.h new file mode 100644 index 000000000..3172b1a93 --- /dev/null +++ b/private/windows/gina/winlogon/scrnsave.h @@ -0,0 +1,27 @@ +/****************************** Module Header ******************************\ +* Module Name: scrnsave.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Secure Attention Key Sequence +* +* History: +* 01-23-91 Davidc Created. +\***************************************************************************/ + +// +// Exported function prototypes +// + +BOOL +ScreenSaverEnabled( + PGLOBALS pGlobals + ); + +int +RunScreenSaver( + PGLOBALS pGlobals, + BOOL WindowStationLocked, + BOOL AllowFastUnlock + ); + diff --git a/private/windows/gina/winlogon/secutil.c b/private/windows/gina/winlogon/secutil.c new file mode 100644 index 000000000..8189a8a2f --- /dev/null +++ b/private/windows/gina/winlogon/secutil.c @@ -0,0 +1,2676 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1993. +// +// File: secutil.c +// +// Contents: Security Utilities +// +// Classes: +// +// Functions: +// +// History: 8-25-94 RichardW Created +// +//---------------------------------------------------------------------------- + +#include "precomp.h" +#pragma hdrstop + +// +// 'Constants' used in this module only. +// +SID_IDENTIFIER_AUTHORITY gSystemSidAuthority = SECURITY_NT_AUTHORITY; +SID_IDENTIFIER_AUTHORITY gLocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY; +PSID gLocalSid; // Initialized in 'InitializeSecurityGlobals' +PSID gAdminSid; // Initialized in 'InitializeSecurityGlobals' +PSID pWinlogonSid; + +typedef struct _MYACELIST { + DWORD Total; + DWORD Active; + DWORD WinlogonOnly; + MYACE * Aces; + DWORD TotalSids; + DWORD ActiveSids; + PSID * Sids; +} MYACELIST, * PMYACELIST; + + +// +// Define all access to windows objects +// + +#define DESKTOP_ALL (DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | \ + DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | \ + DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK | \ + DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | \ + DESKTOP_SWITCHDESKTOP | STANDARD_RIGHTS_REQUIRED) + +#define WINSTA_ALL (WINSTA_ENUMDESKTOPS | WINSTA_READATTRIBUTES | \ + WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | \ + WINSTA_WRITEATTRIBUTES | WINSTA_ACCESSGLOBALATOMS | \ + WINSTA_EXITWINDOWS | WINSTA_ENUMERATE | \ + WINSTA_READSCREEN | \ + STANDARD_RIGHTS_REQUIRED) + +#define WINSTA_ATOMS (WINSTA_ACCESSGLOBALATOMS | \ + WINSTA_ACCESSCLIPBOARD ) + +// +// Private prototypes +// + +BOOL +InitializeWindowsSecurity( + PGLOBALS pGlobals + ); + +VOID +InitializeSecurityGlobals( + VOID + ); + +PVOID +CreateAccessAllowedAce( + PSID Sid, + ACCESS_MASK AccessMask, + UCHAR AceFlags, + UCHAR InheritFlags + ); + +VOID +DestroyAce( + PVOID Ace + ); + +PMYACELIST +CreateAceList( + DWORD Count); + +BOOL +InitializeWinstaSecurity( + PWinstaDescription pWinsta); + +/***************************************************************************\ +* InitializeSecurity +* +* Initializes the security module +* +* Returns TRUE on success, FALSE on failure +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +BOOL +InitializeSecurity( + PGLOBALS pGlobals + ) +{ + // + // Set up our module globals + // + InitializeSecurityGlobals(); + + pWinlogonSid = pGlobals->WinlogonSid; + + + // + // Initialize the removable medial module + // + + RmvInitializeRemovableMediaSrvcs(); + + + // + // Initialize windows security aspects + // + if (!InitializeWindowsSecurity(pGlobals)) { + DebugLog((DEB_ERROR, "failed to initialize windows security\n")); + return(FALSE); + } + + // + // Change user to 'system' + // + if (!SecurityChangeUser(pGlobals, NULL, NULL, pWinlogonSid, FALSE)) { + DebugLog((DEB_ERROR, "failed to set user to system\n")); + return(FALSE); + } + + return TRUE; +} + + +/***************************************************************************\ +* InitializeWindowsSecurity +* +* Initializes windows specific parts of security module +* +* Returns TRUE on success, FALSE on failure +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +BOOL +InitializeWindowsSecurity( + PGLOBALS pGlobals + ) +{ + + // + // Register with windows so we can create windowstation etc. + // + if (!RegisterLogonProcess((DWORD)NtCurrentTeb()->ClientId.UniqueProcess, TRUE)) { + DebugLog((DEB_ERROR, "could not register itself as logon process\n")); + return(FALSE); + } + + // + // Create and open the windowstation + // + if (!(pGlobals->WindowStation.hwinsta = CreateWindowStationW(WINDOW_STATION_NAME, + 0, MAXIMUM_ALLOWED, NULL))) { + DebugLog((DEB_ERROR, "could not create windowstation\n")); + return(FALSE); + } + pGlobals->WindowStation.pszWinsta = WINDOW_STATION_NAME; + + // + // Associate winlogon with this window-station + // + if (!SetProcessWindowStation(pGlobals->WindowStation.hwinsta)) { + DebugLog((DEB_ERROR, "failed to set process window-station\n")); + return(FALSE); + } + +#if 0 + // + // Set up window-station security (no user access yet) + // + if (!SetWindowStationSecurity(pGlobals, NULL)) { + DebugLog((DEB_ERROR, "failed to set window-station security\n")); + return(FALSE); + } +#endif + + if ( !InitializeWinstaSecurity( &pGlobals->WindowStation ) ) + { + DebugLog((DEB_ERROR, "failed to init winsta security\n" )); + return( FALSE ); + } + +#ifdef LATER // put the window-station lock back here when windows allows us to create desktops with the lock + // + // Lock the window-station now before we create any desktops + // + if (LockWindowStation(pGlobals->hwinsta) == WSS_ERROR) { + DebugLog((DEB_ERROR, "failed to lock window-station\n")); + return(FALSE); + } +#endif + // + // Create and open the desktops. + // Pass in NULL for the default display + // + + if (!(pGlobals->WindowStation.hdeskWinlogon = CreateDesktop((LPTSTR)WINLOGON_DESKTOP_NAME, + NULL, NULL, 0, MAXIMUM_ALLOWED, NULL))) { + DebugLog((DEB_ERROR, "Failed to create winlogon desktop\n")); + return(FALSE); + } + + if (!(pGlobals->WindowStation.hdeskApplication = CreateDesktop((LPTSTR)APPLICATION_DESKTOP_NAME, + NULL, NULL, 0, MAXIMUM_ALLOWED, NULL))) { + DebugLog((DEB_ERROR, "Failed to create application desktop\n")); + return(FALSE); + } + + // + // Make sure that this thread is started on the logon desktop. This + // is to ensure that apps will not be able to see its threadinfo + // data. + // + + SetThreadDesktop(pGlobals->WindowStation.hdeskWinlogon); + GetDesktopWindow(); + + if (SetThreadDesktop(pGlobals->WindowStation.hdeskApplication)) + { + pGlobals->WindowStation.hwndAppDesktop = GetDesktopWindow(); + SetThreadDesktop(pGlobals->WindowStation.hdeskWinlogon); + } + else + { + DebugLog((DEB_WARN, "Could not set thread desktop to app desktop, %d\n", GetLastError())); + } + + pGlobals->WindowStation.hdeskScreenSaver = NULL; + + // + // Set desktop security (no user access yet) + // + if (!SetWinlogonDesktopSecurity(pGlobals->WindowStation.hdeskWinlogon, pWinlogonSid)) { + DebugLog((DEB_ERROR, "Failed to set winlogon desktop security\n")); + return(FALSE); + } + if (!SetUserDesktopSecurity(pGlobals->WindowStation.hdeskApplication, NULL, pWinlogonSid)) { + DebugLog((DEB_ERROR, "Failed to set application desktop security\n")); + return(FALSE); + } + + // + // Associate winlogon with its desktop + // + if (!SetThreadDesktop(pGlobals->WindowStation.hdeskWinlogon)) { + DebugLog((DEB_ERROR, "Failed to associate winlogon with winlogon desktop\n")); + return(FALSE); + } + + + // + // Switch to the winlogon desktop + // + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon); + + return(TRUE); +} + + + + + +/***************************************************************************\ +* SetMyAce +* +* Helper routine that fills in a MYACE structure. +* +* History: +* 02-06-92 Davidc Created +\***************************************************************************/ +VOID +SetMyAce( + PMYACE MyAce, + PSID Sid, + ACCESS_MASK Mask, + UCHAR InheritFlags + ) +{ + MyAce->Sid = Sid; + MyAce->AccessMask= Mask; + MyAce->InheritFlags = InheritFlags; +} + + +/***************************************************************************\ +* SetWindowStationSecurity +* +* Sets the security on the specified window station given the logon sid passed. +* +* If the UserSid = NULL, no access is given to anyone other than winlogon +* +* Returns TRUE on success, FALSE on failure +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +BOOL +SetWindowStationSecurity( + PGLOBALS pGlobals, + PSID UserSid + ) +{ + MYACE Ace[9]; + ACEINDEX AceCount = 0; + PSECURITY_DESCRIPTOR SecurityDescriptor; + SECURITY_INFORMATION si; + BOOL Result; + + // + // Define the Winlogon ACEs + // + + SetMyAce(&(Ace[AceCount++]), + pWinlogonSid, + WINSTA_ALL, + NO_PROPAGATE_INHERIT_ACE + ); + SetMyAce(&(Ace[AceCount++]), + pWinlogonSid, + GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL, + OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE + ); + + // + // Define the Admin ACEs + // + + SetMyAce(&(Ace[AceCount++]), + gAdminSid, + WINSTA_ENUMERATE | WINSTA_READATTRIBUTES, + NO_PROPAGATE_INHERIT_ACE + ); + SetMyAce(&(Ace[AceCount++]), + gAdminSid, + DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | DESKTOP_ENUMERATE | + DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU, + OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE + ); + + // + // Define the User ACEs + // + + if (UserSid != NULL) { + + SetMyAce(&(Ace[AceCount++]), + UserSid, + WINSTA_ALL, + NO_PROPAGATE_INHERIT_ACE + ); + SetMyAce(&(Ace[AceCount++]), + UserSid, + GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL, + OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE + ); + + } + + // + // If a user is logged in and UserSid is not the logged in user sid, + // add the logged in user's sid. + // + + if (pGlobals->UserLoggedOn && UserSid != NULL && + !RtlEqualSid(pGlobals->UserProcessData.UserSid, UserSid)) { + + SetMyAce(&(Ace[AceCount++]), + pGlobals->UserProcessData.UserSid, + WINSTA_ALL, + NO_PROPAGATE_INHERIT_ACE + ); + SetMyAce(&(Ace[AceCount++]), + pGlobals->UserProcessData.UserSid, + GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL, + OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE + ); + } + + // Check we didn't goof + ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount); + + // + // Create the security descriptor + // + + SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount); + if (SecurityDescriptor == NULL) { + DebugLog((DEB_ERROR, "failed to create winsta security descriptor\n")); + return(FALSE); + } + + // + // Set the DACL on the object + // + + si = DACL_SECURITY_INFORMATION; + Result = SetUserObjectSecurity(pGlobals->WindowStation.hwinsta, &si, SecurityDescriptor); + + // + // Free up the security descriptor + // + + DeleteSecurityDescriptor(SecurityDescriptor); + + // + // Return success status + // + + if (!Result) { + DebugLog((DEB_ERROR, "failed to set windowstation security\n")); + } + return(Result); +} + + +/***************************************************************************\ +* SetWinlogonDesktopSecurity +* +* Sets the security on the specified desktop so only winlogon can access it +* +* Returns TRUE on success, FALSE on failure +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +BOOL +SetWinlogonDesktopSecurity( + HDESK hdesktop, + PSID WinlogonSid + ) +{ + MYACE Ace[2]; + ACEINDEX AceCount = 0; + PSECURITY_DESCRIPTOR SecurityDescriptor; + SECURITY_INFORMATION si; + BOOL Result; + + // + // Define the Winlogon ACEs + // + + SetMyAce(&(Ace[AceCount++]), + WinlogonSid, + DESKTOP_ALL, + 0 + ); + + // + // Add enumerate access for administrators + // + + SetMyAce(&(Ace[AceCount++]), + gAdminSid, + DESKTOP_ENUMERATE | STANDARD_RIGHTS_REQUIRED, + NO_PROPAGATE_INHERIT_ACE + ); + + // Check we didn't goof + ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount); + + // + // Create the security descriptor + // + + SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount); + if (SecurityDescriptor == NULL) { + DebugLog((DEB_ERROR, "failed to create winlogon desktop security descriptor\n")); + return(FALSE); + } + + // + // Set the DACL on the object + // + + si = DACL_SECURITY_INFORMATION; + Result = SetUserObjectSecurity(hdesktop, &si, SecurityDescriptor); + + // + // Free up the security descriptor + // + + DeleteSecurityDescriptor(SecurityDescriptor); + + // + // Return success status + // + + if (!Result) { + DebugLog((DEB_ERROR, "failed to set winlogon desktop security\n")); + } + return(Result); +} + + +/***************************************************************************\ +* SetUserDesktopSecurity +* +* Sets the security on the specified desktop given the logon sid passed. +* +* If UserSid = NULL, access is given only to winlogon +* +* Returns TRUE on success, FALSE on failure +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +BOOL +SetUserDesktopSecurity( + HDESK hdesktop, + PSID UserSid, + PSID WinlogonSid + ) +{ + MYACE Ace[3]; + ACEINDEX AceCount = 0; + PSECURITY_DESCRIPTOR SecurityDescriptor; + SECURITY_INFORMATION si; + BOOL Result; + ACCESS_MASK DesktopAccess; + DWORD MiserlyAccess; + + // + // Define the Winlogon ACEs + // + + SetMyAce(&(Ace[AceCount++]), + WinlogonSid, + DESKTOP_ALL, + 0 + ); + + // + // Define the Admin ACEs + // + + MiserlyAccess = GetProfileInt( WINLOGON, RESTRICT_NONINTERACTIVE_ACCESS, 0 ); + + if ( MiserlyAccess ) + { + DesktopAccess = DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | + DESKTOP_ENUMERATE | + DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU ; + } + else + { + + DesktopAccess = DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | + DESKTOP_ENUMERATE | + DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU | + GENERIC_EXECUTE ; + } + + SetMyAce(&(Ace[AceCount++]), + gAdminSid, + DesktopAccess, + 0 + ); + + // + // Define the User ACEs + // + + if (UserSid != NULL) { + + SetMyAce(&(Ace[AceCount++]), + UserSid, + DESKTOP_ALL, + 0 + ); + } + + // Check we didn't goof + ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount); + + // + // Create the security descriptor + // + + SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount); + if (SecurityDescriptor == NULL) { + DebugLog((DEB_ERROR, "failed to create user desktop security descriptor\n")); + return(FALSE); + } + + // + // Set the DACL on the object + // + + si = DACL_SECURITY_INFORMATION; + Result = SetUserObjectSecurity(hdesktop, &si, SecurityDescriptor); + + // + // Free up the security descriptor + // + + DeleteSecurityDescriptor(SecurityDescriptor); + + // + // Return success status + // + + if (!Result) { + DebugLog((DEB_ERROR, "failed to set user desktop security\n")); + } + return(Result); +} + + +/***************************************************************************\ +* CreateLogonSid +* +* Creates a logon sid for a new logon. +* +* If LogonId is non NULL, on return the LUID that is part of the logon +* sid is returned here. +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +PSID +CreateLogonSid( + PLUID LogonId OPTIONAL + ) +{ + NTSTATUS Status; + ULONG Length; + PSID Sid; + LUID Luid; + + // + // Generate a locally unique id to include in the logon sid + // + + Status = NtAllocateLocallyUniqueId(&Luid); + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "Failed to create LUID, status = 0x%lx", Status)); + return(NULL); + } + + + // + // Allocate space for the sid and fill it in. + // + + Length = RtlLengthRequiredSid(SECURITY_LOGON_IDS_RID_COUNT); + + Sid = (PSID)Alloc(Length); + ASSERTMSG("Winlogon failed to allocate memory for logonsid", Sid != NULL); + + if (Sid != NULL) { + + RtlInitializeSid(Sid, &gSystemSidAuthority, SECURITY_LOGON_IDS_RID_COUNT); + + ASSERT(SECURITY_LOGON_IDS_RID_COUNT == 3); + + *(RtlSubAuthoritySid(Sid, 0)) = SECURITY_LOGON_IDS_RID; + *(RtlSubAuthoritySid(Sid, 1 )) = Luid.HighPart; + *(RtlSubAuthoritySid(Sid, 2 )) = Luid.LowPart; + } + + + // + // Return the logon LUID if required. + // + + if (LogonId != NULL) { + *LogonId = Luid; + } + + return(Sid); +} + + +/***************************************************************************\ +* DeleteLogonSid +* +* Frees up memory allocated for logon sid +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +VOID +DeleteLogonSid( + PSID Sid + ) +{ + Free(Sid); +} + + +/***************************************************************************\ +* InitializeSecurityGlobals +* +* Initializes the various global constants (mainly Sids used in this module. +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +VOID +InitializeSecurityGlobals( + VOID + ) +{ + NTSTATUS Status; + + // + // Initialize the local sid for later + // + + Status = RtlAllocateAndInitializeSid( + &gLocalSidAuthority, + 1, + SECURITY_LOCAL_RID, + 0, 0, 0, 0, 0, 0, 0, + &gLocalSid + ); + + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "Failed to initialize local sid, status = 0x%lx", Status)); + } + + // + // Initialize the admin sid for later + // + + Status = RtlAllocateAndInitializeSid( + &gSystemSidAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &gAdminSid + ); + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "Failed to initialize admin alias sid, status = 0x%lx", Status)); + } + +} + + +/***************************************************************************\ +* EnablePrivilege +* +* Enables/disables the specified well-known privilege in the current thread +* token if there is one, otherwise the current process token. +* +* Returns TRUE on success, FALSE on failure +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +BOOL +EnablePrivilege( + ULONG Privilege, + BOOL Enable + ) +{ + NTSTATUS Status; + BOOLEAN WasEnabled; + + // + // Try the thread token first + // + + Status = RtlAdjustPrivilege(Privilege, + (BOOLEAN)Enable, + TRUE, + &WasEnabled); + + if (Status == STATUS_NO_TOKEN) { + + // + // No thread token, use the process token + // + + Status = RtlAdjustPrivilege(Privilege, + (BOOLEAN)Enable, + FALSE, + &WasEnabled); + } + + + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "Failed to %ws privilege : 0x%lx, status = 0x%lx", Enable ? TEXT("enable") : TEXT("disable"), Privilege, Status)); + return(FALSE); + } + + return(TRUE); +} + + +/***************************************************************************\ +* ClearUserProcessData +* +* Resets fields in user process data. Should be used at startup when structure +* contents are unknown. +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +VOID +ClearUserProcessData( + PUSER_PROCESS_DATA UserProcessData + ) +{ + UserProcessData->UserToken = NULL; + UserProcessData->UserSid = NULL; + UserProcessData->NewProcessSD = NULL; + UserProcessData->NewProcessTokenSD = NULL; + UserProcessData->NewThreadSD = NULL; + UserProcessData->NewThreadTokenSD = NULL; + + // + // Use the PagedPoolLimit field as an indication as to whether + // any of the quota fields have been set. A zero PagedPoolLimit + // is not legit, and so makes for a greate indicator. + // + + UserProcessData->Quotas.PagedPoolLimit = 0; + + // + // the following two fields will be set by MOAP. + // + + UserProcessData->CurrentDirectory = NULL; + UserProcessData->pEnvironment = NULL; +} + + +/***************************************************************************\ +* SetUserProcessData +* +* Sets up the user process data structure for a new user. +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +BOOL +SetUserProcessData( + PUSER_PROCESS_DATA UserProcessData, + HANDLE UserToken, + PQUOTA_LIMITS Quotas OPTIONAL, + PSID UserSid, + PSID WinlogonSid + ) +{ + NTSTATUS Status; + + // + // Free an existing UserSid + // + if (UserProcessData->UserSid != NULL) { + // + // Don't free winlogon sid if this was a system logon (or no logon) + // + if (UserProcessData->UserSid != WinlogonSid) { + DeleteLogonSid(UserProcessData->UserSid); + } + UserProcessData->UserSid = NULL; + } + + // + // Free up the logon token + // + + if (UserProcessData->UserToken != NULL) { + Status = NtClose(UserProcessData->UserToken); + ASSERT(NT_SUCCESS(Status)); + UserProcessData->UserToken = NULL; + } + + // + // Free up any existing security descriptors + // + if (UserProcessData->NewProcessSD != NULL) { + DeleteSecurityDescriptor(UserProcessData->NewProcessSD); + } + if (UserProcessData->NewProcessTokenSD != NULL) { + DeleteSecurityDescriptor(UserProcessData->NewProcessTokenSD); + } + if (UserProcessData->NewThreadSD != NULL) { + DeleteSecurityDescriptor(UserProcessData->NewThreadSD); + } + if (UserProcessData->NewThreadTokenSD != NULL) { + DeleteSecurityDescriptor(UserProcessData->NewThreadTokenSD); + } + + // + // Store the new user's token and sid + // + + ASSERT(UserSid != NULL); // should always have a non-NULL user sid + + UserProcessData->UserToken = UserToken; + UserProcessData->UserSid = UserSid; + + // + // Save the user's quota limits + // + + if (ARGUMENT_PRESENT(Quotas)) { + UserProcessData->Quotas = (*Quotas); + } + + + // + // Set up new security descriptors + // +#if 0 + UserProcessData->NewProcessSD = CreateUserProcessSD( + UserSid, + WinlogonSid); + + ASSERT(UserProcessData->NewProcessSD != NULL); + + UserProcessData->NewProcessTokenSD = CreateUserProcessTokenSD( + UserSid, + WinlogonSid); + + ASSERT(UserProcessData->NewProcessTokenSD != NULL); +#endif + UserProcessData->NewThreadSD = CreateUserThreadSD( + UserSid, + WinlogonSid); + + ASSERT(UserProcessData->NewThreadSD != NULL); + + UserProcessData->NewThreadTokenSD = CreateUserThreadTokenSD( + UserSid, + WinlogonSid); + + ASSERT(UserProcessData->NewThreadTokenSD != NULL); + + return(TRUE); +} + + +/***************************************************************************\ +* FUNCTION: SecurityChangeUser +* +* PURPOSE: Sets up any security information for the new user. +* This should be called whenever a user logs on or off. +* UserLoggedOn should be set to indicate winlogon state, i.e. +* TRUE if a real user is logged on, FALSE if this call is setting +* our user back to system. (Note that UserToken and Sid may be +* the winlogon token/sid on DBG machines where we allow system logon) +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 12-09-91 Davidc Created. +* +\***************************************************************************/ + +BOOL +SecurityChangeUser( + PGLOBALS pGlobals, + HANDLE Token, + PQUOTA_LIMITS Quotas OPTIONAL, + PSID LogonSid, + BOOL UserLoggedOn + ) +{ + LUID luidNone = { 0, 0 }; + + // + // Set appropriate protection on windows objects + // + + if ( UserLoggedOn || pGlobals->fExecuteSetup ) + { + AddUserToWinsta( &pGlobals->WindowStation, + LogonSid, + Token ); + + } + else + { + RemoveUserFromWinsta( &pGlobals->WindowStation, + pGlobals->UserProcessData.UserToken ); + } + +#if 0 + SetWindowStationSecurity(pGlobals, + (UserLoggedOn || pGlobals->fExecuteSetup) ? LogonSid : NULL); +#endif + + SetUserDesktopSecurity( pGlobals->WindowStation.hdeskApplication, + LogonSid, + pWinlogonSid); + + // + // Setup new-process data + // + + SetUserProcessData(&pGlobals->UserProcessData, + Token, + Quotas, + LogonSid, + pWinlogonSid); + + // + // Setup the appropriate new environment + // + + if (UserLoggedOn) { + + // + // Do nothing to the profile or environment. Environment and profiles + // are all handled in wlx.c:LogonAttempt and DoStartShell + // + + pGlobals->LogoffFlags = 0; + pGlobals->TickCount = GetTickCount(); + + } else { + + // + // Restore the system environment + // + + CloseIniFileUserMapping(pGlobals); + + ResetEnvironment(pGlobals); + + SetWindowStationUser(pGlobals->WindowStation.hwinsta, &luidNone, NULL, 0); + + } + + + // + // Store whether there is a real user logged on or not + // + + pGlobals->UserLoggedOn = UserLoggedOn; + + return(TRUE); +} + + +/***************************************************************************\ +* CreateSecurityDescriptor +* +* Creates a security descriptor containing an ACL containing the specified ACEs +* +* A SD created with this routine should be destroyed using +* DeleteSecurityDescriptor +* +* Returns a pointer to the security descriptor or NULL on failure. +* +* 02-06-92 Davidc Created. +\***************************************************************************/ + +PSECURITY_DESCRIPTOR +CreateSecurityDescriptor( + PMYACE MyAce, + ACEINDEX AceCount + ) +{ + NTSTATUS Status; + ACEINDEX AceIndex; + PACCESS_ALLOWED_ACE *Ace; + PACL Acl = NULL; + PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; + ULONG LengthAces; + ULONG LengthAcl; + ULONG LengthSd; + + // + // Allocate space for the ACE pointer array + // + + Ace = (PACCESS_ALLOWED_ACE *)Alloc(sizeof(PACCESS_ALLOWED_ACE) * AceCount); + if (Ace == NULL) { + DebugLog((DEB_ERROR, "Failed to allocated ACE array\n")); + return(NULL); + } + + // + // Create the ACEs and calculate total ACE size + // + + LengthAces = 0; + for (AceIndex=0; AceIndex < AceCount; AceIndex ++) { + Ace[AceIndex] = CreateAccessAllowedAce(MyAce[AceIndex].Sid, + MyAce[AceIndex].AccessMask, + 0, + MyAce[AceIndex].InheritFlags); + if (Ace[AceIndex] == NULL) { + DebugLog((DEB_ERROR, "Failed to allocate ace\n")); + } else { + LengthAces += Ace[AceIndex]->Header.AceSize; + } + } + + // + // Calculate ACL and SD sizes + // + + LengthAcl = sizeof(ACL) + LengthAces; + LengthSd = SECURITY_DESCRIPTOR_MIN_LENGTH; + + // + // Create the ACL + // + + Acl = Alloc(LengthAcl); + + if (Acl != NULL) { + + Status = RtlCreateAcl(Acl, LengthAcl, ACL_REVISION); + ASSERT(NT_SUCCESS(Status)); + + // + // Add the ACES to the ACL and destroy the ACEs + // + + for (AceIndex = 0; AceIndex < AceCount; AceIndex ++) { + + if (Ace[AceIndex] != NULL) { + + Status = RtlAddAce(Acl, ACL_REVISION, 0, Ace[AceIndex], + Ace[AceIndex]->Header.AceSize); + + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "AddAce failed, status = 0x%lx", Status)); + } + + DestroyAce(Ace[AceIndex]); + } + } + + } else { + DebugLog((DEB_ERROR, "Failed to allocate ACL\n")); + } + + // + // Free the ACE pointer array + // + Free(Ace); + + // + // Create the security descriptor + // + + SecurityDescriptor = Alloc(LengthSd); + + if (SecurityDescriptor != NULL) { + + Status = RtlCreateSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION); + ASSERT(NT_SUCCESS(Status)); + + // + // Set the DACL on the security descriptor + // + Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Acl, FALSE); + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "SetDACLSD failed, status = 0x%lx", Status)); + } + } else { + DebugLog((DEB_ERROR, "Failed to allocate security descriptor\n")); + } + + // + // Return with our spoils + // + return(SecurityDescriptor); +} + + +/***************************************************************************\ +* DeleteSecurityDescriptor +* +* Deletes a security descriptor created using CreateSecurityDescriptor +* +* Returns TRUE on success, FALSE on failure +* +* 02-06-92 Davidc Created. +\***************************************************************************/ + +BOOL +DeleteSecurityDescriptor( + PSECURITY_DESCRIPTOR SecurityDescriptor + ) +{ + NTSTATUS Status; + PACL Acl; + BOOLEAN Present; + BOOLEAN Defaulted; + + ASSERT(SecurityDescriptor != NULL); + + // + // Get the ACL + // + Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor, + &Present, &Acl, &Defaulted); + if (NT_SUCCESS(Status)) { + + // + // Destroy the ACL + // + if (Present && (Acl != NULL)) { + Free(Acl); + } + } else { + DebugLog((DEB_ERROR, "Failed to get DACL from security descriptor being destroyed, Status = 0x%lx", Status)); + } + + // + // Destroy the Security Descriptor + // + Free(SecurityDescriptor); + + return(TRUE); +} + + +/***************************************************************************\ +* CreateAccessAllowedAce +* +* Allocates memory for an ACCESS_ALLOWED_ACE and fills it in. +* The memory should be freed by calling DestroyACE. +* +* Returns pointer to ACE on success, NULL on failure +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +PVOID +CreateAccessAllowedAce( + PSID Sid, + ACCESS_MASK AccessMask, + UCHAR AceFlags, + UCHAR InheritFlags + ) +{ + ULONG LengthSid = RtlLengthSid(Sid); + ULONG LengthACE = sizeof(ACE_HEADER) + sizeof(ACCESS_MASK) + LengthSid; + PACCESS_ALLOWED_ACE Ace; + + Ace = (PACCESS_ALLOWED_ACE)Alloc(LengthACE); + if (Ace == NULL) { + DebugLog((DEB_ERROR, "CreateAccessAllowedAce : Failed to allocate ace\n")); + return NULL; + } + + Ace->Header.AceType = ACCESS_ALLOWED_ACE_TYPE; + Ace->Header.AceSize = (UCHAR)LengthACE; + Ace->Header.AceFlags = AceFlags | InheritFlags; + Ace->Mask = AccessMask; + RtlCopySid(LengthSid, (PSID)(&(Ace->SidStart)), Sid ); + + return(Ace); +} + + +/***************************************************************************\ +* DestroyAce +* +* Frees the memory allocate for an ACE +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +VOID +DestroyAce( + PVOID Ace + ) +{ + Free(Ace); +} + + +/***************************************************************************\ +* FUNCTION: ImpersonateUser +* +* PURPOSE: Impersonates the user by setting the users token +* on the specified thread. If no thread is specified the token +* is set on the current thread. +* +* RETURNS: Handle to be used on call to StopImpersonating() or NULL on failure +* If a non-null thread handle was passed in, the handle returned will +* be the one passed in. (See note) +* +* NOTES: Take care when passing in a thread handle and then calling +* StopImpersonating() with the handle returned by this routine. +* StopImpersonating() will close any thread handle passed to it - +* even yours ! +* +* HISTORY: +* +* 04-21-92 Davidc Created. +* +\***************************************************************************/ + +HANDLE +ImpersonateUser( + PUSER_PROCESS_DATA UserProcessData, + HANDLE ThreadHandle + ) +{ + NTSTATUS Status, IgnoreStatus; + HANDLE UserToken = UserProcessData->UserToken; + SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE ImpersonationToken; + BOOL ThreadHandleOpened = FALSE; + + if (ThreadHandle == NULL) { + + // + // Get a handle to the current thread. + // Once we have this handle, we can set the user's impersonation + // token into the thread and remove it later even though we ARE + // the user for the removal operation. This is because the handle + // contains the access rights - the access is not re-evaluated + // at token removal time. + // + + Status = NtDuplicateObject( NtCurrentProcess(), // Source process + NtCurrentThread(), // Source handle + NtCurrentProcess(), // Target process + &ThreadHandle, // Target handle + THREAD_SET_THREAD_TOKEN,// Access + 0L, // Attributes + DUPLICATE_SAME_ATTRIBUTES + ); + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "ImpersonateUser : Failed to duplicate thread handle, status = 0x%lx", Status)); + return(NULL); + } + + ThreadHandleOpened = TRUE; + } + + + // + // If the usertoken is NULL, there's nothing to do + // + + if (UserToken != NULL) { + + // + // UserToken is a primary token - create an impersonation token version + // of it so we can set it on our thread + // + + InitializeObjectAttributes( + &ObjectAttributes, + NULL, + 0L, + NULL, + UserProcessData->NewThreadTokenSD); + + SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation; + SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + SecurityQualityOfService.EffectiveOnly = FALSE; + + ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService; + + + Status = NtDuplicateToken( UserToken, + TOKEN_IMPERSONATE | TOKEN_READ, + &ObjectAttributes, + FALSE, + TokenImpersonation, + &ImpersonationToken + ); + if (!NT_SUCCESS(Status)) { + + DebugLog((DEB_ERROR, "Failed to duplicate users token to create impersonation thread, status = 0x%lx", Status)); + + if (ThreadHandleOpened) { + IgnoreStatus = NtClose(ThreadHandle); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + return(NULL); + } + + + + // + // Set the impersonation token on this thread so we 'are' the user + // + + Status = NtSetInformationThread( ThreadHandle, + ThreadImpersonationToken, + (PVOID)&ImpersonationToken, + sizeof(ImpersonationToken) + ); + // + // We're finished with our handle to the impersonation token + // + + IgnoreStatus = NtClose(ImpersonationToken); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + // + // Check we set the token on our thread ok + // + + if (!NT_SUCCESS(Status)) { + + DebugLog((DEB_ERROR, "Failed to set user impersonation token on winlogon thread, status = 0x%lx", Status)); + + if (ThreadHandleOpened) { + IgnoreStatus = NtClose(ThreadHandle); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + return(NULL); + } + + } + + + return(ThreadHandle); + +} + + +/***************************************************************************\ +* FUNCTION: StopImpersonating +* +* PURPOSE: Stops impersonating the client by removing the token on the +* current thread. +* +* PARAMETERS: ThreadHandle - handle returned by ImpersonateUser() call. +* +* RETURNS: TRUE on success, FALSE on failure +* +* NOTES: If a thread handle was passed in to ImpersonateUser() then the +* handle returned was one and the same. If this is passed to +* StopImpersonating() the handle will be closed. Take care ! +* +* HISTORY: +* +* 04-21-92 Davidc Created. +* +\***************************************************************************/ + +BOOL +StopImpersonating( + HANDLE ThreadHandle + ) +{ + NTSTATUS Status, IgnoreStatus; + HANDLE ImpersonationToken; + + + // + // Remove the user's token from our thread so we are 'ourself' again + // + + ImpersonationToken = NULL; + + Status = NtSetInformationThread( ThreadHandle, + ThreadImpersonationToken, + (PVOID)&ImpersonationToken, + sizeof(ImpersonationToken) + ); + // + // We're finished with the thread handle + // + + IgnoreStatus = NtClose(ThreadHandle); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "Failed to remove user impersonation token from winlogon thread, status = 0x%lx", Status)); + } + + return(NT_SUCCESS(Status)); +} + + +/***************************************************************************\ +* ExecUserThread +* +* Creates a thread of the winlogon process running in the logged on user's +* context. +* +* Returns thread handle on success, NULL on failure. +* +* Thread handle returned has all access to thread. +* +* 05-04-92 Davidc Created. +\***************************************************************************/ + +HANDLE ExecUserThread( + IN PGLOBALS pGlobals, + IN LPTHREAD_START_ROUTINE lpStartAddress, + IN LPVOID Parameter, + IN DWORD Flags, + OUT LPDWORD ThreadId + ) +{ + SECURITY_ATTRIBUTES saThread; + PUSER_PROCESS_DATA UserProcessData = &pGlobals->UserProcessData; + HANDLE ThreadHandle, Handle; + BOOL Result = FALSE; + DWORD ResumeResult, IgnoreResult; + + // + // Initialize thread security info + // + + saThread.nLength = sizeof(SECURITY_ATTRIBUTES); + saThread.lpSecurityDescriptor = UserProcessData->NewThreadSD; + saThread.bInheritHandle = FALSE; + + // + // Create the thread suspended + // + + ThreadHandle = CreateThread( + &saThread, + 0, // Default Stack size + lpStartAddress, + Parameter, + CREATE_SUSPENDED | Flags, + ThreadId); + + if (ThreadHandle == NULL) { + DebugLog((DEB_ERROR, "User thread creation failed! Error = %d\n", GetLastError())); + return(NULL); + } + + + // + // Switch the thread to user context. + // + + Handle = ImpersonateUser(UserProcessData, ThreadHandle); + + if (Handle == NULL) { + + DebugLog((DEB_ERROR, "Failed to set user context on thread!\n")); + + } else { + + // + // Should have got back the handle we passed in + // + + ASSERT(Handle == ThreadHandle); + + // + // Let the thread run + // + + ResumeResult = ResumeThread(ThreadHandle); + + if (ResumeResult == -1) { + DebugLog((DEB_ERROR, "failed to resume thread, error = %d", GetLastError())); + + } else { + + // + // Success + // + + Result = TRUE; + + } + } + + + + if (!Result) { + + // + // Terminate the thread + // + + IgnoreResult = TerminateThread(ThreadHandle, 0); + ASSERT(IgnoreResult); + + // + // Close the thread handle + // + + IgnoreResult = CloseHandle(ThreadHandle); + ASSERT(IgnoreResult); + + ThreadHandle = NULL; + } + + + return(ThreadHandle); +} + + + +/***************************************************************************\ +* CreateUserProfileKeySD +* +* Creates a security descriptor to protect registry keys in the user profile +* +* History: +* 22-Dec-92 Davidc Created +* 04-May-93 Johannec added 3rd parameter for locked groups set in upedit.exe +\***************************************************************************/ +PSECURITY_DESCRIPTOR +CreateUserProfileKeySD( + PSID UserSid, + PSID WinlogonSid, + BOOL AllAccess + ) +{ + MYACE Ace[3]; + ACEINDEX AceCount = 0; + PSECURITY_DESCRIPTOR SecurityDescriptor; + + + ASSERT(UserSid != NULL); // should always have a non-null user sid + + // + // Define the User ACEs + // + + SetMyAce(&(Ace[AceCount++]), + UserSid, + AllAccess ? KEY_ALL_ACCESS : + KEY_ALL_ACCESS & ~(KEY_SET_VALUE | KEY_CREATE_SUB_KEY | DELETE), + OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE + ); + + // + // Define the Admin ACEs + // + + SetMyAce(&(Ace[AceCount++]), + gAdminSid, + KEY_ALL_ACCESS, + OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE + ); + + // + // Define the Winlogon ACEs + // + + SetMyAce(&(Ace[AceCount++]), + WinlogonSid, + KEY_ALL_ACCESS, + OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE + ); + + + // Check we didn't goof + ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount); + + // + // Create the security descriptor + // + + SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount); + if (SecurityDescriptor == NULL) { + DebugLog((DEB_ERROR, "failed to create user process security descriptor\n\n")); + } + + return(SecurityDescriptor); +} + +/***************************************************************************\ +* CreateUserProcessTokenSD +* +* Creates a security descriptor to protect primary tokens on user processes +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +PSECURITY_DESCRIPTOR +CreateUserProcessTokenSD( + PSID UserSid, + PSID WinlogonSid + ) +{ + MYACE Ace[2]; + ACEINDEX AceCount = 0; + PSECURITY_DESCRIPTOR SecurityDescriptor; + + ASSERT(UserSid != NULL); // should always have a non-null user sid + + // + // Define the User ACEs + // + + SetMyAce(&(Ace[AceCount++]), + UserSid, + TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | + TOKEN_ADJUST_DEFAULT | TOKEN_QUERY | + TOKEN_DUPLICATE | TOKEN_IMPERSONATE | READ_CONTROL, + 0 + ); + + // + // Define the Winlogon ACEs + // + + SetMyAce(&(Ace[AceCount++]), + WinlogonSid, + TOKEN_READ, + 0 + ); + + // Check we didn't goof + ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount); + + // + // Create the security descriptor + // + + SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount); + if (SecurityDescriptor == NULL) { + DebugLog((DEB_ERROR, "failed to create user process token security descriptor")); + } + + return(SecurityDescriptor); + + DBG_UNREFERENCED_PARAMETER(WinlogonSid); +} + +/***************************************************************************\ +* CreateUserProcessSD +* +* Creates a security descriptor to protect user processes +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +PSECURITY_DESCRIPTOR +CreateUserProcessSD( + PSID UserSid, + PSID WinlogonSid + ) +{ + MYACE Ace[2]; + ACEINDEX AceCount = 0; + PSECURITY_DESCRIPTOR SecurityDescriptor; + + ASSERT(UserSid != NULL); // should always have a non-null user sid + + // + // Define the Winlogon ACEs + // + + SetMyAce(&(Ace[AceCount++]), + WinlogonSid, + PROCESS_SET_INFORMATION | // Allow primary token to be set + PROCESS_TERMINATE | SYNCHRONIZE, // Allow screen-saver control + 0 + ); + + // + // Define the User ACEs + // + + SetMyAce(&(Ace[AceCount++]), + UserSid, + PROCESS_ALL_ACCESS, + 0 + ); + + // Check we didn't goof + ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount); + + // + // Create the security descriptor + // + + SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount); + if (SecurityDescriptor == NULL) { + DebugLog((DEB_ERROR, "failed to create user process security descriptor")); + } + + return(SecurityDescriptor); +} + + +/***************************************************************************\ +* TestTokenForAdmin +* +* Returns TRUE if the token passed represents an admin user, otherwise FALSE +* +* The token handle passed must have TOKEN_QUERY access. +* +* History: +* 05-06-92 Davidc Created +\***************************************************************************/ +BOOL +TestTokenForAdmin( + HANDLE Token + ) +{ + NTSTATUS Status; + ULONG InfoLength; + PTOKEN_GROUPS TokenGroupList; + ULONG GroupIndex; + BOOL FoundAdmin; + + // + // Get a list of groups in the token + // + + Status = NtQueryInformationToken( + Token, // Handle + TokenGroups, // TokenInformationClass + NULL, // TokenInformation + 0, // TokenInformationLength + &InfoLength // ReturnLength + ); + + if ((Status != STATUS_SUCCESS) && (Status != STATUS_BUFFER_TOO_SMALL)) { + + DebugLog((DEB_ERROR, "failed to get group info for admin token, status = 0x%lx\n", Status)); + return(FALSE); + } + + + TokenGroupList = Alloc(InfoLength); + + if (TokenGroupList == NULL) { + DebugLog((DEB_ERROR, "unable to allocate memory for token groups\n")); + return(FALSE); + } + + Status = NtQueryInformationToken( + Token, // Handle + TokenGroups, // TokenInformationClass + TokenGroupList, // TokenInformation + InfoLength, // TokenInformationLength + &InfoLength // ReturnLength + ); + + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "failed to query groups for admin token, status = 0x%lx\n", Status)); + Free(TokenGroupList); + return(FALSE); + } + + + // + // Search group list for admin alias + // + + FoundAdmin = FALSE; + + for (GroupIndex=0; GroupIndex < TokenGroupList->GroupCount; GroupIndex++ ) { + + if (RtlEqualSid(TokenGroupList->Groups[GroupIndex].Sid, gAdminSid)) { + FoundAdmin = TRUE; + break; + } + } + + // + // Tidy up + // + + Free(TokenGroupList); + + + + return(FoundAdmin); +} + + +/***************************************************************************\ +* SetProcessToken +* +* Set the primary token of the specified process +* If the specified token is NULL, this routine does nothing. +* +* It assumed that the handles in ProcessInformation are the handles returned +* on creation of the process and therefore have all access. +* +* Returns TRUE on success, FALSE on failure. +* +* 01-31-91 Davidc Created. +\***************************************************************************/ + +BOOL +SetProcessToken( + PGLOBALS pGlobals, + HANDLE hProcess, + HANDLE hThread, + HANDLE hToken + ) +{ + NTSTATUS Status, AdjustStatus; + PROCESS_ACCESS_TOKEN PrimaryTokenInfo; + HANDLE TokenToAssign; + OBJECT_ATTRIBUTES ObjectAttributes; + BOOLEAN WasEnabled; + PSECURITY_DESCRIPTOR psd; + + + // + // Check for a NULL token. (No need to do anything) + // The process will run in the parent process's context and inherit + // the default ACL from the parent process's token. + // + if (hToken == NULL) + { + return(TRUE); + } + + psd = CreateUserProcessTokenSD( pGlobals->UserProcessData.UserSid, + pGlobals->WinlogonSid ); + + // + // A primary token can only be assigned to one process. + // Duplicate the logon token so we can assign one to the new + // process. + // + + InitializeObjectAttributes( + &ObjectAttributes, + NULL, + 0, + NULL, + psd + ); + + Status = NtDuplicateToken( + hToken, // Duplicate this token + 0, // Same desired access + &ObjectAttributes, + FALSE, // EffectiveOnly + TokenPrimary, // TokenType + &TokenToAssign // Duplicate token handle stored here + ); + + DeleteSecurityDescriptor(psd); + + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "SetProcessToken failed to duplicate primary token for new user process, status = 0x%lx\n", Status)); + return(FALSE); + } + + // + // Set the process's primary token + // + + + // + // Enable the required privilege + // + + Status = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, + FALSE, &WasEnabled); + if (NT_SUCCESS(Status)) { + + PrimaryTokenInfo.Token = TokenToAssign; + PrimaryTokenInfo.Thread = hThread; + + Status = NtSetInformationProcess( + hProcess, + ProcessAccessToken, + (PVOID)&PrimaryTokenInfo, + (ULONG)sizeof(PROCESS_ACCESS_TOKEN) + ); + // + // Restore the privilege to its previous state + // + + AdjustStatus = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, + WasEnabled, FALSE, &WasEnabled); + if (!NT_SUCCESS(AdjustStatus)) { + DebugLog((DEB_ERROR, "failed to restore assign-primary-token privilege to previous enabled state\n")); + } + + if (NT_SUCCESS(Status)) { + Status = AdjustStatus; + } + } else { + DebugLog((DEB_ERROR, "failed to enable assign-primary-token privilege\n")); + } + + // + // We're finished with the token handle + // + + CloseHandle(TokenToAssign); + + + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "SetProcessToken failed to set primary token for new user process, Status = 0x%lx\n", Status)); + SetLastError(RtlNtStatusToDosError(Status)); + } + + return (NT_SUCCESS(Status)); +} + + +/***************************************************************************\ +* CreateUserThreadSD +* +* Creates a security descriptor to protect user threads in the winlogon process +* +* History: +* 05-04-92 Davidc Created +\***************************************************************************/ +PSECURITY_DESCRIPTOR +CreateUserThreadSD( + PSID UserSid, + PSID WinlogonSid + ) +{ + MYACE Ace[2]; + ACEINDEX AceCount = 0; + PSECURITY_DESCRIPTOR SecurityDescriptor; + + ASSERT(UserSid != NULL); // should always have a non-null user sid + + // + // Define the Winlogon ACEs + // + + SetMyAce(&(Ace[AceCount++]), + WinlogonSid, + THREAD_QUERY_INFORMATION | + THREAD_SET_THREAD_TOKEN | + THREAD_SUSPEND_RESUME | + THREAD_TERMINATE | SYNCHRONIZE, + 0 + ); + + // + // Define the User ACEs + // + + SetMyAce(&(Ace[AceCount++]), + UserSid, + THREAD_GET_CONTEXT | + THREAD_QUERY_INFORMATION, + 0 + ); + + // Check we didn't goof + ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount); + + // + // Create the security descriptor + // + + SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount); + if (SecurityDescriptor == NULL) { + DebugLog((DEB_ERROR, "failed to create user process security descriptor\n")); + } + + return(SecurityDescriptor); +} + + +/***************************************************************************\ +* CreateUserThreadTokenSD +* +* Creates a security descriptor to protect tokens on user threads +* +* History: +* 12-05-91 Davidc Created +\***************************************************************************/ +PSECURITY_DESCRIPTOR +CreateUserThreadTokenSD( + PSID UserSid, + PSID WinlogonSid + ) +{ + MYACE Ace[2]; + ACEINDEX AceCount = 0; + PSECURITY_DESCRIPTOR SecurityDescriptor; + + ASSERT(UserSid != NULL); // should always have a non-null user sid + + // + // Define the User ACEs + // + + SetMyAce(&(Ace[AceCount++]), + UserSid, + TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | + TOKEN_ADJUST_DEFAULT | TOKEN_QUERY | + TOKEN_DUPLICATE | TOKEN_IMPERSONATE | READ_CONTROL, + 0 + ); + + // + // Define the Winlogon ACEs + // + + SetMyAce(&(Ace[AceCount++]), + WinlogonSid, + TOKEN_ALL_ACCESS, + 0 + ); + + // Check we didn't goof + ASSERT((sizeof(Ace) / sizeof(MYACE)) >= AceCount); + + // + // Create the security descriptor + // + + SecurityDescriptor = CreateSecurityDescriptor(Ace, AceCount); + if (SecurityDescriptor == NULL) { + DebugLog((DEB_ERROR, "failed to create user process token security descriptor\n")); + } + + return(SecurityDescriptor); + + DBG_UNREFERENCED_PARAMETER(WinlogonSid); +} + + +//+--------------------------------------------------------------------------- +// +// Function: CreateAceList +// +// Synopsis: Create and initialize the ACELIST to track access to the winsta +// +// Arguments: [Count] -- +// +// History: 6-24-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +PMYACELIST +CreateAceList( + DWORD Count) +{ + PMYACELIST pList; + + pList = LocalAlloc( LMEM_FIXED, sizeof( MYACELIST ) ); + + if ( pList ) + { + pList->Aces = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, + sizeof( MYACE ) * Count ); + + if ( pList->Aces ) + { + pList->Total = Count; + pList->Active = 0; + + pList->Sids = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, + sizeof( PSID ) * Count ); + + if ( pList->Sids ) + { + pList->TotalSids = Count; + pList->ActiveSids = 0; + + return( pList ); + } + + return( pList ); + } + + LocalFree( pList ); + } + + return( NULL ); +} + + +//+--------------------------------------------------------------------------- +// +// Function: AceListAddSid +// +// Synopsis: Adds a SID to the list where the ace list is maintained. +// +// Arguments: [pList] -- +// [Sid] -- +// +// History: 6-24-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +PSID +AceListAddSid( + PMYACELIST pList, + PSID Sid ) +{ + PVOID pCopy; + PSID SidCopy; + DWORD SidSize; + + if ( pList->ActiveSids == pList->TotalSids ) + { + pCopy = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, + sizeof( PSID ) * (pList->TotalSids * 2 ) ); + + if ( pCopy ) + { + CopyMemory( pCopy, pList->Sids, sizeof(PSID) * pList->ActiveSids ); + LocalFree( pList->Sids ); + pList->Sids = pCopy; + } + else + { + return( NULL ); + } + + } + + SidSize = RtlLengthSid( Sid ); + + SidCopy = LocalAlloc( LMEM_FIXED, SidSize ); + + if ( SidCopy ) + { + CopyMemory( SidCopy, Sid, SidSize ); + + pList->Sids[ pList->ActiveSids++ ] = SidCopy ; + + return( SidCopy ); + } + + return( NULL ); + +} + + +//+--------------------------------------------------------------------------- +// +// Function: AceListRemoveSid +// +// Synopsis: Removes and frees a SID from the list +// +// Arguments: [pList] -- List +// [Sid] -- Sid to remove +// [Absolute] -- TRUE -> pointer match, FALSE -> SID match +// +// History: 6-24-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +VOID +AceListRemoveSid( + PMYACELIST pList, + PSID Sid, + BOOL Absolute ) +{ + DWORD i; + PSID SidToDelete; + + for ( i = 0 ; i < pList->ActiveSids ; i++ ) + { + if ( Absolute ) + { + if ( pList->Sids[ i ] == Sid ) + { + break; + } + } + else + { + if ( RtlEqualSid( pList->Sids[i], Sid ) ) + { + break; + } + } + + } + + if ( i == pList->ActiveSids ) + { + return; + } + + SidToDelete = pList->Sids[ i ]; + + pList->ActiveSids --; + + pList->Sids[ i ] = pList->Sids[ pList->ActiveSids ]; + + LocalFree( SidToDelete ); + +} +//+--------------------------------------------------------------------------- +// +// Function: AceListSetWinstaSecurity +// +// Synopsis: Applies the ACL in pList to the window station +// +// Arguments: [pList] -- +// [hWinsta] -- +// +// History: 6-24-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL +AceListSetWinstaSecurity( + PMYACELIST pList, + DWORD Count, + HWINSTA hWinsta ) +{ + PSECURITY_DESCRIPTOR SecurityDescriptor; + SECURITY_INFORMATION si; + BOOL Result; + + // + // Create the security descriptor + // + + SecurityDescriptor = CreateSecurityDescriptor(pList->Aces, Count ); + if (SecurityDescriptor == NULL) { + DebugLog((DEB_ERROR, "failed to create winsta security descriptor\n")); + return(FALSE); + } + + // + // Set the DACL on the object + // + + si = DACL_SECURITY_INFORMATION; + Result = SetUserObjectSecurity(hWinsta, &si, SecurityDescriptor); + + // + // Free up the security descriptor + // + + DeleteSecurityDescriptor(SecurityDescriptor); + + // + // Return success status + // + + if (!Result) { + DebugLog((DEB_ERROR, "failed to set windowstation security\n")); + } + return(Result); +} + +//+--------------------------------------------------------------------------- +// +// Function: InitializeWinstaSecurity +// +// Synopsis: Initializes the window station security to winlogon/admin only +// +// Arguments: [pWinsta] -- +// +// History: 6-24-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL +InitializeWinstaSecurity( + PWinstaDescription pWinsta) +{ + PMYACELIST pList; + DWORD MiserlyAccess; + ACCESS_MASK WinstaAccess, DesktopAccess; + + pList = CreateAceList( 16 ); + + if ( !pList ) + { + return( FALSE ); + } + + pWinsta->Acl = pList; + + // + // Define the Winlogon ACEs + // + + SetMyAce(& ( pList->Aces[ pList->Active ++ ]), + pWinlogonSid, + WINSTA_ALL, + NO_PROPAGATE_INHERIT_ACE + ); + + SetMyAce(& ( pList->Aces[ pList->Active ++ ]), + pWinlogonSid, + GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL, + OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE + ); + + // + // Define the Admin ACEs + // + + MiserlyAccess = GetProfileInt( WINLOGON, RESTRICT_NONINTERACTIVE_ACCESS, 0 ); + + if ( MiserlyAccess ) + { + WinstaAccess = WINSTA_ENUMERATE | WINSTA_READATTRIBUTES ; + + DesktopAccess = DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | + DESKTOP_ENUMERATE | + DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU ; + } + else + { + WinstaAccess = WINSTA_ENUMERATE | WINSTA_READATTRIBUTES | + WINSTA_ATOMS | STANDARD_RIGHTS_EXECUTE | + WINSTA_EXITWINDOWS ; + + DesktopAccess = DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | + DESKTOP_ENUMERATE | + DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU | + GENERIC_EXECUTE ; + } + + SetMyAce(& ( pList->Aces[ pList->Active ++ ]), + gAdminSid, + WinstaAccess, + NO_PROPAGATE_INHERIT_ACE + ); + + SetMyAce(& ( pList->Aces[ pList->Active ++ ]), + gAdminSid, + DesktopAccess, + OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE + ); + + pList->WinlogonOnly = pList->Active ; + + return( AceListSetWinstaSecurity( pList, pList->Active, pWinsta->hwinsta ) ); + +} + +//+--------------------------------------------------------------------------- +// +// Function: AddUserToWinsta +// +// Synopsis: Adds the specified user to the window station +// +// Arguments: [pWinsta] -- +// [LogonSid] -- +// [Token] -- +// +// History: 6-24-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL +AddUserToWinsta( + PWinstaDescription pWinsta, + PSID LogonSid, + HANDLE Token ) +{ + PTOKEN_USER pUser; + UCHAR Buffer[128]; + PMYACELIST pList; + NTSTATUS Status; + ULONG Needed; + PSID LogonSidCopy; + PSID UserSidCopy; + + pList = pWinsta->Acl; + + pUser = (PTOKEN_USER) Buffer; + + Status = NtQueryInformationToken( Token, + TokenUser, + pUser, + sizeof(Buffer), + &Needed ); + + if ( !NT_SUCCESS( Status ) ) + { + return( FALSE ); + } + + + // + // Define the User ACEs + // + + LogonSidCopy = AceListAddSid( pList, LogonSid ); + + if ( !LogonSidCopy ) + { + return( FALSE ); + } + + UserSidCopy = AceListAddSid( pList, pUser->User.Sid ); + + if ( !UserSidCopy ) + { + AceListRemoveSid( pList, LogonSidCopy, TRUE ); + + return( FALSE ); + } + + SetMyAce( &pList->Aces[ pList->Active ++ ], + LogonSidCopy, + WINSTA_ALL, + NO_PROPAGATE_INHERIT_ACE ); + + SetMyAce( &pList->Aces[ pList->Active ++ ], + LogonSidCopy, + GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL, + OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE ); + + SetMyAce( &pList->Aces[ pList->Active ++ ], + UserSidCopy, + WINSTA_ATOMS, + NO_PROPAGATE_INHERIT_ACE ); + + + return( AceListSetWinstaSecurity( pList, pList->Active, pWinsta->hwinsta ) ); + +} + +//+--------------------------------------------------------------------------- +// +// Function: RemoveUserFromWinsta +// +// Synopsis: Removes a user from the window station +// +// Arguments: [pWinsta] -- +// [Token] -- +// +// History: 6-24-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL +RemoveUserFromWinsta( + PWinstaDescription pWinsta, + HANDLE Token ) +{ + DWORD i; + PTOKEN_USER pUser; + UCHAR Buffer[128]; + PMYACELIST pList; + NTSTATUS Status; + ULONG Needed; + + pList = pWinsta->Acl; + + if ( pList->Active == 0 ) + { + return( FALSE ); + } + + pUser = (PTOKEN_USER) Buffer; + + Status = NtQueryInformationToken( Token, + TokenUser, + pUser, + sizeof(Buffer), + &Needed ); + + if ( !NT_SUCCESS( Status ) ) + { + return( FALSE ); + } + + + for ( i = pList->Active - 1 ; i >= pList->WinlogonOnly ; i-- ) + { + if ( RtlEqualSid( pList->Aces[i].Sid, pUser->User.Sid ) ) + { + break; + } + } + + if ( i < pList->WinlogonOnly ) + { + return( FALSE ); + } + + // + // We add users in blocks of three, usually LogonSid, LogonSid, UserSid. + // Thus, we delete them in threes + // + + if ( i < 2 ) + { + return( FALSE ); + } + + // + // Clean up SIDs + // + + AceListRemoveSid( pList, pList->Aces[ i ].Sid, TRUE ); + + AceListRemoveSid( pList, pList->Aces[ i - 1 ].Sid, TRUE ); + + // + // If there are still more entries after this one, + // slide them down. + // + + if ( pList->Active > i + 1 ) + { + MoveMemory( &pList->Aces[ i - 2 ], + &pList->Aces[ i + 1 ], + (pList->Active - i - 1) * sizeof( MYACE ) ); + } + + pList->Active -= 3; + + return( AceListSetWinstaSecurity( pList, pList->Active, pWinsta->hwinsta ) ); + +} + +//+--------------------------------------------------------------------------- +// +// Function: FastSetWinstaSecurity +// +// Synopsis: Allows fast toggling between "normal" and winlogon only access +// +// Arguments: [pWinsta] -- +// [FullAccess] -- +// +// History: 6-24-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL +FastSetWinstaSecurity( + PWinstaDescription pWinsta, + BOOL FullAccess) +{ + PMYACELIST pList; + + pList = (PMYACELIST) pWinsta->Acl; + + if ( FullAccess ) + { + return( AceListSetWinstaSecurity( pList, + pList->Active, + pWinsta->hwinsta ) ); + + } + else + { + return( AceListSetWinstaSecurity( pList, + pList->WinlogonOnly, + pWinsta->hwinsta ) ); + } + +} diff --git a/private/windows/gina/winlogon/secutil.h b/private/windows/gina/winlogon/secutil.h new file mode 100644 index 000000000..e49628bd2 --- /dev/null +++ b/private/windows/gina/winlogon/secutil.h @@ -0,0 +1,216 @@ +/****************************** Module Header ******************************\ +* Module Name: security.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Define various winlogon security-related routines +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + + +extern PSID pWinlogonSid; + +// +// Types used by security descriptor helper routines +// + +typedef LONG ACEINDEX; +typedef ACEINDEX *PACEINDEX; + +typedef struct _MYACE { + PSID Sid; + ACCESS_MASK AccessMask; + UCHAR InheritFlags; +} MYACE; +typedef MYACE *PMYACE; + + +// +// Exported function prototypes +// + + +VOID +SetMyAce( + PMYACE MyAce, + PSID Sid, + ACCESS_MASK Mask, + UCHAR InheritFlags + ); + +PSECURITY_DESCRIPTOR +CreateSecurityDescriptor( + PMYACE MyAce, + ACEINDEX AceCount + ); + +BOOL +DeleteSecurityDescriptor( + PSECURITY_DESCRIPTOR SecurityDescriptor + ); + + + +BOOL +SetWindowStationSecurity( + IN PGLOBALS pGlobals, + IN PSID UserSid + ); + +BOOL +SetWinlogonDesktopSecurity( + IN HDESK hdesk, + IN PSID WinlogonSid + ); + +BOOL +SetUserDesktopSecurity( + IN HDESK hdesk, + IN PSID UserSid, + IN PSID WinlogonSid + ); + +BOOL +InitializeSecurity( + PGLOBALS pGlobals + ); + + +PSID +CreateLogonSid( + PLUID LogonId OPTIONAL + ); + +VOID +DeleteLogonSid( + PSID Sid + ); + +PSECURITY_DESCRIPTOR +CreateUserProfileKeySD( + PSID UserSid, + PSID WinlogonSid, + BOOL AllAccess + ); + +BOOL +EnablePrivilege( + ULONG Privilege, + BOOL Enable + ); + +VOID +ClearUserProcessData( + PUSER_PROCESS_DATA UserProcessData + ); + +BOOL +SetUserProcessData( + PUSER_PROCESS_DATA UserProcessData, + HANDLE UserToken, + PQUOTA_LIMITS Quotas OPTIONAL, + PSID UserSid, + PSID WinlogonSid + ); + +BOOL +SecurityChangeUser( + PGLOBALS pGlobals, + HANDLE Token, + PQUOTA_LIMITS Quotas OPTIONAL, + PSID LogonSid, + BOOL UserLoggedOn + ); + +BOOL +TestTokenForAdmin( + HANDLE Token + ); + +BOOL +TestUserForAdmin( + PGLOBALS pGlobals, + IN PWCHAR UserName, + IN PWCHAR Domain, + IN PUNICODE_STRING PasswordString + ); + +HANDLE +ImpersonateUser( + PUSER_PROCESS_DATA UserProcessData, + HANDLE ThreadHandle OPTIONAL + ); + +BOOL +StopImpersonating( + HANDLE ThreadHandle + ); + +BOOL +TestUserPrivilege( + PGLOBALS pGlobals, + ULONG Privilege + ); + +VOID +HidePassword( + PUCHAR Seed OPTIONAL, + PUNICODE_STRING Password + ); + + +VOID +RevealPassword( + PUNICODE_STRING HiddenPassword + ); + +VOID +ErasePassword( + PUNICODE_STRING Password + ); + +BOOL +SetProcessToken( + HANDLE hProcess, + HANDLE hThread, + PSECURITY_DESCRIPTOR psd, + HANDLE hToken + ); + +PSECURITY_DESCRIPTOR +CreateUserThreadSD( + PSID UserSid, + PSID WinlogonSid + ); + +PSECURITY_DESCRIPTOR +CreateUserThreadTokenSD( + PSID UserSid, + PSID WinlogonSid + ); + +HANDLE ExecUserThread( + IN PGLOBALS pGlobals, + IN LPTHREAD_START_ROUTINE lpStartAddress, + IN LPVOID Parameter, + IN DWORD Flags, + OUT LPDWORD ThreadId + ); + +BOOL +RemoveUserFromWinsta( + PWinstaDescription pWinsta, + HANDLE Token ); + +BOOL +AddUserToWinsta( + PWinstaDescription pWinsta, + PSID LogonSid, + HANDLE Token ); + +BOOL +FastSetWinstaSecurity( + PWinstaDescription pWinsta, + BOOL FullAccess); diff --git a/private/windows/gina/winlogon/setup.c b/private/windows/gina/winlogon/setup.c new file mode 100644 index 000000000..c746d1413 --- /dev/null +++ b/private/windows/gina/winlogon/setup.c @@ -0,0 +1,582 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + setuplgn.c + +Abstract: + + Routines for the special version of winlogon for Setup. + +Author: + + Ted Miller (tedm) 4-May-1992 + +Revision History: + +--*/ + + +#include "precomp.h" +#pragma hdrstop + +#if DEVL +BOOL bDebugSetup; +#endif + +// +// Handle to the event used by lsa to stall security initialization. +// + +HANDLE LsaStallEvent = NULL; + +// +// Thread Id of the main thread of setuplgn. +// + +DWORD MainThreadId; + + + +DWORD +WaiterThread( + PVOID hProcess + ); + + + + +VOID +CreateLsaStallEvent( + VOID + ) + +/*++ + +Routine Description: + + Create the event used by lsa to stall security initialization. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + OBJECT_ATTRIBUTES EventAttributes; + NTSTATUS Status; + UNICODE_STRING EventName; + HANDLE EventHandle; + + RtlInitUnicodeString(&EventName,TEXT("\\INSTALLATION_SECURITY_HOLD")); + InitializeObjectAttributes(&EventAttributes,&EventName,0,0,NULL); + + Status = NtCreateEvent( &EventHandle, + 0, + &EventAttributes, + NotificationEvent, + FALSE + ); + + if(NT_SUCCESS(Status)) { + LsaStallEvent = EventHandle; + } else { + DebugLog((DEB_ERROR, "Couldn't create lsa stall event (status = %lx)",Status)); + } +} + + +DWORD +CheckSetupType ( + VOID + ) +/*++ + +Routine Description: + + See if the value "SetupType" exists under the Winlogon key in WIN.INI; + return its value. Return SETUPTYPE_NONE if not found. + +Arguments: + + None. + +Return Value: + + SETUPTYPE_xxxx (see SETUPLGN.H). + +--*/ + +{ + DWORD SetupType = SETUPTYPE_NONE ; + HANDLE KeyHandle = OpenNtRegKey(KEYNAME_SETUP) ; + + if (KeyHandle) { + if ( ! ReadRegistry( KeyHandle, + VARNAME_SETUPTYPE, + REG_DWORD, + (TCHAR*) & SetupType, + & SetupType ) ) + SetupType = SETUPTYPE_NONE ; + NtClose(KeyHandle); + } + + return SetupType ; +} + +BOOL +SetSetupType ( + DWORD type + ) +/*++ + +Routine Description: + + Set the "SetupType" value in the Registry. + +Arguments: + + DWORD type (see SETUPLGN.H) + +Return Value: + + TRUE if operation successful. + +--*/ + +{ + char buffer[20]; + BOOL Result = FALSE ; + + HANDLE KeyHandle = OpenNtRegKey(KEYNAME_SETUP) ; + + if (KeyHandle) { + sprintf(buffer,"%ld", type); + Result = WriteRegistry( KeyHandle, + VARNAME_SETUPTYPE_A, + REG_DWORD, buffer, + sizeof type ); + NtClose(KeyHandle); + } + return Result ; +} + + +BOOL +AppendToSetupCommandLine( + LPSTR pszCommandArguments + ) +/*++ + +Routine Description: + + Append to the setup command line in the Registry. + +Arguments: + + LPSTR pszCommandArguments (e.g. " /t STF_COMPUTERNAME = MACHINENAME") + +Return Value: + + TRUE if operation successful. + +--*/ + +{ + TCHAR CmdLineBuffer[512]; + DWORD DataSize; + BOOL Result = FALSE ; + + HANDLE KeyHandle = OpenNtRegKey(KEYNAME_SETUP) ; + + if (KeyHandle) { + DataSize = sizeof(CmdLineBuffer); + Result = ReadRegistry( KeyHandle, + VARNAME_SETUPCMD, + REG_SZ, + CmdLineBuffer, + &DataSize + ); + if (Result) { + UNICODE_STRING UniString; + STRING String; + char szBuf[1024]; + + String.Buffer = szBuf; + String.MaximumLength = sizeof(szBuf); + RtlInitUnicodeString(&UniString, CmdLineBuffer); + RtlUnicodeStringToAnsiString(&String, &UniString, FALSE); + + RtlAppendAsciizToString(&String, pszCommandArguments); + String.Buffer[ String.Length ] = '\0'; + Result = WriteRegistry( KeyHandle, + VARNAME_SETUPCMD_A, + REG_SZ, + szBuf, + String.Length + ); + } + NtClose(KeyHandle); + } + return Result ; +} + + +VOID +ExecuteSetup( + PGLOBALS pGlobals + ) + +/*++ + +Routine Description: + + Execute setup.exe. The command line to be passed to setup is obtained + from HKEY_LOCAL_MACHINE\system\setup:cmdline. Wait for setup to complete + before returning. + +Arguments: + + pGlobals - global data structure + +Return Value: + + None. + +--*/ + +{ + TCHAR CmdLineBuffer[1024]; + TCHAR DebugCmdLineBuffer[1024]; + PWCHAR CmdLine; + USER_PROCESS_DATA UserProcessData; + PROCESS_INFORMATION ProcessInformation; + HANDLE hProcess; + HANDLE hThread; + ULONG ThreadId; + HKEY hKey; + ULONG Result; + ULONG DataSize; + ULONG ExitCode; + MSG Msg; + USEROBJECTFLAGS uof; + + // + // See if this is normal SETUP or special Net IDW setup + // during 2nd phase of triple boot; if Net IDW, alter + // WIN.INI the same way the WINLOGON.CMD script would. + // + if ( pGlobals->SetupType == SETUPTYPE_NETIDW ) { + + // + // Establish the proper shell for the first real user boot + // + // Until the new shell is fully integrated, read which shell is + // desired from the "DefaultShell" value, and copy it over. + + GetProfileString( APPNAME_WINLOGON, + TEXT("DefaultShell"), + TEXT("progman.exe"), + CmdLineBuffer, + sizeof(CmdLineBuffer)/sizeof(WCHAR) ); + + WriteProfileString( APPNAME_WINLOGON, + TEXT("DefaultShell"), + NULL ); + + WriteProfileString( APPNAME_WINLOGON, + VARNAME_SHELL, + CmdLineBuffer ); + + + } + + // + // Get the Setup command line from the registry + // + + if((Result = RegOpenKey( HKEY_LOCAL_MACHINE, + REGNAME_SETUP, + &hKey)) == NO_ERROR) { + + DataSize = sizeof(CmdLineBuffer); + + Result = RegQueryValueEx( hKey, + VARNAME_SETUPCMD, + NULL, + NULL, + (LPBYTE)CmdLineBuffer, + &DataSize + ); + + if(Result == NO_ERROR) { + // DebugLog((DEB_ERROR, "Setup cmd line is '%s'",CmdLineBuffer)); + } else { + DebugLog((DEB_ERROR, "error %u querying CmdLine value from \\system\\setup",Result)); + } + RegCloseKey(hKey); + + } else { + DebugLog((DEB_ERROR, "error %u opening \\system\\setup key for CmdLine (2)",Result)); + } + + // Alter "SetupType" to indicate setup is no long in progress. + + SetSetupType( SETUPTYPE_NONE ) ; + +#ifdef INIT_REGISTRY + // + // Dont do this if we want to boot an extra time as Administrator + // so we can run winlogon.cmd command script. + + if ( pGlobals->SetupType == SETUPTYPE_NETSRW ) { + WriteProfileString( APPNAME_WINLOGON, VARNAME_AUTOLOGON, TEXT("1") ); + } else { +#endif + // Delete "AutoAdminLogon" from WIN.INI if present + // except in the retail upgrade case. + + if(pGlobals->SetupType != SETUPTYPE_UPGRADE) { + WriteProfileString( APPNAME_WINLOGON, VARNAME_AUTOLOGON, NULL ); + } + +#ifdef INIT_REGISTRY + } +#endif + + + RtlZeroMemory(&UserProcessData,sizeof(UserProcessData)); + + // + // Make windowstation and desktop handles inheritable. + // + GetUserObjectInformation(pGlobals->WindowStation.hwinsta, UOI_FLAGS, &uof, sizeof(uof), NULL); + uof.fInherit = TRUE; + SetUserObjectInformation(pGlobals->WindowStation.hwinsta, UOI_FLAGS, &uof, sizeof(uof)); + GetUserObjectInformation(pGlobals->WindowStation.hdeskApplication, UOI_FLAGS, &uof, sizeof(uof), NULL); + uof.fInherit = TRUE; + SetUserObjectInformation(pGlobals->WindowStation.hdeskApplication, UOI_FLAGS, &uof, sizeof(uof)); + + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Application); + + CmdLine = CmdLineBuffer; +#if DEVL + if ( bDebugSetup ) { + wsprintf( DebugCmdLineBuffer, TEXT("ntsd -d %s%s"), + bDebugSetup == 2 ? TEXT("-g -G ") : TEXT(""), + CmdLine + ); + CmdLine = DebugCmdLineBuffer; + } +#endif + + if(StartSystemProcess( CmdLine, + TEXT("winsta0\\Default"), + 0, + 0, // Normal startup feedback + NULL, + FALSE, + &hProcess, + NULL) ) + { + if (hProcess) + { + + + // + // Create a second thread to wait on the setup process. + // When setup terminates, the second thread will send us + // a special message. When we receive the special message, + // exit the dispatch loop. + // + // Do this to allow us to respond to messages sent by the + // system, thus preventing the system from hanging. + // + + MainThreadId = GetCurrentThreadId(); + + hThread = CreateThread( NULL, + 0, + WaiterThread, + (LPVOID)hProcess, + 0, + &ThreadId + ); + if(hThread) { + + while(GetMessage(&Msg,NULL,0,0)) { + DispatchMessage(&Msg); + } + + CloseHandle(hThread); + GetExitCodeProcess(hProcess,&ExitCode); + + // BUGBUG look at exit code; may have to restart machine. + + } else { + DebugLog((DEB_ERROR, "couldn't start waiter thread")); + } + + CloseHandle(hProcess); + + } else { + DebugLog((DEB_ERROR, "couldn't get handle to setup process, error = %u",GetLastError())); + } + } else { + DebugLog((DEB_ERROR, "couldn't exec '%ws'",CmdLine)); + } + + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon); + +} + + +DWORD +WaiterThread( + PVOID hProcess + ) +{ + WaitForSingleObject(hProcess,(DWORD)(-1)); + + PostThreadMessage(MainThreadId,WM_QUIT,0,0); + + ExitThread(0); + + return(0); // prevent compiler warning +} + + + +VOID +CheckForIncompleteSetup ( + PGLOBALS pGlobals + ) +/*++ + +Routine Description: + + Checks to see if setup started but never completed. + Do this by checking to see if the SetupInProgress value has been + reset to 0. This value is set to 1 in the default hives that run setup. + Setup.exe resets it to 0 on successful completion. + +Arguments: + + None. + +Return Value: + + None + +--*/ + +{ + DWORD SetupInProgress = 0 ; + HANDLE KeyHandle = OpenNtRegKey(KEYNAME_SETUP) ; + + if (KeyHandle) { + if ( ! ReadRegistry( KeyHandle, + VARNAME_SETUPINPROGRESS, + REG_DWORD, + (TCHAR*) & SetupInProgress, + & SetupInProgress ) ) { + + SetupInProgress = 0; + } + + NtClose(KeyHandle); + } + + + // + // If setup did not complete then make them reboot + // + + if (SetupInProgress) { + + TimeoutMessageBox(pGlobals, + NULL, + IDS_SETUP_INCOMPLETE, + IDS_WINDOWS_MESSAGE, + MB_ICONSTOP | MB_OK + ); + +#if DBG + // + // On debug builds let them continue if they hold down Ctrl + // + + if ((GetKeyState(VK_LCONTROL) < 0) || + (GetKeyState(VK_RCONTROL) < 0)) { + + return; + } +#endif + + // + // Reboot time + // + + RebootMachine(pGlobals); + + } +} + + +// +// This function checks if the "Repair" value is set. If so, then +// it loads syssetup.dll and calls RepairStartMenuItems +// + +VOID +CheckForRepairRequest (void) +{ + HKEY hkeyWinlogon; + LONG lResult; + DWORD dwSize, dwType; + BOOL bRunRepair = FALSE; + HINSTANCE hSysSetup; + REPAIRSTARTMENUITEMS RepairStartMenuItems; + + + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, WINLOGON_KEY, + 0, KEY_READ | KEY_WRITE, &hkeyWinlogon) == ERROR_SUCCESS) { + + dwSize = sizeof(bRunRepair); + + if (RegQueryValueEx (hkeyWinlogon, L"Repair", + NULL, &dwType, (LPBYTE) &bRunRepair, + &dwSize) == ERROR_SUCCESS) { + + RegDeleteValue (hkeyWinlogon, L"Repair"); + } + + RegCloseKey (hkeyWinlogon); + } + + + if (bRunRepair) { + + hSysSetup = LoadLibrary (L"syssetup.dll"); + + if (hSysSetup) { + + RepairStartMenuItems = (REPAIRSTARTMENUITEMS)GetProcAddress(hSysSetup, + "RepairStartMenuItems"); + + if (RepairStartMenuItems) { + RepairStartMenuItems(); + } + + FreeLibrary (hSysSetup); + } + } + +} diff --git a/private/windows/gina/winlogon/setup.h b/private/windows/gina/winlogon/setup.h new file mode 100644 index 000000000..a13379c8d --- /dev/null +++ b/private/windows/gina/winlogon/setup.h @@ -0,0 +1,102 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + setuplgn.h + +Abstract: + + Private header file for the special version of winlogon for Setup. + +Author: + + Ted Miller (tedm) 4-May-1992 + +Revision History: + +--*/ + +// +// Scalars and functions to test and set the "SetupType" value item +// + +#define SETUPTYPE_NONE 0 +#define SETUPTYPE_FULL 1 +#define SETUPTYPE_NETIDW 2 +#ifdef INIT_REGISTRY +#define SETUPTYPE_NETSRW 3 +#endif +#define SETUPTYPE_UPGRADE 4 + +#define APPNAME_WINLOGON TEXT("Winlogon") +#define VARNAME_SETUPTYPE TEXT("SetupType") +#define VARNAME_SETUPTYPE_A "SetupType" +#define VARNAME_SETUPCMD TEXT("Cmdline") +#define VARNAME_SETUPCMD_A "Cmdline" +#define VARNAME_AUTOLOGON TEXT("AutoAdminLogon") +#define VARNAME_ENABLEQUICKREBOOT TEXT("EnableQuickReboot") +#define VARNAME_ENABLEDESKTOPSWITCHING TEXT("EnableDesktopSwitching") +#define VARNAME_SHELL TEXT("Shell") +#define VARNAME_SETUPINPROGRESS TEXT("SystemSetupInProgress") +#define VARNAME_SETUPINPROGRESS_A "SystemSetupInProgress" +#define KEYNAME_SETUP TEXT("\\Registry\\Machine\\System\\Setup") +#define REGNAME_SETUP TEXT("SYSTEM\\setup") + +DWORD +CheckSetupType ( + VOID + ); + +BOOL +SetSetupType ( + DWORD type + ); + +BOOL +AppendToSetupCommandLine( + LPSTR pszCommandArguments + ); + +// +// Function to execute setup.exe and wait for it to complete. +// + +VOID +ExecuteSetup( + PGLOBALS pGlobals + ); + + +// +// Handle to the event used by lsa to stall security initialization. +// + +HANDLE LsaStallEvent; + + +// +// Function to create an event used by LSA to stall security initialization. +// + +VOID +CreateLsaStallEvent( + VOID + ); + + + +VOID +CheckForIncompleteSetup ( + PGLOBALS pGlobals + ); + + +typedef +VOID (WINAPI * REPAIRSTARTMENUITEMS)( + VOID + ); + +VOID +CheckForRepairRequest (void); diff --git a/private/windows/gina/winlogon/shell.c b/private/windows/gina/winlogon/shell.c new file mode 100644 index 000000000..713e7a38b --- /dev/null +++ b/private/windows/gina/winlogon/shell.c @@ -0,0 +1,170 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1993. +// +// File: shell.c +// +// Contents: Microsoft Logon GUI DLL +// +// History: 7-14-94 RichardW Created +// +//---------------------------------------------------------------------------- + +#include "precomp.h" +#pragma hdrstop + + +BOOL +SetProcessQuotas( + PPROCESS_INFORMATION ProcessInformation, + PUSER_PROCESS_DATA UserProcessData + ) +{ + NTSTATUS Status = STATUS_SUCCESS; + BOOL Result; + QUOTA_LIMITS RequestedLimits; + + RequestedLimits = UserProcessData->Quotas; + RequestedLimits.MinimumWorkingSetSize = 0; + RequestedLimits.MaximumWorkingSetSize = 0; + + if (UserProcessData->Quotas.PagedPoolLimit != 0) { + + Result = EnablePrivilege(SE_INCREASE_QUOTA_PRIVILEGE, TRUE); + if (!Result) { + DebugLog((DEB_ERROR, "failed to enable increase_quota privilege\n")); + return(FALSE); + } + + Status = NtSetInformationProcess( + ProcessInformation->hProcess, + ProcessQuotaLimits, + (PVOID)&RequestedLimits, + (ULONG)sizeof(QUOTA_LIMITS) + ); + + Result = EnablePrivilege(SE_INCREASE_QUOTA_PRIVILEGE, FALSE); + if (!Result) { + DebugLog((DEB_ERROR, "failed to disable increase_quota privilege\n")); + } + } + + +#if DBG + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "SetProcessQuotas failed. Status: 0x%lx\n", Status)); + } +#endif //DBG + + return (NT_SUCCESS(Status)); +} + + +BOOL +ExecApplication( + IN LPTSTR pch, + IN LPTSTR Desktop, + IN PGLOBALS pGlobals, + IN PVOID pEnvironment, + IN DWORD Flags, + IN DWORD StartupFlags, + OUT PPROCESS_INFORMATION ProcessInformation + ) +{ + STARTUPINFO si; + BOOL Result, IgnoreResult; + HANDLE ImpersonationHandle; + + + // + // Initialize process startup info + // + si.cb = sizeof(STARTUPINFO); + si.lpReserved = pch; + si.lpTitle = pch; + si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0L; + si.dwFlags = StartupFlags; + si.wShowWindow = SW_SHOW; // at least let the guy see it + si.lpReserved2 = NULL; + si.cbReserved2 = 0; + si.lpDesktop = Desktop; + + // + // Impersonate the user so we get access checked correctly on + // the file we're trying to execute + // + + ImpersonationHandle = ImpersonateUser(&pGlobals->UserProcessData, NULL); + if (ImpersonationHandle == NULL) { + DebugLog((DEB_ERROR, "ExecApplication failed to impersonate user\n")); + return(FALSE); + } + + + // + // Create the app suspended + // + DebugLog((DEB_TRACE, "About to create process of %ws, on desktop %ws\n", pch, Desktop)); + Result = CreateProcessAsUser( + pGlobals->UserProcessData.UserToken, + NULL, + pch, + NULL, + NULL, + FALSE, + Flags | CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT, + pEnvironment, + NULL, + &si, + ProcessInformation); + + + IgnoreResult = StopImpersonating(ImpersonationHandle); + ASSERT(IgnoreResult); + + return(Result); + +} + + + +BOOL +WINAPI +WlxStartApplication( + PVOID pWlxContext, + PWSTR pszDesktop, + PVOID pEnvironment, + PWSTR pszCmdLine + ) +{ + PROCESS_INFORMATION ProcessInformation; + BOOL bExec; + + bExec = ExecApplication (pszCmdLine, + pszDesktop, + g_pGlobals, + pEnvironment, + 0, + STARTF_USESHOWWINDOW, + &ProcessInformation); + + if (!bExec) { + return(FALSE); + } + + if (SetProcessQuotas(&ProcessInformation, + &g_pGlobals->UserProcessData)) { + ResumeThread(ProcessInformation.hThread); + + } else { + TerminateProcess(ProcessInformation.hProcess, + ERROR_ACCESS_DENIED); + } + + + CloseHandle(ProcessInformation.hThread); + CloseHandle(ProcessInformation.hProcess); + + return(TRUE); +} diff --git a/private/windows/gina/winlogon/shutdown.h b/private/windows/gina/winlogon/shutdown.h new file mode 100644 index 000000000..9c7579aea --- /dev/null +++ b/private/windows/gina/winlogon/shutdown.h @@ -0,0 +1,23 @@ +// +// The Shutdown Query dialog and Logoff Windows NT dialog +// are shared by Progman (included in windows\shell\progman\progman.dlg), +// and therefore changes to them or the filename should not be made +// unless tested with Progman first. +// This header file is included in windows\shell\progman\pmdlg.h +// +// 11/10/92 johannec +// + +#define IDD_SHUTDOWN_QUERY 1200 +#define IDD_CLOSEAPPS 1201 +#define IDD_RESTART 1202 +#define IDD_END_WINDOWS_SESSION 1203 +#define IDD_POWEROFF 1204 + +#define DLGSEL_LOGOFF 0 +#define DLGSEL_SHUTDOWN 1 +#define DLGSEL_SHUTDOWN_AND_RESTART 2 +#define DLGSEL_SHUTDOWN_AND_POWEROFF 3 + +#define SHUTDOWN_SETTING_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Shutdown" +#define SHUTDOWN_SETTING L"Shutdown Setting" diff --git a/private/windows/gina/winlogon/shutdown.ico b/private/windows/gina/winlogon/shutdown.ico Binary files differnew file mode 100644 index 000000000..2512943dd --- /dev/null +++ b/private/windows/gina/winlogon/shutdown.ico diff --git a/private/windows/gina/winlogon/sources b/private/windows/gina/winlogon/sources new file mode 100644 index 000000000..3080168ff --- /dev/null +++ b/private/windows/gina/winlogon/sources @@ -0,0 +1,88 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + + +MAJORCOMP=windows +MINORCOMP=winlogon + +TARGETNAME=winlogon +TARGETPATH=obj +TARGETTYPE=PROGRAM + +INCLUDES=.;$(TARGET_DIRECTORY);..\..\inc;..\..\..\inc;..\..\screg\winreg +C_DEFINES=-DUNICODE + +!ifndef MSC_WARNING_LEVEL +MSC_WARNING_LEVEL=-W3 -WX +!endif + +BACKGROUND_USE=1 + +SOURCES= \ + debug.c \ + doslog.c \ + envvar.c \ + ginamgr.c \ + logoff.c \ + misc.c \ + monitor.c \ + msgalias.c \ + provider.c \ + regini.c \ + removabl.c \ + res.rc \ + sas.c \ + scrnsave.c \ + secutil.c \ + setup.c \ + shell.c \ + sysinit.c \ + sysshut.c \ + timeout.c \ + usrenv.c \ + usrpro.c \ + win31mig.c \ + winlogon.c \ + winutil.c \ + wlx.c \ + wlxutil.c + +USE_CRTDLL=1 + +UMTYPE=windows +UMENTRY=winmain +TARGETLIBS= \ + $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \ + $(BASEDIR)\public\sdk\lib\*\netapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\lsadll.lib \ + $(BASEDIR)\public\sdk\lib\*\user32p.lib \ + $(BASEDIR)\public\sdk\lib\*\ntdll.lib \ + $(BASEDIR)\public\sdk\lib\*\userenv.lib \ + ..\..\screg\winreg\server\obj\*\winreg.lib \ + ..\..\screg\winreg\perflib\obj\*\perflib.lib \ + ..\..\screg\winreg\lib\obj\*\wrlib.lib + +NTTARGETFILE0=wlevents.h diff --git a/private/windows/gina/winlogon/stringid.h b/private/windows/gina/winlogon/stringid.h new file mode 100644 index 000000000..d091e553f --- /dev/null +++ b/private/windows/gina/winlogon/stringid.h @@ -0,0 +1,44 @@ +/****************************** Module Header ******************************\ +* Module Name: stringid.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Defines resource ids for resources other than dialogs +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +// +// Strings +// +#define IDS_RESTART_SYSTEM 1520 +#define IDS_WINDOWS_MESSAGE 1524 +#define IDS_SAFE_TO_TURN_OFF 1526 +#define IDS_SHUTDOWN_MESSAGE 1527 + + + +#define IDS_SYSTEM_SCREEN_SAVER_NAME 1564 + + +#define IDS_SOUND_DLL 1570 +#define IDS_WAVEOUTGETNUMDEVS 1571 +#define IDS_PLAYSOUND 1572 +#define IDS_MIGRATESOUNDEVENTS 1573 +#define IDS_MIDI_DLL 1574 +#define IDS_MIGRATEMIDIUSER 1575 + +#define IDS_NO_PAGING_FILE 1580 +#define IDS_LIMITED_RESOURCES 1581 + +#define IDS_INVALID_TIME 1582 +#define IDS_INVALID_TIME_MSG 1583 + +#define IDS_SETUP_INCOMPLETE 1590 + +#define IDS_ACCOUNT_LOCKED 1603 + +#define IDS_ADMIN_ACCOUNT_NAME 1700 + +#define IDD_SHUTDOWN_BITMAP 4 diff --git a/private/windows/gina/winlogon/strings.h b/private/windows/gina/winlogon/strings.h new file mode 100644 index 000000000..3cc268148 --- /dev/null +++ b/private/windows/gina/winlogon/strings.h @@ -0,0 +1,54 @@ +/****************************** Module Header ******************************\ +* Module Name: strings.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Defines strings that do not need to be localized. +* +* History: +* 11-17-92 Davidc Created. +\***************************************************************************/ + +// +// App name strings +// + +#define WINLOGON TEXT("WINLOGON") + + +// +// Define where we store the most recent logon information +// + +#define APPLICATION_NAME WINLOGON + +// +// Define where we get screen-saver information +// + +#define SCREEN_SAVER_INI_FILE TEXT("system.ini") +#define SCREEN_SAVER_INI_SECTION TEXT("boot") +#define SCREEN_SAVER_FILENAME_KEY TEXT("SCRNSAVE.EXE") +#define SCREEN_SAVER_SECURE_KEY TEXT("ScreenSaverIsSecure") + +#define WINDOWS_INI_SECTION TEXT("Windows") +#define SCREEN_SAVER_ENABLED_KEY TEXT("ScreenSaveActive") + +// +// Gina is loaded from: +// + +#define GINA_KEY TEXT("GinaDll") +#define LOCK_GRACE_PERIOD_KEY TEXT("ScreenSaverGracePeriod") +#define LOCK_DEFAULT_VALUE 5 +#define KEEP_RAS_AFTER_LOGOFF TEXT("KeepRasConnections") +#define RESTRICT_NONINTERACTIVE_ACCESS TEXT("RestrictNonInteractiveAccess") + + +// +// Shell= line in the registry +// + +#define SHELL_KEY TEXT("Shell") +#define RASAPI32 TEXT("rasapi32.dll") +#define RASMAN_SERVICE_NAME TEXT("RASMAN") diff --git a/private/windows/gina/winlogon/strings.rc b/private/windows/gina/winlogon/strings.rc new file mode 100644 index 000000000..3b9fad652 --- /dev/null +++ b/private/windows/gina/winlogon/strings.rc @@ -0,0 +1,46 @@ +/****************************** Module Header ******************************\ +* Module Name: strings.rc +* +* Copyright (c) 1991, Microsoft Corporation +* +* Defines string resources +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +STRINGTABLE +BEGIN + +IDS_RESTART_SYSTEM "Restart system" +IDS_WINDOWS_MESSAGE "Windows Message" +IDS_SAFE_TO_TURN_OFF "It is now safe to turn off the computer." +IDS_SHUTDOWN_MESSAGE "Shutdown Message" +IDS_NO_PAGING_FILE "Your system is running without a properly sized paging file. Please use the virtual memory option of the System applet in the Control Panel to create a paging file, or to increase the initial size of your paging file." +IDS_LIMITED_RESOURCES "Limited Virtual Memory" +IDS_INVALID_TIME_MSG "The time or date on your system is invalid. Please use the date/time applet in the Control Panel to properly set your system time and date." +IDS_INVALID_TIME "Invalid System Time" +IDS_SETUP_INCOMPLETE "The system is not fully installed. Please run setup again." +IDS_ACCOUNT_LOCKED "Unable to log you on because your account has been locked out, please contact your administrator." + + +// +// Screen saver strings +// + +IDS_SYSTEM_SCREEN_SAVER_NAME "system.scr" + +IDS_ADMIN_ACCOUNT_NAME "Administrator" + +// +// Define multimedia strings +// + +IDS_SOUND_DLL "WINMM" +IDS_WAVEOUTGETNUMDEVS "waveOutGetNumDevs" +IDS_PLAYSOUND "PlaySound" +IDS_MIGRATESOUNDEVENTS "MigrateSoundEvents" +IDS_MIDI_DLL "WINMM" +IDS_MIGRATEMIDIUSER "MigrateMidiUser" + +END diff --git a/private/windows/gina/winlogon/sysinit.c b/private/windows/gina/winlogon/sysinit.c new file mode 100644 index 000000000..fb6ceee9b --- /dev/null +++ b/private/windows/gina/winlogon/sysinit.c @@ -0,0 +1,969 @@ +/****************************** Module Header ******************************\ +* Module Name: sysinit.c +* +* Copyright (c) 1991, Microsoft Corporation +* +* Winlogon main module +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +BOOLEAN PageFilePopup = FALSE; + +TCHAR szMemMan[] = + TEXT("System\\CurrentControlSet\\Control\\Session Manager\\Memory Management"); + +TCHAR szNoPageFile[] = TEXT("TempPageFile"); + +HANDLE hSystemProcesses[MAXIMUM_WAIT_OBJECTS]; +DWORD cSystemProcesses; + +#define DEBUG_COMMAND TEXT("ntsd -d ") +#define DEBUG_COMMAND_NO_WAIT TEXT("ntsd -d -g ") +#define SELECT_DEBUG_COMMAND(x) (x & DEB_DEBUG_NOWAIT ? DEBUG_COMMAND_NO_WAIT : DEBUG_COMMAND) + + +// +// Bogus #2: InitializeWinreg is defined in regrpc.h, but we can't include +// it in any file that uses RegXxx APIs. So, rather than add yet another +// source file, this is explicitly prototyped here, and any change there has +// to be reflected here. +// + +BOOL +InitializeWinreg(void); + + +// +// Look for autocheck logs, and log them +// + +VOID +DealWithAutochkLogs( + VOID + ) +{ + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE Handle; + HANDLE DirectoryHandle; + + POBJECT_DIRECTORY_INFORMATION DirInfo; + CHAR DirInfoBuffer[ 256 ]; + ULONG Context, Length; + BOOLEAN RestartScan; + GLOBALS LocalGlobals; + + UNICODE_STRING UnicodeString; + UNICODE_STRING LinkTarget; + UNICODE_STRING LinkTypeName; + UNICODE_STRING LinkTargetPrefix; + WCHAR LinkTargetBuffer[ MAXIMUM_FILENAME_LENGTH ]; + WCHAR LogFile[MAX_PATH]; + HANDLE LogFileHandle; + DWORD FileSize,BytesRead; + WCHAR *FileBuffer; + DWORD ServerRetryCount; + DWORD rv; + DWORD gle; + UINT OldMode; + + + ZeroMemory(&LocalGlobals,sizeof(LocalGlobals)); + LinkTarget.Buffer = LinkTargetBuffer; + + DirInfo = (POBJECT_DIRECTORY_INFORMATION)&DirInfoBuffer; + RestartScan = TRUE; + RtlInitUnicodeString( &LinkTypeName, L"SymbolicLink" ); + RtlInitUnicodeString( &LinkTargetPrefix, L"\\Device\\Harddisk" ); + + RtlInitUnicodeString( &UnicodeString, L"\\DosDevices" ); + InitializeObjectAttributes( &ObjectAttributes, + &UnicodeString, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + Status = NtOpenDirectoryObject( &DirectoryHandle, + DIRECTORY_QUERY, + &ObjectAttributes + ); + if (!NT_SUCCESS( Status )) { + return; + } + + while (TRUE) { + Status = NtQueryDirectoryObject( DirectoryHandle, + (PVOID)DirInfo, + sizeof( DirInfoBuffer ), + TRUE, + RestartScan, + &Context, + &Length + ); + if (!NT_SUCCESS( Status )) { + Status = STATUS_SUCCESS; + break; + } + + if (RtlEqualUnicodeString( &DirInfo->TypeName, &LinkTypeName, TRUE ) && + DirInfo->Name.Buffer[(DirInfo->Name.Length>>1)-1] == L':') { + InitializeObjectAttributes( &ObjectAttributes, + &DirInfo->Name, + OBJ_CASE_INSENSITIVE, + DirectoryHandle, + NULL + ); + Status = NtOpenSymbolicLinkObject( &Handle, + SYMBOLIC_LINK_QUERY, + &ObjectAttributes + ); + if (NT_SUCCESS( Status )) { + LinkTarget.Length = 0; + LinkTarget.MaximumLength = sizeof( LinkTargetBuffer ); + Status = NtQuerySymbolicLinkObject( Handle, + &LinkTarget, + NULL + ); + NtClose( Handle ); + if (NT_SUCCESS( Status ) && + RtlPrefixUnicodeString( &LinkTargetPrefix, &LinkTarget, TRUE ) + ) { + + CopyMemory(LogFile,DirInfo->Name.Buffer,DirInfo->Name.Length); + LogFile[DirInfo->Name.Length >> 1] = (WCHAR)0; + wcscat(LogFile,L"\\bootex.log"); + + OldMode = SetErrorMode( SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS ); + + LogFileHandle = CreateFileW( + LogFile, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + + (VOID )SetErrorMode( OldMode ); + + if ( LogFileHandle != INVALID_HANDLE_VALUE ) { + FileSize = GetFileSize(LogFileHandle,NULL); + if ( FileSize != 0xffffffff ) { + + // + // truncate the file data if necessary + // + if ( FileSize > 32000 ) { + FileSize = 32000; + } + FileBuffer = LocalAlloc(LMEM_FIXED,FileSize+sizeof(WCHAR)); + if ( FileBuffer ) { + FileBuffer[FileSize>>1] = (WCHAR)'\0'; + if ( ReadFile(LogFileHandle,FileBuffer,FileSize,&BytesRead,NULL) ) { + FileBuffer[BytesRead>>1] = (WCHAR)'\0'; + + ServerRetryCount = 0; +tryagain: + LocalGlobals.hEventLog = RegisterEventSource( + NULL, + TEXT("Autochk") + ); + if ( LocalGlobals.hEventLog ) { + rv = ReportWinlogonEvent( + &LocalGlobals, + EVENTLOG_INFORMATION_TYPE, + EVENT_AUTOCHK_DATA, + 0, + NULL, + 1, + FileBuffer + ); + DeregisterEventSource(LocalGlobals.hEventLog); + LocalGlobals.hEventLog = NULL; + NtClose(LogFileHandle); + LogFileHandle = INVALID_HANDLE_VALUE; + if ( rv == ERROR_SUCCESS ) { + DeleteFile(LogFile); + } + } + else { + gle = GetLastError(); + if ( (gle == RPC_S_SERVER_UNAVAILABLE || + gle == RPC_S_UNKNOWN_IF) + && ServerRetryCount < 10 ) { + Sleep(1000); + ServerRetryCount++; + goto tryagain; + } + } + + } + } + } + if (LogFileHandle != INVALID_HANDLE_VALUE ) { + NtClose(LogFileHandle); + } + } + + } + } + } + + RestartScan = FALSE; + if (!NT_SUCCESS( Status )) { + break; + } + } + NtClose(DirectoryHandle); + return; +} + + +DWORD FontLoaderThread( void ) +{ + LoadLocalFonts(); + ExitThread(0); + return(0); // prevent compiler warning +} + +HANDLE +StartLoadingFonts(void) +{ + HANDLE hThread; + DWORD ThreadId = 0; + + hThread = CreateThread( (LPSECURITY_ATTRIBUTES) NULL, + 0, + (LPTHREAD_START_ROUTINE) FontLoaderThread, + 0, + 0, + &ThreadId + ); + + // + // We don't need this handle (we're not going to wait), so get rid of + // it now, rather than later. + // + + return( hThread ); +} + + +BOOL InitSystemFontInfo( + PGLOBALS pGlobals + ) +{ + TCHAR *FontNames, *FontName; + TCHAR FontPath[ MAX_PATH ]; + ULONG cb = 63 * 1024; + + + FontNames = Alloc( cb ); + ASSERTMSG("Winlogon failed to allocate memory for reading font information", FontNames != NULL); + if (FontNames == NULL) { + return FALSE; + } + + if (GetProfileString( TEXT("Fonts"), NULL, TEXT(""), FontNames, cb )) { + FontName = FontNames; + while (*FontName) { + if (GetProfileString( TEXT("Fonts"), FontName, TEXT(""), FontPath, sizeof( FontPath ) )) { + switch (AddFontResource( FontPath )) { + case 0: + KdPrint(("WINLOGON: Unable to add new font path: %ws\n", FontPath )); + break; + + case 1: + KdPrint(("WINLOGON: Found new font path: %ws\n", FontPath )); + break; + + default: + KdPrint(("WINLOGON: Found existing font path: %ws\n", FontPath )); + RemoveFontResource( FontPath ); + break; + } + } + while (*FontName++) ; + } + } else { + KdPrint(("WINLOGON: Unable to read font info from win.ini - %u\n", GetLastError())); + } + + Free( FontNames ); + return TRUE; +} + + +/***************************************************************************\ +* SetProcessPriority +* +* Sets the priority of the winlogon process. +* +* History: +* 18-May-1992 Davidc Created. +\***************************************************************************/ +BOOL SetProcessPriority( + VOID + ) +{ + // + // Bump us up to the high priority class + // + + if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) { + DebugLog((DEB_ERROR, "Failed to raise it's own process priority, error = %d", GetLastError())); + return(FALSE); + } + + // + // Set this thread to high priority since we'll be handling all input + // + + if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST)) { + DebugLog((DEB_ERROR, "Failed to raise main thread priority, error = %d", GetLastError())); + return(FALSE); + } + + return(TRUE); +} + + +VOID +CreateTemporaryPageFile() +{ + LONG FileSizeInMegabytes; + UNICODE_STRING PagingFileName; + NTSTATUS st; + LARGE_INTEGER MinPagingFileSize; + LARGE_INTEGER MaxPagingFileSize; + UNICODE_STRING FileName; + BOOLEAN TranslationStatus; + TCHAR TemporaryPageFile[MAX_PATH+1]; + NTSTATUS PfiStatus,PiStatus; + ULONG ReturnLength; + SYSTEM_PAGEFILE_INFORMATION pfi; + SYSTEM_PERFORMANCE_INFORMATION PerfInfo; + HKEY hkeyMM; + DWORD dwRegData = 0; + + + GetSystemDirectory(TemporaryPageFile,sizeof(TemporaryPageFile)); + wcscat(TemporaryPageFile,TEXT("\\temppf.sys")); + DeleteFile(TemporaryPageFile); + + // + // Check to see if we have a pagefile, warn the user if we don't + // + + PfiStatus = NtQuerySystemInformation( + SystemPageFileInformation, + &pfi, + sizeof(pfi), + &ReturnLength + ); + + PiStatus = NtQuerySystemInformation( + SystemPerformanceInformation, + &PerfInfo, + sizeof(PerfInfo), + NULL + ); + // + // if you have no page file, or your total commit limit is at it's minimum, + // then create an additional pagefile and tel the user to do something... + // + + if ( (NT_SUCCESS(PfiStatus) && (ReturnLength == 0)) || + (NT_SUCCESS(PiStatus) && PerfInfo.CommitLimit <= 5500 ) ) { + + // + // Set a flag in registry so USERINIT knows to run VMApp. + // + dwRegData = 1; + + PageFilePopup = TRUE; + + // + // create a temporary pagefile to get us through logon/control + // panel activation + // + // + + GetSystemDirectory(TemporaryPageFile,sizeof(TemporaryPageFile)); + lstrcat(TemporaryPageFile,TEXT("\\temppf.sys")); + + + // + // Start with a 20mb pagefile + // + + FileSizeInMegabytes = 20; + + RtlInitUnicodeString(&PagingFileName, TemporaryPageFile); + + MinPagingFileSize = RtlEnlargedIntegerMultiply(FileSizeInMegabytes,0x100000); + MaxPagingFileSize = MinPagingFileSize; + + + TranslationStatus = RtlDosPathNameToNtPathName_U( + PagingFileName.Buffer, + &FileName, + NULL, + NULL + ); + + if ( TranslationStatus ) { + +retry: + st = NtCreatePagingFile( + (PUNICODE_STRING)&FileName, + &MinPagingFileSize, + &MaxPagingFileSize, + 0 + ); + + if (!NT_SUCCESS( st )) { + + if ( FileSizeInMegabytes > 0 ) { + FileSizeInMegabytes -= 2; + MinPagingFileSize = RtlEnlargedIntegerMultiply(FileSizeInMegabytes,0x100000); + MaxPagingFileSize = MinPagingFileSize; + goto retry; + } + } else { + MoveFileExW(PagingFileName.Buffer,NULL,MOVEFILE_DELAY_UNTIL_REBOOT); + + } + + RtlFreeHeap(RtlProcessHeap(), 0, FileName.Buffer); + + } + } + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szMemMan, 0, + KEY_WRITE, &hkeyMM) == ERROR_SUCCESS) { + if (dwRegData == 1) { + RegSetValueEx (hkeyMM, szNoPageFile, 0, REG_DWORD, + (LPBYTE)&dwRegData, sizeof(dwRegData)); + } else + RegDeleteValue(hkeyMM, (LPTSTR)szNoPageFile); + RegCloseKey(hkeyMM); + } +} + + +BOOL +StartSystemProcess( + PWSTR pszCommandLine, + PWSTR pszDesktop, + DWORD Flags, + DWORD StartupFlags, + PVOID pEnvironment, + BOOLEAN fSaveHandle, + HANDLE *phProcess, + HANDLE *phThread + ) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + BOOL Result; +#if DBG + WCHAR szExtra[MAX_PATH]; +#endif + + // + // Initialize process startup info + // + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.lpReserved = pszCommandLine; + si.lpTitle = pszCommandLine; + si.dwFlags = StartupFlags; + si.wShowWindow = SW_SHOW; // at least let the guy see it + si.lpDesktop = pszDesktop; + + // + // Special debug helpers for our friends + // +#if DBG + if ((WinlogonInfoLevel & DEB_DEBUG_LSA) && + ((wcsncmp(pszCommandLine, TEXT("lsass"), 5) == 0) || + (wcsncmp(pszCommandLine, TEXT("spmgr"), 5) == 0) )) + { + wcscpy(szExtra, SELECT_DEBUG_COMMAND(WinlogonInfoLevel)); + wcscat(szExtra, pszCommandLine); + pszCommandLine = szExtra; + } + if ((WinlogonInfoLevel & DEB_DEBUG_MPR) && + (wcsncmp(pszCommandLine, TEXT("mpnotify"), 8) == 0)) + { + wcscpy(szExtra, SELECT_DEBUG_COMMAND(WinlogonInfoLevel)); + wcscat(szExtra, pszCommandLine); + pszCommandLine = szExtra; + } + if ((WinlogonInfoLevel & DEB_DEBUG_SERVICES) && + (wcsncmp(pszCommandLine, TEXT("services"), 8) == 0)) + { + wcscpy(szExtra, SELECT_DEBUG_COMMAND(WinlogonInfoLevel)); + wcscat(szExtra, pszCommandLine); + pszCommandLine = szExtra; + } +#endif + + + // + // Create the app suspended + // + Result = CreateProcess(NULL, + pszCommandLine, + NULL, + NULL, + FALSE, + Flags | CREATE_UNICODE_ENVIRONMENT, + pEnvironment, + NULL, + &si, + &pi); + + + if (Result) + { + if (!phProcess) + { + if (fSaveHandle) + { + if (cSystemProcesses < MAXIMUM_WAIT_OBJECTS) + { + hSystemProcesses[cSystemProcesses++] = pi.hProcess; + } + } + else + { + CloseHandle(pi.hProcess); + } + } + else + { + *phProcess = pi.hProcess; + } + if (!phThread) + { + CloseHandle(pi.hThread); + } + else + { + *phThread = pi.hThread; + } + } + + return(Result); +} + + +BOOL +ExecSystemProcesses( + PGLOBALS pGlobals + ) +{ + BOOL SystemStarted = FALSE ; + SYSTEM_CRASH_STATE_INFORMATION CrashState; + PWSTR pszStartLine; + PWSTR pszTok; + DWORD dwStarted = 0; + PVOID pEnvironment; + + // + // Initialize the shutdown server + // + + RpcpInitRpcServer(); + if ( !InitializeShutdownModule( pGlobals ) ) { + ASSERT( FALSE ); + DebugLog((DEB_ERROR, "Cannot InitializeShutdownModule.")); + } + + // + // Initialize the registry server + // + // NB: This is prototyped local to this file. Any change must be + // reflected above. + // + + if ( !InitializeWinreg() ) { + ASSERT( FALSE ); + DebugLog((DEB_ERROR, "Cannot InitializeWinreg.")); + } + + + + // + // must start services.exe server before anything else. If there is an + // entry ServiceControllerStart in win.ini, use it as the command. + // + pszStartLine = AllocAndGetProfileString(APPLICATION_NAME, + TEXT("ServiceControllerStart"), + TEXT("services.exe")); + + if (!pszStartLine) + { + DebugLog((DEB_ERROR, "Can't allocate space, so this exec probably won't work\n")); + pszStartLine = TEXT("services.exe"); + } + + if (CreateUserEnvironment(&pEnvironment)) + { + SetupBasicEnvironment(&pEnvironment); + } + else + { + DebugLog((DEB_ERROR, "Failed to create initial environment\n")); + + // + // Set this to NULL, and let CreateProcess deal with any + // memory constraints. + // + pEnvironment = NULL; + } + + if (!StartSystemProcess(pszStartLine, + APPLICATION_DESKTOP_NAME, + 0, + STARTF_FORCEOFFFEEDBACK, + pEnvironment, + FALSE, // Don't stash this handle away + NULL, NULL)) + { + DebugLog((DEB_ERROR, "Couldn't start %ws, %d\n", pszStartLine, GetLastError())); + } + + else + { + HANDLE hRPCRegServer; + int error, + i = 0 ; + + while(i < 20000) { + Sleep(1000); i+=1000; + if (hRPCRegServer = OpenEventA(SYNCHRONIZE, FALSE, "Microsoft.RPC_Registry_Server")) { + //WLPrint(("RPC_Registry_Server event openned")); + error = WaitForSingleObject(hRPCRegServer, 100); + CloseHandle(hRPCRegServer); + break; + } + } + } + + Free(pszStartLine); + + // + // If this is standard installation or network installation, we need to + // create an event to stall lsa security initialization. In the case of + // WINNT -> WINNT and AS -> AS upgrade we shouldn't stall LSA. + // + if (pGlobals->fExecuteSetup && (pGlobals->SetupType != SETUPTYPE_UPGRADE)) { + CreateLsaStallEvent(); + } + + // + // If there is a system dump available, start up the save dump process to + // capture it so that it doesn't use as much paging file so that it is + // available for system use. + // + + NtQuerySystemInformation( SystemCrashDumpStateInformation, + &CrashState, + sizeof( CrashState ), + (PULONG) NULL ); + if (CrashState.ValidCrashDump) { + pszStartLine = AllocAndGetProfileString(APPLICATION_NAME, + TEXT("SaveDumpStart"), + TEXT("savedump.exe")); + + + if (!StartSystemProcess(pszStartLine, + APPLICATION_DESKTOP_NAME, + 0, + STARTF_FORCEOFFFEEDBACK, + pEnvironment, + FALSE, // Don't care about syncing later + NULL, NULL)) + { + DebugLog((DEB_ERROR, "Couldn't start %ws, %d\n", pszStartLine, GetLastError())); + } + Free(pszStartLine); + } + + // + // Startup system processes + // These must be started for authentication initialization to succeed + // because one of the system processes is the LSA server. + // + pszStartLine = AllocAndGetProfileString(APPLICATION_NAME, + TEXT("System"), + NULL); + + pszTok = wcstok(pszStartLine, TEXT(",")); + while (pszTok) + { + // + // Skip any blanks... + // + if (*pszTok == TEXT(' ')) + { + while (*pszTok++ == TEXT(' ')) + ; + } + + if (StartSystemProcess( pszTok, + APPLICATION_DESKTOP_NAME, + 0, + STARTF_FORCEOFFFEEDBACK, + pEnvironment, + TRUE, // Save this handle to sync with + NULL, NULL)) + { + dwStarted++; + } + pszTok = wcstok(NULL, TEXT(",")); + + } + + Free(pszStartLine); + + RtlDestroyEnvironment(pEnvironment); + + return TRUE; +} + + +/***************************************************************************\ +* InitializeSound +* +* Set up a global function variable to address the sound playing routine. +* If no wave devices are present, this variable will remain 0 and no sound +* will be made by WinLogon. +* +* History: +* 6-May-1992 SteveDav Created +\***************************************************************************/ +void InitializeSound( + PGLOBALS pGlobals) +{ + // + // Load the sound playing module. If no wave devices are available + // free the library, and set the address of the sound function to 0 + // + + CHAR ResourceString[MAX_STRING_BYTES]; + HANDLE hLib; + + // Set the initial value (should not be necessary) + pGlobals->PlaySound = NULL; + pGlobals->MigrateSoundEvents = NULL; + + // + // Get name of sound library + // + if (!LoadStringA(NULL, IDS_SOUND_DLL, ResourceString, sizeof(ResourceString))) { + // Cannot get the name of the sound library + return; + } + + hLib = LoadLibraryA(ResourceString); + + if (hLib) { + + /* We must use the Ascii version of LoadString as GetProcAddress */ + /* takes an Ascii string only... */ + + /* Whenever a user logs in, have WINMM.DLL check if there are any */ + /* sound events within the [SOUNDS] section of CONTROL.INI that */ + /* haven't been ported into HKCU/AppEvents. Here, we find the */ + /* relevant routine within WINMM.DLL so it can be called when */ + /* appropriate. */ + + if (!LoadStringA(NULL, IDS_MIGRATESOUNDEVENTS, ResourceString, sizeof(ResourceString))) { + /* we do not know the name of the routine to call */ + pGlobals->MigrateSoundEvents = NULL; + } else { + pGlobals->MigrateSoundEvents = (SOUNDPROC)GetProcAddress(hLib, ResourceString); + } + + if (!LoadStringA(NULL, IDS_WAVEOUTGETNUMDEVS, ResourceString, sizeof(ResourceString))) { + /* we do not know the name of the routine to call */ + //return; We must free the library... + } else { + pGlobals->PlaySound = (SOUNDPROC)GetProcAddress(hLib, ResourceString); + } + + if (pGlobals->PlaySound) { + /* See how many wave devices there are - if none, or we fail + * to load the name of PlaySound, then unload WINMM and never + * try and call it again. + */ + UINT n; + n = (UINT)(*(pGlobals->PlaySound))(); + if (n && + LoadStringA(NULL, IDS_PLAYSOUND, ResourceString, sizeof(ResourceString))) { + pGlobals->PlaySound = (SOUNDPROC)GetProcAddress(hLib, ResourceString); + } else { + pGlobals->PlaySound = NULL; + //DebugLog((DEB_ERROR, "Winlogon: NO WAVE devices")); + } + } + + if (!pGlobals->PlaySound && !pGlobals->MigrateSoundEvents) { + //DebugLog((DEB_ERROR, "Winlogon: Unloading WINMM")); + FreeLibrary(hLib); + } + + } +#if DBG + else { /* Could not load WINMM */ + DebugLog((DEB_ERROR, "Could not load WINMM")); // Keep this debug message. It's an error + } +#endif +} + + + +/***************************************************************************\ +* InitializeMidi +* +* Set up a global function variable to address the Midi Migrate User routine. +* +* History: +* 1-3-96 ShawnB Created +\***************************************************************************/ +void InitializeMidi( + PGLOBALS pGlobals) +{ + // + // Load the Midi Migration module. + // + + CHAR ResourceString[MAX_STRING_BYTES]; + HMODULE hModule; + BOOL fFreeLib; + + // Set the initial value (should not be necessary) + pGlobals->MigrateMidiUser = NULL; + + // + // Get name of Midi library + // + if (!LoadStringA(NULL, IDS_MIDI_DLL, ResourceString, sizeof(ResourceString))) { + // Cannot get the name of the Midi library + return; + } + + // Check if Already loaded (by InitializeSound) + hModule = GetModuleHandleA(ResourceString); + if (!hModule) + { + // Load it ourselves + hModule = (HMODULE)LoadLibraryA(ResourceString); + fFreeLib = TRUE; + } + else + { + fFreeLib = FALSE; + } + + if (hModule) { + + /* We must use the Ascii version of LoadString as GetProcAddress */ + /* takes an Ascii string only... */ + + /* Whenever a user logs in, have WINMM.DLL check if the user needs */ + /* their MIDI registry info updated. Here, we find the */ + /* relevant routine within WINMM.DLL so it can be called when */ + /* appropriate. */ + + if (!LoadStringA(NULL, IDS_MIGRATEMIDIUSER, ResourceString, sizeof(ResourceString))) { + /* we do not know the name of the routine to call */ + pGlobals->MigrateMidiUser = NULL; + } else { + pGlobals->MigrateMidiUser = (MIDIPROC)GetProcAddress(hModule, ResourceString); + } + + + if (!pGlobals->MigrateMidiUser) { + //DebugLog((DEB_ERROR, "Winlogon: Unloading WINMM")); + if (fFreeLib) + { + FreeLibrary(hModule); + } + } + + } +#if DBG + else { /* Could not load WINMM */ + DebugLog((DEB_ERROR, "Could not load WINMM")); // Keep this debug message. It's an error + } +#endif +} // End InitializeMidi + +BOOL +WaitForSystemProcesses( + PGLOBALS pGlobals) +{ + DWORD i; + DWORD Exit; + + // + // First, verify all handles: + // + + for (i = 0; i < cSystemProcesses ; i++ ) + { + +WaitLoopTop: + + if (GetExitCodeProcess(hSystemProcesses[i], &Exit)) + { + if (Exit == STILL_ACTIVE) + { + // + // Ooh, a good one. Keep it. + // + + continue; + } + + } + + // + // Bad handle, one way or another + // + + CloseHandle(hSystemProcesses[i]); + hSystemProcesses[i] = hSystemProcesses[--cSystemProcesses]; + + if (i != cSystemProcesses) + { + goto WaitLoopTop; // Retry same index, but do not increment + } + + } + + if (!cSystemProcesses) + { + return(TRUE); + } + + Exit = WaitForMultipleObjectsEx( cSystemProcesses, + hSystemProcesses, + FALSE, + 4000, + FALSE ); + + + return(Exit != WAIT_TIMEOUT); +} diff --git a/private/windows/gina/winlogon/sysinit.h b/private/windows/gina/winlogon/sysinit.h new file mode 100644 index 000000000..f567f6fad --- /dev/null +++ b/private/windows/gina/winlogon/sysinit.h @@ -0,0 +1,65 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1993. +// +// File: sysinit.h +// +// Contents: +// +// Classes: +// +// Functions: +// +// History: 12-05-94 RichardW Created +// +//---------------------------------------------------------------------------- + +VOID +DealWithAutochkLogs( + VOID + ); + +HANDLE +StartLoadingFonts(void); + +BOOL InitSystemFontInfo( + PGLOBALS pGlobals + ); + +BOOL SetProcessPriority( + VOID + ); + +void +InitializeSound(PGLOBALS pGlobals); + +void +InitializeMidi(PGLOBALS pGlobals); + +VOID CreateTemporaryPageFile(); + + +BOOL +StartSystemProcess( + PWSTR pszCommandLine, + PWSTR pszDesktop, + DWORD Flags, + DWORD StartupFlags, + PVOID pEnvironment, + BOOLEAN fSaveHandle, + HANDLE *phProcess, + HANDLE *phThread + ); + + +BOOL +ExecSystemProcesses( + PGLOBALS pGlobals + ); + +BOOL +WaitForSystemProcesses( + PGLOBALS pGlobals); + +extern BOOLEAN PageFilePopup; diff --git a/private/windows/gina/winlogon/sysshut.c b/private/windows/gina/winlogon/sysshut.c new file mode 100644 index 000000000..65ff4c55f --- /dev/null +++ b/private/windows/gina/winlogon/sysshut.c @@ -0,0 +1,1395 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + Shutdown.c + +Abstract: + + This module contains the server side implementation for the Win32 remote + shutdown APIs, that is: + + - BaseInitiateSystemShutdown + - BaseAbortSystemShutdown + +Author: + + Dave Chalmers (davidc) 29-Apr-1992 + +Notes: + + +Revision History: + + 19-Oct-1993 Danl + Removed HackExtraThread which was only here to work around a bug in + the UserSrv. The text describing the reason for this workaround is + as follows: + HACKHACK - Work around bug in UserSrv that causes ExitWindowsEx to + fail (error 5) when called by a process which doesn't + have any threads which have called User APIs. Remove + after UserSrv is fixed. See NTBUG 11601. + Workaround is to create a thread which will make one + API call then sleep forever. + +--*/ + + +#include "precomp.h" +#pragma hdrstop + +#define RPC_NO_WINDOWS_H +#include "regrpc.h" +#include "ntrpcp.h" +#include <rpc.h> + +// // // // // // + +// +// Shutdown Dialog Return Codes: +// + +#define SHUTDOWN_SUCCESS 0 +#define SHUTDOWN_USER_LOGOFF 1 +#define SHUTDOWN_DESKTOP_SWITCH 2 +#define SHUTDOWN_CANCELLED 3 + +// +// System Shutdown globals +// + +RTL_CRITICAL_SECTION ShutdownCriticalSection; // Protect global shutdown data + +// +// Set when a thread has a shutdown 'in progress' +// (Protected by critical section) +// + +BOOL ShutdownInProgress; + +// +// Set when a thread wants to interrupt the shutdown +// (Protected by critical section) +// + +BOOL AbortShutdown; + + +// +// Data for shutdown UI - this is protected by the ShutdownInProgress flag. +// i.e. only the current shutdown thread manipulates this data. +// + +LARGE_INTEGER ShutdownTime; +DWORD ShutdownDelayInSeconds; +PTCH ShutdownMessage; +DWORD ExitWindowsFlags; +DWORD GinaCode; +PTSTR UserName; +PTSTR UserDomain; +BOOL AllowLogonDuringShutdown = TRUE ; +ActiveDesktops ShutdownDesktop; +WINDOWPLACEMENT ShutdownWindowPlacement; +BOOL ShutdownGetPlacement = FALSE; +BOOL ShutdownHasBegun = FALSE; + +// +// Data captured during initialization +// + +PGLOBALS GlobalWinMainData; + + + + + +// +// Private prototypes +// + + +DWORD +InitializeShutdownData( + PUNICODE_STRING lpMessage, + DWORD dwTimeout, + BOOL bForceAppsClosed, + BOOL bRebootAfterShutdown + ); + +VOID +FreeShutdownData( + VOID + ); + +BOOL WINAPI +ShutdownApiDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ); + +BOOL +UpdateTimeToShutdown( + HWND hDlg + ); + +VOID +CentreWindow( + HWND hwnd + ); + +DWORD +TestClientPrivilege( + VOID + ); + +DWORD +GetClientId( + PTSTR *UserName, + PTSTR *UserDomain + ); + +VOID +DeleteClientId( + PTSTR UserName, + PTSTR UserDomain + ); + +BOOL +InsertClientId( + HWND hDlg, + int ControlId, + PTSTR UserName, + PTSTR UserDomain + ); + + + + + +BOOL +InitializeShutdownModule( + PGLOBALS pGlobals + ) +/*++ + +Routine Description: + + Does any initializtion required for this module. + +Arguments: + + pGlobals - Pointer to global data defined in WinMain + +Return Value: + + Returns TRUE on success, FALSE on failure. + +--*/ +{ + NTSTATUS Status; + + // + // Initialize global variables + // + + ShutdownInProgress = FALSE; + + // + // Initialize critical section to protect globals + // + + Status = RtlInitializeCriticalSection(&ShutdownCriticalSection); + +#if DBG + if (!NT_SUCCESS(Status)) { + DbgPrint("Registry Server : Shutdown : Failed to initialize critical section\n"); + } +#endif + + GlobalWinMainData = pGlobals; + return(NT_SUCCESS(Status)); +} + +ULONG +BaseInitiateSystemShutdown( + IN PREGISTRY_SERVER_NAME ServerName, + IN PUNICODE_STRING lpMessage OPTIONAL, + IN DWORD dwTimeout, + IN BOOLEAN bForceAppsClosed, + IN BOOLEAN bRebootAfterShutdown + ) +/*++ + +Routine Description: + + Initiates the shutdown of this machine. + +Arguments: + + ServerName - Name of machine this server code is running on. (Ignored) + + lpMessage - message to display during shutdown timeout period. + + dwTimeout - number of seconds to delay before shutting down + + bForceAppsClosed - Normally applications may prevent system shutdown. + - If this true, all applications are terminated unconditionally. + + bRebootAfterShutdown - TRUE if the system should reboot. FALSE if it should + - be left in a shutdown state. + +Return Value: + + Returns ERROR_SUCCESS (0) for success; error-code for failure. + +--*/ + +{ + NTSTATUS Status; + DWORD Error; + + // + // Check the caller has the appropriate privilege + // + + Error = TestClientPrivilege(); + if (Error != ERROR_SUCCESS) { + return(Error); + } + + // + // Enter the critical section so we can look at our globals + // + + Status = RtlEnterCriticalSection(&ShutdownCriticalSection); + if (!NT_SUCCESS(Status)) { + return(RtlNtStatusToDosError(Status)); + } + + // + // Set up our global shutdown data. + // Fail if a shutdown is already in progress + // + + if (ShutdownInProgress) { + Error = ERROR_SHUTDOWN_IN_PROGRESS; + } else { + + // + // Set up our globals for the shutdown thread to use. + // + + Error = InitializeShutdownData(lpMessage, + dwTimeout, + bForceAppsClosed, + bRebootAfterShutdown + ); + if (Error == ERROR_SUCCESS) { + ShutdownInProgress = TRUE; + AbortShutdown = FALSE; + } + } + + // + // Leave the critical section + // + + Status = RtlLeaveCriticalSection(&ShutdownCriticalSection); + if (Error == ERROR_SUCCESS) { + if (!NT_SUCCESS(Status)) { + Error = RtlNtStatusToDosError(Status); + } + } else { + ASSERT(NT_SUCCESS(Status)); + } + + + + // + // Create a thread to handle the shutdown (UI and calling ExitWindows) + // The thread will handle resetting our shutdown data and globals. + // + + if (Error == ERROR_SUCCESS) { + int Result; + + // + // Have winlogon create us a thread running on the user's desktop. + // + // The thread will do a call back to ShutdownThread() + // + + GlobalWinMainData->LogoffFlags = EWX_WINLOGON_API_SHUTDOWN | ExitWindowsFlags; + Result = InitiateLogoff( GlobalWinMainData, + EWX_WINLOGON_API_SHUTDOWN | ExitWindowsFlags ); + + + if (Result != DLG_SUCCESS ) { + Error = GetLastError(); + KdPrint(("InitiateSystemShutdown : Failed to create shutdown thread. Error = %d\n", Error)); + FreeShutdownData(); + ShutdownInProgress = FALSE; // Atomic operation + } + } + + return(Error); + + UNREFERENCED_PARAMETER(ServerName); +} + + + +DWORD +InitializeShutdownData( + PUNICODE_STRING lpMessage, + DWORD dwTimeout, + BOOL bForceAppsClosed, + BOOL bRebootAfterShutdown + ) +/*++ + +Routine Description: + + Stores the passed shutdown parameters in our global data. + +Return Value: + + Returns ERROR_SUCCESS (0) for success; error-code for failure. + +--*/ + +{ + NTSTATUS Status; + LARGE_INTEGER TimeNow; + LARGE_INTEGER Delay; + DWORD Error; + + // + // Set the shutdown time + // + + ShutdownDelayInSeconds = dwTimeout; + + Status = NtQuerySystemTime(&TimeNow); + if (!NT_SUCCESS(Status)) { + return(RtlNtStatusToDosError(Status)); + } + + Delay = RtlEnlargedUnsignedMultiply(dwTimeout, 10000000); // Delay in 100ns + + ShutdownTime.QuadPart = TimeNow.QuadPart + Delay.QuadPart; + + + // + // Set the shutdown flags + // + // We set the EWX_WINLOGON_OLD_xxx and EWX_xxx both since this message + // originates from the winlogon process. When these flags actually bubble + // back to the active dialog box, winlogon expects the EWX_WINLOGON_OLD_xxx + // to indicate the 'real' request. + // + + ExitWindowsFlags = EWX_LOGOFF | EWX_SHUTDOWN | EWX_WINLOGON_OLD_SHUTDOWN; + ExitWindowsFlags |= bForceAppsClosed ? EWX_FORCE : 0; + ExitWindowsFlags |= bRebootAfterShutdown ? + (EWX_REBOOT | EWX_WINLOGON_OLD_REBOOT) : 0; + + if (bRebootAfterShutdown) + { + GinaCode = WLX_SAS_ACTION_SHUTDOWN_REBOOT; + } + else + { + GinaCode = WLX_SAS_ACTION_SHUTDOWN; + } + + + // + // Store the caller's username and domain. + // + + Error = GetClientId(&UserName, &UserDomain); + if (Error != ERROR_SUCCESS) { + return(Error); + } + + + // + // Set the shutdown message + // + + if (lpMessage != NULL) { + + // + // Copy the message into a global buffer + // + + USHORT Bytes = lpMessage->Length + (USHORT)sizeof(UNICODE_NULL); + + ShutdownMessage = (PTCH)LocalAlloc(LPTR, Bytes); + if (ShutdownMessage == NULL) { + DeleteClientId(UserName, UserDomain); + return(ERROR_NOT_ENOUGH_MEMORY); + } + + RtlMoveMemory(ShutdownMessage, lpMessage->Buffer, lpMessage->Length); + ShutdownMessage[lpMessage->Length / sizeof(WCHAR)] = 0; // Null terminate + + } else { + ShutdownMessage = NULL; + } + + + return(ERROR_SUCCESS); +} + + + +VOID +FreeShutdownData( + VOID + ) +/*++ + +Routine Description: + + Frees up any memory allocated to store the shutdown data + +Return Value: + + None. + +--*/ + +{ + if (ShutdownMessage != NULL) { + LocalFree(ShutdownMessage); + ShutdownMessage = NULL; + } + + DeleteClientId(UserName, UserDomain); + UserName = NULL; + UserDomain = NULL; +} + + + +BOOLEAN +ShutdownThread( + VOID + ) +/*++ + +Routine Description: + + Handles the display of a shutdown dialog and coordinating with the + AbortShutdown API. + +Arguments: + + None + +Return Value: + + TRUE - system should be shut down + FALSE - shutdown was aborted + +--*/ +{ + NTSTATUS Status; + DWORD Error; + BOOL DoShutdown = TRUE; + HDESK hdesk; + BOOL CloseDesktopHandle; + DWORD Result; + BOOL Locked; + BOOL Success; + + // + // Quick check so we don't get into thorny race conditions. + // + + if ( ShutdownDelayInSeconds == 0 ) + { + + FreeShutdownData(); + + RtlEnterCriticalSection( &ShutdownCriticalSection ); + + ShutdownInProgress = FALSE ; + + RtlLeaveCriticalSection( &ShutdownCriticalSection ); + + GlobalWinMainData->LastGinaRet = GinaCode; + + ShutdownHasBegun = TRUE; + + return( TRUE ); + + } + + + hdesk = GetActiveDesktop(&GlobalWinMainData->WindowStation, + &CloseDesktopHandle, + &Locked); + + while (hdesk != NULL) + { + DebugLog((DEB_TRACE, "Starting shutdown dialog on desktop %x\n", hdesk)); + + if (Locked) + { + UnlockWindowStation(GlobalWinMainData->WindowStation.hwinsta); + } + + Success = SetThreadDesktop(hdesk); + if (!Success) + { + DebugLog((DEB_TRACE, "Unable to set desktop, %d\n", GetLastError())); + } + + if (Locked) + { + LockWindowStation(GlobalWinMainData->WindowStation.hwinsta); + } + + ShutdownDesktop = GlobalWinMainData->WindowStation.ActiveDesktop; + + + // + // Push the timeout past the shutdown delay, so that we can + // catch the messages we want, without stomping on the timeout + // structures. + // + Result = DialogBoxParam( GetModuleHandle(NULL), + MAKEINTRESOURCE( IDD_SYSTEM_SHUTDOWN ), + NULL, + ShutdownApiDlgProc, + (LPARAM) 0 ); + + DebugLog((DEB_TRACE, "Shutdown Dialog Returned %d\n", Result )); + + + + if (CloseDesktopHandle) + { + CloseDesktop( hdesk ); + } + + if ((Result == SHUTDOWN_SUCCESS) || + (Result == SHUTDOWN_CANCELLED) ) + { + break; + } + + // + // Trickier ones: + // + + if (Result == SHUTDOWN_USER_LOGOFF) + { + if (!AllowLogonDuringShutdown) + { + break; + } + + } + + ShutdownGetPlacement = TRUE; + + hdesk = GetActiveDesktop(&GlobalWinMainData->WindowStation, + &CloseDesktopHandle, + &Locked); + + DebugLog((DEB_TRACE, "Switching to current desktop and restarting dialog\n")); + + } + + // + // The shutdown has either completed or been cancelled + // Reset our globals. + // + // Note we need to reset the shutdown-in-progress flag before + // entering the non-abortable part of shutdown so that anyone + // trying to abort from here on in will get a failure return code. + // + + FreeShutdownData(); + + + Status = RtlEnterCriticalSection(&ShutdownCriticalSection); + Error = RtlNtStatusToDosError(Status); + + if (Error == ERROR_SUCCESS) { + + // + // Reset the global shutdown-in-progress flag + // and check for an abort request. + // + + if (AbortShutdown) { + DoShutdown = FALSE; + } + + ShutdownInProgress = FALSE; + + // + // Leave the critical section + // + + Status = RtlLeaveCriticalSection(&ShutdownCriticalSection); + if (!NT_SUCCESS(Status)) { + Error = RtlNtStatusToDosError(Status); + } + } + + // + // If DoShutdown, update the last gina ret so that + // the shutdown code will know what to do: + // + + if ( DoShutdown ) + { + GlobalWinMainData->LastGinaRet = GinaCode; + + ShutdownHasBegun = TRUE; + } + + + + // + // Tell the caller if he should shut down. + // + + return DoShutdown; + +} + + + +BOOL WINAPI +ShutdownApiDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ) +/*++ + +Routine Description: + + Processes messages for the shutdown dialog + + Dialog returns ERROR_SUCCESS if shutdown should proceed, + ERROR_OPERATION_ABORTED if shutdown should be cancelled. + +--*/ +{ + HMENU hMenu; + + switch (message) { + + case WM_INITDIALOG: + + // + // Add the caller's id to the main message text + // + + InsertClientId(hDlg, IDD_SYSTEM_MESSAGE, UserName, UserDomain); + + // + // Setup the client's message + // + + SetDlgItemText(hDlg, IDD_MESSAGE, ShutdownMessage); + + // + // Remove the close item from the system menu + // + + hMenu = GetSystemMenu(hDlg, FALSE); + DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); + + // + // Position ourselves + // + + if ( ShutdownGetPlacement ) + { + SetWindowPlacement( hDlg, &ShutdownWindowPlacement ); + } + else + { + CentreWindow(hDlg); + } + + SetWindowPos( hDlg, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW ); + + + // + // Start the timer + // + + SetTimer(hDlg, 0, 1000, NULL); // 1 second timer + + // + // Check if it's over before we've even started + // + + if (UpdateTimeToShutdown(hDlg)) { + + // It's already time to shutdown + EndDialog(hDlg, SHUTDOWN_SUCCESS); + } + + // + // Let everyone know what state we're in + // + + GlobalWinMainData->PreviousWinlogonState = GlobalWinMainData->WinlogonState; + GlobalWinMainData->WinlogonState = Winsta_InShutdownDlg; + +// GlobalWinMainData->ShutdownStarted = TRUE; + + return(TRUE); + + + case WM_TIMER: + + // + // Check for abort flag + // + + if (AbortShutdown) { + if (GlobalWinMainData->WinlogonState == Winsta_InShutdownDlg) { + GlobalWinMainData->WinlogonState = GlobalWinMainData->PreviousWinlogonState; + } +// GlobalWinMainData->ShutdownStarted = FALSE; + EndDialog(hDlg, SHUTDOWN_CANCELLED); + return(TRUE); + } + + if ( GlobalWinMainData->WindowStation.ActiveDesktop != ShutdownDesktop ) + { + GetWindowPlacement( hDlg, &ShutdownWindowPlacement ); + EndDialog( hDlg, SHUTDOWN_DESKTOP_SWITCH ); + return( TRUE ); + } + + // + // Update the time delay and check if our time's up + // + + if (!UpdateTimeToShutdown(hDlg)) { + + // + // Keep waiting + // + + return(TRUE); + } + + // + // Shutdown time has arrived. Drop through... + // + + case WLX_WM_SAS: + + + DebugLog((DEB_TRACE, "Sas message received? wParam = %d\n", wParam )); + if ((wParam == WLX_SAS_TYPE_SCRNSVR_TIMEOUT) && + (message == WLX_WM_SAS)) { + + // + // Don't end the dialog if it's just a screen saver timeout + // + + return(TRUE); + + } else if ((wParam == WLX_SAS_TYPE_CTRL_ALT_DEL) && + (message == WLX_WM_SAS)) { + + // + // Also don't end the dialog if it's a Ctrl-Alt-Del + // + + Sleep (1000); + return(TRUE); + + } else { + + // + // If the user logs off, preempt the timeout, restore the state + // + + if (GlobalWinMainData->WinlogonState == Winsta_InShutdownDlg) { + GlobalWinMainData->WinlogonState = GlobalWinMainData->PreviousWinlogonState; + } + + EndDialog(hDlg, SHUTDOWN_SUCCESS); + + return(TRUE); + + } + + } + + // We didn't process this message + return FALSE; + + UNREFERENCED_PARAMETER(lParam); +} + + + + +BOOL +UpdateTimeToShutdown( + HWND hDlg + ) +/*++ + +Routine Description: + + Updates the display of the time to system shutdown. + +Returns: + + TRUE if shutdown time has arrived, otherwise FALSE + +--*/ +{ + NTSTATUS Status; + BOOLEAN Success; + LARGE_INTEGER TimeNow; + ULONG ElapsedSecondsNow; + ULONG ElapsedSecondsAtShutdown; + ULONG SecondsRemaining; + ULONG DaysRemaining; + ULONG HoursRemaining; + ULONG MinutesRemaining; + TCHAR Message[40]; + + // + // Set the shutdown time + // + + Status = NtQuerySystemTime(&TimeNow); + ASSERT(NT_SUCCESS(Status)); + + if (TimeNow.QuadPart >= ShutdownTime.QuadPart) + { + return(TRUE); + } + + Success = RtlTimeToSecondsSince1980(&TimeNow, &ElapsedSecondsNow); + ASSERT(Success); + + Success = RtlTimeToSecondsSince1980(&ShutdownTime, &ElapsedSecondsAtShutdown); + ASSERT(Success); + + SecondsRemaining = ElapsedSecondsAtShutdown - ElapsedSecondsNow; + + // + // Convert the seconds remaining to a string + // + + MinutesRemaining = SecondsRemaining / 60; + HoursRemaining = MinutesRemaining / 60; + DaysRemaining = HoursRemaining / 24; + + SecondsRemaining = SecondsRemaining % 60; + MinutesRemaining = MinutesRemaining % 60; + HoursRemaining = HoursRemaining % 24; + + if (DaysRemaining > 0) { + wsprintf(Message, TEXT("%d days"), DaysRemaining); + } else { + wsprintf(Message, TEXT("%02d:%02d:%02d"), HoursRemaining, MinutesRemaining, SecondsRemaining); + } + + SetDlgItemText(hDlg, IDD_TIMER, Message); + + return(FALSE); +} + + + +ULONG +BaseAbortSystemShutdown( + IN PREGISTRY_SERVER_NAME ServerName + ) +/*++ + +Routine Description: + + Aborts a pending shutdown of this machine. + +Arguments: + + ServerName - Name of machine this server code is running on. (Ignored) + +Return Value: + + Returns ERROR_SUCCESS (0) for success; error-code for failure. + +--*/ + +{ + NTSTATUS Status; + DWORD Error; + + // + // Check the caller has the appropriate privilege + // + + Error = TestClientPrivilege(); + if (Error != ERROR_SUCCESS) { + return(Error); + } + + // + // Enter the critical section so we can look at our globals + // + + Status = RtlEnterCriticalSection(&ShutdownCriticalSection); + if (!NT_SUCCESS(Status)) { + return(RtlNtStatusToDosError(Status)); + } + + + // + // If a shutdown is in progress, set the abort flag + // + + if (ShutdownInProgress) { + AbortShutdown = TRUE; + Error = ERROR_SUCCESS; + } else + { + if ( ShutdownHasBegun ) + { + Error = ERROR_SHUTDOWN_IN_PROGRESS; + } + else + { + Error = ERROR_NO_SHUTDOWN_IN_PROGRESS; + } + } + + // + // Leave the critical section + // + + Status = RtlLeaveCriticalSection(&ShutdownCriticalSection); + if (Error == ERROR_SUCCESS) { + if (!NT_SUCCESS(Status)) { + Error = RtlNtStatusToDosError(Status); + } + } else { + ASSERT(NT_SUCCESS(Status)); + } + + return(Error); + + UNREFERENCED_PARAMETER(ServerName); +} + + + +DWORD +TestClientPrivilege( + VOID + ) +/*++ + +Routine Description: + + Checks if the client has the privilege to perform the requested shutdown. + +Arguments: + + None + +Return Value: + + ERROR_SUCCESS if the client has the appropriate privilege. + + ERROR_ACCESS_DENIED - client does not have the required privilege + +--*/ +{ + NTSTATUS Status, IgnoreStatus; + BOOL LocalConnection; + LUID PrivilegeRequired; + PRIVILEGE_SET PrivilegeSet; + BOOLEAN Privileged; + USER_SESSION_KEY SessionKey; + HANDLE Token; + + UNICODE_STRING SubSystemName; // LATER this should be global + RtlInitUnicodeString(&SubSystemName, L"Win32 Registry/SystemShutdown module"); + + // + // Find out if this is a local connection + // + + Status = RtlGetUserSessionKeyServer(NULL, &SessionKey); + if (NT_SUCCESS(Status)) { + + LocalConnection = (Status == STATUS_LOCAL_USER_SESSION_KEY); + + if (LocalConnection) { + PrivilegeRequired = RtlConvertLongToLuid(SE_SHUTDOWN_PRIVILEGE); + } else { + PrivilegeRequired = RtlConvertLongToLuid(SE_REMOTE_SHUTDOWN_PRIVILEGE); + } + + + // + // See if the client has the required privilege + // + + Status = I_RpcMapWin32Status(RpcImpersonateClient( NULL )); + if (NT_SUCCESS(Status)) { + + PrivilegeSet.PrivilegeCount = 1; + PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY; + PrivilegeSet.Privilege[0].Luid = PrivilegeRequired; + PrivilegeSet.Privilege[0].Attributes = 0; + + Status = NtOpenThreadToken( NtCurrentThread(), + TOKEN_QUERY, + TRUE, + &Token); + if (NT_SUCCESS(Status)) { + + Status = NtPrivilegeCheck(Token, + &PrivilegeSet, + &Privileged); + + if (NT_SUCCESS(Status) || (Status == STATUS_PRIVILEGE_NOT_HELD)) { + + Status = NtPrivilegeObjectAuditAlarm( + &SubSystemName, + NULL, + Token, + 0, + &PrivilegeSet, + Privileged); + } + + IgnoreStatus = NtClose(Token); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + } + + IgnoreStatus = I_RpcMapWin32Status(RpcRevertToSelf()); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } + + + // + // Handle unexpected errors + // + + if (!NT_SUCCESS(Status)) { + return(RtlNtStatusToDosError(Status)); + } + + + // + // If they failed the privilege check, return an error + // + + if (!Privileged) { + return(ERROR_ACCESS_DENIED); + } + + // + // They passed muster + // + + return(ERROR_SUCCESS); +} + + + + +DWORD +GetClientId( + PTSTR *UserName, + PTSTR *UserDomain + ) +/*++ + +Routine Description: + + Gets the name and domain of the caller, allocates and returns pointers + to the information. + + Note we have RPC impersonate the client to discover their ID. + +Arguments: + + UserName - a pointer to a NULL terminated string containing the client's + user name is returned here. + + DomainName - a pointer to a NULL terminated string containing the client's + domain name is returned here. + + The caller should free UserName and DomainName by calling DeleteClientId + +Return Value: + + ERROR_SUCCESS - UserName and UserDomain contain valid pointers + + Other - UserName and UserDomain are invalid + +--*/ +{ + HANDLE TokenHandle; + DWORD cbNeeded; + PTOKEN_USER pUserToken; + BOOL ReturnValue=FALSE; + DWORD cbDomain; + DWORD cbName; + SID_NAME_USE SidNameUse; + DWORD Error; + DWORD IgnoreError; + + // + // Prepare for failure + // + + *UserName = NULL; + *UserDomain = NULL; + + + Error = RpcImpersonateClient(NULL); + if (Error != ERROR_SUCCESS) { + return(Error); + } + + if (OpenThreadToken(GetCurrentThread(), + TOKEN_QUERY, + FALSE, + &TokenHandle)) { + // + // Get the user Sid + // + + if (!GetTokenInformation(TokenHandle, TokenUser, (PVOID)NULL, 0, &cbNeeded)) { + + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + + pUserToken = (PTOKEN_USER)LocalAlloc(LPTR, cbNeeded); + + if (pUserToken != NULL) { + + if (GetTokenInformation(TokenHandle, TokenUser, pUserToken, + cbNeeded, &cbNeeded)) { + + // + // Convert User Sid to name/domain + // + + cbName = 0; + cbDomain = 0; + + if (!LookupAccountSid(NULL, + pUserToken->User.Sid, + NULL, &cbName, + NULL, &cbDomain, + &SidNameUse)) { + + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + + *UserDomain = (PTSTR)LocalAlloc(LPTR, cbDomain*sizeof(TCHAR)); + *UserName = (PTSTR)LocalAlloc(LPTR, cbName*sizeof(TCHAR)); + + if ((*UserDomain != NULL) && (*UserName != NULL)) { + + ReturnValue = LookupAccountSid( + NULL, + pUserToken->User.Sid, + *UserName, &cbName, + *UserDomain, &cbDomain, + &SidNameUse); + } + } + + } + } + + LocalFree(pUserToken); + } + } + } + + CloseHandle(TokenHandle); + } + + + IgnoreError = RpcRevertToSelf(); + ASSERT(IgnoreError == ERROR_SUCCESS); + + + // + // Clean up on failure + // + + if (ReturnValue) { + Error = ERROR_SUCCESS; + } else { + + Error = GetLastError(); + + DeleteClientId(*UserName, *UserDomain); + + *UserName = NULL; + *UserDomain = NULL; + } + + + return(Error); +} + + + + +VOID +DeleteClientId( + PTSTR UserName, + PTSTR UserDomain + ) +/*++ + +Routine Description: + + Frees the client id returned previously by GetClientId + +Arguments: + + UserName - a pointer to the username returned by GetClientId. + + DomainName - a pointer to the domain name eturned by GetClientId + +Return Value: + + None + +--*/ +{ + if (UserName != NULL) { + LocalFree(UserName); + } + + if (UserDomain != NULL) { + LocalFree(UserDomain); + } + +} + + + + +BOOL +InsertClientId( + HWND hDlg, + int ControlId, + PTSTR UserName, + PTSTR UserDomain + ) +/*++ + +Routine Description: + + Takes the text from the specified dialog control, treats it as a printf + formatting string and inserts the user name and domain as the first 2 + string identifiers (%s) + +Arguments: + + UserName - a pointer to the username returned by GetClientId. + + DomainName - a pointer to the domain name eturned by GetClientId + +Return Value: + + TRUE on success, FALSE on failure + +--*/ +{ + DWORD StringLength; + DWORD StringBytes; + PTSTR FormatBuffer; + PTSTR Buffer; + + // + // Allocate space for the formatting string out of the control + // + + StringLength = (DWORD)SendMessage(GetDlgItem(hDlg, ControlId), WM_GETTEXTLENGTH, 0, 0); + StringBytes = (StringLength + 1) * sizeof(TCHAR); // Allow for terminator + + FormatBuffer = (PTSTR)LocalAlloc(LPTR, StringBytes); + if (FormatBuffer == NULL) { + return(FALSE); + } + + // + // Read the format string into the buffer + // + + GetDlgItemText(hDlg, ControlId, FormatBuffer, StringLength); + + // + // Calculate the maximum size of the string we'll create + // i.e. Formatting string + username + userdomain + // + + StringLength += lstrlen(UserName); + StringLength += lstrlen(UserDomain); + + // + // Allocate space for formatted string + // + + StringBytes = (StringLength + 1) * sizeof(TCHAR); // Allow for terminator + + Buffer = (PTSTR)LocalAlloc(LPTR, StringBytes); + if (Buffer == NULL) { + LocalFree(FormatBuffer); + return(FALSE); + } + + // + // Insert the user id into the format string + // + + wsprintf(Buffer, FormatBuffer, UserDomain, UserName); + ASSERT((lstrlen(Buffer) * sizeof(TCHAR)) < StringBytes); + + // + // Replace the control text with our formatted result + // + + SetDlgItemText(hDlg, ControlId, Buffer); + + // + // Tidy up + // + + LocalFree(FormatBuffer); + LocalFree(Buffer); + + + return(TRUE); +} diff --git a/private/windows/gina/winlogon/sysshut.dlg b/private/windows/gina/winlogon/sysshut.dlg new file mode 100644 index 000000000..9c1ff7e24 --- /dev/null +++ b/private/windows/gina/winlogon/sysshut.dlg @@ -0,0 +1,15 @@ +#include "sysshut.h" + +IDD_SYSTEM_SHUTDOWN DIALOG 35, 46, 184, 119 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "System Shutdown" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "This system is shutting down. Please save all work in progress and log off. Any unsaved changes will be lost. This shutdown was initiated by %s\\%s.", + IDD_SYSTEM_MESSAGE, 33, 11, 147, 42 + RTEXT "Time before shutdown :", 102, 21, 59, 85, 8 + GROUPBOX "Message", 104, 30, 73, 146, 38 + LTEXT "Text", IDD_MESSAGE, 36, 83, 137, 25 + LTEXT "00:00:00", IDD_TIMER, 114, 59, 41, 8 + ICON 32513, -1, 7, 12, 18, 20 +END diff --git a/private/windows/gina/winlogon/sysshut.h b/private/windows/gina/winlogon/sysshut.h new file mode 100644 index 000000000..493c05f9c --- /dev/null +++ b/private/windows/gina/winlogon/sysshut.h @@ -0,0 +1,14 @@ +#define IDD_SYSTEM_SHUTDOWN 1300 +#define IDD_TIMER 1303 +#define IDD_MESSAGE 1305 +#define IDD_SYSTEM_MESSAGE 1306 + +BOOLEAN +ShutdownThread( + VOID + ); + +BOOL +InitializeShutdownModule( + PGLOBALS pGlobals + ); diff --git a/private/windows/gina/winlogon/timeout.c b/private/windows/gina/winlogon/timeout.c new file mode 100644 index 000000000..a03daa3c6 --- /dev/null +++ b/private/windows/gina/winlogon/timeout.c @@ -0,0 +1,1248 @@ +/****************************** Module Header ******************************\ +* Module Name: timeout.c +* +* Copyright (c) 1991, Microsoft Corporation +* +* Support routines to implement dialog input timeouts +* +* History: +* 12-05-91 Davidc Created. +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +// +// Define private notification message sent to dialogs on an interrupt +// + +#define WM_DLG_INTERRUPT (WM_USER+600) + +// +// Define value returned by a message box or dialog box when interrupted +// This value must be interpreted using the InterrruptType variable +// + +#define DLG_INTERRUPT 0 // Must be 0 so we can return it from message boxes + +// +// Define the maximum number of nested dialogs we can handle +// + +#define MAX_NESTED_TIMEOUTS 5 +#define TIMEOUT_DEFAULT 120 + +// +// Globals - these will need to put in an instance data structure +// when we have multiple logon threads +// + +// +// Define array used to store window handles who ask for timeout service. +// This array is used as a push down stack to suppport nested timeouts +// + +HWND hwndArray[MAX_NESTED_TIMEOUTS]; +TIMEOUT TimeoutArray[MAX_NESTED_TIMEOUTS]; +PGLOBALS GlobalsArray[MAX_NESTED_TIMEOUTS]; +LONG NestedIndex = 0; + + +// +// Variable that stores the required return code for the top dialog. +// This is only used if we force a dialog to return DLG_INTERRUPT. +// We have to use this mechanism because message boxes will not allow +// you to return any random value. DLG_INTERRUPT is 0 which message +// boxes let through. +// + +int TopDialogReturnCode = DLG_INTERRUPT; + + + +HWND hwndMessageOwner = NULL;// Store the owner of the message box + // we want to find +LPTSTR TimeoutTitle = NULL; // Stores the title of the message box we + // are going to timeout on. +BOOL InputHooksInstalled = FALSE; +BOOL TimeoutOccurred = FALSE; +BOOL DialogActive = FALSE; + +// +// Variables that support the input timeout +// + +int TimerID = 0; +HHOOK hhkMouse; +HHOOK hhkKeyboard; + + +// +// Internal Prototypes +// + +BOOL PushMessageTimeout(HWND hwndOwner, TIMEOUT TimeDelay, LPTSTR Title, PGLOBALS pGlobals); +BOOL PopMessageTimeout(VOID); +BOOL PushTimeout(HWND hwnd, TIMEOUT TimeDelay, PGLOBALS pGlobals); +BOOL PopTimeout(VOID); +BOOL StartTopTimeout(VOID); +HWND GetTopTimeout(PTIMEOUT pTimeDelay); +BOOL SetTopTimeout(HWND hwnd); +BOOL SetTimeout(TIMEOUT TimeDelay); +BOOL CheckTimeout(HWND hwnd); +BOOL SetInputHooks(VOID); +BOOL ReleaseInputHooks(VOID); +LRESULT WINAPI MouseHookfn(int nCode, WPARAM wParam, LPARAM lParam); +LRESULT WINAPI KeyboardHookfn(int nCode, WPARAM wParam, LPARAM lParam); +BOOL StartTimer(TIMEOUT TimeDelay); +WORD WINAPI Timerfn(HWND hwnd, WORD Message, int IDEvent, DWORD dwTime); +BOOL StopTimer(VOID); +BOOL FindMessageBox(VOID); +BOOL WINAPI FindEnumfn(HWND, LPARAM); +TIMEOUT InheritTimeout(TIMEOUT TimeDelay); +BOOL HandleScreenSaverTimeout(HWND hwndTop, TIMEOUT Timeout); +BOOL HandleUserLogoff(PGLOBALS pGlobals, HWND hwndTop, TIMEOUT Timeout, LONG Flags); + + +/***************************************************************************\ +* ForwardMessage +* +* Sends a message to the window on top of the timeout stack +* +* 12-05-91 Davidc Created. +\***************************************************************************/ +#if 0 + +LONG +ForwardMessage( + PGLOBALS pGlobals, + UINT Message, + WPARAM wParam, + LPARAM lParam + ) +{ + TIMEOUT Timeout; + HWND hwndTop = GetTopTimeout(&Timeout); + + // If there is nothing on the stack, dump the message + if (hwndTop == NULL) { + DebugLog((DEB_ERROR, "No-one on the stack to forward message to !")); + ASSERT(FALSE); + return(0); + } + + switch (Message) { + + case WM_SAS: + SendMessage(hwndTop, Message, wParam, lParam); + break; + + case WM_INPUT_TIMEOUT: + + // Check the timeout condition still exists and exit the dialog + if (CheckTimeout(hwndTop)) { + + EndTopDialog(hwndTop, DLG_INPUT_TIMEOUT); + + } + break; + + case WM_SCREEN_SAVER_TIMEOUT: + + // + // Ignore if the current user doesn't have an enabled screen-saver. + // The timeout notification may have been in the message queue when + // we changed user - the new user may not have a screen-saver. + // + + if (ScreenSaverEnabled(pGlobals)) { + HandleScreenSaverTimeout(hwndTop, Timeout); + } + + break; + + case WM_USER_LOGOFF: + + HandleUserLogoff(pGlobals, hwndTop, Timeout, lParam); + + break; + + default: + DebugLog((DEB_ERROR, "Unexpected message supplied to ForwardMessage")); + ASSERT(FALSE); + break; + } + + return(0); +} + + +/***************************************************************************\ +* HandleScreenSaverTimeout +* +* Deal with a screen-saver timeout notification from windows. +* +* If the topmost window has a timeout, the screen-saver timeout simply +* interrupts the dialog and causes it to return DLG_SCREEN_SAVER_TIMEOUT. +* +* If the topmost window has no timeout, the window stack is searched from +* the top for the first window to have the TIMEOUT_SS_NOTIFY bit set. +* This window is then sent a WM_SCREEN_SAVER_TIMEOUT message. +* This allows a non-timeout window to take responsibility for starting +* the screen-saver from any-non timeout windows above it. +* +* Returns TRUE +* +* 12-05-91 Davidc Created. +\***************************************************************************/ +BOOL +HandleScreenSaverTimeout( + HWND hwndTop, + TIMEOUT Timeout + ) +{ + HWND hwndNotify; + LONG Index; + + // + // If top top window has a timeout, it should be interrupted + // + + if (TIMEOUT_VALUE(Timeout) != TIMEOUT_NONE) { + + EndTopDialog(hwndTop, DLG_SCREEN_SAVER_TIMEOUT); + + return(TRUE); // We're done + } + + + // + // The top window has no timeout, search for window with notify bit set + // + + for (Index = NestedIndex - 1; Index >= 0; Index --) { + if (TIMEOUT_NOTIFY(TimeoutArray[Index])) { + // Found a window with the notify bit set + break; + } + } + + // Q.A. + ASSERTMSG("Notify window not found on timeout stack", Index >= 0); + ASSERTMSG("Found notify window with a non-zero timeout !!", + TIMEOUT_VALUE(TimeoutArray[Index]) == TIMEOUT_NONE); + + hwndNotify = hwndArray[Index]; + + ASSERTMSG("Found notify window with a NULL hwnd !!", hwndNotify != NULL); + + // + // Forward the message to the notify window + // + + SendMessage(hwndNotify, WM_SCREEN_SAVER_TIMEOUT, 0, 0); + + return(TRUE); // We've dealt with it +} + +#endif + +/***************************************************************************\ +* MapResultCode +* +* Maps the DLG_INTERRUPT message to the appropriate return code stored +* in TopDialogReturnCode +* +* 03-17-92 Davidc Created. +\***************************************************************************/ + +int +MapResultCode( + int Result, + int TopDialogReturnCode + ) +{ + if (Result == DLG_INTERRUPT) { + + ASSERT(TopDialogReturnCode != DLG_INTERRUPT); + + Result = TopDialogReturnCode; + +#if DBG + // + // Reset the stored return code so we can detect if an interrupt is + // generated without the code being set correctly. + // + + TopDialogReturnCode = DLG_INTERRUPT; +#endif + } + + if (Result == -1) { + DebugLog((DEB_ERROR, "Failed to create dialog or message box")); + } + + return(Result); +} + + +/***************************************************************************\ +* ProcessDialogTimeout +* +* Called as first part of dialog routine that wants to timeout after +* there is no input for a specified time. Simply sets up the hwnd on top +* of our stack. +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +VOID ProcessDialogTimeout( + HWND hwnd, + UINT Message, + DWORD wParam, + LONG lParam) +{ + switch (Message) { + + case WM_INITDIALOG: + if (!SetTopTimeout(hwnd)) { + DebugLog((DEB_ERROR, "ProcessDialogTimeout failed to set the hwnd on top of the timeout stack")); + } + } + DBG_UNREFERENCED_PARAMETER(Message); + DBG_UNREFERENCED_PARAMETER(wParam); + DBG_UNREFERENCED_PARAMETER(lParam); +} + + +/***************************************************************************\ +* PushDialogTimeout +* +* Sets up an input timeout for the dialog box that is just about to be created +* +* Returns TRUE on success, FALSE on failure +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL PushDialogTimeout( + TIMEOUT TimeDelay, + PGLOBALS pGlobals) +{ + TimeDelay = InheritTimeout(TimeDelay); + + // Push the timeout with a NULL hwnd (to be filled in later) + PushTimeout(NULL, TimeDelay, pGlobals); + + + return(TRUE); +} + + +/***************************************************************************\ +* PushMessageTimeout +* +* Sets up an input timeout for the message box that is just about to be created +* +* Returns TRUE on success, FALSE on failure +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL PushMessageTimeout( + HWND hwndOwner, + TIMEOUT TimeDelay, + LPTSTR Title, + PGLOBALS pGlobals) +{ + TimeDelay = InheritTimeout(TimeDelay); + + // Check the last message box has been dealt with + ASSERTMSG("Last timeout message box not identified yet !", TimeoutTitle == NULL); + + // Push the timeout with a NULL hwnd (to be filled in later) + PushTimeout(NULL, TimeDelay, pGlobals); + + // Store the message box title so that when we need to operate on the + // message box, we can go and search for it by title. + + TimeoutTitle = Title; + hwndMessageOwner = hwndOwner; + DialogActive = TRUE; + + return(TRUE); +} + + +/***************************************************************************\ +* PopMessageTimeout +* +* Pops the top timeout from the stack and reenables the timeout for the +* next guy on the stack +* +* Returns TRUE on success, FALSE on failure +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL PopMessageTimeout(VOID) +{ + PopTimeout(); + + // Reset our global in case we never went off to search for the + // message box 'cos we never had to + + TimeoutTitle = NULL; + DialogActive = FALSE; + + return(TRUE); +} + + +/***************************************************************************\ +* FindMessageBox +* +* Searches for the message box that our globals tell us we're looking for. +* +* Returns TRUE if found, otherwise FALSE +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL FindMessageBox(VOID) +{ + BOOL Found; + + Found = !EnumTaskWindows(GetCurrentThreadId(), FindEnumfn, 0); + + if (!Found) { + DebugLog((DEB_ERROR, "Message box window <%s> not found !!!", TimeoutTitle)); + } + + return(Found); +} + + +/***************************************************************************\ +* FindEnumfn +* +* Enumeration function used by FindMessageBox() +* +* Returns FALSE if message box is found, otherwise TRUE +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL WINAPI +FindEnumfn( + HWND hwnd, + LPARAM lParam) +{ + TCHAR Title[MAX_STRING_BYTES]; + + // See if this hwnd is the one + + GetWindowText(hwnd, Title, sizeof(Title)); + + if (lstrcmp(Title, TimeoutTitle) == 0) { + + // Found it + + if (!SetTopTimeout(hwnd)) { + DebugLog((DEB_ERROR, "failed to set top timeout for message box !!")); + } + + // Reset our global + TimeoutTitle = NULL; + + return(FALSE); // Stop enumeration + } + + return(TRUE); // Continue enumeration + + DBG_UNREFERENCED_PARAMETER(lParam); +} + + +/***************************************************************************\ +* InheritTimeout +* +* Converts a timedelay value that is potentially TIMEOUT_CURRENT to +* an independent timedelay value. +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +TIMEOUT InheritTimeout( + TIMEOUT TimeDelay + ) +{ + TIMEOUT ResultantTimeDelay; + + if (TIMEOUT_VALUE(TimeDelay) == TIMEOUT_CURRENT) { + // + // Inherit timedelay from previous window + // + HWND hwndTop = GetTopTimeout(&ResultantTimeDelay); + + if (!hwndTop) + { + DebugLog((DEB_WARN, "Tried to use TIMEOUT_CURRENT with no current timeout\n")); + ResultantTimeDelay = TIMEOUT_DEFAULT; + } + + // Don't inherit the TIMEOUT_SS_NOTIFY bit + ResultantTimeDelay &= TIMEOUT_VALUE_MASK; + + // Set the notify bit to match the one passed in + ResultantTimeDelay |= TIMEOUT_NOTIFY(TimeDelay); + + } else { + // + // No inheritance + // + ResultantTimeDelay = TimeDelay; + } + + return(ResultantTimeDelay); +} + + +/***************************************************************************\ +* PushTimeout +* +* Internal routine to push the timeout information onto a stack +* +* Returns TRUE on success, FALSE on failure +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL PushTimeout( + HWND hwnd, + TIMEOUT TimeDelay, + PGLOBALS pGlobals) +{ + // Check we don't have an outstanding unidentified message box + ASSERTMSG("Last timeout message box not identified yet !", TimeoutTitle == NULL); + + if (NestedIndex < MAX_NESTED_TIMEOUTS) { + +#if DBG + // Check we never push a TIMEOUT_NONE dialog when there's + // a timeout window already on the stack. + // This would be unusual. + + if (TIMEOUT_VALUE(TimeDelay) == TIMEOUT_NONE) { + if (NestedIndex > 0) { + if (TIMEOUT_VALUE(TimeoutArray[NestedIndex-1]) != TIMEOUT_NONE) { + ASSERTMSG("Non-timeout window pushed on top of timeout window !!!", FALSE); + } + } + } +#endif + hwndArray[NestedIndex] = hwnd; + GlobalsArray[NestedIndex] = pGlobals; + TimeoutArray[NestedIndex++] = TimeDelay; + + StartTopTimeout(); + + return(TRUE); + + } else { + + DebugLog((DEB_ERROR, "PushTimeout - Out of input timeout slots !!")); + return(FALSE); + } + +} + + +/***************************************************************************\ +* PopTimeout +* +* Pops the top timeout from the stack and reenables the timeout for the +* next guy on the stack +* +* Returns TRUE on success, FALSE on failure +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL PopTimeout(VOID) +{ + if (NestedIndex <= 0) { + DebugLog((DEB_ERROR, "PopTimeout called with an empty timeout stack !!")); + return(FALSE); + } + + NestedIndex --; + + // This call will disable timeouts on an empty stack + StartTopTimeout(); + + return(TRUE); +} + + +/***************************************************************************\ +* StartTopTimeout +* +* Starts (or restarts) the timeout on the top of the stack. +* If the stack is empty, timeouts are disabled. +* +* Returns TRUE on success, FALSE on failure +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL StartTopTimeout(VOID) +{ + TIMEOUT TimeDelay; + + if (NestedIndex > 0) { + TimeDelay = TimeoutArray[NestedIndex - 1]; + DebugLog((DEB_TRACE_TIMEOUT, "Enabling timeout after %d seconds\n", TIMEOUT_VALUE(TimeDelay))); + } else { + DebugLog((DEB_TRACE_TIMEOUT, "Disabling timeouts\n")); + TimeDelay = TIMEOUT_NONE; // Disable timeouts + } + + SetTimeout(TIMEOUT_VALUE(TimeDelay)); + + return(TRUE); +} + + +/***************************************************************************\ +* GetTopTimeout +* +* Returns the timeout on the top of the stack. +* +* Returns top hwnd and time-delay or NULL on failure +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +HWND GetTopTimeout( + PTIMEOUT pTimeDelay) +{ + HWND hwndTop = NULL; + TIMEOUT TimeDelay = 0; + + if (TimeoutTitle != NULL) { + // Go search for message box before getting the top hwnd + FindMessageBox(); + } + + if (NestedIndex > 0) { + hwndTop = hwndArray[NestedIndex - 1]; + TimeDelay = TimeoutArray[NestedIndex - 1]; + } + + if (pTimeDelay != NULL) { + *pTimeDelay = TimeDelay; + } + + return(hwndTop); +} + +//+--------------------------------------------------------------------------- +// +// Function: TimeoutUpdateTopTimeout +// +// Synopsis: Updates the current timeout, returns FALSE if no window +// active, TRUE if it was updated. +// +// Arguments: [Timeout] -- +// +// History: 3-05-96 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL +TimeoutUpdateTopTimeout( + DWORD Timeout) +{ + if ( DialogActive ) + { + if ( TimeoutTitle != NULL ) + { + FindMessageBox(); + } + + if ( NestedIndex > 0 ) + { + TimeoutArray[ NestedIndex - 1 ] = Timeout; + return( TRUE ); + } + + } + + return( FALSE ); +} + + + +/***************************************************************************\ +* SetTopTimeout +* +* Sets the hwnd on top of the timeout stack to the one specified. +* The top timeout value is left unchanged. +* +* Checks if the existing hwnd is NULL, if not this call fails. +* +* Returns TRUE on success, FALSE on failure. +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL SetTopTimeout( + HWND hwnd) +{ + if (NestedIndex <= 0) { + DebugLog((DEB_ERROR, "SetTopTimeout : Tried to set the top hwnd on an empty stack !!")); + return(FALSE); + } + + if (hwndArray[NestedIndex - 1] != NULL) { + DebugLog((DEB_ERROR, "SetTopTimeout : hwnd at top of stack is not NULL !!")); + return(FALSE); + } + + hwndArray[NestedIndex - 1] = hwnd; + + return(TRUE); +} + + +/***************************************************************************\ +* SetTimeout +* +* Sets an input timeout for the specified window using the specified delay. +* +* If TimeDelay = 0, no timeout is enabled for this window. +* +* Returns TRUE on success, FALSE on failure +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL SetTimeout( + TIMEOUT TimeDelay) +{ + if (TimeDelay != 0) { + + if (!InputHooksInstalled) { + SetInputHooks(); + } + StartTimer(TimeDelay); + + } else { + + if (InputHooksInstalled) { + ReleaseInputHooks(); + } + + StopTimer(); + } + + return (TRUE); +} + + +/***************************************************************************\ +* CheckTimeout +* +* Returns TRUE if a timeout is still occurring for this window, otherwise FALSE +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL CheckTimeout( + HWND hwnd) +{ + if (GetTopTimeout(NULL) != hwnd) { + return(FALSE); + } + + return (TimeoutOccurred); +} + + +/***************************************************************************\ +* StartTimer +* +* Starts (or restarts) the input timer with the specified delay +* +* Returns TRUE on success, FALSE on failure +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL StartTimer( + TIMEOUT TimeDelay) +{ + StopTimer(); + + TimerID = SetTimer(NULL, TimerID, TimeDelay * 1000, (TIMERPROC)Timerfn); + + return(TimerID != 0); +} + + +/***************************************************************************\ +* StopTimer +* +* Stops the input timer and resets the timeout flag +* +* Returns TRUE on success, FALSE on failure +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL StopTimer(VOID) +{ + if (TimerID != 0) { + KillTimer(NULL, TimerID); + TimerID = 0; + } + + TimeoutOccurred = FALSE; + + return(TRUE); +} + + +/***************************************************************************\ +* Timerfn +* +* Called when the timer goes off. +* +* 12-05-91 Davidc Created. +\***************************************************************************/ +WORD WINAPI Timerfn( + HWND hwnd, + WORD Message, + int IDEvent, + DWORD dwTime) +{ + // Set the timeout flag and stop any more timeouts + + TimeoutOccurred = TRUE; + + KillTimer(NULL, TimerID); + TimerID = 0; + + if (GetTopTimeout(NULL) == NULL) { + DebugLog((DEB_ERROR, "Input timer went off, but top timeout has NULL hwnd")); + } + + // Send a message to the window who owns the timeout + // ForwardMessage(NULL, WM_INPUT_TIMEOUT, FALSE, 0); + + DebugLog((DEB_TRACE_TIMEOUT, "Input timer went off, sending TIMEOUT\n")); + if (DialogActive) + { + EndTopDialog(NULL, WLX_DLG_INPUT_TIMEOUT); + } + else + SASRouter(GlobalsArray[NestedIndex - 1], WLX_SAS_TYPE_TIMEOUT); + + return(0); + + DBG_UNREFERENCED_PARAMETER(hwnd); + DBG_UNREFERENCED_PARAMETER(Message); + DBG_UNREFERENCED_PARAMETER(IDEvent); + DBG_UNREFERENCED_PARAMETER(dwTime); +} + + +/***************************************************************************\ +* SetInputHooks +* +* Installs input hooks +* +* Returns TRUE on success, FALSE on failure +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL SetInputHooks(VOID) +{ + InputHooksInstalled = TRUE; + + + hhkMouse = SetWindowsHook(WH_MOUSE, MouseHookfn); + hhkKeyboard = SetWindowsHook(WH_KEYBOARD, KeyboardHookfn); + + return(TRUE); +} + + +/***************************************************************************\ +* ReleaseInputHooks +* +* Uninstalls input hooks +* +* Returns TRUE on success, FALSE on failure +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL ReleaseInputHooks(VOID) +{ + UnhookWindowsHook(WH_MOUSE, MouseHookfn); + UnhookWindowsHook(WH_KEYBOARD, KeyboardHookfn); + + InputHooksInstalled = FALSE; + + return(TRUE); +} + + +/***************************************************************************\ +* MouseHookfn +* +* Called when there is mouse input for this app +* +* 12-05-91 Davidc Created. +\***************************************************************************/ +LRESULT WINAPI +MouseHookfn( + int nCode, + WPARAM wParam, + LPARAM lParam + ) +{ + if (nCode >= 0) { + if (!TimeoutOccurred) { + StartTopTimeout(); + } + } + + return(DefHookProc(nCode, wParam, lParam, &hhkMouse)); +} + + +/***************************************************************************\ +* KeyboardHookfn +* +* Called when there is keyboard input for this app +* +* 12-05-91 Davidc Created. +\***************************************************************************/ +LRESULT WINAPI +KeyboardHookfn( + int nCode, + WPARAM wParam, + LPARAM lParam + ) +{ + if (nCode >= 0) { + if (!TimeoutOccurred) { + StartTopTimeout(); + } + } + + return(DefHookProc(nCode, wParam, lParam, &hhkKeyboard)); +} + + +/***************************************************************************\ +* TimeoutMessageBox +* +* Same as a normal message box, but times out if there is no user input +* for the specified number of seconds +* For convenience, this api takes string resource ids rather than string +* pointers as input. The resources are loaded from the .exe module +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +int TimeoutMessageBoxEx( + PGLOBALS pGlobals, + HWND hwnd, + UINT IdText, + UINT IdCaption, + UINT wType, + TIMEOUT Timeout) +{ + TCHAR CaptionBuffer[MAX_STRING_BYTES]; + PTCHAR Caption = CaptionBuffer; + TCHAR Text[MAX_STRING_BYTES]; + + LoadString(NULL, IdText, Text, MAX_STRING_LENGTH); + + if (IdCaption != 0) { + LoadString(NULL, IdCaption, Caption, MAX_STRING_LENGTH); + } else { + Caption = NULL; + } + + return TimeoutMessageBoxlpstr(pGlobals, hwnd, Text, Caption, wType, Timeout); +} + + +/***************************************************************************\ +* TimeoutMessageBoxlpstr +* +* Same as a normal message box, but times out if there is no user input +* for the specified number of seconds +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +int TimeoutMessageBoxlpstr( + PGLOBALS pGlobals, + HWND hwnd, + LPTSTR Text, + LPTSTR Caption, + UINT wType, + TIMEOUT Timeout) +{ + int Result; + int DlgResult; + +#if DBG + // + // Reset the return code so if we forget to set if before returning + // an interrupt MapResultCode will detect it. + // Also this allows us to check the interrupt type is not overwritten + // by a second interrupt before we clear it. + // + TopDialogReturnCode = DLG_INTERRUPT; +#endif + + // Set up input timeout + PushMessageTimeout(hwnd, Timeout, Caption, pGlobals); + + Result = MessageBox(hwnd, Text, Caption, wType); + + // Re-enable previous timeout + PopMessageTimeout(); + + DlgResult = MapResultCode(Result, TopDialogReturnCode); + +#if DBG + // + // Reset the stored return code so we can detect if an interrupt is + // generated without the code being set. + // + + TopDialogReturnCode = DLG_INTERRUPT; +#endif + + return(DlgResult); +} + + +/***************************************************************************\ +* TimeoutDialogBoxParam +* +* Same as a normal dialog box, but times out if there is no user input +* for the specified number of seconds +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +int +TimeoutDialogBoxParam( + PGLOBALS pGlobals, + HANDLE hInstance, + LPTSTR lpTemplateName, + HWND hwndParent, + DLGPROC lpDialogFunc, + LONG dwInitParam, + TIMEOUT Timeout) +{ + int Result; + MSG msg; + +#if DBG + // + // Reset the return code so if we forget to set if before returning + // an interrupt MapResultCode will detect it. + // Also this allows us to check the interrupt type is not overwritten + // by a second interrupt before we clear it. + // + TopDialogReturnCode = DLG_INTERRUPT; +#endif + + while (TRUE) { + PushDialogTimeout(Timeout, pGlobals); + + Result = DialogBoxParam(hInstance, + lpTemplateName, + hwndParent, + lpDialogFunc, + dwInitParam + ); +#if DBG + if (Result < 0) + { + if ((DWORD) lpTemplateName > 0x00010000) + { + DebugLog((DEB_WARN, "DialogBoxParam(%#x, %ws, %#x, %#x, %#x) failed, error %d\n", + hInstance, lpTemplateName, hwndParent, lpDialogFunc, + dwInitParam, GetLastError() )); + } + else + { + DebugLog((DEB_WARN, "DialogBoxParam(%#x, %#x, %#x, %#x, %#x) failed, error %d\n", + hInstance, lpTemplateName, hwndParent, lpDialogFunc, + dwInitParam, GetLastError() )); + + } + } +#endif + + // Re-enable previous timeout + PopTimeout(); + + // + // If the dialog returned due to WM_QUIT, eat the message + // and do the dialog again. + // + + if (!PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE)) { + break; + } + } + + return(Result); +} + + +/***************************************************************************\ +* TimeoutDialogBoxIndirectParam +* +* Same as a normal dialog box, but times out if there is no user input +* for the specified number of seconds +* +* 05-15-92 Davidc Created. +\***************************************************************************/ + +int +TimeoutDialogBoxIndirectParam( + PGLOBALS pGlobals, + HANDLE hInstance, + LPDLGTEMPLATE Template, + HWND hwndParent, + DLGPROC lpDialogFunc, + LONG dwInitParam, + TIMEOUT Timeout) +{ + int Result; + MSG msg; + + + while (TRUE) { + PushDialogTimeout(Timeout, pGlobals); + + Result = DialogBoxIndirectParam(hInstance, + Template, + hwndParent, + lpDialogFunc, + dwInitParam + ); + + // Re-enable previous timeout + PopTimeout(); + + // + // If the dialog returned due to WM_QUIT, eat the message + // and do the dialog again. + // + + if (!PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE)) { + break; + } + } + + return(Result); +} + + + +/***************************************************************************\ +* EndTopDialog +* +* Interrupts the dialog on top of the timeout stack and causes it to return +* the code specified. +* +* The passed hwnd is that of the caller. This is not used other than as +* a debugging aid. +* +* The dlgresult passed must be one of the interrupt types. +* +* Returns TRUE on success, FALSE on failure. +* +* 12-05-91 Davidc Created. +\***************************************************************************/ + +BOOL EndTopDialog( + HWND hwnd, + int DlgResult + ) +{ + HWND hwndTop = GetTopTimeout(NULL); + + if (hwndTop == NULL) { + DebugLog((DEB_ERROR, "EndTopDialog called with no dialogs on the stack!")); + ASSERT(FALSE); + return(FALSE); + } + +#ifdef WINLOGON_TEST + if (hwndTop != hwnd) { + DebugLog((DEB_ERROR, "Ending top dialog from lower-level dialog!")); + } +#endif + + // + // Check the value has not already been set + // + + ASSERT(TopDialogReturnCode == DLG_INTERRUPT); + + TopDialogReturnCode = DlgResult; + + return (EndDialog(hwndTop, DLG_INTERRUPT)); +} + +BOOL +KillMessageBox( DWORD SasCode ) +{ + DWORD EndCode; + + // + // This will kill any outstanding message boxes due to an incoming + // SAS event. This is used by SendSasToTopWindow when it is forwarding + // along an GINA specific SAS. This kills pending message boxes so that + // the dialog regains control. + // + + if (DialogActive) + { + switch ( SasCode ) + { + case WLX_SAS_TYPE_TIMEOUT: + EndCode = WLX_DLG_INPUT_TIMEOUT ; + break; + + case WLX_SAS_TYPE_SCRNSVR_TIMEOUT: + EndCode = WLX_DLG_SCREEN_SAVER_TIMEOUT ; + break; + + default: + EndCode = WLX_DLG_SAS; + break; + } + + EndTopDialog(NULL, EndCode); + return(TRUE); + } + return(FALSE); +} diff --git a/private/windows/gina/winlogon/timeout.h b/private/windows/gina/winlogon/timeout.h new file mode 100644 index 000000000..8f679d671 --- /dev/null +++ b/private/windows/gina/winlogon/timeout.h @@ -0,0 +1,129 @@ +/****************************** Module Header ******************************\ +* Module Name: timeout.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Defines apis to support dialog and message-box input timeouts +* +* History: +* 12-05-91 Davidc Created. +\***************************************************************************/ + + +// +// Define timeout type - represents timeout value in seconds +// +typedef ULONG TIMEOUT; +typedef TIMEOUT * PTIMEOUT; + + +// +// Define special timeout values +// +// The top bit of the timeout value is the 'notify bit'. +// This bit is only used when the timeout value = TIMEOUT_NONE +// +// When a screen-saver timeout occurs, the timeout stack is searched +// from the top down for the first occurrence of +// 1) A window with a timeout OR +// 2) The first TIMEOUT_NONE window with the notify-bit set. +// +// In case 1), the window is timed-out and returns DLG_SCREEN_SAVER_TIMEOUT. +// +// In case 2), a WM_SCREEN_SAVER_TIMEOUT message is posted to the window. +// +// The notify bit is never inherited. If required, it must be specified in +// addition to TIMEOUT_CURRENT. +// +// NOTE SAS messages are always sent to the topmost timeout window +// +// NOTE User logoff messages cause the top window to return DLG_USER_LOGOFF +// if it has a non-0 timeout, otherwise the window receives a WM_USER_LOGOFF +// message. +// + +#define TIMEOUT_VALUE_MASK (0x0fffffff) +#define TIMEOUT_NOTIFY_MASK (0x10000000) + +#define TIMEOUT_VALUE(t) (t & TIMEOUT_VALUE_MASK) +#define TIMEOUT_NOTIFY(t) (t & TIMEOUT_NOTIFY_MASK) + +#define TIMEOUT_SS_NOTIFY (TIMEOUT_NOTIFY_MASK) +#define TIMEOUT_CURRENT (TIMEOUT_VALUE_MASK) // Use existing timeout +#define TIMEOUT_NONE (0) // Disable input timeout + + + + + + +// +// Exported function prototypes +// + + +LONG ForwardMessage( + PGLOBALS pGlobals, + UINT Message, + WPARAM wParam, + LPARAM lParam + ); + +VOID ProcessDialogTimeout( + HWND hwnd, + UINT Message, + DWORD wParam, + LONG lParam + ); + +int TimeoutMessageBoxEx( + PGLOBALS pGlobals, + HWND hWnd, + UINT IdText, + UINT IdCaption, + UINT wType, + TIMEOUT Timeout + ); + +int TimeoutMessageBoxlpstr( + PGLOBALS pGlobals, + HWND hWnd, + LPTSTR Text, + LPTSTR Caption, + UINT wType, + TIMEOUT Timeout + ); + +int +TimeoutDialogBoxParam( + PGLOBALS pGlobals, + HANDLE hInstance, + LPTSTR lpTemplateName, + HWND hWndParent, + DLGPROC lpDialogFunc, + LONG dwInitParam, + TIMEOUT Timeout + ); + +int +TimeoutDialogBoxIndirectParam( + PGLOBALS pGlobals, + HANDLE hInstance, + LPDLGTEMPLATE Template, + HWND hwndParent, + DLGPROC lpDialogFunc, + LONG dwInitParam, + TIMEOUT Timeout + ); + +BOOL EndTopDialog( + HWND hwnd, + int DlgResult + ); + +BOOL SetTopTimeout(HWND hwnd); + + +BOOL +TimeoutUpdateTopTimeout( + DWORD Timeout); diff --git a/private/windows/gina/winlogon/usrenv.c b/private/windows/gina/winlogon/usrenv.c new file mode 100644 index 000000000..41f7e6d1d --- /dev/null +++ b/private/windows/gina/winlogon/usrenv.c @@ -0,0 +1,564 @@ +/****************************** Module Header ******************************\ +* Module Name: logon.c +* +* Copyright (c) 1992, Microsoft Corporation +* +* Handles logoff dialog. +* +* History: +* 2-25-92 JohanneC Created - +* +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +BOOL +SetGINAEnvVars ( + PGLOBALS pGlobals + ); + + + +BOOL +SetupBasicEnvironment( + PVOID * ppEnv + ) +{ + TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH+1]; + DWORD dwComputerNameSize = MAX_COMPUTERNAME_LENGTH+1; + + if (GetComputerName (szComputerName, &dwComputerNameSize)) { + SetUserEnvironmentVariable(ppEnv, COMPUTERNAME_VARIABLE, (LPTSTR) szComputerName, TRUE); + } + + return(TRUE); +} + +/***************************************************************************\ +* SetupUserEnvironment +* +* Initializes all system and user environment variables, retrieves the user's +* profile, sets current directory... +* +* Returns TRUE on success, FALSE on failure. +* +* History: +* 2-28-92 Johannec Created +* +\***************************************************************************/ +BOOL +SetupUserEnvironment( + PGLOBALS pGlobals + ) +{ + PVOID pEnv = NULL; + TCHAR lpHomeShare[MAX_PATH] = TEXT(""); + TCHAR lpHomePath[MAX_PATH] = TEXT(""); + TCHAR lpHomeDrive[4] = TEXT(""); + TCHAR lpHomeDirectory[MAX_PATH] = TEXT(""); + TCHAR lpUserProfile[MAX_PATH]; + DWORD dwSize = MAX_PATH; + NTSTATUS Status; + + if (!pGlobals->hEventLog) { + // + // Register the event source for winlogon events. + // + pGlobals->hEventLog = RegisterEventSource(NULL, EVENTLOG_SOURCE); + } + + /* + * Create a new environment for the user. + */ + + if (!CreateUserEnvironment(&pEnv)) + { + return(FALSE); + } + + SetupBasicEnvironment(&pEnv); + + /* + * Initialize user's environment. + */ + +#if 0 + SetUserEnvironmentVariable(&pEnv, USERNAME_VARIABLE, (LPTSTR)pGlobals->UserName, TRUE); + SetUserEnvironmentVariable(&pEnv, USERDOMAIN_VARIABLE, (LPTSTR)pGlobals->Domain, TRUE); + + if (pGlobals->Profile->HomeDirectoryDrive.Length && + (pGlobals->Profile->HomeDirectoryDrive.Length + 1) < MAX_PATH) { + lstrcpy(lpHomeDrive, pGlobals->Profile->HomeDirectoryDrive.Buffer); + } + + if (pGlobals->Profile->HomeDirectory.Length && + (pGlobals->Profile->HomeDirectory.Length + 1) < MAX_PATH) { + lstrcpy(lpHomeDirectory, pGlobals->Profile->HomeDirectory.Buffer); + } +#endif + SetHomeDirectoryEnvVars(&pEnv, lpHomeDirectory, + lpHomeDrive, lpHomeShare, lpHomePath); +#if 0 + if (pGlobals->Profile->ProfilePath.Length) { + pGlobals->UserProfile.ProfilePath = + AllocAndExpandEnvironmentStrings(pGlobals->Profile->ProfilePath.Buffer); + } else { + pGlobals->UserProfile.ProfilePath = NULL; + } +#endif + + // + // Load the user's profile into the registry + // + + Status = RestoreUserProfile(pGlobals); + if (Status != STATUS_SUCCESS) { + DebugLog((DEB_ERROR, "restoring the user profile failed")); + return(FALSE); + } + + + // + // Set USERPROFILE environment variable + // + + if (GetUserProfileDirectory (pGlobals->UserProcessData.UserToken, + lpUserProfile, &dwSize)) { + + SetUserEnvironmentVariable(&pEnv, USERPROFILE_VARIABLE, lpUserProfile, TRUE); + } + + + pGlobals->UserProcessData.pEnvironment = pEnv; + + if (pGlobals->UserProcessData.CurrentDirectory = (LPTSTR)Alloc( + sizeof(TCHAR)*(lstrlen(lpHomeDirectory)+1))) + lstrcpy(pGlobals->UserProcessData.CurrentDirectory, lpHomeDirectory); + + // + // Set the GINA environment variables in the registry + // + + SetGINAEnvVars (pGlobals); + + + /* + * Set all windows controls to be the user's settings. + */ + InitSystemParametersInfo(pGlobals, TRUE); + + return(TRUE); + +} + +/***************************************************************************\ +* ResetEnvironment +* +* +* History: +* 2-28-92 Johannec Created +* +\***************************************************************************/ +VOID +ResetEnvironment( + PGLOBALS pGlobals + ) +{ + + // + // If they were logged on as system, all these values will be NULL + // + + if (pGlobals->UserProcessData.CurrentDirectory) { + Free(pGlobals->UserProcessData.CurrentDirectory); + pGlobals->UserProcessData.CurrentDirectory = NULL; + } + if (pGlobals->UserProcessData.pEnvironment) { + RtlDestroyEnvironment(pGlobals->UserProcessData.pEnvironment); + pGlobals->UserProcessData.pEnvironment = NULL; + } + if (pGlobals->UserProfile.ProfilePath) { + Free(pGlobals->UserProfile.ProfilePath); + pGlobals->UserProfile.ProfilePath = NULL; + } + if (pGlobals->UserProfile.PolicyPath) { + Free(pGlobals->UserProfile.PolicyPath); + pGlobals->UserProfile.PolicyPath = NULL; + } + if (pGlobals->UserProfile.NetworkDefaultUserProfile) { + Free(pGlobals->UserProfile.NetworkDefaultUserProfile); + pGlobals->UserProfile.NetworkDefaultUserProfile = NULL; + } + if (pGlobals->UserProfile.ServerName) { + Free(pGlobals->UserProfile.ServerName); + pGlobals->UserProfile.ServerName = NULL; + } + if (pGlobals->UserProfile.Environment) { + Free(pGlobals->UserProfile.Environment); + pGlobals->UserProfile.Environment = NULL; + } + + // + // Reset all windows controls to be the default settings + // + + InitSystemParametersInfo(pGlobals, FALSE); +} + + + + + +/***************************************************************************\ +* OpenHKeyCurrentUser +* +* Opens HKeyCurrentUser to point at the current logged on user's profile. +* +* Returns TRUE on success, FALSE on failure +* +* History: +* 06-16-92 Davidc Created +* +\***************************************************************************/ +BOOL +OpenHKeyCurrentUser( + PGLOBALS pGlobals + ) +{ + DWORD err; + HANDLE ImpersonationHandle; + BOOL Result; + + // + // Make sure HKEY_CURRENT_USER is closed before + // remapping it. + // + + try { + + RegCloseKey(HKEY_CURRENT_USER); + + } except(EXCEPTION_EXECUTE_HANDLER) {}; + + + // + // Get in the correct context before we reference the registry + // + + ImpersonationHandle = ImpersonateUser(&pGlobals->UserProcessData, NULL); + if (ImpersonationHandle == NULL) { + DebugLog((DEB_ERROR, "OpenHKeyCurrentUser failed to impersonate user")); + return(FALSE); + } + + + // + // Access the registry to force HKEY_CURRENT_USER to be re-opened + // + + err = RegEnumKey(HKEY_CURRENT_USER, 0, NULL, 0); + + // + // Return to our own context + // + + Result = StopImpersonating(ImpersonationHandle); + ASSERT(Result); + + + return(TRUE); +} + + + +/***************************************************************************\ +* CloseHKeyCurrentUser +* +* Closes HKEY_CURRENT_USER. +* Any registry reference will automatically re-open it, so this is +* only a token gesture - but it allows the registry hive to be unloaded. +* +* Returns nothing +* +* History: +* 06-16-92 Davidc Created +* +\***************************************************************************/ +VOID +CloseHKeyCurrentUser( + PGLOBALS pGlobals + ) +{ + DWORD err; + + err = RegCloseKey(HKEY_CURRENT_USER); + ASSERT(err == ERROR_SUCCESS); +} + + +/***************************************************************************\ +* FUNCTION: SetEnvironmentULong +* +* PURPOSE: Sets the value of an environment variable to the string +* representation of the passed data. +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 01-12-93 Davidc Created. +* +\***************************************************************************/ + +BOOL +SetEnvironmentULong( + LPTSTR Variable, + ULONG Value + ) +{ + TCHAR Buffer[10]; + int Result; + + Result = _snwprintf(Buffer, sizeof(Buffer)/sizeof(TCHAR), TEXT("%x"), Value); + ASSERT(Result < sizeof(Buffer)); + + return (SetEnvironmentVariable(Variable, Buffer)); +} + + +/***************************************************************************\ +* FUNCTION: SetEnvironmentLargeInt +* +* PURPOSE: Sets the value of an environment variable to the string +* representation of the passed data. +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 01-12-93 Davidc Created. +* +\***************************************************************************/ + +BOOL +SetEnvironmentLargeInt( + LPTSTR Variable, + LARGE_INTEGER Value + ) +{ + TCHAR Buffer[20]; + int Result; + + Result = _snwprintf(Buffer, sizeof(Buffer)/sizeof(TCHAR), TEXT("%x:%x"), Value.HighPart, Value.LowPart); + ASSERT(Result < sizeof(Buffer)); + + return (SetEnvironmentVariable(Variable, Buffer)); +} + +/***************************************************************************\ +* FUNCTION: SetGINAEnvVars +* +* PURPOSE: Sets the environment variables GINA passed back to the +* volatile environment in the user's profile +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 09-26-95 EricFlo Created. +* +\***************************************************************************/ + +#define MAX_VALUE_LEN 1024 + +BOOL +SetGINAEnvVars ( + PGLOBALS pGlobals + ) +{ + + TCHAR szValueName[MAX_VALUE_LEN + 1]; + TCHAR szValue[MAX_VALUE_LEN + 1]; + LPTSTR lpEnv = pGlobals->UserProfile.Environment; + LPTSTR lpEnd, lpBegin, lpTemp; + HKEY hKey = NULL; + DWORD dwDisp; + BOOL bRetVal = FALSE; + DWORD cPercent, len, i; + + + + if (!lpEnv) { + return TRUE; + } + + + if (!OpenHKeyCurrentUser(pGlobals)) { + DebugLog((DEB_ERROR, "SetGINAEnvVars: Failed to open HKeyCurrentUser")); + return FALSE; + } + + + if (RegCreateKeyEx(HKEY_CURRENT_USER, + TEXT("Volatile Environment"), + 0, + NULL, + REG_OPTION_VOLATILE, + KEY_WRITE, + NULL, + &hKey, + &dwDisp) != ERROR_SUCCESS) { + goto Exit; + } + + + + lpEnd = lpBegin = lpEnv; + + for (;;) { + + // + // Skip leading blanks + // + + while (*lpEnd == TEXT(' ')) { + lpEnd++; + } + + lpBegin = lpEnd; + + + + // + // Search for the = sign + // + + while (*lpEnd && *lpEnd != TEXT('=')) { + lpEnd++; + } + + if (!*lpEnd) { + goto Exit; + } + + // + // Null terminate and copy to value name buffer + // + + *lpEnd = TEXT('\0'); + + if (lstrlen(lpBegin) + 1 > MAX_VALUE_LEN) { + goto Exit; + } + + lstrcpy (szValueName, lpBegin); + + + *lpEnd++ = TEXT('='); + + + // + // Trim off any trailing spaces + // + + lpTemp = szValueName + (lstrlen (szValueName) - 1); + + while (*lpTemp && (*lpTemp == TEXT(' ')) ) { + lpTemp--; + } + + lpTemp++; + *lpTemp = TEXT('\0'); + + + + // + // Skip leading blanks before value data + // + + while (*lpEnd == TEXT(' ')) { + lpEnd++; + } + + lpBegin = lpEnd; + + + // + // Search for the null terminator + // + + while (*lpEnd) { + lpEnd++; + } + + if (lstrlen(lpBegin) + 1 > MAX_VALUE_LEN) { + goto Exit; + } + + lstrcpy (szValue, lpBegin); + + + // + // Trim off any trailing spaces + // + + lpTemp = szValue + (lstrlen (szValue) - 1); + + while (*lpTemp && (*lpTemp == TEXT(' ')) ) { + lpTemp--; + } + + lpTemp++; + *lpTemp = TEXT('\0'); + + + + // + // Scan the value data to see if a 2 % signs exist. + // If so, then this is an expand_sz type. + // + + cPercent = 0; + len = lstrlen (szValue); + + for (i = 0; i < len; i++) { + if (szValue[i] == TEXT('%')) { + cPercent++; + } + } + + + // + // Set it in the user profile + // + + RegSetValueEx (hKey, + szValueName, + 0, + (cPercent >= 2) ? REG_EXPAND_SZ : REG_SZ, + (LPBYTE) szValue, + (len + 1) * sizeof(TCHAR)); + + lpEnd++; + + if (!*lpEnd) { + break; + } + + lpBegin = lpEnd; + } + + bRetVal = TRUE; + +Exit: + + if (hKey) { + RegCloseKey (hKey); + } + + CloseHKeyCurrentUser(pGlobals); + + return bRetVal; + +} diff --git a/private/windows/gina/winlogon/usrenv.h b/private/windows/gina/winlogon/usrenv.h new file mode 100644 index 000000000..6891a689d --- /dev/null +++ b/private/windows/gina/winlogon/usrenv.h @@ -0,0 +1,96 @@ +/****************************** Module Header ******************************\ +* Module Name: usrenv.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Define constants user by and apis in usrenv.c +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +#define COLON TEXT(':') +#define BSLASH TEXT('\\') + +// +// Define the source for the event log handle used to log profile failures. +// +#define EVENTLOG_SOURCE TEXT("Winlogon") + + +// +// Value names for for different environment variables +// + +#define PATH_VARIABLE TEXT("PATH") +#define LIBPATH_VARIABLE TEXT("LibPath") +#define OS2LIBPATH_VARIABLE TEXT("Os2LibPath") +#define AUTOEXECPATH_VARIABLE TEXT("AutoexecPath") + +#define HOMEDRIVE_VARIABLE TEXT("HOMEDRIVE") +#define HOMESHARE_VARIABLE TEXT("HOMESHARE") +#define HOMEPATH_VARIABLE TEXT("HOMEPATH") + +#define COMPUTERNAME_VARIABLE TEXT("COMPUTERNAME") +#define USERNAME_VARIABLE TEXT("USERNAME") +#define USERDOMAIN_VARIABLE TEXT("USERDOMAIN") +#define USERPROFILE_VARIABLE TEXT("USERPROFILE") + +// +// Default directories used when the user's home directory does not exist +// or is invalid. +// + +#define ROOT_DIRECTORY TEXT("\\") +#define USERS_DIRECTORY TEXT("\\users") +#define USERS_DEFAULT_DIRECTORY TEXT("\\users\\default") + +#define NULL_STRING TEXT("") + +// +// Defines for Logon script paths. +// + +#define SERVER_SCRIPT_PATH TEXT("\\NETLOGON\\") +#define LOCAL_SCRIPT_PATH TEXT("\\repl\\import\\scripts\\") + + +// +// Prototypes +// + + +BOOL +SetupUserEnvironment( + PGLOBALS pGlobals + ); + +VOID +ResetEnvironment( + PGLOBALS pGlobals + ); + +BOOL +SetupBasicEnvironment( + PVOID * ppEnv + ); + +VOID InitSystemParametersInfo( + PGLOBALS pGlobals, + BOOL bUserLoggedOn + ); + +BOOL +OpenHKeyCurrentUser( + PGLOBALS pGlobals + ); + +VOID +CloseHKeyCurrentUser( + PGLOBALS pGlobals + ); + +VOID +ClearUserProfileData( + PUSER_PROFILE_INFO UserProfileData + ); diff --git a/private/windows/gina/winlogon/usrpro.c b/private/windows/gina/winlogon/usrpro.c new file mode 100644 index 000000000..5f9c4ba7e --- /dev/null +++ b/private/windows/gina/winlogon/usrpro.c @@ -0,0 +1,260 @@ +/****************************** Module Header ******************************\ +* Module Name: logon.c +* +* Copyright (c) 1992, Microsoft Corporation +* +* Handles loading and unloading user profiles. +* +* History: +* 2-25-92 JohanneC Created - +* +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +///////////////////////////////////////////////////////////////////////// +// +// Global variables for this module. +// +///////////////////////////////////////////////////////////////////////// +PSID gGuestsDomainSid = NULL; +SID_IDENTIFIER_AUTHORITY gNtAuthority = SECURITY_NT_AUTHORITY; + + + +/***************************************************************************\ +* IsUserAGuest +* +* returns TRUE if the user is a member of the Guests domain. If so, no +* cached profile should be created and when the profile is not available +* use the user default profile. +* +* History: +* 04-30-93 Johannec Created +* +\***************************************************************************/ +BOOL IsUserAGuest(PGLOBALS pGlobals) +{ + NTSTATUS Status; + ULONG InfoLength; + PTOKEN_GROUPS TokenGroupList; + ULONG GroupIndex; + BOOL FoundGuests; + + + if (TestTokenForAdmin(pGlobals->UserProcessData.UserToken)) { + // + // The user is an admin, ignore the fact that the user could be a + // guest too. + // + return(FALSE); + } + if (!gGuestsDomainSid) { + + // + // Create Guests domain sid. + // + Status = RtlAllocateAndInitializeSid( + &gNtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_GUESTS, + 0, 0, 0, 0, 0, 0, + &gGuestsDomainSid + ); + } + + // + // Test if user is in the Guests domain + // + + // + // Get a list of groups in the token + // + + Status = NtQueryInformationToken( + pGlobals->UserProcessData.UserToken, // Handle + TokenGroups, // TokenInformationClass + NULL, // TokenInformation + 0, // TokenInformationLength + &InfoLength // ReturnLength + ); + + if ((Status != STATUS_SUCCESS) && (Status != STATUS_BUFFER_TOO_SMALL)) { + + DebugLog((DEB_ERROR, "failed to get group info for guests token, status = 0x%lx", Status)); + return(FALSE); + } + + + TokenGroupList = Alloc(InfoLength); + + if (TokenGroupList == NULL) { + DebugLog((DEB_ERROR, "unable to allocate memory for token groups\n")); + return(FALSE); + } + + Status = NtQueryInformationToken( + pGlobals->UserProcessData.UserToken, // Handle + TokenGroups, // TokenInformationClass + TokenGroupList, // TokenInformation + InfoLength, // TokenInformationLength + &InfoLength // ReturnLength + ); + + if (!NT_SUCCESS(Status)) { + DebugLog((DEB_ERROR, "failed to query groups for guests token, status = 0x%lx", Status)); + Free(TokenGroupList); + return(FALSE); + } + + + // + // Search group list for guests alias + // + + FoundGuests = FALSE; + + for (GroupIndex=0; GroupIndex < TokenGroupList->GroupCount; GroupIndex++ ) { + + if (RtlEqualSid(TokenGroupList->Groups[GroupIndex].Sid, gGuestsDomainSid)) { + FoundGuests = TRUE; + break; + } + } + + // + // Tidy up + // + + Free(TokenGroupList); + + return(FoundGuests); +} + +/***************************************************************************\ +* RestoreUserProfile +* +* Downloads the user's profile if possible, otherwise use either cached +* profile or default Windows profile. +* +* Returns TRUE on success, FALSE on failure. +* +* History: +* 2-28-92 Johannec Created +* +\***************************************************************************/ +NTSTATUS +RestoreUserProfile( + PGLOBALS pGlobals + ) +{ + PROFILEINFO pi; + BOOL bSilent = FALSE; + HKEY hKey; + LONG lResult; + DWORD dwType, dwSize; + + + // + // Check if the "NoPopups" flag is set. + // + + lResult = RegOpenKeyEx (HKEY_LOCAL_MACHINE, + TEXT("System\\CurrentControlSet\\Control\\Windows"), + 0, + KEY_READ, + &hKey); + + if (lResult == ERROR_SUCCESS) { + + dwSize = sizeof (bSilent); + + RegQueryValueEx(hKey, + TEXT("NoPopupsOnBoot"), + NULL, + &dwType, + (LPBYTE) &bSilent, + &dwSize); + + RegCloseKey (hKey); + } + + + // + // Load the profile + // + + pi.dwSize = sizeof(PROFILEINFO); + pi.dwFlags = PI_APPLYPOLICY | (bSilent ? PI_NOUI : 0); + pi.lpUserName = pGlobals->UserName; + pi.lpProfilePath = pGlobals->UserProfile.ProfilePath; + pi.lpDefaultPath = pGlobals->UserProfile.NetworkDefaultUserProfile; + pi.lpServerName = pGlobals->UserProfile.ServerName; + pi.lpPolicyPath = pGlobals->UserProfile.PolicyPath; + + if (LoadUserProfile(pGlobals->UserProcessData.UserToken, &pi)) { + pGlobals->UserProfile.hProfile = pi.hProfile; + return ERROR_SUCCESS; + } else { + pGlobals->UserProfile.hProfile = NULL; + } + + // + // Failure + // + + return GetLastError(); + +} + +/***************************************************************************\ +* SaveUserProfile +* +* Saves the user's profile changes. +* +* +* History: +* 2-28-92 Johannec Created +* +\***************************************************************************/ +BOOL +SaveUserProfile( + PGLOBALS pGlobals + ) +{ + HANDLE hEventStart, hEventDone; + + // + // Notify RAS Autodial service that the + // user has is logging off. + // + hEventStart = OpenEvent(EVENT_ALL_ACCESS, FALSE, L"RasAutodialLogoffUser"); + hEventDone = OpenEvent(EVENT_ALL_ACCESS, FALSE, L"RasAutodialLogoffUserDone"); + + if (hEventStart != NULL && hEventDone != NULL) { + + // + // Toggle the event so RAS can save it's settings and + // close it's HKEY_CURRENT_USER key. + // + + SetEvent(hEventStart); + + // + // Autodial will toggle the event again when it's finished. + // + + WaitForSingleObject (hEventDone, 20000); + + CloseHandle(hEventStart); + CloseHandle(hEventDone); + } + + + + return UnloadUserProfile (pGlobals->UserProcessData.UserToken, + pGlobals->UserProfile.hProfile); + +} diff --git a/private/windows/gina/winlogon/usrpro.h b/private/windows/gina/winlogon/usrpro.h new file mode 100644 index 000000000..4296fd9dc --- /dev/null +++ b/private/windows/gina/winlogon/usrpro.h @@ -0,0 +1,29 @@ +/****************************** Module Header ******************************\ +* Module Name: usrpro.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Define apis in usrpro.c +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +// +// Prototypes +// + +BOOL +SaveUserProfile( + PGLOBALS pGlobals + ); + +NTSTATUS +RestoreUserProfile( + PGLOBALS pGlobals + ); + +BOOL +IsUserAGuest( + PGLOBALS pGlobals + ); diff --git a/private/windows/gina/winlogon/win31mig.c b/private/windows/gina/winlogon/win31mig.c new file mode 100644 index 000000000..d310d5350 --- /dev/null +++ b/private/windows/gina/winlogon/win31mig.c @@ -0,0 +1,287 @@ +/****************************** Module Header ******************************\ +* Module Name: logon.c +* +* Copyright (c) 1991, Microsoft Corporation +* +* Implements functions to allow a user to control migration of +* Windows 3.1 configuration information from the .INI, .GRP and REG.DAT +* files into the Windows/NT when the logon for the first time. +* +* History: +* 02-23-93 Stevewo Created. +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +typedef struct _WIN31_MIGRATION_DIALOG { + PGLOBALS pGlobals; + DWORD Win31MigrationFlags; +} WIN31_MIGRATION_DIALOG, * PWIN31_MIGRATION_DIALOG; + +// +// Private prototypes +// + +BOOL WINAPI +Win31MigrationDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ); + +/***************************************************************************\ +* FUNCTION: Windows31Migration +* +* PURPOSE: Checks to see if there is any Windows 3.1 data to +* migrate to the Windows/NT registry, and if so, +* puts up a dialog box for the user to control the +* process and watch it happen. +* +* RETURNS: TRUE/FALSE +* +* HISTORY: +* +* 02-23-93 Stevewo Created. +* +\***************************************************************************/ + +BOOL +Windows31Migration( + PGLOBALS pGlobals + ) +{ + HANDLE ImpersonationHandle; + WIN31_MIGRATION_DIALOG DialogInfo; + DWORD Win31MigrationFlags; + BOOL bDisplayDialog = TRUE; + HKEY hkeyWinlogon; + DWORD dwResult, dwType, dwSize; + + + // + // Get in the correct context before we reference the registry + // + + ImpersonationHandle = ImpersonateUser(&pGlobals->UserProcessData, NULL); + if (ImpersonationHandle == NULL) { + DebugLog((DEB_ERROR, "Win31Migration failed to impersonate user for query\n")); + return(TRUE); + } + + Win31MigrationFlags = QueryWindows31FilesMigration( Win31LogonEvent ); + + StopImpersonating(ImpersonationHandle); + + if (Win31MigrationFlags == 0) { + return(TRUE); + } + + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, WINLOGON_KEY, + 0, KEY_READ, &hkeyWinlogon) == ERROR_SUCCESS) { + + dwSize = sizeof(dwResult); + + if (RegQueryValueEx (hkeyWinlogon, TEXT("win9xupg"), + NULL, &dwType, (LPBYTE) &dwResult, + &dwSize) == ERROR_SUCCESS) { + + if (dwResult) { + bDisplayDialog = FALSE; + } + } + + RegCloseKey (hkeyWinlogon); + } + + + if (!bDisplayDialog) { + return(TRUE); + } + + + DialogInfo.pGlobals = pGlobals; + DialogInfo.Win31MigrationFlags = Win31MigrationFlags; + + return WlxDialogBoxParam(pGlobals, + pGlobals->hInstance, + (LPTSTR) MAKEINTRESOURCE(IDD_WIN31MIG), + NULL, + Win31MigrationDlgProc, + (LPARAM)&DialogInfo + ); +} + + +BOOL WINAPI +Win31MigrationStatusCallback( + IN PWSTR Status, + IN PVOID CallbackParameter + ) +{ + HWND hDlg = (HWND)CallbackParameter; + + return SetDlgItemTextW(hDlg, IDD_WIN31MIG_STATUS, Status); +} + + + +/***************************************************************************\ +* FUNCTION: Win31MigrationDlgProc +* +* PURPOSE: Processes messages for Windows 3.1 Migration dialog +* +* RETURNS: TRUE/FALSE +* +* HISTORY: +* +* 02-23-93 Stevewo Created. +* +\***************************************************************************/ + +BOOL WINAPI +Win31MigrationDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ) +{ + PWIN31_MIGRATION_DIALOG pDialogInfo = (PWIN31_MIGRATION_DIALOG) GetWindowLong(hDlg, GWL_USERDATA); + HANDLE ImpersonationHandle; + UINT idFocus; + + switch (message) { + + case WM_INITDIALOG: + + // + // Make sure that we don't get killed by a stray SAS + // + SetMapperFlag(hDlg, MAPPERFLAG_WINLOGON); + + pDialogInfo = (PWIN31_MIGRATION_DIALOG) lParam; + SetWindowLong(hDlg, GWL_USERDATA, lParam); + + + if (pDialogInfo->Win31MigrationFlags & WIN31_MIGRATE_INIFILES) { + CheckDlgButton(hDlg, idFocus = IDD_WIN31MIG_INIFILES, 1 ); + } else { + CheckDlgButton(hDlg, IDD_WIN31MIG_INIFILES, 0 ); + } + + if (pDialogInfo->Win31MigrationFlags & WIN31_MIGRATE_GROUPS) { + CheckDlgButton(hDlg, idFocus = IDD_WIN31MIG_GROUPS, 1 ); + } else { + CheckDlgButton(hDlg, IDD_WIN31MIG_GROUPS, 0 ); + } + + CentreWindow(hDlg); + SetFocus(GetDlgItem(hDlg, idFocus)); + + + return(TRUE); + + case WM_COMMAND: + + switch (HIWORD(wParam)) { + + default: + + switch (LOWORD(wParam)) { + + case IDOK: + pDialogInfo->Win31MigrationFlags = 0; + if (IsDlgButtonChecked(hDlg, IDD_WIN31MIG_INIFILES) == 1) { + pDialogInfo->Win31MigrationFlags |= WIN31_MIGRATE_INIFILES; + } + + if (IsDlgButtonChecked(hDlg, IDD_WIN31MIG_GROUPS) == 1) { + pDialogInfo->Win31MigrationFlags |= WIN31_MIGRATE_GROUPS; + } + + if (pDialogInfo->Win31MigrationFlags != 0) { + SetCursor( LoadCursor( NULL, IDC_WAIT ) ); + // + // Get in the correct context before we reference the registry + // + + ImpersonationHandle = ImpersonateUser(&pDialogInfo->pGlobals->UserProcessData, NULL); + if (ImpersonationHandle == NULL) { + DebugLog((DEB_ERROR, "Win31MigrationDlgProc failed to impersonate user for update\n")); + EndDialog(hDlg, DLG_FAILURE); + return(TRUE); + } + + OpenProfileUserMapping(); + + SynchronizeWindows31FilesAndWindowsNTRegistry( Win31LogonEvent, + pDialogInfo->Win31MigrationFlags, + Win31MigrationStatusCallback, + hDlg + ); + + + // + // If they migrated the groups, then we need to set + // the group convert flag so links are created. + // + + + if (pDialogInfo->Win31MigrationFlags & WIN31_MIGRATE_GROUPS) { + HKEY hKey; + DWORD dwDisp; + BOOL bRunGrpConv; + + if (RegCreateKeyEx (HKEY_CURRENT_USER, WINLOGON_KEY, + 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, NULL, &hKey, &dwDisp) == + ERROR_SUCCESS) { + + + bRunGrpConv = TRUE; + + RegSetValueEx (hKey, TEXT("RunGrpConv"), 0, REG_DWORD, + (LPBYTE) &bRunGrpConv, sizeof(bRunGrpConv)); + + RegCloseKey (hKey); + } + + } + + CloseProfileUserMapping(); + StopImpersonating(ImpersonationHandle); + SetCursor( LoadCursor( NULL, IDC_ARROW ) ); + } + + EndDialog(hDlg, DLG_SUCCESS); + + if (pDialogInfo->Win31MigrationFlags & WIN31_MIGRATE_INIFILES) { + InitSystemParametersInfo(pDialogInfo->pGlobals, TRUE); + } + + return(TRUE); + + case IDCANCEL: + EndDialog(hDlg, DLG_FAILURE); + return(TRUE); + + } + break; + + } + break; + + case WLX_WM_SAS: + // Ignore it + return(TRUE); + + case WM_PAINT: + break; // Fall through to do default processing + // We may have validated part of the window. + } + + // We didn't process the message + return(FALSE); +} diff --git a/private/windows/gina/winlogon/win31mig.dlg b/private/windows/gina/winlogon/win31mig.dlg new file mode 100644 index 000000000..d7c7bab49 --- /dev/null +++ b/private/windows/gina/winlogon/win31mig.dlg @@ -0,0 +1,19 @@ +1 DLGINCLUDE "win31mig.H" + +IDD_WIN31MIG DIALOG 170, 22, 231, 186 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Windows 3.x Migration" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "You have installed Windows NT into an existing Windows 3.x directory. You have the option of migrating portions of your Windows 3.x environment into the Windows NT environment.", + 807, 7, 17, 217, 29 + AUTOCHECKBOX "Migrate Windows 3.x &WIN.INI and CONTROL.INI", + IDD_WIN31MIG_INIFILES, 16, 77, 172, 10 + AUTOCHECKBOX "Migrate Windows 3.x &Program Manager group files", + IDD_WIN31MIG_GROUPS, 16, 103, 176, 10 + DEFPUSHBUTTON "OK", IDOK, 65, 158, 40, 14 + PUSHBUTTON "Cancel", IDCANCEL, 121, 158, 40, 14 + LTEXT "Please select below the parts you wish to migrate into the Windows NT environment.", + 806, 7, 45, 214, 18 + CTEXT "", IDD_WIN31MIG_STATUS, 16, 126, 174, 13 +END diff --git a/private/windows/gina/winlogon/win31mig.h b/private/windows/gina/winlogon/win31mig.h new file mode 100644 index 000000000..90545c6f4 --- /dev/null +++ b/private/windows/gina/winlogon/win31mig.h @@ -0,0 +1,24 @@ +/****************************** Module Header ******************************\ +* Module Name: win31mig.h +* +* Copyright (c) 1993, Microsoft Corporation +* +* Constants for the Windows 3.1 Migration dialog +* +* NOTE - this file is maintained by dlgedit. Do not edit directly +* +* History: +* 01-08-93 Stevewo Created. +\***************************************************************************/ + +#ifndef RC_INVOKED +BOOL +Windows31Migration( + PGLOBALS pGlobals + ); +#endif /* !RC_INVOKED */ + +#define IDD_WIN31MIG 801 +#define IDD_WIN31MIG_INIFILES 802 +#define IDD_WIN31MIG_GROUPS 803 +#define IDD_WIN31MIG_STATUS 804 diff --git a/private/windows/gina/winlogon/winlogon.c b/private/windows/gina/winlogon/winlogon.c new file mode 100644 index 000000000..bf2e87b75 --- /dev/null +++ b/private/windows/gina/winlogon/winlogon.c @@ -0,0 +1,378 @@ +/****************************** Module Header ******************************\ +* Module Name: winlogon.c +* +* Copyright (c) 1991, Microsoft Corporation +* +* Winlogon main module +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +// +// Global pointer to the pGlobals structure +// + +PGLOBALS g_pGlobals = NULL; +HANDLE hFontThread = NULL; // Handle to the fontloader + +/***************************************************************************\ +* InitializeGlobals +* +* +* History: +* 12-09-91 Davidc Created. +* 6-May-1992 SteveDav Added MM sound initialisation +* 1-03-96 ShawnB Added MM Midi initalisation +\***************************************************************************/ +BOOL InitializeGlobals( + PGLOBALS pGlobals, + HANDLE hInstance) +{ + SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY; + ULONG SidLength; + BOOL Result; + TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH+1]; + DWORD dwComputerNameSize = MAX_COMPUTERNAME_LENGTH+1; + TCHAR szDefaultUser[MAX_PATH]; + + + // + // Get a copy of the computer name in *my* environment, so that we + // can look at it later. + // + + if (GetComputerName (szComputerName, &dwComputerNameSize)) { + SetEnvironmentVariable(COMPUTERNAME_VARIABLE, (LPTSTR) szComputerName); + } + + + // + // Set the default USERPROFILE location + // + + ExpandEnvironmentStrings (TEXT("%SystemRoot%\\Profiles\\Default User"), + szDefaultUser, MAX_PATH); + SetEnvironmentVariable(TEXT("USERPROFILE"), szDefaultUser); + + + // + // Save pGlobals in the global pointer + // + + g_pGlobals = pGlobals; + + + // + // Zero init the structure just to be safe. + // + + RtlZeroMemory(pGlobals, sizeof(GLOBALS)); + + pGlobals->CheckMark = GLOBALS_CHECKMARK; + + // + // Store away our instance handle + // + + pGlobals->hInstance = hInstance; + + // + // Get our sid so it can be put on object ACLs + // + + SidLength = RtlLengthRequiredSid(1); + pGlobals->WinlogonSid = (PSID)Alloc(SidLength); + ASSERTMSG("Winlogon failed to allocate memory for system sid", pGlobals->WinlogonSid != NULL); + + RtlInitializeSid(pGlobals->WinlogonSid, &SystemSidAuthority, 1); + *(RtlSubAuthoritySid(pGlobals->WinlogonSid, 0)) = SECURITY_LOCAL_SYSTEM_RID; + + // + // Initialize (clear) the user process data. + // It will be setup correctly in the first SecurityChangeUser() call + // + + ClearUserProcessData(&pGlobals->UserProcessData); + + // + // Initialize (clear) the user profile data + // It will be setup correctly in the first SecurityChangeUser() call + // + + ClearUserProfileData(&pGlobals->UserProfile); + + + // + // Initialize the multi-media stuff + // + + InitializeSound(pGlobals); + InitializeMidi(pGlobals); + + // + // Initialize the handle to MPR.DLL. This dll must be loaded in the + // user's context because of calls to winreg apis. It is therefore + // loaded after the user has logged on, in SetupUserEnvironment. + // It is used to restore and nuke the user's network connections. + // + + pGlobals->hMPR = NULL; + + // + // Initialize the handle to the eventlog to NULL. This will be initialize + // the first time a user logs on. All profile event logging will use + // this handle. + // + pGlobals->hEventLog = NULL; + + // + // Set the SETUP Booleans + // + pGlobals->SetupType = CheckSetupType() ; + pGlobals->fExecuteSetup = pGlobals->SetupType == SETUPTYPE_FULL +#ifdef INIT_REGISTRY + || pGlobals->SetupType == SETUPTYPE_NETSRW +#endif + || pGlobals->SetupType == SETUPTYPE_NETIDW + || pGlobals->SetupType == SETUPTYPE_UPGRADE; + + + // + // Close the ini file mapping so we get an error if we try + // to use ini apis without explicitly opening a new mapping. + // + + CloseIniFileUserMapping(pGlobals); + + + return TRUE; +} + + + +void +DoSetup(PGLOBALS pGlobals) +{ + BOOL EnableResult; + + ExecuteSetup(pGlobals); + + // + // Enable the shutdown privilege + // This should always succeed - we are either system or a user who + // successfully passed the privilege check in ExitWindowsEx. + // + + EnableResult = EnablePrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE); + ASSERT(EnableResult); + + NtShutdownSystem(ShutdownReboot); + +} + +/***************************************************************************\ +* WinMain +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +int WINAPI WinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpszCmdParam, + int nCmdShow) +{ + GLOBALS Globals; + int Result; + DWORD Win31MigrationFlags; + + // + // Initialize debug support and logging + // + + InitDebugSupport(); + + // + // Make ourselves more important + // + + if (!SetProcessPriority()) + { + ExitProcess( EXIT_INITIALIZATION_ERROR ); + } + + // + // Initialize the globals + // + + InitializeGlobals(&Globals, (HANDLE)hInstance); + + // + // If we are supposed to run setup, then we probably don't + // have a paging file yet. + // + + if (!Globals.fExecuteSetup) + { + CreateTemporaryPageFile(); + } + + // + // Do any DOS-specific initialization: + // + + BootDOS(); + + + // + // Initialize the rest of security + // + + if ( !InitializeSecurity(&Globals) ) + { + ExitProcess( EXIT_SECURITY_INIT_ERROR ); + } + + // + // Determine and load the GINA DLL to use + // + + if ( ! DetermineUserInterface(&Globals) ) + { + ExitProcess( EXIT_GINA_ERROR ); + } + + + + DebugLog((DEB_TRACE_INIT, "Execute system processes:\n")); + + if ( ! ExecSystemProcesses(&Globals) ) + { + ExitProcess( EXIT_SYSTEM_PROCESS_ERROR ); + } + + DebugLog((DEB_TRACE_INIT, "Done with system processes:\n")); + + + // BUGBUG: This can probably go in front of ExecSystemProcesses(). + +#ifdef INIT_REGISTRY + InitializeDefaultRegistry(&Globals); +#endif + + + + // + // Decide what to do about setup. + // + + if (Globals.fExecuteSetup) + { + // + // Run setup, then reboot: This never returns. + // + + DoSetup(&Globals); + } + else + { + // + // Don't go any further if setup didn't complete fully. If this + // machine has not completed setup correctly, this will not return. + // + + CheckForIncompleteSetup(&Globals); + + } + + + + + // + // Initialize the secure attention sequence + // + + if (!SASInit(&Globals)) + { + ExitProcess( EXIT_NO_SAS_ERROR ); + } + + // + // Check to see if there is any WIN.INI or REG.DAT to migrate into + // Windows/NT registry. + // + + Win31MigrationFlags = QueryWindows31FilesMigration( Win31SystemStartEvent ); + if (Win31MigrationFlags != 0) { + SynchronizeWindows31FilesAndWindowsNTRegistry( Win31SystemStartEvent, + Win31MigrationFlags, + NULL, + NULL + ); + InitSystemFontInfo(&Globals); + } + +#ifdef _X86_ + + // + // Do OS/2 Subsystem boot-time migration. + // Only applicable to x86 builds. + // + + Os2MigrationProcedure(); + +#endif + + + // + // Load those pesky fonts: + // + + hFontThread = StartLoadingFonts(); + + + // + // Check if we need to run setup's GUI repair code + // + + CheckForRepairRequest (); + + + + // + // Main logon/logoff loop + // + + + MainLoop(&Globals); + + + + // + // Shutdown the machine + // + + ShutdownMachine(&Globals, Globals.LastGinaRet); + + + // + // Should never get here + // + + DebugLog((DEB_ERROR, "ShutdownMachine failed!\n")); + ASSERT(!"ShutdownMachine failed!"); + + SASTerminate(); + + ExitProcess( EXIT_SHUTDOWN_FAILURE ); + + return( 0 ); + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpszCmdParam); + UNREFERENCED_PARAMETER(nCmdShow); +} diff --git a/private/windows/gina/winlogon/winlogon.h b/private/windows/gina/winlogon/winlogon.h new file mode 100644 index 000000000..581b343ab --- /dev/null +++ b/private/windows/gina/winlogon/winlogon.h @@ -0,0 +1,418 @@ +/****************************** Module Header ******************************\ +* Module Name: winlogon.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Main header file for winlogon +* +* History: +* 12-09-91 Davidc Created. +* 6-May-1992 SteveDav Added space for WINMM sound function +\***************************************************************************/ + + +#ifndef RC_INVOKED +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <ntlsa.h> +#include <ntmsv1_0.h> +#include <lmsname.h> +#endif + + +#include <windows.h> +#include <winbasep.h> +#include <winuserp.h> +#include <winsecp.h> +#include <mmsystem.h> +#include <winwlx.h> + +#ifndef RC_INVOKED + +// +// Exit Codes, that will show up the bugcheck: +// + +#define EXIT_INITIALIZATION_ERROR 1024 +#define EXIT_SECURITY_INIT_ERROR 1025 +#define EXIT_GINA_ERROR 1026 +#define EXIT_SYSTEM_PROCESS_ERROR 1027 +#define EXIT_NO_SAS_ERROR 1028 +#define EXIT_SHUTDOWN_FAILURE 1029 +#define EXIT_GINA_INIT_ERROR 1030 + +// +// Tempoary development aid - system logon capability +// + +#if DBG +#define SYSTEM_LOGON +#endif + +#if DEVL +#define INIT_REGISTRY 1 +#endif + +// +// Temporary development aid - Ctrl-Tasklist starts cmd shell +// +#if DBG +#define CTRL_TASKLIST_SHELL +#endif + + +// +// Define the input timeout delay for logon dialogs (seconds) +// + +#define LOGON_TIMEOUT 120 + + +// +// Define the input timeout delay for the security options dialog (seconds) +// + +#define OPTIONS_TIMEOUT 120 + + +// +// Define the number of days warning we give the user before their password expires +// + +#define PASSWORD_EXPIRY_WARNING_DAYS 14 + + +// +// Define the maximum time we display the 'wait for user to be logged off' +// dialog. This dialog should be interrupted by the user being logged off. +// This timeout is a safety measure in case that doesn't happen because +// of some system error. +// + +#define WAIT_FOR_USER_LOGOFF_DLG_TIMEOUT 120 // seconds + + +// +// Define the account lockout limits +// +// A delay of LOCKOUT_BAD_LOGON_DELAY seconds will be added to +// each failed logon if more than LOCKOUT_BAD_LOGON_COUNT failed logons +// have occurred in the last LOCKOUT_BAD_LOGON_PERIOD seconds. +// + +#define LOCKOUT_BAD_LOGON_COUNT 5 +#define LOCKOUT_BAD_LOGON_PERIOD 60 // seconds +#define LOCKOUT_BAD_LOGON_DELAY 30 // seconds + + + +// +// Define the maximum length of strings we'll use in winlogon +// + +#define MAX_STRING_LENGTH 255 +#define MAX_STRING_BYTES (MAX_STRING_LENGTH + 1) + + +// +// Define the typical length of a string +// This is used as an initial allocation size for most string routines. +// If this is insufficient, the block is reallocated larger and +// the operation retried. i.e. Make this big enough for most strings +// to fit first time. +// + +#define TYPICAL_STRING_LENGTH 60 + + + +// +// Windows object names +// + +#define WINDOW_STATION_NAME TEXT("WinSta0") +#define APPLICATION_DESKTOP_NAME TEXT("Default") +#define WINLOGON_DESKTOP_NAME TEXT("Winlogon") +#define SCREENSAVER_DESKTOP_NAME TEXT("Screen-saver") + +#define APPLICATION_DESKTOP_PATH TEXT("WinSta0\\Default") +#define WINLOGON_DESKTOP_PATH TEXT("WinSta0\\Winlogon") +#define SCREENSAVER_DESKTOP_PATH TEXT("WinSta0\\Screen-saver") + + +// +// Winlogon's registry location +// + +#define WINLOGON_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon" + + +// +// Define the structure that contains information used when starting +// user processes. +// This structure should only be modified by SetUserProcessData() +// + +typedef struct { + HANDLE UserToken; // NULL if no user logged on + PSID UserSid; // == WinlogonSid if no user logged on + PSECURITY_DESCRIPTOR NewProcessSD; + PSECURITY_DESCRIPTOR NewProcessTokenSD; + PSECURITY_DESCRIPTOR NewThreadSD; + PSECURITY_DESCRIPTOR NewThreadTokenSD; + QUOTA_LIMITS Quotas; + LPTSTR CurrentDirectory; + PVOID pEnvironment; +} USER_PROCESS_DATA; +typedef USER_PROCESS_DATA *PUSER_PROCESS_DATA; + +// +// Define the structure that contains information about the user's profile. +// This is used in SetupUserEnvironment and ResetEnvironment (in usrenv.c) +// This data is only valid while a user is logged on. +// + +typedef struct { + LPTSTR ProfilePath; + HANDLE hProfile; + LPTSTR PolicyPath; + LPTSTR NetworkDefaultUserProfile; + LPTSTR ServerName; + LPTSTR Environment; +} USER_PROFILE_INFO; +typedef USER_PROFILE_INFO *PUSER_PROFILE_INFO; + + + +// +// Get any data types defined in module headers and used in GLOBALS +// +#define TYPES_ONLY +#include "ginamgr.h" +#undef TYPES_ONLY + + +typedef enum _WinstaState { + Winsta_PreLoad, + Winsta_Initialize, + Winsta_NoOne, + Winsta_NoOne_Display, + Winsta_NoOne_SAS, + Winsta_LoggedOnUser_StartShell, + Winsta_LoggedOnUser, + Winsta_LoggedOn_SAS, + Winsta_Locked, + Winsta_Locked_Display, + Winsta_Locked_SAS, + Winsta_WaitForLogoff, + Winsta_WaitForShutdown, + Winsta_Shutdown, + Winsta_InShutdownDlg, + Winsta_StateMax +} WinstaState; + +#define IsSASState(State) ((State == Winsta_NoOne_SAS) || \ + (State == Winsta_LoggedOn_SAS) || \ + (State == Winsta_Locked_SAS) ) + +#define IsDisplayState(State) ((State == Winsta_NoOne_Display) || \ + (State == Winsta_Locked_Display) || \ + (State == Winsta_WaitForLogoff) ) + +#if DBG +extern char * StateNames[Winsta_StateMax]; + +#define GetState(x) ( x < (sizeof(StateNames) / sizeof(char *)) ? StateNames[x] : "Invalid") +#endif + +extern BOOLEAN SasMessages; +#define DisableSasMessages() SasMessages = FALSE; +#define TestSasMessages() (SasMessages) +VOID +EnableSasMessages(HWND hWnd); + +BOOL +QueueSasEvent( + DWORD dwSasType); + +BOOL +FetchPendingSas( + PDWORD pSasType); + +BOOL +TestPendingSas(VOID); + +BOOL +KillMessageBox( DWORD SasCode ); + +typedef enum _ActiveDesktops { + Desktop_Winlogon, + Desktop_ScreenSaver, + Desktop_Application, + Desktop_Previous +} ActiveDesktops; + +typedef struct _WinstaDescription { + HWINSTA hwinsta; // Handle to window station + HDESK hdeskWinlogon; // Desktop handles + HDESK hdeskApplication; // "" + HWND hwndAppDesktop; // " + HDESK hdeskScreenSaver; // " + HDESK hdeskPrevious; // Previous + ActiveDesktops ActiveDesktop; // Current, active desktop + ActiveDesktops PreviousDesktop; // Previous desktop + PWSTR pszWinsta; // Name of window station. + PWSTR pszDesktop; // Name of current desktop. + DWORD DesktopLength; // Length of name. + BOOL Locked; // Do I think it's locked? + PVOID Acl; // Stored ACL +} WinstaDescription, * PWinstaDescription; + + +typedef UINT (FAR WINAPI *SOUNDPROC)(); +typedef BOOL (FAR WINAPI *MIDIPROC)(); + + +// +// Define the winlogon global structure. +// + +typedef struct { + DWORD CheckMark; + + // Filled in by InitializeGlobals at startup + HANDLE hInstance; + PSID WinlogonSid; + SOUNDPROC PlaySound; + SOUNDPROC MigrateSoundEvents; + MIDIPROC MigrateMidiUser; + HANDLE hMPR; // handle to MPR.DLL + // Needed to call WNetRestoreConnection when logging + // on the user (in SetupUserEnviron), and to call + // WNetNukeConnections when logging of the user. + // Cannot be called directly beacuse it uses the + // winreg apis, and thus it has to be loaded + // after the profiles are loaded (SetupUserEnvironment). + // Has to be loaded in the user context. + + HANDLE hEventLog; + + // Filled in by InitializeSecurity() at startup + WinstaDescription WindowStation; + PGINASESSION pGina; + WinstaState WinlogonState; + WinstaState PreviousWinlogonState; + BOOL ForwardCAD; + DWORD SasType; + DWORD LastGinaRet; + DWORD LogoffFlags; + BOOL ScreenSaverActive; + BOOL ShutdownStarted; + + // Filled in during startup + BOOL AuditLogFull; + BOOL AuditLogNearFull; + + // Always valid, indicates if we have a user logged on + BOOL UserLoggedOn; + DWORD IniRef; + DWORD TickCount; + + // Always valid - used to start new processes and screen-saver + USER_PROCESS_DATA UserProcessData; + LUID LogonId; + + // Filled in during SetupUserEnvironment, and used in ResetEnvironment. + // Valid only when a user is logged on. + + PWSTR LogonScripts; + PWSTR UserName; + PWSTR Domain; + USER_PROFILE_INFO UserProfile; + + + // + // Value of SetupType from registry + // + + ULONG SetupType ; + + // + // Boolean flag indicating whether SETUP is to be run + // + BOOL fExecuteSetup ; + + WCHAR DesktopName[ TYPICAL_STRING_LENGTH ]; + DWORD DesktopNameLength; + +} GLOBALS; +typedef GLOBALS *PGLOBALS; + +#define GLOBALS_CHECKMARK 0x616f6947 +#define VerifyHandle(h) ((PGLOBALS) (((PGLOBALS)h)->CheckMark == GLOBALS_CHECKMARK) ? h : NULL) + +// +// Global pointer to the pGlobals structure +// + +extern PGLOBALS g_pGlobals; +extern HANDLE hFontThread; +extern BOOL ExitWindowsInProgress ; + + +// +// Define a macro to determine if we're a workstation or not +// This allows easy changes as new product types are added. +// + +#define IsWorkstation(prodtype) (((prodtype) == NtProductWinNt) \ + || ((prodtype) == NtProductServer)) + + +#define DLG_SUCCESS IDOK +#define DLG_FAILURE IDCANCEL + +// +// Include individual module header files +// +#include "wlxutil.h" +#include "regini.h" +#include "logon.h" +#include "loggedon.h" +#include "sas.h" +#include "winutil.h" +#include "sysinit.h" +#include "ginamgr.h" +#include "debug.h" +#include "strings.h" +#include "wlxutil.h" +#include "doslog.h" +#include "regini.h" +#include "secutil.h" +#include "logoff.h" +#include "misc.h" +#include "msgalias.h" +#include "usrpro.h" +#include "usrenv.h" +#include "envvar.h" +#include "monitor.h" +#include "scrnsave.h" +#include "timeout.h" +#include "provider.h" +#include "removabl.h" + + +#ifdef _X86_ +#include "os2ssmig.h" +#endif + +#endif /* !RC_INVOKED */ + +// +// Include resource header files +// +#include "win31mig.h" +#include "wlevents.h" +#include "stringid.h" +#include "dialogs.h" diff --git a/private/windows/gina/winlogon/winutil.c b/private/windows/gina/winlogon/winutil.c new file mode 100644 index 000000000..b9a63bd25 --- /dev/null +++ b/private/windows/gina/winlogon/winutil.c @@ -0,0 +1,834 @@ +/****************************** Module Header ******************************\ +* Module Name: winutil.c +* +* Copyright (c) 1991, Microsoft Corporation +* +* Implements windows specific utility functions +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +#include "precomp.h" + +#if DBG +char * DesktopNames[] = {"Winlogon", "ScreenSaver", "Application", "[Previous]"}; +#define DbgGetDesktopName(x) (x < (sizeof(DesktopNames) / sizeof(char *)) ? DesktopNames[x] : "Unknown!") +#endif + + +HDESK +GetActiveDesktop( + PWinstaDescription pWindowStation, + BOOL * pCloseWhenDone, + BOOL * pLocked) +{ + HDESK hDesk; + ActiveDesktops Desktop; + + Desktop = pWindowStation->ActiveDesktop; + if (Desktop == -1) + { + Desktop = pWindowStation->PreviousDesktop; + } + + switch ( Desktop ) + { + case Desktop_Application: + hDesk = OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED); + *pCloseWhenDone = TRUE; + *pLocked = FALSE; + break; + + case Desktop_Winlogon: + hDesk = pWindowStation->hdeskWinlogon; + *pCloseWhenDone = FALSE; + *pLocked = TRUE; + break; + + case Desktop_ScreenSaver: + hDesk = pWindowStation->hdeskScreenSaver; + *pCloseWhenDone = FALSE; + *pLocked = FALSE; + break; + + default: + DebugLog((DEB_TRACE, "Unknown desktop: %d\n", Desktop)); + *pCloseWhenDone = FALSE; + hDesk = NULL; + *pLocked = FALSE; + break; + + } + + return(hDesk); +} + +BOOL +ToggleDesktopLock( + PWinstaDescription pWindowStation) +{ + BOOL bRet; + + if (pWindowStation->Locked) + { + bRet = UnlockWindowStation( pWindowStation->hwinsta ); + pWindowStation->Locked = FALSE; + } + else + { + bRet = LockWindowStation( pWindowStation->hwinsta ); + pWindowStation->Locked = TRUE; + } + + return( bRet ); +} + +BOOL +SetReturnDesktop( + PWinstaDescription pWindowStation, + PWLX_DESKTOP pDesktop) +{ + WCHAR DesktopName[TYPICAL_STRING_LENGTH]; + DWORD Needed; + PWSTR pszDesktop; + BOOL FreeDesktopString = FALSE; + HDESK hDesk; + + if ( pWindowStation->ActiveDesktop == Desktop_Application ) + { + return( FALSE ); + } + + if ( (pDesktop->Flags & WLX_DESKTOP_NAME) == 0 ) + { + if (!GetUserObjectInformation( pDesktop->hDesktop, + UOI_NAME, + DesktopName, + TYPICAL_STRING_LENGTH, + &Needed ) ) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER ) + { + return( FALSE ); + } + + pszDesktop = LocalAlloc( LMEM_FIXED, Needed ); + + if ( !pszDesktop ) + { + return( FALSE ); + } + + GetUserObjectInformation( pDesktop->hDesktop, + UOI_NAME, + pszDesktop, + Needed, + &Needed ); + + FreeDesktopString = TRUE; + + } + else + { + pszDesktop = DesktopName; + + FreeDesktopString = FALSE; + } + + } + else + { + pszDesktop = pDesktop->pszDesktopName; + + FreeDesktopString = FALSE; + + } + + hDesk = OpenDesktop( pszDesktop, 0, FALSE, MAXIMUM_ALLOWED ); + + if (!hDesk) + { + if (FreeDesktopString) + { + LocalFree( pszDesktop ); + } + return( FALSE ); + } + + CloseDesktop( pWindowStation->hdeskPrevious ); + pWindowStation->hdeskPrevious = hDesk; + + if (FreeDesktopString) + { + LocalFree( pszDesktop ); + } + + return( TRUE ); + +} + +BOOL +SetActiveDesktop( + PWinstaDescription pWindowStation, + ActiveDesktops Desktop) +{ + HDESK hDesk; + HDESK hPrevious; + DWORD LengthNeeded; + + if (Desktop == pWindowStation->ActiveDesktop) + { + return(TRUE); + } + + if (pWindowStation->ActiveDesktop == Desktop_Application) + { + LockWindowStation(pWindowStation->hwinsta); + pWindowStation->hdeskPrevious = OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED); + + if (!GetUserObjectInformation( pWindowStation->hdeskPrevious, + UOI_NAME, + pWindowStation->pszDesktop, + pWindowStation->DesktopLength, + &LengthNeeded) ) + { + if (pWindowStation->DesktopLength != TYPICAL_STRING_LENGTH) + { + LocalFree( pWindowStation->pszDesktop ); + } + pWindowStation->pszDesktop = LocalAlloc( LMEM_FIXED, LengthNeeded ); + if (pWindowStation->pszDesktop) + { + pWindowStation->DesktopLength = LengthNeeded; + + if (!GetUserObjectInformation( pWindowStation->hdeskPrevious, + UOI_NAME, + pWindowStation->pszDesktop, + pWindowStation->DesktopLength, + &LengthNeeded)) + { + pWindowStation->pszDesktop[0] = 0; + } + } + else + { + pWindowStation->DesktopLength = 0; + } + } + + DebugLog((DEB_TRACE, "Source desktop was %ws\n", pWindowStation->pszDesktop)); + } + + switch (Desktop) + { + case Desktop_Winlogon: + hDesk = pWindowStation->hdeskWinlogon; + break; + case Desktop_ScreenSaver: + hDesk = pWindowStation->hdeskScreenSaver; + break; + case Desktop_Application: + if (pWindowStation->hdeskPrevious) + { + hDesk = pWindowStation->hdeskPrevious; + } + else + { + hDesk = pWindowStation->hdeskApplication; + } + break; + default: + DebugLog((DEB_ERROR, "Error: Invalid desktop specified %d\n", Desktop)); + return(FALSE); + } + if (SwitchDesktop(hDesk)) + { + DebugLog((DEB_TRACE, "Switching desktop from %s to %s\n", + DbgGetDesktopName(pWindowStation->ActiveDesktop), + DbgGetDesktopName(Desktop) )); + + pWindowStation->PreviousDesktop = pWindowStation->ActiveDesktop; + pWindowStation->ActiveDesktop = Desktop; + + // + // If we're switching back to the user's desktop, then unlock the + // window station, so that the user can switch desktops again. Also, + // close our handle to the desktop. Note! Unlock before close, so + // that if this is the last handle to the desktop, cleanup can occur + // correctly. + // + + if (pWindowStation->ActiveDesktop == Desktop_Application) + { + UnlockWindowStation(pWindowStation->hwinsta); + if (pWindowStation->hdeskPrevious) + { + DebugLog((DEB_TRACE, "Closing handle %x to users desktop\n", pWindowStation->hdeskPrevious)); + CloseDesktop(pWindowStation->hdeskPrevious); + pWindowStation->hdeskPrevious = NULL; + } + } + + + return(TRUE); + } + DebugLog((DEB_WARN, "Could not switch desktop!\n")); + return(FALSE); +} + + +/***************************************************************************\ +* FUNCTION: AllocAndGetPrivateProfileString +* +* PURPOSE: Allocates memory for and returns pointer to a copy of the +* specified profile string +* The returned string should be freed using Free() +* +* RETURNS: Pointer to copy of profile string or NULL on failure. +* +* HISTORY: +* +* 12-Nov-92 Davidc Created. +* +\***************************************************************************/ + +LPTSTR +AllocAndGetPrivateProfileString( + LPCTSTR lpAppName, + LPCTSTR lpKeyName, + LPCTSTR lpDefault, + LPCTSTR lpFileName + ) +{ + LPTSTR String; + LONG LengthAllocated; + LONG LengthCopied; + + // + // Pick a random buffer length, if it's not big enough reallocate + // it and try again until it is. + // + + LengthAllocated = TYPICAL_STRING_LENGTH; + + String = Alloc(LengthAllocated * sizeof(TCHAR)); + if (String == NULL) { + DebugLog((DEB_ERROR, "AllocAndGetPrivateProfileString : Failed to allocate %d bytes for string", LengthAllocated * sizeof(TCHAR))); + return(NULL); + } + + while (TRUE) { + + LengthCopied = GetPrivateProfileString( lpAppName, + lpKeyName, + lpDefault, + String, + LengthAllocated, + lpFileName + ); + // + // If the returned value is our passed size - 1 (weird way for error) + // then our buffer is too small. Make it bigger and start over again. + // + + if (LengthCopied == (LengthAllocated - 1)) { + + DebugLog((DEB_TRACE, "AllocAndGetPrivateProfileString: Failed with buffer length = %d, reallocating and retrying", LengthAllocated)); + + LengthAllocated *= 2; + String = ReAlloc(String, LengthAllocated * sizeof(TCHAR)); + if (String == NULL) { + DebugLog((DEB_ERROR, "AllocAndGetPrivateProfileString : Failed to reallocate %d bytes for string", LengthAllocated * sizeof(TCHAR))); + break; + } + + // + // Go back and try to read it again + // + + } else { + + // + // Success! + // + + break; + } + + } + + return(String); +} + + +/***************************************************************************\ +* FUNCTION: WritePrivateProfileInt +* +* PURPOSE: Writes out an integer to a profile file +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 12-Nov-92 Davidc Created. +* +\***************************************************************************/ + +BOOL +WritePrivateProfileInt( + LPCTSTR lpAppName, + LPCTSTR lpKeyName, + UINT Value, + LPCTSTR lpFileName + ) +{ + NTSTATUS Status; + TCHAR String[30]; + UNICODE_STRING UniString; + + UniString.MaximumLength = 30; + UniString.Buffer = String; + + Status = RtlIntegerToUnicodeString(Value,10,&UniString); + + if (!NT_SUCCESS(Status)) { + return(FALSE); + } + + return (WritePrivateProfileString(lpAppName, lpKeyName, UniString.Buffer, lpFileName)); + +} + + +/***************************************************************************\ +* FUNCTION: AllocAndExpandEnvironmentStrings +* +* PURPOSE: Allocates memory for and returns pointer to buffer containing +* the passed string expanded to include environment strings +* The returned buffer should be freed using Free() +* +* RETURNS: Pointer to expanded string or NULL on failure. +* +* HISTORY: +* +* 21-Dec-92 Davidc Created. +* +\***************************************************************************/ + +LPTSTR +AllocAndExpandEnvironmentStrings( + LPCTSTR lpszSrc + ) +{ + LPTSTR String; + LONG LengthAllocated; + LONG LengthCopied; + + // + // Pick a random buffer length, if it's not big enough reallocate + // it and try again until it is. + // + + LengthAllocated = lstrlen(lpszSrc) + TYPICAL_STRING_LENGTH; + + String = Alloc(LengthAllocated * sizeof(TCHAR)); + if (String == NULL) { + DebugLog((DEB_ERROR, "AllocAndExpandEnvironmentStrings : Failed to allocate %d bytes for string\n", LengthAllocated * sizeof(TCHAR))); + return(NULL); + } + + while (TRUE) { + + LengthCopied = ExpandEnvironmentStrings( lpszSrc, + String, + LengthAllocated + ); + if (LengthCopied == 0) { + DebugLog((DEB_ERROR, "AllocAndExpandEnvironmentStrings : ExpandEnvironmentStrings failed, error = %d\n", GetLastError())); + Free(String); + String = NULL; + break; + } + + // + // If the buffer was too small, make it bigger and try again + // + + if (LengthCopied > LengthAllocated) { + + DebugLog((DEB_TRACE, "AllocAndExpandEnvironmentStrings: Failed with buffer length = %d, reallocating to %d and retrying (retry should succeed)\n", LengthAllocated, LengthCopied)); + + String = ReAlloc(String, LengthCopied * sizeof(TCHAR)); + LengthAllocated = LengthCopied; + if (String == NULL) { + DebugLog((DEB_ERROR, "AllocAndExpandEnvironmentStrings : Failed to reallocate %d bytes for string", LengthAllocated * sizeof(TCHAR))); + break; + } + + // + // Go back and try to expand the string again + // + + } else { + + // + // Success! + // + + break; + } + + } + + return(String); +} + + +/***************************************************************************\ +* FUNCTION: AllocAndRegEnumKey +* +* PURPOSE: Allocates memory for and returns pointer to buffer containing +* the next registry sub-key name under the specified key +* The returned buffer should be freed using Free() +* +* RETURNS: Pointer to sub-key name or NULL on failure. The reason for the +* error can be obtains using GetLastError() +* +* HISTORY: +* +* 21-Dec-92 Davidc Created. +* +\***************************************************************************/ + +LPTSTR +AllocAndRegEnumKey( + HKEY hKey, + DWORD iSubKey + ) +{ + LPTSTR String; + LONG LengthAllocated; + + // + // Pick a random buffer length, if it's not big enough reallocate + // it and try again until it is. + // + + LengthAllocated = TYPICAL_STRING_LENGTH; + + String = Alloc(LengthAllocated * sizeof(TCHAR)); + if (String == NULL) { + DebugLog((DEB_ERROR, "AllocAndRegEnumKey : Failed to allocate %d bytes for string", LengthAllocated * sizeof(TCHAR))); + return(NULL); + } + + while (TRUE) { + + DWORD Error = RegEnumKey(hKey, iSubKey, String, LengthAllocated); + if (Error == ERROR_SUCCESS) { + break; + } + + if (Error != ERROR_MORE_DATA) { + + if (Error != ERROR_NO_MORE_ITEMS) { + DebugLog((DEB_ERROR, "AllocAndRegEnumKey : RegEnumKey failed, error = %d", Error)); + } + + Free(String); + String = NULL; + SetLastError(Error); + break; + } + + // + // The buffer was too small, make it bigger and try again + // + + DebugLog((DEB_TRACE, "AllocAndRegEnumKey: Failed with buffer length = %d, reallocating and retrying", LengthAllocated)); + + LengthAllocated *= 2; + String = ReAlloc(String, LengthAllocated * sizeof(TCHAR)); + if (String == NULL) { + DebugLog((DEB_ERROR, "AllocAndRegEnumKey : Failed to reallocate %d bytes for string", LengthAllocated * sizeof(TCHAR))); + break; + } + } + + return(String); +} + + +/***************************************************************************\ +* FUNCTION: AllocAndRegQueryValueEx +* +* PURPOSE: Version of RegQueryValueEx that returns value in allocated buffer. +* The returned buffer should be freed using Free() +* +* RETURNS: Pointer to key value or NULL on failure. The reason for the +* error can be obtains using GetLastError() +* +* HISTORY: +* +* 15-Jan-93 Davidc Created. +* +\***************************************************************************/ + +LPTSTR +AllocAndRegQueryValueEx( + HKEY hKey, + LPTSTR lpValueName, + LPDWORD lpReserved, + LPDWORD lpType + ) +{ + LPTSTR String; + DWORD BytesAllocated; + + // + // Pick a random buffer length, if it's not big enough reallocate + // it and try again until it is. + // + + BytesAllocated = TYPICAL_STRING_LENGTH * sizeof(TCHAR); + + String = Alloc(BytesAllocated); + if (String == NULL) { + DebugLog((DEB_ERROR, "AllocAndRegQueryValueEx : Failed to allocate %d bytes for string", BytesAllocated)); + return(NULL); + } + + while (TRUE) { + + DWORD Error; + DWORD BytesReturned = BytesAllocated; + + Error = RegQueryValueEx(hKey, + lpValueName, + lpReserved, + lpType, + (LPBYTE)String, + &BytesReturned); + if (Error == ERROR_SUCCESS) { + break; + } + + if (Error != ERROR_MORE_DATA) { + + DebugLog((DEB_ERROR, "AllocAndRegQueryValueEx : RegQueryValueEx failed, error = %d", Error)); + Free(String); + String = NULL; + SetLastError(Error); + break; + } + + // + // The buffer was too small, make it bigger and try again + // + + DebugLog((DEB_TRACE, "AllocAndRegQueryValueEx: Failed with buffer length = %d bytes, reallocating and retrying", BytesAllocated)); + + BytesAllocated *= 2; + String = ReAlloc(String, BytesAllocated); + if (String == NULL) { + DebugLog((DEB_ERROR, "AllocAndRegQueryValueEx : Failed to reallocate %d bytes for string", BytesAllocated)); + break; + } + } + + return(String); +} + + +/***************************************************************************\ +* CentreWindow +* +* Purpose : Positions a window so that it is centred in its parent +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ +VOID +CentreWindow( + HWND hwnd + ) +{ + RECT rect; + LONG dx, dy; + LONG dxParent, dyParent; + LONG Style; + + // Get window rect + GetWindowRect(hwnd, &rect); + + dx = rect.right - rect.left; + dy = rect.bottom - rect.top; + + // Get parent rect + Style = GetWindowLong(hwnd, GWL_STYLE); + if ((Style & WS_CHILD) == 0) { + + // Return the desktop windows size (size of main screen) + dxParent = GetSystemMetrics(SM_CXSCREEN); + dyParent = GetSystemMetrics(SM_CYSCREEN); + } else { + HWND hwndParent; + RECT rectParent; + + hwndParent = GetParent(hwnd); + if (hwndParent == NULL) { + hwndParent = GetDesktopWindow(); + } + + GetWindowRect(hwndParent, &rectParent); + + dxParent = rectParent.right - rectParent.left; + dyParent = rectParent.bottom - rectParent.top; + } + + // Centre the child in the parent + rect.left = (dxParent - dx) / 2; + rect.top = (dyParent - dy) / 3; + + // Move the child into position + SetWindowPos(hwnd, HWND_TOPMOST, rect.left, rect.top, 0, 0, SWP_NOSIZE); + + SetForegroundWindow(hwnd); +} + + +/***************************************************************************\ +* SetupSystemMenu +* +* Purpose : Does any manipulation required for a dialog system menu. +* Should be called during WM_INITDIALOG processing for a dialog +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ +VOID +SetupSystemMenu( + HWND hDlg + ) +{ + // Remove the Close item from the system menu if we don't + // have a CANCEL button + + if (GetDlgItem(hDlg, IDCANCEL) == NULL) { + + HMENU hMenu = GetSystemMenu(hDlg, FALSE); + + DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); + } + +} + +/***************************************************************************\ +* FUNCTION: OpenIniFileUserMapping +* +* PURPOSE: Forces the ini file mapping apis to reference the current user's +* registry. +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 24-Aug-92 Davidc Created. +* +\***************************************************************************/ + +BOOL +OpenIniFileUserMapping( + PGLOBALS pGlobals + ) +{ + BOOL Result; + HANDLE ImpersonationHandle; + + // + // Impersonate the user + // + + if (pGlobals->IniRef == 0) + { + + ImpersonationHandle = ImpersonateUser(&pGlobals->UserProcessData, NULL); + + if (ImpersonationHandle == NULL) { + DebugLog((DEB_ERROR, "OpenIniFileUserMapping failed to impersonate user")); + return(FALSE); + } + + DebugLog((DEB_TRACE, "Actually opening user mapping. User %s logged on\n", + pGlobals->UserLoggedOn ? "is" : "is not")); + + Result = OpenProfileUserMapping(); + + if (!Result) { + DebugLog((DEB_ERROR, "OpenProfileUserMapping failed, error = %d", GetLastError())); + } + + // + // Revert to being 'ourself' + // + + if (!StopImpersonating(ImpersonationHandle)) { + DebugLog((DEB_ERROR, "OpenIniFileUserMapping failed to revert to self")); + } + + + } + else + { + Result = TRUE; + } + + pGlobals->IniRef++; + + DebugLog((DEB_TRACE, "ProfileUserMapping Refs = %d\n", pGlobals->IniRef)); + + return(Result); +} + +/***************************************************************************\ +* FUNCTION: CloseIniFileUserMapping +* +* PURPOSE: Closes the ini file mapping to the user's registry such +* that future use of the ini apis will fail if they reference +* the user's registry. +* +* RETURNS: Nothing +* +* HISTORY: +* +* 24-Aug-92 Davidc Created. +* +\***************************************************************************/ + +VOID +CloseIniFileUserMapping( + PGLOBALS pGlobals + ) +{ + BOOL Result; + + if (pGlobals->IniRef) + { + if (--pGlobals->IniRef == 0) + { + + DebugLog((DEB_TRACE, "Actually closing user mapping\n")); + + Result = CloseProfileUserMapping(); + + if (!Result) { + DebugLog((DEB_ERROR, "CloseProfileUserMapping failed, error = %d", GetLastError())); + } + + } + + } + + DebugLog((DEB_TRACE, "ProfileUserMapping Refs = %d\n", pGlobals->IniRef)); +} diff --git a/private/windows/gina/winlogon/winutil.h b/private/windows/gina/winlogon/winutil.h new file mode 100644 index 000000000..62f8b76b6 --- /dev/null +++ b/private/windows/gina/winlogon/winutil.h @@ -0,0 +1,127 @@ +/****************************** Module Header ******************************\ +* Module Name: winutil.h +* +* Copyright (c) 1991, Microsoft Corporation +* +* Define windows utility functions +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + + +// +// Exported function prototypes +// + + +BOOL +SetActiveDesktop( + PWinstaDescription pWindowStation, + ActiveDesktops Desktop); + +BOOL +SetReturnDesktop( + PWinstaDescription pWindowStation, + PWLX_DESKTOP pDesktop); + +HDESK +GetActiveDesktop( + PWinstaDescription pWindowStation, + BOOL * pCloseWhenDone, + BOOL * pLocked); + +BOOL +ToggleDesktopLock( + PWinstaDescription pWindowStation); + +BOOL +OpenIniFileUserMapping( + PGLOBALS pGlobals + ); + +VOID +CloseIniFileUserMapping( + PGLOBALS pGlobals + ); + +LPTSTR +AllocAndGetPrivateProfileString( + LPCTSTR lpAppName, + LPCTSTR lpKeyName, + LPCTSTR lpDefault, + LPCTSTR lpFileName + ); + +#define AllocAndGetProfileString(App, Key, Def) \ + AllocAndGetPrivateProfileString(App, Key, Def, NULL) + + +BOOL +WritePrivateProfileInt( + LPCTSTR lpAppName, + LPCTSTR lpKeyName, + UINT Value, + LPCTSTR lpFileName + ); + +#define WriteProfileInt(App, Key, Value) \ + WritePrivateProfileInt(App, Key, Value, NULL) + + +LPTSTR +AllocAndExpandEnvironmentStrings( + LPCTSTR lpszSrc + ); + +LPTSTR +AllocAndRegEnumKey( + HKEY hKey, + DWORD iSubKey + ); + +LPTSTR +AllocAndRegQueryValueEx( + HKEY hKey, + LPTSTR lpValueName, + LPDWORD lpReserved, + LPDWORD lpType + ); + +BOOL +SetEnvironmentULong( + LPTSTR Variable, + ULONG Value + ); + +BOOL +SetEnvironmentLargeInt( + LPTSTR Variable, + LARGE_INTEGER Value + ); + +LPWSTR +EncodeMultiSzW( + IN LPWSTR MultiSz + ); + +VOID +CentreWindow( + HWND hwnd + ); + +VOID +SetupSystemMenu( + HWND hDlg + ); + + +// +// Memory macros +// + +#define Alloc(c) ((PVOID)LocalAlloc(LPTR, c)) +#define ReAlloc(p, c) ((PVOID)LocalReAlloc(p, c, LPTR | LMEM_MOVEABLE)) +#define Free(p) ((VOID)LocalFree(p)) + + diff --git a/private/windows/gina/winlogon/wlevents.mc b/private/windows/gina/winlogon/wlevents.mc new file mode 100644 index 000000000..ff4a2f29d --- /dev/null +++ b/private/windows/gina/winlogon/wlevents.mc @@ -0,0 +1,62 @@ +;/*++ BUILD Version: 0001 // Increment this if a change has global effects +; +;Copyright (c) 1992 Microsoft Corporation +; +;Module Name: +; +; wlevents.h +; +;Abstract: +; +; Definitions for User Profiles Events +; +;Author: +; +; Johannec 12-08-93 +; +;Revision History: +; +;Notes: +; +; This file is generated by the MC tool from the wlevents.mc file. +; +;--*/ +; +; +;#ifndef _PROFEVT_ +;#define _PROFEVT_ +; + + +SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS + Informational=0x1:STATUS_SEVERITY_INFORMATIONAL + Warning=0x2:STATUS_SEVERITY_WARNING + Error=0x3:STATUS_SEVERITY_ERROR + ) + +; +;///////////////////////////////////////////////////////////////////////// +;// +;// Winlogon Events (1000 - 1999) +;// +;///////////////////////////////////////////////////////////////////////// +; + +MessageId=1000 Severity=Error SymbolicName=EVENT_SET_HOME_DIRECTORY_FAILED +Language=English +Failed to set the user's home directory %1. +. + +MessageId=1001 Severity=Informational SymbolicName=EVENT_AUTOCHK_DATA +Language=English +%1 +. + +MessageId=1002 Severity=Informational SymbolicName=EVENT_SHELL_RESTARTED +Language=English +The shell stopped unexpectedly and %1 was restarted. +. + +; +;#endif // _PROFEVT_ +; diff --git a/private/windows/gina/winlogon/wlx.c b/private/windows/gina/winlogon/wlx.c new file mode 100644 index 000000000..1fe4f2ea5 --- /dev/null +++ b/private/windows/gina/winlogon/wlx.c @@ -0,0 +1,2126 @@ +/****************************** Module Header ******************************\ +* Module Name: wlx.c +* +* Copyright (c) 1991, Microsoft Corporation +* +* Winlogon main module +* +* History: +* 12-09-91 Davidc Created. +\***************************************************************************/ + +#include "precomp.h" +#pragma hdrstop + +#define WM_HIDEOURSELVES WM_USER + 600 + + +#if DBG +char * SASTypes[] = { "Timeout", "Ctrl-Alt-Del", "ScreenSaver Timeout", + "ScreenSaver Activity", "User Logoff" }; +#define SASName(x) (x < (sizeof(SASTypes) / sizeof(char *)) ? SASTypes[x] : "User Defined") + +char * WlxRets[] = { "invalid", "Logon", "None", "LockWksta", "Logoff", "Shutdown", + "Pwd Changed", "TaskList", "UnlockWksta", "ForceLogoff", + "Shutdown-PowerOff", "Shutdown-Reboot" }; +#define WlxName(x) (x < (sizeof(WlxRets) / sizeof(char *)) ? WlxRets[x] : "Invalid") +#endif + +#define IsShutdown(x) ((x == WLX_SAS_ACTION_SHUTDOWN) || \ + (x == WLX_SAS_ACTION_SHUTDOWN_REBOOT) || \ + (x == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF) ) + +#define RealFlagsFromStoredFlags(Flags) \ + EWX_LOGOFF | \ + ((Flags & EWX_WINLOGON_OLD_SYSTEM) ? EWX_SYSTEM_CALLER : 0) | \ + ((Flags & EWX_WINLOGON_OLD_SHUTDOWN) ? EWX_SHUTDOWN : 0) | \ + ((Flags & EWX_WINLOGON_OLD_REBOOT) ? EWX_REBOOT : 0) | \ + ((Flags & EWX_WINLOGON_OLD_POWEROFF) ? EWX_POWEROFF : 0) + +#define StoredFlagsFromRealFlags(Flags) \ + EWX_LOGOFF | \ + ((Flags & EWX_SYSTEM_CALLER) ? EWX_WINLOGON_OLD_SYSTEM : 0) | \ + ((Flags & EWX_SHUTDOWN) ? EWX_WINLOGON_OLD_SHUTDOWN : 0) | \ + ((Flags & EWX_REBOOT) ? EWX_WINLOGON_OLD_REBOOT : 0) | \ + ((Flags & EWX_POWEROFF) ? EWX_WINLOGON_OLD_POWEROFF : 0) + + +BOOLEAN SasMessages = TRUE; + +// +// For checking page file +// + +extern TCHAR szMemMan[]; + +extern TCHAR szNoPageFile[]; + +// +// For migration +// + +TCHAR szAdminName[ MAX_STRING_BYTES ]; + +// +// Local Prototypes +// + +int +DoLockWksta( + PGLOBALS pGlobals, + BOOL ScreenSaverInvoked); + +int +DoScreenSaver( + PGLOBALS pGlobals, + BOOL WkstaLocked); + +void +WinsrvNotify( + PGLOBALS pGlobals, + DWORD SasType); + +BOOL +LoggedonDlgInit( + HWND hDlg + ); + + + +//+--------------------------------------------------------------------------- +// +// Function: DropWorkingSet +// +// Synopsis: Reduce working set when we're +// +// Arguments: (none) +// +// History: 11-04-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +void +DropWorkingSet(void) +{ + NTSTATUS Status; + QUOTA_LIMITS Quota; + + Status = NtQueryInformationProcess( NtCurrentProcess(), + ProcessQuotaLimits, + &Quota, + sizeof(QUOTA_LIMITS), + NULL ); + + if (NT_SUCCESS(Status)) + { + Quota.MinimumWorkingSetSize = 0xFFFFFFFF; + Quota.MaximumWorkingSetSize = 0xFFFFFFFF; + + NtSetInformationProcess(NtCurrentProcess(), + ProcessQuotaLimits, + &Quota, + sizeof(QUOTA_LIMITS) ); + } +} + + + +//+--------------------------------------------------------------------------- +// +// Function: InitializeGinaDll +// +// Synopsis: Initializes the gina +// +// Arguments: [pGlobals] -- +// +// History: 10-17-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL +InitializeGinaDll(PGLOBALS pGlobals) +{ + PGINASESSION pGina; + + pGina = pGlobals->pGina; + +#if DBG + if (TEST_FLAG(GinaBreakFlags, BREAK_INITIALIZE)) + { + DebugLog((DEB_TRACE, "About to call WlxInitialize(%ws, 1, NULL, @%#x, @%#x)\n", + pGlobals->WindowStation.pszWinsta, + &WlxDispatchTable, &pGina->pGinaContext)); + + DebugBreak(); + } +#endif + + + // + // Perversely, this may not return. The GINA may in fact call SASNotify + // immediately, so update the state before we go in: + // + + pGlobals->WinlogonState = Winsta_NoOne; + DebugLog((DEB_TRACE_STATE, "InitGina: State is %d %s\n", Winsta_NoOne, GetState(Winsta_NoOne))); + + if (!pGina->pWlxInitialize( pGlobals->WindowStation.pszWinsta, + pGlobals, + NULL, + (PVOID) &WlxDispatchTable, + &pGina->pGinaContext)) + { + // + // If the GINA failed to init, we're dead. bugcheck time: + // + + ExitProcess( EXIT_GINA_INIT_ERROR ); + } + return(TRUE); +} + + + +//+--------------------------------------------------------------------------- +// +// Function: SASRouter +// +// Synopsis: Routes an SAS event to the appropriate recipient +// +// Arguments: [pGlobals] -- +// [SasType] -- +// +// History: 8-24-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +void +SASRouter( PGLOBALS pGlobals, + DWORD SasType ) +{ + + + if (!TestSasMessages()) + { + QueueSasEvent(SasType); + return; + } + + pGlobals->SasType = SasType; + + if (!IsSASState(pGlobals->WinlogonState)) + { + if (IsDisplayState(pGlobals->WinlogonState) || + (pGlobals->WinlogonState == Winsta_WaitForShutdown) || + (pGlobals->WinlogonState == Winsta_InShutdownDlg) || + (pGlobals->WinlogonState == Winsta_Locked)) + { + DebugLog((DEB_TRACE, "In state %s, sending kill message to window\n", + GetState(pGlobals->WinlogonState))); + + if (!SendSasToTopWindow(pGlobals, SasType)) + DebugLog((DEB_WARN, "No window to send SAS notice to?\n")); + } + + // + // If this was a timeout message, + if ((SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT) || + (SasType == WLX_SAS_TYPE_TIMEOUT) ) + { + // + // We do *not* change state on a timeout! + // + return; + } + + ChangeStateForSAS(pGlobals); + + + if (!pGlobals->ScreenSaverActive) + { + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon); + } + + // + // We should be in one of the three base states now: + // + + DebugLog((DEB_TRACE_STATE, "SASRouter: In state %s\n", GetState(pGlobals->WinlogonState))); + switch (pGlobals->WinlogonState) + { + case Winsta_NoOne_SAS: + case Winsta_LoggedOn_SAS: + case Winsta_Locked_SAS: + case Winsta_WaitForLogoff: + case Winsta_WaitForShutdown: + case Winsta_InShutdownDlg: + DisableSasMessages(); + break; + + + default: + DebugLog((DEB_ERROR, "SASRouter: Incorrect state %d, %s.\n", + pGlobals->WinlogonState, GetState(pGlobals->WinlogonState))); + break; + + } + } + else + { + // + // We are already handling an SAS attempt. + // + // Note: This may fail. There may not be a window currently to + // receive the message. Life is tough that way. The SAS will be + // *dropped*. + // + + DebugLog((DEB_TRACE, "Sending SAS %s to top window\n", SASName(SasType))); + + SendSasToTopWindow(pGlobals, SasType); + } + + +} + + +//+--------------------------------------------------------------------------- +// +// Function: CADNotify +// +// Synopsis: Called by sas.c, this is the entrypoint for a Ctrl-Alt-Del +// call. Expanded to handle all notification from winsrv. +// +// Arguments: [pGlobals] -- +// [SasType] -- +// +// Algorithm: +// +// History: 10-17-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +void +CADNotify( + PGLOBALS pGlobals, + DWORD SasType) +{ + DebugLog((DEB_TRACE, "Received SAS from winsrv, code %d (%s)\n", SasType, SASName(SasType))); + if (SasType == WLX_SAS_TYPE_USER_LOGOFF) + { + WinsrvNotify(pGlobals, SasType); + } + else if (pGlobals->ForwardCAD) + { + SASRouter(pGlobals, SasType); + } +} + +PWSTR +AllocAndDuplicateString( + PWSTR pszString) +{ + int len; + PWSTR pszNewString; + + if (!pszString) + { + return(NULL); + } + + len = (wcslen(pszString) + 1) * sizeof(WCHAR); + + pszNewString = LocalAlloc(LMEM_FIXED, len); + if (pszNewString) + { + CopyMemory(pszNewString, pszString, len); + } + + return(pszNewString); + +} + +PWSTR +AllocAndDuplicateStrings( + PWSTR pszStrings) +{ + DWORD len; + PWSTR pszNewStrings; + + if (!pszStrings) + { + return(NULL); + } + + + len = LocalSize (pszStrings); + + pszNewStrings = LocalAlloc(LPTR, len); + if (pszNewStrings) + { + CopyMemory(pszNewStrings, pszStrings, len); + } + + return(pszNewStrings); + +} + + +PVOID +CopyEnvironment( + PVOID pEnv) +{ + MEMORY_BASIC_INFORMATION mbi; + PVOID pNew; + + if (VirtualQueryEx( + GetCurrentProcess(), + pEnv, + &mbi, + sizeof(mbi) ) ) + { + pNew = VirtualAlloc(NULL, + mbi.RegionSize, + MEM_COMMIT, + PAGE_READWRITE); + if (pNew) + { + CopyMemory(pNew, pEnv, mbi.RegionSize); + return(pNew); + } + } + + return(NULL); +} + + + +//+--------------------------------------------------------------------------- +// +// Function: LogonAttempt +// +// Synopsis: Handles a logon attempt. +// +// Arguments: [pGlobals] -- +// +// History: 10-17-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +int +LogonAttempt( + PGLOBALS pGlobals) +{ + DWORD WlxResult; + PGINASESSION pGina; + WLX_MPR_NOTIFY_INFO MprInfo; + PWLX_PROFILE_V2_0 pProfileInfo; + PSID pLogonSid; + DWORD Options; + HANDLE hToken; + HANDLE uh; + int MprRet; + + + pGina = pGlobals->pGina; + + pLogonSid = CreateLogonSid(NULL); + +#if DBG + if (TEST_FLAG(GinaBreakFlags, BREAK_LOGGEDOUT)) + { + DebugLog((DEB_TRACE, "About to call WlxLoggedOutSAS(%#x, %d, @%#x, @%x,\n", + pGina->pGinaContext, pGlobals->SasType, &pGlobals->LogonId, pLogonSid)); + DebugLog((DEB_TRACE, " @%#x, @%#x, @%#x, @%#x)\n", &Options, &hToken, + &MprInfo, &pProfileInfo)); + + DebugBreak(); + } +#endif + + WlxSetTimeout(pGlobals, 120); + + WlxResult = pGina->pWlxLoggedOutSAS(pGina->pGinaContext, + pGlobals->SasType, + &pGlobals->LogonId, + pLogonSid, + &Options, + &hToken, + &MprInfo, + &pProfileInfo ); + + DebugLog((DEB_TRACE, "WlxLoggedOutSAS returned %d, %s\n", WlxResult, WlxName(WlxResult))); + + if (WlxResult != WLX_SAS_ACTION_LOGON ) + { + DebugLog((DEB_TRACE_STATE, "LogonAttempt: Resetting state to %s\n", GetState(Winsta_NoOne))); + + pGlobals->WinlogonState = Winsta_NoOne; + + DeleteLogonSid(pLogonSid); + + return(WlxResult); + } + + // + // Okay, someone logged on. This, this is interesting. + // + + if (MprInfo.pszUserName) + { + pGlobals->UserName = AllocAndDuplicateString(MprInfo.pszUserName); + } + + + MprRet = MprLogonNotify( + pGlobals, + NULL, + MprInfo.pszUserName, + MprInfo.pszDomain, + MprInfo.pszPassword, + MprInfo.pszOldPassword, + &pGlobals->LogonId, + &pGlobals->LogonScripts); + + DestroyMprInfo(&MprInfo); + + // + // Wait on the font loading thread here, so we don't inadvertantly re-enter + // the stuff in user that gets confused. + // + + if ( hFontThread ) + { + WaitForSingleObject( hFontThread, INFINITE ); + + CloseHandle( hFontThread ); + + hFontThread = NULL; + + } + + SecurityChangeUser(pGlobals, hToken, NULL, pLogonSid, TRUE); + + if (!TEST_FLAG(Options, WLX_LOGON_OPT_NO_PROFILE)) + { + if (pProfileInfo) { + + if (pProfileInfo->pszProfile) + { + pGlobals->UserProfile.ProfilePath = + AllocAndExpandEnvironmentStrings(pProfileInfo->pszProfile); + LocalFree(pProfileInfo->pszProfile); + } + else + { + pGlobals->UserProfile.ProfilePath = NULL; + } + + if (pProfileInfo->dwType >= WLX_PROFILE_TYPE_V2_0) { + if (pProfileInfo->pszPolicy) + { + pGlobals->UserProfile.PolicyPath = + AllocAndDuplicateString(pProfileInfo->pszPolicy); + + LocalFree(pProfileInfo->pszPolicy); + } + else + { + pGlobals->UserProfile.PolicyPath = NULL; + } + + if (pProfileInfo->pszNetworkDefaultUserProfile) + { + pGlobals->UserProfile.NetworkDefaultUserProfile = + AllocAndDuplicateString(pProfileInfo->pszNetworkDefaultUserProfile); + + LocalFree(pProfileInfo->pszNetworkDefaultUserProfile); + } + else + { + pGlobals->UserProfile.NetworkDefaultUserProfile = NULL; + } + + if (pProfileInfo->pszServerName) + { + pGlobals->UserProfile.ServerName = + AllocAndDuplicateString(pProfileInfo->pszServerName); + + LocalFree(pProfileInfo->pszServerName); + } + else + { + pGlobals->UserProfile.ServerName = NULL; + } + + if (pProfileInfo->pszEnvironment) + { + pGlobals->UserProfile.Environment = + AllocAndDuplicateStrings(pProfileInfo->pszEnvironment); + + LocalFree(pProfileInfo->pszEnvironment); + } + else + { + pGlobals->UserProfile.Environment = NULL; + } + + + } else { + + pGlobals->UserProfile.PolicyPath = NULL; + pGlobals->UserProfile.NetworkDefaultUserProfile = NULL; + pGlobals->UserProfile.ServerName = NULL; + pGlobals->UserProfile.Environment = NULL; + } + + } + + DebugLog((DEB_TRACE_PROFILE, "Using initial profile path of %ws\n", pGlobals->UserProfile.ProfilePath)); + + LocalFree(pProfileInfo); + + // + // Load profile, set environment variables, etc. + // + + if (SetupUserEnvironment(pGlobals)) + { + OpenIniFileUserMapping(pGlobals); + + uh = ImpersonateUser(&pGlobals->UserProcessData, NULL); + + SetWindowStationUser(pGlobals->WindowStation.hwinsta, &pGlobals->LogonId, + pGlobals->UserProcessData.UserSid, + RtlLengthSid(pGlobals->UserProcessData.UserSid)); + + StopImpersonating(uh); + + // + // Update the window station lock so that apps can start. + // + + UnlockWindowStation(pGlobals->WindowStation.hwinsta); + LockWindowStation(pGlobals->WindowStation.hwinsta); + + // + // allocate floppies and CDRoms (if so configured) + // + + RmvAllocateRemovableMedia( pLogonSid ); + } + else + { + // + // Whoops, something went wrong. we *must* log the user + // out. We do this by passing LOGOFF back to mainloop. + // + + WlxResult = WLX_SAS_ACTION_LOGOFF; + } + + + } + + return(WlxResult); + +} + +/****************************************************************************\ +* +* FUNCTION: DisplayPreShellLogonMessages +* +* PURPOSE: Displays any security warnings to the user after a successful logon +* The messages are displayed before the shell starts +* +* RETURNS: DLG_SUCCESS - the dialogs were displayed successfully. +* DLG_INTERRUPTED() - a set defined in winlogon.h +* +* NOTE: Screen-saver timeouts are handled by our parent dialog so this +* routine should never return DLG_SCREEN_SAVER_TIMEOUT +* +* HISTORY: +* +* 12-09-91 Davidc Created. +* +\****************************************************************************/ + +int +DisplayPreShellLogonMessages( + PGLOBALS pGlobals + ) +{ + int Result; + + if (PageFilePopup) { + HKEY hkeyMM; + DWORD dwTempFile, cbTempFile, dwType; + + // + // WinLogon created a temp page file. If a previous user has not + // created a real one already, then inform this user to do so. + // + + 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) { + + WlxSetTimeout(pGlobals, TIMEOUT_NONE); + + Result = TimeoutMessageBox( + pGlobals, + NULL, + IDS_NO_PAGING_FILE, + IDS_LIMITED_RESOURCES, + MB_OK | MB_ICONSTOP + ); + + if (Result == WLX_DLG_INPUT_TIMEOUT) { + return(Result); + } + } + } + + return(DLG_SUCCESS); +} + +//+--------------------------------------------------------------------------- +// +// Function: DoStartShell +// +// Synopsis: +// +// Effects: +// +// Arguments: [pGlobals] -- +// +// Requires: +// +// Returns: +// +// Signals: +// +// Modifies: +// +// Algorithm: +// +// History: 9-30-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL +DoStartShell( + PGLOBALS pGlobals + ) +{ + PGINASESSION pGina; + HANDLE hImp; + PVOID pNewEnvironment; + + (void) DisplayPreShellLogonMessages(pGlobals); + + // + // If not logging in as Guest, System or Administrator then check for + // migration of Windows 3.1 configuration inforation. + // + + if (szAdminName[ 0 ] == TEXT('\0')) + { + LoadString(NULL, IDS_ADMIN_ACCOUNT_NAME, szAdminName, sizeof(szAdminName)); + } + + + if (!IsUserAGuest(pGlobals) && + _wcsicmp(pGlobals->UserName, szAdminName) + ) + { + Windows31Migration(pGlobals); + } + + + pGina = pGlobals->pGina; + + + // + // Play the user's logon sound + // + + if (pGlobals->PlaySound || + pGlobals->MigrateSoundEvents || + pGlobals->MigrateMidiUser) + { + BOOL fBeep; + + if (OpenIniFileUserMapping(pGlobals)) + { + hImp = ImpersonateUser(&pGlobals->UserProcessData, NULL); + + // + // Migrate Users MIDI settings + // + + if (pGlobals->MigrateMidiUser) { + (*(pGlobals->MigrateMidiUser))(); + } + + if (pGlobals->MigrateSoundEvents) { + (*(pGlobals->MigrateSoundEvents))(); + } + + if (pGlobals->PlaySound) { + + // + // Whenever a user logs in, have WINMM.DLL check if there + // are any sound events within the [SOUNDS] section of + // CONTROL.INI that haven't been ported into HKCU/AppEvents. + // If there are, migrate those schemes to their new home. + // This must be done before the upcoming PlaySound() call, + // as PlaySound() uses the HKCU/AppEvents schemes-listing + // to resolve an SND_ALIAS_ID request. + // + + if (!SystemParametersInfo(SPI_GETBEEP, 0, &fBeep, FALSE)) { + // Failed to get hold of beep setting. Should we be + // noisy or quiet? We have to choose one value... + fBeep = TRUE; + } + + if (fBeep) { + (*(pGlobals->PlaySound))((LPCSTR)SND_ALIAS_SYSTEMSTART, + NULL, + SND_ALIAS_ID | SND_ASYNC | SND_NODEFAULT); + } + } + + StopImpersonating(hImp); + + CloseIniFileUserMapping(pGlobals); + } + + } + + WlxSetTimeout(pGlobals, 120); + + pNewEnvironment = CopyEnvironment(pGlobals->UserProcessData.pEnvironment); + +#if DBG + if (TEST_FLAG(GinaBreakFlags, BREAK_ACTIVATE)) + { + DebugLog((DEB_TRACE, "About to call WlxActivateUserShell(%#x, %ws, %ws, %#x)\n", + pGina->pGinaContext, APPLICATION_DESKTOP_PATH, + pGlobals->LogonScripts, NULL)); + DebugBreak(); + } +#endif + return( pGina->pWlxActivateUserShell( pGina->pGinaContext, + APPLICATION_DESKTOP_PATH, + pGlobals->LogonScripts, + pNewEnvironment) ); +} + +//+--------------------------------------------------------------------------- +// +// Function: HandleLoggedOn +// +// Synopsis: +// +// Effects: +// +// Arguments: [pGlobals] -- +// [SasType] -- +// +// Requires: +// +// Returns: +// +// Signals: +// +// Modifies: +// +// Algorithm: +// +// History: 9-30-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +HandleLoggedOn( + PGLOBALS pGlobals, + DWORD SasType) +{ + DWORD Result; + WLX_MPR_NOTIFY_INFO MprInfo; + PGINASESSION pGina; + int Flags; + int MprRet; + int LogoffResult; + + pGina = pGlobals->pGina; + + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon); + + ZeroMemory(&MprInfo, sizeof(MprInfo)); + + WlxSetTimeout(pGlobals, 120); + +#if DBG + if (TEST_FLAG(GinaBreakFlags, BREAK_LOGGEDON)) + { + DebugLog((DEB_TRACE, "About to call WlxLoggedOnSAS( %#x, %d, NULL)\n", + pGina->pGinaContext, pGlobals->SasType)); + DebugBreak(); + } +#endif + + Result = pGina->pWlxLoggedOnSAS( pGina->pGinaContext, + SasType, + NULL ); + + WlxSetTimeout(pGlobals, TIMEOUT_NONE); + + DebugLog((DEB_TRACE, "WlxLoggedOnSAS returned %d, %s\n", Result, WlxName(Result))); + pGlobals->LastGinaRet = Result; + + // + // if a new SAS has come in while we were processing that one, repost it and + // pick it up in LoggedOnDlgProc. + // + + if ( pGlobals->SasType != SasType ) + { + DebugLog((DEB_TRACE, "New SAS (%d: %s) came in while handling (%d: %s). Routing it now\n", + pGlobals->SasType, SASName(pGlobals->SasType), + SasType, SASName(SasType) )); + + SASRouter( pGlobals, pGlobals->SasType ); + + } + + if (Result == WLX_SAS_ACTION_LOCK_WKSTA) + { + return (DoLockWksta(pGlobals, FALSE)); + } + + if ((Result == WLX_SAS_ACTION_TASKLIST) || + (Result == WLX_SAS_ACTION_NONE)) + { + if ((pGlobals->WinlogonState != Winsta_WaitForLogoff) && + (pGlobals->WinlogonState != Winsta_WaitForShutdown) && + (pGlobals->WinlogonState != Winsta_InShutdownDlg) ) + { + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Application); + } + if (Result == WLX_SAS_ACTION_TASKLIST) + { + WCHAR szTaskMgr[] = L"taskmgr.exe"; + + DebugLog((DEB_TRACE, "Starting taskmgr.exe.\n")); + + if (pGlobals->UserLoggedOn ) { + pGina->pWlxStartApplication(pGina->pGinaContext, + APPLICATION_DESKTOP_PATH, + pGlobals->UserProcessData.pEnvironment, + szTaskMgr); + } + } + + TickleMessenger(); + + return(Result); + + } + + switch (Result) + { + case WLX_SAS_ACTION_LOGOFF: + Flags = EWX_LOGOFF; + break; + + case WLX_SAS_ACTION_FORCE_LOGOFF: + Flags = EWX_LOGOFF | EWX_FORCE; + break; + + case WLX_SAS_ACTION_SHUTDOWN: + Flags = EWX_LOGOFF | EWX_WINLOGON_OLD_SHUTDOWN; + break; + + case WLX_SAS_ACTION_SHUTDOWN_REBOOT: + Flags = EWX_LOGOFF | EWX_WINLOGON_OLD_SHUTDOWN | EWX_WINLOGON_OLD_REBOOT; + break; + + case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF: + Flags = EWX_LOGOFF | EWX_WINLOGON_OLD_SHUTDOWN | EWX_WINLOGON_OLD_POWEROFF; + break; + + default: + DebugLog((DEB_ERROR, "Incorrect result (%d) from WlxLoggedOnSAS\n", Result)); + return(0); + } + + + LogoffResult = InitiateLogoff(pGlobals, Flags); + if (LogoffResult == DLG_FAILURE) + { + return(WLX_SAS_ACTION_NONE); + } + + return(Result); + +} + + + +//+--------------------------------------------------------------------------- +// +// Function: DoLockWksta +// +// Synopsis: +// +// Arguments: [pGlobals] -- +// +// History: 9-16-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +int +DoLockWksta( + PGLOBALS pGlobals, + BOOL ScreenSaverInvoked) +{ + int Result; + PGINASESSION pGina; + + pGlobals->WinlogonState = Winsta_Locked; + DebugLog((DEB_TRACE_STATE, "DoLockWksta: Setting state to %s\n", GetState(Winsta_Locked))); + + pGina = pGlobals->pGina; + + LockWindowStation(pGlobals->WindowStation.hwinsta); + + do + { + + pGlobals->WinlogonState = Winsta_Locked_Display; + DebugLog((DEB_TRACE_STATE, "DoLockWksta: Setting state to %s\n", + GetState(Winsta_Locked_Display))); +#if DBG + if (TEST_FLAG(GinaBreakFlags, BREAK_DISPLAYLOCKED)) + { + DebugLog((DEB_TRACE, "About to call WlxDisplayLockedNotice( %#x )\n", + pGina->pGinaContext )); + DebugBreak(); + } +#endif + // + // No input timeout + // + + WlxSetTimeout(pGlobals, TIMEOUT_NONE); + + pGina->pWlxDisplayLockedNotice( pGina->pGinaContext ); + + DebugLog((DEB_TRACE, "Out of DisplayLockedNotice, SAS = %s\n", + SASName(pGlobals->SasType))); + + if (pGlobals->SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT) + { + // + // If we were invoked as part of a secure screen saver, + // then this timeout means that we should return to it + // and let it cycle. + // + if (ScreenSaverInvoked) + { + return(WLX_SAS_ACTION_NONE); + } + + // + // Invoke the screen saver: + // + + if (DoScreenSaver(pGlobals, TRUE) >= 0) + { + + // + // Jump right back to the top. + // + + Result = WLX_SAS_ACTION_NONE; + + continue; + } + + // + // A return of -1 indicates that some other SAS occurred, + // e.g. a Logoff, or a GINA specific SAS. Fall through to + // the default handling. + // + + } + + // + // An unfortunate label, but things get awfully convoluted switching + // between the screen saver and the locked state. The screen saver + // has stopped due to a SAS, and here is where we figure out what to + // do. This is jumped to from below, if the WkstaLocked dialog + // ended with a screen saver timeout. + // + + +ResetLockCall: + + if (pGlobals->SasType == WLX_SAS_TYPE_USER_LOGOFF) + { + if (pGlobals->LogoffFlags & EWX_WINLOGON_API_SHUTDOWN) + { + pGlobals->WinlogonState = Winsta_Shutdown; + } + return(WLX_SAS_ACTION_LOGOFF); + } + + +#if DBG + if (TEST_FLAG(GinaBreakFlags, BREAK_WKSTALOCKED)) + { + DebugLog((DEB_TRACE, "About to call WlxWkstaLockedSAS( %#x, %d )\n", + pGina->pGinaContext, pGlobals->SasType )); + DebugBreak(); + } +#endif + WlxSetTimeout(pGlobals, 120); + + Result = pGina->pWlxWkstaLockedSAS( pGina->pGinaContext, pGlobals->SasType ); + + WlxSetTimeout(pGlobals, TIMEOUT_NONE); + + DebugLog((DEB_TRACE, "WlxWkstaLockedSAS returned %d, %s\n", Result, WlxName(Result))); + + pGlobals->LastGinaRet = Result; + + if ( (Result == WLX_SAS_ACTION_NONE) && + (pGlobals->SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT ) ) + { + // + // The GINA was interrupted by a screen saver timeout. If + // we were invoked by a screen saver, we should return immediately, + // otherwise, run the screen saver. Same as before when the display + // call timed out. + // + + if (ScreenSaverInvoked) + { + return(WLX_SAS_ACTION_NONE); + } + + // + // A return of -1 indicates that the screen saver terminated + // due to some other SAS. Jump back up to the point where we + // handle that, so that we have one point where we do things + // correctly. + // + + if ( DoScreenSaver( pGlobals, TRUE ) < 0 ) + { + goto ResetLockCall; + + } + + // + // Otherwise, fall through, and loop again. + // + + + } + + } while (Result == WLX_SAS_ACTION_NONE); + + + if (Result == WLX_SAS_ACTION_FORCE_LOGOFF) + { + InitiateLogoff(pGlobals, EWX_LOGOFF | EWX_FORCE); + } + else + { + SetActiveDesktop( &pGlobals->WindowStation, Desktop_Application ); + + TickleMessenger(); + } + + return(Result); +} + + +//+--------------------------------------------------------------------------- +// +// Function: DoScreenSaver +// +// Synopsis: Starts up the screen saver +// +// Effects: +// +// Arguments: [pGlobals] -- +// +// Requires: +// +// Returns: +// +// Signals: +// +// Modifies: +// +// Algorithm: +// +// History: 10-13-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +int +DoScreenSaver( + PGLOBALS pGlobals, + BOOL WkstaLocked) +{ + int Result; + BOOL FastUnlock; + + + // + // WkstaLocked indicates that we were called by the DoLockWksta + // path. This means that we should not recursively call them, since + // there is no stopping case and we could chew up a lot of stack. + // So, we loop here, but we can break out if RunScreenSaver doesn't + // return Wksta locked. + // + + // + // FastUnlock determines if we allow a grace period or not. If the + // wksta is locked coming in, then it is not allowed at all. If it is + // not locked on entry, then we allow it once. + // + + FastUnlock = !WkstaLocked; + + do + { + Result = RunScreenSaver(pGlobals, FALSE, FastUnlock); + + FastUnlock = FALSE; + + if (Result == WLX_SAS_ACTION_LOCK_WKSTA) + { + // + // Ok, it's a secure screen saver. If we are already locked, + // break and return + // + if (WkstaLocked) + { + break; + } + + // + // Ok, it's not. Invoke the lock code ourselves, but tell it + // that it's being called from the screen saver path. + // + + Result = DoLockWksta(pGlobals, TRUE); + + } + else + { + break; + } + + // + // Loop clause: we only get here if we have invoked DoLockWksta. + // Loop only if it return WLX_SAS_ACTION_NONE, not unlock or force + // logoff. + // + + } while (Result == WLX_SAS_ACTION_NONE); + + if ( (Result == -1) || (Result == WLX_SAS_ACTION_LOGOFF) ) + { + return( Result ); + } + else if (Result == WLX_SAS_ACTION_FORCE_LOGOFF) + { + return(Result); + } + return(0); + +} + + + +/***************************************************************************\ +* FUNCTION: LogoffWaitDlgProc +* +* PURPOSE: Processes messages for the forced logoff wait dialog +* +* RETURNS: +* DLG_FAILURE - the dialog could not be displayed +* DLG_INTERRUPTED() - this is a set of possible interruptions (see winlogon.h) +* +* HISTORY: +* +* 05-09-92 Davidc Created. +* +\***************************************************************************/ + +BOOL +CALLBACK +LogoffWaitDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ) +{ + switch (message) { + + case WM_INITDIALOG: + + EnableSasMessages(hDlg); + + SetWindowLong(hDlg, GWL_USERDATA, lParam); + + CentreWindow(hDlg); + + return(TRUE); + + } + + // We didn't process this message + return FALSE; +} + + +BOOL +WaitForForceLogoff( + HWND hWnd, + PGLOBALS pGlobals) +{ + int Result; + + do + { + SetActiveDesktop( &pGlobals->WindowStation, Desktop_Winlogon ); + + WlxSetTimeout(pGlobals, TIMEOUT_NONE); + + Result = WlxDialogBoxParam( pGlobals, + pGlobals->hInstance, + MAKEINTRESOURCE(IDD_FORCED_LOGOFF_WAIT), + hWnd, + LogoffWaitDlgProc, + (LONG) pGlobals); + + } while ( (Result == WLX_DLG_INPUT_TIMEOUT) || + (Result == WLX_DLG_SCREEN_SAVER_TIMEOUT) ); + + + return(TRUE); + +} + +/***************************************************************************\ +* FUNCTION: LoggedOnDlgProc +* +* PURPOSE: Processes messages for the logged-on control dialog +* +* DIALOG RETURNS: +* +* DLG_FAILURE - Couldn't bring up the dialog +* DLG_LOGOFF() - The user logged off +* +* NOTES: +* +* On entry, it assumed that the winlogon desktop is switched to and the +* desktop lock is held. This same state exists on exit. +* +* HISTORY: +* +* 12-09-91 Davidc Created. +* +\***************************************************************************/ + +BOOL WINAPI +LoggedonDlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam + ) +{ + PGLOBALS pGlobals = (PGLOBALS)GetWindowLong(hDlg, GWL_USERDATA); + int Result; + + + switch (message) + { + + case WM_INITDIALOG: + SetWindowLong(hDlg, GWL_USERDATA, lParam); + pGlobals = (PGLOBALS)lParam; + + if (!LoggedonDlgInit(hDlg)) { + EndDialog(hDlg, DLG_FAILURE); + return(TRUE); + } + + // Send ourselves a message so we can hide ourselves without the + // dialog code trying to force us to be visible + PostMessage(hDlg, WM_HIDEOURSELVES, 0, 0); + + // + // + // Switch to app desktop and release lock + // + + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Application); + + // + // Tickle the messenger so it will display any queue'd messages. + // (This call is a kind of NoOp). + // + TickleMessenger(); + + return(TRUE); + + case WM_HIDEOURSELVES: + ShowWindow(hDlg, SW_HIDE); + + DropWorkingSet(); + + return(TRUE); + + case WLX_WM_SAS: + + // + // Disable further SAS events until we decide what to do. If + // we start another window, they will automagically be forwarded + // to it. This lets us call right into the individual cases. + // + + DisableSasMessages(); + + if (wParam == WLX_SAS_TYPE_SCRNSVR_TIMEOUT) + { + Result = DoScreenSaver(pGlobals, FALSE); + + if ( (Result < 0) || (Result == WLX_SAS_ACTION_LOGOFF) ) + { + // + // Ugly case: the screen saver received a SAS event + // which has interrupted it. So, that SAS is now stored + // in the globals. We snag it, stuff it in wParam, and + // FALL THROUGH to the rest of this code. + // + + wParam = pGlobals->SasType; + } + else if (Result == WLX_SAS_ACTION_FORCE_LOGOFF) + { + EndDialog(hDlg, WLX_SAS_ACTION_FORCE_LOGOFF); + return(TRUE); + + } + else + { + EnableSasMessages(hDlg); + return(TRUE); + } + } + + // + // Ok, more ugly cases. The user could asynchronously log off + // while we're in HandleLoggedOn(), in which case we would get + // the logoff notify in some other dialog, and returned to us + // here. But, we also have some logoff cases coming through + // here if we are waiting for the logoff, so if this is not + // winsrv telling us it's logged the guy off, ask the gina + // what to do. + // + + if ((wParam != WLX_SAS_TYPE_USER_LOGOFF) && + (wParam != WLX_SAS_TYPE_TIMEOUT) ) + { + Result = HandleLoggedOn(pGlobals, (DWORD) wParam); + } + else + { + Result = -1; + } + + + if ((wParam == WLX_SAS_TYPE_USER_LOGOFF ) || + (Result == WLX_SAS_ACTION_LOGOFF ) || + (Result == WLX_SAS_ACTION_FORCE_LOGOFF ) ) + { + // + // If we were shut down by the remote guy, handle that + // + if (pGlobals->WinlogonState == Winsta_Shutdown) + { + EndDialog(hDlg, pGlobals->LastGinaRet); + return(TRUE); + } + if ((pGlobals->WinlogonState == Winsta_WaitForLogoff) || + (wParam == WLX_SAS_TYPE_USER_LOGOFF) ) + { + if (IsShutdown(pGlobals->LastGinaRet)) + { + pGlobals->WinlogonState = Winsta_WaitForShutdown; + } + else + pGlobals->WinlogonState = Winsta_NoOne; + + DebugLog((DEB_TRACE_STATE, "LoggedOnDlg: setting state to %s\n", + GetState(pGlobals->WinlogonState))); + + EndDialog(hDlg, pGlobals->LastGinaRet); + return(TRUE); + + } + else + { + DebugLog((DEB_TRACE_STATE, "LoggedOnDlg: setting state to WaitForLogoff\n")); + pGlobals->WinlogonState = Winsta_WaitForLogoff; + + // + // If this is a force-logoff, end now, so that we fall + // through to the special dialog (WaitForForceLogoff()) + // + + if (Result == WLX_SAS_ACTION_FORCE_LOGOFF) + { + EndDialog(hDlg, Result); + return(TRUE); + } + } + } + else + { + // + // Now it is perverse. If the user logged off *while the + // options dialog was up*, they will return NONE, expecting + // us to deal with it correctly. + // + + if (pGlobals->WinlogonState == Winsta_WaitForLogoff) + { + if (IsShutdown(pGlobals->LastGinaRet)) + { + pGlobals->WinlogonState = Winsta_WaitForShutdown; + } + else + pGlobals->WinlogonState = Winsta_NoOne; + + DebugLog((DEB_TRACE_STATE, "LoggedOnDlg: setting state to %s\n", + GetState(pGlobals->WinlogonState))); + + EndDialog(hDlg, pGlobals->LastGinaRet); + return(TRUE); + + } + } + + EnableSasMessages(hDlg); + + DropWorkingSet(); + + return(TRUE); + } + + // We didn't process this message + return(FALSE); +} + + +/***************************************************************************\ +* FUNCTION: LoggedonDlgInit +* +* PURPOSE: Handles initialization of logged-on dialog +* +* RETURNS: TRUE on success, FALSE on failure +* +* HISTORY: +* +* 12-09-91 Davidc Created. +* +\***************************************************************************/ + +BOOL +LoggedonDlgInit( + HWND hDlg + ) +{ + PGLOBALS pGlobals = (PGLOBALS)GetWindowLong(hDlg, GWL_USERDATA); + + // Set our size to zero so we we don't appear + SetWindowPos(hDlg, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOREDRAW | SWP_NOZORDER); + + SetMapperFlag(hDlg, MAPPERFLAG_WINLOGON); + + return(TRUE); +} + + + +//+--------------------------------------------------------------------------- +// +// Function: BlockWaitForUserAction +// +// Synopsis: Blocks, waiting for the interactive user to do something, or +// a SAS to come in from the gina. +// +// Effects: +// +// Arguments: [pGlobals] -- +// +// Requires: +// +// Returns: +// +// Signals: +// +// Modifies: +// +// Algorithm: +// +// History: 10-17-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- + + +int +BlockWaitForUserAction(PGLOBALS pGlobals) +{ + int res; + + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Application); + WlxSetTimeout(pGlobals, TIMEOUT_NONE); + res = WlxDialogBoxParam( pGlobals, pGlobals->hInstance, + MAKEINTRESOURCE(IDD_CONTROL), + NULL, + LoggedonDlgProc, + (LONG) pGlobals) ; +#if DBG + if (res == -1) + { + DebugLog((DEB_ERROR, "Failed to start LoggedOnDlgProc, %d\n", GetLastError())); + } +#endif + + return(res); + +} + + + +//+--------------------------------------------------------------------------- +// +// Function: MainLoop +// +// Synopsis: Main winlogon loop. +// +// Arguments: [pGlobals] -- +// +// History: 10-17-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +void +MainLoop(PGLOBALS pGlobals) +{ + DWORD WlxResult; + PGINASESSION pGina; + int ScreenSaverResult; + + WlxResult = WLX_SAS_ACTION_NONE; + pGina = pGlobals->pGina; + + // + // Initialize the gina dll + // + + if (!InitializeGinaDll(pGlobals)) + { + return; + } + + // + // So long as action is none, loop here: + // + + while (WlxResult == WLX_SAS_ACTION_NONE) + { + DealWithAutochkLogs(); + + // + // If no one is logged on, switch to the display state, and call + // the gina to display a message. This is structured this way so + // that a gina can call us immediately during Initialize, and we + // can fall into the loop in the correct state. + // + if (pGlobals->WinlogonState == Winsta_NoOne) + { + pGlobals->WinlogonState = Winsta_NoOne_Display; + DebugLog((DEB_TRACE_STATE, "Setting state to %s\n", + GetState(Winsta_NoOne_Display))); +#if DBG + if (TEST_FLAG(GinaBreakFlags, BREAK_DISPLAY)) + { + DebugLog((DEB_TRACE, "About to call WlxDisplaySASNotice(%x)\n", + pGina->pGinaContext)); + DebugBreak(); + } +#endif + WlxSetTimeout(pGlobals, TIMEOUT_NONE); + + pGina->pWlxDisplaySASNotice(pGina->pGinaContext); + + // + // If we got a user logoff notify, that means that WE HAVE ALREADY + // SHUT DOWN. A remote shutdown has taken place, and it has been + // started by sysshut.c. + // + if (pGlobals->SasType == WLX_SAS_TYPE_USER_LOGOFF) + { + // + // We are *done* + // + + DebugLog(( DEB_TRACE_STATE, "Received Logoff, setting state to %s\n", + GetState(Winsta_Shutdown) )); + + pGlobals->WinlogonState = Winsta_Shutdown; + + break; + } + + // + // If we got a time out, + // + if (pGlobals->SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT) + { + // + // run the screen saver + // + + ScreenSaverResult = DoScreenSaver( pGlobals, FALSE ); + + if (ScreenSaverResult < 0) + { + // + // This means that a SAS other than activity cancelled + // the screen saver, such as a GINA specific one. + // In this case, drop on through to the logonattempt, + // since the current sas is in pGlobals. + // + + NOTHING ; + + } + else + { + if ( ( (ScreenSaverResult == 0) && + (pGlobals->SasType == WLX_SAS_TYPE_USER_LOGOFF) ) || + (ScreenSaverResult == WLX_SAS_ACTION_LOGOFF) ) + { + // + // Shutdown during the screen saver. + // + + DebugLog(( DEB_TRACE_STATE, "Received Logoff during screensaver, setting state to %s\n", + GetState(Winsta_Shutdown) )); + + pGlobals->WinlogonState = Winsta_Shutdown; + + break; + + } + + // + // And start the loop over again. + // + WlxResult = WLX_SAS_ACTION_NONE; + + // + // Remember, we're in Winsta_NoOne_Display right now, so we + // reset this so that we'll drop back into this at the top of + // the loop. + // + + DebugLog((DEB_TRACE_STATE, "Resetting to %s\n", + GetState(Winsta_NoOne) )); + + pGlobals->WinlogonState = Winsta_NoOne; + + continue; + } + + // + // If we got a user logoff notify, that means that WE HAVE ALREADY + // SHUT DOWN. A remote shutdown has taken place, and it has been + // started by sysshut.c. + // + if (pGlobals->SasType == WLX_SAS_TYPE_USER_LOGOFF) + { + // + // We are *done* + // + DebugLog(( DEB_TRACE_STATE, "Received Logoff during no-one screensaver, setting state to %s\n", + GetState(Winsta_Shutdown) )); + + pGlobals->WinlogonState = Winsta_Shutdown; + break; + } + } + + } + + WlxResult = LogonAttempt(pGlobals); + + if (WlxResult == WLX_SAS_ACTION_NONE) + { + // + // If we got a user logoff notify, that means that WE HAVE ALREADY + // SHUT DOWN. A remote shutdown has taken place, and it has been + // started by sysshut.c. + // + if (pGlobals->SasType == WLX_SAS_TYPE_USER_LOGOFF) + { + // + // We are *done* + // + + DebugLog(( DEB_TRACE_STATE, "Got logoff during logon, setting to %s\n", + GetState(Winsta_Shutdown) )); + + pGlobals->WinlogonState = Winsta_Shutdown; + + break; + } + // + // If we got a time out (meaning a screensaver timeout + // occurred during the logon prompt, then the prompt should be dead, + // but we'll hit here + // + if (pGlobals->SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT) + { + // + // run the screen saver + // + ScreenSaverResult = DoScreenSaver(pGlobals, FALSE); + + if (ScreenSaverResult < 0) + { + // + // This means that a SAS other than activity cancelled + // the screen saver, such as a GINA specific one. + // In this case, drop on through to the logonattempt, + // since the current sas is in pGlobals. + // + + NOTHING ; + + } + else + { + if ( (ScreenSaverResult == 0) && + (pGlobals->SasType == WLX_SAS_TYPE_USER_LOGOFF) ) + { + // + // Shutdown during the screen saver. + // + + DebugLog(( DEB_TRACE_STATE, "Received Logoff during screensaver, setting state to %s\n", + GetState(Winsta_Shutdown) )); + + pGlobals->WinlogonState = Winsta_Shutdown; + + break; + + } + + } + + // + // We're already at WlxResult == NONE, and State == NoOne, + // so we can just continue + // + + } + + // + // Make sure that we're back to NoOne: + // + + pGlobals->WinlogonState = Winsta_NoOne; + + continue; + } + + if (IsShutdownReturn(WlxResult)) + { + pGlobals->LastGinaRet = WlxResult; + DebugLog((DEB_TRACE_STATE, "Setting state to %d (%s)\n", + Winsta_WaitForShutdown, GetState(Winsta_WaitForShutdown))); + pGlobals->WinlogonState = Winsta_WaitForShutdown; + break; + } + + // + // Because profile or something else could have gone wrong, the gina + // could have returned LOGON, but it was changed to LOGOFF in + // LogonAttempt(). In that case, we don't try and start the shell, + // we just go straight to logoff processing. + // + + if (WlxResult == WLX_SAS_ACTION_LOGON) + { + if (DoStartShell(pGlobals)) + { + WlxResult = BlockWaitForUserAction(pGlobals); + + if (WlxResult == WLX_SAS_ACTION_FORCE_LOGOFF) + { + WaitForForceLogoff(NULL, pGlobals); + + WlxResult = WLX_SAS_ACTION_LOGOFF; + + } + } + else + { + WlxResult = WLX_SAS_ACTION_LOGOFF; + } + } + + EnableSasMessages(NULL); + + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon); + + if (pGlobals->WinlogonState == Winsta_Shutdown) + { + break; + } + + Logoff(pGlobals, WlxResult); + + SecurityChangeUser(pGlobals, NULL, NULL, pGlobals->WinlogonSid, FALSE); + + if (WlxResult == WLX_SAS_ACTION_LOGOFF) + { + pGlobals->WinlogonState = Winsta_NoOne; + DebugLog((DEB_TRACE, "WlxResult was logoff, so beginning loop again\n")); + DebugLog((DEB_TRACE_STATE, "State set to %s\n", GetState(Winsta_NoOne))); + WlxResult = WLX_SAS_ACTION_NONE; + } + + // + // Notify the GINA that the user is logged off + // + +#if DBG + if (TEST_FLAG(GinaBreakFlags, BREAK_LOGOFF)) + { + DebugLog((DEB_TRACE, "About to call WlxLogoff(%x)\n", pGina->pGinaContext)); + DebugBreak(); + } +#endif + pGina->pWlxLogoff(pGina->pGinaContext); + + // + // Toggle the winsta lock on and off. This clears the openlock, and + // sets the switchlock, allowing services to start, but no one can + // switch the active desktop. + // + + UnlockWindowStation(pGlobals->WindowStation.hwinsta); + LockWindowStation(pGlobals->WindowStation.hwinsta); + +#if DBG + if ((WlxResult == WLX_SAS_ACTION_NONE) || + (WlxResult == WLX_SAS_ACTION_LOGON) || + (WlxResult == WLX_SAS_ACTION_SHUTDOWN) || + (WlxResult == WLX_SAS_ACTION_SHUTDOWN_REBOOT) || + (WlxResult == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF) ) + { + continue; + } + + DebugLog((DEB_TRACE, "WlxResult not acceptible value: %d\n", WlxResult)); + DebugLog((DEB_TRACE, "Resetting to WLX_SAS_ACTION_NONE\n")); + WlxResult = WLX_SAS_ACTION_NONE; +#endif + + } + +} + + +//+--------------------------------------------------------------------------- +// +// Function: LogoffFlagsToWlxCode +// +// Synopsis: Translates a winsrv USER_LOGOFF message flags to a +// wlx return code. +// +// Arguments: [Flags] -- +// +// History: 10-17-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +DWORD +LogoffFlagsToWlxCode(DWORD Flags) +{ + if (Flags & EWX_POWEROFF) + { + return(WLX_SAS_ACTION_SHUTDOWN_POWER_OFF); + } + if (Flags & EWX_REBOOT) + { + return(WLX_SAS_ACTION_SHUTDOWN_REBOOT); + } + if (Flags & EWX_SHUTDOWN) + { + return(WLX_SAS_ACTION_SHUTDOWN); + } + + return(WLX_SAS_ACTION_LOGOFF); + +} + + +//+--------------------------------------------------------------------------- +// +// Function: WinsrvNotify +// +// Synopsis: Handles when winsrv talks to us +// +// Arguments: [pGlobals] -- +// [SasType] -- +// +// Algorithm: +// +// History: 10-17-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +void +WinsrvNotify( + PGLOBALS pGlobals, + DWORD SasType) +{ + DWORD RealFlags; + DWORD LogoffResult; + WinstaState PriorState; + // + // If the caller isn't system, and no-one is logged on, discard this message. + // + + if (!(pGlobals->LogoffFlags & EWX_SYSTEM_CALLER) && !pGlobals->UserLoggedOn) + { + DebugLog((DEB_TRACE, "Discarding notice from winsrv!\n")); + return; + } + + // + // If this indicates that winlogon initiated this message (by calling + // InitiateLogoff() somewhere else, or we're in a wait state, then pass + // the message along. This is what will kill our LoggedOnDlg. + // + + if ((pGlobals->LogoffFlags & EWX_WINLOGON_CALLER) || + (pGlobals->WinlogonState == Winsta_WaitForLogoff) || + (pGlobals->WinlogonState == Winsta_WaitForShutdown) || + (pGlobals->WinlogonState == Winsta_InShutdownDlg) ) + { + SASRouter(pGlobals, SasType); + RealFlags = RealFlagsFromStoredFlags(pGlobals->LogoffFlags); + pGlobals->LastGinaRet = LogoffFlagsToWlxCode(RealFlags); + return; + } + + // + // Well, this means that the user has called ExitWindowsEx(), and winsrv + // has passed the ball to us. We have to turn around and ask the gina if + // logoff is ok. This is convenient for some security architectures. I + // guess. + // + +#if DBG + if (TEST_FLAG(GinaBreakFlags, BREAK_ISLOGOFFOK)) + { + DebugLog((DEB_TRACE, "About to call WlxIsLogoffOk(%#x)\n", pGlobals->pGina->pGinaContext)); + DebugBreak(); + } +#endif + if (!pGlobals->pGina->pWlxIsLogoffOk(pGlobals->pGina->pGinaContext)) + { + DebugLog((DEB_TRACE, "Gina said no logoff...\n")); + return; + } + + + // + // Well, if we're not in a wait state, then initiate logoff and possibly + // shutdown. Convoluted, right? + // + + if ((pGlobals->WinlogonState != Winsta_WaitForLogoff) && + (pGlobals->WinlogonState != Winsta_WaitForShutdown) && + (pGlobals->WinlogonState != Winsta_InShutdownDlg) ) + { + PriorState = pGlobals->WinlogonState; + pGlobals->WinlogonState = Winsta_WaitForLogoff; + DebugLog((DEB_TRACE_STATE, "WinsrvNotify: Setting state to %s\n", + GetState(Winsta_WaitForLogoff))); + + pGlobals->LastGinaRet = LogoffFlagsToWlxCode(pGlobals->LogoffFlags); + DebugLog((DEB_TRACE, "Setting lastginaret to %s\n", + WlxName(pGlobals->LastGinaRet))); + + LogoffResult = InitiateLogoff( pGlobals, + (pGlobals->LogoffFlags & EWX_FORCE) | + StoredFlagsFromRealFlags(pGlobals->LogoffFlags) + ); + + if (LogoffResult == DLG_FAILURE) + { + DebugLog((DEB_TRACE, "Logoff refused, resetting\n")); + pGlobals->WinlogonState = PriorState; + DebugLog((DEB_TRACE_STATE, "WinsrvNotify: resetting state back to %s\n", + GetState(PriorState))); + } + } + +} diff --git a/private/windows/gina/winlogon/wlxutil.c b/private/windows/gina/winlogon/wlxutil.c new file mode 100644 index 000000000..f2bb9f72e --- /dev/null +++ b/private/windows/gina/winlogon/wlxutil.c @@ -0,0 +1,1203 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1993. +// +// File: wlxutil.c +// +// Contents: WLX helper functions +// +// Classes: +// +// Functions: +// +// History: 8-24-94 RichardW Created +// +//---------------------------------------------------------------------------- + +#include "precomp.h" +#pragma hdrstop + +#if DBG +char * StateNames[] = {"Preload", "Initialize", "NoOne", "NoOne_Display", + "NoOne_SAS", "LoggedOnUser_StartShell", "LoggedOnUser", + "LoggedOn_SAS", "Locked", "Locked_Display", "Locked_SAS", + "WaitForLogoff", "WaitForShutdown", "Shutdown" }; +#endif + + +WLX_DISPATCH_VERSION_1_1 WlxDispatchTable = { + WlxUseCtrlAltDel, + WlxSetContextPointer, + WlxSasNotify, + WlxSetTimeout, + WlxAssignShellProtection, + WlxMessageBox, + WlxDialogBox, + WlxDialogBoxParam, + WlxDialogBoxIndirect, + WlxDialogBoxIndirectParam, + WlxSwitchDesktopToUser, + WlxSwitchDesktopToWinlogon, + WlxChangePasswordNotify, + WlxGetSourceDesktop, + WlxSetReturnDesktop, + WlxCreateUserDesktop, + WlxChangePasswordNotifyEx }; + + + +typedef struct _WindowMapper { + DWORD fMapper; + HWND hWnd; + DLGPROC DlgProc; + struct _WindowMapper * pPrev; + LPARAM InitialParameter; +} WindowMapper, * PWindowMapper; +#define MAPPERFLAG_ACTIVE 1 +#define MAPPERFLAG_DIALOG 2 +#define MAPPERFLAG_SAS 4 +#define MAPPERFLAG_WINLOGON 8 + +#define MAX_WINDOW_MAPPERS 32 + +WindowMapper Mappers[MAX_WINDOW_MAPPERS]; +DWORD cActiveWindow; +DWORD PendingSasEvents[MAX_WINDOW_MAPPERS]; +DWORD PendingSasHead; +DWORD PendingSasTail; + +void +InitWindowMappers() +{ + ZeroMemory(Mappers, sizeof(WindowMapper) * MAX_WINDOW_MAPPERS); + cActiveWindow = 0; + PendingSasHead = 0; + PendingSasTail = 0; +} + +PWindowMapper +LocateTopMappedWindow(void) +{ + int i; + for (i = 0; i < MAX_WINDOW_MAPPERS ; i++ ) + { + if (Mappers[i].fMapper & MAPPERFLAG_SAS) + { + return(&Mappers[i]); + } + } + + return(NULL); + +} + +PWindowMapper +AllocWindowMapper(void) +{ + int i; + PWindowMapper pMap; + + for (i = 0 ; i < MAX_WINDOW_MAPPERS ; i++ ) + { + if ((Mappers[i].fMapper & MAPPERFLAG_ACTIVE) == 0) + { + cActiveWindow ++; + pMap = LocateTopMappedWindow(); + if (pMap) + { + FLAG_OFF(pMap->fMapper, MAPPERFLAG_SAS); + } + + Mappers[i].hWnd = NULL; + FLAG_ON(Mappers[i].fMapper, MAPPERFLAG_ACTIVE | MAPPERFLAG_SAS); + Mappers[i].pPrev = pMap; + + return(&Mappers[i]); + } + } + return(NULL); +} + +PWindowMapper +LocateWindowMapper(HWND hWnd) +{ + int i; + + for (i = 0; i < MAX_WINDOW_MAPPERS ; i++ ) + { + if (Mappers[i].hWnd == hWnd) + { + return(&Mappers[i]); + } + } + + return(NULL); +} + +void +FreeWindowMapper(PWindowMapper pMap) +{ + pMap->hWnd = NULL; + pMap->DlgProc = NULL; + if (pMap->fMapper & MAPPERFLAG_SAS) + { + if (pMap->pPrev) + { + FLAG_ON(pMap->pPrev->fMapper, MAPPERFLAG_SAS); + } + } + pMap->fMapper = 0; + pMap->pPrev = NULL; + cActiveWindow--; +} + +HWND +LocateTopWindow(VOID) +{ + PWindowMapper pMap; + + pMap = LocateTopMappedWindow(); + if (pMap) + { + return(pMap->hWnd); + } + return(NULL); +} + +BOOL +SetMapperFlag( + HWND hWnd, + DWORD Flag + ) +{ + PWindowMapper pMap; + + pMap = LocateWindowMapper(hWnd); + if (!pMap) + { + return(FALSE); + } + + pMap->fMapper |= Flag; + + return(TRUE); + +} + + +BOOL +QueueSasEvent( + DWORD dwSasType) +{ + if (((PendingSasTail + 1) % MAX_WINDOW_MAPPERS) == PendingSasHead) + { + return(FALSE); + } + + PendingSasEvents[PendingSasTail] = dwSasType; + PendingSasTail ++; + PendingSasTail %= MAX_WINDOW_MAPPERS; + + return(TRUE); +} + +BOOL +FetchPendingSas( + PDWORD pSasType) +{ + if (PendingSasHead == PendingSasTail) + { + return(FALSE); + } + *pSasType = PendingSasEvents[PendingSasHead++]; + PendingSasHead %= MAX_WINDOW_MAPPERS; + return(TRUE); +} + +BOOL +TestPendingSas(VOID) +{ + return (PendingSasHead == PendingSasTail); +} + +VOID +EnableSasMessages(HWND hWnd) +{ + DWORD SasType; + + SasMessages = TRUE; + while (FetchPendingSas(&SasType)) + { + if (hWnd) + { +#if DBG + DebugLog((DEB_TRACE, "Posting queued Sas %d to window %x\n", + SasType, hWnd )); +#endif + + PostMessage(hWnd, WLX_WM_SAS, (WPARAM) SasType, 0); + } + } +} + + +//+--------------------------------------------------------------------------- +// +// Function: RootWndProc +// +// Synopsis: This is the base window proc for all testgina windows. +// +// Arguments: [hWnd] -- +// [Message] -- +// [wParam] -- +// [lParam] -- +// +// History: 7-18-94 RichardW Created +// +// Notes: +// +//---------------------------------------------------------------------------- +BOOL +CALLBACK +RootDlgProc( + HWND hWnd, + UINT Message, + WPARAM wParam, + LPARAM lParam) +{ + PWindowMapper pMap; + int res; + BOOLEAN bRet; + + // + // If this is a WM_INITDIALOG message, then the parameter is the mapping, + // which needs to have a hwnd associated with it. Otherwise, do the normal + // preprocessing. + // + if (Message == WM_INITDIALOG) + { + pMap = (PWindowMapper) lParam; + pMap->hWnd = hWnd; + SetTopTimeout(hWnd); + lParam = pMap->InitialParameter; + // + // Now that everything is done, enable sas messages again. This + // protects us from people pounding on the c-a-d keys, when our response + // time is slow, e.g. due to stress. We also drain the queue of pending + // SAS events. + // + EnableSasMessages(hWnd); + } + else + { + pMap = LocateWindowMapper(hWnd); + if (!pMap) + { + return(FALSE); + } + } + + if (Message == WLX_WM_SAS && + ((pMap->fMapper & MAPPERFLAG_WINLOGON) == 0)) + { + if ((wParam == WLX_SAS_TYPE_TIMEOUT) || + (wParam == WLX_SAS_TYPE_SCRNSVR_TIMEOUT) ) + { + DebugLog((DEB_TRACE, "Sending timeout to top window.\n")); + } + } + + bRet = pMap->DlgProc(hWnd, Message, wParam, lParam); + if (!bRet) + { + if (Message == WM_INITDIALOG) + { + return(bRet); + } + if (Message == WLX_WM_SAS) + { + if ((pMap->fMapper & MAPPERFLAG_WINLOGON) == 0) + { + // + // Re-enable the messages + EnableSasMessages(pMap->hWnd); + + } + switch (wParam) + { + case WLX_SAS_TYPE_CTRL_ALT_DEL: + default: + res = WLX_DLG_SAS; + break; + + case WLX_SAS_TYPE_TIMEOUT: + res = WLX_DLG_INPUT_TIMEOUT; + break; + case WLX_SAS_TYPE_SCRNSVR_TIMEOUT: + res = WLX_DLG_SCREEN_SAVER_TIMEOUT; + break; + case WLX_SAS_TYPE_USER_LOGOFF: + res = WLX_DLG_USER_LOGOFF; + break; + } + if (res) + { + EndDialog(hWnd, res); + bRet = TRUE; + } + } + } + else + { + if (Message == WLX_WM_SAS && + ((pMap->fMapper & MAPPERFLAG_WINLOGON) == 0)) + { + // + // Re-enable the messages + // + EnableSasMessages(pMap->hWnd); + + switch (wParam) + { + case WLX_SAS_TYPE_TIMEOUT: + res = WLX_DLG_INPUT_TIMEOUT; + break; + + case WLX_SAS_TYPE_SCRNSVR_TIMEOUT: + res = WLX_DLG_SCREEN_SAVER_TIMEOUT; + break; + + case WLX_SAS_TYPE_USER_LOGOFF: + res = WLX_DLG_USER_LOGOFF; + break; + + default: + res = 0; + break; + } + + if (res) + { + DebugLog((DEB_TRACE, "Gina ate the SAS (%d) message, but ending it anyway.\n", wParam)); + EndDialog(hWnd, res); + } + + } + } + + return(bRet); + +} + +VOID +ChangeStateForSAS(PGLOBALS pGlobals) +{ +#if DBG + WinstaState State = pGlobals->WinlogonState; +#endif + if ((pGlobals->SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT) && + (pGlobals->SasType == WLX_SAS_TYPE_TIMEOUT) ) + { + DebugLog((DEB_TRACE, "SAS was a timeout, no state change\n")); + return; + } + switch (pGlobals->WinlogonState) + { + case Winsta_NoOne: + case Winsta_NoOne_Display: + pGlobals->WinlogonState = Winsta_NoOne_SAS; + break; + + case Winsta_Locked: + case Winsta_Locked_Display: + pGlobals->WinlogonState = Winsta_Locked_SAS; + break; + + case Winsta_LoggedOnUser: + case Winsta_LoggedOnUser_StartShell: + pGlobals->WinlogonState = Winsta_LoggedOn_SAS; + break; + + case Winsta_WaitForLogoff: + case Winsta_WaitForShutdown: + case Winsta_InShutdownDlg: + break; + + default: + DebugLog((DEB_ERROR, "Don't know how to get to next state from %d, %s\n", + pGlobals->WinlogonState, GetState(pGlobals->WinlogonState))); + } +#if DBG + DebugLog((DEB_TRACE, "ChangeStateForSAS: Went from %d (%s) to %d (%s)\n", + State, GetState(State), pGlobals->WinlogonState, + GetState(pGlobals->WinlogonState) )); +#endif +} + + +BOOL +SendSasToTopWindow( + PGLOBALS pGlobals, + DWORD SasType) +{ + PWindowMapper pMap; +#if DBG + WCHAR WindowName[32]; +#endif + + if (cActiveWindow) + { + if ((pGlobals->WinlogonState == Winsta_InShutdownDlg) && + (SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT)) + { + return(TRUE); + } + + pMap = LocateTopMappedWindow(); + + if (!pMap) + { + return(FALSE); + } + + if ( ( SasType > WLX_SAS_TYPE_MAX_MSFT_VALUE) || + ( SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT) || + ( SasType == WLX_SAS_TYPE_TIMEOUT) ) + { + // + // Either a timeout (which we have to forward), or a private + // which we have to forward. Kill any message boxes + // + if (KillMessageBox( SasType )) + { + DebugLog((DEB_TRACE, "Killed a pending message box\n")); + } + } + +#if DBG + GetWindowText( pMap->hWnd, WindowName, 32 ); + DebugLog((DEB_TRACE, "Sending SAS code %d to window %x (%ws) \n", SasType, pMap->hWnd, WindowName )); + +#endif + + + PostMessage(pMap->hWnd, WLX_WM_SAS, (WPARAM) SasType, 0); + + // + // This will cause them to be queued, and then handled later. + // + DisableSasMessages(); + + return(TRUE); + } + + return(FALSE); +} + + + + +VOID +DestroyMprInfo( + PWLX_MPR_NOTIFY_INFO pMprInfo) +{ + if (pMprInfo->pszUserName) + { + LocalFree(pMprInfo->pszUserName); + } + + if (pMprInfo->pszDomain) + { + LocalFree(pMprInfo->pszDomain); + } + + if (pMprInfo->pszPassword) + { + ZeroMemory(pMprInfo->pszPassword, wcslen(pMprInfo->pszPassword) * 2); + LocalFree(pMprInfo->pszPassword); + } + + if (pMprInfo->pszOldPassword) + { + ZeroMemory(pMprInfo->pszOldPassword, wcslen(pMprInfo->pszOldPassword) * 2); + LocalFree(pMprInfo->pszOldPassword); + } +} + + + + + + + + + +VOID +WINAPI +WlxUseCtrlAltDel( + HANDLE hWlx) +{ + PGLOBALS pGlobals; + + if (pGlobals = VerifyHandle(hWlx)) + { + pGlobals->ForwardCAD = TRUE; + } +} + +VOID +WINAPI +WlxSetContextPointer( + HANDLE hWlx, + PVOID pWlxContext + ) +{ + PGLOBALS pGlobals; + + if (pGlobals = VerifyHandle(hWlx)) + { + pGlobals->pGina->pGinaContext = pWlxContext; + } + +} + +VOID +WINAPI +WlxSasNotify( + HANDLE hWlx, + DWORD SasType + ) +{ + PGLOBALS pGlobals; + + if (pGlobals = VerifyHandle(hWlx)) + { + switch (SasType) + { + case WLX_SAS_TYPE_USER_LOGOFF: + case WLX_SAS_TYPE_TIMEOUT: + case WLX_SAS_TYPE_SCRNSVR_TIMEOUT: + DebugLog((DEB_ERROR, "Illegal SAS Type (%d) passed to WlxSasNotify\n", SasType )); + return; + + default: + SASRouter(pGlobals, SasType); + } + } +} + +BOOL +WINAPI +WlxSetTimeout( + HANDLE hWlx, + DWORD Timeout + ) +{ + PGLOBALS pGlobals; + + if (pGlobals = VerifyHandle(hWlx)) + { + if ((pGlobals->WinlogonState == Winsta_NoOne_Display) || + (pGlobals->WinlogonState == Winsta_Locked_Display) ) + { + if (Timeout) + { + SetLastErrorEx(ERROR_INVALID_PARAMETER, SLE_ERROR); + return(FALSE); + } + } + pGlobals->pGina->cTimeout = Timeout; + TimeoutUpdateTopTimeout( Timeout ); + return(TRUE); + } + + SetLastErrorEx(ERROR_INVALID_HANDLE, SLE_ERROR); + return(FALSE); + +} + +int +WINAPI +WlxAssignShellProtection( + HANDLE hWlx, + HANDLE hToken, + HANDLE hProcess, + HANDLE hThread + ) +{ + PGLOBALS pGlobals; + PTOKEN_DEFAULT_DACL pDefDacl; + DWORD cDefDacl = 0; + NTSTATUS Status; + PSECURITY_DESCRIPTOR psd; + unsigned char buf[SECURITY_DESCRIPTOR_MIN_LENGTH]; + BOOL Success; + + if (!(pGlobals = VerifyHandle(hWlx))) + { + DebugLog((DEB_ERROR, "Invalid hWlx handle\n")); + return(ERROR_INVALID_HANDLE); + } + + Status = NtQueryInformationToken(hToken, TokenDefaultDacl, NULL, 0, &cDefDacl); + if (!NT_SUCCESS(Status) && ( Status != STATUS_BUFFER_TOO_SMALL )) + { + return(RtlNtStatusToDosError(Status)); + } + + + pDefDacl = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, cDefDacl); + if (!pDefDacl) + { + return(ERROR_OUTOFMEMORY); + } + + Status = NtQueryInformationToken(hToken, TokenDefaultDacl, + pDefDacl, cDefDacl, &cDefDacl); + + if (!NT_SUCCESS(Status)) + { + LocalFree(pDefDacl); + return(RtlNtStatusToDosError(Status)); + } + + psd = (PSECURITY_DESCRIPTOR) buf; + InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(psd, TRUE, pDefDacl->DefaultDacl, FALSE); + + Success = SetKernelObjectSecurity(hProcess, DACL_SECURITY_INFORMATION, psd); + + LocalFree(pDefDacl); + + if (Success) + { + if (SetProcessToken(pGlobals, hProcess, hThread, hToken)) + return(0); + } + + return(GetLastError()); + +} + + +int WINAPI +WlxMessageBox( + HANDLE hWlx, + HWND hWnd, + LPWSTR lpsz1, + LPWSTR lpsz2, + UINT fmb) +{ + PGLOBALS pGlobals; + + if (!(pGlobals = VerifyHandle(hWlx))) + { + DebugLog((DEB_ERROR, "Invalid hWlx handle\n")); + SetLastErrorEx(ERROR_INVALID_HANDLE, SLE_ERROR); + return(-1); + } + return(TimeoutMessageBoxlpstr(pGlobals, hWnd, lpsz1, lpsz2, fmb, + pGlobals->pGina->cTimeout | TIMEOUT_SS_NOTIFY ) ); +} + +int WINAPI +WlxDialogBox( + HANDLE hWlx, + HANDLE hInstance, + LPWSTR lpsz1, + HWND hWnd, + DLGPROC dlgproc) +{ + return(WlxDialogBoxParam(hWlx, hInstance, lpsz1, hWnd, dlgproc, 0)); +} + +int WINAPI +WlxDialogBoxIndirect( + HANDLE hWlx, + HANDLE hInstance, + LPCDLGTEMPLATE lpTemplate, + HWND hWnd, + DLGPROC dlgproc) +{ + return(WlxDialogBoxIndirectParam(hWlx, hInstance, lpTemplate, hWnd, dlgproc, 0)); +} + + + +int WINAPI +WlxDialogBoxParam( + HANDLE hWlx, + HANDLE hInstance, + LPWSTR lpsz1, + HWND hWnd, + DLGPROC dlgproc, + LPARAM lParam) +{ + PWindowMapper pMap; + PGLOBALS pGlobals; + int res; + + + pMap = AllocWindowMapper(); + if (!pMap) + { + ASSERTMSG("Too many nested windows? send mail to richardw", pMap); + DebugLog((DEB_ERROR, "Too many nested windows?!?\n")); + SetLastError(ERROR_OUTOFMEMORY); + return(-1); + } + + pMap->InitialParameter = lParam; + pMap->DlgProc = dlgproc; + pMap->fMapper |= MAPPERFLAG_DIALOG; + + if (!(pGlobals = VerifyHandle(hWlx))) + { + DebugLog((DEB_ERROR, "Invalid hWlx handle\n")); + SetLastErrorEx(ERROR_INVALID_HANDLE, SLE_ERROR); + return(-1); + } + + //res = DialogBoxParam(hInstance, lpsz1, hWnd, RootDlgProc, (LPARAM) pMap); + res = TimeoutDialogBoxParam(pGlobals, hInstance, lpsz1, hWnd, + RootDlgProc, (LPARAM) pMap, + pGlobals->pGina->cTimeout | TIMEOUT_SS_NOTIFY); + + FreeWindowMapper(pMap); + + return(res); +} + +int WINAPI +WlxDialogBoxIndirectParam( + HANDLE hWlx, + HANDLE hInstance, + LPCDLGTEMPLATE lpTemplate, + HWND hWnd, + DLGPROC dlgproc, + LPARAM lParam) +{ + PWindowMapper pMap; + int res; + + + pMap = AllocWindowMapper(); + if (!pMap) + { + ASSERTMSG("Too many nested windows? send mail to richardw", pMap); + DebugLog((DEB_ERROR, "Too many nested windows?!?\n")); + SetLastError(ERROR_OUTOFMEMORY); + return(-1); + } + + pMap->InitialParameter = lParam; + pMap->DlgProc = dlgproc; + pMap->fMapper |= MAPPERFLAG_DIALOG; + + res = DialogBoxIndirectParam(hInstance, lpTemplate, hWnd, RootDlgProc, (LPARAM) pMap); + + FreeWindowMapper(pMap); + + return(res); +} + +int WINAPI +WlxSwitchDesktopToUser( + HANDLE hWlx) +{ + PGLOBALS pGlobals; + + if (!(pGlobals = VerifyHandle(hWlx))) + { + DebugLog((DEB_ERROR, "Invalid hWlx handle\n")); + return(ERROR_INVALID_HANDLE); + } + + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Application); + SetThreadDesktop(pGlobals->WindowStation.hdeskApplication); + + return(0); + +} + +int WINAPI +WlxSwitchDesktopToWinlogon( + HANDLE hWlx) +{ + PGLOBALS pGlobals; + + if (!(pGlobals = VerifyHandle(hWlx))) + { + DebugLog((DEB_ERROR, "Invalid hWlx handle\n")); + return(ERROR_INVALID_HANDLE); + } + + SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon); + SetThreadDesktop(pGlobals->WindowStation.hdeskWinlogon); + + return(0); + +} + +int WINAPI +WlxChangePasswordNotify( + HANDLE hWlx, + PWLX_MPR_NOTIFY_INFO pMprInfo, + DWORD dwChangeInfo) +{ + PGLOBALS pGlobals; + int Result; + + return WlxChangePasswordNotifyEx( hWlx, pMprInfo, dwChangeInfo, NULL, NULL ); + + +} + +int WINAPI +WlxChangePasswordNotifyEx( + HANDLE hWlx, + PWLX_MPR_NOTIFY_INFO pMprInfo, + DWORD dwChangeInfo, + PWSTR pszProvider, + PVOID pvReserved) +{ + PGLOBALS pGlobals; + int Result; + + if (!(pGlobals = VerifyHandle(hWlx))) + { + DebugLog((DEB_ERROR, "Invalid hWlx handle\n")); + return(ERROR_INVALID_HANDLE); + } + + Result = MprChangePasswordNotify( + pGlobals, + LocateTopWindow(), + pszProvider, + pMprInfo->pszUserName, + pMprInfo->pszDomain, + pMprInfo->pszPassword, + pMprInfo->pszOldPassword, + dwChangeInfo, + FALSE); + + DestroyMprInfo(pMprInfo); + + if (Result == DLG_SUCCESS) + { + return(0); + } + else + return(ERROR_INVALID_PARAMETER); + +} + +BOOL +WINAPI +WlxGetSourceDesktop( + HANDLE hWlx, + PWLX_DESKTOP * ppDesktop) +{ + PGLOBALS pGlobals; + DWORD len; + PWLX_DESKTOP pDesktop; + + if (!(pGlobals = VerifyHandle(hWlx))) + { + DebugLog((DEB_ERROR, "Invalid hWlx handle\n")); + SetLastError(ERROR_INVALID_HANDLE); + return( FALSE ); + } + + if (pGlobals->WindowStation.pszDesktop) + { + len = (wcslen(pGlobals->WindowStation.pszDesktop) + 1) * sizeof(WCHAR); + } + else + { + len = 0; + } + + pDesktop = LocalAlloc( LMEM_FIXED, sizeof(WLX_DESKTOP) + len ); + + if (!pDesktop) + { + return( FALSE ); + } + + pDesktop->Size = sizeof(WLX_DESKTOP); + pDesktop->Flags = WLX_DESKTOP_NAME; + pDesktop->hDesktop = NULL; + pDesktop->pszDesktopName = (PWSTR) (pDesktop + 1); + if (len) + { + wcscpy( pDesktop->pszDesktopName, pGlobals->WindowStation.pszDesktop ); + } + + *ppDesktop = pDesktop; + + return( TRUE ); +} + +BOOL +WINAPI +WlxSetReturnDesktop( + HANDLE hWlx, + PWLX_DESKTOP pDesktop) +{ + PGLOBALS pGlobals; + + if (!(pGlobals = VerifyHandle(hWlx))) + { + DebugLog((DEB_ERROR, "Invalid hWlx handle\n")); + SetLastError(ERROR_INVALID_HANDLE); + return( FALSE ); + } + + if ((pDesktop->Size != sizeof(WLX_DESKTOP)) || + ((pDesktop->Flags & (WLX_DESKTOP_HANDLE | WLX_DESKTOP_NAME)) == 0) ) + { + DebugLog((DEB_ERROR, "Invalid desktop\n")); + SetLastError( ERROR_INVALID_PARAMETER ); + return( FALSE ); + } + + + return( SetReturnDesktop( &pGlobals->WindowStation, pDesktop ) ); + +} + +BOOL +WINAPI +WlxCreateUserDesktop( + HANDLE hWlx, + HANDLE hToken, + DWORD Flags, + PWSTR pszDesktopName, + PWLX_DESKTOP * ppDesktop) +{ + PGLOBALS pGlobals; + PTOKEN_GROUPS pGroups; + PTOKEN_USER pUser; + DWORD Needed; + NTSTATUS Status; + DWORD i; + PSID pSid; + PWLX_DESKTOP pDesktop; + + + if (!(pGlobals = VerifyHandle(hWlx))) + { + DebugLog((DEB_ERROR, "Invalid hWlx handle\n")); + SetLastError(ERROR_INVALID_HANDLE); + return( FALSE ); + } + + if (((Flags & (WLX_CREATE_INSTANCE_ONLY | WLX_CREATE_USER)) == 0 ) || + ((Flags & (WLX_CREATE_INSTANCE_ONLY | WLX_CREATE_USER)) == + (WLX_CREATE_INSTANCE_ONLY | WLX_CREATE_USER) ) ) + { + DebugLog((DEB_ERROR, "Invalid flags\n")); + SetLastError( ERROR_INVALID_PARAMETER ); + return( FALSE ); + } + + pGroups = NULL; + pUser = NULL; + pSid = NULL; + + if ( Flags & WLX_CREATE_INSTANCE_ONLY ) + { + Status = NtQueryInformationToken( hToken, + TokenGroups, + NULL, + 0, + &Needed ); + + if ( Status != STATUS_BUFFER_TOO_SMALL ) + { + SetLastError( RtlNtStatusToDosError( Status ) ); + return( FALSE ); + } + + pGroups = (PTOKEN_GROUPS) LocalAlloc( LMEM_FIXED, Needed ); + + if ( !pGroups ) + { + return( FALSE ); + } + + Status = NtQueryInformationToken( hToken, + TokenGroups, + pGroups, + Needed, + &Needed ); + + if ( !NT_SUCCESS( Status ) ) + { + LocalFree( pGroups ); + SetLastError( RtlNtStatusToDosError( Status ) ); + return( FALSE ); + } + + for (i = 0 ; i < pGroups->GroupCount ; i++ ) + { + if ( pGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID ) + { + pSid = pGroups->Groups[i].Sid; + break; + } + } + + } + else + { + Status = NtQueryInformationToken( hToken, + TokenUser, + NULL, + 0, + &Needed ); + + if ( Status != STATUS_BUFFER_TOO_SMALL ) + { + SetLastError( RtlNtStatusToDosError( Status ) ); + return( FALSE ); + } + + pUser = (PTOKEN_USER) LocalAlloc( LMEM_FIXED, Needed ); + + if ( !pUser ) + { + return( FALSE ); + } + + Status = NtQueryInformationToken( hToken, + TokenUser, + pUser, + Needed, + &Needed ); + + if ( !NT_SUCCESS( Status ) ) + { + LocalFree( pUser ); + SetLastError( RtlNtStatusToDosError( Status ) ); + return( FALSE ); + } + + pSid = pUser->User.Sid; + + } + + if ( !pSid ) + { + SetLastError( ERROR_INVALID_PARAMETER ); + goto CleanUp; + } + + // + // Okay, we have the right SID now, so create the desktop. + // + + Needed = sizeof( WLX_DESKTOP ) + (wcslen( pszDesktopName ) + 1 ) * sizeof(WCHAR); + + pDesktop = (PWLX_DESKTOP) LocalAlloc( LMEM_FIXED, Needed ); + + if ( !pDesktop ) + { + goto CleanUp; + } + + pDesktop->Size = sizeof( WLX_DESKTOP ); + pDesktop->Flags = WLX_DESKTOP_NAME; + pDesktop->hDesktop = NULL; + pDesktop->pszDesktopName = (PWSTR) (pDesktop + 1); + + wcscpy( pDesktop->pszDesktopName, pszDesktopName ); + + pDesktop->hDesktop = CreateDesktop( pszDesktopName, + NULL, NULL, 0, MAXIMUM_ALLOWED, NULL); + + if ( !pDesktop->hDesktop ) + { + goto CleanUp; + } + + if (!SetUserDesktopSecurity(pDesktop->hDesktop, + pSid, pWinlogonSid ) ) + { + goto CleanUp; + } + + if (!AddUserToWinsta( &pGlobals->WindowStation, + pSid, + hToken ) ) + { + goto CleanUp; + } + + *ppDesktop = pDesktop; + pDesktop->Flags |= WLX_DESKTOP_HANDLE; + + if ( pGroups ) + { + LocalFree( pGroups ); + } + + if ( pUser ) + { + LocalFree( pUser ); + } + + return( TRUE ); + + +CleanUp: + + if ( pDesktop ) + { + if ( pDesktop->hDesktop ) + { + CloseDesktop( pDesktop->hDesktop ); + } + + LocalFree( pDesktop ); + } + + if ( pGroups ) + { + LocalFree( pGroups ); + } + + if ( pUser ) + { + LocalFree( pUser ); + } + + return( FALSE ); + +} + +BOOL +WINAPI +WlxCloseUserDesktop( + HANDLE hWlx, + PWLX_DESKTOP pDesktop, + HANDLE hToken ) +{ + PGLOBALS pGlobals; + + if (!(pGlobals = VerifyHandle(hWlx))) + { + DebugLog((DEB_ERROR, "Invalid hWlx handle\n")); + SetLastError(ERROR_INVALID_HANDLE); + return( FALSE ); + } + + if ( RemoveUserFromWinsta( &pGlobals->WindowStation, hToken ) ) + { + return( CloseDesktop( pDesktop->hDesktop ) ); + } + + return( FALSE ); +} diff --git a/private/windows/gina/winlogon/wlxutil.h b/private/windows/gina/winlogon/wlxutil.h new file mode 100644 index 000000000..212a3973a --- /dev/null +++ b/private/windows/gina/winlogon/wlxutil.h @@ -0,0 +1,70 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1993. +// +// File: wlxutil.h +// +// Contents: +// +// Classes: +// +// Functions: +// +// History: 8-24-94 RichardW Created +// +//---------------------------------------------------------------------------- + +#ifndef _WLXUTIL_H_ +#define _WLXUTIL_H_ + + +VOID WINAPI WlxUseCtrlAltDel(HANDLE); +VOID WINAPI WlxSasNotify(HANDLE, DWORD); +VOID WINAPI WlxSetContextPointer(HANDLE, PVOID); +BOOL WINAPI WlxSetTimeout(HANDLE, DWORD); +int WINAPI WlxAssignShellProtection(HANDLE, HANDLE, HANDLE, HANDLE); +int WINAPI WlxMessageBox(HANDLE, HWND, LPWSTR, LPWSTR, UINT); +int WINAPI WlxDialogBox(HANDLE, HANDLE, LPWSTR, HWND, DLGPROC); +int WINAPI WlxDialogBoxIndirect(HANDLE, HANDLE, LPCDLGTEMPLATE, HWND, DLGPROC); +int WINAPI WlxDialogBoxParam(HANDLE, HANDLE, LPWSTR, HWND, DLGPROC, LPARAM); +int WINAPI WlxDialogBoxIndirectParam(HANDLE, HANDLE, LPCDLGTEMPLATE, HWND, DLGPROC, LPARAM); +int WINAPI WlxSwitchDesktopToUser(HANDLE); +int WINAPI WlxSwitchDesktopToWinlogon(HANDLE); +int WINAPI WlxChangePasswordNotify(HANDLE, PWLX_MPR_NOTIFY_INFO, DWORD); +BOOL WINAPI WlxGetSourceDesktop(HANDLE, PWLX_DESKTOP *); +BOOL WINAPI WlxSetReturnDesktop(HANDLE, PWLX_DESKTOP); +BOOL WINAPI WlxCreateUserDesktop(HANDLE, HANDLE, DWORD, PWSTR, PWLX_DESKTOP *); +int WINAPI WlxChangePasswordNotifyEx( HANDLE, PWLX_MPR_NOTIFY_INFO, DWORD, PWSTR, PVOID); + +extern WLX_DISPATCH_VERSION_1_0 OldWlxDispatchTable; +extern WLX_DISPATCH_VERSION_1_1 WlxDispatchTable; + + +void +SASRouter( PGLOBALS pGlobals, + DWORD SasType ); + +BOOL +SendSasToTopWindow( + PGLOBALS pGlobals, + DWORD SasType); + +VOID +ChangeStateForSAS(PGLOBALS pGlobals); + +#define MAPPERFLAG_WINLOGON 8 +BOOL +SetMapperFlag( + HWND hWnd, + DWORD Flag + ); + +VOID +DestroyMprInfo( + PWLX_MPR_NOTIFY_INFO pMprInfo); + +DWORD +LogoffFlagsToWlxCode(DWORD Flags); + +#endif |