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

  1. #include "StdAfx.h"
  2. #include "pkghandlers.h"
  3. #include "Utils.h"
  4. #include "IISHelpers.h"
  5. #include "IISMigrTool.h"
  6. // COutPackage implementation
  7. /////////////////////////////////////////////////////////////////////////////////////////
  8. COutPackage::COutPackage( HANDLE hFile, bool bCompress, HCRYPTKEY hCryptKey )
  9. {
  10. _ASSERT( hFile != INVALID_HANDLE_VALUE );
  11. m_hFile = hFile;
  12. m_hCryptKey = hCryptKey;
  13. m_bCompress = bCompress;
  14. m_spBuffer = TByteAutoPtr( new BYTE[ COutPackage::DefaultBufferSize ] );
  15. }
  16. /*
  17. Adds file to the package. The file must exists.
  18. Optionally - the data is compressed or encrypted
  19. Optionally - the file DACL is exported
  20. Files data goes to the output file
  21. Everything else goes to the XML doc ( spXMLDoc ) under the node spRoot
  22. */
  23. void COutPackage::AddFile( LPCWSTR wszName,
  24. const IXMLDOMDocumentPtr& spXMLDoc,
  25. const IXMLDOMElementPtr& spRoot,
  26. DWORD dwOptions )const
  27. {
  28. TFileHandle shInput( ::CreateFile( wszName,
  29. GENERIC_READ,
  30. FILE_SHARE_READ,
  31. NULL,
  32. OPEN_EXISTING,
  33. FILE_ATTRIBUTE_NORMAL,
  34. NULL ) );
  35. IF_FAILED_BOOL_THROW( shInput.IsValid(),
  36. CObjectException( IDS_E_OPENFILE, wszName ) );
  37. // Create the node for the file data
  38. IXMLDOMElementPtr spEl = CXMLTools::CreateSubNode( spXMLDoc, spRoot, L"File" );
  39. // Store the file atttributes
  40. CXMLTools::SetAttrib( spEl, L"Attributes", Convert::ToString( ::GetFileAttributes( wszName ) ).c_str() );
  41. // Set the current position in file. This is where trhe file data starts
  42. CXMLTools::SetAttrib( spEl, L"StartsAt", Convert::ToString( CTools::GetFilePtrPos( m_hFile ) ).c_str() );
  43. // Set the name ( remove the path. store only the name )
  44. {
  45. _ASSERT( DefaultBufferSize > MAX_PATH );
  46. LPWSTR wszPath = reinterpret_cast<LPWSTR>( m_spBuffer.get() );
  47. ::wcscpy( wszPath, wszName );
  48. ::PathStripPathW( wszPath );
  49. CXMLTools::SetAttrib( spEl, L"Name", wszPath );
  50. }
  51. DWORDLONG dwLength = 0; // How much the file occupies from the output file
  52. DWORD dwBytesRead = 0;
  53. // Write "FD" as a mark for the begining of the file. This will serve for verification
  54. // when exctracting for corrupted or wrong package
  55. CTools::WriteFile( m_hFile, "FD", 2 * sizeof( char ) );
  56. // Read the file and process the data
  57. do
  58. {
  59. IF_FAILED_BOOL_THROW( ::ReadFile( shInput.get(),
  60. m_spBuffer.get(),
  61. DefaultBufferSize,
  62. &dwBytesRead,
  63. NULL ),
  64. CObjectException( IDS_E_READFILE, wszName ) );
  65. // Compress it if we need to
  66. if ( m_bCompress )
  67. {
  68. }
  69. // Encrypt it if we need to
  70. if ( m_hCryptKey != NULL )
  71. {
  72. IF_FAILED_BOOL_THROW( ::CryptEncrypt( m_hCryptKey,
  73. NULL,
  74. dwBytesRead != DefaultBufferSize,
  75. 0,
  76. m_spBuffer.get(),
  77. &dwBytesRead,
  78. DefaultBufferSize ),
  79. CObjectException( IDS_E_CRYPT_CRYPTO, wszName ) );
  80. }
  81. // Write the result
  82. CTools::WriteFile( m_hFile, m_spBuffer.get(), dwBytesRead );
  83. dwLength += dwBytesRead;
  84. }while( dwBytesRead == DefaultBufferSize );
  85. // Export the file security settings if we need to
  86. if ( !( dwOptions & afNoDACL ) )
  87. {
  88. ExportFileDACL( wszName, spXMLDoc, spEl, ( dwOptions & afAllowNoInhAce ) != 0 );
  89. }
  90. // Store the data length
  91. CXMLTools::SetAttrib( spEl, L"Length", Convert::ToString( dwLength ).c_str() );
  92. }
  93. void COutPackage::AddPath( LPCWSTR wszPath,
  94. const IXMLDOMDocumentPtr& spXMLDoc,
  95. const IXMLDOMElementPtr& spRoot,
  96. DWORD dwOptions )const
  97. {
  98. _ASSERT( ( wszPath != NULL ) && ::PathIsDirectoryW( wszPath ) );
  99. WCHAR wszDir[ MAX_PATH ];
  100. CFindFile Search;
  101. // Export the root ( wszPath ) with the name "\"
  102. AddPathOnly( wszPath, L"\\", spXMLDoc, spRoot, dwOptions );
  103. // Export each dir under wszPath
  104. bool bFound = Search.FindFirst( wszPath,
  105. CFindFile::ffAbsolutePaths |
  106. CFindFile::ffAddFilename |
  107. CFindFile::ffRecursive |
  108. CFindFile::ffGetDirs,
  109. wszDir,
  110. NULL );
  111. // This is the offset from the begining of wszPath, where the subdir ( relative to wszSiteRoot )
  112. // starts. I.e. wszSiteRoot + dwRootOffset points to the subdir only
  113. // ( wszSiteRoot="c:\InetPub", wszPath="c:\InetPub\Subdir",wszPath + dwRootOffset="\Subdir" )
  114. size_t nRootOffset = ::wcslen( wszPath );
  115. while( bFound )
  116. {
  117. // Allow inherited file ACES to be skipped
  118. // We will allow this even if it is not allowed for the dir itself, because
  119. // the subdirs are children of the dir ( wszPath ) and there is no need to explicitly export their ACEs
  120. AddPathOnly( wszDir, wszDir + nRootOffset, spXMLDoc, spRoot, dwOptions | afAllowNoInhAce );
  121. bFound = Search.Next( NULL, wszDir, NULL );
  122. };
  123. }
  124. void COutPackage::WriteSIDsToXML( DWORD dwSiteID,
  125. const IXMLDOMDocumentPtr& spXMLDoc,
  126. const IXMLDOMElementPtr& spRoot )const
  127. {
  128. _ASSERT( spXMLDoc != NULL );
  129. _ASSERT( spRoot != NULL );
  130. IXMLDOMElementPtr spSIDList = CXMLTools::CreateSubNode( spXMLDoc, spRoot, L"SIDList" );
  131. // We will need the local machine name and the anonymous user account name
  132. std::wstring strMachine = CTools::GetMachineName();
  133. std::wstring strUser;
  134. {
  135. CIISSite Site( dwSiteID );
  136. strUser = Site.GetAnonUser();
  137. }
  138. DWORD nID = 0;
  139. for ( TSIDList::const_iterator it = m_SIDList.begin();
  140. it != m_SIDList.end();
  141. ++it, ++nID )
  142. {
  143. std::wstring strAccount;
  144. std::wstring strDomain;
  145. SID_NAME_USE SidUsage;
  146. _SidType SidType;
  147. if ( !GetSIDDetails( it->get(),
  148. strUser.c_str(),
  149. strMachine.c_str(),
  150. /*r*/strAccount,
  151. /*r*/strDomain,
  152. /*r*/SidUsage,
  153. /*r*/SidType ) )
  154. {
  155. // This SID is not exportable - remove all ACEs that reference it
  156. RemoveSidFromXML( spXMLDoc, nID );
  157. }
  158. else
  159. {
  160. WriteSIDToXML( CXMLTools::CreateSubNode( spXMLDoc, spSIDList, L"SID" ),
  161. nID,
  162. strAccount.c_str(),
  163. strDomain.c_str(),
  164. SidUsage,
  165. SidType );
  166. }
  167. }
  168. }
  169. /*
  170. Gets the details fro a SID. Returns true if this SID should be exported and false otherwise
  171. */
  172. bool COutPackage::GetSIDDetails( PSID pSID,
  173. LPCWSTR wszIISUser,
  174. LPCWSTR wszMachine,
  175. std::wstring& rstrAccount,
  176. std::wstring& rstrDomain,
  177. SID_NAME_USE& rSidUsage,
  178. _SidType& rSidType )const
  179. {
  180. _ASSERT( ( pSID != NULL ) && ( wszIISUser != NULL ) && ( wszMachine != NULL ) );
  181. _ASSERT( ::IsValidSid( pSID ) );
  182. DWORD dwNameLen = 256;
  183. DWORD dwDomainLen = 256;
  184. BOOL bResult = FALSE;
  185. std::auto_ptr<WCHAR> spName;
  186. std::auto_ptr<WCHAR> spDomain;
  187. SID_NAME_USE SidUse;
  188. do
  189. {
  190. spName = std::auto_ptr<WCHAR>( new WCHAR[ dwNameLen ] );
  191. spDomain = std::auto_ptr<WCHAR>( new WCHAR[ dwDomainLen ] );
  192. bResult = ::LookupAccountSid( NULL,
  193. pSID,
  194. spName.get(),
  195. &dwNameLen,
  196. spDomain.get(),
  197. &dwDomainLen,
  198. &SidUse );
  199. }while( !bResult && ( ERROR_INSUFFICIENT_BUFFER == ::GetLastError() ) );
  200. // If we cannot find the SID - skip it
  201. if ( !bResult ) return false;
  202. // We handle this type of SIDs. Everything else is skiped
  203. // 1. All domain/external accounts ( spDomain != wszLocalMachine )
  204. // 2. All well known SIDS ( like SYSTEM, Power Users, etc... )
  205. // 3. The anonymous web user ( spName == wszAnonymousUser )
  206. // 4. All built-in aliases ( like Administrators )
  207. _SidType sidType = sidInvalid;
  208. // NOTE: The check order is important
  209. // Is this the IIS anonymous user?
  210. if ( ( ::StrCmpIW( spName.get(), wszIISUser ) == 0 ) &&
  211. ( ::StrCmpIW( spDomain.get(), wszMachine ) == 0 ) )
  212. {
  213. sidType = sidIISUser;
  214. }
  215. // Is this a built-in SID
  216. else if ( ( SidTypeAlias == SidUse ) || ( SidTypeWellKnownGroup == SidUse ) )
  217. {
  218. sidType = sidWellKnown;
  219. }
  220. // Is a non-local account SID
  221. else if ( ( ::StrCmpIW( spDomain.get(), wszMachine ) != 0 ) )
  222. {
  223. sidType = sidExternal;
  224. }
  225. // Skip all other SIDs
  226. if ( sidInvalid == sidType ) return false;
  227. rstrAccount = spName.get();
  228. rstrDomain = spDomain.get();
  229. rSidUsage = SidUse;
  230. rSidType = sidType;
  231. return true;
  232. }
  233. void COutPackage::WriteSIDToXML( const IXMLDOMElementPtr& spSID,
  234. DWORD dwID,
  235. LPCWSTR wszAccount,
  236. LPCWSTR wszDomain,
  237. SID_NAME_USE SidUsage,
  238. _SidType SidType )const
  239. {
  240. _ASSERT( spSID != NULL );
  241. _ASSERT( wszAccount != NULL );
  242. _ASSERT( wszDomain != NULL );
  243. _ASSERT( SidType != sidInvalid );
  244. LPCWSTR wszSidType = NULL;
  245. switch( SidType )
  246. {
  247. case sidIISUser:
  248. wszSidType = L"IISUser";
  249. break;
  250. case sidWellKnown:
  251. wszSidType = L"WellKnown";
  252. break;
  253. case sidExternal:
  254. wszSidType = L"External";
  255. break;
  256. default:
  257. _ASSERT( false );
  258. };
  259. CXMLTools::SetAttrib( spSID, L"ID", Convert::ToString( dwID ).c_str() );
  260. CXMLTools::SetAttrib( spSID, L"Type", wszSidType );
  261. CXMLTools::SetAttrib( spSID, L"Account", wszAccount );
  262. CXMLTools::SetAttrib( spSID, L"Domain", wszDomain );
  263. CXMLTools::SetAttrib( spSID, L"Usage", Convert::ToString( static_cast<DWORD>( SidUsage ) ).c_str() );
  264. }
  265. void COutPackage::RemoveSidFromXML( const IXMLDOMDocumentPtr& spDoc, DWORD nSidID )const
  266. {
  267. _ASSERT( spDoc != NULL );
  268. WCHAR wszXPath[ 32 ];
  269. ::swprintf( wszXPath, L"//ACE[@ID=%d]", nSidID );
  270. IXMLDOMElementPtr spRoot;
  271. IF_FAILED_HR_THROW( spDoc->get_documentElement( &spRoot ),
  272. CBaseException( IDS_E_XML_PARSE ) );
  273. CXMLTools::RemoveNodes( spRoot, wszXPath );
  274. }
  275. void COutPackage::ExportFileDACL( LPCWSTR wszObject,
  276. const IXMLDOMDocumentPtr& spDoc,
  277. const IXMLDOMElementPtr& spRoot,
  278. bool bAllowSkipInherited )const
  279. {
  280. _ASSERT( wszObject != NULL );
  281. _ASSERT( ( spDoc != NULL ) && ( spRoot != NULL ) );
  282. ACL* pACL = NULL;
  283. // Get the security info for this dir
  284. LPWSTR wszBuffer = reinterpret_cast<LPWSTR>( m_spBuffer.get() );
  285. ::wcscpy( wszBuffer, wszObject );
  286. IF_FAILED_BOOL_THROW( ::GetNamedSecurityInfo( wszBuffer,
  287. SE_FILE_OBJECT,
  288. DACL_SECURITY_INFORMATION,
  289. NULL,
  290. NULL,
  291. &pACL,
  292. NULL,
  293. NULL ) == ERROR_SUCCESS,
  294. CObjectException( IDS_E_READ_FSECURITY, wszObject ) );
  295. if ( NULL == pACL ) return;
  296. // Export each ACE in the ACL
  297. for ( int i = 0; i < pACL->AceCount; ++i )
  298. {
  299. LPVOID pAce = NULL;
  300. VERIFY( ::GetAce( pACL, i, &pAce ) );
  301. ExportAce( pAce, spDoc, spRoot, bAllowSkipInherited );
  302. }
  303. }
  304. void COutPackage::ExportAce( LPVOID pACE,
  305. const IXMLDOMDocumentPtr& spDoc,
  306. const IXMLDOMElementPtr& spRoot,
  307. bool bAllowSkipInherited )const
  308. {
  309. _ASSERT( pACE != NULL );
  310. // Right now only ACCESS_ALLOWED_ACE_TYPE and ACCESS_DENIED_ACE_TYPE are exported
  311. BYTE btType = reinterpret_cast<ACE_HEADER*>( pACE )->AceType;
  312. BYTE btFlags = reinterpret_cast<ACE_HEADER*>( pACE )->AceFlags;
  313. PSID pSID = NULL;
  314. // Do not export inherited ACES
  315. if ( bAllowSkipInherited && ( btFlags & INHERITED_ACE ) ) return;
  316. if ( ( ACCESS_ALLOWED_ACE_TYPE == btType ) || ( ACCESS_DENIED_ACE_TYPE == btType ) )
  317. {
  318. _ASSERT( sizeof( ACCESS_ALLOWED_ACE ) == sizeof( ACCESS_DENIED_ACE ) );
  319. // The type bellow doesn't matter. Both of the types have the same offset
  320. // of the SidStart member
  321. ACCESS_ALLOWED_ACE* pTypedAce = reinterpret_cast<ACCESS_ALLOWED_ACE*>( pACE );
  322. pSID = reinterpret_cast<PSID>( &( pTypedAce->SidStart ) );
  323. }
  324. else
  325. {
  326. // Unsupported type
  327. return;
  328. }
  329. // Here we will export all SIDs. Then, when exporting the SID list we will remove
  330. // the SIDs that are not exportable ( local user/groups are not exported ).
  331. // Also we will remove all ACE nodes from the XML that reference this SID
  332. // This way we save the time for each SID lookup here
  333. IXMLDOMElementPtr spACE = CXMLTools::CreateSubNode( spDoc, spRoot, L"ACE" );
  334. // Set the ACE attribs
  335. CXMLTools::SetAttrib( spACE, L"SID", Convert::ToString( IDFromSID( pSID ) ).c_str() );
  336. CXMLTools::SetAttrib( spACE, L"Type", Convert::ToString( btType ).c_str() );
  337. CXMLTools::SetAttrib( spACE, L"Flags", Convert::ToString( btFlags ).c_str() );
  338. CXMLTools::SetAttrib( spACE, L"Mask", Convert::ToString( reinterpret_cast<ACCESS_ALLOWED_ACE*>( pACE )->Mask ).c_str() );
  339. }
  340. DWORD COutPackage::IDFromSID( PSID pSID )const
  341. {
  342. _ASSERT( ( pSID != NULL ) && ( ::IsValidSid( pSID ) ) );
  343. DWORD iPos = 0;
  344. for ( TSIDList::const_iterator it = m_SIDList.begin();
  345. it != m_SIDList.end();
  346. ++it, ++iPos )
  347. {
  348. if ( ::EqualSid( it->get(), pSID ) )
  349. {
  350. return iPos;
  351. }
  352. }
  353. // If we are here - the SID is not in the list. So add it
  354. m_SIDList.push_back( _sid_ptr( pSID ) );
  355. return static_cast<DWORD>( m_SIDList.size() - 1 );
  356. }
  357. /*
  358. Add wszPath content to the output
  359. Only files in wszPath are added ( non recursive )
  360. */
  361. void COutPackage::AddPathOnly( LPCWSTR wszPath,
  362. LPCWSTR wszName,
  363. const IXMLDOMDocumentPtr& spXMLDoc,
  364. const IXMLDOMElementPtr& spRoot,
  365. DWORD dwOptions )const
  366. {
  367. _ASSERT( wszPath != NULL );
  368. _ASSERT( wszName != NULL );
  369. // Create the node to hold this dir
  370. IXMLDOMElementPtr spDir = CXMLTools::CreateSubNode( spXMLDoc, spRoot, L"Dir" );
  371. CXMLTools::SetAttrib( spDir, L"Attributes", Convert::ToString( ::GetFileAttributes( wszPath ) ).c_str() );
  372. CXMLTools::SetAttrib( spDir, L"Name", wszName );
  373. if ( !( dwOptions & afNoDACL ) )
  374. {
  375. ExportFileDACL( wszPath, spXMLDoc, spDir, ( dwOptions & afAllowNoInhAce ) != 0 );
  376. }
  377. WCHAR wszFile[ MAX_PATH ];
  378. CFindFile Search;
  379. bool bFound = Search.FindFirst( wszPath,
  380. CFindFile::ffGetFiles |
  381. CFindFile::ffAbsolutePaths |
  382. CFindFile::ffAddFilename,
  383. wszFile,
  384. NULL );
  385. while( bFound )
  386. {
  387. if ( m_CallbackInfo.pCallback != NULL )
  388. {
  389. m_CallbackInfo.pCallback( m_CallbackInfo.pCtx, wszFile, true );
  390. }
  391. // Allow inherited file ACES to be skipped
  392. // We will allow this even if it is not allowed for the dir itself, because
  393. // the files are children of the dir and there is no need to explicitly export their ACEs
  394. AddFile( wszFile, spXMLDoc, spDir, dwOptions | afAllowNoInhAce );
  395. if ( m_CallbackInfo.pCallback != NULL )
  396. {
  397. m_CallbackInfo.pCallback( m_CallbackInfo.pCtx, wszFile, false );
  398. }
  399. bFound = Search.Next( NULL, wszFile, NULL );
  400. }
  401. }
  402. // CInPackage implementation
  403. /////////////////////////////////////////////////////////////////////////////////////////
  404. CInPackage::CInPackage( const IXMLDOMNodePtr& spSite,
  405. HANDLE hFile,
  406. bool bCompressed,
  407. HCRYPTKEY hDecryptKey )
  408. {
  409. // Load the SIDs from the XML
  410. LoadSIDs( spSite );
  411. m_hDecryptKey = hDecryptKey;
  412. m_hFile = hFile;
  413. m_bCompressed = bCompressed;
  414. m_spBuffer = TByteAutoPtr( new BYTE[ DefaultBufferSize ] );
  415. }
  416. void CInPackage::ExtractVDir( const IXMLDOMNodePtr& spVDir, DWORD dwOptions )
  417. {
  418. _ASSERT( spVDir != NULL );
  419. // Get all contained dirs and export them
  420. IXMLDOMNodeListPtr spDirList;
  421. IXMLDOMNodePtr spDir;
  422. IF_FAILED_HR_THROW( spVDir->selectNodes( _bstr_t( L"Dir" ), &spDirList ),
  423. CBaseException( IDS_E_XML_PARSE ) );
  424. while( S_OK == spDirList->nextNode( &spDir ) )
  425. {
  426. ExtractDir( spDir, CXMLTools::GetAttrib( spVDir, L"Path" ).c_str(), dwOptions );
  427. };
  428. }
  429. void CInPackage::LoadSIDs( const IXMLDOMNodePtr& spSite )
  430. {
  431. _ASSERT( spSite != NULL );
  432. IXMLDOMDocumentPtr spXmlDoc;
  433. IXMLDOMNodeListPtr spList;
  434. IXMLDOMNodePtr spSIDs;
  435. IF_FAILED_HR_THROW( spSite->selectNodes( _bstr_t( L"SIDList" ), &spList ),
  436. CBaseException( IDS_E_XML_PARSE ) );
  437. IF_FAILED_BOOL_THROW( S_OK == spList->nextNode( &spSIDs ),
  438. CBaseException( IDS_E_XML_PARSE, ERROR_NOT_FOUND ) );
  439. IF_FAILED_HR_THROW( spSite->get_ownerDocument( &spXmlDoc ),
  440. CBaseException( IDS_E_XML_PARSE ) );
  441. std::wstring strLocalMachine = CTools::GetMachineName();
  442. std::wstring strSourceMachine = CXMLTools::GetDataValueAbs( spXmlDoc,
  443. L"/IISMigrPkg",
  444. L"Machine",
  445. NULL );
  446. // Get all SID entries from the XML
  447. IXMLDOMNodeListPtr spSIDList;
  448. IXMLDOMNodePtr spSID;
  449. IF_FAILED_HR_THROW( spSIDs->selectNodes( _bstr_t( L"SID" ), &spSIDList ),
  450. CBaseException( IDS_E_XML_PARSE ) );
  451. while( S_OK == spSIDList->nextNode( &spSID ) )
  452. {
  453. DWORD dwID = ULONG_MAX;
  454. TByteAutoPtr spSIDData;
  455. // Check if this SID exists on the local machine
  456. if ( LookupSID( spSID, strLocalMachine.c_str(), strSourceMachine.c_str(), /*r*/dwID, /*r*/spSIDData ) )
  457. {
  458. m_SIDs.insert( TSIDMap::value_type( dwID, _sid_ptr( spSIDData.get() ) ) );
  459. }
  460. };
  461. }
  462. bool CInPackage::LookupSID( const IXMLDOMNodePtr& spSID,
  463. LPCWSTR wszLocalMachine,
  464. LPCWSTR wszSourceMachine,
  465. DWORD& rdwID,
  466. TByteAutoPtr& rspData )
  467. {
  468. _ASSERT( spSID != NULL );
  469. _ASSERT( wszLocalMachine != NULL );
  470. rdwID = 0;
  471. rspData = TByteAutoPtr( NULL );
  472. // Get the SID data
  473. DWORD dwID = Convert::ToDWORD( CXMLTools::GetAttrib( spSID, L"ID" ).c_str() );
  474. std::wstring strType = CXMLTools::GetAttrib( spSID, L"Type" );
  475. std::wstring strAccount = CXMLTools::GetAttrib( spSID, L"Account" );
  476. std::wstring strDomainXML= CXMLTools::GetAttrib( spSID, L"Domain" );
  477. SID_NAME_USE SidUsageXML = static_cast<SID_NAME_USE>(
  478. Convert::ToDWORD( CXMLTools::GetAttrib( spSID, L"Usage" ).c_str() ) );
  479. SID_NAME_USE SidUsageLocal;
  480. // Check if this is the IIS anonymous user
  481. if ( ::StrCmpIW( strType.c_str(), L"IISUser" ) == 0 )
  482. {
  483. // Get the local anonymous user ( the one set at the WebSite level - it is the default )
  484. std::wstring strUser = CIISSite::GetDefaultAnonUser();
  485. // Get the local SID
  486. DWORD dwSize = 0;
  487. DWORD dwDummie = 0;
  488. ::LookupAccountName( NULL,
  489. strUser.c_str(),
  490. NULL,
  491. &dwSize,
  492. NULL,
  493. &dwDummie,
  494. &SidUsageLocal );
  495. _ASSERT( dwSize > 0 );
  496. rspData = TByteAutoPtr( new BYTE[ dwSize ] );
  497. std::auto_ptr<WCHAR> spDummie( new WCHAR[ dwDummie ] );
  498. // We must found this SID. it was created by the IIS
  499. VERIFY ( ::LookupAccountName( NULL,
  500. strUser.c_str(),
  501. rspData.get(),
  502. &dwSize,
  503. spDummie.get(),
  504. &dwDummie,
  505. &SidUsageLocal ) );
  506. return true;
  507. }
  508. TByteAutoPtr spData;
  509. DWORD dwSIDSize = 32;
  510. std::auto_ptr<WCHAR> spDomain;
  511. DWORD dwDomainSize = 64;
  512. BOOL bResult = FALSE;
  513. do
  514. {
  515. spData = TByteAutoPtr( new BYTE[ dwSIDSize ] );
  516. spDomain = std::auto_ptr<WCHAR>( new WCHAR[ dwDomainSize ] );
  517. bResult = ::LookupAccountNameW( NULL,
  518. strAccount.c_str(),
  519. spData.get(),
  520. &dwSIDSize,
  521. spDomain.get(),
  522. &dwDomainSize,
  523. &SidUsageLocal );
  524. }while( !bResult && ( ERROR_INSUFFICIENT_BUFFER == ::GetLastError() ) );
  525. // If we cannot find such account on this machine - skip this SID
  526. if ( !bResult ) return false;
  527. bool bValidSID = false;
  528. // For well-known sids - check that the domain name, if it is the name of the source machine
  529. // is the name of the local machine.
  530. if ( ::StrCmpIW( strType.c_str(), L"WellKnown" ) == 0 )
  531. {
  532. // First check if the domain names match ( i.e. "NT AUTHORITY" )
  533. // in case they match - we have valid SID if the SID usage matches as well
  534. if ( ( ::StrCmpIW( strDomainXML.c_str(), spDomain.get() ) == 0 ) &&
  535. ( SidUsageXML == SidUsageLocal ) )
  536. {
  537. bValidSID = true;
  538. }
  539. // Else - the SID is also valid if the domain is the name of the machine
  540. else if ( ( ::StrCmpIW( strDomainXML.c_str(), wszSourceMachine ) == 0 ) &&
  541. ( ::StrCmpIW( spDomain.get(), wszLocalMachine ) == 0 ) &&
  542. ( SidUsageXML == SidUsageLocal ) )
  543. {
  544. bValidSID = true;
  545. }
  546. }
  547. // Now check the external SIDs
  548. else if ( ::StrCmpIW( strType.c_str(), L"External" ) == 0 )
  549. {
  550. // External SIDs are valid if domain name match as well as the name usage
  551. if ( ( ::StrCmpIW( strDomainXML.c_str(), spDomain.get() ) == 0 ) &&
  552. ( SidUsageXML == SidUsageLocal ) )
  553. {
  554. bValidSID = true;
  555. }
  556. }
  557. if ( bValidSID )
  558. {
  559. rspData = spData;
  560. rdwID = dwID;
  561. }
  562. return bValidSID;
  563. }
  564. void CInPackage::ExtractDir( const IXMLDOMNodePtr& spDir, LPCWSTR wszRoot, DWORD dwOptions )
  565. {
  566. _ASSERT( spDir != NULL );
  567. _ASSERT( ::PathIsDirectoryW( wszRoot ) );
  568. WCHAR wszFullPath[ MAX_PATH ];
  569. CDirTools::PathAppendLocal( wszFullPath, wszRoot, CXMLTools::GetAttrib( spDir, L"Name" ).c_str() );
  570. if ( !::CreateDirectoryW( wszFullPath, NULL ) )
  571. {
  572. IF_FAILED_BOOL_THROW( ::GetLastError() == ERROR_ALREADY_EXISTS,
  573. CObjectException( IDS_E_CREATEDIR, wszFullPath ) );
  574. }
  575. // If we need to clean the dir - do it now
  576. if ( ( dwOptions & impPurgeOldData ) )
  577. {
  578. CDirTools::CleanupDir( wszFullPath, false, true );
  579. }
  580. DWORD dwAttribs = Convert::ToDWORD( CXMLTools::GetAttrib( spDir, L"Attributes" ).c_str() );
  581. IF_FAILED_BOOL_THROW( ::SetFileAttributes( wszFullPath, dwAttribs ),
  582. CObjectException( IDS_E_WRITEFILE, wszFullPath ) );
  583. if ( !( dwOptions & edNoDACL ) && !m_SIDs.empty() )
  584. {
  585. ApplyFileObjSecurity( spDir, wszFullPath );
  586. }
  587. // Extract the files
  588. IXMLDOMNodeListPtr spFileList;
  589. IXMLDOMNodePtr spFile;
  590. IF_FAILED_HR_THROW( spDir->selectNodes( _bstr_t( L"File" ), &spFileList ),
  591. CBaseException( IDS_E_XML_PARSE ) );
  592. while( S_OK == spFileList->nextNode( &spFile ) )
  593. {
  594. std::wstring strName = CXMLTools::GetAttrib( spFile, L"Name" );
  595. if ( m_CallbackInfo.pCallback != NULL )
  596. {
  597. m_CallbackInfo.pCallback( m_CallbackInfo.pCtx, strName.c_str(), true );
  598. }
  599. ExtractFile( spFile, wszFullPath, dwOptions );
  600. if ( m_CallbackInfo.pCallback != NULL )
  601. {
  602. m_CallbackInfo.pCallback( m_CallbackInfo.pCtx, strName.c_str(), false );
  603. }
  604. };
  605. }
  606. void CInPackage::ExtractFile( const IXMLDOMNodePtr& spFile, LPCWSTR wszDir, DWORD dwOptions )
  607. {
  608. _ASSERT( spFile != NULL );
  609. _ASSERT( ::PathIsDirectoryW( wszDir ) );
  610. DWORD dwAttribs = Convert::ToDWORD( CXMLTools::GetAttrib( spFile, L"Attributes" ).c_str() );
  611. DWORDLONG dwStartsAt = Convert::ToDWORDLONG( CXMLTools::GetAttrib( spFile, L"StartsAt" ).c_str() );
  612. DWORDLONG dwLength = Convert::ToDWORDLONG( CXMLTools::GetAttrib( spFile, L"Length" ).c_str() );
  613. std::wstring strName = CXMLTools::GetAttrib( spFile, L"Name" );
  614. WCHAR wszFullPath[ MAX_PATH ];
  615. CDirTools::PathAppendLocal( wszFullPath, wszDir, strName.c_str() );
  616. // Change the file attribs to remove the "read-only" one
  617. IF_FAILED_BOOL_THROW( ::SetFileAttributes( wszFullPath, FILE_ATTRIBUTE_NORMAL ) || ( ::GetLastError() == ERROR_FILE_NOT_FOUND ),
  618. CObjectException( IDS_E_WRITEFILE, wszFullPath ) );
  619. TFileHandle shFile( ::CreateFile( wszFullPath,
  620. GENERIC_WRITE,
  621. 0,
  622. NULL,
  623. CREATE_ALWAYS,
  624. dwAttribs,
  625. NULL ) );
  626. IF_FAILED_BOOL_THROW( shFile.IsValid(), CObjectException( IDS_E_OPENFILE, wszFullPath ) );
  627. DWORD dwRead = 0;
  628. // Write the file data
  629. CTools::SetFilePtrPos( m_hFile, dwStartsAt );
  630. // At the begining of every file, there is the "FD" chars. Check we have mark here
  631. char aszMark[ 2 ];
  632. IF_FAILED_BOOL_THROW( ::ReadFile( m_hFile, aszMark, 2, &dwRead, NULL ) && ( 2 == dwRead ),
  633. CBaseException( IDS_E_PKG_CURRUPTED ) );
  634. IF_FAILED_BOOL_THROW( ::strncmp( aszMark, "FD", 2 ) == 0, CBaseException( IDS_E_PKG_CURRUPTED ) );
  635. while( dwLength > 0 )
  636. {
  637. DWORD dwToRead = static_cast<DWORD>( min( dwLength, DefaultBufferSize ) ); // How much to read this pass
  638. // Read the data. we MUST read as much as we want
  639. IF_FAILED_BOOL_THROW(
  640. ::ReadFile( m_hFile, m_spBuffer.get(), dwToRead, &dwRead, NULL ) &&
  641. ( dwRead == dwToRead ),
  642. CBaseException( IDS_E_PKG_CURRUPTED ) );
  643. // If the package was encrypted - decrypt the data
  644. if ( m_hDecryptKey != NULL )
  645. {
  646. IF_FAILED_BOOL_THROW( ::CryptDecrypt( m_hDecryptKey,
  647. NULL,
  648. dwLength == dwRead,
  649. 0,
  650. m_spBuffer.get(),
  651. &dwToRead ),
  652. CObjectException( IDS_E_CRYPT_CRYPTO, wszFullPath ) );
  653. }
  654. // Write the data in the new file
  655. CTools::WriteFile( shFile.get(), m_spBuffer.get(), dwRead );
  656. _ASSERT( dwRead <= dwLength );
  657. dwLength -= dwRead;
  658. };
  659. // Aply this file's security settings
  660. if ( !( dwOptions & edNoDACL ) && !m_SIDs.empty() )
  661. {
  662. ApplyFileObjSecurity( spFile, wszFullPath );
  663. }
  664. }
  665. void CInPackage::ApplyFileObjSecurity( const IXMLDOMNodePtr& spObj, LPCWSTR wszName )
  666. {
  667. IXMLDOMNodeListPtr spAceList;
  668. IXMLDOMNodePtr spAce;
  669. IF_FAILED_HR_THROW( spObj->selectNodes( _bstr_t( L"ACE" ), &spAceList ),
  670. CBaseException( IDS_E_XML_PARSE ) );
  671. DWORD dwAclSize = sizeof( ACL ); // This will be the ACL header + all ACEs size
  672. DWORD dwAceCount = 0;
  673. // Calc the ACL's size
  674. while( S_OK == spAceList->nextNode( &spAce ) )
  675. {
  676. // Get the ACE type ( allowed/denied ace )
  677. DWORD dwType = Convert::ToDWORD( CXMLTools::GetAttrib( spAce, L"Type" ).c_str() );
  678. DWORD dwID = Convert::ToDWORD( CXMLTools::GetAttrib( spAce, L"SID" ).c_str() );
  679. IF_FAILED_BOOL_THROW( ( ACCESS_ALLOWED_ACE_TYPE == dwType ) || ( ACCESS_DENIED_ACE_TYPE == dwType ),
  680. CBaseException( IDS_E_XML_PARSE, ERROR_INVALID_DATA ) );
  681. // Get the SID for this ACE. If the sid is not in the map - skip this ACE
  682. TSIDMap::const_iterator it = m_SIDs.find( dwID );
  683. if ( it == m_SIDs.end() ) continue;
  684. PSID pSID = it->second.get();
  685. // Add the size of the ACE itself
  686. dwAclSize += ( ACCESS_ALLOWED_ACE_TYPE == dwType ? sizeof( ACCESS_ALLOWED_ACE) : sizeof( ACCESS_DENIED_ACE ) );
  687. // Remove the size of the SidStart member ( it is part of both the ACE and the SID )
  688. dwAclSize -= sizeof( DWORD );
  689. // Add the SID length
  690. _ASSERT( ::IsValidSid( pSID ) );
  691. dwAclSize += ::GetLengthSid( pSID );
  692. ++dwAceCount;
  693. };
  694. // If no ACEs were found - exit
  695. if ( 0 == dwAceCount ) return;
  696. VERIFY( SUCCEEDED( spAceList->reset() ) );
  697. // Allocate the buffer for the ACL
  698. TByteAutoPtr spACL( new BYTE[ dwAclSize ] );
  699. PACL pACL = reinterpret_cast<PACL>( spACL.get() );
  700. VERIFY( ::InitializeAcl( pACL, dwAclSize, ACL_REVISION ) );
  701. // Build the ACL
  702. DWORD dwCurrentAce = 0;
  703. while( S_OK == spAceList->nextNode( &spAce ) )
  704. {
  705. // Get the ACE type ( allowed/denied ace )
  706. DWORD dwType = Convert::ToDWORD( CXMLTools::GetAttrib( spAce, L"Type" ).c_str() );
  707. DWORD dwID = Convert::ToDWORD( CXMLTools::GetAttrib( spAce, L"SID" ).c_str() );
  708. DWORD dwAceFlags = Convert::ToDWORD( CXMLTools::GetAttrib( spAce, L"Flags" ).c_str() );
  709. DWORD dwAceMask = Convert::ToDWORD( CXMLTools::GetAttrib( spAce, L"Mask" ).c_str() );
  710. _ASSERT( ( ACCESS_ALLOWED_ACE_TYPE == dwType ) || ( ACCESS_DENIED_ACE_TYPE == dwType ) );
  711. // Get the SID for this ACE. If the sid is not in the map - skip this ACE
  712. TSIDMap::const_iterator it = m_SIDs.find( dwID );
  713. if ( it == m_SIDs.end() ) continue;
  714. PSID pSID = it->second.get();
  715. if ( ACCESS_ALLOWED_ACE_TYPE == dwType )
  716. {
  717. VERIFY( ::AddAccessAllowedAce( pACL,
  718. ACL_REVISION,
  719. dwAceMask,
  720. pSID ) );
  721. }
  722. else
  723. {
  724. VERIFY( ::AddAccessDeniedAce( pACL,
  725. ACL_REVISION,
  726. dwAceMask,
  727. pSID ) );
  728. }
  729. // Set the ACE's flags
  730. // We cannot use AddAccessDeniedAceEx on NT4 - so set the flags directly
  731. ACCESS_ALLOWED_ACE* pACE = NULL;
  732. VERIFY( ::GetAce( pACL, dwCurrentAce, reinterpret_cast<LPVOID*>( &pACE ) ) );
  733. pACE->Header.AceFlags = static_cast<BYTE>( dwAceFlags );
  734. ++dwCurrentAce;
  735. };
  736. // Finally - apply the ACL to the object
  737. IF_FAILED_BOOL_THROW( ::SetNamedSecurityInfo( const_cast<LPWSTR>( wszName ),
  738. SE_FILE_OBJECT,
  739. DACL_SECURITY_INFORMATION,
  740. NULL,
  741. NULL,
  742. pACL,
  743. NULL ) == ERROR_SUCCESS,
  744. CObjectException( IDS_E_APPLY_DACL, wszName ) );
  745. }