|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
efsrtl.c
Abstract:
This module contains the code that implements the EFS call back routines.
Author:
Robert Gu (robertg) 08-Dec-1996
Environment:
Kernel mode
Revision History:
--*/
#include "efsrtl.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, EfsEncryptKeyFsData)
#pragma alloc_text(PAGE, EfsOpenFile)
#pragma alloc_text(PAGE, EfsFileControl)
#pragma alloc_text(PAGE, EfsRead)
#pragma alloc_text(PAGE, EfsWrite)
#pragma alloc_text(PAGE, EfsFreeContext)
#pragma alloc_text(PAGE, EfsMountVolumn)
#pragma alloc_text(PAGE, EfsDismountVolumn)
#pragma alloc_text(PAGE, EfsDismountVolumn)
#endif
VOID EfsEncryptKeyFsData( IN PVOID DataBuffer, IN ULONG DataLength, IN ULONG DataEncOffset, IN ULONG RefdataEncOffset, IN ULONG RefdataEncLength ) /*++
Routine Description:
This is called by EFS driver to prepare a FSCTL input data buffer. The result data will be in the format of SUB-CODE plain text, [FSCTL_CODE, SUB-CODE, refdata, [refdata]sk, $EFS]sk
Arguments:
DataBuffer -- Point to a buffer holding the FSCTL input data.
DataLength -- Input data length.
DataEncOffset -- The offset of the first byte to be encrypted.
RefdataEncOffset -- The offset of the first reference byte to be encrypted. Second round encryption.
RefdataEncLength -- The length of the refdata.
Return Value:
No.
--*/ {
LONG bytesToBeEnc; PUCHAR pWorkData; ULONG encryptionRound;
PAGED_CODE();
//
// Data to be encrypted must be in the blocks of DES_BLOCKLEN
//
ASSERT( ((DataLength - DataEncOffset) % DES_BLOCKLEN) == 0 ); ASSERT( (RefdataEncLength % DES_BLOCKLEN) == 0 );
//
// Encrypt the reference data first. Reference data is the data we used to
// verify the caller. The data can be in the form FEK or sessionKey or
// sessionKey plus some changeable data
//
pWorkData = ((PUCHAR)DataBuffer) + RefdataEncOffset; bytesToBeEnc = (LONG) RefdataEncLength; encryptionRound = 1;
do {
while ( bytesToBeEnc > 0 ) {
//
// Encrypt data with DES
//
des( pWorkData, pWorkData, &(EfsData.SessionDesTable[0]), ENCRYPT );
pWorkData += DES_BLOCKLEN; bytesToBeEnc -= DES_BLOCKLEN;
}
//
// Then encrypt the whole data except the header bytes.
//
pWorkData = ((PUCHAR)DataBuffer) + DataEncOffset; bytesToBeEnc = (LONG) (DataLength - DataEncOffset); encryptionRound++;
} while ( encryptionRound < 3 );
return;
}
NTSTATUS EfsOpenFile( IN OBJECT_HANDLE FileHdl, IN OBJECT_HANDLE ParentDir OPTIONAL, IN PIO_STACK_LOCATION IrpSp, IN ULONG FileDirFlag, IN ULONG SystemState, IN PIRP_CONTEXT IrpContext, IN PDEVICE_OBJECT VolDo, IN PVOID PfileKeyContext, IN OUT PVOID *PContext, IN OUT PULONG PContextLength, IN OUT PVOID *PCreateContext, IN OUT PBOOLEAN Reserved ) /*++
Routine Description:
This is a call back routine. It will be called back by file system when an encrypted file is opened or a new file under encrypted directory is created.
Arguments:
FileHdl -- An object handle of the file
ParentDir - An object handle of the parent. Can be null for create file in root directory. It will be used by EFS only a new file is created.
IrpSp -- Irp Stack Location pointer.
FileDirFlag -- Indicating the status of the parent of the stream, may have four values, FILE_NEW, FILE_EXISTING, DIRECTORY_NEW and DIRECTORY_EXISTING and the status of the stream itself.
IrpContext - Used in NtOfsCreateAttributeEx().
VolDo - A pointer to the volumn device object.
PContext - Not used by EFS.
PContextLength - Not used by EFS.
Return Value:
Result of the operation. File system should fail the CREATE IRP if fail code returned.
--*/ {
NTSTATUS ntStatus = STATUS_SUCCESS; PEFS_CONTEXT pEFSContext; ULONG efsLength; PVOID efsStreamData; ULONG information = 0; IN PFILE_OBJECT fileObject = IrpSp->FileObject; /*
PIO_SECURITY_CONTEXT sContext; sContext = IrpSp->Parameters.Create.SecurityContext; DbgPrint( "\n Create: Desired Access %x\n", sContext->DesiredAccess ); DbgPrint( "\n Create: Original Desired Access %x\n", sContext->AccessState->OriginalDesiredAccess ); DbgPrint( "\n Create: PrevGrant Access %x\n", sContext->AccessState->PreviouslyGrantedAccess ); DbgPrint( "\n Create: Remaining Desired Access %x\n", sContext->AccessState->RemainingDesiredAccess ); */ PAGED_CODE();
//
// If read/write data is not required, we will always succeed the call.
// Treadted as plain text file. No encryption/decryption will be involved.
//
CheckValidKeyBlock(*PContext,"Please contact RobertG if you see this. EfsOpenFile() in.\n"); #if DBG
if ( (EFSTRACEALL | EFSTRACELIGHT ) & EFSDebug ){ DbgPrint( "\n EFSFILTER: ****** EFS RTL CREATE ****** \n" ); DbgPrint( "EFSFILTER: FileDir %x\n", FileDirFlag ); DbgPrint( "EFSFILTER: Access %x\n", IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess ); } #endif
if ( FALSE == EfsData.EfsInitialized ){
//
// Not initialized yet.
//
return STATUS_INVALID_DEVICE_REQUEST; }
if ( (IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_SYSTEM) && ( FileDirFlag & (FILE_NEW | DIRECTORY_NEW) )){
//
// Do not encrypt SYSTEM File if creating new file
//
return STATUS_SUCCESS; }
if ( (IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && ((FileDirFlag & EXISTING_FILE_ENCRYPTED) == 0) && ((FileDirFlag & (FILE_NEW | DIRECTORY_NEW) ) == 0)){
//
// Do not encrypt a stream if the file is not encrypted
//
return STATUS_SUCCESS; }
if ( (FileDirFlag & (FILE_EXISTING | DIRECTORY_EXISTING)) && !( FileDirFlag & STREAM_NEW ) && !( IrpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess & ( FILE_APPEND_DATA | FILE_READ_DATA | FILE_WRITE_DATA | FILE_EXECUTE )) ) {
return STATUS_SUCCESS;
}
//
// Allocate the EFS context block
//
*PCreateContext = (PEFS_CONTEXT)ExAllocateFromNPagedLookasideList(&(EfsData.EfsContextPool)); if ( NULL == *PCreateContext){ return STATUS_INSUFFICIENT_RESOURCES; } pEFSContext = (PEFS_CONTEXT)*PCreateContext;
//
// Set initial status value and initialize the event
//
RtlZeroMemory( pEFSContext, sizeof( EFS_CONTEXT ) ); pEFSContext->Status = NO_FURTHER_PROCESSING; pEFSContext->Flags = SystemState; KeInitializeEvent(&( pEFSContext->FinishEvent ), SynchronizationEvent, FALSE);
switch (FileDirFlag & FILE_DIR_TYPE) {
case FILE_EXISTING:
//
// An existing file. Either a new stream created or
// an existing stream opened
// The user must be verified.
// Trying to open $EFS on the file.
//
#if DBG
if ( (EFSTRACEALL | EFSTRACELIGHT ) & EFSDebug ){ DbgPrint( " EFSFILTER: ****** File Existed ****** \n" ); } #endif
try{
ntStatus = EfsReadEfsData( FileHdl, IrpContext, &efsStreamData, &efsLength, &information ); } finally { if (AbnormalTermination()) { ExFreeToNPagedLookasideList(&(EfsData.EfsContextPool), pEFSContext ); *PCreateContext = NULL; } }
if ( EFS_READ_SUCCESSFUL == information ){
#if DBG
if ( (EFSTRACEALL | EFSTRACELIGHT ) & EFSDebug ){
DbgPrint( " EFSFILTER: ****** $EFS Existed ****** \n" );
} #endif
//
// Check if multi-stream.
//
if ( PfileKeyContext && SkipCheckStream(IrpSp, efsStreamData)) {
//
// Skip calling the user mode code
//
ExFreePool(efsStreamData); efsStreamData = NULL;
if ( NULL == *PContext ) { *PContext = GetKeyBlobBuffer(((PKEY_BLOB)PfileKeyContext)->AlgorithmID); if (*PContext) {
*PContextLength = ((PKEY_BLOB) *PContext)->KeyLength; RtlCopyMemory( *PContext, PfileKeyContext, ((PKEY_BLOB)PfileKeyContext)->KeyLength );
} else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES; ExFreeToNPagedLookasideList(&(EfsData.EfsContextPool), pEFSContext ); *PCreateContext = NULL;
} }
if (*PContext) { if ( FileDirFlag & STREAM_NEW ){
//
// New stream, we need to turn on the bit
//
#if DBG
if ( EFSTRACEALL & EFSDebug ){ DbgPrint("Cache New Named String\n"); } #endif
pEFSContext->Status = TURN_ON_ENCRYPTION_BIT | TURN_ON_BIT_ONLY | NO_OPEN_CACHE_CHECK;
} else {
//
// Open existing stream, no further actions required.
//
#if DBG
if ( EFSTRACEALL & EFSDebug ){ DbgPrint("Cache Existing Named String\n"); } #endif
ExFreeToNPagedLookasideList(&(EfsData.EfsContextPool), pEFSContext ); *PCreateContext = NULL; ntStatus = STATUS_SUCCESS; } }
} else {
//
// Set the pointers in context block
//
pEFSContext->EfsStreamData = efsStreamData; pEFSContext->Status = VERIFY_USER_REQUIRED;
if ( NULL == *PContext ) {
//
// Do not check open cache. We need the key blob.
//
pEFSContext->Status |= NO_OPEN_CACHE_CHECK; }
if ( FileDirFlag & STREAM_NEW ) { #if DBG
if ( (EFSTRACEALL | EFSTRACELIGHT ) & EFSDebug ){ DbgPrint( " EFSFILTER: ****** File Existed & Stream New ****** \n" ); } #endif
pEFSContext->Status |= TURN_ON_ENCRYPTION_BIT; } }
}
//
// If EFS_READ_SUCCESSFUL != information
// ntStatus might still be STATUS_SUCCESS which means it is not
// encrypted by EFS and we succeeded call.
// Should we fail the call?
//
break;
case FILE_NEW:
//
// A new file created
// New FEK, DDF, DRF needed
// Trying to open $EFS on the parent directory
//
#if DBG
if ( (EFSTRACEALL | EFSTRACELIGHT ) & EFSDebug ){ DbgPrint( " EFSFILTER: ****** File New ****** \n" ); } #endif
try { ntStatus = EfsReadEfsData( ParentDir, IrpContext, &efsStreamData, &efsLength, &information ); } finally { if (AbnormalTermination()) { ExFreeToNPagedLookasideList(&(EfsData.EfsContextPool), pEFSContext ); *PCreateContext = NULL; } }
if ( EFS_READ_SUCCESSFUL == information ){
#if DBG
if ( (EFSTRACEALL | EFSTRACELIGHT ) & EFSDebug ){
DbgPrint( " EFSFILTER: ****** Parent $EFS Existed ****** \n" );
} #endif
//
// Set the pointers in context block
//
pEFSContext->EfsStreamData = efsStreamData; pEFSContext->Status = NEW_FILE_EFS_REQUIRED | TURN_ON_ENCRYPTION_BIT | NO_OPEN_CACHE_CHECK;
} else if ( OPEN_EFS_FAIL == information ) { pEFSContext->EfsStreamData = NULL; pEFSContext->Status = NEW_FILE_EFS_REQUIRED | TURN_ON_ENCRYPTION_BIT | NO_OPEN_CACHE_CHECK; ntStatus = STATUS_SUCCESS; }
//
// If EFS_READ_SUCCESSFUL != information
// ntStatus might still be STATUS_SUCCESS which means it is not
// encrypted by EFS and we succeeded call.
// Should we fail the call?
//
break;
case DIRECTORY_NEW:
#if DBG
if ( (EFSTRACEALL | EFSTRACELIGHT ) & EFSDebug ){ DbgPrint( " EFSFILTER: ****** Directory New ****** \n" ); } #endif
//
// A new directory created
// New Public keys needed
// Trying to open $EFS on the parent directory
//
try {
ntStatus = EfsReadEfsData( ParentDir, IrpContext, &efsStreamData, &efsLength, &information );
} finally { if (AbnormalTermination()) { ExFreeToNPagedLookasideList(&(EfsData.EfsContextPool), pEFSContext ); *PCreateContext = NULL; } }
if ( EFS_READ_SUCCESSFUL == information ){
#if DBG
if ( (EFSTRACEALL | EFSTRACELIGHT ) & EFSDebug ){ DbgPrint( " EFSFILTER: ****** Parent $EFS Existed ****** \n" ); } #endif
//
// Set the pointers in context block
//
pEFSContext->EfsStreamData = efsStreamData; pEFSContext->Status = NEW_DIR_EFS_REQUIRED | TURN_ON_ENCRYPTION_BIT | NO_OPEN_CACHE_CHECK;
} else if ( OPEN_EFS_FAIL == information ) { pEFSContext->EfsStreamData = NULL; pEFSContext->Status = NEW_DIR_EFS_REQUIRED | TURN_ON_ENCRYPTION_BIT | NO_OPEN_CACHE_CHECK; ntStatus = STATUS_SUCCESS; }
//
// If EFS_READ_SUCCESSFUL != information
// ntStatus might still be STATUS_SUCCESS which means it is not
// encrypted by EFS and we succeeded call.
// Should we fail the call?
//
break;
case DIRECTORY_EXISTING:
#if DBG
if ( EFSTRACEALL & EFSDebug ){ DbgPrint( " EFSFILTER: ****** Directory Existed ****** \n" ); } #endif
//
// An existing directory. Either a new stream created or
// an existing stream opened
// We do not encrypt data stream for Directory. Ignore this.
//
default:
break;
}
CheckValidKeyBlock(*PContext,"Please contact RobertG if you see this. EfsOpenFile() Out.\n"); return ntStatus; }
NTSTATUS EfsFileControl( IN PVOID PInputBuffer, IN ULONG InputDataLength, OUT PVOID POutputBuffer OPTIONAL, IN OUT PULONG POutputBufferLength, IN ULONG EncryptionFlag, IN ULONG AccessFlag, IN ULONG SystemState, IN ULONG FsControlCode, IN OBJECT_HANDLE FileHdl, IN PIRP_CONTEXT IrpContext, IN PDEVICE_OBJECT VolDo, IN ATTRIBUTE_HANDLE StreamHdl, IN OUT PVOID *PContext, IN OUT PULONG PContextLength ) /*++
Routine Description:
This is a call back routine. It will be called back by file system to support EFS's FSCTL APIs
Arguments:
PInputBuffer - Pointer to the input data buffer. The first 4 bytes are for information to Ntfs or some other drivers only. The EFS related data are encrypted in the following bytes. The first 4 encrypted bytes are subfunction code in the form of EFS_XXX. General package looks like this, Subcode plain text, EFS subfunction code, EFS subcode cipher text, FSCTL specific data.
InputDataLength - The length of the input data buffer.
POutputBuffer - Pointer to the output data buffer.
POutputBufferLength - The length of the output data.
EncryptionFlag - Indicating if this stream is encrypted or not.
AccessFlag - Indicating the desired access when the stream is opened.
FsControlCode - Indicating what FSCTL was originally called.
FileHdl - Used to access the $EFS.
IrpContext - Irp context used to call NtOfsCreateAttributeEx().
VolDo - A pointer to the volumn device object.
StreamHdl - Stream to be worked on.
PContext - BLOB(key) for READ or WRITE later.
PContextLength - The length of the context.
Return Value:
STATUS_SUCCESS for successful operation.
--*/ {
ULONG functionCode; ULONG bytesSame; ULONG efsLength; ULONG workOffset; ULONG information=0; ULONG dataFlushLength = FIELD_OFFSET(GENERAL_FS_DATA, EfsData[0]); PUCHAR pCmdContext = NULL; PVOID efsStreamData = NULL; NTSTATUS ntStatus; ATTRIBUTE_HANDLE attribute; BOOLEAN verifyInput;
PAGED_CODE();
CheckValidKeyBlock(*PContext,"Please contact RobertG if you see this. EfsFileControl() in.\n"); #if DBG
if ( (EFSTRACEALL | EFSTRACELIGHT ) & EFSDebug ){ DbgPrint( "\n EFSFILTER: ****** EFS RTL FSCTL ****** \n" ); } #endif
if ( (NULL == PInputBuffer) || ( FALSE == EfsData.EfsInitialized )){ return STATUS_INVALID_DEVICE_REQUEST; }
//
// Input data is encrypted by DES with sessionKey.
// As long as we do not change the algorithm for the input data
// We need guarantee the data length is in multiple of DES block size.
// The first four bytes is always in plain text intended to hold the data
// the NTFS is interested in.
// The general format of input data is,
// Sub-code plain text, [FsCode, Sub-code cipher text, [FsData]]sk
//
if ((InputDataLength < (ULONG)(FIELD_OFFSET(FSCTL_INPUT, EfsFsData[0]) + FIELD_OFFSET(GENERAL_FS_DATA, EfsData[0]))) || ((( InputDataLength - sizeof( ULONG )) % DES_BLOCKLEN ) != 0)) { return STATUS_INVALID_DEVICE_REQUEST; }
pCmdContext = ExAllocatePoolWithTag( PagedPool, InputDataLength, 'csfE' );
if ( NULL == pCmdContext ){
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Decrypt FSCTL input buffer. No CBC is used.
//
try { RtlCopyMemory( pCmdContext, PInputBuffer, InputDataLength ); } except (EXCEPTION_EXECUTE_HANDLER) { ntStatus = GetExceptionCode(); ExFreePool( pCmdContext ); if (FsRtlIsNtstatusExpected( ntStatus)) { return ntStatus; } else { return STATUS_INVALID_USER_BUFFER; } }
workOffset = sizeof( ULONG ); while ( workOffset < InputDataLength ){
des( pCmdContext + workOffset, pCmdContext + workOffset, &(EfsData.SessionDesTable[0]), DECRYPT );
workOffset += DES_BLOCKLEN; }
functionCode = ((PFSCTL_INPUT)pCmdContext)->EfsFsCode;
#if DBG
if ( (EFSTRACEALL | EFSTRACELIGHT ) & EFSDebug ){ DbgPrint( "\n EFSFILTER: EFS RTL FSCTL=%x \n", functionCode); } #endif
//
// Check the codes match for set encrypt and decrypt to guard the integrity
// of the encryption status. The NTFS is going to set/clear the bits. We really
// want to make sure the FSCTL is issued by the right module.
//
if ( FSCTL_SET_ENCRYPTION == FsControlCode){ if (SystemState & SYSTEM_IS_READONLY) {
//
// This could be issued from right components
//
RtlSecureZeroMemory(&(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), dataFlushLength); ExFreePool( pCmdContext ); return STATUS_MEDIA_WRITE_PROTECTED; } if ( EFS_SET_ENCRYPT == functionCode ){ if ( ((PFSCTL_INPUT)pCmdContext)->PlainSubCode != (((PFSCTL_INPUT)pCmdContext)->CipherSubCode & ~EFS_FSCTL_ON_DIR ) ){
ExFreePool( pCmdContext ); return STATUS_INVALID_DEVICE_REQUEST;
} } else if ( (EFS_SET_ATTRIBUTE != functionCode) && (EFS_OVERWRITE_ATTRIBUTE != functionCode) ){
ExFreePool( pCmdContext ); return STATUS_INVALID_DEVICE_REQUEST;
} }
switch ( functionCode ){
case EFS_SET_ATTRIBUTE:
//
// Write $EFS and/or set key Blob
// subCode is a bit mask for the combination of write $EFS and set blob
// [FsData] = FEK, [FEK]sk, [$EFS]
// FEK == sessionKey when set key Blob is not required
//
// We cannot check access rights here. This call will be made if the
// user creates a new file and without any access requirement. We
// still want to setup FEK inside this call.
//
if ( !EfsVerifyKeyFsData( &(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), InputDataLength) ){
//
// Input data format error. Could be issued from attacker.
//
ExFreePool( pCmdContext ); return STATUS_INVALID_PARAMETER;
}
try { ntStatus = SetEfsData( pCmdContext, InputDataLength, SystemState, FileHdl, IrpContext, PContext, PContextLength ); } finally {
dataFlushLength = 2 * (EFS_KEY_SIZE((PEFS_KEY) &(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]))); RtlSecureZeroMemory(&(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), dataFlushLength); ExFreePool( pCmdContext );
} CheckValidKeyBlock(*PContext,"Please contact RobertG if you see this. EfsFileControl() Out 1.\n"); return ntStatus;
case EFS_SET_ENCRYPT:
if ( !( AccessFlag & ( READ_DATA_ACCESS | WRITE_DATA_ACCESS ))){
//
// Check access flag. Could be called by an attacker.
//
ExFreePool( pCmdContext ); return STATUS_ACCESS_DENIED;
}
try { ntStatus = EfsSetEncrypt( pCmdContext, InputDataLength, EncryptionFlag, FileHdl, IrpContext, PContext, PContextLength ); } finally {
//
// Memory should have been zeroed in EfsSetEncrypt.
//
ExFreePool( pCmdContext ); }
CheckValidKeyBlock(*PContext,"Please contact RobertG if you see this. EfsFileControl() Out 2.\n"); return ntStatus;
case EFS_GET_ATTRIBUTE:
//
// Provide read access to $EFS for EFS service
// Verify the input data format first.
//
try { if ( (NULL == POutputBuffer) || (*POutputBufferLength < sizeof(ULONG)) || !EfsVerifyGeneralFsData( &(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), InputDataLength)){
ExFreePool( pCmdContext ); return STATUS_INVALID_PARAMETER;
} } except(EXCEPTION_EXECUTE_HANDLER) { ntStatus = GetExceptionCode(); ExFreePool( pCmdContext ); if (FsRtlIsNtstatusExpected( ntStatus)) { return ntStatus; } else { return STATUS_INVALID_USER_BUFFER; } }
if ( !(EncryptionFlag & STREAM_ENCRYPTED) ){
RtlSecureZeroMemory(&(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), dataFlushLength); ExFreePool( pCmdContext ); return STATUS_INVALID_DEVICE_REQUEST;
}
//
// Try to read an existing $EFS
//
try { ntStatus = EfsReadEfsData( FileHdl, IrpContext, &efsStreamData, &efsLength, &information ); } finally {
//
// Zero pCmdContext. This is not needed if the the input format data
// is not good as in the above error cases, which means some is trying
// to attack us.
//
RtlSecureZeroMemory(&(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), dataFlushLength); ExFreePool( pCmdContext ); pCmdContext = NULL;
}
if ( EFS_READ_SUCCESSFUL == information ){
//
// Everything is OK. We do not check user ID here,
// we suppose that has been checked by the service.
//
try { ntStatus = STATUS_SUCCESS; if ( efsLength > *POutputBufferLength ) {
* (ULONG *) POutputBuffer = efsLength; *POutputBufferLength = sizeof(ULONG); ExFreePool( efsStreamData ); return STATUS_BUFFER_TOO_SMALL;
}
RtlCopyMemory(POutputBuffer, efsStreamData, efsLength); *POutputBufferLength = efsLength; } except (EXCEPTION_EXECUTE_HANDLER) { ntStatus = GetExceptionCode(); if (!FsRtlIsNtstatusExpected( ntStatus)) { ntStatus = STATUS_INVALID_USER_BUFFER; } }
ExFreePool( efsStreamData ); return ntStatus;
} else if ( ( OPEN_EFS_FAIL == information ) || ( EFS_FORMAT_ERROR == information ) ) {
//
// EFS does not exist or not encrypted by the EFS ?
//
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
}
//
// Other error while opening $EFS
//
return ntStatus;
case EFS_DEL_ATTRIBUTE:
if (SystemState & SYSTEM_IS_READONLY) { RtlSecureZeroMemory(&(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), dataFlushLength); ExFreePool( pCmdContext ); return STATUS_MEDIA_WRITE_PROTECTED; } if ( !( AccessFlag & WRITE_DATA_ACCESS )){
//
// Check access flag
//
RtlSecureZeroMemory(&(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), dataFlushLength); ExFreePool( pCmdContext ); return STATUS_ACCESS_DENIED;
}
//
// Delete $EFS after all the stream has been decrypted.
//
if ( EncryptionFlag ){
//
// Stream has not been decrypted
//
RtlSecureZeroMemory(&(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), dataFlushLength); ExFreePool( pCmdContext ); return STATUS_INVALID_DEVICE_REQUEST;
}
//
// [FsData] = SessionKey, Handle, Handle, [SessionKey, Handle, Handle]sk
// Verify the FsData format.
//
if ( !EfsVerifyGeneralFsData( &(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), InputDataLength) ){
//
// Input data format error. No need to zero attacker provided pCmdContext block.
//
ExFreePool( pCmdContext ); return STATUS_INVALID_PARAMETER;
}
//
// Delete the $EFS stream
//
try { ntStatus = EfsDeleteEfsData( FileHdl, IrpContext ); } finally { RtlSecureZeroMemory(&(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), dataFlushLength); ExFreePool( pCmdContext ); }
return ntStatus;
case EFS_ENCRYPT_DONE:
//
// Change the transition state of $EFS to normal state
// Fall through intended.
//
#if DBG
if ( EFSTRACEALL & EFSDebug ){ DbgPrint( "\n EFSFILTER: Encryption Done %x\n", functionCode ); } #endif
case EFS_DECRYPT_BEGIN:
if (SystemState & SYSTEM_IS_READONLY) { ExFreePool( pCmdContext ); return STATUS_MEDIA_WRITE_PROTECTED; } if ( !( AccessFlag & WRITE_DATA_ACCESS )){
//
// Check access flag
//
ExFreePool( pCmdContext ); return STATUS_ACCESS_DENIED;
}
//
// Mark the transition state of $EFS
//
try { ntStatus = EfsModifyEfsState( functionCode, pCmdContext, InputDataLength, FileHdl, IrpContext ); } finally {
RtlSecureZeroMemory(&(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), dataFlushLength); ExFreePool( pCmdContext );
}
return ntStatus;
case EFS_OVERWRITE_ATTRIBUTE:
if ( ((PFSCTL_INPUT)pCmdContext)->CipherSubCode & SET_EFS_KEYBLOB ){
//
// FEK, [FEK]sk, [$EFS]
//
dataFlushLength = 2 * (EFS_KEY_SIZE((PEFS_KEY) &(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0])));
} else {
//
// SessionKey, Handle, Handle, [SessionKey, Handle, Handle]sk
//
dataFlushLength = FIELD_OFFSET(GENERAL_FS_DATA, EfsData[0]);
}
if ( !( AccessFlag & ( WRITE_DATA_ACCESS | RESTORE_ACCESS ))){
//
// Check access flag
//
RtlSecureZeroMemory(&(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), dataFlushLength); ExFreePool( pCmdContext ); return STATUS_ACCESS_DENIED;
}
//
// Mostly used in import
// Overwrite $EFS and/or set key Blob
// subCode is a bit mask for the combination of write $EFS and set blob
//
if ( ((PFSCTL_INPUT)pCmdContext)->CipherSubCode & SET_EFS_KEYBLOB ){
verifyInput = EfsVerifyKeyFsData( &(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), InputDataLength );
} else {
verifyInput = EfsVerifyGeneralFsData( &(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), InputDataLength );
}
if ( !verifyInput ){
//
// Input data format error. No need to zero attacker provided memory.
//
ExFreePool( pCmdContext ); return STATUS_INVALID_PARAMETER;
}
try { ntStatus = SetEfsData( pCmdContext, InputDataLength, SystemState, FileHdl, IrpContext, PContext, PContextLength ); } finally {
RtlSecureZeroMemory(&(((PFSCTL_INPUT)pCmdContext)->EfsFsData[0]), dataFlushLength); ExFreePool( pCmdContext );
} CheckValidKeyBlock(*PContext,"Please contact RobertG if you see this. EfsFileControl() Out 3.\n"); return ntStatus;
default: // ASSERT (FALSE);
ExFreePool( pCmdContext ); return STATUS_INVALID_DEVICE_REQUEST; } CheckValidKeyBlock(*PContext,"Please contact RobertG if you see this. EfsFileControl() Out 4.\n"); }
NTSTATUS EfsRead( IN OUT PUCHAR InOutBuffer, IN PLARGE_INTEGER Offset, IN ULONG BufferSize, IN PVOID Context ) /*++
Routine Description:
This is a call back routine. It will be called back by file system and decrypt the data in the buffer provided by the file system.
Arguments:
InOutBuffer - Pointer to the data block to be decrypted.
Offset - Pointer to the offset of the block in the file. Relative to the beginning of the file.
BufferSize - Length of the data block.
Context - Information needed to decrypt the file. Passed to the file system on EfsOpenFile()
Return Value:
This routine will not cause error. Unless the memory passed in is not valid. In that case, memory flush will occur.
--*/ { ULONGLONG chainBlockIV[2]; PUCHAR pWorkBuffer = InOutBuffer; EfsDecFunc pDecryptFunc;
PAGED_CODE();
#if DBG
if ( EFSTRACEALL & EFSDebug ){ DbgPrint( "\n EFSFILTER: READ Bytes = %x, Offset = %x\n", BufferSize, Offset->QuadPart); } #endif
//
// Data length should be in multiple of the chunk (512 Bytes)
// Data offset (relative to the begining of the stream) should
// Start at chunk boundary
//
CheckValidKeyBlock(Context,"Please contact RobertG if you see this. EfsRead() in.\n"); ASSERT (BufferSize % CHUNK_SIZE == 0); ASSERT (Offset->QuadPart % CHUNK_SIZE == 0);
switch (((PKEY_BLOB)Context)->AlgorithmID){
case CALG_3DES: chainBlockIV[0] = Offset->QuadPart + EFS_IV; pDecryptFunc = EFSDes3Dec; break; case CALG_DESX: chainBlockIV[0] = Offset->QuadPart + EFS_IV; pDecryptFunc = EFSDesXDec; break; case CALG_AES_256: chainBlockIV[0] = Offset->QuadPart + EFS_AES_IVL; chainBlockIV[1] = Offset->QuadPart + EFS_AES_IVH; pDecryptFunc = EFSAesDec; break; case CALG_DES: default: chainBlockIV[0] = Offset->QuadPart + EFS_IV; pDecryptFunc = EFSDesDec; break; }
while ( BufferSize > 0 ){
pDecryptFunc(pWorkBuffer, (PUCHAR) &chainBlockIV[0], (PKEY_BLOB) Context, CHUNK_SIZE );
pWorkBuffer += CHUNK_SIZE; chainBlockIV[0] += CHUNK_SIZE; if (((PKEY_BLOB)Context)->AlgorithmID == CALG_AES_256) { chainBlockIV[1] += CHUNK_SIZE; } BufferSize -= CHUNK_SIZE; }
CheckValidKeyBlock(Context,"Please contact RobertG if you see this. EfsRead() out.\n"); return ( STATUS_SUCCESS ); }
NTSTATUS EfsWrite( IN PUCHAR InBuffer, OUT PUCHAR OutBuffer, IN PLARGE_INTEGER Offset, IN ULONG BufferSize, IN PUCHAR Context ) /*++
Routine Description:
This is a call back routine. It will be called back by file system and encrypt the data in the buffer provided by the file system.
Note: The input data buffer can only be touched once.
Arguments:
InBuffer - Pointer to the data block to be encrypted.
OutBuffer - Pointer to the data buffer to hold the encrypted data.
Offset - Pointer to the offset of the block in the file. Relative to the beginning of the file.
BufferSize - Length of the data block.
Context - Information needed to decrypt the file. Passed to the file system on EfsOpenFile()
Return Value:
This routine will not cause error. Unless the memory passed in is not valid. In that case, memory flush will occur.
--*/ { ULONGLONG chainBlockIV[2]; PUCHAR pWorkInBuffer = InBuffer; PUCHAR pWorkOutBuffer = OutBuffer; EfsEncFunc pEncryptFunc;
PAGED_CODE();
//
// Data length should be in multiple of the chunk (512 Bytes)
// Data offset (relative to the begining of the stream) should
// Start at chunk boundary
//
CheckValidKeyBlock(Context,"Please contact RobertG if you see this. EfsWrite() in.\n"); ASSERT (BufferSize % CHUNK_SIZE == 0); ASSERT (Offset->QuadPart % CHUNK_SIZE == 0);
#if DBG
if ( EFSTRACEALL & EFSDebug ){ DbgPrint( "\n EFSFILTER: WRITE Bytes = %x, Offset = %x\n", BufferSize, Offset->QuadPart); } #endif
switch (((PKEY_BLOB)Context)->AlgorithmID){ case CALG_3DES: chainBlockIV[0] = Offset->QuadPart + EFS_IV; pEncryptFunc = EFSDes3Enc; break; case CALG_DESX: chainBlockIV[0] = Offset->QuadPart + EFS_IV; pEncryptFunc = EFSDesXEnc; break; case CALG_AES_256: chainBlockIV[0] = Offset->QuadPart + EFS_AES_IVL; chainBlockIV[1] = Offset->QuadPart + EFS_AES_IVH; pEncryptFunc = EFSAesEnc; break; case CALG_DES: default: chainBlockIV[0] = Offset->QuadPart + EFS_IV; pEncryptFunc = EFSDesEnc; break; }
while ( BufferSize > 0 ){ pEncryptFunc(pWorkInBuffer, pWorkOutBuffer, (PUCHAR) &chainBlockIV, (PKEY_BLOB)Context, CHUNK_SIZE );
pWorkInBuffer += CHUNK_SIZE; pWorkOutBuffer += CHUNK_SIZE; chainBlockIV[0] += CHUNK_SIZE; if (((PKEY_BLOB)Context)->AlgorithmID == CALG_AES_256) { chainBlockIV[1] += CHUNK_SIZE; } BufferSize -= CHUNK_SIZE; } CheckValidKeyBlock(Context,"Please contact RobertG if you see this. EfsWrite() out.\n"); return STATUS_SUCCESS; }
VOID EfsFreeContext( IN OUT PVOID *PContext ) /*++
Routine Description:
This is a call back routine. It will be called back by file system to free the context block.
Arguments:
PContext - Context block to be freed.
Return Value:
This routine will not cause error. Unless the memory passed in is not valid.
--*/ { PAGED_CODE();
#if DBG
if ( EFSTRACEALL & EFSDebug ){ DbgPrint( " EFSFILTER: ****** Free Key ****** \n" ); } #endif
CheckValidKeyBlock(*PContext,"Please contact RobertG if you see this. EfsFreeContext() in.\n"); if (*PContext){ FreeMemoryBlock(PContext); }
}
NTSTATUS EfsMountVolumn( IN PDEVICE_OBJECT VolDo, IN PDEVICE_OBJECT RealDevice ) /*++
Routine Description:
This is a call back routine. It will be called back by file system when a volumn needs to be attached
Arguments:
VolDo - Volume device object RealDevice - Volume real device object
Return Value:
The status of operation.
--*/ { PDEVICE_OBJECT fsfDeviceObject; PDEVICE_OBJECT deviceObject; NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
#if DBG
if ( EFSTRACEALL & EFSDebug ){ DbgPrint( "\n *****EFSFILTER: RTL mount.***** \n" ); } #endif
return STATUS_SUCCESS;
}
VOID EfsDismountVolumn( IN PDEVICE_OBJECT VolDo ) /*++
Routine Description:
This is a call back routine. It will be called back by file system when a volumn is dismounted
Arguments:
VolDo - volumn's device object.
Return Value:
No return value.
--*/ { PAGED_CODE();
#if DBG
if ( EFSTRACEALL & EFSDebug ){
DbgPrint( "EFSFILTER: Dismount callback. \n" );
}
#endif
}
|