/* * @implemented */ VOID NTAPI SeImpersonateClient(IN PSECURITY_CLIENT_CONTEXT ClientContext, IN PETHREAD ServerThread OPTIONAL) { PAGED_CODE(); SeImpersonateClientEx(ClientContext, ServerThread); }
NTSTATUS NTAPI CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes, IN PSECURITY_CLIENT_CONTEXT ImpersonationContext, IN OUT PBOOLEAN Allocate, OUT PCMHIVE *NewHive, IN ULONG CheckFlags) { PUNICODE_STRING FileName; NTSTATUS Status; PAGED_CODE(); /* Open the file in the current security context */ FileName = FileAttributes->ObjectName; Status = CmpInitHiveFromFile(FileName, 0, NewHive, Allocate, CheckFlags); if (((Status == STATUS_ACCESS_DENIED) || (Status == STATUS_NO_SUCH_USER) || (Status == STATUS_WRONG_PASSWORD) || (Status == STATUS_ACCOUNT_EXPIRED) || (Status == STATUS_ACCOUNT_DISABLED) || (Status == STATUS_ACCOUNT_RESTRICTION)) && (ImpersonationContext)) { /* We failed due to an account/security error, impersonate SYSTEM */ Status = SeImpersonateClientEx(ImpersonationContext, NULL); if (NT_SUCCESS(Status)) { /* Now try again */ Status = CmpInitHiveFromFile(FileName, 0, NewHive, Allocate, CheckFlags); /* Restore impersonation token */ PsRevertToSelf(); } } /* Return status of open attempt */ return Status; }
NTSTATUS NTAPI NpImpersonateClientContext(IN PNP_CCB Ccb) { NTSTATUS Status; PSECURITY_CLIENT_CONTEXT ClientContext; PAGED_CODE(); ClientContext = Ccb->ClientContext; if (ClientContext) { Status = SeImpersonateClientEx(ClientContext, NULL); } else { Status = STATUS_CANNOT_IMPERSONATE; } return Status; }
NTSTATUS NtImpersonateThread( __in HANDLE ServerThreadHandle, __in HANDLE ClientThreadHandle, __in PSECURITY_QUALITY_OF_SERVICE SecurityQos ) /*++ Routine Description: This routine is used to cause the server thread to impersonate the client thread. The impersonation is done according to the specified quality of service parameters. Arguments: ServerThreadHandle - Is a handle to the server thread (the impersonator, or doing the impersonation). This handle must be open for THREAD_IMPERSONATE access. ClientThreadHandle - Is a handle to the Client thread (the impersonatee, or one being impersonated). This handle must be open for THREAD_DIRECT_IMPERSONATION access. SecurityQos - A pointer to security quality of service information indicating what form of impersonation is to be performed. Return Value: STATUS_SUCCESS - Indicates the call completed successfully. --*/ { KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PETHREAD ClientThread, ServerThread; SECURITY_QUALITY_OF_SERVICE CapturedQos; SECURITY_CLIENT_CONTEXT ClientSecurityContext; // // Get previous processor mode and probe and capture arguments if necessary // PreviousMode = KeGetPreviousMode(); try { if (PreviousMode != KernelMode) { ProbeForReadSmallStructure (SecurityQos, sizeof (SECURITY_QUALITY_OF_SERVICE), sizeof (ULONG)); } CapturedQos = *SecurityQos; } except (ExSystemExceptionFilter ()) { return GetExceptionCode (); } // // Reference the client thread, checking for appropriate access. // Status = ObReferenceObjectByHandle (ClientThreadHandle, // Handle THREAD_DIRECT_IMPERSONATION, // DesiredAccess PsThreadType, // ObjectType PreviousMode, // AccessMode &ClientThread, // Object NULL); // GrantedAccess if (!NT_SUCCESS (Status)) { return Status; } // // Reference the client thread, checking for appropriate access. // Status = ObReferenceObjectByHandle (ServerThreadHandle, // Handle THREAD_IMPERSONATE, // DesiredAccess PsThreadType, // ObjectType PreviousMode, // AccessMode &ServerThread, // Object NULL); // GrantedAccess if (!NT_SUCCESS (Status)) { ObDereferenceObject (ClientThread); return Status; } // // Get the client's security context // Status = SeCreateClientSecurity (ClientThread, // ClientThread &CapturedQos, // SecurityQos FALSE, // ServerIsRemote &ClientSecurityContext); // ClientContext if (!NT_SUCCESS (Status)) { ObDereferenceObject (ServerThread); ObDereferenceObject (ClientThread); return Status; } // // Impersonate the client // Status = SeImpersonateClientEx (&ClientSecurityContext, ServerThread); SeDeleteClientSecurity (&ClientSecurityContext); // // Done. // ObDereferenceObject (ServerThread); ObDereferenceObject (ClientThread); return Status ; }
static VOID IoThreadProc (PVOID threadArg) { EncryptedIoQueue *queue = (EncryptedIoQueue *) threadArg; PLIST_ENTRY listEntry; EncryptedIoRequest *request; KeSetPriorityThread (KeGetCurrentThread(), LOW_REALTIME_PRIORITY); if (!queue->IsFilterDevice && queue->SecurityClientContext) { #ifdef DEBUG NTSTATUS status = #endif SeImpersonateClientEx (queue->SecurityClientContext, NULL); ASSERT (NT_SUCCESS (status)); } while (!queue->ThreadExitRequested) { if (!NT_SUCCESS (KeWaitForSingleObject (&queue->IoThreadQueueNotEmptyEvent, Executive, KernelMode, FALSE, NULL))) continue; if (queue->ThreadExitRequested) break; while ((listEntry = ExInterlockedRemoveHeadList (&queue->IoThreadQueue, &queue->IoThreadQueueLock))) { InterlockedDecrement (&queue->IoThreadPendingRequestCount); request = CONTAINING_RECORD (listEntry, EncryptedIoRequest, ListEntry); #ifdef TC_TRACE_IO_QUEUE Dump ("%c %I64d [%I64d] roff=%I64d rlen=%d\n", request->Item->Write ? 'W' : 'R', request->Item->OriginalIrpOffset.QuadPart, GetElapsedTime (&queue->LastPerformanceCounter), request->Offset.QuadPart, request->Length); #endif // Perform IO request if no preceding request of the item failed if (NT_SUCCESS (request->Item->Status)) { if (queue->IsFilterDevice) { if (queue->RemapEncryptedArea && request->EncryptedLength > 0) { if (request->EncryptedLength != request->Length) { // Up to three subfragments may be required to handle a partially remapped fragment int subFragment; byte *subFragmentData = request->Data; for (subFragment = 0 ; subFragment < 3; ++subFragment) { LARGE_INTEGER subFragmentOffset; ULONG subFragmentLength; subFragmentOffset.QuadPart = request->Offset.QuadPart; switch (subFragment) { case 0: subFragmentLength = (ULONG) request->EncryptedOffset; break; case 1: subFragmentOffset.QuadPart += request->EncryptedOffset + queue->RemappedAreaOffset; subFragmentLength = request->EncryptedLength; break; case 2: subFragmentOffset.QuadPart += request->EncryptedOffset + request->EncryptedLength; subFragmentLength = (ULONG) (request->Length - (request->EncryptedOffset + request->EncryptedLength)); break; } if (subFragmentLength > 0) { if (request->Item->Write) request->Item->Status = TCWriteDevice (queue->LowerDeviceObject, subFragmentData, subFragmentOffset, subFragmentLength); else request->Item->Status = TCCachedRead (queue, NULL, subFragmentData, subFragmentOffset, subFragmentLength); subFragmentData += subFragmentLength; } } } else { // Remap the fragment LARGE_INTEGER remappedOffset; remappedOffset.QuadPart = request->Offset.QuadPart + queue->RemappedAreaOffset; if (request->Item->Write) request->Item->Status = TCWriteDevice (queue->LowerDeviceObject, request->Data, remappedOffset, request->Length); else request->Item->Status = TCCachedRead (queue, NULL, request->Data, remappedOffset, request->Length); } } else { if (request->Item->Write) request->Item->Status = TCWriteDevice (queue->LowerDeviceObject, request->Data, request->Offset, request->Length); else request->Item->Status = TCCachedRead (queue, NULL, request->Data, request->Offset, request->Length); } } else { IO_STATUS_BLOCK ioStatus; if (request->Item->Write) request->Item->Status = ZwWriteFile (queue->HostFileHandle, NULL, NULL, NULL, &ioStatus, request->Data, request->Length, &request->Offset, NULL); else request->Item->Status = TCCachedRead (queue, &ioStatus, request->Data, request->Offset, request->Length); if (NT_SUCCESS (request->Item->Status) && ioStatus.Information != request->Length) request->Item->Status = STATUS_END_OF_FILE; } } if (request->Item->Write) { queue->ReadAheadBufferValid = FALSE; ReleaseFragmentBuffer (queue, request->Data); if (request->CompleteOriginalIrp) { CompleteOriginalIrp (request->Item, request->Item->Status, NT_SUCCESS (request->Item->Status) ? request->Item->OriginalLength : 0); } ReleasePoolBuffer (queue, request); } else { BOOL readAhead = FALSE; if (NT_SUCCESS (request->Item->Status)) memcpy (request->OrigDataBufferFragment, request->Data, request->Length); ReleaseFragmentBuffer (queue, request->Data); request->Data = request->OrigDataBufferFragment; if (request->CompleteOriginalIrp && queue->LastReadLength > 0 && NT_SUCCESS (request->Item->Status) && InterlockedExchangeAdd (&queue->IoThreadPendingRequestCount, 0) == 0) { readAhead = TRUE; InterlockedIncrement (&queue->OutstandingIoCount); } ExInterlockedInsertTailList (&queue->CompletionThreadQueue, &request->CompletionListEntry, &queue->CompletionThreadQueueLock); KeSetEvent (&queue->CompletionThreadQueueNotEmptyEvent, IO_DISK_INCREMENT, FALSE); if (readAhead) { queue->ReadAheadBufferValid = FALSE; queue->ReadAheadOffset.QuadPart = queue->LastReadOffset.QuadPart + queue->LastReadLength; queue->ReadAheadLength = queue->LastReadLength; if (queue->ReadAheadOffset.QuadPart + queue->ReadAheadLength <= queue->MaxReadAheadOffset.QuadPart) { #ifdef TC_TRACE_IO_QUEUE Dump ("A %I64d [%I64d] roff=%I64d rlen=%d\n", request->Item->OriginalIrpOffset.QuadPart, GetElapsedTime (&queue->LastPerformanceCounter), queue->ReadAheadOffset, queue->ReadAheadLength); #endif if (queue->IsFilterDevice) { queue->ReadAheadBufferValid = NT_SUCCESS (TCReadDevice (queue->LowerDeviceObject, queue->ReadAheadBuffer, queue->ReadAheadOffset, queue->ReadAheadLength)); } else { IO_STATUS_BLOCK ioStatus; queue->ReadAheadBufferValid = NT_SUCCESS (ZwReadFile (queue->HostFileHandle, NULL, NULL, NULL, &ioStatus, queue->ReadAheadBuffer, queue->ReadAheadLength, &queue->ReadAheadOffset, NULL)); queue->ReadAheadLength = (ULONG) ioStatus.Information; } } DecrementOutstandingIoCount (queue); } } } } PsTerminateSystemThread (STATUS_SUCCESS); }
NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, MOUNT_STRUCT *mount, PWSTR pwszMountVolume, BOOL bRawDevice) { FILE_STANDARD_INFORMATION FileStandardInfo; FILE_BASIC_INFORMATION FileBasicInfo; OBJECT_ATTRIBUTES oaFileAttributes; UNICODE_STRING FullFileName; IO_STATUS_BLOCK IoStatusBlock; PCRYPTO_INFO cryptoInfoPtr = NULL; PCRYPTO_INFO tmpCryptoInfo = NULL; LARGE_INTEGER lDiskLength; __int64 partitionStartingOffset = 0; int volumeType; char *readBuffer = 0; NTSTATUS ntStatus = 0; BOOL forceAccessCheck = (!bRawDevice && !(OsMajorVersion == 5 &&OsMinorVersion == 0)); // Windows 2000 does not support OBJ_FORCE_ACCESS_CHECK attribute BOOL disableBuffering = TRUE; BOOL exclusiveAccess = mount->bExclusiveAccess; Extension->pfoDeviceFile = NULL; Extension->hDeviceFile = NULL; Extension->bTimeStampValid = FALSE; RtlInitUnicodeString (&FullFileName, pwszMountVolume); InitializeObjectAttributes (&oaFileAttributes, &FullFileName, OBJ_CASE_INSENSITIVE | (forceAccessCheck ? OBJ_FORCE_ACCESS_CHECK : 0) | OBJ_KERNEL_HANDLE, NULL, NULL); KeInitializeEvent (&Extension->keVolumeEvent, NotificationEvent, FALSE); if (Extension->SecurityClientContextValid) { ntStatus = SeImpersonateClientEx (&Extension->SecurityClientContext, NULL); if (!NT_SUCCESS (ntStatus)) goto error; } mount->VolumeMountedReadOnlyAfterDeviceWriteProtected = FALSE; // If we are opening a device, query its size first if (bRawDevice) { PARTITION_INFORMATION pi; PARTITION_INFORMATION_EX pix; LARGE_INTEGER diskLengthInfo; DISK_GEOMETRY dg; STORAGE_PROPERTY_QUERY storagePropertyQuery = {0}; STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR storageDescriptor = {0}; ntStatus = IoGetDeviceObjectPointer (&FullFileName, FILE_READ_DATA | FILE_READ_ATTRIBUTES, &Extension->pfoDeviceFile, &Extension->pFsdDevice); if (!NT_SUCCESS (ntStatus)) goto error; ntStatus = TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_DRIVE_GEOMETRY, (char *) &dg, sizeof (dg)); if (!NT_SUCCESS (ntStatus)) goto error; lDiskLength.QuadPart = dg.Cylinders.QuadPart * dg.SectorsPerTrack * dg.TracksPerCylinder * dg.BytesPerSector; Extension->HostBytesPerSector = dg.BytesPerSector; storagePropertyQuery.PropertyId = StorageAccessAlignmentProperty; storagePropertyQuery.QueryType = PropertyStandardQuery; /* IOCTL_STORAGE_QUERY_PROPERTY supported only on Vista and above */ if (NT_SUCCESS (TCSendHostDeviceIoControlRequestEx (DeviceObject, Extension, IOCTL_STORAGE_QUERY_PROPERTY, (char*) &storagePropertyQuery, sizeof(storagePropertyQuery), (char *) &storageDescriptor, sizeof (storageDescriptor)))) { Extension->HostBytesPerPhysicalSector = storageDescriptor.BytesPerPhysicalSector; } else { Extension->HostBytesPerPhysicalSector = dg.BytesPerSector; } // Drive geometry is used only when IOCTL_DISK_GET_PARTITION_INFO fails if (NT_SUCCESS (TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_PARTITION_INFO_EX, (char *) &pix, sizeof (pix)))) { lDiskLength.QuadPart = pix.PartitionLength.QuadPart; partitionStartingOffset = pix.StartingOffset.QuadPart; } // Windows 2000 does not support IOCTL_DISK_GET_PARTITION_INFO_EX else if (NT_SUCCESS (TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_PARTITION_INFO, (char *) &pi, sizeof (pi)))) { lDiskLength.QuadPart = pi.PartitionLength.QuadPart; partitionStartingOffset = pi.StartingOffset.QuadPart; } else if (NT_SUCCESS (TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_LENGTH_INFO, &diskLengthInfo, sizeof (diskLengthInfo)))) { lDiskLength = diskLengthInfo; } ProbingHostDeviceForWrite = TRUE; if (!mount->bMountReadOnly && TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IsHiddenSystemRunning() ? TC_IOCTL_DISK_IS_WRITABLE : IOCTL_DISK_IS_WRITABLE, NULL, 0) == STATUS_MEDIA_WRITE_PROTECTED) { mount->bMountReadOnly = TRUE; DeviceObject->Characteristics |= FILE_READ_ONLY_DEVICE; mount->VolumeMountedReadOnlyAfterDeviceWriteProtected = TRUE; } ProbingHostDeviceForWrite = FALSE; // Some Windows tools (e.g. diskmgmt, diskpart, vssadmin) fail or experience timeouts when there is a raw device // open for exclusive access. Therefore, exclusive access is used only for file-hosted volumes. // Applications requiring a consistent device image need to acquire exclusive write access first. This is prevented // when a device-hosted volume is mounted. exclusiveAccess = FALSE; } else { // Limit the maximum required buffer size if (mount->BytesPerSector > 128 * BYTES_PER_KB) { ntStatus = STATUS_INVALID_PARAMETER; goto error; } Extension->HostBytesPerSector = mount->BytesPerSector; Extension->HostBytesPerPhysicalSector = mount->BytesPerPhysicalSector; if (Extension->HostBytesPerSector != TC_SECTOR_SIZE_FILE_HOSTED_VOLUME) disableBuffering = FALSE; } // Open the volume hosting file/device if (!mount->bMountReadOnly) { ntStatus = ZwCreateFile (&Extension->hDeviceFile, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &oaFileAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM, exclusiveAccess ? 0 : FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_RANDOM_ACCESS | FILE_WRITE_THROUGH | (disableBuffering ? FILE_NO_INTERMEDIATE_BUFFERING : 0) | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); } /* 26-4-99 NT for some partitions returns this code, it is really a access denied */ if (ntStatus == 0xc000001b) ntStatus = STATUS_ACCESS_DENIED; mount->VolumeMountedReadOnlyAfterAccessDenied = FALSE; if (mount->bMountReadOnly || ntStatus == STATUS_ACCESS_DENIED) { ntStatus = ZwCreateFile (&Extension->hDeviceFile, GENERIC_READ | SYNCHRONIZE, &oaFileAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM, exclusiveAccess ? FILE_SHARE_READ : FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_RANDOM_ACCESS | FILE_WRITE_THROUGH | (disableBuffering ? FILE_NO_INTERMEDIATE_BUFFERING : 0) | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (NT_SUCCESS (ntStatus) && !mount->bMountReadOnly) mount->VolumeMountedReadOnlyAfterAccessDenied = TRUE; Extension->bReadOnly = TRUE; DeviceObject->Characteristics |= FILE_READ_ONLY_DEVICE; } else Extension->bReadOnly = FALSE; /* 26-4-99 NT for some partitions returns this code, it is really a access denied */ if (ntStatus == 0xc000001b) { /* Partitions which return this code can still be opened with FILE_SHARE_READ but this causes NT problems elsewhere in particular if you do FILE_SHARE_READ NT will die later if anyone even tries to open the partition (or file for that matter...) */ ntStatus = STATUS_SHARING_VIOLATION; } if (!NT_SUCCESS (ntStatus)) { goto error; } // If we have opened a file, query its size now if (bRawDevice == FALSE) { ntStatus = ZwQueryInformationFile (Extension->hDeviceFile, &IoStatusBlock, &FileBasicInfo, sizeof (FileBasicInfo), FileBasicInformation); if (NT_SUCCESS (ntStatus)) { if (mount->bPreserveTimestamp) { Extension->fileCreationTime = FileBasicInfo.CreationTime; Extension->fileLastAccessTime = FileBasicInfo.LastAccessTime; Extension->fileLastWriteTime = FileBasicInfo.LastWriteTime; Extension->fileLastChangeTime = FileBasicInfo.ChangeTime; Extension->bTimeStampValid = TRUE; } ntStatus = ZwQueryInformationFile (Extension->hDeviceFile, &IoStatusBlock, &FileStandardInfo, sizeof (FileStandardInfo), FileStandardInformation); } if (!NT_SUCCESS (ntStatus)) { Dump ("ZwQueryInformationFile failed while opening file: NTSTATUS 0x%08x\n", ntStatus); goto error; } lDiskLength.QuadPart = FileStandardInfo.EndOfFile.QuadPart; if (FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_COMPRESSED) { Dump ("File \"%ls\" is marked as compressed - not supported!\n", pwszMountVolume); mount->nReturnCode = ERR_COMPRESSION_NOT_SUPPORTED; ntStatus = STATUS_SUCCESS; goto error; } ntStatus = ObReferenceObjectByHandle (Extension->hDeviceFile, FILE_ALL_ACCESS, *IoFileObjectType, KernelMode, &Extension->pfoDeviceFile, 0); if (!NT_SUCCESS (ntStatus)) { goto error; } /* Get the FSD device for the file (probably either NTFS or FAT) */ Extension->pFsdDevice = IoGetRelatedDeviceObject (Extension->pfoDeviceFile); } else { // Try to gain "raw" access to the partition in case there is a live filesystem on it (otherwise, // the NTFS driver guards hidden sectors and prevents mounting using a backup header e.g. after the user // accidentally quick-formats a dismounted partition-hosted TrueCrypt volume as NTFS). PFILE_OBJECT pfoTmpDeviceFile = NULL; if (NT_SUCCESS (ObReferenceObjectByHandle (Extension->hDeviceFile, FILE_ALL_ACCESS, *IoFileObjectType, KernelMode, &pfoTmpDeviceFile, NULL)) && pfoTmpDeviceFile != NULL) { TCFsctlCall (pfoTmpDeviceFile, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0); ObDereferenceObject (pfoTmpDeviceFile); } } // Check volume size if (lDiskLength.QuadPart < TC_MIN_VOLUME_SIZE_LEGACY || lDiskLength.QuadPart > TC_MAX_VOLUME_SIZE) { mount->nReturnCode = ERR_VOL_SIZE_WRONG; ntStatus = STATUS_SUCCESS; goto error; } Extension->DiskLength = lDiskLength.QuadPart; Extension->HostLength = lDiskLength.QuadPart; readBuffer = TCalloc (max (max (TC_VOLUME_HEADER_EFFECTIVE_SIZE, PAGE_SIZE), Extension->HostBytesPerSector)); if (readBuffer == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; goto error; } // Go through all volume types (e.g., normal, hidden) for (volumeType = TC_VOLUME_TYPE_NORMAL; volumeType < TC_VOLUME_TYPE_COUNT; volumeType++) { Dump ("Trying to open volume type %d\n", volumeType); /* Read the volume header */ if (!mount->bPartitionInInactiveSysEncScope || (mount->bPartitionInInactiveSysEncScope && volumeType == TC_VOLUME_TYPE_HIDDEN)) { // Header of a volume that is not within the scope of system encryption, or // header of a system hidden volume (containing a hidden OS) LARGE_INTEGER headerOffset; if (mount->UseBackupHeader && lDiskLength.QuadPart <= TC_TOTAL_VOLUME_HEADERS_SIZE) continue; switch (volumeType) { case TC_VOLUME_TYPE_NORMAL: headerOffset.QuadPart = mount->UseBackupHeader ? lDiskLength.QuadPart - TC_VOLUME_HEADER_GROUP_SIZE : TC_VOLUME_HEADER_OFFSET; break; case TC_VOLUME_TYPE_HIDDEN: if (lDiskLength.QuadPart <= TC_VOLUME_HEADER_GROUP_SIZE) continue; headerOffset.QuadPart = mount->UseBackupHeader ? lDiskLength.QuadPart - TC_HIDDEN_VOLUME_HEADER_OFFSET : TC_HIDDEN_VOLUME_HEADER_OFFSET; break; } Dump ("Reading volume header at %I64d\n", headerOffset.QuadPart); ntStatus = ZwReadFile (Extension->hDeviceFile, NULL, NULL, NULL, &IoStatusBlock, readBuffer, bRawDevice ? max (TC_VOLUME_HEADER_EFFECTIVE_SIZE, Extension->HostBytesPerSector) : TC_VOLUME_HEADER_EFFECTIVE_SIZE, &headerOffset, NULL); } else { // Header of a partition that is within the scope of system encryption WCHAR parentDrivePath [47+1] = {0}; HANDLE hParentDeviceFile = NULL; UNICODE_STRING FullParentPath; OBJECT_ATTRIBUTES oaParentFileAttributes; LARGE_INTEGER parentKeyDataOffset; RtlStringCbPrintfW (parentDrivePath, sizeof (parentDrivePath), WIDE ("\\Device\\Harddisk%d\\Partition0"), mount->nPartitionInInactiveSysEncScopeDriveNo); Dump ("Mounting partition within scope of system encryption (reading key data from: %ls)\n", parentDrivePath); RtlInitUnicodeString (&FullParentPath, parentDrivePath); InitializeObjectAttributes (&oaParentFileAttributes, &FullParentPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); ntStatus = ZwCreateFile (&hParentDeviceFile, GENERIC_READ | SYNCHRONIZE, &oaParentFileAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_RANDOM_ACCESS | FILE_WRITE_THROUGH | FILE_NO_INTERMEDIATE_BUFFERING | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (!NT_SUCCESS (ntStatus)) { if (hParentDeviceFile != NULL) ZwClose (hParentDeviceFile); Dump ("Cannot open %ls\n", parentDrivePath); goto error; } parentKeyDataOffset.QuadPart = TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET; ntStatus = ZwReadFile (hParentDeviceFile, NULL, NULL, NULL, &IoStatusBlock, readBuffer, max (TC_VOLUME_HEADER_EFFECTIVE_SIZE, Extension->HostBytesPerSector), &parentKeyDataOffset, NULL); if (hParentDeviceFile != NULL) ZwClose (hParentDeviceFile); } if (!NT_SUCCESS (ntStatus) && ntStatus != STATUS_END_OF_FILE) { Dump ("Read failed: NTSTATUS 0x%08x\n", ntStatus); goto error; } if (ntStatus == STATUS_END_OF_FILE || IoStatusBlock.Information < TC_VOLUME_HEADER_EFFECTIVE_SIZE) { Dump ("Read didn't read enough data\n"); // If FSCTL_ALLOW_EXTENDED_DASD_IO failed and there is a live filesystem on the partition, then the // filesystem driver may report EOF when we are reading hidden sectors (when the filesystem is // shorter than the partition). This can happen for example after the user quick-formats a dismounted // partition-hosted TrueCrypt volume and then tries to mount the volume using the embedded backup header. memset (readBuffer, 0, TC_VOLUME_HEADER_EFFECTIVE_SIZE); } /* Attempt to recognize the volume (decrypt the header) */ ReadVolumeHeaderRecoveryMode = mount->RecoveryMode; if ((volumeType == TC_VOLUME_TYPE_HIDDEN) && mount->bProtectHiddenVolume) { mount->nReturnCode = ReadVolumeHeaderWCache ( FALSE, mount->bCache, mount->bCachePim, readBuffer, &mount->ProtectedHidVolPassword, mount->ProtectedHidVolPkcs5Prf, mount->ProtectedHidVolPim, mount->bTrueCryptMode, &tmpCryptoInfo); } else { mount->nReturnCode = ReadVolumeHeaderWCache ( mount->bPartitionInInactiveSysEncScope && volumeType == TC_VOLUME_TYPE_NORMAL, mount->bCache, mount->bCachePim, readBuffer, &mount->VolumePassword, mount->pkcs5_prf, mount->VolumePim, mount->bTrueCryptMode, &Extension->cryptoInfo); } ReadVolumeHeaderRecoveryMode = FALSE; if (mount->nReturnCode == 0 || mount->nReturnCode == ERR_CIPHER_INIT_WEAK_KEY) { /* Volume header successfully decrypted */ if (!Extension->cryptoInfo) { /* should never happen */ mount->nReturnCode = ERR_OUTOFMEMORY; ntStatus = STATUS_SUCCESS; goto error; } Dump ("Volume header decrypted\n"); Dump ("Required program version = %x\n", (int) Extension->cryptoInfo->RequiredProgramVersion); Dump ("Legacy volume = %d\n", (int) Extension->cryptoInfo->LegacyVolume); if (IsHiddenSystemRunning() && !Extension->cryptoInfo->hiddenVolume) { Extension->bReadOnly = mount->bMountReadOnly = TRUE; HiddenSysLeakProtectionCount++; } Extension->cryptoInfo->bProtectHiddenVolume = FALSE; Extension->cryptoInfo->bHiddenVolProtectionAction = FALSE; Extension->cryptoInfo->bPartitionInInactiveSysEncScope = mount->bPartitionInInactiveSysEncScope; /* compute the ID of this volume: SHA-512 of the effective header */ sha256 (Extension->volumeID, readBuffer, TC_VOLUME_HEADER_EFFECTIVE_SIZE); if (volumeType == TC_VOLUME_TYPE_NORMAL) { if (mount->bPartitionInInactiveSysEncScope) { if (Extension->cryptoInfo->EncryptedAreaStart.Value > (unsigned __int64) partitionStartingOffset || Extension->cryptoInfo->EncryptedAreaStart.Value + Extension->cryptoInfo->VolumeSize.Value <= (unsigned __int64) partitionStartingOffset) { // The partition is not within the key scope of system encryption mount->nReturnCode = ERR_PASSWORD_WRONG; ntStatus = STATUS_SUCCESS; goto error; } if (Extension->cryptoInfo->EncryptedAreaLength.Value != Extension->cryptoInfo->VolumeSize.Value) { // Partial encryption is not supported for volumes mounted as regular mount->nReturnCode = ERR_ENCRYPTION_NOT_COMPLETED; ntStatus = STATUS_SUCCESS; goto error; } } else if (Extension->cryptoInfo->HeaderFlags & TC_HEADER_FLAG_NONSYS_INPLACE_ENC) { if (Extension->cryptoInfo->EncryptedAreaLength.Value != Extension->cryptoInfo->VolumeSize.Value) { // Non-system in-place encryption process has not been completed on this volume mount->nReturnCode = ERR_NONSYS_INPLACE_ENC_INCOMPLETE; ntStatus = STATUS_SUCCESS; goto error; } } } Extension->cryptoInfo->FirstDataUnitNo.Value = 0; if (Extension->cryptoInfo->hiddenVolume && IsHiddenSystemRunning()) { // Prevent mount of a hidden system partition if the system hosted on it is currently running if (memcmp (Extension->cryptoInfo->master_keydata, GetSystemDriveCryptoInfo()->master_keydata, EAGetKeySize (Extension->cryptoInfo->ea)) == 0) { mount->nReturnCode = ERR_VOL_ALREADY_MOUNTED; ntStatus = STATUS_SUCCESS; goto error; } } switch (volumeType) { case TC_VOLUME_TYPE_NORMAL: Extension->cryptoInfo->hiddenVolume = FALSE; if (mount->bPartitionInInactiveSysEncScope) { Extension->cryptoInfo->volDataAreaOffset = 0; Extension->DiskLength = lDiskLength.QuadPart; Extension->cryptoInfo->FirstDataUnitNo.Value = partitionStartingOffset / ENCRYPTION_DATA_UNIT_SIZE; } else if (Extension->cryptoInfo->LegacyVolume) { Extension->cryptoInfo->volDataAreaOffset = TC_VOLUME_HEADER_SIZE_LEGACY; Extension->DiskLength = lDiskLength.QuadPart - TC_VOLUME_HEADER_SIZE_LEGACY; } else { Extension->cryptoInfo->volDataAreaOffset = Extension->cryptoInfo->EncryptedAreaStart.Value; Extension->DiskLength = Extension->cryptoInfo->VolumeSize.Value; } break; case TC_VOLUME_TYPE_HIDDEN: cryptoInfoPtr = mount->bProtectHiddenVolume ? tmpCryptoInfo : Extension->cryptoInfo; Extension->cryptoInfo->hiddenVolumeOffset = cryptoInfoPtr->EncryptedAreaStart.Value; Dump ("Hidden volume offset = %I64d\n", Extension->cryptoInfo->hiddenVolumeOffset); Dump ("Hidden volume size = %I64d\n", cryptoInfoPtr->hiddenVolumeSize); Dump ("Hidden volume end = %I64d\n", Extension->cryptoInfo->hiddenVolumeOffset + cryptoInfoPtr->hiddenVolumeSize - 1); // Validate the offset if (Extension->cryptoInfo->hiddenVolumeOffset % ENCRYPTION_DATA_UNIT_SIZE != 0) { mount->nReturnCode = ERR_VOL_SIZE_WRONG; ntStatus = STATUS_SUCCESS; goto error; } // If we are supposed to actually mount the hidden volume (not just to protect it) if (!mount->bProtectHiddenVolume) { Extension->DiskLength = cryptoInfoPtr->hiddenVolumeSize; Extension->cryptoInfo->hiddenVolume = TRUE; Extension->cryptoInfo->volDataAreaOffset = Extension->cryptoInfo->hiddenVolumeOffset; } else { // Hidden volume protection Extension->cryptoInfo->hiddenVolume = FALSE; Extension->cryptoInfo->bProtectHiddenVolume = TRUE; Extension->cryptoInfo->hiddenVolumeProtectedSize = tmpCryptoInfo->hiddenVolumeSize; Dump ("Hidden volume protection active: %I64d-%I64d (%I64d)\n", Extension->cryptoInfo->hiddenVolumeOffset, Extension->cryptoInfo->hiddenVolumeProtectedSize + Extension->cryptoInfo->hiddenVolumeOffset - 1, Extension->cryptoInfo->hiddenVolumeProtectedSize); } break; } Dump ("Volume data offset = %I64d\n", Extension->cryptoInfo->volDataAreaOffset); Dump ("Volume data size = %I64d\n", Extension->DiskLength); Dump ("Volume data end = %I64d\n", Extension->cryptoInfo->volDataAreaOffset + Extension->DiskLength - 1); if (Extension->DiskLength == 0) { Dump ("Incorrect volume size\n"); continue; } // If this is a hidden volume, make sure we are supposed to actually // mount it (i.e. not just to protect it) if (volumeType == TC_VOLUME_TYPE_NORMAL || !mount->bProtectHiddenVolume) { // Validate sector size if (bRawDevice && Extension->cryptoInfo->SectorSize != Extension->HostBytesPerSector) { mount->nReturnCode = ERR_PARAMETER_INCORRECT; ntStatus = STATUS_SUCCESS; goto error; } // Calculate virtual volume geometry Extension->TracksPerCylinder = 1; Extension->SectorsPerTrack = 1; Extension->BytesPerSector = Extension->cryptoInfo->SectorSize; Extension->NumberOfCylinders = Extension->DiskLength / Extension->BytesPerSector; Extension->PartitionType = 0; Extension->bRawDevice = bRawDevice; memset (Extension->wszVolume, 0, sizeof (Extension->wszVolume)); if (wcsstr (pwszMountVolume, WIDE ("\\??\\UNC\\")) == pwszMountVolume) { /* UNC path */ RtlStringCbPrintfW (Extension->wszVolume, sizeof (Extension->wszVolume), WIDE ("\\??\\\\%s"), pwszMountVolume + 7); } else { RtlStringCbCopyW (Extension->wszVolume, sizeof(Extension->wszVolume),pwszMountVolume); } memset (Extension->wszLabel, 0, sizeof (Extension->wszLabel)); RtlStringCbCopyW (Extension->wszLabel, sizeof(Extension->wszLabel), mount->wszLabel); } // If we are to protect a hidden volume we cannot exit yet, for we must also // decrypt the hidden volume header. if (!(volumeType == TC_VOLUME_TYPE_NORMAL && mount->bProtectHiddenVolume)) { TCfree (readBuffer); if (tmpCryptoInfo != NULL) { crypto_close (tmpCryptoInfo); tmpCryptoInfo = NULL; } return STATUS_SUCCESS; } } else if ((mount->bProtectHiddenVolume && volumeType == TC_VOLUME_TYPE_NORMAL) || mount->nReturnCode != ERR_PASSWORD_WRONG) { /* If we are not supposed to protect a hidden volume, the only error that is tolerated is ERR_PASSWORD_WRONG (to allow mounting a possible hidden volume). If we _are_ supposed to protect a hidden volume, we do not tolerate any error (both volume headers must be successfully decrypted). */ break; } } /* Failed due to some non-OS reason so we drop through and return NT SUCCESS then nReturnCode is checked later in user-mode */ if (mount->nReturnCode == ERR_OUTOFMEMORY) ntStatus = STATUS_INSUFFICIENT_RESOURCES; else ntStatus = STATUS_SUCCESS; error: if (mount->nReturnCode == ERR_SUCCESS) mount->nReturnCode = ERR_PASSWORD_WRONG; if (tmpCryptoInfo != NULL) { crypto_close (tmpCryptoInfo); tmpCryptoInfo = NULL; } if (Extension->cryptoInfo) { crypto_close (Extension->cryptoInfo); Extension->cryptoInfo = NULL; } if (Extension->bTimeStampValid) { RestoreTimeStamp (Extension); } /* Close the hDeviceFile */ if (Extension->hDeviceFile != NULL) ZwClose (Extension->hDeviceFile); /* The cryptoInfo pointer is deallocated if the readheader routines fail so there is no need to deallocate here */ /* Dereference the user-mode file object */ if (Extension->pfoDeviceFile != NULL) ObDereferenceObject (Extension->pfoDeviceFile); /* Free the tmp IO buffers */ if (readBuffer != NULL) TCfree (readBuffer); return ntStatus; }