Source code of Windows XP (NT5)
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.

1935 lines
49 KiB

  1. /*++
  2. Copyright (c) 1992-2000 Microsoft Corporation
  3. Module Name:
  4. aclconv.cxx
  5. Abstract:
  6. This module contains function definitions for the ACLCONV class,
  7. which implements conversion of Lanman 2.x ACLs into NT ACLs.
  8. Author:
  9. Bill McJohn (billmc) 29-Jan-1992
  10. Revision History:
  11. Environment:
  12. ULIB, User Mode
  13. --*/
  14. #define _NTAPI_ULIB_
  15. #include "ulib.hxx"
  16. #include "ulibcl.hxx"
  17. #include "array.hxx"
  18. #include "arrayit.hxx"
  19. #include "arg.hxx"
  20. #include "smsg.hxx"
  21. #include "rtmsg.h"
  22. #include "wstring.hxx"
  23. #include "system.hxx"
  24. #include "aclconv.hxx"
  25. #include "file.hxx"
  26. #include "filestrm.hxx"
  27. #include "logfile.hxx"
  28. BOOLEAN
  29. QueryFileSystemName(
  30. IN PCWSTRING RootName,
  31. OUT PDSTRING FileSystemName
  32. )
  33. /*++
  34. Routine Description:
  35. Determines the name of the file system on the specified volume.
  36. Arguments:
  37. RootName -- Supplies the name of the volume's root directory.
  38. FileSystemName -- Receives the file system name.
  39. Return Value:
  40. TRUE upon successful completion.
  41. --*/
  42. {
  43. WCHAR NameBuffer[8];
  44. if( !GetVolumeInformation( RootName->GetWSTR(),
  45. NULL,
  46. 0,
  47. NULL,
  48. NULL,
  49. NULL,
  50. NameBuffer,
  51. 8 ) ) {
  52. return FALSE;
  53. }
  54. return( FileSystemName->Initialize( NameBuffer ) );
  55. }
  56. BOOLEAN
  57. EnablePrivilege(
  58. PWSTR Privilege
  59. )
  60. /*++
  61. Routine Description:
  62. This routine tries to adjust the priviliege of the current process.
  63. Arguments:
  64. Privilege - String with the name of the privilege to be adjusted.
  65. Return Value:
  66. Returns TRUE if the privilege could be adjusted.
  67. Returns FALSE, otherwise.
  68. --*/
  69. {
  70. HANDLE TokenHandle;
  71. LUID_AND_ATTRIBUTES LuidAndAttributes;
  72. TOKEN_PRIVILEGES TokenPrivileges;
  73. if( !OpenProcessToken( GetCurrentProcess(),
  74. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  75. &TokenHandle ) ) {
  76. DebugPrint( "OpenProcessToken failed" );
  77. return( FALSE );
  78. }
  79. if( !LookupPrivilegeValue( NULL,
  80. Privilege,
  81. &( LuidAndAttributes.Luid ) ) ) {
  82. DebugPrintTrace(( "LookupPrivilegeValue failed, Error = %#d \n", GetLastError() ));
  83. return( FALSE );
  84. }
  85. LuidAndAttributes.Attributes = SE_PRIVILEGE_ENABLED;
  86. TokenPrivileges.PrivilegeCount = 1;
  87. TokenPrivileges.Privileges[0] = LuidAndAttributes;
  88. if( !AdjustTokenPrivileges( TokenHandle,
  89. FALSE,
  90. &TokenPrivileges,
  91. 0,
  92. NULL,
  93. NULL ) ) {
  94. DebugPrintTrace(( "AdjustTokenPrivileges failed, Error = %#x \n", GetLastError() ));
  95. return( FALSE );
  96. }
  97. if( GetLastError() != NO_ERROR ) {
  98. return( FALSE );
  99. }
  100. return( TRUE );
  101. }
  102. INT __cdecl
  103. main(
  104. )
  105. /*++
  106. Routine Description:
  107. Entry point for the ACL conversion utility.
  108. Arguments:
  109. None.
  110. Return Value:
  111. An error level--0 indicates success.
  112. --*/
  113. {
  114. INT ExitCode = 0;
  115. if( !DEFINE_CLASS_DESCRIPTOR( ACLCONV ) ||
  116. !DEFINE_CLASS_DESCRIPTOR( SID_CACHE ) ||
  117. !DEFINE_CLASS_DESCRIPTOR( ACL_CONVERT_NODE ) ) {
  118. return 1;
  119. }
  120. {
  121. ACLCONV Aclconv;
  122. if( Aclconv.Initialize( &ExitCode ) ) {
  123. if( Aclconv.IsInListMode() ) {
  124. ExitCode = Aclconv.ListLogFile();
  125. } else {
  126. ExitCode = Aclconv.ConvertAcls();
  127. }
  128. }
  129. }
  130. return ExitCode;
  131. }
  132. DEFINE_CONSTRUCTOR( ACLCONV, PROGRAM );
  133. ACLCONV::~ACLCONV(
  134. )
  135. {
  136. Destroy();
  137. }
  138. VOID
  139. ACLCONV::Construct(
  140. )
  141. /*++
  142. Routine Description:
  143. Helper method for object construction.
  144. Arguments:
  145. None.
  146. Return Value:
  147. None.
  148. --*/
  149. {
  150. _DataFileRevision = DataFileRevisionUnknown;
  151. _DataFile = NULL;
  152. _DataFileStream = NULL;
  153. _LogFile = NULL;
  154. _LogFileStream = NULL;
  155. _AclWorkFile = NULL;
  156. _AclWorkStream = NULL;
  157. _NewDrive = NULL;
  158. _RootNode = NULL;
  159. _DriveName = NULL;
  160. _DomainName = NULL;
  161. _SidLookupTableName = NULL;
  162. }
  163. VOID
  164. ACLCONV::Destroy(
  165. )
  166. /*++
  167. Routine Description:
  168. Helper function for object destruction.
  169. Arguments:
  170. None.
  171. Return Value:
  172. None.
  173. --*/
  174. {
  175. _DataFileRevision = DataFileRevisionUnknown;
  176. _NextReadOffset = 0;
  177. _BytesRemainingInCurrentGroup = 0;
  178. DELETE( _DataFile );
  179. DELETE( _DataFileStream );
  180. DELETE( _LogFile );
  181. DELETE( _LogFileStream );
  182. DELETE( _AclWorkFile );
  183. DELETE( _AclWorkStream );
  184. DELETE( _NewDrive );
  185. DELETE( _RootNode );
  186. DELETE( _DriveName );
  187. DELETE( _DomainName );
  188. DELETE( _SidLookupTableName );
  189. }
  190. BOOLEAN
  191. ACLCONV::Initialize(
  192. OUT PINT ExitCode
  193. )
  194. /*++
  195. Routine Description:
  196. Initialize the ACLCONV object.
  197. Arguments:
  198. ExitCode -- Receives an error level if this method fails.
  199. Return Value:
  200. TRUE upon successful completion.
  201. --*/
  202. {
  203. Destroy();
  204. if( !PROGRAM::Initialize( ) ) {
  205. Destroy();
  206. *ExitCode = 1;
  207. return FALSE;
  208. }
  209. return ParseArguments( ExitCode );
  210. }
  211. INT
  212. ACLCONV::ListLogFile(
  213. )
  214. /*++
  215. Routine Description:
  216. This method reads a log file produced by a previous run of
  217. ACLCONV and displays the errors logged to that file.
  218. Arguments:
  219. None.
  220. Return Value:
  221. An error level--zero indicates success.
  222. --*/
  223. {
  224. LM_ACCESS_LIST AccessEntries[ MAX_ACCESS_ENTRIES ];
  225. ULONG AceConversionCodes[ MAX_ACCESS_ENTRIES ];
  226. ACLCONV_LOGFILE_HEADER LogFileHeader;
  227. DSTRING ResourceName;
  228. ULONG AccessEntryCount, BytesRead, ConversionCode, i;
  229. INT ExitCode = 0;
  230. USHORT AuditInfo;
  231. // Open the log file and reset the seek pointer to the beginning
  232. // of the file.
  233. //
  234. if( (_LogFile = SYSTEM::QueryFile( &_LogFilePath )) == NULL ||
  235. (_LogFileStream = _LogFile->QueryStream( READ_ACCESS )) == NULL ) {
  236. // Cannot create log file.
  237. DisplayMessage( MSG_ACLCONV_CANT_OPEN_FILE,
  238. ERROR_MESSAGE,
  239. "%W",
  240. _LogFilePath.GetPathString() );
  241. return 1;
  242. }
  243. // Check the log file signature:
  244. //
  245. if( !_LogFileStream->MovePointerPosition( 0, STREAM_BEGINNING ) ||
  246. !_LogFileStream->Read( (PBYTE)&LogFileHeader,
  247. sizeof( ACLCONV_LOGFILE_HEADER ),
  248. &BytesRead ) ||
  249. BytesRead != sizeof( ACLCONV_LOGFILE_HEADER ) ||
  250. LogFileHeader.Signature != AclconvLogFileSignature ) {
  251. DisplayMessage( MSG_ACLCONV_INVALID_LOG_FILE,
  252. ERROR_MESSAGE );
  253. return 1;
  254. }
  255. _NextReadOffset = sizeof( ACLCONV_LOGFILE_HEADER );
  256. while( ReadNextLogRecord( &ExitCode,
  257. &ResourceName,
  258. &ConversionCode,
  259. &AuditInfo,
  260. MAX_ACCESS_ENTRIES,
  261. &AccessEntryCount,
  262. AccessEntries,
  263. AceConversionCodes ) ) {
  264. // Scan to see if there are any entries to display
  265. //
  266. if( AccessEntryCount != 0 ) {
  267. DisplayMessage( MSG_ACLCONV_RESOURCE_NAME,
  268. NORMAL_MESSAGE,
  269. "%W",
  270. &ResourceName );
  271. for( i = 0; i < AccessEntryCount; i++ ) {
  272. DisplayAce( (ACL_CONVERT_CODE)ConversionCode,
  273. (ACE_CONVERT_CODE)AceConversionCodes[i],
  274. AccessEntries + i );
  275. }
  276. }
  277. }
  278. if( ExitCode ) {
  279. DisplayMessage( MSG_ACLCONV_LOGFILE_READ_ERROR, ERROR_MESSAGE );
  280. }
  281. return ExitCode;
  282. }
  283. NONVIRTUAL
  284. BOOLEAN
  285. ACLCONV::DisplayAce(
  286. IN ACL_CONVERT_CODE AclConvertCode,
  287. IN ACE_CONVERT_CODE AceConvertCode,
  288. IN PLM_ACCESS_LIST Ace
  289. )
  290. /*++
  291. Routine Description:
  292. This method displays the conversion result for a single ACE.
  293. Arguments:
  294. AclConvertCode -- Supplies the overall conversion code for
  295. the resource to which this ACE is attached.
  296. AceConvertCode -- Supplies the conversion result for this
  297. particular ACE. Note that if the AclConvertCode
  298. is not ACL_CONVERT_SUCCESS, it takes priority
  299. over AceConvertCode.
  300. Ace -- Supplies the ACE in question.
  301. Return Value:
  302. TRUE upon successful completion.
  303. --*/
  304. {
  305. WCHAR WideNameBuffer[ UNLEN + 1 ];
  306. DSTRING Temp;
  307. DSTRING Name;
  308. memset( WideNameBuffer, 0, sizeof( WideNameBuffer ) );
  309. // Display the user's name. If it's a group, prepend an
  310. // asterisk.
  311. //
  312. if( !MultiByteToWideChar( _SourceCodepage,
  313. 0,
  314. Ace->acl_ugname,
  315. strlen( Ace->acl_ugname ),
  316. WideNameBuffer,
  317. UNLEN + 1 ) ) {
  318. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  319. return FALSE;
  320. }
  321. if( !Temp.Initialize( WideNameBuffer ) ) {
  322. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  323. return FALSE;
  324. }
  325. if( Ace->acl_access & LM_ACCESS_GROUP ) {
  326. if( !Name.Initialize( "*" ) ||
  327. !Name.Strcat( &Temp ) ) {
  328. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  329. return FALSE;
  330. }
  331. } else {
  332. if( !Name.Initialize( &Temp ) ) {
  333. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  334. return FALSE;
  335. }
  336. }
  337. DisplayMessage( MSG_ACLCONV_USERNAME, NORMAL_MESSAGE, "%W", &Name );
  338. // Display the permissions:
  339. //
  340. if( (Ace->acl_access & ~LM_ACCESS_GROUP) == 0 ) {
  341. // This is a no-access ACE.
  342. //
  343. DisplayMessage( MSG_ACLCONV_NONE_PERM );
  344. } else {
  345. // This ACE grants some sort of access--check each type
  346. // of access in turn, displaying all we find.
  347. //
  348. if( Ace->acl_access & LM_ACCESS_READ ) {
  349. DisplayMessage( MSG_ACLCONV_READ_PERM );
  350. }
  351. if( Ace->acl_access & LM_ACCESS_WRITE ) {
  352. DisplayMessage( MSG_ACLCONV_WRITE_PERM );
  353. }
  354. if( Ace->acl_access & LM_ACCESS_CREATE ) {
  355. DisplayMessage( MSG_ACLCONV_CREATE_PERM );
  356. }
  357. if( Ace->acl_access & LM_ACCESS_EXEC ) {
  358. DisplayMessage( MSG_ACLCONV_EXECUTE_PERM );
  359. }
  360. if( Ace->acl_access & LM_ACCESS_DELETE ) {
  361. DisplayMessage( MSG_ACLCONV_DELETE_PERM );
  362. }
  363. if( Ace->acl_access & LM_ACCESS_ATRIB ) {
  364. DisplayMessage( MSG_ACLCONV_ATTR_PERM );
  365. }
  366. if( Ace->acl_access & LM_ACCESS_PERM ) {
  367. DisplayMessage( MSG_ACLCONV_PERM_PERM );
  368. }
  369. }
  370. // Display the cause of failure:
  371. //
  372. if( AclConvertCode != ACL_CONVERT_SUCCESS ) {
  373. // The failure is associated with the resource.
  374. //
  375. switch( AclConvertCode ) {
  376. case ACL_CONVERT_RESOURCE_NOT_FOUND :
  377. DisplayMessage( MSG_ACLCONV_FILE_NOT_FOUND );
  378. break;
  379. case ACL_CONVERT_ERROR :
  380. default :
  381. DisplayMessage( MSG_ACLCONV_ERROR_IN_CONVERSION );
  382. break;
  383. }
  384. } else {
  385. // Display the ACE conversion result.
  386. //
  387. switch( AceConvertCode ) {
  388. case ACL_CONVERT_SUCCESS :
  389. DisplayMessage( MSG_ACLCONV_ACE_CONVERTED );
  390. break;
  391. case ACE_CONVERT_DROPPED :
  392. DisplayMessage( MSG_ACLCONV_ACE_DROPPED );
  393. break;
  394. case ACE_CONVERT_SID_NOT_FOUND :
  395. DisplayMessage( MSG_ACLCONV_SID_NOT_FOUND );
  396. break;
  397. case ACE_CONVERT_ERROR :
  398. default:
  399. DisplayMessage( MSG_ACLCONV_ERROR_IN_CONVERSION );
  400. break;
  401. }
  402. }
  403. return TRUE;
  404. }
  405. NONVIRTUAL
  406. BOOLEAN
  407. ACLCONV::ReadNextLogRecord(
  408. OUT PINT ExitCode,
  409. OUT PWSTRING ResourceString,
  410. OUT PULONG ConversionCode,
  411. OUT PUSHORT AuditInfo,
  412. IN ULONG MaxEntries,
  413. OUT PULONG AccessEntryCount,
  414. OUT PLM_ACCESS_LIST AccessEntries,
  415. OUT PULONG AceConversionCodes
  416. )
  417. /*++
  418. Routine Description:
  419. This method reads the next log entry from the log file.
  420. Arguments:
  421. ExitCode -- receives an exit code if an error occurs.
  422. ResourceString -- receives the name of the resource
  423. ConversionCode -- receives the conversion result for this resource.
  424. AuditInfo -- receives the audit information for this resource.
  425. MaxEntries -- supplies the maximum number of ACE's that can
  426. be written to the output buffers.
  427. AccessEntryCount -- receives the number of ACE's written to
  428. the output buffers.
  429. AccessEntries -- receives the logged ACE's
  430. AceConversionCodes -- receives the conversion results for the
  431. individual ACE's.
  432. Return Value:
  433. TRUE upon successful completion (in which case ExitCode may
  434. be ignored). FALSE if there are no more entries (in which
  435. case ExitCode is zero) or if an error occurs (in which case
  436. ExitCode is non-zero).
  437. --*/
  438. {
  439. ACLCONV_LOG_RECORD_HEADER Header;
  440. ULONG BytesRead;
  441. if( _LogFileStream->IsAtEnd() ) {
  442. // No more entries to read.
  443. *ExitCode = 0;
  444. return FALSE;
  445. }
  446. // Read the log record header
  447. //
  448. if( !_LogFileStream->Read( (PBYTE)&Header,
  449. sizeof( ACLCONV_LOG_RECORD_HEADER ),
  450. &BytesRead ) ||
  451. BytesRead != sizeof( ACLCONV_LOG_RECORD_HEADER ) ) {
  452. *ExitCode = 1;
  453. return FALSE;
  454. }
  455. *ConversionCode = Header.ConversionResult;
  456. *AuditInfo = Header.LmAuditMask;
  457. *AccessEntryCount = Header.AccessEntryCount;
  458. // Make sure that the name is not longer than the maximum
  459. // name length (plus room for trailing NULL) and then read
  460. // it into the name workspace and use it to initialize the
  461. // client's resource name string.
  462. //
  463. if( Header.ResourceNameLength > MAX_RESOURCE_NAME_LENGTH + 1 ||
  464. !_LogFileStream->Read( (PBYTE)_NameBuffer,
  465. Header.ResourceNameLength * sizeof( WCHAR ),
  466. &BytesRead ) ||
  467. BytesRead != Header.ResourceNameLength * sizeof( WCHAR ) ||
  468. !ResourceString->Initialize( _NameBuffer ) ) {
  469. *ExitCode = 1;
  470. return FALSE;
  471. }
  472. // Make sure the ACE's and their associated convert codes will
  473. // fit in the supplied buffers:
  474. //
  475. if( Header.AccessEntryCount > MaxEntries ) {
  476. *ExitCode = 1;
  477. return FALSE;
  478. }
  479. // Read the ACE conversion codes and the ACE's themselves:
  480. //
  481. if( Header.AccessEntryCount != 0 &&
  482. ( !_LogFileStream->Read( (PBYTE)AceConversionCodes,
  483. Header.AccessEntryCount * sizeof( ULONG ),
  484. &BytesRead ) ||
  485. BytesRead != Header.AccessEntryCount * sizeof( ULONG ) ) ||
  486. ( !_LogFileStream->Read( (PBYTE)AccessEntries,
  487. Header.AccessEntryCount *
  488. sizeof( LM_ACCESS_LIST ),
  489. &BytesRead ) ||
  490. BytesRead != Header.AccessEntryCount * sizeof( LM_ACCESS_LIST ) ) ) {
  491. *ExitCode = 1;
  492. return FALSE;
  493. }
  494. return TRUE;
  495. }
  496. INT
  497. ACLCONV::ConvertAcls(
  498. )
  499. /*++
  500. Routine Description:
  501. This method reads the ACL's from the data file into a tree
  502. of ACL Convert Nodes, and then converts the ACL's to NT
  503. security descriptors and applies them to the files and
  504. directories in question.
  505. Arguments:
  506. None.
  507. Return Value:
  508. An error level--zero indicates success.
  509. --*/
  510. {
  511. LM_ACCESS_LIST AccessEntries[MAX_ACCESS_ENTRIES];
  512. INHERITANCE_BUFFER Inheritance;
  513. FSTRING NtfsString;
  514. DSTRING FsName;
  515. DSTRING CurrentResource;
  516. PATH CurrentResourcePath;
  517. ACLCONV_LOGFILE_HEADER LogfileHeader;
  518. PARRAY Components = NULL;
  519. PARRAY_ITERATOR ComponentIterator = NULL;
  520. PACL_CONVERT_NODE CurrentNode, NextNode;
  521. PWSTRING CurrentComponent;
  522. ULONG AccessEntryCount, BytesWritten;
  523. USHORT LmAuditInfo;
  524. INT ExitCode = 0;
  525. DSTRING AclWorkString;
  526. // Open aclwork.dat and read the contents into a special
  527. // sid cache.
  528. if (!AclWorkString.Initialize(L"aclwork.dat")) {
  529. return 1;
  530. }
  531. if (!_AclWorkPath.Initialize(&AclWorkString)) {
  532. return 1;
  533. }
  534. if (NULL == (_AclWorkFile = SYSTEM::QueryFile(&_AclWorkPath))) {
  535. // try to open aclwork.dat in the same directory as the
  536. // data file.
  537. if (!_AclWorkPath.Initialize(&_DataFilePath)) {
  538. return 1;
  539. }
  540. if (!_AclWorkPath.SetName(&AclWorkString)) {
  541. return 1;
  542. }
  543. _AclWorkFile = SYSTEM::QueryFile(&_AclWorkPath);
  544. }
  545. if (NULL != _AclWorkFile &&
  546. NULL != (_AclWorkStream = _AclWorkFile->QueryStream(READ_ACCESS))) {
  547. // DisplayMessage( MSG_ACLCONV_USING_ACLWORK, NORMAL_MESSAGE );
  548. if (!ReadAclWorkSids()) {
  549. return 1;
  550. }
  551. }
  552. // Open the data file and determine its format (ie. what
  553. // revision of BackAcc produced it).
  554. if( (_DataFile = SYSTEM::QueryFile( &_DataFilePath )) == NULL ||
  555. (_DataFileStream = _DataFile->QueryStream( READ_ACCESS )) == NULL ) {
  556. DisplayMessage( MSG_ACLCONV_CANT_OPEN_FILE,
  557. ERROR_MESSAGE,
  558. "%W",
  559. _DataFilePath.GetPathString() );
  560. return 1;
  561. }
  562. // Note that DetermineDataFileRevision sets _DataFileRevision.
  563. if( !DetermineDataFileRevision( ) ||
  564. _DataFileRevision == DataFileRevisionUnknown ) {
  565. DisplayMessage( MSG_ACLCONV_DATAFILE_BAD_FORMAT,
  566. ERROR_MESSAGE,
  567. "%W",
  568. _DataFilePath.GetPathString() );
  569. return 1;
  570. }
  571. // Create the log file.
  572. LogfileHeader.Signature = AclconvLogFileSignature;
  573. if( (_LogFile = SYSTEM::MakeFile( &_LogFilePath )) == NULL ||
  574. (_LogFileStream = _LogFile->QueryStream( WRITE_ACCESS )) == NULL ||
  575. !_LogFileStream->Write( (PBYTE)&LogfileHeader,
  576. sizeof( ACLCONV_LOGFILE_HEADER ),
  577. &BytesWritten ) ||
  578. BytesWritten != sizeof( ACLCONV_LOGFILE_HEADER ) ) {
  579. // Cannot create log file.
  580. DisplayMessage( MSG_ACLCONV_CANT_OPEN_FILE,
  581. ERROR_MESSAGE,
  582. "%W",
  583. _LogFilePath.GetPathString() );
  584. return 1;
  585. }
  586. while( ReadNextAcl( &ExitCode,
  587. &CurrentResource,
  588. MAX_ACCESS_ENTRIES,
  589. &AccessEntryCount,
  590. (PVOID)AccessEntries,
  591. &LmAuditInfo ) ) {
  592. if( CurrentResource.QueryChCount() == 0 ) {
  593. // This resource has no name; ignore it.
  594. //
  595. continue;
  596. }
  597. if( !CurrentResourcePath.Initialize( &CurrentResource ) ) {
  598. DisplayMessage( MSG_ACLCONV_DATAFILE_ERROR, ERROR_MESSAGE );
  599. return 1;
  600. }
  601. // If the user specified a substitute drive, use it.
  602. //
  603. if( _NewDrive != NULL &&
  604. !CurrentResourcePath.SetDevice( _NewDrive ) ) {
  605. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  606. return 1;
  607. }
  608. if( _RootNode == NULL ) {
  609. // This is the first ACL--create the root of the tree
  610. // and determine the name of the drive.
  611. if( !(_DriveName = CurrentResourcePath.QueryRoot()) ||
  612. !(_RootNode = NEW ACL_CONVERT_NODE) ||
  613. !_RootNode->Initialize( _DriveName ) ) {
  614. DELETE( _RootNode );
  615. DELETE( _DriveName );
  616. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  617. return 1;
  618. }
  619. }
  620. // Fetch the component array for this resource.
  621. DELETE( ComponentIterator );
  622. if( Components != NULL ) {
  623. Components->DeleteAllMembers();
  624. }
  625. DELETE( Components );
  626. if( !(Components = CurrentResourcePath.QueryComponentArray()) ||
  627. !(ComponentIterator = (PARRAY_ITERATOR)
  628. Components->QueryIterator()) ) {
  629. DELETE( ComponentIterator );
  630. if( Components != NULL ) {
  631. Components->DeleteAllMembers();
  632. }
  633. DELETE( Components );
  634. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  635. return 1;
  636. }
  637. CurrentNode = _RootNode;
  638. ComponentIterator->Reset();
  639. // The first component is the drive & root directory, which
  640. // isn't interesting.
  641. CurrentComponent = (PWSTRING)ComponentIterator->GetNext();
  642. // Traverse the tree down to the end of the path, creating
  643. // new nodes as needed.
  644. while( (CurrentComponent = (PWSTRING)ComponentIterator->GetNext())
  645. != NULL ) {
  646. if( !(NextNode = CurrentNode->GetChild( CurrentComponent )) &&
  647. !(NextNode = CurrentNode->AddChild( CurrentComponent )) ) {
  648. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  649. return 1;
  650. }
  651. CurrentNode = NextNode;
  652. }
  653. // Add the Lanman ACL to the node which represents the end of
  654. // the path.
  655. if( !CurrentNode->AddLanmanAcl( AccessEntryCount,
  656. AccessEntries,
  657. LmAuditInfo ) ) {
  658. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  659. return 1;
  660. }
  661. }
  662. if( ExitCode != 0 ) {
  663. DisplayMessage( MSG_ACLCONV_DATAFILE_ERROR, ERROR_MESSAGE );
  664. return 1;
  665. }
  666. // Traverse the tree and convert all the ACE's, propagating
  667. // as we go.
  668. //
  669. // Adjust this process' privileges so that it can twiddle
  670. // System ACL's.
  671. //
  672. if( !EnablePrivilege( (LPWSTR)SE_SECURITY_NAME ) ) {
  673. DisplayMessage( MSG_ACLCONV_CONVERSION_ERROR, ERROR_MESSAGE );
  674. ExitCode = 1;
  675. }
  676. if( ExitCode == 0 &&
  677. _RootNode != NULL ) {
  678. // Make sure the target drive is NTFS.
  679. //
  680. if( !NtfsString.Initialize( (PWSTR)L"NTFS" ) ||
  681. !QueryFileSystemName( _RootNode->GetName(), &FsName ) ) {
  682. DisplayMessage( MSG_ACLCONV_CANT_DETERMINE_FILESYSTEM, ERROR_MESSAGE );
  683. ExitCode = 1;
  684. } else if( FsName.Stricmp( &NtfsString ) != 0 ) {
  685. DisplayMessage( MSG_ACLCONV_TARGET_NOT_NTFS, ERROR_MESSAGE );
  686. ExitCode = 1;
  687. } else {
  688. // Set up an empty inheritance buffer to pass
  689. // to the root.
  690. //
  691. Inheritance.RecessiveDeniedAces = NULL;
  692. Inheritance.RecessiveAllowedAces = NULL;
  693. Inheritance.DominantDeniedAces = NULL;
  694. Inheritance.DominantAllowedAces = NULL;
  695. Inheritance.RecessiveDeniedMaxLength = 0;
  696. Inheritance.RecessiveAllowedMaxLength = 0;
  697. Inheritance.DominantDeniedMaxLength = 0;
  698. Inheritance.DominantAllowedMaxLength = 0;
  699. Inheritance.RecessiveDeniedLength = 0;
  700. Inheritance.RecessiveAllowedLength = 0;
  701. Inheritance.DominantDeniedLength = 0;
  702. Inheritance.DominantAllowedLength = 0;
  703. if( !_RootNode->Convert( NULL,
  704. &Inheritance,
  705. this ) ) {
  706. DisplayMessage( MSG_ACLCONV_CONVERSION_ERROR, ERROR_MESSAGE );
  707. ExitCode = 1;
  708. }
  709. }
  710. }
  711. if( ExitCode == 0 ) {
  712. DisplayMessage( MSG_ACLCONV_CONVERT_COMPLETE );
  713. }
  714. return ExitCode;
  715. }
  716. BOOLEAN
  717. ACLCONV::LogConversion(
  718. IN PPATH Resource,
  719. IN ULONG ConversionCode,
  720. IN ULONG LmAuditInfo,
  721. IN ULONG AccessEntryCount,
  722. IN PCULONG AceConversionCodes,
  723. IN PCLM_ACCESS_LIST AccessEntries
  724. )
  725. /*+
  726. Routine Description:
  727. This method writes information about the conversion of a resource
  728. to the log file.
  729. Arguments:
  730. Resource -- Supplies the path to the resource
  731. ConversionCode -- Supplies the conversion result for the
  732. resource.
  733. LmAuditInfo -- Supplies the Lanman 2.x audit information
  734. associated with the resource.
  735. AccessEntryCount -- Supplies the number of Lanman 2.x access
  736. entries associated with the resource.
  737. AceConversionCodes -- Supplies the conversion results of the
  738. individual ACE's
  739. AccessEntries -- Supplies the Lanman 2.x access control
  740. entries.
  741. Return Value:
  742. TRUE upon successful completion.
  743. --*/
  744. {
  745. ACLCONV_LOG_RECORD_HEADER Header;
  746. PCWSTRING PathString;
  747. ULONG NameLength, BytesWritten;
  748. DebugPtrAssert( Resource );
  749. DebugPtrAssert( _LogFileStream );
  750. if( (PathString = Resource->GetPathString()) == NULL ||
  751. (NameLength = PathString->QueryChCount()) > MAX_RESOURCE_NAME_LENGTH ||
  752. !PathString->QueryWSTR( 0,
  753. TO_END,
  754. _NameBuffer,
  755. MAX_RESOURCE_NAME_LENGTH + 1 ) ){
  756. return FALSE;
  757. }
  758. Header.ResourceNameLength = NameLength + 1;
  759. Header.ConversionResult = ConversionCode;
  760. Header.LmAuditMask = (USHORT)LmAuditInfo;
  761. Header.AccessEntryCount = (USHORT)AccessEntryCount;
  762. if(!_LogFileStream->Write( (PBYTE)&Header,
  763. sizeof( ACLCONV_LOG_RECORD_HEADER ),
  764. &BytesWritten ) ||
  765. BytesWritten != sizeof( ACLCONV_LOG_RECORD_HEADER ) ||
  766. !_LogFileStream->Write( (PBYTE)_NameBuffer,
  767. (NameLength + 1) * sizeof( WCHAR ),
  768. &BytesWritten ) ||
  769. BytesWritten != (NameLength + 1) * sizeof( WCHAR )) {
  770. DisplayMessage( MSG_ACLCONV_LOGFILE_ERROR, ERROR_MESSAGE );
  771. return FALSE;
  772. }
  773. if( AccessEntryCount != 0 &&
  774. ( !_LogFileStream->Write( (PBYTE)AceConversionCodes,
  775. AccessEntryCount * sizeof(ULONG),
  776. &BytesWritten ) ||
  777. BytesWritten != AccessEntryCount * sizeof(ULONG) ||
  778. !_LogFileStream->Write( (PBYTE)AccessEntries,
  779. AccessEntryCount * sizeof( LM_ACCESS_LIST ),
  780. &BytesWritten ) ||
  781. BytesWritten != AccessEntryCount * sizeof( LM_ACCESS_LIST ) ) ) {
  782. DisplayMessage( MSG_ACLCONV_LOGFILE_ERROR, ERROR_MESSAGE );
  783. return FALSE;
  784. }
  785. return TRUE;
  786. }
  787. BOOLEAN
  788. ACLCONV::ParseArguments(
  789. OUT PINT ExitCode
  790. )
  791. /*++
  792. Routine Description:
  793. This method parses the arguments given to ACLCONV and sets the
  794. state of the object appropriately.
  795. The accepted syntax is:
  796. ACLCONV [/?] [/V] /DATA:datafile /LOG:logfile
  797. Arguments:
  798. ExitCode -- Receives an exit code if the method fails.
  799. Return Value:
  800. TRUE upon successful completion.
  801. Note that this method will fail, but return an exit-code
  802. of zero (success) if the user specifies the /? argument.
  803. --*/
  804. {
  805. ARRAY ArgArray; // Array of arguments
  806. ARRAY LexArray; // Array of lexemes
  807. ARGUMENT_LEXEMIZER ArgLex; // Argument Lexemizer
  808. STRING_ARGUMENT ProgramNameArgument; // Program name argument
  809. PATH_ARGUMENT DataFileArgument; // Path to data file
  810. PATH_ARGUMENT LogFileArgument; // Path to log file
  811. PATH_ARGUMENT DriveArgument; // New drive to use
  812. FLAG_ARGUMENT ListArgument; // List flag argument
  813. FLAG_ARGUMENT HelpArgument; // Help flag argument
  814. STRING_ARGUMENT DomainArgument; // Domain name argument
  815. LONG_ARGUMENT CodepageArgument; // Source Codepage argument
  816. STRING_ARGUMENT SidLookupArgument; // Filename of lookup table
  817. PWSTRING InvalidArg; // Invalid argument catcher
  818. DSTRING Backslash; // Backslash
  819. DSTRING RootDir; // Root directory of the new drive
  820. UINT DriveType;
  821. DebugPtrAssert( ExitCode );
  822. //
  823. // Initialize all the argument parsing machinery.
  824. //
  825. if( !ArgArray.Initialize( 5, 1 ) ||
  826. !LexArray.Initialize( 5, 1 ) ||
  827. !ArgLex.Initialize( &LexArray ) ||
  828. !HelpArgument.Initialize( "/?" ) ||
  829. !ListArgument.Initialize( "/LIST" ) ||
  830. !ProgramNameArgument.Initialize( "*" ) ||
  831. !DataFileArgument.Initialize( "/DATA:*" ) ||
  832. !LogFileArgument.Initialize( "/LOG:*" ) ||
  833. !DriveArgument.Initialize( "/NEWDRIVE:*" ) ||
  834. !DomainArgument.Initialize( "/DOMAIN:*" ) ||
  835. !CodepageArgument.Initialize( "/CODEPAGE:*" ) ||
  836. !SidLookupArgument.Initialize( "/SIDLOOKUP:*" ) ) {
  837. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  838. *ExitCode = 1;
  839. return FALSE;
  840. }
  841. //
  842. // The ACL conversion utility is case-insensitive
  843. //
  844. ArgLex.SetCaseSensitive( FALSE );
  845. // Put the arguments into the argument array
  846. if( !ArgArray.Put( &HelpArgument ) ||
  847. !ArgArray.Put( &ListArgument ) ||
  848. !ArgArray.Put( &DataFileArgument ) ||
  849. !ArgArray.Put( &LogFileArgument ) ||
  850. !ArgArray.Put( &DriveArgument ) ||
  851. !ArgArray.Put( &DomainArgument ) ||
  852. !ArgArray.Put( &CodepageArgument ) ||
  853. !ArgArray.Put( &ProgramNameArgument ) ) {
  854. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  855. *ExitCode = 1;
  856. return FALSE;
  857. }
  858. //
  859. // Lexemize the command line.
  860. //
  861. if ( !ArgLex.PrepareToParse() ) {
  862. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  863. *ExitCode = 1;
  864. return FALSE;
  865. }
  866. //
  867. // Parse the arguments.
  868. //
  869. if( !ArgLex.DoParsing( &ArgArray ) ) {
  870. DisplayMessage( MSG_CONV_INVALID_PARAMETER, ERROR_MESSAGE, "%W", InvalidArg = ArgLex.QueryInvalidArgument() );
  871. DELETE( InvalidArg );
  872. *ExitCode = 1;
  873. return FALSE;
  874. }
  875. //
  876. // If the user requested help, give it.
  877. //
  878. if( HelpArgument.QueryFlag() ) {
  879. DisplayMessage( MSG_ACLCONV_USAGE );
  880. *ExitCode = 0;
  881. return FALSE;
  882. }
  883. // The log file must be specified, and either the data
  884. // file or the list argument (but not both) must be
  885. // provided.
  886. //
  887. if( !LogFileArgument.IsValueSet() ||
  888. ( !DataFileArgument.IsValueSet() &&
  889. !ListArgument.IsValueSet() ) ||
  890. ( DataFileArgument.IsValueSet() &&
  891. ListArgument.IsValueSet() ) ) {
  892. DisplayMessage( MSG_ACLCONV_USAGE );
  893. *ExitCode = 1;
  894. return FALSE;
  895. }
  896. // If the drive argument has been supplied, record it:
  897. //
  898. if( DriveArgument.IsValueSet() ) {
  899. _NewDrive = DriveArgument.GetPath()->QueryDevice();
  900. if( _NewDrive == NULL ) {
  901. DisplayMessage( MSG_INVALID_PARAMETER,
  902. ERROR_MESSAGE,
  903. "%W",
  904. DriveArgument.GetPath()->GetPathString() );
  905. *ExitCode = 1;
  906. return FALSE;
  907. }
  908. // Validate the drive.
  909. //
  910. if( !RootDir.Initialize( _NewDrive ) ||
  911. !Backslash.Initialize( "\\" ) ||
  912. !RootDir.Strcat( &Backslash ) ) {
  913. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  914. *ExitCode = 1;
  915. return FALSE;
  916. }
  917. DriveType = GetDriveTypeW( RootDir.GetWSTR() );
  918. switch ( DriveType ) {
  919. case DRIVE_FIXED:
  920. case DRIVE_REMOVABLE:
  921. // The drive type is acceptable.
  922. //
  923. break;
  924. case 0:
  925. case 1:
  926. case DRIVE_CDROM:
  927. case DRIVE_REMOTE:
  928. case DRIVE_RAMDISK:
  929. default:
  930. // The drive type is invalid.
  931. //
  932. DisplayMessage( MSG_ACLCONV_INVALID_DRIVE, ERROR_MESSAGE, "%W", _NewDrive );
  933. *ExitCode = 1;
  934. return FALSE;
  935. }
  936. }
  937. // If a domain name has been specified, remember it:
  938. //
  939. if( DomainArgument.IsValueSet() ) {
  940. if( (_DomainName = NEW DSTRING) == NULL ||
  941. !_DomainName->Initialize( DomainArgument.GetString() ) ) {
  942. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  943. *ExitCode = 1;
  944. return FALSE;
  945. }
  946. }
  947. // If a source codepage has been specified, use it; otherwise,
  948. // use CP_OEMCP as default.
  949. //
  950. if( !CodepageArgument.IsValueSet() ) {
  951. _SourceCodepage = CP_OEMCP;
  952. } else {
  953. _SourceCodepage = CodepageArgument.QueryLong();
  954. if( !IsValidCodePage( _SourceCodepage ) ) {
  955. DisplayMessage( MSG_ACLCONV_BAD_CODEPAGE, ERROR_MESSAGE );
  956. *ExitCode = 1;
  957. return FALSE;
  958. }
  959. }
  960. if( SidLookupArgument.IsValueSet() ) {
  961. _SidLookupTableName = SidLookupArgument.GetString()->QueryWSTR();
  962. }
  963. _IsInListMode = ListArgument.IsValueSet();
  964. if( _IsInListMode ) {
  965. // In list mode, only the Log File path need be present.
  966. //
  967. if( !_LogFilePath.Initialize( LogFileArgument.GetPath() ) ) {
  968. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  969. *ExitCode = 1;
  970. return FALSE;
  971. }
  972. } else {
  973. // The object is not in list mode, so both the Data File
  974. // and Log File paths must be present.
  975. //
  976. if( !_DataFilePath.Initialize( DataFileArgument.GetPath() ) ||
  977. !_LogFilePath.Initialize( LogFileArgument.GetPath() ) ) {
  978. DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  979. *ExitCode = 1;
  980. return FALSE;
  981. }
  982. }
  983. if( !_SidCache.Initialize( 100 ) ) {
  984. return FALSE;
  985. }
  986. if (!_AclWorkSids.Initialize(1)) {
  987. return FALSE;
  988. }
  989. return TRUE;
  990. }
  991. BOOLEAN
  992. ACLCONV::DetermineDataFileRevision(
  993. )
  994. /*++
  995. Routine Description:
  996. This method examines the data file to determine what revision
  997. of the Lanman BackAcc utility produced it. It sets the private
  998. member variable _DataFileRevision.
  999. Arguments:
  1000. None.
  1001. Return Value:
  1002. TRUE upon successful completion. _DataFileRevision is set
  1003. appropriately.
  1004. --*/
  1005. {
  1006. CHAR Buffer[RecognitionSize];
  1007. ULONG BytesRead;
  1008. // If the first four bytes of the file are the LM2.0 backacc signature,
  1009. // assume the data file was produced by LM2.0 backacc. LM2.1 backacc
  1010. // data files are recognizable by the string "LM210 BACKACC" at
  1011. // byte offset 4.
  1012. _DataFileRevision = DataFileRevisionUnknown;
  1013. if( _DataFileStream == NULL ||
  1014. !_DataFileStream->ReadAt( (PBYTE)Buffer,
  1015. RecognitionSize,
  1016. 0,
  1017. STREAM_BEGINNING,
  1018. &BytesRead ) ||
  1019. BytesRead != RecognitionSize ) {
  1020. return FALSE;
  1021. }
  1022. if( strncmp( Buffer, (PCHAR)&Lm20BackaccSignature, 4) == 0 ) {
  1023. _DataFileRevision = DataFileRevisionLanman20;
  1024. return TRUE;
  1025. }
  1026. if( strncmp( Buffer+Lm21BackaccSignatureOffset,
  1027. Lm21BackaccSignature,
  1028. Lm21BackaccSignatureLength ) == 0 ) {
  1029. _DataFileRevision = DataFileRevisionLanman21;
  1030. return TRUE;
  1031. }
  1032. return FALSE;
  1033. }
  1034. BOOLEAN
  1035. ACLCONV::UpdateAndQueryCurrentLM21Name(
  1036. IN ULONG DirectoryLevel,
  1037. IN PCSTR NewComponent,
  1038. OUT PWSTRING CurrentName
  1039. )
  1040. /*++
  1041. Routine Description:
  1042. This function updates the current resource name while
  1043. traversing the LM21 BackAcc data file.
  1044. Arguments:
  1045. DirectoryLevel -- Supplies the directory level of the
  1046. most-recently-encountered component.
  1047. (0 is the drive, 1 is the root directory,
  1048. 2 is an element in the root directory, and
  1049. so forth.)
  1050. NewComponent -- Supplies the name of the most-recently-
  1051. encountered component.
  1052. CurrentName -- Receives the updated current resource name.
  1053. Return Value:
  1054. TRUE upon successful completion.
  1055. --*/
  1056. {
  1057. STATIC PDSTRING Components[256];
  1058. STATIC PDSTRING BackSlash;
  1059. STATIC ULONG CurrentLevel = 0;
  1060. WCHAR ComponentBuffer[ MAX_RESOURCE_NAME_LENGTH ];
  1061. ULONG i;
  1062. // If BackSlash hasn't been initialized yet,
  1063. // initialize it.
  1064. //
  1065. if( BackSlash == NULL ) {
  1066. if( (BackSlash = NEW DSTRING) == NULL ||
  1067. !BackSlash->Initialize( "\\" ) ) {
  1068. return FALSE;
  1069. }
  1070. }
  1071. if( DirectoryLevel == 0 ) {
  1072. return( CurrentName->Initialize( "" ) );
  1073. }
  1074. while( CurrentLevel >= DirectoryLevel ) {
  1075. // Trim off the last component of the path.
  1076. //
  1077. CurrentLevel--;
  1078. DELETE( Components[CurrentLevel] );
  1079. }
  1080. // Now we're ready to add a new component to the end of the
  1081. // current path.
  1082. //
  1083. if( DirectoryLevel != CurrentLevel + 1 ) {
  1084. DebugPrint( "ACLCONV: skipped a level in name tree.\n" );
  1085. return FALSE;
  1086. }
  1087. memset( ComponentBuffer, 0, sizeof( ComponentBuffer ) );
  1088. if( (Components[CurrentLevel] = NEW DSTRING) == NULL ||
  1089. !MultiByteToWideChar( _SourceCodepage,
  1090. 0,
  1091. NewComponent,
  1092. strlen( NewComponent ),
  1093. ComponentBuffer,
  1094. MAX_RESOURCE_NAME_LENGTH ) ||
  1095. !Components[CurrentLevel]->Initialize( ComponentBuffer ) ) {
  1096. return FALSE;
  1097. }
  1098. CurrentLevel++;
  1099. // Now copy the current path to the output string.
  1100. //
  1101. if( !CurrentName->Initialize( "" ) ) {
  1102. return FALSE;
  1103. }
  1104. for( i = 0; i < CurrentLevel; i++ ) {
  1105. // There's no backslash before the first component (the drive);
  1106. // if the prefix ends in a backslash, don't add one. Otherwise,
  1107. // add a backslash.
  1108. //
  1109. if( i > 0 &&
  1110. CurrentName->QueryChAt( CurrentName->QueryChCount() - 1 ) != '\\' &&
  1111. !CurrentName->Strcat( BackSlash ) ) {
  1112. return FALSE;
  1113. }
  1114. // Add the next component
  1115. //
  1116. if( !CurrentName->Strcat( Components[i] ) ) {
  1117. return FALSE;
  1118. }
  1119. }
  1120. return TRUE;
  1121. }
  1122. BOOLEAN
  1123. ACLCONV::ReadNextAcl(
  1124. OUT PINT ExitCode,
  1125. OUT PWSTRING ResourceString,
  1126. IN ULONG MaxEntries,
  1127. OUT PULONG AccessEntryCount,
  1128. OUT PVOID AccessEntries,
  1129. OUT PUSHORT AuditInfo
  1130. )
  1131. /*++
  1132. Routine Description:
  1133. This method reads the next ACL record from the data file. The
  1134. ACL record describes the resource and its associated Access Control
  1135. Entries.
  1136. Arguments:
  1137. ExitCode -- Receives an error level if an error occurs.
  1138. ResourceString -- Receives the name of the resource.
  1139. MaxEntries -- Supplies the maximum number of entries
  1140. that will fit in the supplied buffer.
  1141. AccessEntryCount -- Receives the number of access entries read.
  1142. AccessEntries -- Receives the access entries.
  1143. AuditInfo -- Receives the Lanman 2.x audit information
  1144. for the resource.
  1145. Return Value:
  1146. TRUE upon successful completion. If the end of the file is
  1147. reached without error, this method returns FALSE, with *ExitCode
  1148. equal to zero.
  1149. --*/
  1150. {
  1151. CHAR ResourceName[ MAX_RESOURCE_NAME_LENGTH ];
  1152. WCHAR WideResourceName[ MAX_RESOURCE_NAME_LENGTH ];
  1153. lm20_resource_info ResourceInfo;
  1154. lm21_aclhdr Lm21Header;
  1155. lm21_aclrec Lm21AclRec;
  1156. ULONG BytesRead, TotalBytesRead;
  1157. if( _DataFileStream == NULL ) {
  1158. DebugAbort( "Data stream not set up.\n" );
  1159. return FALSE;
  1160. }
  1161. switch( _DataFileRevision ) {
  1162. case DataFileRevisionUnknown :
  1163. DebugAbort( "Trying to read from unknown data file revision.\n" );
  1164. *ExitCode = 1;
  1165. return FALSE;
  1166. case DataFileRevisionLanman20 :
  1167. if( _NextReadOffset == 0 ) {
  1168. // This is the first read, so we skip over the header
  1169. // information.
  1170. _NextReadOffset = LM20_BACKACC_HEADER_SIZE +
  1171. LM20_INDEX_SIZE * LM20_NINDEX;
  1172. }
  1173. if( !_DataFileStream->MovePointerPosition( _NextReadOffset,
  1174. STREAM_BEGINNING ) ) {
  1175. *ExitCode = 1;
  1176. return FALSE;
  1177. }
  1178. if( _DataFileStream->IsAtEnd() ) {
  1179. // No more entries to read.
  1180. *ExitCode = 0;
  1181. return FALSE;
  1182. }
  1183. // Read the resource header information.
  1184. if( !_DataFileStream->Read( (PBYTE)&ResourceInfo,
  1185. LM20_RESOURCE_INFO_HEADER_SIZE,
  1186. &BytesRead ) ||
  1187. BytesRead != LM20_RESOURCE_INFO_HEADER_SIZE ) {
  1188. *ExitCode = 1;
  1189. return FALSE;
  1190. }
  1191. // Read the name and initialize ResourceString
  1192. //
  1193. memset( WideResourceName, 0, sizeof( WideResourceName ) );
  1194. if( ResourceInfo.namelen > MAX_RESOURCE_NAME_LENGTH ||
  1195. !_DataFileStream->Read( (PBYTE)ResourceName,
  1196. ResourceInfo.namelen,
  1197. &BytesRead ) ||
  1198. BytesRead != ResourceInfo.namelen ||
  1199. !MultiByteToWideChar( _SourceCodepage,
  1200. 0,
  1201. ResourceName,
  1202. strlen( ResourceName ),
  1203. WideResourceName,
  1204. MAX_RESOURCE_NAME_LENGTH ) ||
  1205. !ResourceString->Initialize( WideResourceName ) ) {
  1206. *ExitCode = 1;
  1207. return FALSE;
  1208. }
  1209. // Read the access entries
  1210. if( (ULONG)ResourceInfo.acc1_count > MaxEntries ||
  1211. !_DataFileStream->Read( (PBYTE)AccessEntries,
  1212. ResourceInfo.acc1_count *
  1213. LM_ACCESS_LIST_SIZE,
  1214. &BytesRead ) ||
  1215. BytesRead != (ULONG)( ResourceInfo.acc1_count * LM_ACCESS_LIST_SIZE ) ) {
  1216. *ExitCode = 1;
  1217. return FALSE;
  1218. }
  1219. *AccessEntryCount = ResourceInfo.acc1_count;
  1220. *AuditInfo = ResourceInfo.acc1_attr;
  1221. _NextReadOffset += LM20_RESOURCE_INFO_HEADER_SIZE +
  1222. ResourceInfo.namelen +
  1223. ResourceInfo.acc1_count * LM_ACCESS_LIST_SIZE;
  1224. return TRUE;
  1225. case DataFileRevisionLanman21 :
  1226. while( _NextReadOffset == 0 || _BytesRemainingInCurrentGroup == 0 ) {
  1227. // The current offset is at the beginning of a group. If
  1228. // this also the end of the file, there are no more records
  1229. // to read; otherwise, read the header of this group.
  1230. if( !_DataFileStream->MovePointerPosition( _NextReadOffset,
  1231. STREAM_BEGINNING ) ) {
  1232. *ExitCode = 1;
  1233. return FALSE;
  1234. }
  1235. if( _DataFileStream->IsAtEnd() ) {
  1236. // No more entries to read.
  1237. *ExitCode = 0;
  1238. return FALSE;
  1239. }
  1240. if( !_DataFileStream->Read( (PBYTE)&Lm21Header,
  1241. LM21_ACLHDR_SIZE,
  1242. &BytesRead ) ||
  1243. BytesRead != LM21_ACLHDR_SIZE ) {
  1244. *ExitCode = 1;
  1245. return FALSE;
  1246. }
  1247. _NextReadOffset += LM21_ACLHDR_SIZE;
  1248. _BytesRemainingInCurrentGroup = Lm21Header.NxtHdr -
  1249. _NextReadOffset;
  1250. }
  1251. // Read the ACL Record
  1252. if( !_DataFileStream->Read( (PBYTE)&Lm21AclRec,
  1253. LM21_ACLREC_SIZE,
  1254. &BytesRead ) ||
  1255. BytesRead != LM21_ACLREC_SIZE ) {
  1256. *ExitCode = 1;
  1257. return FALSE;
  1258. }
  1259. TotalBytesRead = BytesRead;
  1260. // Read the name and initialize ResourceString
  1261. if( Lm21AclRec.NameBytes > MAX_RESOURCE_NAME_LENGTH ||
  1262. !_DataFileStream->Read( (PBYTE)ResourceName,
  1263. Lm21AclRec.NameBytes,
  1264. &BytesRead ) ||
  1265. BytesRead != (ULONG)Lm21AclRec.NameBytes ) {
  1266. *ExitCode = 1;
  1267. return FALSE;
  1268. }
  1269. if( !UpdateAndQueryCurrentLM21Name( Lm21AclRec.DirLvl,
  1270. ResourceName,
  1271. ResourceString ) ) {
  1272. *ExitCode = 1;
  1273. return FALSE;
  1274. }
  1275. TotalBytesRead += BytesRead;
  1276. // Read the access entries:
  1277. //
  1278. if( Lm21AclRec.AclCnt == -1 ) {
  1279. *AccessEntryCount = 0;
  1280. *AuditInfo = 0;
  1281. } else {
  1282. if( (ULONG)Lm21AclRec.AclCnt > MaxEntries ||
  1283. !_DataFileStream->Read( (PBYTE)AccessEntries,
  1284. Lm21AclRec.AclCnt *
  1285. LM_ACCESS_LIST_SIZE,
  1286. &BytesRead ) ||
  1287. BytesRead != (ULONG)( Lm21AclRec.AclCnt * LM_ACCESS_LIST_SIZE ) ) {
  1288. *ExitCode = 1;
  1289. return FALSE;
  1290. }
  1291. TotalBytesRead += BytesRead;
  1292. *AccessEntryCount = Lm21AclRec.AclCnt;
  1293. *AuditInfo = Lm21AclRec.AuditAttrib;
  1294. }
  1295. // Check that this read didn't overflow the current group--if
  1296. // it did, the file format is not as expected.
  1297. if( TotalBytesRead > _BytesRemainingInCurrentGroup ) {
  1298. *ExitCode = 1;
  1299. return FALSE;
  1300. }
  1301. _NextReadOffset += TotalBytesRead;
  1302. _BytesRemainingInCurrentGroup -= TotalBytesRead;
  1303. return TRUE;
  1304. default:
  1305. *ExitCode = 1;
  1306. return FALSE;
  1307. }
  1308. }
  1309. BOOLEAN
  1310. ACLCONV::ReadAclWorkSids(
  1311. )
  1312. {
  1313. WORD n_entries;
  1314. ULONG n_read;
  1315. ULONG i;
  1316. USHORT name_len;
  1317. WCHAR name[64];
  1318. DSTRING Name;
  1319. PSID pSid;
  1320. ULONG sid_len;
  1321. DSTRING Domain;
  1322. if (!Domain.Initialize("UserConv")) {
  1323. DisplayMessage(MSG_CONV_NO_MEMORY, ERROR_MESSAGE);
  1324. return FALSE;
  1325. }
  1326. if (!_AclWorkStream->Read((PUCHAR)&n_entries, sizeof(WORD), &n_read) ||
  1327. n_read != sizeof(WORD)) {
  1328. DisplayMessage(MSG_ACLCONV_DATAFILE_BAD_FORMAT, ERROR_MESSAGE,
  1329. "%W", _AclWorkPath.GetPathString());
  1330. return FALSE;
  1331. }
  1332. if (!_AclWorkSids.Initialize(n_entries)) {
  1333. DisplayMessage(MSG_CONV_NO_MEMORY, ERROR_MESSAGE);
  1334. return FALSE;
  1335. }
  1336. //
  1337. // Read each entry from the aclwork.dat file and add it to this
  1338. // sid cache.
  1339. //
  1340. for (i = 0; i < n_entries; ++i) {
  1341. // read the length of the name
  1342. if (!_AclWorkStream->Read((PUCHAR)&name_len, sizeof(name_len),
  1343. &n_read) || n_read != sizeof(name_len)) {
  1344. DisplayMessage(MSG_ACLCONV_DATAFILE_BAD_FORMAT, ERROR_MESSAGE,
  1345. "%W", _AclWorkPath.GetPathString());
  1346. return FALSE;
  1347. }
  1348. // read the name
  1349. if (!_AclWorkStream->Read((PUCHAR)name, name_len, &n_read) ||
  1350. n_read != name_len) {
  1351. DisplayMessage(MSG_ACLCONV_DATAFILE_BAD_FORMAT, ERROR_MESSAGE,
  1352. "%W", _AclWorkPath.GetPathString());
  1353. return FALSE;
  1354. }
  1355. name[name_len / sizeof(WCHAR)] = UNICODE_NULL;
  1356. if (!Name.Initialize(name)) {
  1357. DisplayMessage(MSG_CONV_NO_MEMORY, ERROR_MESSAGE);
  1358. return FALSE;
  1359. }
  1360. if (!_AclWorkStream->Read((PUCHAR)&sid_len, sizeof(sid_len), &n_read) ||
  1361. n_read != sizeof(sid_len)) {
  1362. DisplayMessage(MSG_ACLCONV_DATAFILE_BAD_FORMAT, ERROR_MESSAGE,
  1363. "%W", _AclWorkPath.GetPathString());
  1364. return FALSE;
  1365. }
  1366. pSid = (PSID)MALLOC(sid_len);
  1367. if (NULL == pSid) {
  1368. DisplayMessage(MSG_CONV_NO_MEMORY, ERROR_MESSAGE);
  1369. return FALSE;
  1370. }
  1371. if (!_AclWorkStream->Read((PUCHAR)pSid, sid_len, &n_read) ||
  1372. n_read != sid_len) {
  1373. DisplayMessage(MSG_ACLCONV_DATAFILE_BAD_FORMAT, ERROR_MESSAGE,
  1374. "%W", _AclWorkPath.GetPathString());
  1375. return FALSE;
  1376. }
  1377. DebugAssert(RtlValidSid(pSid));
  1378. if (!_AclWorkSids.CacheSid( &Domain, &Name, pSid, sid_len)) {
  1379. DisplayMessage(MSG_CONV_NO_MEMORY, ERROR_MESSAGE);
  1380. return FALSE;
  1381. }
  1382. FREE(pSid);
  1383. }
  1384. return TRUE;
  1385. }