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.
1214 lines
30 KiB
1214 lines
30 KiB
// Util.cpp : Implementation of CUtil
|
|
#include "stdafx.h"
|
|
#include "CompatUI.h"
|
|
#include "Util.h"
|
|
#include <shlobj.h>
|
|
#include <shellapi.h>
|
|
#include <shlwapi.h>
|
|
#include <msi.h>
|
|
#include <sfc.h>
|
|
#include <wininet.h>
|
|
#include "Aclapi.h"
|
|
|
|
extern "C" {
|
|
#include "shimdb.h"
|
|
}
|
|
|
|
#pragma warning(disable:4786)
|
|
#include <string>
|
|
#include <xstring>
|
|
#include <map>
|
|
#include <locale>
|
|
#include <algorithm>
|
|
#include <vector>
|
|
using namespace std;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CUtil
|
|
extern "C"
|
|
VOID
|
|
InvalidateAppcompatCacheEntry(
|
|
LPCWSTR pwszDosPath
|
|
);
|
|
|
|
BOOL
|
|
GetExePathFromObject(
|
|
LPCTSTR lpszPath, // path to an arbitrary object
|
|
CComBSTR& bstrExePath
|
|
);
|
|
|
|
|
|
typedef
|
|
INSTALLSTATE (WINAPI*PMsiGetComponentPath)(
|
|
LPCTSTR szProduct, // product code for client product
|
|
LPCTSTR szComponent, // component ID
|
|
LPTSTR lpPathBuf, // returned path
|
|
DWORD *pcchBuf // buffer character count
|
|
);
|
|
|
|
typedef
|
|
UINT (WINAPI* PMsiGetShortcutTarget)(
|
|
LPCTSTR szShortcutTarget, // path to shortcut link file
|
|
LPTSTR szProductCode, // fixed length buffer for product code
|
|
LPTSTR szFeatureId, // fixed length buffer for feature id
|
|
LPTSTR szComponentCode // fixed length buffer for component code
|
|
);
|
|
|
|
BOOL
|
|
IsUserAdmin(
|
|
void
|
|
);
|
|
|
|
BOOL
|
|
GiveUsersWriteAccess(
|
|
void
|
|
);
|
|
|
|
BOOL
|
|
IsLUAEnabled(
|
|
LPCWSTR pszLayers
|
|
);
|
|
|
|
wstring
|
|
StrUpCase(
|
|
wstring& wstr
|
|
);
|
|
|
|
BOOL
|
|
ShimExpandEnvironmentVars(
|
|
LPCTSTR lpszCmd,
|
|
CComBSTR& bstr
|
|
)
|
|
{
|
|
DWORD dwLength;
|
|
LPTSTR lpBuffer = NULL;
|
|
BOOL bExpanded = FALSE;
|
|
if (_tcschr(lpszCmd, TEXT('%')) == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
dwLength = ExpandEnvironmentStrings(lpszCmd, NULL, 0);
|
|
if (!dwLength) {
|
|
goto out;
|
|
}
|
|
|
|
lpBuffer = new TCHAR[dwLength];
|
|
if (NULL == lpBuffer) {
|
|
goto out;
|
|
}
|
|
|
|
dwLength = ExpandEnvironmentStrings(lpszCmd, lpBuffer, dwLength);
|
|
if (!dwLength) {
|
|
goto out;
|
|
}
|
|
bstr = lpBuffer;
|
|
bExpanded = TRUE;
|
|
|
|
out:
|
|
if (!bExpanded) {
|
|
bstr = lpszCmd;
|
|
}
|
|
if (lpBuffer) {
|
|
delete[] lpBuffer;
|
|
}
|
|
return bExpanded;
|
|
}
|
|
|
|
wstring
|
|
ShimUnquotePath(
|
|
LPCTSTR pwszFileName
|
|
)
|
|
{
|
|
wstring sFileName;
|
|
LPCTSTR pScan = pwszFileName;
|
|
LPCTSTR pQuote;
|
|
LPCTSTR pLastQuote = NULL;
|
|
|
|
// skip over the leading spaces
|
|
pScan += _tcsspn(pScan, TEXT(" \t"));
|
|
while (*pScan) {
|
|
pQuote = _tcschr(pScan, TEXT('\"'));
|
|
if (NULL == pQuote) {
|
|
sFileName += pScan;
|
|
break;
|
|
}
|
|
//
|
|
// we found a quote
|
|
// is this the first quote we've found?
|
|
if (pLastQuote == NULL) {
|
|
pLastQuote = pQuote;
|
|
// add the current string
|
|
sFileName += wstring(pScan, (int)(pQuote-pScan));
|
|
} else {
|
|
// we have a closing quote
|
|
++pLastQuote;
|
|
sFileName += wstring(pLastQuote, (int)(pQuote-pLastQuote));
|
|
pLastQuote = NULL;
|
|
}
|
|
|
|
pScan = pQuote + 1;
|
|
|
|
}
|
|
|
|
return sFileName;
|
|
}
|
|
|
|
//
|
|
// ShimGetPathFromCmdLine
|
|
//
|
|
|
|
BOOL
|
|
ShimGetPathFromCmdLine(
|
|
LPCTSTR pwszCmdLine,
|
|
CComBSTR& StrPath
|
|
)
|
|
{
|
|
TCHAR chSave;
|
|
LPTSTR pScan = (LPTSTR)pwszCmdLine;
|
|
LPTSTR pAppName;
|
|
TCHAR szBuffer[MAX_PATH];
|
|
DWORD dwLength;
|
|
DWORD dwAttributes;
|
|
BOOL bScan = FALSE;
|
|
CComBSTR bstrPathName;
|
|
|
|
if (*pScan == TEXT('\"')) {
|
|
// seek till we find matching "
|
|
pAppName = ++pScan;
|
|
while (*pScan) {
|
|
if (*pScan == TEXT('\"')) {
|
|
break;
|
|
}
|
|
++pScan;
|
|
}
|
|
} else {
|
|
pAppName = pScan;
|
|
while (*pScan) {
|
|
if (_istspace(*pScan)) {
|
|
bScan = TRUE;
|
|
break;
|
|
}
|
|
++pScan;
|
|
}
|
|
}
|
|
|
|
while (TRUE) {
|
|
|
|
chSave = *pScan;
|
|
*pScan = TEXT('\0');
|
|
|
|
ShimExpandEnvironmentVars(pAppName, bstrPathName);
|
|
|
|
//
|
|
// check whether bstrPathName is valid (failure due to memory allocation is possible)
|
|
//
|
|
if (!bstrPathName) {
|
|
//
|
|
// overloaded operator ! for CComBSTR will check whether bstrPathName == NULL
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check this path
|
|
//
|
|
dwLength = SearchPathW(NULL,
|
|
bstrPathName,
|
|
TEXT(".exe"),
|
|
CHARCOUNT(szBuffer),
|
|
szBuffer,
|
|
NULL);
|
|
//
|
|
// restore the character
|
|
//
|
|
*pScan = chSave;
|
|
|
|
if (dwLength && dwLength < CHARCOUNT(szBuffer)) {
|
|
//
|
|
// check attributes
|
|
//
|
|
dwAttributes = GetFileAttributesW(szBuffer);
|
|
if ((dwAttributes != (DWORD)-1) &&
|
|
!(dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
//
|
|
// we are mighty done
|
|
//
|
|
StrPath = (LPCWSTR)szBuffer;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (!bScan || *pScan == TEXT('\0')) {
|
|
break;
|
|
}
|
|
|
|
++pScan;
|
|
while (*pScan) {
|
|
if (_istspace(*pScan)) {
|
|
break;
|
|
}
|
|
++pScan;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// This class allows us to change error mode on selected apis
|
|
//
|
|
//
|
|
|
|
class CSaveErrorMode {
|
|
public:
|
|
CSaveErrorMode() {
|
|
m_uiErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
}
|
|
~CSaveErrorMode() {
|
|
SetErrorMode(m_uiErrorMode);
|
|
}
|
|
protected:
|
|
UINT m_uiErrorMode;
|
|
};
|
|
|
|
|
|
STDMETHODIMP CUtil::IsCompatWizardDisabled(BOOL* pbDisabled)
|
|
{
|
|
HKEY hKey;
|
|
LONG lResult;
|
|
DWORD dwValue, dwSize = sizeof(dwValue);
|
|
DWORD dwType;
|
|
|
|
//
|
|
// First, check for the whole engine being disabled.
|
|
//
|
|
lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
POLICY_KEY_APPCOMPAT_W,
|
|
0,
|
|
KEY_READ,
|
|
&hKey);
|
|
if (lResult == ERROR_SUCCESS) {
|
|
dwValue = 0;
|
|
lResult = RegQueryValueExW(hKey,
|
|
POLICY_VALUE_DISABLE_ENGINE_W,
|
|
0,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
&dwSize);
|
|
RegCloseKey (hKey);
|
|
}
|
|
|
|
//
|
|
// The default is enabled, so if we didn't find a value, treat it like the value is 0.
|
|
//
|
|
if (lResult != ERROR_SUCCESS || dwValue == 0) {
|
|
//
|
|
// Check for the proppage being disabled.
|
|
//
|
|
lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
POLICY_KEY_APPCOMPAT_W,
|
|
0,
|
|
KEY_READ,
|
|
&hKey);
|
|
if (lResult == ERROR_SUCCESS) {
|
|
dwValue = 0;
|
|
lResult = RegQueryValueExW(hKey,
|
|
POLICY_VALUE_DISABLE_WIZARD_W,
|
|
0,
|
|
&dwType,
|
|
(LPBYTE) &dwValue, &dwSize);
|
|
RegCloseKey (hKey);
|
|
}
|
|
|
|
//
|
|
// The default is to be enabled, so if we didn't find a value, or the value is 0, then we're good to go.
|
|
//
|
|
}
|
|
|
|
|
|
*pbDisabled = (lResult == ERROR_SUCCESS) && (dwValue != 0);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
STDMETHODIMP CUtil::RemoveArgs(BSTR pVar, VARIANT* pRet)
|
|
{
|
|
// TODO: Add your implementation code here
|
|
CComBSTR bstr;
|
|
CSaveErrorMode ErrMode;
|
|
|
|
if (!ShimGetPathFromCmdLine(pVar, bstr)) {
|
|
bstr = pVar;
|
|
}
|
|
|
|
pRet->vt = VT_BSTR;
|
|
pRet->bstrVal = bstr.Copy();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CUtil::GetItemKeys(BSTR pVar, VARIANT *pRet)
|
|
{
|
|
CComBSTR bstr = pVar;
|
|
CComBSTR bstrOut;
|
|
LPWSTR pwszPermKeys;
|
|
DWORD cbSize;
|
|
BOOL bLayers;
|
|
|
|
if (!m_Safe) {
|
|
return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
bLayers = SdbGetPermLayerKeys(bstr, NULL, &cbSize, GPLK_ALL);
|
|
if (bLayers) {
|
|
pwszPermKeys = new WCHAR[cbSize / sizeof(WCHAR)];
|
|
if (pwszPermKeys == NULL) {
|
|
return HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
|
|
}
|
|
|
|
bLayers = SdbGetPermLayerKeys(bstr, pwszPermKeys, &cbSize, GPLK_ALL);
|
|
if (bLayers) {
|
|
bstrOut = pwszPermKeys;
|
|
}
|
|
delete [] pwszPermKeys;
|
|
}
|
|
|
|
if (!bstrOut) {
|
|
pRet->vt = VT_NULL;
|
|
} else {
|
|
pRet->vt = VT_BSTR;
|
|
pRet->bstrVal = bstrOut.Copy();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CUtil::SetItemKeys(BSTR pszPath, VARIANT* pKeys, VARIANT* pKeysMachine, BOOL *pVal)
|
|
{
|
|
// TODO: Add your implementation code here
|
|
CComBSTR bstrKeys;
|
|
CComBSTR bstrKeysMachine;
|
|
BOOL bSuccess = TRUE;
|
|
VARIANT varKeys;
|
|
VARIANT varKeysMachine;
|
|
|
|
if (!m_Safe) {
|
|
return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
|
|
InvalidateAppcompatCacheEntry(pszPath);
|
|
|
|
if (pKeys->vt == VT_NULL || pKeys->vt == VT_EMPTY) {
|
|
bSuccess &= SdbDeletePermLayerKeys(pszPath, FALSE);
|
|
} else {
|
|
VariantInit(&varKeys);
|
|
if (SUCCEEDED(VariantChangeType(&varKeys, pKeys, 0, VT_BSTR))) {
|
|
bstrKeys = varKeys.bstrVal;
|
|
VariantClear(&varKeys);
|
|
}
|
|
|
|
if (bstrKeys) {
|
|
bSuccess &= SdbSetPermLayerKeys(pszPath, bstrKeys, FALSE);
|
|
}
|
|
}
|
|
|
|
if (IsUserAdmin()) {
|
|
if (pKeysMachine->vt == VT_NULL || pKeysMachine->vt == VT_EMPTY) {
|
|
bSuccess &= SdbDeletePermLayerKeys(pszPath, TRUE);
|
|
} else {
|
|
VariantInit(&varKeysMachine);
|
|
if (SUCCEEDED(VariantChangeType(&varKeysMachine, pKeysMachine, 0, VT_BSTR))) {
|
|
bstrKeysMachine = varKeysMachine.bstrVal;
|
|
VariantClear(&varKeysMachine);
|
|
}
|
|
if (bstrKeysMachine) {
|
|
bSuccess &= SdbSetPermLayerKeys(pszPath, bstrKeysMachine, TRUE);
|
|
//
|
|
// find whether we need to have LUA setting handled by granting write access to a common area
|
|
//
|
|
if (IsLUAEnabled(bstrKeysMachine)) {
|
|
GiveUsersWriteAccess();
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
*pVal = bSuccess;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL
|
|
SearchGroupForSID(
|
|
DWORD dwGroup,
|
|
BOOL* pfIsMember
|
|
)
|
|
{
|
|
PSID pSID = NULL;
|
|
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
|
|
BOOL fRes = TRUE;
|
|
|
|
if (!AllocateAndInitializeSid(&SIDAuth,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
dwGroup,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
&pSID)) {
|
|
ATLTRACE(_T("[SearchGroupForSID] AllocateAndInitializeSid failed 0x%lx\n"),
|
|
GetLastError());
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (!CheckTokenMembership(NULL, pSID, pfIsMember)) {
|
|
ATLTRACE(_T("[SearchGroupForSID] CheckTokenMembership failed 0x%x\n"),
|
|
GetLastError());
|
|
fRes = FALSE;
|
|
}
|
|
|
|
FreeSid(pSID);
|
|
|
|
return fRes;
|
|
}
|
|
|
|
BOOL
|
|
IsUserAdmin(
|
|
void
|
|
)
|
|
{
|
|
BOOL fIsUser, fIsAdmin, fIsPowerUser;
|
|
|
|
if (!SearchGroupForSID(DOMAIN_ALIAS_RID_USERS, &fIsUser) ||
|
|
!SearchGroupForSID(DOMAIN_ALIAS_RID_ADMINS, &fIsAdmin)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return (fIsUser && fIsAdmin);
|
|
}
|
|
|
|
|
|
BOOL
|
|
GiveUsersWriteAccess(
|
|
void
|
|
)
|
|
{
|
|
DWORD dwRes;
|
|
EXPLICIT_ACCESS ea;
|
|
PACL pOldDACL;
|
|
PACL pNewDACL = NULL;
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
|
|
PSID pUsersSID = NULL;
|
|
TCHAR szDir[MAX_PATH];
|
|
|
|
ExpandEnvironmentStrings(LUA_REDIR_W, szDir, MAX_PATH);
|
|
|
|
if (!CreateDirectory(szDir, NULL)) {
|
|
DWORD err = GetLastError();
|
|
|
|
if (GetLastError() != ERROR_ALREADY_EXISTS) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
dwRes = GetNamedSecurityInfo(szDir,
|
|
SE_FILE_OBJECT,
|
|
DACL_SECURITY_INFORMATION,
|
|
NULL,
|
|
NULL,
|
|
&pOldDACL,
|
|
NULL,
|
|
&pSD);
|
|
|
|
if (ERROR_SUCCESS != dwRes) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!AllocateAndInitializeSid(&SIDAuth,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_USERS,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
&pUsersSID) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize an EXPLICIT_ACCESS structure for the new ACE.
|
|
//
|
|
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
|
|
|
|
ea.grfAccessPermissions = FILE_GENERIC_WRITE | FILE_GENERIC_READ | DELETE;
|
|
ea.grfAccessMode = GRANT_ACCESS;
|
|
ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
|
|
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
|
|
ea.Trustee.ptstrName = (LPTSTR)pUsersSID;
|
|
|
|
//
|
|
// Create a new ACL that merges the new ACE
|
|
// into the existing DACL.
|
|
//
|
|
dwRes = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL);
|
|
|
|
if (ERROR_SUCCESS != dwRes) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwRes = SetNamedSecurityInfo(szDir,
|
|
SE_FILE_OBJECT,
|
|
DACL_SECURITY_INFORMATION,
|
|
NULL,
|
|
NULL,
|
|
pNewDACL,
|
|
NULL);
|
|
|
|
if (ERROR_SUCCESS != dwRes) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (pSD) {
|
|
LocalFree(pSD);
|
|
}
|
|
|
|
if (pUsersSID) {
|
|
FreeSid(pUsersSID);
|
|
}
|
|
|
|
if (pNewDACL) {
|
|
LocalFree(pNewDACL);
|
|
}
|
|
|
|
return (dwRes == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL
|
|
IsLUAEnabled(
|
|
LPCWSTR pszLayers
|
|
)
|
|
{
|
|
LPCWSTR pchStart = pszLayers;
|
|
LPCWSTR pch;
|
|
wstring strTok;
|
|
BOOL bLUAEnabled = FALSE;
|
|
|
|
pchStart += wcsspn(pchStart, L"#!");
|
|
|
|
while (pchStart != NULL && *pchStart != L'\0' && !bLUAEnabled) {
|
|
pchStart += wcsspn(pchStart, L" \t");
|
|
if (*pchStart == L'\0') {
|
|
break;
|
|
}
|
|
|
|
pch = wcspbrk(pchStart, L" \t");
|
|
if (pch == NULL) {
|
|
strTok = pchStart;
|
|
pchStart = NULL;
|
|
} else {
|
|
strTok = wstring(pchStart, (size_t)(pch - pchStart));
|
|
StrUpCase(strTok);
|
|
pchStart = pch;
|
|
}
|
|
bLUAEnabled = (strTok == L"LUA");
|
|
}
|
|
|
|
return bLUAEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
// this method returns true when we should present the LUA checkbox and
|
|
// false when we should not
|
|
|
|
STDMETHODIMP CUtil::CheckAdminPrivileges(BOOL *pVal)
|
|
{
|
|
*pVal = IsUserAdmin();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CUtil::RunApplication(BSTR pLayers, BSTR pszCmdLine, BOOL bEnableLog, DWORD* pResult)
|
|
{
|
|
|
|
LPCWSTR pszExt;
|
|
BOOL bShellExecute = FALSE;
|
|
BOOL bSuccess = FALSE;
|
|
DWORD dwError = 0;
|
|
DWORD dwBinaryType = 0;
|
|
DWORD dwCreateProcessFlags = 0;
|
|
CSaveErrorMode ErrMode;
|
|
|
|
if (!m_Safe) {
|
|
return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
|
|
CComBSTR bstrAppName = pszCmdLine;
|
|
ShimGetPathFromCmdLine(pszCmdLine, bstrAppName);
|
|
|
|
CComBSTR bstrExePath;
|
|
|
|
if (!bstrAppName) {
|
|
dwError = ERROR_BAD_PATHNAME;
|
|
goto Done;
|
|
}
|
|
|
|
|
|
// check if we are using .lnk file
|
|
|
|
pszExt = PathFindExtension(bstrAppName);
|
|
if (pszExt != NULL) {
|
|
bShellExecute = !_tcsicmp(pszExt, TEXT(".lnk"));
|
|
}
|
|
|
|
if (!bShellExecute) { // not shell exec? check the binary
|
|
if (!pszExt || (_tcsicmp(pszExt, TEXT(".cmd")) && _tcsicmp(pszExt, TEXT(".bat")))) {
|
|
bShellExecute = !GetBinaryTypeW(bstrAppName, &dwBinaryType);
|
|
if (!bShellExecute && (dwBinaryType == SCS_WOW_BINARY || dwBinaryType == SCS_PIF_BINARY)) {
|
|
dwCreateProcessFlags |= CREATE_SEPARATE_WOW_VDM;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// invalidate cache
|
|
//
|
|
if (::GetExePathFromObject(bstrAppName, bstrExePath)) {
|
|
InvalidateAppcompatCacheEntry(bstrExePath);
|
|
}
|
|
|
|
|
|
SetEnvironmentVariable(TEXT("__COMPAT_LAYER"), pLayers);
|
|
SetEnvironmentVariable(TEXT("SHIM_FILE_LOG"), bEnableLog ? TEXT("shim.log") : NULL);
|
|
|
|
//
|
|
// now we shall either ShellExecute or do a CreateProcess
|
|
//
|
|
if (bShellExecute) {
|
|
int nLength = bstrAppName.Length() + 3;
|
|
LPTSTR pszCmdLineShellExec = new TCHAR[nLength];
|
|
SHELLEXECUTEINFO ShExecInfo = { 0 };
|
|
|
|
if (pszCmdLineShellExec == NULL) {
|
|
//
|
|
// out of memory, just do a create process
|
|
//
|
|
goto HandleCreateProcess;
|
|
}
|
|
nLength = _sntprintf(pszCmdLineShellExec,
|
|
nLength,
|
|
TEXT("\"%s\""),
|
|
(LPCTSTR)bstrAppName);
|
|
if (nLength < 0) {
|
|
delete[] pszCmdLineShellExec;
|
|
goto HandleCreateProcess;
|
|
}
|
|
|
|
ShExecInfo.cbSize = sizeof(ShExecInfo);
|
|
ShExecInfo.fMask = 0; //SEE_MASK_FLAG_NO_UI;
|
|
ShExecInfo.lpVerb = L"open";
|
|
ShExecInfo.lpFile = pszCmdLineShellExec;
|
|
ShExecInfo.nShow = SW_SHOW;
|
|
|
|
bSuccess = ShellExecuteEx(&ShExecInfo);
|
|
if (!bSuccess || (int)((INT_PTR)ShExecInfo.hInstApp) < 32) {
|
|
dwError = GetLastError();
|
|
}
|
|
delete[] pszCmdLineShellExec;
|
|
|
|
|
|
} else {
|
|
HandleCreateProcess:
|
|
|
|
// get working directory
|
|
TCHAR szWorkingDir[MAX_PATH];
|
|
LPTSTR pAppName = (LPTSTR)bstrAppName.m_str;
|
|
int nLength;
|
|
LPTSTR pszWorkingDir = NULL;
|
|
STARTUPINFO StartupInfo;
|
|
PROCESS_INFORMATION ProcessInfo;
|
|
|
|
LPTSTR pBackslash = _tcsrchr(pAppName, TEXT('\\'));
|
|
if (pBackslash != NULL) {
|
|
//
|
|
// check for root
|
|
//
|
|
nLength = (int)(pBackslash - pAppName);
|
|
if (nLength == 2 && pAppName[1] == TEXT(':')) {
|
|
++nLength;
|
|
}
|
|
_tcsncpy(szWorkingDir, pAppName, nLength);
|
|
szWorkingDir[nLength] = TEXT('\0');
|
|
pszWorkingDir = szWorkingDir;
|
|
}
|
|
|
|
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
|
|
StartupInfo.cb = sizeof(StartupInfo);
|
|
ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
|
|
|
|
bSuccess = CreateProcess(NULL,
|
|
pszCmdLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
pszWorkingDir,
|
|
&StartupInfo,
|
|
&ProcessInfo);
|
|
|
|
if (!bSuccess) {
|
|
dwError = GetLastError();
|
|
}
|
|
|
|
if (ProcessInfo.hThread) {
|
|
CloseHandle(ProcessInfo.hThread);
|
|
}
|
|
if (ProcessInfo.hProcess) {
|
|
CloseHandle(ProcessInfo.hProcess);
|
|
}
|
|
}
|
|
|
|
Done:
|
|
|
|
*pResult = dwError;
|
|
|
|
SetEnvironmentVariable(TEXT("__COMPAT_LAYER"), NULL);
|
|
SetEnvironmentVariable(TEXT("SHIM_FILE_LOG") , NULL);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL
|
|
GetExePathFromLink(
|
|
LPMALLOC pMalloc,
|
|
LPCITEMIDLIST pidlLinkFull,
|
|
LPCTSTR pszLinkFile,
|
|
LPTSTR pszExePath,
|
|
DWORD dwBufferSize
|
|
)
|
|
{
|
|
IShellLink* psl = NULL;
|
|
WIN32_FIND_DATA wfd;
|
|
HRESULT hr;
|
|
BOOL bSuccess = FALSE;
|
|
TCHAR szPath[MAX_PATH];
|
|
IPersistFile* ipf = NULL;
|
|
IShellLinkDataList* pdl;
|
|
DWORD dwFlags = 0;
|
|
BOOL bMsiLink = FALSE;
|
|
HMODULE hMSI = NULL;
|
|
|
|
// first do the link
|
|
|
|
hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IShellLink, (LPVOID*)&psl);
|
|
if (!SUCCEEDED(hr)) {
|
|
return FALSE; // we can't create link object
|
|
}
|
|
|
|
hr = psl->SetIDList(pidlLinkFull); // set the id list
|
|
if (!SUCCEEDED(hr)) {
|
|
goto out;
|
|
}
|
|
|
|
hr = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ipf);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto out;
|
|
}
|
|
|
|
hr = ipf->Load(pszLinkFile, STGM_READ);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// resolve the link for now
|
|
//
|
|
// hr = psl->Resolve(NULL, SLR_NO_UI|SLR_NOUPDATE);
|
|
//
|
|
|
|
hr = psl->QueryInterface(IID_IShellLinkDataList, (LPVOID*)&pdl);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = pdl->GetFlags(&dwFlags);
|
|
|
|
bMsiLink = SUCCEEDED(hr) && (dwFlags & SLDF_HAS_DARWINID);
|
|
|
|
pdl->Release();
|
|
}
|
|
|
|
if (bMsiLink) {
|
|
//
|
|
// this is msi link, we need to crack it using msi
|
|
//
|
|
UINT ErrCode;
|
|
TCHAR szProduct [MAX_PATH];
|
|
TCHAR szFeatureId [MAX_PATH];
|
|
TCHAR szComponentCode[MAX_PATH];
|
|
INSTALLSTATE is;
|
|
|
|
hMSI = LoadLibrary(TEXT("Msi.dll"));
|
|
if (NULL == hMSI) {
|
|
goto out;
|
|
}
|
|
|
|
PMsiGetComponentPath pfnGetComponentPath = (PMsiGetComponentPath )GetProcAddress(hMSI, "MsiGetComponentPathW");
|
|
PMsiGetShortcutTarget pfnGetShortcutTarget = (PMsiGetShortcutTarget)GetProcAddress(hMSI, "MsiGetShortcutTargetW");
|
|
|
|
if (!pfnGetComponentPath || !pfnGetShortcutTarget)
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
ErrCode = pfnGetShortcutTarget(pszLinkFile,
|
|
szProduct,
|
|
szFeatureId,
|
|
szComponentCode);
|
|
if (ERROR_SUCCESS != ErrCode) {
|
|
goto out;
|
|
}
|
|
|
|
*pszExePath = TEXT('\0');
|
|
|
|
is = pfnGetComponentPath(szProduct, szComponentCode, pszExePath, &dwBufferSize);
|
|
bSuccess = (INSTALLSTATE_LOCAL == is);
|
|
|
|
} else {
|
|
|
|
hr = psl->GetPath(pszExePath, dwBufferSize, &wfd, 0);
|
|
bSuccess = SUCCEEDED(hr);
|
|
}
|
|
|
|
|
|
out:
|
|
if (hMSI) {
|
|
FreeLibrary(hMSI);
|
|
}
|
|
|
|
if (NULL != ipf) {
|
|
ipf->Release();
|
|
}
|
|
|
|
if (NULL != psl) {
|
|
psl->Release();
|
|
}
|
|
|
|
return bSuccess;
|
|
|
|
}
|
|
|
|
BOOL
|
|
GetExePathFromObject(
|
|
LPCTSTR lpszPath, // path to an arbitrary object
|
|
CComBSTR& bstrExePath
|
|
)
|
|
{
|
|
IShellFolder* psh = NULL;
|
|
LPMALLOC pMalloc = NULL;
|
|
HRESULT hr;
|
|
LPITEMIDLIST pidl = NULL;
|
|
DWORD dwAttributes;
|
|
CComBSTR bstr;
|
|
BOOL bSuccess = FALSE;
|
|
|
|
hr = SHGetMalloc(&pMalloc);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
hr = SHGetDesktopFolder(&psh);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
if (!ShimGetPathFromCmdLine(lpszPath, bstr)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// parse display name
|
|
//
|
|
dwAttributes = SFGAO_LINK | SFGAO_FILESYSTEM | SFGAO_VALIDATE;
|
|
|
|
hr = psh->ParseDisplayName(NULL,
|
|
NULL,
|
|
bstr, // path to the executable file
|
|
NULL, // number of chars eaten
|
|
&pidl,
|
|
&dwAttributes);
|
|
if (!SUCCEEDED(hr)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// display name parsed, check whether it's a link
|
|
//
|
|
if (dwAttributes & SFGAO_LINK) {
|
|
TCHAR szExePath[MAX_PATH];
|
|
//
|
|
// it's a link, crack it
|
|
//
|
|
bSuccess = GetExePathFromLink(pMalloc,
|
|
pidl,
|
|
bstr,
|
|
szExePath,
|
|
CHARCOUNT(szExePath));
|
|
// after recovering the path -- get the env vars expanded
|
|
if (bSuccess) {
|
|
ShimExpandEnvironmentVars(szExePath, bstrExePath);
|
|
}
|
|
|
|
} else if (dwAttributes & SFGAO_FILESYSTEM) {
|
|
//
|
|
// filesystem object (a file)
|
|
//
|
|
bstrExePath = bstr;
|
|
bSuccess = TRUE;
|
|
}
|
|
|
|
|
|
cleanup:
|
|
|
|
|
|
if (pidl) {
|
|
pMalloc->Free(pidl);
|
|
}
|
|
if (psh) {
|
|
psh->Release();
|
|
}
|
|
if (pMalloc) {
|
|
pMalloc->Release();
|
|
}
|
|
return bSuccess;
|
|
}
|
|
|
|
STDMETHODIMP CUtil::GetExePathFromObject(BSTR pszPath, VARIANT* pExePath)
|
|
{
|
|
CComBSTR bstrExePath;
|
|
BOOL bSuccess;
|
|
CSaveErrorMode ErrMode;
|
|
|
|
if (!m_Safe) {
|
|
return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
bSuccess = ::GetExePathFromObject(pszPath, bstrExePath);
|
|
if (bSuccess) {
|
|
pExePath->vt = VT_BSTR;
|
|
pExePath->bstrVal = bstrExePath.Copy();
|
|
} else {
|
|
pExePath->vt = VT_NULL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CUtil::IsSystemTarget(BSTR bstrPath, BOOL *pbSystemTarget)
|
|
{
|
|
CComBSTR bstrExePath;
|
|
BOOL bSystemTarget = FALSE;
|
|
DWORD dwAttributes;
|
|
CSaveErrorMode ErrMode;
|
|
|
|
/*++
|
|
//
|
|
// This code was used to check system directory
|
|
//
|
|
|
|
ULONG uSize;
|
|
int nch;
|
|
TCHAR szSystemDir[MAX_PATH];
|
|
TCHAR szCommonPath[MAX_PATH];
|
|
|
|
uSize = ::GetSystemWindowsDirectory(szSystemDir,
|
|
CHARCOUNT(szSystemDir));
|
|
if (uSize == 0 || uSize > CHARCOUNT(szSystemDir)) {
|
|
*pbSystemTarget = FALSE;
|
|
return S_OK;
|
|
}
|
|
--*/
|
|
|
|
|
|
if (!::GetExePathFromObject(bstrPath, bstrExePath)) {
|
|
bstrExePath = bstrPath;
|
|
}
|
|
|
|
if (!bstrExePath) {
|
|
goto Done;
|
|
}
|
|
|
|
/*++
|
|
// this code was used before -- it checked system directory as
|
|
// well as sfc, we are only checking sfc now
|
|
|
|
nch = PathCommonPrefix(szSystemDir, bstrExePath, szCommonPath);
|
|
bSystemTarget = (nch == (int)uSize);
|
|
if (!bSystemTarget) {
|
|
bSystemTarget = SfcIsFileProtected(NULL, bstrExePath);
|
|
}
|
|
--*/
|
|
|
|
bSystemTarget = SfcIsFileProtected(NULL, bstrExePath);
|
|
|
|
|
|
if (!bSystemTarget) {
|
|
dwAttributes = GetFileAttributes(bstrExePath);
|
|
bSystemTarget = (dwAttributes == (DWORD)-1 || (dwAttributes & FILE_ATTRIBUTE_DIRECTORY));
|
|
}
|
|
|
|
Done:
|
|
|
|
*pbSystemTarget = bSystemTarget;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CUtil::IsExecutableFile(BSTR bstrPath, BOOL *pbExecutableFile)
|
|
{
|
|
BOOL bExecutable = FALSE;
|
|
DWORD dwExeType = 0;
|
|
CComBSTR bstrExePath;
|
|
LPCWSTR pszExt;
|
|
CSaveErrorMode ErrMode;
|
|
|
|
if (!m_Safe) {
|
|
return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
if (!::GetExePathFromObject(bstrPath, bstrExePath)) {
|
|
bstrExePath = bstrPath;
|
|
}
|
|
|
|
if (!bstrExePath) {
|
|
//
|
|
// this could happen on a memory allocation failure path
|
|
//
|
|
goto Done;
|
|
}
|
|
|
|
bExecutable = GetBinaryTypeW(bstrExePath, &dwExeType);
|
|
if (!bExecutable) {
|
|
// is this a batch file? if not -- you are out!
|
|
pszExt = PathFindExtension(bstrPath);
|
|
if (pszExt != NULL) {
|
|
bExecutable = !_tcsicmp(pszExt, TEXT(".bat")) ||
|
|
!_tcsicmp(pszExt, TEXT(".cmd"));
|
|
}
|
|
}
|
|
|
|
Done:
|
|
|
|
*pbExecutableFile = bExecutable;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Security: Check for invocation via the hcp protocol and system as the host
|
|
//
|
|
//
|
|
BOOL
|
|
CheckHost(
|
|
IUnknown* pObjectClientSite
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
CComPtr<IOleClientSite> spClientSite;
|
|
BSTR bstrURL = NULL;
|
|
TCHAR szScheme[INTERNET_MAX_SCHEME_LENGTH];
|
|
TCHAR szHostName[INTERNET_MAX_HOST_NAME_LENGTH];
|
|
URL_COMPONENTS UrlComponents = { 0 };
|
|
BOOL bHostVerified = FALSE;
|
|
|
|
if (pObjectClientSite == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
CComQIPtr<IServiceProvider, &IID_IServiceProvider> spServiceProvider(pObjectClientSite);
|
|
|
|
if (spServiceProvider == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
CComPtr<IWebBrowser2> spBrowser;
|
|
|
|
hr = spServiceProvider->QueryService(SID_SWebBrowserApp,
|
|
IID_IWebBrowser2,
|
|
reinterpret_cast<void**>(&spBrowser));
|
|
if (!SUCCEEDED(hr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// we have browser i/f -- get url now
|
|
//
|
|
|
|
hr = spBrowser->get_LocationURL(&bstrURL);
|
|
if (!SUCCEEDED(hr) || bstrURL == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// crack the url
|
|
//
|
|
UrlComponents.dwStructSize = sizeof(UrlComponents);
|
|
UrlComponents.lpszScheme = szScheme;
|
|
UrlComponents.dwSchemeLength = CHARCOUNT(szScheme);
|
|
UrlComponents.lpszHostName = szHostName;
|
|
UrlComponents.dwHostNameLength = CHARCOUNT(szHostName);
|
|
|
|
if (!InternetCrackUrl(bstrURL, 0, 0, &UrlComponents)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// now we should be good to go to check scheme and the host name
|
|
//
|
|
if (_tcsicmp(UrlComponents.lpszScheme, TEXT("hcp")) != 0 ) {
|
|
//
|
|
// bad protocol
|
|
//
|
|
goto cleanup;
|
|
}
|
|
|
|
if (_tcsicmp(UrlComponents.lpszHostName, TEXT("system")) != 0) {
|
|
//
|
|
// bad host
|
|
//
|
|
goto cleanup;
|
|
}
|
|
|
|
bHostVerified = TRUE;
|
|
|
|
cleanup:
|
|
|
|
if (bstrURL) {
|
|
SysFreeString(bstrURL);
|
|
}
|
|
|
|
return bHostVerified;
|
|
|
|
}
|
|
|
|
|