648 lines
17 KiB
C
648 lines
17 KiB
C
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
slmcheck.c
|
|
|
|
Abstract:
|
|
|
|
This source file defines a single function that will check the
|
|
consistency of a SLM Status file.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "slmcheck.tmh"
|
|
#pragma hdrstop
|
|
|
|
#ifdef SLMDBG
|
|
|
|
//
|
|
// This is terrible
|
|
//
|
|
NTSTATUS
|
|
NtCreateEvent (
|
|
OUT PHANDLE EventHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
|
|
IN EVENT_TYPE EventType,
|
|
IN BOOLEAN InitialState
|
|
);
|
|
|
|
//
|
|
// So is this
|
|
//
|
|
NTSTATUS
|
|
NtWaitForSingleObject(
|
|
IN HANDLE Handle,
|
|
IN BOOLEAN Alertable,
|
|
IN PLARGE_INTEGER Timeout OPTIONAL
|
|
);
|
|
|
|
BOOLEAN SrvDisallowSlmAccessEnabled = FALSE;
|
|
BOOLEAN SrvSlmFailed = FALSE;
|
|
|
|
#define toupper(c) ( (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c )
|
|
|
|
|
|
NTSTATUS
|
|
SrvpValidateStatusFile(
|
|
IN PVOID StatusFileData,
|
|
IN ULONG StatusFileLength,
|
|
OUT PULONG FileOffsetOfInvalidData
|
|
);
|
|
|
|
VOID
|
|
SrvReportCorruptSlmStatus (
|
|
IN PUNICODE_STRING StatusFile,
|
|
IN NTSTATUS Status,
|
|
IN ULONG Offset,
|
|
IN ULONG Operation,
|
|
IN PSESSION Session
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ANSI_STRING ansiStatusFile;
|
|
TIME time;
|
|
TIME_FIELDS timeFields;
|
|
ULONG i, j;
|
|
UNICODE_STRING userName;
|
|
|
|
if( SrvSlmFailed ) {
|
|
return;
|
|
}
|
|
|
|
status = RtlUnicodeStringToAnsiString( &ansiStatusFile, StatusFile, TRUE );
|
|
ASSERT( NT_SUCCESS(status) );
|
|
|
|
KeQuerySystemTime( &time );
|
|
RtlTimeToTimeFields( &time, &timeFields );
|
|
|
|
SrvGetUserAndDomainName ( Session, &userName, NULL );
|
|
|
|
KdPrint(( "\n*** CORRUPT STATUS FILE DETECTED ***\n"
|
|
" File: %Z\n"
|
|
" Status: 0x%lx, Offset: 0x%lx, detected on %s\n",
|
|
&ansiStatusFile, Status, Offset,
|
|
Operation == SLMDBG_CLOSE ? "close" : "rename" ));
|
|
KdPrint(( " Workstation: %wZ, User: %wZ, OS: %d\n",
|
|
&Session->Connection->PagedConnection->ClientMachineNameString,
|
|
&userName,
|
|
&SrvClientTypes[Session->Connection->SmbDialect] ));
|
|
KdPrint(( " Current time: %d-%d-%d ",
|
|
timeFields.Month, timeFields.Day, timeFields.Year ));
|
|
KdPrint(( "%d:%d:%d\n",
|
|
timeFields.Hour, timeFields.Minute, timeFields.Second ));
|
|
|
|
SrvReleaseUserAndDomainName( Session, &userName, NULL );
|
|
|
|
#if 0
|
|
//
|
|
// Send a broadcast message.
|
|
//
|
|
SrvSendSecondClassMailslot( ansiStatusFile.Buffer, StatusFile->Length + 1,
|
|
"BLUBBER", "BLUBBER" );
|
|
#endif
|
|
|
|
RtlFreeAnsiString( &ansiStatusFile );
|
|
|
|
} // SrvReportCorruptSlmStatus
|
|
|
|
VOID
|
|
SrvReportSlmStatusOperations (
|
|
IN PRFCB Rfcb,
|
|
IN BOOLEAN Force
|
|
)
|
|
{
|
|
ULONG first, last, i;
|
|
PRFCB_TRACE trace;
|
|
TIME_FIELDS timeFields;
|
|
PSZ command;
|
|
BOOLEAN oplockBreak;
|
|
|
|
if( !Force && SrvSlmFailed ) {
|
|
return;
|
|
}
|
|
|
|
KdPrint(( " Number of operations: %d, number of writes: %d\n",
|
|
Rfcb->OperationCount, Rfcb->WriteCount ));
|
|
|
|
if( Rfcb->Connection && GET_BLOCK_STATE(Rfcb->Connection) != BlockStateActive ) {
|
|
KdPrint(( " Connection State = %u\n", GET_BLOCK_STATE( Rfcb->Connection )));
|
|
}
|
|
|
|
if ( Rfcb->TraceWrapped || (Rfcb->NextTrace != 0) ) {
|
|
|
|
first = Rfcb->TraceWrapped ? Rfcb->NextTrace : 0;
|
|
last = Rfcb->NextTrace == 0 ? SLMDBG_TRACE_COUNT - 1 :
|
|
Rfcb->NextTrace - 1;
|
|
|
|
i = first;
|
|
|
|
while ( TRUE ) {
|
|
|
|
trace = &Rfcb->Trace[i];
|
|
|
|
RtlTimeToTimeFields( &trace->Time, &timeFields );
|
|
KdPrint(( " %s%d: ", i < 10 ? "0" : "", i ));
|
|
KdPrint(( "%d-%d-%d ",
|
|
timeFields.Month, timeFields.Day, timeFields.Year ));
|
|
KdPrint(( "%s%d:%s%d:",
|
|
timeFields.Hour < 10 ? "0" : "", timeFields.Hour,
|
|
timeFields.Minute < 10 ? "0" : "", timeFields.Minute ));
|
|
KdPrint(( "%s%d: ",
|
|
timeFields.Second < 10 ? "0" : "", timeFields.Second ));
|
|
|
|
oplockBreak = FALSE;
|
|
|
|
switch ( trace->Command ) {
|
|
|
|
case SMB_COM_READ:
|
|
case SMB_COM_WRITE:
|
|
case SMB_COM_READ_ANDX:
|
|
case SMB_COM_WRITE_ANDX:
|
|
case SMB_COM_LOCK_AND_READ:
|
|
case SMB_COM_WRITE_AND_UNLOCK:
|
|
case SMB_COM_WRITE_AND_CLOSE:
|
|
case SMB_COM_READ_RAW:
|
|
case SMB_COM_WRITE_RAW:
|
|
case SMB_COM_LOCK_BYTE_RANGE:
|
|
case SMB_COM_UNLOCK_BYTE_RANGE:
|
|
case SMB_COM_LOCKING_ANDX:
|
|
|
|
switch ( trace->Command ) {
|
|
|
|
case SMB_COM_READ:
|
|
command = "Read";
|
|
break;
|
|
|
|
case SMB_COM_WRITE:
|
|
command = "Write";
|
|
break;
|
|
|
|
case SMB_COM_READ_ANDX:
|
|
command = "Read And X";
|
|
break;
|
|
|
|
case SMB_COM_WRITE_ANDX:
|
|
command = "Write And X";
|
|
break;
|
|
|
|
case SMB_COM_LOCK_AND_READ:
|
|
command = "Lock And Read";
|
|
break;
|
|
|
|
case SMB_COM_WRITE_AND_UNLOCK:
|
|
command = "Write And Unlock";
|
|
break;
|
|
|
|
case SMB_COM_WRITE_AND_CLOSE:
|
|
command = "Write And Close";
|
|
break;
|
|
|
|
case SMB_COM_READ_RAW:
|
|
if ( (trace->Flags & 1) == 0 ) {
|
|
command = "Read Raw (copy)";
|
|
} else {
|
|
command = "Read Raw (MDL)";
|
|
}
|
|
break;
|
|
|
|
case SMB_COM_WRITE_RAW:
|
|
if ( (trace->Flags & 2) == 0 ) {
|
|
if ( (trace->Flags & 1) == 0 ) {
|
|
command = "Write Raw (copy, no immed)";
|
|
} else {
|
|
command = "Write Raw (MDL, no immed)";
|
|
}
|
|
} else {
|
|
if ( (trace->Flags & 1) == 0 ) {
|
|
command = "Write Raw (copy, immed)";
|
|
} else {
|
|
command = "Write Raw (MDL, immed)";
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SMB_COM_LOCK_BYTE_RANGE:
|
|
command = "Lock Byte Range";
|
|
break;
|
|
|
|
case SMB_COM_UNLOCK_BYTE_RANGE:
|
|
command = "Unlock Byte Range";
|
|
break;
|
|
|
|
case SMB_COM_LOCKING_ANDX:
|
|
if ( trace->Flags == 0 ) {
|
|
command = "Locking And X (lock)";
|
|
} else if ( trace->Flags == 1 ) {
|
|
command = "Locking And X (unlock)";
|
|
} else {
|
|
command = "Locking And X (release oplock)";
|
|
oplockBreak = TRUE;
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
if ( !oplockBreak ) {
|
|
KdPrint(( "%s, offset = 0x%lx, len = 0x%lx\n",
|
|
command, trace->Data.ReadWrite.Offset,
|
|
trace->Data.ReadWrite.Length ));
|
|
} else {
|
|
KdPrint(( "%s\n", command ));
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
KdPrint(( "command = 0x%lx, flags = 0x%lx\n",
|
|
trace->Command, trace->Flags ));
|
|
|
|
}
|
|
|
|
if ( i == last ) break;
|
|
|
|
if ( ++i == SLMDBG_TRACE_COUNT ) i = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SrvSendSecondClassMailslot( "SLM CORRUPTION", 15, "BLUBBER", "BLUBBER" );
|
|
|
|
return;
|
|
|
|
} // SrvReportSlmStatusOperations
|
|
|
|
VOID
|
|
SrvCreateMagicSlmName (
|
|
IN PUNICODE_STRING StatusFile,
|
|
OUT PUNICODE_STRING MagicFile
|
|
)
|
|
{
|
|
LONG fileLength;
|
|
ULONG dirLength;
|
|
PCHAR magicName = "\\status.nfw";
|
|
PCHAR src;
|
|
PWCH dest;
|
|
|
|
fileLength = (strlen( magicName ) + 1) * sizeof(WCHAR);
|
|
dirLength = SrvGetSubdirectoryLength( StatusFile );
|
|
MagicFile->MaximumLength = (USHORT)(dirLength + fileLength);
|
|
MagicFile->Length = (USHORT)(MagicFile->MaximumLength - sizeof(WCHAR));
|
|
MagicFile->Buffer = ExAllocatePool( PagedPool, MagicFile->MaximumLength );
|
|
ASSERT( MagicFile->Buffer != NULL );
|
|
|
|
RtlCopyMemory( MagicFile->Buffer, StatusFile->Buffer, dirLength );
|
|
src = magicName;
|
|
dest = (PWCH)((PCHAR)MagicFile->Buffer + dirLength);
|
|
for ( fileLength = strlen(magicName); fileLength >= 0; fileLength-- ) {
|
|
*dest++ = *src++;
|
|
}
|
|
|
|
return;
|
|
|
|
} // SrvCreateMagicSlmName
|
|
|
|
VOID
|
|
SrvDisallowSlmAccess (
|
|
IN PUNICODE_STRING StatusFile,
|
|
IN HANDLE RootDirectory
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING file;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
HANDLE fileHandle;
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
if( SrvSlmFailed ) {
|
|
return;
|
|
}
|
|
|
|
SrvSlmFailed = TRUE;
|
|
|
|
if( SrvDisallowSlmAccessEnabled == FALSE ) {
|
|
return;
|
|
}
|
|
|
|
SrvCreateMagicSlmName( StatusFile, &file );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&file,
|
|
OBJ_CASE_INSENSITIVE,
|
|
RootDirectory,
|
|
NULL
|
|
);
|
|
|
|
KdPrint(( "Disallowing access to SLM directory %wZ\n", &file ));
|
|
|
|
status = IoCreateFile(
|
|
&fileHandle,
|
|
GENERIC_READ,
|
|
&objectAttributes,
|
|
&iosb,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN_IF,
|
|
FILE_NON_DIRECTORY_FILE,
|
|
NULL,
|
|
0,
|
|
CreateFileTypeNone,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
ExFreePool( file.Buffer );
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
status = iosb.Status;
|
|
}
|
|
if ( NT_SUCCESS(status) ) {
|
|
NtClose( fileHandle );
|
|
} else {
|
|
KdPrint(( "Attempt to disallow SLM access failed: 0x%lx\n", status ));
|
|
}
|
|
|
|
return;
|
|
|
|
} // SrvDisallowSlmAccess
|
|
|
|
BOOLEAN
|
|
SrvIsSlmAccessDisallowed (
|
|
IN PUNICODE_STRING StatusFile,
|
|
IN HANDLE RootDirectory
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING file;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
HANDLE fileHandle;
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
if ( !SrvDisallowSlmAccessEnabled ) {
|
|
return FALSE;
|
|
}
|
|
|
|
SrvCreateMagicSlmName( StatusFile, &file );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&file,
|
|
OBJ_CASE_INSENSITIVE,
|
|
RootDirectory,
|
|
NULL
|
|
);
|
|
|
|
status = IoCreateFile(
|
|
&fileHandle,
|
|
GENERIC_READ,
|
|
&objectAttributes,
|
|
&iosb,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_NON_DIRECTORY_FILE,
|
|
NULL,
|
|
0,
|
|
CreateFileTypeNone,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
ExFreePool( file.Buffer );
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
status = iosb.Status;
|
|
}
|
|
if ( NT_SUCCESS(status) ) {
|
|
NtClose( fileHandle );
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
} // SrvIsSlmAccessDisallowed
|
|
|
|
BOOLEAN
|
|
SrvIsEtcFile (
|
|
IN PUNICODE_STRING FileName
|
|
)
|
|
{
|
|
if ( ((RtlUnicodeStringToAnsiSize( FileName ) - 1) >= 4) &&
|
|
(toupper(FileName->Buffer[0]) == 'E') &&
|
|
(toupper(FileName->Buffer[1]) == 'T') &&
|
|
(toupper(FileName->Buffer[2]) == 'C') &&
|
|
( FileName->Buffer[3] == '\\') ) {
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
LONG i;
|
|
|
|
for ( i = 0;
|
|
i < (LONG)RtlUnicodeStringToAnsiSize( FileName ) - 1 - 4;
|
|
i++ ) {
|
|
|
|
if ( ( FileName->Buffer[i] == '\\') &&
|
|
(toupper(FileName->Buffer[i+1]) == 'E' ) &&
|
|
(toupper(FileName->Buffer[i+2]) == 'T' ) &&
|
|
(toupper(FileName->Buffer[i+3]) == 'C' ) &&
|
|
( FileName->Buffer[i+4] == '\\') ) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // SrvIsEtcFile
|
|
|
|
BOOLEAN
|
|
SrvIsSlmStatus (
|
|
IN PUNICODE_STRING StatusFile
|
|
)
|
|
{
|
|
UNICODE_STRING baseName;
|
|
|
|
if ( !SrvIsEtcFile( StatusFile ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
SrvGetBaseFileName( StatusFile, &baseName );
|
|
|
|
if ( ((RtlUnicodeStringToAnsiSize( &baseName ) - 1) == 10) &&
|
|
(toupper(baseName.Buffer[0]) == 'S') &&
|
|
(toupper(baseName.Buffer[1]) == 'T') &&
|
|
(toupper(baseName.Buffer[2]) == 'A') &&
|
|
(toupper(baseName.Buffer[3]) == 'T') &&
|
|
(toupper(baseName.Buffer[4]) == 'U') &&
|
|
(toupper(baseName.Buffer[5]) == 'S') &&
|
|
( baseName.Buffer[6] == '.') &&
|
|
(toupper(baseName.Buffer[7]) == 'S') &&
|
|
(toupper(baseName.Buffer[8]) == 'L') &&
|
|
(toupper(baseName.Buffer[9]) == 'M') ) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // SrvIsSlmStatus
|
|
|
|
BOOLEAN
|
|
SrvIsTempSlmStatus (
|
|
IN PUNICODE_STRING StatusFile
|
|
)
|
|
{
|
|
UNICODE_STRING baseName;
|
|
|
|
if ( !SrvIsEtcFile( StatusFile ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
SrvGetBaseFileName( StatusFile, &baseName );
|
|
|
|
if ( ((RtlUnicodeStringToAnsiSize( &baseName ) - 1) == 5) &&
|
|
(toupper(baseName.Buffer[0]) == 'T') &&
|
|
( baseName.Buffer[1] == '0') ) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // SrvIsTempSlmStatus
|
|
|
|
NTSTATUS
|
|
SrvValidateSlmStatus(
|
|
IN HANDLE StatusFile,
|
|
IN PWORK_CONTEXT WorkContext,
|
|
OUT PULONG FileOffsetOfInvalidData
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PULONG buffer, p, ep, s;
|
|
LARGE_INTEGER offset;
|
|
ULONG previousRun = 0;
|
|
HANDLE eventHandle;
|
|
OBJECT_ATTRIBUTES obja;
|
|
ULONG key;
|
|
|
|
#define ZERORUNLEN 2048
|
|
|
|
#define SLMREADSIZE (10 * 4096)
|
|
|
|
if( SrvSlmFailed ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
buffer = ExAllocatePoolWithTag( NonPagedPool, SLMREADSIZE, BlockTypeDataBuffer );
|
|
if( buffer == NULL ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
*FileOffsetOfInvalidData = 0;
|
|
offset.QuadPart = 0;
|
|
|
|
InitializeObjectAttributes( &obja, NULL, OBJ_CASE_INSENSITIVE, NULL, NULL );
|
|
|
|
Status = NtCreateEvent( &eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
&obja,
|
|
SynchronizationEvent,
|
|
FALSE
|
|
);
|
|
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if( ARGUMENT_PRESENT( WorkContext ) ) {
|
|
key = WorkContext->Rfcb->ShiftedFid |
|
|
SmbGetAlignedUshort( &WorkContext->RequestHeader->Pid );
|
|
}
|
|
|
|
//
|
|
// Scan through the file, looking for a run of zeros
|
|
//
|
|
while( 1 ) {
|
|
|
|
RtlZeroMemory( &IoStatus, sizeof( IoStatus ) );
|
|
|
|
Status = NtReadFile( StatusFile,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&IoStatus,
|
|
buffer,
|
|
SLMREADSIZE,
|
|
&offset,
|
|
ARGUMENT_PRESENT( WorkContext ) ? &key : NULL
|
|
);
|
|
|
|
if( Status == STATUS_PENDING ) {
|
|
NtWaitForSingleObject( eventHandle, FALSE, NULL );
|
|
}
|
|
|
|
Status = IoStatus.Status;
|
|
|
|
if( Status == STATUS_END_OF_FILE ) {
|
|
break;
|
|
}
|
|
|
|
if( Status != STATUS_SUCCESS ) {
|
|
NtClose( eventHandle );
|
|
ExFreePool( buffer );
|
|
return Status;
|
|
}
|
|
|
|
if( IoStatus.Information == 0 ) {
|
|
break;
|
|
}
|
|
|
|
ep = (PULONG)((ULONG)buffer + IoStatus.Information);
|
|
|
|
for( p = buffer; p < ep; p++ ) {
|
|
if( *p == 0 ) {
|
|
for( s = p; s < ep && *s == 0; s++ )
|
|
;
|
|
|
|
if( (ULONG)s - (ULONG)p >= ZERORUNLEN ) {
|
|
|
|
*FileOffsetOfInvalidData = offset.LowPart + ((ULONG)p - (ULONG)buffer);
|
|
|
|
KdPrint(( "SRV: Run of %u zeros in SLM file at offset %u decimal!\n",
|
|
(ULONG)s - (ULONG)p, *FileOffsetOfInvalidData ));
|
|
|
|
ExFreePool( buffer );
|
|
NtClose( eventHandle );
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
p = s;
|
|
|
|
}
|
|
}
|
|
|
|
offset.QuadPart += IoStatus.Information;
|
|
}
|
|
|
|
NtClose( eventHandle );
|
|
ExFreePool( buffer );
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
#endif
|