1936 lines
49 KiB
C++
1936 lines
49 KiB
C++
/*++
|
||
|
||
Copyright (c) 1992-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
aclconv.cxx
|
||
|
||
Abstract:
|
||
|
||
This module contains function definitions for the ACLCONV class,
|
||
which implements conversion of Lanman 2.x ACLs into NT ACLs.
|
||
|
||
Author:
|
||
|
||
Bill McJohn (billmc) 29-Jan-1992
|
||
|
||
Revision History:
|
||
|
||
|
||
Environment:
|
||
|
||
ULIB, User Mode
|
||
|
||
--*/
|
||
|
||
|
||
#define _NTAPI_ULIB_
|
||
|
||
#include "ulib.hxx"
|
||
#include "ulibcl.hxx"
|
||
#include "array.hxx"
|
||
#include "arrayit.hxx"
|
||
#include "arg.hxx"
|
||
#include "smsg.hxx"
|
||
#include "rtmsg.h"
|
||
#include "wstring.hxx"
|
||
#include "system.hxx"
|
||
#include "aclconv.hxx"
|
||
#include "file.hxx"
|
||
#include "filestrm.hxx"
|
||
|
||
#include "logfile.hxx"
|
||
|
||
|
||
BOOLEAN
|
||
QueryFileSystemName(
|
||
IN PCWSTRING RootName,
|
||
OUT PDSTRING FileSystemName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determines the name of the file system on the specified volume.
|
||
|
||
Arguments:
|
||
|
||
RootName -- Supplies the name of the volume's root directory.
|
||
FileSystemName -- Receives the file system name.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
WCHAR NameBuffer[8];
|
||
|
||
if( !GetVolumeInformation( RootName->GetWSTR(),
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NameBuffer,
|
||
8 ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
return( FileSystemName->Initialize( NameBuffer ) );
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
EnablePrivilege(
|
||
PWSTR Privilege
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine tries to adjust the priviliege of the current process.
|
||
|
||
|
||
Arguments:
|
||
|
||
Privilege - String with the name of the privilege to be adjusted.
|
||
|
||
Return Value:
|
||
|
||
Returns TRUE if the privilege could be adjusted.
|
||
Returns FALSE, otherwise.
|
||
|
||
|
||
--*/
|
||
{
|
||
HANDLE TokenHandle;
|
||
LUID_AND_ATTRIBUTES LuidAndAttributes;
|
||
|
||
TOKEN_PRIVILEGES TokenPrivileges;
|
||
|
||
|
||
if( !OpenProcessToken( GetCurrentProcess(),
|
||
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
||
&TokenHandle ) ) {
|
||
DebugPrint( "OpenProcessToken failed" );
|
||
return( FALSE );
|
||
}
|
||
|
||
|
||
if( !LookupPrivilegeValue( NULL,
|
||
Privilege,
|
||
&( LuidAndAttributes.Luid ) ) ) {
|
||
DebugPrintTrace(( "LookupPrivilegeValue failed, Error = %#d \n", GetLastError() ));
|
||
return( FALSE );
|
||
}
|
||
|
||
LuidAndAttributes.Attributes = SE_PRIVILEGE_ENABLED;
|
||
TokenPrivileges.PrivilegeCount = 1;
|
||
TokenPrivileges.Privileges[0] = LuidAndAttributes;
|
||
|
||
if( !AdjustTokenPrivileges( TokenHandle,
|
||
FALSE,
|
||
&TokenPrivileges,
|
||
0,
|
||
NULL,
|
||
NULL ) ) {
|
||
DebugPrintTrace(( "AdjustTokenPrivileges failed, Error = %#x \n", GetLastError() ));
|
||
return( FALSE );
|
||
}
|
||
|
||
if( GetLastError() != NO_ERROR ) {
|
||
return( FALSE );
|
||
}
|
||
return( TRUE );
|
||
}
|
||
|
||
|
||
INT __cdecl
|
||
main(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Entry point for the ACL conversion utility.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
An error level--0 indicates success.
|
||
|
||
--*/
|
||
{
|
||
INT ExitCode = 0;
|
||
|
||
if( !DEFINE_CLASS_DESCRIPTOR( ACLCONV ) ||
|
||
!DEFINE_CLASS_DESCRIPTOR( SID_CACHE ) ||
|
||
!DEFINE_CLASS_DESCRIPTOR( ACL_CONVERT_NODE ) ) {
|
||
|
||
return 1;
|
||
}
|
||
|
||
{
|
||
ACLCONV Aclconv;
|
||
|
||
if( Aclconv.Initialize( &ExitCode ) ) {
|
||
|
||
if( Aclconv.IsInListMode() ) {
|
||
|
||
ExitCode = Aclconv.ListLogFile();
|
||
|
||
} else {
|
||
|
||
ExitCode = Aclconv.ConvertAcls();
|
||
}
|
||
}
|
||
}
|
||
|
||
return ExitCode;
|
||
}
|
||
|
||
|
||
DEFINE_CONSTRUCTOR( ACLCONV, PROGRAM );
|
||
|
||
ACLCONV::~ACLCONV(
|
||
)
|
||
{
|
||
Destroy();
|
||
}
|
||
|
||
VOID
|
||
ACLCONV::Construct(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Helper method for object construction.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
_DataFileRevision = DataFileRevisionUnknown;
|
||
|
||
_DataFile = NULL;
|
||
_DataFileStream = NULL;
|
||
_LogFile = NULL;
|
||
_LogFileStream = NULL;
|
||
|
||
_AclWorkFile = NULL;
|
||
_AclWorkStream = NULL;
|
||
|
||
_NewDrive = NULL;
|
||
|
||
_RootNode = NULL;
|
||
_DriveName = NULL;
|
||
_DomainName = NULL;
|
||
_SidLookupTableName = NULL;
|
||
|
||
}
|
||
|
||
VOID
|
||
ACLCONV::Destroy(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Helper function for object destruction.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
_DataFileRevision = DataFileRevisionUnknown;
|
||
_NextReadOffset = 0;
|
||
_BytesRemainingInCurrentGroup = 0;
|
||
|
||
DELETE( _DataFile );
|
||
DELETE( _DataFileStream );
|
||
DELETE( _LogFile );
|
||
DELETE( _LogFileStream );
|
||
|
||
DELETE( _AclWorkFile );
|
||
DELETE( _AclWorkStream );
|
||
DELETE( _NewDrive );
|
||
DELETE( _RootNode );
|
||
DELETE( _DriveName );
|
||
DELETE( _DomainName );
|
||
DELETE( _SidLookupTableName );
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
ACLCONV::Initialize(
|
||
OUT PINT ExitCode
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialize the ACLCONV object.
|
||
|
||
Arguments:
|
||
|
||
ExitCode -- Receives an error level if this method fails.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
Destroy();
|
||
|
||
if( !PROGRAM::Initialize( ) ) {
|
||
|
||
Destroy();
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
return ParseArguments( ExitCode );
|
||
}
|
||
|
||
|
||
INT
|
||
ACLCONV::ListLogFile(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method reads a log file produced by a previous run of
|
||
ACLCONV and displays the errors logged to that file.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
An error level--zero indicates success.
|
||
|
||
--*/
|
||
{
|
||
LM_ACCESS_LIST AccessEntries[ MAX_ACCESS_ENTRIES ];
|
||
ULONG AceConversionCodes[ MAX_ACCESS_ENTRIES ];
|
||
|
||
ACLCONV_LOGFILE_HEADER LogFileHeader;
|
||
DSTRING ResourceName;
|
||
ULONG AccessEntryCount, BytesRead, ConversionCode, i;
|
||
INT ExitCode = 0;
|
||
USHORT AuditInfo;
|
||
|
||
// Open the log file and reset the seek pointer to the beginning
|
||
// of the file.
|
||
//
|
||
if( (_LogFile = SYSTEM::QueryFile( &_LogFilePath )) == NULL ||
|
||
(_LogFileStream = _LogFile->QueryStream( READ_ACCESS )) == NULL ) {
|
||
|
||
// Cannot create log file.
|
||
|
||
DisplayMessage( MSG_ACLCONV_CANT_OPEN_FILE,
|
||
ERROR_MESSAGE,
|
||
"%W",
|
||
_LogFilePath.GetPathString() );
|
||
return 1;
|
||
}
|
||
|
||
// Check the log file signature:
|
||
//
|
||
if( !_LogFileStream->MovePointerPosition( 0, STREAM_BEGINNING ) ||
|
||
!_LogFileStream->Read( (PBYTE)&LogFileHeader,
|
||
sizeof( ACLCONV_LOGFILE_HEADER ),
|
||
&BytesRead ) ||
|
||
BytesRead != sizeof( ACLCONV_LOGFILE_HEADER ) ||
|
||
LogFileHeader.Signature != AclconvLogFileSignature ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_INVALID_LOG_FILE,
|
||
ERROR_MESSAGE );
|
||
|
||
return 1;
|
||
}
|
||
|
||
_NextReadOffset = sizeof( ACLCONV_LOGFILE_HEADER );
|
||
|
||
while( ReadNextLogRecord( &ExitCode,
|
||
&ResourceName,
|
||
&ConversionCode,
|
||
&AuditInfo,
|
||
MAX_ACCESS_ENTRIES,
|
||
&AccessEntryCount,
|
||
AccessEntries,
|
||
AceConversionCodes ) ) {
|
||
|
||
// Scan to see if there are any entries to display
|
||
//
|
||
if( AccessEntryCount != 0 ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_RESOURCE_NAME,
|
||
NORMAL_MESSAGE,
|
||
"%W",
|
||
&ResourceName );
|
||
|
||
for( i = 0; i < AccessEntryCount; i++ ) {
|
||
|
||
DisplayAce( (ACL_CONVERT_CODE)ConversionCode,
|
||
(ACE_CONVERT_CODE)AceConversionCodes[i],
|
||
AccessEntries + i );
|
||
}
|
||
}
|
||
}
|
||
|
||
if( ExitCode ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_LOGFILE_READ_ERROR, ERROR_MESSAGE );
|
||
}
|
||
|
||
return ExitCode;
|
||
}
|
||
|
||
|
||
|
||
NONVIRTUAL
|
||
BOOLEAN
|
||
ACLCONV::DisplayAce(
|
||
IN ACL_CONVERT_CODE AclConvertCode,
|
||
IN ACE_CONVERT_CODE AceConvertCode,
|
||
IN PLM_ACCESS_LIST Ace
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method displays the conversion result for a single ACE.
|
||
|
||
Arguments:
|
||
|
||
AclConvertCode -- Supplies the overall conversion code for
|
||
the resource to which this ACE is attached.
|
||
AceConvertCode -- Supplies the conversion result for this
|
||
particular ACE. Note that if the AclConvertCode
|
||
is not ACL_CONVERT_SUCCESS, it takes priority
|
||
over AceConvertCode.
|
||
Ace -- Supplies the ACE in question.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
WCHAR WideNameBuffer[ UNLEN + 1 ];
|
||
DSTRING Temp;
|
||
DSTRING Name;
|
||
|
||
memset( WideNameBuffer, 0, sizeof( WideNameBuffer ) );
|
||
|
||
// Display the user's name. If it's a group, prepend an
|
||
// asterisk.
|
||
//
|
||
if( !MultiByteToWideChar( _SourceCodepage,
|
||
0,
|
||
Ace->acl_ugname,
|
||
strlen( Ace->acl_ugname ),
|
||
WideNameBuffer,
|
||
UNLEN + 1 ) ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
return FALSE;
|
||
}
|
||
|
||
if( !Temp.Initialize( WideNameBuffer ) ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
return FALSE;
|
||
}
|
||
|
||
if( Ace->acl_access & LM_ACCESS_GROUP ) {
|
||
|
||
if( !Name.Initialize( "*" ) ||
|
||
!Name.Strcat( &Temp ) ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
return FALSE;
|
||
}
|
||
|
||
} else {
|
||
|
||
if( !Name.Initialize( &Temp ) ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
DisplayMessage( MSG_ACLCONV_USERNAME, NORMAL_MESSAGE, "%W", &Name );
|
||
|
||
|
||
// Display the permissions:
|
||
//
|
||
if( (Ace->acl_access & ~LM_ACCESS_GROUP) == 0 ) {
|
||
|
||
// This is a no-access ACE.
|
||
//
|
||
DisplayMessage( MSG_ACLCONV_NONE_PERM );
|
||
|
||
} else {
|
||
|
||
// This ACE grants some sort of access--check each type
|
||
// of access in turn, displaying all we find.
|
||
//
|
||
if( Ace->acl_access & LM_ACCESS_READ ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_READ_PERM );
|
||
}
|
||
|
||
if( Ace->acl_access & LM_ACCESS_WRITE ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_WRITE_PERM );
|
||
}
|
||
|
||
if( Ace->acl_access & LM_ACCESS_CREATE ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_CREATE_PERM );
|
||
}
|
||
|
||
if( Ace->acl_access & LM_ACCESS_EXEC ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_EXECUTE_PERM );
|
||
}
|
||
|
||
if( Ace->acl_access & LM_ACCESS_DELETE ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_DELETE_PERM );
|
||
}
|
||
|
||
if( Ace->acl_access & LM_ACCESS_ATRIB ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_ATTR_PERM );
|
||
}
|
||
|
||
if( Ace->acl_access & LM_ACCESS_PERM ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_PERM_PERM );
|
||
}
|
||
}
|
||
|
||
|
||
// Display the cause of failure:
|
||
//
|
||
if( AclConvertCode != ACL_CONVERT_SUCCESS ) {
|
||
|
||
// The failure is associated with the resource.
|
||
//
|
||
switch( AclConvertCode ) {
|
||
|
||
case ACL_CONVERT_RESOURCE_NOT_FOUND :
|
||
DisplayMessage( MSG_ACLCONV_FILE_NOT_FOUND );
|
||
break;
|
||
|
||
case ACL_CONVERT_ERROR :
|
||
default :
|
||
DisplayMessage( MSG_ACLCONV_ERROR_IN_CONVERSION );
|
||
break;
|
||
}
|
||
|
||
} else {
|
||
|
||
// Display the ACE conversion result.
|
||
//
|
||
switch( AceConvertCode ) {
|
||
|
||
case ACL_CONVERT_SUCCESS :
|
||
DisplayMessage( MSG_ACLCONV_ACE_CONVERTED );
|
||
break;
|
||
|
||
case ACE_CONVERT_DROPPED :
|
||
DisplayMessage( MSG_ACLCONV_ACE_DROPPED );
|
||
break;
|
||
|
||
case ACE_CONVERT_SID_NOT_FOUND :
|
||
DisplayMessage( MSG_ACLCONV_SID_NOT_FOUND );
|
||
break;
|
||
|
||
case ACE_CONVERT_ERROR :
|
||
default:
|
||
DisplayMessage( MSG_ACLCONV_ERROR_IN_CONVERSION );
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
NONVIRTUAL
|
||
BOOLEAN
|
||
ACLCONV::ReadNextLogRecord(
|
||
OUT PINT ExitCode,
|
||
OUT PWSTRING ResourceString,
|
||
OUT PULONG ConversionCode,
|
||
OUT PUSHORT AuditInfo,
|
||
IN ULONG MaxEntries,
|
||
OUT PULONG AccessEntryCount,
|
||
OUT PLM_ACCESS_LIST AccessEntries,
|
||
OUT PULONG AceConversionCodes
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method reads the next log entry from the log file.
|
||
|
||
Arguments:
|
||
|
||
ExitCode -- receives an exit code if an error occurs.
|
||
ResourceString -- receives the name of the resource
|
||
ConversionCode -- receives the conversion result for this resource.
|
||
AuditInfo -- receives the audit information for this resource.
|
||
MaxEntries -- supplies the maximum number of ACE's that can
|
||
be written to the output buffers.
|
||
AccessEntryCount -- receives the number of ACE's written to
|
||
the output buffers.
|
||
AccessEntries -- receives the logged ACE's
|
||
AceConversionCodes -- receives the conversion results for the
|
||
individual ACE's.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion (in which case ExitCode may
|
||
be ignored). FALSE if there are no more entries (in which
|
||
case ExitCode is zero) or if an error occurs (in which case
|
||
ExitCode is non-zero).
|
||
|
||
--*/
|
||
{
|
||
ACLCONV_LOG_RECORD_HEADER Header;
|
||
ULONG BytesRead;
|
||
|
||
if( _LogFileStream->IsAtEnd() ) {
|
||
|
||
// No more entries to read.
|
||
|
||
*ExitCode = 0;
|
||
return FALSE;
|
||
}
|
||
|
||
// Read the log record header
|
||
//
|
||
if( !_LogFileStream->Read( (PBYTE)&Header,
|
||
sizeof( ACLCONV_LOG_RECORD_HEADER ),
|
||
&BytesRead ) ||
|
||
BytesRead != sizeof( ACLCONV_LOG_RECORD_HEADER ) ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
*ConversionCode = Header.ConversionResult;
|
||
*AuditInfo = Header.LmAuditMask;
|
||
*AccessEntryCount = Header.AccessEntryCount;
|
||
|
||
// Make sure that the name is not longer than the maximum
|
||
// name length (plus room for trailing NULL) and then read
|
||
// it into the name workspace and use it to initialize the
|
||
// client's resource name string.
|
||
//
|
||
if( Header.ResourceNameLength > MAX_RESOURCE_NAME_LENGTH + 1 ||
|
||
!_LogFileStream->Read( (PBYTE)_NameBuffer,
|
||
Header.ResourceNameLength * sizeof( WCHAR ),
|
||
&BytesRead ) ||
|
||
BytesRead != Header.ResourceNameLength * sizeof( WCHAR ) ||
|
||
!ResourceString->Initialize( _NameBuffer ) ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
// Make sure the ACE's and their associated convert codes will
|
||
// fit in the supplied buffers:
|
||
//
|
||
if( Header.AccessEntryCount > MaxEntries ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
// Read the ACE conversion codes and the ACE's themselves:
|
||
//
|
||
if( Header.AccessEntryCount != 0 &&
|
||
( !_LogFileStream->Read( (PBYTE)AceConversionCodes,
|
||
Header.AccessEntryCount * sizeof( ULONG ),
|
||
&BytesRead ) ||
|
||
BytesRead != Header.AccessEntryCount * sizeof( ULONG ) ) ||
|
||
|
||
( !_LogFileStream->Read( (PBYTE)AccessEntries,
|
||
Header.AccessEntryCount *
|
||
sizeof( LM_ACCESS_LIST ),
|
||
&BytesRead ) ||
|
||
BytesRead != Header.AccessEntryCount * sizeof( LM_ACCESS_LIST ) ) ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
INT
|
||
ACLCONV::ConvertAcls(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method reads the ACL's from the data file into a tree
|
||
of ACL Convert Nodes, and then converts the ACL's to NT
|
||
security descriptors and applies them to the files and
|
||
directories in question.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
An error level--zero indicates success.
|
||
|
||
--*/
|
||
{
|
||
|
||
LM_ACCESS_LIST AccessEntries[MAX_ACCESS_ENTRIES];
|
||
INHERITANCE_BUFFER Inheritance;
|
||
|
||
FSTRING NtfsString;
|
||
DSTRING FsName;
|
||
DSTRING CurrentResource;
|
||
PATH CurrentResourcePath;
|
||
|
||
ACLCONV_LOGFILE_HEADER LogfileHeader;
|
||
|
||
PARRAY Components = NULL;
|
||
PARRAY_ITERATOR ComponentIterator = NULL;
|
||
PACL_CONVERT_NODE CurrentNode, NextNode;
|
||
PWSTRING CurrentComponent;
|
||
|
||
ULONG AccessEntryCount, BytesWritten;
|
||
USHORT LmAuditInfo;
|
||
|
||
INT ExitCode = 0;
|
||
|
||
DSTRING AclWorkString;
|
||
|
||
// Open aclwork.dat and read the contents into a special
|
||
// sid cache.
|
||
|
||
if (!AclWorkString.Initialize(L"aclwork.dat")) {
|
||
return 1;
|
||
}
|
||
if (!_AclWorkPath.Initialize(&AclWorkString)) {
|
||
return 1;
|
||
}
|
||
|
||
if (NULL == (_AclWorkFile = SYSTEM::QueryFile(&_AclWorkPath))) {
|
||
|
||
// try to open aclwork.dat in the same directory as the
|
||
// data file.
|
||
|
||
if (!_AclWorkPath.Initialize(&_DataFilePath)) {
|
||
return 1;
|
||
}
|
||
if (!_AclWorkPath.SetName(&AclWorkString)) {
|
||
return 1;
|
||
}
|
||
|
||
_AclWorkFile = SYSTEM::QueryFile(&_AclWorkPath);
|
||
}
|
||
if (NULL != _AclWorkFile &&
|
||
NULL != (_AclWorkStream = _AclWorkFile->QueryStream(READ_ACCESS))) {
|
||
|
||
// DisplayMessage( MSG_ACLCONV_USING_ACLWORK, NORMAL_MESSAGE );
|
||
|
||
if (!ReadAclWorkSids()) {
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
// Open the data file and determine its format (ie. what
|
||
// revision of BackAcc produced it).
|
||
|
||
if( (_DataFile = SYSTEM::QueryFile( &_DataFilePath )) == NULL ||
|
||
(_DataFileStream = _DataFile->QueryStream( READ_ACCESS )) == NULL ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_CANT_OPEN_FILE,
|
||
ERROR_MESSAGE,
|
||
"%W",
|
||
_DataFilePath.GetPathString() );
|
||
return 1;
|
||
}
|
||
|
||
// Note that DetermineDataFileRevision sets _DataFileRevision.
|
||
|
||
if( !DetermineDataFileRevision( ) ||
|
||
_DataFileRevision == DataFileRevisionUnknown ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_DATAFILE_BAD_FORMAT,
|
||
ERROR_MESSAGE,
|
||
"%W",
|
||
_DataFilePath.GetPathString() );
|
||
return 1;
|
||
}
|
||
|
||
|
||
// Create the log file.
|
||
|
||
LogfileHeader.Signature = AclconvLogFileSignature;
|
||
|
||
if( (_LogFile = SYSTEM::MakeFile( &_LogFilePath )) == NULL ||
|
||
(_LogFileStream = _LogFile->QueryStream( WRITE_ACCESS )) == NULL ||
|
||
!_LogFileStream->Write( (PBYTE)&LogfileHeader,
|
||
sizeof( ACLCONV_LOGFILE_HEADER ),
|
||
&BytesWritten ) ||
|
||
BytesWritten != sizeof( ACLCONV_LOGFILE_HEADER ) ) {
|
||
|
||
// Cannot create log file.
|
||
|
||
DisplayMessage( MSG_ACLCONV_CANT_OPEN_FILE,
|
||
ERROR_MESSAGE,
|
||
"%W",
|
||
_LogFilePath.GetPathString() );
|
||
return 1;
|
||
}
|
||
|
||
|
||
while( ReadNextAcl( &ExitCode,
|
||
&CurrentResource,
|
||
MAX_ACCESS_ENTRIES,
|
||
&AccessEntryCount,
|
||
(PVOID)AccessEntries,
|
||
&LmAuditInfo ) ) {
|
||
|
||
if( CurrentResource.QueryChCount() == 0 ) {
|
||
|
||
// This resource has no name; ignore it.
|
||
//
|
||
continue;
|
||
}
|
||
|
||
if( !CurrentResourcePath.Initialize( &CurrentResource ) ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_DATAFILE_ERROR, ERROR_MESSAGE );
|
||
return 1;
|
||
}
|
||
|
||
// If the user specified a substitute drive, use it.
|
||
//
|
||
if( _NewDrive != NULL &&
|
||
!CurrentResourcePath.SetDevice( _NewDrive ) ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
return 1;
|
||
}
|
||
|
||
if( _RootNode == NULL ) {
|
||
|
||
// This is the first ACL--create the root of the tree
|
||
// and determine the name of the drive.
|
||
|
||
if( !(_DriveName = CurrentResourcePath.QueryRoot()) ||
|
||
!(_RootNode = NEW ACL_CONVERT_NODE) ||
|
||
!_RootNode->Initialize( _DriveName ) ) {
|
||
|
||
DELETE( _RootNode );
|
||
DELETE( _DriveName );
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
// Fetch the component array for this resource.
|
||
|
||
DELETE( ComponentIterator );
|
||
|
||
if( Components != NULL ) {
|
||
|
||
Components->DeleteAllMembers();
|
||
}
|
||
|
||
DELETE( Components );
|
||
|
||
if( !(Components = CurrentResourcePath.QueryComponentArray()) ||
|
||
!(ComponentIterator = (PARRAY_ITERATOR)
|
||
Components->QueryIterator()) ) {
|
||
|
||
DELETE( ComponentIterator );
|
||
|
||
if( Components != NULL ) {
|
||
|
||
Components->DeleteAllMembers();
|
||
}
|
||
|
||
DELETE( Components );
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
return 1;
|
||
}
|
||
|
||
CurrentNode = _RootNode;
|
||
|
||
ComponentIterator->Reset();
|
||
|
||
// The first component is the drive & root directory, which
|
||
// isn't interesting.
|
||
|
||
CurrentComponent = (PWSTRING)ComponentIterator->GetNext();
|
||
|
||
// Traverse the tree down to the end of the path, creating
|
||
// new nodes as needed.
|
||
|
||
while( (CurrentComponent = (PWSTRING)ComponentIterator->GetNext())
|
||
!= NULL ) {
|
||
|
||
if( !(NextNode = CurrentNode->GetChild( CurrentComponent )) &&
|
||
!(NextNode = CurrentNode->AddChild( CurrentComponent )) ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
return 1;
|
||
}
|
||
|
||
CurrentNode = NextNode;
|
||
}
|
||
|
||
// Add the Lanman ACL to the node which represents the end of
|
||
// the path.
|
||
|
||
if( !CurrentNode->AddLanmanAcl( AccessEntryCount,
|
||
AccessEntries,
|
||
LmAuditInfo ) ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
|
||
if( ExitCode != 0 ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_DATAFILE_ERROR, ERROR_MESSAGE );
|
||
return 1;
|
||
}
|
||
|
||
// Traverse the tree and convert all the ACE's, propagating
|
||
// as we go.
|
||
//
|
||
|
||
// Adjust this process' privileges so that it can twiddle
|
||
// System ACL's.
|
||
//
|
||
if( !EnablePrivilege( (LPWSTR)SE_SECURITY_NAME ) ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_CONVERSION_ERROR, ERROR_MESSAGE );
|
||
ExitCode = 1;
|
||
}
|
||
|
||
if( ExitCode == 0 &&
|
||
_RootNode != NULL ) {
|
||
|
||
// Make sure the target drive is NTFS.
|
||
//
|
||
if( !NtfsString.Initialize( (PWSTR)L"NTFS" ) ||
|
||
!QueryFileSystemName( _RootNode->GetName(), &FsName ) ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_CANT_DETERMINE_FILESYSTEM, ERROR_MESSAGE );
|
||
ExitCode = 1;
|
||
|
||
} else if( FsName.Stricmp( &NtfsString ) != 0 ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_TARGET_NOT_NTFS, ERROR_MESSAGE );
|
||
ExitCode = 1;
|
||
|
||
} else {
|
||
|
||
// Set up an empty inheritance buffer to pass
|
||
// to the root.
|
||
//
|
||
Inheritance.RecessiveDeniedAces = NULL;
|
||
Inheritance.RecessiveAllowedAces = NULL;
|
||
Inheritance.DominantDeniedAces = NULL;
|
||
Inheritance.DominantAllowedAces = NULL;
|
||
|
||
Inheritance.RecessiveDeniedMaxLength = 0;
|
||
Inheritance.RecessiveAllowedMaxLength = 0;
|
||
Inheritance.DominantDeniedMaxLength = 0;
|
||
Inheritance.DominantAllowedMaxLength = 0;
|
||
|
||
Inheritance.RecessiveDeniedLength = 0;
|
||
Inheritance.RecessiveAllowedLength = 0;
|
||
Inheritance.DominantDeniedLength = 0;
|
||
Inheritance.DominantAllowedLength = 0;
|
||
|
||
if( !_RootNode->Convert( NULL,
|
||
&Inheritance,
|
||
this ) ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_CONVERSION_ERROR, ERROR_MESSAGE );
|
||
ExitCode = 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
if( ExitCode == 0 ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_CONVERT_COMPLETE );
|
||
}
|
||
|
||
return ExitCode;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
ACLCONV::LogConversion(
|
||
IN PPATH Resource,
|
||
IN ULONG ConversionCode,
|
||
IN ULONG LmAuditInfo,
|
||
IN ULONG AccessEntryCount,
|
||
IN PCULONG AceConversionCodes,
|
||
IN PCLM_ACCESS_LIST AccessEntries
|
||
)
|
||
/*+
|
||
|
||
Routine Description:
|
||
|
||
This method writes information about the conversion of a resource
|
||
to the log file.
|
||
|
||
Arguments:
|
||
|
||
Resource -- Supplies the path to the resource
|
||
ConversionCode -- Supplies the conversion result for the
|
||
resource.
|
||
LmAuditInfo -- Supplies the Lanman 2.x audit information
|
||
associated with the resource.
|
||
AccessEntryCount -- Supplies the number of Lanman 2.x access
|
||
entries associated with the resource.
|
||
AceConversionCodes -- Supplies the conversion results of the
|
||
individual ACE's
|
||
AccessEntries -- Supplies the Lanman 2.x access control
|
||
entries.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
ACLCONV_LOG_RECORD_HEADER Header;
|
||
PCWSTRING PathString;
|
||
|
||
ULONG NameLength, BytesWritten;
|
||
|
||
DebugPtrAssert( Resource );
|
||
DebugPtrAssert( _LogFileStream );
|
||
|
||
if( (PathString = Resource->GetPathString()) == NULL ||
|
||
(NameLength = PathString->QueryChCount()) > MAX_RESOURCE_NAME_LENGTH ||
|
||
!PathString->QueryWSTR( 0,
|
||
TO_END,
|
||
_NameBuffer,
|
||
MAX_RESOURCE_NAME_LENGTH + 1 ) ){
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
Header.ResourceNameLength = NameLength + 1;
|
||
Header.ConversionResult = ConversionCode;
|
||
Header.LmAuditMask = (USHORT)LmAuditInfo;
|
||
Header.AccessEntryCount = (USHORT)AccessEntryCount;
|
||
|
||
if(!_LogFileStream->Write( (PBYTE)&Header,
|
||
sizeof( ACLCONV_LOG_RECORD_HEADER ),
|
||
&BytesWritten ) ||
|
||
BytesWritten != sizeof( ACLCONV_LOG_RECORD_HEADER ) ||
|
||
!_LogFileStream->Write( (PBYTE)_NameBuffer,
|
||
(NameLength + 1) * sizeof( WCHAR ),
|
||
&BytesWritten ) ||
|
||
BytesWritten != (NameLength + 1) * sizeof( WCHAR )) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_LOGFILE_ERROR, ERROR_MESSAGE );
|
||
return FALSE;
|
||
}
|
||
|
||
if( AccessEntryCount != 0 &&
|
||
( !_LogFileStream->Write( (PBYTE)AceConversionCodes,
|
||
AccessEntryCount * sizeof(ULONG),
|
||
&BytesWritten ) ||
|
||
BytesWritten != AccessEntryCount * sizeof(ULONG) ||
|
||
!_LogFileStream->Write( (PBYTE)AccessEntries,
|
||
AccessEntryCount * sizeof( LM_ACCESS_LIST ),
|
||
&BytesWritten ) ||
|
||
BytesWritten != AccessEntryCount * sizeof( LM_ACCESS_LIST ) ) ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_LOGFILE_ERROR, ERROR_MESSAGE );
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
ACLCONV::ParseArguments(
|
||
OUT PINT ExitCode
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method parses the arguments given to ACLCONV and sets the
|
||
state of the object appropriately.
|
||
|
||
The accepted syntax is:
|
||
|
||
ACLCONV [/?] [/V] /DATA:datafile /LOG:logfile
|
||
|
||
Arguments:
|
||
|
||
ExitCode -- Receives an exit code if the method fails.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
Note that this method will fail, but return an exit-code
|
||
of zero (success) if the user specifies the /? argument.
|
||
|
||
--*/
|
||
{
|
||
ARRAY ArgArray; // Array of arguments
|
||
ARRAY LexArray; // Array of lexemes
|
||
ARGUMENT_LEXEMIZER ArgLex; // Argument Lexemizer
|
||
STRING_ARGUMENT ProgramNameArgument; // Program name argument
|
||
PATH_ARGUMENT DataFileArgument; // Path to data file
|
||
PATH_ARGUMENT LogFileArgument; // Path to log file
|
||
PATH_ARGUMENT DriveArgument; // New drive to use
|
||
FLAG_ARGUMENT ListArgument; // List flag argument
|
||
FLAG_ARGUMENT HelpArgument; // Help flag argument
|
||
STRING_ARGUMENT DomainArgument; // Domain name argument
|
||
LONG_ARGUMENT CodepageArgument; // Source Codepage argument
|
||
STRING_ARGUMENT SidLookupArgument; // Filename of lookup table
|
||
PWSTRING InvalidArg; // Invalid argument catcher
|
||
DSTRING Backslash; // Backslash
|
||
DSTRING RootDir; // Root directory of the new drive
|
||
UINT DriveType;
|
||
|
||
|
||
DebugPtrAssert( ExitCode );
|
||
|
||
//
|
||
// Initialize all the argument parsing machinery.
|
||
//
|
||
if( !ArgArray.Initialize( 5, 1 ) ||
|
||
!LexArray.Initialize( 5, 1 ) ||
|
||
!ArgLex.Initialize( &LexArray ) ||
|
||
!HelpArgument.Initialize( "/?" ) ||
|
||
!ListArgument.Initialize( "/LIST" ) ||
|
||
!ProgramNameArgument.Initialize( "*" ) ||
|
||
!DataFileArgument.Initialize( "/DATA:*" ) ||
|
||
!LogFileArgument.Initialize( "/LOG:*" ) ||
|
||
!DriveArgument.Initialize( "/NEWDRIVE:*" ) ||
|
||
!DomainArgument.Initialize( "/DOMAIN:*" ) ||
|
||
!CodepageArgument.Initialize( "/CODEPAGE:*" ) ||
|
||
!SidLookupArgument.Initialize( "/SIDLOOKUP:*" ) ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// The ACL conversion utility is case-insensitive
|
||
//
|
||
ArgLex.SetCaseSensitive( FALSE );
|
||
|
||
// Put the arguments into the argument array
|
||
|
||
if( !ArgArray.Put( &HelpArgument ) ||
|
||
!ArgArray.Put( &ListArgument ) ||
|
||
!ArgArray.Put( &DataFileArgument ) ||
|
||
!ArgArray.Put( &LogFileArgument ) ||
|
||
!ArgArray.Put( &DriveArgument ) ||
|
||
!ArgArray.Put( &DomainArgument ) ||
|
||
!ArgArray.Put( &CodepageArgument ) ||
|
||
!ArgArray.Put( &ProgramNameArgument ) ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Lexemize the command line.
|
||
//
|
||
if ( !ArgLex.PrepareToParse() ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Parse the arguments.
|
||
//
|
||
if( !ArgLex.DoParsing( &ArgArray ) ) {
|
||
|
||
|
||
DisplayMessage( MSG_CONV_INVALID_PARAMETER, ERROR_MESSAGE, "%W", InvalidArg = ArgLex.QueryInvalidArgument() );
|
||
DELETE( InvalidArg );
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
//
|
||
// If the user requested help, give it.
|
||
//
|
||
if( HelpArgument.QueryFlag() ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_USAGE );
|
||
|
||
*ExitCode = 0;
|
||
return FALSE;
|
||
}
|
||
|
||
// The log file must be specified, and either the data
|
||
// file or the list argument (but not both) must be
|
||
// provided.
|
||
//
|
||
if( !LogFileArgument.IsValueSet() ||
|
||
( !DataFileArgument.IsValueSet() &&
|
||
!ListArgument.IsValueSet() ) ||
|
||
( DataFileArgument.IsValueSet() &&
|
||
ListArgument.IsValueSet() ) ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_USAGE );
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
// If the drive argument has been supplied, record it:
|
||
//
|
||
if( DriveArgument.IsValueSet() ) {
|
||
|
||
_NewDrive = DriveArgument.GetPath()->QueryDevice();
|
||
|
||
if( _NewDrive == NULL ) {
|
||
|
||
DisplayMessage( MSG_INVALID_PARAMETER,
|
||
ERROR_MESSAGE,
|
||
"%W",
|
||
DriveArgument.GetPath()->GetPathString() );
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
// Validate the drive.
|
||
//
|
||
if( !RootDir.Initialize( _NewDrive ) ||
|
||
!Backslash.Initialize( "\\" ) ||
|
||
!RootDir.Strcat( &Backslash ) ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
DriveType = GetDriveTypeW( RootDir.GetWSTR() );
|
||
|
||
switch ( DriveType ) {
|
||
|
||
case DRIVE_FIXED:
|
||
case DRIVE_REMOVABLE:
|
||
|
||
// The drive type is acceptable.
|
||
//
|
||
break;
|
||
|
||
case 0:
|
||
case 1:
|
||
case DRIVE_CDROM:
|
||
case DRIVE_REMOTE:
|
||
case DRIVE_RAMDISK:
|
||
default:
|
||
|
||
// The drive type is invalid.
|
||
//
|
||
DisplayMessage( MSG_ACLCONV_INVALID_DRIVE, ERROR_MESSAGE, "%W", _NewDrive );
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
// If a domain name has been specified, remember it:
|
||
//
|
||
if( DomainArgument.IsValueSet() ) {
|
||
|
||
if( (_DomainName = NEW DSTRING) == NULL ||
|
||
!_DomainName->Initialize( DomainArgument.GetString() ) ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
// If a source codepage has been specified, use it; otherwise,
|
||
// use CP_OEMCP as default.
|
||
//
|
||
if( !CodepageArgument.IsValueSet() ) {
|
||
|
||
_SourceCodepage = CP_OEMCP;
|
||
|
||
} else {
|
||
|
||
_SourceCodepage = CodepageArgument.QueryLong();
|
||
|
||
if( !IsValidCodePage( _SourceCodepage ) ) {
|
||
|
||
DisplayMessage( MSG_ACLCONV_BAD_CODEPAGE, ERROR_MESSAGE );
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if( SidLookupArgument.IsValueSet() ) {
|
||
|
||
_SidLookupTableName = SidLookupArgument.GetString()->QueryWSTR();
|
||
}
|
||
|
||
_IsInListMode = ListArgument.IsValueSet();
|
||
|
||
if( _IsInListMode ) {
|
||
|
||
// In list mode, only the Log File path need be present.
|
||
//
|
||
if( !_LogFilePath.Initialize( LogFileArgument.GetPath() ) ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
} else {
|
||
|
||
// The object is not in list mode, so both the Data File
|
||
// and Log File paths must be present.
|
||
//
|
||
if( !_DataFilePath.Initialize( DataFileArgument.GetPath() ) ||
|
||
!_LogFilePath.Initialize( LogFileArgument.GetPath() ) ) {
|
||
|
||
DisplayMessage( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
if( !_SidCache.Initialize( 100 ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
if (!_AclWorkSids.Initialize(1)) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
|
||
BOOLEAN
|
||
ACLCONV::DetermineDataFileRevision(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method examines the data file to determine what revision
|
||
of the Lanman BackAcc utility produced it. It sets the private
|
||
member variable _DataFileRevision.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion. _DataFileRevision is set
|
||
appropriately.
|
||
|
||
--*/
|
||
{
|
||
CHAR Buffer[RecognitionSize];
|
||
ULONG BytesRead;
|
||
|
||
// If the first four bytes of the file are the LM2.0 backacc signature,
|
||
// assume the data file was produced by LM2.0 backacc. LM2.1 backacc
|
||
// data files are recognizable by the string "LM210 BACKACC" at
|
||
// byte offset 4.
|
||
|
||
|
||
_DataFileRevision = DataFileRevisionUnknown;
|
||
|
||
if( _DataFileStream == NULL ||
|
||
!_DataFileStream->ReadAt( (PBYTE)Buffer,
|
||
RecognitionSize,
|
||
0,
|
||
STREAM_BEGINNING,
|
||
&BytesRead ) ||
|
||
BytesRead != RecognitionSize ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
if( strncmp( Buffer, (PCHAR)&Lm20BackaccSignature, 4) == 0 ) {
|
||
|
||
_DataFileRevision = DataFileRevisionLanman20;
|
||
return TRUE;
|
||
}
|
||
|
||
if( strncmp( Buffer+Lm21BackaccSignatureOffset,
|
||
Lm21BackaccSignature,
|
||
Lm21BackaccSignatureLength ) == 0 ) {
|
||
|
||
_DataFileRevision = DataFileRevisionLanman21;
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
BOOLEAN
|
||
ACLCONV::UpdateAndQueryCurrentLM21Name(
|
||
IN ULONG DirectoryLevel,
|
||
IN PCSTR NewComponent,
|
||
OUT PWSTRING CurrentName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function updates the current resource name while
|
||
traversing the LM21 BackAcc data file.
|
||
|
||
Arguments:
|
||
|
||
DirectoryLevel -- Supplies the directory level of the
|
||
most-recently-encountered component.
|
||
(0 is the drive, 1 is the root directory,
|
||
2 is an element in the root directory, and
|
||
so forth.)
|
||
NewComponent -- Supplies the name of the most-recently-
|
||
encountered component.
|
||
CurrentName -- Receives the updated current resource name.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
STATIC PDSTRING Components[256];
|
||
STATIC PDSTRING BackSlash;
|
||
STATIC ULONG CurrentLevel = 0;
|
||
|
||
WCHAR ComponentBuffer[ MAX_RESOURCE_NAME_LENGTH ];
|
||
ULONG i;
|
||
|
||
// If BackSlash hasn't been initialized yet,
|
||
// initialize it.
|
||
//
|
||
if( BackSlash == NULL ) {
|
||
|
||
if( (BackSlash = NEW DSTRING) == NULL ||
|
||
!BackSlash->Initialize( "\\" ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if( DirectoryLevel == 0 ) {
|
||
|
||
return( CurrentName->Initialize( "" ) );
|
||
}
|
||
|
||
while( CurrentLevel >= DirectoryLevel ) {
|
||
|
||
// Trim off the last component of the path.
|
||
//
|
||
CurrentLevel--;
|
||
DELETE( Components[CurrentLevel] );
|
||
}
|
||
|
||
|
||
// Now we're ready to add a new component to the end of the
|
||
// current path.
|
||
//
|
||
if( DirectoryLevel != CurrentLevel + 1 ) {
|
||
|
||
DebugPrint( "ACLCONV: skipped a level in name tree.\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
memset( ComponentBuffer, 0, sizeof( ComponentBuffer ) );
|
||
|
||
if( (Components[CurrentLevel] = NEW DSTRING) == NULL ||
|
||
!MultiByteToWideChar( _SourceCodepage,
|
||
0,
|
||
NewComponent,
|
||
strlen( NewComponent ),
|
||
ComponentBuffer,
|
||
MAX_RESOURCE_NAME_LENGTH ) ||
|
||
!Components[CurrentLevel]->Initialize( ComponentBuffer ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
CurrentLevel++;
|
||
|
||
// Now copy the current path to the output string.
|
||
//
|
||
if( !CurrentName->Initialize( "" ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
for( i = 0; i < CurrentLevel; i++ ) {
|
||
|
||
// There's no backslash before the first component (the drive);
|
||
// if the prefix ends in a backslash, don't add one. Otherwise,
|
||
// add a backslash.
|
||
//
|
||
if( i > 0 &&
|
||
CurrentName->QueryChAt( CurrentName->QueryChCount() - 1 ) != '\\' &&
|
||
!CurrentName->Strcat( BackSlash ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
// Add the next component
|
||
//
|
||
if( !CurrentName->Strcat( Components[i] ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
ACLCONV::ReadNextAcl(
|
||
OUT PINT ExitCode,
|
||
OUT PWSTRING ResourceString,
|
||
IN ULONG MaxEntries,
|
||
OUT PULONG AccessEntryCount,
|
||
OUT PVOID AccessEntries,
|
||
OUT PUSHORT AuditInfo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method reads the next ACL record from the data file. The
|
||
ACL record describes the resource and its associated Access Control
|
||
Entries.
|
||
|
||
Arguments:
|
||
|
||
ExitCode -- Receives an error level if an error occurs.
|
||
ResourceString -- Receives the name of the resource.
|
||
MaxEntries -- Supplies the maximum number of entries
|
||
that will fit in the supplied buffer.
|
||
AccessEntryCount -- Receives the number of access entries read.
|
||
AccessEntries -- Receives the access entries.
|
||
AuditInfo -- Receives the Lanman 2.x audit information
|
||
for the resource.
|
||
|
||
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion. If the end of the file is
|
||
reached without error, this method returns FALSE, with *ExitCode
|
||
equal to zero.
|
||
|
||
--*/
|
||
{
|
||
CHAR ResourceName[ MAX_RESOURCE_NAME_LENGTH ];
|
||
WCHAR WideResourceName[ MAX_RESOURCE_NAME_LENGTH ];
|
||
|
||
lm20_resource_info ResourceInfo;
|
||
lm21_aclhdr Lm21Header;
|
||
lm21_aclrec Lm21AclRec;
|
||
ULONG BytesRead, TotalBytesRead;
|
||
|
||
if( _DataFileStream == NULL ) {
|
||
|
||
DebugAbort( "Data stream not set up.\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
switch( _DataFileRevision ) {
|
||
|
||
case DataFileRevisionUnknown :
|
||
|
||
DebugAbort( "Trying to read from unknown data file revision.\n" );
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
|
||
case DataFileRevisionLanman20 :
|
||
|
||
if( _NextReadOffset == 0 ) {
|
||
|
||
// This is the first read, so we skip over the header
|
||
// information.
|
||
|
||
_NextReadOffset = LM20_BACKACC_HEADER_SIZE +
|
||
LM20_INDEX_SIZE * LM20_NINDEX;
|
||
|
||
}
|
||
|
||
if( !_DataFileStream->MovePointerPosition( _NextReadOffset,
|
||
STREAM_BEGINNING ) ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
if( _DataFileStream->IsAtEnd() ) {
|
||
|
||
// No more entries to read.
|
||
|
||
*ExitCode = 0;
|
||
return FALSE;
|
||
}
|
||
|
||
// Read the resource header information.
|
||
|
||
if( !_DataFileStream->Read( (PBYTE)&ResourceInfo,
|
||
LM20_RESOURCE_INFO_HEADER_SIZE,
|
||
&BytesRead ) ||
|
||
BytesRead != LM20_RESOURCE_INFO_HEADER_SIZE ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
// Read the name and initialize ResourceString
|
||
//
|
||
memset( WideResourceName, 0, sizeof( WideResourceName ) );
|
||
|
||
if( ResourceInfo.namelen > MAX_RESOURCE_NAME_LENGTH ||
|
||
!_DataFileStream->Read( (PBYTE)ResourceName,
|
||
ResourceInfo.namelen,
|
||
&BytesRead ) ||
|
||
BytesRead != ResourceInfo.namelen ||
|
||
!MultiByteToWideChar( _SourceCodepage,
|
||
0,
|
||
ResourceName,
|
||
strlen( ResourceName ),
|
||
WideResourceName,
|
||
MAX_RESOURCE_NAME_LENGTH ) ||
|
||
!ResourceString->Initialize( WideResourceName ) ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
// Read the access entries
|
||
|
||
if( (ULONG)ResourceInfo.acc1_count > MaxEntries ||
|
||
!_DataFileStream->Read( (PBYTE)AccessEntries,
|
||
ResourceInfo.acc1_count *
|
||
LM_ACCESS_LIST_SIZE,
|
||
&BytesRead ) ||
|
||
BytesRead != (ULONG)( ResourceInfo.acc1_count * LM_ACCESS_LIST_SIZE ) ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
*AccessEntryCount = ResourceInfo.acc1_count;
|
||
*AuditInfo = ResourceInfo.acc1_attr;
|
||
|
||
_NextReadOffset += LM20_RESOURCE_INFO_HEADER_SIZE +
|
||
ResourceInfo.namelen +
|
||
ResourceInfo.acc1_count * LM_ACCESS_LIST_SIZE;
|
||
|
||
return TRUE;
|
||
|
||
case DataFileRevisionLanman21 :
|
||
|
||
while( _NextReadOffset == 0 || _BytesRemainingInCurrentGroup == 0 ) {
|
||
|
||
// The current offset is at the beginning of a group. If
|
||
// this also the end of the file, there are no more records
|
||
// to read; otherwise, read the header of this group.
|
||
|
||
if( !_DataFileStream->MovePointerPosition( _NextReadOffset,
|
||
STREAM_BEGINNING ) ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
if( _DataFileStream->IsAtEnd() ) {
|
||
|
||
// No more entries to read.
|
||
|
||
*ExitCode = 0;
|
||
return FALSE;
|
||
}
|
||
|
||
if( !_DataFileStream->Read( (PBYTE)&Lm21Header,
|
||
LM21_ACLHDR_SIZE,
|
||
&BytesRead ) ||
|
||
BytesRead != LM21_ACLHDR_SIZE ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
_NextReadOffset += LM21_ACLHDR_SIZE;
|
||
_BytesRemainingInCurrentGroup = Lm21Header.NxtHdr -
|
||
_NextReadOffset;
|
||
}
|
||
|
||
// Read the ACL Record
|
||
|
||
if( !_DataFileStream->Read( (PBYTE)&Lm21AclRec,
|
||
LM21_ACLREC_SIZE,
|
||
&BytesRead ) ||
|
||
BytesRead != LM21_ACLREC_SIZE ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
TotalBytesRead = BytesRead;
|
||
|
||
// Read the name and initialize ResourceString
|
||
|
||
if( Lm21AclRec.NameBytes > MAX_RESOURCE_NAME_LENGTH ||
|
||
!_DataFileStream->Read( (PBYTE)ResourceName,
|
||
Lm21AclRec.NameBytes,
|
||
&BytesRead ) ||
|
||
BytesRead != (ULONG)Lm21AclRec.NameBytes ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
if( !UpdateAndQueryCurrentLM21Name( Lm21AclRec.DirLvl,
|
||
ResourceName,
|
||
ResourceString ) ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
TotalBytesRead += BytesRead;
|
||
|
||
// Read the access entries:
|
||
//
|
||
if( Lm21AclRec.AclCnt == -1 ) {
|
||
|
||
*AccessEntryCount = 0;
|
||
*AuditInfo = 0;
|
||
|
||
} else {
|
||
|
||
if( (ULONG)Lm21AclRec.AclCnt > MaxEntries ||
|
||
!_DataFileStream->Read( (PBYTE)AccessEntries,
|
||
Lm21AclRec.AclCnt *
|
||
LM_ACCESS_LIST_SIZE,
|
||
&BytesRead ) ||
|
||
BytesRead != (ULONG)( Lm21AclRec.AclCnt * LM_ACCESS_LIST_SIZE ) ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
TotalBytesRead += BytesRead;
|
||
|
||
*AccessEntryCount = Lm21AclRec.AclCnt;
|
||
*AuditInfo = Lm21AclRec.AuditAttrib;
|
||
}
|
||
|
||
|
||
// Check that this read didn't overflow the current group--if
|
||
// it did, the file format is not as expected.
|
||
|
||
if( TotalBytesRead > _BytesRemainingInCurrentGroup ) {
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
}
|
||
|
||
_NextReadOffset += TotalBytesRead;
|
||
_BytesRemainingInCurrentGroup -= TotalBytesRead;
|
||
|
||
|
||
return TRUE;
|
||
|
||
default:
|
||
|
||
*ExitCode = 1;
|
||
return FALSE;
|
||
|
||
}
|
||
}
|
||
|
||
BOOLEAN
|
||
ACLCONV::ReadAclWorkSids(
|
||
)
|
||
{
|
||
WORD n_entries;
|
||
ULONG n_read;
|
||
ULONG i;
|
||
USHORT name_len;
|
||
WCHAR name[64];
|
||
DSTRING Name;
|
||
PSID pSid;
|
||
ULONG sid_len;
|
||
|
||
DSTRING Domain;
|
||
|
||
if (!Domain.Initialize("UserConv")) {
|
||
DisplayMessage(MSG_CONV_NO_MEMORY, ERROR_MESSAGE);
|
||
return FALSE;
|
||
}
|
||
|
||
if (!_AclWorkStream->Read((PUCHAR)&n_entries, sizeof(WORD), &n_read) ||
|
||
n_read != sizeof(WORD)) {
|
||
DisplayMessage(MSG_ACLCONV_DATAFILE_BAD_FORMAT, ERROR_MESSAGE,
|
||
"%W", _AclWorkPath.GetPathString());
|
||
return FALSE;
|
||
}
|
||
|
||
if (!_AclWorkSids.Initialize(n_entries)) {
|
||
DisplayMessage(MSG_CONV_NO_MEMORY, ERROR_MESSAGE);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Read each entry from the aclwork.dat file and add it to this
|
||
// sid cache.
|
||
//
|
||
|
||
for (i = 0; i < n_entries; ++i) {
|
||
|
||
// read the length of the name
|
||
|
||
if (!_AclWorkStream->Read((PUCHAR)&name_len, sizeof(name_len),
|
||
&n_read) || n_read != sizeof(name_len)) {
|
||
|
||
DisplayMessage(MSG_ACLCONV_DATAFILE_BAD_FORMAT, ERROR_MESSAGE,
|
||
"%W", _AclWorkPath.GetPathString());
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
// read the name
|
||
|
||
if (!_AclWorkStream->Read((PUCHAR)name, name_len, &n_read) ||
|
||
n_read != name_len) {
|
||
|
||
DisplayMessage(MSG_ACLCONV_DATAFILE_BAD_FORMAT, ERROR_MESSAGE,
|
||
"%W", _AclWorkPath.GetPathString());
|
||
|
||
return FALSE;
|
||
}
|
||
name[name_len / sizeof(WCHAR)] = UNICODE_NULL;
|
||
|
||
if (!Name.Initialize(name)) {
|
||
DisplayMessage(MSG_CONV_NO_MEMORY, ERROR_MESSAGE);
|
||
return FALSE;
|
||
}
|
||
|
||
if (!_AclWorkStream->Read((PUCHAR)&sid_len, sizeof(sid_len), &n_read) ||
|
||
n_read != sizeof(sid_len)) {
|
||
|
||
DisplayMessage(MSG_ACLCONV_DATAFILE_BAD_FORMAT, ERROR_MESSAGE,
|
||
"%W", _AclWorkPath.GetPathString());
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
pSid = (PSID)MALLOC(sid_len);
|
||
if (NULL == pSid) {
|
||
DisplayMessage(MSG_CONV_NO_MEMORY, ERROR_MESSAGE);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
if (!_AclWorkStream->Read((PUCHAR)pSid, sid_len, &n_read) ||
|
||
n_read != sid_len) {
|
||
|
||
DisplayMessage(MSG_ACLCONV_DATAFILE_BAD_FORMAT, ERROR_MESSAGE,
|
||
"%W", _AclWorkPath.GetPathString());
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
DebugAssert(RtlValidSid(pSid));
|
||
|
||
if (!_AclWorkSids.CacheSid( &Domain, &Name, pSid, sid_len)) {
|
||
DisplayMessage(MSG_CONV_NO_MEMORY, ERROR_MESSAGE);
|
||
return FALSE;
|
||
}
|
||
|
||
FREE(pSid);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|