windows-nt/Source/XPSP1/NT/base/fs/efs/efsrtl.c

1415 lines
38 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
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;
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 < sizeof(FSCTL_INPUT)) || ((( 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) {
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
//
ExFreePool( pCmdContext );
return STATUS_INVALID_PARAMETER;
}
try {
ntStatus = SetEfsData(
pCmdContext,
InputDataLength,
SystemState,
FileHdl,
IrpContext,
PContext,
PContextLength
);
} finally {
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
//
ExFreePool( pCmdContext );
return STATUS_ACCESS_DENIED;
}
try {
ntStatus = EfsSetEncrypt(
pCmdContext,
InputDataLength,
EncryptionFlag,
FileHdl,
IrpContext,
PContext,
PContextLength
);
} finally {
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) ){
ExFreePool( pCmdContext );
return STATUS_INVALID_DEVICE_REQUEST;
}
//
// Try to read an existing $EFS
//
try {
ntStatus = EfsReadEfsData(
FileHdl,
IrpContext,
&efsStreamData,
&efsLength,
&information
);
} finally {
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) {
ExFreePool( pCmdContext );
return STATUS_MEDIA_WRITE_PROTECTED;
}
if ( !( AccessFlag & WRITE_DATA_ACCESS )){
//
// Check access flag
//
ExFreePool( pCmdContext );
return STATUS_ACCESS_DENIED;
}
//
// Delete $EFS after all the stream has been decrypted.
//
if ( EncryptionFlag ){
//
// Stream has not been decrypted
//
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
//
ExFreePool( pCmdContext );
return STATUS_INVALID_PARAMETER;
}
//
// Delete the $EFS stream
//
try {
ntStatus = EfsDeleteEfsData( FileHdl, IrpContext );
} finally {
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 {
ExFreePool( pCmdContext );
}
return ntStatus;
case EFS_OVERWRITE_ATTRIBUTE:
if ( !( AccessFlag &
( WRITE_DATA_ACCESS |
RESTORE_ACCESS ))){
//
// Check access flag
//
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
//
ExFreePool( pCmdContext );
return STATUS_INVALID_PARAMETER;
}
try {
ntStatus = SetEfsData(
pCmdContext,
InputDataLength,
SystemState,
FileHdl,
IrpContext,
PContext,
PContextLength
);
} finally {
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
}