/*++ 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 }