Send proper messages/events to processes during logoff and kill them if they
[reactos.git] / reactos / subsys / system / winlogon / sas.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: services/winlogon/sas.c
6 * PURPOSE: Secure Attention Sequence
7 * PROGRAMMER: Thomas Weidenmueller (w3seek@users.sourceforge.net)
8 * UPDATE HISTORY:
9 * Created 28/03/2004
10 */
11
12 #include "winlogon.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 #define WINLOGON_SAS_CLASS L"SAS window class"
18 #define WINLOGON_SAS_TITLE L"SAS"
19
20 #define HK_CTRL_ALT_DEL 0
21 #define HK_CTRL_SHIFT_ESC 1
22
23 #ifdef __USE_W32API
24 extern BOOL STDCALL SetLogonNotifyWindow(HWND Wnd, HWINSTA WinSta);
25 #endif
26
27 void
28 DispatchSAS(PWLSESSION Session, DWORD dwSasType)
29 {
30 Session->SASAction = dwSasType;
31
32 }
33
34 void
35 UninitSAS(PWLSESSION Session)
36 {
37 if(Session->SASWindow)
38 {
39 DestroyWindow(Session->SASWindow);
40 Session->SASWindow = NULL;
41 }
42 }
43
44 BOOL
45 SetupSAS(PWLSESSION Session, HWND hwndSAS)
46 {
47 /* Register Ctrl+Alt+Del Hotkey */
48 if(!RegisterHotKey(hwndSAS, HK_CTRL_ALT_DEL, MOD_CONTROL | MOD_ALT, VK_DELETE))
49 {
50 DPRINT1("WL-SAS: Unable to register Ctrl+Alt+Del hotkey!\n");
51 return FALSE;
52 }
53
54 /* Register Ctrl+Shift+Esc */
55 Session->TaskManHotkey = RegisterHotKey(hwndSAS, HK_CTRL_SHIFT_ESC, MOD_CONTROL | MOD_SHIFT, VK_ESCAPE);
56 if(!Session->TaskManHotkey)
57 {
58 DPRINT1("WL-SAS: Warning: Unable to register Ctrl+Alt+Esc hotkey!\n");
59 }
60 return TRUE;
61 }
62
63 BOOL
64 DestroySAS(PWLSESSION Session, HWND hwndSAS)
65 {
66 /* Unregister hotkeys */
67
68 UnregisterHotKey(hwndSAS, HK_CTRL_ALT_DEL);
69
70 if(Session->TaskManHotkey)
71 {
72 UnregisterHotKey(hwndSAS, HK_CTRL_SHIFT_ESC);
73 }
74
75 return TRUE;
76 }
77
78 #define EWX_ACTION_MASK 0xffffffeb
79 #define EWX_FLAGS_MASK 0x00000014
80
81 typedef struct tagLOGOFF_SHUTDOWN_DATA
82 {
83 UINT Flags;
84 PWLSESSION Session;
85 } LOGOFF_SHUTDOWN_DATA, *PLOGOFF_SHUTDOWN_DATA;
86
87 static DWORD WINAPI
88 LogoffShutdownThread(LPVOID Parameter)
89 {
90 PLOGOFF_SHUTDOWN_DATA LSData = (PLOGOFF_SHUTDOWN_DATA) Parameter;
91
92 if (! ImpersonateLoggedOnUser(LSData->Session->UserToken))
93 {
94 DPRINT1("ImpersonateLoggedOnUser failed with error %d\n", GetLastError());
95 return 0;
96 }
97 if (! ExitWindowsEx(EWX_INTERNAL_KILL_USER_APPS | (LSData->Flags & EWX_FLAGS_MASK)
98 | (EWX_LOGOFF == (LSData->Flags & EWX_ACTION_MASK) ? EWX_INTERNAL_FLAG_LOGOFF : 0),
99 0))
100 {
101 DPRINT1("Unable to kill user apps, error %d\n", GetLastError());
102 RevertToSelf();
103 return 0;
104 }
105 RevertToSelf();
106
107 HeapFree(GetProcessHeap(), 0, LSData);
108
109 return 1;
110 }
111
112 static LRESULT
113 HandleExitWindows(PWLSESSION Session, DWORD RequestingProcessId, UINT Flags)
114 {
115 UINT Action;
116 HANDLE Process;
117 HANDLE Token;
118 HANDLE Thread;
119 BOOL CheckResult;
120 PPRIVILEGE_SET PrivSet;
121 PLOGOFF_SHUTDOWN_DATA LSData;
122
123 /* Check parameters */
124 Action = Flags & EWX_ACTION_MASK;
125 if (EWX_LOGOFF != Action && EWX_SHUTDOWN != Action && EWX_REBOOT != Action
126 && EWX_POWEROFF != Action)
127 {
128 DPRINT1("Invalid ExitWindows action 0x%x\n", Action);
129 return STATUS_INVALID_PARAMETER;
130 }
131
132 /* Check privilege */
133 if (EWX_LOGOFF != Action)
134 {
135 Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, RequestingProcessId);
136 if (NULL == Process)
137 {
138 DPRINT1("OpenProcess failed with error %d\n", GetLastError());
139 return STATUS_INVALID_HANDLE;
140 }
141 if (! OpenProcessToken(Process, TOKEN_QUERY, &Token))
142 {
143 DPRINT1("OpenProcessToken failed with error %d\n", GetLastError());
144 CloseHandle(Process);
145 return STATUS_INVALID_HANDLE;
146 }
147 CloseHandle(Process);
148 PrivSet = HeapAlloc(GetProcessHeap(), 0, sizeof(PRIVILEGE_SET) + sizeof(LUID_AND_ATTRIBUTES));
149 if (NULL == PrivSet)
150 {
151 DPRINT1("Failed to allocate mem for privilege set\n");
152 CloseHandle(Token);
153 return STATUS_NO_MEMORY;
154 }
155 PrivSet->PrivilegeCount = 1;
156 PrivSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
157 if (! LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &PrivSet->Privilege[0].Luid))
158 {
159 DPRINT1("LookupPrivilegeValue failed with error %d\n", GetLastError());
160 HeapFree(GetProcessHeap(), 0, PrivSet);
161 CloseHandle(Token);
162 return STATUS_UNSUCCESSFUL;
163 }
164 if (! PrivilegeCheck(Token, PrivSet, &CheckResult))
165 {
166 DPRINT1("PrivilegeCheck failed with error %d\n", GetLastError());
167 HeapFree(GetProcessHeap(), 0, PrivSet);
168 CloseHandle(Token);
169 return STATUS_ACCESS_DENIED;
170 }
171 HeapFree(GetProcessHeap(), 0, PrivSet);
172 CloseHandle(Token);
173 if (! CheckResult)
174 {
175 DPRINT1("SE_SHUTDOWN privilege not enabled\n");
176 return STATUS_ACCESS_DENIED;
177 }
178 }
179
180 LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA));
181 if (NULL == LSData)
182 {
183 DPRINT1("Failed to allocate mem for thread data\n");
184 return STATUS_NO_MEMORY;
185 }
186 LSData->Flags = Flags;
187 LSData->Session = Session;
188 Thread = CreateThread(NULL, 0, LogoffShutdownThread, (LPVOID) LSData, 0, NULL);
189 if (NULL == Thread)
190 {
191 DPRINT1("Unable to create shutdown thread, error %d\n", GetLastError());
192 HeapFree(GetProcessHeap(), 0, LSData);
193 return STATUS_UNSUCCESSFUL;
194 }
195 CloseHandle(Thread);
196
197 return 1;
198 }
199
200 LRESULT CALLBACK
201 SASProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
202 {
203 PWLSESSION Session = (PWLSESSION)GetWindowLongPtr(hwnd, GWL_USERDATA);
204 if(!Session)
205 {
206 return DefWindowProc(hwnd, uMsg, wParam, lParam);
207 }
208 switch(uMsg)
209 {
210 case WM_HOTKEY:
211 {
212 switch(wParam)
213 {
214 case HK_CTRL_ALT_DEL:
215 DPRINT1("SAS: CTR+ALT+DEL\n");
216 break;
217 case HK_CTRL_SHIFT_ESC:
218 DPRINT1("SAS: CTR+SHIFT+ESC\n");
219 break;
220 }
221 return 0;
222 }
223 case WM_CREATE:
224 {
225 if(!SetupSAS(Session, hwnd))
226 {
227 /* Fail! */
228 return 1;
229 }
230 return 0;
231 }
232 case PM_WINLOGON_EXITWINDOWS:
233 {
234 return HandleExitWindows(Session, (DWORD) wParam, (UINT) lParam);
235 }
236 case WM_DESTROY:
237 {
238 DestroySAS(Session, hwnd);
239 return 0;
240 }
241 }
242 return DefWindowProc(hwnd, uMsg, wParam, lParam);
243 }
244
245 BOOL
246 InitializeSAS(PWLSESSION Session)
247 {
248 WNDCLASSEX swc;
249
250 /* register SAS window class.
251 WARNING! MAKE SURE WE ARE IN THE WINLOGON DESKTOP! */
252 swc.cbSize = sizeof(WNDCLASSEXW);
253 swc.style = CS_SAVEBITS;
254 swc.lpfnWndProc = SASProc;
255 swc.cbClsExtra = 0;
256 swc.cbWndExtra = 0;
257 swc.hInstance = hAppInstance;
258 swc.hIcon = NULL;
259 swc.hCursor = NULL;
260 swc.hbrBackground = NULL;
261 swc.lpszMenuName = NULL;
262 swc.lpszClassName = WINLOGON_SAS_CLASS;
263 swc.hIconSm = NULL;
264 RegisterClassEx(&swc);
265
266 /* create invisible SAS window */
267 Session->SASWindow = CreateWindowEx(0, WINLOGON_SAS_CLASS, WINLOGON_SAS_TITLE, WS_POPUP,
268 0, 0, 0, 0, 0, 0, hAppInstance, NULL);
269 if(!Session->SASWindow)
270 {
271 DPRINT1("WL: Failed to create SAS window\n");
272 return FALSE;
273 }
274
275 /* Save the Session pointer so the window proc can access it */
276 SetWindowLongPtr(Session->SASWindow, GWL_USERDATA, (DWORD_PTR)Session);
277
278 /* Register SAS window to receive SAS notifications */
279 if(!SetLogonNotifyWindow(Session->SASWindow, Session->InteractiveWindowStation))
280 {
281 UninitSAS(Session);
282 DPRINT1("WL: Failed to register SAS window\n");
283 return FALSE;
284 }
285
286 return TRUE;
287 }
288