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.

1031 lines
21 KiB

  1. #ifndef _TAPI_OBJECT_WITH_SITE_H_
  2. #define _TAPI_OBJECT_WITH_SITE_H_
  3. /*++
  4. Copyright (c) 1999 Microsoft Corporation
  5. Module Name:
  6. ObjectWithSite.h
  7. Abstract:
  8. The implementation of IObjectWithSite interface that allows
  9. for per-page persistent data to be stored in registry or as
  10. a cookie.
  11. --*/
  12. #include <Mshtml.h>
  13. #include <Wininet.h>
  14. //
  15. // this url is used to construct the URL for cookies -- a security measure
  16. // so a script applet cannot drop a cookie with the same name and data
  17. // and fool us into thinking it is our cookie
  18. //
  19. static const TCHAR gszHardCodedURL[] = _T("http://www.microsoft.com/");
  20. //
  21. // the expiration date is needed to make the cookie persistent
  22. //
  23. static const TCHAR gszCookieData[] =
  24. _T("6; expires = Sat, 12-Sep-2099 00:00:00 GMT");
  25. //
  26. // dummy suffix to be appended to the url string
  27. //
  28. static const TCHAR gszURLSuffix[] =
  29. _T("/url");
  30. class CObjectWithSite : public IObjectWithSite
  31. {
  32. public:
  33. //
  34. // current validation level. used to determine whether the page is safe,
  35. // unsafe, or whether information from the user is needed
  36. //
  37. enum EnValidation { VALIDATED_SAFE, VALIDATED_SAFE_PERMANENT, VALIDATED_UNSAFE, UNVALIDATED };
  38. public:
  39. //
  40. // store type
  41. //
  42. enum EnMechanism { COOKIES, REGISTRY };
  43. CObjectWithSite(TCHAR const *pszStorageName)
  44. :m_pszURL(NULL),
  45. m_dwSecurityZone(URLZONE_UNTRUSTED),
  46. m_pUnkSite(NULL),
  47. m_pszStorageName(NULL)
  48. {
  49. SetStorageName(pszStorageName);
  50. }
  51. ~CObjectWithSite()
  52. {
  53. if (m_pszURL)
  54. {
  55. delete m_pszURL;
  56. m_pszURL = NULL;
  57. }
  58. if (m_pUnkSite)
  59. {
  60. m_pUnkSite->Release();
  61. m_pUnkSite = NULL;
  62. }
  63. if (m_pszStorageName)
  64. {
  65. delete m_pszStorageName;
  66. m_pszStorageName = NULL;
  67. }
  68. }
  69. ////////////////////////////
  70. //
  71. // IObjectWithSite methods
  72. STDMETHOD(SetSite)(IUnknown *pUnkSite)
  73. {
  74. if ((NULL != pUnkSite) && IsBadCodePtr((FARPROC)pUnkSite))
  75. {
  76. return E_POINTER;
  77. }
  78. s_ObjectWithSiteCritSection.Lock();
  79. //
  80. // we are moving away from a page. this is the new page, as far as
  81. // validation logic is concerned, so invalidate the current page
  82. //
  83. if (NULL == pUnkSite)
  84. {
  85. Validate(UNVALIDATED);
  86. }
  87. //
  88. // Get URL and zone information for this site
  89. //
  90. //
  91. // Note: we could delay this until we are actually asked for
  92. // zone or URL, but this should not be a performance bottlneck
  93. // in our case, so do this now to keep the code simple.
  94. StoreURLAndZone(pUnkSite);
  95. //
  96. // replace the current site pointer with the new one
  97. //
  98. if (m_pUnkSite)
  99. {
  100. m_pUnkSite->Release();
  101. }
  102. m_pUnkSite = pUnkSite;
  103. if (m_pUnkSite)
  104. {
  105. m_pUnkSite->AddRef();
  106. }
  107. s_ObjectWithSiteCritSection.Unlock();
  108. return S_OK;
  109. }
  110. STDMETHOD(GetSite)(REFIID riid, void **ppSite)
  111. {
  112. HRESULT hr = E_POINTER;
  113. if (!IsBadWritePtr(ppSite, sizeof(void*)))
  114. {
  115. s_ObjectWithSiteCritSection.Lock();
  116. *ppSite = NULL;
  117. if (m_pUnkSite)
  118. {
  119. hr = m_pUnkSite->QueryInterface(riid, ppSite);
  120. }
  121. else
  122. {
  123. hr = E_FAIL;
  124. }
  125. s_ObjectWithSiteCritSection.Unlock();
  126. }
  127. return hr;
  128. }
  129. //
  130. // has this page been validated?
  131. //
  132. EnValidation GetValidation()
  133. {
  134. //
  135. // if the page has not been validated, see if it is marked as safe
  136. //
  137. s_ObjectWithSiteCritSection.Lock();
  138. if (UNVALIDATED == s_enValidation)
  139. {
  140. if (IsPageSafe())
  141. {
  142. s_enValidation = VALIDATED_SAFE;
  143. }
  144. }
  145. EnValidation enValidation = s_enValidation;
  146. s_ObjectWithSiteCritSection.Unlock();
  147. return enValidation;
  148. }
  149. //
  150. // validate page as safe, unsafe, or reset validation
  151. //
  152. EnValidation Validate(EnValidation enNewValidation)
  153. {
  154. s_ObjectWithSiteCritSection.Lock();
  155. //
  156. // keep the validation before the change
  157. //
  158. EnValidation enOldValidation = s_enValidation;
  159. //
  160. // safe permanent is a special case:
  161. //
  162. if (VALIDATED_SAFE_PERMANENT == enNewValidation)
  163. {
  164. //
  165. // set persistent safety flag and
  166. // validate page as safe
  167. //
  168. MarkPageAsSafe();
  169. enNewValidation = VALIDATED_SAFE;
  170. }
  171. //
  172. // change our validation level for this page
  173. //
  174. s_enValidation = enNewValidation;
  175. s_ObjectWithSiteCritSection.Unlock();
  176. return enOldValidation;
  177. }
  178. BOOL IsIntranet()
  179. {
  180. //
  181. // if anything other that intranet assume internet -- a less secure zone
  182. //
  183. s_ObjectWithSiteCritSection.Lock();
  184. BOOL bIntranet = ( m_dwSecurityZone == URLZONE_INTRANET );
  185. s_ObjectWithSiteCritSection.Unlock();
  186. return bIntranet;
  187. }
  188. ////////////////////
  189. //
  190. // HaveSite()
  191. //
  192. // return true if we have a site pointer
  193. //
  194. BOOL HaveSite()
  195. {
  196. s_ObjectWithSiteCritSection.Lock();
  197. BOOL bHaveSite = FALSE;
  198. if (NULL != m_pUnkSite)
  199. {
  200. bHaveSite = TRUE;
  201. }
  202. s_ObjectWithSiteCritSection.Unlock();
  203. return bHaveSite;
  204. }
  205. private:
  206. ////////////////////////////
  207. //
  208. // store the current url in the "safe" list
  209. //
  210. //
  211. // not thread safe, called from inside a lock
  212. //
  213. HRESULT MarkPageAsSafe(EnMechanism enMechanism = COOKIES)
  214. {
  215. //
  216. // if storage is invalid, the object has not been properly initialized
  217. //
  218. if (IsBadStringPtr(m_pszStorageName, -1))
  219. {
  220. return E_UNEXPECTED;
  221. }
  222. //
  223. // is we don't have the url, can't do what we are asked
  224. //
  225. if (NULL == m_pszURL)
  226. {
  227. return S_FALSE;
  228. }
  229. //
  230. // if url is garbage, we have a problem
  231. //
  232. if ( IsBadStringPtr(m_pszURL, -1) )
  233. {
  234. return E_FAIL;
  235. }
  236. HRESULT hr = E_FAIL;
  237. switch (enMechanism)
  238. {
  239. case REGISTRY:
  240. hr = MarkPageSafeInRegistry(m_pszStorageName);
  241. break;
  242. case COOKIES:
  243. hr = MarkPageSafeCookie(m_pszStorageName);
  244. break;
  245. default:
  246. break;
  247. }
  248. return hr;
  249. }
  250. //
  251. // Returns TRUE if the current page is in the safe list
  252. //
  253. //
  254. // not thread safe, called from inside a lock
  255. //
  256. BOOL IsPageSafe( EnMechanism enMechanism = COOKIES )
  257. {
  258. //
  259. // if we cannot get safety marking for whatever reason,
  260. // return false
  261. //
  262. _ASSERTE(NULL != m_pszStorageName);
  263. if ( IsBadStringPtr(m_pszURL, -1) ||
  264. IsBadStringPtr(m_pszStorageName, -1))
  265. {
  266. return FALSE;
  267. }
  268. BOOL bSafe = FALSE;
  269. switch (enMechanism)
  270. {
  271. case REGISTRY:
  272. bSafe = IsPageSafeRegistry(m_pszStorageName);
  273. break;
  274. case COOKIES:
  275. bSafe = IsPageSafeCookie(m_pszStorageName);
  276. break;
  277. default:
  278. break;
  279. }
  280. return bSafe;
  281. }
  282. private:
  283. //
  284. // this method is only called from the constructor. not thread safe.
  285. //
  286. HRESULT SetStorageName(TCHAR const *pszStorageName)
  287. {
  288. //
  289. // calling this method invalidates the old storage name
  290. // so deallocate it before doing anything else
  291. //
  292. if (NULL != m_pszStorageName)
  293. {
  294. delete m_pszStorageName;
  295. m_pszStorageName = NULL;
  296. }
  297. //
  298. // argument must be valid
  299. //
  300. if (IsBadStringPtr(pszStorageName, -1))
  301. {
  302. return E_POINTER;
  303. }
  304. //
  305. // allocate buffer for the new storage name
  306. //
  307. size_t nSize = _tcsclen(pszStorageName) + 1;
  308. m_pszStorageName = new TCHAR[nSize];
  309. if (NULL == m_pszStorageName)
  310. {
  311. return E_OUTOFMEMORY;
  312. }
  313. _tcscpy(m_pszStorageName, pszStorageName);
  314. return S_OK;
  315. }
  316. //
  317. // cache the url string and security zone id
  318. // not thread safe must be called from inside a lock
  319. //
  320. HRESULT StoreURLAndZone(IUnknown *pUnkSite)
  321. {
  322. //
  323. // reset zone and deallocate URL, if it exists
  324. //
  325. m_dwSecurityZone = URLZONE_UNTRUSTED;
  326. if (m_pszURL)
  327. {
  328. delete m_pszURL;
  329. m_pszURL = NULL;
  330. }
  331. if (pUnkSite == NULL)
  332. {
  333. return S_OK;
  334. }
  335. //
  336. // use pUnkSite to get to IHTMLDocument2, which will give us the URL
  337. //
  338. //
  339. // these interfaces need to be released on exit.
  340. // smart pointers will do exactly what we need
  341. //
  342. HRESULT hr = E_FAIL;
  343. CComPtr<IOleClientSite> pSite;
  344. if (FAILED(hr = pUnkSite->QueryInterface(IID_IOleClientSite, (LPVOID *) &pSite)))
  345. {
  346. return hr;
  347. }
  348. CComPtr<IOleContainer> pOleCtr;
  349. if (FAILED(hr = pSite->GetContainer(&pOleCtr)))
  350. {
  351. return hr;
  352. }
  353. CComPtr<IHTMLDocument2> pDoc;
  354. if (FAILED(hr = pOleCtr->QueryInterface(IID_IHTMLDocument2, (LPVOID *) &pDoc)))
  355. {
  356. return hr;
  357. }
  358. //
  359. // get and keep the url
  360. //
  361. BSTR bstrURL;
  362. if (FAILED(hr = pDoc->get_URL(&bstrURL)))
  363. {
  364. return hr;
  365. }
  366. UINT nURLLength = SysStringLen(bstrURL) + 1;
  367. _ASSERTE(NULL == m_pszURL);
  368. m_pszURL = new TCHAR[nURLLength];
  369. if (NULL == m_pszURL)
  370. {
  371. SysFreeString(bstrURL);
  372. return E_OUTOFMEMORY;
  373. }
  374. #ifdef _UNICODE
  375. _tcscpy(m_pszURL, bstrURL);
  376. #else
  377. int r = WideCharToMultiByte(
  378. CP_ACP,
  379. 0,
  380. bstrURL,
  381. nURLLength,
  382. m_pszURL,
  383. nURLLength,
  384. NULL,
  385. NULL );
  386. if (0 == r)
  387. {
  388. SysFreeString(bstrURL);
  389. delete m_pszURL;
  390. m_pszURL = NULL;
  391. return E_FAIL;
  392. }
  393. #endif
  394. //
  395. // whatever follows '#' and '?' is "extra info" and is not considered
  396. // to be a part of the actual URL by Internet(Set/Get)Coookie. Extra
  397. // Info has no value for us -- so throw it out
  398. //
  399. TCHAR *psDelimiter = _tcsstr(m_pszURL, _T("#"));
  400. if (NULL != psDelimiter)
  401. {
  402. *psDelimiter = _T('\0');
  403. }
  404. psDelimiter = _tcsstr(m_pszURL, _T("?"));
  405. if (NULL != psDelimiter)
  406. {
  407. *psDelimiter = _T('\0');
  408. }
  409. //
  410. // at this point we cached the URL
  411. // now attempt to get the security zone. if we fail getting zone
  412. // information still keep the url.
  413. //
  414. //
  415. // Get security zone
  416. //
  417. CComPtr<IInternetSecurityManager> pSecMgr;
  418. hr = CoCreateInstance(CLSID_InternetSecurityManager,
  419. NULL,
  420. CLSCTX_INPROC_SERVER,
  421. IID_IInternetSecurityManager,
  422. (LPVOID *) &pSecMgr);
  423. if (pSecMgr == NULL)
  424. {
  425. SysFreeString(bstrURL);
  426. return hr;
  427. }
  428. hr = pSecMgr->MapUrlToZone(bstrURL, &m_dwSecurityZone, 0);
  429. //
  430. // if failed, reset url to untrusted, just in case
  431. //
  432. if ( FAILED(hr) )
  433. {
  434. m_dwSecurityZone = URLZONE_UNTRUSTED;
  435. }
  436. SysFreeString(bstrURL);
  437. //
  438. // we should have at least the URL at this point
  439. //
  440. return S_OK;
  441. }
  442. //
  443. // drop a cookie for this page as an indicator that this page is safe
  444. //
  445. HRESULT MarkPageSafeCookie(TCHAR const *pszCookieName)
  446. {
  447. TCHAR *pszURL = NULL;
  448. //
  449. // generate the url for the cookie
  450. // remember to delete the returned string
  451. //
  452. GenerateURLString(&pszURL);
  453. if (NULL == pszURL)
  454. return E_OUTOFMEMORY;
  455. BOOL bReturn = InternetSetCookie(pszURL, pszCookieName, gszCookieData);
  456. delete pszURL;
  457. return (bReturn)?S_OK:E_FAIL;
  458. }
  459. //
  460. // presence of a cookie for this page is an indicator that it's safe
  461. // returns TRUE if the cookie exists. FALSE otherwise
  462. //
  463. BOOL IsPageSafeCookie(TCHAR const *pszCookieName)
  464. {
  465. //
  466. // m_pszURL was checked by the calling function and the object
  467. // is protected. m_pszURL should never be null here.
  468. //
  469. _ASSERTE(m_pszURL);
  470. //
  471. // same goes for pszCookieName
  472. //
  473. _ASSERTE(pszCookieName);
  474. BOOL bReturn = FALSE;
  475. BOOL bFinalReturn = FALSE;
  476. TCHAR *pszURL = NULL;
  477. // remember to delete the returned string
  478. GenerateURLString(&pszURL);
  479. if (NULL == pszURL)
  480. {
  481. return FALSE;
  482. }
  483. //
  484. // see how much data the cookie contains
  485. //
  486. DWORD dwCookieDataSize = 0;
  487. //
  488. // assuming the return code is TRUE if the method succeeds in getting
  489. // get the buffer size. the current documentation is not 100% clear
  490. //
  491. bReturn = InternetGetCookie(pszURL, pszCookieName, NULL, &dwCookieDataSize);
  492. //
  493. // dwCookieDataSize has the length of cookie data
  494. //
  495. if ( bReturn && dwCookieDataSize )
  496. {
  497. //
  498. // allocate the buffer for cookie data
  499. //
  500. TCHAR *pCookieDataBuffer = new TCHAR[dwCookieDataSize];
  501. if (NULL != pCookieDataBuffer)
  502. {
  503. //
  504. // all cookies for this page are returned in cookie data,
  505. // the name argument is ignored
  506. //
  507. bReturn = InternetGetCookie(pszURL,
  508. pszCookieName,
  509. pCookieDataBuffer,
  510. &dwCookieDataSize);
  511. //
  512. // is succeeded, parse cookie data buffer to see if the
  513. // cookie we are looking for is there
  514. //
  515. if ( bReturn && ( NULL != _tcsstr(pCookieDataBuffer, pszCookieName) ) )
  516. {
  517. bFinalReturn = TRUE;
  518. }
  519. delete pCookieDataBuffer;
  520. pCookieDataBuffer = NULL;
  521. }
  522. }
  523. delete pszURL;
  524. pszURL = NULL;
  525. return bFinalReturn;
  526. }
  527. //
  528. // add a registry entry for this page as an indicator that the page is safe
  529. // returns TRUE if the registry entry exists
  530. //
  531. HRESULT MarkPageSafeInRegistry(TCHAR const *szRegistryKeyName)
  532. {
  533. _ASSERTE(m_pszURL);
  534. //
  535. // open the registry key. create if not there
  536. //
  537. DWORD dwDisposition = 0;
  538. HKEY hKey = 0;
  539. LONG rc = RegCreateKeyEx(HKEY_CURRENT_USER,
  540. szRegistryKeyName,
  541. 0,
  542. NULL,
  543. REG_OPTION_NON_VOLATILE,
  544. KEY_ALL_ACCESS,
  545. NULL,
  546. &hKey,
  547. &dwDisposition);
  548. if ( rc == ERROR_SUCCESS )
  549. {
  550. DWORD dwData = 0;
  551. //
  552. // add the current URL to the registry
  553. //
  554. rc = RegSetValueEx(hKey,
  555. m_pszURL,
  556. 0,
  557. REG_DWORD,
  558. (BYTE*)&dwData,
  559. sizeof(DWORD));
  560. }
  561. if (hKey)
  562. {
  563. RegCloseKey(hKey);
  564. }
  565. hKey = NULL;
  566. if (rc == ERROR_SUCCESS )
  567. {
  568. return S_OK;
  569. }
  570. else
  571. {
  572. return E_FAIL;
  573. }
  574. }
  575. //
  576. // presence of a registry entry for this page indicates that the
  577. // page is safe
  578. //
  579. BOOL IsPageSafeRegistry(TCHAR const *szRegistryKeyName)
  580. {
  581. DWORD dwDisposition = 0;
  582. HKEY hKey = 0;
  583. //
  584. // the default is not safe
  585. //
  586. if (NULL == m_pszURL)
  587. {
  588. return FALSE;
  589. }
  590. //
  591. // open the registry key where the page information is kept.
  592. // create if not there
  593. //
  594. LONG rc = RegCreateKeyEx(HKEY_CURRENT_USER,
  595. szRegistryKeyName,
  596. 0,
  597. NULL,
  598. REG_OPTION_NON_VOLATILE,
  599. KEY_CREATE_SUB_KEY | KEY_READ,
  600. NULL,
  601. &hKey,
  602. &dwDisposition);
  603. if ( rc == ERROR_SUCCESS )
  604. {
  605. DWORD dwDataType = 0;
  606. DWORD dwDataSize = 0;
  607. //
  608. // read the setting for the current page.
  609. // Note: we don't need the actual data, we just
  610. // want to see if the value exists
  611. //
  612. rc = RegQueryValueEx(hKey,
  613. m_pszURL,
  614. 0,
  615. &dwDataType,
  616. NULL,
  617. &dwDataSize
  618. );
  619. }
  620. if (hKey)
  621. {
  622. RegCloseKey(hKey);
  623. }
  624. hKey = NULL;
  625. return (rc == ERROR_SUCCESS);
  626. }
  627. //
  628. // build the URL string based on the hardcoded URL and
  629. // the actual URL for this page.
  630. // we are hoping that the striing will be unique (per page) and no
  631. // mischevious scripting app can drop a cookie corresponding to
  632. // this URL
  633. //
  634. // Note: if the implementation of of Internet(Set/Get)Cookie changes
  635. // to have stricter validation for the URL string, this technique will
  636. // not work
  637. //
  638. void GenerateURLString(TCHAR **ppszURL)
  639. {
  640. //
  641. // the precondition is that m_pszURL exists
  642. //
  643. _ASSERT(NULL != m_pszURL);
  644. *ppszURL = NULL;
  645. //
  646. // alias the char pointer pointer to by *pszURL.
  647. // so it is easier to refer to.
  648. //
  649. TCHAR* &pszURL = *ppszURL;
  650. //
  651. // allocate memory for concatenated string
  652. //
  653. pszURL = new TCHAR[_tcslen(gszHardCodedURL) +
  654. _tcslen(m_pszURL) +
  655. _tcslen(gszURLSuffix) + 1];
  656. // concatenate
  657. if (pszURL)
  658. {
  659. *pszURL = _T('\0');
  660. _tcscat(pszURL, gszHardCodedURL);
  661. _tcscat(pszURL, m_pszURL);
  662. _tcscat(pszURL, gszURLSuffix);
  663. }
  664. }
  665. private:
  666. //
  667. // cached URL string
  668. //
  669. TCHAR *m_pszURL;
  670. //
  671. // cached security zone
  672. //
  673. DWORD m_dwSecurityZone;
  674. //
  675. // site for IObjectWithSite
  676. //
  677. IUnknown *m_pUnkSite;
  678. //
  679. // thread safety
  680. //
  681. static CComAutoCriticalSection s_ObjectWithSiteCritSection;
  682. //
  683. // the status of the current page
  684. //
  685. static EnValidation s_enValidation;
  686. //
  687. // name of the persistent cookie or registry key
  688. //
  689. TCHAR *m_pszStorageName;
  690. };
  691. #endif // _TAPI_OBJECT_WITH_SITE_H_