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.

795 lines
20 KiB

  1. /*++
  2. Copyright (c) 1999-2000 Microsoft Corporation
  3. Module Name:
  4. helpass.c
  5. Abstract:
  6. Salem related function.
  7. Author:
  8. HueiWang 4/26/2000
  9. --*/
  10. #define LSCORE_NO_ICASRV_GLOBALS
  11. #include "precomp.h"
  12. #include <tdi.h>
  13. #include <winsock2.h>
  14. #include <ws2tcpip.h>
  15. #include "tsremdsk.h"
  16. #include "sessmgr.h"
  17. #include "sessmgr_i.c"
  18. extern "C"
  19. NTSTATUS
  20. xxxQueryRemoteAddress(
  21. PWINSTATION pWinStation,
  22. PWINSTATIONREMOTEADDRESS pRemoteAddress
  23. );
  24. HRESULT
  25. __LogSalemEvent(
  26. IN IRemoteDesktopHelpSessionMgr* iSessMgr,
  27. IN ULONG eventType,
  28. IN ULONG eventCode,
  29. IN int numStrings,
  30. IN BSTR EventStrings[]
  31. );
  32. //
  33. // Function copied from atlconv.h, we don't include
  34. // any ATL header in termsrv.
  35. //
  36. BSTR A2WBSTR(LPCSTR lp)
  37. {
  38. if (lp == NULL)
  39. return NULL;
  40. BSTR str = NULL;
  41. int nConvertedLen = MultiByteToWideChar(
  42. GetACP(), 0, lp,
  43. -1, NULL, NULL)-1;
  44. str = ::SysAllocStringLen(NULL, nConvertedLen);
  45. if (str != NULL)
  46. {
  47. MultiByteToWideChar(GetACP(), 0, lp, -1,
  48. str, nConvertedLen);
  49. }
  50. return str;
  51. }
  52. NTSTATUS
  53. TSHelpAssistantQueryLogonCredentials(
  54. ExtendedClientCredentials* pCredential
  55. )
  56. /*++
  57. Description:
  58. Retrieve HelpAssistant logon credential, routine first retrieve
  59. infor passed from client and then decrypt password
  60. Parameters:
  61. pWinStation : Pointer to WINSTATION
  62. pCredential : Pointer to ExtendedClientCredentials to receive HelpAssistant
  63. credential.
  64. Returns:
  65. STATUS_SUCCESS or STATUS_INVALID_PARAMETER
  66. --*/
  67. {
  68. LPWSTR pszHelpAssistantPassword = NULL;
  69. NTSTATUS Status;
  70. LPWSTR pszHelpAssistantAccountName = NULL;
  71. LPWSTR pszHelpAssistantAccountDomain = NULL;
  72. if( pCredential )
  73. {
  74. ZeroMemory( pCredential, sizeof(ExtendedClientCredentials) );
  75. Status = TSGetHelpAssistantAccountName(&pszHelpAssistantAccountDomain, &pszHelpAssistantAccountName);
  76. if( ERROR_SUCCESS == Status )
  77. {
  78. // make sure we don't overwrite buffer, length can't be
  79. // more than 255 characters.
  80. lstrcpyn(
  81. pCredential->UserName,
  82. pszHelpAssistantAccountName,
  83. EXTENDED_USERNAME_LEN
  84. );
  85. lstrcpyn(
  86. pCredential->Domain,
  87. pszHelpAssistantAccountDomain,
  88. EXTENDED_DOMAIN_LEN
  89. );
  90. Status = TSGetHelpAssistantAccountPassword( &pszHelpAssistantPassword );
  91. if( ERROR_SUCCESS == Status )
  92. {
  93. ASSERT( lstrlen(pszHelpAssistantPassword) < EXTENDED_PASSWORD_LEN );
  94. if( lstrlen(pszHelpAssistantPassword) < EXTENDED_PASSWORD_LEN )
  95. {
  96. // Password contains encrypted version, overwrite with
  97. // clear text.
  98. lstrcpy( pCredential->Password, pszHelpAssistantPassword );
  99. }
  100. else
  101. {
  102. Status = STATUS_INVALID_PARAMETER;
  103. }
  104. }
  105. }
  106. }
  107. else
  108. {
  109. ASSERT( FALSE );
  110. Status = STATUS_INVALID_PARAMETER;
  111. }
  112. if( NULL != pszHelpAssistantAccountDomain )
  113. {
  114. LocalFree( pszHelpAssistantAccountDomain );
  115. }
  116. if( NULL != pszHelpAssistantAccountName )
  117. {
  118. LocalFree(pszHelpAssistantAccountName);
  119. }
  120. if( NULL != pszHelpAssistantPassword )
  121. {
  122. LocalFree( pszHelpAssistantPassword );
  123. }
  124. return Status;
  125. }
  126. BOOL
  127. TSIsSessionHelpSession(
  128. PWINSTATION pWinStation,
  129. BOOL* pValid
  130. )
  131. /*++
  132. Routine Description:
  133. Determine if a session is HelpAssistant session.
  134. Parameters:
  135. pWinStation : Pointer to WINSTATION structure.
  136. pValid : Optional Pointer to BOOL to receive status of ticket,
  137. TRUE of ticket is valid, FALSE if ticket is invalid or
  138. help is disabled.
  139. Returns:
  140. TRUE/FALSE Funtion return TRUE even if ticket is invalid, caller
  141. should check pValid to determine if ticket is valid or not.
  142. --*/
  143. {
  144. BOOL bReturn;
  145. BOOL bValidHelpSession = FALSE;
  146. if( NULL == pWinStation )
  147. {
  148. ASSERT( NULL != pWinStation );
  149. SetLastError( ERROR_INVALID_PARAMETER );
  150. bReturn = FALSE;
  151. goto CLEANUPANDEXIT;
  152. }
  153. if( pWinStation->Client.ProtocolType != PROTOCOL_RDP )
  154. {
  155. //
  156. // HelpAssistant is RDP specific and not on console
  157. DBGPRINT( ("TermSrv : HelpAssistant protocol type not RDP \n") );
  158. bValidHelpSession = FALSE;
  159. bReturn = FALSE;
  160. }
  161. else if( WSF_ST_HELPSESSION_NOTHELPSESSION & pWinStation->StateFlags )
  162. {
  163. // We are sure that this session is not HelpAssistant Session.
  164. bReturn = FALSE;
  165. bValidHelpSession = FALSE;
  166. }
  167. else if( WSF_ST_HELPSESSION_HELPSESSIONINVALID & pWinStation->StateFlags )
  168. {
  169. // Help assistant logon but password or ticket ID is invalid
  170. bReturn = TRUE;
  171. bValidHelpSession = FALSE;
  172. }
  173. else if( WSF_ST_HELPSESSION_HELPSESSION & pWinStation->StateFlags )
  174. {
  175. // We are sure this is help assistant logon
  176. bReturn = TRUE;
  177. bValidHelpSession = TRUE;
  178. }
  179. else
  180. {
  181. //
  182. // Clear RA state flags.
  183. //
  184. pWinStation->StateFlags &= ~WSF_ST_HELPSESSION_FLAGS;
  185. if( !pWinStation->Client.UserName[0] || !pWinStation->Client.Password[0] ||
  186. !pWinStation->Client.WorkDirectory[0] )
  187. {
  188. bReturn = FALSE;
  189. bValidHelpSession = FALSE;
  190. pWinStation->StateFlags |= WSF_ST_HELPSESSION_NOTHELPSESSION;
  191. }
  192. else
  193. {
  194. //
  195. // TermSrv might call this routine with data send from client,
  196. // client always send hardcoded SALEMHELPASSISTANTACCOUNT_NAME
  197. //
  198. if( lstrcmpi( pWinStation->Client.UserName, SALEMHELPASSISTANTACCOUNT_NAME ) )
  199. {
  200. bReturn = FALSE;
  201. bValidHelpSession = FALSE;
  202. pWinStation->StateFlags |= WSF_ST_HELPSESSION_NOTHELPSESSION;
  203. goto CLEANUPANDEXIT;
  204. }
  205. //
  206. // this is helpassistant login.
  207. //
  208. bReturn = TRUE;
  209. //
  210. // Check if machine policy restrict help or
  211. // in Help mode, deny access if not.
  212. //
  213. if( FALSE == TSIsMachinePolicyAllowHelp() || FALSE == TSIsMachineInHelpMode() )
  214. {
  215. bValidHelpSession = FALSE;
  216. pWinStation->StateFlags |= WSF_ST_HELPSESSION_HELPSESSIONINVALID;
  217. goto CLEANUPANDEXIT;
  218. }
  219. if( TSVerifyHelpSessionAndLogSalemEvent(pWinStation) )
  220. {
  221. bValidHelpSession = TRUE;
  222. pWinStation->StateFlags |= WSF_ST_HELPSESSION_HELPSESSION;
  223. }
  224. else
  225. {
  226. //
  227. // Either ticket is invalid or expired.
  228. //
  229. bValidHelpSession = FALSE;
  230. pWinStation->StateFlags |= WSF_ST_HELPSESSION_HELPSESSIONINVALID;
  231. }
  232. }
  233. }
  234. CLEANUPANDEXIT:
  235. if( pValid )
  236. {
  237. *pValid = bValidHelpSession;
  238. }
  239. return bReturn;
  240. }
  241. DWORD WINAPI
  242. SalemStartupThreadProc( LPVOID ptr )
  243. /*++
  244. Temporary code to start up Salem sessmgr, post B2 need to move sessmgr into svchost
  245. --*/
  246. {
  247. HRESULT hRes = S_OK;
  248. IRemoteDesktopHelpSessionMgr* pISessMgr = NULL;
  249. if( TSIsMachineInSystemRestore() ) {
  250. // Ignore value if we can restore cached LSA key.
  251. // user can always resend ticket again as in XP.
  252. TSSystemRestoreResetValues();
  253. }
  254. //
  255. // Startup sessmgr if there is outstanding ticket and
  256. // we just rebooted from system restore.
  257. //
  258. if( !TSIsMachineInHelpMode() )
  259. {
  260. ExitThread(hRes);
  261. return hRes;
  262. }
  263. hRes = CoInitialize( NULL );
  264. if( FAILED(hRes) )
  265. {
  266. DBGPRINT( ("TermSrv : TSStartupSalem() CoInitialize() failed with 0x%08x\n", hRes) );
  267. // Failed in COM, return FALSE.
  268. goto CLEANUPANDEXIT;
  269. }
  270. hRes = CoCreateInstance(
  271. CLSID_RemoteDesktopHelpSessionMgr,
  272. NULL,
  273. CLSCTX_ALL,
  274. IID_IRemoteDesktopHelpSessionMgr,
  275. (LPVOID *) &pISessMgr
  276. );
  277. if( FAILED(hRes) || NULL == pISessMgr )
  278. {
  279. DBGPRINT( ("TermSrv : TSStartupSalem() CoCreateInstance() failed with 0x%08x\n", hRes) );
  280. // Can't initialize sessmgr
  281. goto CLEANUPANDEXIT;
  282. }
  283. CLEANUPANDEXIT:
  284. if( NULL != pISessMgr )
  285. {
  286. pISessMgr->Release();
  287. }
  288. CoUninitialize();
  289. ExitThread(hRes);
  290. return hRes;
  291. }
  292. void
  293. TSStartupSalem()
  294. {
  295. HANDLE hThread;
  296. hThread = CreateThread( NULL, 0, SalemStartupThreadProc, NULL, 0, NULL );
  297. if( NULL != hThread )
  298. {
  299. CloseHandle( hThread );
  300. }
  301. return;
  302. }
  303. BOOL
  304. TSVerifyHelpSessionAndLogSalemEvent(
  305. PWINSTATION pWinStation
  306. )
  307. /*++
  308. Description:
  309. Verify help session is a valid, non-expired pending help session,
  310. log an event if help session is invalid.
  311. Parameters:
  312. pWinStation : Point to WINSTATION
  313. Returns:
  314. TRUE/FALSE
  315. Note :
  316. WorkDirectory is HelpSessionID and InitialProgram contains
  317. password to pending help session
  318. --*/
  319. {
  320. HRESULT hRes;
  321. IRemoteDesktopHelpSessionMgr* pISessMgr = NULL;
  322. BOOL bSuccess = FALSE;
  323. BSTR bstrHelpSessId = NULL;
  324. BSTR bstrHelpSessPwd = NULL;
  325. WINSTATIONREMOTEADDRESS winstationRemoteAddress;
  326. DWORD dwReturnLength;
  327. NTSTATUS Status;
  328. BSTR bstrExpertIpAddressFromClient = NULL;
  329. BSTR bstrExpertIpAddressFromServer = NULL;
  330. // only have three strings in this event
  331. BSTR bstrEventStrings[3];
  332. hRes = CoInitialize( NULL );
  333. if( FAILED(hRes) )
  334. {
  335. DBGPRINT( ("TermSrv : TSIsHelpSessionValid() CoInitialize() failed with 0x%08x\n", hRes) );
  336. // Failed in COM, return FALSE.
  337. return FALSE;
  338. }
  339. hRes = CoCreateInstance(
  340. CLSID_RemoteDesktopHelpSessionMgr,
  341. NULL,
  342. CLSCTX_ALL,
  343. IID_IRemoteDesktopHelpSessionMgr,
  344. (LPVOID *) &pISessMgr
  345. );
  346. if( FAILED(hRes) || NULL == pISessMgr )
  347. {
  348. DBGPRINT( ("TermSrv : TSIsHelpSessionValid() CoCreateInstance() failed with 0x%08x\n", hRes) );
  349. // Can't initialize sessmgr
  350. goto CLEANUPANDEXIT;
  351. }
  352. //
  353. // Set the security level to impersonate. This is required by
  354. // the session manager.
  355. //
  356. hRes = CoSetProxyBlanket(
  357. (IUnknown *)pISessMgr,
  358. RPC_C_AUTHN_DEFAULT,
  359. RPC_C_AUTHZ_DEFAULT,
  360. NULL,
  361. RPC_C_AUTHN_LEVEL_DEFAULT,
  362. RPC_C_IMP_LEVEL_IMPERSONATE,
  363. NULL,
  364. EOAC_NONE
  365. );
  366. if( FAILED(hRes) )
  367. {
  368. DBGPRINT( ("TermSrv : TSIsHelpSessionValid() CoSetProxyBlanket() failed with 0x%08x\n", hRes) );
  369. // can't impersonate, return FALSE
  370. goto CLEANUPANDEXIT;
  371. }
  372. bstrHelpSessId = ::SysAllocString(pWinStation->Client.WorkDirectory);
  373. bstrHelpSessPwd = ::SysAllocString(pWinStation->Client.InitialProgram);
  374. if( NULL == bstrHelpSessId || NULL == bstrHelpSessPwd )
  375. {
  376. // We are so out of memory, treat as error
  377. goto CLEANUPANDEXIT;
  378. }
  379. // Verify help session
  380. hRes = pISessMgr->IsValidHelpSession(
  381. bstrHelpSessId,
  382. bstrHelpSessPwd
  383. );
  384. bSuccess = SUCCEEDED(hRes);
  385. if( FALSE == bSuccess )
  386. {
  387. // Log invalid help ticket event here.
  388. Status = xxxQueryRemoteAddress( pWinStation, &winstationRemoteAddress );
  389. bstrExpertIpAddressFromClient = ::SysAllocString( pWinStation->Client.ClientAddress );
  390. if( !NT_SUCCESS(Status) || AF_INET != winstationRemoteAddress.sin_family )
  391. {
  392. //
  393. // we don't support other than IPV4 now or we failed to retrieve address
  394. // from driver, use what's send in from client.
  395. bstrExpertIpAddressFromServer = ::SysAllocString( pWinStation->Client.ClientAddress );
  396. }
  397. else
  398. {
  399. // refer to in_addr structure.
  400. struct in_addr S;
  401. S.S_un.S_addr = winstationRemoteAddress.ipv4.in_addr;
  402. bstrExpertIpAddressFromServer = A2WBSTR( inet_ntoa(S) );
  403. }
  404. if( !bstrExpertIpAddressFromClient || !bstrExpertIpAddressFromServer )
  405. {
  406. // we are out of memory, can't log event.
  407. goto CLEANUPANDEXIT;
  408. }
  409. bstrEventStrings[0] = bstrExpertIpAddressFromClient;
  410. bstrEventStrings[1] = bstrExpertIpAddressFromServer;
  411. bstrEventStrings[2] = bstrHelpSessId;
  412. __LogSalemEvent(
  413. pISessMgr,
  414. EVENTLOG_INFORMATION_TYPE,
  415. REMOTEASSISTANCE_EVENTLOG_TERMSRV_INVALID_TICKET,
  416. 3,
  417. bstrEventStrings
  418. );
  419. }
  420. CLEANUPANDEXIT:
  421. if( NULL != pISessMgr )
  422. {
  423. pISessMgr->Release();
  424. }
  425. if( NULL != bstrHelpSessId )
  426. {
  427. ::SysFreeString( bstrHelpSessId );
  428. }
  429. if( NULL != bstrHelpSessPwd )
  430. {
  431. ::SysFreeString( bstrHelpSessPwd );
  432. }
  433. if( NULL != bstrExpertIpAddressFromClient )
  434. {
  435. ::SysFreeString( bstrExpertIpAddressFromClient );
  436. }
  437. if( NULL != bstrExpertIpAddressFromServer )
  438. {
  439. ::SysFreeString( bstrExpertIpAddressFromServer );
  440. }
  441. DBGPRINT( ("TermSrv : TSIsHelpSessionValid() returns 0x%08x\n", hRes) );
  442. CoUninitialize();
  443. return bSuccess;
  444. }
  445. VOID
  446. TSLogSalemReverseConnection(
  447. PWINSTATION pWinStation,
  448. PICA_STACK_ADDRESS pStackAddress
  449. )
  450. /*++
  451. --*/
  452. {
  453. HRESULT hRes;
  454. IRemoteDesktopHelpSessionMgr* pISessMgr = NULL;
  455. BOOL bSuccess = FALSE;
  456. int index;
  457. // Fours string for this event
  458. BSTR bstrEventStrings[3];
  459. ZeroMemory( bstrEventStrings, sizeof(bstrEventStrings) );
  460. hRes = CoInitialize( NULL );
  461. if( FAILED(hRes) )
  462. {
  463. DBGPRINT( ("TermSrv : TSLogSalemReverseConnection() CoInitialize() failed with 0x%08x\n", hRes) );
  464. goto CLEANUPANDEXIT;
  465. }
  466. hRes = CoCreateInstance(
  467. CLSID_RemoteDesktopHelpSessionMgr,
  468. NULL,
  469. CLSCTX_ALL,
  470. IID_IRemoteDesktopHelpSessionMgr,
  471. (LPVOID *) &pISessMgr
  472. );
  473. if( FAILED(hRes) || NULL == pISessMgr )
  474. {
  475. DBGPRINT( ("TermSrv : TSLogSalemReverseConnection() CoCreateInstance() failed with 0x%08x\n", hRes) );
  476. // Can't initialize sessmgr
  477. goto CLEANUPANDEXIT;
  478. }
  479. //
  480. // Set the security level to impersonate. This is required by
  481. // the session manager.
  482. //
  483. hRes = CoSetProxyBlanket(
  484. (IUnknown *)pISessMgr,
  485. RPC_C_AUTHN_DEFAULT,
  486. RPC_C_AUTHZ_DEFAULT,
  487. NULL,
  488. RPC_C_AUTHN_LEVEL_DEFAULT,
  489. RPC_C_IMP_LEVEL_IMPERSONATE,
  490. NULL,
  491. EOAC_NONE
  492. );
  493. if( FAILED(hRes) )
  494. {
  495. DBGPRINT( ("TermSrv : TSLogSalemReverseConnection() CoSetProxyBlanket() failed with 0x%08x\n", hRes) );
  496. // can't impersonate, return FALSE
  497. goto CLEANUPANDEXIT;
  498. }
  499. //
  500. // sessmgr expect event string in following order
  501. //
  502. // IP address send from client.
  503. // IP address that termsrv connect to, this is part of the expert connect parm.
  504. // Help Session Ticket ID
  505. //
  506. bstrEventStrings[0] = ::SysAllocString( pWinStation->Client.ClientAddress );
  507. {
  508. struct in_addr S;
  509. PTDI_ADDRESS_IP pIpAddress = (PTDI_ADDRESS_IP)&((PCHAR)pStackAddress)[2];
  510. // refer to in_addr structure.
  511. S.S_un.S_addr = pIpAddress->in_addr;
  512. bstrEventStrings[1] = A2WBSTR( inet_ntoa(S) );
  513. }
  514. bstrEventStrings[2] = ::SysAllocString(pWinStation->Client.WorkDirectory);
  515. if( NULL != bstrEventStrings[0] &&
  516. NULL != bstrEventStrings[1] &&
  517. NULL != bstrEventStrings[2] )
  518. {
  519. hRes = __LogSalemEvent(
  520. pISessMgr,
  521. EVENTLOG_INFORMATION_TYPE,
  522. REMOTEASSISTANCE_EVENTLOG_TERMSRV_REVERSE_CONNECT,
  523. 3,
  524. bstrEventStrings
  525. );
  526. }
  527. CLEANUPANDEXIT:
  528. if( NULL != pISessMgr )
  529. {
  530. pISessMgr->Release();
  531. }
  532. for(index=0; index < sizeof(bstrEventStrings)/sizeof(bstrEventStrings[0]); index++)
  533. {
  534. if( !bstrEventStrings[index] )
  535. {
  536. ::SysFreeString( bstrEventStrings[index] );
  537. }
  538. }
  539. DBGPRINT( ("TermSrv : TSLogSalemReverseConnection() returns 0x%08x\n", hRes) );
  540. CoUninitialize();
  541. return;
  542. }
  543. HRESULT
  544. __LogSalemEvent(
  545. IN IRemoteDesktopHelpSessionMgr* pISessMgr,
  546. IN ULONG eventType,
  547. IN ULONG eventCode,
  548. IN int numStrings,
  549. IN BSTR bstrEventStrings[]
  550. )
  551. /*++
  552. Description:
  553. Create a safearray and pass parameters to sessmgr.
  554. Parameters:
  555. Returns:
  556. S_OK or error code.
  557. --*/
  558. {
  559. HRESULT hRes = S_OK;
  560. VARIANT EventStrings;
  561. int index;
  562. // we only have three string to be included in the event log.
  563. SAFEARRAY* psa = NULL;
  564. SAFEARRAYBOUND bounds;
  565. BSTR* bstrArray = NULL;
  566. bounds.cElements = numStrings;
  567. bounds.lLbound = 0;
  568. VariantInit(&EventStrings);
  569. //
  570. // Create a safearray to pass all event string
  571. //
  572. psa = SafeArrayCreate(VT_BSTR, 1, &bounds);
  573. if( NULL == psa )
  574. {
  575. goto CLEANUPANDEXIT;
  576. }
  577. // Required, lock the safe array
  578. hRes = SafeArrayAccessData(psa, (void **)&bstrArray);
  579. if( SUCCEEDED(hRes) )
  580. {
  581. for(index=0; index < numStrings; index++)
  582. {
  583. bstrArray[index] = bstrEventStrings[index];
  584. }
  585. EventStrings.vt = VT_ARRAY | VT_BSTR;
  586. EventStrings.parray = psa;
  587. hRes = pISessMgr->LogSalemEvent(
  588. eventType,
  589. eventCode,
  590. &EventStrings
  591. );
  592. //
  593. // make sure we clear BSTR array or VariantClear() will invoke
  594. // SafeArrayDestroy() which in term will invoke ::SysFreeString()
  595. // on each BSTR.
  596. //
  597. for(index=0; index < numStrings; index++)
  598. {
  599. bstrArray[index] = NULL;
  600. }
  601. hRes = SafeArrayUnaccessData( psa );
  602. ASSERT( SUCCEEDED(hRes) );
  603. // make sure we don't destroy safe array twice, VariantClear()
  604. // will destroy it.
  605. psa = NULL;
  606. }
  607. CLEANUPANDEXIT:
  608. hRes = VariantClear(&EventStrings);
  609. ASSERT( SUCCEEDED(hRes) );
  610. if( psa != NULL )
  611. {
  612. SafeArrayDestroy(psa);
  613. }
  614. return hRes;
  615. }
  616. HRESULT
  617. TSRemoteAssistancePrepareSystemRestore()
  618. /*++
  619. Routine Description:
  620. Prepare system for RA specific system restore, this includes RA specific encryption key,
  621. registry settings that we need preserve.
  622. Parameters:
  623. None.
  624. Returns:
  625. S_OK or error code.
  626. --*/
  627. {
  628. // Here we have different implementation for XPSP1 and .NET, on .NET, all Salem related
  629. // stuff goes into sessmgr, that is this function will invoke sessmgr's necessay method to
  630. // deal with system restore; however, SP1 installer does not kick off same OCMANAGER setup
  631. // and we will also have to worry about SP1 uinstall issue. When merging two tree on longhorn
  632. // we need to take .NET approach.
  633. return TSSystemRestoreCacheValues();
  634. }