You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1340 lines
31 KiB
1340 lines
31 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 2001
|
|
//
|
|
// File: razacl.c
|
|
//
|
|
// Contents:
|
|
//
|
|
// History: 4/16/2001 richardw Created
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
|
|
#include <windows.h>
|
|
#include <sddl.h>
|
|
#include <lm.h>
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
UNICODE_STRING SddlFile ;
|
|
UNICODE_STRING SourceDir ;
|
|
UNICODE_STRING RootDir ;
|
|
UNICODE_STRING ExtraDirs[] ;
|
|
|
|
BOOL Background ;
|
|
BOOL DoShares ;
|
|
BOOL CreateSddl ;
|
|
BOOL Force ;
|
|
DWORD DebugFlag ;
|
|
BOOL AddSelf ;
|
|
BOOL DoSacl ;
|
|
|
|
SECURITY_INFORMATION SecurityInfo ;
|
|
|
|
typedef DWORD
|
|
(NTAPI FS_WALK_CALLBACK)(
|
|
PVOID Parameter,
|
|
PWSTR File
|
|
);
|
|
|
|
typedef struct _FS_ENGINE_STATS {
|
|
|
|
ULONG Files ;
|
|
ULONG Directories ;
|
|
ULONG EngineErrors ;
|
|
ULONG CallbackErrors ;
|
|
} FS_ENGINE_STATS, * PFS_ENGINE_STATS ;
|
|
|
|
typedef FS_WALK_CALLBACK * PFS_WALK_CALLBACK ;
|
|
|
|
typedef struct _FS_WALK_CONTROL {
|
|
|
|
PVOID Parameter ;
|
|
PFS_WALK_CALLBACK Callback ;
|
|
ULONG Options ;
|
|
|
|
PFS_ENGINE_STATS Stats ;
|
|
HANDLE Search ;
|
|
WCHAR CurrentPath[ MAX_PATH ];
|
|
WCHAR SearchPath[ MAX_PATH ];
|
|
WCHAR FilePath[ MAX_PATH ];
|
|
} FS_WALK_CONTROL, * PFS_WALK_CONTROL ;
|
|
|
|
|
|
|
|
typedef struct _PARAM {
|
|
LPSTR Argument ;
|
|
ULONG Flags ;
|
|
PVOID Value ;
|
|
PVOID Default ;
|
|
} PARAM, * PPARAM ;
|
|
|
|
|
|
#define PARAM_TYPE_STRING 0x00000001
|
|
#define PARAM_TYPE_ULONG 0x00000002
|
|
#define PARAM_TYPE_BOOL 0x00000003
|
|
#define PARAM_TYPE_MASK 0x0000FFFF
|
|
|
|
#define PARAM_TYPE_SINGLE 0x00010000 // Single value
|
|
#define PARAM_TYPE_COMMA 0x00020000 // Comma separated multi value
|
|
#define PARAM_TYPE_OPTIONAL 0x00040000 // Optional value
|
|
#define PARAM_TYPE_REQUIRED 0x00080000 // Required argument
|
|
#define PARAM_TYPE_HIDDEN 0x00100000 // Not dumped during help
|
|
#define PARAM_TYPE_MULTIPLE 0x00200000 // Can be specified multiple times
|
|
|
|
#define PARAM_TYPE_FOUND 0x10000000 // Found during arg scan
|
|
|
|
|
|
PARAM Parameters[] = {
|
|
{ "background", PARAM_TYPE_BOOL, &Background, (PVOID) FALSE },
|
|
{ "sddlfile", PARAM_TYPE_STRING | PARAM_TYPE_REQUIRED, &SddlFile, (PVOID) NULL },
|
|
{ "shares", PARAM_TYPE_BOOL | PARAM_TYPE_SINGLE | PARAM_TYPE_OPTIONAL, &DoShares, (PVOID) NULL },
|
|
{ "source", PARAM_TYPE_STRING | PARAM_TYPE_SINGLE, &SourceDir, (PVOID) NULL },
|
|
{ "createsddl", PARAM_TYPE_BOOL | PARAM_TYPE_SINGLE | PARAM_TYPE_OPTIONAL, &CreateSddl, (PVOID) NULL },
|
|
{ "root", PARAM_TYPE_STRING | PARAM_TYPE_REQUIRED, &RootDir, (PVOID) NULL },
|
|
{ "force", PARAM_TYPE_BOOL | PARAM_TYPE_SINGLE | PARAM_TYPE_OPTIONAL, &Force, (PVOID) NULL },
|
|
{ "debug", PARAM_TYPE_ULONG | PARAM_TYPE_HIDDEN, &DebugFlag, (PVOID) NULL },
|
|
{ "addself", PARAM_TYPE_BOOL | PARAM_TYPE_SINGLE | PARAM_TYPE_OPTIONAL, &AddSelf, (PVOID) TRUE },
|
|
{ "sacl", PARAM_TYPE_BOOL | PARAM_TYPE_SINGLE | PARAM_TYPE_OPTIONAL, &DoSacl, (PVOID) NULL }
|
|
|
|
};
|
|
|
|
#define ARGSET ( (sizeof ( Parameters ) / sizeof( PARAM ) ) )
|
|
|
|
VOID
|
|
DECLSPEC_NORETURN
|
|
Usage(
|
|
char * Me
|
|
)
|
|
{
|
|
int i ;
|
|
printf("%s - usage\n", Me);
|
|
for ( i = 0 ; i < ARGSET ; i++ )
|
|
{
|
|
if ( ( Parameters[ i ].Flags & PARAM_TYPE_HIDDEN ) == 0 )
|
|
{
|
|
printf("\t/%s%s\n", Parameters[i].Argument,
|
|
Parameters[ i ].Flags & PARAM_TYPE_SINGLE ? "" : ":value" );
|
|
}
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
VOID
|
|
FatalError(
|
|
PWSTR Message,
|
|
DWORD Error,
|
|
PWSTR Object
|
|
)
|
|
{
|
|
if ( Object )
|
|
{
|
|
fprintf( stderr, "Fatal error %d while working on object %ws\n",
|
|
Error, Object );
|
|
fputws( Message, stderr );
|
|
fputws( L"\n", stderr );
|
|
|
|
}
|
|
else
|
|
{
|
|
fprintf( stderr, "Fatal error %d, %s\n", Error, Message );
|
|
}
|
|
exit( Error );
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
DoParam(
|
|
int argc,
|
|
char * argv[]
|
|
)
|
|
{
|
|
int i ;
|
|
int j ;
|
|
PSTR Arg ;
|
|
PSTR Colon;
|
|
PSTR Format ;
|
|
BOOL Bail = FALSE ;
|
|
|
|
//
|
|
// Initialize Defaults:
|
|
//
|
|
|
|
for ( j = 0 ; j < ARGSET ; j++ )
|
|
{
|
|
switch ( Parameters[ j ].Flags & PARAM_TYPE_MASK )
|
|
{
|
|
case PARAM_TYPE_STRING:
|
|
if ( Parameters[ j ].Default )
|
|
{
|
|
RtlInitUnicodeString( (PUNICODE_STRING) Parameters[ j ].Value,
|
|
(LPWSTR) Parameters[ j ].Default );
|
|
|
|
}
|
|
else
|
|
{
|
|
ZeroMemory( Parameters[ j ].Value, sizeof( UNICODE_STRING ) );
|
|
}
|
|
break;
|
|
|
|
case PARAM_TYPE_ULONG:
|
|
* ((PULONG) Parameters[ j ].Value) = (ULONG) ((ULONG_PTR) Parameters[ j ].Default );
|
|
break;
|
|
|
|
case PARAM_TYPE_BOOL:
|
|
* ((PBOOL) Parameters[ j ].Value) = (BOOL) ((ULONG_PTR) Parameters[ j ].Default );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for ( i = 1 ; i < argc ; i++ )
|
|
{
|
|
Arg=argv[i];
|
|
|
|
if ( (*Arg == '/') ||
|
|
(*Arg == '-') )
|
|
{
|
|
|
|
Arg++ ;
|
|
|
|
Colon = strchr( Arg, ':' );
|
|
|
|
if ( Colon )
|
|
{
|
|
*Colon = '\0';
|
|
}
|
|
|
|
//
|
|
// Scan through the possible arguments
|
|
//
|
|
|
|
for ( j = 0 ; j < ARGSET ; j++ )
|
|
{
|
|
if ( _stricmp( Arg, Parameters[ j ].Argument ) == 0 )
|
|
{
|
|
//
|
|
// Found a parameter that matched. Now, check the supplied type:
|
|
//
|
|
|
|
if ( ( Parameters[ j ].Flags & PARAM_TYPE_FOUND ) &&
|
|
! ( Parameters[ j ].Flags & PARAM_TYPE_MULTIPLE ) )
|
|
{
|
|
printf("%s can be specified only once\n", Arg );
|
|
Usage( argv[0] );
|
|
}
|
|
|
|
if ( Colon )
|
|
{
|
|
*Colon++ = ':' ;
|
|
}
|
|
|
|
if ( ( (Parameters[ j ].Flags & PARAM_TYPE_OPTIONAL) == 0 ) &&
|
|
( ( Colon == NULL ) || ( *Colon == '\0' ) ) )
|
|
{
|
|
printf("%s needs a value with it\n", Arg );
|
|
Usage( argv[0] );
|
|
|
|
}
|
|
|
|
if ( ( Colon != NULL ) &&
|
|
( Parameters[ j ].Flags & PARAM_TYPE_SINGLE ) )
|
|
{
|
|
printf("%s takes no value\n", Arg );
|
|
Usage( argv[0] );
|
|
}
|
|
|
|
|
|
switch ( Parameters[ j ].Flags & PARAM_TYPE_MASK )
|
|
{
|
|
case PARAM_TYPE_STRING:
|
|
|
|
if ( !RtlCreateUnicodeStringFromAsciiz(
|
|
(PUNICODE_STRING) Parameters[ j ].Value,
|
|
Colon ) )
|
|
{
|
|
printf("out of memory\n");
|
|
Usage( argv[0] );
|
|
}
|
|
break;
|
|
|
|
case PARAM_TYPE_ULONG:
|
|
|
|
if ( !Colon )
|
|
{
|
|
*((PULONG) Parameters[ j ].Value) = 0 ;
|
|
}
|
|
else
|
|
{
|
|
Format = "%ul" ;
|
|
|
|
if ( *Colon == '0' )
|
|
{
|
|
//
|
|
// Possible different base.
|
|
//
|
|
|
|
if ( *(Colon + 1 ) )
|
|
{
|
|
switch ( *(Colon + 1) )
|
|
{
|
|
case 'x':
|
|
Format = "%x" ;
|
|
break;
|
|
|
|
case 'X':
|
|
Format = "%X" ;
|
|
break;
|
|
|
|
default:
|
|
Format = "%ul" ;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
sscanf( Colon, Format, Parameters[ j ].Value );
|
|
}
|
|
break;
|
|
|
|
case PARAM_TYPE_BOOL:
|
|
if ( !Colon )
|
|
{
|
|
*((PBOOL) Parameters[ j ].Value ) = TRUE ;
|
|
}
|
|
else
|
|
{
|
|
if ( *Colon == '1' )
|
|
{
|
|
*((PBOOL) Parameters[ j ].Value ) = TRUE ;
|
|
}
|
|
else
|
|
{
|
|
*((PBOOL) Parameters[ j ].Value ) = FALSE ;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
Usage( argv[ 0 ] );
|
|
}
|
|
|
|
Parameters[ j ].Flags |= PARAM_TYPE_FOUND ;
|
|
|
|
break; // break out of for loop
|
|
}
|
|
|
|
}
|
|
if ( j == ARGSET )
|
|
{
|
|
printf("-%s unrecognized\n", Arg );
|
|
Usage( argv[ 0 ] );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
printf("%s unexpected\n", Arg );
|
|
Usage( argv[0] );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Validate input:
|
|
//
|
|
|
|
Bail = FALSE ;
|
|
|
|
for ( j = 0 ; j < ARGSET ; j++ )
|
|
{
|
|
if ( (Parameters[ j ].Flags & PARAM_TYPE_REQUIRED) )
|
|
{
|
|
if ( ! ( Parameters[ j ].Flags & PARAM_TYPE_FOUND ) )
|
|
{
|
|
printf(" /%s missing\n", Parameters[ j ].Argument );
|
|
Bail = TRUE ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( Bail )
|
|
{
|
|
Usage( argv[ 0 ] );
|
|
}
|
|
|
|
}
|
|
|
|
BOOL
|
|
CheckRootFileSystem(
|
|
VOID
|
|
)
|
|
{
|
|
WCHAR Volume[ 8 ];
|
|
WCHAR Temp[ MAX_PATH ];
|
|
DWORD Size ;
|
|
DWORD FsFlags ;
|
|
|
|
|
|
if ( RootDir.Buffer[ 1 ] == L':' )
|
|
{
|
|
Volume[ 0 ] = RootDir.Buffer[ 0 ];
|
|
Volume[ 1 ] = RootDir.Buffer[ 1 ];
|
|
}
|
|
else
|
|
{
|
|
Size = MAX_PATH ;
|
|
|
|
GetCurrentDirectory( MAX_PATH, Temp );
|
|
|
|
Volume[ 0 ] = Temp[ 0 ];
|
|
Volume[ 1 ] = Temp[ 1 ];
|
|
|
|
}
|
|
|
|
Volume[ 2 ] = L'\\';
|
|
Volume[ 3 ] = L'\0';
|
|
|
|
if ( GetVolumeInformation(
|
|
Volume,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&Size,
|
|
&FsFlags,
|
|
Temp,
|
|
MAX_PATH ) )
|
|
{
|
|
if ( FsFlags & FS_PERSISTENT_ACLS )
|
|
{
|
|
return TRUE ;
|
|
}
|
|
}
|
|
|
|
return FALSE ;
|
|
}
|
|
|
|
PWSTR
|
|
GetTargetOfReparse(
|
|
PWSTR ReparsePoint
|
|
)
|
|
{
|
|
HANDLE Handle ;
|
|
NTSTATUS Status ;
|
|
OBJECT_ATTRIBUTES ObjA ;
|
|
UNICODE_STRING UnicodeName ;
|
|
NTSTATUS IgnoreStatus ;
|
|
IO_STATUS_BLOCK IoStatusBlock ;
|
|
PWSTR Target = NULL ;
|
|
|
|
FILE_DISPOSITION_INFORMATION Disposition;
|
|
|
|
PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
|
|
PCHAR ReparseBuffer = NULL;
|
|
ULONG ReparsePointTag = IO_REPARSE_TAG_RESERVED_ZERO;
|
|
USHORT ReparseDataLength = 0;
|
|
ULONG DesiredAccess ;
|
|
ULONG CreateOptions ;
|
|
|
|
IgnoreStatus = RtlDosPathNameToNtPathName_U(
|
|
ReparsePoint,
|
|
&UnicodeName,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS( IgnoreStatus ) )
|
|
{
|
|
return NULL ;
|
|
}
|
|
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjA,
|
|
&UnicodeName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
DesiredAccess = FILE_READ_DATA | SYNCHRONIZE;
|
|
CreateOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT;
|
|
|
|
|
|
//
|
|
// Open the reparse point for query.
|
|
//
|
|
|
|
Status = NtOpenFile(
|
|
&Handle,
|
|
DesiredAccess,
|
|
&ObjA,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
CreateOptions
|
|
);
|
|
|
|
RtlFreeHeap( RtlProcessHeap(), 0, UnicodeName.Buffer );
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
return NULL ;
|
|
}
|
|
|
|
|
|
ReparseDataLength = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
|
|
ReparseBuffer = RtlAllocateHeap(
|
|
RtlProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
ReparseDataLength
|
|
);
|
|
|
|
if (ReparseBuffer == NULL) {
|
|
|
|
FatalError( L"No memory", ERROR_OUTOFMEMORY, ReparsePoint );
|
|
}
|
|
|
|
//
|
|
// Now go and get the data.
|
|
//
|
|
|
|
Status = NtFsControlFile(
|
|
Handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
FSCTL_GET_REPARSE_POINT, // no input buffer
|
|
NULL, // input buffer length
|
|
0,
|
|
(PVOID)ReparseBuffer,
|
|
ReparseDataLength
|
|
);
|
|
|
|
|
|
|
|
//
|
|
// Close the file and free the buffer.
|
|
//
|
|
|
|
NtClose( Handle );
|
|
|
|
//
|
|
// Display the buffer.
|
|
//
|
|
|
|
ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
|
|
|
|
if ((ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) ||
|
|
(ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_SYMBOLIC_LINK)) {
|
|
|
|
if ( DebugFlag )
|
|
{
|
|
UNICODE_STRING NtLinkValue ;
|
|
|
|
NtLinkValue.Buffer = &ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer[ 0 ];
|
|
NtLinkValue.Length = ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
|
NtLinkValue.MaximumLength = NtLinkValue.Length ;
|
|
|
|
printf("base path is %wZ\n", &NtLinkValue );
|
|
|
|
}
|
|
|
|
|
|
Target = LocalAlloc( LMEM_FIXED,
|
|
(ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameLength + 1) * sizeof( WCHAR ) );
|
|
|
|
if ( Target )
|
|
{
|
|
RtlCopyMemory(
|
|
Target,
|
|
&ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer[ ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof( WCHAR ) + 1 ],
|
|
ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameLength
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
}
|
|
|
|
//
|
|
// Free the buffer.
|
|
//
|
|
|
|
RtlFreeHeap( RtlProcessHeap(), 0, ReparseBufferHeader );
|
|
|
|
return Target ;
|
|
}
|
|
|
|
|
|
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
GetRootSecurity(
|
|
VOID
|
|
)
|
|
{
|
|
HANDLE hRoot ;
|
|
NTSTATUS Status ;
|
|
BOOLEAN WasEnabled ;
|
|
PSECURITY_DESCRIPTOR psd ;
|
|
ULONG Size ;
|
|
SECURITY_INFORMATION si ;
|
|
ACCESS_MASK access ;
|
|
DWORD Returned ;
|
|
|
|
si = DACL_SECURITY_INFORMATION ;
|
|
access = READ_CONTROL ;
|
|
|
|
if ( DoSacl )
|
|
{
|
|
Status = RtlAdjustPrivilege(
|
|
SE_SECURITY_PRIVILEGE,
|
|
TRUE,
|
|
FALSE,
|
|
&WasEnabled );
|
|
|
|
if ( NT_SUCCESS( Status ) )
|
|
{
|
|
si |= SACL_SECURITY_INFORMATION ;
|
|
access |= ACCESS_SYSTEM_SECURITY ;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
Size = 0 ;
|
|
psd = NULL ;
|
|
|
|
GetFileSecurity(
|
|
RootDir.Buffer,
|
|
si,
|
|
NULL,
|
|
0,
|
|
&Size );
|
|
|
|
psd = LocalAlloc( LMEM_FIXED, Size );
|
|
|
|
if ( !GetFileSecurity(
|
|
RootDir.Buffer,
|
|
si,
|
|
psd,
|
|
Size,
|
|
&Size ) )
|
|
{
|
|
FatalError( L"Could not read security descriptor",
|
|
GetLastError(),
|
|
RootDir.Buffer );
|
|
|
|
}
|
|
|
|
|
|
|
|
return psd ;
|
|
}
|
|
|
|
DWORD
|
|
WriteSddlFile(
|
|
VOID
|
|
)
|
|
{
|
|
PSECURITY_DESCRIPTOR psd ;
|
|
ULONG Size ;
|
|
LPSTR AnsiStringSD ;
|
|
ULONG AnsiStringSDLen ;
|
|
HANDLE hSddlFile ;
|
|
CHAR Term[ 2 ] = { 0x0d, 0x0a };
|
|
|
|
psd = GetRootSecurity();
|
|
|
|
if ( !ConvertSecurityDescriptorToStringSecurityDescriptorA(
|
|
psd,
|
|
1,
|
|
DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION,
|
|
&AnsiStringSD,
|
|
&AnsiStringSDLen ) )
|
|
{
|
|
|
|
FatalError( L"Cannot convert security descriptor to string\n",
|
|
GetLastError(),
|
|
NULL );
|
|
|
|
}
|
|
|
|
|
|
hSddlFile = CreateFile(
|
|
SddlFile.Buffer,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL );
|
|
|
|
if ( hSddlFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
FatalError(
|
|
L"Cannot write security descriptor to file",
|
|
GetLastError(),
|
|
SddlFile.Buffer );
|
|
|
|
}
|
|
|
|
WriteFile(
|
|
hSddlFile,
|
|
AnsiStringSD,
|
|
AnsiStringSDLen,
|
|
&Size,
|
|
NULL );
|
|
|
|
LocalFree( AnsiStringSD );
|
|
|
|
WriteFile(
|
|
hSddlFile,
|
|
Term, sizeof(Term),
|
|
&Size, NULL );
|
|
|
|
CloseHandle( hSddlFile );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
InsertMe(
|
|
PSECURITY_DESCRIPTOR old,
|
|
ACCESS_MASK AccessRequired
|
|
)
|
|
{
|
|
DWORD Size ;
|
|
PACL OldAcl ;
|
|
PACL NewAcl ;
|
|
BOOL Present ;
|
|
BOOL Ignored ;
|
|
ACL_SIZE_INFORMATION AclSize ;
|
|
PSID Me = NULL ;
|
|
HANDLE hToken ;
|
|
UCHAR Scratch[ SECURITY_MAX_SID_SIZE + sizeof( TOKEN_USER ) ];
|
|
PTOKEN_USER User ;
|
|
PACCESS_ALLOWED_ACE NewAce ;
|
|
PACCESS_ALLOWED_ACE OldAce ;
|
|
PSECURITY_DESCRIPTOR psd ;
|
|
|
|
|
|
if ( OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken ) )
|
|
{
|
|
if ( GetTokenInformation( hToken, TokenUser, Scratch, sizeof( Scratch ), &Size ) )
|
|
{
|
|
User = (PTOKEN_USER) Scratch ;
|
|
|
|
Me = User->User.Sid ;
|
|
|
|
}
|
|
|
|
CloseHandle( hToken );
|
|
}
|
|
|
|
if ( !Me )
|
|
{
|
|
return NULL ;
|
|
|
|
}
|
|
|
|
|
|
GetSecurityDescriptorDacl(old, &Present, &OldAcl, &Ignored );
|
|
if ( !Present )
|
|
{
|
|
return NULL ;
|
|
|
|
}
|
|
|
|
GetAclInformation( OldAcl, &AclSize, sizeof( AclSize ), AclSizeInformation );
|
|
|
|
NewAcl = LocalAlloc(LMEM_FIXED, AclSize.AclBytesInUse + (sizeof( ACCESS_ALLOWED_ACE ) + GetLengthSid( Me )));
|
|
|
|
if ( !NewAcl )
|
|
{
|
|
return NULL ;
|
|
}
|
|
|
|
InitializeAcl( NewAcl, AclSize.AclBytesInUse + (sizeof( ACCESS_ALLOWED_ACE ) + GetLengthSid( Me ) ), ACL_REVISION );
|
|
|
|
AddAccessAllowedAce( NewAcl, ACL_REVISION, AccessRequired, Me );
|
|
|
|
FindFirstFreeAce( NewAcl, &NewAce );
|
|
|
|
GetAce( OldAcl, 0, &OldAce );
|
|
|
|
CopyMemory( NewAce, OldAce, AclSize.AclBytesInUse - sizeof( ACL ));
|
|
|
|
NewAcl->AceCount += (USHORT) AclSize.AceCount ;
|
|
|
|
psd = LocalAlloc(LMEM_FIXED, sizeof( SECURITY_DESCRIPTOR ) );
|
|
|
|
if ( psd )
|
|
{
|
|
InitializeSecurityDescriptor( psd, SECURITY_DESCRIPTOR_REVISION );
|
|
|
|
SetSecurityDescriptorDacl(psd, TRUE, NewAcl, FALSE );
|
|
|
|
}
|
|
|
|
return psd ;
|
|
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ReadSecurityDescriptor
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [SddlFileName] --
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
ReadSecurityDescriptor(
|
|
PWSTR SddlFileName
|
|
)
|
|
{
|
|
|
|
HANDLE hFile ;
|
|
PSECURITY_DESCRIPTOR psd ;
|
|
ULONG Size ;
|
|
ULONG SizeRead ;
|
|
PUCHAR Buffer ;
|
|
PSECURITY_DESCRIPTOR psdNew ;
|
|
|
|
|
|
hFile = CreateFileW(
|
|
SddlFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_DELETE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL );
|
|
|
|
if ( hFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
FatalError(
|
|
L"Unable to read ACL file",
|
|
GetLastError(),
|
|
SddlFile.Buffer );
|
|
|
|
}
|
|
|
|
Size = GetFileSize( hFile, NULL );
|
|
|
|
if ( Size == (DWORD) -1 )
|
|
{
|
|
FatalError(
|
|
L"ACL file corrupt, too large",
|
|
0,
|
|
NULL );
|
|
|
|
}
|
|
|
|
Buffer = LocalAlloc( LMEM_FIXED, Size + 1 );
|
|
|
|
if ( !Buffer )
|
|
{
|
|
FatalError(
|
|
L"Out of memory",
|
|
GetLastError(),
|
|
NULL );
|
|
|
|
}
|
|
|
|
if ( !ReadFile( hFile, Buffer, Size, &SizeRead, NULL ) )
|
|
{
|
|
FatalError(
|
|
L"Unable to read ACL file",
|
|
GetLastError(),
|
|
SddlFile.Buffer );
|
|
|
|
|
|
}
|
|
|
|
CloseHandle( hFile );
|
|
|
|
Buffer[ Size ] = '\0';
|
|
|
|
while ( ( Buffer[ Size - 1 ] == 0x0a ) ||
|
|
( Buffer[ Size - 1 ] == 0x0d ) ||
|
|
( Buffer[ Size - 1 ] == 0x1a ) )
|
|
{
|
|
Buffer[ Size - 1 ] = '\0' ;
|
|
Size-- ;
|
|
|
|
}
|
|
|
|
if ( !ConvertStringSecurityDescriptorToSecurityDescriptorA(
|
|
Buffer,
|
|
1,
|
|
&psd,
|
|
&Size ) )
|
|
{
|
|
|
|
FatalError(
|
|
L"ACL file is corrupt",
|
|
GetLastError(),
|
|
SddlFile.Buffer );
|
|
}
|
|
|
|
LocalFree( Buffer );
|
|
|
|
if ( AddSelf )
|
|
{
|
|
//
|
|
// Need to merge in a sid for "me"
|
|
//
|
|
|
|
psdNew = InsertMe( psd, FILE_ALL_ACCESS );
|
|
|
|
if ( psdNew )
|
|
{
|
|
LocalFree( psd );
|
|
psd = psdNew ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return psd ;
|
|
|
|
}
|
|
|
|
BOOL
|
|
WalkCallback(
|
|
PVOID Parameter,
|
|
PWSTR Path
|
|
)
|
|
{
|
|
|
|
|
|
return SetFileSecurity(
|
|
Path,
|
|
SecurityInfo,
|
|
Parameter );
|
|
|
|
}
|
|
|
|
BOOL
|
|
WalkEngine(
|
|
PFS_WALK_CONTROL Control
|
|
)
|
|
{
|
|
PFS_WALK_CONTROL NewControl ;
|
|
WIN32_FIND_DATA FindData ;
|
|
PWSTR Scan;
|
|
BOOL CallbackStatus ;
|
|
DWORD Status ;
|
|
DWORD FileTest ;
|
|
DWORD Limit ;
|
|
PSECURITY_DESCRIPTOR psd = NULL ;
|
|
//
|
|
// First, check for an override file:
|
|
//
|
|
|
|
if ( wcslen( Control->CurrentPath ) > MAX_PATH - 7 )
|
|
{
|
|
return FALSE ;
|
|
|
|
}
|
|
|
|
wcsncpy( Control->SearchPath, Control->CurrentPath, MAX_PATH );
|
|
wcscat( Control->SearchPath, L"acl.txt" );
|
|
|
|
FileTest = GetFileAttributes( Control->SearchPath );
|
|
if ( FileTest != (DWORD) -1 )
|
|
{
|
|
//
|
|
// File exists. Load it and use it for all files from here on down
|
|
//
|
|
|
|
psd = ReadSecurityDescriptor( Control->SearchPath );
|
|
|
|
if ( psd )
|
|
{
|
|
Control->Parameter = psd ;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
wcscpy( Control->SearchPath, Control->CurrentPath );
|
|
wcscat( Control->SearchPath, L"*.*" );
|
|
|
|
Control->Search = FindFirstFile( Control->SearchPath, &FindData );
|
|
|
|
if ( Control->Search )
|
|
{
|
|
wcscpy( Control->FilePath, Control->CurrentPath);
|
|
Scan = &Control->FilePath[ wcslen( Control->FilePath ) ]; // one char past trailing backslash
|
|
Limit = MAX_PATH - wcslen( Control->FilePath );
|
|
|
|
do
|
|
{
|
|
|
|
if ( wcslen( FindData.cFileName ) > Limit )
|
|
{
|
|
Control->Stats->EngineErrors++ ;
|
|
fprintf(stderr, "File '%ws' in directory '%ws' exceeds maximum length",
|
|
FindData.cFileName, Control->CurrentPath );
|
|
continue;
|
|
|
|
}
|
|
wcscpy( Scan, FindData.cFileName );
|
|
|
|
if ( wcscmp( Scan,L".." ) == 0 )
|
|
{
|
|
//
|
|
// always immediately skip the parent link
|
|
//
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
CallbackStatus = Control->Callback(
|
|
Control->Parameter,
|
|
Control->FilePath );
|
|
|
|
if ( !CallbackStatus )
|
|
{
|
|
Control->Stats->CallbackErrors ++ ;
|
|
}
|
|
|
|
if ( wcscmp( Scan, L".") == 0 )
|
|
{
|
|
//
|
|
// allow for processing the current directory,
|
|
// but do not recurse...
|
|
//
|
|
continue;
|
|
|
|
}
|
|
|
|
if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
|
|
{
|
|
//
|
|
// Time to recurse (we do depth first, it's easier)
|
|
//
|
|
|
|
Control->Stats->Directories++ ;
|
|
|
|
NewControl = LocalAlloc( LMEM_FIXED, sizeof( FS_WALK_CONTROL ) );
|
|
|
|
if ( NewControl )
|
|
{
|
|
NewControl->Callback = Control->Callback ;
|
|
NewControl->Parameter = Control->Parameter ;
|
|
NewControl->Options = Control->Options ;
|
|
NewControl->Stats = Control->Stats ;
|
|
|
|
wcscpy(NewControl->CurrentPath, Control->FilePath );
|
|
wcscat(NewControl->CurrentPath, L"\\");
|
|
|
|
WalkEngine( NewControl );
|
|
|
|
LocalFree( NewControl );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
Control->Stats->EngineErrors ++ ;
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
Control->Stats->Files++ ;
|
|
|
|
}
|
|
|
|
} while ( FindNextFile( Control->Search, &FindData ) );
|
|
|
|
FindClose( Control->Search );
|
|
|
|
}
|
|
else
|
|
{
|
|
Control->Stats->EngineErrors++ ;
|
|
}
|
|
|
|
if ( psd )
|
|
{
|
|
LocalFree( psd );
|
|
}
|
|
|
|
return TRUE ;
|
|
|
|
}
|
|
|
|
BOOL
|
|
WalkTree(
|
|
PWSTR StartPath,
|
|
ULONG Options,
|
|
PFS_WALK_CALLBACK Callback,
|
|
PVOID Parameter
|
|
)
|
|
{
|
|
|
|
PFS_WALK_CONTROL Control ;
|
|
FS_ENGINE_STATS Stats ;
|
|
PWSTR Scan;
|
|
|
|
Control = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, sizeof( FS_WALK_CONTROL ) );
|
|
|
|
if ( !Control )
|
|
{
|
|
return FALSE ;
|
|
|
|
}
|
|
|
|
ZeroMemory( &Stats, sizeof( Stats ) );
|
|
|
|
Control->Callback = Callback ;
|
|
Control->Parameter = Parameter ;
|
|
Control->Options = 0 ;
|
|
Control->Stats = &Stats ;
|
|
|
|
wcscpy( Control->CurrentPath, StartPath );
|
|
Scan = &Control->CurrentPath[ wcslen( Control->CurrentPath ) - 1 ];
|
|
if ( *Scan != L'\\' )
|
|
{
|
|
Scan++ ;
|
|
*Scan = L'\\';
|
|
|
|
}
|
|
|
|
if ( !Callback( Parameter, StartPath ) )
|
|
{
|
|
Stats.CallbackErrors++ ;
|
|
|
|
}
|
|
|
|
Stats.Directories = 1;
|
|
|
|
WalkEngine(Control);
|
|
|
|
if ( DebugFlag )
|
|
{
|
|
printf("Directories \t%d\n", Stats.Directories );
|
|
printf("Files \t%d\n", Stats.Files );
|
|
printf("EngineErrors\t%d\n", Stats.EngineErrors );
|
|
printf("CallbackErrors\t%d\n", Stats.CallbackErrors );
|
|
|
|
|
|
|
|
}
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
UpdateAcls(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
PSECURITY_DESCRIPTOR psd ;
|
|
NTSTATUS Status ;
|
|
SECURITY_INFORMATION si ;
|
|
SECURITY_DESCRIPTOR_CONTROL control ;
|
|
ULONG ignored;
|
|
BOOLEAN WasEnabled ;
|
|
|
|
psd = ReadSecurityDescriptor( SddlFile.Buffer );
|
|
|
|
|
|
if ( !psd )
|
|
{
|
|
return FALSE ;
|
|
|
|
}
|
|
|
|
RtlGetControlSecurityDescriptor(psd,&control,&ignored);
|
|
|
|
si = 0 ;
|
|
|
|
if ( control & SE_DACL_PRESENT )
|
|
{
|
|
si |= DACL_SECURITY_INFORMATION ;
|
|
|
|
}
|
|
|
|
if ( control & SE_SACL_PRESENT )
|
|
{
|
|
|
|
Status = RtlAdjustPrivilege(
|
|
SE_SECURITY_PRIVILEGE,
|
|
TRUE,
|
|
FALSE,
|
|
&WasEnabled );
|
|
|
|
if ( NT_SUCCESS( Status ) )
|
|
{
|
|
si |= SACL_SECURITY_INFORMATION ;
|
|
}
|
|
}
|
|
|
|
SecurityInfo = si ;
|
|
|
|
printf("Updating ACLs from %ws for tree %ws\n",
|
|
SddlFile.Buffer, RootDir.Buffer );
|
|
|
|
WalkTree(RootDir.Buffer, 0, WalkCallback, psd );
|
|
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
BOOL
|
|
TestAcl(
|
|
VOID
|
|
)
|
|
{
|
|
PSECURITY_DESCRIPTOR Current ;
|
|
PSECURITY_DESCRIPTOR Stored ;
|
|
PACL CurrentAcl ;
|
|
PACL StoredAcl ;
|
|
ULONG CurrentLength ;
|
|
ULONG StoredLength ;
|
|
BOOL Match = FALSE ;
|
|
NTSTATUS Status ;
|
|
BOOLEAN Present ;
|
|
BOOLEAN Defaulted ;
|
|
ACL_SIZE_INFORMATION AclSize ;
|
|
|
|
Stored = ReadSecurityDescriptor( SddlFile.Buffer );
|
|
|
|
Status = RtlGetDaclSecurityDescriptor(Stored,&Present,&StoredAcl,&Defaulted);
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
return FALSE ;
|
|
|
|
}
|
|
|
|
Current = GetRootSecurity();
|
|
|
|
Status = RtlGetDaclSecurityDescriptor(Current,&Present,&CurrentAcl,&Defaulted);
|
|
|
|
if ( !NT_SUCCESS( Status ) || (CurrentAcl == NULL) )
|
|
{
|
|
return FALSE ;
|
|
|
|
}
|
|
|
|
Status = RtlQueryInformationAcl(StoredAcl,&AclSize,sizeof(AclSize),AclSizeInformation);
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
return FALSE ;
|
|
|
|
}
|
|
|
|
StoredLength = AclSize.AclBytesInUse ;
|
|
|
|
Status = RtlQueryInformationAcl( CurrentAcl,&AclSize,sizeof(AclSize), AclSizeInformation );
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
return FALSE ;
|
|
|
|
}
|
|
|
|
CurrentLength = AclSize.AclBytesInUse;
|
|
|
|
if ( CurrentLength == StoredLength )
|
|
{
|
|
Match = RtlEqualMemory(
|
|
CurrentAcl,
|
|
StoredAcl,
|
|
CurrentLength );
|
|
}
|
|
|
|
LocalFree( Stored );
|
|
LocalFree( Current );
|
|
|
|
return Match ;
|
|
|
|
}
|
|
|
|
|
|
int __cdecl main (int argc, char *argv[])
|
|
{
|
|
|
|
DoParam( argc, argv );
|
|
|
|
if ( DebugFlag )
|
|
{
|
|
printf("SDDL File \t%ws\n", SddlFile.Buffer );
|
|
printf("Root Dir \t%ws\n", RootDir.Buffer );
|
|
|
|
|
|
}
|
|
|
|
if ( CreateSddl )
|
|
{
|
|
WriteSddlFile();
|
|
return 0 ;
|
|
}
|
|
|
|
if ( !CheckRootFileSystem() )
|
|
{
|
|
printf("Volume does not support ACLs\n" );
|
|
return 0 ;
|
|
}
|
|
|
|
if ( !Force )
|
|
{
|
|
if ( TestAcl() )
|
|
{
|
|
if ( DebugFlag )
|
|
{
|
|
printf("ACL is up-to-date\n" );
|
|
}
|
|
return 0 ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
UpdateAcls();
|
|
|
|
|
|
}
|