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.
 
 
 
 
 
 

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;
}