Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2993 lines
81 KiB

/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
efsinit.cxx
Abstract:
EFS (Encrypting File System) Server
Author:
Robert Reichel (RobertRe) July 4, 1997
Robert Gu (RobertGu) Jan 7, 1998
Environment:
Revision History:
--*/
#include <lsapch.hxx>
extern "C" {
#include <nt.h>
#include <ntdef.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#include <efsstruc.h>
#include "lsasrvp.h"
#include "debug.h"
#include "efssrv.hxx"
#include "userkey.h"
#include <cfgmgr32.h>
#include <initguid.h>
#include <mountmgr.h>
#include <Userenv.h>
}
#define TIME_UNIT 10000000 // 1 TIME_UNIT == 1 second
#define CERT_VALID_TIME 86400 // seconds - Default cache length 1 day
//
// Local prototypes of CFGMGR32 functions for dynamic load
//
typedef CMAPI CONFIGRET (WINAPI *
CM_GET_DEVICE_INTERFACE_LISTW)(
IN LPGUID,
IN DEVINSTID_W,
OUT PWCHAR,
IN ULONG,
IN ULONG );
typedef CMAPI CONFIGRET (WINAPI *
CM_GET_DEVICE_INTERFACE_LIST_SIZEW)(
IN PULONG,
IN LPGUID,
IN DEVINSTID_W,
IN ULONG
);
typedef CONFIGRET ( * CMP_WAITSERVICESAVAILABLE)(
IN HMACHINE
);
//
// Extern Vars
//
extern RTL_RESOURCE RecoveryPolicyResource;
extern CURRENT_RECOVERY_POLICY CurrentRecoveryPolicy;
extern "C" BOOLEAN EfsDisabled;
//
// Event handle used to sync with the driver
//
HANDLE EfsInitEventHandle = 0;
//
// Efs event handle to get policy change notification
//
HANDLE EfsPolicyEventHandle = 0;
HANDLE EfsWaitHandle = 0;
EFS_POL_CALLBACK EfsPolCallBack;
BOOLEAN EfsServerInitialized = FALSE;
extern "C" BOOLEAN EfsPersonalVer = TRUE;
BOOLEAN EfspInDomain = FALSE;
//
// Cache values
//
HCRYPTPROV hProvVerify = 0;
WCHAR EfsComputerName[MAX_COMPUTERNAME_LENGTH + 1];
LIST_ENTRY UserCacheList;
RTL_CRITICAL_SECTION GuardCacheListLock;
LONG UserCacheListLimit = 5; // We might read this number from the registry in the future
LONG UserCacheListCount = 0;
LONGLONG CACHE_CERT_VALID_TIME;
LONG RecoveryCertIsValidating = 0;
static PSID LocalSystemSid;
DWORD
InitRecoveryPolicy(
VOID
);
DWORD
SetDefaultRecoveryPolicy(
VOID
);
DWORD
ParseRecoveryPolicy_1_1(
IN PLSAPR_POLICY_DOMAIN_EFS_INFO PolicyEfsInfo OPTIONAL,
OUT PCURRENT_RECOVERY_POLICY ParsedRecoveryPolicy
);
VOID
EfsGetRegSettings(
VOID
);
NTSTATUS
EfsServerInit(
VOID
)
/*++
Routine Description:
This routine is called during server initialization to allow
EFS to initialize its data structures and inform the EFS Driver
that it's up and running.
Arguments:
None.
Return Value:
None.
--*/
{
OBJECT_ATTRIBUTES ObjA;
NTSTATUS Status;
UNICODE_STRING EfsInitEventName;
OSVERSIONINFOEX Osvi;
DWORD Result;
DWORD nSize = MAX_COMPUTERNAME_LENGTH + 1;
CACHE_CERT_VALID_TIME = (LONGLONG) CERT_VALID_TIME;
CACHE_CERT_VALID_TIME *= (LONGLONG) TIME_UNIT;
if ( !GetComputerName ( EfsComputerName, &nSize )){
KdPrint(("EfsServerInit - GetComputerName failed 0x%lx\n", GetLastError()));
return STATUS_UNSUCCESSFUL;
}
Osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (GetVersionEx((LPOSVERSIONINFO)&Osvi)){
if ((Osvi.wProductType == VER_NT_WORKSTATION) && ( Osvi.wSuiteMask & VER_SUITE_PERSONAL)) {
EfsPersonalVer = TRUE;
return STATUS_UNSUCCESSFUL;
} else {
if (Osvi.wProductType != VER_NT_WORKSTATION) {
//
// A server or DC. Increase the cache node value by 95.
// This value could be reset by the registry value.
//
UserCacheListLimit += 95;
}
EfsPersonalVer = FALSE;
}
} else {
//
// Treat as personal
//
EfsPersonalVer = TRUE;
return STATUS_UNSUCCESSFUL;
}
//
// Init the cache list
//
InitializeListHead(&UserCacheList);
Status = RtlInitializeCriticalSection(&GuardCacheListLock);
if (!NT_SUCCESS(Status)) {
return Status;
}
EfsGetRegSettings();
Status = InitDriverSessionKey();
if (!NT_SUCCESS(Status)) {
KdPrint(("EfsServerInit - EFS Init Session Key failed 0x%lx\n", Status));
return( Status );
}
DebugLog((DEB_TRACE_EFS, "In EfsServerInit\n" ));
//
// Zero out the initial recovery policy structure
//
Result = ParseRecoveryPolicy_1_1(
NULL,
&CurrentRecoveryPolicy
);
//
// Determine if we're in a domain or not. This must be done
// before checking the recovery policy.
//
EfspRoleChangeCallback( (POLICY_NOTIFICATION_INFORMATION_CLASS)NULL );
Status = LsaIRegisterPolicyChangeNotificationCallback(
&EfspRoleChangeCallback,
PolicyNotifyDnsDomainInformation
);
//
// Try to get the current recovery policy.
//
__try{
RtlInitializeResource( &RecoveryPolicyResource );
} __except (EXCEPTION_EXECUTE_HANDLER){
Status = GetExceptionCode();
}
if ( !NT_SUCCESS( Status ) ) {
return Status ;
}
Result = InitRecoveryPolicy();
if (ERROR_SUCCESS != Result) {
//
// We couldn't initialize our recovery policy. Continue to
// initialize the server so that the driver can proceed.
//
DebugLog((DEB_ERROR, "EfsServerInit - EFS Init Recovery Policy failed 0x%lx\n\n" ,Result ));
}
//
// Try to establish connection with the driver.
// Normally, the driver has not been loaded yet. Create
// an event and wait for the driver. If the event already
// exists, signal it.
//
RtlInitUnicodeString( &EfsInitEventName, L"\\EFSInitEvent" );
InitializeObjectAttributes(
&ObjA,
&EfsInitEventName,
0,
NULL,
NULL
);
Status = NtCreateEvent(
&EfsInitEventHandle,
EVENT_MODIFY_STATE,
&ObjA,
NotificationEvent,
FALSE
);
if (!NT_SUCCESS(Status)) {
if (STATUS_OBJECT_NAME_COLLISION == Status) {
//
// EFS driver has been loaded.
// Open and signal the event. Event handle will be closed
// in GenerateDriverSessionKey()
//
Status = NtOpenEvent(
&EfsInitEventHandle,
EVENT_MODIFY_STATE,
&ObjA
);
//
// If the EFS Init event could not be opened, the EFS Server cannot
// synchronize with the EFS Driver so neither component will
// function correctly.
//
if (!NT_SUCCESS(Status)) {
KdPrint(("EfsServerInit - Connection with the driver failed 0x%lx\n",Status));
return( Status );
}
//
// Signal the EFS Init Event. If the signalling fails, the EFS Server
// is not able to synchronize properly with the EFS Driver.
// This is a serious error which prevents both components from
// functioning correctly.
//
Status = NtSetEvent( EfsInitEventHandle, NULL );
if (!NT_SUCCESS(Status)) {
KdPrint(("EfsServerInit - Init Event Set failed 0x%lx\n",Status));
return( Status );
}
} else {
//
// Other unexpected error
//
KdPrint(("EfsServerInit - Event handling failed 0x%lx\n",Status));
return( Status );
}
}
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
Status = RtlAllocateAndInitializeSid(
&NtAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&LocalSystemSid
);
Status = LsaIRegisterPolicyChangeNotificationCallback(
&RecoveryInformationCallback,
PolicyNotifyDomainEfsInformation
);
//
// Check EFS disable policy if a DC member
//
if (EfspInDomain) {
//
// Only do this if in the domain
//
EfsPolicyEventHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
if (EfsPolicyEventHandle) {
HANDLE waitHandle;
EfsPolCallBack.EfsDisable = &EfsDisabled;
EfsPolCallBack.EfsPolicyEventHandle = &EfsPolicyEventHandle;
//
// We got the event handle. Let's register it to the GP notification.
// If we can't wait on it, we don't need to register it.
//
if (RegisterGPNotification(EfsPolicyEventHandle, TRUE)){
if (!NT_SUCCESS(RtlRegisterWait(
&EfsWaitHandle,
EfsPolicyEventHandle,
EfsGetPolRegSettings,
&EfsPolCallBack,
INFINITE,
WT_EXECUTEONLYONCE))){
//
// We couldn't use the thread pool.
//
UnregisterGPNotification(EfsPolicyEventHandle);
CloseHandle(EfsPolicyEventHandle);
EfsPolicyEventHandle = 0;
}
} else {
//
// We failed to register. No need to wait for the notification.
//
//RtlDeregisterWait(EfsWaitHandle);
CloseHandle(EfsPolicyEventHandle);
EfsPolicyEventHandle = 0;
}
}
//
// Now let's read the policy data left by the last session.
// Pass in &EfsDisabled so that later we could easily change this to
// include more features, such as algorithms controlled by the policy.
//
EfsApplyLastPolicy(&EfsDisabled);
} else {
//
// Delete the possible left over key if there is. We may move this to
// DC disjoin later.
//
EfsRemoveKey();
}
EfsServerInitialized = TRUE;
if (NT_SUCCESS( Status )) {
DebugLog((DEB_TRACE_EFS, "EFS Server initialized successfully\n" ));
} else {
DebugLog((DEB_ERROR, "EFS Server Init failed, Status = %x\n" ,Status ));
}
return( Status );
}
NTSTATUS
InitDriverSessionKey(
VOID
)
/*++
Routine Description:
Generates a session key to be used by the driver and the server
for sending information back and forth securely.
Arguments:
None.
Return Value:
STATUS_SUCCESS on success, STATUS_UNSUCCESSFUL otherwise.
--*/
{
BOOL rc;
//
// hProvVerify will remain open until the process is shut down.
// CryptReleaseContext(hProvVerify, 0) will not be called for this handle.
//
//
if (!CryptAcquireContext(&hProvVerify, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
KdPrint(("InitDriverSessionKey - CryptAcquireContext failed, error = %x\n", GetLastError()));
return( STATUS_UNSUCCESSFUL );
}
rc = CryptGenRandom( hProvVerify, SESSION_KEY_SIZE, DriverSessionKey );
if (!rc) {
return( STATUS_UNSUCCESSFUL );
} else {
LsaPid = (HANDLE) LongToPtr(GetCurrentProcessId());
deskey( &DesTable, DriverSessionKey );
return( STATUS_SUCCESS );
}
}
NTSTATUS
GenerateDriverSessionKey(
OUT PEFS_INIT_DATAEXG InitDataExg
)
/*++
Routine Description:
Actually it just sends the pre-created session key back to the
caller. The key is generated in InitDriverSessionKey.
Arguments:
InitDataExg - Supplies a pointer to a structure in which to put
the initial data to be sent to the driver.
Return Value:
STATUS_SUCCESS.
--*/
{
memcpy( InitDataExg->Key, DriverSessionKey, SESSION_KEY_SIZE );
InitDataExg->LsaProcessID = LsaPid;
if ( EfsInitEventHandle ){
//
// Connection to driver established
//
NtClose( EfsInitEventHandle );
EfsInitEventHandle = 0;
}
return( STATUS_SUCCESS );
}
inline
VOID
AcquireRecoveryPolicyWriteLock()
{
BOOL b = RtlAcquireResourceExclusive( &RecoveryPolicyResource, TRUE );
ASSERT( b );
}
inline
VOID
ReleaseRecoveryPolicyWriteLock()
{
RtlReleaseResource( &RecoveryPolicyResource );
}
DWORD
InitRecoveryPolicy(
VOID
)
/*++
Routine Description:
This routine is used to initialize the current recovery policy.
Arguments:
None
Return Value:
None.
--*/
{
//
// Attempt to query the recovery policy
// from LSA, and if there isn't any,
// attempt to create a default set.
//
DWORD Result = ERROR_SUCCESS;
PLSAPR_POLICY_DOMAIN_EFS_INFO PolicyEfsInfo = NULL;
NTSTATUS Status = LsarQueryDomainInformationPolicy(
LsapPolicyHandle,
PolicyDomainEfsInformation,
(PLSAPR_POLICY_DOMAIN_INFORMATION *)&PolicyEfsInfo
);
if (!NT_SUCCESS( Status )) {
return( RtlNtStatusToDosError(Status) );
}
//
// We're going to parse it right into the global that maintains
// the current recovery data, so take the write lock.
//
AcquireRecoveryPolicyWriteLock();
//
// Free the old parsed recovery bits
//
FreeParsedRecoveryPolicy( &CurrentRecoveryPolicy );
Result = ParseRecoveryPolicy_1_1( PolicyEfsInfo, &CurrentRecoveryPolicy );
if (PolicyEfsInfo != NULL) {
LsaIFree_LSAPR_POLICY_DOMAIN_INFORMATION (
PolicyDomainEfsInformation,
(PLSAPR_POLICY_DOMAIN_INFORMATION)PolicyEfsInfo
);
}
ReleaseRecoveryPolicyWriteLock();
return( Result );
}
DWORD
ParseRecoveryPolicy_1_1(
IN PLSAPR_POLICY_DOMAIN_EFS_INFO PolicyEfsInfo OPTIONAL,
OUT PCURRENT_RECOVERY_POLICY ParsedRecoveryPolicy
)
/*++
Routine Description:
This routine takes the currently defined recovery policy and parses
it into a form that can be used conveniently.
Arguments:
PolicyEfsInfo - Optionally supplies a pointer to the current
recovery policy from the LSA.
ParsedRecoveryPolicy - Returns a structure containing recovery information
in an easy-to-digest format.
Return Value:
return-value - Description of conditions needed to return value. - or -
None.
--*/
{
//
// Fill in the contents of the ParsedRecoveryPolicy structure
// with the contents of the passed policy information.
//
PRECOVERY_POLICY_1_1 RecoveryPolicy;
DWORD Found = 0;
PRECOVERY_KEY_1_1 RecoveryKey;
DWORD rc = ERROR_SUCCESS;
BOOL b = FALSE;
if (PolicyEfsInfo == NULL) {
//
// NULL recovery policy
//
ParsedRecoveryPolicy->PolicyStatus = RECOVERY_POLICY_NULL;
goto NoPolicy1;
}
RecoveryPolicy = (PRECOVERY_POLICY_1_1)PolicyEfsInfo->EfsBlob;
if (RecoveryPolicy == NULL) {
//
// Empty recovery policy
//
ParsedRecoveryPolicy->PolicyStatus = RECOVERY_POLICY_EMPTY;
goto NoPolicy1;
}
ParsedRecoveryPolicy->dwKeyCount = RecoveryPolicy->RecoveryPolicyHeader.RecoveryKeyCount;
if (ParsedRecoveryPolicy->dwKeyCount == 0) {
ParsedRecoveryPolicy->PolicyStatus = RECOVERY_POLICY_NO_AGENT;
goto NoPolicy1;
}
__try {
//
// Scan the recovery data looking for recovery keys in a format we understand
//
ULONG i;
RecoveryKey = (PRECOVERY_KEY_1_1) &(RecoveryPolicy->RecoveryKeyList[0]);
for (i=0 ; i< (ParsedRecoveryPolicy->dwKeyCount) ; i++) {
PEFS_PUBLIC_KEY_INFO PublicKeyInfo = &RecoveryKey->PublicKeyInfo;
if (* ((ULONG UNALIGNED *) &(PublicKeyInfo->KeySourceTag)) == EfsCertificate) {
Found++;
}
RecoveryKey = (PRECOVERY_KEY_1_1)( ((PBYTE)RecoveryKey) + * ((ULONG UNALIGNED *) &(RecoveryKey->TotalLength)) );
}
if (0 == Found) {
ParsedRecoveryPolicy->PolicyStatus = RECOVERY_POLICY_BAD_POLICY;
goto NoPolicy1;
} else {
ParsedRecoveryPolicy->Base = (PBYTE)LsapAllocateLsaHeap( 7 * sizeof( PVOID ) * Found );
if (ParsedRecoveryPolicy->Base) {
ZeroMemory( ParsedRecoveryPolicy->Base, 7 * sizeof( PVOID ) * Found);
PBYTE Base = ParsedRecoveryPolicy->Base;
ParsedRecoveryPolicy->pbHash = (PBYTE *)Base;
Base += Found * sizeof(PVOID);
ParsedRecoveryPolicy->cbHash = (PDWORD)Base;
Base += Found * sizeof(PVOID);
ParsedRecoveryPolicy->pbPublicKeys = (PBYTE *)Base;
Base += Found * sizeof(PVOID);
ParsedRecoveryPolicy->cbPublicKeys = (PDWORD)Base;
Base += Found * sizeof(PVOID);
ParsedRecoveryPolicy->lpDisplayInfo = (LPWSTR *)Base;
Base += Found * sizeof(PVOID);
ParsedRecoveryPolicy->pCertContext = (PCCERT_CONTEXT *)Base;
Base += Found * sizeof(PVOID);
ParsedRecoveryPolicy->pSid = (PSID *)Base;
} else {
ParsedRecoveryPolicy->PolicyStatus = RECOVERY_POLICY_NO_MEMORY;
return( ERROR_NOT_ENOUGH_MEMORY );
}
ParsedRecoveryPolicy->dwKeyCount = Found;
ParsedRecoveryPolicy->PolicyStatus = RECOVERY_POLICY_OK;
//
// Make a copy of the policy information so we can free what we were passed in the caller
//
RecoveryKey = (PRECOVERY_KEY_1_1) &(RecoveryPolicy->RecoveryKeyList[0]);
for (i=0 ; i< (ParsedRecoveryPolicy->dwKeyCount) ; i++) {
PEFS_PUBLIC_KEY_INFO PublicKeyInfo = &RecoveryKey->PublicKeyInfo;
if (* ((ULONG UNALIGNED *) &(PublicKeyInfo->KeySourceTag)) == EfsCertificate) {
b = ParseRecoveryCertificate( PublicKeyInfo,
&ParsedRecoveryPolicy->pbHash[i],
&ParsedRecoveryPolicy->cbHash[i],
&ParsedRecoveryPolicy->pbPublicKeys[i],
&ParsedRecoveryPolicy->cbPublicKeys[i],
&ParsedRecoveryPolicy->lpDisplayInfo[i],
&ParsedRecoveryPolicy->pCertContext[i],
&ParsedRecoveryPolicy->pSid[i]
);
if (!b) {
break;
}
}
RecoveryKey = (PRECOVERY_KEY_1_1)( ((PBYTE)RecoveryKey) + * ((ULONG UNALIGNED *) &(RecoveryKey->TotalLength)) );
}
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
b = FALSE;
//
// There was something wrong with the recovery policy.
// Return this so we can at least print out an error.
//
rc = GetExceptionCode();
}
if (!b) {
//
// Something failed, clean up
//
rc = GetLastError();
FreeParsedRecoveryPolicy( ParsedRecoveryPolicy );
ParsedRecoveryPolicy->PolicyStatus = RECOVERY_POLICY_UNKNOWN_BAD;
DebugLog((DEB_WARN, "Error parsing recovery policy\n" ));
}
//
// Policy refreshed. The Cert validation needs to be refreshed.
//
ParsedRecoveryPolicy->CertValidated = CERT_NOT_VALIDATED;
return( rc );
NoPolicy1:
ParsedRecoveryPolicy->dwKeyCount = 0;
ParsedRecoveryPolicy->TimeStamp.QuadPart = 0;
ParsedRecoveryPolicy->CertValidated = CERT_NOT_VALIDATED;
ParsedRecoveryPolicy->Base = NULL;
ParsedRecoveryPolicy->pbHash = NULL;
ParsedRecoveryPolicy->cbHash = NULL;
ParsedRecoveryPolicy->pbPublicKeys = NULL;
ParsedRecoveryPolicy->cbPublicKeys = NULL;
ParsedRecoveryPolicy->lpDisplayInfo = NULL;
ParsedRecoveryPolicy->pCertContext = NULL;
ParsedRecoveryPolicy->pSid = NULL;
return( ERROR_SUCCESS );
}
VOID
FreeParsedRecoveryPolicy(
PCURRENT_RECOVERY_POLICY ParsedRecoveryPolicy
)
/*++
Routine Description:
This routine will free the allocated memory in a
CURRENT_RECOVERY_POLICY structure.
Arguments:
ParsedRecoveryPolicy - Supplies a structure that has
had data parsed into it.
Return Value:
None.
--*/
{
//
// Walk through the recovery policy and free everything
//
DWORD i;
if (ParsedRecoveryPolicy->Base) {
for (i=0; i<ParsedRecoveryPolicy->dwKeyCount; i++) {
if (ParsedRecoveryPolicy->pbHash[i] != NULL) {
LsapFreeLsaHeap( ParsedRecoveryPolicy->pbHash[i] );
}
if (ParsedRecoveryPolicy->pbPublicKeys[i] != NULL) {
LsapFreeLsaHeap( ParsedRecoveryPolicy->pbPublicKeys[i] );
}
if (ParsedRecoveryPolicy->lpDisplayInfo[i] != NULL) {
LsapFreeLsaHeap( ParsedRecoveryPolicy->lpDisplayInfo[i] );
}
if (ParsedRecoveryPolicy->pCertContext[i] != NULL) {
CertFreeCertificateContext( ParsedRecoveryPolicy->pCertContext[i] );
}
if (ParsedRecoveryPolicy->pSid[i] != NULL) {
LsapFreeLsaHeap( ParsedRecoveryPolicy->pSid[i] );
}
}
LsapFreeLsaHeap( ParsedRecoveryPolicy->Base );
}
//
// Paranoia
//
ParsedRecoveryPolicy->Base = NULL;
ParsedRecoveryPolicy->CertValidated = CERT_NOT_VALIDATED;
ParsedRecoveryPolicy->PolicyStatus = RECOVERY_POLICY_NULL;
ParsedRecoveryPolicy->dwKeyCount = 0;
ParsedRecoveryPolicy->pbHash = NULL;
ParsedRecoveryPolicy->cbHash = NULL;
ParsedRecoveryPolicy->pbPublicKeys = NULL;
ParsedRecoveryPolicy->cbPublicKeys = NULL;
ParsedRecoveryPolicy->lpDisplayInfo = NULL;
ParsedRecoveryPolicy->pCertContext = NULL;
ParsedRecoveryPolicy->pSid = NULL;
}
DWORD WINAPI
EFSRecover(
IN LPVOID Param
)
/*++
Routine Description:
Enumerate the volumes and do the possible recovery jobs caused by
power outage or crash during encryption or decryption.
Arguments:
Param -- Standard parameter for thread. Not used.
Return Value:
Operation result.
--*/
{
CONFIGRET RetCode;
WCHAR *VolBuffer;
WCHAR *PathName;
ULONG VolOffset;
ULONG bufLen;
HMODULE hCfgMgr ;
CMP_WAITSERVICESAVAILABLE pCMP_WaitServicesAvailable ;
CM_GET_DEVICE_INTERFACE_LIST_SIZEW pCM_Get_Device_Interface_List_Size ;
CM_GET_DEVICE_INTERFACE_LISTW pCM_Get_Device_Interface_List ;
// hCfgMgr = LoadLibrary( TEXT("cfgmgr32.dll" ) );
hCfgMgr = LoadLibrary( TEXT("setupapi.dll" ) );
if ( hCfgMgr ){
pCMP_WaitServicesAvailable = (CMP_WAITSERVICESAVAILABLE)
GetProcAddress( hCfgMgr,
"CMP_WaitServicesAvailable" );
pCM_Get_Device_Interface_List_Size = (CM_GET_DEVICE_INTERFACE_LIST_SIZEW)
GetProcAddress( hCfgMgr,
"CM_Get_Device_Interface_List_SizeW" );
pCM_Get_Device_Interface_List = (CM_GET_DEVICE_INTERFACE_LISTW)
GetProcAddress( hCfgMgr,
"CM_Get_Device_Interface_ListW" );
if ( (!pCMP_WaitServicesAvailable) ||
(!pCM_Get_Device_Interface_List_Size) ||
(!pCM_Get_Device_Interface_List) )
{
FreeLibrary( hCfgMgr );
return GetLastError() ;
}
} else {
return GetLastError() ;
}
RetCode = pCMP_WaitServicesAvailable( NULL );
if ( CR_SUCCESS != RetCode ){
FreeLibrary( hCfgMgr );
return RetCode;
}
RetCode = pCM_Get_Device_Interface_List_Size(
&bufLen,
(LPGUID)&MOUNTDEV_MOUNTED_DEVICE_GUID,
NULL,
CM_GET_DEVICE_INTERFACE_LIST_PRESENT
);
if ( CR_SUCCESS == RetCode ){
SafeAllocaAllocate(VolBuffer, bufLen * sizeof(WCHAR));
SafeAllocaAllocate(PathName, MAX_PATH_LENGTH);
if ( (NULL != VolBuffer) && (NULL != PathName) ){
RetCode = pCM_Get_Device_Interface_List(
(LPGUID)&MOUNTDEV_MOUNTED_DEVICE_GUID,
NULL,
VolBuffer,
bufLen,
CM_GET_DEVICE_INTERFACE_LIST_PRESENT
);
if ( CR_SUCCESS == RetCode ){
VolOffset = 0;
while (*(VolBuffer + VolOffset)) {
//
// See if recovery is needed on the volume
//
wcscpy(PathName, VolBuffer + VolOffset);
wcscat(PathName, EFSDIR);
TryRecoverVol(VolBuffer + VolOffset, PathName);
while (*(VolBuffer + VolOffset++));
}
} else {
HANDLE EventHandleLog = RegisterEventSource(
NULL,
EFSSOURCE
);
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_GET_VOLUMES_ERROR,
NULL,
0,
4,
NULL,
&RetCode
);
DeregisterEventSource( EventHandleLog );
}
if ( CR_REGISTRY_ERROR == RetCode ){
//
// Map CR error to winerror
//
RetCode = ERROR_REGISTRY_CORRUPT;
}
}
SafeAllocaFree( VolBuffer );
SafeAllocaFree( PathName );
} else {
SafeAllocaFree( PathName );
SafeAllocaFree( VolBuffer );
RetCode = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
DWORD RetCode1 = GetLastError();
HANDLE EventHandleLog = RegisterEventSource(
NULL,
EFSSOURCE
);
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_PNP_NOT_READY,
NULL,
0,
4,
NULL,
&RetCode1
);
DeregisterEventSource( EventHandleLog );
}
if ( CR_REGISTRY_ERROR == RetCode1 ){
//
// Map CR error to winerror
//
RetCode = ERROR_REGISTRY_CORRUPT;
}
}
FreeLibrary( hCfgMgr );
return RetCode;
}
void
TryRecoverVol(
IN const WCHAR *VolumeName,
IN WCHAR *CacheDir
)
/*++
Routine Description:
Do the possible recovery jobs caused by
power outage or crash during encryption or decryption on the volume.
Arguments:
VolumeName -- volume name to be checked.
CacheDir -- EFSCACHE dir. The buffer can be used in this routine.
Return Value:
None.
--*/
{
HANDLE EfsCacheDir;
LPWIN32_FIND_DATA FindFileInfo;
HANDLE FindHandle;
//
// Check if the directory EFSCACHE exist or not
//
EfsCacheDir = CreateFile(
CacheDir,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
0
);
if (EfsCacheDir == INVALID_HANDLE_VALUE){
//
// No recovery needed
//
return;
}
CloseHandle(EfsCacheDir);
//
// Search the possible log file
//
SafeAllocaAllocate(FindFileInfo, sizeof ( WIN32_FIND_DATA ));
ASSERT(FindFileInfo);
if ( NULL == FindFileInfo )
{
return;
}
wcscat(CacheDir, EFSLOGPATTERN);
FindHandle = FindFirstFile( CacheDir, FindFileInfo );
if (FindHandle == INVALID_HANDLE_VALUE)
{
//
// No LogFile found. No recovery needed
//
SafeAllocaFree(FindFileInfo);
return;
}
//
// Log file found. FT procedure begins
//
HANDLE EventHandleLog = RegisterEventSource(NULL,
EFSSOURCE);
if ( EventHandleLog )
{
//
// Error in handling Event Log should not prevent real FT job.
//
ReportEvent(
EventHandleLog,
EVENTLOG_INFORMATION_TYPE,
0,
EFS_FT_STARTED,
NULL,
0,
0,
NULL,
NULL
);
}
for (;;)
{
BOOL b;
//
// Our log file name should be less than 12.
//
if (wcslen(FindFileInfo->cFileName) <= 12) {
TryRecoverFile( VolumeName, FindFileInfo, EventHandleLog );
}
b = FindNextFile( FindHandle, FindFileInfo );
if ( !b )
{
break;
}
}
if ( EventHandleLog )
{
DeregisterEventSource( EventHandleLog );
}
SafeAllocaFree( FindFileInfo);
FindClose( FindHandle );
return;
}
void
TryRecoverFile(
IN const WCHAR *VolumeName,
IN LPWIN32_FIND_DATA FindFileInfo,
IN HANDLE EventHandleLog
)
/*++
Routine Description:
Do the possible recovery jobs caused by
power outage or crash during encryption or decryption for the file.
Arguments:
VolumeName -- volume name the log file is in.
FindFileInfo -- Information about this log file.
Return Value:
None.
--*/
{
WCHAR *FileName = NULL;
HANDLE LogFile = INVALID_HANDLE_VALUE;
HANDLE TmpFile = NULL;
HANDLE Target;
HANDLE VolumeHandle = INVALID_HANDLE_VALUE;
LPWSTR TargetName;
LPWSTR TmpName = NULL;
LOGHEADER FileHeader;
ULONG SectorSize;
BYTE *ReadBuffer = NULL;
BYTE *StatusBuffer = NULL;
BOOL b;
ULONG BufferSize;
DWORD BytesRead;
ULONG CheckSum;
EFS_ACTION_STATUS Action;
NTSTATUS Status;
UNICODE_STRING FileId;
VolumeHandle = CreateFile(VolumeName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
0);
if ( INVALID_HANDLE_VALUE == VolumeHandle )
{
//
// Directory on this volume opened successfully. It is unlikely that we will
// come here.
//
return;
}
SafeAllocaAllocate(FileName, MAX_PATH_LENGTH);
//
// Out of memory is unlikely at the boot time
//
ASSERT (FileName);
if ( NULL == FileName )
{
goto Cleanup;
}
//
// Construct the log file name. FindFileInfo->cFileName has been limited by the caller
// that the max length is 12. So the following wcscat should be OK.
//
wcscpy( FileName, VolumeName );
wcscat( FileName, EFSDIR );
wcscat( FileName, L"\\");
wcscat( FileName, FindFileInfo->cFileName);
LogFile = CreateFile(
FileName,
GENERIC_READ | GENERIC_WRITE | DELETE,
0,
NULL,
OPEN_EXISTING,
0,
0
);
if ( INVALID_HANDLE_VALUE == LogFile )
{
DWORD ErrCode = GetLastError();
if ( EventHandleLog )
{
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_OPEN_LOGFILE_ERROR,
NULL,
0,
4,
NULL,
&ErrCode
);
}
goto Cleanup;
}
b = ReadFile(
LogFile,
&FileHeader,
sizeof( LOGHEADER ),
&BytesRead,
NULL
);
if ( !b )
{
//
// File IO error
//
DWORD ErrCode = GetLastError();
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_READ_LOGFILE_ERROR,
NULL,
0,
4,
NULL,
&ErrCode
);
}
goto Cleanup;
}
if ( 0 == BytesRead ){
//
// Zero length log file. Nothing started. Delete it.
//
MarkFileForDelete( LogFile );
goto Cleanup;
}
if ( (BytesRead < sizeof( LOGHEADER )) ||
(LOGSIGLEN * sizeof (WCHAR) != RtlCompareMemory(
&(FileHeader.SIGNATURE[0]),
LOGSIG,
LOGSIGLEN * sizeof (WCHAR)
)) ||
( LOGVERID != FileHeader.VerID )){
//
// Probably not our log file
//
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_INFORMATION_TYPE,
0,
EFS_LOGFILE_FORMAT_ERROR,
NULL,
0,
0,
NULL,
NULL
);
}
goto Cleanup;
}
//
// Read in the whole header to continue the process
//
SectorSize = FileHeader.SectorSize;
BufferSize = FileHeader.HeaderBlockSize;
ASSERT( BufferSize % SectorSize == 0 );
SafeAllocaAllocate(ReadBuffer, BufferSize);
//
// StatusBuffer must be aligned for cached IO.
// LsapAllocateLsaHeap() is not aligned.
//
if ((FileHeader.Flag & LOG_DIRECTORY) == 0) {
StatusBuffer = (BYTE *) VirtualAlloc(
NULL,
FileHeader.OffsetStatus2 - FileHeader.OffsetStatus1,
MEM_COMMIT,
PAGE_READWRITE
);
} else {
StatusBuffer = NULL;
}
ASSERT( (FileHeader.OffsetStatus2 - FileHeader.OffsetStatus1) % SectorSize == 0 );
ASSERT( ReadBuffer );
if ( (NULL == ReadBuffer) || ((NULL == StatusBuffer) && ((FileHeader.Flag & LOG_DIRECTORY) == 0)) )
{
//
// Out of memory is almost impossible during the boot time.
//
goto Cleanup;
}
SetFilePointer( LogFile, 0, NULL, FILE_BEGIN);
b = ReadFile(
LogFile,
ReadBuffer,
BufferSize,
&BytesRead,
NULL
);
if ( !b || ( BytesRead != BufferSize ) ){
//
// File IO error, Should sent out some debug Info?
//
DWORD ErrCode = GetLastError();
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_READ_LOGFILE_ERROR,
NULL,
0,
4,
NULL,
&ErrCode
);
}
goto Cleanup;
}
CheckSum = GetCheckSum(ReadBuffer, FileHeader.HeaderSize );
if (CheckSum == *(ULONG* ) (ReadBuffer + BufferSize - sizeof(ULONG))){
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK IoStatusBlock;
//
// The header is in good condition. Get TargetName and TmpName for the error
// msg in system log.
//
TargetName = (WCHAR *)( ReadBuffer + FileHeader.TargetFilePathOffset );
if ( (FileHeader.Flag & LOG_DIRECTORY) == 0 )
{
//
// Operation was on File not on Directory
// The real status is in Status block. Read in the status block.
// Log file may updated. We need to reopen it with non-cached IO.
//
CloseHandle( LogFile );
LogFile = CreateFile(FileName,
GENERIC_READ | GENERIC_WRITE | DELETE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING,
0);
//
// Log File name is done. We are going to use FileName memory space
// to get a temp file name.
//
if ( INVALID_HANDLE_VALUE == LogFile )
{
//
// Log File cannot be open as non-cached IO. Try next time.
// This is weird.
//
DWORD ErrCode = GetLastError();
ASSERT (FALSE);
if ( EventHandleLog )
{
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_OPEN_LOGFILE_NC_ERROR,
NULL,
0,
4,
NULL,
&ErrCode
);
}
goto Cleanup;
}
//
// Open the temp file first.
//
TmpName = (WCHAR *)( ReadBuffer + FileHeader.TempFilePathOffset );
FileId.Buffer = (WCHAR *) &(FileHeader.TempFileInternalName);
FileId.Length = FileId.MaximumLength = sizeof (LARGE_INTEGER);
InitializeObjectAttributes(
&Obja,
&FileId,
0,
VolumeHandle,
NULL
);
Status = NtCreateFile(
&TmpFile,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
&Obja,
&IoStatusBlock,
NULL,
0,
0,
FILE_OPEN,
FILE_OPEN_BY_FILE_ID | FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT,
NULL,
0
);
if (NT_SUCCESS( Status )){
OBJECT_NAME_INFORMATION *ATempFileName;
ULONG PathLength;
//
// Temp file opened with File ID. File open by ID cannot be deleted. So
// we need to get a name and reopen it using the name with DELETE
// access
//
ATempFileName = (OBJECT_NAME_INFORMATION *)FileName;
Status = NtQueryObject(
TmpFile,
ObjectNameInformation,
ATempFileName,
MAX_PATH_LENGTH,
&PathLength
);
if ( NT_SUCCESS( Status ) ){
CloseHandle(TmpFile);
TmpFile = 0;
InitializeObjectAttributes(
&Obja,
&(ATempFileName->Name),
0,
0,
NULL
);
Status = NtCreateFile(
&TmpFile,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE | DELETE,
&Obja,
&IoStatusBlock,
NULL,
0,
0,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT,
NULL,
0
);
if (!NT_SUCCESS( Status )){
//
// Well, we cannot open with the name got. Strange.
// Reopen it with File ID. Temp file is not going to be deleted.
//
ASSERT(FALSE);
if ( EventHandleLog ){
LPWSTR lpStrings[2];
lpStrings[1] = TargetName;
lpStrings[0] = TmpName;
ReportEvent(
EventHandleLog,
EVENTLOG_WARNING_TYPE,
0,
EFS_TMP_OPEN_NAME_ERROR,
NULL,
2,
4,
(LPCTSTR *) &lpStrings[0],
&Status
);
}
InitializeObjectAttributes(
&Obja,
&FileId,
0,
VolumeHandle,
NULL
);
Status = NtCreateFile(
&TmpFile,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
&Obja,
&IoStatusBlock,
NULL,
0,
0,
FILE_OPEN,
FILE_OPEN_BY_FILE_ID | FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT,
NULL,
0
);
if (!NT_SUCCESS( Status )){
//
// Well, more strange. We cannot even open the file we opened
// Nothing we can do this time. Try next time.
//
if ( EventHandleLog ){
LPWSTR lpStrings[2];
lpStrings[1] = TargetName;
lpStrings[0] = TmpName;
ReportEvent(
EventHandleLog,
EVENTLOG_WARNING_TYPE,
0,
EFS_TMP_FILEID_ERROR,
NULL,
2,
4,
(LPCTSTR*) &lpStrings[0],
&Status
);
}
goto Cleanup;
}
}
} else {
if ( EventHandleLog ){
LPWSTR lpStrings[2];
lpStrings[0] = TargetName;
lpStrings[1] = TmpName;
ReportEvent(
EventHandleLog,
EVENTLOG_WARNING_TYPE,
0,
EFS_TMP_FILENAME_ERROR,
NULL,
2,
4,
(LPCTSTR*) &lpStrings[0],
&Status
);
}
}
//
// If a name cannot be got, the following apply.
// Temp file is existed. But we cannot get a name. Weird.
// The recover will go on. But the temp file will not be deleted
//
} else {
//
// Temp file is not exist. May be deleted by our operations.
// Make sure the handle is 0.
//
TmpFile = 0;
}
Status = ReadLogFile(
LogFile,
StatusBuffer,
FileHeader.OffsetStatus1,
FileHeader.OffsetStatus2
);
if (!NT_SUCCESS( Status )) {
//
// Status copies are not valid. Nothing have started or we can do nothing
// to recover. Delete the log file.
//
MarkFileForDelete( LogFile );
goto Cleanup;
}
Action = * (EFS_ACTION_STATUS *) (StatusBuffer + sizeof (ULONG));
} else {
//
// Operations were on a directory
//
if ( FileHeader.Flag & LOG_DECRYPTION ){
Action = BeginDecryptDir;
} else {
Action = BeginEncryptDir;
}
}
//
// Open the target file.
//
FileId.Buffer = (WCHAR* ) &(FileHeader.TargetFileInternalName);
FileId.Length = FileId.MaximumLength = sizeof (LARGE_INTEGER);
InitializeObjectAttributes(
&Obja,
&FileId,
0,
VolumeHandle,
NULL
);
Status = NtCreateFile(
&Target,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE ,
&Obja,
&IoStatusBlock,
NULL,
0,
0,
FILE_OPEN,
FILE_OPEN_BY_FILE_ID | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT,
NULL,
0
);
if (!NT_SUCCESS( Status )) {
//
// OOPS! We can not open the target. What can we do without the target.
// Delete the log file.
//
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_WARNING_TYPE,
0,
EFS_TARGET_OPEN_ERROR,
NULL,
1,
4,
(LPCTSTR*) &TargetName,
&Status
);
}
MarkFileForDelete( LogFile );
goto Cleanup;
}
//
// We survived so far. Now is the show time!
//
DoRecover(
Target,
TmpFile,
LogFile,
TargetName,
TmpName,
FileHeader.OffsetStatus2 - FileHeader.OffsetStatus1,
FileHeader.OffsetStatus1,
Action,
EventHandleLog
);
CloseHandle ( Target );
} else {
//
// The header is not fully written. Nothing could have be done.
// Just delete the log file and we are done.
//
MarkFileForDelete( LogFile );
}
Cleanup:
if (VolumeHandle != INVALID_HANDLE_VALUE)
{
CloseHandle( VolumeHandle );
}
if (LogFile != INVALID_HANDLE_VALUE)
{
CloseHandle( LogFile );
}
if (TmpFile != NULL)
{
CloseHandle( TmpFile );
}
SafeAllocaFree(FileName);
SafeAllocaFree(ReadBuffer);
if (StatusBuffer)
{
VirtualFree(StatusBuffer,
0,
MEM_RELEASE);
}
return;
}
NTSTATUS
ReadLogFile(
IN HANDLE LogFile,
OUT BYTE* ReadBuffer,
IN ULONG FirstCopy,
IN ULONG SecondCopy
)
/*++
Routine Description:
Read Status Log Information.
Arguments:
LogFile -- A handle to the log file
ReadBuffer -- Buffer for the output data.
FirstCopy -- Offset of first status information copy in the logfile.
SecondCopy -- Offset of second status information copy in the logfile.
Return Value:
The status of operation.
--*/
{
ULONG ReadBytes;
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
LARGE_INTEGER ByteOffset;
ULONG CheckSum;
BOOLEAN WrongCheckSum = FALSE;
//
// Write out the header sector
//
ByteOffset.QuadPart = (LONGLONG) FirstCopy;
ReadBytes = SecondCopy - FirstCopy;
Status = NtReadFile(
LogFile,
0,
NULL,
NULL,
&IoStatusBlock,
ReadBuffer,
ReadBytes,
&ByteOffset,
NULL
);
if ( NT_SUCCESS(Status) ) {
//
// Check the integrity of the data
//
CheckSum = GetCheckSum( ReadBuffer, 2 * sizeof (ULONG) );
if ( CheckSum != *(ULONG *)( ReadBuffer + ReadBytes - sizeof (ULONG) )){
WrongCheckSum = TRUE;
}
}
if ( !NT_SUCCESS(Status) || WrongCheckSum ) {
ByteOffset.QuadPart = (LONGLONG) SecondCopy;
Status = NtReadFile(
LogFile,
0,
NULL,
NULL,
&IoStatusBlock,
ReadBuffer,
ReadBytes,
&ByteOffset,
NULL
);
if ( NT_SUCCESS(Status) ) {
//
// Check the integrity of the data
//
CheckSum = GetCheckSum( ReadBuffer, 2 * sizeof (ULONG) );
if ( CheckSum != *(ULONG *)( ReadBuffer + ReadBytes - sizeof (ULONG) )){
//
// Both status copy are bad.
//
Status = STATUS_FILE_CORRUPT_ERROR;
}
}
}
return Status;
}
NTSTATUS
DoRecover(
IN HANDLE Target,
IN HANDLE TmpFile OPTIONAL,
IN HANDLE LogFile,
IN LPCWSTR TargetName,
IN LPCWSTR TmpName OPTIONAL,
IN ULONG StatusCopySize,
IN ULONG StatusStartOffset,
IN ULONG Action,
IN HANDLE EventHandleLog
)
/*++
Routine Description:
Do the real dirty recovery job here.
Arguments:
Target -- A handle to the target file or directory.
TmpFile -- A handle to the temp file if Target is a file.
LogFile -- A handle to the log file.
TargetName -- Target file name. This info is used for error log only.
TmpName -- Temp backup file name. This info is used for error log only.
StatusCopySize -- Log file status copy section size.
StatusStartOffset -- Offset of status copy in the log file.
Action -- The status to begin.
EventHandleLog -- Event log handle.
Return Value:
The status of operation.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
EFSP_OPERATION Operation = EncryptRecovering;
if ( ( BeginEncryptDir == Action ) || ( BeginDecryptDir == Action ) ){
//
// In both cases, we will do Decrypt on the directory
//
Status = DecryptDir( Target, TargetName );
if ( NT_SUCCESS(Status) ) {
//
// The operation was successful
//
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_INFORMATION_TYPE,
0,
EFS_TARGET_RECOVERED,
NULL,
1,
0,
(LPCTSTR*) &TargetName,
NULL
);
}
MarkFileForDelete( LogFile );
} else {
//
// EFS driver might not be loaded. Log the information.
//
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_DRIVER_MISSING,
NULL,
1,
0,
(LPCTSTR*) &TargetName,
NULL
);
}
}
} else {
//
// File operations
//
switch ( Action ) {
case EncryptionMessup:
//
// The original file is messed up due to the unknown reason.
// Operation required,
// Same as EncryptTmpFileWritten.
//
Operation = EncryptRecovering;
// ***** Fall through intended *****
case DecryptTmpFileWritten:
//
// Temp file is created and written during the decryption. The original file is probably
// damaged. Operation required,
// Same as the EncryptTmpFileWritten. The file will end up as plain text.
//
if ( DecryptTmpFileWritten == Action ){
Operation = DecryptRecovering;
}
//
// ***** Fall through intended *****
//
case EncryptTmpFileWritten:
if ( EncryptTmpFileWritten == Action ){
Operation = EncryptRecovering;
}
//
// Temp file is created and written during the encryption. The original file is probably
// damaged. Operation required,
// FSCTL decrypt each stream, copy the data back to the original file. Log file
// will be updated to BeginEncryptFile after all the streams are copied back. Then
// continue the case indicated in BeginEncryptFile.
//
if ( !TmpFile ){
//
// Temp backup not existed or open failed, we can do nothing for this.
// Log the error information and quit.
//
if ( EventHandleLog ){
LPCWSTR lpStrings[2];
lpStrings[1] = TargetName;
lpStrings[0] = TmpName;
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_TMPFILE_MISSING,
NULL,
2,
4,
&lpStrings[0],
&Status
);
}
return STATUS_NO_SUCH_FILE;
}
Status = RestoreTarget(
Target,
TmpFile,
TargetName,
TmpName,
EventHandleLog,
Operation
);
if ( NT_SUCCESS(Status) ) {
WriteLogFile(
LogFile,
StatusCopySize,
StatusStartOffset,
BeginEncryptFile
);
} else {
return Status;
}
// ***** Fall through intended *****
case DecryptionDone:
//
// All the streams are marked decrypted. The file might still have the flag set.
// Operation required,
// Same as BeginEncryptFile.
//
// ***** Fall through intended *****
case EncryptionBackout:
//
// Encryption failed but we managed the original streams back. The file might
// be in a transition status.
// Operation required,
// Same as BeginEncryptFile.
//
// ***** Fall through intended *****
case BeginEncryptFile:
//
// File encryption just begin. Original file is not changed.
// Operation required,
// Remove the $EFS, remove the tempfile if existed and remove the log file.
//
Status = SendGenFsctl(
Target,
EFS_DECRYPT_FILE,
EFS_DECRYPT_FILE,
EFS_SET_ENCRYPT,
FSCTL_SET_ENCRYPTION
);
if ( NT_SUCCESS(Status) ) {
if ( TmpFile ){
MarkFileForDelete( TmpFile );
}
MarkFileForDelete( LogFile );
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_INFORMATION_TYPE,
0,
EFS_TARGET_RECOVERED,
NULL,
1,
0,
&TargetName,
NULL
);
}
} else {
//
// EFS driver may not be loaded. Write the log info.
//
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_DRIVER_MISSING,
NULL,
1,
0,
&TargetName,
NULL
);
}
}
break;
case BeginDecryptFile:
//
// File decryption just begin. Original file is not changed.
// Operation required,
// Set the transition status to normal, remove the tempfile if existed and
// remove the log file.
//
// ***** Fall through intended *****
case EncryptionDone:
//
// All the streams were encrypted. The original file might be in transition status.
// Operation required,
// FSCTL to set the transition status to normal. Update the log status to
// EncryptionSrcDone. Continue to EncryptionSrcDone.
//
Status = SendGenFsctl(
Target,
0,
0,
EFS_ENCRYPT_DONE,
FSCTL_ENCRYPTION_FSCTL_IO
);
if ( !NT_SUCCESS(Status) ) {
//
// EFS driver might not be loaded. Log error.
//
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_DRIVER_MISSING,
NULL,
1,
0,
&TargetName,
NULL
);
}
return Status;
}
// ***** Fall through intended *****
case EncryptionSrcDone:
//
// The original file is encrypted successfully. The temp file might still be left.
// Operation required,
// Remove the temp file if existed and remove the log file.
//
if ( TmpFile ){
MarkFileForDelete( TmpFile );
}
MarkFileForDelete( LogFile );
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_INFORMATION_TYPE,
0,
EFS_TARGET_RECOVERED,
NULL,
1,
0,
&TargetName,
NULL
);
}
break;
default:
//
// Not our log file or tempered by someone.
// Write to the log to notify the user.
//
Status = STATUS_FILE_CORRUPT_ERROR;
break;
}
}
return Status;
}
NTSTATUS
DecryptDir(
IN HANDLE Target,
IN LPCWSTR TargetName
)
/*++
Routine Description:
Decrypt a directory.
Arguments:
Target -- A handle to the target file or directory.
TargetName -- Target file name. This info is used for error log only.
Return Value:
The status of operation.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
Status = SendGenFsctl(
Target,
EFS_DECRYPT_STREAM,
EFS_DECRYPT_DIRSTR,
EFS_SET_ENCRYPT,
FSCTL_SET_ENCRYPTION
);
if ( NT_SUCCESS( Status ) ){
Status = SendGenFsctl(
Target,
EFS_DECRYPT_FILE,
EFS_DECRYPT_DIRFILE,
EFS_SET_ENCRYPT,
FSCTL_SET_ENCRYPTION
);
}
return Status;
}
NTSTATUS
RestoreTarget(
IN HANDLE Target,
IN HANDLE TmpFile,
IN LPCWSTR TargetName,
IN LPCWSTR TmpName,
IN HANDLE EventHandleLog,
EFSP_OPERATION Operation
)
/*++
Routine Description:
Copy all the streams in the temp backup file to the original target file.
Arguments:
Target -- A handle to the target file or directory.
TmpFile -- A handle to the temp file if Target is a file.
TargetName -- Target file name. This info is used for error log only.
TmpName -- Temp backup file name. This info is used for error log only.
Operation -- Indicate if encryption or decryption was tried.
Return Value:
The status of operation.
--*/
{
NTSTATUS Status;
DWORD hResult;
PFILE_STREAM_INFORMATION StreamInfoBase = NULL;
ULONG StreamInfoSize = 0;
ULONG StreamCount = 0;
PEFS_STREAM_SIZE StreamSizes;
PHANDLE StreamHandles;
PUNICODE_STRING StreamNames;
Status = GetStreamInformation(
TmpFile,
&StreamInfoBase,
&StreamInfoSize
);
if ( NT_SUCCESS( Status ) ){
hResult = OpenFileStreams(
TmpFile,
FILE_SHARE_DELETE, // have to share with delete-ers, since the main stream is open for delete
OPEN_FOR_FTR,
StreamInfoBase,
FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT,
NULL,
&StreamNames, // Free this but not the contents!
&StreamHandles, // Free this
&StreamSizes, // Free this
&StreamCount
);
if (hResult == ERROR_SUCCESS){
PHANDLE TargetStreamHandles;
LONG ArrIndex;
SafeAllocaAllocate(TargetStreamHandles, StreamCount * sizeof (HANDLE));
if ( TargetStreamHandles ){
//
// Open streams at the original target file
//
for ( ArrIndex = 0; ArrIndex < (LONG) StreamCount; ArrIndex++){
if ( StreamHandles[ArrIndex] == TmpFile ){
TargetStreamHandles[ArrIndex] = Target;
} else {
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK IoStatusBlock;
InitializeObjectAttributes(
&Obja,
&StreamNames[ArrIndex],
0,
Target,
NULL
);
Status = NtCreateFile(
&TargetStreamHandles[ArrIndex],
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
&Obja,
&IoStatusBlock,
NULL,
0,
0,
FILE_OPEN_IF,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT,
NULL,
0
);
if (!NT_SUCCESS( Status )){
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_TARGET_STREAM_OPEN_ERROR,
NULL,
1,
4,
&TargetName,
&Status
);
}
break;
}
}
}
if ( NT_SUCCESS( Status ) ){
ULONG FsInputDataSize;
PUCHAR FsInputData;
//
// Adjust ArrIndex for clean up
//
ArrIndex--;
//
// Make a FSCTL request input data block
//
FsInputDataSize = 7 * sizeof( ULONG ) + 2 * sizeof(DriverSessionKey);
SafeAllocaAllocate(FsInputData, FsInputDataSize);
if ( FsInputData )
{
BOOLEAN CleanupSuccessful;
( VOID )GetDecryptFsInput(
Target,
FsInputData,
&FsInputDataSize
);
hResult = CopyFileStreams(
StreamHandles, // handles to streams on the backup file
TargetStreamHandles, // Handles to the streams on the original file
StreamCount, // number of streams
StreamSizes, // sizes of streams
Operation, // mark StreamHandles as Decrypted before copy
FsInputData, // FSCTL input data
FsInputDataSize, // FSCTL input data size
&CleanupSuccessful
);
SafeAllocaFree( FsInputData );
if ( hResult != ERROR_SUCCESS )
{
if ( EventHandleLog ){
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_STREAM_COPY_ERROR,
NULL,
1,
4,
&TargetName,
&hResult
);
}
Status = STATUS_UNSUCCESSFUL;
}
}
else
{
//
// Out of memory. Almost impossible during LSA init.
//
ASSERT(FALSE);
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
//
// Clean up TargetStreamHandles at the target.
//
while (ArrIndex >= 0)
{
if ( TargetStreamHandles[ArrIndex] != Target )
{
CloseHandle(TargetStreamHandles[ArrIndex]);
}
ArrIndex--;
}
SafeAllocaFree( TargetStreamHandles );
}
//
// Clean up StreamHandles and etc.
//
for (ArrIndex = 0; ArrIndex< (LONG) StreamCount ; ArrIndex++) {
if ( StreamHandles[ArrIndex] != TmpFile){
NtClose( StreamHandles[ArrIndex] );
}
}
LsapFreeLsaHeap( StreamHandles);
LsapFreeLsaHeap( StreamNames);
LsapFreeLsaHeap( StreamSizes);
} else {
//
// Not all the requested streams could be opened.
// Write the log info.
//
if ( EventHandleLog ){
LPCWSTR lpStrings[2];
lpStrings[1] = TargetName;
lpStrings[0] = TmpName;
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_TMP_STREAM_OPEN_ERROR,
NULL,
2,
4,
&lpStrings[0],
&hResult
);
}
Status = STATUS_UNSUCCESSFUL;
}
LsapFreeLsaHeap( StreamInfoBase );
} else {
//
// Stream info cannot be got. Write LOG info.
//
if ( EventHandleLog ){
LPCWSTR lpStrings[2];
lpStrings[1] = TargetName;
lpStrings[0] = TmpName;
ReportEvent(
EventHandleLog,
EVENTLOG_ERROR_TYPE,
0,
EFS_TMP_STREAM_INFO_ERROR,
NULL,
2,
4,
&lpStrings[0],
&Status
);
}
}
return Status;
}
BOOL
ParseRecoveryCertificate(
IN PEFS_PUBLIC_KEY_INFO pPublicKeyInfo,
OUT PBYTE * pbHash,
OUT PDWORD cbHash,
OUT PBYTE * pbPublicKey,
OUT PDWORD cbPublicKey,
OUT LPWSTR * lpDisplayInfo,
OUT PCCERT_CONTEXT * pCertContext,
OUT PSID * pSid
)
/*++
Routine Description:
This routine takes a certificate passed in the recovery policy and
extracts the interesting information.
Arguments:
pPublicKeyInfo - Takes the public key info structure from the
recovery policy.
pbHash - Returns the hash of the passed certificate.
cbHash - Returns the lengh in bytes of the returned hash.
pbPublicKey - Returns a pointer to the public key blob of the certificate.
cbPublicKey - Returns the length in bytes of the public key.
lpDisplayInfo - Returns display information about the certificate.
pCertContext - Cert context for the passed certificate.
pSid - Returns SID of the recovery agent
Return Value:
TRUE on success, FALSE on failure. Call GetLastError() for more details.
--*/
{
//
// Get the certificate out of the public key info structure
//
PEFS_PUBLIC_KEY_INFO pAlignedPublicKeyInfo;
BOOLEAN freeAlignedInfo;
DWORD rc = ERROR_SUCCESS;
rc = EfsAlignBlock(
pPublicKeyInfo,
(PVOID *)&pAlignedPublicKeyInfo,
&freeAlignedInfo
);
if (!pAlignedPublicKeyInfo) {
//
// OOM. Treat it as not current.
//
rc = ERROR_NOT_ENOUGH_MEMORY;
return FALSE;
}
ASSERT( pAlignedPublicKeyInfo->KeySourceTag == EfsCertificate );
//
// Initialize OUT parameters
//
*pbHash = NULL;
*pbPublicKey = NULL;
*lpDisplayInfo = NULL;
*pCertContext = NULL;
*pSid = NULL;
PBYTE pbCert = (PBYTE)OFFSET_TO_POINTER(CertificateInfo.Certificate, pAlignedPublicKeyInfo);
DWORD cbCert = pAlignedPublicKeyInfo->CertificateInfo.CertificateLength;
*pCertContext = CertCreateCertificateContext(
CRYPT_ASN_ENCODING,
(const PBYTE)pbCert,
cbCert);
if (*pCertContext) {
PCERT_INFO pCertInfo = (*pCertContext)->pCertInfo;
CERT_PUBLIC_KEY_INFO * pSubjectPublicKeyInfo = &pCertInfo->SubjectPublicKeyInfo;
CRYPT_BIT_BLOB * PublicKey = &pSubjectPublicKeyInfo->PublicKey;
*cbPublicKey = 0;
if (CryptDecodeObject(
CRYPT_ASN_ENCODING,
RSA_CSP_PUBLICKEYBLOB,
PublicKey->pbData,
PublicKey->cbData,
0,
NULL,
cbPublicKey
)) {
if (*pbPublicKey = (PBYTE)LsapAllocateLsaHeap( *cbPublicKey )) {
if (CryptDecodeObject(
CRYPT_ASN_ENCODING,
RSA_CSP_PUBLICKEYBLOB,
PublicKey->pbData,
PublicKey->cbData,
0,
*pbPublicKey,
cbPublicKey
)) {
//
// Get the certificate hash
//
*cbHash = 0;
if (CertGetCertificateContextProperty(
*pCertContext,
CERT_HASH_PROP_ID,
NULL,
cbHash
)) {
*pbHash = (PBYTE)LsapAllocateLsaHeap( *cbHash );
if (*pbHash) {
if (CertGetCertificateContextProperty(
*pCertContext,
CERT_HASH_PROP_ID,
*pbHash,
cbHash
)) {
//
// Get the display information
//
*lpDisplayInfo = EfspGetCertDisplayInformation( *pCertContext );
if (*lpDisplayInfo == NULL) {
rc = GetLastError();
}
//
// Try to get the recovery agent SID
// This info is not very important. If we fail, we should continue.
//
if (pAlignedPublicKeyInfo->PossibleKeyOwner) {
DWORD SidLength;
PSID RecSid = (PSID) OFFSET_TO_POINTER( PossibleKeyOwner, pAlignedPublicKeyInfo );
SidLength = GetLengthSid(RecSid);
*pSid = (PSID)LsapAllocateLsaHeap( SidLength );
if (*pSid) {
RtlCopyMemory( *pSid, RecSid, SidLength );
}
}
} else {
rc = GetLastError();
}
} else {
rc = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
rc = GetLastError();
}
} else {
rc = GetLastError();
}
} else {
rc = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
rc = GetLastError();
}
} else {
rc = GetLastError();
}
if (freeAlignedInfo) {
LsapFreeLsaHeap( pAlignedPublicKeyInfo );
}
if (rc != ERROR_SUCCESS) {
//
// Free the stuff we were going to return
//
if (*pbHash != NULL) {
LsapFreeLsaHeap( *pbHash );
*pbHash = NULL;
}
if (*pbPublicKey != NULL) {
LsapFreeLsaHeap( *pbPublicKey );
*pbPublicKey = NULL;
}
if (*lpDisplayInfo != NULL) {
LsapFreeLsaHeap( *lpDisplayInfo );
*lpDisplayInfo = NULL;
}
if (*pCertContext != NULL) {
CertFreeCertificateContext( *pCertContext );
*pCertContext = NULL;
}
if (*pSid != NULL) {
LsapFreeLsaHeap( *pSid );
*pSid = NULL;
}
}
SetLastError( rc );
return( rc == ERROR_SUCCESS );
}