Source code of Windows XP (NT5)
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.

617 lines
15 KiB

  1. /************************************************************************
  2. Copyright (c) 2001 - Microsoft Corporation
  3. Module Name :
  4. csens.cpp
  5. Abstract :
  6. Code for recieving logon notifications from SENS.
  7. Author :
  8. Revision History :
  9. ***********************************************************************/
  10. #include "stdafx.h"
  11. #include <wtsapi32.h>
  12. #if !defined(BITS_V12_ON_NT4)
  13. #include "csens.tmh"
  14. #endif
  15. HRESULT GetConsoleUserPresent( bool * pfPresent );
  16. HRESULT GetConsoleUsername( LPWSTR * User );
  17. //------------------------------------------------------------------------
  18. CLogonNotification::CLogonNotification() :
  19. m_EventSystem( NULL ),
  20. m_TypeLib( NULL ),
  21. m_TypeInfo( NULL )
  22. {
  23. try
  24. {
  25. m_EventSubscriptions[0] = NULL;
  26. m_EventSubscriptions[1] = NULL;
  27. #if defined( BITS_V12_ON_NT4 )
  28. {
  29. // try to load the SENS typelibrary
  30. // {D597DEED-5B9F-11D1-8DD2-00AA004ABD5E}
  31. HRESULT Hr;
  32. static const GUID SensTypeLibGUID =
  33. { 0xD597DEED, 0x5B9F, 0x11D1, { 0x8D, 0xD2, 0x00, 0xAA, 0x00, 0x4A, 0xBD, 0x5E } };
  34. Hr = LoadRegTypeLib( SensTypeLibGUID, 2, 0, GetSystemDefaultLCID(), &m_TypeLib);
  35. if ( TYPE_E_CANTLOADLIBRARY == Hr || TYPE_E_LIBNOTREGISTERED == Hr )
  36. {
  37. Hr = LoadRegTypeLib( SensTypeLibGUID, 1, 0, GetSystemDefaultLCID(), &m_TypeLib );
  38. if ( TYPE_E_CANTLOADLIBRARY == Hr || TYPE_E_LIBNOTREGISTERED == Hr )
  39. Hr = LoadTypeLibEx( L"SENS.DLL", REGKIND_NONE, &m_TypeLib );
  40. }
  41. THROW_HRESULT( Hr );
  42. }
  43. #else
  44. THROW_HRESULT( LoadTypeLibEx( L"SENS.DLL", REGKIND_NONE, &m_TypeLib ) );
  45. #endif
  46. THROW_HRESULT( m_TypeLib->GetTypeInfoOfGuid( __uuidof( ISensLogon ), &m_TypeInfo ) );
  47. THROW_HRESULT( CoCreateInstance( CLSID_CEventSystem,
  48. NULL,
  49. CLSCTX_SERVER,
  50. IID_IEventSystem,
  51. (void**)&m_EventSystem
  52. ) );
  53. // Register for the individual methods
  54. const WCHAR *MethodNames[] =
  55. {
  56. L"Logon",
  57. L"Logoff"
  58. };
  59. const WCHAR *UniqueIdentifies[] =
  60. {
  61. L"{c69c8f03-b25c-45d1-96fa-6dfb1f292b26}",
  62. L"{5f4f5e8d-4599-4ba0-b53d-1de5440b8770}"
  63. };
  64. for( SIZE_T i = 0; i < ( sizeof(MethodNames) / sizeof(*MethodNames) ); i++ )
  65. {
  66. WCHAR EventGuidString[ 50 ];
  67. THROW_HRESULT( CoCreateInstance( CLSID_CEventSubscription,
  68. NULL,
  69. CLSCTX_SERVER,
  70. IID_IEventSubscription,
  71. (LPVOID *) &m_EventSubscriptions[i]
  72. ) );
  73. StringFromGUID2( SENSGUID_EVENTCLASS_LOGON, EventGuidString, 50 );
  74. THROW_HRESULT( m_EventSubscriptions[i]->put_EventClassID( EventGuidString ) );
  75. THROW_HRESULT( m_EventSubscriptions[i]->put_SubscriberInterface( this ) );
  76. THROW_HRESULT( m_EventSubscriptions[i]->put_SubscriptionName( (BSTR) L"Microsoft-BITS" ) );
  77. THROW_HRESULT( m_EventSubscriptions[i]->put_Description( (BSTR) L"BITS Notification" ) );
  78. THROW_HRESULT( m_EventSubscriptions[i]->put_Enabled( FALSE ) );
  79. THROW_HRESULT( m_EventSubscriptions[i]->put_MethodName( (BSTR)MethodNames[i] ) );
  80. THROW_HRESULT( m_EventSubscriptions[i]->put_SubscriptionID( (BSTR)UniqueIdentifies[i] ) );
  81. THROW_HRESULT( m_EventSystem->Store(PROGID_EventSubscription, m_EventSubscriptions[i] ) );
  82. }
  83. }
  84. catch( ComError Error )
  85. {
  86. Cleanup();
  87. throw;
  88. }
  89. }
  90. void
  91. CLogonNotification::DeRegisterNotification()
  92. {
  93. SafeRelease( m_EventSubscriptions[0] );
  94. SafeRelease( m_EventSubscriptions[1] );
  95. if ( m_EventSystem )
  96. {
  97. int ErrorIndex;
  98. m_EventSystem->Remove( PROGID_EventSubscription,
  99. L"EventClassID == {D5978630-5B9F-11D1-8DD2-00AA004ABD5E} AND SubscriptionName == Microsoft-BITS",
  100. &ErrorIndex );
  101. SafeRelease( m_EventSystem );
  102. }
  103. }
  104. void
  105. CLogonNotification::Cleanup()
  106. {
  107. DeRegisterNotification();
  108. SafeRelease( m_TypeInfo );
  109. SafeRelease( m_TypeLib );
  110. LogInfo("cleanup complete");
  111. }
  112. HRESULT CLogonNotification::SetEnableState( bool fEnable )
  113. {
  114. try
  115. {
  116. for (int i=0; i <= 1; ++i)
  117. {
  118. THROW_HRESULT( m_EventSubscriptions[i]->put_Enabled( fEnable ) );
  119. }
  120. for (int i=0; i <= 1; ++i)
  121. {
  122. THROW_HRESULT( m_EventSystem->Store(PROGID_EventSubscription, m_EventSubscriptions[i] ) );
  123. }
  124. LogInfo("SENS enable state is %d", fEnable);
  125. return S_OK;
  126. }
  127. catch ( ComError err )
  128. {
  129. LogInfo("SENS set enable state (%d) returned %x", fEnable, err.Error());
  130. return err.Error();
  131. }
  132. }
  133. STDMETHODIMP
  134. CLogonNotification::GetIDsOfNames(
  135. REFIID,
  136. OLECHAR FAR* FAR* rgszNames,
  137. unsigned int cNames,
  138. LCID,
  139. DISPID FAR* rgDispId )
  140. {
  141. return m_TypeInfo->GetIDsOfNames(
  142. rgszNames,
  143. cNames,
  144. rgDispId );
  145. }
  146. STDMETHODIMP
  147. CLogonNotification::GetTypeInfo(
  148. unsigned int iTInfo,
  149. LCID,
  150. ITypeInfo FAR* FAR* ppTInfo )
  151. {
  152. if ( iTInfo != 0 )
  153. return DISP_E_BADINDEX;
  154. *ppTInfo = m_TypeInfo;
  155. m_TypeInfo->AddRef();
  156. return S_OK;
  157. }
  158. STDMETHODIMP
  159. CLogonNotification::GetTypeInfoCount(
  160. unsigned int FAR* pctinfo )
  161. {
  162. *pctinfo = 1;
  163. return S_OK;
  164. }
  165. STDMETHODIMP
  166. CLogonNotification::Invoke(
  167. DISPID dispID,
  168. REFIID riid,
  169. LCID,
  170. WORD wFlags,
  171. DISPPARAMS FAR* pDispParams,
  172. VARIANT FAR* pvarResult,
  173. EXCEPINFO FAR* pExcepInfo,
  174. unsigned int FAR* puArgErr )
  175. {
  176. if (riid != IID_NULL)
  177. {
  178. return DISP_E_UNKNOWNINTERFACE;
  179. }
  180. return m_TypeInfo->Invoke(
  181. (IDispatch*) this,
  182. dispID,
  183. wFlags,
  184. pDispParams,
  185. pvarResult,
  186. pExcepInfo,
  187. puArgErr
  188. );
  189. }
  190. STDMETHODIMP
  191. CLogonNotification::DisplayLock(
  192. BSTR UserName )
  193. {
  194. return S_OK;
  195. }
  196. STDMETHODIMP
  197. CLogonNotification::DisplayUnlock(
  198. BSTR UserName )
  199. {
  200. return S_OK;
  201. }
  202. STDMETHODIMP
  203. CLogonNotification::StartScreenSaver(
  204. BSTR UserName )
  205. {
  206. return S_OK;
  207. }
  208. STDMETHODIMP
  209. CLogonNotification::StopScreenSaver(
  210. BSTR UserName )
  211. {
  212. return S_OK;
  213. }
  214. STDMETHODIMP
  215. CLogonNotification::Logon(
  216. BSTR UserName )
  217. {
  218. LogInfo( "SENS logon notification for %S", (WCHAR*)UserName );
  219. HRESULT Hr = SessionLogonCallback( 0 );
  220. LogInfo( "SENS logon notification for %S processed, %!winerr!", (WCHAR*)UserName, Hr );
  221. return Hr;
  222. }
  223. STDMETHODIMP
  224. CLogonNotification::Logoff(
  225. BSTR UserName )
  226. {
  227. LogInfo( "SENS logoff notification for %S", (WCHAR*)UserName );
  228. HRESULT Hr = SessionLogoffCallback( 0 );
  229. LogInfo( "SENS logoff notification for %S processed, %!winerr!", (WCHAR*)UserName, Hr );
  230. return Hr;
  231. }
  232. STDMETHODIMP
  233. CLogonNotification::StartShell(
  234. BSTR UserName )
  235. {
  236. return S_OK;
  237. }
  238. //------------------------------------------------------------------------
  239. CTerminalServerLogonNotification::CTerminalServerLogonNotification()
  240. : m_PendingUserChecks( 0 ),
  241. m_fConsoleUser( false )
  242. {
  243. }
  244. CTerminalServerLogonNotification::~CTerminalServerLogonNotification()
  245. {
  246. while (m_PendingUserChecks)
  247. {
  248. LogInfo("m_PendingUserChecks is %d", m_PendingUserChecks);
  249. Sleep(50);
  250. }
  251. }
  252. STDMETHODIMP
  253. CTerminalServerLogonNotification::Logon(
  254. BSTR UserName )
  255. {
  256. HRESULT Hr = S_OK;
  257. LogInfo( "TS SENS logon notification for %S", (WCHAR*)UserName );
  258. if (!m_fConsoleUser)
  259. {
  260. // Wait a few seconds in case TS hasn't seen the notification yet, then
  261. // check whetherthe notification was for the console.
  262. // if it fails, not much recourse.
  263. //
  264. Hr = QueueConsoleUserCheck();
  265. }
  266. LogInfo( "hr = %!winerr!", Hr );
  267. return Hr;
  268. }
  269. STDMETHODIMP
  270. CTerminalServerLogonNotification::Logoff(
  271. BSTR UserName )
  272. {
  273. HRESULT Hr = S_OK;
  274. LogInfo( "TS SENS logoff notification for %S", (WCHAR*)UserName );
  275. if (m_fConsoleUser)
  276. {
  277. bool fSame;
  278. LPWSTR ConsoleUserName = NULL;
  279. Hr = GetConsoleUsername( &ConsoleUserName );
  280. if (FAILED( Hr ))
  281. {
  282. //
  283. // unable to check. Security dictates that we be conservative and remove the user.
  284. //
  285. LogError("unable to fetch console username %x, thus logoff callback", Hr);
  286. Hr = SessionLogoffCallback( 0 );
  287. m_fConsoleUser = false;
  288. }
  289. else if (ConsoleUserName == NULL)
  290. {
  291. //
  292. // no user logged in at the console
  293. //
  294. LogInfo("no one logged in at the console, thus logoff callback");
  295. Hr = SessionLogoffCallback( 0 );
  296. m_fConsoleUser = false;
  297. }
  298. else if (0 != _wcsicmp( UserName, ConsoleUserName))
  299. {
  300. LogInfo("console user is %S; doesn't match", ConsoleUserName);
  301. delete [] ConsoleUserName;
  302. Hr = S_OK;
  303. }
  304. else
  305. {
  306. // correct user, but (s)he might have logged off from a TS session.
  307. // We should wait a few seconds before checking the console state because the
  308. // TS code may not have seen the logoff notification yet. Because Logoff is a synchronous
  309. // notification, we cannot just Sleep before checking..
  310. //
  311. delete [] ConsoleUserName;
  312. if (FAILED(QueueConsoleUserCheck()))
  313. {
  314. //
  315. // unable to check. Security dictates that we be conservative and remove the user.
  316. //
  317. LogError("unable to queue check, thus logoff callback");
  318. Hr = SessionLogoffCallback( 0 );
  319. m_fConsoleUser = false;
  320. }
  321. }
  322. }
  323. else
  324. {
  325. LogInfo("ignoring, no console user");
  326. }
  327. LogInfo( "hr = %!winerr!", Hr );
  328. return Hr;
  329. }
  330. HRESULT
  331. CTerminalServerLogonNotification::QueueConsoleUserCheck()
  332. {
  333. if (QueueUserWorkItem( UserCheckThreadProc, this, WT_EXECUTELONGFUNCTION ))
  334. {
  335. InterlockedIncrement( &m_PendingUserChecks );
  336. LogInfo("queued user check: about %d pending", m_PendingUserChecks );
  337. return S_OK;
  338. }
  339. else
  340. {
  341. DWORD s = GetLastError();
  342. LogError("unable to queue user check %!winerr!", s);
  343. return HRESULT_FROM_WIN32( s );
  344. }
  345. }
  346. DWORD WINAPI
  347. CTerminalServerLogonNotification::UserCheckThreadProc(
  348. LPVOID arg
  349. )
  350. {
  351. CTerminalServerLogonNotification * _this = reinterpret_cast<CTerminalServerLogonNotification *>(arg);
  352. LogInfo("sleeping before user check");
  353. Sleep( 5 * 1000 );
  354. _this->ConsoleUserCheck();
  355. return 0;
  356. }
  357. void CTerminalServerLogonNotification::ConsoleUserCheck()
  358. {
  359. HRESULT Hr;
  360. LogInfo("SENS console user check");
  361. if (IsServiceShuttingDown())
  362. {
  363. LogWarning("service is shutting down.");
  364. InterlockedDecrement( &m_PendingUserChecks );
  365. return;
  366. }
  367. bool bConsoleUser;
  368. Hr = GetConsoleUserPresent( &bConsoleUser );
  369. //
  370. // Security requires us to be conservative: if we can't tell whether the user
  371. // is logged in, we must release his token.
  372. //
  373. if (FAILED(Hr))
  374. {
  375. LogError("GetConsoleUserPresent returned %x", Hr );
  376. }
  377. if (FAILED(Hr) || !bConsoleUser)
  378. {
  379. LogInfo("logoff callback");
  380. if (FAILED(SessionLogoffCallback( 0 )))
  381. {
  382. // unusual: the only obvious generator is
  383. // - no known user at console
  384. // - TS logon or failing console logon
  385. // - memory allocation failure referring to m_ActiveSessions[ session ]
  386. // either way, we don't think a user is at the console, so m_fConsoleUser should be false.
  387. }
  388. m_fConsoleUser = false;
  389. }
  390. else
  391. {
  392. LogInfo("logon callback");
  393. m_fConsoleUser = true;
  394. if (FAILED(SessionLogonCallback( 0 )))
  395. {
  396. // no user token available, but we still know that there is a console user.
  397. }
  398. }
  399. InterlockedDecrement( &m_PendingUserChecks );
  400. }
  401. HRESULT
  402. GetConsoleUserPresent( bool * pfPresent )
  403. {
  404. /*
  405. If logon fails, we still know that there is a user at the console.
  406. Setting the flag will prevent queued checks for further logons, and
  407. logoff handles the no-user case.
  408. For Logoff, regardless of exit path there is no user recorded for that session.
  409. Setting the flag prevents queued checks for future logoffs.
  410. */
  411. INT * pConnectState = 0;
  412. DWORD size;
  413. if (WTSQuerySessionInformation( WTS_CURRENT_SERVER,
  414. 0,
  415. WTSConnectState,
  416. reinterpret_cast<LPTSTR *>(&pConnectState),
  417. &size))
  418. {
  419. LogInfo("console session state is %d", *pConnectState);
  420. if (*pConnectState == WTSActive ||
  421. *pConnectState == WTSDisconnected)
  422. {
  423. LogInfo("console user present");
  424. *pfPresent = true;
  425. }
  426. else
  427. {
  428. LogInfo("no console user");
  429. *pfPresent = false;
  430. }
  431. WTSFreeMemory( pConnectState );
  432. return S_OK;
  433. }
  434. else
  435. {
  436. DWORD s = GetLastError();
  437. LogInfo("WTSQuerySessionInformation returned %!winerr!", s);
  438. return HRESULT_FROM_WIN32( s );
  439. }
  440. }
  441. HRESULT GetConsoleUsername( LPWSTR * pFinalName )
  442. {
  443. HRESULT hr;
  444. LPWSTR UserName = NULL;
  445. LPWSTR DomainName = NULL;
  446. *pFinalName = NULL;
  447. try
  448. {
  449. DWORD UserSize;
  450. DWORD DomainSize;
  451. if (!WTSQuerySessionInformationW( WTS_CURRENT_SERVER,
  452. 0,
  453. WTSUserName,
  454. &UserName,
  455. &UserSize))
  456. {
  457. ThrowLastError();
  458. }
  459. if (!WTSQuerySessionInformationW( WTS_CURRENT_SERVER,
  460. 0,
  461. WTSDomainName,
  462. &DomainName,
  463. &DomainSize))
  464. {
  465. ThrowLastError();
  466. }
  467. *pFinalName = new WCHAR[ DomainSize + 1 + UserSize + 1 ];
  468. hr = StringCchPrintf( *pFinalName,
  469. UserSize + 1 + DomainSize + 1,
  470. L"%s\\%s", DomainName, UserName
  471. );
  472. }
  473. catch ( ComError err )
  474. {
  475. delete [] *pFinalName;
  476. *pFinalName = NULL;
  477. hr = err.Error();
  478. }
  479. if (DomainName)
  480. {
  481. WTSFreeMemory( DomainName );
  482. }
  483. if (UserName)
  484. {
  485. WTSFreeMemory( UserName );
  486. }
  487. return hr;
  488. }