/*++ Copyright (c) 1996 Microsoft Corporation Module Name: efs.c Abstract: This module contains the code that implements the EFS file system filter driver. Author: Robert Gu (robertg) 29-Oct-1996 Environment: Kernel mode Revision History: --*/ #include "efs.h" #include "efsrtl.h" #define BUFFER_SIZE 1024 #define BUFFER_REG_VAL L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\NTFS\\EFS\\Parameters" #define MAX_ALLOC_BUFFER L"MaximumBlob" #define EFS_KERNEL_CACHE_PERIOD L"EFSKCACHEPERIOD" // // Global storage for this file system filter driver. // EFS_DATA EfsData; WORK_QUEUE_ITEM EfsShutdownCleanupWorkItem; // // $EFS stream name // WCHAR AttrName[5] = L"$EFS"; #if DBG ULONG EFSDebug = 0; #endif ENCRYPTION_CALL_BACK EFSCallBackTable = { ENCRYPTION_CURRENT_INTERFACE_VERSION, ENCRYPTION_ALL_STREAMS, EfsOpenFile, NULL, EFSFilePostCreate, EfsFileControl, EfsFileControl, EFSFsControl, EfsRead, EfsWrite, EfsFreeContext }; VOID EfspShutdownCleanup( IN PVOID Parameter ); // // Assign text sections for each routine. // #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, EfspShutdownCleanup) #pragma alloc_text(PAGE, EfsInitialization) #pragma alloc_text(PAGE, EfsGetSessionKey) #pragma alloc_text(PAGE, GetKeyBlobLength) #pragma alloc_text(PAGE, GetKeyBlobBuffer) #pragma alloc_text(PAGE, SetKeyTable) #pragma alloc_text(PAGE, EfsInitFips) #endif VOID EfspShutdownCleanup( IN PVOID Parameter ) { PEPROCESS LsaProcess; PAGED_CODE(); UNREFERENCED_PARAMETER(Parameter); if (EfsData.LsaProcess) { LsaProcess = EfsData.LsaProcess; EfsData.LsaProcess = NULL; ObDereferenceObject(LsaProcess); } } NTSTATUS EfsInitialization( void ) /*++ Routine Description: This is the initialization routine for the general purpose file system filter driver. This routine creates the device object that represents this driver in the system and registers it for watching all file systems that register or unregister themselves as active file systems. Arguments: DriverObject - Pointer to driver object created by the system. Return Value: The function value is the final status from the initialization operation. --*/ { UNICODE_STRING nameString; PDEVICE_EXTENSION deviceExtension; PFILE_OBJECT fileObject; NTSTATUS status; HANDLE threadHdl; ULONG i; OBJECT_ATTRIBUTES objAttr; UNICODE_STRING efsInitEventName; UNICODE_STRING efsBufValue; ULONG resultLength; PKEY_VALUE_PARTIAL_INFORMATION pPartialValue = NULL; HANDLE efsKey; EFS_INIT_DATAEXG InitDataFromSrv; PAGED_CODE(); // // Mark our global data record // EfsData.AllocMaxBuffer = FALSE; EfsData.FipsFileObject = NULL; EfsData.FipsFunctionTable.Fips3Des = NULL; EfsData.FipsFunctionTable.Fips3Des3Key = NULL; EfsData.EfsDriverCacheLength = DefaultTimeExpirePeriod; RtlInitUnicodeString( &efsBufValue, BUFFER_REG_VAL ); InitializeObjectAttributes( &objAttr, &efsBufValue, OBJ_CASE_INSENSITIVE, NULL, NULL ); status = ZwOpenKey( &efsKey, KEY_READ, &objAttr); if (NT_SUCCESS(status)) { pPartialValue = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(NonPagedPool, BUFFER_SIZE); if (pPartialValue) { RtlInitUnicodeString(&efsBufValue, MAX_ALLOC_BUFFER); status = ZwQueryValueKey( efsKey, &efsBufValue, KeyValuePartialInformation, (PVOID)pPartialValue, BUFFER_SIZE, &resultLength ); if (NT_SUCCESS(status)) { ASSERT(pPartialValue->Type == REG_DWORD); if (*((PLONG)&(pPartialValue->Data))){ EfsData.AllocMaxBuffer = TRUE; } } RtlInitUnicodeString(&efsBufValue, EFS_KERNEL_CACHE_PERIOD); status = ZwQueryValueKey( efsKey, &efsBufValue, KeyValuePartialInformation, (PVOID)pPartialValue, BUFFER_SIZE, &resultLength ); if (NT_SUCCESS(status)) { ASSERT(pPartialValue->Type == REG_DWORD); if (((*((DWORD *)&(pPartialValue->Data))) >= MINCACHEPERIOD) && ((*((DWORD *)&(pPartialValue->Data))) <= MAXCACHEPERIOD)){ EfsData.EfsDriverCacheLength = *((DWORD *)&(pPartialValue->Data)); EfsData.EfsDriverCacheLength *= 10000000; } } ExFreePool(pPartialValue); } ZwClose(efsKey); } EfsData.NodeTypeCode = EFS_NTC_DATA_HEADER; EfsData.NodeByteSize = sizeof( EFS_DATA ); EfsData.EfsInitialized = FALSE; EfsData.InitEventHandle = NULL; EfsData.LsaProcess = NULL; // // Initialize global data structures. // ExInitializeWorkItem( &EfsShutdownCleanupWorkItem, &EfspShutdownCleanup, NULL ); status = PoQueueShutdownWorkItem( &EfsShutdownCleanupWorkItem ); if (!NT_SUCCESS(status)) { return status; } InitializeListHead( &(EfsData.EfsOpenCacheList) ); InitializeListHead( &(EfsData.EfsKeyLookAsideList) ); ExInitializeFastMutex( &(EfsData.EfsKeyBlobMemSrcMutex) ); ExInitializeFastMutex( &(EfsData.EfsOpenCacheMutex) ); // // Initialize the event lookaside list // ExInitializeNPagedLookasideList(&(EfsData.EfsEventPool), NULL, NULL, 0, sizeof(KEVENT), 'levE', EFS_EVENTDEPTH ); // // Try to allocate at least one event in the list. This one will be used for // sure later. // { PVOID pTryEvent; pTryEvent = ExAllocateFromNPagedLookasideList(&(EfsData.EfsEventPool)); if ( NULL == pTryEvent ){ // // Free previously allocated memory // ExDeleteNPagedLookasideList(&(EfsData.EfsEventPool)); return STATUS_NO_MEMORY; } ExFreeToNPagedLookasideList(&(EfsData.EfsEventPool), pTryEvent); } // // Initialize the context lookaside list // ExInitializeNPagedLookasideList(&(EfsData.EfsContextPool), NULL, NULL, 0, sizeof(EFS_CONTEXT), 'nocE', EFS_CONTEXTDEPTH ); // // Initialize the cache lookaside list // ExInitializePagedLookasideList(&(EfsData.EfsOpenCachePool), NULL, NULL, 0, sizeof(OPEN_CACHE), 'hcoE', EFS_CACHEDEPTH ); ExInitializePagedLookasideList(&(EfsData.EfsMemSourceItem), NULL, NULL, 0, sizeof(KEY_BLOB_RAMPOOL), 'msfE', EFS_ALGDEPTH ); ExInitializeNPagedLookasideList(&(EfsData.EfsLookAside), NULL, NULL, 0, sizeof(NPAGED_LOOKASIDE_LIST), 'msfE', EFS_ALGDEPTH ); status = NtOfsRegisterCallBacks( Encryption, &EFSCallBackTable ); if (!NT_SUCCESS(status)) { // // Register callback failed // ExDeleteNPagedLookasideList(&(EfsData.EfsEventPool)); ExDeleteNPagedLookasideList(&(EfsData.EfsContextPool)); ExDeletePagedLookasideList(&(EfsData.EfsOpenCachePool)); ExDeletePagedLookasideList(&(EfsData.EfsMemSourceItem)); ExDeleteNPagedLookasideList(&(EfsData.EfsLookAside)); return status; } RtlInitUnicodeString(&(EfsData.EfsName), &AttrName[0]); // // Create an event // RtlInitUnicodeString( &efsInitEventName, L"\\EFSInitEvent" ); InitializeObjectAttributes( &objAttr, &efsInitEventName, 0, NULL, NULL ); // // Try to create an event. If the event was not created, the EFS // server is not loaded yet. We will create a thread waiting for // EFS server to be loaded. If the event was already created, we // will just go ahead and get the session key from the EFS server. // status = ZwCreateEvent( &(EfsData.InitEventHandle), EVENT_MODIFY_STATE, &objAttr, NotificationEvent, FALSE ); if (!NT_SUCCESS(status)) { if ( STATUS_OBJECT_NAME_COLLISION == status ){ // // EFS server has been loaded. This is the normal case. // Call server to get the session key. // status = GenerateSessionKey( &InitDataFromSrv ); if (NT_SUCCESS( status )) { // // Set session key // RtlCopyMemory( &(EfsData.SessionKey[0]), InitDataFromSrv.Key, DES_KEYSIZE ); deskey( (DESTable*)&(EfsData.SessionDesTable[0]), &(EfsData.SessionKey[0])); status = PsLookupProcessByProcessId( InitDataFromSrv.LsaProcessID, &(EfsData.LsaProcess) ); if (NT_SUCCESS( status )) { EfsData.EfsInitialized = TRUE; if ( PsGetCurrentProcess() != EfsData.LsaProcess ){ KAPC_STATE ApcState; KeStackAttachProcess ( EfsData.LsaProcess, &ApcState ); InitSecurityInterface(); KeUnstackDetachProcess(&ApcState); } else { InitSecurityInterface(); } EfsInitFips(); } else { #if DBG if ( (EFSTRACEALL | EFSTRACELIGHT) & EFSDebug ){ DbgPrint("PsLookupProcessByProcessId failed, status = %x\n",status); } #endif // // Failed to get the process pointer // ExDeleteNPagedLookasideList(&(EfsData.EfsEventPool)); ExDeleteNPagedLookasideList(&(EfsData.EfsContextPool)); ExDeletePagedLookasideList(&(EfsData.EfsOpenCachePool)); ExDeletePagedLookasideList(&(EfsData.EfsMemSourceItem)); ExDeleteNPagedLookasideList(&(EfsData.EfsLookAside)); } } else { #if DBG if ( (EFSTRACEALL | EFSTRACELIGHT) & EFSDebug ){ DbgPrint("GenerateSessionKey failed, status = %x\n",status); } #endif // // Failed to get the session key // ExDeleteNPagedLookasideList(&(EfsData.EfsEventPool)); ExDeleteNPagedLookasideList(&(EfsData.EfsContextPool)); ExDeletePagedLookasideList(&(EfsData.EfsOpenCachePool)); ExDeletePagedLookasideList(&(EfsData.EfsMemSourceItem)); ExDeleteNPagedLookasideList(&(EfsData.EfsLookAside)); } } else { // // Unexpected error occured. EFS cannot be loaded // #if DBG if ( (EFSTRACEALL | EFSTRACELIGHT ) & EFSDebug ){ DbgPrint("EFSFILTER: Efs init event creation failed.%x\n", status); } #endif ExDeleteNPagedLookasideList(&(EfsData.EfsEventPool)); ExDeleteNPagedLookasideList(&(EfsData.EfsContextPool)); ExDeletePagedLookasideList(&(EfsData.EfsOpenCachePool)); ExDeletePagedLookasideList(&(EfsData.EfsMemSourceItem)); ExDeleteNPagedLookasideList(&(EfsData.EfsLookAside)); } } else { // // The server is not ready yet. // Create a thread and wait for the server in that thread // status = PsCreateSystemThread( &threadHdl, GENERIC_ALL, NULL, NULL, NULL, EfsGetSessionKey, NULL ); if ( NT_SUCCESS( status ) ){ ZwClose( threadHdl ); } else { ExDeleteNPagedLookasideList(&(EfsData.EfsEventPool)); ExDeleteNPagedLookasideList(&(EfsData.EfsContextPool)); ExDeletePagedLookasideList(&(EfsData.EfsOpenCachePool)); ExDeletePagedLookasideList(&(EfsData.EfsMemSourceItem)); ExDeleteNPagedLookasideList(&(EfsData.EfsLookAside)); } } return status; } VOID EfsUninitialization( VOID ) { PLIST_ENTRY pLink; PKEY_BLOB_RAMPOOL pTmpItem; PNPAGED_LOOKASIDE_LIST MemSrcList; while (!IsListEmpty (&EfsData.EfsKeyLookAsideList)) { pLink = RemoveHeadList (&EfsData.EfsKeyLookAsideList); pTmpItem = CONTAINING_RECORD(pLink, KEY_BLOB_RAMPOOL, MemSourceChain); MemSrcList = pTmpItem->MemSourceList; ExDeleteNPagedLookasideList(MemSrcList); ExFreeToNPagedLookasideList(&(EfsData.EfsLookAside), MemSrcList ); ExFreeToPagedLookasideList(&(EfsData.EfsMemSourceItem), pTmpItem ); } ExDeleteNPagedLookasideList(&(EfsData.EfsEventPool)); ExDeleteNPagedLookasideList(&(EfsData.EfsContextPool)); ExDeletePagedLookasideList(&(EfsData.EfsOpenCachePool)); ExDeletePagedLookasideList(&(EfsData.EfsMemSourceItem)); ExDeleteNPagedLookasideList(&(EfsData.EfsLookAside)); if (EfsData.FipsFileObject) { ObDereferenceObject(EfsData.FipsFileObject); EfsData.FipsFileObject = NULL; } } VOID EfsGetSessionKey( IN PVOID StartContext ) /*++ Routine Description: This routine is invoked in DriverEntry. It runs in a seperate thread. The purpose of this routine is to wait for the EFS server. And Get the session key. Arguments: StartContext - Start context of the thread. Return Value: None. --*/ { SECURITY_DESCRIPTOR efsInitEventSecurityDescriptor; NTSTATUS status; EFS_INIT_DATAEXG InitDataFromSrv; #if DBG if ( EFSTRACEALL & EFSDebug ){ DbgPrint( "EFSFILTER: Thread started. %x\n", EfsData.NodeTypeCode ); } #endif #if 0 // // Prepare to create an event for synchronizing with the Efs. // First, build the Security Descriptor for the Init Event Object // status = RtlCreateSecurityDescriptor( &efsInitEventSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); if (!NT_SUCCESS(status)) { #if DBG DbgPrint(("EFSFILTER: Creating Efs Init Event Desc failed 0x%lx\n", status)); #endif return ; } // // Allocate a temporary buffer from the paged pool. It is a fatal // system error if the allocation fails since security cannot be // enabled. // aclSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeLocalSystemSid); efsInitEventSecurityDescriptor.Dacl = ExAllocatePoolWithTag(PagedPool, aclSize, 'cAeS'); if (efsInitEventSecurityDescriptor.Dacl == NULL) { #if DBG DbgPrint(("EFSFILTER: Insufficient resources to initialize\n")); #endif return; } // // Now create the Discretionary ACL within the Security Descriptor // status = RtlCreateAcl( efsInitEventSecurityDescriptor.Dacl, aclSize, ACL_REVISION2 ); if (!NT_SUCCESS(status)) { #if DBG DbgPrint(("EFSFILTER: Creating Efs Init Event Dacl failed 0x%lx\n", status)); #endif return; } // // Now add an ACE giving GENERIC_ALL access to the User ID // status = RtlAddAccessAllowedAce( efsInitEventSecurityDescriptor.Dacl, ACL_REVISION2, GENERIC_ALL, SeLocalSystemSid ); if (!NT_SUCCESS(status)) { #if DBG DbgPrint(("EFSFILTER: Adding Efs Init Event ACE failed 0x%lx\n", status)); #endif return; } #endif // #if 0 status = ZwWaitForSingleObject ( EfsData.InitEventHandle, FALSE, (PLARGE_INTEGER)NULL ); ZwClose( EfsData.InitEventHandle ); // // Call server to get the session key // status = GenerateSessionKey( &InitDataFromSrv ); if (!NT_SUCCESS( status )) { #if DBG if ( (EFSTRACEALL | EFSTRACELIGHT) & EFSDebug ){ DbgPrint("GenerateSessionKey failed, status = %x\n",status); } #endif return; } // // Set session key // RtlCopyMemory( &(EfsData.SessionKey[0]), InitDataFromSrv.Key, DES_KEYSIZE ); deskey( (DESTable*)&(EfsData.SessionDesTable[0]), &(EfsData.SessionKey[0])); status = PsLookupProcessByProcessId( InitDataFromSrv.LsaProcessID, &(EfsData.LsaProcess) ); if (NT_SUCCESS( status )) { EfsData.EfsInitialized = TRUE; if ( PsGetCurrentProcess() != EfsData.LsaProcess ){ KAPC_STATE ApcState; //KeAttachProcess(EfsData.LsaProcess); KeStackAttachProcess ( EfsData.LsaProcess, &ApcState ); InitSecurityInterface(); //KeDetachProcess(); KeUnstackDetachProcess(&ApcState); } else { InitSecurityInterface(); } EfsInitFips(); } else { #if DBG if ( (EFSTRACEALL | EFSTRACELIGHT) & EFSDebug ){ DbgPrint("PsLookupProcessByProcessId failed, status = %x\n",status); } #endif } return; } ULONG GetKeyBlobLength( ULONG AlgID ) { if (EfsData.AllocMaxBuffer) { return AES_KEY_BLOB_LENGTH_256; } switch (AlgID){ case CALG_DESX: return DESX_KEY_BLOB_LENGTH; case CALG_3DES: return DES3_KEY_BLOB_LENGTH; case CALG_AES_256: return AES_KEY_BLOB_LENGTH_256; case CALG_DES: default: return DES_KEY_BLOB_LENGTH; } return 0; } PKEY_BLOB GetKeyBlobBuffer( ULONG AlgID ) { PNPAGED_LOOKASIDE_LIST MemSrcList = NULL; PKEY_BLOB_RAMPOOL KeyBlobPoolListItem = NULL; PKEY_BLOB_RAMPOOL pTmpItem = NULL; ULONG KeyBlobLength; PLIST_ENTRY pLink = NULL; PKEY_BLOB NewKeyBlob; KeyBlobLength = GetKeyBlobLength(AlgID); if (!KeyBlobLength){ ASSERT(KeyBlobLength); return NULL; } ExAcquireFastMutex( &(EfsData.EfsKeyBlobMemSrcMutex)); for (pLink = EfsData.EfsKeyLookAsideList.Flink; pLink != &(EfsData.EfsKeyLookAsideList); pLink = pLink->Flink) { pTmpItem = CONTAINING_RECORD(pLink, KEY_BLOB_RAMPOOL, MemSourceChain); if (pTmpItem->AlgorithmID == AlgID) { // // The lookaside list already exists // MemSrcList = pTmpItem->MemSourceList; break; } } ExReleaseFastMutex( &(EfsData.EfsKeyBlobMemSrcMutex) ); if ( MemSrcList == NULL ) { // // No lookaside for this type of key. Go and create one item. // MemSrcList = (PNPAGED_LOOKASIDE_LIST)ExAllocateFromNPagedLookasideList(&(EfsData.EfsLookAside)); KeyBlobPoolListItem = (PKEY_BLOB_RAMPOOL) ExAllocateFromPagedLookasideList(&(EfsData.EfsMemSourceItem)); if ( (NULL == MemSrcList) || (NULL == KeyBlobPoolListItem) ){ if (MemSrcList) { ExFreeToNPagedLookasideList(&(EfsData.EfsLookAside), MemSrcList ); } if (KeyBlobPoolListItem){ ExFreeToPagedLookasideList(&(EfsData.EfsMemSourceItem), KeyBlobPoolListItem ); } return NULL; } RtlZeroMemory( KeyBlobPoolListItem, sizeof( KEY_BLOB_RAMPOOL ) ); KeyBlobPoolListItem->MemSourceList = MemSrcList; KeyBlobPoolListItem->AlgorithmID = AlgID; ExInitializeNPagedLookasideList( MemSrcList, NULL, NULL, 0, KeyBlobLength, 'msfE', EFS_KEYDEPTH ); ExAcquireFastMutex( &(EfsData.EfsKeyBlobMemSrcMutex)); InsertHeadList( &(EfsData.EfsKeyLookAsideList), &(KeyBlobPoolListItem->MemSourceChain)); ExReleaseFastMutex( &(EfsData.EfsKeyBlobMemSrcMutex) ); } // // Allocate the Key Blob // NewKeyBlob = (PKEY_BLOB)ExAllocateFromNPagedLookasideList(MemSrcList); if (NewKeyBlob){ NewKeyBlob->AlgorithmID = AlgID; NewKeyBlob->KeyLength = KeyBlobLength; NewKeyBlob->MemSource = MemSrcList; } return NewKeyBlob; } BOOLEAN SetKeyTable( PKEY_BLOB KeyBlob, PEFS_KEY EfsKey ) { char DesXTmpKey[DESX_KEYSIZE]; switch ( EfsKey->Algorithm ){ case CALG_3DES: if (EfsData.AllocMaxBuffer) { RtlZeroMemory( &(KeyBlob->Key[0]) + DES3_TABLESIZE, KeyBlob->KeyLength - DES3_KEY_BLOB_LENGTH); } if (EfsData.FipsFunctionTable.Fips3Des3Key) { EfsData.FipsFunctionTable.Fips3Des3Key( (DES3TABLE*) &(KeyBlob->Key[0]), ((char *)EfsKey) + sizeof ( EFS_KEY ) ); } else { return FALSE; } //tripledes3key( // (DES3TABLE*) &(KeyBlob->Key[0]), // ((char *)EfsKey) + sizeof ( EFS_KEY ) // ); break; case CALG_DESX: // // Flush the non used area. // if (EfsData.AllocMaxBuffer) { RtlZeroMemory( &(KeyBlob->Key[0]) + DESX_TABLESIZE, KeyBlob->KeyLength - DESX_KEY_BLOB_LENGTH); } desexpand128to192( ((char *)EfsKey) + sizeof ( EFS_KEY ), DesXTmpKey ); desxkey( (DESXTable*) &(KeyBlob->Key[0]), DesXTmpKey ); break; case CALG_AES_256: aeskey( (AESTable*) &(KeyBlob->Key[0]), ((char *)EfsKey) + sizeof ( EFS_KEY ), AES_ROUNDS_256 ); break; case CALG_DES: default: if (EfsData.AllocMaxBuffer) { RtlZeroMemory( &(KeyBlob->Key[0]) + DES_TABLESIZE, KeyBlob->KeyLength - DES_KEY_BLOB_LENGTH); } deskey( (DESTable*) &(KeyBlob->Key[0]), ((char *)EfsKey) + sizeof ( EFS_KEY ) ); break; } return TRUE; } BOOLEAN EfsInitFips(VOID) /*++ Routine Description: Initialize the FIPS library table. Arguments: Return Value: TRUE/FALSE. --*/ { UNICODE_STRING deviceName; NTSTATUS status; PDEVICE_OBJECT pDeviceObject; // PFILE_OBJECT pFileObject = NULL; PIRP pIrp; IO_STATUS_BLOCK IoStatusBlock; PAGED_CODE(); RtlInitUnicodeString(&deviceName, FIPS_DEVICE_NAME); // // Get the file and device objects for FIPS. // status = IoGetDeviceObjectPointer( &deviceName, FILE_ALL_ACCESS, &EfsData.FipsFileObject, &pDeviceObject); if (status != STATUS_SUCCESS) { return FALSE; } // // Build the request to send to FIPS to get library table. // pIrp = IoBuildDeviceIoControlRequest( IOCTL_FIPS_GET_FUNCTION_TABLE, pDeviceObject, NULL, 0, &EfsData.FipsFunctionTable, sizeof(FIPS_FUNCTION_TABLE), FALSE, NULL, &IoStatusBlock ); if (pIrp == NULL) { #if DBG DbgPrint("EfsInitFips: IoBuildDeviceIoControlRequest IOCTL_FIPS_GET_FUNCTION_TABLE failed.\n"); #endif ObDereferenceObject(EfsData.FipsFileObject); EfsData.FipsFileObject = NULL; return FALSE; } status = IoCallDriver(pDeviceObject, pIrp); if (status != STATUS_SUCCESS) { ObDereferenceObject(EfsData.FipsFileObject); EfsData.FipsFileObject = NULL; #if DBG DbgPrint("EfsInitFips: IoCallDriver failed, status = %x\n",status); #endif return FALSE; } return TRUE; }