blob: 7449fcf3fbd5e689aa8198adcba2f5b0fc658557 [file] [log] [blame]
// Copyright 2007-2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
// note: this file is originally from Microsoft for SiteLock, but has been
// modified to support getting the browser URL via NPAPI so we can use this
// for validating within Firefox and other browsers as well
#ifndef OMAHA_PLUGINS_SITELOCK_H__
#define OMAHA_PLUGINS_SITELOCK_H__
///////////////////////////////////////////////////////////////////////////////
// SiteLock 1.14
// ATL sample code for restricting activation of ActiveX controls.
// Copyright Microsoft Corporation. All rights reserved.
// Last updated: July 19 2007
// Includes
#include <atlctl.h>
#include <comdef.h>
#include <shlguid.h>
#include <dispex.h> // IObjectIdentity
// Pragmas
#pragma once
#include "common/logging.h"
// Version information
#define SITELOCK_VERSION 0x00010014 // 1.14
#ifdef SITELOCK_SUPPORT_DOWNLOAD
#pragma message("sitelock.h : This version of SiteLock " \
"does not support downloading.")
#endif
// Overview:
// To enable scripting, developers must declare their ActiveX controls as
// "safe for scripting".
// This is done by implementing interface IObjectSafety, which comes with very
// important safety assumptions. Once marked as "safe for scripting", ActiveX
// controls may be activated by untrusted web sites. Therefore "safe for
// scripting" controls must guarantee all their methods are safe regardless of
// the activation context. Practically however, it may not be possible for an
// ActiveX control to guarantee safety in all activation contexts. The site lock
// framework allows developers to specify which zones and domains can
// instantiate ActiveX controls. For example, this may allow a developer
// to implement methods that can only be called if the activation context is the
// intranet zone.
// Usage:
// 1/ Include current header file sitelock.h (after including ATL header files).
//
// 2/ Derive from "public IObjectSafetySiteLockImpl
// <CYourClass, INTERFACESAFE_FOR...>,".
// This replaces the default IObjectSafetyImpl interface implementation.
//
// 3/ Add the following to your control's COM map:
// COM_INTERFACE_ENTRY(IObjectSafety)
// COM_INTERFACE_ENTRY(IObjectSafetySiteLock)
//
// 4/ Add one of the following to specify allowed activation contexts:
// a) A public (or friend) member variable, for example:
// const CYourClass::SiteList CYourClass::rgslTrustedSites[6] =
// {{ SiteList::Deny, L"http", L"users.microsoft.com" },
// { SiteList::Allow, L"http", L"microsoft com" },
// { SiteList::Allow, L"http", SITELOCK_INTRANET_ZONE },
// { SiteList::Deny, L"https", L"users.microsoft.com" },
// { SiteList::Allow, L"https", L"microsoft.com" },
// { SiteList::Allow, L"https", SITELOCK_INTRANET_ZONE }};
//
// b) A set of site lock macros, for example:
// #define SITELOCK_USE_MAP (prior to including sitelock.h)
// BEGIN_SITELOCK_MAP()
// SITELOCK_DENYHTTP ( L"users.microsoft.com" )
// SITELOCK_ALLOWHTTP ( L"microsoft com" )
// SITELOCK_ALLOWHTTP ( SITELOCK_INTRANET_ZONE )
// SITELOCK_DENYHTTPS ( L"users.microsoft.com" )
// SITELOCK_ALLOWHTTPS ( L"microsoft.com" )
// SITELOCK_ALLOWHTTPS ( SITELOCK_INTRANET_ZONE )
// END_SITELOCK_MAP()
//
// The examples above block "*.users.microsoft.com" sites (http and https).
// The examples above allow "*.microsoft.com" sites (http and https).
// The examples above allow intranet sites (http and https).
//
// 5/ Choose an expiry lifespan:
// You can specify the lifespan of your control one of two ways.
// By declaring an enumeration (slightly more efficient):
// enum { dwControlLifespan = (lifespan in days) };
// By declaring a member variable:
// static const DWORD dwControlLifespan = (lifespan in days);
// When in doubt, choose a shorter duration rather than a longer one.
// Expiration can be disabled by adding #define SITELOCK_NO_EXPIRY before
// including sitelock.h.
//
// 6/ Implement IObjectWithSite or IOleObject:
// IObjectWithSite is a lightweight interface able to indicate the
// activation URL to site lock.
// IOleObject is a heavier interface providing additional OLE capabilities.
// If you need IOleObject, add #define SITELOCK_USE_IOLEOBJECT before
// including sitelock.h.
// Otherwise, simply implement IObjectWithSite:
// - Derive from "IObjectWithSiteImpl<CYourClass>".
// - Add COM_INTERFACE_ENTRY(IObjectWithSite) to your control's COM map.
// You should never implement both IObjectWithSite and IOleObject.
//
// 7/ Link with urlmon.lib.
// Detailed usage:
// --- Entries ---:
// Site lock entries are defined by the following elements:
// iAllowType is:
// SiteList::Allow: allowed location
// SiteList::Deny: blocked location
// szScheme is:
// L"http": non-SSL location
// L"https": SSL-enabled location
// Other: in rare cases, the scheme may be outlook:, ms-help:, etc.
// szDomain is:
// Doman: a string defining a domain
// Zone: a constant specifying a zone
//
// --- Ordering ---:
// Entries are matched in the order they appear in.
// The first entry that matches will be accepted.
// Deny entries should therefore be placed before allow entries.
//
// --- Protocols ---:
// To support multiple protocols (http and https), define separate entries.
//
// --- Domain names ---:
// This sample code performs a case-sensitive comparison after domain
// normalization. Whether domain normalization converts strings to lower
// case depends on the scheme provider.
//
// If a domain does not contain any special indicator, only domains with the
// right suffix will match. For example:
// An entry of "microsoft.com" will match "microsoft.com".
// An entry of "microsoft.com" will match "office.microsoft.com"
// An entry of "microsoft.com" will not match "mymicrosoft.com"
// An entry of "microsoft.com" will not match "www.microsoft.com.hacker.com"
//
// If a domain begins with "*.", only child domains will match.
// For example:
// An entry of "*.microsoft.com" will match "foo.microsoft.com".
// An entry of "*.microsoft.com" will not match "microsoft.com".
//
// If a domain begins with "=", only the specified domain will match.
// For example:
// An entry of "=microsoft.com" will match "microsoft.com".
// An entry of "=microsoft.com" will not match "foo.microsoft.com".
//
// If a domain is set to "*", all domains will match.
// This is useful to only restrict to specific schemes (ex: http vs. https).
//
// If a domain name is NULL, then the scheme provider should return an error
// when asking for the domain. This is appropriate for protocols
// (outlook: or ms-help:) that do not use server names.
//
// If a domain name is SITELOCK_INTRANET_ZONE, then any server in the
// Intranet zone will match. Due to a zone limitation, sites in the user's
// Trusted Sites list will also match. However, since Trusted Sites
// typically permit downloading and running of unsigned, unsafe controls,
// security is limited for those sites anyway.
//
// If a domain name is SITELOCK_MYCOMPUTER_ZONE, then any page residing on
// the user's local machine will match.
//
// If a domain name is SITELOCK_TRUSTED_ZONE, then any page residing in the
// user's Trusted Sites list will match.
// Language checks
#ifndef __cplusplus
#error ATL Requires C++
#endif
// Windows constants
#if (WINVER < 0x0600)
#define IDN_USE_STD3_ASCII_RULES 0x02 // Enforce STD3 ASCII restrictions
#endif
// Function prototypes
typedef int (WINAPI * PFN_IdnToAscii)(DWORD, LPCWSTR, int, LPWSTR, int);
// Macros
#ifndef cElements
template<typename T> static char cElementsVerify(void const *, T) throw() {
return 0;
}
template<typename T> static void cElementsVerify(T *const, T *const *) throw() {
};
#define cElements(arr) (sizeof(cElementsVerify(arr, &(arr))) * \
(sizeof(arr)/sizeof(*(arr))))
#endif
// Restrictions
#define SITELOCK_INTRANET_ZONE ((const OLECHAR *)-1)
#define SITELOCK_MYCOMPUTER_ZONE ((const OLECHAR *)-2)
#define SITELOCK_TRUSTED_ZONE ((const OLECHAR *)-3)
#ifndef SITELOCK_NO_EXPIRY
// Helper functions for expiry
#if defined(_WIN64) && defined(_M_IA64)
#pragma section(".base", long, read, write) // NO_LINT
extern "C" __declspec(allocate(".base")) extern IMAGE_DOS_HEADER __ImageBase;
#else
extern "C" IMAGE_DOS_HEADER __ImageBase;
#endif
#define ImageNtHeaders(pBase) ((PIMAGE_NT_HEADERS)((PCHAR)(pBase) + \
((PIMAGE_DOS_HEADER)(pBase))->e_lfanew))
#define LODWORD(_qw) ((DWORD)(_qw))
#define HIDWORD(_qw) ((DWORD)(((_qw) >> 32) & 0xffffffff))
inline void _UNIXTimeToFILETIME(time_t t, LPFILETIME ft) {
// The time_t is a 32-bit value for the number of seconds since
// January 1, 1970.
// A FILETIME is a 64-bit for the number of 100-nanosecond periods since
// January 1, 1601.
// Convert by multiplying the time_t value by 1e+7 to get to the same base
// granularity, then add the numeric equivalent of January 1, 1970 as
// FILETIME.
ULONGLONG qw = ((ULONGLONG)t * 10000000ui64) + 116444736000000000ui64;
ft->dwHighDateTime = HIDWORD(qw);
ft->dwLowDateTime = LODWORD(qw);
}
inline time_t _FILETIMEToUNIXTime(LPFILETIME ft) {
ULONGLONG qw = (((ULONGLONG)ft->dwHighDateTime)<<32) + ft->dwLowDateTime;
return (time_t)((qw - 116444736000000000ui64) / 10000000ui64);
}
#endif // SITELOCK_NO_EXPIRY
// Interface declaring "safe for scripting" methods with additional site lock
// capabilities
class __declspec(uuid("7FEB54AE-E3F9-40FC-AB5A-28A545C0F193"))
ATL_NO_VTABLE IObjectSafetySiteLock : public IObjectSafety {
public:
// Site lock entry definition
struct SiteList {
enum SiteListCategory {
Allow, // permit
Deny, // disallow
Download // OBSOLETE, do not use
} iAllowType;
const OLECHAR* szScheme; // scheme (http or https)
const OLECHAR* szDomain; // domain
};
// Capability definition
enum Capability {
CanDownload = 0x00000001, // OBSOLETE. Here for backwards compat only.
UsesIOleObject = 0x00000002, // Use IOleObject instead of IObjectWithSite.
HasExpiry = 0x00000004, // Control will expire when lifespan elapsed.
};
// Returns capabilities (this can be used by testing tools to query for
// custom capabilities or version information)
STDMETHOD(GetCapabilities)(DWORD* pdwCapability) = 0;
// Returns site lock entries controlling activation
STDMETHOD(GetApprovedSites)(const SiteList** pSiteList, DWORD* cSites) = 0;
// Returns lifespan as number of days and date (version 1.05 or higher)
STDMETHOD(GetExpiryDate)(DWORD* pdwLifespan, FILETIME* pExpiryDate) = 0;
};
inline LPCTSTR const UrlZoneToString(DWORD dwZone) {
switch (dwZone) {
case URLZONE_LOCAL_MACHINE: return _T("URLZONE_LOCAL_MACHINE");
case URLZONE_INTRANET: return _T("URLZONE_INTRANET");
case URLZONE_TRUSTED: return _T("URLZONE_TRUSTED");
case URLZONE_INTERNET: return _T("URLZONE_INTERNET");
case URLZONE_UNTRUSTED: return _T("URLZONE_UNTRUSTED");
default: return _T("URLZONE_UNKNOWN");
}
}
bool AreObjectsEqual(IDispatch* disp1, IDispatch* disp2) {
// if the arguments are equal then the objects are equal
if (disp1 == disp2) return true;
// if the arguments are not equal, then compare the IUnknowns
if (disp1 && disp2) {
CComPtr<IUnknown> unk1;
CComPtr<IUnknown> unk2;
// This must always succeed.
VERIFY1(SUCCEEDED(disp1->QueryInterface(&unk1)));
VERIFY1(SUCCEEDED(disp2->QueryInterface(&unk2)));
ASSERT1(unk1 && unk2);
if (unk1 == unk2) return true;
// Not all the hope is lost. If the IUnknown pointers are different, try
// to query for object identity and use that to compare.
if (unk1 && unk2) {
CComPtr<IObjectIdentity> object_identity;
if (SUCCEEDED(unk1.QueryInterface(&object_identity))) {
if (object_identity) {
HRESULT hr = object_identity->IsEqualObject(unk2);
ASSERT(SUCCEEDED(hr), (_T("")));
return hr == S_OK;
}
}
}
}
return false;
}
#ifdef SITELOCK_USE_MAP
// Site lock actual map entry macro
#define SITELOCK_ALLOWHTTPS(domain) \
{IObjectSafetySiteLock::SiteList::Allow, L"https", domain},
#define SITELOCK_DENYHTTPS(domain) \
{IObjectSafetySiteLock::SiteList::Deny, L"https", domain},
#define SITELOCK_ALLOWHTTP(domain) \
{IObjectSafetySiteLock::SiteList::Allow, L"http", domain},
#define SITELOCK_DENYHTTP(domain) \
{IObjectSafetySiteLock::SiteList::Deny, L"http", domain},
// Site lock begin map entry macro
#define BEGIN_SITELOCK_MAP() \
static const IObjectSafetySiteLock::SiteList * \
GetSiteLockMapAndCount(DWORD* dwCount) \
{ \
static IObjectSafetySiteLock::SiteList rgslTrustedSites[] = { \
// NO_LINT
// Site lock end map entry macro
#define END_SITELOCK_MAP() \
{(IObjectSafetySiteLock::SiteList::SiteListCategory)0, 0, 0}}; \
*dwCount = cElements(rgslTrustedSites) - 1; \
return rgslTrustedSites; \
} \
static const IObjectSafetySiteLock::SiteList * GetSiteLockMap() \
{ \
DWORD dwCount = 0; \
return GetSiteLockMapAndCount(&dwCount); \
} \
static DWORD GetSiteLockMapCount() \
{ \
DWORD dwCount = 0; \
GetSiteLockMapAndCount(&dwCount); \
return dwCount; \
} \
#endif // SITELOCK_USE_MAP
///////////////////////////////////////////////////////////////////////////////
// CSiteLock - Site lock templated class
template <typename T>
class ATL_NO_VTABLE CSiteLock {
public:
#ifdef SITELOCK_NO_EXPIRY
bool ControlExpired(DWORD = 0) { return false; }
#else
bool ControlExpired(DWORD dwExpiresDays = T::dwControlLifespan) {
SYSTEMTIME st = {0};
FILETIME ft = {0};
GetSystemTime(&st);
if (!SystemTimeToFileTime(&st, &ft))
return true;
time_t ttTime = _FILETIMEToUNIXTime(&ft);
time_t ttExpire = ImageNtHeaders(&__ImageBase)->FileHeader.TimeDateStamp;
ttExpire += dwExpiresDays*86400;
return (ttTime > ttExpire);
}
#endif
#ifdef SITELOCK_USE_MAP
// Checks if the activation URL is in an allowed domain / zone
bool InApprovedDomain(
const IObjectSafetySiteLock::SiteList* rgslTrustedSites =
T::GetSiteLockMap(),
int cTrustedSites = T::GetSiteLockMapCount()) {
#else
// Checks if the activation URL is in an allowed domain / zone
bool InApprovedDomain(
const IObjectSafetySiteLock::SiteList* rgslTrustedSites =
T::rgslTrustedSites,
int cTrustedSites = cElements(T::rgslTrustedSites)) {
#endif
// Retrieve the activation URL
CComBSTR bstrUrl;
if (!GetOurUrl(bstrUrl)) {
CORE_LOG(LEVEL_ERROR,
(_T("[CSiteLock::InApprovedDomain]")
_T("[unsafe: failed to get the url]")));
return false;
}
return InApprovedDomain(bstrUrl, rgslTrustedSites, cTrustedSites);
}
protected:
// Retrieves the activation URL
bool GetOurUrl(BSTR* bstr_url) {
if (NULL == bstr_url) {
return false;
}
CComPtr<IServiceProvider> spSrvProv;
if (!GetServiceProvider(&spSrvProv)) {
return false;
}
ASSERT1(spSrvProv);
// See if we're hosted within IE
// Query the site for a web browser object
CComPtr<IWebBrowser2> spWebBrowser;
CComPtr<IHTMLDocument2> spHTMLDocument2;
HRESULT hr = spSrvProv->QueryService(
SID_SWebBrowserApp,
IID_IWebBrowser2,
reinterpret_cast<void**>(&spWebBrowser));
if (FAILED(hr)) {
#ifdef SITELOCK_USE_IOLEOBJECT
return false;
#else
// Local declarations
CComPtr<IOleContainer> spContainer;
CComPtr<IOleClientSite> spClientSite;
// Get the client site, container, document, and url.
T* pT = static_cast<T*>(this);
hr = pT->GetSite(IID_IOleClientSite,
reinterpret_cast<void**>(&spClientSite));
if (FAILED(hr)) {
return false;
}
hr = spClientSite->GetContainer(&spContainer);
if (FAILED(hr)) {
return false;
}
hr = spContainer.QueryInterface(&spHTMLDocument2);
if (FAILED(hr)) {
return false;
}
if (FAILED(spHTMLDocument2->get_URL(bstr_url))) {
return false;
}
#endif
} else {
ASSERT1(spWebBrowser);
CComPtr<IDispatch> spDocument;
hr = spWebBrowser->get_Document(&spDocument);
if (FAILED(hr) || !spDocument) {
return false;
}
hr = spDocument.QueryInterface(&spHTMLDocument2);
if (FAILED(hr) || !spHTMLDocument2) {
return false;
}
// Retrieves the URL of the document hosting the control.
hr = spHTMLDocument2->get_URL(bstr_url);
if (FAILED(hr) || !*bstr_url) {
return false;
}
}
#ifdef DEBUG
// A little bit of debug code to dump the urls that we care about.
// Find out if this is a top level window or a frame.
CComPtr<IHTMLWindow2> spWindow;
hr = spHTMLDocument2->get_parentWindow(&spWindow);
if (FAILED(hr) || !spWindow) {
return false;
}
// Get our window. This is the window.self
CComPtr<IHTMLWindow2> spWindowCur;
hr = spWindow->get_self(&spWindowCur);
if (FAILED(hr) || !spWindowCur) {
return false;
}
// Get the top level window.
CComPtr<IHTMLWindow2> spWindowTop;
hr = spWindow->get_top(&spWindowTop);
if (FAILED(hr) || !spWindowTop) {
return false;
}
// in a frameless situation, the windows are the same.
bool isTopLevel = AreObjectsEqual(spWindowCur, spWindowTop);
if (!isTopLevel) {
CORE_LOG(L3,
(_T("[CSiteLock::GetOurUrl][We are hosted in a frame][url=%s]"),
(*bstr_url)));
}
// Retrieves the URL of the resource that Microsoft Internet Explorer
// is currently displaying.
CComBSTR browserUrl;
VERIFY1(SUCCEEDED(spWebBrowser->get_LocationURL(&browserUrl)));
// Retrieves the URL of the current window and document hosting the control.
CComPtr<IHTMLLocation> spLocation;
VERIFY1(SUCCEEDED(spWindowCur->get_location(&spLocation)));
CComBSTR curUrl;
VERIFY1(SUCCEEDED(spLocation->get_href(&curUrl)));
// Retrieves the URL of the top window.
spLocation = 0;
VERIFY1(SUCCEEDED(spWindowTop->get_location(&spLocation)));
// In a cross-scripting situation we won't be able to get the href property
CComBSTR topUrl;
if (FAILED(spLocation->get_href(&topUrl))) {
topUrl = _T("null");
}
ASSERT1(*bstr_url && curUrl && topUrl && browserUrl);
CORE_LOG(L3,
(_T("[CSiteLock::GetOurUrl][doc=%s][self=%s][top=%s][browser=%s]"),
*bstr_url,
curUrl,
topUrl,
browserUrl));
#endif
ASSERT(*bstr_url, (_T("post-condition")));
return true;
}
private:
bool InApprovedDomain(CComBSTR bstrUrl,
const IObjectSafetySiteLock::SiteList *rgslTrustedSites,
int cTrustedSites) {
if (!bstrUrl) {
return false;
}
DWORD dwZone = URLZONE_UNTRUSTED;
if (!GetUrlZone(bstrUrl, &dwZone)) {
CORE_LOG(LEVEL_ERROR, (_T("[CSiteLock::InApprovedDomain]")
_T("[unsafe: failed to get url zone][url=%s]"),
bstrUrl));
return false;
}
// Check if the activation URL is in an allowed domain / zone
if (!FApprovedDomain(bstrUrl, dwZone, rgslTrustedSites, cTrustedSites)) {
ASSERT(FALSE, (_T("[CSiteLock::InApprovedDomain][unsafe: not in approved")
_T(" domain][url=%s][zone=%s]")
_T("- Check your ciconfig.ini to make sure you have the ")
_T("right server in there!"),
bstrUrl, UrlZoneToString(dwZone)));
CORE_LOG(LEVEL_ERROR, (_T("[CSiteLock::InApprovedDomain][unsafe: not in")
_T(" approved domain][url=%s][zone=%s]"),
bstrUrl, UrlZoneToString(dwZone)));
return false;
}
CORE_LOG(LEVEL_INF3,
(_T("[CSiteLock::InApprovedDomain][safe][%s][%s]"),
bstrUrl,
UrlZoneToString(dwZone)));
return true;
}
bool GetUrlZone(CComBSTR bstrURL, DWORD* zone_out) {
if (!bstrURL || !zone_out) {
return false;
}
CComPtr<IServiceProvider> spSrvProv;
CComPtr<IInternetSecurityManager> spInetSecMgr;
if (!GetServiceProvider(&spSrvProv) ||
FAILED(spSrvProv->QueryService(
SID_SInternetSecurityManager,
__uuidof(IInternetSecurityManager),
reinterpret_cast<void **>(&spInetSecMgr)))) {
RET_FALSE_IF_FAILED(::CoInternetCreateSecurityManager(NULL,
&spInetSecMgr,
NULL));
}
ASSERT1(spInetSecMgr);
RET_FALSE_IF_FAILED(spInetSecMgr->MapUrlToZone(bstrURL, zone_out, 0));
return true;
}
bool GetServiceProvider(IServiceProvider** ppSrvProv) {
if (NULL == ppSrvProv) {
return false;
}
// Get the current pointer as an instance of the template class
T* pT = static_cast<T*>(this);
#ifdef SITELOCK_USE_IOLEOBJECT
CComPtr<IOleClientSite> spClientSite;
RET_FALSE_IF_FAILED(pT->GetClientSite(
reinterpret_cast<IOleClientSite**>(ppSrvProv)));
RET_IF_FALSE(spClientSite, false);
RET_FALSE_IF_FAILED(spClientSite->QueryInterface(
IID_IServiceProvider,
reinterpret_cast<void **>(ppSrvProv)));
#else // USE_IOBJECTWITHSITE
if FAILED(pT->GetSite(IID_IServiceProvider,
reinterpret_cast<void**>(ppSrvProv))) {
return false;
}
#endif
ASSERT(ppSrvProv, (_T("post-condition"))); // Post-condition.
return true;
}
// Checks if an activation URL is in an allowed domain / zone
bool FApprovedDomain(const OLECHAR* wzUrl,
DWORD dwZone,
const IObjectSafetySiteLock::SiteList* rgslTrustedSites,
int cTrustedSites) {
// Declarations
HRESULT hr = S_OK;
OLECHAR wzDomain[INTERNET_MAX_HOST_NAME_LENGTH + 1] = {0};
OLECHAR wzScheme[INTERNET_MAX_SCHEME_LENGTH + 1] = {0};
// Retrieve the normalized domain and scheme
hr = GetDomainAndScheme(wzUrl,
wzScheme,
cElements(wzScheme),
wzDomain,
cElements(wzDomain));
if (FAILED(hr)) {
return false;
}
// Try to match the activation URL with each entry in order
DWORD cbScheme = (::lstrlenW(wzScheme) + 1) * sizeof(OLECHAR);
for (int i = 0; i < cTrustedSites; i++) {
// Try to match by scheme
DWORD cbSiteScheme = (::lstrlenW(rgslTrustedSites[i].szScheme) + 1) *
sizeof(OLECHAR);
if (cbScheme != cbSiteScheme) {
continue;
}
if (0 != ::memcmp(wzScheme, rgslTrustedSites[i].szScheme, cbScheme)) {
continue;
}
// Try to match by zone
if (rgslTrustedSites[i].szDomain == SITELOCK_INTRANET_ZONE) {
if ((dwZone == URLZONE_INTRANET) || (dwZone == URLZONE_TRUSTED)) {
return (rgslTrustedSites[i].iAllowType ==
IObjectSafetySiteLock::SiteList::Allow);
}
} else if (rgslTrustedSites[i].szDomain == SITELOCK_MYCOMPUTER_ZONE) {
if (dwZone == URLZONE_LOCAL_MACHINE) {
return (rgslTrustedSites[i].iAllowType ==
IObjectSafetySiteLock::SiteList::Allow);
}
} else if (rgslTrustedSites[i].szDomain == SITELOCK_TRUSTED_ZONE) {
if (dwZone == URLZONE_TRUSTED) {
return (rgslTrustedSites[i].iAllowType ==
IObjectSafetySiteLock::SiteList::Allow);
}
// Try to match by domain name
} else if (MatchDomains(rgslTrustedSites[i].szDomain, wzDomain)) {
return (rgslTrustedSites[i].iAllowType ==
IObjectSafetySiteLock::SiteList::Allow);
}
}
return false;
};
// Normalizes an international domain name
HRESULT NormalizeDomain(OLECHAR * wzDomain, int cchDomain) {
// Data validation
if (!wzDomain) {
return E_POINTER;
}
// If the domain is only 7-bit ASCII, normalization is not required
bool fFoundUnicode = false;
for (const OLECHAR * wz = wzDomain; *wz != 0; wz++) {
if (0x80 <= *wz) {
fFoundUnicode = true;
break;
}
}
if (!fFoundUnicode) {
return S_OK;
}
// Construct a fully qualified path to the Windows system directory
static const WCHAR wzNormaliz[] = L"normaliz.dll";
static const int cchNormaliz = cElements(wzNormaliz);
WCHAR wzDllPath[MAX_PATH + 1] = {0};
if (!::GetSystemDirectoryW(wzDllPath,
cElements(wzDllPath) - cchNormaliz - 1)) {
return E_FAIL;
}
int cchDllPath = ::lstrlenW(wzDllPath);
if (!cchDllPath) {
return E_FAIL;
}
if (wzDllPath[cchDllPath-1] != L'\\') {
wzDllPath[cchDllPath++] = L'\\';
}
::CopyMemory(wzDllPath + cchDllPath,
wzNormaliz,
cchNormaliz * sizeof(WCHAR));
// Load the DLL used for domain normalization
HMODULE hNormaliz = ::LoadLibraryExW(wzDllPath,
NULL,
LOAD_WITH_ALTERED_SEARCH_PATH);
if (!hNormaliz) {
return E_FAIL;
}
HRESULT hr = E_FAIL;
// Locate the entry point used for domain normalization
PFN_IdnToAscii pfnIdnToAscii =
(PFN_IdnToAscii)::GetProcAddress(hNormaliz, "IdnToAscii");
if (!pfnIdnToAscii) {
goto cleanup;
}
// Normalize the domain name
WCHAR wzEncoded[INTERNET_MAX_HOST_NAME_LENGTH + 1];
int cchEncode = pfnIdnToAscii(IDN_USE_STD3_ASCII_RULES,
wzDomain,
::lstrlenW(wzDomain),
wzEncoded,
cElements(wzEncoded));
if (0 == cchEncode) {
hr = HRESULT_FROM_WIN32(::GetLastError());
goto cleanup;
}
// Copy results to the input buffer
if (cchEncode >= cchDomain) {
hr = E_OUTOFMEMORY;
goto cleanup;
}
::CopyMemory(wzDomain, wzEncoded, cchEncode * sizeof(WCHAR));
hr = S_OK;
cleanup:
if (hNormaliz) {
::CloseHandle(hNormaliz);
}
return hr;
}
// Extracts a normalized domain and scheme from an activation URL
HRESULT GetDomainAndScheme(const OLECHAR* wzUrl,
OLECHAR* wzScheme,
DWORD cchScheme,
OLECHAR* wzDomain,
DWORD cchDomain) {
// Data validation
if (!wzDomain || !wzScheme) {
return E_POINTER;
}
// Extract the scheme
HRESULT hr = ::UrlGetPartW(wzUrl, wzScheme, &cchScheme, URL_PART_SCHEME, 0);
if (FAILED(hr)) {
return E_FAIL;
}
// Extract the host name
DWORD cchDomain2 = cchDomain;
hr = ::UrlGetPartW(wzUrl, wzDomain, &cchDomain2, URL_PART_HOSTNAME, 0);
if (FAILED(hr)) {
*wzDomain = 0;
}
// Exclude any URL specifying a user name or password
if ((0 == ::_wcsicmp(wzScheme, L"http")) ||
(0 == ::_wcsicmp(wzScheme, L"https"))) {
DWORD cch = 1;
WCHAR wzTemp[1] = {0};
::UrlGetPartW(wzUrl, wzTemp, &cch, URL_PART_USERNAME, 0);
if (1 < cch) {
return E_FAIL;
}
::UrlGetPartW(wzUrl, wzTemp, &cch, URL_PART_PASSWORD, 0);
if (1 < cch) {
return E_FAIL;
}
}
// Normalize the domain name
return NormalizeDomain(wzDomain, cchDomain);
}
// Attempts to match an activation URL with a domain name
bool MatchDomains(const OLECHAR* wzTrustedDomain,
const OLECHAR* wzOurDomain) {
// Data validation
if (!wzTrustedDomain) {
return (0 == *wzOurDomain); // match only if empty
}
// Declarations
int cchTrusted = ::lstrlenW(wzTrustedDomain);
int cchOur = ::lstrlenW(wzOurDomain);
bool fForcePrefix = false;
bool fDenyPrefix = false;
// Check if all activation URLs should be matched
if (0 == ::wcscmp(wzTrustedDomain, L"*")) {
return true;
}
// Check if the entry is like *. and setup the comparison range
if ((2 < cchTrusted) &&
(L'*' == wzTrustedDomain[0]) &&
(L'.' == wzTrustedDomain[1])) {
fForcePrefix = true;
wzTrustedDomain += 2;
cchTrusted -= 2;
// Check if the entry is like = and setup the comparison range
} else if ((1 < cchTrusted) && (L'=' == wzTrustedDomain[0])) {
fDenyPrefix = true;
wzTrustedDomain++;
cchTrusted--;
};
// Check if there is a count mismatch
if (cchTrusted > cchOur) {
return false;
}
// Compare URLs on the desired character range
if (0 != ::memcmp(wzOurDomain + cchOur - cchTrusted,
wzTrustedDomain,
cchTrusted * sizeof(OLECHAR))) {
return false;
}
// Compare URLs without allowing child domains
if (!fForcePrefix && (cchTrusted == cchOur)) {
return true;
}
// Compare URLs requiring child domains
if (!fDenyPrefix && (wzOurDomain[cchOur - cchTrusted - 1] == L'.')) {
return true;
}
return false;
}
};
///////////////////////////////////////////////////////////////////////////////
// IObjectSafetySiteLockImpl - "Safe for scripting" template
template <typename T, DWORD dwSupportedSafety>
class ATL_NO_VTABLE IObjectSafetySiteLockImpl
: public IObjectSafetySiteLock,
public CSiteLock<T> {
public:
// Constructor
IObjectSafetySiteLockImpl(): m_dwCurrentSafety(0) {}
// Returns safety options
STDMETHOD(GetInterfaceSafetyOptions)(REFIID riid,
DWORD * pdwSupportedOptions,
DWORD * pdwEnabledOptions) {
// Data validation
if (!pdwSupportedOptions || !pdwEnabledOptions) {
return E_POINTER;
}
// Declarations
HRESULT hr = S_OK;
IUnknown* pUnk = NULL;
// Get the current pointer as an instance of the template class
T * pT = static_cast<T*>(this);
// Check if the requested COM interface is supported
hr = pT->GetUnknown()->QueryInterface(riid,
reinterpret_cast<void**>(&pUnk));
if (FAILED(hr)) {
*pdwSupportedOptions = 0;
*pdwEnabledOptions = 0;
return hr;
}
// Release the interface
pUnk->Release();
// Check expiry and if the activation URL is allowed
if (!ControlExpired() && InApprovedDomain()) {
*pdwSupportedOptions = dwSupportedSafety;
*pdwEnabledOptions = m_dwCurrentSafety;
} else {
*pdwSupportedOptions = dwSupportedSafety;
*pdwEnabledOptions = 0;
}
return S_OK;
}
// Sets safety options
STDMETHOD(SetInterfaceSafetyOptions)(REFIID riid,
DWORD dwOptionSetMask,
DWORD dwEnabledOptions) {
// Declarations
HRESULT hr = S_OK;
IUnknown* pUnk = NULL;
// Get the current pointer as an instance of the template class
T * pT = static_cast<T*>(this);
// Check if we support the interface and return E_NOINTERFACE if we don't
// Check if the requested COM interface is supported
hr = pT->GetUnknown()->QueryInterface(riid,
reinterpret_cast<void**>(&pUnk));
if (FAILED(hr)) {
return hr;
}
// Release the interface
pUnk->Release();
// Reject unsupported requests
if (dwOptionSetMask & ~dwSupportedSafety) {
return E_FAIL;
}
// Calculate safety options
DWORD dwNewSafety = (m_dwCurrentSafety & ~dwOptionSetMask) |
(dwOptionSetMask & dwEnabledOptions);
if (m_dwCurrentSafety != dwNewSafety) {
// Check expiry and if the activation URL is allowed
if (ControlExpired() || !InApprovedDomain()) {
return E_FAIL;
}
// Set safety options
m_dwCurrentSafety = dwNewSafety;
}
return S_OK;
}
// Returns capabilities (this can be used by testing tools to query for
// custom capabilities or version information)
STDMETHOD(GetCapabilities)(DWORD * pdwCapability) {
// Data validation
if (!pdwCapability) {
return E_POINTER;
}
// Return the version if 0 is passed in
if (0 == *pdwCapability) {
*pdwCapability = SITELOCK_VERSION;
return S_OK;
}
// Return the options if 1 is passed in
if (1 == *pdwCapability) {
*pdwCapability =
#ifdef SITELOCK_USE_IOLEOBJECT
Capability::UsesIOleObject |
#endif
#ifndef SITELOCK_NO_EXPIRY
Capability::HasExpiry |
#endif
0;
return S_OK;
}
// Return not implemented otherwise
*pdwCapability = 0;
return E_NOTIMPL;
}
// Returns site lock entries controlling activation
STDMETHOD(GetApprovedSites)(const SiteList ** pSiteList, DWORD * pcEntries) {
// Data validation
if (!pSiteList || !pcEntries) {
return E_POINTER;
}
// Return specified site lock entries
#ifdef SITELOCK_USE_MAP
// Use the site lock map
*pSiteList = T::GetSiteLockMapAndCount(*pcEntries);
#else
// Use the static member
*pSiteList = T::rgslTrustedSites;
*pcEntries = cElements(T::rgslTrustedSites);
#endif
return S_OK;
}
STDMETHOD(GetExpiryDate)(DWORD * pdwLifespan, FILETIME * pExpiryDate) {
if (!pdwLifespan || !pExpiryDate) {
return E_POINTER;
}
#ifdef SITELOCK_NO_EXPIRY
*pdwLifespan = 0;
::ZeroMemory(reinterpret_cast<void*>(pExpiryDate), sizeof(FILETIME));
return E_NOTIMPL;
#else
*pdwLifespan = T::dwControlLifespan;
// Calculate expiry date from life span
time_t ttExpire = ImageNtHeaders(&__ImageBase)->FileHeader.TimeDateStamp;
ttExpire += T::dwControlLifespan*86400; // seconds per day
_UNIXTimeToFILETIME(ttExpire, pExpiryDate);
return S_OK;
#endif
}
private:
// Current safety
DWORD m_dwCurrentSafety;
};
#endif // OMAHA_PLUGINS_SITELOCK_H__