MSI, Inno and NSIS installers are automatically detected (also if they are nested by ExeInZip). Others can specify command line switches in the rapps-db package.
Commit 51ee32f5f8 moved the `WNetClearConnections()` in the main
Winlogon thread, where it now runs.
`WNetClearConnections()` calls a 3rd-party module (nfs41_np.dll)
that invokes `kernel32!OutputDebugStringA()`.
The SEH usage pattern in `OutputDebugStringA()`, when compiled with
GCC and PSEH, generates an erroneous chain of exception handlers, that,
when running in an execution environment like that of winlogon.exe,
triggers a crash. See CORE-20316 for more details and testing.
As a temporary measure, hackfix away the problem by surrounding the
`WNetClearConnections()` call in a `_SEH2_TRY/_SEH2_EXCEPT` block
(the net effect is to "add" the missing exception handler entry).
Hack for commit 51ee32f5f8
CORE-20307 CORE-20309 CORE-20316
- Add the context stack including the PUSHD and POPD commands.
- Add the required changes to the HELP command and command execution to support the context stack.
- Add support for sub helpers.
- The single default button in the "Shutdown Computer" and "GINA failed
to load" dialogs will be automatically focused by the dialog manager,
so there is no need to invoke `SetFocus()` in their `WM_INITDIALOG`
handler -- which also erroneously returned `TRUE`, thus ignoring any
focus set by the caller :D
- Use `DeleteMenu()` instead of `RemoveMenu()` so that it'll free any
memory resources.
Fix some erroneous `SetFocus()` invocations in dialog procedures:
- In `WM_INITDIALOG`, don't return `TRUE` if focus is changed to a
control, otherwise any focus set by the caller would be ignored;
- In other handlers, `SetFocus()` would generate an inconsistent
behaviour with the dialog push-buttons, as mentioned in:
https://devblogs.microsoft.com/oldnewthing/20040802-00/?p=38283
Some partition entries (such as those added by the CreateInsertBlankRegion function) may not have their Volume member allocated. This fixes the USETUP crash when such partitions are handled.
- Both 3rd-party modules and GINA shutdown notifications should be
called just when shutdown is being started.
Addendum to commit d3b9c68d22 (r66252)
CORE-8322
- Factor the two `DisplayStatusMessage()` calls for shutdown vs. reboot.
- The `LogonHandler` notification should be invoked only once for a
given logon operation. It is invoked in `HandleLogon()` after access
to the window station (and desktop) is allowed for the user logging in.
And so, it must not be invoked elsewhere, when no user is logged in
(or a logon failure happened), like after a call to `WlxDisplaySASNotice()`...
- Invoke the `StartShellHandler` notification **before** starting the
shell, and the `PostShellHandler` notification **after** the shell
started.
- Invoke the `LogoffHandler` notification on the application desktop,
just after having terminated all the user's programs, and before
switching back to Winlogon's desktop.
- Add and show the 'Logging off' status message.
- When performing the following actions:
`WLX_SAS_ACTION_LOGOFF`, `WLX_SAS_ACTION_FORCE_LOGOFF`,
as well as:
`WLX_SAS_ACTION_SHUTDOWN`, `WLX_SAS_ACTION_SHUTDOWN_POWER_OFF`,
`WLX_SAS_ACTION_SHUTDOWN_REBOOT`,
ensure that Winlogon is in a correct LogonState for doing the logoff
sequence.
----
Test results for Winlogon notifications, after fix:
- `Asynchronous: FALSE, Impersonation: FALSE`
```
WLEventStartup: 30 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventLogon: 32 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventStartShell: 32 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventPostShell: 32 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventLock: 32 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventUnlock: 32 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventStartScreenSaver: 32 tests executed (0 marked as todo, 6 failures), 0 skipped.
WLEventStopScreenSaver: 32 tests executed (0 marked as todo, 5 failures), 0 skipped.
WLEventLogoff: 32 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventShutdown: 31 tests executed (0 marked as todo, 3 failures), 0 skipped.
```
- `Asynchronous: FALSE, Impersonation: TRUE`
```
WLEventStartup: 30 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventLogon: 33 tests executed (0 marked as todo, 3 failures), 0 skipped.
WLEventStartShell: 33 tests executed (0 marked as todo, 3 failures), 0 skipped.
WLEventPostShell: 33 tests executed (0 marked as todo, 3 failures), 0 skipped.
WLEventLock: 33 tests executed (0 marked as todo, 3 failures), 0 skipped.
WLEventUnlock: 33 tests executed (0 marked as todo, 3 failures), 0 skipped.
WLEventStartScreenSaver: 34 tests executed (0 marked as todo, 3 failures), 0 skipped.
WLEventStopScreenSaver: 34 tests executed (0 marked as todo, 2 failures), 0 skipped.
WLEventLogoff: 34 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventShutdown: 31 tests executed (0 marked as todo, 3 failures), 0 skipped.
```
Per-user network connections (to shared drives...) are restored at user
logon: `HandleLogon() -> RestoreAllConnections()`.
These connections should be closed only at user logoff, in `HandleLogoff()`,
instead of inside the common logoff/shutdown thread, which is also invoked
at... shutdown!
- Isolate the network connections closing inside a `CloseAllConnections()`
helper (which also performs the necessary user thread impersonation).
- Invoke this helper directly inside `HandleLogoff()`, and also re-enable
the `IDS_CLOSINGNETWORKCONNECTIONS` message display.
Test results:
- Test 1a: `Asynchronous: FALSE, Impersonation: FALSE`
BEFORE the fix:
OK, the thread isn't impersonated:
```
Thread Token : 0x00000000 - User: '(null)\(null)'
```
BUT these two WLX notify info fields aren't set when the user is logged in:
```
Info.UserName : '(null)'
Info.Domain : '(null)'
```
Results:
```
WLEventStartup: 30 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventLogon: 30 tests executed (0 marked as todo, 3 failures), 2 skipped.
WLEventStartShell: 30 tests executed (0 marked as todo, 3 failures), 2 skipped.
(NOTE: WLEventPostShell isn't yet implemented in ReactOS)
WLEventLock: 30 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventUnlock: 30 tests executed (0 marked as todo, 3 failures), 2 skipped.
WLEventStartScreenSaver: 30 tests executed (0 marked as todo, 9 failures), 2 skipped.
WLEventStopScreenSaver: 30 tests executed (0 marked as todo, 7 failures), 2 skipped.
WLEventLogoff: 30 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventShutdown: 31 tests executed (0 marked as todo, 3 failures), 0 skipped.
```
- Test 1b: `Asynchronous: FALSE, Impersonation: FALSE`
AFTER the fix:
OK, the thread isn't impersonated:
```
Thread Token : 0x00000000 - User: '(null)\(null)'
```
OK, these two WLX notify info fields are now set when the user is logged in:
```
Info.UserName : 'Administrator'
Info.Domain : 'MYCOMPUTERNAME'
```
Results:
```
WLEventStartup: 30 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventLogon: 32 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventStartShell: 32 tests executed (0 marked as todo, 1 failure), 0 skipped.
(NOTE: WLEventPostShell isn't yet implemented in ReactOS)
WLEventLock: 32 tests executed (0 marked as todo, 2 failures), 0 skipped.
WLEventUnlock: 32 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventStartScreenSaver: 32 tests executed (0 marked as todo, 7 failures), 0 skipped.
WLEventStopScreenSaver: 32 tests executed (0 marked as todo, 5 failures), 0 skipped.
WLEventLogoff: 32 tests executed (0 marked as todo, 2 failures), 0 skipped.
WLEventShutdown: 31 tests executed (0 marked as todo, 3 failures), 0 skipped.
```
Less failed tests and none skipped anymore.
- Test 2a: `Asynchronous: FALSE, Impersonation: TRUE`
BEFORE the fix:
OK, the thread impersonates the user when (s)he is logged in:
```
Thread Token : 0x00000360 - User: 'MYCOMPUTERNAME\Administrator'
```
BUT these two WLX notify info fields aren't set when the user is logged in:
```
Info.UserName : '(null)'
Info.Domain : '(null)'
```
Results:
```
WLEventStartup: 30 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventLogon: 31 tests executed (0 marked as todo, 5 failures), 2 skipped.
WLEventStartShell: 31 tests executed (0 marked as todo, 5 failures), 2 skipped.
(NOTE: WLEventPostShell isn't yet implemented in ReactOS)
WLEventLock: 31 tests executed (0 marked as todo, 6 failures), 2 skipped.
WLEventUnlock: 31 tests executed (0 marked as todo, 5 failures), 2 skipped.
WLEventStartScreenSaver: 32 tests executed (0 marked as todo, 6 failures), 2 skipped.
WLEventStopScreenSaver: 32 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventLogoff: 32 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventShutdown: 31 tests executed (0 marked as todo, 3 failures), 0 skipped.
```
- Test 2b: `Asynchronous: FALSE, Impersonation: TRUE`
AFTER the fix:
OK, the thread impersonates the user when (s)he is logged in:
```
Thread Token : 0x00000360 - User: 'MYCOMPUTERNAME\Administrator'
```
OK, these two WLX notify info fields are now set when the user is logged in:
```
Info.UserName : 'Administrator'
Info.Domain : 'MYCOMPUTERNAME'
```
Results:
```
WLEventStartup: 30 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventLogon: 33 tests executed (0 marked as todo, 3 failures), 0 skipped.
WLEventStartShell: 33 tests executed (0 marked as todo, 3 failures), 0 skipped.
(NOTE: WLEventPostShell isn't yet implemented in ReactOS)
WLEventLock: 33 tests executed (0 marked as todo, 4 failures), 0 skipped.
WLEventUnlock: 33 tests executed (0 marked as todo, 3 failures), 0 skipped.
WLEventStartScreenSaver: 34 tests executed (0 marked as todo, 4 failures), 0 skipped.
WLEventStopScreenSaver: 34 tests executed (0 marked as todo, 2 failures), 0 skipped.
WLEventLogoff: 34 tests executed (0 marked as todo, 2 failures), 0 skipped.
WLEventShutdown: 31 tests executed (0 marked as todo, 3 failures), 0 skipped.
```
As well, less failed tests and none skipped anymore.
Also, capture in `CallNotificationDll()` the notify info structure template
before invoking each DLL handler: a malicious caller might modify its contents,
and so we need to re-initialize its contents for each call.
Test results:
- Test 1: `Asynchronous: FALSE, Impersonation: FALSE`
Before & After the fix, give the same results:
```
WLEventStartup: 30 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventLogon: 30 tests executed (0 marked as todo, 3 failures), 2 skipped.
WLEventStartShell: 30 tests executed (0 marked as todo, 3 failures), 2 skipped.
(NOTE: WLEventPostShell isn't yet implemented in ReactOS)
WLEventLock: 30 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventUnlock: 30 tests executed (0 marked as todo, 3 failures), 2 skipped.
WLEventStartScreenSaver: 30 tests executed (0 marked as todo, 9 failures), 2 skipped.
WLEventStopScreenSaver: 30 tests executed (0 marked as todo, 7 failures), 2 skipped.
WLEventLogoff: 30 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventShutdown: 31 tests executed (0 marked as todo, 3 failures), 0 skipped.
```
* The current notification thread doesn't impersonate any user, the
dumped thread token shows:
`Thread Token : 0x00000000 - User: '(null)\(null)'`
(except for `WLEventStartScreenSaver` and `WLEventStopScreenSaver`
which currently in ReactOS, impersonate the logged user and shows e.g.,
`Thread Token : 0x00000250 - User: 'MYCOMPUTERNAME\Administrator' ).
- Test 2: `Asynchronous: FALSE, Impersonation: TRUE`
Before the fix:
```
WLEventStartup: 30 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventLogon: 29 tests executed (0 marked as todo, 8 failures), 4 skipped.
WLEventStartShell: 29 tests executed (0 marked as todo, 8 failures), 4 skipped.
(NOTE: WLEventPostShell isn't yet implemented in ReactOS)
WLEventLock: 29 tests executed (0 marked as todo, 9 failures), 4 skipped.
WLEventUnlock: 29 tests executed (0 marked as todo, 8 failures), 4 skipped.
WLEventStartScreenSaver: 32 tests executed (0 marked as todo, 6 failures), 2 skipped.
WLEventStopScreenSaver: 32 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventLogoff: 30 tests executed (0 marked as todo, 7 failures), 4 skipped.
WLEventShutdown: 31 tests executed (0 marked as todo, 3 failures), 0 skipped.
```
* There are more failures (and skipped tests), because in impersonation
mode, the tests expect that the current thread is impersonating the
logged-on user, and validate whether this impersonated user matches
what the WLX notification `Domain` and `UserName` information structure
members are. However, these two fields aren't set yet, and the thread
isn't impersonating any user yet.
* The dumped thread token shows the same information as in Test 1.
- Test 3: `Asynchronous: FALSE, Impersonation: TRUE`
After the fix:
```
WLEventStartup: 30 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventLogon: 31 tests executed (0 marked as todo, 5 failures), 2 skipped.
WLEventStartShell: 31 tests executed (0 marked as todo, 5 failures), 2 skipped.
(NOTE: WLEventPostShell isn't yet implemented in ReactOS)
WLEventLock: 31 tests executed (0 marked as todo, 6 failures), 2 skipped.
WLEventUnlock: 31 tests executed (0 marked as todo, 5 failures), 2 skipped.
WLEventStartScreenSaver: 32 tests executed (0 marked as todo, 6 failures), 2 skipped.
WLEventStopScreenSaver: 32 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventLogoff: 32 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventShutdown: 31 tests executed (0 marked as todo, 3 failures), 0 skipped.
```
* There are now less skipped tests than in Test 2, as much as in Test 1.
* There are also less failed tests than in Test 2; however, there are
few more failed tests than in Test 1, but this is balanced by new
tests being executed.
The reason is that now, compared to Test 2 in impersonation mode, the
current thread is correctly impersonating the logged-on user, if any.
However, the WLX notification `Domain` and `UserName` information
structure members still aren't set yet, thus some of the tests fail.
* The dumped thread token for all notifications now show e.g.,
`Thread Token : 0x00000360 - User: 'MYCOMPUTERNAME\Administrator'`
(`WLEventStartup`, `WLEventShutdown` don't show any, since no user
is logged-in at these stages.)
And retrieve all its exported notification handlers.
This reverts parts of commit 5d4f69bf35.
Because notification DLLs can keep internal state between successive
notifications, they are kept loaded until Winlogon termination.
The SFC notification DLL is also loaded. Because sfc.dll redirects to
sfc_os.dll, the latter is then automatically loaded. This allows doing
what commit 88ee639b06 (r68615) was aiming at, in a less hackish manner.
CORE-9598
Test results:
- Before:
We observe the repeated loading/unloading of the DLL at each
notification call:
```
WLNOTIFY(ac.b0): Entering `DllMain`(hInst: 0x10000000, dwReason: 0x1, pReserved: 0x00000000)
...
WLNOTIFY(ac.b0): Entering `DllMain`(hInst: 0x10000000, dwReason: 0x0, pReserved: 0x00000000)
```
The global state of the notification DLL is lost between each
notification, and such testing errors happen:
```
modules\rostests\win32\winlogon\wlntfytests\wlntfytests.c:786: Test failed: **** WLEventLogon: ERROR: Wrong state NON-INITIALIZED, expected Startup or Logoff
err:(modules\rostests\win32\winlogon\wlntfytests\wlntfytests.c:1034) **** WLEventLogon: Changing state NON-INITIALIZED to Logon
```
The previous notification state is always reset to `NON-INITIALIZED` (its initial value).
Test results for all the notifications:
```
WLEventStartup: 30 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventLogon: 30 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventStartShell: 30 tests executed (0 marked as todo, 4 failures), 2 skipped.
(NOTE: WLEventPostShell isn't yet implemented in ReactOS)
WLEventLock: 30 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventUnlock: 30 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventStartScreenSaver: 30 tests executed (0 marked as todo, 10 failures), 0 skipped.
WLEventStopScreenSaver: 30 tests executed (0 marked as todo, 9 failures), 0 skipped.
WLEventLogoff: 30 tests executed (0 marked as todo, 5 failures), 2 skipped.
WLEventShutdown: 31 tests executed (0 marked as todo, 5 failures), 0 skipped.
```
- After:
The DLL is loaded only once, at the first notification, then stays
loaded for the whole life of winlogon.exe:
```
WLNOTIFY(ac.b0): Entering `DllMain`(hInst: 0x10000000, dwReason: 0x1, pReserved: 0x00000000)
```
and the global state of the notification DLL is now kept between each
notification, there are no `ERROR: Wrong state NON-INITIALIZED, expected ...`
errors anymore.
Test results for all the notifications:
```
WLEventStartup: 30 tests executed (0 marked as todo, 1 failure), 0 skipped.
WLEventLogon: 30 tests executed (0 marked as todo, 3 failures), 2 skipped.
WLEventStartShell: 30 tests executed (0 marked as todo, 3 failures), 2 skipped.
(NOTE: WLEventPostShell isn't yet implemented in ReactOS)
WLEventLock: 30 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventUnlock: 30 tests executed (0 marked as todo, 3 failures), 2 skipped.
WLEventStartScreenSaver: 30 tests executed (0 marked as todo, 9 failures), 2 skipped.
WLEventStopScreenSaver: 30 tests executed (0 marked as todo, 7 failures), 2 skipped.
WLEventLogoff: 30 tests executed (0 marked as todo, 4 failures), 2 skipped.
WLEventShutdown: 31 tests executed (0 marked as todo, 3 failures), 0 skipped.
```
Expand the "DllName" notification DLL path string, independently of
whether it is REG_SZ or REG_EXPAND_SZ, before loading it, similarly
to Windows' winlogon.exe.
Validate also the type/size of the retrieved registry value.
Historical note:
Investigation shows that this functionality, introduced between builds
1902 and 1906 of Windows NT 5.0 (future 2000) Beta 3, has always been
"nop-ed" and has remained this way till Windows Server 2003.
The value read from the "SafeMode" registry value is unconditionally
overridden afterwards, causing the notification DLLs to always be loaded.
In ReactOS, this functionality is restored, and only the notifications
tagged as such are loaded in SafeMode.
Furthermore:
Analysis of strings in Win2000 and WinXP/2003 winlogon.exe, show that
the "Safe" registry value doesn't exist for notifications; instead,
it is named "SafeMode".
The "Safe" value appears only for the SensLogn (SENS Winlogon Event) handler
registry entry. My hypothesis is that the value name is a typo for the
"SafeMode" value. It has been introduced in the `\Winlogon\Notify\SensLogn`
registry entry for SensLogn around Windows NT 5.0 build 1946.