diff options
author | Jedidiah Barber <contact@jedbarber.id.au> | 2021-07-14 11:27:03 +1200 |
---|---|---|
committer | Jedidiah Barber <contact@jedbarber.id.au> | 2021-07-14 11:27:03 +1200 |
commit | 3cb7fdea950dd2d0377f0d9ad8a88fcb7c48b842 (patch) | |
tree | cedbfc08a6bf0bd8cb6ec6c8d8dd94a4e715439b /Driver/Fuse |
Diffstat (limited to 'Driver/Fuse')
-rw-r--r-- | Driver/Fuse/Driver.make | 16 | ||||
-rw-r--r-- | Driver/Fuse/FuseService.cpp | 592 | ||||
-rw-r--r-- | Driver/Fuse/FuseService.h | 73 |
3 files changed, 681 insertions, 0 deletions
diff --git a/Driver/Fuse/Driver.make b/Driver/Fuse/Driver.make new file mode 100644 index 0000000..7e08e36 --- /dev/null +++ b/Driver/Fuse/Driver.make @@ -0,0 +1,16 @@ +# +# Copyright (c) 2008 TrueCrypt Developers Association. All rights reserved. +# +# Governed by the TrueCrypt License 3.0 the full text of which is contained in +# the file License.txt included in TrueCrypt binary and source code distribution +# packages. +# + +NAME := Driver + +OBJS := +OBJS += FuseService.o + +CXXFLAGS += $(shell pkg-config fuse --cflags) + +include $(BUILD_INC)/Makefile.inc diff --git a/Driver/Fuse/FuseService.cpp b/Driver/Fuse/FuseService.cpp new file mode 100644 index 0000000..fda56e0 --- /dev/null +++ b/Driver/Fuse/FuseService.cpp @@ -0,0 +1,592 @@ +/* + Copyright (c) 2008 TrueCrypt Developers Association. All rights reserved. + + Governed by the TrueCrypt License 3.0 the full text of which is contained in + the file License.txt included in TrueCrypt binary and source code distribution + packages. +*/ + +#define FUSE_USE_VERSION 25 +#include <errno.h> +#include <fcntl.h> +#include <fuse.h> +#include <iostream> +#include <signal.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <time.h> +#include <sys/mman.h> +#include <sys/time.h> +#include <sys/wait.h> + +#include "FuseService.h" +#include "Platform/FileStream.h" +#include "Platform/MemoryStream.h" +#include "Platform/Serializable.h" +#include "Platform/SystemLog.h" +#include "Platform/Unix/Pipe.h" +#include "Platform/Unix/Poller.h" +#include "Volume/EncryptionThreadPool.h" +#include "Core/Core.h" + +namespace TrueCrypt +{ + static int fuse_service_access (const char *path, int mask) + { + try + { + if (!FuseService::CheckAccessRights()) + return -EACCES; + } + catch (...) + { + return FuseService::ExceptionToErrorCode(); + } + + return 0; + } + + static void *fuse_service_init () + { + try + { + // Termination signals are handled by a separate process to allow clean dismount on shutdown + struct sigaction action; + Memory::Zero (&action, sizeof (action)); + action.sa_handler = SIG_IGN; + + sigaction (SIGINT, &action, nullptr); + sigaction (SIGQUIT, &action, nullptr); + sigaction (SIGTERM, &action, nullptr); + + if (!EncryptionThreadPool::IsRunning()) + EncryptionThreadPool::Start(); + } + catch (exception &e) + { + SystemLog::WriteException (e); + } + catch (...) + { + SystemLog::WriteException (UnknownException (SRC_POS)); + } + + return nullptr; + } + + static void fuse_service_destroy (void *userdata) + { + try + { + FuseService::Dismount(); + } + catch (exception &e) + { + SystemLog::WriteException (e); + } + catch (...) + { + SystemLog::WriteException (UnknownException (SRC_POS)); + } + } + + static int fuse_service_getattr (const char *path, struct stat *statData) + { + try + { + Memory::Zero (statData, sizeof(*statData)); + + statData->st_uid = FuseService::GetUserId(); + statData->st_gid = FuseService::GetGroupId(); + statData->st_atime = time (NULL); + statData->st_ctime = time (NULL); + statData->st_mtime = time (NULL); + + if (strcmp (path, "/") == 0) + { + statData->st_mode = S_IFDIR | 0500; + statData->st_nlink = 2; + } + else + { + if (!FuseService::CheckAccessRights()) + return -EACCES; + + if (strcmp (path, FuseService::GetVolumeImagePath()) == 0) + { + statData->st_mode = S_IFREG | 0600; + statData->st_nlink = 1; + statData->st_size = FuseService::GetVolumeSize(); + } + else if (strcmp (path, FuseService::GetControlPath()) == 0) + { + statData->st_mode = S_IFREG | 0600; + statData->st_nlink = 1; + statData->st_size = FuseService::GetVolumeInfo()->Size(); + } + else + { + return -ENOENT; + } + } + } + catch (...) + { + return FuseService::ExceptionToErrorCode(); + } + + return 0; + } + + static int fuse_service_opendir (const char *path, struct fuse_file_info *fi) + { + try + { + if (!FuseService::CheckAccessRights()) + return -EACCES; + + if (strcmp (path, "/") != 0) + return -ENOENT; + } + catch (...) + { + return FuseService::ExceptionToErrorCode(); + } + + return 0; + } + + static int fuse_service_open (const char *path, struct fuse_file_info *fi) + { + try + { + if (!FuseService::CheckAccessRights()) + return -EACCES; + + if (strcmp (path, FuseService::GetVolumeImagePath()) == 0) + return 0; + + if (strcmp (path, FuseService::GetControlPath()) == 0) + { + fi->direct_io = 1; + return 0; + } + } + catch (...) + { + return FuseService::ExceptionToErrorCode(); + } + return -ENOENT; + } + + static int fuse_service_read (const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) + { + try + { + if (!FuseService::CheckAccessRights()) + return -EACCES; + + if (strcmp (path, FuseService::GetVolumeImagePath()) == 0) + { + try + { + // Test for read beyond the end of the volume + if ((uint64) offset + size > FuseService::GetVolumeSize()) + size = FuseService::GetVolumeSize() - offset; + + size_t sectorSize = FuseService::GetVolumeSectorSize(); + if (size % sectorSize != 0 || offset % sectorSize != 0) + { + // Support for non-sector-aligned read operations is required by some loop device tools + // which may analyze the volume image before attaching it as a device + + uint64 alignedOffset = offset - (offset % sectorSize); + uint64 alignedSize = size + (offset % sectorSize); + + if (alignedSize % sectorSize != 0) + alignedSize += sectorSize - (alignedSize % sectorSize); + + SecureBuffer alignedBuffer (alignedSize); + + FuseService::ReadVolumeSectors (alignedBuffer, alignedOffset); + BufferPtr ((byte *) buf, size).CopyFrom (alignedBuffer.GetRange (offset % sectorSize, size)); + } + else + { + FuseService::ReadVolumeSectors (BufferPtr ((byte *) buf, size), offset); + } + } + catch (MissingVolumeData) + { + return 0; + } + + return size; + } + + if (strcmp (path, FuseService::GetControlPath()) == 0) + { + shared_ptr <Buffer> infoBuf = FuseService::GetVolumeInfo(); + BufferPtr outBuf ((byte *)buf, size); + + if (offset >= (off_t) infoBuf->Size()) + return 0; + + if (offset + size > infoBuf->Size()) + size = infoBuf->Size () - offset; + + outBuf.CopyFrom (infoBuf->GetRange (offset, size)); + return size; + } + } + catch (...) + { + return FuseService::ExceptionToErrorCode(); + } + + return -ENOENT; + } + + static int fuse_service_readdir (const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) + { + try + { + if (!FuseService::CheckAccessRights()) + return -EACCES; + + if (strcmp (path, "/") != 0) + return -ENOENT; + + filler (buf, ".", NULL, 0); + filler (buf, "..", NULL, 0); + filler (buf, FuseService::GetVolumeImagePath() + 1, NULL, 0); + filler (buf, FuseService::GetControlPath() + 1, NULL, 0); + } + catch (...) + { + return FuseService::ExceptionToErrorCode(); + } + + return 0; + } + + static int fuse_service_write (const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) + { + try + { + if (!FuseService::CheckAccessRights()) + return -EACCES; + + if (strcmp (path, FuseService::GetVolumeImagePath()) == 0) + { + FuseService::WriteVolumeSectors (BufferPtr ((byte *) buf, size), offset); + return size; + } + + if (strcmp (path, FuseService::GetControlPath()) == 0) + { + if (FuseService::AuxDeviceInfoReceived()) + return -EACCES; + + FuseService::ReceiveAuxDeviceInfo (ConstBufferPtr ((const byte *)buf, size)); + return size; + } + } +#ifdef TC_FREEBSD + // FreeBSD apparently retries failed write operations forever, which may lead to a system crash. + catch (VolumeReadOnly&) + { + return size; + } + catch (VolumeProtected&) + { + return size; + } +#endif + catch (...) + { + return FuseService::ExceptionToErrorCode(); + } + + return -ENOENT; + } + + bool FuseService::CheckAccessRights () + { + return fuse_get_context()->uid == 0 || fuse_get_context()->uid == UserId; + } + + void FuseService::CloseMountedVolume () + { + if (MountedVolume) + { + // This process will exit before the use count of MountedVolume reaches zero + if (MountedVolume->GetFile().use_count() > 1) + MountedVolume->GetFile()->Close(); + + if (MountedVolume.use_count() > 1) + delete MountedVolume.get(); + + MountedVolume.reset(); + } + } + + void FuseService::Dismount () + { + CloseMountedVolume(); + + if (EncryptionThreadPool::IsRunning()) + EncryptionThreadPool::Stop(); + } + + int FuseService::ExceptionToErrorCode () + { + try + { + throw; + } + catch (std::bad_alloc) + { + return -ENOMEM; + } + catch (ParameterIncorrect &e) + { + SystemLog::WriteException (e); + return -EINVAL; + } + catch (VolumeProtected&) + { + return -EPERM; + } + catch (VolumeReadOnly&) + { + return -EPERM; + } + catch (SystemException &e) + { + SystemLog::WriteException (e); + return -static_cast <int> (e.GetErrorCode()); + } + catch (std::exception &e) + { + SystemLog::WriteException (e); + return -EINTR; + } + catch (...) + { + SystemLog::WriteException (UnknownException (SRC_POS)); + return -EINTR; + } + } + + shared_ptr <Buffer> FuseService::GetVolumeInfo () + { + shared_ptr <Stream> stream (new MemoryStream); + + { + ScopeLock lock (OpenVolumeInfoMutex); + + OpenVolumeInfo.Set (*MountedVolume); + OpenVolumeInfo.SlotNumber = SlotNumber; + + OpenVolumeInfo.Serialize (stream); + } + + ConstBufferPtr infoBuf = dynamic_cast <MemoryStream&> (*stream); + shared_ptr <Buffer> outBuf (new Buffer (infoBuf.Size())); + outBuf->CopyFrom (infoBuf); + + return outBuf; + } + + const char *FuseService::GetVolumeImagePath () + { +#ifdef TC_MACOSX + return "/volume.dmg"; +#else + return "/volume"; +#endif + } + + uint64 FuseService::GetVolumeSize () + { + if (!MountedVolume) + throw NotInitialized (SRC_POS); + + return MountedVolume->GetSize(); + } + + void FuseService::Mount (shared_ptr <Volume> openVolume, VolumeSlotNumber slotNumber, const string &fuseMountPoint) + { + list <string> args; + args.push_back (FuseService::GetDeviceType()); + args.push_back (fuseMountPoint); + +#ifdef TC_MACOSX + args.push_back ("-o"); + args.push_back ("noping_diskarb"); + args.push_back ("-o"); + args.push_back ("nobrowse"); + + if (getuid() == 0 || geteuid() == 0) +#endif + { + args.push_back ("-o"); + args.push_back ("allow_other"); + } + + ExecFunctor execFunctor (openVolume, slotNumber); + Process::Execute ("fuse", args, -1, &execFunctor); + + for (int t = 0; true; t++) + { + try + { + if (FilesystemPath (fuseMountPoint + FuseService::GetControlPath()).GetType() == FilesystemPathType::File) + break; + } + catch (...) + { + if (t > 50) + throw; + + Thread::Sleep (100); + } + } + } + + void FuseService::ReadVolumeSectors (const BufferPtr &buffer, uint64 byteOffset) + { + if (!MountedVolume) + throw NotInitialized (SRC_POS); + + MountedVolume->ReadSectors (buffer, byteOffset); + } + + void FuseService::ReceiveAuxDeviceInfo (const ConstBufferPtr &buffer) + { + shared_ptr <Stream> stream (new MemoryStream (buffer)); + Serializer sr (stream); + + ScopeLock lock (OpenVolumeInfoMutex); + OpenVolumeInfo.VirtualDevice = sr.DeserializeString ("VirtualDevice"); + OpenVolumeInfo.LoopDevice = sr.DeserializeString ("LoopDevice"); + } + + void FuseService::SendAuxDeviceInfo (const DirectoryPath &fuseMountPoint, const DevicePath &virtualDevice, const DevicePath &loopDevice) + { + File fuseServiceControl; + fuseServiceControl.Open (string (fuseMountPoint) + GetControlPath(), File::OpenWrite); + + shared_ptr <Stream> stream (new MemoryStream); + Serializer sr (stream); + + sr.Serialize ("VirtualDevice", string (virtualDevice)); + sr.Serialize ("LoopDevice", string (loopDevice)); + fuseServiceControl.Write (dynamic_cast <MemoryStream&> (*stream)); + } + + void FuseService::WriteVolumeSectors (const ConstBufferPtr &buffer, uint64 byteOffset) + { + if (!MountedVolume) + throw NotInitialized (SRC_POS); + + MountedVolume->WriteSectors (buffer, byteOffset); + } + + void FuseService::OnSignal (int signal) + { + try + { + shared_ptr <VolumeInfo> volume = Core->GetMountedVolume (SlotNumber); + + if (volume) + Core->DismountVolume (volume, true); + } + catch (...) { } + + _exit (0); + } + + void FuseService::ExecFunctor::operator() (int argc, char *argv[]) + { + struct timeval tv; + gettimeofday (&tv, NULL); + FuseService::OpenVolumeInfo.SerialInstanceNumber = (uint64)tv.tv_sec * 1000000ULL + tv.tv_usec; + + FuseService::MountedVolume = MountedVolume; + FuseService::SlotNumber = SlotNumber; + + FuseService::UserId = getuid(); + FuseService::GroupId = getgid(); + + if (getenv ("SUDO_UID")) + { + try + { + string s (getenv ("SUDO_UID")); + FuseService::UserId = static_cast <uid_t> (StringConverter::ToUInt64 (s)); + + if (getenv ("SUDO_GID")) + { + s = getenv ("SUDO_GID"); + FuseService::GroupId = static_cast <gid_t> (StringConverter::ToUInt64 (s)); + } + } + catch (...) { } + } + + static fuse_operations fuse_service_oper; + + fuse_service_oper.access = fuse_service_access; + fuse_service_oper.destroy = fuse_service_destroy; + fuse_service_oper.getattr = fuse_service_getattr; + fuse_service_oper.init = fuse_service_init; + fuse_service_oper.open = fuse_service_open; + fuse_service_oper.opendir = fuse_service_opendir; + fuse_service_oper.read = fuse_service_read; + fuse_service_oper.readdir = fuse_service_readdir; + fuse_service_oper.write = fuse_service_write; + + // Create a new session + setsid (); + + // Fork handler of termination signals + SignalHandlerPipe.reset (new Pipe); + + int forkedPid = fork(); + throw_sys_if (forkedPid == -1); + + if (forkedPid == 0) + { + CloseMountedVolume(); + + struct sigaction action; + Memory::Zero (&action, sizeof (action)); + action.sa_handler = OnSignal; + + sigaction (SIGINT, &action, nullptr); + sigaction (SIGQUIT, &action, nullptr); + sigaction (SIGTERM, &action, nullptr); + + // Wait for the exit of the main service + byte buf[1]; + if (read (SignalHandlerPipe->GetReadFD(), buf, sizeof (buf))) { } // Errors ignored + + _exit (0); + } + + SignalHandlerPipe->GetWriteFD(); + + _exit (fuse_main (argc, argv, &fuse_service_oper)); + } + + VolumeInfo FuseService::OpenVolumeInfo; + Mutex FuseService::OpenVolumeInfoMutex; + shared_ptr <Volume> FuseService::MountedVolume; + VolumeSlotNumber FuseService::SlotNumber; + uid_t FuseService::UserId; + gid_t FuseService::GroupId; + auto_ptr <Pipe> FuseService::SignalHandlerPipe; +} diff --git a/Driver/Fuse/FuseService.h b/Driver/Fuse/FuseService.h new file mode 100644 index 0000000..5ff2dd7 --- /dev/null +++ b/Driver/Fuse/FuseService.h @@ -0,0 +1,73 @@ +/* + Copyright (c) 2008 TrueCrypt Developers Association. All rights reserved. + + Governed by the TrueCrypt License 3.0 the full text of which is contained in + the file License.txt included in TrueCrypt binary and source code distribution + packages. +*/ + +#ifndef TC_HEADER_Driver_Fuse_FuseService +#define TC_HEADER_Driver_Fuse_FuseService + +#include "Platform/Platform.h" +#include "Platform/Unix/Pipe.h" +#include "Platform/Unix/Process.h" +#include "Volume/VolumeInfo.h" +#include "Volume/Volume.h" + +namespace TrueCrypt +{ + + class FuseService + { + protected: + struct ExecFunctor : public ProcessExecFunctor + { + ExecFunctor (shared_ptr <Volume> openVolume, VolumeSlotNumber slotNumber) + : MountedVolume (openVolume), SlotNumber (slotNumber) + { + } + virtual void operator() (int argc, char *argv[]); + + protected: + shared_ptr <Volume> MountedVolume; + VolumeSlotNumber SlotNumber; + }; + + friend class ExecFunctor; + + public: + static bool AuxDeviceInfoReceived () { return !OpenVolumeInfo.VirtualDevice.IsEmpty(); } + static bool CheckAccessRights (); + static void Dismount (); + static int ExceptionToErrorCode (); + static const char *GetControlPath () { return "/control"; } + static const char *GetVolumeImagePath (); + static string GetDeviceType () { return "truecrypt"; } + static uid_t GetGroupId () { return GroupId; } + static uid_t GetUserId () { return UserId; } + static shared_ptr <Buffer> GetVolumeInfo (); + static uint64 GetVolumeSize (); + static uint64 GetVolumeSectorSize () { return MountedVolume->GetSectorSize(); } + static void Mount (shared_ptr <Volume> openVolume, VolumeSlotNumber slotNumber, const string &fuseMountPoint); + static void ReadVolumeSectors (const BufferPtr &buffer, uint64 byteOffset); + static void ReceiveAuxDeviceInfo (const ConstBufferPtr &buffer); + static void SendAuxDeviceInfo (const DirectoryPath &fuseMountPoint, const DevicePath &virtualDevice, const DevicePath &loopDevice = DevicePath()); + static void WriteVolumeSectors (const ConstBufferPtr &buffer, uint64 byteOffset); + + protected: + FuseService (); + static void CloseMountedVolume (); + static void OnSignal (int signal); + + static VolumeInfo OpenVolumeInfo; + static Mutex OpenVolumeInfoMutex; + static shared_ptr <Volume> MountedVolume; + static VolumeSlotNumber SlotNumber; + static uid_t UserId; + static gid_t GroupId; + static auto_ptr <Pipe> SignalHandlerPipe; + }; +} + +#endif // TC_HEADER_Driver_Fuse_FuseService |