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.

666 lines
19 KiB

  1. /****************************************************************************/
  2. // notify.c
  3. //
  4. // Copyright (C) 1997-1999 Microsoft Corp.
  5. /****************************************************************************/
  6. #include "precomp.h"
  7. #pragma hdrstop
  8. #include "errorlog.h"
  9. #include "regapi.h"
  10. #include "drdbg.h"
  11. #include "rdpprutl.h"
  12. #include "Sddl.h"
  13. //
  14. // Some helpful tips about winlogon's notify events
  15. //
  16. // 1) If you plan to put up any UI at logoff, you have to set
  17. // Asynchronous flag to 0. If this isn't set to 0, the user's
  18. // profile will fail to unload because UI is still active.
  19. //
  20. // 2) If you need to spawn child processes, you have to use
  21. // CreateProcessAsUser() otherwise the process will start
  22. // on winlogon's desktop (not the user's)
  23. //
  24. // 2) The logon notification comes before the user's network
  25. // connections are restored. If you need the user's persisted
  26. // net connections, use the StartShell event.
  27. //
  28. //
  29. // Global debug flag.
  30. extern DWORD GLOBAL_DEBUG_FLAGS;
  31. BOOL g_Console = TRUE;
  32. ULONG g_SessionId;
  33. BOOL g_InitialProg = FALSE;
  34. HANDLE hExecProg;
  35. HINSTANCE g_hInstance = NULL;
  36. CRITICAL_SECTION GlobalsLock;
  37. CRITICAL_SECTION ExecProcLock;
  38. BOOL g_IsPersonal;
  39. BOOL bInitLocks = FALSE;
  40. #define NOTIFY_PATH TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Notify\\HydraNotify")
  41. #define VOLATILE_PATH TEXT("Volatile Environment")
  42. #define STARTUP_PROGRAM TEXT("StartupPrograms")
  43. #define APPLICATION_DESKTOP_NAME TEXT("Default")
  44. #define WINDOW_STATION_NAME TEXT("WinSta0")
  45. #define IsTerminalServer() (BOOLEAN)(USER_SHARED_DATA->SuiteMask & (1 << TerminalServer))
  46. PCRITICAL_SECTION
  47. CtxGetSyslibCritSect(void);
  48. BOOL TSDLLInit(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
  49. {
  50. NTSTATUS Status;
  51. OSVERSIONINFOEX versionInfo;
  52. static BOOL sLogInit = FALSE;
  53. switch (dwReason)
  54. {
  55. case DLL_PROCESS_ATTACH:
  56. {
  57. if (!IsTerminalServer()) {
  58. return FALSE;
  59. }
  60. g_hInstance = hInstance;
  61. if (g_SessionId = NtCurrentPeb()->SessionId) {
  62. g_Console = FALSE;
  63. }
  64. Status = RtlInitializeCriticalSection( &GlobalsLock );
  65. if( !NT_SUCCESS(Status) ) {
  66. OutputDebugString (TEXT("LibMain (PROCESS_ATTACH): Could not initialize critical section\n"));
  67. return(FALSE);
  68. }
  69. Status = RtlInitializeCriticalSection( &ExecProcLock );
  70. if( !NT_SUCCESS(Status) ) {
  71. OutputDebugString (TEXT("LibMain (PROCESS_ATTACH): Could not initialize critical section\n"));
  72. RtlDeleteCriticalSection( &GlobalsLock );
  73. return(FALSE);
  74. }
  75. if (CtxGetSyslibCritSect() != NULL) {
  76. TsInitLogging();
  77. sLogInit = TRUE;
  78. }else{
  79. RtlDeleteCriticalSection( &GlobalsLock );
  80. RtlDeleteCriticalSection( &ExecProcLock );
  81. return FALSE;
  82. }
  83. //
  84. // Find out if we are running Personal.
  85. //
  86. versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  87. if (!GetVersionEx((LPOSVERSIONINFO)&versionInfo)) {
  88. DBGMSG(DBG_TRACE, ("GetVersionEx: %08X\n", GetLastError()));
  89. RtlDeleteCriticalSection( &GlobalsLock );
  90. RtlDeleteCriticalSection( &ExecProcLock );
  91. return FALSE;
  92. }
  93. g_IsPersonal = (versionInfo.wProductType == VER_NT_WORKSTATION) &&
  94. (versionInfo.wSuiteMask & VER_SUITE_PERSONAL);
  95. bInitLocks = TRUE;
  96. }
  97. break;
  98. case DLL_PROCESS_DETACH:
  99. {
  100. PRTL_CRITICAL_SECTION pLock = NULL;
  101. g_hInstance = NULL;
  102. if (sLogInit) {
  103. TsStopLogging();
  104. pLock = CtxGetSyslibCritSect();
  105. if (pLock)
  106. RtlDeleteCriticalSection(pLock);
  107. }
  108. if (bInitLocks) {
  109. RtlDeleteCriticalSection( &GlobalsLock );
  110. RtlDeleteCriticalSection( &ExecProcLock );
  111. bInitLocks = FALSE;
  112. }
  113. }
  114. break;
  115. }
  116. return TRUE;
  117. }
  118. VOID ExecApplications() {
  119. BOOL rc;
  120. ULONG ReturnLength;
  121. WDCONFIG WdInfo;
  122. //
  123. // HelpAssistant session doesn't need rdpclip.exe
  124. //
  125. if( WinStationIsHelpAssistantSession(SERVERNAME_CURRENT, LOGONID_CURRENT) ) {
  126. return;
  127. }
  128. //
  129. // Query winstation driver info
  130. //
  131. rc = WinStationQueryInformation(
  132. SERVERNAME_CURRENT,
  133. LOGONID_CURRENT,
  134. WinStationWd,
  135. (PVOID)&WdInfo,
  136. sizeof(WDCONFIG),
  137. &ReturnLength);
  138. if (rc) {
  139. if (ReturnLength == sizeof(WDCONFIG)) {
  140. HKEY hSpKey;
  141. WCHAR szRegPath[MAX_PATH];
  142. //
  143. // Open winstation driver reg key
  144. //
  145. wcscpy( szRegPath, WD_REG_NAME );
  146. wcscat( szRegPath, L"\\" );
  147. wcscat( szRegPath, WdInfo.WdPrefix );
  148. wcscat( szRegPath, L"wd" );
  149. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegPath, 0, KEY_READ,
  150. &hSpKey) == ERROR_SUCCESS) {
  151. DWORD dwLen;
  152. DWORD dwType;
  153. WCHAR szCmdLine[MAX_PATH];
  154. //
  155. // Get StartupPrograms string value
  156. //
  157. dwLen = sizeof( szCmdLine );
  158. if (RegQueryValueEx(hSpKey, STARTUP_PROGRAM, NULL, &dwType,
  159. (PCHAR) &szCmdLine, &dwLen) == ERROR_SUCCESS) {
  160. PWSTR pszTok;
  161. WCHAR szDesktop[MAX_PATH];
  162. STARTUPINFO si;
  163. PROCESS_INFORMATION pi;
  164. LPBYTE lpEnvironment = NULL;
  165. //
  166. // set STARTUPINFO fields
  167. //
  168. wsprintfW(szDesktop, L"%s\\%s", WINDOW_STATION_NAME,
  169. APPLICATION_DESKTOP_NAME);
  170. si.cb = sizeof(STARTUPINFO);
  171. si.lpReserved = NULL;
  172. si.lpTitle = NULL;
  173. si.lpDesktop = szDesktop;
  174. si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0L;
  175. si.dwFlags = STARTF_USESHOWWINDOW;
  176. si.wShowWindow = SW_SHOWNORMAL | SW_SHOWMINNOACTIVE;
  177. si.lpReserved2 = NULL;
  178. si.cbReserved2 = 0;
  179. //
  180. // Get the user Environment block to be used in CreateProcessAsUser
  181. //
  182. if (CreateEnvironmentBlock (&lpEnvironment, g_UserToken, FALSE)) {
  183. //
  184. // Enumerate the StartupPrograms string,
  185. //
  186. pszTok = wcstok(szCmdLine, L",");
  187. while (pszTok != NULL) {
  188. // skip any white space
  189. if (*pszTok == L' ') {
  190. while (*pszTok++ == L' ');
  191. }
  192. //
  193. // Call CreateProcessAsUser to start the program
  194. //
  195. si.lpReserved = (LPTSTR)pszTok;
  196. si.lpTitle = (LPTSTR)pszTok;
  197. rc = CreateProcessAsUser(
  198. g_UserToken,
  199. NULL,
  200. (LPTSTR)pszTok,
  201. NULL,
  202. NULL,
  203. FALSE,
  204. NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT,
  205. lpEnvironment,
  206. NULL,
  207. &si,
  208. &pi);
  209. if (rc) {
  210. DebugLog((DEB_TRACE, "TSNOTIFY: successfully called CreateProcessAsUser for %s",
  211. (LPTSTR)pszTok));
  212. CloseHandle(pi.hThread);
  213. //CloseHandle(pi.hProcess);
  214. hExecProg = pi.hProcess;
  215. }
  216. else {
  217. DebugLog((DEB_ERROR, "TSNOTIFY: failed calling CreateProcessAsUser for %s",
  218. (LPTSTR)pszTok));
  219. }
  220. // move onto the next token
  221. pszTok = wcstok(NULL, L",");
  222. }
  223. DestroyEnvironmentBlock(lpEnvironment);
  224. }
  225. else {
  226. DebugLog((DEB_ERROR,
  227. "TSNOTIFY: failed to get Environment block for user, %ld",
  228. GetLastError()));
  229. }
  230. }
  231. else {
  232. DebugLog((DEB_ERROR, "TSNOTIFY: failed to read the StartupPrograms key"));
  233. }
  234. RegCloseKey(hSpKey);
  235. }
  236. else {
  237. DebugLog((DEB_ERROR, "TSNOTIFY: failed to open the rdpwd key"));
  238. }
  239. }
  240. else {
  241. DebugLog((DEB_ERROR, "TSNOTIFY: WinStationQueryInformation didn't return correct length"));
  242. }
  243. }
  244. else {
  245. DebugLog((DEB_ERROR, "TSNOTIFY: WinStationQueryInformation call failed"));
  246. }
  247. }
  248. VOID TSUpdateUserConfig( PWLX_NOTIFICATION_INFO pInfo)
  249. {
  250. HINSTANCE hLib;
  251. typedef void ( WINAPI TypeDef_fp) ( HANDLE );
  252. TypeDef_fp *fp1;
  253. hLib = LoadLibrary(TEXT("winsta.dll"));
  254. if ( !hLib)
  255. {
  256. DebugLog (( DEB_ERROR, "TSNOTIFY: Unable to load lib winsta.dll"));
  257. return;
  258. }
  259. fp1 = ( TypeDef_fp *)
  260. GetProcAddress(hLib, "_WinStationUpdateUserConfig");
  261. if (fp1)
  262. {
  263. fp1 ( pInfo->hToken );
  264. }
  265. else
  266. {
  267. DebugLog (( DEB_ERROR, "TSNOTIFY: Unable to find proc in winsta.dll"));
  268. }
  269. FreeLibrary(hLib);
  270. }
  271. VOID TSEventLogon (PWLX_NOTIFICATION_INFO pInfo)
  272. {
  273. if (!IsTerminalServer()) {
  274. return;
  275. }
  276. g_UserToken = pInfo->hToken;
  277. if (!g_Console) {
  278. //
  279. // Notify the EXEC service that the user is
  280. // logged on
  281. //
  282. CtxExecServerLogon( pInfo->hToken );
  283. }
  284. EnterCriticalSection( &ExecProcLock );
  285. if (!IsActiveConsoleSession() && (hExecProg == NULL)) {
  286. //
  287. // Search for StartupPrograms string in Terminal Server WD registry key
  288. // and start processes as needed
  289. //
  290. ExecApplications();
  291. }
  292. LeaveCriticalSection( &ExecProcLock );
  293. }
  294. VOID TSEventLogoff (PWLX_NOTIFICATION_INFO pInfo)
  295. {
  296. if (!IsTerminalServer()) {
  297. return;
  298. }
  299. if (!g_Console) {
  300. RemovePerSessionTempDirs();
  301. CtxExecServerLogoff();
  302. }
  303. if ( g_InitialProg ) {
  304. DeleteUserProcessMonitor( UserProcessMonitor );
  305. }
  306. if (g_Console) {
  307. //
  308. //Turn off the install mode if the console user is logging off
  309. //
  310. SetTermsrvAppInstallMode( FALSE );
  311. }
  312. EnterCriticalSection( &ExecProcLock );
  313. // Shut down the user-mode RDP device manager component.
  314. if (!g_IsPersonal) {
  315. UMRDPDR_Shutdown();
  316. }
  317. g_UserToken = NULL;
  318. CloseHandle(hExecProg);
  319. hExecProg = NULL;
  320. LeaveCriticalSection( &ExecProcLock );
  321. }
  322. VOID TSEventStartup (PWLX_NOTIFICATION_INFO pInfo)
  323. {
  324. if (!IsTerminalServer()) {
  325. return;
  326. }
  327. if (!g_Console) {
  328. //
  329. // Start ExecServer thread
  330. //
  331. StartExecServerThread();
  332. }
  333. }
  334. VOID TSEventShutdown (PWLX_NOTIFICATION_INFO pInfo)
  335. {
  336. if (!IsTerminalServer()) {
  337. return;
  338. }
  339. // Shut down the user-mode RDP device manager component. This function can be
  340. // called multiple times, in the event that it was already called as a result of
  341. // a log off.
  342. if (!g_IsPersonal) {
  343. UMRDPDR_Shutdown();
  344. }
  345. }
  346. LPTSTR GetStringSid(PWLX_NOTIFICATION_INFO pInfo)
  347. {
  348. LPTSTR sStringSid = NULL;
  349. DWORD ReturnLength = 0;
  350. PTOKEN_USER pTokenUser = NULL;
  351. PSID pSid = NULL;
  352. NtQueryInformationToken(pInfo->hToken,
  353. TokenUser,
  354. NULL,
  355. 0,
  356. &ReturnLength);
  357. if (ReturnLength == 0)
  358. return NULL;
  359. pTokenUser = RtlAllocateHeap(RtlProcessHeap(), 0, ReturnLength);
  360. if (pTokenUser != NULL)
  361. {
  362. if (NT_SUCCESS(NtQueryInformationToken(pInfo->hToken,
  363. TokenUser,
  364. pTokenUser,
  365. ReturnLength,
  366. &ReturnLength)))
  367. {
  368. pSid = pTokenUser->User.Sid;
  369. if (pSid != NULL)
  370. {
  371. if (!ConvertSidToStringSid(pSid, &sStringSid))
  372. sStringSid = NULL;
  373. }
  374. }
  375. }
  376. if (pTokenUser != NULL)
  377. RtlFreeHeap(RtlProcessHeap(), 0, pTokenUser);
  378. return sStringSid;
  379. }
  380. BOOL IsAppServer(void)
  381. {
  382. OSVERSIONINFOEX osVersionInfo;
  383. DWORDLONG dwlConditionMask = 0;
  384. BOOL fIsWTS = FALSE;
  385. osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  386. fIsWTS = GetVersionEx((OSVERSIONINFO *)&osVersionInfo) &&
  387. (osVersionInfo.wSuiteMask & VER_SUITE_TERMINAL) &&
  388. !(osVersionInfo.wSuiteMask & VER_SUITE_SINGLEUSERTS);
  389. return fIsWTS;
  390. }
  391. VOID RemoveClassesKey(PWLX_NOTIFICATION_INFO pInfo)
  392. {
  393. HINSTANCE hLib;
  394. typedef BOOL ( WINAPI TypeDef_fp) (LPTSTR);
  395. TypeDef_fp *fp1;
  396. LPTSTR sStringSid = NULL;
  397. sStringSid = GetStringSid(pInfo);
  398. if (sStringSid == NULL)
  399. {
  400. DebugLog((DEB_ERROR, "TSNOTIFY: Unable to obtain sid"));
  401. return;
  402. }
  403. hLib = LoadLibrary(TEXT("tsappcmp.dll"));
  404. if (!hLib)
  405. {
  406. DebugLog((DEB_ERROR, "TSNOTIFY: Unable to load lib tsappcmp.dll"));
  407. return;
  408. }
  409. fp1 = (TypeDef_fp*)
  410. GetProcAddress(hLib, "TermsrvRemoveClassesKey");
  411. if (fp1)
  412. fp1(sStringSid);
  413. else
  414. DebugLog((DEB_ERROR, "TSNOTIFY: Unable to find proc in tsappcmp.dll"));
  415. FreeLibrary(hLib);
  416. }
  417. VOID TSEventStartShell (PWLX_NOTIFICATION_INFO pInfo)
  418. {
  419. if (!IsTerminalServer())
  420. return;
  421. // We are either a TS-App-Server, a TS-Remote-Admin, or a PTS since
  422. // IsTerminalServer() call is using the kernel flag to check this.
  423. // by now, group policy has update user's hive, so we can tell termsrv
  424. // to update user's config.
  425. TSUpdateUserConfig(pInfo);
  426. if (IsAppServer())
  427. RemoveClassesKey(pInfo);
  428. }
  429. VOID TSEventReconnect (PWLX_NOTIFICATION_INFO pInfo)
  430. {
  431. if (!IsTerminalServer()) {
  432. return;
  433. }
  434. EnterCriticalSection( &ExecProcLock );
  435. if (!IsActiveConsoleSession()) {
  436. if (g_UserToken && hExecProg == NULL) {
  437. //
  438. // Search for StartupPrograms string in Terminal Server WD registry key
  439. // and start processes as needed
  440. //
  441. ExecApplications();
  442. // Initialize the user-mode RDP device manager component.
  443. if (!g_IsPersonal) {
  444. if (!UMRDPDR_Initialize(g_UserToken)) {
  445. WCHAR buf[256];
  446. WCHAR *parms[1];
  447. parms[0] = buf;
  448. wsprintf(buf, L"%ld", g_SessionId);
  449. TsLogError(EVENT_NOTIFY_INIT_FAILED, EVENTLOG_ERROR_TYPE, 1, parms, __LINE__);
  450. }
  451. }
  452. }
  453. } else {
  454. if (hExecProg) {
  455. TerminateProcess(hExecProg, 0);
  456. CloseHandle(hExecProg);
  457. hExecProg = NULL;
  458. }
  459. // Shut down the user-mode RDP device manager component.
  460. if (!g_IsPersonal) {
  461. UMRDPDR_Shutdown();
  462. }
  463. }
  464. LeaveCriticalSection( &ExecProcLock );
  465. }
  466. VOID TSEventDisconnect (PWLX_NOTIFICATION_INFO pInfo)
  467. {
  468. if (!IsTerminalServer()) {
  469. return;
  470. }
  471. }
  472. VOID TSEventPostShell (PWLX_NOTIFICATION_INFO pInfo)
  473. {
  474. OSVERSIONINFOEX versionInfo;
  475. if (!IsTerminalServer()) {
  476. return;
  477. }
  478. if ( !g_Console ) {
  479. ULONG Length;
  480. BOOLEAN Result;
  481. WINSTATIONCONFIG ConfigData;
  482. Result = WinStationQueryInformation( SERVERNAME_CURRENT,
  483. LOGONID_CURRENT,
  484. WinStationConfiguration,
  485. &ConfigData,
  486. sizeof(ConfigData),
  487. &Length );
  488. if (Result && ConfigData.User.InitialProgram[0] &&
  489. lstrcmpi(ConfigData.User.InitialProgram, TEXT("explorer.exe"))) {
  490. if ( !(UserProcessMonitor = StartUserProcessMonitor()) ) {
  491. DebugLog((DEB_ERROR, "Failed to start user process monitor thread"));
  492. }
  493. g_InitialProg = TRUE;
  494. }
  495. }
  496. //
  497. // Clean up old TS queues on Pro.
  498. //
  499. versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  500. if (!GetVersionEx((LPOSVERSIONINFO)&versionInfo)) {
  501. DBGMSG(DBG_TRACE, ("GetVersionEx: %08X\n", GetLastError()));
  502. ASSERT(FALSE);
  503. }
  504. //
  505. // This code only runs on Pro because it's the only platform where
  506. // we can guarantee that we have one session per machine. Printers are
  507. // cleaned up on boot in Server.
  508. //
  509. else if ((versionInfo.wProductType == VER_NT_WORKSTATION) &&
  510. !(versionInfo.wSuiteMask & VER_SUITE_PERSONAL)) {
  511. RDPDRUTL_RemoveAllTSPrinters();
  512. }
  513. //
  514. // This code shouldn't run on Personal. Device redirection isn't
  515. // supported for Personal.
  516. //
  517. if (!g_IsPersonal) {
  518. EnterCriticalSection( &ExecProcLock );
  519. // Initialize the user-mode RDP device manager component.
  520. if (!UMRDPDR_Initialize(pInfo->hToken)) {
  521. WCHAR buf[256];
  522. WCHAR *parms[1];
  523. wsprintf(buf, L"%ld", g_SessionId);
  524. parms[0] = buf;
  525. TsLogError(EVENT_NOTIFY_INIT_FAILED, EVENTLOG_ERROR_TYPE, 1, parms, __LINE__);
  526. }
  527. LeaveCriticalSection(&ExecProcLock);
  528. }
  529. }