Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1300 lines
38 KiB

  1. /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2. Microsoft Windows, Copyright (C) Microsoft Corporation, 2000
  3. File: Wlballoon.cpp
  4. Content: Implementation of the notification balloon class.
  5. History: 03-22-2001 dsie created
  6. ------------------------------------------------------------------------------*/
  7. #pragma warning (disable: 4100)
  8. #pragma warning (disable: 4706)
  9. ////////////////////
  10. //
  11. // Include
  12. //
  13. #include <nt.h>
  14. #include <ntrtl.h>
  15. #include <nturtl.h>
  16. #include <windows.h>
  17. #include <winwlx.h>
  18. #include <shobjidl.h>
  19. #include <shellapi.h>
  20. #include <stdio.h>
  21. #include "debug.h"
  22. #include "wlballoon.rh"
  23. ////////////////////
  24. //
  25. // Defines
  26. //
  27. #define MAX_RESOURCE_STRING_SIZE 512
  28. #define IQUERY_CANCEL_INTERVAL (10 * 1000)
  29. #define BALLOON_SHOW_TIME (15 * 1000)
  30. #define BALLOON_SHOW_INTERVAL (2 * 60 * 1000)
  31. #define BALLOON_RESHOW_COUNT (0)
  32. #ifdef DBG
  33. #define BALLOON_NOTIFICATION_INTERVAL 60*1000 // Who wants to wait 10 minutes...
  34. #define BALLOON_INACTIVITY_TIMEOUT 60*1000 // Who wants to wait 15 minutes...
  35. #else
  36. #define BALLOON_NOTIFICATION_INTERVAL 10*60*1000
  37. #define BALLOON_INACTIVITY_TIMEOUT 15*60*1000
  38. #endif
  39. #define LOGOFF_NOTIFICATION_EVENT_NAME L"Local\\WlballoonLogoffNotification"
  40. #define KERBEROS_NOTIFICATION_EVENT_NAME L"WlballoonKerberosNotificationEventName"
  41. //#define KERBEROS_NOTIFICATION_EVENT_NAME L"KerbNotification"
  42. #define KERBEROS_NOTIFICATION_EVENT_NAME_SC L"KerbNotificationSC"
  43. ////////////////////
  44. //
  45. // Classes
  46. //
  47. class CBalloon : IQueryContinue
  48. {
  49. public:
  50. CBalloon(HANDLE hEvent);
  51. ~CBalloon();
  52. // IUnknown
  53. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  54. STDMETHODIMP_(ULONG) AddRef();
  55. STDMETHODIMP_(ULONG) Release();
  56. // IQueryContinue
  57. STDMETHODIMP QueryContinue(); // S_OK -> Continue, otherwise S_FALSE
  58. STDMETHODIMP ShowBalloon(HWND hWnd, HINSTANCE hInstance);
  59. private:
  60. LONG m_cRef;
  61. HANDLE m_hEvent;
  62. };
  63. class CNotify
  64. {
  65. public:
  66. CNotify();
  67. ~CNotify();
  68. DWORD RegisterNotification(LUID luidCurrentUser, HANDLE hUserToken);
  69. DWORD UnregisterNotification();
  70. private:
  71. HANDLE m_hWait;
  72. HANDLE m_hLogoffEvent;
  73. HANDLE m_hKerberosEvent;
  74. // HANDLE m_hKerberosEventSC;
  75. HANDLE m_hThread;
  76. LUID m_luidCU;
  77. HANDLE m_hUserToken;
  78. static VOID CALLBACK RegisterWaitNotificationCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired);
  79. static DWORD WINAPI NotificationThreadProc(PVOID lpParameter);
  80. };
  81. ////////////////////
  82. //
  83. // Typedefs
  84. //
  85. typedef struct
  86. {
  87. HANDLE hWait;
  88. HANDLE hEvent;
  89. HMODULE hModule;
  90. CNotify * pNotify;
  91. } LOGOFFDATA, * PLOGOFFDATA;
  92. LPWSTR CreateNotificationEventName(LPCWSTR pwszSuffixName, LUID luidCurrentUser);
  93. //+----------------------------------------------------------------------------
  94. //
  95. // CBalloon
  96. //
  97. //-----------------------------------------------------------------------------
  98. CBalloon::CBalloon(HANDLE hEvent)
  99. {
  100. m_cRef = 1;
  101. m_hEvent = hEvent;
  102. }
  103. //+----------------------------------------------------------------------------
  104. //
  105. // ~CBalloon
  106. //
  107. //-----------------------------------------------------------------------------
  108. CBalloon::~CBalloon()
  109. {
  110. ASSERT(m_hEvent);
  111. PrivateDebugTrace("Info: Destroying the balloon event.\n");
  112. CloseHandle(m_hEvent);
  113. return;
  114. }
  115. //+----------------------------------------------------------------------------
  116. //
  117. // QueryInterface
  118. //
  119. //-----------------------------------------------------------------------------
  120. HRESULT CBalloon::QueryInterface(REFIID riid, void **ppv)
  121. {
  122. HRESULT hr = S_OK;
  123. if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IQueryContinue))
  124. {
  125. *ppv = static_cast<IQueryContinue *>(this);
  126. AddRef();
  127. }
  128. else
  129. {
  130. *ppv = NULL;
  131. hr = E_NOINTERFACE;
  132. }
  133. return hr;
  134. }
  135. //+----------------------------------------------------------------------------
  136. //
  137. // AddRef
  138. //
  139. //-----------------------------------------------------------------------------
  140. STDMETHODIMP_(ULONG) CBalloon::AddRef()
  141. {
  142. return InterlockedIncrement(&m_cRef);
  143. }
  144. //+----------------------------------------------------------------------------
  145. //
  146. // Release
  147. //
  148. //-----------------------------------------------------------------------------
  149. STDMETHODIMP_(ULONG) CBalloon::Release()
  150. {
  151. ASSERT(0 != m_cRef);
  152. ULONG cRef = InterlockedDecrement(&m_cRef);
  153. if (0 == cRef)
  154. {
  155. delete this;
  156. }
  157. return cRef;
  158. }
  159. //+----------------------------------------------------------------------------
  160. //
  161. // QueryContinue
  162. //
  163. //-----------------------------------------------------------------------------
  164. STDMETHODIMP CBalloon::QueryContinue()
  165. {
  166. ASSERT(m_hEvent);
  167. switch (WaitForSingleObject(m_hEvent, 0))
  168. {
  169. case WAIT_OBJECT_0:
  170. DebugTrace("Info: Logoff event is signaled, dismissing notification balloon.\n");
  171. return S_FALSE;
  172. case WAIT_TIMEOUT:
  173. PrivateDebugTrace("Info: Logoff event not set, continue to show notification balloon.\n");
  174. return S_OK;
  175. case WAIT_FAILED:
  176. default:
  177. DebugTrace("Error [%#x]: WaitForSingleObject() failed, dismissing notification balloon.\n", GetLastError());
  178. break;
  179. }
  180. return E_FAIL;
  181. }
  182. //+----------------------------------------------------------------------------
  183. //
  184. // ShowBalloon
  185. //
  186. //-----------------------------------------------------------------------------
  187. STDMETHODIMP CBalloon::ShowBalloon(HWND hWnd, HINSTANCE hInstance)
  188. {
  189. HRESULT hr = S_OK;
  190. BOOL bCoInitialized = FALSE;
  191. HICON hIcon = NULL;
  192. WCHAR wszTitle[MAX_RESOURCE_STRING_SIZE] = L"";
  193. WCHAR wszText[MAX_RESOURCE_STRING_SIZE] = L"";
  194. IUserNotification * pIUserNotification = NULL;
  195. PrivateDebugTrace("Entering CBalloon::ShowBalloon.\n");
  196. ASSERT(m_hEvent);
  197. ASSERT(hInstance);
  198. if (FAILED(hr = CoInitialize(NULL)))
  199. {
  200. DebugTrace("Error [%#x]: CoInitialize() failed.\n", hr);
  201. goto ErrorReturn;
  202. }
  203. bCoInitialized = TRUE;
  204. if (FAILED(hr = CoCreateInstance(CLSID_UserNotification,
  205. NULL,
  206. CLSCTX_ALL,
  207. IID_IUserNotification,
  208. (void **) &pIUserNotification)))
  209. {
  210. DebugTrace("Error [%#x]: CoCreateInstance() failed.\n", hr);
  211. goto ErrorReturn;
  212. }
  213. if (FAILED(hr = pIUserNotification->SetBalloonRetry(BALLOON_SHOW_TIME,
  214. BALLOON_SHOW_INTERVAL,
  215. BALLOON_RESHOW_COUNT)))
  216. {
  217. DebugTrace("Error [%#x]: pIUserNotification->SetBalloonRetry() failed.\n", hr);
  218. goto ErrorReturn;
  219. }
  220. if (NULL == (hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_KERBEROS_TICKET))))
  221. {
  222. hr = HRESULT_FROM_WIN32(GetLastError());
  223. DebugTrace("Error [%#x]: LoadIcon() failed for IDI_KERBEROS_TICKET.\n", hr);
  224. goto ErrorReturn;
  225. }
  226. if (!LoadStringW(hInstance, IDS_BALLOON_TIP, wszText, MAX_RESOURCE_STRING_SIZE))
  227. {
  228. hr = HRESULT_FROM_WIN32(GetLastError());
  229. DebugTrace("Error [%#x]: LoadStringW() failed for IDS_BALLOON_TIP.\n", hr);
  230. goto ErrorReturn;
  231. }
  232. if (FAILED(hr = pIUserNotification->SetIconInfo(hIcon, wszText)))
  233. {
  234. DebugTrace("Error [%#x]: pIUserNotification->SetIconInfo() failed.\n", hr);
  235. goto ErrorReturn;
  236. }
  237. if (!LoadStringW(hInstance, IDS_BALLOON_TITLE, wszTitle, MAX_RESOURCE_STRING_SIZE))
  238. {
  239. hr = HRESULT_FROM_WIN32(GetLastError());
  240. DebugTrace("Error [%#x]: LoadStringW() failed for IDS_BALLOON_TITLE.\n", hr);
  241. goto ErrorReturn;
  242. }
  243. if (!LoadStringW(hInstance, IDS_BALLOON_TEXT, wszText, MAX_RESOURCE_STRING_SIZE))
  244. {
  245. hr = HRESULT_FROM_WIN32(GetLastError());
  246. DebugTrace("Error [%#x]: LoadStringW() failed for IDS_BALLOON_TEXT.\n", hr);
  247. goto ErrorReturn;
  248. }
  249. if (FAILED(hr = pIUserNotification->SetBalloonInfo(wszTitle, wszText, NIIF_ERROR)))
  250. {
  251. DebugTrace("Error [%#x]: pIUserNotification->SetBalloonInfo() failed.\n", hr);
  252. goto ErrorReturn;
  253. }
  254. if (FAILED(hr = pIUserNotification->Show(static_cast<IQueryContinue *>(this), IQUERY_CANCEL_INTERVAL)))
  255. {
  256. DebugTrace("Error [%#x]: pIUserNotification->Show() failed.\n", hr);
  257. goto ErrorReturn;
  258. }
  259. CommonReturn:
  260. if (hIcon)
  261. {
  262. DestroyIcon(hIcon);
  263. }
  264. if (pIUserNotification)
  265. {
  266. pIUserNotification->Release();
  267. }
  268. if (bCoInitialized)
  269. {
  270. CoUninitialize();
  271. }
  272. PrivateDebugTrace("Leaving CBalloon::ShowBalloon().\n");
  273. return hr;
  274. ErrorReturn:
  275. ASSERT(hr != S_OK);
  276. goto CommonReturn;
  277. }
  278. //+----------------------------------------------------------------------------
  279. //
  280. // Function : ShowNotificationBalloonW
  281. //
  282. // Synopsis : Display the notification balloon periodically until the specified
  283. // event is set.
  284. //
  285. // Parameter: HWND hWnd
  286. // HINSTANCE hInstance
  287. // LPWSTR lpwszCommandLine - Event name
  288. // int nCmdShow
  289. //
  290. // Return : None.
  291. //
  292. // Remarks : This function is intended to be called through RunDll32 from
  293. // Winlogon. The reason we put these in wlnotify.dll is to save
  294. // distributing another EXE.
  295. //
  296. // Sample calling command line:
  297. //
  298. // RunDll32 wlnotify.dll,ShowNotificationBalloon EventName
  299. //
  300. //-----------------------------------------------------------------------------
  301. void CALLBACK ShowNotificationBalloonW(HWND hWnd,
  302. HINSTANCE hInstance,
  303. LPWSTR lpwszCommandLine,
  304. int nCmdShow)
  305. {
  306. HRESULT hr = S_OK;
  307. HANDLE hLogoffEvent = NULL;
  308. HMODULE hModule = NULL;
  309. CBalloon * pBalloon = NULL;
  310. PrivateDebugTrace("Entering ShowNotificationBalloonW().\n");
  311. if (NULL == (hModule = LoadLibraryW(L"wlnotify.dll")))
  312. {
  313. DebugTrace("Error [%#x]: LoadLibraryW() failed for wlnotify.dll.\n", GetLastError());
  314. goto ErrorReturn;
  315. }
  316. if (NULL == lpwszCommandLine)
  317. {
  318. DebugTrace("Error [%#x]: invalid argument, lpwszCommandLine is NULL.\n", E_INVALIDARG);
  319. goto ErrorReturn;
  320. }
  321. if (NULL == (hLogoffEvent = OpenEventW(SYNCHRONIZE, FALSE, LOGOFF_NOTIFICATION_EVENT_NAME)))
  322. {
  323. DebugTrace("Error [%#x]: OpenEventW() failed for event %S.\n", GetLastError(), LOGOFF_NOTIFICATION_EVENT_NAME);
  324. goto ErrorReturn;
  325. }
  326. if (NULL == (pBalloon = new CBalloon(hLogoffEvent)))
  327. {
  328. DebugTrace("Error [%#x]: new CBalloon() failed.\n", ERROR_NOT_ENOUGH_MEMORY);
  329. goto ErrorReturn;
  330. }
  331. hLogoffEvent = NULL; // this will prevent a double close in the cleanup below.
  332. if (S_OK == (hr = pBalloon->ShowBalloon(NULL, hModule)))
  333. {
  334. WCHAR wszTitle[MAX_RESOURCE_STRING_SIZE] = L"";
  335. WCHAR wszText[MAX_RESOURCE_STRING_SIZE] = L"";
  336. DebugTrace("Info: User clicked on icon.\n");
  337. if (!LoadStringW(hModule, IDS_BALLOON_DIALOG_TITLE, wszTitle, sizeof(wszTitle) / sizeof(wszTitle[0])))
  338. {
  339. hr = HRESULT_FROM_WIN32(GetLastError());
  340. DebugTrace("Error [%#x]: LoadStringW() failed for IDS_BALLOON_DIALOG_TITLE.\n", hr);
  341. goto CommonReturn; // hLogoffEvent will be closed in CBalloon destructor
  342. }
  343. if (!LoadStringW(hModule,
  344. GetSystemMetrics(SM_REMOTESESSION) ? IDS_BALLOON_DIALOG_TS_TEXT : IDS_BALLOON_DIALOG_TEXT,
  345. wszText, sizeof(wszText) / sizeof(wszText[0])))
  346. {
  347. hr = HRESULT_FROM_WIN32(GetLastError());
  348. DebugTrace("Error [%#x]: LoadStringW() failed for IDS_BALLOON_DIALOG_TEXT.\n", hr);
  349. goto CommonReturn; // hLogoffEvent will be closed in CBalloon destructor
  350. }
  351. MessageBoxW(hWnd, wszText, wszTitle, MB_OK | MB_ICONERROR);
  352. }
  353. else if (S_FALSE == hr)
  354. {
  355. DebugTrace("Info: IQueryContinue cancelled the notification.\n");
  356. }
  357. else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
  358. {
  359. DebugTrace("Info: Balloon icon timed out.\n");
  360. }
  361. else
  362. {
  363. DebugTrace("Error [%#x]: pBalloon->ShowBalloon() failed.\n", hr);
  364. }
  365. CommonReturn:
  366. if (hLogoffEvent)
  367. {
  368. CloseHandle(hLogoffEvent);
  369. }
  370. if (pBalloon)
  371. {
  372. pBalloon->Release();
  373. }
  374. if (hModule)
  375. {
  376. FreeLibrary(hModule);
  377. }
  378. PrivateDebugTrace("Leaving ShowNotificationBalloonW().\n");
  379. return;
  380. ErrorReturn:
  381. goto CommonReturn;
  382. }
  383. //+----------------------------------------------------------------------------
  384. //
  385. // CNotify
  386. //
  387. //-----------------------------------------------------------------------------
  388. CNotify::CNotify()
  389. {
  390. m_hWait = NULL;
  391. m_hLogoffEvent = NULL;
  392. m_hKerberosEvent = NULL;
  393. // m_hKerberosEventSC = NULL;
  394. m_hThread = NULL;
  395. }
  396. //+----------------------------------------------------------------------------
  397. //
  398. // ~CNotify
  399. //
  400. //-----------------------------------------------------------------------------
  401. CNotify::~CNotify()
  402. {
  403. PrivateDebugTrace("Info: Destroying the CNotify object.\n");
  404. if (m_hWait)
  405. {
  406. UnregisterWait(m_hWait);
  407. }
  408. if (m_hLogoffEvent)
  409. {
  410. CloseHandle(m_hLogoffEvent);
  411. }
  412. if (m_hKerberosEvent)
  413. {
  414. CloseHandle(m_hKerberosEvent);
  415. }
  416. // if (m_hKerberosEventSC)
  417. // {
  418. // CloseHandle(m_hKerberosEventSC);
  419. // }
  420. if (m_hThread)
  421. {
  422. CloseHandle(m_hThread);
  423. }
  424. return;
  425. }
  426. //+----------------------------------------------------------------------------
  427. //
  428. // RegisterNotification
  429. //
  430. // To register and wait for the Kerberos notification event. When the event is
  431. // signaled, a ticket icon and balloon will appear in the systray to warn the
  432. // user about the problem, and suggest them to lock and then unlock the machine
  433. // with their new password.
  434. //
  435. //-----------------------------------------------------------------------------
  436. DWORD CNotify::RegisterNotification(LUID luidCurrentUser, HANDLE hUserToken)
  437. {
  438. DWORD dwRetCode = 0;
  439. LPWSTR lpwstrEventName = NULL;
  440. PrivateDebugTrace("Entering CNotify::RegisterNotification().\n");
  441. if (NULL == (m_hLogoffEvent = OpenEventW(SYNCHRONIZE, FALSE, LOGOFF_NOTIFICATION_EVENT_NAME)))
  442. {
  443. dwRetCode = GetLastError();
  444. DebugTrace("Error [%#x]: OpenEventW() failed for event %S.\n", dwRetCode, LOGOFF_NOTIFICATION_EVENT_NAME);
  445. goto ErrorReturn;
  446. }
  447. if (NULL == (lpwstrEventName = CreateNotificationEventName(KERBEROS_NOTIFICATION_EVENT_NAME, luidCurrentUser)))
  448. {
  449. dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
  450. DebugTrace("Error [%#x]: CreateNotificationEventName() failed\n", dwRetCode);
  451. goto ErrorReturn;
  452. }
  453. if (NULL == (m_hKerberosEvent = CreateEventW(NULL, FALSE, FALSE, lpwstrEventName)))
  454. {
  455. dwRetCode = GetLastError();
  456. DebugTrace("Error [%#x]: CreateEventW() failed for event %S.\n", dwRetCode, lpwstrEventName);
  457. goto ErrorReturn;
  458. }
  459. if (ERROR_ALREADY_EXISTS == GetLastError())
  460. {
  461. dwRetCode = ERROR_SINGLE_INSTANCE_APP;
  462. DebugTrace("Error [%#x]: cannot run more than one instance of this code per unique event.\n", dwRetCode);
  463. goto ErrorReturn;
  464. }
  465. free(lpwstrEventName);
  466. lpwstrEventName = NULL;
  467. // Failure for this one are not fatal (we will only have a delay)
  468. /* if (NULL != (lpwstrEventName = CreateNotificationEventName(KERBEROS_NOTIFICATION_EVENT_NAME_SC, luidCurrentUser)))
  469. {
  470. if (NULL == (m_hKerberosEventSC = CreateEventW(NULL, FALSE, FALSE, lpwstrEventName)))
  471. {
  472. dwRetCode = GetLastError();
  473. DebugTrace("Error [%#x]: CreateEventW() failed for event %S.\n", dwRetCode, lpwstrEventName);
  474. }
  475. }
  476. else
  477. {
  478. dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
  479. DebugTrace("Error [%#x]: CreateNotificationEventName() failed\n", dwRetCode);
  480. }
  481. */
  482. m_luidCU = luidCurrentUser;
  483. m_hUserToken = hUserToken;
  484. if (!RegisterWaitForSingleObject(&m_hWait,
  485. m_hKerberosEvent,
  486. CNotify::RegisterWaitNotificationCallback,
  487. (PVOID) this,
  488. INFINITE,
  489. WT_EXECUTEONLYONCE))
  490. {
  491. dwRetCode = (DWORD) E_UNEXPECTED;
  492. DebugTrace("Unexpected error: RegisterWaitForSingleObject() failed.\n");
  493. goto ErrorReturn;
  494. }
  495. CommonReturn:
  496. if (NULL != lpwstrEventName)
  497. {
  498. free(lpwstrEventName);
  499. }
  500. PrivateDebugTrace("Leaving CNotify::RegisterNotification().\n");
  501. return dwRetCode;
  502. ErrorReturn:
  503. ASSERT(0 != dwRetCode);
  504. if (m_hWait)
  505. {
  506. UnregisterWait(m_hWait);
  507. m_hWait = NULL;
  508. }
  509. if (m_hLogoffEvent)
  510. {
  511. CloseHandle(m_hLogoffEvent);
  512. m_hLogoffEvent = NULL;
  513. }
  514. if (m_hKerberosEvent)
  515. {
  516. CloseHandle(m_hKerberosEvent);
  517. m_hKerberosEvent = NULL;
  518. }
  519. /* if (m_hKerberosEventSC)
  520. {
  521. CloseHandle(m_hKerberosEventSC);
  522. m_hKerberosEventSC = NULL;
  523. }
  524. */
  525. goto CommonReturn;
  526. }
  527. //+----------------------------------------------------------------------------
  528. //
  529. // UnregisterNotification
  530. //
  531. // Unregister the Kerberos notification wait event registered by
  532. // RegisterNotification().
  533. //
  534. //-----------------------------------------------------------------------------
  535. DWORD CNotify::UnregisterNotification()
  536. {
  537. DWORD dwRetCode = 0;
  538. PrivateDebugTrace("Entering CNotify::UnregisterNotification().\n");
  539. // No thread won't start anymore
  540. if (m_hWait)
  541. {
  542. UnregisterWait(m_hWait);
  543. m_hWait = NULL;
  544. }
  545. // That should be safe
  546. if (m_hThread)
  547. {
  548. if (WaitForSingleObject(m_hThread, INFINITE) == WAIT_FAILED)
  549. {
  550. DebugTrace("Error [%#x]: WaitForSingleObject() on the thread failed.\n", GetLastError());
  551. }
  552. }
  553. PrivateDebugTrace("Leaving CNotify::UnregisterNotification().\n");
  554. return dwRetCode;
  555. }
  556. //+----------------------------------------------------------------------------
  557. //
  558. // RegisterWaitNotificationCallback
  559. //
  560. //-----------------------------------------------------------------------------
  561. VOID CALLBACK CNotify::RegisterWaitNotificationCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
  562. {
  563. DWORD dwThreadId = 0;
  564. CNotify * pNotify = NULL;
  565. PrivateDebugTrace("Entering CNotify::RegisterWaitNotificationCallback().\n");
  566. (void) TimerOrWaitFired;
  567. ASSERT(lpParameter);
  568. pNotify = (CNotify *) lpParameter;
  569. // We can clean that up at this point (it's OK for EXECUTEONLYONCE callbacks)
  570. UnregisterWait(pNotify->m_hWait);
  571. pNotify->m_hWait = NULL;
  572. if (pNotify->m_hThread)
  573. {
  574. CloseHandle(pNotify->m_hThread);
  575. }
  576. // Do the work in another thread
  577. if (NULL == (pNotify->m_hThread = CreateThread(NULL,
  578. 0,
  579. (LPTHREAD_START_ROUTINE) CNotify::NotificationThreadProc,
  580. lpParameter,
  581. 0,
  582. &dwThreadId)))
  583. {
  584. DebugTrace("Error [%#x]: CreateThread() for NotificationThreadProc failed.\n", GetLastError());
  585. }
  586. PrivateDebugTrace("Leaving CNotify::RegisterWaitNotificationCallback().\n");
  587. return;
  588. }
  589. //+----------------------------------------------------------------------------
  590. //
  591. // NotificationThreadProc
  592. //
  593. //-----------------------------------------------------------------------------
  594. DWORD WINAPI CNotify::NotificationThreadProc(PVOID lpParameter)
  595. {
  596. DWORD dwWait;
  597. HANDLE rhHandles[2];
  598. STARTUPINFOW si;
  599. PROCESS_INFORMATION pi;
  600. LPWSTR wszKerberosEventName;
  601. DWORD dwRetCode = 0;
  602. WCHAR wszCommandLine[MAX_PATH * 2];
  603. CNotify * pNotify = NULL;
  604. int iChars;
  605. PrivateDebugTrace("Entering CNotify::NotificationThreadProc().\n");
  606. ASSERT(lpParameter);
  607. pNotify = (CNotify *) lpParameter;
  608. ASSERT(pNotify->m_hLogoffEvent);
  609. ASSERT(pNotify->m_hKerberosEvent);
  610. ASSERT(pNotify->m_hUserToken);
  611. /* if ((pNotify->m_hKerberosEventSC) &&
  612. (WAIT_OBJECT_0 == WaitForSingleObject(pNotify->m_hKerberosEventSC, 0)))
  613. {
  614. // In this case we want the balloon NOW!
  615. DebugTrace("Info: SC Event is set too. We want the balloon NOW!\n");
  616. dwTimeout = 0;
  617. // We need to set the kerberos event below
  618. SetEvent(pNotify->m_hKerberosEvent);
  619. }
  620. */
  621. rhHandles[0] = pNotify->m_hLogoffEvent;
  622. rhHandles[1] = pNotify->m_hKerberosEvent;
  623. if (NULL == (wszKerberosEventName = CreateNotificationEventName(KERBEROS_NOTIFICATION_EVENT_NAME, pNotify->m_luidCU)))
  624. {
  625. dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
  626. DebugTrace("Error [%#x]: out of memory in NotificationThreadProc.\n", dwRetCode);
  627. goto ErrorReturn;
  628. }
  629. iChars = _snwprintf(wszCommandLine, sizeof(wszCommandLine) / sizeof(wszCommandLine[0]),
  630. L"RunDll32.exe wlnotify.dll,ShowNotificationBalloon %s", wszKerberosEventName);
  631. free(wszKerberosEventName);
  632. if ((iChars < 0) || (iChars == sizeof(wszCommandLine) / sizeof(wszCommandLine[0])))
  633. {
  634. DebugTrace("Error: _snwprintf failed in NotificationThreadProc.\n");
  635. dwRetCode = SEC_E_BUFFER_TOO_SMALL;
  636. goto ErrorReturn;
  637. }
  638. for (;;)
  639. {
  640. DebugTrace("Info: Kick off process to show balloon.\n");
  641. ZeroMemory(&pi, sizeof(pi));
  642. ZeroMemory(&si, sizeof(si));
  643. si.cb = sizeof(si);
  644. si.lpDesktop = L"WinSta0\\Default";
  645. if (!CreateProcessAsUserW(
  646. pNotify->m_hUserToken,
  647. NULL,
  648. wszCommandLine,
  649. NULL,
  650. NULL,
  651. FALSE,
  652. 0,
  653. NULL,
  654. NULL,
  655. &si,
  656. &pi))
  657. {
  658. dwRetCode = GetLastError();
  659. DebugTrace( "Error [%#x]: CreateProcessW() failed.\n", dwRetCode);
  660. goto ErrorReturn;
  661. }
  662. WaitForSingleObject(pi.hProcess, INFINITE);
  663. CloseHandle(pi.hThread);
  664. CloseHandle(pi.hProcess);
  665. DebugTrace("Info: Process showing balloon is done.\n");
  666. // The user dismissed the balloon
  667. // We shouldn't show another one for some time.
  668. switch (dwWait = WaitForSingleObject(rhHandles[0], BALLOON_NOTIFICATION_INTERVAL))
  669. {
  670. case WAIT_FAILED:
  671. dwRetCode = GetLastError();
  672. DebugTrace("Error [%#x]: WaitForSingleObject() on the logoff event failed.\n", dwRetCode);
  673. // fall through
  674. case WAIT_OBJECT_0:
  675. // Logoff detected!, get out of here
  676. DebugTrace("Info: Logoff detected!, get out of here.\n");
  677. goto CommonReturn;
  678. }
  679. // BALLOON_NOTIFICATION_INTERVAL elapsed
  680. // We ignore all kerb notifications received in the interval (ie we reset the event)
  681. if (WAIT_FAILED == WaitForSingleObject(rhHandles[1], 0))
  682. {
  683. dwRetCode = GetLastError();
  684. DebugTrace("Error [%#x]: WaitForSingleObject() on the kerb event failed.\n", dwRetCode);
  685. goto ErrorReturn;
  686. }
  687. // Now we wait for logoff or a new notification
  688. switch (dwWait = WaitForMultipleObjects(2, rhHandles, FALSE, BALLOON_INACTIVITY_TIMEOUT))
  689. {
  690. case WAIT_FAILED:
  691. dwRetCode = GetLastError();
  692. DebugTrace("Error [%#x]: WaitForMultipleleObjects() failed.\n", dwRetCode);
  693. // fall through
  694. case WAIT_OBJECT_0:
  695. // Logoff detected!, get out of here
  696. DebugTrace("Info: Logoff detected!, get out of here.\n");
  697. goto CommonReturn;
  698. case WAIT_OBJECT_0+1:
  699. DebugTrace("Info: New notification! Let's show the balloon again.\n");
  700. break;
  701. case WAIT_TIMEOUT:
  702. DebugTrace("Info: We've not been useful for a while, let's go away.\n");
  703. // Let's give us a chance to restart though...
  704. if (!RegisterWaitForSingleObject(&pNotify->m_hWait,
  705. pNotify->m_hKerberosEvent,
  706. CNotify::RegisterWaitNotificationCallback,
  707. lpParameter,
  708. INFINITE,
  709. WT_EXECUTEONLYONCE))
  710. {
  711. dwRetCode = GetLastError();
  712. DebugTrace("Error [%#x]: RegisterWaitForSingleObject() failed.\n", dwRetCode);
  713. goto ErrorReturn;
  714. }
  715. // Reset this guy
  716. // if (pNotify->m_hKerberosEventSC)
  717. // WaitForSingleObject(pNotify->m_hKerberosEventSC, 0);
  718. DebugTrace("Info: Registered wait for callback again.\n");
  719. goto CommonReturn;
  720. }
  721. }
  722. CommonReturn:
  723. PrivateDebugTrace("Leaving CNotify::NotificationThreadProc().\n");
  724. return dwRetCode;
  725. ErrorReturn:
  726. ASSERT(0 != dwRetCode);
  727. goto CommonReturn;
  728. }
  729. //+----------------------------------------------------------------------------
  730. //
  731. // GetCurrentUsersLuid
  732. //
  733. //-----------------------------------------------------------------------------
  734. DWORD GetCurrentUsersLuid(LUID *pluid)
  735. {
  736. DWORD dwRetCode = 0;
  737. DWORD cb = 0;
  738. TOKEN_STATISTICS stats;
  739. HANDLE hThreadToken = NULL;
  740. PrivateDebugTrace("Entering GetCurrentUsersLuid().\n");
  741. if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hThreadToken))
  742. {
  743. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hThreadToken))
  744. {
  745. dwRetCode = GetLastError();
  746. DebugTrace("Error [%#x]: OpenProcessToken() failed.\n", dwRetCode);
  747. goto CLEANUP;
  748. }
  749. }
  750. if (!GetTokenInformation(hThreadToken, TokenStatistics, &stats, sizeof(stats), &cb))
  751. {
  752. dwRetCode = GetLastError();
  753. DebugTrace("Error [%#x]: GetTokenInformation() failed.\n", dwRetCode);
  754. goto CLEANUP;
  755. }
  756. *pluid = stats.AuthenticationId;
  757. CLEANUP:
  758. if (NULL != hThreadToken)
  759. {
  760. CloseHandle(hThreadToken);
  761. }
  762. PrivateDebugTrace("Leaving GetCurrentUsersLuid().\n");
  763. return dwRetCode;
  764. }
  765. //+----------------------------------------------------------------------------
  766. //
  767. // CreateNotificationEventName
  768. //
  769. //-----------------------------------------------------------------------------
  770. LPWSTR CreateNotificationEventName(LPCWSTR pwszSuffixName, LUID luidCurrentUser)
  771. {
  772. LPWSTR pwszEventName = NULL;
  773. WCHAR wszPrefixName[] = L"Global\\";
  774. WCHAR wszLuid[20] = L"";
  775. WCHAR wszSeparator[] = L"_";
  776. PrivateDebugTrace("Entering CreateNotificationEventName().\n");
  777. wsprintfW(wszLuid, L"%08x%08x", luidCurrentUser.HighPart, luidCurrentUser.LowPart);
  778. if (NULL == (pwszEventName = (LPWSTR) malloc((lstrlenW(wszPrefixName) +
  779. lstrlenW(wszLuid) +
  780. lstrlenW(wszSeparator) +
  781. lstrlenW(pwszSuffixName) + 1) * sizeof(WCHAR))))
  782. {
  783. DebugTrace("Error: out of memory.\n");
  784. }
  785. else
  786. {
  787. lstrcpyW(pwszEventName, wszPrefixName);
  788. lstrcatW(pwszEventName, wszLuid);
  789. lstrcatW(pwszEventName, wszSeparator);
  790. lstrcatW(pwszEventName, pwszSuffixName);
  791. }
  792. PrivateDebugTrace("Leaving CreateNotificationEventName().\n");
  793. return pwszEventName;
  794. }
  795. //+----------------------------------------------------------------------------
  796. //
  797. // LogoffThreadProc
  798. //
  799. //-----------------------------------------------------------------------------
  800. DWORD WINAPI LogoffThreadProc(PVOID lpParameter)
  801. {
  802. HMODULE hModule = NULL;
  803. PLOGOFFDATA pLogoffData;
  804. PrivateDebugTrace("Entering LogoffThreadProc().\n");
  805. ASSERT(lpParameter);
  806. if (pLogoffData = (PLOGOFFDATA) lpParameter)
  807. {
  808. if (pLogoffData->hWait)
  809. {
  810. UnregisterWait(pLogoffData->hWait);
  811. }
  812. if (pLogoffData->pNotify)
  813. {
  814. pLogoffData->pNotify->UnregisterNotification();
  815. delete pLogoffData->pNotify;
  816. }
  817. if (pLogoffData->hEvent)
  818. {
  819. CloseHandle(pLogoffData->hEvent);
  820. }
  821. if (pLogoffData->hModule)
  822. {
  823. hModule = pLogoffData->hModule;
  824. }
  825. LocalFree(pLogoffData);
  826. }
  827. PrivateDebugTrace("Leaving LogoffThreadProc().\n");
  828. if (hModule)
  829. {
  830. FreeLibraryAndExitThread(hModule, 0);
  831. }
  832. return 0;
  833. }
  834. //+----------------------------------------------------------------------------
  835. //
  836. // LogoffWaitCallback
  837. //
  838. //-----------------------------------------------------------------------------
  839. VOID CALLBACK LogoffWaitCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
  840. {
  841. PLOGOFFDATA pLogoffData;
  842. PrivateDebugTrace("Entering LogoffWaitCallback().\n");
  843. (void) TimerOrWaitFired;
  844. ASSERT(lpParameter);
  845. if (pLogoffData = (PLOGOFFDATA) lpParameter)
  846. {
  847. DWORD dwThreadId = 0;
  848. HANDLE hThread = NULL;
  849. if (hThread = CreateThread(NULL,
  850. 0,
  851. (LPTHREAD_START_ROUTINE) LogoffThreadProc,
  852. lpParameter,
  853. 0,
  854. &dwThreadId))
  855. {
  856. CloseHandle(hThread);
  857. }
  858. else
  859. {
  860. DebugTrace("Error [%#x]: CreateThread() for LogoffThreadProc failed.\n", GetLastError());
  861. }
  862. }
  863. PrivateDebugTrace("Leaving LogoffWaitCallback().\n");
  864. return;
  865. }
  866. //+----------------------------------------------------------------------------
  867. //
  868. // Public
  869. //
  870. //-----------------------------------------------------------------------------
  871. //+----------------------------------------------------------------------------
  872. //
  873. // Function : RegisterTicketExpiredNotificationEvent
  874. //
  875. // Synopsis : To register and wait for the Kerberos notification event. When the
  876. // event is signaled, a ticket icon and balloon will appear in the
  877. // systray to warn the user about the problem, and suggest them to
  878. // lock and then unlock the machine with their new password.
  879. //
  880. // Parameter: PWLX_NOTIFICATION_INFO pNotificationInfo
  881. //
  882. // Return : If the function succeeds, zero is returned.
  883. //
  884. // If the function fails, a non-zero error code is returned.
  885. //
  886. // Remarks : This function should only be called by Winlogon LOGON
  887. // notification mechanism with the Asynchronous and Impersonate
  888. // flags set to 1.
  889. //
  890. // Also for each RegisterKerberosNotificationEvent() call, a
  891. // pairing call by Winlogon LOGOFF notification mechanism to
  892. // UnregisterKerberosNotificationEvent() must be made at the
  893. // end of each logon session.
  894. //
  895. //-----------------------------------------------------------------------------
  896. DWORD WINAPI RegisterTicketExpiredNotificationEvent(PWLX_NOTIFICATION_INFO pNotificationInfo)
  897. {
  898. DWORD dwRetCode = 0;
  899. LPWSTR pwszLogoffEventName = LOGOFF_NOTIFICATION_EVENT_NAME;
  900. LUID luidCurrentUser;
  901. PLOGOFFDATA pLogoffData = NULL;
  902. PrivateDebugTrace("Entering RegisterTicketExpiredNotificationEvent().\n");
  903. if (NULL == (pLogoffData = (PLOGOFFDATA) LocalAlloc(LPTR, sizeof(LOGOFFDATA))))
  904. {
  905. dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
  906. DebugTrace("Error [%#x]: out of memory.\n", dwRetCode);
  907. goto ErrorReturn;
  908. }
  909. if (NULL == (pLogoffData->hEvent = CreateEventW(NULL, TRUE, FALSE, pwszLogoffEventName)))
  910. {
  911. dwRetCode = GetLastError();
  912. DebugTrace("Error [%#x]: CreateEventW() failed for event %S.\n", dwRetCode, pwszLogoffEventName);
  913. goto ErrorReturn;
  914. }
  915. if (ERROR_ALREADY_EXISTS == GetLastError())
  916. {
  917. dwRetCode = ERROR_SINGLE_INSTANCE_APP;
  918. DebugTrace("Error [%#x]: cannot run more than one instance of this code per session.\n", dwRetCode);
  919. goto ErrorReturn;
  920. }
  921. DebugTrace("Info: Logoff event name = %S.\n", pwszLogoffEventName);
  922. if (0 != (dwRetCode = GetCurrentUsersLuid(&luidCurrentUser)))
  923. {
  924. DebugTrace("Error [%#x]: GetCurrentUsersLuid() failed.\n", dwRetCode);
  925. goto ErrorReturn;
  926. }
  927. if (NULL == (pLogoffData->hModule = LoadLibraryW(L"wlnotify.dll")))
  928. {
  929. dwRetCode = GetLastError();
  930. DebugTrace("Error [%#x]: LoadLibraryW() failed.\n", dwRetCode);
  931. goto ErrorReturn;
  932. }
  933. if (NULL == (pLogoffData->pNotify = new CNotify()))
  934. {
  935. dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
  936. DebugTrace("Error [%#x]: new CNotify() failed.\n", dwRetCode);
  937. goto ErrorReturn;
  938. }
  939. if (0 != (dwRetCode = pLogoffData->pNotify->RegisterNotification(luidCurrentUser, pNotificationInfo->hToken)))
  940. {
  941. goto ErrorReturn;
  942. }
  943. if (!RegisterWaitForSingleObject(&pLogoffData->hWait,
  944. pLogoffData->hEvent,
  945. LogoffWaitCallback,
  946. (PVOID) pLogoffData,
  947. INFINITE,
  948. WT_EXECUTEONLYONCE))
  949. {
  950. dwRetCode = (DWORD) E_UNEXPECTED;
  951. DebugTrace("Unexpected error: RegisterWaitForSingleObject() failed for LogoffWaitCallback().\n");
  952. goto ErrorReturn;
  953. }
  954. CommonReturn:
  955. PrivateDebugTrace("Leaving RegisterTicketExpiredNotificationEvent().\n");
  956. return dwRetCode;;
  957. ErrorReturn:
  958. ASSERT(0 != dwRetCode);
  959. if (pLogoffData)
  960. {
  961. if (pLogoffData->hWait)
  962. {
  963. UnregisterWait(pLogoffData->hWait);
  964. }
  965. if (pLogoffData->pNotify)
  966. {
  967. pLogoffData->pNotify->UnregisterNotification();
  968. delete pLogoffData->pNotify;
  969. }
  970. if (pLogoffData->hEvent)
  971. {
  972. CloseHandle(pLogoffData->hEvent);
  973. }
  974. if (pLogoffData->hModule)
  975. {
  976. FreeLibrary(pLogoffData->hModule);
  977. }
  978. LocalFree(pLogoffData);
  979. }
  980. goto CommonReturn;
  981. }
  982. //+----------------------------------------------------------------------------
  983. //
  984. // Function : UnregisterTicketExpiredNotificationEvent
  985. //
  986. // Synopsis : To unregister the Kerberos notification wait event registered by
  987. // RegisterKerberosNotificationEvent().
  988. //
  989. // Parameter: PWLX_NOTIFICATION_INFO pNotificationInfo
  990. //
  991. // Return : If the function succeeds, zero is returned.
  992. //
  993. // If the function fails, a non-zero error code is returned.
  994. //
  995. // Remarks : This function should only be called by Winlogon LOGON
  996. // notification mechanism with the Asynchronous and Impersonate
  997. // flags set to 1.
  998. //
  999. //-----------------------------------------------------------------------------
  1000. DWORD WINAPI UnregisterTicketExpiredNotificationEvent(PWLX_NOTIFICATION_INFO pNotificationInfo)
  1001. {
  1002. DWORD dwRetCode = 0;
  1003. HANDLE hLogoffEvent = NULL;
  1004. PrivateDebugTrace("Entering UnregisterTicketExpiredNotificationEvent().\n");
  1005. if (NULL == (hLogoffEvent = OpenEventW(EVENT_MODIFY_STATE, FALSE, LOGOFF_NOTIFICATION_EVENT_NAME)))
  1006. {
  1007. dwRetCode = GetLastError();
  1008. DebugTrace("Error [%#x]: OpenEventW() failed for event %S.\n", dwRetCode, LOGOFF_NOTIFICATION_EVENT_NAME);
  1009. goto ErrorReturn;
  1010. }
  1011. if (!SetEvent(hLogoffEvent))
  1012. {
  1013. dwRetCode = GetLastError();
  1014. DebugTrace("Error [%#x]: SetEvent() failed for event %S.\n", dwRetCode, LOGOFF_NOTIFICATION_EVENT_NAME);
  1015. goto ErrorReturn;
  1016. }
  1017. CommonReturn:
  1018. if (hLogoffEvent)
  1019. {
  1020. CloseHandle(hLogoffEvent);
  1021. }
  1022. PrivateDebugTrace("Leaving UnregisterTicketExpiredNotificationEvent().\n");
  1023. return dwRetCode;
  1024. ErrorReturn:
  1025. ASSERT(0 != dwRetCode);
  1026. goto CommonReturn;
  1027. }