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