Files
reactos/dll/win32/msgina/gui.c
T
Hermès Bélusca-Maïto 1499e7ef53 [MSGINA] Improve Security and Shutdown dialog features (#8372)
- Respect system policies for showing or hiding:

  * Security dialog "Lock Workstation", "Log Off", "Shut Down",
    "Change Password", "Task Manager" buttons;

  * "Log Off" entry in the Shutdown dialog;

- Disable the "Shut Down" Security dialog button, and the "Stand by",
  "Shut down" entries in the Shutdown dialog, if the logged user
  doesn't have the SeShutdownPrivilege.

- Add other missing `WLX_SHUTDOWN_STATE_*` shutdown flags that are
  supported by Windows.

- Improve the retrieval of shutdown options to be displayed in the
  "Shut Down" dialog. In particular, don't hardcode any sort of
  defaults, but let them come from what the user can do (Is logoff
  allowed? Does (s)he have the rights to shutdown/reboot? etc.).
  If no shutdown options are available, then simply don't display
  the dialog!

- Don't hardcode the default selected shutdown option. Either it comes
  from the user's registry and is valid (i.e. corresponds to an existing
  shutdown option in the dialog), otherwise, fall back to the first
  option in the dialog.

  In particular this means:
  * when opening the "Shut Down" dialog from the C-A-D Security
    dialog, by default we will select the "Log Off" option, or
    whatever the user last selected;
  * when opening the dialog from the "Turn Off" Start Menu item, default
    to the "Shut down" option, or whatever the user last selected.
  * when opening the dialog from the C-A-D "Log On" dialog (no user
    is logged in), the "Shut down" option will also be selected, or
    whatever the system operator last selected.

- For the shell-invokable `ShellShutdownDialog()` function, implement
  support for using a custom display user name, or the one in the
  `"Logon User Name"` registry value saved by `WlxActivateUserShell()`.
  Plus, the 3rd parameter specifies the list of shutdown options to
  *exclude* from the options list.
2025-09-04 22:57:23 +02:00

1808 lines
52 KiB
C

