10041 lines
288 KiB
C++
10041 lines
288 KiB
C++
/*++
|
||
|
||
Copyright (c) 1997-1999 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
efsapi.cxx
|
||
|
||
Abstract:
|
||
|
||
EFS (Encrypting File System) API Interfaces
|
||
|
||
Author:
|
||
|
||
Robert Reichel (RobertRe)
|
||
Robert Gu (RobertG)
|
||
|
||
Environment:
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include <lsapch.hxx>
|
||
|
||
//
|
||
// Fodder
|
||
|
||
extern "C" {
|
||
#include <nt.h>
|
||
#include <ntdef.h>
|
||
#include <ntrtl.h>
|
||
#include <nturtl.h>
|
||
#include <windows.h>
|
||
#include <stdio.h>
|
||
#include <wincrypt.h>
|
||
#include <efsstruc.h>
|
||
#include "lsasrvp.h"
|
||
#include "debug.h"
|
||
#include "efssrv.hxx"
|
||
#include "userkey.h"
|
||
#include "lsapmsgs.h"
|
||
|
||
}
|
||
|
||
|
||
//Constant -- This buffer length should be enough for EFS temp file name
|
||
#define TEMPFILELEN 26
|
||
|
||
//
|
||
// Initial memory allocation block size for $EFS
|
||
//
|
||
#define INIT_EFS_BLOCK_SIZE 4096
|
||
#define INITBUFFERSIZE 4096
|
||
|
||
|
||
#define ENCRYPT 1
|
||
|
||
#define EfsErrPrint //
|
||
|
||
#define BASIC_KEY_INFO 1
|
||
|
||
//
|
||
// Global Variables
|
||
//
|
||
|
||
DESTable DesTable;
|
||
|
||
UCHAR DriverSessionKey[DES_BLOCKLEN];
|
||
|
||
HANDLE LsaPid = NULL;
|
||
|
||
//
|
||
// Prototypes
|
||
//
|
||
|
||
ULONG
|
||
StringInfoCmp(
|
||
IN PFILE_STREAM_INFORMATION StreamInfoBase,
|
||
IN PFILE_STREAM_INFORMATION LaterStreamInfoBase,
|
||
IN ULONG StreamInfoSize
|
||
);
|
||
|
||
BOOLEAN
|
||
EncryptFSCTLData(
|
||
IN ULONG Fsctl,
|
||
IN ULONG Psc,
|
||
IN ULONG Csc,
|
||
IN PVOID EfsData,
|
||
IN ULONG EfsDataLength,
|
||
IN OUT PUCHAR Buffer,
|
||
IN OUT PULONG BufferLength
|
||
);
|
||
|
||
BOOLEAN
|
||
SendHandle(
|
||
IN HANDLE Handle,
|
||
IN OUT PUCHAR EfsData,
|
||
IN OUT PULONG EfsDataLength
|
||
);
|
||
|
||
BOOLEAN
|
||
SendEfs(
|
||
IN PEFS_KEY Fek,
|
||
IN PEFS_DATA_STREAM_HEADER Efs,
|
||
OUT PUCHAR EfsData,
|
||
OUT PULONG EfsDataLength
|
||
);
|
||
|
||
BOOLEAN
|
||
SendHandleAndEfs(
|
||
IN HANDLE Handle,
|
||
IN PEFS_DATA_STREAM_HEADER Efs,
|
||
IN OUT PUCHAR EfsData,
|
||
IN OUT PULONG EfsDataLength
|
||
);
|
||
|
||
NTSTATUS
|
||
SendSkFsctl(
|
||
IN ULONG PSC,
|
||
IN ULONG CSC,
|
||
IN ULONG EfsCode,
|
||
IN PUCHAR InputData,
|
||
IN ULONG InputDataSize,
|
||
IN HANDLE Handle,
|
||
IN ULONG FsCode,
|
||
OUT IO_STATUS_BLOCK *IoStatusBlock
|
||
);
|
||
|
||
NTSTATUS
|
||
EndErrorEncryptFile(
|
||
IN HANDLE FileHandle,
|
||
IN PUCHAR InputData,
|
||
IN ULONG InputDataSize,
|
||
OUT IO_STATUS_BLOCK *IoStatusBlock
|
||
);
|
||
|
||
NTSTATUS
|
||
GetRootHandle(
|
||
IN HANDLE FileHandle,
|
||
PHANDLE RootDirectoryHandle
|
||
);
|
||
|
||
NTSTATUS
|
||
GetParentEfsStream(
|
||
IN HANDLE CurrentFileHandle,
|
||
IN PUNICODE_STRING CurrentFileName,
|
||
OUT PEFS_DATA_STREAM_HEADER *ParentEfsStream
|
||
);
|
||
|
||
DWORD
|
||
MyCopyFile(
|
||
HANDLE SourceFile,
|
||
PUNICODE_STRING StreamNames,
|
||
PHANDLE StreamHandles,
|
||
PEFS_STREAM_SIZE StreamSizes,
|
||
ULONG StreamCount,
|
||
HANDLE hTargetFile,
|
||
PHANDLE * TargetStreamHandles
|
||
);
|
||
|
||
VOID
|
||
CleanupOpenFileStreams(
|
||
IN PHANDLE Handles OPTIONAL,
|
||
IN PUNICODE_STRING StreamNames OPTIONAL,
|
||
IN PEFS_STREAM_SIZE Sizes OPTIONAL,
|
||
IN PFILE_STREAM_INFORMATION StreamInfoBase OPTIONAL,
|
||
IN HANDLE HSourceFile OPTIONAL,
|
||
IN ULONG StreamCount
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
GetBackupFileName(
|
||
LPCWSTR SourceFile,
|
||
LPWSTR * BackupName
|
||
);
|
||
|
||
|
||
DWORD
|
||
CopyStream(
|
||
HANDLE Target,
|
||
HANDLE Source,
|
||
PEFS_STREAM_SIZE StreamSize
|
||
);
|
||
|
||
|
||
DWORD
|
||
CheckVolumeSpace(
|
||
PFILE_FS_SIZE_INFORMATION VolInfo,
|
||
PEFS_STREAM_SIZE StreamSizes,
|
||
PHANDLE StreamHandles,
|
||
ULONG StreamCount
|
||
);
|
||
|
||
DWORD
|
||
CompressStreams(
|
||
PEFS_STREAM_SIZE StreamSizes,
|
||
PHANDLE StreamHandles,
|
||
ULONG State,
|
||
ULONG StreamCount
|
||
);
|
||
|
||
DWORD
|
||
CheckOpenSection(
|
||
PEFS_STREAM_SIZE StreamSizes,
|
||
PHANDLE StreamHandles,
|
||
ULONG StreamCount
|
||
);
|
||
|
||
DWORD
|
||
CopyStreamSection(
|
||
HANDLE Target,
|
||
HANDLE SrcMapping,
|
||
PLARGE_INTEGER Offset,
|
||
PLARGE_INTEGER DataLength,
|
||
PLARGE_INTEGER AllocationGranularity
|
||
);
|
||
|
||
BOOL
|
||
EfspSetEfsOnFile(
|
||
IN HANDLE hFile,
|
||
PEFS_DATA_STREAM_HEADER pEfsStream,
|
||
IN PEFS_KEY pNewFek
|
||
);
|
||
|
||
NTSTATUS
|
||
GetFileEfsStream(
|
||
IN HANDLE hFile,
|
||
OUT PEFS_DATA_STREAM_HEADER * pEfsStream
|
||
);
|
||
|
||
DWORD
|
||
EncryptFileSrv(
|
||
IN PEFS_USER_INFO pEfsUserInfo,
|
||
IN PUNICODE_STRING SourceFileNameU,
|
||
IN HANDLE LogFileH
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the top level routine of the EncryptFile API. It
|
||
opens the passed source file and copies all of its data streams to
|
||
a backup file in a known location. It then marks all of the streams
|
||
of the source as encrypted, and copies them back.
|
||
|
||
Arguments:
|
||
|
||
SourceFileName - Supplies a Unicode string with the name of
|
||
the file to be encrypted.
|
||
|
||
LogFileH - Log file handle. Log file is zero size when passed in.
|
||
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS on success, other on failure.
|
||
|
||
--*/
|
||
{
|
||
BOOL b = TRUE;
|
||
BOOLEAN CleanupSuccessful = TRUE;
|
||
BOOLEAN KeepLogFile = FALSE;
|
||
|
||
ULONG StatusInfoOffset = 0 ;
|
||
DWORD hResult = ERROR_SUCCESS;
|
||
DWORD FileAttributes;
|
||
|
||
HANDLE FileHandle;
|
||
HANDLE hSourceFile;
|
||
HANDLE hBackupFile = 0;
|
||
PHANDLE StreamHandles = NULL;
|
||
|
||
LPWSTR SourceFileName;
|
||
LPWSTR BackupFileName;
|
||
|
||
FILE_FS_SIZE_INFORMATION VolInfo;
|
||
FILE_INTERNAL_INFORMATION SourceID;
|
||
FILE_INTERNAL_INFORMATION BackupID;
|
||
|
||
NTSTATUS Status;
|
||
|
||
OBJECT_ATTRIBUTES Obja;
|
||
|
||
PFILE_STREAM_INFORMATION LaterStreamInfoBase = NULL;
|
||
PFILE_STREAM_INFORMATION StreamInfoBase = NULL;
|
||
|
||
PEFS_STREAM_SIZE StreamSizes = NULL;
|
||
|
||
PUNICODE_STRING StreamNames = NULL;
|
||
|
||
UINT TmpResult;
|
||
|
||
ULONG LaterStreamInfoSize = 0;
|
||
ULONG StreamCount = 0;
|
||
ULONG StreamInfoSize = 0;
|
||
ULONG i;
|
||
DWORD tmpDW;
|
||
|
||
PEFS_DATA_STREAM_HEADER ParentEfsStream = NULL;
|
||
PEFS_DATA_STREAM_HEADER CurrentEfsStream = NULL;
|
||
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
ULONG InputDataSize;
|
||
ULONG EfsDataLength;
|
||
PUCHAR InputData;
|
||
WORD WebDavPath = 0;
|
||
|
||
//
|
||
// Convert the source file name into an LPWSTR
|
||
//
|
||
|
||
if (!LogFileH) {
|
||
|
||
//
|
||
// No LogFile means WEBDAVPATH
|
||
//
|
||
|
||
WebDavPath = WEBDAVPATH;
|
||
}
|
||
|
||
SourceFileName = (LPWSTR)LsapAllocateLsaHeap( SourceFileNameU->Length + sizeof( UNICODE_NULL ));
|
||
|
||
if (SourceFileName == NULL) {
|
||
|
||
MarkFileForDelete( LogFileH );
|
||
EfsErrPrint("Out of memory allocating SourceFileName");
|
||
return( ERROR_NOT_ENOUGH_MEMORY );
|
||
}
|
||
|
||
SourceFileName[SourceFileNameU->Length/sizeof(WCHAR)] = UNICODE_NULL;
|
||
|
||
RtlCopyMemory( SourceFileName, SourceFileNameU->Buffer, SourceFileNameU->Length );
|
||
|
||
DebugLog((DEB_TRACE_EFS, "Encrypting file %ws \n", SourceFileName));
|
||
|
||
FileAttributes = GetFileAttributes( SourceFileName );
|
||
|
||
if (FileAttributes == -1) {
|
||
if (LogFileH) {
|
||
MarkFileForDelete( LogFileH );
|
||
}
|
||
LsapFreeLsaHeap( SourceFileName );
|
||
EfsErrPrint("GetFileAttributes failed with -1");
|
||
return GetLastError();
|
||
|
||
}
|
||
|
||
//
|
||
// Open the target file
|
||
//
|
||
|
||
if ( FileAttributes & FILE_ATTRIBUTE_DIRECTORY ){
|
||
tmpDW = FILE_FLAG_BACKUP_SEMANTICS;
|
||
} else {
|
||
tmpDW = FILE_FLAG_OPEN_REPARSE_POINT;
|
||
}
|
||
|
||
//
|
||
// CreateFile will add FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE
|
||
//
|
||
|
||
hSourceFile = CreateFile(
|
||
SourceFileName,
|
||
FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
|
||
0,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
tmpDW,
|
||
NULL
|
||
);
|
||
|
||
if (hSourceFile != INVALID_HANDLE_VALUE) {
|
||
|
||
NTSTATUS Status1;
|
||
|
||
if ( FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||
|
||
//
|
||
// Fail the call if the file is HSM or SIS file
|
||
//
|
||
FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
|
||
|
||
Status = NtQueryInformationFile(
|
||
hSourceFile,
|
||
&IoStatusBlock,
|
||
&TagInfo,
|
||
sizeof ( FILE_ATTRIBUTE_TAG_INFORMATION ),
|
||
FileAttributeTagInformation
|
||
);
|
||
|
||
if ( NT_SUCCESS( Status ) && ( (IO_REPARSE_TAG_HSM == TagInfo.ReparseTag) || (IO_REPARSE_TAG_SIS == TagInfo.ReparseTag))) {
|
||
|
||
//
|
||
// Log the error saying we do not support HSM and SIS
|
||
//
|
||
|
||
EfsLogEntry(
|
||
EVENTLOG_ERROR_TYPE,
|
||
0,
|
||
EFS_REPARSE_FILE_ERROR,
|
||
1,
|
||
sizeof(ULONG),
|
||
(LPCWSTR *)&SourceFileName,
|
||
&TagInfo.ReparseTag
|
||
);
|
||
|
||
if (LogFileH) {
|
||
MarkFileForDelete( LogFileH );
|
||
}
|
||
LsapFreeLsaHeap( SourceFileName );
|
||
CloseHandle( hSourceFile );
|
||
EfsErrPrint("This is a SIS or HSM file.\n");
|
||
return ERROR_ACCESS_DENIED;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
Status = NtQueryVolumeInformationFile(
|
||
hSourceFile,
|
||
&IoStatusBlock,
|
||
&VolInfo,
|
||
sizeof ( FILE_FS_SIZE_INFORMATION ),
|
||
FileFsSizeInformation
|
||
);
|
||
|
||
if (WebDavPath != WEBDAVPATH) {
|
||
|
||
Status1 = NtQueryInformationFile(
|
||
hSourceFile,
|
||
&IoStatusBlock,
|
||
&SourceID,
|
||
sizeof ( FILE_INTERNAL_INFORMATION ),
|
||
FileInternalInformation
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// SourceID not needed for WEB DAV file
|
||
//
|
||
|
||
|
||
Status1 = STATUS_SUCCESS;
|
||
}
|
||
|
||
if ( NT_SUCCESS( Status ) && NT_SUCCESS( Status1 ) ) {
|
||
|
||
/*
|
||
//
|
||
// Get parent directory $EFS
|
||
// We will visit this in Blackcomb
|
||
//
|
||
|
||
RpcRevertToSelf();
|
||
Status = GetParentEfsStream(
|
||
hSourceFile,
|
||
SourceFileNameU,
|
||
&ParentEfsStream
|
||
);
|
||
|
||
*/
|
||
|
||
|
||
if (NT_SUCCESS( Status ) ) {
|
||
|
||
if ( FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
|
||
|
||
if ( FileAttributes & FILE_ATTRIBUTE_COMPRESSED ){
|
||
|
||
USHORT State = COMPRESSION_FORMAT_NONE;
|
||
ULONG Length;
|
||
|
||
//
|
||
// Uncompress the directory first
|
||
//
|
||
|
||
b = DeviceIoControl(hSourceFile,
|
||
FSCTL_SET_COMPRESSION,
|
||
&State,
|
||
sizeof(USHORT),
|
||
NULL,
|
||
0,
|
||
&Length,
|
||
FALSE
|
||
);
|
||
|
||
if ( !b ){
|
||
hResult = GetLastError();
|
||
DebugLog((DEB_WARN, "DeviceIoControl failed, setting hResult = %d\n", hResult ));
|
||
EfsErrPrint("Uncompress the directory failed. Win Error=%d\n",hResult);
|
||
}
|
||
|
||
}
|
||
|
||
if (hResult == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Set_Encrypt on directory
|
||
//
|
||
|
||
//
|
||
// We do not check the operation of the LOG.
|
||
// Should we fail the normal operation just because the LOG operations
|
||
// failed? The answer is probably not. The chance to use the LOG file is very
|
||
// slim. We will use TxF when it is ready and we have time to deal with this.
|
||
//
|
||
|
||
if (LogFileH) {
|
||
|
||
CreateLogHeader(
|
||
LogFileH,
|
||
VolInfo.BytesPerSector,
|
||
&(SourceID.IndexNumber),
|
||
NULL,
|
||
SourceFileName,
|
||
NULL,
|
||
Encrypting,
|
||
BeginEncryptDir,
|
||
NULL
|
||
);
|
||
|
||
}
|
||
|
||
PEFS_KEY Fek ;
|
||
|
||
b = GenerateFEK( &Fek );
|
||
|
||
if (b) {
|
||
|
||
if (ConstructDirectoryEFS( pEfsUserInfo, Fek, &CurrentEfsStream )) {
|
||
|
||
//
|
||
// Prepare an Input data buffer in the form of
|
||
// PSC, [EFS_FC, CSC, SK, H, H, [SK, H, H]sk, $EFS]sk
|
||
//
|
||
|
||
InputDataSize = 2 * sizeof(DriverSessionKey) + 7 * sizeof(ULONG)
|
||
+ CurrentEfsStream->Length;
|
||
|
||
InputData = (PUCHAR)LsapAllocateLsaHeap( InputDataSize );
|
||
|
||
if ( NULL != InputData ) {
|
||
|
||
EfsDataLength = InputDataSize - 3 * sizeof(ULONG);
|
||
( VOID ) SendHandleAndEfs(
|
||
hSourceFile,
|
||
CurrentEfsStream,
|
||
InputData + 3 * sizeof(ULONG),
|
||
&EfsDataLength
|
||
);
|
||
|
||
( VOID ) EncryptFSCTLData(
|
||
EFS_SET_ENCRYPT,
|
||
EFS_ENCRYPT_STREAM,
|
||
EFS_ENCRYPT_DIRSTR,
|
||
InputData + 3*sizeof(ULONG),
|
||
EfsDataLength,
|
||
InputData,
|
||
&InputDataSize
|
||
);
|
||
|
||
Status = NtFsControlFile(
|
||
hSourceFile,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_SET_ENCRYPTION,
|
||
InputData,
|
||
InputDataSize,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
hResult = ERROR_SUCCESS;
|
||
|
||
} else {
|
||
|
||
DebugLog((DEB_ERROR, "EncryptFileSrv: NtFsControlFile failed, status = (%x)\n" ,Status ));
|
||
hResult = RtlNtStatusToDosError( Status );
|
||
|
||
//
|
||
// Make sure the error was mapped
|
||
//
|
||
|
||
if (hResult == ERROR_MR_MID_NOT_FOUND) {
|
||
|
||
DebugLog((DEB_WARN, "Unable to map NT Error (%x) to Win32 error, returning ERROR_ENCRYPTION_FAILED\n" , Status ));
|
||
hResult = ERROR_ENCRYPTION_FAILED;
|
||
}
|
||
EfsErrPrint("Encrypt directory FSCTL failed. Win Error=%d\n",hResult);
|
||
}
|
||
|
||
|
||
LsapFreeLsaHeap( InputData );
|
||
|
||
} else {
|
||
|
||
hResult = ERROR_NOT_ENOUGH_MEMORY;
|
||
EfsErrPrint("FSCTL input data memory allocation failed.\n");
|
||
}
|
||
|
||
LsapFreeLsaHeap( CurrentEfsStream );
|
||
|
||
} else {
|
||
|
||
hResult = GetLastError();
|
||
ASSERT( hResult != ERROR_SUCCESS );
|
||
DebugLog((DEB_WARN, "ConstructDirectoryEFS returned %x to hResult\n" ,hResult ));
|
||
EfsErrPrint("ConstructDirectoryEFS failed. Win Error=%d\n",hResult);
|
||
}
|
||
|
||
LsapFreeLsaHeap( Fek );
|
||
Fek = NULL;
|
||
|
||
} else {
|
||
|
||
hResult = GetLastError();
|
||
ASSERT( hResult != ERROR_SUCCESS );
|
||
EfsErrPrint("Generate directory FEK failed. Win Error=%d\n",hResult);
|
||
}
|
||
|
||
if ((hResult != ERROR_SUCCESS) && ( FileAttributes & FILE_ATTRIBUTE_COMPRESSED )) {
|
||
|
||
//
|
||
// Restore the compression state
|
||
//
|
||
|
||
USHORT State = COMPRESSION_FORMAT_DEFAULT;
|
||
ULONG Length;
|
||
|
||
(VOID) DeviceIoControl(hSourceFile,
|
||
FSCTL_SET_COMPRESSION,
|
||
&State,
|
||
sizeof(USHORT),
|
||
NULL,
|
||
0,
|
||
&Length,
|
||
FALSE
|
||
);
|
||
}
|
||
|
||
//
|
||
// if ConstructDirectoryEFS fail, error will be returned in the end.
|
||
//
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// It's a file, not a directory.
|
||
//
|
||
|
||
//
|
||
// Enumerate all the streams on the file
|
||
//
|
||
|
||
Status = GetStreamInformation(
|
||
hSourceFile,
|
||
&StreamInfoBase, // Free this
|
||
&StreamInfoSize
|
||
);
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
hResult = OpenFileStreams(
|
||
hSourceFile,
|
||
0,
|
||
OPEN_FOR_ENC,
|
||
StreamInfoBase,
|
||
FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
|
||
FILE_OPEN,
|
||
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT,
|
||
&VolInfo,
|
||
&StreamNames, // Free this but not the contents!
|
||
&StreamHandles, // Free this
|
||
&StreamSizes, // Free this
|
||
&StreamCount
|
||
);
|
||
|
||
if (hResult == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// We have handles to all of the streams of the file
|
||
//
|
||
|
||
//
|
||
// Prepare an Input data buffer in the form of
|
||
// PSC, [EFS_FC, CSC, Fek, [Fek]sk, $EFS]sk
|
||
//
|
||
|
||
|
||
PEFS_KEY Fek ;
|
||
|
||
b = GenerateFEK( &Fek );
|
||
|
||
if (b) {
|
||
|
||
b = ConstructEFS(
|
||
pEfsUserInfo,
|
||
Fek,
|
||
ParentEfsStream,
|
||
&CurrentEfsStream
|
||
);
|
||
|
||
if (b) {
|
||
|
||
InputDataSize = 2 * EFS_KEY_SIZE( Fek ) + 3 * sizeof(ULONG)
|
||
+ CurrentEfsStream->Length;
|
||
|
||
InputData = (PUCHAR)LsapAllocateLsaHeap( InputDataSize );
|
||
|
||
if ( NULL != InputData ){
|
||
|
||
//
|
||
// This routine creates the backup file.
|
||
//
|
||
|
||
Status = CreateBackupFile(
|
||
SourceFileNameU,
|
||
&hBackupFile,
|
||
&BackupID,
|
||
&BackupFileName
|
||
);
|
||
|
||
//
|
||
// If power down happens right here before
|
||
// the log file header is written, we will have a zero size
|
||
// temp file left on the disk. This is very unlikely and not
|
||
// a big deal we have a zero size file left on the disk.
|
||
// After we use TxF, this will not even be an issue.
|
||
//
|
||
|
||
if ( NT_SUCCESS(Status) ){
|
||
|
||
if (LogFileH) {
|
||
|
||
Status = CreateLogHeader(
|
||
LogFileH,
|
||
VolInfo.BytesPerSector,
|
||
&(SourceID.IndexNumber),
|
||
&(BackupID.IndexNumber),
|
||
SourceFileName,
|
||
BackupFileName,
|
||
Encrypting,
|
||
BeginEncryptFile,
|
||
&StatusInfoOffset
|
||
);
|
||
|
||
}
|
||
|
||
|
||
LsapFreeLsaHeap( BackupFileName );
|
||
|
||
if ( NT_SUCCESS(Status) ){
|
||
|
||
if (LogFileH){
|
||
|
||
Status = WriteLogFile(
|
||
LogFileH,
|
||
VolInfo.BytesPerSector,
|
||
StatusInfoOffset,
|
||
BeginEncryptFile
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ){
|
||
|
||
//
|
||
// Delete the backup file
|
||
//
|
||
|
||
MarkFileForDelete( hBackupFile );
|
||
CloseHandle( hBackupFile );
|
||
hBackupFile = 0;
|
||
EfsErrPrint("Failed to write the log file.\n");
|
||
}
|
||
}
|
||
|
||
|
||
} else {
|
||
MarkFileForDelete( hBackupFile );
|
||
CloseHandle( hBackupFile );
|
||
hBackupFile = 0;
|
||
EfsErrPrint("Failed to create the Log header.\n");
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Fail to create the backup file
|
||
// Log it.
|
||
//
|
||
|
||
EfsLogEntry(
|
||
EVENTLOG_ERROR_TYPE,
|
||
0,
|
||
EFS_TMP_FILE_ERROR,
|
||
1,
|
||
sizeof(NTSTATUS),
|
||
(LPCWSTR *)&SourceFileName,
|
||
&Status
|
||
);
|
||
|
||
EfsErrPrint("Failed to create the backup file.");
|
||
}
|
||
|
||
if ( NT_SUCCESS(Status) ){
|
||
|
||
EfsDataLength = InputDataSize - 3 * sizeof(ULONG);
|
||
|
||
//
|
||
// Prepare the FSCTL input data
|
||
//
|
||
|
||
( VOID ) SendEfs(
|
||
Fek,
|
||
CurrentEfsStream,
|
||
InputData + 3 * sizeof(ULONG),
|
||
&EfsDataLength
|
||
);
|
||
|
||
|
||
( VOID ) EncryptFSCTLData(
|
||
EFS_SET_ENCRYPT,
|
||
EFS_ENCRYPT_FILE,
|
||
EFS_ENCRYPT_FILE,
|
||
InputData + 3*sizeof(ULONG),
|
||
EfsDataLength,
|
||
InputData,
|
||
&InputDataSize
|
||
);
|
||
|
||
//
|
||
// Write the $EFS and turn on the bits
|
||
//
|
||
|
||
Status = NtFsControlFile(
|
||
hSourceFile,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_SET_ENCRYPTION,
|
||
InputData,
|
||
InputDataSize,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
if ( NT_SUCCESS( Status ) ){
|
||
|
||
//
|
||
// All failures from here on out need to be closed
|
||
// with another FSCTL call.
|
||
//
|
||
|
||
|
||
//
|
||
// Enumerate the streams again, and make sure nothing
|
||
// has changed
|
||
//
|
||
|
||
Status = GetStreamInformation(
|
||
hSourceFile,
|
||
&LaterStreamInfoBase, // Free this
|
||
&LaterStreamInfoSize
|
||
);
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
if (StreamInfoSize == LaterStreamInfoSize) {
|
||
|
||
//
|
||
// Compare the original stream info structure with the one we just got,
|
||
// and make sure they're identical. If not, punt.
|
||
//
|
||
|
||
ULONG rc = StringInfoCmp(StreamInfoBase, LaterStreamInfoBase, StreamInfoSize);
|
||
|
||
if (rc == 0) {
|
||
|
||
//
|
||
// Copy the file to the backup file. Success if target exists
|
||
// (because the make temporary file command creates it).
|
||
//
|
||
|
||
PHANDLE TargetHandles;
|
||
|
||
//
|
||
// Revert to self to be able to open the multiple streams on the backup file
|
||
//
|
||
|
||
RpcRevertToSelf();
|
||
|
||
hResult = MyCopyFile(
|
||
hSourceFile, // handle to the file we're copying from (source file)
|
||
StreamNames, // names of the streams of the source file
|
||
StreamHandles, // handles to the streams of the source file
|
||
StreamSizes, // sizes of the streams of the source file
|
||
StreamCount, // number of streams we're copying, including unnamed data stream
|
||
hBackupFile, // Backup file handle
|
||
&TargetHandles // new handles of corresponding streams on backup file
|
||
);
|
||
|
||
//
|
||
// Even the impersonation failed, it is OK. We already got all the handles we need.
|
||
//
|
||
|
||
( VOID ) RpcImpersonateClient( NULL );
|
||
|
||
if (hResult == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// The backup file now exists and has data in it.
|
||
// We do not check the error code of WriteLogFile for the following reasons,
|
||
// 1. We are overwriting the sectors, out of disk space is not possible. The sectors have
|
||
// just been written, defective sector is very unlikely.
|
||
// 2. In case, the sector becomes defective between the two writes. More than 99.99%
|
||
// we can still finish the job without any problem.
|
||
// 3. In real life, it is very unlikely that power down or crash happens here and the sectors
|
||
// just become defective right after last write.
|
||
//
|
||
// When TxF is used later, this will not be an issue.
|
||
//
|
||
|
||
if (LogFileH){
|
||
|
||
( VOID )WriteLogFile(
|
||
LogFileH,
|
||
VolInfo.BytesPerSector,
|
||
StatusInfoOffset,
|
||
EncryptTmpFileWritten
|
||
);
|
||
|
||
}
|
||
|
||
hResult = CheckOpenSection(
|
||
StreamSizes,
|
||
StreamHandles,
|
||
StreamCount
|
||
);
|
||
|
||
if ( ERROR_SUCCESS == hResult ){
|
||
hResult = CompressStreams(
|
||
StreamSizes,
|
||
StreamHandles,
|
||
COMPRESSION_FORMAT_NONE,
|
||
StreamCount
|
||
);
|
||
if (ERROR_SUCCESS != hResult) {
|
||
EfsErrPrint("CompressStreams Failed. Win Error=%d\n",hResult);
|
||
}
|
||
} else {
|
||
|
||
DebugLog((DEB_ERROR, "CompressStreams returned %d\n" ,hResult ));
|
||
|
||
EfsErrPrint("Failed to check the open section. Win Error=%d\n",hResult);
|
||
}
|
||
|
||
|
||
if ( ERROR_SUCCESS == hResult ){
|
||
//
|
||
// Reuse the input data buffer for each stream
|
||
// FSCTL call.
|
||
//
|
||
|
||
( VOID )SendEfs(
|
||
Fek,
|
||
CurrentEfsStream,
|
||
InputData + 3 * sizeof(ULONG),
|
||
&EfsDataLength
|
||
);
|
||
|
||
|
||
( VOID ) EncryptFSCTLData(
|
||
EFS_SET_ENCRYPT,
|
||
EFS_ENCRYPT_STREAM,
|
||
EFS_ENCRYPT_STREAM,
|
||
InputData + 3*sizeof(ULONG),
|
||
EfsDataLength,
|
||
InputData,
|
||
&InputDataSize
|
||
);
|
||
|
||
//
|
||
// Copy each stream from the backup to the original.
|
||
// CopyFileStreams attempts to undo things in case of problems,
|
||
// so we just have to report success or failure.
|
||
//
|
||
|
||
hResult = CopyFileStreams(
|
||
TargetHandles, // handles to streams on the backup file
|
||
StreamHandles, // handles to streams on the source file
|
||
StreamCount, // number of streams
|
||
StreamSizes, // sizes of streams
|
||
Encrypting, // mark StreamHandles as Encrypted before copy
|
||
InputData, // FSCTL input data
|
||
InputDataSize, // FSCTL input data size
|
||
&CleanupSuccessful
|
||
);
|
||
|
||
if (hResult != ERROR_SUCCESS) {
|
||
EfsErrPrint("CopyFileStreams Failed. Win Error=%d\n",hResult);
|
||
DebugLog((DEB_ERROR, "CopyFileStreams returned %d\n" ,hResult ));
|
||
}
|
||
}
|
||
|
||
LsapFreeLsaHeap( Fek );
|
||
Fek = NULL;
|
||
|
||
if (hResult == ERROR_SUCCESS || CleanupSuccessful) {
|
||
|
||
if (hResult == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Encryption is almost done. The file is still in a transit status
|
||
// No error checking for writing the log file for the same reason
|
||
// mentioned above.
|
||
//
|
||
|
||
if (LogFileH){
|
||
|
||
( VOID )WriteLogFile(
|
||
LogFileH,
|
||
VolInfo.BytesPerSector,
|
||
StatusInfoOffset,
|
||
EncryptionDone
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Reuse the InputData buffer.
|
||
// FSCTL Mark encryption success
|
||
//
|
||
|
||
Status = SendSkFsctl(
|
||
0,
|
||
0,
|
||
EFS_ENCRYPT_DONE,
|
||
InputData,
|
||
InputDataSize,
|
||
hSourceFile,
|
||
FSCTL_ENCRYPTION_FSCTL_IO,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
} else {
|
||
//
|
||
// FSCTL Fail Encrypting. We can reuse the InputData.
|
||
// No stream has been encrypted yet.
|
||
// Decrypt File will do the trick to restore the file status.
|
||
//
|
||
|
||
if (LogFileH){
|
||
|
||
( VOID )WriteLogFile(
|
||
LogFileH,
|
||
VolInfo.BytesPerSector,
|
||
StatusInfoOffset,
|
||
EncryptionBackout
|
||
);
|
||
|
||
}
|
||
|
||
( VOID ) EndErrorEncryptFile(
|
||
hSourceFile,
|
||
InputData,
|
||
InputDataSize,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
}
|
||
|
||
if (LogFileH){
|
||
|
||
( VOID )WriteLogFile(
|
||
LogFileH,
|
||
VolInfo.BytesPerSector,
|
||
StatusInfoOffset,
|
||
EncryptionSrcDone
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Either the operation worked, or it failed but we managed to clean
|
||
// up after ourselves. In either case, we no longer need the backup
|
||
// file or the log file.
|
||
//
|
||
// Delete the backup file first.
|
||
//
|
||
|
||
LONG j;
|
||
// Do we need delete each stream?
|
||
for (j=StreamCount - 1 ; j >= 0 ; j--) {
|
||
MarkFileForDelete( TargetHandles[j] );
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// The operation failed and we couldn't clean up. Keep the
|
||
// log file and the backup file around so we can retry on
|
||
// reboot.
|
||
//
|
||
|
||
if (LogFileH){
|
||
|
||
KeepLogFile = TRUE;
|
||
|
||
( VOID )WriteLogFile(
|
||
LogFileH,
|
||
VolInfo.BytesPerSector,
|
||
StatusInfoOffset,
|
||
EncryptionMessup
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Regardless of what happened, we don't need these any more.
|
||
//
|
||
|
||
LONG j;
|
||
|
||
for ( j = StreamCount -1 ; j >=0; j--) {
|
||
CloseHandle( TargetHandles[j] );
|
||
}
|
||
hBackupFile = 0;
|
||
|
||
LsapFreeLsaHeap( TargetHandles );
|
||
|
||
} else {
|
||
|
||
//
|
||
// We couldn't copy everything to the backup file.
|
||
// No need to record error log information after this point.
|
||
// Tell the driver that the operation has failed.
|
||
//
|
||
// MyCopyFile has already taken care of cleaning up the
|
||
// backup file.
|
||
//
|
||
hBackupFile = 0;
|
||
DebugLog((DEB_ERROR, "MyCopyFile failed, error = %d\n" ,hResult ));
|
||
|
||
LsapFreeLsaHeap( Fek );
|
||
Fek = NULL;
|
||
//
|
||
// FSCTL Fail Encrypting. We can reuse the InputData.
|
||
// No stream has been encrypted yet.
|
||
// Decrypt File will do the trick to restore the file status.
|
||
//
|
||
|
||
//
|
||
// We ignore the return status from FSCTL call,
|
||
// If it is failed, the only way to restore the status
|
||
// is by rebooting the system.
|
||
//
|
||
// Robert Reichel, you need think about what happened if
|
||
// the following call failed, although it is very unlikely.
|
||
// If the call fails and the log file gets removed, the file
|
||
// will be unaccessible. Convert the return code to hResult is
|
||
// obviously not correct.
|
||
//
|
||
|
||
( VOID ) EndErrorEncryptFile(
|
||
hSourceFile,
|
||
InputData,
|
||
InputDataSize,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
EfsErrPrint("Failed to make a copy of the file. Win Error=%d\n",hResult);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Somebody added/removed stream(s).
|
||
// FSCTL Fail Encrypting. We can reuse the InputData.
|
||
// No stream has been encrypted yet.
|
||
// Decrypt File will do the trick to restore the file status.
|
||
//
|
||
MarkFileForDelete( hBackupFile );
|
||
|
||
LsapFreeLsaHeap( Fek );
|
||
Fek = NULL;
|
||
( VOID ) EndErrorEncryptFile(
|
||
hSourceFile,
|
||
InputData,
|
||
InputDataSize,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
hResult = ERROR_ENCRYPTION_FAILED;
|
||
EfsErrPrint("Streams changed while doing encryption, StringInfoCmp(). Win Error=%d\n",hResult);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Stream info size changed
|
||
// FSCTL Fail Encrypting. We can reuse the InputData.
|
||
// No stream has been encrypted yet.
|
||
// Decrypt File will do the trick to restore the file status.
|
||
//
|
||
MarkFileForDelete( hBackupFile );
|
||
|
||
LsapFreeLsaHeap( Fek );
|
||
Fek = NULL;
|
||
//
|
||
// We ignore the return status from FSCTL call,
|
||
// If it is failed, the only way to restore the status
|
||
// is by rebooting the system.
|
||
//
|
||
// Robert Reichel, you need think about what happened if
|
||
// the following call failed, although it is very unlikely.
|
||
// If the call fails and the log file gets removed, the file
|
||
// will be unaccessible. Convert the return code to hResult is
|
||
// obviously not correct.
|
||
//
|
||
|
||
( VOID ) EndErrorEncryptFile(
|
||
hSourceFile,
|
||
InputData,
|
||
InputDataSize,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
hResult = ERROR_ENCRYPTION_FAILED;
|
||
EfsErrPrint("Streams changed while doing encryption, Size not eaqual. Win Error=%d\n",hResult);
|
||
}
|
||
|
||
LsapFreeLsaHeap( LaterStreamInfoBase );
|
||
|
||
} else {
|
||
|
||
//
|
||
// GetStreamInformation() Failed
|
||
// FSCTL Fail Encrypting. We can reuse the InputData.
|
||
// No stream has been encrypted yet.
|
||
// Decrypt File will do the trick to restore the file status.
|
||
//
|
||
MarkFileForDelete( hBackupFile );
|
||
|
||
( VOID ) EndErrorEncryptFile(
|
||
hSourceFile,
|
||
InputData,
|
||
InputDataSize,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
LsapFreeLsaHeap( Fek );
|
||
Fek = NULL;
|
||
hResult = RtlNtStatusToDosError( Status );
|
||
DebugLog((DEB_ERROR, "GetStreamInformation failed, error = %d\n" ,hResult ));
|
||
EfsErrPrint("Cannot get the Streams for verification. Win Error=%d\n",hResult);
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Set encryption failed. No bits are turned on.
|
||
// Only the unnamed data stream is created in temp file
|
||
//
|
||
MarkFileForDelete( hBackupFile );
|
||
|
||
LsapFreeLsaHeap( Fek );
|
||
Fek = NULL;
|
||
DebugLog((DEB_ERROR, "EncryptFileSrv: NtFsControlFile failed, status = (%x)\n" ,Status ));
|
||
hResult = RtlNtStatusToDosError( Status );
|
||
|
||
//
|
||
// Make sure the error was mapped
|
||
//
|
||
|
||
if (hResult == ERROR_MR_MID_NOT_FOUND) {
|
||
|
||
DebugLog((DEB_WARN, "Unable to map NT Error (%x) to Win32 error, returning ERROR_ENCRYPTION_FAILED\n" , Status ));
|
||
hResult = ERROR_ENCRYPTION_FAILED;
|
||
}
|
||
EfsErrPrint("Failed to write the $EFS or turn on the bit. Win Error=%d\n",hResult);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Create Backup File failed or write log file failed
|
||
// Temp file is already deleted.
|
||
//
|
||
LsapFreeLsaHeap( Fek );
|
||
Fek = NULL;
|
||
DebugLog((DEB_ERROR, "EncryptFileSrv: CreateBackupFile or CreateLogHeader failed, status = (%x)\n" ,Status ));
|
||
hResult = RtlNtStatusToDosError( Status );
|
||
|
||
//
|
||
// Make sure the error was mapped
|
||
//
|
||
|
||
if (hResult == ERROR_MR_MID_NOT_FOUND) {
|
||
|
||
DebugLog((DEB_WARN, "Unable to map NT Error (%x) to Win32 error, returning ERROR_ENCRYPTION_FAILED\n" , Status ));
|
||
hResult = ERROR_ENCRYPTION_FAILED;
|
||
}
|
||
|
||
}
|
||
|
||
if ( hBackupFile ){
|
||
|
||
CloseHandle( hBackupFile );
|
||
hBackupFile = 0;
|
||
|
||
}
|
||
|
||
if ( InputData ){
|
||
|
||
LsapFreeLsaHeap( InputData );
|
||
}
|
||
|
||
} else {
|
||
|
||
LsapFreeLsaHeap( Fek );
|
||
Fek = NULL;
|
||
hResult = ERROR_NOT_ENOUGH_MEMORY;
|
||
EfsErrPrint("Out of memory.\n");
|
||
}
|
||
|
||
LsapFreeLsaHeap( CurrentEfsStream );
|
||
|
||
} else {
|
||
|
||
hResult = GetLastError();
|
||
|
||
DebugLog((DEB_ERROR, "ConstructEFS returned %x\n" ,hResult ));
|
||
LsapFreeLsaHeap( Fek );
|
||
Fek = NULL;
|
||
|
||
EfsErrPrint("Failed to generate the FEK. Win Error=%d\n",hResult);
|
||
}
|
||
|
||
} else {
|
||
|
||
hResult = GetLastError();
|
||
|
||
EfsErrPrint("Failed to generate the FEK. Win Error=%d\n",hResult);
|
||
}
|
||
|
||
CleanupOpenFileStreams(
|
||
StreamHandles,
|
||
StreamNames,
|
||
StreamSizes,
|
||
NULL,
|
||
hSourceFile,
|
||
StreamCount
|
||
);
|
||
|
||
StreamHandles = NULL;
|
||
StreamNames = NULL;
|
||
hSourceFile = NULL;
|
||
|
||
} else {
|
||
|
||
DebugLog((DEB_ERROR, "OpenFileStreams returned %d\n" ,hResult ));
|
||
EfsErrPrint("Failed to open all the streams. Win Error=%d\n",hResult);
|
||
}
|
||
|
||
|
||
LsapFreeLsaHeap( StreamInfoBase );
|
||
StreamInfoBase = NULL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Unable to get stream information
|
||
//
|
||
|
||
DebugLog((DEB_ERROR, "Unable to obtain stream information, status = %x\n" ,Status ));
|
||
|
||
hResult = RtlNtStatusToDosError( Status );
|
||
|
||
//
|
||
// Make sure the error was mapped
|
||
//
|
||
|
||
if (hResult == ERROR_MR_MID_NOT_FOUND) {
|
||
|
||
DebugLog((DEB_WARN, "Unable to map NT Error (%x) to Win32 error, returning ERROR_ENCRYPTION_FAILED\n" , Status ));
|
||
hResult = ERROR_ENCRYPTION_FAILED;
|
||
}
|
||
EfsErrPrint("Failed to get all streams. Win Error=%d\n",hResult);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free ParentEfsStream
|
||
//
|
||
|
||
if ( ParentEfsStream ){
|
||
|
||
LsapFreeLsaHeap( ParentEfsStream );
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// GetParentEfsStream failed
|
||
//
|
||
|
||
|
||
hResult = RtlNtStatusToDosError( Status );
|
||
|
||
//
|
||
// Make sure the error was mapped
|
||
//
|
||
|
||
if (hResult == ERROR_MR_MID_NOT_FOUND) {
|
||
|
||
DebugLog((DEB_WARN, "Unable to map NT Error (%x) to Win32 error, returning ERROR_ENCRYPTION_FAILED\n" , Status ));
|
||
hResult = ERROR_ENCRYPTION_FAILED;
|
||
}
|
||
EfsErrPrint("GetParentEfsStream failed. Win Error=%d\n",hResult);
|
||
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Either get volume info failed or get target file ID failed
|
||
//
|
||
if ( !NT_SUCCESS(Status1) ){
|
||
Status = Status1;
|
||
}
|
||
hResult = RtlNtStatusToDosError( Status );
|
||
|
||
//
|
||
// Make sure the error was mapped
|
||
//
|
||
|
||
if (hResult == ERROR_MR_MID_NOT_FOUND) {
|
||
|
||
DebugLog((DEB_WARN, "Unable to map NT Error (%x) to Win32 error, returning ERROR_ENCRYPTION_FAILED\n" , Status ));
|
||
hResult = ERROR_ENCRYPTION_FAILED;
|
||
}
|
||
|
||
EfsErrPrint("Either get volume info failed or get target file ID failed. Win Error=%d\n",hResult);
|
||
}
|
||
|
||
if ( hSourceFile ){
|
||
|
||
CloseHandle( hSourceFile );
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Open source file failed
|
||
//
|
||
|
||
hResult = GetLastError();
|
||
EfsErrPrint("EFS Open source file failed. Win Error=%d FileName=%ws\n", hResult, SourceFileName);
|
||
}
|
||
|
||
if (!KeepLogFile && LogFileH) {
|
||
//
|
||
// Delete the Log File
|
||
//
|
||
|
||
MarkFileForDelete( LogFileH );
|
||
}
|
||
|
||
|
||
LsapFreeLsaHeap( SourceFileName );
|
||
|
||
if (hResult != ERROR_SUCCESS) {
|
||
DebugLog((DEB_WARN, "EncryptFileSrv returning %x\n", hResult ));
|
||
}
|
||
|
||
return( hResult );
|
||
}
|
||
|
||
|
||
|
||
DWORD
|
||
DecryptFileSrv(
|
||
IN PUNICODE_STRING SourceFileNameU,
|
||
IN HANDLE LogFileH,
|
||
IN ULONG Recovery
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the top level routine of the EncryptFile API. It
|
||
opens the passed source file and copies all of its data streams to
|
||
a backup file in a known location. It then marks all of the streams
|
||
of the source as encrypted, and copies them back.
|
||
|
||
Arguments:
|
||
|
||
SourceFileName - Supplies a Unicode string with the name of
|
||
the file to be encrypted.
|
||
|
||
LogFileH - Log file handle. Log file is zero size when passed in.
|
||
|
||
Recovery - If the decryption is for recovery or not
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS on success, other on failure.
|
||
|
||
--*/
|
||
{
|
||
|
||
BOOL b = TRUE;
|
||
BOOLEAN CleanupSuccessful = FALSE;
|
||
BOOLEAN KeepLogFile = FALSE;
|
||
|
||
ULONG StatusInfoOffset = 0 ;
|
||
DWORD hResult = ERROR_SUCCESS;
|
||
DWORD FileAttributes;
|
||
|
||
HANDLE FileHandle;
|
||
HANDLE hSourceFile = NULL ;
|
||
HANDLE hBackupFile = 0;
|
||
PHANDLE StreamHandles = NULL;
|
||
|
||
LPWSTR SourceFileName;
|
||
LPWSTR BackupFileName;
|
||
|
||
FILE_FS_SIZE_INFORMATION VolInfo;
|
||
FILE_INTERNAL_INFORMATION SourceID;
|
||
FILE_INTERNAL_INFORMATION BackupID;
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
OBJECT_ATTRIBUTES Obja;
|
||
|
||
PFILE_STREAM_INFORMATION LaterStreamInfoBase = NULL;
|
||
PFILE_STREAM_INFORMATION StreamInfoBase = NULL;
|
||
|
||
PEFS_STREAM_SIZE StreamSizes = NULL;
|
||
|
||
PUNICODE_STRING StreamNames = NULL;
|
||
|
||
UINT TmpResult;
|
||
|
||
ULONG LaterStreamInfoSize = 0;
|
||
ULONG StreamCount = 0;
|
||
ULONG StreamInfoSize = 0;
|
||
ULONG i;
|
||
DWORD tmpDW;
|
||
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
ULONG InputDataSize;
|
||
ULONG EfsDataLength;
|
||
PUCHAR InputData;
|
||
ULONG CreateOption = FILE_SYNCHRONOUS_IO_NONALERT;
|
||
UNICODE_STRING SrcNtName;
|
||
WORD WebDavPath = 0;
|
||
|
||
if (!LogFileH) {
|
||
|
||
//
|
||
// No LogFile means WEBDAVPATH
|
||
//
|
||
|
||
WebDavPath = WEBDAVPATH;
|
||
}
|
||
|
||
//
|
||
// Convert the source file name into an LPWSTR
|
||
//
|
||
|
||
SourceFileName = (LPWSTR)LsapAllocateLsaHeap( SourceFileNameU->Length + sizeof( UNICODE_NULL ));
|
||
|
||
if (SourceFileName == NULL) {
|
||
|
||
if (LogFileH) {
|
||
MarkFileForDelete( LogFileH );
|
||
}
|
||
return( ERROR_NOT_ENOUGH_MEMORY );
|
||
}
|
||
|
||
InputDataSize = 7 * sizeof ( ULONG ) + 2 * sizeof ( DriverSessionKey );
|
||
InputData = (PUCHAR)LsapAllocateLsaHeap( InputDataSize );
|
||
if ( InputData == NULL ) {
|
||
|
||
if (LogFileH) {
|
||
MarkFileForDelete( LogFileH );
|
||
}
|
||
LsapFreeLsaHeap( SourceFileName );
|
||
return( ERROR_NOT_ENOUGH_MEMORY );
|
||
}
|
||
EfsDataLength = InputDataSize - 3 * sizeof ( ULONG );
|
||
|
||
|
||
SourceFileName[SourceFileNameU->Length/sizeof(WCHAR)] = UNICODE_NULL;
|
||
RtlCopyMemory( SourceFileName, SourceFileNameU->Buffer, SourceFileNameU->Length );
|
||
|
||
DebugLog((DEB_TRACE_EFS, "Decrypting file %ws \n", SourceFileName));
|
||
|
||
FileAttributes = GetFileAttributes( SourceFileName );
|
||
|
||
if (FileAttributes == -1) {
|
||
|
||
if (LogFileH) {
|
||
MarkFileForDelete( LogFileH );
|
||
}
|
||
LsapFreeLsaHeap( SourceFileName );
|
||
LsapFreeLsaHeap( InputData );
|
||
return GetLastError();
|
||
|
||
}
|
||
|
||
if (!(FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||
CreateOption |= FILE_OPEN_REPARSE_POINT;
|
||
}
|
||
|
||
b = RtlDosPathNameToNtPathName_U(
|
||
SourceFileName,
|
||
&SrcNtName,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if ( b ){
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&SrcNtName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
Status = NtCreateFile(
|
||
&hSourceFile,
|
||
FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
0,
|
||
0,
|
||
FILE_OPEN,
|
||
CreateOption,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
//
|
||
// Free the NT name
|
||
//
|
||
RtlFreeHeap(
|
||
RtlProcessHeap(),
|
||
0,
|
||
SrcNtName.Buffer
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// The error code here is not quite accurate here. It is very unlikely
|
||
// getting here. Possibly out of memory
|
||
//
|
||
|
||
Status = STATUS_ENCRYPTION_FAILED;
|
||
hResult = GetLastError();
|
||
|
||
}
|
||
|
||
if ( NT_SUCCESS( Status ) ) {
|
||
|
||
//
|
||
// Determine if this path is to a directory or a file. If it's a directory,
|
||
// we have very little to do.
|
||
//
|
||
|
||
Status = NtQueryVolumeInformationFile(
|
||
hSourceFile,
|
||
&IoStatusBlock,
|
||
&VolInfo,
|
||
sizeof ( FILE_FS_SIZE_INFORMATION ),
|
||
FileFsSizeInformation
|
||
);
|
||
|
||
if ( NT_SUCCESS( Status ) && (WebDavPath != WEBDAVPATH) ){
|
||
|
||
Status = NtQueryInformationFile(
|
||
hSourceFile,
|
||
&IoStatusBlock,
|
||
&SourceID,
|
||
sizeof ( FILE_INTERNAL_INFORMATION ),
|
||
FileInternalInformation
|
||
);
|
||
|
||
}
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
|
||
CloseHandle( hSourceFile );
|
||
if (LogFileH) {
|
||
MarkFileForDelete( LogFileH );
|
||
}
|
||
LsapFreeLsaHeap( SourceFileName );
|
||
LsapFreeLsaHeap( InputData );
|
||
hResult = RtlNtStatusToDosError( Status );
|
||
|
||
//
|
||
// Make sure the error was mapped
|
||
//
|
||
|
||
if (hResult == ERROR_MR_MID_NOT_FOUND) {
|
||
|
||
DebugLog((DEB_WARN, "Unable to map NT Error (%x) to Win32 error, returning ERROR_ENCRYPTION_FAILED\n" , Status ));
|
||
hResult = ERROR_DECRYPTION_FAILED;
|
||
}
|
||
|
||
return hResult;
|
||
}
|
||
|
||
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
|
||
//
|
||
// We do not check the operation of the LOG.
|
||
// Should we fail the normal operation just because the LOG operations
|
||
// failed? The answer is probably not. The chance to use the LOG file is very
|
||
// slim. We will use TxF when it is ready and we have time to deal with this.
|
||
//
|
||
|
||
if (LogFileH) {
|
||
CreateLogHeader(
|
||
LogFileH,
|
||
VolInfo.BytesPerSector,
|
||
&(SourceID.IndexNumber),
|
||
NULL,
|
||
SourceFileName,
|
||
NULL,
|
||
Decrypting,
|
||
BeginDecryptDir,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
//
|
||
// FSCTL Do Directory stuff
|
||
//
|
||
|
||
|
||
Status = SendSkFsctl(
|
||
EFS_DECRYPT_STREAM,
|
||
EFS_DECRYPT_DIRSTR,
|
||
EFS_SET_ENCRYPT,
|
||
InputData,
|
||
InputDataSize,
|
||
hSourceFile,
|
||
FSCTL_SET_ENCRYPTION,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
if ( NT_SUCCESS( Status ) && IoStatusBlock.Information ){
|
||
|
||
//
|
||
// IoStatusBlock.Information != 0 means there is no more encrypted
|
||
// streams in the file (or dir).
|
||
// If the following call failed, we could hardly restore the dir to the
|
||
// perfect condition. It is very unlikely to fail here while we
|
||
// succeed above.
|
||
//
|
||
|
||
Status = SendSkFsctl(
|
||
EFS_DECRYPT_FILE,
|
||
EFS_DECRYPT_DIRFILE,
|
||
EFS_SET_ENCRYPT,
|
||
InputData,
|
||
InputDataSize,
|
||
hSourceFile,
|
||
FSCTL_SET_ENCRYPTION,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
} else if ( NT_SUCCESS( Status ) && (IoStatusBlock.Information == 0)) {
|
||
|
||
//
|
||
// More than 1 streams on the directory were encrypted. Log it.
|
||
//
|
||
|
||
|
||
EfsLogEntry(
|
||
EVENTLOG_ERROR_TYPE,
|
||
0,
|
||
EFS_DIR_MULTISTR_ERROR,
|
||
1,
|
||
0,
|
||
(LPCWSTR *)&SourceFileName,
|
||
NULL
|
||
);
|
||
|
||
}
|
||
|
||
|
||
CloseHandle( hSourceFile );
|
||
if (LogFileH) {
|
||
MarkFileForDelete( LogFileH );
|
||
}
|
||
LsapFreeLsaHeap( SourceFileName );
|
||
LsapFreeLsaHeap( InputData );
|
||
if ( NT_SUCCESS( Status ) ){
|
||
|
||
return( ERROR_SUCCESS );
|
||
|
||
} else {
|
||
|
||
return(ERROR_DECRYPTION_FAILED);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Enumerate all the streams on the file
|
||
//
|
||
|
||
Status = GetStreamInformation(
|
||
hSourceFile,
|
||
&StreamInfoBase, // Free this
|
||
&StreamInfoSize
|
||
);
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
hResult = OpenFileStreams(
|
||
hSourceFile,
|
||
0,
|
||
OPEN_FOR_DEC,
|
||
StreamInfoBase,
|
||
FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
|
||
FILE_OPEN,
|
||
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT,
|
||
&VolInfo,
|
||
&StreamNames, // Don't free this!
|
||
&StreamHandles, // Free this
|
||
&StreamSizes, // Free this
|
||
&StreamCount
|
||
);
|
||
|
||
if (hResult == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// This routine creates the backup file.
|
||
//
|
||
|
||
Status = CreateBackupFile(
|
||
SourceFileNameU,
|
||
&hBackupFile,
|
||
&BackupID,
|
||
&BackupFileName
|
||
);
|
||
|
||
//
|
||
// If power down happens right here before
|
||
// the log file header is written, we will have a zero size
|
||
// temp file left on the disk. This is very unlikely and not
|
||
// a big deal we have a zero size file left on the disk.
|
||
//
|
||
|
||
if ( NT_SUCCESS(Status) ){
|
||
|
||
if (LogFileH) {
|
||
Status = CreateLogHeader(
|
||
LogFileH,
|
||
VolInfo.BytesPerSector,
|
||
&(SourceID.IndexNumber),
|
||
&(BackupID.IndexNumber),
|
||
SourceFileName,
|
||
BackupFileName,
|
||
Decrypting,
|
||
BeginDecryptFile,
|
||
&StatusInfoOffset
|
||
);
|
||
}
|
||
|
||
//
|
||
// BackupFile name not needed any more.
|
||
//
|
||
|
||
LsapFreeLsaHeap( BackupFileName );
|
||
|
||
if ( NT_SUCCESS(Status) ){
|
||
|
||
if (LogFileH) {
|
||
Status = WriteLogFile(
|
||
LogFileH,
|
||
VolInfo.BytesPerSector,
|
||
StatusInfoOffset,
|
||
BeginDecryptFile
|
||
);
|
||
}
|
||
|
||
if ( !NT_SUCCESS(Status) ){
|
||
|
||
//
|
||
// Delete the backup file
|
||
//
|
||
|
||
MarkFileForDelete( hBackupFile );
|
||
CloseHandle( hBackupFile );
|
||
hBackupFile = 0;
|
||
}
|
||
|
||
} else {
|
||
MarkFileForDelete( hBackupFile );
|
||
CloseHandle( hBackupFile );
|
||
hBackupFile = 0;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Fail to create the backup file
|
||
// Log it.
|
||
//
|
||
|
||
|
||
EfsLogEntry(
|
||
EVENTLOG_ERROR_TYPE,
|
||
0,
|
||
EFS_TMP_FILE_ERROR,
|
||
1,
|
||
sizeof(NTSTATUS),
|
||
(LPCWSTR *)&SourceFileName,
|
||
&Status
|
||
);
|
||
|
||
}
|
||
|
||
if ( NT_SUCCESS(Status) ){
|
||
|
||
//
|
||
// We have handles to all of the streams of the file
|
||
//
|
||
// Enter "Decrypting" state here. FSCTL call.
|
||
//
|
||
// All failures from here on out need to be closed
|
||
// with another FSCTL call.
|
||
//
|
||
Status = SendSkFsctl(
|
||
0,
|
||
0,
|
||
EFS_DECRYPT_BEGIN,
|
||
InputData,
|
||
InputDataSize,
|
||
hSourceFile,
|
||
FSCTL_ENCRYPTION_FSCTL_IO,
|
||
&IoStatusBlock
|
||
);
|
||
}
|
||
|
||
if ( !NT_SUCCESS( Status ) ){
|
||
|
||
//
|
||
// Nothing has been done to the file.
|
||
//
|
||
|
||
CleanupOpenFileStreams(
|
||
StreamHandles,
|
||
StreamNames,
|
||
StreamSizes,
|
||
StreamInfoBase,
|
||
hSourceFile,
|
||
StreamCount
|
||
);
|
||
|
||
StreamHandles = NULL;
|
||
StreamNames = NULL;
|
||
StreamInfoBase = NULL;
|
||
if (LogFileH) {
|
||
MarkFileForDelete( LogFileH );
|
||
}
|
||
if ( hBackupFile ) {
|
||
MarkFileForDelete( hBackupFile );
|
||
CloseHandle( hBackupFile );
|
||
}
|
||
LsapFreeLsaHeap( SourceFileName );
|
||
LsapFreeLsaHeap( InputData );
|
||
|
||
hResult = RtlNtStatusToDosError( Status );
|
||
|
||
//
|
||
// Make sure the error was mapped
|
||
//
|
||
|
||
if (hResult == ERROR_MR_MID_NOT_FOUND) {
|
||
|
||
DebugLog((DEB_WARN, "Unable to map NT Error (%x) to Win32 error, returning ERROR_ENCRYPTION_FAILED\n" , Status ));
|
||
hResult = ERROR_DECRYPTION_FAILED;
|
||
}
|
||
|
||
return hResult;
|
||
}
|
||
|
||
//
|
||
// Enumerate the streams again, and make sure nothing
|
||
// has changed. From this point on, the file is in TRANSITION
|
||
// status. Any failure should not delete the logfile, unless the FSCTL
|
||
// EFS_ENCRYPT_DONE is called
|
||
//
|
||
|
||
Status = GetStreamInformation(
|
||
hSourceFile,
|
||
&LaterStreamInfoBase, // Free this
|
||
&LaterStreamInfoSize
|
||
);
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
if (StreamInfoSize == LaterStreamInfoSize) {
|
||
|
||
//
|
||
// Compare the original stream info structure with the one we just got,
|
||
// and make sure they're identical. If not, punt.
|
||
//
|
||
|
||
ULONG rc = StringInfoCmp(StreamInfoBase, LaterStreamInfoBase, StreamInfoSize);
|
||
|
||
if (rc == 0) {
|
||
|
||
//
|
||
// Copy the file to the backup file. Success if target exists
|
||
// (because the make temporary file command creates it).
|
||
//
|
||
|
||
PHANDLE TargetHandles = NULL;
|
||
|
||
//
|
||
// Revert to self to open multiple streams on the backup file
|
||
//
|
||
|
||
RpcRevertToSelf();
|
||
|
||
hResult = MyCopyFile(
|
||
hSourceFile, // handle to the file we're copying from (source file)
|
||
StreamNames, // names of the streams of the source file
|
||
StreamHandles, // handles to the streams of the source file
|
||
StreamSizes, // sizes of the streams of the source file
|
||
StreamCount, // number of streams we're copying, including unnamed data stream
|
||
hBackupFile, // Backup file handle
|
||
&TargetHandles // new handles of corresponding streams on backup file
|
||
);
|
||
|
||
|
||
//
|
||
// Even the impersonation failed, it is OK. We already got all the handles we need.
|
||
//
|
||
|
||
( VOID ) RpcImpersonateClient( NULL );
|
||
|
||
if (hResult == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// The backup file now exists and has data in it.
|
||
// We do not check the error code of WriteLogFile for the following reasons,
|
||
// 1. We are overwriting the sectors, out of disk space is not possible. The sectors have
|
||
// just been written, defective sector is very unlikely.
|
||
// 2. In case, the sector becomes defective between the two writes. More than 99.99%
|
||
// we can still finish the job without any problem.
|
||
// 3. In real life, it is very unlikely that power down or crash happens here and the sectors
|
||
// just become defective right after last write.
|
||
//
|
||
// When later TxF is used, we will not manage the log file.
|
||
//
|
||
|
||
if (LogFileH) {
|
||
( VOID )WriteLogFile(
|
||
LogFileH,
|
||
VolInfo.BytesPerSector,
|
||
StatusInfoOffset,
|
||
DecryptTmpFileWritten
|
||
);
|
||
}
|
||
|
||
|
||
hResult = CheckOpenSection(
|
||
StreamSizes,
|
||
StreamHandles,
|
||
StreamCount
|
||
);
|
||
|
||
if ( ERROR_SUCCESS == hResult ){
|
||
|
||
//
|
||
// Reuse the input data buffer for each stream
|
||
// FSCTL call.
|
||
//
|
||
|
||
( VOID )SendHandle(
|
||
hSourceFile,
|
||
InputData + 3 * sizeof( ULONG ),
|
||
&EfsDataLength
|
||
);
|
||
|
||
( VOID ) EncryptFSCTLData(
|
||
EFS_SET_ENCRYPT,
|
||
EFS_DECRYPT_STREAM,
|
||
EFS_DECRYPT_STREAM,
|
||
InputData + 3 * sizeof(ULONG),
|
||
EfsDataLength,
|
||
InputData,
|
||
&InputDataSize
|
||
);
|
||
|
||
//
|
||
// Copy each stream from the backup to the original.
|
||
// CopyFileStreams attempts to undo things in case of problems,
|
||
// so we just have to report success or failure.
|
||
//
|
||
|
||
hResult = CopyFileStreams(
|
||
TargetHandles, // handles to streams on the backup file
|
||
StreamHandles, // handles to streams on the source file
|
||
StreamCount, // number of streams
|
||
StreamSizes, // sizes of streams
|
||
Decrypting, // mark StreamHandles as Encrypted before copy
|
||
InputData, // FSCTL input data
|
||
InputDataSize, // FSCTL input data size
|
||
&CleanupSuccessful
|
||
);
|
||
}
|
||
|
||
if (hResult == ERROR_SUCCESS ) {
|
||
|
||
//
|
||
// Encryption is almost done. The file is still in a transit status
|
||
// No error checking for writing the log file for the same reason
|
||
// mentioned above.
|
||
//
|
||
|
||
if (LogFileH) {
|
||
( VOID )WriteLogFile(
|
||
LogFileH,
|
||
VolInfo.BytesPerSector,
|
||
StatusInfoOffset,
|
||
DecryptionDone
|
||
);
|
||
}
|
||
|
||
//
|
||
// FSCTL Mark Decryption success
|
||
//
|
||
|
||
( VOID ) SendSkFsctl(
|
||
EFS_DECRYPT_FILE,
|
||
EFS_DECRYPT_FILE,
|
||
EFS_SET_ENCRYPT,
|
||
InputData,
|
||
InputDataSize,
|
||
hSourceFile,
|
||
FSCTL_SET_ENCRYPTION,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
//
|
||
// Delete the backup file first.
|
||
//
|
||
|
||
LONG j;
|
||
|
||
|
||
for (j = StreamCount - 1 ; j >= 0 ; j--) {
|
||
MarkFileForDelete( TargetHandles[j] );
|
||
}
|
||
|
||
} else {
|
||
|
||
if ( CleanupSuccessful ){
|
||
|
||
//
|
||
// The operation failed, but we could back out cleanly.
|
||
// Delete the backup file first.
|
||
//
|
||
|
||
LONG j;
|
||
|
||
|
||
for (j = StreamCount - 1 ; j >= 0 ; j--) {
|
||
MarkFileForDelete( TargetHandles[j] );
|
||
}
|
||
|
||
|
||
} else {
|
||
//
|
||
// The operation failed and we couldn't clean up. Keep the
|
||
// log file and the backup file around so we can retry on
|
||
// reboot.
|
||
//
|
||
|
||
KeepLogFile = TRUE;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
//
|
||
// Regardless of what happened, we don't need these any more.
|
||
//
|
||
|
||
LONG j;
|
||
|
||
for (j=StreamCount - 1 ; j >= 0 ; j--) {
|
||
|
||
CloseHandle( TargetHandles[j] );
|
||
|
||
}
|
||
|
||
LsapFreeLsaHeap( TargetHandles );
|
||
|
||
} else {
|
||
|
||
//
|
||
// We couldn't copy everything to the backup file.
|
||
//
|
||
// Tell the driver that the operation has failed.
|
||
//
|
||
// MyCopyFile has already taken care of cleaning up the
|
||
// backup file.
|
||
//
|
||
|
||
DebugLog((DEB_ERROR, "MyCopyFile failed, error = %d\n" ,hResult ));
|
||
|
||
//
|
||
// FSCTL Fail Decrypting
|
||
// EFS_ENCRYPT_DONE will do the trick.
|
||
//
|
||
|
||
( VOID ) SendSkFsctl(
|
||
0,
|
||
0,
|
||
EFS_ENCRYPT_DONE,
|
||
InputData,
|
||
InputDataSize,
|
||
hSourceFile,
|
||
FSCTL_ENCRYPTION_FSCTL_IO,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
if ( hBackupFile ) {
|
||
//
|
||
// MyCopyFile has deleted the backup file
|
||
//
|
||
|
||
hBackupFile = 0;
|
||
}
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// FSCTL Fail Decrypting
|
||
// EFS_ENCRYPT_DONE will do the trick.
|
||
//
|
||
|
||
( VOID ) SendSkFsctl(
|
||
0,
|
||
0,
|
||
EFS_ENCRYPT_DONE,
|
||
InputData,
|
||
InputDataSize,
|
||
hSourceFile,
|
||
FSCTL_ENCRYPTION_FSCTL_IO,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
if ( hBackupFile ) {
|
||
MarkFileForDelete( hBackupFile );
|
||
CloseHandle( hBackupFile );
|
||
hBackupFile = 0;
|
||
}
|
||
|
||
hResult = ERROR_DECRYPTION_FAILED;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// FSCTL Fail Decrypting
|
||
// EFS_ENCRYPT_DONE will do the trick.
|
||
//
|
||
|
||
( VOID ) SendSkFsctl(
|
||
0,
|
||
0,
|
||
EFS_ENCRYPT_DONE,
|
||
InputData,
|
||
InputDataSize,
|
||
hSourceFile,
|
||
FSCTL_ENCRYPTION_FSCTL_IO,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
if ( hBackupFile ) {
|
||
MarkFileForDelete( hBackupFile );
|
||
CloseHandle( hBackupFile );
|
||
hBackupFile = 0;
|
||
}
|
||
|
||
hResult = ERROR_DECRYPTION_FAILED;
|
||
}
|
||
|
||
|
||
LsapFreeLsaHeap( LaterStreamInfoBase );
|
||
|
||
} else {
|
||
|
||
//
|
||
// FSCTL Fail Decrypting
|
||
// EFS_ENCRYPT_DONE will do the trick.
|
||
//
|
||
|
||
( VOID ) SendSkFsctl(
|
||
0,
|
||
0,
|
||
EFS_ENCRYPT_DONE,
|
||
InputData,
|
||
InputDataSize,
|
||
hSourceFile,
|
||
FSCTL_ENCRYPTION_FSCTL_IO,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
if ( hBackupFile ) {
|
||
MarkFileForDelete( hBackupFile );
|
||
CloseHandle( hBackupFile );
|
||
hBackupFile = 0;
|
||
}
|
||
|
||
hResult = RtlNtStatusToDosError( Status );
|
||
|
||
//
|
||
// Make sure the error was mapped
|
||
//
|
||
|
||
if (hResult == ERROR_MR_MID_NOT_FOUND) {
|
||
|
||
DebugLog((DEB_WARN, "Unable to map NT Error (%x) to Win32 error, returning ERROR_ENCRYPTION_FAILED\n" , Status ));
|
||
hResult = ERROR_DECRYPTION_FAILED;
|
||
}
|
||
|
||
}
|
||
|
||
CleanupOpenFileStreams(
|
||
StreamHandles,
|
||
StreamNames,
|
||
StreamSizes,
|
||
NULL,
|
||
hSourceFile,
|
||
StreamCount
|
||
);
|
||
|
||
StreamHandles = NULL;
|
||
StreamNames = NULL;
|
||
hSourceFile = NULL;
|
||
}
|
||
|
||
LsapFreeLsaHeap( StreamInfoBase );
|
||
|
||
} else {
|
||
|
||
hResult = RtlNtStatusToDosError( Status );
|
||
|
||
//
|
||
// Make sure the error was mapped
|
||
//
|
||
|
||
if (hResult == ERROR_MR_MID_NOT_FOUND) {
|
||
|
||
DebugLog((DEB_WARN, "Unable to map NT Error (%x) to Win32 error, returning ERROR_ENCRYPTION_FAILED\n" , Status ));
|
||
hResult = ERROR_DECRYPTION_FAILED;
|
||
}
|
||
}
|
||
|
||
if ( hSourceFile ){
|
||
|
||
CloseHandle( hSourceFile );
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
if ( Status != STATUS_ENCRYPTION_FAILED ){
|
||
//
|
||
// NtCreateFile failed
|
||
//
|
||
|
||
if (FileAttributes & FILE_ATTRIBUTE_READONLY) {
|
||
hResult = ERROR_FILE_READ_ONLY;
|
||
} else {
|
||
|
||
hResult = RtlNtStatusToDosError( Status );
|
||
|
||
//
|
||
// Make sure the error was mapped
|
||
//
|
||
|
||
if (hResult == ERROR_MR_MID_NOT_FOUND) {
|
||
|
||
DebugLog((DEB_WARN, "Unable to map NT Error (%x) to Win32 error, returning ERROR_ENCRYPTION_FAILED\n" , Status ));
|
||
hResult = ERROR_DECRYPTION_FAILED;
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
// else Convert Dos name failed
|
||
}
|
||
|
||
if (!KeepLogFile && LogFileH) {
|
||
MarkFileForDelete( LogFileH );
|
||
}
|
||
|
||
//
|
||
// Free memory
|
||
//
|
||
|
||
LsapFreeLsaHeap( SourceFileName );
|
||
LsapFreeLsaHeap( InputData );
|
||
|
||
return( hResult );
|
||
}
|
||
|
||
|
||
|
||
DWORD
|
||
MyCopyFile(
|
||
HANDLE hSourceFile,
|
||
PUNICODE_STRING StreamNames,
|
||
PHANDLE StreamHandles,
|
||
PEFS_STREAM_SIZE StreamSizes,
|
||
ULONG StreamCount,
|
||
HANDLE hTargetFile,
|
||
PHANDLE * TargetStreamHandles
|
||
)
|
||
{
|
||
|
||
ULONG i;
|
||
NTSTATUS Status;
|
||
PHANDLE TargetHandles;
|
||
DWORD hResult = 0;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
TargetHandles = (PHANDLE)LsapAllocateLsaHeap( StreamCount * sizeof( HANDLE ));
|
||
|
||
if (TargetHandles == NULL) {
|
||
return( ERROR_NOT_ENOUGH_MEMORY );
|
||
}
|
||
|
||
RtlZeroMemory( TargetHandles, StreamCount * sizeof( HANDLE ));
|
||
for (i=0 ; i<StreamCount ; i++) {
|
||
|
||
//
|
||
// For each stream on the source, create a stream with the same name on the target
|
||
//
|
||
|
||
if ( (DEF_STR_LEN == StreamNames[i].Length) &&
|
||
!memcmp( StreamNames[i].Buffer,
|
||
DEFAULT_STREAM,
|
||
StreamNames[i].Length
|
||
)){
|
||
|
||
//
|
||
// Default stream
|
||
//
|
||
|
||
TargetHandles[i] = hTargetFile;
|
||
|
||
if ( !(StreamSizes[i].StreamFlag & FILE_ATTRIBUTE_SPARSE_FILE) ){
|
||
|
||
//
|
||
// Reserve space for non sparse stream
|
||
//
|
||
FILE_END_OF_FILE_INFORMATION FileSize;
|
||
|
||
FileSize.EndOfFile = StreamSizes[i].EOFSize;
|
||
Status = NtSetInformationFile(
|
||
TargetHandles[i],
|
||
&IoStatusBlock,
|
||
&FileSize,
|
||
sizeof(FileSize),
|
||
FileEndOfFileInformation
|
||
);
|
||
|
||
} else {
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
} else {
|
||
OBJECT_ATTRIBUTES Obja;
|
||
PLARGE_INTEGER AllocSize;
|
||
|
||
if ( !(StreamSizes[i].StreamFlag & FILE_ATTRIBUTE_SPARSE_FILE) ){
|
||
|
||
//
|
||
// Reserve space for non sparse stream
|
||
//
|
||
|
||
AllocSize = &(StreamSizes[i].EOFSize);
|
||
|
||
} else {
|
||
|
||
AllocSize = NULL;
|
||
|
||
}
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&StreamNames[i],
|
||
0,
|
||
hTargetFile,
|
||
NULL
|
||
);
|
||
|
||
Status = NtCreateFile(
|
||
&TargetHandles[i],
|
||
#ifdef EFSDBG
|
||
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
|
||
#else
|
||
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE | DELETE,
|
||
#endif
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
AllocSize,
|
||
0,
|
||
#ifdef EFSDBG
|
||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, // have to share with delete-ers, since the main stream is open for delete
|
||
#else
|
||
FILE_SHARE_DELETE, // have to share with delete-ers, since the main stream is open for delete
|
||
#endif
|
||
FILE_CREATE,
|
||
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
if (!NT_SUCCESS( Status )){
|
||
|
||
//
|
||
// Just make sure it is zero
|
||
//
|
||
|
||
TargetHandles[i] = 0;
|
||
}
|
||
|
||
}
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
USHORT State = COMPRESSION_FORMAT_NONE;
|
||
ULONG Length;
|
||
|
||
if ( StreamSizes[i].StreamFlag & FILE_ATTRIBUTE_COMPRESSED ){
|
||
|
||
State = COMPRESSION_FORMAT_DEFAULT;
|
||
|
||
}
|
||
|
||
//
|
||
// Return code is not checked. Failing to compress or decompress the
|
||
// the temp file should not prevent the encryption operation.
|
||
//
|
||
|
||
(VOID) DeviceIoControl(
|
||
TargetHandles[i],
|
||
FSCTL_SET_COMPRESSION,
|
||
&State,
|
||
sizeof(USHORT),
|
||
NULL,
|
||
0,
|
||
&Length,
|
||
FALSE
|
||
);
|
||
|
||
hResult = CopyStream( TargetHandles[i], StreamHandles[i], &StreamSizes[i] );
|
||
}
|
||
|
||
if (!NT_SUCCESS( Status ) || hResult != ERROR_SUCCESS) {
|
||
|
||
//
|
||
// We either failed in creating a new stream on the target,
|
||
// or in copying it from the source to the target. Regardless,
|
||
// for each stream we created in the target, delete it.
|
||
//
|
||
|
||
ULONG StreamsCreated;
|
||
|
||
if (NT_SUCCESS( Status ) || TargetHandles[i]) {
|
||
|
||
StreamsCreated = i;
|
||
|
||
} else {
|
||
|
||
StreamsCreated = i-1;
|
||
}
|
||
|
||
ULONG j;
|
||
|
||
//
|
||
// Default stream handle is in TargetHandles[0]
|
||
//
|
||
|
||
for ( j = 0 ; j <=StreamsCreated ; j++) {
|
||
|
||
//
|
||
// Paranoia: check if the handle is valid.
|
||
//
|
||
|
||
if (TargetHandles[j]){
|
||
MarkFileForDelete( TargetHandles[j] );
|
||
CloseHandle( TargetHandles[j] );
|
||
}
|
||
}
|
||
|
||
LsapFreeLsaHeap( TargetHandles );
|
||
|
||
//
|
||
// Just to be safe
|
||
//
|
||
|
||
TargetHandles = NULL;
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
hResult = RtlNtStatusToDosError( Status );
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
*TargetStreamHandles = TargetHandles;
|
||
|
||
return( hResult );
|
||
}
|
||
|
||
DWORD
|
||
OpenFileStreams(
|
||
IN HANDLE hSourceFile,
|
||
IN ULONG ShareMode,
|
||
IN ULONG Flag,
|
||
IN PFILE_STREAM_INFORMATION StreamInfoBase,
|
||
IN ULONG FileAccess,
|
||
IN ULONG CreateDisposition,
|
||
IN ULONG CreateOption,
|
||
IN PFILE_FS_SIZE_INFORMATION VolInfo,
|
||
OUT PUNICODE_STRING * StreamNames,
|
||
OUT PHANDLE * StreamHandles,
|
||
OUT PEFS_STREAM_SIZE * StreamSizes,
|
||
OUT PULONG StreamCount
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine opens the streams of file hSourceFile.
|
||
|
||
Arguments:
|
||
|
||
hSourceFile - Supplies an opened handle to the file being worked.
|
||
|
||
ShareMode - The share mode to open the streams.
|
||
|
||
Flags - The reason for open the streams.
|
||
|
||
StreamInfoBase - Information about the streams;
|
||
|
||
StreamNames - The names of the streams.
|
||
|
||
StreamHandles - The handles of the streams.
|
||
|
||
StreamCount - Number strteams in the file.
|
||
|
||
FileAccess - Desired access to the streams.
|
||
|
||
CreateDisposition - Create disposition of the streams.
|
||
|
||
CreateOption - Create options of the streams.
|
||
|
||
StreamSizes - the sizes and attributes of the streams.
|
||
|
||
Return Value:
|
||
|
||
Result of the operation.
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
PUNICODE_STRING Names = NULL;
|
||
PHANDLE Handles = NULL;
|
||
PEFS_STREAM_SIZE Sizes = NULL;
|
||
DWORD rc = ERROR_SUCCESS;
|
||
|
||
PFILE_STREAM_INFORMATION StreamInfo = StreamInfoBase;
|
||
BOOL b;
|
||
|
||
//
|
||
// First, count the number of streams
|
||
//
|
||
|
||
if (StreamInfoBase == NULL) {
|
||
|
||
//
|
||
// No stream to open. TRUE for most DIR.
|
||
//
|
||
|
||
*StreamCount = 0;
|
||
return ERROR_SUCCESS;
|
||
|
||
}
|
||
|
||
*StreamCount = 0;
|
||
|
||
while ( StreamInfo ) {
|
||
|
||
(*StreamCount)++;
|
||
|
||
if (StreamInfo->NextEntryOffset){
|
||
StreamInfo = (PFILE_STREAM_INFORMATION)((PCHAR) StreamInfo + StreamInfo->NextEntryOffset);
|
||
} else {
|
||
StreamInfo = NULL;
|
||
}
|
||
}
|
||
|
||
DebugLog((DEB_TRACE_EFS, "Found %d streams\n",*StreamCount));
|
||
|
||
//
|
||
// Allocate enough room for pointers and handles to the streams and their names
|
||
//
|
||
|
||
Handles = (PHANDLE) LsapAllocateLsaHeap( *StreamCount * sizeof( HANDLE ));
|
||
Names = (PUNICODE_STRING) LsapAllocateLsaHeap( *StreamCount * sizeof( UNICODE_STRING ));
|
||
if ( StreamSizes ){
|
||
Sizes = (PEFS_STREAM_SIZE) LsapAllocateLsaHeap( *StreamCount * sizeof( EFS_STREAM_SIZE ));
|
||
}
|
||
|
||
if (Names == NULL || Handles == NULL || (StreamSizes && (Sizes == NULL))) {
|
||
|
||
if (Handles != NULL) {
|
||
LsapFreeLsaHeap( Handles );
|
||
}
|
||
|
||
if (Names != NULL) {
|
||
LsapFreeLsaHeap( Names );
|
||
}
|
||
|
||
if (Sizes != NULL) {
|
||
LsapFreeLsaHeap( Sizes );
|
||
}
|
||
|
||
DebugLog((DEB_ERROR, "Out of heap in OpenFileStreams\n"));
|
||
return( ERROR_NOT_ENOUGH_MEMORY );
|
||
}
|
||
|
||
//
|
||
// Zero out the handle array to simplify cleanup later
|
||
//
|
||
|
||
RtlZeroMemory( Handles, *StreamCount * sizeof( HANDLE ));
|
||
|
||
//
|
||
// Open a handle to each stream for exclusive access
|
||
//
|
||
|
||
StreamInfo = StreamInfoBase;
|
||
|
||
ULONG i = 0;
|
||
|
||
while ( StreamInfo ) {
|
||
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
//
|
||
// Build a string descriptor for the name of the stream.
|
||
//
|
||
|
||
Names[i].Buffer = &StreamInfo->StreamName[0];
|
||
Names[i].MaximumLength = Names[i].Length = (USHORT) StreamInfo->StreamNameLength;
|
||
|
||
if (Sizes != NULL) {
|
||
Sizes[i].EOFSize = StreamInfo->StreamSize;
|
||
Sizes[i].AllocSize = StreamInfo->StreamAllocationSize;
|
||
}
|
||
|
||
DebugLog((DEB_TRACE_EFS, "Opening stream %wZ\n",&Names[i]));
|
||
|
||
if ( StreamInfo->NextEntryOffset ){
|
||
StreamInfo = (PFILE_STREAM_INFORMATION)((PCHAR) StreamInfo + StreamInfo->NextEntryOffset);
|
||
} else {
|
||
StreamInfo = NULL;
|
||
}
|
||
|
||
//
|
||
// To avoid sharing violation, we do not open the default stream again.
|
||
// This also improves the performance
|
||
//
|
||
|
||
if ( (DEF_STR_LEN == Names[i].Length) &&
|
||
!memcmp( Names[i].Buffer,
|
||
DEFAULT_STREAM,
|
||
DEF_STR_LEN
|
||
)
|
||
){
|
||
|
||
Handles[i] = hSourceFile;
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Open the source stream.
|
||
//
|
||
|
||
OBJECT_ATTRIBUTES Obja;
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&Names[i],
|
||
0,
|
||
hSourceFile,
|
||
NULL
|
||
);
|
||
|
||
Status = NtCreateFile(
|
||
&Handles[i],
|
||
FileAccess,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
0,
|
||
ShareMode,
|
||
FILE_OPEN,
|
||
CreateOption,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
}
|
||
|
||
if ( NT_SUCCESS(Status) && ( Flag != OPEN_FOR_EXP ) ){
|
||
|
||
//
|
||
// Flush the streams. This is not allowed in export because of the permission.
|
||
//
|
||
|
||
Status = NtFlushBuffersFile(
|
||
Handles[i],
|
||
&IoStatusBlock
|
||
);
|
||
|
||
}
|
||
|
||
if ( (Sizes != NULL) && NT_SUCCESS(Status) ) {
|
||
|
||
FILE_BASIC_INFORMATION StreamBasicInfo;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
//
|
||
// Get File Attributes
|
||
//
|
||
Status = NtQueryInformationFile(
|
||
Handles[i],
|
||
&IoStatusBlock,
|
||
&StreamBasicInfo,
|
||
sizeof ( FILE_BASIC_INFORMATION ),
|
||
FileBasicInformation
|
||
);
|
||
|
||
if (NT_SUCCESS(Status)){
|
||
Sizes[i].StreamFlag = StreamBasicInfo.FileAttributes;
|
||
}
|
||
|
||
}
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
|
||
DebugLog((DEB_ERROR, "Unable to open stream %wZ, status = (%x)\n", &Names[i], Status ));
|
||
rc = RtlNtStatusToDosError( Status );
|
||
|
||
//
|
||
// In case the error isn't mapped, make sure we return something intelligent
|
||
//
|
||
|
||
if (rc == ERROR_MR_MID_NOT_FOUND) {
|
||
DebugLog((DEB_TRACE_EFS, "OpenFileStreams returning ERROR_ENCRYPTION_FAILED\n" ));
|
||
rc = ERROR_ENCRYPTION_FAILED;
|
||
}
|
||
|
||
break;
|
||
|
||
}
|
||
i++;
|
||
|
||
}
|
||
|
||
//
|
||
// Estimate the space for Encrypt or Decrypt operation
|
||
//
|
||
|
||
if ( (rc == ERROR_SUCCESS) &&
|
||
((Flag == OPEN_FOR_ENC) || (Flag == OPEN_FOR_DEC))){
|
||
|
||
rc = CheckVolumeSpace( VolInfo, Sizes, Handles, *StreamCount);
|
||
|
||
}
|
||
|
||
if ( rc != ERROR_SUCCESS ) {
|
||
|
||
ULONG j;
|
||
|
||
for (j=0 ; j<i ; j++) {
|
||
if ( hSourceFile != Handles[j] ){
|
||
NtClose( Handles[j] );
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( rc == ERROR_SUCCESS ) {
|
||
|
||
*StreamNames = Names;
|
||
*StreamHandles = Handles;
|
||
if (StreamSizes){
|
||
*StreamSizes = Sizes;
|
||
}
|
||
|
||
} else {
|
||
|
||
LsapFreeLsaHeap( Names);
|
||
LsapFreeLsaHeap( Handles);
|
||
if ( Sizes ){
|
||
LsapFreeLsaHeap( Sizes);
|
||
}
|
||
|
||
}
|
||
|
||
return( rc );
|
||
}
|
||
|
||
NTSTATUS
|
||
GetStreamInformation(
|
||
IN HANDLE SourceFile,
|
||
OUT PFILE_STREAM_INFORMATION * StreamInfoBase,
|
||
OUT PULONG StreamInfoSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine queries the stream information from the passed source file.
|
||
|
||
Arguments:
|
||
|
||
SourceFile - Supplies an opened handle to the file being queried.
|
||
|
||
StreamInfoBase - Returns a pointer to the stream information as returned
|
||
by NtQueryInformationFile().
|
||
|
||
StreamInfoSize - Returns the size of the information returned in the
|
||
StreamInfoBase parameter.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS on success, else failure, either from RtlAllocateHeap()
|
||
or NtQueryInformationFile();
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
*StreamInfoSize = 4096;
|
||
|
||
//
|
||
// Stolen from CreateDirectoryExW (base\client\dir.c)
|
||
//
|
||
|
||
do {
|
||
*StreamInfoBase = (PFILE_STREAM_INFORMATION)LsapAllocateLsaHeap( *StreamInfoSize);
|
||
if ( NULL == *StreamInfoBase ) {
|
||
return( STATUS_NO_MEMORY );
|
||
}
|
||
RtlZeroMemory(*StreamInfoBase, *StreamInfoSize);
|
||
|
||
Status = NtQueryInformationFile(
|
||
SourceFile,
|
||
&IoStatusBlock,
|
||
(PVOID) *StreamInfoBase,
|
||
*StreamInfoSize,
|
||
FileStreamInformation
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
LsapFreeLsaHeap( *StreamInfoBase);
|
||
*StreamInfoBase = NULL;
|
||
*StreamInfoSize *= 2;
|
||
} else {
|
||
if (IoStatusBlock.Information == 0) {
|
||
|
||
//
|
||
// No data stream were found. True for most DIR.
|
||
//
|
||
|
||
LsapFreeLsaHeap( *StreamInfoBase);
|
||
*StreamInfoBase = NULL;
|
||
*StreamInfoSize = 0;
|
||
|
||
}
|
||
}
|
||
|
||
} while ( Status == STATUS_BUFFER_OVERFLOW ||
|
||
Status == STATUS_BUFFER_TOO_SMALL );
|
||
|
||
return( Status );
|
||
}
|
||
|
||
|
||
DWORD
|
||
CopyFileStreams(
|
||
PHANDLE SourceStreams,
|
||
PHANDLE StreamHandles,
|
||
ULONG StreamCount,
|
||
PEFS_STREAM_SIZE StreamSizes,
|
||
EFSP_OPERATION Operation,
|
||
PUCHAR FsInputData,
|
||
ULONG FsInputDataSize,
|
||
PBOOLEAN CleanupSuccessful
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes an array of source handles and target handles
|
||
and attempts to copy the sources to the targets, in the order
|
||
that the handles appear in the arrays.
|
||
|
||
If there is an error part of the way through, this routine will
|
||
try to clean up as well as it can, and return to the user whether
|
||
or not to consider the target file corrupted.
|
||
|
||
Arguments:
|
||
|
||
SourceStreams - Supplies an array of handles to streams to be copied.
|
||
|
||
StreamHandles - Supplies an array of handles of target streams to be
|
||
copied into.
|
||
|
||
StreamCount - Supplies the number of elemets in the first two arrays.
|
||
|
||
StreamSizes - Supplies an array of sizes of the streams being copied.
|
||
|
||
Operation - Whether the streams are being encrypted or decrypted.
|
||
|
||
FsInputData - Supplies the input data for the FSCTL_SET_ENCRYPTION call.
|
||
|
||
FsInputDataSize - The length of FsInputData.
|
||
|
||
CleanupSuccessful - If there is a failure, this parameter will return
|
||
whether or not one or more of the streams on the file has been
|
||
corrupted.
|
||
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS on success, failure otherwise. In case of failure,
|
||
callers should examine the CleanupSuccessful flag to determine the
|
||
state of the target file.
|
||
|
||
--*/
|
||
{
|
||
ULONG StreamIndex;
|
||
DWORD hResult = 0;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
BOOL b = TRUE;
|
||
ULONG i;
|
||
DWORD rc = ERROR_SUCCESS;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
FILE_BASIC_INFORMATION StreamBasicInfo = { 0 };
|
||
|
||
*CleanupSuccessful = TRUE;
|
||
|
||
for (StreamIndex = 0; StreamIndex < StreamCount ; StreamIndex++) {
|
||
|
||
if ( Operation == EncryptRecovering ) {
|
||
|
||
//
|
||
// Called for recovering an unsuccessful encrypt. Get the attributes first.
|
||
//
|
||
|
||
Status = NtQueryInformationFile(
|
||
StreamHandles[StreamIndex],
|
||
&IoStatusBlock,
|
||
&StreamBasicInfo,
|
||
sizeof ( FILE_BASIC_INFORMATION ),
|
||
FileBasicInformation
|
||
);
|
||
|
||
if ( !NT_SUCCESS( Status )) {
|
||
|
||
b = FALSE;
|
||
break;
|
||
|
||
}
|
||
}
|
||
|
||
if ( ( Operation != EncryptRecovering ) ||
|
||
(StreamBasicInfo.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) ){
|
||
//
|
||
// Mark target stream encrypted or decrypted.
|
||
// Encrypt or decrypt information is in the input data
|
||
//
|
||
Status = NtFsControlFile(
|
||
StreamHandles[StreamIndex],
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_SET_ENCRYPTION,
|
||
FsInputData,
|
||
FsInputDataSize,
|
||
NULL,
|
||
0
|
||
);
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
if ( Operation == EncryptRecovering ) {
|
||
|
||
//
|
||
// Check if we need to re-compress the file
|
||
//
|
||
|
||
if ( StreamSizes[StreamIndex].StreamFlag & FILE_ATTRIBUTE_COMPRESSED ){
|
||
|
||
//
|
||
// Compress the target stream
|
||
//
|
||
|
||
USHORT State = COMPRESSION_FORMAT_DEFAULT;
|
||
ULONG Length;
|
||
|
||
//
|
||
// Return code is not checked. Failing to compress or decompress the
|
||
// the original file should not prevent the recovery.
|
||
//
|
||
|
||
(VOID) DeviceIoControl(
|
||
StreamHandles[StreamIndex],
|
||
FSCTL_SET_COMPRESSION,
|
||
&State,
|
||
sizeof(USHORT),
|
||
NULL,
|
||
0,
|
||
&Length,
|
||
FALSE
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
hResult = CopyStream( StreamHandles[StreamIndex], SourceStreams[StreamIndex], &StreamSizes[StreamIndex] );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Not successful, but if we haven't modified the file at all, we can
|
||
// still back out cleanly.
|
||
//
|
||
|
||
if (StreamIndex == 0) {
|
||
return( RtlNtStatusToDosError( Status ) );
|
||
}
|
||
}
|
||
|
||
if (!NT_SUCCESS( Status ) || hResult != ERROR_SUCCESS) {
|
||
|
||
b = FALSE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!b) {
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// Save the reason why we failed so we can return it.
|
||
//
|
||
|
||
rc = RtlNtStatusToDosError( Status );
|
||
|
||
} else {
|
||
|
||
rc = hResult;
|
||
}
|
||
|
||
//
|
||
// Something failed, back up and clean up. StreamIndex has the
|
||
// index of the last stream we were operating on.
|
||
//
|
||
|
||
if (Operation == Encrypting) {
|
||
|
||
//
|
||
// If we were encrypting, then we need to go back and mark each
|
||
// stream decrypted, and attempt to restore it from the backup.
|
||
// If either of those fail, we're hosed.
|
||
//
|
||
|
||
for (i=0; i<StreamIndex ; i++) {
|
||
|
||
//
|
||
// Mark stream decrypted. We can reuse the input data buffer
|
||
// here.
|
||
//
|
||
|
||
( VOID )GetDecryptFsInput(
|
||
StreamHandles[StreamIndex],
|
||
FsInputData,
|
||
&FsInputDataSize
|
||
);
|
||
|
||
Status = NtFsControlFile(
|
||
StreamHandles[StreamIndex],
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_SET_ENCRYPTION,
|
||
FsInputData,
|
||
FsInputDataSize,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
//
|
||
// Attempt to copy the stream from backup
|
||
//
|
||
|
||
hResult = CopyStream( StreamHandles[StreamIndex], SourceStreams[StreamIndex], &StreamSizes[StreamIndex] );
|
||
|
||
if (!NT_SUCCESS( Status ) || hResult != ERROR_SUCCESS) {
|
||
|
||
*CleanupSuccessful = FALSE;
|
||
|
||
//
|
||
// Give up
|
||
//
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Decrypting. Not a whole lot we can do here, because
|
||
// we can't put the file back the way it was.
|
||
//
|
||
|
||
*CleanupSuccessful = FALSE;
|
||
}
|
||
}
|
||
|
||
return( rc );
|
||
}
|
||
|
||
|
||
DWORD
|
||
CopyStream(
|
||
HANDLE Target,
|
||
HANDLE Source,
|
||
PEFS_STREAM_SIZE StreamSize
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine copies a file stream from a source stream to a target
|
||
stream. It assumes that the streams have been opened for appropriate
|
||
access.
|
||
|
||
Arguments:
|
||
|
||
Target - Supplies a handle to the stream to be written to. This handle
|
||
must be open for WRITE access.
|
||
|
||
Source - Supplies a stream handle to copy from. This handle must be open
|
||
for READ access.
|
||
|
||
StreamSize - Supplies the size of the stream in bytes.
|
||
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS on success, failure otherwise.
|
||
|
||
--*/
|
||
{
|
||
|
||
SYSTEM_INFO SystemInfo;
|
||
LARGE_INTEGER StreamOffset;
|
||
LARGE_INTEGER AllocationGranularity;
|
||
HANDLE hStreamMapping;
|
||
DWORD rc = NO_ERROR;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
NTSTATUS Status;
|
||
|
||
if ( 0 == (StreamSize->EOFSize).QuadPart ){
|
||
|
||
return ERROR_SUCCESS;
|
||
|
||
}
|
||
|
||
GetSystemInfo( &SystemInfo );
|
||
SetFilePointer( Target, 0, NULL, FILE_BEGIN);
|
||
|
||
AllocationGranularity.HighPart = 0;
|
||
AllocationGranularity.LowPart = SystemInfo.dwAllocationGranularity;
|
||
|
||
hStreamMapping = CreateFileMapping( Source, NULL, PAGE_READONLY, 0, 0, NULL );
|
||
|
||
if (hStreamMapping == NULL) {
|
||
return( GetLastError() );
|
||
}
|
||
|
||
if ( StreamSize->StreamFlag & FILE_ATTRIBUTE_SPARSE_FILE ){
|
||
|
||
//
|
||
// Sparsed stream. Query the ranges first.
|
||
//
|
||
|
||
FILE_ALLOCATED_RANGE_BUFFER InputData;
|
||
PFILE_ALLOCATED_RANGE_BUFFER Ranges;
|
||
ULONG OutDataBufferSize;
|
||
ULONG RangeCount;
|
||
|
||
//
|
||
// Set the target as a sparse file
|
||
//
|
||
|
||
Status = NtFsControlFile(
|
||
Target,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_SET_SPARSE,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
if ( NT_SUCCESS(Status) ){
|
||
|
||
//
|
||
// Set the EOF of the target
|
||
//
|
||
FILE_END_OF_FILE_INFORMATION FileSize;
|
||
|
||
FileSize.EndOfFile = StreamSize->EOFSize;
|
||
Status = NtSetInformationFile(
|
||
Target,
|
||
&IoStatusBlock,
|
||
&FileSize,
|
||
sizeof(FileSize),
|
||
FileEndOfFileInformation
|
||
);
|
||
|
||
}
|
||
|
||
if ( !NT_SUCCESS(Status) ){
|
||
|
||
CloseHandle( hStreamMapping );
|
||
return RtlNtStatusToDosError( Status );
|
||
|
||
}
|
||
|
||
InputData.FileOffset.QuadPart = 0;
|
||
InputData.Length.QuadPart = 0x7fffffffffffffff;
|
||
|
||
OutDataBufferSize = INITBUFFERSIZE;
|
||
|
||
do {
|
||
|
||
Ranges = (PFILE_ALLOCATED_RANGE_BUFFER)LsapAllocateLsaHeap( OutDataBufferSize );
|
||
|
||
if ( NULL == Ranges ){
|
||
CloseHandle( hStreamMapping );
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
Status = NtFsControlFile(
|
||
Source,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_QUERY_ALLOCATED_RANGES,
|
||
&InputData,
|
||
sizeof( FILE_ALLOCATED_RANGE_BUFFER ),
|
||
Ranges,
|
||
OutDataBufferSize
|
||
);
|
||
|
||
if (Status == STATUS_PENDING){
|
||
|
||
ASSERT(TRUE);
|
||
WaitForSingleObject(
|
||
Source,
|
||
INFINITE
|
||
);
|
||
|
||
Status = IoStatusBlock.Status;
|
||
}
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
LsapFreeLsaHeap( Ranges );
|
||
Ranges = NULL;
|
||
OutDataBufferSize += INITBUFFERSIZE;
|
||
}
|
||
|
||
} while ( Status == STATUS_BUFFER_OVERFLOW );
|
||
|
||
if ( NT_SUCCESS(Status) ){
|
||
|
||
//
|
||
// We got the ranges
|
||
//
|
||
|
||
RangeCount = (ULONG)IoStatusBlock.Information / sizeof (FILE_ALLOCATED_RANGE_BUFFER);
|
||
for ( ULONG ii = 0; ii < RangeCount; ii++ ){
|
||
|
||
DWORD LowMoved;
|
||
|
||
//
|
||
// Move the target file pointer first
|
||
//
|
||
|
||
StreamOffset = Ranges[ii].FileOffset;
|
||
LowMoved = SetFilePointer(
|
||
Target,
|
||
StreamOffset.LowPart,
|
||
&StreamOffset.HighPart,
|
||
FILE_BEGIN
|
||
);
|
||
if ( ( LowMoved != 0xFFFFFFFF ) || ( NO_ERROR != (rc = GetLastError()))){
|
||
|
||
rc = CopyStreamSection(
|
||
Target,
|
||
hStreamMapping,
|
||
&StreamOffset,
|
||
&(Ranges[ii].Length),
|
||
&AllocationGranularity
|
||
);
|
||
|
||
}
|
||
if ( NO_ERROR != rc ) {
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
LsapFreeLsaHeap( Ranges );
|
||
Ranges = NULL;
|
||
|
||
} else {
|
||
|
||
rc = RtlNtStatusToDosError( Status );
|
||
if ( Ranges ) {
|
||
LsapFreeLsaHeap( Ranges );
|
||
Ranges = NULL;
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Non sparsed stream
|
||
//
|
||
|
||
StreamOffset.HighPart = 0;
|
||
StreamOffset.LowPart = 0;
|
||
|
||
rc = CopyStreamSection(
|
||
Target,
|
||
hStreamMapping,
|
||
&StreamOffset,
|
||
&(StreamSize->EOFSize),
|
||
&AllocationGranularity
|
||
);
|
||
|
||
}
|
||
|
||
CloseHandle( hStreamMapping );
|
||
|
||
if ( rc == NO_ERROR ) {
|
||
|
||
//
|
||
// Flush the stream
|
||
//
|
||
|
||
Status = NtFlushBuffersFile(
|
||
Target,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
rc = RtlNtStatusToDosError( Status );
|
||
}
|
||
}
|
||
|
||
return( rc );
|
||
}
|
||
|
||
|
||
VOID
|
||
CleanupOpenFileStreams(
|
||
IN PHANDLE Handles OPTIONAL,
|
||
IN PUNICODE_STRING StreamNames OPTIONAL,
|
||
IN PEFS_STREAM_SIZE Sizes OPTIONAL,
|
||
IN PFILE_STREAM_INFORMATION StreamInfoBase OPTIONAL,
|
||
IN HANDLE HSourceFile OPTIONAL,
|
||
IN ULONG StreamCount
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine cleans up after a call to OpenFileStreams.
|
||
|
||
Arguments:
|
||
|
||
Handles - Supplies the array of handles returned from OpenFileStreams.
|
||
|
||
Sizes - Supplies the array of stream sizes returned from OpenFileStreams.
|
||
|
||
StreamCount - Supplies the number of streams returned from OpenFileStreams.
|
||
|
||
|
||
Return Value:
|
||
|
||
None. This is no recovery operation should this routine fail.
|
||
|
||
--*/
|
||
{
|
||
ULONG i;
|
||
|
||
if ( Handles ){
|
||
|
||
for (ULONG i = 0; i<StreamCount ; i++) {
|
||
if (HSourceFile == Handles[i]) {
|
||
HSourceFile = 0;
|
||
}
|
||
NtClose( Handles[i] );
|
||
}
|
||
LsapFreeLsaHeap( Handles);
|
||
if (HSourceFile) {
|
||
|
||
//
|
||
// Dir with data streams will get here
|
||
//
|
||
|
||
NtClose( HSourceFile );
|
||
|
||
}
|
||
|
||
} else if ( HSourceFile ){
|
||
|
||
//
|
||
// HSourceFile is among one of Handles[]
|
||
//
|
||
|
||
|
||
NtClose( HSourceFile );
|
||
|
||
}
|
||
|
||
if ( StreamNames ){
|
||
LsapFreeLsaHeap( StreamNames);
|
||
}
|
||
|
||
if ( Sizes ){
|
||
LsapFreeLsaHeap( Sizes);
|
||
}
|
||
|
||
if ( StreamInfoBase ){
|
||
LsapFreeLsaHeap( StreamInfoBase );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
MarkFileForDelete(
|
||
HANDLE FileHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function marks the passed file for delete, so that it
|
||
will be deleted by the system when its last handle is closed.
|
||
|
||
Arguments:
|
||
|
||
FileHandle - A handle to a file that has been opened for DELETE
|
||
access (see comments in the procedure body about this).
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
FILE_DISPOSITION_INFORMATION Disposition;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
NTSTATUS Status;
|
||
|
||
#ifdef EFSDBG
|
||
|
||
//
|
||
// If we're in debugging mode, the file has not been opened
|
||
// for delete access, since that would prohibit any other
|
||
// process from accessing the file (which we want to do during
|
||
// the debugging phase).
|
||
//
|
||
// Open the file again, for delete access this time, and
|
||
// mark it for delete. In the normal case, we don't have
|
||
// to do this.
|
||
//
|
||
|
||
OBJECT_ATTRIBUTES Obja;
|
||
UNICODE_STRING Unicode;
|
||
|
||
RtlInitUnicodeString( &Unicode, NULL );
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&Unicode,
|
||
0,
|
||
FileHandle,
|
||
NULL
|
||
);
|
||
|
||
Status = NtCreateFile(
|
||
&FileHandle, // yes, overwrite the parameter
|
||
DELETE | SYNCHRONIZE,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
0,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
FILE_OPEN,
|
||
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
DbgPrint("NtCreateFile in MarkFileForDelete failed, status = %x\n",Status);
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// "DeleteFile is defined to be DeleteFileW, so undef it here...
|
||
//
|
||
|
||
#undef DeleteFile
|
||
|
||
Disposition.DeleteFile = TRUE;
|
||
|
||
Status = NtSetInformationFile(
|
||
FileHandle,
|
||
&IoStatusBlock,
|
||
&Disposition,
|
||
sizeof(Disposition),
|
||
FileDispositionInformation
|
||
);
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
WCHAR ErrorCode[16];
|
||
LPWSTR pErrorCode;
|
||
|
||
_ltow( Status, ErrorCode, 16 );
|
||
pErrorCode = &ErrorCode[0];
|
||
|
||
EfsLogEntry(
|
||
EVENTLOG_WARNING_TYPE,
|
||
0,
|
||
EFS_DEL_LOGFILE_ERROR,
|
||
1,
|
||
0,
|
||
(LPCWSTR *)&pErrorCode,
|
||
NULL
|
||
);
|
||
|
||
}
|
||
|
||
#ifdef EFSDBG
|
||
|
||
CloseHandle( FileHandle );
|
||
|
||
#endif
|
||
}
|
||
|
||
BOOLEAN
|
||
SendHandle(
|
||
IN HANDLE Handle,
|
||
IN OUT PUCHAR EfsData,
|
||
IN OUT PULONG EfsDataLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Constructs an EFS Data section of the form (Handle will be truncated to ULONG for SunDown)
|
||
|
||
SK, Handle, Handle, [SK, Handle, Handle]sk
|
||
|
||
Arguments:
|
||
|
||
Handle - Supplies the handle value to be encoded
|
||
|
||
EfsData - Supplies a buffer large enough to contain the
|
||
output data.
|
||
|
||
EfsDataLength - Supplies the length of the EfsData, and
|
||
returns either the length required or the length
|
||
used.
|
||
|
||
Return Value:
|
||
|
||
TRUE on success, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// Compute the total size required
|
||
//
|
||
|
||
ULONG TotalSize = 4 * sizeof( ULONG ) + 2 * sizeof( DriverSessionKey );
|
||
|
||
if (*EfsDataLength < TotalSize) {
|
||
*EfsDataLength = TotalSize;
|
||
return( FALSE );
|
||
}
|
||
|
||
*EfsDataLength = TotalSize;
|
||
|
||
//
|
||
// Copy everything in, and encrypt what needs to be encrypted.
|
||
//
|
||
|
||
PUCHAR Where = EfsData;
|
||
|
||
RtlCopyMemory( Where, DriverSessionKey, sizeof( DriverSessionKey ) );
|
||
Where += sizeof( DriverSessionKey );
|
||
|
||
PULONG pUlong = (PULONG)Where;
|
||
*pUlong = PtrToUlong(Handle);
|
||
Where += sizeof( ULONG );
|
||
|
||
pUlong = (PULONG)Where;
|
||
*pUlong = PtrToUlong(Handle);
|
||
Where += sizeof( ULONG );
|
||
|
||
PUCHAR CryptData = Where;
|
||
|
||
RtlCopyMemory( CryptData, EfsData, TotalSize/2 );
|
||
|
||
//
|
||
// Now encrypt the data starting at CryptData
|
||
//
|
||
|
||
LONG bytesToBeEnc = (LONG)(TotalSize/2);
|
||
ASSERT( (bytesToBeEnc % DES_BLOCKLEN) == 0 );
|
||
|
||
while ( bytesToBeEnc > 0 ) {
|
||
|
||
//
|
||
// Encrypt data with DES
|
||
//
|
||
|
||
des( CryptData,
|
||
CryptData,
|
||
&DesTable,
|
||
ENCRYPT
|
||
);
|
||
|
||
CryptData += DES_BLOCKLEN;
|
||
bytesToBeEnc -= DES_BLOCKLEN;
|
||
}
|
||
|
||
return( TRUE );
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
SendEfs(
|
||
IN PEFS_KEY Fek,
|
||
IN PEFS_DATA_STREAM_HEADER Efs,
|
||
OUT PUCHAR EfsData,
|
||
OUT PULONG EfsDataLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Constructs an EFS Data section of the form
|
||
|
||
FEK, [FEK]sk, $EFS
|
||
|
||
Arguments:
|
||
|
||
Fek - Supplies the FEK to be encoded
|
||
|
||
Efs - Supplies the EFS to be encoded
|
||
|
||
EfsData - Supplies a buffer large enough for the returned
|
||
data.
|
||
|
||
EfsDataLength - Supplies the length of the EfsData, and
|
||
returns either the length required or the length
|
||
used.
|
||
|
||
Return Value:
|
||
|
||
TRUE on success, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// Compute the total size required
|
||
//
|
||
|
||
ULONG TotalSize = 2 * EFS_KEY_SIZE( Fek ) + Efs->Length;
|
||
|
||
if (*EfsDataLength < TotalSize) {
|
||
*EfsDataLength = TotalSize;
|
||
return( FALSE );
|
||
}
|
||
|
||
*EfsDataLength = TotalSize;
|
||
|
||
//
|
||
// Copy in the FEK twice, followed by the EFS stream
|
||
//
|
||
|
||
PUCHAR Where = EfsData;
|
||
|
||
RtlCopyMemory( Where, Fek, EFS_KEY_SIZE( Fek ) );
|
||
Where += EFS_KEY_SIZE( Fek );
|
||
|
||
//
|
||
// Save the location that we're going to encrypt
|
||
//
|
||
|
||
PUCHAR CryptData = Where;
|
||
|
||
RtlCopyMemory( Where, Fek, EFS_KEY_SIZE( Fek ) );
|
||
Where += EFS_KEY_SIZE( Fek );
|
||
|
||
if ( Where != (PUCHAR) Efs ){
|
||
RtlCopyMemory( Where, Efs, Efs->Length );
|
||
}
|
||
|
||
//
|
||
// Encrypt the second FEK
|
||
//
|
||
|
||
LONG bytesToBeEnc = (LONG)(EFS_KEY_SIZE( Fek ));
|
||
ASSERT( (bytesToBeEnc % DES_BLOCKLEN) == 0 );
|
||
|
||
while ( bytesToBeEnc > 0 ) {
|
||
|
||
//
|
||
// Encrypt data with DES
|
||
//
|
||
|
||
des( CryptData,
|
||
CryptData,
|
||
&DesTable,
|
||
ENCRYPT
|
||
);
|
||
|
||
CryptData += DES_BLOCKLEN;
|
||
bytesToBeEnc -= DES_BLOCKLEN;
|
||
}
|
||
|
||
return( TRUE );
|
||
}
|
||
|
||
BOOLEAN
|
||
SendHandleAndEfs(
|
||
IN HANDLE Handle,
|
||
IN PEFS_DATA_STREAM_HEADER Efs,
|
||
IN OUT PUCHAR EfsData,
|
||
IN OUT PULONG EfsDataLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Constructs an EFS Data section of the form (Handle will be truncated to ULONG for SunDown)
|
||
|
||
SK, Handle, Handle, [SK, Handle, Handle]sk $EFS
|
||
|
||
Arguments:
|
||
|
||
Fek - Supplies the FEK to be encoded
|
||
|
||
Efs - Supplies the EFS to be encoded
|
||
|
||
EfsData - Returns a buffer containing the EFS data
|
||
|
||
EfsDataLength - Returns the length of the EFS data.
|
||
|
||
Return Value:
|
||
|
||
TRUE on success, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
ULONG TotalSize = 4 * sizeof( ULONG ) + 2 * sizeof( DriverSessionKey ) + Efs->Length;
|
||
|
||
if (*EfsDataLength < TotalSize) {
|
||
*EfsDataLength = TotalSize;
|
||
return( FALSE );
|
||
}
|
||
|
||
if ( SendHandle( Handle, EfsData, EfsDataLength ) ) {
|
||
|
||
//
|
||
// Tack the EFS onto the end of the buffer
|
||
//
|
||
|
||
RtlCopyMemory( EfsData + (*EfsDataLength), Efs, Efs->Length );
|
||
*EfsDataLength += Efs->Length;
|
||
return( TRUE );
|
||
|
||
} else {
|
||
|
||
return( FALSE );
|
||
}
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
EncryptFSCTLData(
|
||
IN ULONG Fsctl,
|
||
IN ULONG Psc,
|
||
IN ULONG Csc,
|
||
IN PVOID EfsData,
|
||
IN ULONG EfsDataLength,
|
||
IN OUT PUCHAR Buffer,
|
||
IN OUT PULONG BufferLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Constructs the input to the various FSCTL routines based
|
||
on the passed parameters. The general form is:
|
||
|
||
PSC, [EFS_FC, CSC, [EFS Data]]sk
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
TRUE on success, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
ULONG TotalSize = 3 * sizeof( ULONG ) + EfsDataLength;
|
||
|
||
if (*BufferLength < TotalSize) {
|
||
*BufferLength = TotalSize;
|
||
return( FALSE );
|
||
}
|
||
|
||
*BufferLength = TotalSize;
|
||
|
||
//
|
||
// Copy all the data in, and encrypt what needs to be encrypted
|
||
//
|
||
|
||
PULONG pUlong = (PULONG)Buffer;
|
||
|
||
*pUlong++ = Psc;
|
||
*pUlong++ = Fsctl;
|
||
*pUlong++ = Csc;
|
||
|
||
//
|
||
// EfsData might point to inside Buffer and the data already in place
|
||
//
|
||
|
||
if ( (PVOID)pUlong != (PVOID)EfsData )
|
||
RtlCopyMemory( (PUCHAR)pUlong, EfsData, EfsDataLength );
|
||
|
||
LONG bytesToBeEnc = (LONG)(2 * sizeof(ULONG) + EfsDataLength);
|
||
ASSERT( (bytesToBeEnc % DES_BLOCKLEN) == 0 );
|
||
|
||
PUCHAR CryptData = Buffer + sizeof( ULONG );
|
||
|
||
while ( bytesToBeEnc > 0 ) {
|
||
|
||
//
|
||
// Encrypt data with DES
|
||
//
|
||
|
||
des( CryptData,
|
||
CryptData,
|
||
&DesTable,
|
||
ENCRYPT
|
||
);
|
||
|
||
CryptData += DES_BLOCKLEN;
|
||
bytesToBeEnc -= DES_BLOCKLEN;
|
||
}
|
||
|
||
return( TRUE );
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
GetParentEfsStream(
|
||
IN HANDLE CurrentFileHandle,
|
||
IN PUNICODE_STRING CurrentFileName,
|
||
OUT PEFS_DATA_STREAM_HEADER *ParentEfsStream
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get the $EFS from the parent directory
|
||
|
||
Arguments:
|
||
|
||
SourceFileName -- Current file or directory name.
|
||
|
||
ParentEfsStream
|
||
|
||
Return Value:
|
||
|
||
Status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG Index;
|
||
HANDLE ParentDir;
|
||
PUCHAR InputData;
|
||
ULONG InputDataSize;
|
||
ULONG OutputDataSize;
|
||
ULONG EfsDataLength;
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
//
|
||
// Get the parent name
|
||
//
|
||
|
||
*ParentEfsStream = NULL;
|
||
|
||
Index = (CurrentFileName->Length)/sizeof( WCHAR ) - 1;
|
||
while ( (Index > 0) && ( CurrentFileName->Buffer[Index] != L'\\') )
|
||
Index--;
|
||
|
||
if ( Index <= 0 )
|
||
return STATUS_OBJECT_PATH_NOT_FOUND;
|
||
|
||
LPWSTR ParentDirName;
|
||
/*
|
||
if ( CurrentFileName->Buffer[Index-1] == L':' ){
|
||
|
||
//
|
||
// Parent is a root directory
|
||
//
|
||
|
||
Status = GetRootHandle( CurrentFileHandle, &ParentDir );
|
||
|
||
if (!NT_SUCCESS( Status )){
|
||
|
||
*ParentEfsStream = NULL;
|
||
return STATUS_OBJECT_PATH_NOT_FOUND;
|
||
}
|
||
|
||
|
||
} else {
|
||
*/
|
||
|
||
//
|
||
// A normal directory. We can use WIN 32 API to open it.
|
||
//
|
||
|
||
ParentDirName = (LPWSTR) LsapAllocateLsaHeap( ( Index + 1 ) * sizeof(WCHAR) );
|
||
if ( ParentDirName == NULL ) {
|
||
|
||
*ParentEfsStream = NULL;
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
}
|
||
RtlCopyMemory( ParentDirName, &CurrentFileName->Buffer[0], Index * sizeof(WCHAR));
|
||
ParentDirName[Index] = UNICODE_NULL;
|
||
|
||
//
|
||
// FILE_FLAG_BACKUP_SEMANTICS is required to open a directory.
|
||
// How about if a user does not have the backup privilege?
|
||
//
|
||
|
||
ParentDir = CreateFile(
|
||
ParentDirName,
|
||
FILE_READ_ATTRIBUTES,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
FILE_FLAG_BACKUP_SEMANTICS,
|
||
NULL
|
||
);
|
||
//
|
||
// There is no need for us to hold ParentDirName any more
|
||
//
|
||
|
||
LsapFreeLsaHeap( ParentDirName );
|
||
|
||
if ( ParentDir == INVALID_HANDLE_VALUE ){
|
||
return STATUS_OBJECT_PATH_NOT_FOUND;
|
||
}
|
||
/*
|
||
}
|
||
*/
|
||
|
||
//
|
||
// Now we got a handle to the parent directory in ParentDir.
|
||
// Allocate input and output data buffer
|
||
//
|
||
|
||
OutputDataSize = INIT_EFS_BLOCK_SIZE;
|
||
*ParentEfsStream = (PEFS_DATA_STREAM_HEADER) LsapAllocateLsaHeap( OutputDataSize );
|
||
if ( *ParentEfsStream == NULL ){
|
||
|
||
CloseHandle( ParentDir );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// PSC, [EFS_FC, CSC , SK, H, H, [SK, H, H]sk]sk
|
||
// PSC, CSC are ignored in this FSCTL call
|
||
//
|
||
|
||
InputDataSize = 2 * sizeof(DriverSessionKey) + 7 * sizeof(ULONG);
|
||
InputData = (PUCHAR)LsapAllocateLsaHeap( InputDataSize );
|
||
if ( InputData == NULL ){
|
||
|
||
LsapFreeLsaHeap( *ParentEfsStream );
|
||
*ParentEfsStream = NULL;
|
||
CloseHandle( ParentDir );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Prepare an input data for making a FSCTL call to get the $EFS
|
||
//
|
||
|
||
EfsDataLength = 2 * sizeof(DriverSessionKey) + 4 * sizeof(ULONG);
|
||
SendHandle( ParentDir, InputData + 3*sizeof(ULONG), &EfsDataLength );
|
||
|
||
(VOID) EncryptFSCTLData(
|
||
EFS_GET_ATTRIBUTE,
|
||
0,
|
||
0,
|
||
InputData + 3*sizeof(ULONG),
|
||
EfsDataLength,
|
||
InputData,
|
||
&InputDataSize
|
||
);
|
||
|
||
Status = NtFsControlFile(
|
||
ParentDir,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_ENCRYPTION_FSCTL_IO,
|
||
InputData,
|
||
InputDataSize,
|
||
*ParentEfsStream,
|
||
OutputDataSize
|
||
);
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// Check if the output data buffer too small
|
||
// Try again if it is.
|
||
//
|
||
|
||
if ( Status == STATUS_BUFFER_TOO_SMALL ){
|
||
|
||
OutputDataSize = *(ULONG*)(*ParentEfsStream);
|
||
if (OutputDataSize > INIT_EFS_BLOCK_SIZE){
|
||
|
||
LsapFreeLsaHeap( *ParentEfsStream );
|
||
*ParentEfsStream = (PEFS_DATA_STREAM_HEADER)LsapAllocateLsaHeap( OutputDataSize );
|
||
if ( *ParentEfsStream ) {
|
||
Status = NtFsControlFile(
|
||
ParentDir,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_ENCRYPTION_FSCTL_IO,
|
||
InputData,
|
||
InputDataSize,
|
||
*ParentEfsStream,
|
||
OutputDataSize
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( !NT_SUCCESS( Status ) ){
|
||
|
||
if ( *ParentEfsStream ){
|
||
LsapFreeLsaHeap( *ParentEfsStream );
|
||
*ParentEfsStream = NULL;
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
}
|
||
}
|
||
|
||
LsapFreeLsaHeap( InputData );
|
||
CloseHandle( ParentDir );
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
GetRootHandle(
|
||
IN HANDLE FileHandle,
|
||
PHANDLE RootDirectoryHandle
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get the handle to the root directory
|
||
|
||
Arguments:
|
||
|
||
FileHandle -- Current file or directory handle.
|
||
|
||
RootDirectoryHandle -- Parent directory
|
||
|
||
Return Value:
|
||
|
||
Status of operation.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
UNICODE_STRING FileId;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
//
|
||
// This is magic. It opens the root directory of a volume by ID,
|
||
// relative to the passed file name.
|
||
//
|
||
|
||
ULONG FileIdBuffer[2];
|
||
|
||
FileIdBuffer[0] = 0x00000005;
|
||
FileIdBuffer[1] = 0x00050000;
|
||
|
||
FileId.Length = FileId.MaximumLength = 8;
|
||
FileId.Buffer = (PWSTR)FileIdBuffer;
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&FileId,
|
||
0,
|
||
FileHandle,
|
||
NULL
|
||
);
|
||
|
||
Status = NtCreateFile(
|
||
RootDirectoryHandle,
|
||
FILE_READ_ATTRIBUTES,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
0,
|
||
FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||
FILE_OPEN,
|
||
FILE_OPEN_BY_FILE_ID,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
return( Status );
|
||
}
|
||
|
||
BOOLEAN
|
||
GetDecryptFsInput(
|
||
IN HANDLE Handle,
|
||
OUT PUCHAR InputData,
|
||
OUT PULONG InputDataSize
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get the handle to the root directory
|
||
|
||
Arguments:
|
||
|
||
Handle -- Current file or directory handle.
|
||
|
||
InputData -- Data buffer for the decrypt FSCTL input data.
|
||
PSC, [EFS_FC, CSC, SK, H, H, [SK, H, H]sk]sk
|
||
|
||
InputDataSize -- FSCTL input data length.
|
||
|
||
Return Value:
|
||
|
||
TRUE IF SUCCESSFUL.
|
||
|
||
--*/
|
||
{
|
||
ULONG RequiredSize;
|
||
ULONG EfsDataSize;
|
||
|
||
RequiredSize = 7 * sizeof( ULONG ) + 2 * sizeof(DriverSessionKey);
|
||
if ( *InputDataSize < RequiredSize ){
|
||
*InputDataSize = RequiredSize;
|
||
return FALSE;
|
||
}
|
||
|
||
*InputDataSize = RequiredSize;
|
||
EfsDataSize = RequiredSize - 3 * sizeof( ULONG );
|
||
|
||
( VOID )SendHandle(
|
||
Handle,
|
||
InputData + 3 * sizeof( ULONG ),
|
||
&EfsDataSize
|
||
);
|
||
|
||
( VOID ) EncryptFSCTLData(
|
||
EFS_SET_ENCRYPT,
|
||
EFS_DECRYPT_STREAM,
|
||
EFS_DECRYPT_STREAM,
|
||
InputData + 3 * sizeof(ULONG),
|
||
EfsDataSize,
|
||
InputData,
|
||
InputDataSize
|
||
);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
NTSTATUS
|
||
EndErrorEncryptFile(
|
||
IN HANDLE FileHandle,
|
||
IN PUCHAR InputData,
|
||
IN ULONG InputDataSize,
|
||
OUT IO_STATUS_BLOCK *IoStatusBlock
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removed the $EFS and clear the encrypt bit for the file.
|
||
|
||
Arguments:
|
||
|
||
FileHandle -- Current file handle.
|
||
|
||
InputData -- Data buffer for the decrypt FSCTL input data.
|
||
PSC, [EFS_FC, CSC, SK, H, H, [SK, H, H]sk]sk
|
||
|
||
InputDataSize -- FSCTL input data length.
|
||
|
||
IoStatusBlock -- Status information from FSCTL call.
|
||
|
||
Return Value:
|
||
|
||
The status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
return (SendSkFsctl(
|
||
EFS_DECRYPT_FILE,
|
||
EFS_DECRYPT_FILE,
|
||
EFS_SET_ENCRYPT,
|
||
InputData,
|
||
InputDataSize,
|
||
FileHandle,
|
||
FSCTL_SET_ENCRYPTION,
|
||
IoStatusBlock
|
||
)
|
||
);
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
SendSkFsctl(
|
||
IN ULONG Psc,
|
||
IN ULONG Csc,
|
||
IN ULONG EfsCode,
|
||
IN PUCHAR InputData,
|
||
IN ULONG InputDataSize,
|
||
IN HANDLE Handle,
|
||
IN ULONG FsCode,
|
||
OUT IO_STATUS_BLOCK *IoStatusBlock
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Send FSCTL call with general EFS Data format. See comments
|
||
for InputData
|
||
|
||
Arguments:
|
||
|
||
Psc -- Plain subcode.
|
||
|
||
Csc -- Cipher subcode
|
||
|
||
EfsCode -- EFS function code.
|
||
|
||
InputData -- Data buffer for the decrypt FSCTL input data.
|
||
PSC, [EFS_FC, CSC, SK, H, H, [SK, H, H]sk]sk
|
||
|
||
InputDataSize -- FSCTL input data length.
|
||
|
||
Handle -- Current stream handle.
|
||
|
||
FsCode -- FSCTL control code.
|
||
|
||
IoStatusBlock -- Status information from FSCTL call.
|
||
|
||
Return Value:
|
||
|
||
The status of operation.
|
||
|
||
--*/
|
||
{
|
||
|
||
ULONG EfsDataLength = InputDataSize - 3 * sizeof (ULONG);
|
||
ULONG RequiredSize = 7 * sizeof( ULONG ) + 2 * sizeof(DriverSessionKey);
|
||
BOOLEAN DummyOutput = FALSE;
|
||
ULONG OutPutLen = 0;
|
||
VOID *OutPutData = NULL;
|
||
|
||
if ( InputDataSize < RequiredSize ){
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
( VOID )SendHandle(
|
||
Handle,
|
||
InputData + 3 * sizeof( ULONG ),
|
||
&EfsDataLength
|
||
);
|
||
|
||
( VOID ) EncryptFSCTLData(
|
||
EfsCode,
|
||
Psc,
|
||
Csc,
|
||
InputData + 3 * sizeof(ULONG),
|
||
EfsDataLength,
|
||
InputData,
|
||
&InputDataSize
|
||
);
|
||
|
||
if (EFS_DECRYPT_STREAM == Psc) {
|
||
OutPutData = (VOID *)&DummyOutput;
|
||
OutPutLen = sizeof(BOOLEAN);
|
||
}
|
||
return ( NtFsControlFile(
|
||
Handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
IoStatusBlock,
|
||
FsCode,
|
||
InputData,
|
||
InputDataSize,
|
||
OutPutData,
|
||
OutPutLen
|
||
)
|
||
);
|
||
|
||
}
|
||
|
||
DWORD
|
||
GetVolumeRoot(
|
||
IN PUNICODE_STRING SrcFileName,
|
||
OUT PUNICODE_STRING RootPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get the root path name from the target file name
|
||
|
||
Arguments:
|
||
|
||
SrcFileName -- Target file name.
|
||
RootPathInfo -- Root path information
|
||
|
||
Return Value:
|
||
|
||
The status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG BufferLength;
|
||
WCHAR *PathName;
|
||
BOOL GotRoot;
|
||
DWORD RetCode = ERROR_SUCCESS;
|
||
|
||
BufferLength = (ULONG)((SrcFileName->Length + sizeof(WCHAR)) <= MAX_PATH * sizeof(WCHAR)?
|
||
(MAX_PATH + 1) * sizeof(WCHAR) : (SrcFileName->Length + sizeof (WCHAR)));
|
||
PathName = (WCHAR *) LsapAllocateLsaHeap(BufferLength);
|
||
|
||
if ( !PathName ) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RootPath->MaximumLength = (USHORT) BufferLength;
|
||
GotRoot = GetVolumePathName(
|
||
SrcFileName->Buffer,
|
||
PathName,
|
||
BufferLength
|
||
);
|
||
|
||
if (GotRoot){
|
||
RootPath->Buffer = PathName;
|
||
RootPath->Length = (USHORT) wcslen(PathName) * sizeof (WCHAR);
|
||
} else {
|
||
RetCode = GetLastError();
|
||
RootPath->Buffer = NULL;
|
||
RootPath->Length = 0;
|
||
RootPath->MaximumLength = 0;
|
||
LsapFreeLsaHeap( PathName );
|
||
}
|
||
|
||
|
||
return RetCode;
|
||
}
|
||
|
||
NTSTATUS
|
||
GetLogFile(
|
||
IN PUNICODE_STRING RootPath,
|
||
OUT HANDLE *LogFile
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Create the log file.
|
||
|
||
Arguments:
|
||
|
||
RootPath -- Volume root name.
|
||
LogFile -- Log file handle
|
||
|
||
Return Value:
|
||
|
||
The status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
HANDLE FileHdl;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
UNICODE_STRING FileName;
|
||
UNICODE_STRING RootNtName;
|
||
UNICODE_STRING LogFileName;
|
||
DWORD FileAttributes;
|
||
PSECURITY_DESCRIPTOR SD;
|
||
BOOLEAN b;
|
||
|
||
b = RtlDosPathNameToNtPathName_U(
|
||
RootPath->Buffer,
|
||
&RootNtName,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if ( b ) {
|
||
//
|
||
// Allocate space for the temp log file name
|
||
//
|
||
|
||
FileName.Buffer = (WCHAR *) LsapAllocateLsaHeap(
|
||
RootNtName.Length +
|
||
EFSDIRLEN +
|
||
TEMPFILELEN);
|
||
if ( !FileName.Buffer ){
|
||
|
||
//
|
||
// Free the NT name
|
||
//
|
||
RtlFreeHeap(
|
||
RtlProcessHeap(),
|
||
0,
|
||
RootNtName.Buffer
|
||
);
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Make the EFS directory name of the volume
|
||
//
|
||
|
||
RtlCopyMemory(FileName.Buffer, RootNtName.Buffer, RootNtName.Length - sizeof(WCHAR));
|
||
RtlCopyMemory(
|
||
FileName.Buffer + (RootNtName.Length / sizeof(WCHAR)) - 1,
|
||
EFSDIR,
|
||
EFSDIRLEN
|
||
);
|
||
FileName.Length = RootNtName.Length + EFSDIRLEN - sizeof(WCHAR);
|
||
FileName.MaximumLength = RootNtName.Length + EFSDIRLEN + TEMPFILELEN;
|
||
FileName.Buffer[FileName.Length / sizeof (WCHAR)] = 0;
|
||
|
||
//
|
||
// Free the NT name
|
||
//
|
||
RtlFreeHeap(
|
||
RtlProcessHeap(),
|
||
0,
|
||
RootNtName.Buffer
|
||
);
|
||
|
||
//
|
||
// Create the LOG directory and file Security Descriptor
|
||
//
|
||
|
||
Status = MakeSystemFullControlSD( &SD );
|
||
if ( NT_SUCCESS(Status) ){
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&FileName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
0,
|
||
SD
|
||
);
|
||
|
||
//
|
||
// Open the EFS Log directory or Create if not exist
|
||
//
|
||
|
||
Status = NtCreateFile(
|
||
&FileHdl,
|
||
MAXIMUM_ALLOWED,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
|
||
FILE_SHARE_VALID_FLAGS,
|
||
FILE_OPEN_IF,
|
||
FILE_DIRECTORY_FILE ,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
if (NT_SUCCESS(Status)){
|
||
|
||
//
|
||
// The cache directory is created hidden and system access only. This will be
|
||
// created before any real encryption is done. So there is no need for us to check
|
||
// the encryption status of this directory.
|
||
//
|
||
|
||
CloseHandle(FileHdl);
|
||
|
||
//
|
||
// Now trying to get the logfile name and create it
|
||
//
|
||
|
||
Status = CreateLogFile( &FileName, SD, LogFile );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Cannot open the EFSCACHE dir
|
||
//
|
||
|
||
EfsLogEntry(
|
||
EVENTLOG_ERROR_TYPE,
|
||
0,
|
||
EFS_OPEN_CACHE_ERROR,
|
||
0,
|
||
sizeof(NTSTATUS),
|
||
NULL,
|
||
&Status
|
||
);
|
||
|
||
}
|
||
{
|
||
|
||
//
|
||
// Delete SD
|
||
//
|
||
|
||
NTSTATUS TmpStatus;
|
||
BOOLEAN Present;
|
||
BOOLEAN b;
|
||
PACL pAcl;
|
||
|
||
TmpStatus = RtlGetDaclSecurityDescriptor(SD, &Present, &pAcl, &b);
|
||
if ( NT_SUCCESS(TmpStatus) && Present ){
|
||
|
||
LsapFreeLsaHeap(pAcl);
|
||
|
||
}
|
||
LsapFreeLsaHeap(SD);
|
||
}
|
||
}
|
||
|
||
LsapFreeLsaHeap( FileName.Buffer );
|
||
} else {
|
||
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
MakeSystemFullControlSD(
|
||
OUT PSECURITY_DESCRIPTOR *ppSD
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Create a system full control Security Descriptor.
|
||
|
||
Arguments:
|
||
|
||
ppSD -- System full control security descriptor
|
||
|
||
Return Value:
|
||
|
||
The status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS NtStatus = STATUS_SUCCESS;
|
||
PSID SystemSid =NULL;
|
||
SID_IDENTIFIER_AUTHORITY IdentifierAuthority=SECURITY_NT_AUTHORITY;
|
||
PACL pAcl =NULL;
|
||
DWORD cAclSize, cSDSize=0;
|
||
|
||
//
|
||
// build system sid
|
||
//
|
||
SystemSid = (PSID) LsapAllocateLsaHeap(RtlLengthRequiredSid(1));
|
||
|
||
if ( NULL == SystemSid )
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
|
||
NtStatus = RtlInitializeSid(SystemSid, &IdentifierAuthority, (UCHAR)1);
|
||
if ( !NT_SUCCESS(NtStatus) ){
|
||
LsapFreeLsaHeap( SystemSid );
|
||
return NtStatus;
|
||
}
|
||
|
||
*(RtlSubAuthoritySid(SystemSid, 0)) = SECURITY_LOCAL_SYSTEM_RID;
|
||
|
||
//
|
||
// build a DACL for system full control
|
||
//
|
||
|
||
cAclSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_OBJECT_ACE) +
|
||
RtlLengthSid(SystemSid);
|
||
|
||
pAcl = (PACL)LsapAllocateLsaHeap(cAclSize);
|
||
*ppSD = (PSECURITY_DESCRIPTOR) LsapAllocateLsaHeap(SECURITY_DESCRIPTOR_MIN_LENGTH);
|
||
|
||
if ( ( NULL == pAcl ) || ( NULL == *ppSD ) ){
|
||
if ( pAcl ) {
|
||
LsapFreeLsaHeap( pAcl );
|
||
}
|
||
if ( *ppSD ) {
|
||
LsapFreeLsaHeap( *ppSD );
|
||
*ppSD = NULL;
|
||
}
|
||
LsapFreeLsaHeap( SystemSid );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory(pAcl, cAclSize);
|
||
|
||
pAcl->AclRevision = ACL_REVISION_DS;
|
||
pAcl->Sbz1 = (BYTE)0;
|
||
pAcl->AclSize = (USHORT)cAclSize;
|
||
pAcl->AceCount = 0;
|
||
|
||
//
|
||
// add a ace to the acl for System full control for file objects
|
||
// the access type is ACCESS_ALLOWED_ACE
|
||
// inheritance flag is CIOI
|
||
//
|
||
NtStatus = RtlAddAccessAllowedAceEx (
|
||
pAcl,
|
||
ACL_REVISION_DS,
|
||
OBJECT_INHERIT_ACE |
|
||
CONTAINER_INHERIT_ACE,
|
||
GENERIC_ALL,
|
||
SystemSid
|
||
);
|
||
|
||
if ( NT_SUCCESS(NtStatus) ){
|
||
|
||
NtStatus = RtlCreateSecurityDescriptor( *ppSD,
|
||
SECURITY_DESCRIPTOR_REVISION );
|
||
|
||
if ( NT_SUCCESS(NtStatus) ){
|
||
|
||
//
|
||
// Then set DACL (permission) to the security descriptor
|
||
//
|
||
|
||
NtStatus = RtlSetDaclSecurityDescriptor (
|
||
*ppSD,
|
||
TRUE,
|
||
pAcl,
|
||
FALSE
|
||
);
|
||
|
||
if ( NT_SUCCESS(NtStatus) ){
|
||
|
||
((SECURITY_DESCRIPTOR *) *ppSD)->Control |= SE_DACL_PROTECTED;
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// free memory for SystemSid
|
||
//
|
||
|
||
LsapFreeLsaHeap( SystemSid );
|
||
|
||
if (!NT_SUCCESS(NtStatus)){
|
||
LsapFreeLsaHeap( pAcl );
|
||
LsapFreeLsaHeap( *ppSD );
|
||
*ppSD = NULL;
|
||
}
|
||
|
||
return (NtStatus);
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
CreateLogFile(
|
||
IN PUNICODE_STRING FileName,
|
||
IN PSECURITY_DESCRIPTOR SD,
|
||
OUT HANDLE *LogFile
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Create a temp log file. We could not use the API GetTempFile to get the temp log file.
|
||
We need our special Security Descriptor.
|
||
|
||
Arguments:
|
||
|
||
FileName -- Directory to create the temp log file. Has enough space to add temporary
|
||
file name.
|
||
|
||
SD -- Security Descriptor.
|
||
|
||
LogFile -- File handle.
|
||
|
||
Return Value:
|
||
|
||
The status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
ULONG Index = 0;
|
||
PWCHAR TempLogFileName;
|
||
int TempFileNameLen;
|
||
USHORT OldLength;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
BOOLEAN StopLoop = FALSE;
|
||
|
||
OldLength = FileName->Length + sizeof (WCHAR);
|
||
FileName->Buffer[ FileName->Length/sizeof(WCHAR) ] = L'\\';
|
||
TempLogFileName = FileName->Buffer + OldLength/sizeof (WCHAR) ;
|
||
|
||
for (; Index < 10000; Index++){
|
||
TempFileNameLen = swprintf( TempLogFileName, L"EFS%d.LOG", Index);
|
||
FileName->Length = OldLength + (USHORT) TempFileNameLen * sizeof (WCHAR);
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
FileName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
0,
|
||
SD
|
||
);
|
||
|
||
Status = NtCreateFile(
|
||
LogFile,
|
||
FILE_READ_DATA | FILE_WRITE_DATA | DELETE | SYNCHRONIZE,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
FILE_ATTRIBUTE_HIDDEN,
|
||
0,
|
||
FILE_CREATE,
|
||
FILE_NO_INTERMEDIATE_BUFFERING | FILE_SYNCHRONOUS_IO_NONALERT ,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
switch (Status) {
|
||
case STATUS_SUCCESS:
|
||
case STATUS_NO_SUCH_FILE:
|
||
case STATUS_OBJECT_PATH_INVALID:
|
||
case STATUS_OBJECT_PATH_SYNTAX_BAD:
|
||
case STATUS_DIRECTORY_IS_A_REPARSE_POINT:
|
||
case STATUS_OBJECT_PATH_NOT_FOUND:
|
||
case STATUS_ACCESS_DENIED:
|
||
case STATUS_DISK_CORRUPT_ERROR:
|
||
case STATUS_FILE_CORRUPT_ERROR:
|
||
case STATUS_DISK_FULL:
|
||
StopLoop = TRUE;
|
||
break;
|
||
default:
|
||
break;
|
||
|
||
}
|
||
if (StopLoop) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
CreateLogHeader(
|
||
IN HANDLE LogFile,
|
||
IN ULONG SectorSize,
|
||
IN PLARGE_INTEGER TragetID,
|
||
IN PLARGE_INTEGER BackupID OPTIONAL,
|
||
IN LPCWSTR SrcFileName,
|
||
IN LPCWSTR BackupFileName OPTIONAL,
|
||
IN EFSP_OPERATION Operation,
|
||
IN EFS_ACTION_STATUS Action,
|
||
OUT ULONG *LogInfoOffset OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Create a log file header.
|
||
|
||
Arguments:
|
||
|
||
LogFile -- A handle to the log file
|
||
|
||
SectorSize -- Sector size of the volume which the log file is in.
|
||
|
||
TragetID -- Target file ID.
|
||
|
||
BackupID -- Backup file ID.
|
||
|
||
SrcFileName -- Target file path
|
||
|
||
BackupFileName -- Backup file path
|
||
|
||
Operation -- Encrypting or Decrypting
|
||
|
||
Action -- The status of the operation.
|
||
|
||
LogInfoOffset -- Starting offset of status info copy
|
||
|
||
Return Value:
|
||
|
||
The status of operation.
|
||
|
||
--*/
|
||
{
|
||
BYTE *WorkBuffer;
|
||
PULONG tmpULong;
|
||
PLARGE_INTEGER tmpLL;
|
||
ULONG WorkOffset;
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
LARGE_INTEGER ByteOffset;
|
||
ULONG BufferSize;
|
||
ULONG HeadDataSize;
|
||
ULONG SrcFileNameLen; // File path name length in bytes
|
||
ULONG BackupFileNameLen;
|
||
|
||
SrcFileNameLen = (wcslen ( SrcFileName ) + 1 ) * sizeof (WCHAR);
|
||
if ( BackupFileName ) {
|
||
BackupFileNameLen = (wcslen ( BackupFileName ) + 1) * sizeof (WCHAR);
|
||
} else {
|
||
BackupFileNameLen = 0;
|
||
}
|
||
|
||
HeadDataSize = sizeof ( LOGHEADER ) + SrcFileNameLen + BackupFileNameLen;
|
||
|
||
BufferSize = HeadDataSize + sizeof (ULONG); // Data + CheckSum
|
||
if ( BufferSize <= SectorSize ){
|
||
BufferSize = SectorSize;
|
||
} else {
|
||
BufferSize = ((ULONG)((BufferSize - 1) / SectorSize ) + 1) * SectorSize;
|
||
}
|
||
|
||
//
|
||
// The memory used here must be aligned with sector boundary.
|
||
// We cannot use LsapAllocateLsaHeap() here
|
||
//
|
||
|
||
WorkBuffer = (BYTE *) VirtualAlloc(
|
||
NULL,
|
||
BufferSize,
|
||
MEM_COMMIT,
|
||
PAGE_READWRITE
|
||
);
|
||
if ( NULL == WorkBuffer ){
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Prepare common log header
|
||
//
|
||
RtlCopyMemory( ((PLOGHEADER)WorkBuffer)->SIGNATURE, LOGSIG, sizeof(WCHAR) * LOGSIGLEN );
|
||
((PLOGHEADER)WorkBuffer)->VerID = LOGVERID;
|
||
((PLOGHEADER)WorkBuffer)->SectorSize = SectorSize;
|
||
if ( Decrypting == Operation ) {
|
||
((PLOGHEADER)WorkBuffer)->Flag = LOG_DECRYPTION;
|
||
} else {
|
||
((PLOGHEADER)WorkBuffer)->Flag = 0;
|
||
}
|
||
|
||
((PLOGHEADER)WorkBuffer)->HeaderSize = HeadDataSize;
|
||
((PLOGHEADER)WorkBuffer)->HeaderBlockSize = BufferSize;
|
||
((PLOGHEADER)WorkBuffer)->TargetFilePathOffset = sizeof( LOGHEADER );
|
||
((PLOGHEADER)WorkBuffer)->TargetFilePathLength = SrcFileNameLen;
|
||
RtlCopyMemory(
|
||
WorkBuffer + ((PLOGHEADER)WorkBuffer)->TargetFilePathOffset,
|
||
SrcFileName,
|
||
SrcFileNameLen
|
||
);
|
||
|
||
if ( BackupFileName ){
|
||
|
||
((PLOGHEADER)WorkBuffer)->TempFilePathOffset = sizeof( LOGHEADER ) + SrcFileNameLen;
|
||
((PLOGHEADER)WorkBuffer)->TempFilePathLength = BackupFileNameLen;
|
||
((PLOGHEADER)WorkBuffer)->TempFileInternalName.QuadPart = BackupID->QuadPart;
|
||
RtlCopyMemory(
|
||
WorkBuffer + ((PLOGHEADER)WorkBuffer)->TempFilePathOffset,
|
||
BackupFileName,
|
||
BackupFileNameLen
|
||
);
|
||
|
||
} else {
|
||
|
||
((PLOGHEADER)WorkBuffer)->TempFilePathOffset = 0;
|
||
((PLOGHEADER)WorkBuffer)->TempFilePathLength = 0;
|
||
((PLOGHEADER)WorkBuffer)->TempFileInternalName.QuadPart = (LONGLONG) 0;
|
||
|
||
}
|
||
|
||
((PLOGHEADER)WorkBuffer)->LengthOfTargetFileInternalName = sizeof (LARGE_INTEGER);
|
||
((PLOGHEADER)WorkBuffer)->TargetFileInternalName.QuadPart = TragetID->QuadPart;
|
||
((PLOGHEADER)WorkBuffer)->LengthOfTempFileInternalName = sizeof (LARGE_INTEGER);
|
||
|
||
|
||
switch (Action){
|
||
case BeginEncryptDir:
|
||
case BeginDecryptDir:
|
||
|
||
//
|
||
// No status information required for directory operation
|
||
// If crash happens before the completion, we always switch the status
|
||
// to decrypted status.
|
||
//
|
||
|
||
((PLOGHEADER)WorkBuffer)->OffsetStatus1 = 0;
|
||
((PLOGHEADER)WorkBuffer)->OffsetStatus2 =0;
|
||
((PLOGHEADER)WorkBuffer)->Flag |= LOG_DIRECTORY;
|
||
break;
|
||
|
||
case BeginEncryptFile:
|
||
case BeginDecryptFile:
|
||
|
||
//
|
||
// To guarantee the atomic operation, status info copy begins
|
||
// at sector boundary.
|
||
//
|
||
|
||
((PLOGHEADER)WorkBuffer)->OffsetStatus1 = BufferSize;
|
||
((PLOGHEADER)WorkBuffer)->OffsetStatus2 = BufferSize + SectorSize;
|
||
if ( LogInfoOffset ){
|
||
*LogInfoOffset = BufferSize;
|
||
}
|
||
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
CreateBlockSum(WorkBuffer, HeadDataSize, BufferSize );
|
||
|
||
//
|
||
// Write out the header sector
|
||
//
|
||
ByteOffset.QuadPart = (LONGLONG) 0;
|
||
|
||
Status = NtWriteFile(
|
||
LogFile,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
WorkBuffer,
|
||
BufferSize,
|
||
&ByteOffset,
|
||
NULL
|
||
);
|
||
|
||
VirtualFree(
|
||
WorkBuffer,
|
||
0,
|
||
MEM_RELEASE
|
||
);
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
WriteLogFile(
|
||
IN HANDLE LogFile,
|
||
IN ULONG SectorSize,
|
||
IN ULONG StartOffset,
|
||
IN EFS_ACTION_STATUS Action
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Write Log Information.
|
||
|
||
Arguments:
|
||
|
||
LogFile -- A handle to the log file
|
||
|
||
SectorSize -- Sector size of the volume which the log file is in.
|
||
|
||
Action -- The status of the operation.
|
||
|
||
Return Value:
|
||
|
||
The status of operation.
|
||
--*/
|
||
{
|
||
BYTE *WorkBuffer;
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
LARGE_INTEGER ByteOffset;
|
||
PULONG tmpULong;
|
||
|
||
//
|
||
// The memory used here must be aligned with sector boundary.
|
||
// We cannot use LsapAllocateLsaHeap() here
|
||
//
|
||
|
||
WorkBuffer = (BYTE *) VirtualAlloc(
|
||
NULL,
|
||
SectorSize,
|
||
MEM_COMMIT,
|
||
PAGE_READWRITE
|
||
);
|
||
if ( NULL == WorkBuffer ){
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
tmpULong = (PULONG) WorkBuffer;
|
||
*tmpULong = 2 * sizeof ( ULONG );
|
||
* (tmpULong + 1) = Action;
|
||
CreateBlockSum(WorkBuffer, *tmpULong, SectorSize );
|
||
|
||
//
|
||
// Write out the header sector
|
||
//
|
||
ByteOffset.QuadPart = (LONGLONG) StartOffset;
|
||
|
||
Status = NtWriteFile(
|
||
LogFile,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
WorkBuffer,
|
||
SectorSize,
|
||
&ByteOffset,
|
||
NULL
|
||
);
|
||
|
||
if ( NT_SUCCESS(Status) ) {
|
||
ByteOffset.QuadPart = (LONGLONG) (StartOffset + SectorSize);
|
||
|
||
Status = NtWriteFile(
|
||
LogFile,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
WorkBuffer,
|
||
SectorSize,
|
||
&ByteOffset,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
VirtualFree(
|
||
WorkBuffer,
|
||
0,
|
||
MEM_RELEASE
|
||
);
|
||
return Status;
|
||
|
||
}
|
||
|
||
ULONG
|
||
GetCheckSum(
|
||
IN BYTE *WorkBuffer,
|
||
IN ULONG Length
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Get the checksum of the written info. A simple checksum
|
||
algorithm is used.
|
||
|
||
Arguments:
|
||
|
||
WorkBuffer -- Starting point
|
||
|
||
Length -- Length of the data to be checksumed.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
ULONG CheckSum = 0;
|
||
ULONG *WorkData;
|
||
|
||
WorkData = (ULONG*)WorkBuffer;
|
||
while ( WorkData < (ULONG *)(WorkBuffer + Length) ){
|
||
|
||
//
|
||
// It is OK to add more bytes beyond WorkBuffer + Length if
|
||
// Length is not a multiple of sizeof (ULONG)
|
||
//
|
||
|
||
CheckSum += *WorkData++;
|
||
}
|
||
|
||
return CheckSum;
|
||
|
||
}
|
||
|
||
VOID
|
||
CreateBlockSum(
|
||
IN BYTE *WorkBuffer,
|
||
IN ULONG Length,
|
||
IN ULONG BlockSize
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Create a simple checksum for the sector. The checksum is not security
|
||
sensitive. Only for the purpose of detecting disk write error. A simple checksum
|
||
algorithm is used.
|
||
|
||
Arguments:
|
||
|
||
WorkBuffer -- Starting point
|
||
|
||
Length -- Length of the data to be checksumed.
|
||
|
||
SectorSize -- Sector size of the volume which the log file is in.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
ULONG CheckSum = 0;
|
||
ULONG *WorkData;
|
||
|
||
ASSERT ( Length <= BlockSize - sizeof (ULONG));
|
||
|
||
CheckSum = GetCheckSum( WorkBuffer, Length );
|
||
|
||
//
|
||
// Put the checksum at the end of sector
|
||
//
|
||
|
||
WorkData = (ULONG*) (WorkBuffer + BlockSize - sizeof(ULONG));
|
||
*WorkData = CheckSum;
|
||
return;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
CreateBackupFile(
|
||
IN PUNICODE_STRING SourceFileNameU,
|
||
OUT HANDLE *BackupFileHdl,
|
||
OUT FILE_INTERNAL_INFORMATION *BackupID,
|
||
OUT LPWSTR *BackupFileName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Create a backup file
|
||
|
||
Arguments:
|
||
|
||
SourceFileName -- Source file name
|
||
|
||
BackupFileHdl -- Backup file handle pointer
|
||
|
||
BackupID -- Backup file ID information.
|
||
|
||
Return Value:
|
||
|
||
The status of operation.
|
||
|
||
--*/
|
||
{
|
||
LONG Index;
|
||
int TempFileNameLen;
|
||
LPWSTR BackupPureName;
|
||
UNICODE_STRING BackupFile;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
PSECURITY_DESCRIPTOR SD;
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// Assume source file name in the format XXX...XXX\XXX or XXX
|
||
// No format of X:XXX. (Must be X:\XXX)
|
||
// The name was converted by APIs. The above assumption should be correct.
|
||
//
|
||
|
||
Index = SourceFileNameU->Length/sizeof(WCHAR) - 1;
|
||
while ( Index >= 0 ){
|
||
//
|
||
// Find the last '\'
|
||
//
|
||
if ( SourceFileNameU->Buffer[Index--] == L'\\'){
|
||
break;
|
||
}
|
||
}
|
||
|
||
Index++;
|
||
|
||
//
|
||
// Adjust the Index to point to the end of the directory not including '\'
|
||
//
|
||
|
||
if ( SourceFileNameU->Buffer[Index] == L'\\'){
|
||
Index++;
|
||
}
|
||
|
||
//
|
||
// Allocate space for the backup file name
|
||
//
|
||
|
||
*BackupFileName = (LPWSTR) LsapAllocateLsaHeap( (Index + 20) * sizeof( WCHAR ));
|
||
|
||
if ( NULL == *BackupFileName ){
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory( *BackupFileName, SourceFileNameU->Buffer, Index * sizeof(WCHAR));
|
||
BackupPureName = *BackupFileName + Index;
|
||
|
||
//
|
||
// Create file Security Descriptor
|
||
//
|
||
|
||
Status = MakeSystemFullControlSD( &SD );
|
||
if ( NT_SUCCESS(Status) ){
|
||
|
||
BOOLEAN StopLoop = FALSE;
|
||
|
||
for (ULONG ii = 0; ii < 100000; ii++){
|
||
|
||
BOOLEAN b;
|
||
|
||
TempFileNameLen = swprintf(BackupPureName, L"EFS%d.TMP", ii);
|
||
b = RtlDosPathNameToNtPathName_U(
|
||
*BackupFileName,
|
||
&BackupFile,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if ( b ){
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&BackupFile,
|
||
OBJ_CASE_INSENSITIVE,
|
||
0,
|
||
SD
|
||
);
|
||
|
||
//
|
||
// Create the EFS Temp file
|
||
// Does not hurt using FILE_OPEN_REPARSE_POINT
|
||
//
|
||
|
||
Status = NtCreateFile(
|
||
BackupFileHdl,
|
||
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE | DELETE,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
|
||
0,
|
||
FILE_CREATE,
|
||
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT,
|
||
NULL,
|
||
0
|
||
);
|
||
if ( STATUS_ACCESS_DENIED == Status ) {
|
||
|
||
//
|
||
// Let's try to open it in the Local_System
|
||
//
|
||
|
||
|
||
RpcRevertToSelf();
|
||
Status = NtCreateFile(
|
||
BackupFileHdl,
|
||
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE | DELETE,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
|
||
0,
|
||
FILE_CREATE,
|
||
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
if (RPC_S_OK != RpcImpersonateClient( NULL )){
|
||
|
||
//
|
||
// This is very unlikely. We have did this before and it all succeeded.
|
||
// If this happens, let's quit as if we get STATUS_ACCESS_DENIED.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
MarkFileForDelete(*BackupFileHdl);
|
||
CloseHandle( *BackupFileHdl );
|
||
*BackupFileHdl = 0;
|
||
}
|
||
|
||
Status = STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
}
|
||
|
||
RtlFreeHeap(
|
||
RtlProcessHeap(),
|
||
0,
|
||
BackupFile.Buffer
|
||
);
|
||
|
||
switch (Status) {
|
||
case STATUS_SUCCESS:
|
||
case STATUS_NO_SUCH_FILE:
|
||
case STATUS_OBJECT_PATH_INVALID:
|
||
case STATUS_OBJECT_PATH_SYNTAX_BAD:
|
||
case STATUS_DIRECTORY_IS_A_REPARSE_POINT:
|
||
case STATUS_OBJECT_PATH_NOT_FOUND:
|
||
case STATUS_ACCESS_DENIED:
|
||
case STATUS_DISK_CORRUPT_ERROR:
|
||
case STATUS_FILE_CORRUPT_ERROR:
|
||
case STATUS_DISK_FULL:
|
||
StopLoop = TRUE;
|
||
break;
|
||
default:
|
||
break;
|
||
|
||
}
|
||
if (StopLoop) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( NT_SUCCESS(Status) ){
|
||
if ( NT_SUCCESS(Status) ){
|
||
|
||
//
|
||
// Get FileID
|
||
//
|
||
Status = NtQueryInformationFile(
|
||
*BackupFileHdl,
|
||
&IoStatusBlock,
|
||
BackupID,
|
||
sizeof ( FILE_INTERNAL_INFORMATION ),
|
||
FileInternalInformation
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ){
|
||
MarkFileForDelete(*BackupFileHdl);
|
||
CloseHandle( *BackupFileHdl );
|
||
*BackupFileHdl = 0;
|
||
}
|
||
|
||
} else {
|
||
|
||
MarkFileForDelete(*BackupFileHdl);
|
||
CloseHandle( *BackupFileHdl );
|
||
*BackupFileHdl = 0;
|
||
|
||
}
|
||
}
|
||
|
||
{
|
||
|
||
//
|
||
// Delete SD
|
||
//
|
||
|
||
NTSTATUS TmpStatus;
|
||
BOOLEAN Present;
|
||
BOOLEAN b;
|
||
PACL pAcl;
|
||
|
||
TmpStatus = RtlGetDaclSecurityDescriptor(SD, &Present, &pAcl, &b);
|
||
if ( NT_SUCCESS(TmpStatus) && Present ){
|
||
|
||
LsapFreeLsaHeap(pAcl);
|
||
|
||
}
|
||
LsapFreeLsaHeap(SD);
|
||
}
|
||
|
||
}
|
||
|
||
if ( !NT_SUCCESS(Status) ){
|
||
|
||
LsapFreeLsaHeap( *BackupFileName );
|
||
*BackupFileName = NULL;
|
||
|
||
}
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SendGenFsctl(
|
||
IN HANDLE Target,
|
||
IN ULONG Psc,
|
||
IN ULONG Csc,
|
||
IN ULONG EfsCode,
|
||
IN ULONG FsCode
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Set the encrypted file status to normal.
|
||
|
||
Arguments:
|
||
|
||
Target -- A handle to the target file or directory.
|
||
|
||
Psc -- Plain sub code
|
||
|
||
Csc -- Cipher sub code
|
||
|
||
EfsCode -- Efs function code
|
||
|
||
FsCode -- FSCTL code
|
||
|
||
Return Value:
|
||
|
||
The status of operation.
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
ULONG InputDataSize;
|
||
PUCHAR InputData;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
InputDataSize = 7 * sizeof ( ULONG ) + 2 * sizeof ( DriverSessionKey );
|
||
InputData = (PUCHAR)LsapAllocateLsaHeap( InputDataSize );
|
||
|
||
if ( InputData == NULL ) {
|
||
|
||
//
|
||
// This is unlikely to happen during the boot time.
|
||
//
|
||
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
//
|
||
// Sync FSCTL assumed
|
||
//
|
||
|
||
Status = SendSkFsctl(
|
||
Psc,
|
||
Csc,
|
||
EfsCode,
|
||
InputData,
|
||
InputDataSize,
|
||
Target,
|
||
FsCode,
|
||
&IoStatusBlock
|
||
);
|
||
|
||
LsapFreeLsaHeap( InputData );
|
||
return Status;
|
||
}
|
||
|
||
|
||
DWORD
|
||
EfsOpenFileRaw(
|
||
IN LPCWSTR FileName,
|
||
IN LPCWSTR LocalFileName,
|
||
IN BOOL NetSession,
|
||
IN ULONG Flags,
|
||
OUT PVOID * Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to open an encrypted file. It opens the file and
|
||
prepares the necessary context to be used in ReadRaw data and WriteRaw
|
||
data.
|
||
|
||
|
||
Arguments:
|
||
|
||
FileName -- Remote File name of the file to be exported. Used to check the share.
|
||
|
||
LocalFileName -- Local file name for real jobs.
|
||
|
||
NetSession -- Indicates network session.
|
||
|
||
Flags -- Indicating if open for export or import; for directory or file.
|
||
|
||
Context - Export context to be used by READ operation later. Caller should
|
||
pass this back in ReadRaw().
|
||
|
||
|
||
Return Value:
|
||
|
||
Result of the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
||
ACCESS_MASK FileAccess = 0 ;
|
||
BOOL Privilege = FALSE; // FALSE - not a backup operator
|
||
ULONG CreateDist = 0;
|
||
ULONG CreateOptions = 0;
|
||
ULONG ShareMode = 0;
|
||
HANDLE HSourceFile;
|
||
NTSTATUS NtStatus;
|
||
DWORD HResult;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
OBJECT_ATTRIBUTES NetObjectAttributes;
|
||
IO_STATUS_BLOCK IoStatus;
|
||
UNICODE_STRING UniFileName;
|
||
UNICODE_STRING UniNetFileName={0,0,NULL};
|
||
BOOLEAN TranslationStatus;
|
||
|
||
PFILE_STREAM_INFORMATION StreamInfoBase = NULL;
|
||
ULONG StreamInfoSize = 0;
|
||
|
||
PUNICODE_STRING StreamNames = NULL;
|
||
PHANDLE StreamHandles = NULL;
|
||
ULONG StreamCount = 0;
|
||
|
||
TOKEN_PRIVILEGES Privs;
|
||
PTOKEN_PRIVILEGES OldPrivs;
|
||
BOOL b;
|
||
HANDLE TokenHandle = 0;
|
||
DWORD ReturnLength;
|
||
|
||
//
|
||
// Convert file name to UNICODE_STRING and UNC format
|
||
// Create an OBJECT_ATTRIBUTES
|
||
//
|
||
|
||
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
||
LocalFileName,
|
||
&UniFileName,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if ( !TranslationStatus ) {
|
||
|
||
return ERROR_PATH_NOT_FOUND;
|
||
|
||
}
|
||
|
||
if (NetSession) {
|
||
|
||
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
||
FileName,
|
||
&UniNetFileName,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if ( !TranslationStatus ) {
|
||
|
||
RtlFreeHeap(
|
||
RtlProcessHeap(),
|
||
0,
|
||
UniFileName.Buffer
|
||
);
|
||
|
||
return ERROR_PATH_NOT_FOUND;
|
||
|
||
}
|
||
|
||
InitializeObjectAttributes(
|
||
&NetObjectAttributes,
|
||
&UniNetFileName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
}
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&UniFileName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if ( Flags & CREATE_FOR_IMPORT ){
|
||
|
||
//
|
||
// Prepare parameters for create of import
|
||
//
|
||
|
||
FileAccess = FILE_WRITE_ATTRIBUTES;
|
||
|
||
if ( Flags & CREATE_FOR_DIR ){
|
||
|
||
//
|
||
// Import a directory
|
||
//
|
||
|
||
FileAccess |= FILE_WRITE_DATA | FILE_READ_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE;
|
||
CreateDist = FILE_OPEN_IF;
|
||
CreateOptions |= FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_COMPRESSION;
|
||
FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Import file
|
||
// Should we use FILE_SUPERSEDE here?
|
||
//
|
||
|
||
FileAccess |= SYNCHRONIZE;
|
||
CreateDist = FILE_OVERWRITE_IF;
|
||
CreateOptions |= FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_COMPRESSION;
|
||
if (Flags & OVERWRITE_HIDDEN) {
|
||
FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// If export is requested and the file is not encrypted,
|
||
// Fail the call.
|
||
//
|
||
|
||
FileAccess = FILE_READ_ATTRIBUTES;
|
||
|
||
//
|
||
// Prepare parameters for create of export
|
||
//
|
||
|
||
CreateDist = FILE_OPEN;
|
||
if ( Flags & CREATE_FOR_DIR ){
|
||
|
||
//
|
||
// Export a directory
|
||
//
|
||
|
||
FileAccess |= FILE_READ_DATA;
|
||
CreateOptions |= FILE_DIRECTORY_FILE;
|
||
|
||
} else {
|
||
FileAccess |= SYNCHRONIZE;
|
||
CreateOptions |= FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT;
|
||
}
|
||
|
||
}
|
||
|
||
OldPrivs = ( TOKEN_PRIVILEGES *) LsapAllocateLsaHeap(sizeof( TOKEN_PRIVILEGES ));
|
||
|
||
if ( OldPrivs == NULL ){
|
||
|
||
RtlFreeHeap(
|
||
RtlProcessHeap(),
|
||
0,
|
||
UniFileName.Buffer
|
||
);
|
||
|
||
if (NetSession) {
|
||
|
||
RtlFreeHeap(
|
||
RtlProcessHeap(),
|
||
0,
|
||
UniNetFileName.Buffer
|
||
);
|
||
|
||
}
|
||
|
||
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
|
||
}
|
||
|
||
//
|
||
// We're impersonating, use the thread token.
|
||
//
|
||
|
||
b = OpenThreadToken(
|
||
GetCurrentThread(),
|
||
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
||
FALSE,
|
||
&TokenHandle
|
||
);
|
||
|
||
if ( b ) {
|
||
|
||
//
|
||
// We've got a token handle
|
||
//
|
||
|
||
//
|
||
// If we're doing a create for import, enable restore privilege,
|
||
// otherwise enable backup privilege.
|
||
//
|
||
|
||
|
||
Privs.PrivilegeCount = 1;
|
||
Privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||
|
||
if ( !(Flags & CREATE_FOR_IMPORT) ){
|
||
|
||
Privs.Privileges[0].Luid = RtlConvertLongToLuid(SE_BACKUP_PRIVILEGE);
|
||
|
||
} else {
|
||
|
||
Privs.Privileges[0].Luid = RtlConvertLongToLuid(SE_RESTORE_PRIVILEGE);
|
||
}
|
||
|
||
ReturnLength = sizeof( TOKEN_PRIVILEGES );
|
||
|
||
(VOID) AdjustTokenPrivileges (
|
||
TokenHandle,
|
||
FALSE,
|
||
&Privs,
|
||
sizeof( TOKEN_PRIVILEGES ),
|
||
OldPrivs,
|
||
&ReturnLength
|
||
);
|
||
|
||
if ( ERROR_SUCCESS == GetLastError() ) {
|
||
|
||
Privilege = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Privilege adjust failed
|
||
//
|
||
|
||
CloseHandle( TokenHandle );
|
||
TokenHandle = 0;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We did not get the handle.
|
||
//
|
||
|
||
TokenHandle = 0;
|
||
|
||
}
|
||
|
||
//
|
||
// Caller will call RpcRevertToSelf().
|
||
// OldPrivs is not needed any more.
|
||
//
|
||
|
||
LsapFreeLsaHeap( OldPrivs );
|
||
OldPrivs = NULL;
|
||
|
||
if ( !Privilege ){
|
||
|
||
//
|
||
// Not a backup operator
|
||
//
|
||
if ( !(Flags & CREATE_FOR_IMPORT) ){
|
||
|
||
FileAccess |= FILE_READ_DATA;
|
||
|
||
} else {
|
||
|
||
FileAccess |= FILE_WRITE_DATA;
|
||
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// A backup operator or the user with the privilege
|
||
//
|
||
|
||
CreateOptions |= FILE_OPEN_FOR_BACKUP_INTENT;
|
||
if ( !(Flags & CREATE_FOR_DIR) ){
|
||
|
||
FileAccess |= DELETE;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (NetSession) {
|
||
|
||
//
|
||
// This create is for checking share access only. The handle from this is not good for
|
||
// FSCTL with data buffer larger than 64K.
|
||
//
|
||
|
||
NtStatus = NtCreateFile(
|
||
&HSourceFile,
|
||
FileAccess,
|
||
&NetObjectAttributes,
|
||
&IoStatus,
|
||
(PLARGE_INTEGER) NULL,
|
||
FileAttributes,
|
||
ShareMode,
|
||
CreateDist,
|
||
CreateOptions,
|
||
(PVOID) NULL,
|
||
0L
|
||
);
|
||
|
||
RtlFreeHeap(
|
||
RtlProcessHeap(),
|
||
0,
|
||
UniNetFileName.Buffer
|
||
);
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
CloseHandle( HSourceFile );
|
||
} else {
|
||
|
||
RtlFreeHeap(
|
||
RtlProcessHeap(),
|
||
0,
|
||
UniFileName.Buffer
|
||
);
|
||
|
||
if ( TokenHandle ){
|
||
CloseHandle( TokenHandle );
|
||
}
|
||
|
||
return RtlNtStatusToDosError( NtStatus );
|
||
}
|
||
|
||
}
|
||
|
||
NtStatus = NtCreateFile(
|
||
&HSourceFile,
|
||
FileAccess,
|
||
&ObjectAttributes,
|
||
&IoStatus,
|
||
(PLARGE_INTEGER) NULL,
|
||
FileAttributes,
|
||
ShareMode,
|
||
CreateDist,
|
||
CreateOptions,
|
||
(PVOID) NULL,
|
||
0L
|
||
);
|
||
|
||
RtlFreeHeap(
|
||
RtlProcessHeap(),
|
||
0,
|
||
UniFileName.Buffer
|
||
);
|
||
|
||
//
|
||
// No need for FILE_DIRECTORY_FILE any more
|
||
//
|
||
|
||
CreateOptions &= ~FILE_DIRECTORY_FILE;
|
||
FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
|
||
|
||
if (NT_SUCCESS(NtStatus)){
|
||
|
||
if ( Flags & CREATE_FOR_IMPORT ){
|
||
|
||
if (Flags & CREATE_FOR_DIR) {
|
||
|
||
//
|
||
// If the dir existed and compressed, we need extra steps to uncompressed it
|
||
//
|
||
|
||
|
||
FILE_BASIC_INFORMATION StreamBasicInfo;
|
||
|
||
//
|
||
// Get File Attributes
|
||
//
|
||
NtStatus = NtQueryInformationFile(
|
||
HSourceFile,
|
||
&IoStatus,
|
||
&StreamBasicInfo,
|
||
sizeof ( FILE_BASIC_INFORMATION ),
|
||
FileBasicInformation
|
||
);
|
||
|
||
if (NT_SUCCESS(NtStatus)){
|
||
if (StreamBasicInfo.FileAttributes & FILE_ATTRIBUTE_COMPRESSED){
|
||
|
||
USHORT State = COMPRESSION_FORMAT_NONE;
|
||
ULONG Length;
|
||
|
||
//
|
||
// Attempt to uncompress the directory
|
||
//
|
||
|
||
b = DeviceIoControl(
|
||
HSourceFile,
|
||
FSCTL_SET_COMPRESSION,
|
||
&State,
|
||
sizeof(USHORT),
|
||
NULL,
|
||
0,
|
||
&Length,
|
||
FALSE
|
||
);
|
||
|
||
if (!b) {
|
||
HResult = GetLastError();
|
||
CloseHandle( HSourceFile );
|
||
if ( TokenHandle ){
|
||
CloseHandle( TokenHandle );
|
||
}
|
||
return HResult;
|
||
}
|
||
}
|
||
} else {
|
||
|
||
CloseHandle( HSourceFile );
|
||
if ( TokenHandle ){
|
||
CloseHandle( TokenHandle );
|
||
}
|
||
return RtlNtStatusToDosError( NtStatus );
|
||
|
||
}
|
||
|
||
}
|
||
//
|
||
// Prepare import context
|
||
//
|
||
|
||
*Context = LsapAllocateLsaHeap(sizeof( IMPORT_CONTEXT ));
|
||
|
||
if ( *Context ){
|
||
|
||
(( PIMPORT_CONTEXT ) *Context)->Flag = CONTEXT_FOR_IMPORT;
|
||
if (Flags & CREATE_FOR_DIR) {
|
||
(( PIMPORT_CONTEXT ) *Context)->Flag |= CONTEXT_OPEN_FOR_DIR;
|
||
}
|
||
(( PIMPORT_CONTEXT ) *Context)->Handle = HSourceFile;
|
||
(( PIMPORT_CONTEXT ) *Context)->ContextID = EFS_CONTEXT_ID;
|
||
(( PIMPORT_CONTEXT ) *Context)->Attribute = FileAttributes;
|
||
(( PIMPORT_CONTEXT ) *Context)->CreateDisposition = CreateDist;
|
||
(( PIMPORT_CONTEXT ) *Context)->CreateOptions = CreateOptions;
|
||
(( PIMPORT_CONTEXT ) *Context)->DesiredAccess = FileAccess;
|
||
|
||
} else {
|
||
|
||
CloseHandle( HSourceFile );
|
||
HSourceFile = 0;
|
||
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
|
||
FILE_BASIC_INFORMATION StreamBasicInfo;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
//
|
||
// Get File Attributes
|
||
//
|
||
NtStatus = NtQueryInformationFile(
|
||
HSourceFile,
|
||
&IoStatusBlock,
|
||
&StreamBasicInfo,
|
||
sizeof ( FILE_BASIC_INFORMATION ),
|
||
FileBasicInformation
|
||
);
|
||
|
||
if ( NT_SUCCESS(NtStatus)) {
|
||
|
||
if ( !(StreamBasicInfo.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) ){
|
||
|
||
NtStatus = STATUS_ACCESS_DENIED;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
|
||
CloseHandle( HSourceFile );
|
||
HSourceFile = 0;
|
||
|
||
if ( TokenHandle ){
|
||
CloseHandle( TokenHandle );
|
||
}
|
||
|
||
return RtlNtStatusToDosError( NtStatus );
|
||
|
||
}
|
||
|
||
//
|
||
// Prepare export context
|
||
//
|
||
|
||
NtStatus = GetStreamInformation(
|
||
HSourceFile,
|
||
&StreamInfoBase,
|
||
&StreamInfoSize
|
||
);
|
||
|
||
if (NT_SUCCESS(NtStatus)){
|
||
|
||
if (FileAccess & DELETE) {
|
||
ShareMode |= FILE_SHARE_DELETE;
|
||
}
|
||
|
||
HResult = OpenFileStreams(
|
||
HSourceFile,
|
||
ShareMode,
|
||
OPEN_FOR_EXP,
|
||
StreamInfoBase,
|
||
FileAccess,
|
||
CreateDist,
|
||
CreateOptions,
|
||
NULL,
|
||
&StreamNames,
|
||
&StreamHandles,
|
||
NULL,
|
||
&StreamCount
|
||
);
|
||
|
||
if ( HResult == NO_ERROR ) {
|
||
|
||
*Context = LsapAllocateLsaHeap( sizeof( EXPORT_CONTEXT ) );
|
||
|
||
if ( *Context ){
|
||
|
||
((PEXPORT_CONTEXT) *Context)->Flag = CONTEXT_FOR_EXPORT;
|
||
if (Flags & CREATE_FOR_DIR) {
|
||
(( PEXPORT_CONTEXT ) *Context)->Flag |= CONTEXT_OPEN_FOR_DIR;
|
||
}
|
||
((PEXPORT_CONTEXT) *Context)->Handle = HSourceFile;
|
||
((PEXPORT_CONTEXT ) *Context)->ContextID = EFS_CONTEXT_ID;
|
||
((PEXPORT_CONTEXT) *Context)->NumberOfStreams = StreamCount;
|
||
((PEXPORT_CONTEXT) *Context)->StreamHandles = StreamHandles;
|
||
((PEXPORT_CONTEXT) *Context)->StreamNames = StreamNames;
|
||
((PEXPORT_CONTEXT) *Context)->StreamInfoBase = StreamInfoBase;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Out of memory
|
||
//
|
||
|
||
CleanupOpenFileStreams(
|
||
StreamHandles,
|
||
StreamNames,
|
||
NULL,
|
||
StreamInfoBase,
|
||
HSourceFile,
|
||
StreamCount
|
||
);
|
||
|
||
StreamHandles = NULL;
|
||
StreamNames = NULL;
|
||
StreamInfoBase = NULL;
|
||
HSourceFile = 0;
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Open streams wrong, free StreamInfoBase
|
||
//
|
||
|
||
if (StreamInfoBase) {
|
||
LsapFreeLsaHeap( StreamInfoBase );
|
||
StreamInfoBase = NULL;
|
||
}
|
||
CloseHandle( HSourceFile );
|
||
HSourceFile = 0;
|
||
if ( TokenHandle ){
|
||
CloseHandle( TokenHandle );
|
||
}
|
||
|
||
return HResult;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Get stream info wrong
|
||
//
|
||
|
||
CloseHandle( HSourceFile );
|
||
HSourceFile = 0;
|
||
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
if ( TokenHandle ){
|
||
CloseHandle( TokenHandle );
|
||
}
|
||
|
||
return RtlNtStatusToDosError( NtStatus );
|
||
|
||
}
|
||
|
||
VOID
|
||
EfsCloseFileRaw(
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine frees the resources allocated by the CreateRaw
|
||
|
||
Arguments:
|
||
|
||
Context - Created by the EfsOpenFileRaw.
|
||
|
||
Return Value:
|
||
|
||
NO.
|
||
|
||
--*/
|
||
{
|
||
if ( !Context || (((PEXPORT_CONTEXT) Context)->ContextID != EFS_CONTEXT_ID) ){
|
||
return;
|
||
}
|
||
|
||
if ( !(((PEXPORT_CONTEXT) Context)->Flag & CONTEXT_INVALID) ){
|
||
|
||
if ( ((PEXPORT_CONTEXT) Context)->Flag & CONTEXT_FOR_IMPORT ){
|
||
|
||
//
|
||
// Free import context
|
||
//
|
||
|
||
CleanupOpenFileStreams(
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
((PIMPORT_CONTEXT) Context)->Handle,
|
||
0
|
||
);
|
||
|
||
//
|
||
// Defensive code
|
||
//
|
||
|
||
((PIMPORT_CONTEXT) Context)->Flag |= CONTEXT_INVALID;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Free export context
|
||
//
|
||
|
||
CleanupOpenFileStreams(
|
||
((PEXPORT_CONTEXT) Context)->StreamHandles,
|
||
((PEXPORT_CONTEXT) Context)->StreamNames,
|
||
NULL,
|
||
((PEXPORT_CONTEXT) Context)->StreamInfoBase,
|
||
((PEXPORT_CONTEXT) Context)->Handle,
|
||
((PEXPORT_CONTEXT) Context)->NumberOfStreams
|
||
);
|
||
|
||
//
|
||
// Defensive code
|
||
//
|
||
|
||
((PEXPORT_CONTEXT) Context)->Flag |= CONTEXT_INVALID;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
LsapFreeLsaHeap( Context);
|
||
|
||
}
|
||
|
||
long EfsReadFileRaw(
|
||
PVOID Context,
|
||
PVOID EfsOutPipe
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to read encrypted file's raw data. It uses
|
||
NTFS FSCTL to get the data.
|
||
|
||
Arguments:
|
||
|
||
Context -- Context handle.
|
||
EfsOutPipe -- Pipe handle.
|
||
|
||
Return Value:
|
||
|
||
The result of operation.
|
||
|
||
--*/
|
||
{
|
||
|
||
VOID *FsctlInput = NULL;
|
||
VOID *WorkBuffer = NULL;
|
||
VOID *BufPointer;
|
||
VOID *FsctlOutput;
|
||
USHORT *PUShort;
|
||
PULONG PUlong;
|
||
ULONG FsctlInputLength;
|
||
ULONG EfsDataLength;
|
||
ULONG FsctlOutputLength;
|
||
ULONG SendDataLength;
|
||
ULONG WkBufLength;
|
||
ULONG BytesAdvanced;
|
||
DWORD HResult = NO_ERROR;
|
||
BOOLEAN MoreToRead = TRUE;
|
||
BOOLEAN StreamEncrypted = TRUE;
|
||
ULONG StreamIndex;
|
||
LONGLONG StreamOffset;
|
||
NTSTATUS NtStatus;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
FILE_BASIC_INFORMATION StreamInfo;
|
||
ULONG ii;
|
||
|
||
if ( !Context ||
|
||
( ( (PEXPORT_CONTEXT) Context)->Flag &
|
||
( CONTEXT_FOR_IMPORT | CONTEXT_INVALID )) ||
|
||
(((PEXPORT_CONTEXT) Context)->ContextID != EFS_CONTEXT_ID)
|
||
){
|
||
|
||
//
|
||
// Flush the pipe and return error.
|
||
//
|
||
|
||
HResult = EFSSendPipeData( (char *)&SendDataLength, 0, EfsOutPipe );
|
||
return ERROR_ACCESS_DENIED;
|
||
|
||
}
|
||
|
||
//
|
||
// Allocate necessary memory
|
||
//
|
||
|
||
FsctlInput = LsapAllocateLsaHeap( FSCTL_EXPORT_INPUT_LENGTH );
|
||
|
||
//
|
||
// Try to allocate a reasonable size buffer. The size can be fine tuned later, but should
|
||
// at least one page plus 4K. FSCTL_OUTPUT_LESS_LENGTH should be n * page size.
|
||
// FSCTL_OUTPUT_MIN_LENGTH can be fine tuned later. It should be at least one page
|
||
// plus 4K.
|
||
//
|
||
|
||
WkBufLength = FSCTL_OUTPUT_INITIAL_LENGTH;
|
||
|
||
while ( !WorkBuffer && WkBufLength >= FSCTL_OUTPUT_MIN_LENGTH ){
|
||
|
||
//
|
||
// Sector alignment is required here.
|
||
//
|
||
|
||
WorkBuffer = VirtualAlloc(
|
||
NULL,
|
||
WkBufLength,
|
||
MEM_COMMIT,
|
||
PAGE_READWRITE
|
||
);
|
||
|
||
if ( !WorkBuffer ){
|
||
|
||
//
|
||
// Memory allocation failed.
|
||
// Try smaller allocation.
|
||
//
|
||
|
||
WkBufLength -= FSCTL_OUTPUT_LESS_LENGTH;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if ( !WorkBuffer || !FsctlInput ){
|
||
|
||
//
|
||
// Not enough memory to run export
|
||
//
|
||
|
||
if ( WorkBuffer ){
|
||
|
||
VirtualFree(
|
||
WorkBuffer,
|
||
0,
|
||
MEM_RELEASE
|
||
);
|
||
|
||
}
|
||
|
||
if ( FsctlInput ){
|
||
|
||
LsapFreeLsaHeap( FsctlInput );
|
||
|
||
}
|
||
|
||
//
|
||
// Flush the pipe and return error.
|
||
//
|
||
|
||
HResult = EFSSendPipeData( (char *)&SendDataLength, 0, EfsOutPipe );
|
||
return ERROR_OUTOFMEMORY;
|
||
|
||
}
|
||
|
||
RtlZeroMemory( FsctlInput, FSCTL_EXPORT_INPUT_LENGTH );
|
||
RtlZeroMemory( WorkBuffer, WkBufLength );
|
||
|
||
//
|
||
// Prepare the export file header
|
||
//
|
||
|
||
(( PEFSEXP_FILE_HEADER )WorkBuffer )->VersionID = EFS_EXP_FORMAT_CURRENT_VERSION;
|
||
RtlCopyMemory( &((( PEFSEXP_FILE_HEADER )WorkBuffer )->FileSignature[0]),
|
||
FILE_SIGNATURE,
|
||
EFS_SIGNATURE_LENGTH * sizeof( WCHAR )
|
||
);
|
||
|
||
BufPointer = (char *) WorkBuffer + sizeof ( EFSEXP_FILE_HEADER );
|
||
(( PEFSEXP_STREAM_HEADER )BufPointer )->Length = sizeof (USHORT) +
|
||
sizeof (EFSEXP_STREAM_HEADER);
|
||
|
||
RtlCopyMemory( &((( PEFSEXP_STREAM_HEADER )BufPointer )->StreamSignature[0]),
|
||
STREAM_SIGNATURE,
|
||
EFS_SIGNATURE_LENGTH * sizeof( WCHAR )
|
||
);
|
||
(( PEFSEXP_STREAM_HEADER )BufPointer )->NameLength = sizeof (USHORT);
|
||
|
||
BufPointer = (char *)BufPointer + sizeof (EFSEXP_STREAM_HEADER);
|
||
PUShort = (USHORT *)BufPointer;
|
||
*PUShort = EFS_STREAM_ID;
|
||
|
||
//
|
||
// Let's send out the File header and stream header
|
||
//
|
||
|
||
|
||
SendDataLength = (ULONG)((char *)BufPointer - (char *)WorkBuffer) + sizeof (USHORT);
|
||
HResult = EFSSendPipeData( (char *)WorkBuffer, SendDataLength, EfsOutPipe );
|
||
if (HResult != NO_ERROR) {
|
||
|
||
VirtualFree(
|
||
WorkBuffer,
|
||
0,
|
||
MEM_RELEASE
|
||
);
|
||
|
||
|
||
LsapFreeLsaHeap( FsctlInput );
|
||
|
||
|
||
//
|
||
// Flush the pipe and return error.
|
||
//
|
||
|
||
(void) EFSSendPipeData( (char *)&SendDataLength, 0, EfsOutPipe );
|
||
return HResult;
|
||
}
|
||
|
||
//
|
||
// Reset BufPointer so that it is aligned again.
|
||
//
|
||
|
||
RtlZeroMemory( WorkBuffer, SendDataLength );
|
||
BufPointer = WorkBuffer;
|
||
|
||
RtlCopyMemory( &((( PEFSEXP_DATA_HEADER )BufPointer )->DataSignature[0]),
|
||
DATA_SIGNATURE,
|
||
EFS_SIGNATURE_LENGTH * sizeof( WCHAR )
|
||
);
|
||
FsctlOutput = (char *)BufPointer + sizeof ( EFSEXP_DATA_HEADER );
|
||
FsctlOutputLength = WkBufLength - ( (ULONG) (( char* ) FsctlOutput - ( char* )WorkBuffer) );
|
||
|
||
//
|
||
// Issue the FSCTL to get the $EFS
|
||
//
|
||
EfsDataLength = FsctlInputLength = COMMON_FSCTL_HEADER_SIZE;
|
||
|
||
( VOID )SendHandle(
|
||
((PEXPORT_CONTEXT) Context)->Handle,
|
||
(PUCHAR)FsctlInput + 3 * sizeof( ULONG ),
|
||
&EfsDataLength
|
||
);
|
||
|
||
( VOID ) EncryptFSCTLData(
|
||
EFS_GET_ATTRIBUTE,
|
||
0,
|
||
0,
|
||
(PUCHAR)FsctlInput + 3 * sizeof(ULONG),
|
||
EfsDataLength,
|
||
(PUCHAR)FsctlInput,
|
||
&FsctlInputLength
|
||
);
|
||
|
||
NtStatus = NtFsControlFile(
|
||
((PEXPORT_CONTEXT) Context)->Handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_ENCRYPTION_FSCTL_IO,
|
||
FsctlInput,
|
||
FsctlInputLength,
|
||
FsctlOutput,
|
||
FsctlOutputLength
|
||
);
|
||
|
||
if (!NT_SUCCESS( NtStatus )) {
|
||
|
||
//
|
||
// Check if the output data buffer too small
|
||
// Try again if it is.
|
||
//
|
||
|
||
if ( NtStatus == STATUS_BUFFER_TOO_SMALL ){
|
||
|
||
ULONG EfsMetaDataLength;
|
||
ULONG BytesInBuffer;
|
||
VOID *TmpBuffer;
|
||
|
||
EfsMetaDataLength = *((ULONG *)FsctlOutput);
|
||
BytesInBuffer = (ULONG) (( char* ) FsctlOutput - ( char* )WorkBuffer);
|
||
WkBufLength = EfsMetaDataLength + BytesInBuffer;
|
||
|
||
// Make it a multiple of 4K
|
||
|
||
WkBufLength = ((WkBufLength + FSCTL_OUTPUT_MISC_LENGTH - 1) / FSCTL_OUTPUT_MISC_LENGTH) * FSCTL_OUTPUT_MISC_LENGTH;
|
||
|
||
TmpBuffer = VirtualAlloc(
|
||
NULL,
|
||
WkBufLength,
|
||
MEM_COMMIT,
|
||
PAGE_READWRITE
|
||
);
|
||
|
||
if (TmpBuffer) {
|
||
RtlCopyMemory(TmpBuffer, WorkBuffer, BytesInBuffer);
|
||
|
||
VirtualFree(
|
||
WorkBuffer,
|
||
0,
|
||
MEM_RELEASE
|
||
);
|
||
WorkBuffer = TmpBuffer;
|
||
FsctlOutput = (char *)WorkBuffer + BytesInBuffer;
|
||
FsctlOutputLength = WkBufLength - BytesInBuffer;
|
||
BufPointer = WorkBuffer;
|
||
|
||
NtStatus = NtFsControlFile(
|
||
((PEXPORT_CONTEXT) Context)->Handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_ENCRYPTION_FSCTL_IO,
|
||
FsctlInput,
|
||
FsctlInputLength,
|
||
FsctlOutput,
|
||
FsctlOutputLength
|
||
);
|
||
} else {
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( NT_SUCCESS(NtStatus)){
|
||
|
||
(( PEFSEXP_DATA_HEADER )BufPointer )->Length = sizeof (EFSEXP_DATA_HEADER) + *((ULONG *)FsctlOutput);
|
||
|
||
//
|
||
// Send out the $EFS stream
|
||
//
|
||
|
||
SendDataLength = WkBufLength - FsctlOutputLength + *((ULONG *)FsctlOutput);
|
||
|
||
HResult = EFSSendPipeData( (char *)WorkBuffer, SendDataLength, EfsOutPipe );
|
||
|
||
//
|
||
// Now begin to processing other data streams
|
||
//
|
||
|
||
|
||
StreamIndex = 0;
|
||
StreamOffset = 0;
|
||
|
||
if (((PEXPORT_CONTEXT) Context)->NumberOfStreams == 0) {
|
||
|
||
MoreToRead = FALSE;
|
||
|
||
} else {
|
||
|
||
( (PREQUEST_RAW_ENCRYPTED_DATA)FsctlInput )->Length = WkBufLength - FSCTL_OUTPUT_MISC_LENGTH;
|
||
FsctlInputLength = sizeof ( REQUEST_RAW_ENCRYPTED_DATA );
|
||
|
||
}
|
||
|
||
while ( (HResult == NO_ERROR) && MoreToRead ){
|
||
|
||
//
|
||
// Fill the request header
|
||
//
|
||
|
||
( (PREQUEST_RAW_ENCRYPTED_DATA)FsctlInput )->FileOffset = StreamOffset;
|
||
|
||
//
|
||
// Prepare output data
|
||
//
|
||
|
||
BufPointer = WorkBuffer ;
|
||
|
||
if ( 0 == StreamOffset ){
|
||
|
||
//
|
||
// Check if the stream is encrypted or not
|
||
// For the current version, we only support non-encrypted
|
||
// stream in directory file. Non-encrypted stream in normal
|
||
// file may be exported but import is not supported. EFS does
|
||
// not support mixed data stream in a file.
|
||
//
|
||
|
||
NtStatus = NtQueryInformationFile(
|
||
((PEXPORT_CONTEXT) Context)->StreamHandles[ StreamIndex ],
|
||
&IoStatusBlock,
|
||
&StreamInfo,
|
||
sizeof (FILE_BASIC_INFORMATION),
|
||
FileBasicInformation
|
||
);
|
||
|
||
if ( !NT_SUCCESS( NtStatus ) ){
|
||
|
||
//
|
||
// Error occured. Quit processing.
|
||
//
|
||
|
||
HResult = RtlNtStatusToDosError( NtStatus );
|
||
break;
|
||
|
||
}
|
||
|
||
if ( StreamInfo.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED ){
|
||
|
||
StreamEncrypted = TRUE;
|
||
(( PEFSEXP_STREAM_HEADER )BufPointer )->Flag = 0;
|
||
|
||
} else {
|
||
|
||
StreamEncrypted = FALSE;
|
||
(( PEFSEXP_STREAM_HEADER )BufPointer )->Flag = STREAM_NOT_ENCRYPTED;
|
||
|
||
}
|
||
|
||
//
|
||
// A new stream started. Insert a stream header
|
||
//
|
||
|
||
(( PEFSEXP_STREAM_HEADER )BufPointer )->NameLength =
|
||
((PEXPORT_CONTEXT) Context)->StreamNames[ StreamIndex ].Length;
|
||
SendDataLength = (( PEFSEXP_STREAM_HEADER )BufPointer )->Length =
|
||
(( PEFSEXP_STREAM_HEADER )BufPointer )->NameLength +
|
||
sizeof (EFSEXP_STREAM_HEADER);
|
||
|
||
RtlCopyMemory( &((( PEFSEXP_STREAM_HEADER )BufPointer )->StreamSignature[0]),
|
||
STREAM_SIGNATURE,
|
||
EFS_SIGNATURE_LENGTH * sizeof( WCHAR )
|
||
);
|
||
|
||
(( PEFSEXP_STREAM_HEADER )BufPointer )->Reserved[0] =
|
||
(( PEFSEXP_STREAM_HEADER )BufPointer )->Reserved[1] =
|
||
0;
|
||
|
||
BufPointer = (char *)BufPointer + sizeof (EFSEXP_STREAM_HEADER);
|
||
|
||
RtlCopyMemory( BufPointer,
|
||
((PEXPORT_CONTEXT) Context)->StreamNames[ StreamIndex ].Buffer,
|
||
((PEXPORT_CONTEXT) Context)->StreamNames[ StreamIndex ].Length
|
||
);
|
||
|
||
//
|
||
// Let's send out the data so that we can better aligned for data section
|
||
//
|
||
|
||
HResult = EFSSendPipeData( (char *)WorkBuffer, SendDataLength, EfsOutPipe );
|
||
if (HResult != NO_ERROR) {
|
||
break;
|
||
} else {
|
||
BufPointer = WorkBuffer;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Prepare data header
|
||
//
|
||
|
||
(( PEFSEXP_DATA_HEADER )BufPointer )->Flag = 0;
|
||
RtlCopyMemory( &((( PEFSEXP_DATA_HEADER )BufPointer )->DataSignature[0]),
|
||
DATA_SIGNATURE,
|
||
EFS_SIGNATURE_LENGTH * sizeof( WCHAR )
|
||
);
|
||
FsctlOutput = (char *)BufPointer + sizeof ( EFSEXP_DATA_HEADER );
|
||
FsctlOutputLength = WkBufLength - (ULONG)( ( char* ) FsctlOutput - ( char* )WorkBuffer);
|
||
|
||
//
|
||
// Read raw data
|
||
//
|
||
if ( StreamEncrypted ){
|
||
|
||
//
|
||
// Stream Encrypted. This is a sync call.
|
||
//
|
||
|
||
NtStatus = NtFsControlFile(
|
||
((PEXPORT_CONTEXT) Context)->StreamHandles[ StreamIndex ],
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_READ_RAW_ENCRYPTED,
|
||
FsctlInput,
|
||
FsctlInputLength,
|
||
FsctlOutput,
|
||
FsctlOutputLength
|
||
);
|
||
|
||
|
||
if ( !NT_SUCCESS( NtStatus ) && ( STATUS_END_OF_FILE != NtStatus) ){
|
||
|
||
//
|
||
// Error occured. Quit processing.
|
||
//
|
||
|
||
HResult = RtlNtStatusToDosError( NtStatus );
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// Calculate the length of data send to caller
|
||
//
|
||
SendDataLength = ((PENCRYPTED_DATA_INFO)FsctlOutput)->OutputBufferOffset;
|
||
for ( ii=0; ii < ((PENCRYPTED_DATA_INFO)FsctlOutput)->NumberOfDataBlocks; ii++){
|
||
|
||
SendDataLength += ((PENCRYPTED_DATA_INFO)FsctlOutput)->DataBlockSize[ii];
|
||
|
||
}
|
||
|
||
(( PEFSEXP_DATA_HEADER )BufPointer )->Length = SendDataLength +
|
||
sizeof ( EFSEXP_DATA_HEADER );
|
||
SendDataLength += (ULONG)(( char* ) FsctlOutput - ( char* )WorkBuffer);
|
||
|
||
//
|
||
// Check if this is the last stream block
|
||
//
|
||
|
||
BytesAdvanced = ((PENCRYPTED_DATA_INFO)FsctlOutput)->NumberOfDataBlocks <<
|
||
((PENCRYPTED_DATA_INFO)FsctlOutput)->DataUnitShift;
|
||
|
||
if ( ( STATUS_END_OF_FILE == NtStatus ) ||
|
||
(((PENCRYPTED_DATA_INFO)FsctlOutput)->BytesWithinFileSize < BytesAdvanced)
|
||
) {
|
||
|
||
//
|
||
// Last block in this stream
|
||
//
|
||
|
||
StreamOffset = 0;
|
||
StreamIndex++;
|
||
if ( StreamIndex >= ((PEXPORT_CONTEXT) Context)->NumberOfStreams ){
|
||
|
||
MoreToRead = FALSE;
|
||
HResult = NO_ERROR;
|
||
|
||
}
|
||
|
||
if ( STATUS_END_OF_FILE == NtStatus ){
|
||
|
||
//
|
||
// End of file. No need to send data to caller
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// More data block to be read for this stream.
|
||
//
|
||
|
||
StreamOffset = ((PENCRYPTED_DATA_INFO)FsctlOutput)->StartingFileOffset
|
||
+ BytesAdvanced;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Not encrypted stream. Use normal read.
|
||
//
|
||
|
||
NtStatus = NtReadFile(
|
||
((PEXPORT_CONTEXT) Context)->StreamHandles[ StreamIndex ],
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FsctlOutput,
|
||
FsctlOutputLength,
|
||
(PLARGE_INTEGER)&StreamOffset,
|
||
NULL
|
||
);
|
||
|
||
if ( !NT_SUCCESS( NtStatus ) && ( STATUS_END_OF_FILE != NtStatus) ){
|
||
|
||
//
|
||
// Error occured. Quit processing.
|
||
//
|
||
|
||
HResult = RtlNtStatusToDosError( NtStatus );
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// Calculate the length of data send to caller
|
||
//
|
||
|
||
SendDataLength = (ULONG)IoStatusBlock.Information;
|
||
(( PEFSEXP_DATA_HEADER )BufPointer )->Length = SendDataLength +
|
||
sizeof ( EFSEXP_DATA_HEADER );
|
||
|
||
SendDataLength += (ULONG)(( char* ) FsctlOutput - ( char* )WorkBuffer);
|
||
|
||
//
|
||
// Check if this is the last stream block
|
||
//
|
||
|
||
BytesAdvanced = (ULONG)IoStatusBlock.Information;
|
||
|
||
if ( ( STATUS_END_OF_FILE == NtStatus ) || (FsctlOutputLength > BytesAdvanced)) {
|
||
|
||
//
|
||
// Last block in this stream
|
||
//
|
||
|
||
StreamOffset = 0;
|
||
StreamIndex++;
|
||
if ( StreamIndex >= ((PEXPORT_CONTEXT) Context)->NumberOfStreams ){
|
||
|
||
MoreToRead = FALSE;
|
||
HResult = NO_ERROR;
|
||
|
||
}
|
||
|
||
if ( STATUS_END_OF_FILE == NtStatus ){
|
||
|
||
//
|
||
// End of file. No need to send data to caller
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// More data block to be read for this stream.
|
||
//
|
||
|
||
StreamOffset += BytesAdvanced;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
HResult = EFSSendPipeData( (char *)WorkBuffer, SendDataLength, EfsOutPipe );
|
||
|
||
}//while
|
||
|
||
} else {
|
||
|
||
//
|
||
// Read $EFS wrong
|
||
//
|
||
|
||
HResult = RtlNtStatusToDosError( NtStatus );
|
||
|
||
}
|
||
|
||
//
|
||
// End the sending data with length of 0 byte. (This flushes the pipe.)
|
||
//
|
||
|
||
EFSSendPipeData( (char *)WorkBuffer, 0, EfsOutPipe );
|
||
|
||
//
|
||
// Finished. Clean up the memory.
|
||
//
|
||
|
||
VirtualFree(
|
||
WorkBuffer,
|
||
0,
|
||
MEM_RELEASE
|
||
);
|
||
|
||
LsapFreeLsaHeap( FsctlInput );
|
||
|
||
return HResult;
|
||
|
||
}
|
||
|
||
ULONG
|
||
CheckSignature(
|
||
void *Signature
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the signature type.
|
||
|
||
Arguments:
|
||
|
||
Signature - Signature string.
|
||
|
||
Return Value:
|
||
|
||
The type of signature. 0 for bogus signature.
|
||
|
||
--*/
|
||
{
|
||
|
||
if ( !memcmp( Signature, FILE_SIGNATURE, SIG_LENGTH )){
|
||
|
||
return SIG_EFS_FILE;
|
||
|
||
}
|
||
|
||
if ( !memcmp( Signature, STREAM_SIGNATURE, SIG_LENGTH )){
|
||
|
||
return SIG_EFS_STREAM;
|
||
|
||
}
|
||
|
||
if ( !memcmp( Signature, DATA_SIGNATURE, SIG_LENGTH )){
|
||
|
||
return SIG_EFS_DATA;
|
||
|
||
}
|
||
|
||
return SIG_NO_MATCH;
|
||
}
|
||
|
||
long
|
||
EfsWriteFileRaw(
|
||
PVOID Context,
|
||
PVOID EfsInPipe
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to write encrypted file's raw data. It uses
|
||
NTFS FSCTL to put the data.
|
||
|
||
Arguments:
|
||
|
||
Context -- Context handle.
|
||
EfsInPipe -- Pipe handle.
|
||
|
||
Return Value:
|
||
|
||
The result of operation.
|
||
|
||
--*/
|
||
{
|
||
DWORD HResult = NO_ERROR;
|
||
ULONG GetDataLength;
|
||
ULONG NextToRead;
|
||
ULONG FsctlInputLength;
|
||
ULONG BytesInBuffer;
|
||
VOID *WorkBuffer = NULL;
|
||
VOID *ReadBuffer = NULL;
|
||
VOID *FsctlInput;
|
||
VOID *BufPointer;
|
||
|
||
NTSTATUS NtStatus;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
HANDLE CurrentStream = 0;
|
||
LONGLONG StreamOffset;
|
||
ULONG SigID;
|
||
UNICODE_STRING StreamName;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
PEFSEXP_STREAM_HEADER StreamHeader;
|
||
TOKEN_PRIVILEGES Privs;
|
||
PTOKEN_PRIVILEGES OldPrivs = NULL;
|
||
HANDLE TokenHandle = 0;
|
||
DWORD ReturnLength;
|
||
BOOL GotToken;
|
||
BOOLEAN PrivilegeEnabled = FALSE;
|
||
BOOLEAN MoreByteToWrite = TRUE;
|
||
BOOLEAN CrntStrIsDefault = FALSE;
|
||
BOOLEAN CrntStreamEncrypted = TRUE;
|
||
|
||
if ( !Context ||
|
||
!( ((PIMPORT_CONTEXT) Context)->Flag & CONTEXT_FOR_IMPORT ) ||
|
||
( ((PIMPORT_CONTEXT) Context)->Flag & CONTEXT_INVALID ) ||
|
||
(((PEXPORT_CONTEXT) Context)->ContextID != EFS_CONTEXT_ID)
|
||
){
|
||
|
||
return ERROR_ACCESS_DENIED;
|
||
|
||
}
|
||
|
||
//
|
||
// Allocate necessary memory
|
||
//
|
||
|
||
WorkBuffer = VirtualAlloc(
|
||
NULL,
|
||
FSCTL_OUTPUT_INITIAL_LENGTH,
|
||
MEM_COMMIT,
|
||
PAGE_READWRITE
|
||
);
|
||
|
||
if ( !WorkBuffer ){
|
||
return ERROR_OUTOFMEMORY;
|
||
}
|
||
|
||
//
|
||
// Read in the file headers first.
|
||
//
|
||
|
||
GetDataLength = sizeof ( EFSEXP_FILE_HEADER ) +
|
||
sizeof ( EFSEXP_STREAM_HEADER ) +
|
||
sizeof ( USHORT ) +
|
||
sizeof ( EFSEXP_DATA_HEADER ) +
|
||
sizeof ( ULONG );
|
||
|
||
HResult = EFSReceivePipeData( (char *)WorkBuffer, &GetDataLength, EfsInPipe );
|
||
|
||
if ( NO_ERROR != HResult ){
|
||
|
||
VirtualFree(
|
||
WorkBuffer,
|
||
0,
|
||
MEM_RELEASE
|
||
);
|
||
return HResult;
|
||
|
||
}
|
||
|
||
//
|
||
// Verify file format
|
||
//
|
||
|
||
if ( SIG_EFS_FILE != CheckSignature(
|
||
(char *)WorkBuffer +
|
||
sizeof( ULONG)
|
||
) ||
|
||
SIG_EFS_STREAM != CheckSignature(
|
||
(char *)WorkBuffer +
|
||
sizeof( EFSEXP_FILE_HEADER ) +
|
||
sizeof( ULONG)
|
||
) ||
|
||
SIG_EFS_DATA != CheckSignature(
|
||
(char *)WorkBuffer +
|
||
sizeof( EFSEXP_FILE_HEADER ) +
|
||
sizeof ( EFSEXP_STREAM_HEADER ) +
|
||
sizeof ( USHORT ) +
|
||
sizeof( ULONG)
|
||
) ||
|
||
EFS_STREAM_ID != *((USHORT *)(
|
||
(char *)WorkBuffer +
|
||
sizeof( EFSEXP_FILE_HEADER ) +
|
||
sizeof ( EFSEXP_STREAM_HEADER )
|
||
)) ||
|
||
EFS_EXP_FORMAT_CURRENT_VERSION != ((PEFSEXP_FILE_HEADER)WorkBuffer)->VersionID ){
|
||
|
||
//
|
||
// Signature does not match. This includes file which has less bytes than
|
||
// expected head information.
|
||
//
|
||
|
||
VirtualFree(
|
||
WorkBuffer,
|
||
0,
|
||
MEM_RELEASE
|
||
);
|
||
|
||
return ERROR_BAD_FORMAT;
|
||
|
||
}
|
||
|
||
//
|
||
// Read in $EFS
|
||
//
|
||
|
||
|
||
RtlCopyMemory( WorkBuffer, (char *)WorkBuffer + GetDataLength - sizeof(ULONG), sizeof( ULONG ) );
|
||
BytesInBuffer = sizeof(ULONG);
|
||
BufPointer = (char *)WorkBuffer + BytesInBuffer;
|
||
|
||
//
|
||
// The read will include the length of the next block.
|
||
//
|
||
|
||
NextToRead = GetDataLength = *((PULONG)WorkBuffer) ;
|
||
FsctlInputLength = FSCTL_OUTPUT_INITIAL_LENGTH - BytesInBuffer;
|
||
|
||
if ((NextToRead + NextToRead + FSCTL_OUTPUT_MISC_LENGTH) > (FSCTL_OUTPUT_INITIAL_LENGTH - BytesInBuffer)) {
|
||
|
||
//
|
||
// We need a large buffer to hold 2 $EFS plus some head info
|
||
//
|
||
|
||
VOID *TmpBuffer;
|
||
ULONG NewBufferLength;
|
||
|
||
NewBufferLength = ((NextToRead + NextToRead + FSCTL_OUTPUT_MISC_LENGTH + BytesInBuffer
|
||
+ FSCTL_OUTPUT_MISC_LENGTH - 1) / FSCTL_OUTPUT_MISC_LENGTH) * FSCTL_OUTPUT_MISC_LENGTH;
|
||
|
||
TmpBuffer = VirtualAlloc(
|
||
NULL,
|
||
NewBufferLength,
|
||
MEM_COMMIT,
|
||
PAGE_READWRITE
|
||
);
|
||
if (TmpBuffer) {
|
||
|
||
RtlCopyMemory( TmpBuffer, WorkBuffer, BytesInBuffer);
|
||
|
||
VirtualFree(
|
||
WorkBuffer,
|
||
0,
|
||
MEM_RELEASE
|
||
);
|
||
WorkBuffer = TmpBuffer;
|
||
BufPointer = (char *)WorkBuffer + BytesInBuffer;
|
||
FsctlInputLength = NewBufferLength - BytesInBuffer;
|
||
|
||
} else {
|
||
|
||
VirtualFree(
|
||
WorkBuffer,
|
||
0,
|
||
MEM_RELEASE
|
||
);
|
||
return ERROR_OUTOFMEMORY;
|
||
}
|
||
|
||
}
|
||
|
||
HResult = EFSReceivePipeData( (char *)BufPointer, &NextToRead, EfsInPipe );
|
||
if ( NO_ERROR != HResult ){
|
||
|
||
VirtualFree(
|
||
WorkBuffer,
|
||
0,
|
||
MEM_RELEASE
|
||
);
|
||
return HResult;
|
||
|
||
}
|
||
|
||
if ( GetDataLength > NextToRead){
|
||
|
||
//
|
||
// No data stream followed $EFS. This is either a 0 length file
|
||
// Or a directory file.
|
||
//
|
||
|
||
MoreByteToWrite = FALSE;
|
||
NextToRead = 0;
|
||
|
||
} else {
|
||
|
||
NextToRead = *(ULONG UNALIGNED *)(( char *) BufPointer + GetDataLength - sizeof (ULONG));
|
||
|
||
}
|
||
|
||
//
|
||
// The $EFS is in. Write it out!
|
||
// First prepare the FsctlInput
|
||
//
|
||
|
||
FsctlInput = (char*) BufPointer + GetDataLength;
|
||
FsctlInputLength -= GetDataLength;
|
||
|
||
//
|
||
// Send FsctlInputData to the server
|
||
//
|
||
|
||
HResult = GetOverWriteEfsAttrFsctlInput(
|
||
(( PIMPORT_CONTEXT ) Context)->Flag,
|
||
(( PIMPORT_CONTEXT ) Context)->DesiredAccess,
|
||
( char * )BufPointer - sizeof (ULONG),
|
||
GetDataLength,
|
||
(char *)FsctlInput,
|
||
&FsctlInputLength
|
||
);
|
||
|
||
if ( NO_ERROR != HResult ){
|
||
|
||
VirtualFree(
|
||
WorkBuffer,
|
||
0,
|
||
MEM_RELEASE
|
||
);
|
||
return HResult;
|
||
|
||
}
|
||
|
||
NtStatus = NtFsControlFile(
|
||
((PIMPORT_CONTEXT) Context)->Handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_SET_ENCRYPTION ,
|
||
FsctlInput,
|
||
FsctlInputLength,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if ( NT_SUCCESS( NtStatus )){
|
||
|
||
DWORD ShareMode = 0;
|
||
|
||
//
|
||
// $EFS is written now
|
||
//
|
||
|
||
StreamOffset = 0;
|
||
|
||
//
|
||
// ********* Trick Trick Trick *********
|
||
// NTFS will have better performance if align the data block.
|
||
// We already have the length field of the block which is a ULONG
|
||
// field. Here we start our offset at sizeof (ULONG).
|
||
// Not only performance, now is required by NTFS. Otherwise it will
|
||
// fail.
|
||
//
|
||
|
||
BufPointer = (char *)WorkBuffer + sizeof (ULONG);
|
||
|
||
while ( MoreByteToWrite ){
|
||
|
||
GetDataLength = NextToRead;
|
||
|
||
//
|
||
// The read will include the length of the next block.
|
||
// No backward reading here.
|
||
//
|
||
|
||
HResult = EFSReceivePipeData( (char *)BufPointer, &GetDataLength, EfsInPipe );
|
||
|
||
if ( NO_ERROR != HResult ){
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
if ( GetDataLength < NextToRead ){
|
||
|
||
//
|
||
// End of file reached
|
||
//
|
||
|
||
MoreByteToWrite = FALSE;
|
||
NextToRead = 0;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Prepare for next read block. Be careful about the alignment here.
|
||
//
|
||
|
||
RtlCopyMemory((char *) &NextToRead,
|
||
(char *) BufPointer + GetDataLength - sizeof (ULONG),
|
||
sizeof (ULONG)
|
||
);
|
||
|
||
}
|
||
|
||
SigID = CheckSignature( BufPointer );
|
||
if ( SIG_EFS_STREAM == SigID ){
|
||
|
||
//
|
||
// This is a stream block. Create a new stream.
|
||
//
|
||
|
||
StreamHeader = (PEFSEXP_STREAM_HEADER)((char *)BufPointer - sizeof( ULONG ));
|
||
if ( StreamHeader->Flag & STREAM_NOT_ENCRYPTED ){
|
||
|
||
CrntStreamEncrypted = FALSE;
|
||
|
||
} else {
|
||
|
||
CrntStreamEncrypted = TRUE;
|
||
}
|
||
|
||
StreamName.Length = (USHORT) StreamHeader->NameLength;
|
||
|
||
StreamName.Buffer = ( USHORT* )((char *)BufPointer +
|
||
sizeof ( EFSEXP_STREAM_HEADER ) -
|
||
sizeof ( ULONG ));
|
||
|
||
if ( CurrentStream && !CrntStrIsDefault){
|
||
|
||
//
|
||
// Close the previous stream
|
||
//
|
||
|
||
NtClose(CurrentStream);
|
||
CurrentStream = 0;
|
||
|
||
}
|
||
|
||
if ( (DEF_STR_LEN == StreamName.Length) &&
|
||
!memcmp( StreamName.Buffer,
|
||
DEFAULT_STREAM,
|
||
StreamName.Length
|
||
)
|
||
){
|
||
|
||
//
|
||
// Default data stream to be processed.
|
||
// This is the most case. We need to optimize this!!!
|
||
//
|
||
|
||
CurrentStream = ((PIMPORT_CONTEXT) Context)->Handle;
|
||
CrntStrIsDefault = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Other data streams
|
||
//
|
||
|
||
if ( (((( PIMPORT_CONTEXT ) Context)->CreateOptions) & FILE_OPEN_FOR_BACKUP_INTENT) &&
|
||
( !PrivilegeEnabled) ){
|
||
|
||
//
|
||
// Enable the Privilege. We only enable once. If fail, we return.
|
||
//
|
||
|
||
PrivilegeEnabled = TRUE;
|
||
|
||
OldPrivs = ( TOKEN_PRIVILEGES *) LsapAllocateLsaHeap(sizeof( TOKEN_PRIVILEGES ));
|
||
|
||
if ( OldPrivs == NULL ){
|
||
|
||
HResult = ERROR_NOT_ENOUGH_MEMORY;
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// We're impersonating, use the thread token.
|
||
//
|
||
|
||
GotToken = OpenThreadToken(
|
||
GetCurrentThread(),
|
||
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
||
FALSE,
|
||
&TokenHandle
|
||
);
|
||
|
||
if ( GotToken ) {
|
||
|
||
//
|
||
// We've got a token handle
|
||
//
|
||
|
||
Privs.PrivilegeCount = 1;
|
||
Privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||
Privs.Privileges[0].Luid = RtlConvertLongToLuid(SE_RESTORE_PRIVILEGE);
|
||
|
||
ReturnLength = sizeof( TOKEN_PRIVILEGES );
|
||
|
||
(VOID) AdjustTokenPrivileges (
|
||
TokenHandle,
|
||
FALSE,
|
||
&Privs,
|
||
sizeof( TOKEN_PRIVILEGES ),
|
||
OldPrivs,
|
||
&ReturnLength
|
||
);
|
||
|
||
HResult = GetLastError();
|
||
|
||
//
|
||
// Caller will call RpcRevertToSelf().
|
||
// OldPrivs is not needed any more.
|
||
//
|
||
|
||
LsapFreeLsaHeap( OldPrivs );
|
||
OldPrivs = NULL;
|
||
|
||
if ( ERROR_SUCCESS != HResult ) {
|
||
|
||
//
|
||
// Privilege adjust failed
|
||
//
|
||
|
||
CloseHandle( TokenHandle );
|
||
TokenHandle = 0;
|
||
break;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We did not get the handle.
|
||
//
|
||
|
||
TokenHandle = 0;
|
||
HResult = GetLastError();
|
||
LsapFreeLsaHeap( OldPrivs );
|
||
OldPrivs = NULL;
|
||
break;
|
||
|
||
}
|
||
|
||
if (((PIMPORT_CONTEXT) Context)->DesiredAccess & DELETE) {
|
||
ShareMode = FILE_SHARE_DELETE;
|
||
}
|
||
}
|
||
|
||
StreamName.MaximumLength = StreamName.Length;
|
||
|
||
CrntStrIsDefault = FALSE;
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&StreamName,
|
||
0,
|
||
((PIMPORT_CONTEXT) Context)->Handle,
|
||
NULL
|
||
);
|
||
|
||
NtStatus = NtCreateFile(
|
||
&CurrentStream,
|
||
((PIMPORT_CONTEXT) Context)->DesiredAccess,
|
||
&ObjectAttributes,
|
||
&IoStatusBlock,
|
||
(PLARGE_INTEGER) NULL,
|
||
((PIMPORT_CONTEXT) Context)->Attribute,
|
||
ShareMode,
|
||
((PIMPORT_CONTEXT) Context)->CreateDisposition,
|
||
((PIMPORT_CONTEXT) Context)->CreateOptions,
|
||
(PVOID) NULL,
|
||
0L
|
||
);
|
||
|
||
if ( !NT_SUCCESS( NtStatus ) ){
|
||
|
||
HResult = RtlNtStatusToDosError( NtStatus );
|
||
break;
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// Stream header processed. Adjust BufPointer to make it consistant with ReadRaw
|
||
//
|
||
|
||
BufPointer = (char *)WorkBuffer + sizeof (ULONG);
|
||
continue;
|
||
|
||
}
|
||
|
||
if ( SIG_EFS_DATA != SigID ){
|
||
|
||
//
|
||
// Corrupted file
|
||
//
|
||
HResult = ERROR_FILE_CORRUPT;
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// Processing the data block
|
||
// After all the above is done, this should be a piece of cake!
|
||
//
|
||
|
||
FsctlInput = (char *)BufPointer + sizeof (EFSEXP_DATA_HEADER) - sizeof (ULONG);
|
||
FsctlInputLength = GetDataLength - sizeof (EFSEXP_DATA_HEADER);
|
||
if ( !MoreByteToWrite ){
|
||
|
||
//
|
||
// Adjust for the last block. There is no extra length
|
||
// field for the next block.
|
||
//
|
||
|
||
FsctlInputLength += sizeof (ULONG);
|
||
}
|
||
|
||
if ( CrntStreamEncrypted ){
|
||
|
||
//
|
||
// Most of the case.
|
||
//
|
||
|
||
//
|
||
// finally writing data out
|
||
//
|
||
|
||
NtStatus = NtFsControlFile(
|
||
CurrentStream,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_WRITE_RAW_ENCRYPTED,
|
||
FsctlInput,
|
||
FsctlInputLength,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Currently only support plain data stream on directory
|
||
//
|
||
|
||
NtStatus = NtWriteFile(
|
||
CurrentStream,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FsctlInput,
|
||
FsctlInputLength,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
}
|
||
|
||
if ( !NT_SUCCESS( NtStatus ) ){
|
||
|
||
HResult = RtlNtStatusToDosError( NtStatus );
|
||
break;
|
||
|
||
}
|
||
|
||
BufPointer = (char *)WorkBuffer + sizeof (ULONG);
|
||
HResult = NO_ERROR;
|
||
|
||
} //while loop
|
||
|
||
} else {
|
||
|
||
//
|
||
// Writing $EFS error
|
||
//
|
||
|
||
HResult = RtlNtStatusToDosError( NtStatus );
|
||
|
||
}
|
||
|
||
if ( CurrentStream && !CrntStrIsDefault ){
|
||
|
||
NtClose(CurrentStream);
|
||
|
||
}
|
||
|
||
if ( TokenHandle ){
|
||
CloseHandle( TokenHandle );
|
||
}
|
||
|
||
VirtualFree(
|
||
WorkBuffer,
|
||
0,
|
||
MEM_RELEASE
|
||
);
|
||
return HResult;
|
||
|
||
}
|
||
|
||
DWORD
|
||
GetOverWriteEfsAttrFsctlInput(
|
||
ULONG Flag,
|
||
ULONG AccessFlag,
|
||
char *InputData,
|
||
ULONG InputDataLength,
|
||
char *OutputData,
|
||
ULONG *OutputDataLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to prepare the FSCTL input data buffer for
|
||
EFS_OVERWRITE_ATTRIBUTE used in import.
|
||
|
||
Arguments:
|
||
|
||
Flag -- Indicate the type of the target.
|
||
|
||
AccessFlag -- Indicate the kind of access required.
|
||
|
||
InputData -- Required input data ($EFS)
|
||
|
||
InputDataLength -- The length of the input data.
|
||
|
||
OutputData -- The prepared data as the result of this routine.
|
||
|
||
OutputDataLength -- The length of the output data.
|
||
|
||
Return Value:
|
||
|
||
The result of operation.
|
||
|
||
--*/
|
||
{
|
||
DWORD HResult = NO_ERROR;
|
||
PEFS_KEY Fek = NULL;
|
||
PEFS_DATA_STREAM_HEADER NewEfs = NULL;
|
||
ULONG EfsDataLength = 0 ;
|
||
ULONG OutBufLen = *OutputDataLength;
|
||
BOOLEAN WithFek = FALSE;
|
||
PBYTE SourceEfs;
|
||
ULONG CipherSubCode;
|
||
HANDLE hToken;
|
||
HANDLE hProfile;
|
||
EFS_USER_INFO EfsUserInfo;
|
||
|
||
|
||
if ( !(Flag & CONTEXT_OPEN_FOR_DIR) ){
|
||
|
||
//
|
||
// Not for directory file. Check if we can get
|
||
// FEK or not.
|
||
//
|
||
|
||
if (EfspGetUserInfo( &EfsUserInfo )) {
|
||
|
||
if (EfspLoadUserProfile( &EfsUserInfo, &hToken, &hProfile )) {
|
||
|
||
HResult = DecryptFek(
|
||
&EfsUserInfo,
|
||
( PEFS_DATA_STREAM_HEADER ) InputData,
|
||
&Fek,
|
||
&NewEfs,
|
||
0
|
||
);
|
||
|
||
EfspUnloadUserProfile( hToken, hProfile );
|
||
|
||
} else {
|
||
|
||
HResult = GetLastError();
|
||
|
||
}
|
||
|
||
EfspFreeUserInfo( &EfsUserInfo );
|
||
|
||
} else {
|
||
|
||
HResult = GetLastError();
|
||
|
||
}
|
||
|
||
if ( NO_ERROR == HResult ){
|
||
|
||
WithFek = TRUE;
|
||
|
||
} else {
|
||
|
||
if ( AccessFlag & FILE_WRITE_DATA ){
|
||
|
||
//
|
||
// A general user without the key.
|
||
//
|
||
|
||
|
||
|
||
return ERROR_ACCESS_DENIED;
|
||
|
||
} else {
|
||
|
||
HResult = NO_ERROR;
|
||
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
if ( WithFek ){
|
||
|
||
//
|
||
// Calculate the length of output buffer
|
||
// and the offset to put the $EFS
|
||
//
|
||
|
||
*OutputDataLength = 3 * sizeof(ULONG) +
|
||
2 * EFS_KEY_SIZE( Fek );
|
||
|
||
if (NewEfs){
|
||
SourceEfs = (PBYTE) NewEfs;
|
||
} else {
|
||
SourceEfs = (PBYTE) InputData;
|
||
}
|
||
*OutputDataLength += *(PULONG)SourceEfs;
|
||
|
||
if (OutBufLen >= *OutputDataLength) {
|
||
|
||
EfsDataLength = *OutputDataLength - 3 * sizeof(ULONG);
|
||
|
||
( VOID ) SendEfs(
|
||
Fek,
|
||
(PEFS_DATA_STREAM_HEADER) SourceEfs,
|
||
(PBYTE) OutputData + 3 * sizeof(ULONG),
|
||
&EfsDataLength
|
||
);
|
||
} else {
|
||
HResult = ERROR_INSUFFICIENT_BUFFER;
|
||
}
|
||
|
||
//
|
||
// Free the memory we have allocated.
|
||
//
|
||
|
||
if ( Fek ){
|
||
|
||
LsapFreeLsaHeap( Fek );
|
||
|
||
}
|
||
if ( NewEfs ){
|
||
|
||
LsapFreeLsaHeap( NewEfs );
|
||
|
||
}
|
||
|
||
CipherSubCode = WRITE_EFS_ATTRIBUTE | SET_EFS_KEYBLOB;
|
||
|
||
} else {
|
||
|
||
//
|
||
// No FEK required.
|
||
//
|
||
|
||
*OutputDataLength = COMMON_FSCTL_HEADER_SIZE +
|
||
*(PULONG)InputData;
|
||
|
||
|
||
if (OutBufLen >= *OutputDataLength) {
|
||
EfsDataLength = *OutputDataLength - 3 * sizeof(ULONG);
|
||
|
||
( VOID ) SendHandleAndEfs(
|
||
(HANDLE) ULongToPtr(Flag),
|
||
(PEFS_DATA_STREAM_HEADER) InputData,
|
||
(PBYTE) OutputData + 3 * sizeof(ULONG),
|
||
&EfsDataLength
|
||
);
|
||
} else {
|
||
HResult = ERROR_INSUFFICIENT_BUFFER;
|
||
}
|
||
|
||
CipherSubCode = WRITE_EFS_ATTRIBUTE;
|
||
|
||
}
|
||
|
||
if ( NO_ERROR == HResult ) {
|
||
|
||
( VOID ) EncryptFSCTLData(
|
||
EFS_OVERWRITE_ATTRIBUTE,
|
||
EFS_ENCRYPT_STREAM,
|
||
CipherSubCode,
|
||
(PBYTE) OutputData + 3 * sizeof(ULONG),
|
||
EfsDataLength,
|
||
(PBYTE) OutputData,
|
||
OutputDataLength
|
||
);
|
||
|
||
}
|
||
|
||
return HResult;
|
||
|
||
}
|
||
|
||
DWORD
|
||
CheckVolumeSpace(
|
||
PFILE_FS_SIZE_INFORMATION VolInfo,
|
||
PEFS_STREAM_SIZE StreamSizes,
|
||
PHANDLE StreamHandles,
|
||
ULONG StreamCount
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine estimates if the volume has enough disk space to do the encryption or
|
||
decryption operation. The estimates is not accurate. System overheads are not included.
|
||
Real available space could be more or less when the operation begins. If we are not sure,
|
||
the operation will continue until we really run out of space.
|
||
|
||
Arguments:
|
||
|
||
VolInfo -- Information to calculate how much space left.
|
||
|
||
StreamSizes -- Information to calculate how much space needed.
|
||
|
||
StreamCount -- Number of data streams.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS returned if there might be enough space.
|
||
|
||
--*/
|
||
{
|
||
LARGE_INTEGER SpaceLeft;
|
||
LARGE_INTEGER SpaceNeeded;
|
||
ULONG ClusterSize;
|
||
ULONG ii;
|
||
|
||
SpaceLeft = VolInfo->AvailableAllocationUnits;
|
||
ClusterSize = VolInfo->SectorsPerAllocationUnit * VolInfo->BytesPerSector;
|
||
SpaceLeft.QuadPart *= ClusterSize;
|
||
for ( ii = 0, SpaceNeeded.QuadPart = 0; ii < StreamCount; ii++)
|
||
{
|
||
if ( StreamSizes[ii].StreamFlag & ( FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_SPARSE_FILE ) ){
|
||
|
||
FILE_STANDARD_INFORMATION StreamStdInfo;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// Get File Attributes
|
||
//
|
||
Status = NtQueryInformationFile(
|
||
StreamHandles[ii],
|
||
&IoStatusBlock,
|
||
&StreamStdInfo,
|
||
sizeof ( FILE_STANDARD_INFORMATION ),
|
||
FileStandardInformation
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)){
|
||
|
||
//
|
||
// We got error. We are not sure if we have enough space. Give it a try.
|
||
//
|
||
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
if ( StreamSizes[ii].StreamFlag & FILE_ATTRIBUTE_SPARSE_FILE ){
|
||
|
||
//
|
||
// A sparse file (may be compressed). The more accurate way is to query
|
||
// the ranges. Even with that is still a rough estimate. For better performance,
|
||
// we use the STD info.
|
||
//
|
||
|
||
SpaceNeeded.QuadPart += StreamStdInfo.AllocationSize.QuadPart;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Compressed file. Using Virtual Allocation Size + Total Allocation Size
|
||
//
|
||
|
||
SpaceNeeded.QuadPart += StreamSizes[ii].AllocSize.QuadPart + StreamStdInfo.AllocationSize.QuadPart;
|
||
|
||
}
|
||
|
||
} else {
|
||
SpaceNeeded.QuadPart += StreamSizes[ii].AllocSize.QuadPart;
|
||
}
|
||
|
||
if ( SpaceNeeded.QuadPart >= SpaceLeft.QuadPart ){
|
||
return ERROR_DISK_FULL;
|
||
}
|
||
}
|
||
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
DWORD
|
||
CompressStreams(
|
||
PEFS_STREAM_SIZE StreamSizes,
|
||
PHANDLE StreamHandles,
|
||
ULONG State,
|
||
ULONG StreamCount
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine compresses or decompresses the passed in streams.
|
||
|
||
Arguments:
|
||
|
||
StreamSizes -- Holding streams' original state info.
|
||
|
||
StreamHandles -- Stream handles.
|
||
|
||
State -- New compressed state.
|
||
|
||
StreamCount -- Number of data streams.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS returned if there might be enough space.
|
||
|
||
--*/
|
||
{
|
||
DWORD rc = ERROR_SUCCESS;
|
||
ULONG Length;
|
||
ULONG ii;
|
||
BOOL b = TRUE;
|
||
|
||
for (ii = 0; ii < StreamCount; ii++){
|
||
|
||
b = DeviceIoControl(
|
||
StreamHandles[ii],
|
||
FSCTL_SET_COMPRESSION,
|
||
&State,
|
||
sizeof(USHORT),
|
||
NULL,
|
||
0,
|
||
&Length,
|
||
FALSE
|
||
);
|
||
|
||
if ( !b ){
|
||
rc = GetLastError();
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
if ( !b ){
|
||
|
||
//
|
||
// Error happened. Try to restore the stream state.
|
||
//
|
||
|
||
for ( ULONG jj = 0; jj < ii; jj++){
|
||
|
||
if ( StreamSizes[ jj ].StreamFlag & FILE_ATTRIBUTE_COMPRESSED ){
|
||
|
||
State = COMPRESSION_FORMAT_DEFAULT;
|
||
|
||
} else {
|
||
|
||
State = COMPRESSION_FORMAT_NONE;
|
||
|
||
}
|
||
|
||
//
|
||
// Error is not checked. We can only recover the state as much
|
||
// as we can.
|
||
//
|
||
|
||
(VOID) DeviceIoControl(
|
||
StreamHandles[jj],
|
||
FSCTL_SET_COMPRESSION,
|
||
&State,
|
||
sizeof(USHORT),
|
||
NULL,
|
||
0,
|
||
&Length,
|
||
FALSE
|
||
);
|
||
}
|
||
|
||
}
|
||
|
||
return rc;
|
||
}
|
||
|
||
DWORD
|
||
CheckOpenSection(
|
||
PEFS_STREAM_SIZE StreamSizes,
|
||
PHANDLE StreamHandles,
|
||
ULONG StreamCount
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine set EOF of a stream to 0 and then back to its original size.
|
||
All the stream content is lost. Valid length is set to 0. This process will also
|
||
speed up a compressed file encryption.
|
||
|
||
Arguments:
|
||
|
||
StreamSizes -- Holding streams' original state info.
|
||
|
||
StreamHandles -- Stream handles.
|
||
|
||
StreamCount -- Number of data streams.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS returned if succeed.
|
||
|
||
--*/
|
||
{
|
||
ULONG ii;
|
||
FILE_END_OF_FILE_INFORMATION FileSize;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
for (ii = 0; ii < StreamCount; ii++){
|
||
FileSize.EndOfFile.QuadPart = 0;
|
||
Status = NtSetInformationFile(
|
||
StreamHandles[ii],
|
||
&IoStatusBlock,
|
||
&FileSize,
|
||
sizeof(FileSize),
|
||
FileEndOfFileInformation
|
||
);
|
||
if ( !NT_SUCCESS(Status) ){
|
||
|
||
//
|
||
// A section handle may be open on the stream.
|
||
//
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( NT_SUCCESS(Status) ){
|
||
for (ii = 0; ii < StreamCount; ii++){
|
||
FileSize.EndOfFile = StreamSizes[ii].EOFSize;
|
||
Status = NtSetInformationFile(
|
||
StreamHandles[ii],
|
||
&IoStatusBlock,
|
||
&FileSize,
|
||
sizeof(FileSize),
|
||
FileEndOfFileInformation
|
||
);
|
||
if ( !NT_SUCCESS(Status) ){
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return RtlNtStatusToDosError( Status );
|
||
}
|
||
|
||
DWORD
|
||
CopyStreamSection(
|
||
HANDLE Target,
|
||
HANDLE SrcMapping,
|
||
PLARGE_INTEGER Offset,
|
||
PLARGE_INTEGER DataLength,
|
||
PLARGE_INTEGER AllocationGranularity
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine copies a section of data from source stream
|
||
to target stream.
|
||
|
||
Arguments:
|
||
|
||
Target -- Destination stream handle.
|
||
|
||
SrcMapping -- Source stream mapping handle.
|
||
|
||
Offset -- Data offdset in the stream.
|
||
|
||
DataLength -- Byte count to be copied.
|
||
|
||
AllocationGranularity -- Allocation granularity.
|
||
|
||
Return Value:
|
||
|
||
Results of the operation.
|
||
|
||
--*/
|
||
{
|
||
LARGE_INTEGER RemainingData = *DataLength;
|
||
LARGE_INTEGER StreamOffset = *Offset;
|
||
ULONG BytesToCopy;
|
||
PVOID pbFile;
|
||
DWORD BytesWritten;
|
||
BOOL b;
|
||
DWORD rc = NO_ERROR;
|
||
|
||
while ( RemainingData.QuadPart > 0 ) {
|
||
|
||
//
|
||
// Determine number of bytes to be mapped
|
||
//
|
||
|
||
if ( RemainingData.QuadPart < AllocationGranularity->QuadPart ) {
|
||
BytesToCopy = RemainingData.LowPart;
|
||
} else {
|
||
BytesToCopy = AllocationGranularity->LowPart;
|
||
}
|
||
|
||
pbFile = MapViewOfFile(
|
||
SrcMapping,
|
||
FILE_MAP_READ,
|
||
StreamOffset.HighPart,
|
||
StreamOffset.LowPart,
|
||
BytesToCopy
|
||
);
|
||
|
||
if (pbFile != NULL) {
|
||
|
||
//
|
||
// Write the data to the target stream
|
||
//
|
||
|
||
b = WriteFile(
|
||
Target,
|
||
pbFile,
|
||
BytesToCopy,
|
||
&BytesWritten,
|
||
NULL
|
||
);
|
||
|
||
UnmapViewOfFile( pbFile );
|
||
|
||
LARGE_INTEGER BytesCopied;
|
||
|
||
BytesCopied.HighPart = 0;
|
||
BytesCopied.LowPart = BytesToCopy;
|
||
|
||
RemainingData.QuadPart -= BytesCopied.QuadPart;
|
||
StreamOffset.QuadPart += BytesCopied.QuadPart;
|
||
|
||
if (!b) {
|
||
rc = GetLastError();
|
||
DebugLog((DEB_ERROR, "WriteFile failed, error = %d\n", rc ));
|
||
break;
|
||
}
|
||
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
DebugLog((DEB_ERROR, "MapViewOfFile failed, error = %d\n" ,rc));
|
||
break;
|
||
}
|
||
}
|
||
|
||
return rc;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
GetFileEfsStream(
|
||
IN HANDLE hFile,
|
||
OUT PEFS_DATA_STREAM_HEADER * pEfsStream
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get the $EFS from the passed file or directory
|
||
|
||
Arguments:
|
||
|
||
hFile - An open handle the the file or directory of interest.
|
||
|
||
pEfsStream - Returns a pointer to a block of memory containing the EFS stream
|
||
for the passed file. Free with LsapFreeLsaHeap();
|
||
|
||
Return Value:
|
||
|
||
Status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG Index;
|
||
ULONG cbOutputData;
|
||
ULONG EfsDataLength;
|
||
NTSTATUS Status;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
ULONG TmpOutputData;
|
||
|
||
*pEfsStream = NULL;
|
||
|
||
//
|
||
// Now we got a handle to the parent directory in ParentDir.
|
||
// Allocate input and output data buffer
|
||
//
|
||
|
||
cbOutputData = sizeof( ULONG );
|
||
|
||
//
|
||
// PSC, [EFS_FC, CSC , SK, H, H, [SK, H, H]sk]sk
|
||
// PSC, CSC are ignored in this FSCTL call
|
||
//
|
||
|
||
const ULONG InputDataSize = (2 * sizeof(DriverSessionKey)) + (7 * sizeof(ULONG));
|
||
BYTE InputData[InputDataSize];
|
||
|
||
ULONG cbInputData = InputDataSize;
|
||
|
||
//
|
||
// Prepare an input data for making a FSCTL call to get the $EFS
|
||
//
|
||
|
||
EfsDataLength = 2 * sizeof(DriverSessionKey) + 4 * sizeof(ULONG);
|
||
SendHandle( hFile, InputData + 3*sizeof(ULONG), &EfsDataLength );
|
||
|
||
(VOID) EncryptFSCTLData(
|
||
EFS_GET_ATTRIBUTE,
|
||
0,
|
||
0,
|
||
InputData + (3 * sizeof(ULONG)),
|
||
EfsDataLength,
|
||
InputData,
|
||
&cbInputData
|
||
);
|
||
|
||
//
|
||
// First call to get the size
|
||
//
|
||
|
||
Status = NtFsControlFile(
|
||
hFile,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_ENCRYPTION_FSCTL_IO,
|
||
InputData,
|
||
cbInputData,
|
||
&TmpOutputData,
|
||
cbOutputData
|
||
);
|
||
|
||
ASSERT(!NT_SUCCESS( Status ));
|
||
|
||
if (Status == STATUS_BUFFER_TOO_SMALL) {
|
||
|
||
//
|
||
// Check if the output data buffer too small
|
||
// Try again if it is.
|
||
//
|
||
|
||
cbOutputData = TmpOutputData;
|
||
*pEfsStream = (PEFS_DATA_STREAM_HEADER)LsapAllocateLsaHeap( cbOutputData );
|
||
|
||
if ( *pEfsStream ) {
|
||
|
||
Status = NtFsControlFile(
|
||
hFile,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_ENCRYPTION_FSCTL_IO,
|
||
InputData,
|
||
cbInputData,
|
||
*pEfsStream,
|
||
cbOutputData
|
||
);
|
||
} else {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if ( !NT_SUCCESS( Status ) ){
|
||
|
||
if ( *pEfsStream ){
|
||
LsapFreeLsaHeap( *pEfsStream );
|
||
*pEfsStream = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
ULONG
|
||
StringInfoCmp(
|
||
IN PFILE_STREAM_INFORMATION StreamInfoBaseSrc,
|
||
IN PFILE_STREAM_INFORMATION StreamInfoBaseDst,
|
||
IN ULONG StreamInfoSize
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine compares to blocks FILE_STREAM_INFORMATION. The StreamAllocationSize
|
||
cound differ and they should still be thought as eaqual.
|
||
|
||
Arguments:
|
||
|
||
StreamInfoBaseSrc - Source block
|
||
|
||
StreamInfoBaseDst - Dstination block
|
||
|
||
StreamInfoSize - Block size in bytes
|
||
|
||
Return Value:
|
||
|
||
0 if compares same.
|
||
|
||
--*/
|
||
{
|
||
ULONG rc;
|
||
|
||
rc = memcmp(StreamInfoBaseSrc, StreamInfoBaseDst, StreamInfoSize);
|
||
if (rc) {
|
||
do {
|
||
|
||
rc = memcmp(StreamInfoBaseSrc->StreamName,
|
||
StreamInfoBaseDst->StreamName,
|
||
StreamInfoBaseSrc->StreamNameLength
|
||
);
|
||
|
||
if (StreamInfoBaseSrc->NextEntryOffset){
|
||
StreamInfoBaseSrc = (PFILE_STREAM_INFORMATION)((PCHAR) StreamInfoBaseSrc + StreamInfoBaseSrc->NextEntryOffset);
|
||
} else {
|
||
StreamInfoBaseSrc = NULL;
|
||
}
|
||
|
||
if (StreamInfoBaseDst->NextEntryOffset){
|
||
StreamInfoBaseDst = (PFILE_STREAM_INFORMATION)((PCHAR) StreamInfoBaseDst + StreamInfoBaseDst->NextEntryOffset);
|
||
} else {
|
||
StreamInfoBaseDst = NULL;
|
||
}
|
||
|
||
if (((StreamInfoBaseSrc == NULL) || (StreamInfoBaseDst == NULL)) && (StreamInfoBaseSrc != StreamInfoBaseDst)) {
|
||
rc = 1;
|
||
}
|
||
|
||
} while ( (rc == 0) && StreamInfoBaseSrc );
|
||
}
|
||
|
||
return rc;
|
||
}
|
||
|
||
//
|
||
// Beta 2 API
|
||
//
|
||
|
||
|
||
|
||
|
||
|
||
DWORD
|
||
AddUsersToFileSrv(
|
||
IN PEFS_USER_INFO pEfsUserInfo,
|
||
IN LPCTSTR lpFileName,
|
||
IN DWORD nUsers,
|
||
IN PENCRYPTION_CERTIFICATE * pEncryptionCertificates
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will add an entry in the DDF field of the passed file
|
||
for each certificate passed.
|
||
|
||
The file will not be modified at all if any errors occur
|
||
during processing.
|
||
|
||
Arguments:
|
||
|
||
lpFileName - Supplies the name of the file to be encrypted. File may
|
||
be local or remote. It will be opened for exclusive access.
|
||
|
||
dwCertificates - Supplies the number of certificate structures in the
|
||
pEncryptionCertificates array.
|
||
|
||
pEncryptionCertificates - Supplies an array of pointers to certificate
|
||
structures, one for each user to be added to the file.
|
||
|
||
|
||
Return Value:
|
||
|
||
This routine will fail under the following circumstances:
|
||
|
||
Passed file is not encrypted.
|
||
|
||
Passed file cannot be opened for exclusive access.
|
||
|
||
Caller does not have keys to decrypt the file.
|
||
|
||
A passed certificate was not structurally valid. In this case,
|
||
the entire operation will fail.
|
||
|
||
And all the other reasons why an EFS operation can fail
|
||
(EFS not present, no recovery policy, etc)
|
||
|
||
--*/
|
||
{
|
||
BOOL b = FALSE;
|
||
DWORD rc = ERROR_SUCCESS;
|
||
|
||
//
|
||
// Open the passed file and get the EFS stream
|
||
//
|
||
|
||
//
|
||
// Open for READ_ATTRIBUTES so we don't go through all the noise of
|
||
// decrypting the FEK when we don't really care to (we're going to have to
|
||
// do that here anyway). We could open the file just to be sure the decrypt
|
||
// is going to work, but there's no point in speeding up the failure case.
|
||
//
|
||
|
||
PEFS_DATA_STREAM_HEADER pEfsStream = NULL;
|
||
|
||
HANDLE hFile;
|
||
|
||
DWORD FileAttributes;
|
||
DWORD Flags = 0;
|
||
|
||
FileAttributes = GetFileAttributes( lpFileName );
|
||
if (FileAttributes == -1) {
|
||
return GetLastError();
|
||
}
|
||
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
Flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||
}
|
||
hFile = CreateFile(
|
||
lpFileName,
|
||
FILE_READ_ATTRIBUTES | FILE_WRITE_DATA,
|
||
0,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
Flags,
|
||
NULL
|
||
);
|
||
|
||
if (hFile != INVALID_HANDLE_VALUE) {
|
||
|
||
//
|
||
// Get the EFS stream
|
||
//
|
||
|
||
NTSTATUS Status;
|
||
|
||
Status = GetFileEfsStream(
|
||
hFile,
|
||
&pEfsStream
|
||
);
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// Decrypt the FEK, if possible
|
||
//
|
||
|
||
PEFS_KEY Fek;
|
||
PEFS_DATA_STREAM_HEADER UpdatedEfs = NULL;
|
||
|
||
rc = DecryptFek( pEfsUserInfo, pEfsStream, &Fek, &UpdatedEfs, 0 );
|
||
|
||
if (rc == ERROR_SUCCESS) {
|
||
|
||
if (UpdatedEfs != NULL) {
|
||
|
||
//
|
||
// Something changed since the last time this file was
|
||
// opened.
|
||
//
|
||
|
||
LsapFreeLsaHeap( pEfsStream );
|
||
pEfsStream = UpdatedEfs;
|
||
UpdatedEfs = NULL;
|
||
}
|
||
|
||
//
|
||
// For each certificate passed to us,
|
||
// add it to the EFS stream.
|
||
//
|
||
|
||
for (DWORD i=0; i<nUsers ; i++) {
|
||
|
||
PENCRYPTION_CERTIFICATE pEncryptionCert;
|
||
BOOLEAN bRet;
|
||
|
||
__try{
|
||
|
||
pEncryptionCert= pEncryptionCertificates[i];
|
||
bRet = AddUserToEFS(
|
||
pEfsStream,
|
||
pEncryptionCert->pUserSid,
|
||
Fek,
|
||
pEncryptionCert->pCertBlob->pbData,
|
||
pEncryptionCert->pCertBlob->cbData,
|
||
&UpdatedEfs
|
||
);
|
||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
bRet = FALSE;
|
||
SetLastError( ERROR_INVALID_PARAMETER );
|
||
|
||
}
|
||
|
||
|
||
if (bRet) {
|
||
|
||
//
|
||
// Toss the old EFS stream and pick up the new one.
|
||
//
|
||
|
||
if (UpdatedEfs) {
|
||
b = TRUE;
|
||
LsapFreeLsaHeap( pEfsStream );
|
||
pEfsStream = UpdatedEfs;
|
||
UpdatedEfs = NULL;
|
||
}
|
||
|
||
} else {
|
||
|
||
b = FALSE;
|
||
rc = GetLastError();
|
||
break;
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we got out with everything working,
|
||
// set the new EFS stream on the file. Otherwise,
|
||
// clean up and fail the entire operation.
|
||
//
|
||
|
||
if (b) {
|
||
|
||
//
|
||
// Set the new EFS stream on the file.
|
||
//
|
||
|
||
if (!EfspSetEfsOnFile( hFile, pEfsStream, NULL )) {
|
||
rc = GetLastError();
|
||
}
|
||
}
|
||
}
|
||
|
||
LsapFreeLsaHeap( pEfsStream );
|
||
|
||
} else {
|
||
|
||
rc = RtlNtStatusToDosError( Status );
|
||
}
|
||
|
||
CloseHandle( hFile );
|
||
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
}
|
||
|
||
return( rc );
|
||
}
|
||
|
||
|
||
BOOL
|
||
EfspSetEfsOnFile(
|
||
IN HANDLE hFile,
|
||
PEFS_DATA_STREAM_HEADER pEfsStream,
|
||
IN PEFS_KEY pNewFek OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routine sets the passed EFS stream onto the passed file. The
|
||
file must be open for WRITE_DATA access.
|
||
|
||
Arguments:
|
||
|
||
hFile - Supplies a handle to the file being modified.
|
||
|
||
pEfsStream - Supplies the new EFS stream to be placed on the file.
|
||
|
||
Return Value:
|
||
|
||
TRUE on success, FALSE on failure. Call GetLastError() for more details.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL b = FALSE;
|
||
|
||
DWORD OutputDataLength = 0;
|
||
DWORD EfsDataLength = 0;
|
||
PBYTE OutputData = NULL;
|
||
|
||
if (ARGUMENT_PRESENT( pNewFek )) {
|
||
|
||
OutputDataLength = 3 * sizeof(ULONG) + 2 * EFS_KEY_SIZE( pNewFek ) + pEfsStream->Length;
|
||
|
||
EfsDataLength = OutputDataLength - 3 * sizeof(ULONG);
|
||
|
||
OutputData = (PBYTE)LsapAllocateLsaHeap( OutputDataLength );
|
||
|
||
if (OutputData) {
|
||
|
||
b = SendEfs(
|
||
pNewFek,
|
||
pEfsStream,
|
||
(PBYTE) OutputData + 3 * sizeof(ULONG),
|
||
&EfsDataLength
|
||
);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Not changing the FEK on the file.
|
||
//
|
||
|
||
OutputDataLength = COMMON_FSCTL_HEADER_SIZE + pEfsStream->Length;
|
||
|
||
EfsDataLength = OutputDataLength - 3 * sizeof(ULONG);
|
||
|
||
OutputData = (PBYTE)LsapAllocateLsaHeap( OutputDataLength );
|
||
|
||
if (OutputData) {
|
||
|
||
b = SendHandleAndEfs(
|
||
hFile,
|
||
pEfsStream,
|
||
(PBYTE) OutputData + 3 * sizeof(ULONG),
|
||
&EfsDataLength
|
||
);
|
||
}
|
||
}
|
||
|
||
if (b) {
|
||
|
||
DWORD Attributes = WRITE_EFS_ATTRIBUTE;
|
||
|
||
if (ARGUMENT_PRESENT( pNewFek )) {
|
||
Attributes |= SET_EFS_KEYBLOB;
|
||
}
|
||
|
||
b = EncryptFSCTLData(
|
||
EFS_OVERWRITE_ATTRIBUTE,
|
||
EFS_ENCRYPT_STREAM,
|
||
Attributes,
|
||
(PBYTE) OutputData + 3 * sizeof(ULONG),
|
||
EfsDataLength,
|
||
(PBYTE) OutputData,
|
||
&OutputDataLength
|
||
);
|
||
|
||
//
|
||
// As currently implemented, this routine cannot fail.
|
||
//
|
||
|
||
ASSERT(b);
|
||
|
||
NTSTATUS NtStatus;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
DWORD FsControl;
|
||
|
||
if (ARGUMENT_PRESENT( pNewFek )) {
|
||
FsControl = FSCTL_SET_ENCRYPTION;
|
||
} else {
|
||
FsControl = FSCTL_ENCRYPTION_FSCTL_IO;
|
||
}
|
||
|
||
NtStatus = NtFsControlFile(
|
||
hFile,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FsControl,
|
||
OutputData,
|
||
OutputDataLength,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if ( NT_SUCCESS( NtStatus )){
|
||
|
||
b = TRUE;
|
||
|
||
} else {
|
||
|
||
b = FALSE;
|
||
|
||
SetLastError( RtlNtStatusToDosError( NtStatus ) );
|
||
}
|
||
}
|
||
|
||
if (OutputData != NULL) {
|
||
LsapFreeLsaHeap( OutputData );
|
||
}
|
||
|
||
return( b );
|
||
}
|
||
|
||
|
||
DWORD
|
||
QueryUsersOnFileSrv(
|
||
IN LPCTSTR lpFileName,
|
||
OUT PDWORD pnUsers,
|
||
OUT PENCRYPTION_CERTIFICATE_HASH ** pUsers
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will return a buffer containing the certificate hashes
|
||
and SIDs for the users who can decrypt the specified file.
|
||
|
||
Note that the current user does not need to be able to decrypt the
|
||
file.
|
||
|
||
Arguments:
|
||
|
||
lpFileName - Supplies the file to be examined. This file will be opened
|
||
for exclusive access. The caller must have READ_ATTRIBUTES access to
|
||
the file.
|
||
|
||
pnUsers - Returns the number of entries in the pHashes array. This field will be
|
||
set even if pHashes is NULL.
|
||
|
||
pHashes - Supplies a buffer to be filled with an array of ENCRYPTION_CERTIFICATE_HASH
|
||
structures, one for each user listed in the DDF of the file. This parameter may
|
||
be NULL if the caller is trying to determine the required size.
|
||
|
||
|
||
Return Value:
|
||
|
||
This routine will fail if:
|
||
|
||
The passed file is not encrypted.
|
||
|
||
The passed buffer is non-NULL but not large enough.
|
||
|
||
The current user does not have READ_ATTRIBUTES access to the file.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Open the file
|
||
//
|
||
|
||
BOOL b = FALSE;
|
||
HANDLE hFile;
|
||
PEFS_DATA_STREAM_HEADER pEfsStream;
|
||
DWORD rc = ERROR_SUCCESS;
|
||
|
||
DWORD FileAttributes;
|
||
DWORD Flags = 0;
|
||
|
||
*pnUsers = NULL;
|
||
*pUsers = NULL;
|
||
|
||
|
||
FileAttributes = GetFileAttributes( lpFileName );
|
||
if (FileAttributes == -1) {
|
||
return GetLastError();
|
||
}
|
||
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
Flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||
}
|
||
hFile = CreateFile(
|
||
lpFileName,
|
||
FILE_READ_ATTRIBUTES,
|
||
0,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
Flags,
|
||
NULL
|
||
);
|
||
|
||
if (hFile != INVALID_HANDLE_VALUE) {
|
||
|
||
//
|
||
// Get the EFS stream
|
||
//
|
||
|
||
NTSTATUS Status;
|
||
|
||
Status = GetFileEfsStream(
|
||
hFile,
|
||
&pEfsStream
|
||
);
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
PENCRYPTED_KEYS pDDF = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataDecryptionField, pEfsStream );
|
||
|
||
__try {
|
||
|
||
//
|
||
// We may have gotten corrupted bits off the disk. We can't
|
||
// verify the checksum on the EfsStream, because we don't have the
|
||
// FEK (and we don't want to require the caller to have to decrypt
|
||
// the file to get this information). So wrap this call in a
|
||
// try-except so we don't risk blowing up.
|
||
//
|
||
|
||
b = QueryCertsFromEncryptedKeys(
|
||
pDDF,
|
||
pnUsers,
|
||
pUsers
|
||
);
|
||
|
||
if (!b) {
|
||
rc = GetLastError();
|
||
}
|
||
|
||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
b = FALSE;
|
||
|
||
rc = GetExceptionCode();
|
||
|
||
if (ERROR_NOACCESS == rc) {
|
||
rc = ERROR_FILE_CORRUPT;
|
||
}
|
||
}
|
||
|
||
LsapFreeLsaHeap( pEfsStream );
|
||
|
||
} else {
|
||
|
||
rc = RtlNtStatusToDosError( Status );
|
||
}
|
||
|
||
CloseHandle( hFile );
|
||
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
}
|
||
|
||
if (rc != ERROR_SUCCESS) {
|
||
DebugLog((DEB_WARN, "QueryUsersOnFileSrv returning %x\n" ,rc ));
|
||
}
|
||
|
||
return( rc );
|
||
}
|
||
|
||
|
||
DWORD
|
||
QueryRecoveryAgentsSrv(
|
||
IN LPCTSTR lpFileName,
|
||
OUT PDWORD pnRecoveryAgents,
|
||
OUT PENCRYPTION_CERTIFICATE_HASH ** pRecoveryAgents
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will return a buffer containing the certificate hashes
|
||
for the recovery agents on the passed file.
|
||
|
||
Note that the current user does not need to be able to decrypt the
|
||
file.
|
||
|
||
[ Should we combine this routine with the one above, and just take a
|
||
flag? Or should there be one routine that returns everything, perhaps
|
||
with a mark for each user that is in the DRF? There are several ways
|
||
to do this. ]
|
||
|
||
Arguments:
|
||
|
||
lpFileName - Supplies the file to be examined. This file will be opened
|
||
for exclusive access. The caller must have READ_ATTRIBUTES access to
|
||
the file.
|
||
|
||
pcbBuffer - Supplies/returns the size of the buffer passed in the in pHashes
|
||
parameter. If pHashes is NULL, the function will succeed and the required
|
||
size will be returned.
|
||
|
||
pHashes - Supplies a buffer to be filled with an array of ENCRYPTION_CERTIFICATE_HASH
|
||
structures, one for each user listed in the DRF of the file. This parameter may
|
||
be NULL if the caller is trying to determine the required size.
|
||
|
||
|
||
Return Value:
|
||
|
||
This routine will fail if:
|
||
|
||
The passed file is not encrypted.
|
||
|
||
The passed buffer is non-NULL but not large enough.
|
||
|
||
The current user does not have READ_ATTRIBUTES access to the file.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Open the file
|
||
//
|
||
|
||
BOOL b = FALSE;
|
||
HANDLE hFile;
|
||
PEFS_DATA_STREAM_HEADER pEfsStream;
|
||
DWORD rc = ERROR_SUCCESS;
|
||
|
||
DWORD FileAttributes;
|
||
DWORD Flags = 0;
|
||
|
||
*pnRecoveryAgents = 0;
|
||
*pRecoveryAgents = NULL;
|
||
|
||
FileAttributes = GetFileAttributes( lpFileName );
|
||
if (FileAttributes == -1) {
|
||
return GetLastError();
|
||
}
|
||
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
Flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||
}
|
||
hFile = CreateFile(
|
||
lpFileName,
|
||
FILE_READ_ATTRIBUTES,
|
||
0,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
Flags,
|
||
NULL
|
||
);
|
||
|
||
if (hFile != INVALID_HANDLE_VALUE) {
|
||
|
||
//
|
||
// Get the EFS stream
|
||
//
|
||
|
||
NTSTATUS Status;
|
||
|
||
Status = GetFileEfsStream(
|
||
hFile,
|
||
&pEfsStream
|
||
);
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
PENCRYPTED_KEYS pDRF = (PENCRYPTED_KEYS)OFFSET_TO_POINTER( DataRecoveryField, pEfsStream );
|
||
|
||
if ( (PVOID)pDRF != (PVOID)pEfsStream) {
|
||
|
||
__try {
|
||
|
||
b = QueryCertsFromEncryptedKeys(
|
||
pDRF,
|
||
pnRecoveryAgents,
|
||
pRecoveryAgents
|
||
);
|
||
|
||
if (!b) {
|
||
rc = GetLastError();
|
||
}
|
||
|
||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
b = FALSE;
|
||
|
||
rc = GetExceptionCode();
|
||
|
||
if (ERROR_NOACCESS == rc) {
|
||
rc = ERROR_FILE_CORRUPT;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
|
||
LsapFreeLsaHeap( pEfsStream );
|
||
|
||
} else {
|
||
|
||
rc = RtlNtStatusToDosError( Status );
|
||
}
|
||
|
||
CloseHandle( hFile );
|
||
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
}
|
||
|
||
if (rc != ERROR_SUCCESS) {
|
||
DebugLog((DEB_WARN, "QueryRecoveryAgentsSrv returning %x\n" ,rc ));
|
||
}
|
||
|
||
return( rc );
|
||
}
|
||
|
||
|
||
DWORD
|
||
RemoveUsersFromFileSrv(
|
||
IN PEFS_USER_INFO pEfsUserInfo,
|
||
IN LPCTSTR lpFileName,
|
||
IN DWORD nUsers,
|
||
IN PENCRYPTION_CERTIFICATE_HASH * pHashes
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will remove the passed users from the list
|
||
of people who can decrypt the passed file.
|
||
|
||
The file will not be modified at all if any errors occur
|
||
during processing.
|
||
|
||
Arguments:
|
||
|
||
lpFileName - Supplies a pointer to the file to be edited. This file
|
||
will be opened for exclusive access.
|
||
|
||
nHashes - Supplies the number of hashes in the pHashes array.
|
||
|
||
pHashes - Supplies an array of pointers to hash structures for subjects to be
|
||
removed from the file.
|
||
|
||
|
||
Return Value:
|
||
|
||
This function will fail if:
|
||
|
||
The passed file is not encrypted.
|
||
|
||
The user does not have sufficient access to the file.
|
||
|
||
--*/
|
||
{
|
||
PEFS_DATA_STREAM_HEADER UpdatedEfs = NULL;
|
||
HANDLE hFile;
|
||
PEFS_DATA_STREAM_HEADER pEfsStream = NULL;
|
||
DWORD rc = ERROR_SUCCESS;
|
||
PEFS_KEY Fek = NULL;
|
||
|
||
DWORD FileAttributes;
|
||
DWORD Flags = 0;
|
||
|
||
FileAttributes = GetFileAttributes( lpFileName );
|
||
if (FileAttributes == -1) {
|
||
return GetLastError();
|
||
}
|
||
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
Flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||
}
|
||
hFile = CreateFile(
|
||
lpFileName,
|
||
FILE_READ_ATTRIBUTES| FILE_WRITE_DATA,
|
||
0,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
Flags,
|
||
NULL
|
||
);
|
||
|
||
if (hFile != INVALID_HANDLE_VALUE) {
|
||
|
||
//
|
||
// Get the EFS stream
|
||
//
|
||
|
||
NTSTATUS Status;
|
||
|
||
Status = GetFileEfsStream(
|
||
hFile,
|
||
&pEfsStream
|
||
);
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
rc = DecryptFek( pEfsUserInfo, pEfsStream, &Fek, &UpdatedEfs, 0 );
|
||
|
||
if (ERROR_SUCCESS == rc) {
|
||
|
||
//
|
||
// If we got an EFS stream back, toss the
|
||
// one from the file and use it.
|
||
//
|
||
|
||
if ( UpdatedEfs != NULL ) {
|
||
LsapFreeLsaHeap( pEfsStream );
|
||
pEfsStream = UpdatedEfs;
|
||
UpdatedEfs = NULL;
|
||
}
|
||
|
||
if (RemoveUsersFromEfsStream(
|
||
pEfsStream,
|
||
nUsers,
|
||
pHashes,
|
||
Fek,
|
||
&UpdatedEfs
|
||
)) {
|
||
|
||
//
|
||
// If we got an EFS stream back, toss the old EFS
|
||
// stream and pick up the new one. We'll only get
|
||
// an EFS stream back if we found someone to remove.
|
||
//
|
||
|
||
if (UpdatedEfs != NULL) {
|
||
|
||
LsapFreeLsaHeap( pEfsStream );
|
||
pEfsStream = UpdatedEfs;
|
||
UpdatedEfs = NULL;
|
||
|
||
if (!EfspSetEfsOnFile( hFile, pEfsStream, NULL )) {
|
||
rc = GetLastError();
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
}
|
||
|
||
if (UpdatedEfs != NULL) {
|
||
LsapFreeLsaHeap( UpdatedEfs );
|
||
}
|
||
|
||
LsapFreeLsaHeap( Fek );
|
||
}
|
||
|
||
LsapFreeLsaHeap( pEfsStream );
|
||
|
||
} else {
|
||
|
||
rc = RtlNtStatusToDosError( Status );
|
||
}
|
||
|
||
CloseHandle( hFile );
|
||
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
}
|
||
|
||
return( rc );
|
||
}
|
||
|
||
|
||
DWORD
|
||
SetFileEncryptionKeySrv(
|
||
IN PEFS_USER_INFO pEfsUserInfo,
|
||
IN PENCRYPTION_CERTIFICATE pEncryptionCertificate
|
||
)
|
||
{
|
||
DWORD rc = ERROR_SUCCESS;
|
||
PBYTE pbHash = NULL;
|
||
DWORD cbHash;
|
||
|
||
|
||
//
|
||
// Get the current cert hash
|
||
//
|
||
|
||
(void) GetCurrentHash(
|
||
pEfsUserInfo,
|
||
&pbHash,
|
||
&cbHash
|
||
);
|
||
|
||
//
|
||
// If no certificate was passed, call the code to create
|
||
// a new user key from scratch.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT( pEncryptionCertificate )) {
|
||
|
||
//
|
||
// Create a new key
|
||
//
|
||
|
||
rc = EfspReplaceUserKeyInformation( pEfsUserInfo );
|
||
|
||
} else {
|
||
|
||
__try{
|
||
|
||
if ( pEncryptionCertificate->pCertBlob ){
|
||
rc = EfspInstallCertAsUserKey( pEfsUserInfo, pEncryptionCertificate );
|
||
} else {
|
||
rc = ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
rc = ERROR_INVALID_PARAMETER;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if ( (ERROR_SUCCESS == rc) && pbHash) {
|
||
|
||
//
|
||
// Operation succeeded and there was a current key. Make our best effort
|
||
// to mark the old cert as archived.
|
||
//
|
||
|
||
PCCERT_CONTEXT pCertContext;
|
||
|
||
pCertContext = GetCertContextFromCertHash(
|
||
pbHash,
|
||
cbHash,
|
||
CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | CERT_SYSTEM_STORE_CURRENT_USER
|
||
);
|
||
|
||
if (pCertContext != NULL) {
|
||
|
||
CRYPT_DATA_BLOB dataBlob = {0, NULL};
|
||
|
||
//
|
||
// This is best effort. We do not need to check the return code.
|
||
//
|
||
|
||
(void) CertSetCertificateContextProperty(
|
||
pCertContext,
|
||
CERT_ARCHIVED_PROP_ID,
|
||
0,
|
||
&dataBlob
|
||
);
|
||
|
||
|
||
CertFreeCertificateContext( pCertContext );
|
||
|
||
}
|
||
}
|
||
|
||
if (pbHash) {
|
||
LsapFreeLsaHeap(pbHash);
|
||
}
|
||
|
||
|
||
|
||
return( rc );
|
||
}
|
||
|
||
DWORD
|
||
DuplicateEncryptionInfoFileSrv (
|
||
PEFS_USER_INFO pEfsUserInfo,
|
||
LPCWSTR lpSrcFileName,
|
||
LPCWSTR lpDestFileName,
|
||
LPCWSTR lpDestUncName,
|
||
DWORD dwCreationDistribution,
|
||
DWORD dwAttributes,
|
||
PEFS_RPC_BLOB pRelativeSD,
|
||
BOOL bInheritHandle
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will transfer the EFS information from the source file
|
||
to the target file. It assumes
|
||
|
||
The caller has FILE_READ_ATTRIBUTES access to the source file
|
||
|
||
The caller has WRITE_ATTRIBUTE and WRITE_DATA access to the target. If the target
|
||
is encrypted, the caller must be able to decrypt it.
|
||
|
||
Arguments:
|
||
|
||
pEfsUserInfo - Supplies the user info structure for the current caller.
|
||
|
||
lpSrcFileName - Supplies a pointer to the name of the source file.
|
||
|
||
lpDestFileName - Supplies a pointer to the name of the destination file.
|
||
|
||
dwCreationDistribution - CreationDistribution used in CreateFile.
|
||
|
||
dwAttributes - File attributes used in CreateFile.
|
||
|
||
pRelativeSD - Relative Security Descriptor.
|
||
|
||
BOOL - bInheritHandle.
|
||
|
||
Return Value:
|
||
|
||
Win32 error.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Get the encryption information off of the src file.
|
||
//
|
||
|
||
HANDLE hSrcFile;
|
||
HANDLE hDestFile;
|
||
DWORD rc = ERROR_SUCCESS;
|
||
BOOLEAN fResult = FALSE;
|
||
PEFS_KEY Fek;
|
||
|
||
DWORD FileAttributes;
|
||
DWORD FileAttributesDst;
|
||
DWORD FlagsSrc = 0;
|
||
DWORD creationDistribution = 0;
|
||
|
||
FileAttributes = GetFileAttributes( lpSrcFileName );
|
||
if (FileAttributes == -1) {
|
||
return GetLastError();
|
||
}
|
||
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
FlagsSrc = FILE_FLAG_BACKUP_SEMANTICS;
|
||
}
|
||
|
||
//
|
||
// Try to open the file.
|
||
//
|
||
|
||
hSrcFile = CreateFile(
|
||
lpSrcFileName,
|
||
FILE_READ_ATTRIBUTES,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
FlagsSrc,
|
||
NULL
|
||
);
|
||
|
||
if (hSrcFile != INVALID_HANDLE_VALUE) {
|
||
|
||
NTSTATUS Status;
|
||
PEFS_DATA_STREAM_HEADER pEfsStream;
|
||
|
||
Status = GetFileEfsStream(
|
||
hSrcFile,
|
||
&pEfsStream
|
||
);
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
PEFS_DATA_STREAM_HEADER NewEfs = NULL;
|
||
GUID NewId;
|
||
|
||
//
|
||
// We need to change the file ID here. DecryptFek will recalculate
|
||
// the checksum for us.
|
||
//
|
||
|
||
RPC_STATUS RpcStatus = UuidCreate ( &NewId );
|
||
|
||
if (RpcStatus == ERROR_SUCCESS || RpcStatus == RPC_S_UUID_LOCAL_ONLY) {
|
||
RtlCopyMemory( &(pEfsStream->EfsId), &NewId, sizeof( GUID ) );
|
||
}
|
||
|
||
rc = DecryptFek(
|
||
pEfsUserInfo,
|
||
pEfsStream,
|
||
&Fek,
|
||
&NewEfs,
|
||
0
|
||
);
|
||
|
||
if (rc == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Got the $EFS, now prepares to create destination
|
||
//
|
||
|
||
FileAttributesDst = GetFileAttributes( lpDestFileName );
|
||
if (FileAttributesDst == -1) {
|
||
|
||
rc = GetLastError();
|
||
|
||
FileAttributesDst = 0;
|
||
|
||
if ((ERROR_FILE_NOT_FOUND == rc) || (ERROR_PATH_NOT_FOUND == rc)) {
|
||
rc = ERROR_SUCCESS;
|
||
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
creationDistribution = FILE_CREATE;
|
||
|
||
//
|
||
// Force the new destination to be a directory
|
||
//
|
||
|
||
dwAttributes |= FILE_ATTRIBUTE_DIRECTORY;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// File exist. Check if Dir to Dir or File to File
|
||
//
|
||
|
||
if (dwCreationDistribution == CREATE_NEW) {
|
||
|
||
//
|
||
// The file is already existed. CREATE_NEW will fail.
|
||
//
|
||
|
||
rc = ERROR_FILE_EXISTS;
|
||
|
||
} else if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY){
|
||
|
||
if (!(FileAttributesDst & FILE_ATTRIBUTE_DIRECTORY)) {
|
||
|
||
rc = ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH;
|
||
|
||
} else {
|
||
|
||
if (FileAttributesDst & FILE_ATTRIBUTE_ENCRYPTED) {
|
||
creationDistribution = FILE_OPEN;
|
||
}
|
||
|
||
}
|
||
} else {
|
||
|
||
if (FileAttributesDst & FILE_ATTRIBUTE_DIRECTORY) {
|
||
|
||
rc = ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Now we can Create/open the destination.
|
||
// We have not validate the share access yet. We will use the UNC name
|
||
// to validate if it is a network session.
|
||
//
|
||
|
||
OBJECT_ATTRIBUTES Obja;
|
||
UNICODE_STRING DstNtName;
|
||
LPWSTR DstFileName = NULL;
|
||
LPWSTR LongFileName = NULL;
|
||
DWORD FileNameLength;
|
||
BOOL b = TRUE;
|
||
ULONG CreateOptions = 0;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
RtlInitUnicodeString(
|
||
&DstNtName,
|
||
NULL
|
||
);
|
||
|
||
if (rc == ERROR_SUCCESS) {
|
||
|
||
if (lpDestUncName) {
|
||
|
||
|
||
if ( (FileNameLength = wcslen(lpDestUncName)) >= MAX_PATH ) {
|
||
|
||
//
|
||
// We need \\?\UNC\server\share\dir\file format to open the file.
|
||
//
|
||
|
||
DstFileName = LongFileName = (LPWSTR)LsapAllocateLsaHeap( (FileNameLength + 8) * sizeof (WCHAR) );
|
||
if (!LongFileName) {
|
||
rc = ERROR_NOT_ENOUGH_MEMORY;
|
||
} else {
|
||
|
||
wcscpy(LongFileName, L"\\\\?\\UNC");
|
||
wcscat(LongFileName, &lpDestUncName[1]);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
DstFileName = (LPWSTR) lpDestUncName;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
DstFileName = (LPWSTR) lpDestFileName;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
if ( rc != ERROR_SUCCESS ) {
|
||
|
||
if (NewEfs) {
|
||
LsapFreeLsaHeap( NewEfs );
|
||
}
|
||
LsapFreeLsaHeap( Fek );
|
||
LsapFreeLsaHeap( pEfsStream );
|
||
CloseHandle( hSrcFile );
|
||
return rc;
|
||
|
||
}
|
||
|
||
b = RtlDosPathNameToNtPathName_U(
|
||
DstFileName,
|
||
&DstNtName,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if ( b ){
|
||
|
||
if (!creationDistribution) {
|
||
creationDistribution = (dwCreationDistribution == CREATE_NEW) ? FILE_CREATE : FILE_OVERWRITE_IF;
|
||
}
|
||
|
||
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
CreateOptions = FILE_DIRECTORY_FILE;
|
||
} else {
|
||
|
||
//
|
||
// NTFS does not support FILE_NO_COPRESSION for the dir.
|
||
//
|
||
|
||
CreateOptions |= FILE_NO_COMPRESSION;
|
||
}
|
||
|
||
|
||
//
|
||
// Encryption bit is not needed. We will encrypt it any way.
|
||
//
|
||
|
||
dwAttributes &= ~(FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_READONLY);
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&DstNtName,
|
||
bInheritHandle ? OBJ_INHERIT | OBJ_CASE_INSENSITIVE : OBJ_CASE_INSENSITIVE,
|
||
0,
|
||
pRelativeSD? pRelativeSD->pbData:NULL
|
||
);
|
||
|
||
Status = NtCreateFile(
|
||
&hDestFile,
|
||
FILE_READ_DATA | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
dwAttributes,
|
||
0,
|
||
creationDistribution,
|
||
CreateOptions | FILE_SYNCHRONOUS_IO_NONALERT,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
if ( NT_SUCCESS(Status) && lpDestUncName ) {
|
||
|
||
//
|
||
// If this is a net session, we need to close the loopback handle.
|
||
// This handle is not good to send large FSCTL request.
|
||
// In this case, the file should already exist or overwritten.
|
||
// No parameters for create new file are needed, such as SD.
|
||
//
|
||
|
||
RtlFreeHeap(
|
||
RtlProcessHeap(),
|
||
0,
|
||
DstNtName.Buffer
|
||
);
|
||
|
||
RtlInitUnicodeString(
|
||
&DstNtName,
|
||
NULL
|
||
);
|
||
|
||
CloseHandle( hDestFile );
|
||
|
||
Status = STATUS_NO_SUCH_FILE;
|
||
|
||
b = RtlDosPathNameToNtPathName_U(
|
||
lpDestFileName,
|
||
&DstNtName,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if ( b ){
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&DstNtName,
|
||
bInheritHandle ? OBJ_INHERIT | OBJ_CASE_INSENSITIVE : OBJ_CASE_INSENSITIVE,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
Status = NtCreateFile(
|
||
&hDestFile,
|
||
FILE_READ_DATA | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
dwAttributes,
|
||
0,
|
||
FILE_OPEN,
|
||
CreateOptions | FILE_SYNCHRONOUS_IO_NONALERT,
|
||
NULL,
|
||
0
|
||
);
|
||
}
|
||
|
||
}
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// Work around for FILE_NO_COMPRESSION
|
||
//
|
||
|
||
if ((FileAttributesDst & FILE_ATTRIBUTE_COMPRESSED) || ((dwCreationDistribution == CREATE_NEW) && (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))) {
|
||
|
||
//
|
||
// Let's decompressed the dir
|
||
//
|
||
|
||
USHORT State = COMPRESSION_FORMAT_NONE;
|
||
ULONG Length;
|
||
|
||
//
|
||
// Attempt to uncompress the directory. Best effort. If it fails, we still continue.
|
||
//
|
||
|
||
b = DeviceIoControl(
|
||
hDestFile,
|
||
FSCTL_SET_COMPRESSION,
|
||
&State,
|
||
sizeof(USHORT),
|
||
NULL,
|
||
0,
|
||
&Length,
|
||
FALSE
|
||
);
|
||
|
||
}
|
||
|
||
if (!EfspSetEfsOnFile( hDestFile, NewEfs? NewEfs : pEfsStream, Fek )) {
|
||
|
||
rc = GetLastError();
|
||
if ( ERROR_INVALID_FUNCTION == rc ) {
|
||
|
||
//
|
||
// lpDestFileName is a local path. Change it to be a volume name.
|
||
//
|
||
|
||
DWORD FileSystemFlags;
|
||
WCHAR RootDirName[4];
|
||
|
||
wcsncpy(RootDirName, lpDestFileName, 3);
|
||
RootDirName[3] = 0;
|
||
if(GetVolumeInformation(
|
||
RootDirName,
|
||
NULL, // Volume name.
|
||
0, // Volume name length.
|
||
NULL, // Serial number.
|
||
NULL, // Maximum length.
|
||
&FileSystemFlags,
|
||
NULL, // File system type.
|
||
0
|
||
)){
|
||
|
||
if (!(FileSystemFlags & FILE_SUPPORTS_ENCRYPTION)) {
|
||
|
||
//
|
||
// Let's map the error.
|
||
//
|
||
|
||
rc = ERROR_VOLUME_NOT_SUPPORT_EFS;
|
||
if (dwCreationDistribution == CREATE_NEW) {
|
||
CloseHandle( hDestFile );
|
||
hDestFile = 0;
|
||
DeleteFileW(lpDestFileName);
|
||
}
|
||
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
if (hDestFile) {
|
||
CloseHandle( hDestFile );
|
||
}
|
||
|
||
} else {
|
||
|
||
rc = RtlNtStatusToDosError( Status );
|
||
|
||
}
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
|
||
}
|
||
|
||
if (DstNtName.Buffer) {
|
||
RtlFreeHeap(
|
||
RtlProcessHeap(),
|
||
0,
|
||
DstNtName.Buffer
|
||
);
|
||
}
|
||
|
||
if (LongFileName) {
|
||
LsapFreeLsaHeap( LongFileName );
|
||
}
|
||
|
||
if (NewEfs) {
|
||
LsapFreeLsaHeap( NewEfs );
|
||
}
|
||
LsapFreeLsaHeap( Fek );
|
||
}
|
||
|
||
LsapFreeLsaHeap( pEfsStream );
|
||
|
||
} else {
|
||
|
||
rc = RtlNtStatusToDosError( Status );
|
||
}
|
||
|
||
CloseHandle( hSrcFile );
|
||
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
}
|
||
|
||
return( rc );
|
||
}
|
||
|
||
VOID
|
||
EfsLogEntry (
|
||
WORD wType,
|
||
WORD wCategory,
|
||
DWORD dwEventID,
|
||
WORD wNumStrings,
|
||
DWORD dwDataSize,
|
||
LPCTSTR *lpStrings,
|
||
LPVOID lpRawData
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
This routine wraps the call to ReportEvent.
|
||
Arguments:
|
||
|
||
See info for ReportEvent.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
--*/
|
||
{
|
||
|
||
HANDLE EventHandleLog;
|
||
|
||
EventHandleLog = RegisterEventSource(
|
||
NULL,
|
||
EFSSOURCE
|
||
);
|
||
|
||
if ( EventHandleLog ){
|
||
|
||
ReportEvent(
|
||
EventHandleLog,
|
||
wType,
|
||
wCategory,
|
||
dwEventID,
|
||
NULL,
|
||
wNumStrings,
|
||
dwDataSize,
|
||
lpStrings,
|
||
lpRawData
|
||
);
|
||
DeregisterEventSource( EventHandleLog );
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
EfsFileKeyInfoSrv(
|
||
IN LPCWSTR lpFileName,
|
||
IN DWORD InfoClass,
|
||
OUT PDWORD nbData,
|
||
OUT PBYTE *pbData
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine gets the internal info about the encryption used on the file.
|
||
|
||
Arguments:
|
||
|
||
lpFileName - File name.
|
||
|
||
nbData - Length of pbData returned.
|
||
|
||
pbData - Info returned
|
||
|
||
Return Value:
|
||
|
||
Win32 error.
|
||
|
||
--*/
|
||
{
|
||
PEFS_KEY Fek = NULL;
|
||
HANDLE hFile;
|
||
PEFS_DATA_STREAM_HEADER pEfsStream = NULL;
|
||
NTSTATUS Status;
|
||
DWORD rc = ERROR_SUCCESS;
|
||
DWORD FileAttributes;
|
||
DWORD Flags = 0;
|
||
|
||
EFS_USER_INFO EfsUserInfo;
|
||
PEFS_KEY_INFO pKeyInfo;
|
||
|
||
*nbData = 0;
|
||
*pbData = NULL;
|
||
|
||
if (InfoClass != BASIC_KEY_INFO) {
|
||
return ERROR_INVALID_PARAMETER;
|
||
}
|
||
FileAttributes = GetFileAttributes( lpFileName );
|
||
if (FileAttributes == -1) {
|
||
return GetLastError();
|
||
}
|
||
if ((FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) == 0) {
|
||
return ERROR_FILE_NOT_ENCRYPTED;
|
||
}
|
||
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
Flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||
}
|
||
hFile = CreateFile(
|
||
lpFileName,
|
||
FILE_READ_ATTRIBUTES,
|
||
0,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
Flags,
|
||
NULL
|
||
);
|
||
if (hFile == INVALID_HANDLE_VALUE) {
|
||
return GetLastError();
|
||
}
|
||
|
||
|
||
Status = GetFileEfsStream(
|
||
hFile,
|
||
&pEfsStream
|
||
);
|
||
CloseHandle(hFile);
|
||
|
||
if (NT_SUCCESS( Status )){
|
||
if (EfspGetUserInfo( &EfsUserInfo )) {
|
||
|
||
HANDLE hToken;
|
||
HANDLE hProfile;
|
||
|
||
if (EfspLoadUserProfile( &EfsUserInfo, &hToken, &hProfile )) {
|
||
|
||
rc = EfsGetFek(
|
||
&EfsUserInfo,
|
||
pEfsStream,
|
||
&Fek
|
||
);
|
||
|
||
if (ERROR_SUCCESS == rc) {
|
||
pKeyInfo = (PEFS_KEY_INFO) MIDL_user_allocate( sizeof(EFS_KEY_INFO) );
|
||
pKeyInfo->dwVersion = 1;
|
||
pKeyInfo->Entropy = Fek->Entropy;
|
||
pKeyInfo->Algorithm = Fek->Algorithm;
|
||
pKeyInfo->KeyLength = Fek->KeyLength;
|
||
*pbData = (PBYTE) pKeyInfo;
|
||
*nbData = sizeof(EFS_KEY_INFO);
|
||
LsapFreeLsaHeap( Fek );
|
||
|
||
}
|
||
|
||
EfspUnloadUserProfile( hToken, hProfile );
|
||
|
||
}
|
||
EfspFreeUserInfo( &EfsUserInfo );
|
||
}
|
||
} else {
|
||
rc = RtlNtStatusToDosError( Status );
|
||
}
|
||
LsapFreeLsaHeap( pEfsStream );
|
||
|
||
return rc;
|
||
}
|