Files
2025-09-18 17:55:52 -04:00

761 lines
25 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// 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 <wininet.h>
// Pragmas
#pragma once
// 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)
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 compatibility 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;
};
#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[] = { \
// 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;
DWORD dwZone = URLZONE_UNTRUSTED;
if (!GetOurUrl(bstrUrl, dwZone))
return false;
// Check if the activation URL is in an allowed domain / zone
return FApprovedDomain(bstrUrl, dwZone, rgslTrustedSites, cTrustedSites);
}
// Retrieves the activation URL
bool GetOurUrl(CComBSTR &bstrURL, DWORD &dwZone)
{
// Declarations
HRESULT hr = S_OK;
CComPtr<IServiceProvider> spSrvProv;
CComPtr<IInternetSecurityManager> spInetSecMgr;
CComPtr<IWebBrowser2> spWebBrowser;
// Get the current pointer as an instance of the template class
T * pT = static_cast<T*>(this);
// Retrieve the activation site
CComPtr<IOleClientSite> spClientSite;
#ifdef SITELOCK_USE_IOLEOBJECT
hr = pT->GetClientSite((IOleClientSite **)&spClientSite);
if (FAILED(hr) || spClientSite == NULL)
return false;
hr = spClientSite->QueryInterface(IID_IServiceProvider, (void **)&spSrvProv);
#else
hr = pT->GetSite(IID_IServiceProvider, (void**)&spSrvProv);
#endif
if (FAILED(hr))
return false;
// Query the site for a web browser object
hr = spSrvProv->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, (void **)&spWebBrowser);
if (FAILED(hr))
{
// Local declarations
CComPtr<IHTMLDocument2> spDoc;
CComPtr<IOleContainer> spContainer;
// Reinitialize
spSrvProv = NULL;
spClientSite = NULL;
// Get the client site, container, and provider, etc.
hr = pT->GetSite(IID_IOleClientSite, (void**)&spClientSite);
if (FAILED(hr))
return false;
hr = spClientSite->GetContainer(&spContainer);
if (FAILED(hr))
return false;
hr = spContainer->QueryInterface(IID_IHTMLDocument2, (void **)&spDoc);
if (FAILED(hr))
return false;
if (FAILED(spDoc->get_URL(&bstrURL)))
return false;
hr = spClientSite->QueryInterface(IID_IServiceProvider, (void **)&spSrvProv);
if (FAILED(hr))
return false;
}
else
{
// Query the web browser object for the activation URL
hr = spWebBrowser->get_LocationURL(&bstrURL);
if (FAILED(hr))
return false;
}
// Query the site for its associated security manager
hr = spSrvProv->QueryService(SID_SInternetSecurityManager, IID_IInternetSecurityManager, (void **)&spInetSecMgr);
if (FAILED(hr))
return false;
// Query the security manager for the zone the activation URL belongs to
hr = spInetSecMgr->MapUrlToZone(bstrURL, &dwZone, 0);
if (FAILED(hr))
return false;
return true;
}
private:
// 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, (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, (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((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;
};