From 7ffce028d04a6b13ef762e2b89c34b688e8ca59d Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Sat, 31 May 2014 18:44:53 +0200 Subject: Add TrueCrypt 7.1a MacOSX/Linux specific source files. --- src/Core/Unix/CoreService.cpp | 544 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 544 insertions(+) create mode 100644 src/Core/Unix/CoreService.cpp (limited to 'src/Core/Unix/CoreService.cpp') diff --git a/src/Core/Unix/CoreService.cpp b/src/Core/Unix/CoreService.cpp new file mode 100644 index 00000000..0ec636c7 --- /dev/null +++ b/src/Core/Unix/CoreService.cpp @@ -0,0 +1,544 @@ +/* + Copyright (c) 2008-2010 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. +*/ + +#include "CoreService.h" +#include +#include +#include "Platform/FileStream.h" +#include "Platform/MemoryStream.h" +#include "Platform/Serializable.h" +#include "Platform/SystemLog.h" +#include "Platform/Thread.h" +#include "Platform/Unix/Poller.h" +#include "Core/Core.h" +#include "CoreUnix.h" +#include "CoreServiceRequest.h" +#include "CoreServiceResponse.h" + +namespace TrueCrypt +{ + template + auto_ptr CoreService::GetResponse () + { + auto_ptr deserializedObject (Serializable::DeserializeNew (ServiceOutputStream)); + + Exception *deserializedException = dynamic_cast (deserializedObject.get()); + if (deserializedException) + deserializedException->Throw(); + + if (dynamic_cast (deserializedObject.get()) == nullptr) + throw ParameterIncorrect (SRC_POS); + + return auto_ptr (dynamic_cast (deserializedObject.release())); + } + + void CoreService::ProcessElevatedRequests () + { + int pid = fork(); + throw_sys_if (pid == -1); + if (pid == 0) + { + try + { + int f = open ("/dev/null", 0); + throw_sys_sub_if (f == -1, "/dev/null"); + throw_sys_if (dup2 (f, STDERR_FILENO) == -1); + + // Wait for sync code + while (true) + { + byte b; + throw_sys_if (read (STDIN_FILENO, &b, 1) != 1); + if (b != 0x00) + continue; + + throw_sys_if (read (STDIN_FILENO, &b, 1) != 1); + if (b != 0x11) + continue; + + throw_sys_if (read (STDIN_FILENO, &b, 1) != 1); + if (b == 0x22) + break; + } + + ElevatedPrivileges = true; + ProcessRequests (STDIN_FILENO, STDOUT_FILENO); + _exit (0); + } + catch (exception &e) + { +#ifdef DEBUG + SystemLog::WriteException (e); +#endif + } + catch (...) { } + _exit (1); + } + } + + void CoreService::ProcessRequests (int inputFD, int outputFD) + { + try + { + Core = CoreDirect; + + shared_ptr inputStream (new FileStream (inputFD != -1 ? inputFD : InputPipe->GetReadFD())); + shared_ptr outputStream (new FileStream (outputFD != -1 ? outputFD : OutputPipe->GetWriteFD())); + + while (true) + { + shared_ptr request = Serializable::DeserializeNew (inputStream); + + try + { + // ExitRequest + if (dynamic_cast (request.get()) != nullptr) + { + if (ElevatedServiceAvailable) + request->Serialize (ServiceInputStream); + return; + } + + if (!ElevatedPrivileges && request->ElevateUserPrivileges) + { + if (!ElevatedServiceAvailable) + { + finally_do_arg (string *, &request->AdminPassword, { StringConverter::Erase (*finally_arg); }); + + CoreService::StartElevated (*request); + ElevatedServiceAvailable = true; + } + + request->Serialize (ServiceInputStream); + GetResponse ()->Serialize (outputStream); + continue; + } + + // CheckFilesystemRequest + CheckFilesystemRequest *checkRequest = dynamic_cast (request.get()); + if (checkRequest) + { + Core->CheckFilesystem (checkRequest->MountedVolumeInfo, checkRequest->Repair); + + CheckFilesystemResponse().Serialize (outputStream); + continue; + } + + // DismountFilesystemRequest + DismountFilesystemRequest *dismountFsRequest = dynamic_cast (request.get()); + if (dismountFsRequest) + { + Core->DismountFilesystem (dismountFsRequest->MountPoint, dismountFsRequest->Force); + + DismountFilesystemResponse().Serialize (outputStream); + continue; + } + + // DismountVolumeRequest + DismountVolumeRequest *dismountRequest = dynamic_cast (request.get()); + if (dismountRequest) + { + DismountVolumeResponse response; + response.DismountedVolumeInfo = Core->DismountVolume (dismountRequest->MountedVolumeInfo, dismountRequest->IgnoreOpenFiles, dismountRequest->SyncVolumeInfo); + response.Serialize (outputStream); + continue; + } + + // GetDeviceSectorSizeRequest + GetDeviceSectorSizeRequest *getDeviceSectorSizeRequest = dynamic_cast (request.get()); + if (getDeviceSectorSizeRequest) + { + GetDeviceSectorSizeResponse response; + response.Size = Core->GetDeviceSectorSize (getDeviceSectorSizeRequest->Path); + response.Serialize (outputStream); + continue; + } + + // GetDeviceSizeRequest + GetDeviceSizeRequest *getDeviceSizeRequest = dynamic_cast (request.get()); + if (getDeviceSizeRequest) + { + GetDeviceSizeResponse response; + response.Size = Core->GetDeviceSize (getDeviceSizeRequest->Path); + response.Serialize (outputStream); + continue; + } + + // GetHostDevicesRequest + GetHostDevicesRequest *getHostDevicesRequest = dynamic_cast (request.get()); + if (getHostDevicesRequest) + { + GetHostDevicesResponse response; + response.HostDevices = Core->GetHostDevices (getHostDevicesRequest->PathListOnly); + response.Serialize (outputStream); + continue; + } + + // MountVolumeRequest + MountVolumeRequest *mountRequest = dynamic_cast (request.get()); + if (mountRequest) + { + MountVolumeResponse ( + Core->MountVolume (*mountRequest->Options) + ).Serialize (outputStream); + + continue; + } + + // SetFileOwnerRequest + SetFileOwnerRequest *setFileOwnerRequest = dynamic_cast (request.get()); + if (setFileOwnerRequest) + { + CoreUnix *coreUnix = dynamic_cast (Core.get()); + if (!coreUnix) + throw ParameterIncorrect (SRC_POS); + + coreUnix->SetFileOwner (setFileOwnerRequest->Path, setFileOwnerRequest->Owner); + SetFileOwnerResponse().Serialize (outputStream); + continue; + } + + throw ParameterIncorrect (SRC_POS); + } + catch (Exception &e) + { + e.Serialize (outputStream); + } + catch (exception &e) + { + ExternalException (SRC_POS, StringConverter::ToExceptionString (e)).Serialize (outputStream); + } + } + } + catch (exception &e) + { +#ifdef DEBUG + SystemLog::WriteException (e); +#endif + throw; + } + } + + void CoreService::RequestCheckFilesystem (shared_ptr mountedVolume, bool repair) + { + CheckFilesystemRequest request (mountedVolume, repair); + SendRequest (request); + } + + void CoreService::RequestDismountFilesystem (const DirectoryPath &mountPoint, bool force) + { + DismountFilesystemRequest request (mountPoint, force); + SendRequest (request); + } + + shared_ptr CoreService::RequestDismountVolume (shared_ptr mountedVolume, bool ignoreOpenFiles, bool syncVolumeInfo) + { + DismountVolumeRequest request (mountedVolume, ignoreOpenFiles, syncVolumeInfo); + return SendRequest (request)->DismountedVolumeInfo; + } + + uint32 CoreService::RequestGetDeviceSectorSize (const DevicePath &devicePath) + { + GetDeviceSectorSizeRequest request (devicePath); + return SendRequest (request)->Size; + } + + uint64 CoreService::RequestGetDeviceSize (const DevicePath &devicePath) + { + GetDeviceSizeRequest request (devicePath); + return SendRequest (request)->Size; + } + + HostDeviceList CoreService::RequestGetHostDevices (bool pathListOnly) + { + GetHostDevicesRequest request (pathListOnly); + return SendRequest (request)->HostDevices; + } + + shared_ptr CoreService::RequestMountVolume (MountOptions &options) + { + MountVolumeRequest request (&options); + return SendRequest (request)->MountedVolumeInfo; + } + + void CoreService::RequestSetFileOwner (const FilesystemPath &path, const UserId &owner) + { + SetFileOwnerRequest request (path, owner); + SendRequest (request); + } + + template + auto_ptr CoreService::SendRequest (CoreServiceRequest &request) + { + static Mutex mutex; + ScopeLock lock (mutex); + + if (request.RequiresElevation()) + { + request.ElevateUserPrivileges = true; + request.FastElevation = !ElevatedServiceAvailable; + request.ApplicationExecutablePath = Core->GetApplicationExecutablePath(); + + while (!ElevatedServiceAvailable) + { + try + { + request.Serialize (ServiceInputStream); + auto_ptr response (GetResponse ()); + ElevatedServiceAvailable = true; + return response; + } + catch (ElevationFailed &e) + { + if (!request.FastElevation) + { + ExceptionEventArgs args (e); + Core->WarningEvent.Raise (args); + } + + request.FastElevation = false; + (*AdminPasswordCallback) (request.AdminPassword); + } + } + } + + finally_do_arg (string *, &request.AdminPassword, { StringConverter::Erase (*finally_arg); }); + + request.Serialize (ServiceInputStream); + return GetResponse (); + } + + void CoreService::Start () + { + InputPipe.reset (new Pipe()); + OutputPipe.reset (new Pipe()); + + int pid = fork(); + throw_sys_if (pid == -1); + + if (pid == 0) + { + try + { + ProcessRequests(); + _exit (0); + } + catch (...) { } + _exit (1); + } + + ServiceInputStream = shared_ptr (new FileStream (InputPipe->GetWriteFD())); + ServiceOutputStream = shared_ptr (new FileStream (OutputPipe->GetReadFD())); + } + + void CoreService::StartElevated (const CoreServiceRequest &request) + { + auto_ptr inPipe (new Pipe()); + auto_ptr outPipe (new Pipe()); + Pipe errPipe; + + int forkedPid = fork(); + throw_sys_if (forkedPid == -1); + + if (forkedPid == 0) + { + try + { + try + { + throw_sys_if (dup2 (inPipe->GetReadFD(), STDIN_FILENO) == -1); + throw_sys_if (dup2 (outPipe->GetWriteFD(), STDOUT_FILENO) == -1); + throw_sys_if (dup2 (errPipe.GetWriteFD(), STDERR_FILENO) == -1); + + string appPath = request.ApplicationExecutablePath; + if (appPath.empty()) + appPath = "truecrypt"; + + const char *args[] = { "sudo", "-S", "-p", "", appPath.c_str(), TC_CORE_SERVICE_CMDLINE_OPTION, nullptr }; + execvp (args[0], ((char* const*) args)); + throw SystemException (SRC_POS, args[0]); + } + catch (Exception &) + { + throw; + } + catch (exception &e) + { + throw ExternalException (SRC_POS, StringConverter::ToExceptionString (e)); + } + catch (...) + { + throw UnknownException (SRC_POS); + } + } + catch (Exception &e) + { + try + { + shared_ptr outputStream (new FileStream (errPipe.GetWriteFD())); + e.Serialize (outputStream); + } + catch (...) { } + } + + _exit (1); + } + + vector adminPassword (request.AdminPassword.size() + 1); + int timeout = 6000; + + if (request.FastElevation) + { + string dummyPassword = "dummy\n"; + adminPassword = vector (dummyPassword.size()); + Memory::Copy (&adminPassword.front(), dummyPassword.c_str(), dummyPassword.size()); + timeout = 1000; + } + else + { + Memory::Copy (&adminPassword.front(), request.AdminPassword.c_str(), request.AdminPassword.size()); + adminPassword[request.AdminPassword.size()] = '\n'; + } + + if (write (inPipe->GetWriteFD(), &adminPassword.front(), adminPassword.size())) { } // Errors ignored + + Memory::Erase (&adminPassword.front(), adminPassword.size()); + + throw_sys_if (fcntl (outPipe->GetReadFD(), F_SETFL, O_NONBLOCK) == -1); + throw_sys_if (fcntl (errPipe.GetReadFD(), F_SETFL, O_NONBLOCK) == -1); + + vector buffer (4096), errOutput (4096); + buffer.clear (); + errOutput.clear (); + + Poller poller (outPipe->GetReadFD(), errPipe.GetReadFD()); + int status, waitRes; + int exitCode = 1; + + try + { + do + { + ssize_t bytesRead = 0; + foreach (int fd, poller.WaitForData (timeout)) + { + bytesRead = read (fd, &buffer[0], buffer.capacity()); + if (bytesRead > 0 && fd == errPipe.GetReadFD()) + { + errOutput.insert (errOutput.end(), buffer.begin(), buffer.begin() + bytesRead); + + if (bytesRead > 5 && bytesRead < 80) // Short message captured + timeout = 200; + } + } + + if (bytesRead == 0) + { + waitRes = waitpid (forkedPid, &status, 0); + break; + } + + } while ((waitRes = waitpid (forkedPid, &status, WNOHANG)) == 0); + } + catch (TimeOut&) + { + if ((waitRes = waitpid (forkedPid, &status, WNOHANG)) == 0) + { + inPipe->Close(); + outPipe->Close(); + errPipe.Close(); + + if (request.FastElevation) + { + // Prevent defunct process + struct WaitFunctor : public Functor + { + WaitFunctor (int pid) : Pid (pid) { } + virtual void operator() () + { + int status; + for (int t = 0; t < 10 && waitpid (Pid, &status, WNOHANG) == 0; t++) + Thread::Sleep (1000); + } + int Pid; + }; + Thread thread; + thread.Start (new WaitFunctor (forkedPid)); + + throw ElevationFailed (SRC_POS, "sudo", 1, ""); + } + + waitRes = waitpid (forkedPid, &status, 0); + } + } + + if (!errOutput.empty()) + { + auto_ptr deserializedObject; + Exception *deserializedException = nullptr; + + try + { + shared_ptr stream (new MemoryStream (ConstBufferPtr ((byte *) &errOutput[0], errOutput.size()))); + deserializedObject.reset (Serializable::DeserializeNew (stream)); + deserializedException = dynamic_cast (deserializedObject.get()); + } + catch (...) { } + + if (deserializedException) + deserializedException->Throw(); + } + + throw_sys_if (waitRes == -1); + exitCode = (WIFEXITED (status) ? WEXITSTATUS (status) : 1); + if (exitCode != 0) + { + string strErrOutput; + + if (!errOutput.empty()) + strErrOutput.insert (strErrOutput.begin(), errOutput.begin(), errOutput.end()); + + // sudo may require a tty even if -S is used + if (strErrOutput.find (" tty") != string::npos) + strErrOutput += "\nTo enable use of 'sudo' by applications without a terminal window, please disable 'requiretty' option in '/etc/sudoers'. Newer versions of sudo automatically determine whether a terminal is required ('requiretty' option is obsolete)."; + + throw ElevationFailed (SRC_POS, "sudo", exitCode, strErrOutput); + } + + throw_sys_if (fcntl (outPipe->GetReadFD(), F_SETFL, 0) == -1); + + ServiceInputStream = shared_ptr (new FileStream (inPipe->GetWriteFD())); + ServiceOutputStream = shared_ptr (new FileStream (outPipe->GetReadFD())); + + // Send sync code + byte sync[] = { 0, 0x11, 0x22 }; + ServiceInputStream->Write (ConstBufferPtr (sync, array_capacity (sync))); + + AdminInputPipe = inPipe; + AdminOutputPipe = outPipe; + } + + void CoreService::Stop () + { + ExitRequest exitRequest; + exitRequest.Serialize (ServiceInputStream); + } + + shared_ptr CoreService::AdminPasswordCallback; + + auto_ptr CoreService::AdminInputPipe; + auto_ptr CoreService::AdminOutputPipe; + + auto_ptr CoreService::InputPipe; + auto_ptr CoreService::OutputPipe; + shared_ptr CoreService::ServiceInputStream; + shared_ptr CoreService::ServiceOutputStream; + + bool CoreService::ElevatedPrivileges = false; + bool CoreService::ElevatedServiceAvailable = false; +} -- cgit v1.2.3