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.
 
 
 
 
 
 

950 lines
30 KiB

#include "StdAfx.h"
#include "pkghandlers.h"
#include "Utils.h"
#include "IISHelpers.h"
#include "IISMigrTool.h"
// COutPackage implementation
/////////////////////////////////////////////////////////////////////////////////////////
COutPackage::COutPackage( HANDLE hFile, bool bCompress, HCRYPTKEY hCryptKey )
{
_ASSERT( hFile != INVALID_HANDLE_VALUE );
m_hFile = hFile;
m_hCryptKey = hCryptKey;
m_bCompress = bCompress;
m_spBuffer = TByteAutoPtr( new BYTE[ COutPackage::DefaultBufferSize ] );
}
/*
Adds file to the package. The file must exists.
Optionally - the data is compressed or encrypted
Optionally - the file DACL is exported
Files data goes to the output file
Everything else goes to the XML doc ( spXMLDoc ) under the node spRoot
*/
void COutPackage::AddFile( LPCWSTR wszName,
const IXMLDOMDocumentPtr& spXMLDoc,
const IXMLDOMElementPtr& spRoot,
DWORD dwOptions )const
{
TFileHandle shInput( ::CreateFile( wszName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL ) );
IF_FAILED_BOOL_THROW( shInput.IsValid(),
CObjectException( IDS_E_OPENFILE, wszName ) );
// Create the node for the file data
IXMLDOMElementPtr spEl = CXMLTools::CreateSubNode( spXMLDoc, spRoot, L"File" );
// Store the file atttributes
CXMLTools::SetAttrib( spEl, L"Attributes", Convert::ToString( ::GetFileAttributes( wszName ) ).c_str() );
// Set the current position in file. This is where trhe file data starts
CXMLTools::SetAttrib( spEl, L"StartsAt", Convert::ToString( CTools::GetFilePtrPos( m_hFile ) ).c_str() );
// Set the name ( remove the path. store only the name )
{
_ASSERT( DefaultBufferSize > MAX_PATH );
LPWSTR wszPath = reinterpret_cast<LPWSTR>( m_spBuffer.get() );
::wcscpy( wszPath, wszName );
::PathStripPathW( wszPath );
CXMLTools::SetAttrib( spEl, L"Name", wszPath );
}
DWORDLONG dwLength = 0; // How much the file occupies from the output file
DWORD dwBytesRead = 0;
// Write "FD" as a mark for the begining of the file. This will serve for verification
// when exctracting for corrupted or wrong package
CTools::WriteFile( m_hFile, "FD", 2 * sizeof( char ) );
// Read the file and process the data
do
{
IF_FAILED_BOOL_THROW( ::ReadFile( shInput.get(),
m_spBuffer.get(),
DefaultBufferSize,
&dwBytesRead,
NULL ),
CObjectException( IDS_E_READFILE, wszName ) );
// Compress it if we need to
if ( m_bCompress )
{
}
// Encrypt it if we need to
if ( m_hCryptKey != NULL )
{
IF_FAILED_BOOL_THROW( ::CryptEncrypt( m_hCryptKey,
NULL,
dwBytesRead != DefaultBufferSize,
0,
m_spBuffer.get(),
&dwBytesRead,
DefaultBufferSize ),
CObjectException( IDS_E_CRYPT_CRYPTO, wszName ) );
}
// Write the result
CTools::WriteFile( m_hFile, m_spBuffer.get(), dwBytesRead );
dwLength += dwBytesRead;
}while( dwBytesRead == DefaultBufferSize );
// Export the file security settings if we need to
if ( !( dwOptions & afNoDACL ) )
{
ExportFileDACL( wszName, spXMLDoc, spEl, ( dwOptions & afAllowNoInhAce ) != 0 );
}
// Store the data length
CXMLTools::SetAttrib( spEl, L"Length", Convert::ToString( dwLength ).c_str() );
}
void COutPackage::AddPath( LPCWSTR wszPath,
const IXMLDOMDocumentPtr& spXMLDoc,
const IXMLDOMElementPtr& spRoot,
DWORD dwOptions )const
{
_ASSERT( ( wszPath != NULL ) && ::PathIsDirectoryW( wszPath ) );
WCHAR wszDir[ MAX_PATH ];
CFindFile Search;
// Export the root ( wszPath ) with the name "\"
AddPathOnly( wszPath, L"\\", spXMLDoc, spRoot, dwOptions );
// Export each dir under wszPath
bool bFound = Search.FindFirst( wszPath,
CFindFile::ffAbsolutePaths |
CFindFile::ffAddFilename |
CFindFile::ffRecursive |
CFindFile::ffGetDirs,
wszDir,
NULL );
// This is the offset from the begining of wszPath, where the subdir ( relative to wszSiteRoot )
// starts. I.e. wszSiteRoot + dwRootOffset points to the subdir only
// ( wszSiteRoot="c:\InetPub", wszPath="c:\InetPub\Subdir",wszPath + dwRootOffset="\Subdir" )
size_t nRootOffset = ::wcslen( wszPath );
while( bFound )
{
// Allow inherited file ACES to be skipped
// We will allow this even if it is not allowed for the dir itself, because
// the subdirs are children of the dir ( wszPath ) and there is no need to explicitly export their ACEs
AddPathOnly( wszDir, wszDir + nRootOffset, spXMLDoc, spRoot, dwOptions | afAllowNoInhAce );
bFound = Search.Next( NULL, wszDir, NULL );
};
}
void COutPackage::WriteSIDsToXML( DWORD dwSiteID,
const IXMLDOMDocumentPtr& spXMLDoc,
const IXMLDOMElementPtr& spRoot )const
{
_ASSERT( spXMLDoc != NULL );
_ASSERT( spRoot != NULL );
IXMLDOMElementPtr spSIDList = CXMLTools::CreateSubNode( spXMLDoc, spRoot, L"SIDList" );
// We will need the local machine name and the anonymous user account name
std::wstring strMachine = CTools::GetMachineName();
std::wstring strUser;
{
CIISSite Site( dwSiteID );
strUser = Site.GetAnonUser();
}
DWORD nID = 0;
for ( TSIDList::const_iterator it = m_SIDList.begin();
it != m_SIDList.end();
++it, ++nID )
{
std::wstring strAccount;
std::wstring strDomain;
SID_NAME_USE SidUsage;
_SidType SidType;
if ( !GetSIDDetails( it->get(),
strUser.c_str(),
strMachine.c_str(),
/*r*/strAccount,
/*r*/strDomain,
/*r*/SidUsage,
/*r*/SidType ) )
{
// This SID is not exportable - remove all ACEs that reference it
RemoveSidFromXML( spXMLDoc, nID );
}
else
{
WriteSIDToXML( CXMLTools::CreateSubNode( spXMLDoc, spSIDList, L"SID" ),
nID,
strAccount.c_str(),
strDomain.c_str(),
SidUsage,
SidType );
}
}
}
/*
Gets the details fro a SID. Returns true if this SID should be exported and false otherwise
*/
bool COutPackage::GetSIDDetails( PSID pSID,
LPCWSTR wszIISUser,
LPCWSTR wszMachine,
std::wstring& rstrAccount,
std::wstring& rstrDomain,
SID_NAME_USE& rSidUsage,
_SidType& rSidType )const
{
_ASSERT( ( pSID != NULL ) && ( wszIISUser != NULL ) && ( wszMachine != NULL ) );
_ASSERT( ::IsValidSid( pSID ) );
DWORD dwNameLen = 256;
DWORD dwDomainLen = 256;
BOOL bResult = FALSE;
std::auto_ptr<WCHAR> spName;
std::auto_ptr<WCHAR> spDomain;
SID_NAME_USE SidUse;
do
{
spName = std::auto_ptr<WCHAR>( new WCHAR[ dwNameLen ] );
spDomain = std::auto_ptr<WCHAR>( new WCHAR[ dwDomainLen ] );
bResult = ::LookupAccountSid( NULL,
pSID,
spName.get(),
&dwNameLen,
spDomain.get(),
&dwDomainLen,
&SidUse );
}while( !bResult && ( ERROR_INSUFFICIENT_BUFFER == ::GetLastError() ) );
// If we cannot find the SID - skip it
if ( !bResult ) return false;
// We handle this type of SIDs. Everything else is skiped
// 1. All domain/external accounts ( spDomain != wszLocalMachine )
// 2. All well known SIDS ( like SYSTEM, Power Users, etc... )
// 3. The anonymous web user ( spName == wszAnonymousUser )
// 4. All built-in aliases ( like Administrators )
_SidType sidType = sidInvalid;
// NOTE: The check order is important
// Is this the IIS anonymous user?
if ( ( ::StrCmpIW( spName.get(), wszIISUser ) == 0 ) &&
( ::StrCmpIW( spDomain.get(), wszMachine ) == 0 ) )
{
sidType = sidIISUser;
}
// Is this a built-in SID
else if ( ( SidTypeAlias == SidUse ) || ( SidTypeWellKnownGroup == SidUse ) )
{
sidType = sidWellKnown;
}
// Is a non-local account SID
else if ( ( ::StrCmpIW( spDomain.get(), wszMachine ) != 0 ) )
{
sidType = sidExternal;
}
// Skip all other SIDs
if ( sidInvalid == sidType ) return false;
rstrAccount = spName.get();
rstrDomain = spDomain.get();
rSidUsage = SidUse;
rSidType = sidType;
return true;
}
void COutPackage::WriteSIDToXML( const IXMLDOMElementPtr& spSID,
DWORD dwID,
LPCWSTR wszAccount,
LPCWSTR wszDomain,
SID_NAME_USE SidUsage,
_SidType SidType )const
{
_ASSERT( spSID != NULL );
_ASSERT( wszAccount != NULL );
_ASSERT( wszDomain != NULL );
_ASSERT( SidType != sidInvalid );
LPCWSTR wszSidType = NULL;
switch( SidType )
{
case sidIISUser:
wszSidType = L"IISUser";
break;
case sidWellKnown:
wszSidType = L"WellKnown";
break;
case sidExternal:
wszSidType = L"External";
break;
default:
_ASSERT( false );
};
CXMLTools::SetAttrib( spSID, L"ID", Convert::ToString( dwID ).c_str() );
CXMLTools::SetAttrib( spSID, L"Type", wszSidType );
CXMLTools::SetAttrib( spSID, L"Account", wszAccount );
CXMLTools::SetAttrib( spSID, L"Domain", wszDomain );
CXMLTools::SetAttrib( spSID, L"Usage", Convert::ToString( static_cast<DWORD>( SidUsage ) ).c_str() );
}
void COutPackage::RemoveSidFromXML( const IXMLDOMDocumentPtr& spDoc, DWORD nSidID )const
{
_ASSERT( spDoc != NULL );
WCHAR wszXPath[ 32 ];
::swprintf( wszXPath, L"//ACE[@ID=%d]", nSidID );
IXMLDOMElementPtr spRoot;
IF_FAILED_HR_THROW( spDoc->get_documentElement( &spRoot ),
CBaseException( IDS_E_XML_PARSE ) );
CXMLTools::RemoveNodes( spRoot, wszXPath );
}
void COutPackage::ExportFileDACL( LPCWSTR wszObject,
const IXMLDOMDocumentPtr& spDoc,
const IXMLDOMElementPtr& spRoot,
bool bAllowSkipInherited )const
{
_ASSERT( wszObject != NULL );
_ASSERT( ( spDoc != NULL ) && ( spRoot != NULL ) );
ACL* pACL = NULL;
// Get the security info for this dir
LPWSTR wszBuffer = reinterpret_cast<LPWSTR>( m_spBuffer.get() );
::wcscpy( wszBuffer, wszObject );
IF_FAILED_BOOL_THROW( ::GetNamedSecurityInfo( wszBuffer,
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
&pACL,
NULL,
NULL ) == ERROR_SUCCESS,
CObjectException( IDS_E_READ_FSECURITY, wszObject ) );
if ( NULL == pACL ) return;
// Export each ACE in the ACL
for ( int i = 0; i < pACL->AceCount; ++i )
{
LPVOID pAce = NULL;
VERIFY( ::GetAce( pACL, i, &pAce ) );
ExportAce( pAce, spDoc, spRoot, bAllowSkipInherited );
}
}
void COutPackage::ExportAce( LPVOID pACE,
const IXMLDOMDocumentPtr& spDoc,
const IXMLDOMElementPtr& spRoot,
bool bAllowSkipInherited )const
{
_ASSERT( pACE != NULL );
// Right now only ACCESS_ALLOWED_ACE_TYPE and ACCESS_DENIED_ACE_TYPE are exported
BYTE btType = reinterpret_cast<ACE_HEADER*>( pACE )->AceType;
BYTE btFlags = reinterpret_cast<ACE_HEADER*>( pACE )->AceFlags;
PSID pSID = NULL;
// Do not export inherited ACES
if ( bAllowSkipInherited && ( btFlags & INHERITED_ACE ) ) return;
if ( ( ACCESS_ALLOWED_ACE_TYPE == btType ) || ( ACCESS_DENIED_ACE_TYPE == btType ) )
{
_ASSERT( sizeof( ACCESS_ALLOWED_ACE ) == sizeof( ACCESS_DENIED_ACE ) );
// The type bellow doesn't matter. Both of the types have the same offset
// of the SidStart member
ACCESS_ALLOWED_ACE* pTypedAce = reinterpret_cast<ACCESS_ALLOWED_ACE*>( pACE );
pSID = reinterpret_cast<PSID>( &( pTypedAce->SidStart ) );
}
else
{
// Unsupported type
return;
}
// Here we will export all SIDs. Then, when exporting the SID list we will remove
// the SIDs that are not exportable ( local user/groups are not exported ).
// Also we will remove all ACE nodes from the XML that reference this SID
// This way we save the time for each SID lookup here
IXMLDOMElementPtr spACE = CXMLTools::CreateSubNode( spDoc, spRoot, L"ACE" );
// Set the ACE attribs
CXMLTools::SetAttrib( spACE, L"SID", Convert::ToString( IDFromSID( pSID ) ).c_str() );
CXMLTools::SetAttrib( spACE, L"Type", Convert::ToString( btType ).c_str() );
CXMLTools::SetAttrib( spACE, L"Flags", Convert::ToString( btFlags ).c_str() );
CXMLTools::SetAttrib( spACE, L"Mask", Convert::ToString( reinterpret_cast<ACCESS_ALLOWED_ACE*>( pACE )->Mask ).c_str() );
}
DWORD COutPackage::IDFromSID( PSID pSID )const
{
_ASSERT( ( pSID != NULL ) && ( ::IsValidSid( pSID ) ) );
DWORD iPos = 0;
for ( TSIDList::const_iterator it = m_SIDList.begin();
it != m_SIDList.end();
++it, ++iPos )
{
if ( ::EqualSid( it->get(), pSID ) )
{
return iPos;
}
}
// If we are here - the SID is not in the list. So add it
m_SIDList.push_back( _sid_ptr( pSID ) );
return static_cast<DWORD>( m_SIDList.size() - 1 );
}
/*
Add wszPath content to the output
Only files in wszPath are added ( non recursive )
*/
void COutPackage::AddPathOnly( LPCWSTR wszPath,
LPCWSTR wszName,
const IXMLDOMDocumentPtr& spXMLDoc,
const IXMLDOMElementPtr& spRoot,
DWORD dwOptions )const
{
_ASSERT( wszPath != NULL );
_ASSERT( wszName != NULL );
// Create the node to hold this dir
IXMLDOMElementPtr spDir = CXMLTools::CreateSubNode( spXMLDoc, spRoot, L"Dir" );
CXMLTools::SetAttrib( spDir, L"Attributes", Convert::ToString( ::GetFileAttributes( wszPath ) ).c_str() );
CXMLTools::SetAttrib( spDir, L"Name", wszName );
if ( !( dwOptions & afNoDACL ) )
{
ExportFileDACL( wszPath, spXMLDoc, spDir, ( dwOptions & afAllowNoInhAce ) != 0 );
}
WCHAR wszFile[ MAX_PATH ];
CFindFile Search;
bool bFound = Search.FindFirst( wszPath,
CFindFile::ffGetFiles |
CFindFile::ffAbsolutePaths |
CFindFile::ffAddFilename,
wszFile,
NULL );
while( bFound )
{
if ( m_CallbackInfo.pCallback != NULL )
{
m_CallbackInfo.pCallback( m_CallbackInfo.pCtx, wszFile, true );
}
// Allow inherited file ACES to be skipped
// We will allow this even if it is not allowed for the dir itself, because
// the files are children of the dir and there is no need to explicitly export their ACEs
AddFile( wszFile, spXMLDoc, spDir, dwOptions | afAllowNoInhAce );
if ( m_CallbackInfo.pCallback != NULL )
{
m_CallbackInfo.pCallback( m_CallbackInfo.pCtx, wszFile, false );
}
bFound = Search.Next( NULL, wszFile, NULL );
}
}
// CInPackage implementation
/////////////////////////////////////////////////////////////////////////////////////////
CInPackage::CInPackage( const IXMLDOMNodePtr& spSite,
HANDLE hFile,
bool bCompressed,
HCRYPTKEY hDecryptKey )
{
// Load the SIDs from the XML
LoadSIDs( spSite );
m_hDecryptKey = hDecryptKey;
m_hFile = hFile;
m_bCompressed = bCompressed;
m_spBuffer = TByteAutoPtr( new BYTE[ DefaultBufferSize ] );
}
void CInPackage::ExtractVDir( const IXMLDOMNodePtr& spVDir, DWORD dwOptions )
{
_ASSERT( spVDir != NULL );
// Get all contained dirs and export them
IXMLDOMNodeListPtr spDirList;
IXMLDOMNodePtr spDir;
IF_FAILED_HR_THROW( spVDir->selectNodes( _bstr_t( L"Dir" ), &spDirList ),
CBaseException( IDS_E_XML_PARSE ) );
while( S_OK == spDirList->nextNode( &spDir ) )
{
ExtractDir( spDir, CXMLTools::GetAttrib( spVDir, L"Path" ).c_str(), dwOptions );
};
}
void CInPackage::LoadSIDs( const IXMLDOMNodePtr& spSite )
{
_ASSERT( spSite != NULL );
IXMLDOMDocumentPtr spXmlDoc;
IXMLDOMNodeListPtr spList;
IXMLDOMNodePtr spSIDs;
IF_FAILED_HR_THROW( spSite->selectNodes( _bstr_t( L"SIDList" ), &spList ),
CBaseException( IDS_E_XML_PARSE ) );
IF_FAILED_BOOL_THROW( S_OK == spList->nextNode( &spSIDs ),
CBaseException( IDS_E_XML_PARSE, ERROR_NOT_FOUND ) );
IF_FAILED_HR_THROW( spSite->get_ownerDocument( &spXmlDoc ),
CBaseException( IDS_E_XML_PARSE ) );
std::wstring strLocalMachine = CTools::GetMachineName();
std::wstring strSourceMachine = CXMLTools::GetDataValueAbs( spXmlDoc,
L"/IISMigrPkg",
L"Machine",
NULL );
// Get all SID entries from the XML
IXMLDOMNodeListPtr spSIDList;
IXMLDOMNodePtr spSID;
IF_FAILED_HR_THROW( spSIDs->selectNodes( _bstr_t( L"SID" ), &spSIDList ),
CBaseException( IDS_E_XML_PARSE ) );
while( S_OK == spSIDList->nextNode( &spSID ) )
{
DWORD dwID = ULONG_MAX;
TByteAutoPtr spSIDData;
// Check if this SID exists on the local machine
if ( LookupSID( spSID, strLocalMachine.c_str(), strSourceMachine.c_str(), /*r*/dwID, /*r*/spSIDData ) )
{
m_SIDs.insert( TSIDMap::value_type( dwID, _sid_ptr( spSIDData.get() ) ) );
}
};
}
bool CInPackage::LookupSID( const IXMLDOMNodePtr& spSID,
LPCWSTR wszLocalMachine,
LPCWSTR wszSourceMachine,
DWORD& rdwID,
TByteAutoPtr& rspData )
{
_ASSERT( spSID != NULL );
_ASSERT( wszLocalMachine != NULL );
rdwID = 0;
rspData = TByteAutoPtr( NULL );
// Get the SID data
DWORD dwID = Convert::ToDWORD( CXMLTools::GetAttrib( spSID, L"ID" ).c_str() );
std::wstring strType = CXMLTools::GetAttrib( spSID, L"Type" );
std::wstring strAccount = CXMLTools::GetAttrib( spSID, L"Account" );
std::wstring strDomainXML= CXMLTools::GetAttrib( spSID, L"Domain" );
SID_NAME_USE SidUsageXML = static_cast<SID_NAME_USE>(
Convert::ToDWORD( CXMLTools::GetAttrib( spSID, L"Usage" ).c_str() ) );
SID_NAME_USE SidUsageLocal;
// Check if this is the IIS anonymous user
if ( ::StrCmpIW( strType.c_str(), L"IISUser" ) == 0 )
{
// Get the local anonymous user ( the one set at the WebSite level - it is the default )
std::wstring strUser = CIISSite::GetDefaultAnonUser();
// Get the local SID
DWORD dwSize = 0;
DWORD dwDummie = 0;
::LookupAccountName( NULL,
strUser.c_str(),
NULL,
&dwSize,
NULL,
&dwDummie,
&SidUsageLocal );
_ASSERT( dwSize > 0 );
rspData = TByteAutoPtr( new BYTE[ dwSize ] );
std::auto_ptr<WCHAR> spDummie( new WCHAR[ dwDummie ] );
// We must found this SID. it was created by the IIS
VERIFY ( ::LookupAccountName( NULL,
strUser.c_str(),
rspData.get(),
&dwSize,
spDummie.get(),
&dwDummie,
&SidUsageLocal ) );
return true;
}
TByteAutoPtr spData;
DWORD dwSIDSize = 32;
std::auto_ptr<WCHAR> spDomain;
DWORD dwDomainSize = 64;
BOOL bResult = FALSE;
do
{
spData = TByteAutoPtr( new BYTE[ dwSIDSize ] );
spDomain = std::auto_ptr<WCHAR>( new WCHAR[ dwDomainSize ] );
bResult = ::LookupAccountNameW( NULL,
strAccount.c_str(),
spData.get(),
&dwSIDSize,
spDomain.get(),
&dwDomainSize,
&SidUsageLocal );
}while( !bResult && ( ERROR_INSUFFICIENT_BUFFER == ::GetLastError() ) );
// If we cannot find such account on this machine - skip this SID
if ( !bResult ) return false;
bool bValidSID = false;
// For well-known sids - check that the domain name, if it is the name of the source machine
// is the name of the local machine.
if ( ::StrCmpIW( strType.c_str(), L"WellKnown" ) == 0 )
{
// First check if the domain names match ( i.e. "NT AUTHORITY" )
// in case they match - we have valid SID if the SID usage matches as well
if ( ( ::StrCmpIW( strDomainXML.c_str(), spDomain.get() ) == 0 ) &&
( SidUsageXML == SidUsageLocal ) )
{
bValidSID = true;
}
// Else - the SID is also valid if the domain is the name of the machine
else if ( ( ::StrCmpIW( strDomainXML.c_str(), wszSourceMachine ) == 0 ) &&
( ::StrCmpIW( spDomain.get(), wszLocalMachine ) == 0 ) &&
( SidUsageXML == SidUsageLocal ) )
{
bValidSID = true;
}
}
// Now check the external SIDs
else if ( ::StrCmpIW( strType.c_str(), L"External" ) == 0 )
{
// External SIDs are valid if domain name match as well as the name usage
if ( ( ::StrCmpIW( strDomainXML.c_str(), spDomain.get() ) == 0 ) &&
( SidUsageXML == SidUsageLocal ) )
{
bValidSID = true;
}
}
if ( bValidSID )
{
rspData = spData;
rdwID = dwID;
}
return bValidSID;
}
void CInPackage::ExtractDir( const IXMLDOMNodePtr& spDir, LPCWSTR wszRoot, DWORD dwOptions )
{
_ASSERT( spDir != NULL );
_ASSERT( ::PathIsDirectoryW( wszRoot ) );
WCHAR wszFullPath[ MAX_PATH ];
CDirTools::PathAppendLocal( wszFullPath, wszRoot, CXMLTools::GetAttrib( spDir, L"Name" ).c_str() );
if ( !::CreateDirectoryW( wszFullPath, NULL ) )
{
IF_FAILED_BOOL_THROW( ::GetLastError() == ERROR_ALREADY_EXISTS,
CObjectException( IDS_E_CREATEDIR, wszFullPath ) );
}
// If we need to clean the dir - do it now
if ( ( dwOptions & impPurgeOldData ) )
{
CDirTools::CleanupDir( wszFullPath, false, true );
}
DWORD dwAttribs = Convert::ToDWORD( CXMLTools::GetAttrib( spDir, L"Attributes" ).c_str() );
IF_FAILED_BOOL_THROW( ::SetFileAttributes( wszFullPath, dwAttribs ),
CObjectException( IDS_E_WRITEFILE, wszFullPath ) );
if ( !( dwOptions & edNoDACL ) && !m_SIDs.empty() )
{
ApplyFileObjSecurity( spDir, wszFullPath );
}
// Extract the files
IXMLDOMNodeListPtr spFileList;
IXMLDOMNodePtr spFile;
IF_FAILED_HR_THROW( spDir->selectNodes( _bstr_t( L"File" ), &spFileList ),
CBaseException( IDS_E_XML_PARSE ) );
while( S_OK == spFileList->nextNode( &spFile ) )
{
std::wstring strName = CXMLTools::GetAttrib( spFile, L"Name" );
if ( m_CallbackInfo.pCallback != NULL )
{
m_CallbackInfo.pCallback( m_CallbackInfo.pCtx, strName.c_str(), true );
}
ExtractFile( spFile, wszFullPath, dwOptions );
if ( m_CallbackInfo.pCallback != NULL )
{
m_CallbackInfo.pCallback( m_CallbackInfo.pCtx, strName.c_str(), false );
}
};
}
void CInPackage::ExtractFile( const IXMLDOMNodePtr& spFile, LPCWSTR wszDir, DWORD dwOptions )
{
_ASSERT( spFile != NULL );
_ASSERT( ::PathIsDirectoryW( wszDir ) );
DWORD dwAttribs = Convert::ToDWORD( CXMLTools::GetAttrib( spFile, L"Attributes" ).c_str() );
DWORDLONG dwStartsAt = Convert::ToDWORDLONG( CXMLTools::GetAttrib( spFile, L"StartsAt" ).c_str() );
DWORDLONG dwLength = Convert::ToDWORDLONG( CXMLTools::GetAttrib( spFile, L"Length" ).c_str() );
std::wstring strName = CXMLTools::GetAttrib( spFile, L"Name" );
WCHAR wszFullPath[ MAX_PATH ];
CDirTools::PathAppendLocal( wszFullPath, wszDir, strName.c_str() );
// Change the file attribs to remove the "read-only" one
IF_FAILED_BOOL_THROW( ::SetFileAttributes( wszFullPath, FILE_ATTRIBUTE_NORMAL ) || ( ::GetLastError() == ERROR_FILE_NOT_FOUND ),
CObjectException( IDS_E_WRITEFILE, wszFullPath ) );
TFileHandle shFile( ::CreateFile( wszFullPath,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
dwAttribs,
NULL ) );
IF_FAILED_BOOL_THROW( shFile.IsValid(), CObjectException( IDS_E_OPENFILE, wszFullPath ) );
DWORD dwRead = 0;
// Write the file data
CTools::SetFilePtrPos( m_hFile, dwStartsAt );
// At the begining of every file, there is the "FD" chars. Check we have mark here
char aszMark[ 2 ];
IF_FAILED_BOOL_THROW( ::ReadFile( m_hFile, aszMark, 2, &dwRead, NULL ) && ( 2 == dwRead ),
CBaseException( IDS_E_PKG_CURRUPTED ) );
IF_FAILED_BOOL_THROW( ::strncmp( aszMark, "FD", 2 ) == 0, CBaseException( IDS_E_PKG_CURRUPTED ) );
while( dwLength > 0 )
{
DWORD dwToRead = static_cast<DWORD>( min( dwLength, DefaultBufferSize ) ); // How much to read this pass
// Read the data. we MUST read as much as we want
IF_FAILED_BOOL_THROW(
::ReadFile( m_hFile, m_spBuffer.get(), dwToRead, &dwRead, NULL ) &&
( dwRead == dwToRead ),
CBaseException( IDS_E_PKG_CURRUPTED ) );
// If the package was encrypted - decrypt the data
if ( m_hDecryptKey != NULL )
{
IF_FAILED_BOOL_THROW( ::CryptDecrypt( m_hDecryptKey,
NULL,
dwLength == dwRead,
0,
m_spBuffer.get(),
&dwToRead ),
CObjectException( IDS_E_CRYPT_CRYPTO, wszFullPath ) );
}
// Write the data in the new file
CTools::WriteFile( shFile.get(), m_spBuffer.get(), dwRead );
_ASSERT( dwRead <= dwLength );
dwLength -= dwRead;
};
// Aply this file's security settings
if ( !( dwOptions & edNoDACL ) && !m_SIDs.empty() )
{
ApplyFileObjSecurity( spFile, wszFullPath );
}
}
void CInPackage::ApplyFileObjSecurity( const IXMLDOMNodePtr& spObj, LPCWSTR wszName )
{
IXMLDOMNodeListPtr spAceList;
IXMLDOMNodePtr spAce;
IF_FAILED_HR_THROW( spObj->selectNodes( _bstr_t( L"ACE" ), &spAceList ),
CBaseException( IDS_E_XML_PARSE ) );
DWORD dwAclSize = sizeof( ACL ); // This will be the ACL header + all ACEs size
DWORD dwAceCount = 0;
// Calc the ACL's size
while( S_OK == spAceList->nextNode( &spAce ) )
{
// Get the ACE type ( allowed/denied ace )
DWORD dwType = Convert::ToDWORD( CXMLTools::GetAttrib( spAce, L"Type" ).c_str() );
DWORD dwID = Convert::ToDWORD( CXMLTools::GetAttrib( spAce, L"SID" ).c_str() );
IF_FAILED_BOOL_THROW( ( ACCESS_ALLOWED_ACE_TYPE == dwType ) || ( ACCESS_DENIED_ACE_TYPE == dwType ),
CBaseException( IDS_E_XML_PARSE, ERROR_INVALID_DATA ) );
// Get the SID for this ACE. If the sid is not in the map - skip this ACE
TSIDMap::const_iterator it = m_SIDs.find( dwID );
if ( it == m_SIDs.end() ) continue;
PSID pSID = it->second.get();
// Add the size of the ACE itself
dwAclSize += ( ACCESS_ALLOWED_ACE_TYPE == dwType ? sizeof( ACCESS_ALLOWED_ACE) : sizeof( ACCESS_DENIED_ACE ) );
// Remove the size of the SidStart member ( it is part of both the ACE and the SID )
dwAclSize -= sizeof( DWORD );
// Add the SID length
_ASSERT( ::IsValidSid( pSID ) );
dwAclSize += ::GetLengthSid( pSID );
++dwAceCount;
};
// If no ACEs were found - exit
if ( 0 == dwAceCount ) return;
VERIFY( SUCCEEDED( spAceList->reset() ) );
// Allocate the buffer for the ACL
TByteAutoPtr spACL( new BYTE[ dwAclSize ] );
PACL pACL = reinterpret_cast<PACL>( spACL.get() );
VERIFY( ::InitializeAcl( pACL, dwAclSize, ACL_REVISION ) );
// Build the ACL
DWORD dwCurrentAce = 0;
while( S_OK == spAceList->nextNode( &spAce ) )
{
// Get the ACE type ( allowed/denied ace )
DWORD dwType = Convert::ToDWORD( CXMLTools::GetAttrib( spAce, L"Type" ).c_str() );
DWORD dwID = Convert::ToDWORD( CXMLTools::GetAttrib( spAce, L"SID" ).c_str() );
DWORD dwAceFlags = Convert::ToDWORD( CXMLTools::GetAttrib( spAce, L"Flags" ).c_str() );
DWORD dwAceMask = Convert::ToDWORD( CXMLTools::GetAttrib( spAce, L"Mask" ).c_str() );
_ASSERT( ( ACCESS_ALLOWED_ACE_TYPE == dwType ) || ( ACCESS_DENIED_ACE_TYPE == dwType ) );
// Get the SID for this ACE. If the sid is not in the map - skip this ACE
TSIDMap::const_iterator it = m_SIDs.find( dwID );
if ( it == m_SIDs.end() ) continue;
PSID pSID = it->second.get();
if ( ACCESS_ALLOWED_ACE_TYPE == dwType )
{
VERIFY( ::AddAccessAllowedAce( pACL,
ACL_REVISION,
dwAceMask,
pSID ) );
}
else
{
VERIFY( ::AddAccessDeniedAce( pACL,
ACL_REVISION,
dwAceMask,
pSID ) );
}
// Set the ACE's flags
// We cannot use AddAccessDeniedAceEx on NT4 - so set the flags directly
ACCESS_ALLOWED_ACE* pACE = NULL;
VERIFY( ::GetAce( pACL, dwCurrentAce, reinterpret_cast<LPVOID*>( &pACE ) ) );
pACE->Header.AceFlags = static_cast<BYTE>( dwAceFlags );
++dwCurrentAce;
};
// Finally - apply the ACL to the object
IF_FAILED_BOOL_THROW( ::SetNamedSecurityInfo( const_cast<LPWSTR>( wszName ),
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
pACL,
NULL ) == ERROR_SUCCESS,
CObjectException( IDS_E_APPLY_DACL, wszName ) );
}