/*
* PROJECT: ReactOS msgina.dll
* FILE: dll/win32/msgina/gui.c
* PURPOSE: ReactOS Logon GINA DLL
* PROGRAMMERS: Hervé Poussineau ([email protected])
* Katayama Hirofumi MZ ([email protected])
*/
#include "msgina.h"
#include <wingdi.h>
#include <winnls.h>
#include <ndk/exfuncs.h>
typedef struct _DISPLAYSTATUSMSG
{
PGINA_CONTEXT Context;
HDESK hDesktop;
DWORD dwOptions;
PWSTR pTitle;
PWSTR pMessage;
HANDLE StartupEvent;
} DISPLAYSTATUSMSG, *PDISPLAYSTATUSMSG;
typedef struct _LEGALNOTICEDATA
{
LPWSTR pszCaption;
LPWSTR pszText;
} LEGALNOTICEDATA, *PLEGALNOTICEDATA;
// Timer ID for the animated dialog bar.
#define IDT_BAR 1
#define ISKEYDOWN(x) (GetKeyState(x) & 0x8000)
typedef struct _DLG_DATA
{
PGINA_CONTEXT pgContext;
HBITMAP hLogoBitmap;
HBITMAP hBarBitmap;
HWND hWndBarCtrl;
DWORD BarCounter;
DWORD LogoWidth;
DWORD LogoHeight;
DWORD BarWidth;
DWORD BarHeight;
} DLG_DATA, *PDLG_DATA;
static PDLG_DATA
DlgData_Create(HWND hwndDlg, PGINA_CONTEXT pgContext)
{
PDLG_DATA pDlgData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pDlgData));
if (pDlgData)
{
pDlgData->pgContext = pgContext;
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)pDlgData);
}
return pDlgData;
}
static VOID
DlgData_LoadBitmaps(_Inout_ PDLG_DATA pDlgData)
{
BITMAP bm;
if (!pDlgData)
{
return;
}
pDlgData->hLogoBitmap = LoadImageW(pDlgData->pgContext->hDllInstance,
MAKEINTRESOURCEW(IDI_ROSLOGO), IMAGE_BITMAP,
0, 0, LR_DEFAULTCOLOR);
if (pDlgData->hLogoBitmap)
{
GetObject(pDlgData->hLogoBitmap, sizeof(bm), &bm);
pDlgData->LogoWidth = bm.bmWidth;
pDlgData->LogoHeight = bm.bmHeight;
}
pDlgData->hBarBitmap = LoadImageW(hDllInstance, MAKEINTRESOURCEW(IDI_BAR),
IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
if (pDlgData->hBarBitmap)
{
GetObject(pDlgData->hBarBitmap, sizeof(bm), &bm);
pDlgData->BarWidth = bm.bmWidth;
pDlgData->BarHeight = bm.bmHeight;
}
}
static VOID
DlgData_Destroy(_Inout_ HWND hwndDlg)
{
PDLG_DATA pDlgData;
pDlgData = (PDLG_DATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
if (!pDlgData)
{
return;
}
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)NULL);
if (pDlgData->hBarBitmap)
{
DeleteObject(pDlgData->hBarBitmap);
}
if (pDlgData->hLogoBitmap)
{
DeleteObject(pDlgData->hLogoBitmap);
}
HeapFree(GetProcessHeap(), 0, pDlgData);
}
static BOOL
GUIInitialize(
IN OUT PGINA_CONTEXT pgContext)
{
TRACE("GUIInitialize(%p)\n", pgContext);
return TRUE;
}
static
VOID
SetWelcomeText(HWND hWnd)
{
PWCHAR pBuffer = NULL, p;
HKEY hKey;
DWORD BufSize, dwType, dwWelcomeSize, dwTitleLength;
LONG rc;
/* Open the Winlogon key */
rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
0,
KEY_QUERY_VALUE,
&hKey);
if (rc != ERROR_SUCCESS)
{
WARN("RegOpenKeyExW() failed with error %lu\n", rc);
return;
}
/* Get the size of the Welcome value */
dwWelcomeSize = 0;
rc = RegQueryValueExW(hKey,
L"Welcome",
NULL,
&dwType,
NULL,
&dwWelcomeSize);
if (rc == ERROR_FILE_NOT_FOUND || dwWelcomeSize == 0 || dwType != REG_SZ)
goto done;
dwTitleLength = GetWindowTextLengthW(hWnd);
BufSize = dwWelcomeSize + ((dwTitleLength + 1) * sizeof(WCHAR));
pBuffer = HeapAlloc(GetProcessHeap(), 0, BufSize);
if (pBuffer == NULL)
goto done;
GetWindowTextW(hWnd, pBuffer, BufSize / sizeof(WCHAR));
wcscat(pBuffer, L" ");
p = &pBuffer[dwTitleLength + 1];
RegQueryValueExW(hKey,
L"Welcome",
NULL,
&dwType,
(PBYTE)p,
&dwWelcomeSize);
SetWindowText(hWnd, pBuffer);
done:
if (pBuffer != NULL)
HeapFree(GetProcessHeap(), 0, pBuffer);
RegCloseKey(hKey);
}
static VOID
AdjustStatusMessageWindow(HWND hwndDlg, PDLG_DATA pDlgData)
{
INT xOld, yOld, cxOld, cyOld;
INT xNew, yNew, cxNew, cyNew;
INT cxLabel, cyLabel, dyLabel;
RECT rc, rcBar, rcLabel, rcWnd;
BITMAP bmLogo, bmBar;
DWORD style, exstyle;
HWND hwndLogo = GetDlgItem(hwndDlg, IDC_ROSLOGO);
HWND hwndBar = GetDlgItem(hwndDlg, IDC_BAR);
HWND hwndLabel = GetDlgItem(hwndDlg, IDC_STATUS_MESSAGE);
/* This adjustment is for CJK only */
switch (PRIMARYLANGID(GetUserDefaultLangID()))
{
case LANG_CHINESE:
case LANG_JAPANESE:
case LANG_KOREAN:
break;
default:
return;
}
if (!GetObjectW(pDlgData->hLogoBitmap, sizeof(BITMAP), &bmLogo) ||
!GetObjectW(pDlgData->hBarBitmap, sizeof(BITMAP), &bmBar))
{
return;
}
GetWindowRect(hwndBar, &rcBar);
MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcBar, 2);
dyLabel = bmLogo.bmHeight - rcBar.top;
GetWindowRect(hwndLabel, &rcLabel);
MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcLabel, 2);
cxLabel = rcLabel.right - rcLabel.left;
cyLabel = rcLabel.bottom - rcLabel.top;
MoveWindow(hwndLogo, 0, 0, bmLogo.bmWidth, bmLogo.bmHeight, TRUE);
MoveWindow(hwndBar, 0, bmLogo.bmHeight, bmLogo.bmWidth, bmBar.bmHeight, TRUE);
MoveWindow(hwndLabel, rcLabel.left, rcLabel.top + dyLabel, cxLabel, cyLabel, TRUE);
GetWindowRect(hwndDlg, &rcWnd);
xOld = rcWnd.left;
yOld = rcWnd.top;
cxOld = rcWnd.right - rcWnd.left;
cyOld = rcWnd.bottom - rcWnd.top;
GetClientRect(hwndDlg, &rc);
SetRect(&rc, 0, 0, bmLogo.bmWidth, rc.bottom - rc.top); /* new client size */
style = (DWORD)GetWindowLongPtrW(hwndDlg, GWL_STYLE);
exstyle = (DWORD)GetWindowLongPtrW(hwndDlg, GWL_EXSTYLE);
AdjustWindowRectEx(&rc, style, FALSE, exstyle);
cxNew = rc.right - rc.left;
cyNew = (rc.bottom - rc.top) + dyLabel;
xNew = xOld - (cxNew - cxOld) / 2;
yNew = yOld - (cyNew - cyOld) / 2;
MoveWindow(hwndDlg, xNew, yNew, cxNew, cyNew, TRUE);
}
static INT_PTR CALLBACK
StatusDialogProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PDLG_DATA pDlgData;
UNREFERENCED_PARAMETER(wParam);
pDlgData = (PDLG_DATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
PDISPLAYSTATUSMSG msg = (PDISPLAYSTATUSMSG)lParam;
if (!msg)
return FALSE;
msg->Context->hStatusWindow = hwndDlg;
if (msg->pTitle)
SetWindowTextW(hwndDlg, msg->pTitle);
SetDlgItemTextW(hwndDlg, IDC_STATUS_MESSAGE, msg->pMessage);
SetEvent(msg->StartupEvent);
pDlgData = DlgData_Create(hwndDlg, msg->Context);
if (pDlgData == NULL)
return FALSE;
DlgData_LoadBitmaps(pDlgData);
if (pDlgData->hBarBitmap)
{
if (SetTimer(hwndDlg, IDT_BAR, 20, NULL) == 0)
{
ERR("SetTimer(IDT_BAR) failed: %d\n", GetLastError());
}
else
{
/* Get the animation bar control */
pDlgData->hWndBarCtrl = GetDlgItem(hwndDlg, IDC_BAR);
}
}
AdjustStatusMessageWindow(hwndDlg, pDlgData);
return TRUE;
}
case WM_TIMER:
{
if (pDlgData && pDlgData->hBarBitmap)
{
/*
* Default rotation bar image width is 413 (same as logo)
* We can divide 413 by 7 without remainder
*/
pDlgData->BarCounter = (pDlgData->BarCounter + 7) % pDlgData->BarWidth;
InvalidateRect(pDlgData->hWndBarCtrl, NULL, FALSE);
UpdateWindow(pDlgData->hWndBarCtrl);
}
return TRUE;
}
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT lpDis = (LPDRAWITEMSTRUCT)lParam;
if (lpDis->CtlID != IDC_BAR)
{
return FALSE;
}
if (pDlgData && pDlgData->hBarBitmap)
{
HDC hdcMem;
HGDIOBJ hOld;
DWORD off = pDlgData->BarCounter;
DWORD iw = pDlgData->BarWidth;
DWORD ih = pDlgData->BarHeight;
hdcMem = CreateCompatibleDC(lpDis->hDC);
hOld = SelectObject(hdcMem, pDlgData->hBarBitmap);
BitBlt(lpDis->hDC, off, 0, iw - off, ih, hdcMem, 0, 0, SRCCOPY);
BitBlt(lpDis->hDC, 0, 0, off, ih, hdcMem, iw - off, 0, SRCCOPY);
SelectObject(hdcMem, hOld);
DeleteDC(hdcMem);
return TRUE;
}
return FALSE;
}
case WM_DESTROY:
{
if (pDlgData && pDlgData->hBarBitmap)
{
KillTimer(hwndDlg, IDT_BAR);
}
DlgData_Destroy(hwndDlg);
return TRUE;
}
}
return FALSE;
}
static DWORD WINAPI
StartupWindowThread(LPVOID lpParam)
{
HDESK hDesk;
PDISPLAYSTATUSMSG msg = (PDISPLAYSTATUSMSG)lpParam;
/* When SetThreadDesktop is called the system closes the desktop handle when needed
so we have to create a new handle because this handle may still be in use by winlogon */
if (!DuplicateHandle ( GetCurrentProcess(),
msg->hDesktop,
GetCurrentProcess(),
(HANDLE*)&hDesk,
0,
FALSE,
DUPLICATE_SAME_ACCESS))
{
ERR("Duplicating handle failed!\n");
HeapFree(GetProcessHeap(), 0, lpParam);
return FALSE;
}
if(!SetThreadDesktop(hDesk))
{
ERR("Setting thread desktop failed!\n");
HeapFree(GetProcessHeap(), 0, lpParam);
return FALSE;
}
DialogBoxParamW(
hDllInstance,
MAKEINTRESOURCEW(IDD_STATUS),
GetDesktopWindow(),
StatusDialogProc,
(LPARAM)lpParam);
HeapFree(GetProcessHeap(), 0, lpParam);
return TRUE;
}
static BOOL
GUIDisplayStatusMessage(
IN PGINA_CONTEXT pgContext,
IN HDESK hDesktop,
IN DWORD dwOptions,
IN PWSTR pTitle,
IN PWSTR pMessage)
{
PDISPLAYSTATUSMSG msg;
HANDLE Thread;
DWORD ThreadId;
TRACE("GUIDisplayStatusMessage(%ws)\n", pMessage);
if (!pgContext->hStatusWindow)
{
/*
* If everything goes correctly, 'msg' is freed
* by the 'StartupWindowThread' thread.
*/
msg = (PDISPLAYSTATUSMSG)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(*msg));
if(!msg)
return FALSE;
msg->Context = pgContext;
msg->dwOptions = dwOptions;
msg->pTitle = pTitle;
msg->pMessage = pMessage;
msg->hDesktop = hDesktop;
msg->StartupEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
if (!msg->StartupEvent)
{
HeapFree(GetProcessHeap(), 0, msg);
return FALSE;
}
Thread = CreateThread(NULL,
0,
StartupWindowThread,
(PVOID)msg,
0,
&ThreadId);
if (Thread)
{
/* 'msg' will be freed by 'StartupWindowThread' */
CloseHandle(Thread);
WaitForSingleObject(msg->StartupEvent, INFINITE);
CloseHandle(msg->StartupEvent);
return TRUE;
}
else
{
/*
* The 'StartupWindowThread' thread couldn't be created,
* so we need to free the allocated 'msg'.
*/
HeapFree(GetProcessHeap(), 0, msg);
}
return FALSE;
}
if (pTitle)
SetWindowTextW(pgContext->hStatusWindow, pTitle);
SetDlgItemTextW(pgContext->hStatusWindow, IDC_STATUS_MESSAGE, pMessage);
return TRUE;
}
static BOOL
GUIRemoveStatusMessage(
IN PGINA_CONTEXT pgContext)
{
if (pgContext->hStatusWindow)
{
EndDialog(pgContext->hStatusWindow, 0);
pgContext->hStatusWindow = NULL;
}
return TRUE;
}
static INT_PTR CALLBACK
WelcomeDialogProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PDLG_DATA pDlgData;
pDlgData = (PDLG_DATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
pDlgData = DlgData_Create(hwndDlg, (PGINA_CONTEXT)lParam);
if (pDlgData == NULL)
return FALSE;
DlgData_LoadBitmaps(pDlgData);
return TRUE;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
if (pDlgData && pDlgData->hLogoBitmap)
{
BeginPaint(hwndDlg, &ps);
DrawStateW(ps.hdc, NULL, NULL, (LPARAM)pDlgData->hLogoBitmap, (WPARAM)0, 0, 0, 0, 0, DST_BITMAP);
EndPaint(hwndDlg, &ps);
}
return TRUE;
}
case WM_DESTROY:
{
DlgData_Destroy(hwndDlg);
return TRUE;
}
}
return FALSE;
}
static VOID
GUIDisplaySASNotice(
IN OUT PGINA_CONTEXT pgContext)
{
/* Display the notice window */
pgContext->pWlxFuncs->WlxDialogBoxParam(pgContext->hWlx,
pgContext->hDllInstance,
MAKEINTRESOURCEW(IDD_WELCOME),
GetDesktopWindow(),
WelcomeDialogProc,
(LPARAM)pgContext);
}
/* Get the text contained in a textbox. Allocates memory in pText
* to contain the text. Returns TRUE in case of success */
static BOOL
GetTextboxText(
IN HWND hwndDlg,
IN INT TextboxId,
OUT LPWSTR *pText)
{
LPWSTR Text;
int Count;
Count = GetWindowTextLength(GetDlgItem(hwndDlg, TextboxId));
Text = HeapAlloc(GetProcessHeap(), 0, (Count + 1) * sizeof(WCHAR));
if (!Text)
return FALSE;
if (Count != GetWindowTextW(GetDlgItem(hwndDlg, TextboxId), Text, Count + 1))
{
HeapFree(GetProcessHeap(), 0, Text);
return FALSE;
}
*pText = Text;
return TRUE;
}
static
INT
ResourceMessageBox(
IN PGINA_CONTEXT pgContext,
IN HWND hwnd,
IN UINT uType,
IN UINT uCaption,
IN UINT uText)
{
WCHAR szCaption[256];
WCHAR szText[256];
LoadStringW(pgContext->hDllInstance, uCaption, szCaption, _countof(szCaption));
LoadStringW(pgContext->hDllInstance, uText, szText, _countof(szText));
return pgContext->pWlxFuncs->WlxMessageBox(pgContext->hWlx,
hwnd,
szText,
szCaption,
uType);
}
static
BOOL
DoChangePassword(
IN PGINA_CONTEXT pgContext,
IN HWND hwndDlg)
{
WCHAR UserName[256];
WCHAR Domain[256];
WCHAR OldPassword[256];
WCHAR NewPassword1[256];
WCHAR NewPassword2[256];
PMSV1_0_CHANGEPASSWORD_REQUEST RequestBuffer = NULL;
PMSV1_0_CHANGEPASSWORD_RESPONSE ResponseBuffer = NULL;
ULONG RequestBufferSize;
ULONG ResponseBufferSize = 0;
LPWSTR Ptr;
BOOL res = FALSE;
NTSTATUS ProtocolStatus;
NTSTATUS Status;
GetDlgItemTextW(hwndDlg, IDC_CHANGEPWD_USERNAME, UserName, _countof(UserName));
GetDlgItemTextW(hwndDlg, IDC_CHANGEPWD_DOMAIN, Domain, _countof(Domain));
GetDlgItemTextW(hwndDlg, IDC_CHANGEPWD_OLDPWD, OldPassword, _countof(OldPassword));
GetDlgItemTextW(hwndDlg, IDC_CHANGEPWD_NEWPWD1, NewPassword1, _countof(NewPassword1));
GetDlgItemTextW(hwndDlg, IDC_CHANGEPWD_NEWPWD2, NewPassword2, _countof(NewPassword2));
/* Compare the two passwords and fail if they do not match */
if (wcscmp(NewPassword1, NewPassword2) != 0)
{
ResourceMessageBox(pgContext,
hwndDlg,
MB_OK | MB_ICONEXCLAMATION,
IDS_CHANGEPWDTITLE,
IDS_NONMATCHINGPASSWORDS);
goto done;
}
/* Calculate the request buffer size */
RequestBufferSize = sizeof(MSV1_0_CHANGEPASSWORD_REQUEST) +
((wcslen(Domain) + 1) * sizeof(WCHAR)) +
((wcslen(UserName) + 1) * sizeof(WCHAR)) +
((wcslen(OldPassword) + 1) * sizeof(WCHAR)) +
((wcslen(NewPassword1) + 1) * sizeof(WCHAR));
/* Allocate the request buffer */
RequestBuffer = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
RequestBufferSize);
if (RequestBuffer == NULL)
{
ERR("HeapAlloc failed\n");
goto done;
}
/* Initialize the request buffer */
RequestBuffer->MessageType = MsV1_0ChangePassword;
RequestBuffer->Impersonating = TRUE;
Ptr = (LPWSTR)((ULONG_PTR)RequestBuffer + sizeof(MSV1_0_CHANGEPASSWORD_REQUEST));
/* Pack the domain name */
RequestBuffer->DomainName.Length = (USHORT)wcslen(Domain) * sizeof(WCHAR);
RequestBuffer->DomainName.MaximumLength = RequestBuffer->DomainName.Length + sizeof(WCHAR);
RequestBuffer->DomainName.Buffer = Ptr;
RtlCopyMemory(RequestBuffer->DomainName.Buffer,
Domain,
RequestBuffer->DomainName.MaximumLength);
Ptr = (LPWSTR)((ULONG_PTR)Ptr + RequestBuffer->DomainName.MaximumLength);
/* Pack the user name */
RequestBuffer->AccountName.Length = (USHORT)wcslen(UserName) * sizeof(WCHAR);
RequestBuffer->AccountName.MaximumLength = RequestBuffer->AccountName.Length + sizeof(WCHAR);
RequestBuffer->AccountName.Buffer = Ptr;
RtlCopyMemory(RequestBuffer->AccountName.Buffer,
UserName,
RequestBuffer->AccountName.MaximumLength);
Ptr = (LPWSTR)((ULONG_PTR)Ptr + RequestBuffer->AccountName.MaximumLength);
/* Pack the old password */
RequestBuffer->OldPassword.Length = (USHORT)wcslen(OldPassword) * sizeof(WCHAR);
RequestBuffer->OldPassword.MaximumLength = RequestBuffer->OldPassword.Length + sizeof(WCHAR);
RequestBuffer->OldPassword.Buffer = Ptr;
RtlCopyMemory(RequestBuffer->OldPassword.Buffer,
OldPassword,
RequestBuffer->OldPassword.MaximumLength);
Ptr = (LPWSTR)((ULONG_PTR)Ptr + RequestBuffer->OldPassword.MaximumLength);
/* Pack the new password */
RequestBuffer->NewPassword.Length = (USHORT)wcslen(NewPassword1) * sizeof(WCHAR);
RequestBuffer->NewPassword.MaximumLength = RequestBuffer->NewPassword.Length + sizeof(WCHAR);
RequestBuffer->NewPassword.Buffer = Ptr;
RtlCopyMemory(RequestBuffer->NewPassword.Buffer,
NewPassword1,
RequestBuffer->NewPassword.MaximumLength);
/* Connect to the LSA server */
if (ConnectToLsa(pgContext) != ERROR_SUCCESS)
{
ERR("ConnectToLsa() failed\n");
goto done;
}
/* Call the authentication package */
Status = LsaCallAuthenticationPackage(pgContext->LsaHandle,
pgContext->AuthenticationPackage,
RequestBuffer,
RequestBufferSize,
(PVOID*)&ResponseBuffer,
&ResponseBufferSize,
&ProtocolStatus);
if (!NT_SUCCESS(Status))
{
ERR("LsaCallAuthenticationPackage failed (Status 0x%08lx)\n", Status);
goto done;
}
if (!NT_SUCCESS(ProtocolStatus))
{
TRACE("LsaCallAuthenticationPackage failed (ProtocolStatus 0x%08lx)\n", ProtocolStatus);
goto done;
}
res = TRUE;
ResourceMessageBox(pgContext,
hwndDlg,
MB_OK | MB_ICONINFORMATION,
IDS_CHANGEPWDTITLE,
IDS_PASSWORDCHANGED);
if ((wcscmp(UserName, pgContext->UserName) == 0) &&
(wcscmp(Domain, pgContext->DomainName) == 0) &&
(wcscmp(OldPassword, pgContext->Password) == 0))
{
SecureZeroMemory(pgContext->Password, sizeof(pgContext->Password));
wcscpy(pgContext->Password, NewPassword1);
}
done:
/* Zero out the password buffers */
SecureZeroMemory(&OldPassword, sizeof(OldPassword));
SecureZeroMemory(&NewPassword1, sizeof(NewPassword1));
SecureZeroMemory(&NewPassword2, sizeof(NewPassword2));
if (RequestBuffer != NULL)
{
/* Zero out the password buffers before freeing them */
SecureZeroMemory(RequestBuffer->OldPassword.Buffer,
RequestBuffer->OldPassword.MaximumLength);
SecureZeroMemory(&RequestBuffer->OldPassword, sizeof(RequestBuffer->OldPassword));
SecureZeroMemory(RequestBuffer->NewPassword.Buffer,
RequestBuffer->NewPassword.MaximumLength);
SecureZeroMemory(&RequestBuffer->NewPassword, sizeof(RequestBuffer->NewPassword));
HeapFree(GetProcessHeap(), 0, RequestBuffer);
}
if (ResponseBuffer != NULL)
LsaFreeReturnBuffer(ResponseBuffer);
return res;
}
static INT_PTR CALLBACK
ChangePasswordDialogProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PGINA_CONTEXT pgContext;
pgContext = (PGINA_CONTEXT)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
pgContext = (PGINA_CONTEXT)lParam;
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)pgContext);
SetDlgItemTextW(hwndDlg, IDC_CHANGEPWD_USERNAME, pgContext->UserName);
SendDlgItemMessageW(hwndDlg, IDC_CHANGEPWD_DOMAIN, CB_ADDSTRING, 0, (LPARAM)pgContext->DomainName);
SendDlgItemMessageW(hwndDlg, IDC_CHANGEPWD_DOMAIN, CB_SETCURSEL, 0, 0);
SetFocus(GetDlgItem(hwndDlg, IDC_CHANGEPWD_OLDPWD));
return FALSE; // Default focus is changed.
}
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
if (DoChangePassword(pgContext, hwndDlg))
{
EndDialog(hwndDlg, TRUE);
}
else
{
SetDlgItemTextW(hwndDlg, IDC_CHANGEPWD_OLDPWD, NULL);
SetDlgItemTextW(hwndDlg, IDC_CHANGEPWD_NEWPWD1, NULL);
SetDlgItemTextW(hwndDlg, IDC_CHANGEPWD_NEWPWD2, NULL);
SendMessageW(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_CHANGEPWD_OLDPWD), TRUE);
}
return TRUE;
case IDCANCEL:
EndDialog(hwndDlg, FALSE);
return TRUE;
}
break;
case WM_CLOSE:
EndDialog(hwndDlg, FALSE);
return TRUE;
}
return FALSE;
}
static VOID
OnInitSecurityDlg(
_In_ HWND hwnd,
_In_ PGINA_CONTEXT pgContext)
{
HKEY hKeyCurrentUser, hKey;
DWORD dwValue;
LONG lRet;
WCHAR Buffer1[256];
WCHAR Buffer2[256];
WCHAR Buffer3[256];
WCHAR Buffer4[512];
LoadStringW(pgContext->hDllInstance, IDS_LOGONMSG, Buffer1, _countof(Buffer1));
wsprintfW(Buffer2, L"%s\\%s", pgContext->DomainName, pgContext->UserName);
wsprintfW(Buffer4, Buffer1, Buffer2);
SetDlgItemTextW(hwnd, IDC_SECURITY_MESSAGE, Buffer4);
LoadStringW(pgContext->hDllInstance, IDS_LOGONDATE, Buffer1, _countof(Buffer1));
GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE,
(SYSTEMTIME*)&pgContext->LogonTime, NULL, Buffer2, _countof(Buffer2));
GetTimeFormatW(LOCALE_USER_DEFAULT, 0,
(SYSTEMTIME*)&pgContext->LogonTime, NULL, Buffer3, _countof(Buffer3));
wsprintfW(Buffer4, Buffer1, Buffer2, Buffer3);
SetDlgItemTextW(hwnd, IDC_SECURITY_LOGONDATE, Buffer4);
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
0,
KEY_QUERY_VALUE, &hKey);
if (lRet == ERROR_SUCCESS)
{
lRet = ReadRegDwordValue(hKey, L"DisableLockWorkstation", &dwValue);
if ((lRet == ERROR_SUCCESS) && !!dwValue)
EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_LOCK), FALSE);
RegCloseKey(hKey);
}
/* Open the per-user registry key */
lRet = RegOpenLoggedOnHKCU(pgContext->UserToken,
KEY_QUERY_VALUE,
&hKeyCurrentUser);
if (lRet != ERROR_SUCCESS)
{
/* We couldn't, bail out */
return;
}
lRet = RegOpenKeyExW(hKeyCurrentUser,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
0,
KEY_QUERY_VALUE, &hKey);
if (lRet == ERROR_SUCCESS)
{
lRet = ReadRegDwordValue(hKey, L"DisableLockWorkstation", &dwValue);
if ((lRet == ERROR_SUCCESS) && !!dwValue)
EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_LOCK), FALSE);
lRet = ReadRegDwordValue(hKey, L"DisableChangePassword", &dwValue);
if ((lRet == ERROR_SUCCESS) && !!dwValue)
EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_CHANGEPWD), FALSE);
lRet = ReadRegDwordValue(hKey, L"DisableTaskMgr", &dwValue);
if ((lRet == ERROR_SUCCESS) && !!dwValue)
EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_TASKMGR), FALSE);
RegCloseKey(hKey);
}
lRet = RegOpenKeyExW(hKeyCurrentUser,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer",
0,
KEY_QUERY_VALUE, &hKey);
if (lRet == ERROR_SUCCESS)
{
lRet = ReadRegDwordValue(hKey, L"NoLogoff", &dwValue);
if ((lRet == ERROR_SUCCESS) && !!dwValue)
EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_LOGOFF), FALSE);
// TODO: Disable also if "NoDisconnect" on Terminal Services
/* Disable the "Shutdown" button if the user doesn't have shutdown privilege */
lRet = ReadRegDwordValue(hKey, L"NoClose", &dwValue);
if (((lRet == ERROR_SUCCESS) && !!dwValue) ||
!TestTokenPrivilege(pgContext->UserToken, SE_SHUTDOWN_PRIVILEGE))
{
EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_SHUTDOWN), FALSE);
}
RegCloseKey(hKey);
}
RegCloseKey(hKeyCurrentUser);
}
static BOOL
OnChangePassword(
IN HWND hwnd,
IN PGINA_CONTEXT pgContext)
{
INT res;
TRACE("OnChangePassword()\n");
res = pgContext->pWlxFuncs->WlxDialogBoxParam(
pgContext->hWlx,
pgContext->hDllInstance,
MAKEINTRESOURCEW(IDD_CHANGEPWD),
hwnd,
ChangePasswordDialogProc,
(LPARAM)pgContext);
TRACE("Result: %x\n", res);
return FALSE;
}
static INT_PTR CALLBACK
LogOffDialogProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
case IDCANCEL:
EndDialog(hwndDlg, LOWORD(wParam));
return TRUE;
}
break;
case WM_CLOSE:
EndDialog(hwndDlg, IDCANCEL);
return TRUE;
}
return FALSE;
}
static
INT
OnLogOff(
IN HWND hwndDlg,
IN PGINA_CONTEXT pgContext)
{
return pgContext->pWlxFuncs->WlxDialogBoxParam(
pgContext->hWlx,
pgContext->hDllInstance,
MAKEINTRESOURCEW(IDD_LOGOFF),
hwndDlg,
LogOffDialogProc,
(LPARAM)pgContext);
}
static
INT
OnShutDown(
_In_ HWND hwndDlg,
_In_ PGINA_CONTEXT pgContext,
_In_ DWORD dwExcludeOptions)
{
HKEY hKeyCurrentUser = NULL;
DWORD ShutdownOptions = 0;
INT ret;
/* Open the per-user registry key */
if (RegOpenLoggedOnHKCU(pgContext->UserToken,
KEY_QUERY_VALUE | KEY_CREATE_SUB_KEY | KEY_SET_VALUE,
&hKeyCurrentUser) != ERROR_SUCCESS)
{
ERR("RegOpenLoggedOnHKCU() failed with error %ld\n", GetLastError());
}
pgContext->nShutdownAction = 0;
if (hKeyCurrentUser)
{
ShutdownOptions = GetAllowedShutdownOptions(hKeyCurrentUser, pgContext->UserToken);
pgContext->nShutdownAction = LoadShutdownSelState(hKeyCurrentUser);
}
ShutdownOptions &= ~dwExcludeOptions;
ret = ShutdownDialog(hwndDlg, ShutdownOptions, pgContext);
if ((ret == IDOK) && hKeyCurrentUser)
SaveShutdownSelState(hKeyCurrentUser, pgContext->nShutdownAction);
if (hKeyCurrentUser)
RegCloseKey(hKeyCurrentUser);
return ret;
}
static INT_PTR CALLBACK
SecurityDialogProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PGINA_CONTEXT pgContext;
pgContext = (PGINA_CONTEXT)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
pgContext = (PGINA_CONTEXT)lParam;
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)pgContext);
SetWelcomeText(hwndDlg);
OnInitSecurityDlg(hwndDlg, (PGINA_CONTEXT)lParam);
return TRUE;
}
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_SECURITY_LOCK:
EndDialog(hwndDlg, WLX_SAS_ACTION_LOCK_WKSTA);
return TRUE;
case IDC_SECURITY_LOGOFF:
if (ISKEYDOWN(VK_CONTROL))
{
if (ResourceMessageBox(pgContext,
hwndDlg,
MB_OKCANCEL | MB_ICONSTOP,
IDS_EMERGENCYLOGOFFTITLE,
IDS_EMERGENCYLOGOFF) == IDOK)
{
EndDialog(hwndDlg, WLX_SAS_ACTION_FORCE_LOGOFF);
}
}
else if (OnLogOff(hwndDlg, pgContext) == IDOK)
{
EndDialog(hwndDlg, WLX_SAS_ACTION_LOGOFF);
}
return TRUE;
case IDC_SECURITY_SHUTDOWN:
/* Emergency restart feature */
if (ISKEYDOWN(VK_CONTROL))
{
if (ResourceMessageBox(pgContext,
hwndDlg,
MB_OKCANCEL | MB_ICONSTOP,
IDS_EMERGENCYRESTARTTITLE,
IDS_EMERGENCYRESTART) == IDOK)
{
BOOLEAN Old;
ERR("Emergency restarting NT...\n");
RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &Old);
NtShutdownSystem(ShutdownReboot);
RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, Old, FALSE, &Old);
}
}
else if (OnShutDown(hwndDlg, pgContext, 0) == IDOK)
{
EndDialog(hwndDlg, pgContext->nShutdownAction);
}
return TRUE;
case IDC_SECURITY_CHANGEPWD:
if (OnChangePassword(hwndDlg, pgContext))
EndDialog(hwndDlg, WLX_SAS_ACTION_PWD_CHANGED);
return TRUE;
case IDC_SECURITY_TASKMGR:
EndDialog(hwndDlg, WLX_SAS_ACTION_TASKLIST);
return TRUE;
case IDCANCEL:
EndDialog(hwndDlg, WLX_SAS_ACTION_NONE);
return TRUE;
}
break;
}
case WM_CLOSE:
{
EndDialog(hwndDlg, WLX_SAS_ACTION_NONE);
return TRUE;
}
}
return FALSE;
}
static INT
GUILoggedOnSAS(
IN OUT PGINA_CONTEXT pgContext,
IN DWORD dwSasType)
{
INT result;
if (dwSasType != WLX_SAS_TYPE_CTRL_ALT_DEL)
{
/* Nothing to do for WLX_SAS_TYPE_TIMEOUT; the dialog will
* close itself thanks to the use of WlxDialogBoxParam */
return WLX_SAS_ACTION_NONE;
}
result = pgContext->pWlxFuncs->WlxDialogBoxParam(
pgContext->hWlx,
pgContext->hDllInstance,
MAKEINTRESOURCEW(IDD_SECURITY),
GetDesktopWindow(),
SecurityDialogProc,
(LPARAM)pgContext);
if (result < WLX_SAS_ACTION_LOGON ||
result > WLX_SAS_ACTION_SWITCH_CONSOLE)
{
result = WLX_SAS_ACTION_NONE;
}
return result;
}
static
BOOL
DoLogon(
IN HWND hwndDlg,
IN OUT PGINA_CONTEXT pgContext)
{
LPWSTR UserName = NULL;
LPWSTR Password = NULL;
LPWSTR Domain = NULL;
BOOL result = FALSE;
NTSTATUS Status, SubStatus = STATUS_SUCCESS;
if (GetTextboxText(hwndDlg, IDC_LOGON_USERNAME, &UserName) && *UserName == '\0')
goto done;
if (GetTextboxText(hwndDlg, IDC_LOGON_DOMAIN, &Domain) && *Domain == '\0')
goto done;
if (!GetTextboxText(hwndDlg, IDC_LOGON_PASSWORD, &Password))
goto done;
Status = DoLoginTasks(pgContext, UserName, Domain, Password, &SubStatus);
if (Status == STATUS_LOGON_FAILURE)
{
ResourceMessageBox(pgContext,
hwndDlg,
MB_OK | MB_ICONEXCLAMATION,
IDS_LOGONTITLE,
IDS_LOGONWRONGUSERORPWD);
goto done;
}
else if (Status == STATUS_ACCOUNT_RESTRICTION)
{
TRACE("DoLoginTasks failed! Status 0x%08lx SubStatus 0x%08lx\n", Status, SubStatus);
if (SubStatus == STATUS_ACCOUNT_DISABLED)
{
ResourceMessageBox(pgContext,
hwndDlg,
MB_OK | MB_ICONEXCLAMATION,
IDS_LOGONTITLE,
IDS_LOGONUSERDISABLED);
goto done;
}
else if (SubStatus == STATUS_ACCOUNT_LOCKED_OUT)
{
TRACE("Account locked!\n");
ResourceMessageBox(pgContext,
hwndDlg,
MB_OK | MB_ICONERROR,
IDS_LOGONTITLE,
IDS_ACCOUNTLOCKED);
goto done;
}
else if ((SubStatus == STATUS_PASSWORD_MUST_CHANGE) ||
(SubStatus == STATUS_PASSWORD_EXPIRED))
{
if (SubStatus == STATUS_PASSWORD_MUST_CHANGE)
{
ResourceMessageBox(pgContext,
hwndDlg,
MB_OK | MB_ICONSTOP,
IDS_LOGONTITLE,
IDS_PASSWORDMUSTCHANGE);
}
else
{
ResourceMessageBox(pgContext,
hwndDlg,
MB_OK | MB_ICONSTOP,
IDS_LOGONTITLE,
IDS_PASSWORDEXPIRED);
}
if (!OnChangePassword(hwndDlg, pgContext))
goto done;
Status = DoLoginTasks(pgContext,
pgContext->UserName,
pgContext->DomainName,
pgContext->Password,
&SubStatus);
if (!NT_SUCCESS(Status))
{
TRACE("Login after password change failed! (Status 0x%08lx)\n", Status);
goto done;
}
}
else if (SubStatus == STATUS_ACCOUNT_EXPIRED)
{
ResourceMessageBox(pgContext,
hwndDlg,
MB_OK | MB_ICONEXCLAMATION,
IDS_LOGONTITLE,
IDS_ACCOUNTEXPIRED);
}
else if (SubStatus == STATUS_INVALID_LOGON_HOURS)
{
ResourceMessageBox(pgContext,
hwndDlg,
MB_OK | MB_ICONERROR,
IDS_LOGONTITLE,
IDS_INVALIDLOGONHOURS);
goto done;
}
else if (SubStatus == STATUS_INVALID_WORKSTATION)
{
ResourceMessageBox(pgContext,
hwndDlg,
MB_OK | MB_ICONERROR,
IDS_LOGONTITLE,
IDS_INVALIDWORKSTATION);
goto done;
}
else
{
TRACE("Other error!\n");
ResourceMessageBox(pgContext,
hwndDlg,
MB_OK | MB_ICONERROR,
IDS_LOGONTITLE,
IDS_ACCOUNTRESTRICTION);
goto done;
}
}
else if (!NT_SUCCESS(Status))
{
TRACE("DoLoginTasks failed! Status 0x%08lx\n", Status);
goto done;
}
if (!CreateProfile(pgContext, UserName, Domain, Password))
{
ERR("Failed to create the profile!\n");
goto done;
}
SecureZeroMemory(pgContext->Password, sizeof(pgContext->Password));
wcscpy(pgContext->Password, Password);
result = TRUE;
done:
pgContext->bAutoAdminLogon = FALSE;
if (UserName != NULL)
HeapFree(GetProcessHeap(), 0, UserName);
if (Password != NULL)
{
/* Zero out the password buffer before freeing it */
SIZE_T pwdLen = (wcslen(Password) + 1) * sizeof(WCHAR);
SecureZeroMemory(Password, pwdLen);
HeapFree(GetProcessHeap(), 0, Password);
}
if (Domain != NULL)
HeapFree(GetProcessHeap(), 0, Domain);
return result;
}
static
VOID
SetDomainComboBox(
HWND hwndDomainComboBox,
PGINA_CONTEXT pgContext)
{
WCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD dwComputerNameLength;
LONG lIndex = 0;
LONG lFindIndex;
SendMessageW(hwndDomainComboBox, CB_RESETCONTENT, 0, 0);
dwComputerNameLength = _countof(szComputerName);
if (GetComputerNameW(szComputerName, &dwComputerNameLength))
{
lIndex = SendMessageW(hwndDomainComboBox, CB_ADDSTRING, 0, (LPARAM)szComputerName);
}
if (pgContext->DomainName[0])
{
lFindIndex = SendMessageW(hwndDomainComboBox, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)pgContext->DomainName);
if (lFindIndex == CB_ERR)
{
lIndex = SendMessageW(hwndDomainComboBox, CB_ADDSTRING, 0, (LPARAM)pgContext->DomainName);
}
else
{
lIndex = lFindIndex;
}
}
SendMessageW(hwndDomainComboBox, CB_SETCURSEL, lIndex, 0);
}
static INT_PTR CALLBACK
LogonDialogProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PDLG_DATA pDlgData;
pDlgData = (PDLG_DATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
/* FIXME: take care of NoDomainUI */
pDlgData = DlgData_Create(hwndDlg, (PGINA_CONTEXT)lParam);
if (pDlgData == NULL)
return FALSE;
DlgData_LoadBitmaps(pDlgData);
SetWelcomeText(hwndDlg);
if (pDlgData->pgContext->bAutoAdminLogon ||
!pDlgData->pgContext->bDontDisplayLastUserName)
{
SetDlgItemTextW(hwndDlg, IDC_LOGON_USERNAME, pDlgData->pgContext->UserName);
}
if (pDlgData->pgContext->bAutoAdminLogon)
SetDlgItemTextW(hwndDlg, IDC_LOGON_PASSWORD, pDlgData->pgContext->Password);
SetDomainComboBox(GetDlgItem(hwndDlg, IDC_LOGON_DOMAIN), pDlgData->pgContext);
if (pDlgData->pgContext->bDisableCAD)
EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE);
if (!pDlgData->pgContext->bShutdownWithoutLogon)
EnableWindow(GetDlgItem(hwndDlg, IDC_LOGON_SHUTDOWN), FALSE);
SetFocus(GetDlgItem(hwndDlg, pDlgData->pgContext->bDontDisplayLastUserName ? IDC_LOGON_USERNAME : IDC_LOGON_PASSWORD));
if (pDlgData->pgContext->bAutoAdminLogon)
PostMessage(GetDlgItem(hwndDlg, IDOK), BM_CLICK, 0, 0);
return FALSE; // Default focus is changed.
}
case WM_PAINT:
{
PAINTSTRUCT ps;
if (pDlgData && pDlgData->hLogoBitmap)
{
BeginPaint(hwndDlg, &ps);
DrawStateW(ps.hdc, NULL, NULL, (LPARAM)pDlgData->hLogoBitmap, (WPARAM)0, 0, 0, 0, 0, DST_BITMAP);
EndPaint(hwndDlg, &ps);
}
return TRUE;
}
case WM_DESTROY:
DlgData_Destroy(hwndDlg);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
if (DoLogon(hwndDlg, pDlgData->pgContext))
{
EndDialog(hwndDlg, WLX_SAS_ACTION_LOGON);
}
else
{
SetDlgItemTextW(hwndDlg, IDC_LOGON_PASSWORD, NULL);
SendMessageW(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_LOGON_PASSWORD), TRUE);
}
return TRUE;
case IDCANCEL:
EndDialog(hwndDlg, WLX_SAS_ACTION_NONE);
return TRUE;
case IDC_LOGON_SHUTDOWN:
if (OnShutDown(hwndDlg, pDlgData->pgContext,
WLX_SHUTDOWN_STATE_DISCONNECT | WLX_SHUTDOWN_STATE_LOGOFF) == IDOK)
{
EndDialog(hwndDlg, pDlgData->pgContext->nShutdownAction);
}
return TRUE;
}
break;
}
return FALSE;
}
static
INT_PTR
CALLBACK
LegalNoticeDialogProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PLEGALNOTICEDATA pLegalNotice;
switch (uMsg)
{
case WM_INITDIALOG:
pLegalNotice = (PLEGALNOTICEDATA)lParam;
SetWindowTextW(hwndDlg, pLegalNotice->pszCaption);
SetDlgItemTextW(hwndDlg, IDC_LEGALNOTICE_TEXT, pLegalNotice->pszText);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
case IDCANCEL:
EndDialog(hwndDlg, 0);
return TRUE;
}
break;
}
return FALSE;
}
static INT
GUILoggedOutSAS(
IN OUT PGINA_CONTEXT pgContext)
{
LEGALNOTICEDATA LegalNotice = {NULL, NULL};
HKEY hKey = NULL;
LONG rc;
int result;
TRACE("GUILoggedOutSAS()\n");
rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
0,
KEY_QUERY_VALUE,
&hKey);
if (rc == ERROR_SUCCESS)
{
ReadRegSzValue(hKey,
L"LegalNoticeCaption",
&LegalNotice.pszCaption);
ReadRegSzValue(hKey,
L"LegalNoticeText",
&LegalNotice.pszText);
RegCloseKey(hKey);
}
if (LegalNotice.pszCaption != NULL && LegalNotice.pszCaption[0] &&
LegalNotice.pszText != NULL && LegalNotice.pszText[0])
{
pgContext->pWlxFuncs->WlxDialogBoxParam(pgContext->hWlx,
pgContext->hDllInstance,
MAKEINTRESOURCEW(IDD_LEGALNOTICE),
GetDesktopWindow(),
LegalNoticeDialogProc,
(LPARAM)&LegalNotice);
}
if (LegalNotice.pszCaption != NULL)
HeapFree(GetProcessHeap(), 0, LegalNotice.pszCaption);
if (LegalNotice.pszText != NULL)
HeapFree(GetProcessHeap(), 0, LegalNotice.pszText);
result = pgContext->pWlxFuncs->WlxDialogBoxParam(
pgContext->hWlx,
pgContext->hDllInstance,
MAKEINTRESOURCEW(IDD_LOGON),
GetDesktopWindow(),
LogonDialogProc,
(LPARAM)pgContext);
if (result >= WLX_SAS_ACTION_LOGON &&
result <= WLX_SAS_ACTION_SWITCH_CONSOLE)
{
WARN("WlxLoggedOutSAS() returns 0x%x\n", result);
return result;
}
WARN("WlxDialogBoxParam() failed (0x%x)\n", result);
return WLX_SAS_ACTION_NONE;
}
static VOID
SetLockMessage(HWND hwnd,
INT nDlgItem,
PGINA_CONTEXT pgContext)
{
WCHAR Buffer1[256];
WCHAR Buffer2[256];
WCHAR Buffer3[512];
LoadStringW(pgContext->hDllInstance, IDS_LOCKMSG, Buffer1, _countof(Buffer1));
wsprintfW(Buffer2, L"%s\\%s", pgContext->DomainName, pgContext->UserName);
wsprintfW(Buffer3, Buffer1, Buffer2);
SetDlgItemTextW(hwnd, nDlgItem, Buffer3);
}
static
BOOL
DoUnlock(
IN HWND hwndDlg,
IN PGINA_CONTEXT pgContext,
OUT LPINT Action)
{
WCHAR Buffer1[256];
WCHAR Buffer2[256];
LPWSTR UserName = NULL;
LPWSTR Password = NULL;
BOOL res = FALSE;
if (GetTextboxText(hwndDlg, IDC_UNLOCK_USERNAME, &UserName) && *UserName == '\0')
{
HeapFree(GetProcessHeap(), 0, UserName);
return FALSE;
}
if (GetTextboxText(hwndDlg, IDC_UNLOCK_PASSWORD, &Password))
{
if (UserName != NULL && Password != NULL &&
wcscmp(UserName, pgContext->UserName) == 0 &&
wcscmp(Password, pgContext->Password) == 0)
{
*Action = WLX_SAS_ACTION_UNLOCK_WKSTA;
res = TRUE;
}
else if (wcscmp(UserName, pgContext->UserName) == 0 &&
wcscmp(Password, pgContext->Password) != 0)
{
/* Wrong Password */
LoadStringW(pgContext->hDllInstance, IDS_LOCKEDWRONGPASSWORD, Buffer2, _countof(Buffer2));
LoadStringW(pgContext->hDllInstance, IDS_COMPUTERLOCKED, Buffer1, _countof(Buffer1));
MessageBoxW(hwndDlg, Buffer2, Buffer1, MB_OK | MB_ICONERROR);
}
else
{
/* Wrong user name */
if (DoAdminUnlock(pgContext, UserName, NULL, Password))
{
*Action = WLX_SAS_ACTION_UNLOCK_WKSTA;
res = TRUE;
}
else
{
LoadStringW(pgContext->hDllInstance, IDS_LOCKEDWRONGUSER, Buffer1, _countof(Buffer1));
wsprintfW(Buffer2, Buffer1, pgContext->DomainName, pgContext->UserName);
LoadStringW(pgContext->hDllInstance, IDS_COMPUTERLOCKED, Buffer1, _countof(Buffer1));
MessageBoxW(hwndDlg, Buffer2, Buffer1, MB_OK | MB_ICONERROR);
}
}
}
if (UserName != NULL)
HeapFree(GetProcessHeap(), 0, UserName);
if (Password != NULL)
{
/* Zero out the password buffer before freeing it */
SIZE_T pwdLen = (wcslen(Password) + 1) * sizeof(WCHAR);
SecureZeroMemory(Password, pwdLen);
HeapFree(GetProcessHeap(), 0, Password);
}
return res;
}
static
INT_PTR
CALLBACK
UnlockDialogProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PDLG_DATA pDlgData;
INT result = WLX_SAS_ACTION_NONE;
pDlgData = (PDLG_DATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
pDlgData = DlgData_Create(hwndDlg, (PGINA_CONTEXT)lParam);
if (pDlgData == NULL)
return FALSE;
DlgData_LoadBitmaps(pDlgData);
SetWelcomeText(hwndDlg);
SetLockMessage(hwndDlg, IDC_UNLOCK_MESSAGE, pDlgData->pgContext);
SetDlgItemTextW(hwndDlg, IDC_UNLOCK_USERNAME, pDlgData->pgContext->UserName);
if (pDlgData->pgContext->bDisableCAD)
EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE);
SetFocus(GetDlgItem(hwndDlg, IDC_UNLOCK_PASSWORD));
return FALSE; // Default focus is changed.
}
case WM_PAINT:
{
PAINTSTRUCT ps;
if (pDlgData && pDlgData->hLogoBitmap)
{
BeginPaint(hwndDlg, &ps);
DrawStateW(ps.hdc, NULL, NULL, (LPARAM)pDlgData->hLogoBitmap, (WPARAM)0, 0, 0, 0, 0, DST_BITMAP);
EndPaint(hwndDlg, &ps);
}
return TRUE;
}
case WM_DESTROY:
DlgData_Destroy(hwndDlg);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
if (DoUnlock(hwndDlg, pDlgData->pgContext, &result))
{
EndDialog(hwndDlg, result);
}
else
{
SetDlgItemTextW(hwndDlg, IDC_UNLOCK_PASSWORD, NULL);
SendMessageW(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_UNLOCK_PASSWORD), TRUE);
}
return TRUE;
case IDCANCEL:
EndDialog(hwndDlg, WLX_SAS_ACTION_NONE);
return TRUE;
}
break;
}
return FALSE;
}
static INT
GUILockedSAS(
IN OUT PGINA_CONTEXT pgContext)
{
int result;
TRACE("GUILockedSAS()\n");
result = pgContext->pWlxFuncs->WlxDialogBoxParam(
pgContext->hWlx,
pgContext->hDllInstance,
MAKEINTRESOURCEW(IDD_UNLOCK),
GetDesktopWindow(),
UnlockDialogProc,
(LPARAM)pgContext);
if (result >= WLX_SAS_ACTION_LOGON &&
result <= WLX_SAS_ACTION_SWITCH_CONSOLE)
{
WARN("GUILockedSAS() returns 0x%x\n", result);
return result;
}
WARN("GUILockedSAS() failed (0x%x)\n", result);
return WLX_SAS_ACTION_NONE;
}
static INT_PTR CALLBACK
LockedDialogProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PDLG_DATA pDlgData;
pDlgData = (PDLG_DATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
pDlgData = DlgData_Create(hwndDlg, (PGINA_CONTEXT)lParam);
if (pDlgData == NULL)
return FALSE;
DlgData_LoadBitmaps(pDlgData);
SetWelcomeText(hwndDlg);
SetLockMessage(hwndDlg, IDC_LOCKED_MESSAGE, pDlgData->pgContext);
return TRUE;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
if (pDlgData && pDlgData->hLogoBitmap)
{
BeginPaint(hwndDlg, &ps);
DrawStateW(ps.hdc, NULL, NULL, (LPARAM)pDlgData->hLogoBitmap, (WPARAM)0, 0, 0, 0, 0, DST_BITMAP);
EndPaint(hwndDlg, &ps);
}
return TRUE;
}
case WM_DESTROY:
{
DlgData_Destroy(hwndDlg);
return TRUE;
}
}
return FALSE;
}
static VOID
GUIDisplayLockedNotice(
IN OUT PGINA_CONTEXT pgContext)
{
pgContext->pWlxFuncs->WlxDialogBoxParam(
pgContext->hWlx,
pgContext->hDllInstance,
MAKEINTRESOURCEW(IDD_LOCKED),
GetDesktopWindow(),
LockedDialogProc,
(LPARAM)pgContext);
}
GINA_UI GinaGraphicalUI = {
GUIInitialize,
GUIDisplayStatusMessage,
GUIRemoveStatusMessage,
GUIDisplaySASNotice,
GUILoggedOnSAS,
GUILoggedOutSAS,
GUILockedSAS,
GUIDisplayLockedNotice,
};