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.
1091 lines
30 KiB
1091 lines
30 KiB
//=======================================================================
|
|
//
|
|
// Copyright (c) 1998-2001 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// File: RedirectUtil.cpp
|
|
// Author: Charles Ma, 9/19/2001
|
|
//
|
|
// Revision History:
|
|
//
|
|
//
|
|
//
|
|
// Description:
|
|
//
|
|
// Helper function(s) for handling server redirect
|
|
// Can be shared by IU control and other Windows Update components
|
|
//
|
|
//=======================================================================
|
|
|
|
#include <iucommon.h>
|
|
#include <logging.h>
|
|
#include <stringutil.h>
|
|
#include <fileutil.h> // for using function GetIndustryUpdateDirectory()
|
|
#include <download.h>
|
|
#include <trust.h>
|
|
#include <memutil.h>
|
|
|
|
#include <wininet.h> // for define of INTERNET_MAX_URL_LENGTH
|
|
|
|
#include <RedirectUtil.h>
|
|
#include <MISTSAFE.h>
|
|
#include <wusafefn.h>
|
|
|
|
|
|
const TCHAR IDENTNEWCABDIR[] = _T("temp"); // temp name for newly downloaded cab
|
|
// we need to validate time before we take it as a good iuident.cab
|
|
const TCHAR IDENTCAB[] = _T("iuident.cab");
|
|
const TCHAR REDIRECT_SECTION[] = _T("redirect");
|
|
|
|
|
|
//
|
|
// private structure, which defines data used to
|
|
// determine server redirect key
|
|
//
|
|
typedef struct OS_VER_FOR_REDIRECT
|
|
{
|
|
DWORD dwMajor;
|
|
DWORD dwMinor;
|
|
DWORD dwBuildNumber;
|
|
DWORD dwSPMajor;
|
|
DWORD dwSPMinor;
|
|
} OSVerForRedirect, *pOSVerForRedirect;
|
|
|
|
const OSVerForRedirect MAX_VERSION = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// private helper function:
|
|
// read data in string, convert it to structure
|
|
// string ends with \0 or "-"
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
HRESULT ConvertStrToOSVer(LPCTSTR pszVer, pOSVerForRedirect pOSVer)
|
|
{
|
|
int Numbers[5] = {0, 0, 0, 0, 0}; // default version component val is 0
|
|
int n = 0;
|
|
|
|
if (NULL == pOSVer || NULL == pszVer)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// recognizing numbers from string can be done in two ways:
|
|
// 1. more acceptive: stop if known ending char, otherwise continue
|
|
// 2. more rejective: stop if anything not known.
|
|
// we use the first way
|
|
//
|
|
while ('\0' != *pszVer &&
|
|
_T('-') != *pszVer &&
|
|
_T('=') != *pszVer &&
|
|
n < sizeof(Numbers)/sizeof(int))
|
|
{
|
|
if (_T('.') == *pszVer)
|
|
{
|
|
n++;
|
|
}
|
|
else if (_T('0') <= *pszVer && *pszVer <= _T('9'))
|
|
{
|
|
//
|
|
// if this is a digit, add to the current ver component
|
|
//
|
|
Numbers[n] = Numbers[n]*10 + (*pszVer - _T('0'));
|
|
}
|
|
//
|
|
// else - for any other chars, skip it and continue,
|
|
// therefore we are using a very acceptive algorithm
|
|
//
|
|
|
|
pszVer++;
|
|
}
|
|
|
|
pOSVer->dwMajor = Numbers[0];
|
|
pOSVer->dwMinor = Numbers[1];
|
|
pOSVer->dwBuildNumber = Numbers[2];
|
|
pOSVer->dwSPMajor = Numbers[3];
|
|
pOSVer->dwSPMinor = Numbers[4];
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// Private helper function: retrieve version info from current OS
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
HRESULT GetCurrentOSVerInfo(pOSVerForRedirect pOSVer)
|
|
{
|
|
OSVERSIONINFO osVer;
|
|
OSVERSIONINFOEX osVerEx;
|
|
|
|
osVer.dwOSVersionInfoSize = sizeof(osVer);
|
|
osVerEx.dwOSVersionInfoSize = sizeof(osVerEx);
|
|
|
|
if (NULL == pOSVer)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// first, get basic version info
|
|
//
|
|
if (0 == GetVersionEx(&osVer))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
//
|
|
// check what kinf of platform is this?
|
|
//
|
|
if (VER_PLATFORM_WIN32_WINDOWS == osVer.dwPlatformId ||
|
|
(VER_PLATFORM_WIN32_NT == osVer.dwPlatformId && osVer.dwMajorVersion < 5) )
|
|
{
|
|
//
|
|
// if this is Win9X or NT4 and below, then OSVERSIONINFO is the only thing we can get
|
|
// unless we hard code all those SP strings here.
|
|
// Since Windows Update team has no intention to set different site
|
|
// for different releases and SPs of these down level OS, we simply put 0.0 for
|
|
// SP components.
|
|
//
|
|
osVerEx.dwMajorVersion = osVer.dwMajorVersion;
|
|
osVerEx.dwMinorVersion = osVer.dwMinorVersion;
|
|
osVerEx.dwBuildNumber = osVer.dwBuildNumber;
|
|
osVerEx.wServicePackMajor = osVerEx.wServicePackMinor = 0x0;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// for later OS, we can get OSVERSIONINFOEX data, which contains SP data
|
|
//
|
|
if (0 == GetVersionEx((LPOSVERSIONINFO)&osVerEx))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
|
|
pOSVer->dwMajor = osVerEx.dwMajorVersion;
|
|
pOSVer->dwMinor = osVerEx.dwMinorVersion;
|
|
pOSVer->dwBuildNumber = osVerEx.dwBuildNumber;
|
|
pOSVer->dwSPMajor = osVerEx.wServicePackMajor;
|
|
pOSVer->dwSPMinor = osVerEx.wServicePackMinor;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// Private helper function: to tell one given ver structure is between
|
|
// two known ver structures or not.
|
|
//
|
|
// when compare, pass all 3 structures in ptr. Any NULL ptr will return FALSE
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
BOOL IsVerInRange(pOSVerForRedirect pVerToBeTested,
|
|
const pOSVerForRedirect pVerRangeStart,
|
|
const pOSVerForRedirect pVerRangeEnd)
|
|
{
|
|
if (NULL == pVerToBeTested ||
|
|
NULL == pVerRangeStart ||
|
|
NULL == pVerRangeEnd)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return ((pVerRangeStart->dwMajor < pVerToBeTested->dwMajor && // if major in the range
|
|
pVerToBeTested->dwMajor < pVerRangeEnd->dwMajor) ||
|
|
((pVerRangeStart->dwMajor == pVerToBeTested->dwMajor || // or major equal
|
|
pVerRangeEnd->dwMajor == pVerToBeTested->dwMajor) &&
|
|
((pVerRangeStart->dwMinor < pVerToBeTested->dwMinor && // and minor in the range
|
|
pVerToBeTested->dwMinor < pVerRangeEnd->dwMinor) ||
|
|
((pVerRangeStart->dwMinor == pVerToBeTested->dwMinor || // or minor equal too
|
|
pVerToBeTested->dwMinor == pVerRangeEnd->dwMinor) &&
|
|
((pVerRangeStart->dwBuildNumber < pVerToBeTested->dwBuildNumber && // and build number in the range
|
|
pVerToBeTested->dwBuildNumber < pVerRangeEnd->dwBuildNumber) ||
|
|
((pVerRangeStart->dwBuildNumber == pVerToBeTested->dwBuildNumber || // or build number equal too
|
|
pVerToBeTested->dwBuildNumber == pVerRangeEnd->dwBuildNumber) &&
|
|
((pVerRangeStart->dwSPMajor < pVerToBeTested->dwSPMajor && // and service pack major within
|
|
pVerToBeTested->dwSPMajor < pVerRangeEnd->dwSPMajor) ||
|
|
((pVerRangeStart->dwSPMajor == pVerToBeTested->dwSPMajor || // or spmajor equal too
|
|
pVerToBeTested->dwSPMajor == pVerRangeEnd->dwSPMajor) &&
|
|
((pVerRangeStart->dwSPMinor <= pVerToBeTested->dwSPMinor && // and sp minor within
|
|
pVerToBeTested->dwSPMinor <= pVerRangeEnd->dwSPMinor)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
));
|
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// GetRedirectServerUrl()
|
|
// Search the [redirect] section of the given init file for the base
|
|
// server URL corresponding to the OS version.
|
|
//
|
|
// Parameters:
|
|
// pcszInitFile - file name (including path) of the ini file.
|
|
// if this paramater is NULL or empty string,
|
|
// then it's assumed IUident.txt file.
|
|
// lpszNewUrl - point to a buffer to receive redirect server url, if found
|
|
// nBufSize - size of pointed buffer, in number of chars
|
|
//
|
|
// Returns:
|
|
// HRESULT about success or error of this action
|
|
// S_OK - the redirect server url is found and been put into pszBuffer
|
|
// S_FALSE - no redirect server url defined for this OS.
|
|
// other - error code
|
|
//
|
|
// Comments:
|
|
// Expected section in IUIDENT has the following format;
|
|
// Section name: [redirect]
|
|
// Its entries should be defined according to GetINIValueByOSVer().
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
|
|
HRESULT GetRedirectServerUrl(
|
|
LPCTSTR pcszInitFile, // path of file name.
|
|
LPTSTR lpszNewUrl, // points to a buffer to receive new server url
|
|
int nBufSize // size of buffer, in chars
|
|
)
|
|
{
|
|
LOG_Block("GetRedirectServerUrl()");
|
|
|
|
return GetINIValueByOSVer(
|
|
pcszInitFile,
|
|
REDIRECT_SECTION,
|
|
lpszNewUrl,
|
|
nBufSize);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// GetINIValueByOSVer()
|
|
// Search the specified section of the given init file for
|
|
// the value corresponding to the version of the OS.
|
|
//
|
|
// Parameters:
|
|
// pcszInitFile - file name (including path) of the ini file.
|
|
// if this paramater is NULL or empty string,
|
|
// then it's assumed IUident.txt file.
|
|
// pcszSection - section name which the key is under
|
|
// lpszValue - point to a buffer to receive the entry value, if found
|
|
// nBufSize - size of pointed buffer, in number of chars
|
|
//
|
|
// Returns:
|
|
// HRESULT about success or error of this action
|
|
// S_OK - the redirect server url is found and been put into pszBuffer
|
|
// S_FALSE - no value defined for this OS.
|
|
// other - error code
|
|
//
|
|
// Comments:
|
|
// Expected section in IUIDENT has the following format;
|
|
// this section contains zero or more entries, each entry has format:
|
|
// <beginVersionRange>-<endVersionRange>=<redirect server url>
|
|
// where:
|
|
// <beginVersionRange> ::= <VersionRangeBound>
|
|
// <endVersionRange> ::= <VersionRangeBound>
|
|
// <VersionRangeBound> ::= EMPTY | Major[.Minor[.Build[.ServicePackMajor[.ServicePackMinor]]]]
|
|
// <redirect server url>=http://blahblah....
|
|
// an empty version range bound means boundless.
|
|
// a missing version component at end of a version data string means default value 0.
|
|
// (e.g., 5.2 = 5.2.0.0.0)
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
|
|
HRESULT GetINIValueByOSVer(
|
|
LPCTSTR pcszInitFile, // path of file name.
|
|
LPCTSTR pcszSection, // section name
|
|
LPTSTR lpszValue, // points to a buffer to receive new server url
|
|
int nBufSize) // size of buffer, in chars
|
|
{
|
|
LOG_Block("GetINIValueByOSVer");
|
|
|
|
HRESULT hr = S_OK;
|
|
TCHAR szInitFile[MAX_PATH];
|
|
LPTSTR pszBuffer = NULL;
|
|
LPTSTR pszCurrentChar = NULL;
|
|
LPCTSTR pszDash = NULL;
|
|
DWORD dwRet;
|
|
DWORD dwSize = INTERNET_MAX_URL_LENGTH;
|
|
|
|
if (NULL == pcszSection || NULL == lpszValue || nBufSize < 1)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
OSVerForRedirect osCurrent, osBegin, osEnd;
|
|
|
|
CleanUpIfFailedAndSetHrMsg(GetCurrentOSVerInfo(&osCurrent));
|
|
|
|
pszBuffer = (LPTSTR) malloc(dwSize * sizeof(TCHAR));
|
|
CleanUpFailedAllocSetHrMsg(pszBuffer);
|
|
|
|
//
|
|
// find out what's the right init file to search
|
|
//
|
|
if (NULL == pcszInitFile ||
|
|
_T('\0') == *pcszInitFile)
|
|
{
|
|
//
|
|
// if not specified, use iuident.txt
|
|
//
|
|
GetIndustryUpdateDirectory(pszBuffer);
|
|
if (FAILED(hr=PathCchCombine(szInitFile,ARRAYSIZE(szInitFile), pszBuffer, IDENTTXT)) )
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lstrcpyn(szInitFile, pcszInitFile, ARRAYSIZE(szInitFile));
|
|
}
|
|
|
|
LOG_Out(_T("Init file to retrieve redirect data: %s"), szInitFile);
|
|
|
|
//
|
|
// read in all key names
|
|
//
|
|
if (GetPrivateProfileString(
|
|
pcszSection,
|
|
NULL,
|
|
_T(""),
|
|
pszBuffer,
|
|
dwSize,
|
|
szInitFile) == dwSize-2)
|
|
{
|
|
//
|
|
// buffer too small? assume bad ident. stop here
|
|
//
|
|
hr = S_FALSE;
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// loop through each key
|
|
//
|
|
pszCurrentChar = pszBuffer;
|
|
while (_T('\0') != *pszCurrentChar)
|
|
{
|
|
//
|
|
// for the current key, we first try to make sure it's in the right format:
|
|
// there should be a dash "-". If no, then assume this key is bad and we try to
|
|
// skip it.
|
|
//
|
|
pszDash = MyStrChr(pszCurrentChar, _T('-'));
|
|
|
|
if (NULL != pszDash)
|
|
{
|
|
//
|
|
// get lower bound of ver range. If string starts with "-",
|
|
// then the returned ver would be 0.0.0.0.0
|
|
//
|
|
ConvertStrToOSVer(pszCurrentChar, &osBegin);
|
|
|
|
//
|
|
// get upper bound of ver range
|
|
//
|
|
pszDash++;
|
|
ConvertStrToOSVer(pszDash, &osEnd);
|
|
if (0x0 == osEnd.dwMajor &&
|
|
0x0 == osEnd.dwMinor &&
|
|
0x0 == osEnd.dwBuildNumber &&
|
|
0x0 == osEnd.dwSPMajor &&
|
|
0x0 == osEnd.dwSPMinor)
|
|
{
|
|
//
|
|
// if 0.0.0.0.0. it means nothing after "-".
|
|
// assume the upper bound is unlimited
|
|
//
|
|
osEnd = MAX_VERSION;
|
|
}
|
|
|
|
if (IsVerInRange(&osCurrent, &osBegin, &osEnd))
|
|
{
|
|
//
|
|
// the current OS falls in this range.
|
|
// we read the redirect URL
|
|
//
|
|
if (GetPrivateProfileString(
|
|
pcszSection,
|
|
pszCurrentChar, // use current str as key
|
|
_T(""),
|
|
lpszValue,
|
|
nBufSize,
|
|
szInitFile) == nBufSize - 1)
|
|
{
|
|
Win32MsgSetHrGotoCleanup(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
hr = S_OK;
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
//
|
|
// move to next string
|
|
//
|
|
pszCurrentChar += lstrlen(pszCurrentChar) + 1;
|
|
}
|
|
|
|
//
|
|
// if come to here, it means no suitable version range found.
|
|
//
|
|
*lpszValue = _T('\0');
|
|
hr = S_FALSE;
|
|
|
|
CleanUp:
|
|
SafeFree(pszBuffer);
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// DownloadCab()
|
|
// download a cab file of specific name from a base web address. The
|
|
// file will be saved locally, with file trust verified and extracted to
|
|
// a specific folder.
|
|
//
|
|
// Parameters:
|
|
// hQuitEvent - the event handle to cancel this operation
|
|
// ptszCabName - the file name of the cab file (eg. iuident.cab)
|
|
// ptszBaseUrl - the base web address to download the cab file
|
|
// ptszExtractDir - the local dir to save the cab file and those extracted from it
|
|
// dwFlags - the set of flags to be passed to DownloadFileLite()
|
|
// fExtractFiles (default as TRUE) - extract files
|
|
//
|
|
// Returns:
|
|
// HRESULT about success or error of this action
|
|
// S_OK - iuident.cab was successfully downloaded into the specified location
|
|
// other - error code
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
|
|
HRESULT DownloadCab(
|
|
HANDLE hQuitEvent,
|
|
LPCTSTR ptszCabName,
|
|
LPCTSTR ptszBaseUrl,
|
|
LPCTSTR ptszExtractDir,
|
|
DWORD dwFlags,
|
|
BOOL fExtractFiles)
|
|
{
|
|
LOG_Block("DownloadCab");
|
|
|
|
LPTSTR ptszFullCabUrl;
|
|
|
|
if (NULL == ptszCabName ||
|
|
NULL == ptszBaseUrl ||
|
|
_T('\0') == *ptszBaseUrl ||
|
|
NULL == ptszExtractDir ||
|
|
_T('\0') == *ptszExtractDir)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (NULL == (ptszFullCabUrl = (LPTSTR) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH)))
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
TCHAR tszTarget[MAX_PATH+1];
|
|
int nBaseUrlLen = lstrlen(ptszBaseUrl);
|
|
|
|
if (SUCCEEDED(PathCchCombine(tszTarget,ARRAYSIZE(tszTarget),ptszExtractDir, ptszCabName)) &&
|
|
INTERNET_MAX_URL_LENGTH > nBaseUrlLen)
|
|
{
|
|
|
|
hr=StringCchCopyEx(ptszFullCabUrl,INTERNET_MAX_URL_LENGTH,ptszBaseUrl,NULL,NULL,MISTSAFE_STRING_FLAGS);
|
|
CleanUpIfFailedAndMsg(hr);
|
|
|
|
|
|
if (_T('/') != ptszFullCabUrl[nBaseUrlLen-1])
|
|
{
|
|
ptszFullCabUrl[nBaseUrlLen++] = _T('/');
|
|
}
|
|
|
|
if (INTERNET_MAX_URL_LENGTH > nBaseUrlLen + lstrlen(ptszCabName))
|
|
{
|
|
|
|
//
|
|
// changes made by charlma 4/24/2002: add a safegard:
|
|
//
|
|
// first, make sure that if the local file exist, then it must be trusted. Otherwise,
|
|
// it will block the download if the size/timestamp match the server file.
|
|
//
|
|
if (FileExists(tszTarget))
|
|
{
|
|
hr = VerifyFileTrust(tszTarget, NULL, ReadWUPolicyShowTrustUI());
|
|
if (FAILED(hr))
|
|
{
|
|
(void)DeleteFile(tszTarget);
|
|
}
|
|
}
|
|
|
|
hr=StringCchCopyEx(ptszFullCabUrl+ nBaseUrlLen,INTERNET_MAX_URL_LENGTH-nBaseUrlLen,ptszCabName,NULL,NULL,MISTSAFE_STRING_FLAGS);
|
|
CleanUpIfFailedAndMsg(hr);
|
|
|
|
|
|
// if (SUCCEEDED(hr = DownloadFile(
|
|
// ptszFullCabUrl, // full http url
|
|
// ptszBaseUrl,
|
|
// tszTarget, // optional local file name to rename the downloaded file to if pszLocalPath does not contain file name
|
|
// NULL,
|
|
// &hQuitEvent, // quit event
|
|
// 1,
|
|
// NULL,
|
|
// NULL,
|
|
// dwFlags))) //dwFlags | WUDF_ALLOWWINHTTPONLY)))
|
|
if (SUCCEEDED(hr = DownloadFileLite(
|
|
ptszFullCabUrl, // full http url
|
|
tszTarget, // optional local file name to rename the downloaded file to if pszLocalPath does not contain file name
|
|
hQuitEvent, // quit event
|
|
dwFlags))) //dwFlags | WUDF_ALLOWWINHTTPONLY)))
|
|
{
|
|
// need to use the VerifyFile function, not CheckWinTrust (WU bug # 12251)
|
|
if (SUCCEEDED(hr = VerifyFileTrust(tszTarget, NULL, ReadWUPolicyShowTrustUI())))
|
|
{
|
|
if (WAIT_TIMEOUT != WaitForSingleObject(hQuitEvent, 0))
|
|
{
|
|
hr = E_ABORT;
|
|
LOG_ErrorMsg(hr);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// changed by charlma for bug 602435:
|
|
// added new flag to tell if we should extract files. default as TRUE
|
|
//
|
|
if (fExtractFiles)
|
|
{
|
|
if (IUExtractFiles(tszTarget, ptszExtractDir))
|
|
{
|
|
hr = S_OK;
|
|
if (WAIT_TIMEOUT != WaitForSingleObject(hQuitEvent, 0))
|
|
{
|
|
hr = E_ABORT;
|
|
LOG_ErrorMsg(hr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
LOG_Error(_T("failed to extract %s"), tszTarget);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_Error(_T("VerifyFileTrust(\"%s\", NULL, ReadWUPolicyShowTrustUI()) failed (%#lx)"), tszTarget, hr);
|
|
DeleteFile(tszTarget);
|
|
}
|
|
}
|
|
#ifdef DBG
|
|
else
|
|
{
|
|
LOG_Error(_T("DownloadFileLite(\"%s\", \"%s\", xxx, %#lx) failed (%#lx)."), ptszFullCabUrl, tszTarget, dwFlags, hr);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
|
|
CleanUp:
|
|
|
|
free(ptszFullCabUrl);
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// ValidateNewlyDownloadedCab()
|
|
//
|
|
// This is a new helper function to validate the newly downloaded iuident.cab
|
|
//
|
|
// Description:
|
|
// The newly downloaded iuident.cab will be saved as IUIDENTNEWCAB
|
|
// then this function will do the following validation:
|
|
// (1) if local iuident.cab not exist, then the new one is valid
|
|
// (2) otherwise, extract iuident.txt from both cabs, make sure
|
|
// the one from new cab has later date then the one from existing cab.
|
|
// (3) If not valid, then delete the new cab.
|
|
//
|
|
// Return:
|
|
// S_OK: validated, existing cab been replaced with the new one
|
|
// S_FALSE: not valid, new cab deleted.
|
|
// error: any error encountered during validation
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
HRESULT ValidateNewlyDownloadedCab(LPCTSTR lpszNewIdentCab)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fRet;
|
|
DWORD dwErr;
|
|
TCHAR szExistingIdent[MAX_PATH + 1];
|
|
TCHAR szIUDir[MAX_PATH + 1];
|
|
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
FILETIME ft1, ft2;
|
|
ZeroMemory(&ft1, sizeof(ft1));
|
|
ZeroMemory(&ft2, sizeof(ft2));
|
|
|
|
LOG_Block("ValidateNewlyDownloadedCab()");
|
|
|
|
if (NULL == lpszNewIdentCab)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
LOG_ErrorMsg(hr);
|
|
return hr;
|
|
}
|
|
|
|
if (!FileExists(lpszNewIdentCab))
|
|
{
|
|
LOG_ErrorMsg(ERROR_PATH_NOT_FOUND);
|
|
hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// create existing cab path
|
|
//
|
|
fRet = GetWUDirectory(szIUDir, ARRAYSIZE(szIUDir), TRUE);
|
|
CleanUpIfFalseAndSetHrMsg(!fRet, HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND));
|
|
|
|
hr = PathCchCombine(szExistingIdent, ARRAYSIZE(szExistingIdent), szIUDir, IDENTCAB);
|
|
CleanUpIfFailedAndMsg(hr);
|
|
|
|
//
|
|
// if original ident not exist, we will assume the new one is valid,
|
|
// since we don't have anything else to validate against!
|
|
//
|
|
if (!FileExists(szExistingIdent))
|
|
{
|
|
LOG_Internet(_T("%s not exist. Will use new cab"), szExistingIdent);
|
|
hr = S_OK;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (!IUExtractFiles(szExistingIdent, szIUDir, IDENTTXT))
|
|
{
|
|
LOG_Internet(_T("Error 0x%x when extracting ident.txt from %s. Use new one"), GetLastError(), szExistingIdent);
|
|
hr = S_OK;
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// get the time stamp from the extacted files: we borrow szExistingIdent buffer
|
|
// to contstruct the file name of iuident.txt
|
|
//
|
|
hr = PathCchCombine(szExistingIdent, ARRAYSIZE(szExistingIdent), szIUDir, IDENTTXT);
|
|
CleanUpIfFailedAndMsg(hr);
|
|
|
|
//
|
|
// open file for retrieving modified time
|
|
//
|
|
hFile = CreateFile(szExistingIdent, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
LOG_ErrorMsg(GetLastError());
|
|
hr = S_OK; // use new cab
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (!GetFileTime(hFile, NULL, NULL, &ft1))
|
|
{
|
|
LOG_ErrorMsg(GetLastError());
|
|
hr = S_OK; // use new cab
|
|
goto CleanUp;
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
DeleteFile(szExistingIdent);
|
|
|
|
//
|
|
// extract files from new cab
|
|
//
|
|
if (!IUExtractFiles(lpszNewIdentCab, szIUDir, IDENTTXT))
|
|
{
|
|
dwErr = GetLastError();
|
|
LOG_Internet(_T("Error 0x%x when extracting ident.txt from %s"), dwErr, lpszNewIdentCab);
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// open file for retrieving modified time
|
|
//
|
|
hFile = CreateFile(szExistingIdent, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
dwErr = GetLastError();
|
|
LOG_ErrorMsg(dwErr);
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (!GetFileTime(hFile, NULL, NULL, &ft2))
|
|
{
|
|
dwErr = GetLastError();
|
|
LOG_ErrorMsg(dwErr);
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
goto CleanUp;
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
DeleteFile(szExistingIdent);
|
|
|
|
//
|
|
// compare the two values: if ft2 (from new cab) is later than ft1 (from old cab)
|
|
// then S_OK, otherwise, S_FALSE
|
|
//
|
|
hr = ((ft2.dwHighDateTime > ft1.dwHighDateTime) ||
|
|
((ft2.dwHighDateTime == ft1.dwHighDateTime) &&
|
|
(ft2.dwLowDateTime > ft1.dwLowDateTime)))
|
|
? S_OK : S_FALSE;
|
|
|
|
|
|
CleanUp:
|
|
|
|
if (INVALID_HANDLE_VALUE != hFile)
|
|
{
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
//
|
|
// validated. copy the new cab to existing cab name
|
|
//
|
|
(void)PathCchCombine(szExistingIdent, ARRAYSIZE(szExistingIdent), szIUDir, IDENTCAB);
|
|
if (CopyFile(lpszNewIdentCab, szExistingIdent, FALSE))
|
|
{
|
|
LOG_Internet(_T("New cab is better, copy to existing one, if any"));
|
|
}
|
|
else
|
|
{
|
|
dwErr = GetLastError();
|
|
LOG_ErrorMsg(dwErr);
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// if not to use the new cab, we delete it.
|
|
//
|
|
LOG_Internet(_T("Error (0x%x) or new iuident.cab not better than old one."), hr);
|
|
if ((ft2.dwHighDateTime != ft1.dwHighDateTime) || (ft2.dwLowDateTime != ft1.dwLowDateTime))
|
|
{
|
|
LOG_Internet(_T("Found bad iuident.cab downloaded! Try to delete it."));
|
|
if (!DeleteFile(lpszNewIdentCab))
|
|
{
|
|
LOG_ErrorMsg(GetLastError());
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// clean up the extracted ident
|
|
//
|
|
if (SUCCEEDED(PathCchCombine(szExistingIdent, ARRAYSIZE(szExistingIdent), szIUDir, IDENTTXT)))
|
|
{
|
|
DeleteFile(szExistingIdent);
|
|
}
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// DownloadIUIdent()
|
|
// download iuident.cab from a specific location, if provided.
|
|
// Otherwise get it from where the WUServer registry value points to.
|
|
// Either case, it will handle ident redirection.
|
|
//
|
|
// Parameters:
|
|
// hQuitEvent - the event handle to cancel this operation
|
|
// ptszBaseUrl - the initial base URL for iuident.cab, must be no bigger than
|
|
// (INTERNET_MAX_URL_LENGTH) TCHARs. Otherwise use
|
|
// WUServer entry from policy. If entry not found,
|
|
// use "http://windowsupdate.microsoft.com/v4"
|
|
// ptszFileCacheDir - the local base path to store the iuident.cab and
|
|
// the files extracted from it
|
|
// dwFlags - the set of flags used by DownloadCab()
|
|
// fIdentFromPolicy - tell if this is corpwu use. It has these impacts:
|
|
// TRUE: (1) no iuident.txt timestamp validation will be done by
|
|
// comparing the newly downloaded cab and existing one.
|
|
// (2) if download fail and ident cab exist and valid,
|
|
// we will verify trust and extract iuident to use.
|
|
// FALSE: will validate newly downloaded cab against existing one
|
|
//
|
|
// Returns:
|
|
// HRESULT about success or error of this action
|
|
// S_OK - iuident.cab was successfully downloaded into the specified location
|
|
// other - error code
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
|
|
HRESULT DownloadIUIdent(
|
|
HANDLE hQuitEvent,
|
|
LPCTSTR ptszBaseUrl,
|
|
LPTSTR ptszFileCacheDir,
|
|
DWORD dwFlags,
|
|
BOOL fIdentFromPolicy
|
|
)
|
|
{
|
|
LOG_Block("DownloadIUIdent");
|
|
|
|
HRESULT hr = S_OK;
|
|
TCHAR tszTargetPath[MAX_PATH + 1];
|
|
LPTSTR ptszIdentBaseUrl = NULL;
|
|
BOOL fVerifyTempDir = TRUE;
|
|
DWORD dwErr = 0;
|
|
|
|
USES_MY_MEMORY;
|
|
|
|
if (NULL == ptszBaseUrl ||
|
|
NULL == ptszFileCacheDir)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ptszIdentBaseUrl = (LPTSTR) MemAlloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH);
|
|
CleanUpFailedAllocSetHrMsg(ptszIdentBaseUrl);
|
|
|
|
hr = StringCchCopyEx(ptszIdentBaseUrl, INTERNET_MAX_URL_LENGTH, ptszBaseUrl, NULL,NULL,MISTSAFE_STRING_FLAGS);
|
|
CleanUpIfFailedAndMsg(hr);
|
|
|
|
int iRedirectCounter = 3; // any non-negative value; to catch circular reference
|
|
|
|
while (0 <= iRedirectCounter)
|
|
{
|
|
if (fIdentFromPolicy)
|
|
{
|
|
//
|
|
// for corpwu case, always download it to overwrite the original
|
|
// no iuident.txt timestamp validation needed.
|
|
//
|
|
hr = StringCchCopyEx(tszTargetPath, ARRAYSIZE(tszTargetPath), ptszFileCacheDir, NULL,NULL,MISTSAFE_STRING_FLAGS);
|
|
CleanUpIfFailedAndMsg(hr);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// constrcut the temp local path for consumer case: download it to v4\temp
|
|
//
|
|
hr = PathCchCombine(tszTargetPath, ARRAYSIZE(tszTargetPath), ptszFileCacheDir, IDENTNEWCABDIR);
|
|
CleanUpIfFailedAndMsg(hr);
|
|
if (fVerifyTempDir)
|
|
{
|
|
if (!CreateNestedDirectory(tszTargetPath))
|
|
{
|
|
dwErr = GetLastError();
|
|
LOG_ErrorMsg(dwErr);
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
goto CleanUp;
|
|
}
|
|
fVerifyTempDir = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
hr = DownloadCab(
|
|
hQuitEvent,
|
|
IDENTCAB,
|
|
ptszIdentBaseUrl,
|
|
tszTargetPath,
|
|
dwFlags,
|
|
FALSE); // download cab without extracting it.
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG_ErrorMsg(hr);
|
|
|
|
// Bad Case, couldn't download the iuident.. iuident is needed for security..
|
|
#if defined(UNICODE) || defined(_UNICODE)
|
|
LogError(hr, "Failed to download %ls from %ls to %ls", IDENTCAB, ptszIdentBaseUrl, tszTargetPath);
|
|
#else
|
|
LogError(hr, "Failed to download %s from %s to %s", IDENTCAB, ptszIdentBaseUrl, tszTargetPath);
|
|
#endif
|
|
//
|
|
// construct original path.
|
|
//
|
|
HRESULT hr1 = PathCchCombine(tszTargetPath, ARRAYSIZE(tszTargetPath), ptszFileCacheDir, IDENTCAB);
|
|
if (FAILED(hr1))
|
|
{
|
|
LOG_ErrorMsg(hr1);
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (fIdentFromPolicy && FileExists(tszTargetPath))
|
|
{
|
|
//
|
|
// charlma: moved the fix from selfupd.cpp to here:
|
|
//
|
|
|
|
// bug 580808 CorpWU: IU: If corpwu server is not available when user navigates to web site,
|
|
// website displays x80072ee7 error and cannot be used.
|
|
// Fix:
|
|
// if corpwu policy is set but the corpwu server is unavailable,
|
|
// we fail over to the local iuident.
|
|
// This is true for both corpwu client and site client.
|
|
hr = S_OK;
|
|
#if defined(DBG)
|
|
LOG_Out(_T("Ignore above error, use local copy of %s from %s"), IDENTCAB, ptszFileCacheDir);
|
|
#endif
|
|
#if defined(UNICODE) || defined(_UNICODE)
|
|
LogMessage("Ignore above error, use local copy of %ls from %ls", IDENTCAB, ptszFileCacheDir);
|
|
#else
|
|
LogMessage("Ignore above error, use local copy of %s from %s", IDENTCAB, ptszFileCacheDir);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// if this is the consumer case, or iuident.cab not exist, can't continue
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if defined(UNICODE) || defined(_UNICODE)
|
|
LogMessage("Downloaded %ls from %ls to %ls", IDENTCAB, ptszIdentBaseUrl, ptszFileCacheDir);
|
|
#else
|
|
LogMessage("Downloaded %s from %s to %s", IDENTCAB, ptszIdentBaseUrl, ptszFileCacheDir);
|
|
#endif
|
|
//
|
|
// added by charlma for bug 602435 fix: verify the signed time stamp of
|
|
// the downloaded cab is newer than the local one.
|
|
//
|
|
if (!fIdentFromPolicy)
|
|
{
|
|
//
|
|
// if the newly downloaded cab is newer, and nothing bad happen (SUCCEEDED(hr)), we
|
|
// we'll have an iuident.cab there, new or old.
|
|
//
|
|
(void) PathCchCombine(tszTargetPath, ARRAYSIZE(tszTargetPath), ptszFileCacheDir, IDENTNEWCABDIR);
|
|
hr = PathCchAppend(tszTargetPath, ARRAYSIZE(tszTargetPath), IDENTCAB);
|
|
CleanUpIfFailedAndMsg(hr);
|
|
|
|
hr = ValidateNewlyDownloadedCab(tszTargetPath);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// if we need to use old one, it's fine. so we correct S_FALSE to S_OK;
|
|
//
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
//
|
|
// construct original path. we won't fail since we already tried IDENTNEWCAB on this buffer
|
|
//
|
|
(void)PathCchCombine(tszTargetPath, ARRAYSIZE(tszTargetPath), ptszFileCacheDir, IDENTCAB);
|
|
|
|
}
|
|
|
|
//
|
|
// validat the iuidentcab trust
|
|
//
|
|
if (FAILED(hr = VerifyFileTrust(tszTargetPath, NULL, ReadWUPolicyShowTrustUI())))
|
|
{
|
|
//
|
|
// alreaady logged by VerifyFileTrust(), so just bail out.
|
|
//
|
|
DeleteFile(tszTargetPath);
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// now, we have iuident.cab ready to use. extract the files
|
|
//
|
|
if (!IUExtractFiles(tszTargetPath, ptszFileCacheDir, IDENTTXT))
|
|
{
|
|
dwErr = GetLastError();
|
|
LOG_Internet(_T("Error 0x%x when extracting ident.txt from %s"), dwErr, tszTargetPath);
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// now we use tszTargetPath buffer to construct the iuident.txt file
|
|
//
|
|
hr = PathCchCombine(tszTargetPath, ARRAYSIZE(tszTargetPath), ptszFileCacheDir, IDENTTXT);
|
|
CleanUpIfFailedAndMsg(hr);
|
|
|
|
|
|
//
|
|
// check to see if this OS needs redirect ident
|
|
//
|
|
if (FAILED(hr = GetRedirectServerUrl(tszTargetPath, ptszIdentBaseUrl, INTERNET_MAX_URL_LENGTH)))
|
|
{
|
|
LOG_Error(_T("GetRedirectServerUrl(%s, %s, ...) failed (%#lx)"), tszTargetPath, ptszIdentBaseUrl, hr);
|
|
break;
|
|
}
|
|
|
|
if (S_FALSE == hr || _T('\0') == ptszIdentBaseUrl[0])
|
|
{
|
|
LOG_Out(_T("no more redirection"));
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
|
|
if (WAIT_TIMEOUT != WaitForSingleObject(hQuitEvent, 0))
|
|
{
|
|
hr = E_ABORT;
|
|
LOG_ErrorMsg(hr);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// this OS should be redirect to get new ident.
|
|
//
|
|
iRedirectCounter--;
|
|
}
|
|
if (0 > iRedirectCounter)
|
|
{
|
|
// possible circular reference
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
return hr;
|
|
}
|