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.
 
 
 
 
 
 

1135 lines
32 KiB

#include "stdafx.h"
#include <wininet.h>
#include "CommonFuncs.h"
#include "FileHash.h"
#define SAFE_LOCAL_SCRIPTS_KEY TEXT("Software\\Microsoft\\WBEM\\SafeLocalScripts")
#define VS_PATH_KEY TEXT("Software\\Microsoft\\VisualStudio\\7.0\\Setup\\VS")
#define DEVENV_VALUE TEXT("VS7EnvironmentLocation")
#define VC_PATH_KEY TEXT("Software\\Microsoft\\VisualStudio\\7.0\\Setup\\VC")
#define VC_PRODUCTDIR_VALUE TEXT("ProductDir")
#define VS_VER_INDEPENDANT_PATH_KEY TEXT("Software\\Microsoft\\VisualStudio")
TCHAR strVSPathKey[MAX_PATH * 2]= VS_PATH_KEY;
TCHAR strVCPathKey[MAX_PATH * 2] = VC_PATH_KEY;
HRESULT ConvertToTString(BSTR strPath,TCHAR **ppStr);
// QUESTIONS:
// - What is passed to SetSite when we are create in script?
// - If we are passed an IOleClientSite, is it a good idea to QueryService for
// an IWebBrowserApp?
// - Is there a better way to get the IHTMLDocument2 when we are created through
// script?
// Here are some general notes about what I've observed when creating objects
// in HTML with IE 5.x.
// Observed IE 5.x Behavior
// If an object implements IOleObject AND IObjectWithSite
// - For objects created in an HTML page with an <OBJECT...> tag, IE calls
// IOleObject::SetClientSite and passes an IOleClientSite object
// - For object created in script of HTML page using JScript
// 'new ActiveXObject' or VBScript 'CreateObject' function, IE calls
// IObjectWithSite::SetSite with a ??? object
// If an object implements IObjectWithSite (and NOT IOleObject)
// - For object created in HTML page with <OBJECT...> tag, IE calls
// IObjectWithSite::SetSite and passes an IOleClientSite object
// - For object created in script of HTML page using JScript
// 'new ActiveXObject' or VBScript 'CreateObject' function, IE calls
// IObjectWithSite::SetSite with a ??? object
// BYTE *pbData = NULL;
// DWORD dwSize;
// GetSourceFromDoc(pDoc, &pbData, &dwSize);
// Get the original source to the document specified by pDoc
HRESULT GetSourceFromDoc(IHTMLDocument2 *pDoc, BYTE **ppbData, DWORD *pdwSize)
{
HRESULT hr = E_FAIL;
IPersistStreamInit *pPersistStreamInit = NULL;
IStream *pStream = NULL;
*ppbData = NULL;
__try
{
if(FAILED(hr = pDoc->QueryInterface(IID_IPersistStreamInit, (void**) &pPersistStreamInit)))
__leave;
if (FAILED(hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream)))
__leave;
if(FAILED(hr = pPersistStreamInit->Save(pStream, TRUE)))
__leave;
// We are not responsible for freeing this HGLOBAL
HGLOBAL hGlobal = NULL;
if(FAILED(hr = GetHGlobalFromStream(pStream, &hGlobal)))
__leave;
STATSTG ss;
if(FAILED(hr = pStream->Stat(&ss, STATFLAG_NONAME)))
__leave;
// This should never happen
if(ss.cbSize.HighPart != 0)
__leave;
if(NULL == ((*ppbData) = new BYTE[ss.cbSize.LowPart]))
__leave;
LPVOID pHTMLText = NULL;
if(NULL == (pHTMLText = GlobalLock(hGlobal)))
__leave;
*pdwSize = ss.cbSize.LowPart;
memcpy(*ppbData, pHTMLText, ss.cbSize.LowPart);
GlobalUnlock(hGlobal);
hr = S_OK;
}
__finally
{
// If we did not finish, but we allocated memory, we free it.
if(FAILED(hr) && (*ppbData)!=NULL)
delete [] (*ppbData);
if(pPersistStreamInit)
pPersistStreamInit->Release();
if(pStream)
pStream->Release();
}
return hr;
}
// For a control specified by pUnk, get the IServiceProvider of the host
HRESULT GetSiteServices(IUnknown *pUnk, IServiceProvider **ppServProv)
{
HRESULT hr = E_FAIL;
IOleObject *pOleObj = NULL;
IObjectWithSite *pObjWithSite = NULL;
IOleClientSite *pSite = NULL;
__try
{
// Check if the ActiveX control supports IOleObject.
if(SUCCEEDED(pUnk->QueryInterface(IID_IOleObject, (void**)&pOleObj)))
{
// If the control was created through an <OBJECT...> tag, IE will
// have passed us an IOleClientSite. If we have not been passed
// an IOleClientSite, GetClientSite will still SUCCEED, but pSite
// will be NULL. In this case, we just go to the next section.
if(SUCCEEDED(pOleObj->GetClientSite(&pSite)) && pSite)
{
hr = pSite->QueryInterface(IID_IServiceProvider, (void**)ppServProv);
// At this point, we are done and do not want to process the
// code in the next seciont
__leave;
}
}
// At this point, one of two things has happened:
// 1) We didn't support IOleObject
// 2) We supported IOleObject, but we were never passed an IOleClientSite
// In either case, we now need to look at IObjectWithSite to try to get
// to our site
if(FAILED(hr = pUnk->QueryInterface(IID_IObjectWithSite, (void**)&pObjWithSite)))
__leave;
hr = pObjWithSite->GetSite(IID_IServiceProvider, (void**)ppServProv);
}
__finally
{
// Release any interfaces we used along the way
if(pOleObj)
pOleObj->Release();
if(pObjWithSite)
pObjWithSite->Release();
if(pSite)
pSite->Release();
}
return hr;
}
// This function shows how to get to the IHTMLDocument2 that created an
// arbitrary control represented by pUnk
HRESULT GetDocument(IUnknown *pUnk, IHTMLDocument2 **ppDoc)
{
HRESULT hr = E_FAIL;
IServiceProvider* pServProv = NULL;
IDispatch *pDisp = NULL;
__try
{
if(FAILED(hr = GetSiteServices(pUnk, &pServProv)))
__leave;
if(FAILED(hr = pServProv->QueryService(SID_SContainerDispatch, IID_IDispatch, (void**)&pDisp)))
__leave;
hr = pDisp->QueryInterface(IID_IHTMLDocument2, (void**)ppDoc);
}
__finally
{
if(pServProv)
pServProv->Release();
if(pDisp)
pDisp->Release();
}
return hr;
}
// This function will Release() the current document and return a pointer to
// the parent document. If no parent document is available, this function
// will return NULL (but will still release the current document)
IHTMLDocument2 *GetParentDocument(IHTMLDocument2 *pDoc)
{
BSTR bstrURL = NULL;
BSTR bstrURLParent = NULL;
IHTMLWindow2 *pWndParent = NULL;
IHTMLWindow2 *pWndParentParent = NULL;
IHTMLDocument2 *pDocParent = NULL;
__try
{
if(FAILED(pDoc->get_URL(&bstrURL)))
__leave;
if(FAILED(pDoc->get_parentWindow(&pWndParent)))
__leave;
if(FAILED(pWndParent->get_parent(&pWndParentParent)))
__leave;
if(FAILED(pWndParentParent->get_document(&pDocParent)))
__leave;
if(FAILED(pDocParent->get_URL(&bstrURLParent)))
__leave;
// TODO: Make this more robust
if(0 == lstrcmpW(bstrURL, bstrURLParent))
{
// We are at the top document. Release the new document pointer we
// just received.
pDocParent->Release();
pDocParent = NULL;
}
}
__finally
{
if(bstrURL)
SysFreeString(bstrURL);
if(bstrURLParent)
SysFreeString(bstrURLParent);
if(pWndParent)
pWndParent->Release();
if(pWndParentParent)
pWndParentParent->Release();
if(pDoc)
pDoc->Release();
}
return pDocParent;
}
// Try to append bstr2 to pbstr1. If this function fails, pbstr1 will still
// point to the original valid allocated bstr.
HRESULT AppendBSTR(BSTR *pbstr1, BSTR bstr2)
{
HRESULT hr = S_OK;
CComBSTR bstr;
if(FAILED(bstr.AppendBSTR(*pbstr1)))
hr = E_FAIL;
if(FAILED(bstr.AppendBSTR(bstr2)))
hr = E_FAIL;
if(SUCCEEDED(hr))
{
SysFreeString(*pbstr1);
*pbstr1 = bstr.Detach();
}
return hr;
}
BSTR AllocBSTR(LPCTSTR lpsz)
{
CComBSTR bstr(lpsz);
return bstr.Detach();
}
BOOL IsURLLocal(LPWSTR szURL)
{
CComBSTR bstrURL(szURL);
if ( !bstrURL )
return FALSE;
if(FAILED(bstrURL.ToLower()))
return FALSE;
// Make sure the URL starts with 'file://'
// NOTE: Calling code may rely on the fact that this method verifies that
// the URL starts with file://. If you change this function to work
// differently, you must examine the places that call this method so that
// they don't rely on this assumption.
if(0 != wcsncmp(bstrURL, L"file://", 7))
return FALSE;
// Make sure the next part is a drive letter, such as 'C:\'
if(0 != wcsncmp(&(bstrURL[8]), L":\\", 2))
return FALSE;
WCHAR drive = bstrURL[7];
// Make sure the URL points to drive 'a' to 'z'
if(drive < 'a' || drive > 'z')
return FALSE;
TCHAR szDrive[4];
StringCchCopy(szDrive,sizeof(szDrive),TEXT("c:\\")); // 4505 in WMI
szDrive[0] = (TCHAR)drive;
UINT uDriveType = GetDriveType(szDrive);
return (DRIVE_FIXED == uDriveType);
}
// Try to convert the BSTR to lower case. If this function fails, pbstr will
// still point to the original valid allocated bstr.
HRESULT ToLowerBSTR(BSTR *pbstr)
{
CComBSTR bstr;
if(FAILED(bstr.AppendBSTR(*pbstr)))
return E_FAIL;
if(FAILED(bstr.ToLower()))
return E_FAIL;
SysFreeString(*pbstr);
*pbstr = bstr.Detach();
return S_OK;
}
// For a given instance of an ActiveX control (represented by pUnk), and a
// specified strProgId, this function creates a 'full path' that can be checked
// in the registry to see if object creation should be allowed. The full
// location is created from the following information
// 1) The name of the current EXE
// 2) The ProgId requested
// 3) The HREF of the current document
// 4) The HREF of every parent document up the available hierarchy
// All of the documents in the hierarchy must be on a local hard drive or the
// function will fail. In addition, if any piece of informaiton along the way
// is not available, the function will fail. This increases the security of
// our process.
// This function will also create a BSTR in *pbstrHash that contains the
// cumulative MD5 hash of the document and its parents. This BSTR will be
// allocated by the function and should be freed by the caller. If the
// function returns NULL for the full location, it will also return NULL for
// *pbstrHash
BSTR GetFullLocation(IUnknown *pUnk, BSTR strProgId, BSTR *pbstrHash)
{
HRESULT hr = E_FAIL;
IHTMLDocument2 *pDoc = NULL;
BSTR bstrURL = NULL;
BSTR bstrFullLocation = NULL;
*pbstrHash = NULL;
BYTE *pbData = NULL;
BSTR bstrHash = NULL;
__try
{
if(FAILED(GetDocument(pUnk, &pDoc)))
__leave;
TCHAR szFilename[_MAX_PATH];
TCHAR szFilenameLong[_MAX_PATH];
GetModuleFileName(NULL, szFilenameLong, _MAX_PATH);
GetShortPathName(szFilenameLong, szFilename, _MAX_PATH);
if(NULL == (bstrFullLocation = AllocBSTR(szFilename)))
__leave;
if(FAILED(AppendBSTR(&bstrFullLocation, strProgId)))
__leave;
if(NULL == (*pbstrHash = AllocBSTR(_T(""))))
__leave;
int nDepth = 0;
do
{
// Make sure we don't get stuck in some infinite loop of parent
// documents. If we do get more than 100 levels of parent
// documents, we assume failure
if(++nDepth >= 100)
__leave;
if(FAILED(pDoc->get_URL(&bstrURL)))
__leave;
DWORD dwDataSize = 0;
if(FAILED(GetSourceFromDoc(pDoc, &pbData, &dwDataSize)))
__leave;
MD5Hash hash;
if(FAILED(hash.HashData(pbData, dwDataSize)))
__leave;
if(NULL == (bstrHash = hash.GetHashBSTR()))
__leave;
if(FAILED(AppendBSTR(pbstrHash, bstrHash)))
__leave;
SysFreeString(bstrHash);
bstrHash = NULL;
delete [] pbData;
pbData = NULL;
// Make sure every document is on the local hard drive
if(!IsURLLocal(bstrURL))
__leave;
if(FAILED(AppendBSTR(&bstrFullLocation, bstrURL)))
__leave;
SysFreeString(bstrURL);
bstrURL = NULL;
} while (NULL != (pDoc = GetParentDocument(pDoc)));
// Make sure we do not have any embeded NULLs. If we do, we just
// FAIL the call
if(SysStringLen(bstrFullLocation) != wcslen(bstrFullLocation))
__leave;
// Make the location lower case
if(FAILED(ToLowerBSTR(&bstrFullLocation)))
__leave;
// We've now created the normalized full location
hr = S_OK;
}
__finally
{
// pDoc should be NULL if we got to the top of the hierarchy. If not,
// we should release it
if(pDoc)
pDoc->Release();
// pbData should be NULL unless there was an error calculating the hash
if(pbData)
delete [] pbData;
// bstrHash should be NULL unless there was a problem
if(bstrHash)
SysFreeString(bstrHash);
// bstrURL should be NULL unless there was a problem
if(bstrURL)
SysFreeString(bstrURL);
// If we didn't make it all the way to the end, we free the full location
if(FAILED(hr) && bstrFullLocation)
{
SysFreeString(bstrFullLocation);
bstrFullLocation = NULL;
}
// If we didn't make it all the way to the end, we free the checksum
if(FAILED(hr) && *pbstrHash)
{
SysFreeString(*pbstrHash);
*pbstrHash = NULL;
}
}
return bstrFullLocation;
}
// This version of the control is hard coded to only allow ProgIds to be
// registered under restricted conditions. In this version, this means
// that the process registering a ProgID must be DevEnv.exe, as specified
// by the value in:
// HKLM\Software\Microsoft\VisualStudio\7.0\Setup\VS\VS7EnvironmentLocation
// Also, only wbemscripting.swbemlocator and wbemscripting.swbemsink can
// be registered
HRESULT AreCrippledCriteriaMet(BSTR strProgId)
{
BSTR bstrProgIdLowerCase = NULL;
BSTR bstrModuleName = NULL;
BSTR bstrDevEnvPath = NULL;
HKEY hKeyVSPaths = NULL;
HRESULT hr = E_FAIL;
__try
{
////////////////////////////////////////////////////////////////////////////////
// Make sure the ProgId is wbemscripting.swbemsink or wbemscripting.swbemlocator
// Copy strProgId to tempory BSTR
if(NULL == (bstrProgIdLowerCase = SysAllocString(strProgId)))
__leave;
// Change it to lower case
if(FAILED(ToLowerBSTR(&bstrProgIdLowerCase)))
__leave;
// See if the prog id is for the sink or locator. If not, leave
if(0 != wcscmp(bstrProgIdLowerCase, L"wbemscripting.swbemsink") && 0 != wcscmp(bstrProgIdLowerCase, L"wbemscripting.swbemlocator"))
__leave;
////////////////////////////////////////////////////////////////////////////////
// Make sure we are running from devenv.exe
TCHAR szFilename[_MAX_PATH];
TCHAR szFilenameLong[_MAX_PATH];
TCHAR szDevEnvLong[_MAX_PATH];
TCHAR szDevEnv[_MAX_PATH];
GetModuleFileName(NULL, szFilenameLong, _MAX_PATH);
GetShortPathName(szFilenameLong, szFilename, _MAX_PATH);
// Make into BSTR
if(NULL == (bstrModuleName = AllocBSTR(szFilename)))
__leave;
// Make lower case
if(FAILED(ToLowerBSTR(&bstrModuleName)))
__leave;
// Open the registry key to get the path to DevEnv.exe
if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,strVSPathKey,0,KEY_QUERY_VALUE,&hKeyVSPaths))
__leave;
DWORD cbValue = _MAX_PATH * sizeof(TCHAR);
DWORD dwType = 0;
if(ERROR_SUCCESS != RegQueryValueEx(hKeyVSPaths, DEVENV_VALUE, NULL, &dwType, (LPBYTE)szDevEnvLong, &cbValue))
__leave;
if(dwType != REG_SZ)
__leave;
GetShortPathName(szDevEnvLong, szDevEnv, _MAX_PATH);
// make BSTR for devenv.exe path
if(NULL == (bstrDevEnvPath = AllocBSTR(szDevEnv)))
__leave;
// Make lower case
if(FAILED(ToLowerBSTR(&bstrDevEnvPath)))
__leave;
// If current process is not the registered DevEnv.exe, we will 'fail'
if(0 != wcscmp(bstrModuleName, bstrDevEnvPath))
__leave;
hr = S_OK;
}
__finally
{
if(bstrProgIdLowerCase)
SysFreeString(bstrProgIdLowerCase);
if(bstrModuleName)
SysFreeString(bstrModuleName);
if(bstrDevEnvPath)
SysFreeString(bstrDevEnvPath);
if(hKeyVSPaths)
RegCloseKey(hKeyVSPaths);
}
return hr;
}
// Makes sure the string starts with the specified string
// On success, it updates the passed in pointer to point to the next character
HRESULT StartsWith(LPCWSTR *ppsz, LPCWSTR pszTest)
{
int len = wcslen(pszTest);
if(0 != wcsncmp(*ppsz, pszTest, len))
return E_FAIL;
*ppsz += len;
return S_OK;
}
// Makes sure the next character is 0 through 9, and updates the input pointer
// by one character
HRESULT NextCharacterIsDigit(LPCWSTR *ppsz)
{
WCHAR c = **ppsz;
if(c < L'0' || c > L'9')
return E_FAIL;
(*ppsz)++;
return S_OK;
}
// For a special case, where the crippled criteria are met and we are dealking
// with a well known document, we will hard code acceptance of control creation
// This method tests if the crippled critera are met, and if this is a well
// known document
HRESULT IsWellKnownHostDocument(IUnknown *pUnk, BSTR strProgId)
{
HRESULT hr = E_FAIL;
IHTMLDocument2 *pDoc = NULL;
IHTMLDocument2 *pParentDoc = NULL;
BSTR bstrURL = NULL;
BSTR bstrDocumentFile = NULL;
BSTR bstrVCPath = NULL;
HKEY hKey = NULL;
__try
{
// Make sure the crippled criteria are met. In other words, we are
// running in a known instance of devenv.exe, and we are requesting a
// known ProgId
if(FAILED(AreCrippledCriteriaMet(strProgId)))
__leave;
// Get the HTML Document
if(FAILED(GetDocument(pUnk, &pDoc)))
__leave;
// If there is a parent document, this is not a well know document
if(NULL != (pParentDoc = GetParentDocument(pDoc)))
__leave;
// Get the URL of the document
if(FAILED(pDoc->get_URL(&bstrURL)))
__leave;
// Make sure the well known document canidate is on the local hard drive
if(!IsURLLocal(bstrURL))
__leave;
// Open the registry key to get the VC path
if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,strVCPathKey,0,KEY_QUERY_VALUE,&hKey))
__leave;
TCHAR szVCPath[_MAX_PATH];
TCHAR szVCPathURL[_MAX_PATH*2];
DWORD cbValue = _MAX_PATH * sizeof(TCHAR);
DWORD dwType = 0;
if(ERROR_SUCCESS != RegQueryValueEx(hKey, VC_PRODUCTDIR_VALUE, NULL, &dwType, (LPBYTE)szVCPath, &cbValue))
__leave;
if(dwType != REG_SZ)
__leave;
// Canonicalize the VC path
cbValue = _MAX_PATH*2; // Length in TCHARs of szVCPathURL
if(!InternetCanonicalizeUrl(szVCPath, szVCPathURL, &cbValue, 0))
__leave;
// make BSTR for devenv.exe path
if(NULL == (bstrVCPath = AllocBSTR(szVCPathURL)))
__leave;
// Make lower case
if(FAILED(ToLowerBSTR(&bstrVCPath)))
__leave;
// Make document path lower case
if(FAILED(ToLowerBSTR(&bstrURL)))
__leave;
LPCWSTR szStartDoc = bstrURL;
// Make sure we start with the correct VC directory
if(FAILED(StartsWith(&szStartDoc, bstrVCPath)))
__leave;
// Make sure we next have "VCWizards\ClassWiz\ATL\"
if(FAILED(StartsWith(&szStartDoc, L"vcwizards\\classwiz\\atl\\")))
__leave;
// Make sure we next have 'event\' or 'instance\'
if(FAILED(StartsWith(&szStartDoc, L"event\\")) && FAILED(StartsWith(&szStartDoc, L"instance\\")))
__leave;
// Make sure we next have "html\"
if(FAILED(StartsWith(&szStartDoc, L"html\\")))
__leave;
// Make sure the next four characters are numbers
if(FAILED(NextCharacterIsDigit(&szStartDoc)))
__leave;
if(FAILED(NextCharacterIsDigit(&szStartDoc)))
__leave;
if(FAILED(NextCharacterIsDigit(&szStartDoc)))
__leave;
if(FAILED(NextCharacterIsDigit(&szStartDoc)))
__leave;
// Make sure what's left is '\wmiclass.htm'
if(0 != wcscmp(szStartDoc, L"\\wmiclass.htm"))
__leave;
hr = S_OK;
}
__finally
{
if(pDoc)
pDoc->Release();
if(pParentDoc)
pParentDoc->Release();
if(bstrURL)
SysFreeString(bstrURL);
if(bstrDocumentFile)
SysFreeString(bstrDocumentFile);
if(bstrVCPath)
SysFreeString(bstrVCPath);
if(hKey)
RegCloseKey(hKey);
}
return hr;
}
// For a given instance of an ActiveXControl (specified by pUnk), see if it is
// permitted to create the object specified by bstrProgId. This is done by
// verifying that the control was created in an allowed HTML document.
HRESULT IsCreateObjectAllowed(IUnknown *pUnk, BSTR strProgId, BSTR *pstrValueName)
{
BSTR bstrFullLocation = NULL;
HRESULT hr = E_FAIL;
HKEY hKey = NULL;
LPTSTR pszValueName = NULL;
LPTSTR pszValue = NULL;
__try
{
BSTR bstrHash = NULL;
// Make sure the crippled criteria are met
if(FAILED(AreCrippledCriteriaMet(strProgId)))
__leave;
// We are going to hard code a specific set of conditions that are
// allowed. We will only do this if pstrValueName is NULL (which
// happens during CreateObject and CanCreateObject).
// NOTE: this performs a redundant check to make sure the crippled
// criteria are met
if(FAILED(IsWellKnownHostDocument(pUnk, strProgId)))
{
__leave;
}
// Get the full location
if(NULL == (bstrFullLocation = GetFullLocation(pUnk, strProgId, &bstrHash)))
__leave;
SysFreeString(bstrHash);
// Make sure we don't have a zero length string
if(0 == SysStringLen(bstrFullLocation))
__leave;
// Open the registry key to see if this full location is registered
if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,SAFE_LOCAL_SCRIPTS_KEY,0,KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE ,&hKey))
__leave;
// Get info on the max lenghts of values in this key
DWORD cValues, cMaxValueNameLen, cMaxValueLen;
if(ERROR_SUCCESS != RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &cValues, &cMaxValueNameLen, &cMaxValueLen, NULL, NULL))
__leave;
// Allocate space for the value name
if(NULL == (pszValueName = new TCHAR[cMaxValueNameLen + 1]))
__leave;
// Allocate space for the value (this may be twice as big as necessary in UNICODE)
if(NULL == (pszValue = new TCHAR[cMaxValueLen + 1]))
__leave;
for(DWORD dw = 0;dw<cValues;dw++)
{
DWORD cValueNameLen = cMaxValueNameLen+1;
DWORD cbData = (cMaxValueLen+1)*sizeof(TCHAR);
DWORD dwType;
if(ERROR_SUCCESS != RegEnumValue(hKey, dw, pszValueName, &cValueNameLen, NULL, &dwType, (LPBYTE)pszValue, &cbData))
continue;
if(dwType != REG_SZ)
continue;
BSTR bstrValue = AllocBSTR(pszValue);
if(!bstrValue)
continue;
// SEE IF WE HAVE A MATCH
if(0 == wcscmp(bstrFullLocation, bstrValue))
{
// Return the ValueName if requested
if(pstrValueName)
{
*pstrValueName = AllocBSTR(pszValueName);
}
hr = S_OK;
}
SysFreeString(bstrValue);
if(SUCCEEDED(hr))
__leave; // WE FOUND A MATCH
}
}
__finally
{
if(bstrFullLocation)
SysFreeString(bstrFullLocation);
if(hKey)
RegCloseKey(hKey);
if(pszValueName)
delete [] pszValueName;
if(pszValue)
delete [] pszValue;
}
return hr;
}
// This function will register the location of the current ActiveX control
// (specified by pUnk) to be allowed to create objects of type strProgId
HRESULT RegisterCurrentDoc(IUnknown *pUnk, BSTR strProgId)
{
USES_CONVERSION;
HRESULT hr = E_FAIL;
BSTR bstrFullLocation = NULL;
LPTSTR pszFullLocation = NULL;
HKEY hKey = NULL;
__try
{
// Make sure the crippled criteria are met
if(FAILED(AreCrippledCriteriaMet(strProgId)))
__leave;
// See if we are already registered
if(SUCCEEDED(IsCreateObjectAllowed(pUnk, strProgId, NULL)))
{
hr = S_OK;
__leave;
}
// TODO: Maybe reuse some of the code from IsCreateObjectAllowed
BSTR bstrHash = NULL;
// Get the full location
if(NULL == (bstrFullLocation = GetFullLocation(pUnk, strProgId, &bstrHash)))
__leave;
SysFreeString(bstrHash);
// Make sure we don't have a zero length string
if(0 == SysStringLen(bstrFullLocation))
__leave;
if(bstrFullLocation != NULL)
{
#ifdef _UNICODE
pszFullLocation = bstrFullLocation;
#else
pszFullLocation = new TCHAR[SysStringLen(bstrFullLocation) + 1];
if(pszFullLocation == NULL)
__leave;
if(0 == WideCharToMultiByte(CP_ACP, 0, bstrFullLocation, -1, pszFullLocation, (SysStringLen(bstrFullLocation) + 1) * sizeof(TCHAR), NULL, NULL))
__leave;
#endif
}
if(NULL == pszFullLocation)
__leave;
// Create or open the registry key to store the registration
if(ERROR_SUCCESS != RegCreateKeyEx( HKEY_LOCAL_MACHINE,
SAFE_LOCAL_SCRIPTS_KEY,
0,
TEXT(""),
REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE | KEY_QUERY_VALUE,
NULL,
&hKey,
NULL))
__leave;
// Find an empty slot (no more than 1000 registrations
TCHAR sz[10];
for(int i=1;i<1000;i++)
{
StringCchPrintf(sz,sizeof(sz),TEXT("%i"),i);
DWORD cbValue;
if(ERROR_SUCCESS != RegQueryValueEx(hKey, sz, NULL, NULL, NULL, &cbValue))
break; // There is nothing in this slot
}
// See if we found a slot
if(i>=1000)
__leave;
// Register the location
if(ERROR_SUCCESS != RegSetValueEx(hKey, sz, 0, REG_SZ, (CONST BYTE *)pszFullLocation, (lstrlen(pszFullLocation) + 1) *sizeof(TCHAR)))
__leave;
// Registered!
hr = S_OK;
}
__finally
{
if(bstrFullLocation)
SysFreeString(bstrFullLocation);
#ifndef _UNICODE
if( pszFullLocation)
delete []pszFullLocation;
#endif
if(hKey)
RegCloseKey(hKey);
}
return hr;
}
// This function will remove any registration for the current document and
// strProgId
HRESULT UnRegisterCurrentDoc(IUnknown *pUnk, BSTR strProgId)
{
USES_CONVERSION;
BSTR bstrValueName = NULL;
// Make sure the crippled criteria are met
if(FAILED(AreCrippledCriteriaMet(strProgId)))
return E_FAIL;
HKEY hKey = NULL;
if(ERROR_SUCCESS != RegOpenKey(HKEY_LOCAL_MACHINE, SAFE_LOCAL_SCRIPTS_KEY, &hKey))
return E_FAIL;
// Make sure to remove ALL instances of this doc/strProgId in the registry
// NOTE: Each iteration of this loop allocates some space off of the stack
// for the conversion to ANSI (if not UNICODE build). This should not be a
// problem since there should not be too many keys ever registered with the
// same location.
while(SUCCEEDED(IsCreateObjectAllowed(pUnk, strProgId, &bstrValueName)) && bstrValueName)
{
LPTSTR szValueName = NULL;
if(FAILED(ConvertToTString(bstrValueName,&szValueName)))
{
SysFreeString(bstrValueName);
return E_FAIL;
}
SysFreeString(bstrValueName);
bstrValueName = NULL;
RegDeleteValue(hKey, szValueName);
delete [] szValueName;
}
RegCloseKey(hKey);
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
// VC 6.0 did not ship with header files that included the CONFIRMSAFETY
// definition.
#ifndef CONFIRMSAFETYACTION_LOADOBJECT
EXTERN_C const GUID GUID_CUSTOM_CONFIRMOBJECTSAFETY;
#define CONFIRMSAFETYACTION_LOADOBJECT 0x00000001
struct CONFIRMSAFETY
{
CLSID clsid;
IUnknown * pUnk;
DWORD dwFlags;
};
#endif
const GUID GUID_CUSTOM_CONFIRMOBJECTSAFETY =
{ 0x10200490, 0xfa38, 0x11d0, { 0xac, 0xe, 0x0, 0xa0, 0xc9, 0xf, 0xff, 0xc0 }};
///////////////////////////////////////////////////////////////////////////////
HRESULT SafeCreateObject(IUnknown *pUnkControl, BOOL fSafetyEnabled, CLSID clsid, IUnknown **ppUnk)
{
HRESULT hr = E_FAIL;
IInternetHostSecurityManager *pSecMan = NULL;
IServiceProvider *pServProv = NULL;
__try
{
if (fSafetyEnabled)
{
if(FAILED(hr = GetSiteServices(pUnkControl, &pServProv)))
__leave;
if(FAILED(hr = pServProv->QueryService(SID_SInternetHostSecurityManager, IID_IInternetHostSecurityManager, (void**)&pSecMan)))
__leave;
// Ask security manager if we can create objects.
DWORD dwPolicy = 0x12345678;
if(FAILED(hr = pSecMan->ProcessUrlAction(URLACTION_ACTIVEX_RUN, (BYTE *)&dwPolicy, sizeof(dwPolicy), (BYTE *)&clsid, sizeof(clsid), 0, 0)))
__leave;
// TODO: BUG: If we are loaded in an HTA, hr returns S_OK, but
// dwPolicy only has the first byte set to zero. See documentation
// for ProcessUrlAction.
// NOTE: This bug is caused by CClient::ProcessUrlAction in
// nt\private\inet\mshtml\src\other\htmlapp\site.cxx. This line
// uses *pPolicy = dwPolicy, but pPolicy is a BYTE * so only the
// first byte of the policy is copied to the output parameter.
// To fix this, we check for hr==S_OK (as opposed to S_FALSE), and
// see if dwPolicy is 0x12345600 (in other words, only the lower
// byte of dwPolicy was changed). As per the documentation, S_OK
// alone should be enough to assume the dwPolicy was
// URL_POLICY_ALLOW
if(S_OK == hr && 0x12345600 == dwPolicy)
dwPolicy = URLPOLICY_ALLOW;
if(URLPOLICY_ALLOW != dwPolicy)
{
hr = E_FAIL;
__leave;;
}
}
// Create the requested object
if (FAILED(hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)ppUnk)))
__leave;
if (fSafetyEnabled)
{
// Query the security manager to see if this object is safe to use.
DWORD dwPolicy, *pdwPolicy;
DWORD cbPolicy;
CONFIRMSAFETY csafe;
csafe.pUnk = *ppUnk;
csafe.clsid = clsid;
csafe.dwFlags = 0;
// csafe.dwFlags = (fWillLoad ? CONFIRMSAFETYACTION_LOADOBJECT : 0);
if(FAILED(hr = pSecMan->QueryCustomPolicy(GUID_CUSTOM_CONFIRMOBJECTSAFETY, (BYTE **)&pdwPolicy, &cbPolicy, (BYTE *)&csafe, sizeof(csafe), 0)))
__leave;
dwPolicy = URLPOLICY_DISALLOW;
if (NULL != pdwPolicy)
{
if (sizeof(DWORD) <= cbPolicy)
dwPolicy = *pdwPolicy;
CoTaskMemFree(pdwPolicy);
}
if(URLPOLICY_ALLOW != dwPolicy)
{
hr = E_FAIL;
__leave;
}
}
hr = S_OK;
}
__finally
{
// If we did not succeeded, we need to release the object we created (if any)
if(FAILED(hr) && (*ppUnk))
{
(*ppUnk)->Release();
*ppUnk = NULL;
}
if(pServProv)
pServProv->Release();
if(pSecMan)
pSecMan->Release();
}
return hr;
}
BOOL IsInternetHostSecurityManagerAvailable(IUnknown *pUnkControl)
{
HRESULT hr = E_FAIL;
IInternetHostSecurityManager *pSecMan = NULL;
IServiceProvider *pServProv = NULL;
__try
{
if(FAILED(hr = GetSiteServices(pUnkControl, &pServProv)))
__leave;
if(FAILED(hr = pServProv->QueryService(SID_SInternetHostSecurityManager, IID_IInternetHostSecurityManager, (void**)&pSecMan)))
__leave;
}
__finally
{
if(pServProv)
pServProv->Release();
if(pSecMan)
pSecMan->Release();
}
return SUCCEEDED(hr);
}
HRESULT SetVSInstallDirectory(IDispatch * pEnv)
{
HRESULT hr = E_FAIL;
DISPID dispid;
LPOLESTR szMember = OLESTR("RegistryRoot");;
VARIANT varResult;
VariantInit(&varResult);
TCHAR *pTemp = NULL;
DISPPARAMS dispParams;
dispParams.cArgs = 0;
dispParams.cNamedArgs = 0;
dispParams.rgvarg = NULL;
dispParams.rgdispidNamedArgs = NULL;
if(pEnv)
{
hr = pEnv->GetIDsOfNames(IID_NULL ,&szMember,1, LOCALE_SYSTEM_DEFAULT,&dispid);
if(SUCCEEDED(hr))
{
hr = pEnv->Invoke(dispid,IID_NULL,GetThreadLocale(),DISPATCH_PROPERTYGET,&dispParams,&varResult,NULL,NULL);
if(SUCCEEDED(hr))
{
hr = ConvertToTString(varResult.bstrVal,&pTemp);
if(SUCCEEDED(hr))
{
// Check if this is the standard VS Location in Registry
if(_tcsncmp(pTemp,VS_VER_INDEPENDANT_PATH_KEY,_tcslen(VS_VER_INDEPENDANT_PATH_KEY)) == 0)
{
StringCchCopy(strVSPathKey,MAX_PATH * 2,pTemp);
StringCchCopy(strVCPathKey,MAX_PATH * 2,pTemp);
StringCchCat(strVSPathKey,MAX_PATH * 2,TEXT("\\Setup\\VS"));
StringCchCat(strVCPathKey,MAX_PATH * 2,TEXT("\\Setup\\VC"));
}
else
{
hr = E_FAIL;
}
delete [] pTemp;
}
VariantClear(&varResult);
}
}
}
return hr;
}
HRESULT ConvertToTString(BSTR strPath,TCHAR **ppStr)
{
HRESULT hr = E_OUTOFMEMORY;
#ifdef _UNICODE
*ppStr = new TCHAR[SysStringLength(strPath) + 1];
if(*ppStr)
{
StringCchCopy(*ppStr,sizeof(*ppStr),strPath);
hr = S_OK;
}
#else
*ppStr = new TCHAR[SysStringLen(strPath) + 1];
if(WideCharToMultiByte(CP_ACP, 0, strPath, -1, *ppStr, (SysStringLen(strPath) + 1) * sizeof(TCHAR), NULL, NULL))
{
hr = S_OK;
}
else
delete [] *ppStr;
#endif
return hr;
}