2422 lines
65 KiB
C++
2422 lines
65 KiB
C++
/*++
|
||
|
||
Copyright (c) 1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
convnode.cxx
|
||
|
||
Abstract:
|
||
|
||
This module contains member function definitions for the
|
||
ACL_CONVERT_NODE class, which models the nodes in the ACL
|
||
Conversion tree.
|
||
|
||
|
||
Author:
|
||
|
||
Bill McJohn (billmc) 09-Feb-1992
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
#define _NTAPI_ULIB_
|
||
|
||
#include "ulib.hxx"
|
||
#include "ulibcl.hxx"
|
||
#include "error.hxx"
|
||
#include "wstring.hxx"
|
||
#include "file.hxx"
|
||
#include "filestrm.hxx"
|
||
#include "system.hxx"
|
||
#include "dir.hxx"
|
||
|
||
#include "convnode.hxx"
|
||
#include "acehelp.hxx"
|
||
|
||
#include "backacc.hxx"
|
||
#include "logfile.hxx"
|
||
#include "aclconv.hxx"
|
||
#include "aclres.h"
|
||
|
||
extern "C" {
|
||
#include <stdio.h>
|
||
}
|
||
|
||
LM_ACCESS_LIST AdminsImplicitAce;
|
||
BOOLEAN AdminsImplicitAceInitialized = FALSE;
|
||
|
||
BOOLEAN
|
||
CreateImplicitAce(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets up the implicit ACE which grants ADMINS full
|
||
access to all resources. Under Lanman 2.x, this access is implicit;
|
||
under NT, it must be explicitly granted.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
CONST ULONG AdminsStringBufferLength = 32;
|
||
WCHAR AdminsStringBuffer[AdminsStringBufferLength];
|
||
FSTRING AdminsString;
|
||
|
||
if( AdminsImplicitAceInitialized ) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
memset( &AdminsImplicitAce, 0, sizeof(AdminsImplicitAce) );
|
||
|
||
AdminsImplicitAce.agl_ugname_pad_1 = 0;
|
||
AdminsImplicitAce.acl_access = LM_ACCESS_ALL | LM_ACCESS_GROUP;
|
||
|
||
// Fetch the name as a resource and convert it to an eight-bit
|
||
// string.
|
||
//
|
||
if( LoadString( GetModuleHandle(NULL),
|
||
IDS_ACLCONV_LANMAN_ADMINS_NAME,
|
||
AdminsStringBuffer,
|
||
AdminsStringBufferLength ) == 0 ) {
|
||
|
||
DebugPrintf( "ACLCONV: LoadString failed--error %d.\n", GetLastError() );
|
||
return FALSE;
|
||
}
|
||
|
||
if( !AdminsString.Initialize( AdminsStringBuffer ) ||
|
||
!AdminsString.QuerySTR( 0,
|
||
TO_END,
|
||
AdminsImplicitAce.acl_ugname,
|
||
UNLEN+1 ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: can't convert admins string to STR.\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
AdminsImplicitAceInitialized = TRUE;
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
IsWindowsDir(
|
||
IN PCPATH ObjectPath
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function determines whether the input canonical path is
|
||
the Windows NT path.
|
||
|
||
Arguments:
|
||
|
||
ObjectPath -- Supplies the path in question.
|
||
|
||
Return Value:
|
||
|
||
TRUE if ObjectPath represents the Windows NT Directory,
|
||
otherwise FALSE.
|
||
|
||
--*/
|
||
{
|
||
STATIC WCHAR DirBuffer[MAX_PATH];
|
||
STATIC BOOLEAN Initialized = FALSE;
|
||
FSTRING WindowsDirString;
|
||
|
||
if( !Initialized &&
|
||
GetWindowsDirectory( DirBuffer, MAX_PATH ) == 0 ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
Initialized = TRUE;
|
||
|
||
if( WindowsDirString.Initialize( DirBuffer ) &&
|
||
WindowsDirString.Stricmp( ObjectPath->GetPathString() ) == 0 ) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
BOOL
|
||
InitializeWellKnownSid(
|
||
IN ULONG SidId,
|
||
OUT PSID Sid,
|
||
IN OUT PULONG Length
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is a worker for MyLookupAccountName. It fills in
|
||
a SID for one of the well-known users or groups pendant from
|
||
SECURITY_NT_AUTHORITY. These users and groups are:
|
||
|
||
Administrator
|
||
Guest
|
||
Administrators
|
||
Users
|
||
Guests
|
||
|
||
Arguments:
|
||
|
||
SidId -- Supplies the well-known SID Identifier sub-authority
|
||
relative to SECURITY_BUILTIN_DOMAIN_RID. This may be:
|
||
|
||
DOMAIN_USER_RID_ADMIN
|
||
DOMAIN_USER_RID_GUEST
|
||
DOMAIN_ALIAS_RID_ADMINS
|
||
DOMAIN_ALIAS_RID_USERS
|
||
DOMAIN_ALIAS_RID_GUESTS
|
||
|
||
Sid -- Receives the new SID.
|
||
Length -- Supplies the length of the buffer; receives the
|
||
length of the SID. If the buffer is NULL or if
|
||
the length is not sufficient, *Length is set to
|
||
the required Length, but the function returns FALSE.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion. Note that *Length will be
|
||
set to the required length even if the function fails.
|
||
|
||
--*/
|
||
{
|
||
SID_IDENTIFIER_AUTHORITY IdentifierAuthority = SECURITY_NT_AUTHORITY;
|
||
ULONG LengthRequired;
|
||
NTSTATUS Status;
|
||
|
||
LengthRequired = RtlLengthRequiredSid( 2 );
|
||
|
||
if( *Length < LengthRequired ) {
|
||
|
||
*Length = LengthRequired;
|
||
return FALSE;
|
||
}
|
||
|
||
if( Sid != NULL ) {
|
||
|
||
Status = RtlInitializeSid( Sid, &IdentifierAuthority, 2 );
|
||
|
||
if( !NT_SUCCESS(Status) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
*(RtlSubAuthoritySid( Sid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
|
||
*(RtlSubAuthoritySid( Sid, 1 )) = SidId;
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
BOOL
|
||
AclconvLookupAccountName(
|
||
IN PACLCONV Aclconv,
|
||
IN PCWSTRING AccountName,
|
||
OUT PSID Sid,
|
||
OUT PULONG SidLength
|
||
)
|
||
{
|
||
STATIC BOOLEAN Initialized = FALSE;
|
||
STATIC PDSTRING AdminString, AdminsString, UsersString,
|
||
GuestsString, BackSlash, CurrentDomain;
|
||
|
||
CONST ULONG StringBufferLength = 64;
|
||
WCHAR StringBuffer[StringBufferLength];
|
||
|
||
CONST DomainBufferLength = 0x80;
|
||
ULONG FoundDomainNameLength;
|
||
ULONG CachedSidLength;
|
||
WCHAR FoundDomainName[0x80];
|
||
SID_NAME_USE UserType;
|
||
|
||
DSTRING QualifiedName;
|
||
BOOLEAN Result;
|
||
|
||
if( !Initialized ) {
|
||
|
||
// Initialize the strings for the special names
|
||
//
|
||
if( (AdminString = NEW DSTRING) == NULL ||
|
||
(AdminsString = NEW DSTRING) == NULL ||
|
||
(UsersString = NEW DSTRING) == NULL ||
|
||
(GuestsString = NEW DSTRING) == NULL ||
|
||
(BackSlash = NEW DSTRING) == NULL ||
|
||
(CurrentDomain = NEW DSTRING) == NULL ) {
|
||
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
if( LoadString( GetModuleHandle(NULL),
|
||
IDS_ACLCONV_LANMAN_ADMIN_NAME,
|
||
StringBuffer,
|
||
StringBufferLength ) == 0 ||
|
||
!AdminString->Initialize( StringBuffer ) ||
|
||
LoadString( GetModuleHandle(NULL),
|
||
IDS_ACLCONV_LANMAN_ADMINS_NAME,
|
||
StringBuffer,
|
||
StringBufferLength ) == 0 ||
|
||
!AdminsString->Initialize( StringBuffer ) ||
|
||
LoadString( GetModuleHandle(NULL),
|
||
IDS_ACLCONV_LANMAN_USERS_NAME,
|
||
StringBuffer,
|
||
StringBufferLength ) == 0 ||
|
||
!UsersString->Initialize( StringBuffer ) ||
|
||
LoadString( GetModuleHandle(NULL),
|
||
IDS_ACLCONV_LANMAN_GUESTS_NAME,
|
||
StringBuffer,
|
||
StringBufferLength ) == 0 ||
|
||
!GuestsString->Initialize( StringBuffer ) ||
|
||
!BackSlash->Initialize( "\\" ) ||
|
||
!CurrentDomain->Initialize( "" ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
Initialized = TRUE;
|
||
|
||
//
|
||
// Check for SIDs mapped by userconv.
|
||
//
|
||
|
||
DSTRING UserConvDomain;
|
||
|
||
if (!UserConvDomain.Initialize("UserConv")) {
|
||
return FALSE;
|
||
}
|
||
|
||
if (Aclconv->GetAclWorkSids()->IsNamePresent(&UserConvDomain,
|
||
AccountName, &CachedSidLength)) {
|
||
|
||
DebugAssert(CachedSidLength != 0);
|
||
|
||
if ( CachedSidLength > *SidLength ) {
|
||
|
||
// The client's buffer is not big enough.
|
||
//
|
||
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
||
*SidLength = CachedSidLength;
|
||
return FALSE;
|
||
|
||
} else {
|
||
|
||
// The name is in the cache and the client's buffer
|
||
// is big enough. Get it out:
|
||
//
|
||
return( Aclconv->GetAclWorkSids()->QueryCachedSid(&UserConvDomain,
|
||
AccountName,
|
||
Sid,
|
||
SidLength ) );
|
||
}
|
||
}
|
||
|
||
// Check for well-known SID's. Note that ADMIN is not mapped,
|
||
// since an account ADMIN is created by PORTUAS. (And a good
|
||
// thing, too, since generating the ADMIN SID would require
|
||
// accessing the Account Domain.)
|
||
//
|
||
if( AccountName->Stricmp( AdminsString ) == 0 ) {
|
||
|
||
return InitializeWellKnownSid( DOMAIN_ALIAS_RID_ADMINS, Sid, SidLength );
|
||
}
|
||
|
||
if( AccountName->Stricmp( UsersString ) == 0 ) {
|
||
|
||
return InitializeWellKnownSid( DOMAIN_ALIAS_RID_USERS, Sid, SidLength );
|
||
}
|
||
|
||
if( AccountName->Stricmp( GuestsString ) == 0 ) {
|
||
|
||
return InitializeWellKnownSid( DOMAIN_ALIAS_RID_GUESTS, Sid, SidLength );
|
||
}
|
||
|
||
// OK, it's not one of the well-known SID's.
|
||
//
|
||
// Check the cache:
|
||
//
|
||
if( Aclconv->GetSidCache()->IsNamePresent( Aclconv->GetDomainName(),
|
||
AccountName,
|
||
&CachedSidLength ) ) {
|
||
|
||
if( CachedSidLength == 0 ) {
|
||
|
||
// The name is present in the cache without a SID,
|
||
// which means we've looked for this name before and
|
||
// not found it.
|
||
//
|
||
SetLastError( ERROR_NONE_MAPPED );
|
||
return FALSE;
|
||
|
||
} else if ( CachedSidLength > *SidLength ) {
|
||
|
||
// The client's buffer is not big enough.
|
||
//
|
||
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
||
*SidLength = CachedSidLength;
|
||
return FALSE;
|
||
|
||
} else {
|
||
|
||
// The name is in the cache and the client's buffer
|
||
// is big enough. Get it out:
|
||
//
|
||
return( Aclconv->GetSidCache()->QueryCachedSid( Aclconv->GetDomainName(),
|
||
AccountName,
|
||
Sid,
|
||
SidLength ) );
|
||
}
|
||
}
|
||
|
||
|
||
// The SID we want isn't in the cache. If the client specified
|
||
// a domain, then look for the name in that domain. Otherwise,
|
||
// search the last domain in which a name was found; if that
|
||
// fails, search the world.
|
||
//
|
||
if( Aclconv->GetDomainName() ) {
|
||
|
||
if( !QualifiedName.Initialize( Aclconv->GetDomainName() ) ||
|
||
!QualifiedName.Strcat( BackSlash ) ||
|
||
!QualifiedName.Strcat( AccountName ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
FoundDomainNameLength = DomainBufferLength;
|
||
Result = LookupAccountName( NULL,
|
||
(PWSTR)QualifiedName.GetWSTR(),
|
||
Sid,
|
||
SidLength,
|
||
FoundDomainName,
|
||
&FoundDomainNameLength,
|
||
&UserType );
|
||
|
||
if( Result ) {
|
||
|
||
// Found the SID. Add the SID to the cache. Since
|
||
// the specified domain is global to ACLCONV, there's
|
||
// no need to maintain the current domain.
|
||
//
|
||
Aclconv->GetSidCache()->CacheSid( Aclconv->GetDomainName(),
|
||
AccountName,
|
||
Sid,
|
||
*SidLength );
|
||
|
||
} else if( GetLastError() == ERROR_NONE_MAPPED ) {
|
||
|
||
// There is no such user--record that fact in the cache.
|
||
//
|
||
Aclconv->GetSidCache()->CacheSid( Aclconv->GetDomainName(),
|
||
AccountName,
|
||
NULL,
|
||
0 );
|
||
}
|
||
|
||
} else {
|
||
|
||
// Search the current domain, i.e. the last domain in which
|
||
// a search succeeded.
|
||
//
|
||
if( CurrentDomain->QueryChCount() ) {
|
||
|
||
// Construct a qualified name based on the current
|
||
// domain and search that domain.
|
||
//
|
||
if( !QualifiedName.Initialize( CurrentDomain ) ||
|
||
!QualifiedName.Strcat( BackSlash ) ||
|
||
!QualifiedName.Strcat( AccountName ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
FoundDomainNameLength = DomainBufferLength;
|
||
Result = LookupAccountName( NULL,
|
||
(PWSTR)QualifiedName.GetWSTR(),
|
||
Sid,
|
||
SidLength,
|
||
FoundDomainName,
|
||
&FoundDomainNameLength,
|
||
&UserType );
|
||
|
||
} else {
|
||
|
||
Result = FALSE;
|
||
SetLastError( ERROR_NONE_MAPPED );
|
||
}
|
||
|
||
if( !Result && GetLastError() == ERROR_NONE_MAPPED ) {
|
||
|
||
// This name isn't in the current domain (or there is no
|
||
// current domain). Do an unqualified search.
|
||
//
|
||
FoundDomainNameLength = DomainBufferLength;
|
||
Result = LookupAccountName( NULL,
|
||
(PWSTR)AccountName->GetWSTR(),
|
||
Sid,
|
||
SidLength,
|
||
FoundDomainName,
|
||
&FoundDomainNameLength,
|
||
&UserType );
|
||
|
||
if( !Result && GetLastError() == ERROR_NONE_MAPPED ) {
|
||
|
||
// This name does not exist in any trusted domain.
|
||
// Record that fact in the cache.
|
||
//
|
||
Aclconv->GetSidCache()->CacheSid( NULL, AccountName, NULL, 0 );
|
||
}
|
||
}
|
||
|
||
if( Result ) {
|
||
|
||
// This is a match. Record the current domain
|
||
// and cache the SID.
|
||
//
|
||
if( !CurrentDomain->Initialize( FoundDomainName,
|
||
FoundDomainNameLength ) ) {
|
||
|
||
SetLastError( ERROR_OUTOFMEMORY );
|
||
return FALSE;
|
||
}
|
||
|
||
Aclconv->GetSidCache()->CacheSid( CurrentDomain,
|
||
AccountName,
|
||
Sid,
|
||
*SidLength );
|
||
}
|
||
}
|
||
|
||
return Result;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
MapSpecialNames(
|
||
IN OUT PWSTRING Name,
|
||
OUT PBOOLEAN IsAdmin
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method transforms special OS/2 names (ADMIN, ADMINS)
|
||
into their corresponding NT names (Administrator, Administrators).
|
||
Other strings are left unchanged.
|
||
|
||
Arguments:
|
||
|
||
Name -- Supplies the name to map. Receives the result of
|
||
the transformation.
|
||
IsAdmin -- Receives TRUE if the name is the administrator account
|
||
or the administrators group.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
STATIC BOOLEAN Initialized = FALSE;
|
||
STATIC PWSTRING LmAdminName = NULL,
|
||
LmAdminsName = NULL,
|
||
NtAdminName = NULL,
|
||
NtAdminsName = NULL;
|
||
|
||
CONST ULONG StringBufferLength = 64;
|
||
WCHAR StringBuffer[StringBufferLength];
|
||
|
||
if( !Initialized ) {
|
||
|
||
if( (LmAdminName = NEW DSTRING) == NULL ||
|
||
(LmAdminsName = NEW DSTRING) == NULL ||
|
||
(NtAdminName = NEW DSTRING) == NULL ||
|
||
(NtAdminsName = NEW DSTRING) == NULL ||
|
||
LoadString( GetModuleHandle(NULL),
|
||
IDS_ACLCONV_LANMAN_ADMIN_NAME,
|
||
StringBuffer,
|
||
StringBufferLength ) == 0 ||
|
||
!LmAdminName->Initialize( StringBuffer ) ||
|
||
LoadString( GetModuleHandle(NULL),
|
||
IDS_ACLCONV_LANMAN_ADMINS_NAME,
|
||
StringBuffer,
|
||
StringBufferLength ) == 0 ||
|
||
!LmAdminsName->Initialize( StringBuffer ) ||
|
||
LoadString( GetModuleHandle(NULL),
|
||
IDS_ACLCONV_NT_ADMIN_NAME,
|
||
StringBuffer,
|
||
StringBufferLength ) == 0 ||
|
||
!NtAdminName->Initialize( StringBuffer ) ||
|
||
LoadString( GetModuleHandle(NULL),
|
||
IDS_ACLCONV_NT_ADMINS_NAME,
|
||
StringBuffer,
|
||
StringBufferLength ) == 0 ||
|
||
!NtAdminsName->Initialize( StringBuffer ) ) {
|
||
|
||
DELETE( LmAdminName );
|
||
DELETE( LmAdminsName );
|
||
DELETE( NtAdminName );
|
||
DELETE( NtAdminsName );
|
||
}
|
||
|
||
Initialized = TRUE;
|
||
}
|
||
|
||
if( LmAdminName == NULL ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
*IsAdmin = FALSE;
|
||
|
||
if( Name->Stricmp( LmAdminName ) == 0 ) {
|
||
|
||
*IsAdmin = TRUE;
|
||
return( Name->Initialize( NtAdminName ) );
|
||
|
||
} else if( Name->Stricmp( LmAdminsName ) == 0 ) {
|
||
|
||
*IsAdmin = TRUE;
|
||
return( Name->Initialize( NtAdminsName ) );
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
BoilAcl(
|
||
IN PACL Source,
|
||
OUT PACL Dest,
|
||
IN ULONG MaxLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function boils an ACL down to the mininum size.
|
||
|
||
Arguments:
|
||
|
||
Source -- Supplies a pointer to the source ACL
|
||
Dest -- Supplies a pointer to the buffer in which the
|
||
reduced ACL will be constructed.
|
||
MaxLength -- Supplies the size (in bytes) of the destination buffer.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
ACL_SIZE_INFORMATION AclInfo;
|
||
ACL_REVISION_INFORMATION AclRevisionInfo;
|
||
|
||
PVOID CurrentAce;
|
||
ULONG CurrentAceIndex, AceSize;
|
||
|
||
if( !GetAclInformation( Source,
|
||
&AclInfo,
|
||
sizeof( ACL_SIZE_INFORMATION ),
|
||
AclSizeInformation ) ||
|
||
!GetAclInformation( Source,
|
||
&AclRevisionInfo,
|
||
sizeof( ACL_REVISION_INFORMATION ),
|
||
AclRevisionInformation ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: GetAclInformation failed.\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
// Make sure the ACL fits in the destination buffer.
|
||
//
|
||
if( AclInfo.AclBytesInUse > MaxLength ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
// Initialize the destination to be exactly the right size:
|
||
//
|
||
if( !InitializeAcl( Dest,
|
||
AclInfo.AclBytesInUse,
|
||
AclRevisionInfo.AclRevision ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: InitializeAcl failed.\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
if( AclInfo.AceCount == 0 ) {
|
||
|
||
// No ACE's to copy--we're done.
|
||
//
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
// Determine the size of the ACE's in the source ACL:
|
||
//
|
||
AceSize = 0;
|
||
|
||
for( CurrentAceIndex = 0;
|
||
CurrentAceIndex < AclInfo.AceCount;
|
||
CurrentAceIndex++ ) {
|
||
|
||
if( !GetAce( Source, CurrentAceIndex, &CurrentAce ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: GetAce failed.\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
AceSize += ((PACE_HEADER)CurrentAce)->AceSize;
|
||
}
|
||
|
||
// Copy the ACE's:
|
||
//
|
||
if( !GetAce( Source, 0, &CurrentAce ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: GetAce failed.\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
if( !AddAce( Dest,
|
||
AclRevisionInfo.AclRevision,
|
||
0,
|
||
CurrentAce,
|
||
AceSize ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: AddAce failed.\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Definitions for class-data members.
|
||
//
|
||
|
||
BYTE ACL_CONVERT_NODE::_SelfRelativeSDBuffer[ SecurityDescriptorBufferSize ];
|
||
BYTE ACL_CONVERT_NODE::_AbsoluteSDBuffer[ SecurityDescriptorBufferSize ];
|
||
BYTE ACL_CONVERT_NODE::_AclWorkBuffer[ AclBufferSize ];
|
||
BYTE ACL_CONVERT_NODE::_DaclBuffer[ AclBufferSize ];
|
||
BYTE ACL_CONVERT_NODE::_SaclBuffer[ AclBufferSize ];
|
||
BYTE ACL_CONVERT_NODE::_SystemAces[ SystemAceBufferSize ];
|
||
|
||
DEFINE_CONSTRUCTOR( ACL_CONVERT_NODE, OBJECT );
|
||
|
||
ACL_CONVERT_NODE::~ACL_CONVERT_NODE(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method is the destructor for this class.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
Destroy();
|
||
}
|
||
|
||
VOID
|
||
ACL_CONVERT_NODE::Construct(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method is the helper function for object construction.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
_LanmanAclPresent = FALSE;
|
||
_AccessEntryCount = 0;
|
||
_AccessEntries = NULL;
|
||
_AceConversionResults = NULL;
|
||
_AuditInfo = 0;
|
||
_ChildIterator = NULL;
|
||
}
|
||
|
||
VOID
|
||
ACL_CONVERT_NODE::Destroy(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method cleans up the object in preparation for destruction
|
||
or reinitialization.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
_LanmanAclPresent = FALSE;
|
||
_AccessEntryCount = 0;
|
||
|
||
FREE( _AccessEntries );
|
||
FREE( _AceConversionResults );
|
||
|
||
_AuditInfo = 0;
|
||
|
||
if( _ChildIterator != NULL ) {
|
||
|
||
DELETE( _ChildIterator );
|
||
_Children.DeleteAllMembers();
|
||
}
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
ACL_CONVERT_NODE::Initialize(
|
||
IN PCWSTRING ComponentName,
|
||
IN BOOLEAN IsTransient
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method prepares the object for use.
|
||
|
||
Arguments:
|
||
|
||
ComponentName -- Supplies the component name (ie. last component
|
||
of the path) for this node.
|
||
IsTransient -- Supplies a flag indicating whether this
|
||
node is transient. A transient node is
|
||
created while the parent is being converted,
|
||
and may be deleted after it has been converted.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
Notes:
|
||
|
||
This class is reinitializable.
|
||
|
||
--*/
|
||
{
|
||
Destroy();
|
||
|
||
if( !_Children.Initialize() ||
|
||
(_ChildIterator = _Children.QueryIterator()) == NULL ||
|
||
!_ComponentName.Initialize( ComponentName ) ) {
|
||
|
||
Destroy();
|
||
return FALSE;
|
||
}
|
||
|
||
_IsTransient = IsTransient;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
ACL_CONVERT_NODE::AddLanmanAcl(
|
||
IN ULONG AccessEntryCount,
|
||
IN PLM_ACCESS_LIST AccessEntries,
|
||
IN USHORT AuditInfo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method adds a set of Lanman 2.x access entries and audit
|
||
information to the node.
|
||
|
||
Arguments:
|
||
|
||
AccessEntryCount -- Supplies the number of Lanman 2.x ACE's
|
||
to add.
|
||
AccessEntries -- Supplies the Lanman 2.x ACE's.
|
||
AuditInfo -- Supplies the audit information.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
PLM_ACCESS_LIST NewAccessEntries;
|
||
PULONG NewConversionResults;
|
||
ULONG NewAccessEntryCount, i;
|
||
|
||
// If the Lanman ACL has no entries and no audit information,
|
||
// then it can't be set (but the method succeeds).
|
||
//
|
||
if( AccessEntryCount == 0 && AuditInfo == 0 ) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
if( AccessEntryCount != 0 ) {
|
||
|
||
NewAccessEntryCount = _AccessEntryCount + AccessEntryCount;
|
||
|
||
if( (NewAccessEntries =
|
||
(PLM_ACCESS_LIST)MALLOC( sizeof(LM_ACCESS_LIST) *
|
||
NewAccessEntryCount )) ==
|
||
NULL ||
|
||
(NewConversionResults =
|
||
(PULONG)MALLOC(sizeof(ULONG) * NewAccessEntryCount)) == NULL ) {
|
||
|
||
FREE( NewAccessEntries );
|
||
return FALSE;
|
||
}
|
||
|
||
if( _AccessEntryCount != 0 ) {
|
||
|
||
DebugPtrAssert( _AccessEntries );
|
||
DebugPtrAssert( _AceConversionResults );
|
||
|
||
memcpy( NewAccessEntries,
|
||
_AccessEntries,
|
||
sizeof(LM_ACCESS_LIST) * _AccessEntryCount );
|
||
|
||
memcpy( NewConversionResults,
|
||
_AceConversionResults,
|
||
sizeof(ULONG) * _AccessEntryCount );
|
||
}
|
||
|
||
|
||
memcpy( NewAccessEntries + _AccessEntryCount,
|
||
AccessEntries,
|
||
sizeof(LM_ACCESS_LIST) * AccessEntryCount );
|
||
|
||
for( i = _AccessEntryCount; i < NewAccessEntryCount; i++ ) {
|
||
|
||
NewConversionResults[i] = 0;
|
||
}
|
||
|
||
FREE( _AccessEntries );
|
||
FREE( _AceConversionResults );
|
||
|
||
_AccessEntries = NewAccessEntries;
|
||
_AceConversionResults = NewConversionResults;
|
||
_AccessEntryCount += AccessEntryCount;
|
||
}
|
||
|
||
|
||
if( AuditInfo != 0 ) {
|
||
|
||
_AuditInfo |= AuditInfo;
|
||
ConvertAuditBits( _AuditInfo,
|
||
&_DirSuccessfulAuditMask,
|
||
&_DirFailedAuditMask,
|
||
&_FileSuccessfulAuditMask,
|
||
&_FileFailedAuditMask );
|
||
}
|
||
|
||
_LanmanAclPresent = TRUE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
PACL_CONVERT_NODE
|
||
ACL_CONVERT_NODE::GetChild(
|
||
IN PCWSTRING SearchName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method searches the child list to find the
|
||
specified name.
|
||
|
||
Arguments:
|
||
|
||
SearchName -- Supplies the name to find.
|
||
|
||
Return Value:
|
||
|
||
If no child if found with the specified name, this method
|
||
return NULL. Otherwise, it returns the child with that name.
|
||
|
||
Notes:
|
||
|
||
The child list is maintained in sorted order by Component Name.
|
||
--*/
|
||
{
|
||
PACL_CONVERT_NODE CurrentChild;
|
||
LONG CompareResult;
|
||
|
||
// Spin backwards through the list of children until I run out
|
||
// or find one that's less than or equal to the one I want.
|
||
//
|
||
_ChildIterator->Reset();
|
||
|
||
while( (CurrentChild =
|
||
(PACL_CONVERT_NODE)_ChildIterator->GetPrevious()) != NULL &&
|
||
(CompareResult = SearchName->Stricmp( CurrentChild->GetName())) < 0 );
|
||
|
||
if( CurrentChild == NULL || CompareResult != 0 ) {
|
||
|
||
return NULL;
|
||
|
||
} else {
|
||
|
||
return CurrentChild;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
PACL_CONVERT_NODE
|
||
ACL_CONVERT_NODE::AddChild(
|
||
IN PCWSTRING ChildComponentName,
|
||
IN BOOLEAN IsTransient
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method adds a child node.
|
||
|
||
Arguments:
|
||
|
||
ChildComponentName -- supplies the component name of the new child.
|
||
IsTransient -- Supplies a flag indicating whether this
|
||
node is transient. A transient node is
|
||
created while the parent is being converted,
|
||
and may be deleted after it has been converted.
|
||
|
||
|
||
Return Value:
|
||
|
||
A pointer to the newly-created child node, or NULL to indicate
|
||
failure.
|
||
|
||
Notes:
|
||
|
||
The child list is maintained in sorted order by Component Name.
|
||
|
||
--*/
|
||
{
|
||
PACL_CONVERT_NODE NewChild, CurrentChild;
|
||
LONG CompareResult;
|
||
|
||
|
||
if( (NewChild = NEW ACL_CONVERT_NODE) == NULL ||
|
||
!NewChild->Initialize( ChildComponentName, IsTransient ) ) {
|
||
|
||
DELETE( NewChild );
|
||
return FALSE;
|
||
}
|
||
|
||
// Spin backwards through the list of children until I run out or
|
||
// find one that's less than the one I want to add.
|
||
|
||
_ChildIterator->Reset();
|
||
|
||
while( (CurrentChild =
|
||
(PACL_CONVERT_NODE)_ChildIterator->GetPrevious()) != NULL &&
|
||
(CompareResult = ChildComponentName->Stricmp( CurrentChild->GetName())) < 0 );
|
||
|
||
// The iterator now points at the member after which I want to
|
||
// insert the new entry.
|
||
//
|
||
_ChildIterator->GetNext();
|
||
|
||
if( _Children.Insert( NewChild, _ChildIterator ) ) {
|
||
|
||
// Successfully inserted the new child into the list.
|
||
|
||
return NewChild;
|
||
|
||
} else {
|
||
|
||
// Couldn't insert the new child.
|
||
|
||
DELETE( NewChild );
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
BOOLEAN
|
||
ACL_CONVERT_NODE::Convert(
|
||
IN OUT PPATH CurrentPath,
|
||
IN OUT PCINHERITANCE_BUFFER Inheritance,
|
||
IN OUT PACLCONV AclConv
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method converts the Lanman 2.x ACL associated with this
|
||
node into an NT ACL.
|
||
|
||
Arguments:
|
||
|
||
CurrentPath -- Supplies the path of this node's parent. This
|
||
node adds its own component name to the path,
|
||
passes it to its children, and then restores
|
||
it to its original state.
|
||
A value of NULL indicates that this node
|
||
is the root of the tree.
|
||
|
||
Inheritance -- Supplies the inhertied-aces buffer.
|
||
AclConv -- Supplies the program object for error logging.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
CONST ULONG PagefileNameBufferLength = 64;
|
||
WCHAR PagefileNameBuffer[PagefileNameBufferLength];
|
||
|
||
INHERITANCE_BUFFER NewInheritance;
|
||
DSTRING PagefileString;
|
||
|
||
PFSN_DIRECTORY DirFsn;
|
||
PFSN_FILE FileFsn;
|
||
PACL_CONVERT_NODE CurrentChild;
|
||
|
||
PCINHERITANCE_BUFFER InheritanceToUse;
|
||
|
||
BOOLEAN IsRoot, IsDir;
|
||
BOOLEAN NoError = TRUE;
|
||
BOOLEAN AclToConvert = FALSE;
|
||
|
||
// Short-circuit: files or directories named 'pagefile.sys'
|
||
// are ignored.
|
||
//
|
||
if( LoadString( GetModuleHandle(NULL),
|
||
IDS_ACLCONV_PAGEFILE_NAME,
|
||
PagefileNameBuffer,
|
||
PagefileNameBufferLength ) == 0 ) {
|
||
|
||
DebugPrintf( "ACLCONV: LoadString failed--error %d.\n", GetLastError() );
|
||
return FALSE;
|
||
}
|
||
|
||
if( !PagefileString.Initialize( PagefileNameBuffer ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: Can't initialize PagefileNameString.\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
if( PagefileString.Stricmp( &_ComponentName ) == 0 ) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
// This node has an ACL to convert if it has an explicit Lanman
|
||
// ACL or if it has a non-empty inheritance.
|
||
//
|
||
if( _LanmanAclPresent ||
|
||
Inheritance->RecessiveDeniedLength != 0 ||
|
||
Inheritance->DominantDeniedLength != 0 ||
|
||
Inheritance->RecessiveAllowedLength != 0 ||
|
||
Inheritance->DominantAllowedLength != 0 ) {
|
||
|
||
AclToConvert = TRUE;
|
||
}
|
||
|
||
// Add the current component to the path. If this node is the root,
|
||
// create the path.
|
||
//
|
||
if( CurrentPath == NULL ) {
|
||
|
||
IsRoot = TRUE;
|
||
|
||
if( (CurrentPath = NEW PATH) == NULL ||
|
||
!CurrentPath->Initialize( &_ComponentName ) ) {
|
||
|
||
DebugPrint( "ACLCONV: Cannot initialize path object for root." );
|
||
return FALSE;
|
||
}
|
||
|
||
} else {
|
||
|
||
IsRoot = FALSE;
|
||
|
||
if( !CurrentPath->AppendBase( &_ComponentName ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: Cannot append to path" );
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
// Determine whether this node represents a file, a directory, or
|
||
// a non-existent object.
|
||
|
||
if( (DirFsn = SYSTEM::QueryDirectory( CurrentPath )) != NULL ) {
|
||
|
||
IsDir = TRUE;
|
||
|
||
// Fill in the rest of the children of this node, ie.
|
||
// those children that do not have an explicit Lanman
|
||
// ACL. If this node doesn't have an ACL to convert,
|
||
// don't bother expanding the children, since the
|
||
// children will only receive an empty inheritance.
|
||
//
|
||
if( AclToConvert && !ExpandChildren( DirFsn ) ) {
|
||
|
||
if( IsRoot ) {
|
||
|
||
DELETE( CurrentPath );
|
||
|
||
} else {
|
||
|
||
CurrentPath->TruncateBase();
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
DELETE( DirFsn );
|
||
DirFsn = NULL;
|
||
|
||
} else if( (FileFsn = SYSTEM::QueryFile( CurrentPath )) != NULL ) {
|
||
|
||
IsDir = FALSE;
|
||
|
||
DELETE( FileFsn );
|
||
FileFsn = NULL;
|
||
|
||
} else {
|
||
|
||
// This resource does not exist. Note that fact in the
|
||
// log file and carry on. Note that this node is logged
|
||
// only if it has an associated Lanman ACL.
|
||
//
|
||
if( _LanmanAclPresent &&
|
||
!AclConv->LogConversion( CurrentPath,
|
||
ACL_CONVERT_RESOURCE_NOT_FOUND,
|
||
_AuditInfo,
|
||
_AccessEntryCount,
|
||
_AceConversionResults,
|
||
_AccessEntries ) ) {
|
||
|
||
DebugPrint( "ACLCONV--LogConversion failed.\n" );
|
||
NoError = FALSE;
|
||
}
|
||
|
||
// Clean up the path--delete it if this node allocated it,
|
||
// otherwise remove the last component.
|
||
|
||
if( IsRoot ) {
|
||
|
||
DELETE( CurrentPath );
|
||
|
||
} else {
|
||
|
||
CurrentPath->TruncateBase();
|
||
}
|
||
|
||
return NoError;
|
||
}
|
||
|
||
|
||
if( _LanmanAclPresent ) {
|
||
|
||
// This node has a Lanman 2.x ACL of its own, so it
|
||
// ignores the ACE's inherited from its parent and
|
||
// it starts a dynasty of its own.
|
||
//
|
||
NoError = QueryInheritance( AclConv, &NewInheritance, IsDir );
|
||
|
||
InheritanceToUse = &NewInheritance;
|
||
|
||
} else {
|
||
|
||
// This node does not have a Lanman 2.x ACL, so it
|
||
// just uses whatever it inherited from its parent.
|
||
//
|
||
InheritanceToUse = Inheritance;
|
||
}
|
||
|
||
// InheritanceToUse now points at the inheritance buffer
|
||
// with the NT ACE's that I want to attach to this file or
|
||
// directory and its descendants. Recurse into the children
|
||
// first, in case the ACE's I put on this object would prevent
|
||
// me from accessing the children.
|
||
//
|
||
if( NoError ) {
|
||
|
||
// Recurse into the children:
|
||
|
||
_ChildIterator->Reset();
|
||
|
||
while( (CurrentChild =
|
||
(PACL_CONVERT_NODE)_ChildIterator->GetNext()) != NULL &&
|
||
NoError ) {
|
||
|
||
NoError = CurrentChild->Convert( CurrentPath,
|
||
InheritanceToUse,
|
||
AclConv );
|
||
}
|
||
}
|
||
|
||
// Delete the transient children:
|
||
//
|
||
_ChildIterator->Reset();
|
||
CurrentChild = (PACL_CONVERT_NODE)_ChildIterator->GetNext();
|
||
|
||
while( CurrentChild != NULL ) {
|
||
|
||
if( CurrentChild->IsTransient() ) {
|
||
|
||
// This child is transient, and may be deleted.
|
||
//
|
||
_Children.Remove( _ChildIterator );
|
||
DELETE( CurrentChild );
|
||
|
||
CurrentChild = (PACL_CONVERT_NODE)_ChildIterator->GetCurrent();
|
||
|
||
} else {
|
||
|
||
// This child is not transient; keep it and go on
|
||
// to the next.
|
||
//
|
||
CurrentChild = (PACL_CONVERT_NODE)_ChildIterator->GetNext();
|
||
}
|
||
}
|
||
|
||
// If this object has an ACL to convert, attach the ACE's to
|
||
// this object.
|
||
//
|
||
if( AclToConvert && NoError ) {
|
||
|
||
NoError = AddAces( CurrentPath,
|
||
InheritanceToUse,
|
||
IsDir,
|
||
!_LanmanAclPresent );
|
||
|
||
if( !NoError &&
|
||
(GetLastError() == ERROR_FILE_NOT_FOUND ||
|
||
GetLastError() == ERROR_PATH_NOT_FOUND) ) {
|
||
|
||
// We can handle this error--log it.
|
||
//
|
||
if( _LanmanAclPresent &&
|
||
!AclConv->LogConversion( CurrentPath,
|
||
ACL_CONVERT_RESOURCE_NOT_FOUND,
|
||
_AuditInfo,
|
||
_AccessEntryCount,
|
||
_AceConversionResults,
|
||
_AccessEntries ) ) {
|
||
|
||
DebugPrint( "ACLCONV--LogConversion failed.\n" );
|
||
NoError = FALSE;
|
||
|
||
} else {
|
||
|
||
NoError = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
// Log the results of this conversion, if it didn't error out.
|
||
// Note that only those resources which have actual (not just
|
||
// inherited) ACL's are logged.
|
||
|
||
if( _LanmanAclPresent && NoError ) {
|
||
|
||
NoError = AclConv->LogConversion(CurrentPath,
|
||
ACL_CONVERT_SUCCESS,
|
||
_AuditInfo,
|
||
_AccessEntryCount,
|
||
_AceConversionResults,
|
||
_AccessEntries );
|
||
}
|
||
|
||
// Clean up the inheritance buffers:
|
||
//
|
||
if( _LanmanAclPresent ) {
|
||
|
||
FREE( NewInheritance.RecessiveDeniedAces );
|
||
FREE( NewInheritance.DominantDeniedAces );
|
||
FREE( NewInheritance.RecessiveAllowedAces );
|
||
FREE( NewInheritance.DominantAllowedAces );
|
||
}
|
||
|
||
// Clean up the path--delete it if this node allocated it, otherwise
|
||
// remove the last component.
|
||
|
||
if( IsRoot ) {
|
||
|
||
DELETE( CurrentPath );
|
||
|
||
} else {
|
||
|
||
CurrentPath->TruncateBase();
|
||
}
|
||
|
||
|
||
return NoError;
|
||
}
|
||
|
||
// The inheritance scratchpad provides a place to construct
|
||
// and convert ACE's without having to calculate their size
|
||
// in advance.
|
||
//
|
||
BYTE ScratchRecessiveDenied[ AclBufferSize ];
|
||
BYTE ScratchDominantDenied[ AclBufferSize ];
|
||
BYTE ScratchRecessiveAllowed[ AclBufferSize ];
|
||
BYTE ScratchDominantAllowed[ AclBufferSize ];
|
||
|
||
INHERITANCE_BUFFER InheritanceScratchpad =
|
||
{
|
||
ScratchRecessiveDenied,
|
||
AclBufferSize,
|
||
0,
|
||
ScratchDominantDenied,
|
||
AclBufferSize,
|
||
0,
|
||
ScratchRecessiveAllowed,
|
||
AclBufferSize,
|
||
0,
|
||
ScratchDominantAllowed,
|
||
AclBufferSize,
|
||
0
|
||
};
|
||
|
||
|
||
|
||
BOOLEAN
|
||
ACL_CONVERT_NODE::QueryInheritance(
|
||
IN PACLCONV AclConv,
|
||
IN OUT PINHERITANCE_BUFFER Inheritance,
|
||
IN BOOLEAN IsDir
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method converts the Lanman 2.x ACL associated with
|
||
this node into a set of NT ACE's.
|
||
|
||
Arguments:
|
||
|
||
AclConv -- Supplies the program object.
|
||
Inheritance -- Receives the converted ACE information.
|
||
IsDir -- Supplies a flag indicating whether the
|
||
node is a directory.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
PVOID RecessiveDenied, DominantDenied, RecessiveAllowed, DominantAllowed;
|
||
ULONG i;
|
||
|
||
// Set up the inheritance in a known state in case of error return.
|
||
//
|
||
memset( Inheritance, 0, sizeof( INHERITANCE_BUFFER ) );
|
||
|
||
|
||
if( !_LanmanAclPresent ) {
|
||
|
||
// This node does not have a Lanman 2.x ACL, so it
|
||
// cannot generate an inheritance.
|
||
//
|
||
return FALSE;
|
||
}
|
||
|
||
// Clear the scratch pad.
|
||
//
|
||
InheritanceScratchpad.RecessiveDeniedLength = 0;
|
||
InheritanceScratchpad.DominantDeniedLength = 0;
|
||
InheritanceScratchpad.RecessiveAllowedLength = 0;
|
||
InheritanceScratchpad.DominantAllowedLength = 0;
|
||
|
||
// Convert the Lanman 2.x ACL into the scratchpad. Since
|
||
// Admins have implicit access to all files in Lanman,
|
||
// toss in the implicit ACE for good measure.
|
||
//
|
||
if( !AdminsImplicitAceInitialized && !CreateImplicitAce() ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
ConvertOneAce( AclConv,
|
||
&InheritanceScratchpad,
|
||
&AdminsImplicitAce,
|
||
IsDir );
|
||
|
||
for( i = 0; i < _AccessEntryCount; i++ ) {
|
||
|
||
_AceConversionResults[i] = ConvertOneAce( AclConv,
|
||
&InheritanceScratchpad,
|
||
_AccessEntries + i,
|
||
IsDir );
|
||
|
||
if( _AceConversionResults[i] == ACE_CONVERT_ERROR ) {
|
||
|
||
DebugPrint( "ACLCONV: ConvertOneAce failed.\n" );
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
// Now allocate buffers for the inheritance buffer
|
||
// under construction.
|
||
//
|
||
RecessiveDenied = MALLOC( InheritanceScratchpad.RecessiveDeniedLength );
|
||
DominantDenied = MALLOC( InheritanceScratchpad.DominantDeniedLength );
|
||
RecessiveAllowed = MALLOC( InheritanceScratchpad.RecessiveAllowedLength );
|
||
DominantAllowed = MALLOC( InheritanceScratchpad.DominantAllowedLength );
|
||
|
||
if( RecessiveDenied == NULL || DominantDenied == NULL ||
|
||
RecessiveAllowed == NULL || DominantAllowed == NULL ) {
|
||
|
||
FREE( RecessiveDenied );
|
||
FREE( DominantDenied );
|
||
FREE( RecessiveAllowed );
|
||
FREE( DominantAllowed );
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
// Copy information into the new buffers and set up the
|
||
// inheritance buffer for return.
|
||
//
|
||
memmove( RecessiveDenied,
|
||
InheritanceScratchpad.RecessiveDeniedAces,
|
||
InheritanceScratchpad.RecessiveDeniedLength );
|
||
|
||
memmove( DominantDenied,
|
||
InheritanceScratchpad.DominantDeniedAces,
|
||
InheritanceScratchpad.DominantDeniedLength );
|
||
|
||
memmove( RecessiveAllowed,
|
||
InheritanceScratchpad.RecessiveAllowedAces,
|
||
InheritanceScratchpad.RecessiveAllowedLength );
|
||
|
||
memmove( DominantAllowed,
|
||
InheritanceScratchpad.DominantAllowedAces,
|
||
InheritanceScratchpad.DominantAllowedLength );
|
||
|
||
Inheritance->RecessiveDeniedAces = RecessiveDenied;
|
||
Inheritance->RecessiveDeniedMaxLength = InheritanceScratchpad.RecessiveDeniedLength;
|
||
Inheritance->RecessiveDeniedLength = InheritanceScratchpad.RecessiveDeniedLength;
|
||
|
||
Inheritance->DominantDeniedAces = DominantDenied;
|
||
Inheritance->DominantDeniedMaxLength = InheritanceScratchpad.DominantDeniedLength;
|
||
Inheritance->DominantDeniedLength = InheritanceScratchpad.DominantDeniedLength;
|
||
|
||
Inheritance->RecessiveAllowedAces = RecessiveAllowed;
|
||
Inheritance->RecessiveAllowedMaxLength = InheritanceScratchpad.RecessiveAllowedLength;
|
||
Inheritance->RecessiveAllowedLength = InheritanceScratchpad.RecessiveAllowedLength;
|
||
|
||
Inheritance->DominantAllowedAces = DominantAllowed;
|
||
Inheritance->DominantAllowedMaxLength = InheritanceScratchpad.DominantAllowedLength;
|
||
Inheritance->DominantAllowedLength = InheritanceScratchpad.DominantAllowedLength;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
ACE_CONVERT_CODE
|
||
ACL_CONVERT_NODE::ConvertOneAce(
|
||
IN PACLCONV AclConv,
|
||
IN OUT PINHERITANCE_BUFFER Inheritance,
|
||
IN PLM_ACCESS_LIST AccessEntry,
|
||
IN BOOLEAN IsDir
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method converts a single Lanman ACE.
|
||
|
||
Arguments:
|
||
|
||
AclConv -- Supplies the program object.
|
||
Inheritance -- Supplies the buffers for the converted ACE's.
|
||
This method will add ACE's to this buffer.
|
||
AccessEntry -- Supplies the Lanman 2.x access entry.
|
||
IsDir -- Supplies a flag indicating (if TRUE) that
|
||
this node represents a directory.
|
||
|
||
Return Value:
|
||
|
||
An ACE_CONVERT_CODE indicating the result of this conversion.
|
||
|
||
--*/
|
||
{
|
||
CONST SidBufferLength = 128;
|
||
STATIC BYTE SidBuffer[SidBufferLength];
|
||
|
||
WCHAR NameBuffer[ UNLEN + 1 ];
|
||
DSTRING NameString;
|
||
|
||
PSID Sid;
|
||
ULONG SidLength, OldListLength,
|
||
NewAceLength, TargetBufferLength;
|
||
BOOLEAN NoAccess;
|
||
ACCESS_MASK DirAccessMask, FileAccessMask;
|
||
ACE_CONVERT_CODE Result;
|
||
UCHAR InheritBits;
|
||
PVOID TargetBuffer;
|
||
BOOLEAN AddFailed = FALSE;
|
||
|
||
|
||
// Set up the name in a UNICODE buffer. If it is a
|
||
// special name, remap it.
|
||
//
|
||
memset( NameBuffer, 0, sizeof( NameBuffer ) );
|
||
|
||
if( !MultiByteToWideChar( AclConv->QuerySourceCodepage(),
|
||
0,
|
||
AccessEntry->acl_ugname,
|
||
strlen( AccessEntry->acl_ugname ),
|
||
NameBuffer,
|
||
UNLEN + 1 ) ||
|
||
!NameString.Initialize( NameBuffer ) ) {
|
||
|
||
return ACE_CONVERT_ERROR;
|
||
}
|
||
|
||
// Set NoAccess to TRUE if this is an ACE that grants no
|
||
// access rights. For individuals, such ACE's become
|
||
// Accessed-Denied ACE's, for groups they're no-ops.
|
||
|
||
NoAccess = ((AccessEntry->acl_access & ~LM_ACCESS_GROUP) == 0);
|
||
|
||
if( NoAccess && (AccessEntry->acl_access & LM_ACCESS_GROUP) ) {
|
||
|
||
// This is an ACE which grants no permissions to a group--
|
||
// drop it.
|
||
|
||
return ACE_CONVERT_DROPPED;
|
||
}
|
||
|
||
|
||
// Get the SID for this user.
|
||
//
|
||
Sid = (PSID)SidBuffer;
|
||
SidLength = SidBufferLength;
|
||
|
||
if( !AclconvLookupAccountName( AclConv,
|
||
&NameString,
|
||
Sid,
|
||
&SidLength ) ) {
|
||
|
||
if( GetLastError() == ERROR_NONE_MAPPED ) {
|
||
|
||
return ACE_CONVERT_SID_NOT_FOUND;
|
||
|
||
} else {
|
||
|
||
return ACE_CONVERT_ERROR;
|
||
}
|
||
}
|
||
|
||
// Create NT Access Masks that correspond to the Lanman access
|
||
// mask in this ACE. Note that there are two versions, one for
|
||
// directories and one for files.
|
||
|
||
ConvertAccessMasks( AccessEntry->acl_access,
|
||
&DirAccessMask,
|
||
&FileAccessMask );
|
||
|
||
|
||
// Assume the worst:
|
||
|
||
Result = ACE_CONVERT_ERROR;
|
||
|
||
if( IsDir &&
|
||
DirAccessMask != FileAccessMask ) {
|
||
|
||
// This object is a directory, and this ACE will be inherited
|
||
// differently by files and subdirectories. Therefore, I need
|
||
// to create two ACE's--one with the OBJECT_INHERIT_ACE and
|
||
// INHERIT_ONLY_ACE bits set (to pass on to files), the other
|
||
// with the CONTAINER_INHERIT_ACE bit set (to pass on to
|
||
// directories). The former goes on the recessive ACE list,
|
||
// the latter on the dominant ACE list.
|
||
|
||
if( NoAccess ) {
|
||
|
||
// This is an access-denied ACE. Save the current length
|
||
// of the list of recessive access-denied ACE's in case
|
||
// I have to restore it.
|
||
|
||
OldListLength = Inheritance->RecessiveDeniedLength;
|
||
|
||
TargetBuffer = (PBYTE)Inheritance->RecessiveDeniedAces +
|
||
Inheritance->RecessiveDeniedLength;
|
||
|
||
TargetBufferLength = Inheritance->RecessiveDeniedMaxLength -
|
||
Inheritance->RecessiveDeniedLength;
|
||
|
||
if( CreateAccessDeniedAce( TargetBuffer,
|
||
TargetBufferLength,
|
||
FileAccessMask,
|
||
OBJECT_INHERIT_ACE | INHERIT_ONLY_ACE,
|
||
Sid,
|
||
&NewAceLength ) ) {
|
||
|
||
// Successfully added the recessive ACE; update the
|
||
// length of the recessive list and then add the
|
||
// dominant ACE.
|
||
|
||
Inheritance->RecessiveDeniedLength += NewAceLength;
|
||
|
||
TargetBuffer = (PBYTE)Inheritance->DominantDeniedAces +
|
||
Inheritance->DominantDeniedLength;
|
||
|
||
TargetBufferLength =
|
||
Inheritance->DominantDeniedMaxLength -
|
||
Inheritance->DominantDeniedLength;
|
||
|
||
if( CreateAccessDeniedAce( TargetBuffer,
|
||
TargetBufferLength,
|
||
DirAccessMask,
|
||
CONTAINER_INHERIT_ACE,
|
||
Sid,
|
||
&NewAceLength ) ) {
|
||
|
||
// Update the length of the dominant list to
|
||
// include the new ACE.
|
||
|
||
Inheritance->DominantDeniedLength += NewAceLength;
|
||
|
||
Result = ACE_CONVERT_SUCCESS;
|
||
|
||
} else {
|
||
|
||
// Couldn't add the dominant ACE--back out the
|
||
// recessive ACE by restoring the original length
|
||
// of the recessive list.
|
||
|
||
Inheritance->RecessiveDeniedLength = OldListLength;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
// This is an access-allowed ACE. Save the current length
|
||
// of the list of recessive access-allowed ACE's in case
|
||
// I have to restore it.
|
||
//
|
||
OldListLength = Inheritance->RecessiveAllowedLength;
|
||
|
||
// If the file mask is non-zero, create the corresponding
|
||
// recessive access-allowed ACE.
|
||
//
|
||
if( FileAccessMask != 0 ) {
|
||
|
||
TargetBuffer = (PBYTE)Inheritance->RecessiveAllowedAces +
|
||
Inheritance->RecessiveAllowedLength;
|
||
|
||
TargetBufferLength = Inheritance->RecessiveAllowedMaxLength -
|
||
Inheritance->RecessiveAllowedLength;
|
||
|
||
if( CreateAccessAllowedAce( TargetBuffer,
|
||
TargetBufferLength,
|
||
FileAccessMask,
|
||
OBJECT_INHERIT_ACE | INHERIT_ONLY_ACE,
|
||
Sid,
|
||
&NewAceLength ) ) {
|
||
|
||
// Successfully added the recessive ACE; update the
|
||
// length of the recessive list
|
||
//
|
||
Inheritance->RecessiveAllowedLength += NewAceLength;
|
||
|
||
} else {
|
||
|
||
AddFailed = TRUE;
|
||
}
|
||
}
|
||
|
||
if( !AddFailed && DirAccessMask != 0 ) {
|
||
|
||
TargetBuffer = (PBYTE)Inheritance->DominantAllowedAces +
|
||
Inheritance->DominantAllowedLength;
|
||
|
||
TargetBufferLength =
|
||
Inheritance->DominantAllowedMaxLength -
|
||
Inheritance->DominantAllowedLength;
|
||
|
||
if( CreateAccessAllowedAce( TargetBuffer,
|
||
TargetBufferLength,
|
||
DirAccessMask,
|
||
CONTAINER_INHERIT_ACE,
|
||
Sid,
|
||
&NewAceLength ) ) {
|
||
|
||
// Update the length of the dominant list to
|
||
// include the new ACE.
|
||
|
||
Inheritance->DominantAllowedLength += NewAceLength;
|
||
|
||
} else {
|
||
|
||
// Couldn't add the dominant ACE--back out the
|
||
// recessive ACE by restoring the original length
|
||
// of the recessive list.
|
||
|
||
Inheritance->RecessiveAllowedLength = OldListLength;
|
||
AddFailed = TRUE;
|
||
}
|
||
}
|
||
|
||
if( !AddFailed ) {
|
||
|
||
Result = ACE_CONVERT_SUCCESS;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
// Either this is object is a file, in which case this ACE will
|
||
// never be inherited by anything, or inheritance of this ACE
|
||
// is unaffected by whether the child is a file or directory.
|
||
// Therefore, I only need to create one ACE. If this object is
|
||
// a directory, create an ACE with the OBJECT_INHERIT_ACE and
|
||
// CONTAINER_INHERIT_ACE bits set; if it's a file, create it
|
||
// with no inherit bits set. The new ACE goes on the appropriate
|
||
// dominant list (since it applies to the current object as
|
||
// well as its descendants).
|
||
//
|
||
// If this ACE is an ACCESS_ALLOWED ACE that grants no access,
|
||
// omit it.
|
||
|
||
InheritBits = ( IsDir ?
|
||
(OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE) :
|
||
0 );
|
||
|
||
if( NoAccess ) {
|
||
|
||
// Add an Access Denied ACE to the inherited list of
|
||
// dominant access denied ACE's.
|
||
|
||
TargetBuffer = (PBYTE)Inheritance->DominantDeniedAces +
|
||
Inheritance->DominantDeniedLength;
|
||
|
||
TargetBufferLength = Inheritance->DominantDeniedMaxLength -
|
||
Inheritance->DominantDeniedLength;
|
||
|
||
if( CreateAccessDeniedAce( TargetBuffer,
|
||
TargetBufferLength,
|
||
FileAccessMask,
|
||
InheritBits,
|
||
Sid,
|
||
&NewAceLength ) ) {
|
||
|
||
// Update the state of the list to show that a new
|
||
// ACE was successful added.
|
||
|
||
Inheritance->DominantDeniedLength += NewAceLength;
|
||
Result = ACE_CONVERT_SUCCESS;
|
||
}
|
||
|
||
} else if( FileAccessMask == 0 ) {
|
||
|
||
// This is an ACCESS_ALLOWED ACE that grants no access--
|
||
// it can be safely ignored.
|
||
//
|
||
Result = ACE_CONVERT_SUCCESS;
|
||
|
||
} else {
|
||
|
||
// Add an Access Allowed ACE to the inherited list of
|
||
// dominant access allowed ACE's.
|
||
|
||
TargetBuffer = (PBYTE)Inheritance->DominantAllowedAces +
|
||
Inheritance->DominantAllowedLength;
|
||
|
||
TargetBufferLength = Inheritance->DominantAllowedMaxLength -
|
||
Inheritance->DominantAllowedLength;
|
||
|
||
if( CreateAccessAllowedAce( TargetBuffer,
|
||
TargetBufferLength,
|
||
FileAccessMask,
|
||
InheritBits,
|
||
Sid,
|
||
&NewAceLength ) ) {
|
||
|
||
// Update the state of the list to show that a new ACE
|
||
// was successfully added.
|
||
|
||
Inheritance->DominantAllowedLength += NewAceLength;
|
||
Result = ACE_CONVERT_SUCCESS;
|
||
}
|
||
}
|
||
}
|
||
|
||
return Result;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
ACL_CONVERT_NODE::AddAces(
|
||
IN PCPATH ResourceName,
|
||
IN PCINHERITANCE_BUFFER Inheritance,
|
||
IN BOOLEAN IsDir,
|
||
IN BOOLEAN ByDefault
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method adds the ACE's in the inheritance buffer to the
|
||
resource represented by this node.
|
||
|
||
Arguments:
|
||
|
||
ResourceName -- Supplies the path of the resource represented
|
||
by this node.
|
||
Inheritance -- Supplies the buffer with ACE's to add.
|
||
IsDir -- Supplies a flag which indicates, if TRUE,
|
||
that this resource is a directory.
|
||
ByDefault -- Supplies a flag which indicates, if TRUE,
|
||
that the ACE's being added are all inherited.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
SECURITY_DESCRIPTOR AbsoluteSD;
|
||
|
||
PCWSTRING PathString;
|
||
PSECURITY_DESCRIPTOR SelfRelativeSD;
|
||
PACL NewDacl, NewSacl, BoiledDacl, BoiledSacl;
|
||
|
||
SECURITY_INFORMATION RequestedInformation;
|
||
ACCESS_MASK SuccessfulAuditMask, FailedAuditMask;
|
||
ULONG SelfRelativeSDLength,
|
||
SystemAcesLength,
|
||
NewAceLength;
|
||
BOOLEAN EditSacl, EditDacl;
|
||
|
||
// Check to see if there's actually anything to do:
|
||
//
|
||
EditSacl = (_AuditInfo != 0);
|
||
|
||
EditDacl = ( Inheritance->DominantDeniedLength != 0 ||
|
||
Inheritance->DominantAllowedLength != 0 ||
|
||
(IsDir && Inheritance->RecessiveDeniedLength != 0) ||
|
||
(IsDir && Inheritance->RecessiveAllowedLength != 0) );
|
||
|
||
if( !EditSacl && !EditDacl ) {
|
||
|
||
// There's nothing to be done to this resource--short circuit.
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
// If this is the Windows NT Directory, silently return.
|
||
//
|
||
if( IsWindowsDir( ResourceName ) ) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
// Get the name of the resource as a WSTR so I can feed
|
||
// it to the Win32 API.
|
||
|
||
if( (PathString = ResourceName->GetPathString()) == NULL ||
|
||
PathString->QueryChCount() > MAX_RESOURCE_NAME_LENGTH ) {
|
||
|
||
DebugPrintf( "ACLCONV: Unable to query path string.\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
RequestedInformation = 0;
|
||
|
||
if( EditSacl ) {
|
||
|
||
RequestedInformation |= SACL_SECURITY_INFORMATION;
|
||
}
|
||
|
||
if ( EditDacl ) {
|
||
|
||
|
||
RequestedInformation |= DACL_SECURITY_INFORMATION;
|
||
}
|
||
|
||
|
||
// Get the existing security information for this resource,
|
||
// in self-relative format.
|
||
|
||
// NOTE -- because of the behaviour or GetFileSecurity,
|
||
// I always request the owner so that I won't get abused in
|
||
// the case where the file has no ACL. If this is a bug
|
||
// in the security API, then I can take this owner thing
|
||
// out when that bug gets fixed.
|
||
//
|
||
SelfRelativeSD = (PSECURITY_DESCRIPTOR)_SelfRelativeSDBuffer;
|
||
|
||
if( !GetFileSecurity( (PWSTR)PathString->GetWSTR(),
|
||
RequestedInformation | OWNER_SECURITY_INFORMATION,
|
||
SelfRelativeSD,
|
||
SecurityDescriptorBufferSize,
|
||
&SelfRelativeSDLength ) ||
|
||
SelfRelativeSDLength > SecurityDescriptorBufferSize ) {
|
||
|
||
DebugPrintf( "ACLCONV: GetFileSecurity failed (error %d).\n", GetLastError() );
|
||
return FALSE;
|
||
}
|
||
|
||
// Create a new security descriptor in Absolute format,
|
||
// to which a new SACL and/or DACL will be attached as
|
||
// needed.
|
||
//
|
||
if( !InitializeSecurityDescriptor( &AbsoluteSD,
|
||
SECURITY_DESCRIPTOR_REVISION ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: InitializeSecurityDescriptor failed.\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
if( EditSacl ) {
|
||
|
||
// Create a new SACL for this file or directory.
|
||
//
|
||
NewSacl = (PACL)_AclWorkBuffer;
|
||
|
||
if( !InitializeAcl( NewSacl, AclBufferSize, ACL_REVISION ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
// Create the appropriate System Audit ACEs for this beast.
|
||
// First, determine which pair of audit masks to use.
|
||
|
||
if( IsDir ) {
|
||
|
||
SuccessfulAuditMask = _DirSuccessfulAuditMask;
|
||
FailedAuditMask = _DirFailedAuditMask;
|
||
|
||
} else {
|
||
|
||
SuccessfulAuditMask = _FileSuccessfulAuditMask;
|
||
FailedAuditMask = _FileFailedAuditMask;
|
||
}
|
||
|
||
// Now create the system audit ACE's in the _SystemAces buffer.
|
||
// (Static to this class).
|
||
//
|
||
SystemAcesLength = 0;
|
||
NewAceLength = 0;
|
||
|
||
if( SuccessfulAuditMask != 0 &&
|
||
!CreateSystemAuditAce( _SystemAces,
|
||
SystemAceBufferSize,
|
||
SuccessfulAuditMask,
|
||
0,
|
||
FALSE,
|
||
&NewAceLength ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
SystemAcesLength += NewAceLength;
|
||
NewAceLength = 0;
|
||
|
||
if( FailedAuditMask != 0 &&
|
||
!CreateSystemAuditAce( (PBYTE)_SystemAces + SystemAcesLength,
|
||
SystemAceBufferSize - SystemAcesLength,
|
||
FailedAuditMask,
|
||
0,
|
||
TRUE,
|
||
&NewAceLength ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
SystemAcesLength += NewAceLength;
|
||
|
||
// Add the newly-created system audit aces to the SACL.
|
||
|
||
if( SystemAcesLength != 0 &&
|
||
!AddAce( NewSacl,
|
||
ACL_REVISION,
|
||
MAXULONG,
|
||
_SystemAces,
|
||
SystemAcesLength ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
// Boil the SACL down to its minimum size and attach
|
||
// it to the security descriptor.
|
||
//
|
||
BoiledSacl = (PACL)_SaclBuffer;
|
||
|
||
if( !BoilAcl( NewSacl, BoiledSacl, AclBufferSize ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
if( !SetSecurityDescriptorSacl( &AbsoluteSD,
|
||
TRUE,
|
||
BoiledSacl,
|
||
ByDefault ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: SetSecurityDescriptorSacl failed (error %d).\n", GetLastError() );
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if( EditDacl ) {
|
||
|
||
// Create a new DACL for the file or directory.
|
||
//
|
||
NewDacl = (PACL)_AclWorkBuffer;
|
||
|
||
if( !InitializeAcl( NewDacl, AclBufferSize, ACL_REVISION ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: InitializeAcl failed.\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
// Copy in the new Access Denied ACE's...
|
||
//
|
||
if( ( IsDir &&
|
||
Inheritance->RecessiveDeniedLength != 0 &&
|
||
!AddAce( NewDacl,
|
||
ACL_REVISION,
|
||
MAXULONG,
|
||
Inheritance->RecessiveDeniedAces,
|
||
Inheritance->RecessiveDeniedLength ) ) ||
|
||
( Inheritance->DominantDeniedLength != 0 &&
|
||
!AddAce( NewDacl,
|
||
ACL_REVISION,
|
||
MAXULONG,
|
||
Inheritance->DominantDeniedAces,
|
||
Inheritance->DominantDeniedLength ) ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: Cannot add access-denied ACE's\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
// ... and the new Access Allowed ACE's:
|
||
//
|
||
if( ( IsDir &&
|
||
Inheritance->RecessiveAllowedLength != 0 &&
|
||
!AddAce( NewDacl,
|
||
ACL_REVISION,
|
||
MAXULONG,
|
||
Inheritance->RecessiveAllowedAces,
|
||
Inheritance->RecessiveAllowedLength ) ) ||
|
||
( Inheritance->DominantAllowedLength != 0 &&
|
||
!AddAce( NewDacl,
|
||
ACL_REVISION,
|
||
MAXULONG,
|
||
Inheritance->DominantAllowedAces,
|
||
Inheritance->DominantAllowedLength ) ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: Cannot add access-allowed ACE's\n" );
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
// Boil the DACL down to its minimum size:
|
||
//
|
||
BoiledDacl = (PACL)_DaclBuffer;
|
||
|
||
if( !BoilAcl( NewDacl, BoiledDacl, AclBufferSize ) ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
// Attach the new DACL to the Absolute Security Descriptor.
|
||
//
|
||
if( !SetSecurityDescriptorDacl( &AbsoluteSD,
|
||
TRUE,
|
||
BoiledDacl,
|
||
ByDefault ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: SetSecurityDescriptorDacl failed (error %d).\n", GetLastError() );
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
// Save the security descriptor on the file.
|
||
//
|
||
if( !SetFileSecurity( (PWSTR)PathString->GetWSTR(),
|
||
RequestedInformation,
|
||
&AbsoluteSD ) ) {
|
||
|
||
DebugPrintf( "ACLCONV: SetFileSecurity failed (error %d).\n", GetLastError() );
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
ACL_CONVERT_NODE::ExpandChildren(
|
||
PFSN_DIRECTORY DirFsn
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method expands the child list to include all children
|
||
of this directory. Newly-created child nodes are marked
|
||
as transient.
|
||
|
||
Arguments:
|
||
|
||
DirFsn -- Provides the Directory FSNode object for this
|
||
node.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
PARRAY FsNodeArray = NULL;
|
||
PITERATOR Iterator = NULL;
|
||
PWSTRING ChildName = NULL;
|
||
PFSNODE ChildFsNode;
|
||
BOOLEAN NoError = TRUE;
|
||
|
||
if( (FsNodeArray = DirFsn->QueryFsnodeArray()) == NULL ) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
if( (Iterator = FsNodeArray->QueryIterator()) == NULL ) {
|
||
|
||
DELETE( FsNodeArray );
|
||
return FALSE;
|
||
}
|
||
|
||
Iterator->Reset();
|
||
|
||
while( NoError && (ChildFsNode = (PFSNODE)Iterator->GetNext()) != NULL ) {
|
||
|
||
ChildName = ChildFsNode->QueryName();
|
||
|
||
if( ChildName == NULL ) {
|
||
|
||
NoError = FALSE;
|
||
break;
|
||
}
|
||
|
||
// If there is not already a child in the list with
|
||
// this name, create one and mark it as transient.
|
||
//
|
||
if( GetChild( ChildName ) == NULL &&
|
||
AddChild( ChildName, TRUE ) == NULL ) {
|
||
|
||
NoError = FALSE;
|
||
break;
|
||
}
|
||
|
||
DELETE( ChildName );
|
||
}
|
||
|
||
// Clean up.
|
||
//
|
||
DELETE( Iterator );
|
||
|
||
FsNodeArray->DeleteAllMembers();
|
||
DELETE( FsNodeArray );
|
||
DELETE( ChildName );
|
||
|
||
return NoError;
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
ACL_CONVERT_NODE::Dump(
|
||
IN PPATH ParentPath
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method displays the contents of the subtree rooted at
|
||
this node.
|
||
|
||
Arguments:
|
||
|
||
ParentPath -- Supplies the path of the parent directory. A
|
||
value of NULL indicates that this is the root
|
||
of the tree.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PFSN_DIRECTORY DirFsn;
|
||
PFSN_FILE FileFsn;
|
||
PCWSTRING PathString;
|
||
PACL_CONVERT_NODE CurrentChild;
|
||
ULONG i;
|
||
PWSTR PathBuffer, pw;
|
||
CHAR ch;
|
||
BOOLEAN IsRoot, IsDir, Exists;
|
||
|
||
|
||
if( ParentPath == NULL ) {
|
||
|
||
IsRoot = TRUE;
|
||
|
||
if( (ParentPath = NEW PATH) == NULL ||
|
||
!ParentPath->Initialize( &_ComponentName ) ) {
|
||
|
||
printf( "*****DUMP ERROR*****\n" );
|
||
return;
|
||
}
|
||
|
||
} else {
|
||
|
||
IsRoot = FALSE;
|
||
|
||
if( !ParentPath->AppendBase( &_ComponentName ) ) {
|
||
|
||
printf( "*****DUMP ERROR*****\n" );
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Determine whether this node represents a file, a directory, or
|
||
// a non-existent object.
|
||
|
||
if( (DirFsn = SYSTEM::QueryDirectory( ParentPath )) != NULL ) {
|
||
|
||
Exists = TRUE;
|
||
IsDir = TRUE;
|
||
DELETE( DirFsn );
|
||
DirFsn = NULL;
|
||
|
||
} else if( (FileFsn = SYSTEM::QueryFile( ParentPath )) != NULL ) {
|
||
|
||
Exists = TRUE;
|
||
IsDir = FALSE;
|
||
|
||
DELETE( FileFsn );
|
||
FileFsn = NULL;
|
||
|
||
} else {
|
||
|
||
Exists = FALSE;
|
||
}
|
||
|
||
|
||
if( (PathString = ParentPath->GetPathString()) == NULL ||
|
||
(PathBuffer = PathString->QueryWSTR( )) == NULL ) {
|
||
|
||
printf( "*****ERROR*****\n" );
|
||
return;
|
||
}
|
||
|
||
for( pw = PathBuffer; *pw != 0; pw++ ) {
|
||
|
||
printf( "%c", *pw );
|
||
}
|
||
|
||
if( !Exists ) {
|
||
|
||
printf( " DOES NOT EXIST." );
|
||
|
||
} else if ( IsDir ) {
|
||
|
||
printf( " (directory)" );
|
||
|
||
} else {
|
||
|
||
printf( " (file)" );
|
||
}
|
||
|
||
printf( "\n" );
|
||
|
||
if( _LanmanAclPresent ) {
|
||
|
||
printf( " Audit Info = 0x%x\n", _AuditInfo );
|
||
|
||
for( i = 0; i < _AccessEntryCount; i++ ) {
|
||
|
||
ch = ( _AccessEntries[i].acl_access & LM_ACCESS_GROUP ) ? '*' : ' ';
|
||
|
||
printf( " %c%-20s\n", ch, _AccessEntries[i].acl_ugname );
|
||
}
|
||
}
|
||
|
||
|
||
// Spin through the kids.
|
||
|
||
_ChildIterator->Reset();
|
||
|
||
while( (CurrentChild = (PACL_CONVERT_NODE)_ChildIterator->GetNext()) != NULL ) {
|
||
|
||
CurrentChild->Dump( ParentPath );
|
||
}
|
||
|
||
// Clean up this addition to the path.
|
||
|
||
if( IsRoot ) {
|
||
|
||
DELETE( ParentPath );
|
||
|
||
} else {
|
||
|
||
ParentPath->TruncateBase();
|
||
}
|
||
}
|