You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1311 lines
42 KiB
1311 lines
42 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
create.c
|
|
|
|
Abstract:
|
|
|
|
This module will handle the IRP_MJ_CREATE (and all associated support
|
|
routines) requests.
|
|
|
|
Author:
|
|
|
|
Robert Gu (robertg) 29-Oct-1996
|
|
Environment:
|
|
|
|
Kernel Mode Only
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "efs.h"
|
|
#include "efsrtl.h"
|
|
#include "efsext.h"
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
//
|
|
// cannot make this code paged because of calls to
|
|
// virtual memory functions.
|
|
//
|
|
//#pragma alloc_text(PAGE, EFSFilePostCreate)
|
|
//#pragma alloc_text(PAGE, EFSPostCreate)
|
|
//
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
EFSFilePostCreate(
|
|
IN PDEVICE_OBJECT VolDo,
|
|
IN PIRP Irp,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN NTSTATUS Status,
|
|
IN OUT PVOID *PCreateContext
|
|
)
|
|
{
|
|
PEFS_CONTEXT pEfsContext;
|
|
KIRQL savedIrql;
|
|
NTSTATUS EfsStatus = STATUS_SUCCESS;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!PCreateContext) {
|
|
return Status;
|
|
}
|
|
|
|
pEfsContext = *PCreateContext;
|
|
|
|
if (( NT_SUCCESS( Status ) && (Status != STATUS_PENDING) && (Status != STATUS_REPARSE))
|
|
&& pEfsContext){
|
|
|
|
if ( NO_FURTHER_PROCESSING != pEfsContext->Status ){
|
|
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
|
|
#if DBG
|
|
if ( (EFSTRACEALL | EFSTRACELIGHT ) & EFSDebug ){
|
|
|
|
DbgPrint( " EFSFILTER: Begin post create. \n" );
|
|
|
|
}
|
|
#endif
|
|
|
|
if ((pEfsContext->EfsStreamData) &&
|
|
(EFS_STREAM_TRANSITION == ((PEFS_STREAM)(pEfsContext->EfsStreamData))->Status)) {
|
|
|
|
PSID SystemSid;
|
|
SID_IDENTIFIER_AUTHORITY IdentifierAuthority = SECURITY_NT_AUTHORITY;
|
|
|
|
//
|
|
// $EFS indicates transition state.
|
|
// Only the system can open it
|
|
//
|
|
|
|
SystemSid = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
RtlLengthRequiredSid(1),
|
|
'msfE'
|
|
);
|
|
|
|
if ( SystemSid ){
|
|
|
|
EfsStatus = RtlInitializeSid( SystemSid, &IdentifierAuthority, (UCHAR) 1 );
|
|
|
|
if ( NT_SUCCESS(EfsStatus) ){
|
|
|
|
PACCESS_TOKEN accessToken = NULL;
|
|
PTOKEN_USER UserId = NULL;
|
|
|
|
*(RtlSubAuthoritySid(SystemSid, 0 )) = SECURITY_LOCAL_SYSTEM_RID;
|
|
|
|
//
|
|
// We got the system SID. Now try to get the caller's SID.
|
|
//
|
|
|
|
accessToken = irpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.ClientToken;
|
|
if(!accessToken) {
|
|
accessToken = irpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.PrimaryToken;
|
|
}
|
|
if ( accessToken ){
|
|
|
|
//
|
|
// Get User ID
|
|
//
|
|
|
|
EfsStatus = SeQueryInformationToken(
|
|
accessToken,
|
|
TokenUser,
|
|
&UserId
|
|
);
|
|
|
|
if ( NT_SUCCESS(EfsStatus) ){
|
|
|
|
//
|
|
// Got the user SID
|
|
//
|
|
|
|
if ( !RtlEqualSid ( SystemSid, UserId->User.Sid) ) {
|
|
|
|
EfsStatus = STATUS_ACCESS_DENIED;
|
|
|
|
}
|
|
}
|
|
|
|
ExFreePool( UserId );
|
|
|
|
} else {
|
|
//
|
|
// Cannot get the user token
|
|
//
|
|
|
|
EfsStatus = STATUS_ACCESS_DENIED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ExFreePool( SystemSid );
|
|
|
|
} else {
|
|
|
|
EfsStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// $EFS in normal status
|
|
// Set Key Blob and/or write $EFS
|
|
//
|
|
// Legacy problem, The fourth parameter of EfsPostCreate (OpenType)
|
|
// was used to indicate a recovery open. The design was changed. Now
|
|
// this parameter is not used. It is not worth to take out the parameter
|
|
// now. This will need to change several modules, EFS.SYS, KSECDD.SYS
|
|
// SEcur32.lib and LSASRV.DLL. We might just leave it as reserved for future use.
|
|
// To speed up a little bit, pass in 0.
|
|
//
|
|
|
|
EfsStatus = EFSPostCreate(
|
|
VolDo,
|
|
Irp,
|
|
pEfsContext,
|
|
0
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (pEfsContext){
|
|
|
|
//
|
|
// Release memory if necessary
|
|
//
|
|
|
|
*PCreateContext = NULL;
|
|
if ( pEfsContext->EfsStreamData ) {
|
|
|
|
ExFreePool(pEfsContext->EfsStreamData);
|
|
pEfsContext->EfsStreamData = NULL;
|
|
|
|
}
|
|
|
|
ExFreeToNPagedLookasideList(&(EfsData.EfsContextPool), pEfsContext );
|
|
}
|
|
|
|
if (!NT_SUCCESS(EfsStatus)) {
|
|
return EfsStatus;
|
|
}
|
|
|
|
return Status; // If EFS operation succeeded, just return the original status code.
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
EFSPostCreate(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PEFS_CONTEXT EfsContext,
|
|
IN ULONG OpenType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function calls the EFS server to get FEK, $EFS, set Key Blob and or write $EFS.
|
|
|
|
We could not use user's space to talk to LSA so we need to attach to LSA to allocate
|
|
|
|
memory in LSA space. We could cause APC dead lock if we call LSA while attached to LSA.
|
|
|
|
We need to detach before calling LSA and reattach to get data back from LSA.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the target device object.
|
|
|
|
Irp - Pointer to the I/O Request Packet that represents the operation.
|
|
|
|
EfsContext - A context block associated with the file object.
|
|
|
|
OpenType - File create(open) option
|
|
|
|
IrpContext - NTFS internal data
|
|
|
|
FileHdl - NTFS internal data
|
|
|
|
AttributeHandle - NTFS internal data
|
|
|
|
|
|
--*/
|
|
{
|
|
PEFS_KEY fek = NULL;
|
|
PEFS_DATA_STREAM_HEADER efsStream = NULL;
|
|
PVOID currentEfsStream = NULL;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
PVOID bufferBase = NULL;
|
|
ULONG currentEfsStreamLength = 0;
|
|
ULONG bufferLength;
|
|
SIZE_T regionSize = 0;
|
|
PACCESS_TOKEN accessToken = NULL;
|
|
PTOKEN_USER UserId = NULL;
|
|
GUID *EfsId = NULL;
|
|
HANDLE CrntProcess = NULL;
|
|
BOOLEAN ProcessAttached = FALSE;
|
|
BOOLEAN ProcessNeedAttach = FALSE;
|
|
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel = SecurityImpersonation;
|
|
KAPC_STATE ApcState;
|
|
/*
|
|
PIO_SECURITY_CONTEXT sContext;
|
|
sContext = irpSp->Parameters.Create.SecurityContext;
|
|
DbgPrint( "\n PostCreate: Desired Access %x\n", sContext->DesiredAccess );
|
|
DbgPrint( "\n PostCreate: Orginal Desired Access %x\n", sContext->AccessState->OriginalDesiredAccess );
|
|
DbgPrint( "\n PostCreate: PrevGrant Access %x\n", sContext->AccessState->PreviouslyGrantedAccess );
|
|
DbgPrint( "\n PostCreate: Remaining Desired Access %x\n", sContext->AccessState->RemainingDesiredAccess );
|
|
*/
|
|
//
|
|
// Check if we can use the cache to verify the open
|
|
//
|
|
|
|
if ( !(EfsContext->Status & NO_OPEN_CACHE_CHECK) ){
|
|
|
|
if ( irpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.ClientToken ){
|
|
accessToken = irpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.ClientToken;
|
|
ImpersonationLevel = irpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.ImpersonationLevel;
|
|
} else {
|
|
accessToken = irpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.PrimaryToken;
|
|
}
|
|
|
|
if ( accessToken ){
|
|
|
|
//
|
|
// Get User ID
|
|
//
|
|
|
|
status = SeQueryInformationToken(
|
|
accessToken,
|
|
TokenUser,
|
|
&UserId
|
|
);
|
|
|
|
if ( NT_SUCCESS(status) ){
|
|
|
|
if ( EfsFindInCache(
|
|
&((( PEFS_DATA_STREAM_HEADER ) EfsContext->EfsStreamData)->EfsId),
|
|
UserId
|
|
)) {
|
|
|
|
ExFreePool( UserId );
|
|
#if DBG
|
|
if ( (EFSTRACEALL ) & EFSDebug ){
|
|
DbgPrint( " EFS:Open with cache. \n" );
|
|
}
|
|
#endif
|
|
return ( STATUS_SUCCESS );
|
|
}
|
|
}
|
|
|
|
//
|
|
// UserId will be freed later
|
|
//
|
|
|
|
}
|
|
|
|
//
|
|
// Check cache failure should not block the normal operations
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Clear the cache bit
|
|
//
|
|
|
|
EfsContext->Status &= ~NO_OPEN_CACHE_CHECK;
|
|
|
|
//
|
|
// Check if it is ACCESS_ATTRIBUTE ONLY
|
|
//
|
|
|
|
if ( !( irpSp->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess &
|
|
( FILE_APPEND_DATA | FILE_READ_DATA | FILE_WRITE_DATA | FILE_EXECUTE )) &&
|
|
( EfsContext->Status & TURN_ON_ENCRYPTION_BIT ) &&
|
|
( !(EfsContext->Status & (NEW_FILE_EFS_REQUIRED | NEW_DIR_EFS_REQUIRED)))){
|
|
|
|
//
|
|
// A new stream is to be created without data access required. We might not
|
|
// have the keys to decrypt the $EFS. We just need to turn on the bit here.
|
|
// Changed the real action required.
|
|
// Free the memory not required by this action.
|
|
//
|
|
#if DBG
|
|
if ( (EFSTRACEALL ) & EFSDebug ){
|
|
DbgPrint( " EFS:Open accessing attr only. \n" );
|
|
}
|
|
#endif
|
|
|
|
if (EfsContext->EfsStreamData){
|
|
ExFreePool(EfsContext->EfsStreamData);
|
|
EfsContext->EfsStreamData = NULL;
|
|
}
|
|
EfsContext->Status = TURN_ON_ENCRYPTION_BIT | TURN_ON_BIT_ONLY ;
|
|
|
|
} else if ( !(EfsContext->Status & TURN_ON_BIT_ONLY) ) {
|
|
|
|
if (accessToken == NULL){
|
|
if ( irpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.ClientToken ){
|
|
accessToken = irpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.ClientToken;
|
|
ImpersonationLevel = irpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.ImpersonationLevel;
|
|
} else {
|
|
accessToken = irpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.PrimaryToken;
|
|
}
|
|
|
|
//
|
|
// Get User ID
|
|
//
|
|
|
|
status = SeQueryInformationToken(
|
|
accessToken,
|
|
TokenUser,
|
|
&UserId
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Do not refresh the cache
|
|
//
|
|
|
|
UserId = NULL;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Allocate virtual memory.
|
|
// Move the $EFS to virtual memory, LPC requires this.
|
|
//
|
|
|
|
if ( PsGetCurrentProcess() != EfsData.LsaProcess ){
|
|
|
|
ProcessNeedAttach = TRUE;
|
|
|
|
status = ObReferenceObjectByPointer(
|
|
EfsData.LsaProcess,
|
|
0,
|
|
NULL,
|
|
KernelMode);
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
KeStackAttachProcess (
|
|
EfsData.LsaProcess,
|
|
&ApcState
|
|
);
|
|
ProcessAttached = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
CrntProcess = NtCurrentProcess();
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
if (EfsContext->EfsStreamData){
|
|
regionSize = currentEfsStreamLength = * (ULONG*)(EfsContext->EfsStreamData);
|
|
|
|
status = ZwAllocateVirtualMemory(
|
|
CrntProcess,
|
|
(PVOID *) ¤tEfsStream,
|
|
0,
|
|
®ionSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) ){
|
|
|
|
BOOLEAN OldCopyOnOpen;
|
|
BOOLEAN OldEffectiveOnly;
|
|
SECURITY_IMPERSONATION_LEVEL OldImpersonationLevel;
|
|
PACCESS_TOKEN OldClientToken;
|
|
|
|
|
|
OldClientToken = PsReferenceImpersonationToken(
|
|
PsGetCurrentThread(),
|
|
&OldCopyOnOpen,
|
|
&OldEffectiveOnly,
|
|
&OldImpersonationLevel
|
|
);
|
|
|
|
if ( EfsContext->Status != (TURN_ON_ENCRYPTION_BIT | TURN_ON_BIT_ONLY) &&
|
|
( NULL != currentEfsStream) ){
|
|
|
|
RtlCopyMemory(
|
|
currentEfsStream,
|
|
EfsContext->EfsStreamData,
|
|
currentEfsStreamLength
|
|
);
|
|
|
|
//
|
|
// Free the memory first to increase chance of getting new memory
|
|
//
|
|
|
|
ExFreePool(EfsContext->EfsStreamData);
|
|
EfsContext->EfsStreamData = NULL;
|
|
}
|
|
|
|
//
|
|
// Detach process before calling user mode
|
|
//
|
|
|
|
if (ProcessAttached){
|
|
KeUnstackDetachProcess(&ApcState);
|
|
ProcessAttached = FALSE;
|
|
}
|
|
|
|
switch ( EfsContext->Status & ACTION_REQUIRED){
|
|
case VERIFY_USER_REQUIRED:
|
|
|
|
status = PsImpersonateClient(
|
|
PsGetCurrentThread(),
|
|
accessToken,
|
|
TRUE,
|
|
TRUE,
|
|
ImpersonationLevel
|
|
);
|
|
|
|
//
|
|
// Call service to verify the user
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
status = EfsDecryptFek(
|
|
&fek,
|
|
(PEFS_DATA_STREAM_HEADER) currentEfsStream,
|
|
currentEfsStreamLength,
|
|
OpenType,
|
|
&efsStream,
|
|
&bufferBase,
|
|
&bufferLength
|
|
);
|
|
|
|
if ( OldClientToken ) {
|
|
PsImpersonateClient(
|
|
PsGetCurrentThread(),
|
|
OldClientToken,
|
|
OldCopyOnOpen,
|
|
OldEffectiveOnly,
|
|
OldImpersonationLevel
|
|
);
|
|
PsDereferenceImpersonationToken(OldClientToken);
|
|
} else {
|
|
PsRevertToSelf( );
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Impersonation failed
|
|
//
|
|
|
|
if ( OldClientToken ) {
|
|
PsDereferenceImpersonationToken(OldClientToken);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NEW_FILE_EFS_REQUIRED:
|
|
//
|
|
// Call service to get new FEK, $EFS
|
|
//
|
|
|
|
if (EfsContext->Flags & SYSTEM_IS_READONLY) {
|
|
ASSERT(FALSE);
|
|
status = STATUS_MEDIA_WRITE_PROTECTED;
|
|
if ( OldClientToken ) {
|
|
PsDereferenceImpersonationToken(OldClientToken);
|
|
}
|
|
break;
|
|
}
|
|
|
|
status = PsImpersonateClient(
|
|
PsGetCurrentThread(),
|
|
accessToken,
|
|
TRUE,
|
|
TRUE,
|
|
ImpersonationLevel
|
|
);
|
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = EfsGenerateKey(
|
|
&fek,
|
|
&efsStream,
|
|
(PEFS_DATA_STREAM_HEADER) currentEfsStream,
|
|
currentEfsStreamLength,
|
|
&bufferBase,
|
|
&bufferLength
|
|
);
|
|
|
|
if ( OldClientToken ) {
|
|
PsImpersonateClient(
|
|
PsGetCurrentThread(),
|
|
OldClientToken,
|
|
OldCopyOnOpen,
|
|
OldEffectiveOnly,
|
|
OldImpersonationLevel
|
|
);
|
|
PsDereferenceImpersonationToken(OldClientToken);
|
|
} else {
|
|
PsRevertToSelf( );
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Impersonation failed
|
|
//
|
|
|
|
if ( OldClientToken ) {
|
|
PsDereferenceImpersonationToken(OldClientToken);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case NEW_DIR_EFS_REQUIRED:
|
|
//
|
|
// Call service to get new $EFS
|
|
//
|
|
|
|
if (EfsContext->Flags & SYSTEM_IS_READONLY) {
|
|
ASSERT(FALSE);
|
|
status = STATUS_MEDIA_WRITE_PROTECTED;
|
|
if ( OldClientToken ) {
|
|
PsDereferenceImpersonationToken(OldClientToken);
|
|
}
|
|
break;
|
|
}
|
|
|
|
status = PsImpersonateClient(
|
|
PsGetCurrentThread(),
|
|
accessToken,
|
|
TRUE,
|
|
TRUE,
|
|
ImpersonationLevel
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
status = GenerateDirEfs(
|
|
(PEFS_DATA_STREAM_HEADER) currentEfsStream,
|
|
currentEfsStreamLength,
|
|
&efsStream,
|
|
&bufferBase,
|
|
&bufferLength
|
|
);
|
|
|
|
if ( OldClientToken ) {
|
|
PsImpersonateClient(
|
|
PsGetCurrentThread(),
|
|
OldClientToken,
|
|
OldCopyOnOpen,
|
|
OldEffectiveOnly,
|
|
OldImpersonationLevel
|
|
);
|
|
PsDereferenceImpersonationToken(OldClientToken);
|
|
} else {
|
|
PsRevertToSelf( );
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Impersonation failed
|
|
//
|
|
|
|
if ( OldClientToken ) {
|
|
PsDereferenceImpersonationToken(OldClientToken);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case TURN_ON_BIT_ONLY:
|
|
//
|
|
// Fall through intended
|
|
//
|
|
|
|
default:
|
|
|
|
if ( OldClientToken ) {
|
|
PsDereferenceImpersonationToken(OldClientToken);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
if ( ProcessNeedAttach ){
|
|
|
|
KeStackAttachProcess (
|
|
EfsData.LsaProcess,
|
|
&ApcState
|
|
);
|
|
ProcessAttached = TRUE;
|
|
|
|
}
|
|
|
|
if (fek && (fek->Algorithm == CALG_3DES) && !EfsData.FipsFunctionTable.Fips3Des3Key ) {
|
|
|
|
//
|
|
// User requested 3des but fips is not available, quit.
|
|
//
|
|
|
|
|
|
if (bufferBase){
|
|
|
|
SIZE_T bufferSize;
|
|
|
|
bufferSize = bufferLength;
|
|
ZwFreeVirtualMemory(
|
|
CrntProcess,
|
|
&bufferBase,
|
|
&bufferSize,
|
|
MEM_RELEASE
|
|
);
|
|
|
|
}
|
|
status = STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) ){
|
|
|
|
KEVENT event;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
PIRP fsCtlIrp;
|
|
PIO_STACK_LOCATION fsCtlIrpSp;
|
|
ULONG inputDataLength;
|
|
ULONG actionType;
|
|
ULONG usingCurrentEfs;
|
|
ULONG FsCode;
|
|
PULONG pUlong;
|
|
|
|
//
|
|
// We got our FEK, $EFS. Set it with a FSCTL
|
|
// Prepare the input data buffer first
|
|
//
|
|
|
|
switch ( EfsContext->Status & ACTION_REQUIRED ){
|
|
case VERIFY_USER_REQUIRED:
|
|
|
|
EfsId = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof (GUID),
|
|
'msfE'
|
|
);
|
|
|
|
if ( EfsId ){
|
|
RtlCopyMemory(
|
|
EfsId,
|
|
&(((PEFS_DATA_STREAM_HEADER) currentEfsStream)->EfsId),
|
|
sizeof( GUID ) );
|
|
}
|
|
|
|
//
|
|
// Free memory first
|
|
//
|
|
ZwFreeVirtualMemory(
|
|
CrntProcess,
|
|
¤tEfsStream,
|
|
®ionSize,
|
|
MEM_RELEASE
|
|
);
|
|
|
|
//
|
|
// Prepare input data buffer
|
|
//
|
|
|
|
inputDataLength = EFS_FSCTL_HEADER_LENGTH + 2 * EFS_KEY_SIZE( fek );
|
|
|
|
actionType = SET_EFS_KEYBLOB;
|
|
|
|
if ( efsStream && !(EfsContext->Flags & SYSTEM_IS_READONLY)){
|
|
//
|
|
// $EFS updated
|
|
//
|
|
|
|
inputDataLength += *(ULONG *)efsStream;
|
|
actionType |= WRITE_EFS_ATTRIBUTE;
|
|
}
|
|
|
|
currentEfsStream = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
inputDataLength,
|
|
'msfE'
|
|
);
|
|
|
|
//
|
|
// Deal with out of memory here
|
|
//
|
|
if ( NULL == currentEfsStream ){
|
|
|
|
//
|
|
// Out of memory
|
|
//
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
|
|
}
|
|
|
|
pUlong = (ULONG *) currentEfsStream;
|
|
*pUlong = ((PFSCTL_INPUT)currentEfsStream)->CipherSubCode
|
|
= actionType;
|
|
|
|
((PFSCTL_INPUT)currentEfsStream)->EfsFsCode = EFS_SET_ATTRIBUTE;
|
|
|
|
RtlCopyMemory(
|
|
((PUCHAR) currentEfsStream) + EFS_FSCTL_HEADER_LENGTH,
|
|
fek,
|
|
EFS_KEY_SIZE( fek )
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
((PUCHAR) currentEfsStream) + EFS_FSCTL_HEADER_LENGTH
|
|
+ EFS_KEY_SIZE( fek ),
|
|
fek,
|
|
EFS_KEY_SIZE( fek )
|
|
);
|
|
|
|
if ( efsStream && !(EfsContext->Flags & SYSTEM_IS_READONLY)){
|
|
|
|
RtlCopyMemory(
|
|
((PUCHAR) currentEfsStream) + EFS_FSCTL_HEADER_LENGTH
|
|
+ 2 * EFS_KEY_SIZE( fek ),
|
|
efsStream,
|
|
*(ULONG *)efsStream
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Encrypt our Input data
|
|
//
|
|
EfsEncryptKeyFsData(
|
|
currentEfsStream,
|
|
inputDataLength,
|
|
sizeof(ULONG),
|
|
EFS_FSCTL_HEADER_LENGTH + EFS_KEY_SIZE( fek ),
|
|
EFS_KEY_SIZE( fek )
|
|
);
|
|
|
|
//
|
|
// Let's clear the plain FEK
|
|
//
|
|
|
|
RtlSecureZeroMemory(EFS_KEY_DATA(fek), fek->KeyLength);
|
|
|
|
break;
|
|
|
|
case NEW_FILE_EFS_REQUIRED:
|
|
|
|
EfsId = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof (GUID),
|
|
'msfE'
|
|
);
|
|
|
|
if ( EfsId ){
|
|
RtlCopyMemory(
|
|
EfsId,
|
|
&(efsStream->EfsId),
|
|
sizeof( GUID ) );
|
|
}
|
|
|
|
//
|
|
// Free memory first
|
|
//
|
|
|
|
if ( currentEfsStream ){
|
|
ZwFreeVirtualMemory(
|
|
CrntProcess,
|
|
¤tEfsStream,
|
|
®ionSize,
|
|
MEM_RELEASE
|
|
);
|
|
}
|
|
|
|
//
|
|
// Prepare input data buffer
|
|
//
|
|
|
|
inputDataLength = EFS_FSCTL_HEADER_LENGTH
|
|
+ 2 * EFS_KEY_SIZE( fek )
|
|
+ *(ULONG *)efsStream;
|
|
|
|
currentEfsStream = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
inputDataLength,
|
|
'msfE'
|
|
);
|
|
|
|
//
|
|
// Deal with out of memory here
|
|
//
|
|
if ( NULL == currentEfsStream ){
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
|
|
}
|
|
|
|
pUlong = (ULONG *) currentEfsStream;
|
|
*pUlong = ((PFSCTL_INPUT)currentEfsStream)->CipherSubCode
|
|
= WRITE_EFS_ATTRIBUTE | SET_EFS_KEYBLOB;
|
|
|
|
((PFSCTL_INPUT)currentEfsStream)->EfsFsCode = EFS_SET_ATTRIBUTE;
|
|
|
|
RtlCopyMemory(
|
|
((PUCHAR) currentEfsStream) + EFS_FSCTL_HEADER_LENGTH,
|
|
fek,
|
|
EFS_KEY_SIZE( fek )
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
((PUCHAR) currentEfsStream) + EFS_FSCTL_HEADER_LENGTH
|
|
+ EFS_KEY_SIZE( fek ),
|
|
fek,
|
|
EFS_KEY_SIZE( fek )
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
((PUCHAR) currentEfsStream) + EFS_FSCTL_HEADER_LENGTH
|
|
+ 2 * EFS_KEY_SIZE( fek ) ,
|
|
efsStream,
|
|
*(ULONG *)efsStream
|
|
);
|
|
|
|
//
|
|
// Encrypt our Input data
|
|
//
|
|
EfsEncryptKeyFsData(
|
|
currentEfsStream,
|
|
inputDataLength,
|
|
sizeof(ULONG),
|
|
EFS_FSCTL_HEADER_LENGTH + EFS_KEY_SIZE( fek ),
|
|
EFS_KEY_SIZE( fek )
|
|
);
|
|
|
|
//
|
|
// Let's clear the plain FEK
|
|
//
|
|
|
|
RtlSecureZeroMemory(EFS_KEY_DATA(fek), fek->KeyLength);
|
|
|
|
break;
|
|
|
|
case NEW_DIR_EFS_REQUIRED:
|
|
//
|
|
// Prepare input data buffer
|
|
//
|
|
|
|
inputDataLength = EFS_FSCTL_HEADER_LENGTH
|
|
+ 2 * ( sizeof( EFS_KEY ) + DES_KEYSIZE );
|
|
|
|
if ( NULL == efsStream ){
|
|
|
|
//
|
|
// New directory will inherit the parent $EFS
|
|
//
|
|
|
|
usingCurrentEfs = TRUE;
|
|
inputDataLength += currentEfsStreamLength;
|
|
efsStream = currentEfsStream;
|
|
|
|
} else {
|
|
|
|
//
|
|
// New $EFS generated. Not in ver 1.0
|
|
//
|
|
|
|
usingCurrentEfs = FALSE;
|
|
inputDataLength += *(ULONG *)efsStream;
|
|
|
|
//
|
|
// Free memory first
|
|
//
|
|
|
|
if (currentEfsStream){
|
|
ZwFreeVirtualMemory(
|
|
CrntProcess,
|
|
¤tEfsStream,
|
|
®ionSize,
|
|
MEM_RELEASE
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
currentEfsStream = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
inputDataLength,
|
|
'msfE'
|
|
);
|
|
|
|
//
|
|
// Deal with out of memory here
|
|
//
|
|
if ( NULL == currentEfsStream ){
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
|
|
}
|
|
|
|
pUlong = (ULONG *) currentEfsStream;
|
|
*pUlong = ((PFSCTL_INPUT)currentEfsStream)->CipherSubCode
|
|
= WRITE_EFS_ATTRIBUTE;
|
|
|
|
((PFSCTL_INPUT)currentEfsStream)->EfsFsCode = EFS_SET_ATTRIBUTE;
|
|
|
|
//
|
|
// Make up an false FEK with session key
|
|
//
|
|
|
|
((PEFS_KEY)&(((PFSCTL_INPUT)currentEfsStream)->EfsFsData[0]))->KeyLength
|
|
= DES_KEYSIZE;
|
|
|
|
((PEFS_KEY)&(((PFSCTL_INPUT)currentEfsStream)->EfsFsData[0]))->Algorithm
|
|
= CALG_DES;
|
|
|
|
RtlCopyMemory(
|
|
((PUCHAR) currentEfsStream) + EFS_FSCTL_HEADER_LENGTH + sizeof ( EFS_KEY ),
|
|
&(EfsData.SessionKey),
|
|
DES_KEYSIZE
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
((PUCHAR) currentEfsStream) + EFS_FSCTL_HEADER_LENGTH
|
|
+ DES_KEYSIZE + sizeof ( EFS_KEY ) ,
|
|
((PUCHAR) currentEfsStream) + EFS_FSCTL_HEADER_LENGTH,
|
|
DES_KEYSIZE + sizeof ( EFS_KEY )
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
((PUCHAR) currentEfsStream) + EFS_FSCTL_HEADER_LENGTH
|
|
+ 2 * ( sizeof ( EFS_KEY ) + DES_KEYSIZE) ,
|
|
efsStream,
|
|
*(ULONG *)efsStream
|
|
);
|
|
|
|
if ( usingCurrentEfs && efsStream ) {
|
|
|
|
//
|
|
// Free memory
|
|
//
|
|
|
|
ZwFreeVirtualMemory(
|
|
CrntProcess,
|
|
&efsStream,
|
|
®ionSize,
|
|
MEM_RELEASE
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Encrypt our Input data
|
|
//
|
|
EfsEncryptKeyFsData(
|
|
currentEfsStream,
|
|
inputDataLength,
|
|
sizeof(ULONG),
|
|
EFS_FSCTL_HEADER_LENGTH + DES_KEYSIZE + sizeof ( EFS_KEY ),
|
|
DES_KEYSIZE + sizeof ( EFS_KEY )
|
|
);
|
|
|
|
break;
|
|
|
|
case TURN_ON_BIT_ONLY:
|
|
|
|
//
|
|
// Prepare input data buffer
|
|
//
|
|
|
|
inputDataLength = EFS_FSCTL_HEADER_LENGTH
|
|
+ 2 * ( sizeof( EFS_KEY ) + DES_KEYSIZE );
|
|
|
|
currentEfsStream = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
inputDataLength,
|
|
'msfE'
|
|
);
|
|
|
|
//
|
|
// Deal with out of memory here
|
|
//
|
|
if ( NULL == currentEfsStream ){
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
|
|
}
|
|
|
|
((PFSCTL_INPUT)currentEfsStream)->CipherSubCode = 0;
|
|
((PFSCTL_INPUT)currentEfsStream)->EfsFsCode = EFS_SET_ATTRIBUTE;
|
|
|
|
//
|
|
// Make up an false FEK with session key
|
|
//
|
|
|
|
((PEFS_KEY)&(((PFSCTL_INPUT)currentEfsStream)->EfsFsData[0]))->KeyLength
|
|
= DES_KEYSIZE;
|
|
|
|
((PEFS_KEY)&(((PFSCTL_INPUT)currentEfsStream)->EfsFsData[0]))->Algorithm
|
|
= CALG_DES;
|
|
|
|
RtlCopyMemory(
|
|
((PUCHAR) currentEfsStream) + EFS_FSCTL_HEADER_LENGTH + sizeof ( EFS_KEY ),
|
|
&(EfsData.SessionKey),
|
|
DES_KEYSIZE
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
((PUCHAR) currentEfsStream) + EFS_FSCTL_HEADER_LENGTH
|
|
+ DES_KEYSIZE + sizeof ( EFS_KEY ) ,
|
|
((PUCHAR) currentEfsStream) + EFS_FSCTL_HEADER_LENGTH,
|
|
DES_KEYSIZE + sizeof ( EFS_KEY )
|
|
);
|
|
|
|
//
|
|
// Encrypt our Input data
|
|
//
|
|
EfsEncryptKeyFsData(
|
|
currentEfsStream,
|
|
inputDataLength,
|
|
sizeof(ULONG),
|
|
EFS_FSCTL_HEADER_LENGTH + DES_KEYSIZE + sizeof ( EFS_KEY ),
|
|
DES_KEYSIZE + sizeof ( EFS_KEY )
|
|
);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Free the memory from the EFS server
|
|
//
|
|
|
|
if (bufferBase){
|
|
|
|
SIZE_T bufferSize;
|
|
|
|
bufferSize = bufferLength;
|
|
ZwFreeVirtualMemory(
|
|
CrntProcess,
|
|
&bufferBase,
|
|
&bufferSize,
|
|
MEM_RELEASE
|
|
);
|
|
|
|
}
|
|
|
|
if (ProcessAttached){
|
|
KeUnstackDetachProcess(&ApcState);
|
|
ObDereferenceObject(EfsData.LsaProcess);
|
|
ProcessAttached = FALSE;
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) ){
|
|
|
|
|
|
//
|
|
// Prepare a FSCTL IRP
|
|
//
|
|
KeInitializeEvent( &event, SynchronizationEvent, FALSE);
|
|
|
|
if ( EfsContext->Status & TURN_ON_ENCRYPTION_BIT ) {
|
|
FsCode = FSCTL_SET_ENCRYPTION;
|
|
*(ULONG *) currentEfsStream = EFS_ENCRYPT_STREAM;
|
|
} else {
|
|
FsCode = FSCTL_ENCRYPTION_FSCTL_IO;
|
|
}
|
|
|
|
fsCtlIrp = IoBuildDeviceIoControlRequest( FsCode,
|
|
DeviceObject,
|
|
currentEfsStream,
|
|
inputDataLength,
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
&event,
|
|
&ioStatus
|
|
);
|
|
if ( fsCtlIrp ) {
|
|
|
|
fsCtlIrpSp = IoGetNextIrpStackLocation( fsCtlIrp );
|
|
fsCtlIrpSp->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
|
|
fsCtlIrpSp->MinorFunction = IRP_MN_USER_FS_REQUEST;
|
|
fsCtlIrpSp->FileObject = irpSp->FileObject;
|
|
|
|
status = IoCallDriver( DeviceObject, fsCtlIrp);
|
|
if (status == STATUS_PENDING) {
|
|
|
|
status = KeWaitForSingleObject( &event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL );
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ){
|
|
//
|
|
// Write EFS and set Key Blob failed. Failed the create
|
|
//
|
|
|
|
status = STATUS_ACCESS_DENIED;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Refresh the cache
|
|
//
|
|
|
|
if ( EfsId ){
|
|
if ( !accessToken ){
|
|
|
|
if ( irpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.ClientToken ){
|
|
accessToken = irpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.ClientToken;
|
|
} else {
|
|
accessToken = irpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.PrimaryToken;
|
|
}
|
|
|
|
if ( accessToken ){
|
|
|
|
//
|
|
// Get User ID
|
|
//
|
|
|
|
status = SeQueryInformationToken(
|
|
accessToken,
|
|
TokenUser,
|
|
&UserId
|
|
);
|
|
}
|
|
}
|
|
|
|
if (UserId && NT_SUCCESS(status)){
|
|
|
|
status = EfsRefreshCache(
|
|
EfsId,
|
|
UserId
|
|
);
|
|
|
|
if (NT_SUCCESS(status)){
|
|
|
|
//
|
|
// Cache set successfully.
|
|
// UserId should not be deleted in this routine.
|
|
//
|
|
|
|
UserId = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cache should not affect the normal operations
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// Failed allocate IRP
|
|
//
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
ExFreePool( currentEfsStream );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Failed allocating memory for currentEfsStream
|
|
// Use the status returned.
|
|
//
|
|
|
|
}
|
|
} else {
|
|
//
|
|
// Failed on calling EFS server.
|
|
// Because of the down level support, we cannot return the new error status code.
|
|
//
|
|
|
|
status = STATUS_ACCESS_DENIED;
|
|
|
|
ZwFreeVirtualMemory(
|
|
CrntProcess,
|
|
¤tEfsStream,
|
|
®ionSize,
|
|
MEM_RELEASE
|
|
);
|
|
|
|
if (ProcessAttached){
|
|
KeUnstackDetachProcess(&ApcState);
|
|
ObDereferenceObject(EfsData.LsaProcess);
|
|
ProcessAttached = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Allocate virtual memory failed. Use the status returned.
|
|
//
|
|
|
|
if (ProcessAttached){
|
|
KeUnstackDetachProcess(&ApcState);
|
|
ObDereferenceObject(EfsData.LsaProcess);
|
|
ProcessAttached = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
if ( UserId ){
|
|
|
|
ExFreePool( UserId );
|
|
|
|
}
|
|
|
|
if ( EfsId ){
|
|
|
|
ExFreePool( EfsId );
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|